kirby-types 0.7.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -20,16 +20,39 @@ yarn add -D kirby-types
20
20
  ## Basic Usage
21
21
 
22
22
  ```ts
23
- import type { KirbyQuery } from "kirby-types";
23
+ import type { KirbyQuery, ParseKirbyQuery } from "kirby-types";
24
24
 
25
25
  // Strictly typed query
26
26
  const query: KirbyQuery = 'page.children.filterBy("featured", true)';
27
27
 
28
- // Invalid queries will throw a type error
29
- let invalidQuery: KirbyQuery;
30
- invalidQuery = "unknown"; // Not a valid model
31
- invalidQuery = 'site("'; // Empty parentheses
32
- invalidQuery = 'site("value"'; // Missing closing parenthesis
28
+ // Parse query strings into structured objects
29
+ type BasicQuery = ParseKirbyQuery<"site">;
30
+ // Result: { model: "site"; chain: [] }
31
+
32
+ type DotNotationQuery = ParseKirbyQuery<"page.children.listed">;
33
+ // Result: {
34
+ // model: "page";
35
+ // chain: [
36
+ // { type: "property"; name: "children" },
37
+ // { type: "property"; name: "listed" }
38
+ // ]
39
+ // }
40
+
41
+ type MethodQuery = ParseKirbyQuery<'site("home")'>;
42
+ // Result: {
43
+ // model: "site";
44
+ // chain: [{ type: "method"; name: "site"; params: '"home"' }]
45
+ // }
46
+
47
+ type ComplexQuery =
48
+ ParseKirbyQuery<'page.children.filterBy("status", "published")'>;
49
+ // Result: {
50
+ // model: "page";
51
+ // chain: [
52
+ // { type: "property"; name: "children" },
53
+ // { type: "method"; name: "filterBy"; params: '"status", "published"' }
54
+ // ]
55
+ // }
33
56
  ```
34
57
 
35
58
  ## API
@@ -44,6 +67,7 @@ By clicking on a type name, you will be redirected to the corresponding TypeScri
44
67
 
45
68
  - [`KirbyQueryModel`](./src/query.d.ts) - Matches any supported KirbyQL model.
46
69
  - [`KirbyQuery`](./src/query.d.ts) - Matches a KirbyQL [`query`](https://getkirby.com/docs/guide/blueprints/query-language).
70
+ - [`ParseKirbyQuery`](./src/query.d.ts) - Parses a KirbyQL query string into a structured object with model and chain information.
47
71
 
48
72
  ### Blocks
49
73
 
package/index.d.ts CHANGED
@@ -1,13 +1,18 @@
1
- export { KirbyApiResponse } from "./src/api";
2
- export {
1
+ export type { KirbyApiResponse } from "./src/api";
2
+ export type {
3
3
  KirbyBlock,
4
4
  KirbyDefaultBlocks,
5
5
  KirbyDefaultBlockType,
6
6
  } from "./src/blocks";
7
- export {
7
+ export type {
8
8
  KirbyQueryRequest,
9
9
  KirbyQueryResponse,
10
10
  KirbyQuerySchema,
11
11
  } from "./src/kql";
12
- export { KirbyLayout, KirbyLayoutColumn } from "./src/layout";
13
- export { KirbyQuery, KirbyQueryChain, KirbyQueryModel } from "./src/query";
12
+ export type { KirbyLayout, KirbyLayoutColumn } from "./src/layout";
13
+ export type {
14
+ KirbyQuery,
15
+ KirbyQueryChain,
16
+ KirbyQueryModel,
17
+ ParseKirbyQuery,
18
+ } from "./src/query";
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "kirby-types",
3
3
  "type": "module",
4
- "version": "0.7.2",
5
- "packageManager": "pnpm@9.11.0",
4
+ "version": "1.0.0",
5
+ "packageManager": "pnpm@9.15.9",
6
6
  "description": "A collection of TypeScript types for the Kirby CMS",
7
7
  "author": "Johann Schopplich <hello@johannschopplich.com>",
8
8
  "license": "MIT",
@@ -25,7 +25,7 @@
25
25
  "exports": {
26
26
  ".": {
27
27
  "types": "./index.d.ts",
28
- "import": "./index.mjs"
28
+ "default": "./index.js"
29
29
  }
30
30
  },
31
31
  "types": "./index.d.ts",
@@ -42,11 +42,11 @@
42
42
  "test": "tsd -f \"test/**/*.test-d.ts\""
43
43
  },
44
44
  "devDependencies": {
45
- "@antfu/eslint-config": "^3.7.1",
46
- "bumpp": "^9.5.2",
47
- "eslint": "^9.10.0",
48
- "prettier": "^3.3.3",
49
- "tsd": "^0.31.2",
50
- "typescript": "^5.5.4"
45
+ "@antfu/eslint-config": "^3.16.0",
46
+ "bumpp": "^9.11.1",
47
+ "eslint": "^9.27.0",
48
+ "prettier": "^3.5.3",
49
+ "tsd": "^0.32.0",
50
+ "typescript": "^5.8.3"
51
51
  }
52
52
  }
package/src/query.d.ts CHANGED
@@ -1,3 +1,35 @@
1
+ /**
2
+ * Represents all supported model names in Kirby Query Language.
3
+ *
4
+ * This type includes all built-in Kirby models that can be used as the starting point
5
+ * for queries, plus any custom models you define.
6
+ *
7
+ * Built-in models include:
8
+ * - `site` - The site object
9
+ * - `page` - A page object
10
+ * - `user` - A user object
11
+ * - `file` - A file object
12
+ * - `collection` - A collection object
13
+ * - `kirby` - The Kirby instance
14
+ * - `content` - Content field data
15
+ * - `item` - Generic item in collections
16
+ * - `arrayItem` - An item in an array
17
+ * - `structureItem` - An item in a structure field
18
+ * - `block` - A block in the blocks field
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * // Using built-in models
23
+ * const siteModel: KirbyQueryModel = "site";
24
+ * const pageModel: KirbyQueryModel = "page";
25
+ *
26
+ * // Using with custom models
27
+ * type CustomModels = "product" | "category";
28
+ * const customModel: KirbyQueryModel<CustomModels> = "product";
29
+ * ```
30
+ *
31
+ * @template CustomModel - Additional custom model names to include
32
+ */
1
33
  export type KirbyQueryModel<CustomModel extends string = never> =
2
34
  | "collection"
3
35
  | "kirby"
@@ -12,22 +44,204 @@ export type KirbyQueryModel<CustomModel extends string = never> =
12
44
  | "block"
13
45
  | CustomModel;
14
46
 
15
- // For simple dot-based queries like `site.title`, `page.images`, etc.
47
+ /**
48
+ * Helper type for dot notation queries (e.g., `model.property.method`).
49
+ * @internal
50
+ */
16
51
  type DotNotationQuery<M extends string = never> =
17
52
  `${KirbyQueryModel<M>}.${string}`;
18
53
 
19
- // For function-based queries like `page("id")`, `user("name")`, etc.
54
+ /**
55
+ * Helper type for function notation queries (e.g., `model(params)` or `model(params).chain`).
56
+ * @internal
57
+ */
20
58
  type FunctionNotationQuery<M extends string = never> =
21
- `${KirbyQueryModel<M>}(${string})${string}`;
59
+ | `${KirbyQueryModel<M>}(${string})`
60
+ | `${KirbyQueryModel<M>}(${string})${string}`;
22
61
 
23
- // Combines the two types above to allow for either dot or function notation
62
+ /**
63
+ * Represents query chains that extend beyond a simple model name.
64
+ *
65
+ * This type covers all valid query patterns that start with a model and include
66
+ * additional property access or method calls:
67
+ *
68
+ * - **Dot notation**: `model.property.method()`
69
+ * - **Function calls**: `model(params)`
70
+ * - **Mixed chains**: `model(params).property.method()`
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * // Dot notation queries
75
+ * const dotQuery: KirbyQueryChain = "page.children.listed";
76
+ * const methodQuery: KirbyQueryChain = "page.children.filterBy('featured', true)";
77
+ *
78
+ * // Function notation queries
79
+ * const funcQuery: KirbyQueryChain = 'site("home")';
80
+ * const mixedQuery: KirbyQueryChain = 'page("blog").children.sortBy("date")';
81
+ *
82
+ * // With custom models
83
+ * type CustomModels = "product" | "category";
84
+ * const customQuery: KirbyQueryChain<CustomModels> = "product.price";
85
+ * ```
86
+ *
87
+ * @template M - Optional custom model names to include in validation
88
+ */
24
89
  export type KirbyQueryChain<M extends string = never> =
25
90
  | DotNotationQuery<M>
26
91
  | FunctionNotationQuery<M>;
27
92
 
93
+ /**
94
+ * Represents any valid Kirby Query Language (KQL) string.
95
+ *
96
+ * This is the main type for validating KQL queries. It accepts:
97
+ * - Simple model names (e.g., `"site"`, `"page"`)
98
+ * - Property chains (e.g., `"page.children.listed"`)
99
+ * - Method calls (e.g., `'site("home")'`, `'page.filterBy("status", "published")'`)
100
+ * - Complex mixed queries (e.g., `'page("blog").children.filterBy("featured", true).sortBy("date")'`)
101
+ *
102
+ * Invalid queries (unknown models, malformed syntax) will be rejected at the type level.
103
+ *
104
+ * @example
105
+ * ```ts
106
+ * // Valid queries
107
+ * const simpleQuery: KirbyQuery = "site";
108
+ * const propertyQuery: KirbyQuery = "page.children.listed";
109
+ * const methodQuery: KirbyQuery = 'page.filterBy("featured", true)';
110
+ * const complexQuery: KirbyQuery = 'site("home").children.sortBy("date", "desc").limit(10)';
111
+ *
112
+ * // Custom models
113
+ * type MyModels = "product" | "category";
114
+ * const customQuery: KirbyQuery<MyModels> = "product.price";
115
+ *
116
+ * // Invalid queries (these will cause TypeScript errors)
117
+ * // const invalid: KirbyQuery = "unknownModel"; // ❌ Unknown model
118
+ * // const invalid: KirbyQuery<MyModels> = "user"; // ❌ Not in custom models
119
+ * ```
120
+ *
121
+ * @template CustomModel - Optional custom model names to include alongside built-in models
122
+ */
28
123
  export type KirbyQuery<CustomModel extends string = never> =
29
124
  | KirbyQueryModel<CustomModel>
30
- // Ensures that it must match the pattern exactly, but not more broadly
31
125
  | (string extends KirbyQueryChain<CustomModel>
32
126
  ? never
33
127
  : KirbyQueryChain<CustomModel>);
128
+
129
+ /**
130
+ * Parses a Kirby Query Language (KQL) string into a structured object.
131
+ *
132
+ * This type breaks down a query string into its constituent parts:
133
+ * - `model`: The root model the query starts with (e.g., `site`, `page`, `user`)
134
+ * - `chain`: An array of query segments representing the method calls and property accesses
135
+ *
136
+ * @example
137
+ * ```ts
138
+ * // Basic model query
139
+ * type Basic = ParseKirbyQuery<"site">;
140
+ * // Result: { model: "site"; chain: [] }
141
+ *
142
+ * // Property chain query
143
+ * type Props = ParseKirbyQuery<"page.children.listed">;
144
+ * // Result: {
145
+ * // model: "page";
146
+ * // chain: [
147
+ * // { type: "property"; name: "children" },
148
+ * // { type: "property"; name: "listed" }
149
+ * // ]
150
+ * // }
151
+ *
152
+ * // Method call query
153
+ * type Method = ParseKirbyQuery<'site("home")'>;
154
+ * // Result: {
155
+ * // model: "site";
156
+ * // chain: [{ type: "method"; name: "site"; params: '"home"' }]
157
+ * // }
158
+ *
159
+ * // Complex query with mixed property and method calls
160
+ * type Complex = ParseKirbyQuery<'page.children.filterBy("featured", true).sortBy("date")'>;
161
+ * // Result: {
162
+ * // model: "page";
163
+ * // chain: [
164
+ * // { type: "property"; name: "children" },
165
+ * // { type: "method"; name: "filterBy"; params: '"featured", true' },
166
+ * // { type: "method"; name: "sortBy"; params: '"date"' }
167
+ * // ]
168
+ * // }
169
+ * ```
170
+ *
171
+ * @template T - The query string to parse
172
+ * @template M - Optional custom model names to include in validation
173
+ */
174
+ export type ParseKirbyQuery<T extends string, M extends string = never> =
175
+ // Case 1: Simple model name (e.g., "site", "page")
176
+ T extends KirbyQueryModel<M>
177
+ ? { model: T; chain: [] }
178
+ : // Case 2: Dot notation (e.g., "page.children.listed")
179
+ T extends `${infer Model}.${infer Chain}`
180
+ ? Model extends KirbyQueryModel<M>
181
+ ? { model: Model; chain: ParseQueryChain<Chain> }
182
+ : never
183
+ : // Case 3: Method call only (e.g., 'site("home")')
184
+ T extends `${infer Model}(${infer Params})`
185
+ ? Model extends KirbyQueryModel<M>
186
+ ? { model: Model; chain: [ParseQuerySegment<T>] }
187
+ : never
188
+ : // Case 4: Method call followed by chain (e.g., 'site("home").children')
189
+ T extends `${infer Model}(${infer Params})${infer Rest}`
190
+ ? Model extends KirbyQueryModel<M>
191
+ ? Rest extends `.${infer Chain}`
192
+ ? {
193
+ model: Model;
194
+ chain: [
195
+ ParseQuerySegment<`${Model}(${Params})`>,
196
+ ...ParseQueryChain<Chain>,
197
+ ];
198
+ }
199
+ : never
200
+ : never
201
+ : never;
202
+
203
+ /**
204
+ * Recursively parses a chain of query segments separated by dots.
205
+ *
206
+ * @example
207
+ * ```ts
208
+ * type Chain = ParseQueryChain<"children.listed.first">;
209
+ * // Result: [
210
+ * // { type: "property"; name: "children" },
211
+ * // { type: "property"; name: "listed" },
212
+ * // { type: "property"; name: "first" }
213
+ * // ]
214
+ * ```
215
+ *
216
+ * @internal
217
+ */
218
+ type ParseQueryChain<T extends string> =
219
+ T extends `${infer First}.${infer Rest}`
220
+ ? [ParseQuerySegment<First>, ...ParseQueryChain<Rest>]
221
+ : [ParseQuerySegment<T>];
222
+
223
+ /**
224
+ * Parses a single query segment to determine if it's a property access or method call.
225
+ *
226
+ * @example
227
+ * ```ts
228
+ * type Property = ParseQuerySegment<"children">;
229
+ * // Result: { type: "property"; name: "children" }
230
+ *
231
+ * type Method = ParseQuerySegment<'filterBy("status", "published")'>;
232
+ * // Result: { type: "method"; name: "filterBy"; params: '"status", "published"' }
233
+ * ```
234
+ *
235
+ * @internal
236
+ */
237
+ type ParseQuerySegment<T extends string> =
238
+ T extends `${infer Name}(${infer Params})`
239
+ ? {
240
+ type: "method";
241
+ name: Name;
242
+ params: Params;
243
+ }
244
+ : {
245
+ type: "property";
246
+ name: T;
247
+ };