@lunora/config 1.0.0-alpha.7 → 1.0.0-alpha.71

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.
Files changed (39) hide show
  1. package/LICENSE.md +6 -0
  2. package/__assets__/package-og.svg +1 -1
  3. package/dist/index.d.mts +885 -461
  4. package/dist/index.d.ts +885 -461
  5. package/dist/index.mjs +18 -10
  6. package/dist/packem_shared/AGENT_MODE_ENV-B5R7jpY7.mjs +17 -0
  7. package/dist/packem_shared/DEV_DAEMON_ENV-FIEtW7Fz.mjs +163 -0
  8. package/dist/packem_shared/{LUNORA_EVENT_SOURCE-D2fDeGB6.mjs → LUNORA_EVENT_SOURCE-M1KM9_pR.mjs} +28 -2
  9. package/dist/packem_shared/{PACKAGE_SECRETS_REGISTRY-CySy5vR_.mjs → PACKAGE_SECRETS_REGISTRY-BeRSvrl5.mjs} +45 -1
  10. package/dist/packem_shared/{POLICY_SCAFFOLD_ENDPOINT-CiC2IGKx.mjs → POLICY_SCAFFOLD_ENDPOINT-CvTZpaZF.mjs} +7 -10
  11. package/dist/packem_shared/{REMOTE_ELIGIBLE_KEYS-BC7_e9Bz.mjs → REMOTE_ELIGIBLE_KEYS-C-F_Flgy.mjs} +1 -6
  12. package/dist/packem_shared/{REQUIRED_COMPATIBILITY_DATE-ycqievRl.mjs → REQUIRED_COMPATIBILITY_DATE-DP0SFVy9.mjs} +135 -4
  13. package/dist/packem_shared/{SCHEMA_EDIT_ENDPOINT-Df-Wrix-.mjs → SCHEMA_EDIT_ENDPOINT-Bt2SNkpV.mjs} +4 -8
  14. package/dist/packem_shared/{SEED_ENDPOINT-DVCjaGO-.mjs → SEED_ENDPOINT-7izz_Jao.mjs} +1 -1
  15. package/dist/packem_shared/WORKERS_CACHE_MIN_DATE-CaXbS7CX.mjs +11 -0
  16. package/dist/packem_shared/assetContentType-ByZjeV3D.mjs +72 -0
  17. package/dist/packem_shared/{buildPackageSecretsBlock-S74dgmwy.mjs → buildPackageSecretsBlock-B-dWb-Sa.mjs} +115 -21
  18. package/dist/packem_shared/collectWranglerSecretVariables-p4ezBYQ4.mjs +56 -0
  19. package/dist/packem_shared/{createConfirm-fvpdgJ9s.mjs → createConfirm-BMikeJC9.mjs} +22 -5
  20. package/dist/packem_shared/{detectFramework-Br-BcPBq.mjs → detectFramework-U08038Yp.mjs} +3 -3
  21. package/dist/packem_shared/discoverAgentInfo-eXA3hF4v.mjs +19 -0
  22. package/dist/packem_shared/{discoverSchemaInfo-DWtypqpP.mjs → discoverSchemaInfo-BB-CKlTK.mjs} +1 -1
  23. package/dist/packem_shared/{inferLunoraBindings-XNJH4pup.mjs → inferLunoraBindings-BcvvtpSG.mjs} +146 -51
  24. package/dist/packem_shared/jsonc-edit-SbcD7MAe.mjs +9 -0
  25. package/dist/packem_shared/parseDevVariable-B0FEvfHC.mjs +21 -0
  26. package/dist/packem_shared/{policy-scaffold.d-DCmwn7zQ.d.ts → policy-scaffold.d-CFd2FlqG.d.mts} +22 -22
  27. package/dist/packem_shared/{policy-scaffold.d-DCmwn7zQ.d.mts → policy-scaffold.d-CFd2FlqG.d.ts} +22 -22
  28. package/dist/packem_shared/{reconcileWranglerBindings-DTHmqTbL.mjs → reconcileWranglerBindings-2Cww5VLa.mjs} +114 -29
  29. package/dist/packem_shared/reconcileWranglerCompatibilityDate-C8c497ag.mjs +31 -0
  30. package/dist/packem_shared/reconcileWranglerCrons-DvsceVFz.mjs +29 -0
  31. package/dist/packem_shared/{serveJsonHandler-B4OLTGLS.mjs → serveJsonHandler-Cl2Wqkv8.mjs} +2 -2
  32. package/dist/packem_shared/streamContainerLogs-BZ4cOZwH.mjs +157 -0
  33. package/dist/packem_shared/write-atomic-B2REqapi.mjs +14 -0
  34. package/dist/studio-host/index.d.mts +134 -92
  35. package/dist/studio-host/index.d.ts +134 -92
  36. package/dist/studio-host/index.mjs +6 -6
  37. package/package.json +11 -8
  38. package/dist/packem_shared/loadStudioAssets-Csk5RS4E.mjs +0 -28
  39. package/dist/packem_shared/parseDevVariable-CJiq2IwE.mjs +0 -30
package/dist/index.mjs CHANGED
@@ -1,22 +1,30 @@
1
+ export { AGENT_MODE_ENV, detectAiAgent } from './packem_shared/AGENT_MODE_ENV-B5R7jpY7.mjs';
2
+ export { discoverAgentInfo } from './packem_shared/discoverAgentInfo-eXA3hF4v.mjs';
1
3
  export { AGENT_RULES_DIR, AGENT_RULES_HINT, AGENT_RULES_HINT_ENV, LUNORA_SKILL_NAMES, ROOT_SKILL_NAME, claimAgentRulesHint, detectAgentRules } from './packem_shared/AGENT_RULES_DIR-lcgC08aE.mjs';
2
4
  export { discoverContainerInfo } from './packem_shared/discoverContainerInfo-BXFs6Wav.mjs';
3
- export { detectFramework } from './packem_shared/detectFramework-Br-BcPBq.mjs';
5
+ export { streamContainerLogs } from './packem_shared/streamContainerLogs-BZ4cOZwH.mjs';
6
+ export { detectFramework, readProjectDependencyNames } from './packem_shared/detectFramework-U08038Yp.mjs';
7
+ export { DEV_DAEMON_ENV, DEV_HANDOFF_ENV, DEV_LOG_FILE, DEV_LOG_FILE_ENV, DEV_STATE_DIR, DEV_STATE_FILE, claimDevServerState, clearDevServerState, isProcessAlive, isRecordedProcessCurrent, readDevServerState, readLiveDevServerState, updateDevServerState, writeDevServerState } from './packem_shared/DEV_DAEMON_ENV-FIEtW7Fz.mjs';
4
8
  export { DEV_VARS_EXAMPLE_FILE, DEV_VARS_FILE, DEV_VARS_KEY_PATTERN, parseDevVariableEntries } from './packem_shared/DEV_VARS_EXAMPLE_FILE-dJPNTEnK.mjs';
5
- export { inferLunoraBindings, packageNamesFromBindings } from './packem_shared/inferLunoraBindings-XNJH4pup.mjs';
9
+ export { inferLunoraBindings, packageNamesFromBindings } from './packem_shared/inferLunoraBindings-BcvvtpSG.mjs';
6
10
  export { LINKED_PROJECT_DIR, LINKED_PROJECT_FILE, readLinkedProject, writeLinkedProject } from './packem_shared/LINKED_PROJECT_DIR-CXwXzV_C.mjs';
7
- export { LUNORA_EVENT_SOURCE, formatLunoraEvent } from './packem_shared/LUNORA_EVENT_SOURCE-D2fDeGB6.mjs';
11
+ export { LUNORA_EVENT_SOURCE, formatLunoraEvent } from './packem_shared/LUNORA_EVENT_SOURCE-M1KM9_pR.mjs';
8
12
  export { default as LunoraReporter } from './packem_shared/LunoraReporter-Ci-bDCK9.mjs';
9
- export { PACKAGE_SECRETS_REGISTRY, secretsForPackages } from './packem_shared/PACKAGE_SECRETS_REGISTRY-CySy5vR_.mjs';
13
+ export { PACKAGE_SECRETS_REGISTRY, secretsForPackages } from './packem_shared/PACKAGE_SECRETS_REGISTRY-BeRSvrl5.mjs';
10
14
  export { LUNORA_CONFIG_FILE, interpretRemote, readProjectRemotePreference } from './packem_shared/LUNORA_CONFIG_FILE-CtcIcB5-.mjs';
11
- export { createConfirm, isInteractive, promptMultiSelect, promptSelect, promptYesNo } from './packem_shared/createConfirm-fvpdgJ9s.mjs';
12
- export { reconcileWranglerBindings } from './packem_shared/reconcileWranglerBindings-DTHmqTbL.mjs';
13
- export { REMOTE_ELIGIBLE_KEYS, injectRemoteFlags, isRemoteEnvEnabled, materializeRemoteWranglerConfig, planRemoteBindings, resolveRemoteEnabled } from './packem_shared/REMOTE_ELIGIBLE_KEYS-BC7_e9Bz.mjs';
14
- export { buildPackageSecretsBlock, ensureDevVariables, ensureDevVarsExample, isPlaceholderValue, planDevVariablesAugment, planDevVariablesScaffold } from './packem_shared/buildPackageSecretsBlock-S74dgmwy.mjs';
15
+ export { createConfirm, isInteractive, promptMultiSelect, promptSelect, promptText, promptYesNo } from './packem_shared/createConfirm-BMikeJC9.mjs';
16
+ export { reconcileWranglerBindings } from './packem_shared/reconcileWranglerBindings-2Cww5VLa.mjs';
17
+ export { reconcileWranglerCompatibilityDate } from './packem_shared/reconcileWranglerCompatibilityDate-C8c497ag.mjs';
18
+ export { reconcileWranglerCrons } from './packem_shared/reconcileWranglerCrons-DvsceVFz.mjs';
19
+ export { REMOTE_ELIGIBLE_KEYS, injectRemoteFlags, isRemoteEnvEnabled, materializeRemoteWranglerConfig, planRemoteBindings, resolveRemoteEnabled } from './packem_shared/REMOTE_ELIGIBLE_KEYS-C-F_Flgy.mjs';
20
+ export { buildPackageSecretsBlock, ensureDevVariables, ensureDevVarsExample, fillDevSecrets, generateSecretValue, isMintableSecretKey, isPlaceholderValue, planDevSecretsFill, planDevVariablesAugment, planDevVariablesScaffold, requiredSecrets } from './packem_shared/buildPackageSecretsBlock-B-dWb-Sa.mjs';
15
21
  export { applyAdditiveEdit, classifyEdit } from './packem_shared/applyAdditiveEdit-C-snTFEV.mjs';
16
22
  export { parseSchema } from './packem_shared/parseSchema-DSeyktvG.mjs';
17
23
  export { classifyPolicyEdit, scaffoldPolicyFile, wireRlsIntoProcedure } from './packem_shared/classifyPolicyEdit-BHeAqF8P.mjs';
18
- export { discoverSchemaInfo } from './packem_shared/discoverSchemaInfo-DWtypqpP.mjs';
24
+ export { discoverSchemaInfo } from './packem_shared/discoverSchemaInfo-BB-CKlTK.mjs';
19
25
  export { ACCENT, BADGES, BADGE_COLUMN_WIDTH, LUNA_ART, LUNA_BUNNY, LUNA_NAME, LUNA_SIGNOFF, STEP_BADGE_NAMES, badgeLead, badgeWidth, padBadge, paintAnswer, paintBadge } from './packem_shared/ACCENT-DW1XJn8i.mjs';
26
+ export { WORKERS_CACHE_MIN_DATE, isCacheEnabled } from './packem_shared/WORKERS_CACHE_MIN_DATE-CaXbS7CX.mjs';
20
27
  export { discoverWorkflowInfo } from './packem_shared/discoverWorkflowInfo-CedvR0mn.mjs';
21
28
  export { WRANGLER_FILES, findWranglerFile, readWranglerJsonc } from './packem_shared/WRANGLER_FILES-DwSuC-Kn.mjs';
22
- export { REQUIRED_COMPATIBILITY_DATE, REQUIRED_FLAG, validateWrangler, validateWranglerConfig, validateWranglerProject, withTailConsumer } from './packem_shared/REQUIRED_COMPATIBILITY_DATE-ycqievRl.mjs';
29
+ export { collectWranglerSecretVariables, scanWranglerVariablesForSecrets } from './packem_shared/collectWranglerSecretVariables-p4ezBYQ4.mjs';
30
+ export { REQUIRED_COMPATIBILITY_DATE, REQUIRED_FLAG, validateWrangler, validateWranglerConfig, validateWranglerProject, withTailConsumer } from './packem_shared/REQUIRED_COMPATIBILITY_DATE-DP0SFVy9.mjs';
@@ -0,0 +1,17 @@
1
+ import { detectAiSession } from '@visulima/find-ai-runner';
2
+
3
+ const AGENT_MODE_ENV = "LUNORA_AGENT_MODE";
4
+ const isDisabled = (value) => {
5
+ const normalized = value.trim().toLowerCase();
6
+ return normalized === "0" || normalized === "false";
7
+ };
8
+ const detectAiAgent = (env = process.env) => {
9
+ const override = env[AGENT_MODE_ENV];
10
+ if (override !== void 0 && override !== "") {
11
+ return isDisabled(override) ? void 0 : { name: "agent (forced)", variable: AGENT_MODE_ENV };
12
+ }
13
+ const session = detectAiSession(env);
14
+ return session === void 0 ? void 0 : { name: session.agent, variable: session.signal };
15
+ };
16
+
17
+ export { AGENT_MODE_ENV, detectAiAgent };
@@ -0,0 +1,163 @@
1
+ import { mkdirSync, writeFileSync, rmSync, existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ const DEV_STATE_DIR = ".lunora";
5
+ const DEV_STATE_FILE = join(DEV_STATE_DIR, "dev.json");
6
+ const DEV_LOG_FILE = join(DEV_STATE_DIR, "dev.log");
7
+ const DEV_DAEMON_ENV = "LUNORA_DEV_DAEMON";
8
+ const DEV_LOG_FILE_ENV = "LUNORA_DEV_LOG_FILE";
9
+ const DEV_HANDOFF_ENV = "LUNORA_DEV_HANDOFF_PID";
10
+ const isObject = (value) => value !== null && typeof value === "object";
11
+ const stringField = (record, key) => {
12
+ const value = record[key];
13
+ return typeof value === "string" && value.length > 0 ? value : void 0;
14
+ };
15
+ const isProcessAlive = (pid) => {
16
+ if (!Number.isInteger(pid) || pid <= 0) {
17
+ return false;
18
+ }
19
+ try {
20
+ process.kill(pid, 0);
21
+ return true;
22
+ } catch {
23
+ return false;
24
+ }
25
+ };
26
+ const START_TIME_SKEW_MS = 1e4;
27
+ const LINUX_CLOCK_TICKS_PER_SECOND = 100;
28
+ const processStartTimeMs = (pid) => {
29
+ if (process.platform !== "linux") {
30
+ return void 0;
31
+ }
32
+ try {
33
+ const stat = readFileSync(`/proc/${String(pid)}/stat`, "utf8");
34
+ const fields = stat.slice(stat.lastIndexOf(")") + 2).split(" ");
35
+ const startTicks = Number(fields[19]);
36
+ const bootLine = readFileSync("/proc/stat", "utf8").split("\n").find((line) => line.startsWith("btime "));
37
+ const bootSeconds = Number(bootLine?.slice("btime ".length));
38
+ if (!Number.isFinite(startTicks) || !Number.isFinite(bootSeconds)) {
39
+ return void 0;
40
+ }
41
+ return bootSeconds * 1e3 + startTicks / LINUX_CLOCK_TICKS_PER_SECOND * 1e3;
42
+ } catch {
43
+ return void 0;
44
+ }
45
+ };
46
+ const isRecordedProcessCurrent = (state) => {
47
+ if (!isProcessAlive(state.pid)) {
48
+ return false;
49
+ }
50
+ const recordedAtMs = state.startedAt === void 0 ? Number.NaN : Date.parse(state.startedAt);
51
+ if (Number.isNaN(recordedAtMs)) {
52
+ return true;
53
+ }
54
+ const actualStartMs = processStartTimeMs(state.pid);
55
+ return actualStartMs === void 0 || actualStartMs <= recordedAtMs + START_TIME_SKEW_MS;
56
+ };
57
+ const readDevServerState = (projectRoot) => {
58
+ const path = join(projectRoot, DEV_STATE_FILE);
59
+ if (!existsSync(path)) {
60
+ return void 0;
61
+ }
62
+ let parsed;
63
+ try {
64
+ parsed = JSON.parse(readFileSync(path, "utf8"));
65
+ } catch {
66
+ return void 0;
67
+ }
68
+ if (!isObject(parsed) || typeof parsed["pid"] !== "number") {
69
+ return void 0;
70
+ }
71
+ const url = stringField(parsed, "url");
72
+ if (url === void 0) {
73
+ return void 0;
74
+ }
75
+ return {
76
+ background: parsed["background"] === true,
77
+ logFile: stringField(parsed, "logFile"),
78
+ mode: parsed["mode"] === "vite" ? "vite" : "cli",
79
+ pid: parsed["pid"],
80
+ startedAt: stringField(parsed, "startedAt"),
81
+ studioUrl: stringField(parsed, "studioUrl"),
82
+ url
83
+ };
84
+ };
85
+ const writeDevServerState = (projectRoot, state) => {
86
+ try {
87
+ mkdirSync(join(projectRoot, DEV_STATE_DIR), { recursive: true });
88
+ const path = join(projectRoot, DEV_STATE_FILE);
89
+ writeFileSync(path, `${JSON.stringify(state, void 0, 2)}
90
+ `, "utf8");
91
+ return path;
92
+ } catch {
93
+ return void 0;
94
+ }
95
+ };
96
+ const updateDevServerState = (projectRoot, patch) => {
97
+ const existing = readDevServerState(projectRoot);
98
+ if (existing === void 0) {
99
+ return void 0;
100
+ }
101
+ const merged = { ...existing, ...patch };
102
+ writeDevServerState(projectRoot, merged);
103
+ return merged;
104
+ };
105
+ const clearDevServerState = (projectRoot, expectedPid) => {
106
+ try {
107
+ if (expectedPid !== void 0) {
108
+ const current = readDevServerState(projectRoot);
109
+ if (current !== void 0 && current.pid !== expectedPid) {
110
+ return;
111
+ }
112
+ }
113
+ rmSync(join(projectRoot, DEV_STATE_FILE), { force: true });
114
+ } catch {
115
+ }
116
+ };
117
+ const readLiveDevServerState = (projectRoot) => {
118
+ const state = readDevServerState(projectRoot);
119
+ if (state === void 0) {
120
+ return void 0;
121
+ }
122
+ if (isRecordedProcessCurrent(state)) {
123
+ return state;
124
+ }
125
+ clearDevServerState(projectRoot, state.pid);
126
+ return void 0;
127
+ };
128
+ const resolveClaimConflict = (projectRoot, state, supersedePid) => {
129
+ const existing = readLiveDevServerState(projectRoot);
130
+ if (existing === void 0) {
131
+ return void 0;
132
+ }
133
+ if (supersedePid !== void 0 && existing.pid === supersedePid) {
134
+ return { ok: writeDevServerState(projectRoot, state) !== void 0 };
135
+ }
136
+ if (existing.pid !== state.pid) {
137
+ return { existing, ok: false };
138
+ }
139
+ return { ok: writeDevServerState(projectRoot, state) !== void 0 };
140
+ };
141
+ const claimDevServerState = (projectRoot, state, options) => {
142
+ const path = join(projectRoot, DEV_STATE_FILE);
143
+ const payload = `${JSON.stringify(state, void 0, 2)}
144
+ `;
145
+ for (let attempt = 0; attempt < 2; attempt += 1) {
146
+ try {
147
+ mkdirSync(join(projectRoot, DEV_STATE_DIR), { recursive: true });
148
+ writeFileSync(path, payload, { encoding: "utf8", flag: "wx" });
149
+ return { ok: true };
150
+ } catch (error) {
151
+ if (error.code !== "EEXIST") {
152
+ return { ok: writeDevServerState(projectRoot, state) !== void 0 };
153
+ }
154
+ const resolved = resolveClaimConflict(projectRoot, state, options?.supersedePid);
155
+ if (resolved !== void 0) {
156
+ return resolved;
157
+ }
158
+ }
159
+ }
160
+ return { ok: writeDevServerState(projectRoot, state) !== void 0 };
161
+ };
162
+
163
+ export { DEV_DAEMON_ENV, DEV_HANDOFF_ENV, DEV_LOG_FILE, DEV_LOG_FILE_ENV, DEV_STATE_DIR, DEV_STATE_FILE, claimDevServerState, clearDevServerState, isProcessAlive, isRecordedProcessCurrent, readDevServerState, readLiveDevServerState, updateDevServerState, writeDevServerState };
@@ -1,10 +1,27 @@
1
+ const stringifyFieldValue = (value) => {
2
+ if (typeof value === "string") {
3
+ return value;
4
+ }
5
+ try {
6
+ return JSON.stringify(value) ?? String(value);
7
+ } catch {
8
+ return String(value);
9
+ }
10
+ };
11
+ const formatLogFields = (fields) => {
12
+ if (typeof fields !== "object" || fields === null || Array.isArray(fields)) {
13
+ return "";
14
+ }
15
+ return Object.entries(fields).map(([key, value]) => `${key}=${stringifyFieldValue(value)}`).join(" ");
16
+ };
17
+
1
18
  const LUNORA_EVENT_SOURCE = "lunora";
2
19
  const ROOT_SHARD_NAME = "__root__";
3
20
  const asString = (value) => typeof value === "string" ? value : "";
4
21
  const asStringList = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
5
22
  const formatDuration = (value) => typeof value === "number" && Number.isFinite(value) ? `${String(Math.round(value))}ms` : "?ms";
6
23
  const toLineLevel = (rawLevel) => {
7
- if (rawLevel === "error") {
24
+ if (rawLevel === "error" || rawLevel === "fatal") {
8
25
  return "error";
9
26
  }
10
27
  if (rawLevel === "warn") {
@@ -72,7 +89,16 @@ const formatLunoraEvent = (line) => {
72
89
  }
73
90
  const functionPath = asString(event.function) || "<unknown>";
74
91
  if (event.type === "log") {
75
- return { kind: "log", level: toLineLevel(asString(event.level)), text: `${labelFor(functionPath, event)} ${asString(event.message)}`.trimEnd() };
92
+ const parts = [`${labelFor(functionPath, event)} ${asString(event.message)}`.trimEnd()];
93
+ const fields = formatLogFields(event.fields);
94
+ if (fields !== "") {
95
+ parts.push(fields);
96
+ }
97
+ const traceId = asString(event.traceId);
98
+ if (traceId !== "") {
99
+ parts.push(`trace=${traceId.slice(0, 8)}`);
100
+ }
101
+ return { kind: "log", level: toLineLevel(asString(event.level)), text: parts.join(" ") };
76
102
  }
77
103
  if (event.type === "request") {
78
104
  return formatRequest(event, functionPath);
@@ -1,3 +1,11 @@
1
+ const CORE_SECRETS = [
2
+ {
3
+ description: "Bearer token the local Lunora Studio uses to call the worker's admin endpoints (data browser, schema edits) in dev. Generate with: openssl rand -hex 32",
4
+ docsUrl: "https://lunora.sh/docs/packages/studio",
5
+ key: "LUNORA_ADMIN_TOKEN",
6
+ placeholderValue: "replace-with-openssl-rand-hex-32"
7
+ }
8
+ ];
1
9
  const PACKAGE_SECRETS_REGISTRY = {
2
10
  "@lunora/auth": [
3
11
  {
@@ -45,6 +53,42 @@ const PACKAGE_SECRETS_REGISTRY = {
45
53
  docsUrl: "https://lunora.sh/docs/packages/payment#polar",
46
54
  key: "POLAR_WEBHOOK_SECRET",
47
55
  placeholderValue: "<your-polar-webhook-secret>"
56
+ },
57
+ {
58
+ description: "Autumn secret key (am_sk_…) for the Autumn payment adapter. Obtain at https://app.useautumn.com/dev",
59
+ docsUrl: "https://lunora.sh/docs/packages/payment#autumn",
60
+ key: "AUTUMN_SECRET_KEY",
61
+ placeholderValue: "<your-autumn-secret-key>"
62
+ },
63
+ {
64
+ description: "Autumn webhook signing secret for verifying Standard Webhooks event payloads. Obtain from your Autumn dashboard webhook settings.",
65
+ docsUrl: "https://lunora.sh/docs/packages/payment#autumn",
66
+ key: "AUTUMN_WEBHOOK_SECRET",
67
+ placeholderValue: "<your-autumn-webhook-secret>"
68
+ },
69
+ {
70
+ description: "Dodo Payments API key (bearer token) for the Dodo Payments adapter. Obtain at https://app.dodopayments.com/developer/api-keys",
71
+ docsUrl: "https://lunora.sh/docs/packages/payment#dodo-payments",
72
+ key: "DODO_PAYMENTS_API_KEY",
73
+ placeholderValue: "<your-dodo-payments-api-key>"
74
+ },
75
+ {
76
+ description: "Dodo Payments webhook signing secret (whsec_…) for verifying event payloads. Obtain at https://app.dodopayments.com/developer/webhooks",
77
+ docsUrl: "https://lunora.sh/docs/packages/payment#dodo-payments",
78
+ key: "DODO_PAYMENTS_WEBHOOK_KEY",
79
+ placeholderValue: "<your-dodo-payments-webhook-secret>"
80
+ },
81
+ {
82
+ description: "Creem API key for the Creem payment adapter. Obtain at https://www.creem.io/dashboard/developers",
83
+ docsUrl: "https://lunora.sh/docs/packages/payment#creem",
84
+ key: "CREEM_API_KEY",
85
+ placeholderValue: "<your-creem-api-key>"
86
+ },
87
+ {
88
+ description: "Creem webhook signing secret for verifying the creem-signature header. Obtain from your Creem dashboard webhook settings.",
89
+ docsUrl: "https://lunora.sh/docs/packages/payment#creem",
90
+ key: "CREEM_WEBHOOK_SECRET",
91
+ placeholderValue: "<your-creem-webhook-secret>"
48
92
  }
49
93
  ]
50
94
  };
@@ -59,4 +103,4 @@ const secretsForPackages = (packageNames) => {
59
103
  return result;
60
104
  };
61
105
 
62
- export { PACKAGE_SECRETS_REGISTRY, secretsForPackages };
106
+ export { CORE_SECRETS, PACKAGE_SECRETS_REGISTRY, secretsForPackages };
@@ -1,7 +1,9 @@
1
- import { existsSync, readFileSync, writeFileSync, renameSync } from 'node:fs';
1
+ import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join, isAbsolute, relative } from 'node:path';
3
3
  import { runCodegen, CodegenDiagnosticError } from '@lunora/codegen';
4
+ import { LunoraError } from '@lunora/errors';
4
5
  import { classifyPolicyEdit, scaffoldPolicyFile, wireRlsIntoProcedure } from './classifyPolicyEdit-BHeAqF8P.mjs';
6
+ import { w as writeFileAtomic } from './write-atomic-B2REqapi.mjs';
5
7
 
6
8
  const POLICY_SCAFFOLD_ENDPOINT = "/__lunora/policy-scaffold";
7
9
  const statusForFailure = (reason) => {
@@ -13,11 +15,6 @@ const statusForFailure = (reason) => {
13
15
  }
14
16
  return reason === "destructive" ? 409 : 422;
15
17
  };
16
- const writeAtomic = (path, text) => {
17
- const temporaryPath = `${path}.lunora-tmp`;
18
- writeFileSync(temporaryPath, text, "utf8");
19
- renameSync(temporaryPath, path);
20
- };
21
18
  const runCodegenForResponse = (request, okBody) => {
22
19
  let diagnostics = [];
23
20
  try {
@@ -52,7 +49,7 @@ const handleScaffoldPolicy = (request, edit) => {
52
49
  if (existsSync(targetPath)) {
53
50
  return { body: { error: "file-exists", fileName: result.fileName, ok: false }, status: 409 };
54
51
  }
55
- writeAtomic(targetPath, result.source);
52
+ writeFileAtomic(targetPath, result.source);
56
53
  return runCodegenForResponse(request, { fileName: result.fileName });
57
54
  };
58
55
  const resolveProcedureFile = (projectRoot, lunoraDirectory, filePath) => {
@@ -76,7 +73,7 @@ const handleWireRls = (request, edit) => {
76
73
  if (!wired.ok) {
77
74
  return { body: { error: wired.reason, ok: false }, status: statusForFailure(wired.reason) };
78
75
  }
79
- writeAtomic(procedurePath, wired.text);
76
+ writeFileAtomic(procedurePath, wired.text);
80
77
  return runCodegenForResponse(request, { exportName: edit.exportName });
81
78
  };
82
79
  const handlePolicyScaffoldRequest = (request) => {
@@ -84,7 +81,7 @@ const handlePolicyScaffoldRequest = (request) => {
84
81
  return { body: { error: "method-not-allowed", ok: false }, status: 405 };
85
82
  }
86
83
  const { body } = request;
87
- if (body === void 0 || typeof body !== "object" || typeof body.kind !== "string") {
84
+ if (body === void 0 || body === null || typeof body !== "object" || typeof body.kind !== "string") {
88
85
  return { body: { error: "invalid-edit", ok: false }, status: 400 };
89
86
  }
90
87
  const edit = body;
@@ -97,7 +94,7 @@ const handlePolicyScaffoldRequest = (request) => {
97
94
  if (edit.kind === "wireRls") {
98
95
  return handleWireRls(request, edit);
99
96
  }
100
- throw new Error(`unhandled additive policy edit kind: ${String(edit.kind)}`);
97
+ throw new LunoraError("INTERNAL", `unhandled additive policy edit kind: ${String(edit.kind)}`);
101
98
  };
102
99
 
103
100
  export { POLICY_SCAFFOLD_ENDPOINT, handlePolicyScaffoldRequest };
@@ -1,9 +1,8 @@
1
1
  import { writeFileSync, rmSync } from 'node:fs';
2
- import { modify, applyEdits } from 'jsonc-parser';
2
+ import { a as applyModify } from './jsonc-edit-SbcD7MAe.mjs';
3
3
  import { findWranglerFile, readWranglerJsonc } from './WRANGLER_FILES-DwSuC-Kn.mjs';
4
4
  import { join } from 'node:path';
5
5
 
6
- const FORMATTING = { formattingOptions: { insertSpaces: true, tabSize: 4 } };
7
6
  const REMOTE_ELIGIBLE_KEYS = {
8
7
  ai: { label: "AI", shape: "object" },
9
8
  d1_databases: { label: "D1", shape: "array" },
@@ -38,10 +37,6 @@ const planSection = (section, parsed) => {
38
37
  return entry === null || entry === void 0 ? [] : [{ binding: entryName(entry, section), kind: label, path: [], section }];
39
38
  };
40
39
  const planRemoteBindings = (parsed) => REMOTE_ELIGIBLE_KEY_LIST.flatMap((section) => planSection(section, parsed));
41
- const applyModify = (text, path, value) => {
42
- const edits = modify(text, [...path], value, FORMATTING);
43
- return edits.length > 0 ? applyEdits(text, edits) : text;
44
- };
45
40
  const injectRemoteFlags = (text, plans) => {
46
41
  let next = text;
47
42
  for (const plan of plans) {
@@ -1,6 +1,7 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { dirname, join } from 'node:path';
3
- import { discoverSchemaInfo } from './discoverSchemaInfo-DWtypqpP.mjs';
3
+ import { discoverSchemaInfo } from './discoverSchemaInfo-BB-CKlTK.mjs';
4
+ import { isCacheEnabled, WORKERS_CACHE_MIN_DATE } from './WORKERS_CACHE_MIN_DATE-CaXbS7CX.mjs';
4
5
  import { findWranglerFile, readWranglerJsonc } from './WRANGLER_FILES-DwSuC-Kn.mjs';
5
6
 
6
7
  const REQUIRED_COMPATIBILITY_DATE = "2026-04-07";
@@ -76,6 +77,7 @@ const validateContainerEntry = (entry, label, checks) => {
76
77
  warnings.push(`${label} ("${entry.class_name}") declares no max_instances — set a cap so a traffic spike can't fan out unbounded container spend`);
77
78
  }
78
79
  };
80
+ const objectBindingEntries = (value) => Array.isArray(value) ? value.filter((entry) => entry !== null && typeof entry === "object") : [];
79
81
  const validateContainers = (wrangler, errors, warnings) => {
80
82
  if (wrangler.containers === void 0) {
81
83
  return;
@@ -88,7 +90,7 @@ const validateContainers = (wrangler, errors, warnings) => {
88
90
  if (entries.length === 0) {
89
91
  return;
90
92
  }
91
- const boundClasses = new Set((wrangler.durable_objects?.bindings ?? []).map((binding) => binding.class_name));
93
+ const boundClasses = new Set(objectBindingEntries(wrangler.durable_objects?.bindings).map((binding) => binding.class_name));
92
94
  const migrations = wrangler.migrations ?? [];
93
95
  const sqliteClasses = new Set(migrations.flatMap((migration) => [...migration?.new_sqlite_classes ?? []]));
94
96
  const nonSqliteClasses = new Set(migrations.flatMap((migration) => [...migration?.new_classes ?? []]));
@@ -127,6 +129,82 @@ const validateWorkflows = (wrangler, errors) => {
127
129
  }
128
130
  }
129
131
  };
132
+ const validateQueueProducers = (producers, errors) => {
133
+ if (producers === void 0) {
134
+ return;
135
+ }
136
+ if (!Array.isArray(producers)) {
137
+ errors.push("queues.producers must be an array of { binding, queue } entries");
138
+ return;
139
+ }
140
+ const entries = producers;
141
+ for (const [index, entry] of entries.entries()) {
142
+ const label = `queues.producers[${String(index)}]`;
143
+ if (!entry || typeof entry !== "object") {
144
+ errors.push(`${label} must be a { binding, queue } object`);
145
+ continue;
146
+ }
147
+ if (typeof entry.binding !== "string" || entry.binding.length === 0) {
148
+ errors.push(`${label} must have a non-empty "binding" naming the Queue producer (e.g. QUEUE_EMAIL)`);
149
+ }
150
+ if (typeof entry.queue !== "string" || entry.queue.length === 0) {
151
+ errors.push(`${label} must have a non-empty "queue" naming the deployed queue`);
152
+ }
153
+ }
154
+ };
155
+ const validateQueueConsumers = (consumers, errors) => {
156
+ if (consumers === void 0) {
157
+ return;
158
+ }
159
+ if (!Array.isArray(consumers)) {
160
+ errors.push("queues.consumers must be an array of { queue } entries");
161
+ return;
162
+ }
163
+ const entries = consumers;
164
+ for (const [index, entry] of entries.entries()) {
165
+ const label = `queues.consumers[${String(index)}]`;
166
+ if (!entry || typeof entry !== "object") {
167
+ errors.push(`${label} must be a { queue } object`);
168
+ continue;
169
+ }
170
+ if (typeof entry.queue !== "string" || entry.queue.length === 0) {
171
+ errors.push(`${label} must have a non-empty "queue" naming the consumed queue`);
172
+ }
173
+ }
174
+ };
175
+ const validateQueues = (wrangler, errors) => {
176
+ if (wrangler.queues === void 0) {
177
+ return;
178
+ }
179
+ if (typeof wrangler.queues !== "object" || Array.isArray(wrangler.queues)) {
180
+ errors.push("queues must be a { producers, consumers } object");
181
+ return;
182
+ }
183
+ validateQueueProducers(wrangler.queues.producers, errors);
184
+ validateQueueConsumers(wrangler.queues.consumers, errors);
185
+ };
186
+ const validateSecretsStore = (wrangler, errors) => {
187
+ if (wrangler.secrets_store_secrets === void 0) {
188
+ return;
189
+ }
190
+ if (!Array.isArray(wrangler.secrets_store_secrets)) {
191
+ errors.push("secrets_store_secrets must be an array of { binding, store_id, secret_name } entries");
192
+ return;
193
+ }
194
+ const entries = wrangler.secrets_store_secrets;
195
+ for (const [index, entry] of entries.entries()) {
196
+ const label = `secrets_store_secrets[${String(index)}]`;
197
+ if (!entry || typeof entry !== "object") {
198
+ errors.push(`${label} must be a { binding, store_id, secret_name } object`);
199
+ continue;
200
+ }
201
+ for (const field of ["binding", "store_id", "secret_name"]) {
202
+ if (typeof entry[field] !== "string" || entry[field].length === 0) {
203
+ errors.push(`${label} must have a non-empty "${field}"`);
204
+ }
205
+ }
206
+ }
207
+ };
130
208
  const isNonEmptyString = (value) => typeof value === "string" && value.length > 0;
131
209
  const asBindingEntries = (value) => value;
132
210
  const HINT_BINDING_RULES = [
@@ -137,6 +215,13 @@ const HINT_BINDING_RULES = [
137
215
  hintMessage: (label, binding) => `${label} ("${binding}") has no "id" — run \`wrangler kv namespace create\` and set the namespace id, or the binding can't resolve`,
138
216
  key: "kv_namespaces"
139
217
  },
218
+ {
219
+ arrayMessage: "flagship must be an array of { binding, app_id } entries",
220
+ bindingMessage: (label) => `${label} must have a non-empty "binding" naming the Flagship binding`,
221
+ hintField: "app_id",
222
+ hintMessage: (label, binding) => `${label} ("${binding}") has no "app_id" — create a Flagship app and set its id, or the binding can't resolve`,
223
+ key: "flagship"
224
+ },
140
225
  {
141
226
  arrayMessage: "hyperdrive must be an array of { binding, id } entries",
142
227
  bindingMessage: (label) => `${label} must have a non-empty "binding" naming the Hyperdrive binding`,
@@ -303,6 +388,45 @@ const validateObservability = (wrangler, errors) => {
303
388
  }
304
389
  }
305
390
  };
391
+ const validateCache = (wrangler, errors) => {
392
+ const { cache } = wrangler;
393
+ if (cache === void 0) {
394
+ return;
395
+ }
396
+ if (typeof cache !== "object" || cache === null || Array.isArray(cache)) {
397
+ errors.push('cache must be an object (e.g. { "enabled": true })');
398
+ return;
399
+ }
400
+ if (cache.enabled !== void 0 && typeof cache.enabled !== "boolean") {
401
+ errors.push("cache.enabled must be a boolean (true or false)");
402
+ }
403
+ };
404
+ const validateExports = (wrangler, errors) => {
405
+ const { exports } = wrangler;
406
+ if (exports === void 0) {
407
+ return;
408
+ }
409
+ if (typeof exports !== "object" || exports === null || Array.isArray(exports)) {
410
+ errors.push("exports must be an object keyed by entrypoint name");
411
+ return;
412
+ }
413
+ for (const [name, entry] of Object.entries(exports)) {
414
+ if (typeof entry !== "object" || entry === null) {
415
+ errors.push(`exports["${name}"] must be an object`);
416
+ continue;
417
+ }
418
+ if (entry.type !== void 0 && typeof entry.type !== "string") {
419
+ errors.push(`exports["${name}"].type must be a string`);
420
+ }
421
+ if (entry.cache !== void 0) {
422
+ if (typeof entry.cache !== "object" || entry.cache === null || Array.isArray(entry.cache)) {
423
+ errors.push(`exports["${name}"].cache must be an object`);
424
+ } else if (entry.cache.enabled !== void 0 && typeof entry.cache.enabled !== "boolean") {
425
+ errors.push(`exports["${name}"].cache.enabled must be a boolean`);
426
+ }
427
+ }
428
+ }
429
+ };
306
430
  const validateAssets = (wrangler, errors) => {
307
431
  const { assets } = wrangler;
308
432
  if (assets === void 0) {
@@ -372,7 +496,7 @@ const validateWranglerConfig = (wrangler, schema) => {
372
496
  errors.push("wrangler config is not a valid object");
373
497
  return { errors, valid: false, warnings };
374
498
  }
375
- const durableObjectBindings = wrangler.durable_objects?.bindings ?? [];
499
+ const durableObjectBindings = objectBindingEntries(wrangler.durable_objects?.bindings);
376
500
  const shardBinding = durableObjectBindings.find((binding) => binding.name === "SHARD" && binding.class_name === "ShardDO");
377
501
  if (!shardBinding) {
378
502
  errors.push(
@@ -385,8 +509,11 @@ const validateWranglerConfig = (wrangler, schema) => {
385
509
  } else if (compatibilityDate < REQUIRED_COMPATIBILITY_DATE) {
386
510
  errors.push(`compatibility_date must be >= "${REQUIRED_COMPATIBILITY_DATE}" (got "${compatibilityDate || "<missing>"}")`);
387
511
  }
512
+ if (isCacheEnabled(wrangler) && ISO_DATE_PATTERN.test(compatibilityDate) && compatibilityDate < WORKERS_CACHE_MIN_DATE) {
513
+ errors.push(`cache.enabled requires compatibility_date >= "${WORKERS_CACHE_MIN_DATE}" (got "${compatibilityDate || "<missing>"}")`);
514
+ }
388
515
  if (schema?.hasGlobalTable) {
389
- const d1Bindings = wrangler.d1_databases ?? [];
516
+ const d1Bindings = objectBindingEntries(wrangler.d1_databases);
390
517
  const databaseBinding = d1Bindings.find((binding) => binding.binding === "DB");
391
518
  if (!databaseBinding) {
392
519
  errors.push(
@@ -398,6 +525,8 @@ const validateWranglerConfig = (wrangler, schema) => {
398
525
  validateTailConsumers(wrangler, errors);
399
526
  validateContainers(wrangler, errors, warnings);
400
527
  validateWorkflows(wrangler, errors);
528
+ validateQueues(wrangler, errors);
529
+ validateSecretsStore(wrangler, errors);
401
530
  for (const rule of HINT_BINDING_RULES) {
402
531
  validateHintBinding(wrangler, rule, errors, warnings);
403
532
  }
@@ -412,6 +541,8 @@ const validateWranglerConfig = (wrangler, schema) => {
412
541
  validatePlacement(wrangler, errors);
413
542
  validateObservability(wrangler, errors);
414
543
  validateAssets(wrangler, errors);
544
+ validateCache(wrangler, errors);
545
+ validateExports(wrangler, errors);
415
546
  validateCorsVariables(wrangler, errors);
416
547
  return { errors, valid: errors.length === 0, warnings };
417
548
  };
@@ -1,7 +1,8 @@
1
- import { existsSync, readFileSync, writeFileSync, renameSync } from 'node:fs';
1
+ import { existsSync, readFileSync } from 'node:fs';
2
2
  import { runCodegen, CodegenDiagnosticError } from '@lunora/codegen';
3
3
  import { classifyEdit, applyAdditiveEdit } from './applyAdditiveEdit-C-snTFEV.mjs';
4
4
  import { parseSchema } from './parseSchema-DSeyktvG.mjs';
5
+ import { w as writeFileAtomic } from './write-atomic-B2REqapi.mjs';
5
6
  import { join } from 'node:path';
6
7
 
7
8
  const SCHEMA_EDIT_ENDPOINT = "/__lunora/schema-edit";
@@ -36,11 +37,6 @@ const readSchema = (schemaPath) => {
36
37
  }
37
38
  return { tables: parsed.tables };
38
39
  };
39
- const writeSchemaAtomic = (schemaPath, text) => {
40
- const temporaryPath = `${schemaPath}.lunora-tmp`;
41
- writeFileSync(temporaryPath, text, "utf8");
42
- renameSync(temporaryPath, schemaPath);
43
- };
44
40
  const needsMigrationResponse = (edit) => {
45
41
  return {
46
42
  body: {
@@ -54,7 +50,7 @@ const needsMigrationResponse = (edit) => {
54
50
  };
55
51
  const handlePost = (request, schemaPath) => {
56
52
  const edit = request.body;
57
- if (edit === void 0 || typeof edit !== "object" || typeof edit.kind !== "string") {
53
+ if (edit === void 0 || edit === null || typeof edit !== "object" || typeof edit.kind !== "string") {
58
54
  return { body: { error: "invalid-edit", ok: false }, status: 400 };
59
55
  }
60
56
  if (classifyEdit(edit) === "destructive") {
@@ -67,7 +63,7 @@ const handlePost = (request, schemaPath) => {
67
63
  if (!applied.ok) {
68
64
  return { body: { error: applied.reason, ok: false }, status: statusForFailure(applied.reason) };
69
65
  }
70
- writeSchemaAtomic(schemaPath, applied.text);
66
+ writeFileAtomic(schemaPath, applied.text);
71
67
  let diagnostics = [];
72
68
  try {
73
69
  runCodegen({ lunoraDirectory: request.schemaDirectory ?? "lunora", projectRoot: request.projectRoot });
@@ -26,7 +26,7 @@ const handleSeedRequest = (request) => {
26
26
  return { body: { error: "method-not-allowed", ok: false }, status: 405 };
27
27
  }
28
28
  const { body } = request;
29
- if (body === void 0 || typeof body !== "object") {
29
+ if (body === void 0 || body === null || typeof body !== "object") {
30
30
  return { body: { error: "invalid-request", ok: false }, status: 400 };
31
31
  }
32
32
  const { count, existingIds, seed, table } = body;