@s2script/sdk 0.33.0 → 0.34.0

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 (3) hide show
  1. package/README.md +37 -0
  2. package/dist/cli.js +348 -234
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -6595,7 +6595,7 @@ function positionals(args, flagsWithValue) {
6595
6595
 
6596
6596
  // src/workspace/build-all.ts
6597
6597
  import { writeFileSync as writeFileSync4 } from "node:fs";
6598
- import { join as join14 } from "node:path";
6598
+ import { join as join15 } from "node:path";
6599
6599
 
6600
6600
  // src/workspace/graph.ts
6601
6601
  function topoOrder(batch, warn = console.warn) {
@@ -6705,6 +6705,8 @@ function formatRangeViolations(violations) {
6705
6705
  }
6706
6706
 
6707
6707
  // src/workspace/bundle.ts
6708
+ import { existsSync as existsSync11, readFileSync as readFileSync12 } from "node:fs";
6709
+ import { join as join14 } from "node:path";
6708
6710
  var MAX_BUNDLE_MEMBERS = 64;
6709
6711
  function isBundleWorkspace(ws) {
6710
6712
  return ws.pkg.s2script?.kind === "bundle";
@@ -6714,11 +6716,50 @@ function scopeOf(name) {
6714
6716
  const slash = name.indexOf("/");
6715
6717
  return slash > 1 ? name.slice(1, slash) : null;
6716
6718
  }
6717
- function bundleProblems(ws) {
6718
- if (!isBundleWorkspace(ws)) return [];
6719
- const pkg = ws.pkg;
6720
- const problems = [];
6719
+ function bundlesField(ws) {
6720
+ return ws.pkg.s2script?.workspace?.bundles;
6721
+ }
6722
+ function bundleSources(ws) {
6723
+ const raw = bundlesField(ws);
6721
6724
  const where2 = "package.json (workspace root)";
6725
+ if (raw === void 0) return { sources: [], problems: [] };
6726
+ if (!Array.isArray(raw) || raw.some((g) => typeof g !== "string" || g === "")) {
6727
+ return { sources: [], problems: [`${where2}: s2script.workspace.bundles must be an array of directories or globs`] };
6728
+ }
6729
+ const problems = [];
6730
+ const seen = /* @__PURE__ */ new Set();
6731
+ const sources2 = [];
6732
+ for (const glob of raw) {
6733
+ const dirs = expandDirGlob(ws.root, glob).filter((rel) => existsSync11(join14(ws.root, rel, "package.json")));
6734
+ if (dirs.length === 0) {
6735
+ problems.push(`${where2}: s2script.workspace.bundles entry ${JSON.stringify(glob)} matches no directory with a package.json`);
6736
+ continue;
6737
+ }
6738
+ for (const rel of dirs) {
6739
+ if (seen.has(rel)) continue;
6740
+ seen.add(rel);
6741
+ const dir = join14(ws.root, rel);
6742
+ try {
6743
+ sources2.push({ relDir: rel, dir, pkg: JSON.parse(readFileSync12(join14(dir, "package.json"), "utf8")) });
6744
+ } catch (e) {
6745
+ sources2.push({ relDir: rel, dir, pkg: null, error: `cannot read ${rel}/package.json: ${e.message}` });
6746
+ }
6747
+ }
6748
+ }
6749
+ return { sources: sources2.sort((a2, b) => a2.relDir < b.relDir ? -1 : 1), problems };
6750
+ }
6751
+ function isUnder(relDir, bundleRel) {
6752
+ return bundleRel === "" || relDir.startsWith(`${bundleRel}/`);
6753
+ }
6754
+ function membersOf(ws, bundleRel) {
6755
+ return ws.plugins.filter((p) => !p.private && isUnder(p.relDir, bundleRel));
6756
+ }
6757
+ function optionalOf(pkg) {
6758
+ const optional = pkg.s2script?.bundle?.optional;
6759
+ return Array.isArray(optional) ? optional.filter((o) => typeof o === "string") : [];
6760
+ }
6761
+ function declarationProblems(where2, pkg, members) {
6762
+ const problems = [];
6722
6763
  if (typeof pkg.name !== "string" || pkg.name === "") {
6723
6764
  problems.push(`${where2}: a bundle needs a "name" \u2014 it is the bundle's registry name`);
6724
6765
  }
@@ -6727,13 +6768,12 @@ function bundleProblems(ws) {
6727
6768
  }
6728
6769
  if (pkg.private === true) {
6729
6770
  problems.push(
6730
- `${where2}: s2script.kind is "bundle" but the root is "private": true \u2014 a private root is never published; remove "private" or drop the bundle kind`
6771
+ `${where2}: s2script.kind is "bundle" but the package is "private": true \u2014 a private bundle is never published; remove "private" or drop the bundle kind`
6731
6772
  );
6732
6773
  }
6733
6774
  if (pkg.main !== void 0 || pkg.s2script?.main !== void 0) {
6734
6775
  problems.push(`${where2}: a bundle has no code of its own \u2014 remove "main"`);
6735
6776
  }
6736
- const members = ws.plugins.filter((p) => !p.private);
6737
6777
  if (members.length === 0) {
6738
6778
  problems.push(`${where2}: a bundle needs at least one non-private plugin in s2script.workspace.plugins`);
6739
6779
  }
@@ -6766,7 +6806,7 @@ function bundleProblems(ws) {
6766
6806
  for (const name of optional) {
6767
6807
  if (!memberNames.has(name)) {
6768
6808
  problems.push(
6769
- `${where2}: s2script.bundle.optional names ${JSON.stringify(name)}, which is not a non-private plugin of this workspace`
6809
+ `${where2}: s2script.bundle.optional names ${JSON.stringify(name)}, which is not a non-private plugin of this bundle`
6770
6810
  );
6771
6811
  }
6772
6812
  }
@@ -6775,16 +6815,78 @@ function bundleProblems(ws) {
6775
6815
  }
6776
6816
  return problems;
6777
6817
  }
6778
- function readBundle(ws) {
6779
- if (!isBundleWorkspace(ws)) return null;
6780
- const optional = ws.pkg.s2script.bundle?.optional ?? [];
6818
+ function bundleProblems(ws) {
6819
+ const rootBundle = isBundleWorkspace(ws);
6820
+ const { sources: sources2, problems } = bundleSources(ws);
6821
+ if (rootBundle && bundlesField(ws) !== void 0) {
6822
+ problems.push(
6823
+ `package.json (workspace root): s2script.kind is "bundle" and s2script.workspace.bundles is set \u2014 a root bundle cannot also contain bundles; drop the root's kind, or drop the bundles list`
6824
+ );
6825
+ return problems;
6826
+ }
6827
+ if (rootBundle) return [...problems, ...declarationProblems("package.json (workspace root)", ws.pkg, membersOf(ws, ""))];
6828
+ const memberDirs = new Set([...ws.plugins, ...ws.libs].map((m) => m.relDir));
6829
+ const byName = /* @__PURE__ */ new Map();
6830
+ for (const src of sources2) {
6831
+ const where2 = `${src.relDir}/package.json`;
6832
+ if (src.pkg === null) {
6833
+ problems.push(src.error);
6834
+ continue;
6835
+ }
6836
+ if (src.pkg.s2script?.kind !== "bundle") {
6837
+ problems.push(`${where2}: is listed in s2script.workspace.bundles but has no s2script.kind "bundle"`);
6838
+ continue;
6839
+ }
6840
+ if (src.pkg.s2script.workspace !== void 0) {
6841
+ problems.push(
6842
+ `${where2}: a bundle directory must not carry s2script.workspace \u2014 s2s would treat it as its own workspace root when run inside it; remove the block (the parent root is the workspace)`
6843
+ );
6844
+ }
6845
+ if (memberDirs.has(src.relDir)) {
6846
+ problems.push(
6847
+ `${src.relDir}: is both a workspace package and a bundle directory \u2014 narrow the root's "workspaces" globs so the bundle descriptor is not a package`
6848
+ );
6849
+ }
6850
+ const nested = sources2.find((o) => o !== src && isUnder(src.relDir, o.relDir));
6851
+ if (nested) {
6852
+ problems.push(`${src.relDir}: is inside bundle directory ${nested.relDir} \u2014 bundle directories cannot nest`);
6853
+ }
6854
+ if (typeof src.pkg.name === "string" && src.pkg.name !== "") {
6855
+ const first = byName.get(src.pkg.name);
6856
+ if (first !== void 0) {
6857
+ problems.push(`${src.relDir}: duplicate bundle name ${JSON.stringify(src.pkg.name)} \u2014 already declared by ${first}`);
6858
+ } else {
6859
+ byName.set(src.pkg.name, src.relDir);
6860
+ }
6861
+ }
6862
+ problems.push(...declarationProblems(where2, src.pkg, membersOf(ws, src.relDir)));
6863
+ }
6864
+ return problems;
6865
+ }
6866
+ function toBundle(ws, relDir, dir, pkg) {
6781
6867
  return {
6782
- name: ws.pkg.name,
6783
- version: ws.pkg.version,
6784
- members: ws.plugins.filter((p) => !p.private),
6785
- optional: new Set(optional)
6868
+ name: pkg.name,
6869
+ version: pkg.version,
6870
+ dir,
6871
+ relDir,
6872
+ pkg,
6873
+ members: membersOf(ws, relDir),
6874
+ optional: new Set(optionalOf(pkg))
6786
6875
  };
6787
6876
  }
6877
+ function readBundle(ws) {
6878
+ return isBundleWorkspace(ws) ? toBundle(ws, "", ws.root, ws.pkg) : null;
6879
+ }
6880
+ function readBundles(ws) {
6881
+ const root = readBundle(ws);
6882
+ if (root) return [root];
6883
+ return bundleSources(ws).sources.filter((s) => s.pkg?.s2script?.kind === "bundle").map((s) => toBundle(ws, s.relDir, s.dir, s.pkg));
6884
+ }
6885
+ function bundleOfPlugin(bundles) {
6886
+ const out = /* @__PURE__ */ new Map();
6887
+ for (const b of bundles) for (const m of b.members) out.set(m.name, b.name);
6888
+ return out;
6889
+ }
6788
6890
  function bundleMembers(bundle) {
6789
6891
  const out = {};
6790
6892
  for (const m of bundle.members) {
@@ -6915,7 +7017,7 @@ function stampWorkspaceVersion(ws, version) {
6915
7017
  dirty = true;
6916
7018
  }
6917
7019
  }
6918
- if (dirty) writeFileSync4(join14(p.dir, "package.json"), `${JSON.stringify(next, null, 2)}
7020
+ if (dirty) writeFileSync4(join15(p.dir, "package.json"), `${JSON.stringify(next, null, 2)}
6919
7021
  `);
6920
7022
  }
6921
7023
  return result;
@@ -8396,11 +8498,11 @@ async function runWorkspace(argv2, root, interactive, filters, stampVersion) {
8396
8498
  import { resolve as resolve16 } from "node:path";
8397
8499
 
8398
8500
  // src/registry/deploy.ts
8399
- import { readdirSync as readdirSync6, readFileSync as readFileSync15 } from "node:fs";
8501
+ import { readdirSync as readdirSync6, readFileSync as readFileSync16 } from "node:fs";
8400
8502
  import { resolve as resolve15 } from "node:path";
8401
8503
 
8402
8504
  // src/types-pack.ts
8403
- import { readFileSync as readFileSync12 } from "node:fs";
8505
+ import { readFileSync as readFileSync13 } from "node:fs";
8404
8506
 
8405
8507
  // src/tar.ts
8406
8508
  import { gzipSync } from "node:zlib";
@@ -8456,7 +8558,7 @@ function packTar(files, opts = {}) {
8456
8558
 
8457
8559
  // src/types-pack.ts
8458
8560
  function packTypesTarball(input) {
8459
- const dts = readFileSync12(input.typesPath);
8561
+ const dts = readFileSync13(input.typesPath);
8460
8562
  const pkgJson = Buffer.from(
8461
8563
  JSON.stringify(
8462
8564
  {
@@ -8481,8 +8583,8 @@ function packTypesTarball(input) {
8481
8583
 
8482
8584
  // src/source-pack.ts
8483
8585
  var import_ignore = __toESM(require_ignore(), 1);
8484
- import { readdirSync as readdirSync5, readFileSync as readFileSync13, statSync as statSync5 } from "node:fs";
8485
- import { dirname as dirname6, join as join15, relative as relative3, resolve as resolve14, sep as sep4 } from "node:path";
8586
+ import { readdirSync as readdirSync5, readFileSync as readFileSync14, statSync as statSync5 } from "node:fs";
8587
+ import { dirname as dirname6, join as join16, relative as relative3, resolve as resolve14, sep as sep4 } from "node:path";
8486
8588
 
8487
8589
  // ../../node_modules/balanced-match/dist/esm/index.js
8488
8590
  var balanced = (a2, b, str) => {
@@ -10380,7 +10482,7 @@ function readIgnoreFile(dir) {
10380
10482
  for (const name of [".npmignore", ".gitignore"]) {
10381
10483
  let text3;
10382
10484
  try {
10383
- text3 = readFileSync13(join15(dir, name), "utf8");
10485
+ text3 = readFileSync14(join16(dir, name), "utf8");
10384
10486
  } catch {
10385
10487
  continue;
10386
10488
  }
@@ -10437,7 +10539,7 @@ function entryPoints(pkg) {
10437
10539
  function npmWorkspaceRoot(absDir) {
10438
10540
  for (let d = dirname6(absDir); ; d = dirname6(d)) {
10439
10541
  try {
10440
- const pj = JSON.parse(readFileSync13(join15(d, "package.json"), "utf8"));
10542
+ const pj = JSON.parse(readFileSync14(join16(d, "package.json"), "utf8"));
10441
10543
  if (pj && pj.workspaces) return d;
10442
10544
  } catch {
10443
10545
  }
@@ -10456,7 +10558,7 @@ function workspaceRulesets(absDir) {
10456
10558
  }
10457
10559
  function listPackageFiles(dir, pkg) {
10458
10560
  const absDir = resolve14(dir);
10459
- const pj = pkg ?? JSON.parse(readFileSync13(join15(absDir, "package.json"), "utf8"));
10561
+ const pj = pkg ?? JSON.parse(readFileSync14(join16(absDir, "package.json"), "utf8"));
10460
10562
  const files = filesPatterns(pj.files);
10461
10563
  const never = (0, import_ignore.default)().add(NEVER_PACKED);
10462
10564
  const forced = new Set(entryPoints(pj));
@@ -10469,7 +10571,7 @@ function listPackageFiles(dir, pkg) {
10469
10571
  (a2, b) => a2.name < b.name ? -1 : a2.name > b.name ? 1 : 0
10470
10572
  );
10471
10573
  for (const entry of entries) {
10472
- const childAbs = join15(abs, entry.name);
10574
+ const childAbs = join16(abs, entry.name);
10473
10575
  const rel = toPosix2(relative3(absDir, childAbs));
10474
10576
  if (entry.isSymbolicLink()) continue;
10475
10577
  if (entry.isDirectory()) {
@@ -10494,8 +10596,8 @@ function listPackageFiles(dir, pkg) {
10494
10596
  function packSourceTarball(dir, pkg) {
10495
10597
  const absDir = resolve14(dir);
10496
10598
  const files = listPackageFiles(absDir, pkg).map((path2) => {
10497
- const abs = join15(absDir, path2);
10498
- const content = readFileSync13(abs);
10599
+ const abs = join16(absDir, path2);
10600
+ const content = readFileSync14(abs);
10499
10601
  const mode = statSync5(abs).mode & 73 ? 493 : 420;
10500
10602
  return { path: path2, content, mode };
10501
10603
  });
@@ -10522,11 +10624,11 @@ function tarballFilename(name, version) {
10522
10624
  }
10523
10625
 
10524
10626
  // src/registry/credentials.ts
10525
- import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync14, writeFileSync as writeFileSync5 } from "node:fs";
10627
+ import { existsSync as existsSync12, mkdirSync as mkdirSync4, readFileSync as readFileSync15, writeFileSync as writeFileSync5 } from "node:fs";
10526
10628
  import { homedir } from "node:os";
10527
- import { join as join16 } from "node:path";
10629
+ import { join as join17 } from "node:path";
10528
10630
  function credentialsPath() {
10529
- return join16(homedir(), ".s2script", "credentials.json");
10631
+ return join17(homedir(), ".s2script", "credentials.json");
10530
10632
  }
10531
10633
  function normalizeRegistryUrl(url) {
10532
10634
  const trimmed = url.replace(/\/$/, "");
@@ -10544,9 +10646,9 @@ function loadCredentials() {
10544
10646
  return { registryUrl: defaultRegistryUrl(), token: fromEnv };
10545
10647
  }
10546
10648
  const p = credentialsPath();
10547
- if (!existsSync11(p)) return null;
10649
+ if (!existsSync12(p)) return null;
10548
10650
  try {
10549
- const j = JSON.parse(readFileSync14(p, "utf8"));
10651
+ const j = JSON.parse(readFileSync15(p, "utf8"));
10550
10652
  if (!j.token) return null;
10551
10653
  return {
10552
10654
  registryUrl: normalizeRegistryUrl(j.registryUrl || defaultRegistryUrl()),
@@ -10557,7 +10659,7 @@ function loadCredentials() {
10557
10659
  }
10558
10660
  }
10559
10661
  function saveCredentials(creds) {
10560
- const dir = join16(homedir(), ".s2script");
10662
+ const dir = join17(homedir(), ".s2script");
10561
10663
  mkdirSync4(dir, { recursive: true });
10562
10664
  writeFileSync5(
10563
10665
  credentialsPath(),
@@ -10808,7 +10910,7 @@ function readPackageReadme(pluginDir) {
10808
10910
  const names = readdirSync6(pluginDir).filter((n3) => /^readme(\.(md|markdown))?$/i.test(n3));
10809
10911
  if (names.length === 0) return null;
10810
10912
  const name = names.find((n3) => n3 === "README.md") ?? names.sort()[0];
10811
- const text3 = readFileSync15(resolve15(pluginDir, name), "utf8").trim();
10913
+ const text3 = readFileSync16(resolve15(pluginDir, name), "utf8").trim();
10812
10914
  if (!text3) return null;
10813
10915
  if (text3.length > MAX_README_CHARS) {
10814
10916
  throw new Error(
@@ -10832,7 +10934,7 @@ function assembleDeployArchive(pluginDir, pkg, outPath, opts = {}) {
10832
10934
  const absDir = resolve15(pluginDir);
10833
10935
  const access = opts.access ?? resolveAccess(pkg);
10834
10936
  if (packageKind(pkg) === "library") {
10835
- const lib = readFileSync15(outPath);
10937
+ const lib = readFileSync16(outPath);
10836
10938
  const manifestEntry2 = unzipSync(lib)["manifest.json"];
10837
10939
  if (!manifestEntry2) {
10838
10940
  throw new Error(`built archive missing manifest.json: ${outPath}`);
@@ -10849,7 +10951,7 @@ function assembleDeployArchive(pluginDir, pkg, outPath, opts = {}) {
10849
10951
  }
10850
10952
  const gate = assertPublishesTypes(pkg, absDir);
10851
10953
  if (!gate.ok) throw new Error(`publish gate failed: ${gate.error}`);
10852
- const s2sp = readFileSync15(outPath);
10954
+ const s2sp = readFileSync16(outPath);
10853
10955
  const manifestEntry = unzipSync(s2sp)["manifest.json"];
10854
10956
  if (!manifestEntry) {
10855
10957
  throw new Error(`built archive missing manifest.json: ${outPath}`);
@@ -10875,7 +10977,7 @@ function assembleDeployArchive(pluginDir, pkg, outPath, opts = {}) {
10875
10977
  }
10876
10978
  async function deployPlugin(opts) {
10877
10979
  const absDir = resolve15(opts.dir);
10878
- const pkg = JSON.parse(readFileSync15(resolve15(absDir, "package.json"), "utf8"));
10980
+ const pkg = JSON.parse(readFileSync16(resolve15(absDir, "package.json"), "utf8"));
10879
10981
  assertDeployable(pkg, absDir);
10880
10982
  const access = resolveAccess(pkg, opts.access);
10881
10983
  assertAccessAllowed(pkg.name ?? absDir, access);
@@ -10970,7 +11072,8 @@ async function uploadPlan(plan, built, client, opts = {}) {
10970
11072
  const archive = assembleDeployArchive(b.plugin.dir, b.plugin.pkg, b.outPath, {
10971
11073
  access: opts.access
10972
11074
  });
10973
- if (opts.bundle !== void 0) archive.manifest = { ...archive.manifest, bundle: opts.bundle };
11075
+ const bundle = typeof opts.bundle === "string" ? opts.bundle : opts.bundle?.get(entry.plugin.name);
11076
+ if (bundle !== void 0) archive.manifest = { ...archive.manifest, bundle };
10974
11077
  const res = await client.deploy(archive);
10975
11078
  results.push({ plugin: entry.plugin, status: "published", detail: res.reviewState });
10976
11079
  } catch (e) {
@@ -11132,11 +11235,17 @@ async function runWorkspaceDeploy(opts) {
11132
11235
  }
11133
11236
  const { ordered, built, unpublishableLibraries } = builtPluginsFromOutcomes(ws, build3.outcomes);
11134
11237
  const plan = await computePlan(ordered, (name, version) => isAlreadyPublished(client, name, version));
11135
- const bundle = readBundle(ws);
11136
- const bundleEntry = bundle ? await planBundle(bundle, client) : null;
11238
+ const inRun = new Set(ordered.map((p) => p.name));
11239
+ const bundles = readBundles(ws);
11240
+ const planned = bundles.filter((b) => b.members.every((m) => inRun.has(m.name)));
11241
+ const bundleEntries = [];
11242
+ for (const b of planned) bundleEntries.push(await planBundle(b, client));
11137
11243
  label("plan:");
11138
11244
  console.log(formatPlan(plan));
11139
- if (bundleEntry) console.log(formatBundlePlan(bundleEntry));
11245
+ for (const entry of bundleEntries) console.log(formatBundlePlan(entry));
11246
+ for (const b of bundles) {
11247
+ if (!planned.includes(b)) console.log(` ${b.name} ${b.version} skip (not every member is in --filter)`);
11248
+ }
11140
11249
  if (unpublishableLibraries.length > 0) {
11141
11250
  console.log(formatUnpublishableLibraries(unpublishableLibraries));
11142
11251
  }
@@ -11147,14 +11256,15 @@ async function runWorkspaceDeploy(opts) {
11147
11256
  assertAccessAllowed(e.plugin.name, a2);
11148
11257
  if (a2 === "restricted") restricted.push(e.plugin.name);
11149
11258
  }
11150
- if (bundleEntry?.reason === "PUBLISH") {
11151
- const a2 = accessFlag ?? resolveAccess(ws.pkg);
11152
- assertAccessAllowed(bundleEntry.bundle.name, a2);
11153
- if (a2 !== void 0) bundleEntry.manifest = { ...bundleEntry.manifest, access: a2 };
11154
- if (a2 === "restricted") restricted.push(bundleEntry.bundle.name);
11259
+ for (const entry of bundleEntries) {
11260
+ if (entry.reason !== "PUBLISH") continue;
11261
+ const a2 = accessFlag ?? resolveAccess(entry.bundle.pkg);
11262
+ assertAccessAllowed(entry.bundle.name, a2);
11263
+ if (a2 !== void 0) entry.manifest = { ...entry.manifest, access: a2 };
11264
+ if (a2 === "restricted") restricted.push(entry.bundle.name);
11155
11265
  }
11156
11266
  if (restricted.length > 0) console.log(` restricted: ${restricted.join(", ")}`);
11157
- const bundlePublishes = bundleEntry?.reason === "PUBLISH" ? 1 : 0;
11267
+ const bundlePublishes = bundleEntries.filter((e) => e.reason === "PUBLISH").length;
11158
11268
  const toPublish = publishCount(plan) + bundlePublishes;
11159
11269
  const noun = (n3) => `${n3} package${n3 === 1 ? "" : "s"}`;
11160
11270
  if (toPublish === 0) {
@@ -11186,7 +11296,7 @@ async function runWorkspaceDeploy(opts) {
11186
11296
  process.exit(130);
11187
11297
  }
11188
11298
  }
11189
- const results = await uploadPlan(plan, built, client, { bundle: bundle?.name, access: accessFlag });
11299
+ const results = await uploadPlan(plan, built, client, { bundle: bundleOfPlugin(bundles), access: accessFlag });
11190
11300
  let failures = 0;
11191
11301
  for (const r2 of results) {
11192
11302
  if (r2.status === "failed") failures++;
@@ -11197,8 +11307,9 @@ async function runWorkspaceDeploy(opts) {
11197
11307
  console.log(line);
11198
11308
  }
11199
11309
  }
11200
- if (bundleEntry) {
11201
- const r2 = await uploadBundle(bundleEntry, results, client);
11310
+ for (const entry of bundleEntries) {
11311
+ const members = new Set(entry.bundle.members.map((m) => m.name));
11312
+ const r2 = await uploadBundle(entry, results.filter((m) => members.has(m.plugin.name)), client);
11202
11313
  if (r2.status === "failed") failures++;
11203
11314
  const line = `${r2.bundle.name}@${r2.bundle.version} (bundle): ${r2.status}${r2.detail ? ` (${r2.detail})` : ""}`;
11204
11315
  if (interactive) {
@@ -11216,14 +11327,14 @@ async function runWorkspaceDeploy(opts) {
11216
11327
  import { resolve as resolve17 } from "node:path";
11217
11328
 
11218
11329
  // src/version/version.ts
11219
- import { existsSync as existsSync12 } from "node:fs";
11220
- import { join as join20 } from "node:path";
11330
+ import { existsSync as existsSync13 } from "node:fs";
11331
+ import { join as join21 } from "node:path";
11221
11332
 
11222
11333
  // src/version/changesets.ts
11223
11334
  var import_semver = __toESM(require_semver2(), 1);
11224
- import { readFileSync as readFileSync16 } from "node:fs";
11335
+ import { readFileSync as readFileSync17 } from "node:fs";
11225
11336
  import { createRequire as createRequire2 } from "node:module";
11226
- import { join as join17 } from "node:path";
11337
+ import { join as join18 } from "node:path";
11227
11338
  import { pathToFileURL } from "node:url";
11228
11339
  var SUPPORTED = {
11229
11340
  "@changesets/assemble-release-plan": ">=6.0.0 <7.0.0",
@@ -11243,7 +11354,7 @@ function named(mod, key) {
11243
11354
  return outer?.[key];
11244
11355
  }
11245
11356
  async function loadChangesets(workspaceRoot) {
11246
- const require3 = createRequire2(join17(workspaceRoot, "package.json"));
11357
+ const require3 = createRequire2(join18(workspaceRoot, "package.json"));
11247
11358
  const missing = [];
11248
11359
  const wrongVersion = [];
11249
11360
  const resolved = /* @__PURE__ */ new Map();
@@ -11258,7 +11369,7 @@ async function loadChangesets(workspaceRoot) {
11258
11369
  missing.push(name);
11259
11370
  continue;
11260
11371
  }
11261
- const version = String(JSON.parse(readFileSync16(manifest, "utf8")).version ?? "");
11372
+ const version = String(JSON.parse(readFileSync17(manifest, "utf8")).version ?? "");
11262
11373
  if (!(0, import_semver.satisfies)(version, range2, { includePrerelease: true })) {
11263
11374
  wrongVersion.push(`${name}@${version} (supported: ${range2})`);
11264
11375
  continue;
@@ -11358,8 +11469,8 @@ function mirrorPackages(ws) {
11358
11469
 
11359
11470
  // src/version/ranges.ts
11360
11471
  var import_semver2 = __toESM(require_semver2(), 1);
11361
- import { readFileSync as readFileSync17, writeFileSync as writeFileSync6 } from "node:fs";
11362
- import { join as join18 } from "node:path";
11472
+ import { readFileSync as readFileSync18, writeFileSync as writeFileSync6 } from "node:fs";
11473
+ import { join as join19 } from "node:path";
11363
11474
  var BUMP_LEVEL = { none: 0, patch: 1, minor: 2, major: 3 };
11364
11475
  function rangePrefix(range2) {
11365
11476
  if (range2.startsWith(">=")) return ">=";
@@ -11378,7 +11489,7 @@ function shouldUpdate(release, range2, config) {
11378
11489
  return BUMP_LEVEL[release.type] >= minLevel;
11379
11490
  }
11380
11491
  function readJsonFile(path2) {
11381
- const raw = readFileSync17(path2, "utf8");
11492
+ const raw = readFileSync18(path2, "utf8");
11382
11493
  const match2 = /^[ \t]*[{[]\s*\n([ \t]+)/.exec(raw);
11383
11494
  return {
11384
11495
  json: JSON.parse(raw),
@@ -11419,7 +11530,7 @@ function rewriteSiblingRanges(ws, releases, config) {
11419
11530
  for (const [consumerName, ifaceRanges] of edits) {
11420
11531
  const plugin = ws.plugins.find((p) => p.name === consumerName);
11421
11532
  if (plugin === void 0) continue;
11422
- const path2 = join18(plugin.dir, "package.json");
11533
+ const path2 = join19(plugin.dir, "package.json");
11423
11534
  const { json, indent, eol } = readJsonFile(path2);
11424
11535
  const s2 = json.s2script;
11425
11536
  if (s2?.pluginDependencies === void 0) continue;
@@ -11433,42 +11544,46 @@ function rewriteSiblingRanges(ws, releases, config) {
11433
11544
  // src/version/bundle-bump.ts
11434
11545
  var import_semver3 = __toESM(require_semver2(), 1);
11435
11546
  import { writeFileSync as writeFileSync7 } from "node:fs";
11436
- import { join as join19 } from "node:path";
11547
+ import { join as join20 } from "node:path";
11437
11548
  var LEVEL = { none: 0, patch: 1, minor: 2, major: 3 };
11438
- function plannedBundleBump(ws, releases) {
11439
- const bundle = readBundle(ws);
11440
- if (!bundle) return null;
11549
+ function plannedBundleBump(bundle, releases) {
11441
11550
  const members = new Set(bundle.members.map((m) => m.name));
11442
11551
  const memberReleases = releases.filter((r2) => members.has(r2.name) && r2.type !== "none");
11443
11552
  if (memberReleases.length === 0) return null;
11444
11553
  const type = memberReleases.reduce((t2, r2) => LEVEL[r2.type] > LEVEL[t2] ? r2.type : t2, "none");
11445
11554
  const to = (0, import_semver3.inc)(bundle.version, type);
11446
11555
  if (to === null) {
11447
- throw new Error(`s2s version: the bundle's version ${JSON.stringify(bundle.version)} is not valid semver`);
11556
+ throw new Error(
11557
+ `s2s version: the bundle ${bundle.name}'s version ${JSON.stringify(bundle.version)} is not valid semver`
11558
+ );
11448
11559
  }
11449
11560
  return {
11450
11561
  name: bundle.name,
11451
11562
  from: bundle.version,
11452
11563
  to,
11453
11564
  type,
11454
- because: memberReleases.map((r2) => `${r2.name}@${r2.newVersion}`)
11565
+ because: memberReleases.map((r2) => `${r2.name}@${r2.newVersion}`),
11566
+ path: join20(bundle.dir, "package.json")
11455
11567
  };
11456
11568
  }
11457
- function bumpBundle(ws, releases) {
11458
- const bump = plannedBundleBump(ws, releases);
11459
- if (!bump) return { bump: null, touchedFiles: [] };
11460
- const path2 = join19(ws.root, "package.json");
11461
- const { json, indent, eol } = readJsonFile(path2);
11462
- json.version = bump.to;
11463
- writeFileSync7(path2, JSON.stringify(json, null, indent) + eol);
11464
- return { bump, touchedFiles: [path2] };
11569
+ function plannedBundleBumps(ws, releases) {
11570
+ return readBundles(ws).map((b) => plannedBundleBump(b, releases)).filter((b) => b !== null);
11571
+ }
11572
+ function bumpBundles(ws, releases) {
11573
+ const bumps = plannedBundleBumps(ws, releases);
11574
+ for (const bump of bumps) {
11575
+ const { json, indent, eol } = readJsonFile(bump.path);
11576
+ json.version = bump.to;
11577
+ writeFileSync7(bump.path, JSON.stringify(json, null, indent) + eol);
11578
+ }
11579
+ return { bumps, touchedFiles: bumps.map((b) => b.path) };
11465
11580
  }
11466
11581
 
11467
11582
  // src/version/version.ts
11468
11583
  async function versionWorkspace(root, opts = {}) {
11469
11584
  const ws = loadWorkspace(root);
11470
- const configPath = join20(ws.root, ".changeset", "config.json");
11471
- if (!existsSync12(configPath)) {
11585
+ const configPath = join21(ws.root, ".changeset", "config.json");
11586
+ if (!existsSync13(configPath)) {
11472
11587
  throw new Error(
11473
11588
  `s2s version: ${configPath} does not exist \u2014 this workspace is not set up for changesets. Run \`npx changeset init\` at ${ws.root}.`
11474
11589
  );
@@ -11482,15 +11597,15 @@ async function versionWorkspace(root, opts = {}) {
11482
11597
  const plan = cs.assembleReleasePlan(changesets, mirror, config, preState);
11483
11598
  const touchedFiles = await cs.applyReleasePlan(plan, real, config);
11484
11599
  const ranges = rewriteSiblingRanges(ws, plan.releases, config);
11485
- const bundle = bumpBundle(ws, plan.releases);
11600
+ const bundles = bumpBundles(ws, plan.releases);
11486
11601
  return {
11487
11602
  root: ws.root,
11488
11603
  changesetCount: changesets.length,
11489
11604
  releases: plan.releases,
11490
11605
  rewrites: ranges.rewrites,
11491
11606
  skips: ranges.skips,
11492
- bundleBump: bundle.bump,
11493
- touchedFiles: [...touchedFiles, ...ranges.touchedFiles, ...bundle.touchedFiles],
11607
+ bundleBumps: bundles.bumps,
11608
+ touchedFiles: [...touchedFiles, ...ranges.touchedFiles, ...bundles.touchedFiles],
11494
11609
  changesetsVersions: cs.versions
11495
11610
  };
11496
11611
  }
@@ -11526,8 +11641,7 @@ async function run3(argv2) {
11526
11641
  for (const r2 of result.releases) {
11527
11642
  lines.push(`${r2.name} ${r2.oldVersion} -> ${r2.newVersion} (${r2.type})`);
11528
11643
  }
11529
- if (result.bundleBump) {
11530
- const b = result.bundleBump;
11644
+ for (const b of result.bundleBumps) {
11531
11645
  lines.push(`${b.name} ${b.from} -> ${b.to} (${b.type}, bundle \u2014 follows ${b.because.join(", ")})`);
11532
11646
  }
11533
11647
  for (const rw of result.rewrites) {
@@ -11555,15 +11669,15 @@ import { resolve as resolve19 } from "node:path";
11555
11669
  import {
11556
11670
  mkdirSync as mkdirSync6,
11557
11671
  writeFileSync as writeFileSync10,
11558
- readFileSync as readFileSync19,
11559
- existsSync as existsSync14
11672
+ readFileSync as readFileSync20,
11673
+ existsSync as existsSync15
11560
11674
  } from "node:fs";
11561
- import { join as join23, resolve as resolve18 } from "node:path";
11675
+ import { join as join24, resolve as resolve18 } from "node:path";
11562
11676
  import { gunzipSync } from "node:zlib";
11563
11677
 
11564
11678
  // src/registry/npmrc.ts
11565
- import { existsSync as existsSync13, readFileSync as readFileSync18, writeFileSync as writeFileSync8 } from "node:fs";
11566
- import { join as join21 } from "node:path";
11679
+ import { existsSync as existsSync14, readFileSync as readFileSync19, writeFileSync as writeFileSync8 } from "node:fs";
11680
+ import { join as join22 } from "node:path";
11567
11681
  function npmCompatRegistryUrl(registryUrl) {
11568
11682
  return `${registryUrl.replace(/\/$/, "")}/npm/`;
11569
11683
  }
@@ -11579,8 +11693,8 @@ function ensureScopeNpmrc(projectDir, packageName, registryUrl) {
11579
11693
  const compat = npmCompatRegistryUrl(registryUrl);
11580
11694
  const key = `@${scope}:registry`;
11581
11695
  const line = `${key}=${compat}`;
11582
- const npmrcPath = join21(projectDir, ".npmrc");
11583
- let existing = existsSync13(npmrcPath) ? readFileSync18(npmrcPath, "utf8") : "";
11696
+ const npmrcPath = join22(projectDir, ".npmrc");
11697
+ let existing = existsSync14(npmrcPath) ? readFileSync19(npmrcPath, "utf8") : "";
11584
11698
  const lines = existing.split(/\r?\n/);
11585
11699
  const idx = lines.findIndex((l2) => l2.trimStart().startsWith(`${key}=`) || l2.trimStart().startsWith(`${key} =`));
11586
11700
  if (idx >= 0) {
@@ -11596,7 +11710,7 @@ function ensureScopeNpmrc(projectDir, packageName, registryUrl) {
11596
11710
 
11597
11711
  // src/registry/lib-extract.ts
11598
11712
  import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync9 } from "node:fs";
11599
- import { join as join22 } from "node:path";
11713
+ import { join as join23 } from "node:path";
11600
11714
  var WANTED = /* @__PURE__ */ new Set(["index.js", "index.d.ts"]);
11601
11715
  function extractLibArchive(s2lib, outDir, meta) {
11602
11716
  const entries = unzipSync(new Uint8Array(s2lib));
@@ -11605,7 +11719,7 @@ function extractLibArchive(s2lib, outDir, meta) {
11605
11719
  mkdirSync5(outDir, { recursive: true });
11606
11720
  for (const [name, bytes] of Object.entries(entries)) {
11607
11721
  if (!WANTED.has(name)) continue;
11608
- writeFileSync9(join22(outDir, name), Buffer.from(bytes));
11722
+ writeFileSync9(join23(outDir, name), Buffer.from(bytes));
11609
11723
  }
11610
11724
  let apiVersion = "1.x";
11611
11725
  try {
@@ -11614,7 +11728,7 @@ function extractLibArchive(s2lib, outDir, meta) {
11614
11728
  } catch {
11615
11729
  }
11616
11730
  writeFileSync9(
11617
- join22(outDir, "package.json"),
11731
+ join23(outDir, "package.json"),
11618
11732
  JSON.stringify(
11619
11733
  { name: meta.name, version: meta.version, main: "index.js", types: "index.d.ts", s2script: { kind: "library", apiVersion } },
11620
11734
  null,
@@ -11651,13 +11765,13 @@ function extractTypesTarball(tgz, outDir) {
11651
11765
  if (!name || name.endsWith("/")) continue;
11652
11766
  const base = name.replace(/^package\//, "");
11653
11767
  if (base === "package.json") {
11654
- writeFileSync10(join23(outDir, "package.json"), content);
11768
+ writeFileSync10(join24(outDir, "package.json"), content);
11655
11769
  } else if (base === "api.d.ts" || base === "index.d.ts") {
11656
- writeFileSync10(join23(outDir, "index.d.ts"), content);
11770
+ writeFileSync10(join24(outDir, "index.d.ts"), content);
11657
11771
  wroteContract = true;
11658
11772
  }
11659
11773
  }
11660
- if (!wroteContract && !existsSync14(join23(outDir, "index.d.ts"))) {
11774
+ if (!wroteContract && !existsSync15(join24(outDir, "index.d.ts"))) {
11661
11775
  throw new Error("types tarball missing package/api.d.ts (or index.d.ts)");
11662
11776
  }
11663
11777
  }
@@ -11682,10 +11796,10 @@ async function addPackage(opts) {
11682
11796
  }
11683
11797
  if (resolved.kind === "library") {
11684
11798
  const s2lib = await client.downloadLib(name, resolved.version);
11685
- const libDir = join23(absDir, ".s2script", "libs", ...name.split("/"));
11799
+ const libDir = join24(absDir, ".s2script", "libs", ...name.split("/"));
11686
11800
  extractLibArchive(s2lib, libDir, { name, version: resolved.version });
11687
- const pkgPath2 = join23(absDir, "package.json");
11688
- const pkg2 = JSON.parse(readFileSync19(pkgPath2, "utf8"));
11801
+ const pkgPath2 = join24(absDir, "package.json");
11802
+ const pkg2 = JSON.parse(readFileSync20(pkgPath2, "utf8"));
11689
11803
  pkg2.s2script = pkg2.s2script ?? {};
11690
11804
  pkg2.s2script.libraries = pkg2.s2script.libraries ?? {};
11691
11805
  pkg2.s2script.libraries[name] = rangeForResolved(range2, resolved.version);
@@ -11706,10 +11820,10 @@ async function addPackage(opts) {
11706
11820
  throw new Error(`${name}@${resolved.version} has no types artifact (runtime-only package?)`);
11707
11821
  }
11708
11822
  const tgz = await client.downloadTypes(name, resolved.version);
11709
- const typesDir = join23(absDir, ".s2script", "types", ...name.split("/"));
11823
+ const typesDir = join24(absDir, ".s2script", "types", ...name.split("/"));
11710
11824
  extractTypesTarball(tgz, typesDir);
11711
- const pkgPath = join23(absDir, "package.json");
11712
- const pkg = JSON.parse(readFileSync19(pkgPath, "utf8"));
11825
+ const pkgPath = join24(absDir, "package.json");
11826
+ const pkg = JSON.parse(readFileSync20(pkgPath, "utf8"));
11713
11827
  pkg.s2script = pkg.s2script ?? {};
11714
11828
  pkg.s2script.pluginDependencies = pkg.s2script.pluginDependencies ?? {};
11715
11829
  pkg.s2script.pluginDependencies[name] = rangeForResolved(range2, resolved.version);
@@ -11790,37 +11904,37 @@ async function run4(argv2) {
11790
11904
  }
11791
11905
 
11792
11906
  // src/commands/install.ts
11793
- import { existsSync as existsSync19, writeFileSync as writeFileSync15 } from "node:fs";
11794
- import { join as join27 } from "node:path";
11907
+ import { existsSync as existsSync20, writeFileSync as writeFileSync15 } from "node:fs";
11908
+ import { join as join28 } from "node:path";
11795
11909
 
11796
11910
  // src/server/manifest.ts
11797
11911
  var import_semver4 = __toESM(require_semver2(), 1);
11798
- import { existsSync as existsSync15, readFileSync as readFileSync20, renameSync, writeFileSync as writeFileSync11 } from "node:fs";
11799
- import { basename as basename2, join as join24, resolve as resolve20 } from "node:path";
11912
+ import { existsSync as existsSync16, readFileSync as readFileSync21, renameSync, writeFileSync as writeFileSync11 } from "node:fs";
11913
+ import { basename as basename2, join as join25, resolve as resolve20 } from "node:path";
11800
11914
  var SERVER_MANIFEST = "plugins.json";
11801
11915
  var SERVER_LOCK = "plugins-lock.json";
11802
11916
  var PLUGINS_SUBDIR = "plugins";
11803
11917
  var DISABLED_SUBDIR = "disabled";
11804
11918
  function readJson(path2) {
11805
- const j = JSON.parse(readFileSync20(path2, "utf8"));
11919
+ const j = JSON.parse(readFileSync21(path2, "utf8"));
11806
11920
  if (!j || typeof j !== "object" || Array.isArray(j)) throw new Error(`${path2}: must be a JSON object`);
11807
11921
  return j;
11808
11922
  }
11809
11923
  function findServerRoot(start) {
11810
11924
  for (const dir of [resolve20(start), resolve20(start, "addons", "s2script")]) {
11811
- if (existsSync15(join24(dir, SERVER_MANIFEST))) return dir;
11925
+ if (existsSync16(join25(dir, SERVER_MANIFEST))) return dir;
11812
11926
  }
11813
11927
  return null;
11814
11928
  }
11815
11929
  function findAddonRoot(start) {
11816
11930
  const found = findServerRoot(start);
11817
11931
  if (found) return found;
11818
- if (existsSync15(resolve20(start, PLUGINS_SUBDIR))) return resolve20(start);
11932
+ if (existsSync16(resolve20(start, PLUGINS_SUBDIR))) return resolve20(start);
11819
11933
  const nested = resolve20(start, "addons", "s2script");
11820
- return existsSync15(nested) ? nested : null;
11934
+ return existsSync16(nested) ? nested : null;
11821
11935
  }
11822
11936
  function parseServerManifest(root, raw) {
11823
- const label = join24(root, SERVER_MANIFEST);
11937
+ const label = join25(root, SERVER_MANIFEST);
11824
11938
  const depsRaw = raw.dependencies ?? {};
11825
11939
  if (typeof depsRaw !== "object" || Array.isArray(depsRaw)) {
11826
11940
  throw new Error(`${label}: "dependencies" must be an object (plugin name \u2192 version range)`);
@@ -11869,8 +11983,8 @@ function parseServerManifest(root, raw) {
11869
11983
  };
11870
11984
  }
11871
11985
  function readServerManifest(root) {
11872
- const path2 = join24(root, SERVER_MANIFEST);
11873
- if (!existsSync15(path2)) throw new Error(`no ${SERVER_MANIFEST} in ${root} \u2014 run \`s2s server init\` first`);
11986
+ const path2 = join25(root, SERVER_MANIFEST);
11987
+ if (!existsSync16(path2)) throw new Error(`no ${SERVER_MANIFEST} in ${root} \u2014 run \`s2s server init\` first`);
11874
11988
  return parseServerManifest(root, readJson(path2));
11875
11989
  }
11876
11990
  function newServerManifestJson(name, dependencies) {
@@ -11884,15 +11998,15 @@ function writeServerManifest(m) {
11884
11998
  else delete out.with;
11885
11999
  if (m.disabled.length > 0) out.disabled = [...m.disabled].sort();
11886
12000
  else delete out.disabled;
11887
- const path2 = join24(m.root, SERVER_MANIFEST);
12001
+ const path2 = join25(m.root, SERVER_MANIFEST);
11888
12002
  const tmp = `${path2}.${process.pid}.tmp`;
11889
12003
  writeFileSync11(tmp, JSON.stringify(out, null, 2) + "\n");
11890
12004
  renameSync(tmp, path2);
11891
12005
  }
11892
12006
 
11893
12007
  // src/server/commands.ts
11894
- import { existsSync as existsSync18, readdirSync as readdirSync7, readFileSync as readFileSync23, writeFileSync as writeFileSync14 } from "node:fs";
11895
- import { join as join26, resolve as resolve22 } from "node:path";
12008
+ import { existsSync as existsSync19, readdirSync as readdirSync7, readFileSync as readFileSync24, writeFileSync as writeFileSync14 } from "node:fs";
12009
+ import { join as join27, resolve as resolve22 } from "node:path";
11896
12010
  var import_semver5 = __toESM(require_semver2(), 1);
11897
12011
 
11898
12012
  // src/registry/install.ts
@@ -11945,7 +12059,7 @@ async function resolveMerged(client, specs, withMembers = {}) {
11945
12059
 
11946
12060
  // src/server/lock.ts
11947
12061
  import { createHash as createHash4 } from "node:crypto";
11948
- import { existsSync as existsSync16, readFileSync as readFileSync21, renameSync as renameSync2, writeFileSync as writeFileSync12 } from "node:fs";
12062
+ import { existsSync as existsSync17, readFileSync as readFileSync22, renameSync as renameSync2, writeFileSync as writeFileSync12 } from "node:fs";
11949
12063
  var LOCKFILE_VERSION = 3;
11950
12064
  function integrityFromSha256Hex(hex) {
11951
12065
  return `sha256-${Buffer.from(hex, "hex").toString("base64")}`;
@@ -12019,8 +12133,8 @@ function parseLock(text3, label = "plugins-lock.json") {
12019
12133
  };
12020
12134
  }
12021
12135
  function readLock(path2) {
12022
- if (!existsSync16(path2)) return null;
12023
- return parseLock(readFileSync21(path2, "utf8"), path2);
12136
+ if (!existsSync17(path2)) return null;
12137
+ return parseLock(readFileSync22(path2, "utf8"), path2);
12024
12138
  }
12025
12139
  function writeLock(path2, lock) {
12026
12140
  const tmp = `${path2}.${process.pid}.tmp`;
@@ -12029,8 +12143,8 @@ function writeLock(path2, lock) {
12029
12143
  }
12030
12144
 
12031
12145
  // src/server/sync.ts
12032
- import { existsSync as existsSync17, mkdirSync as mkdirSync7, readFileSync as readFileSync22, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync13 } from "node:fs";
12033
- import { dirname as dirname7, join as join25, resolve as resolve21, sep as sep5 } from "node:path";
12146
+ import { existsSync as existsSync18, mkdirSync as mkdirSync7, readFileSync as readFileSync23, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync13 } from "node:fs";
12147
+ import { dirname as dirname7, join as join26, resolve as resolve21, sep as sep5 } from "node:path";
12034
12148
  function relPath(t2) {
12035
12149
  return t2.disabled ? `${DISABLED_SUBDIR}/${t2.filename}` : t2.filename;
12036
12150
  }
@@ -12111,18 +12225,18 @@ async function syncPlugins(opts) {
12111
12225
  try {
12112
12226
  for (const t2 of opts.targets) {
12113
12227
  const key = lockKey(relPath(t2));
12114
- const dest = join25(dir, relPath(t2));
12228
+ const dest = join26(dir, relPath(t2));
12115
12229
  result.plugins[key] = {
12116
12230
  name: t2.name,
12117
12231
  version: t2.version,
12118
12232
  resolved: t2.url,
12119
12233
  integrity: integrityFromSha256Hex(t2.sha256)
12120
12234
  };
12121
- if (existsSync17(dest) && sha256Hex(readFileSync22(dest)) === t2.sha256) {
12235
+ if (existsSync18(dest) && sha256Hex(readFileSync23(dest)) === t2.sha256) {
12122
12236
  result.kept.push(t2.filename);
12123
12237
  continue;
12124
12238
  }
12125
- const movedFrom = Object.entries(opts.oldLock?.plugins ?? {}).filter(([k, p]) => k !== key && p.name === t2.name).map(([k]) => join25(opts.root, k)).find((f) => existsSync17(f) && sha256Hex(readFileSync22(f)) === t2.sha256);
12239
+ const movedFrom = Object.entries(opts.oldLock?.plugins ?? {}).filter(([k, p]) => k !== key && p.name === t2.name).map(([k]) => join26(opts.root, k)).find((f) => existsSync18(f) && sha256Hex(readFileSync23(f)) === t2.sha256);
12126
12240
  if (movedFrom) {
12127
12241
  result.moved.push(t2.filename);
12128
12242
  if (opts.dryRun) log3(`would ${t2.disabled ? "disable" : "enable"} ${t2.name}@${t2.version}`);
@@ -12159,10 +12273,10 @@ async function syncPlugins(opts) {
12159
12273
  }
12160
12274
  for (const [key, old] of Object.entries(opts.oldLock?.plugins ?? {})) {
12161
12275
  if (key in result.plugins) continue;
12162
- const file = join25(opts.root, key);
12163
- if (!existsSync17(file)) continue;
12276
+ const file = join26(opts.root, key);
12277
+ if (!existsSync18(file)) continue;
12164
12278
  const want = sha256HexFromIntegrity(old.integrity);
12165
- if (want && sha256Hex(readFileSync22(file)) === want) {
12279
+ if (want && sha256Hex(readFileSync23(file)) === want) {
12166
12280
  if (opts.dryRun) log3(`would remove ${old.name}@${old.version} (${key})`);
12167
12281
  else {
12168
12282
  rmSync3(file);
@@ -12214,7 +12328,7 @@ async function serverInstall(opts) {
12214
12328
  }
12215
12329
  manifest = { ...manifest, with: { ...manifest.with, [parseSpec2(opts.specs[0]).name]: opts.with } };
12216
12330
  }
12217
- const lockPath = join26(opts.root, SERVER_LOCK);
12331
+ const lockPath = join27(opts.root, SERVER_LOCK);
12218
12332
  const lock = readLock(lockPath);
12219
12333
  if (opts.disabled) {
12220
12334
  if (!opts.specs?.length) throw new Error("--disabled needs the plugins to install: s2s install <name> --disabled");
@@ -12265,7 +12379,7 @@ async function serverUpdate(opts) {
12265
12379
  const log3 = opts.log ?? (() => {
12266
12380
  });
12267
12381
  const manifest = readServerManifest(opts.root);
12268
- const lockPath = join26(opts.root, SERVER_LOCK);
12382
+ const lockPath = join27(opts.root, SERVER_LOCK);
12269
12383
  const lock = readLock(lockPath);
12270
12384
  const names = opts.names && opts.names.length > 0 ? opts.names : Object.keys(manifest.dependencies);
12271
12385
  for (const n3 of names) {
@@ -12283,12 +12397,12 @@ async function serverUpdate(opts) {
12283
12397
  }
12284
12398
  function scanLocalPlugins(root, onUnreadable = () => {
12285
12399
  }) {
12286
- const dir = join26(root, PLUGINS_SUBDIR);
12287
- if (!existsSync18(dir)) return [];
12400
+ const dir = join27(root, PLUGINS_SUBDIR);
12401
+ if (!existsSync19(dir)) return [];
12288
12402
  const out = [];
12289
12403
  for (const entry of readdirSync7(dir, { withFileTypes: true })) {
12290
12404
  if (!entry.isFile() || !entry.name.endsWith(".s2sp")) continue;
12291
- const bytes = readFileSync23(join26(dir, entry.name));
12405
+ const bytes = readFileSync24(join27(dir, entry.name));
12292
12406
  try {
12293
12407
  const files = unzipSync(new Uint8Array(bytes), { filter: (f) => f.name === "manifest.json" });
12294
12408
  const m = JSON.parse(Buffer.from(files["manifest.json"]).toString("utf8"));
@@ -12302,8 +12416,8 @@ function scanLocalPlugins(root, onUnreadable = () => {
12302
12416
  }
12303
12417
  async function serverInit(opts) {
12304
12418
  const root = resolve22(opts.root);
12305
- const pkgPath = join26(root, SERVER_MANIFEST);
12306
- if (existsSync18(pkgPath) && !opts.force) {
12419
+ const pkgPath = join27(root, SERVER_MANIFEST);
12420
+ if (existsSync19(pkgPath) && !opts.force) {
12307
12421
  throw new Error(`${pkgPath} already exists \u2014 pass --force to replace it`);
12308
12422
  }
12309
12423
  const report = { managed: [], unmanaged: [] };
@@ -12344,7 +12458,7 @@ async function serverInit(opts) {
12344
12458
  const raw = newServerManifestJson(opts.name ?? "s2script-server", dependencies);
12345
12459
  writeFileSync14(pkgPath, JSON.stringify(raw, null, 2) + "\n");
12346
12460
  const manifest = parseServerManifest(root, raw);
12347
- writeLock(join26(root, SERVER_LOCK), lockFor(manifest, plugins));
12461
+ writeLock(join27(root, SERVER_LOCK), lockFor(manifest, plugins));
12348
12462
  return report;
12349
12463
  }
12350
12464
  async function serverSetEnabled(opts) {
@@ -12362,7 +12476,7 @@ async function serverSetEnabled(opts) {
12362
12476
  else set.add(n3);
12363
12477
  }
12364
12478
  manifest = { ...manifest, disabled: [...set].sort() };
12365
- const lockPath = join26(opts.root, SERVER_LOCK);
12479
+ const lockPath = join27(opts.root, SERVER_LOCK);
12366
12480
  const lock = readLock(lockPath);
12367
12481
  const specs = pinnedSpecs(manifest, lock, sat);
12368
12482
  const { targets } = await resolveTargets(opts.client, specs, manifest.with, {
@@ -12434,11 +12548,11 @@ async function run5(argv2) {
12434
12548
  }
12435
12549
  await runServer(async () => {
12436
12550
  const withMembers = collectWith(argv2);
12437
- if (!existsSync19(join27(root, SERVER_MANIFEST))) {
12551
+ if (!existsSync20(join28(root, SERVER_MANIFEST))) {
12438
12552
  if (dryRun) throw new Error(`no ${SERVER_MANIFEST} in ${root} yet \u2014 run without --dry-run to create one`);
12439
12553
  const fresh = newServerManifestJson("s2script-server", {});
12440
- writeFileSync15(join27(root, SERVER_MANIFEST), JSON.stringify(fresh, null, 2) + "\n");
12441
- console.log(`created ${join27(root, SERVER_MANIFEST)}`);
12554
+ writeFileSync15(join28(root, SERVER_MANIFEST), JSON.stringify(fresh, null, 2) + "\n");
12555
+ console.log(`created ${join28(root, SERVER_MANIFEST)}`);
12442
12556
  }
12443
12557
  return serverInstall({
12444
12558
  root,
@@ -12459,26 +12573,26 @@ import { resolve as resolve25 } from "node:path";
12459
12573
  import {
12460
12574
  mkdirSync as mkdirSync8,
12461
12575
  writeFileSync as writeFileSync16,
12462
- existsSync as existsSync20,
12576
+ existsSync as existsSync21,
12463
12577
  readdirSync as readdirSync8,
12464
- readFileSync as readFileSync24,
12578
+ readFileSync as readFileSync25,
12465
12579
  symlinkSync
12466
12580
  } from "node:fs";
12467
- import { dirname as dirname8, isAbsolute, join as join28, relative as relative4, resolve as resolve23, basename as basename4 } from "node:path";
12581
+ import { dirname as dirname8, isAbsolute, join as join29, relative as relative4, resolve as resolve23, basename as basename4 } from "node:path";
12468
12582
  import { spawnSync } from "node:child_process";
12469
12583
  import { fileURLToPath as fileURLToPath2 } from "node:url";
12470
12584
  function readCliVersion() {
12471
12585
  const here = dirname8(fileURLToPath2(import.meta.url));
12472
12586
  const candidates = [
12473
- join28(here, "..", "package.json"),
12587
+ join29(here, "..", "package.json"),
12474
12588
  // dist/cli.js → packages/sdk/package.json
12475
- join28(here, "..", "..", "package.json")
12589
+ join29(here, "..", "..", "package.json")
12476
12590
  // src/create → packages/sdk/package.json
12477
12591
  ];
12478
12592
  for (const p of candidates) {
12479
- if (!existsSync20(p)) continue;
12593
+ if (!existsSync21(p)) continue;
12480
12594
  try {
12481
- const pkg = JSON.parse(readFileSync24(p, "utf8"));
12595
+ const pkg = JSON.parse(readFileSync25(p, "utf8"));
12482
12596
  if (pkg.name === "@s2script/sdk" && pkg.version) return pkg.version;
12483
12597
  } catch {
12484
12598
  }
@@ -12488,9 +12602,9 @@ function readCliVersion() {
12488
12602
  function findLocalPackagesDir() {
12489
12603
  const here = dirname8(fileURLToPath2(import.meta.url));
12490
12604
  const candidates = [
12491
- join28(here, "..", ".."),
12605
+ join29(here, "..", ".."),
12492
12606
  // dist/cli.js → packages
12493
- join28(here, "..", "..", "..")
12607
+ join29(here, "..", "..", "..")
12494
12608
  // src/create → packages
12495
12609
  ];
12496
12610
  for (const c2 of candidates) {
@@ -12506,7 +12620,7 @@ function defaultNameFromPath(dir, kind = "plugin") {
12506
12620
  return `@${kind}/${slugifyBasename(dir)}`;
12507
12621
  }
12508
12622
  function assertEmptyTarget(targetPath) {
12509
- if (!existsSync20(targetPath)) return;
12623
+ if (!existsSync21(targetPath)) return;
12510
12624
  const kids = readdirSync8(targetPath);
12511
12625
  const meaningful = kids.filter((k) => k !== ".git");
12512
12626
  if (meaningful.length) {
@@ -12555,8 +12669,8 @@ function registryDevDeps(game, sdkVersion, resolve29 = resolvePublishedVersion)
12555
12669
  function fileDevDeps(packagesDir, game) {
12556
12670
  const deps = {};
12557
12671
  for (const n3 of createPackageNames(game)) {
12558
- const abs = join28(packagesDir, n3);
12559
- if (!existsSync20(join28(abs, "package.json"))) return void 0;
12672
+ const abs = join29(packagesDir, n3);
12673
+ if (!existsSync21(join29(abs, "package.json"))) return void 0;
12560
12674
  deps[`@s2script/${n3}`] = `file:${abs}`;
12561
12675
  }
12562
12676
  return deps;
@@ -12621,7 +12735,7 @@ function tsconfigJson() {
12621
12735
  ) + "\n";
12622
12736
  }
12623
12737
  function workspaceTsconfigJson(targetPath, workspaceRoot) {
12624
- const rel = relative4(targetPath, join28(workspaceRoot, "tsconfig.base.json")).replace(/\\/g, "/");
12738
+ const rel = relative4(targetPath, join29(workspaceRoot, "tsconfig.base.json")).replace(/\\/g, "/");
12625
12739
  return JSON.stringify(
12626
12740
  {
12627
12741
  extends: rel.startsWith(".") ? rel : `./${rel}`,
@@ -12697,10 +12811,10 @@ dist/
12697
12811
  `;
12698
12812
  }
12699
12813
  function linkLocalPackagesForNoInstall(targetPath, localPackagesDir) {
12700
- const nm = join28(targetPath, "node_modules");
12814
+ const nm = join29(targetPath, "node_modules");
12701
12815
  mkdirSync8(nm, { recursive: true });
12702
- const dest = join28(nm, "@s2script");
12703
- if (!existsSync20(dest)) symlinkSync(localPackagesDir, dest);
12816
+ const dest = join29(nm, "@s2script");
12817
+ if (!existsSync21(dest)) symlinkSync(localPackagesDir, dest);
12704
12818
  }
12705
12819
  function runInstall(dir, pm) {
12706
12820
  if (pm === "none") return;
@@ -12729,7 +12843,7 @@ async function createPlugin(opts = {}) {
12729
12843
  );
12730
12844
  }
12731
12845
  let slug = workspaceRoot !== void 0 && opts.path !== void 0 ? assertBareSlug(opts.path) : opts.path;
12732
- let targetPath = workspaceRoot === void 0 ? resolve23(opts.path ?? ".") : slug === void 0 ? void 0 : join28(workspaceRoot, "plugins", slug);
12846
+ let targetPath = workspaceRoot === void 0 ? resolve23(opts.path ?? ".") : slug === void 0 ? void 0 : join29(workspaceRoot, "plugins", slug);
12733
12847
  if (targetPath !== void 0) assertEmptyTarget(targetPath);
12734
12848
  if (interactive) {
12735
12849
  intro2("Create an s2script plugin");
@@ -12739,7 +12853,7 @@ async function createPlugin(opts = {}) {
12739
12853
  slug = assertBareSlug(
12740
12854
  await text2({ message: "Plugin name (directory under plugins/)", placeholder: "myplugin" })
12741
12855
  );
12742
- targetPath = join28(workspaceRoot, "plugins", slug);
12856
+ targetPath = join29(workspaceRoot, "plugins", slug);
12743
12857
  assertEmptyTarget(targetPath);
12744
12858
  }
12745
12859
  }
@@ -12802,37 +12916,37 @@ async function createPlugin(opts = {}) {
12802
12916
  install = workspaceRoot !== void 0 ? "none" : install ?? (opts.noInstall ? "none" : "npm");
12803
12917
  const finalTargetPath = targetPath;
12804
12918
  const localPackagesDir = workspaceRoot === void 0 ? findLocalPackagesDir() : void 0;
12805
- mkdirSync8(join28(finalTargetPath, "src"), { recursive: true });
12919
+ mkdirSync8(join29(finalTargetPath, "src"), { recursive: true });
12806
12920
  if (workspaceRoot !== void 0) {
12807
- writeFileSync16(join28(finalTargetPath, "package.json"), minimalPackageJsonContent(name));
12921
+ writeFileSync16(join29(finalTargetPath, "package.json"), minimalPackageJsonContent(name));
12808
12922
  writeFileSync16(
12809
- join28(finalTargetPath, "tsconfig.json"),
12923
+ join29(finalTargetPath, "tsconfig.json"),
12810
12924
  workspaceTsconfigJson(finalTargetPath, workspaceRoot)
12811
12925
  );
12812
12926
  } else if (kind === "library") {
12813
12927
  const version = readCliVersion();
12814
12928
  writeFileSync16(
12815
- join28(finalTargetPath, "package.json"),
12929
+ join29(finalTargetPath, "package.json"),
12816
12930
  libraryPackageJsonContent(name, game, version, localPackagesDir)
12817
12931
  );
12818
- writeFileSync16(join28(finalTargetPath, "tsconfig.json"), tsconfigJson());
12819
- writeFileSync16(join28(finalTargetPath, "eslint.config.mjs"), eslintConfig());
12932
+ writeFileSync16(join29(finalTargetPath, "tsconfig.json"), tsconfigJson());
12933
+ writeFileSync16(join29(finalTargetPath, "eslint.config.mjs"), eslintConfig());
12820
12934
  } else {
12821
12935
  const version = readCliVersion();
12822
12936
  writeFileSync16(
12823
- join28(finalTargetPath, "package.json"),
12937
+ join29(finalTargetPath, "package.json"),
12824
12938
  packageJsonContent(name, game, version, localPackagesDir)
12825
12939
  );
12826
- writeFileSync16(join28(finalTargetPath, "tsconfig.json"), tsconfigJson());
12827
- writeFileSync16(join28(finalTargetPath, "eslint.config.mjs"), eslintConfig());
12940
+ writeFileSync16(join29(finalTargetPath, "tsconfig.json"), tsconfigJson());
12941
+ writeFileSync16(join29(finalTargetPath, "eslint.config.mjs"), eslintConfig());
12828
12942
  }
12829
12943
  if (kind === "library") {
12830
- writeFileSync16(join28(finalTargetPath, "src", "index.ts"), LIBRARY_ENTRY);
12831
- writeFileSync16(join28(finalTargetPath, "src", "index.d.ts"), LIBRARY_TYPES);
12944
+ writeFileSync16(join29(finalTargetPath, "src", "index.ts"), LIBRARY_ENTRY);
12945
+ writeFileSync16(join29(finalTargetPath, "src", "index.d.ts"), LIBRARY_TYPES);
12832
12946
  } else {
12833
- writeFileSync16(join28(finalTargetPath, "src", "plugin.ts"), pluginSource(game));
12947
+ writeFileSync16(join29(finalTargetPath, "src", "plugin.ts"), pluginSource(game));
12834
12948
  }
12835
- writeFileSync16(join28(finalTargetPath, ".gitignore"), gitignore());
12949
+ writeFileSync16(join29(finalTargetPath, ".gitignore"), gitignore());
12836
12950
  let installed = false;
12837
12951
  if (workspaceRoot === void 0) {
12838
12952
  if (install !== "none") {
@@ -12860,7 +12974,7 @@ async function createPlugin(opts = {}) {
12860
12974
 
12861
12975
  // src/create/workspace.ts
12862
12976
  import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync17 } from "node:fs";
12863
- import { join as join29, resolve as resolve24 } from "node:path";
12977
+ import { join as join30, resolve as resolve24 } from "node:path";
12864
12978
  var ROOT_DEFAULT_GAME = "cs2";
12865
12979
  var CHANGESETS_CLI_RANGE = "^2.29.5";
12866
12980
  function defaultWorkspaceName(dir) {
@@ -12944,14 +13058,14 @@ async function createWorkspace(opts = {}) {
12944
13058
  name = name ?? defaultWorkspaceName(targetPath);
12945
13059
  const version = readCliVersion();
12946
13060
  const localPackagesDir = findLocalPackagesDir();
12947
- mkdirSync9(join29(targetPath, "plugins"), { recursive: true });
12948
- mkdirSync9(join29(targetPath, "packages"), { recursive: true });
12949
- mkdirSync9(join29(targetPath, ".changeset"), { recursive: true });
12950
- writeFileSync17(join29(targetPath, "package.json"), rootPackageJsonContent(name, version, localPackagesDir));
12951
- writeFileSync17(join29(targetPath, "tsconfig.base.json"), tsconfigBaseJson());
12952
- writeFileSync17(join29(targetPath, "eslint.config.mjs"), eslintConfig());
12953
- writeFileSync17(join29(targetPath, ".changeset", "config.json"), changesetConfigJson());
12954
- writeFileSync17(join29(targetPath, ".gitignore"), gitignore());
13061
+ mkdirSync9(join30(targetPath, "plugins"), { recursive: true });
13062
+ mkdirSync9(join30(targetPath, "packages"), { recursive: true });
13063
+ mkdirSync9(join30(targetPath, ".changeset"), { recursive: true });
13064
+ writeFileSync17(join30(targetPath, "package.json"), rootPackageJsonContent(name, version, localPackagesDir));
13065
+ writeFileSync17(join30(targetPath, "tsconfig.base.json"), tsconfigBaseJson());
13066
+ writeFileSync17(join30(targetPath, "eslint.config.mjs"), eslintConfig());
13067
+ writeFileSync17(join30(targetPath, ".changeset", "config.json"), changesetConfigJson());
13068
+ writeFileSync17(join30(targetPath, ".gitignore"), gitignore());
12955
13069
  return { dir: targetPath, name };
12956
13070
  }
12957
13071
 
@@ -13187,8 +13301,8 @@ async function run7(argv2) {
13187
13301
  }
13188
13302
 
13189
13303
  // src/config/gen.ts
13190
- import { readFileSync as readFileSync25, writeFileSync as writeFileSync18, mkdirSync as mkdirSync10 } from "node:fs";
13191
- import { join as join30 } from "node:path";
13304
+ import { readFileSync as readFileSync26, writeFileSync as writeFileSync18, mkdirSync as mkdirSync10 } from "node:fs";
13305
+ import { join as join31 } from "node:path";
13192
13306
 
13193
13307
  // src/config/config-path.ts
13194
13308
  function configFileName(id) {
@@ -13235,12 +13349,12 @@ function genConfigFromManifest(manifest, outDir) {
13235
13349
  const id = typeof manifest.id === "string" ? manifest.id : "";
13236
13350
  if (!id) throw new Error("config gen: manifest declares config but has no id");
13237
13351
  mkdirSync10(outDir, { recursive: true });
13238
- const path2 = join30(outDir, configFileName(id));
13352
+ const path2 = join31(outDir, configFileName(id));
13239
13353
  writeFileSync18(path2, generateDefaultJsonc(config));
13240
13354
  return path2;
13241
13355
  }
13242
13356
  function genConfigForS2sp(s2spPath, outDir) {
13243
- const entry = unzipSync(readFileSync25(s2spPath))["manifest.json"];
13357
+ const entry = unzipSync(readFileSync26(s2spPath))["manifest.json"];
13244
13358
  if (!entry) throw new Error(`config gen: ${s2spPath} has no manifest.json`);
13245
13359
  const manifest = JSON.parse(Buffer.from(entry).toString("utf8"));
13246
13360
  return genConfigFromManifest(manifest, outDir);
@@ -13287,12 +13401,12 @@ async function run8(argv2) {
13287
13401
  }
13288
13402
 
13289
13403
  // src/commands/codegen.ts
13290
- import { dirname as dirname9, join as join35 } from "node:path";
13404
+ import { dirname as dirname9, join as join36 } from "node:path";
13291
13405
  import { fileURLToPath as fileURLToPath3 } from "node:url";
13292
13406
 
13293
13407
  // src/schemagen/gen.ts
13294
- import { readFileSync as readFileSync26, writeFileSync as writeFileSync19 } from "node:fs";
13295
- import { join as join31 } from "node:path";
13408
+ import { readFileSync as readFileSync27, writeFileSync as writeFileSync19 } from "node:fs";
13409
+ import { join as join32 } from "node:path";
13296
13410
 
13297
13411
  // src/schemagen/model.ts
13298
13412
  function enumMemberNames(enumName, members) {
@@ -13841,16 +13955,16 @@ var EMBED_PATH = "games/cs2/codegen-embedded.json";
13841
13955
  var JS_OUT = "games/cs2/js/schema.generated.js";
13842
13956
  var DTS_OUT = "packages/cs2/schema.generated.d.ts";
13843
13957
  function runGenSchema(repoRoot2, opts) {
13844
- const catalog = JSON.parse(readFileSync26(join31(repoRoot2, CATALOG_PATH), "utf8"));
13845
- const list = JSON.parse(readFileSync26(join31(repoRoot2, LIST_PATH), "utf8"));
13958
+ const catalog = JSON.parse(readFileSync27(join32(repoRoot2, CATALOG_PATH), "utf8"));
13959
+ const list = JSON.parse(readFileSync27(join32(repoRoot2, LIST_PATH), "utf8"));
13846
13960
  let embed = [];
13847
13961
  try {
13848
- embed = JSON.parse(readFileSync26(join31(repoRoot2, EMBED_PATH), "utf8"));
13962
+ embed = JSON.parse(readFileSync27(join32(repoRoot2, EMBED_PATH), "utf8"));
13849
13963
  } catch {
13850
13964
  }
13851
13965
  let enumCatalog = {};
13852
13966
  try {
13853
- enumCatalog = JSON.parse(readFileSync26(join31(repoRoot2, ENUMS_PATH), "utf8"));
13967
+ enumCatalog = JSON.parse(readFileSync27(join32(repoRoot2, ENUMS_PATH), "utf8"));
13854
13968
  } catch {
13855
13969
  }
13856
13970
  const model = buildModel(catalog, list, embed, enumCatalog);
@@ -13861,11 +13975,11 @@ function runGenSchema(repoRoot2, opts) {
13861
13975
  const drift = [];
13862
13976
  const files = [[JS_OUT, js], [DTS_OUT, dts]];
13863
13977
  for (const [rel, content] of files) {
13864
- const abs = join31(repoRoot2, rel);
13978
+ const abs = join32(repoRoot2, rel);
13865
13979
  if (opts.check) {
13866
13980
  let cur = "";
13867
13981
  try {
13868
- cur = readFileSync26(abs, "utf8");
13982
+ cur = readFileSync27(abs, "utf8");
13869
13983
  } catch {
13870
13984
  }
13871
13985
  if (cur !== content) drift.push(rel);
@@ -13880,8 +13994,8 @@ function runGenSchema(repoRoot2, opts) {
13880
13994
  }
13881
13995
 
13882
13996
  // src/eventgen/gen.ts
13883
- import { readFileSync as readFileSync27, writeFileSync as writeFileSync20 } from "node:fs";
13884
- import { join as join32 } from "node:path";
13997
+ import { readFileSync as readFileSync28, writeFileSync as writeFileSync20 } from "node:fs";
13998
+ import { join as join33 } from "node:path";
13885
13999
 
13886
14000
  // src/eventgen/model.ts
13887
14001
  var TYPE_TO_GETTER = {
@@ -13966,15 +14080,15 @@ function emitEventDts(model) {
13966
14080
  var CATALOG_PATH2 = "games/cs2/gamedata/event-catalog.json";
13967
14081
  var DTS_OUT2 = "packages/cs2/events.generated.d.ts";
13968
14082
  function runGenEvents(repoRoot2, opts) {
13969
- const catalog = JSON.parse(readFileSync27(join32(repoRoot2, CATALOG_PATH2), "utf8"));
14083
+ const catalog = JSON.parse(readFileSync28(join33(repoRoot2, CATALOG_PATH2), "utf8"));
13970
14084
  const model = buildEventModel(catalog);
13971
14085
  const dts = emitEventDts(model);
13972
14086
  const drift = [];
13973
- const abs = join32(repoRoot2, DTS_OUT2);
14087
+ const abs = join33(repoRoot2, DTS_OUT2);
13974
14088
  if (opts.check) {
13975
14089
  let cur = "";
13976
14090
  try {
13977
- cur = readFileSync27(abs, "utf8");
14091
+ cur = readFileSync28(abs, "utf8");
13978
14092
  } catch {
13979
14093
  }
13980
14094
  if (cur !== dts) drift.push(DTS_OUT2);
@@ -13985,8 +14099,8 @@ function runGenEvents(repoRoot2, opts) {
13985
14099
  }
13986
14100
 
13987
14101
  // src/navgen/gen.ts
13988
- import { readFileSync as readFileSync28, writeFileSync as writeFileSync21 } from "node:fs";
13989
- import { join as join33 } from "node:path";
14102
+ import { readFileSync as readFileSync29, writeFileSync as writeFileSync21 } from "node:fs";
14103
+ import { join as join34 } from "node:path";
13990
14104
 
13991
14105
  // src/navgen/model.ts
13992
14106
  var SUPPORTED_NAV_KINDS = /* @__PURE__ */ new Set([
@@ -14200,8 +14314,8 @@ var CATALOG_PATH3 = "games/cs2/gamedata/schema-catalog.json";
14200
14314
  var JS_OUT2 = "games/cs2/js/nav.generated.js";
14201
14315
  var DTS_OUT3 = "packages/cs2/nav.generated.d.ts";
14202
14316
  function runGenNav(repoRoot2, opts) {
14203
- const config = JSON.parse(readFileSync28(join33(repoRoot2, TARGETS_PATH), "utf8"));
14204
- const catalog = JSON.parse(readFileSync28(join33(repoRoot2, CATALOG_PATH3), "utf8"));
14317
+ const config = JSON.parse(readFileSync29(join34(repoRoot2, TARGETS_PATH), "utf8"));
14318
+ const catalog = JSON.parse(readFileSync29(join34(repoRoot2, CATALOG_PATH3), "utf8"));
14205
14319
  const model = buildNavModel(config, catalog);
14206
14320
  const js = emitNavJs(model);
14207
14321
  const dts = emitNavDts(model);
@@ -14209,11 +14323,11 @@ function runGenNav(repoRoot2, opts) {
14209
14323
  const drift = [];
14210
14324
  const files = [[JS_OUT2, js], [DTS_OUT3, dts]];
14211
14325
  for (const [rel, content] of files) {
14212
- const abs = join33(repoRoot2, rel);
14326
+ const abs = join34(repoRoot2, rel);
14213
14327
  if (opts.check) {
14214
14328
  let cur = "";
14215
14329
  try {
14216
- cur = readFileSync28(abs, "utf8");
14330
+ cur = readFileSync29(abs, "utf8");
14217
14331
  } catch {
14218
14332
  }
14219
14333
  if (cur !== content) drift.push(rel);
@@ -14225,22 +14339,22 @@ function runGenNav(repoRoot2, opts) {
14225
14339
  }
14226
14340
 
14227
14341
  // src/hookgen/gen.ts
14228
- import { readFileSync as readFileSync29, writeFileSync as writeFileSync22 } from "node:fs";
14229
- import { join as join34 } from "node:path";
14342
+ import { readFileSync as readFileSync30, writeFileSync as writeFileSync22 } from "node:fs";
14343
+ import { join as join35 } from "node:path";
14230
14344
  var GAMEDATA_PATH = "games/cs2/gamedata/game.cs2.jsonc";
14231
14345
  var DTS_OUT4 = "packages/cs2/hooks.generated.d.ts";
14232
14346
  function runGenHooks(repoRoot2, opts) {
14233
- const raw = readFileSync29(join34(repoRoot2, GAMEDATA_PATH), "utf8");
14347
+ const raw = readFileSync30(join35(repoRoot2, GAMEDATA_PATH), "utf8");
14234
14348
  const gd = JSON.parse(stripJsonComments(raw));
14235
14349
  const hooks = gd.hooks ?? {};
14236
14350
  const model = buildHookModel(hooks);
14237
14351
  const dts = emitHookDts(model);
14238
14352
  const drift = [];
14239
- const abs = join34(repoRoot2, DTS_OUT4);
14353
+ const abs = join35(repoRoot2, DTS_OUT4);
14240
14354
  if (opts.check) {
14241
14355
  let cur = "";
14242
14356
  try {
14243
- cur = readFileSync29(abs, "utf8");
14357
+ cur = readFileSync30(abs, "utf8");
14244
14358
  } catch {
14245
14359
  }
14246
14360
  if (cur !== dts) drift.push(DTS_OUT4);
@@ -14253,7 +14367,7 @@ function runGenHooks(repoRoot2, opts) {
14253
14367
 
14254
14368
  // src/commands/codegen.ts
14255
14369
  function repoRoot() {
14256
- return join35(dirname9(fileURLToPath3(import.meta.url)), "..", "..", "..");
14370
+ return join36(dirname9(fileURLToPath3(import.meta.url)), "..", "..", "..");
14257
14371
  }
14258
14372
  async function run9(kind, argv2) {
14259
14373
  const root = repoRoot();
@@ -14310,9 +14424,9 @@ async function run9(kind, argv2) {
14310
14424
  }
14311
14425
 
14312
14426
  // src/commands/inspect.ts
14313
- import { readFileSync as readFileSync30 } from "node:fs";
14427
+ import { readFileSync as readFileSync31 } from "node:fs";
14314
14428
  function inspectArchive(path2) {
14315
- const files = unzipSync(readFileSync30(path2));
14429
+ const files = unzipSync(readFileSync31(path2));
14316
14430
  if (!files["manifest.json"]) throw new Error(`${path2}: missing manifest.json`);
14317
14431
  const manifest = JSON.parse(Buffer.from(files["manifest.json"]).toString("utf8"));
14318
14432
  const lines = [`plugin: ${manifest.id ?? "(unknown)"}`];
@@ -14341,8 +14455,8 @@ async function run10(argv2) {
14341
14455
  }
14342
14456
 
14343
14457
  // src/engine-functions/migrate-v1.ts
14344
- import { closeSync, existsSync as existsSync21, linkSync, lstatSync as lstatSync2, mkdirSync as mkdirSync11, openSync, readFileSync as readFileSync31, readdirSync as readdirSync9, renameSync as renameSync4, unlinkSync, writeFileSync as writeFileSync23 } from "node:fs";
14345
- import { dirname as dirname10, isAbsolute as isAbsolute2, join as join36, relative as relative5, resolve as resolve26, sep as sep6 } from "node:path";
14458
+ import { closeSync, existsSync as existsSync22, linkSync, lstatSync as lstatSync2, mkdirSync as mkdirSync11, openSync, readFileSync as readFileSync32, readdirSync as readdirSync9, renameSync as renameSync4, unlinkSync, writeFileSync as writeFileSync23 } from "node:fs";
14459
+ import { dirname as dirname10, isAbsolute as isAbsolute2, join as join37, relative as relative5, resolve as resolve26, sep as sep6 } from "node:path";
14346
14460
  import { createHash as createHash6, randomUUID } from "node:crypto";
14347
14461
  import ts6 from "typescript";
14348
14462
  var PLATFORM3 = "linuxsteamrt64";
@@ -14469,11 +14583,11 @@ function hookOf(name, value, signatures, errors) {
14469
14583
  }
14470
14584
  function sourceFiles(dir, pkg, errors) {
14471
14585
  const result = /* @__PURE__ */ new Set();
14472
- const src = join36(dir, "src");
14586
+ const src = join37(dir, "src");
14473
14587
  function walk(at) {
14474
- if (!existsSync21(at)) return;
14588
+ if (!existsSync22(at)) return;
14475
14589
  for (const item of readdirSync9(at, { withFileTypes: true })) {
14476
- const p = join36(at, item.name);
14590
+ const p = join37(at, item.name);
14477
14591
  if (item.isDirectory()) walk(p);
14478
14592
  else if (item.isFile() && /\.[cm]?[jt]sx?$/.test(item.name)) result.add(p);
14479
14593
  }
@@ -14481,12 +14595,12 @@ function sourceFiles(dir, pkg, errors) {
14481
14595
  walk(src);
14482
14596
  if (valid2(pkg.main)) {
14483
14597
  const p = resolve26(dir, pkg.main);
14484
- if (contained(dir, p) && existsSync21(p) && lstatSync2(p).isFile()) result.add(p);
14598
+ if (contained(dir, p) && existsSync22(p) && lstatSync2(p).isFile()) result.add(p);
14485
14599
  }
14486
14600
  const queue = [...result];
14487
14601
  for (let i2 = 0; i2 < queue.length; i2++) {
14488
14602
  const file = queue[i2];
14489
- const ast = ts6.createSourceFile(file, readFileSync31(file, "utf8"), ts6.ScriptTarget.Latest, true);
14603
+ const ast = ts6.createSourceFile(file, readFileSync32(file, "utf8"), ts6.ScriptTarget.Latest, true);
14490
14604
  const follow = (spec, node) => {
14491
14605
  if (!spec.startsWith(".")) return;
14492
14606
  const at = `${relative5(dir, file)}:${ast.getLineAndCharacterOfPosition(node.getStart(ast)).line + 1}`;
@@ -14496,8 +14610,8 @@ function sourceFiles(dir, pkg, errors) {
14496
14610
  return;
14497
14611
  }
14498
14612
  const stem = base.replace(/\.[cm]?[jt]sx?$/, "");
14499
- const candidates = [base, ...[".ts", ".tsx", ".js", ".jsx", ".mts", ".mjs", ".cts", ".cjs"].map((x) => stem + x), ...["index.ts", "index.tsx", "index.js", "index.jsx"].map((x) => join36(base, x))];
14500
- const found = candidates.find((p) => contained(dir, p) && existsSync21(p) && lstatSync2(p).isFile());
14613
+ const candidates = [base, ...[".ts", ".tsx", ".js", ".jsx", ".mts", ".mjs", ".cts", ".cjs"].map((x) => stem + x), ...["index.ts", "index.tsx", "index.js", "index.jsx"].map((x) => join37(base, x))];
14614
+ const found = candidates.find((p) => contained(dir, p) && existsSync22(p) && lstatSync2(p).isFile());
14501
14615
  if (!found) {
14502
14616
  errors.push(`${at}: unresolved local module ${JSON.stringify(spec)}`);
14503
14617
  return;
@@ -14527,7 +14641,7 @@ function sourceFiles(dir, pkg, errors) {
14527
14641
  function scanSource(files, calls, hooks, report) {
14528
14642
  const namespaces = new Set(hooks.values());
14529
14643
  for (const file of files) {
14530
- const source = ts6.createSourceFile(file, readFileSync31(file, "utf8"), ts6.ScriptTarget.Latest, true);
14644
+ const source = ts6.createSourceFile(file, readFileSync32(file, "utf8"), ts6.ScriptTarget.Latest, true);
14531
14645
  if (source.parseDiagnostics?.length) report.ambiguities.push(`${relative5(report.package, file)}: source syntax errors prevent reliable reference analysis`);
14532
14646
  const ambiguity = (node, message) => {
14533
14647
  report.ambiguities.push(`${relative5(report.package, file)}:${source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1}: ${message}`);
@@ -14667,12 +14781,12 @@ function scanSource(files, calls, hooks, report) {
14667
14781
  }
14668
14782
  function analyzeV1Migration(pluginDir, options = {}) {
14669
14783
  const dir = resolve26(pluginDir);
14670
- const destination = join36(dir, "gamedata", "functions.jsonc");
14784
+ const destination = join37(dir, "gamedata", "functions.jsonc");
14671
14785
  const report = { scope: SCOPE, package: dir, destination, entries: [], permissions: [], sourceReferences: [], generatedDeclarations: [], generatedDeclarationHashes: {}, recommendedEdits: [], ambiguities: [], filesChanged: [] };
14672
14786
  if (pathExists(destination) && !options.force) report.ambiguities.push(`${destination}: destination already exists; use --force to replace it`);
14673
14787
  let pkg;
14674
14788
  try {
14675
- pkg = parseFunctionFile(join36(dir, "package.json"), readFileSync31(join36(dir, "package.json"), "utf8"));
14789
+ pkg = parseFunctionFile(join37(dir, "package.json"), readFileSync32(join37(dir, "package.json"), "utf8"));
14676
14790
  } catch (e) {
14677
14791
  report.ambiguities.push(`package.json: ${e.message}`);
14678
14792
  return report;
@@ -14694,7 +14808,7 @@ function analyzeV1Migration(pluginDir, options = {}) {
14694
14808
  }
14695
14809
  let gd;
14696
14810
  try {
14697
- gd = parseFunctionFile(input, readFileSync31(input, "utf8"));
14811
+ gd = parseFunctionFile(input, readFileSync32(input, "utf8"));
14698
14812
  } catch (e) {
14699
14813
  report.ambiguities.push(`gamedata: ${e.message}`);
14700
14814
  return report;
@@ -14794,10 +14908,10 @@ function analyzeV1Migration(pluginDir, options = {}) {
14794
14908
  }
14795
14909
  }
14796
14910
  report.output = author;
14797
- report.generatedDeclarations = ["gamedata.d.ts", "hooks.d.ts"].map((n3) => join36(dir, ".s2script", n3)).filter(existsSync21);
14911
+ report.generatedDeclarations = ["gamedata.d.ts", "hooks.d.ts"].map((n3) => join37(dir, ".s2script", n3)).filter(existsSync22);
14798
14912
  for (const file of report.generatedDeclarations) {
14799
14913
  try {
14800
- report.generatedDeclarationHashes[file] = createHash6("sha256").update(readFileSync31(file)).digest("hex");
14914
+ report.generatedDeclarationHashes[file] = createHash6("sha256").update(readFileSync32(file)).digest("hex");
14801
14915
  } catch (e) {
14802
14916
  report.ambiguities.push(`${file}: generated declaration could not be read: ${e.message}`);
14803
14917
  }
@@ -14816,7 +14930,7 @@ function migrateV1Package(pluginDir, options = {}) {
14816
14930
  const report = analyzeV1Migration(pluginDir, options);
14817
14931
  if (report.ambiguities.length || !report.output) return report;
14818
14932
  const dest = report.destination;
14819
- const temp = join36(dirname10(dest), `.functions.jsonc.${process.pid}.${randomUUID()}.tmp`);
14933
+ const temp = join37(dirname10(dest), `.functions.jsonc.${process.pid}.${randomUUID()}.tmp`);
14820
14934
  try {
14821
14935
  mkdirSync11(dirname10(dest), { recursive: true });
14822
14936
  const fd2 = openSync(temp, "wx", 384);
@@ -14838,7 +14952,7 @@ function migrateV1Package(pluginDir, options = {}) {
14838
14952
  report.publication = void 0;
14839
14953
  report.ambiguities.push(`${dest}: publication failed: ${e.message}`);
14840
14954
  } finally {
14841
- if (existsSync21(temp)) unlinkSync(temp);
14955
+ if (existsSync22(temp)) unlinkSync(temp);
14842
14956
  }
14843
14957
  return report;
14844
14958
  }
@@ -14858,8 +14972,8 @@ async function run11(argv2) {
14858
14972
 
14859
14973
  // src/commands/pack.ts
14860
14974
  import { createHash as createHash7 } from "node:crypto";
14861
- import { readFileSync as readFileSync32, writeFileSync as writeFileSync24 } from "node:fs";
14862
- import { join as join37, resolve as resolve27 } from "node:path";
14975
+ import { readFileSync as readFileSync33, writeFileSync as writeFileSync24 } from "node:fs";
14976
+ import { join as join38, resolve as resolve27 } from "node:path";
14863
14977
  function formatBytes(n3) {
14864
14978
  if (n3 < 1e3) return `${n3}B`;
14865
14979
  if (n3 < 1e3 * 1e3) return `${(n3 / 1e3).toFixed(1)}kB`;
@@ -14867,9 +14981,9 @@ function formatBytes(n3) {
14867
14981
  }
14868
14982
  function packPackage(dir) {
14869
14983
  const absDir = resolve27(dir);
14870
- const pkg = JSON.parse(readFileSync32(join37(absDir, "package.json"), "utf8"));
14984
+ const pkg = JSON.parse(readFileSync33(join38(absDir, "package.json"), "utf8"));
14871
14985
  if (typeof pkg.name !== "string" || typeof pkg.version !== "string") {
14872
- throw new Error(`${join37(absDir, "package.json")}: "name" and "version" are required to pack`);
14986
+ throw new Error(`${join38(absDir, "package.json")}: "name" and "version" are required to pack`);
14873
14987
  }
14874
14988
  const pack = packSourceTarball(absDir, pkg);
14875
14989
  return {
@@ -14927,8 +15041,8 @@ async function run12(argv2) {
14927
15041
  }
14928
15042
 
14929
15043
  // src/commands/server.ts
14930
- import { existsSync as existsSync22 } from "node:fs";
14931
- import { join as join38, resolve as resolve28 } from "node:path";
15044
+ import { existsSync as existsSync23 } from "node:fs";
15045
+ import { join as join39, resolve as resolve28 } from "node:path";
14932
15046
  async function run13(argv2) {
14933
15047
  const [sub, dir] = positionals(argv2, ["--registry", "--name"]);
14934
15048
  if (sub !== "init") {
@@ -14937,7 +15051,7 @@ async function run13(argv2) {
14937
15051
  }
14938
15052
  const root = dir ? resolve28(dir) : findAddonRoot(".") ?? resolve28("addons", "s2script");
14939
15053
  try {
14940
- if (!existsSync22(join38(root, "plugins"))) {
15054
+ if (!existsSync23(join39(root, "plugins"))) {
14941
15055
  throw new Error(`${root} has no plugins/ directory \u2014 pass your addons/s2script directory`);
14942
15056
  }
14943
15057
  const report = await serverInit({
@@ -14949,7 +15063,7 @@ async function run13(argv2) {
14949
15063
  for (const m of report.managed) console.log(`managed ${m.name}@${m.version} (${m.filename})`);
14950
15064
  for (const u4 of report.unmanaged) console.log(`unmanaged ${u4.filename}: ${u4.reason}`);
14951
15065
  console.log(
14952
- `wrote ${join38(root, "plugins.json")} and plugins-lock.json \u2014 ${report.managed.length} managed, ${report.unmanaged.length} left as they are`
15066
+ `wrote ${join39(root, "plugins.json")} and plugins-lock.json \u2014 ${report.managed.length} managed, ${report.unmanaged.length} left as they are`
14953
15067
  );
14954
15068
  } catch (e) {
14955
15069
  console.error(String(e instanceof Error ? e.message : e));