@dotdo/postgres 0.1.1 → 0.1.3

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.
Files changed (111) hide show
  1. package/README.md +73 -1
  2. package/dist/client/index.d.ts +47 -0
  3. package/dist/client/index.d.ts.map +1 -0
  4. package/dist/client/index.js +47 -0
  5. package/dist/client/index.js.map +1 -0
  6. package/dist/client/postgres-client.d.ts +273 -0
  7. package/dist/client/postgres-client.d.ts.map +1 -0
  8. package/dist/client/postgres-client.js +389 -0
  9. package/dist/client/postgres-client.js.map +1 -0
  10. package/dist/client/types.d.ts +167 -0
  11. package/dist/client/types.d.ts.map +1 -0
  12. package/dist/client/types.js +7 -0
  13. package/dist/client/types.js.map +1 -0
  14. package/dist/do/index.d.ts +18 -0
  15. package/dist/do/index.d.ts.map +1 -0
  16. package/dist/do/index.js +18 -0
  17. package/dist/do/index.js.map +1 -0
  18. package/dist/do/postgres.d.ts +110 -0
  19. package/dist/do/postgres.d.ts.map +1 -0
  20. package/dist/do/postgres.js +266 -0
  21. package/dist/do/postgres.js.map +1 -0
  22. package/dist/do/sql.d.ts +92 -0
  23. package/dist/do/sql.d.ts.map +1 -0
  24. package/dist/do/sql.js +204 -0
  25. package/dist/do/sql.js.map +1 -0
  26. package/dist/index.d.ts +25 -30
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +29 -30
  29. package/dist/index.js.map +1 -1
  30. package/dist/mcp/binding.d.ts +47 -0
  31. package/dist/mcp/binding.d.ts.map +1 -0
  32. package/dist/mcp/binding.js +183 -0
  33. package/dist/mcp/binding.js.map +1 -0
  34. package/dist/mcp/index.d.ts +92 -0
  35. package/dist/mcp/index.d.ts.map +1 -0
  36. package/dist/mcp/index.js +91 -0
  37. package/dist/mcp/index.js.map +1 -0
  38. package/dist/mcp/server.d.ts +62 -0
  39. package/dist/mcp/server.d.ts.map +1 -0
  40. package/dist/mcp/server.js +278 -0
  41. package/dist/mcp/server.js.map +1 -0
  42. package/dist/mcp/tools.d.ts +58 -0
  43. package/dist/mcp/tools.d.ts.map +1 -0
  44. package/dist/mcp/tools.js +356 -0
  45. package/dist/mcp/tools.js.map +1 -0
  46. package/dist/mcp/types.d.ts +139 -0
  47. package/dist/mcp/types.d.ts.map +1 -0
  48. package/dist/mcp/types.js +7 -0
  49. package/dist/mcp/types.js.map +1 -0
  50. package/dist/pglite/workers-pglite.d.ts +13 -4
  51. package/dist/pglite/workers-pglite.d.ts.map +1 -1
  52. package/dist/pglite/workers-pglite.js +110 -5
  53. package/dist/pglite/workers-pglite.js.map +1 -1
  54. package/dist/pglite-assets/pglite.data +0 -0
  55. package/dist/pglite-assets/pglite.wasm +0 -0
  56. package/dist/worker/auth.d.ts.map +1 -1
  57. package/dist/worker/auth.js +16 -6
  58. package/dist/worker/auth.js.map +1 -1
  59. package/dist/worker/background-pglite-manager.d.ts +243 -0
  60. package/dist/worker/background-pglite-manager.d.ts.map +1 -0
  61. package/dist/worker/background-pglite-manager.js +528 -0
  62. package/dist/worker/background-pglite-manager.js.map +1 -0
  63. package/dist/worker/do-pglite-manager.d.ts +77 -0
  64. package/dist/worker/do-pglite-manager.d.ts.map +1 -1
  65. package/dist/worker/do-pglite-manager.js +189 -12
  66. package/dist/worker/do-pglite-manager.js.map +1 -1
  67. package/dist/worker/entry.d.ts.map +1 -1
  68. package/dist/worker/entry.js +108 -26
  69. package/dist/worker/entry.js.map +1 -1
  70. package/dist/worker/index.d.ts +7 -1
  71. package/dist/worker/index.d.ts.map +1 -1
  72. package/dist/worker/index.js +19 -1
  73. package/dist/worker/index.js.map +1 -1
  74. package/dist/worker/lazy-pglite-manager.d.ts +242 -0
  75. package/dist/worker/lazy-pglite-manager.d.ts.map +1 -0
  76. package/dist/worker/lazy-pglite-manager.js +463 -0
  77. package/dist/worker/lazy-pglite-manager.js.map +1 -0
  78. package/package.json +20 -6
  79. package/src/client/index.ts +61 -0
  80. package/src/client/postgres-client.ts +442 -0
  81. package/src/client/types.ts +211 -0
  82. package/src/do/index.ts +18 -0
  83. package/src/do/postgres.ts +367 -0
  84. package/src/do/sql.ts +280 -0
  85. package/src/index.ts +50 -30
  86. package/src/mcp/binding.ts +236 -0
  87. package/src/mcp/index.ts +122 -0
  88. package/src/mcp/server.ts +361 -0
  89. package/src/mcp/tools.ts +464 -0
  90. package/src/mcp/types.ts +148 -0
  91. package/src/pglite/workers-pglite.ts +141 -12
  92. package/src/pglite-assets/pglite.data +0 -0
  93. package/src/pglite-assets/pglite.wasm +0 -0
  94. package/src/worker/auth.ts +17 -6
  95. package/src/worker/background-pglite-manager.ts +680 -0
  96. package/src/worker/do-pglite-manager.ts +235 -19
  97. package/src/worker/entry.ts +112 -30
  98. package/src/worker/index.ts +71 -1
  99. package/src/worker/lazy-pglite-manager.ts +595 -0
  100. package/dist/iceberg/duckdb-wasm.d.ts +0 -447
  101. package/dist/iceberg/duckdb-wasm.d.ts.map +0 -1
  102. package/dist/iceberg/duckdb-wasm.js +0 -600
  103. package/dist/iceberg/duckdb-wasm.js.map +0 -1
  104. package/dist/iceberg/test-fixtures.d.ts +0 -151
  105. package/dist/iceberg/test-fixtures.d.ts.map +0 -1
  106. package/dist/iceberg/test-fixtures.js +0 -446
  107. package/dist/iceberg/test-fixtures.js.map +0 -1
  108. package/dist/worker/__mocks__/cloudflare-workers.d.ts +0 -31
  109. package/dist/worker/__mocks__/cloudflare-workers.d.ts.map +0 -1
  110. package/dist/worker/__mocks__/cloudflare-workers.js +0 -33
  111. package/dist/worker/__mocks__/cloudflare-workers.js.map +0 -1
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Lazy PGLite Manager for PostgresDO
3
+ *
4
+ * SPIKE: Lazy WASM Loading Experiment
5
+ *
6
+ * This module implements a lazy loading variant of PGLiteManager that defers
7
+ * WASM loading until the first query is executed, rather than loading on
8
+ * DO instantiation.
9
+ *
10
+ * ## Hypothesis
11
+ *
12
+ * Loading WASM only on first query (not on DO instantiation) may improve:
13
+ * 1. Cold start perception (faster initial response for non-query endpoints)
14
+ * 2. Memory usage (don't load if no queries made)
15
+ *
16
+ * ## Trade-offs
17
+ *
18
+ * - Eager loading: ~1200ms cold start, all subsequent requests fast
19
+ * - Lazy loading: ~0ms until first query, first query pays ~1200ms, subsequent fast
20
+ *
21
+ * For workloads where:
22
+ * - Many requests hit non-query endpoints (health checks, metadata) -> lazy wins
23
+ * - All requests are queries -> eager wins (same total latency, better predictability)
24
+ * - Mixed workload -> depends on query ratio
25
+ *
26
+ * ## Implementation
27
+ *
28
+ * The manager provides:
29
+ * - `isWASMLoaded()` - Check if WASM is loaded without triggering load
30
+ * - `ensureWASMLoaded()` - Explicitly trigger WASM loading
31
+ * - `query()` - Execute query (lazy loads WASM if needed)
32
+ * - Promise deduplication for concurrent first queries
33
+ *
34
+ * @module worker/lazy-pglite-manager
35
+ */
36
+ import type { PostgresConfig } from './types';
37
+ import { PluginManager } from './plugin-manager';
38
+ import type { PGliteLike } from './do-pglite-manager';
39
+ /**
40
+ * Check if the lazy hoisted PGLite instance exists.
41
+ */
42
+ export declare function hasLazyHoistedPglite(): boolean;
43
+ /**
44
+ * Get diagnostics about the lazy hoisted WASM state.
45
+ */
46
+ export declare function getLazyHoistedPgliteDiagnostics(): {
47
+ hasInstance: boolean;
48
+ hasPendingPromise: boolean;
49
+ moduleInstanceId: string;
50
+ wasmLoadedAt: number | null;
51
+ timeSinceLoad: number | null;
52
+ };
53
+ /**
54
+ * Reset the lazy hoisted PGLite instance.
55
+ * WARNING: This should only be used in tests.
56
+ * @internal
57
+ */
58
+ export declare function resetLazyHoistedPglite(): void;
59
+ /**
60
+ * WASM loading state for diagnostics
61
+ */
62
+ export type WASMLoadingState = 'not_loaded' | 'loading' | 'loaded' | 'error';
63
+ /**
64
+ * Configuration for Lazy PGLite manager
65
+ */
66
+ export interface LazyPGLiteManagerConfig {
67
+ /** Database name */
68
+ database?: string;
69
+ /** Enable debug mode */
70
+ debug?: boolean;
71
+ /** Plugin configuration */
72
+ plugins?: PostgresConfig['plugins'];
73
+ /** Custom PGLite factory for testing */
74
+ createPGLite?: () => Promise<PGliteLike>;
75
+ /**
76
+ * Disable WASM hoisting optimization.
77
+ * When false (default), the PGLite WASM instance is cached at module level.
78
+ * @default false
79
+ */
80
+ disableHoisting?: boolean;
81
+ }
82
+ /**
83
+ * LazyPGLiteManager defers WASM loading until the first query.
84
+ *
85
+ * Unlike the eager PGLiteManager, this manager does NOT load WASM during
86
+ * initialize(). WASM is only loaded when query() or ensureWASMLoaded() is called.
87
+ *
88
+ * Benefits:
89
+ * - Non-query endpoints (health, metadata, stats) respond immediately
90
+ * - Memory is not consumed until queries are actually executed
91
+ * - Better cold start perception for mixed workloads
92
+ *
93
+ * Trade-offs:
94
+ * - First query incurs the full WASM loading time (~1200ms)
95
+ * - Less predictable query latency (first vs subsequent)
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * const manager = new LazyPGLiteManager({ database: 'mydb' })
100
+ *
101
+ * // Initialize (fast - no WASM loading)
102
+ * await manager.initialize()
103
+ *
104
+ * // Check if WASM is loaded (without triggering load)
105
+ * console.log(manager.isWASMLoaded()) // false
106
+ *
107
+ * // First query triggers WASM load (~1200ms)
108
+ * const result1 = await manager.query('SELECT 1')
109
+ *
110
+ * // Now WASM is loaded
111
+ * console.log(manager.isWASMLoaded()) // true
112
+ *
113
+ * // Subsequent queries are fast (~ms)
114
+ * const result2 = await manager.query('SELECT 2')
115
+ * ```
116
+ */
117
+ export declare class LazyPGLiteManager {
118
+ private pglite;
119
+ private initialized;
120
+ private loadingState;
121
+ private loadError;
122
+ private config;
123
+ private pluginManager;
124
+ /** Track whether this manager is using the hoisted instance */
125
+ private usingHoistedInstance;
126
+ /** Timing metrics for diagnostics */
127
+ private metrics;
128
+ constructor(config?: LazyPGLiteManagerConfig);
129
+ /**
130
+ * Check if the manager is initialized.
131
+ * Note: This returns true after initialize() is called, even if WASM is not loaded.
132
+ */
133
+ isInitialized(): boolean;
134
+ /**
135
+ * Check if WASM is loaded (without triggering load).
136
+ * This is the key difference from eager loading.
137
+ */
138
+ isWASMLoaded(): boolean;
139
+ /**
140
+ * Get the current WASM loading state.
141
+ */
142
+ getLoadingState(): WASMLoadingState;
143
+ /**
144
+ * Get any error that occurred during WASM loading.
145
+ */
146
+ getLoadError(): Error | null;
147
+ /**
148
+ * Get the PGLite instance (throws if not loaded).
149
+ * For lazy loading, prefer using query() which handles loading automatically.
150
+ */
151
+ getInstance(): PGliteLike;
152
+ /**
153
+ * Get the PGLite instance or null if not loaded.
154
+ */
155
+ getInstanceOrNull(): PGliteLike | null;
156
+ /**
157
+ * Get the plugin manager.
158
+ */
159
+ getPluginManager(): PluginManager;
160
+ /**
161
+ * Get timing metrics for diagnostics.
162
+ */
163
+ getMetrics(): {
164
+ initializeCalledAt: number | null;
165
+ firstQueryAt: number | null;
166
+ wasmLoadStartAt: number | null;
167
+ wasmLoadEndAt: number | null;
168
+ wasmLoadDurationMs: number | null;
169
+ timeToFirstQuery: number | null;
170
+ };
171
+ /**
172
+ * Initialize the manager (does NOT load WASM).
173
+ *
174
+ * This is intentionally fast - WASM loading is deferred to first query.
175
+ * Safe to call multiple times.
176
+ */
177
+ initialize(): Promise<void>;
178
+ /**
179
+ * Explicitly ensure WASM is loaded.
180
+ *
181
+ * Use this if you want to pre-warm WASM loading before the first query,
182
+ * but still want the option to skip loading entirely for non-query requests.
183
+ */
184
+ ensureWASMLoaded(): Promise<void>;
185
+ /**
186
+ * Internal method to load WASM.
187
+ */
188
+ private loadWASM;
189
+ /**
190
+ * Execute a query (lazy loads WASM if needed).
191
+ *
192
+ * This is the primary method for interacting with the database.
193
+ * On first call, it will trigger WASM loading.
194
+ */
195
+ query<T = unknown>(sql: string, params?: unknown[]): Promise<{
196
+ rows: T[];
197
+ fields: {
198
+ name: string;
199
+ dataTypeID: number;
200
+ }[];
201
+ affectedRows?: number;
202
+ }>;
203
+ /**
204
+ * Check if PGLite is responsive by running a simple query.
205
+ * This will trigger WASM loading if not already loaded.
206
+ */
207
+ checkLiveness(): Promise<boolean>;
208
+ /**
209
+ * Close PGLite instance.
210
+ */
211
+ close(timeoutMs?: number): Promise<void>;
212
+ /**
213
+ * Reset manager state (for testing or hibernation recovery).
214
+ */
215
+ reset(): void;
216
+ /**
217
+ * Check if this manager is using the hoisted WASM instance.
218
+ */
219
+ isUsingHoistedInstance(): boolean;
220
+ /**
221
+ * Get comprehensive diagnostics about lazy loading state.
222
+ */
223
+ getDiagnostics(): {
224
+ initialized: boolean;
225
+ wasmLoaded: boolean;
226
+ loadingState: WASMLoadingState;
227
+ usingHoistedInstance: boolean;
228
+ metrics: ReturnType<LazyPGLiteManager['getMetrics']>;
229
+ hoisted: {
230
+ hasInstance: boolean;
231
+ hasPendingPromise: boolean;
232
+ moduleInstanceId: string;
233
+ wasmLoadedAt: number | null;
234
+ timeSinceLoad: number | null;
235
+ };
236
+ };
237
+ }
238
+ /**
239
+ * Factory function to create a LazyPGLiteManager.
240
+ */
241
+ export declare function createLazyPGLiteManager(config?: LazyPGLiteManagerConfig): LazyPGLiteManager;
242
+ //# sourceMappingURL=lazy-pglite-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lazy-pglite-manager.d.ts","sourceRoot":"","sources":["../../src/worker/lazy-pglite-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,463 @@
1
+ /**
2
+ * Lazy PGLite Manager for PostgresDO
3
+ *
4
+ * SPIKE: Lazy WASM Loading Experiment
5
+ *
6
+ * This module implements a lazy loading variant of PGLiteManager that defers
7
+ * WASM loading until the first query is executed, rather than loading on
8
+ * DO instantiation.
9
+ *
10
+ * ## Hypothesis
11
+ *
12
+ * Loading WASM only on first query (not on DO instantiation) may improve:
13
+ * 1. Cold start perception (faster initial response for non-query endpoints)
14
+ * 2. Memory usage (don't load if no queries made)
15
+ *
16
+ * ## Trade-offs
17
+ *
18
+ * - Eager loading: ~1200ms cold start, all subsequent requests fast
19
+ * - Lazy loading: ~0ms until first query, first query pays ~1200ms, subsequent fast
20
+ *
21
+ * For workloads where:
22
+ * - Many requests hit non-query endpoints (health checks, metadata) -> lazy wins
23
+ * - All requests are queries -> eager wins (same total latency, better predictability)
24
+ * - Mixed workload -> depends on query ratio
25
+ *
26
+ * ## Implementation
27
+ *
28
+ * The manager provides:
29
+ * - `isWASMLoaded()` - Check if WASM is loaded without triggering load
30
+ * - `ensureWASMLoaded()` - Explicitly trigger WASM loading
31
+ * - `query()` - Execute query (lazy loads WASM if needed)
32
+ * - Promise deduplication for concurrent first queries
33
+ *
34
+ * @module worker/lazy-pglite-manager
35
+ */
36
+ import { PluginManager } from './plugin-manager';
37
+ // Import WASM module and data bundle for Workers
38
+ // These are bundled at build time as CompiledWasm and Data modules
39
+ import pgliteWasm from '../pglite-assets/pglite.wasm';
40
+ import pgliteData from '../pglite-assets/pglite.data';
41
+ // Import Workers-compatible PGLite wrapper
42
+ import { createWorkersPGLite } from '../pglite/workers-pglite';
43
+ // =============================================================================
44
+ // Module-Level Lazy WASM State (for hoisting across DO reinstantiation)
45
+ // =============================================================================
46
+ /**
47
+ * Hoisted PGLite instance - survives DO class reinstantiation within same isolate.
48
+ * Same as eager loading, but only populated on first query.
49
+ */
50
+ let lazyHoistedPglite = null;
51
+ /**
52
+ * Promise for in-progress PGLite initialization.
53
+ * Prevents duplicate WASM loading when multiple queries execute concurrently.
54
+ */
55
+ let lazyHoistedPglitePromise = null;
56
+ /**
57
+ * Timestamp when WASM was first loaded (for diagnostics)
58
+ */
59
+ let wasmLoadedAt = null;
60
+ /**
61
+ * Unique identifier for this module instance (for debugging/diagnostics).
62
+ */
63
+ const LAZY_MODULE_INSTANCE_ID = Math.random().toString(36).slice(2, 10);
64
+ /**
65
+ * Check if the lazy hoisted PGLite instance exists.
66
+ */
67
+ export function hasLazyHoistedPglite() {
68
+ return lazyHoistedPglite !== null;
69
+ }
70
+ /**
71
+ * Get diagnostics about the lazy hoisted WASM state.
72
+ */
73
+ export function getLazyHoistedPgliteDiagnostics() {
74
+ const now = Date.now();
75
+ return {
76
+ hasInstance: lazyHoistedPglite !== null,
77
+ hasPendingPromise: lazyHoistedPglitePromise !== null,
78
+ moduleInstanceId: LAZY_MODULE_INSTANCE_ID,
79
+ wasmLoadedAt,
80
+ timeSinceLoad: wasmLoadedAt !== null ? now - wasmLoadedAt : null,
81
+ };
82
+ }
83
+ /**
84
+ * Reset the lazy hoisted PGLite instance.
85
+ * WARNING: This should only be used in tests.
86
+ * @internal
87
+ */
88
+ export function resetLazyHoistedPglite() {
89
+ lazyHoistedPglite = null;
90
+ lazyHoistedPglitePromise = null;
91
+ wasmLoadedAt = null;
92
+ }
93
+ /**
94
+ * Lazily get or create the hoisted PGLite instance.
95
+ *
96
+ * This function is called ONLY when a query is executed, not during
97
+ * manager initialization.
98
+ */
99
+ async function lazyGetOrCreateHoistedPglite(options) {
100
+ // Fast path: already initialized
101
+ if (lazyHoistedPglite) {
102
+ return lazyHoistedPglite;
103
+ }
104
+ // Deduplication: return existing promise if initialization in progress
105
+ if (lazyHoistedPglitePromise) {
106
+ return lazyHoistedPglitePromise;
107
+ }
108
+ const loadStartTime = performance.now();
109
+ // Start initialization
110
+ lazyHoistedPglitePromise = createWorkersPGLite({
111
+ wasmModule: pgliteWasm,
112
+ fsBundle: pgliteData,
113
+ database: options.database ?? 'postgres',
114
+ debug: options.debug ?? 0,
115
+ });
116
+ try {
117
+ lazyHoistedPglite = await lazyHoistedPglitePromise;
118
+ wasmLoadedAt = Date.now();
119
+ const loadDuration = performance.now() - loadStartTime;
120
+ console.log(`[LazyPGLiteManager] WASM LOADED (lazy, on first query) - took ${loadDuration.toFixed(2)}ms, instance: ${LAZY_MODULE_INSTANCE_ID}`);
121
+ return lazyHoistedPglite;
122
+ }
123
+ finally {
124
+ // Clear the promise after resolution (success or failure)
125
+ lazyHoistedPglitePromise = null;
126
+ }
127
+ }
128
+ // =============================================================================
129
+ // LazyPGLiteManager Class
130
+ // =============================================================================
131
+ /**
132
+ * LazyPGLiteManager defers WASM loading until the first query.
133
+ *
134
+ * Unlike the eager PGLiteManager, this manager does NOT load WASM during
135
+ * initialize(). WASM is only loaded when query() or ensureWASMLoaded() is called.
136
+ *
137
+ * Benefits:
138
+ * - Non-query endpoints (health, metadata, stats) respond immediately
139
+ * - Memory is not consumed until queries are actually executed
140
+ * - Better cold start perception for mixed workloads
141
+ *
142
+ * Trade-offs:
143
+ * - First query incurs the full WASM loading time (~1200ms)
144
+ * - Less predictable query latency (first vs subsequent)
145
+ *
146
+ * @example
147
+ * ```typescript
148
+ * const manager = new LazyPGLiteManager({ database: 'mydb' })
149
+ *
150
+ * // Initialize (fast - no WASM loading)
151
+ * await manager.initialize()
152
+ *
153
+ * // Check if WASM is loaded (without triggering load)
154
+ * console.log(manager.isWASMLoaded()) // false
155
+ *
156
+ * // First query triggers WASM load (~1200ms)
157
+ * const result1 = await manager.query('SELECT 1')
158
+ *
159
+ * // Now WASM is loaded
160
+ * console.log(manager.isWASMLoaded()) // true
161
+ *
162
+ * // Subsequent queries are fast (~ms)
163
+ * const result2 = await manager.query('SELECT 2')
164
+ * ```
165
+ */
166
+ export class LazyPGLiteManager {
167
+ pglite = null;
168
+ initialized = false;
169
+ loadingState = 'not_loaded';
170
+ loadError = null;
171
+ config;
172
+ pluginManager;
173
+ /** Track whether this manager is using the hoisted instance */
174
+ usingHoistedInstance = false;
175
+ /** Timing metrics for diagnostics */
176
+ metrics = {
177
+ initializeCalledAt: null,
178
+ firstQueryAt: null,
179
+ wasmLoadStartAt: null,
180
+ wasmLoadEndAt: null,
181
+ };
182
+ constructor(config = {}) {
183
+ this.config = {
184
+ database: config.database ?? 'postgres',
185
+ debug: config.debug ?? false,
186
+ };
187
+ if (config.plugins !== undefined) {
188
+ this.config.plugins = config.plugins;
189
+ }
190
+ if (config.createPGLite !== undefined) {
191
+ this.config.createPGLite = config.createPGLite;
192
+ }
193
+ if (config.disableHoisting !== undefined) {
194
+ this.config.disableHoisting = config.disableHoisting;
195
+ }
196
+ // Build plugin manager config
197
+ const pluginManagerConfig = {};
198
+ if (config.plugins !== undefined) {
199
+ pluginManagerConfig.plugins = config.plugins;
200
+ }
201
+ if (config.debug !== undefined) {
202
+ pluginManagerConfig.debug = config.debug;
203
+ }
204
+ this.pluginManager = new PluginManager(pluginManagerConfig);
205
+ }
206
+ /**
207
+ * Check if the manager is initialized.
208
+ * Note: This returns true after initialize() is called, even if WASM is not loaded.
209
+ */
210
+ isInitialized() {
211
+ return this.initialized;
212
+ }
213
+ /**
214
+ * Check if WASM is loaded (without triggering load).
215
+ * This is the key difference from eager loading.
216
+ */
217
+ isWASMLoaded() {
218
+ return this.pglite !== null;
219
+ }
220
+ /**
221
+ * Get the current WASM loading state.
222
+ */
223
+ getLoadingState() {
224
+ return this.loadingState;
225
+ }
226
+ /**
227
+ * Get any error that occurred during WASM loading.
228
+ */
229
+ getLoadError() {
230
+ return this.loadError;
231
+ }
232
+ /**
233
+ * Get the PGLite instance (throws if not loaded).
234
+ * For lazy loading, prefer using query() which handles loading automatically.
235
+ */
236
+ getInstance() {
237
+ if (!this.pglite) {
238
+ throw new Error('PGLite WASM not loaded. Call ensureWASMLoaded() or query() first.');
239
+ }
240
+ return this.pglite;
241
+ }
242
+ /**
243
+ * Get the PGLite instance or null if not loaded.
244
+ */
245
+ getInstanceOrNull() {
246
+ return this.pglite;
247
+ }
248
+ /**
249
+ * Get the plugin manager.
250
+ */
251
+ getPluginManager() {
252
+ return this.pluginManager;
253
+ }
254
+ /**
255
+ * Get timing metrics for diagnostics.
256
+ */
257
+ getMetrics() {
258
+ const wasmLoadDurationMs = this.metrics.wasmLoadStartAt !== null && this.metrics.wasmLoadEndAt !== null
259
+ ? this.metrics.wasmLoadEndAt - this.metrics.wasmLoadStartAt
260
+ : null;
261
+ const timeToFirstQuery = this.metrics.initializeCalledAt !== null && this.metrics.firstQueryAt !== null
262
+ ? this.metrics.firstQueryAt - this.metrics.initializeCalledAt
263
+ : null;
264
+ return {
265
+ ...this.metrics,
266
+ wasmLoadDurationMs,
267
+ timeToFirstQuery,
268
+ };
269
+ }
270
+ /**
271
+ * Initialize the manager (does NOT load WASM).
272
+ *
273
+ * This is intentionally fast - WASM loading is deferred to first query.
274
+ * Safe to call multiple times.
275
+ */
276
+ async initialize() {
277
+ if (this.initialized) {
278
+ return;
279
+ }
280
+ this.metrics.initializeCalledAt = performance.now();
281
+ this.initialized = true;
282
+ console.log(`[LazyPGLiteManager] Initialized (WASM not loaded yet)`);
283
+ }
284
+ /**
285
+ * Explicitly ensure WASM is loaded.
286
+ *
287
+ * Use this if you want to pre-warm WASM loading before the first query,
288
+ * but still want the option to skip loading entirely for non-query requests.
289
+ */
290
+ async ensureWASMLoaded() {
291
+ if (this.pglite) {
292
+ return;
293
+ }
294
+ await this.loadWASM();
295
+ }
296
+ /**
297
+ * Internal method to load WASM.
298
+ */
299
+ async loadWASM() {
300
+ if (this.pglite) {
301
+ return this.pglite;
302
+ }
303
+ if (this.loadingState === 'loading') {
304
+ // Wait for existing load to complete
305
+ if (lazyHoistedPglitePromise) {
306
+ const result = await lazyHoistedPglitePromise;
307
+ this.pglite = result;
308
+ this.loadingState = 'loaded';
309
+ return result;
310
+ }
311
+ }
312
+ this.loadingState = 'loading';
313
+ this.metrics.wasmLoadStartAt = performance.now();
314
+ try {
315
+ // Use custom factory if provided (for testing)
316
+ if (this.config.createPGLite) {
317
+ this.usingHoistedInstance = false;
318
+ this.pglite = await this.config.createPGLite();
319
+ }
320
+ else if (this.config.disableHoisting) {
321
+ // Create new instance without hoisting
322
+ this.usingHoistedInstance = false;
323
+ console.log(`[LazyPGLiteManager] WASM hoisting disabled - creating new instance`);
324
+ this.pglite = await createWorkersPGLite({
325
+ wasmModule: pgliteWasm,
326
+ fsBundle: pgliteData,
327
+ database: this.config.database ?? 'postgres',
328
+ debug: this.config.debug ? 1 : 0,
329
+ });
330
+ }
331
+ else {
332
+ // Use the lazy hoisted instance
333
+ const wasmWasAlreadyLoaded = lazyHoistedPglite !== null;
334
+ this.pglite = await lazyGetOrCreateHoistedPglite({
335
+ database: this.config.database,
336
+ debug: this.config.debug ? 1 : 0,
337
+ });
338
+ this.usingHoistedInstance = true;
339
+ if (wasmWasAlreadyLoaded) {
340
+ const diagnostics = getLazyHoistedPgliteDiagnostics();
341
+ console.log(`[LazyPGLiteManager] WASM REUSED (lazy hoisted) - loaded ${diagnostics.timeSinceLoad}ms ago, instance: ${diagnostics.moduleInstanceId}`);
342
+ }
343
+ }
344
+ this.metrics.wasmLoadEndAt = performance.now();
345
+ this.loadingState = 'loaded';
346
+ // Wait for ready if needed
347
+ if (this.pglite.waitReady) {
348
+ await this.pglite.waitReady;
349
+ }
350
+ // Run auto-create for plugins
351
+ await this.pluginManager.runAutoCreate(this.pglite);
352
+ return this.pglite;
353
+ }
354
+ catch (error) {
355
+ this.loadingState = 'error';
356
+ this.loadError = error instanceof Error ? error : new Error(String(error));
357
+ throw error;
358
+ }
359
+ }
360
+ /**
361
+ * Execute a query (lazy loads WASM if needed).
362
+ *
363
+ * This is the primary method for interacting with the database.
364
+ * On first call, it will trigger WASM loading.
365
+ */
366
+ async query(sql, params) {
367
+ // Track first query time
368
+ if (this.metrics.firstQueryAt === null) {
369
+ this.metrics.firstQueryAt = performance.now();
370
+ }
371
+ // Lazy load WASM if not loaded
372
+ const pglite = await this.loadWASM();
373
+ // Execute query
374
+ return pglite.query(sql, params);
375
+ }
376
+ /**
377
+ * Check if PGLite is responsive by running a simple query.
378
+ * This will trigger WASM loading if not already loaded.
379
+ */
380
+ async checkLiveness() {
381
+ try {
382
+ const result = await this.query('SELECT 1 as alive');
383
+ return result.rows.length > 0;
384
+ }
385
+ catch {
386
+ return false;
387
+ }
388
+ }
389
+ /**
390
+ * Close PGLite instance.
391
+ */
392
+ async close(timeoutMs = 5000) {
393
+ // If using hoisted instance, don't close it
394
+ if (this.usingHoistedInstance) {
395
+ console.log(`[LazyPGLiteManager] Skipping close of hoisted WASM instance (will be reused)`);
396
+ this.pglite = null;
397
+ this.initialized = false;
398
+ this.usingHoistedInstance = false;
399
+ this.loadingState = 'not_loaded';
400
+ return;
401
+ }
402
+ if (!this.pglite?.close) {
403
+ this.pglite = null;
404
+ this.initialized = false;
405
+ this.loadingState = 'not_loaded';
406
+ return;
407
+ }
408
+ try {
409
+ await Promise.race([
410
+ this.pglite.close(),
411
+ new Promise((_, reject) => setTimeout(() => reject(new Error('PGlite close timeout')), timeoutMs)),
412
+ ]);
413
+ }
414
+ catch (error) {
415
+ console.error('Error closing PGlite:', error);
416
+ }
417
+ this.pglite = null;
418
+ this.initialized = false;
419
+ this.loadingState = 'not_loaded';
420
+ }
421
+ /**
422
+ * Reset manager state (for testing or hibernation recovery).
423
+ */
424
+ reset() {
425
+ this.pglite = null;
426
+ this.initialized = false;
427
+ this.usingHoistedInstance = false;
428
+ this.loadingState = 'not_loaded';
429
+ this.loadError = null;
430
+ this.metrics = {
431
+ initializeCalledAt: null,
432
+ firstQueryAt: null,
433
+ wasmLoadStartAt: null,
434
+ wasmLoadEndAt: null,
435
+ };
436
+ }
437
+ /**
438
+ * Check if this manager is using the hoisted WASM instance.
439
+ */
440
+ isUsingHoistedInstance() {
441
+ return this.usingHoistedInstance;
442
+ }
443
+ /**
444
+ * Get comprehensive diagnostics about lazy loading state.
445
+ */
446
+ getDiagnostics() {
447
+ return {
448
+ initialized: this.initialized,
449
+ wasmLoaded: this.pglite !== null,
450
+ loadingState: this.loadingState,
451
+ usingHoistedInstance: this.usingHoistedInstance,
452
+ metrics: this.getMetrics(),
453
+ hoisted: getLazyHoistedPgliteDiagnostics(),
454
+ };
455
+ }
456
+ }
457
+ /**
458
+ * Factory function to create a LazyPGLiteManager.
459
+ */
460
+ export function createLazyPGLiteManager(config) {
461
+ return new LazyPGLiteManager(config);
462
+ }
463
+ //# sourceMappingURL=lazy-pglite-manager.js.map