@stackframe/stack-shared 2.8.22 → 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 (164) hide show
  1. package/CHANGELOG.md +30 -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 +816 -294
  7. package/dist/config/schema.d.ts +816 -294
  8. package/dist/config/schema.js +471 -82
  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 -78
  15. package/dist/esm/config/schema.js.map +1 -1
  16. package/dist/esm/helpers/emails.js +185 -0
  17. package/dist/esm/helpers/emails.js.map +1 -0
  18. package/dist/esm/interface/admin-interface.js +58 -36
  19. package/dist/esm/interface/admin-interface.js.map +1 -1
  20. package/dist/esm/interface/client-interface.js +41 -0
  21. package/dist/esm/interface/client-interface.js.map +1 -1
  22. package/dist/esm/interface/crud/{oauth.js → connected-accounts.js} +2 -2
  23. package/dist/esm/interface/crud/connected-accounts.js.map +1 -0
  24. package/dist/esm/interface/crud/oauth-providers.js +87 -0
  25. package/dist/esm/interface/crud/oauth-providers.js.map +1 -0
  26. package/dist/esm/interface/crud/projects.js +7 -5
  27. package/dist/esm/interface/crud/projects.js.map +1 -1
  28. package/dist/esm/interface/server-interface.js +51 -0
  29. package/dist/esm/interface/server-interface.js.map +1 -1
  30. package/dist/esm/known-errors.js +34 -1
  31. package/dist/esm/known-errors.js.map +1 -1
  32. package/dist/esm/schema-fields.js +112 -24
  33. package/dist/esm/schema-fields.js.map +1 -1
  34. package/dist/esm/utils/currencies.js +52 -0
  35. package/dist/esm/utils/currencies.js.map +1 -0
  36. package/dist/esm/utils/dates.js +55 -1
  37. package/dist/esm/utils/dates.js.map +1 -1
  38. package/dist/esm/utils/errors.js.map +1 -1
  39. package/dist/esm/utils/esbuild.js +14 -4
  40. package/dist/esm/utils/esbuild.js.map +1 -1
  41. package/dist/esm/utils/oauth.js +1 -1
  42. package/dist/esm/utils/oauth.js.map +1 -1
  43. package/dist/esm/utils/objects.js +2 -0
  44. package/dist/esm/utils/objects.js.map +1 -1
  45. package/dist/esm/utils/strings.js +4 -0
  46. package/dist/esm/utils/strings.js.map +1 -1
  47. package/dist/esm/utils/types.js +45 -0
  48. package/dist/esm/utils/types.js.map +1 -1
  49. package/dist/helpers/emails.d.mts +50 -0
  50. package/dist/helpers/emails.d.ts +50 -0
  51. package/dist/helpers/emails.js +216 -0
  52. package/dist/helpers/emails.js.map +1 -0
  53. package/dist/helpers/password.d.mts +1 -0
  54. package/dist/helpers/password.d.ts +1 -0
  55. package/dist/helpers/production-mode.d.mts +1 -0
  56. package/dist/helpers/production-mode.d.ts +1 -0
  57. package/dist/index.d.mts +3 -3
  58. package/dist/index.d.ts +3 -3
  59. package/dist/interface/admin-interface.d.mts +24 -15
  60. package/dist/interface/admin-interface.d.ts +24 -15
  61. package/dist/interface/admin-interface.js +58 -36
  62. package/dist/interface/admin-interface.js.map +1 -1
  63. package/dist/interface/client-interface.d.mts +25 -1
  64. package/dist/interface/client-interface.d.ts +25 -1
  65. package/dist/interface/client-interface.js +41 -0
  66. package/dist/interface/client-interface.js.map +1 -1
  67. package/dist/interface/crud/{oauth.d.mts → connected-accounts.d.mts} +1 -0
  68. package/dist/interface/crud/{oauth.d.ts → connected-accounts.d.ts} +1 -0
  69. package/dist/interface/crud/{oauth.js → connected-accounts.js} +5 -5
  70. package/dist/interface/crud/connected-accounts.js.map +1 -0
  71. package/dist/interface/crud/contact-channels.d.mts +1 -0
  72. package/dist/interface/crud/contact-channels.d.ts +1 -0
  73. package/dist/interface/crud/current-user.d.mts +1 -0
  74. package/dist/interface/crud/current-user.d.ts +1 -0
  75. package/dist/interface/crud/email-templates.d.mts +1 -0
  76. package/dist/interface/crud/email-templates.d.ts +1 -0
  77. package/dist/interface/crud/emails.d.mts +1 -0
  78. package/dist/interface/crud/emails.d.ts +1 -0
  79. package/dist/interface/crud/internal-api-keys.d.mts +1 -0
  80. package/dist/interface/crud/internal-api-keys.d.ts +1 -0
  81. package/dist/interface/crud/notification-preferences.d.mts +1 -0
  82. package/dist/interface/crud/notification-preferences.d.ts +1 -0
  83. package/dist/interface/crud/oauth-providers.d.mts +173 -0
  84. package/dist/interface/crud/oauth-providers.d.ts +173 -0
  85. package/dist/interface/crud/oauth-providers.js +107 -0
  86. package/dist/interface/crud/oauth-providers.js.map +1 -0
  87. package/dist/interface/crud/project-api-keys.d.mts +1 -0
  88. package/dist/interface/crud/project-api-keys.d.ts +1 -0
  89. package/dist/interface/crud/project-permissions.d.mts +1 -0
  90. package/dist/interface/crud/project-permissions.d.ts +1 -0
  91. package/dist/interface/crud/projects.d.mts +16 -12
  92. package/dist/interface/crud/projects.d.ts +16 -12
  93. package/dist/interface/crud/projects.js +5 -3
  94. package/dist/interface/crud/projects.js.map +1 -1
  95. package/dist/interface/crud/sessions.d.mts +1 -0
  96. package/dist/interface/crud/sessions.d.ts +1 -0
  97. package/dist/interface/crud/svix-token.d.mts +1 -0
  98. package/dist/interface/crud/svix-token.d.ts +1 -0
  99. package/dist/interface/crud/team-invitation-details.d.mts +1 -0
  100. package/dist/interface/crud/team-invitation-details.d.ts +1 -0
  101. package/dist/interface/crud/team-invitation.d.mts +1 -0
  102. package/dist/interface/crud/team-invitation.d.ts +1 -0
  103. package/dist/interface/crud/team-member-profiles.d.mts +1 -0
  104. package/dist/interface/crud/team-member-profiles.d.ts +1 -0
  105. package/dist/interface/crud/team-memberships.d.mts +1 -0
  106. package/dist/interface/crud/team-memberships.d.ts +1 -0
  107. package/dist/interface/crud/team-permissions.d.mts +1 -0
  108. package/dist/interface/crud/team-permissions.d.ts +1 -0
  109. package/dist/interface/crud/teams.d.mts +1 -0
  110. package/dist/interface/crud/teams.d.ts +1 -0
  111. package/dist/interface/crud/users.d.mts +1 -0
  112. package/dist/interface/crud/users.d.ts +1 -0
  113. package/dist/interface/server-interface.d.mts +46 -1
  114. package/dist/interface/server-interface.d.ts +46 -1
  115. package/dist/interface/server-interface.js +51 -0
  116. package/dist/interface/server-interface.js.map +1 -1
  117. package/dist/known-errors.d.mts +9 -0
  118. package/dist/known-errors.d.ts +9 -0
  119. package/dist/known-errors.js +34 -1
  120. package/dist/known-errors.js.map +1 -1
  121. package/dist/schema-fields.d.mts +51 -7
  122. package/dist/schema-fields.d.ts +51 -7
  123. package/dist/schema-fields.js +128 -24
  124. package/dist/schema-fields.js.map +1 -1
  125. package/dist/utils/currencies.d.mts +39 -0
  126. package/dist/utils/currencies.d.ts +39 -0
  127. package/dist/utils/currencies.js +78 -0
  128. package/dist/utils/currencies.js.map +1 -0
  129. package/dist/utils/dates.d.mts +5 -1
  130. package/dist/utils/dates.d.ts +5 -1
  131. package/dist/utils/dates.js +58 -2
  132. package/dist/utils/dates.js.map +1 -1
  133. package/dist/utils/errors.d.mts +9 -0
  134. package/dist/utils/errors.d.ts +9 -0
  135. package/dist/utils/errors.js.map +1 -1
  136. package/dist/utils/esbuild.d.mts +3 -0
  137. package/dist/utils/esbuild.d.ts +3 -0
  138. package/dist/utils/esbuild.js +14 -4
  139. package/dist/utils/esbuild.js.map +1 -1
  140. package/dist/utils/oauth.d.mts +2 -2
  141. package/dist/utils/oauth.d.ts +2 -2
  142. package/dist/utils/oauth.js +1 -1
  143. package/dist/utils/oauth.js.map +1 -1
  144. package/dist/utils/objects.d.mts +23 -8
  145. package/dist/utils/objects.d.ts +23 -8
  146. package/dist/utils/objects.js +2 -0
  147. package/dist/utils/objects.js.map +1 -1
  148. package/dist/utils/strings.d.mts +3 -1
  149. package/dist/utils/strings.d.ts +3 -1
  150. package/dist/utils/strings.js +5 -0
  151. package/dist/utils/strings.js.map +1 -1
  152. package/dist/utils/types.d.mts +73 -2
  153. package/dist/utils/types.d.ts +73 -2
  154. package/dist/utils/types.js +54 -0
  155. package/dist/utils/types.js.map +1 -1
  156. package/package.json +1 -1
  157. package/dist/esm/helpers/email-themes.js +0 -45
  158. package/dist/esm/helpers/email-themes.js.map +0 -1
  159. package/dist/esm/interface/crud/oauth.js.map +0 -1
  160. package/dist/helpers/email-themes.d.mts +0 -13
  161. package/dist/helpers/email-themes.d.ts +0 -13
  162. package/dist/helpers/email-themes.js +0 -71
  163. package/dist/helpers/email-themes.js.map +0 -1
  164. package/dist/interface/crud/oauth.js.map +0 -1
@@ -1,109 +1,122 @@
1
1
  // src/config/schema.ts
2
+ import * as yup from "yup";
3
+ import { DEFAULT_EMAIL_TEMPLATES, DEFAULT_EMAIL_THEMES, DEFAULT_EMAIL_THEME_ID } from "../helpers/emails.js";
2
4
  import * as schemaFields from "../schema-fields.js";
3
- import { yupBoolean, yupObject, yupRecord, yupString, yupUnion } from "../schema-fields.js";
5
+ import { yupBoolean, yupDate, yupMixed, yupNever, yupNumber, yupObject, yupRecord, yupString, yupTuple, yupUnion } from "../schema-fields.js";
6
+ import { isShallowEqual } from "../utils/arrays.js";
7
+ import { StackAssertionError } from "../utils/errors.js";
4
8
  import { allProviders } from "../utils/oauth.js";
5
- import { get, has, isObjectLike, mapValues, set } from "../utils/objects.js";
6
- import { DEFAULT_EMAIL_THEME_ID, DEFAULT_EMAIL_THEMES } from "../helpers/email-themes.js";
9
+ import { deleteKey, filterUndefined, get, has, isObjectLike, mapValues, set, typedAssign, typedFromEntries } from "../utils/objects.js";
10
+ import { Result } from "../utils/results.js";
11
+ import { typeAssert, typeAssertExtends, typeAssertIs } from "../utils/types.js";
12
+ import { NormalizationError, assertNormalized, getInvalidConfigReason, normalize } from "./format.js";
7
13
  var configLevels = ["project", "branch", "environment", "organization"];
8
14
  var permissionRegex = /^\$?[a-z0-9_:]+$/;
9
15
  var customPermissionRegex = /^[a-z0-9_:]+$/;
16
+ function canNoLongerBeOverridden(schema, keys) {
17
+ const notOmitted = schema.concat(yupObject(
18
+ Object.fromEntries(keys.map((key) => [key, schema.getNested(key).meta({ stackConfigCanNoLongerBeOverridden: true })]))
19
+ ));
20
+ return notOmitted;
21
+ }
10
22
  var projectConfigSchema = yupObject({
11
23
  sourceOfTruth: yupUnion(
12
24
  yupObject({
13
- type: yupString().oneOf(["hosted"]).optional()
14
- }).defined(),
25
+ type: yupString().oneOf(["hosted"]).defined()
26
+ }),
15
27
  yupObject({
16
- type: yupString().oneOf(["neon"]).optional(),
28
+ type: yupString().oneOf(["neon"]).defined(),
17
29
  connectionStrings: yupRecord(
18
30
  yupString().defined(),
19
31
  yupString().defined()
20
32
  ).defined()
21
- }).defined(),
33
+ }),
22
34
  yupObject({
23
- type: yupString().oneOf(["postgres"]).optional(),
35
+ type: yupString().oneOf(["postgres"]).defined(),
24
36
  connectionString: yupString().defined()
25
- }).defined()
26
- ).optional()
37
+ })
38
+ )
27
39
  });
28
40
  var branchRbacDefaultPermissions = yupRecord(
29
- yupString().optional().matches(permissionRegex),
41
+ yupString().matches(permissionRegex),
30
42
  yupBoolean().isTrue().optional()
31
- ).optional();
43
+ );
32
44
  var branchRbacSchema = yupObject({
33
45
  permissions: yupRecord(
34
- yupString().optional().matches(customPermissionRegex),
46
+ yupString().matches(customPermissionRegex),
35
47
  yupObject({
36
48
  description: yupString().optional(),
37
49
  scope: yupString().oneOf(["team", "project"]).optional(),
38
50
  containedPermissionIds: yupRecord(
39
- yupString().optional().matches(permissionRegex),
51
+ yupString().matches(permissionRegex),
40
52
  yupBoolean().isTrue().optional()
41
53
  ).optional()
42
54
  }).optional()
43
- ).optional(),
55
+ ),
44
56
  defaultPermissions: yupObject({
45
57
  teamCreator: branchRbacDefaultPermissions,
46
58
  teamMember: branchRbacDefaultPermissions,
47
59
  signUp: branchRbacDefaultPermissions
48
- }).optional()
49
- }).optional();
60
+ })
61
+ });
50
62
  var branchApiKeysSchema = yupObject({
51
63
  enabled: yupObject({
52
- team: yupBoolean().optional(),
53
- user: yupBoolean().optional()
54
- }).optional()
55
- }).optional();
64
+ team: yupBoolean(),
65
+ user: yupBoolean()
66
+ })
67
+ });
56
68
  var branchAuthSchema = yupObject({
57
- allowSignUp: yupBoolean().optional(),
69
+ allowSignUp: yupBoolean(),
58
70
  password: yupObject({
59
- allowSignIn: yupBoolean().optional()
60
- }).optional(),
71
+ allowSignIn: yupBoolean()
72
+ }),
61
73
  otp: yupObject({
62
- allowSignIn: yupBoolean().optional()
63
- }).optional(),
74
+ allowSignIn: yupBoolean()
75
+ }),
64
76
  passkey: yupObject({
65
- allowSignIn: yupBoolean().optional()
66
- }).optional(),
77
+ allowSignIn: yupBoolean()
78
+ }),
67
79
  oauth: yupObject({
68
80
  accountMergeStrategy: yupString().oneOf(["link_method", "raise_error", "allow_duplicates"]).optional(),
69
81
  providers: yupRecord(
70
- yupString().optional().matches(permissionRegex),
82
+ yupString().matches(permissionRegex),
71
83
  yupObject({
72
84
  type: yupString().oneOf(allProviders).optional(),
73
- allowSignIn: yupBoolean().optional(),
74
- allowConnectedAccounts: yupBoolean().optional()
75
- }).defined()
76
- ).optional()
77
- }).optional()
78
- }).optional();
85
+ allowSignIn: yupBoolean(),
86
+ allowConnectedAccounts: yupBoolean()
87
+ })
88
+ )
89
+ })
90
+ });
79
91
  var branchDomain = yupObject({
80
- allowLocalhost: yupBoolean().optional()
81
- }).optional();
82
- var branchConfigSchema = projectConfigSchema.omit(["sourceOfTruth"]).concat(yupObject({
92
+ allowLocalhost: yupBoolean()
93
+ });
94
+ var branchConfigSchema = canNoLongerBeOverridden(projectConfigSchema, ["sourceOfTruth"]).concat(yupObject({
83
95
  rbac: branchRbacSchema,
84
96
  teams: yupObject({
85
- createPersonalTeamOnSignUp: yupBoolean().optional(),
86
- allowClientTeamCreation: yupBoolean().optional()
87
- }).optional(),
97
+ createPersonalTeamOnSignUp: yupBoolean(),
98
+ allowClientTeamCreation: yupBoolean()
99
+ }),
88
100
  users: yupObject({
89
- allowClientUserDeletion: yupBoolean().optional()
90
- }).optional(),
101
+ allowClientUserDeletion: yupBoolean()
102
+ }),
91
103
  apiKeys: branchApiKeysSchema,
92
104
  domains: branchDomain,
93
105
  auth: branchAuthSchema,
94
106
  emails: yupObject({
95
- theme: schemaFields.emailThemeSchema.optional(),
96
- themeList: schemaFields.emailThemeListSchema.optional()
107
+ selectedThemeId: schemaFields.emailThemeSchema,
108
+ themes: schemaFields.emailThemeListSchema,
109
+ templates: schemaFields.emailTemplateListSchema
97
110
  })
98
111
  }));
99
112
  var environmentConfigSchema = branchConfigSchema.concat(yupObject({
100
113
  auth: branchConfigSchema.getNested("auth").concat(yupObject({
101
114
  oauth: branchConfigSchema.getNested("auth").getNested("oauth").concat(yupObject({
102
115
  providers: yupRecord(
103
- yupString().optional().matches(permissionRegex),
116
+ yupString().matches(permissionRegex),
104
117
  yupObject({
105
118
  type: yupString().oneOf(allProviders).optional(),
106
- isShared: yupBoolean().optional(),
119
+ isShared: yupBoolean(),
107
120
  clientId: schemaFields.oauthClientIdSchema.optional(),
108
121
  clientSecret: schemaFields.oauthClientSecretSchema.optional(),
109
122
  facebookConfigId: schemaFields.oauthFacebookConfigIdSchema.optional(),
@@ -111,12 +124,12 @@ var environmentConfigSchema = branchConfigSchema.concat(yupObject({
111
124
  allowSignIn: yupBoolean().optional(),
112
125
  allowConnectedAccounts: yupBoolean().optional()
113
126
  })
114
- ).optional()
115
- }).optional())
127
+ )
128
+ }))
116
129
  })),
117
130
  emails: branchConfigSchema.getNested("emails").concat(yupObject({
118
131
  server: yupObject({
119
- isShared: yupBoolean().optional(),
132
+ isShared: yupBoolean(),
120
133
  host: schemaFields.emailHostSchema.optional().nonEmpty(),
121
134
  port: schemaFields.emailPortSchema.optional(),
122
135
  username: schemaFields.emailUsernameSchema.optional().nonEmpty(),
@@ -124,32 +137,103 @@ var environmentConfigSchema = branchConfigSchema.concat(yupObject({
124
137
  senderName: schemaFields.emailSenderNameSchema.optional().nonEmpty(),
125
138
  senderEmail: schemaFields.emailSenderEmailSchema.optional().nonEmpty()
126
139
  })
127
- }).optional()),
140
+ })),
128
141
  domains: branchConfigSchema.getNested("domains").concat(yupObject({
129
142
  trustedDomains: yupRecord(
130
- yupString().uuid().optional(),
143
+ yupString(),
131
144
  yupObject({
132
- baseUrl: schemaFields.urlSchema.optional(),
133
- handlerPath: schemaFields.handlerPathSchema.optional()
145
+ baseUrl: schemaFields.urlSchema,
146
+ handlerPath: schemaFields.handlerPathSchema
134
147
  })
135
- ).optional()
148
+ )
136
149
  }))
137
150
  }));
138
151
  var organizationConfigSchema = environmentConfigSchema.concat(yupObject({}));
152
+ function migrateConfigOverride(type, oldUnmigratedConfigOverride) {
153
+ const isBranchOrHigher = ["branch", "environment", "organization"].includes(type);
154
+ const isEnvironmentOrHigher = ["environment", "organization"].includes(type);
155
+ let res = oldUnmigratedConfigOverride;
156
+ if (isBranchOrHigher) {
157
+ res = renameProperty(res, "emails.theme", "emails.selectedThemeId");
158
+ }
159
+ if (isEnvironmentOrHigher) {
160
+ res = mapProperty(res, "domains.trustedDomains", (value) => {
161
+ if (Array.isArray(value)) {
162
+ return typedFromEntries(value.map((v, i) => [`${i}`, v]));
163
+ }
164
+ return value;
165
+ });
166
+ }
167
+ if (isBranchOrHigher) {
168
+ res = removeProperty(res, "emails.themeList");
169
+ res = removeProperty(res, "emails.templateList");
170
+ }
171
+ if (type === "environment") {
172
+ res = removeProperty(res, "sourceOfTruth");
173
+ }
174
+ return res;
175
+ }
176
+ function removeProperty(obj, path) {
177
+ return mapProperty(obj, path, () => void 0);
178
+ }
179
+ function mapProperty(obj, path, mapper) {
180
+ const keyParts = path.split(".");
181
+ for (let i = 0; i < keyParts.length; i++) {
182
+ const pathPrefix = keyParts.slice(0, i).join(".");
183
+ const pathSuffix = keyParts.slice(i).join(".");
184
+ if (has(obj, pathPrefix) && isObjectLike(get(obj, pathPrefix))) {
185
+ const newValue = mapProperty(get(obj, pathPrefix), pathSuffix, mapper);
186
+ set(obj, pathPrefix, newValue);
187
+ }
188
+ }
189
+ if (has(obj, path)) {
190
+ const newValue = mapper(get(obj, path));
191
+ if (newValue !== void 0) {
192
+ set(obj, path, newValue);
193
+ } else {
194
+ deleteKey(obj, path);
195
+ }
196
+ }
197
+ return obj;
198
+ }
199
+ function renameProperty(obj, oldPath, newPath) {
200
+ const oldKeyParts = oldPath.split(".");
201
+ const newKeyParts = newPath.split(".");
202
+ 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}`);
203
+ for (let i = 0; i < oldKeyParts.length; i++) {
204
+ const pathPrefix = oldKeyParts.slice(0, i).join(".");
205
+ const oldPathSuffix = oldKeyParts.slice(i).join(".");
206
+ const newPathSuffix = newKeyParts.slice(i).join(".");
207
+ if (has(obj, pathPrefix) && isObjectLike(get(obj, pathPrefix))) {
208
+ set(obj, pathPrefix, renameProperty(get(obj, pathPrefix), oldPathSuffix, newPathSuffix));
209
+ }
210
+ }
211
+ if (has(obj, oldPath)) {
212
+ set(obj, newPath, get(obj, oldPath));
213
+ deleteKey(obj, oldPath);
214
+ }
215
+ return obj;
216
+ }
139
217
  var projectConfigDefaults = {
140
218
  sourceOfTruth: {
141
- type: "hosted"
219
+ type: "hosted",
220
+ connectionStrings: void 0,
221
+ connectionString: void 0
142
222
  }
143
223
  };
144
224
  var branchConfigDefaults = {};
145
225
  var environmentConfigDefaults = {};
146
226
  var organizationConfigDefaults = {
147
227
  rbac: {
148
- permissions: (key) => ({}),
228
+ permissions: (key) => ({
229
+ containedPermissionIds: (key2) => void 0,
230
+ description: void 0,
231
+ scope: void 0
232
+ }),
149
233
  defaultPermissions: {
150
- teamCreator: {},
151
- teamMember: {},
152
- signUp: {}
234
+ teamCreator: (key) => void 0,
235
+ teamMember: (key) => void 0,
236
+ signUp: (key) => void 0
153
237
  }
154
238
  },
155
239
  apiKeys: {
@@ -168,6 +252,7 @@ var organizationConfigDefaults = {
168
252
  domains: {
169
253
  allowLocalhost: false,
170
254
  trustedDomains: (key) => ({
255
+ baseUrl: void 0,
171
256
  handlerPath: "/handler"
172
257
  })
173
258
  },
@@ -185,44 +270,341 @@ var organizationConfigDefaults = {
185
270
  oauth: {
186
271
  accountMergeStrategy: "link_method",
187
272
  providers: (key) => ({
273
+ type: void 0,
188
274
  isShared: true,
189
275
  allowSignIn: false,
190
- allowConnectedAccounts: false
276
+ allowConnectedAccounts: false,
277
+ clientId: void 0,
278
+ clientSecret: void 0,
279
+ facebookConfigId: void 0,
280
+ microsoftTenantId: void 0
191
281
  })
192
282
  }
193
283
  },
194
284
  emails: {
195
285
  server: {
196
- isShared: true
286
+ isShared: true,
287
+ host: void 0,
288
+ port: void 0,
289
+ username: void 0,
290
+ password: void 0,
291
+ senderName: void 0,
292
+ senderEmail: void 0
197
293
  },
198
- theme: DEFAULT_EMAIL_THEME_ID,
199
- themeList: DEFAULT_EMAIL_THEMES
294
+ selectedThemeId: DEFAULT_EMAIL_THEME_ID,
295
+ themes: typedAssign((key) => ({
296
+ displayName: "Unnamed Theme",
297
+ tsxSource: "Error: Theme config is missing TypeScript source code."
298
+ }), DEFAULT_EMAIL_THEMES),
299
+ templates: typedAssign((key) => ({
300
+ displayName: "Unnamed Template",
301
+ tsxSource: "Error: Template config is missing TypeScript source code.",
302
+ themeId: void 0
303
+ }), DEFAULT_EMAIL_TEMPLATES)
200
304
  }
201
305
  };
306
+ typeAssertIs()();
307
+ typeAssertIs()();
308
+ function deepReplaceFunctionsWithObjects(obj) {
309
+ return mapValues({ ...obj }, (v) => isObjectLike(v) ? deepReplaceFunctionsWithObjects(v) : v);
310
+ }
202
311
  function applyDefaults(defaults, config) {
203
- const res = typeof defaults === "function" ? {} : mapValues(defaults, (v) => typeof v === "function" ? {} : typeof v === "object" ? applyDefaults(v, {}) : v);
204
- for (const [key, mergeValue] of Object.entries(config)) {
205
- const baseValue = typeof defaults === "function" ? defaults(key) : has(defaults, key) ? get(defaults, key) : void 0;
206
- if (baseValue !== void 0) {
207
- if (isObjectLike(baseValue) && isObjectLike(mergeValue)) {
208
- set(res, key, applyDefaults(baseValue, mergeValue));
209
- continue;
312
+ const res = deepReplaceFunctionsWithObjects(defaults);
313
+ outer: for (const [key, mergeValue] of Object.entries(config)) {
314
+ if (mergeValue === void 0) continue;
315
+ const keyParts = key.split(".");
316
+ let baseValue = defaults;
317
+ let currentRes = res;
318
+ for (const [index, part] of keyParts.entries()) {
319
+ baseValue = has(baseValue, part) ? get(baseValue, part) : typeof baseValue === "function" ? baseValue(part) : void 0;
320
+ if (baseValue === void 0 || !isObjectLike(baseValue)) {
321
+ set(res, key, mergeValue);
322
+ continue outer;
210
323
  }
324
+ if (!has(currentRes, part)) set(currentRes, part, deepReplaceFunctionsWithObjects(baseValue));
325
+ currentRes = get(currentRes, part);
211
326
  }
212
- set(res, key, mergeValue);
327
+ set(res, key, isObjectLike(mergeValue) ? applyDefaults(baseValue, mergeValue) : mergeValue);
213
328
  }
214
329
  return res;
215
330
  }
331
+ function applyProjectDefaults(config) {
332
+ return applyDefaults(projectConfigDefaults, config);
333
+ }
334
+ function applyBranchDefaults(config) {
335
+ return applyDefaults(
336
+ branchConfigDefaults,
337
+ applyDefaults(
338
+ projectConfigDefaults,
339
+ config
340
+ )
341
+ );
342
+ }
343
+ function applyEnvironmentDefaults(config) {
344
+ return applyDefaults(
345
+ environmentConfigDefaults,
346
+ applyDefaults(
347
+ branchConfigDefaults,
348
+ applyDefaults(
349
+ projectConfigDefaults,
350
+ config
351
+ )
352
+ )
353
+ );
354
+ }
355
+ function applyOrganizationDefaults(config) {
356
+ return applyDefaults(
357
+ organizationConfigDefaults,
358
+ applyDefaults(
359
+ environmentConfigDefaults,
360
+ applyDefaults(
361
+ branchConfigDefaults,
362
+ applyDefaults(
363
+ projectConfigDefaults,
364
+ config
365
+ )
366
+ )
367
+ )
368
+ );
369
+ }
370
+ async function sanitizeProjectConfig(config) {
371
+ assertNormalized(config);
372
+ const oldSourceOfTruth = config.sourceOfTruth;
373
+ const sourceOfTruth = oldSourceOfTruth.type === "neon" && typeof oldSourceOfTruth.connectionStrings === "object" ? {
374
+ type: "neon",
375
+ connectionStrings: { ...filterUndefined(oldSourceOfTruth.connectionStrings) }
376
+ } : oldSourceOfTruth.type === "postgres" && typeof oldSourceOfTruth.connectionString === "string" ? {
377
+ type: "postgres",
378
+ connectionString: oldSourceOfTruth.connectionString
379
+ } : {
380
+ type: "hosted"
381
+ };
382
+ return {
383
+ ...config,
384
+ sourceOfTruth
385
+ };
386
+ }
387
+ async function sanitizeBranchConfig(config) {
388
+ assertNormalized(config);
389
+ const prepared = await sanitizeProjectConfig(config);
390
+ return {
391
+ ...prepared
392
+ };
393
+ }
394
+ async function sanitizeEnvironmentConfig(config) {
395
+ assertNormalized(config);
396
+ const prepared = await sanitizeBranchConfig(config);
397
+ return {
398
+ ...prepared
399
+ };
400
+ }
401
+ async function sanitizeOrganizationConfig(config) {
402
+ assertNormalized(config);
403
+ const prepared = await sanitizeEnvironmentConfig(config);
404
+ const themes = {
405
+ ...DEFAULT_EMAIL_THEMES,
406
+ ...prepared.emails.themes
407
+ };
408
+ const templates = {
409
+ ...DEFAULT_EMAIL_TEMPLATES,
410
+ ...prepared.emails.templates
411
+ };
412
+ return {
413
+ ...prepared,
414
+ emails: {
415
+ ...prepared.emails,
416
+ selectedThemeId: has(themes, prepared.emails.selectedThemeId) ? prepared.emails.selectedThemeId : DEFAULT_EMAIL_THEME_ID,
417
+ themes,
418
+ templates
419
+ }
420
+ };
421
+ }
422
+ async function getConfigOverrideErrors(schema, configOverride, options = {}) {
423
+ if (typeof configOverride !== "object" || configOverride === null) {
424
+ return Result.error("Config override must be a non-null object.");
425
+ }
426
+ if (Object.getPrototypeOf(configOverride) !== Object.getPrototypeOf({})) {
427
+ return Result.error("Config override must be plain old JavaScript object.");
428
+ }
429
+ const reason = getInvalidConfigReason(configOverride, { configName: "override" });
430
+ if (reason) return Result.error("Invalid config format: " + reason);
431
+ const getSubSchema = (schema2, key) => {
432
+ const keyParts = key.split(".");
433
+ if (!schema2.hasNested(keyParts[0])) {
434
+ return void 0;
435
+ }
436
+ const nestedSchema = schema2.getNested(keyParts[0]);
437
+ if (nestedSchema.meta()?.stackConfigCanNoLongerBeOverridden && !options.allowPropertiesThatCanNoLongerBeOverridden) {
438
+ return void 0;
439
+ }
440
+ if (keyParts.length === 1) {
441
+ return nestedSchema;
442
+ } else {
443
+ return getSubSchema(nestedSchema, keyParts.slice(1).join("."));
444
+ }
445
+ };
446
+ const getRestrictedSchemaBase = (path, schema2) => {
447
+ const schemaInfo = schema2.meta()?.stackSchemaInfo;
448
+ switch (schemaInfo?.type) {
449
+ case "string": {
450
+ const stringSchema = schema2;
451
+ const description = stringSchema.describe();
452
+ let res = yupString();
453
+ if (description.tests.some((t) => t.name === "uuid")) {
454
+ res = res.uuid();
455
+ }
456
+ return res;
457
+ }
458
+ case "number": {
459
+ return yupNumber();
460
+ }
461
+ case "boolean": {
462
+ return yupBoolean();
463
+ }
464
+ case "date": {
465
+ return yupDate();
466
+ }
467
+ case "mixed": {
468
+ return yupMixed();
469
+ }
470
+ case "array": {
471
+ throw new StackAssertionError(`Arrays are not supported in config JSON files (besides tuples). Use a record instead.`, { schemaInfo, schema: schema2 });
472
+ }
473
+ case "tuple": {
474
+ return yupTuple(schemaInfo.items.map((s, index) => getRestrictedSchema(path + `[${index}]`, s)));
475
+ }
476
+ case "union": {
477
+ const schemas = schemaInfo.items;
478
+ const nonObjectSchemas = [...schemas.entries()].filter(([index, s]) => s.meta()?.stackSchemaInfo?.type !== "object");
479
+ const objectSchemas = schemas.filter((s) => s.meta()?.stackSchemaInfo?.type === "object");
480
+ const allObjectSchemaKeys = [...new Set(objectSchemas.flatMap((s) => Object.keys(s.fields)))];
481
+ const mergedObjectSchema = yupObject(
482
+ Object.fromEntries(
483
+ allObjectSchemaKeys.map((key) => [key, yupUnion(
484
+ ...objectSchemas.flatMap((s, index) => s.hasNested(key) ? [s.getNested(key)] : [])
485
+ )])
486
+ )
487
+ );
488
+ return yupUnion(
489
+ ...nonObjectSchemas.map(([index, s]) => getRestrictedSchema(path + `|variant-${index}|`, s)),
490
+ ...objectSchemas.length > 0 ? [getRestrictedSchema(path + (nonObjectSchemas.length > 0 ? `|variant|` : ""), mergedObjectSchema)] : []
491
+ );
492
+ }
493
+ case "record": {
494
+ return yupRecord(getRestrictedSchema(path + ".key", schemaInfo.keySchema), getRestrictedSchema(path + ".value", schemaInfo.valueSchema));
495
+ }
496
+ case "object": {
497
+ const objectSchema = schema2;
498
+ return yupObject(
499
+ Object.fromEntries(
500
+ Object.entries(objectSchema.fields).map(([key, value]) => [key, getRestrictedSchema(path + "." + key, value)])
501
+ )
502
+ );
503
+ }
504
+ case "never": {
505
+ return yupNever();
506
+ }
507
+ default: {
508
+ throw new StackAssertionError(`Unknown schema info at path ${path}: ${JSON.stringify(schemaInfo)}`, { schemaInfo, schema: schema2 });
509
+ }
510
+ }
511
+ };
512
+ const getRestrictedSchema = (path, schema2) => {
513
+ let restricted = getRestrictedSchemaBase(path, schema2);
514
+ restricted = restricted.nullable();
515
+ const description = schema2.describe();
516
+ if (description.oneOf.length > 0) {
517
+ restricted = restricted.oneOf(description.oneOf);
518
+ }
519
+ if (description.notOneOf.length > 0) {
520
+ restricted = restricted.notOneOf(description.notOneOf);
521
+ }
522
+ return restricted;
523
+ };
524
+ for (const [key, value] of Object.entries(configOverride)) {
525
+ if (value === void 0) continue;
526
+ const subSchema = getSubSchema(schema, key);
527
+ if (!subSchema) {
528
+ return Result.error(`The key ${JSON.stringify(key)} is not valid for the schema.`);
529
+ }
530
+ let restrictedSchema = getRestrictedSchema(key, subSchema);
531
+ try {
532
+ await restrictedSchema.validate(value, {
533
+ strict: true,
534
+ ...{
535
+ // Although `path` is not part of the yup types, it is actually recognized and does the correct thing
536
+ path: key
537
+ },
538
+ context: {
539
+ noUnknownPathPrefixes: [""]
540
+ }
541
+ });
542
+ } catch (error) {
543
+ if (error instanceof yup.ValidationError) {
544
+ return Result.error(error.message);
545
+ }
546
+ throw error;
547
+ }
548
+ }
549
+ return Result.ok(null);
550
+ }
551
+ async function assertNoConfigOverrideErrors(schema, config, options = {}) {
552
+ const res = await getConfigOverrideErrors(schema, config, options);
553
+ if (res.status === "error") throw new StackAssertionError(`Config override is invalid \u2014 at a place where it should have already been validated! ${res.error}`, { options, config, schema });
554
+ }
555
+ typeAssertIs()();
556
+ typeAssertExtends()();
557
+ typeAssertExtends()();
558
+ async function getIncompleteConfigWarnings(schema, incompleteConfig) {
559
+ await assertNoConfigOverrideErrors(schema, incompleteConfig, { allowPropertiesThatCanNoLongerBeOverridden: true });
560
+ let normalized;
561
+ try {
562
+ normalized = normalize(incompleteConfig, { onDotIntoNull: "empty-object" });
563
+ } catch (error) {
564
+ if (error instanceof NormalizationError) {
565
+ return Result.error(`Config is not normalizable. ` + error.message);
566
+ }
567
+ throw error;
568
+ }
569
+ try {
570
+ await schema.validate(normalized, {
571
+ strict: true,
572
+ context: {
573
+ noUnknownPathPrefixes: [""]
574
+ }
575
+ });
576
+ return Result.ok(null);
577
+ } catch (error) {
578
+ if (error instanceof yup.ValidationError) {
579
+ return Result.error(error.message);
580
+ }
581
+ throw error;
582
+ }
583
+ }
584
+ typeAssertExtends()();
585
+ typeAssertExtends()();
586
+ typeAssertExtends()();
587
+ typeAssertExtends()();
588
+ typeAssert()();
589
+ typeAssert()();
590
+ typeAssertExtends()();
216
591
  export {
217
- applyDefaults,
218
- branchConfigDefaults,
592
+ applyBranchDefaults,
593
+ applyEnvironmentDefaults,
594
+ applyOrganizationDefaults,
595
+ applyProjectDefaults,
596
+ assertNoConfigOverrideErrors,
219
597
  branchConfigSchema,
220
598
  configLevels,
221
- environmentConfigDefaults,
222
599
  environmentConfigSchema,
223
- organizationConfigDefaults,
600
+ getConfigOverrideErrors,
601
+ getIncompleteConfigWarnings,
602
+ migrateConfigOverride,
224
603
  organizationConfigSchema,
225
- projectConfigDefaults,
226
- projectConfigSchema
604
+ projectConfigSchema,
605
+ sanitizeBranchConfig,
606
+ sanitizeEnvironmentConfig,
607
+ sanitizeOrganizationConfig,
608
+ sanitizeProjectConfig
227
609
  };
228
610
  //# sourceMappingURL=schema.js.map