@ducklings/workers 1.4.3-dev.1 → 1.4.3-dev.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.
package/README.md CHANGED
@@ -35,6 +35,71 @@ export default {
35
35
  };
36
36
  ```
37
37
 
38
+ ## Singleton Initialization (Recommended)
39
+
40
+ For production use, reuse the database and connection across requests to avoid re-initialization overhead:
41
+
42
+ ```typescript
43
+ import { init, DuckDB, type Connection } from '@ducklings/workers';
44
+ import wasmModule from '@ducklings/workers/wasm';
45
+
46
+ // Global state (reused across requests in the same Worker instance)
47
+ let db: DuckDB | null = null;
48
+ let conn: Connection | null = null;
49
+ let initialized = false;
50
+
51
+ async function ensureInitialized(): Promise<void> {
52
+ if (initialized && db && conn) {
53
+ return;
54
+ }
55
+
56
+ await init({ wasmModule });
57
+ db = new DuckDB();
58
+ conn = db.connect();
59
+ initialized = true;
60
+ }
61
+
62
+ export default {
63
+ async fetch(request: Request): Promise<Response> {
64
+ await ensureInitialized();
65
+
66
+ const rows = await conn!.query('SELECT 42 as answer');
67
+ return Response.json(rows);
68
+ }
69
+ };
70
+ ```
71
+
72
+ ## Vite Plugin
73
+
74
+ For projects using Vite with `@cloudflare/vite-plugin` to build Cloudflare Workers, we provide a plugin that handles WASM file resolution and copying:
75
+
76
+ ```typescript
77
+ // vite.config.ts
78
+ import { defineConfig } from 'vite';
79
+ import { cloudflare } from '@cloudflare/vite-plugin';
80
+ import { ducklingsWorkerPlugin } from '@ducklings/workers/vite-plugin';
81
+
82
+ export default defineConfig({
83
+ plugins: [
84
+ ducklingsWorkerPlugin(),
85
+ cloudflare(),
86
+ ],
87
+ });
88
+ ```
89
+
90
+ ### Plugin Options
91
+
92
+ ```typescript
93
+ ducklingsWorkerPlugin({
94
+ // Name of the WASM file in the output directory (default: 'duckdb-workers.wasm')
95
+ wasmFileName: 'duckdb-workers.wasm',
96
+ })
97
+ ```
98
+
99
+ The plugin:
100
+ - Resolves `@ducklings/workers/wasm` imports to a relative path for wrangler
101
+ - Automatically copies the WASM file to the correct output directory (works with Cloudflare vite plugin's nested output structure)
102
+
38
103
  ## Features
39
104
 
40
105
  - Async API - all query methods return Promises
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Vite plugin for @ducklings/workers on Cloudflare Workers
3
+ *
4
+ * This plugin handles WASM file resolution and emission for Cloudflare Workers deployments.
5
+ * Designed to work with @cloudflare/vite-plugin.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ /**
10
+ * Vite Plugin interface (minimal definition to avoid hard dependency on vite types)
11
+ */
12
+ interface VitePlugin {
13
+ name: string;
14
+ enforce?: 'pre' | 'post';
15
+ configResolved?: (config: ResolvedConfig) => void;
16
+ resolveId?: (id: string) => {
17
+ id: string;
18
+ external: true;
19
+ } | null;
20
+ writeBundle?: (options: {
21
+ dir?: string;
22
+ }, bundle: Record<string, unknown>) => void;
23
+ }
24
+ interface ResolvedConfig {
25
+ root: string;
26
+ build: {
27
+ outDir: string;
28
+ };
29
+ }
30
+ /**
31
+ * Configuration options for the Ducklings Workers Vite plugin.
32
+ */
33
+ interface DucklingsWorkersPluginOptions {
34
+ /**
35
+ * Name of the WASM file in the output directory.
36
+ * @default 'duckdb-workers.wasm'
37
+ */
38
+ wasmFileName?: string;
39
+ }
40
+ /**
41
+ * Resolves the path to the WASM file bundled with @ducklings/workers.
42
+ *
43
+ * @returns The absolute path to the WASM file
44
+ */
45
+ declare function getWasmPath(): string;
46
+ /**
47
+ * Vite plugin for Cloudflare Workers that handles Ducklings Workers WASM integration.
48
+ *
49
+ * This plugin:
50
+ * - Resolves `@ducklings/workers/wasm` imports to a relative path for wrangler
51
+ * - Copies the WASM file to the output directory after the bundle is written
52
+ *
53
+ * Designed to work with @cloudflare/vite-plugin. The plugin detects the actual
54
+ * output directory structure created by the Cloudflare plugin and places the
55
+ * WASM file in the correct location.
56
+ *
57
+ * @param options - Plugin configuration options
58
+ * @returns Vite plugin
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * // vite.config.ts
63
+ * import { defineConfig } from 'vite';
64
+ * import { cloudflare } from '@cloudflare/vite-plugin';
65
+ * import { ducklingsWorkerPlugin } from '@ducklings/workers/vite-plugin';
66
+ *
67
+ * export default defineConfig({
68
+ * plugins: [
69
+ * ducklingsWorkerPlugin(),
70
+ * cloudflare(),
71
+ * ],
72
+ * });
73
+ * ```
74
+ */
75
+ declare function ducklingsWorkerPlugin(options?: DucklingsWorkersPluginOptions): VitePlugin;
76
+
77
+ export { type DucklingsWorkersPluginOptions, ducklingsWorkerPlugin as default, ducklingsWorkerPlugin, getWasmPath };
@@ -0,0 +1,65 @@
1
+ import { dirname, resolve, join } from 'path';
2
+ import { existsSync, readdirSync, mkdirSync, copyFileSync } from 'fs';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ // Ducklings Workers - Vite Plugin for Cloudflare Workers
6
+
7
+ function getWasmPath() {
8
+ const currentDir = dirname(fileURLToPath(import.meta.url));
9
+ return resolve(currentDir, "wasm", "duckdb-workers.wasm");
10
+ }
11
+ function ducklingsWorkerPlugin(options = {}) {
12
+ const { wasmFileName = "duckdb-workers.wasm" } = options;
13
+ let projectRoot;
14
+ let outDir;
15
+ return {
16
+ name: "ducklings-workers-plugin",
17
+ enforce: "pre",
18
+ configResolved(config) {
19
+ projectRoot = config.root;
20
+ outDir = config.build.outDir;
21
+ },
22
+ // Resolve WASM imports from the package to relative path
23
+ resolveId(id) {
24
+ if (id === "@ducklings/workers/wasm") {
25
+ return { id: `./${wasmFileName}`, external: true };
26
+ }
27
+ return null;
28
+ },
29
+ // Copy WASM file after bundle is written - handles Cloudflare plugin's nested output
30
+ writeBundle(options2) {
31
+ const wasmSrcPath = getWasmPath();
32
+ const baseOutDir = resolve(projectRoot, outDir);
33
+ let targetDir = options2.dir || baseOutDir;
34
+ if (options2.dir) {
35
+ targetDir = options2.dir;
36
+ } else {
37
+ if (existsSync(baseOutDir)) {
38
+ const entries = readdirSync(baseOutDir, { withFileTypes: true });
39
+ for (const entry of entries) {
40
+ if (entry.isDirectory()) {
41
+ const potentialIndexPath = join(baseOutDir, entry.name, "index.js");
42
+ if (existsSync(potentialIndexPath)) {
43
+ targetDir = join(baseOutDir, entry.name);
44
+ break;
45
+ }
46
+ }
47
+ }
48
+ }
49
+ }
50
+ const wasmDestPath = join(targetDir, wasmFileName);
51
+ try {
52
+ mkdirSync(targetDir, { recursive: true });
53
+ copyFileSync(wasmSrcPath, wasmDestPath);
54
+ console.log(`[ducklings] WASM file written to ${wasmDestPath}`);
55
+ } catch (e) {
56
+ console.error("[ducklings] Failed to copy WASM file:", e);
57
+ }
58
+ }
59
+ };
60
+ }
61
+ var vite_plugin_default = ducklingsWorkerPlugin;
62
+
63
+ export { vite_plugin_default as default, ducklingsWorkerPlugin, getWasmPath };
64
+ //# sourceMappingURL=vite-plugin.js.map
65
+ //# sourceMappingURL=vite-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/vite-plugin/index.ts"],"names":["options"],"mappings":";;;;;;AA+CO,SAAS,WAAA,GAAsB;AAEpC,EAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,aAAA,CAAc,MAAA,CAAA,IAAA,CAAY,GAAG,CAAC,CAAA;AAEzD,EAAA,OAAO,OAAA,CAAQ,UAAA,EAAY,MAAA,EAAQ,qBAAqB,CAAA;AAC1D;AA+BO,SAAS,qBAAA,CAAsB,OAAA,GAAyC,EAAC,EAAe;AAC7F,EAAA,MAAM,EAAE,YAAA,GAAe,qBAAA,EAAsB,GAAI,OAAA;AAEjD,EAAA,IAAI,WAAA;AACJ,EAAA,IAAI,MAAA;AAEJ,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,0BAAA;AAAA,IACN,OAAA,EAAS,KAAA;AAAA,IAET,eAAe,MAAA,EAAQ;AACrB,MAAA,WAAA,GAAc,MAAA,CAAO,IAAA;AACrB,MAAA,MAAA,GAAS,OAAO,KAAA,CAAM,MAAA;AAAA,IACxB,CAAA;AAAA;AAAA,IAGA,UAAU,EAAA,EAAI;AACZ,MAAA,IAAI,OAAO,yBAAA,EAA2B;AAEpC,QAAA,OAAO,EAAE,EAAA,EAAI,CAAA,EAAA,EAAK,YAAY,CAAA,CAAA,EAAI,UAAU,IAAA,EAAc;AAAA,MAC5D;AACA,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA;AAAA,IAGA,YAAYA,QAAAA,EAAS;AACnB,MAAA,MAAM,cAAc,WAAA,EAAY;AAChC,MAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,WAAA,EAAa,MAAM,CAAA;AAK9C,MAAA,IAAI,SAAA,GAAYA,SAAQ,GAAA,IAAO,UAAA;AAG/B,MAAA,IAAIA,SAAQ,GAAA,EAAK;AACf,QAAA,SAAA,GAAYA,QAAAA,CAAQ,GAAA;AAAA,MACtB,CAAA,MAAO;AAEL,QAAA,IAAI,UAAA,CAAW,UAAU,CAAA,EAAG;AAC1B,UAAA,MAAM,UAAU,WAAA,CAAY,UAAA,EAAY,EAAE,aAAA,EAAe,MAAM,CAAA;AAC/D,UAAA,KAAA,MAAW,SAAS,OAAA,EAAS;AAC3B,YAAA,IAAI,KAAA,CAAM,aAAY,EAAG;AACvB,cAAA,MAAM,kBAAA,GAAqB,IAAA,CAAK,UAAA,EAAY,KAAA,CAAM,MAAM,UAAU,CAAA;AAClE,cAAA,IAAI,UAAA,CAAW,kBAAkB,CAAA,EAAG;AAClC,gBAAA,SAAA,GAAY,IAAA,CAAK,UAAA,EAAY,KAAA,CAAM,IAAI,CAAA;AACvC,gBAAA;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,MAAA,MAAM,YAAA,GAAe,IAAA,CAAK,SAAA,EAAW,YAAY,CAAA;AAEjD,MAAA,IAAI;AACF,QAAA,SAAA,CAAU,SAAA,EAAW,EAAE,SAAA,EAAW,IAAA,EAAM,CAAA;AACxC,QAAA,YAAA,CAAa,aAAa,YAAY,CAAA;AACtC,QAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,iCAAA,EAAoC,YAAY,CAAA,CAAE,CAAA;AAAA,MAChE,SAAS,CAAA,EAAG;AACV,QAAA,OAAA,CAAQ,KAAA,CAAM,yCAAyC,CAAC,CAAA;AAAA,MAC1D;AAAA,IACF;AAAA,GACF;AACF;AAEA,IAAO,mBAAA,GAAQ","file":"vite-plugin.js","sourcesContent":["/**\n * Vite plugin for @ducklings/workers on Cloudflare Workers\n *\n * This plugin handles WASM file resolution and emission for Cloudflare Workers deployments.\n * Designed to work with @cloudflare/vite-plugin.\n *\n * @packageDocumentation\n */\n\nimport { resolve, dirname, join } from 'node:path';\nimport { copyFileSync, mkdirSync, existsSync, readdirSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * Vite Plugin interface (minimal definition to avoid hard dependency on vite types)\n */\ninterface VitePlugin {\n name: string;\n enforce?: 'pre' | 'post';\n configResolved?: (config: ResolvedConfig) => void;\n resolveId?: (id: string) => { id: string; external: true } | null;\n writeBundle?: (options: { dir?: string }, bundle: Record<string, unknown>) => void;\n}\n\ninterface ResolvedConfig {\n root: string;\n build: {\n outDir: string;\n };\n}\n\n/**\n * Configuration options for the Ducklings Workers Vite plugin.\n */\nexport interface DucklingsWorkersPluginOptions {\n /**\n * Name of the WASM file in the output directory.\n * @default 'duckdb-workers.wasm'\n */\n wasmFileName?: string;\n}\n\n/**\n * Resolves the path to the WASM file bundled with @ducklings/workers.\n *\n * @returns The absolute path to the WASM file\n */\nexport function getWasmPath(): string {\n // Get the directory of this module\n const currentDir = dirname(fileURLToPath(import.meta.url));\n // WASM file is in the wasm subdirectory relative to dist\n return resolve(currentDir, 'wasm', 'duckdb-workers.wasm');\n}\n\n/**\n * Vite plugin for Cloudflare Workers that handles Ducklings Workers WASM integration.\n *\n * This plugin:\n * - Resolves `@ducklings/workers/wasm` imports to a relative path for wrangler\n * - Copies the WASM file to the output directory after the bundle is written\n *\n * Designed to work with @cloudflare/vite-plugin. The plugin detects the actual\n * output directory structure created by the Cloudflare plugin and places the\n * WASM file in the correct location.\n *\n * @param options - Plugin configuration options\n * @returns Vite plugin\n *\n * @example\n * ```typescript\n * // vite.config.ts\n * import { defineConfig } from 'vite';\n * import { cloudflare } from '@cloudflare/vite-plugin';\n * import { ducklingsWorkerPlugin } from '@ducklings/workers/vite-plugin';\n *\n * export default defineConfig({\n * plugins: [\n * ducklingsWorkerPlugin(),\n * cloudflare(),\n * ],\n * });\n * ```\n */\nexport function ducklingsWorkerPlugin(options: DucklingsWorkersPluginOptions = {}): VitePlugin {\n const { wasmFileName = 'duckdb-workers.wasm' } = options;\n\n let projectRoot: string;\n let outDir: string;\n\n return {\n name: 'ducklings-workers-plugin',\n enforce: 'pre' as const,\n\n configResolved(config) {\n projectRoot = config.root;\n outDir = config.build.outDir;\n },\n\n // Resolve WASM imports from the package to relative path\n resolveId(id) {\n if (id === '@ducklings/workers/wasm') {\n // Return external reference that will be resolved at runtime by wrangler\n return { id: `./${wasmFileName}`, external: true as const };\n }\n return null;\n },\n\n // Copy WASM file after bundle is written - handles Cloudflare plugin's nested output\n writeBundle(options) {\n const wasmSrcPath = getWasmPath();\n const baseOutDir = resolve(projectRoot, outDir);\n\n // The Cloudflare vite plugin creates a nested structure like:\n // dist/{worker_name}/index.js\n // We need to find where index.js is and put the WASM file there\n let targetDir = options.dir || baseOutDir;\n\n // If options.dir is provided, use it directly\n if (options.dir) {\n targetDir = options.dir;\n } else {\n // Look for subdirectories that contain index.js (Cloudflare plugin structure)\n if (existsSync(baseOutDir)) {\n const entries = readdirSync(baseOutDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n const potentialIndexPath = join(baseOutDir, entry.name, 'index.js');\n if (existsSync(potentialIndexPath)) {\n targetDir = join(baseOutDir, entry.name);\n break;\n }\n }\n }\n }\n }\n\n const wasmDestPath = join(targetDir, wasmFileName);\n\n try {\n mkdirSync(targetDir, { recursive: true });\n copyFileSync(wasmSrcPath, wasmDestPath);\n console.log(`[ducklings] WASM file written to ${wasmDestPath}`);\n } catch (e) {\n console.error('[ducklings] Failed to copy WASM file:', e);\n }\n },\n };\n}\n\nexport default ducklingsWorkerPlugin;\n"]}
package/dist/wasm.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ // Type declaration for the DuckDB WASM module
2
+ declare const wasmModule: WebAssembly.Module;
3
+ export default wasmModule;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ducklings/workers",
3
- "version": "1.4.3-dev.1",
3
+ "version": "1.4.3-dev.3",
4
4
  "description": "Minimal DuckDB WASM for Cloudflare Workers and serverless environments",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -12,8 +12,15 @@
12
12
  "import": "./dist/index.js",
13
13
  "require": "./dist/index.cjs"
14
14
  },
15
- "./wasm": "./dist/wasm/duckdb-workers.wasm",
16
- "./wasm/*": "./dist/wasm/*"
15
+ "./wasm": {
16
+ "types": "./dist/wasm.d.ts",
17
+ "default": "./dist/wasm/duckdb-workers.wasm"
18
+ },
19
+ "./wasm/*": "./dist/wasm/*",
20
+ "./vite-plugin": {
21
+ "types": "./dist/vite-plugin.d.ts",
22
+ "import": "./dist/vite-plugin.js"
23
+ }
17
24
  },
18
25
  "files": [
19
26
  "dist",
@@ -21,7 +28,7 @@
21
28
  ],
22
29
  "scripts": {
23
30
  "build": "tsup && pnpm postbuild",
24
- "postbuild": "mkdir -p dist/wasm && cp ../../dist/duckdb-workers.js dist/wasm/ && cp ../../dist/duckdb-workers.wasm dist/wasm/",
31
+ "postbuild": "mkdir -p dist/wasm && cp ../../dist/duckdb-workers.js dist/wasm/ && cp ../../dist/duckdb-workers.wasm dist/wasm/ && cp src/wasm.d.ts dist/wasm.d.ts",
25
32
  "dev": "tsup --watch",
26
33
  "test": "vitest run --reporter=dot",
27
34
  "test:watch": "vitest --reporter=dot",
@@ -46,6 +53,14 @@
46
53
  "typescript": "^5.9.3",
47
54
  "vitest": "^4.0.16"
48
55
  },
56
+ "peerDependencies": {
57
+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0"
58
+ },
59
+ "peerDependenciesMeta": {
60
+ "vite": {
61
+ "optional": true
62
+ }
63
+ },
49
64
  "keywords": [
50
65
  "duckdb",
51
66
  "wasm",