praetom 0.2.4 → 0.3.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_active-channel.d.ts +28 -0
- package/dist/_active-channel.d.ts.map +1 -0
- package/dist/_active-channel.js +237 -0
- package/dist/_active-channel.js.map +1 -0
- package/dist/_esm-loader.d.ts +24 -26
- package/dist/_esm-loader.d.ts.map +1 -1
- package/dist/_esm-loader.js +48 -84
- package/dist/_esm-loader.js.map +1 -1
- package/dist/_internal.d.ts +67 -0
- package/dist/_internal.d.ts.map +1 -1
- package/dist/_internal.js +187 -1
- package/dist/_internal.js.map +1 -1
- package/dist/browser.d.ts +0 -4
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +0 -11
- package/dist/browser.js.map +1 -1
- package/dist/cli/cli.js +6 -1
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/commands/discover.d.ts +10 -1
- package/dist/cli/commands/discover.d.ts.map +1 -1
- package/dist/cli/commands/discover.js +40 -10
- package/dist/cli/commands/discover.js.map +1 -1
- package/dist/cli/commands/workspaces.d.ts +10 -0
- package/dist/cli/commands/workspaces.d.ts.map +1 -1
- package/dist/cli/commands/workspaces.js +46 -0
- package/dist/cli/commands/workspaces.js.map +1 -1
- package/dist/discover/ast-scan.d.ts +44 -0
- package/dist/discover/ast-scan.d.ts.map +1 -0
- package/dist/discover/ast-scan.js +331 -0
- package/dist/discover/ast-scan.js.map +1 -0
- package/dist/discover/ast-scan.test.d.ts +12 -0
- package/dist/discover/ast-scan.test.d.ts.map +1 -0
- package/dist/discover/ast-scan.test.js +188 -0
- package/dist/discover/ast-scan.test.js.map +1 -0
- package/dist/server.d.ts +10 -11
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +209 -91
- package/dist/server.js.map +1 -1
- package/dist/vite/index.d.ts +8 -0
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +65 -77
- package/dist/vite/index.js.map +1 -1
- package/dist/webpack/index.d.ts +13 -0
- package/dist/webpack/index.d.ts.map +1 -1
- package/dist/webpack/index.js +45 -4
- package/dist/webpack/index.js.map +1 -1
- package/dist/webpack/loader.d.ts +13 -8
- package/dist/webpack/loader.d.ts.map +1 -1
- package/dist/webpack/loader.js +59 -16
- package/dist/webpack/loader.js.map +1 -1
- package/dist/webpack/shared.d.ts +45 -1
- package/dist/webpack/shared.d.ts.map +1 -1
- package/dist/webpack/shared.js +227 -12
- package/dist/webpack/shared.js.map +1 -1
- package/package.json +15 -7
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live active-feature-map channel.
|
|
3
|
+
*
|
|
4
|
+
* Default transport is **SSE** over fetch (`text/event-stream`). The
|
|
5
|
+
* server pushes a `snapshot` event on connect and on every server-side
|
|
6
|
+
* change to the active set; each event carries the full active list,
|
|
7
|
+
* which we hand to `setActiveMap` so existing late-binding wraps pick
|
|
8
|
+
* up the new state on their next call.
|
|
9
|
+
*
|
|
10
|
+
* Edge / serverless environments (Vercel Edge, AWS Lambda, Cloudflare
|
|
11
|
+
* Workers) don't keep long-lived connections — we fall back to 30s
|
|
12
|
+
* polling on `/api/runtime/active`. The fallback also fires
|
|
13
|
+
* permanently after `MAX_RECONNECT_FAILURES` consecutive SSE
|
|
14
|
+
* connection failures.
|
|
15
|
+
*/
|
|
16
|
+
interface ChannelConfig {
|
|
17
|
+
configEndpoint: string;
|
|
18
|
+
ingestId: string;
|
|
19
|
+
warn: (msg: string) => void;
|
|
20
|
+
/** SDK version stamp; surfaces in the dashboard's live-instances panel. */
|
|
21
|
+
sdkVersion?: string;
|
|
22
|
+
/** Service identifier (heartbeat key); surfaces alongside SDK version. */
|
|
23
|
+
serviceName?: string | null;
|
|
24
|
+
}
|
|
25
|
+
export declare function startActiveChannel(cfg: ChannelConfig): void;
|
|
26
|
+
export declare function stopActiveChannel(): void;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=_active-channel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_active-channel.d.ts","sourceRoot":"","sources":["../src/_active-channel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAWH,UAAU,aAAa;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAYD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAU3D;AAED,wBAAgB,iBAAiB,IAAI,IAAI,CASxC"}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live active-feature-map channel.
|
|
3
|
+
*
|
|
4
|
+
* Default transport is **SSE** over fetch (`text/event-stream`). The
|
|
5
|
+
* server pushes a `snapshot` event on connect and on every server-side
|
|
6
|
+
* change to the active set; each event carries the full active list,
|
|
7
|
+
* which we hand to `setActiveMap` so existing late-binding wraps pick
|
|
8
|
+
* up the new state on their next call.
|
|
9
|
+
*
|
|
10
|
+
* Edge / serverless environments (Vercel Edge, AWS Lambda, Cloudflare
|
|
11
|
+
* Workers) don't keep long-lived connections — we fall back to 30s
|
|
12
|
+
* polling on `/api/runtime/active`. The fallback also fires
|
|
13
|
+
* permanently after `MAX_RECONNECT_FAILURES` consecutive SSE
|
|
14
|
+
* connection failures.
|
|
15
|
+
*/
|
|
16
|
+
import { setActiveMap } from "./_internal.js";
|
|
17
|
+
const POLL_INTERVAL_MS = 30_000;
|
|
18
|
+
const MAX_RECONNECT_FAILURES = 5;
|
|
19
|
+
const BACKOFF_BASE_MS = 1_000;
|
|
20
|
+
const BACKOFF_MAX_MS = 60_000;
|
|
21
|
+
let _abort = null;
|
|
22
|
+
let _pollTimer = null;
|
|
23
|
+
let _lastPollEtag = null;
|
|
24
|
+
let _reconnectFailures = 0;
|
|
25
|
+
export function startActiveChannel(cfg) {
|
|
26
|
+
stopActiveChannel(); // idempotent — register() can be called twice
|
|
27
|
+
if (shouldPollOnly()) {
|
|
28
|
+
cfg.warn("active-channel using polling (edge/serverless environment)");
|
|
29
|
+
startPolling(cfg);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
void connectSse(cfg);
|
|
33
|
+
}
|
|
34
|
+
export function stopActiveChannel() {
|
|
35
|
+
if (_abort) {
|
|
36
|
+
_abort.abort();
|
|
37
|
+
_abort = null;
|
|
38
|
+
}
|
|
39
|
+
if (_pollTimer) {
|
|
40
|
+
clearInterval(_pollTimer);
|
|
41
|
+
_pollTimer = null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Detect runtime contexts where SSE is a bad fit. Short-lived per-
|
|
46
|
+
* request invocations can't keep a connection open across requests, so
|
|
47
|
+
* they fall back to per-boot polling.
|
|
48
|
+
*/
|
|
49
|
+
function shouldPollOnly() {
|
|
50
|
+
const env = globalThis.process?.env ?? {};
|
|
51
|
+
if (typeof globalThis.EdgeRuntime !== "undefined") {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
if (env.AWS_LAMBDA_FUNCTION_NAME)
|
|
55
|
+
return true;
|
|
56
|
+
if (env.VERCEL && env.VERCEL_REGION) {
|
|
57
|
+
// Vercel serverless functions; long-running services on Vercel
|
|
58
|
+
// (rare for Node, common for Hono/Cloudflare) won't set
|
|
59
|
+
// VERCEL_REGION at runtime.
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
async function connectSse(cfg) {
|
|
65
|
+
while (true) {
|
|
66
|
+
const abort = new AbortController();
|
|
67
|
+
_abort = abort;
|
|
68
|
+
const url = activeStreamUrl(cfg.configEndpoint);
|
|
69
|
+
try {
|
|
70
|
+
const res = await fetch(url, {
|
|
71
|
+
method: "GET",
|
|
72
|
+
headers: identityHeaders(cfg, "text/event-stream"),
|
|
73
|
+
signal: abort.signal,
|
|
74
|
+
});
|
|
75
|
+
if (!res.ok || !res.body) {
|
|
76
|
+
throw new Error(`HTTP ${res.status}`);
|
|
77
|
+
}
|
|
78
|
+
_reconnectFailures = 0;
|
|
79
|
+
await consumeSseStream(res.body, cfg.warn);
|
|
80
|
+
// Stream ended cleanly (server closed). Reconnect after a short
|
|
81
|
+
// pause unless we're shutting down.
|
|
82
|
+
if (abort.signal.aborted)
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
if (abort.signal.aborted)
|
|
87
|
+
return;
|
|
88
|
+
_reconnectFailures += 1;
|
|
89
|
+
cfg.warn(`active-channel SSE error (${_reconnectFailures}/${MAX_RECONNECT_FAILURES}): ${e.message}`);
|
|
90
|
+
if (_reconnectFailures >= MAX_RECONNECT_FAILURES) {
|
|
91
|
+
cfg.warn(`active-channel falling back to polling after ${MAX_RECONNECT_FAILURES} consecutive failures`);
|
|
92
|
+
startPolling(cfg);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
await sleep(backoffMs(_reconnectFailures), _abort?.signal);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function consumeSseStream(body, warn) {
|
|
100
|
+
const reader = body.getReader();
|
|
101
|
+
const decoder = new TextDecoder();
|
|
102
|
+
let buffer = "";
|
|
103
|
+
try {
|
|
104
|
+
while (true) {
|
|
105
|
+
const { value, done } = await reader.read();
|
|
106
|
+
if (done)
|
|
107
|
+
return;
|
|
108
|
+
buffer += decoder.decode(value, { stream: true });
|
|
109
|
+
// SSE events are separated by blank lines (\n\n). Parse one at a time.
|
|
110
|
+
let idx;
|
|
111
|
+
while ((idx = buffer.indexOf("\n\n")) !== -1) {
|
|
112
|
+
const rawEvent = buffer.slice(0, idx);
|
|
113
|
+
buffer = buffer.slice(idx + 2);
|
|
114
|
+
const parsed = parseSseEvent(rawEvent);
|
|
115
|
+
if (!parsed)
|
|
116
|
+
continue;
|
|
117
|
+
if (parsed.event === "snapshot") {
|
|
118
|
+
try {
|
|
119
|
+
const payload = JSON.parse(parsed.data);
|
|
120
|
+
setActiveMap(payload.active);
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
warn(`active-channel: invalid snapshot payload: ${e.message}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
else if (parsed.event === "error") {
|
|
127
|
+
warn(`active-channel: server error event: ${parsed.data}`);
|
|
128
|
+
}
|
|
129
|
+
// 'heartbeat' (SSE comment) is dropped during parsing — see below.
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
try {
|
|
135
|
+
reader.releaseLock();
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// already released
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function parseSseEvent(raw) {
|
|
143
|
+
let event = "message";
|
|
144
|
+
const dataLines = [];
|
|
145
|
+
for (const line of raw.split("\n")) {
|
|
146
|
+
if (line.startsWith(":"))
|
|
147
|
+
continue; // SSE comment (heartbeats land here)
|
|
148
|
+
if (line.startsWith("event:")) {
|
|
149
|
+
event = line.slice(6).trim();
|
|
150
|
+
}
|
|
151
|
+
else if (line.startsWith("data:")) {
|
|
152
|
+
dataLines.push(line.slice(5).replace(/^ /, ""));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (dataLines.length === 0 && event === "message")
|
|
156
|
+
return null;
|
|
157
|
+
return { event, data: dataLines.join("\n") };
|
|
158
|
+
}
|
|
159
|
+
function startPolling(cfg) {
|
|
160
|
+
// Fire immediately so initial state lands before the first interval.
|
|
161
|
+
void pollOnce(cfg);
|
|
162
|
+
_pollTimer = setInterval(() => {
|
|
163
|
+
void pollOnce(cfg);
|
|
164
|
+
}, POLL_INTERVAL_MS);
|
|
165
|
+
if (typeof _pollTimer.unref === "function") {
|
|
166
|
+
_pollTimer.unref();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
async function pollOnce(cfg) {
|
|
170
|
+
const url = activeSnapshotUrl(cfg.configEndpoint);
|
|
171
|
+
try {
|
|
172
|
+
const headers = identityHeaders(cfg, "application/json");
|
|
173
|
+
if (_lastPollEtag)
|
|
174
|
+
headers["if-none-match"] = _lastPollEtag;
|
|
175
|
+
const res = await fetch(url, { headers });
|
|
176
|
+
if (res.status === 304)
|
|
177
|
+
return;
|
|
178
|
+
if (!res.ok) {
|
|
179
|
+
cfg.warn(`active-channel poll: HTTP ${res.status}`);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
_lastPollEtag = res.headers.get("etag");
|
|
183
|
+
const payload = (await res.json());
|
|
184
|
+
setActiveMap(payload.active);
|
|
185
|
+
}
|
|
186
|
+
catch (e) {
|
|
187
|
+
cfg.warn(`active-channel poll error: ${e.message}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Standard request identity headers — surface the SDK + Node version
|
|
192
|
+
* and the service name so the dashboard's live-instances panel can
|
|
193
|
+
* group connections meaningfully.
|
|
194
|
+
*/
|
|
195
|
+
function identityHeaders(cfg, accept) {
|
|
196
|
+
const headers = {
|
|
197
|
+
authorization: `Bearer ${cfg.ingestId}`,
|
|
198
|
+
accept,
|
|
199
|
+
};
|
|
200
|
+
if (cfg.sdkVersion)
|
|
201
|
+
headers["x-praetom-sdk-version"] = cfg.sdkVersion;
|
|
202
|
+
const nodeVersion = globalThis
|
|
203
|
+
.process?.versions?.node;
|
|
204
|
+
if (nodeVersion)
|
|
205
|
+
headers["x-praetom-node-version"] = nodeVersion;
|
|
206
|
+
if (cfg.serviceName)
|
|
207
|
+
headers["x-praetom-service"] = cfg.serviceName;
|
|
208
|
+
return headers;
|
|
209
|
+
}
|
|
210
|
+
function activeStreamUrl(configEndpoint) {
|
|
211
|
+
// Replace `/runtime/config` → `/runtime/active/stream` so customers
|
|
212
|
+
// overriding the endpoint keep their override semantics.
|
|
213
|
+
return configEndpoint.replace(/\/config(?:\/?$|\?)/, "/active/stream$1");
|
|
214
|
+
}
|
|
215
|
+
function activeSnapshotUrl(configEndpoint) {
|
|
216
|
+
return configEndpoint.replace(/\/config(?:\/?$|\?)/, "/active$1");
|
|
217
|
+
}
|
|
218
|
+
function backoffMs(attempt) {
|
|
219
|
+
const exp = Math.min(BACKOFF_MAX_MS, BACKOFF_BASE_MS * 2 ** attempt);
|
|
220
|
+
// Add ±25% jitter to avoid thundering herd on widespread server restarts.
|
|
221
|
+
return Math.floor(exp * (0.75 + Math.random() * 0.5));
|
|
222
|
+
}
|
|
223
|
+
function sleep(ms, signal) {
|
|
224
|
+
return new Promise((resolve) => {
|
|
225
|
+
if (signal?.aborted)
|
|
226
|
+
return resolve();
|
|
227
|
+
const t = setTimeout(resolve, ms);
|
|
228
|
+
if (typeof t.unref === "function") {
|
|
229
|
+
t.unref();
|
|
230
|
+
}
|
|
231
|
+
signal?.addEventListener("abort", () => {
|
|
232
|
+
clearTimeout(t);
|
|
233
|
+
resolve();
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=_active-channel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_active-channel.js","sourceRoot":"","sources":["../src/_active-channel.ts"],"names":[],"mappings":"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"}
|
package/dist/_esm-loader.d.ts
CHANGED
|
@@ -4,36 +4,34 @@
|
|
|
4
4
|
* (Express/Fastify/Hono/Nest with `.mjs`/`.ts`), Bun, and pre-bundle
|
|
5
5
|
* dev mode for several frameworks.
|
|
6
6
|
*
|
|
7
|
-
* Registered via `module.register('./dist/_esm-loader.js',
|
|
8
|
-
* { data: {
|
|
7
|
+
* Registered via `module.register('./dist/_esm-loader.js',
|
|
8
|
+
* import.meta.url, { data: { eligibility: [...] } })` from server.ts's
|
|
9
|
+
* installEsmLoader().
|
|
9
10
|
*
|
|
10
|
-
* Strategy:
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* `
|
|
14
|
-
* warning) for shapes that don't match.
|
|
11
|
+
* Strategy: source rewriting on files matching an *eligibility* entry.
|
|
12
|
+
* The rewrite emits late-binding wraps (`__praetomLateBindWrap`) — no
|
|
13
|
+
* slug is baked in. At call time, the wrap reads the shared
|
|
14
|
+
* `activeMap` on globalThis and decides whether to emit a span.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* export function NAME(...)
|
|
19
|
-
* export const NAME = async (...)
|
|
20
|
-
* export const NAME = (...)
|
|
21
|
-
*
|
|
22
|
-
* Does NOT handle (yet):
|
|
23
|
-
* export default ...
|
|
24
|
-
* export { NAME }
|
|
25
|
-
* export { NAME as default }
|
|
26
|
-
* class exports
|
|
27
|
-
*
|
|
28
|
-
* The bundler plugins (Vite/Webpack) cover the cases this loader skips
|
|
29
|
-
* — bundled apps go through the plugin, not this loader.
|
|
16
|
+
* The loader appends an inline `//# sourceMappingURL=data:` so stack
|
|
17
|
+
* traces stay aligned to the original lines after the rewrite.
|
|
30
18
|
*/
|
|
31
|
-
|
|
32
|
-
slug: string;
|
|
33
|
-
entrySymbol?: string;
|
|
34
|
-
}
|
|
19
|
+
import type { MessagePort } from "node:worker_threads";
|
|
35
20
|
interface InitData {
|
|
36
|
-
|
|
21
|
+
eligibility: Array<[string, ReadonlyArray<{
|
|
22
|
+
exportName: string;
|
|
23
|
+
}>]>;
|
|
24
|
+
/**
|
|
25
|
+
* Optional MessagePort the main thread uses to push eligibility
|
|
26
|
+
* updates after boot. When provided, the loader listens for messages
|
|
27
|
+
* shaped `{ type: "eligibility", eligibility: [...] }` and swaps the
|
|
28
|
+
* in-memory map. When omitted, eligibility is frozen at boot
|
|
29
|
+
* (back-compat with pre-hot-pickup SDK builds).
|
|
30
|
+
*
|
|
31
|
+
* Already-loaded modules keep their boot-time wrap (or lack of wrap)
|
|
32
|
+
* — the loader only runs on `load()` for fresh imports.
|
|
33
|
+
*/
|
|
34
|
+
port?: MessagePort;
|
|
37
35
|
}
|
|
38
36
|
interface LoadResult {
|
|
39
37
|
format: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_esm-loader.d.ts","sourceRoot":"","sources":["../src/_esm-loader.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"_esm-loader.d.ts","sourceRoot":"","sources":["../src/_esm-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AASvD,UAAU,QAAQ;IAChB,WAAW,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,aAAa,CAAC;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC,CAAC;IACpE;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB;AAOD,UAAU,UAAU;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,eAAe,CAAC;IAChD,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,KAAK,QAAQ,GAAG,CACd,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,OAAO,KACb,OAAO,CAAC,UAAU,CAAC,CAAC;AAKzB,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAiB/C;AAgBD,wBAAsB,IAAI,CACxB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtD,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,UAAU,CAAC,CA+CrB"}
|
package/dist/_esm-loader.js
CHANGED
|
@@ -4,35 +4,48 @@
|
|
|
4
4
|
* (Express/Fastify/Hono/Nest with `.mjs`/`.ts`), Bun, and pre-bundle
|
|
5
5
|
* dev mode for several frameworks.
|
|
6
6
|
*
|
|
7
|
-
* Registered via `module.register('./dist/_esm-loader.js',
|
|
8
|
-
* { data: {
|
|
7
|
+
* Registered via `module.register('./dist/_esm-loader.js',
|
|
8
|
+
* import.meta.url, { data: { eligibility: [...] } })` from server.ts's
|
|
9
|
+
* installEsmLoader().
|
|
9
10
|
*
|
|
10
|
-
* Strategy:
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* `
|
|
14
|
-
* warning) for shapes that don't match.
|
|
11
|
+
* Strategy: source rewriting on files matching an *eligibility* entry.
|
|
12
|
+
* The rewrite emits late-binding wraps (`__praetomLateBindWrap`) — no
|
|
13
|
+
* slug is baked in. At call time, the wrap reads the shared
|
|
14
|
+
* `activeMap` on globalThis and decides whether to emit a span.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* export function NAME(...)
|
|
19
|
-
* export const NAME = async (...)
|
|
20
|
-
* export const NAME = (...)
|
|
21
|
-
*
|
|
22
|
-
* Does NOT handle (yet):
|
|
23
|
-
* export default ...
|
|
24
|
-
* export { NAME }
|
|
25
|
-
* export { NAME as default }
|
|
26
|
-
* class exports
|
|
27
|
-
*
|
|
28
|
-
* The bundler plugins (Vite/Webpack) cover the cases this loader skips
|
|
29
|
-
* — bundled apps go through the plugin, not this loader.
|
|
16
|
+
* The loader appends an inline `//# sourceMappingURL=data:` so stack
|
|
17
|
+
* traces stay aligned to the original lines after the rewrite.
|
|
30
18
|
*/
|
|
31
|
-
|
|
19
|
+
import { wrapModuleSourceForLateBinding } from "./webpack/shared";
|
|
20
|
+
let _eligibility = new Map();
|
|
32
21
|
let _initialized = false;
|
|
33
22
|
export function initialize(data) {
|
|
34
|
-
|
|
23
|
+
applyEligibility(data.eligibility);
|
|
35
24
|
_initialized = true;
|
|
25
|
+
// Subscribe to live updates from the main thread. Once the loader's
|
|
26
|
+
// map is replaced, every subsequent `load()` call wraps against the
|
|
27
|
+
// fresh eligibility set — same semantics as the CJS hook's runtime-
|
|
28
|
+
// re-read of mapByPath.
|
|
29
|
+
if (data.port) {
|
|
30
|
+
data.port.on("message", (msg) => {
|
|
31
|
+
if (!isEligibilityUpdate(msg))
|
|
32
|
+
return;
|
|
33
|
+
applyEligibility(msg.eligibility);
|
|
34
|
+
});
|
|
35
|
+
// Don't let the loader's port keep the process alive past its
|
|
36
|
+
// real work.
|
|
37
|
+
if (typeof data.port.unref === "function")
|
|
38
|
+
data.port.unref();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function applyEligibility(next) {
|
|
42
|
+
_eligibility = new Map(next.map(([path, exports]) => [path, { exports: [...exports] }]));
|
|
43
|
+
}
|
|
44
|
+
function isEligibilityUpdate(msg) {
|
|
45
|
+
if (!msg || typeof msg !== "object")
|
|
46
|
+
return false;
|
|
47
|
+
const m = msg;
|
|
48
|
+
return m.type === "eligibility" && Array.isArray(m.eligibility);
|
|
36
49
|
}
|
|
37
50
|
export async function load(url, context, nextLoad) {
|
|
38
51
|
const result = await nextLoad(url, context);
|
|
@@ -49,7 +62,7 @@ export async function load(url, context, nextLoad) {
|
|
|
49
62
|
catch {
|
|
50
63
|
return result;
|
|
51
64
|
}
|
|
52
|
-
const match =
|
|
65
|
+
const match = matchEligibility(filePath);
|
|
53
66
|
if (!match)
|
|
54
67
|
return result;
|
|
55
68
|
const source = typeof result.source === "string"
|
|
@@ -59,76 +72,27 @@ export async function load(url, context, nextLoad) {
|
|
|
59
72
|
? new Uint8Array(result.source)
|
|
60
73
|
: result.source)
|
|
61
74
|
: "";
|
|
62
|
-
const wrapped =
|
|
75
|
+
const wrapped = wrapModuleSourceForLateBinding(source, match.featurePath, match.file.exports.map((e) => e.exportName), filePath);
|
|
63
76
|
if (!wrapped) {
|
|
64
77
|
// eslint-disable-next-line no-console
|
|
65
78
|
console.warn(`[praetom] could not wrap ${filePath} (export shape not recognized; falling back to no-op)`);
|
|
66
79
|
return result;
|
|
67
80
|
}
|
|
68
|
-
|
|
81
|
+
const mapJson = wrapped.map.toString();
|
|
82
|
+
const inlined = Buffer.from(mapJson, "utf8").toString("base64");
|
|
83
|
+
const sourceWithMap = wrapped.code + `\n//# sourceMappingURL=data:application/json;base64,${inlined}\n`;
|
|
84
|
+
return { ...result, source: sourceWithMap };
|
|
69
85
|
}
|
|
70
|
-
function
|
|
86
|
+
function matchEligibility(absPath) {
|
|
71
87
|
const normalized = absPath.replace(/\\/g, "/");
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
// segment featurePaths are skipped (ambiguous).
|
|
88
|
+
const direct = _eligibility.get(normalized);
|
|
89
|
+
if (direct)
|
|
90
|
+
return { featurePath: normalized, file: direct };
|
|
91
|
+
for (const [featurePath, file] of _eligibility) {
|
|
77
92
|
if (featurePath.includes("/") && normalized.endsWith(`/${featurePath}`)) {
|
|
78
|
-
return
|
|
93
|
+
return { featurePath, file };
|
|
79
94
|
}
|
|
80
95
|
}
|
|
81
96
|
return null;
|
|
82
97
|
}
|
|
83
|
-
const PRAETOM_IMPORT_MARKER = "__praetom_wrap_import__";
|
|
84
|
-
const HTTP_METHODS = new Set([
|
|
85
|
-
"GET",
|
|
86
|
-
"POST",
|
|
87
|
-
"PUT",
|
|
88
|
-
"PATCH",
|
|
89
|
-
"DELETE",
|
|
90
|
-
"OPTIONS",
|
|
91
|
-
"HEAD",
|
|
92
|
-
]);
|
|
93
|
-
function wrapModuleSource(source, slug, entrySymbol) {
|
|
94
|
-
if (source.includes(PRAETOM_IMPORT_MARKER)) {
|
|
95
|
-
// Already wrapped (shouldn't happen with nextLoad short-circuit but
|
|
96
|
-
// belt + suspenders).
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
// Pattern A: `export (async )?function NAME(` — rewrite the export
|
|
100
|
-
// keyword off, rename the function to __praetom_orig_NAME, and
|
|
101
|
-
// append an `export const NAME = __wrap(...)`.
|
|
102
|
-
// Pattern B: `export const NAME = ` (function value) — same shape.
|
|
103
|
-
const targets = new Set();
|
|
104
|
-
let rewritten = source.replace(/^export\s+(async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(/gm, (_match, asyncKw, name) => {
|
|
105
|
-
targets.add(name);
|
|
106
|
-
return `${asyncKw ?? ""}function __praetom_orig_${name}(`;
|
|
107
|
-
});
|
|
108
|
-
rewritten = rewritten.replace(/^export\s+const\s+([A-Za-z_$][\w$]*)\s*=/gm, (_match, name) => {
|
|
109
|
-
// Only wrap if the entry name is the requested entrySymbol (when
|
|
110
|
-
// specified) or an HTTP method (Next.js App Router convention).
|
|
111
|
-
// Otherwise leave alone — `export const foo = "bar"` shouldn't
|
|
112
|
-
// become `wrapEntryPoint(..., "bar")`.
|
|
113
|
-
if (!shouldWrap(name, entrySymbol))
|
|
114
|
-
return `export const ${name} =`;
|
|
115
|
-
targets.add(name);
|
|
116
|
-
return `const __praetom_orig_${name} =`;
|
|
117
|
-
});
|
|
118
|
-
if (targets.size === 0)
|
|
119
|
-
return null;
|
|
120
|
-
const filtered = [...targets].filter((n) => shouldWrap(n, entrySymbol));
|
|
121
|
-
if (filtered.length === 0)
|
|
122
|
-
return null;
|
|
123
|
-
const importLine = `import { __praetomWrapEntryPoint as ${PRAETOM_IMPORT_MARKER} } from "praetom";\n`;
|
|
124
|
-
const wraps = filtered
|
|
125
|
-
.map((name) => `export const ${name} = ${PRAETOM_IMPORT_MARKER}(${JSON.stringify(slug)}, ${JSON.stringify(name)}, __praetom_orig_${name});`)
|
|
126
|
-
.join("\n");
|
|
127
|
-
return `${importLine}${rewritten}\n${wraps}\n`;
|
|
128
|
-
}
|
|
129
|
-
function shouldWrap(name, entrySymbol) {
|
|
130
|
-
if (entrySymbol)
|
|
131
|
-
return name === entrySymbol;
|
|
132
|
-
return HTTP_METHODS.has(name) || name === "handler" || name === "default";
|
|
133
|
-
}
|
|
134
98
|
//# sourceMappingURL=_esm-loader.js.map
|
package/dist/_esm-loader.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_esm-loader.js","sourceRoot":"","sources":["../src/_esm-loader.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"_esm-loader.js","sourceRoot":"","sources":["../src/_esm-loader.ts"],"names":[],"mappings":"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"}
|
package/dist/_internal.d.ts
CHANGED
|
@@ -47,6 +47,37 @@ export declare function currentFeatureContext(): FeatureContext | undefined;
|
|
|
47
47
|
* bundler-plugin-injected code can call it; customers should never
|
|
48
48
|
* import it directly.
|
|
49
49
|
*/
|
|
50
|
+
/**
|
|
51
|
+
* Late-binding wrap. Installed once at module-load time for every
|
|
52
|
+
* `(path, exportName)` pair the server declares as eligible. The wrap
|
|
53
|
+
* defers the slug decision to call time: each invocation reads the
|
|
54
|
+
* shared `activeMap` (mutated by the active-channel client when the
|
|
55
|
+
* dashboard adds/removes features) and either:
|
|
56
|
+
* - finds an entry for `path:exportName` → feature-wraps with that slug, or
|
|
57
|
+
* - finds nothing → passes straight through to the original.
|
|
58
|
+
*
|
|
59
|
+
* The passthrough path adds one `Map.get(string)` per call (~tens of
|
|
60
|
+
* ns). The active path is identical cost to a normal `wrapEntryPoint`.
|
|
61
|
+
*
|
|
62
|
+
* Preserves `.name`, `.length`, and `Function.prototype.toString` so
|
|
63
|
+
* frameworks that introspect function shape (Express middleware's
|
|
64
|
+
* length-based handler detection, NestJS decorators, etc.) keep
|
|
65
|
+
* working.
|
|
66
|
+
*/
|
|
67
|
+
export declare function lateBindWrap(path: string, exportName: string, original: (...args: unknown[]) => unknown): (...args: unknown[]) => unknown;
|
|
68
|
+
/**
|
|
69
|
+
* Snapshot-mode update: replace the active map wholesale. Called by
|
|
70
|
+
* the active-channel client on every snapshot event (SSE or poll).
|
|
71
|
+
* Cheap — single Map mutation, no allocation per entry beyond the
|
|
72
|
+
* source iteration.
|
|
73
|
+
*/
|
|
74
|
+
export declare function setActiveMap(entries: ReadonlyArray<{
|
|
75
|
+
path: string;
|
|
76
|
+
exportName: string;
|
|
77
|
+
slug: string;
|
|
78
|
+
}>): void;
|
|
79
|
+
/** Read-only access for diagnostic / test code. */
|
|
80
|
+
export declare function getActiveMapSize(): number;
|
|
50
81
|
export declare function wrapEntryPoint(slug: string, entryName: string, original: (...args: unknown[]) => unknown): (...args: unknown[]) => unknown;
|
|
51
82
|
/**
|
|
52
83
|
* Public primitive — fire a point-in-time event tagged with the current
|
|
@@ -54,8 +85,44 @@ export declare function wrapEntryPoint(slug: string, entryName: string, original
|
|
|
54
85
|
* latency splits, business events, or error context that the entry-
|
|
55
86
|
* point wrap doesn't capture by itself.
|
|
56
87
|
*
|
|
88
|
+
* ## Context propagation (Node.js)
|
|
89
|
+
*
|
|
90
|
+
* `emit()` discovers the current feature via Node's `AsyncLocalStorage`.
|
|
91
|
+
* That means it works **only inside the async causality chain started
|
|
92
|
+
* by a wrapped entry point**:
|
|
93
|
+
*
|
|
94
|
+
* - ✅ awaited code paths inside the handler (`await db.query(...)` →
|
|
95
|
+
* subsequent `emit()`)
|
|
96
|
+
* - ✅ `setTimeout` / `setImmediate` / `queueMicrotask` callbacks
|
|
97
|
+
* scheduled inside the handler — they inherit the ALS store
|
|
98
|
+
* - ✅ Promise chains that fork off the handler's call stack
|
|
99
|
+
* - ❌ `EventEmitter` listeners attached *outside* the handler and
|
|
100
|
+
* fired *from* it — listeners run in the caller's ALS scope, not
|
|
101
|
+
* the emitter's. Wrap listener attachment inside the handler (or
|
|
102
|
+
* pass the slug explicitly via a closure) to keep context.
|
|
103
|
+
* - ❌ Code executed from a **background worker / cron / queue
|
|
104
|
+
* consumer** that wasn't itself wrapped. There's no entry-point
|
|
105
|
+
* wrap, so there's no feature context. The `emit()` still records
|
|
106
|
+
* as a root span with no parent, but won't aggregate into any
|
|
107
|
+
* feature's signal.
|
|
108
|
+
* - ❌ Code that escapes the request via a detached `setImmediate`
|
|
109
|
+
* chain where you intentionally `als.exit()` (unusual; if you're
|
|
110
|
+
* doing this you already know).
|
|
111
|
+
*
|
|
112
|
+
* If you need feature context on a background path, wrap the background
|
|
113
|
+
* worker's entry point itself (the bundler plugin handles this when
|
|
114
|
+
* you point a feature contract at the worker's `path`).
|
|
115
|
+
*
|
|
57
116
|
* Outside a feature context the span still emits, but with no parent —
|
|
58
117
|
* use sparingly for top-level signals.
|
|
118
|
+
*
|
|
119
|
+
* ## Browser
|
|
120
|
+
*
|
|
121
|
+
* Browser builds don't use ALS (it doesn't exist in the browser). The
|
|
122
|
+
* current feature is read from a per-tab stack maintained by
|
|
123
|
+
* `__praetomWrapEntryPoint` — same shape, different mechanism. Same
|
|
124
|
+
* gotchas: `emit()` inside an event listener attached outside the wrap
|
|
125
|
+
* won't pick up the feature.
|
|
59
126
|
*/
|
|
60
127
|
export declare function emit(eventName: string, payload?: Record<string, unknown>): void;
|
|
61
128
|
export declare function flush(): Promise<void>;
|
package/dist/_internal.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_internal.d.ts","sourceRoot":"","sources":["../src/_internal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA4CH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;
|
|
1
|
+
{"version":3,"file":"_internal.d.ts","sourceRoot":"","sources":["../src/_internal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA4CH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAgDD,wBAAgB,IAAI,CAAC,IAAI,EAAE;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,IAAI,CAOP;AAED;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,CAAC,EAC7B,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAClC,OAAO,CAAC,CAAC,CAAC,CAOZ;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAiB3D;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,CAAC,EACnC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG;IAAE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;CAAE,EAC3F,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAClC,OAAO,CAAC,CAAC,CAAC,CAOZ;AAqBD,wBAAgB,qBAAqB,IAAI,cAAc,GAAG,SAAS,CAElE;AAYD;;;;;;;;;;;GAWG;AACH;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,GACxC,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CA0BjC;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GACzE,IAAI,CAKN;AAED,mDAAmD;AACnD,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AA6CD,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,GACxC,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CA6BjC;AA8CD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,IAAI,CAClB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,IAAI,CAgBN;AAqCD,wBAAsB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAE3C;AAED,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAM9C"}
|