@stackframe/stack-shared 2.8.64 → 2.8.66
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/dist/config/db-sync-mappings.d.mts +17 -0
- package/dist/config/db-sync-mappings.d.ts +17 -0
- package/dist/config/db-sync-mappings.js +195 -0
- package/dist/config/db-sync-mappings.js.map +1 -0
- package/dist/config/schema-fuzzer.test.js +24 -0
- package/dist/config/schema-fuzzer.test.js.map +1 -1
- package/dist/config/schema.d.mts +198 -24
- package/dist/config/schema.d.ts +198 -24
- package/dist/config/schema.js +56 -3
- package/dist/config/schema.js.map +1 -1
- package/dist/esm/config/db-sync-mappings.js +170 -0
- package/dist/esm/config/db-sync-mappings.js.map +1 -0
- package/dist/esm/config/schema-fuzzer.test.js +24 -0
- package/dist/esm/config/schema-fuzzer.test.js.map +1 -1
- package/dist/esm/config/schema.js +56 -3
- package/dist/esm/config/schema.js.map +1 -1
- package/dist/esm/interface/admin-interface.js.map +1 -1
- package/dist/esm/interface/client-interface.js.map +1 -1
- package/dist/esm/interface/crud/current-user.js +3 -1
- package/dist/esm/interface/crud/current-user.js.map +1 -1
- package/dist/esm/interface/crud/sign-up-rules.js +1 -0
- package/dist/esm/interface/crud/sign-up-rules.js.map +1 -0
- package/dist/esm/interface/crud/users.js +40 -6
- package/dist/esm/interface/crud/users.js.map +1 -1
- package/dist/esm/known-errors.js +13 -0
- package/dist/esm/known-errors.js.map +1 -1
- package/dist/esm/schema-fields.js +6 -4
- package/dist/esm/schema-fields.js.map +1 -1
- package/dist/esm/utils/types.js.map +1 -1
- package/dist/interface/admin-interface.d.mts +2 -4
- package/dist/interface/admin-interface.d.ts +2 -4
- package/dist/interface/admin-interface.js.map +1 -1
- package/dist/interface/client-interface.js.map +1 -1
- package/dist/interface/crud/current-user.d.mts +22 -2
- package/dist/interface/crud/current-user.d.ts +22 -2
- package/dist/interface/crud/current-user.js +3 -1
- package/dist/interface/crud/current-user.js.map +1 -1
- package/dist/interface/crud/project-api-keys.d.mts +2 -2
- package/dist/interface/crud/project-api-keys.d.ts +2 -2
- package/dist/interface/crud/sign-up-rules.d.mts +13 -0
- package/dist/interface/crud/sign-up-rules.d.ts +13 -0
- package/dist/interface/crud/sign-up-rules.js +19 -0
- package/dist/interface/crud/sign-up-rules.js.map +1 -0
- package/dist/interface/crud/team-member-profiles.d.mts +14 -2
- package/dist/interface/crud/team-member-profiles.d.ts +14 -2
- package/dist/interface/crud/users.d.mts +52 -4
- package/dist/interface/crud/users.d.ts +52 -4
- package/dist/interface/crud/users.js +40 -6
- package/dist/interface/crud/users.js.map +1 -1
- package/dist/interface/webhooks.d.mts +14 -2
- package/dist/interface/webhooks.d.ts +14 -2
- package/dist/known-errors.d.mts +5 -2
- package/dist/known-errors.d.ts +5 -2
- package/dist/known-errors.js +13 -0
- package/dist/known-errors.js.map +1 -1
- package/dist/schema-fields.d.mts +9 -3
- package/dist/schema-fields.d.ts +9 -3
- package/dist/schema-fields.js +7 -4
- package/dist/schema-fields.js.map +1 -1
- package/dist/sessions.d.mts +2 -2
- package/dist/sessions.d.ts +2 -2
- package/dist/utils/types.d.mts +2 -2
- package/dist/utils/types.d.ts +2 -2
- package/dist/utils/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// src/config/db-sync-mappings.ts
|
|
2
|
+
var DEFAULT_DB_SYNC_MAPPINGS = {
|
|
3
|
+
"users": {
|
|
4
|
+
sourceTables: { "ProjectUser": "ProjectUser" },
|
|
5
|
+
targetTable: "users",
|
|
6
|
+
targetTableSchemas: {
|
|
7
|
+
postgres: `
|
|
8
|
+
CREATE TABLE IF NOT EXISTS "users" (
|
|
9
|
+
"id" uuid PRIMARY KEY NOT NULL,
|
|
10
|
+
"display_name" text,
|
|
11
|
+
"profile_image_url" text,
|
|
12
|
+
"primary_email" text,
|
|
13
|
+
"primary_email_verified" boolean NOT NULL DEFAULT false,
|
|
14
|
+
"signed_up_at" timestamp without time zone NOT NULL,
|
|
15
|
+
"client_metadata" jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
16
|
+
"client_read_only_metadata" jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
17
|
+
"server_metadata" jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
18
|
+
"is_anonymous" boolean NOT NULL DEFAULT false
|
|
19
|
+
);
|
|
20
|
+
REVOKE ALL ON "users" FROM PUBLIC;
|
|
21
|
+
GRANT SELECT ON "users" TO PUBLIC;
|
|
22
|
+
|
|
23
|
+
CREATE TABLE IF NOT EXISTS "_stack_sync_metadata" (
|
|
24
|
+
"mapping_name" text PRIMARY KEY NOT NULL,
|
|
25
|
+
"last_synced_sequence_id" bigint NOT NULL DEFAULT -1,
|
|
26
|
+
"updated_at" timestamp without time zone NOT NULL DEFAULT now()
|
|
27
|
+
);
|
|
28
|
+
`.trim()
|
|
29
|
+
},
|
|
30
|
+
internalDbFetchQuery: `
|
|
31
|
+
SELECT *
|
|
32
|
+
FROM (
|
|
33
|
+
SELECT
|
|
34
|
+
"ProjectUser"."projectUserId" AS "id",
|
|
35
|
+
"ProjectUser"."displayName" AS "display_name",
|
|
36
|
+
"ProjectUser"."profileImageUrl" AS "profile_image_url",
|
|
37
|
+
(
|
|
38
|
+
SELECT "ContactChannel"."value"
|
|
39
|
+
FROM "ContactChannel"
|
|
40
|
+
WHERE "ContactChannel"."projectUserId" = "ProjectUser"."projectUserId"
|
|
41
|
+
AND "ContactChannel"."tenancyId" = "ProjectUser"."tenancyId"
|
|
42
|
+
AND "ContactChannel"."type" = 'EMAIL'
|
|
43
|
+
AND "ContactChannel"."isPrimary" = 'TRUE'
|
|
44
|
+
LIMIT 1
|
|
45
|
+
) AS "primary_email",
|
|
46
|
+
COALESCE(
|
|
47
|
+
(
|
|
48
|
+
SELECT "ContactChannel"."isVerified"
|
|
49
|
+
FROM "ContactChannel"
|
|
50
|
+
WHERE "ContactChannel"."projectUserId" = "ProjectUser"."projectUserId"
|
|
51
|
+
AND "ContactChannel"."tenancyId" = "ProjectUser"."tenancyId"
|
|
52
|
+
AND "ContactChannel"."type" = 'EMAIL'
|
|
53
|
+
AND "ContactChannel"."isPrimary" = 'TRUE'
|
|
54
|
+
LIMIT 1
|
|
55
|
+
),
|
|
56
|
+
false
|
|
57
|
+
) AS "primary_email_verified",
|
|
58
|
+
"ProjectUser"."createdAt" AS "signed_up_at",
|
|
59
|
+
COALESCE("ProjectUser"."clientMetadata", '{}'::jsonb) AS "client_metadata",
|
|
60
|
+
COALESCE("ProjectUser"."clientReadOnlyMetadata", '{}'::jsonb) AS "client_read_only_metadata",
|
|
61
|
+
COALESCE("ProjectUser"."serverMetadata", '{}'::jsonb) AS "server_metadata",
|
|
62
|
+
"ProjectUser"."isAnonymous" AS "is_anonymous",
|
|
63
|
+
"ProjectUser"."sequenceId" AS "sequence_id",
|
|
64
|
+
"ProjectUser"."tenancyId",
|
|
65
|
+
false AS "is_deleted"
|
|
66
|
+
FROM "ProjectUser"
|
|
67
|
+
WHERE "ProjectUser"."tenancyId" = $1::uuid
|
|
68
|
+
|
|
69
|
+
UNION ALL
|
|
70
|
+
|
|
71
|
+
SELECT
|
|
72
|
+
("DeletedRow"."primaryKey"->>'projectUserId')::uuid AS "id",
|
|
73
|
+
NULL::text AS "display_name",
|
|
74
|
+
NULL::text AS "profile_image_url",
|
|
75
|
+
NULL::text AS "primary_email",
|
|
76
|
+
false AS "primary_email_verified",
|
|
77
|
+
"DeletedRow"."deletedAt"::timestamp without time zone AS "signed_up_at",
|
|
78
|
+
'{}'::jsonb AS "client_metadata",
|
|
79
|
+
'{}'::jsonb AS "client_read_only_metadata",
|
|
80
|
+
'{}'::jsonb AS "server_metadata",
|
|
81
|
+
false AS "is_anonymous",
|
|
82
|
+
"DeletedRow"."sequenceId" AS "sequence_id",
|
|
83
|
+
"DeletedRow"."tenancyId",
|
|
84
|
+
true AS "is_deleted"
|
|
85
|
+
FROM "DeletedRow"
|
|
86
|
+
WHERE
|
|
87
|
+
"DeletedRow"."tenancyId" = $1::uuid
|
|
88
|
+
AND "DeletedRow"."tableName" = 'ProjectUser'
|
|
89
|
+
) AS "_src"
|
|
90
|
+
WHERE "sequence_id" IS NOT NULL
|
|
91
|
+
AND "sequence_id" > $2::bigint
|
|
92
|
+
ORDER BY "sequence_id" ASC
|
|
93
|
+
LIMIT 1000
|
|
94
|
+
`.trim(),
|
|
95
|
+
// Last parameter = mapping_name (for metadata tracking)
|
|
96
|
+
externalDbUpdateQueries: {
|
|
97
|
+
postgres: `
|
|
98
|
+
WITH params AS (
|
|
99
|
+
SELECT
|
|
100
|
+
$1::uuid AS "id",
|
|
101
|
+
$2::text AS "display_name",
|
|
102
|
+
$3::text AS "profile_image_url",
|
|
103
|
+
$4::text AS "primary_email",
|
|
104
|
+
$5::boolean AS "primary_email_verified",
|
|
105
|
+
$6::timestamp without time zone AS "signed_up_at",
|
|
106
|
+
$7::jsonb AS "client_metadata",
|
|
107
|
+
$8::jsonb AS "client_read_only_metadata",
|
|
108
|
+
$9::jsonb AS "server_metadata",
|
|
109
|
+
$10::boolean AS "is_anonymous",
|
|
110
|
+
$11::bigint AS "sequence_id",
|
|
111
|
+
$12::boolean AS "is_deleted",
|
|
112
|
+
$13::text AS "mapping_name"
|
|
113
|
+
),
|
|
114
|
+
deleted AS (
|
|
115
|
+
DELETE FROM "users" u
|
|
116
|
+
USING params p
|
|
117
|
+
WHERE p."is_deleted" = true AND u."id" = p."id"
|
|
118
|
+
RETURNING 1
|
|
119
|
+
),
|
|
120
|
+
upserted AS (
|
|
121
|
+
INSERT INTO "users" (
|
|
122
|
+
"id",
|
|
123
|
+
"display_name",
|
|
124
|
+
"profile_image_url",
|
|
125
|
+
"primary_email",
|
|
126
|
+
"primary_email_verified",
|
|
127
|
+
"signed_up_at",
|
|
128
|
+
"client_metadata",
|
|
129
|
+
"client_read_only_metadata",
|
|
130
|
+
"server_metadata",
|
|
131
|
+
"is_anonymous"
|
|
132
|
+
)
|
|
133
|
+
SELECT
|
|
134
|
+
p."id",
|
|
135
|
+
p."display_name",
|
|
136
|
+
p."profile_image_url",
|
|
137
|
+
p."primary_email",
|
|
138
|
+
p."primary_email_verified",
|
|
139
|
+
p."signed_up_at",
|
|
140
|
+
p."client_metadata",
|
|
141
|
+
p."client_read_only_metadata",
|
|
142
|
+
p."server_metadata",
|
|
143
|
+
p."is_anonymous"
|
|
144
|
+
FROM params p
|
|
145
|
+
WHERE p."is_deleted" = false
|
|
146
|
+
ON CONFLICT ("id") DO UPDATE SET
|
|
147
|
+
"display_name" = EXCLUDED."display_name",
|
|
148
|
+
"profile_image_url" = EXCLUDED."profile_image_url",
|
|
149
|
+
"primary_email" = EXCLUDED."primary_email",
|
|
150
|
+
"primary_email_verified" = EXCLUDED."primary_email_verified",
|
|
151
|
+
"signed_up_at" = EXCLUDED."signed_up_at",
|
|
152
|
+
"client_metadata" = EXCLUDED."client_metadata",
|
|
153
|
+
"client_read_only_metadata" = EXCLUDED."client_read_only_metadata",
|
|
154
|
+
"server_metadata" = EXCLUDED."server_metadata",
|
|
155
|
+
"is_anonymous" = EXCLUDED."is_anonymous"
|
|
156
|
+
RETURNING 1
|
|
157
|
+
)
|
|
158
|
+
INSERT INTO "_stack_sync_metadata" ("mapping_name", "last_synced_sequence_id", "updated_at")
|
|
159
|
+
SELECT p."mapping_name", p."sequence_id", now() FROM params p
|
|
160
|
+
ON CONFLICT ("mapping_name") DO UPDATE SET
|
|
161
|
+
"last_synced_sequence_id" = GREATEST("_stack_sync_metadata"."last_synced_sequence_id", EXCLUDED."last_synced_sequence_id"),
|
|
162
|
+
"updated_at" = now();
|
|
163
|
+
`.trim()
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
export {
|
|
168
|
+
DEFAULT_DB_SYNC_MAPPINGS
|
|
169
|
+
};
|
|
170
|
+
//# sourceMappingURL=db-sync-mappings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/config/db-sync-mappings.ts"],"sourcesContent":["export const DEFAULT_DB_SYNC_MAPPINGS = {\n \"users\": {\n sourceTables: { \"ProjectUser\": \"ProjectUser\" },\n targetTable: \"users\",\n targetTableSchemas: {\n postgres: `\n CREATE TABLE IF NOT EXISTS \"users\" (\n \"id\" uuid PRIMARY KEY NOT NULL,\n \"display_name\" text,\n \"profile_image_url\" text,\n \"primary_email\" text,\n \"primary_email_verified\" boolean NOT NULL DEFAULT false,\n \"signed_up_at\" timestamp without time zone NOT NULL,\n \"client_metadata\" jsonb NOT NULL DEFAULT '{}'::jsonb,\n \"client_read_only_metadata\" jsonb NOT NULL DEFAULT '{}'::jsonb,\n \"server_metadata\" jsonb NOT NULL DEFAULT '{}'::jsonb,\n \"is_anonymous\" boolean NOT NULL DEFAULT false\n );\n REVOKE ALL ON \"users\" FROM PUBLIC;\n GRANT SELECT ON \"users\" TO PUBLIC;\n\n CREATE TABLE IF NOT EXISTS \"_stack_sync_metadata\" (\n \"mapping_name\" text PRIMARY KEY NOT NULL,\n \"last_synced_sequence_id\" bigint NOT NULL DEFAULT -1,\n \"updated_at\" timestamp without time zone NOT NULL DEFAULT now()\n );\n `.trim(),\n },\n internalDbFetchQuery: `\n SELECT *\n FROM (\n SELECT\n \"ProjectUser\".\"projectUserId\" AS \"id\",\n \"ProjectUser\".\"displayName\" AS \"display_name\",\n \"ProjectUser\".\"profileImageUrl\" AS \"profile_image_url\",\n (\n SELECT \"ContactChannel\".\"value\"\n FROM \"ContactChannel\"\n WHERE \"ContactChannel\".\"projectUserId\" = \"ProjectUser\".\"projectUserId\"\n AND \"ContactChannel\".\"tenancyId\" = \"ProjectUser\".\"tenancyId\"\n AND \"ContactChannel\".\"type\" = 'EMAIL'\n AND \"ContactChannel\".\"isPrimary\" = 'TRUE'\n LIMIT 1\n ) AS \"primary_email\",\n COALESCE(\n (\n SELECT \"ContactChannel\".\"isVerified\"\n FROM \"ContactChannel\"\n WHERE \"ContactChannel\".\"projectUserId\" = \"ProjectUser\".\"projectUserId\"\n AND \"ContactChannel\".\"tenancyId\" = \"ProjectUser\".\"tenancyId\"\n AND \"ContactChannel\".\"type\" = 'EMAIL'\n AND \"ContactChannel\".\"isPrimary\" = 'TRUE'\n LIMIT 1\n ),\n false\n ) AS \"primary_email_verified\",\n \"ProjectUser\".\"createdAt\" AS \"signed_up_at\",\n COALESCE(\"ProjectUser\".\"clientMetadata\", '{}'::jsonb) AS \"client_metadata\",\n COALESCE(\"ProjectUser\".\"clientReadOnlyMetadata\", '{}'::jsonb) AS \"client_read_only_metadata\",\n COALESCE(\"ProjectUser\".\"serverMetadata\", '{}'::jsonb) AS \"server_metadata\",\n \"ProjectUser\".\"isAnonymous\" AS \"is_anonymous\",\n \"ProjectUser\".\"sequenceId\" AS \"sequence_id\",\n \"ProjectUser\".\"tenancyId\",\n false AS \"is_deleted\"\n FROM \"ProjectUser\"\n WHERE \"ProjectUser\".\"tenancyId\" = $1::uuid\n\n UNION ALL\n\n SELECT\n (\"DeletedRow\".\"primaryKey\"->>'projectUserId')::uuid AS \"id\",\n NULL::text AS \"display_name\",\n NULL::text AS \"profile_image_url\",\n NULL::text AS \"primary_email\",\n false AS \"primary_email_verified\",\n \"DeletedRow\".\"deletedAt\"::timestamp without time zone AS \"signed_up_at\",\n '{}'::jsonb AS \"client_metadata\",\n '{}'::jsonb AS \"client_read_only_metadata\",\n '{}'::jsonb AS \"server_metadata\",\n false AS \"is_anonymous\",\n \"DeletedRow\".\"sequenceId\" AS \"sequence_id\",\n \"DeletedRow\".\"tenancyId\",\n true AS \"is_deleted\"\n FROM \"DeletedRow\"\n WHERE\n \"DeletedRow\".\"tenancyId\" = $1::uuid\n AND \"DeletedRow\".\"tableName\" = 'ProjectUser'\n ) AS \"_src\"\n WHERE \"sequence_id\" IS NOT NULL\n AND \"sequence_id\" > $2::bigint\n ORDER BY \"sequence_id\" ASC\n LIMIT 1000\n `.trim(),\n // Last parameter = mapping_name (for metadata tracking)\n externalDbUpdateQueries: {\n postgres: `\n WITH params AS (\n SELECT\n $1::uuid AS \"id\",\n $2::text AS \"display_name\",\n $3::text AS \"profile_image_url\",\n $4::text AS \"primary_email\",\n $5::boolean AS \"primary_email_verified\",\n $6::timestamp without time zone AS \"signed_up_at\",\n $7::jsonb AS \"client_metadata\",\n $8::jsonb AS \"client_read_only_metadata\",\n $9::jsonb AS \"server_metadata\",\n $10::boolean AS \"is_anonymous\",\n $11::bigint AS \"sequence_id\",\n $12::boolean AS \"is_deleted\",\n $13::text AS \"mapping_name\"\n ),\n deleted AS (\n DELETE FROM \"users\" u\n USING params p\n WHERE p.\"is_deleted\" = true AND u.\"id\" = p.\"id\"\n RETURNING 1\n ),\n upserted AS (\n INSERT INTO \"users\" (\n \"id\",\n \"display_name\",\n \"profile_image_url\",\n \"primary_email\",\n \"primary_email_verified\",\n \"signed_up_at\",\n \"client_metadata\",\n \"client_read_only_metadata\",\n \"server_metadata\",\n \"is_anonymous\"\n )\n SELECT\n p.\"id\",\n p.\"display_name\",\n p.\"profile_image_url\",\n p.\"primary_email\",\n p.\"primary_email_verified\",\n p.\"signed_up_at\",\n p.\"client_metadata\",\n p.\"client_read_only_metadata\",\n p.\"server_metadata\",\n p.\"is_anonymous\"\n FROM params p\n WHERE p.\"is_deleted\" = false\n ON CONFLICT (\"id\") DO UPDATE SET\n \"display_name\" = EXCLUDED.\"display_name\",\n \"profile_image_url\" = EXCLUDED.\"profile_image_url\",\n \"primary_email\" = EXCLUDED.\"primary_email\",\n \"primary_email_verified\" = EXCLUDED.\"primary_email_verified\",\n \"signed_up_at\" = EXCLUDED.\"signed_up_at\",\n \"client_metadata\" = EXCLUDED.\"client_metadata\",\n \"client_read_only_metadata\" = EXCLUDED.\"client_read_only_metadata\",\n \"server_metadata\" = EXCLUDED.\"server_metadata\",\n \"is_anonymous\" = EXCLUDED.\"is_anonymous\"\n RETURNING 1\n )\n INSERT INTO \"_stack_sync_metadata\" (\"mapping_name\", \"last_synced_sequence_id\", \"updated_at\")\n SELECT p.\"mapping_name\", p.\"sequence_id\", now() FROM params p\n ON CONFLICT (\"mapping_name\") DO UPDATE SET\n \"last_synced_sequence_id\" = GREATEST(\"_stack_sync_metadata\".\"last_synced_sequence_id\", EXCLUDED.\"last_synced_sequence_id\"),\n \"updated_at\" = now();\n `.trim(),\n },\n },\n} as const;\n"],"mappings":";AAAO,IAAM,2BAA2B;AAAA,EACtC,SAAS;AAAA,IACP,cAAc,EAAE,eAAe,cAAc;AAAA,IAC7C,aAAa;AAAA,IACb,oBAAoB;AAAA,MAClB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAqBR,KAAK;AAAA,IACT;AAAA,IACA,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAgEpB,KAAK;AAAA;AAAA,IAEP,yBAAyB;AAAA,MACvB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAkER,KAAK;AAAA,IACT;AAAA,EACF;AACF;","names":[]}
|
|
@@ -33,6 +33,30 @@ var branchSchemaFuzzerConfig = [{
|
|
|
33
33
|
allowConnectedAccounts: [true, false]
|
|
34
34
|
}]
|
|
35
35
|
}]
|
|
36
|
+
}],
|
|
37
|
+
signUpRules: [{
|
|
38
|
+
"some-rule-id": [{
|
|
39
|
+
enabled: [true, false],
|
|
40
|
+
displayName: ["Block Test Emails", "Allow Only Company Domain"],
|
|
41
|
+
priority: [0, 1, 100],
|
|
42
|
+
condition: ['email.endsWith("@test.com")', 'emailDomain == "company.com"'],
|
|
43
|
+
action: [{
|
|
44
|
+
type: ["allow", "reject", "restrict", "log"],
|
|
45
|
+
message: ["", "Sign up is not allowed for this email"]
|
|
46
|
+
}]
|
|
47
|
+
}]
|
|
48
|
+
}],
|
|
49
|
+
signUpRulesDefaultAction: ["allow", "reject"]
|
|
50
|
+
}],
|
|
51
|
+
dbSync: [{
|
|
52
|
+
externalDatabases: [{
|
|
53
|
+
"some-external-db-id": [{
|
|
54
|
+
type: ["postgres"],
|
|
55
|
+
connectionString: [
|
|
56
|
+
"postgres://user:password@host:port/database",
|
|
57
|
+
"some-connection-string"
|
|
58
|
+
]
|
|
59
|
+
}]
|
|
36
60
|
}]
|
|
37
61
|
}],
|
|
38
62
|
dataVault: [{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/config/schema-fuzzer.test.ts"],"sourcesContent":["import { ALL_APPS } from \"../apps/apps-config\";\nimport { SUPPORTED_CURRENCIES } from \"../utils/currency-constants\";\nimport { StackAssertionError } from \"../utils/errors\";\nimport { getOrUndefined, isObjectLike, set, typedEntries, typedFromEntries } from \"../utils/objects\";\nimport { nicify } from \"../utils/strings\";\nimport { normalize, override } from \"./format\";\nimport { BranchConfigNormalizedOverride, EnvironmentConfigNormalizedOverride, OrganizationConfigNormalizedOverride, ProjectConfigNormalizedOverride, applyBranchDefaults, applyEnvironmentDefaults, applyOrganizationDefaults, applyProjectDefaults, assertNoConfigOverrideErrors, branchConfigSchema, environmentConfigSchema, migrateConfigOverride, organizationConfigSchema, projectConfigSchema, sanitizeBranchConfig, sanitizeEnvironmentConfig, sanitizeOrganizationConfig, sanitizeProjectConfig } from \"./schema\";\n\ntype FuzzerConfig<T> = ReadonlyArray<T extends object ? ([T] extends [any[]] ? { readonly [K in keyof T]: FuzzerConfig<T[K]> } : Required<{\n [K in keyof T]: FuzzerConfig<T[K]>;\n}> & Record<string, FuzzerConfig<any>>) : T>;\n\nconst projectSchemaFuzzerConfig = [{\n sourceOfTruth: [{\n type: [\"hosted\", \"neon\", \"postgres\"],\n connectionString: [\"\", \"postgres://user:password@host:port/database\", \"THIS IS A STRING LOLOL\"],\n connectionStrings: [{\n \"123-some-branch-id\": [\"\", \"THIS IS A CONNECTION STRING OR SO\"],\n }],\n }],\n}] satisfies FuzzerConfig<ProjectConfigNormalizedOverride>;\n\nconst branchSchemaFuzzerConfig = [{\n apiKeys: [{\n enabled: [{\n team: [true, false],\n user: [true, false],\n }],\n }],\n auth: [{\n allowSignUp: [true, false],\n password: [{\n allowSignIn: [true, false],\n }],\n otp: [{\n allowSignIn: [true, false],\n }],\n passkey: [{\n allowSignIn: [true, false],\n }],\n oauth: [{\n accountMergeStrategy: [\"link_method\", \"raise_error\", \"allow_duplicates\"],\n providers: [{\n \"google\": [{\n type: [\"google\", \"github\", \"x\"] as const,\n allowSignIn: [true, false],\n allowConnectedAccounts: [true, false],\n }],\n }],\n }],\n }],\n dataVault: [{\n stores: [{\n \"some-store-id\": [{\n displayName: [\"Some Store\", \"Some Other Store\"],\n }],\n \"some-other-store-id\": [{\n displayName: [\"Some Store\", \"Some Other Store\"],\n }],\n }],\n }],\n payments: [{\n blockNewPurchases: [false, true],\n autoPay: [{\n interval: [[[0, 1, -3, 100, 0.333, Infinity], [\"day\", \"week\", \"month\", \"year\"]]] as const,\n }],\n productLines: [{\n \"some-product-line-id\": [{\n displayName: [\"Some Product Line\", \"Some Other Product Line\"],\n customerType: [\"user\", \"team\", \"custom\"] as const,\n }],\n }],\n catalogs: [{ // ensure migration works\n \"some-product-line-id\": [{\n displayName: [\"Some Product Line\", \"Some Other Product Line\"],\n }],\n }],\n groups: [{ // ensure migration works\n \"some-product-line-id\": [{\n displayName: [\"Some Product Line\", \"Some Other Product Line\"],\n }],\n }],\n items: [{\n \"some-item-id\": [{\n customerType: [\"user\", \"team\", \"custom\"] as const,\n displayName: [\"Some Item\", \"Some Other Item\"],\n }],\n }],\n products: [{\n \"some-product-id\": [{\n displayName: [\"Some Product\", \"Some Other Product\"],\n customerType: [\"user\", \"team\", \"custom\"] as const,\n freeTrial: [[[0, 1, -3, 100, 0.333, Infinity], [\"day\", \"week\", \"month\", \"year\"]]] as const,\n serverOnly: [true, false],\n stackable: [true, false],\n productLineId: [\"some-product-line-id\", \"some-other-product-line-id\"],\n catalogId: [\"some-product-line-id\", \"some-other-product-line-id\"], // ensure migration works\n groupId: [\"some-product-line-id\", \"some-other-product-line-id\"], // ensure migration works\n isAddOnTo: [false, { \"some-product-id\": [true], \"some-other-product-id\": [true] }] as const,\n prices: [\"include-by-default\" as \"include-by-default\", {\n \"some-price-id\": [{\n ...typedFromEntries(SUPPORTED_CURRENCIES.map(currency => [currency.code, [\"100_00\", \"not a number\", \"Infinity\", \"0\"]])),\n interval: [[[0, 1, -3, 100, 0.333, Infinity], [\"day\", \"week\", \"month\", \"year\"]]] as const,\n serverOnly: [true, false],\n freeTrial: [[[0, 1, -3, 100, 0.333, Infinity], [\"day\", \"week\", \"month\", \"year\"]]] as const,\n }],\n }],\n includedItems: [{\n \"some-item-id\": [{\n quantity: [0, 1, -3, 100, 0.333, Infinity],\n repeat: [\"never\", [[0, 1, -3, 100, 0.333, Infinity], [\"day\", \"week\", \"month\", \"year\"]]] as const,\n expires: [\"never\", \"when-purchase-expires\", \"when-repeated\"] as const,\n }],\n }],\n }],\n }],\n }],\n emails: [{\n themes: [{\n \"12345678-1234-4234-9234-123456789012\": [{\n displayName: [\"Some Theme\", \"Some Other Theme\"],\n tsxSource: [\"\", \"some typescript source code\"],\n }],\n }],\n selectedThemeId: [\"some-theme-id\", \"some-other-theme-id\"],\n templates: [{\n \"12345678-1234-4234-9234-123456789012\": [{\n themeId: [\"some-theme-id\", \"some-other-theme-id\"],\n displayName: [\"Some Template\", \"Some Other Template\"],\n tsxSource: [\"\", \"some typescript source code\"],\n }],\n }],\n }],\n teams: [{\n createPersonalTeamOnSignUp: [true, false],\n allowClientTeamCreation: [true, false],\n }],\n users: [{\n allowClientUserDeletion: [true, false],\n }],\n rbac: [{\n permissions: [{\n \"some_permission_id\": [{\n containedPermissionIds: [{\n \"some_permission_id\": [true],\n \"$some_other_permission_id\": [true],\n }] as const,\n description: [\"Some Permission\", \"Some Other Permission\"],\n scope: [\"team\", \"project\"] as const,\n }],\n }],\n defaultPermissions: [{\n teamCreator: [{\n \"some_permission_id\": [true],\n \"$some_other_permission_id\": [true],\n }] as const,\n teamMember: [{\n \"some_permission_id\": [true],\n \"$some_other_permission_id\": [true],\n }] as const,\n signUp: [{\n \"some_permission_id\": [true],\n \"$some_other_permission_id\": [true],\n }] as const,\n }],\n }],\n domains: [{}],\n apps: [{\n installed: [typedFromEntries(typedEntries(ALL_APPS).map(([key, value]) => [key, [{\n enabled: [true, false],\n }]]))],\n }],\n onboarding: [{\n requireEmailVerification: [true, false],\n }],\n}] satisfies FuzzerConfig<BranchConfigNormalizedOverride>;\n\nconst environmentSchemaFuzzerConfig = [{\n ...branchSchemaFuzzerConfig[0],\n auth: [{\n ...branchSchemaFuzzerConfig[0].auth[0],\n oauth: [{\n ...branchSchemaFuzzerConfig[0].auth[0].oauth[0],\n providers: [typedFromEntries(typedEntries(branchSchemaFuzzerConfig[0].auth[0].oauth[0].providers[0]).map(([key, value]) => [key, [{\n ...value[0],\n isShared: [true, false],\n clientId: [\"some-client-id\"],\n clientSecret: [\"some-client-secret\"],\n facebookConfigId: [\"some-facebook-config-id\"],\n microsoftTenantId: [\"some-microsoft-tenant-id\"],\n appleBundles: [{ \"some-bundle-id\": [{ bundleId: [\"com.example.app\"] }] }],\n }]]))] as const,\n }],\n }],\n domains: [{\n allowLocalhost: [true, false],\n trustedDomains: [{\n \"some-domain-id\": [{\n baseUrl: [\"https://example.com/something-here\"],\n handlerPath: [\"/something-here\"],\n }],\n }],\n }],\n emails: [{\n ...branchSchemaFuzzerConfig[0].emails[0],\n server: [{\n isShared: [true, false],\n provider: [\"resend\", \"smtp\"] as const,\n host: [\"example.com\", \"://super weird host that's not valid\"],\n port: [1234, 0.12543, -100, Infinity],\n username: [\"some-username\", \"some username with a space\"],\n password: [\"some-password\", \"some password with a space\"],\n senderName: [\"Some Sender\"],\n senderEmail: [\"some-sender@example.com\", \"some invalid email\"],\n }],\n }],\n payments: [{\n ...branchSchemaFuzzerConfig[0].payments[0],\n testMode: [false, true],\n }],\n}] satisfies FuzzerConfig<EnvironmentConfigNormalizedOverride>;\n\nconst organizationSchemaFuzzerConfig = environmentSchemaFuzzerConfig satisfies FuzzerConfig<OrganizationConfigNormalizedOverride>;\n\nfunction setDeep<T>(obj: T, path: string[], value: any) {\n if (!isObjectLike(obj)) return obj;\n\n if (path.length === 0) {\n throw new Error(\"Path is empty\");\n } else if (path.length === 1) {\n set(obj as any, path[0], value);\n } else {\n const [key, ...rest] = path;\n setDeep(getOrUndefined(obj as any, key), rest, value);\n }\n}\n\nfunction createFuzzerInput<T>(config: FuzzerConfig<T>, progress: number): T {\n progress = Math.min(1, 2 * progress);\n const createShouldRandom = (strength: number) => {\n const chance = Math.random() * strength * 1.2 - 0.1;\n return () => Math.random() < chance;\n };\n const createShouldObjectDependent = (strength: number) => {\n const objectChance = Math.random() * strength * 1.2 - 0.1;\n const primitiveChance = Math.random() * strength * 1.2 - 0.1;\n return (v: any) => Math.random() * Math.random() < (isObjectLike(v) ? objectChance : primitiveChance);\n };\n\n const shouldKeep = createShouldObjectDependent(progress * 1);\n const shouldMakeNested = createShouldObjectDependent(1.25);\n const shouldNull = createShouldRandom(0.25);\n\n let res: any;\n const recurse = <U>(outputPath: string[], config: FuzzerConfig<U>, forceNested: boolean, forceNonNull: boolean) => {\n let subConfig: any = config[Math.floor(Math.random() * config.length)];\n const originalValue = isObjectLike(subConfig) ? (Array.isArray(subConfig) ? [] : {}) : subConfig;\n\n const newValue = forceNonNull || !shouldNull() ? originalValue : null;\n const newOutputPath = forceNested || shouldMakeNested(originalValue) || outputPath.length === 0 ? outputPath : [outputPath.join(\".\")];\n if (outputPath.length === 0) {\n res = newValue;\n } else {\n if (forceNested || shouldKeep(originalValue)) {\n setDeep(res, newOutputPath, newValue);\n }\n }\n if (isObjectLike(subConfig)) {\n for (const [key, newValue] of typedEntries(subConfig)) {\n recurse([...newOutputPath, key], newValue, Array.isArray(subConfig), Array.isArray(subConfig));\n }\n }\n };\n recurse<T>([], config, false, true);\n return res;\n}\n\nundefined?.test(\"fuzz schemas\", async ({ expect }) => {\n const totalIterations = process.env.CI ? 1000 : 200;\n for (let i = 0; i < totalIterations; i++) {\n const projectInput = createFuzzerInput<ProjectConfigNormalizedOverride>(projectSchemaFuzzerConfig, i / totalIterations);\n const branchInput = createFuzzerInput<BranchConfigNormalizedOverride>(branchSchemaFuzzerConfig, i / totalIterations);\n const environmentInput = createFuzzerInput<EnvironmentConfigNormalizedOverride>(environmentSchemaFuzzerConfig, i / totalIterations);\n const organizationInput = createFuzzerInput<OrganizationConfigNormalizedOverride>(organizationSchemaFuzzerConfig, i / totalIterations);\n\n try {\n const projectMigrated = migrateConfigOverride(\"project\", projectInput);\n await assertNoConfigOverrideErrors(projectConfigSchema, projectMigrated);\n const projectOverridden = override({}, projectMigrated);\n await sanitizeProjectConfig(normalize(applyProjectDefaults(projectOverridden), { onDotIntoNonObject: \"ignore\" }) as any);\n\n const branchMigrated = migrateConfigOverride(\"branch\", branchInput);\n await assertNoConfigOverrideErrors(branchConfigSchema, branchMigrated);\n const branchOverridden = override(projectOverridden, branchMigrated);\n await sanitizeBranchConfig(normalize(applyBranchDefaults(branchOverridden), { onDotIntoNonObject: \"ignore\" }) as any);\n\n const environmentMigrated = migrateConfigOverride(\"environment\", environmentInput);\n await assertNoConfigOverrideErrors(environmentConfigSchema, environmentMigrated);\n const environmentOverridden = override(branchOverridden, environmentMigrated);\n await sanitizeEnvironmentConfig(normalize(applyEnvironmentDefaults(environmentOverridden), { onDotIntoNonObject: \"ignore\" }) as any);\n\n const organizationMigrated = migrateConfigOverride(\"organization\", organizationInput);\n await assertNoConfigOverrideErrors(organizationConfigSchema, organizationMigrated);\n const organizationOverridden = override(environmentOverridden, organizationMigrated);\n await sanitizeOrganizationConfig(normalize(applyOrganizationDefaults(organizationOverridden), { onDotIntoNonObject: \"ignore\" }) as any);\n\n } catch (e) {\n const data = {\n cause: e,\n inputs: {\n projectInput,\n branchInput,\n environmentInput,\n organizationInput,\n },\n } as const;\n console.error(\"Failed to fuzz schema in iteration ${i}/${totalIterations}!\", nicify(data));\n throw new StackAssertionError(`Error in iteration ${i}/${totalIterations} of schema fuzz: ${e}`, { cause: e });\n }\n }\n});\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/config/schema-fuzzer.test.ts"],"sourcesContent":["import { ALL_APPS } from \"../apps/apps-config\";\nimport { SUPPORTED_CURRENCIES } from \"../utils/currency-constants\";\nimport { StackAssertionError } from \"../utils/errors\";\nimport { getOrUndefined, isObjectLike, set, typedEntries, typedFromEntries } from \"../utils/objects\";\nimport { nicify } from \"../utils/strings\";\nimport { normalize, override } from \"./format\";\nimport { BranchConfigNormalizedOverride, EnvironmentConfigNormalizedOverride, OrganizationConfigNormalizedOverride, ProjectConfigNormalizedOverride, applyBranchDefaults, applyEnvironmentDefaults, applyOrganizationDefaults, applyProjectDefaults, assertNoConfigOverrideErrors, branchConfigSchema, environmentConfigSchema, migrateConfigOverride, organizationConfigSchema, projectConfigSchema, sanitizeBranchConfig, sanitizeEnvironmentConfig, sanitizeOrganizationConfig, sanitizeProjectConfig } from \"./schema\";\n\ntype FuzzerConfig<T> = ReadonlyArray<T extends object ? ([T] extends [any[]] ? { readonly [K in keyof T]: FuzzerConfig<T[K]> } : Required<{\n [K in keyof T]: FuzzerConfig<T[K]>;\n}> & Record<string, FuzzerConfig<any>>) : T>;\n\nconst projectSchemaFuzzerConfig = [{\n sourceOfTruth: [{\n type: [\"hosted\", \"neon\", \"postgres\"],\n connectionString: [\"\", \"postgres://user:password@host:port/database\", \"THIS IS A STRING LOLOL\"],\n connectionStrings: [{\n \"123-some-branch-id\": [\"\", \"THIS IS A CONNECTION STRING OR SO\"],\n }],\n }],\n}] satisfies FuzzerConfig<ProjectConfigNormalizedOverride>;\n\nconst branchSchemaFuzzerConfig = [{\n apiKeys: [{\n enabled: [{\n team: [true, false],\n user: [true, false],\n }],\n }],\n auth: [{\n allowSignUp: [true, false],\n password: [{\n allowSignIn: [true, false],\n }],\n otp: [{\n allowSignIn: [true, false],\n }],\n passkey: [{\n allowSignIn: [true, false],\n }],\n oauth: [{\n accountMergeStrategy: [\"link_method\", \"raise_error\", \"allow_duplicates\"],\n providers: [{\n \"google\": [{\n type: [\"google\", \"github\", \"x\"] as const,\n allowSignIn: [true, false],\n allowConnectedAccounts: [true, false],\n }],\n }],\n }],\n signUpRules: [{\n \"some-rule-id\": [{\n enabled: [true, false],\n displayName: [\"Block Test Emails\", \"Allow Only Company Domain\"],\n priority: [0, 1, 100],\n condition: ['email.endsWith(\"@test.com\")', 'emailDomain == \"company.com\"'],\n action: [{\n type: [\"allow\", \"reject\", \"restrict\", \"log\"] as const,\n message: [\"\", \"Sign up is not allowed for this email\"],\n }],\n }],\n }],\n signUpRulesDefaultAction: [\"allow\", \"reject\"],\n }],\n dbSync: [{\n externalDatabases: [{\n \"some-external-db-id\": [{\n type: [\"postgres\"] as const,\n connectionString: [\n \"postgres://user:password@host:port/database\",\n \"some-connection-string\",\n ],\n }],\n }],\n }],\n dataVault: [{\n stores: [{\n \"some-store-id\": [{\n displayName: [\"Some Store\", \"Some Other Store\"],\n }],\n \"some-other-store-id\": [{\n displayName: [\"Some Store\", \"Some Other Store\"],\n }],\n }],\n }],\n payments: [{\n blockNewPurchases: [false, true],\n autoPay: [{\n interval: [[[0, 1, -3, 100, 0.333, Infinity], [\"day\", \"week\", \"month\", \"year\"]]] as const,\n }],\n productLines: [{\n \"some-product-line-id\": [{\n displayName: [\"Some Product Line\", \"Some Other Product Line\"],\n customerType: [\"user\", \"team\", \"custom\"] as const,\n }],\n }],\n catalogs: [{ // ensure migration works\n \"some-product-line-id\": [{\n displayName: [\"Some Product Line\", \"Some Other Product Line\"],\n }],\n }],\n groups: [{ // ensure migration works\n \"some-product-line-id\": [{\n displayName: [\"Some Product Line\", \"Some Other Product Line\"],\n }],\n }],\n items: [{\n \"some-item-id\": [{\n customerType: [\"user\", \"team\", \"custom\"] as const,\n displayName: [\"Some Item\", \"Some Other Item\"],\n }],\n }],\n products: [{\n \"some-product-id\": [{\n displayName: [\"Some Product\", \"Some Other Product\"],\n customerType: [\"user\", \"team\", \"custom\"] as const,\n freeTrial: [[[0, 1, -3, 100, 0.333, Infinity], [\"day\", \"week\", \"month\", \"year\"]]] as const,\n serverOnly: [true, false],\n stackable: [true, false],\n productLineId: [\"some-product-line-id\", \"some-other-product-line-id\"],\n catalogId: [\"some-product-line-id\", \"some-other-product-line-id\"], // ensure migration works\n groupId: [\"some-product-line-id\", \"some-other-product-line-id\"], // ensure migration works\n isAddOnTo: [false, { \"some-product-id\": [true], \"some-other-product-id\": [true] }] as const,\n prices: [\"include-by-default\" as \"include-by-default\", {\n \"some-price-id\": [{\n ...typedFromEntries(SUPPORTED_CURRENCIES.map(currency => [currency.code, [\"100_00\", \"not a number\", \"Infinity\", \"0\"]])),\n interval: [[[0, 1, -3, 100, 0.333, Infinity], [\"day\", \"week\", \"month\", \"year\"]]] as const,\n serverOnly: [true, false],\n freeTrial: [[[0, 1, -3, 100, 0.333, Infinity], [\"day\", \"week\", \"month\", \"year\"]]] as const,\n }],\n }],\n includedItems: [{\n \"some-item-id\": [{\n quantity: [0, 1, -3, 100, 0.333, Infinity],\n repeat: [\"never\", [[0, 1, -3, 100, 0.333, Infinity], [\"day\", \"week\", \"month\", \"year\"]]] as const,\n expires: [\"never\", \"when-purchase-expires\", \"when-repeated\"] as const,\n }],\n }],\n }],\n }],\n }],\n emails: [{\n themes: [{\n \"12345678-1234-4234-9234-123456789012\": [{\n displayName: [\"Some Theme\", \"Some Other Theme\"],\n tsxSource: [\"\", \"some typescript source code\"],\n }],\n }],\n selectedThemeId: [\"some-theme-id\", \"some-other-theme-id\"],\n templates: [{\n \"12345678-1234-4234-9234-123456789012\": [{\n themeId: [\"some-theme-id\", \"some-other-theme-id\"],\n displayName: [\"Some Template\", \"Some Other Template\"],\n tsxSource: [\"\", \"some typescript source code\"],\n }],\n }],\n }],\n teams: [{\n createPersonalTeamOnSignUp: [true, false],\n allowClientTeamCreation: [true, false],\n }],\n users: [{\n allowClientUserDeletion: [true, false],\n }],\n rbac: [{\n permissions: [{\n \"some_permission_id\": [{\n containedPermissionIds: [{\n \"some_permission_id\": [true],\n \"$some_other_permission_id\": [true],\n }] as const,\n description: [\"Some Permission\", \"Some Other Permission\"],\n scope: [\"team\", \"project\"] as const,\n }],\n }],\n defaultPermissions: [{\n teamCreator: [{\n \"some_permission_id\": [true],\n \"$some_other_permission_id\": [true],\n }] as const,\n teamMember: [{\n \"some_permission_id\": [true],\n \"$some_other_permission_id\": [true],\n }] as const,\n signUp: [{\n \"some_permission_id\": [true],\n \"$some_other_permission_id\": [true],\n }] as const,\n }],\n }],\n domains: [{}],\n apps: [{\n installed: [typedFromEntries(typedEntries(ALL_APPS).map(([key, value]) => [key, [{\n enabled: [true, false],\n }]]))],\n }],\n onboarding: [{\n requireEmailVerification: [true, false],\n }],\n}] satisfies FuzzerConfig<BranchConfigNormalizedOverride>;\n\nconst environmentSchemaFuzzerConfig = [{\n ...branchSchemaFuzzerConfig[0],\n auth: [{\n ...branchSchemaFuzzerConfig[0].auth[0],\n oauth: [{\n ...branchSchemaFuzzerConfig[0].auth[0].oauth[0],\n providers: [typedFromEntries(typedEntries(branchSchemaFuzzerConfig[0].auth[0].oauth[0].providers[0]).map(([key, value]) => [key, [{\n ...value[0],\n isShared: [true, false],\n clientId: [\"some-client-id\"],\n clientSecret: [\"some-client-secret\"],\n facebookConfigId: [\"some-facebook-config-id\"],\n microsoftTenantId: [\"some-microsoft-tenant-id\"],\n appleBundles: [{ \"some-bundle-id\": [{ bundleId: [\"com.example.app\"] }] }],\n }]]))] as const,\n }],\n }],\n domains: [{\n allowLocalhost: [true, false],\n trustedDomains: [{\n \"some-domain-id\": [{\n baseUrl: [\"https://example.com/something-here\"],\n handlerPath: [\"/something-here\"],\n }],\n }],\n }],\n emails: [{\n ...branchSchemaFuzzerConfig[0].emails[0],\n server: [{\n isShared: [true, false],\n provider: [\"resend\", \"smtp\"] as const,\n host: [\"example.com\", \"://super weird host that's not valid\"],\n port: [1234, 0.12543, -100, Infinity],\n username: [\"some-username\", \"some username with a space\"],\n password: [\"some-password\", \"some password with a space\"],\n senderName: [\"Some Sender\"],\n senderEmail: [\"some-sender@example.com\", \"some invalid email\"],\n }],\n }],\n payments: [{\n ...branchSchemaFuzzerConfig[0].payments[0],\n testMode: [false, true],\n }],\n}] satisfies FuzzerConfig<EnvironmentConfigNormalizedOverride>;\n\nconst organizationSchemaFuzzerConfig = environmentSchemaFuzzerConfig satisfies FuzzerConfig<OrganizationConfigNormalizedOverride>;\n\nfunction setDeep<T>(obj: T, path: string[], value: any) {\n if (!isObjectLike(obj)) return obj;\n\n if (path.length === 0) {\n throw new Error(\"Path is empty\");\n } else if (path.length === 1) {\n set(obj as any, path[0], value);\n } else {\n const [key, ...rest] = path;\n setDeep(getOrUndefined(obj as any, key), rest, value);\n }\n}\n\nfunction createFuzzerInput<T>(config: FuzzerConfig<T>, progress: number): T {\n progress = Math.min(1, 2 * progress);\n const createShouldRandom = (strength: number) => {\n const chance = Math.random() * strength * 1.2 - 0.1;\n return () => Math.random() < chance;\n };\n const createShouldObjectDependent = (strength: number) => {\n const objectChance = Math.random() * strength * 1.2 - 0.1;\n const primitiveChance = Math.random() * strength * 1.2 - 0.1;\n return (v: any) => Math.random() * Math.random() < (isObjectLike(v) ? objectChance : primitiveChance);\n };\n\n const shouldKeep = createShouldObjectDependent(progress * 1);\n const shouldMakeNested = createShouldObjectDependent(1.25);\n const shouldNull = createShouldRandom(0.25);\n\n let res: any;\n const recurse = <U>(outputPath: string[], config: FuzzerConfig<U>, forceNested: boolean, forceNonNull: boolean) => {\n let subConfig: any = config[Math.floor(Math.random() * config.length)];\n const originalValue = isObjectLike(subConfig) ? (Array.isArray(subConfig) ? [] : {}) : subConfig;\n\n const newValue = forceNonNull || !shouldNull() ? originalValue : null;\n const newOutputPath = forceNested || shouldMakeNested(originalValue) || outputPath.length === 0 ? outputPath : [outputPath.join(\".\")];\n if (outputPath.length === 0) {\n res = newValue;\n } else {\n if (forceNested || shouldKeep(originalValue)) {\n setDeep(res, newOutputPath, newValue);\n }\n }\n if (isObjectLike(subConfig)) {\n for (const [key, newValue] of typedEntries(subConfig)) {\n recurse([...newOutputPath, key], newValue, Array.isArray(subConfig), Array.isArray(subConfig));\n }\n }\n };\n recurse<T>([], config, false, true);\n return res;\n}\n\nundefined?.test(\"fuzz schemas\", async ({ expect }) => {\n const totalIterations = process.env.CI ? 1000 : 200;\n for (let i = 0; i < totalIterations; i++) {\n const projectInput = createFuzzerInput<ProjectConfigNormalizedOverride>(projectSchemaFuzzerConfig, i / totalIterations);\n const branchInput = createFuzzerInput<BranchConfigNormalizedOverride>(branchSchemaFuzzerConfig, i / totalIterations);\n const environmentInput = createFuzzerInput<EnvironmentConfigNormalizedOverride>(environmentSchemaFuzzerConfig, i / totalIterations);\n const organizationInput = createFuzzerInput<OrganizationConfigNormalizedOverride>(organizationSchemaFuzzerConfig, i / totalIterations);\n\n try {\n const projectMigrated = migrateConfigOverride(\"project\", projectInput);\n await assertNoConfigOverrideErrors(projectConfigSchema, projectMigrated);\n const projectOverridden = override({}, projectMigrated);\n await sanitizeProjectConfig(normalize(applyProjectDefaults(projectOverridden), { onDotIntoNonObject: \"ignore\" }) as any);\n\n const branchMigrated = migrateConfigOverride(\"branch\", branchInput);\n await assertNoConfigOverrideErrors(branchConfigSchema, branchMigrated);\n const branchOverridden = override(projectOverridden, branchMigrated);\n await sanitizeBranchConfig(normalize(applyBranchDefaults(branchOverridden), { onDotIntoNonObject: \"ignore\" }) as any);\n\n const environmentMigrated = migrateConfigOverride(\"environment\", environmentInput);\n await assertNoConfigOverrideErrors(environmentConfigSchema, environmentMigrated);\n const environmentOverridden = override(branchOverridden, environmentMigrated);\n await sanitizeEnvironmentConfig(normalize(applyEnvironmentDefaults(environmentOverridden), { onDotIntoNonObject: \"ignore\" }) as any);\n\n const organizationMigrated = migrateConfigOverride(\"organization\", organizationInput);\n await assertNoConfigOverrideErrors(organizationConfigSchema, organizationMigrated);\n const organizationOverridden = override(environmentOverridden, organizationMigrated);\n await sanitizeOrganizationConfig(normalize(applyOrganizationDefaults(organizationOverridden), { onDotIntoNonObject: \"ignore\" }) as any);\n\n } catch (e) {\n const data = {\n cause: e,\n inputs: {\n projectInput,\n branchInput,\n environmentInput,\n organizationInput,\n },\n } as const;\n console.error(\"Failed to fuzz schema in iteration ${i}/${totalIterations}!\", nicify(data));\n throw new StackAssertionError(`Error in iteration ${i}/${totalIterations} of schema fuzz: ${e}`, { cause: e });\n }\n }\n});\n"],"mappings":"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","names":[]}
|
|
@@ -9,6 +9,7 @@ import { StackAssertionError } from "../utils/errors.js";
|
|
|
9
9
|
import { allProviders } from "../utils/oauth.js";
|
|
10
10
|
import { filterUndefined, get, getOrUndefined, has, isObjectLike, mapValues, set, typedAssign, typedEntries, typedFromEntries } from "../utils/objects.js";
|
|
11
11
|
import { Result } from "../utils/results.js";
|
|
12
|
+
import { stringCompare } from "../utils/strings.js";
|
|
12
13
|
import { typeAssert, typeAssertExtends, typeAssertIs } from "../utils/types.js";
|
|
13
14
|
import { NormalizationError, assertNormalized, getInvalidConfigReason, normalize } from "./format.js";
|
|
14
15
|
import { migrateCatalogsToProductLines } from "./migrate-catalogs-to-product-lines.js";
|
|
@@ -97,7 +98,26 @@ var branchAuthSchema = yupObject({
|
|
|
97
98
|
allowConnectedAccounts: yupBoolean()
|
|
98
99
|
})
|
|
99
100
|
)
|
|
100
|
-
})
|
|
101
|
+
}),
|
|
102
|
+
signUpRules: yupRecord(
|
|
103
|
+
userSpecifiedIdSchema("signUpRuleId"),
|
|
104
|
+
yupObject({
|
|
105
|
+
enabled: yupBoolean(),
|
|
106
|
+
displayName: yupString(),
|
|
107
|
+
// Priority for rule ordering (higher number = higher priority, evaluated first)
|
|
108
|
+
// Rules with same priority are sorted alphabetically by ID
|
|
109
|
+
priority: yupNumber().integer().min(0),
|
|
110
|
+
// CEL expression string - evaluated against signup context
|
|
111
|
+
// Example: 'email.endsWith("@gmail.com") && authMethod == "password"'
|
|
112
|
+
condition: yupString(),
|
|
113
|
+
action: yupObject({
|
|
114
|
+
type: yupString().oneOf(["allow", "reject", "restrict", "log"]).defined(),
|
|
115
|
+
message: yupString().optional()
|
|
116
|
+
// for reject action custom message (internal use, not shown to user)
|
|
117
|
+
})
|
|
118
|
+
})
|
|
119
|
+
),
|
|
120
|
+
signUpRulesDefaultAction: yupString().oneOf(["allow", "reject"])
|
|
101
121
|
});
|
|
102
122
|
var branchPaymentsSchema = yupObject({
|
|
103
123
|
blockNewPurchases: yupBoolean(),
|
|
@@ -170,6 +190,15 @@ var branchConfigSchema = canNoLongerBeOverridden(projectConfigSchema, ["sourceOf
|
|
|
170
190
|
templates: schemaFields.emailTemplateListSchema
|
|
171
191
|
}),
|
|
172
192
|
payments: branchPaymentsSchema,
|
|
193
|
+
dbSync: yupObject({
|
|
194
|
+
externalDatabases: yupRecord(
|
|
195
|
+
userSpecifiedIdSchema("externalDatabaseId"),
|
|
196
|
+
yupObject({
|
|
197
|
+
type: yupString().oneOf(["postgres"]).defined(),
|
|
198
|
+
connectionString: yupString().defined()
|
|
199
|
+
})
|
|
200
|
+
)
|
|
201
|
+
}),
|
|
173
202
|
dataVault: yupObject({
|
|
174
203
|
stores: yupRecord(
|
|
175
204
|
userSpecifiedIdSchema("storeId"),
|
|
@@ -390,7 +419,18 @@ var organizationConfigDefaults = {
|
|
|
390
419
|
microsoftTenantId: void 0,
|
|
391
420
|
appleBundles: void 0
|
|
392
421
|
})
|
|
393
|
-
}
|
|
422
|
+
},
|
|
423
|
+
signUpRules: (key) => ({
|
|
424
|
+
enabled: false,
|
|
425
|
+
displayName: void 0,
|
|
426
|
+
priority: 0,
|
|
427
|
+
condition: void 0,
|
|
428
|
+
action: {
|
|
429
|
+
type: "allow",
|
|
430
|
+
message: void 0
|
|
431
|
+
}
|
|
432
|
+
}),
|
|
433
|
+
signUpRulesDefaultAction: "allow"
|
|
394
434
|
},
|
|
395
435
|
emails: {
|
|
396
436
|
server: {
|
|
@@ -447,6 +487,12 @@ var organizationConfigDefaults = {
|
|
|
447
487
|
customerType: "user"
|
|
448
488
|
})
|
|
449
489
|
},
|
|
490
|
+
dbSync: {
|
|
491
|
+
externalDatabases: (key) => ({
|
|
492
|
+
type: void 0,
|
|
493
|
+
connectionString: void 0
|
|
494
|
+
})
|
|
495
|
+
},
|
|
450
496
|
dataVault: {
|
|
451
497
|
stores: (key) => ({
|
|
452
498
|
displayName: "Unnamed Vault"
|
|
@@ -581,6 +627,12 @@ async function sanitizeOrganizationConfig(config) {
|
|
|
581
627
|
}];
|
|
582
628
|
}));
|
|
583
629
|
const appSortIndices = new Map(Object.keys(ALL_APPS).map((appId, index) => [appId, index]));
|
|
630
|
+
const sortedRuleEntries = typedEntries(prepared.auth.signUpRules).sort((a, b) => {
|
|
631
|
+
const priorityA = a[1].priority;
|
|
632
|
+
const priorityB = b[1].priority;
|
|
633
|
+
if (priorityA !== priorityB) return priorityB - priorityA;
|
|
634
|
+
return stringCompare(a[0], b[0]);
|
|
635
|
+
});
|
|
584
636
|
return {
|
|
585
637
|
...prepared,
|
|
586
638
|
auth: {
|
|
@@ -588,7 +640,8 @@ async function sanitizeOrganizationConfig(config) {
|
|
|
588
640
|
oauth: {
|
|
589
641
|
...prepared.auth.oauth,
|
|
590
642
|
providers: typedFromEntries(typedEntries(prepared.auth.oauth.providers).filter(([key, value]) => value.type !== void 0))
|
|
591
|
-
}
|
|
643
|
+
},
|
|
644
|
+
signUpRules: typedFromEntries(sortedRuleEntries)
|
|
592
645
|
},
|
|
593
646
|
emails: {
|
|
594
647
|
...prepared.emails,
|