@b9g/platform 0.1.10 → 0.1.11
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/chunk-P57PW2II.js +11 -0
- package/package.json +29 -5
- package/src/config.d.ts +32 -6
- package/src/config.js +98 -17
- package/src/cookie-store.js +2 -0
- package/src/index.d.ts +9 -5
- package/src/index.js +11 -3
- package/src/runtime.d.ts +16 -8
- package/src/runtime.js +81 -25
- package/src/single-threaded.d.ts +1 -1
- package/src/single-threaded.js +9 -4
- package/src/worker-pool.js +8 -9
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined")
|
|
5
|
+
return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
__require
|
|
11
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@b9g/platform",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "ServiceWorker-first universal deployment platform. Write ServiceWorker apps once, deploy anywhere (Node/Bun/Cloudflare). Registry-based multi-app orchestration.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"serviceworker",
|
|
@@ -15,15 +15,39 @@
|
|
|
15
15
|
"shovel"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@b9g/async-context": "^0.1.
|
|
19
|
-
"@b9g/cache": "^0.1.
|
|
20
|
-
"@b9g/filesystem": "^0.1.
|
|
18
|
+
"@b9g/async-context": "^0.1.4",
|
|
19
|
+
"@b9g/cache": "^0.1.5",
|
|
20
|
+
"@b9g/filesystem": "^0.1.7",
|
|
21
21
|
"@logtape/logtape": "^1.2.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@b9g/libuild": "^0.1.
|
|
24
|
+
"@b9g/libuild": "^0.1.18",
|
|
25
25
|
"bun-types": "latest"
|
|
26
26
|
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"@logtape/file": "^1.0.0",
|
|
29
|
+
"@logtape/otel": "^1.0.0",
|
|
30
|
+
"@logtape/sentry": "^1.0.0",
|
|
31
|
+
"@logtape/syslog": "^1.0.0",
|
|
32
|
+
"@logtape/cloudwatch-logs": "^1.0.0"
|
|
33
|
+
},
|
|
34
|
+
"peerDependenciesMeta": {
|
|
35
|
+
"@logtape/file": {
|
|
36
|
+
"optional": true
|
|
37
|
+
},
|
|
38
|
+
"@logtape/otel": {
|
|
39
|
+
"optional": true
|
|
40
|
+
},
|
|
41
|
+
"@logtape/sentry": {
|
|
42
|
+
"optional": true
|
|
43
|
+
},
|
|
44
|
+
"@logtape/syslog": {
|
|
45
|
+
"optional": true
|
|
46
|
+
},
|
|
47
|
+
"@logtape/cloudwatch-logs": {
|
|
48
|
+
"optional": true
|
|
49
|
+
}
|
|
50
|
+
},
|
|
27
51
|
"type": "module",
|
|
28
52
|
"types": "src/index.d.ts",
|
|
29
53
|
"module": "src/index.js",
|
package/src/config.d.ts
CHANGED
|
@@ -58,13 +58,27 @@ export interface BucketConfig {
|
|
|
58
58
|
}
|
|
59
59
|
/** Log level for filtering */
|
|
60
60
|
export type LogLevel = "debug" | "info" | "warning" | "error";
|
|
61
|
+
/** Sink configuration */
|
|
62
|
+
export interface SinkConfig {
|
|
63
|
+
provider: string;
|
|
64
|
+
/** Provider-specific options (path, maxSize, etc.) */
|
|
65
|
+
[key: string]: any;
|
|
66
|
+
}
|
|
67
|
+
/** Per-category logging configuration */
|
|
68
|
+
export interface CategoryLoggingConfig {
|
|
69
|
+
level?: LogLevel;
|
|
70
|
+
sinks?: SinkConfig[];
|
|
71
|
+
}
|
|
61
72
|
export interface LoggingConfig {
|
|
62
|
-
/** Default log level. Defaults to "
|
|
73
|
+
/** Default log level. Defaults to "info" */
|
|
63
74
|
level?: LogLevel;
|
|
64
|
-
/**
|
|
65
|
-
|
|
75
|
+
/** Default sinks. Defaults to console */
|
|
76
|
+
sinks?: SinkConfig[];
|
|
77
|
+
/** Per-category config (inherits from top-level, can override level and/or sinks) */
|
|
78
|
+
categories?: Record<string, CategoryLoggingConfig>;
|
|
66
79
|
}
|
|
67
80
|
export interface ShovelConfig {
|
|
81
|
+
platform?: string;
|
|
68
82
|
port?: number | string;
|
|
69
83
|
host?: string;
|
|
70
84
|
workers?: number | string;
|
|
@@ -72,11 +86,18 @@ export interface ShovelConfig {
|
|
|
72
86
|
caches?: Record<string, CacheConfig>;
|
|
73
87
|
buckets?: Record<string, BucketConfig>;
|
|
74
88
|
}
|
|
89
|
+
/** Processed logging config with all defaults applied */
|
|
90
|
+
export interface ProcessedLoggingConfig {
|
|
91
|
+
level: LogLevel;
|
|
92
|
+
sinks: SinkConfig[];
|
|
93
|
+
categories: Record<string, CategoryLoggingConfig>;
|
|
94
|
+
}
|
|
75
95
|
export interface ProcessedShovelConfig {
|
|
96
|
+
platform?: string;
|
|
76
97
|
port: number;
|
|
77
98
|
host: string;
|
|
78
99
|
workers: number;
|
|
79
|
-
logging:
|
|
100
|
+
logging: ProcessedLoggingConfig;
|
|
80
101
|
caches: Record<string, CacheConfig>;
|
|
81
102
|
buckets: Record<string, BucketConfig>;
|
|
82
103
|
}
|
|
@@ -93,9 +114,11 @@ export declare function loadConfig(cwd: string): ProcessedShovelConfig;
|
|
|
93
114
|
* @param loggingConfig - The logging configuration from ProcessedShovelConfig.logging
|
|
94
115
|
* @param options - Additional options
|
|
95
116
|
* @param options.reset - Whether to reset existing LogTape config (default: true)
|
|
117
|
+
* @param options.cwd - Working directory for resolving relative paths
|
|
96
118
|
*/
|
|
97
|
-
export declare function configureLogging(loggingConfig:
|
|
119
|
+
export declare function configureLogging(loggingConfig: ProcessedLoggingConfig, options?: {
|
|
98
120
|
reset?: boolean;
|
|
121
|
+
cwd?: string;
|
|
99
122
|
}): Promise<void>;
|
|
100
123
|
/**
|
|
101
124
|
* Get cache config for a specific cache name (with pattern matching)
|
|
@@ -129,6 +152,8 @@ export declare function createBucketFactory(options: BucketFactoryOptions): (nam
|
|
|
129
152
|
export interface CacheFactoryOptions {
|
|
130
153
|
/** Shovel configuration for cache settings */
|
|
131
154
|
config?: ProcessedShovelConfig;
|
|
155
|
+
/** Default provider when not specified in config. Defaults to "memory". */
|
|
156
|
+
defaultProvider?: string;
|
|
132
157
|
}
|
|
133
158
|
/**
|
|
134
159
|
* Creates a cache factory function for CustomCacheStorage.
|
|
@@ -137,7 +162,8 @@ export interface CacheFactoryOptions {
|
|
|
137
162
|
* Provider resolution:
|
|
138
163
|
* 1. "memory" -> built-in MemoryCache
|
|
139
164
|
* 2. "redis" -> @b9g/cache-redis (blessed module)
|
|
140
|
-
* 3.
|
|
165
|
+
* 3. "cloudflare" -> native Cloudflare caches.open(name)
|
|
166
|
+
* 4. Any other string -> treated as a module name (e.g., "my-custom-cache")
|
|
141
167
|
*
|
|
142
168
|
* Custom cache modules must export a class that:
|
|
143
169
|
* - Extends Cache (from @b9g/cache)
|
package/src/config.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/// <reference types="./config.d.ts" />
|
|
2
|
+
import "../chunk-P57PW2II.js";
|
|
3
|
+
|
|
2
4
|
// src/config.ts
|
|
3
5
|
import { readFileSync } from "fs";
|
|
4
6
|
import { resolve } from "path";
|
|
5
7
|
import { Cache } from "@b9g/cache";
|
|
6
|
-
import { configure
|
|
8
|
+
import { configure } from "@logtape/logtape";
|
|
7
9
|
function getEnv() {
|
|
8
10
|
if (typeof import.meta !== "undefined" && import.meta.env) {
|
|
9
11
|
return import.meta.env;
|
|
@@ -392,12 +394,15 @@ function loadConfig(cwd) {
|
|
|
392
394
|
const processed = processConfigValue(rawConfig, env, {
|
|
393
395
|
strict: true
|
|
394
396
|
});
|
|
397
|
+
const defaultSinks = [{ provider: "console" }];
|
|
395
398
|
const config = {
|
|
399
|
+
platform: processed.platform,
|
|
396
400
|
port: typeof processed.port === "number" ? processed.port : 3e3,
|
|
397
401
|
host: processed.host || "localhost",
|
|
398
402
|
workers: typeof processed.workers === "number" ? processed.workers : 1,
|
|
399
403
|
logging: {
|
|
400
|
-
level: processed.logging?.level || "
|
|
404
|
+
level: processed.logging?.level || "info",
|
|
405
|
+
sinks: processed.logging?.sinks || defaultSinks,
|
|
401
406
|
categories: processed.logging?.categories || {}
|
|
402
407
|
},
|
|
403
408
|
caches: processed.caches || {},
|
|
@@ -418,15 +423,89 @@ var SHOVEL_CATEGORIES = [
|
|
|
418
423
|
"cache-redis",
|
|
419
424
|
"router"
|
|
420
425
|
];
|
|
426
|
+
var BUILTIN_SINK_PROVIDERS = {
|
|
427
|
+
console: { module: "@logtape/logtape", factory: "getConsoleSink" },
|
|
428
|
+
file: { module: "@logtape/file", factory: "getFileSink" },
|
|
429
|
+
rotating: { module: "@logtape/file", factory: "getRotatingFileSink" },
|
|
430
|
+
"stream-file": { module: "@logtape/file", factory: "getStreamFileSink" },
|
|
431
|
+
otel: { module: "@logtape/otel", factory: "getOpenTelemetrySink" },
|
|
432
|
+
sentry: { module: "@logtape/sentry", factory: "getSentrySink" },
|
|
433
|
+
syslog: { module: "@logtape/syslog", factory: "getSyslogSink" },
|
|
434
|
+
cloudwatch: {
|
|
435
|
+
module: "@logtape/cloudwatch-logs",
|
|
436
|
+
factory: "getCloudWatchLogsSink"
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
async function createSink(config, options = {}) {
|
|
440
|
+
const { provider, ...sinkOptions } = config;
|
|
441
|
+
if (sinkOptions.path && options.cwd) {
|
|
442
|
+
sinkOptions.path = resolve(options.cwd, sinkOptions.path);
|
|
443
|
+
}
|
|
444
|
+
const builtin = BUILTIN_SINK_PROVIDERS[provider];
|
|
445
|
+
const modulePath = builtin?.module || provider;
|
|
446
|
+
const factoryName = builtin?.factory || "default";
|
|
447
|
+
const module = await import(modulePath);
|
|
448
|
+
const factory = module[factoryName] || module.default;
|
|
449
|
+
if (!factory) {
|
|
450
|
+
throw new Error(
|
|
451
|
+
`Sink module "${modulePath}" has no export "${factoryName}"`
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
return factory(sinkOptions);
|
|
455
|
+
}
|
|
421
456
|
async function configureLogging(loggingConfig, options = {}) {
|
|
422
|
-
const { level, categories } = loggingConfig;
|
|
457
|
+
const { level, sinks: defaultSinkConfigs, categories } = loggingConfig;
|
|
423
458
|
const reset = options.reset !== false;
|
|
459
|
+
const allSinkConfigs = /* @__PURE__ */ new Map();
|
|
460
|
+
const sinkNameMap = /* @__PURE__ */ new Map();
|
|
461
|
+
for (let i = 0; i < defaultSinkConfigs.length; i++) {
|
|
462
|
+
const config = defaultSinkConfigs[i];
|
|
463
|
+
const name = `sink_${i}`;
|
|
464
|
+
allSinkConfigs.set(name, config);
|
|
465
|
+
sinkNameMap.set(config, name);
|
|
466
|
+
}
|
|
467
|
+
let sinkIndex = defaultSinkConfigs.length;
|
|
468
|
+
for (const [_, categoryConfig] of Object.entries(categories)) {
|
|
469
|
+
if (categoryConfig.sinks) {
|
|
470
|
+
for (const config of categoryConfig.sinks) {
|
|
471
|
+
let found = false;
|
|
472
|
+
for (const [existingConfig, _name] of sinkNameMap) {
|
|
473
|
+
if (JSON.stringify(existingConfig) === JSON.stringify(config)) {
|
|
474
|
+
found = true;
|
|
475
|
+
break;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
if (!found) {
|
|
479
|
+
const name = `sink_${sinkIndex++}`;
|
|
480
|
+
allSinkConfigs.set(name, config);
|
|
481
|
+
sinkNameMap.set(config, name);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
const sinks = {};
|
|
487
|
+
for (const [name, config] of allSinkConfigs) {
|
|
488
|
+
sinks[name] = await createSink(config, { cwd: options.cwd });
|
|
489
|
+
}
|
|
490
|
+
const getSinkNames = (configs) => {
|
|
491
|
+
return configs.map((config) => {
|
|
492
|
+
for (const [existingConfig, name] of sinkNameMap) {
|
|
493
|
+
if (JSON.stringify(existingConfig) === JSON.stringify(config)) {
|
|
494
|
+
return name;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
return "";
|
|
498
|
+
}).filter(Boolean);
|
|
499
|
+
};
|
|
500
|
+
const defaultSinkNames = getSinkNames(defaultSinkConfigs);
|
|
424
501
|
const loggers = SHOVEL_CATEGORIES.map((category) => {
|
|
425
|
-
const
|
|
502
|
+
const categoryConfig = categories[category];
|
|
503
|
+
const categoryLevel = categoryConfig?.level || level;
|
|
504
|
+
const categorySinks = categoryConfig?.sinks ? getSinkNames(categoryConfig.sinks) : defaultSinkNames;
|
|
426
505
|
return {
|
|
427
506
|
category: [category],
|
|
428
507
|
level: categoryLevel,
|
|
429
|
-
sinks:
|
|
508
|
+
sinks: categorySinks
|
|
430
509
|
};
|
|
431
510
|
});
|
|
432
511
|
loggers.push({
|
|
@@ -436,9 +515,7 @@ async function configureLogging(loggingConfig, options = {}) {
|
|
|
436
515
|
});
|
|
437
516
|
await configure({
|
|
438
517
|
reset,
|
|
439
|
-
sinks
|
|
440
|
-
console: getConsoleSink()
|
|
441
|
-
},
|
|
518
|
+
sinks,
|
|
442
519
|
loggers
|
|
443
520
|
});
|
|
444
521
|
}
|
|
@@ -510,21 +587,26 @@ var BUILTIN_CACHE_PROVIDERS = {
|
|
|
510
587
|
redis: "@b9g/cache-redis"
|
|
511
588
|
};
|
|
512
589
|
function createCacheFactory(options = {}) {
|
|
513
|
-
const { config } = options;
|
|
590
|
+
const { config, defaultProvider = "memory" } = options;
|
|
514
591
|
return async (name) => {
|
|
515
592
|
const cacheConfig = config ? getCacheConfig(config, name) : {};
|
|
516
|
-
const provider = String(cacheConfig.provider ||
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
593
|
+
const provider = String(cacheConfig.provider || defaultProvider);
|
|
594
|
+
if (provider === "cloudflare") {
|
|
595
|
+
const nativeCaches = globalThis.__cloudflareCaches ?? globalThis.caches;
|
|
596
|
+
if (!nativeCaches) {
|
|
597
|
+
throw new Error(
|
|
598
|
+
"Cloudflare cache provider requires native caches API. This provider only works in Cloudflare Workers environment."
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
return nativeCaches.open(name);
|
|
523
602
|
}
|
|
603
|
+
const { provider: _, ...cacheOptions } = cacheConfig;
|
|
604
|
+
const modulePath = BUILTIN_CACHE_PROVIDERS[provider] || provider;
|
|
524
605
|
try {
|
|
525
606
|
const module = await import(modulePath);
|
|
526
607
|
const CacheClass = module.default || // Default export
|
|
527
608
|
module.RedisCache || // Named export for redis
|
|
609
|
+
module.MemoryCache || // Named export for memory
|
|
528
610
|
module.Cache || // Generic Cache export
|
|
529
611
|
Object.values(module).find(
|
|
530
612
|
(v) => typeof v === "function" && v.prototype instanceof Cache
|
|
@@ -534,7 +616,6 @@ function createCacheFactory(options = {}) {
|
|
|
534
616
|
`Cache module "${modulePath}" does not export a valid cache class. Expected a default export or named export (RedisCache, Cache) that extends Cache.`
|
|
535
617
|
);
|
|
536
618
|
}
|
|
537
|
-
const { provider: _, ...cacheOptions } = cacheConfig;
|
|
538
619
|
return new CacheClass(name, cacheOptions);
|
|
539
620
|
} catch (error) {
|
|
540
621
|
if (error.code === "ERR_MODULE_NOT_FOUND" || error.code === "MODULE_NOT_FOUND") {
|
package/src/cookie-store.js
CHANGED
package/src/index.d.ts
CHANGED
|
@@ -134,16 +134,20 @@ export declare function detectDeploymentPlatform(): string | null;
|
|
|
134
134
|
*/
|
|
135
135
|
export declare function detectDevelopmentPlatform(): string;
|
|
136
136
|
/**
|
|
137
|
-
* Resolve platform name from options or auto-detect
|
|
137
|
+
* Resolve platform name from options, config, or auto-detect
|
|
138
138
|
*
|
|
139
139
|
* Priority:
|
|
140
|
-
* 1. Explicit --platform or --target flag
|
|
141
|
-
* 2.
|
|
142
|
-
* 3.
|
|
140
|
+
* 1. Explicit --platform or --target CLI flag
|
|
141
|
+
* 2. shovel.json or package.json "shovel.platform" field
|
|
142
|
+
* 3. Deployment platform detection (production environments)
|
|
143
|
+
* 4. Development platform detection (local runtime)
|
|
143
144
|
*/
|
|
144
145
|
export declare function resolvePlatform(options: {
|
|
145
146
|
platform?: string;
|
|
146
147
|
target?: string;
|
|
148
|
+
config?: {
|
|
149
|
+
platform?: string;
|
|
150
|
+
};
|
|
147
151
|
}): string;
|
|
148
152
|
/**
|
|
149
153
|
* Create platform instance based on name
|
|
@@ -189,7 +193,7 @@ export declare function getPlatform(name?: string): Platform;
|
|
|
189
193
|
export declare function getPlatformAsync(name?: string): Promise<Platform>;
|
|
190
194
|
export { ServiceWorkerPool, type WorkerPoolOptions, type WorkerMessage, type WorkerRequest, type WorkerResponse, type WorkerLoadMessage, type WorkerReadyMessage, type WorkerErrorMessage, type WorkerInitMessage, type WorkerInitializedMessage, } from "./worker-pool.js";
|
|
191
195
|
export { SingleThreadedRuntime, type SingleThreadedRuntimeOptions, } from "./single-threaded.js";
|
|
192
|
-
export { ShovelServiceWorkerRegistration,
|
|
196
|
+
export { ShovelServiceWorkerRegistration, ServiceWorkerGlobals, FetchEvent, InstallEvent, ActivateEvent, ExtendableEvent, } from "./runtime.js";
|
|
193
197
|
export { RequestCookieStore, type CookieListItem, type CookieInit, type CookieStoreGetOptions, type CookieStoreDeleteOptions, type CookieSameSite, type CookieList, parseCookieHeader, serializeCookie, parseSetCookieHeader, } from "./cookie-store.js";
|
|
194
198
|
export { CustomBucketStorage } from "@b9g/filesystem";
|
|
195
199
|
export { loadConfig, configureLogging, getCacheConfig, getBucketConfig, parseConfigExpr, processConfigValue, matchPattern, createBucketFactory, createCacheFactory, type ShovelConfig, type CacheConfig, type BucketConfig, type LoggingConfig, type LogLevel, type BucketFactoryOptions, type CacheFactoryOptions, type ProcessedShovelConfig, } from "./config.js";
|
package/src/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
/// <reference types="./index.d.ts" />
|
|
2
|
+
import "../chunk-P57PW2II.js";
|
|
3
|
+
|
|
2
4
|
// src/index.ts
|
|
3
5
|
import * as Path from "path";
|
|
4
6
|
import { readFileSync } from "fs";
|
|
@@ -12,7 +14,7 @@ import {
|
|
|
12
14
|
} from "./single-threaded.js";
|
|
13
15
|
import {
|
|
14
16
|
ShovelServiceWorkerRegistration,
|
|
15
|
-
|
|
17
|
+
ServiceWorkerGlobals,
|
|
16
18
|
FetchEvent,
|
|
17
19
|
InstallEvent,
|
|
18
20
|
ActivateEvent,
|
|
@@ -55,7 +57,10 @@ function detectPlatformFromPackageJSON(cwd) {
|
|
|
55
57
|
const pkg = JSON.parse(pkgContent);
|
|
56
58
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
57
59
|
return selectPlatformFromDeps(deps);
|
|
58
|
-
} catch {
|
|
60
|
+
} catch (err) {
|
|
61
|
+
if (err.code !== "ENOENT") {
|
|
62
|
+
throw err;
|
|
63
|
+
}
|
|
59
64
|
return null;
|
|
60
65
|
}
|
|
61
66
|
}
|
|
@@ -122,6 +127,9 @@ function resolvePlatform(options) {
|
|
|
122
127
|
if (options.target) {
|
|
123
128
|
return options.target;
|
|
124
129
|
}
|
|
130
|
+
if (options.config?.platform) {
|
|
131
|
+
return options.config.platform;
|
|
132
|
+
}
|
|
125
133
|
const deploymentPlatform = detectDeploymentPlatform();
|
|
126
134
|
if (deploymentPlatform) {
|
|
127
135
|
return deploymentPlatform;
|
|
@@ -234,8 +242,8 @@ export {
|
|
|
234
242
|
FetchEvent,
|
|
235
243
|
InstallEvent,
|
|
236
244
|
RequestCookieStore,
|
|
245
|
+
ServiceWorkerGlobals,
|
|
237
246
|
ServiceWorkerPool,
|
|
238
|
-
ShovelGlobalScope,
|
|
239
247
|
ShovelServiceWorkerRegistration,
|
|
240
248
|
SingleThreadedRuntime,
|
|
241
249
|
configureLogging,
|
package/src/runtime.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* This module provides the complete ServiceWorker runtime environment for Shovel:
|
|
5
5
|
* - Base Event Classes (ExtendableEvent, FetchEvent, InstallEvent, ActivateEvent)
|
|
6
6
|
* - ServiceWorker API Type Shims (Client, Clients, ServiceWorkerRegistration, etc.)
|
|
7
|
-
* -
|
|
7
|
+
* - ServiceWorkerGlobals (installs ServiceWorker globals onto any JavaScript runtime)
|
|
8
8
|
*
|
|
9
9
|
* Based on: https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API#interfaces
|
|
10
10
|
*/
|
|
@@ -330,7 +330,7 @@ interface NotificationOptions {
|
|
|
330
330
|
timestamp?: number;
|
|
331
331
|
vibrate?: number[];
|
|
332
332
|
}
|
|
333
|
-
export interface
|
|
333
|
+
export interface ServiceWorkerGlobalsOptions {
|
|
334
334
|
/** ServiceWorker registration instance */
|
|
335
335
|
registration: ServiceWorkerRegistration;
|
|
336
336
|
/** Bucket storage (file system access) - REQUIRED */
|
|
@@ -353,12 +353,15 @@ export declare class WorkerGlobalScope {
|
|
|
353
353
|
export declare class DedicatedWorkerGlobalScope extends WorkerGlobalScope {
|
|
354
354
|
}
|
|
355
355
|
/**
|
|
356
|
-
*
|
|
356
|
+
* ServiceWorkerGlobals - Installs ServiceWorker globals onto globalThis
|
|
357
357
|
*
|
|
358
|
-
* This
|
|
359
|
-
*
|
|
358
|
+
* This class holds ServiceWorker API implementations (caches, buckets, clients, etc.)
|
|
359
|
+
* and patches them onto globalThis via install(). It maintains the browser invariant
|
|
360
|
+
* that self === globalThis while providing ServiceWorker APIs.
|
|
361
|
+
*
|
|
362
|
+
* Use restore() to revert all patches (useful for testing).
|
|
360
363
|
*/
|
|
361
|
-
export declare class
|
|
364
|
+
export declare class ServiceWorkerGlobals implements ServiceWorkerGlobalScope {
|
|
362
365
|
#private;
|
|
363
366
|
readonly self: any;
|
|
364
367
|
readonly registration: ServiceWorkerRegistration;
|
|
@@ -405,7 +408,7 @@ export declare class ShovelGlobalScope implements ServiceWorkerGlobalScope {
|
|
|
405
408
|
ononline: ((ev: Event) => any) | null;
|
|
406
409
|
onrejectionhandled: ((ev: PromiseRejectionEvent) => any) | null;
|
|
407
410
|
onunhandledrejection: ((ev: PromiseRejectionEvent) => any) | null;
|
|
408
|
-
constructor(options:
|
|
411
|
+
constructor(options: ServiceWorkerGlobalsOptions);
|
|
409
412
|
/**
|
|
410
413
|
* Standard ServiceWorker skipWaiting() implementation
|
|
411
414
|
* Allows the ServiceWorker to activate immediately
|
|
@@ -419,8 +422,13 @@ export declare class ShovelGlobalScope implements ServiceWorkerGlobalScope {
|
|
|
419
422
|
dispatchEvent(event: Event): boolean;
|
|
420
423
|
/**
|
|
421
424
|
* Install this scope as the global scope
|
|
422
|
-
*
|
|
425
|
+
* Patches globalThis with ServiceWorker globals while maintaining self === globalThis
|
|
423
426
|
*/
|
|
424
427
|
install(): void;
|
|
428
|
+
/**
|
|
429
|
+
* Restore original globals (for testing)
|
|
430
|
+
* Reverts all patched globals to their original values
|
|
431
|
+
*/
|
|
432
|
+
restore(): void;
|
|
425
433
|
}
|
|
426
434
|
export {};
|
package/src/runtime.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
/// <reference types="./runtime.d.ts" />
|
|
2
|
+
import "../chunk-P57PW2II.js";
|
|
3
|
+
|
|
2
4
|
// src/runtime.ts
|
|
3
5
|
import { RequestCookieStore } from "./cookie-store.js";
|
|
4
6
|
import { AsyncContext } from "@b9g/async-context";
|
|
@@ -11,6 +13,23 @@ if (import.meta.env && !import.meta.env.MODE && import.meta.env.NODE_ENV) {
|
|
|
11
13
|
import.meta.env.MODE = import.meta.env.NODE_ENV;
|
|
12
14
|
}
|
|
13
15
|
var cookieStoreStorage = new AsyncContext.Variable();
|
|
16
|
+
var fetchDepthStorage = new AsyncContext.Variable();
|
|
17
|
+
var MAX_FETCH_DEPTH = 10;
|
|
18
|
+
var PATCHED_KEYS = [
|
|
19
|
+
"self",
|
|
20
|
+
"fetch",
|
|
21
|
+
"caches",
|
|
22
|
+
"buckets",
|
|
23
|
+
"registration",
|
|
24
|
+
"clients",
|
|
25
|
+
"skipWaiting",
|
|
26
|
+
"addEventListener",
|
|
27
|
+
"removeEventListener",
|
|
28
|
+
"dispatchEvent",
|
|
29
|
+
"WorkerGlobalScope",
|
|
30
|
+
"DedicatedWorkerGlobalScope",
|
|
31
|
+
"cookieStore"
|
|
32
|
+
];
|
|
14
33
|
function promiseWithTimeout(promise, timeoutMs, errorMessage) {
|
|
15
34
|
return Promise.race([
|
|
16
35
|
promise,
|
|
@@ -609,7 +628,7 @@ var WorkerGlobalScope = class {
|
|
|
609
628
|
};
|
|
610
629
|
var DedicatedWorkerGlobalScope = class extends WorkerGlobalScope {
|
|
611
630
|
};
|
|
612
|
-
var
|
|
631
|
+
var ServiceWorkerGlobals = class {
|
|
613
632
|
// Self-reference (standard in ServiceWorkerGlobalScope)
|
|
614
633
|
// Type assertion: we provide a compatible subset of WorkerGlobalScope
|
|
615
634
|
self;
|
|
@@ -624,6 +643,8 @@ var ShovelGlobalScope = class {
|
|
|
624
643
|
clients;
|
|
625
644
|
// Shovel-specific development features
|
|
626
645
|
#isDevelopment;
|
|
646
|
+
// Snapshot of original globals before patching (for restore())
|
|
647
|
+
#originals;
|
|
627
648
|
// Web API required properties
|
|
628
649
|
// Note: Using RequestCookieStore but typing as any for flexibility with global CookieStore type
|
|
629
650
|
// cookieStore is retrieved from AsyncContext for per-request isolation
|
|
@@ -665,7 +686,22 @@ var ShovelGlobalScope = class {
|
|
|
665
686
|
);
|
|
666
687
|
}
|
|
667
688
|
fetch(input, init) {
|
|
668
|
-
|
|
689
|
+
const urlString = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
690
|
+
const isRelative = urlString.startsWith("/") || urlString.startsWith("./");
|
|
691
|
+
if (!isRelative) {
|
|
692
|
+
const originalFetch = this.#originals.fetch;
|
|
693
|
+
return originalFetch(input, init);
|
|
694
|
+
}
|
|
695
|
+
const currentDepth = fetchDepthStorage.get() ?? 0;
|
|
696
|
+
if (currentDepth >= MAX_FETCH_DEPTH) {
|
|
697
|
+
return Promise.reject(
|
|
698
|
+
new Error(`Maximum self-fetch depth (${MAX_FETCH_DEPTH}) exceeded`)
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
const request = new Request(new URL(urlString, "http://localhost"), init);
|
|
702
|
+
return fetchDepthStorage.run(currentDepth + 1, () => {
|
|
703
|
+
return this.registration.handleRequest(request);
|
|
704
|
+
});
|
|
669
705
|
}
|
|
670
706
|
queueMicrotask(callback) {
|
|
671
707
|
globalThis.queueMicrotask(callback);
|
|
@@ -703,7 +739,12 @@ var ShovelGlobalScope = class {
|
|
|
703
739
|
onrejectionhandled;
|
|
704
740
|
onunhandledrejection;
|
|
705
741
|
constructor(options) {
|
|
706
|
-
|
|
742
|
+
const g = globalThis;
|
|
743
|
+
this.#originals = {};
|
|
744
|
+
for (const key of PATCHED_KEYS) {
|
|
745
|
+
this.#originals[key] = g[key];
|
|
746
|
+
}
|
|
747
|
+
this.self = globalThis;
|
|
707
748
|
this.registration = options.registration;
|
|
708
749
|
this.caches = options.caches;
|
|
709
750
|
this.buckets = options.buckets;
|
|
@@ -774,28 +815,43 @@ var ShovelGlobalScope = class {
|
|
|
774
815
|
}
|
|
775
816
|
/**
|
|
776
817
|
* Install this scope as the global scope
|
|
777
|
-
*
|
|
818
|
+
* Patches globalThis with ServiceWorker globals while maintaining self === globalThis
|
|
778
819
|
*/
|
|
779
820
|
install() {
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
this.postMessage = postMessage.bind(globalThis);
|
|
786
|
-
}
|
|
787
|
-
globalThis.addEventListener = this.addEventListener.bind(this);
|
|
788
|
-
globalThis.removeEventListener = this.removeEventListener.bind(this);
|
|
789
|
-
globalThis.dispatchEvent = this.dispatchEvent.bind(this);
|
|
790
|
-
if (this.caches) {
|
|
791
|
-
globalThis.caches = this.caches;
|
|
821
|
+
const g = globalThis;
|
|
822
|
+
g.WorkerGlobalScope = WorkerGlobalScope;
|
|
823
|
+
g.DedicatedWorkerGlobalScope = DedicatedWorkerGlobalScope;
|
|
824
|
+
if (typeof g.self === "undefined") {
|
|
825
|
+
g.self = globalThis;
|
|
792
826
|
}
|
|
793
|
-
|
|
794
|
-
|
|
827
|
+
g.addEventListener = this.addEventListener.bind(this);
|
|
828
|
+
g.removeEventListener = this.removeEventListener.bind(this);
|
|
829
|
+
g.dispatchEvent = this.dispatchEvent.bind(this);
|
|
830
|
+
g.caches = this.caches;
|
|
831
|
+
g.buckets = this.buckets;
|
|
832
|
+
g.registration = this.registration;
|
|
833
|
+
g.skipWaiting = this.skipWaiting.bind(this);
|
|
834
|
+
g.clients = this.clients;
|
|
835
|
+
g.fetch = this.fetch.bind(this);
|
|
836
|
+
Object.defineProperty(g, "cookieStore", {
|
|
837
|
+
get: () => cookieStoreStorage.get(),
|
|
838
|
+
configurable: true
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* Restore original globals (for testing)
|
|
843
|
+
* Reverts all patched globals to their original values
|
|
844
|
+
*/
|
|
845
|
+
restore() {
|
|
846
|
+
const g = globalThis;
|
|
847
|
+
for (const key of PATCHED_KEYS) {
|
|
848
|
+
const original = this.#originals[key];
|
|
849
|
+
if (original === void 0) {
|
|
850
|
+
delete g[key];
|
|
851
|
+
} else {
|
|
852
|
+
g[key] = original;
|
|
853
|
+
}
|
|
795
854
|
}
|
|
796
|
-
globalThis.registration = this.registration;
|
|
797
|
-
globalThis.skipWaiting = this.skipWaiting.bind(this);
|
|
798
|
-
globalThis.clients = this.clients;
|
|
799
855
|
}
|
|
800
856
|
};
|
|
801
857
|
var logger = getLogger(["worker"]);
|
|
@@ -832,7 +888,7 @@ async function handleFetchEvent(request) {
|
|
|
832
888
|
const response = await registration.handleRequest(request);
|
|
833
889
|
return response;
|
|
834
890
|
} catch (error) {
|
|
835
|
-
logger.error("[Worker] ServiceWorker request failed", { error });
|
|
891
|
+
logger.error("[Worker] ServiceWorker request failed: {error}", { error });
|
|
836
892
|
console.error("[Worker] ServiceWorker request failed:", error);
|
|
837
893
|
const response = new Response("ServiceWorker request failed", {
|
|
838
894
|
status: 500
|
|
@@ -856,7 +912,7 @@ async function loadServiceWorker(entrypoint) {
|
|
|
856
912
|
if (!caches || !buckets) {
|
|
857
913
|
throw new Error("Runtime not initialized - missing caches or buckets");
|
|
858
914
|
}
|
|
859
|
-
scope = new
|
|
915
|
+
scope = new ServiceWorkerGlobals({
|
|
860
916
|
registration,
|
|
861
917
|
caches,
|
|
862
918
|
buckets
|
|
@@ -913,7 +969,7 @@ async function initializeRuntime(config, baseDir) {
|
|
|
913
969
|
buckets = new CustomBucketStorage(createBucketFactory({ baseDir, config }));
|
|
914
970
|
logger.info(`[Worker-${workerId}] Creating and installing scope`);
|
|
915
971
|
registration = new ShovelServiceWorkerRegistration();
|
|
916
|
-
scope = new
|
|
972
|
+
scope = new ServiceWorkerGlobals({ registration, caches, buckets });
|
|
917
973
|
scope.install();
|
|
918
974
|
_workerSelf = scope;
|
|
919
975
|
logger.info(`[Worker-${workerId}] Runtime initialized successfully`);
|
|
@@ -987,9 +1043,9 @@ export {
|
|
|
987
1043
|
Notification,
|
|
988
1044
|
NotificationEvent,
|
|
989
1045
|
PushEvent,
|
|
1046
|
+
ServiceWorkerGlobals,
|
|
990
1047
|
ShovelClient,
|
|
991
1048
|
ShovelClients,
|
|
992
|
-
ShovelGlobalScope,
|
|
993
1049
|
ShovelNavigationPreloadManager,
|
|
994
1050
|
ShovelPushMessageData,
|
|
995
1051
|
ShovelServiceWorker,
|
package/src/single-threaded.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export declare class SingleThreadedRuntime {
|
|
|
26
26
|
#private;
|
|
27
27
|
constructor(options: SingleThreadedRuntimeOptions);
|
|
28
28
|
/**
|
|
29
|
-
* Initialize the runtime (install
|
|
29
|
+
* Initialize the runtime (install ServiceWorker globals)
|
|
30
30
|
*/
|
|
31
31
|
init(): Promise<void>;
|
|
32
32
|
/**
|
package/src/single-threaded.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
/// <reference types="./single-threaded.d.ts" />
|
|
2
|
+
import "../chunk-P57PW2II.js";
|
|
3
|
+
|
|
2
4
|
// src/single-threaded.ts
|
|
3
5
|
import { getLogger } from "@logtape/logtape";
|
|
4
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
ServiceWorkerGlobals,
|
|
8
|
+
ShovelServiceWorkerRegistration
|
|
9
|
+
} from "./runtime.js";
|
|
5
10
|
import { CustomBucketStorage } from "@b9g/filesystem";
|
|
6
11
|
import { CustomCacheStorage } from "@b9g/cache";
|
|
7
12
|
import {
|
|
@@ -24,7 +29,7 @@ var SingleThreadedRuntime = class {
|
|
|
24
29
|
createBucketFactory({ baseDir: options.baseDir, config: options.config })
|
|
25
30
|
);
|
|
26
31
|
this.#registration = new ShovelServiceWorkerRegistration();
|
|
27
|
-
this.#scope = new
|
|
32
|
+
this.#scope = new ServiceWorkerGlobals({
|
|
28
33
|
registration: this.#registration,
|
|
29
34
|
caches: cacheStorage,
|
|
30
35
|
buckets: bucketStorage
|
|
@@ -32,14 +37,14 @@ var SingleThreadedRuntime = class {
|
|
|
32
37
|
logger.info("SingleThreadedRuntime created", { baseDir: options.baseDir });
|
|
33
38
|
}
|
|
34
39
|
/**
|
|
35
|
-
* Initialize the runtime (install
|
|
40
|
+
* Initialize the runtime (install ServiceWorker globals)
|
|
36
41
|
*/
|
|
37
42
|
async init() {
|
|
38
43
|
if (this.#config?.logging) {
|
|
39
44
|
await configureLogging(this.#config.logging);
|
|
40
45
|
}
|
|
41
46
|
this.#scope.install();
|
|
42
|
-
logger.info("SingleThreadedRuntime initialized -
|
|
47
|
+
logger.info("SingleThreadedRuntime initialized - globals installed");
|
|
43
48
|
}
|
|
44
49
|
/**
|
|
45
50
|
* Load and run a ServiceWorker entrypoint
|
package/src/worker-pool.js
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
/// <reference types="./worker-pool.d.ts" />
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}
|
|
5
|
-
if (typeof require !== "undefined")
|
|
6
|
-
return require.apply(this, arguments);
|
|
7
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
8
|
-
});
|
|
2
|
+
import {
|
|
3
|
+
__require
|
|
4
|
+
} from "../chunk-P57PW2II.js";
|
|
9
5
|
|
|
10
6
|
// src/worker-pool.ts
|
|
11
7
|
import * as Path from "path";
|
|
@@ -29,7 +25,10 @@ function resolveWorkerScript(entrypoint) {
|
|
|
29
25
|
return bundledWorker;
|
|
30
26
|
}
|
|
31
27
|
}
|
|
32
|
-
} catch {
|
|
28
|
+
} catch (err) {
|
|
29
|
+
if (err.code !== "ENOENT") {
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
33
32
|
}
|
|
34
33
|
}
|
|
35
34
|
try {
|
|
@@ -246,7 +245,7 @@ var ServiceWorkerPool = class {
|
|
|
246
245
|
this.#pendingRequests.delete(message.requestID);
|
|
247
246
|
}
|
|
248
247
|
} else {
|
|
249
|
-
logger.error("Worker error", { error: message.error });
|
|
248
|
+
logger.error("Worker error: {error}", { error: message.error });
|
|
250
249
|
}
|
|
251
250
|
}
|
|
252
251
|
#handleReady(message) {
|