@elytracms/cli 0.0.87 → 0.0.88

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elytracms/cli",
3
3
  "author": "finaldream <hi@finaldream.de>",
4
- "version": "0.0.87",
4
+ "version": "0.0.88",
5
5
  "description": "The Elytra CLI — run a consuming repo's studio, frontend, and Convex backend under one command.",
6
6
  "license": "MIT",
7
7
  "type": "module",
@@ -17,7 +17,7 @@
17
17
  "node": ">=22.18.0"
18
18
  },
19
19
  "dependencies": {
20
- "@elytracms/core": "^0.0.87"
20
+ "@elytracms/core": "^0.0.88"
21
21
  },
22
22
  "devDependencies": {
23
23
  "vitest": "^4.1.10"
package/src/cli.js CHANGED
@@ -117,7 +117,9 @@ and are checked in the studio), and reports the generated-previews artifacts
117
117
  (manifest/files present; hash staleness lives in \`elytra previews --check\`).
118
118
  With studio/ deps installed, config validity is checked for real: doctor
119
119
  executes elytra.config.ts through your studio's own Vite (takes a few
120
- seconds); without deps it stays an honest "unknown" with the reason.
120
+ seconds); without deps it stays an honest "unknown" with the reason. With
121
+ the config resolved, it also reads the public delivery snapshot and warns
122
+ about published documents whose collection the config does not declare.
121
123
  Exit non-zero if any check fails. Read-only.
122
124
 
123
125
  Options:
package/src/doctor.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { readFileSync } from 'node:fs'
2
2
  import { createRequire } from 'node:module'
3
3
  import { dirname, join } from 'node:path'
4
+ import { orphanedCollections } from '@elytracms/core/cms-core'
4
5
  import {
5
6
  REVALIDATE_ENV_ENDPOINT,
6
7
  REVALIDATE_ENV_SECRET,
@@ -8,6 +9,7 @@ import {
8
9
  checkDeploymentReady,
9
10
  expectedDeploymentEnv,
10
11
  probeAuthSigningKeys,
12
+ probeDeliverySnapshot,
11
13
  probeDeployedFunctionsVersion,
12
14
  probeRevalidateEndpoint,
13
15
  } from '@elytracms/core/export-sync/deployment-ready'
@@ -201,7 +203,7 @@ export function configFactsFromOutcome(outcome) {
201
203
  issueCount: issues.length,
202
204
  ...(firstIssue ? { firstIssue: formatConfigIssue(firstIssue) } : {}),
203
205
  }
204
- const project = valid ? result.projects?.[0] : undefined
206
+ const project = resolvedProjectOf(outcome)
205
207
  const collections = project?.collections
206
208
  // EC-524: the `componentIds` fact was gathered here for EC-508's presentation
207
209
  // check, which resolved a collection's declared `presentation.wrapper` against
@@ -216,6 +218,60 @@ export function configFactsFromOutcome(outcome) {
216
218
  }
217
219
  }
218
220
 
221
+ /**
222
+ * The project a resolved config outcome describes — the first one, the same choice
223
+ * {@link configFactsFromOutcome} makes for `collections` — or `undefined` when the
224
+ * config did not resolve validly (or was never evaluated). Pure.
225
+ */
226
+ export function resolvedProjectOf(outcome) {
227
+ if (!outcome || outcome.kind !== 'resolved' || outcome.result?.ok !== true) return undefined
228
+ return outcome.result.projects?.[0]
229
+ }
230
+
231
+ // --- Published orphans (EC-606) -----------------------------------------------------
232
+ //
233
+ // A published document whose collection the config does not declare is still on the
234
+ // public delivery snapshot, but no route or query addresses it and the studio hides it.
235
+ // The doctor reads the snapshot (public, published-only — what every visitor receives)
236
+ // and runs core's ONE counter (`orphanedCollections`, the same one the studio's session
237
+ // hydration runs) against the resolved config's collection ids.
238
+
239
+ /**
240
+ * Gather the `publishedOrphans` fact for a convex target, or `undefined` for any other
241
+ * target (no deployment, no snapshot). Without a declared set — the config did not
242
+ * resolve here — the fact is an honest `unknown` carrying why, and no request is made.
243
+ * `project` is {@link resolvedProjectOf}'s answer; `configFacts` is what
244
+ * {@link configFactsFromOutcome} returned (for the reason text).
245
+ */
246
+ export async function gatherPublishedOrphans({
247
+ target,
248
+ project,
249
+ configFacts = {},
250
+ fetchImpl = fetch,
251
+ probe = probeDeliverySnapshot,
252
+ } = {}) {
253
+ if (target?.kind !== 'convex') return undefined
254
+ if (!project || !Array.isArray(project.collections)) {
255
+ const reason =
256
+ configFacts.config !== undefined && !configFacts.config.valid
257
+ ? 'fix the workspace config issues first (see config-valid).'
258
+ : (configFacts.configUnavailableDetail ?? 'elytra.config.ts was not evaluated here.')
259
+ return {
260
+ status: 'unknown',
261
+ detail: `Published documents were not checked against the declared collections — ${reason}`,
262
+ }
263
+ }
264
+ const snapshot = await probe(target.url, project.slug, fetchImpl)
265
+ if (snapshot.status !== 'answered') return { status: 'unknown', detail: snapshot.detail }
266
+ return {
267
+ status: 'counted',
268
+ orphans: orphanedCollections(
269
+ snapshot.collections.map((collection) => ({ collection, published: true })),
270
+ project.collections.map((collection) => collection.id),
271
+ ),
272
+ }
273
+ }
274
+
219
275
  // --- Deployment env-var presence (EC-399) --------------------------------------
220
276
  //
221
277
  // The Convex deployment's own env (`npx convex env set …`) lives in NO file a
@@ -441,6 +497,11 @@ export function formatReport(report) {
441
497
  * version and the `functions-version` check gets a real answer; otherwise the fact is
442
498
  * omitted → an honest `unknown`. Resolution stays at the entry point so this function
443
499
  * remains pure/injected like every other gatherer here.
500
+ *
501
+ * EC-606: against a convex target the `app` bucket also reports PUBLISHED ORPHANS —
502
+ * published documents whose collection the resolved config does not declare — read
503
+ * from the public delivery snapshot ({@link gatherPublishedOrphans}). A `warn` at most,
504
+ * so the exit code never changes; without a resolved config it is an honest `unknown`.
444
505
  */
445
506
  export async function runDoctor({
446
507
  env = {},
@@ -491,12 +552,23 @@ export async function runDoctor({
491
552
  // (announced by the harness itself — the boot costs seconds). Merged BEFORE the
492
553
  // contract composes the report so `config-valid`/`asset-collection` are real checks.
493
554
  let configFacts = {}
555
+ let outcome
494
556
  if (configOutcome !== undefined || root !== undefined) {
495
- const outcome = configOutcome ?? (await executeWorkspaceConfig({ root, studioDir, log }))
557
+ outcome = configOutcome ?? (await executeWorkspaceConfig({ root, studioDir, log }))
496
558
  configFacts = configFactsFromOutcome(outcome)
497
559
  Object.assign(facts, configFacts)
498
560
  }
499
561
 
562
+ // EC-606: published documents of undeclared collections — one more request on the
563
+ // same public query API, made only when the config resolved (it needs the declared set).
564
+ const publishedOrphans = await gatherPublishedOrphans({
565
+ target,
566
+ project: resolvedProjectOf(outcome),
567
+ configFacts,
568
+ fetchImpl,
569
+ })
570
+ if (publishedOrphans) facts.publishedOrphans = publishedOrphans
571
+
500
572
  const report = checkDeploymentReady(facts)
501
573
 
502
574
  // EC-440: the previews bucket (repo artifacts, backend-independent). Appended to