@tangleai/context 0.21.1 → 0.24.1
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/CHANGELOG.md +34 -0
- package/README.md +24 -23
- package/package.json +4 -4
- package/src/archive.d.ts +3 -3
- package/src/archive.js +49 -44
- package/src/environment.d.ts +52 -22
- package/src/environment.js +491 -548
- package/src/evidence.d.ts +5 -10
- package/src/evidence.js +49 -51
- package/src/index.d.ts +10 -9
- package/src/index.js +9 -10
- package/src/ledger.d.ts +123 -83
- package/src/ledger.js +1039 -1185
- package/src/recall.d.ts +44 -23
- package/src/recall.js +48 -68
- package/src/retention.d.ts +7 -7
- package/src/retention.js +88 -71
- package/src/schemas/evidence.d.ts +11 -10
- package/src/schemas/evidence.js +14 -21
- package/src/schemas/ledger.d.ts +737 -430
- package/src/schemas/ledger.js +130 -243
- package/src/schemas/patch.d.ts +122 -108
- package/src/schemas/patch.js +59 -64
- package/src/storage/memory.d.ts +3 -8
- package/src/storage/memory.js +41 -43
- package/src/storage/slot.d.ts +3 -4
- package/src/storage/slot.js +104 -96
- package/src/storage/transaction.d.ts +3 -18
- package/src/storage/transaction.js +22 -34
package/src/schemas/patch.d.ts
CHANGED
|
@@ -1,12 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* The refinement patch: a deliberately small subset of RFC 6902.
|
|
3
|
+
*
|
|
4
|
+
* A model that has finished a run is asked what it learned. The unsafe
|
|
5
|
+
* answer is a rewritten prompt or a restated set of memories — a
|
|
6
|
+
* rewrite is unreviewable, unbounded, and a model asked to restate what
|
|
7
|
+
* it remembers will drift it. The safe answer is a PATCH: small enough
|
|
8
|
+
* to read, addressed enough to audit, and reversible with a snapshot.
|
|
9
|
+
*
|
|
10
|
+
* Three restrictions, and each one exists because the alternative fails
|
|
11
|
+
* on the tier this package targets:
|
|
12
|
+
*
|
|
13
|
+
* - **`path` is a pattern, not a free string.** It matches exactly the
|
|
14
|
+
* supplemental subtree — append or replace a memory, append or
|
|
15
|
+
* replace a skill, append one progress entry — and nothing else. The
|
|
16
|
+
* base system prompt is not merely undocumented as a target, it is
|
|
17
|
+
* unaddressable: it is not in the document a refinement is applied
|
|
18
|
+
* to, and no path that could reach it matches this pattern. That is
|
|
19
|
+
* D5 asserted rather than described.
|
|
20
|
+
* - **`op` is three verbs.** `move`, `copy` and `test` are legal RFC
|
|
21
|
+
* 6902 and useless here; every one of them is another shape a small
|
|
22
|
+
* model can get subtly wrong, and none of them expresses anything
|
|
23
|
+
* `add`/`replace`/`remove` cannot.
|
|
24
|
+
* - **The number of operations is capped.** A refinement is meant to be
|
|
25
|
+
* a few evidence-backed updates. A patch of forty operations is a
|
|
26
|
+
* rewrite wearing a patch's clothes, and it is also the shape that
|
|
27
|
+
* makes a repair round useless — the model cannot tell which of forty
|
|
28
|
+
* operations the error came from.
|
|
29
|
+
*
|
|
30
|
+
* `value` carries no `id` and no `at` on purpose. Identity and time are
|
|
31
|
+
* the ledger's to mint: a model that could choose an id could overwrite
|
|
32
|
+
* a record it never read, and a model that could choose a timestamp
|
|
33
|
+
* could put a memory in front of one that came later — the recency
|
|
34
|
+
* ordering everything downstream depends on is not the model's to set.
|
|
6
35
|
*/
|
|
7
|
-
export function refinementPatchSchema(options?: {
|
|
8
|
-
maxOps?: number;
|
|
9
|
-
}): any;
|
|
10
36
|
/**
|
|
11
37
|
* Where a refinement may write. Anchored, and read as: append or address
|
|
12
38
|
* one memory, append or address one skill, append one progress entry.
|
|
@@ -16,20 +42,28 @@ export function refinementPatchSchema(options?: {
|
|
|
16
42
|
* a memory is revised by replacing it whole, with fresh evidence, so
|
|
17
43
|
* that every stored record was validated as a whole exactly once.
|
|
18
44
|
*/
|
|
19
|
-
export const REFINEMENT_PATH_PATTERN
|
|
45
|
+
export declare const REFINEMENT_PATH_PATTERN = "^(/memories/(-|[0-9]+)|/skills/(-|[0-9]+)|/goal/progress/-)$";
|
|
20
46
|
/** The default cap on operations per refinement. */
|
|
21
|
-
export const DEFAULT_MAX_OPS
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
47
|
+
export declare const DEFAULT_MAX_OPS = 6;
|
|
48
|
+
/**
|
|
49
|
+
* A proposed memory. `evidence` is required here AND by the ledger's own
|
|
50
|
+
* schema — the same requirement twice, on purpose: this copy is what
|
|
51
|
+
* constrains decoding (a model generating against it tends to write the
|
|
52
|
+
* evidence rather than be corrected into it), and the ledger's copy is
|
|
53
|
+
* what makes the rule true even for a patch that never went near a
|
|
54
|
+
* model. It is the mechanism that stops a refinement laundering a
|
|
55
|
+
* hallucination into durable state.
|
|
56
|
+
*/
|
|
57
|
+
export declare const MEMORY_PROPOSAL_SCHEMA: {
|
|
58
|
+
type: string;
|
|
59
|
+
properties: {
|
|
60
|
+
text: {
|
|
61
|
+
type: string;
|
|
62
|
+
minLength: number;
|
|
63
|
+
description: string;
|
|
64
|
+
};
|
|
65
|
+
evidence: {
|
|
66
|
+
anyOf: ({
|
|
33
67
|
type: "object";
|
|
34
68
|
properties: Record<string, object>;
|
|
35
69
|
required: string[];
|
|
@@ -38,92 +72,72 @@ export namespace MEMORY_PROPOSAL_SCHEMA {
|
|
|
38
72
|
type: string;
|
|
39
73
|
minLength: number;
|
|
40
74
|
})[];
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
export { minLength_6 as minLength };
|
|
109
|
-
let description_3: string;
|
|
110
|
-
export { description_3 as description };
|
|
111
|
-
}
|
|
112
|
-
export namespace evidence_1 {
|
|
113
|
-
let type_12: string;
|
|
114
|
-
export { type_12 as type };
|
|
115
|
-
let minLength_7: number;
|
|
116
|
-
export { minLength_7 as minLength };
|
|
117
|
-
let description_4: string;
|
|
118
|
-
export { description_4 as description };
|
|
119
|
-
}
|
|
120
|
-
export { evidence_1 as evidence };
|
|
121
|
-
}
|
|
122
|
-
export { properties_2 as properties };
|
|
123
|
-
let required_2: string[];
|
|
124
|
-
export { required_2 as required };
|
|
125
|
-
let additionalProperties_2: boolean;
|
|
126
|
-
export { additionalProperties_2 as additionalProperties };
|
|
127
|
-
}
|
|
75
|
+
};
|
|
76
|
+
tags: {
|
|
77
|
+
type: string;
|
|
78
|
+
items: {
|
|
79
|
+
type: string;
|
|
80
|
+
minLength: number;
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
required: string[];
|
|
85
|
+
additionalProperties: boolean;
|
|
86
|
+
};
|
|
87
|
+
/** A proposed skill: when it applies, what to do, which tools it needs. */
|
|
88
|
+
export declare const SKILL_PROPOSAL_SCHEMA: {
|
|
89
|
+
type: string;
|
|
90
|
+
properties: {
|
|
91
|
+
name: {
|
|
92
|
+
type: string;
|
|
93
|
+
minLength: number;
|
|
94
|
+
};
|
|
95
|
+
when: {
|
|
96
|
+
type: string;
|
|
97
|
+
minLength: number;
|
|
98
|
+
description: string;
|
|
99
|
+
};
|
|
100
|
+
instructions: {
|
|
101
|
+
type: string;
|
|
102
|
+
minLength: number;
|
|
103
|
+
description: string;
|
|
104
|
+
};
|
|
105
|
+
tools: {
|
|
106
|
+
type: string;
|
|
107
|
+
items: {
|
|
108
|
+
type: string;
|
|
109
|
+
minLength: number;
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
required: string[];
|
|
114
|
+
additionalProperties: boolean;
|
|
115
|
+
};
|
|
116
|
+
/** A proposed progress entry against the active goal. */
|
|
117
|
+
export declare const PROGRESS_PROPOSAL_SCHEMA: {
|
|
118
|
+
type: string;
|
|
119
|
+
properties: {
|
|
120
|
+
note: {
|
|
121
|
+
type: string;
|
|
122
|
+
minLength: number;
|
|
123
|
+
description: string;
|
|
124
|
+
};
|
|
125
|
+
evidence: {
|
|
126
|
+
type: string;
|
|
127
|
+
minLength: number;
|
|
128
|
+
description: string;
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
required: string[];
|
|
132
|
+
additionalProperties: boolean;
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Path-discriminated RFC 6902 operations. Provider decoding uses this same
|
|
136
|
+
* schema in non-strict mode; local validation remains authoritative.
|
|
137
|
+
* @param [options]
|
|
138
|
+
*/
|
|
139
|
+
export declare function refinementPatchSchema(options?: {
|
|
140
|
+
maxOps?: number;
|
|
141
|
+
}): any;
|
|
128
142
|
/** The full schema used for handwritten and generated proposals alike. */
|
|
129
|
-
export const REFINEMENT_PATCH_SCHEMA: any;
|
|
143
|
+
export declare const REFINEMENT_PATCH_SCHEMA: any;
|
package/src/schemas/patch.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//@ts-check
|
|
2
1
|
/**
|
|
3
2
|
* The refinement patch: a deliberately small subset of RFC 6902.
|
|
4
3
|
*
|
|
@@ -34,9 +33,7 @@
|
|
|
34
33
|
* could put a memory in front of one that came later — the recency
|
|
35
34
|
* ordering everything downstream depends on is not the model's to set.
|
|
36
35
|
*/
|
|
37
|
-
|
|
38
|
-
import { CLAIM_EVIDENCE_SCHEMA } from './evidence.js';
|
|
39
|
-
|
|
36
|
+
import { CLAIM_EVIDENCE_SCHEMA } from "./evidence.js";
|
|
40
37
|
/**
|
|
41
38
|
* Where a refinement may write. Anchored, and read as: append or address
|
|
42
39
|
* one memory, append or address one skill, append one progress entry.
|
|
@@ -46,12 +43,9 @@ import { CLAIM_EVIDENCE_SCHEMA } from './evidence.js';
|
|
|
46
43
|
* a memory is revised by replacing it whole, with fresh evidence, so
|
|
47
44
|
* that every stored record was validated as a whole exactly once.
|
|
48
45
|
*/
|
|
49
|
-
export const REFINEMENT_PATH_PATTERN =
|
|
50
|
-
'^(/memories/(-|[0-9]+)|/skills/(-|[0-9]+)|/goal/progress/-)$';
|
|
51
|
-
|
|
46
|
+
export const REFINEMENT_PATH_PATTERN = '^(/memories/(-|[0-9]+)|/skills/(-|[0-9]+)|/goal/progress/-)$';
|
|
52
47
|
/** The default cap on operations per refinement. */
|
|
53
48
|
export const DEFAULT_MAX_OPS = 6;
|
|
54
|
-
|
|
55
49
|
/**
|
|
56
50
|
* A proposed memory. `evidence` is required here AND by the ledger's own
|
|
57
51
|
* schema — the same requirement twice, on purpose: this copy is what
|
|
@@ -62,74 +56,75 @@ export const DEFAULT_MAX_OPS = 6;
|
|
|
62
56
|
* hallucination into durable state.
|
|
63
57
|
*/
|
|
64
58
|
export const MEMORY_PROPOSAL_SCHEMA = {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
59
|
+
type: 'object',
|
|
60
|
+
properties: {
|
|
61
|
+
text: { type: 'string', minLength: 1, description: 'The fact worth carrying, in one sentence.' },
|
|
62
|
+
evidence: { anyOf: [{ type: 'string', minLength: 1 }, CLAIM_EVIDENCE_SCHEMA] },
|
|
63
|
+
tags: { type: 'array', items: { type: 'string', minLength: 1 } },
|
|
64
|
+
},
|
|
65
|
+
required: ['text', 'evidence'],
|
|
66
|
+
additionalProperties: false,
|
|
73
67
|
};
|
|
74
|
-
|
|
75
68
|
/** A proposed skill: when it applies, what to do, which tools it needs. */
|
|
76
69
|
export const SKILL_PROPOSAL_SCHEMA = {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
70
|
+
type: 'object',
|
|
71
|
+
properties: {
|
|
72
|
+
name: { type: 'string', minLength: 1 },
|
|
73
|
+
when: { type: 'string', minLength: 1, description: 'The situation this recipe applies to.' },
|
|
74
|
+
instructions: { type: 'string', minLength: 1, description: 'The steps, concretely.' },
|
|
75
|
+
tools: { type: 'array', items: { type: 'string', minLength: 1 } },
|
|
76
|
+
},
|
|
77
|
+
required: ['name', 'when', 'instructions'],
|
|
78
|
+
additionalProperties: false,
|
|
86
79
|
};
|
|
87
|
-
|
|
88
80
|
/** A proposed progress entry against the active goal. */
|
|
89
81
|
export const PROGRESS_PROPOSAL_SCHEMA = {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
82
|
+
type: 'object',
|
|
83
|
+
properties: {
|
|
84
|
+
note: { type: 'string', minLength: 1, description: 'What was done or established.' },
|
|
85
|
+
evidence: { type: 'string', minLength: 1, description: 'What in the run establishes it.' },
|
|
86
|
+
},
|
|
87
|
+
required: ['note', 'evidence'],
|
|
88
|
+
additionalProperties: false,
|
|
97
89
|
};
|
|
98
|
-
|
|
99
90
|
/**
|
|
100
91
|
* Path-discriminated RFC 6902 operations. Provider decoding uses this same
|
|
101
92
|
* schema in non-strict mode; local validation remains authoritative.
|
|
102
|
-
* @param
|
|
103
|
-
* @returns {any}
|
|
93
|
+
* @param [options]
|
|
104
94
|
*/
|
|
105
95
|
export function refinementPatchSchema(options = {}) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
96
|
+
const maxOps = options.maxOps ?? DEFAULT_MAX_OPS;
|
|
97
|
+
if (!Number.isSafeInteger(maxOps) || maxOps < 0)
|
|
98
|
+
throw new TypeError('maxOps must be a non-negative safe integer');
|
|
99
|
+
const branch = (op, pattern, value) => ({
|
|
100
|
+
type: 'object',
|
|
101
|
+
properties: {
|
|
102
|
+
op: { enum: op }, path: { type: 'string', pattern },
|
|
103
|
+
...(value ? { value } : {})
|
|
104
|
+
},
|
|
105
|
+
required: value ? ['op', 'path', 'value'] : ['op', 'path'],
|
|
106
|
+
additionalProperties: false,
|
|
107
|
+
});
|
|
108
|
+
const index = '(0|[1-9][0-9]*)';
|
|
109
|
+
return {
|
|
110
|
+
$id: 'https://jarenjs.github.io/schemas/ai/refinement-patch.json',
|
|
111
|
+
title: 'Refinement patch',
|
|
112
|
+
type: 'array', maxItems: maxOps,
|
|
113
|
+
$defs: {
|
|
114
|
+
memory: MEMORY_PROPOSAL_SCHEMA, skill: SKILL_PROPOSAL_SCHEMA,
|
|
115
|
+
progress: PROGRESS_PROPOSAL_SCHEMA
|
|
116
|
+
},
|
|
117
|
+
items: {
|
|
118
|
+
oneOf: [
|
|
119
|
+
...[['memories', 'memory'], ['skills', 'skill']].flatMap(([path, kind]) => [
|
|
120
|
+
branch(['add'], `^/${path}/(-|${index})$`, { $ref: `#/$defs/${kind}` }),
|
|
121
|
+
branch(['replace'], `^/${path}/${index}$`, { $ref: `#/$defs/${kind}` }),
|
|
122
|
+
branch(['remove'], `^/${path}/${index}$`, null),
|
|
123
|
+
]),
|
|
124
|
+
branch(['add'], '^/goal/progress/-$', { $ref: '#/$defs/progress' }),
|
|
125
|
+
]
|
|
126
|
+
},
|
|
127
|
+
};
|
|
132
128
|
}
|
|
133
|
-
|
|
134
129
|
/** The full schema used for handwritten and generated proposals alike. */
|
|
135
130
|
export const REFINEMENT_PATCH_SCHEMA = refinementPatchSchema();
|
package/src/storage/memory.d.ts
CHANGED
|
@@ -70,17 +70,12 @@
|
|
|
70
70
|
* copies: a caller that mutates what it stored — or what it read —
|
|
71
71
|
* cannot reach inside the ledger.
|
|
72
72
|
*
|
|
73
|
-
* @param
|
|
74
|
-
* @returns {{ get: (key: string) => Promise<any>,
|
|
75
|
-
* set: (key: string, value: any) => Promise<void>,
|
|
76
|
-
* delete: (key: string) => Promise<void>,
|
|
77
|
-
* keys: (prefix?: string) => Promise<string[]>,
|
|
78
|
-
* mutate: import('./transaction.js').StorageMutation }}
|
|
73
|
+
* @param [backing] - an existing map to adopt
|
|
79
74
|
*/
|
|
80
|
-
export function createMemoryStorage(backing?: Map<string, string>): {
|
|
75
|
+
export declare function createMemoryStorage(backing?: Map<string, string>): {
|
|
81
76
|
get: (key: string) => Promise<any>;
|
|
82
77
|
set: (key: string, value: any) => Promise<void>;
|
|
83
78
|
delete: (key: string) => Promise<void>;
|
|
84
79
|
keys: (prefix?: string) => Promise<string[]>;
|
|
85
|
-
mutate: import(
|
|
80
|
+
mutate: import('./transaction.ts').StorageMutation;
|
|
86
81
|
};
|
package/src/storage/memory.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//@ts-check
|
|
2
1
|
/**
|
|
3
2
|
* The in-memory storage adapter: the ledger's default, and the reason
|
|
4
3
|
* `createLedger()` works with no arguments at all.
|
|
@@ -58,7 +57,6 @@
|
|
|
58
57
|
* `via: 'sweep'` — which is why the in-memory adapter below does not
|
|
59
58
|
* grow it.
|
|
60
59
|
*/
|
|
61
|
-
|
|
62
60
|
/**
|
|
63
61
|
* Create an in-memory adapter with JSON-value semantics: every value is
|
|
64
62
|
* serialized on the way in and parsed on the way out, so what survives
|
|
@@ -72,47 +70,47 @@
|
|
|
72
70
|
* copies: a caller that mutates what it stored — or what it read —
|
|
73
71
|
* cannot reach inside the ledger.
|
|
74
72
|
*
|
|
75
|
-
* @param
|
|
76
|
-
* @returns {{ get: (key: string) => Promise<any>,
|
|
77
|
-
* set: (key: string, value: any) => Promise<void>,
|
|
78
|
-
* delete: (key: string) => Promise<void>,
|
|
79
|
-
* keys: (prefix?: string) => Promise<string[]>,
|
|
80
|
-
* mutate: import('./transaction.js').StorageMutation }}
|
|
73
|
+
* @param [backing] - an existing map to adopt
|
|
81
74
|
*/
|
|
82
75
|
export function createMemoryStorage(backing = new Map()) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
76
|
+
return {
|
|
77
|
+
mutate: async (prefix, transform) => {
|
|
78
|
+
const matches = (key) => typeof prefix === 'string' ? key.startsWith(prefix)
|
|
79
|
+
: (prefix.keys ?? []).includes(key) || (prefix.prefixes ?? []).some((part) => key.startsWith(part));
|
|
80
|
+
// No await between read and publication: even separate adapters sharing
|
|
81
|
+
// this map observe one complete mutation. Serialize every value first.
|
|
82
|
+
const current = Object.fromEntries([...backing].filter(([key]) => matches(key))
|
|
83
|
+
.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)
|
|
84
|
+
.map(([key, raw]) => [key, JSON.parse(raw)]));
|
|
85
|
+
const outcome = transform(current);
|
|
86
|
+
if (!outcome || typeof outcome.then === 'function')
|
|
87
|
+
throw new TypeError('mutate callback must be synchronous');
|
|
88
|
+
if (outcome.next !== undefined) {
|
|
89
|
+
const entries = Object.entries(outcome.next).map(([key, value]) => {
|
|
90
|
+
if (!matches(key))
|
|
91
|
+
throw new TypeError('mutation escaped its namespace');
|
|
92
|
+
return [key, JSON.stringify(value)];
|
|
93
|
+
});
|
|
94
|
+
for (const key of backing.keys())
|
|
95
|
+
if (matches(key))
|
|
96
|
+
backing.delete(key);
|
|
97
|
+
for (const [key, raw] of entries)
|
|
98
|
+
backing.set(key, raw);
|
|
99
|
+
}
|
|
100
|
+
return outcome.result;
|
|
101
|
+
},
|
|
102
|
+
get: async (key) => {
|
|
103
|
+
const raw = backing.get(key);
|
|
104
|
+
return raw === undefined ? undefined : JSON.parse(raw);
|
|
105
|
+
},
|
|
106
|
+
set: async (key, value) => {
|
|
107
|
+
backing.set(key, JSON.stringify(value));
|
|
108
|
+
},
|
|
109
|
+
delete: async (key) => {
|
|
110
|
+
backing.delete(key);
|
|
111
|
+
},
|
|
112
|
+
keys: async (prefix = '') => [...backing.keys()]
|
|
113
|
+
.filter((key) => key.startsWith(prefix))
|
|
114
|
+
.sort(),
|
|
115
|
+
};
|
|
118
116
|
}
|
package/src/storage/slot.d.ts
CHANGED
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
* slot that swallows errors cannot promise durability; status says unverified.
|
|
4
4
|
* Web Locks serialize tabs using the same slot name. Without them the adapter
|
|
5
5
|
* explicitly exposes only the four-method, single-writer contract.
|
|
6
|
-
* @param
|
|
7
|
-
* @param {{ locks?: any, name?: string, singleWriter?: boolean }} [options]
|
|
6
|
+
* @param [options]
|
|
8
7
|
*/
|
|
9
|
-
export function createSlotLedgerStorage(slot: {
|
|
8
|
+
export declare function createSlotLedgerStorage(slot: {
|
|
10
9
|
read: () => any;
|
|
11
10
|
write: (data: any) => any;
|
|
12
11
|
key?: string;
|
|
@@ -26,5 +25,5 @@ export function createSlotLedgerStorage(slot: {
|
|
|
26
25
|
set: (key: any, value: any) => Promise<any>;
|
|
27
26
|
delete: (key: any) => Promise<any>;
|
|
28
27
|
keys: (prefix?: string) => Promise<string[]>;
|
|
29
|
-
mutate?: (prefix: any, transform: any) => Promise<any
|
|
28
|
+
mutate?: ((prefix: any, transform: any) => Promise<any>) | undefined;
|
|
30
29
|
};
|