@hexclave/shared 1.0.36 → 1.0.37
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/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.js +2 -2
- package/dist/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.js.map +1 -1
- package/dist/config/format.js +10 -0
- package/dist/config/format.js.map +1 -1
- package/dist/config/schema.d.ts +50 -50
- package/dist/config-eval.d.ts +31 -0
- package/dist/config-eval.d.ts.map +1 -0
- package/dist/config-eval.js +117 -0
- package/dist/config-eval.js.map +1 -0
- package/dist/config-rendering.d.ts +3 -6
- package/dist/config-rendering.d.ts.map +1 -1
- package/dist/config-rendering.js +21 -69
- package/dist/config-rendering.js.map +1 -1
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.js +2 -2
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.js.map +1 -1
- package/dist/esm/config/format.js +10 -0
- package/dist/esm/config/format.js.map +1 -1
- package/dist/esm/config/schema.d.ts +50 -50
- package/dist/esm/config-eval.d.ts +31 -0
- package/dist/esm/config-eval.d.ts.map +1 -0
- package/dist/esm/config-eval.js +112 -0
- package/dist/esm/config-eval.js.map +1 -0
- package/dist/esm/config-rendering.d.ts +3 -6
- package/dist/esm/config-rendering.d.ts.map +1 -1
- package/dist/esm/config-rendering.js +15 -58
- package/dist/esm/config-rendering.js.map +1 -1
- package/dist/esm/interface/admin-interface.d.ts +44 -2
- package/dist/esm/interface/admin-interface.d.ts.map +1 -1
- package/dist/esm/interface/admin-interface.js +56 -1
- package/dist/esm/interface/admin-interface.js.map +1 -1
- package/dist/esm/interface/admin-metrics.d.ts +12 -12
- package/dist/esm/interface/client-interface.js +1 -1
- package/dist/esm/interface/conversations.d.ts +4 -4
- package/dist/esm/interface/crud/current-user.d.ts +12 -12
- package/dist/esm/interface/crud/email-outbox.d.ts +180 -180
- package/dist/esm/interface/crud/products.d.ts +15 -15
- package/dist/esm/interface/crud/products.d.ts.map +1 -1
- package/dist/esm/interface/crud/project-api-keys.d.ts +2 -2
- package/dist/esm/interface/crud/projects.d.ts +28 -28
- package/dist/esm/interface/crud/team-member-profiles.d.ts +26 -26
- package/dist/esm/interface/crud/transactions.d.ts +20 -20
- package/dist/esm/interface/crud/transactions.d.ts.map +1 -1
- package/dist/esm/interface/crud/users.d.ts +16 -16
- package/dist/esm/interface/plan-usage.d.ts +2 -2
- package/dist/esm/interface/server-interface.js +1 -1
- package/dist/esm/interface/webhooks.d.ts +2 -2
- package/dist/esm/known-errors.d.ts +1 -1
- package/dist/esm/schema-fields.d.ts +35 -2
- package/dist/esm/schema-fields.d.ts.map +1 -1
- package/dist/esm/schema-fields.js +53 -2
- package/dist/esm/schema-fields.js.map +1 -1
- package/dist/esm/sessions.d.ts +7 -7
- package/dist/esm/utils/jsx-editable-transpiler.js +2 -2
- package/dist/esm/utils/promises.js +1 -1
- package/dist/interface/admin-interface.d.ts +44 -2
- package/dist/interface/admin-interface.d.ts.map +1 -1
- package/dist/interface/admin-interface.js +56 -1
- package/dist/interface/admin-interface.js.map +1 -1
- package/dist/interface/admin-metrics.d.ts +12 -12
- package/dist/interface/client-interface.js +1 -1
- package/dist/interface/conversations.d.ts +4 -4
- package/dist/interface/crud/current-user.d.ts +12 -12
- package/dist/interface/crud/email-outbox.d.ts +180 -180
- package/dist/interface/crud/products.d.ts +15 -15
- package/dist/interface/crud/products.d.ts.map +1 -1
- package/dist/interface/crud/project-api-keys.d.ts +2 -2
- package/dist/interface/crud/projects.d.ts +28 -28
- package/dist/interface/crud/team-member-profiles.d.ts +26 -26
- package/dist/interface/crud/transactions.d.ts +20 -20
- package/dist/interface/crud/transactions.d.ts.map +1 -1
- package/dist/interface/crud/users.d.ts +16 -16
- package/dist/interface/plan-usage.d.ts +2 -2
- package/dist/interface/server-interface.js +1 -1
- package/dist/interface/webhooks.d.ts +2 -2
- package/dist/known-errors.d.ts +1 -1
- package/dist/schema-fields.d.ts +35 -2
- package/dist/schema-fields.d.ts.map +1 -1
- package/dist/schema-fields.js +55 -1
- package/dist/schema-fields.js.map +1 -1
- package/dist/sessions.d.ts +7 -7
- package/dist/utils/jsx-editable-transpiler.js +4 -4
- package/dist/utils/promises.js +1 -1
- package/package.json +2 -1
- package/src/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.ts +2 -2
- package/src/config/format.ts +18 -0
- package/src/config-eval.ts +140 -0
- package/src/config-rendering.ts +23 -79
- package/src/interface/admin-interface.ts +85 -5
- package/src/schema-fields.ts +59 -1
- package/dist/esm/hexclave-config-file.d.ts +0 -38
- package/dist/esm/hexclave-config-file.d.ts.map +0 -1
- package/dist/esm/hexclave-config-file.js +0 -144
- package/dist/esm/hexclave-config-file.js.map +0 -1
- package/dist/hexclave-config-file.d.ts +0 -38
- package/dist/hexclave-config-file.d.ts.map +0 -1
- package/dist/hexclave-config-file.js +0 -153
- package/dist/hexclave-config-file.js.map +0 -1
- package/src/hexclave-config-file.ts +0 -215
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","names":[],"sources":["../../../src/config/format.ts"],"sourcesContent":["// Format spec lives in the eng-handbook (random-thoughts/config-json-format.md).\n// (Previously linked to a public repo that doesn't exist — internal handbook only.)\n\nimport { HexclaveAssertionError, 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 readonly any[]\n ? { [K in keyof N]: _NormalizesTo<N[K]> }\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 HexclaveAssertionError(`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\n/**\n * Removes keys from a config override, using the same nested key logic as the `override` function.\n * Resetting key \"a.b\" also resets \"a.b.c\" (and any other descendants).\n * Handles both flat dot-notation keys and nested object keys.\n */\nexport function removeKeysFromConfig(config: Config, keysToRemove: string[]): Config {\n if (keysToRemove.length === 0) return config;\n\n const result: Config = {};\n for (const [key, value] of Object.entries(config)) {\n if (value === undefined) continue;\n\n // Check if this flat key matches or is a child of any key to remove (same logic as override)\n const shouldRemove = keysToRemove.some(k => key === k || key.startsWith(k + '.'));\n if (shouldRemove) continue;\n\n // Check if any key to remove is a descendant of this key (meaning it's nested inside this value)\n const childKeysToRemove = keysToRemove\n .filter(k => k.startsWith(key + '.'))\n .map(k => k.slice(key.length + 1));\n\n if (childKeysToRemove.length > 0 && typeof value === 'object' && value !== null && !Array.isArray(value)) {\n const cleaned = removeKeysFromConfig(value as Config, childKeysToRemove);\n if (Object.keys(cleaned).length > 0) {\n result[key] = cleaned;\n }\n } else {\n result[key] = value;\n }\n }\n return result;\n}\n\nimport.meta.vitest?.test(\"removeKeysFromConfig(...)\", ({ expect }) => {\n // Basic flat key removal\n expect(removeKeysFromConfig({ a: 1, b: 2 }, [\"a\"])).toEqual({ b: 2 });\n expect(removeKeysFromConfig({ \"a.b\": 1, \"a.c\": 2, d: 3 }, [\"a.b\"])).toEqual({ \"a.c\": 2, d: 3 });\n\n // Removing a parent removes children (flat keys)\n expect(removeKeysFromConfig({ \"a.b\": 1, \"a.b.c\": 2, \"a.d\": 3 }, [\"a.b\"])).toEqual({ \"a.d\": 3 });\n expect(removeKeysFromConfig({ \"a.b\": 1, \"a.c\": 2, \"a.d\": 3 }, [\"a\"])).toEqual({});\n expect(removeKeysFromConfig({ \"teams.allowClientTeamCreation\": true, \"teams.createPersonalTeamOnSignUp\": true }, [\"teams\"])).toEqual({});\n\n // Nested object key removal\n expect(removeKeysFromConfig({ teams: { allowClientTeamCreation: true, createPersonalTeamOnSignUp: true } }, [\"teams.allowClientTeamCreation\"])).toEqual({ teams: { createPersonalTeamOnSignUp: true } });\n expect(removeKeysFromConfig({ teams: { allowClientTeamCreation: true } }, [\"teams.allowClientTeamCreation\"])).toEqual({});\n expect(removeKeysFromConfig({ teams: { allowClientTeamCreation: true, createPersonalTeamOnSignUp: true } }, [\"teams\"])).toEqual({});\n expect(removeKeysFromConfig({ a: { b: { c: 1, d: 2 } } }, [\"a.b.c\"])).toEqual({ a: { b: { d: 2 } } });\n\n // Mixed flat and nested\n expect(removeKeysFromConfig({ \"teams.allowClientTeamCreation\": true, teams: { createPersonalTeamOnSignUp: true } }, [\"teams.allowClientTeamCreation\"])).toEqual({ teams: { createPersonalTeamOnSignUp: true } });\n expect(removeKeysFromConfig({ \"teams.allowClientTeamCreation\": true, teams: { createPersonalTeamOnSignUp: true } }, [\"teams\"])).toEqual({});\n\n // Nested with dot-notation inner keys\n expect(removeKeysFromConfig({ teams: { \"a.b\": 1 } }, [\"teams.a.b\"])).toEqual({});\n expect(removeKeysFromConfig({ teams: { \"a.b.c\": 1 } }, [\"teams.a.b\"])).toEqual({});\n\n // No keys to remove\n expect(removeKeysFromConfig({ a: 1, b: 2 }, [])).toEqual({ a: 1, b: 2 });\n\n // Key not present (no-op)\n expect(removeKeysFromConfig({ a: 1, b: 2 }, [\"c\"])).toEqual({ a: 1, b: 2 });\n expect(removeKeysFromConfig({ a: 1, b: 2 }, [\"a.b\"])).toEqual({ a: 1, b: 2 });\n\n // Multiple keys to remove\n expect(removeKeysFromConfig({ \"a.b\": 1, \"c.d\": 2, \"e.f\": 3 }, [\"a.b\", \"e.f\"])).toEqual({ \"c.d\": 2 });\n expect(removeKeysFromConfig({ a: { b: 1 }, c: { d: 2 } }, [\"a.b\", \"c.d\"])).toEqual({});\n\n // Removing non-object values with nested key path (no-op for non-objects)\n expect(removeKeysFromConfig({ a: \"string\" }, [\"a.b\"])).toEqual({ a: \"string\" });\n expect(removeKeysFromConfig({ a: 123 }, [\"a.b\"])).toEqual({ a: 123 });\n expect(removeKeysFromConfig({ a: null }, [\"a.b\"])).toEqual({ a: null });\n\n // Array values are preserved (not recursed into)\n expect(removeKeysFromConfig({ a: [1, 2, 3] }, [\"a.0\"])).toEqual({ a: [1, 2, 3] });\n});\n\nimport.meta.vitest?.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 * If provided, keys that are silently dropped during normalization (when a dot-notation key\n * references a parent that doesn't exist or is not an object and the behavior is \"ignore\")\n * will be pushed to this array.\n */\n droppedKeys?: string[],\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 HexclaveAssertionError(`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 options.droppedKeys?.push(key);\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 options.droppedKeys?.push(key);\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\nimport.meta.vitest?.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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"}
|
|
1
|
+
{"version":3,"file":"format.js","names":[],"sources":["../../../src/config/format.ts"],"sourcesContent":["// Format spec lives in the eng-handbook (random-thoughts/config-json-format.md).\n// (Previously linked to a public repo that doesn't exist — internal handbook only.)\n\nimport { HexclaveAssertionError, 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 readonly any[]\n ? { [K in keyof N]: _NormalizesTo<N[K]> }\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 HexclaveAssertionError(`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\n/**\n * Removes keys from a config override, using the same nested key logic as the `override` function.\n * Resetting key \"a.b\" also resets \"a.b.c\" (and any other descendants).\n * Handles both flat dot-notation keys and nested object keys.\n */\nexport function removeKeysFromConfig(config: Config, keysToRemove: string[]): Config {\n if (keysToRemove.length === 0) return config;\n\n const result: Config = {};\n for (const [key, value] of Object.entries(config)) {\n if (value === undefined) continue;\n\n // Check if this flat key matches or is a child of any key to remove (same logic as override)\n const shouldRemove = keysToRemove.some(k => key === k || key.startsWith(k + '.'));\n if (shouldRemove) continue;\n\n // Check if any key to remove is a descendant of this key (meaning it's nested inside this value)\n const childKeysToRemove = keysToRemove\n .filter(k => k.startsWith(key + '.'))\n .map(k => k.slice(key.length + 1));\n\n if (childKeysToRemove.length > 0 && typeof value === 'object' && value !== null && !Array.isArray(value)) {\n const cleaned = removeKeysFromConfig(value as Config, childKeysToRemove);\n if (Object.keys(cleaned).length > 0) {\n result[key] = cleaned;\n }\n } else {\n result[key] = value;\n }\n }\n return result;\n}\n\nimport.meta.vitest?.test(\"removeKeysFromConfig(...)\", ({ expect }) => {\n // Basic flat key removal\n expect(removeKeysFromConfig({ a: 1, b: 2 }, [\"a\"])).toEqual({ b: 2 });\n expect(removeKeysFromConfig({ \"a.b\": 1, \"a.c\": 2, d: 3 }, [\"a.b\"])).toEqual({ \"a.c\": 2, d: 3 });\n\n // Removing a parent removes children (flat keys)\n expect(removeKeysFromConfig({ \"a.b\": 1, \"a.b.c\": 2, \"a.d\": 3 }, [\"a.b\"])).toEqual({ \"a.d\": 3 });\n expect(removeKeysFromConfig({ \"a.b\": 1, \"a.c\": 2, \"a.d\": 3 }, [\"a\"])).toEqual({});\n expect(removeKeysFromConfig({ \"teams.allowClientTeamCreation\": true, \"teams.createPersonalTeamOnSignUp\": true }, [\"teams\"])).toEqual({});\n\n // Nested object key removal\n expect(removeKeysFromConfig({ teams: { allowClientTeamCreation: true, createPersonalTeamOnSignUp: true } }, [\"teams.allowClientTeamCreation\"])).toEqual({ teams: { createPersonalTeamOnSignUp: true } });\n expect(removeKeysFromConfig({ teams: { allowClientTeamCreation: true } }, [\"teams.allowClientTeamCreation\"])).toEqual({});\n expect(removeKeysFromConfig({ teams: { allowClientTeamCreation: true, createPersonalTeamOnSignUp: true } }, [\"teams\"])).toEqual({});\n expect(removeKeysFromConfig({ a: { b: { c: 1, d: 2 } } }, [\"a.b.c\"])).toEqual({ a: { b: { d: 2 } } });\n\n // Mixed flat and nested\n expect(removeKeysFromConfig({ \"teams.allowClientTeamCreation\": true, teams: { createPersonalTeamOnSignUp: true } }, [\"teams.allowClientTeamCreation\"])).toEqual({ teams: { createPersonalTeamOnSignUp: true } });\n expect(removeKeysFromConfig({ \"teams.allowClientTeamCreation\": true, teams: { createPersonalTeamOnSignUp: true } }, [\"teams\"])).toEqual({});\n\n // Nested with dot-notation inner keys\n expect(removeKeysFromConfig({ teams: { \"a.b\": 1 } }, [\"teams.a.b\"])).toEqual({});\n expect(removeKeysFromConfig({ teams: { \"a.b.c\": 1 } }, [\"teams.a.b\"])).toEqual({});\n\n // No keys to remove\n expect(removeKeysFromConfig({ a: 1, b: 2 }, [])).toEqual({ a: 1, b: 2 });\n\n // Key not present (no-op)\n expect(removeKeysFromConfig({ a: 1, b: 2 }, [\"c\"])).toEqual({ a: 1, b: 2 });\n expect(removeKeysFromConfig({ a: 1, b: 2 }, [\"a.b\"])).toEqual({ a: 1, b: 2 });\n\n // Multiple keys to remove\n expect(removeKeysFromConfig({ \"a.b\": 1, \"c.d\": 2, \"e.f\": 3 }, [\"a.b\", \"e.f\"])).toEqual({ \"c.d\": 2 });\n expect(removeKeysFromConfig({ a: { b: 1 }, c: { d: 2 } }, [\"a.b\", \"c.d\"])).toEqual({});\n\n // Removing non-object values with nested key path (no-op for non-objects)\n expect(removeKeysFromConfig({ a: \"string\" }, [\"a.b\"])).toEqual({ a: \"string\" });\n expect(removeKeysFromConfig({ a: 123 }, [\"a.b\"])).toEqual({ a: 123 });\n expect(removeKeysFromConfig({ a: null }, [\"a.b\"])).toEqual({ a: null });\n\n // Array values are preserved (not recursed into)\n expect(removeKeysFromConfig({ a: [1, 2, 3] }, [\"a.0\"])).toEqual({ a: [1, 2, 3] });\n});\n\nimport.meta.vitest?.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 * If provided, keys that are silently dropped during normalization (when a dot-notation key\n * references a parent that doesn't exist or is not an object and the behavior is \"ignore\")\n * will be pushed to this array.\n */\n droppedKeys?: string[],\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 HexclaveAssertionError(`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 options.droppedKeys?.push(key);\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 options.droppedKeys?.push(key);\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\nimport.meta.vitest?.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 expect(normalize({\n \"auth.allowSignUp\": false,\n \"apps.installed.payments.enabled\": false,\n }, {\n onDotIntoNonObject: \"ignore\",\n onDotIntoNull: \"empty-object\",\n })).toEqual({\n auth: {\n allowSignUp: false,\n },\n apps: {\n installed: {\n payments: {\n enabled: false,\n },\n },\n },\n });\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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"}
|
|
@@ -84,7 +84,7 @@ declare const branchConfigSchema: yup$1.Schema<Omit<Omit<{
|
|
|
84
84
|
sourceOfTruth: {
|
|
85
85
|
type: "hosted";
|
|
86
86
|
};
|
|
87
|
-
}, string>, "
|
|
87
|
+
}, string>, "users" | "teams" | "auth" | "onboarding" | "rbac" | "payments" | "emails" | "apiKeys" | "apps" | "domains" | "dbSync" | "dataVault"> & {
|
|
88
88
|
payments?: {
|
|
89
89
|
blockNewPurchases?: boolean | undefined;
|
|
90
90
|
autoPay?: {
|
|
@@ -125,6 +125,13 @@ declare const branchConfigSchema: yup$1.Schema<Omit<Omit<{
|
|
|
125
125
|
}>;
|
|
126
126
|
}>;
|
|
127
127
|
} | undefined;
|
|
128
|
+
users: {
|
|
129
|
+
allowClientUserDeletion?: boolean | undefined;
|
|
130
|
+
};
|
|
131
|
+
teams: {
|
|
132
|
+
createPersonalTeamOnSignUp?: boolean | undefined;
|
|
133
|
+
allowClientTeamCreation?: boolean | undefined;
|
|
134
|
+
};
|
|
128
135
|
auth: {
|
|
129
136
|
allowSignUp?: boolean | undefined;
|
|
130
137
|
signUpRulesDefaultAction?: string | undefined;
|
|
@@ -159,10 +166,6 @@ declare const branchConfigSchema: yup$1.Schema<Omit<Omit<{
|
|
|
159
166
|
onboarding: {
|
|
160
167
|
requireEmailVerification?: boolean | undefined;
|
|
161
168
|
};
|
|
162
|
-
teams: {
|
|
163
|
-
createPersonalTeamOnSignUp?: boolean | undefined;
|
|
164
|
-
allowClientTeamCreation?: boolean | undefined;
|
|
165
|
-
};
|
|
166
169
|
rbac: {
|
|
167
170
|
permissions: Record<string, {
|
|
168
171
|
description?: string | undefined;
|
|
@@ -187,9 +190,6 @@ declare const branchConfigSchema: yup$1.Schema<Omit<Omit<{
|
|
|
187
190
|
tsxSource: string;
|
|
188
191
|
}>;
|
|
189
192
|
};
|
|
190
|
-
users: {
|
|
191
|
-
allowClientUserDeletion?: boolean | undefined;
|
|
192
|
-
};
|
|
193
193
|
apiKeys: {
|
|
194
194
|
enabled: {
|
|
195
195
|
team?: boolean | undefined;
|
|
@@ -197,7 +197,7 @@ declare const branchConfigSchema: yup$1.Schema<Omit<Omit<{
|
|
|
197
197
|
};
|
|
198
198
|
};
|
|
199
199
|
apps: {
|
|
200
|
-
installed: Record<"authentication" | "fraud-protection" | "analytics" | "onboarding" | "
|
|
200
|
+
installed: Record<"teams" | "authentication" | "fraud-protection" | "analytics" | "onboarding" | "rbac" | "api-keys" | "payments" | "emails" | "support" | "email-api" | "data-vault" | "webhooks" | "tv-mode" | "launch-checklist" | "catalyst" | "neon" | "convex" | "vercel" | "tanstack-start" | "clickmaps" | "session-replays", {
|
|
201
201
|
enabled?: boolean | undefined;
|
|
202
202
|
}>;
|
|
203
203
|
};
|
|
@@ -218,7 +218,7 @@ declare const branchConfigSchema: yup$1.Schema<Omit<Omit<{
|
|
|
218
218
|
project: {
|
|
219
219
|
requirePublishableClientKey: undefined;
|
|
220
220
|
};
|
|
221
|
-
}, string>, "
|
|
221
|
+
}, string>, "users" | "teams" | "auth" | "onboarding" | "rbac" | "payments" | "emails" | "apiKeys" | "apps" | "domains" | "dbSync" | "dataVault"> & {
|
|
222
222
|
rbac: {
|
|
223
223
|
permissions: undefined;
|
|
224
224
|
defaultPermissions: {
|
|
@@ -293,7 +293,7 @@ declare const environmentConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<Omit<{
|
|
|
293
293
|
sourceOfTruth: {
|
|
294
294
|
type: "hosted";
|
|
295
295
|
};
|
|
296
|
-
}, string>, "
|
|
296
|
+
}, string>, "users" | "teams" | "auth" | "onboarding" | "rbac" | "payments" | "emails" | "apiKeys" | "apps" | "domains" | "dbSync" | "dataVault"> & {
|
|
297
297
|
payments?: {
|
|
298
298
|
blockNewPurchases?: boolean | undefined;
|
|
299
299
|
autoPay?: {
|
|
@@ -334,6 +334,13 @@ declare const environmentConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<Omit<{
|
|
|
334
334
|
}>;
|
|
335
335
|
}>;
|
|
336
336
|
} | undefined;
|
|
337
|
+
users: {
|
|
338
|
+
allowClientUserDeletion?: boolean | undefined;
|
|
339
|
+
};
|
|
340
|
+
teams: {
|
|
341
|
+
createPersonalTeamOnSignUp?: boolean | undefined;
|
|
342
|
+
allowClientTeamCreation?: boolean | undefined;
|
|
343
|
+
};
|
|
337
344
|
auth: {
|
|
338
345
|
allowSignUp?: boolean | undefined;
|
|
339
346
|
signUpRulesDefaultAction?: string | undefined;
|
|
@@ -368,10 +375,6 @@ declare const environmentConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<Omit<{
|
|
|
368
375
|
onboarding: {
|
|
369
376
|
requireEmailVerification?: boolean | undefined;
|
|
370
377
|
};
|
|
371
|
-
teams: {
|
|
372
|
-
createPersonalTeamOnSignUp?: boolean | undefined;
|
|
373
|
-
allowClientTeamCreation?: boolean | undefined;
|
|
374
|
-
};
|
|
375
378
|
rbac: {
|
|
376
379
|
permissions: Record<string, {
|
|
377
380
|
description?: string | undefined;
|
|
@@ -396,9 +399,6 @@ declare const environmentConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<Omit<{
|
|
|
396
399
|
tsxSource: string;
|
|
397
400
|
}>;
|
|
398
401
|
};
|
|
399
|
-
users: {
|
|
400
|
-
allowClientUserDeletion?: boolean | undefined;
|
|
401
|
-
};
|
|
402
402
|
apiKeys: {
|
|
403
403
|
enabled: {
|
|
404
404
|
team?: boolean | undefined;
|
|
@@ -406,7 +406,7 @@ declare const environmentConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<Omit<{
|
|
|
406
406
|
};
|
|
407
407
|
};
|
|
408
408
|
apps: {
|
|
409
|
-
installed: Record<"authentication" | "fraud-protection" | "analytics" | "onboarding" | "
|
|
409
|
+
installed: Record<"teams" | "authentication" | "fraud-protection" | "analytics" | "onboarding" | "rbac" | "api-keys" | "payments" | "emails" | "support" | "email-api" | "data-vault" | "webhooks" | "tv-mode" | "launch-checklist" | "catalyst" | "neon" | "convex" | "vercel" | "tanstack-start" | "clickmaps" | "session-replays", {
|
|
410
410
|
enabled?: boolean | undefined;
|
|
411
411
|
}>;
|
|
412
412
|
};
|
|
@@ -575,7 +575,7 @@ declare const environmentConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<Omit<{
|
|
|
575
575
|
project: {
|
|
576
576
|
requirePublishableClientKey: undefined;
|
|
577
577
|
};
|
|
578
|
-
}, string>, "
|
|
578
|
+
}, string>, "users" | "teams" | "auth" | "onboarding" | "rbac" | "payments" | "emails" | "apiKeys" | "apps" | "domains" | "dbSync" | "dataVault"> & {
|
|
579
579
|
rbac: {
|
|
580
580
|
permissions: undefined;
|
|
581
581
|
defaultPermissions: {
|
|
@@ -648,7 +648,7 @@ declare const environmentConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<Omit<{
|
|
|
648
648
|
project: {
|
|
649
649
|
requirePublishableClientKey: undefined;
|
|
650
650
|
};
|
|
651
|
-
}, string>, "
|
|
651
|
+
}, string>, "users" | "teams" | "auth" | "onboarding" | "rbac" | "payments" | "emails" | "apiKeys" | "apps" | "domains" | "dbSync" | "dataVault"> & {
|
|
652
652
|
rbac: {
|
|
653
653
|
permissions: undefined;
|
|
654
654
|
defaultPermissions: {
|
|
@@ -721,7 +721,7 @@ declare const environmentConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<Omit<{
|
|
|
721
721
|
project: {
|
|
722
722
|
requirePublishableClientKey: undefined;
|
|
723
723
|
};
|
|
724
|
-
}, string>, "
|
|
724
|
+
}, string>, "users" | "teams" | "auth" | "onboarding" | "rbac" | "payments" | "emails" | "apiKeys" | "apps" | "domains" | "dbSync" | "dataVault"> & {
|
|
725
725
|
rbac: {
|
|
726
726
|
permissions: undefined;
|
|
727
727
|
defaultPermissions: {
|
|
@@ -797,7 +797,7 @@ declare const environmentConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<Omit<{
|
|
|
797
797
|
project: {
|
|
798
798
|
requirePublishableClientKey: undefined;
|
|
799
799
|
};
|
|
800
|
-
}, string>, "
|
|
800
|
+
}, string>, "users" | "teams" | "auth" | "onboarding" | "rbac" | "payments" | "emails" | "apiKeys" | "apps" | "domains" | "dbSync" | "dataVault"> & {
|
|
801
801
|
rbac: {
|
|
802
802
|
permissions: undefined;
|
|
803
803
|
defaultPermissions: {
|
|
@@ -883,7 +883,7 @@ declare const environmentConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<Omit<{
|
|
|
883
883
|
project: {
|
|
884
884
|
requirePublishableClientKey: undefined;
|
|
885
885
|
};
|
|
886
|
-
}, string>, "
|
|
886
|
+
}, string>, "users" | "teams" | "auth" | "onboarding" | "rbac" | "payments" | "emails" | "apiKeys" | "apps" | "domains" | "dbSync" | "dataVault"> & {
|
|
887
887
|
rbac: {
|
|
888
888
|
permissions: undefined;
|
|
889
889
|
defaultPermissions: {
|
|
@@ -959,7 +959,7 @@ declare const environmentConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<Omit<{
|
|
|
959
959
|
project: {
|
|
960
960
|
requirePublishableClientKey: undefined;
|
|
961
961
|
};
|
|
962
|
-
}, string>, "
|
|
962
|
+
}, string>, "users" | "teams" | "auth" | "onboarding" | "rbac" | "payments" | "emails" | "apiKeys" | "apps" | "domains" | "dbSync" | "dataVault"> & {
|
|
963
963
|
rbac: {
|
|
964
964
|
permissions: undefined;
|
|
965
965
|
defaultPermissions: {
|
|
@@ -1041,7 +1041,7 @@ declare const organizationConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<NonNu
|
|
|
1041
1041
|
sourceOfTruth: {
|
|
1042
1042
|
type: "hosted";
|
|
1043
1043
|
};
|
|
1044
|
-
}, string>, "
|
|
1044
|
+
}, string>, "users" | "teams" | "auth" | "onboarding" | "rbac" | "payments" | "emails" | "apiKeys" | "apps" | "domains" | "dbSync" | "dataVault"> & {
|
|
1045
1045
|
payments?: {
|
|
1046
1046
|
blockNewPurchases?: boolean | undefined;
|
|
1047
1047
|
autoPay?: {
|
|
@@ -1082,6 +1082,13 @@ declare const organizationConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<NonNu
|
|
|
1082
1082
|
}>;
|
|
1083
1083
|
}>;
|
|
1084
1084
|
} | undefined;
|
|
1085
|
+
users: {
|
|
1086
|
+
allowClientUserDeletion?: boolean | undefined;
|
|
1087
|
+
};
|
|
1088
|
+
teams: {
|
|
1089
|
+
createPersonalTeamOnSignUp?: boolean | undefined;
|
|
1090
|
+
allowClientTeamCreation?: boolean | undefined;
|
|
1091
|
+
};
|
|
1085
1092
|
auth: {
|
|
1086
1093
|
allowSignUp?: boolean | undefined;
|
|
1087
1094
|
signUpRulesDefaultAction?: string | undefined;
|
|
@@ -1116,10 +1123,6 @@ declare const organizationConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<NonNu
|
|
|
1116
1123
|
onboarding: {
|
|
1117
1124
|
requireEmailVerification?: boolean | undefined;
|
|
1118
1125
|
};
|
|
1119
|
-
teams: {
|
|
1120
|
-
createPersonalTeamOnSignUp?: boolean | undefined;
|
|
1121
|
-
allowClientTeamCreation?: boolean | undefined;
|
|
1122
|
-
};
|
|
1123
1126
|
rbac: {
|
|
1124
1127
|
permissions: Record<string, {
|
|
1125
1128
|
description?: string | undefined;
|
|
@@ -1144,9 +1147,6 @@ declare const organizationConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<NonNu
|
|
|
1144
1147
|
tsxSource: string;
|
|
1145
1148
|
}>;
|
|
1146
1149
|
};
|
|
1147
|
-
users: {
|
|
1148
|
-
allowClientUserDeletion?: boolean | undefined;
|
|
1149
|
-
};
|
|
1150
1150
|
apiKeys: {
|
|
1151
1151
|
enabled: {
|
|
1152
1152
|
team?: boolean | undefined;
|
|
@@ -1154,7 +1154,7 @@ declare const organizationConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<NonNu
|
|
|
1154
1154
|
};
|
|
1155
1155
|
};
|
|
1156
1156
|
apps: {
|
|
1157
|
-
installed: Record<"authentication" | "fraud-protection" | "analytics" | "onboarding" | "
|
|
1157
|
+
installed: Record<"teams" | "authentication" | "fraud-protection" | "analytics" | "onboarding" | "rbac" | "api-keys" | "payments" | "emails" | "support" | "email-api" | "data-vault" | "webhooks" | "tv-mode" | "launch-checklist" | "catalyst" | "neon" | "convex" | "vercel" | "tanstack-start" | "clickmaps" | "session-replays", {
|
|
1158
1158
|
enabled?: boolean | undefined;
|
|
1159
1159
|
}>;
|
|
1160
1160
|
};
|
|
@@ -1323,7 +1323,7 @@ declare const organizationConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<NonNu
|
|
|
1323
1323
|
project: {
|
|
1324
1324
|
requirePublishableClientKey: undefined;
|
|
1325
1325
|
};
|
|
1326
|
-
}, string>, "
|
|
1326
|
+
}, string>, "users" | "teams" | "auth" | "onboarding" | "rbac" | "payments" | "emails" | "apiKeys" | "apps" | "domains" | "dbSync" | "dataVault"> & {
|
|
1327
1327
|
rbac: {
|
|
1328
1328
|
permissions: undefined;
|
|
1329
1329
|
defaultPermissions: {
|
|
@@ -1396,7 +1396,7 @@ declare const organizationConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<NonNu
|
|
|
1396
1396
|
project: {
|
|
1397
1397
|
requirePublishableClientKey: undefined;
|
|
1398
1398
|
};
|
|
1399
|
-
}, string>, "
|
|
1399
|
+
}, string>, "users" | "teams" | "auth" | "onboarding" | "rbac" | "payments" | "emails" | "apiKeys" | "apps" | "domains" | "dbSync" | "dataVault"> & {
|
|
1400
1400
|
rbac: {
|
|
1401
1401
|
permissions: undefined;
|
|
1402
1402
|
defaultPermissions: {
|
|
@@ -1469,7 +1469,7 @@ declare const organizationConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<NonNu
|
|
|
1469
1469
|
project: {
|
|
1470
1470
|
requirePublishableClientKey: undefined;
|
|
1471
1471
|
};
|
|
1472
|
-
}, string>, "
|
|
1472
|
+
}, string>, "users" | "teams" | "auth" | "onboarding" | "rbac" | "payments" | "emails" | "apiKeys" | "apps" | "domains" | "dbSync" | "dataVault"> & {
|
|
1473
1473
|
rbac: {
|
|
1474
1474
|
permissions: undefined;
|
|
1475
1475
|
defaultPermissions: {
|
|
@@ -1545,7 +1545,7 @@ declare const organizationConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<NonNu
|
|
|
1545
1545
|
project: {
|
|
1546
1546
|
requirePublishableClientKey: undefined;
|
|
1547
1547
|
};
|
|
1548
|
-
}, string>, "
|
|
1548
|
+
}, string>, "users" | "teams" | "auth" | "onboarding" | "rbac" | "payments" | "emails" | "apiKeys" | "apps" | "domains" | "dbSync" | "dataVault"> & {
|
|
1549
1549
|
rbac: {
|
|
1550
1550
|
permissions: undefined;
|
|
1551
1551
|
defaultPermissions: {
|
|
@@ -1631,7 +1631,7 @@ declare const organizationConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<NonNu
|
|
|
1631
1631
|
project: {
|
|
1632
1632
|
requirePublishableClientKey: undefined;
|
|
1633
1633
|
};
|
|
1634
|
-
}, string>, "
|
|
1634
|
+
}, string>, "users" | "teams" | "auth" | "onboarding" | "rbac" | "payments" | "emails" | "apiKeys" | "apps" | "domains" | "dbSync" | "dataVault"> & {
|
|
1635
1635
|
rbac: {
|
|
1636
1636
|
permissions: undefined;
|
|
1637
1637
|
defaultPermissions: {
|
|
@@ -1707,7 +1707,7 @@ declare const organizationConfigSchema: yup$1.Schema<Omit<NonNullable<Omit<NonNu
|
|
|
1707
1707
|
project: {
|
|
1708
1708
|
requirePublishableClientKey: undefined;
|
|
1709
1709
|
};
|
|
1710
|
-
}, string>, "
|
|
1710
|
+
}, string>, "users" | "teams" | "auth" | "onboarding" | "rbac" | "payments" | "emails" | "apiKeys" | "apps" | "domains" | "dbSync" | "dataVault"> & {
|
|
1711
1711
|
rbac: {
|
|
1712
1712
|
permissions: undefined;
|
|
1713
1713
|
defaultPermissions: {
|
|
@@ -2077,14 +2077,14 @@ declare function sanitizeOrganizationConfig(config: OrganizationRenderedConfigBe
|
|
|
2077
2077
|
priority: number;
|
|
2078
2078
|
condition: string | undefined;
|
|
2079
2079
|
action: {
|
|
2080
|
-
type: "allow" | "reject" | "restrict" | "log";
|
|
2081
2080
|
message: string | undefined;
|
|
2081
|
+
type: "allow" | "reject" | "restrict" | "log";
|
|
2082
2082
|
};
|
|
2083
2083
|
}>;
|
|
2084
|
-
allowSignUp: boolean;
|
|
2085
2084
|
password: {
|
|
2086
2085
|
allowSignIn: boolean;
|
|
2087
2086
|
};
|
|
2087
|
+
allowSignUp: boolean;
|
|
2088
2088
|
otp: {
|
|
2089
2089
|
allowSignIn: boolean;
|
|
2090
2090
|
};
|
|
@@ -2146,8 +2146,8 @@ declare function sanitizeOrganizationConfig(config: OrganizationRenderedConfigBe
|
|
|
2146
2146
|
stackable: boolean | undefined;
|
|
2147
2147
|
includedItems: {
|
|
2148
2148
|
[x: string]: {
|
|
2149
|
-
repeat: "never" | [number, "day" | "week" | "month" | "year"];
|
|
2150
2149
|
quantity: number;
|
|
2150
|
+
repeat: "never" | [number, "day" | "week" | "month" | "year"];
|
|
2151
2151
|
expires: "never" | "when-purchase-expires" | "when-repeated";
|
|
2152
2152
|
};
|
|
2153
2153
|
};
|
|
@@ -2183,6 +2183,13 @@ declare function sanitizeOrganizationConfig(config: OrganizationRenderedConfigBe
|
|
|
2183
2183
|
} & {
|
|
2184
2184
|
readonly type: "hosted";
|
|
2185
2185
|
};
|
|
2186
|
+
users: {
|
|
2187
|
+
allowClientUserDeletion: boolean;
|
|
2188
|
+
};
|
|
2189
|
+
teams: {
|
|
2190
|
+
createPersonalTeamOnSignUp: boolean;
|
|
2191
|
+
allowClientTeamCreation: boolean;
|
|
2192
|
+
};
|
|
2186
2193
|
analytics: {
|
|
2187
2194
|
queryFolders: {
|
|
2188
2195
|
[x: string]: {
|
|
@@ -2201,10 +2208,6 @@ declare function sanitizeOrganizationConfig(config: OrganizationRenderedConfigBe
|
|
|
2201
2208
|
onboarding: {
|
|
2202
2209
|
requireEmailVerification: boolean;
|
|
2203
2210
|
};
|
|
2204
|
-
teams: {
|
|
2205
|
-
createPersonalTeamOnSignUp: boolean;
|
|
2206
|
-
allowClientTeamCreation: boolean;
|
|
2207
|
-
};
|
|
2208
2211
|
rbac: {
|
|
2209
2212
|
permissions: {
|
|
2210
2213
|
[x: string]: {
|
|
@@ -2227,9 +2230,6 @@ declare function sanitizeOrganizationConfig(config: OrganizationRenderedConfigBe
|
|
|
2227
2230
|
};
|
|
2228
2231
|
};
|
|
2229
2232
|
};
|
|
2230
|
-
users: {
|
|
2231
|
-
allowClientUserDeletion: boolean;
|
|
2232
|
-
};
|
|
2233
2233
|
apiKeys: {
|
|
2234
2234
|
enabled: {
|
|
2235
2235
|
team: boolean;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { showOnboardingHexclaveConfigValue } from "./config-authoring";
|
|
2
|
+
|
|
3
|
+
//#region src/config-eval.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Thrown when a config file evaluates successfully but its exported `config`
|
|
6
|
+
* isn't a usable shape (missing, or not an object / "show-onboarding" string).
|
|
7
|
+
* Distinct from the underlying loader errors jiti throws, so callers can tell a
|
|
8
|
+
* malformed config apart from a file that simply failed to load.
|
|
9
|
+
*/
|
|
10
|
+
declare class ConfigFileEvalError extends Error {
|
|
11
|
+
constructor(message: string);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Walks up from `dir` to find the nearest `package.json` and returns the
|
|
15
|
+
* best SDK package to use for the `HexclaveConfig` type import.
|
|
16
|
+
*/
|
|
17
|
+
declare function detectImportPackageFromDir(dir: string): string | undefined;
|
|
18
|
+
/** A config object, or the `"show-onboarding"` sentinel that stands in for one. */
|
|
19
|
+
type ParsedConfigValue = Record<string, unknown> | typeof showOnboardingHexclaveConfigValue;
|
|
20
|
+
/**
|
|
21
|
+
* Evaluates config file content using jiti and returns the exported `config`
|
|
22
|
+
* value.
|
|
23
|
+
*
|
|
24
|
+
* WARNING: This executes arbitrary code via `jiti.evalModule` — only use on
|
|
25
|
+
* content that is fully operator-controlled (local filesystem). Never call
|
|
26
|
+
* this on untrusted input (e.g. content fetched from a remote repository).
|
|
27
|
+
*/
|
|
28
|
+
declare function evalConfigFileContent(content: string, filePath: string): ParsedConfigValue;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { ConfigFileEvalError, ParsedConfigValue, detectImportPackageFromDir, evalConfigFileContent };
|
|
31
|
+
//# sourceMappingURL=config-eval.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-eval.d.ts","names":[],"sources":["../../src/config-eval.ts"],"mappings":";;;;;AAcA;;;;cAAa,mBAAA,SAA4B,KAAA;cAC3B,OAAA;AAAA;;;AAUd;;iBAAgB,0BAAA,CAA2B,GAAA;;KA4B/B,iBAAA,GAAoB,MAAA,2BAAiC,iCAAA;AAAjE;;;;;AAcA;;;AAdA,iBAcgB,qBAAA,CAAsB,OAAA,UAAiB,QAAA,WAAmB,iBAAA"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { showOnboardingHexclaveConfigValue } from "./config-authoring.js";
|
|
2
|
+
import { existsSync, readFileSync } from "fs";
|
|
3
|
+
import { createJiti } from "jiti";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { detectConfigImportPackage } from "./config-rendering.js";
|
|
6
|
+
|
|
7
|
+
//#region src/config-eval.ts
|
|
8
|
+
const jiti = createJiti(import.meta.url, { moduleCache: false });
|
|
9
|
+
/**
|
|
10
|
+
* Thrown when a config file evaluates successfully but its exported `config`
|
|
11
|
+
* isn't a usable shape (missing, or not an object / "show-onboarding" string).
|
|
12
|
+
* Distinct from the underlying loader errors jiti throws, so callers can tell a
|
|
13
|
+
* malformed config apart from a file that simply failed to load.
|
|
14
|
+
*/
|
|
15
|
+
var ConfigFileEvalError = class extends Error {
|
|
16
|
+
constructor(message) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = "ConfigFileEvalError";
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Walks up from `dir` to find the nearest `package.json` and returns the
|
|
23
|
+
* best SDK package to use for the `HexclaveConfig` type import.
|
|
24
|
+
*/
|
|
25
|
+
function detectImportPackageFromDir(dir) {
|
|
26
|
+
let current = dir;
|
|
27
|
+
while (true) {
|
|
28
|
+
const pkgPath = path.join(current, "package.json");
|
|
29
|
+
if (existsSync(pkgPath)) try {
|
|
30
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
31
|
+
return detectConfigImportPackage([...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.devDependencies ?? {})]);
|
|
32
|
+
} catch {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const parent = path.dirname(current);
|
|
36
|
+
if (parent === current) break;
|
|
37
|
+
current = parent;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function isRecord(value) {
|
|
41
|
+
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
42
|
+
}
|
|
43
|
+
function invalidConfigShape(filePath) {
|
|
44
|
+
return new ConfigFileEvalError(`Invalid config in ${filePath}. The file must export a plain \`config\` object or "${showOnboardingHexclaveConfigValue}".`);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Evaluates config file content using jiti and returns the exported `config`
|
|
48
|
+
* value.
|
|
49
|
+
*
|
|
50
|
+
* WARNING: This executes arbitrary code via `jiti.evalModule` — only use on
|
|
51
|
+
* content that is fully operator-controlled (local filesystem). Never call
|
|
52
|
+
* this on untrusted input (e.g. content fetched from a remote repository).
|
|
53
|
+
*/
|
|
54
|
+
function evalConfigFileContent(content, filePath) {
|
|
55
|
+
if (content.trim() === "") return {};
|
|
56
|
+
const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(filePath);
|
|
57
|
+
const mod = jiti.evalModule(content, { filename: resolvedPath });
|
|
58
|
+
if (!isRecord(mod)) throw invalidConfigShape(filePath);
|
|
59
|
+
const config = mod.config;
|
|
60
|
+
if (config === void 0) throw invalidConfigShape(filePath);
|
|
61
|
+
if (typeof config === "string") {
|
|
62
|
+
if (config !== showOnboardingHexclaveConfigValue) throw new ConfigFileEvalError(`Invalid config in ${filePath}. String config values must be "${showOnboardingHexclaveConfigValue}", got "${config}".`);
|
|
63
|
+
return config;
|
|
64
|
+
}
|
|
65
|
+
if (isRecord(config)) return config;
|
|
66
|
+
throw invalidConfigShape(filePath);
|
|
67
|
+
}
|
|
68
|
+
(void 0)?.test("evalConfigFileContent parses static config exports", ({ expect }) => {
|
|
69
|
+
expect(evalConfigFileContent(`
|
|
70
|
+
import type { StackConfig } from "@hexclave/js";
|
|
71
|
+
export const config: StackConfig = {
|
|
72
|
+
auth: { allowSignUp: true },
|
|
73
|
+
payments: { testMode: false },
|
|
74
|
+
};
|
|
75
|
+
`, "stack.config.ts")).toMatchInlineSnapshot(`
|
|
76
|
+
{
|
|
77
|
+
"auth": {
|
|
78
|
+
"allowSignUp": true,
|
|
79
|
+
},
|
|
80
|
+
"payments": {
|
|
81
|
+
"testMode": false,
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
`);
|
|
85
|
+
});
|
|
86
|
+
(void 0)?.test("evalConfigFileContent parses show-onboarding", ({ expect }) => {
|
|
87
|
+
expect(evalConfigFileContent("export const config = \"show-onboarding\";", "stack.config.ts")).toBe("show-onboarding");
|
|
88
|
+
});
|
|
89
|
+
(void 0)?.test("evalConfigFileContent rejects content without config export", ({ expect }) => {
|
|
90
|
+
expect(() => evalConfigFileContent("export const other = {};", "stack.config.ts")).toThrow(/must export/);
|
|
91
|
+
});
|
|
92
|
+
(void 0)?.test("evalConfigFileContent rejects arbitrary string config values", ({ expect }) => {
|
|
93
|
+
expect(() => evalConfigFileContent("export const config = \"arbitrary-string\";", "stack.config.ts")).toThrow(/must be "show-onboarding"/);
|
|
94
|
+
});
|
|
95
|
+
(void 0)?.test("evalConfigFileContent rejects unresolvable config factories", ({ expect }) => {
|
|
96
|
+
expect(() => evalConfigFileContent("export const config = makeConfig();", "stack.config.ts")).toThrow();
|
|
97
|
+
});
|
|
98
|
+
(void 0)?.test("evalConfigFileContent rejects missing config import targets", ({ expect }) => {
|
|
99
|
+
expect(() => evalConfigFileContent(`
|
|
100
|
+
import missingConfigPart from "./missing-config-part";
|
|
101
|
+
export const config = { auth: missingConfigPart };
|
|
102
|
+
`, "/tmp/hexclave-missing-import-config.ts")).toThrow();
|
|
103
|
+
});
|
|
104
|
+
(void 0)?.test("evalConfigFileContent surfaces invalid syntax as a loader error, not ConfigFileEvalError", ({ expect }) => {
|
|
105
|
+
const evalInvalid = () => evalConfigFileContent("export const config = {", "stack.config.ts");
|
|
106
|
+
expect(evalInvalid).toThrow();
|
|
107
|
+
expect(evalInvalid).not.toThrow(ConfigFileEvalError);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
//#endregion
|
|
111
|
+
export { ConfigFileEvalError, detectImportPackageFromDir, evalConfigFileContent };
|
|
112
|
+
//# sourceMappingURL=config-eval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-eval.js","names":[],"sources":["../../src/config-eval.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"fs\";\nimport { createJiti } from \"jiti\";\nimport path from \"path\";\nimport { showOnboardingHexclaveConfigValue } from \"./config-authoring\";\nimport { detectConfigImportPackage } from \"./config-rendering\";\n\nconst jiti = createJiti(import.meta.url, { moduleCache: false });\n\n/**\n * Thrown when a config file evaluates successfully but its exported `config`\n * isn't a usable shape (missing, or not an object / \"show-onboarding\" string).\n * Distinct from the underlying loader errors jiti throws, so callers can tell a\n * malformed config apart from a file that simply failed to load.\n */\nexport class ConfigFileEvalError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ConfigFileEvalError\";\n }\n}\n\n/**\n * Walks up from `dir` to find the nearest `package.json` and returns the\n * best SDK package to use for the `HexclaveConfig` type import.\n */\nexport function detectImportPackageFromDir(dir: string): string | undefined {\n let current = dir;\n while (true) {\n const pkgPath = path.join(current, \"package.json\");\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n const deps = [\n ...Object.keys(pkg.dependencies ?? {}),\n ...Object.keys(pkg.devDependencies ?? {}),\n ];\n return detectConfigImportPackage(deps);\n } catch {\n return undefined;\n }\n }\n const parent = path.dirname(current);\n if (parent === current) break;\n current = parent;\n }\n return undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value != null && typeof value === \"object\" && !Array.isArray(value);\n}\n\n/** A config object, or the `\"show-onboarding\"` sentinel that stands in for one. */\nexport type ParsedConfigValue = Record<string, unknown> | typeof showOnboardingHexclaveConfigValue;\n\nfunction invalidConfigShape(filePath: string): ConfigFileEvalError {\n return new ConfigFileEvalError(`Invalid config in ${filePath}. The file must export a plain \\`config\\` object or \"${showOnboardingHexclaveConfigValue}\".`);\n}\n\n/**\n * Evaluates config file content using jiti and returns the exported `config`\n * value.\n *\n * WARNING: This executes arbitrary code via `jiti.evalModule` — only use on\n * content that is fully operator-controlled (local filesystem). Never call\n * this on untrusted input (e.g. content fetched from a remote repository).\n */\nexport function evalConfigFileContent(content: string, filePath: string): ParsedConfigValue {\n if (content.trim() === \"\") return {};\n const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(filePath);\n const mod: unknown = jiti.evalModule(content, { filename: resolvedPath });\n if (!isRecord(mod)) {\n throw invalidConfigShape(filePath);\n }\n const config = mod.config;\n if (config === undefined) {\n throw invalidConfigShape(filePath);\n }\n if (typeof config === \"string\") {\n if (config !== showOnboardingHexclaveConfigValue) {\n throw new ConfigFileEvalError(`Invalid config in ${filePath}. String config values must be \"${showOnboardingHexclaveConfigValue}\", got \"${config}\".`);\n }\n return config;\n }\n if (isRecord(config)) return config;\n throw invalidConfigShape(filePath);\n}\n\n// --- inline vitest tests ---\n\nimport.meta.vitest?.test(\"evalConfigFileContent parses static config exports\", ({ expect }) => {\n expect(evalConfigFileContent(`\n import type { StackConfig } from \"@hexclave/js\";\n export const config: StackConfig = {\n auth: { allowSignUp: true },\n payments: { testMode: false },\n };\n `, \"stack.config.ts\")).toMatchInlineSnapshot(`\n {\n \"auth\": {\n \"allowSignUp\": true,\n },\n \"payments\": {\n \"testMode\": false,\n },\n }\n `);\n});\n\nimport.meta.vitest?.test(\"evalConfigFileContent parses show-onboarding\", ({ expect }) => {\n expect(evalConfigFileContent('export const config = \"show-onboarding\";', \"stack.config.ts\")).toBe(\"show-onboarding\");\n});\n\nimport.meta.vitest?.test(\"evalConfigFileContent rejects content without config export\", ({ expect }) => {\n expect(() => evalConfigFileContent(\"export const other = {};\", \"stack.config.ts\")).toThrow(/must export/);\n});\n\nimport.meta.vitest?.test(\"evalConfigFileContent rejects arbitrary string config values\", ({ expect }) => {\n expect(() => evalConfigFileContent('export const config = \"arbitrary-string\";', \"stack.config.ts\")).toThrow(/must be \"show-onboarding\"/);\n});\n\nimport.meta.vitest?.test(\"evalConfigFileContent rejects unresolvable config factories\", ({ expect }) => {\n expect(() => evalConfigFileContent(\"export const config = makeConfig();\", \"stack.config.ts\")).toThrow();\n});\n\nimport.meta.vitest?.test(\"evalConfigFileContent rejects missing config import targets\", ({ expect }) => {\n expect(() => evalConfigFileContent(`\n import missingConfigPart from \"./missing-config-part\";\n export const config = { auth: missingConfigPart };\n `, \"/tmp/hexclave-missing-import-config.ts\")).toThrow();\n});\n\nimport.meta.vitest?.test(\"evalConfigFileContent surfaces invalid syntax as a loader error, not ConfigFileEvalError\", ({ expect }) => {\n // A malformed file fails inside jiti's parser, so the thrown error is a loader\n // error — NOT a ConfigFileEvalError. Callers depend on that distinction to route\n // it to \"Failed to load config file\" rather than \"Invalid config\".\n const evalInvalid = () => evalConfigFileContent(\"export const config = {\", \"stack.config.ts\");\n expect(evalInvalid).toThrow();\n expect(evalInvalid).not.toThrow(ConfigFileEvalError);\n});\n"],"mappings":";;;;;;;AAMA,MAAM,OAAO,WAAW,OAAO,KAAK,KAAK,EAAE,aAAa,OAAO,CAAC;;;;;;;AAQhE,IAAa,sBAAb,cAAyC,MAAM;CAC7C,YAAY,SAAiB;AAC3B,QAAM,QAAQ;AACd,OAAK,OAAO;;;;;;;AAQhB,SAAgB,2BAA2B,KAAiC;CAC1E,IAAI,UAAU;AACd,QAAO,MAAM;EACX,MAAM,UAAU,KAAK,KAAK,SAAS,eAAe;AAClD,MAAI,WAAW,QAAQ,CACrB,KAAI;GACF,MAAM,MAAM,KAAK,MAAM,aAAa,SAAS,QAAQ,CAAC;AAKtD,UAAO,0BAJM,CACX,GAAG,OAAO,KAAK,IAAI,gBAAgB,EAAE,CAAC,EACtC,GAAG,OAAO,KAAK,IAAI,mBAAmB,EAAE,CAAC,CAC1C,CACqC;UAChC;AACN;;EAGJ,MAAM,SAAS,KAAK,QAAQ,QAAQ;AACpC,MAAI,WAAW,QAAS;AACxB,YAAU;;;AAKd,SAAS,SAAS,OAAkD;AAClE,QAAO,SAAS,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,MAAM;;AAM5E,SAAS,mBAAmB,UAAuC;AACjE,QAAO,IAAI,oBAAoB,qBAAqB,SAAS,uDAAuD,kCAAkC,IAAI;;;;;;;;;;AAW5J,SAAgB,sBAAsB,SAAiB,UAAqC;AAC1F,KAAI,QAAQ,MAAM,KAAK,GAAI,QAAO,EAAE;CACpC,MAAM,eAAe,KAAK,WAAW,SAAS,GAAG,WAAW,KAAK,QAAQ,SAAS;CAClF,MAAM,MAAe,KAAK,WAAW,SAAS,EAAE,UAAU,cAAc,CAAC;AACzE,KAAI,CAAC,SAAS,IAAI,CAChB,OAAM,mBAAmB,SAAS;CAEpC,MAAM,SAAS,IAAI;AACnB,KAAI,WAAW,OACb,OAAM,mBAAmB,SAAS;AAEpC,KAAI,OAAO,WAAW,UAAU;AAC9B,MAAI,WAAW,kCACb,OAAM,IAAI,oBAAoB,qBAAqB,SAAS,kCAAkC,kCAAkC,UAAU,OAAO,IAAI;AAEvJ,SAAO;;AAET,KAAI,SAAS,OAAO,CAAE,QAAO;AAC7B,OAAM,mBAAmB,SAAS;;CAKpC,SAAW,KAAC,uDAAyD,EAAA,aAAa;AAChF,QAAO,sBAAsB;;;;;;KAM1B,kBAAkB,CAAC,CAAC,sBAAsB;;;;;;;;;IAS3C;EACF;CAEF,SAAW,KAAC,iDAAgD,EAAA,aAAgB;AAC1E,QAAO,sBAAsB,8CAA4C,kBAAkB,CAAC,CAAC,KAAK,kBAAkB;EACpH;CAEF,SAAW,KAAC,gEAAmE,EAAA,aAAY;AACzF,cAAa,sBAAsB,4BAA4B,kBAAkB,CAAC,CAAC,QAAQ,cAAc;EACzG;CAEF,SAAW,KAAC,iEAAoE,EAAA,aAAY;AAC1F,cAAa,sBAAsB,+CAA6C,kBAAkB,CAAC,CAAC,QAAQ,4BAA4B;EACxI;CAEF,SAAW,KAAC,gEAAgE,EAAA,aAAe;AACzF,cAAa,sBAAsB,uCAAuC,kBAAkB,CAAC,CAAC,SAAS;EACvG;CAEF,SAAW,KAAC,gEAAkE,EAAA,aAAa;AACzF,cAAa,sBAAsB;;;KAGhC,yCAAyC,CAAC,CAAC,SAAS;EACvD;CAEF,SAAW,KAAC,6FAAmF,EAAA,aAAyB;CAItH,MAAM,oBAAoB,sBAAsB,2BAA2B,kBAAkB;AAC7F,QAAO,YAAY,CAAC,SAAS;AAC7B,QAAO,YAAY,CAAC,IAAI,QAAQ,oBAAoB;EACpD"}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { hexclaveConfigFileExportsConfig, parseHexclaveConfigFileContent, renderConfigFileContent, tryParseHexclaveConfigFileContent } from "./hexclave-config-file";
|
|
2
|
-
|
|
3
1
|
//#region src/config-rendering.d.ts
|
|
4
2
|
/**
|
|
5
3
|
* Given a list of dependency names (from package.json), returns the SDK
|
|
@@ -8,10 +6,9 @@ import { hexclaveConfigFileExportsConfig, parseHexclaveConfigFileContent, render
|
|
|
8
6
|
*/
|
|
9
7
|
declare function detectConfigImportPackage(dependencies: string[]): string | undefined;
|
|
10
8
|
/**
|
|
11
|
-
*
|
|
12
|
-
* best SDK package to use for the `HexclaveConfig` type import.
|
|
9
|
+
* Renders a config object into the source text of a `stack.config.ts` file.
|
|
13
10
|
*/
|
|
14
|
-
declare function
|
|
11
|
+
declare function renderConfigFileContent(config: unknown, importPackage?: string): string;
|
|
15
12
|
//#endregion
|
|
16
|
-
export { detectConfigImportPackage,
|
|
13
|
+
export { detectConfigImportPackage, renderConfigFileContent };
|
|
17
14
|
//# sourceMappingURL=config-rendering.d.ts.map
|