lecodes-cli 2.2.11 → 2.2.14
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 +4 -2
- package/dist/index.js +1372 -915
- package/package.json +9 -9
- package/runtime/sdk-types.json +1 -1
- package/runtime/web/VERSION.json +3 -3
- package/runtime/web/player.js +2 -2
- package/runtime/web-lite/lite.js +9 -9
- package/src/cli/args.ts +10 -3
- package/src/cli/command.ts +7 -0
- package/src/cli/run.ts +2 -1
- package/src/commands/app/android.ts +7 -10
- package/src/commands/app/desktop.ts +3 -3
- package/src/commands/app/index.ts +10 -13
- package/src/commands/app/shared.ts +43 -17
- package/src/commands/app/templates/android.ts +1 -1
- package/src/commands/index.ts +3 -1
- package/src/commands/init/templates.ts +4 -3
- package/src/commands/publish.ts +38 -0
- package/src/commands/push.ts +28 -15
- package/src/commands/releases.ts +168 -0
- package/src/dev/backendWorker.ts +26 -3
- package/src/dev/localBackend.ts +7 -2
- package/src/dev/projectBackend.ts +8 -2
- package/src/hosts/desktopScript.ts +3 -3
- package/src/platform/api.ts +116 -8
- package/src/plugin/generate.ts +9 -6
- package/src/plugin/permissions.ts +42 -10
package/dist/index.js
CHANGED
|
@@ -64,9 +64,9 @@ var package_default;
|
|
|
64
64
|
var init_package = __esm(() => {
|
|
65
65
|
package_default = {
|
|
66
66
|
name: "lecodes-cli",
|
|
67
|
-
version: "2.2.
|
|
67
|
+
version: "2.2.14",
|
|
68
68
|
dependencies: {
|
|
69
|
-
"@letary/chisel": "^0.
|
|
69
|
+
"@letary/chisel": "^0.15.0",
|
|
70
70
|
jimp: "^1.6.1",
|
|
71
71
|
"lecodes-sdk": "workspace:*"
|
|
72
72
|
},
|
|
@@ -88,7 +88,7 @@ var init_package = __esm(() => {
|
|
|
88
88
|
},
|
|
89
89
|
peerDependencies: {
|
|
90
90
|
"lecodes-design": "^0.11.0",
|
|
91
|
-
"lecodes-headless": "^2.2.
|
|
91
|
+
"lecodes-headless": "^2.2.14",
|
|
92
92
|
"lecodes-3d-editor": "^0.9.0",
|
|
93
93
|
"lecodes-assets": "^0.2.1",
|
|
94
94
|
"heic-decode": "^2.1.0",
|
|
@@ -158,10 +158,10 @@ var init_package = __esm(() => {
|
|
|
158
158
|
});
|
|
159
159
|
|
|
160
160
|
// ../../sdk/src/version.ts
|
|
161
|
-
var SDK_VERSION = "2.2.
|
|
161
|
+
var SDK_VERSION = "2.2.14";
|
|
162
162
|
|
|
163
163
|
// src/cli/args.ts
|
|
164
|
-
var isNegativeNumber = (s) => /^-(\d|\.\d)/.test(s), isValue = (s) => !s.startsWith("-") || isNegativeNumber(s), parseArgv = (argv) => {
|
|
164
|
+
var isNegativeNumber = (s) => /^-(\d|\.\d)/.test(s), isValue = (s) => !s.startsWith("-") || isNegativeNumber(s), parseArgv = (argv, booleans = new Set) => {
|
|
165
165
|
const out = { positional: [], flags: {} };
|
|
166
166
|
for (let i = 0;i < argv.length; i++) {
|
|
167
167
|
const arg = argv[i];
|
|
@@ -183,7 +183,13 @@ var isNegativeNumber = (s) => /^-(\d|\.\d)/.test(s), isValue = (s) => !s.startsW
|
|
|
183
183
|
}
|
|
184
184
|
const key = arg.slice(long ? 2 : 1);
|
|
185
185
|
const next = argv[i + 1];
|
|
186
|
-
if (
|
|
186
|
+
if (booleans.has(key)) {
|
|
187
|
+
if (next === "true" || next === "false") {
|
|
188
|
+
out.flags[key] = next;
|
|
189
|
+
i++;
|
|
190
|
+
} else
|
|
191
|
+
out.flags[key] = true;
|
|
192
|
+
} else if (next !== undefined && isValue(next)) {
|
|
187
193
|
out.flags[key] = next;
|
|
188
194
|
i++;
|
|
189
195
|
} else
|
|
@@ -243,6 +249,15 @@ var str = (help, opts) => ({ type: "string", help, ...opts }), num = (help, opts
|
|
|
243
249
|
out[name] = present;
|
|
244
250
|
}
|
|
245
251
|
return out;
|
|
252
|
+
}, booleanFlagNames = (spec) => {
|
|
253
|
+
const names = new Set;
|
|
254
|
+
for (const [name, s] of Object.entries(spec))
|
|
255
|
+
if (s.type === "boolean") {
|
|
256
|
+
names.add(name);
|
|
257
|
+
if (s.alias)
|
|
258
|
+
names.add(s.alias);
|
|
259
|
+
}
|
|
260
|
+
return names;
|
|
246
261
|
}, unknownFlags = (spec, raw) => {
|
|
247
262
|
const known = new Set;
|
|
248
263
|
for (const [name, s] of Object.entries(spec)) {
|
|
@@ -498,8 +513,8 @@ var all = (groups) => groups.flatMap((g) => g.commands), closest = (name, cmds)
|
|
|
498
513
|
process.exitCode = 1;
|
|
499
514
|
return;
|
|
500
515
|
}
|
|
501
|
-
const parsed = parseArgv(rest);
|
|
502
516
|
const spec = node.flags ?? {};
|
|
517
|
+
const parsed = parseArgv(rest, booleanFlagNames(spec));
|
|
503
518
|
if (!node.passthrough) {
|
|
504
519
|
const unknown = unknownFlags(spec, parsed.flags);
|
|
505
520
|
if (unknown.length)
|
|
@@ -525,6 +540,7 @@ var all = (groups) => groups.flatMap((g) => g.commands), closest = (name, cmds)
|
|
|
525
540
|
process.exitCode = 1;
|
|
526
541
|
};
|
|
527
542
|
var init_run = __esm(() => {
|
|
543
|
+
init_command();
|
|
528
544
|
init_args();
|
|
529
545
|
init_command();
|
|
530
546
|
init_errors();
|
|
@@ -590,7 +606,7 @@ var errorMessage = (body, status) => {
|
|
|
590
606
|
return b.message;
|
|
591
607
|
}
|
|
592
608
|
return `Request failed with status ${status}`;
|
|
593
|
-
}, apiRequest = async (apiUrl, path, opts = {}) => {
|
|
609
|
+
}, ApiError, apiRequest = async (apiUrl, path, opts = {}) => {
|
|
594
610
|
const headers = {};
|
|
595
611
|
const isForm = opts.body instanceof FormData;
|
|
596
612
|
if (opts.token)
|
|
@@ -610,13 +626,64 @@ var errorMessage = (body, status) => {
|
|
|
610
626
|
const contentType = resp.headers.get("content-type") ?? "";
|
|
611
627
|
const parsed = contentType.startsWith("application/json") ? await resp.json() : contentType.startsWith("text/") ? await resp.text() : null;
|
|
612
628
|
if (resp.status >= 400)
|
|
613
|
-
throw new
|
|
629
|
+
throw new ApiError(errorMessage(parsed, resp.status), resp.status, parsed);
|
|
614
630
|
return parsed;
|
|
615
631
|
}, getAccount = (apiUrl, token) => apiRequest(apiUrl, "/account", { token }), getProject = (apiUrl, token, uuid) => apiRequest(apiUrl, `/projects/${uuid}`, { token }), createProject = (apiUrl, token, name, teamUuid) => apiRequest(apiUrl, "/projects", { token, body: { name, teamUuid } }), getCommits = (apiUrl, token, uuid) => apiRequest(apiUrl, `/projects/${uuid}/commits`, { token }), getTypes = (apiUrl) => apiRequest(apiUrl, "/types"), uploadBlob = (apiUrl, token, uuid, name, bytes) => {
|
|
616
632
|
const form = new FormData;
|
|
617
633
|
form.append("file", new Blob([bytes]), name);
|
|
618
634
|
return apiRequest(apiUrl, `/projects/${uuid}/blobs`, { token, body: form });
|
|
619
|
-
}, sync = (apiUrl, token, uuid, payload) => apiRequest(apiUrl, `/projects/${uuid}/sync`, { token, body: payload }),
|
|
635
|
+
}, sync = (apiUrl, token, uuid, payload) => apiRequest(apiUrl, `/projects/${uuid}/sync`, { token, body: payload }), SchemaChangeError, opText = (op) => {
|
|
636
|
+
if (typeof op === "string")
|
|
637
|
+
return op;
|
|
638
|
+
if (op && typeof op === "object" && typeof op.text === "string")
|
|
639
|
+
return op.text;
|
|
640
|
+
return JSON.stringify(op);
|
|
641
|
+
}, schemaChangeOf = (e) => {
|
|
642
|
+
if (!(e instanceof ApiError) || e.status !== 409 || !e.body || typeof e.body !== "object")
|
|
643
|
+
return e;
|
|
644
|
+
const body = e.body;
|
|
645
|
+
const destructive = Array.isArray(body.destructive) ? body.destructive.map(opText) : null;
|
|
646
|
+
const incompatible = typeof body.incompatible === "string" ? body.incompatible : body.incompatible === true ? "" : null;
|
|
647
|
+
if (!destructive && incompatible === null)
|
|
648
|
+
return e;
|
|
649
|
+
return new SchemaChangeError(typeof body.error === "string" ? body.error : "The schema change needs a confirmation", destructive ?? [], incompatible);
|
|
650
|
+
}, releaseFailure = (e, opts) => {
|
|
651
|
+
const schema = schemaChangeOf(e);
|
|
652
|
+
if (schema !== e || !(e instanceof ApiError))
|
|
653
|
+
return schema;
|
|
654
|
+
if (opts.build && e.status === 400)
|
|
655
|
+
return new CliError(`Build failed:
|
|
656
|
+
${e.message}`, 400);
|
|
657
|
+
if (e.status === 502 || e.status === 503) {
|
|
658
|
+
const detail = e.message.trimStart().startsWith("<") ? "" : `: ${e.message.trim()}`;
|
|
659
|
+
const service = opts.build ? "The platform's build service is unavailable" : "The platform could not reach the app backend";
|
|
660
|
+
return new CliError(`${service} (${e.status})${detail}. Nothing changed — try again in a minute.`, e.status);
|
|
661
|
+
}
|
|
662
|
+
return e;
|
|
663
|
+
}, publishProject = async (apiUrl, token, uuid, opts = {}) => {
|
|
664
|
+
try {
|
|
665
|
+
const body = { ...opts.allowDestructive ? { allowDestructive: true } : {}, ...opts.force ? { force: true } : {} };
|
|
666
|
+
return await apiRequest(apiUrl, `/projects/${uuid}/publish`, { token, body });
|
|
667
|
+
} catch (e) {
|
|
668
|
+
throw releaseFailure(e, { build: true });
|
|
669
|
+
}
|
|
670
|
+
}, listReleases = async (apiUrl, token, uuid) => (await apiRequest(apiUrl, `/projects/${uuid}/publish`, { token })).releases, useRelease = async (apiUrl, token, uuid, version, opts = {}) => {
|
|
671
|
+
try {
|
|
672
|
+
return await apiRequest(apiUrl, `/projects/${uuid}/releases/${encodeURIComponent(version)}/use`, { token, body: opts.allowDestructive ? { allowDestructive: true } : {} });
|
|
673
|
+
} catch (e) {
|
|
674
|
+
throw releaseFailure(e, { build: false });
|
|
675
|
+
}
|
|
676
|
+
}, uploadReleaseBundle = async (apiUrl, token, uuid, bundle) => {
|
|
677
|
+
const form = new FormData;
|
|
678
|
+
form.append("app", new Blob([bundle.code], { type: "text/javascript" }), "app.js");
|
|
679
|
+
if (bundle.map !== null)
|
|
680
|
+
form.append("map", new Blob([bundle.map], { type: "application/json" }), "app.js.map");
|
|
681
|
+
try {
|
|
682
|
+
return await apiRequest(apiUrl, `/projects/${uuid}/releases/upload`, { token, body: form });
|
|
683
|
+
} catch (e) {
|
|
684
|
+
throw releaseFailure(e, { build: false });
|
|
685
|
+
}
|
|
686
|
+
}, enableDesignShare = (apiUrl, token, uuid) => apiRequest(apiUrl, `/projects/${uuid}/design/share`, { token, body: {} }), disableDesignShare = (apiUrl, token, uuid) => apiRequest(apiUrl, `/projects/${uuid}/design/share`, { token, method: "DELETE" }), setDesignCommentsOpen = (apiUrl, token, uuid, open) => apiRequest(apiUrl, `/projects/${uuid}/design/comments/open`, { token, body: { open } }), listDesignComments = (apiUrl, token, uuid) => apiRequest(apiUrl, `/projects/${uuid}/design/comments`, { token }), createDesignComment = (apiUrl, token, uuid, input) => apiRequest(apiUrl, `/projects/${uuid}/design/comments`, { token, body: input }), replyDesignComment = (apiUrl, token, uuid, threadId, body, origin) => apiRequest(apiUrl, `/projects/${uuid}/design/comments/${threadId}/messages`, { token, body: { body, origin } }), resolveDesignComment = (apiUrl, token, uuid, threadId, resolved) => apiRequest(apiUrl, `/projects/${uuid}/design/comments/${threadId}`, { token, method: "PATCH", body: { resolved } }), sendPush = (apiUrl, token, uuid, payload) => apiRequest(apiUrl, `/projects/${uuid}/push/send`, { token, body: payload }), listPushDevices = (apiUrl, token, uuid, user) => apiRequest(apiUrl, `/projects/${uuid}/push/registrations${user ? `?user=${encodeURIComponent(user)}` : ""}`, { token }), forgetPushDevice = (apiUrl, token, uuid, address) => apiRequest(apiUrl, `/projects/${uuid}/push/registrations/${encodeURIComponent(address)}`, { token, method: "DELETE" }), listPushKeys = (apiUrl, token, uuid) => apiRequest(apiUrl, `/projects/${uuid}/push/keys`, { token }), createPushKey = (apiUrl, token, uuid, name) => apiRequest(apiUrl, `/projects/${uuid}/push/keys`, { token, body: { name } }), revokePushKey = (apiUrl, token, uuid, id) => apiRequest(apiUrl, `/projects/${uuid}/push/keys/${id}`, { token, method: "DELETE" }), downloadBlob = async (apiUrl, src) => {
|
|
620
687
|
const resp = await fetch(`${apiUrl}${src}`);
|
|
621
688
|
if (!resp.ok)
|
|
622
689
|
throw new CliError(`Failed to download ${src} (status ${resp.status})`);
|
|
@@ -624,6 +691,22 @@ var errorMessage = (body, status) => {
|
|
|
624
691
|
};
|
|
625
692
|
var init_api = __esm(() => {
|
|
626
693
|
init_cli();
|
|
694
|
+
ApiError = class ApiError extends CliError {
|
|
695
|
+
body;
|
|
696
|
+
constructor(message, status, body) {
|
|
697
|
+
super(message, status);
|
|
698
|
+
this.body = body;
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
SchemaChangeError = class SchemaChangeError extends CliError {
|
|
702
|
+
destructive;
|
|
703
|
+
incompatible;
|
|
704
|
+
constructor(message, destructive, incompatible) {
|
|
705
|
+
super(message, 409);
|
|
706
|
+
this.destructive = destructive;
|
|
707
|
+
this.incompatible = incompatible;
|
|
708
|
+
}
|
|
709
|
+
};
|
|
627
710
|
});
|
|
628
711
|
|
|
629
712
|
// ../../sdk/src/compile/assetName.ts
|
|
@@ -170224,7 +170307,7 @@ var normalizePath2 = (path) => {
|
|
|
170224
170307
|
continue;
|
|
170225
170308
|
const exported = new Map(checker.getExportsOfModule(modSym).map((s) => [s.name, s]));
|
|
170226
170309
|
for (const ex of exports) {
|
|
170227
|
-
if (ex.kind === "other")
|
|
170310
|
+
if (ex.kind === "other" || ex.kind === "push")
|
|
170228
170311
|
continue;
|
|
170229
170312
|
const id = `${serverModuleId(path)}#${ex.name}`;
|
|
170230
170313
|
let sym = exported.get(ex.name);
|
|
@@ -170545,6 +170628,8 @@ var SERVER_FILE, isServerPath = (path) => SERVER_FILE.test(path), serverModuleId
|
|
|
170545
170628
|
const s = init.trimStart();
|
|
170546
170629
|
if (/^channel\s*[<(]/.test(s))
|
|
170547
170630
|
return "channel";
|
|
170631
|
+
if (/^pushChannel\s*[<(]/.test(s))
|
|
170632
|
+
return "push";
|
|
170548
170633
|
if (/^async\s+function\b/.test(s))
|
|
170549
170634
|
return "fn";
|
|
170550
170635
|
if (/^async\b/.test(s))
|
|
@@ -170588,7 +170673,7 @@ var SERVER_FILE, isServerPath = (path) => SERVER_FILE.test(path), serverModuleId
|
|
|
170588
170673
|
}
|
|
170589
170674
|
}
|
|
170590
170675
|
return found.sort((a, b) => a.at - b.at).map(({ name, kind }) => ({ name, kind }));
|
|
170591
|
-
}, serverStubSource = (path, exports, serverUrl) => {
|
|
170676
|
+
}, serverStubSource = (path, exports, serverUrl, push = exports.some((e) => e.kind === "push")) => {
|
|
170592
170677
|
const mod = serverModuleId(path);
|
|
170593
170678
|
const url = JSON.stringify(serverUrl);
|
|
170594
170679
|
const lines = exports.map(({ name, kind }) => {
|
|
@@ -170597,8 +170682,12 @@ var SERVER_FILE, isServerPath = (path) => SERVER_FILE.test(path), serverModuleId
|
|
|
170597
170682
|
return `export const ${name} = __rpc(${url}, ${id})`;
|
|
170598
170683
|
if (kind === "channel")
|
|
170599
170684
|
return `export const ${name} = __channel(${url}, ${id})`;
|
|
170685
|
+
if (kind === "push")
|
|
170686
|
+
return `export const ${name} = __pushChannel(${url}, ${id})`;
|
|
170600
170687
|
return `export const ${name} = __serverOnly(${id})`;
|
|
170601
170688
|
});
|
|
170689
|
+
if (push)
|
|
170690
|
+
lines.unshift("__pushEnable()");
|
|
170602
170691
|
return `// client stub of ${mod} — generated by the lecodes compiler
|
|
170603
170692
|
${lines.join(`
|
|
170604
170693
|
`)}
|
|
@@ -170606,9 +170695,9 @@ ${lines.join(`
|
|
|
170606
170695
|
}, ClientPublishError, clientPublishes = (entries) => {
|
|
170607
170696
|
const channels = new Map;
|
|
170608
170697
|
for (const [path, exports] of Object.entries(scanServerModules(entries))) {
|
|
170609
|
-
const names = exports.filter((e) => e.kind === "channel").map((e) => e.name);
|
|
170610
|
-
if (names.
|
|
170611
|
-
channels.set(path.replace(/\.[tj]s$/, ""),
|
|
170698
|
+
const names = new Map(exports.filter((e) => e.kind === "channel" || e.kind === "push").map((e) => [e.name, e.kind === "push" ? "send" : "publish"]));
|
|
170699
|
+
if (names.size)
|
|
170700
|
+
channels.set(path.replace(/\.[tj]s$/, ""), names);
|
|
170612
170701
|
}
|
|
170613
170702
|
if (channels.size === 0)
|
|
170614
170703
|
return [];
|
|
@@ -170627,17 +170716,18 @@ ${lines.join(`
|
|
|
170627
170716
|
continue;
|
|
170628
170717
|
for (const part of m[1].split(",")) {
|
|
170629
170718
|
const [imported, local] = part.split(/\s+as\s+/).map((s) => s.trim());
|
|
170630
|
-
|
|
170631
|
-
|
|
170719
|
+
const verb = imported ? names.get(imported) : undefined;
|
|
170720
|
+
if (verb)
|
|
170721
|
+
locals.push([local ?? imported, verb]);
|
|
170632
170722
|
}
|
|
170633
170723
|
}
|
|
170634
170724
|
if (!locals.length)
|
|
170635
170725
|
continue;
|
|
170636
170726
|
const code = blankCommentsAndStrings(src);
|
|
170637
|
-
for (const local of locals) {
|
|
170638
|
-
for (const m of code.matchAll(new RegExp(`(?<![\\w$.])${local.replace(/\$/g, "\\$")}\\s*\\.\\s
|
|
170727
|
+
for (const [local, verb] of locals) {
|
|
170728
|
+
for (const m of code.matchAll(new RegExp(`(?<![\\w$.])${local.replace(/\$/g, "\\$")}\\s*\\.\\s*${verb}\\s*\\(`, "g"))) {
|
|
170639
170729
|
out.push(`${serverModuleId(e.path)}:${code.slice(0, m.index).split(`
|
|
170640
|
-
`).length}: ${local}
|
|
170730
|
+
`).length}: ${local}.${verb}(…)`);
|
|
170641
170731
|
}
|
|
170642
170732
|
}
|
|
170643
170733
|
}
|
|
@@ -170677,7 +170767,11 @@ ${lines.join(`
|
|
|
170677
170767
|
out[normalizePath3(e.path)] = scanServerExports(e.text ?? "", serverModuleId(e.path));
|
|
170678
170768
|
}
|
|
170679
170769
|
return out;
|
|
170680
|
-
}, stubServerEntries = (entries, serverUrl) =>
|
|
170770
|
+
}, stubServerEntries = (entries, serverUrl) => {
|
|
170771
|
+
const modules = scanServerModules(entries);
|
|
170772
|
+
const push = Object.values(modules).some((exports) => exports.some((e) => e.kind === "push"));
|
|
170773
|
+
return entries.map((e) => e.type === "text" && isServerPath(e.path) ? { ...e, text: serverStubSource(e.path, modules[normalizePath3(e.path)] ?? [], serverUrl, push) } : e);
|
|
170774
|
+
}, compileServerBundle = async (opts) => {
|
|
170681
170775
|
const modules = scanServerModules(opts.entries);
|
|
170682
170776
|
if (Object.keys(modules).length === 0)
|
|
170683
170777
|
return null;
|
|
@@ -170714,7 +170808,7 @@ var init_serverSplit = __esm(() => {
|
|
|
170714
170808
|
ClientPublishError = class ClientPublishError extends Error {
|
|
170715
170809
|
problems;
|
|
170716
170810
|
constructor(problems) {
|
|
170717
|
-
super(`a channel goes from the server to the app — the app
|
|
170811
|
+
super(`a channel goes from the server to the app — the app listens; to send, call a server function that publishes (a push channel: that sends):
|
|
170718
170812
|
${problems.join(`
|
|
170719
170813
|
`)}`);
|
|
170720
170814
|
this.name = "ClientPublishError";
|
|
@@ -171163,7 +171257,7 @@ var init_shaderSchema = __esm(() => {
|
|
|
171163
171257
|
});
|
|
171164
171258
|
|
|
171165
171259
|
// ../../sdk/src/compile/header.ts
|
|
171166
|
-
var buildHeader = (name, code, opts = {}) => {
|
|
171260
|
+
var ENGINE_SIGNALS, buildHeader = (name, code, opts = {}) => {
|
|
171167
171261
|
let header = `// sdk: ${SDK_VERSION}
|
|
171168
171262
|
`;
|
|
171169
171263
|
header += `// name: ${name.slice(0, 20)}
|
|
@@ -171181,6 +171275,10 @@ var buildHeader = (name, code, opts = {}) => {
|
|
|
171181
171275
|
`;
|
|
171182
171276
|
if (code.includes("_creator2d.createScene"))
|
|
171183
171277
|
header += `// 2d: enable
|
|
171278
|
+
`;
|
|
171279
|
+
for (const [name, re] of ENGINE_SIGNALS)
|
|
171280
|
+
if (re.test(code))
|
|
171281
|
+
header += `// ${name}: enable
|
|
171184
171282
|
`;
|
|
171185
171283
|
if (usesAR(code, opts.plugins))
|
|
171186
171284
|
header += `// ar: enable
|
|
@@ -171214,7 +171312,14 @@ var buildHeader = (name, code, opts = {}) => {
|
|
|
171214
171312
|
`;
|
|
171215
171313
|
return header;
|
|
171216
171314
|
}, usesAR = (code, plugins) => !!plugins?.includes("ar") && code.includes("_creator.createScene");
|
|
171217
|
-
var init_header = () => {
|
|
171315
|
+
var init_header = __esm(() => {
|
|
171316
|
+
ENGINE_SIGNALS = [
|
|
171317
|
+
["physics", /_creator\.(?:physicsCreateBody|physicsConfigure|physicsRaycast|characterCreate|vehicleCreate|ragdollCreate)\(/],
|
|
171318
|
+
["net", /\b_creatorNet\b/],
|
|
171319
|
+
["nav", /_creatorNav\.\w+\(/],
|
|
171320
|
+
["audio", /_creatorAudio\.(?:loadClip|play)\(/]
|
|
171321
|
+
];
|
|
171322
|
+
});
|
|
171218
171323
|
|
|
171219
171324
|
// ../../sdk/src/compile/sourcemap.ts
|
|
171220
171325
|
var offsetSourceMap = (mapJson, lines) => {
|
|
@@ -171228,295 +171333,8 @@ var offsetSourceMap = (mapJson, lines) => {
|
|
|
171228
171333
|
return `//# lecodesMap=data:application/json;charset=utf-8;base64,${b64}`;
|
|
171229
171334
|
};
|
|
171230
171335
|
|
|
171231
|
-
// ../../sdk/src/compile/
|
|
171232
|
-
var
|
|
171233
|
-
const seen = new Map;
|
|
171234
|
-
for (const e of entries)
|
|
171235
|
-
if (e.type === "resource") {
|
|
171236
|
-
const b = localAssetBase(e.path);
|
|
171237
|
-
seen.set(b, (seen.get(b) ?? 0) + 1);
|
|
171238
|
-
}
|
|
171239
|
-
const names = new Map;
|
|
171240
|
-
for (const e of entries)
|
|
171241
|
-
if (e.type === "resource") {
|
|
171242
|
-
const b = localAssetBase(e.path);
|
|
171243
|
-
names.set(e.path, (seen.get(b) ?? 0) > 1 ? e.path.replace(/^[./]+/, "").replace(/[\\/]/g, "__") : b);
|
|
171244
|
-
}
|
|
171245
|
-
return names;
|
|
171246
|
-
}, entriesToFiles = (entries, opts) => {
|
|
171247
|
-
const files = {};
|
|
171248
|
-
const localNames = localAssetNames(entries);
|
|
171249
|
-
const localAssetId = (nameExpr) => `_creatorFetch.local(${nameExpr})`;
|
|
171250
|
-
for (const e of entries) {
|
|
171251
|
-
if (e.type === "resource") {
|
|
171252
|
-
if (opts.localAssets && !e.remote) {
|
|
171253
|
-
const name = localNames.get(e.path) ?? localAssetBase(e.path);
|
|
171254
|
-
files[e.path] = `export default ("id:" + ${localAssetId(JSON.stringify(name))})`;
|
|
171255
|
-
} else {
|
|
171256
|
-
files[e.path] = `export default "${opts.publicUrl}${e.fileSrc ?? ""}"`;
|
|
171257
|
-
}
|
|
171258
|
-
} else if (e.type === "shader") {
|
|
171259
|
-
const shaders = e.shaders ?? [];
|
|
171260
|
-
if (e.shadersLocal && shaders.length > 0) {
|
|
171261
|
-
const name = shaders[0].src.split(/[\/]/).pop() ?? "";
|
|
171262
|
-
const [prefix, suffix = ".filamat"] = name.split("_opengl");
|
|
171263
|
-
files[e.path] = `export default ("id:" + ${localAssetId(`${JSON.stringify(prefix + "_")} + _creator.backend + ${JSON.stringify(suffix)}`)})`;
|
|
171264
|
-
} else if (shaders.length > 0) {
|
|
171265
|
-
files[e.path] = `export default \`${opts.publicUrl}${shaders[0].src.replace("_opengl", "_${_creator.backend}")}\``;
|
|
171266
|
-
} else {
|
|
171267
|
-
files[e.path] = `export default ""`;
|
|
171268
|
-
}
|
|
171269
|
-
} else if (e.path.toLowerCase().endsWith(".svg")) {
|
|
171270
|
-
files[e.path] = `export default SvgSource(\`${e.text ?? ""}\`)`;
|
|
171271
|
-
} else if (e.path.toLowerCase().endsWith(".json")) {
|
|
171272
|
-
files[e.path] = `export default ${e.text?.trim() || "null"}`;
|
|
171273
|
-
} else {
|
|
171274
|
-
files[e.path] = e.text ?? "";
|
|
171275
|
-
}
|
|
171276
|
-
}
|
|
171277
|
-
return files;
|
|
171278
|
-
}, pluginsOfAppJson = (entries) => {
|
|
171279
|
-
const app = entries.find((e) => e.path.replace(/^[./\\]+/, "") === "app.json");
|
|
171280
|
-
if (!app?.text)
|
|
171281
|
-
return [];
|
|
171282
|
-
try {
|
|
171283
|
-
const plugins = JSON.parse(app.text).plugins;
|
|
171284
|
-
return plugins && typeof plugins === "object" && !Array.isArray(plugins) ? Object.keys(plugins) : [];
|
|
171285
|
-
} catch {
|
|
171286
|
-
return [];
|
|
171287
|
-
}
|
|
171288
|
-
}, pluginsOfBundle = (mapJson) => {
|
|
171289
|
-
const ids = new Set;
|
|
171290
|
-
try {
|
|
171291
|
-
for (const source of JSON.parse(mapJson).sources ?? []) {
|
|
171292
|
-
const m = /\/plugins\/gen\/([^/]+)\/sdk\//.exec(source);
|
|
171293
|
-
if (m)
|
|
171294
|
-
ids.add(m[1]);
|
|
171295
|
-
}
|
|
171296
|
-
} catch {}
|
|
171297
|
-
return [...ids].sort();
|
|
171298
|
-
}, compileProjectWithMap = async (opts) => {
|
|
171299
|
-
const published = clientPublishes(opts.entries);
|
|
171300
|
-
if (published.length)
|
|
171301
|
-
throw new ClientPublishError(published);
|
|
171302
|
-
const entries = stubServerEntries(opts.entries, opts.serverUrl ?? "");
|
|
171303
|
-
const entry = opts.entryOverride ?? detectEntry(entries, scanModulesChisel).entry;
|
|
171304
|
-
if (!entry) {
|
|
171305
|
-
throw new Error("Couldn't find an entrypoint (no .ts/.js project file — name yours main.ts). Pass entryOverride.");
|
|
171306
|
-
}
|
|
171307
|
-
const files = entriesToFiles(entries, opts);
|
|
171308
|
-
const { code, map } = await bundleProjectWithMap(entry, files, opts.format ?? "esm", opts.minify, opts.editor, opts.libraries, opts.sourcemap ?? true);
|
|
171309
|
-
const plugins = opts.plugins ?? pluginsOfAppJson(entries);
|
|
171310
|
-
if (map && opts.onWarning) {
|
|
171311
|
-
const missing = pluginsOfBundle(map).filter((id) => !plugins.includes(id));
|
|
171312
|
-
if (missing.length) {
|
|
171313
|
-
opts.onWarning(`The app uses ${missing.map((id) => `"${id}"`).join(", ")} and app.json does not list ${missing.length === 1 ? "it" : "them"}: a plugin that is not listed is not there — in a shell built from this project and in the browser alike. Add to app.json: "plugins": { ${missing.map((id) => `"${id}": "*"`).join(", ")} }`);
|
|
171314
|
-
}
|
|
171315
|
-
}
|
|
171316
|
-
if (opts.header === false)
|
|
171317
|
-
return { code, map, entry };
|
|
171318
|
-
const header = buildHeader(opts.name, code, { icon: opts.icon, fonts: opts.fontsHeader, icons: opts.iconsHeader, plugins });
|
|
171319
|
-
const headerLines = (header.match(/\n/g) ?? []).length;
|
|
171320
|
-
return { code: header + code, map: map ? offsetSourceMap(map, headerLines) : null, entry };
|
|
171321
|
-
}, compileProject = async (opts) => {
|
|
171322
|
-
const { code, map } = await compileProjectWithMap(opts);
|
|
171323
|
-
return map ? `${code}
|
|
171324
|
-
${inlineSourceMapComment(map)}` : code;
|
|
171325
|
-
};
|
|
171326
|
-
var init_compileProject = __esm(() => {
|
|
171327
|
-
init_bundler();
|
|
171328
|
-
init_detectEntry();
|
|
171329
|
-
init_header();
|
|
171330
|
-
init_serverSplit();
|
|
171331
|
-
});
|
|
171332
|
-
|
|
171333
|
-
// ../../sdk/src/compile/sceneEditor.ts
|
|
171334
|
-
var SCENE_HARNESS_DIR = "./.scene-editor", SCENE_ENTRY_PATH = "./.scene-entry.ts", resolveRelative = (fromPath, spec) => {
|
|
171335
|
-
const segs = (p) => p.split("/").filter((s) => s !== "" && s !== ".");
|
|
171336
|
-
const out = segs(fromPath).slice(0, -1);
|
|
171337
|
-
for (const seg of segs(spec)) {
|
|
171338
|
-
if (seg === "..")
|
|
171339
|
-
out.pop();
|
|
171340
|
-
else
|
|
171341
|
-
out.push(seg);
|
|
171342
|
-
}
|
|
171343
|
-
return "./" + out.join("/");
|
|
171344
|
-
}, sceneImportsOf = (sceneText, scenePath, projectPaths) => {
|
|
171345
|
-
const known = new Set(projectPaths);
|
|
171346
|
-
const out = [];
|
|
171347
|
-
const re = /^\s*import\s+[A-Za-z_$][\w$]*\s+from\s+["']([^"']+\.scene(?:\.ts)?)["']/gm;
|
|
171348
|
-
for (const m of sceneText.matchAll(re)) {
|
|
171349
|
-
const spec = m[1];
|
|
171350
|
-
if (!spec.startsWith("."))
|
|
171351
|
-
continue;
|
|
171352
|
-
let p = resolveRelative(scenePath, spec);
|
|
171353
|
-
if (!p.endsWith(".ts"))
|
|
171354
|
-
p += ".ts";
|
|
171355
|
-
if (known.has(p) && !out.includes(p))
|
|
171356
|
-
out.push(p);
|
|
171357
|
-
}
|
|
171358
|
-
return out;
|
|
171359
|
-
}, sceneEditorEntries = (scenePath, harness, projectPaths = [], sceneImports = []) => {
|
|
171360
|
-
const entries = Object.entries(harness).map(([name, text]) => ({
|
|
171361
|
-
path: `${SCENE_HARNESS_DIR}/${name}`,
|
|
171362
|
-
type: "text",
|
|
171363
|
-
text
|
|
171364
|
-
}));
|
|
171365
|
-
entries.push({
|
|
171366
|
-
path: `${SCENE_HARNESS_DIR}/.edit-mode.ts`,
|
|
171367
|
-
type: "text",
|
|
171368
|
-
text: `;(globalThis as any).__lecodesSceneEdit = true
|
|
171369
|
-
export {}
|
|
171370
|
-
`
|
|
171371
|
-
});
|
|
171372
|
-
const editorImports = projectPaths.filter((p) => p.endsWith(".editor.ts")).map((p) => `import "${p}"
|
|
171373
|
-
`).join("");
|
|
171374
|
-
const materialPaths = projectPaths.filter((p) => p.endsWith(".material.ts"));
|
|
171375
|
-
const materialImports = materialPaths.map((p, i) => `import __mat${i} from "${p}"
|
|
171376
|
-
`).join("");
|
|
171377
|
-
const materialRegister = materialPaths.length === 0 ? "" : `;(globalThis as any).__lecodesMaterials = { ${materialPaths.map((p, i) => `${JSON.stringify(p)}: __mat${i}`).join(", ")} }
|
|
171378
|
-
`;
|
|
171379
|
-
const sceneImportLines = sceneImports.map((p, i) => `import __scn${i} from "${p}"
|
|
171380
|
-
`).join("");
|
|
171381
|
-
const sceneRegister = sceneImports.length === 0 ? "" : `;(globalThis as any).__lecodesSceneFiles = { ${sceneImports.map((p, i) => `${JSON.stringify(p)}: __scn${i}`).join(", ")} }
|
|
171382
|
-
`;
|
|
171383
|
-
entries.push({
|
|
171384
|
-
path: SCENE_ENTRY_PATH,
|
|
171385
|
-
type: "text",
|
|
171386
|
-
text: `import "${SCENE_HARNESS_DIR}/.edit-mode.ts"
|
|
171387
|
-
` + materialImports + sceneImportLines + `import "${scenePath}"
|
|
171388
|
-
` + editorImports + `import "${SCENE_HARNESS_DIR}/main.ts"
|
|
171389
|
-
` + materialRegister + sceneRegister
|
|
171390
|
-
});
|
|
171391
|
-
return entries;
|
|
171392
|
-
};
|
|
171393
|
-
|
|
171394
|
-
// ../../sdk/src/compile/fontRegistry.ts
|
|
171395
|
-
var DEFAULT_FONT_REGISTRY_BASE = "https://cdn.le.codes/fonts/v1", fontRegistryBase = () => typeof process !== "undefined" && process.env?.LECODES_FONT_REGISTRY?.trim() || DEFAULT_FONT_REGISTRY_BASE, faceFileName = (id, weight, italic, ext) => `${id}-${weight}${italic ? "-italic" : ""}.${ext}`, LATIN, CYR, OFL = "OFL-1.1", f = (family, category, scripts, weights, italics, defaultWeights) => ({ family, category, scripts, weights, italics, defaultWeights, license: OFL }), FONT_REGISTRY;
|
|
171396
|
-
var init_fontRegistry = __esm(() => {
|
|
171397
|
-
LATIN = ["latin"];
|
|
171398
|
-
CYR = ["latin", "cyrillic"];
|
|
171399
|
-
FONT_REGISTRY = {
|
|
171400
|
-
inter: f("Inter", "sans-serif", CYR, [100, 200, 300, 400, 500, 600, 700, 800, 900], [], [400, 500, 700]),
|
|
171401
|
-
manrope: f("Manrope", "sans-serif", CYR, [200, 300, 400, 500, 600, 700, 800], [], [400, 500, 700]),
|
|
171402
|
-
"golos-text": f("Golos Text", "sans-serif", CYR, [400, 500, 600, 700, 800, 900], [], [400, 500, 700]),
|
|
171403
|
-
onest: f("Onest", "sans-serif", CYR, [100, 200, 300, 400, 500, 600, 700, 800, 900], [], [400, 500, 700]),
|
|
171404
|
-
rubik: f("Rubik", "sans-serif", CYR, [300, 400, 500, 600, 700, 800, 900], [300, 400, 500, 600, 700, 800, 900], [400, 500, 700]),
|
|
171405
|
-
nunito: f("Nunito", "sans-serif", CYR, [200, 300, 400, 500, 600, 700, 800, 900], [400, 700], [400, 600, 700]),
|
|
171406
|
-
montserrat: f("Montserrat", "sans-serif", CYR, [100, 200, 300, 400, 500, 600, 700, 800, 900], [400, 700], [400, 500, 700]),
|
|
171407
|
-
"ibm-plex-sans": f("IBM Plex Sans", "sans-serif", CYR, [100, 200, 300, 400, 500, 600, 700], [400, 700], [400, 500, 700]),
|
|
171408
|
-
"pt-sans": f("PT Sans", "sans-serif", CYR, [400, 700], [400, 700], [400, 700]),
|
|
171409
|
-
jost: f("Jost", "sans-serif", CYR, [100, 200, 300, 400, 500, 600, 700, 800, 900], [400, 700], [400, 500, 700]),
|
|
171410
|
-
"exo-2": f("Exo 2", "sans-serif", CYR, [100, 200, 300, 400, 500, 600, 700, 800, 900], [400, 700], [400, 500, 700]),
|
|
171411
|
-
unbounded: f("Unbounded", "display", CYR, [200, 300, 400, 500, 600, 700, 800, 900], [], [400, 700]),
|
|
171412
|
-
"russo-one": f("Russo One", "display", CYR, [400], [], [400]),
|
|
171413
|
-
oswald: f("Oswald", "display", CYR, [200, 300, 400, 500, 600, 700], [], [400, 700]),
|
|
171414
|
-
comfortaa: f("Comfortaa", "display", CYR, [300, 400, 500, 600, 700], [], [400, 700]),
|
|
171415
|
-
"space-grotesk": f("Space Grotesk", "display", LATIN, [300, 400, 500, 600, 700], [], [400, 700]),
|
|
171416
|
-
"playfair-display": f("Playfair Display", "serif", CYR, [400, 500, 600, 700, 800, 900], [400, 700], [400, 700]),
|
|
171417
|
-
lora: f("Lora", "serif", CYR, [400, 500, 600, 700], [400, 700], [400, 700]),
|
|
171418
|
-
"pt-serif": f("PT Serif", "serif", CYR, [400, 700], [400, 700], [400, 700]),
|
|
171419
|
-
cormorant: f("Cormorant", "serif", CYR, [300, 400, 500, 600, 700], [400, 700], [400, 700]),
|
|
171420
|
-
literata: f("Literata", "serif", CYR, [200, 300, 400, 500, 600, 700, 800, 900], [400, 700], [400, 700]),
|
|
171421
|
-
"jetbrains-mono": f("JetBrains Mono", "monospace", CYR, [100, 200, 300, 400, 500, 600, 700, 800], [400, 700], [400, 700]),
|
|
171422
|
-
"fira-code": f("Fira Code", "monospace", CYR, [300, 400, 500, 600, 700], [], [400, 700]),
|
|
171423
|
-
"ibm-plex-mono": f("IBM Plex Mono", "monospace", CYR, [100, 200, 300, 400, 500, 600, 700], [400, 700], [400, 700]),
|
|
171424
|
-
caveat: f("Caveat", "handwriting", CYR, [400, 500, 600, 700], [], [400, 700]),
|
|
171425
|
-
pacifico: f("Pacifico", "handwriting", CYR, [400], [], [400])
|
|
171426
|
-
};
|
|
171427
|
-
});
|
|
171428
|
-
|
|
171429
|
-
// ../../sdk/src/compile/sfnt.ts
|
|
171430
|
-
var TAG_TTF = 65536, TAG_OTTO = 1330926671, TAG_TTCF = 1953784678, TAG_WOFF = 2001684038, TAG_WOF2 = 2001684018, parseSfnt = (bytes) => {
|
|
171431
|
-
const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
171432
|
-
if (bytes.byteLength < 12)
|
|
171433
|
-
throw new Error("not a font file (truncated)");
|
|
171434
|
-
const version = dv.getUint32(0);
|
|
171435
|
-
if (version === TAG_TTCF)
|
|
171436
|
-
throw new Error("font collections (.ttc) aren't supported — export a single face");
|
|
171437
|
-
if (version === TAG_WOFF || version === TAG_WOF2)
|
|
171438
|
-
throw new Error("WOFF/WOFF2 project fonts aren't supported — use the .ttf/.otf");
|
|
171439
|
-
if (version !== TAG_TTF && version !== TAG_OTTO)
|
|
171440
|
-
throw new Error("not a TTF/OTF font file");
|
|
171441
|
-
const numTables = dv.getUint16(4);
|
|
171442
|
-
const tables = new Map;
|
|
171443
|
-
for (let i = 0;i < numTables; i++) {
|
|
171444
|
-
const rec = 12 + i * 16;
|
|
171445
|
-
if (rec + 16 > bytes.byteLength)
|
|
171446
|
-
throw new Error("not a font file (truncated table directory)");
|
|
171447
|
-
const tag = String.fromCharCode(bytes[rec], bytes[rec + 1], bytes[rec + 2], bytes[rec + 3]);
|
|
171448
|
-
tables.set(tag, { offset: dv.getUint32(rec + 8), length: dv.getUint32(rec + 12) });
|
|
171449
|
-
}
|
|
171450
|
-
const family = readFamily(dv, bytes, tables.get("name"));
|
|
171451
|
-
if (!family)
|
|
171452
|
-
throw new Error("no family name in the font's `name` table");
|
|
171453
|
-
let weight = 400;
|
|
171454
|
-
let italic = false;
|
|
171455
|
-
const os2 = tables.get("OS/2");
|
|
171456
|
-
if (os2 && os2.offset + 64 <= bytes.byteLength) {
|
|
171457
|
-
weight = dv.getUint16(os2.offset + 4);
|
|
171458
|
-
italic = (dv.getUint16(os2.offset + 62) & 1) === 1;
|
|
171459
|
-
} else {
|
|
171460
|
-
const head = tables.get("head");
|
|
171461
|
-
if (head && head.offset + 46 <= bytes.byteLength) {
|
|
171462
|
-
const macStyle = dv.getUint16(head.offset + 44);
|
|
171463
|
-
if (macStyle & 1)
|
|
171464
|
-
weight = 700;
|
|
171465
|
-
italic = (macStyle & 2) === 2;
|
|
171466
|
-
}
|
|
171467
|
-
}
|
|
171468
|
-
if (weight < 1 || weight > 1000)
|
|
171469
|
-
weight = 400;
|
|
171470
|
-
return { family, weight, italic };
|
|
171471
|
-
}, readFamily = (dv, bytes, name) => {
|
|
171472
|
-
if (!name || name.offset + 6 > bytes.byteLength)
|
|
171473
|
-
return "";
|
|
171474
|
-
const base = name.offset;
|
|
171475
|
-
const count = dv.getUint16(base + 2);
|
|
171476
|
-
const stringOffset = base + dv.getUint16(base + 4);
|
|
171477
|
-
let best = "";
|
|
171478
|
-
let bestScore = -1;
|
|
171479
|
-
for (let i = 0;i < count; i++) {
|
|
171480
|
-
const rec = base + 6 + i * 12;
|
|
171481
|
-
if (rec + 12 > bytes.byteLength)
|
|
171482
|
-
break;
|
|
171483
|
-
const platformID = dv.getUint16(rec);
|
|
171484
|
-
const languageID = dv.getUint16(rec + 4);
|
|
171485
|
-
const nameID = dv.getUint16(rec + 6);
|
|
171486
|
-
const length = dv.getUint16(rec + 8);
|
|
171487
|
-
const offset = stringOffset + dv.getUint16(rec + 10);
|
|
171488
|
-
if (nameID !== 16 && nameID !== 1)
|
|
171489
|
-
continue;
|
|
171490
|
-
if (offset + length > bytes.byteLength)
|
|
171491
|
-
continue;
|
|
171492
|
-
let score = nameID === 16 ? 100 : 0;
|
|
171493
|
-
if (platformID === 3)
|
|
171494
|
-
score += languageID === 1033 ? 30 : 20;
|
|
171495
|
-
else if (platformID === 0)
|
|
171496
|
-
score += 10;
|
|
171497
|
-
if (score <= bestScore)
|
|
171498
|
-
continue;
|
|
171499
|
-
const text = platformID === 1 ? decodeLatin1(bytes, offset, length) : decodeUtf16be(bytes, offset, length);
|
|
171500
|
-
if (!text.trim())
|
|
171501
|
-
continue;
|
|
171502
|
-
best = text.trim();
|
|
171503
|
-
bestScore = score;
|
|
171504
|
-
}
|
|
171505
|
-
return best;
|
|
171506
|
-
}, decodeUtf16be = (bytes, offset, length) => {
|
|
171507
|
-
let out = "";
|
|
171508
|
-
for (let i = 0;i + 1 < length; i += 2)
|
|
171509
|
-
out += String.fromCharCode(bytes[offset + i] << 8 | bytes[offset + i + 1]);
|
|
171510
|
-
return out;
|
|
171511
|
-
}, decodeLatin1 = (bytes, offset, length) => {
|
|
171512
|
-
let out = "";
|
|
171513
|
-
for (let i = 0;i < length; i++)
|
|
171514
|
-
out += String.fromCharCode(bytes[offset + i]);
|
|
171515
|
-
return out;
|
|
171516
|
-
};
|
|
171517
|
-
|
|
171518
|
-
// ../../sdk/src/compile/fontMacro.ts
|
|
171519
|
-
var FONT_ID, CODE_FILE, DECLARES_FONT, isIdentStart = (c) => c >= "A" && c <= "Z" || c >= "a" && c <= "z" || c === "_" || c === "$", isIdentPart = (c) => isIdentStart(c) || c >= "0" && c <= "9", skipString2 = (src, start) => {
|
|
171336
|
+
// ../../sdk/src/compile/assetIconMacro.ts
|
|
171337
|
+
var ICON_ID, isIdentStart = (c) => c >= "A" && c <= "Z" || c >= "a" && c <= "z" || c === "_" || c === "$", isIdentPart = (c) => isIdentStart(c) || c >= "0" && c <= "9", skipString2 = (src, start) => {
|
|
171520
171338
|
const quote = src[start];
|
|
171521
171339
|
const n = src.length;
|
|
171522
171340
|
let i = start + 1;
|
|
@@ -171626,12 +171444,7 @@ var FONT_ID, CODE_FILE, DECLARES_FONT, isIdentStart = (c) => c >= "A" && c <= "Z
|
|
|
171626
171444
|
if (inner.includes(q) && !inner.includes("\\" + q))
|
|
171627
171445
|
return null;
|
|
171628
171446
|
return inner.replace(/\\(.)/g, "$1");
|
|
171629
|
-
},
|
|
171630
|
-
let k = i - 1;
|
|
171631
|
-
while (k >= 0 && /\s/.test(src[k]))
|
|
171632
|
-
k--;
|
|
171633
|
-
return k >= 0 ? src[k] : "";
|
|
171634
|
-
}, scanFontCalls = (source) => {
|
|
171447
|
+
}, scanAssetIconCalls = (source) => {
|
|
171635
171448
|
const calls = [];
|
|
171636
171449
|
const n = source.length;
|
|
171637
171450
|
let i = 0;
|
|
@@ -171659,7 +171472,7 @@ var FONT_ID, CODE_FILE, DECLARES_FONT, isIdentStart = (c) => c >= "A" && c <= "Z
|
|
|
171659
171472
|
let j = i + 1;
|
|
171660
171473
|
while (j < n && isIdentPart(source[j]))
|
|
171661
171474
|
j++;
|
|
171662
|
-
if (source.slice(i, j) === "
|
|
171475
|
+
if (source.slice(i, j) === "assetIcon" && prevNonSpace(source, i) !== ".") {
|
|
171663
171476
|
const call = parseCall(source, i, j);
|
|
171664
171477
|
if (call) {
|
|
171665
171478
|
calls.push(call);
|
|
@@ -171673,6 +171486,11 @@ var FONT_ID, CODE_FILE, DECLARES_FONT, isIdentStart = (c) => c >= "A" && c <= "Z
|
|
|
171673
171486
|
i++;
|
|
171674
171487
|
}
|
|
171675
171488
|
return calls;
|
|
171489
|
+
}, prevNonSpace = (src, i) => {
|
|
171490
|
+
let k = i - 1;
|
|
171491
|
+
while (k >= 0 && /\s/.test(src[k]))
|
|
171492
|
+
k--;
|
|
171493
|
+
return k >= 0 ? src[k] : "";
|
|
171676
171494
|
}, parseCall = (src, start, identEnd) => {
|
|
171677
171495
|
const n = src.length;
|
|
171678
171496
|
let i = identEnd;
|
|
@@ -171683,9 +171501,621 @@ var FONT_ID, CODE_FILE, DECLARES_FONT, isIdentStart = (c) => c >= "A" && c <= "Z
|
|
|
171683
171501
|
const close = matchParen(src, i);
|
|
171684
171502
|
if (close === -1)
|
|
171685
171503
|
return null;
|
|
171686
|
-
const call = { start, end: close + 1,
|
|
171504
|
+
const call = { start, end: close + 1, id: "", pack: "", name: "" };
|
|
171687
171505
|
const args = splitTopLevel(src.slice(i + 1, close), ",");
|
|
171688
|
-
const
|
|
171506
|
+
const id = stringLiteralValue(args[0] ?? "");
|
|
171507
|
+
if (id === null) {
|
|
171508
|
+
call.error = `assetIcon(...) needs a string-literal id like "lucide:bell" (got \`${(args[0] ?? "").trim() || "()"}\`).`;
|
|
171509
|
+
return call;
|
|
171510
|
+
}
|
|
171511
|
+
const m = ICON_ID.exec(id);
|
|
171512
|
+
if (!m) {
|
|
171513
|
+
call.error = `assetIcon("${id}"): id must be "pack:name", lowercase [a-z0-9-] (e.g. "lucide:bell").`;
|
|
171514
|
+
return call;
|
|
171515
|
+
}
|
|
171516
|
+
call.id = id;
|
|
171517
|
+
call.pack = m[1];
|
|
171518
|
+
call.name = m[2];
|
|
171519
|
+
const optsText = args[1]?.trim();
|
|
171520
|
+
if (optsText) {
|
|
171521
|
+
const color = extractColor(optsText);
|
|
171522
|
+
if (color.error) {
|
|
171523
|
+
call.error = color.error;
|
|
171524
|
+
return call;
|
|
171525
|
+
}
|
|
171526
|
+
if (color.bake !== undefined)
|
|
171527
|
+
call.bake = color.bake;
|
|
171528
|
+
else if (color.tint !== undefined)
|
|
171529
|
+
call.tint = color.tint;
|
|
171530
|
+
}
|
|
171531
|
+
return call;
|
|
171532
|
+
}, extractColor = (optsText) => {
|
|
171533
|
+
if (!optsText.startsWith("{") || !optsText.endsWith("}")) {
|
|
171534
|
+
return { error: `assetIcon(id, opts): opts must be an object literal like { color: "#5b8cff" }.` };
|
|
171535
|
+
}
|
|
171536
|
+
for (const prop of splitTopLevel(optsText.slice(1, -1), ",")) {
|
|
171537
|
+
const kv = splitTopLevel(prop, ":");
|
|
171538
|
+
if (kv.length < 2)
|
|
171539
|
+
continue;
|
|
171540
|
+
const key = kv[0].trim().replace(/^["'`]|["'`]$/g, "");
|
|
171541
|
+
if (key !== "color")
|
|
171542
|
+
continue;
|
|
171543
|
+
const raw = kv.slice(1).join(":").trim();
|
|
171544
|
+
if (!raw)
|
|
171545
|
+
continue;
|
|
171546
|
+
const literal = stringLiteralValue(raw);
|
|
171547
|
+
return literal !== null ? { bake: literal } : { tint: raw };
|
|
171548
|
+
}
|
|
171549
|
+
return {};
|
|
171550
|
+
}, rewriteAssetIcons = (source, calls, svgById) => {
|
|
171551
|
+
let out = source;
|
|
171552
|
+
for (const call of [...calls].sort((a, b) => b.start - a.start)) {
|
|
171553
|
+
const svg = svgById.get(call.id);
|
|
171554
|
+
if (svg === undefined)
|
|
171555
|
+
continue;
|
|
171556
|
+
out = out.slice(0, call.start) + iconExpr(svg, call) + out.slice(call.end);
|
|
171557
|
+
}
|
|
171558
|
+
return out;
|
|
171559
|
+
}, iconExpr = (svg, call) => {
|
|
171560
|
+
const body = call.bake ? svg.split("currentColor").join(call.bake) : svg;
|
|
171561
|
+
const src = `SvgSource(\`${escapeTemplate(body)}\`)`;
|
|
171562
|
+
return call.tint ? `Object.assign(${src}, { tintColor: (${call.tint}) })` : src;
|
|
171563
|
+
}, escapeTemplate = (s) => s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${"), applyAssetIconMacro = async (entries, resolveSvgs) => {
|
|
171564
|
+
const scanned = entries.filter((e) => e.type === "text" && typeof e.text === "string" && e.text.includes("assetIcon")).map((e) => ({ entry: e, calls: scanAssetIconCalls(e.text) })).filter((s) => s.calls.length > 0);
|
|
171565
|
+
if (scanned.length === 0)
|
|
171566
|
+
return [];
|
|
171567
|
+
const errors = scanned.flatMap((s) => s.calls.filter((c) => c.error).map((c) => c.error));
|
|
171568
|
+
if (errors.length)
|
|
171569
|
+
throw new Error([...new Set(errors)].join(`
|
|
171570
|
+
`));
|
|
171571
|
+
const ids = [...new Set(scanned.flatMap((s) => s.calls.map((c) => c.id)))];
|
|
171572
|
+
const svgById = await resolveSvgs(ids);
|
|
171573
|
+
const missing = ids.filter((id) => !svgById.has(id));
|
|
171574
|
+
if (missing.length)
|
|
171575
|
+
throw new Error(`assetIcon: could not resolve icon(s): ${missing.join(", ")}.`);
|
|
171576
|
+
for (const { entry, calls } of scanned)
|
|
171577
|
+
entry.text = rewriteAssetIcons(entry.text, calls, svgById);
|
|
171578
|
+
return ids;
|
|
171579
|
+
}, buildIconsHeaderValue = (ids) => [...new Set(ids.map((id) => id.split(":")[0]))].sort().join(";"), editDistance = (a, b, cap) => {
|
|
171580
|
+
if (Math.abs(a.length - b.length) > cap)
|
|
171581
|
+
return cap + 1;
|
|
171582
|
+
let prev = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
171583
|
+
for (let i = 1;i <= a.length; i++) {
|
|
171584
|
+
const cur = [i];
|
|
171585
|
+
let rowMin = i;
|
|
171586
|
+
for (let j = 1;j <= b.length; j++) {
|
|
171587
|
+
cur[j] = Math.min(prev[j] + 1, cur[j - 1] + 1, prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
171588
|
+
if (cur[j] < rowMin)
|
|
171589
|
+
rowMin = cur[j];
|
|
171590
|
+
}
|
|
171591
|
+
if (rowMin > cap)
|
|
171592
|
+
return cap + 1;
|
|
171593
|
+
prev = cur;
|
|
171594
|
+
}
|
|
171595
|
+
return prev[b.length];
|
|
171596
|
+
}, suggestIconNames = (want, icons) => {
|
|
171597
|
+
const wantParts = new Set(want.split("-").filter(Boolean));
|
|
171598
|
+
const scored = [];
|
|
171599
|
+
for (const [name, info] of Object.entries(icons)) {
|
|
171600
|
+
const lenDiff = Math.abs(name.length - want.length) / 100;
|
|
171601
|
+
let rank;
|
|
171602
|
+
if (name.includes(want) || want.includes(name))
|
|
171603
|
+
rank = lenDiff;
|
|
171604
|
+
else {
|
|
171605
|
+
const d = editDistance(want, name, 2);
|
|
171606
|
+
if (d <= 2)
|
|
171607
|
+
rank = 1 + d;
|
|
171608
|
+
else if (info.kw?.some((k) => wantParts.has(k)))
|
|
171609
|
+
rank = 4 + lenDiff;
|
|
171610
|
+
else
|
|
171611
|
+
continue;
|
|
171612
|
+
}
|
|
171613
|
+
scored.push({ name, rank });
|
|
171614
|
+
}
|
|
171615
|
+
return scored.sort((a, b) => a.rank - b.rank || (a.name < b.name ? -1 : 1)).slice(0, 5).map((s) => s.name);
|
|
171616
|
+
}, iconNotFoundError = async (pack, name, read, registry) => {
|
|
171617
|
+
const metaRaw = await read(`${pack}/meta.json`);
|
|
171618
|
+
if (metaRaw !== null) {
|
|
171619
|
+
let icons = {};
|
|
171620
|
+
try {
|
|
171621
|
+
icons = JSON.parse(metaRaw).icons ?? {};
|
|
171622
|
+
} catch {}
|
|
171623
|
+
const similar = suggestIconNames(name, icons);
|
|
171624
|
+
const hint = similar.length ? ` Did you mean ${similar.map((s) => `"${pack}:${s}"`).join(", ")}?` : ` No similar name in the "${pack}" pack either.`;
|
|
171625
|
+
return new Error(`assetIcon: icon "${pack}:${name}" not found.${hint}`);
|
|
171626
|
+
}
|
|
171627
|
+
const licRaw = await read("licenses.json");
|
|
171628
|
+
if (licRaw !== null) {
|
|
171629
|
+
try {
|
|
171630
|
+
const packs = Object.keys(JSON.parse(licRaw)).sort();
|
|
171631
|
+
return new Error(`assetIcon: unknown icon pack "${pack}" (in "${pack}:${name}"). Available packs: ${packs.join(", ")}.`);
|
|
171632
|
+
} catch {}
|
|
171633
|
+
}
|
|
171634
|
+
return Object.assign(new Error(`assetIcon: can't reach the icon registry${registry ? ` (${registry})` : ""} to resolve "${pack}:${name}". ` + `Check your connection, or set LECODES_ICON_REGISTRY.`), { transient: true });
|
|
171635
|
+
}, VENDORED_ICON_PATH, collectVendoredIcons = (entries) => {
|
|
171636
|
+
const map = new Map;
|
|
171637
|
+
for (const e of entries) {
|
|
171638
|
+
if (e.type !== "text" || typeof e.text !== "string")
|
|
171639
|
+
continue;
|
|
171640
|
+
const m = VENDORED_ICON_PATH.exec(e.path);
|
|
171641
|
+
if (m)
|
|
171642
|
+
map.set(`${m[1]}:${m[2]}`, e.text);
|
|
171643
|
+
}
|
|
171644
|
+
return map;
|
|
171645
|
+
};
|
|
171646
|
+
var init_assetIconMacro = __esm(() => {
|
|
171647
|
+
ICON_ID = /^([a-z0-9]+(?:-[a-z0-9]+)*):([a-z0-9]+(?:-[a-z0-9]+)*)$/;
|
|
171648
|
+
VENDORED_ICON_PATH = /(?:^|\/)assets\/icons\/([a-z0-9]+(?:-[a-z0-9]+)*)\/([a-z0-9]+(?:-[a-z0-9]+)*)\.svg$/;
|
|
171649
|
+
});
|
|
171650
|
+
|
|
171651
|
+
// ../../sdk/src/compile/compileProject.ts
|
|
171652
|
+
var localAssetBase = (path) => path.split(/[\\/]/).pop() ?? path, localAssetNames = (entries) => {
|
|
171653
|
+
const seen = new Map;
|
|
171654
|
+
for (const e of entries)
|
|
171655
|
+
if (e.type === "resource") {
|
|
171656
|
+
const b = localAssetBase(e.path);
|
|
171657
|
+
seen.set(b, (seen.get(b) ?? 0) + 1);
|
|
171658
|
+
}
|
|
171659
|
+
const names = new Map;
|
|
171660
|
+
for (const e of entries)
|
|
171661
|
+
if (e.type === "resource") {
|
|
171662
|
+
const b = localAssetBase(e.path);
|
|
171663
|
+
names.set(e.path, (seen.get(b) ?? 0) > 1 ? e.path.replace(/^[./]+/, "").replace(/[\\/]/g, "__") : b);
|
|
171664
|
+
}
|
|
171665
|
+
return names;
|
|
171666
|
+
}, entriesToFiles = (entries, opts) => {
|
|
171667
|
+
const files = {};
|
|
171668
|
+
const localNames = localAssetNames(entries);
|
|
171669
|
+
const localAssetId = (nameExpr) => `_creatorFetch.local(${nameExpr})`;
|
|
171670
|
+
for (const e of entries) {
|
|
171671
|
+
if (e.type === "resource") {
|
|
171672
|
+
if (opts.localAssets && !e.remote) {
|
|
171673
|
+
const name = localNames.get(e.path) ?? localAssetBase(e.path);
|
|
171674
|
+
files[e.path] = `export default ("id:" + ${localAssetId(JSON.stringify(name))})`;
|
|
171675
|
+
} else {
|
|
171676
|
+
files[e.path] = `export default "${opts.publicUrl}${e.fileSrc ?? ""}"`;
|
|
171677
|
+
}
|
|
171678
|
+
} else if (e.type === "shader") {
|
|
171679
|
+
const shaders = e.shaders ?? [];
|
|
171680
|
+
if (e.shadersLocal && shaders.length > 0) {
|
|
171681
|
+
const name = shaders[0].src.split(/[\/]/).pop() ?? "";
|
|
171682
|
+
const [prefix, suffix = ".filamat"] = name.split("_opengl");
|
|
171683
|
+
files[e.path] = `export default ("id:" + ${localAssetId(`${JSON.stringify(prefix + "_")} + _creator.backend + ${JSON.stringify(suffix)}`)})`;
|
|
171684
|
+
} else if (shaders.length > 0) {
|
|
171685
|
+
files[e.path] = `export default \`${opts.publicUrl}${shaders[0].src.replace("_opengl", "_${_creator.backend}")}\``;
|
|
171686
|
+
} else {
|
|
171687
|
+
files[e.path] = `export default ""`;
|
|
171688
|
+
}
|
|
171689
|
+
} else if (e.path.toLowerCase().endsWith(".svg")) {
|
|
171690
|
+
files[e.path] = `export default SvgSource(\`${escapeTemplate(e.text ?? "")}\`)`;
|
|
171691
|
+
} else if (e.path.toLowerCase().endsWith(".json")) {
|
|
171692
|
+
files[e.path] = `export default ${e.text?.trim() || "null"}`;
|
|
171693
|
+
} else if (!CODE_EXT.test(e.path)) {
|
|
171694
|
+
files[e.path] = `export default ${JSON.stringify(e.text ?? "")}`;
|
|
171695
|
+
} else {
|
|
171696
|
+
files[e.path] = e.text ?? "";
|
|
171697
|
+
}
|
|
171698
|
+
}
|
|
171699
|
+
return files;
|
|
171700
|
+
}, pluginSpecsOf = (plugins) => {
|
|
171701
|
+
if (Array.isArray(plugins))
|
|
171702
|
+
return Object.fromEntries(plugins.filter((p) => typeof p === "string").map((id) => [id, "*"]));
|
|
171703
|
+
if (plugins && typeof plugins === "object") {
|
|
171704
|
+
return Object.fromEntries(Object.entries(plugins).map(([id, v]) => [id, typeof v === "string" ? v : "*"]));
|
|
171705
|
+
}
|
|
171706
|
+
return {};
|
|
171707
|
+
}, pluginIdsOf = (plugins) => Object.keys(pluginSpecsOf(plugins)), pluginsOfAppJson = (entries) => {
|
|
171708
|
+
const app = entries.find((e) => e.path.replace(/^[./\\]+/, "") === "app.json");
|
|
171709
|
+
if (!app?.text)
|
|
171710
|
+
return [];
|
|
171711
|
+
try {
|
|
171712
|
+
return pluginIdsOf(JSON.parse(app.text).plugins);
|
|
171713
|
+
} catch {
|
|
171714
|
+
return [];
|
|
171715
|
+
}
|
|
171716
|
+
}, pluginsOfBundle = (mapJson) => {
|
|
171717
|
+
const ids = new Set;
|
|
171718
|
+
try {
|
|
171719
|
+
for (const source of JSON.parse(mapJson).sources ?? []) {
|
|
171720
|
+
const m = /\/plugins\/gen\/([^/]+)\/sdk\//.exec(source);
|
|
171721
|
+
if (m)
|
|
171722
|
+
ids.add(m[1]);
|
|
171723
|
+
}
|
|
171724
|
+
} catch {}
|
|
171725
|
+
return [...ids].sort();
|
|
171726
|
+
}, compileProjectWithMap = async (opts) => {
|
|
171727
|
+
const published = clientPublishes(opts.entries);
|
|
171728
|
+
if (published.length)
|
|
171729
|
+
throw new ClientPublishError(published);
|
|
171730
|
+
const entries = stubServerEntries(opts.entries, opts.serverUrl ?? "");
|
|
171731
|
+
const entry = opts.entryOverride ?? detectEntry(entries, scanModulesChisel).entry;
|
|
171732
|
+
if (!entry) {
|
|
171733
|
+
throw new Error("Couldn't find an entrypoint (no .ts/.js project file — name yours main.ts). Pass entryOverride.");
|
|
171734
|
+
}
|
|
171735
|
+
const files = entriesToFiles(entries, opts);
|
|
171736
|
+
const { code, map } = await bundleProjectWithMap(entry, files, opts.format ?? "esm", opts.minify, opts.editor, opts.libraries, opts.sourcemap ?? true);
|
|
171737
|
+
const plugins = opts.plugins ?? pluginsOfAppJson(entries);
|
|
171738
|
+
if (map && opts.onWarning) {
|
|
171739
|
+
const missing = pluginsOfBundle(map).filter((id) => !plugins.includes(id));
|
|
171740
|
+
if (missing.length) {
|
|
171741
|
+
opts.onWarning(`The app uses ${missing.map((id) => `"${id}"`).join(", ")} and app.json does not list ${missing.length === 1 ? "it" : "them"}: a plugin that is not listed is not there — in a shell built from this project and in the browser alike. Add to app.json: "plugins": { ${missing.map((id) => `"${id}": "*"`).join(", ")} }`);
|
|
171742
|
+
}
|
|
171743
|
+
}
|
|
171744
|
+
if (opts.header === false)
|
|
171745
|
+
return { code, map, entry };
|
|
171746
|
+
const header = buildHeader(opts.name, code, { icon: opts.icon, fonts: opts.fontsHeader, icons: opts.iconsHeader, plugins });
|
|
171747
|
+
const headerLines = (header.match(/\n/g) ?? []).length;
|
|
171748
|
+
return { code: header + code, map: map ? offsetSourceMap(map, headerLines) : null, entry };
|
|
171749
|
+
}, compileProject = async (opts) => {
|
|
171750
|
+
const { code, map } = await compileProjectWithMap(opts);
|
|
171751
|
+
return map ? `${code}
|
|
171752
|
+
${inlineSourceMapComment(map)}` : code;
|
|
171753
|
+
};
|
|
171754
|
+
var init_compileProject = __esm(() => {
|
|
171755
|
+
init_bundler();
|
|
171756
|
+
init_detectEntry();
|
|
171757
|
+
init_header();
|
|
171758
|
+
init_serverSplit();
|
|
171759
|
+
init_assetMacro();
|
|
171760
|
+
init_assetIconMacro();
|
|
171761
|
+
});
|
|
171762
|
+
|
|
171763
|
+
// ../../sdk/src/compile/sceneEditor.ts
|
|
171764
|
+
var SCENE_HARNESS_DIR = "./.scene-editor", SCENE_ENTRY_PATH = "./.scene-entry.ts", resolveRelative = (fromPath, spec) => {
|
|
171765
|
+
const segs = (p) => p.split("/").filter((s) => s !== "" && s !== ".");
|
|
171766
|
+
const out = segs(fromPath).slice(0, -1);
|
|
171767
|
+
for (const seg of segs(spec)) {
|
|
171768
|
+
if (seg === "..")
|
|
171769
|
+
out.pop();
|
|
171770
|
+
else
|
|
171771
|
+
out.push(seg);
|
|
171772
|
+
}
|
|
171773
|
+
return "./" + out.join("/");
|
|
171774
|
+
}, sceneImportsOf = (sceneText, scenePath, projectPaths) => {
|
|
171775
|
+
const known = new Set(projectPaths);
|
|
171776
|
+
const out = [];
|
|
171777
|
+
const re = /^\s*import\s+[A-Za-z_$][\w$]*\s+from\s+["']([^"']+\.scene(?:\.ts)?)["']/gm;
|
|
171778
|
+
for (const m of sceneText.matchAll(re)) {
|
|
171779
|
+
const spec = m[1];
|
|
171780
|
+
if (!spec.startsWith("."))
|
|
171781
|
+
continue;
|
|
171782
|
+
let p = resolveRelative(scenePath, spec);
|
|
171783
|
+
if (!p.endsWith(".ts"))
|
|
171784
|
+
p += ".ts";
|
|
171785
|
+
if (known.has(p) && !out.includes(p))
|
|
171786
|
+
out.push(p);
|
|
171787
|
+
}
|
|
171788
|
+
return out;
|
|
171789
|
+
}, sceneEditorEntries = (scenePath, harness, projectPaths = [], sceneImports = []) => {
|
|
171790
|
+
const entries = Object.entries(harness).map(([name, text]) => ({
|
|
171791
|
+
path: `${SCENE_HARNESS_DIR}/${name}`,
|
|
171792
|
+
type: "text",
|
|
171793
|
+
text
|
|
171794
|
+
}));
|
|
171795
|
+
entries.push({
|
|
171796
|
+
path: `${SCENE_HARNESS_DIR}/.edit-mode.ts`,
|
|
171797
|
+
type: "text",
|
|
171798
|
+
text: `;(globalThis as any).__lecodesSceneEdit = true
|
|
171799
|
+
export {}
|
|
171800
|
+
`
|
|
171801
|
+
});
|
|
171802
|
+
const editorImports = projectPaths.filter((p) => p.endsWith(".editor.ts")).map((p) => `import "${p}"
|
|
171803
|
+
`).join("");
|
|
171804
|
+
const materialPaths = projectPaths.filter((p) => p.endsWith(".material.ts"));
|
|
171805
|
+
const materialImports = materialPaths.map((p, i) => `import __mat${i} from "${p}"
|
|
171806
|
+
`).join("");
|
|
171807
|
+
const materialRegister = materialPaths.length === 0 ? "" : `;(globalThis as any).__lecodesMaterials = { ${materialPaths.map((p, i) => `${JSON.stringify(p)}: __mat${i}`).join(", ")} }
|
|
171808
|
+
`;
|
|
171809
|
+
const sceneImportLines = sceneImports.map((p, i) => `import __scn${i} from "${p}"
|
|
171810
|
+
`).join("");
|
|
171811
|
+
const sceneRegister = sceneImports.length === 0 ? "" : `;(globalThis as any).__lecodesSceneFiles = { ${sceneImports.map((p, i) => `${JSON.stringify(p)}: __scn${i}`).join(", ")} }
|
|
171812
|
+
`;
|
|
171813
|
+
entries.push({
|
|
171814
|
+
path: SCENE_ENTRY_PATH,
|
|
171815
|
+
type: "text",
|
|
171816
|
+
text: `import "${SCENE_HARNESS_DIR}/.edit-mode.ts"
|
|
171817
|
+
` + materialImports + sceneImportLines + `import "${scenePath}"
|
|
171818
|
+
` + editorImports + `import "${SCENE_HARNESS_DIR}/main.ts"
|
|
171819
|
+
` + materialRegister + sceneRegister
|
|
171820
|
+
});
|
|
171821
|
+
return entries;
|
|
171822
|
+
};
|
|
171823
|
+
|
|
171824
|
+
// ../../sdk/src/compile/fontRegistry.ts
|
|
171825
|
+
var DEFAULT_FONT_REGISTRY_BASE = "https://cdn.le.codes/fonts/v1", fontRegistryBase = () => typeof process !== "undefined" && process.env?.LECODES_FONT_REGISTRY?.trim() || DEFAULT_FONT_REGISTRY_BASE, faceFileName = (id, weight, italic, ext) => `${id}-${weight}${italic ? "-italic" : ""}.${ext}`, LATIN, CYR, OFL = "OFL-1.1", f = (family, category, scripts, weights, italics, defaultWeights) => ({ family, category, scripts, weights, italics, defaultWeights, license: OFL }), FONT_REGISTRY;
|
|
171826
|
+
var init_fontRegistry = __esm(() => {
|
|
171827
|
+
LATIN = ["latin"];
|
|
171828
|
+
CYR = ["latin", "cyrillic"];
|
|
171829
|
+
FONT_REGISTRY = {
|
|
171830
|
+
inter: f("Inter", "sans-serif", CYR, [100, 200, 300, 400, 500, 600, 700, 800, 900], [], [400, 500, 700]),
|
|
171831
|
+
manrope: f("Manrope", "sans-serif", CYR, [200, 300, 400, 500, 600, 700, 800], [], [400, 500, 700]),
|
|
171832
|
+
"golos-text": f("Golos Text", "sans-serif", CYR, [400, 500, 600, 700, 800, 900], [], [400, 500, 700]),
|
|
171833
|
+
onest: f("Onest", "sans-serif", CYR, [100, 200, 300, 400, 500, 600, 700, 800, 900], [], [400, 500, 700]),
|
|
171834
|
+
rubik: f("Rubik", "sans-serif", CYR, [300, 400, 500, 600, 700, 800, 900], [300, 400, 500, 600, 700, 800, 900], [400, 500, 700]),
|
|
171835
|
+
nunito: f("Nunito", "sans-serif", CYR, [200, 300, 400, 500, 600, 700, 800, 900], [400, 700], [400, 600, 700]),
|
|
171836
|
+
montserrat: f("Montserrat", "sans-serif", CYR, [100, 200, 300, 400, 500, 600, 700, 800, 900], [400, 700], [400, 500, 700]),
|
|
171837
|
+
"ibm-plex-sans": f("IBM Plex Sans", "sans-serif", CYR, [100, 200, 300, 400, 500, 600, 700], [400, 700], [400, 500, 700]),
|
|
171838
|
+
"pt-sans": f("PT Sans", "sans-serif", CYR, [400, 700], [400, 700], [400, 700]),
|
|
171839
|
+
jost: f("Jost", "sans-serif", CYR, [100, 200, 300, 400, 500, 600, 700, 800, 900], [400, 700], [400, 500, 700]),
|
|
171840
|
+
"exo-2": f("Exo 2", "sans-serif", CYR, [100, 200, 300, 400, 500, 600, 700, 800, 900], [400, 700], [400, 500, 700]),
|
|
171841
|
+
unbounded: f("Unbounded", "display", CYR, [200, 300, 400, 500, 600, 700, 800, 900], [], [400, 700]),
|
|
171842
|
+
"russo-one": f("Russo One", "display", CYR, [400], [], [400]),
|
|
171843
|
+
oswald: f("Oswald", "display", CYR, [200, 300, 400, 500, 600, 700], [], [400, 700]),
|
|
171844
|
+
comfortaa: f("Comfortaa", "display", CYR, [300, 400, 500, 600, 700], [], [400, 700]),
|
|
171845
|
+
"space-grotesk": f("Space Grotesk", "display", LATIN, [300, 400, 500, 600, 700], [], [400, 700]),
|
|
171846
|
+
"playfair-display": f("Playfair Display", "serif", CYR, [400, 500, 600, 700, 800, 900], [400, 700], [400, 700]),
|
|
171847
|
+
lora: f("Lora", "serif", CYR, [400, 500, 600, 700], [400, 700], [400, 700]),
|
|
171848
|
+
"pt-serif": f("PT Serif", "serif", CYR, [400, 700], [400, 700], [400, 700]),
|
|
171849
|
+
cormorant: f("Cormorant", "serif", CYR, [300, 400, 500, 600, 700], [400, 700], [400, 700]),
|
|
171850
|
+
literata: f("Literata", "serif", CYR, [200, 300, 400, 500, 600, 700, 800, 900], [400, 700], [400, 700]),
|
|
171851
|
+
"jetbrains-mono": f("JetBrains Mono", "monospace", CYR, [100, 200, 300, 400, 500, 600, 700, 800], [400, 700], [400, 700]),
|
|
171852
|
+
"fira-code": f("Fira Code", "monospace", CYR, [300, 400, 500, 600, 700], [], [400, 700]),
|
|
171853
|
+
"ibm-plex-mono": f("IBM Plex Mono", "monospace", CYR, [100, 200, 300, 400, 500, 600, 700], [400, 700], [400, 700]),
|
|
171854
|
+
caveat: f("Caveat", "handwriting", CYR, [400, 500, 600, 700], [], [400, 700]),
|
|
171855
|
+
pacifico: f("Pacifico", "handwriting", CYR, [400], [], [400])
|
|
171856
|
+
};
|
|
171857
|
+
});
|
|
171858
|
+
|
|
171859
|
+
// ../../sdk/src/compile/sfnt.ts
|
|
171860
|
+
var TAG_TTF = 65536, TAG_OTTO = 1330926671, TAG_TTCF = 1953784678, TAG_WOFF = 2001684038, TAG_WOF2 = 2001684018, parseSfnt = (bytes) => {
|
|
171861
|
+
const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
171862
|
+
if (bytes.byteLength < 12)
|
|
171863
|
+
throw new Error("not a font file (truncated)");
|
|
171864
|
+
const version = dv.getUint32(0);
|
|
171865
|
+
if (version === TAG_TTCF)
|
|
171866
|
+
throw new Error("font collections (.ttc) aren't supported — export a single face");
|
|
171867
|
+
if (version === TAG_WOFF || version === TAG_WOF2)
|
|
171868
|
+
throw new Error("WOFF/WOFF2 project fonts aren't supported — use the .ttf/.otf");
|
|
171869
|
+
if (version !== TAG_TTF && version !== TAG_OTTO)
|
|
171870
|
+
throw new Error("not a TTF/OTF font file");
|
|
171871
|
+
const numTables = dv.getUint16(4);
|
|
171872
|
+
const tables = new Map;
|
|
171873
|
+
for (let i = 0;i < numTables; i++) {
|
|
171874
|
+
const rec = 12 + i * 16;
|
|
171875
|
+
if (rec + 16 > bytes.byteLength)
|
|
171876
|
+
throw new Error("not a font file (truncated table directory)");
|
|
171877
|
+
const tag = String.fromCharCode(bytes[rec], bytes[rec + 1], bytes[rec + 2], bytes[rec + 3]);
|
|
171878
|
+
tables.set(tag, { offset: dv.getUint32(rec + 8), length: dv.getUint32(rec + 12) });
|
|
171879
|
+
}
|
|
171880
|
+
const family = readFamily(dv, bytes, tables.get("name"));
|
|
171881
|
+
if (!family)
|
|
171882
|
+
throw new Error("no family name in the font's `name` table");
|
|
171883
|
+
let weight = 400;
|
|
171884
|
+
let italic = false;
|
|
171885
|
+
const os2 = tables.get("OS/2");
|
|
171886
|
+
if (os2 && os2.offset + 64 <= bytes.byteLength) {
|
|
171887
|
+
weight = dv.getUint16(os2.offset + 4);
|
|
171888
|
+
italic = (dv.getUint16(os2.offset + 62) & 1) === 1;
|
|
171889
|
+
} else {
|
|
171890
|
+
const head = tables.get("head");
|
|
171891
|
+
if (head && head.offset + 46 <= bytes.byteLength) {
|
|
171892
|
+
const macStyle = dv.getUint16(head.offset + 44);
|
|
171893
|
+
if (macStyle & 1)
|
|
171894
|
+
weight = 700;
|
|
171895
|
+
italic = (macStyle & 2) === 2;
|
|
171896
|
+
}
|
|
171897
|
+
}
|
|
171898
|
+
if (weight < 1 || weight > 1000)
|
|
171899
|
+
weight = 400;
|
|
171900
|
+
return { family, weight, italic };
|
|
171901
|
+
}, readFamily = (dv, bytes, name) => {
|
|
171902
|
+
if (!name || name.offset + 6 > bytes.byteLength)
|
|
171903
|
+
return "";
|
|
171904
|
+
const base = name.offset;
|
|
171905
|
+
const count = dv.getUint16(base + 2);
|
|
171906
|
+
const stringOffset = base + dv.getUint16(base + 4);
|
|
171907
|
+
let best = "";
|
|
171908
|
+
let bestScore = -1;
|
|
171909
|
+
for (let i = 0;i < count; i++) {
|
|
171910
|
+
const rec = base + 6 + i * 12;
|
|
171911
|
+
if (rec + 12 > bytes.byteLength)
|
|
171912
|
+
break;
|
|
171913
|
+
const platformID = dv.getUint16(rec);
|
|
171914
|
+
const languageID = dv.getUint16(rec + 4);
|
|
171915
|
+
const nameID = dv.getUint16(rec + 6);
|
|
171916
|
+
const length = dv.getUint16(rec + 8);
|
|
171917
|
+
const offset = stringOffset + dv.getUint16(rec + 10);
|
|
171918
|
+
if (nameID !== 16 && nameID !== 1)
|
|
171919
|
+
continue;
|
|
171920
|
+
if (offset + length > bytes.byteLength)
|
|
171921
|
+
continue;
|
|
171922
|
+
let score = nameID === 16 ? 100 : 0;
|
|
171923
|
+
if (platformID === 3)
|
|
171924
|
+
score += languageID === 1033 ? 30 : 20;
|
|
171925
|
+
else if (platformID === 0)
|
|
171926
|
+
score += 10;
|
|
171927
|
+
if (score <= bestScore)
|
|
171928
|
+
continue;
|
|
171929
|
+
const text = platformID === 1 ? decodeLatin1(bytes, offset, length) : decodeUtf16be(bytes, offset, length);
|
|
171930
|
+
if (!text.trim())
|
|
171931
|
+
continue;
|
|
171932
|
+
best = text.trim();
|
|
171933
|
+
bestScore = score;
|
|
171934
|
+
}
|
|
171935
|
+
return best;
|
|
171936
|
+
}, decodeUtf16be = (bytes, offset, length) => {
|
|
171937
|
+
let out = "";
|
|
171938
|
+
for (let i = 0;i + 1 < length; i += 2)
|
|
171939
|
+
out += String.fromCharCode(bytes[offset + i] << 8 | bytes[offset + i + 1]);
|
|
171940
|
+
return out;
|
|
171941
|
+
}, decodeLatin1 = (bytes, offset, length) => {
|
|
171942
|
+
let out = "";
|
|
171943
|
+
for (let i = 0;i < length; i++)
|
|
171944
|
+
out += String.fromCharCode(bytes[offset + i]);
|
|
171945
|
+
return out;
|
|
171946
|
+
};
|
|
171947
|
+
|
|
171948
|
+
// ../../sdk/src/compile/fontMacro.ts
|
|
171949
|
+
var FONT_ID, CODE_FILE, DECLARES_FONT, isIdentStart2 = (c) => c >= "A" && c <= "Z" || c >= "a" && c <= "z" || c === "_" || c === "$", isIdentPart2 = (c) => isIdentStart2(c) || c >= "0" && c <= "9", skipString3 = (src, start) => {
|
|
171950
|
+
const quote = src[start];
|
|
171951
|
+
const n = src.length;
|
|
171952
|
+
let i = start + 1;
|
|
171953
|
+
while (i < n) {
|
|
171954
|
+
const c = src[i];
|
|
171955
|
+
if (c === "\\") {
|
|
171956
|
+
i += 2;
|
|
171957
|
+
continue;
|
|
171958
|
+
}
|
|
171959
|
+
if (quote === "`" && c === "$" && src[i + 1] === "{") {
|
|
171960
|
+
i = skipBraces2(src, i + 1);
|
|
171961
|
+
continue;
|
|
171962
|
+
}
|
|
171963
|
+
if (c === quote)
|
|
171964
|
+
return i + 1;
|
|
171965
|
+
i++;
|
|
171966
|
+
}
|
|
171967
|
+
return n;
|
|
171968
|
+
}, skipBraces2 = (src, open) => {
|
|
171969
|
+
const n = src.length;
|
|
171970
|
+
let depth = 0;
|
|
171971
|
+
let i = open;
|
|
171972
|
+
while (i < n) {
|
|
171973
|
+
const c = src[i];
|
|
171974
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
171975
|
+
i = skipString3(src, i);
|
|
171976
|
+
continue;
|
|
171977
|
+
}
|
|
171978
|
+
if (c === "{")
|
|
171979
|
+
depth++;
|
|
171980
|
+
else if (c === "}") {
|
|
171981
|
+
depth--;
|
|
171982
|
+
if (depth === 0)
|
|
171983
|
+
return i + 1;
|
|
171984
|
+
}
|
|
171985
|
+
i++;
|
|
171986
|
+
}
|
|
171987
|
+
return n;
|
|
171988
|
+
}, matchParen2 = (src, open) => {
|
|
171989
|
+
const n = src.length;
|
|
171990
|
+
let depth = 0;
|
|
171991
|
+
let i = open;
|
|
171992
|
+
while (i < n) {
|
|
171993
|
+
const c = src[i];
|
|
171994
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
171995
|
+
i = skipString3(src, i);
|
|
171996
|
+
continue;
|
|
171997
|
+
}
|
|
171998
|
+
if (c === "/" && src[i + 1] === "/") {
|
|
171999
|
+
i += 2;
|
|
172000
|
+
while (i < n && src[i] !== `
|
|
172001
|
+
`)
|
|
172002
|
+
i++;
|
|
172003
|
+
continue;
|
|
172004
|
+
}
|
|
172005
|
+
if (c === "/" && src[i + 1] === "*") {
|
|
172006
|
+
i += 2;
|
|
172007
|
+
while (i < n && !(src[i] === "*" && src[i + 1] === "/"))
|
|
172008
|
+
i++;
|
|
172009
|
+
i += 2;
|
|
172010
|
+
continue;
|
|
172011
|
+
}
|
|
172012
|
+
if (c === "(")
|
|
172013
|
+
depth++;
|
|
172014
|
+
else if (c === ")") {
|
|
172015
|
+
depth--;
|
|
172016
|
+
if (depth === 0)
|
|
172017
|
+
return i;
|
|
172018
|
+
}
|
|
172019
|
+
i++;
|
|
172020
|
+
}
|
|
172021
|
+
return -1;
|
|
172022
|
+
}, splitTopLevel2 = (text, delim) => {
|
|
172023
|
+
const parts = [];
|
|
172024
|
+
const n = text.length;
|
|
172025
|
+
let depth = 0;
|
|
172026
|
+
let last = 0;
|
|
172027
|
+
let i = 0;
|
|
172028
|
+
while (i < n) {
|
|
172029
|
+
const c = text[i];
|
|
172030
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
172031
|
+
i = skipString3(text, i);
|
|
172032
|
+
continue;
|
|
172033
|
+
}
|
|
172034
|
+
if (c === "(" || c === "[" || c === "{")
|
|
172035
|
+
depth++;
|
|
172036
|
+
else if (c === ")" || c === "]" || c === "}")
|
|
172037
|
+
depth = Math.max(0, depth - 1);
|
|
172038
|
+
else if (depth === 0 && c === delim) {
|
|
172039
|
+
parts.push(text.slice(last, i));
|
|
172040
|
+
last = i + 1;
|
|
172041
|
+
}
|
|
172042
|
+
i++;
|
|
172043
|
+
}
|
|
172044
|
+
parts.push(text.slice(last));
|
|
172045
|
+
return parts;
|
|
172046
|
+
}, stringLiteralValue2 = (text) => {
|
|
172047
|
+
const t = text.trim();
|
|
172048
|
+
if (t.length < 2)
|
|
172049
|
+
return null;
|
|
172050
|
+
const q = t[0];
|
|
172051
|
+
if (q !== '"' && q !== "'" && q !== "`" || t[t.length - 1] !== q)
|
|
172052
|
+
return null;
|
|
172053
|
+
const inner = t.slice(1, -1);
|
|
172054
|
+
if (q === "`" && inner.includes("${"))
|
|
172055
|
+
return null;
|
|
172056
|
+
if (inner.includes(q) && !inner.includes("\\" + q))
|
|
172057
|
+
return null;
|
|
172058
|
+
return inner.replace(/\\(.)/g, "$1");
|
|
172059
|
+
}, prevNonSpace2 = (src, i) => {
|
|
172060
|
+
let k = i - 1;
|
|
172061
|
+
while (k >= 0 && /\s/.test(src[k]))
|
|
172062
|
+
k--;
|
|
172063
|
+
return k >= 0 ? src[k] : "";
|
|
172064
|
+
}, scanFontCalls = (source) => {
|
|
172065
|
+
const calls = [];
|
|
172066
|
+
const n = source.length;
|
|
172067
|
+
let i = 0;
|
|
172068
|
+
while (i < n) {
|
|
172069
|
+
const c = source[i];
|
|
172070
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
172071
|
+
i = skipString3(source, i);
|
|
172072
|
+
continue;
|
|
172073
|
+
}
|
|
172074
|
+
if (c === "/" && source[i + 1] === "/") {
|
|
172075
|
+
i += 2;
|
|
172076
|
+
while (i < n && source[i] !== `
|
|
172077
|
+
`)
|
|
172078
|
+
i++;
|
|
172079
|
+
continue;
|
|
172080
|
+
}
|
|
172081
|
+
if (c === "/" && source[i + 1] === "*") {
|
|
172082
|
+
i += 2;
|
|
172083
|
+
while (i < n && !(source[i] === "*" && source[i + 1] === "/"))
|
|
172084
|
+
i++;
|
|
172085
|
+
i += 2;
|
|
172086
|
+
continue;
|
|
172087
|
+
}
|
|
172088
|
+
if (isIdentStart2(c)) {
|
|
172089
|
+
let j = i + 1;
|
|
172090
|
+
while (j < n && isIdentPart2(source[j]))
|
|
172091
|
+
j++;
|
|
172092
|
+
if (source.slice(i, j) === "font" && prevNonSpace2(source, i) !== ".") {
|
|
172093
|
+
const call = parseCall2(source, i, j);
|
|
172094
|
+
if (call) {
|
|
172095
|
+
calls.push(call);
|
|
172096
|
+
i = call.end;
|
|
172097
|
+
continue;
|
|
172098
|
+
}
|
|
172099
|
+
}
|
|
172100
|
+
i = j;
|
|
172101
|
+
continue;
|
|
172102
|
+
}
|
|
172103
|
+
i++;
|
|
172104
|
+
}
|
|
172105
|
+
return calls;
|
|
172106
|
+
}, parseCall2 = (src, start, identEnd) => {
|
|
172107
|
+
const n = src.length;
|
|
172108
|
+
let i = identEnd;
|
|
172109
|
+
while (i < n && /\s/.test(src[i]))
|
|
172110
|
+
i++;
|
|
172111
|
+
if (src[i] !== "(")
|
|
172112
|
+
return null;
|
|
172113
|
+
const close = matchParen2(src, i);
|
|
172114
|
+
if (close === -1)
|
|
172115
|
+
return null;
|
|
172116
|
+
const call = { start, end: close + 1, arg: "", kind: "registry" };
|
|
172117
|
+
const args = splitTopLevel2(src.slice(i + 1, close), ",");
|
|
172118
|
+
const arg = stringLiteralValue2(args[0] ?? "");
|
|
171689
172119
|
if (arg === null) {
|
|
171690
172120
|
call.error = `font(...) needs a string-literal argument — a registry id ("manrope") or a project path ("./fonts/Brand.ttf"). Got \`${(args[0] ?? "").trim() || "()"}\`.`;
|
|
171691
172121
|
return call;
|
|
@@ -171726,10 +172156,10 @@ var FONT_ID, CODE_FILE, DECLARES_FONT, isIdentStart = (c) => c >= "A" && c <= "Z
|
|
|
171726
172156
|
if (!optsText.startsWith("{") || !optsText.endsWith("}")) {
|
|
171727
172157
|
return `font("${call.arg}", opts): opts must be an object literal like { weights: [400, 700] }.`;
|
|
171728
172158
|
}
|
|
171729
|
-
for (const prop of
|
|
172159
|
+
for (const prop of splitTopLevel2(optsText.slice(1, -1), ",")) {
|
|
171730
172160
|
if (!prop.trim())
|
|
171731
172161
|
continue;
|
|
171732
|
-
const kv =
|
|
172162
|
+
const kv = splitTopLevel2(prop, ":");
|
|
171733
172163
|
if (kv.length < 2)
|
|
171734
172164
|
return `font("${call.arg}"): couldn't parse option \`${prop.trim()}\`.`;
|
|
171735
172165
|
const key = kv[0].trim().replace(/^["'`]|["'`]$/g, "");
|
|
@@ -171924,321 +172354,6 @@ var init_fontMacro = __esm(() => {
|
|
|
171924
172354
|
DECLARES_FONT = /\b(?:function|const|let|var|class)\s+font\b|\bimport\s+(?:\{[^}]*\bfont\b[^}]*\}|font)\s+from\b/;
|
|
171925
172355
|
});
|
|
171926
172356
|
|
|
171927
|
-
// ../../sdk/src/compile/assetIconMacro.ts
|
|
171928
|
-
var ICON_ID, isIdentStart2 = (c) => c >= "A" && c <= "Z" || c >= "a" && c <= "z" || c === "_" || c === "$", isIdentPart2 = (c) => isIdentStart2(c) || c >= "0" && c <= "9", skipString3 = (src, start) => {
|
|
171929
|
-
const quote = src[start];
|
|
171930
|
-
const n = src.length;
|
|
171931
|
-
let i = start + 1;
|
|
171932
|
-
while (i < n) {
|
|
171933
|
-
const c = src[i];
|
|
171934
|
-
if (c === "\\") {
|
|
171935
|
-
i += 2;
|
|
171936
|
-
continue;
|
|
171937
|
-
}
|
|
171938
|
-
if (quote === "`" && c === "$" && src[i + 1] === "{") {
|
|
171939
|
-
i = skipBraces2(src, i + 1);
|
|
171940
|
-
continue;
|
|
171941
|
-
}
|
|
171942
|
-
if (c === quote)
|
|
171943
|
-
return i + 1;
|
|
171944
|
-
i++;
|
|
171945
|
-
}
|
|
171946
|
-
return n;
|
|
171947
|
-
}, skipBraces2 = (src, open) => {
|
|
171948
|
-
const n = src.length;
|
|
171949
|
-
let depth = 0;
|
|
171950
|
-
let i = open;
|
|
171951
|
-
while (i < n) {
|
|
171952
|
-
const c = src[i];
|
|
171953
|
-
if (c === '"' || c === "'" || c === "`") {
|
|
171954
|
-
i = skipString3(src, i);
|
|
171955
|
-
continue;
|
|
171956
|
-
}
|
|
171957
|
-
if (c === "{")
|
|
171958
|
-
depth++;
|
|
171959
|
-
else if (c === "}") {
|
|
171960
|
-
depth--;
|
|
171961
|
-
if (depth === 0)
|
|
171962
|
-
return i + 1;
|
|
171963
|
-
}
|
|
171964
|
-
i++;
|
|
171965
|
-
}
|
|
171966
|
-
return n;
|
|
171967
|
-
}, matchParen2 = (src, open) => {
|
|
171968
|
-
const n = src.length;
|
|
171969
|
-
let depth = 0;
|
|
171970
|
-
let i = open;
|
|
171971
|
-
while (i < n) {
|
|
171972
|
-
const c = src[i];
|
|
171973
|
-
if (c === '"' || c === "'" || c === "`") {
|
|
171974
|
-
i = skipString3(src, i);
|
|
171975
|
-
continue;
|
|
171976
|
-
}
|
|
171977
|
-
if (c === "/" && src[i + 1] === "/") {
|
|
171978
|
-
i += 2;
|
|
171979
|
-
while (i < n && src[i] !== `
|
|
171980
|
-
`)
|
|
171981
|
-
i++;
|
|
171982
|
-
continue;
|
|
171983
|
-
}
|
|
171984
|
-
if (c === "/" && src[i + 1] === "*") {
|
|
171985
|
-
i += 2;
|
|
171986
|
-
while (i < n && !(src[i] === "*" && src[i + 1] === "/"))
|
|
171987
|
-
i++;
|
|
171988
|
-
i += 2;
|
|
171989
|
-
continue;
|
|
171990
|
-
}
|
|
171991
|
-
if (c === "(")
|
|
171992
|
-
depth++;
|
|
171993
|
-
else if (c === ")") {
|
|
171994
|
-
depth--;
|
|
171995
|
-
if (depth === 0)
|
|
171996
|
-
return i;
|
|
171997
|
-
}
|
|
171998
|
-
i++;
|
|
171999
|
-
}
|
|
172000
|
-
return -1;
|
|
172001
|
-
}, splitTopLevel2 = (text, delim) => {
|
|
172002
|
-
const parts = [];
|
|
172003
|
-
const n = text.length;
|
|
172004
|
-
let depth = 0;
|
|
172005
|
-
let last = 0;
|
|
172006
|
-
let i = 0;
|
|
172007
|
-
while (i < n) {
|
|
172008
|
-
const c = text[i];
|
|
172009
|
-
if (c === '"' || c === "'" || c === "`") {
|
|
172010
|
-
i = skipString3(text, i);
|
|
172011
|
-
continue;
|
|
172012
|
-
}
|
|
172013
|
-
if (c === "(" || c === "[" || c === "{")
|
|
172014
|
-
depth++;
|
|
172015
|
-
else if (c === ")" || c === "]" || c === "}")
|
|
172016
|
-
depth = Math.max(0, depth - 1);
|
|
172017
|
-
else if (depth === 0 && c === delim) {
|
|
172018
|
-
parts.push(text.slice(last, i));
|
|
172019
|
-
last = i + 1;
|
|
172020
|
-
}
|
|
172021
|
-
i++;
|
|
172022
|
-
}
|
|
172023
|
-
parts.push(text.slice(last));
|
|
172024
|
-
return parts;
|
|
172025
|
-
}, stringLiteralValue2 = (text) => {
|
|
172026
|
-
const t = text.trim();
|
|
172027
|
-
if (t.length < 2)
|
|
172028
|
-
return null;
|
|
172029
|
-
const q = t[0];
|
|
172030
|
-
if (q !== '"' && q !== "'" && q !== "`" || t[t.length - 1] !== q)
|
|
172031
|
-
return null;
|
|
172032
|
-
const inner = t.slice(1, -1);
|
|
172033
|
-
if (q === "`" && inner.includes("${"))
|
|
172034
|
-
return null;
|
|
172035
|
-
if (inner.includes(q) && !inner.includes("\\" + q))
|
|
172036
|
-
return null;
|
|
172037
|
-
return inner.replace(/\\(.)/g, "$1");
|
|
172038
|
-
}, scanAssetIconCalls = (source) => {
|
|
172039
|
-
const calls = [];
|
|
172040
|
-
const n = source.length;
|
|
172041
|
-
let i = 0;
|
|
172042
|
-
while (i < n) {
|
|
172043
|
-
const c = source[i];
|
|
172044
|
-
if (c === '"' || c === "'" || c === "`") {
|
|
172045
|
-
i = skipString3(source, i);
|
|
172046
|
-
continue;
|
|
172047
|
-
}
|
|
172048
|
-
if (c === "/" && source[i + 1] === "/") {
|
|
172049
|
-
i += 2;
|
|
172050
|
-
while (i < n && source[i] !== `
|
|
172051
|
-
`)
|
|
172052
|
-
i++;
|
|
172053
|
-
continue;
|
|
172054
|
-
}
|
|
172055
|
-
if (c === "/" && source[i + 1] === "*") {
|
|
172056
|
-
i += 2;
|
|
172057
|
-
while (i < n && !(source[i] === "*" && source[i + 1] === "/"))
|
|
172058
|
-
i++;
|
|
172059
|
-
i += 2;
|
|
172060
|
-
continue;
|
|
172061
|
-
}
|
|
172062
|
-
if (isIdentStart2(c)) {
|
|
172063
|
-
let j = i + 1;
|
|
172064
|
-
while (j < n && isIdentPart2(source[j]))
|
|
172065
|
-
j++;
|
|
172066
|
-
if (source.slice(i, j) === "assetIcon" && prevNonSpace2(source, i) !== ".") {
|
|
172067
|
-
const call = parseCall2(source, i, j);
|
|
172068
|
-
if (call) {
|
|
172069
|
-
calls.push(call);
|
|
172070
|
-
i = call.end;
|
|
172071
|
-
continue;
|
|
172072
|
-
}
|
|
172073
|
-
}
|
|
172074
|
-
i = j;
|
|
172075
|
-
continue;
|
|
172076
|
-
}
|
|
172077
|
-
i++;
|
|
172078
|
-
}
|
|
172079
|
-
return calls;
|
|
172080
|
-
}, prevNonSpace2 = (src, i) => {
|
|
172081
|
-
let k = i - 1;
|
|
172082
|
-
while (k >= 0 && /\s/.test(src[k]))
|
|
172083
|
-
k--;
|
|
172084
|
-
return k >= 0 ? src[k] : "";
|
|
172085
|
-
}, parseCall2 = (src, start, identEnd) => {
|
|
172086
|
-
const n = src.length;
|
|
172087
|
-
let i = identEnd;
|
|
172088
|
-
while (i < n && /\s/.test(src[i]))
|
|
172089
|
-
i++;
|
|
172090
|
-
if (src[i] !== "(")
|
|
172091
|
-
return null;
|
|
172092
|
-
const close = matchParen2(src, i);
|
|
172093
|
-
if (close === -1)
|
|
172094
|
-
return null;
|
|
172095
|
-
const call = { start, end: close + 1, id: "", pack: "", name: "" };
|
|
172096
|
-
const args = splitTopLevel2(src.slice(i + 1, close), ",");
|
|
172097
|
-
const id = stringLiteralValue2(args[0] ?? "");
|
|
172098
|
-
if (id === null) {
|
|
172099
|
-
call.error = `assetIcon(...) needs a string-literal id like "lucide:bell" (got \`${(args[0] ?? "").trim() || "()"}\`).`;
|
|
172100
|
-
return call;
|
|
172101
|
-
}
|
|
172102
|
-
const m = ICON_ID.exec(id);
|
|
172103
|
-
if (!m) {
|
|
172104
|
-
call.error = `assetIcon("${id}"): id must be "pack:name", lowercase [a-z0-9-] (e.g. "lucide:bell").`;
|
|
172105
|
-
return call;
|
|
172106
|
-
}
|
|
172107
|
-
call.id = id;
|
|
172108
|
-
call.pack = m[1];
|
|
172109
|
-
call.name = m[2];
|
|
172110
|
-
const optsText = args[1]?.trim();
|
|
172111
|
-
if (optsText) {
|
|
172112
|
-
const color = extractColor(optsText);
|
|
172113
|
-
if (color.error) {
|
|
172114
|
-
call.error = color.error;
|
|
172115
|
-
return call;
|
|
172116
|
-
}
|
|
172117
|
-
if (color.bake !== undefined)
|
|
172118
|
-
call.bake = color.bake;
|
|
172119
|
-
else if (color.tint !== undefined)
|
|
172120
|
-
call.tint = color.tint;
|
|
172121
|
-
}
|
|
172122
|
-
return call;
|
|
172123
|
-
}, extractColor = (optsText) => {
|
|
172124
|
-
if (!optsText.startsWith("{") || !optsText.endsWith("}")) {
|
|
172125
|
-
return { error: `assetIcon(id, opts): opts must be an object literal like { color: "#5b8cff" }.` };
|
|
172126
|
-
}
|
|
172127
|
-
for (const prop of splitTopLevel2(optsText.slice(1, -1), ",")) {
|
|
172128
|
-
const kv = splitTopLevel2(prop, ":");
|
|
172129
|
-
if (kv.length < 2)
|
|
172130
|
-
continue;
|
|
172131
|
-
const key = kv[0].trim().replace(/^["'`]|["'`]$/g, "");
|
|
172132
|
-
if (key !== "color")
|
|
172133
|
-
continue;
|
|
172134
|
-
const raw = kv.slice(1).join(":").trim();
|
|
172135
|
-
if (!raw)
|
|
172136
|
-
continue;
|
|
172137
|
-
const literal = stringLiteralValue2(raw);
|
|
172138
|
-
return literal !== null ? { bake: literal } : { tint: raw };
|
|
172139
|
-
}
|
|
172140
|
-
return {};
|
|
172141
|
-
}, rewriteAssetIcons = (source, calls, svgById) => {
|
|
172142
|
-
let out = source;
|
|
172143
|
-
for (const call of [...calls].sort((a, b) => b.start - a.start)) {
|
|
172144
|
-
const svg = svgById.get(call.id);
|
|
172145
|
-
if (svg === undefined)
|
|
172146
|
-
continue;
|
|
172147
|
-
out = out.slice(0, call.start) + iconExpr(svg, call) + out.slice(call.end);
|
|
172148
|
-
}
|
|
172149
|
-
return out;
|
|
172150
|
-
}, iconExpr = (svg, call) => {
|
|
172151
|
-
const body = call.bake ? svg.split("currentColor").join(call.bake) : svg;
|
|
172152
|
-
const src = `SvgSource(\`${escapeTemplate(body)}\`)`;
|
|
172153
|
-
return call.tint ? `Object.assign(${src}, { tintColor: (${call.tint}) })` : src;
|
|
172154
|
-
}, escapeTemplate = (s) => s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${"), applyAssetIconMacro = async (entries, resolveSvgs) => {
|
|
172155
|
-
const scanned = entries.filter((e) => e.type === "text" && typeof e.text === "string" && e.text.includes("assetIcon")).map((e) => ({ entry: e, calls: scanAssetIconCalls(e.text) })).filter((s) => s.calls.length > 0);
|
|
172156
|
-
if (scanned.length === 0)
|
|
172157
|
-
return [];
|
|
172158
|
-
const errors = scanned.flatMap((s) => s.calls.filter((c) => c.error).map((c) => c.error));
|
|
172159
|
-
if (errors.length)
|
|
172160
|
-
throw new Error([...new Set(errors)].join(`
|
|
172161
|
-
`));
|
|
172162
|
-
const ids = [...new Set(scanned.flatMap((s) => s.calls.map((c) => c.id)))];
|
|
172163
|
-
const svgById = await resolveSvgs(ids);
|
|
172164
|
-
const missing = ids.filter((id) => !svgById.has(id));
|
|
172165
|
-
if (missing.length)
|
|
172166
|
-
throw new Error(`assetIcon: could not resolve icon(s): ${missing.join(", ")}.`);
|
|
172167
|
-
for (const { entry, calls } of scanned)
|
|
172168
|
-
entry.text = rewriteAssetIcons(entry.text, calls, svgById);
|
|
172169
|
-
return ids;
|
|
172170
|
-
}, buildIconsHeaderValue = (ids) => [...new Set(ids.map((id) => id.split(":")[0]))].sort().join(";"), editDistance = (a, b, cap) => {
|
|
172171
|
-
if (Math.abs(a.length - b.length) > cap)
|
|
172172
|
-
return cap + 1;
|
|
172173
|
-
let prev = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
172174
|
-
for (let i = 1;i <= a.length; i++) {
|
|
172175
|
-
const cur = [i];
|
|
172176
|
-
let rowMin = i;
|
|
172177
|
-
for (let j = 1;j <= b.length; j++) {
|
|
172178
|
-
cur[j] = Math.min(prev[j] + 1, cur[j - 1] + 1, prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
172179
|
-
if (cur[j] < rowMin)
|
|
172180
|
-
rowMin = cur[j];
|
|
172181
|
-
}
|
|
172182
|
-
if (rowMin > cap)
|
|
172183
|
-
return cap + 1;
|
|
172184
|
-
prev = cur;
|
|
172185
|
-
}
|
|
172186
|
-
return prev[b.length];
|
|
172187
|
-
}, suggestIconNames = (want, icons) => {
|
|
172188
|
-
const wantParts = new Set(want.split("-").filter(Boolean));
|
|
172189
|
-
const scored = [];
|
|
172190
|
-
for (const [name, info] of Object.entries(icons)) {
|
|
172191
|
-
const lenDiff = Math.abs(name.length - want.length) / 100;
|
|
172192
|
-
let rank;
|
|
172193
|
-
if (name.includes(want) || want.includes(name))
|
|
172194
|
-
rank = lenDiff;
|
|
172195
|
-
else {
|
|
172196
|
-
const d = editDistance(want, name, 2);
|
|
172197
|
-
if (d <= 2)
|
|
172198
|
-
rank = 1 + d;
|
|
172199
|
-
else if (info.kw?.some((k) => wantParts.has(k)))
|
|
172200
|
-
rank = 4 + lenDiff;
|
|
172201
|
-
else
|
|
172202
|
-
continue;
|
|
172203
|
-
}
|
|
172204
|
-
scored.push({ name, rank });
|
|
172205
|
-
}
|
|
172206
|
-
return scored.sort((a, b) => a.rank - b.rank || (a.name < b.name ? -1 : 1)).slice(0, 5).map((s) => s.name);
|
|
172207
|
-
}, iconNotFoundError = async (pack, name, read, registry) => {
|
|
172208
|
-
const metaRaw = await read(`${pack}/meta.json`);
|
|
172209
|
-
if (metaRaw !== null) {
|
|
172210
|
-
let icons = {};
|
|
172211
|
-
try {
|
|
172212
|
-
icons = JSON.parse(metaRaw).icons ?? {};
|
|
172213
|
-
} catch {}
|
|
172214
|
-
const similar = suggestIconNames(name, icons);
|
|
172215
|
-
const hint = similar.length ? ` Did you mean ${similar.map((s) => `"${pack}:${s}"`).join(", ")}?` : ` No similar name in the "${pack}" pack either.`;
|
|
172216
|
-
return new Error(`assetIcon: icon "${pack}:${name}" not found.${hint}`);
|
|
172217
|
-
}
|
|
172218
|
-
const licRaw = await read("licenses.json");
|
|
172219
|
-
if (licRaw !== null) {
|
|
172220
|
-
try {
|
|
172221
|
-
const packs = Object.keys(JSON.parse(licRaw)).sort();
|
|
172222
|
-
return new Error(`assetIcon: unknown icon pack "${pack}" (in "${pack}:${name}"). Available packs: ${packs.join(", ")}.`);
|
|
172223
|
-
} catch {}
|
|
172224
|
-
}
|
|
172225
|
-
return Object.assign(new Error(`assetIcon: can't reach the icon registry${registry ? ` (${registry})` : ""} to resolve "${pack}:${name}". ` + `Check your connection, or set LECODES_ICON_REGISTRY.`), { transient: true });
|
|
172226
|
-
}, VENDORED_ICON_PATH, collectVendoredIcons = (entries) => {
|
|
172227
|
-
const map = new Map;
|
|
172228
|
-
for (const e of entries) {
|
|
172229
|
-
if (e.type !== "text" || typeof e.text !== "string")
|
|
172230
|
-
continue;
|
|
172231
|
-
const m = VENDORED_ICON_PATH.exec(e.path);
|
|
172232
|
-
if (m)
|
|
172233
|
-
map.set(`${m[1]}:${m[2]}`, e.text);
|
|
172234
|
-
}
|
|
172235
|
-
return map;
|
|
172236
|
-
};
|
|
172237
|
-
var init_assetIconMacro = __esm(() => {
|
|
172238
|
-
ICON_ID = /^([a-z0-9]+(?:-[a-z0-9]+)*):([a-z0-9]+(?:-[a-z0-9]+)*)$/;
|
|
172239
|
-
VENDORED_ICON_PATH = /(?:^|\/)assets\/icons\/([a-z0-9]+(?:-[a-z0-9]+)*)\/([a-z0-9]+(?:-[a-z0-9]+)*)\.svg$/;
|
|
172240
|
-
});
|
|
172241
|
-
|
|
172242
172357
|
// ../../sdk/src/compile/index.ts
|
|
172243
172358
|
var init_compile = __esm(() => {
|
|
172244
172359
|
init_bundler();
|
|
@@ -207593,15 +207708,26 @@ var init_channel = __esm(() => {
|
|
|
207593
207708
|
seam = globalThis;
|
|
207594
207709
|
});
|
|
207595
207710
|
|
|
207711
|
+
// ../../sdk/src/server/pushChannel.ts
|
|
207712
|
+
var seam2, setPushSender = (sender) => {
|
|
207713
|
+
if (sender)
|
|
207714
|
+
seam2.__lecodesPushSend = sender;
|
|
207715
|
+
else
|
|
207716
|
+
delete seam2.__lecodesPushSend;
|
|
207717
|
+
}, isPushChannel = (v) => typeof v === "object" && v !== null && v.__pushChannel === true;
|
|
207718
|
+
var init_pushChannel = __esm(() => {
|
|
207719
|
+
seam2 = globalThis;
|
|
207720
|
+
});
|
|
207721
|
+
|
|
207596
207722
|
// ../../sdk/src/server/context.ts
|
|
207597
|
-
var
|
|
207723
|
+
var seam3, setRequestProvider = (provider) => {
|
|
207598
207724
|
if (provider)
|
|
207599
|
-
|
|
207725
|
+
seam3.__lecodesRequest = provider;
|
|
207600
207726
|
else
|
|
207601
|
-
delete
|
|
207602
|
-
}, currentRequest = () =>
|
|
207727
|
+
delete seam3.__lecodesRequest;
|
|
207728
|
+
}, currentRequest = () => seam3.__lecodesRequest?.();
|
|
207603
207729
|
var init_context2 = __esm(() => {
|
|
207604
|
-
|
|
207730
|
+
seam3 = globalThis;
|
|
207605
207731
|
});
|
|
207606
207732
|
|
|
207607
207733
|
// ../../sdk/src/server/db/fields.ts
|
|
@@ -207654,9 +207780,9 @@ var init_fields = __esm(() => {
|
|
|
207654
207780
|
});
|
|
207655
207781
|
|
|
207656
207782
|
// ../../sdk/src/server/errors.ts
|
|
207657
|
-
var
|
|
207783
|
+
var ApiError2;
|
|
207658
207784
|
var init_errors3 = __esm(() => {
|
|
207659
|
-
|
|
207785
|
+
ApiError2 = class ApiError2 extends Error {
|
|
207660
207786
|
status;
|
|
207661
207787
|
constructor(status, message = "") {
|
|
207662
207788
|
super(message);
|
|
@@ -207689,30 +207815,30 @@ var FILE_MODEL = "File", filePublicFields = () => ({
|
|
|
207689
207815
|
width: t.int().optional(),
|
|
207690
207816
|
height: t.int().optional(),
|
|
207691
207817
|
createdAt: t.date().default("now")
|
|
207692
|
-
}),
|
|
207818
|
+
}), seam4, setFilesSeam = (value) => {
|
|
207693
207819
|
if (value)
|
|
207694
|
-
|
|
207820
|
+
seam4.__lecodesFiles = value;
|
|
207695
207821
|
else
|
|
207696
|
-
delete
|
|
207822
|
+
delete seam4.__lecodesFiles;
|
|
207697
207823
|
};
|
|
207698
207824
|
var init_models2 = __esm(() => {
|
|
207699
207825
|
init_fields();
|
|
207700
|
-
|
|
207826
|
+
seam4 = globalThis;
|
|
207701
207827
|
});
|
|
207702
207828
|
|
|
207703
207829
|
// ../../sdk/src/server/db/defineDb.ts
|
|
207704
|
-
var
|
|
207830
|
+
var seam5, setDbTransport = (transport) => {
|
|
207705
207831
|
if (transport)
|
|
207706
|
-
|
|
207832
|
+
seam5.__lecodesDbTransport = transport;
|
|
207707
207833
|
else
|
|
207708
|
-
delete
|
|
207834
|
+
delete seam5.__lecodesDbTransport;
|
|
207709
207835
|
}, takeRegisteredDbs = () => {
|
|
207710
|
-
const dbs =
|
|
207711
|
-
|
|
207836
|
+
const dbs = seam5.__lecodesDbs ?? [];
|
|
207837
|
+
seam5.__lecodesDbs = [];
|
|
207712
207838
|
return dbs;
|
|
207713
207839
|
};
|
|
207714
207840
|
var init_defineDb = __esm(() => {
|
|
207715
|
-
|
|
207841
|
+
seam5 = globalThis;
|
|
207716
207842
|
});
|
|
207717
207843
|
|
|
207718
207844
|
// ../../sdk/src/server/validate.ts
|
|
@@ -207827,7 +207953,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
207827
207953
|
var isDb = (v) => typeof v === "object" && v !== null && typeof v.$schema === "string", loadServerModules = (reg = globalThis.__lecodesServer) => {
|
|
207828
207954
|
if (!reg)
|
|
207829
207955
|
throw new Error("loadServerModules: no server bundle registered (globalThis.__lecodesServer)");
|
|
207830
|
-
const out = { endpoints: new Map, channels: new Map, dbs: [], manifest: reg.manifest };
|
|
207956
|
+
const out = { endpoints: new Map, channels: new Map, pushChannels: new Map, dbs: [], manifest: reg.manifest };
|
|
207831
207957
|
for (const [mod, exports] of Object.entries(reg.modules)) {
|
|
207832
207958
|
const kinds = new Map((reg.manifest?.modules?.[mod] ?? []).map((e) => [e.name, e.kind]));
|
|
207833
207959
|
for (const [name, value] of Object.entries(exports)) {
|
|
@@ -207838,6 +207964,9 @@ var isDb = (v) => typeof v === "object" && v !== null && typeof v.$schema === "s
|
|
|
207838
207964
|
} else if (isChannel(value)) {
|
|
207839
207965
|
value.__id = id;
|
|
207840
207966
|
out.channels.set(id, value);
|
|
207967
|
+
} else if (isPushChannel(value)) {
|
|
207968
|
+
value.__id = id;
|
|
207969
|
+
out.pushChannels.set(id, value);
|
|
207841
207970
|
} else if (isDb(value))
|
|
207842
207971
|
out.dbs.push(value);
|
|
207843
207972
|
}
|
|
@@ -207857,9 +207986,10 @@ var isDb = (v) => typeof v === "object" && v !== null && typeof v.$schema === "s
|
|
|
207857
207986
|
if (name !== "MarciEmbeddedError" && name !== "MarciHttpError" || typeof kind !== "string")
|
|
207858
207987
|
return null;
|
|
207859
207988
|
return DB_ERROR_STATUS[kind] ?? null;
|
|
207860
|
-
}, als, installProvider = () => setRequestProvider(() => als.getStore()),
|
|
207861
|
-
const
|
|
207862
|
-
|
|
207989
|
+
}, als, installProvider = () => setRequestProvider(() => als.getStore()), envelope = (server2) => (r, rc) => {
|
|
207990
|
+
const s = rc.auth;
|
|
207991
|
+
const out = s?.issuedToken ? { ...r, session: s.issuedToken } : r;
|
|
207992
|
+
return s && s.userId !== null && !s.pushAddress && server2.pushChannels.size ? { ...out, push: true } : out;
|
|
207863
207993
|
}, createInvoker = (server2, opts = {}) => {
|
|
207864
207994
|
installProvider();
|
|
207865
207995
|
const validate = opts.validate ?? true;
|
|
@@ -207867,6 +207997,7 @@ var isDb = (v) => typeof v === "object" && v !== null && typeof v.$schema === "s
|
|
|
207867
207997
|
const params = server2.manifest.params;
|
|
207868
207998
|
const auth = opts.auth ?? null;
|
|
207869
207999
|
const files = opts.files ?? null;
|
|
208000
|
+
const withSession = envelope(server2);
|
|
207870
208001
|
return async (id, args, ctx = { headers: {} }) => {
|
|
207871
208002
|
const fn = server2.endpoints.get(id);
|
|
207872
208003
|
if (!fn)
|
|
@@ -207933,6 +208064,7 @@ var isDb = (v) => typeof v === "object" && v !== null && typeof v.$schema === "s
|
|
|
207933
208064
|
const onError = opts.onError ?? ((id, e) => console.error(`[server] ${id}:`, e));
|
|
207934
208065
|
const params = server2.manifest.params;
|
|
207935
208066
|
const auth = opts.auth ?? null;
|
|
208067
|
+
const withSession = envelope(server2);
|
|
207936
208068
|
return async (channelId, args = [], ctx = { headers: {} }) => {
|
|
207937
208069
|
const ch = server2.channels.get(channelId);
|
|
207938
208070
|
if (!ch)
|
|
@@ -207965,6 +208097,7 @@ var isDb = (v) => typeof v === "object" && v !== null && typeof v.$schema === "s
|
|
|
207965
208097
|
};
|
|
207966
208098
|
var init_runtime = __esm(() => {
|
|
207967
208099
|
init_channel();
|
|
208100
|
+
init_pushChannel();
|
|
207968
208101
|
init_context2();
|
|
207969
208102
|
init_defineDb();
|
|
207970
208103
|
init_validate();
|
|
@@ -208111,23 +208244,28 @@ var CODE_TTL_MS, CODE_MAX_ATTEMPTS = 5, SEND_MIN_GAP_MS = 30000, SEND_MAX_PER_10
|
|
|
208111
208244
|
return actual.length === expected.length && timingSafeEqual(actual, expected);
|
|
208112
208245
|
}, checkPassword = (password) => {
|
|
208113
208246
|
if (typeof password !== "string" || password.length < PASSWORD_MIN)
|
|
208114
|
-
throw new
|
|
208247
|
+
throw new ApiError2(400, `Password must be at least ${PASSWORD_MIN} characters`);
|
|
208115
208248
|
if (password.length > PASSWORD_MAX)
|
|
208116
|
-
throw new
|
|
208249
|
+
throw new ApiError2(400, `Password must be at most ${PASSWORD_MAX} characters`);
|
|
208117
208250
|
return password;
|
|
208118
208251
|
}, EMAIL_RE, normalizeEmail = (email) => {
|
|
208119
208252
|
const e = typeof email === "string" ? email.trim().toLowerCase() : "";
|
|
208120
208253
|
if (!e || e.length > 254 || !EMAIL_RE.test(e))
|
|
208121
|
-
throw new
|
|
208254
|
+
throw new ApiError2(400, "Invalid email");
|
|
208122
208255
|
return e;
|
|
208123
208256
|
}, LOGIN_RE, parseLogin = (login) => {
|
|
208124
208257
|
const l = typeof login === "string" ? login.trim() : "";
|
|
208125
208258
|
if (l.includes("@"))
|
|
208126
208259
|
return { provider: "email", subject: normalizeEmail(l) };
|
|
208127
208260
|
if (!LOGIN_RE.test(l))
|
|
208128
|
-
throw new
|
|
208261
|
+
throw new ApiError2(400, "Login must be 3–32 letters, digits, dots, dashes or underscores");
|
|
208129
208262
|
return { provider: "login", subject: l.toLowerCase() };
|
|
208130
|
-
}, identityKey = (provider, subject) => `${provider}:${subject}`,
|
|
208263
|
+
}, identityKey = (provider, subject) => `${provider}:${subject}`, PUSH_ADDRESS_HEADER = "x-lecodes-push", PUSH_ADDRESS_RE, pushAddressOf = (ctx) => {
|
|
208264
|
+
const headers = ctx.headers ?? {};
|
|
208265
|
+
const raw = headers[PUSH_ADDRESS_HEADER] ?? Object.entries(headers).find(([k]) => k.toLowerCase() === PUSH_ADDRESS_HEADER)?.[1];
|
|
208266
|
+
const value = typeof raw === "string" ? raw.trim() : "";
|
|
208267
|
+
return PUSH_ADDRESS_RE.test(value) ? value : null;
|
|
208268
|
+
}, window2 = (map, key, now, span) => {
|
|
208131
208269
|
const list = (map.get(key) ?? []).filter((x) => now - x < span);
|
|
208132
208270
|
map.set(key, list);
|
|
208133
208271
|
return list;
|
|
@@ -208153,14 +208291,15 @@ var CODE_TTL_MS, CODE_MAX_ATTEMPTS = 5, SEND_MIN_GAP_MS = 30000, SEND_MAX_PER_10
|
|
|
208153
208291
|
const requireSignedIn = (ctx) => {
|
|
208154
208292
|
const id = stateOf(ctx).userId;
|
|
208155
208293
|
if (id === null)
|
|
208156
|
-
throw new
|
|
208294
|
+
throw new ApiError2(401, "Sign in required");
|
|
208157
208295
|
return id;
|
|
208158
208296
|
};
|
|
208159
208297
|
const mint = async (ctx) => {
|
|
208160
208298
|
const token = randomToken();
|
|
208161
|
-
const
|
|
208162
|
-
const
|
|
208163
|
-
|
|
208299
|
+
const pushAddress = pushAddressOf(ctx);
|
|
208300
|
+
const { id } = await sessions.insert({ tokenHash: await sha2562(token), ...pushAddress ? { pushAddress } : {} });
|
|
208301
|
+
const state = { sessionId: id, userId: null, pushAddress, issuedToken: token };
|
|
208302
|
+
sessionCache.set(token, { sessionId: id, userId: null, pushAddress, at: now() });
|
|
208164
208303
|
ctx.auth = state;
|
|
208165
208304
|
ctx.sessionToken = token;
|
|
208166
208305
|
return state;
|
|
@@ -208181,6 +208320,15 @@ var CODE_TTL_MS, CODE_MAX_ATTEMPTS = 5, SEND_MIN_GAP_MS = 30000, SEND_MAX_PER_10
|
|
|
208181
208320
|
if (opts.rotate)
|
|
208182
208321
|
await rotate(ctx);
|
|
208183
208322
|
};
|
|
208323
|
+
const followPushAddress = async (token, sessionId, held, told) => {
|
|
208324
|
+
if (!told || told === held)
|
|
208325
|
+
return held;
|
|
208326
|
+
await sessions.update(sessionId, { pushAddress: told });
|
|
208327
|
+
const c = sessionCache.get(token);
|
|
208328
|
+
if (c)
|
|
208329
|
+
c.pushAddress = told;
|
|
208330
|
+
return told;
|
|
208331
|
+
};
|
|
208184
208332
|
const resolve = async (ctx) => {
|
|
208185
208333
|
if (!tables)
|
|
208186
208334
|
return;
|
|
@@ -208188,16 +208336,18 @@ var CODE_TTL_MS, CODE_MAX_ATTEMPTS = 5, SEND_MIN_GAP_MS = 30000, SEND_MAX_PER_10
|
|
|
208188
208336
|
if (token) {
|
|
208189
208337
|
const c = sessionCache.get(token);
|
|
208190
208338
|
if (c && now() - c.at < CACHE_TTL_MS) {
|
|
208191
|
-
|
|
208339
|
+
const pushAddress = await followPushAddress(token, c.sessionId, c.pushAddress, pushAddressOf(ctx));
|
|
208340
|
+
ctx.auth = { sessionId: c.sessionId, userId: c.userId, pushAddress };
|
|
208192
208341
|
return;
|
|
208193
208342
|
}
|
|
208194
|
-
const row = await sessions.where({ tokenHash: await sha2562(token) }).select({ id: true, revoked: true, lastSeenAt: true, user: { id: true } }).first();
|
|
208343
|
+
const row = await sessions.where({ tokenHash: await sha2562(token) }).select({ id: true, revoked: true, lastSeenAt: true, pushAddress: true, user: { id: true } }).first();
|
|
208195
208344
|
if (row && !row.revoked) {
|
|
208196
208345
|
const userId = row.user ? row.user.id : null;
|
|
208197
208346
|
if (now() - (row.lastSeenAt ?? 0) > SEEN_UPDATE_MS)
|
|
208198
208347
|
sessions.update(row.id, { lastSeenAt: now() }).then(undefined, () => {});
|
|
208199
|
-
sessionCache.set(token, { sessionId: row.id, userId, at: now() });
|
|
208200
|
-
|
|
208348
|
+
sessionCache.set(token, { sessionId: row.id, userId, pushAddress: row.pushAddress ?? null, at: now() });
|
|
208349
|
+
const pushAddress = await followPushAddress(token, row.id, row.pushAddress ?? null, pushAddressOf(ctx));
|
|
208350
|
+
ctx.auth = { sessionId: row.id, userId, pushAddress };
|
|
208201
208351
|
return;
|
|
208202
208352
|
}
|
|
208203
208353
|
}
|
|
@@ -208210,7 +208360,7 @@ var CODE_TTL_MS, CODE_MAX_ATTEMPTS = 5, SEND_MIN_GAP_MS = 30000, SEND_MAX_PER_10
|
|
|
208210
208360
|
const ident = await findIdentity("email", email);
|
|
208211
208361
|
if (ident) {
|
|
208212
208362
|
if (ident.user.id !== userId)
|
|
208213
|
-
throw new
|
|
208363
|
+
throw new ApiError2(409, "This email belongs to another account");
|
|
208214
208364
|
if (!ident.verifiedAt)
|
|
208215
208365
|
await identities.update(ident.id, { verifiedAt: now(), hash: null });
|
|
208216
208366
|
} else if (create) {
|
|
@@ -208223,7 +208373,7 @@ var CODE_TTL_MS, CODE_MAX_ATTEMPTS = 5, SEND_MIN_GAP_MS = 30000, SEND_MAX_PER_10
|
|
|
208223
208373
|
const { provider, subject } = parseLogin(rawLogin);
|
|
208224
208374
|
const password = checkPassword(rawPassword);
|
|
208225
208375
|
if (await findIdentity(provider, subject))
|
|
208226
|
-
throw new
|
|
208376
|
+
throw new ApiError2(409, provider === "email" ? "An account with this email already exists" : "This login is taken");
|
|
208227
208377
|
const userId = await createUser(data);
|
|
208228
208378
|
await createIdentity(userId, provider, subject, { hash: await hashPassword(password) });
|
|
208229
208379
|
await bind(ctx, userId, { rotate: true });
|
|
@@ -208238,13 +208388,13 @@ var CODE_TTL_MS, CODE_MAX_ATTEMPTS = 5, SEND_MIN_GAP_MS = 30000, SEND_MAX_PER_10
|
|
|
208238
208388
|
const byLogin = window2(passwordFails, `l:${who ? identityKey(who.provider, who.subject) : ""}`, t, 10 * 60000);
|
|
208239
208389
|
const byIp = ctx.ip ? window2(passwordFails, `ip:${ctx.ip}`, t, 10 * 60000) : [];
|
|
208240
208390
|
if (byLogin.length >= PASSWORD_FAILS_PER_LOGIN || byIp.length >= PASSWORD_FAILS_PER_IP)
|
|
208241
|
-
throw new
|
|
208391
|
+
throw new ApiError2(429, "Too many attempts — try again later");
|
|
208242
208392
|
const ident = who ? await findIdentity(who.provider, who.subject) : null;
|
|
208243
208393
|
const ok = !!ident?.hash && typeof rawPassword === "string" && await verifyPassword(rawPassword, ident.hash);
|
|
208244
208394
|
if (!ok) {
|
|
208245
208395
|
byLogin.push(t);
|
|
208246
208396
|
byIp.push(t);
|
|
208247
|
-
throw new
|
|
208397
|
+
throw new ApiError2(401, "Invalid login or password");
|
|
208248
208398
|
}
|
|
208249
208399
|
await bind(ctx, ident.user.id, { rotate: true });
|
|
208250
208400
|
};
|
|
@@ -208255,7 +208405,7 @@ var CODE_TTL_MS, CODE_MAX_ATTEMPTS = 5, SEND_MIN_GAP_MS = 30000, SEND_MAX_PER_10
|
|
|
208255
208405
|
const mine = await identities.where({ user: { id: userId } }).select({ id: true, provider: true });
|
|
208256
208406
|
const typed = mine.filter((i) => i.provider === "email" || i.provider === "login");
|
|
208257
208407
|
if (!typed.length)
|
|
208258
|
-
throw new
|
|
208408
|
+
throw new ApiError2(400, "Your account has no email or login to sign in with a password — sign in with a code first");
|
|
208259
208409
|
const hash = await hashPassword(password);
|
|
208260
208410
|
for (const i of typed)
|
|
208261
208411
|
await identities.update(i.id, { hash });
|
|
@@ -208272,9 +208422,9 @@ var CODE_TTL_MS, CODE_MAX_ATTEMPTS = 5, SEND_MIN_GAP_MS = 30000, SEND_MAX_PER_10
|
|
|
208272
208422
|
const t = now();
|
|
208273
208423
|
const recent = window2(sends, email, t, 10 * 60000);
|
|
208274
208424
|
if (recent.length && t - recent[recent.length - 1] < SEND_MIN_GAP_MS)
|
|
208275
|
-
throw new
|
|
208425
|
+
throw new ApiError2(429, `Try again in ${Math.ceil((SEND_MIN_GAP_MS - (t - recent[recent.length - 1])) / 1000)} s`);
|
|
208276
208426
|
if (recent.length >= SEND_MAX_PER_10MIN)
|
|
208277
|
-
throw new
|
|
208427
|
+
throw new ApiError2(429, "Too many codes requested — try again later");
|
|
208278
208428
|
const code = randomCode();
|
|
208279
208429
|
await sessions.update(s.sessionId, { pendingEmail: email, codeHash: await codeHash(s.sessionId, code), codeExpires: t + CODE_TTL_MS, codeAttempts: 0 });
|
|
208280
208430
|
recent.push(t);
|
|
@@ -208303,12 +208453,12 @@ var CODE_TTL_MS, CODE_MAX_ATTEMPTS = 5, SEND_MIN_GAP_MS = 30000, SEND_MAX_PER_10
|
|
|
208303
208453
|
if (!isDevCode) {
|
|
208304
208454
|
const row = await sessions.where({ id: s.sessionId }).select({ pendingEmail: true, codeHash: true, codeExpires: true, codeAttempts: true }).first();
|
|
208305
208455
|
if (!row?.codeHash || row.pendingEmail !== email || !row.codeExpires || row.codeExpires < now())
|
|
208306
|
-
throw new
|
|
208456
|
+
throw new ApiError2(400, "Code expired — request a new one");
|
|
208307
208457
|
if (row.codeAttempts >= CODE_MAX_ATTEMPTS)
|
|
208308
|
-
throw new
|
|
208458
|
+
throw new ApiError2(429, "Too many attempts — request a new code");
|
|
208309
208459
|
if (typeof code !== "string" || await codeHash(s.sessionId, code.trim()) !== row.codeHash) {
|
|
208310
208460
|
await sessions.update(s.sessionId, { codeAttempts: { $increment: 1 } });
|
|
208311
|
-
throw new
|
|
208461
|
+
throw new ApiError2(400, "Invalid code");
|
|
208312
208462
|
}
|
|
208313
208463
|
}
|
|
208314
208464
|
const userId = await userOfProvenEmail(email);
|
|
@@ -208343,7 +208493,7 @@ var CODE_TTL_MS, CODE_MAX_ATTEMPTS = 5, SEND_MIN_GAP_MS = 30000, SEND_MAX_PER_10
|
|
|
208343
208493
|
signIn: async (ctx, userId) => {
|
|
208344
208494
|
requireAuth();
|
|
208345
208495
|
if (!await users.where({ id: userId }).select({ id: true }).first())
|
|
208346
|
-
throw new
|
|
208496
|
+
throw new ApiError2(404, "No such user");
|
|
208347
208497
|
await bind(ctx, userId, { rotate: true });
|
|
208348
208498
|
},
|
|
208349
208499
|
signOut: async (ctx) => {
|
|
@@ -208389,6 +208539,92 @@ var init_host = __esm(() => {
|
|
|
208389
208539
|
enc = new TextEncoder;
|
|
208390
208540
|
EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
208391
208541
|
LOGIN_RE = /^[\p{L}\p{N}_.-]{3,32}$/u;
|
|
208542
|
+
PUSH_ADDRESS_RE = /^[A-Za-z0-9_-]{1,128}$/;
|
|
208543
|
+
});
|
|
208544
|
+
|
|
208545
|
+
// ../../sdk/src/server/push/host.ts
|
|
208546
|
+
var PUSH_BATCH = 500, chunks = (list, size) => {
|
|
208547
|
+
const out = [];
|
|
208548
|
+
for (let i = 0;i < list.length; i += size)
|
|
208549
|
+
out.push(list.slice(i, i + size));
|
|
208550
|
+
return out;
|
|
208551
|
+
}, createPushHost = (cfg) => {
|
|
208552
|
+
const sessions = cfg.db?.$auth?.session ?? null;
|
|
208553
|
+
const relay = cfg.relay ?? null;
|
|
208554
|
+
const log = cfg.log ?? ((level, text) => level === "warn" ? console.warn(text) : console.info(text));
|
|
208555
|
+
const warned = new Set;
|
|
208556
|
+
const devices = async (userIds) => {
|
|
208557
|
+
const rows = await sessions.where({ user: { id: { $in: userIds } }, revoked: false }).select({ id: true, pushAddress: true });
|
|
208558
|
+
const byAddress = new Map;
|
|
208559
|
+
for (const row of rows) {
|
|
208560
|
+
if (!row.pushAddress)
|
|
208561
|
+
continue;
|
|
208562
|
+
const list = byAddress.get(row.pushAddress) ?? [];
|
|
208563
|
+
list.push(row.id);
|
|
208564
|
+
byAddress.set(row.pushAddress, list);
|
|
208565
|
+
}
|
|
208566
|
+
return byAddress;
|
|
208567
|
+
};
|
|
208568
|
+
const forget = async (byAddress, addresses) => {
|
|
208569
|
+
const ids = addresses.flatMap((a) => byAddress.get(a) ?? []);
|
|
208570
|
+
if (!ids.length)
|
|
208571
|
+
return;
|
|
208572
|
+
await sessions.where({ id: { $in: ids } }).updateMany({ pushAddress: null });
|
|
208573
|
+
};
|
|
208574
|
+
const send = async (channelId, userIds, message) => {
|
|
208575
|
+
if (!sessions) {
|
|
208576
|
+
throw new Error("pushChannel.send: the project's database has no sign-in — a user's devices are the sessions of defineDb({...}).withAuth({ model }), without it there is no one to send to");
|
|
208577
|
+
}
|
|
208578
|
+
if (!relay) {
|
|
208579
|
+
if (!warned.has(channelId)) {
|
|
208580
|
+
warned.add(channelId);
|
|
208581
|
+
log("warn", `${channelId}: notifications are not sent from this backend (no road to the platform's sender) — a deployed backend sends them`);
|
|
208582
|
+
}
|
|
208583
|
+
return { sent: 0 };
|
|
208584
|
+
}
|
|
208585
|
+
const byAddress = await devices(userIds);
|
|
208586
|
+
if (byAddress.size === 0)
|
|
208587
|
+
return { sent: 0 };
|
|
208588
|
+
const wire = {
|
|
208589
|
+
channel: channelId,
|
|
208590
|
+
title: message.title,
|
|
208591
|
+
...message.body !== undefined ? { body: message.body } : {},
|
|
208592
|
+
...message.url !== undefined ? { url: message.url } : {},
|
|
208593
|
+
...message.data !== undefined ? { data: message.data } : {},
|
|
208594
|
+
...message.badge !== undefined ? { badge: message.badge } : {}
|
|
208595
|
+
};
|
|
208596
|
+
let sent = 0;
|
|
208597
|
+
for (let batch of chunks([...byAddress.keys()], PUSH_BATCH)) {
|
|
208598
|
+
let result = await relay(batch, wire);
|
|
208599
|
+
if (result.unknown?.length) {
|
|
208600
|
+
const unknown = new Set(result.unknown);
|
|
208601
|
+
await forget(byAddress, [...unknown]);
|
|
208602
|
+
batch = batch.filter((a) => !unknown.has(a));
|
|
208603
|
+
if (!batch.length)
|
|
208604
|
+
continue;
|
|
208605
|
+
result = await relay(batch, wire);
|
|
208606
|
+
if (result.unknown?.length)
|
|
208607
|
+
await forget(byAddress, result.unknown);
|
|
208608
|
+
}
|
|
208609
|
+
sent += result.sent;
|
|
208610
|
+
const dead = result.failed.filter((f) => f.reason === "disabled").map((f) => f.address);
|
|
208611
|
+
if (dead.length)
|
|
208612
|
+
await forget(byAddress, dead);
|
|
208613
|
+
}
|
|
208614
|
+
return { sent };
|
|
208615
|
+
};
|
|
208616
|
+
return {
|
|
208617
|
+
get enabled() {
|
|
208618
|
+
return relay !== null;
|
|
208619
|
+
},
|
|
208620
|
+
install() {
|
|
208621
|
+
setPushSender(send);
|
|
208622
|
+
},
|
|
208623
|
+
send
|
|
208624
|
+
};
|
|
208625
|
+
};
|
|
208626
|
+
var init_host2 = __esm(() => {
|
|
208627
|
+
init_pushChannel();
|
|
208392
208628
|
});
|
|
208393
208629
|
|
|
208394
208630
|
// ../../sdk/src/server/files/host.ts
|
|
@@ -208787,7 +209023,7 @@ var UploadError, UUID, JOURNAL = "lecodes-files", MB, TMP_MAX_AGE_MS, MAX_IMAGE_
|
|
|
208787
209023
|
}
|
|
208788
209024
|
};
|
|
208789
209025
|
};
|
|
208790
|
-
var
|
|
209026
|
+
var init_host3 = __esm(() => {
|
|
208791
209027
|
init_context2();
|
|
208792
209028
|
init_models2();
|
|
208793
209029
|
UploadError = class UploadError extends Error {
|
|
@@ -208860,15 +209096,17 @@ var describeField = (f) => {
|
|
|
208860
209096
|
models,
|
|
208861
209097
|
endpoints: [...server2.endpoints.keys()].map((id) => ({ id, params: params[id] ?? null })),
|
|
208862
209098
|
channels: [...server2.channels.keys()],
|
|
209099
|
+
pushChannels: [...server2.pushChannels.keys()],
|
|
208863
209100
|
auth: { model: db?.$auth?.model ?? null },
|
|
208864
209101
|
appName: server2.manifest.app?.name
|
|
208865
209102
|
};
|
|
208866
209103
|
};
|
|
208867
|
-
var
|
|
209104
|
+
var init_host4 = __esm(() => {
|
|
208868
209105
|
init_runtime();
|
|
208869
209106
|
init_defineDb();
|
|
208870
209107
|
init_host();
|
|
208871
209108
|
init_host2();
|
|
209109
|
+
init_host3();
|
|
208872
209110
|
init_models2();
|
|
208873
209111
|
init_models();
|
|
208874
209112
|
});
|
|
@@ -209891,7 +210129,7 @@ var require_main = __commonJS(function(exports, module) {
|
|
|
209891
210129
|
});
|
|
209892
210130
|
|
|
209893
210131
|
// src/dev/backendWorker.ts
|
|
209894
|
-
import { existsSync as
|
|
210132
|
+
import { existsSync as existsSync55, mkdirSync as mkdirSync35, mkdtempSync as mkdtempSync8, renameSync as renameSync3, rmSync as rmSync18 } from "node:fs";
|
|
209895
210133
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
209896
210134
|
import { tmpdir as tmpdir8 } from "node:os";
|
|
209897
210135
|
import { join as join58 } from "node:path";
|
|
@@ -209935,13 +210173,23 @@ var post = (m) => {
|
|
|
209935
210173
|
rmSync18(aside, { recursive: true, force: true });
|
|
209936
210174
|
renameSync3(dir, aside);
|
|
209937
210175
|
rmSync18(`${filesDir}-old`, { recursive: true, force: true });
|
|
209938
|
-
if (
|
|
210176
|
+
if (existsSync55(filesDir))
|
|
209939
210177
|
renameSync3(filesDir, `${filesDir}-old`);
|
|
209940
210178
|
mkdirSync35(dir, { recursive: true });
|
|
209941
210179
|
db = openDatabase(dir);
|
|
209942
210180
|
await db.$sync(schema);
|
|
209943
210181
|
return [`database started over — the schema change couldn't be migrated (${reason}); the previous data is in ${aside}`];
|
|
209944
210182
|
}
|
|
210183
|
+
}, pushRelay = (sender) => async (to, message) => {
|
|
210184
|
+
try {
|
|
210185
|
+
return await sendPush(sender.apiUrl, sender.token, sender.uuid, { to, ...message });
|
|
210186
|
+
} catch (e) {
|
|
210187
|
+
const body = e instanceof ApiError ? e.body : null;
|
|
210188
|
+
if (body?.code === "unknown_addresses" && Array.isArray(body.addresses)) {
|
|
210189
|
+
return { sent: 0, failed: [], unknown: body.addresses.filter((a) => typeof a === "string") };
|
|
210190
|
+
}
|
|
210191
|
+
throw new Error(`push: the platform refused the send: ${e.message}`);
|
|
210192
|
+
}
|
|
209945
210193
|
}, load = async (m) => {
|
|
209946
210194
|
const noDb = () => {
|
|
209947
210195
|
throw new Error("db: the database is not open yet — query it inside an endpoint, not while the module loads");
|
|
@@ -209960,6 +210208,11 @@ var post = (m) => {
|
|
|
209960
210208
|
log: (level, text) => post({ t: "log", level, text })
|
|
209961
210209
|
});
|
|
209962
210210
|
authHost.install();
|
|
210211
|
+
createPushHost({
|
|
210212
|
+
db: server2.dbs.find((d) => d.$auth) ?? null,
|
|
210213
|
+
relay: m.push ? pushRelay(m.push) : null,
|
|
210214
|
+
log: (level, text) => post({ t: "log", level, text })
|
|
210215
|
+
}).install();
|
|
209963
210216
|
const auth = authHost.enabled ? authHost : null;
|
|
209964
210217
|
const onError = (id, e) => post({ t: "log", level: "error", text: `${id}: ${e?.stack ?? e}` });
|
|
209965
210218
|
const files = createFilesHost({
|
|
@@ -209982,8 +210235,9 @@ var post = (m) => {
|
|
|
209982
210235
|
post({ t: "loaded", describe, notes });
|
|
209983
210236
|
};
|
|
209984
210237
|
var init_backendWorker = __esm(() => {
|
|
209985
|
-
|
|
210238
|
+
init_host4();
|
|
209986
210239
|
init_peerInstall();
|
|
210240
|
+
init_api();
|
|
209987
210241
|
console.log = hook("log");
|
|
209988
210242
|
console.info = hook("info");
|
|
209989
210243
|
console.warn = hook("warn");
|
|
@@ -210028,7 +210282,7 @@ var init_backendWorker = __esm(() => {
|
|
|
210028
210282
|
// src/index.ts
|
|
210029
210283
|
init_package();
|
|
210030
210284
|
init_cli();
|
|
210031
|
-
import { existsSync as
|
|
210285
|
+
import { existsSync as existsSync56, realpathSync as realpathSync2 } from "node:fs";
|
|
210032
210286
|
import { delimiter as delimiter3, dirname as dirname28, join as join59 } from "node:path";
|
|
210033
210287
|
|
|
210034
210288
|
// src/commands/app/index.ts
|
|
@@ -210635,6 +210889,9 @@ var readManifest2 = (dir, id) => {
|
|
|
210635
210889
|
};
|
|
210636
210890
|
var readManifestIfAny = (dir, id) => existsSync14(join17(dir, MANIFEST_FILE)) ? readManifest2(dir, id) : null;
|
|
210637
210891
|
|
|
210892
|
+
// src/commands/app/shared.ts
|
|
210893
|
+
init_compile();
|
|
210894
|
+
|
|
210638
210895
|
// src/plugin/emitPackage.ts
|
|
210639
210896
|
class PackageError extends Error {
|
|
210640
210897
|
}
|
|
@@ -210731,7 +210988,12 @@ var urlSchemeOf = (config) => {
|
|
|
210731
210988
|
throw new CliError(`app.json "urlScheme" is the app's OWN scheme ("myshop" → myshop://…), not ${scheme}.`);
|
|
210732
210989
|
return scheme;
|
|
210733
210990
|
};
|
|
210734
|
-
var readAppConfigAt = (root) =>
|
|
210991
|
+
var readAppConfigAt = (root) => {
|
|
210992
|
+
const config = readJson2(join18(root, "app.json"));
|
|
210993
|
+
if (config && "plugins" in config)
|
|
210994
|
+
config.plugins = pluginSpecsOf(config.plugins);
|
|
210995
|
+
return config;
|
|
210996
|
+
};
|
|
210735
210997
|
var DESKTOP_DEFAULT_WINDOW = { width: 1280, height: 720 };
|
|
210736
210998
|
var resolveDesktopRenderer = (root, renderer) => {
|
|
210737
210999
|
const raw = renderer ?? process.env.LECODES_DESKTOP_RENDERER ?? readAppConfigAt(root)?.desktop?.renderer;
|
|
@@ -210851,25 +211113,33 @@ var detectEngines = (bundleJs) => {
|
|
|
210851
211113
|
}
|
|
210852
211114
|
const header = lines.join(`
|
|
210853
211115
|
`);
|
|
210854
|
-
|
|
211116
|
+
const on = (name) => new RegExp(`^// ${name}: enable$`, "m").test(header);
|
|
211117
|
+
return { gl: !/^\/\/ gl: disable$/m.test(header), g2: on("2d"), physics: on("physics"), net: on("net"), nav: on("nav"), audio: on("audio") };
|
|
211118
|
+
};
|
|
211119
|
+
var VARIANT_LACKS = {
|
|
211120
|
+
full: [],
|
|
211121
|
+
"3d": ["g2", "physics", "net", "nav", "audio"],
|
|
211122
|
+
"2d": ["gl", "physics", "net", "nav"],
|
|
211123
|
+
core: ["gl", "g2", "physics", "net", "nav"]
|
|
210855
211124
|
};
|
|
211125
|
+
var ENGINE_WORDS = { gl: "3D", g2: "2D", physics: "physics", net: "multiplayer", nav: "navigation", audio: "game audio" };
|
|
211126
|
+
var lacks = (variant, uses) => VARIANT_LACKS[variant].filter((e) => uses[e]).map((e) => ENGINE_WORDS[e]);
|
|
210856
211127
|
var resolveVariant = (requested, bundleJs, configKey = "ios.variant") => {
|
|
210857
211128
|
const uses = detectEngines(bundleJs);
|
|
210858
|
-
const detected =
|
|
211129
|
+
const detected = SDK_VARIANTS.find((v) => lacks(v, uses).length === 0) ?? "full";
|
|
211130
|
+
const inUse = Object.keys(ENGINE_WORDS).filter((e) => uses[e]).map((e) => ENGINE_WORDS[e]);
|
|
211131
|
+
const why = inUse.length > 0 ? `uses ${inUse.join(" + ")}` : "UI only, no engine use detected";
|
|
210859
211132
|
if (requested === "auto")
|
|
210860
|
-
return { variant: detected, detected };
|
|
211133
|
+
return { variant: detected, detected, why };
|
|
210861
211134
|
const variant = requested;
|
|
210862
|
-
const missing =
|
|
210863
|
-
uses.gl && variant !== "3d" && variant !== "full" ? "3D" : null,
|
|
210864
|
-
uses.g2 && variant !== "2d" && variant !== "full" ? "2D" : null
|
|
210865
|
-
].filter(Boolean);
|
|
211135
|
+
const missing = lacks(variant, uses);
|
|
210866
211136
|
if (missing.length > 0) {
|
|
210867
|
-
warn(`project uses
|
|
211137
|
+
warn(`project uses ${missing.join(" + ")} but ${configKey} forces "${variant}", which has no such engine — that part won't run on device; remove ${configKey} to auto-select "${detected}".`);
|
|
210868
211138
|
}
|
|
210869
|
-
return { variant, detected };
|
|
211139
|
+
return { variant, detected, why };
|
|
210870
211140
|
};
|
|
210871
211141
|
var ANDROID_SDK_VARIANTS = [...SDK_VARIANTS, "vulkan"];
|
|
210872
|
-
var ANDROID_SDK_VULKAN_MIN = "2.
|
|
211142
|
+
var ANDROID_SDK_VULKAN_MIN = "2.2.11";
|
|
210873
211143
|
var androidSdkArtifact = (variant, sdkVersion) => {
|
|
210874
211144
|
if (variant === "vulkan" && sdkVersion && cmpVersion(sdkVersion, ANDROID_SDK_VULKAN_MIN) < 0) {
|
|
210875
211145
|
throw new CliError(`android.variant "vulkan" needs lecodes-android-sdk >= ${ANDROID_SDK_VULKAN_MIN} (pinned: ${sdkVersion}) — run \`lecodes app update\` or set android.sdk.`);
|
|
@@ -212992,7 +213262,7 @@ engines the bundle uses and cannot see those, so a project using \`Physics\`,
|
|
|
212992
213262
|
|
|
212993
213263
|
\`android.variant: "vulkan"\` links \`lecodes-sdk-vulkan\`: the full engine set with Filament on
|
|
212994
213264
|
Vulkan (OpenGL ES where the device has no Vulkan 1.1), \`arm64-v8a\` only — for 3D games. Never
|
|
212995
|
-
auto-selected; needs lecodes-android-sdk >= 2.
|
|
213265
|
+
auto-selected; needs lecodes-android-sdk >= 2.2.11.
|
|
212996
213266
|
|
|
212997
213267
|
## App-local native code
|
|
212998
213268
|
|
|
@@ -213757,7 +214027,7 @@ var initAndroid = async (opts) => {
|
|
|
213757
214027
|
if (existsSync19(androidDir)) {
|
|
213758
214028
|
throw new CliError("android/ already exists — run `lecodes app sync` to update it.");
|
|
213759
214029
|
}
|
|
213760
|
-
const existing =
|
|
214030
|
+
const existing = readAppConfigAt(root) ?? {};
|
|
213761
214031
|
const variant = opts.variant ?? existing.android?.variant ?? "auto";
|
|
213762
214032
|
if (variant !== "auto" && !ANDROID_SDK_VARIANTS.includes(variant)) {
|
|
213763
214033
|
throw new CliError(`--variant must be auto, ${ANDROID_SDK_VARIANTS.join(", ")} (got "${variant}")`);
|
|
@@ -213868,20 +214138,14 @@ var syncAndroid = async (root, config, prior) => {
|
|
|
213868
214138
|
if (requestedVariant !== "auto" && !ANDROID_SDK_VARIANTS.includes(requestedVariant)) {
|
|
213869
214139
|
throw new CliError(`app.json android.variant must be "auto" or one of ${ANDROID_SDK_VARIANTS.join(", ")} (got "${requestedVariant}").`);
|
|
213870
214140
|
}
|
|
213871
|
-
const { variant: covered,
|
|
214141
|
+
const { variant: covered, why } = resolveVariant(requestedVariant === "auto" ? "auto" : androidVariantEngines(requestedVariant), bundleJs, "android.variant");
|
|
213872
214142
|
const variant = requestedVariant === "vulkan" ? "vulkan" : covered;
|
|
213873
214143
|
let sdkArtifact = androidSdkArtifact(variant, sdkVersion);
|
|
213874
214144
|
if (sdkArtifact !== "lecodes-sdk" && cmpVersion(sdkVersion, ANDROID_SDK_VARIANTS_MIN) < 0) {
|
|
213875
214145
|
warn(`lecodes-android-sdk ${sdkVersion} predates the variant matrix (${ANDROID_SDK_VARIANTS_MIN}) — linking the full lecodes-sdk AAR; run \`lecodes app update\` to pick up variant builds.`);
|
|
213876
214146
|
sdkArtifact = "lecodes-sdk";
|
|
213877
214147
|
}
|
|
213878
|
-
|
|
213879
|
-
full: "3D + 2D engines in use",
|
|
213880
|
-
"3d": "3D in use, no 2D",
|
|
213881
|
-
"2d": "2D in use, no 3D",
|
|
213882
|
-
core: "no 3D/2D engine use detected"
|
|
213883
|
-
};
|
|
213884
|
-
log(`${c.green("variant")} ${variant} → ${sdkArtifact}${requestedVariant === "auto" ? c.dim(` (auto: ${WHY[detected]})`) : c.dim(" (forced by android.variant)")}`);
|
|
214148
|
+
log(`${c.green("variant")} ${variant} → ${sdkArtifact}${requestedVariant === "auto" ? c.dim(` (auto: ${why})`) : c.dim(" (forced by android.variant)")}`);
|
|
213885
214149
|
const lockPath = join21(root, "plugins.lock.json");
|
|
213886
214150
|
const oldLock = readJson2(lockPath)?.plugins ?? {};
|
|
213887
214151
|
const pluginSpecs = Object.entries(config.plugins ?? {});
|
|
@@ -214114,7 +214378,7 @@ var androidBuildFlags = {
|
|
|
214114
214378
|
};
|
|
214115
214379
|
var buildAndroid = async (flags) => {
|
|
214116
214380
|
const root = projectRoot();
|
|
214117
|
-
const config =
|
|
214381
|
+
const config = readAppConfigAt(root);
|
|
214118
214382
|
const androidDir = join21(root, "android");
|
|
214119
214383
|
if (!config?.android || !existsSync19(androidDir)) {
|
|
214120
214384
|
throw new CliError("No Android shell here — run `lecodes app init android` first.");
|
|
@@ -214318,7 +214582,7 @@ var writeSigningConfig = (root, config, storeFile, alias, storePassword, keyPass
|
|
|
214318
214582
|
}
|
|
214319
214583
|
}
|
|
214320
214584
|
const configPath = join21(root, "app.json");
|
|
214321
|
-
const fresh =
|
|
214585
|
+
const fresh = readAppConfigAt(root) ?? config;
|
|
214322
214586
|
fresh.android = { ...fresh.android ?? {}, signing: { storeFile: envPath, keyAlias: alias } };
|
|
214323
214587
|
writeJson(configPath, fresh);
|
|
214324
214588
|
log(`${c.green("written")} app.json (android.signing — metadata only, no passwords)`);
|
|
@@ -214334,7 +214598,7 @@ var printCertificate = (storeFile, alias, storePassword) => {
|
|
|
214334
214598
|
var PLAY_APP_SIGNING_NOTE = "With Play App Signing (default for new Play apps) this is the UPLOAD key — Google keeps the app signing key, and a lost upload key can be reset through Play support. Without it, losing this file means the app can never be updated. Back it up either way.";
|
|
214335
214599
|
var keystoreProject = () => {
|
|
214336
214600
|
const root = projectRoot();
|
|
214337
|
-
return { root, config:
|
|
214601
|
+
return { root, config: readAppConfigAt(root) ?? {} };
|
|
214338
214602
|
};
|
|
214339
214603
|
var passwordFlag = { password: str("keystore password (else asked on the terminal)", { value: "<pw>" }) };
|
|
214340
214604
|
var keystoreCreateFlags = {
|
|
@@ -215465,7 +215729,7 @@ var dirSize2 = (dir) => readdirSync8(dir, { withFileTypes: true }).reduce((sum,
|
|
|
215465
215729
|
}, 0);
|
|
215466
215730
|
var initDesktop = async (opts) => {
|
|
215467
215731
|
const root = projectRoot();
|
|
215468
|
-
const existing =
|
|
215732
|
+
const existing = readAppConfigAt(root) ?? {};
|
|
215469
215733
|
const name = opts.name ?? existing.name ?? basename8(root);
|
|
215470
215734
|
const window2 = {
|
|
215471
215735
|
width: 1280,
|
|
@@ -215499,7 +215763,7 @@ var desktopBuildFlags = {
|
|
|
215499
215763
|
};
|
|
215500
215764
|
var buildDesktop = async (flags) => {
|
|
215501
215765
|
const root = projectRoot();
|
|
215502
|
-
const config =
|
|
215766
|
+
const config = readAppConfigAt(root) ?? {};
|
|
215503
215767
|
const name = (config.name ?? basename8(root)).trim() || "app";
|
|
215504
215768
|
const slug = flags.slug ?? cleanFileName(name);
|
|
215505
215769
|
const out = resolve11(root, flags.out ?? join24(root, "dist", slug));
|
|
@@ -215776,7 +216040,7 @@ var initIos = async (opts) => {
|
|
|
215776
216040
|
if (existsSync23(iosDir)) {
|
|
215777
216041
|
throw new CliError("ios/ already exists — run `lecodes app sync` to update it.");
|
|
215778
216042
|
}
|
|
215779
|
-
const existing =
|
|
216043
|
+
const existing = readAppConfigAt(root) ?? {};
|
|
215780
216044
|
const variant = opts.variant ?? existing.ios?.variant ?? "auto";
|
|
215781
216045
|
if (variant !== "auto" && !SDK_VARIANTS.includes(variant)) {
|
|
215782
216046
|
throw new CliError(`--variant must be auto, ${SDK_VARIANTS.join(", ")} (got "${variant}")`);
|
|
@@ -215865,7 +216129,7 @@ var init = defineCommand({
|
|
|
215865
216129
|
});
|
|
215866
216130
|
var runSync = async (platformArg) => {
|
|
215867
216131
|
const root = projectRoot();
|
|
215868
|
-
const config =
|
|
216132
|
+
const config = readAppConfigAt(root);
|
|
215869
216133
|
const platform = mobilePlatform(platformArg);
|
|
215870
216134
|
const { hasIos, hasAndroid } = shellsOf(root, config);
|
|
215871
216135
|
if (platform === "ios" && !hasIos)
|
|
@@ -215946,14 +216210,8 @@ var syncIos = async (root, config) => {
|
|
|
215946
216210
|
mkdirSync15(resourcesDir, { recursive: true });
|
|
215947
216211
|
const resources = await compileShell(join25(resourcesDir, "app.js"), { remotePatterns });
|
|
215948
216212
|
const bundleJs = readFileSync20(join25(resourcesDir, "app.js"), "utf8");
|
|
215949
|
-
const { variant,
|
|
215950
|
-
|
|
215951
|
-
core: "UI only",
|
|
215952
|
-
"2d": "uses the 2D engine",
|
|
215953
|
-
"3d": "uses the 3D engine",
|
|
215954
|
-
full: "uses the 3D + 2D engines"
|
|
215955
|
-
};
|
|
215956
|
-
log(`${c.green("variant")} ${variant}${requestedVariant === "auto" ? ` (auto: ${WHY[detected]})` : " (forced by ios.variant)"}`);
|
|
216213
|
+
const { variant, why } = resolveVariant(requestedVariant, bundleJs);
|
|
216214
|
+
log(`${c.green("variant")} ${variant}${requestedVariant === "auto" ? ` (auto: ${why})` : " (forced by ios.variant)"}`);
|
|
215957
216215
|
const sdkWhy = sdkPin === "latest" ? "latest" : config.ios.sdk ? "pinned by ios.sdk" : "from the shell — set ios.sdk or run `lecodes app update`";
|
|
215958
216216
|
log(`${c.green("sdk")} lecodes-ios-sdk ${sdkVersion}${c.dim(` (${sdkWhy})`)}`);
|
|
215959
216217
|
const preload = /^\/\/ preload: (.*)$/m.exec(bundleJs)?.[1].split(";").map((s) => s.trim()).filter(Boolean) ?? [];
|
|
@@ -216076,7 +216334,7 @@ Native OTA JS updates at launch (both shells; the current launch runs the newest
|
|
|
216076
216334
|
var pluginConfig = () => {
|
|
216077
216335
|
const root = projectRoot();
|
|
216078
216336
|
const configPath = join25(root, "app.json");
|
|
216079
|
-
const config =
|
|
216337
|
+
const config = readAppConfigAt(root);
|
|
216080
216338
|
if (!config)
|
|
216081
216339
|
throw new CliError("No app.json here — run `lecodes app init <ios|android>` first.");
|
|
216082
216340
|
config.plugins ??= {};
|
|
@@ -216181,7 +216439,7 @@ var updateSdkPin = async (root, config, wanted, checkOnly, platform) => {
|
|
|
216181
216439
|
};
|
|
216182
216440
|
var runAppUpdate = async (opts = {}) => {
|
|
216183
216441
|
const root = projectRoot();
|
|
216184
|
-
const config =
|
|
216442
|
+
const config = readAppConfigAt(root);
|
|
216185
216443
|
const platform = mobilePlatform(opts.platform);
|
|
216186
216444
|
const { hasIos, hasAndroid } = shellsOf(root, config);
|
|
216187
216445
|
if (platform === "ios" && !hasIos)
|
|
@@ -216339,7 +216597,7 @@ var doctor = defineCommand({
|
|
|
216339
216597
|
examples: ["lecodes app doctor"],
|
|
216340
216598
|
run: async () => {
|
|
216341
216599
|
const root = projectRoot();
|
|
216342
|
-
const config =
|
|
216600
|
+
const config = readAppConfigAt(root);
|
|
216343
216601
|
const { hasIos, hasAndroid } = shellsOf(root, config);
|
|
216344
216602
|
if (!hasIos && !hasAndroid)
|
|
216345
216603
|
throw new CliError(NO_IOS_SHELL);
|
|
@@ -216386,7 +216644,7 @@ var exportOptionsPlist = (method, teamId, destination) => `<?xml version="1.0" e
|
|
|
216386
216644
|
`;
|
|
216387
216645
|
var requireShell = () => {
|
|
216388
216646
|
const root = projectRoot();
|
|
216389
|
-
const config =
|
|
216647
|
+
const config = readAppConfigAt(root);
|
|
216390
216648
|
if (!config?.ios || !existsSync23(join25(root, "ios")))
|
|
216391
216649
|
throw new CliError(NO_IOS_SHELL);
|
|
216392
216650
|
return { root, config };
|
|
@@ -216499,7 +216757,7 @@ var iosUpload = defineCommand({
|
|
|
216499
216757
|
"-allowProvisioningUpdates"
|
|
216500
216758
|
]);
|
|
216501
216759
|
const configPath = join25(root, "app.json");
|
|
216502
|
-
const fresh =
|
|
216760
|
+
const fresh = readAppConfigAt(root);
|
|
216503
216761
|
fresh.ios.buildNumber = (fresh.ios.buildNumber ?? 1) + 1;
|
|
216504
216762
|
writeJson(configPath, fresh);
|
|
216505
216763
|
success(`Uploaded build ${(fresh.ios.buildNumber ?? 2) - 1} — check App Store Connect / TestFlight once processing finishes.`);
|
|
@@ -216745,7 +217003,7 @@ lecodes render --desktop --view iso --png iso.png # 3D: inspection camera fitt
|
|
|
216745
217003
|
# { "view": "iso", "screenshot": "iso.png" } then { "view": "off" }
|
|
216746
217004
|
lecodes render --desktop --script <file> # the same scenario on the NATIVE host (real GPU, the
|
|
216747
217005
|
# real hit-test): the same steps, targets and expects as
|
|
216748
|
-
# headless (only expect.audio / the json output are headless-only)
|
|
217006
|
+
# headless (only expect.audio / expect.toast / the json output are headless-only)
|
|
216749
217007
|
lecodes render --sim --script <file> # the WINDOWLESS native host (lecodes-server): the real
|
|
216750
217008
|
# engines, no GPU, no pixels, no UI hit-test — expect.log /
|
|
216751
217009
|
# expect.paint only; runs on CI and beside an open desktop window
|
|
@@ -216791,11 +217049,12 @@ Three hosts run the same file, at a fixed 16 ms clock on all of them:
|
|
|
216791
217049
|
|---|---|---|---|---|
|
|
216792
217050
|
| headless | (none) | Bun: the runtime + UI renderer + a recording 3D core (no GPU) | names or \`{ x, y }\` | all |
|
|
216793
217051
|
| sim | \`--sim\` | the windowless native server: real engines, no pixels, no UI | \`{ x, y }\`, straight to the scene | \`log\`, \`paint\` |
|
|
216794
|
-
| desktop | \`--desktop\` | the hidden desktop window: pixels, GL materials, the real hit-test | names or \`{ x, y }\` | all but \`audio\` |
|
|
217052
|
+
| desktop | \`--desktop\` | the hidden desktop window: pixels, GL materials, the real hit-test | names or \`{ x, y }\` | all but \`audio\` / \`toast\` |
|
|
216795
217053
|
|
|
216796
217054
|
One flow file, the same verdict on headless and desktop: names resolve the same way (\`name\` →
|
|
216797
217055
|
exact text → substring → placeholder), every step runs (\`hover\`, \`type\`, \`fill\`, \`scroll\`,
|
|
216798
|
-
\`clip\` included) and the semantic expects read the same tree. Only \`expect.audio
|
|
217056
|
+
\`clip\` included) and the semantic expects read the same tree. Only \`expect.audio\`,
|
|
217057
|
+
\`expect.toast\` (the last \`toast()\` message — a toast is not in the tree) and the
|
|
216799
217058
|
\`json\` output are the headless renderer's. On the desktop \`type\` taps the input and types into
|
|
216800
217059
|
it (appends); the sim has no UI, so it takes coordinates and \`expect.log\` / \`expect.paint\` only.
|
|
216801
217060
|
|
|
@@ -216948,10 +217207,11 @@ init_cli();
|
|
|
216948
217207
|
import { basename as basename14 } from "node:path";
|
|
216949
217208
|
|
|
216950
217209
|
// src/dev/projectBackend.ts
|
|
216951
|
-
|
|
217210
|
+
init_host4();
|
|
216952
217211
|
init_projectCompile();
|
|
216953
217212
|
init_peerInstall();
|
|
216954
217213
|
init_manifest();
|
|
217214
|
+
init_shared();
|
|
216955
217215
|
import { rmSync as rmSync9 } from "node:fs";
|
|
216956
217216
|
import { join as join35, relative as relative5 } from "node:path";
|
|
216957
217217
|
|
|
@@ -217044,7 +217304,7 @@ var startLocalBackend = (options) => {
|
|
|
217044
217304
|
log(`[server:error] the server process exited (${status ?? "killed"}) — it starts again with the next call`);
|
|
217045
217305
|
}
|
|
217046
217306
|
});
|
|
217047
|
-
child.send({ t: "load", code: bundle, dbDir, filesDir, root: options.root });
|
|
217307
|
+
child.send({ t: "load", code: bundle, dbDir, filesDir, root: options.root, push: options.push ?? null });
|
|
217048
217308
|
});
|
|
217049
217309
|
const stop = async (l) => {
|
|
217050
217310
|
if (live === l)
|
|
@@ -217405,7 +217665,15 @@ var createProjectBackend = (options) => {
|
|
|
217405
217665
|
subscribe: (_scope, ch, args, ctx) => backend ? backend.subscribe(ch, args, ctx) : Promise.resolve({ ok: false, status: 404, message: "The project has no server files" })
|
|
217406
217666
|
});
|
|
217407
217667
|
const sockets = new Set;
|
|
217408
|
-
const
|
|
217668
|
+
const push = (() => {
|
|
217669
|
+
try {
|
|
217670
|
+
const { apiUrl, token } = session();
|
|
217671
|
+
return { apiUrl, token, uuid: readManifest(root).uuid };
|
|
217672
|
+
} catch {
|
|
217673
|
+
return null;
|
|
217674
|
+
}
|
|
217675
|
+
})();
|
|
217676
|
+
const start = () => startLocalBackend({ root, dbDir, log, push, onPublish: (ch, group, data) => hub.publish("", ch, group, data) });
|
|
217409
217677
|
const envelope = (body) => new Response(JSON.stringify(body), { status: 200, headers: { "content-type": "application/json", "cache-control": "no-store", ...CORS } });
|
|
217410
217678
|
return {
|
|
217411
217679
|
compile: async (entries) => {
|
|
@@ -219979,8 +220247,8 @@ var lightmap_default = defineCommand({
|
|
|
219979
220247
|
// src/commands/link.ts
|
|
219980
220248
|
init_cli();
|
|
219981
220249
|
init_api();
|
|
219982
|
-
import { existsSync as
|
|
219983
|
-
import { basename as basename21, join as join44, resolve as
|
|
220250
|
+
import { existsSync as existsSync40 } from "node:fs";
|
|
220251
|
+
import { basename as basename21, join as join44, resolve as resolve22 } from "node:path";
|
|
219984
220252
|
init_ignore();
|
|
219985
220253
|
init_localFiles();
|
|
219986
220254
|
init_manifest();
|
|
@@ -219990,14 +220258,148 @@ init_types4();
|
|
|
219990
220258
|
// src/commands/push.ts
|
|
219991
220259
|
init_cli();
|
|
219992
220260
|
init_api();
|
|
219993
|
-
import { readFileSync as
|
|
220261
|
+
import { readFileSync as readFileSync32 } from "node:fs";
|
|
219994
220262
|
import { basename as basename20 } from "node:path";
|
|
219995
220263
|
init_localFiles();
|
|
219996
220264
|
init_manifest();
|
|
220265
|
+
|
|
220266
|
+
// src/commands/releases.ts
|
|
220267
|
+
init_cli();
|
|
220268
|
+
init_api();
|
|
220269
|
+
init_localFiles();
|
|
220270
|
+
import { existsSync as existsSync39, readFileSync as readFileSync31 } from "node:fs";
|
|
220271
|
+
import { resolve as resolve21 } from "node:path";
|
|
220272
|
+
init_shared();
|
|
220273
|
+
var compareVersions = (a, b) => {
|
|
220274
|
+
const pa = a.split("."), pb = b.split(".");
|
|
220275
|
+
for (let i = 0;i < Math.max(pa.length, pb.length); i++) {
|
|
220276
|
+
const d = (parseInt(pa[i] ?? "0", 10) || 0) - (parseInt(pb[i] ?? "0", 10) || 0);
|
|
220277
|
+
if (d)
|
|
220278
|
+
return d;
|
|
220279
|
+
}
|
|
220280
|
+
return 0;
|
|
220281
|
+
};
|
|
220282
|
+
var formatSize = (bytes) => bytes < 1024 ? `${bytes} B` : bytes < 1024 * 1024 ? `${(bytes / 1024).toFixed(1)} KB` : `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
220283
|
+
var qrUrl = (apiUrl, uuid) => `${apiUrl}/qr/${uuid}`;
|
|
220284
|
+
var reportRelease = (release, qr, verb = "Published") => {
|
|
220285
|
+
const parts = [release.unchanged ? `Nothing changed — ${release.version} is current` : `${verb} ${release.version}`, release.url.replace(/\/+$/, "")];
|
|
220286
|
+
if (!release.unchanged) {
|
|
220287
|
+
if (typeof release.size === "number")
|
|
220288
|
+
parts.push(formatSize(release.size));
|
|
220289
|
+
if (release.sdk)
|
|
220290
|
+
parts.push(`SDK ${release.sdk}`);
|
|
220291
|
+
}
|
|
220292
|
+
success(parts.join(" · "));
|
|
220293
|
+
info(` ${c.dim("QR")} ${qr}`);
|
|
220294
|
+
};
|
|
220295
|
+
var withSchemaConfirm = async (call, opts) => {
|
|
220296
|
+
try {
|
|
220297
|
+
return await call(false);
|
|
220298
|
+
} catch (e) {
|
|
220299
|
+
if (!(e instanceof SchemaChangeError))
|
|
220300
|
+
throw e;
|
|
220301
|
+
if (e.incompatible !== null) {
|
|
220302
|
+
throw new CliError(`The production database cannot migrate to this schema in place${e.incompatible ? `: ${e.incompatible}` : ""}. Nothing changed.`, 409);
|
|
220303
|
+
}
|
|
220304
|
+
warn("The schema change deletes data in the production database:");
|
|
220305
|
+
for (const op of e.destructive)
|
|
220306
|
+
log(c.red(` ${op}`));
|
|
220307
|
+
if (!opts.allowDestructive) {
|
|
220308
|
+
if (!process.stdin.isTTY)
|
|
220309
|
+
throw new CliError(`Nothing changed. Run \`${opts.retry} --allow-destructive\` to apply it.`, 409);
|
|
220310
|
+
if (!await confirm("Delete that data and go on?", false)) {
|
|
220311
|
+
info(c.dim("Cancelled — nothing changed."));
|
|
220312
|
+
return null;
|
|
220313
|
+
}
|
|
220314
|
+
}
|
|
220315
|
+
return await call(true);
|
|
220316
|
+
}
|
|
220317
|
+
};
|
|
220318
|
+
var allowDestructiveFlag = { "allow-destructive": bool("apply a schema change that deletes production data without asking") };
|
|
220319
|
+
var use = defineCommand({
|
|
220320
|
+
name: "use",
|
|
220321
|
+
usage: "<version>",
|
|
220322
|
+
summary: "Make a release current again — a rollback, no compile",
|
|
220323
|
+
description: "The release's bundle becomes what the project's address, the viewers and the store run, and its server is deployed to production again (the database migrates to that release's schema: a change that deletes data is shown and asked about, like a publish).",
|
|
220324
|
+
flags: allowDestructiveFlag,
|
|
220325
|
+
examples: ["lecodes releases use 1.2.3"],
|
|
220326
|
+
run: async ({ args, flags }) => {
|
|
220327
|
+
const version = args[0]?.replace(/^v/, "");
|
|
220328
|
+
if (!version)
|
|
220329
|
+
throw new UsageError("Which release? `lecodes releases` lists them.");
|
|
220330
|
+
const { apiUrl, token, manifest } = linkedProject();
|
|
220331
|
+
const releases = await listReleases(apiUrl, token, manifest.uuid);
|
|
220332
|
+
const target = releases.find((r) => r.version === version);
|
|
220333
|
+
if (!target)
|
|
220334
|
+
throw new CliError(`${manifest.name} has no release ${version} — \`lecodes releases\` lists them.`);
|
|
220335
|
+
const previous = releases.find((r) => r.current);
|
|
220336
|
+
const res = await withSchemaConfirm((allowDestructive) => useRelease(apiUrl, token, manifest.uuid, version, { allowDestructive }), { allowDestructive: flags["allow-destructive"], retry: `lecodes releases use ${version}` });
|
|
220337
|
+
if (!res)
|
|
220338
|
+
return;
|
|
220339
|
+
const back = previous !== undefined && compareVersions(res.version, previous.version) < 0;
|
|
220340
|
+
reportRelease({ ...target, ...res }, qrUrl(apiUrl, manifest.uuid), back ? "Rolled back to" : "Published");
|
|
220341
|
+
}
|
|
220342
|
+
});
|
|
220343
|
+
var upload2 = defineCommand({
|
|
220344
|
+
name: "upload",
|
|
220345
|
+
usage: "<app.js>",
|
|
220346
|
+
summary: "Make a bundle compiled here the current release — no build on the platform",
|
|
220347
|
+
description: "For a bundle the platform's build service cannot make any more: compiled with an older SDK's CLI (`compile --remote-assets`, so it names the project's uploaded resources), it becomes the current release with its own `// sdk:` — what the viewers of that SDK line run. Local changes are pushed first as the release's checkpoint. A project with server code cannot take one (its server is built with the client).",
|
|
220348
|
+
flags: { message: str('checkpoint message when there are local changes (default "Release")', { alias: "m", value: "<text>" }) },
|
|
220349
|
+
examples: ["lecodes releases upload app.js", 'lecodes releases upload build/app.js -m "Kungur for the 2.1 app"'],
|
|
220350
|
+
run: async ({ args, flags }) => {
|
|
220351
|
+
const file = args[0];
|
|
220352
|
+
if (!file)
|
|
220353
|
+
throw new UsageError("Which bundle? `lecodes releases upload <app.js>`.");
|
|
220354
|
+
const path = resolve21(file);
|
|
220355
|
+
if (!existsSync39(path))
|
|
220356
|
+
throw new CliError(`No such file: ${file}`);
|
|
220357
|
+
const code = readFileSync31(path, "utf8");
|
|
220358
|
+
const sdk = /^\/\/ sdk: (\S+)/m.exec(code)?.[1];
|
|
220359
|
+
if (!sdk)
|
|
220360
|
+
throw new CliError(`${file} is not a LeCodes bundle: no \`// sdk:\` header line. \`lecodes compile --remote-assets -o app.js\` writes one.`);
|
|
220361
|
+
const map = existsSync39(`${path}.map`) ? readFileSync31(`${path}.map`, "utf8") : null;
|
|
220362
|
+
const { root, manifest, apiUrl, token } = linkedProject();
|
|
220363
|
+
if (hasChanges(diffLocal(manifest, scanProject(root, manifest))))
|
|
220364
|
+
await runPush({ message: flags.message || "Release" });
|
|
220365
|
+
const release = await uploadReleaseBundle(apiUrl, token, manifest.uuid, { code, map });
|
|
220366
|
+
reportRelease(release, qrUrl(apiUrl, manifest.uuid), "Uploaded");
|
|
220367
|
+
}
|
|
220368
|
+
});
|
|
220369
|
+
var releases_default = defineCommand({
|
|
220370
|
+
name: "releases",
|
|
220371
|
+
summary: "List the project's releases (● = the current one); `releases use <version>` rolls back, `releases upload <app.js>` releases a bundle compiled here",
|
|
220372
|
+
description: "Every `lecodes publish` (or the editor's Publish button) makes a release: an immutable build with its own version. The current one is what https://<handle>.le.codes, the viewers and the store run.",
|
|
220373
|
+
commands: [use, upload2],
|
|
220374
|
+
examples: ["lecodes releases", "lecodes releases use 1.2.3", "lecodes releases upload app.js"],
|
|
220375
|
+
run: async () => {
|
|
220376
|
+
const { apiUrl, token, manifest } = linkedProject();
|
|
220377
|
+
const releases = await listReleases(apiUrl, token, manifest.uuid);
|
|
220378
|
+
if (releases.length === 0) {
|
|
220379
|
+
info(`${manifest.name} has no releases yet — \`lecodes publish\` makes the first.`);
|
|
220380
|
+
return;
|
|
220381
|
+
}
|
|
220382
|
+
table([
|
|
220383
|
+
["", c.dim("VERSION"), c.dim("DATE"), c.dim("SIZE"), c.dim("SDK")],
|
|
220384
|
+
...releases.map((r) => [
|
|
220385
|
+
r.current ? c.green("●") : "",
|
|
220386
|
+
r.current ? c.bold(r.version) : r.version,
|
|
220387
|
+
relativeTime(r.createdAt),
|
|
220388
|
+
formatSize(r.size),
|
|
220389
|
+
r.sdk
|
|
220390
|
+
])
|
|
220391
|
+
]);
|
|
220392
|
+
if (!releases.some((r) => r.current))
|
|
220393
|
+
info(c.dim(" Unpublished: no release is current."));
|
|
220394
|
+
}
|
|
220395
|
+
});
|
|
220396
|
+
|
|
220397
|
+
// src/commands/push.ts
|
|
219997
220398
|
init_shared();
|
|
219998
220399
|
var pushFlags = {
|
|
219999
220400
|
message: str("checkpoint message (asked for when omitted)", { alias: "m", value: "<text>" }),
|
|
220000
|
-
publish: bool("also
|
|
220401
|
+
publish: bool("also release the project with the push (= `lecodes publish`)"),
|
|
220402
|
+
"force-publish": bool("--publish, and release even when nothing changed — a new SDK on the build service (= `lecodes publish --force`)"),
|
|
220001
220403
|
force: bool("push even if the server moved on (shows + confirms which server files get overwritten)"),
|
|
220002
220404
|
yes: bool("skip the --force overwrite confirmation", { alias: "y" })
|
|
220003
220405
|
};
|
|
@@ -220005,18 +220407,21 @@ var push_default = defineCommand({
|
|
|
220005
220407
|
name: "push",
|
|
220006
220408
|
summary: "Push local changes as one new checkpoint",
|
|
220007
220409
|
flags: pushFlags,
|
|
220008
|
-
examples: ['lecodes push -m "Tweak the menu"', "lecodes push --publish"],
|
|
220410
|
+
examples: ['lecodes push -m "Tweak the menu"', "lecodes push --publish", "lecodes push --force-publish"],
|
|
220009
220411
|
run: ({ flags }) => runPush(flags)
|
|
220010
220412
|
});
|
|
220011
220413
|
var runPush = async (opts) => {
|
|
220012
220414
|
const { root, manifest, apiUrl, token } = linkedProject();
|
|
220013
|
-
const
|
|
220415
|
+
const forcePublish = !!opts["force-publish"];
|
|
220416
|
+
const publish = !!opts.publish || forcePublish;
|
|
220014
220417
|
const scan = scanProject(root, manifest);
|
|
220015
220418
|
const diff = diffLocal(manifest, scan);
|
|
220016
220419
|
if (!hasChanges(diff)) {
|
|
220017
220420
|
if (publish) {
|
|
220018
|
-
|
|
220019
|
-
|
|
220421
|
+
info(c.dim("Working tree clean — publishing the project as it is."));
|
|
220422
|
+
const release = await withSchemaConfirm((allowDestructive) => publishProject(apiUrl, token, manifest.uuid, { allowDestructive, force: forcePublish }), { allowDestructive: false, retry: "lecodes publish" });
|
|
220423
|
+
if (release)
|
|
220424
|
+
reportRelease(release, qrUrl(apiUrl, manifest.uuid));
|
|
220020
220425
|
} else
|
|
220021
220426
|
info(c.dim("Working tree clean — nothing to push."));
|
|
220022
220427
|
await flushComments();
|
|
@@ -220058,9 +220463,9 @@ var runPush = async (opts) => {
|
|
|
220058
220463
|
if (source)
|
|
220059
220464
|
entry.assetId = source.id;
|
|
220060
220465
|
if (isTextKind(kind))
|
|
220061
|
-
entry.text =
|
|
220466
|
+
entry.text = readFileSync32(file.absPath, "utf8");
|
|
220062
220467
|
else if (!source || source.sha !== file.sha) {
|
|
220063
|
-
const blob = await uploadBlob(apiUrl, token, manifest.uuid, basename20(file.path),
|
|
220468
|
+
const blob = await uploadBlob(apiUrl, token, manifest.uuid, basename20(file.path), readFileSync32(file.absPath));
|
|
220064
220469
|
entry.blobFileId = blob.id;
|
|
220065
220470
|
uploaded++;
|
|
220066
220471
|
}
|
|
@@ -220069,11 +220474,11 @@ var runPush = async (opts) => {
|
|
|
220069
220474
|
const folders = scan.dirs.map((path) => ({ path }));
|
|
220070
220475
|
if (uploaded > 0)
|
|
220071
220476
|
info(c.dim(`Uploaded ${uploaded} binary file${uploaded === 1 ? "" : "s"}.`));
|
|
220072
|
-
const res = await sync(apiUrl, token, manifest.uuid, { message, files, folders, publish });
|
|
220477
|
+
const res = await sync(apiUrl, token, manifest.uuid, { message, files, folders, publish, ...forcePublish ? { force: true } : {} });
|
|
220073
220478
|
if (!res.created) {
|
|
220074
220479
|
info(c.dim("Server reported no changes."));
|
|
220075
220480
|
if (publish)
|
|
220076
|
-
reportPublish(res);
|
|
220481
|
+
reportPublish(res, qrUrl(apiUrl, manifest.uuid));
|
|
220077
220482
|
return;
|
|
220078
220483
|
}
|
|
220079
220484
|
const shaByPath = new Map(scan.files.map((f) => [f.path, f.sha]));
|
|
@@ -220089,7 +220494,7 @@ var runPush = async (opts) => {
|
|
|
220089
220494
|
].filter(Boolean).join(", ");
|
|
220090
220495
|
success(`Pushed checkpoint${res.seq ? ` #${res.seq}` : ""}${counts ? ` (${counts})` : ""}: ${message}`);
|
|
220091
220496
|
if (publish)
|
|
220092
|
-
reportPublish(res);
|
|
220497
|
+
reportPublish(res, qrUrl(apiUrl, manifest.uuid));
|
|
220093
220498
|
await flushComments();
|
|
220094
220499
|
};
|
|
220095
220500
|
var flushComments = async () => {
|
|
@@ -220112,11 +220517,11 @@ var serverPathDrift = (project, manifest) => {
|
|
|
220112
220517
|
const parts = [added.length ? describe("added", added) : "", removed.length ? describe("removed", removed) : ""].filter(Boolean);
|
|
220113
220518
|
return parts.length ? parts.join("; ") : null;
|
|
220114
220519
|
};
|
|
220115
|
-
var reportPublish = (res) => {
|
|
220116
|
-
if (res.published)
|
|
220117
|
-
|
|
220520
|
+
var reportPublish = (res, qr) => {
|
|
220521
|
+
if (res.published && res.release)
|
|
220522
|
+
reportRelease(res.release, qr);
|
|
220118
220523
|
else
|
|
220119
|
-
warn(`Push landed, but publish failed: ${res.publishError ?? "unknown error"}
|
|
220524
|
+
warn(`Push landed, but publish failed: ${res.publishError ?? "unknown error"} — \`lecodes publish\` tries again.`);
|
|
220120
220525
|
};
|
|
220121
220526
|
|
|
220122
220527
|
// src/commands/link.ts
|
|
@@ -220171,10 +220576,10 @@ var link_default = defineCommand({
|
|
|
220171
220576
|
run: async ({ args, flags }) => {
|
|
220172
220577
|
const { apiUrl, token } = session();
|
|
220173
220578
|
const dirArg = args[0];
|
|
220174
|
-
const root = dirArg ?
|
|
220175
|
-
if (!
|
|
220579
|
+
const root = dirArg ? resolve22(dirArg) : process.cwd();
|
|
220580
|
+
if (!existsSync40(root))
|
|
220176
220581
|
throw new CliError(`No such directory: ${root}`);
|
|
220177
|
-
if (
|
|
220582
|
+
if (existsSync40(join44(root, LECODES_DIR, "manifest.json")) && !flags.force) {
|
|
220178
220583
|
throw new CliError("This folder is already linked (.lecodes/manifest.json exists) — use `lecodes push`, or `lecodes link --force` to re-link it to another project.");
|
|
220179
220584
|
}
|
|
220180
220585
|
let enclosing = null;
|
|
@@ -220260,9 +220665,9 @@ var login_default = defineCommand({
|
|
|
220260
220665
|
});
|
|
220261
220666
|
|
|
220262
220667
|
// src/commands/navmesh.ts
|
|
220263
|
-
import { existsSync as
|
|
220668
|
+
import { existsSync as existsSync41, mkdirSync as mkdirSync25, mkdtempSync as mkdtempSync5, readdirSync as readdirSync17, readFileSync as readFileSync33, rmSync as rmSync12, statSync as statSync15, unlinkSync as unlinkSync2, writeFileSync as writeFileSync27 } from "node:fs";
|
|
220264
220669
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
220265
|
-
import { basename as basename22, join as join45, relative as relative7, resolve as
|
|
220670
|
+
import { basename as basename22, join as join45, relative as relative7, resolve as resolve23 } from "node:path";
|
|
220266
220671
|
init_cli();
|
|
220267
220672
|
init_shared();
|
|
220268
220673
|
|
|
@@ -221059,7 +221464,7 @@ var range2 = (name, value, fallback, min, max) => {
|
|
|
221059
221464
|
var guessName = (root) => {
|
|
221060
221465
|
const dirs = [join45(root, "src"), root];
|
|
221061
221466
|
for (const dir of dirs) {
|
|
221062
|
-
if (!
|
|
221467
|
+
if (!existsSync41(dir))
|
|
221063
221468
|
continue;
|
|
221064
221469
|
let entries;
|
|
221065
221470
|
try {
|
|
@@ -221071,7 +221476,7 @@ var guessName = (root) => {
|
|
|
221071
221476
|
if (!f.endsWith(".scene.ts"))
|
|
221072
221477
|
continue;
|
|
221073
221478
|
try {
|
|
221074
|
-
if (/\bnavmesh\s*:/.test(
|
|
221479
|
+
if (/\bnavmesh\s*:/.test(readFileSync33(join45(dir, f), "utf8")))
|
|
221075
221480
|
return f.slice(0, -".scene.ts".length);
|
|
221076
221481
|
} catch {}
|
|
221077
221482
|
}
|
|
@@ -221137,9 +221542,9 @@ var bake2 = defineCommand({
|
|
|
221137
221542
|
examples: ["lecodes navmesh bake", "lecodes navmesh bake --name yard --cell 0.2 --obj --logs"],
|
|
221138
221543
|
run: async ({ flags }) => {
|
|
221139
221544
|
const { root } = localProject();
|
|
221140
|
-
const outDir =
|
|
221545
|
+
const outDir = resolve23(root, flags.out);
|
|
221141
221546
|
const entryFile = flags.entry;
|
|
221142
|
-
if (!
|
|
221547
|
+
if (!existsSync41(resolve23(root, entryFile))) {
|
|
221143
221548
|
throw new CliError(`No ${entryFile} in ${root} — pass --entry <file> (the file that builds the level: a scene file with env.navmesh, or code calling NavMesh.load).`);
|
|
221144
221549
|
}
|
|
221145
221550
|
const name = flags.name ?? guessName(root) ?? "scene";
|
|
@@ -221150,7 +221555,7 @@ var bake2 = defineCommand({
|
|
|
221150
221555
|
const logs = flags.logs;
|
|
221151
221556
|
mkdirSync25(outDir, { recursive: true });
|
|
221152
221557
|
const navmeshPath = join45(outDir, `${name}.navmesh`);
|
|
221153
|
-
if (!
|
|
221558
|
+
if (!existsSync41(navmeshPath))
|
|
221154
221559
|
writeFileSync27(navmeshPath, placeholderLnav());
|
|
221155
221560
|
const importPath = "./" + entryFile.replace(/\\/g, "/").replace(/\.tsx?$/, "");
|
|
221156
221561
|
const entryPath = "/.navmesh-entry.ts";
|
|
@@ -221219,9 +221624,9 @@ import ${JSON.stringify(importPath)}
|
|
|
221219
221624
|
if (!config)
|
|
221220
221625
|
throw new CliError("The host reported a dump but no `[navmesh] config` line — SDK/host mismatch?");
|
|
221221
221626
|
const geoPath = join45(outDir, `${name}.navgeo`);
|
|
221222
|
-
if (!
|
|
221627
|
+
if (!existsSync41(geoPath))
|
|
221223
221628
|
throw new CliError(`The host reported a dump but ${geoPath} is missing.`);
|
|
221224
|
-
const geo = readNgeo(new Uint8Array(
|
|
221629
|
+
const geo = readNgeo(new Uint8Array(readFileSync33(geoPath)));
|
|
221225
221630
|
const params = {
|
|
221226
221631
|
agentRadius: range2("radius", flags.radius, config.agent.radius, 0.05, 10),
|
|
221227
221632
|
agentHeight: range2("height", flags.height, config.agent.height, 0.1, 20),
|
|
@@ -221295,25 +221700,44 @@ init_cli();
|
|
|
221295
221700
|
|
|
221296
221701
|
// src/plugin/generate.ts
|
|
221297
221702
|
init_cli();
|
|
221298
|
-
import { existsSync as
|
|
221703
|
+
import { existsSync as existsSync42, mkdirSync as mkdirSync26, readFileSync as readFileSync34, readdirSync as readdirSync18, statSync as statSync16, writeFileSync as writeFileSync28 } from "node:fs";
|
|
221299
221704
|
import { createRequire as createRequire7 } from "node:module";
|
|
221300
|
-
import { basename as basename23, join as join46, relative as relative8, resolve as
|
|
221705
|
+
import { basename as basename23, join as join46, relative as relative8, resolve as resolve24, sep as sep3 } from "node:path";
|
|
221301
221706
|
|
|
221302
221707
|
// src/plugin/permissions.ts
|
|
221303
221708
|
var PERMISSION_KINDS = ["camera", "microphone", "location", "speech"];
|
|
221304
221709
|
var isPermissionKind = (s) => PERMISSION_KINDS.includes(s);
|
|
221305
221710
|
var PERMISSION_NEEDS = {
|
|
221306
|
-
camera: { infoPlist: ["NSCameraUsageDescription"], android: [["android.permission.CAMERA"]] },
|
|
221307
|
-
microphone: {
|
|
221711
|
+
camera: { infoPlist: ["NSCameraUsageDescription"], android: [["android.permission.CAMERA"]], androidFeatures: ["android.hardware.camera"] },
|
|
221712
|
+
microphone: {
|
|
221713
|
+
infoPlist: ["NSMicrophoneUsageDescription"],
|
|
221714
|
+
android: [["android.permission.RECORD_AUDIO"]],
|
|
221715
|
+
androidFeatures: ["android.hardware.microphone"]
|
|
221716
|
+
},
|
|
221308
221717
|
location: {
|
|
221309
221718
|
infoPlist: ["NSLocationWhenInUseUsageDescription"],
|
|
221310
|
-
android: [["android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION"]]
|
|
221719
|
+
android: [["android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION"]],
|
|
221720
|
+
androidFeatures: ["android.hardware.location"]
|
|
221311
221721
|
},
|
|
221312
|
-
speech: {
|
|
221722
|
+
speech: {
|
|
221723
|
+
infoPlist: ["NSSpeechRecognitionUsageDescription"],
|
|
221724
|
+
android: [["android.permission.RECORD_AUDIO"]],
|
|
221725
|
+
androidFeatures: ["android.hardware.microphone"]
|
|
221726
|
+
}
|
|
221313
221727
|
};
|
|
221314
221728
|
var usesPermissions = (xml) => [...xml.matchAll(/<uses-permission(?:-sdk-23)?\b[^>]*\bandroid:name\s*=\s*"([^"]+)"/g)].map((m) => m[1]);
|
|
221315
|
-
var
|
|
221316
|
-
const out =
|
|
221729
|
+
var usesFeatures = (xml) => {
|
|
221730
|
+
const out = [];
|
|
221731
|
+
for (const m of xml.replace(/<!--[\s\S]*?-->/g, "").matchAll(/<uses-feature\b[^>]*>/g)) {
|
|
221732
|
+
const attr = (re) => re.exec(m[0])?.[2];
|
|
221733
|
+
const name = attr(/\bandroid:name\s*=\s*(["'])(.*?)\1/);
|
|
221734
|
+
if (name)
|
|
221735
|
+
out.push({ name, required: attr(/\bandroid:required\s*=\s*(["'])(.*?)\1/)?.trim() !== "false" });
|
|
221736
|
+
}
|
|
221737
|
+
return out;
|
|
221738
|
+
};
|
|
221739
|
+
var missingDeclarations = (kinds, infoPlist, androidNames, androidFeatures) => {
|
|
221740
|
+
const out = { infoPlist: [], android: [], androidFeatures: [] };
|
|
221317
221741
|
for (const kind of kinds) {
|
|
221318
221742
|
const needs = PERMISSION_NEEDS[kind];
|
|
221319
221743
|
if (infoPlist)
|
|
@@ -221326,8 +221750,14 @@ var missingDeclarations = (kinds, infoPlist, androidNames) => {
|
|
|
221326
221750
|
if (!group.some((name) => androidNames.includes(name)))
|
|
221327
221751
|
out.android.push(`${group.join(" or ")} (${kind})`);
|
|
221328
221752
|
}
|
|
221753
|
+
if (androidFeatures)
|
|
221754
|
+
for (const feature of needs.androidFeatures) {
|
|
221755
|
+
const declared = androidFeatures.filter((f) => f.name === feature);
|
|
221756
|
+
if (declared.length === 0 || declared.some((f) => f.required))
|
|
221757
|
+
out.androidFeatures.push(`${feature} (${kind})`);
|
|
221758
|
+
}
|
|
221329
221759
|
}
|
|
221330
|
-
return { infoPlist: [...new Set(out.infoPlist)], android: [...new Set(out.android)] };
|
|
221760
|
+
return { infoPlist: [...new Set(out.infoPlist)], android: [...new Set(out.android)], androidFeatures: [...new Set(out.androidFeatures)] };
|
|
221331
221761
|
};
|
|
221332
221762
|
|
|
221333
221763
|
// src/plugin/contract.ts
|
|
@@ -224135,13 +224565,13 @@ const wire = {
|
|
|
224135
224565
|
var shown = (from, to) => relative8(from, to).split(sep3).join("/");
|
|
224136
224566
|
var CONTRACT_FILE = "contract.d.ts";
|
|
224137
224567
|
var findPlugins = (path) => {
|
|
224138
|
-
const root =
|
|
224139
|
-
if (
|
|
224568
|
+
const root = resolve24(path);
|
|
224569
|
+
if (existsSync42(join46(root, CONTRACT_FILE)))
|
|
224140
224570
|
return [{ id: basename23(root), dir: root }];
|
|
224141
224571
|
const holder = join46(root, "plugins");
|
|
224142
|
-
if (!
|
|
224572
|
+
if (!existsSync42(holder))
|
|
224143
224573
|
return [];
|
|
224144
|
-
return readdirSync18(holder).sort().map((id) => ({ id, dir: join46(holder, id) })).filter((p) => statSync16(p.dir).isDirectory() &&
|
|
224574
|
+
return readdirSync18(holder).sort().map((id) => ({ id, dir: join46(holder, id) })).filter((p) => statSync16(p.dir).isDirectory() && existsSync42(join46(p.dir, CONTRACT_FILE)));
|
|
224145
224575
|
};
|
|
224146
224576
|
var loadTypescript = (dir) => {
|
|
224147
224577
|
for (const from of [join46(dir, "package.json"), import.meta.url]) {
|
|
@@ -224156,25 +224586,25 @@ var loadTypescript = (dir) => {
|
|
|
224156
224586
|
};
|
|
224157
224587
|
var generatePlugin = (ts, plugin) => {
|
|
224158
224588
|
const file = join46(plugin.dir, CONTRACT_FILE);
|
|
224159
|
-
const contract = readContract(ts, file,
|
|
224589
|
+
const contract = readContract(ts, file, readFileSync34(file, "utf8"));
|
|
224160
224590
|
const manifest = readManifestIfAny(plugin.dir, plugin.id);
|
|
224161
224591
|
checkPermissions(plugin, contract, manifest);
|
|
224162
|
-
const handWritten =
|
|
224592
|
+
const handWritten = existsSync42(join46(plugin.dir, "sdk", `${plugin.id}.ts`));
|
|
224163
224593
|
const single = contract.channels.length === 1;
|
|
224164
224594
|
const files = contract.channels.map((channel) => ({
|
|
224165
224595
|
file: join46(plugin.dir, "sdk", single ? `${plugin.id}.gen.ts` : `${plugin.id}.${channel.iface}.gen.ts`),
|
|
224166
224596
|
text: emitSdk(contract, channel, { id: plugin.id, handWritten })
|
|
224167
224597
|
}));
|
|
224168
|
-
if (
|
|
224598
|
+
if (existsSync42(join46(plugin.dir, "ios"))) {
|
|
224169
224599
|
const name = single ? contract.channels[0].iface : contract.channels[0].iface + "Plugin";
|
|
224170
224600
|
files.push({ file: join46(plugin.dir, "ios", `${name}.gen.swift`), text: emitSwift(lower3(contract), { id: plugin.id }) });
|
|
224171
224601
|
}
|
|
224172
|
-
if (
|
|
224602
|
+
if (existsSync42(join46(plugin.dir, "android"))) {
|
|
224173
224603
|
const name = single ? contract.channels[0].iface : contract.channels[0].iface + "Plugin";
|
|
224174
224604
|
const pkg = kotlinPackage(join46(plugin.dir, "android"));
|
|
224175
224605
|
files.push({ file: join46(pkg.dir, `${name}.gen.kt`), text: emitKotlin(lower3(contract), { id: plugin.id, package: pkg.name }) });
|
|
224176
224606
|
}
|
|
224177
|
-
if (
|
|
224607
|
+
if (existsSync42(join46(plugin.dir, "web"))) {
|
|
224178
224608
|
files.push({ file: join46(plugin.dir, "web", `${plugin.id}.gen.ts`), text: emitWeb(lower3(contract), { id: plugin.id }) });
|
|
224179
224609
|
const foreign = hostImports(join46(plugin.dir, "web"));
|
|
224180
224610
|
if (foreign.length)
|
|
@@ -224196,13 +224626,15 @@ var checkPermissions = (plugin, contract, manifest) => {
|
|
|
224196
224626
|
if (kinds.length === 0)
|
|
224197
224627
|
return;
|
|
224198
224628
|
const androidManifest = join46(plugin.dir, "android", "src", "main", "AndroidManifest.xml");
|
|
224199
|
-
const
|
|
224200
|
-
const missing = missingDeclarations(kinds, manifest.ios ? manifest.ios.infoPlist ?? {} : null,
|
|
224629
|
+
const androidXml = manifest.android ? existsSync42(androidManifest) ? readFileSync34(androidManifest, "utf8") : "" : null;
|
|
224630
|
+
const missing = missingDeclarations(kinds, manifest.ios ? manifest.ios.infoPlist ?? {} : null, androidXml === null ? null : usesPermissions(androidXml), androidXml === null ? null : usesFeatures(androidXml));
|
|
224201
224631
|
const problems = [];
|
|
224202
224632
|
if (missing.infoPlist.length)
|
|
224203
224633
|
problems.push(`ios.infoPlist: the contract's @permission needs ${missing.infoPlist.join(", ")} — the usage string the dialog shows`);
|
|
224204
224634
|
if (missing.android.length)
|
|
224205
224635
|
problems.push(`android/src/main/AndroidManifest.xml: the contract's @permission needs <uses-permission android:name="…"/> for ${missing.android.join(", ")}`);
|
|
224636
|
+
if (missing.androidFeatures.length)
|
|
224637
|
+
problems.push(`android/src/main/AndroidManifest.xml: the contract's @permission implies <uses-feature android:name="…" android:required="false"/> for ${missing.androidFeatures.join(", ")} — without it Play requires the hardware`);
|
|
224206
224638
|
if (problems.length)
|
|
224207
224639
|
throw new ManifestError(join46(plugin.dir, MANIFEST_FILE), problems);
|
|
224208
224640
|
};
|
|
@@ -224218,7 +224650,7 @@ var hostImports = (web) => {
|
|
|
224218
224650
|
}
|
|
224219
224651
|
if (!/\.(ts|tsx|js|mjs)$/.test(entry))
|
|
224220
224652
|
continue;
|
|
224221
|
-
|
|
224653
|
+
readFileSync34(path, "utf8").split(`
|
|
224222
224654
|
`).forEach((line, i) => {
|
|
224223
224655
|
const m = /(?:from\s+|import\s*\(\s*|import\s+)["'](lecodes-[^"']*)["']/.exec(line);
|
|
224224
224656
|
if (m && !/^\s*\/\//.test(line))
|
|
@@ -224232,7 +224664,7 @@ var hostImports = (web) => {
|
|
|
224232
224664
|
var kotlinPackage = (android) => {
|
|
224233
224665
|
const root = join46(android, "src", "main", "java");
|
|
224234
224666
|
let dir = root;
|
|
224235
|
-
while (
|
|
224667
|
+
while (existsSync42(dir)) {
|
|
224236
224668
|
const entries = readdirSync18(dir).filter((e) => !e.startsWith("."));
|
|
224237
224669
|
const dirs = entries.filter((e) => statSync16(join46(dir, e)).isDirectory());
|
|
224238
224670
|
if (dirs.length !== 1 || entries.length !== 1)
|
|
@@ -224246,10 +224678,10 @@ var kotlinPackage = (android) => {
|
|
|
224246
224678
|
};
|
|
224247
224679
|
var repoSdkPath = (root) => {
|
|
224248
224680
|
const file = join46(root, "package.json");
|
|
224249
|
-
if (!
|
|
224681
|
+
if (!existsSync42(file))
|
|
224250
224682
|
return DEFAULT_SDK_PATH;
|
|
224251
224683
|
try {
|
|
224252
|
-
const pkg = JSON.parse(
|
|
224684
|
+
const pkg = JSON.parse(readFileSync34(file, "utf8"));
|
|
224253
224685
|
const path = pkg.lecodes?.iosSdkPath;
|
|
224254
224686
|
if (path !== undefined && typeof path !== "string")
|
|
224255
224687
|
throw new PackageError(`package.json lecodes.iosSdkPath: a path, as a string`);
|
|
@@ -224263,13 +224695,13 @@ var repoSdkPath = (root) => {
|
|
|
224263
224695
|
var generate = (path, opts = {}) => {
|
|
224264
224696
|
const plugins = findPlugins(path);
|
|
224265
224697
|
if (plugins.length === 0)
|
|
224266
|
-
throw new CliError(`No plugin under ${
|
|
224698
|
+
throw new CliError(`No plugin under ${resolve24(path)}: a plugin is a directory with a ${CONTRACT_FILE} (one, or plugins/<id>/ of a repo).`);
|
|
224267
224699
|
const ts = loadTypescript(plugins[0].dir);
|
|
224268
|
-
const root =
|
|
224700
|
+
const root = resolve24(path);
|
|
224269
224701
|
const report = { plugins: [], repo: { written: [], stale: [], fresh: [] }, failures: [] };
|
|
224270
224702
|
const place = (f, entry) => {
|
|
224271
224703
|
const name = shown(root, f.file) || basename23(f.file);
|
|
224272
|
-
const current =
|
|
224704
|
+
const current = existsSync42(f.file) ? readFileSync34(f.file, "utf8") : null;
|
|
224273
224705
|
if (current === f.text) {
|
|
224274
224706
|
entry.fresh.push(name);
|
|
224275
224707
|
return;
|
|
@@ -224306,7 +224738,7 @@ ${e.problems.map((p) => ` ${p}`).join(`
|
|
|
224306
224738
|
throw e;
|
|
224307
224739
|
}
|
|
224308
224740
|
}
|
|
224309
|
-
const isRepo = !
|
|
224741
|
+
const isRepo = !existsSync42(join46(root, CONTRACT_FILE));
|
|
224310
224742
|
if (isRepo && report.failures.length === 0 && manifests.some((m) => m.manifest.ios)) {
|
|
224311
224743
|
try {
|
|
224312
224744
|
place({ file: join46(root, PACKAGE_FILE), text: emitPackageSwift(manifests, repoSdkPath(root)) }, report.repo);
|
|
@@ -224582,12 +225014,37 @@ var pn_default = defineCommand({
|
|
|
224582
225014
|
commands: [send, devices, keys]
|
|
224583
225015
|
});
|
|
224584
225016
|
|
|
225017
|
+
// src/commands/publish.ts
|
|
225018
|
+
init_cli();
|
|
225019
|
+
init_api();
|
|
225020
|
+
init_localFiles();
|
|
225021
|
+
init_shared();
|
|
225022
|
+
var publish_default = defineCommand({
|
|
225023
|
+
name: "publish",
|
|
225024
|
+
summary: "Release the project: push local changes, build, make it the live version",
|
|
225025
|
+
description: 'The editor\'s Publish button. Local changes are pushed first as a checkpoint ("Release", or -m). The platform builds the project, deploys its server to production and makes the build the current release at https://<handle>.le.codes — what the viewers and the store run. A project that builds to what is live already makes no release: "Nothing changed — <version> is current" (--force releases it all the same). A schema change that deletes production data is shown and asked about; without a terminal it needs --allow-destructive. `lecodes releases` lists the releases, `lecodes releases use <version>` rolls back.',
|
|
225026
|
+
flags: {
|
|
225027
|
+
message: str('checkpoint message when there are local changes (default "Release")', { alias: "m", value: "<text>" }),
|
|
225028
|
+
force: bool("rebuild even when nothing changed — a new SDK on the build service"),
|
|
225029
|
+
"allow-destructive": bool("apply a schema change that deletes production data without asking")
|
|
225030
|
+
},
|
|
225031
|
+
examples: ["lecodes publish", 'lecodes publish -m "Checkout v2"', "lecodes publish --force", "lecodes publish --allow-destructive"],
|
|
225032
|
+
run: async ({ flags }) => {
|
|
225033
|
+
const { root, manifest, apiUrl, token } = linkedProject();
|
|
225034
|
+
if (hasChanges(diffLocal(manifest, scanProject(root, manifest))))
|
|
225035
|
+
await runPush({ message: flags.message || "Release" });
|
|
225036
|
+
const release = await withSchemaConfirm((allowDestructive) => publishProject(apiUrl, token, manifest.uuid, { allowDestructive, force: flags.force }), { allowDestructive: flags["allow-destructive"], retry: flags.force ? "lecodes publish --force" : "lecodes publish" });
|
|
225037
|
+
if (release)
|
|
225038
|
+
reportRelease(release, qrUrl(apiUrl, manifest.uuid));
|
|
225039
|
+
}
|
|
225040
|
+
});
|
|
225041
|
+
|
|
224585
225042
|
// src/commands/pull.ts
|
|
224586
225043
|
init_cli();
|
|
224587
225044
|
init_api();
|
|
224588
225045
|
init_localFiles();
|
|
224589
225046
|
init_manifest();
|
|
224590
|
-
import { existsSync as
|
|
225047
|
+
import { existsSync as existsSync43, mkdirSync as mkdirSync27, rmSync as rmSync13, writeFileSync as writeFileSync29 } from "node:fs";
|
|
224591
225048
|
import { dirname as dirname21, join as join47 } from "node:path";
|
|
224592
225049
|
init_paths();
|
|
224593
225050
|
init_types4();
|
|
@@ -224618,7 +225075,7 @@ var pull_default = defineCommand({
|
|
|
224618
225075
|
if (remotePaths.has(path))
|
|
224619
225076
|
continue;
|
|
224620
225077
|
const abs = join47(root, ...path.replace(/^\//, "").split("/"));
|
|
224621
|
-
if (
|
|
225078
|
+
if (existsSync43(abs))
|
|
224622
225079
|
rmSync13(abs);
|
|
224623
225080
|
}
|
|
224624
225081
|
const files = await materializeAssets(root, project, apiUrl, token, progress);
|
|
@@ -224679,8 +225136,8 @@ var pullPaths = async (root, manifest, project, paths, apiUrl, token) => {
|
|
|
224679
225136
|
};
|
|
224680
225137
|
|
|
224681
225138
|
// src/commands/render.ts
|
|
224682
|
-
import { mkdirSync as mkdirSync29, readFileSync as
|
|
224683
|
-
import { dirname as dirname22, resolve as
|
|
225139
|
+
import { mkdirSync as mkdirSync29, readFileSync as readFileSync36, writeFileSync as writeFileSync31 } from "node:fs";
|
|
225140
|
+
import { dirname as dirname22, resolve as resolve28 } from "node:path";
|
|
224684
225141
|
|
|
224685
225142
|
// src/compile/headlessBundle.ts
|
|
224686
225143
|
init_api();
|
|
@@ -224767,22 +225224,22 @@ var compileHeadlessBundle = async (opts = {}) => {
|
|
|
224767
225224
|
init_designMeta();
|
|
224768
225225
|
init_cli();
|
|
224769
225226
|
init_paths();
|
|
224770
|
-
import { existsSync as
|
|
224771
|
-
import { isAbsolute, join as join48, relative as relative9, resolve as
|
|
225227
|
+
import { existsSync as existsSync44, readFileSync as readFileSync35, statSync as statSync17 } from "node:fs";
|
|
225228
|
+
import { isAbsolute, join as join48, relative as relative9, resolve as resolve25 } from "node:path";
|
|
224772
225229
|
var SCREEN_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs"];
|
|
224773
225230
|
var SCREEN_ENTRY_PATH = "/.screen-entry.ts";
|
|
224774
225231
|
var DEFAULT_DESIGN_DIR = "design";
|
|
224775
225232
|
var hasSeparator = (file) => file.includes("/") || file.includes("\\");
|
|
224776
225233
|
var findScreenFile = (root, file, dirFlag) => {
|
|
224777
|
-
const candidates = [
|
|
225234
|
+
const candidates = [resolve25(process.cwd(), file)];
|
|
224778
225235
|
if (!hasSeparator(file)) {
|
|
224779
225236
|
candidates.push(join48(root, (dirFlag ?? DEFAULT_DESIGN_DIR).replace(/[/\\]+$/, ""), "screens", file));
|
|
224780
225237
|
}
|
|
224781
225238
|
for (const base of candidates) {
|
|
224782
|
-
if (
|
|
225239
|
+
if (existsSync44(base) && statSync17(base).isFile())
|
|
224783
225240
|
return base;
|
|
224784
225241
|
for (const ext of SCREEN_EXTS)
|
|
224785
|
-
if (
|
|
225242
|
+
if (existsSync44(base + ext))
|
|
224786
225243
|
return base + ext;
|
|
224787
225244
|
}
|
|
224788
225245
|
throw new CliError(`No screen file at "${file}".` + (hasSeparator(file) ? "" : ` Pass a path, or a screen id that exists in ${dirFlag ?? DEFAULT_DESIGN_DIR}/screens/.`));
|
|
@@ -224792,7 +225249,7 @@ var designDirOf = (root, key) => {
|
|
|
224792
225249
|
if (!m)
|
|
224793
225250
|
return null;
|
|
224794
225251
|
const dir = join48(root, m[1]);
|
|
224795
|
-
return
|
|
225252
|
+
return existsSync44(join48(dir, "meta.json")) ? { dir, id: m[2] } : null;
|
|
224796
225253
|
};
|
|
224797
225254
|
var resolveScreenTarget = (root, file, opts = {}) => {
|
|
224798
225255
|
const abs = findScreenFile(root, file, opts.dir);
|
|
@@ -224801,7 +225258,7 @@ var resolveScreenTarget = (root, file, opts = {}) => {
|
|
|
224801
225258
|
throw new CliError(`"${file}" is outside the project (${root}) — a screen renders as part of its project, so its ` + `imports and asset() files resolve from the same tree.`);
|
|
224802
225259
|
}
|
|
224803
225260
|
const key = toKey(rel);
|
|
224804
|
-
if (!/^\s*export\s+default\b/m.test(
|
|
225261
|
+
if (!/^\s*export\s+default\b/m.test(readFileSync35(abs, "utf8"))) {
|
|
224805
225262
|
throw new CliError(`${key.slice(1)} has no default export. A screen file ends with ` + `\`export default UIScreen([...])\` — or \`export default (state) => UIScreen([...])\` for screen states.`);
|
|
224806
225263
|
}
|
|
224807
225264
|
const invoke = opts.state !== undefined ? `screen(${JSON.stringify(opts.state)})` : "screen()";
|
|
@@ -224837,9 +225294,9 @@ init_manifest();
|
|
|
224837
225294
|
|
|
224838
225295
|
// src/hosts/flowPick.ts
|
|
224839
225296
|
init_cli();
|
|
224840
|
-
import { existsSync as
|
|
225297
|
+
import { existsSync as existsSync45, mkdirSync as mkdirSync28, mkdtempSync as mkdtempSync6, rmSync as rmSync14, writeFileSync as writeFileSync30 } from "node:fs";
|
|
224841
225298
|
import { tmpdir as tmpdir6 } from "node:os";
|
|
224842
|
-
import { join as join49, resolve as
|
|
225299
|
+
import { join as join49, resolve as resolve26 } from "node:path";
|
|
224843
225300
|
import { deflateSync as deflateSync2 } from "node:zlib";
|
|
224844
225301
|
var MAX_SIDE = 4096;
|
|
224845
225302
|
var crcTable = (() => {
|
|
@@ -224908,8 +225365,8 @@ var write = (name, bytes) => {
|
|
|
224908
225365
|
};
|
|
224909
225366
|
var fileOf = (at, value, baseDir) => {
|
|
224910
225367
|
if (typeof value === "string") {
|
|
224911
|
-
const path =
|
|
224912
|
-
if (!
|
|
225368
|
+
const path = resolve26(baseDir, value);
|
|
225369
|
+
if (!existsSync45(path))
|
|
224913
225370
|
throw new CliError(`${at}: "pick": no file "${value}" (looked for ${path}).`);
|
|
224914
225371
|
return path;
|
|
224915
225372
|
}
|
|
@@ -224949,7 +225406,7 @@ var resolvePicks = (steps, baseDir) => {
|
|
|
224949
225406
|
|
|
224950
225407
|
// src/hosts/desktopScript.ts
|
|
224951
225408
|
init_cli();
|
|
224952
|
-
import { resolve as
|
|
225409
|
+
import { resolve as resolve27 } from "node:path";
|
|
224953
225410
|
|
|
224954
225411
|
// ../../runtime/headless/src/headless/paintDump.ts
|
|
224955
225412
|
var FLAG_INTERACTIVE = 1;
|
|
@@ -225132,7 +225589,7 @@ var FRAME_MS = 16;
|
|
|
225132
225589
|
var SETTLE_FRAMES = 640;
|
|
225133
225590
|
var ACTIONS = ["tap", "type", "fill", "scroll", "wait", "frames", "drag", "hold", "hover", "key", "keyDown", "keyUp", "look", "mouseTo", "wheel", "stick", "gamepad", "camera", "view"];
|
|
225134
225591
|
var SEMANTIC_EXPECT = ["text", "notText", "node", "notNode", "screen", "value"];
|
|
225135
|
-
var UNSUPPORTED_EXPECT = ["audio"];
|
|
225592
|
+
var UNSUPPORTED_EXPECT = ["audio", "toast"];
|
|
225136
225593
|
var VIEW_NAMES = ["iso", "top", "front", "side", "back"];
|
|
225137
225594
|
var isVec3 = (v) => Array.isArray(v) && v.length === 3 && v.every((n) => typeof n === "number");
|
|
225138
225595
|
var pt = (p) => typeof p === "string" ? p : `(${p.x},${p.y})`;
|
|
@@ -225394,7 +225851,7 @@ var translateScenario = (rawSteps, opts) => {
|
|
|
225394
225851
|
}
|
|
225395
225852
|
}
|
|
225396
225853
|
if (needDump) {
|
|
225397
|
-
plan.paintDump =
|
|
225854
|
+
plan.paintDump = resolve27(opts.outDir, `.dump-${i}.json`);
|
|
225398
225855
|
plan.lines.push(emit(`frames 1`));
|
|
225399
225856
|
plan.lines.push(emit(`dump ${plan.paintDump}`));
|
|
225400
225857
|
budgetMs += 2 * FRAME_MS;
|
|
@@ -225409,7 +225866,7 @@ var translateScenario = (rawSteps, opts) => {
|
|
|
225409
225866
|
throw new CliError(`${at}: "screenshot" must be a path.`);
|
|
225410
225867
|
if (opts.sim)
|
|
225411
225868
|
throw new CliError(`${at}: "screenshot" isn't available with --sim (the windowless host paints nothing) — judge the run by expect.log, or run it with --desktop for pixels.`);
|
|
225412
|
-
plan.screenshot =
|
|
225869
|
+
plan.screenshot = resolve27(opts.outDir, s.screenshot);
|
|
225413
225870
|
const clip = s.clip;
|
|
225414
225871
|
if (clip !== undefined) {
|
|
225415
225872
|
if (typeof clip === "string") {
|
|
@@ -225435,7 +225892,7 @@ var translateScenario = (rawSteps, opts) => {
|
|
|
225435
225892
|
if (s.dump !== undefined) {
|
|
225436
225893
|
if (typeof s.dump !== "string")
|
|
225437
225894
|
throw new CliError(`${at}: "dump" must be a path.`);
|
|
225438
|
-
plan.dump =
|
|
225895
|
+
plan.dump = resolve27(opts.outDir, s.dump);
|
|
225439
225896
|
plan.lines.push(emit(`frames 1`));
|
|
225440
225897
|
plan.lines.push(emit(`dump ${plan.dump}`));
|
|
225441
225898
|
budgetMs += 2 * FRAME_MS;
|
|
@@ -225480,7 +225937,7 @@ var evaluateDesktopRun = (plan, output, readFile = () => null) => {
|
|
|
225480
225937
|
let ok = true;
|
|
225481
225938
|
for (const st of plan.steps) {
|
|
225482
225939
|
const skipped = [
|
|
225483
|
-
...st.skippedExpect.map((k) => `expect.${k} (the headless
|
|
225940
|
+
...st.skippedExpect.map((k) => `expect.${k} (the headless renderer's record — not on a native host)`),
|
|
225484
225941
|
...st.skippedOutputs.map((k) => `${k} output (the headless renderer's shape — use "dump" on a native host)`)
|
|
225485
225942
|
];
|
|
225486
225943
|
let error;
|
|
@@ -225586,7 +226043,7 @@ var parseNativeViews = (raw) => {
|
|
|
225586
226043
|
var requireRenderer = async () => loadPeer("lecodes-headless", "headless", { for: "lecodes render" });
|
|
225587
226044
|
var fileScenarioIO = (dir) => ({
|
|
225588
226045
|
write: (relPath, data) => {
|
|
225589
|
-
const abs =
|
|
226046
|
+
const abs = resolve28(dir, relPath);
|
|
225590
226047
|
mkdirSync29(dirname22(abs), { recursive: true });
|
|
225591
226048
|
writeFileSync31(abs, data);
|
|
225592
226049
|
}
|
|
@@ -225596,9 +226053,9 @@ var resolveFixtures = (value, baseDir) => {
|
|
|
225596
226053
|
return;
|
|
225597
226054
|
let map = value;
|
|
225598
226055
|
if (typeof value === "string") {
|
|
225599
|
-
const abs =
|
|
226056
|
+
const abs = resolve28(baseDir, value);
|
|
225600
226057
|
try {
|
|
225601
|
-
map = JSON.parse(
|
|
226058
|
+
map = JSON.parse(readFileSync36(abs, "utf8"));
|
|
225602
226059
|
} catch (e) {
|
|
225603
226060
|
throw new CliError(`Can't read fixtures "${value}": ${e instanceof Error ? e.message : String(e)}`);
|
|
225604
226061
|
}
|
|
@@ -225611,18 +226068,18 @@ var resolveFixtures = (value, baseDir) => {
|
|
|
225611
226068
|
var readScenarioFile = (path) => {
|
|
225612
226069
|
let raw;
|
|
225613
226070
|
try {
|
|
225614
|
-
raw = JSON.parse(
|
|
226071
|
+
raw = JSON.parse(readFileSync36(resolve28(path), "utf8"));
|
|
225615
226072
|
} catch (e) {
|
|
225616
226073
|
throw new CliError(`Can't read scenario "${path}": ${e instanceof Error ? e.message : String(e)}`);
|
|
225617
226074
|
}
|
|
225618
226075
|
if (Array.isArray(raw))
|
|
225619
|
-
return { steps: resolvePicks(raw, dirname22(
|
|
226076
|
+
return { steps: resolvePicks(raw, dirname22(resolve28(path))) };
|
|
225620
226077
|
if (typeof raw !== "object" || raw === null)
|
|
225621
226078
|
throw new CliError(`Scenario "${path}" must be a JSON object with "steps" (or a bare step array).`);
|
|
225622
226079
|
const file = raw;
|
|
225623
226080
|
if (file.steps === undefined)
|
|
225624
226081
|
throw new CliError(`Scenario "${path}" has no "steps".`);
|
|
225625
|
-
return { ...file, steps: resolvePicks(file.steps, dirname22(
|
|
226082
|
+
return { ...file, steps: resolvePicks(file.steps, dirname22(resolve28(path))) };
|
|
225626
226083
|
};
|
|
225627
226084
|
var parseClip = (raw) => {
|
|
225628
226085
|
if (raw === undefined)
|
|
@@ -225747,7 +226204,7 @@ var renderDesktop = async (flags, file) => {
|
|
|
225747
226204
|
entryOverride: screen?.entryPath
|
|
225748
226205
|
});
|
|
225749
226206
|
if (!sim)
|
|
225750
|
-
mkdirSync29(dirname22(
|
|
226207
|
+
mkdirSync29(dirname22(resolve28(pngFlag)), { recursive: true });
|
|
225751
226208
|
if (scenario) {
|
|
225752
226209
|
const outDir = flags["out-dir"];
|
|
225753
226210
|
const plan = translateScenario(scenario.steps, { outDir, headroomFrames: frames, sim });
|
|
@@ -225843,7 +226300,7 @@ var renderHeadless = async (flags, file) => {
|
|
|
225843
226300
|
const width = flags.width || scenario?.width || screen?.frame?.[0] || projectViewport?.width || 390;
|
|
225844
226301
|
const height = flags.height || scenario?.height || screen?.frame?.[1] || projectViewport?.height || 844;
|
|
225845
226302
|
const onConsole = flags.logs ? (level, text) => logErr(`${c.dim(`[${level}]`)} ${text}`) : undefined;
|
|
225846
|
-
const envelopeFixtures = resolveFixtures(scenario?.fixtures, flags.script !== undefined ? dirname22(
|
|
226303
|
+
const envelopeFixtures = resolveFixtures(scenario?.fixtures, flags.script !== undefined ? dirname22(resolve28(flags.script)) : ".");
|
|
225847
226304
|
const flagFixtures = resolveFixtures(flags.fixtures, ".");
|
|
225848
226305
|
const fixtures = envelopeFixtures || flagFixtures ? { ...envelopeFixtures, ...flagFixtures } : undefined;
|
|
225849
226306
|
if (fixtures) {
|
|
@@ -225884,7 +226341,7 @@ var renderHeadless = async (flags, file) => {
|
|
|
225884
226341
|
${c.red(st.error ?? "failed")}`);
|
|
225885
226342
|
}
|
|
225886
226343
|
if (!result.ok && result.failureArtifacts.length > 0) {
|
|
225887
|
-
logErr(c.dim(` failing frame: ${result.failureArtifacts.map((p) =>
|
|
226344
|
+
logErr(c.dim(` failing frame: ${result.failureArtifacts.map((p) => resolve28(outDir, p)).join(", ")}`));
|
|
225888
226345
|
}
|
|
225889
226346
|
logErr(result.ok ? c.green(`✓ ${result.steps.length}/${steps.length} steps passed`) : c.red(`✗ failed at step ${result.steps.length} of ${steps.length}`));
|
|
225890
226347
|
return done((cb) => cb(), result.ok ? 0 : 1);
|
|
@@ -225905,10 +226362,10 @@ var renderHeadless = async (flags, file) => {
|
|
|
225905
226362
|
for (const w of [...warnings, ...mapWarnings])
|
|
225906
226363
|
warnErr(w);
|
|
225907
226364
|
const pngPath = mapFlag.endsWith(".png") ? mapFlag : mapFlag + ".png";
|
|
225908
|
-
mkdirSync29(dirname22(
|
|
225909
|
-
writeFileSync31(
|
|
226365
|
+
mkdirSync29(dirname22(resolve28(pngPath)), { recursive: true });
|
|
226366
|
+
writeFileSync31(resolve28(pngPath), png);
|
|
225910
226367
|
const jsonPath = pngPath.slice(0, -4) + ".json";
|
|
225911
|
-
writeFileSync31(
|
|
226368
|
+
writeFileSync31(resolve28(jsonPath), JSON.stringify(data, null, 2));
|
|
225912
226369
|
note(`Wrote ${pngPath} + ${jsonPath}`);
|
|
225913
226370
|
return done((cb) => cb());
|
|
225914
226371
|
}
|
|
@@ -225928,8 +226385,8 @@ var renderHeadless = async (flags, file) => {
|
|
|
225928
226385
|
if (isNullSink(pngFlag)) {
|
|
225929
226386
|
note(`Discarded PNG (${pngFlag})`);
|
|
225930
226387
|
} else {
|
|
225931
|
-
mkdirSync29(dirname22(
|
|
225932
|
-
writeFileSync31(
|
|
226388
|
+
mkdirSync29(dirname22(resolve28(pngFlag)), { recursive: true });
|
|
226389
|
+
writeFileSync31(resolve28(pngFlag), png);
|
|
225933
226390
|
note(`Wrote ${pngFlag} (${png.length} bytes, ${outW}x${outH}${clip !== undefined ? ", clipped" : ""})`);
|
|
225934
226391
|
}
|
|
225935
226392
|
return done((cb) => cb());
|
|
@@ -225944,7 +226401,7 @@ var renderHeadless = async (flags, file) => {
|
|
|
225944
226401
|
return done((cb) => cb());
|
|
225945
226402
|
}
|
|
225946
226403
|
if (out) {
|
|
225947
|
-
writeFileSync31(
|
|
226404
|
+
writeFileSync31(resolve28(out), result.text);
|
|
225948
226405
|
note(`Wrote ${out} (${result.text.length} bytes)`);
|
|
225949
226406
|
return done((cb) => cb());
|
|
225950
226407
|
}
|
|
@@ -225956,8 +226413,8 @@ var renderHeadless = async (flags, file) => {
|
|
|
225956
226413
|
|
|
225957
226414
|
// src/commands/scene.ts
|
|
225958
226415
|
init_cli();
|
|
225959
|
-
import { existsSync as
|
|
225960
|
-
import { basename as basename24, dirname as dirname23, join as join51, relative as relative10, resolve as
|
|
226416
|
+
import { existsSync as existsSync47, readFileSync as readFileSync38, readdirSync as readdirSync19, statSync as statSync18, writeFileSync as writeFileSync33 } from "node:fs";
|
|
226417
|
+
import { basename as basename24, dirname as dirname23, join as join51, relative as relative10, resolve as resolve29 } from "node:path";
|
|
225961
226418
|
|
|
225962
226419
|
// src/compile/sceneCompile.ts
|
|
225963
226420
|
init_localFiles();
|
|
@@ -225965,7 +226422,7 @@ init_ignore();
|
|
|
225965
226422
|
init_cli();
|
|
225966
226423
|
init_projectCompile();
|
|
225967
226424
|
import { createHash as createHash8 } from "node:crypto";
|
|
225968
|
-
import { existsSync as
|
|
226425
|
+
import { existsSync as existsSync46, mkdirSync as mkdirSync30, readFileSync as readFileSync37, writeFileSync as writeFileSync32 } from "node:fs";
|
|
225969
226426
|
import { join as join50 } from "node:path";
|
|
225970
226427
|
var TEXT_EXT = /\.(ts|js|json|svg|txt|md)$/i;
|
|
225971
226428
|
var rel = (key) => key.replace(/^\//, "");
|
|
@@ -225977,9 +226434,9 @@ var compileSceneEditor = async (opts) => {
|
|
|
225977
226434
|
for (const file of scan.files) {
|
|
225978
226435
|
const path = "./" + rel(file.path);
|
|
225979
226436
|
if (TEXT_EXT.test(path)) {
|
|
225980
|
-
entries.push({ path, type: "text", text:
|
|
226437
|
+
entries.push({ path, type: "text", text: readFileSync37(file.absPath, "utf8") });
|
|
225981
226438
|
} else if (/\.mat$/i.test(path)) {
|
|
225982
|
-
entries.push({ path, type: "shader", text:
|
|
226439
|
+
entries.push({ path, type: "shader", text: readFileSync37(file.absPath, "utf8"), absPath: file.absPath });
|
|
225983
226440
|
} else {
|
|
225984
226441
|
entries.push({ path, type: "resource", fileSrc: "/project/" + rel(file.path) });
|
|
225985
226442
|
}
|
|
@@ -225997,7 +226454,7 @@ var compileSceneEditor = async (opts) => {
|
|
|
225997
226454
|
const dir = shadersCacheDir(root);
|
|
225998
226455
|
mkdirSync30(dir, { recursive: true });
|
|
225999
226456
|
const liteName = `${hash}_webgl-lite.filamat`;
|
|
226000
|
-
if (!
|
|
226457
|
+
if (!existsSync46(join50(dir, liteName)))
|
|
226001
226458
|
writeFileSync32(join50(dir, liteName), JSON.stringify(buildLiteMaterialArtifact(e.text ?? "")));
|
|
226002
226459
|
const src = `/project/.lecodes/shaders/${hash}_opengl.filamat`;
|
|
226003
226460
|
e.shaders = ["opengl", "webgl-lite"].map((platform) => ({ platform, src }));
|
|
@@ -226035,7 +226492,7 @@ var DEFAULT_PORT3 = 4488;
|
|
|
226035
226492
|
var findAppRoot = (start) => {
|
|
226036
226493
|
let dir = start;
|
|
226037
226494
|
while (true) {
|
|
226038
|
-
if (
|
|
226495
|
+
if (existsSync47(join51(dir, "app.json")))
|
|
226039
226496
|
return dir;
|
|
226040
226497
|
const parent = dirname23(dir);
|
|
226041
226498
|
if (parent === dir)
|
|
@@ -226060,10 +226517,10 @@ var getContext2 = (pathArg) => {
|
|
|
226060
226517
|
root = process.cwd();
|
|
226061
226518
|
manifest = null;
|
|
226062
226519
|
if (pathArg) {
|
|
226063
|
-
const abs =
|
|
226064
|
-
if (
|
|
226520
|
+
const abs = resolve29(process.cwd(), pathArg);
|
|
226521
|
+
if (existsSync47(abs) && statSync18(abs).isDirectory())
|
|
226065
226522
|
root = abs;
|
|
226066
|
-
else if (abs.endsWith(".scene.ts") &&
|
|
226523
|
+
else if (abs.endsWith(".scene.ts") && existsSync47(abs))
|
|
226067
226524
|
root = findAppRoot(dirname23(abs)) ?? dirname23(abs);
|
|
226068
226525
|
else
|
|
226069
226526
|
throw new CliError(`No such folder or .scene.ts: ${pathArg}`);
|
|
@@ -226073,9 +226530,9 @@ var getContext2 = (pathArg) => {
|
|
|
226073
226530
|
}
|
|
226074
226531
|
let scene = null;
|
|
226075
226532
|
if (pathArg?.endsWith(".scene.ts")) {
|
|
226076
|
-
const abs =
|
|
226533
|
+
const abs = resolve29(process.cwd(), pathArg);
|
|
226077
226534
|
const rel = relative10(root, abs).replace(/\\/g, "/");
|
|
226078
|
-
if (
|
|
226535
|
+
if (existsSync47(abs) && !rel.startsWith(".."))
|
|
226079
226536
|
scene = "./" + rel;
|
|
226080
226537
|
}
|
|
226081
226538
|
return { root, manifest, name: manifest?.name ?? basename24(root), scene };
|
|
@@ -226084,9 +226541,9 @@ var writeMcpConfig2 = (root, port) => {
|
|
|
226084
226541
|
const path = join51(root, ".mcp.json");
|
|
226085
226542
|
const entry = { type: "http", url: `http://localhost:${port}/mcp` };
|
|
226086
226543
|
let config = {};
|
|
226087
|
-
if (
|
|
226544
|
+
if (existsSync47(path)) {
|
|
226088
226545
|
try {
|
|
226089
|
-
config = JSON.parse(
|
|
226546
|
+
config = JSON.parse(readFileSync38(path, "utf8"));
|
|
226090
226547
|
} catch {
|
|
226091
226548
|
warnErr(".mcp.json exists but isn't valid JSON — left it untouched. Add the lecodes-scene server by hand.");
|
|
226092
226549
|
return;
|
|
@@ -226201,7 +226658,7 @@ var serve_default = defineCommand({
|
|
|
226201
226658
|
});
|
|
226202
226659
|
|
|
226203
226660
|
// src/commands/shaders/index.ts
|
|
226204
|
-
import { readFileSync as
|
|
226661
|
+
import { readFileSync as readFileSync40 } from "node:fs";
|
|
226205
226662
|
import { relative as relative12 } from "node:path";
|
|
226206
226663
|
init_cli();
|
|
226207
226664
|
init_shared();
|
|
@@ -226210,15 +226667,15 @@ init_shared();
|
|
|
226210
226667
|
init_compile();
|
|
226211
226668
|
init_distRoot();
|
|
226212
226669
|
init_cli();
|
|
226213
|
-
import { existsSync as
|
|
226214
|
-
import { basename as basename26, join as join52, relative as relative11, resolve as
|
|
226670
|
+
import { existsSync as existsSync48, mkdirSync as mkdirSync31, readdirSync as readdirSync20, readFileSync as readFileSync39, writeFileSync as writeFileSync34 } from "node:fs";
|
|
226671
|
+
import { basename as basename26, join as join52, relative as relative11, resolve as resolve30 } from "node:path";
|
|
226215
226672
|
var engineMaterialsDir = () => {
|
|
226216
226673
|
const candidates = [
|
|
226217
226674
|
join52(distRoot, "runtime", "materials"),
|
|
226218
226675
|
join52(distRoot, "..", "..", "engines", "gl", "materials", "src")
|
|
226219
226676
|
];
|
|
226220
226677
|
for (const c of candidates)
|
|
226221
|
-
if (
|
|
226678
|
+
if (existsSync48(join52(c, "unlit.mat")))
|
|
226222
226679
|
return c;
|
|
226223
226680
|
return null;
|
|
226224
226681
|
};
|
|
@@ -226300,11 +226757,11 @@ var writeNewShader = (o) => {
|
|
|
226300
226757
|
const name = o.name.endsWith(".mat") ? o.name.slice(0, -4) : o.name;
|
|
226301
226758
|
if (!NAME_RE.test(name))
|
|
226302
226759
|
throw new CliError(`"${o.name}" is not a shader name — letters, digits, - and _ (starting with a letter), e.g. hologram or rim-light`);
|
|
226303
|
-
const dir =
|
|
226760
|
+
const dir = resolve30(o.cwd ?? process.cwd(), o.dir ?? ".");
|
|
226304
226761
|
const matPath = join52(dir, `${name}.mat`), assetPath = join52(dir, `${name}.material.ts`);
|
|
226305
226762
|
if (!o.force) {
|
|
226306
226763
|
for (const p of [matPath, assetPath])
|
|
226307
|
-
if (
|
|
226764
|
+
if (existsSync48(p))
|
|
226308
226765
|
throw new CliError(`${relative11(o.cwd ?? process.cwd(), p) || basename26(p)} exists — pick another name or pass --force to overwrite`);
|
|
226309
226766
|
}
|
|
226310
226767
|
let mat;
|
|
@@ -226317,7 +226774,7 @@ var writeNewShader = (o) => {
|
|
|
226317
226774
|
const known = engineMaterialNames();
|
|
226318
226775
|
if (!known.includes(ref))
|
|
226319
226776
|
throw new CliError(`no engine material "${ref}". Known: ${known.join(", ")}`);
|
|
226320
|
-
mat = refMat(name, ref,
|
|
226777
|
+
mat = refMat(name, ref, readFileSync39(join52(srcDir, `${ref}.mat`), "utf8"));
|
|
226321
226778
|
} else
|
|
226322
226779
|
mat = starterMat(name);
|
|
226323
226780
|
mkdirSync31(dir, { recursive: true });
|
|
@@ -226349,7 +226806,7 @@ var status2 = async () => {
|
|
|
226349
226806
|
if (matcId === null)
|
|
226350
226807
|
state = c.dim("unknown (matc not downloaded)");
|
|
226351
226808
|
else {
|
|
226352
|
-
const text =
|
|
226809
|
+
const text = readFileSync40(shader.absPath, "utf8");
|
|
226353
226810
|
if (shaderIsCompiled(root, text, matcId))
|
|
226354
226811
|
state = "compiled";
|
|
226355
226812
|
else {
|
|
@@ -226358,7 +226815,7 @@ var status2 = async () => {
|
|
|
226358
226815
|
}
|
|
226359
226816
|
}
|
|
226360
226817
|
log(` ${shader.path.padEnd(width)} ${state}`);
|
|
226361
|
-
const schema = parseShaderSchema(
|
|
226818
|
+
const schema = parseShaderSchema(readFileSync40(shader.absPath, "utf8"));
|
|
226362
226819
|
for (const p of schema.params) {
|
|
226363
226820
|
const range = p.min !== undefined ? ` [${p.min}..${p.max}]` : "";
|
|
226364
226821
|
const def = p.default !== null && p.default !== undefined ? ` = ${JSON.stringify(p.default)}` : "";
|
|
@@ -226399,7 +226856,7 @@ var compileShaders = async (platforms) => {
|
|
|
226399
226856
|
}
|
|
226400
226857
|
const matcExe = await resolveMatcExe();
|
|
226401
226858
|
const matcId = matcIdentity(matcExe);
|
|
226402
|
-
const wasCompiled = new Map(shaders.map((s) => [s.path, shaderIsCompiled(root,
|
|
226859
|
+
const wasCompiled = new Map(shaders.map((s) => [s.path, shaderIsCompiled(root, readFileSync40(s.absPath, "utf8"), matcId)]));
|
|
226403
226860
|
const errors = [];
|
|
226404
226861
|
for (const shader of shaders) {
|
|
226405
226862
|
try {
|
|
@@ -226501,14 +226958,14 @@ var status_default = defineCommand({
|
|
|
226501
226958
|
});
|
|
226502
226959
|
|
|
226503
226960
|
// src/commands/test.ts
|
|
226504
|
-
import { existsSync as
|
|
226505
|
-
import { dirname as dirname25, resolve as
|
|
226961
|
+
import { existsSync as existsSync50, mkdirSync as mkdirSync33, readFileSync as readFileSync41, readdirSync as readdirSync21 } from "node:fs";
|
|
226962
|
+
import { dirname as dirname25, resolve as resolve31 } from "node:path";
|
|
226506
226963
|
init_manifest();
|
|
226507
226964
|
|
|
226508
226965
|
// src/hosts/liteRunner.ts
|
|
226509
226966
|
init_cli();
|
|
226510
226967
|
import { spawn as spawn7 } from "node:child_process";
|
|
226511
|
-
import { existsSync as
|
|
226968
|
+
import { existsSync as existsSync49, mkdirSync as mkdirSync32, rmSync as rmSync15, writeFileSync as writeFileSync35 } from "node:fs";
|
|
226512
226969
|
import { createServer as createServer3 } from "node:net";
|
|
226513
226970
|
import { dirname as dirname24, join as join53 } from "node:path";
|
|
226514
226971
|
import { createInterface as createInterface2 } from "node:readline";
|
|
@@ -226521,7 +226978,7 @@ var NO_NODE = "--lite needs Node on the PATH: the browser is driven from a Node
|
|
|
226521
226978
|
var NO_PLAYWRIGHT = "--lite runs the flows in a browser and needs Playwright: `npm install -D playwright` in the project, then `npx playwright install chromium` once.";
|
|
226522
226979
|
var startDriver = async () => {
|
|
226523
226980
|
const script = join53(distRoot, "src", "hosts", "liteBrowser.mjs");
|
|
226524
|
-
if (!
|
|
226981
|
+
if (!existsSync49(script))
|
|
226525
226982
|
throw new CliError("--lite is not available in this build of the CLI (its browser driver ships with the npm package).");
|
|
226526
226983
|
const child = spawn7("node", [script], { stdio: ["pipe", "pipe", "inherit"] });
|
|
226527
226984
|
const waiting = new Map;
|
|
@@ -227018,7 +227475,7 @@ var slug = (s) => s.replace(/[^a-zA-Z0-9-_]+/g, "-").replace(/^-+|-+$/g, "").sli
|
|
|
227018
227475
|
var readFlowFile = (abs, displayName) => {
|
|
227019
227476
|
let raw;
|
|
227020
227477
|
try {
|
|
227021
|
-
raw = JSON.parse(
|
|
227478
|
+
raw = JSON.parse(readFileSync41(abs, "utf8"));
|
|
227022
227479
|
} catch (e) {
|
|
227023
227480
|
throw new CliError(`Can't read "${displayName}": ${e instanceof Error ? e.message : String(e)}`);
|
|
227024
227481
|
}
|
|
@@ -227112,39 +227569,39 @@ var runTests = async (args, flags) => {
|
|
|
227112
227569
|
if (nativeFixedDt < 0 || nativeFixedDt > 1000)
|
|
227113
227570
|
throw new CliError(`--fixed-dt wants a number of ms (0 = wall clock, max 1000), got "${nativeFixedDt}".`);
|
|
227114
227571
|
const projectViewport = desktopDefaultViewport(root);
|
|
227115
|
-
const testsDir =
|
|
227116
|
-
const designMetaPath =
|
|
227572
|
+
const testsDir = resolve31(root, flags.dir);
|
|
227573
|
+
const designMetaPath = resolve31(root, flags["design-dir"], "meta.json");
|
|
227117
227574
|
const asJson = flags.json;
|
|
227118
227575
|
const out = asJson ? logErr : log;
|
|
227119
227576
|
const cases = [];
|
|
227120
227577
|
if (args.length > 0) {
|
|
227121
227578
|
for (const given of args) {
|
|
227122
|
-
const candidates = [
|
|
227123
|
-
const abs = candidates.find((p) =>
|
|
227579
|
+
const candidates = [resolve31(given), resolve31(testsDir, given), resolve31(testsDir, `${given}.flow.json`)];
|
|
227580
|
+
const abs = candidates.find((p) => existsSync50(p));
|
|
227124
227581
|
if (!abs)
|
|
227125
227582
|
throw new CliError(`No such test: "${given}" (looked in ${testsDir}).`);
|
|
227126
227583
|
cases.push(readFlowFile(abs, given));
|
|
227127
227584
|
}
|
|
227128
|
-
} else if (
|
|
227585
|
+
} else if (existsSync50(testsDir)) {
|
|
227129
227586
|
for (const f of readdirSync21(testsDir).filter((f) => f.endsWith(".flow.json")).sort()) {
|
|
227130
|
-
cases.push(readFlowFile(
|
|
227587
|
+
cases.push(readFlowFile(resolve31(testsDir, f), f));
|
|
227131
227588
|
}
|
|
227132
227589
|
}
|
|
227133
|
-
const defaultPack =
|
|
227134
|
-
const flowFixtures = flags.fixtures !== undefined ? resolveFixtures(flags.fixtures, ".") :
|
|
227590
|
+
const defaultPack = resolve31(testsDir, "fixtures.json");
|
|
227591
|
+
const flowFixtures = flags.fixtures !== undefined ? resolveFixtures(flags.fixtures, ".") : existsSync50(defaultPack) ? resolveFixtures("fixtures.json", testsDir) : undefined;
|
|
227135
227592
|
const flowsOnly = flags.flows;
|
|
227136
227593
|
const wantFlows = !flags["no-flows"] && args.length === 0;
|
|
227137
227594
|
if (flowsOnly)
|
|
227138
227595
|
cases.length = 0;
|
|
227139
227596
|
let skipped = [];
|
|
227140
227597
|
let entryScreen = null;
|
|
227141
|
-
if ((desktop || lite) && wantFlows &&
|
|
227598
|
+
if ((desktop || lite) && wantFlows && existsSync50(designMetaPath)) {
|
|
227142
227599
|
note("Design-derived flows skipped with --desktop / --lite (they read expect.screen and fill inputs; run `lecodes test` headless for them).");
|
|
227143
227600
|
}
|
|
227144
|
-
if (!desktop && !lite && (wantFlows || flowsOnly) &&
|
|
227601
|
+
if (!desktop && !lite && (wantFlows || flowsOnly) && existsSync50(designMetaPath)) {
|
|
227145
227602
|
let meta;
|
|
227146
227603
|
try {
|
|
227147
|
-
meta = JSON.parse(
|
|
227604
|
+
meta = JSON.parse(readFileSync41(designMetaPath, "utf8"));
|
|
227148
227605
|
} catch (e) {
|
|
227149
227606
|
throw new CliError(`Can't read ${designMetaPath}: ${e instanceof Error ? e.message : String(e)}`);
|
|
227150
227607
|
}
|
|
@@ -227177,7 +227634,7 @@ var runTests = async (args, flags) => {
|
|
|
227177
227634
|
}, timeoutMs);
|
|
227178
227635
|
killer.unref?.();
|
|
227179
227636
|
const onConsole = flags.logs ? (level, text) => logErr(`${c.dim(`[${level}]`)} ${text}`) : undefined;
|
|
227180
|
-
const artifactsRoot =
|
|
227637
|
+
const artifactsRoot = resolve31(root, flags.artifacts ?? resolve31(testsDir, ".artifacts"));
|
|
227181
227638
|
const results = [];
|
|
227182
227639
|
const reportCase = (result) => {
|
|
227183
227640
|
const skippedCount = result.steps.reduce((n, s) => n + (s.skipped?.length ?? 0), 0);
|
|
@@ -227197,13 +227654,13 @@ var runTests = async (args, flags) => {
|
|
|
227197
227654
|
}
|
|
227198
227655
|
};
|
|
227199
227656
|
const runNativeCase = async (kase) => {
|
|
227200
|
-
const caseDir =
|
|
227657
|
+
const caseDir = resolve31(artifactsRoot, slug(kase.name));
|
|
227201
227658
|
const plan = translateScenario(kase.steps, { outDir: caseDir, headroomFrames: nativeFrames, sim });
|
|
227202
227659
|
for (const st of plan.steps)
|
|
227203
227660
|
if (st.screenshot)
|
|
227204
227661
|
mkdirSync33(dirname25(st.screenshot), { recursive: true });
|
|
227205
227662
|
mkdirSync33(caseDir, { recursive: true });
|
|
227206
|
-
const finalPng =
|
|
227663
|
+
const finalPng = resolve31(caseDir, "final.png");
|
|
227207
227664
|
const { output } = await runDesktopRender(native.js, native.resources, {
|
|
227208
227665
|
renderer: desktopRenderer,
|
|
227209
227666
|
host: sim ? "server" : "desktop",
|
|
@@ -227217,7 +227674,7 @@ var runTests = async (args, flags) => {
|
|
|
227217
227674
|
script: plan.text,
|
|
227218
227675
|
fixedDtMs: nativeFixedDt
|
|
227219
227676
|
});
|
|
227220
|
-
const r = evaluateDesktopRun(plan, output, (path) =>
|
|
227677
|
+
const r = evaluateDesktopRun(plan, output, (path) => existsSync50(path) ? readFileSync41(path, "utf8") : null);
|
|
227221
227678
|
if (!r.loaded)
|
|
227222
227679
|
throw new CliError("This desktop host doesn't support scripted input (CREATOR_SCRIPT). Update it: lecodes desktop update (or point LECODES_DESKTOP_EXE at a newer build).");
|
|
227223
227680
|
return {
|
|
@@ -227248,7 +227705,7 @@ var runTests = async (args, flags) => {
|
|
|
227248
227705
|
}
|
|
227249
227706
|
}
|
|
227250
227707
|
const runLiteCase = async (kase) => {
|
|
227251
|
-
const caseDir =
|
|
227708
|
+
const caseDir = resolve31(artifactsRoot, slug(kase.name));
|
|
227252
227709
|
const plan = translateScenario(kase.steps, { outDir: caseDir, headroomFrames: 0 });
|
|
227253
227710
|
mkdirSync33(caseDir, { recursive: true });
|
|
227254
227711
|
const output = await liteSession.run(plan, {
|
|
@@ -227256,7 +227713,7 @@ var runTests = async (args, flags) => {
|
|
|
227256
227713
|
height: flags.height || kase.height || projectViewport?.height || 844,
|
|
227257
227714
|
onConsole
|
|
227258
227715
|
});
|
|
227259
|
-
const r = evaluateDesktopRun(plan, output, (path) =>
|
|
227716
|
+
const r = evaluateDesktopRun(plan, output, (path) => existsSync50(path) ? readFileSync41(path, "utf8") : null);
|
|
227260
227717
|
return {
|
|
227261
227718
|
name: kase.name,
|
|
227262
227719
|
kind: kase.kind,
|
|
@@ -227307,14 +227764,14 @@ var runTests = async (args, flags) => {
|
|
|
227307
227764
|
onConsole
|
|
227308
227765
|
});
|
|
227309
227766
|
try {
|
|
227310
|
-
const io = fileScenarioIO(
|
|
227767
|
+
const io = fileScenarioIO(resolve31(artifactsRoot, slug(kase.name)));
|
|
227311
227768
|
const outcome = await renderer.runScenario(session, steps, io);
|
|
227312
227769
|
result = {
|
|
227313
227770
|
name: kase.name,
|
|
227314
227771
|
kind: kase.kind,
|
|
227315
227772
|
ok: outcome.ok,
|
|
227316
227773
|
steps: outcome.steps.map((s) => ({ desc: s.desc, ok: s.ok, error: s.error })),
|
|
227317
|
-
artifacts: outcome.failureArtifacts.map((p) =>
|
|
227774
|
+
artifacts: outcome.failureArtifacts.map((p) => resolve31(artifactsRoot, slug(kase.name), p))
|
|
227318
227775
|
};
|
|
227319
227776
|
} finally {
|
|
227320
227777
|
session.dispose();
|
|
@@ -227365,9 +227822,9 @@ var runTests = async (args, flags) => {
|
|
|
227365
227822
|
init_peerInstall();
|
|
227366
227823
|
import { createRequire as createRequire8 } from "node:module";
|
|
227367
227824
|
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
227368
|
-
import { existsSync as
|
|
227825
|
+
import { existsSync as existsSync51, mkdtempSync as mkdtempSync7, readdirSync as readdirSync22, rmSync as rmSync16, statSync as statSync19 } from "node:fs";
|
|
227369
227826
|
import { tmpdir as tmpdir7 } from "node:os";
|
|
227370
|
-
import { extname, join as join54, relative as relative13, resolve as
|
|
227827
|
+
import { extname, join as join54, relative as relative13, resolve as resolve32 } from "node:path";
|
|
227371
227828
|
init_cli();
|
|
227372
227829
|
init_paths();
|
|
227373
227830
|
var DEFAULT_SIZE = 256;
|
|
@@ -227385,7 +227842,7 @@ var findKtx = () => {
|
|
|
227385
227842
|
"/opt/homebrew/bin/ktx"
|
|
227386
227843
|
].filter((p) => !!p);
|
|
227387
227844
|
for (const path of candidates) {
|
|
227388
|
-
if (
|
|
227845
|
+
if (existsSync51(path))
|
|
227389
227846
|
return path;
|
|
227390
227847
|
}
|
|
227391
227848
|
try {
|
|
@@ -227417,7 +227874,7 @@ var ktxToPng = (ktx, file, out) => {
|
|
|
227417
227874
|
run(["extract", "--transcode", "rgba8", "--level", "0", file, out]);
|
|
227418
227875
|
} catch (e) {
|
|
227419
227876
|
run(["extract", "--level", "0", file, out]);
|
|
227420
|
-
if (!
|
|
227877
|
+
if (!existsSync51(out))
|
|
227421
227878
|
throw e;
|
|
227422
227879
|
}
|
|
227423
227880
|
};
|
|
@@ -227452,8 +227909,8 @@ var thumbs = async (targets, size, force) => {
|
|
|
227452
227909
|
const rendered = [];
|
|
227453
227910
|
if (targets.length > 0) {
|
|
227454
227911
|
for (const arg of targets) {
|
|
227455
|
-
const abs =
|
|
227456
|
-
if (!
|
|
227912
|
+
const abs = resolve32(process.cwd(), arg);
|
|
227913
|
+
if (!existsSync51(abs))
|
|
227457
227914
|
throw new CliError(`No such file or folder: ${arg}`);
|
|
227458
227915
|
if (statSync19(abs).isDirectory())
|
|
227459
227916
|
files.push(...collect(abs));
|
|
@@ -227532,9 +227989,9 @@ init_peerInstall();
|
|
|
227532
227989
|
init_archives();
|
|
227533
227990
|
init_packageManager();
|
|
227534
227991
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
227535
|
-
import { existsSync as
|
|
227992
|
+
import { existsSync as existsSync52, readdirSync as readdirSync23 } from "node:fs";
|
|
227536
227993
|
import { homedir as homedir14 } from "node:os";
|
|
227537
|
-
import { dirname as dirname26, join as join55, resolve as
|
|
227994
|
+
import { dirname as dirname26, join as join55, resolve as resolve34 } from "node:path";
|
|
227538
227995
|
|
|
227539
227996
|
// src/commands/update/types.ts
|
|
227540
227997
|
init_api();
|
|
@@ -227542,10 +227999,10 @@ init_config();
|
|
|
227542
227999
|
init_manifest();
|
|
227543
228000
|
init_types4();
|
|
227544
228001
|
init_cli();
|
|
227545
|
-
import { resolve as
|
|
228002
|
+
import { resolve as resolve33 } from "node:path";
|
|
227546
228003
|
var label = (bundle) => `${Object.keys(bundle).length} file(s)`;
|
|
227547
228004
|
var refreshTypes = async (opts) => {
|
|
227548
|
-
const root = opts.dir ?
|
|
228005
|
+
const root = opts.dir ? resolve33(opts.dir) : (() => {
|
|
227549
228006
|
try {
|
|
227550
228007
|
return findProjectRoot(process.cwd());
|
|
227551
228008
|
} catch {
|
|
@@ -227603,10 +228060,10 @@ var refreshTypes = async (opts) => {
|
|
|
227603
228060
|
|
|
227604
228061
|
// src/commands/update/index.ts
|
|
227605
228062
|
var findProjectDir = (start) => {
|
|
227606
|
-
const home =
|
|
227607
|
-
let dir =
|
|
228063
|
+
const home = resolve34(homedir14());
|
|
228064
|
+
let dir = resolve34(start);
|
|
227608
228065
|
while (true) {
|
|
227609
|
-
if (dir !== home && (
|
|
228066
|
+
if (dir !== home && (existsSync52(join55(dir, "app.json")) || existsSync52(join55(dir, ".lecodes", "manifest.json")) || existsSync52(join55(dir, ".lecodes", "types"))))
|
|
227610
228067
|
return dir;
|
|
227611
228068
|
const parent = dirname26(dir);
|
|
227612
228069
|
if (parent === dir)
|
|
@@ -227643,7 +228100,7 @@ var semverNewer2 = (a, b) => {
|
|
|
227643
228100
|
return 0;
|
|
227644
228101
|
};
|
|
227645
228102
|
var FROM_SOURCE = /[\\/]src[\\/]commands[\\/]/.test(import.meta.url);
|
|
227646
|
-
var STANDALONE =
|
|
228103
|
+
var STANDALONE = existsSync52(join55(dirname26(process.execPath), "runtime")) && existsSync52(join55(dirname26(process.execPath), process.platform === "win32" ? "chisel.exe" : "chisel"));
|
|
227647
228104
|
var update_default = defineCommand({
|
|
227648
228105
|
name: "update",
|
|
227649
228106
|
summary: "Move everything the project depends on to its newest published state",
|
|
@@ -227703,7 +228160,7 @@ ${c.bold(name)} ${c.dim(what)}`);
|
|
|
227703
228160
|
return ["skipped", "not inside a project"];
|
|
227704
228161
|
if (flags["no-shells"])
|
|
227705
228162
|
return ["skipped", "--no-shells"];
|
|
227706
|
-
const present = ["ios", "android"].filter((d) =>
|
|
228163
|
+
const present = ["ios", "android"].filter((d) => existsSync52(join55(root, d)) && readdirSync23(join55(root, d)).length > 0);
|
|
227707
228164
|
if (present.length === 0)
|
|
227708
228165
|
return ["skipped", "no ios/ or android/ shell in this project (`lecodes app init ios|android`)"];
|
|
227709
228166
|
await runSubcommand(app_default, "update");
|
|
@@ -227781,8 +228238,8 @@ init_cli();
|
|
|
227781
228238
|
|
|
227782
228239
|
// src/web/build.ts
|
|
227783
228240
|
import { createHash as createHash9 } from "node:crypto";
|
|
227784
|
-
import { copyFileSync as copyFileSync2, cpSync as cpSync6, existsSync as
|
|
227785
|
-
import { dirname as dirname27, extname as extname2, join as join56, resolve as
|
|
228241
|
+
import { copyFileSync as copyFileSync2, cpSync as cpSync6, existsSync as existsSync53, mkdirSync as mkdirSync34, readFileSync as readFileSync42, readdirSync as readdirSync24, rmSync as rmSync17, statSync as statSync20, writeFileSync as writeFileSync36 } from "node:fs";
|
|
228242
|
+
import { dirname as dirname27, extname as extname2, join as join56, resolve as resolve35 } from "node:path";
|
|
227786
228243
|
init_fonts();
|
|
227787
228244
|
init_assetIcons();
|
|
227788
228245
|
init_projectCompile();
|
|
@@ -227806,8 +228263,8 @@ var walk = (dir, prefix = "") => {
|
|
|
227806
228263
|
};
|
|
227807
228264
|
var buildWeb = async (options) => {
|
|
227808
228265
|
const { root, manifest, name, lite } = options;
|
|
227809
|
-
const out =
|
|
227810
|
-
if (out ===
|
|
228266
|
+
const out = resolve35(root, options.out);
|
|
228267
|
+
if (out === resolve35(root) || resolve35(root).startsWith(out + "\\") || resolve35(root).startsWith(out + "/")) {
|
|
227811
228268
|
throw new CliError(`The output folder is cleaned before a build: "${options.out}" holds the project itself.`);
|
|
227812
228269
|
}
|
|
227813
228270
|
const config = readAppConfigAt(root)?.web ?? {};
|
|
@@ -227817,7 +228274,7 @@ var buildWeb = async (options) => {
|
|
|
227817
228274
|
const assetUrl = (path) => "assets" + encodeURI(path);
|
|
227818
228275
|
const collected = collectLocalEntries(root, manifest, "assets");
|
|
227819
228276
|
const entries = collected.entries.filter((e) => {
|
|
227820
|
-
const abs = e.absPath ?
|
|
228277
|
+
const abs = e.absPath ? resolve35(e.absPath) : null;
|
|
227821
228278
|
return !(abs && (abs === out || abs.startsWith(out + "\\") || abs.startsWith(out + "/")));
|
|
227822
228279
|
});
|
|
227823
228280
|
for (const w of collected.warnings)
|
|
@@ -227859,8 +228316,8 @@ var buildWeb = async (options) => {
|
|
|
227859
228316
|
mkdirSync34(out, { recursive: true });
|
|
227860
228317
|
cpSync6(lite, out, { recursive: true });
|
|
227861
228318
|
const table = join56(out, "features.json");
|
|
227862
|
-
if (
|
|
227863
|
-
const { features, fonts } = JSON.parse(
|
|
228319
|
+
if (existsSync53(table)) {
|
|
228320
|
+
const { features, fonts } = JSON.parse(readFileSync42(table, "utf8"));
|
|
227864
228321
|
if (config.font === "system")
|
|
227865
228322
|
for (const file of fonts ?? [])
|
|
227866
228323
|
rmSync17(join56(out, file), { force: true });
|
|
@@ -227873,9 +228330,9 @@ var buildWeb = async (options) => {
|
|
|
227873
228330
|
rmSync17(table);
|
|
227874
228331
|
}
|
|
227875
228332
|
const pluginsTable = join56(out, "plugins.json");
|
|
227876
|
-
if (
|
|
228333
|
+
if (existsSync53(pluginsTable)) {
|
|
227877
228334
|
const listed = new Set((/^\/\/ plugins: (.+)$/m.exec(header)?.[1] ?? "").split(";").map((id) => id.trim()).filter(Boolean));
|
|
227878
|
-
const { plugins } = JSON.parse(
|
|
228335
|
+
const { plugins } = JSON.parse(readFileSync42(pluginsTable, "utf8"));
|
|
227879
228336
|
const kept = {};
|
|
227880
228337
|
for (const id of Object.keys(plugins)) {
|
|
227881
228338
|
if (listed.has(id))
|
|
@@ -227901,7 +228358,7 @@ var buildWeb = async (options) => {
|
|
|
227901
228358
|
if (!js.includes(entry.fileSrc))
|
|
227902
228359
|
continue;
|
|
227903
228360
|
const to = join56(out, "assets", ...entry.path.split("/").filter(Boolean));
|
|
227904
|
-
if (
|
|
228361
|
+
if (existsSync53(to)) {
|
|
227905
228362
|
warnings.push(`${entry.path}: a file of the page has this name in the built folder — the project's file was not copied`);
|
|
227906
228363
|
continue;
|
|
227907
228364
|
}
|
|
@@ -227909,12 +228366,12 @@ var buildWeb = async (options) => {
|
|
|
227909
228366
|
copyFileSync2(entry.absPath, to);
|
|
227910
228367
|
}
|
|
227911
228368
|
const app = readAppConfigAt(root);
|
|
227912
|
-
let html =
|
|
228369
|
+
let html = readFileSync42(join56(lite, "index.html"), "utf8");
|
|
227913
228370
|
const head = [];
|
|
227914
228371
|
head.push(`<meta name="theme-color" content="${escapeHtml(config.themeColor ?? "#000000")}" />`);
|
|
227915
228372
|
if (app?.icon) {
|
|
227916
|
-
const icon =
|
|
227917
|
-
if (
|
|
228373
|
+
const icon = resolve35(root, app.icon);
|
|
228374
|
+
if (existsSync53(icon)) {
|
|
227918
228375
|
const file = `icon${extname2(icon).toLowerCase() || ".png"}`;
|
|
227919
228376
|
copyFileSync2(icon, join56(out, file));
|
|
227920
228377
|
head.push(`<link rel="icon" href="${file}" />`, `<link rel="apple-touch-icon" href="${file}" />`);
|
|
@@ -227949,12 +228406,12 @@ var buildWeb = async (options) => {
|
|
|
227949
228406
|
};
|
|
227950
228407
|
|
|
227951
228408
|
// src/web/preview.ts
|
|
227952
|
-
import { createReadStream as createReadStream3, existsSync as
|
|
228409
|
+
import { createReadStream as createReadStream3, existsSync as existsSync54, statSync as statSync21 } from "node:fs";
|
|
227953
228410
|
import { createServer as createServer4 } from "node:http";
|
|
227954
|
-
import { join as join57, resolve as
|
|
228411
|
+
import { join as join57, resolve as resolve36 } from "node:path";
|
|
227955
228412
|
var startPreview = async (dir, port) => {
|
|
227956
|
-
const root =
|
|
227957
|
-
const fallback =
|
|
228413
|
+
const root = resolve36(dir);
|
|
228414
|
+
const fallback = existsSync54(join57(root, "404.html")) ? join57(root, "index.html") : null;
|
|
227958
228415
|
const server2 = createServer4((req, res) => {
|
|
227959
228416
|
let path;
|
|
227960
228417
|
try {
|
|
@@ -227971,10 +228428,10 @@ var startPreview = async (dir, port) => {
|
|
|
227971
228428
|
return;
|
|
227972
228429
|
}
|
|
227973
228430
|
let file = join57(root, ...parts);
|
|
227974
|
-
if (
|
|
228431
|
+
if (existsSync54(file) && statSync21(file).isDirectory())
|
|
227975
228432
|
file = join57(file, "index.html");
|
|
227976
228433
|
let status = 200;
|
|
227977
|
-
if (!
|
|
228434
|
+
if (!existsSync54(file) || !statSync21(file).isFile()) {
|
|
227978
228435
|
if (!fallback || /\.[a-z0-9]+$/i.test(parts[parts.length - 1] ?? "")) {
|
|
227979
228436
|
res.writeHead(404, { "content-type": "text/plain" });
|
|
227980
228437
|
res.end("Not found");
|
|
@@ -228164,7 +228621,7 @@ var web_default = defineCommand({
|
|
|
228164
228621
|
|
|
228165
228622
|
// src/commands/index.ts
|
|
228166
228623
|
var commandGroups = (opts) => [
|
|
228167
|
-
{ title: "Project", commands: [init_default, create_default, clone_default, link_default, status_default, diff_default, pull_default, push_default, login_default] },
|
|
228624
|
+
{ title: "Project", commands: [init_default, create_default, clone_default, link_default, status_default, diff_default, pull_default, push_default, publish_default, releases_default, login_default] },
|
|
228168
228625
|
{ title: "Run & test", commands: [
|
|
228169
228626
|
dev_default,
|
|
228170
228627
|
web_default,
|
|
@@ -228201,7 +228658,7 @@ init_peers();
|
|
|
228201
228658
|
var STANDALONE2 = (() => {
|
|
228202
228659
|
const dir = dirname28(process.execPath);
|
|
228203
228660
|
const chisel = process.platform === "win32" ? "chisel.exe" : "chisel";
|
|
228204
|
-
return
|
|
228661
|
+
return existsSync56(join59(dir, "runtime")) && existsSync56(join59(dir, chisel));
|
|
228205
228662
|
})();
|
|
228206
228663
|
var INSTALLED = STANDALONE2 && (() => {
|
|
228207
228664
|
const exe = process.platform === "win32" ? "lecodes.exe" : "lecodes";
|
|
@@ -228217,7 +228674,7 @@ var INSTALLED = STANDALONE2 && (() => {
|
|
|
228217
228674
|
return false;
|
|
228218
228675
|
try {
|
|
228219
228676
|
const cand = join59(d, exe);
|
|
228220
|
-
return
|
|
228677
|
+
return existsSync56(cand) && realpathSync2(cand) === self2;
|
|
228221
228678
|
} catch {
|
|
228222
228679
|
return false;
|
|
228223
228680
|
}
|