@s2script/sdk 0.25.0 → 0.26.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/README.md +56 -0
- package/bans.d.ts +5 -2
- package/clients.d.ts +11 -5
- package/contracts/README.md +4 -0
- package/cookies.d.ts +8 -7
- package/db.d.ts +1 -0
- package/dist/cli.js +891 -293
- package/http.d.ts +1 -0
- package/interfaces.d.ts +120 -0
- package/menu.d.ts +6 -2
- package/net.d.ts +4 -4
- package/package.json +1 -1
- package/plugin.d.ts +123 -16
- package/timers.d.ts +8 -5
- package/ws.d.ts +2 -1
package/dist/cli.js
CHANGED
|
@@ -2076,7 +2076,7 @@ var require_semver2 = __commonJS({
|
|
|
2076
2076
|
});
|
|
2077
2077
|
|
|
2078
2078
|
// src/commands/build.ts
|
|
2079
|
-
import { resolve as
|
|
2079
|
+
import { resolve as resolve12 } from "node:path";
|
|
2080
2080
|
|
|
2081
2081
|
// src/build.ts
|
|
2082
2082
|
import * as esbuild2 from "esbuild";
|
|
@@ -3068,34 +3068,675 @@ function unzipSync(data, opts) {
|
|
|
3068
3068
|
}
|
|
3069
3069
|
|
|
3070
3070
|
// src/build.ts
|
|
3071
|
-
import { readFileSync as readFileSync8, writeFileSync as writeFileSync3, mkdirSync as
|
|
3072
|
-
import { resolve as
|
|
3071
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, rmSync as rmSync2 } from "node:fs";
|
|
3072
|
+
import { resolve as resolve11, join as join11, sep as sep2, basename } from "node:path";
|
|
3073
3073
|
|
|
3074
3074
|
// src/typecheck/typecheck.ts
|
|
3075
|
+
import ts3 from "typescript";
|
|
3076
|
+
|
|
3077
|
+
// src/interop.ts
|
|
3078
|
+
import ts2 from "typescript";
|
|
3079
|
+
import { createHash } from "node:crypto";
|
|
3080
|
+
|
|
3081
|
+
// src/publish-scan.ts
|
|
3075
3082
|
import ts from "typescript";
|
|
3076
|
-
|
|
3077
|
-
|
|
3083
|
+
function pluginApiName(checker, call) {
|
|
3084
|
+
const declaration = checker.getResolvedSignature(call)?.getDeclaration();
|
|
3085
|
+
if (!declaration || !declaration.getSourceFile().fileName.replace(/\\/g, "/").endsWith("/sdk/plugin.d.ts"))
|
|
3086
|
+
return void 0;
|
|
3087
|
+
const name = declaration.name;
|
|
3088
|
+
return name && (ts.isIdentifier(name) || ts.isStringLiteral(name)) ? name.text : void 0;
|
|
3089
|
+
}
|
|
3090
|
+
function recordPublishOrUse(out, method, arg0, sf, node) {
|
|
3091
|
+
if (method === "publish") {
|
|
3092
|
+
if (arg0 !== void 0 && ts.isStringLiteralLike(arg0)) {
|
|
3093
|
+
out.publishNames.push(arg0.text);
|
|
3094
|
+
} else {
|
|
3095
|
+
const { line } = sf.getLineAndCharacterOfPosition(node.getStart());
|
|
3096
|
+
out.dynamicPublishSites.push(`${sf.fileName}:${line + 1}`);
|
|
3097
|
+
}
|
|
3098
|
+
} else if (arg0 !== void 0 && ts.isStringLiteralLike(arg0)) {
|
|
3099
|
+
out.useNames.push(arg0.text);
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
3102
|
+
function recordImportSpecifier(out, spec) {
|
|
3103
|
+
if (spec.startsWith(".") || spec.startsWith("@s2script/")) return;
|
|
3104
|
+
out.importNames.push(spec);
|
|
3105
|
+
}
|
|
3106
|
+
function scanPluginProgram(program, pluginDir) {
|
|
3107
|
+
const checker = program.getTypeChecker();
|
|
3108
|
+
const out = {
|
|
3109
|
+
publishNames: [],
|
|
3110
|
+
dynamicPublishSites: [],
|
|
3111
|
+
useNames: [],
|
|
3112
|
+
importNames: []
|
|
3113
|
+
};
|
|
3114
|
+
const dirPrefix = pluginDir.replace(/\\/g, "/").replace(/\/+$/, "") + "/";
|
|
3115
|
+
for (const sf of program.getSourceFiles()) {
|
|
3116
|
+
if (sf.isDeclarationFile) continue;
|
|
3117
|
+
if (!sf.fileName.replace(/\\/g, "/").startsWith(dirPrefix)) continue;
|
|
3118
|
+
const visit = (node) => {
|
|
3119
|
+
if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier !== void 0 && ts.isStringLiteralLike(node.moduleSpecifier)) {
|
|
3120
|
+
recordImportSpecifier(out, node.moduleSpecifier.text);
|
|
3121
|
+
}
|
|
3122
|
+
if (ts.isCallExpression(node)) {
|
|
3123
|
+
const name = pluginApiName(checker, node);
|
|
3124
|
+
if (name && [
|
|
3125
|
+
"publish",
|
|
3126
|
+
"use",
|
|
3127
|
+
"tryUse",
|
|
3128
|
+
"watchOptional",
|
|
3129
|
+
"bindForwards"
|
|
3130
|
+
].includes(name))
|
|
3131
|
+
recordPublishOrUse(out, name, node.arguments[0], sf, node);
|
|
3132
|
+
}
|
|
3133
|
+
ts.forEachChild(node, visit);
|
|
3134
|
+
};
|
|
3135
|
+
visit(sf);
|
|
3136
|
+
}
|
|
3137
|
+
out.publishNames = [...new Set(out.publishNames)];
|
|
3138
|
+
out.useNames = [...new Set(out.useNames)];
|
|
3139
|
+
out.importNames = [...new Set(out.importNames)];
|
|
3140
|
+
return out;
|
|
3141
|
+
}
|
|
3142
|
+
|
|
3143
|
+
// src/interop.ts
|
|
3144
|
+
import { resolve } from "node:path";
|
|
3145
|
+
|
|
3146
|
+
// src/tsconfig-shared.ts
|
|
3147
|
+
var sharedCompilerOptionsJson = {
|
|
3148
|
+
strict: true,
|
|
3149
|
+
noEmit: true,
|
|
3150
|
+
moduleResolution: "bundler",
|
|
3151
|
+
module: "ESNext",
|
|
3152
|
+
target: "ES2024",
|
|
3153
|
+
lib: ["ES2024"],
|
|
3154
|
+
types: [],
|
|
3155
|
+
skipLibCheck: true,
|
|
3156
|
+
allowImportingTsExtensions: true
|
|
3157
|
+
};
|
|
3158
|
+
function sharedProgramOptions(tsMod) {
|
|
3159
|
+
return {
|
|
3160
|
+
strict: sharedCompilerOptionsJson.strict,
|
|
3161
|
+
noEmit: sharedCompilerOptionsJson.noEmit,
|
|
3162
|
+
allowImportingTsExtensions: sharedCompilerOptionsJson.allowImportingTsExtensions,
|
|
3163
|
+
moduleResolution: tsMod.ModuleResolutionKind.Bundler,
|
|
3164
|
+
module: tsMod.ModuleKind.ESNext,
|
|
3165
|
+
target: tsMod.ScriptTarget.ES2024,
|
|
3166
|
+
lib: sharedCompilerOptionsJson.lib.map((l2) => `lib.${l2.toLowerCase()}.d.ts`),
|
|
3167
|
+
types: [...sharedCompilerOptionsJson.types],
|
|
3168
|
+
skipLibCheck: sharedCompilerOptionsJson.skipLibCheck
|
|
3169
|
+
};
|
|
3170
|
+
}
|
|
3171
|
+
|
|
3172
|
+
// src/interop.ts
|
|
3173
|
+
function canonical(value) {
|
|
3174
|
+
if (typeof value === "string") {
|
|
3175
|
+
if (/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/u.test(
|
|
3176
|
+
value
|
|
3177
|
+
))
|
|
3178
|
+
throw new Error(
|
|
3179
|
+
"canonical metadata requires well-formed Unicode strings"
|
|
3180
|
+
);
|
|
3181
|
+
return JSON.stringify(value);
|
|
3182
|
+
}
|
|
3183
|
+
if (typeof value === "number" && !Number.isFinite(value))
|
|
3184
|
+
throw new Error("canonical metadata requires finite numbers");
|
|
3185
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
3186
|
+
if (value !== null && typeof value === "object")
|
|
3187
|
+
return `{${Object.keys(value).sort().map(
|
|
3188
|
+
(k) => `${canonical(k)}:${canonical(value[k])}`
|
|
3189
|
+
).join(",")}}`;
|
|
3190
|
+
if (value === null || typeof value === "boolean" || typeof value === "number")
|
|
3191
|
+
return JSON.stringify(value);
|
|
3192
|
+
throw new Error("canonical metadata requires JSON values");
|
|
3193
|
+
}
|
|
3194
|
+
function extractContract(path, packagesDir) {
|
|
3195
|
+
const program = ts2.createProgram([path], {
|
|
3196
|
+
...sharedProgramOptions(ts2),
|
|
3197
|
+
skipLibCheck: false,
|
|
3198
|
+
baseUrl: packagesDir,
|
|
3199
|
+
paths: { "@s2script/sdk": ["sdk/index.d.ts"], "@s2script/sdk/*": ["sdk/*.d.ts"] }
|
|
3200
|
+
});
|
|
3201
|
+
const checker = program.getTypeChecker();
|
|
3202
|
+
const sf = program.getSourceFile(path);
|
|
3203
|
+
const fail = (node, reason) => {
|
|
3204
|
+
const pos = sf.getLineAndCharacterOfPosition(node.getStart(sf));
|
|
3205
|
+
throw new Error(
|
|
3206
|
+
`${path}:${pos.line + 1}:${pos.character + 1}: InterfaceContractError: ${reason}`
|
|
3207
|
+
);
|
|
3208
|
+
};
|
|
3209
|
+
const visitImports = (node) => {
|
|
3210
|
+
if (ts2.isImportDeclaration(node)) {
|
|
3211
|
+
const module2 = ts2.isStringLiteral(node.moduleSpecifier) ? node.moduleSpecifier.text : "";
|
|
3212
|
+
const bindings = node.importClause?.namedBindings;
|
|
3213
|
+
const allowed = module2 === "@s2script/sdk/interfaces" ? ["Notification", "Hook", "Transform"] : module2 === "@s2script/sdk/events" || module2 === "@s2script/sdk" ? ["HookResultValue"] : module2 === "@s2script/sdk/entity" ? ["EntityRef"] : [];
|
|
3214
|
+
if (!bindings || !ts2.isNamedImports(bindings) || node.importClause?.name || !bindings.elements.every(
|
|
3215
|
+
(e) => allowed.includes((e.propertyName ?? e.name).text)
|
|
3216
|
+
))
|
|
3217
|
+
fail(
|
|
3218
|
+
node,
|
|
3219
|
+
"contracts must be self-contained; only SDK Notification, Hook, Transform, HookResultValue and EntityRef imports are supported"
|
|
3220
|
+
);
|
|
3221
|
+
}
|
|
3222
|
+
if (ts2.isExportDeclaration(node) && node.moduleSpecifier || ts2.isImportTypeNode(node) || ts2.isImportEqualsDeclaration(node) || ts2.isModuleDeclaration(node))
|
|
3223
|
+
fail(
|
|
3224
|
+
node,
|
|
3225
|
+
"external declarations and module augmentations are not supported in a contract"
|
|
3226
|
+
);
|
|
3227
|
+
ts2.forEachChild(node, visitImports);
|
|
3228
|
+
};
|
|
3229
|
+
visitImports(sf);
|
|
3230
|
+
if (sf.referencedFiles.length || sf.typeReferenceDirectives.length)
|
|
3231
|
+
fail(sf, "external declaration references are not supported");
|
|
3232
|
+
const diags = [
|
|
3233
|
+
...program.getSyntacticDiagnostics(sf),
|
|
3234
|
+
...program.getSemanticDiagnostics(sf)
|
|
3235
|
+
];
|
|
3236
|
+
if (diags.length)
|
|
3237
|
+
fail(sf, ts2.flattenDiagnosticMessageText(diags[0].messageText, "\n"));
|
|
3238
|
+
const module = checker.getSymbolAtLocation(sf);
|
|
3239
|
+
const symbol2 = module && checker.getExportsOfModule(module).find((s) => s.name === "Contract");
|
|
3240
|
+
if (!symbol2) fail(sf, "protocol 2 requires an exported Contract");
|
|
3241
|
+
const contract = checker.getDeclaredTypeOfSymbol(symbol2);
|
|
3242
|
+
const prop = (type, name, node) => {
|
|
3243
|
+
const p = type.getProperty(name);
|
|
3244
|
+
if (!p) fail(node, `missing ${name}`);
|
|
3245
|
+
return checker.getTypeOfSymbolAtLocation(p, p.valueDeclaration ?? node);
|
|
3246
|
+
};
|
|
3247
|
+
for (const name of ["methods", "forwards"]) {
|
|
3248
|
+
const member = prop(contract, name, sf);
|
|
3249
|
+
if (!(member.flags & ts2.TypeFlags.Object) || member.getCallSignatures().length || checker.getIndexInfosOfType(member).length) {
|
|
3250
|
+
fail(sf, `${name} must be a finite object record`);
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3253
|
+
const stack = /* @__PURE__ */ new Set();
|
|
3254
|
+
const schema = (type, node, voidAllowed = false) => {
|
|
3255
|
+
const f = type.flags;
|
|
3256
|
+
if (f & ts2.TypeFlags.Any || f & ts2.TypeFlags.Unknown || f & ts2.TypeFlags.Never)
|
|
3257
|
+
fail(node, `unsupported wire type ${checker.typeToString(type)}`);
|
|
3258
|
+
if (f & ts2.TypeFlags.Void && voidAllowed) return { kind: "void" };
|
|
3259
|
+
if (f & ts2.TypeFlags.Null) return { kind: "null" };
|
|
3260
|
+
if (f & ts2.TypeFlags.StringLiteral)
|
|
3261
|
+
return { kind: "literal", value: type.value };
|
|
3262
|
+
if (f & ts2.TypeFlags.NumberLiteral)
|
|
3263
|
+
return { kind: "literal", value: type.value };
|
|
3264
|
+
if (f & ts2.TypeFlags.BooleanLiteral)
|
|
3265
|
+
return { kind: "literal", value: checker.typeToString(type) === "true" };
|
|
3266
|
+
if (f & ts2.TypeFlags.String) return { kind: "string" };
|
|
3267
|
+
if (f & ts2.TypeFlags.Number) return { kind: "number" };
|
|
3268
|
+
if (f & ts2.TypeFlags.Boolean) return { kind: "boolean" };
|
|
3269
|
+
if (stack.has(type)) fail(node, "recursive wire types are unsupported");
|
|
3270
|
+
stack.add(type);
|
|
3271
|
+
try {
|
|
3272
|
+
if (type.isUnion()) {
|
|
3273
|
+
const variants = type.types.map((t2) => schema(t2, node));
|
|
3274
|
+
const objects = variants.filter((v) => v.kind === "object");
|
|
3275
|
+
if (objects.length > 1 && !Object.keys(objects[0].fields).some(
|
|
3276
|
+
(k) => objects.every(
|
|
3277
|
+
(o) => o.fields[k]?.schema.kind === "literal" && !o.fields[k].optional
|
|
3278
|
+
)
|
|
3279
|
+
))
|
|
3280
|
+
fail(node, "object unions require a literal discriminant");
|
|
3281
|
+
return { kind: "union", variants };
|
|
3282
|
+
}
|
|
3283
|
+
if (checker.isArrayType(type))
|
|
3284
|
+
return {
|
|
3285
|
+
kind: "array",
|
|
3286
|
+
item: schema(
|
|
3287
|
+
checker.getTypeArguments(type)[0],
|
|
3288
|
+
node
|
|
3289
|
+
)
|
|
3290
|
+
};
|
|
3291
|
+
if (!(f & ts2.TypeFlags.Object) || checker.isTupleType(type) || type.getCallSignatures().length || checker.getIndexInfosOfType(type).length)
|
|
3292
|
+
fail(node, `unsupported wire type ${checker.typeToString(type)}`);
|
|
3293
|
+
const sym = type.getSymbol();
|
|
3294
|
+
if (sym?.name === "EntityRef" && sym.declarations?.some(
|
|
3295
|
+
(d) => d.getSourceFile().fileName === resolve(packagesDir, "sdk/entity.d.ts")
|
|
3296
|
+
))
|
|
3297
|
+
return { kind: "entityRef" };
|
|
3298
|
+
if (sym?.declarations?.some(
|
|
3299
|
+
(d) => ts2.isClassDeclaration(d) || d.getSourceFile() !== sf
|
|
3300
|
+
))
|
|
3301
|
+
fail(
|
|
3302
|
+
node,
|
|
3303
|
+
"arbitrary class instances and imported domain types are unsupported"
|
|
3304
|
+
);
|
|
3305
|
+
const fields = /* @__PURE__ */ Object.create(null);
|
|
3306
|
+
for (const p of type.getProperties().sort((a2, b) => a2.name.localeCompare(b.name))) {
|
|
3307
|
+
const decl = p.valueDeclaration ?? node;
|
|
3308
|
+
if (p.name === "__s2ref")
|
|
3309
|
+
fail(decl, "__s2ref is reserved for the EntityRef wire encoding");
|
|
3310
|
+
const optional = !!(p.flags & ts2.SymbolFlags.Optional);
|
|
3311
|
+
let t2 = checker.getTypeOfSymbolAtLocation(p, decl);
|
|
3312
|
+
if (optional && t2.isUnion()) {
|
|
3313
|
+
const parts = t2.types.filter(
|
|
3314
|
+
(t3) => !(t3.flags & ts2.TypeFlags.Undefined)
|
|
3315
|
+
);
|
|
3316
|
+
if (parts.length === 1) t2 = parts[0];
|
|
3317
|
+
else {
|
|
3318
|
+
fields[p.name] = {
|
|
3319
|
+
schema: {
|
|
3320
|
+
kind: "union",
|
|
3321
|
+
variants: parts.map((t3) => schema(t3, decl))
|
|
3322
|
+
},
|
|
3323
|
+
optional
|
|
3324
|
+
};
|
|
3325
|
+
continue;
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
fields[p.name] = { schema: schema(t2, decl), optional };
|
|
3329
|
+
}
|
|
3330
|
+
return { kind: "object", fields };
|
|
3331
|
+
} finally {
|
|
3332
|
+
stack.delete(type);
|
|
3333
|
+
}
|
|
3334
|
+
};
|
|
3335
|
+
const methods = /* @__PURE__ */ Object.create(null);
|
|
3336
|
+
for (const p of prop(contract, "methods", sf).getProperties()) {
|
|
3337
|
+
const node = p.valueDeclaration ?? sf, type = checker.getTypeOfSymbolAtLocation(p, node), sigs = type.getCallSignatures();
|
|
3338
|
+
const scanner = ts2.createScanner(
|
|
3339
|
+
ts2.ScriptTarget.ESNext,
|
|
3340
|
+
false,
|
|
3341
|
+
ts2.LanguageVariant.Standard,
|
|
3342
|
+
p.name
|
|
3343
|
+
);
|
|
3344
|
+
const identifier = scanner.scan() === ts2.SyntaxKind.Identifier && scanner.scan() === ts2.SyntaxKind.EndOfFileToken;
|
|
3345
|
+
if (!identifier || ["on", "off", "then"].includes(p.name))
|
|
3346
|
+
fail(node, `unsupported or reserved method name ${p.name}`);
|
|
3347
|
+
const exported = checker.getExportsOfModule(module).find((s) => s.name === p.name);
|
|
3348
|
+
if (exported && (!checker.isTypeAssignableTo(
|
|
3349
|
+
checker.getTypeOfSymbolAtLocation(
|
|
3350
|
+
exported,
|
|
3351
|
+
exported.valueDeclaration ?? sf
|
|
3352
|
+
),
|
|
3353
|
+
type
|
|
3354
|
+
) || !checker.isTypeAssignableTo(
|
|
3355
|
+
type,
|
|
3356
|
+
checker.getTypeOfSymbolAtLocation(
|
|
3357
|
+
exported,
|
|
3358
|
+
exported.valueDeclaration ?? sf
|
|
3359
|
+
)
|
|
3360
|
+
)))
|
|
3361
|
+
fail(node, `export ${p.name} must agree with Contract.methods`);
|
|
3362
|
+
if (sigs.length !== 1 || sigs[0].typeParameters?.length || p.flags & ts2.SymbolFlags.Optional)
|
|
3363
|
+
fail(node, "methods require one non-generic synchronous signature");
|
|
3364
|
+
const sig = sigs[0];
|
|
3365
|
+
methods[p.name] = {
|
|
3366
|
+
args: sig.parameters.map((p2) => {
|
|
3367
|
+
const d = p2.valueDeclaration;
|
|
3368
|
+
if (d.dotDotDotToken) fail(d, "rest parameters are unsupported");
|
|
3369
|
+
let t2 = checker.getTypeOfSymbolAtLocation(p2, d);
|
|
3370
|
+
if (d.questionToken && t2.isUnion()) {
|
|
3371
|
+
const parts = t2.types.filter(
|
|
3372
|
+
(t3) => !(t3.flags & ts2.TypeFlags.Undefined)
|
|
3373
|
+
);
|
|
3374
|
+
if (parts.length === 1) t2 = parts[0];
|
|
3375
|
+
else fail(d, "optional union arguments are unsupported");
|
|
3376
|
+
}
|
|
3377
|
+
return { schema: schema(t2, d), optional: !!d.questionToken };
|
|
3378
|
+
}),
|
|
3379
|
+
result: schema(checker.getReturnTypeOfSignature(sig), node, true)
|
|
3380
|
+
};
|
|
3381
|
+
}
|
|
3382
|
+
const forwards = /* @__PURE__ */ Object.create(null);
|
|
3383
|
+
for (const p of prop(contract, "forwards", sf).getProperties()) {
|
|
3384
|
+
const node = p.valueDeclaration ?? sf, type = checker.getTypeOfSymbolAtLocation(p, node);
|
|
3385
|
+
const descriptors = [
|
|
3386
|
+
["notification", "__notificationPayload"],
|
|
3387
|
+
["hook", "__hookPayload"],
|
|
3388
|
+
["transform", "__transformPayload"]
|
|
3389
|
+
];
|
|
3390
|
+
const descriptor = descriptors.filter(([, key2]) => type.getProperty(key2));
|
|
3391
|
+
if (descriptor.length !== 1)
|
|
3392
|
+
fail(
|
|
3393
|
+
node,
|
|
3394
|
+
"only SDK Notification<P>, Hook<P> and Transform<P,W> forwards are supported"
|
|
3395
|
+
);
|
|
3396
|
+
const [kind, key] = descriptor[0];
|
|
3397
|
+
const brand = type.getProperty(key);
|
|
3398
|
+
if (!brand.declarations?.some(
|
|
3399
|
+
(d) => d.getSourceFile().fileName === resolve(packagesDir, "sdk/interfaces.d.ts")
|
|
3400
|
+
))
|
|
3401
|
+
fail(node, "forward descriptors must come from the SDK");
|
|
3402
|
+
const payload = schema(
|
|
3403
|
+
checker.getTypeOfSymbolAtLocation(brand, node),
|
|
3404
|
+
node
|
|
3405
|
+
);
|
|
3406
|
+
if (kind === "transform") {
|
|
3407
|
+
if (payload.kind !== "object")
|
|
3408
|
+
fail(node, "Transform requires a finite object payload");
|
|
3409
|
+
const writableType = prop(type, "__transformWritable", node);
|
|
3410
|
+
const keys = writableType.flags & ts2.TypeFlags.Never ? [] : writableType.isUnion() ? writableType.types : [writableType];
|
|
3411
|
+
const writable = keys.map((t2) => {
|
|
3412
|
+
if (!(t2.flags & ts2.TypeFlags.StringLiteral))
|
|
3413
|
+
fail(node, "Transform writable keys must be string field names");
|
|
3414
|
+
const key2 = t2.value;
|
|
3415
|
+
if (!Object.hasOwn(payload.fields, key2))
|
|
3416
|
+
fail(node, "Transform writable key is absent from payload");
|
|
3417
|
+
return key2;
|
|
3418
|
+
}).sort();
|
|
3419
|
+
forwards[p.name] = { kind, payload, writable };
|
|
3420
|
+
} else forwards[p.name] = { kind, payload };
|
|
3421
|
+
}
|
|
3422
|
+
for (const exported of checker.getExportsOfModule(module)) {
|
|
3423
|
+
const typeOnly = exported.declarations?.every(
|
|
3424
|
+
(d) => ts2.isExportSpecifier(d) && (d.isTypeOnly || ts2.isExportDeclaration(d.parent.parent) && d.parent.parent.isTypeOnly)
|
|
3425
|
+
);
|
|
3426
|
+
const target = exported.flags & ts2.SymbolFlags.Alias ? checker.getAliasedSymbol(exported) : exported;
|
|
3427
|
+
if (!typeOnly && target.flags & ts2.SymbolFlags.Value && !Object.hasOwn(methods, exported.name))
|
|
3428
|
+
fail(exported.declarations?.[0] ?? sf, `unsupported value export ${exported.name}; only Contract.methods may export runtime values`);
|
|
3429
|
+
}
|
|
3430
|
+
const metadata = { version: 1, methods, forwards };
|
|
3431
|
+
return {
|
|
3432
|
+
metadata,
|
|
3433
|
+
sha256: createHash("sha256").update(canonical(metadata)).digest("hex")
|
|
3434
|
+
};
|
|
3435
|
+
}
|
|
3436
|
+
function implementationExpression(checker, expression, seen = /* @__PURE__ */ new Set()) {
|
|
3437
|
+
if (seen.has(expression)) return expression;
|
|
3438
|
+
seen.add(expression);
|
|
3439
|
+
const propertyValue = (receiver, name) => {
|
|
3440
|
+
const object = implementationExpression(checker, receiver, seen);
|
|
3441
|
+
const declaration = checker.getTypeAtLocation(object).getProperty(name)?.valueDeclaration;
|
|
3442
|
+
return declaration && (declarationValue(declaration) ?? declaration);
|
|
3443
|
+
};
|
|
3444
|
+
const declarationValue = (declaration) => {
|
|
3445
|
+
if (seen.has(declaration)) return void 0;
|
|
3446
|
+
seen.add(declaration);
|
|
3447
|
+
if ((ts2.isVariableDeclaration(declaration) || ts2.isPropertyAssignment(declaration)) && declaration.initializer)
|
|
3448
|
+
return implementationExpression(checker, declaration.initializer, seen);
|
|
3449
|
+
if (ts2.isShorthandPropertyAssignment(declaration)) {
|
|
3450
|
+
const value = checker.getShorthandAssignmentValueSymbol(
|
|
3451
|
+
declaration
|
|
3452
|
+
)?.valueDeclaration;
|
|
3453
|
+
return value && declarationValue(value);
|
|
3454
|
+
}
|
|
3455
|
+
if (ts2.isBindingElement(declaration) && ts2.isObjectBindingPattern(declaration.parent)) {
|
|
3456
|
+
const object = declarationValue(declaration.parent.parent);
|
|
3457
|
+
const name = declaration.propertyName ?? declaration.name;
|
|
3458
|
+
if (object && (ts2.isIdentifier(name) || ts2.isStringLiteral(name)))
|
|
3459
|
+
return propertyValue(object, name.text);
|
|
3460
|
+
}
|
|
3461
|
+
return void 0;
|
|
3462
|
+
};
|
|
3463
|
+
if (ts2.isParenthesizedExpression(expression))
|
|
3464
|
+
return implementationExpression(checker, expression.expression, seen);
|
|
3465
|
+
if (ts2.isPropertyAccessExpression(expression))
|
|
3466
|
+
return propertyValue(expression.expression, expression.name.text) ?? expression;
|
|
3467
|
+
if (ts2.isElementAccessExpression(expression) && ts2.isStringLiteralLike(expression.argumentExpression))
|
|
3468
|
+
return propertyValue(
|
|
3469
|
+
expression.expression,
|
|
3470
|
+
expression.argumentExpression.text
|
|
3471
|
+
) ?? expression;
|
|
3472
|
+
let symbol2 = checker.getSymbolAtLocation(expression);
|
|
3473
|
+
if (symbol2 && symbol2.flags & ts2.SymbolFlags.Alias)
|
|
3474
|
+
symbol2 = checker.getAliasedSymbol(symbol2);
|
|
3475
|
+
return (symbol2?.valueDeclaration && declarationValue(symbol2.valueDeclaration)) ?? expression;
|
|
3476
|
+
}
|
|
3477
|
+
function checkInteropCalls(program, paths, publishes, generatedPath, pluginDir, dependencies, optionalDependencies) {
|
|
3478
|
+
const checker = program.getTypeChecker(), out = [];
|
|
3479
|
+
const report = (node, message) => out.push({
|
|
3480
|
+
category: ts2.DiagnosticCategory.Error,
|
|
3481
|
+
code: 92002,
|
|
3482
|
+
file: node.getSourceFile(),
|
|
3483
|
+
start: node.getStart(),
|
|
3484
|
+
length: node.getWidth(),
|
|
3485
|
+
messageText: `InterfaceContractError: ${message}`
|
|
3486
|
+
});
|
|
3487
|
+
const contracts = /* @__PURE__ */ new Map();
|
|
3488
|
+
for (const [name, path] of Object.entries(paths)) {
|
|
3489
|
+
const sf = program.getSourceFile(path);
|
|
3490
|
+
const sym = sf && checker.getSymbolAtLocation(sf);
|
|
3491
|
+
const c2 = sym && checker.getExportsOfModule(sym).find((s) => s.name === "Contract");
|
|
3492
|
+
if (c2) contracts.set(name, checker.getDeclaredTypeOfSymbol(c2));
|
|
3493
|
+
}
|
|
3494
|
+
const sdkInterfaces = resolve(
|
|
3495
|
+
program.getCompilerOptions().baseUrl,
|
|
3496
|
+
"sdk/interfaces.d.ts"
|
|
3497
|
+
);
|
|
3498
|
+
for (const sf of program.getSourceFiles()) {
|
|
3499
|
+
const visit = (node) => {
|
|
3500
|
+
if (ts2.isInterfaceDeclaration(node) && node.name.text === "InterfaceContracts" && sf.fileName !== generatedPath && sf.fileName !== sdkInterfaces)
|
|
3501
|
+
report(
|
|
3502
|
+
node,
|
|
3503
|
+
"InterfaceContracts associations are generated; local augmentation is not authority"
|
|
3504
|
+
);
|
|
3505
|
+
if (ts2.isCallExpression(node) && sf.fileName.startsWith(pluginDir + "/")) {
|
|
3506
|
+
const method = pluginApiName(checker, node);
|
|
3507
|
+
if ([
|
|
3508
|
+
"publish",
|
|
3509
|
+
"use",
|
|
3510
|
+
"tryUse",
|
|
3511
|
+
"watchOptional",
|
|
3512
|
+
"bindForwards"
|
|
3513
|
+
].includes(method ?? "")) {
|
|
3514
|
+
const arg = node.arguments[0];
|
|
3515
|
+
const name = arg && ts2.isStringLiteralLike(arg) ? arg.text : null;
|
|
3516
|
+
if (!name || !contracts.has(name))
|
|
3517
|
+
report(
|
|
3518
|
+
node,
|
|
3519
|
+
`interface ${name ?? "(dynamic name)"} has no verified protocol 2 contract; declare the dependency and run s2s add`
|
|
3520
|
+
);
|
|
3521
|
+
else {
|
|
3522
|
+
if (node.typeArguments?.length)
|
|
3523
|
+
report(
|
|
3524
|
+
node,
|
|
3525
|
+
"protocol 2 infers Contract from the interface name; explicit generic arguments are forbidden"
|
|
3526
|
+
);
|
|
3527
|
+
if (method === "watchOptional" && !optionalDependencies.has(name))
|
|
3528
|
+
report(node, "watchOptional requires an optionalPluginDependencies entry");
|
|
3529
|
+
if (method === "bindForwards" && optionalDependencies.has(name))
|
|
3530
|
+
report(node, "bindForwards requires a pluginDependencies entry");
|
|
3531
|
+
if (method === "watchOptional" && node.arguments[1]) {
|
|
3532
|
+
const callback = node.arguments[1];
|
|
3533
|
+
const type = checker.getTypeAtLocation(implementationExpression(checker, callback));
|
|
3534
|
+
const asynchronous = (type2) => type2.isUnion() ? type2.types.some(asynchronous) : !!(type2.flags & ts2.TypeFlags.Any) || !!type2.getProperty("then");
|
|
3535
|
+
if (type.flags & ts2.TypeFlags.Any || type.getCallSignatures().some((signature) => asynchronous(checker.getReturnTypeOfSignature(signature))))
|
|
3536
|
+
report(callback, "watchOptional requires a synchronous attachment callback; thenables are forbidden");
|
|
3537
|
+
}
|
|
3538
|
+
if (method === "publish") {
|
|
3539
|
+
if (!publishes.has(name))
|
|
3540
|
+
report(node, `provider is not authorized to publish ${name}`);
|
|
3541
|
+
const c2 = contracts.get(name), m = c2.getProperty("methods");
|
|
3542
|
+
const expected = checker.getTypeOfSymbolAtLocation(
|
|
3543
|
+
m,
|
|
3544
|
+
m.valueDeclaration
|
|
3545
|
+
);
|
|
3546
|
+
const impl = node.arguments[1];
|
|
3547
|
+
const actual = impl && checker.getTypeAtLocation(impl);
|
|
3548
|
+
const implementationType = impl && checker.getTypeAtLocation(
|
|
3549
|
+
implementationExpression(checker, impl)
|
|
3550
|
+
);
|
|
3551
|
+
if (!actual || actual.flags & ts2.TypeFlags.Any || !checker.isTypeAssignableTo(actual, expected))
|
|
3552
|
+
report(
|
|
3553
|
+
impl ?? node,
|
|
3554
|
+
`producer implementation must agree with ${name} Contract.methods (${checker.typeToString(
|
|
3555
|
+
expected
|
|
3556
|
+
)})`
|
|
3557
|
+
);
|
|
3558
|
+
if (actual) {
|
|
3559
|
+
for (const property of actual.getProperties()) {
|
|
3560
|
+
if (!expected.getProperty(property.name) && checker.getTypeOfSymbolAtLocation(property, impl).getCallSignatures().length) {
|
|
3561
|
+
report(
|
|
3562
|
+
impl,
|
|
3563
|
+
`undeclared method ${property.name} is not in Contract.methods`
|
|
3564
|
+
);
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
if (actual) {
|
|
3569
|
+
for (const expectedMethod of expected.getProperties()) {
|
|
3570
|
+
const implementation = implementationType?.getProperty(
|
|
3571
|
+
expectedMethod.name
|
|
3572
|
+
);
|
|
3573
|
+
if (!implementation) continue;
|
|
3574
|
+
const required = checker.getTypeOfSymbolAtLocation(expectedMethod, impl).getCallSignatures()[0];
|
|
3575
|
+
const declaration = implementation.valueDeclaration;
|
|
3576
|
+
const value = declaration && ts2.isPropertyAssignment(declaration) ? declaration.initializer : declaration && ts2.isShorthandPropertyAssignment(declaration) ? declaration.name : void 0;
|
|
3577
|
+
const methodType = value ? checker.getTypeAtLocation(
|
|
3578
|
+
implementationExpression(checker, value)
|
|
3579
|
+
) : checker.getTypeOfSymbolAtLocation(implementation, impl);
|
|
3580
|
+
const signatures = methodType.getCallSignatures();
|
|
3581
|
+
if (!required || signatures.length !== 1 || signatures[0].typeParameters?.length) {
|
|
3582
|
+
report(
|
|
3583
|
+
impl,
|
|
3584
|
+
`producer method ${expectedMethod.name} signature must be a single non-generic function`
|
|
3585
|
+
);
|
|
3586
|
+
continue;
|
|
3587
|
+
}
|
|
3588
|
+
const provided = signatures[0];
|
|
3589
|
+
const result = checker.getReturnTypeOfSignature(provided);
|
|
3590
|
+
const expectedResult = checker.getReturnTypeOfSignature(required);
|
|
3591
|
+
const resultMatches = expectedResult.flags & ts2.TypeFlags.Void ? !!(result.flags & (ts2.TypeFlags.Void | ts2.TypeFlags.Undefined | ts2.TypeFlags.Never)) : checker.isTypeAssignableTo(result, expectedResult);
|
|
3592
|
+
if (result.flags & ts2.TypeFlags.Any || !resultMatches)
|
|
3593
|
+
report(
|
|
3594
|
+
impl,
|
|
3595
|
+
`producer method ${expectedMethod.name} result must agree with its synchronous Contract signature`
|
|
3596
|
+
);
|
|
3597
|
+
for (const [i2, parameter] of provided.parameters.entries()) {
|
|
3598
|
+
const declaration2 = parameter.valueDeclaration;
|
|
3599
|
+
if (declaration2 && ts2.isParameter(declaration2) && declaration2.dotDotDotToken) {
|
|
3600
|
+
report(
|
|
3601
|
+
impl,
|
|
3602
|
+
`producer method ${expectedMethod.name} signature cannot use rest parameters`
|
|
3603
|
+
);
|
|
3604
|
+
continue;
|
|
3605
|
+
}
|
|
3606
|
+
const input = checker.getTypeOfSymbolAtLocation(
|
|
3607
|
+
parameter,
|
|
3608
|
+
impl
|
|
3609
|
+
);
|
|
3610
|
+
const contractParameter = required.parameters[i2];
|
|
3611
|
+
if (!contractParameter) {
|
|
3612
|
+
if (!(parameter.flags & ts2.SymbolFlags.Optional) && !(declaration2 && ts2.isParameter(declaration2) && (declaration2.questionToken || declaration2.initializer)))
|
|
3613
|
+
report(
|
|
3614
|
+
impl,
|
|
3615
|
+
`producer method ${expectedMethod.name} requires an undeclared input`
|
|
3616
|
+
);
|
|
3617
|
+
} else {
|
|
3618
|
+
const requiredInput = checker.getTypeOfSymbolAtLocation(
|
|
3619
|
+
contractParameter,
|
|
3620
|
+
impl
|
|
3621
|
+
);
|
|
3622
|
+
const variants = requiredInput.isUnion() ? requiredInput.types : [requiredInput];
|
|
3623
|
+
const hasDefault = declaration2 && ts2.isParameter(declaration2) && declaration2.initializer;
|
|
3624
|
+
if (!variants.every(
|
|
3625
|
+
(type) => hasDefault && !!(type.flags & ts2.TypeFlags.Undefined) || checker.isTypeAssignableTo(type, input)
|
|
3626
|
+
))
|
|
3627
|
+
report(
|
|
3628
|
+
impl,
|
|
3629
|
+
`producer method ${expectedMethod.name} input ${parameter.name} is narrower than its Contract signature`
|
|
3630
|
+
);
|
|
3631
|
+
}
|
|
3632
|
+
}
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3635
|
+
} else if (!dependencies.has(name))
|
|
3636
|
+
report(node, `undeclared dependency ${name}`);
|
|
3637
|
+
}
|
|
3638
|
+
}
|
|
3639
|
+
}
|
|
3640
|
+
ts2.forEachChild(node, visit);
|
|
3641
|
+
};
|
|
3642
|
+
visit(sf);
|
|
3643
|
+
}
|
|
3644
|
+
return out;
|
|
3645
|
+
}
|
|
3646
|
+
|
|
3647
|
+
// src/publishes.ts
|
|
3648
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
3649
|
+
import { readFileSync } from "node:fs";
|
|
3650
|
+
function expandPublishes(authored, pkgName, pkgVersion) {
|
|
3651
|
+
if (authored === void 0 || authored === null) return {};
|
|
3652
|
+
if (typeof authored === "string") {
|
|
3653
|
+
if (authored.trim() !== "self") {
|
|
3654
|
+
throw new Error(
|
|
3655
|
+
`publishes: the only valid string form is "self" (got ${JSON.stringify(authored)}); use the map form to publish a differently-named contract`
|
|
3656
|
+
);
|
|
3657
|
+
}
|
|
3658
|
+
return { [pkgName]: pkgVersion };
|
|
3659
|
+
}
|
|
3660
|
+
if (typeof authored !== "object") {
|
|
3661
|
+
throw new Error(`publishes must be "self" or an object (got ${typeof authored})`);
|
|
3662
|
+
}
|
|
3663
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
3664
|
+
for (const [iface, range] of Object.entries(authored)) {
|
|
3665
|
+
if (typeof range !== "string") {
|
|
3666
|
+
throw new Error(`publishes[${JSON.stringify(iface)}] must be a version range string`);
|
|
3667
|
+
}
|
|
3668
|
+
out[iface] = range;
|
|
3669
|
+
}
|
|
3670
|
+
return out;
|
|
3671
|
+
}
|
|
3672
|
+
function hashContract(typesPath) {
|
|
3673
|
+
return createHash2("sha256").update(readFileSync(typesPath)).digest("hex");
|
|
3674
|
+
}
|
|
3675
|
+
var CONCRETE_SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
3676
|
+
function isConcreteVersion(v) {
|
|
3677
|
+
return CONCRETE_SEMVER.test(v.trim());
|
|
3678
|
+
}
|
|
3679
|
+
function derivePublishes(authored, pkgName, pkgVersion, typesPath) {
|
|
3680
|
+
const expanded = expandPublishes(authored, pkgName, pkgVersion);
|
|
3681
|
+
const names = Object.keys(expanded);
|
|
3682
|
+
if (names.length === 0) return {};
|
|
3683
|
+
if (names.length > 1) {
|
|
3684
|
+
throw new Error(
|
|
3685
|
+
`publishes declares ${names.length} interfaces (${names.join(", ")}) but a plugin ships a single "types" contract, so they would all carry the SAME typesSha256 \u2014 a hash that cannot say which contract is which. Publish one interface per plugin (a per-interface "types" map is not supported).`
|
|
3686
|
+
);
|
|
3687
|
+
}
|
|
3688
|
+
if (typesPath === null) {
|
|
3689
|
+
throw new Error(
|
|
3690
|
+
`publishes is set but no contract .d.ts was resolved \u2014 set "types": "api.d.ts" in package.json`
|
|
3691
|
+
);
|
|
3692
|
+
}
|
|
3693
|
+
for (const [iface, v] of Object.entries(expanded)) {
|
|
3694
|
+
if (!isConcreteVersion(v)) {
|
|
3695
|
+
throw new Error(
|
|
3696
|
+
`publishes[${JSON.stringify(iface)}] = ${JSON.stringify(v)} is a RANGE. Resolving a range against a published contract requires the registry (design spec \xA74.6, \xA710 \u2014 not yet implemented). Use a concrete version for a contract you ship yourself, or "self".`
|
|
3697
|
+
);
|
|
3698
|
+
}
|
|
3699
|
+
}
|
|
3700
|
+
const typesSha256 = hashContract(typesPath);
|
|
3701
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
3702
|
+
for (const name of names) {
|
|
3703
|
+
out[name] = { version: expanded[name].trim(), typesSha256 };
|
|
3704
|
+
}
|
|
3705
|
+
return out;
|
|
3706
|
+
}
|
|
3707
|
+
|
|
3708
|
+
// src/typecheck/typecheck.ts
|
|
3709
|
+
import {
|
|
3710
|
+
mkdirSync,
|
|
3711
|
+
existsSync as existsSync6,
|
|
3712
|
+
readdirSync as readdirSync2,
|
|
3713
|
+
readFileSync as readFileSync6,
|
|
3714
|
+
writeFileSync,
|
|
3715
|
+
rmSync,
|
|
3716
|
+
mkdtempSync
|
|
3717
|
+
} from "node:fs";
|
|
3718
|
+
import { join as join7, resolve as resolve7, dirname as dirname3 } from "node:path";
|
|
3078
3719
|
import { tmpdir } from "node:os";
|
|
3079
3720
|
|
|
3080
3721
|
// src/packages-resolve.ts
|
|
3081
3722
|
import { existsSync } from "node:fs";
|
|
3082
|
-
import { dirname, join, resolve } from "node:path";
|
|
3723
|
+
import { dirname, join, resolve as resolve2 } from "node:path";
|
|
3083
3724
|
import { fileURLToPath } from "node:url";
|
|
3084
3725
|
function isPackagesDir(dir) {
|
|
3085
|
-
const abs =
|
|
3726
|
+
const abs = resolve2(dir);
|
|
3086
3727
|
return existsSync(join(abs, "sdk", "globals.d.ts")) || existsSync(join(abs, "sdk", "entity.d.ts"));
|
|
3087
3728
|
}
|
|
3088
3729
|
function findPackagesDirNearCli(fromCliUrl = import.meta.url) {
|
|
3089
3730
|
const start = dirname(fileURLToPath(fromCliUrl));
|
|
3090
3731
|
for (const rel of ["../..", "../../.."]) {
|
|
3091
3732
|
const candidate = join(start, rel);
|
|
3092
|
-
if (isPackagesDir(candidate)) return
|
|
3733
|
+
if (isPackagesDir(candidate)) return resolve2(candidate);
|
|
3093
3734
|
}
|
|
3094
3735
|
return void 0;
|
|
3095
3736
|
}
|
|
3096
3737
|
function resolvePackagesDir(opts) {
|
|
3097
3738
|
if (opts?.explicit) {
|
|
3098
|
-
const abs =
|
|
3739
|
+
const abs = resolve2(opts.explicit);
|
|
3099
3740
|
if (!isPackagesDir(abs)) {
|
|
3100
3741
|
throw new Error(`packages dir does not look like @s2script stubs: ${abs}`);
|
|
3101
3742
|
}
|
|
@@ -3103,7 +3744,7 @@ function resolvePackagesDir(opts) {
|
|
|
3103
3744
|
}
|
|
3104
3745
|
const env = process.env.S2SCRIPT_PACKAGES_DIR;
|
|
3105
3746
|
if (env) {
|
|
3106
|
-
const abs =
|
|
3747
|
+
const abs = resolve2(env);
|
|
3107
3748
|
if (!isPackagesDir(abs)) {
|
|
3108
3749
|
throw new Error(`S2SCRIPT_PACKAGES_DIR does not look like @s2script stubs: ${abs}`);
|
|
3109
3750
|
}
|
|
@@ -3112,7 +3753,7 @@ function resolvePackagesDir(opts) {
|
|
|
3112
3753
|
const nearCli = findPackagesDirNearCli(opts?.fromCliUrl);
|
|
3113
3754
|
if (nearCli) return nearCli;
|
|
3114
3755
|
if (opts?.pluginDir) {
|
|
3115
|
-
const nm = join(
|
|
3756
|
+
const nm = join(resolve2(opts.pluginDir), "node_modules", "@s2script");
|
|
3116
3757
|
if (isPackagesDir(nm)) return nm;
|
|
3117
3758
|
}
|
|
3118
3759
|
throw new Error(
|
|
@@ -3120,32 +3761,6 @@ function resolvePackagesDir(opts) {
|
|
|
3120
3761
|
);
|
|
3121
3762
|
}
|
|
3122
3763
|
|
|
3123
|
-
// src/tsconfig-shared.ts
|
|
3124
|
-
var sharedCompilerOptionsJson = {
|
|
3125
|
-
strict: true,
|
|
3126
|
-
noEmit: true,
|
|
3127
|
-
moduleResolution: "bundler",
|
|
3128
|
-
module: "ESNext",
|
|
3129
|
-
target: "ES2024",
|
|
3130
|
-
lib: ["ES2024"],
|
|
3131
|
-
types: [],
|
|
3132
|
-
skipLibCheck: true,
|
|
3133
|
-
allowImportingTsExtensions: true
|
|
3134
|
-
};
|
|
3135
|
-
function sharedProgramOptions(tsMod) {
|
|
3136
|
-
return {
|
|
3137
|
-
strict: sharedCompilerOptionsJson.strict,
|
|
3138
|
-
noEmit: sharedCompilerOptionsJson.noEmit,
|
|
3139
|
-
allowImportingTsExtensions: sharedCompilerOptionsJson.allowImportingTsExtensions,
|
|
3140
|
-
moduleResolution: tsMod.ModuleResolutionKind.Bundler,
|
|
3141
|
-
module: tsMod.ModuleKind.ESNext,
|
|
3142
|
-
target: tsMod.ScriptTarget.ES2024,
|
|
3143
|
-
lib: sharedCompilerOptionsJson.lib.map((l2) => `lib.${l2.toLowerCase()}.d.ts`),
|
|
3144
|
-
types: [...sharedCompilerOptionsJson.types],
|
|
3145
|
-
skipLibCheck: sharedCompilerOptionsJson.skipLibCheck
|
|
3146
|
-
};
|
|
3147
|
-
}
|
|
3148
|
-
|
|
3149
3764
|
// src/contracts.ts
|
|
3150
3765
|
import { existsSync as existsSync2 } from "node:fs";
|
|
3151
3766
|
import { join as join2 } from "node:path";
|
|
@@ -3158,7 +3773,7 @@ function localContractPath(pluginDir, dep) {
|
|
|
3158
3773
|
|
|
3159
3774
|
// src/workspace/siblings.ts
|
|
3160
3775
|
import { readFileSync as readFileSync4 } from "node:fs";
|
|
3161
|
-
import { join as join5, relative, resolve as
|
|
3776
|
+
import { join as join5, relative, resolve as resolve5 } from "node:path";
|
|
3162
3777
|
|
|
3163
3778
|
// src/workspace/glob.ts
|
|
3164
3779
|
function toPosix(p) {
|
|
@@ -3191,11 +3806,11 @@ function matchGlob(pattern, path) {
|
|
|
3191
3806
|
}
|
|
3192
3807
|
|
|
3193
3808
|
// src/workspace/workspace.ts
|
|
3194
|
-
import { existsSync as existsSync3, readFileSync, readdirSync, statSync } from "node:fs";
|
|
3195
|
-
import { dirname as dirname2, join as join3, resolve as
|
|
3809
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2, readdirSync, statSync } from "node:fs";
|
|
3810
|
+
import { dirname as dirname2, join as join3, resolve as resolve3 } from "node:path";
|
|
3196
3811
|
function readPackageJson(path) {
|
|
3197
3812
|
try {
|
|
3198
|
-
return JSON.parse(
|
|
3813
|
+
return JSON.parse(readFileSync2(path, "utf8"));
|
|
3199
3814
|
} catch (e) {
|
|
3200
3815
|
throw new Error(`cannot read ${path}: ${e.message}`);
|
|
3201
3816
|
}
|
|
@@ -3204,7 +3819,7 @@ var MARKER_TEXT = /"s2script"[\s\S]*"workspace"\s*:/;
|
|
|
3204
3819
|
function carriesWorkspaceMarker(pkgPath) {
|
|
3205
3820
|
let text3;
|
|
3206
3821
|
try {
|
|
3207
|
-
text3 =
|
|
3822
|
+
text3 = readFileSync2(pkgPath, "utf8");
|
|
3208
3823
|
} catch {
|
|
3209
3824
|
return false;
|
|
3210
3825
|
}
|
|
@@ -3216,7 +3831,7 @@ function carriesWorkspaceMarker(pkgPath) {
|
|
|
3216
3831
|
}
|
|
3217
3832
|
}
|
|
3218
3833
|
function findWorkspaceRoot(from) {
|
|
3219
|
-
let dir =
|
|
3834
|
+
let dir = resolve3(from);
|
|
3220
3835
|
for (; ; ) {
|
|
3221
3836
|
if (carriesWorkspaceMarker(join3(dir, "package.json"))) return dir;
|
|
3222
3837
|
const parent = dirname2(dir);
|
|
@@ -3275,7 +3890,7 @@ function pnpmWorkspaceGlobs(root) {
|
|
|
3275
3890
|
if (!existsSync3(path)) return [];
|
|
3276
3891
|
let body;
|
|
3277
3892
|
try {
|
|
3278
|
-
body =
|
|
3893
|
+
body = readFileSync2(path, "utf8");
|
|
3279
3894
|
} catch {
|
|
3280
3895
|
return [];
|
|
3281
3896
|
}
|
|
@@ -3316,7 +3931,7 @@ function loadWorkspace(root) {
|
|
|
3316
3931
|
return workspace;
|
|
3317
3932
|
}
|
|
3318
3933
|
function readWorkspace(root) {
|
|
3319
|
-
const absRoot =
|
|
3934
|
+
const absRoot = resolve3(root);
|
|
3320
3935
|
const pkg = readPackageJson(join3(absRoot, "package.json"));
|
|
3321
3936
|
const marker = pkg.s2script?.workspace;
|
|
3322
3937
|
if (marker === void 0) {
|
|
@@ -3415,72 +4030,11 @@ function readWorkspace(root) {
|
|
|
3415
4030
|
return { workspace: { root: absRoot, pkg, plugins, libs }, problems };
|
|
3416
4031
|
}
|
|
3417
4032
|
function memberForDir(ws, dir) {
|
|
3418
|
-
const abs = toPosix(
|
|
4033
|
+
const abs = toPosix(resolve3(dir));
|
|
3419
4034
|
for (const m of [...ws.plugins, ...ws.libs]) if (toPosix(m.dir) === abs) return m;
|
|
3420
4035
|
return null;
|
|
3421
4036
|
}
|
|
3422
4037
|
|
|
3423
|
-
// src/publishes.ts
|
|
3424
|
-
import { createHash } from "node:crypto";
|
|
3425
|
-
import { readFileSync as readFileSync2 } from "node:fs";
|
|
3426
|
-
function expandPublishes(authored, pkgName, pkgVersion) {
|
|
3427
|
-
if (authored === void 0 || authored === null) return {};
|
|
3428
|
-
if (typeof authored === "string") {
|
|
3429
|
-
if (authored.trim() !== "self") {
|
|
3430
|
-
throw new Error(
|
|
3431
|
-
`publishes: the only valid string form is "self" (got ${JSON.stringify(authored)}); use the map form to publish a differently-named contract`
|
|
3432
|
-
);
|
|
3433
|
-
}
|
|
3434
|
-
return { [pkgName]: pkgVersion };
|
|
3435
|
-
}
|
|
3436
|
-
if (typeof authored !== "object") {
|
|
3437
|
-
throw new Error(`publishes must be "self" or an object (got ${typeof authored})`);
|
|
3438
|
-
}
|
|
3439
|
-
const out = {};
|
|
3440
|
-
for (const [iface, range] of Object.entries(authored)) {
|
|
3441
|
-
if (typeof range !== "string") {
|
|
3442
|
-
throw new Error(`publishes[${JSON.stringify(iface)}] must be a version range string`);
|
|
3443
|
-
}
|
|
3444
|
-
out[iface] = range;
|
|
3445
|
-
}
|
|
3446
|
-
return out;
|
|
3447
|
-
}
|
|
3448
|
-
function hashContract(typesPath) {
|
|
3449
|
-
return createHash("sha256").update(readFileSync2(typesPath)).digest("hex");
|
|
3450
|
-
}
|
|
3451
|
-
var CONCRETE_SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
3452
|
-
function isConcreteVersion(v) {
|
|
3453
|
-
return CONCRETE_SEMVER.test(v.trim());
|
|
3454
|
-
}
|
|
3455
|
-
function derivePublishes(authored, pkgName, pkgVersion, typesPath) {
|
|
3456
|
-
const expanded = expandPublishes(authored, pkgName, pkgVersion);
|
|
3457
|
-
const names = Object.keys(expanded);
|
|
3458
|
-
if (names.length === 0) return {};
|
|
3459
|
-
if (names.length > 1) {
|
|
3460
|
-
throw new Error(
|
|
3461
|
-
`publishes declares ${names.length} interfaces (${names.join(", ")}) but a plugin ships a single "types" contract, so they would all carry the SAME typesSha256 \u2014 a hash that cannot say which contract is which. Publish one interface per plugin (a per-interface "types" map is not supported).`
|
|
3462
|
-
);
|
|
3463
|
-
}
|
|
3464
|
-
if (typesPath === null) {
|
|
3465
|
-
throw new Error(
|
|
3466
|
-
`publishes is set but no contract .d.ts was resolved \u2014 set "types": "api.d.ts" in package.json`
|
|
3467
|
-
);
|
|
3468
|
-
}
|
|
3469
|
-
for (const [iface, v] of Object.entries(expanded)) {
|
|
3470
|
-
if (!isConcreteVersion(v)) {
|
|
3471
|
-
throw new Error(
|
|
3472
|
-
`publishes[${JSON.stringify(iface)}] = ${JSON.stringify(v)} is a RANGE. Resolving a range against a published contract requires the registry (design spec \xA74.6, \xA710 \u2014 not yet implemented). Use a concrete version for a contract you ship yourself, or "self".`
|
|
3473
|
-
);
|
|
3474
|
-
}
|
|
3475
|
-
}
|
|
3476
|
-
const typesSha256 = hashContract(typesPath);
|
|
3477
|
-
const out = {};
|
|
3478
|
-
for (const name of names) {
|
|
3479
|
-
out[name] = { version: expanded[name].trim(), typesSha256 };
|
|
3480
|
-
}
|
|
3481
|
-
return out;
|
|
3482
|
-
}
|
|
3483
|
-
|
|
3484
4038
|
// src/workspace/interfaces.ts
|
|
3485
4039
|
function where(p) {
|
|
3486
4040
|
return `${p.name} (${p.relDir})`;
|
|
@@ -3523,7 +4077,7 @@ function ambiguityProblem(iface, publishers) {
|
|
|
3523
4077
|
|
|
3524
4078
|
// src/publish-gate.ts
|
|
3525
4079
|
import { existsSync as existsSync4, readFileSync as readFileSync3, statSync as statSync2 } from "node:fs";
|
|
3526
|
-
import { join as join4, resolve as
|
|
4080
|
+
import { join as join4, resolve as resolve4 } from "node:path";
|
|
3527
4081
|
function hasPublishes(publishes) {
|
|
3528
4082
|
if (publishes == null) return false;
|
|
3529
4083
|
if (typeof publishes === "string") return publishes.trim().length > 0;
|
|
@@ -3548,7 +4102,7 @@ function assertPublishesTypes(pkg, pluginDir) {
|
|
|
3548
4102
|
error: `published API must be a .d.ts file (got ${JSON.stringify(typesRel)})`
|
|
3549
4103
|
};
|
|
3550
4104
|
}
|
|
3551
|
-
const typesPath =
|
|
4105
|
+
const typesPath = resolve4(pluginDir, typesRel);
|
|
3552
4106
|
if (!existsSync4(typesPath)) {
|
|
3553
4107
|
return { ok: false, error: `types file not found: ${typesRel}` };
|
|
3554
4108
|
}
|
|
@@ -3579,7 +4133,7 @@ function resolveSiblingContracts(pluginDir, deps) {
|
|
|
3579
4133
|
const siblings = /* @__PURE__ */ new Map();
|
|
3580
4134
|
const shadowedCopies = [];
|
|
3581
4135
|
if (deps.length === 0) return { siblings, shadowedCopies };
|
|
3582
|
-
const absDir =
|
|
4136
|
+
const absDir = resolve5(pluginDir);
|
|
3583
4137
|
const root = findWorkspaceRoot(absDir);
|
|
3584
4138
|
if (root === null) return { siblings, shadowedCopies };
|
|
3585
4139
|
const rootPkg = JSON.parse(readFileSync4(join5(root, "package.json"), "utf8"));
|
|
@@ -3587,7 +4141,7 @@ function resolveSiblingContracts(pluginDir, deps) {
|
|
|
3587
4141
|
const scan = scanWorkspace(root);
|
|
3588
4142
|
const ws = scan.workspace;
|
|
3589
4143
|
const { producers, ambiguous, byPlugin } = indexInterfaces(ws.plugins);
|
|
3590
|
-
const self = ws.plugins.find((p) =>
|
|
4144
|
+
const self = ws.plugins.find((p) => resolve5(p.dir) === absDir);
|
|
3591
4145
|
const label = self?.name ?? toPosix(relative(root, absDir));
|
|
3592
4146
|
const unread = scan.problems ?? [];
|
|
3593
4147
|
const problems = [];
|
|
@@ -3599,7 +4153,7 @@ function resolveSiblingContracts(pluginDir, deps) {
|
|
|
3599
4153
|
}
|
|
3600
4154
|
const producer = producers.get(dep);
|
|
3601
4155
|
if (producer === void 0) {
|
|
3602
|
-
const namesake = ws.plugins.find((p) => p.name === dep &&
|
|
4156
|
+
const namesake = ws.plugins.find((p) => p.name === dep && resolve5(p.dir) !== absDir);
|
|
3603
4157
|
if (namesake !== void 0 && (byPlugin.get(namesake)?.length ?? 0) === 0) {
|
|
3604
4158
|
problems.push(
|
|
3605
4159
|
`${dep} (workspace sibling at ${namesake.relDir}) declares no s2script.publishes \u2014 you cannot depend on an interface nobody publishes. The engine resolves a dependency name through iface_published (loader.rs:175), which is never true for a plugin that publishes nothing, so this consumer would park in "waiting" forever and every ctx.use would throw InterfaceUnavailable. Add s2script.publishes and "types" to ${namesake.relDir}/package.json.`
|
|
@@ -3614,7 +4168,7 @@ function resolveSiblingContracts(pluginDir, deps) {
|
|
|
3614
4168
|
continue;
|
|
3615
4169
|
}
|
|
3616
4170
|
const sib = producer.plugin;
|
|
3617
|
-
if (
|
|
4171
|
+
if (resolve5(sib.dir) === absDir) continue;
|
|
3618
4172
|
const gate = assertPublishesTypes(sib.pkg, sib.dir);
|
|
3619
4173
|
if (!gate.ok || gate.typesPath === null) {
|
|
3620
4174
|
const why = gate.ok ? "it declares no s2script.publishes" : gate.error;
|
|
@@ -3637,7 +4191,7 @@ function resolveSiblingContracts(pluginDir, deps) {
|
|
|
3637
4191
|
|
|
3638
4192
|
// src/libraries.ts
|
|
3639
4193
|
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "node:fs";
|
|
3640
|
-
import { join as join6, resolve as
|
|
4194
|
+
import { join as join6, resolve as resolve6 } from "node:path";
|
|
3641
4195
|
function packageKind(pkg) {
|
|
3642
4196
|
const k = pkg.s2script?.kind ?? "plugin";
|
|
3643
4197
|
if (k !== "plugin" && k !== "library") {
|
|
@@ -3655,7 +4209,7 @@ function localLibraryDir(pluginDir, name) {
|
|
|
3655
4209
|
return existsSync5(join6(p, "index.d.ts")) ? p : null;
|
|
3656
4210
|
}
|
|
3657
4211
|
function resolveLibrarySibling(pluginDir, name) {
|
|
3658
|
-
const absDir =
|
|
4212
|
+
const absDir = resolve6(pluginDir);
|
|
3659
4213
|
const root = findWorkspaceRoot(absDir);
|
|
3660
4214
|
if (root === null) return null;
|
|
3661
4215
|
const scan = scanWorkspace(root);
|
|
@@ -3690,7 +4244,7 @@ function resolveLibrarySibling(pluginDir, name) {
|
|
|
3690
4244
|
`library ${name} (workspace sibling at ${lib.relDir}) has no "types" in its package.json`
|
|
3691
4245
|
);
|
|
3692
4246
|
}
|
|
3693
|
-
const typesPath =
|
|
4247
|
+
const typesPath = resolve6(lib.dir, typesRel);
|
|
3694
4248
|
if (!existsSync5(typesPath)) {
|
|
3695
4249
|
throw new Error(
|
|
3696
4250
|
`library ${name} (workspace sibling at ${lib.relDir}) types file not found: ${typesRel}`
|
|
@@ -3783,18 +4337,22 @@ function declaredModules(dtsFiles) {
|
|
|
3783
4337
|
const out = /* @__PURE__ */ new Set();
|
|
3784
4338
|
for (const f of dtsFiles) {
|
|
3785
4339
|
const body = readFileSync6(f, "utf8");
|
|
3786
|
-
for (const m of body.matchAll(/declare\s+module\s+["']([^"']+)["']/g))
|
|
4340
|
+
for (const m of body.matchAll(/declare\s+module\s+["']([^"']+)["']/g))
|
|
4341
|
+
out.add(m[1]);
|
|
3787
4342
|
}
|
|
3788
4343
|
return out;
|
|
3789
4344
|
}
|
|
3790
4345
|
function typecheckPlugin(pluginDir, opts) {
|
|
3791
|
-
const absDir =
|
|
3792
|
-
const packagesDir = opts?.packagesDir ?
|
|
4346
|
+
const absDir = resolve7(pluginDir);
|
|
4347
|
+
const packagesDir = opts?.packagesDir ? resolve7(opts.packagesDir) : resolvePackagesDir({ pluginDir: absDir });
|
|
3793
4348
|
const pkg = JSON.parse(readFileSync6(join7(absDir, "package.json"), "utf8"));
|
|
3794
4349
|
const s2 = pkg.s2script ?? {};
|
|
3795
4350
|
const entryRel = s2.main ?? pkg.main;
|
|
3796
|
-
if (!entryRel)
|
|
3797
|
-
|
|
4351
|
+
if (!entryRel)
|
|
4352
|
+
throw new Error(
|
|
4353
|
+
`typecheckPlugin: no entry point in ${join7(absDir, "package.json")}`
|
|
4354
|
+
);
|
|
4355
|
+
const entry = resolve7(absDir, entryRel);
|
|
3798
4356
|
const isAlwaysResolved = (d) => d === "@s2script/sdk" || d.startsWith("@s2script/sdk/") || d === "@s2script/cs2" || d.startsWith("@s2script/cs2/");
|
|
3799
4357
|
const localDts = localDeclarationFiles(absDir);
|
|
3800
4358
|
const locallyDeclared = declaredModules(localDts);
|
|
@@ -3803,7 +4361,7 @@ function typecheckPlugin(pluginDir, opts) {
|
|
|
3803
4361
|
...Object.keys(s2.optionalPluginDependencies ?? {})
|
|
3804
4362
|
];
|
|
3805
4363
|
const { siblings } = resolveSiblingContracts(absDir, allDeclaredDeps);
|
|
3806
|
-
const contractPaths =
|
|
4364
|
+
const contractPaths = /* @__PURE__ */ Object.create(null);
|
|
3807
4365
|
for (const d of allDeclaredDeps) {
|
|
3808
4366
|
const sibling = siblings.get(d);
|
|
3809
4367
|
if (sibling !== void 0) {
|
|
@@ -3813,6 +4371,53 @@ function typecheckPlugin(pluginDir, opts) {
|
|
|
3813
4371
|
const p = localContractPath(absDir, d);
|
|
3814
4372
|
if (p !== null) contractPaths[d] = [p];
|
|
3815
4373
|
}
|
|
4374
|
+
const protocol2 = s2.interfaceProtocol === 2;
|
|
4375
|
+
const authoritativePaths = Object.fromEntries(
|
|
4376
|
+
Object.entries(contractPaths).map(([name, paths]) => [name, paths[0]])
|
|
4377
|
+
);
|
|
4378
|
+
const ownNames = new Set(
|
|
4379
|
+
Object.keys(expandPublishes(s2.publishes, pkg.name, pkg.version))
|
|
4380
|
+
);
|
|
4381
|
+
if (protocol2 && pkg.types && ownNames.size === 0) ownNames.add(pkg.name);
|
|
4382
|
+
if (protocol2 && pkg.types)
|
|
4383
|
+
for (const name of ownNames)
|
|
4384
|
+
authoritativePaths[name] = resolve7(absDir, pkg.types);
|
|
4385
|
+
const interfaceContracts = /* @__PURE__ */ Object.create(null);
|
|
4386
|
+
if (protocol2) {
|
|
4387
|
+
try {
|
|
4388
|
+
for (const dep of allDeclaredDeps)
|
|
4389
|
+
if (!authoritativePaths[dep])
|
|
4390
|
+
throw new Error(
|
|
4391
|
+
`InterfaceContractError: ${dep} has no verified contract copy; run s2s add`
|
|
4392
|
+
);
|
|
4393
|
+
for (const [name, path] of Object.entries(authoritativePaths)) {
|
|
4394
|
+
interfaceContracts[name] = extractContract(path, packagesDir);
|
|
4395
|
+
if (!siblings.has(name) && !ownNames.has(name)) {
|
|
4396
|
+
const receipt = join7(dirname3(path), "package.json");
|
|
4397
|
+
if (existsSync6(receipt)) {
|
|
4398
|
+
const published = JSON.parse(readFileSync6(receipt, "utf8")).s2script?.publishes?.[name];
|
|
4399
|
+
if (!published || published.typesSha256 !== hashContract(path) || published.contract?.sha256 !== interfaceContracts[name].sha256)
|
|
4400
|
+
throw new Error(
|
|
4401
|
+
`InterfaceContractError: ${name} verified copy hash drift; run s2s add`
|
|
4402
|
+
);
|
|
4403
|
+
}
|
|
4404
|
+
}
|
|
4405
|
+
}
|
|
4406
|
+
} catch (e) {
|
|
4407
|
+
return {
|
|
4408
|
+
ok: false,
|
|
4409
|
+
diagnostics: [
|
|
4410
|
+
{
|
|
4411
|
+
file: entry,
|
|
4412
|
+
line: 1,
|
|
4413
|
+
col: 1,
|
|
4414
|
+
code: 92001,
|
|
4415
|
+
message: e.message
|
|
4416
|
+
}
|
|
4417
|
+
]
|
|
4418
|
+
};
|
|
4419
|
+
}
|
|
4420
|
+
}
|
|
3816
4421
|
const libraryPaths = resolveLibraries(absDir, s2.libraries ?? {}).paths;
|
|
3817
4422
|
const libraryNames = new Set(Object.keys(s2.libraries ?? {}));
|
|
3818
4423
|
const deps = [
|
|
@@ -3826,7 +4431,7 @@ function typecheckPlugin(pluginDir, opts) {
|
|
|
3826
4431
|
const options = {
|
|
3827
4432
|
// Accept explicit `.ts` import extensions (node type-stripping requires them for source-to-source
|
|
3828
4433
|
// imports; esbuild strips them at bundle time). Backward-compatible — extensionless imports still resolve.
|
|
3829
|
-
...sharedProgramOptions(
|
|
4434
|
+
...sharedProgramOptions(ts3),
|
|
3830
4435
|
baseUrl: packagesDir,
|
|
3831
4436
|
paths: {
|
|
3832
4437
|
// Builtins: the root barrel `@s2script/sdk` → packages/sdk/index.d.ts; subpaths
|
|
@@ -3854,16 +4459,60 @@ function typecheckPlugin(pluginDir, opts) {
|
|
|
3854
4459
|
];
|
|
3855
4460
|
const tmp = mkdtempSync(join7(tmpdir(), "s2tc-"));
|
|
3856
4461
|
try {
|
|
3857
|
-
|
|
4462
|
+
const generatedPath = join7(absDir, ".s2script", "interfaces.d.ts");
|
|
4463
|
+
rmSync(generatedPath, { force: true });
|
|
4464
|
+
if (protocol2) {
|
|
4465
|
+
mkdirSync(join7(absDir, ".s2script"), { recursive: true });
|
|
4466
|
+
let moduleId = 0;
|
|
4467
|
+
for (const name of allDeclaredDeps) {
|
|
4468
|
+
const path = authoritativePaths[name], wrapper = join7(tmp, `contract-${moduleId++}.d.ts`);
|
|
4469
|
+
writeFileSync(
|
|
4470
|
+
wrapper,
|
|
4471
|
+
`import type {Contract} from ${JSON.stringify(
|
|
4472
|
+
path
|
|
4473
|
+
)};
|
|
4474
|
+
export * from ${JSON.stringify(path)};
|
|
4475
|
+
export declare const on: import("@s2script/sdk/interfaces").TypedInterfaceHandle<Contract>["on"];
|
|
4476
|
+
` + Object.keys(interfaceContracts[name].metadata.methods).map(
|
|
4477
|
+
(method) => `export declare const ${method}: Contract["methods"][${JSON.stringify(
|
|
4478
|
+
method
|
|
4479
|
+
)}];`
|
|
4480
|
+
).join("\n")
|
|
4481
|
+
);
|
|
4482
|
+
options.paths[name] = [wrapper];
|
|
4483
|
+
}
|
|
4484
|
+
writeFileSync(
|
|
4485
|
+
generatedPath,
|
|
4486
|
+
'import "@s2script/sdk/interfaces";\n' + Object.entries(authoritativePaths).map(
|
|
4487
|
+
([name, path], i2) => `import type { Contract as C${i2} } from ${JSON.stringify(
|
|
4488
|
+
path
|
|
4489
|
+
)};`
|
|
4490
|
+
).join("\n") + '\ndeclare module "@s2script/sdk/interfaces" { interface InterfaceContracts {\n' + Object.keys(authoritativePaths).map((name, i2) => `${JSON.stringify(name)}: C${i2};`).join("\n") + "\n} }\n"
|
|
4491
|
+
);
|
|
4492
|
+
rootNames.push(generatedPath);
|
|
4493
|
+
}
|
|
4494
|
+
if (deps.length && !protocol2) {
|
|
3858
4495
|
const stub = join7(tmp, "ambient.d.ts");
|
|
3859
|
-
writeFileSync(
|
|
4496
|
+
writeFileSync(
|
|
4497
|
+
stub,
|
|
4498
|
+
deps.map((d) => `declare module ${JSON.stringify(d)};`).join("\n") + "\n"
|
|
4499
|
+
);
|
|
3860
4500
|
rootNames.push(stub);
|
|
3861
4501
|
}
|
|
3862
|
-
const program =
|
|
4502
|
+
const program = ts3.createProgram(rootNames, options);
|
|
3863
4503
|
const diags = [
|
|
3864
4504
|
...program.getSyntacticDiagnostics(),
|
|
3865
4505
|
...program.getSemanticDiagnostics(),
|
|
3866
|
-
...program.getGlobalDiagnostics()
|
|
4506
|
+
...program.getGlobalDiagnostics(),
|
|
4507
|
+
...protocol2 ? checkInteropCalls(
|
|
4508
|
+
program,
|
|
4509
|
+
authoritativePaths,
|
|
4510
|
+
ownNames,
|
|
4511
|
+
generatedPath,
|
|
4512
|
+
absDir,
|
|
4513
|
+
new Set(allDeclaredDeps),
|
|
4514
|
+
new Set(Object.keys(s2.optionalPluginDependencies ?? {}))
|
|
4515
|
+
) : []
|
|
3867
4516
|
];
|
|
3868
4517
|
const out = diags.map((d) => {
|
|
3869
4518
|
let file = "?", line = 0, col = 0;
|
|
@@ -3873,9 +4522,21 @@ function typecheckPlugin(pluginDir, opts) {
|
|
|
3873
4522
|
line = lc2.line + 1;
|
|
3874
4523
|
col = lc2.character + 1;
|
|
3875
4524
|
}
|
|
3876
|
-
return {
|
|
4525
|
+
return {
|
|
4526
|
+
file,
|
|
4527
|
+
line,
|
|
4528
|
+
col,
|
|
4529
|
+
code: d.code,
|
|
4530
|
+
message: ts3.flattenDiagnosticMessageText(d.messageText, "\n")
|
|
4531
|
+
};
|
|
3877
4532
|
});
|
|
3878
|
-
return {
|
|
4533
|
+
return {
|
|
4534
|
+
ok: out.length === 0,
|
|
4535
|
+
diagnostics: out,
|
|
4536
|
+
program,
|
|
4537
|
+
interfaceContracts,
|
|
4538
|
+
contractPaths: authoritativePaths
|
|
4539
|
+
};
|
|
3879
4540
|
} finally {
|
|
3880
4541
|
rmSync(tmp, { recursive: true, force: true });
|
|
3881
4542
|
}
|
|
@@ -3982,15 +4643,15 @@ function validateDecl(full, decl, typeStr, errs) {
|
|
|
3982
4643
|
}
|
|
3983
4644
|
|
|
3984
4645
|
// src/api-version.ts
|
|
3985
|
-
var HOST_API_VERSION_MAJOR =
|
|
4646
|
+
var HOST_API_VERSION_MAJOR = 3;
|
|
3986
4647
|
var STAMPED_API_VERSION = `${HOST_API_VERSION_MAJOR}.x`;
|
|
3987
4648
|
|
|
3988
4649
|
// src/npm-deps-gate.ts
|
|
3989
4650
|
import { lstatSync, realpathSync } from "node:fs";
|
|
3990
|
-
import { dirname as
|
|
4651
|
+
import { dirname as dirname4, join as join8, resolve as resolve8, sep } from "node:path";
|
|
3991
4652
|
function findInNodeModules(startDir, dep) {
|
|
3992
4653
|
const segs = dep.split("/");
|
|
3993
|
-
let dir =
|
|
4654
|
+
let dir = resolve8(startDir);
|
|
3994
4655
|
for (; ; ) {
|
|
3995
4656
|
const candidate = join8(dir, "node_modules", ...segs);
|
|
3996
4657
|
try {
|
|
@@ -3998,7 +4659,7 @@ function findInNodeModules(startDir, dep) {
|
|
|
3998
4659
|
return candidate;
|
|
3999
4660
|
} catch {
|
|
4000
4661
|
}
|
|
4001
|
-
const parent =
|
|
4662
|
+
const parent = dirname4(dir);
|
|
4002
4663
|
if (parent === dir) return null;
|
|
4003
4664
|
dir = parent;
|
|
4004
4665
|
}
|
|
@@ -4033,13 +4694,13 @@ function assertNoNpmRuntimeDeps(pkg, pluginDir) {
|
|
|
4033
4694
|
|
|
4034
4695
|
// src/build-library.ts
|
|
4035
4696
|
import * as esbuild from "esbuild";
|
|
4036
|
-
import { readFileSync as readFileSync7, writeFileSync as writeFileSync2, mkdirSync, existsSync as existsSync8, statSync as statSync3 } from "node:fs";
|
|
4037
|
-
import { resolve as
|
|
4697
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync8, statSync as statSync3 } from "node:fs";
|
|
4698
|
+
import { resolve as resolve10, join as join10 } from "node:path";
|
|
4038
4699
|
|
|
4039
4700
|
// src/lint/lint.ts
|
|
4040
4701
|
import { ESLint } from "eslint";
|
|
4041
4702
|
import { existsSync as existsSync7, readdirSync as readdirSync3 } from "node:fs";
|
|
4042
|
-
import { dirname as
|
|
4703
|
+
import { dirname as dirname5, join as join9, resolve as resolve9 } from "node:path";
|
|
4043
4704
|
import s2lint from "@s2script/eslint-plugin";
|
|
4044
4705
|
var CONFIG_FILES = ["eslint.config.js", "eslint.config.mjs", "eslint.config.cjs", "eslint.config.ts"];
|
|
4045
4706
|
function ownConfigDir(pluginDir) {
|
|
@@ -4048,7 +4709,7 @@ function ownConfigDir(pluginDir) {
|
|
|
4048
4709
|
for (; ; ) {
|
|
4049
4710
|
if (CONFIG_FILES.some((f) => existsSync7(join9(dir, f)))) return dir;
|
|
4050
4711
|
if (stopAt === null || dir === stopAt) return null;
|
|
4051
|
-
const parent =
|
|
4712
|
+
const parent = dirname5(dir);
|
|
4052
4713
|
if (parent === dir) return null;
|
|
4053
4714
|
dir = parent;
|
|
4054
4715
|
}
|
|
@@ -4070,7 +4731,7 @@ function collectTsFiles(dir) {
|
|
|
4070
4731
|
return out;
|
|
4071
4732
|
}
|
|
4072
4733
|
async function lintPlugin(pluginDir, program) {
|
|
4073
|
-
const absDir =
|
|
4734
|
+
const absDir = resolve9(pluginDir);
|
|
4074
4735
|
const configDir = ownConfigDir(absDir);
|
|
4075
4736
|
const hasOwnConfig = configDir !== null;
|
|
4076
4737
|
const eslint = hasOwnConfig ? new ESLint({ cwd: configDir, errorOnUnmatchedPattern: false, warnIgnored: false }) : new ESLint({
|
|
@@ -4091,7 +4752,7 @@ async function lintPlugin(pluginDir, program) {
|
|
|
4091
4752
|
|
|
4092
4753
|
// src/build-library.ts
|
|
4093
4754
|
async function buildLibrary(dir, packagesDir) {
|
|
4094
|
-
const absDir =
|
|
4755
|
+
const absDir = resolve10(dir);
|
|
4095
4756
|
const pkgPath = join10(absDir, "package.json");
|
|
4096
4757
|
const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
|
|
4097
4758
|
const s2 = pkg.s2script ?? {};
|
|
@@ -4107,7 +4768,7 @@ async function buildLibrary(dir, packagesDir) {
|
|
|
4107
4768
|
if (typeof typesRel !== "string" || !typesRel.endsWith(".d.ts")) {
|
|
4108
4769
|
throw new Error('a library must set "types" to a .d.ts \u2014 consumers vendor it to typecheck against');
|
|
4109
4770
|
}
|
|
4110
|
-
const typesPath =
|
|
4771
|
+
const typesPath = resolve10(absDir, typesRel);
|
|
4111
4772
|
if (!existsSync8(typesPath) || !statSync3(typesPath).isFile() || statSync3(typesPath).size === 0) {
|
|
4112
4773
|
throw new Error(`types file is missing or empty: ${typesRel}`);
|
|
4113
4774
|
}
|
|
@@ -4154,7 +4815,7 @@ ${lint.output}`);
|
|
|
4154
4815
|
};
|
|
4155
4816
|
const sanitized = String(pkg.name).replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
4156
4817
|
const outDir = join10(absDir, "dist");
|
|
4157
|
-
|
|
4818
|
+
mkdirSync2(outDir, { recursive: true });
|
|
4158
4819
|
const outPath = join10(outDir, `${sanitized}.s2lib`);
|
|
4159
4820
|
writeFileSync2(
|
|
4160
4821
|
outPath,
|
|
@@ -4167,78 +4828,6 @@ ${lint.output}`);
|
|
|
4167
4828
|
return outPath;
|
|
4168
4829
|
}
|
|
4169
4830
|
|
|
4170
|
-
// src/publish-scan.ts
|
|
4171
|
-
import ts2 from "typescript";
|
|
4172
|
-
function isPluginContext(type) {
|
|
4173
|
-
const sym = type.getSymbol() ?? type.aliasSymbol;
|
|
4174
|
-
return sym?.getName() === "PluginContext";
|
|
4175
|
-
}
|
|
4176
|
-
function isFromPluginDts(checker, node) {
|
|
4177
|
-
let sym = checker.getSymbolAtLocation(node);
|
|
4178
|
-
if (sym === void 0) return false;
|
|
4179
|
-
if (sym.flags & ts2.SymbolFlags.Alias) {
|
|
4180
|
-
const aliased = checker.getAliasedSymbol(sym);
|
|
4181
|
-
if (aliased !== void 0) sym = aliased;
|
|
4182
|
-
}
|
|
4183
|
-
for (const d of sym.declarations ?? []) {
|
|
4184
|
-
const f = d.getSourceFile().fileName.replace(/\\/g, "/");
|
|
4185
|
-
if (f.endsWith("/plugin.d.ts")) return true;
|
|
4186
|
-
}
|
|
4187
|
-
return false;
|
|
4188
|
-
}
|
|
4189
|
-
function recordPublishOrUse(out, method, arg0, sf, node) {
|
|
4190
|
-
if (method === "publish") {
|
|
4191
|
-
if (arg0 !== void 0 && ts2.isStringLiteralLike(arg0)) {
|
|
4192
|
-
out.publishNames.push(arg0.text);
|
|
4193
|
-
} else {
|
|
4194
|
-
const { line } = sf.getLineAndCharacterOfPosition(node.getStart());
|
|
4195
|
-
out.dynamicPublishSites.push(`${sf.fileName}:${line + 1}`);
|
|
4196
|
-
}
|
|
4197
|
-
} else if (arg0 !== void 0 && ts2.isStringLiteralLike(arg0)) {
|
|
4198
|
-
out.useNames.push(arg0.text);
|
|
4199
|
-
}
|
|
4200
|
-
}
|
|
4201
|
-
function recordImportSpecifier(out, spec) {
|
|
4202
|
-
if (spec.startsWith(".") || spec.startsWith("@s2script/")) return;
|
|
4203
|
-
out.importNames.push(spec);
|
|
4204
|
-
}
|
|
4205
|
-
function scanPluginProgram(program, pluginDir) {
|
|
4206
|
-
const checker = program.getTypeChecker();
|
|
4207
|
-
const out = { publishNames: [], dynamicPublishSites: [], useNames: [], importNames: [] };
|
|
4208
|
-
const dirPrefix = pluginDir.replace(/\\/g, "/").replace(/\/+$/, "") + "/";
|
|
4209
|
-
for (const sf of program.getSourceFiles()) {
|
|
4210
|
-
if (sf.isDeclarationFile) continue;
|
|
4211
|
-
if (!sf.fileName.replace(/\\/g, "/").startsWith(dirPrefix)) continue;
|
|
4212
|
-
const visit = (node) => {
|
|
4213
|
-
if ((ts2.isImportDeclaration(node) || ts2.isExportDeclaration(node)) && node.moduleSpecifier !== void 0 && ts2.isStringLiteralLike(node.moduleSpecifier)) {
|
|
4214
|
-
recordImportSpecifier(out, node.moduleSpecifier.text);
|
|
4215
|
-
}
|
|
4216
|
-
if (ts2.isCallExpression(node)) {
|
|
4217
|
-
if (ts2.isPropertyAccessExpression(node.expression)) {
|
|
4218
|
-
const method = node.expression.name.text;
|
|
4219
|
-
if (method === "publish" || method === "use" || method === "tryUse") {
|
|
4220
|
-
const recv = checker.getTypeAtLocation(node.expression.expression);
|
|
4221
|
-
if (isPluginContext(recv)) {
|
|
4222
|
-
recordPublishOrUse(out, method, node.arguments[0], sf, node);
|
|
4223
|
-
}
|
|
4224
|
-
}
|
|
4225
|
-
} else if (ts2.isIdentifier(node.expression)) {
|
|
4226
|
-
const name = node.expression.text;
|
|
4227
|
-
if ((name === "publish" || name === "use" || name === "tryUse") && isFromPluginDts(checker, node.expression)) {
|
|
4228
|
-
recordPublishOrUse(out, name, node.arguments[0], sf, node);
|
|
4229
|
-
}
|
|
4230
|
-
}
|
|
4231
|
-
}
|
|
4232
|
-
ts2.forEachChild(node, visit);
|
|
4233
|
-
};
|
|
4234
|
-
visit(sf);
|
|
4235
|
-
}
|
|
4236
|
-
out.publishNames = [...new Set(out.publishNames)];
|
|
4237
|
-
out.useNames = [...new Set(out.useNames)];
|
|
4238
|
-
out.importNames = [...new Set(out.importNames)];
|
|
4239
|
-
return out;
|
|
4240
|
-
}
|
|
4241
|
-
|
|
4242
4831
|
// src/gamedata/types.ts
|
|
4243
4832
|
var PLATFORM = "linuxsteamrt64";
|
|
4244
4833
|
var ARG_KINDS = ["bool", "int", "float", "string", "utlstring", "vector", "entity"];
|
|
@@ -4744,7 +5333,7 @@ function stripJsonComments(src) {
|
|
|
4744
5333
|
|
|
4745
5334
|
// src/build.ts
|
|
4746
5335
|
async function buildPlugin(dir, packagesDir) {
|
|
4747
|
-
const absDir =
|
|
5336
|
+
const absDir = resolve11(dir);
|
|
4748
5337
|
const pkgPath = join11(absDir, "package.json");
|
|
4749
5338
|
const pkg = JSON.parse(readFileSync8(pkgPath, "utf8"));
|
|
4750
5339
|
if (packageKind(pkg) === "library") {
|
|
@@ -4774,7 +5363,7 @@ async function buildPlugin(dir, packagesDir) {
|
|
|
4774
5363
|
const permissions = Array.isArray(s2.permissions) ? s2.permissions : [];
|
|
4775
5364
|
let gamedata;
|
|
4776
5365
|
if (typeof s2.gamedata === "string") {
|
|
4777
|
-
const gdPath =
|
|
5366
|
+
const gdPath = resolve11(absDir, s2.gamedata);
|
|
4778
5367
|
const dirPrefix = absDir.endsWith(sep2) ? absDir : absDir + sep2;
|
|
4779
5368
|
if (gdPath !== absDir && !gdPath.startsWith(dirPrefix)) {
|
|
4780
5369
|
throw new Error(
|
|
@@ -4799,7 +5388,7 @@ async function buildPlugin(dir, packagesDir) {
|
|
|
4799
5388
|
if (gdErrs.length) throw new Error(`invalid gamedata:
|
|
4800
5389
|
${gdErrs.join("\n ")}`);
|
|
4801
5390
|
const genDir = join11(absDir, ".s2script");
|
|
4802
|
-
|
|
5391
|
+
mkdirSync3(genDir, { recursive: true });
|
|
4803
5392
|
writeFileSync3(join11(genDir, "gamedata.d.ts"), generateGamedataTypes(gamedata));
|
|
4804
5393
|
if (gamedata.hooks && Object.keys(gamedata.hooks).length) {
|
|
4805
5394
|
writeFileSync3(join11(genDir, "hooks.d.ts"), generateHookTypes(gamedata));
|
|
@@ -4935,9 +5524,18 @@ ${lint.output}`);
|
|
|
4935
5524
|
if (Object.keys(derivedPublishes).length > 0) {
|
|
4936
5525
|
manifest.publishes = derivedPublishes;
|
|
4937
5526
|
}
|
|
5527
|
+
if (s2.interfaceProtocol === 2) {
|
|
5528
|
+
manifest.interfaceProtocol = 2;
|
|
5529
|
+
const imports = /* @__PURE__ */ Object.create(null);
|
|
5530
|
+
for (const [name2, contract] of Object.entries(tc.interfaceContracts ?? {})) {
|
|
5531
|
+
if (derivedPublishes[name2]) Object.assign(derivedPublishes[name2], { contract });
|
|
5532
|
+
if (declaredDeps.has(name2)) imports[name2] = contract;
|
|
5533
|
+
}
|
|
5534
|
+
manifest.interfaceContracts = imports;
|
|
5535
|
+
}
|
|
4938
5536
|
if (config !== void 0) manifest.config = config;
|
|
4939
5537
|
if (permissions.length > 0) manifest.permissions = permissions;
|
|
4940
|
-
const compiledAgainst =
|
|
5538
|
+
const compiledAgainst = /* @__PURE__ */ Object.create(null);
|
|
4941
5539
|
for (const dep of [
|
|
4942
5540
|
...Object.keys(pluginDependencies),
|
|
4943
5541
|
...Object.keys(optionalPluginDependencies)
|
|
@@ -4965,13 +5563,13 @@ ${lint.output}`);
|
|
|
4965
5563
|
}
|
|
4966
5564
|
const sanitizedId = name.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
4967
5565
|
const outDir = join11(absDir, "dist");
|
|
4968
|
-
|
|
5566
|
+
mkdirSync3(outDir, { recursive: true });
|
|
4969
5567
|
const outPath = join11(outDir, `${sanitizedId}.s2sp`);
|
|
4970
5568
|
writeFileSync3(outPath, zipSync(zipFiles));
|
|
4971
5569
|
return outPath;
|
|
4972
5570
|
}
|
|
4973
5571
|
async function buildPackage(dir, packagesDir) {
|
|
4974
|
-
const pkg = JSON.parse(readFileSync8(join11(
|
|
5572
|
+
const pkg = JSON.parse(readFileSync8(join11(resolve11(dir), "package.json"), "utf8"));
|
|
4975
5573
|
return packageKind(pkg) === "library" ? buildLibrary(dir, packagesDir) : buildPlugin(dir, packagesDir);
|
|
4976
5574
|
}
|
|
4977
5575
|
|
|
@@ -6619,12 +7217,12 @@ async function run(argv2) {
|
|
|
6619
7217
|
const filters = collectFilters(argv2);
|
|
6620
7218
|
const stampVersion = parseFlag(argv2, "--stamp-version");
|
|
6621
7219
|
let dir = positionals(argv2, VALUE_FLAGS)[0];
|
|
6622
|
-
const wsRoot = findWorkspaceRoot(dir === void 0 ? process.cwd() :
|
|
6623
|
-
const workspaceMode = wsRoot !== null && (dir === void 0 ||
|
|
7220
|
+
const wsRoot = findWorkspaceRoot(dir === void 0 ? process.cwd() : resolve12(dir));
|
|
7221
|
+
const workspaceMode = wsRoot !== null && (dir === void 0 || resolve12(dir) === wsRoot);
|
|
6624
7222
|
if (!workspaceMode && (filters.length > 0 || stampVersion !== void 0)) {
|
|
6625
7223
|
const flag = filters.length > 0 ? "--filter" : "--stamp-version";
|
|
6626
7224
|
throw new Error(
|
|
6627
|
-
wsRoot === null ? `${flag} is a workspace flag, but no workspace root was found above ${dir === void 0 ? process.cwd() :
|
|
7225
|
+
wsRoot === null ? `${flag} is a workspace flag, but no workspace root was found above ${dir === void 0 ? process.cwd() : resolve12(dir)} \u2014 a workspace root is a package.json carrying an "s2script": { "workspace": { "plugins": [...] } } block` : `${flag} narrows a WORKSPACE build, but ${dir} names a single plugin \u2014 drop the argument (or pass ${wsRoot}) to build the workspace`
|
|
6628
7226
|
);
|
|
6629
7227
|
}
|
|
6630
7228
|
if (wsRoot !== null && workspaceMode) {
|
|
@@ -6642,7 +7240,7 @@ async function run(argv2) {
|
|
|
6642
7240
|
}
|
|
6643
7241
|
const packagesDir = resolvePackagesDir({
|
|
6644
7242
|
explicit: parseFlag(argv2, "--packages-dir"),
|
|
6645
|
-
pluginDir:
|
|
7243
|
+
pluginDir: resolve12(dir),
|
|
6646
7244
|
fromCliUrl: import.meta.url
|
|
6647
7245
|
});
|
|
6648
7246
|
const out = await task(`Building ${dir}`, () => buildPackage(dir, packagesDir), {
|
|
@@ -6702,11 +7300,11 @@ async function runWorkspace(argv2, root, interactive, filters, stampVersion) {
|
|
|
6702
7300
|
}
|
|
6703
7301
|
|
|
6704
7302
|
// src/commands/deploy.ts
|
|
6705
|
-
import { resolve as
|
|
7303
|
+
import { resolve as resolve14 } from "node:path";
|
|
6706
7304
|
|
|
6707
7305
|
// src/registry/deploy.ts
|
|
6708
7306
|
import { readFileSync as readFileSync11 } from "node:fs";
|
|
6709
|
-
import { resolve as
|
|
7307
|
+
import { resolve as resolve13 } from "node:path";
|
|
6710
7308
|
|
|
6711
7309
|
// src/types-pack.ts
|
|
6712
7310
|
import { readFileSync as readFileSync9 } from "node:fs";
|
|
@@ -6770,7 +7368,7 @@ function packTypesTarball(input) {
|
|
|
6770
7368
|
}
|
|
6771
7369
|
|
|
6772
7370
|
// src/registry/credentials.ts
|
|
6773
|
-
import { existsSync as existsSync9, mkdirSync as
|
|
7371
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "node:fs";
|
|
6774
7372
|
import { homedir } from "node:os";
|
|
6775
7373
|
import { join as join13 } from "node:path";
|
|
6776
7374
|
function credentialsPath() {
|
|
@@ -6806,7 +7404,7 @@ function loadCredentials() {
|
|
|
6806
7404
|
}
|
|
6807
7405
|
function saveCredentials(creds) {
|
|
6808
7406
|
const dir = join13(homedir(), ".s2script");
|
|
6809
|
-
|
|
7407
|
+
mkdirSync4(dir, { recursive: true });
|
|
6810
7408
|
writeFileSync5(
|
|
6811
7409
|
credentialsPath(),
|
|
6812
7410
|
JSON.stringify(
|
|
@@ -6999,7 +7597,7 @@ function assertDeployable(pkg, label) {
|
|
|
6999
7597
|
}
|
|
7000
7598
|
}
|
|
7001
7599
|
function assembleDeployArchive(pluginDir, pkg, outPath) {
|
|
7002
|
-
const absDir =
|
|
7600
|
+
const absDir = resolve13(pluginDir);
|
|
7003
7601
|
if (packageKind(pkg) === "library") {
|
|
7004
7602
|
const lib = readFileSync11(outPath);
|
|
7005
7603
|
const manifestEntry2 = unzipSync(lib)["manifest.json"];
|
|
@@ -7029,8 +7627,8 @@ function assembleDeployArchive(pluginDir, pkg, outPath) {
|
|
|
7029
7627
|
return { manifest, s2sp, lib: null, types };
|
|
7030
7628
|
}
|
|
7031
7629
|
async function deployPlugin(opts) {
|
|
7032
|
-
const absDir =
|
|
7033
|
-
const pkg = JSON.parse(readFileSync11(
|
|
7630
|
+
const absDir = resolve13(opts.dir);
|
|
7631
|
+
const pkg = JSON.parse(readFileSync11(resolve13(absDir, "package.json"), "utf8"));
|
|
7034
7632
|
assertDeployable(pkg, absDir);
|
|
7035
7633
|
const creds = loadCredentials();
|
|
7036
7634
|
if (!creds?.token) {
|
|
@@ -7150,9 +7748,9 @@ async function run2(argv2) {
|
|
|
7150
7748
|
"--stamp-version is a build-only flag (design spec 2026-07-27 \xA75.4) \u2014 s2s deploy never rewrites versions; run `s2s build --stamp-version <v>` first, then `s2s deploy`"
|
|
7151
7749
|
);
|
|
7152
7750
|
}
|
|
7153
|
-
const absDir =
|
|
7751
|
+
const absDir = resolve14(dir);
|
|
7154
7752
|
const root = findWorkspaceRoot(absDir);
|
|
7155
|
-
if (root !== null &&
|
|
7753
|
+
if (root !== null && resolve14(root) === absDir) {
|
|
7156
7754
|
await runWorkspaceDeploy({ root: absDir, argv: argv2, interactive, filters, dryRun, yes, ci });
|
|
7157
7755
|
return;
|
|
7158
7756
|
}
|
|
@@ -7279,7 +7877,7 @@ async function runWorkspaceDeploy(opts) {
|
|
|
7279
7877
|
}
|
|
7280
7878
|
|
|
7281
7879
|
// src/commands/version.ts
|
|
7282
|
-
import { resolve as
|
|
7880
|
+
import { resolve as resolve15 } from "node:path";
|
|
7283
7881
|
|
|
7284
7882
|
// src/version/version.ts
|
|
7285
7883
|
import { existsSync as existsSync10 } from "node:fs";
|
|
@@ -7538,7 +8136,7 @@ function resolveVersionRoot(from) {
|
|
|
7538
8136
|
async function run3(argv2) {
|
|
7539
8137
|
const interactive = isInteractive();
|
|
7540
8138
|
const [dirArg] = positionals(argv2, ["--since"]);
|
|
7541
|
-
const from =
|
|
8139
|
+
const from = resolve15(dirArg ?? ".");
|
|
7542
8140
|
try {
|
|
7543
8141
|
const root = resolveVersionRoot(from);
|
|
7544
8142
|
const result = await task(
|
|
@@ -7575,16 +8173,16 @@ async function run3(argv2) {
|
|
|
7575
8173
|
}
|
|
7576
8174
|
|
|
7577
8175
|
// src/commands/add.ts
|
|
7578
|
-
import { resolve as
|
|
8176
|
+
import { resolve as resolve17 } from "node:path";
|
|
7579
8177
|
|
|
7580
8178
|
// src/registry/add.ts
|
|
7581
8179
|
import {
|
|
7582
|
-
mkdirSync as
|
|
8180
|
+
mkdirSync as mkdirSync6,
|
|
7583
8181
|
writeFileSync as writeFileSync9,
|
|
7584
8182
|
readFileSync as readFileSync15,
|
|
7585
8183
|
existsSync as existsSync12
|
|
7586
8184
|
} from "node:fs";
|
|
7587
|
-
import { join as join19, resolve as
|
|
8185
|
+
import { join as join19, resolve as resolve16 } from "node:path";
|
|
7588
8186
|
import { gunzipSync } from "node:zlib";
|
|
7589
8187
|
|
|
7590
8188
|
// src/registry/npmrc.ts
|
|
@@ -7621,14 +8219,14 @@ function ensureScopeNpmrc(projectDir, packageName, registryUrl) {
|
|
|
7621
8219
|
}
|
|
7622
8220
|
|
|
7623
8221
|
// src/registry/lib-extract.ts
|
|
7624
|
-
import { mkdirSync as
|
|
8222
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync8 } from "node:fs";
|
|
7625
8223
|
import { join as join18 } from "node:path";
|
|
7626
8224
|
var WANTED = /* @__PURE__ */ new Set(["index.js", "index.d.ts"]);
|
|
7627
8225
|
function extractLibArchive(s2lib, outDir, meta) {
|
|
7628
8226
|
const entries = unzipSync(new Uint8Array(s2lib));
|
|
7629
8227
|
if (!entries["index.js"]?.length) throw new Error("library archive missing index.js");
|
|
7630
8228
|
if (!entries["index.d.ts"]?.length) throw new Error("library archive missing index.d.ts");
|
|
7631
|
-
|
|
8229
|
+
mkdirSync5(outDir, { recursive: true });
|
|
7632
8230
|
for (const [name, bytes] of Object.entries(entries)) {
|
|
7633
8231
|
if (!WANTED.has(name)) continue;
|
|
7634
8232
|
writeFileSync8(join18(outDir, name), Buffer.from(bytes));
|
|
@@ -7662,7 +8260,7 @@ function parseSpec(spec) {
|
|
|
7662
8260
|
}
|
|
7663
8261
|
function extractTypesTarball(tgz, outDir) {
|
|
7664
8262
|
const tar = gunzipSync(tgz);
|
|
7665
|
-
|
|
8263
|
+
mkdirSync6(outDir, { recursive: true });
|
|
7666
8264
|
let offset = 0;
|
|
7667
8265
|
let wroteContract = false;
|
|
7668
8266
|
while (offset + 512 <= tar.length) {
|
|
@@ -7692,7 +8290,7 @@ function rangeForResolved(range, version) {
|
|
|
7692
8290
|
return range.startsWith("^") || range === "*" ? `^${major2}.0.0` : range.includes(".") ? range : `^${version}`;
|
|
7693
8291
|
}
|
|
7694
8292
|
async function addPackage(opts) {
|
|
7695
|
-
const absDir =
|
|
8293
|
+
const absDir = resolve16(opts.pluginDir);
|
|
7696
8294
|
const { name, range } = parseSpec(opts.spec);
|
|
7697
8295
|
const creds = loadCredentials();
|
|
7698
8296
|
const client = new RegistryClient({
|
|
@@ -7762,9 +8360,9 @@ async function run4(argv2) {
|
|
|
7762
8360
|
}
|
|
7763
8361
|
try {
|
|
7764
8362
|
const pluginDir = parseFlag(argv2, "--dir") || ".";
|
|
7765
|
-
const absDir =
|
|
8363
|
+
const absDir = resolve17(pluginDir);
|
|
7766
8364
|
const root = findWorkspaceRoot(absDir);
|
|
7767
|
-
if (root !== null &&
|
|
8365
|
+
if (root !== null && resolve17(root) === absDir) {
|
|
7768
8366
|
throw new Error(
|
|
7769
8367
|
`${absDir} is a workspace root (design spec 2026-07-27 \xA74.1), not a plugin \u2014 \`s2s add\` needs a plugin directory: pass --dir <plugin> (e.g. --dir plugins/shop)`
|
|
7770
8368
|
);
|
|
@@ -7811,13 +8409,13 @@ async function run4(argv2) {
|
|
|
7811
8409
|
}
|
|
7812
8410
|
|
|
7813
8411
|
// src/commands/install.ts
|
|
7814
|
-
import { resolve as
|
|
8412
|
+
import { resolve as resolve19 } from "node:path";
|
|
7815
8413
|
import { existsSync as existsSync14 } from "node:fs";
|
|
7816
8414
|
|
|
7817
8415
|
// src/registry/install.ts
|
|
7818
|
-
import { createHash as
|
|
7819
|
-
import { mkdirSync as
|
|
7820
|
-
import { basename as basename2, join as join20, resolve as
|
|
8416
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
8417
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync16, writeFileSync as writeFileSync10, renameSync, existsSync as existsSync13 } from "node:fs";
|
|
8418
|
+
import { basename as basename2, join as join20, resolve as resolve18, sep as sep3 } from "node:path";
|
|
7821
8419
|
function parseManifest(input) {
|
|
7822
8420
|
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
7823
8421
|
throw new Error("manifest must be a JSON object");
|
|
@@ -7847,7 +8445,7 @@ function mergeSpecs(manifest, args) {
|
|
|
7847
8445
|
return merged;
|
|
7848
8446
|
}
|
|
7849
8447
|
function sha256Hex(bytes) {
|
|
7850
|
-
return
|
|
8448
|
+
return createHash3("sha256").update(bytes).digest("hex");
|
|
7851
8449
|
}
|
|
7852
8450
|
var SAFE_FILENAME_RE = /^[A-Za-z0-9._-]+$/;
|
|
7853
8451
|
function safeFilename(e) {
|
|
@@ -7900,13 +8498,13 @@ async function installPlan(opts) {
|
|
|
7900
8498
|
);
|
|
7901
8499
|
}
|
|
7902
8500
|
}
|
|
7903
|
-
const dirResolved =
|
|
8501
|
+
const dirResolved = resolve18(opts.dir);
|
|
7904
8502
|
const targets = install.map((e) => {
|
|
7905
8503
|
if (!e.sha256) {
|
|
7906
8504
|
throw new Error(`refusing ${e.name}@${e.version}: registry did not provide a sha256 to verify against`);
|
|
7907
8505
|
}
|
|
7908
8506
|
const filename = safeFilename(e);
|
|
7909
|
-
const dest =
|
|
8507
|
+
const dest = resolve18(join20(opts.dir, filename));
|
|
7910
8508
|
if (dest !== dirResolved && !dest.startsWith(dirResolved + sep3)) {
|
|
7911
8509
|
throw new Error(`refusing ${e.name}@${e.version}: destination "${dest}" escapes plugins dir "${dirResolved}"`);
|
|
7912
8510
|
}
|
|
@@ -7917,7 +8515,7 @@ async function installPlan(opts) {
|
|
|
7917
8515
|
for (const e of install) log3(`would install ${e.name}@${e.version} -> ${e.filename}`);
|
|
7918
8516
|
return { written: [], skipped: [], warnings };
|
|
7919
8517
|
}
|
|
7920
|
-
|
|
8518
|
+
mkdirSync7(opts.dir, { recursive: true });
|
|
7921
8519
|
const written = [];
|
|
7922
8520
|
const skipped = [];
|
|
7923
8521
|
for (const { entry: e, filename, dest } of targets) {
|
|
@@ -7957,7 +8555,7 @@ async function run5(argv2) {
|
|
|
7957
8555
|
const reviewedOnly = hasFlag(argv2, "--reviewed-only");
|
|
7958
8556
|
let dir = parseFlag(argv2, "--dir");
|
|
7959
8557
|
if (!dir) {
|
|
7960
|
-
if (existsSync14(
|
|
8558
|
+
if (existsSync14(resolve19(AUTODETECT))) dir = resolve19(AUTODETECT);
|
|
7961
8559
|
else {
|
|
7962
8560
|
console.error(
|
|
7963
8561
|
`Usage: s2s install [name[@range]...] --dir <plugins dir>
|
|
@@ -7968,7 +8566,7 @@ async function run5(argv2) {
|
|
|
7968
8566
|
}
|
|
7969
8567
|
const client = new RegistryClient({ baseUrl: registry });
|
|
7970
8568
|
try {
|
|
7971
|
-
const manifest = loadManifestFile(
|
|
8569
|
+
const manifest = loadManifestFile(resolve19(file));
|
|
7972
8570
|
const specs = mergeSpecs(manifest, args);
|
|
7973
8571
|
if (Object.keys(specs).length === 0) {
|
|
7974
8572
|
console.error(
|
|
@@ -7998,22 +8596,22 @@ async function run5(argv2) {
|
|
|
7998
8596
|
}
|
|
7999
8597
|
|
|
8000
8598
|
// src/commands/create.ts
|
|
8001
|
-
import { resolve as
|
|
8599
|
+
import { resolve as resolve22 } from "node:path";
|
|
8002
8600
|
|
|
8003
8601
|
// src/create/create.ts
|
|
8004
8602
|
import {
|
|
8005
|
-
mkdirSync as
|
|
8603
|
+
mkdirSync as mkdirSync8,
|
|
8006
8604
|
writeFileSync as writeFileSync11,
|
|
8007
8605
|
existsSync as existsSync15,
|
|
8008
8606
|
readdirSync as readdirSync4,
|
|
8009
8607
|
readFileSync as readFileSync17,
|
|
8010
8608
|
symlinkSync
|
|
8011
8609
|
} from "node:fs";
|
|
8012
|
-
import { dirname as
|
|
8610
|
+
import { dirname as dirname6, isAbsolute, join as join21, relative as relative2, resolve as resolve20, basename as basename3 } from "node:path";
|
|
8013
8611
|
import { spawnSync } from "node:child_process";
|
|
8014
8612
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
8015
8613
|
function readCliVersion() {
|
|
8016
|
-
const here =
|
|
8614
|
+
const here = dirname6(fileURLToPath2(import.meta.url));
|
|
8017
8615
|
const candidates = [
|
|
8018
8616
|
join21(here, "..", "package.json"),
|
|
8019
8617
|
// dist/cli.js → packages/sdk/package.json
|
|
@@ -8031,7 +8629,7 @@ function readCliVersion() {
|
|
|
8031
8629
|
return "0.1.0";
|
|
8032
8630
|
}
|
|
8033
8631
|
function findLocalPackagesDir() {
|
|
8034
|
-
const here =
|
|
8632
|
+
const here = dirname6(fileURLToPath2(import.meta.url));
|
|
8035
8633
|
const candidates = [
|
|
8036
8634
|
join21(here, "..", ".."),
|
|
8037
8635
|
// dist/cli.js → packages
|
|
@@ -8039,12 +8637,12 @@ function findLocalPackagesDir() {
|
|
|
8039
8637
|
// src/create → packages
|
|
8040
8638
|
];
|
|
8041
8639
|
for (const c2 of candidates) {
|
|
8042
|
-
if (isPackagesDir(c2)) return
|
|
8640
|
+
if (isPackagesDir(c2)) return resolve20(c2);
|
|
8043
8641
|
}
|
|
8044
8642
|
return void 0;
|
|
8045
8643
|
}
|
|
8046
8644
|
function slugifyBasename(dir, fallback = "plugin") {
|
|
8047
|
-
const base = basename3(
|
|
8645
|
+
const base = basename3(resolve20(dir));
|
|
8048
8646
|
return base.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase() || fallback;
|
|
8049
8647
|
}
|
|
8050
8648
|
function defaultNameFromPath(dir, kind = "plugin") {
|
|
@@ -8090,10 +8688,10 @@ function versionSpecFrom(status, stdout2) {
|
|
|
8090
8688
|
const v = (stdout2 ?? "").trim();
|
|
8091
8689
|
return status === 0 && /^\d+\.\d+\.\d+/.test(v) ? `^${v}` : "latest";
|
|
8092
8690
|
}
|
|
8093
|
-
function registryDevDeps(game, sdkVersion,
|
|
8691
|
+
function registryDevDeps(game, sdkVersion, resolve23 = resolvePublishedVersion) {
|
|
8094
8692
|
const deps = {};
|
|
8095
8693
|
for (const n3 of createPackageNames(game)) {
|
|
8096
|
-
deps[`@s2script/${n3}`] = n3 === "sdk" ? `^${sdkVersion}` :
|
|
8694
|
+
deps[`@s2script/${n3}`] = n3 === "sdk" ? `^${sdkVersion}` : resolve23(`@s2script/${n3}`);
|
|
8097
8695
|
}
|
|
8098
8696
|
return deps;
|
|
8099
8697
|
}
|
|
@@ -8162,7 +8760,7 @@ function tsconfigJson() {
|
|
|
8162
8760
|
// `never` — every Engine.call() shows as an error while `s2s build` passes, because the build
|
|
8163
8761
|
// adds the file to its own program (typecheck.ts) but tsconfig never did. Harmless when the
|
|
8164
8762
|
// plugin ships no gamedata: the path simply matches nothing.
|
|
8165
|
-
include: ["src", ".s2script/gamedata.d.ts", "node_modules/@s2script/sdk/globals.d.ts"]
|
|
8763
|
+
include: ["src", ".s2script/gamedata.d.ts", ".s2script/interfaces.d.ts", "node_modules/@s2script/sdk/globals.d.ts"]
|
|
8166
8764
|
},
|
|
8167
8765
|
null,
|
|
8168
8766
|
2
|
|
@@ -8173,7 +8771,7 @@ function workspaceTsconfigJson(targetPath, workspaceRoot) {
|
|
|
8173
8771
|
return JSON.stringify(
|
|
8174
8772
|
{
|
|
8175
8773
|
extends: rel.startsWith(".") ? rel : `./${rel}`,
|
|
8176
|
-
include: ["src", ".s2script/gamedata.d.ts", "node_modules/@s2script/sdk/globals.d.ts"]
|
|
8774
|
+
include: ["src", ".s2script/gamedata.d.ts", ".s2script/interfaces.d.ts", "node_modules/@s2script/sdk/globals.d.ts"]
|
|
8177
8775
|
},
|
|
8178
8776
|
null,
|
|
8179
8777
|
2
|
|
@@ -8246,7 +8844,7 @@ dist/
|
|
|
8246
8844
|
}
|
|
8247
8845
|
function linkLocalPackagesForNoInstall(targetPath, localPackagesDir) {
|
|
8248
8846
|
const nm = join21(targetPath, "node_modules");
|
|
8249
|
-
|
|
8847
|
+
mkdirSync8(nm, { recursive: true });
|
|
8250
8848
|
const dest = join21(nm, "@s2script");
|
|
8251
8849
|
if (!existsSync15(dest)) symlinkSync(localPackagesDir, dest);
|
|
8252
8850
|
}
|
|
@@ -8277,7 +8875,7 @@ async function createPlugin(opts = {}) {
|
|
|
8277
8875
|
);
|
|
8278
8876
|
}
|
|
8279
8877
|
let slug = workspaceRoot !== void 0 && opts.path !== void 0 ? assertBareSlug(opts.path) : opts.path;
|
|
8280
|
-
let targetPath = workspaceRoot === void 0 ?
|
|
8878
|
+
let targetPath = workspaceRoot === void 0 ? resolve20(opts.path ?? ".") : slug === void 0 ? void 0 : join21(workspaceRoot, "plugins", slug);
|
|
8281
8879
|
if (targetPath !== void 0) assertEmptyTarget(targetPath);
|
|
8282
8880
|
if (interactive) {
|
|
8283
8881
|
intro2("Create an s2script plugin");
|
|
@@ -8350,7 +8948,7 @@ async function createPlugin(opts = {}) {
|
|
|
8350
8948
|
install = workspaceRoot !== void 0 ? "none" : install ?? (opts.noInstall ? "none" : "npm");
|
|
8351
8949
|
const finalTargetPath = targetPath;
|
|
8352
8950
|
const localPackagesDir = workspaceRoot === void 0 ? findLocalPackagesDir() : void 0;
|
|
8353
|
-
|
|
8951
|
+
mkdirSync8(join21(finalTargetPath, "src"), { recursive: true });
|
|
8354
8952
|
if (workspaceRoot !== void 0) {
|
|
8355
8953
|
writeFileSync11(join21(finalTargetPath, "package.json"), minimalPackageJsonContent(name));
|
|
8356
8954
|
writeFileSync11(
|
|
@@ -8407,8 +9005,8 @@ async function createPlugin(opts = {}) {
|
|
|
8407
9005
|
}
|
|
8408
9006
|
|
|
8409
9007
|
// src/create/workspace.ts
|
|
8410
|
-
import { mkdirSync as
|
|
8411
|
-
import { join as join22, resolve as
|
|
9008
|
+
import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync12 } from "node:fs";
|
|
9009
|
+
import { join as join22, resolve as resolve21 } from "node:path";
|
|
8412
9010
|
var ROOT_DEFAULT_GAME = "cs2";
|
|
8413
9011
|
var CHANGESETS_CLI_RANGE = "^2.29.5";
|
|
8414
9012
|
function defaultWorkspaceName(dir) {
|
|
@@ -8461,7 +9059,7 @@ function changesetConfigJson() {
|
|
|
8461
9059
|
) + "\n";
|
|
8462
9060
|
}
|
|
8463
9061
|
async function createWorkspace(opts = {}) {
|
|
8464
|
-
const targetPath =
|
|
9062
|
+
const targetPath = resolve21(opts.path ?? ".");
|
|
8465
9063
|
const interactive = isInteractive({ yes: opts.yes });
|
|
8466
9064
|
let name = opts.name;
|
|
8467
9065
|
assertEmptyTarget(targetPath);
|
|
@@ -8492,9 +9090,9 @@ async function createWorkspace(opts = {}) {
|
|
|
8492
9090
|
name = name ?? defaultWorkspaceName(targetPath);
|
|
8493
9091
|
const version = readCliVersion();
|
|
8494
9092
|
const localPackagesDir = findLocalPackagesDir();
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
9093
|
+
mkdirSync9(join22(targetPath, "plugins"), { recursive: true });
|
|
9094
|
+
mkdirSync9(join22(targetPath, "packages"), { recursive: true });
|
|
9095
|
+
mkdirSync9(join22(targetPath, ".changeset"), { recursive: true });
|
|
8498
9096
|
writeFileSync12(join22(targetPath, "package.json"), rootPackageJsonContent(name, version, localPackagesDir));
|
|
8499
9097
|
writeFileSync12(join22(targetPath, "tsconfig.base.json"), tsconfigBaseJson());
|
|
8500
9098
|
writeFileSync12(join22(targetPath, "eslint.config.mjs"), eslintConfig());
|
|
@@ -8527,7 +9125,7 @@ async function run6(argv2) {
|
|
|
8527
9125
|
return;
|
|
8528
9126
|
}
|
|
8529
9127
|
const pathArg = positionals(argv2, VALUE_FLAGS2)[0];
|
|
8530
|
-
const workspaceRoot = findWorkspaceRoot(pathArg === void 0 ? process.cwd() :
|
|
9128
|
+
const workspaceRoot = findWorkspaceRoot(pathArg === void 0 ? process.cwd() : resolve22(pathArg));
|
|
8531
9129
|
const result = await createPlugin({
|
|
8532
9130
|
path: pathArg,
|
|
8533
9131
|
name: parseFlag(argv2, "--name"),
|
|
@@ -8735,7 +9333,7 @@ async function run7(argv2) {
|
|
|
8735
9333
|
}
|
|
8736
9334
|
|
|
8737
9335
|
// src/config/gen.ts
|
|
8738
|
-
import { readFileSync as readFileSync18, writeFileSync as writeFileSync13, mkdirSync as
|
|
9336
|
+
import { readFileSync as readFileSync18, writeFileSync as writeFileSync13, mkdirSync as mkdirSync10 } from "node:fs";
|
|
8739
9337
|
import { join as join23 } from "node:path";
|
|
8740
9338
|
|
|
8741
9339
|
// src/config/config-path.ts
|
|
@@ -8782,7 +9380,7 @@ function genConfigFromManifest(manifest, outDir) {
|
|
|
8782
9380
|
}
|
|
8783
9381
|
const id = typeof manifest.id === "string" ? manifest.id : "";
|
|
8784
9382
|
if (!id) throw new Error("config gen: manifest declares config but has no id");
|
|
8785
|
-
|
|
9383
|
+
mkdirSync10(outDir, { recursive: true });
|
|
8786
9384
|
const path = join23(outDir, configFileName(id));
|
|
8787
9385
|
writeFileSync13(path, generateDefaultJsonc(config));
|
|
8788
9386
|
return path;
|
|
@@ -8835,7 +9433,7 @@ async function run8(argv2) {
|
|
|
8835
9433
|
}
|
|
8836
9434
|
|
|
8837
9435
|
// src/commands/codegen.ts
|
|
8838
|
-
import { dirname as
|
|
9436
|
+
import { dirname as dirname7, join as join28 } from "node:path";
|
|
8839
9437
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
8840
9438
|
|
|
8841
9439
|
// src/schemagen/gen.ts
|
|
@@ -9219,23 +9817,23 @@ function resolveExpr(f) {
|
|
|
9219
9817
|
const base = `off(${S(f.declaringClass)},${S(f.rawName)})`;
|
|
9220
9818
|
return f.addOffset ? `${base} + ${f.addOffset}` : base;
|
|
9221
9819
|
}
|
|
9222
|
-
function accessorEntry(f,
|
|
9820
|
+
function accessorEntry(f, resolve23, guard2) {
|
|
9223
9821
|
const g = guard2 === "" ? "" : `${guard2} `;
|
|
9224
9822
|
const gs = guard2 === "" ? "" : `${guard2.replace("return null;", "return;")} `;
|
|
9225
9823
|
let entry;
|
|
9226
9824
|
if (f.accessorKind === "str") {
|
|
9227
|
-
entry = `get: function () { ${g}return this.ref.readString(${
|
|
9825
|
+
entry = `get: function () { ${g}return this.ref.readString(${resolve23}, ${f.strLen}); }`;
|
|
9228
9826
|
} else if (f.accessorKind === "u64" || f.accessorKind === "i64") {
|
|
9229
|
-
entry = `get: function () { ${g}var v = this.ref.${READ[f.accessorKind]}(${
|
|
9827
|
+
entry = `get: function () { ${g}var v = this.ref.${READ[f.accessorKind]}(${resolve23}); return v === null ? null : v.toString(); }`;
|
|
9230
9828
|
} else if (VEC_INFO[f.accessorKind]) {
|
|
9231
9829
|
const vi = VEC_INFO[f.accessorKind];
|
|
9232
|
-
entry = `get: function () { ${g}var a = this.ref.readFloats(${
|
|
9830
|
+
entry = `get: function () { ${g}var a = this.ref.readFloats(${resolve23}, ${vi.count}); return a === null ? null : new ${vi.cls}(a[0], a[1], a[2]); }`;
|
|
9233
9831
|
} else {
|
|
9234
|
-
entry = `get: function () { ${g}return this.ref.${READ[f.accessorKind]}(${
|
|
9832
|
+
entry = `get: function () { ${g}return this.ref.${READ[f.accessorKind]}(${resolve23}); }`;
|
|
9235
9833
|
}
|
|
9236
9834
|
if (f.writable) {
|
|
9237
9835
|
const wm = WRITE[f.accessorKind];
|
|
9238
|
-
entry += `, set: function (v) { ${gs}var o = ${
|
|
9836
|
+
entry += `, set: function (v) { ${gs}var o = ${resolve23}; if (this.ref.${wm}(o, v)) this.ref.notifyStateChanged(o); }`;
|
|
9239
9837
|
}
|
|
9240
9838
|
return entry;
|
|
9241
9839
|
}
|
|
@@ -9264,9 +9862,9 @@ function emitJs(model) {
|
|
|
9264
9862
|
out.push(` E[${S(e.className)}] = {`);
|
|
9265
9863
|
for (const f of e.fields) {
|
|
9266
9864
|
const sub = `off(${S(f.declaringClass)},${S(f.rawName)})`;
|
|
9267
|
-
const
|
|
9865
|
+
const resolve23 = f.addOffset ? `this.base + ${sub} + ${f.addOffset}` : `this.base + ${sub}`;
|
|
9268
9866
|
const guard2 = `if (this.base < 0 || ${sub} < 0) return null;`;
|
|
9269
|
-
out.push(` ${S(f.propName)}: { ${accessorEntry(f,
|
|
9867
|
+
out.push(` ${S(f.propName)}: { ${accessorEntry(f, resolve23, guard2)} },`);
|
|
9270
9868
|
}
|
|
9271
9869
|
for (const f of e.embeddedFields) {
|
|
9272
9870
|
const sub = `off(${S(f.declaringClass)},${S(f.rawName)})`;
|
|
@@ -9570,25 +10168,25 @@ function emitNavJs(model) {
|
|
|
9570
10168
|
for (const f of w.fields) {
|
|
9571
10169
|
const own = `off(${S2(f.declaringClass)},${S2(f.rawName)})`;
|
|
9572
10170
|
const withDelta = f.addOffset ? `${own} + ${f.addOffset}` : own;
|
|
9573
|
-
const
|
|
10171
|
+
const resolve23 = `${withDelta} + this.base`;
|
|
9574
10172
|
let getter;
|
|
9575
10173
|
const kind = f.accessorKind;
|
|
9576
10174
|
if (kind === "u64") {
|
|
9577
|
-
getter = `get: function () { var v = this.root.readUInt64Via(this.path, ${
|
|
10175
|
+
getter = `get: function () { var v = this.root.readUInt64Via(this.path, ${resolve23}); return v === null ? null : v.toString(); }`;
|
|
9578
10176
|
} else if (kind === "i64") {
|
|
9579
|
-
getter = `get: function () { var v = this.root.readInt64Via(this.path, ${
|
|
10177
|
+
getter = `get: function () { var v = this.root.readInt64Via(this.path, ${resolve23}); return v === null ? null : v.toString(); }`;
|
|
9580
10178
|
} else if (VEC_INFO[kind]) {
|
|
9581
10179
|
const vi = VEC_INFO[kind];
|
|
9582
|
-
getter = `get: function () { var a = this.root.readFloatsChain(this.path, ${
|
|
10180
|
+
getter = `get: function () { var a = this.root.readFloatsChain(this.path, ${resolve23}, ${vi.count}); return a === null ? null : new ${vi.cls}(a[0], a[1], a[2]); }`;
|
|
9583
10181
|
} else if (kind === "handle") {
|
|
9584
|
-
getter = `get: function () { return this.root.readHandleVia(this.path, ${
|
|
10182
|
+
getter = `get: function () { return this.root.readHandleVia(this.path, ${resolve23}); }`;
|
|
9585
10183
|
} else {
|
|
9586
10184
|
const method = READ_VIA[kind];
|
|
9587
10185
|
if (!method) continue;
|
|
9588
|
-
getter = `get: function () { return this.root.${method}(this.path, ${
|
|
10186
|
+
getter = `get: function () { return this.root.${method}(this.path, ${resolve23}); }`;
|
|
9589
10187
|
}
|
|
9590
10188
|
const wmethod = f.navWritable ? WRITE_VIA[kind] : void 0;
|
|
9591
|
-
const setter = wmethod ? `, set: function (v) { this.root.${wmethod}(this.path, ${
|
|
10189
|
+
const setter = wmethod ? `, set: function (v) { this.root.${wmethod}(this.path, ${resolve23}, v); }` : "";
|
|
9592
10190
|
out.push(` ${S2(f.propName)}: { ${getter}${setter} },`);
|
|
9593
10191
|
}
|
|
9594
10192
|
out.push(" });");
|
|
@@ -9742,7 +10340,7 @@ function runGenHooks(repoRoot2, opts) {
|
|
|
9742
10340
|
|
|
9743
10341
|
// src/commands/codegen.ts
|
|
9744
10342
|
function repoRoot() {
|
|
9745
|
-
return join28(
|
|
10343
|
+
return join28(dirname7(fileURLToPath3(import.meta.url)), "..", "..", "..");
|
|
9746
10344
|
}
|
|
9747
10345
|
async function run9(kind, argv2) {
|
|
9748
10346
|
const root = repoRoot();
|