@omgbase/core 0.19.0 → 0.23.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/dist/src/core/ids.d.ts +1 -1
- package/dist/src/core/ingest.d.ts +2 -0
- package/dist/src/core/ingest.js +13 -0
- package/dist/src/core/read/document.d.ts +13 -9
- package/dist/src/core/read/document.js +18 -8
- package/dist/src/core/store/archive.d.ts +196 -0
- package/dist/src/core/store/archive.js +606 -0
- package/dist/src/core/store/gc.d.ts +37 -4
- package/dist/src/core/store/gc.js +62 -28
- package/dist/src/core/store/rebuild.d.ts +2 -0
- package/dist/src/core/store/rebuild.js +8 -2
- package/dist/src/core/store/retention.d.ts +138 -0
- package/dist/src/core/store/retention.js +409 -0
- package/dist/src/core/store/schema.d.ts +3 -2
- package/dist/src/core/store/schema.js +30 -4
- package/dist/src/core/store/size.d.ts +24 -2
- package/dist/src/core/store/size.js +33 -3
- package/dist/src/core/store/store.d.ts +1 -0
- package/dist/src/core/store/store.js +53 -1
- package/dist/src/graph/history.d.ts +2 -0
- package/dist/src/graph/history.js +8 -4
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.js +3 -0
- package/dist/src/mcp/arguments.d.ts +10 -2
- package/dist/src/mcp/arguments.js +37 -3
- package/dist/src/mcp/errors.d.ts +1 -1
- package/dist/src/mcp/errors.js +4 -0
- package/dist/src/mcp/reference.d.ts +1 -1
- package/dist/src/mcp/reference.js +58 -8
- package/dist/src/mcp/server.js +51 -24
- package/dist/src/mutate/effects.d.ts +23 -5
- package/dist/src/mutate/effects.js +111 -22
- package/dist/src/mutate/known-ids.d.ts +19 -0
- package/dist/src/mutate/known-ids.js +47 -7
- package/dist/src/mutate/ops.d.ts +1 -0
- package/dist/src/mutate/ops.js +20 -1
- package/dist/src/mutate/select.d.ts +0 -11
- package/dist/src/mutate/select.js +33 -9
- package/dist/src/oqx-js/context.d.ts +9 -2
- package/dist/src/oqx-js/context.js +92 -30
- package/dist/src/oqx-js/dates.d.ts +105 -0
- package/dist/src/oqx-js/dates.js +427 -0
- package/dist/src/oqx-js/names.d.ts +40 -0
- package/dist/src/oqx-js/names.js +358 -0
- package/dist/src/oqx-js/planner.d.ts +3 -3
- package/dist/src/oqx-js/planner.js +30 -11
- package/dist/src/oqx-js/root-row.d.ts +15 -4
- package/dist/src/oqx-js/root-row.js +18 -10
- package/dist/src/oqx-js/run.d.ts +19 -1
- package/dist/src/oqx-js/run.js +97 -13
- package/dist/src/oqx-js/sql/translate.js +1 -1
- package/dist/src/spec-versions.d.ts +5 -5
- package/dist/src/spec-versions.js +4 -4
- package/dist/src/sync/observe.js +9 -3
- package/package.json +2 -2
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// pushes the hot predicates down; both must agree (conformance suite).
|
|
14
14
|
import { semantics } from "@omgbase/oqx";
|
|
15
15
|
import { storeIndexFor } from "./store-index.js";
|
|
16
|
-
import {
|
|
16
|
+
import { ROOTS, ROOT_NAMES, bareTargetMessage } from "./root-row.js";
|
|
17
17
|
import { COLS, FROM, ORDER, guards } from "./sql/scan.js";
|
|
18
18
|
import { detectRange } from "../core/store/properties.js";
|
|
19
19
|
import { docsRead } from "../core/read/document.js";
|
|
@@ -23,6 +23,7 @@ import { sanitizeFtsQuery } from "../search/fts-query.js";
|
|
|
23
23
|
import { cosineBytes } from "../core/vec.js";
|
|
24
24
|
import { ctxHashHex } from "../search/embeddings.js";
|
|
25
25
|
import { makeBlockContextResolver } from "../search/tasks.js";
|
|
26
|
+
import { dateFunction, dateMethod } from "./dates.js";
|
|
26
27
|
const TARGET = Symbol("oqx.target");
|
|
27
28
|
const ROOT_ROW = Symbol("oqx.rootRow");
|
|
28
29
|
const PROP_SOURCE = Symbol("oqx.propSource");
|
|
@@ -57,15 +58,31 @@ function tagAll(rows, t) {
|
|
|
57
58
|
// `hash`/`raw` is simply the engine's attribute (before this batch of 3.0 the
|
|
58
59
|
// attributes were `$`-prefixed and the bare spelling was an error, §9). A bare
|
|
59
60
|
// target name — `docs`, `links`, … — is a loud error of its own (root-row.ts).
|
|
61
|
+
// `revisions` on a document is a RELATION (its revision list, §1.2, 4.7) in
|
|
62
|
+
// `rel.docs` below, and names.ts's `RELATIONS.docs` knows it as one.
|
|
60
63
|
const DOC_ATTRIBUTES = new Set(["id", "path", "raw", "hash", "updated_at", "title", "tags", "format"]);
|
|
61
64
|
const BLOCK_ATTRIBUTES = new Set(["id", "path", "raw", "hash", "updated_at", "doc_id", "type", "text", "attrs", "depth", "ordinal"]);
|
|
62
65
|
const NODE_ATTRIBUTES = new Set(["id", "path", "doc_id", "block_id", "kind", "name", "value", "attrs"]);
|
|
63
66
|
const EDGE_ATTRIBUTES = new Set(["id", "path", "doc_id", "block_id", "via_id", "dst_id", "dst_path", "uri", "field", "dst_kind", "predicate", "provenance", "anchor"]);
|
|
67
|
+
// The history target (spec/surface §1.2, 4.7): a revision row's own columns
|
|
68
|
+
// under the names `docs_history` already uses (`rev`, `seq`, `commit`, `origin`,
|
|
69
|
+
// `actor`, `hash`), the commit's `ts` and `reason`, `is_current`, and (4.9)
|
|
70
|
+
// `archived` — the revision's bodies live in an archive segment (spec/store §7
|
|
71
|
+
// "Retention"; `docs_read { at }` reads through it when attached).
|
|
72
|
+
const REVISION_ATTRIBUTES = new Set(["id", "path", "rev", "seq", "doc_id", "commit", "ts", "origin", "actor", "reason", "hash", "is_current", "archived"]);
|
|
64
73
|
/** The engine attributes per kind (spec/surface §1.2 table). Exported so the
|
|
65
74
|
* translator's tests pin the two against each other. */
|
|
66
75
|
export const ATTRIBUTES = {
|
|
67
|
-
docs: DOC_ATTRIBUTES, blocks: BLOCK_ATTRIBUTES, nodes: NODE_ATTRIBUTES, links: EDGE_ATTRIBUTES,
|
|
76
|
+
docs: DOC_ATTRIBUTES, blocks: BLOCK_ATTRIBUTES, nodes: NODE_ATTRIBUTES, links: EDGE_ATTRIBUTES, revisions: REVISION_ATTRIBUTES,
|
|
68
77
|
};
|
|
78
|
+
// The revisions scan (spec/surface §1.1, 4.7): every revision of every document
|
|
79
|
+
// of the repository — tombstoned documents included, history is history — with
|
|
80
|
+
// its commit, in the keyset order `(path as of the revision, rev_id)`. The
|
|
81
|
+
// `revisions` table carries no `repo_id`, so the repository guard joins `docs`.
|
|
82
|
+
// `is_current` is `docs_history`'s `isCurrent`: the revision is the document's
|
|
83
|
+
// `current_rev`.
|
|
84
|
+
const REVISION_COLS = "r.rev_id, r.doc_id, r.seq, r.rendered_hash, r.path, r.commit_id, c.ts, c.origin, c.actor, c.reason, (d.current_rev = r.rev_id) AS is_current, (r.archive IS NOT NULL) AS archived";
|
|
85
|
+
const REVISION_FROM = "revisions r JOIN docs d ON d.doc_id = r.doc_id JOIN commits c ON c.commit_id = r.commit_id";
|
|
69
86
|
/** The source handles of a document (§1.2): `data` is the merged authored view
|
|
70
87
|
* (frontmatter + inline), the other two one source each. */
|
|
71
88
|
export const SOURCE_HANDLES = new Set(["data", "frontmatter", "inline"]);
|
|
@@ -87,11 +104,17 @@ export function makeStoreContext(store, repoId, opts = {}) {
|
|
|
87
104
|
return Object.values(r)[0];
|
|
88
105
|
};
|
|
89
106
|
// ---- root collections (the shared scan shape of sql/scan.ts: live rows of
|
|
90
|
-
// this repo, ordered for a stable (path, id) default
|
|
107
|
+
// this repo, ordered for a stable (path, id) default; the revisions scan is
|
|
108
|
+
// its own statement above, in the same (path, id) keyset order) -------------
|
|
91
109
|
const scan = (t) => {
|
|
110
|
+
if (t === "revisions")
|
|
111
|
+
return tagAll(all(`SELECT ${REVISION_COLS} FROM ${REVISION_FROM} WHERE d.repo_id = ? ORDER BY r.path, r.rev_id`, repoId), t);
|
|
92
112
|
const g = guards(t, repoId);
|
|
93
113
|
return tagAll(all(`SELECT ${COLS[t]} FROM ${FROM[t]} WHERE ${g.sql} ORDER BY ${ORDER[t]}`, ...g.params), t);
|
|
94
114
|
};
|
|
115
|
+
// A document's revisions (the `revisions` relation of a docs row, §1.2, 4.7):
|
|
116
|
+
// oldest first, as `docs_history` lists them.
|
|
117
|
+
const docRevisions = (docId) => tagAll(all(`SELECT ${REVISION_COLS} FROM ${REVISION_FROM} WHERE r.doc_id = ? ORDER BY r.seq`, docId), "revisions");
|
|
95
118
|
// A root scan is handed out LAZILY: an array (a `Proxy` over one — `Array.isArray`,
|
|
96
119
|
// iteration, indexing, `length`, rendering all see a plain array of tagged
|
|
97
120
|
// rows) that runs its SELECT on first use. The engine's optimizer asks
|
|
@@ -138,21 +161,14 @@ export function makeStoreContext(store, repoId, opts = {}) {
|
|
|
138
161
|
rootScans.set(proxy, { target: t, rows: fill });
|
|
139
162
|
return proxy;
|
|
140
163
|
};
|
|
141
|
-
const rootFns =
|
|
142
|
-
docs: () => lazyRoot("docs"), blocks: () => lazyRoot("blocks"), nodes: () => lazyRoot("nodes"), links: () => lazyRoot("links"),
|
|
143
|
-
};
|
|
164
|
+
const rootFns = Object.fromEntries(ROOT_NAMES.map((t) => [t, () => lazyRoot(t)]));
|
|
144
165
|
// The root row (spec/surface §1.1, 2.0; spec/oqx 0.18 `rootObject`): the
|
|
145
166
|
// repository, reached as `^$it` from a top-level row (`0^$it` from any depth).
|
|
146
|
-
// Its own enumerable keys are the
|
|
147
|
-
// `entries(^$it)` names them without running a scan and
|
|
148
|
-
// same handle as `^docs` — and `get` answers `id` with the
|
|
149
|
-
// attribute, not an entry). Tagged so `get` recognizes it.
|
|
150
|
-
const rootRow = Object.defineProperties({ [ROOT_ROW]: true }, {
|
|
151
|
-
docs: { enumerable: true, get: () => lazyRoot("docs") },
|
|
152
|
-
blocks: { enumerable: true, get: () => lazyRoot("blocks") },
|
|
153
|
-
nodes: { enumerable: true, get: () => lazyRoot("nodes") },
|
|
154
|
-
links: { enumerable: true, get: () => lazyRoot("links") },
|
|
155
|
-
});
|
|
167
|
+
// Its own enumerable keys are the five collections in `ROOT_NAMES` order —
|
|
168
|
+
// lazy getters, so `entries(^$it)` names them without running a scan and
|
|
169
|
+
// `^$it.docs` is the same handle as `^docs` — and `get` answers `id` with the
|
|
170
|
+
// repository id (an attribute, not an entry). Tagged so `get` recognizes it.
|
|
171
|
+
const rootRow = Object.defineProperties({ [ROOT_ROW]: true }, Object.fromEntries(ROOT_NAMES.map((t) => [t, { enumerable: true, get: () => lazyRoot(t) }])));
|
|
156
172
|
// ---- attrs / property decoding --------------------------------------------
|
|
157
173
|
const parseJson = (v) => {
|
|
158
174
|
if (typeof v !== "string")
|
|
@@ -298,6 +314,8 @@ export function makeStoreContext(store, repoId, opts = {}) {
|
|
|
298
314
|
docs: {
|
|
299
315
|
nodes: (r) => docNodesInOrder(r.doc_id, r.path),
|
|
300
316
|
blocks: (r) => tagAll(docBlocksPreorder(r.doc_id, r.path).map((x) => x.row), "blocks"),
|
|
317
|
+
// A document's revision list (spec/surface §1.2, 4.7), oldest first.
|
|
318
|
+
revisions: (r) => docRevisions(r.doc_id),
|
|
301
319
|
// The linked / linking DOCUMENTS are reached through the link rows (3.0
|
|
302
320
|
// follow-up, §9: the `out` / `in` relations are gone): `out_links collect
|
|
303
321
|
// distinct { from dst }` and `in_links collect distinct { from doc }`.
|
|
@@ -351,6 +369,7 @@ export function makeStoreContext(store, repoId, opts = {}) {
|
|
|
351
369
|
},
|
|
352
370
|
},
|
|
353
371
|
links: {},
|
|
372
|
+
revisions: {},
|
|
354
373
|
};
|
|
355
374
|
const jattr = (row, k) => {
|
|
356
375
|
const a = row.attrs;
|
|
@@ -417,6 +436,11 @@ export function makeStoreContext(store, repoId, opts = {}) {
|
|
|
417
436
|
return undefined;
|
|
418
437
|
return tag(one(`SELECT * FROM docs WHERE doc_id = ?`, row.dst_node), "docs");
|
|
419
438
|
};
|
|
439
|
+
// `doc` on a revision row (spec/surface §1.2, 4.7): the document's CURRENT
|
|
440
|
+
// live row — the revision's `path` may be an earlier one — and absent (the
|
|
441
|
+
// single-with-no-row marker, as `dst` on a dangling link) for a tombstoned
|
|
442
|
+
// document: its revisions are rows, `doc == null` tells them apart.
|
|
443
|
+
const revisionDoc = (row) => tag(one(`SELECT * FROM docs WHERE doc_id = ? AND deleted_commit IS NULL`, row.doc_id), "docs");
|
|
420
444
|
const owningBlock = (row) => {
|
|
421
445
|
if (!row.block_id)
|
|
422
446
|
return undefined;
|
|
@@ -501,6 +525,23 @@ export function makeStoreContext(store, repoId, opts = {}) {
|
|
|
501
525
|
case "dst_kind":
|
|
502
526
|
case "anchor": return row[name];
|
|
503
527
|
}
|
|
528
|
+
if (t === "revisions")
|
|
529
|
+
switch (name) {
|
|
530
|
+
// the `revisions` × `commits` columns under docs_history's names (spec/surface §1.2, 4.7).
|
|
531
|
+
case "id":
|
|
532
|
+
case "rev": return row.rev_id;
|
|
533
|
+
case "path": return referencePath(String(row.path));
|
|
534
|
+
case "seq": return row.seq;
|
|
535
|
+
case "doc_id": return row.doc_id;
|
|
536
|
+
case "commit": return row.commit_id;
|
|
537
|
+
case "ts": return row.ts;
|
|
538
|
+
case "origin": return row.origin;
|
|
539
|
+
case "actor": return row.actor ?? null;
|
|
540
|
+
case "reason": return row.reason ?? null;
|
|
541
|
+
case "hash": return hex(row.rendered_hash);
|
|
542
|
+
case "is_current": return row.is_current === 1 || row.is_current === true;
|
|
543
|
+
case "archived": return row.archived === 1 || row.archived === true;
|
|
544
|
+
}
|
|
504
545
|
return undefined;
|
|
505
546
|
};
|
|
506
547
|
// `tags` on a document: authored ∪ hashtags, each list as the property rule
|
|
@@ -561,8 +602,11 @@ export function makeStoreContext(store, repoId, opts = {}) {
|
|
|
561
602
|
return r;
|
|
562
603
|
if (t === "nodes" && key === "section")
|
|
563
604
|
return r;
|
|
564
|
-
// reach-through to owning entities,
|
|
565
|
-
//
|
|
605
|
+
// reach-through to owning entities, from a link row to its destination and
|
|
606
|
+
// from a revision to its document's current row — statically single
|
|
607
|
+
// relations: absent is the single-with-no-row marker.
|
|
608
|
+
if (t === "revisions" && key === "doc")
|
|
609
|
+
return revisionDoc(r) ?? ABSENT_SINGLE;
|
|
566
610
|
if (t !== "docs" && key === "doc")
|
|
567
611
|
return owningDoc(r) ?? ABSENT_SINGLE;
|
|
568
612
|
if (t === "nodes" && key === "block")
|
|
@@ -576,9 +620,9 @@ export function makeStoreContext(store, repoId, opts = {}) {
|
|
|
576
620
|
// A bare target name that is not one of this row's relations reads a
|
|
577
621
|
// property the row has none of (spec/surface §1.1, 2.0): loud, naming the
|
|
578
622
|
// root-row spelling. `docs`/`links` never get here (run.ts refuses them
|
|
579
|
-
// statically); `blocks` on a block or edge row
|
|
580
|
-
//
|
|
581
|
-
if (
|
|
623
|
+
// statically); `blocks` on a block or edge row, `nodes` on a node or edge
|
|
624
|
+
// row and `revisions` on anything but a document do.
|
|
625
|
+
if (ROOTS.has(key))
|
|
582
626
|
throw new FilterInvalid(bareTargetMessage(key, null), "OQX");
|
|
583
627
|
// the author's data.
|
|
584
628
|
if (t === "docs") {
|
|
@@ -652,8 +696,8 @@ export function makeStoreContext(store, repoId, opts = {}) {
|
|
|
652
696
|
}
|
|
653
697
|
};
|
|
654
698
|
const textMatch = (t, r, terms) => {
|
|
655
|
-
if (t === "links")
|
|
656
|
-
throw new FilterInvalid(
|
|
699
|
+
if (t === "links" || t === "revisions")
|
|
700
|
+
throw new FilterInvalid(`text(...) is not available on the ${t} target`, "10 §5");
|
|
657
701
|
const match = sanitizeFtsQuery(terms);
|
|
658
702
|
if (match === "")
|
|
659
703
|
return false;
|
|
@@ -676,7 +720,7 @@ export function makeStoreContext(store, repoId, opts = {}) {
|
|
|
676
720
|
return one(`SELECT vec FROM embeddings WHERE content_hash = ? AND ctx_hash = ? AND model = ?`, r.raw_hash, Buffer.from(ctxHashHex(ctx), "hex"), model);
|
|
677
721
|
};
|
|
678
722
|
const semanticScore = (t, r, phrase) => {
|
|
679
|
-
if (t === "nodes" || t === "links")
|
|
723
|
+
if (t === "nodes" || t === "links" || t === "revisions")
|
|
680
724
|
throw new FilterInvalid("semantic(...) is available on the docs and blocks targets", "OQX semantic");
|
|
681
725
|
const resolved = sv?.get(phrase);
|
|
682
726
|
if (!resolved)
|
|
@@ -771,10 +815,12 @@ export function makeStoreContext(store, repoId, opts = {}) {
|
|
|
771
815
|
return row.node_id;
|
|
772
816
|
if (t === "links")
|
|
773
817
|
return row.edge_id;
|
|
818
|
+
if (t === "revisions")
|
|
819
|
+
return row.rev_id;
|
|
774
820
|
return row;
|
|
775
821
|
};
|
|
776
822
|
return {
|
|
777
|
-
// The root scope's names (spec/oqx §2 step 5 at the root): the
|
|
823
|
+
// The root scope's names (spec/oqx §2 step 5 at the root): the five lazy
|
|
778
824
|
// scans and `id`, the repository id — so `^docs` / `0^docs` from any row is
|
|
779
825
|
// the scan and `^id` / `0^id` the id.
|
|
780
826
|
root(name) {
|
|
@@ -817,7 +863,9 @@ export function makeStoreContext(store, repoId, opts = {}) {
|
|
|
817
863
|
if (collection === null || typeof collection !== "object")
|
|
818
864
|
return undefined;
|
|
819
865
|
const root = rootScans.get(collection);
|
|
820
|
-
|
|
866
|
+
// The revisions scan has no store index (4.7): the engine builds its own
|
|
867
|
+
// over the materialized rows (one SELECT per context).
|
|
868
|
+
if (!root || root.target === "revisions")
|
|
821
869
|
return undefined;
|
|
822
870
|
const key = `${root.target}\u0000${JSON.stringify(path)}`;
|
|
823
871
|
let index = indexes.get(key);
|
|
@@ -844,6 +892,13 @@ export function makeStoreContext(store, repoId, opts = {}) {
|
|
|
844
892
|
}
|
|
845
893
|
if (name === "refs")
|
|
846
894
|
return { handled: true, value: refs(args[0]) };
|
|
895
|
+
// `date(x)` / `dur(x)` (spec/surface §1.3 "Dates and durations", 4.8):
|
|
896
|
+
// pure over their argument except the named points, which read the host
|
|
897
|
+
// clock (pinned by the spec clock seam). run.ts folds a literal argument
|
|
898
|
+
// before planning, so the arm here serves the non-literal ones.
|
|
899
|
+
const dated = dateFunction(name, args);
|
|
900
|
+
if (dated.handled)
|
|
901
|
+
return dated;
|
|
847
902
|
if (name === "range") {
|
|
848
903
|
const s = args[0];
|
|
849
904
|
if (semantics.isRange(s))
|
|
@@ -854,12 +909,17 @@ export function makeStoreContext(store, repoId, opts = {}) {
|
|
|
854
909
|
const fn = semantics.BUILTIN_FUNCTIONS[name];
|
|
855
910
|
return fn ? { handled: true, value: fn(args) } : { handled: false };
|
|
856
911
|
},
|
|
857
|
-
// Domain methods first
|
|
858
|
-
//
|
|
912
|
+
// Domain methods first, then the date methods (`plus`/`minus`/`until`/the
|
|
913
|
+
// fields/`format` on a date string, `dur_text`/`dur_iso` on a number —
|
|
914
|
+
// §1.3 4.8; total: any other receiver is absent); fall back to oqx-js
|
|
915
|
+
// builtin methods (contains/startsWith/endsWith/matches/size/lower/upper).
|
|
859
916
|
callMethod(name, recv, args) {
|
|
860
917
|
const r = method(name, recv, args);
|
|
861
918
|
if (r.handled)
|
|
862
919
|
return r;
|
|
920
|
+
const dated = dateMethod(name, recv, args);
|
|
921
|
+
if (dated.handled)
|
|
922
|
+
return dated;
|
|
863
923
|
const fn = semantics.BUILTIN_METHODS[name];
|
|
864
924
|
return fn ? { handled: true, value: fn(recv, args) } : { handled: false };
|
|
865
925
|
},
|
|
@@ -891,8 +951,10 @@ export function rowRef(value) {
|
|
|
891
951
|
if (!t)
|
|
892
952
|
return undefined;
|
|
893
953
|
const r = value;
|
|
894
|
-
const id = t === "docs" ? r.doc_id : t === "blocks" ? r.block_id : t === "nodes" ? r.node_id : r.edge_id;
|
|
895
|
-
|
|
954
|
+
const id = t === "docs" ? r.doc_id : t === "blocks" ? r.block_id : t === "nodes" ? r.node_id : t === "revisions" ? r.rev_id : r.edge_id;
|
|
955
|
+
// a revision's path is `revisions.path` — the path it was committed under
|
|
956
|
+
// (a move re-paths the current revision; §1.2, 4.7).
|
|
957
|
+
const path = t === "docs" || t === "revisions" ? r.path : r.__path;
|
|
896
958
|
return { id: String(id), path: path == null ? "" : referencePath(String(path)) };
|
|
897
959
|
}
|
|
898
960
|
export { TARGET };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { CallResult } from "@omgbase/oqx";
|
|
2
|
+
/** A parsed date: the instant, the offset it was WRITTEN in (minutes east of
|
|
3
|
+
* UTC; `0` for `Z` and for a date-only value), whether it is date-only, and
|
|
4
|
+
* whether it carried a fraction of a second as written (kept on render). */
|
|
5
|
+
export interface DateValue {
|
|
6
|
+
ms: number;
|
|
7
|
+
offset: number;
|
|
8
|
+
dateOnly: boolean;
|
|
9
|
+
fraction: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** Parse an ISO-8601 date / datetime string; `null` for anything else (a
|
|
12
|
+
* non-string, a malformed string, an out-of-range field such as month 13 or
|
|
13
|
+
* hour 24). */
|
|
14
|
+
export declare function parseDate(x: unknown): DateValue | null;
|
|
15
|
+
interface Fields {
|
|
16
|
+
year: number;
|
|
17
|
+
month: number;
|
|
18
|
+
day: number;
|
|
19
|
+
hour: number;
|
|
20
|
+
minute: number;
|
|
21
|
+
second: number;
|
|
22
|
+
millis: number;
|
|
23
|
+
weekday: number;
|
|
24
|
+
}
|
|
25
|
+
/** The fields of the value in the offset it was written in (`weekday` 1 = Monday … 7 = Sunday). */
|
|
26
|
+
export declare function fieldsOf(d: DateValue): Fields;
|
|
27
|
+
/** The canonical string: `YYYY-MM-DD` for a date-only value; otherwise
|
|
28
|
+
* `YYYY-MM-DDThh:mm:ss` + `.fff` when the value carries a fraction (as written,
|
|
29
|
+
* or because arithmetic produced one) + `Z` for UTC or the written `±hh:mm`. */
|
|
30
|
+
export declare function renderDate(d: DateValue): string | null;
|
|
31
|
+
/** `date(x)` on a string that is not a named point: the canonical form, or null. */
|
|
32
|
+
export declare function canonicalDate(x: unknown): string | null;
|
|
33
|
+
/** The named points `date()` resolves against the clock. */
|
|
34
|
+
export declare const NAMED_POINTS: ReadonlySet<string>;
|
|
35
|
+
/** Resolve a named point at the instant `nowMs` (UTC): `now` is the instant
|
|
36
|
+
* with milliseconds (the form the store stamps commits in); the others are
|
|
37
|
+
* date-only — `sow`/`eow` the Monday/Sunday of the current week, `som`/`eom`
|
|
38
|
+
* the first/last day of the month, `soy`/`eoy` of the year. Null for a name
|
|
39
|
+
* that is not a point. */
|
|
40
|
+
export declare function namedPoint(name: string, nowMs: number): DateValue | null;
|
|
41
|
+
/** `date(x)`: a named point against the clock, else the canonical ISO form, else null. */
|
|
42
|
+
export declare function dateOf(x: unknown, nowMs?: number): string | null;
|
|
43
|
+
/** A parsed duration: the calendar counts `years` and `months` kept apart from
|
|
44
|
+
* the fixed-length `seconds`, so `plus` can be calendar-aware while `dur()`
|
|
45
|
+
* applies the 365-day / 30-day conventions. */
|
|
46
|
+
export interface Duration {
|
|
47
|
+
years: number;
|
|
48
|
+
months: number;
|
|
49
|
+
seconds: number;
|
|
50
|
+
}
|
|
51
|
+
/** Days per month / year under the fixed conventions `dur()` applies. */
|
|
52
|
+
export declare const MONTH_SECONDS: number;
|
|
53
|
+
export declare const YEAR_SECONDS: number;
|
|
54
|
+
/** Parse a duration string: Dataview's `<number> <unit>` pairs, each pair
|
|
55
|
+
* separated from the next by spaces, a comma, or nothing (`"1h30m"`, `"2 days,
|
|
56
|
+
* 3 hours"`; a trailing separator is malformed), units case-insensitive:
|
|
57
|
+
* y|yr|yrs|year|years, mo|mos|month|months, w|wk|wks|week|weeks, d|day|days,
|
|
58
|
+
* h|hr|hrs|hour|hours, m|min|mins|minute|minutes, s|sec|secs|second|seconds —
|
|
59
|
+
* or an ISO-8601 duration (`P1DT2H`, `PT90M`, `P2W`). Null when it is neither. */
|
|
60
|
+
export declare function parseDuration(x: unknown): Duration | null;
|
|
61
|
+
/** A duration's length in seconds under the conventions: a year is 365 days, a month 30. */
|
|
62
|
+
export declare function durationSeconds(d: Duration): number;
|
|
63
|
+
/** `dur(x)`: a number is that many seconds; a string is parsed; else null. */
|
|
64
|
+
export declare function durOf(x: unknown): number | null;
|
|
65
|
+
/** Shift a date by a whole number of calendar `months` (the month field moves
|
|
66
|
+
* and the day is clamped to the new month's length) and then by `seconds`. A
|
|
67
|
+
* date-only value stays date-only when the seconds are a whole number of days;
|
|
68
|
+
* otherwise it becomes a datetime in its offset (UTC for a date-only receiver). */
|
|
69
|
+
export declare function shiftDate(d: DateValue, months: number, seconds: number): DateValue;
|
|
70
|
+
/** A duration as `plus` applies it: the WHOLE years and months are calendar
|
|
71
|
+
* months; a fractional year or month count is not calendar — its fraction is
|
|
72
|
+
* 365-day / 30-day seconds — and joins the fixed-length seconds. */
|
|
73
|
+
export declare function calendarParts(dur: Duration): {
|
|
74
|
+
months: number;
|
|
75
|
+
seconds: number;
|
|
76
|
+
};
|
|
77
|
+
/** `d.plus(x)` (`sign` 1) / `d.minus(x)` (`sign` -1). */
|
|
78
|
+
export declare function plusMinus(recv: unknown, arg: unknown, sign: 1 | -1): string | null;
|
|
79
|
+
/** `a.until(b)`: seconds from `a` to `b` (negative when `b` is earlier), by instant. */
|
|
80
|
+
export declare function until(a: unknown, b: unknown): number | null;
|
|
81
|
+
/** `d.format(pattern)`: `yyyy` year, `MM` month, `dd` day, `HH` hour, `mm`
|
|
82
|
+
* minute, `ss` second (two digits each), `MMM` the English month abbreviation
|
|
83
|
+
* (`Jan`), `EEE` the English weekday abbreviation (`Mon`); every other
|
|
84
|
+
* character is literal. Fields are read in the written offset; a date-only
|
|
85
|
+
* value has `00` time fields. */
|
|
86
|
+
export declare function formatDate(recv: unknown, pattern: unknown): string | null;
|
|
87
|
+
/** `n.dur_text()`: `"2d 3h"`, `"1h 30m"`, `"45s"` — days, hours, minutes,
|
|
88
|
+
* seconds, zero parts omitted, `"0s"` for zero, a leading `-` for a negative
|
|
89
|
+
* duration, a fractional second kept (`"1.5s"`). Null for a non-number. */
|
|
90
|
+
export declare function durText(n: unknown): string | null;
|
|
91
|
+
/** `n.dur_iso()`: the ISO-8601 duration `P[nD][T[nH][nM][nS]]` — `PT1H30M`,
|
|
92
|
+
* `P2DT3H`, `PT45S`, `PT0S` for zero, a leading `-` for a negative duration.
|
|
93
|
+
* Null for a non-number. */
|
|
94
|
+
export declare function durIso(n: unknown): string | null;
|
|
95
|
+
/** The free functions and their arity. */
|
|
96
|
+
export declare const DATE_FUNCTIONS: Readonly<Record<string, number>>;
|
|
97
|
+
/** The methods and their arity: on a date string, and (`dur_text`/`dur_iso`) on a number. */
|
|
98
|
+
export declare const DATE_METHODS: Readonly<Record<string, number>>;
|
|
99
|
+
/** `callFunction` arm: `date(x)` / `dur(x)`; `{ handled: false }` for any other name. */
|
|
100
|
+
export declare function dateFunction(name: string, args: unknown[], nowMs?: number): CallResult;
|
|
101
|
+
/** `callMethod` arm: the date methods on a string receiver and the duration
|
|
102
|
+
* renderers on a number; a receiver of any other kind yields absent (the
|
|
103
|
+
* methods are total). `{ handled: false }` for any other name. */
|
|
104
|
+
export declare function dateMethod(name: string, recv: unknown, args: unknown[]): CallResult;
|
|
105
|
+
export {};
|