@stackframe/stack-shared 2.8.25 → 2.8.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/config/format.d.mts +20 -5
  3. package/dist/config/format.d.ts +20 -5
  4. package/dist/config/format.js +39 -14
  5. package/dist/config/format.js.map +1 -1
  6. package/dist/config/schema.d.mts +795 -317
  7. package/dist/config/schema.d.ts +795 -317
  8. package/dist/config/schema.js +471 -84
  9. package/dist/config/schema.js.map +1 -1
  10. package/dist/crud.d.mts +1 -0
  11. package/dist/crud.d.ts +1 -0
  12. package/dist/esm/config/format.js +37 -14
  13. package/dist/esm/config/format.js.map +1 -1
  14. package/dist/esm/config/schema.js +460 -80
  15. package/dist/esm/config/schema.js.map +1 -1
  16. package/dist/esm/helpers/emails.js +136 -30
  17. package/dist/esm/helpers/emails.js.map +1 -1
  18. package/dist/esm/interface/admin-interface.js +19 -29
  19. package/dist/esm/interface/admin-interface.js.map +1 -1
  20. package/dist/esm/known-errors.js +24 -1
  21. package/dist/esm/known-errors.js.map +1 -1
  22. package/dist/esm/schema-fields.js +92 -27
  23. package/dist/esm/schema-fields.js.map +1 -1
  24. package/dist/esm/utils/currencies.js +52 -0
  25. package/dist/esm/utils/currencies.js.map +1 -0
  26. package/dist/esm/utils/dates.js +55 -1
  27. package/dist/esm/utils/dates.js.map +1 -1
  28. package/dist/esm/utils/errors.js.map +1 -1
  29. package/dist/esm/utils/objects.js +2 -0
  30. package/dist/esm/utils/objects.js.map +1 -1
  31. package/dist/esm/utils/strings.js +4 -0
  32. package/dist/esm/utils/strings.js.map +1 -1
  33. package/dist/esm/utils/types.js +45 -0
  34. package/dist/esm/utils/types.js.map +1 -1
  35. package/dist/helpers/emails.d.mts +32 -6
  36. package/dist/helpers/emails.d.ts +32 -6
  37. package/dist/helpers/emails.js +138 -30
  38. package/dist/helpers/emails.js.map +1 -1
  39. package/dist/helpers/password.d.mts +1 -0
  40. package/dist/helpers/password.d.ts +1 -0
  41. package/dist/helpers/production-mode.d.mts +1 -0
  42. package/dist/helpers/production-mode.d.ts +1 -0
  43. package/dist/index.d.mts +2 -2
  44. package/dist/index.d.ts +2 -2
  45. package/dist/interface/admin-interface.d.mts +8 -8
  46. package/dist/interface/admin-interface.d.ts +8 -8
  47. package/dist/interface/admin-interface.js +19 -29
  48. package/dist/interface/admin-interface.js.map +1 -1
  49. package/dist/interface/client-interface.d.mts +1 -0
  50. package/dist/interface/client-interface.d.ts +1 -0
  51. package/dist/interface/crud/connected-accounts.d.mts +1 -0
  52. package/dist/interface/crud/connected-accounts.d.ts +1 -0
  53. package/dist/interface/crud/contact-channels.d.mts +1 -0
  54. package/dist/interface/crud/contact-channels.d.ts +1 -0
  55. package/dist/interface/crud/current-user.d.mts +1 -0
  56. package/dist/interface/crud/current-user.d.ts +1 -0
  57. package/dist/interface/crud/email-templates.d.mts +1 -0
  58. package/dist/interface/crud/email-templates.d.ts +1 -0
  59. package/dist/interface/crud/emails.d.mts +1 -0
  60. package/dist/interface/crud/emails.d.ts +1 -0
  61. package/dist/interface/crud/internal-api-keys.d.mts +1 -0
  62. package/dist/interface/crud/internal-api-keys.d.ts +1 -0
  63. package/dist/interface/crud/notification-preferences.d.mts +1 -0
  64. package/dist/interface/crud/notification-preferences.d.ts +1 -0
  65. package/dist/interface/crud/oauth-providers.d.mts +5 -4
  66. package/dist/interface/crud/oauth-providers.d.ts +5 -4
  67. package/dist/interface/crud/project-api-keys.d.mts +3 -2
  68. package/dist/interface/crud/project-api-keys.d.ts +3 -2
  69. package/dist/interface/crud/project-permissions.d.mts +1 -0
  70. package/dist/interface/crud/project-permissions.d.ts +1 -0
  71. package/dist/interface/crud/projects.d.mts +8 -7
  72. package/dist/interface/crud/projects.d.ts +8 -7
  73. package/dist/interface/crud/sessions.d.mts +1 -0
  74. package/dist/interface/crud/sessions.d.ts +1 -0
  75. package/dist/interface/crud/svix-token.d.mts +1 -0
  76. package/dist/interface/crud/svix-token.d.ts +1 -0
  77. package/dist/interface/crud/team-invitation-details.d.mts +1 -0
  78. package/dist/interface/crud/team-invitation-details.d.ts +1 -0
  79. package/dist/interface/crud/team-invitation.d.mts +1 -0
  80. package/dist/interface/crud/team-invitation.d.ts +1 -0
  81. package/dist/interface/crud/team-member-profiles.d.mts +1 -0
  82. package/dist/interface/crud/team-member-profiles.d.ts +1 -0
  83. package/dist/interface/crud/team-memberships.d.mts +1 -0
  84. package/dist/interface/crud/team-memberships.d.ts +1 -0
  85. package/dist/interface/crud/team-permissions.d.mts +1 -0
  86. package/dist/interface/crud/team-permissions.d.ts +1 -0
  87. package/dist/interface/crud/teams.d.mts +1 -0
  88. package/dist/interface/crud/teams.d.ts +1 -0
  89. package/dist/interface/crud/users.d.mts +1 -0
  90. package/dist/interface/crud/users.d.ts +1 -0
  91. package/dist/interface/server-interface.d.mts +1 -0
  92. package/dist/interface/server-interface.d.ts +1 -0
  93. package/dist/known-errors.d.mts +6 -0
  94. package/dist/known-errors.d.ts +6 -0
  95. package/dist/known-errors.js +24 -1
  96. package/dist/known-errors.js.map +1 -1
  97. package/dist/schema-fields.d.mts +39 -8
  98. package/dist/schema-fields.d.ts +39 -8
  99. package/dist/schema-fields.js +101 -27
  100. package/dist/schema-fields.js.map +1 -1
  101. package/dist/utils/currencies.d.mts +39 -0
  102. package/dist/utils/currencies.d.ts +39 -0
  103. package/dist/utils/currencies.js +78 -0
  104. package/dist/utils/currencies.js.map +1 -0
  105. package/dist/utils/dates.d.mts +5 -1
  106. package/dist/utils/dates.d.ts +5 -1
  107. package/dist/utils/dates.js +58 -2
  108. package/dist/utils/dates.js.map +1 -1
  109. package/dist/utils/errors.d.mts +9 -0
  110. package/dist/utils/errors.d.ts +9 -0
  111. package/dist/utils/errors.js.map +1 -1
  112. package/dist/utils/objects.d.mts +23 -8
  113. package/dist/utils/objects.d.ts +23 -8
  114. package/dist/utils/objects.js +2 -0
  115. package/dist/utils/objects.js.map +1 -1
  116. package/dist/utils/strings.d.mts +3 -1
  117. package/dist/utils/strings.d.ts +3 -1
  118. package/dist/utils/strings.js +5 -0
  119. package/dist/utils/strings.js.map +1 -1
  120. package/dist/utils/types.d.mts +73 -2
  121. package/dist/utils/types.d.ts +73 -2
  122. package/dist/utils/types.js +54 -0
  123. package/dist/utils/types.js.map +1 -1
  124. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/config/schema.ts"],"sourcesContent":["import * as yup from \"yup\";\nimport * as schemaFields from \"../schema-fields\";\nimport { yupBoolean, yupObject, yupRecord, yupString, yupUnion } from \"../schema-fields\";\nimport { allProviders } from \"../utils/oauth\";\nimport { DeepMerge, DeepPartial, get, has, isObjectLike, mapValues, set } from \"../utils/objects\";\nimport { Config, NormalizesTo } from \"./format\";\nimport { DEFAULT_EMAIL_THEME_ID, DEFAULT_EMAIL_THEMES, DEFAULT_EMAIL_TEMPLATES } from \"../helpers/emails\";\n\n// NOTE: The validation schemas in here are all schematic validators, not sanity-check validators.\n// For more info, see ./README.md\n\n\nexport const configLevels = ['project', 'branch', 'environment', 'organization'] as const;\nexport type ConfigLevel = typeof configLevels[number];\nconst permissionRegex = /^\\$?[a-z0-9_:]+$/;\nconst customPermissionRegex = /^[a-z0-9_:]+$/;\n\n/**\n * All fields that can be overridden at this level.\n */\nexport const projectConfigSchema = yupObject({\n sourceOfTruth: yupUnion(\n yupObject({\n type: yupString().oneOf(['hosted']).optional(),\n }).defined(),\n yupObject({\n type: yupString().oneOf(['neon']).optional(),\n connectionStrings: yupRecord(\n yupString().defined(),\n yupString().defined(),\n ).defined(),\n }).defined(),\n yupObject({\n type: yupString().oneOf(['postgres']).optional(),\n connectionString: yupString().defined()\n }).defined(),\n ).optional(),\n});\n\n// --- NEW RBAC Schema ---\nconst branchRbacDefaultPermissions = yupRecord(\n yupString().optional().matches(permissionRegex),\n yupBoolean().isTrue().optional(),\n).optional();\n\nconst branchRbacSchema = yupObject({\n permissions: yupRecord(\n yupString().optional().matches(customPermissionRegex),\n yupObject({\n description: yupString().optional(),\n scope: yupString().oneOf(['team', 'project']).optional(),\n containedPermissionIds: yupRecord(\n yupString().optional().matches(permissionRegex),\n yupBoolean().isTrue().optional()\n ).optional(),\n }).optional(),\n ).optional(),\n defaultPermissions: yupObject({\n teamCreator: branchRbacDefaultPermissions,\n teamMember: branchRbacDefaultPermissions,\n signUp: branchRbacDefaultPermissions,\n }).optional(),\n}).optional();\n// --- END NEW RBAC Schema ---\n\n// --- NEW API Keys Schema ---\nconst branchApiKeysSchema = yupObject({\n enabled: yupObject({\n team: yupBoolean().optional(),\n user: yupBoolean().optional(),\n }).optional(),\n}).optional();\n// --- END NEW API Keys Schema ---\n\n\nconst branchAuthSchema = yupObject({\n allowSignUp: yupBoolean().optional(),\n password: yupObject({\n allowSignIn: yupBoolean().optional(),\n }).optional(),\n otp: yupObject({\n allowSignIn: yupBoolean().optional(),\n }).optional(),\n passkey: yupObject({\n allowSignIn: yupBoolean().optional(),\n }).optional(),\n oauth: yupObject({\n accountMergeStrategy: yupString().oneOf(['link_method', 'raise_error', 'allow_duplicates']).optional(),\n providers: yupRecord(\n yupString().optional().matches(permissionRegex),\n yupObject({\n type: yupString().oneOf(allProviders).optional(),\n allowSignIn: yupBoolean().optional(),\n allowConnectedAccounts: yupBoolean().optional(),\n }).defined(),\n ).optional(),\n }).optional(),\n}).optional();\n\nconst branchDomain = yupObject({\n allowLocalhost: yupBoolean().optional(),\n}).optional();\n\nexport const branchConfigSchema = projectConfigSchema.omit(['sourceOfTruth']).concat(yupObject({\n rbac: branchRbacSchema,\n\n teams: yupObject({\n createPersonalTeamOnSignUp: yupBoolean().optional(),\n allowClientTeamCreation: yupBoolean().optional(),\n }).optional(),\n\n users: yupObject({\n allowClientUserDeletion: yupBoolean().optional(),\n }).optional(),\n\n apiKeys: branchApiKeysSchema,\n\n domains: branchDomain,\n\n auth: branchAuthSchema,\n\n emails: yupObject({\n theme: schemaFields.emailThemeSchema.optional(),\n themeList: schemaFields.emailThemeListSchema.optional(),\n templateList: schemaFields.emailTemplateListSchema.optional(),\n }),\n\n}));\n\n\nexport const environmentConfigSchema = branchConfigSchema.concat(yupObject({\n auth: branchConfigSchema.getNested(\"auth\").concat(yupObject({\n oauth: branchConfigSchema.getNested(\"auth\").getNested(\"oauth\").concat(yupObject({\n providers: yupRecord(\n yupString().optional().matches(permissionRegex),\n yupObject({\n type: yupString().oneOf(allProviders).optional(),\n isShared: yupBoolean().optional(),\n clientId: schemaFields.oauthClientIdSchema.optional(),\n clientSecret: schemaFields.oauthClientSecretSchema.optional(),\n facebookConfigId: schemaFields.oauthFacebookConfigIdSchema.optional(),\n microsoftTenantId: schemaFields.oauthMicrosoftTenantIdSchema.optional(),\n allowSignIn: yupBoolean().optional(),\n allowConnectedAccounts: yupBoolean().optional(),\n }),\n ).optional(),\n }).optional()),\n })),\n\n emails: branchConfigSchema.getNested(\"emails\").concat(yupObject({\n server: yupObject({\n isShared: yupBoolean().optional(),\n host: schemaFields.emailHostSchema.optional().nonEmpty(),\n port: schemaFields.emailPortSchema.optional(),\n username: schemaFields.emailUsernameSchema.optional().nonEmpty(),\n password: schemaFields.emailPasswordSchema.optional().nonEmpty(),\n senderName: schemaFields.emailSenderNameSchema.optional().nonEmpty(),\n senderEmail: schemaFields.emailSenderEmailSchema.optional().nonEmpty(),\n }),\n }).optional()),\n\n domains: branchConfigSchema.getNested(\"domains\").concat(yupObject({\n trustedDomains: yupRecord(\n yupString().uuid().optional(),\n yupObject({\n baseUrl: schemaFields.urlSchema.optional(),\n handlerPath: schemaFields.handlerPathSchema.optional(),\n }),\n ).optional(),\n })),\n}));\n\nexport const organizationConfigSchema = environmentConfigSchema.concat(yupObject({}));\n\n\n// Defaults\n// these are objects that are merged together to form the rendered config (see ./README.md)\n// Wherever an object could be used as a value, a function can instead be used to generate the default values on a per-key basis\n// NOTE: These values are the defaults of the schema, NOT the defaults for newly created projects. The values here signify what `null` means for each property. If you want new projects by default to have a certain value set to true, you should update the corresponding function in the backend instead.\nexport const projectConfigDefaults = {\n sourceOfTruth: {\n type: 'hosted',\n },\n} satisfies DeepReplaceAllowFunctionsForObjects<ProjectConfigStrippedNormalizedOverride>;\n\nexport const branchConfigDefaults = {} satisfies DeepReplaceAllowFunctionsForObjects<BranchConfigStrippedNormalizedOverride>;\n\nexport const environmentConfigDefaults = {} satisfies DeepReplaceAllowFunctionsForObjects<EnvironmentConfigStrippedNormalizedOverride>;\n\nexport const organizationConfigDefaults = {\n rbac: {\n permissions: (key: string) => ({}),\n defaultPermissions: {\n teamCreator: {},\n teamMember: {},\n signUp: {},\n },\n },\n\n apiKeys: {\n enabled: {\n team: false,\n user: false,\n },\n },\n\n teams: {\n createPersonalTeamOnSignUp: false,\n allowClientTeamCreation: false,\n },\n\n users: {\n allowClientUserDeletion: false,\n },\n\n domains: {\n allowLocalhost: false,\n trustedDomains: (key: string) => ({\n handlerPath: '/handler',\n }),\n },\n\n auth: {\n allowSignUp: true,\n password: {\n allowSignIn: false,\n },\n otp: {\n allowSignIn: false,\n },\n passkey: {\n allowSignIn: false,\n },\n oauth: {\n accountMergeStrategy: 'link_method',\n providers: (key: string) => ({\n isShared: true,\n allowSignIn: false,\n allowConnectedAccounts: false,\n }),\n },\n },\n\n emails: {\n server: {\n isShared: true,\n },\n theme: DEFAULT_EMAIL_THEME_ID,\n themeList: DEFAULT_EMAIL_THEMES,\n templateList: DEFAULT_EMAIL_TEMPLATES,\n },\n} satisfies DeepReplaceAllowFunctionsForObjects<OrganizationConfigStrippedNormalizedOverride>;\n\nexport type DeepReplaceAllowFunctionsForObjects<T> = T extends object ? { [K in keyof T]: DeepReplaceAllowFunctionsForObjects<T[K]> } | ((arg: keyof T) => DeepReplaceAllowFunctionsForObjects<T[keyof T]>) : T;\nexport type DeepReplaceFunctionsWithObjects<T> = T extends (arg: infer K extends string) => infer R ? DeepReplaceFunctionsWithObjects<Record<K, R>> : (T extends object ? { [K in keyof T]: DeepReplaceFunctionsWithObjects<T[K]> } : T);\nexport type ApplyDefaults<D extends object | ((key: string) => unknown), C extends object> = DeepMerge<DeepReplaceFunctionsWithObjects<D>, C>;\nexport function applyDefaults<D extends object | ((key: string) => unknown), C extends object>(defaults: D, config: C): ApplyDefaults<D, C> {\n const res: any = typeof defaults === 'function' ? {} : mapValues(defaults, v => typeof v === 'function' ? {} : (typeof v === 'object' ? applyDefaults(v as any, {}) : v));\n for (const [key, mergeValue] of Object.entries(config)) {\n const baseValue = typeof defaults === 'function' ? defaults(key) : (has(defaults, key as any) ? get(defaults, key as any) : undefined);\n if (baseValue !== undefined) {\n if (isObjectLike(baseValue) && isObjectLike(mergeValue)) {\n set(res, key, applyDefaults(baseValue, mergeValue));\n continue;\n }\n }\n set(res, key, mergeValue);\n }\n return res as any;\n}\nundefined?.test(\"applyDefaults\", ({ expect }) => {\n expect(applyDefaults({ a: 1 }, { a: 2 })).toEqual({ a: 2 });\n expect(applyDefaults({ a: { b: 1 } }, { a: { c: 2 } })).toEqual({ a: { b: 1, c: 2 } });\n expect(applyDefaults((key: string) => ({ b: key }), { a: {} })).toEqual({ a: { b: \"a\" } });\n expect(applyDefaults({ a: (key: string) => ({ b: key }) }, { a: { c: { d: 1 } } })).toEqual({ a: { c: { b: \"c\", d: 1 } } });\n expect(applyDefaults({ a: (key: string) => ({ b: key }) }, {})).toEqual({ a: {} });\n expect(applyDefaults({ a: { b: (key: string) => ({ b: key }) } }, {})).toEqual({ a: { b: {} } });\n});\n\n// Normalized overrides\nexport type ProjectConfigNormalizedOverride = yup.InferType<typeof projectConfigSchema>;\nexport type BranchConfigNormalizedOverride = yup.InferType<typeof branchConfigSchema>;\nexport type EnvironmentConfigNormalizedOverride = yup.InferType<typeof environmentConfigSchema>;\nexport type OrganizationConfigNormalizedOverride = yup.InferType<typeof organizationConfigSchema>;\n\n// Normalized overrides, but only the fields that will NOT be overridden by a future level anymore\nexport type ProjectConfigStrippedNormalizedOverride = Omit<ProjectConfigNormalizedOverride,\n | keyof BranchConfigNormalizedOverride\n | keyof EnvironmentConfigNormalizedOverride\n | keyof OrganizationConfigNormalizedOverride\n>;\nexport type BranchConfigStrippedNormalizedOverride = Omit<BranchConfigNormalizedOverride,\n | keyof EnvironmentConfigNormalizedOverride\n | keyof OrganizationConfigNormalizedOverride\n>;\nexport type EnvironmentConfigStrippedNormalizedOverride = Omit<EnvironmentConfigNormalizedOverride,\n | keyof OrganizationConfigNormalizedOverride\n>;\nexport type OrganizationConfigStrippedNormalizedOverride = OrganizationConfigNormalizedOverride;\n\n// Overrides\nexport type ProjectConfigOverride = NormalizesTo<ProjectConfigNormalizedOverride>;\nexport type BranchConfigOverride = NormalizesTo<BranchConfigNormalizedOverride>;\nexport type EnvironmentConfigOverride = NormalizesTo<EnvironmentConfigNormalizedOverride>;\nexport type OrganizationConfigOverride = NormalizesTo<OrganizationConfigNormalizedOverride>;\n\n// Override overrides (used to update the overrides)\nexport type ProjectConfigOverrideOverride = Config & DeepPartial<ProjectConfigOverride>;\nexport type BranchConfigOverrideOverride = Config & DeepPartial<BranchConfigOverride>;\nexport type EnvironmentConfigOverrideOverride = Config & DeepPartial<EnvironmentConfigOverride>;\nexport type OrganizationConfigOverrideOverride = Config & DeepPartial<OrganizationConfigOverride>;\n\n// Incomplete configs\nexport type ProjectIncompleteConfig = ApplyDefaults<typeof projectConfigDefaults, ProjectConfigNormalizedOverride>;\nexport type BranchIncompleteConfig = ApplyDefaults<typeof branchConfigDefaults, ProjectIncompleteConfig & BranchConfigNormalizedOverride>;\nexport type EnvironmentIncompleteConfig = ApplyDefaults<typeof environmentConfigDefaults, BranchIncompleteConfig & EnvironmentConfigNormalizedOverride>;\nexport type OrganizationIncompleteConfig = ApplyDefaults<typeof organizationConfigDefaults, EnvironmentIncompleteConfig & OrganizationConfigNormalizedOverride>;\n\n// Rendered configs\nexport type ProjectRenderedConfig = Omit<ProjectIncompleteConfig,\n | keyof BranchConfigNormalizedOverride\n | keyof EnvironmentConfigNormalizedOverride\n | keyof OrganizationConfigNormalizedOverride\n>;\nexport type BranchRenderedConfig = Omit<BranchIncompleteConfig,\n | keyof EnvironmentConfigNormalizedOverride\n | keyof OrganizationConfigNormalizedOverride\n>;\nexport type EnvironmentRenderedConfig = Omit<EnvironmentIncompleteConfig,\n | keyof OrganizationConfigNormalizedOverride\n>;\nexport type OrganizationRenderedConfig = OrganizationIncompleteConfig;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/config/schema.ts"],"sourcesContent":["// TODO: rename this file to spaghetti.ts because that's the kind of code here\n\nimport * as yup from \"yup\";\nimport { DEFAULT_EMAIL_TEMPLATES, DEFAULT_EMAIL_THEMES, DEFAULT_EMAIL_THEME_ID } from \"../helpers/emails\";\nimport * as schemaFields from \"../schema-fields\";\nimport { yupBoolean, yupDate, yupMixed, yupNever, yupNumber, yupObject, yupRecord, yupString, yupTuple, yupUnion } from \"../schema-fields\";\nimport { isShallowEqual } from \"../utils/arrays\";\nimport { StackAssertionError } from \"../utils/errors\";\nimport { allProviders } from \"../utils/oauth\";\nimport { DeepFilterUndefined, DeepMerge, DeepRequiredOrUndefined, deleteKey, filterUndefined, get, has, isObjectLike, mapValues, set, typedAssign, typedFromEntries } from \"../utils/objects\";\nimport { Result } from \"../utils/results\";\nimport { CollapseObjectUnion, Expand, IntersectAll, IsUnion, typeAssert, typeAssertExtends, typeAssertIs } from \"../utils/types\";\nimport { Config, NormalizationError, NormalizesTo, assertNormalized, getInvalidConfigReason, normalize } from \"./format\";\n\nexport const configLevels = ['project', 'branch', 'environment', 'organization'] as const;\nexport type ConfigLevel = typeof configLevels[number];\nconst permissionRegex = /^\\$?[a-z0-9_:]+$/;\nconst customPermissionRegex = /^[a-z0-9_:]+$/;\ndeclare module \"yup\" {\n // eslint-disable-next-line @typescript-eslint/consistent-type-definitions\n export interface CustomSchemaMetadata {\n stackConfigCanNoLongerBeOverridden?: true,\n }\n}\n\nfunction canNoLongerBeOverridden<T extends yup.AnyObjectSchema, K extends string[]>(schema: T, keys: K): yup.Schema<Omit<yup.InferType<T>, K[number]>, T['__context'], Omit<T['__default'], K[number]>, T['__flags']> {\n const notOmitted = schema.concat(yupObject(\n Object.fromEntries(keys.map(key => [key, schema.getNested(key).meta({ stackConfigCanNoLongerBeOverridden: true })]))\n ));\n return notOmitted as any;\n}\n\n/**\n * All fields that can be overridden at this level.\n */\nexport const projectConfigSchema = yupObject({\n sourceOfTruth: yupUnion(\n yupObject({\n type: yupString().oneOf(['hosted']).defined(),\n }),\n yupObject({\n type: yupString().oneOf(['neon']).defined(),\n connectionStrings: yupRecord(\n yupString().defined(),\n yupString().defined(),\n ).defined(),\n }),\n yupObject({\n type: yupString().oneOf(['postgres']).defined(),\n connectionString: yupString().defined()\n }),\n ),\n});\n\n// --- NEW RBAC Schema ---\nconst branchRbacDefaultPermissions = yupRecord(\n yupString().matches(permissionRegex),\n yupBoolean().isTrue().optional(),\n);\n\nconst branchRbacSchema = yupObject({\n permissions: yupRecord(\n yupString().matches(customPermissionRegex),\n yupObject({\n description: yupString().optional(),\n scope: yupString().oneOf(['team', 'project']).optional(),\n containedPermissionIds: yupRecord(\n yupString().matches(permissionRegex),\n yupBoolean().isTrue().optional()\n ).optional(),\n }).optional(),\n ),\n defaultPermissions: yupObject({\n teamCreator: branchRbacDefaultPermissions,\n teamMember: branchRbacDefaultPermissions,\n signUp: branchRbacDefaultPermissions,\n }),\n});\n// --- END NEW RBAC Schema ---\n\n// --- NEW API Keys Schema ---\nconst branchApiKeysSchema = yupObject({\n enabled: yupObject({\n team: yupBoolean(),\n user: yupBoolean(),\n }),\n});\n// --- END NEW API Keys Schema ---\n\n\nconst branchAuthSchema = yupObject({\n allowSignUp: yupBoolean(),\n password: yupObject({\n allowSignIn: yupBoolean(),\n }),\n otp: yupObject({\n allowSignIn: yupBoolean(),\n }),\n passkey: yupObject({\n allowSignIn: yupBoolean(),\n }),\n oauth: yupObject({\n accountMergeStrategy: yupString().oneOf(['link_method', 'raise_error', 'allow_duplicates']).optional(),\n providers: yupRecord(\n yupString().matches(permissionRegex),\n yupObject({\n type: yupString().oneOf(allProviders).optional(),\n allowSignIn: yupBoolean(),\n allowConnectedAccounts: yupBoolean(),\n }),\n ),\n }),\n});\n\nconst branchDomain = yupObject({\n allowLocalhost: yupBoolean(),\n});\n\nexport const branchConfigSchema = canNoLongerBeOverridden(projectConfigSchema, [\"sourceOfTruth\"]).concat(yupObject({\n rbac: branchRbacSchema,\n\n teams: yupObject({\n createPersonalTeamOnSignUp: yupBoolean(),\n allowClientTeamCreation: yupBoolean(),\n }),\n\n users: yupObject({\n allowClientUserDeletion: yupBoolean(),\n }),\n\n apiKeys: branchApiKeysSchema,\n\n domains: branchDomain,\n\n auth: branchAuthSchema,\n\n emails: yupObject({\n selectedThemeId: schemaFields.emailThemeSchema,\n themes: schemaFields.emailThemeListSchema,\n templates: schemaFields.emailTemplateListSchema,\n }),\n}));\n\n\nexport const environmentConfigSchema = branchConfigSchema.concat(yupObject({\n auth: branchConfigSchema.getNested(\"auth\").concat(yupObject({\n oauth: branchConfigSchema.getNested(\"auth\").getNested(\"oauth\").concat(yupObject({\n providers: yupRecord(\n yupString().matches(permissionRegex),\n yupObject({\n type: yupString().oneOf(allProviders).optional(),\n isShared: yupBoolean(),\n clientId: schemaFields.oauthClientIdSchema.optional(),\n clientSecret: schemaFields.oauthClientSecretSchema.optional(),\n facebookConfigId: schemaFields.oauthFacebookConfigIdSchema.optional(),\n microsoftTenantId: schemaFields.oauthMicrosoftTenantIdSchema.optional(),\n allowSignIn: yupBoolean().optional(),\n allowConnectedAccounts: yupBoolean().optional(),\n }),\n ),\n })),\n })),\n\n emails: branchConfigSchema.getNested(\"emails\").concat(yupObject({\n server: yupObject({\n isShared: yupBoolean(),\n host: schemaFields.emailHostSchema.optional().nonEmpty(),\n port: schemaFields.emailPortSchema.optional(),\n username: schemaFields.emailUsernameSchema.optional().nonEmpty(),\n password: schemaFields.emailPasswordSchema.optional().nonEmpty(),\n senderName: schemaFields.emailSenderNameSchema.optional().nonEmpty(),\n senderEmail: schemaFields.emailSenderEmailSchema.optional().nonEmpty(),\n }),\n })),\n\n domains: branchConfigSchema.getNested(\"domains\").concat(yupObject({\n trustedDomains: yupRecord(\n yupString(),\n yupObject({\n baseUrl: schemaFields.urlSchema,\n handlerPath: schemaFields.handlerPathSchema,\n }),\n ),\n })),\n}));\n\nexport const organizationConfigSchema = environmentConfigSchema.concat(yupObject({}));\n\n\n// Migration functions\n//\n// These are used to migrate old config overrides to the new format on the database.\n//\n// THEY SHOULD NOT BE USED FOR ANY OTHER PURPOSE. They should not be used for default values. They should not be used\n// for sanitization. Instead, use the applicable functions for that.\n//\n// We run these migrations over the database when we do a big migration. USE THESE SPARINGLY. USE OTHER METHODS WHENEVER\n// POSSIBLE.\n//\n// The result of this function should be reproducible, and should not contain ANY randomness/non-determinism.\nexport function migrateConfigOverride(type: \"project\" | \"branch\" | \"environment\" | \"organization\", oldUnmigratedConfigOverride: any): any {\n const isBranchOrHigher = [\"branch\", \"environment\", \"organization\"].includes(type);\n const isEnvironmentOrHigher = [\"environment\", \"organization\"].includes(type);\n\n let res = oldUnmigratedConfigOverride;\n\n // BEGIN 2025-07-28: emails.theme is now emails.selectedThemeId\n if (isBranchOrHigher) {\n res = renameProperty(res, \"emails.theme\", \"emails.selectedThemeId\");\n }\n // END\n\n // BEGIN 2025-07-28: domains.trustedDomains can no longer be an array\n if (isEnvironmentOrHigher) {\n res = mapProperty(res, \"domains.trustedDomains\", (value) => {\n if (Array.isArray(value)) {\n return typedFromEntries(value.map((v, i) => [`${i}`, v]));\n }\n return value;\n });\n }\n // END\n\n // BEGIN 2025-07-28: themeList and templateList have been renamed (this was before the email release, so they're safe to remove)\n if (isBranchOrHigher) {\n res = removeProperty(res, \"emails.themeList\");\n res = removeProperty(res, \"emails.templateList\");\n }\n // END\n\n // BEGIN 2025-07-28: sourceOfTruth was mistakenly written to the environment config in some cases, so let's remove it\n if (type === \"environment\") {\n res = removeProperty(res, \"sourceOfTruth\");\n }\n // END\n\n // return the result\n return res;\n};\n\nfunction removeProperty(obj: any, path: string): any {\n return mapProperty(obj, path, () => undefined);\n}\n\nfunction mapProperty(obj: any, path: string, mapper: (value: any) => any): any {\n const keyParts = path.split(\".\");\n\n for (let i = 0; i < keyParts.length; i++) {\n const pathPrefix = keyParts.slice(0, i).join(\".\");\n const pathSuffix = keyParts.slice(i).join(\".\");\n if (has(obj, pathPrefix) && isObjectLike(get(obj, pathPrefix))) {\n const newValue = mapProperty(get(obj, pathPrefix), pathSuffix, mapper);\n set(obj, pathPrefix, newValue);\n }\n }\n if (has(obj, path)) {\n const newValue = mapper(get(obj, path));\n if (newValue !== undefined) {\n set(obj, path, newValue);\n } else {\n deleteKey(obj, path);\n }\n }\n\n return obj;\n}\nundefined?.test(\"mapProperty - basic property mapping\", ({ expect }) => {\n expect(mapProperty({ a: { b: { c: 1 } } }, \"a.b.c\", (value) => value + 1)).toEqual({ a: { b: { c: 2 } } });\n expect(mapProperty({ a: { b: { c: 1 } } }, \"a.b.d\", (value) => value + 1)).toEqual({ a: { b: { c: 1 } } });\n expect(mapProperty({ x: 5 }, \"x\", (value) => value * 2)).toEqual({ x: 10 });\n expect(mapProperty({ a: { b: { c: 1 } } }, \"b.c\", (value) => value * 10)).toEqual({ a: { b: { c: 1 } } });\n expect(mapProperty({ a: 1 }, \"b.c\", (value) => value)).toEqual({ a: 1 });\n});\n\nfunction renameProperty(obj: any, oldPath: string, newPath: string): any {\n const oldKeyParts = oldPath.split(\".\");\n const newKeyParts = newPath.split(\".\");\n if (!isShallowEqual(oldKeyParts.slice(0, -1), newKeyParts.slice(0, -1))) throw new StackAssertionError(`oldPath and newPath must have the same prefix. Provided: ${oldPath} and ${newPath}`);\n\n for (let i = 0; i < oldKeyParts.length; i++) {\n const pathPrefix = oldKeyParts.slice(0, i).join(\".\");\n const oldPathSuffix = oldKeyParts.slice(i).join(\".\");\n const newPathSuffix = newKeyParts.slice(i).join(\".\");\n if (has(obj, pathPrefix) && isObjectLike(get(obj, pathPrefix))) {\n set(obj, pathPrefix, renameProperty(get(obj, pathPrefix), oldPathSuffix, newPathSuffix));\n }\n }\n if (has(obj, oldPath)) {\n set(obj, newPath, get(obj, oldPath));\n deleteKey(obj, oldPath);\n }\n\n return obj;\n}\nundefined?.test(\"renameProperty\", ({ expect }) => {\n // Basic\n expect(renameProperty({ a: 1 }, \"a\", \"b\")).toEqual({ b: 1 });\n expect(renameProperty({ b: { c: 1 } }, \"b.c\", \"b.d\")).toEqual({ b: { d: 1 } });\n expect(renameProperty({ a: { b: { c: 1 } } }, \"a.b.c\", \"a.b.d\")).toEqual({ a: { b: { d: 1 } } });\n expect(renameProperty({ a: { b: { c: 1 } } }, \"a.b.c.d\", \"a.b.c.e\")).toEqual({ a: { b: { c: 1 } } });\n\n // Errors\n expect(() => renameProperty({ a: 1 }, \"a\", \"b.c\")).toThrow();\n});\n\n\n// Defaults\n// these are objects that are merged together to form the rendered config (see ./README.md)\n// Wherever an object could be used as a value, a function can instead be used to generate the default values on a per-key basis\n// To make sure you don't accidentally forget setting a default value, you must explicitly set fields with no default value to `undefined`.\n// NOTE: These values are the defaults of the schema, NOT the defaults for newly created projects. The values here signify what `null` means for each property. If you want new projects by default to have a certain value set to true, you should update the corresponding function in the backend instead.\nconst projectConfigDefaults = {\n sourceOfTruth: {\n type: 'hosted',\n connectionStrings: undefined,\n connectionString: undefined,\n },\n} as const satisfies DefaultsType<ProjectRenderedConfigBeforeDefaults, []>;\n\nconst branchConfigDefaults = {} as const satisfies DefaultsType<BranchRenderedConfigBeforeDefaults, [typeof projectConfigDefaults]>;\n\nconst environmentConfigDefaults = {} as const satisfies DefaultsType<EnvironmentRenderedConfigBeforeDefaults, [typeof branchConfigDefaults, typeof projectConfigDefaults]>;\n\nconst organizationConfigDefaults = {\n rbac: {\n permissions: (key: string) => ({\n containedPermissionIds: (key: string) => undefined,\n description: undefined,\n scope: undefined,\n }),\n defaultPermissions: {\n teamCreator: (key: string) => undefined,\n teamMember: (key: string) => undefined,\n signUp: (key: string) => undefined,\n },\n },\n\n apiKeys: {\n enabled: {\n team: false,\n user: false,\n },\n },\n\n teams: {\n createPersonalTeamOnSignUp: false,\n allowClientTeamCreation: false,\n },\n\n users: {\n allowClientUserDeletion: false,\n },\n\n domains: {\n allowLocalhost: false,\n trustedDomains: (key: string) => ({\n baseUrl: undefined,\n handlerPath: '/handler',\n }) as const,\n },\n\n auth: {\n allowSignUp: true,\n password: {\n allowSignIn: false,\n },\n otp: {\n allowSignIn: false,\n },\n passkey: {\n allowSignIn: false,\n },\n oauth: {\n accountMergeStrategy: 'link_method',\n providers: (key: string) => ({\n type: undefined,\n isShared: true,\n allowSignIn: false,\n allowConnectedAccounts: false,\n clientId: undefined,\n clientSecret: undefined,\n facebookConfigId: undefined,\n microsoftTenantId: undefined,\n }),\n },\n },\n\n emails: {\n server: {\n isShared: true,\n host: undefined,\n port: undefined,\n username: undefined,\n password: undefined,\n senderName: undefined,\n senderEmail: undefined,\n },\n selectedThemeId: DEFAULT_EMAIL_THEME_ID,\n themes: typedAssign((key: string) => ({\n displayName: \"Unnamed Theme\",\n tsxSource: \"Error: Theme config is missing TypeScript source code.\",\n }), DEFAULT_EMAIL_THEMES),\n templates: typedAssign((key: string) => ({\n displayName: \"Unnamed Template\",\n tsxSource: \"Error: Template config is missing TypeScript source code.\",\n themeId: undefined,\n }), DEFAULT_EMAIL_TEMPLATES),\n },\n} as const satisfies DefaultsType<OrganizationRenderedConfigBeforeDefaults, [typeof environmentConfigDefaults, typeof branchConfigDefaults, typeof projectConfigDefaults]>;\n\ntype _DeepOmitDefaultsImpl<T, U> = T extends object ? (\n (\n & /* keys that are both in T and U, *and* the key's value in U is not a subtype of the key's value in T */ { [K in { [Ki in keyof T & keyof U]: U[Ki] extends T[Ki] ? never : Ki }[keyof T & keyof U]]: DeepOmitDefaults<T[K], U[K] & object> }\n & /* keys that are in T but not in U */ { [K in Exclude<keyof T, keyof U>]: T[K] }\n )\n) : T;\ntype DeepOmitDefaults<T, U> = _DeepOmitDefaultsImpl<DeepFilterUndefined<T>, U>;\ntype DefaultsType<T, U extends any[]> = DeepReplaceAllowFunctionsForObjects<DeepOmitDefaults<DeepRequiredOrUndefined<T>, IntersectAll<{ [K in keyof U]: DeepReplaceFunctionsWithObjects<U[K]> }>>>;\ntypeAssertIs<DefaultsType<{ a: { b: Record<string, 123>, c: 456 } }, [{ a: { c: 456 } }]>, { a: { b: ((key: string) => 123) | Record<string, 123 | undefined> & ((key: string) => 123) } }>()();\n\ntype DeepReplaceAllowFunctionsForObjects<T> = T extends object\n ? (\n string extends keyof T\n ? ((arg: Exclude<keyof T, number>) => DeepReplaceAllowFunctionsForObjects<T[keyof T]>) & ({ [K in keyof T]?: DeepReplaceAllowFunctionsForObjects<T[K]> } | {})\n : { [K in keyof T]: DeepReplaceAllowFunctionsForObjects<T[K]> }\n )\n :\n T;\ntype ReplaceFunctionsWithObjects<T> = T & (T extends (arg: infer K extends string) => infer R ? Record<K, R> & object : unknown);\ntype DeepReplaceFunctionsWithObjects<T> = T extends object ? { [K in keyof ReplaceFunctionsWithObjects<T>]: DeepReplaceFunctionsWithObjects<ReplaceFunctionsWithObjects<T>[K]> } : T;\ntypeAssertIs<DeepReplaceFunctionsWithObjects<{ a: { b: 123 } & ((key: string) => number) }>, { a: { b: 123, [key: string]: number } }>()();\n\nfunction deepReplaceFunctionsWithObjects(obj: any): any {\n return mapValues({ ...obj }, v => (isObjectLike(v) ? deepReplaceFunctionsWithObjects(v as any) : v));\n}\nundefined?.test(\"deepReplaceFunctionsWithObjects\", ({ expect }) => {\n expect(deepReplaceFunctionsWithObjects(() => {})).toEqual({});\n expect(deepReplaceFunctionsWithObjects({ a: 3 })).toEqual({ a: 3 });\n expect(deepReplaceFunctionsWithObjects({ a: () => ({ b: 1 }) })).toEqual({ a: {} });\n expect(deepReplaceFunctionsWithObjects({ a: typedAssign(() => ({}), { b: { c: 1 } }) })).toEqual({ a: { b: { c: 1 } } });\n});\n\ntype ApplyDefaults<D extends object | ((key: string) => unknown), C extends object> = {} extends D ? C : DeepMerge<DeepReplaceFunctionsWithObjects<D>, C>; // the {} extends D makes TypeScript not recurse if the defaults are empty, hence allowing us more recursion until \"type instantiation too deep\" kicks in... it's a total hack, but it works, so hey?\nfunction applyDefaults<D extends object | ((key: string) => unknown), C extends object>(defaults: D, config: C): ApplyDefaults<D, C> {\n const res: any = deepReplaceFunctionsWithObjects(defaults);\n\n outer: for (const [key, mergeValue] of Object.entries(config)) {\n if (mergeValue === undefined) continue;\n const keyParts = key.split(\".\");\n let baseValue: any = defaults;\n let currentRes: any = res;\n for (const [index, part] of keyParts.entries()) {\n baseValue = has(baseValue, part) ? get(baseValue, part) : (typeof baseValue === 'function' ? (baseValue as any)(part) : undefined);\n if (baseValue === undefined || !isObjectLike(baseValue)) {\n set(res, key, mergeValue);\n continue outer;\n }\n if (!has(currentRes, part)) set(currentRes, part, deepReplaceFunctionsWithObjects(baseValue) as never);\n currentRes = get(currentRes, part);\n }\n set(res, key, isObjectLike(mergeValue) ? applyDefaults(baseValue, mergeValue) : mergeValue);\n }\n return res as any;\n}\nundefined?.test(\"applyDefaults\", ({ expect }) => {\n // Basic\n expect(applyDefaults({ a: 1 }, { a: 2 })).toEqual({ a: 2 });\n expect(applyDefaults({}, { a: 1 })).toEqual({ a: 1 });\n expect(applyDefaults({ a: { b: 1 } }, { a: { b: 2 } })).toEqual({ a: { b: 2 } });\n expect(applyDefaults({ a: { b: 1 } }, { a: { c: 2 } })).toEqual({ a: { b: 1, c: 2 } });\n expect(applyDefaults({ a: { b: { c: 1, d: 2 } } }, { a: { b: { d: 3, e: 4 } } })).toEqual({ a: { b: { c: 1, d: 3, e: 4 } } });\n\n // Functions\n expect(applyDefaults((key: string) => ({ b: key }), { a: {} })).toEqual({ a: { b: \"a\" } });\n expect(applyDefaults((key1: string) => (key2: string) => ({ a: key1, b: key2 }), { c: { d: {} } })).toEqual({ c: { d: { a: \"c\", b: \"d\" } } });\n expect(applyDefaults({ a: (key: string) => ({ b: key }) }, { a: { c: { d: 1 } } })).toEqual({ a: { c: { b: \"c\", d: 1 } } });\n expect(applyDefaults({ a: (key: string) => ({ b: key }) }, {})).toEqual({ a: {} });\n expect(applyDefaults({ a: { b: (key: string) => ({ b: key }) } }, {})).toEqual({ a: { b: {} } });\n expect(applyDefaults(typedAssign(() => ({ b: 1 }), { a: { b: 1, c: 2 } }), { a: {} })).toEqual({ a: { b: 1, c: 2 } });\n expect(applyDefaults(typedAssign(() => ({ b: 1 }), { a: { b: 1, c: 2 } }), { d: {} })).toEqual({ a: { b: 1, c: 2 }, d: { b: 1 } });\n\n // Dot notation\n expect(applyDefaults({ a: { b: 1 } }, { \"a.c\": 2 })).toEqual({ a: { b: 1 }, \"a.c\": 2 });\n expect(applyDefaults({ a: 1 }, { \"a.b\": 2 })).toEqual({ a: 1, \"a.b\": 2 });\n expect(applyDefaults({ a: null }, { \"a.b\": 2 })).toEqual({ a: null, \"a.b\": 2 });\n expect(applyDefaults({ a: { b: { c: 1 } } }, { \"a.b\": { d: 2 } })).toEqual({ a: { b: { c: 1 } }, \"a.b\": { c: 1, d: 2 } });\n expect(applyDefaults({ a: { b: { c: { d: 1 } } } }, { \"a.b.c\": {} })).toEqual({ a: { b: { c: { d: 1 } } }, \"a.b.c\": { d: 1 } });\n expect(applyDefaults({ a: () => ({ c: 1 }) }, { \"a.b\": { d: 2 } })).toEqual({ a: { b: { c: 1 } }, \"a.b\": { c: 1, d: 2 } });\n expect(applyDefaults({ a: () => () => ({ d: 1 }) }, { \"a.b.c\": {} })).toEqual({ a: { b: { c: { d: 1 } } }, \"a.b.c\": { d: 1 } });\n expect(applyDefaults({ a: { b: () => ({ c: 1, d: 2 }) } }, { \"a.b.x-y.c\": 3 })).toEqual({ a: { b: { \"x-y\": { c: 1, d: 2 } } }, \"a.b.x-y.c\": 3 });\n});\n\nexport function applyProjectDefaults<T extends ProjectRenderedConfigBeforeDefaults>(config: T) {\n return applyDefaults(projectConfigDefaults, config);\n}\n\nexport function applyBranchDefaults<T extends BranchRenderedConfigBeforeDefaults>(config: T) {\n return applyDefaults(\n branchConfigDefaults,\n applyDefaults(\n projectConfigDefaults,\n config\n )\n );\n}\n\nexport function applyEnvironmentDefaults<T extends EnvironmentRenderedConfigBeforeDefaults>(config: T): ApplyDefaults<typeof environmentConfigDefaults, ApplyDefaults<typeof branchConfigDefaults, ApplyDefaults<typeof projectConfigDefaults, T>>> {\n return applyDefaults(\n environmentConfigDefaults,\n applyDefaults(\n branchConfigDefaults,\n applyDefaults(\n projectConfigDefaults,\n config\n ) as any\n ) as any\n ) as any;\n}\n\nexport function applyOrganizationDefaults(config: OrganizationRenderedConfigBeforeDefaults): ApplyDefaults<typeof organizationConfigDefaults, ApplyDefaults<typeof environmentConfigDefaults, ApplyDefaults<typeof branchConfigDefaults, ApplyDefaults<typeof projectConfigDefaults, OrganizationRenderedConfigBeforeDefaults>>>> {\n return applyDefaults(\n organizationConfigDefaults,\n applyDefaults(\n environmentConfigDefaults,\n applyDefaults(\n branchConfigDefaults,\n applyDefaults(\n projectConfigDefaults,\n config\n ) as any\n ) as any\n ) as any\n ) as any;\n}\n\n\nexport async function sanitizeProjectConfig<T extends ProjectRenderedConfigBeforeSanitization>(config: T) {\n assertNormalized(config);\n const oldSourceOfTruth = config.sourceOfTruth;\n const sourceOfTruth =\n oldSourceOfTruth.type === 'neon' && typeof oldSourceOfTruth.connectionStrings === 'object' ? {\n type: 'neon',\n connectionStrings: { ...filterUndefined(oldSourceOfTruth.connectionStrings) as Record<string, string> }\n } as const\n : oldSourceOfTruth.type === 'postgres' && typeof oldSourceOfTruth.connectionString === 'string' ? {\n type: 'postgres',\n connectionString: oldSourceOfTruth.connectionString,\n } as const\n : {\n type: 'hosted',\n } as const;\n\n return {\n ...config,\n sourceOfTruth,\n };\n}\n\nexport async function sanitizeBranchConfig<T extends BranchRenderedConfigBeforeSanitization>(config: T) {\n assertNormalized(config);\n const prepared = await sanitizeProjectConfig(config);\n return {\n ...prepared,\n };\n}\n\nexport async function sanitizeEnvironmentConfig<T extends EnvironmentRenderedConfigBeforeSanitization>(config: T) {\n assertNormalized(config);\n const prepared = await sanitizeBranchConfig(config);\n return {\n ...prepared,\n };\n}\n\nexport async function sanitizeOrganizationConfig(config: OrganizationRenderedConfigBeforeSanitization) {\n assertNormalized(config);\n const prepared = await sanitizeEnvironmentConfig(config);\n const themes: typeof prepared.emails.themes = {\n ...DEFAULT_EMAIL_THEMES,\n ...prepared.emails.themes,\n };\n const templates: typeof prepared.emails.templates = {\n ...DEFAULT_EMAIL_TEMPLATES,\n ...prepared.emails.templates,\n };\n return {\n ...prepared,\n emails: {\n ...prepared.emails,\n selectedThemeId: has(themes, prepared.emails.selectedThemeId) ? prepared.emails.selectedThemeId : DEFAULT_EMAIL_THEME_ID,\n themes,\n templates,\n },\n };\n}\n\n/**\n * Does not require a base config, and hence solely relies on the override itself to validate the config. If it returns\n * no error, you know that the\n *\n * It's crucial that our DB never contains any configs that are not valid according to this function, as this would mean\n * that the config object does not satisfy the ValidatedToHaveNoConfigOverrideErrors type (which is used as an assumption\n * in a whole bunch of places in the code).\n */\nexport async function getConfigOverrideErrors<T extends yup.AnySchema>(schema: T, configOverride: unknown, options: { allowPropertiesThatCanNoLongerBeOverridden?: boolean } = {}): Promise<Result<null, string>> {\n // currently, we go over the schema and ensure that the general requirements for each property are satisfied\n // importantly, we cannot check any cross-property constraints, as those may change depending on the base config\n // also, since overrides can be empty, we cannot have any required properties (TODO: can we have required properties in nested objects? would that even make sense? think about it)\n if (typeof configOverride !== \"object\" || configOverride === null) {\n return Result.error(\"Config override must be a non-null object.\");\n }\n if (Object.getPrototypeOf(configOverride) !== Object.getPrototypeOf({})) {\n return Result.error(\"Config override must be plain old JavaScript object.\");\n }\n // Check config format\n const reason = getInvalidConfigReason(configOverride, { configName: 'override' });\n if (reason) return Result.error(\"Invalid config format: \" + reason);\n\n const getSubSchema = (schema: yup.AnySchema, key: string): yup.AnySchema | undefined => {\n const keyParts = key.split(\".\");\n if (!schema.hasNested(keyParts[0])) {\n return undefined;\n }\n const nestedSchema = schema.getNested(keyParts[0]);\n if (nestedSchema.meta()?.stackConfigCanNoLongerBeOverridden && !options.allowPropertiesThatCanNoLongerBeOverridden) {\n return undefined;\n }\n if (keyParts.length === 1) {\n return nestedSchema;\n } else {\n return getSubSchema(nestedSchema, keyParts.slice(1).join(\".\"));\n }\n };\n\n const getRestrictedSchemaBase = (path: string, schema: yup.AnySchema): yup.AnySchema => {\n const schemaInfo = schema.meta()?.stackSchemaInfo;\n switch (schemaInfo?.type) {\n case \"string\": {\n const stringSchema = schema as yup.StringSchema<any>;\n const description = stringSchema.describe();\n let res = yupString();\n if (description.tests.some(t => t.name === \"uuid\")) {\n res = res.uuid();\n }\n return res;\n }\n case \"number\": {\n return yupNumber();\n }\n case \"boolean\": {\n return yupBoolean();\n }\n case \"date\": {\n return yupDate();\n }\n case \"mixed\": {\n return yupMixed();\n }\n case \"array\": {\n throw new StackAssertionError(`Arrays are not supported in config JSON files (besides tuples). Use a record instead.`, { schemaInfo, schema });\n\n // This is how the implementation would look like, but we don't support arrays in config JSON files (besides tuples)\n // const arraySchema = schema as yup.ArraySchema<any, any, any, any>;\n // const innerType = arraySchema.innerType;\n // return yupArray(innerType ? getRestrictedSchema(path + \".[]\", innerType as any) : undefined);\n }\n case \"tuple\": {\n return yupTuple(schemaInfo.items.map((s, index) => getRestrictedSchema(path + `[${index}]`, s)) as any);\n }\n case \"union\": {\n const schemas = schemaInfo.items;\n const nonObjectSchemas = [...schemas.entries()].filter(([index, s]) => s.meta()?.stackSchemaInfo?.type !== \"object\");\n const objectSchemas = schemas.filter((s): s is yup.ObjectSchema<any> => s.meta()?.stackSchemaInfo?.type === \"object\");\n\n // merge all object schemas into a single schema\n const allObjectSchemaKeys = [...new Set(objectSchemas.flatMap(s => Object.keys(s.fields)))];\n const mergedObjectSchema = yupObject(\n Object.fromEntries(\n allObjectSchemaKeys.map(key => [key, yupUnion(\n ...objectSchemas.flatMap((s, index) => s.hasNested(key) ? [s.getNested(key)] : [])\n )])\n )\n );\n\n return yupUnion(\n ...nonObjectSchemas.map(([index, s]) => getRestrictedSchema(path + `|variant-${index}|`, s)),\n ...objectSchemas.length > 0 ? [getRestrictedSchema(path + (nonObjectSchemas.length > 0 ? `|variant|` : \"\"), mergedObjectSchema)] : [],\n );\n }\n case \"record\": {\n return yupRecord(getRestrictedSchema(path + \".key\", schemaInfo.keySchema) as any, getRestrictedSchema(path + \".value\", schemaInfo.valueSchema));\n }\n case \"object\": {\n const objectSchema = schema as yup.ObjectSchema<any>;\n return yupObject(\n Object.fromEntries(\n Object.entries(objectSchema.fields)\n .map(([key, value]) => [key, getRestrictedSchema(path + \".\" + key, value as any)])\n )\n );\n }\n case \"never\": {\n return yupNever();\n }\n default: {\n throw new StackAssertionError(`Unknown schema info at path ${path}: ${JSON.stringify(schemaInfo)}`, { schemaInfo, schema });\n }\n }\n };\n const getRestrictedSchema = (path: string, schema: yup.AnySchema): yup.AnySchema => {\n let restricted = getRestrictedSchemaBase(path, schema);\n restricted = restricted.nullable();\n const description = schema.describe();\n if (description.oneOf.length > 0) {\n restricted = restricted.oneOf(description.oneOf);\n }\n if (description.notOneOf.length > 0) {\n restricted = restricted.notOneOf(description.notOneOf);\n }\n return restricted;\n };\n\n for (const [key, value] of Object.entries(configOverride)) {\n if (value === undefined) continue;\n const subSchema = getSubSchema(schema, key);\n if (!subSchema) {\n return Result.error(`The key ${JSON.stringify(key)} is not valid for the schema.`);\n }\n let restrictedSchema = getRestrictedSchema(key, subSchema);\n try {\n await restrictedSchema.validate(value, {\n strict: true,\n ...{\n // Although `path` is not part of the yup types, it is actually recognized and does the correct thing\n path: key\n },\n context: {\n noUnknownPathPrefixes: [''],\n },\n });\n } catch (error) {\n if (error instanceof yup.ValidationError) {\n return Result.error(error.message);\n }\n throw error;\n }\n }\n return Result.ok(null);\n}\nexport async function assertNoConfigOverrideErrors<T extends yup.AnySchema>(schema: T, config: unknown, options: { allowPropertiesThatCanNoLongerBeOverridden?: boolean, extraInfo?: any } = {}): Promise<void> {\n const res = await getConfigOverrideErrors(schema, config, options);\n if (res.status === \"error\") throw new StackAssertionError(`Config override is invalid — at a place where it should have already been validated! ${res.error}`, { options, config, schema });\n}\ntype _ValidatedToHaveNoConfigOverrideErrorsImpl<T> =\n IsUnion<T & object> extends true ? _ValidatedToHaveNoConfigOverrideErrorsImpl<CollapseObjectUnion<T & object> | Exclude<T, object>>\n : T extends object ? (T extends any[] ? T : { [K in keyof T]+?: _ValidatedToHaveNoConfigOverrideErrorsImpl<T[K]> })\n : T;\nexport type ValidatedToHaveNoConfigOverrideErrors<T extends yup.AnySchema> = _ValidatedToHaveNoConfigOverrideErrorsImpl<yup.InferType<T>>;\ntypeAssertIs<_ValidatedToHaveNoConfigOverrideErrorsImpl<{ a: string } | { b: number } | boolean>, { a?: string, b?: number } | boolean>()();\ntypeAssertExtends<_ValidatedToHaveNoConfigOverrideErrorsImpl<\"abc\" | 123 | null>, \"abc\" | 123 | null>()();\ntypeAssertExtends<_ValidatedToHaveNoConfigOverrideErrorsImpl<{ a: { b: { c: string } | { d: number } } }>, { a?: { b?: { c?: string, d?: number } } }>()();\n\n/**\n * Checks whether there are any warnings in the incomplete config. A warning doesn't stop the config from being valid,\n * but may require action regardless.\n *\n * The DB can contain configs that are not valid according to this function, as long as they are valid according to\n * the getConfigOverrideErrors function. (This is necessary, because a changing base config may make an override invalid\n * that was previously valid.)\n */\nexport async function getIncompleteConfigWarnings<T extends yup.AnySchema>(schema: T, incompleteConfig: Config): Promise<Result<null, string>> {\n // every rendered config should also be a config override without errors (regardless of whether it has warnings or not)\n await assertNoConfigOverrideErrors(schema, incompleteConfig, { allowPropertiesThatCanNoLongerBeOverridden: true });\n\n let normalized: Config;\n try {\n normalized = normalize(incompleteConfig, { onDotIntoNull: \"empty-object\" });\n } catch (error) {\n if (error instanceof NormalizationError) {\n return Result.error(`Config is not normalizable. ` + error.message);\n }\n throw error;\n }\n\n // test the schema against the normalized config\n try {\n await schema.validate(normalized, {\n strict: true,\n context: {\n noUnknownPathPrefixes: [''],\n },\n });\n return Result.ok(null);\n } catch (error) {\n if (error instanceof yup.ValidationError) {\n return Result.error(error.message);\n }\n throw error;\n }\n}\nexport type ValidatedToHaveNoIncompleteConfigWarnings<T extends yup.AnySchema> = yup.InferType<T>;\n\n\n// Normalized overrides\n// ex.: { a?: { b?: number, c?: string }, d?: number }\ntype ProjectConfigNormalizedOverride = Expand<ValidatedToHaveNoConfigOverrideErrors<typeof projectConfigSchema>>;\ntype BranchConfigNormalizedOverride = Expand<ValidatedToHaveNoConfigOverrideErrors<typeof branchConfigSchema>>;\ntype EnvironmentConfigNormalizedOverride = Expand<ValidatedToHaveNoConfigOverrideErrors<typeof environmentConfigSchema>>;\ntype OrganizationConfigNormalizedOverride = Expand<ValidatedToHaveNoConfigOverrideErrors<typeof organizationConfigSchema>>;\n\n// Overrides\n// ex.: { a?: null | { b?: null | number, c: string }, d?: null | number, \"a.b\"?: number, \"a.c\"?: string }\nexport type ProjectConfigOverride = NormalizesTo<ProjectConfigNormalizedOverride>;\nexport type BranchConfigOverride = NormalizesTo<BranchConfigNormalizedOverride>;\nexport type EnvironmentConfigOverride = NormalizesTo<EnvironmentConfigNormalizedOverride>;\nexport type OrganizationConfigOverride = NormalizesTo<OrganizationConfigNormalizedOverride>;\n\n// Override overrides (used to update the overrides)\n// ex.: { a?: null | { b?: null | number, c?: string }, d?: null | number, \"a.b\"?: number, \"a.c\"?: string }\nexport type ProjectConfigOverrideOverride = ProjectConfigOverride;\nexport type BranchConfigOverrideOverride = BranchConfigOverride;\nexport type EnvironmentConfigOverrideOverride = EnvironmentConfigOverride;\nexport type OrganizationConfigOverrideOverride = OrganizationConfigOverride;\n\n// Incomplete configs\n// note that we infer these types from the override types, not from the schema types directly, as there is no guarantee\n// that all configs in the DB satisfy the schema (the only guarantee we make is that this once *used* to be true)\nexport type ProjectIncompleteConfig = Expand<ProjectConfigNormalizedOverride>;\nexport type BranchIncompleteConfig = Expand<ProjectIncompleteConfig & BranchConfigNormalizedOverride>;\nexport type EnvironmentIncompleteConfig = Expand<BranchIncompleteConfig & EnvironmentConfigNormalizedOverride>;\nexport type OrganizationIncompleteConfig = Expand<EnvironmentIncompleteConfig & OrganizationConfigNormalizedOverride>;\n\n// Rendered configs before defaults, normalization, and sanitization\ntype ProjectRenderedConfigBeforeDefaults = Omit<ProjectIncompleteConfig,\n | keyof BranchConfigNormalizedOverride\n | keyof EnvironmentConfigNormalizedOverride\n | keyof OrganizationConfigNormalizedOverride\n>;\ntype BranchRenderedConfigBeforeDefaults = Omit<BranchIncompleteConfig,\n | keyof EnvironmentConfigNormalizedOverride\n | keyof OrganizationConfigNormalizedOverride\n>;\ntype EnvironmentRenderedConfigBeforeDefaults = Omit<EnvironmentIncompleteConfig,\n | keyof OrganizationConfigNormalizedOverride\n>;\ntype OrganizationRenderedConfigBeforeDefaults = OrganizationIncompleteConfig;\n\n// Rendered configs before sanitization\ntype ProjectRenderedConfigBeforeSanitization = Expand<Awaited<ReturnType<typeof applyProjectDefaults<ProjectRenderedConfigBeforeDefaults>>>>;\ntype BranchRenderedConfigBeforeSanitization = Expand<Awaited<ReturnType<typeof applyBranchDefaults<BranchRenderedConfigBeforeDefaults>>>>;\ntype EnvironmentRenderedConfigBeforeSanitization = Expand<Awaited<ReturnType<typeof applyEnvironmentDefaults<EnvironmentRenderedConfigBeforeDefaults>>>>;\ntype OrganizationRenderedConfigBeforeSanitization = Expand<Awaited<ReturnType<typeof applyOrganizationDefaults>>>;\n\n// Rendered configs after defaults, normalization, and sanitization\nexport type ProjectRenderedConfig = Expand<Awaited<ReturnType<typeof sanitizeProjectConfig<ProjectRenderedConfigBeforeSanitization>>>>;\nexport type BranchRenderedConfig = Expand<Awaited<ReturnType<typeof sanitizeBranchConfig<BranchRenderedConfigBeforeSanitization>>>>;\nexport type EnvironmentRenderedConfig = Expand<Awaited<ReturnType<typeof sanitizeEnvironmentConfig<EnvironmentRenderedConfigBeforeSanitization>>>>;\nexport type OrganizationRenderedConfig = Expand<Awaited<ReturnType<typeof sanitizeOrganizationConfig>>>;\n\n\n// Type assertions (just to make sure the types are correct)\nconst __assertEmptyObjectIsValidProjectOverride: ProjectConfigOverride = {};\nconst __assertEmptyObjectIsValidBranchOverride: BranchConfigOverride = {};\nconst __assertEmptyObjectIsValidEnvironmentOverride: EnvironmentConfigOverride = {};\nconst __assertEmptyObjectIsValidOrganizationOverride: OrganizationConfigOverride = {};\ntypeAssertExtends<ProjectRenderedConfig, { \"sourceOfTruth\": any }>()();\ntypeAssertExtends<BranchRenderedConfig, { \"sourceOfTruth\": any }>()();\ntypeAssertExtends<EnvironmentRenderedConfig, { \"sourceOfTruth\": any }>()();\ntypeAssertExtends<OrganizationRenderedConfig, { \"sourceOfTruth\": any }>()();\ntypeAssert<BranchRenderedConfig extends { \"domains\": any } ? false : true>()();\ntypeAssert<EnvironmentRenderedConfig extends { \"domains\": any } ? false : true>()();\ntypeAssertExtends<OrganizationRenderedConfig, { \"domains\": any }>()();\n"],"mappings":"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","names":["key","schema"]}
package/dist/crud.d.mts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as yup from 'yup';
2
2
  import { NullishCoalesce } from './utils/types.mjs';
3
+ import './utils/strings.mjs';
3
4
 
4
5
  type AccessType = "client" | "server" | "admin";
5
6
  type CrudOperation = "create" | "read" | "update" | "delete";
package/dist/crud.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as yup from 'yup';
2
2
  import { NullishCoalesce } from './utils/types.js';
3
+ import './utils/strings.js';
3
4
 
4
5
  type AccessType = "client" | "server" | "admin";
5
6
  type CrudOperation = "create" | "read" | "update" | "delete";
@@ -72,9 +72,23 @@ var NormalizationError = class extends Error {
72
72
  }
73
73
  };
74
74
  NormalizationError.prototype.name = "NormalizationError";
75
+ function isNormalized(c) {
76
+ assertValidConfig(c);
77
+ for (const [key, value] of Object.entries(c)) {
78
+ if (value === void 0) continue;
79
+ if (key.includes(".")) return false;
80
+ if (value === null) return false;
81
+ }
82
+ return true;
83
+ }
84
+ function assertNormalized(c) {
85
+ assertValidConfig(c);
86
+ if (!isNormalized(c)) throw new StackAssertionError(`Config is not normalized: ${JSON.stringify(c)}`);
87
+ }
75
88
  function normalize(c, options = {}) {
76
89
  assertValidConfig(c);
77
- const onDotIntoNull = options.onDotIntoNull ?? "empty";
90
+ const onDotIntoNonObject = options.onDotIntoNonObject ?? "throw";
91
+ const onDotIntoNull = options.onDotIntoNull ?? "like-non-object";
78
92
  const countDots = (s) => s.match(/\./g)?.length ?? 0;
79
93
  const result = {};
80
94
  const keysByDepth = Object.keys(c).sort((a, b) => countDots(a) - countDots(b));
@@ -86,48 +100,57 @@ function normalize(c, options = {}) {
86
100
  let current = result;
87
101
  for (const keySegment of keySegmentsWithoutLast) {
88
102
  if (!hasAndNotUndefined(current, keySegment)) {
89
- switch (onDotIntoNull) {
90
- case "empty": {
91
- set(current, keySegment, {});
92
- break;
93
- }
103
+ switch (onDotIntoNull === "like-non-object" ? onDotIntoNonObject : onDotIntoNull) {
94
104
  case "throw": {
95
- throw new NormalizationError(`Tried to use dot notation to access ${JSON.stringify(key)}, but ${JSON.stringify(keySegment)} doesn't exist on the object (or is null). Maybe this config is not normalizable?`);
105
+ throw new NormalizationError(`Tried to use dot notation to access ${JSON.stringify(key)}, but ${JSON.stringify(keySegment)} doesn't exist on the object (or is null).`);
96
106
  }
97
107
  case "ignore": {
98
108
  continue outer;
99
109
  }
110
+ case "empty-object": {
111
+ set(current, keySegment, {});
112
+ break;
113
+ }
100
114
  }
101
115
  }
102
116
  const value2 = get(current, keySegment);
103
117
  if (typeof value2 !== "object") {
104
- throw new NormalizationError(`Tried to use dot notation to access ${JSON.stringify(key)}, but ${JSON.stringify(keySegment)} is not an object. Maybe this config is not normalizable?`);
118
+ switch (onDotIntoNonObject) {
119
+ case "throw": {
120
+ throw new NormalizationError(`Tried to use dot notation to access ${JSON.stringify(key)}, but ${JSON.stringify(keySegment)} is not an object.`);
121
+ }
122
+ case "ignore": {
123
+ continue outer;
124
+ }
125
+ }
105
126
  }
106
127
  current = value2;
107
128
  }
108
- setNormalizedValue(current, last, value);
129
+ setNormalizedValue(current, last, value, options);
109
130
  }
110
131
  return result;
111
132
  }
112
- function normalizeValue(value) {
133
+ function normalizeValue(value, options) {
113
134
  if (value === null) throw new NormalizationError("Tried to normalize a null value");
114
- if (Array.isArray(value)) return value.map(normalizeValue);
115
- if (typeof value === "object") return normalize(value);
135
+ if (Array.isArray(value)) return value.map((v) => normalizeValue(v, options));
136
+ if (typeof value === "object") return normalize(value, options);
116
137
  return value;
117
138
  }
118
- function setNormalizedValue(result, key, value) {
139
+ function setNormalizedValue(result, key, value, options) {
119
140
  if (value === null) {
120
141
  if (hasAndNotUndefined(result, key)) {
121
142
  deleteKey(result, key);
122
143
  }
123
144
  } else {
124
- set(result, key, normalizeValue(value));
145
+ set(result, key, normalizeValue(value, options));
125
146
  }
126
147
  }
127
148
  export {
128
149
  NormalizationError,
150
+ assertNormalized,
129
151
  assertValidConfig,
130
152
  getInvalidConfigReason,
153
+ isNormalized,
131
154
  isValidConfig,
132
155
  normalize,
133
156
  override
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/config/format.ts"],"sourcesContent":["// see https://github.com/stack-auth/info/blob/main/eng-handbook/random-thoughts/config-json-format.md\n\nimport { StackAssertionError, throwErr } from \"../utils/errors\";\nimport { deleteKey, filterUndefined, get, hasAndNotUndefined, set } from \"../utils/objects\";\n\n\nexport type ConfigValue = string | number | boolean | null | ConfigValue[] | Config;\nexport type Config = {\n [keyOrDotNotation: string]: ConfigValue | undefined, // must support undefined for optional values\n};\n\nexport type NormalizedConfigValue = string | number | boolean | NormalizedConfig | NormalizedConfigValue[];\nexport type NormalizedConfig = {\n [key: string]: NormalizedConfigValue | undefined, // must support undefined for optional values\n};\n\nexport type _NormalizesTo<N> = N extends object ? (\n & Config\n & { [K in keyof N]?: _NormalizesTo<N[K]> | null }\n & { [K in `${string}.${string}`]: ConfigValue }\n) : N;\nexport type NormalizesTo<N extends NormalizedConfig> = _NormalizesTo<N>;\n\n/**\n * Note that a config can both be valid and not normalizable.\n */\nexport function isValidConfig(c: unknown): c is Config {\n return getInvalidConfigReason(c) === undefined;\n}\n\nexport function getInvalidConfigReason(c: unknown, options: { configName?: string } = {}): string | undefined {\n const configName = options.configName ?? 'config';\n if (c === null || typeof c !== 'object') return `${configName} must be a non-null object`;\n for (const [key, value] of Object.entries(c)) {\n if (value === undefined) continue;\n if (typeof key !== 'string') return `${configName} must have only string keys (found: ${typeof key})`;\n if (!key.match(/^[a-zA-Z0-9_:$][a-zA-Z_:$0-9\\-]*(?:\\.[a-zA-Z0-9_:$][a-zA-Z_:$0-9\\-]*)*$/)) return `All keys of ${configName} must consist of only alphanumeric characters, dots, underscores, colons, dollar signs, or hyphens and start with a character other than a hyphen (found: ${key})`;\n\n const entryName = `${configName}.${key}`;\n const reason = getInvalidConfigValueReason(value, { valueName: entryName });\n if (reason) return reason;\n }\n return undefined;\n}\n\nfunction getInvalidConfigValueReason(value: unknown, options: { valueName?: string } = {}): string | undefined {\n const valueName = options.valueName ?? 'value';\n switch (typeof value) {\n case 'string':\n case 'number':\n case 'boolean': {\n break;\n }\n case 'object': {\n if (value === null) {\n break;\n } else if (Array.isArray(value)) {\n for (const [index, v] of value.entries()) {\n const reason = getInvalidConfigValueReason(v, { valueName: `${valueName}[${index}]` });\n if (reason) return reason;\n }\n } else {\n const reason = getInvalidConfigReason(value, { configName: valueName });\n if (reason) return reason;\n }\n break;\n }\n default: {\n return `${valueName} has an invalid value type ${typeof value} (value: ${value})`;\n }\n }\n return undefined;\n}\n\nexport function assertValidConfig(c: unknown) {\n const reason = getInvalidConfigReason(c);\n if (reason) throw new StackAssertionError(`Invalid config: ${reason}`, { c });\n}\n\nexport function override(c1: Config, ...configs: Config[]) {\n if (configs.length === 0) return c1;\n if (configs.length > 1) return override(override(c1, configs[0]), ...configs.slice(1));\n const c2 = configs[0];\n\n assertValidConfig(c1);\n assertValidConfig(c2);\n\n let result = c1;\n for (const key of Object.keys(filterUndefined(c2))) {\n result = Object.fromEntries(\n Object.entries(result).filter(([k]) => k !== key && !k.startsWith(key + '.'))\n );\n }\n\n return {\n ...result,\n ...filterUndefined(c2),\n };\n}\n\nundefined?.test(\"override(...)\", ({ expect }) => {\n expect(\n override(\n {\n a: 1,\n b: 2,\n \"c.d\": 3,\n \"c.e.f\": 4,\n \"c.g\": 5,\n h: [6, { i: 7 }, 8],\n k: 123,\n l: undefined,\n },\n {\n a: 9,\n \"c.d\": 10,\n \"c.e\": null,\n \"h.0\": 11,\n \"h.1\": {\n j: 12,\n },\n k: undefined,\n },\n )\n ).toEqual({\n a: 9,\n b: 2,\n \"c.d\": 10,\n \"c.e\": null,\n \"c.g\": 5,\n h: [6, { i: 7 }, 8],\n \"h.0\": 11,\n \"h.1\": {\n j: 12,\n },\n k: 123,\n l: undefined,\n });\n});\n\ntype NormalizeOptions = {\n /**\n * What to do if a dot notation is used on null.\n *\n * - \"empty\" (default): Replace the null with an empty object.\n * - \"throw\": Throw an error.\n * - \"ignore\": Ignore the dot notation field.\n */\n onDotIntoNull?: \"empty\" | \"throw\" | \"ignore\",\n}\n\nexport class NormalizationError extends Error {\n constructor(...args: ConstructorParameters<typeof Error>) {\n super(...args);\n }\n}\nNormalizationError.prototype.name = \"NormalizationError\";\n\nexport function normalize(c: Config, options: NormalizeOptions = {}): NormalizedConfig {\n assertValidConfig(c);\n const onDotIntoNull = options.onDotIntoNull ?? \"empty\";\n\n const countDots = (s: string) => s.match(/\\./g)?.length ?? 0;\n const result: NormalizedConfig = {};\n const keysByDepth = Object.keys(c).sort((a, b) => countDots(a) - countDots(b));\n\n outer: for (const key of keysByDepth) {\n const keySegmentsWithoutLast = key.split('.');\n const last = keySegmentsWithoutLast.pop() ?? throwErr('split returns empty array?');\n const value = get(c, key);\n if (value === undefined) continue;\n\n let current: NormalizedConfig = result;\n for (const keySegment of keySegmentsWithoutLast) {\n if (!hasAndNotUndefined(current, keySegment)) {\n switch (onDotIntoNull) {\n case \"empty\": {\n set(current, keySegment, {});\n break;\n }\n case \"throw\": {\n throw new NormalizationError(`Tried to use dot notation to access ${JSON.stringify(key)}, but ${JSON.stringify(keySegment)} doesn't exist on the object (or is null). Maybe this config is not normalizable?`);\n }\n case \"ignore\": {\n continue outer;\n }\n }\n }\n const value = get(current, keySegment);\n if (typeof value !== 'object') {\n throw new NormalizationError(`Tried to use dot notation to access ${JSON.stringify(key)}, but ${JSON.stringify(keySegment)} is not an object. Maybe this config is not normalizable?`);\n }\n current = value as NormalizedConfig;\n }\n setNormalizedValue(current, last, value);\n }\n return result;\n}\n\nfunction normalizeValue(value: ConfigValue): NormalizedConfigValue {\n if (value === null) throw new NormalizationError(\"Tried to normalize a null value\");\n if (Array.isArray(value)) return value.map(normalizeValue);\n if (typeof value === 'object') return normalize(value);\n return value;\n}\n\nfunction setNormalizedValue(result: NormalizedConfig, key: string, value: ConfigValue) {\n if (value === null) {\n if (hasAndNotUndefined(result, key)) {\n deleteKey(result, key);\n }\n } else {\n set(result, key, normalizeValue(value));\n }\n}\n\nundefined?.test(\"normalize(...)\", ({ expect }) => {\n expect(normalize({\n a: 9,\n b: 2,\n c: {},\n \"c.d\": 10,\n \"c.e\": null,\n \"c.g\": 5,\n h: [6, { i: 7 }, 8],\n \"h.0\": 11,\n \"h.1\": {\n j: 12,\n },\n k: { l: {} },\n \"k.l.m\": 13,\n n: undefined,\n })).toEqual({\n a: 9,\n b: 2,\n c: {\n d: 10,\n g: 5,\n },\n h: [11, { j: 12 }, 8],\n k: { l: { m: 13 } },\n });\n\n // dotting into null\n expect(normalize({\n \"b.c\": 2,\n })).toEqual({ b: { c: 2 } });\n expect(() => normalize({\n \"b.c\": 2,\n }, { onDotIntoNull: \"throw\" })).toThrow(`Tried to use dot notation to access \"b.c\", but \"b\" doesn't exist on the object (or is null). Maybe this config is not normalizable?`);\n expect(() => normalize({\n b: null,\n \"b.c\": 2,\n }, { onDotIntoNull: \"throw\" })).toThrow(`Tried to use dot notation to access \"b.c\", but \"b\" doesn't exist on the object (or is null). Maybe this config is not normalizable?`);\n expect(normalize({\n \"b.c\": 2,\n }, { onDotIntoNull: \"ignore\" })).toEqual({});\n\n // dotting into non-object\n expect(() => normalize({\n b: 1,\n \"b.c\": 2,\n })).toThrow(`Tried to use dot notation to access \"b.c\", but \"b\" is not an object. Maybe this config is not normalizable?`);\n});\n"],"mappings":"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","names":["value"]}
1
+ {"version":3,"sources":["../../../src/config/format.ts"],"sourcesContent":["// see https://github.com/stack-auth/info/blob/main/eng-handbook/random-thoughts/config-json-format.md\n\nimport { StackAssertionError, throwErr } from \"../utils/errors\";\nimport { deleteKey, filterUndefined, get, hasAndNotUndefined, set } from \"../utils/objects\";\nimport { OptionalKeys, RequiredKeys } from \"../utils/types\";\n\n\nexport type ConfigValue = string | number | boolean | null | ConfigValue[] | Config;\nexport type Config = {\n [keyOrDotNotation: string]: ConfigValue | undefined, // must support undefined for optional values\n};\n\nexport type NormalizedConfigValue = string | number | boolean | NormalizedConfig | NormalizedConfigValue[];\nexport type NormalizedConfig = {\n [key: string]: NormalizedConfigValue | undefined, // must support undefined for optional values\n};\n\nexport type _NormalizesTo<N> = N extends object ? (\n & Config\n & { [K in OptionalKeys<N>]?: _NormalizesTo<N[K]> | null }\n & { [K in RequiredKeys<N>]: undefined extends N[K] ? _NormalizesTo<N[K]> | null : _NormalizesTo<N[K]> }\n & { [K in `${string}.${string}`]: ConfigValue }\n) : N;\nexport type NormalizesTo<N extends NormalizedConfig> = _NormalizesTo<N>;\n\n/**\n * Note that a config can both be valid and not normalizable.\n */\nexport function isValidConfig(c: unknown): c is Config {\n return getInvalidConfigReason(c) === undefined;\n}\n\nexport function getInvalidConfigReason(c: unknown, options: { configName?: string } = {}): string | undefined {\n const configName = options.configName ?? 'config';\n if (c === null || typeof c !== 'object') return `${configName} must be a non-null object`;\n for (const [key, value] of Object.entries(c)) {\n if (value === undefined) continue;\n if (typeof key !== 'string') return `${configName} must have only string keys (found: ${typeof key})`;\n if (!key.match(/^[a-zA-Z0-9_:$][a-zA-Z_:$0-9\\-]*(?:\\.[a-zA-Z0-9_:$][a-zA-Z_:$0-9\\-]*)*$/)) return `All keys of ${configName} must consist of only alphanumeric characters, dots, underscores, colons, dollar signs, or hyphens and start with a character other than a hyphen (found: ${key})`;\n\n const entryName = `${configName}.${key}`;\n const reason = getInvalidConfigValueReason(value, { valueName: entryName });\n if (reason) return reason;\n }\n return undefined;\n}\n\nfunction getInvalidConfigValueReason(value: unknown, options: { valueName?: string } = {}): string | undefined {\n const valueName = options.valueName ?? 'value';\n switch (typeof value) {\n case 'string':\n case 'number':\n case 'boolean': {\n break;\n }\n case 'object': {\n if (value === null) {\n break;\n } else if (Array.isArray(value)) {\n for (const [index, v] of value.entries()) {\n const reason = getInvalidConfigValueReason(v, { valueName: `${valueName}[${index}]` });\n if (reason) return reason;\n }\n } else {\n const reason = getInvalidConfigReason(value, { configName: valueName });\n if (reason) return reason;\n }\n break;\n }\n default: {\n return `${valueName} has an invalid value type ${typeof value} (value: ${value})`;\n }\n }\n return undefined;\n}\n\nexport function assertValidConfig(c: unknown) {\n const reason = getInvalidConfigReason(c);\n if (reason) throw new StackAssertionError(`Invalid config: ${reason}`, { c });\n}\n\nexport function override(c1: Config, ...configs: Config[]) {\n if (configs.length === 0) return c1;\n if (configs.length > 1) return override(override(c1, configs[0]), ...configs.slice(1));\n const c2 = configs[0];\n\n assertValidConfig(c1);\n assertValidConfig(c2);\n\n let result = c1;\n for (const key of Object.keys(filterUndefined(c2))) {\n result = Object.fromEntries(\n Object.entries(result).filter(([k]) => k !== key && !k.startsWith(key + '.'))\n );\n }\n\n return {\n ...result,\n ...filterUndefined(c2),\n };\n}\n\nundefined?.test(\"override(...)\", ({ expect }) => {\n expect(\n override(\n {\n a: 1,\n b: 2,\n \"c.d\": 3,\n \"c.e.f\": 4,\n \"c.g\": 5,\n h: [6, { i: 7 }, 8],\n k: 123,\n l: undefined,\n },\n {\n a: 9,\n \"c.d\": 10,\n \"c.e\": null,\n \"h.0\": 11,\n \"h.1\": {\n j: 12,\n },\n k: undefined,\n },\n )\n ).toEqual({\n a: 9,\n b: 2,\n \"c.d\": 10,\n \"c.e\": null,\n \"c.g\": 5,\n h: [6, { i: 7 }, 8],\n \"h.0\": 11,\n \"h.1\": {\n j: 12,\n },\n k: 123,\n l: undefined,\n });\n});\n\ntype NormalizeOptions = {\n /**\n * What to do if a dot notation is used on a value that is not an object.\n *\n * - \"throw\" (default): Throw an error.\n * - \"ignore\": Ignore the dot notation field.\n */\n onDotIntoNonObject?: \"throw\" | \"ignore\",\n /**\n * What to do if a dot notation is used on a value that is null.\n *\n * - \"like-non-object\" (default): Treat it like a non-object. See `onDotIntoNonObject`.\n * - \"throw\": Throw an error.\n * - \"ignore\": Ignore the dot notation field.\n * - \"empty-object\": Set the value to an empty object.\n */\n onDotIntoNull?: \"like-non-object\" | \"throw\" | \"ignore\" | \"empty-object\",\n}\n\nexport class NormalizationError extends Error {\n constructor(...args: ConstructorParameters<typeof Error>) {\n super(...args);\n }\n}\nNormalizationError.prototype.name = \"NormalizationError\";\n\nexport function isNormalized(c: Config): c is NormalizedConfig {\n assertValidConfig(c);\n for (const [key, value] of Object.entries(c)) {\n if (value === undefined) continue;\n if (key.includes('.')) return false;\n if (value === null) return false;\n }\n return true;\n}\n\nexport function assertNormalized(c: Config): asserts c is NormalizedConfig {\n assertValidConfig(c);\n if (!isNormalized(c)) throw new StackAssertionError(`Config is not normalized: ${JSON.stringify(c)}`);\n}\n\nexport function normalize(c: Config, options: NormalizeOptions = {}): NormalizedConfig {\n assertValidConfig(c);\n const onDotIntoNonObject = options.onDotIntoNonObject ?? \"throw\";\n const onDotIntoNull = options.onDotIntoNull ?? \"like-non-object\";\n\n const countDots = (s: string) => s.match(/\\./g)?.length ?? 0;\n const result: NormalizedConfig = {};\n const keysByDepth = Object.keys(c).sort((a, b) => countDots(a) - countDots(b));\n\n outer: for (const key of keysByDepth) {\n const keySegmentsWithoutLast = key.split('.');\n const last = keySegmentsWithoutLast.pop() ?? throwErr('split returns empty array?');\n const value = get(c, key);\n if (value === undefined) continue;\n\n let current: NormalizedConfig = result;\n for (const keySegment of keySegmentsWithoutLast) {\n if (!hasAndNotUndefined(current, keySegment)) {\n switch (onDotIntoNull === \"like-non-object\" ? onDotIntoNonObject : onDotIntoNull) {\n case \"throw\": {\n throw new NormalizationError(`Tried to use dot notation to access ${JSON.stringify(key)}, but ${JSON.stringify(keySegment)} doesn't exist on the object (or is null).`);\n }\n case \"ignore\": {\n continue outer;\n }\n case \"empty-object\": {\n set(current, keySegment, {});\n break;\n }\n }\n }\n const value = get(current, keySegment);\n if (typeof value !== 'object') {\n switch (onDotIntoNonObject) {\n case \"throw\": {\n throw new NormalizationError(`Tried to use dot notation to access ${JSON.stringify(key)}, but ${JSON.stringify(keySegment)} is not an object.`);\n }\n case \"ignore\": {\n continue outer;\n }\n }\n }\n current = value as NormalizedConfig;\n }\n setNormalizedValue(current, last, value, options);\n }\n return result;\n}\n\nfunction normalizeValue(value: ConfigValue, options: NormalizeOptions): NormalizedConfigValue {\n if (value === null) throw new NormalizationError(\"Tried to normalize a null value\");\n if (Array.isArray(value)) return value.map(v => normalizeValue(v, options));\n if (typeof value === 'object') return normalize(value, options);\n return value;\n}\n\nfunction setNormalizedValue(result: NormalizedConfig, key: string, value: ConfigValue, options: NormalizeOptions) {\n if (value === null) {\n if (hasAndNotUndefined(result, key)) {\n deleteKey(result, key);\n }\n } else {\n set(result, key, normalizeValue(value, options));\n }\n}\n\nundefined?.test(\"normalize(...)\", ({ expect }) => {\n expect(normalize({\n a: 9,\n b: 2,\n c: {},\n \"c.d\": 10,\n \"c.e\": null,\n \"c.g\": 5,\n h: [6, { i: 7 }, 8],\n \"h.0\": 11,\n \"h.1\": {\n j: 12,\n },\n k: { l: {} },\n \"k.l.m\": 13,\n n: undefined,\n }, { onDotIntoNonObject: \"ignore\" })).toEqual({\n a: 9,\n b: 2,\n c: {\n d: 10,\n g: 5,\n },\n h: [11, { j: 12 }, 8],\n k: { l: { m: 13 } },\n });\n\n // dotting into null\n expect(() => normalize({\n \"b.c\": 2,\n }, { onDotIntoNonObject: \"throw\" })).toThrow(`Tried to use dot notation to access \"b.c\", but \"b\" doesn't exist on the object (or is null)`);\n expect(() => normalize({\n b: null,\n \"b.c\": 2,\n }, { onDotIntoNonObject: \"throw\" })).toThrow(`Tried to use dot notation to access \"b.c\", but \"b\" doesn't exist on the object (or is null)`);\n expect(normalize({\n \"b.c\": 2,\n }, { onDotIntoNonObject: \"ignore\" })).toEqual({});\n\n // dotting into non-object\n expect(() => normalize({\n b: 1,\n \"b.c\": 2,\n }, { onDotIntoNonObject: \"throw\" })).toThrow(`Tried to use dot notation to access \"b.c\", but \"b\" is not an object`);\n expect(normalize({\n b: 1,\n \"b.c\": 2,\n }, { onDotIntoNonObject: \"ignore\" })).toEqual({ b: 1 });\n});\n"],"mappings":"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","names":["value"]}