@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.
- package/CHANGELOG.md +12 -0
- package/dist/config/format.d.mts +20 -5
- package/dist/config/format.d.ts +20 -5
- package/dist/config/format.js +39 -14
- package/dist/config/format.js.map +1 -1
- package/dist/config/schema.d.mts +795 -317
- package/dist/config/schema.d.ts +795 -317
- package/dist/config/schema.js +471 -84
- package/dist/config/schema.js.map +1 -1
- package/dist/crud.d.mts +1 -0
- package/dist/crud.d.ts +1 -0
- package/dist/esm/config/format.js +37 -14
- package/dist/esm/config/format.js.map +1 -1
- package/dist/esm/config/schema.js +460 -80
- package/dist/esm/config/schema.js.map +1 -1
- package/dist/esm/helpers/emails.js +136 -30
- package/dist/esm/helpers/emails.js.map +1 -1
- package/dist/esm/interface/admin-interface.js +19 -29
- package/dist/esm/interface/admin-interface.js.map +1 -1
- package/dist/esm/known-errors.js +24 -1
- package/dist/esm/known-errors.js.map +1 -1
- package/dist/esm/schema-fields.js +92 -27
- package/dist/esm/schema-fields.js.map +1 -1
- package/dist/esm/utils/currencies.js +52 -0
- package/dist/esm/utils/currencies.js.map +1 -0
- package/dist/esm/utils/dates.js +55 -1
- package/dist/esm/utils/dates.js.map +1 -1
- package/dist/esm/utils/errors.js.map +1 -1
- package/dist/esm/utils/objects.js +2 -0
- package/dist/esm/utils/objects.js.map +1 -1
- package/dist/esm/utils/strings.js +4 -0
- package/dist/esm/utils/strings.js.map +1 -1
- package/dist/esm/utils/types.js +45 -0
- package/dist/esm/utils/types.js.map +1 -1
- package/dist/helpers/emails.d.mts +32 -6
- package/dist/helpers/emails.d.ts +32 -6
- package/dist/helpers/emails.js +138 -30
- package/dist/helpers/emails.js.map +1 -1
- package/dist/helpers/password.d.mts +1 -0
- package/dist/helpers/password.d.ts +1 -0
- package/dist/helpers/production-mode.d.mts +1 -0
- package/dist/helpers/production-mode.d.ts +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/interface/admin-interface.d.mts +8 -8
- package/dist/interface/admin-interface.d.ts +8 -8
- package/dist/interface/admin-interface.js +19 -29
- package/dist/interface/admin-interface.js.map +1 -1
- package/dist/interface/client-interface.d.mts +1 -0
- package/dist/interface/client-interface.d.ts +1 -0
- package/dist/interface/crud/connected-accounts.d.mts +1 -0
- package/dist/interface/crud/connected-accounts.d.ts +1 -0
- package/dist/interface/crud/contact-channels.d.mts +1 -0
- package/dist/interface/crud/contact-channels.d.ts +1 -0
- package/dist/interface/crud/current-user.d.mts +1 -0
- package/dist/interface/crud/current-user.d.ts +1 -0
- package/dist/interface/crud/email-templates.d.mts +1 -0
- package/dist/interface/crud/email-templates.d.ts +1 -0
- package/dist/interface/crud/emails.d.mts +1 -0
- package/dist/interface/crud/emails.d.ts +1 -0
- package/dist/interface/crud/internal-api-keys.d.mts +1 -0
- package/dist/interface/crud/internal-api-keys.d.ts +1 -0
- package/dist/interface/crud/notification-preferences.d.mts +1 -0
- package/dist/interface/crud/notification-preferences.d.ts +1 -0
- package/dist/interface/crud/oauth-providers.d.mts +5 -4
- package/dist/interface/crud/oauth-providers.d.ts +5 -4
- package/dist/interface/crud/project-api-keys.d.mts +3 -2
- package/dist/interface/crud/project-api-keys.d.ts +3 -2
- package/dist/interface/crud/project-permissions.d.mts +1 -0
- package/dist/interface/crud/project-permissions.d.ts +1 -0
- package/dist/interface/crud/projects.d.mts +8 -7
- package/dist/interface/crud/projects.d.ts +8 -7
- package/dist/interface/crud/sessions.d.mts +1 -0
- package/dist/interface/crud/sessions.d.ts +1 -0
- package/dist/interface/crud/svix-token.d.mts +1 -0
- package/dist/interface/crud/svix-token.d.ts +1 -0
- package/dist/interface/crud/team-invitation-details.d.mts +1 -0
- package/dist/interface/crud/team-invitation-details.d.ts +1 -0
- package/dist/interface/crud/team-invitation.d.mts +1 -0
- package/dist/interface/crud/team-invitation.d.ts +1 -0
- package/dist/interface/crud/team-member-profiles.d.mts +1 -0
- package/dist/interface/crud/team-member-profiles.d.ts +1 -0
- package/dist/interface/crud/team-memberships.d.mts +1 -0
- package/dist/interface/crud/team-memberships.d.ts +1 -0
- package/dist/interface/crud/team-permissions.d.mts +1 -0
- package/dist/interface/crud/team-permissions.d.ts +1 -0
- package/dist/interface/crud/teams.d.mts +1 -0
- package/dist/interface/crud/teams.d.ts +1 -0
- package/dist/interface/crud/users.d.mts +1 -0
- package/dist/interface/crud/users.d.ts +1 -0
- package/dist/interface/server-interface.d.mts +1 -0
- package/dist/interface/server-interface.d.ts +1 -0
- package/dist/known-errors.d.mts +6 -0
- package/dist/known-errors.d.ts +6 -0
- package/dist/known-errors.js +24 -1
- package/dist/known-errors.js.map +1 -1
- package/dist/schema-fields.d.mts +39 -8
- package/dist/schema-fields.d.ts +39 -8
- package/dist/schema-fields.js +101 -27
- package/dist/schema-fields.js.map +1 -1
- package/dist/utils/currencies.d.mts +39 -0
- package/dist/utils/currencies.d.ts +39 -0
- package/dist/utils/currencies.js +78 -0
- package/dist/utils/currencies.js.map +1 -0
- package/dist/utils/dates.d.mts +5 -1
- package/dist/utils/dates.d.ts +5 -1
- package/dist/utils/dates.js +58 -2
- package/dist/utils/dates.js.map +1 -1
- package/dist/utils/errors.d.mts +9 -0
- package/dist/utils/errors.d.ts +9 -0
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/objects.d.mts +23 -8
- package/dist/utils/objects.d.ts +23 -8
- package/dist/utils/objects.js +2 -0
- package/dist/utils/objects.js.map +1 -1
- package/dist/utils/strings.d.mts +3 -1
- package/dist/utils/strings.d.ts +3 -1
- package/dist/utils/strings.js +5 -0
- package/dist/utils/strings.js.map +1 -1
- package/dist/utils/types.d.mts +73 -2
- package/dist/utils/types.d.ts +73 -2
- package/dist/utils/types.js +54 -0
- package/dist/utils/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,110 +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 {
|
|
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"]).
|
|
14
|
-
})
|
|
25
|
+
type: yupString().oneOf(["hosted"]).defined()
|
|
26
|
+
}),
|
|
15
27
|
yupObject({
|
|
16
|
-
type: yupString().oneOf(["neon"]).
|
|
28
|
+
type: yupString().oneOf(["neon"]).defined(),
|
|
17
29
|
connectionStrings: yupRecord(
|
|
18
30
|
yupString().defined(),
|
|
19
31
|
yupString().defined()
|
|
20
32
|
).defined()
|
|
21
|
-
})
|
|
33
|
+
}),
|
|
22
34
|
yupObject({
|
|
23
|
-
type: yupString().oneOf(["postgres"]).
|
|
35
|
+
type: yupString().oneOf(["postgres"]).defined(),
|
|
24
36
|
connectionString: yupString().defined()
|
|
25
|
-
})
|
|
26
|
-
)
|
|
37
|
+
})
|
|
38
|
+
)
|
|
27
39
|
});
|
|
28
40
|
var branchRbacDefaultPermissions = yupRecord(
|
|
29
|
-
yupString().
|
|
41
|
+
yupString().matches(permissionRegex),
|
|
30
42
|
yupBoolean().isTrue().optional()
|
|
31
|
-
)
|
|
43
|
+
);
|
|
32
44
|
var branchRbacSchema = yupObject({
|
|
33
45
|
permissions: yupRecord(
|
|
34
|
-
yupString().
|
|
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().
|
|
51
|
+
yupString().matches(permissionRegex),
|
|
40
52
|
yupBoolean().isTrue().optional()
|
|
41
53
|
).optional()
|
|
42
54
|
}).optional()
|
|
43
|
-
)
|
|
55
|
+
),
|
|
44
56
|
defaultPermissions: yupObject({
|
|
45
57
|
teamCreator: branchRbacDefaultPermissions,
|
|
46
58
|
teamMember: branchRbacDefaultPermissions,
|
|
47
59
|
signUp: branchRbacDefaultPermissions
|
|
48
|
-
})
|
|
49
|
-
})
|
|
60
|
+
})
|
|
61
|
+
});
|
|
50
62
|
var branchApiKeysSchema = yupObject({
|
|
51
63
|
enabled: yupObject({
|
|
52
|
-
team: yupBoolean()
|
|
53
|
-
user: yupBoolean()
|
|
54
|
-
})
|
|
55
|
-
})
|
|
64
|
+
team: yupBoolean(),
|
|
65
|
+
user: yupBoolean()
|
|
66
|
+
})
|
|
67
|
+
});
|
|
56
68
|
var branchAuthSchema = yupObject({
|
|
57
|
-
allowSignUp: yupBoolean()
|
|
69
|
+
allowSignUp: yupBoolean(),
|
|
58
70
|
password: yupObject({
|
|
59
|
-
allowSignIn: yupBoolean()
|
|
60
|
-
})
|
|
71
|
+
allowSignIn: yupBoolean()
|
|
72
|
+
}),
|
|
61
73
|
otp: yupObject({
|
|
62
|
-
allowSignIn: yupBoolean()
|
|
63
|
-
})
|
|
74
|
+
allowSignIn: yupBoolean()
|
|
75
|
+
}),
|
|
64
76
|
passkey: yupObject({
|
|
65
|
-
allowSignIn: yupBoolean()
|
|
66
|
-
})
|
|
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().
|
|
82
|
+
yupString().matches(permissionRegex),
|
|
71
83
|
yupObject({
|
|
72
84
|
type: yupString().oneOf(allProviders).optional(),
|
|
73
|
-
allowSignIn: yupBoolean()
|
|
74
|
-
allowConnectedAccounts: yupBoolean()
|
|
75
|
-
})
|
|
76
|
-
)
|
|
77
|
-
})
|
|
78
|
-
})
|
|
85
|
+
allowSignIn: yupBoolean(),
|
|
86
|
+
allowConnectedAccounts: yupBoolean()
|
|
87
|
+
})
|
|
88
|
+
)
|
|
89
|
+
})
|
|
90
|
+
});
|
|
79
91
|
var branchDomain = yupObject({
|
|
80
|
-
allowLocalhost: yupBoolean()
|
|
81
|
-
})
|
|
82
|
-
var branchConfigSchema = projectConfigSchema
|
|
92
|
+
allowLocalhost: yupBoolean()
|
|
93
|
+
});
|
|
94
|
+
var branchConfigSchema = canNoLongerBeOverridden(projectConfigSchema, ["sourceOfTruth"]).concat(yupObject({
|
|
83
95
|
rbac: branchRbacSchema,
|
|
84
96
|
teams: yupObject({
|
|
85
|
-
createPersonalTeamOnSignUp: yupBoolean()
|
|
86
|
-
allowClientTeamCreation: yupBoolean()
|
|
87
|
-
})
|
|
97
|
+
createPersonalTeamOnSignUp: yupBoolean(),
|
|
98
|
+
allowClientTeamCreation: yupBoolean()
|
|
99
|
+
}),
|
|
88
100
|
users: yupObject({
|
|
89
|
-
allowClientUserDeletion: yupBoolean()
|
|
90
|
-
})
|
|
101
|
+
allowClientUserDeletion: yupBoolean()
|
|
102
|
+
}),
|
|
91
103
|
apiKeys: branchApiKeysSchema,
|
|
92
104
|
domains: branchDomain,
|
|
93
105
|
auth: branchAuthSchema,
|
|
94
106
|
emails: yupObject({
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
107
|
+
selectedThemeId: schemaFields.emailThemeSchema,
|
|
108
|
+
themes: schemaFields.emailThemeListSchema,
|
|
109
|
+
templates: schemaFields.emailTemplateListSchema
|
|
98
110
|
})
|
|
99
111
|
}));
|
|
100
112
|
var environmentConfigSchema = branchConfigSchema.concat(yupObject({
|
|
101
113
|
auth: branchConfigSchema.getNested("auth").concat(yupObject({
|
|
102
114
|
oauth: branchConfigSchema.getNested("auth").getNested("oauth").concat(yupObject({
|
|
103
115
|
providers: yupRecord(
|
|
104
|
-
yupString().
|
|
116
|
+
yupString().matches(permissionRegex),
|
|
105
117
|
yupObject({
|
|
106
118
|
type: yupString().oneOf(allProviders).optional(),
|
|
107
|
-
isShared: yupBoolean()
|
|
119
|
+
isShared: yupBoolean(),
|
|
108
120
|
clientId: schemaFields.oauthClientIdSchema.optional(),
|
|
109
121
|
clientSecret: schemaFields.oauthClientSecretSchema.optional(),
|
|
110
122
|
facebookConfigId: schemaFields.oauthFacebookConfigIdSchema.optional(),
|
|
@@ -112,12 +124,12 @@ var environmentConfigSchema = branchConfigSchema.concat(yupObject({
|
|
|
112
124
|
allowSignIn: yupBoolean().optional(),
|
|
113
125
|
allowConnectedAccounts: yupBoolean().optional()
|
|
114
126
|
})
|
|
115
|
-
)
|
|
116
|
-
})
|
|
127
|
+
)
|
|
128
|
+
}))
|
|
117
129
|
})),
|
|
118
130
|
emails: branchConfigSchema.getNested("emails").concat(yupObject({
|
|
119
131
|
server: yupObject({
|
|
120
|
-
isShared: yupBoolean()
|
|
132
|
+
isShared: yupBoolean(),
|
|
121
133
|
host: schemaFields.emailHostSchema.optional().nonEmpty(),
|
|
122
134
|
port: schemaFields.emailPortSchema.optional(),
|
|
123
135
|
username: schemaFields.emailUsernameSchema.optional().nonEmpty(),
|
|
@@ -125,32 +137,103 @@ var environmentConfigSchema = branchConfigSchema.concat(yupObject({
|
|
|
125
137
|
senderName: schemaFields.emailSenderNameSchema.optional().nonEmpty(),
|
|
126
138
|
senderEmail: schemaFields.emailSenderEmailSchema.optional().nonEmpty()
|
|
127
139
|
})
|
|
128
|
-
})
|
|
140
|
+
})),
|
|
129
141
|
domains: branchConfigSchema.getNested("domains").concat(yupObject({
|
|
130
142
|
trustedDomains: yupRecord(
|
|
131
|
-
yupString()
|
|
143
|
+
yupString(),
|
|
132
144
|
yupObject({
|
|
133
|
-
baseUrl: schemaFields.urlSchema
|
|
134
|
-
handlerPath: schemaFields.handlerPathSchema
|
|
145
|
+
baseUrl: schemaFields.urlSchema,
|
|
146
|
+
handlerPath: schemaFields.handlerPathSchema
|
|
135
147
|
})
|
|
136
|
-
)
|
|
148
|
+
)
|
|
137
149
|
}))
|
|
138
150
|
}));
|
|
139
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
|
+
}
|
|
140
217
|
var projectConfigDefaults = {
|
|
141
218
|
sourceOfTruth: {
|
|
142
|
-
type: "hosted"
|
|
219
|
+
type: "hosted",
|
|
220
|
+
connectionStrings: void 0,
|
|
221
|
+
connectionString: void 0
|
|
143
222
|
}
|
|
144
223
|
};
|
|
145
224
|
var branchConfigDefaults = {};
|
|
146
225
|
var environmentConfigDefaults = {};
|
|
147
226
|
var organizationConfigDefaults = {
|
|
148
227
|
rbac: {
|
|
149
|
-
permissions: (key) => ({
|
|
228
|
+
permissions: (key) => ({
|
|
229
|
+
containedPermissionIds: (key2) => void 0,
|
|
230
|
+
description: void 0,
|
|
231
|
+
scope: void 0
|
|
232
|
+
}),
|
|
150
233
|
defaultPermissions: {
|
|
151
|
-
teamCreator:
|
|
152
|
-
teamMember:
|
|
153
|
-
signUp:
|
|
234
|
+
teamCreator: (key) => void 0,
|
|
235
|
+
teamMember: (key) => void 0,
|
|
236
|
+
signUp: (key) => void 0
|
|
154
237
|
}
|
|
155
238
|
},
|
|
156
239
|
apiKeys: {
|
|
@@ -169,6 +252,7 @@ var organizationConfigDefaults = {
|
|
|
169
252
|
domains: {
|
|
170
253
|
allowLocalhost: false,
|
|
171
254
|
trustedDomains: (key) => ({
|
|
255
|
+
baseUrl: void 0,
|
|
172
256
|
handlerPath: "/handler"
|
|
173
257
|
})
|
|
174
258
|
},
|
|
@@ -186,45 +270,341 @@ var organizationConfigDefaults = {
|
|
|
186
270
|
oauth: {
|
|
187
271
|
accountMergeStrategy: "link_method",
|
|
188
272
|
providers: (key) => ({
|
|
273
|
+
type: void 0,
|
|
189
274
|
isShared: true,
|
|
190
275
|
allowSignIn: false,
|
|
191
|
-
allowConnectedAccounts: false
|
|
276
|
+
allowConnectedAccounts: false,
|
|
277
|
+
clientId: void 0,
|
|
278
|
+
clientSecret: void 0,
|
|
279
|
+
facebookConfigId: void 0,
|
|
280
|
+
microsoftTenantId: void 0
|
|
192
281
|
})
|
|
193
282
|
}
|
|
194
283
|
},
|
|
195
284
|
emails: {
|
|
196
285
|
server: {
|
|
197
|
-
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
|
|
198
293
|
},
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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)
|
|
202
304
|
}
|
|
203
305
|
};
|
|
306
|
+
typeAssertIs()();
|
|
307
|
+
typeAssertIs()();
|
|
308
|
+
function deepReplaceFunctionsWithObjects(obj) {
|
|
309
|
+
return mapValues({ ...obj }, (v) => isObjectLike(v) ? deepReplaceFunctionsWithObjects(v) : v);
|
|
310
|
+
}
|
|
204
311
|
function applyDefaults(defaults, config) {
|
|
205
|
-
const res =
|
|
206
|
-
for (const [key, mergeValue] of Object.entries(config)) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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;
|
|
212
323
|
}
|
|
324
|
+
if (!has(currentRes, part)) set(currentRes, part, deepReplaceFunctionsWithObjects(baseValue));
|
|
325
|
+
currentRes = get(currentRes, part);
|
|
213
326
|
}
|
|
214
|
-
set(res, key, mergeValue);
|
|
327
|
+
set(res, key, isObjectLike(mergeValue) ? applyDefaults(baseValue, mergeValue) : mergeValue);
|
|
215
328
|
}
|
|
216
329
|
return res;
|
|
217
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()();
|
|
218
591
|
export {
|
|
219
|
-
|
|
220
|
-
|
|
592
|
+
applyBranchDefaults,
|
|
593
|
+
applyEnvironmentDefaults,
|
|
594
|
+
applyOrganizationDefaults,
|
|
595
|
+
applyProjectDefaults,
|
|
596
|
+
assertNoConfigOverrideErrors,
|
|
221
597
|
branchConfigSchema,
|
|
222
598
|
configLevels,
|
|
223
|
-
environmentConfigDefaults,
|
|
224
599
|
environmentConfigSchema,
|
|
225
|
-
|
|
600
|
+
getConfigOverrideErrors,
|
|
601
|
+
getIncompleteConfigWarnings,
|
|
602
|
+
migrateConfigOverride,
|
|
226
603
|
organizationConfigSchema,
|
|
227
|
-
|
|
228
|
-
|
|
604
|
+
projectConfigSchema,
|
|
605
|
+
sanitizeBranchConfig,
|
|
606
|
+
sanitizeEnvironmentConfig,
|
|
607
|
+
sanitizeOrganizationConfig,
|
|
608
|
+
sanitizeProjectConfig
|
|
229
609
|
};
|
|
230
610
|
//# sourceMappingURL=schema.js.map
|