Unverified 提交 0a286015 authored 作者: Will Chen's avatar Will Chen 提交者: GitHub

Passthrough settings so downgrading Dyad doesn't cause issues (#2183)

<!-- CURSOR_SUMMARY --> > [!NOTE] > **Behavior change: preserve unknown settings fields** > > - Apply `.passthrough()` to `UserSettingsSchema` so unknown properties are retained instead of stripped > - Update `readSettings.test.ts` to assert extra fields (e.g., `unknownField`, `deprecatedSetting`, nested objects) are preserved while defaults still apply > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit d7bd0311a14cb4b501c698c987706efe069ab975. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- This is an auto-generated description by cubic. --> ## Summary by cubic Preserves unknown settings fields during validation to prevent data loss when downgrading Dyad. Switches the user settings schema to passthrough and updates tests to match. - **Bug Fixes** - UserSettingsSchema now uses Zod .passthrough() to keep unrecognized fields. - Updated readSettings test to assert extra fields are preserved. <sup>Written for commit d7bd0311a14cb4b501c698c987706efe069ab975. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. -->
上级 25ddf69d
...@@ -248,7 +248,7 @@ describe("readSettings", () => { ...@@ -248,7 +248,7 @@ describe("readSettings", () => {
); );
}); });
it("should strip extra fields not recognized by the schema", () => { it("should preserve extra fields not recognized by the schema", () => {
const mockFileContent = { const mockFileContent = {
selectedModel: { selectedModel: {
name: "gpt-4", name: "gpt-4",
...@@ -256,8 +256,8 @@ describe("readSettings", () => { ...@@ -256,8 +256,8 @@ describe("readSettings", () => {
}, },
telemetryConsent: "opted_in", telemetryConsent: "opted_in",
hasRunBefore: true, hasRunBefore: true,
// Extra fields that are not in the schema // Extra fields that are not in the schema (should be preserved)
unknownField: "should be removed", unknownField: "should be preserved",
deprecatedSetting: true, deprecatedSetting: true,
extraConfig: { extraConfig: {
someValue: 123, someValue: 123,
...@@ -281,10 +281,15 @@ describe("readSettings", () => { ...@@ -281,10 +281,15 @@ describe("readSettings", () => {
expect(result.telemetryConsent).toBe("opted_in"); expect(result.telemetryConsent).toBe("opted_in");
expect(result.hasRunBefore).toBe(true); expect(result.hasRunBefore).toBe(true);
// Extra fields should be stripped by schema validation // Extra fields should be preserved by passthrough()
expect(result).not.toHaveProperty("unknownField"); // eslint-disable-next-line @typescript-eslint/no-explicit-any
expect(result).not.toHaveProperty("deprecatedSetting"); const resultAny = result as any;
expect(result).not.toHaveProperty("extraConfig"); expect(resultAny.unknownField).toBe("should be preserved");
expect(resultAny.deprecatedSetting).toBe(true);
expect(resultAny.extraConfig).toEqual({
someValue: 123,
anotherValue: "test",
});
// Should still have defaults for missing properties // Should still have defaults for missing properties
expect(result.enableAutoUpdate).toBe(true); expect(result.enableAutoUpdate).toBe(true);
......
...@@ -248,70 +248,74 @@ export type AgentToolConsent = z.infer<typeof AgentToolConsentSchema>; ...@@ -248,70 +248,74 @@ export type AgentToolConsent = z.infer<typeof AgentToolConsentSchema>;
/** /**
* Zod schema for user settings * Zod schema for user settings
*/ */
export const UserSettingsSchema = z.object({ export const UserSettingsSchema = z
//////////////////////////////// .object({
// E2E TESTING ONLY. ////////////////////////////////
//////////////////////////////// // E2E TESTING ONLY.
isTestMode: z.boolean().optional(), ////////////////////////////////
isTestMode: z.boolean().optional(),
////////////////////////////////
// DEPRECATED. ////////////////////////////////
//////////////////////////////// // DEPRECATED.
enableProSaverMode: z.boolean().optional(), ////////////////////////////////
dyadProBudget: DyadProBudgetSchema.optional(), enableProSaverMode: z.boolean().optional(),
runtimeMode: RuntimeModeSchema.optional(), dyadProBudget: DyadProBudgetSchema.optional(),
runtimeMode: RuntimeModeSchema.optional(),
////////////////////////////////
// ACTIVE FIELDS. ////////////////////////////////
//////////////////////////////// // ACTIVE FIELDS.
selectedModel: LargeLanguageModelSchema, ////////////////////////////////
providerSettings: z.record(z.string(), ProviderSettingSchema), selectedModel: LargeLanguageModelSchema,
agentToolConsents: z.record(z.string(), AgentToolConsentSchema).optional(), providerSettings: z.record(z.string(), ProviderSettingSchema),
githubUser: GithubUserSchema.optional(), agentToolConsents: z.record(z.string(), AgentToolConsentSchema).optional(),
githubAccessToken: SecretSchema.optional(), githubUser: GithubUserSchema.optional(),
vercelAccessToken: SecretSchema.optional(), githubAccessToken: SecretSchema.optional(),
supabase: SupabaseSchema.optional(), vercelAccessToken: SecretSchema.optional(),
neon: NeonSchema.optional(), supabase: SupabaseSchema.optional(),
autoApproveChanges: z.boolean().optional(), neon: NeonSchema.optional(),
telemetryConsent: z.enum(["opted_in", "opted_out", "unset"]).optional(), autoApproveChanges: z.boolean().optional(),
telemetryUserId: z.string().optional(), telemetryConsent: z.enum(["opted_in", "opted_out", "unset"]).optional(),
hasRunBefore: z.boolean().optional(), telemetryUserId: z.string().optional(),
enableDyadPro: z.boolean().optional(), hasRunBefore: z.boolean().optional(),
experiments: ExperimentsSchema.optional(), enableDyadPro: z.boolean().optional(),
lastShownReleaseNotesVersion: z.string().optional(), experiments: ExperimentsSchema.optional(),
maxChatTurnsInContext: z.number().optional(), lastShownReleaseNotesVersion: z.string().optional(),
thinkingBudget: z.enum(["low", "medium", "high"]).optional(), maxChatTurnsInContext: z.number().optional(),
enableProLazyEditsMode: z.boolean().optional(), thinkingBudget: z.enum(["low", "medium", "high"]).optional(),
proLazyEditsMode: z.enum(["off", "v1", "v2"]).optional(), enableProLazyEditsMode: z.boolean().optional(),
enableProSmartFilesContextMode: z.boolean().optional(), proLazyEditsMode: z.enum(["off", "v1", "v2"]).optional(),
enableProWebSearch: z.boolean().optional(), enableProSmartFilesContextMode: z.boolean().optional(),
proSmartContextOption: SmartContextModeSchema.optional(), enableProWebSearch: z.boolean().optional(),
selectedTemplateId: z.string(), proSmartContextOption: SmartContextModeSchema.optional(),
selectedThemeId: z.string().optional(), selectedTemplateId: z.string(),
enableSupabaseWriteSqlMigration: z.boolean().optional(), selectedThemeId: z.string().optional(),
selectedChatMode: ChatModeSchema.optional(), enableSupabaseWriteSqlMigration: z.boolean().optional(),
acceptedCommunityCode: z.boolean().optional(), selectedChatMode: ChatModeSchema.optional(),
zoomLevel: ZoomLevelSchema.optional(), acceptedCommunityCode: z.boolean().optional(),
zoomLevel: ZoomLevelSchema.optional(),
enableAutoFixProblems: z.boolean().optional(),
enableNativeGit: z.boolean().optional(), enableAutoFixProblems: z.boolean().optional(),
enableAutoUpdate: z.boolean(), enableNativeGit: z.boolean().optional(),
releaseChannel: ReleaseChannelSchema, enableAutoUpdate: z.boolean(),
runtimeMode2: RuntimeMode2Schema.optional(), releaseChannel: ReleaseChannelSchema,
customNodePath: z.string().optional().nullable(), runtimeMode2: RuntimeMode2Schema.optional(),
isRunning: z.boolean().optional(), customNodePath: z.string().optional().nullable(),
lastKnownPerformance: z isRunning: z.boolean().optional(),
.object({ lastKnownPerformance: z
timestamp: z.number(), .object({
memoryUsageMB: z.number(), timestamp: z.number(),
cpuUsagePercent: z.number().optional(), memoryUsageMB: z.number(),
systemMemoryUsageMB: z.number().optional(), cpuUsagePercent: z.number().optional(),
systemMemoryTotalMB: z.number().optional(), systemMemoryUsageMB: z.number().optional(),
systemCpuPercent: z.number().optional(), systemMemoryTotalMB: z.number().optional(),
}) systemCpuPercent: z.number().optional(),
.optional(), })
hideLocalAgentNewChatToast: z.boolean().optional(), .optional(),
}); hideLocalAgentNewChatToast: z.boolean().optional(),
})
// Allow unknown properties to pass through (e.g. future settings
// that should be preserved if user downgrades to an older version)
.passthrough();
/** /**
* Type derived from the UserSettingsSchema * Type derived from the UserSettingsSchema
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论