@hexclave/shared 1.0.36 → 1.0.38
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/reminders.js +2 -1
- package/dist/ai/unified-prompts/reminders.js.map +1 -1
- 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 +147 -147
- 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/reminders.js +2 -1
- package/dist/esm/ai/unified-prompts/reminders.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 +147 -147
- 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/hooks/use-async-callback.js +1 -1
- package/dist/esm/hooks/use-async-external-store.js +1 -1
- package/dist/esm/hooks/use-strict-memo.js +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 +55 -0
- package/dist/esm/interface/admin-interface.js.map +1 -1
- package/dist/esm/interface/admin-metrics.d.ts +12 -12
- package/dist/esm/interface/conversations.d.ts +31 -31
- package/dist/esm/interface/crud/current-user.d.ts +7 -7
- package/dist/esm/interface/crud/email-outbox.d.ts +158 -158
- package/dist/esm/interface/crud/invoices.d.ts +2 -2
- 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/team-member-profiles.d.ts +18 -18
- 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 +12 -12
- package/dist/esm/interface/plan-usage.d.ts +2 -2
- 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/hooks/use-async-callback.js +1 -1
- package/dist/hooks/use-async-external-store.js +1 -1
- package/dist/hooks/use-strict-memo.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 +55 -0
- package/dist/interface/admin-interface.js.map +1 -1
- package/dist/interface/admin-metrics.d.ts +12 -12
- package/dist/interface/conversations.d.ts +31 -31
- package/dist/interface/crud/current-user.d.ts +7 -7
- package/dist/interface/crud/email-outbox.d.ts +158 -158
- package/dist/interface/crud/invoices.d.ts +2 -2
- 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/team-member-profiles.d.ts +18 -18
- 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 +12 -12
- package/dist/interface/plan-usage.d.ts +2 -2
- 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/package.json +2 -1
- package/src/ai/unified-prompts/reminders.ts +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
|
@@ -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
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-rendering.d.ts","names":[],"sources":["../../src/config-rendering.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"config-rendering.d.ts","names":[],"sources":["../../src/config-rendering.ts"],"mappings":";;AA4BA;;;;iBAAgB,yBAAA,CAA0B,YAAA;AAY1C;;;AAAA,iBAAgB,uBAAA,CAAwB,MAAA,WAAiB,aAAA"}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import path from "path";
|
|
3
|
-
import { hexclaveConfigFileExportsConfig, parseHexclaveConfigFileContent, renderConfigFileContent, tryParseHexclaveConfigFileContent } from "./hexclave-config-file.js";
|
|
1
|
+
import { isValidConfig, normalize } from "./config/format.js";
|
|
4
2
|
|
|
5
3
|
//#region src/config-rendering.ts
|
|
4
|
+
const DEFAULT_CONFIG_IMPORT_PACKAGE = "@hexclave/js";
|
|
6
5
|
/**
|
|
7
6
|
* Packages that export the `HexclaveConfig` type, in priority order.
|
|
8
7
|
* The first match found in a project's dependencies wins. Hexclave-branded
|
|
@@ -30,23 +29,19 @@ function detectConfigImportPackage(dependencies) {
|
|
|
30
29
|
for (const pkg of CONFIG_IMPORT_PACKAGES) if (dependencies.includes(pkg)) return pkg;
|
|
31
30
|
}
|
|
32
31
|
/**
|
|
33
|
-
*
|
|
34
|
-
* best SDK package to use for the `HexclaveConfig` type import.
|
|
32
|
+
* Renders a config object into the source text of a `stack.config.ts` file.
|
|
35
33
|
*/
|
|
36
|
-
function
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (parent === current) break;
|
|
48
|
-
current = parent;
|
|
49
|
-
}
|
|
34
|
+
function renderConfigFileContent(config, importPackage) {
|
|
35
|
+
if (!isValidConfig(config)) throw new Error("Invalid config: expected a plain object.");
|
|
36
|
+
const droppedKeys = [];
|
|
37
|
+
const normalizedConfig = normalize(config, {
|
|
38
|
+
onDotIntoNonObject: "ignore",
|
|
39
|
+
onDotIntoNull: "empty-object",
|
|
40
|
+
droppedKeys
|
|
41
|
+
});
|
|
42
|
+
if (droppedKeys.length > 0) throw new Error(`Config has conflicting keys that would be dropped during normalization: ${droppedKeys.map((k) => JSON.stringify(k)).join(", ")}`);
|
|
43
|
+
const pkg = importPackage ?? DEFAULT_CONFIG_IMPORT_PACKAGE;
|
|
44
|
+
return `${`import type { HexclaveConfig } from "${pkg.startsWith("@hexclave/") ? `${pkg}/config` : pkg}";`}\n\nexport const config: HexclaveConfig = ${JSON.stringify(normalizedConfig, null, 2)};\n`;
|
|
50
45
|
}
|
|
51
46
|
(void 0)?.test("renderConfigFileContent normalizes config exports", ({ expect }) => {
|
|
52
47
|
expect(renderConfigFileContent({
|
|
@@ -63,44 +58,6 @@ function detectImportPackageFromDir(dir) {
|
|
|
63
58
|
}
|
|
64
59
|
};`);
|
|
65
60
|
});
|
|
66
|
-
(void 0)?.test("parseHexclaveConfigFileContent parses static config exports", ({ expect }) => {
|
|
67
|
-
expect(parseHexclaveConfigFileContent(`
|
|
68
|
-
import type { StackConfig } from "@hexclave/js";
|
|
69
|
-
export const config: StackConfig = {
|
|
70
|
-
auth: { allowSignUp: true },
|
|
71
|
-
payments: { testMode: false },
|
|
72
|
-
};
|
|
73
|
-
`, "stack.config.ts")).toMatchInlineSnapshot(`
|
|
74
|
-
{
|
|
75
|
-
"auth": {
|
|
76
|
-
"allowSignUp": true,
|
|
77
|
-
},
|
|
78
|
-
"payments": {
|
|
79
|
-
"testMode": false,
|
|
80
|
-
},
|
|
81
|
-
}
|
|
82
|
-
`);
|
|
83
|
-
});
|
|
84
|
-
(void 0)?.test("parseHexclaveConfigFileContent parses show-onboarding", ({ expect }) => {
|
|
85
|
-
expect(parseHexclaveConfigFileContent("export const config = \"show-onboarding\";", "stack.config.ts")).toBe("show-onboarding");
|
|
86
|
-
});
|
|
87
|
-
(void 0)?.test("parseHexclaveConfigFileContent rejects dynamic config exports", ({ expect }) => {
|
|
88
|
-
expect(() => parseHexclaveConfigFileContent("export const config = makeConfig();", "stack.config.ts")).toThrow(/Unsupported config expression/);
|
|
89
|
-
});
|
|
90
|
-
(void 0)?.test("tryParseHexclaveConfigFileContent returns the config for static exports", ({ expect }) => {
|
|
91
|
-
expect(tryParseHexclaveConfigFileContent("export const config = { auth: { allowSignUp: true } };", "stack.config.ts")).toEqual({ auth: { allowSignUp: true } });
|
|
92
|
-
});
|
|
93
|
-
(void 0)?.test("tryParseHexclaveConfigFileContent returns null for non-static exports", ({ expect }) => {
|
|
94
|
-
expect(tryParseHexclaveConfigFileContent("export const config = makeConfig();", "stack.config.ts")).toBeNull();
|
|
95
|
-
expect(tryParseHexclaveConfigFileContent("import x from \"./x.txt\" with { type: \"text\" };\nexport const config = { a: x };", "stack.config.ts")).toBeNull();
|
|
96
|
-
expect(tryParseHexclaveConfigFileContent("export const config = {", "stack.config.ts")).toBeNull();
|
|
97
|
-
});
|
|
98
|
-
(void 0)?.test("hexclaveConfigFileExportsConfig detects a config export", ({ expect }) => {
|
|
99
|
-
expect(hexclaveConfigFileExportsConfig("export const config = { a: 1 };", "stack.config.ts")).toBe(true);
|
|
100
|
-
expect(hexclaveConfigFileExportsConfig("import x from \"./x.txt\" with { type: \"text\" };\nexport const config = { a: x };", "stack.config.ts")).toBe(true);
|
|
101
|
-
expect(hexclaveConfigFileExportsConfig("export const notConfig = { a: 1 };", "stack.config.ts")).toBe(false);
|
|
102
|
-
expect(hexclaveConfigFileExportsConfig("export const config = {", "stack.config.ts")).toBe(false);
|
|
103
|
-
});
|
|
104
61
|
(void 0)?.test("renderConfigFileContent rejects conflicting dotted keys", ({ expect }) => {
|
|
105
62
|
expect(() => renderConfigFileContent({
|
|
106
63
|
"a.b": 1,
|
|
@@ -132,5 +89,5 @@ function detectImportPackageFromDir(dir) {
|
|
|
132
89
|
});
|
|
133
90
|
|
|
134
91
|
//#endregion
|
|
135
|
-
export { detectConfigImportPackage,
|
|
92
|
+
export { detectConfigImportPackage, renderConfigFileContent };
|
|
136
93
|
//# sourceMappingURL=config-rendering.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-rendering.js","names":[],"sources":["../../src/config-rendering.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"fs\";\nimport path from \"path\";\nimport { hexclaveConfigFileExportsConfig, parseHexclaveConfigFileContent, renderConfigFileContent, tryParseHexclaveConfigFileContent } from \"./hexclave-config-file\";\nexport { hexclaveConfigFileExportsConfig, parseHexclaveConfigFileContent, renderConfigFileContent, tryParseHexclaveConfigFileContent };\n\n/**\n * Packages that export the `HexclaveConfig` type, in priority order.\n * The first match found in a project's dependencies wins. Hexclave-branded\n * packages come first (canonical); the legacy `@stackframe/*` names remain\n * so projects pinned to the last legacy release still render a config file\n * that compiles against their installed SDK.\n */\nconst CONFIG_IMPORT_PACKAGES = [\n \"@hexclave/next\",\n \"@hexclave/react\",\n \"@hexclave/tanstack-start\",\n \"@hexclave/js\",\n \"@hexclave/template\",\n \"@stackframe/stack\",\n \"@stackframe/react\",\n \"@stackframe/js\",\n \"@stackframe/template\",\n] as const;\n\n/**\n * Given a list of dependency names (from package.json), returns the SDK\n * package that should be used for the `HexclaveConfig` import, or `undefined`\n * if none of the known packages are installed.\n */\nexport function detectConfigImportPackage(dependencies: string[]): string | undefined {\n for (const pkg of CONFIG_IMPORT_PACKAGES) {\n if (dependencies.includes(pkg)) {\n return pkg;\n }\n }\n return undefined;\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\nimport.meta.vitest?.test(\"renderConfigFileContent normalizes config exports\", ({ expect }) => {\n expect(renderConfigFileContent({\n \"payments.items.todos.displayName\": \"Todo Slots\",\n \"payments.items.todos.customerType\": \"user\",\n })).toContain(`export const config: HexclaveConfig = {\n \"payments\": {\n \"items\": {\n \"todos\": {\n \"displayName\": \"Todo Slots\",\n \"customerType\": \"user\"\n }\n }\n }\n};`);\n});\n\nimport.meta.vitest?.test(\"parseHexclaveConfigFileContent parses static config exports\", ({ expect }) => {\n expect(parseHexclaveConfigFileContent(`\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(\"parseHexclaveConfigFileContent parses show-onboarding\", ({ expect }) => {\n expect(parseHexclaveConfigFileContent('export const config = \"show-onboarding\";', \"stack.config.ts\")).toBe(\"show-onboarding\");\n});\n\nimport.meta.vitest?.test(\"parseHexclaveConfigFileContent rejects dynamic config exports\", ({ expect }) => {\n expect(() => parseHexclaveConfigFileContent(\"export const config = makeConfig();\", \"stack.config.ts\")).toThrow(/Unsupported config expression/);\n});\n\nimport.meta.vitest?.test(\"tryParseHexclaveConfigFileContent returns the config for static exports\", ({ expect }) => {\n expect(tryParseHexclaveConfigFileContent(\"export const config = { auth: { allowSignUp: true } };\", \"stack.config.ts\")).toEqual({\n auth: { allowSignUp: true },\n });\n});\n\nimport.meta.vitest?.test(\"tryParseHexclaveConfigFileContent returns null for non-static exports\", ({ expect }) => {\n // Wrapped in a helper call (e.g. defineStackConfig) -> not a plain literal.\n expect(tryParseHexclaveConfigFileContent(\"export const config = makeConfig();\", \"stack.config.ts\")).toBeNull();\n // References an imported value -> has structure to preserve.\n expect(tryParseHexclaveConfigFileContent('import x from \"./x.txt\" with { type: \"text\" };\\nexport const config = { a: x };', \"stack.config.ts\")).toBeNull();\n // Syntax error.\n expect(tryParseHexclaveConfigFileContent(\"export const config = {\", \"stack.config.ts\")).toBeNull();\n});\n\nimport.meta.vitest?.test(\"hexclaveConfigFileExportsConfig detects a config export\", ({ expect }) => {\n expect(hexclaveConfigFileExportsConfig(\"export const config = { a: 1 };\", \"stack.config.ts\")).toBe(true);\n expect(hexclaveConfigFileExportsConfig('import x from \"./x.txt\" with { type: \"text\" };\\nexport const config = { a: x };', \"stack.config.ts\")).toBe(true);\n expect(hexclaveConfigFileExportsConfig(\"export const notConfig = { a: 1 };\", \"stack.config.ts\")).toBe(false);\n expect(hexclaveConfigFileExportsConfig(\"export const config = {\", \"stack.config.ts\")).toBe(false);\n});\n\nimport.meta.vitest?.test(\"renderConfigFileContent rejects conflicting dotted keys\", ({ expect }) => {\n expect(() => renderConfigFileContent({\n \"a.b\": 1,\n \"a.b.c\": 2,\n })).toThrowError(/conflicting keys.*\"a\\.b\\.c\"/);\n});\n\nimport.meta.vitest?.test(\"renderConfigFileContent rejects invalid config exports\", ({ expect }) => {\n expect(() => renderConfigFileContent(null)).toThrowErrorMatchingInlineSnapshot(\n `[Error: Invalid config: expected a plain object.]`,\n );\n});\n\nimport.meta.vitest?.test(\"renderConfigFileContent uses custom import package\", ({ expect }) => {\n const content = renderConfigFileContent({}, \"@hexclave/next\");\n expect(content).toContain('import type { HexclaveConfig } from \"@hexclave/next/config\";');\n});\n\nimport.meta.vitest?.test(\"renderConfigFileContent defaults to @hexclave/js\", ({ expect }) => {\n const content = renderConfigFileContent({});\n expect(content).toContain('import type { HexclaveConfig } from \"@hexclave/js/config\";');\n});\n\nimport.meta.vitest?.test(\"renderConfigFileContent keeps legacy @stackframe packages on their root entrypoint\", ({ expect }) => {\n // The lightweight `/config` subpath only exists on Hexclave-branded packages;\n // already-published @stackframe/* releases predate it.\n const content = renderConfigFileContent({}, \"@stackframe/next\");\n expect(content).toContain('import type { HexclaveConfig } from \"@stackframe/next\";');\n});\n\nimport.meta.vitest?.test(\"detectConfigImportPackage picks first matching package by priority\", ({ expect }) => {\n expect(detectConfigImportPackage([\"@hexclave/next\", \"@hexclave/js\"])).toBe(\"@hexclave/next\");\n expect(detectConfigImportPackage([\"@hexclave/react\", \"@hexclave/js\"])).toBe(\"@hexclave/react\");\n expect(detectConfigImportPackage([\"@hexclave/js\"])).toBe(\"@hexclave/js\");\n expect(detectConfigImportPackage([\"@hexclave/tanstack-start\"])).toBe(\"@hexclave/tanstack-start\");\n // Hexclave names take priority over legacy stackframe names when both appear.\n expect(detectConfigImportPackage([\"@stackframe/stack\", \"@hexclave/next\"])).toBe(\"@hexclave/next\");\n // Legacy fallback still works for projects pinned to the last @stackframe/* release.\n expect(detectConfigImportPackage([\"@stackframe/stack\"])).toBe(\"@stackframe/stack\");\n expect(detectConfigImportPackage([\"@stackframe/template\"])).toBe(\"@stackframe/template\");\n expect(detectConfigImportPackage([\"lodash\", \"express\"])).toBeUndefined();\n expect(detectConfigImportPackage([])).toBeUndefined();\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"config-rendering.js","names":[],"sources":["../../src/config-rendering.ts"],"sourcesContent":["import { isValidConfig, normalize } from \"./config/format\";\n\nconst DEFAULT_CONFIG_IMPORT_PACKAGE = \"@hexclave/js\";\n\n/**\n * Packages that export the `HexclaveConfig` type, in priority order.\n * The first match found in a project's dependencies wins. Hexclave-branded\n * packages come first (canonical); the legacy `@stackframe/*` names remain\n * so projects pinned to the last legacy release still render a config file\n * that compiles against their installed SDK.\n */\nconst CONFIG_IMPORT_PACKAGES = [\n \"@hexclave/next\",\n \"@hexclave/react\",\n \"@hexclave/tanstack-start\",\n \"@hexclave/js\",\n \"@hexclave/template\",\n \"@stackframe/stack\",\n \"@stackframe/react\",\n \"@stackframe/js\",\n \"@stackframe/template\",\n] as const;\n\n/**\n * Given a list of dependency names (from package.json), returns the SDK\n * package that should be used for the `HexclaveConfig` import, or `undefined`\n * if none of the known packages are installed.\n */\nexport function detectConfigImportPackage(dependencies: string[]): string | undefined {\n for (const pkg of CONFIG_IMPORT_PACKAGES) {\n if (dependencies.includes(pkg)) {\n return pkg;\n }\n }\n return undefined;\n}\n\n/**\n * Renders a config object into the source text of a `stack.config.ts` file.\n */\nexport function renderConfigFileContent(config: unknown, importPackage?: string): string {\n if (!isValidConfig(config)) {\n throw new Error(\"Invalid config: expected a plain object.\");\n }\n\n const droppedKeys: string[] = [];\n const normalizedConfig = normalize(config, {\n onDotIntoNonObject: \"ignore\",\n onDotIntoNull: \"empty-object\",\n droppedKeys,\n });\n if (droppedKeys.length > 0) {\n throw new Error(`Config has conflicting keys that would be dropped during normalization: ${droppedKeys.map(k => JSON.stringify(k)).join(\", \")}`);\n }\n const pkg = importPackage ?? DEFAULT_CONFIG_IMPORT_PACKAGE;\n const importSpecifier = pkg.startsWith(\"@hexclave/\") ? `${pkg}/config` : pkg;\n const importLine = `import type { HexclaveConfig } from \"${importSpecifier}\";`;\n return `${importLine}\\n\\nexport const config: HexclaveConfig = ${JSON.stringify(normalizedConfig, null, 2)};\\n`;\n}\n\n// --- inline vitest tests ---\n\nimport.meta.vitest?.test(\"renderConfigFileContent normalizes config exports\", ({ expect }) => {\n expect(renderConfigFileContent({\n \"payments.items.todos.displayName\": \"Todo Slots\",\n \"payments.items.todos.customerType\": \"user\",\n })).toContain(`export const config: HexclaveConfig = {\n \"payments\": {\n \"items\": {\n \"todos\": {\n \"displayName\": \"Todo Slots\",\n \"customerType\": \"user\"\n }\n }\n }\n};`);\n});\n\nimport.meta.vitest?.test(\"renderConfigFileContent rejects conflicting dotted keys\", ({ expect }) => {\n expect(() => renderConfigFileContent({\n \"a.b\": 1,\n \"a.b.c\": 2,\n })).toThrowError(/conflicting keys.*\"a\\.b\\.c\"/);\n});\n\nimport.meta.vitest?.test(\"renderConfigFileContent rejects invalid config exports\", ({ expect }) => {\n expect(() => renderConfigFileContent(null)).toThrowErrorMatchingInlineSnapshot(\n `[Error: Invalid config: expected a plain object.]`,\n );\n});\n\nimport.meta.vitest?.test(\"renderConfigFileContent uses custom import package\", ({ expect }) => {\n const content = renderConfigFileContent({}, \"@hexclave/next\");\n expect(content).toContain('import type { HexclaveConfig } from \"@hexclave/next/config\";');\n});\n\nimport.meta.vitest?.test(\"renderConfigFileContent defaults to @hexclave/js\", ({ expect }) => {\n const content = renderConfigFileContent({});\n expect(content).toContain('import type { HexclaveConfig } from \"@hexclave/js/config\";');\n});\n\nimport.meta.vitest?.test(\"renderConfigFileContent keeps legacy @stackframe packages on their root entrypoint\", ({ expect }) => {\n const content = renderConfigFileContent({}, \"@stackframe/next\");\n expect(content).toContain('import type { HexclaveConfig } from \"@stackframe/next\";');\n});\n\nimport.meta.vitest?.test(\"detectConfigImportPackage picks first matching package by priority\", ({ expect }) => {\n expect(detectConfigImportPackage([\"@hexclave/next\", \"@hexclave/js\"])).toBe(\"@hexclave/next\");\n expect(detectConfigImportPackage([\"@hexclave/react\", \"@hexclave/js\"])).toBe(\"@hexclave/react\");\n expect(detectConfigImportPackage([\"@hexclave/js\"])).toBe(\"@hexclave/js\");\n expect(detectConfigImportPackage([\"@hexclave/tanstack-start\"])).toBe(\"@hexclave/tanstack-start\");\n expect(detectConfigImportPackage([\"@stackframe/stack\", \"@hexclave/next\"])).toBe(\"@hexclave/next\");\n expect(detectConfigImportPackage([\"@stackframe/stack\"])).toBe(\"@stackframe/stack\");\n expect(detectConfigImportPackage([\"@stackframe/template\"])).toBe(\"@stackframe/template\");\n expect(detectConfigImportPackage([\"lodash\", \"express\"])).toBeUndefined();\n expect(detectConfigImportPackage([])).toBeUndefined();\n});\n"],"mappings":";;;AAEA,MAAM,gCAAgC;;;;;;;;AAStC,MAAM,yBAAyB;CAC7B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;AAOD,SAAgB,0BAA0B,cAA4C;AACpF,MAAK,MAAM,OAAO,uBAChB,KAAI,aAAa,SAAS,IAAI,CAC5B,QAAO;;;;;AASb,SAAgB,wBAAwB,QAAiB,eAAgC;AACvF,KAAI,CAAC,cAAc,OAAO,CACxB,OAAM,IAAI,MAAM,2CAA2C;CAG7D,MAAM,cAAwB,EAAE;CAChC,MAAM,mBAAmB,UAAU,QAAQ;EACzC,oBAAoB;EACpB,eAAe;EACf;EACD,CAAC;AACF,KAAI,YAAY,SAAS,EACvB,OAAM,IAAI,MAAM,2EAA2E,YAAY,KAAI,MAAK,KAAK,UAAU,EAAE,CAAC,CAAC,KAAK,KAAK,GAAG;CAElJ,MAAM,MAAM,iBAAiB;AAG7B,QAAO,GADY,wCADK,IAAI,WAAW,aAAa,GAAG,GAAG,IAAI,WAAW,IACE,IACtD,4CAA4C,KAAK,UAAU,kBAAkB,MAAM,EAAE,CAAC;;CAK7G,SAAW,KAAC,sDAAwD,EAAA,aAAa;AAC/E,QAAO,wBAAwB;EAC7B,oCAAoC;EACpC,qCAAqC;EACtC,CAAC,CAAC,CAAC,UAAU;;;;;;;;;IASZ;EACF;CAEF,SAAW,KAAC,4DAAgE,EAAC,aAAU;AACrF,cAAa,wBAAwB;EACnC,OAAO;EACP,SAAS;EACV,CAAC,CAAC,CAAC,aAAa,8BAA8B;EAC/C;CAEF,SAAW,KAAC,2DAA6D,EAAA,aAAa;AACpF,cAAa,wBAAwB,KAAK,CAAC,CAAC,mCAC1C,oDACD;EACD;CAEF,SAAW,KAAC,uDAAyD,EAAA,aAAa;AAEhF,QADgB,wBAAwB,EAAE,EAAE,iBAAiB,CAC9C,CAAC,UAAU,iEAA+D;EACzF;CAEF,SAAW,KAAC,qDAAmD,EAAQ,aAAS;AAE9E,QADgB,wBAAwB,EAAE,CAAC,CAC5B,CAAC,UAAU,+DAA6D;EACvF;CAEF,SAAW,KAAC,uFAAsF,EAAA,aAAgB;AAEhH,QADgB,wBAAwB,EAAE,EAAE,mBAAmB,CAChD,CAAC,UAAU,4DAA0D;EACpF;CAEF,SAAW,KAAC,uEAAwE,EAAA,aAAc;AAChG,QAAO,0BAA0B,CAAC,kBAAkB,eAAe,CAAC,CAAC,CAAC,KAAK,iBAAiB;AAC5F,QAAO,0BAA0B,CAAC,mBAAmB,eAAe,CAAC,CAAC,CAAC,KAAK,kBAAkB;AAC9F,QAAO,0BAA0B,CAAC,eAAe,CAAC,CAAC,CAAC,KAAK,eAAe;AACxE,QAAO,0BAA0B,CAAC,2BAA2B,CAAC,CAAC,CAAC,KAAK,2BAA2B;AAChG,QAAO,0BAA0B,CAAC,qBAAqB,iBAAiB,CAAC,CAAC,CAAC,KAAK,iBAAiB;AACjG,QAAO,0BAA0B,CAAC,oBAAoB,CAAC,CAAC,CAAC,KAAK,oBAAoB;AAClF,QAAO,0BAA0B,CAAC,uBAAuB,CAAC,CAAC,CAAC,KAAK,uBAAuB;AACxF,QAAO,0BAA0B,CAAC,UAAU,UAAU,CAAC,CAAC,CAAC,eAAe;AACxE,QAAO,0BAA0B,EAAE,CAAC,CAAC,CAAC,eAAe;EACrD"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as yup$1 from "yup";
|
|
2
2
|
import { Result } from "../utils/results";
|
|
3
|
+
import { EnvironmentConfigOverrideOverride } from "../config/schema";
|
|
3
4
|
import { KnownErrors } from "../known-errors";
|
|
4
|
-
import { RestrictedReason, branchConfigSourceSchema } from "../schema-fields";
|
|
5
|
+
import { ConfigAgentRunApi, RestrictedReason, branchConfigSourceSchema } from "../schema-fields";
|
|
5
6
|
import { AccessToken, InternalSession, RefreshToken } from "../sessions";
|
|
6
7
|
import { MoneyAmount } from "../utils/currency-constants";
|
|
7
|
-
import { PlanUsageResponse, PlanUsageResponse as PlanUsageResponse$1 } from "./plan-usage";
|
|
8
8
|
import { AnalyticsClickmapDevice, AnalyticsClickmapKind, AnalyticsClickmapResponse, AnalyticsClickmapTokenResponse, MetricsResponse, MetricsUserCounts, UserActivityResponse } from "./admin-metrics";
|
|
9
9
|
import { AnalyticsQueryOptions, AnalyticsQueryResponse } from "./crud/analytics";
|
|
10
10
|
import { EmailOutboxCrud } from "./crud/email-outbox";
|
|
@@ -16,6 +16,7 @@ import { AdminGetSessionReplayAllEventsResponse, AdminGetSessionReplayChunkEvent
|
|
|
16
16
|
import { SvixTokenCrud } from "./crud/svix-token";
|
|
17
17
|
import { TeamPermissionDefinitionsCrud } from "./crud/team-permissions";
|
|
18
18
|
import { Transaction, TransactionType } from "./crud/transactions";
|
|
19
|
+
import { PlanUsageResponse, PlanUsageResponse as PlanUsageResponse$1 } from "./plan-usage";
|
|
19
20
|
import { HexclaveServerInterface, ServerAuthApplicationOptions } from "./server-interface";
|
|
20
21
|
|
|
21
22
|
//#region src/interface/admin-interface.d.ts
|
|
@@ -246,6 +247,47 @@ declare class HexclaveAdminInterface extends HexclaveServerInterface {
|
|
|
246
247
|
updateConfigOverride(level: "project" | "branch" | "environment", configOverrideOverride: any): Promise<void>;
|
|
247
248
|
getPushedConfigSource(): Promise<BranchConfigSourceApi>;
|
|
248
249
|
unlinkPushedConfigSource(): Promise<void>;
|
|
250
|
+
/**
|
|
251
|
+
* Reads a specific config-agent run's state (or `null`) for the linked GitHub
|
|
252
|
+
* repo. Polled by the dashboard — using the id returned by `applyConfigViaAgent`
|
|
253
|
+
* — for live progress and the review diff. Runs are independent, so each is
|
|
254
|
+
* addressed by its own id rather than "the" run on the branch.
|
|
255
|
+
*/
|
|
256
|
+
getConfigAgentRun(runId: string): Promise<ConfigAgentRunApi | null>;
|
|
257
|
+
/**
|
|
258
|
+
* Applies a dashboard config change to the linked GitHub repo by running the
|
|
259
|
+
* config agent in a sandbox (server-side). Returns immediately with the new run's
|
|
260
|
+
* `id`; poll `getConfigAgentRun(id)` for progress. The GitHub access token is the
|
|
261
|
+
* caller's own OAuth token and is used transiently server-side.
|
|
262
|
+
*/
|
|
263
|
+
applyConfigViaAgent(options: {
|
|
264
|
+
configUpdate: EnvironmentConfigOverrideOverride;
|
|
265
|
+
githubAccessToken: string;
|
|
266
|
+
}): Promise<{
|
|
267
|
+
status: "started";
|
|
268
|
+
id: string;
|
|
269
|
+
}>;
|
|
270
|
+
/**
|
|
271
|
+
* Cancels a specific in-flight agent-driven config write: hard-stops the sandbox
|
|
272
|
+
* so the agent stops mid-work. Also cancels runs in `awaiting_review`. No revert
|
|
273
|
+
* — if the agent already pushed, the commit stays. Returns `not-running` if the
|
|
274
|
+
* run is gone or already terminal.
|
|
275
|
+
*/
|
|
276
|
+
cancelConfigAgentRun(runId: string): Promise<{
|
|
277
|
+
status: "cancelling" | "not-running";
|
|
278
|
+
}>;
|
|
279
|
+
/**
|
|
280
|
+
* Commits a specific run's reviewed change to GitHub. Only valid when that run is in
|
|
281
|
+
* `awaiting_review` status; the change (diff + base commit) was captured at apply time
|
|
282
|
+
* and is rebuilt + pushed via the GitHub API here, so no live sandbox is involved.
|
|
283
|
+
* Returns `not-awaiting-review` if the run isn't in a committable state.
|
|
284
|
+
*/
|
|
285
|
+
commitConfigAgentRun(runId: string, options: {
|
|
286
|
+
githubAccessToken: string;
|
|
287
|
+
commitMessage?: string;
|
|
288
|
+
}): Promise<{
|
|
289
|
+
status: "committing" | "not-awaiting-review";
|
|
290
|
+
}>;
|
|
249
291
|
resetConfigOverrideKeys(level: "branch" | "environment", keys: string[]): Promise<void>;
|
|
250
292
|
createEmailTemplate(displayName: string): Promise<{
|
|
251
293
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-interface.d.ts","names":[],"sources":["../../../src/interface/admin-interface.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"admin-interface.d.ts","names":[],"sources":["../../../src/interface/admin-interface.ts"],"mappings":"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"}
|
|
@@ -350,6 +350,61 @@ var HexclaveAdminInterface = class extends HexclaveServerInterface {
|
|
|
350
350
|
async unlinkPushedConfigSource() {
|
|
351
351
|
await this.sendAdminRequest(`/internal/config/source`, { method: "DELETE" }, null);
|
|
352
352
|
}
|
|
353
|
+
/**
|
|
354
|
+
* Reads a specific config-agent run's state (or `null`) for the linked GitHub
|
|
355
|
+
* repo. Polled by the dashboard — using the id returned by `applyConfigViaAgent`
|
|
356
|
+
* — for live progress and the review diff. Runs are independent, so each is
|
|
357
|
+
* addressed by its own id rather than "the" run on the branch.
|
|
358
|
+
*/
|
|
359
|
+
async getConfigAgentRun(runId) {
|
|
360
|
+
return (await (await this.sendAdminRequest(`/internal/config/github/run?run_id=${encodeURIComponent(runId)}`, { method: "GET" }, null)).json()).agent_run ?? null;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Applies a dashboard config change to the linked GitHub repo by running the
|
|
364
|
+
* config agent in a sandbox (server-side). Returns immediately with the new run's
|
|
365
|
+
* `id`; poll `getConfigAgentRun(id)` for progress. The GitHub access token is the
|
|
366
|
+
* caller's own OAuth token and is used transiently server-side.
|
|
367
|
+
*/
|
|
368
|
+
async applyConfigViaAgent(options) {
|
|
369
|
+
return await (await this.sendAdminRequest(`/internal/config/github/apply`, {
|
|
370
|
+
method: "POST",
|
|
371
|
+
headers: { "content-type": "application/json" },
|
|
372
|
+
body: JSON.stringify({
|
|
373
|
+
github_access_token: options.githubAccessToken,
|
|
374
|
+
config_update_string: JSON.stringify(options.configUpdate)
|
|
375
|
+
})
|
|
376
|
+
}, null)).json();
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Cancels a specific in-flight agent-driven config write: hard-stops the sandbox
|
|
380
|
+
* so the agent stops mid-work. Also cancels runs in `awaiting_review`. No revert
|
|
381
|
+
* — if the agent already pushed, the commit stays. Returns `not-running` if the
|
|
382
|
+
* run is gone or already terminal.
|
|
383
|
+
*/
|
|
384
|
+
async cancelConfigAgentRun(runId) {
|
|
385
|
+
return await (await this.sendAdminRequest(`/internal/config/github/cancel`, {
|
|
386
|
+
method: "POST",
|
|
387
|
+
headers: { "content-type": "application/json" },
|
|
388
|
+
body: JSON.stringify({ run_id: runId })
|
|
389
|
+
}, null)).json();
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Commits a specific run's reviewed change to GitHub. Only valid when that run is in
|
|
393
|
+
* `awaiting_review` status; the change (diff + base commit) was captured at apply time
|
|
394
|
+
* and is rebuilt + pushed via the GitHub API here, so no live sandbox is involved.
|
|
395
|
+
* Returns `not-awaiting-review` if the run isn't in a committable state.
|
|
396
|
+
*/
|
|
397
|
+
async commitConfigAgentRun(runId, options) {
|
|
398
|
+
return await (await this.sendAdminRequest(`/internal/config/github/commit`, {
|
|
399
|
+
method: "POST",
|
|
400
|
+
headers: { "content-type": "application/json" },
|
|
401
|
+
body: JSON.stringify({
|
|
402
|
+
run_id: runId,
|
|
403
|
+
github_access_token: options.githubAccessToken,
|
|
404
|
+
...options.commitMessage ? { commit_message: options.commitMessage } : {}
|
|
405
|
+
})
|
|
406
|
+
}, null)).json();
|
|
407
|
+
}
|
|
353
408
|
async resetConfigOverrideKeys(level, keys) {
|
|
354
409
|
await this.sendAdminRequest(`/internal/config/override/${level}/reset-keys`, {
|
|
355
410
|
method: "POST",
|