@fluixi/devtools 0.2.0-alpha.2 → 0.2.0-alpha.4
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 +80 -2
- package/dist/callsite.cjs +72 -0
- package/dist/callsite.d.ts +22 -0
- package/dist/callsite.d.ts.map +1 -0
- package/dist/callsite.js +82 -0
- package/dist/callsite.mjs +51 -0
- package/dist/change-view.cjs +134 -0
- package/dist/change-view.d.ts +10 -0
- package/dist/change-view.d.ts.map +1 -0
- package/dist/change-view.js +102 -0
- package/dist/change-view.mjs +111 -0
- package/dist/copy.cjs +63 -0
- package/dist/copy.d.ts +9 -0
- package/dist/copy.d.ts.map +1 -0
- package/dist/copy.js +52 -0
- package/dist/copy.mjs +42 -0
- package/dist/describe.cjs +55 -0
- package/dist/describe.d.ts +3 -0
- package/dist/describe.d.ts.map +1 -0
- package/dist/describe.js +47 -0
- package/dist/describe.mjs +34 -0
- package/dist/detail-panel.cjs +307 -0
- package/dist/detail-panel.d.ts +17 -0
- package/dist/detail-panel.d.ts.map +1 -0
- package/dist/detail-panel.js +217 -0
- package/dist/detail-panel.mjs +284 -0
- package/dist/diff.cjs +225 -0
- package/dist/diff.d.ts +31 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +132 -0
- package/dist/diff.mjs +202 -0
- package/dist/dom.cjs +204 -0
- package/dist/dom.d.ts +81 -0
- package/dist/dom.d.ts.map +1 -0
- package/dist/dom.js +269 -0
- package/dist/dom.mjs +183 -0
- package/dist/export.cjs +221 -0
- package/dist/export.d.ts +55 -0
- package/dist/export.d.ts.map +1 -0
- package/dist/export.js +90 -0
- package/dist/export.mjs +209 -0
- package/dist/float.cjs +232 -0
- package/dist/float.d.ts +54 -0
- package/dist/float.d.ts.map +1 -0
- package/dist/float.js +256 -0
- package/dist/float.mjs +211 -0
- package/dist/format.cjs +175 -0
- package/dist/format.d.ts +16 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +188 -0
- package/dist/format.mjs +154 -0
- package/dist/graph-view.cjs +893 -0
- package/dist/graph-view.d.ts +79 -2
- package/dist/graph-view.d.ts.map +1 -1
- package/dist/graph-view.js +866 -50
- package/dist/graph-view.mjs +870 -0
- package/dist/highlight.cjs +140 -0
- package/dist/highlight.d.ts +56 -0
- package/dist/highlight.d.ts.map +1 -0
- package/dist/highlight.js +154 -0
- package/dist/highlight.mjs +119 -0
- package/dist/history.cjs +95 -0
- package/dist/history.d.ts +59 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +85 -0
- package/dist/history.mjs +74 -0
- package/dist/hook.cjs +1562 -0
- package/dist/hook.d.ts +60 -0
- package/dist/hook.d.ts.map +1 -0
- package/dist/hook.js +142 -0
- package/dist/hook.mjs +1553 -0
- package/dist/index.cjs +4552 -0
- package/dist/index.d.ts +44 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -1
- package/dist/index.mjs +4544 -0
- package/dist/inspector.cjs +688 -0
- package/dist/inspector.d.ts +60 -0
- package/dist/inspector.d.ts.map +1 -0
- package/dist/inspector.js +415 -0
- package/dist/inspector.mjs +676 -0
- package/dist/instrument.cjs +497 -0
- package/dist/instrument.d.ts +147 -5
- package/dist/instrument.d.ts.map +1 -1
- package/dist/instrument.js +164 -44
- package/dist/instrument.mjs +487 -0
- package/dist/kind-filter.cjs +172 -0
- package/dist/kind-filter.d.ts +39 -0
- package/dist/kind-filter.d.ts.map +1 -0
- package/dist/kind-filter.js +151 -0
- package/dist/kind-filter.mjs +151 -0
- package/dist/menu.cjs +141 -0
- package/dist/menu.d.ts +37 -0
- package/dist/menu.d.ts.map +1 -0
- package/dist/menu.js +125 -0
- package/dist/menu.mjs +120 -0
- package/dist/observe.cjs +1352 -0
- package/dist/observe.d.ts +80 -0
- package/dist/observe.d.ts.map +1 -0
- package/dist/observe.js +893 -0
- package/dist/observe.mjs +1345 -0
- package/dist/palette.cjs +52 -0
- package/dist/palette.d.ts +11 -0
- package/dist/palette.d.ts.map +1 -0
- package/dist/palette.js +27 -0
- package/dist/palette.mjs +31 -0
- package/dist/path.cjs +45 -0
- package/dist/path.d.ts +19 -0
- package/dist/path.d.ts.map +1 -0
- package/dist/path.js +49 -0
- package/dist/path.mjs +24 -0
- package/dist/plugin.cjs +87 -0
- package/dist/plugin.d.ts +121 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +74 -0
- package/dist/plugin.mjs +66 -0
- package/dist/plugins/context.cjs +98 -0
- package/dist/plugins/context.d.ts +10 -0
- package/dist/plugins/context.d.ts.map +1 -0
- package/dist/plugins/context.js +80 -0
- package/dist/plugins/context.mjs +75 -0
- package/dist/plugins/directives.cjs +98 -0
- package/dist/plugins/directives.d.ts +25 -0
- package/dist/plugins/directives.d.ts.map +1 -0
- package/dist/plugins/directives.js +78 -0
- package/dist/plugins/directives.mjs +75 -0
- package/dist/plugins/index.cjs +162 -0
- package/dist/plugins/index.d.ts +7 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +9 -0
- package/dist/plugins/index.mjs +140 -0
- package/dist/source.cjs +111 -0
- package/dist/source.d.ts +72 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +153 -0
- package/dist/source.mjs +90 -0
- package/dist/timeline.cjs +350 -0
- package/dist/timeline.d.ts +30 -0
- package/dist/timeline.d.ts.map +1 -0
- package/dist/timeline.js +271 -0
- package/dist/timeline.mjs +327 -0
- package/dist/tree.cjs +574 -0
- package/dist/tree.d.ts +128 -0
- package/dist/tree.d.ts.map +1 -0
- package/dist/tree.js +645 -0
- package/dist/tree.mjs +551 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/dist/value-pane.cjs +223 -0
- package/dist/value-pane.d.ts +10 -0
- package/dist/value-pane.d.ts.map +1 -0
- package/dist/value-pane.js +61 -0
- package/dist/value-pane.mjs +200 -0
- package/dist/wire.cjs +109 -0
- package/dist/wire.d.ts +100 -0
- package/dist/wire.d.ts.map +1 -0
- package/dist/wire.js +93 -0
- package/dist/wire.mjs +89 -0
- package/package.json +17 -5
|
@@ -0,0 +1,497 @@
|
|
|
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/instrument.ts
|
|
21
|
+
var instrument_exports = {};
|
|
22
|
+
__export(instrument_exports, {
|
|
23
|
+
KIND_OF_BUILTIN: () => KIND_OF_BUILTIN,
|
|
24
|
+
createReactor: () => createReactor,
|
|
25
|
+
fmt: () => fmt,
|
|
26
|
+
nodeState: () => nodeState,
|
|
27
|
+
nodeVersion: () => nodeVersion,
|
|
28
|
+
short: () => short
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(instrument_exports);
|
|
31
|
+
var import_signal = require("@fluixi/reactive/signal");
|
|
32
|
+
var import_store = require("@fluixi/reactive/store");
|
|
33
|
+
|
|
34
|
+
// src/source.ts
|
|
35
|
+
var MARKER = "__fx_source";
|
|
36
|
+
var COMPONENT_MARKER = "__fx_component";
|
|
37
|
+
var HOLE_MARKER = "__fx_hole";
|
|
38
|
+
var PROPS_MARKER = "__fx_props";
|
|
39
|
+
var MARKS = "__FLUIXI_DEVTOOLS_MARKS__";
|
|
40
|
+
var host = globalThis;
|
|
41
|
+
var shared = host[MARKS] ?? {
|
|
42
|
+
pending: null,
|
|
43
|
+
expiring: false,
|
|
44
|
+
components: /* @__PURE__ */ new Map()
|
|
45
|
+
};
|
|
46
|
+
host[MARKS] = shared;
|
|
47
|
+
function markReactiveSource(file, line, column, name) {
|
|
48
|
+
const location = locate(file, line, column);
|
|
49
|
+
if (shared.pending && location.external && !shared.pending.location?.external) return;
|
|
50
|
+
shared.pending = { name, location };
|
|
51
|
+
shared.expiring = false;
|
|
52
|
+
}
|
|
53
|
+
function locate(file, line, column) {
|
|
54
|
+
const external = !!file && (/^([a-zA-Z]:)?[\\/]/.test(file) || file.includes("node_modules/"));
|
|
55
|
+
return external ? { file, line, column, external } : { file, line, column };
|
|
56
|
+
}
|
|
57
|
+
function markComponentSource(name, file, line, column) {
|
|
58
|
+
const seen = shared.components.get(name);
|
|
59
|
+
const next = locate(file, line, column);
|
|
60
|
+
if (seen === void 0) {
|
|
61
|
+
shared.components.set(name, next);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (!seen || seen.file !== file || seen.line !== line) shared.components.set(name, null);
|
|
65
|
+
}
|
|
66
|
+
function componentSource(name) {
|
|
67
|
+
return shared.components.get(name) ?? void 0;
|
|
68
|
+
}
|
|
69
|
+
function takeReactiveSource() {
|
|
70
|
+
const mark = shared.pending;
|
|
71
|
+
if (mark) {
|
|
72
|
+
if (mark.name !== void 0) shared.pending = { location: mark.location };
|
|
73
|
+
if (!shared.expiring) {
|
|
74
|
+
shared.expiring = true;
|
|
75
|
+
queueMicrotask(() => {
|
|
76
|
+
shared.expiring = false;
|
|
77
|
+
shared.pending = null;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return mark;
|
|
82
|
+
}
|
|
83
|
+
var HOLES = /* @__PURE__ */ new WeakMap();
|
|
84
|
+
var HOLE_TAG = "__fx_at";
|
|
85
|
+
function markHole(accessor, line, column, name) {
|
|
86
|
+
if (accessor && (typeof accessor === "object" || typeof accessor === "function")) {
|
|
87
|
+
const at = name === void 0 ? { line, column } : { line, column, name };
|
|
88
|
+
HOLES.set(accessor, at);
|
|
89
|
+
try {
|
|
90
|
+
Object.defineProperty(accessor, HOLE_TAG, { value: at, configurable: true });
|
|
91
|
+
} catch {
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
shared.pending = null;
|
|
95
|
+
return accessor;
|
|
96
|
+
}
|
|
97
|
+
var PROPS = /* @__PURE__ */ new WeakMap();
|
|
98
|
+
function markProps(props, at) {
|
|
99
|
+
if (props && typeof props === "object") PROPS.set(props, at);
|
|
100
|
+
return props;
|
|
101
|
+
}
|
|
102
|
+
function installSourceMarker() {
|
|
103
|
+
if (typeof host[MARKER] !== "function") host[MARKER] = markReactiveSource;
|
|
104
|
+
if (typeof host[COMPONENT_MARKER] !== "function") host[COMPONENT_MARKER] = markComponentSource;
|
|
105
|
+
if (typeof host[HOLE_MARKER] !== "function") host[HOLE_MARKER] = markHole;
|
|
106
|
+
if (typeof host[PROPS_MARKER] !== "function") host[PROPS_MARKER] = markProps;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/format.ts
|
|
110
|
+
var MAX_DEPTH = 3;
|
|
111
|
+
var MAX_ENTRIES = 8;
|
|
112
|
+
function domName(v) {
|
|
113
|
+
if (typeof v?.nodeType !== "number" || typeof v.nodeName !== "string") return null;
|
|
114
|
+
return v.tagName ? `<${v.tagName.toLowerCase()}>` : `#${v.nodeName.toLowerCase()}`;
|
|
115
|
+
}
|
|
116
|
+
function fnName(v) {
|
|
117
|
+
return v.name ? `ƒ ${v.name}()` : "ƒ";
|
|
118
|
+
}
|
|
119
|
+
function className(v) {
|
|
120
|
+
const name = v.constructor?.name;
|
|
121
|
+
return name && name !== "Object" ? `${name} ` : "";
|
|
122
|
+
}
|
|
123
|
+
function primitive(v) {
|
|
124
|
+
if (v === null) return "null";
|
|
125
|
+
switch (typeof v) {
|
|
126
|
+
case "undefined":
|
|
127
|
+
return "undefined";
|
|
128
|
+
case "string":
|
|
129
|
+
return JSON.stringify(v);
|
|
130
|
+
case "number":
|
|
131
|
+
case "boolean":
|
|
132
|
+
return String(v);
|
|
133
|
+
case "bigint":
|
|
134
|
+
return `${v}n`;
|
|
135
|
+
case "symbol":
|
|
136
|
+
return v.toString();
|
|
137
|
+
case "function":
|
|
138
|
+
return fnName(v);
|
|
139
|
+
default:
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function walk(value, depth, seen) {
|
|
144
|
+
const simple = primitive(value);
|
|
145
|
+
if (simple !== null) return simple;
|
|
146
|
+
const object = value;
|
|
147
|
+
if (seen.has(object)) return "[circular]";
|
|
148
|
+
const dom = domName(object);
|
|
149
|
+
if (dom) return dom;
|
|
150
|
+
if (object instanceof Date) return Number.isNaN(object.getTime()) ? "Invalid Date" : object.toISOString();
|
|
151
|
+
if (object instanceof RegExp) return String(object);
|
|
152
|
+
if (object instanceof Error) return `${object.name}: ${object.message}`;
|
|
153
|
+
if (object instanceof Promise) return "Promise";
|
|
154
|
+
if (ArrayBuffer.isView(object)) {
|
|
155
|
+
return `${className(object).trim()}(${object.length ?? ""})`;
|
|
156
|
+
}
|
|
157
|
+
if (depth >= MAX_DEPTH) return Array.isArray(object) ? "[…]" : "{…}";
|
|
158
|
+
seen.add(object);
|
|
159
|
+
try {
|
|
160
|
+
if (Array.isArray(object)) {
|
|
161
|
+
const shown2 = object.slice(0, MAX_ENTRIES).map((item) => walk(item, depth + 1, seen));
|
|
162
|
+
if (object.length > MAX_ENTRIES) shown2.push("…");
|
|
163
|
+
return `[${shown2.join(", ")}]`;
|
|
164
|
+
}
|
|
165
|
+
if (object instanceof Map) {
|
|
166
|
+
const shown2 = [];
|
|
167
|
+
for (const [k, v] of object) {
|
|
168
|
+
if (shown2.length === MAX_ENTRIES) {
|
|
169
|
+
shown2.push("…");
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
shown2.push(`${walk(k, depth + 1, seen)} => ${walk(v, depth + 1, seen)}`);
|
|
173
|
+
}
|
|
174
|
+
return `Map(${object.size}) {${shown2.join(", ")}}`;
|
|
175
|
+
}
|
|
176
|
+
if (object instanceof Set) {
|
|
177
|
+
const shown2 = [];
|
|
178
|
+
for (const item of object) {
|
|
179
|
+
if (shown2.length === MAX_ENTRIES) {
|
|
180
|
+
shown2.push("…");
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
shown2.push(walk(item, depth + 1, seen));
|
|
184
|
+
}
|
|
185
|
+
return `Set(${object.size}) {${shown2.join(", ")}}`;
|
|
186
|
+
}
|
|
187
|
+
const keys = Object.keys(object);
|
|
188
|
+
const shown = keys.slice(0, MAX_ENTRIES).map((key) => {
|
|
189
|
+
let read;
|
|
190
|
+
try {
|
|
191
|
+
read = walk(object[key], depth + 1, seen);
|
|
192
|
+
} catch {
|
|
193
|
+
read = "[unreadable]";
|
|
194
|
+
}
|
|
195
|
+
return `${key}: ${read}`;
|
|
196
|
+
});
|
|
197
|
+
if (keys.length > MAX_ENTRIES) shown.push("…");
|
|
198
|
+
return `${className(object)}{${shown.join(", ")}}`;
|
|
199
|
+
} finally {
|
|
200
|
+
seen.delete(object);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function formatValue(value, limit = 240) {
|
|
204
|
+
let text;
|
|
205
|
+
try {
|
|
206
|
+
text = walk(value, 0, /* @__PURE__ */ new Set());
|
|
207
|
+
} catch {
|
|
208
|
+
text = "[unreadable]";
|
|
209
|
+
}
|
|
210
|
+
return text.length > limit ? `${text.slice(0, limit - 1)}…` : text;
|
|
211
|
+
}
|
|
212
|
+
function shorten(text, limit = 18) {
|
|
213
|
+
return text.length > limit ? `${text.slice(0, limit - 1)}…` : text;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// src/instrument.ts
|
|
217
|
+
var $NODE = /* @__PURE__ */ Symbol.for("signal-node");
|
|
218
|
+
var KIND_OF_BUILTIN = {
|
|
219
|
+
Show: "control",
|
|
220
|
+
For: "control",
|
|
221
|
+
Index: "control",
|
|
222
|
+
Switch: "control",
|
|
223
|
+
Match: "control",
|
|
224
|
+
Portal: "control",
|
|
225
|
+
Dynamic: "control",
|
|
226
|
+
ErrorBoundary: "control",
|
|
227
|
+
Suspense: "control",
|
|
228
|
+
SuspenseList: "control",
|
|
229
|
+
Await: "control",
|
|
230
|
+
Router: "router",
|
|
231
|
+
Outlet: "router",
|
|
232
|
+
Redirect: "router",
|
|
233
|
+
Link: "router"
|
|
234
|
+
};
|
|
235
|
+
var CORE_STATE = { 0: "clean", 1: "check", 2: "dirty" };
|
|
236
|
+
function nodeVersion(n) {
|
|
237
|
+
return n.core && typeof n.core.version === "number" ? n.core.version : n.runs;
|
|
238
|
+
}
|
|
239
|
+
function nodeState(n) {
|
|
240
|
+
return n.core && typeof n.core.state === "number" ? CORE_STATE[n.core.state] : void 0;
|
|
241
|
+
}
|
|
242
|
+
var fmt = (v) => formatValue(v);
|
|
243
|
+
var short = (s, n = 18) => shorten(s, n);
|
|
244
|
+
function createReactor() {
|
|
245
|
+
installSourceMarker();
|
|
246
|
+
const graph = /* @__PURE__ */ new Map();
|
|
247
|
+
const runsAtLastSample = /* @__PURE__ */ new Map();
|
|
248
|
+
const unnamed = /* @__PURE__ */ new Set();
|
|
249
|
+
const consumerStack = [];
|
|
250
|
+
let seq = 0;
|
|
251
|
+
let dispose = () => {
|
|
252
|
+
};
|
|
253
|
+
let rootOwner = null;
|
|
254
|
+
const claims = (node, value) => {
|
|
255
|
+
for (const dep of node.deps) {
|
|
256
|
+
const other = graph.get(dep);
|
|
257
|
+
if (other?.read && other.read() === value) return false;
|
|
258
|
+
}
|
|
259
|
+
return true;
|
|
260
|
+
};
|
|
261
|
+
const forget = (id) => {
|
|
262
|
+
const owner = (0, import_signal.getOwner)() ?? rootOwner;
|
|
263
|
+
if (owner) {
|
|
264
|
+
(0, import_signal.runWithOwner)(
|
|
265
|
+
owner,
|
|
266
|
+
() => (0, import_signal.onCleanup)(() => {
|
|
267
|
+
graph.delete(id);
|
|
268
|
+
runsAtLastSample.delete(id);
|
|
269
|
+
unnamed.delete(id);
|
|
270
|
+
})
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
const recordRead = (sourceId) => {
|
|
275
|
+
const c = consumerStack[consumerStack.length - 1];
|
|
276
|
+
if (c != null) graph.get(c)?.deps.add(sourceId);
|
|
277
|
+
};
|
|
278
|
+
const recordWrite = (targetId) => {
|
|
279
|
+
const c = consumerStack[consumerStack.length - 1];
|
|
280
|
+
if (c != null && c !== targetId) graph.get(c)?.writes.add(targetId);
|
|
281
|
+
};
|
|
282
|
+
const base = (id, named, kind, fallback) => {
|
|
283
|
+
const mark = takeReactiveSource();
|
|
284
|
+
if (!named && !mark?.name) unnamed.add(id);
|
|
285
|
+
return {
|
|
286
|
+
id,
|
|
287
|
+
label: named ?? mark?.name ?? fallback,
|
|
288
|
+
kind,
|
|
289
|
+
deps: /* @__PURE__ */ new Set(),
|
|
290
|
+
writes: /* @__PURE__ */ new Set(),
|
|
291
|
+
runs: 0,
|
|
292
|
+
lastPulse: 0,
|
|
293
|
+
...mark?.location ? { source: mark.location } : {}
|
|
294
|
+
};
|
|
295
|
+
};
|
|
296
|
+
const trackedCreateSignal = (value, options) => {
|
|
297
|
+
const id = seq++;
|
|
298
|
+
const [get, set] = (0, import_signal.createSignal)(value, options);
|
|
299
|
+
const node = base(id, options?.name, "state", `s${id}`);
|
|
300
|
+
node.read = () => (0, import_signal.untrack)(get);
|
|
301
|
+
node.detail = formatValue(value);
|
|
302
|
+
node.value = shorten(node.detail);
|
|
303
|
+
node.core = get[$NODE] ?? null;
|
|
304
|
+
graph.set(id, node);
|
|
305
|
+
forget(id);
|
|
306
|
+
const read = () => {
|
|
307
|
+
recordRead(id);
|
|
308
|
+
return get();
|
|
309
|
+
};
|
|
310
|
+
const write = (v) => {
|
|
311
|
+
recordWrite(id);
|
|
312
|
+
return set(typeof v === "function" ? v((0, import_signal.untrack)(get)) : v);
|
|
313
|
+
};
|
|
314
|
+
return [read, write];
|
|
315
|
+
};
|
|
316
|
+
const trackedCreateMemo = (fn, value, options) => {
|
|
317
|
+
const id = seq++;
|
|
318
|
+
const node = base(id, options?.name, "memo", `m${id}`);
|
|
319
|
+
graph.set(id, node);
|
|
320
|
+
forget(id);
|
|
321
|
+
const m = (0, import_signal.createMemo)(() => {
|
|
322
|
+
node.core ??= import_signal.Signal.subtle.currentComputed();
|
|
323
|
+
node.deps.clear();
|
|
324
|
+
node.writes.clear();
|
|
325
|
+
node.runs++;
|
|
326
|
+
consumerStack.push(id);
|
|
327
|
+
try {
|
|
328
|
+
return fn();
|
|
329
|
+
} finally {
|
|
330
|
+
consumerStack.pop();
|
|
331
|
+
}
|
|
332
|
+
}, value, options);
|
|
333
|
+
node.read = () => (0, import_signal.untrack)(m);
|
|
334
|
+
return () => {
|
|
335
|
+
recordRead(id);
|
|
336
|
+
return m();
|
|
337
|
+
};
|
|
338
|
+
};
|
|
339
|
+
const trackedCreateEffect = (fn, options) => {
|
|
340
|
+
const id = seq++;
|
|
341
|
+
const node = base(id, options?.name, "effect", `e${id}`);
|
|
342
|
+
graph.set(id, node);
|
|
343
|
+
forget(id);
|
|
344
|
+
(0, import_signal.createEffect)(() => {
|
|
345
|
+
node.core ??= import_signal.Signal.subtle.currentComputed();
|
|
346
|
+
node.deps.clear();
|
|
347
|
+
node.writes.clear();
|
|
348
|
+
node.runs++;
|
|
349
|
+
node.lastPulse = performance.now();
|
|
350
|
+
consumerStack.push(id);
|
|
351
|
+
try {
|
|
352
|
+
fn();
|
|
353
|
+
} finally {
|
|
354
|
+
consumerStack.pop();
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
};
|
|
358
|
+
const trackedCreateStore = (initial, options) => {
|
|
359
|
+
const id = seq++;
|
|
360
|
+
const [proxy, setter] = (0, import_store.createStore)(initial, { immutable: true, ...options });
|
|
361
|
+
const node = base(id, options?.name, "store", `store${id}`);
|
|
362
|
+
node.read = () => {
|
|
363
|
+
try {
|
|
364
|
+
return (0, import_signal.untrack)(() => JSON.stringify(proxy));
|
|
365
|
+
} catch {
|
|
366
|
+
return "{…}";
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
node.value = "{…}";
|
|
370
|
+
graph.set(id, node);
|
|
371
|
+
forget(id);
|
|
372
|
+
const recording = new Proxy(proxy, {
|
|
373
|
+
get(t, p, r) {
|
|
374
|
+
recordRead(id);
|
|
375
|
+
return Reflect.get(t, p, r);
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
const write = (...args) => {
|
|
379
|
+
recordWrite(id);
|
|
380
|
+
return setter(...args);
|
|
381
|
+
};
|
|
382
|
+
return [recording, write];
|
|
383
|
+
};
|
|
384
|
+
const trackedCreateResource = (...args) => {
|
|
385
|
+
const id = seq++;
|
|
386
|
+
const last = args[args.length - 1];
|
|
387
|
+
const name = last && typeof last === "object" && !Array.isArray(last) && typeof last !== "function" ? last.name : void 0;
|
|
388
|
+
const node = base(id, name, "resource", `res${id}`);
|
|
389
|
+
const [data, actions] = (0, import_signal.createResource)(...args);
|
|
390
|
+
node.read = () => {
|
|
391
|
+
try {
|
|
392
|
+
return (0, import_signal.untrack)(() => data.loading ? "⏳ loading" : JSON.stringify(data.latest));
|
|
393
|
+
} catch {
|
|
394
|
+
return "?";
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
node.value = "⏳ loading";
|
|
398
|
+
graph.set(id, node);
|
|
399
|
+
forget(id);
|
|
400
|
+
const accessor = (() => {
|
|
401
|
+
recordRead(id);
|
|
402
|
+
return data();
|
|
403
|
+
});
|
|
404
|
+
for (const k of ["loading", "error", "latest", "state"]) {
|
|
405
|
+
Object.defineProperty(accessor, k, { get: () => data[k] });
|
|
406
|
+
}
|
|
407
|
+
return [accessor, actions];
|
|
408
|
+
};
|
|
409
|
+
const trackedSignal = (value, options) => {
|
|
410
|
+
const [read, write] = trackedCreateSignal(value, options);
|
|
411
|
+
Object.defineProperty(read, "set", { value: write });
|
|
412
|
+
return read;
|
|
413
|
+
};
|
|
414
|
+
const trackedMemo = trackedCreateMemo;
|
|
415
|
+
const trackedEffect = trackedCreateEffect;
|
|
416
|
+
const trackedStore = (initial, options) => {
|
|
417
|
+
const [state, set] = trackedCreateStore(initial, options);
|
|
418
|
+
return new Proxy(state, {
|
|
419
|
+
get: (target, key) => key === "set" ? set : target[key],
|
|
420
|
+
has: (target, key) => key === "set" || Reflect.has(target, key)
|
|
421
|
+
});
|
|
422
|
+
};
|
|
423
|
+
const trackedResource = (...args) => {
|
|
424
|
+
const [accessor, controls] = trackedCreateResource(...args);
|
|
425
|
+
return Object.assign(accessor, controls);
|
|
426
|
+
};
|
|
427
|
+
const api = {
|
|
428
|
+
createSignal: trackedCreateSignal,
|
|
429
|
+
createMemo: trackedCreateMemo,
|
|
430
|
+
createEffect: trackedCreateEffect,
|
|
431
|
+
createStore: trackedCreateStore,
|
|
432
|
+
createResource: trackedCreateResource,
|
|
433
|
+
signal: trackedSignal,
|
|
434
|
+
memo: trackedMemo,
|
|
435
|
+
store: trackedStore,
|
|
436
|
+
effect: trackedEffect,
|
|
437
|
+
resource: trackedResource
|
|
438
|
+
};
|
|
439
|
+
return {
|
|
440
|
+
graph,
|
|
441
|
+
run(setup) {
|
|
442
|
+
dispose();
|
|
443
|
+
graph.clear();
|
|
444
|
+
runsAtLastSample.clear();
|
|
445
|
+
unnamed.clear();
|
|
446
|
+
seq = 0;
|
|
447
|
+
consumerStack.length = 0;
|
|
448
|
+
(0, import_signal.createRoot)((d) => {
|
|
449
|
+
dispose = d;
|
|
450
|
+
rootOwner = (0, import_signal.getOwner)();
|
|
451
|
+
setup(api);
|
|
452
|
+
});
|
|
453
|
+
},
|
|
454
|
+
sample() {
|
|
455
|
+
const changed = [];
|
|
456
|
+
const now = Date.now();
|
|
457
|
+
for (const node of graph.values()) {
|
|
458
|
+
if (!node.read) {
|
|
459
|
+
const seen = runsAtLastSample.get(node.id);
|
|
460
|
+
if (seen !== void 0 && node.runs > seen) {
|
|
461
|
+
node.lastPulse = now;
|
|
462
|
+
changed.push(node.id);
|
|
463
|
+
}
|
|
464
|
+
runsAtLastSample.set(node.id, node.runs);
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
const raw = node.read();
|
|
468
|
+
const stamped = raw?.$name;
|
|
469
|
+
if (node.kind === "memo" && typeof stamped === "string" && stamped && claims(node, raw)) {
|
|
470
|
+
node.kind = KIND_OF_BUILTIN[stamped] ?? "component";
|
|
471
|
+
node.declaredAt = componentSource(stamped);
|
|
472
|
+
node.source ??= node.declaredAt;
|
|
473
|
+
if (unnamed.has(node.id)) {
|
|
474
|
+
node.label = stamped;
|
|
475
|
+
unnamed.delete(node.id);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
const detail = formatValue(raw);
|
|
479
|
+
const next = shorten(detail);
|
|
480
|
+
if (next !== node.value || detail !== node.detail) {
|
|
481
|
+
node.detail = detail;
|
|
482
|
+
node.value = next;
|
|
483
|
+
node.runs++;
|
|
484
|
+
node.lastPulse = now;
|
|
485
|
+
changed.push(node.id);
|
|
486
|
+
}
|
|
487
|
+
runsAtLastSample.set(node.id, node.runs);
|
|
488
|
+
}
|
|
489
|
+
return changed;
|
|
490
|
+
},
|
|
491
|
+
dispose() {
|
|
492
|
+
dispose();
|
|
493
|
+
dispose = () => {
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
}
|
package/dist/instrument.d.ts
CHANGED
|
@@ -1,9 +1,56 @@
|
|
|
1
|
-
|
|
1
|
+
import { Immutable, StoreSetter } from '@fluixi/reactive/store';
|
|
2
|
+
import { type ReactiveSourceLocation } from './source.js';
|
|
3
|
+
import type { DomNode } from './dom.js';
|
|
4
|
+
export type NodeKind = 'state' | 'memo' | 'effect' | 'store' | 'resource' | 'component'
|
|
5
|
+
/** Control flow the framework supplies: `Show`, `For`, `Switch`, `Suspense`, `Await`. */
|
|
6
|
+
| 'control'
|
|
7
|
+
/** The router's own tags: `Router`, `Outlet`, `Link`, `Redirect`. */
|
|
8
|
+
| 'router'
|
|
9
|
+
/** A server route handler: `GET`, `POST` in an api file. */
|
|
10
|
+
| 'handler'
|
|
11
|
+
/** Not a node: a card a view draws to stand for several of them. */
|
|
12
|
+
| 'group'
|
|
13
|
+
/** Not a node: a place in the document a signal writes to. */
|
|
14
|
+
| 'binding'
|
|
15
|
+
/** A directive applied to an element: `use:`, `class:`, `bind:`. */
|
|
16
|
+
| 'directive'
|
|
17
|
+
/** A context, and the components that provide or read it. */
|
|
18
|
+
| 'context'
|
|
19
|
+
/** A component whose job is providing a context. */
|
|
20
|
+
| 'provider'
|
|
21
|
+
/** Not a node: a place in the code that sets a signal. */
|
|
22
|
+
| 'setter';
|
|
2
23
|
export type NodeState = 'clean' | 'check' | 'dirty';
|
|
24
|
+
/**
|
|
25
|
+
* The names the compiler supplies without an import, and what each one is. A control-flow
|
|
26
|
+
* tag stamps its name the same way an author's component does, so without this `Match` and
|
|
27
|
+
* `Switch` are drawn as components the author wrote.
|
|
28
|
+
*
|
|
29
|
+
* Mirrors `resolve/builtins.ts` in @fluixi/compiler. Not imported from there: this package
|
|
30
|
+
* runs beside the application, and the compiler is not on that side of the wall.
|
|
31
|
+
*/
|
|
32
|
+
export declare const KIND_OF_BUILTIN: Record<string, NodeKind>;
|
|
33
|
+
/** One place in the document that a signal drives. */
|
|
34
|
+
export interface DomTarget {
|
|
35
|
+
/** `content` for a text or element hole, `attribute` or `property` otherwise. */
|
|
36
|
+
kind: string;
|
|
37
|
+
/** The attribute or property name, when there is one. */
|
|
38
|
+
name?: string;
|
|
39
|
+
/** The element, as a selector-ish description: `li.done`, `#total`. */
|
|
40
|
+
element: string;
|
|
41
|
+
/** The line of the application that wired this binding up. */
|
|
42
|
+
source?: ReactiveSourceLocation;
|
|
43
|
+
}
|
|
3
44
|
export interface RNode {
|
|
4
45
|
id: number;
|
|
5
46
|
label: string;
|
|
6
47
|
kind: NodeKind;
|
|
48
|
+
/**
|
|
49
|
+
* Whether the label is the author's own, from a compiler mark. False for a positional
|
|
50
|
+
* label and for one the runtime supplied, which between them are the framework's own
|
|
51
|
+
* machinery, which is what a view offers to keep behind one card.
|
|
52
|
+
*/
|
|
53
|
+
authored?: boolean;
|
|
7
54
|
/** Source ids read on the last run (read edges: source → this). */
|
|
8
55
|
deps: Set<number>;
|
|
9
56
|
/** Signal ids written while this node ran (write edges: this → signal). */
|
|
@@ -12,19 +59,88 @@ export interface RNode {
|
|
|
12
59
|
read?: () => unknown;
|
|
13
60
|
/** Last serialized value, for change detection / display. */
|
|
14
61
|
value?: string;
|
|
62
|
+
/** The same value with room to read it, for a tooltip or an inspector. */
|
|
63
|
+
detail?: string;
|
|
15
64
|
/** Times this node recomputed / re-ran. */
|
|
16
65
|
runs: number;
|
|
17
|
-
/** performance.now() of the last change
|
|
66
|
+
/** performance.now() of the last change. Drives the flash. */
|
|
18
67
|
lastPulse: number;
|
|
19
|
-
/** Live core node, when reachable
|
|
68
|
+
/** Live core node, when reachable. Gives the real version and state. */
|
|
20
69
|
core?: {
|
|
21
70
|
version?: number;
|
|
22
71
|
state?: number;
|
|
23
72
|
} | null;
|
|
73
|
+
/** Where the author created this node, when the compiler said so. Always optional. */
|
|
74
|
+
source?: ReactiveSourceLocation;
|
|
75
|
+
/**
|
|
76
|
+
* The node whose run created this one. A component's memo reads nothing of its own, so
|
|
77
|
+
* what it created is what connects it to the rest of the graph.
|
|
78
|
+
*/
|
|
79
|
+
createdIn?: number;
|
|
80
|
+
/**
|
|
81
|
+
* Components only: where the component itself was declared, which is a different place
|
|
82
|
+
* from where it was used and the node made.
|
|
83
|
+
*/
|
|
84
|
+
declaredAt?: ReactiveSourceLocation;
|
|
85
|
+
/**
|
|
86
|
+
* The line of the application that led to this node, for one the compiler did not mark:
|
|
87
|
+
* a signal a package created on the caller's behalf.
|
|
88
|
+
*/
|
|
89
|
+
usedAt?: ReactiveSourceLocation;
|
|
90
|
+
/**
|
|
91
|
+
* How many dom bindings read this node. They are not drawn, since one per attribute and
|
|
92
|
+
* text hole would bury the graph, but a signal read only by them is still read.
|
|
93
|
+
*/
|
|
94
|
+
bindings?: number;
|
|
95
|
+
/**
|
|
96
|
+
* What those bindings write: the text of an `<li>`, the `class` of a `<div>`. Described,
|
|
97
|
+
* never referenced: holding dom nodes would keep a removed subtree alive.
|
|
98
|
+
*/
|
|
99
|
+
targets?: DomTarget[];
|
|
100
|
+
/**
|
|
101
|
+
* Set from outside any computation, by a handler or a timer. The graph has no node to draw an
|
|
102
|
+
* edge from for these, so this is the only record of where they came from.
|
|
103
|
+
*/
|
|
104
|
+
setAt?: ReactiveSourceLocation[];
|
|
105
|
+
/**
|
|
106
|
+
* What this tag rendered, when a producer described it. A panel in the page reads the
|
|
107
|
+
* elements instead; this is for one that cannot.
|
|
108
|
+
*/
|
|
109
|
+
dom?: DomNode[];
|
|
110
|
+
/**
|
|
111
|
+
* Where this tag's element sits in the document, counted across every tag that owns one.
|
|
112
|
+
*
|
|
113
|
+
* Siblings would otherwise sort by id, which is creation order. A keyed list rebuilds a row
|
|
114
|
+
* rather than mutating it, so the replacement takes the highest id and sorted last: toggling
|
|
115
|
+
* the first item in a list moved its card to the end of the band and the flash always landed
|
|
116
|
+
* on the bottom row. Only a producer in the page can work this out.
|
|
117
|
+
*/
|
|
118
|
+
order?: number;
|
|
24
119
|
/** Resource only. */
|
|
25
120
|
loading?: boolean;
|
|
121
|
+
/** This tag arrived as its own chunk, so it is not in the bundle the page started with. */
|
|
122
|
+
lazy?: boolean;
|
|
123
|
+
/** Components only: what the tag was handed, and where each was written. */
|
|
124
|
+
props?: PropReport[];
|
|
125
|
+
/**
|
|
126
|
+
* What plugins have attached, by plugin name. Serialisable, and carried across the wire
|
|
127
|
+
* untouched by anything that does not know the plugin that wrote it.
|
|
128
|
+
*/
|
|
129
|
+
extra?: Record<string, unknown>;
|
|
26
130
|
depth?: number;
|
|
27
131
|
}
|
|
132
|
+
/** One prop of a component tag. The value is read untracked, and only when asked for. */
|
|
133
|
+
export interface PropReport {
|
|
134
|
+
name: string;
|
|
135
|
+
/** Formatted at the moment it was read. Absent when reading threw. */
|
|
136
|
+
value?: string;
|
|
137
|
+
/** Where it was written on the tag. */
|
|
138
|
+
at?: {
|
|
139
|
+
file?: string;
|
|
140
|
+
line?: number;
|
|
141
|
+
column?: number;
|
|
142
|
+
};
|
|
143
|
+
}
|
|
28
144
|
/** The real version (signals/memos) or recompute count fallback. */
|
|
29
145
|
export declare function nodeVersion(n: RNode): number;
|
|
30
146
|
/** The real CLEAN/CHECK/DIRTY state when the core node is reachable (memos), else undefined. */
|
|
@@ -34,7 +150,7 @@ export interface ReactorApi {
|
|
|
34
150
|
name?: string;
|
|
35
151
|
equals?: false | ((a: T, b: T) => boolean);
|
|
36
152
|
}): [() => T, (v: T | ((prev: T) => T)) => T];
|
|
37
|
-
createMemo<T>(fn: () => T, options?: {
|
|
153
|
+
createMemo<T>(fn: () => T, value?: T, options?: {
|
|
38
154
|
name?: string;
|
|
39
155
|
}): () => T;
|
|
40
156
|
createEffect(fn: () => void, options?: {
|
|
@@ -44,7 +160,31 @@ export interface ReactorApi {
|
|
|
44
160
|
name?: string;
|
|
45
161
|
immutable?: boolean;
|
|
46
162
|
}): [T, (...args: unknown[]) => void];
|
|
47
|
-
createResource(...args: unknown[]): [((
|
|
163
|
+
createResource(...args: unknown[]): [(() => unknown) & Record<string, unknown>, unknown];
|
|
164
|
+
signal<T>(value: T, options?: {
|
|
165
|
+
name?: string;
|
|
166
|
+
equals?: false | ((a: T, b: T) => boolean);
|
|
167
|
+
}): (() => T) & ((v: T | ((prev: T) => T)) => T);
|
|
168
|
+
memo<T>(fn: () => T, value?: T, options?: {
|
|
169
|
+
name?: string;
|
|
170
|
+
}): () => T;
|
|
171
|
+
effect(fn: () => void, options?: {
|
|
172
|
+
name?: string;
|
|
173
|
+
}): void;
|
|
174
|
+
store<T extends object>(initial: T, options?: {
|
|
175
|
+
name?: string;
|
|
176
|
+
immutable?: boolean;
|
|
177
|
+
}): Immutable<T> & {
|
|
178
|
+
set: StoreSetter<T>;
|
|
179
|
+
};
|
|
180
|
+
resource<T extends object, S>(fetcher: (source: S) => T | Promise<T>, source?: () => S, options?: {
|
|
181
|
+
name?: string;
|
|
182
|
+
initialValue?: T | (() => T);
|
|
183
|
+
}): () => T | (undefined & {
|
|
184
|
+
loading: boolean;
|
|
185
|
+
error: unknown;
|
|
186
|
+
refetch: () => void;
|
|
187
|
+
});
|
|
48
188
|
}
|
|
49
189
|
export interface Reactor {
|
|
50
190
|
readonly graph: Map<number, RNode>;
|
|
@@ -52,5 +192,7 @@ export interface Reactor {
|
|
|
52
192
|
sample(): number[];
|
|
53
193
|
dispose(): void;
|
|
54
194
|
}
|
|
195
|
+
export declare const fmt: (v: unknown) => string;
|
|
196
|
+
export declare const short: (s: string, n?: number) => string;
|
|
55
197
|
export declare function createReactor(): Reactor;
|
|
56
198
|
//# sourceMappingURL=instrument.d.ts.map
|