@cmssy/cli 16.11.0 → 17.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +84 -30
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -655,7 +655,7 @@ function runAddBlock(options, deps) {
655
655
  log("Next steps:");
656
656
  log(` 1. edit ${layout.componentPath} - define the fields and markup`);
657
657
  log(
658
- ` 2. restart your dev server - the editor picks up "${names.type}" from the next handshake`
658
+ ` 2. save - the editor picks up "${names.type}" on the next hot reload; if the palette does not update, reload the editor`
659
659
  );
660
660
  return 0;
661
661
  } catch (error) {
@@ -2247,35 +2247,19 @@ function objectType(fields, depth) {
2247
2247
  if (!body.length) return "Record<string, unknown>";
2248
2248
  return ["{", ...body, `${INDENT.repeat(depth)}}`].join("\n");
2249
2249
  }
2250
- var PREAMBLE = `/** A translatable field: one string, or one per enabled language. */
2251
- export type CmssyLocalized = string | Record<string, string>;
2250
+ var PREAMBLE = `import type {
2251
+ CmssyLocalizedValue as CmssyLocalized,
2252
+ ResolvedMedia as CmssyMedia,
2253
+ FileFieldValue as CmssyFile,
2254
+ CmssyModelRecord as CmssyRecordOf,
2255
+ } from "@cmssy/core";
2252
2256
 
2253
- /**
2254
- * What a media field reads back. Mirrors \`ResolvedMedia\` in @cmssy/types.
2255
- * A single media field is \`CmssyMedia | null\`: a reference whose asset was
2256
- * deleted resolves to nothing. A gallery drops such entries instead.
2257
- */
2258
- export interface CmssyMedia {
2259
- id: string;
2260
- url: string | null;
2261
- visibility: "public" | "private";
2262
- alt?: string;
2263
- width?: number;
2264
- height?: number;
2257
+ export type { CmssyLocalized, CmssyMedia, CmssyFile, CmssyRecordOf };`;
2258
+ function preambleImports() {
2259
+ const clause = /import type \{([^}]*)\} from "@cmssy\/core";/.exec(PREAMBLE);
2260
+ if (!clause?.[1]) return [];
2261
+ return clause[1].split(",").map((entry) => entry.trim().split(/\s+as\s+/)[0]?.trim() ?? "").filter((name) => name.length > 0);
2265
2262
  }
2266
-
2267
- /** What a file field holds. Mirrors \`FileFieldValue\` in @cmssy/types. */
2268
- export type CmssyFile = string;
2269
-
2270
- /** A record as \`public.model.records\` returns it, with \`data\` typed. */
2271
- export interface CmssyRecordOf<Data> {
2272
- id: string;
2273
- modelId: string;
2274
- status: string;
2275
- createdAt: string;
2276
- updatedAt: string;
2277
- data: Data;
2278
- }`;
2279
2263
  function generateModelTypes(models, options) {
2280
2264
  const command = options.command ?? "npx @cmssy/cli types";
2281
2265
  const sorted = [...models].sort((a, b) => a.slug.localeCompare(b.slug));
@@ -2434,11 +2418,72 @@ async function request(deps, endpoint, query, variables) {
2434
2418
  if (!body.data) throw new CliError("the delivery API returned no data");
2435
2419
  return body.data;
2436
2420
  }
2421
+ function ancestors(from) {
2422
+ const chain = [];
2423
+ let dir = resolvePath(from);
2424
+ for (let steps = dir.split(/[\\/]/).length + 1; steps > 0; steps -= 1) {
2425
+ chain.push(dir);
2426
+ const parent = dirname4(dir);
2427
+ if (parent === dir) break;
2428
+ dir = parent;
2429
+ }
2430
+ return chain;
2431
+ }
2432
+ function exportedNames(declarations) {
2433
+ const source = declarations.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/^\s*\/\/.*$/gm, " ");
2434
+ const found = /* @__PURE__ */ new Set();
2435
+ for (const [, clause] of source.matchAll(/export\s+(?:type\s+)?\{([^}]*)\}/g)) {
2436
+ for (const entry of (clause ?? "").split(",")) {
2437
+ const parts = entry.trim().split(/\s+as\s+/);
2438
+ const name = (parts[1] ?? parts[0] ?? "").trim();
2439
+ if (name.length > 0) found.add(name);
2440
+ }
2441
+ }
2442
+ const declared = /export\s+(?:declare\s+)?(?:abstract\s+)?(?:type|interface|class|const|let|var|function|enum|namespace)\s+(\w+)/g;
2443
+ for (const [, name] of source.matchAll(declared)) {
2444
+ if (name !== void 0) found.add(name);
2445
+ }
2446
+ return found;
2447
+ }
2448
+ function coreShapeGap(cwd) {
2449
+ const names = preambleImports();
2450
+ if (names.length === 0) return null;
2451
+ let manifest = null;
2452
+ let manifestPath = "";
2453
+ for (const dir of ancestors(cwd)) {
2454
+ const candidate = resolvePath(dir, "node_modules/@cmssy/core/package.json");
2455
+ try {
2456
+ manifest = JSON.parse(readFileSync8(candidate, "utf8"));
2457
+ manifestPath = candidate;
2458
+ break;
2459
+ } catch {
2460
+ manifest = null;
2461
+ }
2462
+ }
2463
+ if (!manifest) return null;
2464
+ let declarations;
2465
+ try {
2466
+ declarations = readFileSync8(
2467
+ resolvePath(dirname4(manifestPath), manifest.types ?? "dist/index.d.ts"),
2468
+ "utf8"
2469
+ );
2470
+ } catch {
2471
+ return null;
2472
+ }
2473
+ const exported = exportedNames(declarations);
2474
+ const missing = names.filter((name) => !exported.has(name));
2475
+ return missing.length > 0 ? { version: manifest.version ?? "unknown", missing } : null;
2476
+ }
2437
2477
  function describeDrift(previous, next) {
2438
2478
  const declared = (source, pattern) => new Set([...source.matchAll(pattern)].map((match) => match[1] ?? ""));
2439
- const body = (source) => source.split("/** Every model in the")[0] ?? source;
2440
2479
  const models = /export interface (\w+)Data/g;
2441
2480
  const fields = /^\s{2}(\w+)\??:/gm;
2481
+ const body = (source) => {
2482
+ const head = source.search(/export interface \w+Data\b/);
2483
+ if (head === -1) return "";
2484
+ const scoped = source.slice(head);
2485
+ return scoped.split("/** Every model in the")[0] ?? scoped;
2486
+ };
2442
2487
  const lines = [];
2443
2488
  const report = (label, before, after) => {
2444
2489
  const added = [...after].filter((name) => !before.has(name));
@@ -2446,7 +2491,7 @@ function describeDrift(previous, next) {
2446
2491
  if (added.length) lines.push(`+ ${label}: ${added.join(", ")}`);
2447
2492
  if (removed.length) lines.push(`- ${label}: ${removed.join(", ")}`);
2448
2493
  };
2449
- report("models", declared(previous, models), declared(next, models));
2494
+ report("models", declared(body(previous), models), declared(body(next), models));
2450
2495
  report("fields", declared(body(previous), fields), declared(body(next), fields));
2451
2496
  return lines.length ? lines : ["the generated output differs"];
2452
2497
  }
@@ -2578,6 +2623,15 @@ async function runTypes(options, deps) {
2578
2623
  const inside = relative(deps.cwd, outPath);
2579
2624
  const shown = inside && !inside.startsWith("..") ? inside : outPath;
2580
2625
  const source = generateModelTypes(models, { workspace });
2626
+ const gap = coreShapeGap(deps.cwd);
2627
+ if (gap) {
2628
+ deps.log(
2629
+ `cmssy: the installed @cmssy/core ${gap.version} does not export ${gap.missing.join(", ")}`
2630
+ );
2631
+ deps.log(
2632
+ ` ${shown} imports those shapes - upgrade @cmssy/core to ${cliVersion()} or newer`
2633
+ );
2634
+ }
2581
2635
  let previous = null;
2582
2636
  try {
2583
2637
  previous = readFileSync8(outPath, "utf8");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmssy/cli",
3
- "version": "16.11.0",
3
+ "version": "17.0.0",
4
4
  "description": "The cmssy CLI: `cmssy init` wires cmssy into an existing Next.js, Astro or React Router app; `cmssy link` connects it to a workspace and verifies the editor wiring.",
5
5
  "keywords": [
6
6
  "cmssy",
@@ -28,7 +28,7 @@
28
28
  ],
29
29
  "dependencies": {
30
30
  "esbuild": "^0.27.7",
31
- "@cmssy/core": "16.11.0"
31
+ "@cmssy/core": "17.0.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/node": "^20.0.0",