@lunora/cli 1.0.0-alpha.3 → 1.0.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -16,4 +16,4 @@ export { createRecordingSpawner, defaultSpawner } from './packem_shared/defaultS
16
16
  export { default as parseManifest } from './packem_shared/parseManifest--vZf2FY1.mjs';
17
17
  export { REQUIRED_COMPATIBILITY_DATE, REQUIRED_FLAG, validateWranglerProject as validateWrangler, validateWranglerConfig } from '@lunora/config';
18
18
  export { buildRegistryIndex } from './packem_shared/buildRegistryIndex-BcYe607_.mjs';
19
- export { r as runAddCommand, a as runBuildIndexCommand, b as runRegistryViewCommand } from './packem_shared/commands-DIQ3nf0C.mjs';
19
+ export { r as runAddCommand, a as runBuildIndexCommand, b as runRegistryViewCommand } from './packem_shared/commands-SUPdjsu5.mjs';
@@ -3,7 +3,7 @@ import { findWranglerFile, promptSelect } from '@lunora/config';
3
3
  import { join } from '@visulima/path';
4
4
  import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
5
5
  import { n as normalizeFeature, E as EMAIL_ITEM, D as DEFAULT_AUTH_ITEM, p as promptAuthProvider, A as AUTH_PROVIDER_OPTIONS } from '../packem_shared/features-ocSSpZtS.mjs';
6
- import { r as runAddCommand } from '../packem_shared/commands-DIQ3nf0C.mjs';
6
+ import { r as runAddCommand } from '../packem_shared/commands-SUPdjsu5.mjs';
7
7
 
8
8
  const providerToItem = (provider) => {
9
9
  const value = provider.trim().toLowerCase();
@@ -1,5 +1,5 @@
1
1
  import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
2
- import { r as runAddCommand, b as runRegistryViewCommand, a as runBuildIndexCommand } from '../packem_shared/commands-DIQ3nf0C.mjs';
2
+ import { r as runAddCommand, b as runRegistryViewCommand, a as runBuildIndexCommand } from '../packem_shared/commands-SUPdjsu5.mjs';
3
3
 
4
4
  const execute = defineHandler(({ argument, cwd, logger, options }) => {
5
5
  const subcommand = argument[0];
@@ -2,13 +2,14 @@ import { existsSync, mkdirSync, writeFileSync, readdirSync, mkdtempSync, rmSync,
2
2
  import { tmpdir } from 'node:os';
3
3
  import { detectFramework as detectFramework$1, isInteractive, promptSelect, promptMultiSelect } from '@lunora/config';
4
4
  import { walkSync } from '@visulima/fs';
5
- import { resolve, join as join$1, relative, dirname as dirname$1 } from '@visulima/path';
5
+ import { resolve, join as join$1, relative, dirname as dirname$1, basename } from '@visulima/path';
6
6
  import { downloadTemplate } from 'giget';
7
+ import { modify, applyEdits } from 'jsonc-parser';
7
8
  import { join, dirname } from 'node:path';
8
9
  import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
9
10
  import MagicString from 'magic-string';
10
11
  import { Project, SyntaxKind } from 'ts-morph';
11
- import { c as resolveSourceRef, r as runAddCommand } from '../packem_shared/commands-DIQ3nf0C.mjs';
12
+ import { c as resolveSourceRef, d as resolveDistTag, r as runAddCommand } from '../packem_shared/commands-SUPdjsu5.mjs';
12
13
  import { p as promptAuthProvider, E as EMAIL_ITEM } from '../packem_shared/features-ocSSpZtS.mjs';
13
14
 
14
15
  const GITHUB_CONTENT = `name: Deploy
@@ -19,6 +20,10 @@ on:
19
20
  pull_request:
20
21
  workflow_dispatch:
21
22
 
23
+ # Prerequisite: commit your pnpm-lock.yaml. \`pnpm install --frozen-lockfile\`
24
+ # (below) and the pnpm cache both require it — run \`pnpm install\` locally and
25
+ # commit the lockfile before pushing, or the first CI run fails.
26
+ #
22
27
  # Set these repository secrets (Settings → Secrets and variables → Actions):
23
28
  # CLOUDFLARE_API_TOKEN — a Workers-scoped API token
24
29
  # CLOUDFLARE_ACCOUNT_ID — your Cloudflare account id
@@ -65,6 +70,10 @@ jobs:
65
70
  const GITLAB_CONTENT = `stages:
66
71
  - deploy
67
72
 
73
+ # Prerequisite: commit your pnpm-lock.yaml. \`pnpm install --frozen-lockfile\`
74
+ # (below) requires it — run \`pnpm install\` locally and commit the lockfile
75
+ # before pushing, or the first pipeline fails.
76
+ #
68
77
  # Set these as masked CI/CD variables (Settings → CI/CD → Variables):
69
78
  # CLOUDFLARE_API_TOKEN — a Workers-scoped API token
70
79
  # CLOUDFLARE_ACCOUNT_ID — your Cloudflare account id
@@ -126,6 +135,9 @@ const scaffoldCiWorkflow = (projectRoot, provider, logger, options = {}) => {
126
135
  } else {
127
136
  logger.success(`--ci ${provider}: wrote ${spec.file}`);
128
137
  logger.info(`--ci ${provider}: set CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID as ${spec.secretsHint} to enable deploys.`);
138
+ logger.info(
139
+ `--ci ${provider}: run \`pnpm install\` and commit pnpm-lock.yaml before pushing — the pipeline runs \`pnpm install --frozen-lockfile\`.`
140
+ );
129
141
  }
130
142
  return result;
131
143
  } catch (error) {
@@ -331,6 +343,26 @@ const isTextFile = (filePath) => {
331
343
  return TEXT_EXTENSIONS.has(filePath.slice(lastDot));
332
344
  };
333
345
  const substitute = (content, name) => content.replaceAll("{{name}}", name);
346
+ const isLunoraDep = (name) => name === "lunorash" || name.startsWith("@lunora/");
347
+ const stampLunoraDeps = (packageJsonText, distTag) => {
348
+ let parsed;
349
+ try {
350
+ parsed = JSON.parse(packageJsonText);
351
+ } catch {
352
+ return packageJsonText;
353
+ }
354
+ let text = packageJsonText;
355
+ for (const section of ["dependencies", "devDependencies"]) {
356
+ for (const name of Object.keys(parsed[section] ?? {})) {
357
+ if (!isLunoraDep(name)) {
358
+ continue;
359
+ }
360
+ const edits = modify(text, [section, name], distTag, { formattingOptions: { insertSpaces: true, tabSize: 4 } });
361
+ text = applyEdits(text, edits);
362
+ }
363
+ }
364
+ return text;
365
+ };
334
366
  const collectFiles = (directory) => {
335
367
  const out = [];
336
368
  for (const entry of walkSync(directory, { includeDirs: false, includeFiles: true })) {
@@ -341,12 +373,16 @@ const collectFiles = (directory) => {
341
373
  const copyTemplate = (sourceDirectory, target, name) => {
342
374
  const files = collectFiles(sourceDirectory);
343
375
  const written = [];
376
+ const distTag = resolveDistTag();
344
377
  for (const source of files) {
345
378
  const relativePath = relative(sourceDirectory, source);
346
379
  const destination = join$1(target, relativePath);
347
380
  mkdirSync(dirname$1(destination), { recursive: true });
348
381
  const raw = readFileSync(source);
349
- const text = isTextFile(source) ? substitute(raw.toString("utf8"), name) : void 0;
382
+ let text = isTextFile(source) ? substitute(raw.toString("utf8"), name) : void 0;
383
+ if (text !== void 0 && basename(source) === "package.json") {
384
+ text = stampLunoraDeps(text, distTag);
385
+ }
350
386
  if (text === void 0) {
351
387
  writeFileSync(destination, raw);
352
388
  } else {
@@ -2,12 +2,12 @@ import { existsSync, readFileSync, writeFileSync, mkdirSync, mkdtempSync, rmSync
2
2
  import { dirname, join } from '@visulima/path';
3
3
  import { DEV_VARS_FILE, parseDevVariableEntries, promptYesNo } from '@lunora/config';
4
4
  import { modify, applyEdits, parse } from 'jsonc-parser';
5
+ import { fileURLToPath } from 'node:url';
5
6
  import { collectCatalog, buildRegistryIndex } from './buildRegistryIndex-BcYe607_.mjs';
6
7
  import { insertSchemaExtension } from './insertSchemaExtension-BuzF6-t2.mjs';
7
8
  import { createHash } from 'node:crypto';
8
9
  import { tmpdir } from 'node:os';
9
10
  import { downloadTemplate } from 'giget';
10
- import { fileURLToPath } from 'node:url';
11
11
  import parseManifest from './parseManifest--vZf2FY1.mjs';
12
12
 
13
13
  const DEFAULT_SOURCE_REF_FALLBACK = "alpha";
@@ -59,8 +59,38 @@ const resolveSourceRef = (ref) => {
59
59
  }
60
60
  return resolveVersionRef(resolveCliVersion());
61
61
  };
62
+ const STABLE_DIST_TAG = "latest";
63
+ const resolveDistTag = () => {
64
+ const ref = resolveVersionRef(resolveCliVersion());
65
+ return ref === STABLE_BRANCH ? STABLE_DIST_TAG : ref;
66
+ };
62
67
 
63
- const applyDeps = (deps, projectRoot, logger, section = "dependencies") => {
68
+ const resolveDepRange = (range) => {
69
+ if (!range.startsWith("workspace:")) {
70
+ return range;
71
+ }
72
+ const rest = range.slice("workspace:".length);
73
+ if (rest === "" || rest === "*" || rest === "^" || rest === "~") {
74
+ return resolveDistTag();
75
+ }
76
+ return rest;
77
+ };
78
+ const UMBRELLA_REEXPORTED_DEPS = /* @__PURE__ */ new Set(["@lunora/client", "@lunora/do", "@lunora/runtime", "@lunora/server", "@lunora/values"]);
79
+ const UMBRELLA_IMPORT_RE = /(['"])@lunora\/(client|do|runtime|server|values)(\/[^'"]*)?\1/gu;
80
+ const projectUsesUmbrella = (projectRoot) => {
81
+ const packageJsonPath = join(projectRoot, "package.json");
82
+ if (!existsSync(packageJsonPath)) {
83
+ return false;
84
+ }
85
+ try {
86
+ const parsed = JSON.parse(readFileSync(packageJsonPath, "utf8"));
87
+ return parsed.dependencies?.lunorash !== void 0 || parsed.devDependencies?.lunorash !== void 0;
88
+ } catch {
89
+ return false;
90
+ }
91
+ };
92
+ const rewriteUmbrellaImports = (source) => source.replaceAll(UMBRELLA_IMPORT_RE, (_match, quote, base, subpath) => `${quote}lunorash/${base}${subpath ?? ""}${quote}`);
93
+ const applyDeps = (deps, projectRoot, logger, section = "dependencies", useUmbrella = false) => {
64
94
  const entries = Object.entries(deps);
65
95
  if (entries.length === 0) {
66
96
  return [];
@@ -74,11 +104,15 @@ const applyDeps = (deps, projectRoot, logger, section = "dependencies") => {
74
104
  const parsed = JSON.parse(text);
75
105
  const added = [];
76
106
  for (const [name, range] of entries) {
107
+ if (useUmbrella && UMBRELLA_REEXPORTED_DEPS.has(name)) {
108
+ logger.info(`dep provided by the lunorash umbrella, skipping: ${name}`);
109
+ continue;
110
+ }
77
111
  if (parsed.dependencies?.[name] !== void 0 || parsed.devDependencies?.[name] !== void 0) {
78
112
  logger.info(`dep already present: ${name}`);
79
113
  continue;
80
114
  }
81
- const edits = modify(text, [section, name], range, {
115
+ const edits = modify(text, [section, name], resolveDepRange(range), {
82
116
  formattingOptions: { insertSpaces: true, tabSize: 4 }
83
117
  });
84
118
  text = applyEdits(text, edits);
@@ -199,14 +233,14 @@ const applyBindings = (bindings, projectRoot, logger) => {
199
233
  }
200
234
  return applied;
201
235
  };
202
- const applyItemResources = (manifest, cwd, logger) => {
236
+ const applyItemResources = (manifest, cwd, logger, useUmbrella = false) => {
203
237
  const deps = [];
204
238
  const bindings = [];
205
239
  if (manifest.deps) {
206
- deps.push(...applyDeps(manifest.deps, cwd, logger));
240
+ deps.push(...applyDeps(manifest.deps, cwd, logger, "dependencies", useUmbrella));
207
241
  }
208
242
  if (manifest.devDependencies) {
209
- deps.push(...applyDeps(manifest.devDependencies, cwd, logger, "devDependencies"));
243
+ deps.push(...applyDeps(manifest.devDependencies, cwd, logger, "devDependencies", useUmbrella));
210
244
  }
211
245
  if (manifest.bindings) {
212
246
  bindings.push(...applyBindings(manifest.bindings, cwd, logger));
@@ -318,7 +352,12 @@ const renderDiff = (oldText, newText) => {
318
352
  return out;
319
353
  };
320
354
 
321
- const reconcileSchemaExtension = (file, itemKey, itemDirectory, projectRoot, logger, diff) => {
355
+ const CODE_FILE_RE = /\.[cm]?[jt]sx?$/u;
356
+ const readItemFile = (itemDirectory, file, useUmbrella) => {
357
+ const source = readFileSync(join(itemDirectory, file.from), "utf8");
358
+ return useUmbrella && CODE_FILE_RE.test(file.to) ? rewriteUmbrellaImports(source) : source;
359
+ };
360
+ const reconcileSchemaExtension = (file, itemKey, itemDirectory, projectRoot, logger, diff, useUmbrella) => {
322
361
  const schemaPath = join(projectRoot, "lunora", "schema.ts");
323
362
  if (diff) {
324
363
  logger.info(`~ would merge .extend(${itemKey}.extension) into lunora/schema.ts (and create ${file.to} if absent)`);
@@ -327,9 +366,13 @@ const reconcileSchemaExtension = (file, itemKey, itemDirectory, projectRoot, log
327
366
  const destinationPath = join(projectRoot, file.to);
328
367
  if (!existsSync(destinationPath)) {
329
368
  mkdirSync(dirname(destinationPath), { recursive: true });
330
- writeFileSync(destinationPath, readFileSync(join(itemDirectory, file.from), "utf8"), "utf8");
369
+ writeFileSync(destinationPath, readItemFile(itemDirectory, file, useUmbrella), "utf8");
331
370
  }
332
- const existingSchema = existsSync(schemaPath) ? readFileSync(schemaPath, "utf8") : 'import { defineSchema } from "@lunora/server";\n\nexport const schema = defineSchema({});\n';
371
+ const baseModule = useUmbrella ? "lunorash/server" : "@lunora/server";
372
+ const existingSchema = existsSync(schemaPath) ? readFileSync(schemaPath, "utf8") : `import { defineSchema } from "${baseModule}";
373
+
374
+ export const schema = defineSchema({});
375
+ `;
333
376
  const result = insertSchemaExtension(existingSchema, itemKey);
334
377
  if (result.ok) {
335
378
  mkdirSync(dirname(schemaPath), { recursive: true });
@@ -359,9 +402,9 @@ const previewWholeFile = (file, current, incoming, exists, logger) => {
359
402
  logger.info(` ${line}`);
360
403
  }
361
404
  };
362
- const reconcileWholeFile = (file, itemKey, itemDirectory, projectRoot, logger, lock, reconcileOptions) => {
405
+ const reconcileWholeFile = (file, itemKey, itemDirectory, projectRoot, logger, lock, reconcileOptions, useUmbrella) => {
363
406
  const destinationPath = join(projectRoot, file.to);
364
- const incoming = readFileSync(join(itemDirectory, file.from), "utf8");
407
+ const incoming = readItemFile(itemDirectory, file, useUmbrella);
365
408
  const exists = existsSync(destinationPath);
366
409
  const current = exists ? readFileSync(destinationPath, "utf8") : "";
367
410
  const write = (message) => {
@@ -399,11 +442,11 @@ const reconcileWholeFile = (file, itemKey, itemDirectory, projectRoot, logger, l
399
442
  logger.warn(`conflict: ${file.to} has local edits and an upstream update — wrote ${file.to}.new (use --overwrite to take theirs)`);
400
443
  return { kind: "skipped", path: destinationPath };
401
444
  };
402
- const reconcileFile = (file, itemKey, itemDirectory, projectRoot, logger, lock, reconcileOptions = {}) => {
445
+ const reconcileFile = (file, itemKey, itemDirectory, projectRoot, logger, lock, reconcileOptions = {}, useUmbrella = false) => {
403
446
  if (file.merge === "schema-extension") {
404
- return reconcileSchemaExtension(file, itemKey, itemDirectory, projectRoot, logger, reconcileOptions.diff === true);
447
+ return reconcileSchemaExtension(file, itemKey, itemDirectory, projectRoot, logger, reconcileOptions.diff === true, useUmbrella);
405
448
  }
406
- return reconcileWholeFile(file, itemKey, itemDirectory, projectRoot, logger, lock, reconcileOptions);
449
+ return reconcileWholeFile(file, itemKey, itemDirectory, projectRoot, logger, lock, reconcileOptions, useUmbrella);
407
450
  };
408
451
  const reconcileItems = (items, cwd, logger, reconcileOptions = {}) => {
409
452
  const written = [];
@@ -411,15 +454,16 @@ const reconcileItems = (items, cwd, logger, reconcileOptions = {}) => {
411
454
  const depsAdded = [];
412
455
  const bindingsApplied = [];
413
456
  const lock = readLock(cwd);
457
+ const useUmbrella = projectUsesUmbrella(cwd);
414
458
  for (const { directory, manifest } of items) {
415
459
  for (const file of manifest.files) {
416
- const outcome = reconcileFile(file, manifest.name, directory, cwd, logger, lock, reconcileOptions);
460
+ const outcome = reconcileFile(file, manifest.name, directory, cwd, logger, lock, reconcileOptions, useUmbrella);
417
461
  (outcome.kind === "written" ? written : skipped).push(outcome.path);
418
462
  }
419
463
  if (reconcileOptions.diff) {
420
464
  continue;
421
465
  }
422
- const applied = applyItemResources(manifest, cwd, logger);
466
+ const applied = applyItemResources(manifest, cwd, logger, useUmbrella);
423
467
  depsAdded.push(...applied.deps);
424
468
  bindingsApplied.push(...applied.bindings);
425
469
  }
@@ -740,4 +784,4 @@ const runBuildIndexCommand = async (options) => {
740
784
  return empty;
741
785
  };
742
786
 
743
- export { runBuildIndexCommand as a, runRegistryViewCommand as b, resolveSourceRef as c, runListCommand as d, runAddCommand as r };
787
+ export { runBuildIndexCommand as a, runRegistryViewCommand as b, resolveSourceRef as c, resolveDistTag as d, runListCommand as e, runAddCommand 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-SUPdjsu5.mjs';
4
+ import './buildRegistryIndex-BcYe607_.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/cli",
3
- "version": "1.0.0-alpha.3",
3
+ "version": "1.0.0-alpha.4",
4
4
  "description": "The Lunora CLI: init, dev, deploy, codegen, run, reset, and migrate commands",
5
5
  "keywords": [
6
6
  "agent-skills",
@@ -1,4 +0,0 @@
1
- import 'node:fs';
2
- import '@visulima/path';
3
- export { r as runAddCommand, a as runBuildIndexCommand, d as runListCommand, b as runRegistryViewCommand } from './commands-DIQ3nf0C.mjs';
4
- import './buildRegistryIndex-BcYe607_.mjs';