content-query 0.4.0 → 0.5.1

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.
@@ -1,5 +1,134 @@
1
1
  import { a as invalidQuery } from "./errors-Bfd0R4_P.mjs";
2
2
  import { c as isScalar, i as detach } from "./values-BbTV4QT-.mjs";
3
+ //#region src/collection/names.ts
4
+ /** Words that cannot name a `const` binding in an ES module, or that read as module syntax. */
5
+ const reservedWords = new Set("arguments await break case catch class const continue debugger default delete do else enum eval export extends false finally for function if implements import in instanceof interface let new null package private protected public return static super switch this throw true try typeof var void while with yield".split(" "));
6
+ /** Names Windows reserves for devices, in any letter case, which cannot name a directory. */
7
+ const deviceName = /^(?:con|prn|aux|nul|com[0-9]|lpt[0-9])$/i;
8
+ const identifier = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
9
+ /** Uppercases the first character, keeping the rest of a camelCase name. */
10
+ function capitalize(value) {
11
+ return value.charAt(0).toUpperCase() + value.slice(1);
12
+ }
13
+ /**
14
+ * Converts a collection name into the identifier its entry exports and its import path uses.
15
+ * Characters that cannot appear in an identifier separate camelCase words, a leading digit gets
16
+ * an `_` prefix, and reserved words and Windows device names get a `Collection` suffix.
17
+ *
18
+ * @param name - The collection's key in the `collections` registry.
19
+ * @returns The export name, such as `blogPosts` for `blog-posts`.
20
+ */
21
+ function exportNameOf(name) {
22
+ let converted = name.split(/[^A-Za-z0-9_$]+/).filter(Boolean).map((word, index) => index === 0 ? word : capitalize(word)).join("");
23
+ if (!converted) converted = "collection";
24
+ else if (/^[0-9]/.test(converted)) converted = `_${converted}`;
25
+ return reservedWords.has(converted) || deviceName.test(converted) ? `${converted}Collection` : converted;
26
+ }
27
+ /**
28
+ * Derives the singular word for generated type names by dropping one trailing `s`. Names ending
29
+ * in `ss`, such as `address`, keep it. Irregular plurals use the collection's `typeName`.
30
+ *
31
+ * @param exportName - The collection's export name.
32
+ * @returns The singular form, such as `blogPost` for `blogPosts`.
33
+ */
34
+ function singularOf(exportName) {
35
+ return exportName.length > 1 && exportName.endsWith("s") && !exportName.endsWith("ss") ? exportName.slice(0, -1) : exportName;
36
+ }
37
+ /**
38
+ * Computes every collection's generated names and rejects names that would collide.
39
+ *
40
+ * @param registry - Collection keys with each collection's optional `typeName` override.
41
+ * @returns The names of each collection's entry, keyed by collection name.
42
+ * @throws An `Error` naming the conflict when two collections share an export or type name, or
43
+ * when a `typeName` is not an identifier.
44
+ */
45
+ function collectionNames(registry) {
46
+ const names = /* @__PURE__ */ new Map();
47
+ const exports = /* @__PURE__ */ new Map();
48
+ const types = /* @__PURE__ */ new Map();
49
+ for (const [name, definition] of Object.entries(registry)) {
50
+ const declared = definition?.typeName;
51
+ if (declared !== void 0 && (typeof declared !== "string" || !identifier.test(declared))) throw new Error(`Collection "${name}" typeName must be an identifier such as "Category"; received ${JSON.stringify(declared)}.`);
52
+ const exportName = exportNameOf(name);
53
+ const typeName = `Content${capitalize(declared ?? singularOf(exportName))}`;
54
+ const exported = exports.get(exportName.toLowerCase());
55
+ if (exported !== void 0) throw new Error(`Collections "${exported}" and "${name}" both export "${exportName}" (ignoring letter case), which their #content-query entries cannot share. Rename one of them.`);
56
+ const payloadTypeName = `${typeName}Payload`;
57
+ for (const generated of [typeName, payloadTypeName]) {
58
+ const typed = types.get(generated);
59
+ if (typed !== void 0) throw new Error(`Collections "${typed}" and "${name}" both generate the type "${generated}". Set typeName on one of them.`);
60
+ types.set(generated, name);
61
+ }
62
+ exports.set(exportName.toLowerCase(), name);
63
+ names.set(name, {
64
+ exportName,
65
+ typeName,
66
+ payloadTypeName
67
+ });
68
+ }
69
+ return names;
70
+ }
71
+ //#endregion
72
+ //#region src/query/plan.ts
73
+ /**
74
+ * Rejects a declared relationship whose target collection this query scope has not loaded,
75
+ * naming the collection to pass to `using()`.
76
+ *
77
+ * @param registry - The query scope's registry.
78
+ * @param collection - The collection that declares the relationship.
79
+ * @param name - The relationship, field, or filter name a query used.
80
+ */
81
+ function assertAvailable(registry, collection, name) {
82
+ const unavailable = registry[collection]?.unavailable;
83
+ if (!unavailable || !Object.hasOwn(unavailable, name)) return;
84
+ const target = exportNameOf(unavailable[name]);
85
+ invalidQuery(`Relationship "${name}" targets the ${target} collection, which this query has not loaded. Pass it to using(), such as ${exportNameOf(collection)}.using(${target}).`);
86
+ }
87
+ /** Starts a fresh query without binding a content revision until execution. */
88
+ function emptyPlan(collection) {
89
+ return {
90
+ collection,
91
+ filters: [],
92
+ order: [],
93
+ with: {}
94
+ };
95
+ }
96
+ /** Explains the explicit-selection rule wherever a scope returns rows without selecting data. */
97
+ const NO_SELECTION_MESSAGE = "Select fields, relationships, payloads, or adjacent entries before running this query.";
98
+ /** Explains why `paginate()` is unavailable once a query sets its own limit or offset. */
99
+ const PAGINATE_AFTER_LIMIT_MESSAGE = "paginate() sets the limit and offset itself; remove limit and offset from this query.";
100
+ /**
101
+ * Returns the order a scope's entries follow: explicit clauses, then ascending ID as the final
102
+ * tie-breaker unless ID is already sorted. Scopes without clauses sort by ascending ID, except
103
+ * relationship scopes that preserve an authored ID array's order.
104
+ *
105
+ * @param plan - The scope whose order to resolve.
106
+ * @param preserveOrder - Keeps authored order when the scope has no explicit clauses.
107
+ * @returns The sort clauses to apply, in priority order.
108
+ */
109
+ function effectiveOrder(plan, preserveOrder = false) {
110
+ if (!plan.order.length) return preserveOrder ? [] : [{
111
+ field: "id",
112
+ direction: "asc"
113
+ }];
114
+ const order = [...plan.order];
115
+ if (!order.some((sort) => sort.field === "id")) order.push({
116
+ field: "id",
117
+ direction: "asc"
118
+ });
119
+ return order;
120
+ }
121
+ /**
122
+ * Checks whether a row-returning scope explicitly requests data. Nothing is implicit, not even
123
+ * `id`: a scope must select fields, include a relationship, request a payload, or select adjacency.
124
+ *
125
+ * @param plan - The scope to check; `undefined` when a callback returned no builder.
126
+ * @returns `true` when the scope requests at least one kind of data.
127
+ */
128
+ function hasSelection(plan) {
129
+ return !!plan && (!!plan.select?.length || !!plan.adjacent || Object.keys(plan.with).length > 0 || Object.keys(plan.payloads ?? {}).length > 0);
130
+ }
131
+ //#endregion
3
132
  //#region src/query/operators.ts
4
133
  /** Builds a checked, backend-independent predicate instead of a per-entry callback. */
5
134
  function predicate(operator, field, value) {
@@ -139,7 +268,10 @@ function resolveRelations(registry, collection, expression) {
139
268
  case "has": {
140
269
  const relationships = registry[collection]?.relationships ?? {};
141
270
  const relation = Object.hasOwn(relationships, expression.field) ? relationships[expression.field] : void 0;
142
- if (!relation) invalidQuery(`Unknown relationship "${expression.field}" in has().`);
271
+ if (!relation) {
272
+ assertAvailable(registry, collection, expression.field);
273
+ invalidQuery(`Unknown relationship "${expression.field}" in has().`);
274
+ }
143
275
  return {
144
276
  kind: "relation",
145
277
  collection,
@@ -256,4 +388,4 @@ function validateExpression(expression) {
256
388
  } else invalidQuery("Invalid expression structure.");
257
389
  }
258
390
  //#endregion
259
- export { notIlike as C, resolveRelations as D, or as E, startsWith as O, not as S, notLike as T, isUndefined as _, arrayContainsAny as a, lte as b, eq as c, gte as d, has as f, isNull as g, isMissing as h, arrayContainsAll as i, validateExpression as k, fieldProxy as l, inArray as m, arrayContained as n, contains as o, ilike as p, arrayContains as r, endsWith as s, and as t, gt as u, like as v, notInArray as w, ne as x, lt as y };
391
+ export { NO_SELECTION_MESSAGE as A, notIlike as C, resolveRelations as D, or as E, hasSelection as F, collectionNames as I, assertAvailable as M, effectiveOrder as N, startsWith as O, emptyPlan as P, not as S, notLike as T, isUndefined as _, arrayContainsAny as a, lte as b, eq as c, gte as d, has as f, isNull as g, isMissing as h, arrayContainsAll as i, PAGINATE_AFTER_LIMIT_MESSAGE as j, validateExpression as k, fieldProxy as l, inArray as m, arrayContained as n, contains as o, ilike as p, arrayContains as r, endsWith as s, and as t, gt as u, like as v, notInArray as w, ne as x, lt as y };
@@ -0,0 +1,23 @@
1
+ //#region src/runtime/query-style.d.ts
2
+ /** Query APIs an application can allow through `queryStyle` in content-query.config. */
3
+ type QueryStyle = "fluent" | "relational";
4
+ /**
5
+ * Collection members that start each query style. Every other query API hangs off these members,
6
+ * so gating them gates payloads, relationships, and adjacency as well. `count()` without options
7
+ * belongs to both styles; `count(options)` takes relational options.
8
+ */
9
+ declare const queryStyleMembers: {
10
+ readonly fluent: readonly ["select", "where", "orderBy", "limit", "offset", "with", "withPayload", "selectAdjacent"];
11
+ readonly relational: readonly ["findMany", "findFirst", "paginate", "count(options)"];
12
+ };
13
+ /** A collection member that belongs to exactly one query style. */
14
+ type QueryStyleMember = (typeof queryStyleMembers)[QueryStyle][number];
15
+ /** Collection members that start a query in the given style. */
16
+ type MembersOf<Style extends QueryStyle> = (typeof queryStyleMembers)[Style][number];
17
+ /**
18
+ * Describes a disabled collection member in the type error itself, for example:
19
+ * `posts.select is unavailable because content-query.config sets queryStyle to "relational".`
20
+ */
21
+ type QueryStyleDisabled<Collection extends string, Member extends QueryStyleMember, Style extends QueryStyle> = `${Collection}.${Member} is unavailable because content-query.config sets queryStyle to "${Style}".`;
22
+ //#endregion
23
+ export { QueryStyleMember as i, QueryStyle as n, QueryStyleDisabled as r, MembersOf as t };
package/dist/query.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { _ as Where, a as RelationalWhere, c as Fields, d as QueryFields, f as RelationField, g as SelectionResult, h as Selection, i as RelationalQueryResult, l as Ordered, m as ScalarField, n as RelationalQueries, o as Adjacent, p as Scalar, r as RelationalQueryOptions, s as Field, t as RelationalCountOptions, v as Expression } from "./relational-types-C8xJtLG5.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-PtrzENt0.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;
@@ -64,4 +64,4 @@ export declare function or(...expressions: Expression[]): Expression;
64
64
  /** Negates a predicate's boolean result. */
65
65
  export declare function not(expression: Expression): Expression;
66
66
  //#endregion
67
- export type { Adjacent, RelationalCountOptions as CountOptions, Expression, Field, Fields, QueryFields, RelationalQueryOptions as QueryOptions, RelationalQueryOptions, RelationField, RelationalQueries, RelationalQueryResult, RelationalWhere, Selection, SelectionResult, Where };
67
+ export type { Adjacent, RelationalCountOptions as CountOptions, CursorOptions, CursorPage, Expression, Field, Fields, NumberedPage, PageOptions, PaginateOptions, PaginateResult, QueryFields, RelationalQueryOptions as QueryOptions, RelationalQueryOptions, RelationField, RelationalPaginateOptions, RelationalQueries, RelationalQueryResult, RelationalWhere, Selection, SelectionResult, Where };
package/dist/query.mjs CHANGED
@@ -1 +1 @@
1
- export { t as and, n as arrayContained, r as arrayContains, i as arrayContainsAll, a as arrayContainsAny, o as contains, s as endsWith, c as eq, u as gt, d as gte, f as has, p as ilike, m as inArray, h as isMissing, g as isNull, _ as isUndefined, v as like, y as lt, b as lte, x as ne, S as not, C as notIlike, w as notInArray, T as notLike, E as or, O as startsWith } from "./operators-Cwy8Kx4o.mjs";
1
+ export { t as and, n as arrayContained, r as arrayContains, i as arrayContainsAll, a as arrayContainsAny, o as contains, s as endsWith, c as eq, u as gt, d as gte, f as has, p as ilike, m as inArray, h as isMissing, g as isNull, _ as isUndefined, v as like, y as lt, b as lte, x as ne, S as not, C as notIlike, w as notInArray, T as notLike, E as or, O as startsWith } from "./operators-D-w1-gDg.mjs";
@@ -1,4 +1,4 @@
1
- import { S as VariantOf, _ as RelationshipsOf, g as Relationship, h as QueryData, i as CollectionRegistry, m as PayloadsOf } from "./types-DbFfF6JE.mjs";
1
+ import { C as UnavailableRelationshipsOf, E as VariantOf, S as UnavailableRelationship, _ as Relationship, g as QueryData, h as PayloadsOf, i as CollectionRegistry, v as RelationshipsOf, x as SelectedData } from "./types-WOsFmNnN.mjs";
2
2
  //#region src/query/plan.d.ts
3
3
  type PredicateOperator = "eq" | "ne" | "gt" | "gte" | "lt" | "lte" | "in-array" | "not-in-array" | "contains" | "starts-with" | "ends-with" | "like" | "ilike" | "array-contains" | "array-contains-any" | "array-contains-all" | "array-contained" | "is-null" | "is-undefined" | "is-missing";
4
4
  type Expression = {
@@ -37,6 +37,26 @@ type QueryPlan = {
37
37
  adjacent?: AdjacentPlan;
38
38
  /** Payloads to attach under `_payloads` after filtering, ordering, and pagination. */
39
39
  payloads?: Readonly<Record<string, PlanPayload>>;
40
+ /** A root `paginate()` request; plans that carry it never set `limit` or `offset`. */
41
+ paginate?: PaginatePlan;
42
+ };
43
+ /**
44
+ * A page-number or cursor page request. Cursor keys are already decoded into the sort values of the
45
+ * boundary entry, in effective-order position, so backends never parse cursor strings.
46
+ */
47
+ type PaginatePlan = {
48
+ mode: "page";
49
+ page: number;
50
+ size: number;
51
+ } | {
52
+ mode: "cursor";
53
+ size: number;
54
+ /** `after` pages forward from `key`; `before` pages backward to it. */
55
+ direction: "after" | "before";
56
+ /** Sort values of the boundary entry; omitted to start at the first or last entry. */
57
+ key?: readonly unknown[];
58
+ /** Whether the result includes the filtered total. */
59
+ total: boolean;
40
60
  };
41
61
  /**
42
62
  * Selects previous and next entries. `results` finds neighbors within the scope's own filtered and
@@ -51,6 +71,8 @@ type AdjacentPlan = {
51
71
  type PlanPayload = {
52
72
  variant: Readonly<Record<string, string>>;
53
73
  };
74
+ /** How a plan returns its results; `paginate` requires the plan's `paginate` request. */
75
+ type QueryTerminal = "all" | "first" | "count" | "paginate";
54
76
  /** Describes a payload that queries may request, without its resolver or schema. */
55
77
  type PayloadDescriptor = {
56
78
  variants: readonly string[];
@@ -59,9 +81,18 @@ type QueryRegistry = Record<string, {
59
81
  relationships: Record<string, Relationship>;
60
82
  /** Payload names available to queries; omitted when the collection has none. */
61
83
  payloads?: Record<string, PayloadDescriptor>;
84
+ /**
85
+ * Declared relationships whose target collection this query scope has not loaded, by name, with
86
+ * the target collection. Queries that use one report which collection to pass to `using()`.
87
+ */
88
+ unavailable?: Record<string, string>;
89
+ /** The declared `typeName`, which only generated declarations read. */
90
+ typeName?: string;
62
91
  }>;
63
92
  /** Explains the explicit-selection rule wherever a scope returns rows without selecting data. */
64
93
  declare const NO_SELECTION_MESSAGE: "Select fields, relationships, payloads, or adjacent entries before running this query.";
94
+ /** Explains why `paginate()` is unavailable once a query sets its own limit or offset. */
95
+ declare const PAGINATE_AFTER_LIMIT_MESSAGE: "paginate() sets the limit and offset itself; remove limit and offset from this query.";
65
96
  //#endregion
66
97
  //#region src/query/types.d.ts
67
98
  declare const queryType: unique symbol;
@@ -82,7 +113,9 @@ type RelationField<TargetFields> = {
82
113
  readonly [relationTarget]: TargetFields;
83
114
  };
84
115
  /** Fields and relationships available to a fluent `where()` callback. */
85
- type QueryFields<Registry extends CollectionRegistry, Name extends keyof Registry & string> = Fields<QueryData<Registry[Name]>> & { readonly [Key in keyof RelationshipsOf<Registry[Name]> & string]-?: RelationField<QueryFields<Registry, RelatedName$1<Registry, RelationOf<Registry, Name, Key>>>>; };
116
+ type QueryFields<Registry extends CollectionRegistry, Name extends keyof Registry & string> = Fields<QueryData<Registry[Name]>> & { readonly [Key in keyof RelationshipsOf<Registry[Name]> & string]-?: RelationField<QueryFields<Registry, RelatedName$1<Registry, RelationOf<Registry, Name, Key>>>>; } & { readonly [
117
+ /** A relationship into a collection the query has not loaded reads as the type error. */
118
+ Key in keyof UnavailableRelationshipsOf<Registry[Name]> & string]-?: UnavailableRelationship<Registry[Name], Key>; };
86
119
  type Scalar = string | number | bigint | boolean | Date | null | undefined;
87
120
  type ScalarField<Value> = Field$1<Value> & ([Value] extends [Scalar] ? unknown : never);
88
121
  type Ordered<Value> = NonNullable<Value> extends string ? unknown : NonNullable<Value> extends number ? unknown : NonNullable<Value> extends bigint ? unknown : NonNullable<Value> extends Date ? unknown : never;
@@ -108,28 +141,30 @@ type RelatedName$1<Registry extends CollectionRegistry, Relation extends Relatio
108
141
  /** Applies declared relationship cardinality, optional reference fields, and filtering to a result. */
109
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);
110
143
  type RelationValue<Definition, Relation extends Relationship, Configured> = RelationshipResult<Definition, Relation, ResultOf<Configured>, FilteredOf<Configured>>;
111
- 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> = QueryInfo<Result, Selected, Filtered> & {
112
- select<const Keys extends readonly [keyof QueryData<Registry[Name]> & string, ...(keyof QueryData<Registry[Name]> & string)[]]>(...keys: Keys): Query<Registry, Name, { [Key in Exclude<keyof Result, keyof QueryData<Registry[Name]>> | Keys[number]]: Key extends Keys[number] ? QueryData<Registry[Name]>[Key & keyof QueryData<Registry[Name]>] : Key extends keyof Result ? Result[Key] : never; }, true, Many, Root, Filtered, AllowAdjacent, Pagination>;
113
- where(configure: (fields: QueryFields<Registry, Name>) => Expression): Query<Registry, Name, Result, Selected, Many, Root, true, AllowAdjacent, Pagination>;
114
- 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>;
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>;
146
+ where(configure: (fields: QueryFields<Registry, Name>) => Expression): Query<Registry, Name, Result, Selected, Many, Root, true, AllowAdjacent, Pagination, Paged>;
147
+ 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
+ /** A relationship into a collection the query has not loaded reports which one to supply. */
149
+ with<Key extends keyof UnavailableRelationshipsOf<Registry[Name]> & string>(name: Key, configure: UnavailableRelationship<Registry[Name], Key>): never;
115
150
  } & (Many extends true ? {
116
- orderBy(field: SortKey<QueryData<Registry[Name]>>, direction?: "asc" | "desc"): Query<Registry, Name, Result, Selected, Many, Root, Filtered, AllowAdjacent, Pagination>;
151
+ orderBy(field: SortKey<QueryData<Registry[Name]>>, direction?: "asc" | "desc"): Query<Registry, Name, Result, Selected, Many, Root, Filtered, AllowAdjacent, Pagination, Paged>;
117
152
  } : {}) & (Many extends true ? Pagination extends true ? {
118
- limit(value: number): Query<Registry, Name, Result, Selected, Many, Root, Filtered, AllowAdjacent, Pagination>;
119
- offset(value: number): Query<Registry, Name, Result, Selected, Many, Root, Filtered, AllowAdjacent, Pagination>;
153
+ limit(value: number): Query<Registry, Name, Result, Selected, Many, Root, Filtered, AllowAdjacent, Pagination, true>;
154
+ offset(value: number): Query<Registry, Name, Result, Selected, Many, Root, Filtered, AllowAdjacent, Pagination, true>;
120
155
  } : {} : {}) & (AllowAdjacent extends true ? {
121
156
  /** Attaches a payload under `_payloads` after filtering, ordering, and pagination. */
122
- 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>;
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>;
123
158
  /** Selects neighbor fields within this scope's filtered and ordered results. */
124
159
  selectAdjacent<const Keys extends readonly [keyof QueryData<Registry[Name]> & string, ...(keyof QueryData<Registry[Name]> & string)[]]>(...fields: Keys): Query<Registry, Name, Result & {
125
- _adjacent: Adjacent<{ [Key in Keys[number]]: QueryData<Registry[Name]>[Key & keyof QueryData<Registry[Name]>]; }>;
126
- }, true, Many, Root, Filtered, AllowAdjacent, Pagination>;
160
+ _adjacent: Adjacent<SelectedData<QueryData<Registry[Name]>, Keys[number]>>;
161
+ }, true, Many, Root, Filtered, AllowAdjacent, Pagination, Paged>;
127
162
  selectAdjacent<Configured extends QueryInfo<unknown, true>>(configure: (siblings: Query<Registry, Name, {}, false, true, false, false, false, false>) => Configured): Query<Registry, Name, Result & {
128
163
  _adjacent: Adjacent<ResultOf<Configured>>;
129
- }, true, Many, Root, Filtered, AllowAdjacent, Pagination>;
164
+ }, true, Many, Root, Filtered, AllowAdjacent, Pagination, Paged>;
130
165
  selectAdjacent<const Options extends AdjacentOptions<Registry, Name>>(options: Options & CheckedOptions<Registry, Name, NoInfer<Options>, true, false, false>): Query<Registry, Name, Result & {
131
166
  _adjacent: Adjacent<SelectionResult<Registry, Name, Options["select"]>>;
132
- }, true, Many, Root, Filtered, AllowAdjacent, Pagination>;
167
+ }, true, Many, Root, Filtered, AllowAdjacent, Pagination, Paged>;
133
168
  } : {}) & (Root extends true ? {
134
169
  all: Selected extends true ? () => Promise<Simplify$1<Result>[]> : UnselectedTerminal;
135
170
  execute: Selected extends true ? () => Promise<Simplify$1<Result>[]> : UnselectedTerminal;
@@ -139,7 +174,79 @@ type Query<Registry extends CollectionRegistry, Name extends keyof Registry & st
139
174
  count: [Result] extends [never] ? () => Promise<number> : [Result] extends [{
140
175
  _payloads: unknown;
141
176
  }] ? 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
+ } : {});
183
+ /** `paginate()` after `limit()` or `offset()` reports why as its required `this` type. */
184
+ type PaginateAfterLimit = (this: typeof PAGINATE_AFTER_LIMIT_MESSAGE, ...arguments_: readonly unknown[]) => never;
185
+ /** The `paginate()` overloads for one item type. */
186
+ type Paginate<Item> = {
187
+ (options: PageOptions): Promise<NumberedPage<Item>>;
188
+ (options: CursorOptions & {
189
+ total: true;
190
+ }): Promise<CursorPage<Item, true>>;
191
+ (options: CursorOptions): Promise<CursorPage<Item>>;
192
+ };
193
+ /** Page-number pagination: `page` starts at 1 and `size` is the number of entries per page. */
194
+ type PageOptions = {
195
+ page: number;
196
+ size: number;
197
+ after?: never;
198
+ before?: never;
199
+ total?: never;
200
+ };
201
+ /**
202
+ * Cursor pagination. `after` continues from an earlier page's `endCursor`; `before` returns the
203
+ * entries just before a `startCursor`. A `null` cursor starts at the first entry (`after`) or the
204
+ * last entry (`before`). `total: true` adds the matching total.
205
+ */
206
+ type CursorOptions = {
207
+ size: number;
208
+ page?: never;
209
+ total?: boolean;
210
+ } & ({
211
+ after?: string | null;
212
+ before?: never;
213
+ } | {
214
+ before?: string | null;
215
+ after?: never;
216
+ });
217
+ /** Options accepted by `paginate()`. */
218
+ type PaginateOptions = PageOptions | CursorOptions;
219
+ /** Option names that `paginate()` reads alongside relational query options. */
220
+ type PaginateKey = keyof PageOptions;
221
+ /** One numbered page and where it sits among every matching entry. */
222
+ type NumberedPage<Item> = {
223
+ items: Item[];
224
+ /** Matching entries across every page. */
225
+ total: number;
226
+ page: number;
227
+ /** At least 1: the first page exists even when nothing matches. */
228
+ pageCount: number;
229
+ hasNextPage: boolean;
230
+ hasPreviousPage: boolean;
231
+ };
232
+ /** One cursor page. Both cursors are `null` when the page is empty. */
233
+ type CursorPage<Item, Total extends boolean = false> = {
234
+ items: Item[];
235
+ /** Pass as `before` for the previous page. */
236
+ startCursor: string | null;
237
+ /** Pass as `after` for the next page. */
238
+ endCursor: string | null;
239
+ hasNextPage: boolean;
240
+ hasPreviousPage: boolean;
241
+ } & (Total extends true ? {
242
+ total: number;
142
243
  } : {});
244
+ /** The page type `paginate()` returns for the given options. */
245
+ type PaginateResult<Item, Options> = Options extends {
246
+ page: number;
247
+ } ? NumberedPage<Item> : CursorPage<Item, Options extends {
248
+ total: true;
249
+ } ? true : false>;
143
250
  type Adjacent<Entry> = {
144
251
  previous: Entry | null;
145
252
  next: Entry | null;
@@ -197,9 +304,11 @@ type Selection<Registry extends CollectionRegistry, Name extends keyof Registry
197
304
  type ObjectRelationValue<Registry extends CollectionRegistry, Name extends keyof Registry & string, Key extends keyof RelationshipsOf<Registry[Name]>, Options> = Options extends {
198
305
  select: infer Projection;
199
306
  } ? RelationValue<Registry[Name], RelationOf<Registry, Name, Key>, QueryInfo<SelectionResult<Registry, RelatedName$1<Registry, RelationOf<Registry, Name, Key>>, Projection>, true, "where" extends keyof Options ? true : false>> : never;
200
- type SelectionResult<Registry extends CollectionRegistry, Name extends keyof Registry & string, Projection> = { -readonly [Key in keyof Projection as Projection[Key] extends false | undefined ? never : Key]-?: Key extends "_adjacent" ? Projection[Key] extends {
307
+ /** Data fields an object projection requests, excluding relationships and `_adjacent`. */
308
+ type ProjectedFields<Registry extends CollectionRegistry, Name extends keyof Registry & string, Projection> = Exclude<{ [Key in keyof Projection]-?: Projection[Key] extends false | undefined ? never : Key; }[keyof Projection], "_adjacent" | keyof RelationshipsOf<Registry[Name]>> & keyof QueryData<Registry[Name]>;
309
+ type SelectionResult<Registry extends CollectionRegistry, Name extends keyof Registry & string, Projection> = Simplify$1<{ -readonly [Key in keyof Projection as Projection[Key] extends false | undefined ? never : Key extends ProjectedFields<Registry, Name, Projection> ? never : Key]-?: Key extends "_adjacent" ? Projection[Key] extends {
201
310
  select: infer Neighbors;
202
- } ? Adjacent<SelectionResult<Registry, Name, Neighbors>> : never : Key extends keyof RelationshipsOf<Registry[Name]> ? ObjectRelationValue<Registry, Name, Key, Projection[Key]> : Key extends keyof QueryData<Registry[Name]> ? QueryData<Registry[Name]>[Key] : never; };
311
+ } ? Adjacent<SelectionResult<Registry, Name, Neighbors>> : never : Key extends keyof RelationshipsOf<Registry[Name]> ? ObjectRelationValue<Registry, Name, Key, Projection[Key]> : never; } & SelectedData<QueryData<Registry[Name]>, ProjectedFields<Registry, Name, Projection>>>;
203
312
  type CheckedField<Value, Filter> = Filter extends Scalar ? Filter : { [Operator in keyof Filter]: Operator extends keyof FieldFilter$1<Value> ? Operator extends "not" ? CheckedField<Value, Filter[Operator]> : Filter[Operator] : never; };
204
313
  type CheckedWhereList$1<Data, Filters extends readonly unknown[]> = { [Index in keyof Filters]: CheckedWhere$1<Data, Filters[Index]>; };
205
314
  type CheckedWhere$1<Data, Filter> = Filter extends ((...arguments_: never[]) => unknown) ? Filter : { [Key in keyof Filter]: Key extends "AND" | "OR" | "NOT" ? Filter[Key] extends readonly unknown[] ? CheckedWhereList$1<Data, Filter[Key]> : CheckedWhere$1<Data, Filter[Key]> : Key extends keyof Data ? CheckedField<Data[Key], Filter[Key]> : never; };
@@ -289,11 +398,13 @@ type RelationalQueryOptions<Registry extends CollectionRegistry, Name extends ke
289
398
  } & RelationalQueryOptions<Registry, Name, true, false, false>);
290
399
  payloads?: PayloadOptions<Registry, Name>;
291
400
  } : {});
401
+ /** Options accepted by relational `paginate()`: query options without limit or offset, plus a page request. */
402
+ type RelationalPaginateOptions<Registry extends CollectionRegistry, Name extends keyof Registry & string> = RelationalQueryOptions<Registry, Name, true, true, false> & PaginateOptions;
292
403
  /** Options accepted by the root count helper, whose result ignores sorting and pagination. */
293
404
  type RelationalCountOptions<Registry extends CollectionRegistry, Name extends keyof Registry & string> = Pick<RelationalQueryOptions<Registry, Name>, "where" | "orderBy" | "offset" | "limit">;
294
405
  type ColumnResult<Data, Options> = Options extends {
295
406
  columns: infer Columns;
296
- } ? { -readonly [Key in keyof Columns as Columns[Key] extends true ? Key : never]-?: Key extends keyof Data ? Data[Key] : never; } : {};
407
+ } ? SelectedData<Data, { [Key in keyof Columns]-?: Columns[Key] extends true ? Key : never; }[keyof Columns] & keyof Data> : {};
297
408
  /** Keys whose option values request something, as opposed to `false` or `undefined`. */
298
409
  type Requested<Map> = keyof { [Key in keyof Map as Map[Key] extends false | undefined ? never : Key]: unknown; };
299
410
  /** Whether options select fields, include relationships, request payloads, or select adjacency. */
@@ -331,9 +442,9 @@ type RelationalQueryResult<Registry extends CollectionRegistry, Name extends key
331
442
  type CheckedConditions<Value, Filters extends readonly unknown[]> = { [Index in keyof Filters]: CheckedCondition<Value, Filters[Index]>; };
332
443
  type CheckedCondition<Value, Filter> = Filter extends Scalar ? Filter : { [Key in keyof Filter]: Key extends "AND" | "OR" ? Filter[Key] extends readonly unknown[] ? CheckedConditions<Value, Filter[Key]> : never : Key extends "NOT" ? CheckedCondition<Value, Filter[Key]> : Key extends keyof FieldFilter<Value> ? Filter[Key] : never; };
333
444
  type CheckedWhereList<Registry extends CollectionRegistry, Name extends keyof Registry & string, Filters extends readonly unknown[]> = { [Index in keyof Filters]: CheckedWhere<Registry, Name, Filters[Index]>; };
334
- type CheckedWhere<Registry extends CollectionRegistry, Name extends keyof Registry & string, Filter> = { [Key in keyof Filter]: Key extends "AND" | "OR" | "NOT" ? Filter[Key] extends readonly unknown[] ? CheckedWhereList<Registry, Name, Filter[Key]> : CheckedWhere<Registry, Name, Filter[Key]> : Key extends "RAW" ? Filter[Key] : Key extends keyof RelationshipsOf<Registry[Name]> ? Filter[Key] extends boolean ? Filter[Key] : CheckedWhere<Registry, RelatedName<Registry, Name, Key>, Filter[Key]> : Key extends keyof QueryData<Registry[Name]> ? CheckedCondition<QueryData<Registry[Name]>[Key], Filter[Key]> : never; };
445
+ type CheckedWhere<Registry extends CollectionRegistry, Name extends keyof Registry & string, Filter> = { [Key in keyof Filter]: Key extends "AND" | "OR" | "NOT" ? Filter[Key] extends readonly unknown[] ? CheckedWhereList<Registry, Name, Filter[Key]> : CheckedWhere<Registry, Name, Filter[Key]> : Key extends "RAW" ? Filter[Key] : Key extends keyof RelationshipsOf<Registry[Name]> ? Filter[Key] extends boolean ? Filter[Key] : CheckedWhere<Registry, RelatedName<Registry, Name, Key>, Filter[Key]> : Key extends keyof QueryData<Registry[Name]> ? CheckedCondition<QueryData<Registry[Name]>[Key], Filter[Key]> : UnavailableRelationship<Registry[Name], Key>; };
335
446
  /** Rejects unknown options recursively, including options supplied through inferred variables. */
336
- type CheckedRelationalOptions<Registry extends CollectionRegistry, Name extends keyof Registry & string, Options, Many extends boolean = true, AllowAdjacent extends boolean = true, Pagination extends boolean = true> = { [Key in keyof Options]: Key extends "columns" ? { [Column in keyof Options[Key]]: Column extends keyof QueryData<Registry[Name]> ? Options[Key][Column] : never; } : Key extends "where" ? CheckedWhere<Registry, Name, Options[Key]> : Key extends "with" ? { [Related in keyof Options[Key]]: Related extends keyof RelationshipsOf<Registry[Name]> ? Options[Key][Related] extends false | undefined ? Options[Key][Related] : CheckedRelationalOptions<Registry, RelatedName<Registry, Name, Related>, Options[Key][Related], IsMany<Registry, Name, Related>, AllowAdjacent> & SelectionRequired<Options[Key][Related]> : never; } : Key extends "adjacent" ? AllowAdjacent extends true ? CheckedRelationalOptions<Registry, Name, Omit<Options[Key], "scope">, true, false, false> & SelectionRequired<Options[Key]> & (Options[Key] extends {
447
+ type CheckedRelationalOptions<Registry extends CollectionRegistry, Name extends keyof Registry & string, Options, Many extends boolean = true, AllowAdjacent extends boolean = true, Pagination extends boolean = true> = { [Key in keyof Options]: Key extends "columns" ? { [Column in keyof Options[Key]]: Column extends keyof QueryData<Registry[Name]> ? Options[Key][Column] : never; } : Key extends "where" ? CheckedWhere<Registry, Name, Options[Key]> : Key extends "with" ? { [Related in keyof Options[Key]]: Related extends keyof RelationshipsOf<Registry[Name]> ? Options[Key][Related] extends false | undefined ? Options[Key][Related] : CheckedRelationalOptions<Registry, RelatedName<Registry, Name, Related>, Options[Key][Related], IsMany<Registry, Name, Related>, AllowAdjacent> & SelectionRequired<Options[Key][Related]> : UnavailableRelationship<Registry[Name], Related>; } : Key extends "adjacent" ? AllowAdjacent extends true ? CheckedRelationalOptions<Registry, Name, Omit<Options[Key], "scope">, true, false, false> & SelectionRequired<Options[Key]> & (Options[Key] extends {
337
448
  scope: "results";
338
449
  } ? {
339
450
  scope: "results";
@@ -342,12 +453,19 @@ type CheckedRelationalOptions<Registry extends CollectionRegistry, Name extends
342
453
  } : {
343
454
  scope?: "collection";
344
455
  }) : never : Key extends "payloads" ? AllowAdjacent extends true ? { [Payload in keyof Options[Key]]: Payload extends keyof PayloadsOf<Registry[Name]> ? CheckedPayloadRequest<Options[Key][Payload], VariantOf<Registry[Name], Payload>> : never; } : never : Key extends "orderBy" ? Key extends keyof RelationalQueryOptions<Registry, Name, Many, AllowAdjacent, Pagination> ? { [Field in keyof Options[Key]]: Field extends keyof OrderBy<QueryData<Registry[Name]>> ? Options[Key][Field] : never; } : never : Key extends keyof RelationalQueryOptions<Registry, Name, Many, AllowAdjacent, Pagination> ? Options[Key] : never; };
345
- /** Typed per-collection query entry points that never expose source configuration at runtime. */
346
- type RelationalQueries<Registry extends CollectionRegistry> = string extends keyof Registry ? {} : { readonly [Name in keyof Registry & string]: {
456
+ /** One collection's relational queries: `findMany`, `findFirst`, and `paginate`. */
457
+ type RelationalCollectionQueries<Registry extends CollectionRegistry, Name extends keyof Registry & string> = {
347
458
  /** Returns matching rows with exactly the selected data. Options must select something. */
348
459
  findMany<const Options extends RelationalQueryOptions<Registry, Name>>(options: Options & CheckedRelationalOptions<Registry, Name, NoInfer<Options>> & SelectionRequired<NoInfer<Options>>): Promise<RelationalQueryResult<Registry, Name, Options>[]>;
349
460
  /** Returns the first matching row, or undefined when none matches. */
350
461
  findFirst<const Options extends RelationalQueryOptions<Registry, Name>>(options: Options & CheckedRelationalOptions<Registry, Name, NoInfer<Options>> & SelectionRequired<NoInfer<Options>>): Promise<RelationalQueryResult<Registry, Name, Options> | undefined>;
351
- }; };
462
+ /**
463
+ * Returns one page from one revision: `page` and `size` for page numbers, or `size` with
464
+ * `after`, `before`, or `total` for cursors. `limit` and `offset` are unavailable.
465
+ */
466
+ paginate<const Options extends RelationalPaginateOptions<Registry, Name>>(options: Options & CheckedRelationalOptions<Registry, Name, NoInfer<Omit<Options, PaginateKey>>, true, true, false> & SelectionRequired<NoInfer<Options>>): Promise<PaginateResult<RelationalQueryResult<Registry, Name, Options>, Options>>;
467
+ };
468
+ /** Typed per-collection query entry points that never expose source configuration at runtime. */
469
+ type RelationalQueries<Registry extends CollectionRegistry> = string extends keyof Registry ? {} : { readonly [Name in keyof Registry & string]: RelationalCollectionQueries<Registry, Name>; };
352
470
  //#endregion
353
- export { Where as _, RelationalWhere as a, QueryRegistry as b, Fields as c, QueryFields as d, RelationField as f, SelectionResult as g, Selection as h, RelationalQueryResult as i, Ordered as l, ScalarField as m, RelationalQueries as n, Adjacent as o, Scalar as p, RelationalQueryOptions as r, Field$1 as s, RelationalCountOptions as t, Query as u, Expression as v, QueryPlan as y };
471
+ export { Selection as C, QueryPlan as D, Expression as E, QueryRegistry as O, ScalarField as S, Where as T, PaginateResult as _, RelationalQueryOptions as a, RelationField as b, Adjacent as c, Field$1 as d, Fields as f, PaginateOptions as g, PageOptions as h, RelationalQueries as i, QueryTerminal as k, CursorOptions as l, Ordered as m, RelationalCountOptions as n, RelationalQueryResult as o, NumberedPage as p, RelationalPaginateOptions as r, RelationalWhere as s, RelationalCollectionQueries as t, CursorPage as u, Query as v, SelectionResult as w, Scalar as x, QueryFields as y };
@@ -0,0 +1,56 @@
1
+ import { s as ContentError } from "./collection-types-DhIxnCSM.mjs";
2
+ import { n as ContentRenderResult } from "./types-Dh-Sydyw.mjs";
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";
6
+ //#region src/runtime/revision.d.ts
7
+ type QueryRecord = Record<string, unknown> & {
8
+ id: string;
9
+ };
10
+ /** Custom payload loaders by payload name, entry ID, and canonical variant key. */
11
+ type CustomPayloadTable = ReadonlyMap<string, ReadonlyMap<string, ReadonlyMap<string, () => Promise<unknown>>>>;
12
+ /** The built-in side payload shared by an entry's `_body` and `_rendered` payloads. */
13
+ type BuiltinPayload = {
14
+ body?: string;
15
+ rendered?: ContentRenderResult;
16
+ markdown?: string;
17
+ };
18
+ type PreparedCollection = {
19
+ records: readonly QueryRecord[];
20
+ relationships: Record<string, Relationship>;
21
+ /** Lazy built-in payloads, present only for entries with a body or rendered output. */
22
+ builtins: ReadonlyMap<string, () => Promise<BuiltinPayload>>;
23
+ /**
24
+ * Lazy custom payloads by payload name, entry ID, and canonical variant key. A missing variant
25
+ * key means the entry has no such combination.
26
+ */
27
+ payloads?: CustomPayloadTable;
28
+ sources?: ReadonlyMap<string, string | undefined>;
29
+ };
30
+ type PreparedRevision = {
31
+ registry: QueryRegistry;
32
+ collections: Record<string, PreparedCollection>;
33
+ relations: Map<string, Map<string, readonly QueryRecord[]>>;
34
+ active: number;
35
+ /** Built-in payloads keyed by collection and ID; custom payloads also by name and variant. */
36
+ payloadCache: Map<string, Promise<unknown>>;
37
+ renderCache: Map<string, Promise<ContentRenderResult>>;
38
+ renderMarkdown?: (body: string) => Promise<ContentRenderResult>;
39
+ /** The only query style this revision's configuration allows; absent allows both. */
40
+ queryStyle?: QueryStyle;
41
+ execute?: (plan: QueryPlan, terminal: QueryTerminal, requests: PayloadRequest[]) => Promise<unknown>;
42
+ };
43
+ /** A projected row awaiting the payloads its query scope requested. */
44
+ type PayloadRequest = {
45
+ target: Record<string, unknown>;
46
+ collection: string;
47
+ id: string;
48
+ payloads: NonNullable<QueryPlan["payloads"]>;
49
+ };
50
+ type ContentState = {
51
+ revision?: PreparedRevision;
52
+ registry?: QueryRegistry;
53
+ error?: ContentError;
54
+ };
55
+ //#endregion
56
+ export { PreparedRevision as n, ContentState as t };
@@ -1,4 +1,4 @@
1
- import { a as ContentDiagnostic, o as ContentError, s as ContentErrorCode, t as Content } from "./content-CwTKqRs9.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-DhIxnCSM.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-eXENQJ0O.mjs";
4
- export { type Content, type ContentDiagnostic, ContentError, type ContentErrorCode, type ContentHeading, type ContentRenderResult, type QueryStyle, type QueryStyleDisabled };
3
+ import { n as QueryStyle, r as QueryStyleDisabled } from "./query-style-C2FcT1f9.mjs";
4
+ export { type AnyCollection, type Collection, type CollectionHandles, type ContentDiagnostic, ContentError, type ContentErrorCode, type ContentHeading, type ContentRenderResult, type LoadedRegistry, type QueryStyle, type QueryStyleDisabled };
@@ -4,6 +4,7 @@ import { StandardSchemaV1 } from "@standard-schema/spec";
4
4
  //#region src/collection/types.d.ts
5
5
  declare const builtinPayloadsType: unique symbol;
6
6
  declare const customPayloadsType: unique symbol;
7
+ declare const loadedCollectionsType: unique symbol;
7
8
  type Schema = StandardSchemaV1;
8
9
  type SchemaOutput<Value> = Value extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<Value> : Record<string, unknown>;
9
10
  /** One file or record that a `files` helper read, shaped like a loader entry. */
@@ -127,6 +128,11 @@ type CollectionDefinition<SourceSchema extends Schema | undefined = Schema | und
127
128
  payloads?: Record<string, PayloadDeclaration>;
128
129
  /** Directories, relative to the project root, that every entry's `files` helpers may read. */
129
130
  sharedFiles?: readonly string[];
131
+ /**
132
+ * The singular name in generated types, such as `"Category"` for `ContentCategory`. Defaults to
133
+ * the export name without one trailing `s`.
134
+ */
135
+ typeName?: string;
130
136
  };
131
137
  type CollectionRegistry = Record<string, CollectionDefinition>;
132
138
  type EntryData<Definition> = Definition extends {
@@ -137,12 +143,56 @@ type ComputedData<Definition> = Definition extends {
137
143
  } ? { [Name in keyof Computed]: Computed[Name] extends {
138
144
  schema: infer ResultSchema;
139
145
  } ? SchemaOutput<ResultSchema> : never; } : {};
146
+ /**
147
+ * An entry's ID, validated metadata, and computed fields. JSDoc comments on schema fields and
148
+ * computed field names carry through, so editors show them when hovering a field.
149
+ */
140
150
  type QueryData<Definition> = {
151
+ /** The entry's ID, unique within its collection. */
141
152
  id: string;
142
153
  } & EntryData<Definition> & ComputedData<Definition>;
154
+ /** Maps `Field` without reading `Data`'s modifiers, so every key is required. */
155
+ type PresentFields<Data, Field extends PropertyKey> = { [Key in Field]: Key extends keyof Data ? Data[Key] : never; };
156
+ /** Flattens an intersection into one mutable object type, keeping each property's declaration. */
157
+ type MutableObject<Value> = { -readonly [Key in keyof Value]: Value[Key]; };
158
+ /**
159
+ * The shape of `Data` when a query selects `Field`. Every selected key is present and mutable,
160
+ * and an optional field's value includes `undefined` instead of the key being optional.
161
+ *
162
+ * Each property stays linked to its declaration in `Data`, so JSDoc comments written on schema
163
+ * fields show when hovering a selected field, and go to definition opens the schema.
164
+ */
165
+ type SelectedData<Data, Field extends keyof Data> = MutableObject<PresentFields<Data, Field> & Pick<Data, Field>>;
166
+ /**
167
+ * Type-only marker recording which collections a query scope loaded. A marked definition offers
168
+ * only the relationships whose target is loaded; an unmarked one offers every relationship.
169
+ */
170
+ type LoadedMarker<Loaded> = {
171
+ readonly [loadedCollectionsType]?: Loaded;
172
+ };
173
+ /** The collections a marked definition's query scope loaded, or `unknown` when unmarked. */
174
+ type LoadedOf<Definition> = Definition extends {
175
+ readonly [loadedCollectionsType]?: infer Loaded;
176
+ } ? Loaded : unknown;
177
+ /** The declared relationships, keeping only those into `Loaded` when it is not `unknown`. */
178
+ type RelationshipsInto<Relationships, Loaded, Keep extends boolean> = unknown extends Loaded ? Keep extends true ? Relationships : {} : { [Name in keyof Relationships as (Relationships[Name] extends {
179
+ collection: Loaded;
180
+ } ? Keep : Keep extends true ? false : true) extends true ? Name : never]: Relationships[Name]; };
181
+ /** Relationships a query can follow: every declared one, or only those into loaded collections. */
143
182
  type RelationshipsOf<Definition> = Definition extends {
144
183
  relationships: infer Relationships;
145
- } ? Relationships : {};
184
+ } ? RelationshipsInto<Relationships, LoadedOf<Definition>, true> : {};
185
+ /**
186
+ * The type error for a declared relationship whose target collection the query has not loaded,
187
+ * or `never` for a name that is not such a relationship.
188
+ */
189
+ type UnavailableRelationship<Definition, Name> = Name extends keyof UnavailableRelationshipsOf<Definition> ? UnavailableRelationshipsOf<Definition>[Name] extends {
190
+ collection: infer Target extends string;
191
+ } ? `Relationship "${Name & string}" targets the ${Target} collection, which this query has not loaded. Pass it to using().` : never : never;
192
+ /** Declared relationships into collections the query scope has not loaded. */
193
+ type UnavailableRelationshipsOf<Definition> = Definition extends {
194
+ relationships: infer Relationships;
195
+ } ? RelationshipsInto<Relationships, LoadedOf<Definition>, false> : {};
146
196
  /**
147
197
  * Payloads that Markdown sources, `glob()`, and custom loaders provide for every entry.
148
198
  * Built-in names start with an underscore, so they never collide with custom payloads.
@@ -178,4 +228,4 @@ type PayloadData<Definition, Name extends keyof PayloadsOf<Definition>> = Payloa
178
228
  /** The variant keys a payload request must supply; empty for built-in and single-value payloads. */
179
229
  type VariantOf<Definition, Name> = Name extends keyof CustomPayloadsOf<Definition> ? CustomPayloadsOf<Definition>[Name] extends CustomPayload<unknown, infer Variant> ? Variant : {} : {};
180
230
  //#endregion
181
- export { VariantValues as C, VariantOf as S, RelationshipsOf as _, ComputedContext as a, VariantContext as b, EntryFiles as c, PayloadData as d, PayloadDeclaration as f, Relationship as g, QueryData as h, CollectionRegistry as i, FileEntry as l, PayloadsOf as m, BuiltinPayloads as n, CustomPayload as o, PayloadDefinition as p, CollectionDefinition as r, CustomPayloadMarker as s, BuiltinPayloadMarker as t, PayloadContext as u, Schema as v, VariantField as x, SchemaOutput as y };
231
+ export { UnavailableRelationshipsOf as C, VariantValues as D, VariantOf as E, UnavailableRelationship as S, VariantField as T, Relationship as _, ComputedContext as a, SchemaOutput as b, EntryFiles as c, PayloadContext as d, PayloadData as f, QueryData as g, PayloadsOf as h, CollectionRegistry as i, FileEntry as l, PayloadDefinition as m, BuiltinPayloads as n, CustomPayload as o, PayloadDeclaration as p, CollectionDefinition as r, CustomPayloadMarker as s, BuiltinPayloadMarker as t, LoadedMarker as u, RelationshipsOf as v, VariantContext as w, SelectedData as x, Schema as y };