@stackframe/stack-shared 2.8.40 → 2.8.43
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 +14 -0
- package/dist/apps/apps-config.d.mts +109 -0
- package/dist/apps/apps-config.d.ts +109 -0
- package/dist/apps/apps-config.js +137 -0
- package/dist/apps/apps-config.js.map +1 -0
- package/dist/config/format.js +3 -1
- package/dist/config/format.js.map +1 -1
- package/dist/config/schema-fuzzer.test.d.mts +2 -0
- package/dist/config/schema-fuzzer.test.d.ts +2 -0
- package/dist/config/schema-fuzzer.test.js +206 -0
- package/dist/config/schema-fuzzer.test.js.map +1 -0
- package/dist/config/schema.d.mts +302 -182
- package/dist/config/schema.d.ts +302 -182
- package/dist/config/schema.js +92 -44
- package/dist/config/schema.js.map +1 -1
- package/dist/esm/apps/apps-config.js +112 -0
- package/dist/esm/apps/apps-config.js.map +1 -0
- package/dist/esm/config/format.js +3 -1
- package/dist/esm/config/format.js.map +1 -1
- package/dist/esm/config/schema-fuzzer.test.js +204 -0
- package/dist/esm/config/schema-fuzzer.test.js.map +1 -0
- package/dist/esm/config/schema.js +94 -46
- package/dist/esm/config/schema.js.map +1 -1
- package/dist/esm/interface/admin-interface.js +0 -27
- package/dist/esm/interface/admin-interface.js.map +1 -1
- package/dist/esm/interface/client-interface.js +36 -4
- package/dist/esm/interface/client-interface.js.map +1 -1
- package/dist/esm/interface/crud/products.js +19 -0
- package/dist/esm/interface/crud/products.js.map +1 -0
- package/dist/esm/interface/crud/transactions.js +3 -3
- package/dist/esm/interface/crud/transactions.js.map +1 -1
- package/dist/esm/interface/server-interface.js +22 -0
- package/dist/esm/interface/server-interface.js.map +1 -1
- package/dist/esm/known-errors.js +30 -16
- package/dist/esm/known-errors.js.map +1 -1
- package/dist/esm/schema-fields.js +15 -14
- package/dist/esm/schema-fields.js.map +1 -1
- package/dist/esm/sessions.js +17 -5
- package/dist/esm/sessions.js.map +1 -1
- package/dist/esm/utils/objects.js +4 -0
- package/dist/esm/utils/objects.js.map +1 -1
- package/dist/esm/utils/strings.js +5 -1
- package/dist/esm/utils/strings.js.map +1 -1
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/interface/admin-interface.d.mts +1 -6
- package/dist/interface/admin-interface.d.ts +1 -6
- package/dist/interface/admin-interface.js +0 -27
- package/dist/interface/admin-interface.js.map +1 -1
- package/dist/interface/client-interface.d.mts +5 -2
- package/dist/interface/client-interface.d.ts +5 -2
- package/dist/interface/client-interface.js +36 -4
- package/dist/interface/client-interface.js.map +1 -1
- package/dist/interface/crud/products.d.mts +91 -0
- package/dist/interface/crud/products.d.ts +91 -0
- package/dist/interface/crud/products.js +45 -0
- package/dist/interface/crud/products.js.map +1 -0
- package/dist/interface/crud/transactions.d.mts +2 -2
- package/dist/interface/crud/transactions.d.ts +2 -2
- package/dist/interface/crud/transactions.js +2 -2
- package/dist/interface/crud/transactions.js.map +1 -1
- package/dist/interface/server-interface.d.mts +10 -2
- package/dist/interface/server-interface.d.ts +10 -2
- package/dist/interface/server-interface.js +22 -0
- package/dist/interface/server-interface.js.map +1 -1
- package/dist/known-errors.d.mts +7 -4
- package/dist/known-errors.d.ts +7 -4
- package/dist/known-errors.js +30 -16
- package/dist/known-errors.js.map +1 -1
- package/dist/schema-fields.d.mts +8 -6
- package/dist/schema-fields.d.ts +8 -6
- package/dist/schema-fields.js +18 -17
- package/dist/schema-fields.js.map +1 -1
- package/dist/sessions.d.mts +2 -1
- package/dist/sessions.d.ts +2 -1
- package/dist/sessions.js +16 -4
- package/dist/sessions.js.map +1 -1
- package/dist/utils/objects.js +4 -0
- package/dist/utils/objects.js.map +1 -1
- package/dist/utils/strings.js +5 -1
- package/dist/utils/strings.js.map +1 -1
- package/package.json +7 -7
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// src/apps/apps-config.ts
|
|
2
|
+
var ALL_APPS = {
|
|
3
|
+
"authentication": {
|
|
4
|
+
type: "app",
|
|
5
|
+
displayName: "Authentication",
|
|
6
|
+
subtitle: "User sign-in and account management",
|
|
7
|
+
tags: ["auth", "security"],
|
|
8
|
+
stage: "stable"
|
|
9
|
+
},
|
|
10
|
+
"teams": {
|
|
11
|
+
type: "app",
|
|
12
|
+
displayName: "Teams",
|
|
13
|
+
subtitle: "Team collaboration and management",
|
|
14
|
+
tags: ["collaboration", "organization"],
|
|
15
|
+
stage: "stable"
|
|
16
|
+
},
|
|
17
|
+
"rbac": {
|
|
18
|
+
type: "app",
|
|
19
|
+
displayName: "RBAC",
|
|
20
|
+
subtitle: "Role-based access control and permissions",
|
|
21
|
+
tags: ["security", "permissions"],
|
|
22
|
+
stage: "stable"
|
|
23
|
+
},
|
|
24
|
+
"api-keys": {
|
|
25
|
+
type: "app",
|
|
26
|
+
displayName: "API Keys",
|
|
27
|
+
subtitle: "API key generation and management",
|
|
28
|
+
tags: ["api", "security"],
|
|
29
|
+
stage: "stable"
|
|
30
|
+
},
|
|
31
|
+
"payments": {
|
|
32
|
+
type: "app",
|
|
33
|
+
displayName: "Payments",
|
|
34
|
+
subtitle: "Payment processing and subscription management",
|
|
35
|
+
tags: ["billing", "monetization"],
|
|
36
|
+
stage: "stable"
|
|
37
|
+
},
|
|
38
|
+
"emails": {
|
|
39
|
+
type: "app",
|
|
40
|
+
displayName: "Emails",
|
|
41
|
+
subtitle: "Email template configuration and management",
|
|
42
|
+
tags: ["communication", "templates"],
|
|
43
|
+
stage: "stable"
|
|
44
|
+
},
|
|
45
|
+
"email-api": {
|
|
46
|
+
type: "app",
|
|
47
|
+
displayName: "Email API",
|
|
48
|
+
subtitle: "Programmatic email sending and delivery",
|
|
49
|
+
tags: ["api", "communication"],
|
|
50
|
+
stage: "alpha"
|
|
51
|
+
},
|
|
52
|
+
"data-vault": {
|
|
53
|
+
type: "app",
|
|
54
|
+
displayName: "Data Vault",
|
|
55
|
+
subtitle: "Secure storage for sensitive user data",
|
|
56
|
+
tags: ["security", "storage"],
|
|
57
|
+
stage: "stable"
|
|
58
|
+
},
|
|
59
|
+
"workflows": {
|
|
60
|
+
type: "app",
|
|
61
|
+
displayName: "Workflows",
|
|
62
|
+
subtitle: "Automated business process orchestration",
|
|
63
|
+
tags: ["automation", "processes"],
|
|
64
|
+
stage: "beta"
|
|
65
|
+
},
|
|
66
|
+
"webhooks": {
|
|
67
|
+
type: "app",
|
|
68
|
+
displayName: "Webhooks",
|
|
69
|
+
subtitle: "Real-time event notifications and integrations",
|
|
70
|
+
tags: ["integration", "events"],
|
|
71
|
+
stage: "stable"
|
|
72
|
+
},
|
|
73
|
+
"tv-mode": {
|
|
74
|
+
type: "app",
|
|
75
|
+
displayName: "TV mode",
|
|
76
|
+
subtitle: "Dashboard display for large screens",
|
|
77
|
+
tags: ["display", "monitoring"],
|
|
78
|
+
stage: "alpha"
|
|
79
|
+
},
|
|
80
|
+
"launch-checklist": {
|
|
81
|
+
type: "app",
|
|
82
|
+
displayName: "Launch Checklist",
|
|
83
|
+
subtitle: "Pre-launch verification and readiness checks",
|
|
84
|
+
tags: ["deployment", "verification"],
|
|
85
|
+
stage: "alpha"
|
|
86
|
+
},
|
|
87
|
+
"catalyst": {
|
|
88
|
+
type: "app",
|
|
89
|
+
displayName: "Catalyst",
|
|
90
|
+
subtitle: "Project scaffolding and rapid development",
|
|
91
|
+
tags: ["development", "tooling"],
|
|
92
|
+
stage: "alpha"
|
|
93
|
+
},
|
|
94
|
+
"neon": {
|
|
95
|
+
type: "integration",
|
|
96
|
+
displayName: "Neon",
|
|
97
|
+
subtitle: "Serverless Postgres database integration",
|
|
98
|
+
tags: ["database", "integration"],
|
|
99
|
+
stage: "alpha"
|
|
100
|
+
},
|
|
101
|
+
"convex": {
|
|
102
|
+
type: "integration",
|
|
103
|
+
displayName: "Convex",
|
|
104
|
+
subtitle: "Real-time backend platform integration",
|
|
105
|
+
tags: ["database", "integration", "realtime"],
|
|
106
|
+
stage: "alpha"
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
export {
|
|
110
|
+
ALL_APPS
|
|
111
|
+
};
|
|
112
|
+
//# sourceMappingURL=apps-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/apps/apps-config.ts"],"sourcesContent":["type App = {\n type: \"app\" | \"integration\",\n displayName: string,\n subtitle: string,\n tags: string[],\n stage: \"alpha\" | \"beta\" | \"stable\",\n};\n\nexport const ALL_APPS = {\n \"authentication\": {\n type: \"app\",\n displayName: \"Authentication\",\n subtitle: \"User sign-in and account management\",\n tags: [\"auth\", \"security\"],\n stage: \"stable\",\n },\n \"teams\": {\n type: \"app\",\n displayName: \"Teams\",\n subtitle: \"Team collaboration and management\",\n tags: [\"collaboration\", \"organization\"],\n stage: \"stable\",\n },\n \"rbac\": {\n type: \"app\",\n displayName: \"RBAC\",\n subtitle: \"Role-based access control and permissions\",\n tags: [\"security\", \"permissions\"],\n stage: \"stable\",\n },\n \"api-keys\": {\n type: \"app\",\n displayName: \"API Keys\",\n subtitle: \"API key generation and management\",\n tags: [\"api\", \"security\"],\n stage: \"stable\",\n },\n \"payments\": {\n type: \"app\",\n displayName: \"Payments\",\n subtitle: \"Payment processing and subscription management\",\n tags: [\"billing\", \"monetization\"],\n stage: \"stable\",\n },\n \"emails\": {\n type: \"app\",\n displayName: \"Emails\",\n subtitle: \"Email template configuration and management\",\n tags: [\"communication\", \"templates\"],\n stage: \"stable\",\n },\n \"email-api\": {\n type: \"app\",\n displayName: \"Email API\",\n subtitle: \"Programmatic email sending and delivery\",\n tags: [\"api\", \"communication\"],\n stage: \"alpha\",\n },\n \"data-vault\": {\n type: \"app\",\n displayName: \"Data Vault\",\n subtitle: \"Secure storage for sensitive user data\",\n tags: [\"security\", \"storage\"],\n stage: \"stable\",\n },\n \"workflows\": {\n type: \"app\",\n displayName: \"Workflows\",\n subtitle: \"Automated business process orchestration\",\n tags: [\"automation\", \"processes\"],\n stage: \"beta\",\n },\n \"webhooks\": {\n type: \"app\",\n displayName: \"Webhooks\",\n subtitle: \"Real-time event notifications and integrations\",\n tags: [\"integration\", \"events\"],\n stage: \"stable\",\n },\n \"tv-mode\": {\n type: \"app\",\n displayName: \"TV mode\",\n subtitle: \"Dashboard display for large screens\",\n tags: [\"display\", \"monitoring\"],\n stage: \"alpha\",\n },\n \"launch-checklist\": {\n type: \"app\",\n displayName: \"Launch Checklist\",\n subtitle: \"Pre-launch verification and readiness checks\",\n tags: [\"deployment\", \"verification\"],\n stage: \"alpha\",\n },\n \"catalyst\": {\n type: \"app\",\n displayName: \"Catalyst\",\n subtitle: \"Project scaffolding and rapid development\",\n tags: [\"development\", \"tooling\"],\n stage: \"alpha\",\n },\n \"neon\": {\n type: \"integration\",\n displayName: \"Neon\",\n subtitle: \"Serverless Postgres database integration\",\n tags: [\"database\", \"integration\"],\n stage: \"alpha\",\n },\n \"convex\": {\n type: \"integration\",\n displayName: \"Convex\",\n subtitle: \"Real-time backend platform integration\",\n tags: [\"database\", \"integration\", \"realtime\"],\n stage: \"alpha\",\n },\n} as const satisfies Record<string, App>;\n"],"mappings":";AAQO,IAAM,WAAW;AAAA,EACtB,kBAAkB;AAAA,IAChB,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,CAAC,QAAQ,UAAU;AAAA,IACzB,OAAO;AAAA,EACT;AAAA,EACA,SAAS;AAAA,IACP,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,CAAC,iBAAiB,cAAc;AAAA,IACtC,OAAO;AAAA,EACT;AAAA,EACA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,CAAC,YAAY,aAAa;AAAA,IAChC,OAAO;AAAA,EACT;AAAA,EACA,YAAY;AAAA,IACV,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,CAAC,OAAO,UAAU;AAAA,IACxB,OAAO;AAAA,EACT;AAAA,EACA,YAAY;AAAA,IACV,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,CAAC,WAAW,cAAc;AAAA,IAChC,OAAO;AAAA,EACT;AAAA,EACA,UAAU;AAAA,IACR,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,CAAC,iBAAiB,WAAW;AAAA,IACnC,OAAO;AAAA,EACT;AAAA,EACA,aAAa;AAAA,IACX,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,CAAC,OAAO,eAAe;AAAA,IAC7B,OAAO;AAAA,EACT;AAAA,EACA,cAAc;AAAA,IACZ,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,CAAC,YAAY,SAAS;AAAA,IAC5B,OAAO;AAAA,EACT;AAAA,EACA,aAAa;AAAA,IACX,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,CAAC,cAAc,WAAW;AAAA,IAChC,OAAO;AAAA,EACT;AAAA,EACA,YAAY;AAAA,IACV,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,CAAC,eAAe,QAAQ;AAAA,IAC9B,OAAO;AAAA,EACT;AAAA,EACA,WAAW;AAAA,IACT,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,CAAC,WAAW,YAAY;AAAA,IAC9B,OAAO;AAAA,EACT;AAAA,EACA,oBAAoB;AAAA,IAClB,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,CAAC,cAAc,cAAc;AAAA,IACnC,OAAO;AAAA,EACT;AAAA,EACA,YAAY;AAAA,IACV,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,CAAC,eAAe,SAAS;AAAA,IAC/B,OAAO;AAAA,EACT;AAAA,EACA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,CAAC,YAAY,aAAa;AAAA,IAChC,OAAO;AAAA,EACT;AAAA,EACA,UAAU;AAAA,IACR,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,MAAM,CAAC,YAAY,eAAe,UAAU;AAAA,IAC5C,OAAO;AAAA,EACT;AACF;","names":[]}
|
|
@@ -30,7 +30,9 @@ function getInvalidConfigValueReason(value, options = {}) {
|
|
|
30
30
|
break;
|
|
31
31
|
} else if (Array.isArray(value)) {
|
|
32
32
|
for (const [index, v] of value.entries()) {
|
|
33
|
-
const
|
|
33
|
+
const elementValueName = `${valueName}[${index}]`;
|
|
34
|
+
if (v === null) return `${elementValueName} is null; tuple elements cannot be null`;
|
|
35
|
+
const reason = getInvalidConfigValueReason(v, { valueName: elementValueName });
|
|
34
36
|
if (reason) return reason;
|
|
35
37
|
}
|
|
36
38
|
} else {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/config/format.ts"],"sourcesContent":["// see https://github.com/stack-auth/info/blob/main/eng-handbook/random-thoughts/config-json-format.md\n\nimport { StackAssertionError, throwErr } from \"../utils/errors\";\nimport { deleteKey, filterUndefined, get, hasAndNotUndefined, set } from \"../utils/objects\";\nimport { OptionalKeys, RequiredKeys } from \"../utils/types\";\n\n\nexport type ConfigValue = string | number | boolean | null | ConfigValue[] | Config;\nexport type Config = {\n [keyOrDotNotation: string]: ConfigValue | undefined, // must support undefined for optional values\n};\n\nexport type NormalizedConfigValue = string | number | boolean | NormalizedConfig | NormalizedConfigValue[];\nexport type NormalizedConfig = {\n [key: string]: NormalizedConfigValue | undefined, // must support undefined for optional values\n};\n\nexport type _NormalizesTo<N> = N extends object ? (\n & Config\n & { [K in OptionalKeys<N>]?: _NormalizesTo<N[K]> | null }\n & { [K in RequiredKeys<N>]: undefined extends N[K] ? _NormalizesTo<N[K]> | null : _NormalizesTo<N[K]> }\n & { [K in `${string}.${string}`]: ConfigValue }\n) : N;\nexport type NormalizesTo<N extends NormalizedConfig> = _NormalizesTo<N>;\n\n/**\n * Note that a config can both be valid and not normalizable.\n */\nexport function isValidConfig(c: unknown): c is Config {\n return getInvalidConfigReason(c) === undefined;\n}\n\nexport function getInvalidConfigReason(c: unknown, options: { configName?: string } = {}): string | undefined {\n const configName = options.configName ?? 'config';\n if (c === null || typeof c !== 'object') return `${configName} must be a non-null object`;\n for (const [key, value] of Object.entries(c)) {\n if (value === undefined) continue;\n if (typeof key !== 'string') return `${configName} must have only string keys (found: ${typeof key})`;\n if (!key.match(/^[a-zA-Z0-9_:$][a-zA-Z_:$0-9\\-]*(?:\\.[a-zA-Z0-9_:$][a-zA-Z_:$0-9\\-]*)*$/)) return `All keys of ${configName} must consist of only alphanumeric characters, dots, underscores, colons, dollar signs, or hyphens and start with a character other than a hyphen (found: ${key})`;\n\n const entryName = `${configName}.${key}`;\n const reason = getInvalidConfigValueReason(value, { valueName: entryName });\n if (reason) return reason;\n }\n return undefined;\n}\n\nfunction getInvalidConfigValueReason(value: unknown, options: { valueName?: string } = {}): string | undefined {\n const valueName = options.valueName ?? 'value';\n switch (typeof value) {\n case 'string':\n case 'number':\n case 'boolean': {\n break;\n }\n case 'object': {\n if (value === null) {\n break;\n } else if (Array.isArray(value)) {\n for (const [index, v] of value.entries()) {\n const reason = getInvalidConfigValueReason(v, { valueName: `${valueName}[${index}]` });\n if (reason) return reason;\n }\n } else {\n const reason = getInvalidConfigReason(value, { configName: valueName });\n if (reason) return reason;\n }\n break;\n }\n default: {\n return `${valueName} has an invalid value type ${typeof value} (value: ${value})`;\n }\n }\n return undefined;\n}\n\nexport function assertValidConfig(c: unknown) {\n const reason = getInvalidConfigReason(c);\n if (reason) throw new StackAssertionError(`Invalid config: ${reason}`, { c });\n}\n\nexport function override(c1: Config, ...configs: Config[]) {\n if (configs.length === 0) return c1;\n if (configs.length > 1) return override(override(c1, configs[0]), ...configs.slice(1));\n const c2 = configs[0];\n\n assertValidConfig(c1);\n assertValidConfig(c2);\n\n let result = c1;\n for (const key of Object.keys(filterUndefined(c2))) {\n result = Object.fromEntries(\n Object.entries(result).filter(([k]) => k !== key && !k.startsWith(key + '.'))\n );\n }\n\n return {\n ...result,\n ...filterUndefined(c2),\n };\n}\n\nundefined?.test(\"override(...)\", ({ expect }) => {\n expect(\n override(\n {\n a: 1,\n b: 2,\n \"c.d\": 3,\n \"c.e.f\": 4,\n \"c.g\": 5,\n h: [6, { i: 7 }, 8],\n k: 123,\n l: undefined,\n },\n {\n a: 9,\n \"c.d\": 10,\n \"c.e\": null,\n \"h.0\": 11,\n \"h.1\": {\n j: 12,\n },\n k: undefined,\n },\n )\n ).toEqual({\n a: 9,\n b: 2,\n \"c.d\": 10,\n \"c.e\": null,\n \"c.g\": 5,\n h: [6, { i: 7 }, 8],\n \"h.0\": 11,\n \"h.1\": {\n j: 12,\n },\n k: 123,\n l: undefined,\n });\n});\n\ntype NormalizeOptions = {\n /**\n * What to do if a dot notation is used on a value that is not an object.\n *\n * - \"throw\" (default): Throw an error.\n * - \"ignore\": Ignore the dot notation field.\n */\n onDotIntoNonObject?: \"throw\" | \"ignore\",\n /**\n * What to do if a dot notation is used on a value that is null.\n *\n * - \"like-non-object\" (default): Treat it like a non-object. See `onDotIntoNonObject`.\n * - \"throw\": Throw an error.\n * - \"ignore\": Ignore the dot notation field.\n * - \"empty-object\": Set the value to an empty object.\n */\n onDotIntoNull?: \"like-non-object\" | \"throw\" | \"ignore\" | \"empty-object\",\n}\n\nexport class NormalizationError extends Error {\n constructor(...args: ConstructorParameters<typeof Error>) {\n super(...args);\n }\n}\nNormalizationError.prototype.name = \"NormalizationError\";\n\nexport function isNormalized(c: Config): c is NormalizedConfig {\n assertValidConfig(c);\n for (const [key, value] of Object.entries(c)) {\n if (value === undefined) continue;\n if (key.includes('.')) return false;\n if (value === null) return false;\n }\n return true;\n}\n\nexport function assertNormalized(c: Config): asserts c is NormalizedConfig {\n assertValidConfig(c);\n if (!isNormalized(c)) throw new StackAssertionError(`Config is not normalized: ${JSON.stringify(c)}`);\n}\n\nexport function normalize(c: Config, options: NormalizeOptions = {}): NormalizedConfig {\n assertValidConfig(c);\n const onDotIntoNonObject = options.onDotIntoNonObject ?? \"throw\";\n const onDotIntoNull = options.onDotIntoNull ?? \"like-non-object\";\n\n const countDots = (s: string) => s.match(/\\./g)?.length ?? 0;\n const result: NormalizedConfig = {};\n const keysByDepth = Object.keys(c).sort((a, b) => countDots(a) - countDots(b));\n\n outer: for (const key of keysByDepth) {\n const keySegmentsWithoutLast = key.split('.');\n const last = keySegmentsWithoutLast.pop() ?? throwErr('split returns empty array?');\n const value = get(c, key);\n if (value === undefined) continue;\n\n let current: NormalizedConfig = result;\n for (const keySegment of keySegmentsWithoutLast) {\n if (!hasAndNotUndefined(current, keySegment)) {\n switch (onDotIntoNull === \"like-non-object\" ? onDotIntoNonObject : onDotIntoNull) {\n case \"throw\": {\n throw new NormalizationError(`Tried to use dot notation to access ${JSON.stringify(key)}, but ${JSON.stringify(keySegment)} doesn't exist on the object (or is null).`);\n }\n case \"ignore\": {\n continue outer;\n }\n case \"empty-object\": {\n set(current, keySegment, {});\n break;\n }\n }\n }\n const value = get(current, keySegment);\n if (typeof value !== 'object') {\n switch (onDotIntoNonObject) {\n case \"throw\": {\n throw new NormalizationError(`Tried to use dot notation to access ${JSON.stringify(key)}, but ${JSON.stringify(keySegment)} is not an object.`);\n }\n case \"ignore\": {\n continue outer;\n }\n }\n }\n current = value as NormalizedConfig;\n }\n setNormalizedValue(current, last, value, options);\n }\n return result;\n}\n\nfunction normalizeValue(value: ConfigValue, options: NormalizeOptions): NormalizedConfigValue {\n if (value === null) throw new NormalizationError(\"Tried to normalize a null value\");\n if (Array.isArray(value)) return value.map(v => normalizeValue(v, options));\n if (typeof value === 'object') return normalize(value, options);\n return value;\n}\n\nfunction setNormalizedValue(result: NormalizedConfig, key: string, value: ConfigValue, options: NormalizeOptions) {\n if (value === null) {\n if (hasAndNotUndefined(result, key)) {\n deleteKey(result, key);\n }\n } else {\n set(result, key, normalizeValue(value, options));\n }\n}\n\nundefined?.test(\"normalize(...)\", ({ expect }) => {\n expect(normalize({\n a: 9,\n b: 2,\n c: {},\n \"c.d\": 10,\n \"c.e\": null,\n \"c.g\": 5,\n h: [6, { i: 7 }, 8],\n \"h.0\": 11,\n \"h.1\": {\n j: 12,\n },\n k: { l: {} },\n \"k.l.m\": 13,\n n: undefined,\n }, { onDotIntoNonObject: \"ignore\" })).toEqual({\n a: 9,\n b: 2,\n c: {\n d: 10,\n g: 5,\n },\n h: [11, { j: 12 }, 8],\n k: { l: { m: 13 } },\n });\n\n // dotting into null\n expect(() => normalize({\n \"b.c\": 2,\n }, { onDotIntoNonObject: \"throw\" })).toThrow(`Tried to use dot notation to access \"b.c\", but \"b\" doesn't exist on the object (or is null)`);\n expect(() => normalize({\n b: null,\n \"b.c\": 2,\n }, { onDotIntoNonObject: \"throw\" })).toThrow(`Tried to use dot notation to access \"b.c\", but \"b\" doesn't exist on the object (or is null)`);\n expect(normalize({\n \"b.c\": 2,\n }, { onDotIntoNonObject: \"ignore\" })).toEqual({});\n\n // dotting into non-object\n expect(() => normalize({\n b: 1,\n \"b.c\": 2,\n }, { onDotIntoNonObject: \"throw\" })).toThrow(`Tried to use dot notation to access \"b.c\", but \"b\" is not an object`);\n expect(normalize({\n b: 1,\n \"b.c\": 2,\n }, { onDotIntoNonObject: \"ignore\" })).toEqual({ b: 1 });\n});\n"],"mappings":"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","names":["value"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/config/format.ts"],"sourcesContent":["// see https://github.com/stack-auth/info/blob/main/eng-handbook/random-thoughts/config-json-format.md\n\nimport { StackAssertionError, throwErr } from \"../utils/errors\";\nimport { deleteKey, filterUndefined, get, hasAndNotUndefined, set } from \"../utils/objects\";\nimport { OptionalKeys, RequiredKeys } from \"../utils/types\";\n\n\nexport type ConfigValue = string | number | boolean | null | ConfigValue[] | Config;\nexport type Config = {\n [keyOrDotNotation: string]: ConfigValue | undefined, // must support undefined for optional values\n};\n\nexport type NormalizedConfigValue = string | number | boolean | NormalizedConfig | NormalizedConfigValue[];\nexport type NormalizedConfig = {\n [key: string]: NormalizedConfigValue | undefined, // must support undefined for optional values\n};\n\nexport type _NormalizesTo<N> = N extends object ? (\n & Config\n & { [K in OptionalKeys<N>]?: _NormalizesTo<N[K]> | null }\n & { [K in RequiredKeys<N>]: undefined extends N[K] ? _NormalizesTo<N[K]> | null : _NormalizesTo<N[K]> }\n & { [K in `${string}.${string}`]: ConfigValue }\n) : N;\nexport type NormalizesTo<N extends NormalizedConfig> = _NormalizesTo<N>;\n\n/**\n * Note that a config can both be valid and not normalizable.\n */\nexport function isValidConfig(c: unknown): c is Config {\n return getInvalidConfigReason(c) === undefined;\n}\n\nexport function getInvalidConfigReason(c: unknown, options: { configName?: string } = {}): string | undefined {\n const configName = options.configName ?? 'config';\n if (c === null || typeof c !== 'object') return `${configName} must be a non-null object`;\n for (const [key, value] of Object.entries(c)) {\n if (value === undefined) continue;\n if (typeof key !== 'string') return `${configName} must have only string keys (found: ${typeof key})`;\n if (!key.match(/^[a-zA-Z0-9_:$][a-zA-Z_:$0-9\\-]*(?:\\.[a-zA-Z0-9_:$][a-zA-Z_:$0-9\\-]*)*$/)) return `All keys of ${configName} must consist of only alphanumeric characters, dots, underscores, colons, dollar signs, or hyphens and start with a character other than a hyphen (found: ${key})`;\n\n const entryName = `${configName}.${key}`;\n const reason = getInvalidConfigValueReason(value, { valueName: entryName });\n if (reason) return reason;\n }\n return undefined;\n}\n\nfunction getInvalidConfigValueReason(value: unknown, options: { valueName?: string } = {}): string | undefined {\n const valueName = options.valueName ?? 'value';\n switch (typeof value) {\n case 'string':\n case 'number':\n case 'boolean': {\n break;\n }\n case 'object': {\n if (value === null) {\n break;\n } else if (Array.isArray(value)) {\n for (const [index, v] of value.entries()) {\n const elementValueName = `${valueName}[${index}]`;\n if (v === null) return `${elementValueName} is null; tuple elements cannot be null`;\n const reason = getInvalidConfigValueReason(v, { valueName: elementValueName });\n if (reason) return reason;\n }\n } else {\n const reason = getInvalidConfigReason(value, { configName: valueName });\n if (reason) return reason;\n }\n break;\n }\n default: {\n return `${valueName} has an invalid value type ${typeof value} (value: ${value})`;\n }\n }\n return undefined;\n}\n\nexport function assertValidConfig(c: unknown) {\n const reason = getInvalidConfigReason(c);\n if (reason) throw new StackAssertionError(`Invalid config: ${reason}`, { c });\n}\n\nexport function override(c1: Config, ...configs: Config[]) {\n if (configs.length === 0) return c1;\n if (configs.length > 1) return override(override(c1, configs[0]), ...configs.slice(1));\n const c2 = configs[0];\n\n assertValidConfig(c1);\n assertValidConfig(c2);\n\n let result = c1;\n for (const key of Object.keys(filterUndefined(c2))) {\n result = Object.fromEntries(\n Object.entries(result).filter(([k]) => k !== key && !k.startsWith(key + '.'))\n );\n }\n\n return {\n ...result,\n ...filterUndefined(c2),\n };\n}\n\nundefined?.test(\"override(...)\", ({ expect }) => {\n expect(\n override(\n {\n a: 1,\n b: 2,\n \"c.d\": 3,\n \"c.e.f\": 4,\n \"c.g\": 5,\n h: [6, { i: 7 }, 8],\n k: 123,\n l: undefined,\n },\n {\n a: 9,\n \"c.d\": 10,\n \"c.e\": null,\n \"h.0\": 11,\n \"h.1\": {\n j: 12,\n },\n k: undefined,\n },\n )\n ).toEqual({\n a: 9,\n b: 2,\n \"c.d\": 10,\n \"c.e\": null,\n \"c.g\": 5,\n h: [6, { i: 7 }, 8],\n \"h.0\": 11,\n \"h.1\": {\n j: 12,\n },\n k: 123,\n l: undefined,\n });\n});\n\ntype NormalizeOptions = {\n /**\n * What to do if a dot notation is used on a value that is not an object.\n *\n * - \"throw\" (default): Throw an error.\n * - \"ignore\": Ignore the dot notation field.\n */\n onDotIntoNonObject?: \"throw\" | \"ignore\",\n /**\n * What to do if a dot notation is used on a value that is null.\n *\n * - \"like-non-object\" (default): Treat it like a non-object. See `onDotIntoNonObject`.\n * - \"throw\": Throw an error.\n * - \"ignore\": Ignore the dot notation field.\n * - \"empty-object\": Set the value to an empty object.\n */\n onDotIntoNull?: \"like-non-object\" | \"throw\" | \"ignore\" | \"empty-object\",\n}\n\nexport class NormalizationError extends Error {\n constructor(...args: ConstructorParameters<typeof Error>) {\n super(...args);\n }\n}\nNormalizationError.prototype.name = \"NormalizationError\";\n\nexport function isNormalized(c: Config): c is NormalizedConfig {\n assertValidConfig(c);\n for (const [key, value] of Object.entries(c)) {\n if (value === undefined) continue;\n if (key.includes('.')) return false;\n if (value === null) return false;\n }\n return true;\n}\n\nexport function assertNormalized(c: Config): asserts c is NormalizedConfig {\n assertValidConfig(c);\n if (!isNormalized(c)) throw new StackAssertionError(`Config is not normalized: ${JSON.stringify(c)}`);\n}\n\nexport function normalize(c: Config, options: NormalizeOptions = {}): NormalizedConfig {\n assertValidConfig(c);\n const onDotIntoNonObject = options.onDotIntoNonObject ?? \"throw\";\n const onDotIntoNull = options.onDotIntoNull ?? \"like-non-object\";\n\n const countDots = (s: string) => s.match(/\\./g)?.length ?? 0;\n const result: NormalizedConfig = {};\n const keysByDepth = Object.keys(c).sort((a, b) => countDots(a) - countDots(b));\n\n outer: for (const key of keysByDepth) {\n const keySegmentsWithoutLast = key.split('.');\n const last = keySegmentsWithoutLast.pop() ?? throwErr('split returns empty array?');\n const value = get(c, key);\n if (value === undefined) continue;\n\n let current: NormalizedConfig = result;\n for (const keySegment of keySegmentsWithoutLast) {\n if (!hasAndNotUndefined(current, keySegment)) {\n switch (onDotIntoNull === \"like-non-object\" ? onDotIntoNonObject : onDotIntoNull) {\n case \"throw\": {\n throw new NormalizationError(`Tried to use dot notation to access ${JSON.stringify(key)}, but ${JSON.stringify(keySegment)} doesn't exist on the object (or is null).`);\n }\n case \"ignore\": {\n continue outer;\n }\n case \"empty-object\": {\n set(current, keySegment, {});\n break;\n }\n }\n }\n const value = get(current, keySegment);\n if (typeof value !== 'object') {\n switch (onDotIntoNonObject) {\n case \"throw\": {\n throw new NormalizationError(`Tried to use dot notation to access ${JSON.stringify(key)}, but ${JSON.stringify(keySegment)} is not an object.`);\n }\n case \"ignore\": {\n continue outer;\n }\n }\n }\n current = value as NormalizedConfig;\n }\n setNormalizedValue(current, last, value, options);\n }\n return result;\n}\n\nfunction normalizeValue(value: ConfigValue, options: NormalizeOptions): NormalizedConfigValue {\n if (value === null) throw new NormalizationError(\"Tried to normalize a null value\");\n if (Array.isArray(value)) return value.map(v => normalizeValue(v, options));\n if (typeof value === 'object') return normalize(value, options);\n return value;\n}\n\nfunction setNormalizedValue(result: NormalizedConfig, key: string, value: ConfigValue, options: NormalizeOptions) {\n if (value === null) {\n if (hasAndNotUndefined(result, key)) {\n deleteKey(result, key);\n }\n } else {\n set(result, key, normalizeValue(value, options));\n }\n}\n\nundefined?.test(\"normalize(...)\", ({ expect }) => {\n expect(normalize({\n a: 9,\n b: 2,\n c: {},\n \"c.d\": 10,\n \"c.e\": null,\n \"c.g\": 5,\n h: [6, { i: 7 }, 8],\n \"h.0\": 11,\n \"h.1\": {\n j: 12,\n },\n k: { l: {} },\n \"k.l.m\": 13,\n n: undefined,\n }, { onDotIntoNonObject: \"ignore\" })).toEqual({\n a: 9,\n b: 2,\n c: {\n d: 10,\n g: 5,\n },\n h: [11, { j: 12 }, 8],\n k: { l: { m: 13 } },\n });\n\n // dotting into null\n expect(() => normalize({\n \"b.c\": 2,\n }, { onDotIntoNonObject: \"throw\" })).toThrow(`Tried to use dot notation to access \"b.c\", but \"b\" doesn't exist on the object (or is null)`);\n expect(() => normalize({\n b: null,\n \"b.c\": 2,\n }, { onDotIntoNonObject: \"throw\" })).toThrow(`Tried to use dot notation to access \"b.c\", but \"b\" doesn't exist on the object (or is null)`);\n expect(normalize({\n \"b.c\": 2,\n }, { onDotIntoNonObject: \"ignore\" })).toEqual({});\n\n // dotting into non-object\n expect(() => normalize({\n b: 1,\n \"b.c\": 2,\n }, { onDotIntoNonObject: \"throw\" })).toThrow(`Tried to use dot notation to access \"b.c\", but \"b\" is not an object`);\n expect(normalize({\n b: 1,\n \"b.c\": 2,\n }, { onDotIntoNonObject: \"ignore\" })).toEqual({ b: 1 });\n});\n"],"mappings":";AAEA,SAAS,qBAAqB,gBAAgB;AAC9C,SAAS,WAAW,iBAAiB,KAAK,oBAAoB,WAAW;AAyBlE,SAAS,cAAc,GAAyB;AACrD,SAAO,uBAAuB,CAAC,MAAM;AACvC;AAEO,SAAS,uBAAuB,GAAY,UAAmC,CAAC,GAAuB;AAC5G,QAAM,aAAa,QAAQ,cAAc;AACzC,MAAI,MAAM,QAAQ,OAAO,MAAM,SAAU,QAAO,GAAG,UAAU;AAC7D,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,CAAC,GAAG;AAC5C,QAAI,UAAU,OAAW;AACzB,QAAI,OAAO,QAAQ,SAAU,QAAO,GAAG,UAAU,uCAAuC,OAAO,GAAG;AAClG,QAAI,CAAC,IAAI,MAAM,yEAAyE,EAAG,QAAO,eAAe,UAAU,6JAA6J,GAAG;AAE3R,UAAM,YAAY,GAAG,UAAU,IAAI,GAAG;AACtC,UAAM,SAAS,4BAA4B,OAAO,EAAE,WAAW,UAAU,CAAC;AAC1E,QAAI,OAAQ,QAAO;AAAA,EACrB;AACA,SAAO;AACT;AAEA,SAAS,4BAA4B,OAAgB,UAAkC,CAAC,GAAuB;AAC7G,QAAM,YAAY,QAAQ,aAAa;AACvC,UAAQ,OAAO,OAAO;AAAA,IACpB,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK,WAAW;AACd;AAAA,IACF;AAAA,IACA,KAAK,UAAU;AACb,UAAI,UAAU,MAAM;AAClB;AAAA,MACF,WAAW,MAAM,QAAQ,KAAK,GAAG;AAC/B,mBAAW,CAAC,OAAO,CAAC,KAAK,MAAM,QAAQ,GAAG;AACxC,gBAAM,mBAAmB,GAAG,SAAS,IAAI,KAAK;AAC9C,cAAI,MAAM,KAAM,QAAO,GAAG,gBAAgB;AAC1C,gBAAM,SAAS,4BAA4B,GAAG,EAAE,WAAW,iBAAiB,CAAC;AAC7E,cAAI,OAAQ,QAAO;AAAA,QACrB;AAAA,MACF,OAAO;AACL,cAAM,SAAS,uBAAuB,OAAO,EAAE,YAAY,UAAU,CAAC;AACtE,YAAI,OAAQ,QAAO;AAAA,MACrB;AACA;AAAA,IACF;AAAA,IACA,SAAS;AACP,aAAO,GAAG,SAAS,8BAA8B,OAAO,KAAK,YAAY,KAAK;AAAA,IAChF;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,kBAAkB,GAAY;AAC5C,QAAM,SAAS,uBAAuB,CAAC;AACvC,MAAI,OAAQ,OAAM,IAAI,oBAAoB,mBAAmB,MAAM,IAAI,EAAE,EAAE,CAAC;AAC9E;AAEO,SAAS,SAAS,OAAe,SAAmB;AACzD,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,MAAI,QAAQ,SAAS,EAAG,QAAO,SAAS,SAAS,IAAI,QAAQ,CAAC,CAAC,GAAG,GAAG,QAAQ,MAAM,CAAC,CAAC;AACrF,QAAM,KAAK,QAAQ,CAAC;AAEpB,oBAAkB,EAAE;AACpB,oBAAkB,EAAE;AAEpB,MAAI,SAAS;AACb,aAAW,OAAO,OAAO,KAAK,gBAAgB,EAAE,CAAC,GAAG;AAClD,aAAS,OAAO;AAAA,MACd,OAAO,QAAQ,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,MAAM,OAAO,CAAC,EAAE,WAAW,MAAM,GAAG,CAAC;AAAA,IAC9E;AAAA,EACF;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAG,gBAAgB,EAAE;AAAA,EACvB;AACF;AA6DO,IAAM,qBAAN,cAAiC,MAAM;AAAA,EAC5C,eAAe,MAA2C;AACxD,UAAM,GAAG,IAAI;AAAA,EACf;AACF;AACA,mBAAmB,UAAU,OAAO;AAE7B,SAAS,aAAa,GAAkC;AAC7D,oBAAkB,CAAC;AACnB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,CAAC,GAAG;AAC5C,QAAI,UAAU,OAAW;AACzB,QAAI,IAAI,SAAS,GAAG,EAAG,QAAO;AAC9B,QAAI,UAAU,KAAM,QAAO;AAAA,EAC7B;AACA,SAAO;AACT;AAEO,SAAS,iBAAiB,GAA0C;AACzE,oBAAkB,CAAC;AACnB,MAAI,CAAC,aAAa,CAAC,EAAG,OAAM,IAAI,oBAAoB,6BAA6B,KAAK,UAAU,CAAC,CAAC,EAAE;AACtG;AAEO,SAAS,UAAU,GAAW,UAA4B,CAAC,GAAqB;AACrF,oBAAkB,CAAC;AACnB,QAAM,qBAAqB,QAAQ,sBAAsB;AACzD,QAAM,gBAAgB,QAAQ,iBAAiB;AAE/C,QAAM,YAAY,CAAC,MAAc,EAAE,MAAM,KAAK,GAAG,UAAU;AAC3D,QAAM,SAA2B,CAAC;AAClC,QAAM,cAAc,OAAO,KAAK,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,UAAU,CAAC,IAAI,UAAU,CAAC,CAAC;AAE7E,QAAO,YAAW,OAAO,aAAa;AACpC,UAAM,yBAAyB,IAAI,MAAM,GAAG;AAC5C,UAAM,OAAO,uBAAuB,IAAI,KAAK,SAAS,4BAA4B;AAClF,UAAM,QAAQ,IAAI,GAAG,GAAG;AACxB,QAAI,UAAU,OAAW;AAEzB,QAAI,UAA4B;AAChC,eAAW,cAAc,wBAAwB;AAC/C,UAAI,CAAC,mBAAmB,SAAS,UAAU,GAAG;AAC5C,gBAAQ,kBAAkB,oBAAoB,qBAAqB,eAAe;AAAA,UAChF,KAAK,SAAS;AACZ,kBAAM,IAAI,mBAAmB,uCAAuC,KAAK,UAAU,GAAG,CAAC,SAAS,KAAK,UAAU,UAAU,CAAC,4CAA4C;AAAA,UACxK;AAAA,UACA,KAAK,UAAU;AACb,qBAAS;AAAA,UACX;AAAA,UACA,KAAK,gBAAgB;AACnB,gBAAI,SAAS,YAAY,CAAC,CAAC;AAC3B;AAAA,UACF;AAAA,QACF;AAAA,MACF;AACA,YAAMA,SAAQ,IAAI,SAAS,UAAU;AACrC,UAAI,OAAOA,WAAU,UAAU;AAC7B,gBAAQ,oBAAoB;AAAA,UAC1B,KAAK,SAAS;AACZ,kBAAM,IAAI,mBAAmB,uCAAuC,KAAK,UAAU,GAAG,CAAC,SAAS,KAAK,UAAU,UAAU,CAAC,oBAAoB;AAAA,UAChJ;AAAA,UACA,KAAK,UAAU;AACb,qBAAS;AAAA,UACX;AAAA,QACF;AAAA,MACF;AACA,gBAAUA;AAAA,IACZ;AACA,uBAAmB,SAAS,MAAM,OAAO,OAAO;AAAA,EAClD;AACA,SAAO;AACT;AAEA,SAAS,eAAe,OAAoB,SAAkD;AAC5F,MAAI,UAAU,KAAM,OAAM,IAAI,mBAAmB,iCAAiC;AAClF,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,IAAI,OAAK,eAAe,GAAG,OAAO,CAAC;AAC1E,MAAI,OAAO,UAAU,SAAU,QAAO,UAAU,OAAO,OAAO;AAC9D,SAAO;AACT;AAEA,SAAS,mBAAmB,QAA0B,KAAa,OAAoB,SAA2B;AAChH,MAAI,UAAU,MAAM;AAClB,QAAI,mBAAmB,QAAQ,GAAG,GAAG;AACnC,gBAAU,QAAQ,GAAG;AAAA,IACvB;AAAA,EACF,OAAO;AACL,QAAI,QAAQ,KAAK,eAAe,OAAO,OAAO,CAAC;AAAA,EACjD;AACF;","names":["value"]}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// src/config/schema-fuzzer.test.ts
|
|
2
|
+
import { ALL_APPS } from "../apps/apps-config.js";
|
|
3
|
+
import { SUPPORTED_CURRENCIES } from "../utils/currency-constants.js";
|
|
4
|
+
import { StackAssertionError } from "../utils/errors.js";
|
|
5
|
+
import { getOrUndefined, isObjectLike, set, typedEntries, typedFromEntries } from "../utils/objects.js";
|
|
6
|
+
import { nicify } from "../utils/strings.js";
|
|
7
|
+
import { normalize, override } from "./format.js";
|
|
8
|
+
import { applyBranchDefaults, applyEnvironmentDefaults, applyOrganizationDefaults, applyProjectDefaults, assertNoConfigOverrideErrors, branchConfigSchema, environmentConfigSchema, migrateConfigOverride, organizationConfigSchema, projectConfigSchema, sanitizeBranchConfig, sanitizeEnvironmentConfig, sanitizeOrganizationConfig, sanitizeProjectConfig } from "./schema.js";
|
|
9
|
+
var branchSchemaFuzzerConfig = [{
|
|
10
|
+
apiKeys: [{
|
|
11
|
+
enabled: [{
|
|
12
|
+
team: [true, false],
|
|
13
|
+
user: [true, false]
|
|
14
|
+
}]
|
|
15
|
+
}],
|
|
16
|
+
auth: [{
|
|
17
|
+
allowSignUp: [true, false],
|
|
18
|
+
password: [{
|
|
19
|
+
allowSignIn: [true, false]
|
|
20
|
+
}],
|
|
21
|
+
otp: [{
|
|
22
|
+
allowSignIn: [true, false]
|
|
23
|
+
}],
|
|
24
|
+
passkey: [{
|
|
25
|
+
allowSignIn: [true, false]
|
|
26
|
+
}],
|
|
27
|
+
oauth: [{
|
|
28
|
+
accountMergeStrategy: ["link_method", "raise_error", "allow_duplicates"],
|
|
29
|
+
providers: [{
|
|
30
|
+
"google": [{
|
|
31
|
+
type: ["google", "github", "x"],
|
|
32
|
+
allowSignIn: [true, false],
|
|
33
|
+
allowConnectedAccounts: [true, false]
|
|
34
|
+
}]
|
|
35
|
+
}]
|
|
36
|
+
}]
|
|
37
|
+
}],
|
|
38
|
+
dataVault: [{
|
|
39
|
+
stores: [{
|
|
40
|
+
"some-store-id": [{
|
|
41
|
+
displayName: ["Some Store", "Some Other Store"]
|
|
42
|
+
}],
|
|
43
|
+
"some-other-store-id": [{
|
|
44
|
+
displayName: ["Some Store", "Some Other Store"]
|
|
45
|
+
}]
|
|
46
|
+
}]
|
|
47
|
+
}],
|
|
48
|
+
payments: [{
|
|
49
|
+
testMode: [false, true],
|
|
50
|
+
autoPay: [{
|
|
51
|
+
interval: [[[0, 1, -3, 100, 0.333, Infinity], ["day", "week", "month", "year"]]]
|
|
52
|
+
}],
|
|
53
|
+
catalogs: [{
|
|
54
|
+
"some-catalog-id": [{
|
|
55
|
+
displayName: ["Some Catalog", "Some Other Catalog"]
|
|
56
|
+
}]
|
|
57
|
+
}],
|
|
58
|
+
groups: [{
|
|
59
|
+
"some-catalog-id": [{
|
|
60
|
+
displayName: ["Some Catalog", "Some Other Catalog"]
|
|
61
|
+
}]
|
|
62
|
+
}],
|
|
63
|
+
items: [{
|
|
64
|
+
"some-item-id": [{
|
|
65
|
+
customerType: ["user", "team", "custom"],
|
|
66
|
+
displayName: ["Some Item", "Some Other Item"]
|
|
67
|
+
}]
|
|
68
|
+
}],
|
|
69
|
+
products: [{
|
|
70
|
+
"some-product-id": [{
|
|
71
|
+
displayName: ["Some Product", "Some Other Product"],
|
|
72
|
+
customerType: ["user", "team", "custom"],
|
|
73
|
+
freeTrial: [[[0, 1, -3, 100, 0.333, Infinity], ["day", "week", "month", "year"]]],
|
|
74
|
+
serverOnly: [true, false],
|
|
75
|
+
stackable: [true, false],
|
|
76
|
+
catalogId: ["some-catalog-id", "some-other-catalog-id"],
|
|
77
|
+
groupId: ["some-catalog-id", "some-other-catalog-id"],
|
|
78
|
+
// ensure migration works
|
|
79
|
+
isAddOnTo: [false, { "some-product-id": [true], "some-other-product-id": [true] }],
|
|
80
|
+
prices: ["include-by-default", {
|
|
81
|
+
"some-price-id": [{
|
|
82
|
+
...typedFromEntries(SUPPORTED_CURRENCIES.map((currency) => [currency.code, ["100_00", "not a number", "Infinity", "0"]])),
|
|
83
|
+
interval: [[[0, 1, -3, 100, 0.333, Infinity], ["day", "week", "month", "year"]]],
|
|
84
|
+
serverOnly: [true, false],
|
|
85
|
+
freeTrial: [[[0, 1, -3, 100, 0.333, Infinity], ["day", "week", "month", "year"]]]
|
|
86
|
+
}]
|
|
87
|
+
}],
|
|
88
|
+
includedItems: [{
|
|
89
|
+
"some-item-id": [{
|
|
90
|
+
quantity: [0, 1, -3, 100, 0.333, Infinity],
|
|
91
|
+
repeat: ["never", [[0, 1, -3, 100, 0.333, Infinity], ["day", "week", "month", "year"]]],
|
|
92
|
+
expires: ["never", "when-purchase-expires", "when-repeated"]
|
|
93
|
+
}]
|
|
94
|
+
}]
|
|
95
|
+
}]
|
|
96
|
+
}]
|
|
97
|
+
}],
|
|
98
|
+
emails: [{
|
|
99
|
+
themes: [{
|
|
100
|
+
"12345678-1234-4234-9234-123456789012": [{
|
|
101
|
+
displayName: ["Some Theme", "Some Other Theme"],
|
|
102
|
+
tsxSource: ["", "some typescript source code"]
|
|
103
|
+
}]
|
|
104
|
+
}],
|
|
105
|
+
selectedThemeId: ["some-theme-id", "some-other-theme-id"],
|
|
106
|
+
templates: [{
|
|
107
|
+
"12345678-1234-4234-9234-123456789012": [{
|
|
108
|
+
themeId: ["some-theme-id", "some-other-theme-id"],
|
|
109
|
+
displayName: ["Some Template", "Some Other Template"],
|
|
110
|
+
tsxSource: ["", "some typescript source code"]
|
|
111
|
+
}]
|
|
112
|
+
}]
|
|
113
|
+
}],
|
|
114
|
+
workflows: [{
|
|
115
|
+
availableWorkflows: [{
|
|
116
|
+
"some-workflow-id": [{
|
|
117
|
+
displayName: ["Some Workflow", "Some Other Workflow"],
|
|
118
|
+
tsSource: ["", "some typescript source code"],
|
|
119
|
+
enabled: [true, false]
|
|
120
|
+
}]
|
|
121
|
+
}]
|
|
122
|
+
}],
|
|
123
|
+
teams: [{
|
|
124
|
+
createPersonalTeamOnSignUp: [true, false],
|
|
125
|
+
allowClientTeamCreation: [true, false]
|
|
126
|
+
}],
|
|
127
|
+
users: [{
|
|
128
|
+
allowClientUserDeletion: [true, false]
|
|
129
|
+
}],
|
|
130
|
+
rbac: [{
|
|
131
|
+
permissions: [{
|
|
132
|
+
"some_permission_id": [{
|
|
133
|
+
containedPermissionIds: [{
|
|
134
|
+
"some_permission_id": [true],
|
|
135
|
+
"$some_other_permission_id": [true]
|
|
136
|
+
}],
|
|
137
|
+
description: ["Some Permission", "Some Other Permission"],
|
|
138
|
+
scope: ["team", "project"]
|
|
139
|
+
}]
|
|
140
|
+
}],
|
|
141
|
+
defaultPermissions: [{
|
|
142
|
+
teamCreator: [{
|
|
143
|
+
"some_permission_id": [true],
|
|
144
|
+
"$some_other_permission_id": [true]
|
|
145
|
+
}],
|
|
146
|
+
teamMember: [{
|
|
147
|
+
"some_permission_id": [true],
|
|
148
|
+
"$some_other_permission_id": [true]
|
|
149
|
+
}],
|
|
150
|
+
signUp: [{
|
|
151
|
+
"some_permission_id": [true],
|
|
152
|
+
"$some_other_permission_id": [true]
|
|
153
|
+
}]
|
|
154
|
+
}]
|
|
155
|
+
}],
|
|
156
|
+
domains: [{
|
|
157
|
+
allowLocalhost: [true, false]
|
|
158
|
+
}],
|
|
159
|
+
apps: [{
|
|
160
|
+
installed: [typedFromEntries(typedEntries(ALL_APPS).map(([key, value]) => [key, [{
|
|
161
|
+
enabled: [true, false]
|
|
162
|
+
}]]))]
|
|
163
|
+
}]
|
|
164
|
+
}];
|
|
165
|
+
var environmentSchemaFuzzerConfig = [{
|
|
166
|
+
...branchSchemaFuzzerConfig[0],
|
|
167
|
+
auth: [{
|
|
168
|
+
...branchSchemaFuzzerConfig[0].auth[0],
|
|
169
|
+
oauth: [{
|
|
170
|
+
...branchSchemaFuzzerConfig[0].auth[0].oauth[0],
|
|
171
|
+
providers: [typedFromEntries(typedEntries(branchSchemaFuzzerConfig[0].auth[0].oauth[0].providers[0]).map(([key, value]) => [key, [{
|
|
172
|
+
...value[0],
|
|
173
|
+
isShared: [true, false],
|
|
174
|
+
clientId: ["some-client-id"],
|
|
175
|
+
clientSecret: ["some-client-secret"],
|
|
176
|
+
facebookConfigId: ["some-facebook-config-id"],
|
|
177
|
+
microsoftTenantId: ["some-microsoft-tenant-id"]
|
|
178
|
+
}]]))]
|
|
179
|
+
}]
|
|
180
|
+
}],
|
|
181
|
+
domains: [{
|
|
182
|
+
...branchSchemaFuzzerConfig[0].domains[0],
|
|
183
|
+
trustedDomains: [{
|
|
184
|
+
"some-domain-id": [{
|
|
185
|
+
baseUrl: ["https://example.com/something-here"],
|
|
186
|
+
handlerPath: ["/something-here"]
|
|
187
|
+
}]
|
|
188
|
+
}]
|
|
189
|
+
}],
|
|
190
|
+
emails: [{
|
|
191
|
+
...branchSchemaFuzzerConfig[0].emails[0],
|
|
192
|
+
server: [{
|
|
193
|
+
isShared: [true, false],
|
|
194
|
+
provider: ["resend", "smtp"],
|
|
195
|
+
host: ["example.com", "://super weird host that's not valid"],
|
|
196
|
+
port: [1234, 0.12543, -100, Infinity],
|
|
197
|
+
username: ["some-username", "some username with a space"],
|
|
198
|
+
password: ["some-password", "some password with a space"],
|
|
199
|
+
senderName: ["Some Sender"],
|
|
200
|
+
senderEmail: ["some-sender@example.com", "some invalid email"]
|
|
201
|
+
}]
|
|
202
|
+
}]
|
|
203
|
+
}];
|
|
204
|
+
//# sourceMappingURL=schema-fuzzer.test.js.map
|
|
@@ -0,0 +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 testMode: [false, true],\n autoPay: [{\n interval: [[[0, 1, -3, 100, 0.333, Infinity], [\"day\", \"week\", \"month\", \"year\"]]] as const,\n }],\n catalogs: [{\n \"some-catalog-id\": [{\n displayName: [\"Some Catalog\", \"Some Other Catalog\"],\n }],\n }],\n groups: [{\n \"some-catalog-id\": [{\n displayName: [\"Some Catalog\", \"Some Other Catalog\"],\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 catalogId: [\"some-catalog-id\", \"some-other-catalog-id\"],\n groupId: [\"some-catalog-id\", \"some-other-catalog-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 workflows: [{\n availableWorkflows: [{\n \"some-workflow-id\": [{\n displayName: [\"Some Workflow\", \"Some Other Workflow\"],\n tsSource: [\"\", \"some typescript source code\"],\n enabled: [true, false],\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 allowLocalhost: [true, false],\n }],\n apps: [{\n installed: [typedFromEntries(typedEntries(ALL_APPS).map(([key, value]) => [key, [{\n enabled: [true, false],\n }]]))],\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 }]]))] as const,\n }],\n }],\n domains: [{\n ...branchSchemaFuzzerConfig[0].domains[0],\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}] 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\n"],"mappings":"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","names":[]}
|