@solidnumber/cli 1.20.1 → 1.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/brand.js +11 -2
- package/dist/commands/brand.js.map +1 -1
- package/dist/commands/domains.js +16 -2
- package/dist/commands/domains.js.map +1 -1
- package/dist/commands/graph.d.ts.map +1 -1
- package/dist/commands/graph.js +179 -0
- package/dist/commands/graph.js.map +1 -1
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +91 -0
- package/dist/commands/push.js.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/api-client.d.ts +5 -0
- package/dist/lib/api-client.d.ts.map +1 -1
- package/dist/lib/api-client.js +75 -0
- package/dist/lib/api-client.js.map +1 -1
- package/dist/lib/graph-diff.d.ts +66 -0
- package/dist/lib/graph-diff.d.ts.map +1 -0
- package/dist/lib/graph-diff.js +181 -0
- package/dist/lib/graph-diff.js.map +1 -0
- package/dist/lib/offline-queue.d.ts +49 -0
- package/dist/lib/offline-queue.d.ts.map +1 -0
- package/dist/lib/offline-queue.js +270 -0
- package/dist/lib/offline-queue.js.map +1 -0
- package/dist/lib/sparql-bgp.d.ts +88 -0
- package/dist/lib/sparql-bgp.d.ts.map +1 -0
- package/dist/lib/sparql-bgp.js +386 -0
- package/dist/lib/sparql-bgp.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* graph-diff — compare two JSON-LD documents at the graph level.
|
|
3
|
+
*
|
|
4
|
+
* Sprint: SPRINT-JSONLD-GRAPH-MOAT.md § A+.4
|
|
5
|
+
*
|
|
6
|
+
* Input: two JsonLdDocument values (typically the output of
|
|
7
|
+
* `solid context --jsonld` taken at different points in time).
|
|
8
|
+
*
|
|
9
|
+
* Output: a report listing added, removed, and modified nodes.
|
|
10
|
+
* Modified nodes carry per-predicate operations in JSON-Patch style
|
|
11
|
+
* so consumers can apply or revert any subset.
|
|
12
|
+
*
|
|
13
|
+
* Design notes
|
|
14
|
+
* ------------
|
|
15
|
+
* - Identity is by `@id`. A node with no `@id` is skipped (we can't
|
|
16
|
+
* diff anonymous nodes meaningfully — they'd produce a phantom
|
|
17
|
+
* add+remove for every reshape).
|
|
18
|
+
* - `@type` order is normalized for comparison (sorted) so a node that
|
|
19
|
+
* reorders its types isn't flagged as modified.
|
|
20
|
+
* - Edge changes appear as predicate changes on the source node — we
|
|
21
|
+
* don't emit a separate "edge diff" because every edge in JSON-LD is
|
|
22
|
+
* a predicate value pointing at a target IRI.
|
|
23
|
+
* - Multi-tenant isolation: the caller is responsible for ensuring
|
|
24
|
+
* both documents come from the same tenant. The diff function does
|
|
25
|
+
* not enforce this; it does, however, guarantee that no node from
|
|
26
|
+
* one document leaks into the other's report — every entry comes
|
|
27
|
+
* from one of `before` or `after`, never invented.
|
|
28
|
+
*/
|
|
29
|
+
import type { JsonLdDocument, JsonLdNode } from './graph-walker';
|
|
30
|
+
export type Op = 'add' | 'remove' | 'replace';
|
|
31
|
+
export interface PredicateChange {
|
|
32
|
+
predicate: string;
|
|
33
|
+
op: Op;
|
|
34
|
+
before?: unknown;
|
|
35
|
+
after?: unknown;
|
|
36
|
+
}
|
|
37
|
+
export interface NodeChange {
|
|
38
|
+
'@id': string;
|
|
39
|
+
'@type'?: string | string[];
|
|
40
|
+
predicates: PredicateChange[];
|
|
41
|
+
}
|
|
42
|
+
export interface DiffReport {
|
|
43
|
+
/** Nodes whose `@id` is in `after` but not `before`. */
|
|
44
|
+
added: JsonLdNode[];
|
|
45
|
+
/** Nodes whose `@id` is in `before` but not `after`. */
|
|
46
|
+
removed: JsonLdNode[];
|
|
47
|
+
/** Nodes whose `@id` is in both, but predicates differ. */
|
|
48
|
+
modified: NodeChange[];
|
|
49
|
+
/** Aggregate counts so consumers can decide "is anything different?"
|
|
50
|
+
* without iterating every list. */
|
|
51
|
+
summary: {
|
|
52
|
+
added: number;
|
|
53
|
+
removed: number;
|
|
54
|
+
modified: number;
|
|
55
|
+
unchanged: number;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Compare two JSON-LD documents and return a structured report.
|
|
60
|
+
*
|
|
61
|
+
* Both documents should have a `@graph` array of nodes (the standard
|
|
62
|
+
* shape `services/ai_context_jsonld.build_jsonld()` emits). Documents
|
|
63
|
+
* without a graph array produce an all-empty report.
|
|
64
|
+
*/
|
|
65
|
+
export declare function diff(before: JsonLdDocument, after: JsonLdDocument): DiffReport;
|
|
66
|
+
//# sourceMappingURL=graph-diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-diff.d.ts","sourceRoot":"","sources":["../../src/lib/graph-diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjE,MAAM,MAAM,EAAE,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE9C,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,EAAE,CAAC;IACP,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B,UAAU,EAAE,eAAe,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,UAAU;IACzB,wDAAwD;IACxD,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,wDAAwD;IACxD,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,2DAA2D;IAC3D,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB;wCACoC;IACpC,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAkGD;;;;;;GAMG;AACH,wBAAgB,IAAI,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,cAAc,GAAG,UAAU,CAkD9E"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* graph-diff — compare two JSON-LD documents at the graph level.
|
|
4
|
+
*
|
|
5
|
+
* Sprint: SPRINT-JSONLD-GRAPH-MOAT.md § A+.4
|
|
6
|
+
*
|
|
7
|
+
* Input: two JsonLdDocument values (typically the output of
|
|
8
|
+
* `solid context --jsonld` taken at different points in time).
|
|
9
|
+
*
|
|
10
|
+
* Output: a report listing added, removed, and modified nodes.
|
|
11
|
+
* Modified nodes carry per-predicate operations in JSON-Patch style
|
|
12
|
+
* so consumers can apply or revert any subset.
|
|
13
|
+
*
|
|
14
|
+
* Design notes
|
|
15
|
+
* ------------
|
|
16
|
+
* - Identity is by `@id`. A node with no `@id` is skipped (we can't
|
|
17
|
+
* diff anonymous nodes meaningfully — they'd produce a phantom
|
|
18
|
+
* add+remove for every reshape).
|
|
19
|
+
* - `@type` order is normalized for comparison (sorted) so a node that
|
|
20
|
+
* reorders its types isn't flagged as modified.
|
|
21
|
+
* - Edge changes appear as predicate changes on the source node — we
|
|
22
|
+
* don't emit a separate "edge diff" because every edge in JSON-LD is
|
|
23
|
+
* a predicate value pointing at a target IRI.
|
|
24
|
+
* - Multi-tenant isolation: the caller is responsible for ensuring
|
|
25
|
+
* both documents come from the same tenant. The diff function does
|
|
26
|
+
* not enforce this; it does, however, guarantee that no node from
|
|
27
|
+
* one document leaks into the other's report — every entry comes
|
|
28
|
+
* from one of `before` or `after`, never invented.
|
|
29
|
+
*/
|
|
30
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
+
exports.diff = diff;
|
|
32
|
+
const RESERVED_KEYS = new Set(['@id', '@context', '@type']);
|
|
33
|
+
function nodesById(doc) {
|
|
34
|
+
const out = new Map();
|
|
35
|
+
const list = doc.graph ?? [];
|
|
36
|
+
for (const node of list) {
|
|
37
|
+
if (node && typeof node === 'object' && typeof node['@id'] === 'string') {
|
|
38
|
+
out.set(node['@id'], node);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
function normalizeType(t) {
|
|
44
|
+
if (Array.isArray(t))
|
|
45
|
+
return [...t.map(String)].sort();
|
|
46
|
+
if (typeof t === 'string')
|
|
47
|
+
return [t];
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
function predicateKeys(node) {
|
|
51
|
+
return Object.keys(node).filter((k) => !RESERVED_KEYS.has(k));
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Deep equality for JSON-LD predicate values. We don't need full
|
|
55
|
+
* structural equality (functions, Symbols, NaN) — JSON-LD payloads are
|
|
56
|
+
* pure JSON values, so JSON serialization with sorted keys is the
|
|
57
|
+
* correct equality check.
|
|
58
|
+
*/
|
|
59
|
+
function predicateEqual(a, b) {
|
|
60
|
+
if (a === b)
|
|
61
|
+
return true;
|
|
62
|
+
if (typeof a !== typeof b)
|
|
63
|
+
return false;
|
|
64
|
+
if (a == null || b == null)
|
|
65
|
+
return a === b;
|
|
66
|
+
// Order-insensitive comparison via canonical JSON.
|
|
67
|
+
return canonical(a) === canonical(b);
|
|
68
|
+
}
|
|
69
|
+
function canonical(v) {
|
|
70
|
+
if (v == null)
|
|
71
|
+
return JSON.stringify(v);
|
|
72
|
+
if (typeof v !== 'object')
|
|
73
|
+
return JSON.stringify(v);
|
|
74
|
+
if (Array.isArray(v)) {
|
|
75
|
+
return `[${v.map(canonical).join(',')}]`;
|
|
76
|
+
}
|
|
77
|
+
const obj = v;
|
|
78
|
+
const keys = Object.keys(obj).sort();
|
|
79
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${canonical(obj[k])}`).join(',')}}`;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Compute per-predicate operations between two snapshots of the same
|
|
83
|
+
* @id. Returns an empty array when nothing changed (the caller treats
|
|
84
|
+
* that as "unchanged").
|
|
85
|
+
*/
|
|
86
|
+
function predicateDiff(before, after) {
|
|
87
|
+
const changes = [];
|
|
88
|
+
// @type changes: normalize so reordering doesn't trigger a modify.
|
|
89
|
+
const tBefore = normalizeType(before['@type']);
|
|
90
|
+
const tAfter = normalizeType(after['@type']);
|
|
91
|
+
if (canonical(tBefore) !== canonical(tAfter)) {
|
|
92
|
+
changes.push({
|
|
93
|
+
predicate: '@type',
|
|
94
|
+
op: 'replace',
|
|
95
|
+
before: before['@type'],
|
|
96
|
+
after: after['@type'],
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
const beforeKeys = new Set(predicateKeys(before));
|
|
100
|
+
const afterKeys = new Set(predicateKeys(after));
|
|
101
|
+
// Predicates added in `after`
|
|
102
|
+
for (const k of afterKeys) {
|
|
103
|
+
if (!beforeKeys.has(k)) {
|
|
104
|
+
changes.push({ predicate: k, op: 'add', after: after[k] });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Predicates removed in `after`
|
|
108
|
+
for (const k of beforeKeys) {
|
|
109
|
+
if (!afterKeys.has(k)) {
|
|
110
|
+
changes.push({ predicate: k, op: 'remove', before: before[k] });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Predicates present in both — replace if value changed.
|
|
114
|
+
for (const k of beforeKeys) {
|
|
115
|
+
if (!afterKeys.has(k))
|
|
116
|
+
continue;
|
|
117
|
+
if (!predicateEqual(before[k], after[k])) {
|
|
118
|
+
changes.push({ predicate: k, op: 'replace', before: before[k], after: after[k] });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Stable predicate ordering for deterministic output (tests, snapshots).
|
|
122
|
+
changes.sort((a, b) => a.predicate.localeCompare(b.predicate));
|
|
123
|
+
return changes;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Compare two JSON-LD documents and return a structured report.
|
|
127
|
+
*
|
|
128
|
+
* Both documents should have a `@graph` array of nodes (the standard
|
|
129
|
+
* shape `services/ai_context_jsonld.build_jsonld()` emits). Documents
|
|
130
|
+
* without a graph array produce an all-empty report.
|
|
131
|
+
*/
|
|
132
|
+
function diff(before, after) {
|
|
133
|
+
const beforeMap = nodesById(before);
|
|
134
|
+
const afterMap = nodesById(after);
|
|
135
|
+
const added = [];
|
|
136
|
+
const removed = [];
|
|
137
|
+
const modified = [];
|
|
138
|
+
let unchanged = 0;
|
|
139
|
+
for (const [id, node] of afterMap) {
|
|
140
|
+
if (!beforeMap.has(id)) {
|
|
141
|
+
added.push(node);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
for (const [id, node] of beforeMap) {
|
|
145
|
+
if (!afterMap.has(id)) {
|
|
146
|
+
removed.push(node);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
for (const [id, beforeNode] of beforeMap) {
|
|
150
|
+
const afterNode = afterMap.get(id);
|
|
151
|
+
if (!afterNode)
|
|
152
|
+
continue;
|
|
153
|
+
const changes = predicateDiff(beforeNode, afterNode);
|
|
154
|
+
if (changes.length === 0) {
|
|
155
|
+
unchanged++;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
modified.push({
|
|
159
|
+
'@id': id,
|
|
160
|
+
'@type': afterNode['@type'],
|
|
161
|
+
predicates: changes,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Stable @id ordering for deterministic output.
|
|
166
|
+
added.sort((a, b) => a['@id'].localeCompare(b['@id']));
|
|
167
|
+
removed.sort((a, b) => a['@id'].localeCompare(b['@id']));
|
|
168
|
+
modified.sort((a, b) => a['@id'].localeCompare(b['@id']));
|
|
169
|
+
return {
|
|
170
|
+
added,
|
|
171
|
+
removed,
|
|
172
|
+
modified,
|
|
173
|
+
summary: {
|
|
174
|
+
added: added.length,
|
|
175
|
+
removed: removed.length,
|
|
176
|
+
modified: modified.length,
|
|
177
|
+
unchanged,
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=graph-diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-diff.js","sourceRoot":"","sources":["../../src/lib/graph-diff.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export declare function setQueueMode(on: boolean): void;
|
|
2
|
+
export declare function isQueueMode(): boolean;
|
|
3
|
+
export declare function activateQueueModeIfRequested(argv: string[]): void;
|
|
4
|
+
export interface QueuedMutation {
|
|
5
|
+
'@context': Record<string, unknown>;
|
|
6
|
+
'@id': string;
|
|
7
|
+
'@type': string[];
|
|
8
|
+
method: string;
|
|
9
|
+
url: string;
|
|
10
|
+
body?: unknown;
|
|
11
|
+
idempotencyKey: string;
|
|
12
|
+
queuedAt: string;
|
|
13
|
+
cliVersion?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function queueDir(cwd?: string): string;
|
|
16
|
+
export interface QueueWriteResult {
|
|
17
|
+
/** Absolute path to the queue file. */
|
|
18
|
+
path: string;
|
|
19
|
+
/** The serialized queued mutation. */
|
|
20
|
+
mutation: QueuedMutation;
|
|
21
|
+
}
|
|
22
|
+
export declare function enqueue(method: string, url: string, body: unknown, opts?: {
|
|
23
|
+
cwd?: string;
|
|
24
|
+
cliVersion?: string;
|
|
25
|
+
}): QueueWriteResult;
|
|
26
|
+
/**
|
|
27
|
+
* Return every queued mutation, sorted chronologically by `queuedAt`.
|
|
28
|
+
* Files that fail to parse are skipped with a warning to stderr (we
|
|
29
|
+
* never fail-fast on the queue; one bad file shouldn't block the
|
|
30
|
+
* rest from replaying).
|
|
31
|
+
*/
|
|
32
|
+
export declare function readQueue(cwd?: string): Array<{
|
|
33
|
+
path: string;
|
|
34
|
+
mutation: QueuedMutation;
|
|
35
|
+
}>;
|
|
36
|
+
/** Remove a queued mutation file from disk (called after successful replay). */
|
|
37
|
+
export declare function deleteQueued(filepath: string): void;
|
|
38
|
+
/** Total queue size — surfaced by `solid push --flush --dry-run` etc. */
|
|
39
|
+
export declare function queueSize(cwd?: string): number;
|
|
40
|
+
export declare function isAutoFlushInProgress(): boolean;
|
|
41
|
+
export interface AutoFlushStats {
|
|
42
|
+
succeeded: number;
|
|
43
|
+
failed: number;
|
|
44
|
+
total: number;
|
|
45
|
+
}
|
|
46
|
+
/** Replay every queued mutation using the supplied dispatcher.
|
|
47
|
+
* The dispatcher is injected so this module stays free of axios. */
|
|
48
|
+
export declare function autoFlushQueue(dispatch: (method: string, url: string, body: unknown) => Promise<void>, cwd?: string): Promise<AutoFlushStats>;
|
|
49
|
+
//# sourceMappingURL=offline-queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offline-queue.d.ts","sourceRoot":"","sources":["../../src/lib/offline-queue.ts"],"names":[],"mappings":"AAiDA,wBAAgB,YAAY,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAE9C;AAED,wBAAgB,WAAW,IAAI,OAAO,CAKrC;AAED,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAUjE;AAiBD,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAID,wBAAgB,QAAQ,CAAC,GAAG,GAAE,MAAsB,GAAG,MAAM,CAE5D;AAmBD,MAAM,WAAW,gBAAgB;IAC/B,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,sCAAsC;IACtC,QAAQ,EAAE,cAAc,CAAC;CAC1B;AAED,wBAAgB,OAAO,CACrB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,OAAO,EACb,IAAI,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/C,gBAAgB,CAyClB;AAOD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,GAAG,GAAE,MAAsB,GAAG,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,cAAc,CAAA;CAAE,CAAC,CAsBxG;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAMnD;AAED,yEAAyE;AACzE,wBAAgB,SAAS,CAAC,GAAG,GAAE,MAAsB,GAAG,MAAM,CAI7D;AAoBD,wBAAgB,qBAAqB,IAAI,OAAO,CAE/C;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED;qEACqE;AACrE,wBAAsB,cAAc,CAClC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,EACvE,GAAG,GAAE,MAAsB,GAC1B,OAAO,CAAC,cAAc,CAAC,CAuBzB"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.setQueueMode = setQueueMode;
|
|
40
|
+
exports.isQueueMode = isQueueMode;
|
|
41
|
+
exports.activateQueueModeIfRequested = activateQueueModeIfRequested;
|
|
42
|
+
exports.queueDir = queueDir;
|
|
43
|
+
exports.enqueue = enqueue;
|
|
44
|
+
exports.readQueue = readQueue;
|
|
45
|
+
exports.deleteQueued = deleteQueued;
|
|
46
|
+
exports.queueSize = queueSize;
|
|
47
|
+
exports.isAutoFlushInProgress = isAutoFlushInProgress;
|
|
48
|
+
exports.autoFlushQueue = autoFlushQueue;
|
|
49
|
+
/**
|
|
50
|
+
* Offline mutation queue (A+.6).
|
|
51
|
+
*
|
|
52
|
+
* Closes the offline symmetry: read side is fully offline via
|
|
53
|
+
* `.claude/solid-context.jsonld`, write side now also works without
|
|
54
|
+
* a live backend. Field agents, travel, air-gapped demos.
|
|
55
|
+
*
|
|
56
|
+
* Activation (any of, checked in order):
|
|
57
|
+
* 1. CLI flag: --queue (set at program level via setQueueMode)
|
|
58
|
+
* 2. Env var: SOLID_OFFLINE_QUEUE=1
|
|
59
|
+
* 3. Runtime: setQueueMode(true)
|
|
60
|
+
*
|
|
61
|
+
* Behavior when on:
|
|
62
|
+
* - GET requests pass through unchanged (read side never queues)
|
|
63
|
+
* - Mutations (POST/PUT/PATCH/DELETE) are written to
|
|
64
|
+
* `.solid/queue/<utc>-<uuid>.jsonld` and the call returns a
|
|
65
|
+
* synthetic success carrying the queue file path.
|
|
66
|
+
*
|
|
67
|
+
* Replaying:
|
|
68
|
+
* - `solid push --flush` reads every file under `.solid/queue/`,
|
|
69
|
+
* replays each mutation in chronological order with its
|
|
70
|
+
* idempotency key, deletes on success, leaves on failure.
|
|
71
|
+
*
|
|
72
|
+
* File format (one mutation per file):
|
|
73
|
+
*
|
|
74
|
+
* {
|
|
75
|
+
* "@context": { ... ai-context vocab ... },
|
|
76
|
+
* "@id": "urn:solid:queued:<uuid>",
|
|
77
|
+
* "@type": ["solid:QueuedMutation"],
|
|
78
|
+
* "method": "POST",
|
|
79
|
+
* "url": "/api/v1/kb",
|
|
80
|
+
* "body": { ... },
|
|
81
|
+
* "idempotencyKey": "<uuid>",
|
|
82
|
+
* "queuedAt": "2026-04-25T12:34:56.789Z",
|
|
83
|
+
* "cliVersion": "1.23.0"
|
|
84
|
+
* }
|
|
85
|
+
*
|
|
86
|
+
* The file IS a JSON-LD document so `solid graph --offline` over the
|
|
87
|
+
* queue directory could walk what's pending — useful for the eventual
|
|
88
|
+
* conflict-resolution UX in v1.1.
|
|
89
|
+
*/
|
|
90
|
+
const fs = __importStar(require("fs"));
|
|
91
|
+
const path = __importStar(require("path"));
|
|
92
|
+
const crypto = __importStar(require("crypto"));
|
|
93
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
94
|
+
let enabled = false;
|
|
95
|
+
let bannerShown = false;
|
|
96
|
+
function setQueueMode(on) {
|
|
97
|
+
enabled = Boolean(on);
|
|
98
|
+
}
|
|
99
|
+
function isQueueMode() {
|
|
100
|
+
if (process.env.SOLID_OFFLINE_QUEUE && /^(1|true|yes|on)$/i.test(process.env.SOLID_OFFLINE_QUEUE)) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
return enabled;
|
|
104
|
+
}
|
|
105
|
+
function activateQueueModeIfRequested(argv) {
|
|
106
|
+
// The flag is `--queue` (singular). `--offline` already means
|
|
107
|
+
// something different on `solid graph` (read from local file). We
|
|
108
|
+
// keep the namespaces separate so a user passing `--offline` to
|
|
109
|
+
// graph doesn't accidentally arm queue mode.
|
|
110
|
+
const hasFlag = argv.includes('--queue');
|
|
111
|
+
if (hasFlag || isQueueMode()) {
|
|
112
|
+
setQueueMode(true);
|
|
113
|
+
showBanner();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function showBanner() {
|
|
117
|
+
if (bannerShown)
|
|
118
|
+
return;
|
|
119
|
+
bannerShown = true;
|
|
120
|
+
process.stderr.write(chalk_1.default.cyan.bold('\n📥 [QUEUE]') +
|
|
121
|
+
chalk_1.default.cyan(' Offline mode — mutations are written to .solid/queue/ instead of sent.\n') +
|
|
122
|
+
chalk_1.default.dim(' Replay later with: solid push --flush\n\n'));
|
|
123
|
+
}
|
|
124
|
+
const QUEUE_DIR_NAME = '.solid/queue';
|
|
125
|
+
function queueDir(cwd = process.cwd()) {
|
|
126
|
+
return path.join(cwd, QUEUE_DIR_NAME);
|
|
127
|
+
}
|
|
128
|
+
function ensureQueueDir(cwd = process.cwd()) {
|
|
129
|
+
const dir = queueDir(cwd);
|
|
130
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
131
|
+
return dir;
|
|
132
|
+
}
|
|
133
|
+
function makeUuid() {
|
|
134
|
+
// randomUUID is on Node 14.17+. We require Node >= 20 (engines.node)
|
|
135
|
+
// so this is always available.
|
|
136
|
+
return crypto.randomUUID();
|
|
137
|
+
}
|
|
138
|
+
function enqueue(method, url, body, opts = {}) {
|
|
139
|
+
const dir = ensureQueueDir(opts.cwd);
|
|
140
|
+
const idempotencyKey = makeUuid();
|
|
141
|
+
const queuedAt = new Date().toISOString();
|
|
142
|
+
const id = `urn:solid:queued:${idempotencyKey}`;
|
|
143
|
+
// Lazy-import the vocab so this module stays usable in environments
|
|
144
|
+
// where the JSON-LD context isn't loaded (e.g., direct unit tests).
|
|
145
|
+
// The @context is informational on the wire; replay reads only the
|
|
146
|
+
// method/url/body/idempotencyKey fields.
|
|
147
|
+
const ctx = {
|
|
148
|
+
'@version': 1.1,
|
|
149
|
+
schema: 'http://schema.org/',
|
|
150
|
+
solid: 'https://solidnumber.com/vocab#',
|
|
151
|
+
};
|
|
152
|
+
const mutation = {
|
|
153
|
+
'@context': ctx,
|
|
154
|
+
'@id': id,
|
|
155
|
+
'@type': ['solid:QueuedMutation'],
|
|
156
|
+
method: method.toUpperCase(),
|
|
157
|
+
url,
|
|
158
|
+
body,
|
|
159
|
+
idempotencyKey,
|
|
160
|
+
queuedAt,
|
|
161
|
+
cliVersion: opts.cliVersion,
|
|
162
|
+
};
|
|
163
|
+
// Filename: <utc>-<uuid>.jsonld so directory listing is naturally
|
|
164
|
+
// chronological. UTC compact form (no `:` for filesystem safety).
|
|
165
|
+
const utc = queuedAt.replace(/[:.]/g, '-');
|
|
166
|
+
const filename = `${utc}-${idempotencyKey}.jsonld`;
|
|
167
|
+
const filepath = path.join(dir, filename);
|
|
168
|
+
fs.writeFileSync(filepath, JSON.stringify(mutation, null, 2));
|
|
169
|
+
process.stderr.write(`${chalk_1.default.cyan('[QUEUE]')} ${chalk_1.default.cyan(method.toUpperCase().padEnd(6))} ${url} ${chalk_1.default.dim('→')} ${chalk_1.default.dim(path.relative(opts.cwd ?? process.cwd(), filepath))}\n`);
|
|
170
|
+
return { path: filepath, mutation };
|
|
171
|
+
}
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
// Read — for `solid push --flush`.
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
/**
|
|
176
|
+
* Return every queued mutation, sorted chronologically by `queuedAt`.
|
|
177
|
+
* Files that fail to parse are skipped with a warning to stderr (we
|
|
178
|
+
* never fail-fast on the queue; one bad file shouldn't block the
|
|
179
|
+
* rest from replaying).
|
|
180
|
+
*/
|
|
181
|
+
function readQueue(cwd = process.cwd()) {
|
|
182
|
+
const dir = queueDir(cwd);
|
|
183
|
+
if (!fs.existsSync(dir))
|
|
184
|
+
return [];
|
|
185
|
+
const entries = fs.readdirSync(dir).filter((f) => f.endsWith('.jsonld'));
|
|
186
|
+
const out = [];
|
|
187
|
+
for (const f of entries) {
|
|
188
|
+
const p = path.join(dir, f);
|
|
189
|
+
try {
|
|
190
|
+
const raw = fs.readFileSync(p, 'utf-8');
|
|
191
|
+
const parsed = JSON.parse(raw);
|
|
192
|
+
if (parsed && typeof parsed === 'object' && parsed.method && parsed.url) {
|
|
193
|
+
out.push({ path: p, mutation: parsed });
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
process.stderr.write(chalk_1.default.yellow(`[QUEUE] skipping malformed file: ${f}\n`));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
process.stderr.write(chalk_1.default.yellow(`[QUEUE] skipping unreadable file: ${f}: ${err.message}\n`));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
out.sort((a, b) => a.mutation.queuedAt.localeCompare(b.mutation.queuedAt));
|
|
204
|
+
return out;
|
|
205
|
+
}
|
|
206
|
+
/** Remove a queued mutation file from disk (called after successful replay). */
|
|
207
|
+
function deleteQueued(filepath) {
|
|
208
|
+
try {
|
|
209
|
+
fs.unlinkSync(filepath);
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
// Already gone — fine.
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/** Total queue size — surfaced by `solid push --flush --dry-run` etc. */
|
|
216
|
+
function queueSize(cwd = process.cwd()) {
|
|
217
|
+
const dir = queueDir(cwd);
|
|
218
|
+
if (!fs.existsSync(dir))
|
|
219
|
+
return 0;
|
|
220
|
+
return fs.readdirSync(dir).filter((f) => f.endsWith('.jsonld')).length;
|
|
221
|
+
}
|
|
222
|
+
// ---------------------------------------------------------------------------
|
|
223
|
+
// A+.6b — Auto-flush after a successful mutation.
|
|
224
|
+
//
|
|
225
|
+
// When the CLI proves connectivity (a mutation just succeeded), drain
|
|
226
|
+
// any pending offline queue silently. This is the "holy shit" moment:
|
|
227
|
+
// user lost wifi mid-flight, kept working, regains wifi, the next
|
|
228
|
+
// successful mutation triggers everything they queued to land in
|
|
229
|
+
// chronological order without them lifting a finger.
|
|
230
|
+
//
|
|
231
|
+
// Best-effort: a failure on one queued mutation leaves it in the queue
|
|
232
|
+
// (next attempt picks it up), but every other queued mutation still
|
|
233
|
+
// gets a chance. We use a guard flag so the auto-flush itself doesn't
|
|
234
|
+
// recurse — when each replay fires through the response interceptor,
|
|
235
|
+
// the guard suppresses inner auto-flush attempts.
|
|
236
|
+
// ---------------------------------------------------------------------------
|
|
237
|
+
let _autoFlushInFlight = false;
|
|
238
|
+
function isAutoFlushInProgress() {
|
|
239
|
+
return _autoFlushInFlight;
|
|
240
|
+
}
|
|
241
|
+
/** Replay every queued mutation using the supplied dispatcher.
|
|
242
|
+
* The dispatcher is injected so this module stays free of axios. */
|
|
243
|
+
async function autoFlushQueue(dispatch, cwd = process.cwd()) {
|
|
244
|
+
if (_autoFlushInFlight)
|
|
245
|
+
return { succeeded: 0, failed: 0, total: 0 };
|
|
246
|
+
const items = readQueue(cwd);
|
|
247
|
+
if (items.length === 0)
|
|
248
|
+
return { succeeded: 0, failed: 0, total: 0 };
|
|
249
|
+
_autoFlushInFlight = true;
|
|
250
|
+
let succeeded = 0;
|
|
251
|
+
let failed = 0;
|
|
252
|
+
try {
|
|
253
|
+
for (const { path: filepath, mutation } of items) {
|
|
254
|
+
try {
|
|
255
|
+
await dispatch(mutation.method, mutation.url, mutation.body);
|
|
256
|
+
deleteQueued(filepath);
|
|
257
|
+
succeeded++;
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
// Leave file in queue for the next attempt.
|
|
261
|
+
failed++;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
finally {
|
|
266
|
+
_autoFlushInFlight = false;
|
|
267
|
+
}
|
|
268
|
+
return { succeeded, failed, total: items.length };
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=offline-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offline-queue.js","sourceRoot":"","sources":["../../src/lib/offline-queue.ts"],"names":[],"mappings":"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"}
|