@s2script/sdk 0.30.3 → 0.31.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.
package/dist/cli.js CHANGED
@@ -3455,11 +3455,11 @@ function unzipSync(data, opts) {
3455
3455
  }
3456
3456
 
3457
3457
  // src/build.ts
3458
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, rmSync as rmSync2 } from "node:fs";
3459
- import { resolve as resolve12, join as join12 } from "node:path";
3458
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, rmSync as rmSync2 } from "node:fs";
3459
+ import { resolve as resolve12, join as join13 } from "node:path";
3460
3460
 
3461
3461
  // src/typecheck/typecheck.ts
3462
- import ts4 from "typescript";
3462
+ import ts5 from "typescript";
3463
3463
 
3464
3464
  // src/gamedata/load.ts
3465
3465
  import { readFileSync } from "node:fs";
@@ -3516,9 +3516,9 @@ function stripJsonComments(src) {
3516
3516
 
3517
3517
  // src/gamedata/types.ts
3518
3518
  var PLATFORM = "linuxsteamrt64";
3519
- var ARG_KINDS = ["bool", "int", "float", "string", "utlstring", "vector", "entity"];
3520
- var RET_KINDS = ["void", "bool", "int", "float", "entity"];
3521
- var INT_CLASS_ARGS = /* @__PURE__ */ new Set(["bool", "int", "string", "utlstring", "vector", "entity"]);
3519
+ var ARG_KINDS = ["bool", "int", "float", "string", "utlstring", "vector", "entity", "ptr"];
3520
+ var RET_KINDS = ["void", "bool", "int", "float", "entity", "ptr"];
3521
+ var INT_CLASS_ARGS = /* @__PURE__ */ new Set(["bool", "int", "string", "utlstring", "vector", "entity", "ptr"]);
3522
3522
  var MAX_INT_ARGS = 9;
3523
3523
  var MAX_FLOAT_ARGS = 8;
3524
3524
  var HOOK_SHAPES = ["this_void", "this_f32_i32_i32_i32", "this_f32_i32_i64_i64"];
@@ -3950,14 +3950,16 @@ var ARG_TS = {
3950
3950
  string: "string",
3951
3951
  utlstring: "string",
3952
3952
  vector: VECTOR_TS,
3953
- entity: "EntityRef | null"
3953
+ entity: "EntityRef | null",
3954
+ ptr: "Ptr | null"
3954
3955
  };
3955
3956
  var RET_TS = {
3956
3957
  void: "void",
3957
3958
  bool: "boolean | null",
3958
3959
  int: "number | null",
3959
3960
  float: "number | null",
3960
- entity: "EntityRef | null"
3961
+ entity: "EntityRef | null",
3962
+ ptr: "Ptr | null"
3961
3963
  };
3962
3964
  function generateGamedataTypes(gd) {
3963
3965
  const calls = gd.calls ?? {};
@@ -3979,6 +3981,7 @@ ${lines.join("\n")}
3979
3981
  "// The EntityRef import is REQUIRED: a `declare module` augmentation resolves type names in its",
3980
3982
  "// own file scope, so an un-imported EntityRef degrades to an error skipLibCheck can swallow.",
3981
3983
  'import type { EntityRef } from "@s2script/sdk/entity";',
3984
+ 'import type { Ptr } from "@s2script/sdk/unsafe";',
3982
3985
  "",
3983
3986
  'declare module "@s2script/sdk/unsafe" {',
3984
3987
  ` interface EngineCalls {${body}}`,
@@ -3991,8 +3994,8 @@ function generateHookTypes(gd) {
3991
3994
  }
3992
3995
 
3993
3996
  // src/engine-functions/archive.ts
3994
- import { existsSync, readFileSync as readFileSync2 } from "node:fs";
3995
- import { join } from "node:path";
3997
+ import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
3998
+ import { join as join2 } from "node:path";
3996
3999
 
3997
4000
  // src/engine-functions/parse.ts
3998
4001
  import ts from "typescript";
@@ -4029,6 +4032,139 @@ function parseFunctionFile(path2, text3) {
4029
4032
  return parsed;
4030
4033
  }
4031
4034
 
4035
+ // src/engine-functions/inline.ts
4036
+ import { existsSync, readdirSync, readFileSync as readFileSync2, statSync } from "node:fs";
4037
+ import { join, relative } from "node:path";
4038
+ import ts2 from "typescript";
4039
+ var SKIP = /* @__PURE__ */ new Set(["node_modules", "dist", ".s2script", ".git"]);
4040
+ var SOURCE = /\.(?:ts|tsx|mts|cts)$/;
4041
+ var FACTORIES = /* @__PURE__ */ new Set(["fromSignature", "fromVirtual"]);
4042
+ function sources(dir, out = []) {
4043
+ for (const name of readdirSync(dir).sort()) {
4044
+ if (SKIP.has(name)) continue;
4045
+ const path2 = join(dir, name);
4046
+ const stat = statSync(path2);
4047
+ if (stat.isDirectory()) sources(path2, out);
4048
+ else if (SOURCE.test(name) && !name.endsWith(".d.ts")) out.push(path2);
4049
+ }
4050
+ return out;
4051
+ }
4052
+ function unwrap(node) {
4053
+ while (ts2.isParenthesizedExpression(node) || ts2.isAsExpression(node) || ts2.isSatisfiesExpression(node) || ts2.isTypeAssertionExpression(node)) node = node.expression;
4054
+ return node;
4055
+ }
4056
+ function literal(node, at) {
4057
+ node = unwrap(node);
4058
+ if (ts2.isStringLiteral(node) || ts2.isNoSubstitutionTemplateLiteral(node)) return node.text;
4059
+ if (ts2.isNumericLiteral(node)) return Number(node.text);
4060
+ if (ts2.isPrefixUnaryExpression(node) && node.operator === ts2.SyntaxKind.MinusToken && ts2.isNumericLiteral(node.operand))
4061
+ return -Number(node.operand.text);
4062
+ if (node.kind === ts2.SyntaxKind.TrueKeyword) return true;
4063
+ if (node.kind === ts2.SyntaxKind.FalseKeyword) return false;
4064
+ if (node.kind === ts2.SyntaxKind.NullKeyword) return null;
4065
+ if (ts2.isArrayLiteralExpression(node)) {
4066
+ return node.elements.map((e) => {
4067
+ if (ts2.isSpreadElement(e) || ts2.isOmittedExpression(e)) throw new Error(`${at(e)}: inline engine function declarations must be literals (no spreads or holes)`);
4068
+ return literal(e, at);
4069
+ });
4070
+ }
4071
+ if (ts2.isObjectLiteralExpression(node)) {
4072
+ const out = {};
4073
+ for (const prop of node.properties) {
4074
+ if (!ts2.isPropertyAssignment(prop)) throw new Error(`${at(prop)}: inline engine function declarations must be literals (no spreads, shorthand or methods)`);
4075
+ const name = prop.name;
4076
+ const key = ts2.isIdentifier(name) || ts2.isStringLiteral(name) || ts2.isNumericLiteral(name) ? name.text : void 0;
4077
+ if (key === void 0) throw new Error(`${at(prop)}: inline engine function declarations must use plain property names`);
4078
+ if (Object.hasOwn(out, key)) throw new Error(`${at(prop)}: duplicate key ${JSON.stringify(key)}`);
4079
+ out[key] = literal(prop.initializer, at);
4080
+ }
4081
+ return out;
4082
+ }
4083
+ throw new Error(`${at(node)}: inline engine function declarations must be literals, because the build copies them into the manifest; move a computed declaration to gamedata/functions.jsonc`);
4084
+ }
4085
+ function bindings(file) {
4086
+ const engine = /* @__PURE__ */ new Set(), namespaces = /* @__PURE__ */ new Set();
4087
+ for (const statement of file.statements) {
4088
+ if (!ts2.isImportDeclaration(statement) || !ts2.isStringLiteral(statement.moduleSpecifier)) continue;
4089
+ if (statement.moduleSpecifier.text !== "@s2script/sdk/unsafe") continue;
4090
+ const bound = statement.importClause?.namedBindings;
4091
+ if (!bound) continue;
4092
+ if (ts2.isNamespaceImport(bound)) namespaces.add(bound.name.text);
4093
+ else for (const element of bound.elements) {
4094
+ if ((element.propertyName ?? element.name).text === "Engine") engine.add(element.name.text);
4095
+ }
4096
+ }
4097
+ return { engine, namespaces };
4098
+ }
4099
+ function extractInlineFunctions(pluginDir) {
4100
+ const result = { functions: {}, sites: {} };
4101
+ if (!existsSync(pluginDir)) return result;
4102
+ for (const path2 of sources(pluginDir)) {
4103
+ const text3 = readFileSync2(path2, "utf8");
4104
+ if (!text3.includes("fromSignature") && !text3.includes("fromVirtual")) continue;
4105
+ const file = ts2.createSourceFile(path2, text3, ts2.ScriptTarget.Latest, true);
4106
+ const { engine, namespaces } = bindings(file);
4107
+ if (engine.size === 0 && namespaces.size === 0) continue;
4108
+ const at = (node) => {
4109
+ const { line } = file.getLineAndCharacterOfPosition(node.getStart(file));
4110
+ return `${relative(pluginDir, path2)}:${line + 1}`;
4111
+ };
4112
+ const isEngine = (node) => ts2.isIdentifier(node) && engine.has(node.text) || ts2.isPropertyAccessExpression(node) && node.name.text === "Engine" && ts2.isIdentifier(node.expression) && namespaces.has(node.expression.text);
4113
+ const visit = (node) => {
4114
+ if (ts2.isPropertyAccessExpression(node) && FACTORIES.has(node.name.text) && isEngine(node.expression)) {
4115
+ const factory = node.name.text;
4116
+ const call = node.parent;
4117
+ if (!ts2.isCallExpression(call) || call.expression !== node)
4118
+ throw new Error(`${at(node)}: Engine.${factory} must be called directly, so the build can read its declaration`);
4119
+ const [nameArg, declArg] = call.arguments;
4120
+ if (call.arguments.length !== 2 || !nameArg || !declArg)
4121
+ throw new Error(`${at(call)}: Engine.${factory}(name, declaration) takes exactly two arguments`);
4122
+ const name = literal(nameArg, at);
4123
+ if (typeof name !== "string") throw new Error(`${at(nameArg)}: Engine.${factory} needs a string-literal name`);
4124
+ const decl = literal(declArg, at);
4125
+ if (decl === null || typeof decl !== "object" || Array.isArray(decl))
4126
+ throw new Error(`${at(declArg)}: Engine.${factory} needs an object-literal declaration`);
4127
+ const target2 = decl.target;
4128
+ if (target2 === null || typeof target2 !== "object" || Array.isArray(target2))
4129
+ throw new Error(`${at(declArg)}: Engine.${factory}: declaration.target must be an object`);
4130
+ const kind = target2.kind;
4131
+ if ("ref" in target2) {
4132
+ } else if (factory === "fromVirtual") {
4133
+ if (kind !== void 0 && kind !== "virtual") throw new Error(`${at(declArg)}: Engine.fromVirtual needs a virtual target`);
4134
+ target2.kind = "virtual";
4135
+ } else if (kind !== void 0 && kind !== "signature") {
4136
+ throw new Error(`${at(declArg)}: Engine.fromSignature needs a signature target; use Engine.fromVirtual for a vtable slot`);
4137
+ }
4138
+ if (Object.hasOwn(result.functions, name))
4139
+ throw new Error(`${at(call)}: engine function ${JSON.stringify(name)} is already declared inline at ${result.sites[name]}`);
4140
+ result.functions[name] = decl;
4141
+ result.sites[name] = at(call);
4142
+ }
4143
+ ts2.forEachChild(node, visit);
4144
+ };
4145
+ visit(file);
4146
+ }
4147
+ return result;
4148
+ }
4149
+ function mergeInlineFunctions(file, inline) {
4150
+ const names = Object.keys(inline.functions);
4151
+ if (names.length === 0) return file;
4152
+ const merged = file ? { ...file, functions: { ...file.functions ?? {} } } : { schemaVersion: 2, functions: {} };
4153
+ for (const name of names) {
4154
+ if (Object.hasOwn(merged.functions, name))
4155
+ throw new Error(`${inline.sites[name]}: engine function ${JSON.stringify(name)} is declared both inline and in gamedata/functions.jsonc`);
4156
+ merged.functions[name] = inline.functions[name];
4157
+ }
4158
+ return merged;
4159
+ }
4160
+ function locateInlineError(error, inline) {
4161
+ const err2 = error instanceof Error ? error : new Error(String(error));
4162
+ for (const [name, site] of Object.entries(inline.sites)) {
4163
+ if (err2.message.startsWith(`function ${JSON.stringify(name)}`)) return new Error(`${site}: ${err2.message} (declared inline)`);
4164
+ }
4165
+ return err2;
4166
+ }
4167
+
4032
4168
  // src/engine-functions/abi.generated.ts
4033
4169
  var PLATFORM2 = "linux-x86_64-sysv";
4034
4170
  var RECEIVERS = ["none", "entity"];
@@ -4062,7 +4198,7 @@ var IDENTIFIER2 = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
4062
4198
  var FORBIDDEN_NAMES = /* @__PURE__ */ new Set(["self", "returnValue", "constructor", "prototype", "__proto__"]);
4063
4199
  var SURFACES = ["call", "pre", "post"];
4064
4200
  var RESOLVERS = ["direct", "ctor-body-xref", "lea-disp", "validated-call"];
4065
- var TYPES = ["bool", "i32", "u32", "i64", "u64", "f32", "f64", "entity", "entity?", "string", "vector"];
4201
+ var TYPES = ["bool", "i32", "u32", "i64", "u64", "f32", "f64", "entity", "entity?", "string", "vector", "ptr"];
4066
4202
  var PARAMETER_OWNERSHIP = ["callee-borrowed", "callee-retained", "native-observed"];
4067
4203
  var RETURN_OWNERSHIP = ["caller-borrowed", "native-observed"];
4068
4204
  var copied = (type) => type === "string" || type === "vector";
@@ -4137,7 +4273,7 @@ function projection(type, where2) {
4137
4273
  if (type === "void") return { native: "void", projection: { id: "void", version: 1 } };
4138
4274
  if (!TYPES.includes(type)) throw new Error(`${where2}: unsupported author type ${JSON.stringify(type)}`);
4139
4275
  if (type === "bool") return { native: "u8", projection: { id: "bool", version: 1 } };
4140
- if (type === "entity" || type === "entity?" || type === "string" || type === "vector") {
4276
+ if (type === "entity" || type === "entity?" || type === "string" || type === "vector" || type === "ptr") {
4141
4277
  return { native: "ptr", projection: { id: type, version: 1 } };
4142
4278
  }
4143
4279
  return { native: type, projection: { id: type, version: 1 } };
@@ -4273,9 +4409,15 @@ function summarizeFunctions(bundle) {
4273
4409
 
4274
4410
  // src/engine-functions/archive.ts
4275
4411
  function loadEngineFunctions(pluginDir, ownerId) {
4276
- const path2 = join(pluginDir, "gamedata", "functions.jsonc");
4277
- if (!existsSync(path2)) return void 0;
4278
- return normalizeFunctions(ownerId, parseFunctionFile(path2, readFileSync2(path2, "utf8")));
4412
+ const path2 = join2(pluginDir, "gamedata", "functions.jsonc");
4413
+ const file = existsSync2(path2) ? parseFunctionFile(path2, readFileSync3(path2, "utf8")) : void 0;
4414
+ const inline = extractInlineFunctions(pluginDir);
4415
+ const merged = mergeInlineFunctions(file, inline);
4416
+ try {
4417
+ return normalizeFunctions(ownerId, merged);
4418
+ } catch (error) {
4419
+ throw locateInlineError(error, inline);
4420
+ }
4279
4421
  }
4280
4422
  function engineFunctionsArchive(bundle) {
4281
4423
  return canonicalJson(bundle) + "\n";
@@ -4308,6 +4450,9 @@ function typeOf(projection2) {
4308
4450
  return "string";
4309
4451
  case "vector":
4310
4452
  return "{ readonly x: number; readonly y: number; readonly z: number }";
4453
+ // A raw address: a `Ptr` from this module (in scope inside the augmentation), null for 0.
4454
+ case "ptr":
4455
+ return "Ptr | null";
4311
4456
  default:
4312
4457
  throw new Error(`no public TypeScript projection for ${projection2.id}`);
4313
4458
  }
@@ -4382,27 +4527,27 @@ function emitEngineFunctionTypes(bundle) {
4382
4527
  }
4383
4528
 
4384
4529
  // src/interop.ts
4385
- import ts3 from "typescript";
4530
+ import ts4 from "typescript";
4386
4531
  import { createHash as createHash2 } from "node:crypto";
4387
4532
 
4388
4533
  // src/publish-scan.ts
4389
- import ts2 from "typescript";
4534
+ import ts3 from "typescript";
4390
4535
  function pluginApiName(checker, call) {
4391
4536
  const declaration = checker.getResolvedSignature(call)?.getDeclaration();
4392
4537
  if (!declaration || !declaration.getSourceFile().fileName.replace(/\\/g, "/").endsWith("/sdk/plugin.d.ts"))
4393
4538
  return void 0;
4394
4539
  const name = declaration.name;
4395
- return name && (ts2.isIdentifier(name) || ts2.isStringLiteral(name)) ? name.text : void 0;
4540
+ return name && (ts3.isIdentifier(name) || ts3.isStringLiteral(name)) ? name.text : void 0;
4396
4541
  }
4397
4542
  function recordPublishOrUse(out, method, arg0, sf, node) {
4398
4543
  if (method === "publish") {
4399
- if (arg0 !== void 0 && ts2.isStringLiteralLike(arg0)) {
4544
+ if (arg0 !== void 0 && ts3.isStringLiteralLike(arg0)) {
4400
4545
  out.publishNames.push(arg0.text);
4401
4546
  } else {
4402
4547
  const { line } = sf.getLineAndCharacterOfPosition(node.getStart());
4403
4548
  out.dynamicPublishSites.push(`${sf.fileName}:${line + 1}`);
4404
4549
  }
4405
- } else if (arg0 !== void 0 && ts2.isStringLiteralLike(arg0)) {
4550
+ } else if (arg0 !== void 0 && ts3.isStringLiteralLike(arg0)) {
4406
4551
  out.useNames.push(arg0.text);
4407
4552
  }
4408
4553
  }
@@ -4423,10 +4568,10 @@ function scanPluginProgram(program, pluginDir) {
4423
4568
  if (sf.isDeclarationFile) continue;
4424
4569
  if (!sf.fileName.replace(/\\/g, "/").startsWith(dirPrefix)) continue;
4425
4570
  const visit = (node) => {
4426
- if ((ts2.isImportDeclaration(node) || ts2.isExportDeclaration(node)) && node.moduleSpecifier !== void 0 && ts2.isStringLiteralLike(node.moduleSpecifier)) {
4571
+ if ((ts3.isImportDeclaration(node) || ts3.isExportDeclaration(node)) && node.moduleSpecifier !== void 0 && ts3.isStringLiteralLike(node.moduleSpecifier)) {
4427
4572
  recordImportSpecifier(out, node.moduleSpecifier.text);
4428
4573
  }
4429
- if (ts2.isCallExpression(node)) {
4574
+ if (ts3.isCallExpression(node)) {
4430
4575
  const name = pluginApiName(checker, node);
4431
4576
  if (name && [
4432
4577
  "publish",
@@ -4437,7 +4582,7 @@ function scanPluginProgram(program, pluginDir) {
4437
4582
  ].includes(name))
4438
4583
  recordPublishOrUse(out, name, node.arguments[0], sf, node);
4439
4584
  }
4440
- ts2.forEachChild(node, visit);
4585
+ ts3.forEachChild(node, visit);
4441
4586
  };
4442
4587
  visit(sf);
4443
4588
  }
@@ -4499,8 +4644,8 @@ function canonical(value) {
4499
4644
  throw new Error("canonical metadata requires JSON values");
4500
4645
  }
4501
4646
  function extractContract(path2, packagesDir) {
4502
- const program = ts3.createProgram([path2], {
4503
- ...sharedProgramOptions(ts3),
4647
+ const program = ts4.createProgram([path2], {
4648
+ ...sharedProgramOptions(ts4),
4504
4649
  skipLibCheck: false,
4505
4650
  baseUrl: packagesDir,
4506
4651
  paths: { "@s2script/sdk": ["sdk/index.d.ts"], "@s2script/sdk/*": ["sdk/*.d.ts"] }
@@ -4514,11 +4659,11 @@ function extractContract(path2, packagesDir) {
4514
4659
  );
4515
4660
  };
4516
4661
  const visitImports = (node) => {
4517
- if (ts3.isImportDeclaration(node)) {
4518
- const module2 = ts3.isStringLiteral(node.moduleSpecifier) ? node.moduleSpecifier.text : "";
4519
- const bindings = node.importClause?.namedBindings;
4662
+ if (ts4.isImportDeclaration(node)) {
4663
+ const module2 = ts4.isStringLiteral(node.moduleSpecifier) ? node.moduleSpecifier.text : "";
4664
+ const bindings2 = node.importClause?.namedBindings;
4520
4665
  const allowed = module2 === "@s2script/sdk/interfaces" ? ["Notification", "Hook", "Transform"] : module2 === "@s2script/sdk/events" || module2 === "@s2script/sdk" ? ["HookResultValue"] : module2 === "@s2script/sdk/entity" ? ["EntityRef"] : [];
4521
- if (!bindings || !ts3.isNamedImports(bindings) || node.importClause?.name || !bindings.elements.every(
4666
+ if (!bindings2 || !ts4.isNamedImports(bindings2) || node.importClause?.name || !bindings2.elements.every(
4522
4667
  (e) => allowed.includes((e.propertyName ?? e.name).text)
4523
4668
  ))
4524
4669
  fail(
@@ -4526,12 +4671,12 @@ function extractContract(path2, packagesDir) {
4526
4671
  "contracts must be self-contained; only SDK Notification, Hook, Transform, HookResultValue and EntityRef imports are supported"
4527
4672
  );
4528
4673
  }
4529
- if (ts3.isExportDeclaration(node) && node.moduleSpecifier || ts3.isImportTypeNode(node) || ts3.isImportEqualsDeclaration(node) || ts3.isModuleDeclaration(node))
4674
+ if (ts4.isExportDeclaration(node) && node.moduleSpecifier || ts4.isImportTypeNode(node) || ts4.isImportEqualsDeclaration(node) || ts4.isModuleDeclaration(node))
4530
4675
  fail(
4531
4676
  node,
4532
4677
  "external declarations and module augmentations are not supported in a contract"
4533
4678
  );
4534
- ts3.forEachChild(node, visitImports);
4679
+ ts4.forEachChild(node, visitImports);
4535
4680
  };
4536
4681
  visitImports(sf);
4537
4682
  if (sf.referencedFiles.length || sf.typeReferenceDirectives.length)
@@ -4541,7 +4686,7 @@ function extractContract(path2, packagesDir) {
4541
4686
  ...program.getSemanticDiagnostics(sf)
4542
4687
  ];
4543
4688
  if (diags.length)
4544
- fail(sf, ts3.flattenDiagnosticMessageText(diags[0].messageText, "\n"));
4689
+ fail(sf, ts4.flattenDiagnosticMessageText(diags[0].messageText, "\n"));
4545
4690
  const module = checker.getSymbolAtLocation(sf);
4546
4691
  const symbol2 = module && checker.getExportsOfModule(module).find((s) => s.name === "Contract");
4547
4692
  if (!symbol2) fail(sf, "protocol 2 requires an exported Contract");
@@ -4553,26 +4698,26 @@ function extractContract(path2, packagesDir) {
4553
4698
  };
4554
4699
  for (const name of ["methods", "forwards"]) {
4555
4700
  const member = prop(contract, name, sf);
4556
- if (!(member.flags & ts3.TypeFlags.Object) || member.getCallSignatures().length || checker.getIndexInfosOfType(member).length) {
4701
+ if (!(member.flags & ts4.TypeFlags.Object) || member.getCallSignatures().length || checker.getIndexInfosOfType(member).length) {
4557
4702
  fail(sf, `${name} must be a finite object record`);
4558
4703
  }
4559
4704
  }
4560
4705
  const stack = /* @__PURE__ */ new Set();
4561
4706
  const schema = (type, node, voidAllowed = false) => {
4562
4707
  const f = type.flags;
4563
- if (f & ts3.TypeFlags.Any || f & ts3.TypeFlags.Unknown || f & ts3.TypeFlags.Never)
4708
+ if (f & ts4.TypeFlags.Any || f & ts4.TypeFlags.Unknown || f & ts4.TypeFlags.Never)
4564
4709
  fail(node, `unsupported wire type ${checker.typeToString(type)}`);
4565
- if (f & ts3.TypeFlags.Void && voidAllowed) return { kind: "void" };
4566
- if (f & ts3.TypeFlags.Null) return { kind: "null" };
4567
- if (f & ts3.TypeFlags.StringLiteral)
4710
+ if (f & ts4.TypeFlags.Void && voidAllowed) return { kind: "void" };
4711
+ if (f & ts4.TypeFlags.Null) return { kind: "null" };
4712
+ if (f & ts4.TypeFlags.StringLiteral)
4568
4713
  return { kind: "literal", value: type.value };
4569
- if (f & ts3.TypeFlags.NumberLiteral)
4714
+ if (f & ts4.TypeFlags.NumberLiteral)
4570
4715
  return { kind: "literal", value: type.value };
4571
- if (f & ts3.TypeFlags.BooleanLiteral)
4716
+ if (f & ts4.TypeFlags.BooleanLiteral)
4572
4717
  return { kind: "literal", value: checker.typeToString(type) === "true" };
4573
- if (f & ts3.TypeFlags.String) return { kind: "string" };
4574
- if (f & ts3.TypeFlags.Number) return { kind: "number" };
4575
- if (f & ts3.TypeFlags.Boolean) return { kind: "boolean" };
4718
+ if (f & ts4.TypeFlags.String) return { kind: "string" };
4719
+ if (f & ts4.TypeFlags.Number) return { kind: "number" };
4720
+ if (f & ts4.TypeFlags.Boolean) return { kind: "boolean" };
4576
4721
  if (stack.has(type)) fail(node, "recursive wire types are unsupported");
4577
4722
  stack.add(type);
4578
4723
  try {
@@ -4595,7 +4740,7 @@ function extractContract(path2, packagesDir) {
4595
4740
  node
4596
4741
  )
4597
4742
  };
4598
- if (!(f & ts3.TypeFlags.Object) || checker.isTupleType(type) || type.getCallSignatures().length || checker.getIndexInfosOfType(type).length)
4743
+ if (!(f & ts4.TypeFlags.Object) || checker.isTupleType(type) || type.getCallSignatures().length || checker.getIndexInfosOfType(type).length)
4599
4744
  fail(node, `unsupported wire type ${checker.typeToString(type)}`);
4600
4745
  const sym = type.getSymbol();
4601
4746
  if (sym?.name === "EntityRef" && sym.declarations?.some(
@@ -4603,7 +4748,7 @@ function extractContract(path2, packagesDir) {
4603
4748
  ))
4604
4749
  return { kind: "entityRef" };
4605
4750
  if (sym?.declarations?.some(
4606
- (d) => ts3.isClassDeclaration(d) || d.getSourceFile() !== sf
4751
+ (d) => ts4.isClassDeclaration(d) || d.getSourceFile() !== sf
4607
4752
  ))
4608
4753
  fail(
4609
4754
  node,
@@ -4614,11 +4759,11 @@ function extractContract(path2, packagesDir) {
4614
4759
  const decl = p.valueDeclaration ?? node;
4615
4760
  if (p.name === "__s2ref")
4616
4761
  fail(decl, "__s2ref is reserved for the EntityRef wire encoding");
4617
- const optional = !!(p.flags & ts3.SymbolFlags.Optional);
4762
+ const optional = !!(p.flags & ts4.SymbolFlags.Optional);
4618
4763
  let t2 = checker.getTypeOfSymbolAtLocation(p, decl);
4619
4764
  if (optional && t2.isUnion()) {
4620
4765
  const parts = t2.types.filter(
4621
- (t3) => !(t3.flags & ts3.TypeFlags.Undefined)
4766
+ (t3) => !(t3.flags & ts4.TypeFlags.Undefined)
4622
4767
  );
4623
4768
  if (parts.length === 1) t2 = parts[0];
4624
4769
  else {
@@ -4642,13 +4787,13 @@ function extractContract(path2, packagesDir) {
4642
4787
  const methods = /* @__PURE__ */ Object.create(null);
4643
4788
  for (const p of prop(contract, "methods", sf).getProperties()) {
4644
4789
  const node = p.valueDeclaration ?? sf, type = checker.getTypeOfSymbolAtLocation(p, node), sigs = type.getCallSignatures();
4645
- const scanner = ts3.createScanner(
4646
- ts3.ScriptTarget.ESNext,
4790
+ const scanner = ts4.createScanner(
4791
+ ts4.ScriptTarget.ESNext,
4647
4792
  false,
4648
- ts3.LanguageVariant.Standard,
4793
+ ts4.LanguageVariant.Standard,
4649
4794
  p.name
4650
4795
  );
4651
- const identifier = scanner.scan() === ts3.SyntaxKind.Identifier && scanner.scan() === ts3.SyntaxKind.EndOfFileToken;
4796
+ const identifier = scanner.scan() === ts4.SyntaxKind.Identifier && scanner.scan() === ts4.SyntaxKind.EndOfFileToken;
4652
4797
  if (!identifier || ["on", "off", "then"].includes(p.name))
4653
4798
  fail(node, `unsupported or reserved method name ${p.name}`);
4654
4799
  const exported = checker.getExportsOfModule(module).find((s) => s.name === p.name);
@@ -4666,7 +4811,7 @@ function extractContract(path2, packagesDir) {
4666
4811
  )
4667
4812
  )))
4668
4813
  fail(node, `export ${p.name} must agree with Contract.methods`);
4669
- if (sigs.length !== 1 || sigs[0].typeParameters?.length || p.flags & ts3.SymbolFlags.Optional)
4814
+ if (sigs.length !== 1 || sigs[0].typeParameters?.length || p.flags & ts4.SymbolFlags.Optional)
4670
4815
  fail(node, "methods require one non-generic synchronous signature");
4671
4816
  const sig = sigs[0];
4672
4817
  methods[p.name] = {
@@ -4676,7 +4821,7 @@ function extractContract(path2, packagesDir) {
4676
4821
  let t2 = checker.getTypeOfSymbolAtLocation(p2, d);
4677
4822
  if (d.questionToken && t2.isUnion()) {
4678
4823
  const parts = t2.types.filter(
4679
- (t3) => !(t3.flags & ts3.TypeFlags.Undefined)
4824
+ (t3) => !(t3.flags & ts4.TypeFlags.Undefined)
4680
4825
  );
4681
4826
  if (parts.length === 1) t2 = parts[0];
4682
4827
  else fail(d, "optional union arguments are unsupported");
@@ -4714,9 +4859,9 @@ function extractContract(path2, packagesDir) {
4714
4859
  if (payload.kind !== "object")
4715
4860
  fail(node, "Transform requires a finite object payload");
4716
4861
  const writableType = prop(type, "__transformWritable", node);
4717
- const keys2 = writableType.flags & ts3.TypeFlags.Never ? [] : writableType.isUnion() ? writableType.types : [writableType];
4862
+ const keys2 = writableType.flags & ts4.TypeFlags.Never ? [] : writableType.isUnion() ? writableType.types : [writableType];
4718
4863
  const writable = keys2.map((t2) => {
4719
- if (!(t2.flags & ts3.TypeFlags.StringLiteral))
4864
+ if (!(t2.flags & ts4.TypeFlags.StringLiteral))
4720
4865
  fail(node, "Transform writable keys must be string field names");
4721
4866
  const key2 = t2.value;
4722
4867
  if (!Object.hasOwn(payload.fields, key2))
@@ -4728,10 +4873,10 @@ function extractContract(path2, packagesDir) {
4728
4873
  }
4729
4874
  for (const exported of checker.getExportsOfModule(module)) {
4730
4875
  const typeOnly = exported.declarations?.every(
4731
- (d) => ts3.isExportSpecifier(d) && (d.isTypeOnly || ts3.isExportDeclaration(d.parent.parent) && d.parent.parent.isTypeOnly)
4876
+ (d) => ts4.isExportSpecifier(d) && (d.isTypeOnly || ts4.isExportDeclaration(d.parent.parent) && d.parent.parent.isTypeOnly)
4732
4877
  );
4733
- const target2 = exported.flags & ts3.SymbolFlags.Alias ? checker.getAliasedSymbol(exported) : exported;
4734
- if (!typeOnly && target2.flags & ts3.SymbolFlags.Value && !Object.hasOwn(methods, exported.name))
4878
+ const target2 = exported.flags & ts4.SymbolFlags.Alias ? checker.getAliasedSymbol(exported) : exported;
4879
+ if (!typeOnly && target2.flags & ts4.SymbolFlags.Value && !Object.hasOwn(methods, exported.name))
4735
4880
  fail(exported.declarations?.[0] ?? sf, `unsupported value export ${exported.name}; only Contract.methods may export runtime values`);
4736
4881
  }
4737
4882
  const metadata = { version: 1, methods, forwards };
@@ -4751,40 +4896,40 @@ function implementationExpression(checker, expression, seen = /* @__PURE__ */ ne
4751
4896
  const declarationValue = (declaration) => {
4752
4897
  if (seen.has(declaration)) return void 0;
4753
4898
  seen.add(declaration);
4754
- if ((ts3.isVariableDeclaration(declaration) || ts3.isPropertyAssignment(declaration)) && declaration.initializer)
4899
+ if ((ts4.isVariableDeclaration(declaration) || ts4.isPropertyAssignment(declaration)) && declaration.initializer)
4755
4900
  return implementationExpression(checker, declaration.initializer, seen);
4756
- if (ts3.isShorthandPropertyAssignment(declaration)) {
4901
+ if (ts4.isShorthandPropertyAssignment(declaration)) {
4757
4902
  const value = checker.getShorthandAssignmentValueSymbol(
4758
4903
  declaration
4759
4904
  )?.valueDeclaration;
4760
4905
  return value && declarationValue(value);
4761
4906
  }
4762
- if (ts3.isBindingElement(declaration) && ts3.isObjectBindingPattern(declaration.parent)) {
4907
+ if (ts4.isBindingElement(declaration) && ts4.isObjectBindingPattern(declaration.parent)) {
4763
4908
  const object2 = declarationValue(declaration.parent.parent);
4764
4909
  const name = declaration.propertyName ?? declaration.name;
4765
- if (object2 && (ts3.isIdentifier(name) || ts3.isStringLiteral(name)))
4910
+ if (object2 && (ts4.isIdentifier(name) || ts4.isStringLiteral(name)))
4766
4911
  return propertyValue(object2, name.text);
4767
4912
  }
4768
4913
  return void 0;
4769
4914
  };
4770
- if (ts3.isParenthesizedExpression(expression))
4915
+ if (ts4.isParenthesizedExpression(expression))
4771
4916
  return implementationExpression(checker, expression.expression, seen);
4772
- if (ts3.isPropertyAccessExpression(expression))
4917
+ if (ts4.isPropertyAccessExpression(expression))
4773
4918
  return propertyValue(expression.expression, expression.name.text) ?? expression;
4774
- if (ts3.isElementAccessExpression(expression) && ts3.isStringLiteralLike(expression.argumentExpression))
4919
+ if (ts4.isElementAccessExpression(expression) && ts4.isStringLiteralLike(expression.argumentExpression))
4775
4920
  return propertyValue(
4776
4921
  expression.expression,
4777
4922
  expression.argumentExpression.text
4778
4923
  ) ?? expression;
4779
4924
  let symbol2 = checker.getSymbolAtLocation(expression);
4780
- if (symbol2 && symbol2.flags & ts3.SymbolFlags.Alias)
4925
+ if (symbol2 && symbol2.flags & ts4.SymbolFlags.Alias)
4781
4926
  symbol2 = checker.getAliasedSymbol(symbol2);
4782
4927
  return (symbol2?.valueDeclaration && declarationValue(symbol2.valueDeclaration)) ?? expression;
4783
4928
  }
4784
4929
  function checkInteropCalls(program, paths, publishes, generatedPath, pluginDir, dependencies, optionalDependencies) {
4785
4930
  const checker = program.getTypeChecker(), out = [];
4786
4931
  const report = (node, message) => out.push({
4787
- category: ts3.DiagnosticCategory.Error,
4932
+ category: ts4.DiagnosticCategory.Error,
4788
4933
  code: 92002,
4789
4934
  file: node.getSourceFile(),
4790
4935
  start: node.getStart(),
@@ -4804,12 +4949,12 @@ function checkInteropCalls(program, paths, publishes, generatedPath, pluginDir,
4804
4949
  );
4805
4950
  for (const sf of program.getSourceFiles()) {
4806
4951
  const visit = (node) => {
4807
- if (ts3.isInterfaceDeclaration(node) && node.name.text === "InterfaceContracts" && sf.fileName !== generatedPath && sf.fileName !== sdkInterfaces)
4952
+ if (ts4.isInterfaceDeclaration(node) && node.name.text === "InterfaceContracts" && sf.fileName !== generatedPath && sf.fileName !== sdkInterfaces)
4808
4953
  report(
4809
4954
  node,
4810
4955
  "InterfaceContracts associations are generated; local augmentation is not authority"
4811
4956
  );
4812
- if (ts3.isCallExpression(node) && sf.fileName.startsWith(pluginDir + "/")) {
4957
+ if (ts4.isCallExpression(node) && sf.fileName.startsWith(pluginDir + "/")) {
4813
4958
  const method = pluginApiName(checker, node);
4814
4959
  if ([
4815
4960
  "publish",
@@ -4819,7 +4964,7 @@ function checkInteropCalls(program, paths, publishes, generatedPath, pluginDir,
4819
4964
  "bindForwards"
4820
4965
  ].includes(method ?? "")) {
4821
4966
  const arg = node.arguments[0];
4822
- const name = arg && ts3.isStringLiteralLike(arg) ? arg.text : null;
4967
+ const name = arg && ts4.isStringLiteralLike(arg) ? arg.text : null;
4823
4968
  if (!name || !contracts.has(name))
4824
4969
  report(
4825
4970
  node,
@@ -4838,8 +4983,8 @@ function checkInteropCalls(program, paths, publishes, generatedPath, pluginDir,
4838
4983
  if (method === "watchOptional" && node.arguments[1]) {
4839
4984
  const callback = node.arguments[1];
4840
4985
  const type = checker.getTypeAtLocation(implementationExpression(checker, callback));
4841
- const asynchronous = (type2) => type2.isUnion() ? type2.types.some(asynchronous) : !!(type2.flags & ts3.TypeFlags.Any) || !!type2.getProperty("then");
4842
- if (type.flags & ts3.TypeFlags.Any || type.getCallSignatures().some((signature) => asynchronous(checker.getReturnTypeOfSignature(signature))))
4986
+ const asynchronous = (type2) => type2.isUnion() ? type2.types.some(asynchronous) : !!(type2.flags & ts4.TypeFlags.Any) || !!type2.getProperty("then");
4987
+ if (type.flags & ts4.TypeFlags.Any || type.getCallSignatures().some((signature) => asynchronous(checker.getReturnTypeOfSignature(signature))))
4843
4988
  report(callback, "watchOptional requires a synchronous attachment callback; thenables are forbidden");
4844
4989
  }
4845
4990
  if (method === "publish") {
@@ -4855,7 +5000,7 @@ function checkInteropCalls(program, paths, publishes, generatedPath, pluginDir,
4855
5000
  const implementationType = impl && checker.getTypeAtLocation(
4856
5001
  implementationExpression(checker, impl)
4857
5002
  );
4858
- if (!actual || actual.flags & ts3.TypeFlags.Any || !checker.isTypeAssignableTo(actual, expected))
5003
+ if (!actual || actual.flags & ts4.TypeFlags.Any || !checker.isTypeAssignableTo(actual, expected))
4859
5004
  report(
4860
5005
  impl ?? node,
4861
5006
  `producer implementation must agree with ${name} Contract.methods (${checker.typeToString(
@@ -4880,7 +5025,7 @@ function checkInteropCalls(program, paths, publishes, generatedPath, pluginDir,
4880
5025
  if (!implementation) continue;
4881
5026
  const required = checker.getTypeOfSymbolAtLocation(expectedMethod, impl).getCallSignatures()[0];
4882
5027
  const declaration = implementation.valueDeclaration;
4883
- const value = declaration && ts3.isPropertyAssignment(declaration) ? declaration.initializer : declaration && ts3.isShorthandPropertyAssignment(declaration) ? declaration.name : void 0;
5028
+ const value = declaration && ts4.isPropertyAssignment(declaration) ? declaration.initializer : declaration && ts4.isShorthandPropertyAssignment(declaration) ? declaration.name : void 0;
4884
5029
  const methodType = value ? checker.getTypeAtLocation(
4885
5030
  implementationExpression(checker, value)
4886
5031
  ) : checker.getTypeOfSymbolAtLocation(implementation, impl);
@@ -4895,15 +5040,15 @@ function checkInteropCalls(program, paths, publishes, generatedPath, pluginDir,
4895
5040
  const provided = signatures[0];
4896
5041
  const result = checker.getReturnTypeOfSignature(provided);
4897
5042
  const expectedResult = checker.getReturnTypeOfSignature(required);
4898
- const resultMatches = expectedResult.flags & ts3.TypeFlags.Void ? !!(result.flags & (ts3.TypeFlags.Void | ts3.TypeFlags.Undefined | ts3.TypeFlags.Never)) : checker.isTypeAssignableTo(result, expectedResult);
4899
- if (result.flags & ts3.TypeFlags.Any || !resultMatches)
5043
+ const resultMatches = expectedResult.flags & ts4.TypeFlags.Void ? !!(result.flags & (ts4.TypeFlags.Void | ts4.TypeFlags.Undefined | ts4.TypeFlags.Never)) : checker.isTypeAssignableTo(result, expectedResult);
5044
+ if (result.flags & ts4.TypeFlags.Any || !resultMatches)
4900
5045
  report(
4901
5046
  impl,
4902
5047
  `producer method ${expectedMethod.name} result must agree with its synchronous Contract signature`
4903
5048
  );
4904
5049
  for (const [i2, parameter] of provided.parameters.entries()) {
4905
5050
  const declaration2 = parameter.valueDeclaration;
4906
- if (declaration2 && ts3.isParameter(declaration2) && declaration2.dotDotDotToken) {
5051
+ if (declaration2 && ts4.isParameter(declaration2) && declaration2.dotDotDotToken) {
4907
5052
  report(
4908
5053
  impl,
4909
5054
  `producer method ${expectedMethod.name} signature cannot use rest parameters`
@@ -4916,7 +5061,7 @@ function checkInteropCalls(program, paths, publishes, generatedPath, pluginDir,
4916
5061
  );
4917
5062
  const contractParameter = required.parameters[i2];
4918
5063
  if (!contractParameter) {
4919
- if (!(parameter.flags & ts3.SymbolFlags.Optional) && !(declaration2 && ts3.isParameter(declaration2) && (declaration2.questionToken || declaration2.initializer)))
5064
+ if (!(parameter.flags & ts4.SymbolFlags.Optional) && !(declaration2 && ts4.isParameter(declaration2) && (declaration2.questionToken || declaration2.initializer)))
4920
5065
  report(
4921
5066
  impl,
4922
5067
  `producer method ${expectedMethod.name} requires an undeclared input`
@@ -4927,9 +5072,9 @@ function checkInteropCalls(program, paths, publishes, generatedPath, pluginDir,
4927
5072
  impl
4928
5073
  );
4929
5074
  const variants = requiredInput.isUnion() ? requiredInput.types : [requiredInput];
4930
- const hasDefault = declaration2 && ts3.isParameter(declaration2) && declaration2.initializer;
5075
+ const hasDefault = declaration2 && ts4.isParameter(declaration2) && declaration2.initializer;
4931
5076
  if (!variants.every(
4932
- (type) => hasDefault && !!(type.flags & ts3.TypeFlags.Undefined) || checker.isTypeAssignableTo(type, input)
5077
+ (type) => hasDefault && !!(type.flags & ts4.TypeFlags.Undefined) || checker.isTypeAssignableTo(type, input)
4933
5078
  ))
4934
5079
  report(
4935
5080
  impl,
@@ -4944,7 +5089,7 @@ function checkInteropCalls(program, paths, publishes, generatedPath, pluginDir,
4944
5089
  }
4945
5090
  }
4946
5091
  }
4947
- ts3.forEachChild(node, visit);
5092
+ ts4.forEachChild(node, visit);
4948
5093
  };
4949
5094
  visit(sf);
4950
5095
  }
@@ -4953,7 +5098,7 @@ function checkInteropCalls(program, paths, publishes, generatedPath, pluginDir,
4953
5098
 
4954
5099
  // src/publishes.ts
4955
5100
  import { createHash as createHash3 } from "node:crypto";
4956
- import { readFileSync as readFileSync3 } from "node:fs";
5101
+ import { readFileSync as readFileSync4 } from "node:fs";
4957
5102
  function expandPublishes(authored, pkgName, pkgVersion) {
4958
5103
  if (authored === void 0 || authored === null) return {};
4959
5104
  if (typeof authored === "string") {
@@ -4977,7 +5122,7 @@ function expandPublishes(authored, pkgName, pkgVersion) {
4977
5122
  return out;
4978
5123
  }
4979
5124
  function hashContract(typesPath) {
4980
- return createHash3("sha256").update(readFileSync3(typesPath)).digest("hex");
5125
+ return createHash3("sha256").update(readFileSync4(typesPath)).digest("hex");
4981
5126
  }
4982
5127
  var CONCRETE_SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
4983
5128
  function isConcreteVersion(v) {
@@ -5015,28 +5160,28 @@ function derivePublishes(authored, pkgName, pkgVersion, typesPath) {
5015
5160
  // src/typecheck/typecheck.ts
5016
5161
  import {
5017
5162
  mkdirSync,
5018
- existsSync as existsSync7,
5019
- readdirSync as readdirSync2,
5020
- readFileSync as readFileSync8,
5163
+ existsSync as existsSync8,
5164
+ readdirSync as readdirSync3,
5165
+ readFileSync as readFileSync9,
5021
5166
  writeFileSync,
5022
5167
  rmSync,
5023
5168
  mkdtempSync
5024
5169
  } from "node:fs";
5025
- import { join as join8, resolve as resolve8, dirname as dirname3 } from "node:path";
5170
+ import { join as join9, resolve as resolve8, dirname as dirname3 } from "node:path";
5026
5171
  import { tmpdir } from "node:os";
5027
5172
 
5028
5173
  // src/packages-resolve.ts
5029
- import { existsSync as existsSync2 } from "node:fs";
5030
- import { dirname, join as join2, resolve as resolve3 } from "node:path";
5174
+ import { existsSync as existsSync3 } from "node:fs";
5175
+ import { dirname, join as join3, resolve as resolve3 } from "node:path";
5031
5176
  import { fileURLToPath } from "node:url";
5032
5177
  function isPackagesDir(dir) {
5033
5178
  const abs = resolve3(dir);
5034
- return existsSync2(join2(abs, "sdk", "globals.d.ts")) || existsSync2(join2(abs, "sdk", "entity.d.ts"));
5179
+ return existsSync3(join3(abs, "sdk", "globals.d.ts")) || existsSync3(join3(abs, "sdk", "entity.d.ts"));
5035
5180
  }
5036
5181
  function findPackagesDirNearCli(fromCliUrl = import.meta.url) {
5037
5182
  const start = dirname(fileURLToPath(fromCliUrl));
5038
5183
  for (const rel of ["../..", "../../.."]) {
5039
- const candidate = join2(start, rel);
5184
+ const candidate = join3(start, rel);
5040
5185
  if (isPackagesDir(candidate)) return resolve3(candidate);
5041
5186
  }
5042
5187
  return void 0;
@@ -5060,7 +5205,7 @@ function resolvePackagesDir(opts) {
5060
5205
  const nearCli = findPackagesDirNearCli(opts?.fromCliUrl);
5061
5206
  if (nearCli) return nearCli;
5062
5207
  if (opts?.pluginDir) {
5063
- const nm = join2(resolve3(opts.pluginDir), "node_modules", "@s2script");
5208
+ const nm = join3(resolve3(opts.pluginDir), "node_modules", "@s2script");
5064
5209
  if (isPackagesDir(nm)) return nm;
5065
5210
  }
5066
5211
  throw new Error(
@@ -5069,18 +5214,18 @@ function resolvePackagesDir(opts) {
5069
5214
  }
5070
5215
 
5071
5216
  // src/contracts.ts
5072
- import { existsSync as existsSync3 } from "node:fs";
5073
- import { join as join3 } from "node:path";
5217
+ import { existsSync as existsSync4 } from "node:fs";
5218
+ import { join as join4 } from "node:path";
5074
5219
  function localContractPath(pluginDir, dep) {
5075
5220
  const segs = dep.split("/");
5076
5221
  if (segs.some((s) => s === "" || s === "." || s === "..")) return null;
5077
- const p = join3(pluginDir, ".s2script", "types", ...segs, "index.d.ts");
5078
- return existsSync3(p) ? p : null;
5222
+ const p = join4(pluginDir, ".s2script", "types", ...segs, "index.d.ts");
5223
+ return existsSync4(p) ? p : null;
5079
5224
  }
5080
5225
 
5081
5226
  // src/workspace/siblings.ts
5082
- import { readFileSync as readFileSync6 } from "node:fs";
5083
- import { join as join6, relative, resolve as resolve6 } from "node:path";
5227
+ import { readFileSync as readFileSync7 } from "node:fs";
5228
+ import { join as join7, relative as relative2, resolve as resolve6 } from "node:path";
5084
5229
 
5085
5230
  // src/workspace/glob.ts
5086
5231
  function toPosix(p) {
@@ -5113,11 +5258,11 @@ function matchGlob(pattern, path2) {
5113
5258
  }
5114
5259
 
5115
5260
  // src/workspace/workspace.ts
5116
- import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync, statSync } from "node:fs";
5117
- import { dirname as dirname2, join as join4, resolve as resolve4 } from "node:path";
5261
+ import { existsSync as existsSync5, readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
5262
+ import { dirname as dirname2, join as join5, resolve as resolve4 } from "node:path";
5118
5263
  function readPackageJson(path2) {
5119
5264
  try {
5120
- return JSON.parse(readFileSync4(path2, "utf8"));
5265
+ return JSON.parse(readFileSync5(path2, "utf8"));
5121
5266
  } catch (e) {
5122
5267
  throw new Error(`cannot read ${path2}: ${e.message}`);
5123
5268
  }
@@ -5126,7 +5271,7 @@ var MARKER_TEXT = /"s2script"[\s\S]*"workspace"\s*:/;
5126
5271
  function carriesWorkspaceMarker(pkgPath) {
5127
5272
  let text3;
5128
5273
  try {
5129
- text3 = readFileSync4(pkgPath, "utf8");
5274
+ text3 = readFileSync5(pkgPath, "utf8");
5130
5275
  } catch {
5131
5276
  return false;
5132
5277
  }
@@ -5140,7 +5285,7 @@ function carriesWorkspaceMarker(pkgPath) {
5140
5285
  function findWorkspaceRoot(from) {
5141
5286
  let dir = resolve4(from);
5142
5287
  for (; ; ) {
5143
- if (carriesWorkspaceMarker(join4(dir, "package.json"))) return dir;
5288
+ if (carriesWorkspaceMarker(join5(dir, "package.json"))) return dir;
5144
5289
  const parent = dirname2(dir);
5145
5290
  if (parent === dir) return null;
5146
5291
  dir = parent;
@@ -5148,7 +5293,7 @@ function findWorkspaceRoot(from) {
5148
5293
  }
5149
5294
  function isDir(path2) {
5150
5295
  try {
5151
- return statSync(path2).isDirectory();
5296
+ return statSync2(path2).isDirectory();
5152
5297
  } catch {
5153
5298
  return false;
5154
5299
  }
@@ -5156,11 +5301,11 @@ function isDir(path2) {
5156
5301
  function subdirs(dir) {
5157
5302
  let entries;
5158
5303
  try {
5159
- entries = readdirSync(dir, { withFileTypes: true });
5304
+ entries = readdirSync2(dir, { withFileTypes: true });
5160
5305
  } catch {
5161
5306
  return [];
5162
5307
  }
5163
- return entries.filter((e) => e.name !== "node_modules" && !e.name.startsWith(".")).filter((e) => e.isDirectory() || e.isSymbolicLink() && isDir(join4(dir, e.name))).map((e) => e.name);
5308
+ return entries.filter((e) => e.name !== "node_modules" && !e.name.startsWith(".")).filter((e) => e.isDirectory() || e.isSymbolicLink() && isDir(join5(dir, e.name))).map((e) => e.name);
5164
5309
  }
5165
5310
  function joinRel(rel, name) {
5166
5311
  return rel === "" ? name : `${rel}/${name}`;
@@ -5174,7 +5319,7 @@ function expandDirGlob(root, pattern) {
5174
5319
  return;
5175
5320
  }
5176
5321
  const head = segments[index];
5177
- const here = rel === "" ? root : join4(root, rel);
5322
+ const here = rel === "" ? root : join5(root, rel);
5178
5323
  if (head === "**") {
5179
5324
  walk(index + 1, rel);
5180
5325
  for (const name of subdirs(here)) walk(index, joinRel(rel, name));
@@ -5182,7 +5327,7 @@ function expandDirGlob(root, pattern) {
5182
5327
  }
5183
5328
  if (!head.includes("*")) {
5184
5329
  const next = joinRel(rel, head);
5185
- if (isDir(join4(root, next))) walk(index + 1, next);
5330
+ if (isDir(join5(root, next))) walk(index + 1, next);
5186
5331
  return;
5187
5332
  }
5188
5333
  for (const name of subdirs(here)) {
@@ -5193,11 +5338,11 @@ function expandDirGlob(root, pattern) {
5193
5338
  return [...out].sort();
5194
5339
  }
5195
5340
  function pnpmWorkspaceGlobs(root) {
5196
- const path2 = join4(root, "pnpm-workspace.yaml");
5197
- if (!existsSync4(path2)) return [];
5341
+ const path2 = join5(root, "pnpm-workspace.yaml");
5342
+ if (!existsSync5(path2)) return [];
5198
5343
  let body;
5199
5344
  try {
5200
- body = readFileSync4(path2, "utf8");
5345
+ body = readFileSync5(path2, "utf8");
5201
5346
  } catch {
5202
5347
  return [];
5203
5348
  }
@@ -5239,7 +5384,7 @@ function loadWorkspace(root) {
5239
5384
  }
5240
5385
  function readWorkspace(root) {
5241
5386
  const absRoot = resolve4(root);
5242
- const pkg = readPackageJson(join4(absRoot, "package.json"));
5387
+ const pkg = readPackageJson(join5(absRoot, "package.json"));
5243
5388
  const marker = pkg.s2script?.workspace;
5244
5389
  if (marker === void 0) {
5245
5390
  throw new Error(
@@ -5264,9 +5409,9 @@ function readWorkspace(root) {
5264
5409
  for (const rel of expandDirGlob(absRoot, glob)) candidates.add(rel);
5265
5410
  }
5266
5411
  for (const rel of [...candidates].sort()) {
5267
- const dir = join4(absRoot, rel);
5268
- const pkgPath = join4(dir, "package.json");
5269
- if (!existsSync4(pkgPath)) continue;
5412
+ const dir = join5(absRoot, rel);
5413
+ const pkgPath = join5(dir, "package.json");
5414
+ if (!existsSync5(pkgPath)) continue;
5270
5415
  pluginDirs.add(rel);
5271
5416
  let pluginPkg;
5272
5417
  try {
@@ -5315,9 +5460,9 @@ function readWorkspace(root) {
5315
5460
  const libs = [];
5316
5461
  for (const rel of [...memberDirs].sort()) {
5317
5462
  if (pluginDirs.has(rel)) continue;
5318
- const dir = join4(absRoot, rel);
5319
- const pkgPath = join4(dir, "package.json");
5320
- if (!existsSync4(pkgPath)) continue;
5463
+ const dir = join5(absRoot, rel);
5464
+ const pkgPath = join5(dir, "package.json");
5465
+ if (!existsSync5(pkgPath)) continue;
5321
5466
  let libPkg;
5322
5467
  try {
5323
5468
  libPkg = readPackageJson(pkgPath);
@@ -5383,8 +5528,8 @@ function ambiguityProblem(iface, publishers) {
5383
5528
  }
5384
5529
 
5385
5530
  // src/publish-gate.ts
5386
- import { existsSync as existsSync5, readFileSync as readFileSync5, statSync as statSync2 } from "node:fs";
5387
- import { join as join5, resolve as resolve5 } from "node:path";
5531
+ import { existsSync as existsSync6, readFileSync as readFileSync6, statSync as statSync3 } from "node:fs";
5532
+ import { join as join6, resolve as resolve5 } from "node:path";
5388
5533
  function hasPublishes(publishes) {
5389
5534
  if (publishes == null) return false;
5390
5535
  if (typeof publishes === "string") return publishes.trim().length > 0;
@@ -5410,14 +5555,14 @@ function assertPublishesTypes(pkg, pluginDir) {
5410
5555
  };
5411
5556
  }
5412
5557
  const typesPath = resolve5(pluginDir, typesRel);
5413
- if (!existsSync5(typesPath)) {
5558
+ if (!existsSync6(typesPath)) {
5414
5559
  return { ok: false, error: `types file not found: ${typesRel}` };
5415
5560
  }
5416
- const st = statSync2(typesPath);
5561
+ const st = statSync3(typesPath);
5417
5562
  if (!st.isFile() || st.size === 0) {
5418
5563
  return { ok: false, error: `types file is empty or not a file: ${typesRel}` };
5419
5564
  }
5420
- const body = readFileSync5(typesPath, "utf8").trim();
5565
+ const body = readFileSync6(typesPath, "utf8").trim();
5421
5566
  if (!body) {
5422
5567
  return { ok: false, error: `types file is empty: ${typesRel}` };
5423
5568
  }
@@ -5426,14 +5571,14 @@ function assertPublishesTypes(pkg, pluginDir) {
5426
5571
 
5427
5572
  // src/workspace/siblings.ts
5428
5573
  function isWorkspaceMemberDir(root, rootPkg, dir) {
5429
- const rel = toPosix(relative(root, dir));
5574
+ const rel = toPosix(relative2(root, dir));
5430
5575
  if (rel === "" || rel.startsWith("..")) return false;
5431
5576
  const globs = [...workspaceGlobs(rootPkg.workspaces), ...pnpmWorkspaceGlobs(root)];
5432
5577
  return globs.some((g) => matchGlob(g, rel));
5433
5578
  }
5434
5579
  function shadowedCopyWarning(pluginDir, dep) {
5435
5580
  const copy = localContractPath(pluginDir, dep);
5436
- const where2 = copy === null ? join6(".s2script", "types", dep, "index.d.ts") : relative(pluginDir, copy);
5581
+ const where2 = copy === null ? join7(".s2script", "types", dep, "index.d.ts") : relative2(pluginDir, copy);
5437
5582
  return `WARN: ${dep} resolves to a workspace sibling, so the verified copy at ${toPosix(where2)} is IGNORED and can be deleted \u2014 the sibling's own contract is what this build typechecks and hashes`;
5438
5583
  }
5439
5584
  function resolveSiblingContracts(pluginDir, deps) {
@@ -5443,13 +5588,13 @@ function resolveSiblingContracts(pluginDir, deps) {
5443
5588
  const absDir = resolve6(pluginDir);
5444
5589
  const root = findWorkspaceRoot(absDir);
5445
5590
  if (root === null) return { siblings, shadowedCopies };
5446
- const rootPkg = JSON.parse(readFileSync6(join6(root, "package.json"), "utf8"));
5591
+ const rootPkg = JSON.parse(readFileSync7(join7(root, "package.json"), "utf8"));
5447
5592
  if (!isWorkspaceMemberDir(root, rootPkg, absDir)) return { siblings, shadowedCopies };
5448
5593
  const scan = scanWorkspace(root);
5449
5594
  const ws = scan.workspace;
5450
5595
  const { producers, ambiguous, byPlugin } = indexInterfaces(ws.plugins);
5451
5596
  const self = ws.plugins.find((p) => resolve6(p.dir) === absDir);
5452
- const label = self?.name ?? toPosix(relative(root, absDir));
5597
+ const label = self?.name ?? toPosix(relative2(root, absDir));
5453
5598
  const unread = scan.problems ?? [];
5454
5599
  const problems = [];
5455
5600
  for (const dep of deps) {
@@ -5497,8 +5642,8 @@ function resolveSiblingContracts(pluginDir, deps) {
5497
5642
  }
5498
5643
 
5499
5644
  // src/libraries.ts
5500
- import { existsSync as existsSync6, readFileSync as readFileSync7 } from "node:fs";
5501
- import { join as join7, resolve as resolve7 } from "node:path";
5645
+ import { existsSync as existsSync7, readFileSync as readFileSync8 } from "node:fs";
5646
+ import { join as join8, resolve as resolve7 } from "node:path";
5502
5647
  function packageKind(pkg) {
5503
5648
  const k = pkg.s2script?.kind ?? "plugin";
5504
5649
  if (k !== "plugin" && k !== "library") {
@@ -5507,13 +5652,13 @@ function packageKind(pkg) {
5507
5652
  return k;
5508
5653
  }
5509
5654
  function librariesRoot(pluginDir) {
5510
- return join7(pluginDir, ".s2script", "libs");
5655
+ return join8(pluginDir, ".s2script", "libs");
5511
5656
  }
5512
5657
  function localLibraryDir(pluginDir, name) {
5513
5658
  const segs = name.split("/");
5514
5659
  if (segs.some((s) => s === "" || s === "." || s === "..")) return null;
5515
- const p = join7(librariesRoot(pluginDir), ...segs);
5516
- return existsSync6(join7(p, "index.d.ts")) ? p : null;
5660
+ const p = join8(librariesRoot(pluginDir), ...segs);
5661
+ return existsSync7(join8(p, "index.d.ts")) ? p : null;
5517
5662
  }
5518
5663
  function resolveLibrarySibling(pluginDir, name) {
5519
5664
  const absDir = resolve7(pluginDir);
@@ -5552,7 +5697,7 @@ function resolveLibrarySibling(pluginDir, name) {
5552
5697
  );
5553
5698
  }
5554
5699
  const typesPath = resolve7(lib.dir, typesRel);
5555
- if (!existsSync6(typesPath)) {
5700
+ if (!existsSync7(typesPath)) {
5556
5701
  throw new Error(
5557
5702
  `library ${name} (workspace sibling at ${lib.relDir}) types file not found: ${typesRel}`
5558
5703
  );
@@ -5567,11 +5712,11 @@ function resolveLibraries(pluginDir, declared) {
5567
5712
  for (const name of Object.keys(declared)) {
5568
5713
  const dir = localLibraryDir(pluginDir, name);
5569
5714
  if (dir !== null) {
5570
- paths[name] = [join7(dir, "index.d.ts")];
5715
+ paths[name] = [join8(dir, "index.d.ts")];
5571
5716
  let version = "0.0.0";
5572
5717
  let apiVersion = "1.x";
5573
5718
  try {
5574
- const m = JSON.parse(readFileSync7(join7(dir, "package.json"), "utf8"));
5719
+ const m = JSON.parse(readFileSync8(join8(dir, "package.json"), "utf8"));
5575
5720
  if (typeof m.version === "string") version = m.version;
5576
5721
  if (typeof m.s2script?.apiVersion === "string") apiVersion = m.s2script.apiVersion;
5577
5722
  } catch {
@@ -5622,27 +5767,27 @@ function assertLibrariesResolved(pluginDir, declared, sdkApiVersion) {
5622
5767
 
5623
5768
  // src/typecheck/typecheck.ts
5624
5769
  function localDeclarationFiles(pluginDir) {
5625
- const srcDir = join8(pluginDir, "src");
5626
- if (!existsSync7(srcDir)) return [];
5627
- return readdirSync2(srcDir).filter((f) => f.endsWith(".d.ts")).map((f) => join8(srcDir, f));
5770
+ const srcDir = join9(pluginDir, "src");
5771
+ if (!existsSync8(srcDir)) return [];
5772
+ return readdirSync3(srcDir).filter((f) => f.endsWith(".d.ts")).map((f) => join9(srcDir, f));
5628
5773
  }
5629
5774
  function gamedataDeclarations(pluginDir, pkg) {
5630
5775
  const data = loadPluginGamedata(pluginDir, pkg) ?? {};
5631
5776
  return /* @__PURE__ */ new Map([
5632
- [join8(pluginDir, ".s2script", "gamedata.d.ts"), generateGamedataTypes(data)],
5633
- [join8(pluginDir, ".s2script", "hooks.d.ts"), generateHookTypes(data)]
5777
+ [join9(pluginDir, ".s2script", "gamedata.d.ts"), generateGamedataTypes(data)],
5778
+ [join9(pluginDir, ".s2script", "hooks.d.ts"), generateHookTypes(data)]
5634
5779
  ]);
5635
5780
  }
5636
5781
  function gamePackageDeclarationFiles(pkg, packagesDir) {
5637
5782
  const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
5638
5783
  if (!("@s2script/cs2" in deps)) return [];
5639
- const hooks = join8(packagesDir, "cs2", "hooks.generated.d.ts");
5640
- return existsSync7(hooks) ? [hooks] : [];
5784
+ const hooks = join9(packagesDir, "cs2", "hooks.generated.d.ts");
5785
+ return existsSync8(hooks) ? [hooks] : [];
5641
5786
  }
5642
5787
  function declaredModules(dtsFiles) {
5643
5788
  const out = /* @__PURE__ */ new Set();
5644
5789
  for (const f of dtsFiles) {
5645
- const body = readFileSync8(f, "utf8");
5790
+ const body = readFileSync9(f, "utf8");
5646
5791
  for (const m of body.matchAll(/declare\s+module\s+["']([^"']+)["']/g))
5647
5792
  out.add(m[1]);
5648
5793
  }
@@ -5651,12 +5796,12 @@ function declaredModules(dtsFiles) {
5651
5796
  function typecheckPlugin(pluginDir, opts) {
5652
5797
  const absDir = resolve8(pluginDir);
5653
5798
  const packagesDir = opts?.packagesDir ? resolve8(opts.packagesDir) : resolvePackagesDir({ pluginDir: absDir });
5654
- const pkg = JSON.parse(readFileSync8(join8(absDir, "package.json"), "utf8"));
5799
+ const pkg = JSON.parse(readFileSync9(join9(absDir, "package.json"), "utf8"));
5655
5800
  const s2 = pkg.s2script ?? {};
5656
5801
  const entryRel = s2.main ?? pkg.main;
5657
5802
  if (!entryRel)
5658
5803
  throw new Error(
5659
- `typecheckPlugin: no entry point in ${join8(absDir, "package.json")}`
5804
+ `typecheckPlugin: no entry point in ${join9(absDir, "package.json")}`
5660
5805
  );
5661
5806
  const entry = resolve8(absDir, entryRel);
5662
5807
  const isAlwaysResolved = (d) => d === "@s2script/sdk" || d.startsWith("@s2script/sdk/") || d === "@s2script/cs2" || d.startsWith("@s2script/cs2/");
@@ -5677,6 +5822,11 @@ function typecheckPlugin(pluginDir, opts) {
5677
5822
  const p = localContractPath(absDir, d);
5678
5823
  if (p !== null) contractPaths[d] = [p];
5679
5824
  }
5825
+ const extensionPaths = /* @__PURE__ */ Object.create(null);
5826
+ for (const name of Object.keys(s2.extensions ?? {})) {
5827
+ const local = join9(absDir, ".s2script", "extensions", name, "index.d.ts");
5828
+ if (existsSync8(local)) extensionPaths[name] = [local];
5829
+ }
5680
5830
  const protocol2 = s2.interfaceProtocol === 2;
5681
5831
  const authoritativePaths = Object.fromEntries(
5682
5832
  Object.entries(contractPaths).map(([name, paths]) => [name, paths[0]])
@@ -5699,9 +5849,9 @@ function typecheckPlugin(pluginDir, opts) {
5699
5849
  for (const [name, path2] of Object.entries(authoritativePaths)) {
5700
5850
  interfaceContracts[name] = extractContract(path2, packagesDir);
5701
5851
  if (!siblings.has(name) && !ownNames.has(name)) {
5702
- const receipt = join8(dirname3(path2), "package.json");
5703
- if (existsSync7(receipt)) {
5704
- const published = JSON.parse(readFileSync8(receipt, "utf8")).s2script?.publishes?.[name];
5852
+ const receipt = join9(dirname3(path2), "package.json");
5853
+ if (existsSync8(receipt)) {
5854
+ const published = JSON.parse(readFileSync9(receipt, "utf8")).s2script?.publishes?.[name];
5705
5855
  if (!published || published.typesSha256 !== hashContract(path2) || published.contract?.sha256 !== interfaceContracts[name].sha256)
5706
5856
  throw new Error(
5707
5857
  `InterfaceContractError: ${name} verified copy hash drift; run s2s add`
@@ -5737,7 +5887,7 @@ function typecheckPlugin(pluginDir, opts) {
5737
5887
  const options = {
5738
5888
  // Accept explicit `.ts` import extensions (node type-stripping requires them for source-to-source
5739
5889
  // imports; esbuild strips them at bundle time). Backward-compatible — extensionless imports still resolve.
5740
- ...sharedProgramOptions(ts4),
5890
+ ...sharedProgramOptions(ts5),
5741
5891
  baseUrl: packagesDir,
5742
5892
  paths: {
5743
5893
  // Builtins: the root barrel `@s2script/sdk` → packages/sdk/index.d.ts; subpaths
@@ -5753,28 +5903,29 @@ function typecheckPlugin(pluginDir, opts) {
5753
5903
  "@s2script/cs2/*": ["cs2/*.d.ts"],
5754
5904
  "@s2script/*": ["*/index.d.ts"],
5755
5905
  ...contractPaths,
5756
- ...libraryPaths
5906
+ ...libraryPaths,
5907
+ ...extensionPaths
5757
5908
  }
5758
5909
  };
5759
5910
  const virtualDeclarations = gamedataDeclarations(absDir, pkg);
5760
5911
  const engineFunctions = loadEngineFunctions(absDir, pkg.name);
5761
- virtualDeclarations.set(join8(absDir, ".s2script", "engine-functions.d.ts"), emitEngineFunctionTypes(engineFunctions));
5912
+ virtualDeclarations.set(join9(absDir, ".s2script", "engine-functions.d.ts"), emitEngineFunctionTypes(engineFunctions));
5762
5913
  const rootNames = [
5763
5914
  entry,
5764
- join8(packagesDir, "sdk", "globals.d.ts"),
5915
+ join9(packagesDir, "sdk", "globals.d.ts"),
5765
5916
  ...localDts,
5766
5917
  ...virtualDeclarations.keys(),
5767
5918
  ...gamePackageDeclarationFiles(pkg, packagesDir)
5768
5919
  ];
5769
- const tmp = mkdtempSync(join8(tmpdir(), "s2tc-"));
5920
+ const tmp = mkdtempSync(join9(tmpdir(), "s2tc-"));
5770
5921
  try {
5771
- const generatedPath = join8(absDir, ".s2script", "interfaces.d.ts");
5922
+ const generatedPath = join9(absDir, ".s2script", "interfaces.d.ts");
5772
5923
  rmSync(generatedPath, { force: true });
5773
5924
  if (protocol2) {
5774
- mkdirSync(join8(absDir, ".s2script"), { recursive: true });
5925
+ mkdirSync(join9(absDir, ".s2script"), { recursive: true });
5775
5926
  let moduleId = 0;
5776
5927
  for (const name of allDeclaredDeps) {
5777
- const path2 = authoritativePaths[name], wrapper = join8(tmp, `contract-${moduleId++}.d.ts`);
5928
+ const path2 = authoritativePaths[name], wrapper = join9(tmp, `contract-${moduleId++}.d.ts`);
5778
5929
  writeFileSync(
5779
5930
  wrapper,
5780
5931
  `import type {Contract} from ${JSON.stringify(
@@ -5801,14 +5952,14 @@ export declare const on: import("@s2script/sdk/interfaces").TypedInterfaceHandle
5801
5952
  rootNames.push(generatedPath);
5802
5953
  }
5803
5954
  if (deps.length && !protocol2) {
5804
- const stub = join8(tmp, "ambient.d.ts");
5955
+ const stub = join9(tmp, "ambient.d.ts");
5805
5956
  writeFileSync(
5806
5957
  stub,
5807
5958
  deps.map((d) => `declare module ${JSON.stringify(d)};`).join("\n") + "\n"
5808
5959
  );
5809
5960
  rootNames.push(stub);
5810
5961
  }
5811
- const host = ts4.createCompilerHost(options);
5962
+ const host = ts5.createCompilerHost(options);
5812
5963
  const originalReadFile = host.readFile.bind(host);
5813
5964
  const originalFileExists = host.fileExists.bind(host);
5814
5965
  host.readFile = (file) => virtualDeclarations.get(resolve8(file)) ?? originalReadFile(file);
@@ -5816,9 +5967,9 @@ export declare const on: import("@s2script/sdk/interfaces").TypedInterfaceHandle
5816
5967
  const originalGetSourceFile = host.getSourceFile.bind(host);
5817
5968
  host.getSourceFile = (file, languageVersion, onError, shouldCreateNewSourceFile) => {
5818
5969
  const text3 = virtualDeclarations.get(resolve8(file));
5819
- return text3 === void 0 ? originalGetSourceFile(file, languageVersion, onError, shouldCreateNewSourceFile) : ts4.createSourceFile(file, text3, languageVersion);
5970
+ return text3 === void 0 ? originalGetSourceFile(file, languageVersion, onError, shouldCreateNewSourceFile) : ts5.createSourceFile(file, text3, languageVersion);
5820
5971
  };
5821
- const program = ts4.createProgram(rootNames, options, host);
5972
+ const program = ts5.createProgram(rootNames, options, host);
5822
5973
  const diags = [
5823
5974
  ...program.getSyntacticDiagnostics(),
5824
5975
  ...program.getSemanticDiagnostics(),
@@ -5846,7 +5997,7 @@ export declare const on: import("@s2script/sdk/interfaces").TypedInterfaceHandle
5846
5997
  line,
5847
5998
  col,
5848
5999
  code: d.code,
5849
- message: ts4.flattenDiagnosticMessageText(d.messageText, "\n")
6000
+ message: ts5.flattenDiagnosticMessageText(d.messageText, "\n")
5850
6001
  };
5851
6002
  });
5852
6003
  return {
@@ -5967,12 +6118,12 @@ var STAMPED_API_VERSION = `${HOST_API_VERSION_MAJOR}.x`;
5967
6118
 
5968
6119
  // src/npm-deps-gate.ts
5969
6120
  import { lstatSync, realpathSync } from "node:fs";
5970
- import { dirname as dirname4, join as join9, resolve as resolve9, sep as sep2 } from "node:path";
6121
+ import { dirname as dirname4, join as join10, resolve as resolve9, sep as sep2 } from "node:path";
5971
6122
  function findInNodeModules(startDir, dep) {
5972
6123
  const segs = dep.split("/");
5973
6124
  let dir = resolve9(startDir);
5974
6125
  for (; ; ) {
5975
- const candidate = join9(dir, "node_modules", ...segs);
6126
+ const candidate = join10(dir, "node_modules", ...segs);
5976
6127
  try {
5977
6128
  lstatSync(candidate);
5978
6129
  return candidate;
@@ -6013,20 +6164,20 @@ function assertNoNpmRuntimeDeps(pkg, pluginDir) {
6013
6164
 
6014
6165
  // src/build-library.ts
6015
6166
  import * as esbuild from "esbuild";
6016
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync9, statSync as statSync3 } from "node:fs";
6017
- import { resolve as resolve11, join as join11 } from "node:path";
6167
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync10, statSync as statSync4 } from "node:fs";
6168
+ import { resolve as resolve11, join as join12 } from "node:path";
6018
6169
 
6019
6170
  // src/lint/lint.ts
6020
6171
  import { ESLint } from "eslint";
6021
- import { existsSync as existsSync8, readdirSync as readdirSync3 } from "node:fs";
6022
- import { dirname as dirname5, join as join10, resolve as resolve10 } from "node:path";
6172
+ import { existsSync as existsSync9, readdirSync as readdirSync4 } from "node:fs";
6173
+ import { dirname as dirname5, join as join11, resolve as resolve10 } from "node:path";
6023
6174
  import s2lint from "@s2script/eslint-plugin";
6024
6175
  var CONFIG_FILES = ["eslint.config.js", "eslint.config.mjs", "eslint.config.cjs", "eslint.config.ts"];
6025
6176
  function ownConfigDir(pluginDir) {
6026
6177
  const stopAt = findWorkspaceRoot(pluginDir);
6027
6178
  let dir = pluginDir;
6028
6179
  for (; ; ) {
6029
- if (CONFIG_FILES.some((f) => existsSync8(join10(dir, f)))) return dir;
6180
+ if (CONFIG_FILES.some((f) => existsSync9(join11(dir, f)))) return dir;
6030
6181
  if (stopAt === null || dir === stopAt) return null;
6031
6182
  const parent = dirname5(dir);
6032
6183
  if (parent === dir) return null;
@@ -6036,14 +6187,14 @@ function ownConfigDir(pluginDir) {
6036
6187
  function collectTsFiles(dir) {
6037
6188
  let entries;
6038
6189
  try {
6039
- entries = readdirSync3(dir, { withFileTypes: true });
6190
+ entries = readdirSync4(dir, { withFileTypes: true });
6040
6191
  } catch {
6041
6192
  return [];
6042
6193
  }
6043
6194
  const out = [];
6044
6195
  for (const e of entries) {
6045
6196
  if (e.name === "node_modules" || e.name.startsWith(".")) continue;
6046
- const full = join10(dir, e.name);
6197
+ const full = join11(dir, e.name);
6047
6198
  if (e.isDirectory()) out.push(...collectTsFiles(full));
6048
6199
  else if (e.isFile() && e.name.endsWith(".ts") && !e.name.endsWith(".d.ts")) out.push(full);
6049
6200
  }
@@ -6072,8 +6223,8 @@ async function lintPlugin(pluginDir, program) {
6072
6223
  // src/build-library.ts
6073
6224
  async function buildLibrary(dir, packagesDir) {
6074
6225
  const absDir = resolve11(dir);
6075
- const pkgPath = join11(absDir, "package.json");
6076
- const pkg = JSON.parse(readFileSync9(pkgPath, "utf8"));
6226
+ const pkgPath = join12(absDir, "package.json");
6227
+ const pkg = JSON.parse(readFileSync10(pkgPath, "utf8"));
6077
6228
  const s2 = pkg.s2script ?? {};
6078
6229
  if (s2.pluginDependencies || s2.optionalPluginDependencies) {
6079
6230
  throw new Error(
@@ -6088,7 +6239,7 @@ async function buildLibrary(dir, packagesDir) {
6088
6239
  throw new Error('a library must set "types" to a .d.ts \u2014 consumers vendor it to typecheck against');
6089
6240
  }
6090
6241
  const typesPath = resolve11(absDir, typesRel);
6091
- if (!existsSync9(typesPath) || !statSync3(typesPath).isFile() || statSync3(typesPath).size === 0) {
6242
+ if (!existsSync10(typesPath) || !statSync4(typesPath).isFile() || statSync4(typesPath).size === 0) {
6092
6243
  throw new Error(`types file is missing or empty: ${typesRel}`);
6093
6244
  }
6094
6245
  assertNoNpmRuntimeDeps(pkg, absDir);
@@ -6107,7 +6258,7 @@ ${lint.output}`);
6107
6258
  const entryRelative = s2.main ?? pkg.main;
6108
6259
  if (!entryRelative) throw new Error(`buildLibrary: no entry point in ${pkgPath} (set s2script.main or main)`);
6109
6260
  const result = await esbuild.build({
6110
- entryPoints: [join11(absDir, entryRelative)],
6261
+ entryPoints: [join12(absDir, entryRelative)],
6111
6262
  bundle: true,
6112
6263
  platform: "neutral",
6113
6264
  format: "cjs",
@@ -6133,15 +6284,15 @@ ${lint.output}`);
6133
6284
  types: "index.d.ts"
6134
6285
  };
6135
6286
  const sanitized = String(pkg.name).replace(/[^a-zA-Z0-9._-]/g, "_");
6136
- const outDir = join11(absDir, "dist");
6287
+ const outDir = join12(absDir, "dist");
6137
6288
  mkdirSync2(outDir, { recursive: true });
6138
- const outPath = join11(outDir, `${sanitized}.s2lib`);
6289
+ const outPath = join12(outDir, `${sanitized}.s2lib`);
6139
6290
  writeFileSync2(
6140
6291
  outPath,
6141
6292
  zipSync({
6142
6293
  "manifest.json": Buffer.from(JSON.stringify(manifest, null, 2)),
6143
6294
  "index.js": Buffer.from(result.outputFiles[0].text),
6144
- "index.d.ts": readFileSync9(typesPath)
6295
+ "index.d.ts": readFileSync10(typesPath)
6145
6296
  })
6146
6297
  );
6147
6298
  return outPath;
@@ -6150,8 +6301,8 @@ ${lint.output}`);
6150
6301
  // src/build.ts
6151
6302
  async function buildPlugin(dir, packagesDir) {
6152
6303
  const absDir = resolve12(dir);
6153
- const pkgPath = join12(absDir, "package.json");
6154
- const pkg = JSON.parse(readFileSync10(pkgPath, "utf8"));
6304
+ const pkgPath = join13(absDir, "package.json");
6305
+ const pkg = JSON.parse(readFileSync11(pkgPath, "utf8"));
6155
6306
  if (packageKind(pkg) === "library") {
6156
6307
  throw new Error(`${pkgPath} is s2script.kind="library" \u2014 build it with buildLibrary/buildPackage`);
6157
6308
  }
@@ -6175,6 +6326,17 @@ async function buildPlugin(dir, packagesDir) {
6175
6326
  );
6176
6327
  }
6177
6328
  }
6329
+ const extensions = s2.extensions ?? {};
6330
+ for (const name2 of Object.keys(extensions)) {
6331
+ if (pluginDepNames.has(name2) || Object.hasOwn(libraries, name2)) {
6332
+ throw new Error(
6333
+ `${JSON.stringify(name2)} is declared in s2script.extensions and also as a library or plugin dependency \u2014 a native extension is resolved by the host from addons/s2script/extensions/, so it cannot share a name with either`
6334
+ );
6335
+ }
6336
+ if (name2.startsWith("@s2script/sdk")) {
6337
+ throw new Error(`${JSON.stringify(name2)}: s2script.extensions cannot name an @s2script/sdk module`);
6338
+ }
6339
+ }
6178
6340
  const librariesResolved = assertLibrariesResolved(absDir, libraries, STAMPED_API_VERSION);
6179
6341
  const permissions = Array.isArray(s2.permissions) ? s2.permissions : [];
6180
6342
  const gamedata = loadPluginGamedata(absDir, pkg);
@@ -6187,27 +6349,27 @@ async function buildPlugin(dir, packagesDir) {
6187
6349
  throw new Error(`remove authored ${permission} from s2script.permissions: gamedata/functions.jsonc derives it automatically`);
6188
6350
  }
6189
6351
  }
6190
- mkdirSync3(join12(absDir, ".s2script"), { recursive: true });
6191
- writeFileSync3(join12(absDir, ".s2script", "engine-functions.d.ts"), emitEngineFunctionTypes(engineFunctions));
6352
+ mkdirSync3(join13(absDir, ".s2script"), { recursive: true });
6353
+ writeFileSync3(join13(absDir, ".s2script", "engine-functions.d.ts"), emitEngineFunctionTypes(engineFunctions));
6192
6354
  console.log(`engine functions: derived permissions ${derivedFunctions.permissions.join(", ") || "(none)"}`);
6193
6355
  for (const f of derivedFunctions.summary.functions) {
6194
6356
  if (f.mutates || f.suppresses) console.log(`engine functions: ${f.canonicalId} risks ${[f.mutates ? "mutation" : "", f.suppresses ? "suppression" : ""].filter(Boolean).join(", ")}`);
6195
6357
  }
6196
6358
  } else {
6197
- rmSync2(join12(absDir, ".s2script", "engine-functions.d.ts"), { force: true });
6359
+ rmSync2(join13(absDir, ".s2script", "engine-functions.d.ts"), { force: true });
6198
6360
  }
6199
6361
  if (gamedata) {
6200
- const genDir = join12(absDir, ".s2script");
6362
+ const genDir = join13(absDir, ".s2script");
6201
6363
  mkdirSync3(genDir, { recursive: true });
6202
- writeFileSync3(join12(genDir, "gamedata.d.ts"), generateGamedataTypes(gamedata));
6364
+ writeFileSync3(join13(genDir, "gamedata.d.ts"), generateGamedataTypes(gamedata));
6203
6365
  if (gamedata.hooks && Object.keys(gamedata.hooks).length) {
6204
- writeFileSync3(join12(genDir, "hooks.d.ts"), generateHookTypes(gamedata));
6366
+ writeFileSync3(join13(genDir, "hooks.d.ts"), generateHookTypes(gamedata));
6205
6367
  } else {
6206
- rmSync2(join12(genDir, "hooks.d.ts"), { force: true });
6368
+ rmSync2(join13(genDir, "hooks.d.ts"), { force: true });
6207
6369
  }
6208
6370
  } else {
6209
- rmSync2(join12(absDir, ".s2script", "gamedata.d.ts"), { force: true });
6210
- rmSync2(join12(absDir, ".s2script", "hooks.d.ts"), { force: true });
6371
+ rmSync2(join13(absDir, ".s2script", "gamedata.d.ts"), { force: true });
6372
+ rmSync2(join13(absDir, ".s2script", "hooks.d.ts"), { force: true });
6211
6373
  }
6212
6374
  const declaredDepNames = [
6213
6375
  ...Object.keys(s2.pluginDependencies ?? {}),
@@ -6282,6 +6444,13 @@ ${lint.output}`);
6282
6444
  );
6283
6445
  }
6284
6446
  }
6447
+ for (const ext2 of Object.keys(extensions)) {
6448
+ if (!scan.importNames.includes(ext2)) {
6449
+ console.warn(
6450
+ `WARN: extension ${JSON.stringify(ext2)} is declared but never imported \u2014 the plugin still refuses to load on a server without it`
6451
+ );
6452
+ }
6453
+ }
6285
6454
  const { name, version } = pkg;
6286
6455
  if (s2.apiVersion !== void 0) {
6287
6456
  console.warn(
@@ -6292,7 +6461,8 @@ ${lint.output}`);
6292
6461
  const external = Array.from(/* @__PURE__ */ new Set([
6293
6462
  "@s2script/*",
6294
6463
  ...Object.keys(pluginDependencies),
6295
- ...Object.keys(optionalPluginDependencies)
6464
+ ...Object.keys(optionalPluginDependencies),
6465
+ ...Object.keys(extensions)
6296
6466
  ]));
6297
6467
  const entryRelative = s2.main ?? pkg.main;
6298
6468
  if (!entryRelative) {
@@ -6300,7 +6470,7 @@ ${lint.output}`);
6300
6470
  `buildPlugin: no entry point found in ${pkgPath} (set s2script.main or main)`
6301
6471
  );
6302
6472
  }
6303
- const entryPoint = join12(absDir, entryRelative);
6473
+ const entryPoint = join13(absDir, entryRelative);
6304
6474
  const result = await esbuild2.build({
6305
6475
  entryPoints: [entryPoint],
6306
6476
  bundle: true,
@@ -6344,6 +6514,7 @@ ${lint.output}`);
6344
6514
  manifest.interfaceContracts = imports;
6345
6515
  }
6346
6516
  if (config !== void 0) manifest.config = config;
6517
+ if (Object.keys(extensions).length > 0) manifest.extensions = extensions;
6347
6518
  const gamedataPermissions = [
6348
6519
  ...gamedata?.calls && Object.keys(gamedata.calls).length ? ["engine:calls"] : [],
6349
6520
  ...gamedata?.hooks && Object.keys(gamedata.hooks).length ? ["engine:hooks"] : []
@@ -6374,21 +6545,21 @@ ${lint.output}`);
6374
6545
  if (gamedata) zipFiles["gamedata.json"] = Buffer.from(JSON.stringify(gamedata, null, 2));
6375
6546
  if (engineFunctions) zipFiles["engine-functions.json"] = Buffer.from(engineFunctionsArchive(engineFunctions));
6376
6547
  if (gate.typesPath !== null && Object.keys(derivedPublishes).length > 0) {
6377
- const contract = readFileSync10(gate.typesPath);
6548
+ const contract = readFileSync11(gate.typesPath);
6378
6549
  for (const iface of Object.keys(derivedPublishes)) {
6379
6550
  const safe = iface.replace(/[^a-zA-Z0-9._-]/g, "_");
6380
6551
  zipFiles[`types/${safe}.d.ts`] = contract;
6381
6552
  }
6382
6553
  }
6383
6554
  const sanitizedId = name.replace(/[^a-zA-Z0-9._-]/g, "_");
6384
- const outDir = join12(absDir, "dist");
6555
+ const outDir = join13(absDir, "dist");
6385
6556
  mkdirSync3(outDir, { recursive: true });
6386
- const outPath = join12(outDir, `${sanitizedId}.s2sp`);
6557
+ const outPath = join13(outDir, `${sanitizedId}.s2sp`);
6387
6558
  writeFileSync3(outPath, zipSync(zipFiles));
6388
6559
  return outPath;
6389
6560
  }
6390
6561
  async function buildPackage(dir, packagesDir) {
6391
- const pkg = JSON.parse(readFileSync10(join12(resolve12(dir), "package.json"), "utf8"));
6562
+ const pkg = JSON.parse(readFileSync11(join13(resolve12(dir), "package.json"), "utf8"));
6392
6563
  return packageKind(pkg) === "library" ? buildLibrary(dir, packagesDir) : buildPlugin(dir, packagesDir);
6393
6564
  }
6394
6565
 
@@ -6420,7 +6591,7 @@ function positionals(args, flagsWithValue) {
6420
6591
 
6421
6592
  // src/workspace/build-all.ts
6422
6593
  import { writeFileSync as writeFileSync4 } from "node:fs";
6423
- import { join as join13 } from "node:path";
6594
+ import { join as join14 } from "node:path";
6424
6595
 
6425
6596
  // src/workspace/graph.ts
6426
6597
  function topoOrder(batch, warn = console.warn) {
@@ -6740,7 +6911,7 @@ function stampWorkspaceVersion(ws, version) {
6740
6911
  dirty = true;
6741
6912
  }
6742
6913
  }
6743
- if (dirty) writeFileSync4(join13(p.dir, "package.json"), `${JSON.stringify(next, null, 2)}
6914
+ if (dirty) writeFileSync4(join14(p.dir, "package.json"), `${JSON.stringify(next, null, 2)}
6744
6915
  `);
6745
6916
  }
6746
6917
  return result;
@@ -7884,10 +8055,10 @@ var spinner = ({
7884
8055
  ...I2
7885
8056
  } = {}) => {
7886
8057
  const u4 = isCI();
7887
- let M, T, d = false, S3 = false, s = "", p, w = performance.now();
8058
+ let M, T, d = false, S4 = false, s = "", p, w = performance.now();
7888
8059
  const x = getColumns(n3), k = I2?.styleFrame ?? W, g = (e) => {
7889
8060
  const r2 = e > 1 ? O ?? settings.messages.error : G ?? settings.messages.cancel;
7890
- S3 = e === 1, d && (a2(r2, e), S3 && typeof h2 == "function" && h2());
8061
+ S4 = e === 1, d && (a2(r2, e), S4 && typeof h2 == "function" && h2());
7891
8062
  }, f = () => g(2), i2 = () => g(1), A = () => {
7892
8063
  process.on("uncaughtExceptionMonitor", f), process.on("unhandledRejection", f), process.on("SIGINT", i2), process.on("SIGTERM", i2), process.on("exit", g), m && m.addEventListener("abort", i2);
7893
8064
  }, H = () => {
@@ -7947,7 +8118,7 @@ var spinner = ({
7947
8118
  error: (e = "") => a2(e, 2),
7948
8119
  clear: () => a2("", 0, true),
7949
8120
  get isCancelled() {
7950
- return S3;
8121
+ return S4;
7951
8122
  }
7952
8123
  };
7953
8124
  };
@@ -8221,11 +8392,11 @@ async function runWorkspace(argv2, root, interactive, filters, stampVersion) {
8221
8392
  import { resolve as resolve16 } from "node:path";
8222
8393
 
8223
8394
  // src/registry/deploy.ts
8224
- import { readdirSync as readdirSync5, readFileSync as readFileSync14 } from "node:fs";
8395
+ import { readdirSync as readdirSync6, readFileSync as readFileSync15 } from "node:fs";
8225
8396
  import { resolve as resolve15 } from "node:path";
8226
8397
 
8227
8398
  // src/types-pack.ts
8228
- import { readFileSync as readFileSync11 } from "node:fs";
8399
+ import { readFileSync as readFileSync12 } from "node:fs";
8229
8400
 
8230
8401
  // src/tar.ts
8231
8402
  import { gzipSync } from "node:zlib";
@@ -8281,7 +8452,7 @@ function packTar(files, opts = {}) {
8281
8452
 
8282
8453
  // src/types-pack.ts
8283
8454
  function packTypesTarball(input) {
8284
- const dts = readFileSync11(input.typesPath);
8455
+ const dts = readFileSync12(input.typesPath);
8285
8456
  const pkgJson = Buffer.from(
8286
8457
  JSON.stringify(
8287
8458
  {
@@ -8306,8 +8477,8 @@ function packTypesTarball(input) {
8306
8477
 
8307
8478
  // src/source-pack.ts
8308
8479
  var import_ignore = __toESM(require_ignore(), 1);
8309
- import { readdirSync as readdirSync4, readFileSync as readFileSync12, statSync as statSync4 } from "node:fs";
8310
- import { dirname as dirname6, join as join14, relative as relative2, resolve as resolve14, sep as sep4 } from "node:path";
8480
+ import { readdirSync as readdirSync5, readFileSync as readFileSync13, statSync as statSync5 } from "node:fs";
8481
+ import { dirname as dirname6, join as join15, relative as relative3, resolve as resolve14, sep as sep4 } from "node:path";
8311
8482
 
8312
8483
  // ../../node_modules/balanced-match/dist/esm/index.js
8313
8484
  var balanced = (a2, b, str) => {
@@ -10205,7 +10376,7 @@ function readIgnoreFile(dir) {
10205
10376
  for (const name of [".npmignore", ".gitignore"]) {
10206
10377
  let text3;
10207
10378
  try {
10208
- text3 = readFileSync12(join14(dir, name), "utf8");
10379
+ text3 = readFileSync13(join15(dir, name), "utf8");
10209
10380
  } catch {
10210
10381
  continue;
10211
10382
  }
@@ -10216,7 +10387,7 @@ function readIgnoreFile(dir) {
10216
10387
  function isIgnored(abs, isDir2, rulesets) {
10217
10388
  let ignored = false;
10218
10389
  for (const { base, rules } of rulesets) {
10219
- const rel = toPosix2(relative2(base, abs));
10390
+ const rel = toPosix2(relative3(base, abs));
10220
10391
  if (!rel || rel.startsWith("..")) continue;
10221
10392
  const r2 = rules.test(isDir2 ? `${rel}/` : rel);
10222
10393
  if (r2.ignored) ignored = true;
@@ -10262,7 +10433,7 @@ function entryPoints(pkg) {
10262
10433
  function npmWorkspaceRoot(absDir) {
10263
10434
  for (let d = dirname6(absDir); ; d = dirname6(d)) {
10264
10435
  try {
10265
- const pj = JSON.parse(readFileSync12(join14(d, "package.json"), "utf8"));
10436
+ const pj = JSON.parse(readFileSync13(join15(d, "package.json"), "utf8"));
10266
10437
  if (pj && pj.workspaces) return d;
10267
10438
  } catch {
10268
10439
  }
@@ -10281,7 +10452,7 @@ function workspaceRulesets(absDir) {
10281
10452
  }
10282
10453
  function listPackageFiles(dir, pkg) {
10283
10454
  const absDir = resolve14(dir);
10284
- const pj = pkg ?? JSON.parse(readFileSync12(join14(absDir, "package.json"), "utf8"));
10455
+ const pj = pkg ?? JSON.parse(readFileSync13(join15(absDir, "package.json"), "utf8"));
10285
10456
  const files = filesPatterns(pj.files);
10286
10457
  const never = (0, import_ignore.default)().add(NEVER_PACKED);
10287
10458
  const forced = new Set(entryPoints(pj));
@@ -10290,12 +10461,12 @@ function listPackageFiles(dir, pkg) {
10290
10461
  const isRoot = abs === absDir;
10291
10462
  const own = isRoot && files ? null : readIgnoreFile(abs);
10292
10463
  const here = own ? [...rulesets, own] : rulesets;
10293
- const entries = readdirSync4(abs, { withFileTypes: true }).sort(
10464
+ const entries = readdirSync5(abs, { withFileTypes: true }).sort(
10294
10465
  (a2, b) => a2.name < b.name ? -1 : a2.name > b.name ? 1 : 0
10295
10466
  );
10296
10467
  for (const entry of entries) {
10297
- const childAbs = join14(abs, entry.name);
10298
- const rel = toPosix2(relative2(absDir, childAbs));
10468
+ const childAbs = join15(abs, entry.name);
10469
+ const rel = toPosix2(relative3(absDir, childAbs));
10299
10470
  if (entry.isSymbolicLink()) continue;
10300
10471
  if (entry.isDirectory()) {
10301
10472
  if (never.ignores(`${rel}/`)) continue;
@@ -10319,9 +10490,9 @@ function listPackageFiles(dir, pkg) {
10319
10490
  function packSourceTarball(dir, pkg) {
10320
10491
  const absDir = resolve14(dir);
10321
10492
  const files = listPackageFiles(absDir, pkg).map((path2) => {
10322
- const abs = join14(absDir, path2);
10323
- const content = readFileSync12(abs);
10324
- const mode = statSync4(abs).mode & 73 ? 493 : 420;
10493
+ const abs = join15(absDir, path2);
10494
+ const content = readFileSync13(abs);
10495
+ const mode = statSync5(abs).mode & 73 ? 493 : 420;
10325
10496
  return { path: path2, content, mode };
10326
10497
  });
10327
10498
  const tarball = packTar(
@@ -10347,11 +10518,11 @@ function tarballFilename(name, version) {
10347
10518
  }
10348
10519
 
10349
10520
  // src/registry/credentials.ts
10350
- import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync13, writeFileSync as writeFileSync5 } from "node:fs";
10521
+ import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync14, writeFileSync as writeFileSync5 } from "node:fs";
10351
10522
  import { homedir } from "node:os";
10352
- import { join as join15 } from "node:path";
10523
+ import { join as join16 } from "node:path";
10353
10524
  function credentialsPath() {
10354
- return join15(homedir(), ".s2script", "credentials.json");
10525
+ return join16(homedir(), ".s2script", "credentials.json");
10355
10526
  }
10356
10527
  function normalizeRegistryUrl(url) {
10357
10528
  const trimmed = url.replace(/\/$/, "");
@@ -10369,9 +10540,9 @@ function loadCredentials() {
10369
10540
  return { registryUrl: defaultRegistryUrl(), token: fromEnv };
10370
10541
  }
10371
10542
  const p = credentialsPath();
10372
- if (!existsSync10(p)) return null;
10543
+ if (!existsSync11(p)) return null;
10373
10544
  try {
10374
- const j = JSON.parse(readFileSync13(p, "utf8"));
10545
+ const j = JSON.parse(readFileSync14(p, "utf8"));
10375
10546
  if (!j.token) return null;
10376
10547
  return {
10377
10548
  registryUrl: normalizeRegistryUrl(j.registryUrl || defaultRegistryUrl()),
@@ -10382,7 +10553,7 @@ function loadCredentials() {
10382
10553
  }
10383
10554
  }
10384
10555
  function saveCredentials(creds) {
10385
- const dir = join15(homedir(), ".s2script");
10556
+ const dir = join16(homedir(), ".s2script");
10386
10557
  mkdirSync4(dir, { recursive: true });
10387
10558
  writeFileSync5(
10388
10559
  credentialsPath(),
@@ -10630,10 +10801,10 @@ function assertDeployable(pkg, label) {
10630
10801
  }
10631
10802
  var MAX_README_CHARS = 1e5;
10632
10803
  function readPackageReadme(pluginDir) {
10633
- const names = readdirSync5(pluginDir).filter((n3) => /^readme(\.(md|markdown))?$/i.test(n3));
10804
+ const names = readdirSync6(pluginDir).filter((n3) => /^readme(\.(md|markdown))?$/i.test(n3));
10634
10805
  if (names.length === 0) return null;
10635
10806
  const name = names.find((n3) => n3 === "README.md") ?? names.sort()[0];
10636
- const text3 = readFileSync14(resolve15(pluginDir, name), "utf8").trim();
10807
+ const text3 = readFileSync15(resolve15(pluginDir, name), "utf8").trim();
10637
10808
  if (!text3) return null;
10638
10809
  if (text3.length > MAX_README_CHARS) {
10639
10810
  throw new Error(
@@ -10657,7 +10828,7 @@ function assembleDeployArchive(pluginDir, pkg, outPath, opts = {}) {
10657
10828
  const absDir = resolve15(pluginDir);
10658
10829
  const access = opts.access ?? resolveAccess(pkg);
10659
10830
  if (packageKind(pkg) === "library") {
10660
- const lib = readFileSync14(outPath);
10831
+ const lib = readFileSync15(outPath);
10661
10832
  const manifestEntry2 = unzipSync(lib)["manifest.json"];
10662
10833
  if (!manifestEntry2) {
10663
10834
  throw new Error(`built archive missing manifest.json: ${outPath}`);
@@ -10674,7 +10845,7 @@ function assembleDeployArchive(pluginDir, pkg, outPath, opts = {}) {
10674
10845
  }
10675
10846
  const gate = assertPublishesTypes(pkg, absDir);
10676
10847
  if (!gate.ok) throw new Error(`publish gate failed: ${gate.error}`);
10677
- const s2sp = readFileSync14(outPath);
10848
+ const s2sp = readFileSync15(outPath);
10678
10849
  const manifestEntry = unzipSync(s2sp)["manifest.json"];
10679
10850
  if (!manifestEntry) {
10680
10851
  throw new Error(`built archive missing manifest.json: ${outPath}`);
@@ -10700,7 +10871,7 @@ function assembleDeployArchive(pluginDir, pkg, outPath, opts = {}) {
10700
10871
  }
10701
10872
  async function deployPlugin(opts) {
10702
10873
  const absDir = resolve15(opts.dir);
10703
- const pkg = JSON.parse(readFileSync14(resolve15(absDir, "package.json"), "utf8"));
10874
+ const pkg = JSON.parse(readFileSync15(resolve15(absDir, "package.json"), "utf8"));
10704
10875
  assertDeployable(pkg, absDir);
10705
10876
  const access = resolveAccess(pkg, opts.access);
10706
10877
  assertAccessAllowed(pkg.name ?? absDir, access);
@@ -11041,14 +11212,14 @@ async function runWorkspaceDeploy(opts) {
11041
11212
  import { resolve as resolve17 } from "node:path";
11042
11213
 
11043
11214
  // src/version/version.ts
11044
- import { existsSync as existsSync11 } from "node:fs";
11045
- import { join as join19 } from "node:path";
11215
+ import { existsSync as existsSync12 } from "node:fs";
11216
+ import { join as join20 } from "node:path";
11046
11217
 
11047
11218
  // src/version/changesets.ts
11048
11219
  var import_semver = __toESM(require_semver2(), 1);
11049
- import { readFileSync as readFileSync15 } from "node:fs";
11220
+ import { readFileSync as readFileSync16 } from "node:fs";
11050
11221
  import { createRequire as createRequire2 } from "node:module";
11051
- import { join as join16 } from "node:path";
11222
+ import { join as join17 } from "node:path";
11052
11223
  import { pathToFileURL } from "node:url";
11053
11224
  var SUPPORTED = {
11054
11225
  "@changesets/assemble-release-plan": ">=6.0.0 <7.0.0",
@@ -11068,7 +11239,7 @@ function named(mod, key) {
11068
11239
  return outer?.[key];
11069
11240
  }
11070
11241
  async function loadChangesets(workspaceRoot) {
11071
- const require3 = createRequire2(join16(workspaceRoot, "package.json"));
11242
+ const require3 = createRequire2(join17(workspaceRoot, "package.json"));
11072
11243
  const missing = [];
11073
11244
  const wrongVersion = [];
11074
11245
  const resolved = /* @__PURE__ */ new Map();
@@ -11083,7 +11254,7 @@ async function loadChangesets(workspaceRoot) {
11083
11254
  missing.push(name);
11084
11255
  continue;
11085
11256
  }
11086
- const version = String(JSON.parse(readFileSync15(manifest, "utf8")).version ?? "");
11257
+ const version = String(JSON.parse(readFileSync16(manifest, "utf8")).version ?? "");
11087
11258
  if (!(0, import_semver.satisfies)(version, range2, { includePrerelease: true })) {
11088
11259
  wrongVersion.push(`${name}@${version} (supported: ${range2})`);
11089
11260
  continue;
@@ -11183,8 +11354,8 @@ function mirrorPackages(ws) {
11183
11354
 
11184
11355
  // src/version/ranges.ts
11185
11356
  var import_semver2 = __toESM(require_semver2(), 1);
11186
- import { readFileSync as readFileSync16, writeFileSync as writeFileSync6 } from "node:fs";
11187
- import { join as join17 } from "node:path";
11357
+ import { readFileSync as readFileSync17, writeFileSync as writeFileSync6 } from "node:fs";
11358
+ import { join as join18 } from "node:path";
11188
11359
  var BUMP_LEVEL = { none: 0, patch: 1, minor: 2, major: 3 };
11189
11360
  function rangePrefix(range2) {
11190
11361
  if (range2.startsWith(">=")) return ">=";
@@ -11203,7 +11374,7 @@ function shouldUpdate(release, range2, config) {
11203
11374
  return BUMP_LEVEL[release.type] >= minLevel;
11204
11375
  }
11205
11376
  function readJsonFile(path2) {
11206
- const raw = readFileSync16(path2, "utf8");
11377
+ const raw = readFileSync17(path2, "utf8");
11207
11378
  const match2 = /^[ \t]*[{[]\s*\n([ \t]+)/.exec(raw);
11208
11379
  return {
11209
11380
  json: JSON.parse(raw),
@@ -11244,7 +11415,7 @@ function rewriteSiblingRanges(ws, releases, config) {
11244
11415
  for (const [consumerName, ifaceRanges] of edits) {
11245
11416
  const plugin = ws.plugins.find((p) => p.name === consumerName);
11246
11417
  if (plugin === void 0) continue;
11247
- const path2 = join17(plugin.dir, "package.json");
11418
+ const path2 = join18(plugin.dir, "package.json");
11248
11419
  const { json, indent, eol } = readJsonFile(path2);
11249
11420
  const s2 = json.s2script;
11250
11421
  if (s2?.pluginDependencies === void 0) continue;
@@ -11258,7 +11429,7 @@ function rewriteSiblingRanges(ws, releases, config) {
11258
11429
  // src/version/bundle-bump.ts
11259
11430
  var import_semver3 = __toESM(require_semver2(), 1);
11260
11431
  import { writeFileSync as writeFileSync7 } from "node:fs";
11261
- import { join as join18 } from "node:path";
11432
+ import { join as join19 } from "node:path";
11262
11433
  var LEVEL = { none: 0, patch: 1, minor: 2, major: 3 };
11263
11434
  function plannedBundleBump(ws, releases) {
11264
11435
  const bundle = readBundle(ws);
@@ -11282,7 +11453,7 @@ function plannedBundleBump(ws, releases) {
11282
11453
  function bumpBundle(ws, releases) {
11283
11454
  const bump = plannedBundleBump(ws, releases);
11284
11455
  if (!bump) return { bump: null, touchedFiles: [] };
11285
- const path2 = join18(ws.root, "package.json");
11456
+ const path2 = join19(ws.root, "package.json");
11286
11457
  const { json, indent, eol } = readJsonFile(path2);
11287
11458
  json.version = bump.to;
11288
11459
  writeFileSync7(path2, JSON.stringify(json, null, indent) + eol);
@@ -11292,8 +11463,8 @@ function bumpBundle(ws, releases) {
11292
11463
  // src/version/version.ts
11293
11464
  async function versionWorkspace(root, opts = {}) {
11294
11465
  const ws = loadWorkspace(root);
11295
- const configPath = join19(ws.root, ".changeset", "config.json");
11296
- if (!existsSync11(configPath)) {
11466
+ const configPath = join20(ws.root, ".changeset", "config.json");
11467
+ if (!existsSync12(configPath)) {
11297
11468
  throw new Error(
11298
11469
  `s2s version: ${configPath} does not exist \u2014 this workspace is not set up for changesets. Run \`npx changeset init\` at ${ws.root}.`
11299
11470
  );
@@ -11380,15 +11551,15 @@ import { resolve as resolve19 } from "node:path";
11380
11551
  import {
11381
11552
  mkdirSync as mkdirSync6,
11382
11553
  writeFileSync as writeFileSync10,
11383
- readFileSync as readFileSync18,
11384
- existsSync as existsSync13
11554
+ readFileSync as readFileSync19,
11555
+ existsSync as existsSync14
11385
11556
  } from "node:fs";
11386
- import { join as join22, resolve as resolve18 } from "node:path";
11557
+ import { join as join23, resolve as resolve18 } from "node:path";
11387
11558
  import { gunzipSync } from "node:zlib";
11388
11559
 
11389
11560
  // src/registry/npmrc.ts
11390
- import { existsSync as existsSync12, readFileSync as readFileSync17, writeFileSync as writeFileSync8 } from "node:fs";
11391
- import { join as join20 } from "node:path";
11561
+ import { existsSync as existsSync13, readFileSync as readFileSync18, writeFileSync as writeFileSync8 } from "node:fs";
11562
+ import { join as join21 } from "node:path";
11392
11563
  function npmCompatRegistryUrl(registryUrl) {
11393
11564
  return `${registryUrl.replace(/\/$/, "")}/npm/`;
11394
11565
  }
@@ -11404,8 +11575,8 @@ function ensureScopeNpmrc(projectDir, packageName, registryUrl) {
11404
11575
  const compat = npmCompatRegistryUrl(registryUrl);
11405
11576
  const key = `@${scope}:registry`;
11406
11577
  const line = `${key}=${compat}`;
11407
- const npmrcPath = join20(projectDir, ".npmrc");
11408
- let existing = existsSync12(npmrcPath) ? readFileSync17(npmrcPath, "utf8") : "";
11578
+ const npmrcPath = join21(projectDir, ".npmrc");
11579
+ let existing = existsSync13(npmrcPath) ? readFileSync18(npmrcPath, "utf8") : "";
11409
11580
  const lines = existing.split(/\r?\n/);
11410
11581
  const idx = lines.findIndex((l2) => l2.trimStart().startsWith(`${key}=`) || l2.trimStart().startsWith(`${key} =`));
11411
11582
  if (idx >= 0) {
@@ -11421,7 +11592,7 @@ function ensureScopeNpmrc(projectDir, packageName, registryUrl) {
11421
11592
 
11422
11593
  // src/registry/lib-extract.ts
11423
11594
  import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync9 } from "node:fs";
11424
- import { join as join21 } from "node:path";
11595
+ import { join as join22 } from "node:path";
11425
11596
  var WANTED = /* @__PURE__ */ new Set(["index.js", "index.d.ts"]);
11426
11597
  function extractLibArchive(s2lib, outDir, meta) {
11427
11598
  const entries = unzipSync(new Uint8Array(s2lib));
@@ -11430,7 +11601,7 @@ function extractLibArchive(s2lib, outDir, meta) {
11430
11601
  mkdirSync5(outDir, { recursive: true });
11431
11602
  for (const [name, bytes] of Object.entries(entries)) {
11432
11603
  if (!WANTED.has(name)) continue;
11433
- writeFileSync9(join21(outDir, name), Buffer.from(bytes));
11604
+ writeFileSync9(join22(outDir, name), Buffer.from(bytes));
11434
11605
  }
11435
11606
  let apiVersion = "1.x";
11436
11607
  try {
@@ -11439,7 +11610,7 @@ function extractLibArchive(s2lib, outDir, meta) {
11439
11610
  } catch {
11440
11611
  }
11441
11612
  writeFileSync9(
11442
- join21(outDir, "package.json"),
11613
+ join22(outDir, "package.json"),
11443
11614
  JSON.stringify(
11444
11615
  { name: meta.name, version: meta.version, main: "index.js", types: "index.d.ts", s2script: { kind: "library", apiVersion } },
11445
11616
  null,
@@ -11476,13 +11647,13 @@ function extractTypesTarball(tgz, outDir) {
11476
11647
  if (!name || name.endsWith("/")) continue;
11477
11648
  const base = name.replace(/^package\//, "");
11478
11649
  if (base === "package.json") {
11479
- writeFileSync10(join22(outDir, "package.json"), content);
11650
+ writeFileSync10(join23(outDir, "package.json"), content);
11480
11651
  } else if (base === "api.d.ts" || base === "index.d.ts") {
11481
- writeFileSync10(join22(outDir, "index.d.ts"), content);
11652
+ writeFileSync10(join23(outDir, "index.d.ts"), content);
11482
11653
  wroteContract = true;
11483
11654
  }
11484
11655
  }
11485
- if (!wroteContract && !existsSync13(join22(outDir, "index.d.ts"))) {
11656
+ if (!wroteContract && !existsSync14(join23(outDir, "index.d.ts"))) {
11486
11657
  throw new Error("types tarball missing package/api.d.ts (or index.d.ts)");
11487
11658
  }
11488
11659
  }
@@ -11507,10 +11678,10 @@ async function addPackage(opts) {
11507
11678
  }
11508
11679
  if (resolved.kind === "library") {
11509
11680
  const s2lib = await client.downloadLib(name, resolved.version);
11510
- const libDir = join22(absDir, ".s2script", "libs", ...name.split("/"));
11681
+ const libDir = join23(absDir, ".s2script", "libs", ...name.split("/"));
11511
11682
  extractLibArchive(s2lib, libDir, { name, version: resolved.version });
11512
- const pkgPath2 = join22(absDir, "package.json");
11513
- const pkg2 = JSON.parse(readFileSync18(pkgPath2, "utf8"));
11683
+ const pkgPath2 = join23(absDir, "package.json");
11684
+ const pkg2 = JSON.parse(readFileSync19(pkgPath2, "utf8"));
11514
11685
  pkg2.s2script = pkg2.s2script ?? {};
11515
11686
  pkg2.s2script.libraries = pkg2.s2script.libraries ?? {};
11516
11687
  pkg2.s2script.libraries[name] = rangeForResolved(range2, resolved.version);
@@ -11531,10 +11702,10 @@ async function addPackage(opts) {
11531
11702
  throw new Error(`${name}@${resolved.version} has no types artifact (runtime-only package?)`);
11532
11703
  }
11533
11704
  const tgz = await client.downloadTypes(name, resolved.version);
11534
- const typesDir = join22(absDir, ".s2script", "types", ...name.split("/"));
11705
+ const typesDir = join23(absDir, ".s2script", "types", ...name.split("/"));
11535
11706
  extractTypesTarball(tgz, typesDir);
11536
- const pkgPath = join22(absDir, "package.json");
11537
- const pkg = JSON.parse(readFileSync18(pkgPath, "utf8"));
11707
+ const pkgPath = join23(absDir, "package.json");
11708
+ const pkg = JSON.parse(readFileSync19(pkgPath, "utf8"));
11538
11709
  pkg.s2script = pkg.s2script ?? {};
11539
11710
  pkg.s2script.pluginDependencies = pkg.s2script.pluginDependencies ?? {};
11540
11711
  pkg.s2script.pluginDependencies[name] = rangeForResolved(range2, resolved.version);
@@ -11615,37 +11786,37 @@ async function run4(argv2) {
11615
11786
  }
11616
11787
 
11617
11788
  // src/commands/install.ts
11618
- import { existsSync as existsSync18, writeFileSync as writeFileSync15 } from "node:fs";
11619
- import { join as join26 } from "node:path";
11789
+ import { existsSync as existsSync19, writeFileSync as writeFileSync15 } from "node:fs";
11790
+ import { join as join27 } from "node:path";
11620
11791
 
11621
11792
  // src/server/manifest.ts
11622
11793
  var import_semver4 = __toESM(require_semver2(), 1);
11623
- import { existsSync as existsSync14, readFileSync as readFileSync19, renameSync, writeFileSync as writeFileSync11 } from "node:fs";
11624
- import { basename as basename2, join as join23, resolve as resolve20 } from "node:path";
11794
+ import { existsSync as existsSync15, readFileSync as readFileSync20, renameSync, writeFileSync as writeFileSync11 } from "node:fs";
11795
+ import { basename as basename2, join as join24, resolve as resolve20 } from "node:path";
11625
11796
  var SERVER_MANIFEST = "plugins.json";
11626
11797
  var SERVER_LOCK = "plugins-lock.json";
11627
11798
  var PLUGINS_SUBDIR = "plugins";
11628
11799
  var DISABLED_SUBDIR = "disabled";
11629
11800
  function readJson(path2) {
11630
- const j = JSON.parse(readFileSync19(path2, "utf8"));
11801
+ const j = JSON.parse(readFileSync20(path2, "utf8"));
11631
11802
  if (!j || typeof j !== "object" || Array.isArray(j)) throw new Error(`${path2}: must be a JSON object`);
11632
11803
  return j;
11633
11804
  }
11634
11805
  function findServerRoot(start) {
11635
11806
  for (const dir of [resolve20(start), resolve20(start, "addons", "s2script")]) {
11636
- if (existsSync14(join23(dir, SERVER_MANIFEST))) return dir;
11807
+ if (existsSync15(join24(dir, SERVER_MANIFEST))) return dir;
11637
11808
  }
11638
11809
  return null;
11639
11810
  }
11640
11811
  function findAddonRoot(start) {
11641
11812
  const found = findServerRoot(start);
11642
11813
  if (found) return found;
11643
- if (existsSync14(resolve20(start, PLUGINS_SUBDIR))) return resolve20(start);
11814
+ if (existsSync15(resolve20(start, PLUGINS_SUBDIR))) return resolve20(start);
11644
11815
  const nested = resolve20(start, "addons", "s2script");
11645
- return existsSync14(nested) ? nested : null;
11816
+ return existsSync15(nested) ? nested : null;
11646
11817
  }
11647
11818
  function parseServerManifest(root, raw) {
11648
- const label = join23(root, SERVER_MANIFEST);
11819
+ const label = join24(root, SERVER_MANIFEST);
11649
11820
  const depsRaw = raw.dependencies ?? {};
11650
11821
  if (typeof depsRaw !== "object" || Array.isArray(depsRaw)) {
11651
11822
  throw new Error(`${label}: "dependencies" must be an object (plugin name \u2192 version range)`);
@@ -11694,8 +11865,8 @@ function parseServerManifest(root, raw) {
11694
11865
  };
11695
11866
  }
11696
11867
  function readServerManifest(root) {
11697
- const path2 = join23(root, SERVER_MANIFEST);
11698
- if (!existsSync14(path2)) throw new Error(`no ${SERVER_MANIFEST} in ${root} \u2014 run \`s2s server init\` first`);
11868
+ const path2 = join24(root, SERVER_MANIFEST);
11869
+ if (!existsSync15(path2)) throw new Error(`no ${SERVER_MANIFEST} in ${root} \u2014 run \`s2s server init\` first`);
11699
11870
  return parseServerManifest(root, readJson(path2));
11700
11871
  }
11701
11872
  function newServerManifestJson(name, dependencies) {
@@ -11709,15 +11880,15 @@ function writeServerManifest(m) {
11709
11880
  else delete out.with;
11710
11881
  if (m.disabled.length > 0) out.disabled = [...m.disabled].sort();
11711
11882
  else delete out.disabled;
11712
- const path2 = join23(m.root, SERVER_MANIFEST);
11883
+ const path2 = join24(m.root, SERVER_MANIFEST);
11713
11884
  const tmp = `${path2}.${process.pid}.tmp`;
11714
11885
  writeFileSync11(tmp, JSON.stringify(out, null, 2) + "\n");
11715
11886
  renameSync(tmp, path2);
11716
11887
  }
11717
11888
 
11718
11889
  // src/server/commands.ts
11719
- import { existsSync as existsSync17, readdirSync as readdirSync6, readFileSync as readFileSync22, writeFileSync as writeFileSync14 } from "node:fs";
11720
- import { join as join25, resolve as resolve22 } from "node:path";
11890
+ import { existsSync as existsSync18, readdirSync as readdirSync7, readFileSync as readFileSync23, writeFileSync as writeFileSync14 } from "node:fs";
11891
+ import { join as join26, resolve as resolve22 } from "node:path";
11721
11892
  var import_semver5 = __toESM(require_semver2(), 1);
11722
11893
 
11723
11894
  // src/registry/install.ts
@@ -11770,7 +11941,7 @@ async function resolveMerged(client, specs, withMembers = {}) {
11770
11941
 
11771
11942
  // src/server/lock.ts
11772
11943
  import { createHash as createHash4 } from "node:crypto";
11773
- import { existsSync as existsSync15, readFileSync as readFileSync20, renameSync as renameSync2, writeFileSync as writeFileSync12 } from "node:fs";
11944
+ import { existsSync as existsSync16, readFileSync as readFileSync21, renameSync as renameSync2, writeFileSync as writeFileSync12 } from "node:fs";
11774
11945
  var LOCKFILE_VERSION = 3;
11775
11946
  function integrityFromSha256Hex(hex) {
11776
11947
  return `sha256-${Buffer.from(hex, "hex").toString("base64")}`;
@@ -11844,8 +12015,8 @@ function parseLock(text3, label = "plugins-lock.json") {
11844
12015
  };
11845
12016
  }
11846
12017
  function readLock(path2) {
11847
- if (!existsSync15(path2)) return null;
11848
- return parseLock(readFileSync20(path2, "utf8"), path2);
12018
+ if (!existsSync16(path2)) return null;
12019
+ return parseLock(readFileSync21(path2, "utf8"), path2);
11849
12020
  }
11850
12021
  function writeLock(path2, lock) {
11851
12022
  const tmp = `${path2}.${process.pid}.tmp`;
@@ -11854,8 +12025,8 @@ function writeLock(path2, lock) {
11854
12025
  }
11855
12026
 
11856
12027
  // src/server/sync.ts
11857
- import { existsSync as existsSync16, mkdirSync as mkdirSync7, readFileSync as readFileSync21, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync13 } from "node:fs";
11858
- import { dirname as dirname7, join as join24, resolve as resolve21, sep as sep5 } from "node:path";
12028
+ import { existsSync as existsSync17, mkdirSync as mkdirSync7, readFileSync as readFileSync22, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync13 } from "node:fs";
12029
+ import { dirname as dirname7, join as join25, resolve as resolve21, sep as sep5 } from "node:path";
11859
12030
  function relPath(t2) {
11860
12031
  return t2.disabled ? `${DISABLED_SUBDIR}/${t2.filename}` : t2.filename;
11861
12032
  }
@@ -11936,18 +12107,18 @@ async function syncPlugins(opts) {
11936
12107
  try {
11937
12108
  for (const t2 of opts.targets) {
11938
12109
  const key = lockKey(relPath(t2));
11939
- const dest = join24(dir, relPath(t2));
12110
+ const dest = join25(dir, relPath(t2));
11940
12111
  result.plugins[key] = {
11941
12112
  name: t2.name,
11942
12113
  version: t2.version,
11943
12114
  resolved: t2.url,
11944
12115
  integrity: integrityFromSha256Hex(t2.sha256)
11945
12116
  };
11946
- if (existsSync16(dest) && sha256Hex(readFileSync21(dest)) === t2.sha256) {
12117
+ if (existsSync17(dest) && sha256Hex(readFileSync22(dest)) === t2.sha256) {
11947
12118
  result.kept.push(t2.filename);
11948
12119
  continue;
11949
12120
  }
11950
- const movedFrom = Object.entries(opts.oldLock?.plugins ?? {}).filter(([k, p]) => k !== key && p.name === t2.name).map(([k]) => join24(opts.root, k)).find((f) => existsSync16(f) && sha256Hex(readFileSync21(f)) === t2.sha256);
12121
+ 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);
11951
12122
  if (movedFrom) {
11952
12123
  result.moved.push(t2.filename);
11953
12124
  if (opts.dryRun) log3(`would ${t2.disabled ? "disable" : "enable"} ${t2.name}@${t2.version}`);
@@ -11984,10 +12155,10 @@ async function syncPlugins(opts) {
11984
12155
  }
11985
12156
  for (const [key, old] of Object.entries(opts.oldLock?.plugins ?? {})) {
11986
12157
  if (key in result.plugins) continue;
11987
- const file = join24(opts.root, key);
11988
- if (!existsSync16(file)) continue;
12158
+ const file = join25(opts.root, key);
12159
+ if (!existsSync17(file)) continue;
11989
12160
  const want = sha256HexFromIntegrity(old.integrity);
11990
- if (want && sha256Hex(readFileSync21(file)) === want) {
12161
+ if (want && sha256Hex(readFileSync22(file)) === want) {
11991
12162
  if (opts.dryRun) log3(`would remove ${old.name}@${old.version} (${key})`);
11992
12163
  else {
11993
12164
  rmSync3(file);
@@ -12039,7 +12210,7 @@ async function serverInstall(opts) {
12039
12210
  }
12040
12211
  manifest = { ...manifest, with: { ...manifest.with, [parseSpec2(opts.specs[0]).name]: opts.with } };
12041
12212
  }
12042
- const lockPath = join25(opts.root, SERVER_LOCK);
12213
+ const lockPath = join26(opts.root, SERVER_LOCK);
12043
12214
  const lock = readLock(lockPath);
12044
12215
  if (opts.disabled) {
12045
12216
  if (!opts.specs?.length) throw new Error("--disabled needs the plugins to install: s2s install <name> --disabled");
@@ -12090,7 +12261,7 @@ async function serverUpdate(opts) {
12090
12261
  const log3 = opts.log ?? (() => {
12091
12262
  });
12092
12263
  const manifest = readServerManifest(opts.root);
12093
- const lockPath = join25(opts.root, SERVER_LOCK);
12264
+ const lockPath = join26(opts.root, SERVER_LOCK);
12094
12265
  const lock = readLock(lockPath);
12095
12266
  const names = opts.names && opts.names.length > 0 ? opts.names : Object.keys(manifest.dependencies);
12096
12267
  for (const n3 of names) {
@@ -12108,12 +12279,12 @@ async function serverUpdate(opts) {
12108
12279
  }
12109
12280
  function scanLocalPlugins(root, onUnreadable = () => {
12110
12281
  }) {
12111
- const dir = join25(root, PLUGINS_SUBDIR);
12112
- if (!existsSync17(dir)) return [];
12282
+ const dir = join26(root, PLUGINS_SUBDIR);
12283
+ if (!existsSync18(dir)) return [];
12113
12284
  const out = [];
12114
- for (const entry of readdirSync6(dir, { withFileTypes: true })) {
12285
+ for (const entry of readdirSync7(dir, { withFileTypes: true })) {
12115
12286
  if (!entry.isFile() || !entry.name.endsWith(".s2sp")) continue;
12116
- const bytes = readFileSync22(join25(dir, entry.name));
12287
+ const bytes = readFileSync23(join26(dir, entry.name));
12117
12288
  try {
12118
12289
  const files = unzipSync(new Uint8Array(bytes), { filter: (f) => f.name === "manifest.json" });
12119
12290
  const m = JSON.parse(Buffer.from(files["manifest.json"]).toString("utf8"));
@@ -12127,8 +12298,8 @@ function scanLocalPlugins(root, onUnreadable = () => {
12127
12298
  }
12128
12299
  async function serverInit(opts) {
12129
12300
  const root = resolve22(opts.root);
12130
- const pkgPath = join25(root, SERVER_MANIFEST);
12131
- if (existsSync17(pkgPath) && !opts.force) {
12301
+ const pkgPath = join26(root, SERVER_MANIFEST);
12302
+ if (existsSync18(pkgPath) && !opts.force) {
12132
12303
  throw new Error(`${pkgPath} already exists \u2014 pass --force to replace it`);
12133
12304
  }
12134
12305
  const report = { managed: [], unmanaged: [] };
@@ -12169,7 +12340,7 @@ async function serverInit(opts) {
12169
12340
  const raw = newServerManifestJson(opts.name ?? "s2script-server", dependencies);
12170
12341
  writeFileSync14(pkgPath, JSON.stringify(raw, null, 2) + "\n");
12171
12342
  const manifest = parseServerManifest(root, raw);
12172
- writeLock(join25(root, SERVER_LOCK), lockFor(manifest, plugins));
12343
+ writeLock(join26(root, SERVER_LOCK), lockFor(manifest, plugins));
12173
12344
  return report;
12174
12345
  }
12175
12346
  async function serverSetEnabled(opts) {
@@ -12187,7 +12358,7 @@ async function serverSetEnabled(opts) {
12187
12358
  else set.add(n3);
12188
12359
  }
12189
12360
  manifest = { ...manifest, disabled: [...set].sort() };
12190
- const lockPath = join25(opts.root, SERVER_LOCK);
12361
+ const lockPath = join26(opts.root, SERVER_LOCK);
12191
12362
  const lock = readLock(lockPath);
12192
12363
  const specs = pinnedSpecs(manifest, lock, sat);
12193
12364
  const { targets } = await resolveTargets(opts.client, specs, manifest.with, {
@@ -12259,11 +12430,11 @@ async function run5(argv2) {
12259
12430
  }
12260
12431
  await runServer(async () => {
12261
12432
  const withMembers = collectWith(argv2);
12262
- if (!existsSync18(join26(root, SERVER_MANIFEST))) {
12433
+ if (!existsSync19(join27(root, SERVER_MANIFEST))) {
12263
12434
  if (dryRun) throw new Error(`no ${SERVER_MANIFEST} in ${root} yet \u2014 run without --dry-run to create one`);
12264
12435
  const fresh = newServerManifestJson("s2script-server", {});
12265
- writeFileSync15(join26(root, SERVER_MANIFEST), JSON.stringify(fresh, null, 2) + "\n");
12266
- console.log(`created ${join26(root, SERVER_MANIFEST)}`);
12436
+ writeFileSync15(join27(root, SERVER_MANIFEST), JSON.stringify(fresh, null, 2) + "\n");
12437
+ console.log(`created ${join27(root, SERVER_MANIFEST)}`);
12267
12438
  }
12268
12439
  return serverInstall({
12269
12440
  root,
@@ -12284,26 +12455,26 @@ import { resolve as resolve25 } from "node:path";
12284
12455
  import {
12285
12456
  mkdirSync as mkdirSync8,
12286
12457
  writeFileSync as writeFileSync16,
12287
- existsSync as existsSync19,
12288
- readdirSync as readdirSync7,
12289
- readFileSync as readFileSync23,
12458
+ existsSync as existsSync20,
12459
+ readdirSync as readdirSync8,
12460
+ readFileSync as readFileSync24,
12290
12461
  symlinkSync
12291
12462
  } from "node:fs";
12292
- import { dirname as dirname8, isAbsolute, join as join27, relative as relative3, resolve as resolve23, basename as basename4 } from "node:path";
12463
+ import { dirname as dirname8, isAbsolute, join as join28, relative as relative4, resolve as resolve23, basename as basename4 } from "node:path";
12293
12464
  import { spawnSync } from "node:child_process";
12294
12465
  import { fileURLToPath as fileURLToPath2 } from "node:url";
12295
12466
  function readCliVersion() {
12296
12467
  const here = dirname8(fileURLToPath2(import.meta.url));
12297
12468
  const candidates = [
12298
- join27(here, "..", "package.json"),
12469
+ join28(here, "..", "package.json"),
12299
12470
  // dist/cli.js → packages/sdk/package.json
12300
- join27(here, "..", "..", "package.json")
12471
+ join28(here, "..", "..", "package.json")
12301
12472
  // src/create → packages/sdk/package.json
12302
12473
  ];
12303
12474
  for (const p of candidates) {
12304
- if (!existsSync19(p)) continue;
12475
+ if (!existsSync20(p)) continue;
12305
12476
  try {
12306
- const pkg = JSON.parse(readFileSync23(p, "utf8"));
12477
+ const pkg = JSON.parse(readFileSync24(p, "utf8"));
12307
12478
  if (pkg.name === "@s2script/sdk" && pkg.version) return pkg.version;
12308
12479
  } catch {
12309
12480
  }
@@ -12313,9 +12484,9 @@ function readCliVersion() {
12313
12484
  function findLocalPackagesDir() {
12314
12485
  const here = dirname8(fileURLToPath2(import.meta.url));
12315
12486
  const candidates = [
12316
- join27(here, "..", ".."),
12487
+ join28(here, "..", ".."),
12317
12488
  // dist/cli.js → packages
12318
- join27(here, "..", "..", "..")
12489
+ join28(here, "..", "..", "..")
12319
12490
  // src/create → packages
12320
12491
  ];
12321
12492
  for (const c2 of candidates) {
@@ -12331,8 +12502,8 @@ function defaultNameFromPath(dir, kind = "plugin") {
12331
12502
  return `@${kind}/${slugifyBasename(dir)}`;
12332
12503
  }
12333
12504
  function assertEmptyTarget(targetPath) {
12334
- if (!existsSync19(targetPath)) return;
12335
- const kids = readdirSync7(targetPath);
12505
+ if (!existsSync20(targetPath)) return;
12506
+ const kids = readdirSync8(targetPath);
12336
12507
  const meaningful = kids.filter((k) => k !== ".git");
12337
12508
  if (meaningful.length) {
12338
12509
  throw new Error(`target directory is not empty: ${targetPath}`);
@@ -12380,8 +12551,8 @@ function registryDevDeps(game, sdkVersion, resolve29 = resolvePublishedVersion)
12380
12551
  function fileDevDeps(packagesDir, game) {
12381
12552
  const deps = {};
12382
12553
  for (const n3 of createPackageNames(game)) {
12383
- const abs = join27(packagesDir, n3);
12384
- if (!existsSync19(join27(abs, "package.json"))) return void 0;
12554
+ const abs = join28(packagesDir, n3);
12555
+ if (!existsSync20(join28(abs, "package.json"))) return void 0;
12385
12556
  deps[`@s2script/${n3}`] = `file:${abs}`;
12386
12557
  }
12387
12558
  return deps;
@@ -12446,7 +12617,7 @@ function tsconfigJson() {
12446
12617
  ) + "\n";
12447
12618
  }
12448
12619
  function workspaceTsconfigJson(targetPath, workspaceRoot) {
12449
- const rel = relative3(targetPath, join27(workspaceRoot, "tsconfig.base.json")).replace(/\\/g, "/");
12620
+ const rel = relative4(targetPath, join28(workspaceRoot, "tsconfig.base.json")).replace(/\\/g, "/");
12450
12621
  return JSON.stringify(
12451
12622
  {
12452
12623
  extends: rel.startsWith(".") ? rel : `./${rel}`,
@@ -12522,10 +12693,10 @@ dist/
12522
12693
  `;
12523
12694
  }
12524
12695
  function linkLocalPackagesForNoInstall(targetPath, localPackagesDir) {
12525
- const nm = join27(targetPath, "node_modules");
12696
+ const nm = join28(targetPath, "node_modules");
12526
12697
  mkdirSync8(nm, { recursive: true });
12527
- const dest = join27(nm, "@s2script");
12528
- if (!existsSync19(dest)) symlinkSync(localPackagesDir, dest);
12698
+ const dest = join28(nm, "@s2script");
12699
+ if (!existsSync20(dest)) symlinkSync(localPackagesDir, dest);
12529
12700
  }
12530
12701
  function runInstall(dir, pm) {
12531
12702
  if (pm === "none") return;
@@ -12554,7 +12725,7 @@ async function createPlugin(opts = {}) {
12554
12725
  );
12555
12726
  }
12556
12727
  let slug = workspaceRoot !== void 0 && opts.path !== void 0 ? assertBareSlug(opts.path) : opts.path;
12557
- let targetPath = workspaceRoot === void 0 ? resolve23(opts.path ?? ".") : slug === void 0 ? void 0 : join27(workspaceRoot, "plugins", slug);
12728
+ let targetPath = workspaceRoot === void 0 ? resolve23(opts.path ?? ".") : slug === void 0 ? void 0 : join28(workspaceRoot, "plugins", slug);
12558
12729
  if (targetPath !== void 0) assertEmptyTarget(targetPath);
12559
12730
  if (interactive) {
12560
12731
  intro2("Create an s2script plugin");
@@ -12564,7 +12735,7 @@ async function createPlugin(opts = {}) {
12564
12735
  slug = assertBareSlug(
12565
12736
  await text2({ message: "Plugin name (directory under plugins/)", placeholder: "myplugin" })
12566
12737
  );
12567
- targetPath = join27(workspaceRoot, "plugins", slug);
12738
+ targetPath = join28(workspaceRoot, "plugins", slug);
12568
12739
  assertEmptyTarget(targetPath);
12569
12740
  }
12570
12741
  }
@@ -12627,37 +12798,37 @@ async function createPlugin(opts = {}) {
12627
12798
  install = workspaceRoot !== void 0 ? "none" : install ?? (opts.noInstall ? "none" : "npm");
12628
12799
  const finalTargetPath = targetPath;
12629
12800
  const localPackagesDir = workspaceRoot === void 0 ? findLocalPackagesDir() : void 0;
12630
- mkdirSync8(join27(finalTargetPath, "src"), { recursive: true });
12801
+ mkdirSync8(join28(finalTargetPath, "src"), { recursive: true });
12631
12802
  if (workspaceRoot !== void 0) {
12632
- writeFileSync16(join27(finalTargetPath, "package.json"), minimalPackageJsonContent(name));
12803
+ writeFileSync16(join28(finalTargetPath, "package.json"), minimalPackageJsonContent(name));
12633
12804
  writeFileSync16(
12634
- join27(finalTargetPath, "tsconfig.json"),
12805
+ join28(finalTargetPath, "tsconfig.json"),
12635
12806
  workspaceTsconfigJson(finalTargetPath, workspaceRoot)
12636
12807
  );
12637
12808
  } else if (kind === "library") {
12638
12809
  const version = readCliVersion();
12639
12810
  writeFileSync16(
12640
- join27(finalTargetPath, "package.json"),
12811
+ join28(finalTargetPath, "package.json"),
12641
12812
  libraryPackageJsonContent(name, game, version, localPackagesDir)
12642
12813
  );
12643
- writeFileSync16(join27(finalTargetPath, "tsconfig.json"), tsconfigJson());
12644
- writeFileSync16(join27(finalTargetPath, "eslint.config.mjs"), eslintConfig());
12814
+ writeFileSync16(join28(finalTargetPath, "tsconfig.json"), tsconfigJson());
12815
+ writeFileSync16(join28(finalTargetPath, "eslint.config.mjs"), eslintConfig());
12645
12816
  } else {
12646
12817
  const version = readCliVersion();
12647
12818
  writeFileSync16(
12648
- join27(finalTargetPath, "package.json"),
12819
+ join28(finalTargetPath, "package.json"),
12649
12820
  packageJsonContent(name, game, version, localPackagesDir)
12650
12821
  );
12651
- writeFileSync16(join27(finalTargetPath, "tsconfig.json"), tsconfigJson());
12652
- writeFileSync16(join27(finalTargetPath, "eslint.config.mjs"), eslintConfig());
12822
+ writeFileSync16(join28(finalTargetPath, "tsconfig.json"), tsconfigJson());
12823
+ writeFileSync16(join28(finalTargetPath, "eslint.config.mjs"), eslintConfig());
12653
12824
  }
12654
12825
  if (kind === "library") {
12655
- writeFileSync16(join27(finalTargetPath, "src", "index.ts"), LIBRARY_ENTRY);
12656
- writeFileSync16(join27(finalTargetPath, "src", "index.d.ts"), LIBRARY_TYPES);
12826
+ writeFileSync16(join28(finalTargetPath, "src", "index.ts"), LIBRARY_ENTRY);
12827
+ writeFileSync16(join28(finalTargetPath, "src", "index.d.ts"), LIBRARY_TYPES);
12657
12828
  } else {
12658
- writeFileSync16(join27(finalTargetPath, "src", "plugin.ts"), pluginSource(game));
12829
+ writeFileSync16(join28(finalTargetPath, "src", "plugin.ts"), pluginSource(game));
12659
12830
  }
12660
- writeFileSync16(join27(finalTargetPath, ".gitignore"), gitignore());
12831
+ writeFileSync16(join28(finalTargetPath, ".gitignore"), gitignore());
12661
12832
  let installed = false;
12662
12833
  if (workspaceRoot === void 0) {
12663
12834
  if (install !== "none") {
@@ -12685,7 +12856,7 @@ async function createPlugin(opts = {}) {
12685
12856
 
12686
12857
  // src/create/workspace.ts
12687
12858
  import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync17 } from "node:fs";
12688
- import { join as join28, resolve as resolve24 } from "node:path";
12859
+ import { join as join29, resolve as resolve24 } from "node:path";
12689
12860
  var ROOT_DEFAULT_GAME = "cs2";
12690
12861
  var CHANGESETS_CLI_RANGE = "^2.29.5";
12691
12862
  function defaultWorkspaceName(dir) {
@@ -12769,14 +12940,14 @@ async function createWorkspace(opts = {}) {
12769
12940
  name = name ?? defaultWorkspaceName(targetPath);
12770
12941
  const version = readCliVersion();
12771
12942
  const localPackagesDir = findLocalPackagesDir();
12772
- mkdirSync9(join28(targetPath, "plugins"), { recursive: true });
12773
- mkdirSync9(join28(targetPath, "packages"), { recursive: true });
12774
- mkdirSync9(join28(targetPath, ".changeset"), { recursive: true });
12775
- writeFileSync17(join28(targetPath, "package.json"), rootPackageJsonContent(name, version, localPackagesDir));
12776
- writeFileSync17(join28(targetPath, "tsconfig.base.json"), tsconfigBaseJson());
12777
- writeFileSync17(join28(targetPath, "eslint.config.mjs"), eslintConfig());
12778
- writeFileSync17(join28(targetPath, ".changeset", "config.json"), changesetConfigJson());
12779
- writeFileSync17(join28(targetPath, ".gitignore"), gitignore());
12943
+ mkdirSync9(join29(targetPath, "plugins"), { recursive: true });
12944
+ mkdirSync9(join29(targetPath, "packages"), { recursive: true });
12945
+ mkdirSync9(join29(targetPath, ".changeset"), { recursive: true });
12946
+ writeFileSync17(join29(targetPath, "package.json"), rootPackageJsonContent(name, version, localPackagesDir));
12947
+ writeFileSync17(join29(targetPath, "tsconfig.base.json"), tsconfigBaseJson());
12948
+ writeFileSync17(join29(targetPath, "eslint.config.mjs"), eslintConfig());
12949
+ writeFileSync17(join29(targetPath, ".changeset", "config.json"), changesetConfigJson());
12950
+ writeFileSync17(join29(targetPath, ".gitignore"), gitignore());
12780
12951
  return { dir: targetPath, name };
12781
12952
  }
12782
12953
 
@@ -13012,8 +13183,8 @@ async function run7(argv2) {
13012
13183
  }
13013
13184
 
13014
13185
  // src/config/gen.ts
13015
- import { readFileSync as readFileSync24, writeFileSync as writeFileSync18, mkdirSync as mkdirSync10 } from "node:fs";
13016
- import { join as join29 } from "node:path";
13186
+ import { readFileSync as readFileSync25, writeFileSync as writeFileSync18, mkdirSync as mkdirSync10 } from "node:fs";
13187
+ import { join as join30 } from "node:path";
13017
13188
 
13018
13189
  // src/config/config-path.ts
13019
13190
  function configFileName(id) {
@@ -13060,12 +13231,12 @@ function genConfigFromManifest(manifest, outDir) {
13060
13231
  const id = typeof manifest.id === "string" ? manifest.id : "";
13061
13232
  if (!id) throw new Error("config gen: manifest declares config but has no id");
13062
13233
  mkdirSync10(outDir, { recursive: true });
13063
- const path2 = join29(outDir, configFileName(id));
13234
+ const path2 = join30(outDir, configFileName(id));
13064
13235
  writeFileSync18(path2, generateDefaultJsonc(config));
13065
13236
  return path2;
13066
13237
  }
13067
13238
  function genConfigForS2sp(s2spPath, outDir) {
13068
- const entry = unzipSync(readFileSync24(s2spPath))["manifest.json"];
13239
+ const entry = unzipSync(readFileSync25(s2spPath))["manifest.json"];
13069
13240
  if (!entry) throw new Error(`config gen: ${s2spPath} has no manifest.json`);
13070
13241
  const manifest = JSON.parse(Buffer.from(entry).toString("utf8"));
13071
13242
  return genConfigFromManifest(manifest, outDir);
@@ -13112,12 +13283,12 @@ async function run8(argv2) {
13112
13283
  }
13113
13284
 
13114
13285
  // src/commands/codegen.ts
13115
- import { dirname as dirname9, join as join34 } from "node:path";
13286
+ import { dirname as dirname9, join as join35 } from "node:path";
13116
13287
  import { fileURLToPath as fileURLToPath3 } from "node:url";
13117
13288
 
13118
13289
  // src/schemagen/gen.ts
13119
- import { readFileSync as readFileSync25, writeFileSync as writeFileSync19 } from "node:fs";
13120
- import { join as join30 } from "node:path";
13290
+ import { readFileSync as readFileSync26, writeFileSync as writeFileSync19 } from "node:fs";
13291
+ import { join as join31 } from "node:path";
13121
13292
 
13122
13293
  // src/schemagen/model.ts
13123
13294
  function enumMemberNames(enumName, members) {
@@ -13422,7 +13593,7 @@ function emitDts(model) {
13422
13593
  return id ? `${id} | null` : TSTYPE[f.accessorKind];
13423
13594
  };
13424
13595
  const names = new Set(model.classes.map((c2) => c2.className));
13425
- const out = [HEADER2, 'import type { EntityRef } from "@s2script/sdk/entity";'];
13596
+ const out = [HEADER2, 'import type { EntityRef } from "@s2script/sdk/entity";', 'import type { Ptr } from "@s2script/sdk/unsafe";'];
13426
13597
  const vecClasses = /* @__PURE__ */ new Set();
13427
13598
  for (const c2 of model.classes) for (const f of c2.ownFields) {
13428
13599
  const vi = VEC_INFO[f.accessorKind];
@@ -13484,16 +13655,67 @@ function emitDts(model) {
13484
13655
  " * fields.glow.glowing = true;",
13485
13656
  " */",
13486
13657
  "export declare function wrapEntity<K extends keyof SchemaClasses>(className: K, ref: EntityRef): SchemaClasses[K];",
13658
+ "",
13659
+ "/**",
13660
+ " * A view factory for a raw address: the same field accessors as {@link wrapEntity}, over a `Ptr`",
13661
+ " * from `@s2script/sdk/unsafe` instead of a checked entity reference.",
13662
+ " *",
13663
+ " * UNSAFE. Nothing checks that the address still holds this class; a stale one reads garbage or",
13664
+ " * crashes. Use it for memory an engine call handed you, not for entities you can reference safely.",
13665
+ " *",
13666
+ " * @example",
13667
+ ' * import { Ptr } from "@s2script/sdk/unsafe";',
13668
+ ' * const fields = Ptr.from(pawn)?.as(structView("CCSPlayerPawn"));',
13669
+ " */",
13670
+ "export declare function structView<K extends keyof SchemaClasses>(className: K): (ptr: Ptr) => SchemaClasses[K];",
13487
13671
  ""
13488
13672
  );
13489
13673
  return out.join("\n");
13490
13674
  }
13491
13675
 
13676
+ // src/schemagen/offset-slots.ts
13677
+ var S = JSON.stringify;
13678
+ var OffsetSlots = class {
13679
+ constructor() {
13680
+ this.index = /* @__PURE__ */ new Map();
13681
+ }
13682
+ /** The JS expression yielding the offset of `cls::field` (or -1 while it does not resolve). */
13683
+ expr(cls, field) {
13684
+ const key = `${cls}\0${field}`;
13685
+ let i2 = this.index.get(key);
13686
+ if (i2 === void 0) {
13687
+ i2 = this.index.size;
13688
+ this.index.set(key, i2);
13689
+ }
13690
+ return `(O[${i2}] >= 0 ? O[${i2}] : (O[${i2}] = off(${S(cls)},${S(field)})))`;
13691
+ }
13692
+ /** The declaration of the slot table; emit it once, before any accessor can run. */
13693
+ declaration() {
13694
+ return ` var O = new Int32Array(${Math.max(this.index.size, 1)}).fill(-1);`;
13695
+ }
13696
+ };
13697
+
13698
+ // src/schemagen/shared-module.ts
13699
+ import { createHash as createHash5 } from "node:crypto";
13700
+ function sharedModule(header, name, body, bind) {
13701
+ const hash = createHash5("sha256").update(body.join("\n")).digest("hex").slice(0, 16);
13702
+ return [
13703
+ header,
13704
+ "(function () {",
13705
+ ' var shared = typeof __s2_shared_module === "function" ? __s2_shared_module : function (key, factory) { return factory(); };',
13706
+ ` var M = shared(${JSON.stringify(`${name}:${hash}`)}, function () {`,
13707
+ ...body.map((line) => line === "" ? line : ` ${line}`),
13708
+ " });",
13709
+ ...bind.map((line) => ` ${line}`),
13710
+ "})();"
13711
+ ].join("\n");
13712
+ }
13713
+
13492
13714
  // src/schemagen/emit-js.ts
13493
13715
  var HEADER3 = "// GENERATED by `s2script gen-schema` from schema-catalog.json \u2014 DO NOT EDIT.";
13494
- var S = JSON.stringify;
13495
- function resolveExpr(f) {
13496
- const base = `off(${S(f.declaringClass)},${S(f.rawName)})`;
13716
+ var S2 = JSON.stringify;
13717
+ function resolveExpr(slots, f) {
13718
+ const base = slots.expr(f.declaringClass, f.rawName);
13497
13719
  return f.addOffset ? `${base} + ${f.addOffset}` : base;
13498
13720
  }
13499
13721
  function accessorEntry(f, resolve29, guard2) {
@@ -13506,7 +13728,7 @@ function accessorEntry(f, resolve29, guard2) {
13506
13728
  entry = `get: function () { ${g}var v = this.ref.${READ[f.accessorKind]}(${resolve29}); return v === null ? null : v.toString(); }`;
13507
13729
  } else if (VEC_INFO[f.accessorKind]) {
13508
13730
  const vi = VEC_INFO[f.accessorKind];
13509
- entry = `get: function () { ${g}var a = this.ref.readFloats(${resolve29}, ${vi.count}); return a === null ? null : new ${vi.cls}(a[0], a[1], a[2]); }`;
13731
+ entry = `get: function () { ${g}return this.ref.read${vi.cls}(${resolve29}); }`;
13510
13732
  } else {
13511
13733
  entry = `get: function () { ${g}return this.ref.${READ[f.accessorKind]}(${resolve29}); }`;
13512
13734
  }
@@ -13517,48 +13739,42 @@ function accessorEntry(f, resolve29, guard2) {
13517
13739
  return entry;
13518
13740
  }
13519
13741
  function emitJs(model) {
13520
- const out = [HEADER3, "(function () {", " var off = __s2_schema_offset;", " var A = {};", " var E = {};"];
13521
- const vecClasses = /* @__PURE__ */ new Set();
13522
- for (const c2 of model.classes) for (const f of c2.ownFields) {
13523
- const vi = VEC_INFO[f.accessorKind];
13524
- if (vi) vecClasses.add(vi.cls);
13525
- }
13526
- for (const cls of [...vecClasses].sort()) {
13527
- out.push(` var ${cls} = __s2require("@s2script/sdk/math").${cls};`);
13528
- }
13742
+ const slots = new OffsetSlots();
13743
+ const out = [" var off = __s2_schema_offset;", " var A = {};", " var E = {};"];
13529
13744
  for (const c2 of model.classes) {
13530
- out.push(` A[${S(c2.className)}] = {`);
13745
+ out.push(` A[${S2(c2.className)}] = {`);
13531
13746
  for (const f of c2.ownFields) {
13532
- const entry = accessorEntry(f, resolveExpr(f), "");
13533
- out.push(` ${S(f.propName)}: { ${entry} },`);
13747
+ const entry = accessorEntry(f, resolveExpr(slots, f), "");
13748
+ out.push(` ${S2(f.propName)}: { ${entry} },`);
13534
13749
  }
13535
13750
  for (const f of c2.embeddedFields) {
13536
- out.push(` ${S(f.propName)}: { get: function () { return embed(${S(f.embeddedClass)}, this.ref, off(${S(f.declaringClass)},${S(f.rawName)})); } },`);
13751
+ out.push(` ${S2(f.propName)}: { get: function () { return embed(${S2(f.embeddedClass)}, this.ref, ${slots.expr(f.declaringClass, f.rawName)}); } },`);
13537
13752
  }
13538
13753
  out.push(" };");
13539
13754
  }
13540
13755
  for (const e of model.embedded) {
13541
- out.push(` E[${S(e.className)}] = {`);
13756
+ out.push(` E[${S2(e.className)}] = {`);
13542
13757
  for (const f of e.fields) {
13543
- const sub = `off(${S(f.declaringClass)},${S(f.rawName)})`;
13758
+ const sub = slots.expr(f.declaringClass, f.rawName);
13544
13759
  const resolve29 = f.addOffset ? `this.base + ${sub} + ${f.addOffset}` : `this.base + ${sub}`;
13545
13760
  const guard2 = `if (this.base < 0 || ${sub} < 0) return null;`;
13546
- out.push(` ${S(f.propName)}: { ${accessorEntry(f, resolve29, guard2)} },`);
13761
+ out.push(` ${S2(f.propName)}: { ${accessorEntry(f, resolve29, guard2)} },`);
13547
13762
  }
13548
13763
  for (const f of e.embeddedFields) {
13549
- const sub = `off(${S(f.declaringClass)},${S(f.rawName)})`;
13550
- out.push(` ${S(f.propName)}: { get: function () { var s = ${sub}; return embed(${S(f.embeddedClass)}, this.ref, (this.base < 0 || s < 0) ? -1 : this.base + s); } },`);
13764
+ const sub = slots.expr(f.declaringClass, f.rawName);
13765
+ out.push(` ${S2(f.propName)}: { get: function () { var s = ${sub}; return embed(${S2(f.embeddedClass)}, this.ref, (this.base < 0 || s < 0) ? -1 : this.base + s); } },`);
13551
13766
  }
13552
13767
  out.push(" };");
13553
13768
  }
13769
+ out.push(slots.declaration());
13554
13770
  out.push(" var P = {");
13555
- for (const c2 of model.classes) if (c2.parent) out.push(` ${S(c2.className)}: ${S(c2.parent)},`);
13771
+ for (const c2 of model.classes) if (c2.parent) out.push(` ${S2(c2.className)}: ${S2(c2.parent)},`);
13556
13772
  out.push(" };");
13557
13773
  if (model.enums.length > 0) {
13558
13774
  out.push(" var E_CONST = {};");
13559
13775
  for (const e of model.enums) {
13560
- const body = e.members.map((m) => `${S(m.name)}: ${m.value}`).join(", ");
13561
- out.push(` E_CONST[${S(e.tsName)}] = Object.freeze({ ${body} });`);
13776
+ const body = e.members.map((m) => `${S2(m.name)}: ${m.value}`).join(", ");
13777
+ out.push(` E_CONST[${S2(e.tsName)}] = Object.freeze({ ${body} });`);
13562
13778
  }
13563
13779
  }
13564
13780
  out.push(
@@ -13575,21 +13791,42 @@ function emitJs(model) {
13575
13791
  " Object.defineProperty(proto, name, { get: defs[name].get, set: defs[name].set, enumerable: true, configurable: true });",
13576
13792
  " }",
13577
13793
  " }",
13794
+ " return proto;",
13578
13795
  " }",
13579
- " function wrap(className, ref) { var o = { ref: ref }; applyAccessors(o, className); return o; }",
13796
+ " // One prototype per class, built on first use. A view is then one small object holding its",
13797
+ " // ref (and, embedded, its base offset): the accessors live on the shared prototype, where V8",
13798
+ " // keeps them on the fast path, instead of being redefined on every wrap and every embedded read.",
13799
+ " var PROTO = {}, EPROTO = {}, VIEW = {};",
13800
+ " function protoOf(className) {",
13801
+ " var p = PROTO[className];",
13802
+ " if (p === undefined) { p = Object.freeze(applyAccessors({}, className)); PROTO[className] = p; }",
13803
+ " return p;",
13804
+ " }",
13805
+ " function wrap(className, ref) { var o = Object.create(protoOf(className)); o.ref = ref; return o; }",
13580
13806
  " function embed(className, ref, base) {",
13581
- " var o = { ref: ref, base: base }, defs = E[className];",
13582
- " if (defs) for (var name in defs) {",
13583
- " Object.defineProperty(o, name, { get: defs[name].get, set: defs[name].set, enumerable: true, configurable: true });",
13807
+ " var p = EPROTO[className];",
13808
+ " if (p === undefined) {",
13809
+ " p = {}; var defs = E[className];",
13810
+ " if (defs) for (var name in defs) {",
13811
+ " Object.defineProperty(p, name, { get: defs[name].get, set: defs[name].set, enumerable: true, configurable: true });",
13812
+ " }",
13813
+ " EPROTO[className] = p = Object.freeze(p);",
13584
13814
  " }",
13585
- " return o;",
13815
+ " var o = Object.create(p); o.ref = ref; o.base = base; return o;",
13586
13816
  " }",
13587
- " globalThis.__s2pkg_cs2_schema = { applyAccessors: applyAccessors, wrap: wrap };",
13588
- " // Merge (not overwrite): this prelude runs FIRST, and pawn.js merges onto whatever it finds.",
13589
- " globalThis.__s2pkg_cs2 = Object.assign({}, globalThis.__s2pkg_cs2, { wrapEntity: wrap }" + (model.enums.length > 0 ? ", E_CONST);" : ");"),
13590
- "})();"
13817
+ " // A view factory for `Ptr.as`: the same accessors over a raw address, unchecked.",
13818
+ " function structView(className) {",
13819
+ " var v = VIEW[className];",
13820
+ " if (v === undefined) { v = Object.freeze(function (ptr) { return wrap(className, ptr); }); VIEW[className] = v; }",
13821
+ " return v;",
13822
+ " }",
13823
+ " return { applyAccessors: applyAccessors, wrap: wrap, structView: structView, enums: " + (model.enums.length > 0 ? "E_CONST" : "{}") + " };"
13591
13824
  );
13592
- return out.join("\n");
13825
+ return sharedModule(HEADER3, "@s2script/cs2/schema", out, [
13826
+ "globalThis.__s2pkg_cs2_schema = { applyAccessors: M.applyAccessors, wrap: M.wrap };",
13827
+ "// Merge (not overwrite): this prelude runs FIRST, and pawn.js merges onto whatever it finds.",
13828
+ "globalThis.__s2pkg_cs2 = Object.assign({}, globalThis.__s2pkg_cs2, { wrapEntity: M.wrap, structView: M.structView }, M.enums);"
13829
+ ]);
13593
13830
  }
13594
13831
 
13595
13832
  // src/schemagen/gen.ts
@@ -13600,16 +13837,16 @@ var EMBED_PATH = "games/cs2/codegen-embedded.json";
13600
13837
  var JS_OUT = "games/cs2/js/schema.generated.js";
13601
13838
  var DTS_OUT = "packages/cs2/schema.generated.d.ts";
13602
13839
  function runGenSchema(repoRoot2, opts) {
13603
- const catalog = JSON.parse(readFileSync25(join30(repoRoot2, CATALOG_PATH), "utf8"));
13604
- const list = JSON.parse(readFileSync25(join30(repoRoot2, LIST_PATH), "utf8"));
13840
+ const catalog = JSON.parse(readFileSync26(join31(repoRoot2, CATALOG_PATH), "utf8"));
13841
+ const list = JSON.parse(readFileSync26(join31(repoRoot2, LIST_PATH), "utf8"));
13605
13842
  let embed = [];
13606
13843
  try {
13607
- embed = JSON.parse(readFileSync25(join30(repoRoot2, EMBED_PATH), "utf8"));
13844
+ embed = JSON.parse(readFileSync26(join31(repoRoot2, EMBED_PATH), "utf8"));
13608
13845
  } catch {
13609
13846
  }
13610
13847
  let enumCatalog = {};
13611
13848
  try {
13612
- enumCatalog = JSON.parse(readFileSync25(join30(repoRoot2, ENUMS_PATH), "utf8"));
13849
+ enumCatalog = JSON.parse(readFileSync26(join31(repoRoot2, ENUMS_PATH), "utf8"));
13613
13850
  } catch {
13614
13851
  }
13615
13852
  const model = buildModel(catalog, list, embed, enumCatalog);
@@ -13620,11 +13857,11 @@ function runGenSchema(repoRoot2, opts) {
13620
13857
  const drift = [];
13621
13858
  const files = [[JS_OUT, js], [DTS_OUT, dts]];
13622
13859
  for (const [rel, content] of files) {
13623
- const abs = join30(repoRoot2, rel);
13860
+ const abs = join31(repoRoot2, rel);
13624
13861
  if (opts.check) {
13625
13862
  let cur = "";
13626
13863
  try {
13627
- cur = readFileSync25(abs, "utf8");
13864
+ cur = readFileSync26(abs, "utf8");
13628
13865
  } catch {
13629
13866
  }
13630
13867
  if (cur !== content) drift.push(rel);
@@ -13639,8 +13876,8 @@ function runGenSchema(repoRoot2, opts) {
13639
13876
  }
13640
13877
 
13641
13878
  // src/eventgen/gen.ts
13642
- import { readFileSync as readFileSync26, writeFileSync as writeFileSync20 } from "node:fs";
13643
- import { join as join31 } from "node:path";
13879
+ import { readFileSync as readFileSync27, writeFileSync as writeFileSync20 } from "node:fs";
13880
+ import { join as join32 } from "node:path";
13644
13881
 
13645
13882
  // src/eventgen/model.ts
13646
13883
  var TYPE_TO_GETTER = {
@@ -13725,15 +13962,15 @@ function emitEventDts(model) {
13725
13962
  var CATALOG_PATH2 = "games/cs2/gamedata/event-catalog.json";
13726
13963
  var DTS_OUT2 = "packages/cs2/events.generated.d.ts";
13727
13964
  function runGenEvents(repoRoot2, opts) {
13728
- const catalog = JSON.parse(readFileSync26(join31(repoRoot2, CATALOG_PATH2), "utf8"));
13965
+ const catalog = JSON.parse(readFileSync27(join32(repoRoot2, CATALOG_PATH2), "utf8"));
13729
13966
  const model = buildEventModel(catalog);
13730
13967
  const dts = emitEventDts(model);
13731
13968
  const drift = [];
13732
- const abs = join31(repoRoot2, DTS_OUT2);
13969
+ const abs = join32(repoRoot2, DTS_OUT2);
13733
13970
  if (opts.check) {
13734
13971
  let cur = "";
13735
13972
  try {
13736
- cur = readFileSync26(abs, "utf8");
13973
+ cur = readFileSync27(abs, "utf8");
13737
13974
  } catch {
13738
13975
  }
13739
13976
  if (cur !== dts) drift.push(DTS_OUT2);
@@ -13744,8 +13981,8 @@ function runGenEvents(repoRoot2, opts) {
13744
13981
  }
13745
13982
 
13746
13983
  // src/navgen/gen.ts
13747
- import { readFileSync as readFileSync27, writeFileSync as writeFileSync21 } from "node:fs";
13748
- import { join as join32 } from "node:path";
13984
+ import { readFileSync as readFileSync28, writeFileSync as writeFileSync21 } from "node:fs";
13985
+ import { join as join33 } from "node:path";
13749
13986
 
13750
13987
  // src/navgen/model.ts
13751
13988
  var SUPPORTED_NAV_KINDS = /* @__PURE__ */ new Set([
@@ -13810,7 +14047,7 @@ function buildNavModel(config, catalog) {
13810
14047
 
13811
14048
  // src/navgen/emit-js.ts
13812
14049
  var HEADER5 = "// GENERATED by `s2script gen-nav` from nav-targets.json \u2014 DO NOT EDIT.";
13813
- var S2 = JSON.stringify;
14050
+ var S3 = JSON.stringify;
13814
14051
  var READ_VIA = {
13815
14052
  f32: "readFloat32Via",
13816
14053
  bool: "readBoolVia",
@@ -13827,17 +14064,8 @@ var WRITE_VIA = {
13827
14064
  i32: "writeInt32Via"
13828
14065
  };
13829
14066
  function emitNavJs(model) {
13830
- const out = [HEADER5, "(function () {", " var off = __s2_schema_offset;"];
13831
- const vecClasses = /* @__PURE__ */ new Set();
13832
- for (const w of model.wrappers) {
13833
- for (const f of w.fields) {
13834
- const vi = VEC_INFO[f.accessorKind];
13835
- if (vi) vecClasses.add(vi.cls);
13836
- }
13837
- }
13838
- for (const cls of [...vecClasses].sort()) {
13839
- out.push(` var ${cls} = __s2require("@s2script/sdk/math").${cls};`);
13840
- }
14067
+ const slots = new OffsetSlots();
14068
+ const out = [" var off = __s2_schema_offset;"];
13841
14069
  for (const w of model.wrappers) {
13842
14070
  out.push(` function ${w.wrapper}(root, path, base) { this.root = root; this.path = path; this.base = base; }`);
13843
14071
  out.push(` ${w.wrapper}.prototype.notifyChanged = function () {`);
@@ -13845,7 +14073,7 @@ function emitNavJs(model) {
13845
14073
  out.push(` };`);
13846
14074
  out.push(` Object.defineProperties(${w.wrapper}.prototype, {`);
13847
14075
  for (const f of w.fields) {
13848
- const own = `off(${S2(f.declaringClass)},${S2(f.rawName)})`;
14076
+ const own = slots.expr(f.declaringClass, f.rawName);
13849
14077
  const withDelta = f.addOffset ? `${own} + ${f.addOffset}` : own;
13850
14078
  const resolve29 = `${withDelta} + this.base`;
13851
14079
  let getter;
@@ -13856,7 +14084,7 @@ function emitNavJs(model) {
13856
14084
  getter = `get: function () { var v = this.root.readInt64Via(this.path, ${resolve29}); return v === null ? null : v.toString(); }`;
13857
14085
  } else if (VEC_INFO[kind]) {
13858
14086
  const vi = VEC_INFO[kind];
13859
- getter = `get: function () { var a = this.root.readFloatsChain(this.path, ${resolve29}, ${vi.count}); return a === null ? null : new ${vi.cls}(a[0], a[1], a[2]); }`;
14087
+ getter = `get: function () { return this.root.read${vi.cls}Chain(this.path, ${resolve29}); }`;
13860
14088
  } else if (kind === "handle") {
13861
14089
  getter = `get: function () { return this.root.readHandleVia(this.path, ${resolve29}); }`;
13862
14090
  } else {
@@ -13866,9 +14094,10 @@ function emitNavJs(model) {
13866
14094
  }
13867
14095
  const wmethod = f.navWritable ? WRITE_VIA[kind] : void 0;
13868
14096
  const setter = wmethod ? `, set: function (v) { this.root.${wmethod}(this.path, ${resolve29}, v); }` : "";
13869
- out.push(` ${S2(f.propName)}: { ${getter}${setter} },`);
14097
+ out.push(` ${S3(f.propName)}: { ${getter}${setter} },`);
13870
14098
  }
13871
14099
  out.push(" });");
14100
+ out.push(` Object.freeze(${w.wrapper}.prototype); Object.freeze(${w.wrapper});`);
13872
14101
  }
13873
14102
  const bySource = /* @__PURE__ */ new Map();
13874
14103
  for (const w of model.wrappers) {
@@ -13877,20 +14106,20 @@ function emitNavJs(model) {
13877
14106
  }
13878
14107
  out.push(" function applyNav(proto, className) {");
13879
14108
  for (const [src, wrappers] of [...bySource.entries()].sort((a2, b) => a2[0] < b[0] ? -1 : a2[0] > b[0] ? 1 : 0)) {
13880
- out.push(` if (className === ${S2(src)}) {`);
14109
+ out.push(` if (className === ${S3(src)}) {`);
13881
14110
  for (const w of wrappers) {
13882
- out.push(` Object.defineProperty(proto, ${S2(w.prop)}, {`);
14111
+ out.push(` Object.defineProperty(proto, ${S3(w.prop)}, {`);
13883
14112
  out.push(` get: function () {`);
13884
14113
  out.push(` var _p = [];`);
13885
14114
  for (let i2 = 0; i2 < w.path.length; i2++) {
13886
14115
  const h2 = w.path[i2];
13887
- out.push(` var o${i2} = off(${S2(h2.cls)},${S2(h2.field)}); if (o${i2} < 0) return null; _p.push(o${i2});`);
14116
+ out.push(` var o${i2} = ${slots.expr(h2.cls, h2.field)}; if (o${i2} < 0) return null; _p.push(o${i2});`);
13888
14117
  }
13889
14118
  if (w.base.length > 0) {
13890
14119
  out.push(` var _b = 0;`);
13891
14120
  for (let i2 = 0; i2 < w.base.length; i2++) {
13892
14121
  const h2 = w.base[i2];
13893
- out.push(` var b${i2} = off(${S2(h2.cls)},${S2(h2.field)}); if (b${i2} < 0) return null; _b += b${i2};`);
14122
+ out.push(` var b${i2} = ${slots.expr(h2.cls, h2.field)}; if (b${i2} < 0) return null; _b += b${i2};`);
13894
14123
  }
13895
14124
  out.push(` return new ${w.wrapper}(this.ref, _p, _b);`);
13896
14125
  } else {
@@ -13902,10 +14131,11 @@ function emitNavJs(model) {
13902
14131
  out.push(" }");
13903
14132
  }
13904
14133
  out.push(" }");
13905
- out.push(" globalThis.__s2pkg_cs2_nav = { applyNav: applyNav };");
13906
- out.push("})();");
13907
- out.push("");
13908
- return out.join("\n");
14134
+ out.push(slots.declaration());
14135
+ out.push(" return { applyNav: applyNav };");
14136
+ return sharedModule(HEADER5, "@s2script/cs2/nav", out, [
14137
+ "globalThis.__s2pkg_cs2_nav = { applyNav: M.applyNav };"
14138
+ ]) + "\n";
13909
14139
  }
13910
14140
 
13911
14141
  // src/navgen/emit-dts.ts
@@ -13966,8 +14196,8 @@ var CATALOG_PATH3 = "games/cs2/gamedata/schema-catalog.json";
13966
14196
  var JS_OUT2 = "games/cs2/js/nav.generated.js";
13967
14197
  var DTS_OUT3 = "packages/cs2/nav.generated.d.ts";
13968
14198
  function runGenNav(repoRoot2, opts) {
13969
- const config = JSON.parse(readFileSync27(join32(repoRoot2, TARGETS_PATH), "utf8"));
13970
- const catalog = JSON.parse(readFileSync27(join32(repoRoot2, CATALOG_PATH3), "utf8"));
14199
+ const config = JSON.parse(readFileSync28(join33(repoRoot2, TARGETS_PATH), "utf8"));
14200
+ const catalog = JSON.parse(readFileSync28(join33(repoRoot2, CATALOG_PATH3), "utf8"));
13971
14201
  const model = buildNavModel(config, catalog);
13972
14202
  const js = emitNavJs(model);
13973
14203
  const dts = emitNavDts(model);
@@ -13975,11 +14205,11 @@ function runGenNav(repoRoot2, opts) {
13975
14205
  const drift = [];
13976
14206
  const files = [[JS_OUT2, js], [DTS_OUT3, dts]];
13977
14207
  for (const [rel, content] of files) {
13978
- const abs = join32(repoRoot2, rel);
14208
+ const abs = join33(repoRoot2, rel);
13979
14209
  if (opts.check) {
13980
14210
  let cur = "";
13981
14211
  try {
13982
- cur = readFileSync27(abs, "utf8");
14212
+ cur = readFileSync28(abs, "utf8");
13983
14213
  } catch {
13984
14214
  }
13985
14215
  if (cur !== content) drift.push(rel);
@@ -13991,22 +14221,22 @@ function runGenNav(repoRoot2, opts) {
13991
14221
  }
13992
14222
 
13993
14223
  // src/hookgen/gen.ts
13994
- import { readFileSync as readFileSync28, writeFileSync as writeFileSync22 } from "node:fs";
13995
- import { join as join33 } from "node:path";
14224
+ import { readFileSync as readFileSync29, writeFileSync as writeFileSync22 } from "node:fs";
14225
+ import { join as join34 } from "node:path";
13996
14226
  var GAMEDATA_PATH = "games/cs2/gamedata/game.cs2.jsonc";
13997
14227
  var DTS_OUT4 = "packages/cs2/hooks.generated.d.ts";
13998
14228
  function runGenHooks(repoRoot2, opts) {
13999
- const raw = readFileSync28(join33(repoRoot2, GAMEDATA_PATH), "utf8");
14229
+ const raw = readFileSync29(join34(repoRoot2, GAMEDATA_PATH), "utf8");
14000
14230
  const gd = JSON.parse(stripJsonComments(raw));
14001
14231
  const hooks = gd.hooks ?? {};
14002
14232
  const model = buildHookModel(hooks);
14003
14233
  const dts = emitHookDts(model);
14004
14234
  const drift = [];
14005
- const abs = join33(repoRoot2, DTS_OUT4);
14235
+ const abs = join34(repoRoot2, DTS_OUT4);
14006
14236
  if (opts.check) {
14007
14237
  let cur = "";
14008
14238
  try {
14009
- cur = readFileSync28(abs, "utf8");
14239
+ cur = readFileSync29(abs, "utf8");
14010
14240
  } catch {
14011
14241
  }
14012
14242
  if (cur !== dts) drift.push(DTS_OUT4);
@@ -14019,7 +14249,7 @@ function runGenHooks(repoRoot2, opts) {
14019
14249
 
14020
14250
  // src/commands/codegen.ts
14021
14251
  function repoRoot() {
14022
- return join34(dirname9(fileURLToPath3(import.meta.url)), "..", "..", "..");
14252
+ return join35(dirname9(fileURLToPath3(import.meta.url)), "..", "..", "..");
14023
14253
  }
14024
14254
  async function run9(kind, argv2) {
14025
14255
  const root = repoRoot();
@@ -14076,9 +14306,9 @@ async function run9(kind, argv2) {
14076
14306
  }
14077
14307
 
14078
14308
  // src/commands/inspect.ts
14079
- import { readFileSync as readFileSync29 } from "node:fs";
14309
+ import { readFileSync as readFileSync30 } from "node:fs";
14080
14310
  function inspectArchive(path2) {
14081
- const files = unzipSync(readFileSync29(path2));
14311
+ const files = unzipSync(readFileSync30(path2));
14082
14312
  if (!files["manifest.json"]) throw new Error(`${path2}: missing manifest.json`);
14083
14313
  const manifest = JSON.parse(Buffer.from(files["manifest.json"]).toString("utf8"));
14084
14314
  const lines = [`plugin: ${manifest.id ?? "(unknown)"}`];
@@ -14107,10 +14337,10 @@ async function run10(argv2) {
14107
14337
  }
14108
14338
 
14109
14339
  // src/engine-functions/migrate-v1.ts
14110
- import { closeSync, existsSync as existsSync20, linkSync, lstatSync as lstatSync2, mkdirSync as mkdirSync11, openSync, readFileSync as readFileSync30, readdirSync as readdirSync8, renameSync as renameSync4, unlinkSync, writeFileSync as writeFileSync23 } from "node:fs";
14111
- import { dirname as dirname10, isAbsolute as isAbsolute2, join as join35, relative as relative4, resolve as resolve26, sep as sep6 } from "node:path";
14112
- import { createHash as createHash5, randomUUID } from "node:crypto";
14113
- import ts5 from "typescript";
14340
+ 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";
14341
+ import { dirname as dirname10, isAbsolute as isAbsolute2, join as join36, relative as relative5, resolve as resolve26, sep as sep6 } from "node:path";
14342
+ import { createHash as createHash6, randomUUID } from "node:crypto";
14343
+ import ts6 from "typescript";
14114
14344
  var PLATFORM3 = "linuxsteamrt64";
14115
14345
  var SCOPE = "structurally lossless conversion of declared v1 contract; native ABI completeness is not verified";
14116
14346
  var IDENT = /^[A-Za-z_$][\w$]*$/;
@@ -14131,7 +14361,7 @@ function fields(value, allowed, where2, errors) {
14131
14361
  for (const key of Object.keys(value)) if (!allowed.includes(key)) errors.push(`${where2}: unsupported field ${key}`);
14132
14362
  }
14133
14363
  function contained(dir, path2) {
14134
- const r2 = relative4(dir, path2);
14364
+ const r2 = relative5(dir, path2);
14135
14365
  return r2 === "" || r2 !== ".." && !r2.startsWith(`..${sep6}`) && !isAbsolute2(r2);
14136
14366
  }
14137
14367
  function targetOf(ref, signatures, where2, errors) {
@@ -14235,11 +14465,11 @@ function hookOf(name, value, signatures, errors) {
14235
14465
  }
14236
14466
  function sourceFiles(dir, pkg, errors) {
14237
14467
  const result = /* @__PURE__ */ new Set();
14238
- const src = join35(dir, "src");
14468
+ const src = join36(dir, "src");
14239
14469
  function walk(at) {
14240
- if (!existsSync20(at)) return;
14241
- for (const item of readdirSync8(at, { withFileTypes: true })) {
14242
- const p = join35(at, item.name);
14470
+ if (!existsSync21(at)) return;
14471
+ for (const item of readdirSync9(at, { withFileTypes: true })) {
14472
+ const p = join36(at, item.name);
14243
14473
  if (item.isDirectory()) walk(p);
14244
14474
  else if (item.isFile() && /\.[cm]?[jt]sx?$/.test(item.name)) result.add(p);
14245
14475
  }
@@ -14247,23 +14477,23 @@ function sourceFiles(dir, pkg, errors) {
14247
14477
  walk(src);
14248
14478
  if (valid2(pkg.main)) {
14249
14479
  const p = resolve26(dir, pkg.main);
14250
- if (contained(dir, p) && existsSync20(p) && lstatSync2(p).isFile()) result.add(p);
14480
+ if (contained(dir, p) && existsSync21(p) && lstatSync2(p).isFile()) result.add(p);
14251
14481
  }
14252
14482
  const queue = [...result];
14253
14483
  for (let i2 = 0; i2 < queue.length; i2++) {
14254
14484
  const file = queue[i2];
14255
- const ast = ts5.createSourceFile(file, readFileSync30(file, "utf8"), ts5.ScriptTarget.Latest, true);
14485
+ const ast = ts6.createSourceFile(file, readFileSync31(file, "utf8"), ts6.ScriptTarget.Latest, true);
14256
14486
  const follow = (spec, node) => {
14257
14487
  if (!spec.startsWith(".")) return;
14258
- const at = `${relative4(dir, file)}:${ast.getLineAndCharacterOfPosition(node.getStart(ast)).line + 1}`;
14488
+ const at = `${relative5(dir, file)}:${ast.getLineAndCharacterOfPosition(node.getStart(ast)).line + 1}`;
14259
14489
  const base = resolve26(dirname10(file), spec);
14260
14490
  if (!contained(dir, base)) {
14261
14491
  errors.push(`${at}: local module ${JSON.stringify(spec)} escapes plugin directory`);
14262
14492
  return;
14263
14493
  }
14264
14494
  const stem = base.replace(/\.[cm]?[jt]sx?$/, "");
14265
- 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) => join35(base, x))];
14266
- const found = candidates.find((p) => contained(dir, p) && existsSync20(p) && lstatSync2(p).isFile());
14495
+ 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))];
14496
+ const found = candidates.find((p) => contained(dir, p) && existsSync21(p) && lstatSync2(p).isFile());
14267
14497
  if (!found) {
14268
14498
  errors.push(`${at}: unresolved local module ${JSON.stringify(spec)}`);
14269
14499
  return;
@@ -14273,18 +14503,18 @@ function sourceFiles(dir, pkg, errors) {
14273
14503
  queue.push(found);
14274
14504
  };
14275
14505
  for (const statement of ast.statements) {
14276
- if (!ts5.isImportDeclaration(statement) && !ts5.isExportDeclaration(statement)) continue;
14506
+ if (!ts6.isImportDeclaration(statement) && !ts6.isExportDeclaration(statement)) continue;
14277
14507
  const spec = statement.moduleSpecifier;
14278
- if (spec && ts5.isStringLiteral(spec)) follow(spec.text, spec);
14508
+ if (spec && ts6.isStringLiteral(spec)) follow(spec.text, spec);
14279
14509
  }
14280
14510
  const visit = (node) => {
14281
- if (ts5.isCallExpression(node) && (ts5.isIdentifier(node.expression) && node.expression.text === "require" || node.expression.kind === ts5.SyntaxKind.ImportKeyword)) {
14511
+ if (ts6.isCallExpression(node) && (ts6.isIdentifier(node.expression) && node.expression.text === "require" || node.expression.kind === ts6.SyntaxKind.ImportKeyword)) {
14282
14512
  const arg = node.arguments[0];
14283
- if (!arg || !ts5.isStringLiteral(arg) && !ts5.isNoSubstitutionTemplateLiteral(arg)) {
14284
- errors.push(`${relative4(dir, file)}:${ast.getLineAndCharacterOfPosition(node.getStart(ast)).line + 1}: dynamic module edge cannot be resolved for source analysis`);
14513
+ if (!arg || !ts6.isStringLiteral(arg) && !ts6.isNoSubstitutionTemplateLiteral(arg)) {
14514
+ errors.push(`${relative5(dir, file)}:${ast.getLineAndCharacterOfPosition(node.getStart(ast)).line + 1}: dynamic module edge cannot be resolved for source analysis`);
14285
14515
  } else follow(arg.text, node);
14286
14516
  }
14287
- ts5.forEachChild(node, visit);
14517
+ ts6.forEachChild(node, visit);
14288
14518
  };
14289
14519
  visit(ast);
14290
14520
  }
@@ -14293,152 +14523,152 @@ function sourceFiles(dir, pkg, errors) {
14293
14523
  function scanSource(files, calls, hooks, report) {
14294
14524
  const namespaces = new Set(hooks.values());
14295
14525
  for (const file of files) {
14296
- const source = ts5.createSourceFile(file, readFileSync30(file, "utf8"), ts5.ScriptTarget.Latest, true);
14297
- if (source.parseDiagnostics?.length) report.ambiguities.push(`${relative4(report.package, file)}: source syntax errors prevent reliable reference analysis`);
14526
+ const source = ts6.createSourceFile(file, readFileSync31(file, "utf8"), ts6.ScriptTarget.Latest, true);
14527
+ if (source.parseDiagnostics?.length) report.ambiguities.push(`${relative5(report.package, file)}: source syntax errors prevent reliable reference analysis`);
14298
14528
  const ambiguity = (node, message) => {
14299
- report.ambiguities.push(`${relative4(report.package, file)}:${source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1}: ${message}`);
14529
+ report.ambiguities.push(`${relative5(report.package, file)}:${source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1}: ${message}`);
14300
14530
  };
14301
14531
  const visit = (node) => {
14302
- if ((ts5.isImportDeclaration(node) || ts5.isExportDeclaration(node)) && node.moduleSpecifier && ts5.isStringLiteral(node.moduleSpecifier)) {
14532
+ if ((ts6.isImportDeclaration(node) || ts6.isExportDeclaration(node)) && node.moduleSpecifier && ts6.isStringLiteral(node.moduleSpecifier)) {
14303
14533
  const spec = node.moduleSpecifier.text;
14304
14534
  if (spec === "@s2script/sdk/unsafe") {
14305
- if (ts5.isExportDeclaration(node)) ambiguity(node, "unsafe Engine reexport prevents proving legacy timing/null behavior");
14535
+ if (ts6.isExportDeclaration(node)) ambiguity(node, "unsafe Engine reexport prevents proving legacy timing/null behavior");
14306
14536
  else {
14307
- const bindings = node.importClause?.namedBindings;
14308
- const defaultBinding = bindings && ts5.isNamedImports(bindings) && bindings.elements.some((e) => (e.propertyName?.text ?? e.name.text) === "default");
14309
- if (node.importClause?.name || bindings && ts5.isNamespaceImport(bindings) || defaultBinding) {
14537
+ const bindings2 = node.importClause?.namedBindings;
14538
+ const defaultBinding = bindings2 && ts6.isNamedImports(bindings2) && bindings2.elements.some((e) => (e.propertyName?.text ?? e.name.text) === "default");
14539
+ if (node.importClause?.name || bindings2 && ts6.isNamespaceImport(bindings2) || defaultBinding) {
14310
14540
  ambiguity(node, "indirect Engine namespace import from unsafe SDK prevents proving legacy timing/null behavior");
14311
14541
  }
14312
14542
  }
14313
14543
  }
14314
- if (["node:module", "module"].includes(spec) && (ts5.isExportDeclaration(node) || node.importClause)) {
14315
- ambiguity(node, ts5.isExportDeclaration(node) ? "module loader reexport prevents complete local source analysis" : "indirect module loader import prevents complete local source analysis");
14544
+ if (["node:module", "module"].includes(spec) && (ts6.isExportDeclaration(node) || node.importClause)) {
14545
+ ambiguity(node, ts6.isExportDeclaration(node) ? "module loader reexport prevents complete local source analysis" : "indirect module loader import prevents complete local source analysis");
14316
14546
  }
14317
14547
  }
14318
- if (ts5.isImportEqualsDeclaration(node) && !node.isTypeOnly && ts5.isExternalModuleReference(node.moduleReference)) {
14548
+ if (ts6.isImportEqualsDeclaration(node) && !node.isTypeOnly && ts6.isExternalModuleReference(node.moduleReference)) {
14319
14549
  const ref = node.moduleReference.expression;
14320
- const spec = ref && ts5.isStringLiteral(ref) ? ref.text : void 0;
14550
+ const spec = ref && ts6.isStringLiteral(ref) ? ref.text : void 0;
14321
14551
  if (spec === "@s2script/sdk/unsafe") ambiguity(node, "unsafe Engine import-equals export or alias prevents proving legacy timing/null behavior");
14322
14552
  else if (spec === "node:module" || spec === "module") ambiguity(node, "module loader import-equals export or alias prevents complete local source analysis");
14323
14553
  else ambiguity(node, "import-equals module edge cannot be followed for complete source analysis");
14324
14554
  }
14325
- if (ts5.isImportSpecifier(node) && (node.propertyName?.text ?? node.name.text) === "createRequire") {
14555
+ if (ts6.isImportSpecifier(node) && (node.propertyName?.text ?? node.name.text) === "createRequire") {
14326
14556
  ambiguity(node, "indirect module loader factory prevents complete local source analysis");
14327
14557
  }
14328
- if (ts5.isCallExpression(node) && (ts5.isIdentifier(node.expression) && node.expression.text === "require" || node.expression.kind === ts5.SyntaxKind.ImportKeyword)) {
14558
+ if (ts6.isCallExpression(node) && (ts6.isIdentifier(node.expression) && node.expression.text === "require" || node.expression.kind === ts6.SyntaxKind.ImportKeyword)) {
14329
14559
  const arg = node.arguments[0];
14330
- if (arg && (ts5.isStringLiteral(arg) || ts5.isNoSubstitutionTemplateLiteral(arg)) && arg.text === "@s2script/sdk/unsafe") {
14560
+ if (arg && (ts6.isStringLiteral(arg) || ts6.isNoSubstitutionTemplateLiteral(arg)) && arg.text === "@s2script/sdk/unsafe") {
14331
14561
  ambiguity(node, "indirect Engine namespace load from unsafe SDK prevents proving legacy timing/null behavior");
14332
14562
  }
14333
- if (arg && (ts5.isStringLiteral(arg) || ts5.isNoSubstitutionTemplateLiteral(arg)) && ["node:module", "module"].includes(arg.text)) {
14563
+ if (arg && (ts6.isStringLiteral(arg) || ts6.isNoSubstitutionTemplateLiteral(arg)) && ["node:module", "module"].includes(arg.text)) {
14334
14564
  ambiguity(node, "indirect module loader import prevents complete local source analysis");
14335
14565
  }
14336
14566
  }
14337
- if (ts5.isIdentifier(node) && node.text === "require") {
14567
+ if (ts6.isIdentifier(node) && node.text === "require") {
14338
14568
  const parent = node.parent;
14339
- const directCall = ts5.isCallExpression(parent) && parent.expression === node;
14340
- const declaredName = (ts5.isVariableDeclaration(parent) || ts5.isParameter(parent)) && parent.name === node;
14341
- const propertyName = (ts5.isPropertyAccessExpression(parent) || ts5.isPropertyAssignment(parent)) && parent.name === node;
14342
- if (declaredName || ts5.isImportSpecifier(parent)) ambiguity(node, "shadowed module loader binding prevents complete local source analysis");
14569
+ const directCall = ts6.isCallExpression(parent) && parent.expression === node;
14570
+ const declaredName = (ts6.isVariableDeclaration(parent) || ts6.isParameter(parent)) && parent.name === node;
14571
+ const propertyName = (ts6.isPropertyAccessExpression(parent) || ts6.isPropertyAssignment(parent)) && parent.name === node;
14572
+ if (declaredName || ts6.isImportSpecifier(parent)) ambiguity(node, "shadowed module loader binding prevents complete local source analysis");
14343
14573
  else if (!directCall && !propertyName) ambiguity(node, "indirect module loader alias or escape prevents complete local source analysis");
14344
14574
  }
14345
- if (ts5.isIdentifier(node) && node.text === "createRequire") {
14575
+ if (ts6.isIdentifier(node) && node.text === "createRequire") {
14346
14576
  const parent = node.parent;
14347
- if (!ts5.isImportSpecifier(parent) && !((ts5.isVariableDeclaration(parent) || ts5.isParameter(parent)) && parent.name === node)) {
14577
+ if (!ts6.isImportSpecifier(parent) && !((ts6.isVariableDeclaration(parent) || ts6.isParameter(parent)) && parent.name === node)) {
14348
14578
  ambiguity(node, "indirect module loader factory prevents complete local source analysis");
14349
14579
  }
14350
14580
  }
14351
- if (ts5.isIdentifier(node) && (node.text === "globalThis" || node.text === "global" || node.text === "module")) {
14581
+ if (ts6.isIdentifier(node) && (node.text === "globalThis" || node.text === "global" || node.text === "module")) {
14352
14582
  const parent = node.parent;
14353
- const declaredName = (ts5.isVariableDeclaration(parent) || ts5.isParameter(parent)) && parent.name === node;
14583
+ const declaredName = (ts6.isVariableDeclaration(parent) || ts6.isParameter(parent)) && parent.name === node;
14354
14584
  if (!declaredName) {
14355
- if (ts5.isPropertyAccessExpression(parent) && parent.expression === node) {
14585
+ if (ts6.isPropertyAccessExpression(parent) && parent.expression === node) {
14356
14586
  if (node.text !== "module" && parent.name.text === "Engine") ambiguity(parent, "indirect global Engine access prevents proving legacy timing/null behavior");
14357
14587
  else if (parent.name.text === "require" || parent.name.text === "createRequire") ambiguity(parent, "indirect module loader access prevents complete local source analysis");
14358
14588
  else if (node.text === "module" && parent.name.text !== "exports") ambiguity(parent, "indirect module loader or module escape prevents complete local source analysis");
14359
- } else if (ts5.isElementAccessExpression(parent) && parent.expression === node) {
14589
+ } else if (ts6.isElementAccessExpression(parent) && parent.expression === node) {
14360
14590
  const key = parent.argumentExpression;
14361
- const literal = key && (ts5.isStringLiteral(key) || ts5.isNoSubstitutionTemplateLiteral(key)) ? key.text : void 0;
14362
- if (node.text !== "module" && literal === "Engine") ambiguity(parent, "computed global Engine access prevents proving legacy timing/null behavior");
14363
- else if (literal === "require" || literal === "createRequire" || node.text === "module") ambiguity(parent, "indirect module loader access prevents complete local source analysis");
14364
- else if (literal === void 0) ambiguity(parent, "computed global access may hide Engine or a module loader");
14591
+ const literal2 = key && (ts6.isStringLiteral(key) || ts6.isNoSubstitutionTemplateLiteral(key)) ? key.text : void 0;
14592
+ if (node.text !== "module" && literal2 === "Engine") ambiguity(parent, "computed global Engine access prevents proving legacy timing/null behavior");
14593
+ else if (literal2 === "require" || literal2 === "createRequire" || node.text === "module") ambiguity(parent, "indirect module loader access prevents complete local source analysis");
14594
+ else if (literal2 === void 0) ambiguity(parent, "computed global access may hide Engine or a module loader");
14365
14595
  } else ambiguity(node, `indirect ${node.text} escape may hide Engine or a module loader`);
14366
14596
  }
14367
14597
  }
14368
- if (ts5.isImportSpecifier(node) && node.propertyName?.text === "Engine" && node.name.text !== "Engine") {
14369
- report.ambiguities.push(`${relative4(report.package, file)}:${source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1}: imported Engine alias prevents proving legacy timing/null behavior`);
14598
+ if (ts6.isImportSpecifier(node) && node.propertyName?.text === "Engine" && node.name.text !== "Engine") {
14599
+ report.ambiguities.push(`${relative5(report.package, file)}:${source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1}: imported Engine alias prevents proving legacy timing/null behavior`);
14370
14600
  }
14371
- if (ts5.isExportSpecifier(node) && (node.propertyName?.text ?? node.name.text) === "Engine") {
14372
- report.ambiguities.push(`${relative4(report.package, file)}:${source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1}: exported Engine escape prevents proving legacy timing/null behavior`);
14601
+ if (ts6.isExportSpecifier(node) && (node.propertyName?.text ?? node.name.text) === "Engine") {
14602
+ report.ambiguities.push(`${relative5(report.package, file)}:${source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1}: exported Engine escape prevents proving legacy timing/null behavior`);
14373
14603
  }
14374
- if (ts5.isBindingElement(node) && node.propertyName && ts5.isIdentifier(node.propertyName) && node.propertyName.text === "Engine") {
14375
- report.ambiguities.push(`${relative4(report.package, file)}:${source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1}: destructured Engine alias prevents proving legacy timing/null behavior`);
14604
+ if (ts6.isBindingElement(node) && node.propertyName && ts6.isIdentifier(node.propertyName) && node.propertyName.text === "Engine") {
14605
+ report.ambiguities.push(`${relative5(report.package, file)}:${source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1}: destructured Engine alias prevents proving legacy timing/null behavior`);
14376
14606
  }
14377
- if (ts5.isParameter(node) && hooks.size && ts5.isObjectBindingPattern(node.name) && node.name.elements.some((e) => namespaces.has(e.propertyName && ts5.isIdentifier(e.propertyName) ? e.propertyName.text : e.name.getText(source)))) {
14378
- report.ambiguities.push(`${relative4(report.package, file)}:${source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1}: destructured ctx hook namespace alias prevents proving callback timing`);
14607
+ if (ts6.isParameter(node) && hooks.size && ts6.isObjectBindingPattern(node.name) && node.name.elements.some((e) => namespaces.has(e.propertyName && ts6.isIdentifier(e.propertyName) ? e.propertyName.text : e.name.getText(source)))) {
14608
+ report.ambiguities.push(`${relative5(report.package, file)}:${source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1}: destructured ctx hook namespace alias prevents proving callback timing`);
14379
14609
  }
14380
- if (ts5.isIdentifier(node) && (node.text === "Engine" || node.text === "ctx")) {
14610
+ if (ts6.isIdentifier(node) && (node.text === "Engine" || node.text === "ctx")) {
14381
14611
  const parent = node.parent;
14382
- const declaration = (ts5.isVariableDeclaration(parent) || ts5.isParameter(parent) || ts5.isBindingElement(parent)) && parent.name === node;
14383
- const importName = ts5.isImportSpecifier(parent) || ts5.isImportClause(parent) || ts5.isNamespaceImport(parent) || ts5.isExportSpecifier(parent);
14384
- const propertyName = (ts5.isPropertyAccessExpression(parent) || ts5.isPropertyAssignment(parent)) && parent.name === node;
14385
- if (!declaration && !importName && !propertyName && !(ts5.isElementAccessExpression(parent) && parent.expression === node)) {
14612
+ const declaration = (ts6.isVariableDeclaration(parent) || ts6.isParameter(parent) || ts6.isBindingElement(parent)) && parent.name === node;
14613
+ const importName = ts6.isImportSpecifier(parent) || ts6.isImportClause(parent) || ts6.isNamespaceImport(parent) || ts6.isExportSpecifier(parent);
14614
+ const propertyName = (ts6.isPropertyAccessExpression(parent) || ts6.isPropertyAssignment(parent)) && parent.name === node;
14615
+ if (!declaration && !importName && !propertyName && !(ts6.isElementAccessExpression(parent) && parent.expression === node)) {
14386
14616
  let indirect = false;
14387
- if (ts5.isPropertyAccessExpression(parent) && parent.expression === node) {
14388
- if (node.text === "Engine") indirect = !(["call", "hook"].includes(parent.name.text) && ts5.isCallExpression(parent.parent) && parent.parent.expression === parent);
14617
+ if (ts6.isPropertyAccessExpression(parent) && parent.expression === node) {
14618
+ if (node.text === "Engine") indirect = !(["call", "hook"].includes(parent.name.text) && ts6.isCallExpression(parent.parent) && parent.parent.expression === parent);
14389
14619
  else if (namespaces.has(parent.name.text)) {
14390
14620
  const member = parent.parent;
14391
- indirect = !(ts5.isPropertyAccessExpression(member) && member.expression === parent && hooks.get(member.name.text) === parent.name.text && ts5.isCallExpression(member.parent) && member.parent.expression === member);
14621
+ indirect = !(ts6.isPropertyAccessExpression(member) && member.expression === parent && hooks.get(member.name.text) === parent.name.text && ts6.isCallExpression(member.parent) && member.parent.expression === member);
14392
14622
  }
14393
14623
  } else indirect = node.text === "Engine" || hooks.size > 0;
14394
- if (indirect) report.ambiguities.push(`${relative4(report.package, file)}:${source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1}: indirect ${node.text} alias or escape prevents proving legacy timing/null behavior`);
14624
+ if (indirect) report.ambiguities.push(`${relative5(report.package, file)}:${source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1}: indirect ${node.text} alias or escape prevents proving legacy timing/null behavior`);
14395
14625
  }
14396
14626
  }
14397
- if (ts5.isElementAccessExpression(node) && ts5.isIdentifier(node.expression) && node.expression.text === "Engine") {
14627
+ if (ts6.isElementAccessExpression(node) && ts6.isIdentifier(node.expression) && node.expression.text === "Engine") {
14398
14628
  const line = source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1;
14399
- report.ambiguities.push(`${relative4(report.package, file)}:${line}: computed Engine lookup prevents proving timing/null behavior`);
14629
+ report.ambiguities.push(`${relative5(report.package, file)}:${line}: computed Engine lookup prevents proving timing/null behavior`);
14400
14630
  }
14401
- if (ts5.isElementAccessExpression(node) && hooks.size && (ts5.isIdentifier(node.expression) && node.expression.text === "ctx" || ts5.isPropertyAccessExpression(node.expression) && ts5.isIdentifier(node.expression.expression) && node.expression.expression.text === "ctx")) {
14631
+ if (ts6.isElementAccessExpression(node) && hooks.size && (ts6.isIdentifier(node.expression) && node.expression.text === "ctx" || ts6.isPropertyAccessExpression(node.expression) && ts6.isIdentifier(node.expression.expression) && node.expression.expression.text === "ctx")) {
14402
14632
  const line = source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1;
14403
- report.ambiguities.push(`${relative4(report.package, file)}:${line}: computed ctx hook lookup prevents proving callback timing`);
14633
+ report.ambiguities.push(`${relative5(report.package, file)}:${line}: computed ctx hook lookup prevents proving callback timing`);
14404
14634
  }
14405
- if (ts5.isPropertyAccessExpression(node) && ts5.isIdentifier(node.expression) && node.expression.text === "Engine" && ["call", "hook"].includes(node.name.text) && !(ts5.isCallExpression(node.parent) && node.parent.expression === node)) {
14635
+ if (ts6.isPropertyAccessExpression(node) && ts6.isIdentifier(node.expression) && node.expression.text === "Engine" && ["call", "hook"].includes(node.name.text) && !(ts6.isCallExpression(node.parent) && node.parent.expression === node)) {
14406
14636
  const line = source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1;
14407
- report.ambiguities.push(`${relative4(report.package, file)}:${line}: indirect Engine.${node.name.text} lookup prevents proving timing/null behavior`);
14637
+ report.ambiguities.push(`${relative5(report.package, file)}:${line}: indirect Engine.${node.name.text} lookup prevents proving timing/null behavior`);
14408
14638
  }
14409
- if (ts5.isCallExpression(node)) {
14639
+ if (ts6.isCallExpression(node)) {
14410
14640
  const expr = node.expression;
14411
14641
  let kind;
14412
- if (ts5.isPropertyAccessExpression(expr) && ts5.isIdentifier(expr.expression) && expr.expression.text === "Engine" && ["call", "hook"].includes(expr.name.text)) kind = expr.name.text;
14642
+ if (ts6.isPropertyAccessExpression(expr) && ts6.isIdentifier(expr.expression) && expr.expression.text === "Engine" && ["call", "hook"].includes(expr.name.text)) kind = expr.name.text;
14413
14643
  if (kind) {
14414
14644
  const arg = node.arguments[0];
14415
14645
  const line = source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1;
14416
- if (!arg || !ts5.isStringLiteral(arg) && !ts5.isNoSubstitutionTemplateLiteral(arg)) report.ambiguities.push(`${relative4(report.package, file)}:${line}: dynamic Engine.${kind} reference prevents proving timing/null behavior`);
14646
+ if (!arg || !ts6.isStringLiteral(arg) && !ts6.isNoSubstitutionTemplateLiteral(arg)) report.ambiguities.push(`${relative5(report.package, file)}:${line}: dynamic Engine.${kind} reference prevents proving timing/null behavior`);
14417
14647
  else {
14418
14648
  const name = arg.text;
14419
- if (!(kind === "call" ? calls.has(name) : hooks.has(name))) report.ambiguities.push(`${relative4(report.package, file)}:${line}: Engine.${kind} references undeclared ${name}`);
14649
+ if (!(kind === "call" ? calls.has(name) : hooks.has(name))) report.ambiguities.push(`${relative5(report.package, file)}:${line}: Engine.${kind} references undeclared ${name}`);
14420
14650
  else report.sourceReferences.push({ file, line, kind: `Engine.${kind}`, name, manualEdit: `review ${kind} lookup, null behavior and callback timing for ${name}` });
14421
14651
  }
14422
14652
  }
14423
- if (ts5.isPropertyAccessExpression(expr) && ts5.isPropertyAccessExpression(expr.expression) && ts5.isIdentifier(expr.expression.expression) && expr.expression.expression.text === "ctx") {
14653
+ if (ts6.isPropertyAccessExpression(expr) && ts6.isPropertyAccessExpression(expr.expression) && ts6.isIdentifier(expr.expression.expression) && expr.expression.expression.text === "ctx") {
14424
14654
  const ns = expr.expression.name.text;
14425
14655
  const name = expr.name.text;
14426
14656
  if (hooks.get(name) === ns) report.sourceReferences.push({ file, line: source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1, kind: "ctx hook", name, manualEdit: `review callback timing and replace ctx.${ns}.${name} manually` });
14427
14657
  }
14428
14658
  }
14429
- ts5.forEachChild(node, visit);
14659
+ ts6.forEachChild(node, visit);
14430
14660
  };
14431
14661
  visit(source);
14432
14662
  }
14433
14663
  }
14434
14664
  function analyzeV1Migration(pluginDir, options = {}) {
14435
14665
  const dir = resolve26(pluginDir);
14436
- const destination = join35(dir, "gamedata", "functions.jsonc");
14666
+ const destination = join36(dir, "gamedata", "functions.jsonc");
14437
14667
  const report = { scope: SCOPE, package: dir, destination, entries: [], permissions: [], sourceReferences: [], generatedDeclarations: [], generatedDeclarationHashes: {}, recommendedEdits: [], ambiguities: [], filesChanged: [] };
14438
14668
  if (pathExists(destination) && !options.force) report.ambiguities.push(`${destination}: destination already exists; use --force to replace it`);
14439
14669
  let pkg;
14440
14670
  try {
14441
- pkg = parseFunctionFile(join35(dir, "package.json"), readFileSync30(join35(dir, "package.json"), "utf8"));
14671
+ pkg = parseFunctionFile(join36(dir, "package.json"), readFileSync31(join36(dir, "package.json"), "utf8"));
14442
14672
  } catch (e) {
14443
14673
  report.ambiguities.push(`package.json: ${e.message}`);
14444
14674
  return report;
@@ -14460,7 +14690,7 @@ function analyzeV1Migration(pluginDir, options = {}) {
14460
14690
  }
14461
14691
  let gd;
14462
14692
  try {
14463
- gd = parseFunctionFile(input, readFileSync30(input, "utf8"));
14693
+ gd = parseFunctionFile(input, readFileSync31(input, "utf8"));
14464
14694
  } catch (e) {
14465
14695
  report.ambiguities.push(`gamedata: ${e.message}`);
14466
14696
  return report;
@@ -14560,10 +14790,10 @@ function analyzeV1Migration(pluginDir, options = {}) {
14560
14790
  }
14561
14791
  }
14562
14792
  report.output = author;
14563
- report.generatedDeclarations = ["gamedata.d.ts", "hooks.d.ts"].map((n3) => join35(dir, ".s2script", n3)).filter(existsSync20);
14793
+ report.generatedDeclarations = ["gamedata.d.ts", "hooks.d.ts"].map((n3) => join36(dir, ".s2script", n3)).filter(existsSync21);
14564
14794
  for (const file of report.generatedDeclarations) {
14565
14795
  try {
14566
- report.generatedDeclarationHashes[file] = createHash5("sha256").update(readFileSync30(file)).digest("hex");
14796
+ report.generatedDeclarationHashes[file] = createHash6("sha256").update(readFileSync31(file)).digest("hex");
14567
14797
  } catch (e) {
14568
14798
  report.ambiguities.push(`${file}: generated declaration could not be read: ${e.message}`);
14569
14799
  }
@@ -14582,7 +14812,7 @@ function migrateV1Package(pluginDir, options = {}) {
14582
14812
  const report = analyzeV1Migration(pluginDir, options);
14583
14813
  if (report.ambiguities.length || !report.output) return report;
14584
14814
  const dest = report.destination;
14585
- const temp = join35(dirname10(dest), `.functions.jsonc.${process.pid}.${randomUUID()}.tmp`);
14815
+ const temp = join36(dirname10(dest), `.functions.jsonc.${process.pid}.${randomUUID()}.tmp`);
14586
14816
  try {
14587
14817
  mkdirSync11(dirname10(dest), { recursive: true });
14588
14818
  const fd2 = openSync(temp, "wx", 384);
@@ -14604,7 +14834,7 @@ function migrateV1Package(pluginDir, options = {}) {
14604
14834
  report.publication = void 0;
14605
14835
  report.ambiguities.push(`${dest}: publication failed: ${e.message}`);
14606
14836
  } finally {
14607
- if (existsSync20(temp)) unlinkSync(temp);
14837
+ if (existsSync21(temp)) unlinkSync(temp);
14608
14838
  }
14609
14839
  return report;
14610
14840
  }
@@ -14623,9 +14853,9 @@ async function run11(argv2) {
14623
14853
  }
14624
14854
 
14625
14855
  // src/commands/pack.ts
14626
- import { createHash as createHash6 } from "node:crypto";
14627
- import { readFileSync as readFileSync31, writeFileSync as writeFileSync24 } from "node:fs";
14628
- import { join as join36, resolve as resolve27 } from "node:path";
14856
+ import { createHash as createHash7 } from "node:crypto";
14857
+ import { readFileSync as readFileSync32, writeFileSync as writeFileSync24 } from "node:fs";
14858
+ import { join as join37, resolve as resolve27 } from "node:path";
14629
14859
  function formatBytes(n3) {
14630
14860
  if (n3 < 1e3) return `${n3}B`;
14631
14861
  if (n3 < 1e3 * 1e3) return `${(n3 / 1e3).toFixed(1)}kB`;
@@ -14633,9 +14863,9 @@ function formatBytes(n3) {
14633
14863
  }
14634
14864
  function packPackage(dir) {
14635
14865
  const absDir = resolve27(dir);
14636
- const pkg = JSON.parse(readFileSync31(join36(absDir, "package.json"), "utf8"));
14866
+ const pkg = JSON.parse(readFileSync32(join37(absDir, "package.json"), "utf8"));
14637
14867
  if (typeof pkg.name !== "string" || typeof pkg.version !== "string") {
14638
- throw new Error(`${join36(absDir, "package.json")}: "name" and "version" are required to pack`);
14868
+ throw new Error(`${join37(absDir, "package.json")}: "name" and "version" are required to pack`);
14639
14869
  }
14640
14870
  const pack = packSourceTarball(absDir, pkg);
14641
14871
  return {
@@ -14647,8 +14877,8 @@ function packPackage(dir) {
14647
14877
  filename: tarballFilename(pkg.name, pkg.version),
14648
14878
  size: pack.tarball.length,
14649
14879
  unpackedSize: pack.unpackedSize,
14650
- shasum: createHash6("sha1").update(pack.tarball).digest("hex"),
14651
- integrity: `sha512-${createHash6("sha512").update(pack.tarball).digest("base64")}`,
14880
+ shasum: createHash7("sha1").update(pack.tarball).digest("hex"),
14881
+ integrity: `sha512-${createHash7("sha512").update(pack.tarball).digest("base64")}`,
14652
14882
  files: pack.files,
14653
14883
  entryCount: pack.files.length
14654
14884
  }
@@ -14693,8 +14923,8 @@ async function run12(argv2) {
14693
14923
  }
14694
14924
 
14695
14925
  // src/commands/server.ts
14696
- import { existsSync as existsSync21 } from "node:fs";
14697
- import { join as join37, resolve as resolve28 } from "node:path";
14926
+ import { existsSync as existsSync22 } from "node:fs";
14927
+ import { join as join38, resolve as resolve28 } from "node:path";
14698
14928
  async function run13(argv2) {
14699
14929
  const [sub, dir] = positionals(argv2, ["--registry", "--name"]);
14700
14930
  if (sub !== "init") {
@@ -14703,7 +14933,7 @@ async function run13(argv2) {
14703
14933
  }
14704
14934
  const root = dir ? resolve28(dir) : findAddonRoot(".") ?? resolve28("addons", "s2script");
14705
14935
  try {
14706
- if (!existsSync21(join37(root, "plugins"))) {
14936
+ if (!existsSync22(join38(root, "plugins"))) {
14707
14937
  throw new Error(`${root} has no plugins/ directory \u2014 pass your addons/s2script directory`);
14708
14938
  }
14709
14939
  const report = await serverInit({
@@ -14715,7 +14945,7 @@ async function run13(argv2) {
14715
14945
  for (const m of report.managed) console.log(`managed ${m.name}@${m.version} (${m.filename})`);
14716
14946
  for (const u4 of report.unmanaged) console.log(`unmanaged ${u4.filename}: ${u4.reason}`);
14717
14947
  console.log(
14718
- `wrote ${join37(root, "plugins.json")} and plugins-lock.json \u2014 ${report.managed.length} managed, ${report.unmanaged.length} left as they are`
14948
+ `wrote ${join38(root, "plugins.json")} and plugins-lock.json \u2014 ${report.managed.length} managed, ${report.unmanaged.length} left as they are`
14719
14949
  );
14720
14950
  } catch (e) {
14721
14951
  console.error(String(e instanceof Error ? e.message : e));