@blu3ph4ntom/inval 0.1.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/CHANGELOG.md +21 -0
- package/LICENSE +21 -0
- package/README.md +431 -0
- package/dist/batch.d.ts +5 -0
- package/dist/batch.d.ts.map +1 -0
- package/dist/debug.d.ts +10 -0
- package/dist/debug.d.ts.map +1 -0
- package/dist/graph.d.ts +11 -0
- package/dist/graph.d.ts.map +1 -0
- package/dist/index.cjs +380 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +342 -0
- package/dist/inval.iife.js +367 -0
- package/dist/node.d.ts +4 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +64 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
InvalCycleError: () => InvalCycleError,
|
|
24
|
+
ancestors: () => ancestors,
|
|
25
|
+
batch: () => batch,
|
|
26
|
+
checkForCycles: () => checkForCycles,
|
|
27
|
+
descendants: () => descendants,
|
|
28
|
+
graphSize: () => graphSize,
|
|
29
|
+
input: () => input,
|
|
30
|
+
node: () => node,
|
|
31
|
+
resetIdCounter: () => resetIdCounter,
|
|
32
|
+
stats: () => stats,
|
|
33
|
+
toDot: () => toDot,
|
|
34
|
+
why: () => why
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
37
|
+
|
|
38
|
+
// src/types.ts
|
|
39
|
+
var nextId = 0;
|
|
40
|
+
function generateId() {
|
|
41
|
+
return `n${nextId++}`;
|
|
42
|
+
}
|
|
43
|
+
function resetIdCounter() {
|
|
44
|
+
nextId = 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/graph.ts
|
|
48
|
+
var InvalCycleError = class extends Error {
|
|
49
|
+
constructor(path) {
|
|
50
|
+
super(`Cycle detected: ${path.join(" \u2192 ")}`);
|
|
51
|
+
this.name = "InvalCycleError";
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
function checkForCycles(roots) {
|
|
55
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
56
|
+
const visited = /* @__PURE__ */ new Set();
|
|
57
|
+
function dfs(node2, path) {
|
|
58
|
+
if (visited.has(node2)) return;
|
|
59
|
+
if (visiting.has(node2)) {
|
|
60
|
+
throw new InvalCycleError([...path, node2.id]);
|
|
61
|
+
}
|
|
62
|
+
visiting.add(node2);
|
|
63
|
+
path.push(node2.id);
|
|
64
|
+
if (node2.kind === "computed") {
|
|
65
|
+
for (const parent of node2._parents) {
|
|
66
|
+
dfs(parent, path);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
path.pop();
|
|
70
|
+
visiting.delete(node2);
|
|
71
|
+
visited.add(node2);
|
|
72
|
+
}
|
|
73
|
+
for (const root of roots) {
|
|
74
|
+
dfs(root, []);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function markDirty(source) {
|
|
78
|
+
const visited = /* @__PURE__ */ new Set();
|
|
79
|
+
const queue = [];
|
|
80
|
+
for (const child of source._children) {
|
|
81
|
+
if (!visited.has(child)) {
|
|
82
|
+
visited.add(child);
|
|
83
|
+
queue.push(child);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
let i = 0;
|
|
87
|
+
while (i < queue.length) {
|
|
88
|
+
const node2 = queue[i++];
|
|
89
|
+
for (const child of node2._children) {
|
|
90
|
+
if (!visited.has(child)) {
|
|
91
|
+
visited.add(child);
|
|
92
|
+
queue.push(child);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
for (const child of queue) {
|
|
97
|
+
child._dirty = true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function why(target) {
|
|
101
|
+
if (target.kind === "computed" && !target._dirty) return [];
|
|
102
|
+
const path = [];
|
|
103
|
+
const visited = /* @__PURE__ */ new Set();
|
|
104
|
+
function trace(node2) {
|
|
105
|
+
if (visited.has(node2)) return;
|
|
106
|
+
visited.add(node2);
|
|
107
|
+
path.push(node2.id);
|
|
108
|
+
if (node2.kind === "computed") {
|
|
109
|
+
for (const parent of node2._parents) {
|
|
110
|
+
if (parent.kind === "computed" && parent._dirty) {
|
|
111
|
+
trace(parent);
|
|
112
|
+
} else if (parent.kind === "input" && !visited.has(parent)) {
|
|
113
|
+
visited.add(parent);
|
|
114
|
+
path.push(parent.id);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
trace(target);
|
|
120
|
+
return path;
|
|
121
|
+
}
|
|
122
|
+
function ancestors(node2) {
|
|
123
|
+
const result = [];
|
|
124
|
+
const visited = /* @__PURE__ */ new Set();
|
|
125
|
+
function walk(n) {
|
|
126
|
+
if (visited.has(n)) return;
|
|
127
|
+
visited.add(n);
|
|
128
|
+
result.push(n);
|
|
129
|
+
if (n.kind === "computed") {
|
|
130
|
+
for (const parent of n._parents) {
|
|
131
|
+
walk(parent);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
walk(node2);
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
function descendants(node2) {
|
|
139
|
+
const result = [];
|
|
140
|
+
const visited = /* @__PURE__ */ new Set();
|
|
141
|
+
function walk(n) {
|
|
142
|
+
if (visited.has(n)) return;
|
|
143
|
+
visited.add(n);
|
|
144
|
+
result.push(n);
|
|
145
|
+
for (const child of n._children) {
|
|
146
|
+
walk(child);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
walk(node2);
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
function graphSize(root) {
|
|
153
|
+
return ancestors(root).length;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/batch.ts
|
|
157
|
+
var inBatch = false;
|
|
158
|
+
var batchedInputs = /* @__PURE__ */ new Set();
|
|
159
|
+
function batch(fn) {
|
|
160
|
+
if (inBatch) {
|
|
161
|
+
throw new Error("Cannot nest batch() calls");
|
|
162
|
+
}
|
|
163
|
+
inBatch = true;
|
|
164
|
+
batchedInputs.clear();
|
|
165
|
+
try {
|
|
166
|
+
fn();
|
|
167
|
+
} finally {
|
|
168
|
+
inBatch = false;
|
|
169
|
+
}
|
|
170
|
+
const changed = collectDirtied(batchedInputs);
|
|
171
|
+
batchedInputs.clear();
|
|
172
|
+
return changed;
|
|
173
|
+
}
|
|
174
|
+
function trackInput(node2) {
|
|
175
|
+
if (inBatch) {
|
|
176
|
+
batchedInputs.add(node2);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function collectDirtied(inputs) {
|
|
180
|
+
const changed = /* @__PURE__ */ new Set();
|
|
181
|
+
const queue = [...inputs];
|
|
182
|
+
const visited = /* @__PURE__ */ new Set();
|
|
183
|
+
while (queue.length > 0) {
|
|
184
|
+
const node2 = queue.shift();
|
|
185
|
+
if (visited.has(node2)) continue;
|
|
186
|
+
visited.add(node2);
|
|
187
|
+
changed.add(node2);
|
|
188
|
+
for (const child of node2._children) {
|
|
189
|
+
queue.push(child);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return changed;
|
|
193
|
+
}
|
|
194
|
+
function isInBatch() {
|
|
195
|
+
return inBatch;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// src/node.ts
|
|
199
|
+
function input(value) {
|
|
200
|
+
const self = {
|
|
201
|
+
kind: "input",
|
|
202
|
+
id: generateId(),
|
|
203
|
+
_children: /* @__PURE__ */ new Set(),
|
|
204
|
+
_value: value,
|
|
205
|
+
_dirty: false,
|
|
206
|
+
_disposed: false,
|
|
207
|
+
get() {
|
|
208
|
+
return self._value;
|
|
209
|
+
},
|
|
210
|
+
set(newValue) {
|
|
211
|
+
if (Object.is(self._value, newValue)) return;
|
|
212
|
+
self._value = newValue;
|
|
213
|
+
if (isInBatch()) trackInput(self);
|
|
214
|
+
markDirty(self);
|
|
215
|
+
},
|
|
216
|
+
invalidate() {
|
|
217
|
+
if (isInBatch()) trackInput(self);
|
|
218
|
+
markDirty(self);
|
|
219
|
+
},
|
|
220
|
+
isDirty() {
|
|
221
|
+
return false;
|
|
222
|
+
},
|
|
223
|
+
inspect() {
|
|
224
|
+
return {
|
|
225
|
+
id: self.id,
|
|
226
|
+
kind: "input",
|
|
227
|
+
dirty: false,
|
|
228
|
+
lastValue: self._value,
|
|
229
|
+
computeCount: 0,
|
|
230
|
+
depCount: 0,
|
|
231
|
+
childCount: self._children.size
|
|
232
|
+
};
|
|
233
|
+
},
|
|
234
|
+
dispose() {
|
|
235
|
+
self._disposed = true;
|
|
236
|
+
for (const child of self._children) {
|
|
237
|
+
child._parents = child._parents.filter((p) => p !== self);
|
|
238
|
+
}
|
|
239
|
+
self._children.clear();
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
return self;
|
|
243
|
+
}
|
|
244
|
+
function node(options) {
|
|
245
|
+
const depEntries = Object.entries(options.dependsOn);
|
|
246
|
+
const depKeys = depEntries.map(([k]) => k);
|
|
247
|
+
const depNodes = depEntries.map(([, v]) => v);
|
|
248
|
+
checkForCycles(depNodes);
|
|
249
|
+
const self = {
|
|
250
|
+
kind: "computed",
|
|
251
|
+
id: generateId(),
|
|
252
|
+
_parents: depNodes,
|
|
253
|
+
_children: /* @__PURE__ */ new Set(),
|
|
254
|
+
_compute: options.compute,
|
|
255
|
+
_depKeys: depKeys,
|
|
256
|
+
_depNodes: depNodes,
|
|
257
|
+
_value: void 0,
|
|
258
|
+
_dirty: true,
|
|
259
|
+
_computeCount: 0,
|
|
260
|
+
_disposed: false,
|
|
261
|
+
get() {
|
|
262
|
+
if (self._dirty) {
|
|
263
|
+
recompute(self);
|
|
264
|
+
}
|
|
265
|
+
return self._value;
|
|
266
|
+
},
|
|
267
|
+
invalidate() {
|
|
268
|
+
if (self._dirty) return;
|
|
269
|
+
self._dirty = true;
|
|
270
|
+
markDirty(self);
|
|
271
|
+
},
|
|
272
|
+
isDirty() {
|
|
273
|
+
return self._dirty;
|
|
274
|
+
},
|
|
275
|
+
inspect() {
|
|
276
|
+
return {
|
|
277
|
+
id: self.id,
|
|
278
|
+
kind: "computed",
|
|
279
|
+
dirty: self._dirty,
|
|
280
|
+
lastValue: self._value,
|
|
281
|
+
computeCount: self._computeCount,
|
|
282
|
+
depCount: self._depNodes.length,
|
|
283
|
+
childCount: self._children.size
|
|
284
|
+
};
|
|
285
|
+
},
|
|
286
|
+
dispose() {
|
|
287
|
+
self._disposed = true;
|
|
288
|
+
for (const parent of self._parents) {
|
|
289
|
+
parent._children.delete(self);
|
|
290
|
+
}
|
|
291
|
+
for (const child of self._children) {
|
|
292
|
+
child._parents = child._parents.filter((p) => p !== self);
|
|
293
|
+
}
|
|
294
|
+
self._parents = [];
|
|
295
|
+
self._children.clear();
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
for (const parent of depNodes) {
|
|
299
|
+
parent._children.add(self);
|
|
300
|
+
}
|
|
301
|
+
return self;
|
|
302
|
+
}
|
|
303
|
+
function recompute(self) {
|
|
304
|
+
const deps = {};
|
|
305
|
+
for (let i = 0; i < self._depKeys.length; i++) {
|
|
306
|
+
deps[self._depKeys[i]] = self._depNodes[i].get();
|
|
307
|
+
}
|
|
308
|
+
self._value = self._compute(deps);
|
|
309
|
+
self._dirty = false;
|
|
310
|
+
self._computeCount++;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// src/debug.ts
|
|
314
|
+
function toDot(nodes) {
|
|
315
|
+
const lines = ["digraph inval {"];
|
|
316
|
+
const visited = /* @__PURE__ */ new Set();
|
|
317
|
+
function addNode(node2) {
|
|
318
|
+
if (visited.has(node2)) return;
|
|
319
|
+
visited.add(node2);
|
|
320
|
+
const label = node2.kind === "input" ? `${node2.id} [input]` : `${node2.id} [computed]`;
|
|
321
|
+
lines.push(` "${node2.id}" [label="${label}"];`);
|
|
322
|
+
if (node2.kind === "computed") {
|
|
323
|
+
for (const parent of node2._parents) {
|
|
324
|
+
lines.push(` "${parent.id}" -> "${node2.id}";`);
|
|
325
|
+
addNode(parent);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
for (const node2 of nodes) {
|
|
330
|
+
addNode(node2);
|
|
331
|
+
}
|
|
332
|
+
lines.push("}");
|
|
333
|
+
return lines.join("\n");
|
|
334
|
+
}
|
|
335
|
+
function stats(nodes) {
|
|
336
|
+
let inputCount = 0;
|
|
337
|
+
let computedCount = 0;
|
|
338
|
+
let edgeCount = 0;
|
|
339
|
+
let totalComputeCalls = 0;
|
|
340
|
+
const visited = /* @__PURE__ */ new Set();
|
|
341
|
+
function count(node2) {
|
|
342
|
+
if (visited.has(node2)) return;
|
|
343
|
+
visited.add(node2);
|
|
344
|
+
if (node2.kind === "input") {
|
|
345
|
+
inputCount++;
|
|
346
|
+
} else {
|
|
347
|
+
computedCount++;
|
|
348
|
+
edgeCount += node2._parents.length;
|
|
349
|
+
totalComputeCalls += node2._computeCount;
|
|
350
|
+
for (const parent of node2._parents) {
|
|
351
|
+
count(parent);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
for (const node2 of nodes) {
|
|
356
|
+
count(node2);
|
|
357
|
+
}
|
|
358
|
+
return {
|
|
359
|
+
nodeCount: inputCount + computedCount,
|
|
360
|
+
inputCount,
|
|
361
|
+
computedCount,
|
|
362
|
+
edgeCount,
|
|
363
|
+
totalComputeCalls
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
367
|
+
0 && (module.exports = {
|
|
368
|
+
InvalCycleError,
|
|
369
|
+
ancestors,
|
|
370
|
+
batch,
|
|
371
|
+
checkForCycles,
|
|
372
|
+
descendants,
|
|
373
|
+
graphSize,
|
|
374
|
+
input,
|
|
375
|
+
node,
|
|
376
|
+
resetIdCounter,
|
|
377
|
+
stats,
|
|
378
|
+
toDot,
|
|
379
|
+
why
|
|
380
|
+
});
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { input, node } from './node.js';
|
|
2
|
+
export { batch } from './batch.js';
|
|
3
|
+
export { why, ancestors, descendants, graphSize, checkForCycles, InvalCycleError } from './graph.js';
|
|
4
|
+
export { toDot, stats } from './debug.js';
|
|
5
|
+
export { resetIdCounter } from './types.js';
|
|
6
|
+
export type { InputNode, ComputedNode, Node, NodeOptions, InspectInfo, } from './types.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAClC,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AACpG,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC3C,YAAY,EACV,SAAS,EACT,YAAY,EACZ,IAAI,EACJ,WAAW,EACX,WAAW,GACZ,MAAM,YAAY,CAAA"}
|