@superbee/core 0.2.0-pre.1 → 0.2.0-pre.11
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 +148 -0
- package/dist/artifact-contract.d.ts +90 -0
- package/dist/artifact-contract.d.ts.map +1 -0
- package/dist/artifact-contract.js +205 -0
- package/dist/artifact-contract.js.map +1 -0
- package/dist/backend.d.ts +16 -1
- package/dist/backend.d.ts.map +1 -1
- package/dist/backend.js +85 -42
- package/dist/backend.js.map +1 -1
- package/dist/bundle-ops.d.ts +1 -1
- package/dist/bundle-ops.d.ts.map +1 -1
- package/dist/bundle-ops.js +2 -2
- package/dist/bundle-ops.js.map +1 -1
- package/dist/bundle.d.ts +2 -1
- package/dist/bundle.d.ts.map +1 -1
- package/dist/bundle.js +3 -3
- package/dist/bundle.js.map +1 -1
- package/dist/document-codec.d.ts +6 -1
- package/dist/document-codec.d.ts.map +1 -1
- package/dist/document-codec.js +8 -4
- package/dist/document-codec.js.map +1 -1
- package/dist/document-field-actions.d.ts +13 -1
- package/dist/document-field-actions.d.ts.map +1 -1
- package/dist/document-field-actions.js +47 -6
- package/dist/document-field-actions.js.map +1 -1
- package/dist/document-mutation.d.ts +15 -1
- package/dist/document-mutation.d.ts.map +1 -1
- package/dist/document-mutation.js +42 -11
- package/dist/document-mutation.js.map +1 -1
- package/dist/document-write-policy.d.ts.map +1 -1
- package/dist/document-write-policy.js +4 -2
- package/dist/document-write-policy.js.map +1 -1
- package/dist/engine.d.ts +1 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +4 -3
- package/dist/engine.js.map +1 -1
- package/dist/file-journaled-backend-internal.d.ts +19 -0
- package/dist/file-journaled-backend-internal.d.ts.map +1 -0
- package/dist/file-journaled-backend-internal.js +10 -0
- package/dist/file-journaled-backend-internal.js.map +1 -0
- package/dist/file-journaled-backend.d.ts +162 -0
- package/dist/file-journaled-backend.d.ts.map +1 -0
- package/dist/file-journaled-backend.js +1113 -0
- package/dist/file-journaled-backend.js.map +1 -0
- package/dist/filesystem-host.d.ts +15 -0
- package/dist/filesystem-host.d.ts.map +1 -0
- package/dist/filesystem-host.js +20 -0
- package/dist/filesystem-host.js.map +1 -0
- package/dist/filesystem-identity.d.ts +86 -4
- package/dist/filesystem-identity.d.ts.map +1 -1
- package/dist/filesystem-identity.js +338 -102
- package/dist/filesystem-identity.js.map +1 -1
- package/dist/filesystem-lock.d.ts +16 -3
- package/dist/filesystem-lock.d.ts.map +1 -1
- package/dist/filesystem-lock.js +323 -77
- package/dist/filesystem-lock.js.map +1 -1
- package/dist/filesystem-push-role.d.ts +77 -0
- package/dist/filesystem-push-role.d.ts.map +1 -0
- package/dist/filesystem-push-role.js +165 -0
- package/dist/filesystem-push-role.js.map +1 -0
- package/dist/filesystem.d.ts +17 -0
- package/dist/filesystem.d.ts.map +1 -0
- package/dist/filesystem.js +18 -0
- package/dist/filesystem.js.map +1 -0
- package/dist/frontmatter.d.ts +8 -0
- package/dist/frontmatter.d.ts.map +1 -1
- package/dist/frontmatter.js +14 -4
- package/dist/frontmatter.js.map +1 -1
- package/dist/governed-body-write.d.ts +106 -0
- package/dist/governed-body-write.d.ts.map +1 -0
- package/dist/governed-body-write.js +251 -0
- package/dist/governed-body-write.js.map +1 -0
- package/dist/heads-digest.d.ts +32 -0
- package/dist/heads-digest.d.ts.map +1 -0
- package/dist/heads-digest.js +37 -0
- package/dist/heads-digest.js.map +1 -0
- package/dist/host-policy-snapshot.d.ts +9 -0
- package/dist/host-policy-snapshot.d.ts.map +1 -0
- package/dist/host-policy-snapshot.js +33 -0
- package/dist/host-policy-snapshot.js.map +1 -0
- package/dist/host-text.d.ts +31 -0
- package/dist/host-text.d.ts.map +1 -0
- package/dist/host-text.js +77 -0
- package/dist/host-text.js.map +1 -0
- package/dist/hosted-transport/answer-rows.d.ts +216 -0
- package/dist/hosted-transport/answer-rows.d.ts.map +1 -0
- package/dist/hosted-transport/answer-rows.js +396 -0
- package/dist/hosted-transport/answer-rows.js.map +1 -0
- package/dist/hosted-transport/carrier.d.ts +142 -0
- package/dist/hosted-transport/carrier.d.ts.map +1 -0
- package/dist/hosted-transport/carrier.js +241 -0
- package/dist/hosted-transport/carrier.js.map +1 -0
- package/dist/hosted-transport/history-pages.d.ts +55 -0
- package/dist/hosted-transport/history-pages.d.ts.map +1 -0
- package/dist/hosted-transport/history-pages.js +69 -0
- package/dist/hosted-transport/history-pages.js.map +1 -0
- package/dist/hosted-transport/index.d.ts +18 -0
- package/dist/hosted-transport/index.d.ts.map +1 -0
- package/dist/hosted-transport/index.js +18 -0
- package/dist/hosted-transport/index.js.map +1 -0
- package/dist/hosted-transport/operations.d.ts +85 -0
- package/dist/hosted-transport/operations.d.ts.map +1 -0
- package/dist/hosted-transport/operations.js +128 -0
- package/dist/hosted-transport/operations.js.map +1 -0
- package/dist/hosted-transport/paged-reads.d.ts +87 -0
- package/dist/hosted-transport/paged-reads.d.ts.map +1 -0
- package/dist/hosted-transport/paged-reads.js +238 -0
- package/dist/hosted-transport/paged-reads.js.map +1 -0
- package/dist/hosted-transport/read-adapter.d.ts +250 -0
- package/dist/hosted-transport/read-adapter.d.ts.map +1 -0
- package/dist/hosted-transport/read-adapter.js +557 -0
- package/dist/hosted-transport/read-adapter.js.map +1 -0
- package/dist/hosted-transport/root-write.d.ts +114 -0
- package/dist/hosted-transport/root-write.d.ts.map +1 -0
- package/dist/hosted-transport/root-write.js +171 -0
- package/dist/hosted-transport/root-write.js.map +1 -0
- package/dist/hosted-transport/whole-document-transport.d.ts +203 -0
- package/dist/hosted-transport/whole-document-transport.d.ts.map +1 -0
- package/dist/hosted-transport/whole-document-transport.js +438 -0
- package/dist/hosted-transport/whole-document-transport.js.map +1 -0
- package/dist/index.d.ts +22 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -5
- package/dist/index.js.map +1 -1
- package/dist/indexeddb-backend.d.ts +38 -6
- package/dist/indexeddb-backend.d.ts.map +1 -1
- package/dist/indexeddb-backend.js +511 -46
- package/dist/indexeddb-backend.js.map +1 -1
- package/dist/journaled-backend.d.ts +296 -7
- package/dist/journaled-backend.d.ts.map +1 -1
- package/dist/journaled-backend.js +312 -0
- package/dist/journaled-backend.js.map +1 -1
- package/dist/kind-coverage.d.ts +99 -0
- package/dist/kind-coverage.d.ts.map +1 -0
- package/dist/kind-coverage.js +186 -0
- package/dist/kind-coverage.js.map +1 -0
- package/dist/kinds.d.ts +31 -1
- package/dist/kinds.d.ts.map +1 -1
- package/dist/kinds.js +83 -16
- package/dist/kinds.js.map +1 -1
- package/dist/links.d.ts +1 -1
- package/dist/links.d.ts.map +1 -1
- package/dist/links.js +5 -10
- package/dist/links.js.map +1 -1
- package/dist/managed-document-metadata.d.ts +2 -0
- package/dist/managed-document-metadata.d.ts.map +1 -0
- package/dist/managed-document-metadata.js +9 -0
- package/dist/managed-document-metadata.js.map +1 -0
- package/dist/memory-backend.d.ts +2 -2
- package/dist/memory-backend.d.ts.map +1 -1
- package/dist/memory-backend.js +32 -16
- package/dist/memory-backend.js.map +1 -1
- package/dist/paths.d.ts +6 -2
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +35 -33
- package/dist/paths.js.map +1 -1
- package/dist/platform.d.ts +84 -6
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +9 -4
- package/dist/platform.js.map +1 -1
- package/dist/portable-export.d.ts +119 -0
- package/dist/portable-export.d.ts.map +1 -0
- package/dist/portable-export.js +501 -0
- package/dist/portable-export.js.map +1 -0
- package/dist/publication-filesystem.d.ts +1 -1
- package/dist/publication-filesystem.d.ts.map +1 -1
- package/dist/publication-filesystem.js +1 -1
- package/dist/publication-filesystem.js.map +1 -1
- package/dist/query-filter.d.ts +7 -2
- package/dist/query-filter.d.ts.map +1 -1
- package/dist/query-filter.js +24 -1
- package/dist/query-filter.js.map +1 -1
- package/dist/query-order.d.ts +22 -0
- package/dist/query-order.d.ts.map +1 -0
- package/dist/query-order.js +42 -0
- package/dist/query-order.js.map +1 -0
- package/dist/remote-backend.d.ts +91 -27
- package/dist/remote-backend.d.ts.map +1 -1
- package/dist/remote-backend.js +217 -49
- package/dist/remote-backend.js.map +1 -1
- package/dist/remote-document-codec.d.ts +15 -0
- package/dist/remote-document-codec.d.ts.map +1 -0
- package/dist/remote-document-codec.js +103 -0
- package/dist/remote-document-codec.js.map +1 -0
- package/dist/remote-error.d.ts +38 -0
- package/dist/remote-error.d.ts.map +1 -0
- package/dist/remote-error.js +59 -0
- package/dist/remote-error.js.map +1 -0
- package/dist/remote-operations.d.ts +6 -3
- package/dist/remote-operations.d.ts.map +1 -1
- package/dist/remote-operations.js +20 -5
- package/dist/remote-operations.js.map +1 -1
- package/dist/remote-parsers.d.ts +86 -0
- package/dist/remote-parsers.d.ts.map +1 -0
- package/dist/remote-parsers.js +181 -0
- package/dist/remote-parsers.js.map +1 -0
- package/dist/remote.d.ts +7 -2
- package/dist/remote.d.ts.map +1 -1
- package/dist/remote.js +6 -1
- package/dist/remote.js.map +1 -1
- package/dist/site-capabilities.d.ts +339 -0
- package/dist/site-capabilities.d.ts.map +1 -0
- package/dist/site-capabilities.js +717 -0
- package/dist/site-capabilities.js.map +1 -0
- package/dist/storage.d.ts +4 -2
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +4 -1
- package/dist/storage.js.map +1 -1
- package/dist/types.d.ts +28 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/uncertain-write.d.ts +49 -13
- package/dist/uncertain-write.d.ts.map +1 -1
- package/dist/uncertain-write.js +36 -12
- package/dist/uncertain-write.js.map +1 -1
- package/dist/versioning.d.ts +9 -4
- package/dist/versioning.d.ts.map +1 -1
- package/dist/versioning.js +9 -4
- package/dist/versioning.js.map +1 -1
- package/package.json +38 -2
|
@@ -19,10 +19,46 @@
|
|
|
19
19
|
* attempts, carries the written bytes as `content` and their version as `local`, and takes
|
|
20
20
|
* the next value of a monotonic per-store sequence. The sequence advances only for a write
|
|
21
21
|
* that commits.
|
|
22
|
+
* - {@link JournaledBackend.deleteJournaled} is the deletion counterpart: ONE transaction that
|
|
23
|
+
* removes the document record under the same compare-and-swap, puts and removes meta rows
|
|
24
|
+
* with it, optionally supersedes an intent and records a deletion intent
|
|
25
|
+
* ({@link DOCUMENT_DELETE_KIND}) exactly as `writeJournaled` does for a write, and honours
|
|
26
|
+
* `requireSettled` the same way, so a document the authority deleted
|
|
27
|
+
* leaves the working copy together with its shared base and never while a local edit holds
|
|
28
|
+
* it. An absent target is not a conflict: the meta changes still apply and the result says
|
|
29
|
+
* `absent`, as the plain `delete` answers absence with `false`. With `onHeld`, a hold is not a
|
|
30
|
+
* rejection but the third outcome: the deletion and its own meta changes do not apply, the
|
|
31
|
+
* `onHeld` meta rows apply instead, in the same transaction as the hold check, and the result
|
|
32
|
+
* says `held` and names the holder. A caller that must rewrite a held document's shared base
|
|
33
|
+
* (a pull reconciling a remote deletion) does it there, so no other realm can settle the
|
|
34
|
+
* holding intent between the refusal and the rewrite.
|
|
35
|
+
* - `resolveIntents` on either journaled mutation requires document CAS and the exact complete
|
|
36
|
+
* unsettled target journal. It retires those records atomically with the document, meta, and
|
|
37
|
+
* optional replacement intent, leaving acknowledged history untouched. Recovery may retire a
|
|
38
|
+
* chain whose head is a recorded conflict or refusal, with conflicted, refused, and
|
|
39
|
+
* never-attempted pending successors behind it, never uncertain delivery. The caller
|
|
40
|
+
* preserves recovery evidence in meta; retirement does not assert remote acceptance.
|
|
41
|
+
* - {@link JournaledBackend.retireAcknowledged}, when an adapter offers it, is ONE transaction
|
|
42
|
+
* that removes named acknowledged intents of one target, and named meta rows, under the full
|
|
43
|
+
* guard of that target. Every named intent must be acknowledged in the guard, and the guard is
|
|
44
|
+
* compared in the same transaction, so an intent another realm moved since the caller's read is
|
|
45
|
+
* never removed. Unsettled work is never retired this way. What a retained successor may
|
|
46
|
+
* still need from a retired row is the caller's rule; the adapter only enforces the premise.
|
|
22
47
|
* - {@link JournaledBackend.readWithJournal} is ONE snapshot: the document, every intent
|
|
23
48
|
* targeting it, and the named meta rows, read in one readonly transaction, so a write in
|
|
24
49
|
* another realm between separate reads can never show a caller a document of one moment
|
|
25
50
|
* beside a journal of another. Intents come back in local commit order (by `sequence`).
|
|
51
|
+
* - {@link JournaledBackend.readHeads} is that snapshot over every document at once: each
|
|
52
|
+
* record's head (id, version, writer, time), its leading frontmatter parsed as `read` parses
|
|
53
|
+
* it, its exact bytes, every intent targeting it, and the meta rows the caller names for it,
|
|
54
|
+
* all from ONE readonly transaction, so every row describes the same moment. Bodies are
|
|
55
|
+
* never parsed. A record whose leading block does not parse is a row with `frontmatter`
|
|
56
|
+
* null and the parser's error, never a dropped row and never a failed listing. A caller that
|
|
57
|
+
* keeps less than the row (a version and a provenance, say) passes `project`, which maps
|
|
58
|
+
* each row as it is read so the listing over a large store never holds every document's
|
|
59
|
+
* stored bytes at once; the journal is read whole before the walk, so intents and the
|
|
60
|
+
* content they carry are held for its duration, as a single read holds them. Keys every row
|
|
61
|
+
* needs go in `shared`, read once. Rows come back in the order `list` orders ids.
|
|
26
62
|
* - {@link JournaledBackend.updateIntent} is a compare-and-swap on the intent's `state`: the
|
|
27
63
|
* patch applies only while the record is in `expectedState`, together with any meta rows, in
|
|
28
64
|
* one transaction. A different state or a missing record rejects with
|
|
@@ -37,6 +73,222 @@
|
|
|
37
73
|
* the shipping implementation; a test-only in-memory adapter proves the seam is not a
|
|
38
74
|
* description of that one class.
|
|
39
75
|
*/
|
|
76
|
+
import { assertSafeConceptId } from "./paths.js";
|
|
77
|
+
import { versionOfBytes } from "./versioning.js";
|
|
78
|
+
/**
|
|
79
|
+
* The intent kind of a journaled local deletion: the document leaves at exactly the intent's
|
|
80
|
+
* base. A deletion carries no bytes, so its `content` is {@link DELETION_CONTENT} and its `local`
|
|
81
|
+
* is {@link DELETION_VERSION}, the version of empty bytes, which no document can have (every
|
|
82
|
+
* document has frontmatter). A conflict can therefore never name a deletion's own `local` and
|
|
83
|
+
* read as its commit.
|
|
84
|
+
*/
|
|
85
|
+
export const DOCUMENT_DELETE_KIND = "document.delete";
|
|
86
|
+
export const DELETION_CONTENT = "";
|
|
87
|
+
export const DELETION_VERSION = versionOfBytes(DELETION_CONTENT);
|
|
88
|
+
/** A full snapshot or metadata admission premise changed; nothing was written. */
|
|
89
|
+
export class JournalGuardConflict extends Error {
|
|
90
|
+
name = "JournalGuardConflict";
|
|
91
|
+
target;
|
|
92
|
+
constructor(target) {
|
|
93
|
+
super(`journal guard for '${target}' does not match or is invalid`);
|
|
94
|
+
this.target = target;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Guard values are acyclic plain records, arrays, and primitive values (including undefined).
|
|
99
|
+
* Symbols, accessors, functions, and objects with other prototypes are refused. This restriction
|
|
100
|
+
* applies only to guarded operations; the unguarded metadata store remains opaque.
|
|
101
|
+
*/
|
|
102
|
+
export function captureJournalValue(value) {
|
|
103
|
+
const ancestors = new Set();
|
|
104
|
+
const check = (entry) => {
|
|
105
|
+
if (typeof entry === "function" || typeof entry === "symbol")
|
|
106
|
+
throw new JournalGuardConflict("value");
|
|
107
|
+
if (entry === null || typeof entry !== "object")
|
|
108
|
+
return;
|
|
109
|
+
if (ancestors.has(entry) || (!Array.isArray(entry) && Object.getPrototypeOf(entry) !== Object.prototype && Object.getPrototypeOf(entry) !== null))
|
|
110
|
+
throw new JournalGuardConflict("value");
|
|
111
|
+
ancestors.add(entry);
|
|
112
|
+
for (const key of Reflect.ownKeys(entry)) {
|
|
113
|
+
if (typeof key !== "string")
|
|
114
|
+
throw new JournalGuardConflict("value");
|
|
115
|
+
const descriptor = Object.getOwnPropertyDescriptor(entry, key);
|
|
116
|
+
if (!("value" in descriptor) || (!descriptor.enumerable && !(Array.isArray(entry) && key === "length")))
|
|
117
|
+
throw new JournalGuardConflict("value");
|
|
118
|
+
check(descriptor.value);
|
|
119
|
+
}
|
|
120
|
+
ancestors.delete(entry);
|
|
121
|
+
};
|
|
122
|
+
check(value);
|
|
123
|
+
return structuredClone(value);
|
|
124
|
+
}
|
|
125
|
+
function equalJournalValue(a, b) {
|
|
126
|
+
if (Object.is(a, b))
|
|
127
|
+
return true;
|
|
128
|
+
if (!a || !b || typeof a !== "object" || typeof b !== "object" || Array.isArray(a) !== Array.isArray(b))
|
|
129
|
+
return false;
|
|
130
|
+
if (Array.isArray(a) && a.length !== b.length)
|
|
131
|
+
return false;
|
|
132
|
+
const keys = Object.keys(a);
|
|
133
|
+
return keys.length === Object.keys(b).length && keys.every(key => Object.hasOwn(b, key) && equalJournalValue(a[key], b[key]));
|
|
134
|
+
}
|
|
135
|
+
function assertMetaExpectation(value) {
|
|
136
|
+
if (!value || typeof value !== "object" || typeof value.present !== "boolean" ||
|
|
137
|
+
!equalJournalValue(Object.keys(value).sort(), (value.present ? ["present", "value"] : ["present"]).sort()))
|
|
138
|
+
throw new JournalGuardConflict("meta");
|
|
139
|
+
}
|
|
140
|
+
export function captureJournalGuard(value, target) {
|
|
141
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
142
|
+
throw new JournalGuardConflict(target ?? "guard");
|
|
143
|
+
const guard = captureJournalValue(value);
|
|
144
|
+
if (typeof guard.target !== "string")
|
|
145
|
+
throw new JournalGuardConflict(target ?? "guard");
|
|
146
|
+
target ??= guard.target;
|
|
147
|
+
assertSafeConceptId(guard.target);
|
|
148
|
+
if (guard.target !== target || !Array.isArray(guard.intents) || !Array.isArray(guard.meta) ||
|
|
149
|
+
(guard.document !== null && (!guard.document || typeof guard.document.version !== "string" || typeof guard.document.raw !== "string")))
|
|
150
|
+
throw new JournalGuardConflict(target);
|
|
151
|
+
const requests = new Set(), sequences = new Set(), keys = new Set();
|
|
152
|
+
for (const row of guard.intents) {
|
|
153
|
+
if (!row || row.target !== target || typeof row.requestId !== "string" || requests.has(row.requestId) || !Number.isSafeInteger(row.sequence) || sequences.has(row.sequence))
|
|
154
|
+
throw new JournalGuardConflict(target);
|
|
155
|
+
requests.add(row.requestId);
|
|
156
|
+
sequences.add(row.sequence);
|
|
157
|
+
}
|
|
158
|
+
for (const row of guard.meta) {
|
|
159
|
+
if (!row || typeof row.key !== "string" || keys.has(row.key))
|
|
160
|
+
throw new JournalGuardConflict(target);
|
|
161
|
+
keys.add(row.key);
|
|
162
|
+
assertMetaExpectation(row.expected);
|
|
163
|
+
}
|
|
164
|
+
return guard;
|
|
165
|
+
}
|
|
166
|
+
/** Only omission or undefined selects the unguarded compatibility path. */
|
|
167
|
+
export function captureJournalGuardOption(options, target) {
|
|
168
|
+
const descriptor = Object.getOwnPropertyDescriptor(options, "guard");
|
|
169
|
+
if (!descriptor) {
|
|
170
|
+
if ("guard" in options)
|
|
171
|
+
throw new JournalGuardConflict(target ?? "guard");
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
if (!("value" in descriptor))
|
|
175
|
+
throw new JournalGuardConflict(target ?? "guard");
|
|
176
|
+
return descriptor.value === undefined ? undefined : captureJournalGuard(descriptor.value, target);
|
|
177
|
+
}
|
|
178
|
+
/** Compare all target records, including acknowledged history, and exact named row presence. */
|
|
179
|
+
export function assertJournalGuard(expected, current) {
|
|
180
|
+
const a = captureJournalGuard(expected), b = captureJournalGuard(current, a.target);
|
|
181
|
+
a.intents.sort((x, y) => x.sequence - y.sequence);
|
|
182
|
+
b.intents.sort((x, y) => x.sequence - y.sequence);
|
|
183
|
+
a.meta.sort((x, y) => x.key.localeCompare(y.key));
|
|
184
|
+
b.meta.sort((x, y) => x.key.localeCompare(y.key));
|
|
185
|
+
if (!equalJournalValue(a, b))
|
|
186
|
+
throw new JournalGuardConflict(a.target);
|
|
187
|
+
}
|
|
188
|
+
/** Capture guarded updates before any asynchronous adapter work and protect original history. */
|
|
189
|
+
export function captureIntentUpdate(patch, options) {
|
|
190
|
+
const guard = captureJournalGuardOption(options);
|
|
191
|
+
if (guard !== undefined)
|
|
192
|
+
captureJournalValue({ patch, options });
|
|
193
|
+
if (options.document && guard === undefined)
|
|
194
|
+
throw new JournalGuardConflict(options.document.id);
|
|
195
|
+
const captured = structuredClone({ patch, options });
|
|
196
|
+
if (guard !== undefined) {
|
|
197
|
+
captured.options.guard = guard;
|
|
198
|
+
const mutable = new Set(["state", "attempts", "acknowledgedVersion", "remote", "refusal", "finding", "updatedAt"]);
|
|
199
|
+
if (Object.keys(patch).some(key => !mutable.has(key)) || (captured.options.document && captured.options.document.id !== captured.options.guard.target))
|
|
200
|
+
throw new JournalGuardConflict(captured.options.guard.target);
|
|
201
|
+
assertJournalMetaChanges(captured.options.guard, captured.options.meta ?? [], undefined);
|
|
202
|
+
}
|
|
203
|
+
return captured;
|
|
204
|
+
}
|
|
205
|
+
export function captureMetaWrite(options) {
|
|
206
|
+
const result = captureJournalValue(options);
|
|
207
|
+
if (Object.hasOwn(result, "expected"))
|
|
208
|
+
assertMetaExpectation(result.expected);
|
|
209
|
+
if (result.requireEmptyJournal !== undefined && typeof result.requireEmptyJournal !== "boolean")
|
|
210
|
+
throw new JournalGuardConflict("meta");
|
|
211
|
+
return result;
|
|
212
|
+
}
|
|
213
|
+
export function assertMetaWrite(key, options, actual, intents) {
|
|
214
|
+
if (options.expected !== undefined && !equalJournalValue(captureJournalValue(options.expected), captureJournalValue(actual)))
|
|
215
|
+
throw new JournalGuardConflict(key);
|
|
216
|
+
if (options.requireEmptyJournal && intents.length !== 0)
|
|
217
|
+
throw new JournalGuardConflict(key);
|
|
218
|
+
}
|
|
219
|
+
/** Metadata removal is admitted only with an explicit full-snapshot premise. */
|
|
220
|
+
export function assertJournalMetaChanges(guard, puts, removals) {
|
|
221
|
+
if (guard) {
|
|
222
|
+
captureJournalValue(puts);
|
|
223
|
+
if (puts.some(row => !guard.meta.some(expected => expected.key === row.key)))
|
|
224
|
+
throw new JournalGuardConflict(guard.target);
|
|
225
|
+
}
|
|
226
|
+
if (removals === undefined)
|
|
227
|
+
return;
|
|
228
|
+
if (!guard || !Array.isArray(removals) || removals.some(key => typeof key !== "string") || new Set(removals).size !== removals.length ||
|
|
229
|
+
removals.some(key => puts.some(row => row.key === key) || !guard.meta.some(expected => expected.key === key)))
|
|
230
|
+
throw new JournalGuardConflict(guard?.target ?? "meta");
|
|
231
|
+
}
|
|
232
|
+
/** A guarded write owns one target and creates a globally fresh journal identity. */
|
|
233
|
+
export function assertJournalIntentChanges(guard, existingIdentity, superseded) {
|
|
234
|
+
if (guard && (existingIdentity !== undefined || (superseded !== undefined && superseded.target !== guard.target)))
|
|
235
|
+
throw new JournalGuardConflict(guard.target);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Capture a retirement before any adapter work: the guard is required and complete, every
|
|
239
|
+
* named intent is in it and acknowledged, and every removed meta row is one the guard names.
|
|
240
|
+
*/
|
|
241
|
+
export function captureRetireAcknowledged(target, options) {
|
|
242
|
+
if (!options || typeof options !== "object" || Array.isArray(options))
|
|
243
|
+
throw new JournalGuardConflict(target);
|
|
244
|
+
const guard = captureJournalGuardOption(options, target);
|
|
245
|
+
if (guard === undefined)
|
|
246
|
+
throw new JournalGuardConflict(target);
|
|
247
|
+
const captured = captureJournalValue({ requestIds: options.requestIds, removeMeta: options.removeMeta });
|
|
248
|
+
const ids = captured.requestIds;
|
|
249
|
+
if (!Array.isArray(ids) || ids.length === 0 || ids.some(id => typeof id !== "string") || new Set(ids).size !== ids.length ||
|
|
250
|
+
Object.keys(options).some(key => !["guard", "requestIds", "removeMeta"].includes(key)) ||
|
|
251
|
+
ids.some(id => guard.intents.find(row => row.requestId === id)?.state !== "acknowledged"))
|
|
252
|
+
throw new JournalGuardConflict(target);
|
|
253
|
+
assertJournalMetaChanges(guard, [], captured.removeMeta);
|
|
254
|
+
return { guard, requestIds: ids, ...(captured.removeMeta === undefined ? {} : { removeMeta: captured.removeMeta }) };
|
|
255
|
+
}
|
|
256
|
+
/** Capture write options while retaining the existing synchronous metadata producer. */
|
|
257
|
+
export function captureJournalWriteOptions(options) {
|
|
258
|
+
const descriptors = Object.getOwnPropertyDescriptors(options);
|
|
259
|
+
const data = {};
|
|
260
|
+
let producer;
|
|
261
|
+
for (const key of Reflect.ownKeys(options)) {
|
|
262
|
+
if (typeof key !== "string")
|
|
263
|
+
throw new JournalGuardConflict("options");
|
|
264
|
+
const descriptor = descriptors[key];
|
|
265
|
+
if (!("value" in descriptor) || !descriptor.enumerable)
|
|
266
|
+
throw new JournalGuardConflict("options");
|
|
267
|
+
if (key === "meta" && typeof descriptor.value === "function")
|
|
268
|
+
producer = descriptor.value;
|
|
269
|
+
else
|
|
270
|
+
data[key] = descriptor.value;
|
|
271
|
+
}
|
|
272
|
+
const captured = captureJournalValue(data);
|
|
273
|
+
if (producer)
|
|
274
|
+
captured.meta = producer;
|
|
275
|
+
return captured;
|
|
276
|
+
}
|
|
277
|
+
/** Capture every guarded deletion branch before evaluating held or absent state. */
|
|
278
|
+
export function captureJournalDeleteOptions(target, options) {
|
|
279
|
+
const guard = captureJournalGuardOption(options, target);
|
|
280
|
+
if (guard === undefined)
|
|
281
|
+
return options;
|
|
282
|
+
const captured = captureJournalValue(options);
|
|
283
|
+
captured.guard = guard;
|
|
284
|
+
assertJournalMetaChanges(guard, captured.meta ?? [], captured.removeMeta);
|
|
285
|
+
if (captured.onHeld !== undefined) {
|
|
286
|
+
if (!captured.onHeld || !Array.isArray(captured.onHeld.meta))
|
|
287
|
+
throw new JournalGuardConflict(target);
|
|
288
|
+
assertJournalMetaChanges(guard, captured.onHeld.meta, undefined);
|
|
289
|
+
}
|
|
290
|
+
return captured;
|
|
291
|
+
}
|
|
40
292
|
/**
|
|
41
293
|
* An intent's journal state was not the one the caller expected, or the intent is gone. Settling
|
|
42
294
|
* and superseding are compare-and-swap operations on the intent's own record so two realms (a
|
|
@@ -74,4 +326,64 @@ export class IntentHoldConflict extends Error {
|
|
|
74
326
|
this.state = state;
|
|
75
327
|
}
|
|
76
328
|
}
|
|
329
|
+
/** Recovery was composed against a different or unsafe unsettled journal snapshot. */
|
|
330
|
+
export class JournalSnapshotConflict extends Error {
|
|
331
|
+
name = "JournalSnapshotConflict";
|
|
332
|
+
target;
|
|
333
|
+
constructor(target) {
|
|
334
|
+
super(`journal snapshot for '${target}' changed or cannot be resolved`);
|
|
335
|
+
this.target = target;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* The deletion intent a journaled deletion records, refused before any adapter work when it is
|
|
340
|
+
* not one: another kind, another target, or combined with a guard (a guarded deletion journals
|
|
341
|
+
* nothing). The adapter fills content, local version, sequence and state.
|
|
342
|
+
*/
|
|
343
|
+
export function assertDeletionIntent(target, options) {
|
|
344
|
+
if (options.intent === undefined && options.supersede === undefined)
|
|
345
|
+
return;
|
|
346
|
+
if (options.guard !== undefined || (options.intent !== undefined && (options.intent.kind !== DOCUMENT_DELETE_KIND || options.intent.target !== target))) {
|
|
347
|
+
throw new JournalSnapshotConflict(target);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
/** The record a journaled deletion adds for `intent`. */
|
|
351
|
+
export function deletionIntentRecord(intent, sequence, now) {
|
|
352
|
+
return { ...intent, local: DELETION_VERSION, content: DELETION_CONTENT, sequence, attempts: 0, state: "pending", updatedAt: now };
|
|
353
|
+
}
|
|
354
|
+
export function assertJournalResolutionOptions(target, options) {
|
|
355
|
+
if (!options.resolveIntents)
|
|
356
|
+
return;
|
|
357
|
+
if (options.expectedVersion === undefined || options.requireSettled !== undefined ||
|
|
358
|
+
("intent" in options && options.intent !== undefined && options.intent.target !== target) ||
|
|
359
|
+
("supersede" in options && options.supersede !== undefined) || ("onHeld" in options && options.onHeld !== undefined)) {
|
|
360
|
+
throw new JournalSnapshotConflict(target);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Compare full records, not only state: a changed outcome or retry is new recovery evidence.
|
|
365
|
+
* The head of the set (its lowest sequence) must hold a recorded terminal answer, `conflict` or
|
|
366
|
+
* `refused`; every later row must be `conflict`, `refused` or never-attempted `pending`. An
|
|
367
|
+
* `unknown`, `in_flight` or attempted `pending` head is uncertain delivery and stays refused.
|
|
368
|
+
*/
|
|
369
|
+
export function assertJournalSnapshot(target, expected, current, freshRequestId) {
|
|
370
|
+
const equal = (a, b) => {
|
|
371
|
+
if (Object.is(a, b))
|
|
372
|
+
return true;
|
|
373
|
+
if (!a || !b || typeof a !== "object" || typeof b !== "object" || Array.isArray(a) !== Array.isArray(b))
|
|
374
|
+
return false;
|
|
375
|
+
const keys = Object.keys(a);
|
|
376
|
+
return keys.length === Object.keys(b).length && keys.every(key => Object.prototype.hasOwnProperty.call(b, key) &&
|
|
377
|
+
equal(a[key], b[key]));
|
|
378
|
+
};
|
|
379
|
+
const actual = current.filter(row => row.target === target && row.state !== "acknowledged");
|
|
380
|
+
const ids = new Set(expected.map(row => row.requestId));
|
|
381
|
+
const head = expected.reduce((lowest, row) => lowest === undefined || row.sequence < lowest.sequence ? row : lowest, undefined);
|
|
382
|
+
if ((freshRequestId !== undefined && current.some(row => row.requestId === freshRequestId)) ||
|
|
383
|
+
ids.size !== expected.length || actual.length !== expected.length || head === undefined || !(head.state === "conflict" || head.state === "refused") ||
|
|
384
|
+
expected.some(row => row.target !== target || !(row.state === "conflict" || row.state === "refused" || (row.state === "pending" && row.attempts === 0))) ||
|
|
385
|
+
expected.some(row => !equal(row, actual.find(candidate => candidate.requestId === row.requestId)))) {
|
|
386
|
+
throw new JournalSnapshotConflict(target);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
77
389
|
//# sourceMappingURL=journaled-backend.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"journaled-backend.js","sourceRoot":"","sources":["../src/journaled-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAKH;;;;GAIG;AACH,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC1B,IAAI,GAAG,qBAAqB,CAAC;IACtC,SAAS,CAAS;IAClB,QAAQ,CAAiB;IACzB,MAAM,CAAwB;IAEvC,YAAY,SAAiB,EAAE,QAAwB,EAAE,MAA6B;QACpF,KAAK,CACH,MAAM,KAAK,IAAI;YACb,CAAC,CAAC,WAAW,SAAS,qCAAqC,QAAQ,IAAI;YACvE,CAAC,CAAC,WAAW,SAAS,SAAS,MAAM,WAAW,QAAQ,GAAG,CAC9D,CAAC;QACF,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IACzB,IAAI,GAAG,oBAAoB,CAAC;IACrC,MAAM,CAAY;IAClB,SAAS,CAAS;IAClB,KAAK,CAAiB;IAE/B,YAAY,MAAiB,EAAE,SAAiB,EAAE,KAAqB;QACrE,KAAK,CAAC,IAAI,MAAM,wBAAwB,SAAS,eAAe,KAAK,GAAG,CAAC,CAAC;QAC1E,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;CACF"}
|
|
1
|
+
{"version":3,"file":"journaled-backend.js","sourceRoot":"","sources":["../src/journaled-backend.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { type KindRegistry } from "./kinds.js";
|
|
2
|
+
import type { OkfDocument } from "./types.js";
|
|
3
|
+
/** All records are grouped by actual type; path prefixes never infer a Kind or hide nested records. */
|
|
4
|
+
export declare function kindCoverage(registry: KindRegistry, documents: readonly OkfDocument[], options: {
|
|
5
|
+
now: Date;
|
|
6
|
+
okfVersion?: string;
|
|
7
|
+
kind?: string;
|
|
8
|
+
}): {
|
|
9
|
+
now: string;
|
|
10
|
+
documents: number;
|
|
11
|
+
undeclared_kinds: string[];
|
|
12
|
+
kinds: {
|
|
13
|
+
governs: string;
|
|
14
|
+
records: number;
|
|
15
|
+
coverage: {
|
|
16
|
+
fields: {
|
|
17
|
+
present: number;
|
|
18
|
+
filled: number;
|
|
19
|
+
sentinel: number;
|
|
20
|
+
empty: number;
|
|
21
|
+
rate: number | null;
|
|
22
|
+
field: string;
|
|
23
|
+
required: boolean;
|
|
24
|
+
}[];
|
|
25
|
+
body_prose: {
|
|
26
|
+
filled: number;
|
|
27
|
+
rate: number | null;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
drift: {
|
|
31
|
+
undeclared: {
|
|
32
|
+
key: string;
|
|
33
|
+
records: number;
|
|
34
|
+
rate: number | null;
|
|
35
|
+
examples: string[];
|
|
36
|
+
examples_total: number;
|
|
37
|
+
}[];
|
|
38
|
+
declared_unused: string[];
|
|
39
|
+
envelope: {
|
|
40
|
+
key: string;
|
|
41
|
+
records: number;
|
|
42
|
+
}[];
|
|
43
|
+
};
|
|
44
|
+
links: {
|
|
45
|
+
with_links: number;
|
|
46
|
+
typed: {
|
|
47
|
+
relation: string;
|
|
48
|
+
target_kind: string | null;
|
|
49
|
+
target_matches: boolean | null;
|
|
50
|
+
records: number;
|
|
51
|
+
links: number;
|
|
52
|
+
rate: number | null;
|
|
53
|
+
}[];
|
|
54
|
+
untyped_citations: number;
|
|
55
|
+
declared_never_used: string[];
|
|
56
|
+
dangling: number;
|
|
57
|
+
dangling_examples: string[];
|
|
58
|
+
no_inbound: number;
|
|
59
|
+
};
|
|
60
|
+
freshness: {
|
|
61
|
+
clock: string;
|
|
62
|
+
horizon: string | null;
|
|
63
|
+
horizon_ms: number | null;
|
|
64
|
+
missing: number;
|
|
65
|
+
invalid: number;
|
|
66
|
+
buckets: {
|
|
67
|
+
future: number;
|
|
68
|
+
"0-7d": number;
|
|
69
|
+
"8-30d": number;
|
|
70
|
+
"31-90d": number;
|
|
71
|
+
"91-180d": number;
|
|
72
|
+
"181-365d": number;
|
|
73
|
+
">365d": number;
|
|
74
|
+
};
|
|
75
|
+
older_than_horizon: number | null;
|
|
76
|
+
};
|
|
77
|
+
sources: {
|
|
78
|
+
zero: number;
|
|
79
|
+
total: number;
|
|
80
|
+
invalid_entries: number;
|
|
81
|
+
non_url_resources: number;
|
|
82
|
+
per_record: {
|
|
83
|
+
"0": number;
|
|
84
|
+
"1": number;
|
|
85
|
+
"2": number;
|
|
86
|
+
"3": number;
|
|
87
|
+
"4": number;
|
|
88
|
+
"5+": number;
|
|
89
|
+
};
|
|
90
|
+
distinct_domains: number;
|
|
91
|
+
domains_shown: number;
|
|
92
|
+
top_domains: {
|
|
93
|
+
domain: string;
|
|
94
|
+
sources: number;
|
|
95
|
+
}[];
|
|
96
|
+
};
|
|
97
|
+
}[];
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=kind-coverage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kind-coverage.d.ts","sourceRoot":"","sources":["../src/kind-coverage.ts"],"names":[],"mappings":"AAEA,OAAO,EAA+G,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAI5J,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AA4F9C,uGAAuG;AACvG,wBAAgB,YAAY,CAAC,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,WAAW,EAAE,EAAE,OAAO,EAAE;IAAE,GAAG,EAAE,IAAI,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+CjJ"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/** Read-only measurements over canonical documents and the already-loaded Kind registry. */
|
|
2
|
+
import { isStandardDocumentSetField } from "./document-field-actions.js";
|
|
3
|
+
import { freshnessHorizonMs, projectKindForAuthoring, readKindField, resolveKindFieldCoordinate } from "./kinds.js";
|
|
4
|
+
import { extractMarkdownLinks, parseLinksFromDoc } from "./links.js";
|
|
5
|
+
import { isManagedDocumentMetadataField } from "./managed-document-metadata.js";
|
|
6
|
+
import { parseTimestamp } from "./freshness.js";
|
|
7
|
+
const SENTINELS = new Set(["unknown", "n/a", "na", "none", "tbd", "null", "-", "?"]);
|
|
8
|
+
const EXAMPLES = 3;
|
|
9
|
+
const DOMAIN_LIMIT = 15;
|
|
10
|
+
const DAY = 86400000;
|
|
11
|
+
const compare = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
12
|
+
const ratio = (n, total) => total ? Math.round(n / total * 10000) / 10000 : null;
|
|
13
|
+
const empty = (value) => value == null || (typeof value === "string" && value.trim() === "") ||
|
|
14
|
+
(Array.isArray(value) ? value.length === 0 : typeof value === "object" && Object.keys(value).length === 0);
|
|
15
|
+
const sentinel = (value) => typeof value === "string" && SENTINELS.has(value.trim().toLowerCase());
|
|
16
|
+
const filled = (value) => !empty(value) && !sentinel(value);
|
|
17
|
+
const envelope = (key) => isManagedDocumentMetadataField(key) || isStandardDocumentSetField(key, "0.2") || key === "sources" || key === "tags";
|
|
18
|
+
function bodyProse(body) {
|
|
19
|
+
// Lossless line measurement, not Markdown rendering: every ATX heading is ignored;
|
|
20
|
+
// a Related heading excludes its descendants until the next same/higher-level heading.
|
|
21
|
+
let relatedLevel;
|
|
22
|
+
for (const line of body.split("\n")) {
|
|
23
|
+
const text = line.trim();
|
|
24
|
+
const heading = /^(#{1,6})[ \t]+(.+?)\s*#*\s*$/.exec(text);
|
|
25
|
+
if (heading) {
|
|
26
|
+
const level = heading[1].length;
|
|
27
|
+
if (relatedLevel !== undefined && level <= relatedLevel)
|
|
28
|
+
relatedLevel = undefined;
|
|
29
|
+
if (heading[2].trim().toLowerCase() === "related" && relatedLevel === undefined)
|
|
30
|
+
relatedLevel = level;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (relatedLevel !== undefined)
|
|
34
|
+
continue;
|
|
35
|
+
const prose = text.replace(/^[-*] +/, "");
|
|
36
|
+
if (!prose)
|
|
37
|
+
continue;
|
|
38
|
+
const links = extractMarkdownLinks(prose);
|
|
39
|
+
if (!(links.length === 1 && prose === `[${links[0].text}](${links[0].href})`))
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
function fieldStats(docs, value) {
|
|
45
|
+
let present = 0, sentinels = 0, used = 0;
|
|
46
|
+
for (const doc of docs) {
|
|
47
|
+
const v = value(doc);
|
|
48
|
+
if (v !== undefined)
|
|
49
|
+
used++;
|
|
50
|
+
if (filled(v))
|
|
51
|
+
present++;
|
|
52
|
+
else if (sentinel(v))
|
|
53
|
+
sentinels++;
|
|
54
|
+
}
|
|
55
|
+
return { present: used, filled: present, sentinel: sentinels, empty: docs.length - present - sentinels, rate: ratio(present, docs.length) };
|
|
56
|
+
}
|
|
57
|
+
function checkedOn(kind, docs, now) {
|
|
58
|
+
const buckets = { future: 0, "0-7d": 0, "8-30d": 0, "31-90d": 0, "91-180d": 0, "181-365d": 0, ">365d": 0 };
|
|
59
|
+
let missing = 0, invalid = 0, older = 0;
|
|
60
|
+
const horizon = freshnessHorizonMs(kind);
|
|
61
|
+
for (const doc of docs) {
|
|
62
|
+
const value = doc.frontmatter.checked_on;
|
|
63
|
+
if (!filled(value)) {
|
|
64
|
+
missing++;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
// checked_on is domain date metadata, not the edition's meaningful-change clock.
|
|
68
|
+
const ms = typeof value === "string" ? parseTimestamp(/^\d{4}-\d{2}-\d{2}$/.test(value) ? `${value}T00:00:00Z` : value, "0.2") : null;
|
|
69
|
+
if (ms === null) {
|
|
70
|
+
invalid++;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const ageMs = now.getTime() - ms;
|
|
74
|
+
const days = Math.floor(ageMs / DAY);
|
|
75
|
+
const bucket = days < 0 ? "future" : days <= 7 ? "0-7d" : days <= 30 ? "8-30d" : days <= 90 ? "31-90d" : days <= 180 ? "91-180d" : days <= 365 ? "181-365d" : ">365d";
|
|
76
|
+
buckets[bucket]++;
|
|
77
|
+
if (horizon !== undefined && ageMs > horizon)
|
|
78
|
+
older++;
|
|
79
|
+
}
|
|
80
|
+
return { clock: "checked_on", horizon: kind.freshnessHorizon ?? null, horizon_ms: horizon ?? null, missing, invalid, buckets, older_than_horizon: horizon === undefined ? null : older };
|
|
81
|
+
}
|
|
82
|
+
function sourceStats(docs) {
|
|
83
|
+
const domains = new Map();
|
|
84
|
+
const distribution = { "0": 0, "1": 0, "2": 0, "3": 0, "4": 0, "5+": 0 };
|
|
85
|
+
let total = 0, zero = 0, invalid = 0, nonUrl = 0;
|
|
86
|
+
for (const doc of docs) {
|
|
87
|
+
const raw = doc.frontmatter.sources;
|
|
88
|
+
const list = Array.isArray(raw) ? raw : raw == null ? [] : [raw];
|
|
89
|
+
let count = 0;
|
|
90
|
+
for (const item of list) {
|
|
91
|
+
const resource = typeof item === "string" ? item : item && typeof item === "object" && !Array.isArray(item) ? item.resource : undefined;
|
|
92
|
+
if (typeof resource !== "string" || !filled(resource)) {
|
|
93
|
+
invalid++;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
count++;
|
|
97
|
+
try {
|
|
98
|
+
const domain = new URL(resource).hostname.toLowerCase().replace(/^www\./, "");
|
|
99
|
+
if (!domain) {
|
|
100
|
+
nonUrl++;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
domains.set(domain, (domains.get(domain) ?? 0) + 1);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
nonUrl++;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (!count)
|
|
110
|
+
zero++;
|
|
111
|
+
total += count;
|
|
112
|
+
distribution[count >= 5 ? "5+" : String(count)]++;
|
|
113
|
+
}
|
|
114
|
+
const ordered = [...domains].sort((a, b) => b[1] - a[1] || compare(a[0], b[0]));
|
|
115
|
+
return { zero, total, invalid_entries: invalid, non_url_resources: nonUrl, per_record: distribution, distinct_domains: domains.size, domains_shown: Math.min(domains.size, DOMAIN_LIMIT), top_domains: ordered.slice(0, DOMAIN_LIMIT).map(([domain, sources]) => ({ domain, sources })) };
|
|
116
|
+
}
|
|
117
|
+
/** All records are grouped by actual type; path prefixes never infer a Kind or hide nested records. */
|
|
118
|
+
export function kindCoverage(registry, documents, options) {
|
|
119
|
+
if (!Number.isFinite(options.now.getTime()))
|
|
120
|
+
throw new TypeError("Coverage now must be a valid date");
|
|
121
|
+
const docs = [...documents].sort((a, b) => compare(a.id, b.id));
|
|
122
|
+
const index = new Map(docs.map((doc) => [doc.id, doc]));
|
|
123
|
+
const edges = new Map(docs.map((doc) => [doc.id, parseLinksFromDoc(doc)]));
|
|
124
|
+
const inbound = new Set([...edges.values()].flat().map((link) => link.to));
|
|
125
|
+
const groups = new Map();
|
|
126
|
+
for (const doc of docs) {
|
|
127
|
+
const type = doc.frontmatter.type;
|
|
128
|
+
const group = groups.get(type) ?? [];
|
|
129
|
+
group.push(doc);
|
|
130
|
+
groups.set(type, group);
|
|
131
|
+
}
|
|
132
|
+
const kinds = [...registry.kinds.values()].filter((kind) => options.kind === undefined || kind.governs === options.kind).map((kind) => {
|
|
133
|
+
const records = groups.get(kind.governs) ?? [];
|
|
134
|
+
const authoring = projectKindForAuthoring(options.okfVersion, kind);
|
|
135
|
+
const fields = [...new Set([...authoring.fields.required, ...authoring.fields.optional])].sort(compare);
|
|
136
|
+
const stats = fields.map((field) => ({ field, required: authoring.fields.required.includes(field), ...fieldStats(records, (doc) => readKindField(options.okfVersion, kind, doc.frontmatter, field)) }));
|
|
137
|
+
const declaredStorage = new Set(fields.map((field) => resolveKindFieldCoordinate(options.okfVersion, kind, field)?.storageField ?? field));
|
|
138
|
+
const undeclared = new Map(), envelopes = new Map();
|
|
139
|
+
for (const doc of records)
|
|
140
|
+
for (const key of Object.keys(doc.frontmatter)) {
|
|
141
|
+
if (envelope(key)) {
|
|
142
|
+
envelopes.set(key, (envelopes.get(key) ?? 0) + 1);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (!declaredStorage.has(key)) {
|
|
146
|
+
const ids = undeclared.get(key) ?? [];
|
|
147
|
+
ids.push(doc.id);
|
|
148
|
+
undeclared.set(key, ids);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const relations = new Map();
|
|
152
|
+
let citations = 0, dangling = 0, withLinks = 0;
|
|
153
|
+
const danglingExamples = [];
|
|
154
|
+
for (const doc of records) {
|
|
155
|
+
const links = edges.get(doc.id);
|
|
156
|
+
if (links.length)
|
|
157
|
+
withLinks++;
|
|
158
|
+
for (const link of links) {
|
|
159
|
+
const target = index.get(link.to);
|
|
160
|
+
if (!target) {
|
|
161
|
+
dangling++;
|
|
162
|
+
if (danglingExamples.length < EXAMPLES)
|
|
163
|
+
danglingExamples.push(doc.id);
|
|
164
|
+
}
|
|
165
|
+
if (!Object.hasOwn(kind.links ?? {}, link.text)) {
|
|
166
|
+
citations++;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const targetKind = target?.frontmatter.type ?? null;
|
|
170
|
+
const key = JSON.stringify([link.text, targetKind]);
|
|
171
|
+
const row = relations.get(key) ?? { relation: link.text, target_kind: targetKind, target_matches: targetKind === null ? null : targetKind === kind.links[link.text], records: new Set(), links: 0 };
|
|
172
|
+
row.records.add(doc.id);
|
|
173
|
+
row.links++;
|
|
174
|
+
relations.set(key, row);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const used = new Set([...relations.values()].map((row) => row.relation));
|
|
178
|
+
return { governs: kind.governs, records: records.length,
|
|
179
|
+
coverage: { fields: stats, body_prose: { filled: records.filter((doc) => bodyProse(doc.body)).length, rate: ratio(records.filter((doc) => bodyProse(doc.body)).length, records.length) } },
|
|
180
|
+
drift: { undeclared: [...undeclared].sort(([a], [b]) => compare(a, b)).map(([key, ids]) => ({ key, records: ids.length, rate: ratio(ids.length, records.length), examples: ids.slice(0, EXAMPLES), examples_total: ids.length })), declared_unused: stats.filter((row) => row.present === 0).map((row) => row.field), envelope: [...envelopes].sort(([a], [b]) => compare(a, b)).map(([key, records]) => ({ key, records })) },
|
|
181
|
+
links: { with_links: withLinks, typed: [...relations.values()].sort((a, b) => compare(a.relation, b.relation) || compare(a.target_kind ?? "", b.target_kind ?? "")).map((row) => ({ relation: row.relation, target_kind: row.target_kind, target_matches: row.target_matches, records: row.records.size, links: row.links, rate: ratio(row.records.size, records.length) })), untyped_citations: citations, declared_never_used: Object.keys(kind.links ?? {}).filter((relation) => !used.has(relation)).sort(compare), dangling, dangling_examples: danglingExamples, no_inbound: records.filter((doc) => !inbound.has(doc.id)).length },
|
|
182
|
+
freshness: checkedOn(kind, records, options.now), sources: sourceStats(records) };
|
|
183
|
+
});
|
|
184
|
+
return { now: options.now.toISOString(), documents: docs.length, undeclared_kinds: [...groups.keys()].filter((type) => !registry.kinds.has(type)).sort(compare), kinds };
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=kind-coverage.js.map
|