@fluixi/vite-plugin 1.0.0-alpha.83 → 1.0.0-alpha.85
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/graph-channel.cjs +170 -9
- package/dist/graph-channel.d.ts.map +1 -1
- package/dist/graph-channel.js +38 -10
- package/dist/graph-channel.mjs +138 -9
- package/dist/index.cjs +202 -9
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/index.mjs +171 -9
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +6 -5
package/dist/graph-channel.cjs
CHANGED
|
@@ -1,10 +1,58 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
6
29
|
|
|
7
|
-
//
|
|
30
|
+
// src/graph-channel.ts
|
|
31
|
+
var graph_channel_exports = {};
|
|
32
|
+
__export(graph_channel_exports, {
|
|
33
|
+
withGraphChannel: () => withGraphChannel
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(graph_channel_exports);
|
|
36
|
+
var import_node_module = require("node:module");
|
|
37
|
+
var import_node_fs = require("node:fs");
|
|
38
|
+
var import_node_path = require("node:path");
|
|
39
|
+
var import_node_url = require("node:url");
|
|
40
|
+
var import_meta = {};
|
|
41
|
+
var VIRTUAL = "virtual:fluixi/devtools-agent";
|
|
42
|
+
var RESOLVED = `\0${VIRTUAL}`;
|
|
43
|
+
var ENDPOINT = "/__fluixi/graph";
|
|
44
|
+
var EVENT = "fluixi:graph";
|
|
45
|
+
var MISSING = "the plugin could not load @fluixi/devtools from its own dependencies";
|
|
46
|
+
var WAITING = "no page has reported yet — open the application in a browser";
|
|
47
|
+
var INTERVAL = 100;
|
|
48
|
+
var HISTORY = 500;
|
|
49
|
+
var agentSource = (devtools, plugins, signal, reactive) => `
|
|
50
|
+
import { installDevtoolsHook, observeGraph } from ${JSON.stringify(devtools)};
|
|
51
|
+
import ${JSON.stringify(plugins)};
|
|
52
|
+
import { observeReactiveNodes } from ${JSON.stringify(signal)};
|
|
53
|
+
import { VERSION } from ${JSON.stringify(reactive)};
|
|
54
|
+
|
|
55
|
+
// The runtime this module imports is the application's own, same module graph, same nodes.
|
|
8
56
|
// Nothing waits for the runtime to offer itself; that path is for a tool loaded from outside.
|
|
9
57
|
//
|
|
10
58
|
// \`watch\` is this copy of devtools, handed over with the runtime. A browser extension
|
|
@@ -13,7 +61,7 @@ import { VERSION } from ${JSON.stringify(n)};
|
|
|
13
61
|
const hook = installDevtoolsHook();
|
|
14
62
|
hook.inject({ observeReactiveNodes, version: VERSION, watch: observeGraph });
|
|
15
63
|
|
|
16
|
-
const send = (kind, snapshot) => import.meta.hot?.send(${JSON.stringify(
|
|
64
|
+
const send = (kind, snapshot) => import.meta.hot?.send(${JSON.stringify(EVENT)}, { kind, snapshot });
|
|
17
65
|
|
|
18
66
|
const first = hook.snapshot();
|
|
19
67
|
// Kept and handed back, so a browser panel polling the same hook cannot consume what this
|
|
@@ -26,7 +74,120 @@ const timer = setInterval(() => {
|
|
|
26
74
|
cursor = delta.cursor;
|
|
27
75
|
// Events count on their own: an effect that ran without changing a value moves no node.
|
|
28
76
|
if (delta.nodes.length || delta.removed?.length || delta.events?.length) send('delta', delta);
|
|
29
|
-
}, ${
|
|
77
|
+
}, ${INTERVAL});
|
|
30
78
|
|
|
31
79
|
import.meta.hot?.dispose(() => clearInterval(timer));
|
|
32
|
-
`;
|
|
80
|
+
`;
|
|
81
|
+
function ownDevtools() {
|
|
82
|
+
return resolveForBrowser("@fluixi/devtools");
|
|
83
|
+
}
|
|
84
|
+
function ownPlugins() {
|
|
85
|
+
return resolveForBrowser("@fluixi/devtools/plugins");
|
|
86
|
+
}
|
|
87
|
+
function resolveForBrowser(spec) {
|
|
88
|
+
try {
|
|
89
|
+
const resolved = import_meta.resolve?.(spec);
|
|
90
|
+
if (resolved) return (0, import_node_url.fileURLToPath)(resolved);
|
|
91
|
+
} catch {
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const required = (0, import_node_module.createRequire)(import_meta.url).resolve(spec);
|
|
95
|
+
if (!required.endsWith(".cjs")) return required;
|
|
96
|
+
for (const ext of [".mjs", ".js"]) {
|
|
97
|
+
const sibling = required.slice(0, -".cjs".length) + ext;
|
|
98
|
+
if ((0, import_node_fs.existsSync)(sibling)) return sibling;
|
|
99
|
+
}
|
|
100
|
+
return required;
|
|
101
|
+
} catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function withGraphChannel(base, enabled, start) {
|
|
106
|
+
let wire = null;
|
|
107
|
+
let available = false;
|
|
108
|
+
let reason = MISSING;
|
|
109
|
+
let root = process.cwd();
|
|
110
|
+
let log;
|
|
111
|
+
let graph = /* @__PURE__ */ new Map();
|
|
112
|
+
let stamp = 0;
|
|
113
|
+
let events = [];
|
|
114
|
+
const baseResolveId = base.resolveId;
|
|
115
|
+
const baseTransformIndexHtml = base.transformIndexHtml;
|
|
116
|
+
return {
|
|
117
|
+
...base,
|
|
118
|
+
resolveId(id, importer) {
|
|
119
|
+
if (id === VIRTUAL) return RESOLVED;
|
|
120
|
+
return baseResolveId?.call(this, id, importer) ?? null;
|
|
121
|
+
},
|
|
122
|
+
async load(id) {
|
|
123
|
+
if (id !== RESOLVED) return null;
|
|
124
|
+
const from = (0, import_node_path.join)(root, "fluixi-devtools-agent.js");
|
|
125
|
+
const specs = ["@fluixi/reactive/signal", "@fluixi/reactive"];
|
|
126
|
+
const ids = await Promise.all(specs.map((spec) => this.resolve?.(spec, from, { skipSelf: true })));
|
|
127
|
+
const missing = specs.filter((_, i) => !ids[i]?.id);
|
|
128
|
+
const devtools = ownDevtools();
|
|
129
|
+
const plugins = ownPlugins();
|
|
130
|
+
if (missing.length || !devtools || !plugins) {
|
|
131
|
+
reason = missing.length ? `${missing.join(", ")} did not resolve from ${root}` : MISSING;
|
|
132
|
+
log?.(`[fluixi] reactive graph off — ${reason}`);
|
|
133
|
+
return "export {};";
|
|
134
|
+
}
|
|
135
|
+
return agentSource(`/@fs/${devtools}`, `/@fs/${plugins}`, ids[0].id, ids[1].id);
|
|
136
|
+
},
|
|
137
|
+
async configureServer(server) {
|
|
138
|
+
if (!enabled()) return;
|
|
139
|
+
root = server.config?.root ?? root;
|
|
140
|
+
log = server.config?.logger?.info.bind(server.config.logger);
|
|
141
|
+
wire = await import("@fluixi/devtools/wire").catch(() => null);
|
|
142
|
+
available = wire !== null;
|
|
143
|
+
if (wire) start();
|
|
144
|
+
if (wire) reason = WAITING;
|
|
145
|
+
else {
|
|
146
|
+
log?.(`[fluixi] reactive graph off — ${MISSING}`);
|
|
147
|
+
}
|
|
148
|
+
server.middlewares.use(ENDPOINT, (_req, res) => {
|
|
149
|
+
const snapshot = wire ? { ...wire.serializeGraph(graph, stamp || Date.now()), events } : { t: Date.now(), nodes: [] };
|
|
150
|
+
const body = JSON.stringify(reason ? { ...snapshot, reason } : snapshot);
|
|
151
|
+
res.setHeader("Content-Type", "application/json");
|
|
152
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
153
|
+
res.setHeader("Cache-Control", "no-store");
|
|
154
|
+
res.end(body);
|
|
155
|
+
});
|
|
156
|
+
if (!wire) return;
|
|
157
|
+
server.ws.on(EVENT, (payload) => {
|
|
158
|
+
const message = payload;
|
|
159
|
+
if (!message?.snapshot || !wire) return;
|
|
160
|
+
const full = message.kind === "full";
|
|
161
|
+
graph = wire.applySnapshot(full ? /* @__PURE__ */ new Map() : graph, message.snapshot);
|
|
162
|
+
if (full) events = [];
|
|
163
|
+
const arriving = message.snapshot.events;
|
|
164
|
+
if (arriving?.length) events = [...events, ...arriving].slice(-HISTORY);
|
|
165
|
+
stamp = Date.now();
|
|
166
|
+
reason = null;
|
|
167
|
+
});
|
|
168
|
+
},
|
|
169
|
+
transformIndexHtml: {
|
|
170
|
+
order: "pre",
|
|
171
|
+
handler(html, ctx) {
|
|
172
|
+
const inherited = callInheritedHtml(baseTransformIndexHtml, html, ctx);
|
|
173
|
+
if (!enabled() || !available || !wire) return inherited;
|
|
174
|
+
return {
|
|
175
|
+
html: typeof inherited === "string" ? inherited : html,
|
|
176
|
+
tags: [
|
|
177
|
+
{
|
|
178
|
+
tag: "script",
|
|
179
|
+
// First in the head, so the entry cannot create a node before the agent runs.
|
|
180
|
+
injectTo: "head-prepend",
|
|
181
|
+
attrs: { type: "module", src: `/@id/${VIRTUAL}` }
|
|
182
|
+
}
|
|
183
|
+
]
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function callInheritedHtml(hook, html, ctx) {
|
|
190
|
+
if (typeof hook === "function") return hook(html, ctx);
|
|
191
|
+
const handler = hook?.handler;
|
|
192
|
+
return handler?.(html, ctx);
|
|
193
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-channel.d.ts","sourceRoot":"","sources":["../src/graph-channel.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"graph-channel.d.ts","sourceRoot":"","sources":["../src/graph-channel.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAqGnC;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,GAAG,MAAM,CAyHhG"}
|
package/dist/graph-channel.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
//
|
|
3
3
|
// A dev-only agent module goes into the page ahead of the entry, so no node is created
|
|
4
4
|
// before something is watching. It pushes the graph over the channel Vite already keeps open
|
|
5
|
-
// for HMR, and the dev server holds the latest state and answers a plain GET
|
|
5
|
+
// for HMR, and the dev server holds the latest state and answers a plain GET, so an editor
|
|
6
6
|
// extension, a panel, or curl all read the same thing without speaking HMR.
|
|
7
7
|
//
|
|
8
8
|
// GET /__fluixi/graph → { t, nodes: [...] }
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
// the framework itself is resolved from the project, because watching a second copy of the
|
|
13
13
|
// runtime would watch a graph nobody is using.
|
|
14
14
|
import { createRequire } from 'node:module';
|
|
15
|
+
import { existsSync } from 'node:fs';
|
|
15
16
|
import { join } from 'node:path';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
16
18
|
const VIRTUAL = 'virtual:fluixi/devtools-agent';
|
|
17
19
|
const RESOLVED = `\0${VIRTUAL}`;
|
|
18
20
|
const ENDPOINT = '/__fluixi/graph';
|
|
@@ -31,7 +33,7 @@ import ${JSON.stringify(plugins)};
|
|
|
31
33
|
import { observeReactiveNodes } from ${JSON.stringify(signal)};
|
|
32
34
|
import { VERSION } from ${JSON.stringify(reactive)};
|
|
33
35
|
|
|
34
|
-
// The runtime this module imports is the application's own
|
|
36
|
+
// The runtime this module imports is the application's own, same module graph, same nodes.
|
|
35
37
|
// Nothing waits for the runtime to offer itself; that path is for a tool loaded from outside.
|
|
36
38
|
//
|
|
37
39
|
// \`watch\` is this copy of devtools, handed over with the runtime. A browser extension
|
|
@@ -62,20 +64,46 @@ import.meta.hot?.dispose(() => clearInterval(timer));
|
|
|
62
64
|
*
|
|
63
65
|
* Asking the application to install it as well made the graph depend on a devDependency
|
|
64
66
|
* nobody knew to add, and in a workspace it resolved for one project and not the one beside
|
|
65
|
-
* it
|
|
67
|
+
* it: which reads as the feature being broken rather than absent.
|
|
66
68
|
*/
|
|
67
69
|
function ownDevtools() {
|
|
70
|
+
return resolveForBrowser('@fluixi/devtools');
|
|
71
|
+
}
|
|
72
|
+
/** The built-in plugins, beside the bundle they extend. Collecting happens in the agent. */
|
|
73
|
+
function ownPlugins() {
|
|
74
|
+
return resolveForBrowser('@fluixi/devtools/plugins');
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* A package's ESM entry, by path.
|
|
78
|
+
*
|
|
79
|
+
* The agent below is served to a browser as an ES module and imports these by `/@fs/`
|
|
80
|
+
* path, so the file has to be the one with the named exports. `createRequire().resolve`
|
|
81
|
+
* answers with the `require` condition instead: the CJS build, and a `.cjs` file
|
|
82
|
+
* offers nothing to import, which surfaces as "does not provide an export named
|
|
83
|
+
* 'installDevtoolsHook'". It only started happening when devtools gained a CJS build;
|
|
84
|
+
* before that there was one file and either resolver found it.
|
|
85
|
+
*/
|
|
86
|
+
function resolveForBrowser(spec) {
|
|
87
|
+
// Honours the `import` condition, which is the one that matches how this is loaded.
|
|
68
88
|
try {
|
|
69
|
-
|
|
89
|
+
const resolved = import.meta.resolve?.(spec);
|
|
90
|
+
if (resolved)
|
|
91
|
+
return fileURLToPath(resolved);
|
|
70
92
|
}
|
|
71
93
|
catch {
|
|
72
|
-
|
|
94
|
+
// Fall through to the require-based resolution below.
|
|
73
95
|
}
|
|
74
|
-
}
|
|
75
|
-
/** The built-in plugins, beside the bundle they extend. Collecting happens in the agent. */
|
|
76
|
-
function ownPlugins() {
|
|
77
96
|
try {
|
|
78
|
-
|
|
97
|
+
const required = createRequire(import.meta.url).resolve(spec);
|
|
98
|
+
if (!required.endsWith('.cjs'))
|
|
99
|
+
return required;
|
|
100
|
+
// Landed on the CJS build. Prefer its ESM sibling when the package ships one.
|
|
101
|
+
for (const ext of ['.mjs', '.js']) {
|
|
102
|
+
const sibling = required.slice(0, -'.cjs'.length) + ext;
|
|
103
|
+
if (existsSync(sibling))
|
|
104
|
+
return sibling;
|
|
105
|
+
}
|
|
106
|
+
return required;
|
|
79
107
|
}
|
|
80
108
|
catch {
|
|
81
109
|
return null;
|
|
@@ -148,7 +176,7 @@ export function withGraphChannel(base, enabled, start) {
|
|
|
148
176
|
log?.(`[fluixi] reactive graph off — ${MISSING}`);
|
|
149
177
|
}
|
|
150
178
|
// The endpoint answers either way. A tool asking for the graph of a project that has
|
|
151
|
-
// not turned it on deserves to be told which of the two it is, and 404 says neither
|
|
179
|
+
// not turned it on deserves to be told which of the two it is, and 404 says neither ,
|
|
152
180
|
// it reads the same as a dev server that is not a fluixi project at all.
|
|
153
181
|
server.middlewares.use(ENDPOINT, (_req, res) => {
|
|
154
182
|
const snapshot = wire
|
package/dist/graph-channel.mjs
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
// src/graph-channel.ts
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
var VIRTUAL = "virtual:fluixi/devtools-agent";
|
|
7
|
+
var RESOLVED = `\0${VIRTUAL}`;
|
|
8
|
+
var ENDPOINT = "/__fluixi/graph";
|
|
9
|
+
var EVENT = "fluixi:graph";
|
|
10
|
+
var MISSING = "the plugin could not load @fluixi/devtools from its own dependencies";
|
|
11
|
+
var WAITING = "no page has reported yet — open the application in a browser";
|
|
12
|
+
var INTERVAL = 100;
|
|
13
|
+
var HISTORY = 500;
|
|
14
|
+
var agentSource = (devtools, plugins, signal, reactive) => `
|
|
15
|
+
import { installDevtoolsHook, observeGraph } from ${JSON.stringify(devtools)};
|
|
16
|
+
import ${JSON.stringify(plugins)};
|
|
17
|
+
import { observeReactiveNodes } from ${JSON.stringify(signal)};
|
|
18
|
+
import { VERSION } from ${JSON.stringify(reactive)};
|
|
6
19
|
|
|
7
|
-
// The runtime this module imports is the application's own
|
|
20
|
+
// The runtime this module imports is the application's own, same module graph, same nodes.
|
|
8
21
|
// Nothing waits for the runtime to offer itself; that path is for a tool loaded from outside.
|
|
9
22
|
//
|
|
10
23
|
// \`watch\` is this copy of devtools, handed over with the runtime. A browser extension
|
|
@@ -13,7 +26,7 @@ import { VERSION } from ${JSON.stringify(t)};
|
|
|
13
26
|
const hook = installDevtoolsHook();
|
|
14
27
|
hook.inject({ observeReactiveNodes, version: VERSION, watch: observeGraph });
|
|
15
28
|
|
|
16
|
-
const send = (kind, snapshot) => import.meta.hot?.send(${JSON.stringify(
|
|
29
|
+
const send = (kind, snapshot) => import.meta.hot?.send(${JSON.stringify(EVENT)}, { kind, snapshot });
|
|
17
30
|
|
|
18
31
|
const first = hook.snapshot();
|
|
19
32
|
// Kept and handed back, so a browser panel polling the same hook cannot consume what this
|
|
@@ -26,7 +39,123 @@ const timer = setInterval(() => {
|
|
|
26
39
|
cursor = delta.cursor;
|
|
27
40
|
// Events count on their own: an effect that ran without changing a value moves no node.
|
|
28
41
|
if (delta.nodes.length || delta.removed?.length || delta.events?.length) send('delta', delta);
|
|
29
|
-
}, ${
|
|
42
|
+
}, ${INTERVAL});
|
|
30
43
|
|
|
31
44
|
import.meta.hot?.dispose(() => clearInterval(timer));
|
|
32
|
-
`;
|
|
45
|
+
`;
|
|
46
|
+
function ownDevtools() {
|
|
47
|
+
return resolveForBrowser("@fluixi/devtools");
|
|
48
|
+
}
|
|
49
|
+
function ownPlugins() {
|
|
50
|
+
return resolveForBrowser("@fluixi/devtools/plugins");
|
|
51
|
+
}
|
|
52
|
+
function resolveForBrowser(spec) {
|
|
53
|
+
try {
|
|
54
|
+
const resolved = import.meta.resolve?.(spec);
|
|
55
|
+
if (resolved) return fileURLToPath(resolved);
|
|
56
|
+
} catch {
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const required = createRequire(import.meta.url).resolve(spec);
|
|
60
|
+
if (!required.endsWith(".cjs")) return required;
|
|
61
|
+
for (const ext of [".mjs", ".js"]) {
|
|
62
|
+
const sibling = required.slice(0, -".cjs".length) + ext;
|
|
63
|
+
if (existsSync(sibling)) return sibling;
|
|
64
|
+
}
|
|
65
|
+
return required;
|
|
66
|
+
} catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function withGraphChannel(base, enabled, start) {
|
|
71
|
+
let wire = null;
|
|
72
|
+
let available = false;
|
|
73
|
+
let reason = MISSING;
|
|
74
|
+
let root = process.cwd();
|
|
75
|
+
let log;
|
|
76
|
+
let graph = /* @__PURE__ */ new Map();
|
|
77
|
+
let stamp = 0;
|
|
78
|
+
let events = [];
|
|
79
|
+
const baseResolveId = base.resolveId;
|
|
80
|
+
const baseTransformIndexHtml = base.transformIndexHtml;
|
|
81
|
+
return {
|
|
82
|
+
...base,
|
|
83
|
+
resolveId(id, importer) {
|
|
84
|
+
if (id === VIRTUAL) return RESOLVED;
|
|
85
|
+
return baseResolveId?.call(this, id, importer) ?? null;
|
|
86
|
+
},
|
|
87
|
+
async load(id) {
|
|
88
|
+
if (id !== RESOLVED) return null;
|
|
89
|
+
const from = join(root, "fluixi-devtools-agent.js");
|
|
90
|
+
const specs = ["@fluixi/reactive/signal", "@fluixi/reactive"];
|
|
91
|
+
const ids = await Promise.all(specs.map((spec) => this.resolve?.(spec, from, { skipSelf: true })));
|
|
92
|
+
const missing = specs.filter((_, i) => !ids[i]?.id);
|
|
93
|
+
const devtools = ownDevtools();
|
|
94
|
+
const plugins = ownPlugins();
|
|
95
|
+
if (missing.length || !devtools || !plugins) {
|
|
96
|
+
reason = missing.length ? `${missing.join(", ")} did not resolve from ${root}` : MISSING;
|
|
97
|
+
log?.(`[fluixi] reactive graph off — ${reason}`);
|
|
98
|
+
return "export {};";
|
|
99
|
+
}
|
|
100
|
+
return agentSource(`/@fs/${devtools}`, `/@fs/${plugins}`, ids[0].id, ids[1].id);
|
|
101
|
+
},
|
|
102
|
+
async configureServer(server) {
|
|
103
|
+
if (!enabled()) return;
|
|
104
|
+
root = server.config?.root ?? root;
|
|
105
|
+
log = server.config?.logger?.info.bind(server.config.logger);
|
|
106
|
+
wire = await import("@fluixi/devtools/wire").catch(() => null);
|
|
107
|
+
available = wire !== null;
|
|
108
|
+
if (wire) start();
|
|
109
|
+
if (wire) reason = WAITING;
|
|
110
|
+
else {
|
|
111
|
+
log?.(`[fluixi] reactive graph off — ${MISSING}`);
|
|
112
|
+
}
|
|
113
|
+
server.middlewares.use(ENDPOINT, (_req, res) => {
|
|
114
|
+
const snapshot = wire ? { ...wire.serializeGraph(graph, stamp || Date.now()), events } : { t: Date.now(), nodes: [] };
|
|
115
|
+
const body = JSON.stringify(reason ? { ...snapshot, reason } : snapshot);
|
|
116
|
+
res.setHeader("Content-Type", "application/json");
|
|
117
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
118
|
+
res.setHeader("Cache-Control", "no-store");
|
|
119
|
+
res.end(body);
|
|
120
|
+
});
|
|
121
|
+
if (!wire) return;
|
|
122
|
+
server.ws.on(EVENT, (payload) => {
|
|
123
|
+
const message = payload;
|
|
124
|
+
if (!message?.snapshot || !wire) return;
|
|
125
|
+
const full = message.kind === "full";
|
|
126
|
+
graph = wire.applySnapshot(full ? /* @__PURE__ */ new Map() : graph, message.snapshot);
|
|
127
|
+
if (full) events = [];
|
|
128
|
+
const arriving = message.snapshot.events;
|
|
129
|
+
if (arriving?.length) events = [...events, ...arriving].slice(-HISTORY);
|
|
130
|
+
stamp = Date.now();
|
|
131
|
+
reason = null;
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
transformIndexHtml: {
|
|
135
|
+
order: "pre",
|
|
136
|
+
handler(html, ctx) {
|
|
137
|
+
const inherited = callInheritedHtml(baseTransformIndexHtml, html, ctx);
|
|
138
|
+
if (!enabled() || !available || !wire) return inherited;
|
|
139
|
+
return {
|
|
140
|
+
html: typeof inherited === "string" ? inherited : html,
|
|
141
|
+
tags: [
|
|
142
|
+
{
|
|
143
|
+
tag: "script",
|
|
144
|
+
// First in the head, so the entry cannot create a node before the agent runs.
|
|
145
|
+
injectTo: "head-prepend",
|
|
146
|
+
attrs: { type: "module", src: `/@id/${VIRTUAL}` }
|
|
147
|
+
}
|
|
148
|
+
]
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function callInheritedHtml(hook, html, ctx) {
|
|
155
|
+
if (typeof hook === "function") return hook(html, ctx);
|
|
156
|
+
const handler = hook?.handler;
|
|
157
|
+
return handler?.(html, ctx);
|
|
158
|
+
}
|
|
159
|
+
export {
|
|
160
|
+
withGraphChannel
|
|
161
|
+
};
|