1tube 0.1.21 → 0.1.23
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/deno.json +1 -0
- package/deno.lock +41 -0
- package/package.json +1 -1
- package/src/backends/deno/dep-graph.ts +233 -0
- package/src/backends/deno/hot-reloader.ts +304 -0
- package/src/backends/deno/worker-entry.ts +202 -0
- package/src/backends/deno/worker-host.ts +466 -0
- package/src/discovery.ts +10 -31
- package/src/registry.ts +55 -2
- package/src/server.ts +230 -577
- package/src/deno-hmr-snapshot.ts +0 -159
package/src/discovery.ts
CHANGED
|
@@ -28,13 +28,6 @@ export interface DiscoveryProgress {
|
|
|
28
28
|
export interface DiscoveryOptions {
|
|
29
29
|
/** When set, appended as `?v=` to module URLs to force re-transpilation. */
|
|
30
30
|
cacheBust?: string;
|
|
31
|
-
/**
|
|
32
|
-
* Optional alternate root for dynamic imports. Discovery and manifests still
|
|
33
|
-
* read from `functionsDir`; only module URLs are resolved under this root.
|
|
34
|
-
* Used by Deno-backend HMR snapshots so transitive relative imports get a
|
|
35
|
-
* fresh file URL namespace.
|
|
36
|
-
*/
|
|
37
|
-
importRoot?: string;
|
|
38
31
|
/** Restrict loading to this set of function names (others are skipped entirely). */
|
|
39
32
|
only?: ReadonlySet<string>;
|
|
40
33
|
/** Max number of concurrent dynamic imports. Defaults to 8. */
|
|
@@ -80,9 +73,6 @@ export async function discoverAndLoad(
|
|
|
80
73
|
const errors: Array<{ name: string; error: string }> = [];
|
|
81
74
|
|
|
82
75
|
const resolvedDir = await Deno.realPath(functionsDir);
|
|
83
|
-
const importRoot = options?.importRoot
|
|
84
|
-
? await Deno.realPath(options.importRoot)
|
|
85
|
-
: resolvedDir;
|
|
86
76
|
|
|
87
77
|
const candidates: string[] = [];
|
|
88
78
|
for await (const entry of Deno.readDir(resolvedDir)) {
|
|
@@ -131,7 +121,7 @@ export async function discoverAndLoad(
|
|
|
131
121
|
let completed = 0;
|
|
132
122
|
|
|
133
123
|
const loadOne = async (name: string): Promise<void> => {
|
|
134
|
-
const indexPath = join(
|
|
124
|
+
const indexPath = join(resolvedDir, name, "index.ts");
|
|
135
125
|
// pathToFileURL is the node:url equivalent of std/path's toFileUrl;
|
|
136
126
|
// both produce a `file://` URL whose searchParams we mutate below for
|
|
137
127
|
// HMR cache-busting.
|
|
@@ -153,11 +143,7 @@ export async function discoverAndLoad(
|
|
|
153
143
|
// on demand. This is the single biggest startup-time win for sites
|
|
154
144
|
// with many rarely-called functions.
|
|
155
145
|
if (options?.lazy && !manifest.warm) {
|
|
156
|
-
registry.registerCandidate({
|
|
157
|
-
name,
|
|
158
|
-
moduleUrl: moduleUrl.href,
|
|
159
|
-
manifest,
|
|
160
|
-
});
|
|
146
|
+
registry.registerCandidate({ name, moduleUrl: moduleUrl.href, manifest });
|
|
161
147
|
deferred.push(name);
|
|
162
148
|
const durationMs = performance.now() - start;
|
|
163
149
|
options?.onProgress?.({
|
|
@@ -174,11 +160,7 @@ export async function discoverAndLoad(
|
|
|
174
160
|
// Even in lazy mode, eagerly-warm functions still register a candidate
|
|
175
161
|
// so HMR reloads can re-import via the same URL.
|
|
176
162
|
if (options?.lazy && manifest.warm) {
|
|
177
|
-
registry.registerCandidate({
|
|
178
|
-
name,
|
|
179
|
-
moduleUrl: moduleUrl.href,
|
|
180
|
-
manifest,
|
|
181
|
-
});
|
|
163
|
+
registry.registerCandidate({ name, moduleUrl: moduleUrl.href, manifest });
|
|
182
164
|
}
|
|
183
165
|
await registry.runWithCurrentFunction(name, async () => {
|
|
184
166
|
await import(moduleUrl.href);
|
|
@@ -211,16 +193,13 @@ export async function discoverAndLoad(
|
|
|
211
193
|
// internally, but we still benefit from overlapping transpile + JSR fetches.
|
|
212
194
|
const concurrency = Math.max(1, options?.concurrency ?? 8);
|
|
213
195
|
let cursor = 0;
|
|
214
|
-
const workers = Array.from(
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
},
|
|
223
|
-
);
|
|
196
|
+
const workers = Array.from({ length: Math.min(concurrency, work.length) }, async () => {
|
|
197
|
+
while (true) {
|
|
198
|
+
const i = cursor++;
|
|
199
|
+
if (i >= work.length) return;
|
|
200
|
+
await loadOne(work[i]);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
224
203
|
await Promise.all(workers);
|
|
225
204
|
|
|
226
205
|
return { loaded, skipped, errors, removed, deferred };
|
package/src/registry.ts
CHANGED
|
@@ -77,6 +77,29 @@ export interface FunctionCandidate {
|
|
|
77
77
|
manifest: FunctionManifest;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Out-of-process function handle used by the Worker-backed Deno backend.
|
|
82
|
+
* The gateway holds a `WorkerFunctionHandle` instead of a JS handler;
|
|
83
|
+
* `dispatch()` forwards the request through `postMessage` to the
|
|
84
|
+
* function's Web Worker.
|
|
85
|
+
*
|
|
86
|
+
* Defined here (rather than in `backends/deno/`) so the registry can
|
|
87
|
+
* type its `workerHandles` map without a circular import. The actual
|
|
88
|
+
* implementation lives in `src/backends/deno/worker-host.ts`.
|
|
89
|
+
*/
|
|
90
|
+
export interface WorkerFunctionHandle {
|
|
91
|
+
readonly name: string;
|
|
92
|
+
readonly manifest: FunctionManifest;
|
|
93
|
+
readonly isPublic: boolean;
|
|
94
|
+
readonly timeoutMs?: number;
|
|
95
|
+
dispatch(
|
|
96
|
+
req: Request,
|
|
97
|
+
auth: AuthContext | null,
|
|
98
|
+
signal: AbortSignal,
|
|
99
|
+
): Promise<Response>;
|
|
100
|
+
terminate(): Promise<void>;
|
|
101
|
+
}
|
|
102
|
+
|
|
80
103
|
export class FunctionRegistry {
|
|
81
104
|
private handlers = new Map<string, RegisteredFunction>();
|
|
82
105
|
private pendingManifests = new Map<string, FunctionManifest>();
|
|
@@ -91,6 +114,14 @@ export class FunctionRegistry {
|
|
|
91
114
|
* a JS handler exists.
|
|
92
115
|
*/
|
|
93
116
|
private externalManifests = new Map<string, FunctionManifest>();
|
|
117
|
+
/**
|
|
118
|
+
* Out-of-process worker handles for the Deno backend's Worker-per-function
|
|
119
|
+
* execution model. Treated as live, dispatchable functions: `has()` /
|
|
120
|
+
* `knownNames()` / `manifestFor()` all surface them, but they live in a
|
|
121
|
+
* separate Map so the in-process `handlers` / `candidates` paths used by
|
|
122
|
+
* tests and the legacy in-isolate model stay unaffected.
|
|
123
|
+
*/
|
|
124
|
+
private workerHandles = new Map<string, WorkerFunctionHandle>();
|
|
94
125
|
/** In-flight import dedupe: many concurrent first-requests await the same Promise. */
|
|
95
126
|
private loading = new Map<string, Promise<RegisteredFunction | null>>();
|
|
96
127
|
|
|
@@ -153,16 +184,18 @@ export class FunctionRegistry {
|
|
|
153
184
|
return (
|
|
154
185
|
this.handlers.has(name) ||
|
|
155
186
|
this.candidates.has(name) ||
|
|
156
|
-
this.externalManifests.has(name)
|
|
187
|
+
this.externalManifests.has(name) ||
|
|
188
|
+
this.workerHandles.has(name)
|
|
157
189
|
);
|
|
158
190
|
}
|
|
159
191
|
|
|
160
|
-
/** Names visible to dispatch (loaded handlers + unloaded candidates). */
|
|
192
|
+
/** Names visible to dispatch (loaded handlers + unloaded candidates + worker handles). */
|
|
161
193
|
knownNames(): string[] {
|
|
162
194
|
const out = new Set<string>();
|
|
163
195
|
for (const k of this.handlers.keys()) out.add(k);
|
|
164
196
|
for (const k of this.candidates.keys()) out.add(k);
|
|
165
197
|
for (const k of this.externalManifests.keys()) out.add(k);
|
|
198
|
+
for (const k of this.workerHandles.keys()) out.add(k);
|
|
166
199
|
return [...out].sort();
|
|
167
200
|
}
|
|
168
201
|
|
|
@@ -206,10 +239,28 @@ export class FunctionRegistry {
|
|
|
206
239
|
return (
|
|
207
240
|
this.handlers.get(name)?.manifest ??
|
|
208
241
|
this.candidates.get(name)?.manifest ??
|
|
242
|
+
this.workerHandles.get(name)?.manifest ??
|
|
209
243
|
this.externalManifests.get(name)
|
|
210
244
|
);
|
|
211
245
|
}
|
|
212
246
|
|
|
247
|
+
/**
|
|
248
|
+
* Register a Worker-backed function handle (Deno backend). Replaces any
|
|
249
|
+
* prior handle for `name` — callers are expected to terminate the old
|
|
250
|
+
* worker themselves; the registry doesn't own worker lifecycles.
|
|
251
|
+
*/
|
|
252
|
+
setWorkerHandle(name: string, handle: WorkerFunctionHandle): void {
|
|
253
|
+
this.workerHandles.set(name, handle);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
workerHandle(name: string): WorkerFunctionHandle | undefined {
|
|
257
|
+
return this.workerHandles.get(name);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
clearWorkerHandle(name: string): boolean {
|
|
261
|
+
return this.workerHandles.delete(name);
|
|
262
|
+
}
|
|
263
|
+
|
|
213
264
|
/**
|
|
214
265
|
* Return the loaded handler if present, otherwise dynamically import the
|
|
215
266
|
* candidate module (deduped across concurrent callers) and return the
|
|
@@ -253,6 +304,7 @@ export class FunctionRegistry {
|
|
|
253
304
|
this.pendingManifests.delete(name);
|
|
254
305
|
this.candidates.delete(name);
|
|
255
306
|
this.loading.delete(name);
|
|
307
|
+
this.workerHandles.delete(name);
|
|
256
308
|
return this.handlers.delete(name);
|
|
257
309
|
}
|
|
258
310
|
|
|
@@ -261,6 +313,7 @@ export class FunctionRegistry {
|
|
|
261
313
|
this.pendingManifests.clear();
|
|
262
314
|
this.candidates.clear();
|
|
263
315
|
this.loading.clear();
|
|
316
|
+
this.workerHandles.clear();
|
|
264
317
|
}
|
|
265
318
|
|
|
266
319
|
list(): string[] {
|