@camstack/server 1.1.54 → 1.1.56

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.
@@ -74,6 +74,9 @@ var require_dist = __commonJS({
74
74
  var src_exports = {};
75
75
  __export2(src_exports, {
76
76
  AGENT_ROOT_SPEC: /* @__PURE__ */ __name(() => AGENT_ROOT_SPEC, "AGENT_ROOT_SPEC"),
77
+ DEV_UPLOADS_DIRNAME: /* @__PURE__ */ __name(() => DEV_UPLOADS_DIRNAME, "DEV_UPLOADS_DIRNAME"),
78
+ DEV_UPLOADS_KEEP_COUNT: /* @__PURE__ */ __name(() => DEV_UPLOADS_KEEP_COUNT, "DEV_UPLOADS_KEEP_COUNT"),
79
+ DEV_UPLOAD_MANIFEST_FILE: /* @__PURE__ */ __name(() => DEV_UPLOAD_MANIFEST_FILE, "DEV_UPLOAD_MANIFEST_FILE"),
77
80
  HOST_EXTERNAL_SPECIFIERS: /* @__PURE__ */ __name(() => HOST_EXTERNAL_SPECIFIERS, "HOST_EXTERNAL_SPECIFIERS"),
78
81
  HUB_ROOT_SPEC: /* @__PURE__ */ __name(() => HUB_ROOT_SPEC2, "HUB_ROOT_SPEC"),
79
82
  RESTART_INTENT_FILE: /* @__PURE__ */ __name(() => RESTART_INTENT_FILE, "RESTART_INTENT_FILE"),
@@ -83,11 +86,17 @@ var require_dist = __commonJS({
83
86
  clearRestartIntentMarker: /* @__PURE__ */ __name(() => clearRestartIntentMarker, "clearRestartIntentMarker"),
84
87
  compareSemver: /* @__PURE__ */ __name(() => compareSemver, "compareSemver"),
85
88
  detectWorkspaceRoot: /* @__PURE__ */ __name(() => detectWorkspaceRoot, "detectWorkspaceRoot"),
89
+ devChannelEpoch: /* @__PURE__ */ __name(() => devChannelEpoch, "devChannelEpoch"),
90
+ devUploadManifestPath: /* @__PURE__ */ __name(() => devUploadManifestPath, "devUploadManifestPath"),
91
+ devUploadVersionDir: /* @__PURE__ */ __name(() => devUploadVersionDir, "devUploadVersionDir"),
92
+ devUploadsDir: /* @__PURE__ */ __name(() => devUploadsDir, "devUploadsDir"),
86
93
  emptyServerRootState: /* @__PURE__ */ __name(() => emptyServerRootState, "emptyServerRootState"),
94
+ isDevChannelVersion: /* @__PURE__ */ __name(() => isDevChannelVersion, "isDevChannelVersion"),
87
95
  isHostExternal: /* @__PURE__ */ __name(() => isHostExternal, "isHostExternal"),
88
96
  isServerRootState: /* @__PURE__ */ __name(() => isServerRootState, "isServerRootState"),
89
97
  minNodeMajorOf: /* @__PURE__ */ __name(() => minNodeMajorOf, "minNodeMajorOf"),
90
98
  planBoot: /* @__PURE__ */ __name(() => planBoot, "planBoot"),
99
+ readDevUploadManifest: /* @__PURE__ */ __name(() => readDevUploadManifest, "readDevUploadManifest"),
91
100
  readRestartIntentMarker: /* @__PURE__ */ __name(() => readRestartIntentMarker, "readRestartIntentMarker"),
92
101
  readServerRootState: /* @__PURE__ */ __name(() => readServerRootState, "readServerRootState"),
93
102
  registerActiveRootResolver: /* @__PURE__ */ __name(() => registerActiveRootResolver, "registerActiveRootResolver"),
@@ -100,10 +109,68 @@ var require_dist = __commonJS({
100
109
  validateVersionDir: /* @__PURE__ */ __name(() => validateVersionDir, "validateVersionDir"),
101
110
  versionDir: /* @__PURE__ */ __name(() => versionDir, "versionDir"),
102
111
  versionsDir: /* @__PURE__ */ __name(() => versionsDir, "versionsDir"),
112
+ writeDevUploadManifest: /* @__PURE__ */ __name(() => writeDevUploadManifest, "writeDevUploadManifest"),
103
113
  writeRestartIntentMarker: /* @__PURE__ */ __name(() => writeRestartIntentMarker, "writeRestartIntentMarker"),
104
114
  writeServerRootState: /* @__PURE__ */ __name(() => writeServerRootState, "writeServerRootState")
105
115
  });
106
116
  module2.exports = __toCommonJS2(src_exports);
117
+ var fs = __toESM2(require("fs"));
118
+ var path = __toESM2(require("path"));
119
+ var DEV_UPLOADS_DIRNAME = "dev-uploads";
120
+ var DEV_UPLOAD_MANIFEST_FILE = "manifest.json";
121
+ var DEV_UPLOADS_KEEP_COUNT = 3;
122
+ var DEV_CHANNEL_VERSION_RE = /-dev\.\d+$/;
123
+ function isDevChannelVersion(version) {
124
+ return DEV_CHANNEL_VERSION_RE.test(version);
125
+ }
126
+ __name(isDevChannelVersion, "isDevChannelVersion");
127
+ function devChannelEpoch(version) {
128
+ const match = /-dev\.(\d+)$/.exec(version);
129
+ if (match === null) return null;
130
+ const epoch = Number.parseInt(match[1] ?? "", 10);
131
+ return Number.isNaN(epoch) ? null : epoch;
132
+ }
133
+ __name(devChannelEpoch, "devChannelEpoch");
134
+ function devUploadsDir(rootDir) {
135
+ return path.join(rootDir, DEV_UPLOADS_DIRNAME);
136
+ }
137
+ __name(devUploadsDir, "devUploadsDir");
138
+ function devUploadVersionDir(rootDir, version) {
139
+ return path.join(devUploadsDir(rootDir), version);
140
+ }
141
+ __name(devUploadVersionDir, "devUploadVersionDir");
142
+ function devUploadManifestPath(versionDirPath) {
143
+ return path.join(versionDirPath, DEV_UPLOAD_MANIFEST_FILE);
144
+ }
145
+ __name(devUploadManifestPath, "devUploadManifestPath");
146
+ function isDevUploadManifest(v) {
147
+ if (typeof v !== "object" || v === null) return false;
148
+ const m = v;
149
+ if (typeof m["version"] !== "string") return false;
150
+ const packages = m["packages"];
151
+ if (typeof packages !== "object" || packages === null || Array.isArray(packages)) return false;
152
+ return Object.values(packages).every((filename) => typeof filename === "string");
153
+ }
154
+ __name(isDevUploadManifest, "isDevUploadManifest");
155
+ function readDevUploadManifest(versionDirPath) {
156
+ try {
157
+ const raw = JSON.parse(fs.readFileSync(devUploadManifestPath(versionDirPath), "utf-8"));
158
+ return isDevUploadManifest(raw) ? raw : null;
159
+ } catch {
160
+ return null;
161
+ }
162
+ }
163
+ __name(readDevUploadManifest, "readDevUploadManifest");
164
+ function writeDevUploadManifest(versionDirPath, manifest) {
165
+ fs.mkdirSync(versionDirPath, {
166
+ recursive: true
167
+ });
168
+ const target = devUploadManifestPath(versionDirPath);
169
+ const tmp = `${target}.tmp`;
170
+ fs.writeFileSync(tmp, JSON.stringify(manifest, null, 2), "utf-8");
171
+ fs.renameSync(tmp, target);
172
+ }
173
+ __name(writeDevUploadManifest, "writeDevUploadManifest");
107
174
  var HUB_ROOT_SPEC2 = {
108
175
  packageName: "@camstack/server",
109
176
  entryRelPath: [
@@ -118,8 +185,8 @@ var require_dist = __commonJS({
118
185
  "cli.js"
119
186
  ]
120
187
  };
121
- var fs = __toESM2(require("fs"));
122
- var path = __toESM2(require("path"));
188
+ var fs2 = __toESM2(require("fs"));
189
+ var path2 = __toESM2(require("path"));
123
190
  var SERVER_ROOT_DIRNAME = "server-root";
124
191
  var SERVER_ROOT_STATE_FILE = "state.json";
125
192
  var RESTART_INTENT_FILE = ".restart-intent";
@@ -134,27 +201,27 @@ var require_dist = __commonJS({
134
201
  }
135
202
  __name(emptyServerRootState, "emptyServerRootState");
136
203
  function serverRootDir(dataDir) {
137
- return path.join(dataDir, SERVER_ROOT_DIRNAME);
204
+ return path2.join(dataDir, SERVER_ROOT_DIRNAME);
138
205
  }
139
206
  __name(serverRootDir, "serverRootDir");
140
207
  function versionsDir(rootDir) {
141
- return path.join(rootDir, "versions");
208
+ return path2.join(rootDir, "versions");
142
209
  }
143
210
  __name(versionsDir, "versionsDir");
144
211
  function versionDir(rootDir, version) {
145
- return path.join(versionsDir(rootDir), version);
212
+ return path2.join(versionsDir(rootDir), version);
146
213
  }
147
214
  __name(versionDir, "versionDir");
148
215
  function rootPackageDir2(versionDirPath, spec) {
149
- return path.join(versionDirPath, "node_modules", ...spec.packageName.split("/"));
216
+ return path2.join(versionDirPath, "node_modules", ...spec.packageName.split("/"));
150
217
  }
151
218
  __name(rootPackageDir2, "rootPackageDir");
152
219
  function rootEntryPath2(versionDirPath, spec) {
153
- return path.join(rootPackageDir2(versionDirPath, spec), ...spec.entryRelPath);
220
+ return path2.join(rootPackageDir2(versionDirPath, spec), ...spec.entryRelPath);
154
221
  }
155
222
  __name(rootEntryPath2, "rootEntryPath");
156
223
  function stateFilePath(rootDir) {
157
- return path.join(rootDir, SERVER_ROOT_STATE_FILE);
224
+ return path2.join(rootDir, SERVER_ROOT_STATE_FILE);
158
225
  }
159
226
  __name(stateFilePath, "stateFilePath");
160
227
  function isNullableString(v) {
@@ -186,7 +253,7 @@ var require_dist = __commonJS({
186
253
  __name(isServerRootState, "isServerRootState");
187
254
  function readServerRootState(rootDir) {
188
255
  try {
189
- const raw = JSON.parse(fs.readFileSync(stateFilePath(rootDir), "utf-8"));
256
+ const raw = JSON.parse(fs2.readFileSync(stateFilePath(rootDir), "utf-8"));
190
257
  return isServerRootState(raw) ? raw : null;
191
258
  } catch {
192
259
  return null;
@@ -194,17 +261,17 @@ var require_dist = __commonJS({
194
261
  }
195
262
  __name(readServerRootState, "readServerRootState");
196
263
  function writeServerRootState(rootDir, state) {
197
- fs.mkdirSync(rootDir, {
264
+ fs2.mkdirSync(rootDir, {
198
265
  recursive: true
199
266
  });
200
267
  const target = stateFilePath(rootDir);
201
268
  const tmp = `${target}.tmp`;
202
- fs.writeFileSync(tmp, JSON.stringify(state, null, 2), "utf-8");
203
- fs.renameSync(tmp, target);
269
+ fs2.writeFileSync(tmp, JSON.stringify(state, null, 2), "utf-8");
270
+ fs2.renameSync(tmp, target);
204
271
  }
205
272
  __name(writeServerRootState, "writeServerRootState");
206
273
  function restartIntentMarkerPath(rootDir) {
207
- return path.join(rootDir, RESTART_INTENT_FILE);
274
+ return path2.join(rootDir, RESTART_INTENT_FILE);
208
275
  }
209
276
  __name(restartIntentMarkerPath, "restartIntentMarkerPath");
210
277
  function isRestartIntentMarker(v) {
@@ -214,18 +281,18 @@ var require_dist = __commonJS({
214
281
  }
215
282
  __name(isRestartIntentMarker, "isRestartIntentMarker");
216
283
  function writeRestartIntentMarker(rootDir, marker) {
217
- fs.mkdirSync(rootDir, {
284
+ fs2.mkdirSync(rootDir, {
218
285
  recursive: true
219
286
  });
220
287
  const target = restartIntentMarkerPath(rootDir);
221
288
  const tmp = `${target}.tmp`;
222
- fs.writeFileSync(tmp, JSON.stringify(marker, null, 2), "utf-8");
223
- fs.renameSync(tmp, target);
289
+ fs2.writeFileSync(tmp, JSON.stringify(marker, null, 2), "utf-8");
290
+ fs2.renameSync(tmp, target);
224
291
  }
225
292
  __name(writeRestartIntentMarker, "writeRestartIntentMarker");
226
293
  function readRestartIntentMarker(rootDir) {
227
294
  try {
228
- const raw = JSON.parse(fs.readFileSync(restartIntentMarkerPath(rootDir), "utf-8"));
295
+ const raw = JSON.parse(fs2.readFileSync(restartIntentMarkerPath(rootDir), "utf-8"));
229
296
  return isRestartIntentMarker(raw) ? raw : null;
230
297
  } catch {
231
298
  return null;
@@ -234,7 +301,7 @@ var require_dist = __commonJS({
234
301
  __name(readRestartIntentMarker, "readRestartIntentMarker");
235
302
  function clearRestartIntentMarker(rootDir) {
236
303
  try {
237
- fs.rmSync(restartIntentMarkerPath(rootDir), {
304
+ fs2.rmSync(restartIntentMarkerPath(rootDir), {
238
305
  force: true
239
306
  });
240
307
  } catch {
@@ -251,13 +318,13 @@ var require_dist = __commonJS({
251
318
  function validateVersionDir(rootDir, version, nodeMajor, spec) {
252
319
  const vDir = versionDir(rootDir, version);
253
320
  const entry = rootEntryPath2(vDir, spec);
254
- if (!fs.existsSync(entry)) {
321
+ if (!fs2.existsSync(entry)) {
255
322
  return `root entry missing: ${entry}`;
256
323
  }
257
- const pkgJsonPath = path.join(rootPackageDir2(vDir, spec), "package.json");
324
+ const pkgJsonPath = path2.join(rootPackageDir2(vDir, spec), "package.json");
258
325
  let pkg;
259
326
  try {
260
- const raw = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
327
+ const raw = JSON.parse(fs2.readFileSync(pkgJsonPath, "utf-8"));
261
328
  if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
262
329
  return `package.json malformed: ${pkgJsonPath}`;
263
330
  }
@@ -418,26 +485,26 @@ var require_dist = __commonJS({
418
485
  return 0;
419
486
  }
420
487
  __name(compareSemver, "compareSemver");
421
- var fs2 = __toESM2(require("fs"));
422
- var path2 = __toESM2(require("path"));
488
+ var fs3 = __toESM2(require("fs"));
489
+ var path3 = __toESM2(require("path"));
423
490
  function detectWorkspaceRoot(fromDir) {
424
- let dir = path2.resolve(fromDir);
491
+ let dir = path3.resolve(fromDir);
425
492
  for (; ; ) {
426
- const pkgPath = path2.join(dir, "package.json");
493
+ const pkgPath = path3.join(dir, "package.json");
427
494
  try {
428
- const raw = JSON.parse(fs2.readFileSync(pkgPath, "utf-8"));
495
+ const raw = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
429
496
  if (typeof raw === "object" && raw !== null && raw["workspaces"] !== void 0) {
430
497
  return dir;
431
498
  }
432
499
  } catch {
433
500
  }
434
- const parent = path2.dirname(dir);
501
+ const parent = path3.dirname(dir);
435
502
  if (parent === dir) return null;
436
503
  dir = parent;
437
504
  }
438
505
  }
439
506
  __name(detectWorkspaceRoot, "detectWorkspaceRoot");
440
- var path3 = __toESM2(require("path"));
507
+ var path4 = __toESM2(require("path"));
441
508
  var import_node_url = require("url");
442
509
  var HOST_EXTERNAL_SPECIFIERS = [
443
510
  "@camstack/system",
@@ -461,7 +528,7 @@ var require_dist = __commonJS({
461
528
  console.warn("[starter] module.registerHooks unavailable (Node < 22.15) \u2014 host-external redirect skipped");
462
529
  return;
463
530
  }
464
- const anchorURL = (0, import_node_url.pathToFileURL)(path3.join(activeRootDir, "node_modules", "__camstack_starter_anchor__.js")).href;
531
+ const anchorURL = (0, import_node_url.pathToFileURL)(path4.join(activeRootDir, "node_modules", "__camstack_starter_anchor__.js")).href;
465
532
  const hooks = {
466
533
  resolve: /* @__PURE__ */ __name((specifier, context, nextResolve) => {
467
534
  if (isHostExternal(specifier)) {
@@ -537,8 +604,8 @@ var require_dist = __commonJS({
537
604
  }
538
605
  __name(runNodeStarter, "runNodeStarter");
539
606
  var import_node_child_process = require("child_process");
540
- var fs3 = __toESM2(require("fs"));
541
- var path4 = __toESM2(require("path"));
607
+ var fs4 = __toESM2(require("fs"));
608
+ var path5 = __toESM2(require("path"));
542
609
  var import_node_util = require("util");
543
610
  var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
544
611
  function buildNpmRegistryArgs(registry) {
@@ -555,7 +622,7 @@ var require_dist = __commonJS({
555
622
  var RESTART_REASON_PREFIX = "server-update";
556
623
  function readPackageVersion(pkgJsonPath) {
557
624
  try {
558
- const raw = JSON.parse(fs3.readFileSync(pkgJsonPath, "utf-8"));
625
+ const raw = JSON.parse(fs4.readFileSync(pkgJsonPath, "utf-8"));
559
626
  if (typeof raw === "object" && raw !== null) {
560
627
  const version = raw["version"];
561
628
  if (typeof version === "string") return version;
@@ -586,7 +653,7 @@ var require_dist = __commonJS({
586
653
  this.envNames = options.envNames;
587
654
  this.logger = options.logger;
588
655
  this.restartServerFn = options.restartServer;
589
- this.dataDir = path4.resolve(options.dataDir);
656
+ this.dataDir = path5.resolve(options.dataDir);
590
657
  this.execNpm = options.execNpm ?? (async (args, opts) => {
591
658
  const { stdout } = await execFileAsync("npm", [
592
659
  ...args
@@ -622,7 +689,7 @@ var require_dist = __commonJS({
622
689
  seedVersion() {
623
690
  const seedDir = this.env[this.envNames.seedDir];
624
691
  if (seedDir === void 0 || seedDir.length === 0) return null;
625
- return readPackageVersion(path4.join(seedDir, "package.json"));
692
+ return readPackageVersion(path5.join(seedDir, "package.json"));
626
693
  }
627
694
  rootDir() {
628
695
  return serverRootDir(this.dataDir);
@@ -645,7 +712,7 @@ var require_dist = __commonJS({
645
712
  };
646
713
  return {
647
714
  state: emptyServerRootState(),
648
- corrupt: fs3.existsSync(stateFilePath(rootDir))
715
+ corrupt: fs4.existsSync(stateFilePath(rootDir))
649
716
  };
650
717
  }
651
718
  updateState(state) {
@@ -796,6 +863,17 @@ var require_dist = __commonJS({
796
863
  message: `Refused: ${this.spec.packageName}@${target} is already running.`
797
864
  };
798
865
  }
866
+ if (isDevChannelVersion(target)) {
867
+ const uploadsDir = devUploadVersionDir(this.rootDir(), target);
868
+ if (!fs4.existsSync(uploadsDir)) {
869
+ return {
870
+ accepted: false,
871
+ targetVersion: target,
872
+ restarting: false,
873
+ message: `Refused: dev-channel version ${target} has no uploaded tarballs on this node (${uploadsDir} is missing). Dev server deploys are hub-only in this phase \u2014 upload via \`camstack deploy-server\` against this node first.`
874
+ };
875
+ }
876
+ }
799
877
  this.inFlight = "staging";
800
878
  try {
801
879
  await this.stageAndActivate(target);
@@ -848,57 +926,116 @@ var require_dist = __commonJS({
848
926
  async stageAndActivate(target) {
849
927
  const rootDir = this.rootDir();
850
928
  const vDir = versionsDir(rootDir);
851
- fs3.mkdirSync(vDir, {
929
+ fs4.mkdirSync(vDir, {
852
930
  recursive: true
853
931
  });
854
- const stagingDir = path4.join(vDir, `.staging-${target}-${process.pid}-${this.now()}`);
855
- fs3.mkdirSync(stagingDir, {
932
+ const stagingDir = path5.join(vDir, `.staging-${target}-${process.pid}-${this.now()}`);
933
+ fs4.mkdirSync(stagingDir, {
856
934
  recursive: true
857
935
  });
858
936
  try {
859
- fs3.writeFileSync(path4.join(stagingDir, "package.json"), JSON.stringify({
860
- name: "camstack-node-root",
861
- private: true
862
- }, null, 2), "utf-8");
937
+ const devDir = devUploadVersionDir(rootDir, target);
863
938
  const registry = this.env["CAMSTACK_NPM_REGISTRY"];
864
- await this.execNpm([
939
+ const installArgs = [
865
940
  "install",
866
941
  "--omit=dev",
867
942
  "--no-audit",
868
943
  "--no-fund",
869
- "--loglevel=error",
870
- `${this.spec.packageName}@${target}`,
871
- ...buildNpmRegistryArgs(registry)
872
- ], {
873
- cwd: stagingDir,
874
- timeoutMs: NPM_INSTALL_TIMEOUT_MS
875
- });
944
+ "--loglevel=error"
945
+ ];
946
+ if (fs4.existsSync(devDir)) {
947
+ fs4.writeFileSync(path5.join(stagingDir, "package.json"), JSON.stringify(this.buildDevUploadsPackageJson(devDir, target), null, 2), "utf-8");
948
+ await this.execNpm([
949
+ ...installArgs,
950
+ ...buildNpmRegistryArgs(registry)
951
+ ], {
952
+ cwd: stagingDir,
953
+ timeoutMs: NPM_INSTALL_TIMEOUT_MS
954
+ });
955
+ } else {
956
+ fs4.writeFileSync(path5.join(stagingDir, "package.json"), JSON.stringify({
957
+ name: "camstack-node-root",
958
+ private: true
959
+ }, null, 2), "utf-8");
960
+ await this.execNpm([
961
+ ...installArgs,
962
+ `${this.spec.packageName}@${target}`,
963
+ ...buildNpmRegistryArgs(registry)
964
+ ], {
965
+ cwd: stagingDir,
966
+ timeoutMs: NPM_INSTALL_TIMEOUT_MS
967
+ });
968
+ }
876
969
  const entry = rootEntryPath2(stagingDir, this.spec);
877
- if (!fs3.existsSync(entry)) {
970
+ if (!fs4.existsSync(entry)) {
878
971
  throw new Error(`staged closure is missing the root entry (${entry})`);
879
972
  }
880
- const stagedVersion = readPackageVersion(path4.join(rootPackageDir2(stagingDir, this.spec), "package.json"));
973
+ const stagedVersion = readPackageVersion(path5.join(rootPackageDir2(stagingDir, this.spec), "package.json"));
881
974
  if (stagedVersion !== target) {
882
975
  throw new Error(`staged closure version mismatch: expected ${target}, got ${stagedVersion ?? "unknown"}`);
883
976
  }
884
977
  const dest = versionDir(rootDir, target);
885
- if (fs3.existsSync(dest)) {
978
+ if (fs4.existsSync(dest)) {
886
979
  const aside = `${dest}.evicted-${this.now()}`;
887
- await fs3.promises.rename(dest, aside);
888
- await fs3.promises.rm(aside, {
980
+ await fs4.promises.rename(dest, aside);
981
+ await fs4.promises.rm(aside, {
889
982
  recursive: true,
890
983
  force: true
891
984
  }).catch(() => void 0);
892
985
  }
893
- await fs3.promises.rename(stagingDir, dest);
986
+ await fs4.promises.rename(stagingDir, dest);
894
987
  } catch (err) {
895
- await fs3.promises.rm(stagingDir, {
988
+ await fs4.promises.rm(stagingDir, {
896
989
  recursive: true,
897
990
  force: true
898
991
  }).catch(() => void 0);
899
992
  throw err;
900
993
  }
901
994
  }
995
+ /**
996
+ * Synthetic staging package.json for the dev server-deploy channel:
997
+ * `dependencies` carries the root package as a `file:` ref to its uploaded
998
+ * tgz; `overrides` maps EVERY OTHER uploaded `@camstack/<name>` to its tgz
999
+ * so any transitive occurrence resolves to the uploaded copy, never npm.
1000
+ * Fail-closed: a missing/invalid manifest, a manifest for a different
1001
+ * version, a missing root package, or a listed-but-absent tgz all throw
1002
+ * (surfaced by `applyServerUpdate` as a staging failure).
1003
+ */
1004
+ buildDevUploadsPackageJson(devDir, target) {
1005
+ const manifest = readDevUploadManifest(devDir);
1006
+ if (manifest === null) {
1007
+ throw new Error(`dev-uploads manifest missing or invalid in ${devDir}`);
1008
+ }
1009
+ if (manifest.version !== target) {
1010
+ throw new Error(`dev-uploads manifest version mismatch: dir is for ${target}, manifest says ${manifest.version}`);
1011
+ }
1012
+ const rootTgz = manifest.packages[this.spec.packageName];
1013
+ if (rootTgz === void 0) {
1014
+ throw new Error(`dev-uploads for ${target} do not include the root package ${this.spec.packageName}`);
1015
+ }
1016
+ const fileRef = /* @__PURE__ */ __name((filename) => {
1017
+ const abs = path5.join(devDir, filename);
1018
+ if (!fs4.existsSync(abs)) {
1019
+ throw new Error(`dev-uploads tarball listed in the manifest is missing: ${abs}`);
1020
+ }
1021
+ return `file:${abs}`;
1022
+ }, "fileRef");
1023
+ const overrides = {};
1024
+ for (const [pkgName, filename] of Object.entries(manifest.packages)) {
1025
+ if (pkgName === this.spec.packageName) continue;
1026
+ overrides[pkgName] = fileRef(filename);
1027
+ }
1028
+ return {
1029
+ name: "camstack-node-root",
1030
+ private: true,
1031
+ dependencies: {
1032
+ [this.spec.packageName]: fileRef(rootTgz)
1033
+ },
1034
+ ...Object.keys(overrides).length > 0 ? {
1035
+ overrides
1036
+ } : {}
1037
+ };
1038
+ }
902
1039
  // ── Rollback ──────────────────────────────────────────────────────────
903
1040
  async rollbackServerUpdate() {
904
1041
  if (this.inFlight !== "idle") {
@@ -1031,6 +1168,7 @@ var require_dist = __commonJS({
1031
1168
  promoted.currentVersion,
1032
1169
  promoted.previousVersion
1033
1170
  ].filter((v) => typeof v === "string"));
1171
+ this.sweepDevUploads(DEV_UPLOADS_KEEP_COUNT);
1034
1172
  this.logger.info("root package update confirmed healthy", {
1035
1173
  meta: {
1036
1174
  version: promoted.currentVersion,
@@ -1056,23 +1194,23 @@ var require_dist = __commonJS({
1056
1194
  const vDir = versionsDir(this.rootDir());
1057
1195
  let entries;
1058
1196
  try {
1059
- entries = fs3.readdirSync(vDir);
1197
+ entries = fs4.readdirSync(vDir);
1060
1198
  } catch {
1061
1199
  return;
1062
1200
  }
1063
1201
  for (const entry of entries) {
1064
1202
  if (keep.includes(entry)) continue;
1065
- const full = path4.join(vDir, entry);
1203
+ const full = path5.join(vDir, entry);
1066
1204
  if (entry.startsWith(".")) {
1067
1205
  try {
1068
- const ageMs = this.now() - fs3.statSync(full).mtimeMs;
1206
+ const ageMs = this.now() - fs4.statSync(full).mtimeMs;
1069
1207
  if (ageMs < _RootUpdateService.STALE_TRANSIENT_MS) continue;
1070
1208
  } catch {
1071
1209
  continue;
1072
1210
  }
1073
1211
  }
1074
1212
  try {
1075
- fs3.rmSync(full, {
1213
+ fs4.rmSync(full, {
1076
1214
  recursive: true,
1077
1215
  force: true
1078
1216
  });
@@ -1091,6 +1229,71 @@ var require_dist = __commonJS({
1091
1229
  }
1092
1230
  }
1093
1231
  }
1232
+ /**
1233
+ * Promote-time sweep of `server-root/dev-uploads/`: keep only the
1234
+ * `keepCount` most recent entries (ordered by the self-describing
1235
+ * `-dev.<epochSeconds>` suffix; non-dev-shaped residue counts as oldest).
1236
+ * Uses the same rename-aside graveyard pattern as the addon installer —
1237
+ * a rename is a metadata op that succeeds even when a tarball is held
1238
+ * open, and residue in the graveyard is re-swept on the next promote.
1239
+ */
1240
+ sweepDevUploads(keepCount) {
1241
+ const dir = devUploadsDir(this.rootDir());
1242
+ let entries;
1243
+ try {
1244
+ entries = fs4.readdirSync(dir);
1245
+ } catch {
1246
+ return;
1247
+ }
1248
+ const graveyard = path5.join(dir, ".sweeping");
1249
+ try {
1250
+ for (const residue of fs4.readdirSync(graveyard)) {
1251
+ try {
1252
+ fs4.rmSync(path5.join(graveyard, residue), {
1253
+ recursive: true,
1254
+ force: true
1255
+ });
1256
+ } catch {
1257
+ }
1258
+ }
1259
+ } catch {
1260
+ }
1261
+ const versions = entries.filter((name) => !name.startsWith("."));
1262
+ const byNewestFirst = [
1263
+ ...versions
1264
+ ].sort((a, b) => (devChannelEpoch(b) ?? -1) - (devChannelEpoch(a) ?? -1));
1265
+ const doomed = byNewestFirst.slice(keepCount);
1266
+ if (doomed.length === 0) return;
1267
+ fs4.mkdirSync(graveyard, {
1268
+ recursive: true
1269
+ });
1270
+ for (const entry of doomed) {
1271
+ const aside = path5.join(graveyard, `${entry}-${this.now()}`);
1272
+ try {
1273
+ fs4.renameSync(path5.join(dir, entry), aside);
1274
+ } catch (err) {
1275
+ this.logger.warn("failed to move dev-uploads entry aside for sweep", {
1276
+ meta: {
1277
+ entry,
1278
+ error: err instanceof Error ? err.message : String(err)
1279
+ }
1280
+ });
1281
+ continue;
1282
+ }
1283
+ try {
1284
+ fs4.rmSync(aside, {
1285
+ recursive: true,
1286
+ force: true
1287
+ });
1288
+ this.logger.debug("swept dev-uploads entry", {
1289
+ meta: {
1290
+ entry
1291
+ }
1292
+ });
1293
+ } catch {
1294
+ }
1295
+ }
1296
+ }
1094
1297
  /** Transient (.staging- / .evicted-) dirs younger than this are never pruned. */
1095
1298
  static STALE_TRANSIENT_MS = 24 * 60 * 60 * 1e3;
1096
1299
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/server",
3
- "version": "1.1.54",
3
+ "version": "1.1.56",
4
4
  "private": false,
5
5
  "files": [
6
6
  "dist",
@@ -23,19 +23,19 @@
23
23
  "test:watch": "vitest"
24
24
  },
25
25
  "dependencies": {
26
- "@camstack/addon-admin-ui": "1.1.46",
26
+ "@camstack/addon-admin-ui": "1.1.47",
27
27
  "@camstack/addon-advanced-notifier": "1.1.21",
28
28
  "@camstack/addon-auth": "1.1.6",
29
29
  "@camstack/addon-decoder-nodeav": "1.1.9",
30
30
  "@camstack/addon-notifiers": "1.1.21",
31
31
  "@camstack/addon-pipeline": "1.1.51",
32
- "@camstack/addon-pipeline-orchestrator": "1.1.40",
33
- "@camstack/addon-post-analysis": "1.1.23",
32
+ "@camstack/addon-pipeline-orchestrator": "1.1.41",
33
+ "@camstack/addon-post-analysis": "1.1.24",
34
34
  "@camstack/sdk": "1.1.22",
35
35
  "@camstack/shm-ring": "1.0.21",
36
- "@camstack/system": "1.1.41",
37
- "@camstack/types": "1.1.39",
38
- "@camstack/ui-library": "1.1.31",
36
+ "@camstack/system": "1.1.43",
37
+ "@camstack/types": "1.1.40",
38
+ "@camstack/ui-library": "1.1.32",
39
39
  "@fastify/compress": "^9.0.0",
40
40
  "@fastify/cookie": "^11.0.2",
41
41
  "@fastify/multipart": "^10.0.0",