@b9g/platform 0.1.9 → 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 +48 -1
- package/src/config.js +133 -9
- package/src/cookie-store.js +2 -0
- package/src/index.d.ts +10 -6
- package/src/index.js +13 -3
- package/src/runtime.d.ts +16 -8
- package/src/runtime.js +85 -25
- package/src/single-threaded.d.ts +1 -1
- package/src/single-threaded.js +13 -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
|
@@ -56,17 +56,48 @@ export interface BucketConfig {
|
|
|
56
56
|
region?: string | number;
|
|
57
57
|
endpoint?: string | number;
|
|
58
58
|
}
|
|
59
|
+
/** Log level for filtering */
|
|
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
|
+
}
|
|
72
|
+
export interface LoggingConfig {
|
|
73
|
+
/** Default log level. Defaults to "info" */
|
|
74
|
+
level?: LogLevel;
|
|
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>;
|
|
79
|
+
}
|
|
59
80
|
export interface ShovelConfig {
|
|
81
|
+
platform?: string;
|
|
60
82
|
port?: number | string;
|
|
61
83
|
host?: string;
|
|
62
84
|
workers?: number | string;
|
|
85
|
+
logging?: LoggingConfig;
|
|
63
86
|
caches?: Record<string, CacheConfig>;
|
|
64
87
|
buckets?: Record<string, BucketConfig>;
|
|
65
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
|
+
}
|
|
66
95
|
export interface ProcessedShovelConfig {
|
|
96
|
+
platform?: string;
|
|
67
97
|
port: number;
|
|
68
98
|
host: string;
|
|
69
99
|
workers: number;
|
|
100
|
+
logging: ProcessedLoggingConfig;
|
|
70
101
|
caches: Record<string, CacheConfig>;
|
|
71
102
|
buckets: Record<string, BucketConfig>;
|
|
72
103
|
}
|
|
@@ -76,6 +107,19 @@ export interface ProcessedShovelConfig {
|
|
|
76
107
|
* @param cwd - Current working directory (must be provided by runtime adapter)
|
|
77
108
|
*/
|
|
78
109
|
export declare function loadConfig(cwd: string): ProcessedShovelConfig;
|
|
110
|
+
/**
|
|
111
|
+
* Configure LogTape logging based on Shovel config.
|
|
112
|
+
* Call this in both main thread and workers.
|
|
113
|
+
*
|
|
114
|
+
* @param loggingConfig - The logging configuration from ProcessedShovelConfig.logging
|
|
115
|
+
* @param options - Additional options
|
|
116
|
+
* @param options.reset - Whether to reset existing LogTape config (default: true)
|
|
117
|
+
* @param options.cwd - Working directory for resolving relative paths
|
|
118
|
+
*/
|
|
119
|
+
export declare function configureLogging(loggingConfig: ProcessedLoggingConfig, options?: {
|
|
120
|
+
reset?: boolean;
|
|
121
|
+
cwd?: string;
|
|
122
|
+
}): Promise<void>;
|
|
79
123
|
/**
|
|
80
124
|
* Get cache config for a specific cache name (with pattern matching)
|
|
81
125
|
*/
|
|
@@ -108,6 +152,8 @@ export declare function createBucketFactory(options: BucketFactoryOptions): (nam
|
|
|
108
152
|
export interface CacheFactoryOptions {
|
|
109
153
|
/** Shovel configuration for cache settings */
|
|
110
154
|
config?: ProcessedShovelConfig;
|
|
155
|
+
/** Default provider when not specified in config. Defaults to "memory". */
|
|
156
|
+
defaultProvider?: string;
|
|
111
157
|
}
|
|
112
158
|
/**
|
|
113
159
|
* Creates a cache factory function for CustomCacheStorage.
|
|
@@ -116,7 +162,8 @@ export interface CacheFactoryOptions {
|
|
|
116
162
|
* Provider resolution:
|
|
117
163
|
* 1. "memory" -> built-in MemoryCache
|
|
118
164
|
* 2. "redis" -> @b9g/cache-redis (blessed module)
|
|
119
|
-
* 3.
|
|
165
|
+
* 3. "cloudflare" -> native Cloudflare caches.open(name)
|
|
166
|
+
* 4. Any other string -> treated as a module name (e.g., "my-custom-cache")
|
|
120
167
|
*
|
|
121
168
|
* Custom cache modules must export a class that:
|
|
122
169
|
* - Extends Cache (from @b9g/cache)
|
package/src/config.js
CHANGED
|
@@ -1,8 +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";
|
|
8
|
+
import { configure } from "@logtape/logtape";
|
|
6
9
|
function getEnv() {
|
|
7
10
|
if (typeof import.meta !== "undefined" && import.meta.env) {
|
|
8
11
|
return import.meta.env;
|
|
@@ -391,15 +394,131 @@ function loadConfig(cwd) {
|
|
|
391
394
|
const processed = processConfigValue(rawConfig, env, {
|
|
392
395
|
strict: true
|
|
393
396
|
});
|
|
397
|
+
const defaultSinks = [{ provider: "console" }];
|
|
394
398
|
const config = {
|
|
399
|
+
platform: processed.platform,
|
|
395
400
|
port: typeof processed.port === "number" ? processed.port : 3e3,
|
|
396
401
|
host: processed.host || "localhost",
|
|
397
402
|
workers: typeof processed.workers === "number" ? processed.workers : 1,
|
|
403
|
+
logging: {
|
|
404
|
+
level: processed.logging?.level || "info",
|
|
405
|
+
sinks: processed.logging?.sinks || defaultSinks,
|
|
406
|
+
categories: processed.logging?.categories || {}
|
|
407
|
+
},
|
|
398
408
|
caches: processed.caches || {},
|
|
399
409
|
buckets: processed.buckets || {}
|
|
400
410
|
};
|
|
401
411
|
return config;
|
|
402
412
|
}
|
|
413
|
+
var SHOVEL_CATEGORIES = [
|
|
414
|
+
"cli",
|
|
415
|
+
"watcher",
|
|
416
|
+
"worker",
|
|
417
|
+
"single-threaded",
|
|
418
|
+
"assets",
|
|
419
|
+
"platform-node",
|
|
420
|
+
"platform-bun",
|
|
421
|
+
"platform-cloudflare",
|
|
422
|
+
"cache",
|
|
423
|
+
"cache-redis",
|
|
424
|
+
"router"
|
|
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
|
+
}
|
|
456
|
+
async function configureLogging(loggingConfig, options = {}) {
|
|
457
|
+
const { level, sinks: defaultSinkConfigs, categories } = loggingConfig;
|
|
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);
|
|
501
|
+
const loggers = SHOVEL_CATEGORIES.map((category) => {
|
|
502
|
+
const categoryConfig = categories[category];
|
|
503
|
+
const categoryLevel = categoryConfig?.level || level;
|
|
504
|
+
const categorySinks = categoryConfig?.sinks ? getSinkNames(categoryConfig.sinks) : defaultSinkNames;
|
|
505
|
+
return {
|
|
506
|
+
category: [category],
|
|
507
|
+
level: categoryLevel,
|
|
508
|
+
sinks: categorySinks
|
|
509
|
+
};
|
|
510
|
+
});
|
|
511
|
+
loggers.push({
|
|
512
|
+
category: ["logtape", "meta"],
|
|
513
|
+
level: "warning",
|
|
514
|
+
sinks: []
|
|
515
|
+
});
|
|
516
|
+
await configure({
|
|
517
|
+
reset,
|
|
518
|
+
sinks,
|
|
519
|
+
loggers
|
|
520
|
+
});
|
|
521
|
+
}
|
|
403
522
|
function getCacheConfig(config, name) {
|
|
404
523
|
return matchPattern(name, config.caches) || {};
|
|
405
524
|
}
|
|
@@ -468,21 +587,26 @@ var BUILTIN_CACHE_PROVIDERS = {
|
|
|
468
587
|
redis: "@b9g/cache-redis"
|
|
469
588
|
};
|
|
470
589
|
function createCacheFactory(options = {}) {
|
|
471
|
-
const { config } = options;
|
|
590
|
+
const { config, defaultProvider = "memory" } = options;
|
|
472
591
|
return async (name) => {
|
|
473
592
|
const cacheConfig = config ? getCacheConfig(config, name) : {};
|
|
474
|
-
const provider = String(cacheConfig.provider ||
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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);
|
|
481
602
|
}
|
|
603
|
+
const { provider: _, ...cacheOptions } = cacheConfig;
|
|
604
|
+
const modulePath = BUILTIN_CACHE_PROVIDERS[provider] || provider;
|
|
482
605
|
try {
|
|
483
606
|
const module = await import(modulePath);
|
|
484
607
|
const CacheClass = module.default || // Default export
|
|
485
608
|
module.RedisCache || // Named export for redis
|
|
609
|
+
module.MemoryCache || // Named export for memory
|
|
486
610
|
module.Cache || // Generic Cache export
|
|
487
611
|
Object.values(module).find(
|
|
488
612
|
(v) => typeof v === "function" && v.prototype instanceof Cache
|
|
@@ -492,7 +616,6 @@ function createCacheFactory(options = {}) {
|
|
|
492
616
|
`Cache module "${modulePath}" does not export a valid cache class. Expected a default export or named export (RedisCache, Cache) that extends Cache.`
|
|
493
617
|
);
|
|
494
618
|
}
|
|
495
|
-
const { provider: _, ...cacheOptions } = cacheConfig;
|
|
496
619
|
return new CacheClass(name, cacheOptions);
|
|
497
620
|
} catch (error) {
|
|
498
621
|
if (error.code === "ERR_MODULE_NOT_FOUND" || error.code === "MODULE_NOT_FOUND") {
|
|
@@ -506,6 +629,7 @@ For redis: npm install @b9g/cache-redis`
|
|
|
506
629
|
};
|
|
507
630
|
}
|
|
508
631
|
export {
|
|
632
|
+
configureLogging,
|
|
509
633
|
createBucketFactory,
|
|
510
634
|
createCacheFactory,
|
|
511
635
|
getBucketConfig,
|
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
|
-
export { loadConfig, getCacheConfig, getBucketConfig, parseConfigExpr, processConfigValue, matchPattern, createBucketFactory, createCacheFactory, type ShovelConfig, type CacheConfig, type BucketConfig, type BucketFactoryOptions, type CacheFactoryOptions, type ProcessedShovelConfig, } from "./config.js";
|
|
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,
|
|
@@ -27,6 +29,7 @@ import {
|
|
|
27
29
|
import { CustomBucketStorage } from "@b9g/filesystem";
|
|
28
30
|
import {
|
|
29
31
|
loadConfig,
|
|
32
|
+
configureLogging,
|
|
30
33
|
getCacheConfig,
|
|
31
34
|
getBucketConfig,
|
|
32
35
|
parseConfigExpr,
|
|
@@ -54,7 +57,10 @@ function detectPlatformFromPackageJSON(cwd) {
|
|
|
54
57
|
const pkg = JSON.parse(pkgContent);
|
|
55
58
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
56
59
|
return selectPlatformFromDeps(deps);
|
|
57
|
-
} catch {
|
|
60
|
+
} catch (err) {
|
|
61
|
+
if (err.code !== "ENOENT") {
|
|
62
|
+
throw err;
|
|
63
|
+
}
|
|
58
64
|
return null;
|
|
59
65
|
}
|
|
60
66
|
}
|
|
@@ -121,6 +127,9 @@ function resolvePlatform(options) {
|
|
|
121
127
|
if (options.target) {
|
|
122
128
|
return options.target;
|
|
123
129
|
}
|
|
130
|
+
if (options.config?.platform) {
|
|
131
|
+
return options.config.platform;
|
|
132
|
+
}
|
|
124
133
|
const deploymentPlatform = detectDeploymentPlatform();
|
|
125
134
|
if (deploymentPlatform) {
|
|
126
135
|
return deploymentPlatform;
|
|
@@ -233,10 +242,11 @@ export {
|
|
|
233
242
|
FetchEvent,
|
|
234
243
|
InstallEvent,
|
|
235
244
|
RequestCookieStore,
|
|
245
|
+
ServiceWorkerGlobals,
|
|
236
246
|
ServiceWorkerPool,
|
|
237
|
-
ShovelGlobalScope,
|
|
238
247
|
ShovelServiceWorkerRegistration,
|
|
239
248
|
SingleThreadedRuntime,
|
|
249
|
+
configureLogging,
|
|
240
250
|
createBucketFactory,
|
|
241
251
|
createCacheFactory,
|
|
242
252
|
createPlatform,
|
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";
|
|
@@ -6,10 +8,28 @@ import { CustomBucketStorage } from "@b9g/filesystem";
|
|
|
6
8
|
import { CustomCacheStorage } from "@b9g/cache";
|
|
7
9
|
import { createBucketFactory, createCacheFactory } from "./config.js";
|
|
8
10
|
import { getLogger } from "@logtape/logtape";
|
|
11
|
+
import { configureLogging } from "./config.js";
|
|
9
12
|
if (import.meta.env && !import.meta.env.MODE && import.meta.env.NODE_ENV) {
|
|
10
13
|
import.meta.env.MODE = import.meta.env.NODE_ENV;
|
|
11
14
|
}
|
|
12
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
|
+
];
|
|
13
33
|
function promiseWithTimeout(promise, timeoutMs, errorMessage) {
|
|
14
34
|
return Promise.race([
|
|
15
35
|
promise,
|
|
@@ -608,7 +628,7 @@ var WorkerGlobalScope = class {
|
|
|
608
628
|
};
|
|
609
629
|
var DedicatedWorkerGlobalScope = class extends WorkerGlobalScope {
|
|
610
630
|
};
|
|
611
|
-
var
|
|
631
|
+
var ServiceWorkerGlobals = class {
|
|
612
632
|
// Self-reference (standard in ServiceWorkerGlobalScope)
|
|
613
633
|
// Type assertion: we provide a compatible subset of WorkerGlobalScope
|
|
614
634
|
self;
|
|
@@ -623,6 +643,8 @@ var ShovelGlobalScope = class {
|
|
|
623
643
|
clients;
|
|
624
644
|
// Shovel-specific development features
|
|
625
645
|
#isDevelopment;
|
|
646
|
+
// Snapshot of original globals before patching (for restore())
|
|
647
|
+
#originals;
|
|
626
648
|
// Web API required properties
|
|
627
649
|
// Note: Using RequestCookieStore but typing as any for flexibility with global CookieStore type
|
|
628
650
|
// cookieStore is retrieved from AsyncContext for per-request isolation
|
|
@@ -664,7 +686,22 @@ var ShovelGlobalScope = class {
|
|
|
664
686
|
);
|
|
665
687
|
}
|
|
666
688
|
fetch(input, init) {
|
|
667
|
-
|
|
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
|
+
});
|
|
668
705
|
}
|
|
669
706
|
queueMicrotask(callback) {
|
|
670
707
|
globalThis.queueMicrotask(callback);
|
|
@@ -702,7 +739,12 @@ var ShovelGlobalScope = class {
|
|
|
702
739
|
onrejectionhandled;
|
|
703
740
|
onunhandledrejection;
|
|
704
741
|
constructor(options) {
|
|
705
|
-
|
|
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;
|
|
706
748
|
this.registration = options.registration;
|
|
707
749
|
this.caches = options.caches;
|
|
708
750
|
this.buckets = options.buckets;
|
|
@@ -773,28 +815,43 @@ var ShovelGlobalScope = class {
|
|
|
773
815
|
}
|
|
774
816
|
/**
|
|
775
817
|
* Install this scope as the global scope
|
|
776
|
-
*
|
|
818
|
+
* Patches globalThis with ServiceWorker globals while maintaining self === globalThis
|
|
777
819
|
*/
|
|
778
820
|
install() {
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
this.postMessage = postMessage.bind(globalThis);
|
|
785
|
-
}
|
|
786
|
-
globalThis.addEventListener = this.addEventListener.bind(this);
|
|
787
|
-
globalThis.removeEventListener = this.removeEventListener.bind(this);
|
|
788
|
-
globalThis.dispatchEvent = this.dispatchEvent.bind(this);
|
|
789
|
-
if (this.caches) {
|
|
790
|
-
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;
|
|
791
826
|
}
|
|
792
|
-
|
|
793
|
-
|
|
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
|
+
}
|
|
794
854
|
}
|
|
795
|
-
globalThis.registration = this.registration;
|
|
796
|
-
globalThis.skipWaiting = this.skipWaiting.bind(this);
|
|
797
|
-
globalThis.clients = this.clients;
|
|
798
855
|
}
|
|
799
856
|
};
|
|
800
857
|
var logger = getLogger(["worker"]);
|
|
@@ -831,7 +888,7 @@ async function handleFetchEvent(request) {
|
|
|
831
888
|
const response = await registration.handleRequest(request);
|
|
832
889
|
return response;
|
|
833
890
|
} catch (error) {
|
|
834
|
-
logger.error("[Worker] ServiceWorker request failed", { error });
|
|
891
|
+
logger.error("[Worker] ServiceWorker request failed: {error}", { error });
|
|
835
892
|
console.error("[Worker] ServiceWorker request failed:", error);
|
|
836
893
|
const response = new Response("ServiceWorker request failed", {
|
|
837
894
|
status: 500
|
|
@@ -855,7 +912,7 @@ async function loadServiceWorker(entrypoint) {
|
|
|
855
912
|
if (!caches || !buckets) {
|
|
856
913
|
throw new Error("Runtime not initialized - missing caches or buckets");
|
|
857
914
|
}
|
|
858
|
-
scope = new
|
|
915
|
+
scope = new ServiceWorkerGlobals({
|
|
859
916
|
registration,
|
|
860
917
|
caches,
|
|
861
918
|
buckets
|
|
@@ -899,6 +956,9 @@ var workerId = Math.random().toString(36).substring(2, 8);
|
|
|
899
956
|
var sendMessage;
|
|
900
957
|
async function initializeRuntime(config, baseDir) {
|
|
901
958
|
try {
|
|
959
|
+
if (config?.logging) {
|
|
960
|
+
await configureLogging(config.logging);
|
|
961
|
+
}
|
|
902
962
|
logger.info(`[Worker-${workerId}] Initializing runtime with config`, {
|
|
903
963
|
config,
|
|
904
964
|
baseDir
|
|
@@ -909,7 +969,7 @@ async function initializeRuntime(config, baseDir) {
|
|
|
909
969
|
buckets = new CustomBucketStorage(createBucketFactory({ baseDir, config }));
|
|
910
970
|
logger.info(`[Worker-${workerId}] Creating and installing scope`);
|
|
911
971
|
registration = new ShovelServiceWorkerRegistration();
|
|
912
|
-
scope = new
|
|
972
|
+
scope = new ServiceWorkerGlobals({ registration, caches, buckets });
|
|
913
973
|
scope.install();
|
|
914
974
|
_workerSelf = scope;
|
|
915
975
|
logger.info(`[Worker-${workerId}] Runtime initialized successfully`);
|
|
@@ -983,9 +1043,9 @@ export {
|
|
|
983
1043
|
Notification,
|
|
984
1044
|
NotificationEvent,
|
|
985
1045
|
PushEvent,
|
|
1046
|
+
ServiceWorkerGlobals,
|
|
986
1047
|
ShovelClient,
|
|
987
1048
|
ShovelClients,
|
|
988
|
-
ShovelGlobalScope,
|
|
989
1049
|
ShovelNavigationPreloadManager,
|
|
990
1050
|
ShovelPushMessageData,
|
|
991
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,10 +1,16 @@
|
|
|
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 {
|
|
13
|
+
configureLogging,
|
|
8
14
|
createBucketFactory,
|
|
9
15
|
createCacheFactory
|
|
10
16
|
} from "./config.js";
|
|
@@ -23,7 +29,7 @@ var SingleThreadedRuntime = class {
|
|
|
23
29
|
createBucketFactory({ baseDir: options.baseDir, config: options.config })
|
|
24
30
|
);
|
|
25
31
|
this.#registration = new ShovelServiceWorkerRegistration();
|
|
26
|
-
this.#scope = new
|
|
32
|
+
this.#scope = new ServiceWorkerGlobals({
|
|
27
33
|
registration: this.#registration,
|
|
28
34
|
caches: cacheStorage,
|
|
29
35
|
buckets: bucketStorage
|
|
@@ -31,11 +37,14 @@ var SingleThreadedRuntime = class {
|
|
|
31
37
|
logger.info("SingleThreadedRuntime created", { baseDir: options.baseDir });
|
|
32
38
|
}
|
|
33
39
|
/**
|
|
34
|
-
* Initialize the runtime (install
|
|
40
|
+
* Initialize the runtime (install ServiceWorker globals)
|
|
35
41
|
*/
|
|
36
42
|
async init() {
|
|
43
|
+
if (this.#config?.logging) {
|
|
44
|
+
await configureLogging(this.#config.logging);
|
|
45
|
+
}
|
|
37
46
|
this.#scope.install();
|
|
38
|
-
logger.info("SingleThreadedRuntime initialized -
|
|
47
|
+
logger.info("SingleThreadedRuntime initialized - globals installed");
|
|
39
48
|
}
|
|
40
49
|
/**
|
|
41
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) {
|