@lunora/config 1.0.0-alpha.35 → 1.0.0-alpha.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/index.mjs CHANGED
@@ -12,7 +12,7 @@ export { LUNORA_CONFIG_FILE, interpretRemote, readProjectRemotePreference } from
12
12
  export { createConfirm, isInteractive, promptMultiSelect, promptSelect, promptYesNo } from './packem_shared/createConfirm-fvpdgJ9s.mjs';
13
13
  export { reconcileWranglerBindings } from './packem_shared/reconcileWranglerBindings-Cruwwx1M.mjs';
14
14
  export { REMOTE_ELIGIBLE_KEYS, injectRemoteFlags, isRemoteEnvEnabled, materializeRemoteWranglerConfig, planRemoteBindings, resolveRemoteEnabled } from './packem_shared/REMOTE_ELIGIBLE_KEYS-BC7_e9Bz.mjs';
15
- export { buildPackageSecretsBlock, ensureDevVariables, ensureDevVarsExample, fillDevSecrets, generateSecretValue, isMintableSecretKey, isPlaceholderValue, planDevSecretsFill, planDevVariablesAugment, planDevVariablesScaffold, requiredSecrets } from './packem_shared/buildPackageSecretsBlock-DWDKHViT.mjs';
15
+ export { buildPackageSecretsBlock, ensureDevVariables, ensureDevVarsExample, fillDevSecrets, generateSecretValue, isMintableSecretKey, isPlaceholderValue, planDevSecretsFill, planDevVariablesAugment, planDevVariablesScaffold, requiredSecrets } from './packem_shared/buildPackageSecretsBlock-BCHtfQ2A.mjs';
16
16
  export { applyAdditiveEdit, classifyEdit } from './packem_shared/applyAdditiveEdit-C-snTFEV.mjs';
17
17
  export { parseSchema } from './packem_shared/parseSchema-DSeyktvG.mjs';
18
18
  export { classifyPolicyEdit, scaffoldPolicyFile, wireRlsIntoProcedure } from './packem_shared/classifyPolicyEdit-BHeAqF8P.mjs';
@@ -1,7 +1,7 @@
1
1
  import { randomBytes } from 'node:crypto';
2
- import { existsSync, readFileSync, writeFileSync, renameSync, rmSync } from 'node:fs';
2
+ import { existsSync, readFileSync, writeFileSync, renameSync, rmSync, statSync } from 'node:fs';
3
3
  import { join } from 'node:path';
4
- import { DEV_VARS_FILE, DEV_VARS_EXAMPLE_FILE, parseDevVariableEntries, DEV_VARS_NEWLINE, splitDevVariableLine, unquoteDevVariable } from './DEV_VARS_EXAMPLE_FILE-dJPNTEnK.mjs';
4
+ import { DEV_VARS_FILE, DEV_VARS_EXAMPLE_FILE, splitDevVariableLine, parseDevVariableEntries, DEV_VARS_NEWLINE, unquoteDevVariable } from './DEV_VARS_EXAMPLE_FILE-dJPNTEnK.mjs';
5
5
  import { CORE_SECRETS, PACKAGE_SECRETS_REGISTRY, secretsForPackages } from './PACKAGE_SECRETS_REGISTRY-B8t_SdoZ.mjs';
6
6
 
7
7
  const requiredSecrets = (packageNames) => [...CORE_SECRETS, ...secretsForPackages(packageNames)];
@@ -104,19 +104,48 @@ const atomicCreateDevVariables = (path, content) => {
104
104
  throw error;
105
105
  }
106
106
  };
107
- const appendDevVariables = (path, additions) => {
108
- const existing = readFileSync(path, "utf8");
109
- const separator = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
110
- const content = `${existing}${separator}${additions.join("\n")}
111
- `;
112
- const temporaryPath = `${path}.tmp-${String(process.pid)}`;
107
+ const APPEND_MAX_ATTEMPTS = 5;
108
+ const fingerprintOf = (path) => {
113
109
  try {
114
- writeFileSync(temporaryPath, content, { encoding: "utf8", mode: 384 });
115
- renameSync(temporaryPath, path);
116
- } catch (error) {
117
- rmSync(temporaryPath, { force: true });
118
- throw error;
110
+ const stats = statSync(path);
111
+ return { mtimeMs: stats.mtimeMs, size: stats.size };
112
+ } catch {
113
+ return void 0;
114
+ }
115
+ };
116
+ const sameFingerprint = (a, b) => {
117
+ if (a === void 0 || b === void 0) {
118
+ return false;
119
+ }
120
+ return a.mtimeMs === b.mtimeMs && a.size === b.size;
121
+ };
122
+ const appendDevVariables = (path, buildAdditions) => {
123
+ for (let attempt = 0; attempt < APPEND_MAX_ATTEMPTS; attempt += 1) {
124
+ const beforeFingerprint = fingerprintOf(path);
125
+ const existing = readFileSync(path, "utf8");
126
+ const additions = buildAdditions(existing);
127
+ if (additions.length === 0) {
128
+ return [];
129
+ }
130
+ const separator = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
131
+ const content = `${existing}${separator}${additions.join("\n")}
132
+ `;
133
+ const temporaryPath = `${path}.tmp-${String(process.pid)}-${randomBytes(6).toString("hex")}`;
134
+ try {
135
+ writeFileSync(temporaryPath, content, { encoding: "utf8", mode: 384 });
136
+ const beforeRenameFingerprint = fingerprintOf(path);
137
+ if (!sameFingerprint(beforeFingerprint, beforeRenameFingerprint)) {
138
+ rmSync(temporaryPath, { force: true });
139
+ continue;
140
+ }
141
+ renameSync(temporaryPath, path);
142
+ return additions;
143
+ } catch (error) {
144
+ rmSync(temporaryPath, { force: true });
145
+ throw error;
146
+ }
119
147
  }
148
+ throw new Error(`Failed to append to ${path} after ${String(APPEND_MAX_ATTEMPTS)} attempts — a concurrent writer kept winning the race.`);
120
149
  };
121
150
  const ensureDevVariables = async (deps) => {
122
151
  const devVariablesPath = join(deps.cwd, DEV_VARS_FILE);
@@ -152,9 +181,17 @@ const ensureDevVariables = async (deps) => {
152
181
  deps.info(`Skipped — add ${list} to ${DEV_VARS_FILE} when you're ready.`);
153
182
  return { addedKeys: [], generatedKeys: [], status: "declined" };
154
183
  }
155
- appendDevVariables(devVariablesPath, augment.additions);
156
- deps.info(`Updated ${DEV_VARS_FILE} — added ${list}${generatedSuffix(augment.generatedKeys)}.`);
157
- return { addedKeys: augment.missingKeys, generatedKeys: augment.generatedKeys, status: "augmented" };
184
+ const written = appendDevVariables(
185
+ devVariablesPath,
186
+ (currentContent) => planDevVariablesAugment({ existingContent: currentContent, exampleContent, randomHex: deps.randomHex }).additions
187
+ );
188
+ const writtenKeys = written.map((line) => splitDevVariableLine(line)?.key).filter((key) => key !== void 0);
189
+ if (writtenKeys.length === 0) {
190
+ return { addedKeys: [], generatedKeys: [], status: "exists" };
191
+ }
192
+ const writtenGeneratedKeys = augment.generatedKeys.filter((key) => writtenKeys.includes(key));
193
+ deps.info(`Updated ${DEV_VARS_FILE} — added ${writtenKeys.join(", ")}${generatedSuffix(writtenGeneratedKeys)}.`);
194
+ return { addedKeys: writtenKeys, generatedKeys: writtenGeneratedKeys, status: "augmented" };
158
195
  };
159
196
  const secretEntryBlock = (entry) => {
160
197
  const lines = [`# ${entry.description}`, `# Docs: ${entry.docsUrl}`, `${entry.key}="${entry.placeholderValue}"`];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/config",
3
- "version": "1.0.0-alpha.35",
3
+ "version": "1.0.0-alpha.37",
4
4
  "description": "Internal shared CLI + Vite config layer for Lunora: wrangler.jsonc validation, binding inference, and .dev.vars scaffolding",
5
5
  "keywords": [
6
6
  "bindings",
@@ -50,9 +50,9 @@
50
50
  "access": "public"
51
51
  },
52
52
  "dependencies": {
53
- "@lunora/codegen": "1.0.0-alpha.20",
53
+ "@lunora/codegen": "1.0.0-alpha.22",
54
54
  "@lunora/container": "1.0.0-alpha.5",
55
- "@lunora/seed": "1.0.0-alpha.8",
55
+ "@lunora/seed": "1.0.0-alpha.10",
56
56
  "@visulima/colorize": "2.0.0-alpha.14",
57
57
  "dockerode": "^5.0.1",
58
58
  "es-module-lexer": "^2.1.0",
@@ -60,7 +60,7 @@
60
60
  "ts-morph": "^28.0.0"
61
61
  },
62
62
  "peerDependencies": {
63
- "@lunora/studio": "1.0.0-alpha.21"
63
+ "@lunora/studio": "1.0.0-alpha.23"
64
64
  },
65
65
  "peerDependenciesMeta": {
66
66
  "@lunora/studio": {