@lunora/cli 0.0.0 → 1.0.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/LICENSE.md +105 -0
  2. package/README.md +109 -9
  3. package/__assets__/package-og.svg +14 -0
  4. package/dist/bin.mjs +11 -0
  5. package/dist/index.d.mts +956 -0
  6. package/dist/index.d.ts +956 -0
  7. package/dist/index.mjs +19 -0
  8. package/dist/packem_chunks/handler.mjs +150 -0
  9. package/dist/packem_chunks/handler10.mjs +22 -0
  10. package/dist/packem_chunks/handler11.mjs +192 -0
  11. package/dist/packem_chunks/handler12.mjs +131 -0
  12. package/dist/packem_chunks/handler13.mjs +65 -0
  13. package/dist/packem_chunks/handler14.mjs +58 -0
  14. package/dist/packem_chunks/handler15.mjs +79 -0
  15. package/dist/packem_chunks/handler16.mjs +43 -0
  16. package/dist/packem_chunks/handler17.mjs +105 -0
  17. package/dist/packem_chunks/handler18.mjs +170 -0
  18. package/dist/packem_chunks/handler19.mjs +89 -0
  19. package/dist/packem_chunks/handler2.mjs +114 -0
  20. package/dist/packem_chunks/handler20.mjs +94 -0
  21. package/dist/packem_chunks/handler21.mjs +311 -0
  22. package/dist/packem_chunks/handler3.mjs +204 -0
  23. package/dist/packem_chunks/handler4.mjs +33 -0
  24. package/dist/packem_chunks/handler5.mjs +49 -0
  25. package/dist/packem_chunks/handler6.mjs +91 -0
  26. package/dist/packem_chunks/handler7.mjs +42 -0
  27. package/dist/packem_chunks/handler8.mjs +174 -0
  28. package/dist/packem_chunks/handler9.mjs +16 -0
  29. package/dist/packem_chunks/planDevCommand.mjs +500 -0
  30. package/dist/packem_chunks/runCodegenCommand.mjs +52 -0
  31. package/dist/packem_chunks/runDeployCommand.mjs +504 -0
  32. package/dist/packem_chunks/runInitCommand.mjs +1498 -0
  33. package/dist/packem_chunks/runMigrateGenerateCommand.mjs +397 -0
  34. package/dist/packem_chunks/runResetCommand.mjs +41 -0
  35. package/dist/packem_chunks/runRpcCommand.mjs +68 -0
  36. package/dist/packem_shared/COMMANDS-D3h9Iwvl.mjs +944 -0
  37. package/dist/packem_shared/DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs +244 -0
  38. package/dist/packem_shared/admin-url-4UzT-CI4.mjs +19 -0
  39. package/dist/packem_shared/api-spec-CtA6ilu4.mjs +13 -0
  40. package/dist/packem_shared/buildRegistryIndex-BcYe607_.mjs +38 -0
  41. package/dist/packem_shared/command-BC30oSBW.mjs +14 -0
  42. package/dist/packem_shared/commands-DPKWlqqX.mjs +812 -0
  43. package/dist/packem_shared/createLogger-B40gPzQo.mjs +78 -0
  44. package/dist/packem_shared/createRecordingSpawner-DxI3mebw.mjs +43 -0
  45. package/dist/packem_shared/detect-package-manager-DYp7n3mJ.mjs +61 -0
  46. package/dist/packem_shared/diffSnapshots-BeDvvNiF.mjs +161 -0
  47. package/dist/packem_shared/docker-hMQ97KSQ.mjs +21 -0
  48. package/dist/packem_shared/insertSchemaExtension-BuzF6-t2.mjs +59 -0
  49. package/dist/packem_shared/open-url-Dfq6fAyT.mjs +41 -0
  50. package/dist/packem_shared/output-format-wUvAN6AL.mjs +17 -0
  51. package/dist/packem_shared/parseArgs-YXFuKdEk.mjs +56 -0
  52. package/dist/packem_shared/parseManifest--vZf2FY1.mjs +94 -0
  53. package/dist/packem_shared/resolve-target-qbsJ_5sF.mjs +16 -0
  54. package/dist/packem_shared/runAddCommand-CTRA_JlL.mjs +4 -0
  55. package/dist/packem_shared/schema-drift-gate-BtBt0as0.mjs +79 -0
  56. package/dist/packem_shared/schemaIrToSnapshot-DdsljJT-.mjs +43 -0
  57. package/dist/packem_shared/storage-2RJBhUC4.mjs +84 -0
  58. package/dist/packem_shared/tui-prompts-DEiPCKV-.mjs +661 -0
  59. package/dist/packem_shared/wrangler-name-cy4yhm9j.mjs +12 -0
  60. package/package.json +62 -18
  61. package/skills/README.md +29 -0
  62. package/skills/lunora/SKILL.md +83 -0
  63. package/skills/lunora-create-package/SKILL.md +129 -0
  64. package/skills/lunora-deploy/SKILL.md +150 -0
  65. package/skills/lunora-functions/SKILL.md +182 -0
  66. package/skills/lunora-migration-helper/SKILL.md +194 -0
  67. package/skills/lunora-performance-audit/SKILL.md +143 -0
  68. package/skills/lunora-quickstart/SKILL.md +240 -0
  69. package/skills/lunora-realtime/SKILL.md +177 -0
  70. package/skills/lunora-setup-auth/SKILL.md +170 -0
  71. package/skills/lunora-setup-hyperdrive/SKILL.md +154 -0
  72. package/skills/lunora-setup-hyperdrive-global/SKILL.md +171 -0
  73. package/skills/lunora-setup-mail/SKILL.md +151 -0
  74. package/skills/lunora-setup-scheduler/SKILL.md +157 -0
  75. package/skills/lunora-setup-storage/SKILL.md +158 -0
@@ -0,0 +1,78 @@
1
+ import { STEP_BADGE_NAMES, LunoraReporter } from '@lunora/config';
2
+ import { JsonReporter } from '@visulima/pail/reporter/json';
3
+ import { createPail } from '@visulima/pail/server';
4
+
5
+ const wantJson = () => {
6
+ const flag = process.env.LUNORA_LOG_JSON;
7
+ return flag === "1" || flag === "true";
8
+ };
9
+ const buildReporter = () => {
10
+ const Reporter = wantJson() ? JsonReporter : LunoraReporter;
11
+ return new Reporter();
12
+ };
13
+ const STEP_LOG_TYPES = Object.fromEntries(STEP_BADGE_NAMES.map((name) => [name, { label: name, logLevel: "informational" }]));
14
+ let sharedPail;
15
+ const getPail = () => {
16
+ sharedPail ??= createPail({
17
+ reporters: [buildReporter()],
18
+ scope: ["lunora"],
19
+ stderr: process.stderr,
20
+ stdout: process.stdout,
21
+ types: STEP_LOG_TYPES
22
+ });
23
+ return sharedPail;
24
+ };
25
+ const createLogger = () => {
26
+ return {
27
+ debug: (message) => {
28
+ getPail().debug(message);
29
+ },
30
+ error: (message) => {
31
+ getPail().error(message);
32
+ },
33
+ info: (message) => {
34
+ getPail().info(message);
35
+ },
36
+ success: (message) => {
37
+ getPail().success(message);
38
+ },
39
+ warn: (message) => {
40
+ getPail().warn(message);
41
+ }
42
+ };
43
+ };
44
+ const createStderrLogger = () => {
45
+ const write = (tag, message) => {
46
+ process.stderr.write(`${tag} ${message}
47
+ `);
48
+ };
49
+ return {
50
+ debug: (message) => {
51
+ write("debug", message);
52
+ },
53
+ error: (message) => {
54
+ write("error", message);
55
+ },
56
+ info: (message) => {
57
+ write("info ", message);
58
+ },
59
+ success: (message) => {
60
+ write("ok ", message);
61
+ },
62
+ warn: (message) => {
63
+ write("warn ", message);
64
+ }
65
+ };
66
+ };
67
+ const pail = /* @__PURE__ */ new Proxy({}, {
68
+ get(_target, property) {
69
+ const instance = getPail();
70
+ const value = instance[property];
71
+ return typeof value === "function" ? value.bind(instance) : value;
72
+ }
73
+ });
74
+ const logStep = (type, message) => {
75
+ getPail()[type](message);
76
+ };
77
+
78
+ export { createLogger, createStderrLogger, getPail, logStep, pail };
@@ -0,0 +1,43 @@
1
+ import { spawn } from 'node:child_process';
2
+
3
+ const defaultSpawner = (descriptor) => new Promise((resolve, reject) => {
4
+ const hasInput = typeof descriptor.input === "string";
5
+ const wantCapture = descriptor.captureStdout === true;
6
+ let stdout = "inherit";
7
+ if (wantCapture) {
8
+ stdout = "pipe";
9
+ } else if (descriptor.stdoutToStderr) {
10
+ stdout = 2;
11
+ }
12
+ const child = spawn(descriptor.command, [...descriptor.args], {
13
+ cwd: descriptor.cwd ?? process.cwd(),
14
+ env: descriptor.env ? { ...process.env, ...descriptor.env } : process.env,
15
+ stdio: [hasInput ? "pipe" : "inherit", stdout, "inherit"]
16
+ });
17
+ let captured = "";
18
+ if (wantCapture && child.stdout) {
19
+ child.stdout.on("data", (chunk) => {
20
+ captured += chunk.toString("utf8");
21
+ process.stdout.write(chunk);
22
+ });
23
+ }
24
+ child.on("error", (error) => {
25
+ reject(error);
26
+ });
27
+ child.on("exit", (code, signal) => {
28
+ resolve({ code: code ?? (signal ? 1 : 0), stdout: wantCapture ? captured : void 0 });
29
+ });
30
+ if (hasInput && child.stdin) {
31
+ child.stdin.end(descriptor.input);
32
+ }
33
+ });
34
+ const createRecordingSpawner = (exitCode = 0) => {
35
+ const calls = [];
36
+ const spawner = (descriptor) => {
37
+ calls.push({ descriptor });
38
+ return Promise.resolve({ code: exitCode });
39
+ };
40
+ return { calls, spawner };
41
+ };
42
+
43
+ export { createRecordingSpawner, defaultSpawner };
@@ -0,0 +1,61 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { dirname, join } from '@visulima/path';
4
+
5
+ const FALLBACK = "pnpm";
6
+ const KNOWN_MANAGERS = ["pnpm", "yarn", "npm", "bun"];
7
+ const INSTALL_PREFERENCE = ["pnpm", "bun", "yarn", "npm"];
8
+ const isManagerInstalled = (manager) => {
9
+ try {
10
+ return spawnSync(manager, ["--version"], { stdio: "ignore", timeout: 5e3 }).status === 0;
11
+ } catch {
12
+ return false;
13
+ }
14
+ };
15
+ const detectInstalledManagers = (probe = isManagerInstalled) => INSTALL_PREFERENCE.filter((manager) => probe(manager));
16
+ const installArgsFor = (manager) => {
17
+ return { args: ["install"], command: manager };
18
+ };
19
+ const parseDeclaredManager = (declared) => {
20
+ if (typeof declared !== "string") {
21
+ return void 0;
22
+ }
23
+ return KNOWN_MANAGERS.find((manager) => declared.startsWith(`${manager}@`));
24
+ };
25
+ const readDeclaredManager = (directory) => {
26
+ const candidate = join(directory, "package.json");
27
+ if (!existsSync(candidate)) {
28
+ return void 0;
29
+ }
30
+ try {
31
+ const parsed = JSON.parse(readFileSync(candidate, "utf8"));
32
+ return parseDeclaredManager(parsed.packageManager);
33
+ } catch {
34
+ return void 0;
35
+ }
36
+ };
37
+ const detectPackageManager = (startDirectory) => {
38
+ let directory = startDirectory;
39
+ while (directory && directory !== dirname(directory)) {
40
+ const declared = readDeclaredManager(directory);
41
+ if (declared !== void 0) {
42
+ return declared;
43
+ }
44
+ directory = dirname(directory);
45
+ }
46
+ return FALLBACK;
47
+ };
48
+ const execArgsFor = (manager, command, args) => {
49
+ if (manager === "yarn") {
50
+ return { args: [command, ...args], command: "yarn" };
51
+ }
52
+ if (manager === "bun") {
53
+ return { args: ["x", command, ...args], command: "bun" };
54
+ }
55
+ if (manager === "npm") {
56
+ return { args: ["--", command, ...args], command: "npx" };
57
+ }
58
+ return { args: ["exec", command, ...args], command: "pnpm" };
59
+ };
60
+
61
+ export { detectInstalledManagers as a, detectPackageManager as d, execArgsFor as e, installArgsFor as i };
@@ -0,0 +1,161 @@
1
+ import { quoteIdentifier, sqlAffinityForKind, frameworkColumnDdl, columnRef, physicalIndexName } from '@lunora/d1/dialect';
2
+
3
+ const validatorKindToSqlType = (kind) => sqlAffinityForKind(kind);
4
+ const renderColumnDefinition = (name, column) => {
5
+ const parts = [quoteIdentifier(name), column.sqlType];
6
+ if (!column.nullable) {
7
+ parts.push("NOT NULL");
8
+ }
9
+ return parts.join(" ");
10
+ };
11
+ const renderCreateTable = (table) => {
12
+ const columns = Object.entries(table.columns).map(([columnName, column]) => ` ${renderColumnDefinition(columnName, column)}`);
13
+ const lines = [...frameworkColumnDdl().map((column) => ` ${column}`), ...columns].join(",\n");
14
+ return `CREATE TABLE IF NOT EXISTS ${quoteIdentifier(table.name)} (
15
+ ${lines}
16
+ );`;
17
+ };
18
+ const renderDropTable = (tableName) => `DROP TABLE IF EXISTS ${quoteIdentifier(tableName)};`;
19
+ const renderAddColumn = (tableName, columnName, column) => `ALTER TABLE ${quoteIdentifier(tableName)} ADD COLUMN ${renderColumnDefinition(columnName, column)};`;
20
+ const renderCreateIndex = (tableName, index) => {
21
+ const fields = index.fields.map((field) => columnRef(field)).join(", ");
22
+ const uniqueClause = index.unique ? "UNIQUE " : "";
23
+ return `CREATE ${uniqueClause}INDEX IF NOT EXISTS ${physicalIndexName(tableName, index.name)} ON ${quoteIdentifier(tableName)} (${fields});`;
24
+ };
25
+ const renderDropIndex = (tableName, indexName) => `DROP INDEX IF EXISTS ${physicalIndexName(tableName, indexName)};`;
26
+ const diffExistingColumn = (tableName, columnName, old, column, unsupported) => {
27
+ if (old.sqlType !== column.sqlType) {
28
+ unsupported.push({
29
+ kind: "columnTypeChange",
30
+ summary: `column type change on ${tableName}.${columnName}: ${old.sqlType} → ${column.sqlType} (write SQL manually)`
31
+ });
32
+ }
33
+ if (old.nullable !== column.nullable) {
34
+ unsupported.push({
35
+ kind: "columnTypeChange",
36
+ summary: `nullability change on ${tableName}.${columnName}: ${old.nullable ? "NULL" : "NOT NULL"} → ${column.nullable ? "NULL" : "NOT NULL"} (write SQL manually)`
37
+ });
38
+ }
39
+ };
40
+ const diffColumns = (tableName, previous, next, entries, unsupported) => {
41
+ for (const [columnName, column] of Object.entries(next)) {
42
+ const old = previous[columnName];
43
+ if (old === void 0) {
44
+ entries.push({
45
+ kind: "addColumn",
46
+ sql: renderAddColumn(tableName, columnName, column),
47
+ summary: `ADD COLUMN ${tableName}.${columnName}`
48
+ });
49
+ continue;
50
+ }
51
+ diffExistingColumn(tableName, columnName, old, column, unsupported);
52
+ }
53
+ for (const columnName of Object.keys(previous)) {
54
+ if (next[columnName] === void 0) {
55
+ unsupported.push({
56
+ kind: "dropColumn",
57
+ summary: `DROP COLUMN ${tableName}.${columnName} (SQLite drop-column requires careful migration — write SQL manually)`
58
+ });
59
+ }
60
+ }
61
+ };
62
+ const diffIndexes = (tableName, previous, next, entries, unsupported) => {
63
+ for (const [indexName, index] of Object.entries(next)) {
64
+ const old = previous[indexName];
65
+ if (old === void 0) {
66
+ entries.push({
67
+ kind: "createIndex",
68
+ sql: renderCreateIndex(tableName, index),
69
+ summary: `CREATE INDEX ${indexName} ON ${tableName}`
70
+ });
71
+ continue;
72
+ }
73
+ const fieldsEqual = old.fields.length === index.fields.length && old.fields.every((field, i) => field === index.fields[i]);
74
+ if (!fieldsEqual || old.unique !== index.unique) {
75
+ unsupported.push({
76
+ kind: "indexRename",
77
+ summary: `index ${indexName} changed on ${tableName} — drop+create manually if intentional`
78
+ });
79
+ }
80
+ }
81
+ for (const indexName of Object.keys(previous)) {
82
+ if (next[indexName] === void 0) {
83
+ entries.push({
84
+ kind: "dropIndex",
85
+ sql: renderDropIndex(tableName, indexName),
86
+ summary: `DROP INDEX ${indexName}`
87
+ });
88
+ }
89
+ }
90
+ };
91
+ const diffNewTable = (tableName, table, entries) => {
92
+ entries.push({
93
+ kind: "createTable",
94
+ sql: renderCreateTable(table),
95
+ summary: `CREATE TABLE ${tableName}`
96
+ });
97
+ for (const index of Object.values(table.indexes)) {
98
+ entries.push({
99
+ kind: "createIndex",
100
+ sql: renderCreateIndex(tableName, index),
101
+ summary: `CREATE INDEX ${index.name} ON ${tableName}`
102
+ });
103
+ }
104
+ };
105
+ const diffSnapshots = (previous, next) => {
106
+ const previousTables = previous?.tables ?? {};
107
+ const entries = [];
108
+ const unsupported = [];
109
+ for (const [tableName, table] of Object.entries(next.tables)) {
110
+ const old = previousTables[tableName];
111
+ if (old === void 0) {
112
+ diffNewTable(tableName, table, entries);
113
+ continue;
114
+ }
115
+ diffColumns(tableName, old.columns, table.columns, entries, unsupported);
116
+ diffIndexes(tableName, old.indexes, table.indexes, entries, unsupported);
117
+ }
118
+ for (const [tableName] of Object.entries(previousTables)) {
119
+ if (next.tables[tableName] === void 0) {
120
+ entries.push({
121
+ kind: "dropTable",
122
+ sql: renderDropTable(tableName),
123
+ summary: `DROP TABLE ${tableName}`
124
+ });
125
+ }
126
+ }
127
+ return {
128
+ empty: entries.length === 0 && unsupported.length === 0,
129
+ entries,
130
+ unsupported
131
+ };
132
+ };
133
+ const renderMigrationFile = (name, diff, generatedAt) => {
134
+ const lines = [
135
+ `-- Lunora migration: ${name}`,
136
+ `-- Generated at ${generatedAt}`,
137
+ "-- This file was produced by `lunora migrate generate`. Review carefully before applying.",
138
+ ""
139
+ ];
140
+ for (const entry of diff.entries) {
141
+ lines.push(`-- ${entry.summary}`, entry.sql, "");
142
+ }
143
+ if (diff.unsupported.length > 0) {
144
+ lines.push(
145
+ "-- ---------------------------------------------------------------",
146
+ "-- The following deltas are NOT auto-generated in v0.1.",
147
+ "-- Write the appropriate SQL below by hand:",
148
+ "--"
149
+ );
150
+ for (const entry of diff.unsupported) {
151
+ lines.push(`-- * ${entry.summary}`);
152
+ }
153
+ lines.push("-- ---------------------------------------------------------------", "");
154
+ }
155
+ if (diff.empty) {
156
+ lines.push("-- No changes detected. Re-running `lunora migrate generate` will overwrite this file.", "");
157
+ }
158
+ return lines.join("\n");
159
+ };
160
+
161
+ export { diffSnapshots, renderAddColumn, renderCreateIndex, renderCreateTable, renderDropIndex, renderDropTable, renderMigrationFile, validatorKindToSqlType };
@@ -0,0 +1,21 @@
1
+ import { spawnSync } from 'node:child_process';
2
+
3
+ const isDockerAvailable = () => {
4
+ try {
5
+ return spawnSync("docker", ["info"], { stdio: "ignore" }).status === 0;
6
+ } catch {
7
+ return false;
8
+ }
9
+ };
10
+ const isRailpackAvailable = () => {
11
+ if (typeof process.env.BUILDKIT_HOST !== "string" || process.env.BUILDKIT_HOST.length === 0) {
12
+ return false;
13
+ }
14
+ try {
15
+ return spawnSync("railpack", ["--version"], { stdio: "ignore" }).status === 0;
16
+ } catch {
17
+ return false;
18
+ }
19
+ };
20
+
21
+ export { isRailpackAvailable as a, isDockerAvailable as i };
@@ -0,0 +1,59 @@
1
+ import { Project, SyntaxKind } from 'ts-morph';
2
+
3
+ const VALID_JS_IDENTIFIER = /^[A-Za-z_$][\w$]*$/u;
4
+ const startMarker = (key) => `// lunora:add:${key}:start`;
5
+ const endMarker = (key) => `// lunora:add:${key}:end`;
6
+ const extensionImportSpecifier = (key) => `./${key}/schema`;
7
+ const findDefineSchemaCall = (callExpressions) => {
8
+ for (const call of callExpressions) {
9
+ if (call.getExpression().getText() === "defineSchema") {
10
+ return call;
11
+ }
12
+ }
13
+ return void 0;
14
+ };
15
+ const insertSchemaExtension = (source, key) => {
16
+ if (!VALID_JS_IDENTIFIER.test(key)) {
17
+ return { ok: false, reason: "invalid-identifier" };
18
+ }
19
+ if (source.includes(startMarker(key))) {
20
+ return { ok: false, reason: "already-applied" };
21
+ }
22
+ const project = new Project({
23
+ compilerOptions: { allowJs: true },
24
+ useInMemoryFileSystem: true
25
+ });
26
+ const sourceFile = project.createSourceFile("schema.ts", source, { overwrite: true });
27
+ const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
28
+ const defineSchemaCall = findDefineSchemaCall(callExpressions);
29
+ if (!defineSchemaCall) {
30
+ return { ok: false, reason: "no-define-schema" };
31
+ }
32
+ const tablesArgument = defineSchemaCall.getArguments()[0];
33
+ if (tablesArgument?.getKind() !== SyntaxKind.ObjectLiteralExpression) {
34
+ return { ok: false, reason: "non-object-argument" };
35
+ }
36
+ const variableDeclaration = defineSchemaCall.getFirstAncestorByKind(SyntaxKind.VariableDeclaration);
37
+ if (!variableDeclaration) {
38
+ return { ok: false, reason: "no-define-schema" };
39
+ }
40
+ const initializer = variableDeclaration.getInitializer();
41
+ if (!initializer) {
42
+ return { ok: false, reason: "no-define-schema" };
43
+ }
44
+ const insertAt = initializer.getEnd();
45
+ const chainText = `
46
+ ${startMarker(key)}
47
+ .extend(${key}.extension)
48
+ ${endMarker(key)}
49
+ `;
50
+ sourceFile.insertText(insertAt, chainText);
51
+ const importText = `${startMarker(key)}
52
+ import { ${key} } from "${extensionImportSpecifier(key)}";
53
+ ${endMarker(key)}
54
+ `;
55
+ sourceFile.insertText(0, importText);
56
+ return { ok: true, text: sourceFile.getFullText() };
57
+ };
58
+
59
+ export { insertSchemaExtension };
@@ -0,0 +1,41 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { platform } from 'node:os';
3
+
4
+ const platformCommand = () => {
5
+ const os = platform();
6
+ if (os === "darwin") {
7
+ return { args: [], command: "open" };
8
+ }
9
+ if (os === "win32") {
10
+ return { args: ["/c", "start", ""], command: "cmd" };
11
+ }
12
+ return { args: [], command: "xdg-open" };
13
+ };
14
+ const escapeForCmd = (url) => url.replaceAll("%", "%25").replaceAll("&", "%26").replaceAll("|", "%7C").replaceAll("^", "%5E").replaceAll("<", "%3C").replaceAll(">", "%3E").replaceAll("(", "%28").replaceAll(")", "%29").replaceAll('"', "%22").replaceAll("!", "%21");
15
+ const platformOpener = (url) => new Promise((resolve, reject) => {
16
+ const { args, command } = platformCommand();
17
+ const safeUrl = platform() === "win32" ? escapeForCmd(url) : url;
18
+ const child = spawn(command, [...args, safeUrl], { detached: true, stdio: "ignore" });
19
+ child.once("error", (error) => {
20
+ reject(error);
21
+ });
22
+ child.once("spawn", () => {
23
+ child.unref();
24
+ resolve();
25
+ });
26
+ });
27
+ const openUrl = async (url, options = {}) => {
28
+ let parsed;
29
+ try {
30
+ parsed = new URL(url);
31
+ } catch {
32
+ throw new Error(`Invalid URL: ${url}`);
33
+ }
34
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
35
+ throw new Error(`Refusing to open non-http(s) URL: ${url}`);
36
+ }
37
+ const opener = options.opener ?? platformOpener;
38
+ await opener(url);
39
+ };
40
+
41
+ export { openUrl as o };
@@ -0,0 +1,17 @@
1
+ import { createStderrLogger } from './createLogger-B40gPzQo.mjs';
2
+
3
+ const OUTPUT_FORMATS = /* @__PURE__ */ new Set(["json", "pretty"]);
4
+ const validateOutputFormat = (command, format) => {
5
+ if (format !== void 0 && !OUTPUT_FORMATS.has(format)) {
6
+ return `${command}: unknown --format "${format}" — expected pretty | json`;
7
+ }
8
+ return void 0;
9
+ };
10
+ const isJsonFormat = (format) => format === "json";
11
+ const loggerForFormat = (format, prettyLogger) => isJsonFormat(format) ? createStderrLogger() : prettyLogger;
12
+ const printJson = (result) => {
13
+ process.stdout.write(`${JSON.stringify(result, void 0, 2)}
14
+ `);
15
+ };
16
+
17
+ export { isJsonFormat as i, loggerForFormat as l, printJson as p, validateOutputFormat as v };
@@ -0,0 +1,56 @@
1
+ const consumeLongOption = (body, next, booleanFlags, accumulator) => {
2
+ const eqIndex = body.indexOf("=");
3
+ if (eqIndex !== -1) {
4
+ accumulator.options[body.slice(0, eqIndex)] = body.slice(eqIndex + 1);
5
+ return 1;
6
+ }
7
+ if (booleanFlags.has(body)) {
8
+ accumulator.flags[body] = true;
9
+ return 1;
10
+ }
11
+ if (next !== void 0 && !next.startsWith("-")) {
12
+ accumulator.options[body] = next;
13
+ return 2;
14
+ }
15
+ accumulator.flags[body] = true;
16
+ return 1;
17
+ };
18
+ const consumeShortOption = (body, next, accumulator) => {
19
+ if (body.length > 1) {
20
+ accumulator.options[body[0]] = body.slice(1);
21
+ return 1;
22
+ }
23
+ if (next !== void 0 && !next.startsWith("-")) {
24
+ accumulator.options[body] = next;
25
+ return 2;
26
+ }
27
+ accumulator.flags[body] = true;
28
+ return 1;
29
+ };
30
+ const parseArgs = (argv, booleanFlags = /* @__PURE__ */ new Set()) => {
31
+ const accumulator = { flags: {}, options: {}, positional: [] };
32
+ let index = 0;
33
+ let terminated = false;
34
+ while (index < argv.length) {
35
+ const token = argv[index];
36
+ if (token === void 0) {
37
+ index += 1;
38
+ continue;
39
+ }
40
+ if (terminated || !token.startsWith("-") || token.length === 1) {
41
+ accumulator.positional.push(token);
42
+ index += 1;
43
+ continue;
44
+ }
45
+ if (token === "--") {
46
+ terminated = true;
47
+ index += 1;
48
+ continue;
49
+ }
50
+ const next = argv[index + 1];
51
+ index += token.startsWith("--") ? consumeLongOption(token.slice(2), next, booleanFlags, accumulator) : consumeShortOption(token.slice(1), next, accumulator);
52
+ }
53
+ return { flags: accumulator.flags, options: accumulator.options, positional: accumulator.positional };
54
+ };
55
+
56
+ export { parseArgs as default };
@@ -0,0 +1,94 @@
1
+ const NEWLINE_PRESENT = /[\r\n]/u;
2
+ const VALID_ENV_NAME = /^[A-Za-z_]\w*$/u;
3
+ const VALID_ITEM_NAME = /^[A-Za-z0-9][\w-]*$/u;
4
+ const parseManifest = (raw, itemName) => {
5
+ if (typeof raw !== "object" || raw === null) {
6
+ throw new Error(`registry.json for "${itemName}" is not an object`);
7
+ }
8
+ const record = raw;
9
+ const { name } = record;
10
+ if (typeof name !== "string" || name.length === 0) {
11
+ throw new Error(`registry.json for "${itemName}" is missing a string "name"`);
12
+ }
13
+ if (!VALID_ITEM_NAME.test(name)) {
14
+ throw new Error(
15
+ `registry.json for "${itemName}": name "${name}" must match ${VALID_ITEM_NAME.source} (letters, digits, "-", "_"; no path separators, "..", or code)`
16
+ );
17
+ }
18
+ const filesRaw = record.files;
19
+ if (!Array.isArray(filesRaw)) {
20
+ throw new TypeError(`registry.json for "${itemName}" is missing a "files" array`);
21
+ }
22
+ const files = filesRaw.map((entry, index) => {
23
+ if (typeof entry !== "object" || entry === null) {
24
+ throw new Error(`registry.json "${itemName}": files[${String(index)}] is not an object`);
25
+ }
26
+ const fileRecord = entry;
27
+ const { from } = fileRecord;
28
+ const { to } = fileRecord;
29
+ const { merge } = fileRecord;
30
+ if (typeof from !== "string" || typeof to !== "string") {
31
+ throw new TypeError(`registry.json "${itemName}": files[${String(index)}] needs string "from" and "to"`);
32
+ }
33
+ if (merge !== "create-or-skip" && merge !== "schema-extension") {
34
+ throw new Error(`registry.json "${itemName}": files[${String(index)}].merge must be "create-or-skip" or "schema-extension"`);
35
+ }
36
+ for (const [field, value] of [
37
+ ["from", from],
38
+ ["to", to]
39
+ ]) {
40
+ if (value.includes("..") || value.startsWith("/")) {
41
+ throw new Error(`registry.json "${itemName}": files[${String(index)}].${field} "${value}" must be a relative path without ".."`);
42
+ }
43
+ }
44
+ return { from, merge, to };
45
+ });
46
+ const asStringMap = (value) => typeof value === "object" && value !== null ? value : void 0;
47
+ const deps = asStringMap(record.deps);
48
+ const devDependencies = asStringMap(record.devDependencies);
49
+ const requires = Array.isArray(record.requires) ? record.requires.filter((value) => typeof value === "string") : void 0;
50
+ const bindings = Array.isArray(record.bindings) ? record.bindings.filter((value) => {
51
+ if (typeof value !== "object" || value === null) {
52
+ return false;
53
+ }
54
+ const bindingRecord = value;
55
+ return Array.isArray(bindingRecord.path) && bindingRecord.path.every((segment) => typeof segment === "string");
56
+ }) : void 0;
57
+ const envVariables = Array.isArray(record.envVars) ? record.envVars.filter(
58
+ (value) => typeof value === "object" && value !== null && typeof value.name === "string"
59
+ ).map((entry) => {
60
+ const hasValue = typeof entry.value === "string";
61
+ if (!VALID_ENV_NAME.test(entry.name)) {
62
+ throw new Error(
63
+ `registry.json "${itemName}": envVars["${entry.name}"].name must match ${VALID_ENV_NAME.source} (letters, digits, underscore; no "=" or newline)`
64
+ );
65
+ }
66
+ if (hasValue && NEWLINE_PRESENT.test(entry.value)) {
67
+ throw new Error(`registry.json "${itemName}": envVars["${entry.name}"].value must not contain a newline`);
68
+ }
69
+ if (typeof entry.description === "string" && NEWLINE_PRESENT.test(entry.description)) {
70
+ throw new Error(`registry.json "${itemName}": envVars["${entry.name}"].description must not contain a newline`);
71
+ }
72
+ return {
73
+ ...typeof entry.description === "string" ? { description: entry.description } : {},
74
+ name: entry.name,
75
+ // Default to secret unless a concrete value is provided.
76
+ secret: typeof entry.secret === "boolean" ? entry.secret : !hasValue,
77
+ ...hasValue ? { value: entry.value } : {}
78
+ };
79
+ }) : void 0;
80
+ return {
81
+ bindings,
82
+ deps,
83
+ description: typeof record.description === "string" ? record.description : void 0,
84
+ devDependencies,
85
+ docs: typeof record.docs === "string" ? record.docs : void 0,
86
+ envVars: envVariables,
87
+ files,
88
+ name,
89
+ requires,
90
+ title: typeof record.title === "string" ? record.title : void 0
91
+ };
92
+ };
93
+
94
+ export { parseManifest as default };
@@ -0,0 +1,16 @@
1
+ import { readLinkedProject } from '@lunora/config';
2
+
3
+ const resolveWorkerUrl = ({ cwd, url }) => {
4
+ if (url !== void 0 && url !== "") {
5
+ return url;
6
+ }
7
+ return readLinkedProject(cwd)?.workerUrl;
8
+ };
9
+ const resolveProductionWorkerUrl = ({ cwd, prod, url }) => {
10
+ if (url !== void 0 && url !== "") {
11
+ return url;
12
+ }
13
+ return prod ? readLinkedProject(cwd)?.workerUrl : void 0;
14
+ };
15
+
16
+ export { resolveProductionWorkerUrl as a, resolveWorkerUrl as r };
@@ -0,0 +1,4 @@
1
+ import 'node:fs';
2
+ import '@visulima/path';
3
+ export { r as runAddCommand, a as runBuildIndexCommand, e as runListCommand, b as runRegistryViewCommand } from './commands-DPKWlqqX.mjs';
4
+ import './buildRegistryIndex-BcYe607_.mjs';