content-query 0.0.2 → 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/LICENSE +21 -0
- package/README.md +91 -2
- package/dist/build-DfKd2bpf.d.mts +25 -0
- package/dist/build-Dxz7P1ur.mjs +1439 -0
- package/dist/build.d.mts +2 -0
- package/dist/build.mjs +1 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +97 -0
- package/dist/computed.d.mts +43 -0
- package/dist/computed.mjs +139 -0
- package/dist/content-G69dPPuo.d.mts +109 -0
- package/dist/content-N1gYshjg.mjs +1028 -0
- package/dist/descriptor-D9Th5hzk.d.mts +18 -0
- package/dist/diagnostics-Cu94mZhl.mjs +80 -0
- package/dist/dist-C7_FOzY_.mjs +5405 -0
- package/dist/errors-C2iuBM0G.mjs +72 -0
- package/dist/github-slugger-BMgR_Q0I.mjs +72 -0
- package/dist/glob-Bv0dpvCQ.mjs +159 -0
- package/dist/index.d.mts +78 -0
- package/dist/index.mjs +46 -0
- package/dist/internal/markdown.d.mts +5 -0
- package/dist/internal/markdown.mjs +1 -0
- package/dist/internal.d.mts +54 -0
- package/dist/internal.mjs +89 -0
- package/dist/loaders.d.mts +11 -0
- package/dist/loaders.mjs +86 -0
- package/dist/markdown-CHvBBJNa.mjs +54 -0
- package/dist/next.d.mts +20 -0
- package/dist/next.mjs +142 -0
- package/dist/operators-UqWUhKQJ.mjs +173 -0
- package/dist/query.d.mts +44 -0
- package/dist/query.mjs +1 -0
- package/dist/relational-types-DgK3rLuw.d.mts +278 -0
- package/dist/rolldown-runtime-Bg1v5YCA.mjs +34 -0
- package/dist/runtime.d.mts +3 -0
- package/dist/runtime.mjs +5 -0
- package/dist/schema-C5Str-E8.mjs +20 -0
- package/dist/schema.d.mts +11 -0
- package/dist/schema.mjs +1 -0
- package/dist/server-only-CIJUlyL-.mjs +7 -0
- package/dist/types-C-kdMblM.d.mts +69 -0
- package/dist/types-ChaNhj42.d.mts +143 -0
- package/dist/types-Dh-Sydyw.d.mts +20 -0
- package/dist/validation-D_XdE1pF.mjs +84 -0
- package/dist/values-BbTV4QT-.mjs +901 -0
- package/dist/vite.d.mts +6 -0
- package/dist/vite.mjs +296 -0
- package/package.json +92 -10
package/dist/loaders.mjs
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { o as builtinPath, s as builtinSymbol } from "./validation-D_XdE1pF.mjs";
|
|
2
|
+
import { i as groupDiagnostics, n as contentError, r as describeCause, t as ContentError } from "./errors-C2iuBM0G.mjs";
|
|
3
|
+
import { a as populateStore, i as recordData, n as coerceIdentity, r as parseData, t as glob } from "./glob-Bv0dpvCQ.mjs";
|
|
4
|
+
import { t as assertServerOnly } from "./server-only-CIJUlyL-.mjs";
|
|
5
|
+
import { readFile } from "node:fs/promises";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
//#region src/loaders/file.ts
|
|
9
|
+
/** Declares a multi-entry data file with typed source paths and record keys, plus an optional parser. */
|
|
10
|
+
function file(filename, options = {}) {
|
|
11
|
+
if (typeof filename !== "string" || !filename) throw new TypeError("file() requires a filename.");
|
|
12
|
+
const descriptor = {
|
|
13
|
+
kind: "file",
|
|
14
|
+
filename,
|
|
15
|
+
options
|
|
16
|
+
};
|
|
17
|
+
return {
|
|
18
|
+
[builtinSymbol]: descriptor,
|
|
19
|
+
name: "content-query/file",
|
|
20
|
+
async load(context) {
|
|
21
|
+
const entries = await loadFile(descriptor, fileURLToPath(context.config.root), context.signal ?? new AbortController().signal);
|
|
22
|
+
await populateStore(context, entries);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** Captures a complete multi-entry data source without applying collection validation. */
|
|
27
|
+
async function loadFile(descriptor, root, signal) {
|
|
28
|
+
const resolved = builtinPath(descriptor, root);
|
|
29
|
+
let parsed;
|
|
30
|
+
try {
|
|
31
|
+
const text = await readFile(resolved, {
|
|
32
|
+
encoding: "utf8",
|
|
33
|
+
signal
|
|
34
|
+
});
|
|
35
|
+
parsed = descriptor.options.parser ? await descriptor.options.parser(text) : parseData(text, resolved);
|
|
36
|
+
} catch (cause) {
|
|
37
|
+
signal.throwIfAborted();
|
|
38
|
+
throw contentError(cause, {
|
|
39
|
+
code: "source-error",
|
|
40
|
+
source: resolved,
|
|
41
|
+
message: `Could not load "${descriptor.filename}": ${describeCause(cause)}`,
|
|
42
|
+
cause
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
const array = Array.isArray(parsed);
|
|
46
|
+
if (!array && (!parsed || typeof parsed !== "object")) throw new ContentError({
|
|
47
|
+
code: "source-error",
|
|
48
|
+
source: resolved,
|
|
49
|
+
message: `"${descriptor.filename}" must parse to an array or keyed record.`
|
|
50
|
+
});
|
|
51
|
+
const diagnostics = [];
|
|
52
|
+
const entries = Object.entries(parsed).flatMap(([key, value]) => {
|
|
53
|
+
if (!array && key === "$schema" && typeof value === "string") return [];
|
|
54
|
+
const source = `${resolved}#${key}`;
|
|
55
|
+
try {
|
|
56
|
+
const data = recordData(value);
|
|
57
|
+
const identity = array ? data.id ?? data.slug : key;
|
|
58
|
+
if (identity == null || coerceIdentity(identity) === "") throw new Error("Array entries require a nonempty id or slug.");
|
|
59
|
+
const { id: _transportId, ...metadata } = data;
|
|
60
|
+
return [{
|
|
61
|
+
id: coerceIdentity(identity),
|
|
62
|
+
data: metadata,
|
|
63
|
+
source: array ? source : `${resolved}#${coerceIdentity(identity)}`,
|
|
64
|
+
sourceData: {
|
|
65
|
+
filePath: path.relative(root, resolved).split(path.sep).join("/"),
|
|
66
|
+
key
|
|
67
|
+
}
|
|
68
|
+
}];
|
|
69
|
+
} catch (cause) {
|
|
70
|
+
diagnostics.push({
|
|
71
|
+
code: "source-error",
|
|
72
|
+
source,
|
|
73
|
+
message: `Invalid entry "${key}" in "${descriptor.filename}": ${describeCause(cause)}`,
|
|
74
|
+
cause
|
|
75
|
+
});
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
if (diagnostics.length) throw groupDiagnostics(diagnostics);
|
|
80
|
+
return entries;
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region src/loaders.ts
|
|
84
|
+
assertServerOnly();
|
|
85
|
+
//#endregion
|
|
86
|
+
export { file, glob };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { t as BananaSlug } from "./github-slugger-BMgR_Q0I.mjs";
|
|
2
|
+
import { t as src_default } from "./dist-C7_FOzY_.mjs";
|
|
3
|
+
//#region src/runtime/markdown.ts
|
|
4
|
+
let highlighting;
|
|
5
|
+
let highlighter;
|
|
6
|
+
const markdown = new src_default({
|
|
7
|
+
html: true,
|
|
8
|
+
linkify: true
|
|
9
|
+
});
|
|
10
|
+
markdown.renderer.rules.fence = async (allTokens, index) => {
|
|
11
|
+
const token = allTokens[index];
|
|
12
|
+
const requested = token.info.trim().split(/\s+/)[0] || "text";
|
|
13
|
+
const { bundledLanguages, createHighlighter } = await (highlighting ??= import("shiki"));
|
|
14
|
+
const language = Object.hasOwn(bundledLanguages, requested) ? requested : "text";
|
|
15
|
+
highlighter ??= import("@shikijs/engine-javascript").then(({ createJavaScriptRegexEngine }) => createHighlighter({
|
|
16
|
+
engine: createJavaScriptRegexEngine(),
|
|
17
|
+
themes: ["github-light", "github-dark"],
|
|
18
|
+
langs: []
|
|
19
|
+
}));
|
|
20
|
+
const instance = await highlighter;
|
|
21
|
+
if (language !== "text" && !instance.getLoadedLanguages().includes(language)) await instance.loadLanguage(language);
|
|
22
|
+
return `<div class="language-${language}">${instance.codeToHtml(token.content, {
|
|
23
|
+
lang: language,
|
|
24
|
+
themes: {
|
|
25
|
+
light: "github-light",
|
|
26
|
+
dark: "github-dark"
|
|
27
|
+
},
|
|
28
|
+
defaultColor: "light"
|
|
29
|
+
})}</div>\n`;
|
|
30
|
+
};
|
|
31
|
+
/** Compiles captured Markdown using only library-owned, portable processing code. */
|
|
32
|
+
async function renderMarkdown(body) {
|
|
33
|
+
const slugger = new BananaSlug();
|
|
34
|
+
const tokens = markdown.parse(body, {});
|
|
35
|
+
const headings = [];
|
|
36
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
37
|
+
const token = tokens[index];
|
|
38
|
+
if (token.type !== "heading_open") continue;
|
|
39
|
+
const text = (tokens[index + 1]?.children ?? []).map((child) => child.type === "text" || child.type === "code_inline" || child.type === "image" ? child.content : child.type === "softbreak" || child.type === "hardbreak" ? " " : "").join("");
|
|
40
|
+
const slug = slugger.slug(text);
|
|
41
|
+
token.attrSet("id", slug);
|
|
42
|
+
headings.push({
|
|
43
|
+
depth: Number(token.tag.slice(1)),
|
|
44
|
+
slug,
|
|
45
|
+
text
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
html: await markdown.renderer.renderAsync(tokens, markdown.options, {}),
|
|
50
|
+
headings
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
export { renderMarkdown as t };
|
package/dist/next.d.mts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { t as BuildContentOptions } from "./build-DfKd2bpf.mjs";
|
|
2
|
+
import { NextConfig } from "next";
|
|
3
|
+
//#region src/integrations/next/plugin.d.ts
|
|
4
|
+
/** Content locations for Next.js; the adapter chooses generated storage and the watch lifecycle. */
|
|
5
|
+
type NextContentOptions = Pick<BuildContentOptions, "config" | "outDir">;
|
|
6
|
+
type ConfigContext = {
|
|
7
|
+
defaultConfig: NextConfig;
|
|
8
|
+
};
|
|
9
|
+
type ConfigInput = NextConfig | Promise<NextConfig> | ((phase: string, context: ConfigContext) => NextConfig | Promise<NextConfig>);
|
|
10
|
+
/**
|
|
11
|
+
* Builds content before Next compiles, retaining one managed watcher during `next dev`.
|
|
12
|
+
* Import the generated server entry from Server Components; Next's HMR observes its updates.
|
|
13
|
+
* Next's detached telemetry helper evaluates configuration without starting content sessions.
|
|
14
|
+
* @param nextConfig Existing object, promise, or phase-aware Next.js configuration.
|
|
15
|
+
* @param options Optional content configuration file and generated output directory.
|
|
16
|
+
* @returns An asynchronous Next.js configuration function that owns the content lifecycle.
|
|
17
|
+
*/
|
|
18
|
+
export declare function withContentQuery(nextConfig?: ConfigInput, options?: NextContentOptions): (phase: string, context: ConfigContext) => Promise<NextConfig>;
|
|
19
|
+
//#endregion
|
|
20
|
+
export type { NextContentOptions };
|
package/dist/next.mjs
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { c as contentDeclarations, i as resolveConfigPath, t as buildAtRoot } from "./build-Dxz7P1ur.mjs";
|
|
2
|
+
import { n as serializeContentError } from "./diagnostics-Cu94mZhl.mjs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { PHASE_DEVELOPMENT_SERVER, PHASE_PRODUCTION_BUILD } from "next/constants.js";
|
|
7
|
+
//#region src/integrations/next/publication.ts
|
|
8
|
+
/** Switches a development entry synchronously so error delivery cannot race a later publication. */
|
|
9
|
+
function writeEntry(filename, source) {
|
|
10
|
+
try {
|
|
11
|
+
if (readFileSync(filename, "utf8") === source) return;
|
|
12
|
+
} catch {}
|
|
13
|
+
mkdirSync(path.dirname(filename), { recursive: true });
|
|
14
|
+
const temporary = `${filename}.tmp-${process.pid}-${randomUUID()}`;
|
|
15
|
+
try {
|
|
16
|
+
writeFileSync(temporary, source);
|
|
17
|
+
renameSync(temporary, filename);
|
|
18
|
+
} finally {
|
|
19
|
+
rmSync(temporary, { force: true });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** Publishes fresh runtime imports and optionally prunes previous runs after a successful build. */
|
|
23
|
+
function publishDevelopment(outDir, config, state, { prune = false } = {}) {
|
|
24
|
+
if (!state.error) {
|
|
25
|
+
if (!state.revision) return;
|
|
26
|
+
const entry = path.join(outDir, "server.js");
|
|
27
|
+
const filename = `next-dev-${randomUUID()}.js`;
|
|
28
|
+
const runtime = path.join(outDir, filename);
|
|
29
|
+
writeEntry(runtime, readFileSync(entry, "utf8"));
|
|
30
|
+
writeEntry(entry, `export { content } from ${JSON.stringify(`./${filename}`)};\n`);
|
|
31
|
+
if (prune) pruneDevelopmentEntries(outDir, filename);
|
|
32
|
+
return runtime;
|
|
33
|
+
}
|
|
34
|
+
writeEntry(path.join(outDir, "package.json"), "{\"type\":\"module\",\"private\":true}\n");
|
|
35
|
+
writeEntry(path.join(outDir, "server.d.ts"), contentDeclarations(outDir, config));
|
|
36
|
+
writeEntry(path.join(outDir, "server.js"), `import { restoreContentError } from "content-query/internal";\nthrow restoreContentError(${JSON.stringify(serializeContentError(state.error))});\nexport const content = undefined;\n`);
|
|
37
|
+
}
|
|
38
|
+
/** Removes owned runtime entries from previous runs, preserving the current facade's target. */
|
|
39
|
+
function pruneDevelopmentEntries(outDir, keep) {
|
|
40
|
+
for (const filename of readdirSync(outDir)) if (filename !== keep && /^next-dev-[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}\.js$/.test(filename)) rmSync(path.join(outDir, filename), { force: true });
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/integrations/next/plugin.ts
|
|
44
|
+
const ownerSymbol = Symbol.for("content-query.next-sessions");
|
|
45
|
+
const global = globalThis;
|
|
46
|
+
const owner = global[ownerSymbol] ??= { entries: /* @__PURE__ */ new Map() };
|
|
47
|
+
/** Removes signal listeners when this adapter no longer owns any development sessions. */
|
|
48
|
+
function unobserveShutdown() {
|
|
49
|
+
if (owner.entries.size === 0 && owner.shutdown) {
|
|
50
|
+
process.removeListener("SIGINT", owner.shutdown);
|
|
51
|
+
process.removeListener("SIGTERM", owner.shutdown);
|
|
52
|
+
owner.shutdown = void 0;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** Registers one process-level cleanup while Next retains ownership of process termination. */
|
|
56
|
+
function observeShutdown() {
|
|
57
|
+
if (owner.shutdown) return;
|
|
58
|
+
owner.shutdown = () => {
|
|
59
|
+
const entries = [...owner.entries.values()];
|
|
60
|
+
owner.entries.clear();
|
|
61
|
+
unobserveShutdown();
|
|
62
|
+
Promise.allSettled(entries.map(async (entry) => (await entry.ready).dispose())).then((results) => {
|
|
63
|
+
for (const result of results) if (result.status === "rejected") console.error("[content-query] Could not close Next.js content session", result.reason);
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
process.once("SIGINT", owner.shutdown);
|
|
67
|
+
process.once("SIGTERM", owner.shutdown);
|
|
68
|
+
}
|
|
69
|
+
/** Closes a previous development session before a production build in the same process. */
|
|
70
|
+
async function stopDevelopment(root) {
|
|
71
|
+
const entry = owner.entries.get(root);
|
|
72
|
+
if (entry) {
|
|
73
|
+
owner.entries.delete(root);
|
|
74
|
+
unobserveShutdown();
|
|
75
|
+
await (await entry.ready).dispose();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Reuses a pending session and serializes replacement when content locations change. */
|
|
79
|
+
async function startDevelopment(root, options) {
|
|
80
|
+
const config = await resolveConfigPath(root, options.config);
|
|
81
|
+
const outDir = path.resolve(root, options.outDir ?? "./.content-query");
|
|
82
|
+
const key = JSON.stringify([config, outDir]);
|
|
83
|
+
const previous = owner.entries.get(root);
|
|
84
|
+
if (previous?.key === key) {
|
|
85
|
+
await previous.ready;
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const entry = {
|
|
89
|
+
key,
|
|
90
|
+
ready: Promise.resolve().then(async () => {
|
|
91
|
+
if (previous) await (await previous.ready).dispose();
|
|
92
|
+
let firstPublication = true;
|
|
93
|
+
return buildAtRoot(root, {
|
|
94
|
+
config,
|
|
95
|
+
outDir,
|
|
96
|
+
backend: "generated",
|
|
97
|
+
watch: true
|
|
98
|
+
}, (state) => {
|
|
99
|
+
if (publishDevelopment(outDir, config, state, { prune: firstPublication })) firstPublication = false;
|
|
100
|
+
});
|
|
101
|
+
})
|
|
102
|
+
};
|
|
103
|
+
owner.entries.set(root, entry);
|
|
104
|
+
observeShutdown();
|
|
105
|
+
try {
|
|
106
|
+
await entry.ready;
|
|
107
|
+
} catch (cause) {
|
|
108
|
+
if (owner.entries.get(root) === entry) {
|
|
109
|
+
owner.entries.delete(root);
|
|
110
|
+
unobserveShutdown();
|
|
111
|
+
}
|
|
112
|
+
throw cause;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Builds content before Next compiles, retaining one managed watcher during `next dev`.
|
|
117
|
+
* Import the generated server entry from Server Components; Next's HMR observes its updates.
|
|
118
|
+
* Next's detached telemetry helper evaluates configuration without starting content sessions.
|
|
119
|
+
* @param nextConfig Existing object, promise, or phase-aware Next.js configuration.
|
|
120
|
+
* @param options Optional content configuration file and generated output directory.
|
|
121
|
+
* @returns An asynchronous Next.js configuration function that owns the content lifecycle.
|
|
122
|
+
*/
|
|
123
|
+
function withContentQuery(nextConfig = {}, options = {}) {
|
|
124
|
+
const root = process.cwd();
|
|
125
|
+
return async (phase, context) => {
|
|
126
|
+
const config = await (typeof nextConfig === "function" ? nextConfig(phase, context) : nextConfig);
|
|
127
|
+
if (process.argv[1]?.replaceAll("\\", "/").endsWith("/telemetry/detached-flush.js")) return config;
|
|
128
|
+
if (phase === PHASE_DEVELOPMENT_SERVER) await startDevelopment(root, options);
|
|
129
|
+
else if (phase === PHASE_PRODUCTION_BUILD) {
|
|
130
|
+
await stopDevelopment(root);
|
|
131
|
+
await (await buildAtRoot(root, {
|
|
132
|
+
...options,
|
|
133
|
+
backend: "generated",
|
|
134
|
+
watch: false
|
|
135
|
+
})).dispose();
|
|
136
|
+
pruneDevelopmentEntries(path.resolve(root, options.outDir ?? "./.content-query"));
|
|
137
|
+
}
|
|
138
|
+
return config;
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
//#endregion
|
|
142
|
+
export { withContentQuery };
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { a as invalidQuery } from "./errors-C2iuBM0G.mjs";
|
|
2
|
+
import { c as isScalar, i as detach } from "./values-BbTV4QT-.mjs";
|
|
3
|
+
//#region src/query/operators.ts
|
|
4
|
+
/** Builds a checked, backend-independent predicate instead of a per-entry callback. */
|
|
5
|
+
function predicate(operator, field, value) {
|
|
6
|
+
if (!field || typeof field.name !== "string" || !field.name) invalidQuery("A filter must reference a query field.");
|
|
7
|
+
if ([
|
|
8
|
+
"in-array",
|
|
9
|
+
"not-in-array",
|
|
10
|
+
"array-contains-any",
|
|
11
|
+
"array-contains-all",
|
|
12
|
+
"array-contained"
|
|
13
|
+
].includes(operator)) {
|
|
14
|
+
if (!Array.isArray(value) || !value.every(isScalar)) invalidQuery("Choice arguments must be arrays of scalar values.");
|
|
15
|
+
} else if ([
|
|
16
|
+
"contains",
|
|
17
|
+
"starts-with",
|
|
18
|
+
"ends-with",
|
|
19
|
+
"like",
|
|
20
|
+
"ilike"
|
|
21
|
+
].includes(operator)) {
|
|
22
|
+
if (typeof value !== "string") invalidQuery("String predicates require a string argument.");
|
|
23
|
+
} else if (!operator.startsWith("is-") && !isScalar(value)) invalidQuery("This predicate requires a scalar argument.");
|
|
24
|
+
if ([
|
|
25
|
+
"gt",
|
|
26
|
+
"gte",
|
|
27
|
+
"lt",
|
|
28
|
+
"lte"
|
|
29
|
+
].includes(operator) && (value == null || typeof value === "boolean")) invalidQuery("Range predicates require a string, number, bigint, or date.");
|
|
30
|
+
return {
|
|
31
|
+
kind: "predicate",
|
|
32
|
+
operator,
|
|
33
|
+
field: field.name,
|
|
34
|
+
value: detach(value)
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** Matches a scalar using strict value/date equality. */
|
|
38
|
+
function eq(field, value) {
|
|
39
|
+
return predicate("eq", field, value);
|
|
40
|
+
}
|
|
41
|
+
/** Negates scalar equality. */
|
|
42
|
+
function ne(field, value) {
|
|
43
|
+
return predicate("ne", field, value);
|
|
44
|
+
}
|
|
45
|
+
/** Matches values greater than the supplied scalar. */
|
|
46
|
+
function gt(field, value) {
|
|
47
|
+
return predicate("gt", field, value);
|
|
48
|
+
}
|
|
49
|
+
/** Matches values greater than or equal to the supplied scalar. */
|
|
50
|
+
function gte(field, value) {
|
|
51
|
+
return predicate("gte", field, value);
|
|
52
|
+
}
|
|
53
|
+
/** Matches values less than the supplied scalar. */
|
|
54
|
+
function lt(field, value) {
|
|
55
|
+
return predicate("lt", field, value);
|
|
56
|
+
}
|
|
57
|
+
/** Matches values less than or equal to the supplied scalar. */
|
|
58
|
+
function lte(field, value) {
|
|
59
|
+
return predicate("lte", field, value);
|
|
60
|
+
}
|
|
61
|
+
/** Matches one of a list of scalar choices. */
|
|
62
|
+
function inArray(field, values) {
|
|
63
|
+
return predicate("in-array", field, values);
|
|
64
|
+
}
|
|
65
|
+
/** Matches none of a list of scalar choices. */
|
|
66
|
+
function notInArray(field, values) {
|
|
67
|
+
return predicate("not-in-array", field, values);
|
|
68
|
+
}
|
|
69
|
+
/** Matches a literal substring, without SQL wildcard interpretation. */
|
|
70
|
+
function contains(field, value) {
|
|
71
|
+
return predicate("contains", field, value);
|
|
72
|
+
}
|
|
73
|
+
/** Matches a literal string prefix. */
|
|
74
|
+
function startsWith(field, value) {
|
|
75
|
+
return predicate("starts-with", field, value);
|
|
76
|
+
}
|
|
77
|
+
/** Matches a literal string suffix. */
|
|
78
|
+
function endsWith(field, value) {
|
|
79
|
+
return predicate("ends-with", field, value);
|
|
80
|
+
}
|
|
81
|
+
/** Tests scalar membership in an array, ignoring holes. */
|
|
82
|
+
function arrayContains(field, value) {
|
|
83
|
+
return predicate("array-contains", field, value);
|
|
84
|
+
}
|
|
85
|
+
/** Tests whether any candidate belongs to an existing scalar array. */
|
|
86
|
+
function arrayContainsAny(field, values) {
|
|
87
|
+
return predicate("array-contains-any", field, values);
|
|
88
|
+
}
|
|
89
|
+
/** Tests whether every candidate belongs to an existing scalar array. */
|
|
90
|
+
function arrayContainsAll(field, values) {
|
|
91
|
+
return predicate("array-contains-all", field, values);
|
|
92
|
+
}
|
|
93
|
+
/** Checks explicit null independently from undefined and absence. */
|
|
94
|
+
function isNull(field) {
|
|
95
|
+
return predicate("is-null", field);
|
|
96
|
+
}
|
|
97
|
+
/** Checks undefined, including a missing property. */
|
|
98
|
+
function isUndefined(field) {
|
|
99
|
+
return predicate("is-undefined", field);
|
|
100
|
+
}
|
|
101
|
+
/** Checks own-property absence before projection. */
|
|
102
|
+
function isMissing(field) {
|
|
103
|
+
return predicate("is-missing", field);
|
|
104
|
+
}
|
|
105
|
+
/** Requires all child predicates; an empty conjunction is true. */
|
|
106
|
+
function and(...expressions) {
|
|
107
|
+
expressions.forEach(validateExpression);
|
|
108
|
+
return {
|
|
109
|
+
kind: "and",
|
|
110
|
+
expressions
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/** Requires any child predicate; an empty disjunction is false. */
|
|
114
|
+
function or(...expressions) {
|
|
115
|
+
expressions.forEach(validateExpression);
|
|
116
|
+
return {
|
|
117
|
+
kind: "or",
|
|
118
|
+
expressions
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/** Negates a predicate's boolean result. */
|
|
122
|
+
function not(expression) {
|
|
123
|
+
validateExpression(expression);
|
|
124
|
+
return {
|
|
125
|
+
kind: "not",
|
|
126
|
+
expressions: [expression]
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/** Checks the shape of expression trees even when TypeScript was bypassed. */
|
|
130
|
+
function validateExpression(expression) {
|
|
131
|
+
if (!expression || typeof expression !== "object") invalidQuery("where() must return a structured expression.");
|
|
132
|
+
if (expression.kind === "predicate") {
|
|
133
|
+
if (![
|
|
134
|
+
"eq",
|
|
135
|
+
"ne",
|
|
136
|
+
"gt",
|
|
137
|
+
"gte",
|
|
138
|
+
"lt",
|
|
139
|
+
"lte",
|
|
140
|
+
"in-array",
|
|
141
|
+
"not-in-array",
|
|
142
|
+
"contains",
|
|
143
|
+
"starts-with",
|
|
144
|
+
"ends-with",
|
|
145
|
+
"like",
|
|
146
|
+
"ilike",
|
|
147
|
+
"array-contains",
|
|
148
|
+
"array-contains-any",
|
|
149
|
+
"array-contains-all",
|
|
150
|
+
"array-contained",
|
|
151
|
+
"is-null",
|
|
152
|
+
"is-undefined",
|
|
153
|
+
"is-missing"
|
|
154
|
+
].includes(expression.operator)) invalidQuery("Unknown filter operator.");
|
|
155
|
+
predicate(expression.operator, { name: expression.field }, expression.value);
|
|
156
|
+
} else if (expression.kind === "relation") {
|
|
157
|
+
if ([
|
|
158
|
+
expression.collection,
|
|
159
|
+
expression.field,
|
|
160
|
+
expression.target
|
|
161
|
+
].some((name) => typeof name !== "string" || !name)) invalidQuery("Relationship filters require registered collection and relationship names.");
|
|
162
|
+
validateExpression(expression.expression);
|
|
163
|
+
} else if ([
|
|
164
|
+
"and",
|
|
165
|
+
"or",
|
|
166
|
+
"not"
|
|
167
|
+
].includes(expression.kind) && Array.isArray(expression.expressions)) {
|
|
168
|
+
if (expression.kind === "not" && expression.expressions.length !== 1) invalidQuery("not() requires one expression.");
|
|
169
|
+
expression.expressions.forEach(validateExpression);
|
|
170
|
+
} else invalidQuery("Invalid expression structure.");
|
|
171
|
+
}
|
|
172
|
+
//#endregion
|
|
173
|
+
export { not as _, contains as a, startsWith as b, gt as c, isMissing as d, isNull as f, ne as g, lte as h, arrayContainsAny as i, gte as l, lt as m, arrayContains as n, endsWith as o, isUndefined as p, arrayContainsAll as r, eq as s, and as t, inArray as u, notInArray as v, validateExpression as x, or as y };
|
package/dist/query.d.mts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { a as RelationalWhere, c as Fields, d as Scalar, f as ScalarField, g as Expression, h as Where, i as RelationalQueryResult, l as Ordered, m as SelectionResult, n as RelationalQueries, o as Adjacent, p as Selection, r as RelationalQueryOptions, s as Field, t as RelationalCountOptions } from "./relational-types-DgK3rLuw.mjs";
|
|
2
|
+
//#region src/query/operators.d.ts
|
|
3
|
+
/** Matches a scalar using strict value/date equality. */
|
|
4
|
+
export declare function eq<Value>(field: ScalarField<Value>, value: NoInfer<Value>): Expression;
|
|
5
|
+
/** Negates scalar equality. */
|
|
6
|
+
export declare function ne<Value>(field: ScalarField<Value>, value: NoInfer<Value>): Expression;
|
|
7
|
+
/** Matches values greater than the supplied scalar. */
|
|
8
|
+
export declare function gt<Value>(field: Field<Value> & Ordered<Value>, value: NoInfer<NonNullable<Value>>): Expression;
|
|
9
|
+
/** Matches values greater than or equal to the supplied scalar. */
|
|
10
|
+
export declare function gte<Value>(field: Field<Value> & Ordered<Value>, value: NoInfer<NonNullable<Value>>): Expression;
|
|
11
|
+
/** Matches values less than the supplied scalar. */
|
|
12
|
+
export declare function lt<Value>(field: Field<Value> & Ordered<Value>, value: NoInfer<NonNullable<Value>>): Expression;
|
|
13
|
+
/** Matches values less than or equal to the supplied scalar. */
|
|
14
|
+
export declare function lte<Value>(field: Field<Value> & Ordered<Value>, value: NoInfer<NonNullable<Value>>): Expression;
|
|
15
|
+
/** Matches one of a list of scalar choices. */
|
|
16
|
+
export declare function inArray<Value>(field: ScalarField<Value>, values: readonly NoInfer<Value>[]): Expression;
|
|
17
|
+
/** Matches none of a list of scalar choices. */
|
|
18
|
+
export declare function notInArray<Value>(field: ScalarField<Value>, values: readonly NoInfer<Value>[]): Expression;
|
|
19
|
+
/** Matches a literal substring, without SQL wildcard interpretation. */
|
|
20
|
+
export declare function contains<Value>(field: Field<Value> & (NonNullable<Value> extends string ? unknown : never), value: string): Expression;
|
|
21
|
+
/** Matches a literal string prefix. */
|
|
22
|
+
export declare function startsWith<Value>(field: Field<Value> & (NonNullable<Value> extends string ? unknown : never), value: string): Expression;
|
|
23
|
+
/** Matches a literal string suffix. */
|
|
24
|
+
export declare function endsWith<Value>(field: Field<Value> & (NonNullable<Value> extends string ? unknown : never), value: string): Expression;
|
|
25
|
+
/** Tests scalar membership in an array, ignoring holes. */
|
|
26
|
+
export declare function arrayContains<Value extends Scalar>(field: Field<readonly Value[] | null | undefined>, value: NoInfer<Value>): Expression;
|
|
27
|
+
/** Tests whether any candidate belongs to an existing scalar array. */
|
|
28
|
+
export declare function arrayContainsAny<Value extends Scalar>(field: Field<readonly Value[] | null | undefined>, values: readonly NoInfer<Value>[]): Expression;
|
|
29
|
+
/** Tests whether every candidate belongs to an existing scalar array. */
|
|
30
|
+
export declare function arrayContainsAll<Value extends Scalar>(field: Field<readonly Value[] | null | undefined>, values: readonly NoInfer<Value>[]): Expression;
|
|
31
|
+
/** Checks explicit null independently from undefined and absence. */
|
|
32
|
+
export declare function isNull<Value>(field: Field<Value>): Expression;
|
|
33
|
+
/** Checks undefined, including a missing property. */
|
|
34
|
+
export declare function isUndefined<Value>(field: Field<Value>): Expression;
|
|
35
|
+
/** Checks own-property absence before projection. */
|
|
36
|
+
export declare function isMissing<Value>(field: Field<Value>): Expression;
|
|
37
|
+
/** Requires all child predicates; an empty conjunction is true. */
|
|
38
|
+
export declare function and(...expressions: Expression[]): Expression;
|
|
39
|
+
/** Requires any child predicate; an empty disjunction is false. */
|
|
40
|
+
export declare function or(...expressions: Expression[]): Expression;
|
|
41
|
+
/** Negates a predicate's boolean result. */
|
|
42
|
+
export declare function not(expression: Expression): Expression;
|
|
43
|
+
//#endregion
|
|
44
|
+
export type { Adjacent, RelationalCountOptions as CountOptions, Expression, Field, Fields, RelationalQueryOptions as QueryOptions, RelationalQueryOptions, RelationalQueries, RelationalQueryResult, RelationalWhere, Selection, SelectionResult, Where };
|
package/dist/query.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { t as and, n as arrayContains, r as arrayContainsAll, i as arrayContainsAny, a as contains, o as endsWith, s as eq, c as gt, l as gte, u as inArray, d as isMissing, f as isNull, p as isUndefined, m as lt, h as lte, g as ne, _ as not, v as notInArray, y as or, b as startsWith } from "./operators-UqWUhKQJ.mjs";
|