content-query 0.5.1 → 0.6.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.
package/README.md CHANGED
@@ -78,6 +78,12 @@ An entry imports only its own collection's data, so a route that imports `posts`
78
78
 
79
79
  Queries return only the data they select, not even `id` implicitly. A query must select fields, include a relationship, request a payload, or select adjacent entries; otherwise it fails to type-check and throws an `invalid-query` error. Counts need no selection.
80
80
 
81
+ Later `select()` calls add fields, so a shared builder such as `const postLink = posts.select("id", "title")` can become `postLink.select("publishedAt")` where a page needs more. For detail pages, `posts.byId(id)` starts a query for one entry, and awaiting it returns that entry or `undefined`:
82
+
83
+ ```ts
84
+ const post = await posts.byId("hello").select("title").withPayload("_rendered");
85
+ ```
86
+
81
87
  To follow a relationship, pass its target collection to `using()`. Nothing related loads implicitly:
82
88
 
83
89
  ```ts
@@ -1,7 +1,7 @@
1
- import { a as InvalidRelationships, r as CollectionHandles, s as ContentError } from "./collection-types-DhIxnCSM.mjs";
1
+ import { a as InvalidRelationships, r as CollectionHandles, s as ContentError } from "./collection-types-Ch4YLG8t.mjs";
2
2
  import { i as CollectionRegistry } from "./types-WOsFmNnN.mjs";
3
- import { n as QueryStyle } from "./query-style-C2FcT1f9.mjs";
4
- import "./revision-aqGUf6YV.mjs";
3
+ import { n as QueryStyle } from "./query-style-k8Qd7wh5.mjs";
4
+ import "./revision-vhe9gRyk.mjs";
5
5
  //#region src/preparation/session.d.ts
6
6
  /** A local source captured by a successfully prepared content revision. */
7
7
  type ContentSourceEntry = Readonly<{
@@ -4,7 +4,7 @@ import { i as detach, l as valuePath, o as encode } from "./values-BbTV4QT-.mjs"
4
4
  import { t as assertServerOnly } from "./server-only-CIJUlyL-.mjs";
5
5
  import { n as loadFile } from "./file-DSOcdFj0.mjs";
6
6
  import { I as collectionNames } from "./operators-D-w1-gDg.mjs";
7
- import { a as variantKey, c as CODEC_VERSION, i as isQueryStyle, n as createCollections, o as isRelationship, s as prepareRelationships } from "./collection-Cb5eWGnv.mjs";
7
+ import { a as variantKey, c as CODEC_VERSION, i as isQueryStyle, n as createCollections, o as isRelationship, s as prepareRelationships } from "./collection-txOFpidL.mjs";
8
8
  import { t as renderMarkdown } from "./markdown-CHvBBJNa.mjs";
9
9
  import { access, link, mkdir, readFile, readdir, realpath, rm, rmdir, stat, writeFile } from "node:fs/promises";
10
10
  import path from "node:path";
package/dist/build.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as ContentSourceEvent, i as ContentSourceEntry, n as buildContent, o as ContentSourceSnapshot, r as ContentSession, t as BuildContentOptions } from "./build-Aoi7Unm0.mjs";
1
+ import { a as ContentSourceEvent, i as ContentSourceEntry, n as buildContent, o as ContentSourceSnapshot, r as ContentSession, t as BuildContentOptions } from "./build-DE4ZKHZR.mjs";
2
2
  export { type BuildContentOptions, type ContentSession, type ContentSourceEntry, type ContentSourceEvent, type ContentSourceSnapshot, buildContent };
package/dist/build.mjs CHANGED
@@ -1 +1 @@
1
- export { n as buildContent } from "./build-CIw4yWs9.mjs";
1
+ export { n as buildContent } from "./build-DYw3z9ju.mjs";
package/dist/cli.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { o as payloadIdentity, t as ContentError } from "./errors-Bfd0R4_P.mjs";
3
- import { t as buildAtRoot } from "./build-CIw4yWs9.mjs";
3
+ import { t as buildAtRoot } from "./build-DYw3z9ju.mjs";
4
4
  import { cac } from "cac";
5
5
  //#region package.json
6
- var version = "0.5.1";
6
+ var version = "0.6.0";
7
7
  //#endregion
8
8
  //#region src/integrations/cli/arguments.ts
9
9
  /** Validates one path option after CAC's automatic numeric conversion. */
@@ -481,7 +481,10 @@ function objectPlan(registry, collection, options, many = true, allowAdjacent =
481
481
  //#region src/query/builder.ts
482
482
  const planSymbol = Symbol("content-query.query-plan");
483
483
  const scopeSymbol = Symbol("content-query.query-scope");
484
- /** Creates immutable root or relationship builders over one backend-independent plan. */
484
+ /**
485
+ * Creates immutable root or relationship builders over one backend-independent plan. A root
486
+ * builder that is not `many` is a `byId()` query, which returns one entry or `undefined`.
487
+ */
485
488
  function createBuilder(registry, run, plan, many = true, root = true, scopeToken = {}, allowAdjacent = true, allowPagination = true) {
486
489
  const scope = registry[plan.collection];
487
490
  if (!scope) invalidQuery(`Unknown collection "${plan.collection}".`);
@@ -492,9 +495,12 @@ function createBuilder(registry, run, plan, many = true, root = true, scopeToken
492
495
  const builder = {
493
496
  [planSymbol]: plan,
494
497
  [scopeSymbol]: scopeToken,
498
+ /** Selects fields, adding to earlier selections; a field selected again is kept once. */
495
499
  select(...fields) {
496
- if (plan.select || fields.length === 0) invalidQuery("select() requires distinct prepared fields and can be called once per scope.");
497
- return branch({ select: checkedFields(registry, plan.collection, fields) });
500
+ if (fields.length === 0) invalidQuery("select() requires at least one prepared field.");
501
+ const checked = checkedFields(registry, plan.collection, fields);
502
+ const earlier = plan.select ?? [];
503
+ return branch({ select: [...earlier, ...checked.filter((field) => !earlier.includes(field))] });
498
504
  },
499
505
  where(configure) {
500
506
  if (typeof configure !== "function") invalidQuery("where() requires a configuration callback.");
@@ -503,6 +509,7 @@ function createBuilder(registry, run, plan, many = true, root = true, scopeToken
503
509
  return branch({ filters: [...plan.filters, detach(resolveRelations(registry, plan.collection, expression))] });
504
510
  },
505
511
  orderBy(field, direction = "asc") {
512
+ assertList("orderBy()");
506
513
  return branch({ order: checkedOrder(plan, field, direction, many) });
507
514
  },
508
515
  limit(value) {
@@ -541,6 +548,7 @@ function createBuilder(registry, run, plan, many = true, root = true, scopeToken
541
548
  if (!allowAdjacent || plan.adjacent !== void 0) invalidQuery("Adjacency cannot be repeated or selected recursively.");
542
549
  const [configure] = arguments_;
543
550
  if (configure === void 0 || typeof configure === "string") {
551
+ assertList("Results-scope selectAdjacent()");
544
552
  if (!arguments_.length || arguments_.some((field) => typeof field !== "string")) invalidQuery("selectAdjacent() requires neighbor fields, such as selectAdjacent(\"id\"), or a sibling query.");
545
553
  const selection = {
546
554
  ...emptyPlan(plan.collection),
@@ -569,11 +577,12 @@ function createBuilder(registry, run, plan, many = true, root = true, scopeToken
569
577
  } });
570
578
  },
571
579
  all() {
580
+ assertList("all()");
572
581
  return rows("all");
573
582
  },
574
583
  /** Executes a selected root query, matching direct await behavior. */
575
584
  execute() {
576
- return rows("all");
585
+ return rows(many ? "all" : "first");
577
586
  },
578
587
  first() {
579
588
  return rows("first");
@@ -589,6 +598,7 @@ function createBuilder(registry, run, plan, many = true, root = true, scopeToken
589
598
  */
590
599
  paginate(options) {
591
600
  if (!root) invalidQuery("Nested builders have no terminal methods.");
601
+ assertList("paginate()");
592
602
  if (!hasSelection(plan)) invalidQuery(NO_SELECTION_MESSAGE);
593
603
  return run({
594
604
  ...plan,
@@ -599,7 +609,7 @@ function createBuilder(registry, run, plan, many = true, root = true, scopeToken
599
609
  if (root) Object.defineProperty(builder, "then", { value: (fulfilled, rejected) => {
600
610
  let result;
601
611
  try {
602
- result = rows("all");
612
+ result = rows(many ? "all" : "first");
603
613
  } catch (cause) {
604
614
  result = Promise.reject(cause);
605
615
  }
@@ -614,8 +624,13 @@ function createBuilder(registry, run, plan, many = true, root = true, scopeToken
614
624
  if (!hasSelection(plan)) invalidQuery(NO_SELECTION_MESSAGE);
615
625
  return run(plan, terminal);
616
626
  }
627
+ /** Rejects a list-only clause or terminal on a `byId()` query, which returns one entry. */
628
+ function assertList(member) {
629
+ if (root && !many) invalidQuery(`${member} is unavailable after byId(), which returns one entry.`);
630
+ }
617
631
  /** Checks pagination before creating an independent branch. */
618
632
  function pagination(key, value) {
633
+ assertList(`${key}()`);
619
634
  if (!allowPagination || plan[key] !== void 0) invalidQuery("Pagination requires a nonnegative safe integer, one call per clause, and a many-valued scope.");
620
635
  return branch({ [key]: checkedPagination(value, many) });
621
636
  }
@@ -633,9 +648,38 @@ function createBuilder(registry, run, plan, many = true, root = true, scopeToken
633
648
  * @returns An immutable, reusable root builder.
634
649
  */
635
650
  function createFrom(registry, run, collection) {
651
+ return createBuilder(currentRegistry(registry, collection), run, emptyPlan(collection));
652
+ }
653
+ /**
654
+ * Starts a fluent query for the entry with one ID. The builder returns that entry or `undefined`
655
+ * when awaited, offers no ordering, pagination, `all()`, or results-scope adjacency, and still
656
+ * selects nothing until the query requests data.
657
+ *
658
+ * @param registry - Reads the current query registry, so development builders bind the latest one.
659
+ * @param run - Executes a plan against one pinned revision.
660
+ * @param collection - The collection to query.
661
+ * @param id - The entry's ID.
662
+ * @returns An immutable, reusable single-entry root builder.
663
+ */
664
+ function createById(registry, run, collection, id) {
665
+ const current = currentRegistry(registry, collection);
666
+ if (typeof id !== "string") invalidQuery("byId() requires an entry ID string.");
667
+ const filter = {
668
+ kind: "predicate",
669
+ operator: "eq",
670
+ field: "id",
671
+ value: id
672
+ };
673
+ return createBuilder(current, run, {
674
+ ...emptyPlan(collection),
675
+ filters: [filter]
676
+ }, false);
677
+ }
678
+ /** Reads the registry a new root query binds, rejecting unknown collections. */
679
+ function currentRegistry(registry, collection) {
636
680
  const current = registry();
637
681
  if (typeof collection !== "string" || !Object.hasOwn(current, collection)) invalidQuery(`Unknown collection "${String(collection)}".`);
638
- return createBuilder(current, run, emptyPlan(collection));
682
+ return current;
639
683
  }
640
684
  //#endregion
641
685
  //#region src/query/relational.ts
@@ -1376,6 +1420,7 @@ function createRunner(state) {
1376
1420
  */
1377
1421
  const queryStyleMembers = {
1378
1422
  fluent: [
1423
+ "byId",
1379
1424
  "select",
1380
1425
  "where",
1381
1426
  "orderBy",
@@ -1602,6 +1647,7 @@ function createCollection(primary, supplied = []) {
1602
1647
  };
1603
1648
  for (const member of queryStyleMembers.fluent) handle[member] = (...arguments_) => {
1604
1649
  allow(member);
1650
+ if (member === "byId") return createById(registry, run, name, arguments_[0]);
1605
1651
  return createFrom(registry, run, name)[member](...arguments_);
1606
1652
  };
1607
1653
  handleSources.set(handle, sources);
@@ -1,6 +1,6 @@
1
1
  import { _ as Relationship, g as QueryData, i as CollectionRegistry, u as LoadedMarker, v as RelationshipsOf } from "./types-WOsFmNnN.mjs";
2
- import { n as RelationalCountOptions, t as RelationalCollectionQueries, v as Query } from "./relational-types-PtrzENt0.mjs";
3
- import { i as QueryStyleMember, n as QueryStyle, r as QueryStyleDisabled, t as MembersOf } from "./query-style-C2FcT1f9.mjs";
2
+ import { n as RelationalCountOptions, t as RelationalCollectionQueries, v as Query } from "./relational-types-DnI9wR5r.mjs";
3
+ import { i as QueryStyleMember, n as QueryStyle, r as QueryStyleDisabled, t as MembersOf } from "./query-style-k8Qd7wh5.mjs";
4
4
  //#region src/shared/errors.d.ts
5
5
  type ContentErrorCode = "variant-not-found" | "not-renderable" | "source-error" | "validation-error" | "duplicate-id" | "invalid-reference" | "invalid-query" | "invalid-cursor" | "artifact-error" | "render-error";
6
6
  type ContentDiagnostic = {
@@ -58,8 +58,21 @@ type LoadedRegistry<Registry extends CollectionRegistry, Loaded extends keyof Re
58
58
  type FluentMember = MembersOf<"fluent">;
59
59
  /** The relational members, apart from `count`, which both styles share. */
60
60
  type RelationalMember = "findMany" | "findFirst" | "paginate";
61
+ /** Fluent starters that only a collection handle offers, not a query builder. */
62
+ type HandleStarters<Registry extends CollectionRegistry, Name extends keyof Registry & string> = {
63
+ /**
64
+ * Starts a query for the entry with this ID. Select its data as usual; awaiting the query, or
65
+ * calling `first()` or `execute()`, returns the entry or `undefined`. Later `where()` filters
66
+ * also apply. Lookups use the collection's ID index.
67
+ *
68
+ * @param id - The entry's ID.
69
+ * @returns A single-entry root query, without ordering, pagination, `all()`, or results-scope
70
+ * adjacency.
71
+ */
72
+ byId(id: string): Query<Registry, Name, {}, false, false>;
73
+ };
61
74
  /** Every member before `queryStyle` gating. */
62
- type CollectionMethods<Registry extends CollectionRegistry, Name extends keyof Registry & string> = Pick<Query<Registry, Name>, FluentMember & keyof Query<Registry, Name>> & RelationalCollectionQueries<Registry, Name>;
75
+ type CollectionMethods<Registry extends CollectionRegistry, Name extends keyof Registry & string> = Pick<Query<Registry, Name>, FluentMember & keyof Query<Registry, Name>> & HandleStarters<Registry, Name> & RelationalCollectionQueries<Registry, Name>;
63
76
  /**
64
77
  * Members outside the allowed style. Calls report the message as the required `this` type,
65
78
  * whatever arguments they pass.
@@ -1,6 +1,55 @@
1
1
  import { g as MaybePromise } from "./descriptor-bcPa6XLk.mjs";
2
2
  import { a as ComputedContext } from "./types-WOsFmNnN.mjs";
3
3
  import { StandardSchemaV1 } from "@standard-schema/spec";
4
+ //#region src/computed/fields.d.ts
5
+ /** Result schemas for computed fields that one resolver call fills, keyed by field name. */
6
+ type ComputedFieldSchemas = Record<string, StandardSchemaV1>;
7
+ /**
8
+ * The object a shared resolver returns: one value per field, checked by that field's schema.
9
+ * Fields whose schema accepts `undefined` may be omitted.
10
+ */
11
+ type ComputedFieldValues<Fields extends ComputedFieldSchemas> = { [Key in keyof Fields as undefined extends StandardSchemaV1.InferInput<Fields[Key]> ? never : Key]: StandardSchemaV1.InferInput<Fields[Key]>; } & { [Key in keyof Fields as undefined extends StandardSchemaV1.InferInput<Fields[Key]> ? Key : never]?: StandardSchemaV1.InferInput<Fields[Key]>; };
12
+ /** Declares several computed fields and the one resolver that returns all of their values. */
13
+ type ComputedFieldsOptions<Fields extends ComputedFieldSchemas, Data = Record<string, unknown>, Source = unknown> = {
14
+ /**
15
+ * Each field's result schema, keyed by field name. A JSDoc comment on a key documents that
16
+ * field in entry types and query results.
17
+ */
18
+ fields: Fields;
19
+ /**
20
+ * Returns every field's value for one entry. It runs once per entry each time the entry is
21
+ * prepared, however many fields it fills; files it reads through `files` are tracked as usual.
22
+ * Annotate the context, such as `(context: ComputedContext<Post>)`, to type `data`.
23
+ */
24
+ resolve(this: void, context: ComputedContext<Data, Source>): MaybePromise<ComputedFieldValues<Fields>>;
25
+ };
26
+ /** The computed field definitions that {@link computedFields} returns, one per declared field. */
27
+ type ComputedFieldDefinitions<Fields extends ComputedFieldSchemas, Data = Record<string, unknown>, Source = unknown> = { [Key in keyof Fields]: {
28
+ schema: Fields[Key];
29
+ resolve(context: ComputedContext<Data, Source>): Promise<unknown>;
30
+ }; };
31
+ /**
32
+ * Creates computed fields that share one resolver call per entry, for values that come from the
33
+ * same expensive read, such as several properties of one JSON file beside the entry. Spread the
34
+ * result into `computed`; each field is validated by its own schema and is selected, filtered, and
35
+ * sorted like any other computed field.
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * computed: {
40
+ * ...computedFields({
41
+ * fields: { title: schema.string(), description: schema.string() },
42
+ * resolve: async ({ files }) => JSON.parse(await files.text("info/en-US.json")),
43
+ * }),
44
+ * }
45
+ * ```
46
+ *
47
+ * @param options - The fields' result schemas and the resolver that returns their values.
48
+ * @returns One computed field definition per key of `options.fields`.
49
+ * @throws TypeError - When `fields` declares no schemas or `resolve` is not a function.
50
+ */
51
+ export declare function computedFields<const Fields extends ComputedFieldSchemas, Data = Record<string, unknown>, Source = unknown>(options: ComputedFieldsOptions<Fields, Data, Source>): ComputedFieldDefinitions<Fields, Data, Source>;
52
+ //#endregion
4
53
  //#region src/computed/text.d.ts
5
54
  /** The syntax removed before computing text-derived fields; literal text preserves all characters. */
6
55
  type TextFormat = "markdown" | "text" | "html";
@@ -40,4 +89,4 @@ export declare function excerpt<Data = Record<string, unknown>, Source = unknown
40
89
  resolve(context: ComputedContext<Data, Source>): Promise<string>;
41
90
  };
42
91
  //#endregion
43
- export type { ExcerptOptions, ReadingTimeOptions, TextFormat, TextOptions };
92
+ export type { ComputedFieldDefinitions, ComputedFieldSchemas, ComputedFieldValues, ComputedFieldsOptions, ExcerptOptions, ReadingTimeOptions, TextFormat, TextOptions };
package/dist/computed.mjs CHANGED
@@ -3,6 +3,63 @@ import { n as number, r as string } from "./schema-C5Str-E8.mjs";
3
3
  import { t as src_default } from "./dist-C7_FOzY_.mjs";
4
4
  import getReadingTime from "reading-time";
5
5
  import { Parser } from "htmlparser2";
6
+ //#region src/computed/fields.ts
7
+ assertServerOnly();
8
+ /**
9
+ * Creates computed fields that share one resolver call per entry, for values that come from the
10
+ * same expensive read, such as several properties of one JSON file beside the entry. Spread the
11
+ * result into `computed`; each field is validated by its own schema and is selected, filtered, and
12
+ * sorted like any other computed field.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * computed: {
17
+ * ...computedFields({
18
+ * fields: { title: schema.string(), description: schema.string() },
19
+ * resolve: async ({ files }) => JSON.parse(await files.text("info/en-US.json")),
20
+ * }),
21
+ * }
22
+ * ```
23
+ *
24
+ * @param options - The fields' result schemas and the resolver that returns their values.
25
+ * @returns One computed field definition per key of `options.fields`.
26
+ * @throws TypeError - When `fields` declares no schemas or `resolve` is not a function.
27
+ */
28
+ function computedFields(options) {
29
+ const { fields, resolve } = options ?? {};
30
+ if (!fields || typeof fields !== "object" || Array.isArray(fields)) throw new TypeError("computedFields() fields must be an object of result schemas.");
31
+ const entries = Object.entries(fields);
32
+ if (!entries.length || entries.some(([, schema]) => !isSchema(schema))) throw new TypeError("computedFields() fields must declare at least one field, each with a Standard Schema.");
33
+ if (typeof resolve !== "function") throw new TypeError("computedFields() resolve must be a function.");
34
+ const resolutions = /* @__PURE__ */ new WeakMap();
35
+ /** Resolves every field's value once for the entry being prepared. */
36
+ function values(context) {
37
+ let pending = resolutions.get(context.files);
38
+ if (!pending) {
39
+ pending = (async () => {
40
+ context.signal.throwIfAborted();
41
+ const result = await resolve(context);
42
+ if (!result || typeof result !== "object" || Array.isArray(result)) throw new TypeError("computedFields() resolve must return an object with a value for each field.");
43
+ return result;
44
+ })();
45
+ resolutions.set(context.files, pending);
46
+ }
47
+ return pending;
48
+ }
49
+ return Object.fromEntries(entries.map(([name, schema]) => [name, {
50
+ schema,
51
+ /** Reads this field's value from the entry's shared resolution. */
52
+ async resolve(context) {
53
+ const result = await values(context);
54
+ return Object.hasOwn(result, name) ? result[name] : void 0;
55
+ }
56
+ }]));
57
+ }
58
+ /** Checks for a Standard Schema V1 validator. */
59
+ function isSchema(value) {
60
+ return (typeof value === "object" || typeof value === "function") && value !== null && "~standard" in value;
61
+ }
62
+ //#endregion
6
63
  //#region src/computed/plain-text.ts
7
64
  const markdown = new src_default({ html: true });
8
65
  const ignoredElements = /* @__PURE__ */ new Set([
@@ -136,4 +193,4 @@ function excerpt(options = {}) {
136
193
  return textField(string(), options, (text) => truncate(text, maxLength));
137
194
  }
138
195
  //#endregion
139
- export { excerpt, readingTime, wordCount };
196
+ export { computedFields, excerpt, readingTime, wordCount };
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { a as DataStore, c as Loader, d as LoaderLogger, f as LoaderSource, g as MaybePromise, h as RecordLoader, i as DataEntry, l as LoaderConfig, m as MetaStore, n as GlobOptions, o as FileEntrySource, p as LoaderWatcher, r as BodyLoader, s as FileSource, u as LoaderContext } from "./descriptor-bcPa6XLk.mjs";
2
2
  import { n as ContentRenderResult, r as RenderedContent, t as ContentHeading } from "./types-Dh-Sydyw.mjs";
3
3
  import { D as VariantValues, E as VariantOf, T as VariantField, _ as Relationship, a as ComputedContext, b as SchemaOutput, c as EntryFiles, d as PayloadContext, f as PayloadData, g as QueryData, h as PayloadsOf, i as CollectionRegistry, l as FileEntry, m as PayloadDefinition, n as BuiltinPayloads, o as CustomPayload, p as PayloadDeclaration, r as CollectionDefinition, s as CustomPayloadMarker, t as BuiltinPayloadMarker, w as VariantContext, x as SelectedData, y as Schema } from "./types-WOsFmNnN.mjs";
4
- import { n as QueryStyle } from "./query-style-C2FcT1f9.mjs";
4
+ import { n as QueryStyle } from "./query-style-k8Qd7wh5.mjs";
5
5
  import { StandardSchemaV1 } from "@standard-schema/spec";
6
6
  //#region src/collection/source.d.ts
7
7
  type CollectionSource = string | ({
@@ -1,9 +1,9 @@
1
- import { n as Collection, r as CollectionHandles, s as ContentError } from "./collection-types-DhIxnCSM.mjs";
1
+ import { n as Collection, r as CollectionHandles, s as ContentError } from "./collection-types-Ch4YLG8t.mjs";
2
2
  import { n as ContentRenderResult } from "./types-Dh-Sydyw.mjs";
3
3
  import { i as CollectionRegistry } from "./types-WOsFmNnN.mjs";
4
- import { O as QueryRegistry } from "./relational-types-PtrzENt0.mjs";
5
- import { n as QueryStyle } from "./query-style-C2FcT1f9.mjs";
6
- import { n as PreparedRevision, t as ContentState } from "./revision-aqGUf6YV.mjs";
4
+ import { O as QueryRegistry } from "./relational-types-DnI9wR5r.mjs";
5
+ import { n as QueryStyle } from "./query-style-k8Qd7wh5.mjs";
6
+ import { n as PreparedRevision, t as ContentState } from "./revision-vhe9gRyk.mjs";
7
7
  //#region src/artifacts/types.d.ts
8
8
  type ArtifactManifest = {
9
9
  version: number;
package/dist/internal.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { t as ContentError } from "./errors-Bfd0R4_P.mjs";
2
2
  import { r as decode } from "./values-BbTV4QT-.mjs";
3
- import { i as isQueryStyle, n as createCollections, r as createLiveCollection, t as createCollection } from "./collection-Cb5eWGnv.mjs";
3
+ import { i as isQueryStyle, n as createCollections, r as createLiveCollection, t as createCollection } from "./collection-txOFpidL.mjs";
4
4
  import { t as restoreContentError } from "./diagnostics-DmUZtrJ6.mjs";
5
5
  //#region src/artifacts/runtime.ts
6
6
  /** Checks that a value is a plain record before reading generated payload tables from it. */
package/dist/next.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as BuildContentOptions } from "./build-Aoi7Unm0.mjs";
1
+ import { t as BuildContentOptions } from "./build-DE4ZKHZR.mjs";
2
2
  import { NextConfig } from "next";
3
3
  //#region src/integrations/next/plugin.d.ts
4
4
  /** Content locations for Next.js; the adapter chooses generated storage and the watch lifecycle. */
package/dist/next.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { I as collectionNames } from "./operators-D-w1-gDg.mjs";
2
- import { c as collectionDeclarations, i as resolveConfigPath, t as buildAtRoot } from "./build-CIw4yWs9.mjs";
2
+ import { c as collectionDeclarations, i as resolveConfigPath, t as buildAtRoot } from "./build-DYw3z9ju.mjs";
3
3
  import { n as serializeContentError } from "./diagnostics-DmUZtrJ6.mjs";
4
4
  import path from "node:path";
5
5
  import { randomUUID } from "node:crypto";
@@ -7,7 +7,7 @@ type QueryStyle = "fluent" | "relational";
7
7
  * belongs to both styles; `count(options)` takes relational options.
8
8
  */
9
9
  declare const queryStyleMembers: {
10
- readonly fluent: readonly ["select", "where", "orderBy", "limit", "offset", "with", "withPayload", "selectAdjacent"];
10
+ readonly fluent: readonly ["byId", "select", "where", "orderBy", "limit", "offset", "with", "withPayload", "selectAdjacent"];
11
11
  readonly relational: readonly ["findMany", "findFirst", "paginate", "count(options)"];
12
12
  };
13
13
  /** A collection member that belongs to exactly one query style. */
package/dist/query.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as Selection, E as Expression, S as ScalarField, T as Where, _ as PaginateResult, a as RelationalQueryOptions, b as RelationField, c as Adjacent, d as Field, f as Fields, g as PaginateOptions, h as PageOptions, i as RelationalQueries, l as CursorOptions, m as Ordered, n as RelationalCountOptions, o as RelationalQueryResult, p as NumberedPage, r as RelationalPaginateOptions, s as RelationalWhere, u as CursorPage, w as SelectionResult, x as Scalar, y as QueryFields } from "./relational-types-PtrzENt0.mjs";
1
+ import { C as Selection, E as Expression, S as ScalarField, T as Where, _ as PaginateResult, a as RelationalQueryOptions, b as RelationField, c as Adjacent, d as Field, f as Fields, g as PaginateOptions, h as PageOptions, i as RelationalQueries, l as CursorOptions, m as Ordered, n as RelationalCountOptions, o as RelationalQueryResult, p as NumberedPage, r as RelationalPaginateOptions, s as RelationalWhere, u as CursorPage, w as SelectionResult, x as Scalar, y as QueryFields } from "./relational-types-DnI9wR5r.mjs";
2
2
  //#region src/query/operators.d.ts
3
3
  /** Matches a scalar using strict value/date equality. */
4
4
  export declare function eq<Value>(field: ScalarField<Value>, value: NoInfer<Value>): Expression;
@@ -142,7 +142,8 @@ type RelatedName$1<Registry extends CollectionRegistry, Relation extends Relatio
142
142
  type RelationshipResult<Definition, Relation extends Relationship, Result, Filtered extends boolean = false> = Relation["type"] extends "many" ? Result[] : Result | (Filtered extends true ? null : Relation["referenceSide"] extends "from" ? Relation["from"] extends keyof QueryData<Definition> ? null extends QueryData<Definition>[Relation["from"]] ? null : undefined extends QueryData<Definition>[Relation["from"]] ? null : never : null : null);
143
143
  type RelationValue<Definition, Relation extends Relationship, Configured> = RelationshipResult<Definition, Relation, ResultOf<Configured>, FilteredOf<Configured>>;
144
144
  type Query<Registry extends CollectionRegistry, Name extends keyof Registry & string, Result = {}, Selected extends boolean = false, Many extends boolean = true, Root extends boolean = true, Filtered extends boolean = false, AllowAdjacent extends boolean = true, Pagination extends boolean = true, Paged extends boolean = false> = QueryInfo<Result, Selected, Filtered> & {
145
- select<const Keys extends readonly [keyof QueryData<Registry[Name]> & string, ...(keyof QueryData<Registry[Name]> & string)[]]>(...keys: Keys): Query<Registry, Name, Simplify$1<{ [Key in Exclude<keyof Result, keyof QueryData<Registry[Name]>>]: Result[Key]; } & SelectedData<QueryData<Registry[Name]>, Keys[number]>>, true, Many, Root, Filtered, AllowAdjacent, Pagination, Paged>;
145
+ /** Selects fields, adding to fields that earlier `select()` calls on this scope selected. */
146
+ select<const Keys extends readonly [keyof QueryData<Registry[Name]> & string, ...(keyof QueryData<Registry[Name]> & string)[]]>(...keys: Keys): Query<Registry, Name, Simplify$1<Result & SelectedData<QueryData<Registry[Name]>, Keys[number]>>, true, Many, Root, Filtered, AllowAdjacent, Pagination, Paged>;
146
147
  where(configure: (fields: QueryFields<Registry, Name>) => Expression): Query<Registry, Name, Result, Selected, Many, Root, true, AllowAdjacent, Pagination, Paged>;
147
148
  with<Key extends keyof RelationshipsOf<Registry[Name]> & string, Configured extends QueryInfo<unknown, true>>(name: Key, configure: (query: Query<Registry, RelatedName$1<Registry, RelationOf<Registry, Name, Key>>, {}, false, RelationOf<Registry, Name, Key>["type"] extends "many" ? true : false, false, false, AllowAdjacent>) => Configured): Query<Registry, Name, Result & { [Property in Key]: RelationValue<Registry[Name], RelationOf<Registry, Name, Key>, Configured>; }, true, Many, Root, Filtered, AllowAdjacent, Pagination, Paged>;
148
149
  /** A relationship into a collection the query has not loaded reports which one to supply. */
@@ -152,33 +153,41 @@ type Query<Registry extends CollectionRegistry, Name extends keyof Registry & st
152
153
  } : {}) & (Many extends true ? Pagination extends true ? {
153
154
  limit(value: number): Query<Registry, Name, Result, Selected, Many, Root, Filtered, AllowAdjacent, Pagination, true>;
154
155
  offset(value: number): Query<Registry, Name, Result, Selected, Many, Root, Filtered, AllowAdjacent, Pagination, true>;
155
- } : {} : {}) & (AllowAdjacent extends true ? {
156
- /** Attaches a payload under `_payloads` after filtering, ordering, and pagination. */
157
- withPayload<Key extends keyof PayloadsOf<Registry[Name]> & string>(name: Key, ...variant: keyof VariantOf<Registry[Name], Key> extends never ? [] : [variant: Readonly<VariantOf<Registry[Name], Key>>]): Query<Registry, Name, WithPayload<Result, Key, PayloadsOf<Registry[Name]>[Key]>, true, Many, Root, Filtered, AllowAdjacent, Pagination, Paged>;
156
+ } : {} : {}) & (AllowAdjacent extends true ? [Root, Many] extends [true, false] ? {} : {
158
157
  /** Selects neighbor fields within this scope's filtered and ordered results. */
159
158
  selectAdjacent<const Keys extends readonly [keyof QueryData<Registry[Name]> & string, ...(keyof QueryData<Registry[Name]> & string)[]]>(...fields: Keys): Query<Registry, Name, Result & {
160
159
  _adjacent: Adjacent<SelectedData<QueryData<Registry[Name]>, Keys[number]>>;
161
160
  }, true, Many, Root, Filtered, AllowAdjacent, Pagination, Paged>;
161
+ } : {}) & (AllowAdjacent extends true ? {
162
+ /** Attaches a payload under `_payloads` after filtering, ordering, and pagination. */
163
+ withPayload<Key extends keyof PayloadsOf<Registry[Name]> & string>(name: Key, ...variant: keyof VariantOf<Registry[Name], Key> extends never ? [] : [variant: Readonly<VariantOf<Registry[Name], Key>>]): Query<Registry, Name, WithPayload<Result, Key, PayloadsOf<Registry[Name]>[Key]>, true, Many, Root, Filtered, AllowAdjacent, Pagination, Paged>;
164
+ /** Selects neighbors from an independent sibling scope. */
162
165
  selectAdjacent<Configured extends QueryInfo<unknown, true>>(configure: (siblings: Query<Registry, Name, {}, false, true, false, false, false, false>) => Configured): Query<Registry, Name, Result & {
163
166
  _adjacent: Adjacent<ResultOf<Configured>>;
164
167
  }, true, Many, Root, Filtered, AllowAdjacent, Pagination, Paged>;
165
168
  selectAdjacent<const Options extends AdjacentOptions<Registry, Name>>(options: Options & CheckedOptions<Registry, Name, NoInfer<Options>, true, false, false>): Query<Registry, Name, Result & {
166
169
  _adjacent: Adjacent<SelectionResult<Registry, Name, Options["select"]>>;
167
170
  }, true, Many, Root, Filtered, AllowAdjacent, Pagination, Paged>;
168
- } : {}) & (Root extends true ? {
171
+ } : {}) & (Root extends true ? (Many extends true ? {
169
172
  all: Selected extends true ? () => Promise<Simplify$1<Result>[]> : UnselectedTerminal;
170
173
  execute: Selected extends true ? () => Promise<Simplify$1<Result>[]> : UnselectedTerminal;
171
174
  then: Selected extends true ? Promise<Simplify$1<Result>[]>["then"] : UnselectedThen;
175
+ /**
176
+ * Returns one page from one revision: `{ page, size }` for page numbers, or
177
+ * `{ size, after?, before?, total? }` for cursors. Unavailable after `limit()` or
178
+ * `offset()`.
179
+ */
180
+ paginate: Selected extends true ? [Paged] extends [true] ? PaginateAfterLimit : Paginate<Simplify$1<Result>> : UnselectedTerminal;
181
+ } : {
182
+ /** Runs a `byId()` query, matching direct await: the entry, or `undefined`. */
183
+ execute: Selected extends true ? () => Promise<Simplify$1<Result> | undefined> : UnselectedTerminal;
184
+ then: Selected extends true ? Promise<Simplify$1<Result> | undefined>["then"] : UnselectedThen;
185
+ }) & {
172
186
  first: Selected extends true ? () => Promise<Simplify$1<Result> | undefined> : UnselectedTerminal;
173
187
  /** Counts matching entries without a selection; unavailable once payloads are requested. */
174
188
  count: [Result] extends [never] ? () => Promise<number> : [Result] extends [{
175
189
  _payloads: unknown;
176
190
  }] ? never : () => Promise<number>;
177
- /**
178
- * Returns one page from one revision: `{ page, size }` for page numbers, or
179
- * `{ size, after?, before?, total? }` for cursors. Unavailable after `limit()` or `offset()`.
180
- */
181
- paginate: Selected extends true ? [Paged] extends [true] ? PaginateAfterLimit : Paginate<Simplify$1<Result>> : UnselectedTerminal;
182
191
  } : {});
183
192
  /** `paginate()` after `limit()` or `offset()` reports why as its required `this` type. */
184
193
  type PaginateAfterLimit = (this: typeof PAGINATE_AFTER_LIMIT_MESSAGE, ...arguments_: readonly unknown[]) => never;
@@ -1,8 +1,8 @@
1
- import { s as ContentError } from "./collection-types-DhIxnCSM.mjs";
1
+ import { s as ContentError } from "./collection-types-Ch4YLG8t.mjs";
2
2
  import { n as ContentRenderResult } from "./types-Dh-Sydyw.mjs";
3
3
  import { _ as Relationship } from "./types-WOsFmNnN.mjs";
4
- import { D as QueryPlan, O as QueryRegistry, k as QueryTerminal } from "./relational-types-PtrzENt0.mjs";
5
- import { n as QueryStyle } from "./query-style-C2FcT1f9.mjs";
4
+ import { D as QueryPlan, O as QueryRegistry, k as QueryTerminal } from "./relational-types-DnI9wR5r.mjs";
5
+ import { n as QueryStyle } from "./query-style-k8Qd7wh5.mjs";
6
6
  //#region src/runtime/revision.d.ts
7
7
  type QueryRecord = Record<string, unknown> & {
8
8
  id: string;
@@ -1,4 +1,4 @@
1
- import { c as ContentErrorCode, i as LoadedRegistry, n as Collection, o as ContentDiagnostic, r as CollectionHandles, s as ContentError, t as AnyCollection } from "./collection-types-DhIxnCSM.mjs";
1
+ import { c as ContentErrorCode, i as LoadedRegistry, n as Collection, o as ContentDiagnostic, r as CollectionHandles, s as ContentError, t as AnyCollection } from "./collection-types-Ch4YLG8t.mjs";
2
2
  import { n as ContentRenderResult, t as ContentHeading } from "./types-Dh-Sydyw.mjs";
3
- import { n as QueryStyle, r as QueryStyleDisabled } from "./query-style-C2FcT1f9.mjs";
3
+ import { n as QueryStyle, r as QueryStyleDisabled } from "./query-style-k8Qd7wh5.mjs";
4
4
  export { type AnyCollection, type Collection, type CollectionHandles, type ContentDiagnostic, ContentError, type ContentErrorCode, type ContentHeading, type ContentRenderResult, type LoadedRegistry, type QueryStyle, type QueryStyleDisabled };
package/dist/vite.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as ContentSourceEvent, o as ContentSourceSnapshot, t as BuildContentOptions } from "./build-Aoi7Unm0.mjs";
1
+ import { a as ContentSourceEvent, o as ContentSourceSnapshot, t as BuildContentOptions } from "./build-DE4ZKHZR.mjs";
2
2
  import { Plugin } from "vite";
3
3
  //#region src/integrations/vite/plugin.d.ts
4
4
  /** Public, format-neutral hooks for integrations sharing this plugin's preparation session. */
package/dist/vite.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { I as collectionNames } from "./operators-D-w1-gDg.mjs";
2
- import { a as sessionInternals, i as resolveConfigPath, l as virtualDeclarations, o as withinDirectory, r as configCandidates, s as writeChanged, t as buildAtRoot, u as revisionModules } from "./build-CIw4yWs9.mjs";
2
+ import { a as sessionInternals, i as resolveConfigPath, l as virtualDeclarations, o as withinDirectory, r as configCandidates, s as writeChanged, t as buildAtRoot, u as revisionModules } from "./build-DYw3z9ju.mjs";
3
3
  import { n as serializeContentError } from "./diagnostics-DmUZtrJ6.mjs";
4
4
  import { createRequire } from "node:module";
5
5
  import path from "node:path";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "content-query",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "Typed, framework-independent content collections, queries, and rendering.",
5
5
  "homepage": "https://github.com/yangshun/content-query#readme",
6
6
  "bugs": {