foottrace 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -2
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/lib/cause/explain.js +7 -7
- package/dist/esm/lib/cause/queryMemo.d.ts +18 -0
- package/dist/esm/lib/cause/queryMemo.js +46 -0
- package/dist/esm/lib/cause/recordAdapter.js +12 -19
- package/dist/esm/lib/cause/recordChildren.d.ts +3 -3
- package/dist/esm/lib/cause/recordChildren.js +3 -4
- package/dist/esm/lib/cause/recordCoverage.d.ts +2 -2
- package/dist/esm/lib/cause/recordCoverage.js +1 -1
- package/dist/esm/lib/cause/recordIdentity.js +5 -5
- package/dist/esm/lib/cause/recordLoading.js +3 -3
- package/dist/esm/lib/cause/recordQuery.d.ts +9 -0
- package/dist/esm/lib/cause/recordQuery.js +22 -0
- package/dist/esm/lib/cause/recordScope.js +13 -13
- package/dist/esm/lib/cause/recordValidation.d.ts +4 -4
- package/dist/esm/lib/cause/recordValidation.js +17 -17
- package/dist/esm/lib/cause/session.js +9 -6
- package/dist/esm/lib/cause/storedRecordAdapter.js +16 -10
- package/dist/esm/lib/cause/types.d.ts +2 -1
- package/dist/esm/lib/cause/validation.d.ts +4 -4
- package/dist/esm/lib/cause/validation.js +19 -36
- package/dist/esm/lib/{cause → evidence}/admission.d.ts +5 -1
- package/dist/esm/lib/{cause → evidence}/admission.js +14 -1
- package/dist/esm/lib/evidence/codes.d.ts +33 -0
- package/dist/esm/lib/evidence/codes.js +28 -0
- package/dist/esm/lib/evidence/requirementTypes.d.ts +46 -0
- package/dist/esm/lib/evidence/requirementTypes.js +1 -0
- package/dist/esm/lib/evidence/requirements.d.ts +24 -0
- package/dist/esm/lib/evidence/requirements.js +185 -0
- package/dist/index.js +5 -1
- package/dist/lib/cause/explain.js +6 -6
- package/dist/lib/cause/queryMemo.js +52 -0
- package/dist/lib/cause/recordAdapter.js +10 -17
- package/dist/lib/cause/recordChildren.js +3 -4
- package/dist/lib/cause/recordCoverage.js +1 -1
- package/dist/lib/cause/recordIdentity.js +4 -4
- package/dist/lib/cause/recordLoading.js +2 -2
- package/dist/lib/cause/recordQuery.js +27 -0
- package/dist/lib/cause/recordScope.js +12 -12
- package/dist/lib/cause/recordValidation.js +21 -21
- package/dist/lib/cause/session.js +8 -5
- package/dist/lib/cause/storedRecordAdapter.js +15 -9
- package/dist/lib/cause/validation.js +23 -40
- package/dist/lib/{cause → evidence}/admission.js +14 -1
- package/dist/lib/evidence/codes.js +33 -0
- package/dist/lib/evidence/requirementTypes.js +2 -0
- package/dist/lib/evidence/requirements.js +190 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/lib/cause/queryMemo.d.ts +18 -0
- package/dist/types/lib/cause/recordChildren.d.ts +3 -3
- package/dist/types/lib/cause/recordCoverage.d.ts +2 -2
- package/dist/types/lib/cause/recordQuery.d.ts +9 -0
- package/dist/types/lib/cause/recordValidation.d.ts +4 -4
- package/dist/types/lib/cause/types.d.ts +2 -1
- package/dist/types/lib/cause/validation.d.ts +4 -4
- package/dist/types/lib/{cause → evidence}/admission.d.ts +5 -1
- package/dist/types/lib/evidence/codes.d.ts +33 -0
- package/dist/types/lib/evidence/requirementTypes.d.ts +46 -0
- package/dist/types/lib/evidence/requirements.d.ts +24 -0
- package/package.json +1 -1
- /package/dist/esm/lib/cause/{recordParents.d.ts → recordInspection.d.ts} +0 -0
- /package/dist/esm/lib/cause/{recordParents.js → recordInspection.js} +0 -0
- /package/dist/lib/cause/{recordParents.js → recordInspection.js} +0 -0
- /package/dist/types/lib/cause/{recordParents.d.ts → recordInspection.d.ts} +0 -0
|
@@ -7,13 +7,13 @@ const validation_js_1 = require("./validation.js");
|
|
|
7
7
|
const ranking_js_1 = require("./ranking.js");
|
|
8
8
|
/** Read-only deterministic cause localisation and impact. No inference from timestamps or structural ancestry. */
|
|
9
9
|
async function explain(graph, nodeId, options = {}) {
|
|
10
|
-
(0, validation_js_1.
|
|
10
|
+
(0, validation_js_1.graphName)(nodeId);
|
|
11
11
|
const session = new session_js_1.Session(graph, options);
|
|
12
12
|
try {
|
|
13
13
|
const backward = await (0, walk_js_1.walk)(session, [nodeId], 'parents');
|
|
14
14
|
const missing = [...backward.gaps];
|
|
15
15
|
let candidates = (0, walk_js_1.frontier)(backward).map((item) => item.nodeId);
|
|
16
|
-
const rank = (0, validation_js_1.
|
|
16
|
+
const rank = (0, validation_js_1.graphOwn)(options, 'rankRoots');
|
|
17
17
|
if (rank !== undefined) {
|
|
18
18
|
if (typeof rank !== 'function')
|
|
19
19
|
throw new TypeError('CAUSE_CONFIGURATION_INVALID');
|
|
@@ -34,7 +34,7 @@ async function explain(graph, nodeId, options = {}) {
|
|
|
34
34
|
const rootCause = causeComplete && candidates.length === 1 ? candidates[0] : undefined;
|
|
35
35
|
const forward = await (0, walk_js_1.walk)(session, [nodeId], 'children');
|
|
36
36
|
let impactNodes = Object.freeze(forward.nodes.filter((id) => id !== nodeId));
|
|
37
|
-
const impactGaps = [...forward.gaps], rankImpact = (0, validation_js_1.
|
|
37
|
+
const impactGaps = [...forward.gaps], rankImpact = (0, validation_js_1.graphOwn)(options, 'rankImpact');
|
|
38
38
|
if (typeof rankImpact === 'function') {
|
|
39
39
|
const selection = await session.invoke(undefined, () => rankImpact(impactNodes, session.context), (raw) => (0, ranking_js_1.admitRanking)(raw, new Set(impactNodes), {
|
|
40
40
|
exactPermutation: true,
|
|
@@ -73,19 +73,19 @@ async function attentionList(graph, options = {}) {
|
|
|
73
73
|
const session = new session_js_1.Session(graph, options);
|
|
74
74
|
try {
|
|
75
75
|
let seeds = [], missing = [];
|
|
76
|
-
const requested = (0, validation_js_1.
|
|
76
|
+
const requested = (0, validation_js_1.graphOwn)(options, 'nodeIds');
|
|
77
77
|
const admitSeeds = (raw) => {
|
|
78
78
|
// Only inspect the bounded prefix; the unread tail is explicitly incomplete.
|
|
79
79
|
if (!Array.isArray(raw))
|
|
80
80
|
return (0, validation_js_1.nodeIds)(raw, session.limits.maxNodes);
|
|
81
|
-
const length = (0, validation_js_1.
|
|
81
|
+
const length = (0, validation_js_1.graphOwn)(raw, 'length');
|
|
82
82
|
if (typeof length !== 'number' || !Number.isSafeInteger(length) || length < 0)
|
|
83
83
|
return (0, validation_js_1.nodeIds)(raw, session.limits.maxNodes);
|
|
84
84
|
if (length > session.limits.maxNodes)
|
|
85
85
|
missing.push((0, validation_js_1.gap)('node-limit'));
|
|
86
86
|
const prefix = [];
|
|
87
87
|
for (let i = 0; i < Math.min(length, session.limits.maxNodes); i++)
|
|
88
|
-
prefix.push((0, validation_js_1.
|
|
88
|
+
prefix.push((0, validation_js_1.graphOwn)(raw, String(i)));
|
|
89
89
|
return (0, validation_js_1.nodeIds)(prefix, session.limits.maxNodes);
|
|
90
90
|
};
|
|
91
91
|
if (requested !== undefined) {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.endQueryMemo = exports.queryMemo = exports.beginQueryMemo = void 0;
|
|
4
|
+
/** Share bounded derived work within one query without retaining it in the graph's storage cache. */
|
|
5
|
+
class QueryMemo {
|
|
6
|
+
maximumEntries;
|
|
7
|
+
owners = new Map();
|
|
8
|
+
entries = 0;
|
|
9
|
+
closed = false;
|
|
10
|
+
constructor(maximumEntries) {
|
|
11
|
+
this.maximumEntries = maximumEntries;
|
|
12
|
+
}
|
|
13
|
+
remember(owner, key, create) {
|
|
14
|
+
const previous = this.owners.get(owner);
|
|
15
|
+
if (previous?.has(key))
|
|
16
|
+
return previous.get(key);
|
|
17
|
+
if (this.closed || this.entries >= this.maximumEntries)
|
|
18
|
+
return create();
|
|
19
|
+
const value = create();
|
|
20
|
+
// Creation can finish after disposal or re-enter other readers; neither may refill a closed/full memo.
|
|
21
|
+
if (!this.closed && this.entries < this.maximumEntries) {
|
|
22
|
+
const bucket = this.owners.get(owner) ?? new Map();
|
|
23
|
+
if (!bucket.has(key))
|
|
24
|
+
this.entries++;
|
|
25
|
+
bucket.set(key, value);
|
|
26
|
+
this.owners.set(owner, bucket);
|
|
27
|
+
}
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
close() {
|
|
31
|
+
this.closed = true;
|
|
32
|
+
this.owners.clear();
|
|
33
|
+
this.entries = 0;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const queries = new WeakMap();
|
|
37
|
+
/** Reserve one resolution and one inspection per admitted node, shared across all adapter owners. */
|
|
38
|
+
function beginQueryMemo(context, maximumNodes) {
|
|
39
|
+
queries.set(context, new QueryMemo(maximumNodes * 2));
|
|
40
|
+
}
|
|
41
|
+
exports.beginQueryMemo = beginQueryMemo;
|
|
42
|
+
/** Direct graph calls have no managed query lifetime and deliberately remain uncached. */
|
|
43
|
+
function queryMemo(context) {
|
|
44
|
+
return queries.get(context);
|
|
45
|
+
}
|
|
46
|
+
exports.queryMemo = queryMemo;
|
|
47
|
+
/** Clear synchronously without aborting an otherwise successful query's public signal. */
|
|
48
|
+
function endQueryMemo(context) {
|
|
49
|
+
queries.get(context)?.close();
|
|
50
|
+
queries.delete(context);
|
|
51
|
+
}
|
|
52
|
+
exports.endQueryMemo = endQueryMemo;
|
|
@@ -5,38 +5,31 @@ const recordChildren_js_1 = require("./recordChildren.js");
|
|
|
5
5
|
const recordCoverage_js_1 = require("./recordCoverage.js");
|
|
6
6
|
const recordIdentity_js_1 = require("./recordIdentity.js");
|
|
7
7
|
const recordLoading_js_1 = require("./recordLoading.js");
|
|
8
|
-
const
|
|
8
|
+
const recordQuery_js_1 = require("./recordQuery.js");
|
|
9
9
|
const recordScope_js_1 = require("./recordScope.js");
|
|
10
10
|
const recordValidation_js_1 = require("./recordValidation.js");
|
|
11
|
+
/** Give each failed port its own incomplete page rather than sharing a mutable edge array. */
|
|
12
|
+
const unavailableAdjacency = () => ({ edges: [], complete: false });
|
|
11
13
|
var recordScope_js_2 = require("./recordScope.js");
|
|
12
14
|
Object.defineProperty(exports, "recordOptions", { enumerable: true, get: function () { return recordScope_js_2.recordOptions; } });
|
|
13
15
|
/** Common engine for memory and one-scope-at-a-time host storage. Loader functions are trusted executable code. */
|
|
14
16
|
function createLazyRecordGraph(load, options = {}) {
|
|
15
17
|
const limits = (0, recordScope_js_1.recordOptions)(options), loader = (0, recordLoading_js_1.createRecordScopeLoader)(load, limits);
|
|
16
|
-
const
|
|
18
|
+
const query = (0, recordQuery_js_1.createRecordQuery)(loader, limits.maxEdges);
|
|
17
19
|
return Object.freeze({
|
|
18
20
|
nodeId: (address, node) => (0, recordIdentity_js_1.nodeIdFor)(address, node),
|
|
19
21
|
openScope: (address, context) => (0, recordLoading_js_1.openRecordScope)(address, context, loader),
|
|
20
22
|
async parents(nodeId, context) {
|
|
21
|
-
|
|
22
|
-
return 'failure' in node
|
|
23
|
-
? { edges: [], complete: false }
|
|
24
|
-
: (0, recordParents_js_1.inspectRecordNode)(node, limits.maxEdges, loader.extraGapCodes(node.scope)).adjacency;
|
|
23
|
+
return (0, recordQuery_js_1.whenResolved)(await query.resolve(nodeId, context), (node) => query.inspect(node, context).adjacency, unavailableAdjacency);
|
|
25
24
|
},
|
|
26
25
|
async gaps(nodeId, context) {
|
|
27
|
-
|
|
28
|
-
return 'failure' in node
|
|
29
|
-
? (0, recordCoverage_js_1.gapForRecordFailure)(node.failure)
|
|
30
|
-
: (0, recordParents_js_1.inspectRecordNode)(node, limits.maxEdges, loader.extraGapCodes(node.scope)).gaps;
|
|
26
|
+
return (0, recordQuery_js_1.whenResolved)(await query.resolve(nodeId, context), (node) => query.inspect(node, context).gaps, (failure) => (0, recordCoverage_js_1.gapForRecordFailure)(failure.failure));
|
|
31
27
|
},
|
|
32
28
|
async attention(nodeId, context) {
|
|
33
|
-
return (0,
|
|
29
|
+
return (0, recordQuery_js_1.whenResolved)(await query.resolve(nodeId, context), recordCoverage_js_1.attentionForRecordNode, () => []);
|
|
34
30
|
},
|
|
35
31
|
async children(nodeId, context) {
|
|
36
|
-
|
|
37
|
-
return 'failure' in node
|
|
38
|
-
? { edges: [], complete: false }
|
|
39
|
-
: (0, recordChildren_js_1.childrenForRecordNode)(node, limits.maxEdges, loader.extraGapCodes(node.scope));
|
|
32
|
+
return (0, recordQuery_js_1.whenResolved)(await query.resolve(nodeId, context), (node) => (0, recordChildren_js_1.childrenForRecordNode)(node, limits.maxEdges, query.inspect(node, context)), unavailableAdjacency);
|
|
40
33
|
},
|
|
41
34
|
});
|
|
42
35
|
}
|
|
@@ -45,8 +38,8 @@ exports.createLazyRecordGraph = createLazyRecordGraph;
|
|
|
45
38
|
function createRecordGraph(scopes, options = {}) {
|
|
46
39
|
const limits = (0, recordScope_js_1.recordOptions)(options);
|
|
47
40
|
return createLazyRecordGraph((address) => {
|
|
48
|
-
for (const raw of (0, recordValidation_js_1.
|
|
49
|
-
if ((0, recordValidation_js_1.addressKey)((0, recordValidation_js_1.addressOf)((0, recordValidation_js_1.
|
|
41
|
+
for (const raw of (0, recordValidation_js_1.recordList)(scopes, limits.maxScopes)) {
|
|
42
|
+
if ((0, recordValidation_js_1.addressKey)((0, recordValidation_js_1.addressOf)((0, recordValidation_js_1.recordOwn)(raw, 'address'))) === (0, recordValidation_js_1.addressKey)(address))
|
|
50
43
|
return { scope: raw };
|
|
51
44
|
}
|
|
52
45
|
throw new recordValidation_js_1.RecordRefusal('unavailable');
|
|
@@ -5,7 +5,6 @@ const forwardSliceForKey_js_1 = require("../slice/forwardSliceForKey.js");
|
|
|
5
5
|
const recordControl_js_1 = require("./recordControl.js");
|
|
6
6
|
const recordCoverage_js_1 = require("./recordCoverage.js");
|
|
7
7
|
const recordIdentity_js_1 = require("./recordIdentity.js");
|
|
8
|
-
const recordParents_js_1 = require("./recordParents.js");
|
|
9
8
|
const recordScope_js_1 = require("./recordScope.js");
|
|
10
9
|
/** Keep per-write and conservative fed edges distinct while retaining existing cap and deduplication order. */
|
|
11
10
|
function appendFedChildren(scope, root, maximumEdges, edges) {
|
|
@@ -70,10 +69,10 @@ function appendDataChildren(scope, identity, commitIndex, maximumEdges, edges) {
|
|
|
70
69
|
return complete;
|
|
71
70
|
}
|
|
72
71
|
/** Share parent-side coverage checks before appending data then control children in their established order. */
|
|
73
|
-
function childrenForRecordNode(node, maximumEdges,
|
|
72
|
+
function childrenForRecordNode(node, maximumEdges, inspection) {
|
|
74
73
|
const { scope, identity, commitIndex } = node;
|
|
75
|
-
const
|
|
76
|
-
let complete =
|
|
74
|
+
const edges = [];
|
|
75
|
+
let complete = inspection.gaps.length === 0;
|
|
77
76
|
if (!(0, recordCoverage_js_1.allStageReadsCollected)(scope))
|
|
78
77
|
complete = false;
|
|
79
78
|
complete = appendDataChildren(scope, identity, commitIndex, maximumEdges, edges) && complete;
|
|
@@ -25,7 +25,7 @@ exports.gapForRecordFailure = gapForRecordFailure;
|
|
|
25
25
|
/** Return captured mark references without treating an omitted attention map as collected evidence. */
|
|
26
26
|
function attentionForRecordNode(node) {
|
|
27
27
|
// An omitted map stays unknown coverage; it is never replaced by an affirmative empty map.
|
|
28
|
-
return
|
|
28
|
+
return node.scope.marks?.get(node.identity.runtimeStageId) ?? [];
|
|
29
29
|
}
|
|
30
30
|
exports.attentionForRecordNode = attentionForRecordNode;
|
|
31
31
|
/** Emit host and capture gaps before query-derived gaps so existing evidence order stays stable. */
|
|
@@ -14,7 +14,7 @@ function readNodeIdentity(nodeId) {
|
|
|
14
14
|
catch {
|
|
15
15
|
throw new recordValidation_js_1.RecordRefusal();
|
|
16
16
|
}
|
|
17
|
-
const address = (0, recordValidation_js_1.addressOf)((0, recordValidation_js_1.
|
|
17
|
+
const address = (0, recordValidation_js_1.addressOf)((0, recordValidation_js_1.recordOwn)(fields, 'address')), runtimeStageId = (0, recordValidation_js_1.recordName)((0, recordValidation_js_1.recordOwn)(fields, 'runtimeStageId')), commitIndex = (0, recordValidation_js_1.recordOwn)(fields, 'commitIdx'), key = (0, recordValidation_js_1.recordOwn)(fields, 'key'), read = (0, recordValidation_js_1.recordOwn)(fields, 'read');
|
|
18
18
|
if (commitIndex !== undefined && (!Number.isSafeInteger(commitIndex) || commitIndex < 0))
|
|
19
19
|
throw new recordValidation_js_1.RecordRefusal();
|
|
20
20
|
if ((read !== undefined && read !== true) || (read === true && key === undefined))
|
|
@@ -23,7 +23,7 @@ function readNodeIdentity(nodeId) {
|
|
|
23
23
|
address,
|
|
24
24
|
runtimeStageId,
|
|
25
25
|
...(commitIndex === undefined ? {} : { commitIdx: commitIndex }),
|
|
26
|
-
...(key === undefined ? {} : { key: (0, recordValidation_js_1.
|
|
26
|
+
...(key === undefined ? {} : { key: (0, recordValidation_js_1.recordName)(key, true) }),
|
|
27
27
|
...(read === true ? { read: true } : {}),
|
|
28
28
|
};
|
|
29
29
|
}
|
|
@@ -31,7 +31,7 @@ exports.readNodeIdentity = readNodeIdentity;
|
|
|
31
31
|
/** Stable field order preserves existing address/execution/key IDs across storage and memory readers. */
|
|
32
32
|
function nodeIdFor(addressInput, node, read) {
|
|
33
33
|
try {
|
|
34
|
-
const address = (0, recordValidation_js_1.addressOf)(addressInput), runtimeStageId = (0, recordValidation_js_1.
|
|
34
|
+
const address = (0, recordValidation_js_1.addressOf)(addressInput), runtimeStageId = (0, recordValidation_js_1.recordName)((0, recordValidation_js_1.recordOwn)(node, 'runtimeStageId')), commitIndex = (0, recordValidation_js_1.recordOwn)(node, 'commitIdx'), key = (0, recordValidation_js_1.recordOwn)(node, 'key');
|
|
35
35
|
if (commitIndex !== undefined && (!Number.isSafeInteger(commitIndex) || commitIndex < 0))
|
|
36
36
|
throw new recordValidation_js_1.RecordRefusal();
|
|
37
37
|
const nodeId = 'record:' +
|
|
@@ -39,7 +39,7 @@ function nodeIdFor(addressInput, node, read) {
|
|
|
39
39
|
address,
|
|
40
40
|
runtimeStageId,
|
|
41
41
|
...(commitIndex === undefined ? {} : { commitIdx: commitIndex }),
|
|
42
|
-
...(key === undefined ? {} : { key: (0, recordValidation_js_1.
|
|
42
|
+
...(key === undefined ? {} : { key: (0, recordValidation_js_1.recordName)(key, true) }),
|
|
43
43
|
...(read ? { read: true } : {}),
|
|
44
44
|
});
|
|
45
45
|
return nodeId.length <= 2048 ? nodeId : 'record:invalid';
|
|
@@ -13,8 +13,8 @@ function admitLoadedScope(result, request, limits, state) {
|
|
|
13
13
|
}
|
|
14
14
|
let prepared;
|
|
15
15
|
try {
|
|
16
|
-
prepared = (0, recordScope_js_1.prepareRecordScope)((0, recordValidation_js_1.
|
|
17
|
-
const extra = (0, recordValidation_js_1.
|
|
16
|
+
prepared = (0, recordScope_js_1.prepareRecordScope)((0, recordValidation_js_1.recordOwn)(result, 'scope'), request.address, limits.maxCommits, limits.maxFields);
|
|
17
|
+
const extra = (0, recordValidation_js_1.recordList)((0, recordValidation_js_1.recordOwn)(result, 'gaps') ?? [], 32);
|
|
18
18
|
// Coverage belongs to this admitted object, so cached scopes retain the same gap ordering and ownership.
|
|
19
19
|
state.extraGaps.set(prepared, (0, validation_js_1.gaps)(extra.map((code) => ({ code, evidence: [] }))).map((gap) => gap.code));
|
|
20
20
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.whenResolved = exports.createRecordQuery = void 0;
|
|
4
|
+
const queryMemo_js_1 = require("./queryMemo.js");
|
|
5
|
+
const recordIdentity_js_1 = require("./recordIdentity.js");
|
|
6
|
+
const recordInspection_js_1 = require("./recordInspection.js");
|
|
7
|
+
/** Both dependency directions share the same admitted node and canonical inspection within a query. */
|
|
8
|
+
function createRecordQuery(loader, maximumEdges) {
|
|
9
|
+
const resolutionOwner = {}, inspectionOwner = {};
|
|
10
|
+
return {
|
|
11
|
+
resolve(nodeId, context) {
|
|
12
|
+
const resolve = () => (0, recordIdentity_js_1.resolveRecordNode)(nodeId, context, loader), memo = (0, queryMemo_js_1.queryMemo)(context);
|
|
13
|
+
// A retained success must never bypass the loader's existing cancellation check.
|
|
14
|
+
return memo && !context.signal.aborted ? memo.remember(resolutionOwner, nodeId, resolve) : resolve();
|
|
15
|
+
},
|
|
16
|
+
inspect(node, context) {
|
|
17
|
+
const inspect = () => (0, recordInspection_js_1.inspectRecordNode)(node, maximumEdges, loader.extraGapCodes(node.scope));
|
|
18
|
+
return (0, queryMemo_js_1.queryMemo)(context)?.remember(inspectionOwner, node, inspect) ?? inspect();
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
exports.createRecordQuery = createRecordQuery;
|
|
23
|
+
/** Every port maps the same fixed loading failure while projecting only admitted nodes. */
|
|
24
|
+
function whenResolved(node, onNode, onFailure) {
|
|
25
|
+
return 'failure' in node ? onFailure(node) : onNode(node);
|
|
26
|
+
}
|
|
27
|
+
exports.whenResolved = whenResolved;
|
|
@@ -19,11 +19,11 @@ function recordOptions(options) {
|
|
|
19
19
|
if (prototype !== Object.prototype && prototype !== null)
|
|
20
20
|
throw new TypeError();
|
|
21
21
|
return Object.freeze({
|
|
22
|
-
maxCommits: boundedOption((0, recordValidation_js_1.
|
|
23
|
-
maxFields: boundedOption((0, recordValidation_js_1.
|
|
24
|
-
maxEdges: boundedOption((0, recordValidation_js_1.
|
|
25
|
-
maxScopes: boundedOption((0, recordValidation_js_1.
|
|
26
|
-
loadTimeoutMs: boundedOption((0, recordValidation_js_1.
|
|
22
|
+
maxCommits: boundedOption((0, recordValidation_js_1.recordOwn)(options, 'maxCommits'), 256, 4096),
|
|
23
|
+
maxFields: boundedOption((0, recordValidation_js_1.recordOwn)(options, 'maxFields'), 20000, 1000000),
|
|
24
|
+
maxEdges: boundedOption((0, recordValidation_js_1.recordOwn)(options, 'maxEdges'), 64, 256),
|
|
25
|
+
maxScopes: boundedOption((0, recordValidation_js_1.recordOwn)(options, 'maxScopes'), 32, 256),
|
|
26
|
+
loadTimeoutMs: boundedOption((0, recordValidation_js_1.recordOwn)(options, 'loadTimeoutMs'), 1000, 30000),
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
29
|
catch {
|
|
@@ -33,22 +33,22 @@ function recordOptions(options) {
|
|
|
33
33
|
exports.recordOptions = recordOptions;
|
|
34
34
|
/** Spend one shared field budget before canonical readers see any untrusted scope data. */
|
|
35
35
|
function prepareRecordScope(value, address, maximumCommits, maximumFields) {
|
|
36
|
-
if ((0, recordValidation_js_1.addressKey)((0, recordValidation_js_1.addressOf)((0, recordValidation_js_1.
|
|
36
|
+
if ((0, recordValidation_js_1.addressKey)((0, recordValidation_js_1.addressOf)((0, recordValidation_js_1.recordOwn)(value, 'address'))) !== (0, recordValidation_js_1.addressKey)(address))
|
|
37
37
|
throw new recordValidation_js_1.RecordRefusal();
|
|
38
|
-
const rawLog = (0, recordValidation_js_1.
|
|
39
|
-
const log = rawLog.map((_, commitIndex) => (0, recordValidation_js_1.bundleOf)((0, recordValidation_js_1.
|
|
40
|
-
const reads = (0, recordValidation_js_1.mapOf)((0, recordValidation_js_1.
|
|
38
|
+
const rawLog = (0, recordValidation_js_1.recordList)((0, recordValidation_js_1.recordOwn)(value, 'commitLog'), maximumCommits), budget = { left: maximumFields };
|
|
39
|
+
const log = rawLog.map((_, commitIndex) => (0, recordValidation_js_1.bundleOf)((0, recordValidation_js_1.recordOwn)(rawLog, String(commitIndex)), commitIndex, budget));
|
|
40
|
+
const reads = (0, recordValidation_js_1.mapOf)((0, recordValidation_js_1.recordOwn)(value, 'reads'), maximumCommits, (value) => {
|
|
41
41
|
const readKeys = (0, recordValidation_js_1.names)(value, 128, true);
|
|
42
42
|
(0, recordValidation_js_1.spend)(budget, readKeys.length);
|
|
43
43
|
return readKeys;
|
|
44
44
|
}, budget);
|
|
45
|
-
const control = (0, recordValidation_js_1.mapOf)((0, recordValidation_js_1.
|
|
46
|
-
const marks = (0, recordValidation_js_1.mapOf)((0, recordValidation_js_1.
|
|
45
|
+
const control = (0, recordValidation_js_1.mapOf)((0, recordValidation_js_1.recordOwn)(value, 'controlDeps'), maximumCommits, (value) => ({ deciderId: (0, recordValidation_js_1.recordName)((0, recordValidation_js_1.recordOwn)(value, 'deciderId')) }), budget);
|
|
46
|
+
const marks = (0, recordValidation_js_1.mapOf)((0, recordValidation_js_1.recordOwn)(value, 'attention'), maximumCommits, (value) => {
|
|
47
47
|
const attention = (0, recordValidation_js_1.marksOf)(value);
|
|
48
48
|
(0, recordValidation_js_1.spend)(budget, attention.length + attention.reduce((count, mark) => count + mark.evidence.length, 0));
|
|
49
49
|
return attention;
|
|
50
50
|
}, budget);
|
|
51
|
-
return { address, log, reads, control, marks, complete: (0, recordValidation_js_1.
|
|
51
|
+
return { address, log, reads, control, marks, complete: (0, recordValidation_js_1.recordOwn)(value, 'complete') === true };
|
|
52
52
|
}
|
|
53
53
|
exports.prepareRecordScope = prepareRecordScope;
|
|
54
54
|
/** Preserve both stage reads and stronger per-write prefixes, including their original encounter order. */
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.projectLoaded = exports.mapOf = exports.bundleOf = exports.dataCopy = exports.addressKey = exports.spend = exports.addressOf = exports.
|
|
3
|
+
exports.projectLoaded = exports.mapOf = exports.bundleOf = exports.dataCopy = exports.addressKey = exports.spend = exports.addressOf = exports.recordList = exports.recordName = exports.names = exports.recordOwn = exports.RecordRefusal = exports.marksOf = void 0;
|
|
4
4
|
const validation_js_1 = require("./validation.js");
|
|
5
5
|
Object.defineProperty(exports, "marksOf", { enumerable: true, get: function () { return validation_js_1.attention; } });
|
|
6
|
-
const admission_js_1 = require("
|
|
6
|
+
const admission_js_1 = require("../evidence/admission.js");
|
|
7
7
|
/** Loader refusals stay distinct from graph-port refusals so a failed host load keeps its original classification. */
|
|
8
8
|
class RecordRefusal extends admission_js_1.EvidenceRefusal {
|
|
9
9
|
code;
|
|
@@ -15,19 +15,19 @@ class RecordRefusal extends admission_js_1.EvidenceRefusal {
|
|
|
15
15
|
exports.RecordRefusal = RecordRefusal;
|
|
16
16
|
const refuseRecord = (code) => new RecordRefusal(code);
|
|
17
17
|
const recordDescriptors = { translateTrapErrors: true, requireOwnValue: true };
|
|
18
|
-
function
|
|
18
|
+
function recordOwn(value, key) {
|
|
19
19
|
return (0, admission_js_1.ownData)(value, key, refuseRecord, recordDescriptors);
|
|
20
20
|
}
|
|
21
|
-
exports.
|
|
21
|
+
exports.recordOwn = recordOwn;
|
|
22
22
|
function names(value, maximum = 128, path = false) {
|
|
23
|
-
return
|
|
23
|
+
return recordList(value, maximum).map((item) => recordName(item, path));
|
|
24
24
|
}
|
|
25
25
|
exports.names = names;
|
|
26
|
-
function
|
|
26
|
+
function recordName(value, path = false) {
|
|
27
27
|
return (0, admission_js_1.boundedName)(value, 1024, refuseRecord, path);
|
|
28
28
|
}
|
|
29
|
-
exports.
|
|
30
|
-
function
|
|
29
|
+
exports.recordName = recordName;
|
|
30
|
+
function recordList(value, maximum) {
|
|
31
31
|
return (0, admission_js_1.boundedList)(value, maximum, (item) => item, {
|
|
32
32
|
refuse: refuseRecord,
|
|
33
33
|
overflow: 'node-limit',
|
|
@@ -35,9 +35,9 @@ function list(value, maximum) {
|
|
|
35
35
|
descriptors: recordDescriptors,
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
|
-
exports.
|
|
38
|
+
exports.recordList = recordList;
|
|
39
39
|
function addressOf(value) {
|
|
40
|
-
const logRunId =
|
|
40
|
+
const logRunId = recordName(recordOwn(value, 'logRunId')), drillPath = names(recordOwn(value, 'drillPath'), 16);
|
|
41
41
|
if (logRunId.length + drillPath.reduce((n, s) => n + s.length, 0) > 1536)
|
|
42
42
|
throw new RecordRefusal();
|
|
43
43
|
return Object.freeze({ logRunId, drillPath: Object.freeze(drillPath) });
|
|
@@ -71,7 +71,7 @@ function dataCopy(value, budget) {
|
|
|
71
71
|
if (!Array.isArray(v) && p !== Object.prototype && p !== null)
|
|
72
72
|
throw new RecordRefusal();
|
|
73
73
|
if (Array.isArray(v)) {
|
|
74
|
-
const length =
|
|
74
|
+
const length = recordOwn(v, 'length');
|
|
75
75
|
if (typeof length !== 'number' || !Number.isSafeInteger(length) || length < 0)
|
|
76
76
|
throw new RecordRefusal();
|
|
77
77
|
spend(budget, length);
|
|
@@ -96,7 +96,7 @@ function dataCopy(value, budget) {
|
|
|
96
96
|
for (const key of keys) {
|
|
97
97
|
if (key.length > 1024)
|
|
98
98
|
throw new RecordRefusal('node-limit');
|
|
99
|
-
const v =
|
|
99
|
+
const v = recordOwn(from, key);
|
|
100
100
|
check(v);
|
|
101
101
|
let copy = v;
|
|
102
102
|
if (!leaf(v)) {
|
|
@@ -113,16 +113,16 @@ function dataCopy(value, budget) {
|
|
|
113
113
|
}
|
|
114
114
|
exports.dataCopy = dataCopy;
|
|
115
115
|
function bundleOf(value, idx, budget) {
|
|
116
|
-
const runtimeStageId =
|
|
117
|
-
const trace =
|
|
118
|
-
const t =
|
|
116
|
+
const runtimeStageId = recordName(recordOwn(value, 'runtimeStageId')), stageId = recordName(recordOwn(value, 'stageId'));
|
|
117
|
+
const trace = recordList(recordOwn(value, 'trace'), 128).map((_, i) => {
|
|
118
|
+
const t = recordOwn(recordOwn(value, 'trace'), String(i)), path = recordName(recordOwn(t, 'path'), true), verb = recordOwn(t, 'verb');
|
|
119
119
|
if (!['set', 'merge', 'append', 'delete'].includes(verb))
|
|
120
120
|
throw new RecordRefusal();
|
|
121
|
-
const readKeys =
|
|
121
|
+
const readKeys = recordOwn(t, 'readKeys');
|
|
122
122
|
return { path, verb: verb, ...(readKeys === undefined ? {} : { readKeys: names(readKeys, 128, true) }) };
|
|
123
123
|
});
|
|
124
124
|
spend(budget, trace.length + trace.reduce((n, t) => n + (t.readKeys?.length ?? 0), 0) + 1);
|
|
125
|
-
const overwrite = dataCopy(
|
|
125
|
+
const overwrite = dataCopy(recordOwn(value, 'overwrite'), budget), updates = dataCopy(recordOwn(value, 'updates'), budget);
|
|
126
126
|
if (!overwrite ||
|
|
127
127
|
typeof overwrite !== 'object' ||
|
|
128
128
|
Array.isArray(overwrite) ||
|
|
@@ -130,9 +130,9 @@ function bundleOf(value, idx, budget) {
|
|
|
130
130
|
typeof updates !== 'object' ||
|
|
131
131
|
Array.isArray(updates))
|
|
132
132
|
throw new RecordRefusal();
|
|
133
|
-
const redactedPaths = names(
|
|
134
|
-
const untracked =
|
|
135
|
-
spend(budget, redactedPaths.length + (untracked === undefined ? 0 :
|
|
133
|
+
const redactedPaths = names(recordOwn(value, 'redactedPaths') ?? [], 128, true);
|
|
134
|
+
const untracked = recordOwn(value, 'untrackedSources'), phase = recordOwn(value, 'phase');
|
|
135
|
+
spend(budget, redactedPaths.length + (untracked === undefined ? 0 : recordList(untracked, 128).length));
|
|
136
136
|
return {
|
|
137
137
|
idx,
|
|
138
138
|
stage: stageId,
|
|
@@ -179,7 +179,7 @@ function mapOf(value, limit, read, budget) {
|
|
|
179
179
|
throw new RecordRefusal('node-limit');
|
|
180
180
|
if (budget)
|
|
181
181
|
spend(budget, keys.length);
|
|
182
|
-
return new Map(keys.map((k) => [
|
|
182
|
+
return new Map(keys.map((k) => [recordName(k), read(recordOwn(value, k))]));
|
|
183
183
|
}
|
|
184
184
|
exports.mapOf = mapOf;
|
|
185
185
|
/** Observe native promises by intrinsic without assimilating a data object's then accessor. */
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Session = void 0;
|
|
4
4
|
const validation_js_1 = require("./validation.js");
|
|
5
|
+
const queryMemo_js_1 = require("./queryMemo.js");
|
|
5
6
|
const defaults = { maxNodes: 100, maxDepth: 20, maxEdges: 1000, maxCalls: 1000, timeoutMs: 1000 };
|
|
6
7
|
const ceilings = { maxNodes: 10000, maxDepth: 1000, maxEdges: 100000, maxCalls: 100000, timeoutMs: 30000 };
|
|
7
8
|
/** One deadline, memo table and work budget per public query, never per graph or store. */
|
|
@@ -24,7 +25,7 @@ class Session {
|
|
|
24
25
|
(0, validation_js_1.plain)(options);
|
|
25
26
|
const limits = { ...defaults };
|
|
26
27
|
for (const key of Object.keys(defaults)) {
|
|
27
|
-
const value = (0, validation_js_1.
|
|
28
|
+
const value = (0, validation_js_1.graphOwn)(options, key);
|
|
28
29
|
if (value !== undefined) {
|
|
29
30
|
if (typeof value !== 'number' ||
|
|
30
31
|
!Number.isSafeInteger(value) ||
|
|
@@ -35,13 +36,13 @@ class Session {
|
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
this.limits = Object.freeze(limits);
|
|
38
|
-
const rank = (0, validation_js_1.
|
|
39
|
+
const rank = (0, validation_js_1.graphOwn)(options, 'rankRoots');
|
|
39
40
|
if (rank !== undefined && typeof rank !== 'function')
|
|
40
41
|
throw new TypeError();
|
|
41
|
-
const impactRank = (0, validation_js_1.
|
|
42
|
+
const impactRank = (0, validation_js_1.graphOwn)(options, 'rankImpact');
|
|
42
43
|
if (impactRank !== undefined && typeof impactRank !== 'function')
|
|
43
44
|
throw new TypeError();
|
|
44
|
-
const signal = (0, validation_js_1.
|
|
45
|
+
const signal = (0, validation_js_1.graphOwn)(options, 'signal');
|
|
45
46
|
if (signal !== undefined && !(signal instanceof AbortSignal))
|
|
46
47
|
throw new TypeError();
|
|
47
48
|
this.external = signal;
|
|
@@ -74,8 +75,10 @@ class Session {
|
|
|
74
75
|
this.timedOut = true;
|
|
75
76
|
this.controller.abort();
|
|
76
77
|
}, this.limits.timeoutMs);
|
|
78
|
+
(0, queryMemo_js_1.beginQueryMemo)(this.context, this.limits.maxNodes);
|
|
77
79
|
}
|
|
78
80
|
dispose() {
|
|
81
|
+
(0, queryMemo_js_1.endQueryMemo)(this.context);
|
|
79
82
|
clearTimeout(this.timer);
|
|
80
83
|
this.external?.removeEventListener('abort', this.onAbort);
|
|
81
84
|
}
|
|
@@ -88,7 +91,7 @@ class Session {
|
|
|
88
91
|
return this.controller.signal.aborted ? (0, validation_js_1.gap)(this.timedOut ? 'timeout' : 'cancelled', id) : undefined;
|
|
89
92
|
}
|
|
90
93
|
admit(id) {
|
|
91
|
-
(0, validation_js_1.
|
|
94
|
+
(0, validation_js_1.graphName)(id);
|
|
92
95
|
if (this.nodes.has(id))
|
|
93
96
|
return undefined;
|
|
94
97
|
if (this.nodes.size >= this.limits.maxNodes)
|
|
@@ -9,26 +9,32 @@ function createStoredRecordGraph(host, options = {}) {
|
|
|
9
9
|
const limits = (0, recordAdapter_js_1.recordOptions)(options);
|
|
10
10
|
return (0, recordAdapter_js_1.createLazyRecordGraph)((address, context) => (0, recordValidation_js_1.projectLoaded)(() => host.loadLog(address, context), (loaded) => {
|
|
11
11
|
const max = limits.maxCommits, budget = { left: limits.maxFields };
|
|
12
|
-
const raw = (0, recordValidation_js_1.
|
|
12
|
+
const raw = (0, recordValidation_js_1.recordList)((0, recordValidation_js_1.recordOwn)(loaded, 'entries'), max * 4 + 16), entries = [];
|
|
13
13
|
for (let i = 0; i < raw.length; i++) {
|
|
14
14
|
if (context.signal.aborted)
|
|
15
15
|
throw new recordValidation_js_1.RecordRefusal('cancelled');
|
|
16
|
-
const entry = (0, recordValidation_js_1.
|
|
16
|
+
const entry = (0, recordValidation_js_1.recordOwn)(raw, String(i)), a = (0, recordValidation_js_1.addressOf)((0, recordValidation_js_1.recordOwn)(entry, 'address'));
|
|
17
17
|
if ((0, recordValidation_js_1.addressKey)(a) !== (0, recordValidation_js_1.addressKey)(address))
|
|
18
18
|
throw new recordValidation_js_1.RecordRefusal();
|
|
19
|
-
const kind = (0, recordValidation_js_1.
|
|
19
|
+
const kind = (0, recordValidation_js_1.recordOwn)(entry, 'kind'), at = (0, recordValidation_js_1.recordOwn)(entry, 'at');
|
|
20
20
|
if (typeof at !== 'number' || !Number.isFinite(at))
|
|
21
21
|
throw new recordValidation_js_1.RecordRefusal();
|
|
22
22
|
if (kind === 'commit') {
|
|
23
|
-
const b = (0, recordValidation_js_1.
|
|
23
|
+
const b = (0, recordValidation_js_1.recordOwn)(entry, 'bundle'), idx = (0, recordValidation_js_1.recordOwn)(b, 'idx');
|
|
24
24
|
if (!Number.isSafeInteger(idx) || idx < 0 || idx >= max)
|
|
25
25
|
throw new recordValidation_js_1.RecordRefusal('node-limit');
|
|
26
26
|
entries.push({ kind, address: a, at, bundle: (0, recordValidation_js_1.bundleOf)(b, idx, budget) });
|
|
27
27
|
}
|
|
28
28
|
else if (kind === 'opened')
|
|
29
|
-
entries.push({ kind, address: a, at, initialState: (0, recordValidation_js_1.dataCopy)((0, recordValidation_js_1.
|
|
29
|
+
entries.push({ kind, address: a, at, initialState: (0, recordValidation_js_1.dataCopy)((0, recordValidation_js_1.recordOwn)(entry, 'initialState'), budget) });
|
|
30
30
|
else if (kind === 'closed')
|
|
31
|
-
entries.push({
|
|
31
|
+
entries.push({
|
|
32
|
+
kind,
|
|
33
|
+
address: a,
|
|
34
|
+
at,
|
|
35
|
+
outcome: (0, recordValidation_js_1.recordOwn)(entry, 'outcome'),
|
|
36
|
+
commits: (0, recordValidation_js_1.recordOwn)(entry, 'commits'),
|
|
37
|
+
});
|
|
32
38
|
else if (kind === 'note') {
|
|
33
39
|
/* opaque note bodies never enter dependency projection */
|
|
34
40
|
}
|
|
@@ -49,9 +55,9 @@ function createStoredRecordGraph(host, options = {}) {
|
|
|
49
55
|
scope: {
|
|
50
56
|
address,
|
|
51
57
|
commitLog: log.commitLog,
|
|
52
|
-
reads: (0, recordValidation_js_1.
|
|
53
|
-
controlDeps: (0, recordValidation_js_1.
|
|
54
|
-
attention: (0, recordValidation_js_1.
|
|
58
|
+
reads: (0, recordValidation_js_1.recordOwn)(loaded, 'reads'),
|
|
59
|
+
controlDeps: (0, recordValidation_js_1.recordOwn)(loaded, 'controlDeps'),
|
|
60
|
+
attention: (0, recordValidation_js_1.recordOwn)(loaded, 'attention'),
|
|
55
61
|
complete: log.complete,
|
|
56
62
|
},
|
|
57
63
|
gaps: record.unread.length || log.gaps.length ? ['not-collected'] : [],
|