content-query 0.1.0 → 0.2.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 +23 -1
- package/dist/{build-DSyoRxkZ.d.mts → build-DfKd2bpf.d.mts} +6 -5
- package/dist/{build-C5ef0kkR.mjs → build-Dxz7P1ur.mjs} +68 -30
- package/dist/build.d.mts +1 -1
- package/dist/build.mjs +1 -2
- package/dist/cli.d.mts +1 -1
- package/dist/cli.mjs +3 -3
- package/dist/computed.d.mts +19 -13
- package/dist/computed.mjs +6 -5
- package/dist/{content-Crag1Uch.d.mts → content-G69dPPuo.d.mts} +26 -16
- package/dist/{content-Dj2fQCJ5.mjs → content-N1gYshjg.mjs} +203 -77
- package/dist/{descriptor-Xbo9u5eR.d.mts → descriptor-D9Th5hzk.d.mts} +1 -2
- package/dist/{diagnostics-Dol6ANtq.mjs → diagnostics-Cu94mZhl.mjs} +16 -15
- package/dist/{dist-BQGVLZyl.mjs → dist-C7_FOzY_.mjs} +21 -12
- package/dist/{glob-DJmebCMM.mjs → glob-Bv0dpvCQ.mjs} +3 -3
- package/dist/index.d.mts +34 -16
- package/dist/index.mjs +7 -4
- package/dist/internal/markdown.d.mts +3 -5
- package/dist/internal/markdown.mjs +1 -2
- package/dist/internal.d.mts +12 -12
- package/dist/internal.mjs +7 -7
- package/dist/loaders.d.mts +5 -6
- package/dist/loaders.mjs +6 -5
- package/dist/{markdown-CZBL4Ux_.mjs → markdown-CHvBBJNa.mjs} +2 -2
- package/dist/next.d.mts +3 -4
- package/dist/next.mjs +2 -2
- package/dist/{operators-BQpLfJlx.mjs → operators-UqWUhKQJ.mjs} +2 -2
- package/dist/query.d.mts +22 -23
- package/dist/query.mjs +1 -2
- package/dist/{relational-types-DLniKi5S.d.mts → relational-types-DgK3rLuw.d.mts} +62 -29
- package/dist/{chunk-CJs4Fogs.mjs → rolldown-runtime-Bg1v5YCA.mjs} +2 -2
- package/dist/runtime.d.mts +2 -2
- package/dist/runtime.mjs +3 -4
- package/dist/{schema-CnCbMosm.mjs → schema-C5Str-E8.mjs} +1 -1
- package/dist/schema.d.mts +0 -2
- package/dist/schema.mjs +1 -2
- package/dist/{types-BPh2cBPs.d.mts → types-C-kdMblM.d.mts} +27 -6
- package/dist/{types-hmDOw87q.d.mts → types-ChaNhj42.d.mts} +14 -6
- package/dist/{validation-BvvjSUFc.mjs → validation-D_XdE1pF.mjs} +2 -2
- package/dist/{values-B7lo_2_x.mjs → values-BbTV4QT-.mjs} +109 -108
- package/dist/vite.d.mts +3 -5
- package/dist/vite.mjs +5 -5
- package/package.json +6 -4
- /package/dist/{errors-C8lWyiy2.mjs → errors-C2iuBM0G.mjs} +0 -0
- /package/dist/{github-slugger-BCAkwEOL.mjs → github-slugger-BMgR_Q0I.mjs} +0 -0
- /package/dist/{server-only-CIE-FHx0.mjs → server-only-CIJUlyL-.mjs} +0 -0
- /package/dist/{types-DcvHRmxO.d.mts → types-Dh-Sydyw.d.mts} +0 -0
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Use it with your existing framework. Your application owns its routes, layouts,
|
|
|
9
9
|
- **Typed schemas.** Validate metadata with bundled schema builders or any Standard Schema V1 provider, with inferred TypeScript types.
|
|
10
10
|
- **Flexible sources.** Load local files, use Astro-compatible content loaders, or write your own loader.
|
|
11
11
|
- **Queries and relationships.** Select fields, filter, sort, paginate, and load related entries with validated references.
|
|
12
|
-
- **Markdown rendering.**
|
|
12
|
+
- **Markdown rendering.** Request HTML, headings, and syntax highlighting as query payloads, loaded only when a query asks for them.
|
|
13
13
|
- **Framework integrations.** Use Vite, Next.js, or the standalone CLI, with content updates during development.
|
|
14
14
|
|
|
15
15
|
## Usage
|
|
@@ -54,10 +54,28 @@ const posts = await content
|
|
|
54
54
|
.from("posts")
|
|
55
55
|
.where((fields) => eq(fields.draft, false))
|
|
56
56
|
.orderBy("title");
|
|
57
|
+
|
|
58
|
+
const post = await content.query.posts.findFirst({
|
|
59
|
+
where: { id: posts[0]!.id },
|
|
60
|
+
payloads: { _rendered: true },
|
|
61
|
+
});
|
|
62
|
+
const { html, headings } = post!._payloads._rendered;
|
|
57
63
|
```
|
|
58
64
|
|
|
59
65
|
Add `.content-query/` to `.gitignore`. Follow the quick start for a complete walkthrough, or choose a framework example below.
|
|
60
66
|
|
|
67
|
+
## Migrating to 0.2
|
|
68
|
+
|
|
69
|
+
Version 0.2.0 removes `content.readBody()` and `content.render()`. Request bodies and rendered output as payloads in the query that loads the entry:
|
|
70
|
+
|
|
71
|
+
| 0.1 | 0.2 |
|
|
72
|
+
| ------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
73
|
+
| `await content.readBody("posts", id)` | `(await content.query.posts.findFirst({ where: { id }, payloads: { _body: true } }))?._payloads._body` |
|
|
74
|
+
| `await content.render("posts", id)` | `(await content.query.posts.findFirst({ where: { id }, payloads: { _rendered: true } }))?._payloads._rendered` |
|
|
75
|
+
| Fluent queries | Add `.withPayload("_rendered")` or `.withPayload("_body")` before the terminal. |
|
|
76
|
+
|
|
77
|
+
Built-in payload names start with an underscore: `_body` and `_rendered`. A query for a missing entry returns `undefined` instead of rejecting with `entry-not-found`, which no longer exists. JSON, YAML, and TOML sources and `file()` collections offer no payloads. Rebuild generated content after upgrading; 0.1 artifacts are rejected.
|
|
78
|
+
|
|
61
79
|
## Documentation
|
|
62
80
|
|
|
63
81
|
- [Quick start](https://github.com/yangshun/content-query/blob/main/website/content/guides/quick-start.md)
|
|
@@ -67,4 +85,8 @@ Add `.content-query/` to `.gitignore`. Follow the quick start for a complete wal
|
|
|
67
85
|
|
|
68
86
|
The Vite integration supports Vite 8, and the Next.js integration supports Next.js 16. Both integrations are optional; standalone builds and queries require neither framework.
|
|
69
87
|
|
|
88
|
+
Pass `contentQuery()` directly to Vite or Vite+'s `plugins` array. No `Plugin` type cast is needed; workspace consumers share the pinned toolchain versions in the dependency catalog.
|
|
89
|
+
|
|
90
|
+
Repository development checks package types and isolated consumers with [tsrs](https://github.com/oxc-project/tsrs). Run `vp run check` in the package directory for formatting, linting, and type checks; build tools use the shared TypeScript 6 compatibility package for its `6.0.3` JavaScript API. Native TypeScript `7.1.0-beta` is available at the repository root as `vp exec tsc`.
|
|
91
|
+
|
|
70
92
|
Released under the [MIT license](https://github.com/yangshun/content-query/blob/main/LICENSE).
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { s as InvalidRelationships, t as Content } from "./content-
|
|
3
|
-
|
|
1
|
+
import { i as CollectionRegistry } from "./types-C-kdMblM.mjs";
|
|
2
|
+
import { s as InvalidRelationships, t as Content } from "./content-G69dPPuo.mjs";
|
|
4
3
|
//#region src/preparation/session.d.ts
|
|
5
4
|
type ContentSession<Registry extends CollectionRegistry = {}> = {
|
|
6
5
|
content: Content<Registry>;
|
|
@@ -10,8 +9,10 @@ type ContentSession<Registry extends CollectionRegistry = {}> = {
|
|
|
10
9
|
//#region src/preparation/build.d.ts
|
|
11
10
|
/** Options shared by standalone content builds and host integrations. */
|
|
12
11
|
type BuildContentOptions = {
|
|
13
|
-
/** Exact config path; omission discovers one content-query.config.{ts,mts,cts,js,mjs,cjs} file. */
|
|
14
|
-
|
|
12
|
+
/** Exact config path; omission discovers one content-query.config.{ts,mts,cts,js,mjs,cjs} file. */
|
|
13
|
+
config?: string;
|
|
14
|
+
backend?: "in-memory" | "generated";
|
|
15
|
+
/** Generated backend output directory, relative to the application root; defaults to ./.content-query. */
|
|
15
16
|
outDir?: string;
|
|
16
17
|
watch?: boolean;
|
|
17
18
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { a as builtinLoader, i as validate, n as normalizeSnapshot, o as builtinPath, r as renderedPayload, t as isRecord } from "./validation-
|
|
2
|
-
import { i as groupDiagnostics, n as contentError, o as withContext, t as ContentError } from "./errors-
|
|
3
|
-
import { i as detach, l as valuePath, o as encode } from "./values-
|
|
4
|
-
import { t as assertServerOnly } from "./server-only-
|
|
5
|
-
import { a as CODEC_VERSION, i as prepareRelationships, n as createRuntime, r as isRelationship } from "./content-
|
|
6
|
-
import { t as renderMarkdown } from "./markdown-
|
|
1
|
+
import { a as builtinLoader, i as validate, n as normalizeSnapshot, o as builtinPath, r as renderedPayload, t as isRecord } from "./validation-D_XdE1pF.mjs";
|
|
2
|
+
import { i as groupDiagnostics, n as contentError, o as withContext, t as ContentError } from "./errors-C2iuBM0G.mjs";
|
|
3
|
+
import { i as detach, l as valuePath, o as encode } from "./values-BbTV4QT-.mjs";
|
|
4
|
+
import { t as assertServerOnly } from "./server-only-CIJUlyL-.mjs";
|
|
5
|
+
import { a as CODEC_VERSION, i as prepareRelationships, n as createRuntime, r as isRelationship } from "./content-N1gYshjg.mjs";
|
|
6
|
+
import { t as renderMarkdown } from "./markdown-CHvBBJNa.mjs";
|
|
7
7
|
import { access, link, mkdir, readFile, readdir, realpath, rm, rmdir, stat, writeFile } from "node:fs/promises";
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import { pathToFileURL } from "node:url";
|
|
@@ -33,7 +33,7 @@ function compactJson(source) {
|
|
|
33
33
|
async function revisionModules(revision, { payloadChunkBytes = 0 } = {}) {
|
|
34
34
|
const modules = /* @__PURE__ */ new Map();
|
|
35
35
|
const manifest = {
|
|
36
|
-
version:
|
|
36
|
+
version: 2,
|
|
37
37
|
codec: CODEC_VERSION,
|
|
38
38
|
collections: revision.registry
|
|
39
39
|
};
|
|
@@ -62,7 +62,7 @@ async function revisionModules(revision, { payloadChunkBytes = 0 } = {}) {
|
|
|
62
62
|
batch = [];
|
|
63
63
|
batchBytes = 0;
|
|
64
64
|
}
|
|
65
|
-
for (const [id, load] of collection.
|
|
65
|
+
for (const [id, load] of collection.builtins) {
|
|
66
66
|
const payload = await load();
|
|
67
67
|
const encoded = encode(payload);
|
|
68
68
|
needsRenderer ||= payload.markdown !== void 0;
|
|
@@ -87,7 +87,7 @@ async function revisionModules(revision, { payloadChunkBytes = 0 } = {}) {
|
|
|
87
87
|
const helpers = [...chunks.length ? ["createPayloadLoaders"] : [], ...compressedRecords ? ["decodeCompressed"] : []];
|
|
88
88
|
const loaders = helpers.length ? `import { ${helpers.join(", ")} } from "content-query/internal";\n` : "";
|
|
89
89
|
const loading = chunks.length ? `createPayloadLoaders([${chunks.join(",\n")}])` : `{ ${payloads.join(",\n")} }`;
|
|
90
|
-
const code = `${loaders}export default { records: ${compressedRecords ? `decodeCompressed(${compressedRecords})` : recordSource},
|
|
90
|
+
const code = `${loaders}export default { records: ${compressedRecords ? `decodeCompressed(${compressedRecords})` : recordSource}, builtins: ${loading} };\n`;
|
|
91
91
|
const index = `index-${digest(code)}.js`;
|
|
92
92
|
modules.set(`${directory}/${index}`, code);
|
|
93
93
|
modules.set(`${directory}/index.js`, `export { default } from ${JSON.stringify(`./${index}`)};\n`);
|
|
@@ -224,6 +224,37 @@ async function writeArtifacts(revision, outDir, config, { signal, prune = false
|
|
|
224
224
|
if (prune) await pruneArtifacts(outDir, modules);
|
|
225
225
|
}
|
|
226
226
|
//#endregion
|
|
227
|
+
//#region src/collection/registry.ts
|
|
228
|
+
/** Built-in payload names start with an underscore, which custom payload names cannot use. */
|
|
229
|
+
const builtinPayloads = {
|
|
230
|
+
_body: { variants: [] },
|
|
231
|
+
_rendered: { variants: [] }
|
|
232
|
+
};
|
|
233
|
+
/**
|
|
234
|
+
* Reports whether a collection can carry built-in `_body` and `_rendered` payloads.
|
|
235
|
+
* This depends only on the declaration, never on the entries a source currently contains.
|
|
236
|
+
*
|
|
237
|
+
* @param definition - A collection definition, including the `source` shorthand when used.
|
|
238
|
+
* @returns False for JSON, YAML, and TOML sources and `file()` loaders; true otherwise.
|
|
239
|
+
*/
|
|
240
|
+
function hasBuiltinPayloads(definition) {
|
|
241
|
+
const source = definition.source;
|
|
242
|
+
if (source !== void 0) return typeof source === "string" || source.type === "markdown";
|
|
243
|
+
return builtinLoader(definition.loader)?.kind !== "file";
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Builds the query registry that runtimes validate queries against, without resolvers or schemas.
|
|
247
|
+
*
|
|
248
|
+
* @param registry - The configured collection definitions.
|
|
249
|
+
* @returns Relationship and payload descriptors for each collection.
|
|
250
|
+
*/
|
|
251
|
+
function queryRegistry(registry) {
|
|
252
|
+
return Object.fromEntries(Object.entries(registry).map(([name, definition]) => [name, {
|
|
253
|
+
relationships: definition.relationships ?? {},
|
|
254
|
+
payloads: hasBuiltinPayloads(definition) ? { ...builtinPayloads } : {}
|
|
255
|
+
}]));
|
|
256
|
+
}
|
|
257
|
+
//#endregion
|
|
227
258
|
//#region src/preparation/prepare.ts
|
|
228
259
|
/** Prepares one immutable revision with loader-defined source metadata, aggregating failures. */
|
|
229
260
|
async function prepareRevision(registry, sources, cache, signal, development, sourceDiagnostics = [], parsedData) {
|
|
@@ -231,11 +262,15 @@ async function prepareRevision(registry, sources, cache, signal, development, so
|
|
|
231
262
|
const collections = Object.create(null);
|
|
232
263
|
for (const [collectionName, definition] of Object.entries(registry)) {
|
|
233
264
|
const records = [];
|
|
234
|
-
const
|
|
265
|
+
const builtins = /* @__PURE__ */ new Map();
|
|
235
266
|
const computed = definition.computed ?? {};
|
|
236
267
|
const relationships = definition.relationships ?? {};
|
|
237
268
|
const declarationNames = [...Object.keys(computed), ...Object.keys(relationships)];
|
|
238
|
-
if (declarationNames.some((name) =>
|
|
269
|
+
if (declarationNames.some((name) => [
|
|
270
|
+
"id",
|
|
271
|
+
"_adjacent",
|
|
272
|
+
"_payloads"
|
|
273
|
+
].includes(name)) || new Set(declarationNames).size !== declarationNames.length || Object.values(relationships).some((relation) => !isRelationship(relation))) {
|
|
239
274
|
diagnostics.push({
|
|
240
275
|
code: "validation-error",
|
|
241
276
|
operation: "build",
|
|
@@ -261,7 +296,7 @@ async function prepareRevision(registry, sources, cache, signal, development, so
|
|
|
261
296
|
const previous = cache.get(key);
|
|
262
297
|
if (signature !== void 0 && previous?.signature === signature && previous.definition === definition) {
|
|
263
298
|
records.push(previous.record);
|
|
264
|
-
if (Object.keys(previous.payload).length)
|
|
299
|
+
if (Object.keys(previous.payload).length) builtins.set(entry.id, async () => previous.payload);
|
|
265
300
|
continue;
|
|
266
301
|
}
|
|
267
302
|
try {
|
|
@@ -269,7 +304,7 @@ async function prepareRevision(registry, sources, cache, signal, development, so
|
|
|
269
304
|
const validated = parsed?.has(entry.data) && parsed.get(entry.data) === definition.schema ? entry.data : await validate(definition.schema, entry.data, context);
|
|
270
305
|
signal.throwIfAborted();
|
|
271
306
|
if (!isRecord(validated)) throw new Error("Validated metadata must be a record of named fields.");
|
|
272
|
-
if (Object.hasOwn(validated, "id") || Object.hasOwn(validated, "_adjacent") || declarationNames.some((name) => Object.hasOwn(validated, name))) throw new Error("Metadata, computed fields, relationships, and reserved id/_adjacent must have distinct names.");
|
|
307
|
+
if (Object.hasOwn(validated, "id") || Object.hasOwn(validated, "_adjacent") || Object.hasOwn(validated, "_payloads") || declarationNames.some((name) => Object.hasOwn(validated, name))) throw new Error("Metadata, computed fields, relationships, and reserved id/_adjacent/_payloads must have distinct names.");
|
|
273
308
|
const metadata = detach(validated);
|
|
274
309
|
const values = Object.create(null);
|
|
275
310
|
const before = diagnostics.length;
|
|
@@ -306,16 +341,18 @@ async function prepareRevision(registry, sources, cache, signal, development, so
|
|
|
306
341
|
if (entry.body !== void 0 && (!markdown || descriptor?.kind !== "glob" || descriptor.options.retainBody !== false)) payload.body = entry.body;
|
|
307
342
|
const supplied = renderedPayload(entry);
|
|
308
343
|
if (supplied) payload.rendered = supplied;
|
|
309
|
-
else if (markdown && entry.body !== void 0)
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
344
|
+
else if (markdown && entry.body !== void 0) {
|
|
345
|
+
if (development || descriptor.options.deferRender === true) payload.markdown = entry.body;
|
|
346
|
+
else try {
|
|
347
|
+
payload.rendered = await renderMarkdown(entry.body);
|
|
348
|
+
} catch (cause) {
|
|
349
|
+
throw new ContentError({
|
|
350
|
+
...context,
|
|
351
|
+
code: "render-error",
|
|
352
|
+
message: "Eager Markdown compilation failed.",
|
|
353
|
+
cause
|
|
354
|
+
});
|
|
355
|
+
}
|
|
319
356
|
}
|
|
320
357
|
encode(payload);
|
|
321
358
|
cache.set(key, {
|
|
@@ -325,7 +362,7 @@ async function prepareRevision(registry, sources, cache, signal, development, so
|
|
|
325
362
|
payload
|
|
326
363
|
});
|
|
327
364
|
records.push(record);
|
|
328
|
-
if (Object.keys(payload).length)
|
|
365
|
+
if (Object.keys(payload).length) builtins.set(entry.id, async () => payload);
|
|
329
366
|
} catch (cause) {
|
|
330
367
|
const formatted = cause && typeof cause === "object" && "path" in cause && typeof cause.path === "string" ? cause.path : "";
|
|
331
368
|
const message = cause instanceof Error ? cause.message : "Entry preparation failed.";
|
|
@@ -340,14 +377,14 @@ async function prepareRevision(registry, sources, cache, signal, development, so
|
|
|
340
377
|
collections[collectionName] = {
|
|
341
378
|
records,
|
|
342
379
|
relationships,
|
|
343
|
-
|
|
380
|
+
builtins,
|
|
344
381
|
sources: new Map([...sources.get(collectionName)?.values() ?? []].map((entry) => [entry.id, entry.source]))
|
|
345
382
|
};
|
|
346
383
|
}
|
|
347
384
|
signal.throwIfAborted();
|
|
348
385
|
const revision = {
|
|
349
386
|
collections,
|
|
350
|
-
registry: Object.fromEntries(Object.
|
|
387
|
+
registry: queryRegistry(Object.fromEntries(Object.keys(collections).map((name) => [name, registry[name]]))),
|
|
351
388
|
relations: /* @__PURE__ */ new Map(),
|
|
352
389
|
active: 0,
|
|
353
390
|
payloadCache: /* @__PURE__ */ new Map(),
|
|
@@ -364,7 +401,7 @@ async function prepareRevision(registry, sources, cache, signal, development, so
|
|
|
364
401
|
/** Loads build-only configuration without retaining a previous config module value. */
|
|
365
402
|
async function loadRegistry(config) {
|
|
366
403
|
const filename = await realpath(config);
|
|
367
|
-
const dependencies = new Set([filename]);
|
|
404
|
+
const dependencies = /* @__PURE__ */ new Set([filename]);
|
|
368
405
|
const transpiler = createJiti(filename, {
|
|
369
406
|
moduleCache: false,
|
|
370
407
|
fsCache: false
|
|
@@ -707,7 +744,7 @@ function createStores(current, parsed, changed) {
|
|
|
707
744
|
function guardedWatcher(watcher, run, failed) {
|
|
708
745
|
const listeners = [];
|
|
709
746
|
let running = Promise.resolve();
|
|
710
|
-
const registration = new Set([
|
|
747
|
+
const registration = /* @__PURE__ */ new Set([
|
|
711
748
|
"on",
|
|
712
749
|
"once",
|
|
713
750
|
"addListener",
|
|
@@ -1151,7 +1188,7 @@ async function createSession(options) {
|
|
|
1151
1188
|
registry = nextRegistry;
|
|
1152
1189
|
loaders = nextLoaders;
|
|
1153
1190
|
configDependencies = loaded.dependencies;
|
|
1154
|
-
state.registry =
|
|
1191
|
+
state.registry = queryRegistry(registry);
|
|
1155
1192
|
candidates.clear();
|
|
1156
1193
|
cache.clear();
|
|
1157
1194
|
loadFailures.clear();
|
|
@@ -1166,7 +1203,8 @@ async function createSession(options) {
|
|
|
1166
1203
|
const signal = attempt.signal;
|
|
1167
1204
|
loading = names;
|
|
1168
1205
|
const acquired = await Promise.allSettled([...names].map(async (name) => {
|
|
1169
|
-
|
|
1206
|
+
const entries = await loaders.get(name).load({ signal });
|
|
1207
|
+
return [name, normalizeSnapshot(entries, name)];
|
|
1170
1208
|
}));
|
|
1171
1209
|
if (disposed || currentGeneration !== generation) return;
|
|
1172
1210
|
loading = /* @__PURE__ */ new Set();
|
package/dist/build.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as buildContent, r as ContentSession, t as BuildContentOptions } from "./build-
|
|
1
|
+
import { n as buildContent, r as ContentSession, t as BuildContentOptions } from "./build-DfKd2bpf.mjs";
|
|
2
2
|
export { type BuildContentOptions, type ContentSession, buildContent };
|
package/dist/build.mjs
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { buildContent };
|
|
1
|
+
export { n as buildContent } from "./build-Dxz7P1ur.mjs";
|
package/dist/cli.d.mts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {}
|
package/dist/cli.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as ContentError } from "./errors-
|
|
3
|
-
import { t as buildAtRoot } from "./build-
|
|
2
|
+
import { t as ContentError } from "./errors-C2iuBM0G.mjs";
|
|
3
|
+
import { t as buildAtRoot } from "./build-Dxz7P1ur.mjs";
|
|
4
4
|
import { cac } from "cac";
|
|
5
5
|
//#region package.json
|
|
6
|
-
var version = "0.
|
|
6
|
+
var version = "0.2.0";
|
|
7
7
|
//#endregion
|
|
8
8
|
//#region src/integrations/cli/arguments.ts
|
|
9
9
|
/** Validates one path option after CAC's automatic numeric conversion. */
|
package/dist/computed.d.mts
CHANGED
|
@@ -1,37 +1,43 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { p as MaybePromise } from "./types-ChaNhj42.mjs";
|
|
2
|
+
import { a as ComputedContext } from "./types-C-kdMblM.mjs";
|
|
3
3
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
4
|
-
|
|
5
4
|
//#region src/computed/text.d.ts
|
|
6
5
|
/** The syntax removed before computing text-derived fields; literal text preserves all characters. */
|
|
7
6
|
type TextFormat = "markdown" | "text" | "html";
|
|
8
7
|
/** Selects text from validated entry data or loader metadata instead of the captured body. */
|
|
9
8
|
type TextOptions<Data = Record<string, unknown>, Source = unknown> = {
|
|
10
|
-
/** Defaults to readBody(); undefined text is treated as an empty string. Async selectors are supported. */
|
|
9
|
+
/** Defaults to readBody(); undefined text is treated as an empty string. Async selectors are supported. */
|
|
10
|
+
from?: (context: ComputedContext<Data, Source>) => MaybePromise<string | undefined>;
|
|
11
|
+
/** Input syntax, defaulting to markdown. */
|
|
11
12
|
format?: TextFormat;
|
|
12
13
|
};
|
|
13
14
|
/** Controls the reading-speed estimate in addition to selecting its input text. */
|
|
14
15
|
type ReadingTimeOptions<Data = Record<string, unknown>, Source = unknown> = TextOptions<Data, Source> & {
|
|
15
|
-
/** A finite positive reading speed, defaulting to 200 words per minute. */
|
|
16
|
+
/** A finite positive reading speed, defaulting to 200 words per minute. */
|
|
17
|
+
wordsPerMinute?: number;
|
|
16
18
|
};
|
|
17
19
|
/** Controls the maximum excerpt size in addition to selecting its input text. */
|
|
18
20
|
type ExcerptOptions<Data = Record<string, unknown>, Source = unknown> = TextOptions<Data, Source> & {
|
|
19
|
-
/** A positive integer, defaulting to 160. Counts graphemes and includes the trailing ellipsis. */
|
|
21
|
+
/** A positive integer, defaulting to 160. Counts graphemes and includes the trailing ellipsis. */
|
|
22
|
+
maxLength?: number;
|
|
20
23
|
};
|
|
21
24
|
/** Creates a numeric computed field counting normalized text with reading-time's word boundaries. */
|
|
22
|
-
declare function wordCount<Data = Record<string, unknown>, Source = unknown>(options?: TextOptions<Data, Source>): {
|
|
23
|
-
schema: StandardSchemaV1<unknown, number>;
|
|
25
|
+
export declare function wordCount<Data = Record<string, unknown>, Source = unknown>(options?: TextOptions<Data, Source>): {
|
|
26
|
+
schema: StandardSchemaV1<unknown, number>;
|
|
27
|
+
/** Resolves selected text during preparation and honors cancellation around asynchronous input. */
|
|
24
28
|
resolve(context: ComputedContext<Data, Source>): Promise<number>;
|
|
25
29
|
};
|
|
26
30
|
/** Creates a numeric computed field estimating whole reading minutes, rounded up; empty text returns zero. */
|
|
27
|
-
declare function readingTime<Data = Record<string, unknown>, Source = unknown>(options?: ReadingTimeOptions<Data, Source>): {
|
|
28
|
-
schema: StandardSchemaV1<unknown, number>;
|
|
31
|
+
export declare function readingTime<Data = Record<string, unknown>, Source = unknown>(options?: ReadingTimeOptions<Data, Source>): {
|
|
32
|
+
schema: StandardSchemaV1<unknown, number>;
|
|
33
|
+
/** Resolves selected text during preparation and honors cancellation around asynchronous input. */
|
|
29
34
|
resolve(context: ComputedContext<Data, Source>): Promise<number>;
|
|
30
35
|
};
|
|
31
36
|
/** Creates a string computed field containing a normalized plain-text excerpt with a bounded length. */
|
|
32
|
-
declare function excerpt<Data = Record<string, unknown>, Source = unknown>(options?: ExcerptOptions<Data, Source>): {
|
|
33
|
-
schema: StandardSchemaV1<unknown, string>;
|
|
37
|
+
export declare function excerpt<Data = Record<string, unknown>, Source = unknown>(options?: ExcerptOptions<Data, Source>): {
|
|
38
|
+
schema: StandardSchemaV1<unknown, string>;
|
|
39
|
+
/** Resolves selected text during preparation and honors cancellation around asynchronous input. */
|
|
34
40
|
resolve(context: ComputedContext<Data, Source>): Promise<string>;
|
|
35
41
|
};
|
|
36
42
|
//#endregion
|
|
37
|
-
export {
|
|
43
|
+
export type { ExcerptOptions, ReadingTimeOptions, TextFormat, TextOptions };
|
package/dist/computed.mjs
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { t as assertServerOnly } from "./server-only-
|
|
2
|
-
import { n as number, r as string } from "./schema-
|
|
3
|
-
import { t as src_default } from "./dist-
|
|
1
|
+
import { t as assertServerOnly } from "./server-only-CIJUlyL-.mjs";
|
|
2
|
+
import { n as number, r as string } from "./schema-C5Str-E8.mjs";
|
|
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
6
|
//#region src/computed/plain-text.ts
|
|
7
7
|
const markdown = new src_default({ html: true });
|
|
8
|
-
const ignoredElements = new Set([
|
|
8
|
+
const ignoredElements = /* @__PURE__ */ new Set([
|
|
9
9
|
"head",
|
|
10
10
|
"script",
|
|
11
11
|
"style",
|
|
12
12
|
"template",
|
|
13
13
|
"noscript"
|
|
14
14
|
]);
|
|
15
|
-
const blockElements = new Set([
|
|
15
|
+
const blockElements = /* @__PURE__ */ new Set([
|
|
16
16
|
"address",
|
|
17
17
|
"article",
|
|
18
18
|
"aside",
|
|
@@ -96,6 +96,7 @@ function textField(schema, options, compute) {
|
|
|
96
96
|
if (from !== void 0 && typeof from !== "function") throw new TypeError("Text from must be a function.");
|
|
97
97
|
return {
|
|
98
98
|
schema,
|
|
99
|
+
/** Resolves selected text during preparation and honors cancellation around asynchronous input. */
|
|
99
100
|
async resolve(context) {
|
|
100
101
|
context.signal.throwIfAborted();
|
|
101
102
|
const input = await (from ? from(context) : context.readBody());
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { n as ContentRenderResult } from "./types-
|
|
2
|
-
import {
|
|
3
|
-
import { _ as QueryPlan, n as RelationalQueries, t as RelationalCountOptions, u as Query, v as QueryRegistry } from "./relational-types-
|
|
4
|
-
|
|
1
|
+
import { n as ContentRenderResult } from "./types-Dh-Sydyw.mjs";
|
|
2
|
+
import { c as Relationship, i as CollectionRegistry, l as RelationshipsOf, s as QueryData } from "./types-C-kdMblM.mjs";
|
|
3
|
+
import { _ as QueryPlan, n as RelationalQueries, t as RelationalCountOptions, u as Query, v as QueryRegistry } from "./relational-types-DgK3rLuw.mjs";
|
|
5
4
|
//#region src/collection/registry-types.d.ts
|
|
6
5
|
type Scalar = string | number | bigint | boolean | Date;
|
|
7
6
|
type Element<Value> = NonNullable<Value> extends readonly (infer Item)[] ? NonNullable<Item> : NonNullable<Value>;
|
|
@@ -9,16 +8,17 @@ type Compatible<Left, Right> = NonNullable<Left> extends readonly unknown[] ? No
|
|
|
9
8
|
type ScalarCompatible<Left, Right> = [Element<Left> | Element<Right>] extends [Scalar] ? [Element<Left>] extends [never] ? true : [Element<Right>] extends [never] ? true : [Extract<Element<Left>, Element<Right>> | Extract<Element<Right>, Element<Left>>] extends [never] ? false : true : false;
|
|
10
9
|
type CheckRelation<Registry extends CollectionRegistry, Name extends keyof Registry, Link extends Relationship> = Link["collection"] extends keyof Registry ? Link["from"] extends keyof QueryData<Registry[Name]> ? Link["to"] extends keyof QueryData<Registry[Link["collection"]]> ? Compatible<QueryData<Registry[Name]>[Link["from"]], QueryData<Registry[Link["collection"]]>[Link["to"]]> extends true ? never : "incompatible-relationship-fields" : "unknown-target-field" : "unknown-source-field" : "unknown-target-collection";
|
|
11
10
|
/** Registry-level relationship checks require the complete inferred collection map. */
|
|
12
|
-
type InvalidRelationships<Registry extends CollectionRegistry> = { [Name in keyof Registry]: { [Link in keyof RelationshipsOf<Registry[Name]>]: RelationshipsOf<Registry[Name]>[Link] extends Relationship ? CheckRelation<Registry, Name, RelationshipsOf<Registry[Name]>[Link]> : "invalid-relationship" }[keyof RelationshipsOf<Registry[Name]>] }[keyof Registry];
|
|
11
|
+
type InvalidRelationships<Registry extends CollectionRegistry> = { [Name in keyof Registry]: { [Link in keyof RelationshipsOf<Registry[Name]>]: RelationshipsOf<Registry[Name]>[Link] extends Relationship ? CheckRelation<Registry, Name, RelationshipsOf<Registry[Name]>[Link]> : "invalid-relationship"; }[keyof RelationshipsOf<Registry[Name]>]; }[keyof Registry];
|
|
13
12
|
//#endregion
|
|
14
13
|
//#region src/query/select.d.ts
|
|
15
14
|
/** A selection whose collection and selected result are inferred when from() is supplied. */
|
|
16
15
|
type SelectFrom<Registry extends CollectionRegistry, Keys extends readonly string[]> = {
|
|
17
|
-
/** Binds the collection and rejects selected keys that are absent from its prepared data type. */
|
|
16
|
+
/** Binds the collection and rejects selected keys that are absent from its prepared data type. */
|
|
17
|
+
from<Name extends keyof Registry & string>(collection: Name & (Exclude<Keys[number], keyof QueryData<Registry[NoInfer<Name>]>> extends never ? unknown : never)): Query<Registry, Name, Keys extends readonly [] ? QueryData<Registry[Name]> : { [Key in Keys[number]]-?: Key extends keyof QueryData<Registry[Name]> ? QueryData<Registry[Name]>[Key] : never; }, true>;
|
|
18
18
|
};
|
|
19
19
|
//#endregion
|
|
20
20
|
//#region src/shared/errors.d.ts
|
|
21
|
-
type ContentErrorCode = "
|
|
21
|
+
type ContentErrorCode = "variant-not-found" | "not-renderable" | "source-error" | "validation-error" | "duplicate-id" | "invalid-reference" | "invalid-query" | "artifact-error" | "render-error";
|
|
22
22
|
type ContentDiagnostic = {
|
|
23
23
|
code: ContentErrorCode;
|
|
24
24
|
message: string;
|
|
@@ -52,7 +52,8 @@ declare class ContentError extends Error {
|
|
|
52
52
|
type QueryRecord = Record<string, unknown> & {
|
|
53
53
|
id: string;
|
|
54
54
|
};
|
|
55
|
-
|
|
55
|
+
/** The built-in side payload shared by an entry's `_body` and `_rendered` payloads. */
|
|
56
|
+
type BuiltinPayload = {
|
|
56
57
|
body?: string;
|
|
57
58
|
rendered?: ContentRenderResult;
|
|
58
59
|
markdown?: string;
|
|
@@ -60,7 +61,8 @@ type ItemPayload = {
|
|
|
60
61
|
type PreparedCollection = {
|
|
61
62
|
records: readonly QueryRecord[];
|
|
62
63
|
relationships: Record<string, Relationship>;
|
|
63
|
-
payloads
|
|
64
|
+
/** Lazy built-in payloads, present only for entries with a body or rendered output. */
|
|
65
|
+
builtins: ReadonlyMap<string, () => Promise<BuiltinPayload>>;
|
|
64
66
|
sources?: ReadonlyMap<string, string | undefined>;
|
|
65
67
|
};
|
|
66
68
|
type PreparedRevision = {
|
|
@@ -68,10 +70,17 @@ type PreparedRevision = {
|
|
|
68
70
|
collections: Record<string, PreparedCollection>;
|
|
69
71
|
relations: Map<string, Map<string, readonly QueryRecord[]>>;
|
|
70
72
|
active: number;
|
|
71
|
-
payloadCache: Map<string, Promise<
|
|
73
|
+
payloadCache: Map<string, Promise<BuiltinPayload>>;
|
|
72
74
|
renderCache: Map<string, Promise<ContentRenderResult>>;
|
|
73
75
|
renderMarkdown?: (body: string) => Promise<ContentRenderResult>;
|
|
74
|
-
execute?: (plan: QueryPlan, terminal: "all" | "first" | "count") => Promise<unknown>;
|
|
76
|
+
execute?: (plan: QueryPlan, terminal: "all" | "first" | "count", requests: PayloadRequest[]) => Promise<unknown>;
|
|
77
|
+
};
|
|
78
|
+
/** A projected row awaiting the payloads its query scope requested. */
|
|
79
|
+
type PayloadRequest = {
|
|
80
|
+
target: Record<string, unknown>;
|
|
81
|
+
collection: string;
|
|
82
|
+
id: string;
|
|
83
|
+
payloads: NonNullable<QueryPlan["payloads"]>;
|
|
75
84
|
};
|
|
76
85
|
type ContentState = {
|
|
77
86
|
revision?: PreparedRevision;
|
|
@@ -81,11 +90,12 @@ type ContentState = {
|
|
|
81
90
|
//#endregion
|
|
82
91
|
//#region src/runtime/content.d.ts
|
|
83
92
|
type ContentMethods<Registry extends CollectionRegistry = {}> = {
|
|
84
|
-
/** Starts a SQL-ordered fluent query; field names are checked against the collection in from(). */
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
93
|
+
/** Starts a SQL-ordered fluent query; field names are checked against the collection in from(). */
|
|
94
|
+
select<const Keys extends readonly string[]>(...fields: Keys): SelectFrom<Registry, Keys>;
|
|
95
|
+
/** Collection-scoped relational queries with typed columns, relationships, payloads, and filters. */
|
|
96
|
+
readonly query: RelationalQueries<Registry>;
|
|
97
|
+
/** Counts matching entries before pagination, like the fluent count terminal. */
|
|
98
|
+
count<Name extends keyof Registry & string>(collection: Name, options?: RelationalCountOptions<Registry, Name>): Promise<number>;
|
|
89
99
|
};
|
|
90
100
|
type Content<Registry extends CollectionRegistry = {}> = InvalidRelationships<Registry> extends never ? ContentMethods<Registry> : {
|
|
91
101
|
readonly invalidRelationships: InvalidRelationships<Registry>;
|