@b9g/platform 0.1.12 → 0.1.13

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/src/runtime.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ /// <reference path="./globals.d.ts" />
2
+ /// <reference path="./shovel-config.d.ts" />
1
3
  /**
2
4
  * ServiceWorker Runtime - Complete ServiceWorker API Implementation
3
5
  *
@@ -9,6 +11,8 @@
9
11
  *
10
12
  * Based on: https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API#interfaces
11
13
  */
14
+ import { CustomDirectoryStorage } from "@b9g/filesystem";
15
+ import { CustomCacheStorage, Cache } from "@b9g/cache";
12
16
  declare global {
13
17
  interface CookieListItem {
14
18
  domain?: string;
@@ -69,37 +73,137 @@ import type { DirectoryStorage } from "@b9g/filesystem";
69
73
  import { type Logger } from "@logtape/logtape";
70
74
  /**
71
75
  * Logger storage interface for accessing loggers by category path.
72
- * Unlike CacheStorage/DirectoryStorage which use a registry pattern,
73
- * LoggerStorage uses variadic categories since logging backends are
74
- * always LogTape and per-category config is in shovel.config.json.
76
+ * Uses array signature (matching LogTape) to allow future options parameter.
75
77
  */
76
78
  export interface LoggerStorage {
77
79
  /**
78
- * Open a logger by category path - returns LogTape's Logger directly
79
- * @example loggers.open("app") → getLogger(["app"])
80
- * @example loggers.open("app", "db") getLogger(["app", "db"])
80
+ * Get a logger by category path (sync)
81
+ * @example const logger = self.loggers.get(["app"])
82
+ * @example const logger = self.loggers.get(["app", "db"])
81
83
  */
82
- open(...categories: string[]): Logger;
84
+ get(categories: string[]): Logger;
83
85
  }
84
86
  /**
85
- * Factory function type for creating loggers
87
+ * Factory function type for creating loggers (sync).
86
88
  */
87
- export type LoggerFactory = (...categories: string[]) => Logger;
89
+ export type LoggerFactory = (categories: string[]) => Logger;
88
90
  /**
89
91
  * Custom logger storage implementation that wraps a factory function
90
92
  */
91
93
  export declare class CustomLoggerStorage implements LoggerStorage {
92
94
  #private;
93
95
  constructor(factory: LoggerFactory);
94
- open(...categories: string[]): Logger;
96
+ get(categories: string[]): Logger;
95
97
  }
98
+ import { Database } from "@b9g/zen";
99
+ /**
100
+ * Database configuration from shovel.json.
101
+ * Uses the unified impl pattern like directories and caches.
102
+ */
103
+ export interface DatabaseConfig {
104
+ /** Reified implementation (driver class from build-time code generation) */
105
+ impl?: new (url: string, options?: any) => any;
106
+ /** Database connection URL */
107
+ url?: string;
108
+ /** Additional driver-specific options */
109
+ [key: string]: unknown;
110
+ }
111
+ /**
112
+ * Upgrade event passed to onUpgrade callback during database.open().
113
+ */
114
+ export interface DatabaseUpgradeEvent {
115
+ /** The database being upgraded */
116
+ db: Database;
117
+ /** Previous database version (0 if new) */
118
+ oldVersion: number;
119
+ /** Target version being opened */
120
+ newVersion: number;
121
+ /** Register a promise that must complete before open() resolves */
122
+ waitUntil(promise: Promise<unknown>): void;
123
+ }
124
+ /**
125
+ * DatabaseStorage interface - provides access to named database instances.
126
+ *
127
+ * @example
128
+ * ```typescript
129
+ * // In activate handler - open database with migrations
130
+ * self.addEventListener("activate", (event) => {
131
+ * event.waitUntil(
132
+ * self.databases.open("main", 2, (e) => {
133
+ * e.waitUntil(runMigrations(e));
134
+ * })
135
+ * );
136
+ * });
137
+ *
138
+ * // In fetch handler - get the opened database (sync)
139
+ * self.addEventListener("fetch", (event) => {
140
+ * const db = self.databases.get("main");
141
+ * const users = await db.all(User)`WHERE active = ${true}`;
142
+ * });
143
+ * ```
144
+ */
145
+ export interface DatabaseStorage {
146
+ /**
147
+ * Open a database at a specific version.
148
+ * Imports the driver, creates the Database, runs migrations if needed.
149
+ * Caches the opened instance for later get() calls.
150
+ */
151
+ open(name: string, version: number, onUpgrade?: (event: DatabaseUpgradeEvent) => void): Promise<Database>;
152
+ /**
153
+ * Get an already-opened database.
154
+ * Throws if the database hasn't been opened yet.
155
+ * This is synchronous for fast access in request handlers.
156
+ */
157
+ get(name: string): Database;
158
+ /** Close a specific database */
159
+ close(name: string): Promise<void>;
160
+ /** Close all databases */
161
+ closeAll(): Promise<void>;
162
+ }
163
+ /**
164
+ * Factory function type for creating database drivers.
165
+ * Returns the Database instance and a close function.
166
+ */
167
+ export type DatabaseFactory = (name: string) => Promise<{
168
+ db: Database;
169
+ close: () => Promise<void>;
170
+ }>;
171
+ /**
172
+ * CustomDatabaseStorage implements DatabaseStorage.
173
+ */
174
+ export declare class CustomDatabaseStorage implements DatabaseStorage {
175
+ #private;
176
+ constructor(factory: DatabaseFactory);
177
+ open(name: string, version: number, onUpgrade?: (event: DatabaseUpgradeEvent) => void): Promise<Database>;
178
+ get(name: string): Database;
179
+ close(name: string): Promise<void>;
180
+ closeAll(): Promise<void>;
181
+ }
182
+ /**
183
+ * Create a DatabaseFactory from declarative config.
184
+ *
185
+ * This dynamically imports the driver module at runtime, following
186
+ * the same pattern as createDirectoryFactory and createCacheFactory.
187
+ *
188
+ * @param configs - The databases config from shovel.json
189
+ * @returns An async factory function that creates databases by name
190
+ *
191
+ * @example
192
+ * ```typescript
193
+ * // In platform adapter:
194
+ * const factory = createDatabaseFactory(config.databases);
195
+ * return new CustomDatabaseStorage(factory);
196
+ * ```
197
+ */
198
+ export declare function createDatabaseFactory(configs: Record<string, DatabaseConfig>): DatabaseFactory;
96
199
  /** Symbol for ending dispatch phase (internal use only) */
97
200
  declare const kEndDispatchPhase: unique symbol;
98
201
  /** Symbol for checking if extensions are allowed (internal use only) */
99
202
  declare const kCanExtend: unique symbol;
100
203
  /**
101
- * ExtendableEvent base class following ServiceWorker spec
102
- * Standard constructor: new ExtendableEvent(type) or new ExtendableEvent(type, options)
204
+ * Shovel's ExtendableEvent implementation following ServiceWorker spec.
205
+ *
206
+ * Standard constructor: new ShovelExtendableEvent(type) or new ShovelExtendableEvent(type, options)
103
207
  *
104
208
  * Per spec, waitUntil() can be called:
105
209
  * 1. Synchronously during event dispatch, OR
@@ -107,7 +211,7 @@ declare const kCanExtend: unique symbol;
107
211
  *
108
212
  * See: https://github.com/w3c/ServiceWorker/issues/771
109
213
  */
110
- export declare class ExtendableEvent extends Event {
214
+ export declare class ShovelExtendableEvent extends Event implements ExtendableEvent {
111
215
  #private;
112
216
  constructor(type: string, eventInitDict?: EventInit);
113
217
  waitUntil(promise: Promise<any>): void;
@@ -118,27 +222,48 @@ export declare class ExtendableEvent extends Event {
118
222
  [kCanExtend](): boolean;
119
223
  }
120
224
  /**
121
- * ServiceWorker-style fetch event
225
+ * Options for ShovelFetchEvent constructor (non-standard Shovel extension)
226
+ */
227
+ export interface ShovelFetchEventInit extends EventInit {
228
+ /**
229
+ * Platform-provided callback for extending request lifetime.
230
+ * Called automatically when waitUntil() is invoked.
231
+ * (e.g., Cloudflare's ctx.waitUntil)
232
+ */
233
+ platformWaitUntil?: (promise: Promise<unknown>) => void;
234
+ }
235
+ /**
236
+ * Shovel's FetchEvent implementation.
237
+ *
238
+ * Platforms can subclass this to add platform-specific properties (e.g., env bindings).
239
+ * The platformWaitUntil hook allows platforms to extend request lifetime properly.
122
240
  */
123
- export declare class FetchEvent extends ExtendableEvent {
241
+ export declare class ShovelFetchEvent extends ShovelExtendableEvent implements FetchEvent {
124
242
  #private;
125
243
  readonly request: Request;
126
244
  readonly cookieStore: RequestCookieStore;
127
- constructor(request: Request, eventInitDict?: EventInit);
245
+ readonly clientId: string;
246
+ readonly handled: Promise<undefined>;
247
+ readonly preloadResponse: Promise<any>;
248
+ readonly resultingClientId: string;
249
+ constructor(request: Request, options?: ShovelFetchEventInit);
250
+ waitUntil(promise: Promise<any>): void;
128
251
  respondWith(response: Response | Promise<Response>): void;
129
252
  getResponse(): Promise<Response> | null;
130
253
  hasResponded(): boolean;
254
+ /** The URL of the request (convenience property) */
255
+ get url(): string;
131
256
  }
132
257
  /**
133
- * ServiceWorker-style install event
258
+ * Shovel's InstallEvent implementation
134
259
  */
135
- export declare class InstallEvent extends ExtendableEvent {
260
+ export declare class ShovelInstallEvent extends ShovelExtendableEvent {
136
261
  constructor(eventInitDict?: EventInit);
137
262
  }
138
263
  /**
139
- * ServiceWorker-style activate event
264
+ * Shovel's ActivateEvent implementation
140
265
  */
141
- export declare class ActivateEvent extends ExtendableEvent {
266
+ export declare class ShovelActivateEvent extends ShovelExtendableEvent {
142
267
  constructor(eventInitDict?: EventInit);
143
268
  }
144
269
  /**
@@ -194,7 +319,7 @@ export interface ExtendableMessageEventInit extends EventInit {
194
319
  source?: Client | ServiceWorker | MessagePort | null;
195
320
  ports?: MessagePort[];
196
321
  }
197
- export declare class ExtendableMessageEvent extends ExtendableEvent {
322
+ export declare class ExtendableMessageEvent extends ShovelExtendableEvent {
198
323
  readonly data: any;
199
324
  readonly origin: string;
200
325
  readonly lastEventId: string;
@@ -261,8 +386,13 @@ export declare class ShovelServiceWorkerRegistration extends EventTarget impleme
261
386
  activate(): Promise<void>;
262
387
  /**
263
388
  * Handle a fetch request (Shovel extension)
389
+ *
390
+ * Platforms create a ShovelFetchEvent (or subclass) with platform-specific
391
+ * properties and hooks, then pass it to this method for dispatching.
392
+ *
393
+ * @param event - The fetch event to handle (created by platform adapter)
264
394
  */
265
- handleRequest(request: Request): Promise<Response>;
395
+ handleRequest(event: ShovelFetchEvent): Promise<Response>;
266
396
  /**
267
397
  * Check if ready to handle requests (Shovel extension)
268
398
  */
@@ -351,7 +481,7 @@ export interface NotificationEventInit extends EventInit {
351
481
  notification: Notification;
352
482
  reply?: string | null;
353
483
  }
354
- export declare class NotificationEvent extends ExtendableEvent {
484
+ export declare class NotificationEvent extends ShovelExtendableEvent {
355
485
  readonly action: string;
356
486
  readonly notification: Notification;
357
487
  readonly reply: string | null;
@@ -363,7 +493,7 @@ export declare class NotificationEvent extends ExtendableEvent {
363
493
  export interface PushEventInit extends EventInit {
364
494
  data?: PushMessageData | null;
365
495
  }
366
- export declare class PushEvent extends ExtendableEvent {
496
+ export declare class PushEvent extends ShovelExtendableEvent {
367
497
  readonly data: PushMessageData | null;
368
498
  constructor(type: string, eventInitDict?: PushEventInit);
369
499
  }
@@ -387,7 +517,7 @@ export interface SyncEventInit extends EventInit {
387
517
  tag: string;
388
518
  lastChance?: boolean;
389
519
  }
390
- export declare class SyncEvent extends ExtendableEvent {
520
+ export declare class SyncEvent extends ShovelExtendableEvent {
391
521
  readonly tag: string;
392
522
  readonly lastChance: boolean;
393
523
  constructor(type: string, eventInitDict: SyncEventInit);
@@ -418,6 +548,8 @@ export interface ServiceWorkerGlobalsOptions {
418
548
  registration: ServiceWorkerRegistration;
419
549
  /** Directory storage (file system access) - REQUIRED */
420
550
  directories: DirectoryStorage;
551
+ /** Database storage - OPTIONAL */
552
+ databases?: DatabaseStorage;
421
553
  /** Logger storage (logging access) - REQUIRED */
422
554
  loggers: LoggerStorage;
423
555
  /** Cache storage (required by ServiceWorkerGlobalScope) - REQUIRED */
@@ -452,6 +584,7 @@ export declare class ServiceWorkerGlobals implements ServiceWorkerGlobalScope {
452
584
  readonly registration: ServiceWorkerRegistration;
453
585
  readonly caches: CacheStorage;
454
586
  readonly directories: DirectoryStorage;
587
+ readonly databases?: DatabaseStorage;
455
588
  readonly loggers: LoggerStorage;
456
589
  readonly clients: Clients;
457
590
  get cookieStore(): any;
@@ -518,44 +651,20 @@ export declare class ServiceWorkerGlobals implements ServiceWorkerGlobalScope {
518
651
  */
519
652
  restore(): void;
520
653
  }
521
- /** Log level for filtering */
522
- export type LogLevel = "debug" | "info" | "warning" | "error";
523
- /** Sink configuration */
524
- export interface SinkConfig {
525
- provider: string;
526
- /** Pre-imported factory function (from build-time code generation) */
527
- factory?: (options: Record<string, unknown>) => unknown;
528
- /** Provider-specific options (path, maxSize, etc.) */
529
- [key: string]: any;
530
- }
531
- /** Per-category logging configuration */
532
- export interface CategoryLoggingConfig {
533
- level?: LogLevel;
534
- sinks?: SinkConfig[];
535
- }
536
- export interface LoggingConfig {
537
- /** Default log level. Defaults to "info" */
538
- level?: LogLevel;
539
- /** Default sinks. Defaults to console */
540
- sinks?: SinkConfig[];
541
- /** Per-category config (inherits from top-level, can override level and/or sinks) */
542
- categories?: Record<string, CategoryLoggingConfig>;
543
- }
544
- /** Processed logging config with all defaults applied */
545
- export interface ProcessedLoggingConfig {
546
- level: LogLevel;
547
- sinks: SinkConfig[];
548
- categories: Record<string, CategoryLoggingConfig>;
549
- }
550
654
  /** Cache provider configuration */
551
655
  export interface CacheConfig {
552
- provider?: string;
656
+ /** Reified implementation (class or factory from build-time code generation) */
657
+ impl?: (new (name: string, options?: any) => Cache) | ((name: string, options?: any) => Cache);
658
+ /** Additional options passed to the constructor */
553
659
  [key: string]: unknown;
554
660
  }
555
661
  /** Directory (filesystem) provider configuration */
556
662
  export interface DirectoryConfig {
557
- provider?: string;
663
+ /** Reified implementation (class or factory from build-time code generation) */
664
+ impl?: (new (name: string, options?: any) => FileSystemDirectoryHandle) | ((name: string, options?: any) => FileSystemDirectoryHandle);
665
+ /** Custom path for filesystem directories */
558
666
  path?: string;
667
+ /** Additional options passed to the constructor */
559
668
  [key: string]: unknown;
560
669
  }
561
670
  /** Shovel application configuration (from shovel.json) */
@@ -567,16 +676,160 @@ export interface ShovelConfig {
567
676
  logging?: LoggingConfig;
568
677
  caches?: Record<string, CacheConfig>;
569
678
  directories?: Record<string, DirectoryConfig>;
679
+ databases?: Record<string, DatabaseConfig>;
680
+ }
681
+ /**
682
+ * Creates a directory factory function for CustomDirectoryStorage.
683
+ *
684
+ * Configs must have impl pre-imported (from generated config module).
685
+ * Paths are expected to be already resolved at build time by the path syntax parser.
686
+ * Runtime paths (like [tmpdir]) are evaluated as expressions in the generated config.
687
+ */
688
+ export declare function createDirectoryFactory(configs: Record<string, DirectoryConfig>): (name: string) => Promise<FileSystemDirectoryHandle>;
689
+ export interface CacheFactoryOptions {
690
+ /** Cache configurations with pre-imported CacheClass (from generated config module) */
691
+ configs: Record<string, CacheConfig>;
692
+ /** If true, use PostMessageCache (for workers communicating with main thread) */
693
+ usePostMessage?: boolean;
694
+ }
695
+ /**
696
+ * Creates a cache factory function for CustomCacheStorage.
697
+ * Configs must have impl pre-imported (from generated config module).
698
+ */
699
+ export declare function createCacheFactory(options: CacheFactoryOptions): (name: string) => Promise<Cache>;
700
+ /**
701
+ * Worker message types for the message loop
702
+ */
703
+ export interface WorkerRequestMessage {
704
+ type: "request";
705
+ request: {
706
+ url: string;
707
+ method: string;
708
+ headers: Record<string, string>;
709
+ body?: ArrayBuffer | null;
710
+ };
711
+ requestID: number;
712
+ }
713
+ export interface WorkerResponseMessage {
714
+ type: "response";
715
+ response: {
716
+ status: number;
717
+ statusText: string;
718
+ headers: Record<string, string>;
719
+ body: ArrayBuffer;
720
+ };
721
+ requestID: number;
722
+ }
723
+ export interface WorkerErrorMessage {
724
+ type: "error";
725
+ error: string;
726
+ stack?: string;
727
+ requestID?: number;
728
+ }
729
+ /**
730
+ * Options for initializing the worker runtime
731
+ */
732
+ export interface InitWorkerRuntimeOptions {
733
+ /** Shovel configuration (from shovel:config) */
734
+ config: ShovelConfig;
735
+ }
736
+ /**
737
+ * Result from initializing the worker runtime
738
+ */
739
+ export interface InitWorkerRuntimeResult {
740
+ /** The ServiceWorker registration instance */
741
+ registration: ShovelServiceWorkerRegistration;
742
+ /** The installed ServiceWorkerGlobals scope */
743
+ scope: ServiceWorkerGlobals;
744
+ /** Cache storage instance */
745
+ caches: CustomCacheStorage;
746
+ /** Directory storage instance */
747
+ directories: CustomDirectoryStorage;
748
+ /** Database storage instance (if configured) */
749
+ databases?: CustomDatabaseStorage;
750
+ /** Logger storage instance */
751
+ loggers: CustomLoggerStorage;
752
+ }
753
+ /**
754
+ * Initialize the worker runtime environment.
755
+ * Sets up ServiceWorkerGlobals, caches, directories, and logging.
756
+ *
757
+ * This should be called at the top of a worker entry point before importing user code.
758
+ *
759
+ * @example
760
+ * ```typescript
761
+ * import {config} from "shovel:config";
762
+ * import {initWorkerRuntime, startWorkerMessageLoop} from "@b9g/platform/runtime";
763
+ *
764
+ * const {registration} = await initWorkerRuntime({config});
765
+ *
766
+ * // Import user code (registers event handlers)
767
+ * import "./server.js";
768
+ *
769
+ * // Run lifecycle and start message loop
770
+ * await registration.install();
771
+ * await registration.activate();
772
+ * startWorkerMessageLoop(registration);
773
+ * ```
774
+ */
775
+ export declare function initWorkerRuntime(options: InitWorkerRuntimeOptions): Promise<InitWorkerRuntimeResult>;
776
+ /**
777
+ * Options for the worker message loop
778
+ */
779
+ export interface WorkerMessageLoopOptions {
780
+ registration: ShovelServiceWorkerRegistration;
781
+ databases?: CustomDatabaseStorage;
782
+ caches?: CacheStorage;
783
+ }
784
+ /**
785
+ * Start the worker message loop for handling requests.
786
+ * This function sets up message handling for request/response communication
787
+ * with the main thread via postMessage.
788
+ *
789
+ * @param options - The registration and resources to manage
790
+ */
791
+ export declare function startWorkerMessageLoop(options: ShovelServiceWorkerRegistration | WorkerMessageLoopOptions): void;
792
+ /** Log level for filtering */
793
+ export type LogLevel = "debug" | "info" | "warning" | "error";
794
+ /** Sink configuration */
795
+ export interface SinkConfig {
796
+ /** Reified implementation (factory function from build-time code generation) */
797
+ impl?: (...args: any[]) => unknown;
798
+ /** Additional options passed to the factory (path, maxSize, etc.) */
799
+ [key: string]: unknown;
800
+ }
801
+ /** Logger configuration - matches LogTape's logger config structure */
802
+ export interface LoggerConfig {
803
+ /** Category as string or array for hierarchy. e.g. "myapp" or ["myapp", "db"] */
804
+ category: string | string[];
805
+ /** Log level for this category. Inherits from parent if not specified. */
806
+ level?: LogLevel;
807
+ /** Sink names to add. Inherits from parent by default. */
808
+ sinks?: string[];
809
+ /** Set to "override" to replace parent sinks instead of inherit */
810
+ parentSinks?: "override";
811
+ }
812
+ export interface LoggingConfig {
813
+ /** Named sinks. "console" is always available implicitly. */
814
+ sinks?: Record<string, SinkConfig>;
815
+ /** Logger configurations. Shovel provides defaults for ["shovel", ...] categories. */
816
+ loggers?: LoggerConfig[];
817
+ }
818
+ /** Processed logging config with all defaults applied */
819
+ export interface ProcessedLoggingConfig {
820
+ sinks: Record<string, SinkConfig>;
821
+ loggers: LoggerConfig[];
570
822
  }
571
823
  /**
572
824
  * Configure LogTape logging based on Shovel config.
573
825
  * Call this in both main thread and workers.
574
826
  *
575
- * @param loggingConfig - The logging configuration (sinks defaults to console)
576
- * @param options - Additional options
577
- * @param options.reset - Whether to reset existing LogTape config (default: true)
827
+ * Uses LogTape-aligned config structure:
828
+ * - Named sinks (console is implicit)
829
+ * - Loggers array with category hierarchy support
830
+ * - Shovel provides default loggers for ["shovel", ...] categories
831
+ *
832
+ * @param loggingConfig - The logging configuration
578
833
  */
579
- export declare function configureLogging(loggingConfig: LoggingConfig, options?: {
580
- reset?: boolean;
581
- }): Promise<void>;
834
+ export declare function configureLogging(loggingConfig: LoggingConfig): Promise<void>;
582
835
  export {};