lopata 0.0.1

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 (147) hide show
  1. package/README.md +15 -0
  2. package/package.json +51 -0
  3. package/runtime/bindings/ai.ts +132 -0
  4. package/runtime/bindings/analytics-engine.ts +96 -0
  5. package/runtime/bindings/browser.ts +64 -0
  6. package/runtime/bindings/cache.ts +179 -0
  7. package/runtime/bindings/cf-streams.ts +56 -0
  8. package/runtime/bindings/container-docker.ts +225 -0
  9. package/runtime/bindings/container.ts +662 -0
  10. package/runtime/bindings/crypto-extras.ts +89 -0
  11. package/runtime/bindings/d1.ts +315 -0
  12. package/runtime/bindings/do-executor-inprocess.ts +140 -0
  13. package/runtime/bindings/do-executor-worker.ts +368 -0
  14. package/runtime/bindings/do-executor.ts +45 -0
  15. package/runtime/bindings/do-websocket-bridge.ts +70 -0
  16. package/runtime/bindings/do-worker-entry.ts +220 -0
  17. package/runtime/bindings/do-worker-env.ts +74 -0
  18. package/runtime/bindings/durable-object.ts +992 -0
  19. package/runtime/bindings/email.ts +180 -0
  20. package/runtime/bindings/html-rewriter.ts +84 -0
  21. package/runtime/bindings/hyperdrive.ts +130 -0
  22. package/runtime/bindings/images.ts +381 -0
  23. package/runtime/bindings/kv.ts +359 -0
  24. package/runtime/bindings/queue.ts +507 -0
  25. package/runtime/bindings/r2.ts +759 -0
  26. package/runtime/bindings/rpc-stub.ts +267 -0
  27. package/runtime/bindings/scheduled.ts +172 -0
  28. package/runtime/bindings/service-binding.ts +217 -0
  29. package/runtime/bindings/static-assets.ts +481 -0
  30. package/runtime/bindings/websocket-pair.ts +182 -0
  31. package/runtime/bindings/workflow.ts +858 -0
  32. package/runtime/bunflare-config.ts +56 -0
  33. package/runtime/cli/cache.ts +39 -0
  34. package/runtime/cli/context.ts +105 -0
  35. package/runtime/cli/d1.ts +163 -0
  36. package/runtime/cli/dev.ts +392 -0
  37. package/runtime/cli/kv.ts +84 -0
  38. package/runtime/cli/queues.ts +109 -0
  39. package/runtime/cli/r2.ts +140 -0
  40. package/runtime/cli/traces.ts +251 -0
  41. package/runtime/cli.ts +102 -0
  42. package/runtime/config.ts +148 -0
  43. package/runtime/d1-migrate.ts +37 -0
  44. package/runtime/dashboard/api.ts +174 -0
  45. package/runtime/dashboard/app.tsx +220 -0
  46. package/runtime/dashboard/components/breadcrumb.tsx +16 -0
  47. package/runtime/dashboard/components/buttons.tsx +13 -0
  48. package/runtime/dashboard/components/code-block.tsx +5 -0
  49. package/runtime/dashboard/components/detail-field.tsx +8 -0
  50. package/runtime/dashboard/components/empty-state.tsx +8 -0
  51. package/runtime/dashboard/components/filter-input.tsx +11 -0
  52. package/runtime/dashboard/components/index.ts +16 -0
  53. package/runtime/dashboard/components/key-value-table.tsx +23 -0
  54. package/runtime/dashboard/components/modal.tsx +23 -0
  55. package/runtime/dashboard/components/page-header.tsx +11 -0
  56. package/runtime/dashboard/components/pill-button.tsx +14 -0
  57. package/runtime/dashboard/components/refresh-button.tsx +7 -0
  58. package/runtime/dashboard/components/service-info.tsx +45 -0
  59. package/runtime/dashboard/components/status-badge.tsx +7 -0
  60. package/runtime/dashboard/components/table-link.tsx +5 -0
  61. package/runtime/dashboard/components/table.tsx +26 -0
  62. package/runtime/dashboard/components.tsx +19 -0
  63. package/runtime/dashboard/index.html +23 -0
  64. package/runtime/dashboard/lib.ts +45 -0
  65. package/runtime/dashboard/rpc/client.ts +20 -0
  66. package/runtime/dashboard/rpc/handlers/ai.ts +71 -0
  67. package/runtime/dashboard/rpc/handlers/analytics-engine.ts +53 -0
  68. package/runtime/dashboard/rpc/handlers/cache.ts +24 -0
  69. package/runtime/dashboard/rpc/handlers/config.ts +137 -0
  70. package/runtime/dashboard/rpc/handlers/containers.ts +194 -0
  71. package/runtime/dashboard/rpc/handlers/d1.ts +84 -0
  72. package/runtime/dashboard/rpc/handlers/do.ts +117 -0
  73. package/runtime/dashboard/rpc/handlers/email.ts +82 -0
  74. package/runtime/dashboard/rpc/handlers/errors.ts +32 -0
  75. package/runtime/dashboard/rpc/handlers/generations.ts +60 -0
  76. package/runtime/dashboard/rpc/handlers/kv.ts +76 -0
  77. package/runtime/dashboard/rpc/handlers/overview.ts +94 -0
  78. package/runtime/dashboard/rpc/handlers/queue.ts +79 -0
  79. package/runtime/dashboard/rpc/handlers/r2.ts +72 -0
  80. package/runtime/dashboard/rpc/handlers/scheduled.ts +91 -0
  81. package/runtime/dashboard/rpc/handlers/traces.ts +64 -0
  82. package/runtime/dashboard/rpc/handlers/workers.ts +65 -0
  83. package/runtime/dashboard/rpc/handlers/workflows.ts +171 -0
  84. package/runtime/dashboard/rpc/hooks.ts +132 -0
  85. package/runtime/dashboard/rpc/server.ts +70 -0
  86. package/runtime/dashboard/rpc/types.ts +396 -0
  87. package/runtime/dashboard/sql-browser/data-browser-tab.tsx +122 -0
  88. package/runtime/dashboard/sql-browser/editable-cell.tsx +117 -0
  89. package/runtime/dashboard/sql-browser/filter-row.tsx +99 -0
  90. package/runtime/dashboard/sql-browser/history-panels.tsx +110 -0
  91. package/runtime/dashboard/sql-browser/hooks.ts +137 -0
  92. package/runtime/dashboard/sql-browser/index.ts +4 -0
  93. package/runtime/dashboard/sql-browser/insert-row-form.tsx +85 -0
  94. package/runtime/dashboard/sql-browser/modals.tsx +116 -0
  95. package/runtime/dashboard/sql-browser/schema-browser-tab.tsx +67 -0
  96. package/runtime/dashboard/sql-browser/sql-browser.tsx +52 -0
  97. package/runtime/dashboard/sql-browser/sql-console-tab.tsx +124 -0
  98. package/runtime/dashboard/sql-browser/table-data-view.tsx +566 -0
  99. package/runtime/dashboard/sql-browser/table-sidebar.tsx +38 -0
  100. package/runtime/dashboard/sql-browser/types.ts +61 -0
  101. package/runtime/dashboard/sql-browser/utils.ts +167 -0
  102. package/runtime/dashboard/style.css +177 -0
  103. package/runtime/dashboard/views/ai.tsx +152 -0
  104. package/runtime/dashboard/views/analytics-engine.tsx +169 -0
  105. package/runtime/dashboard/views/cache.tsx +93 -0
  106. package/runtime/dashboard/views/containers.tsx +197 -0
  107. package/runtime/dashboard/views/d1.tsx +81 -0
  108. package/runtime/dashboard/views/do.tsx +168 -0
  109. package/runtime/dashboard/views/email.tsx +235 -0
  110. package/runtime/dashboard/views/errors.tsx +558 -0
  111. package/runtime/dashboard/views/home.tsx +287 -0
  112. package/runtime/dashboard/views/kv.tsx +273 -0
  113. package/runtime/dashboard/views/queue.tsx +193 -0
  114. package/runtime/dashboard/views/r2.tsx +202 -0
  115. package/runtime/dashboard/views/scheduled.tsx +89 -0
  116. package/runtime/dashboard/views/trace-waterfall.tsx +410 -0
  117. package/runtime/dashboard/views/traces.tsx +768 -0
  118. package/runtime/dashboard/views/workers.tsx +55 -0
  119. package/runtime/dashboard/views/workflows.tsx +473 -0
  120. package/runtime/db.ts +258 -0
  121. package/runtime/env.ts +362 -0
  122. package/runtime/error-page/app.tsx +394 -0
  123. package/runtime/error-page/build.ts +269 -0
  124. package/runtime/error-page/index.html +16 -0
  125. package/runtime/error-page/style.css +31 -0
  126. package/runtime/execution-context.ts +18 -0
  127. package/runtime/file-watcher.ts +57 -0
  128. package/runtime/generation-manager.ts +230 -0
  129. package/runtime/generation.ts +411 -0
  130. package/runtime/plugin.ts +292 -0
  131. package/runtime/request-cf.ts +28 -0
  132. package/runtime/rpc-validate.ts +154 -0
  133. package/runtime/tracing/context.ts +40 -0
  134. package/runtime/tracing/db.ts +73 -0
  135. package/runtime/tracing/frames.ts +75 -0
  136. package/runtime/tracing/instrument.ts +186 -0
  137. package/runtime/tracing/span.ts +138 -0
  138. package/runtime/tracing/store.ts +499 -0
  139. package/runtime/tracing/types.ts +47 -0
  140. package/runtime/vite-plugin/config-plugin.ts +68 -0
  141. package/runtime/vite-plugin/dev-server-plugin.ts +493 -0
  142. package/runtime/vite-plugin/dist/index.mjs +52333 -0
  143. package/runtime/vite-plugin/globals-plugin.ts +94 -0
  144. package/runtime/vite-plugin/index.ts +43 -0
  145. package/runtime/vite-plugin/modules-plugin.ts +88 -0
  146. package/runtime/vite-plugin/react-router-plugin.ts +95 -0
  147. package/runtime/worker-registry.ts +52 -0
@@ -0,0 +1,31 @@
1
+ @import "tailwindcss" source(".");
2
+
3
+ @theme {
4
+ --color-surface: #f9fafb;
5
+ --color-surface-raised: #f3f4f6;
6
+ --color-surface-hover: #f3f4f6;
7
+ --color-surface-dim: #e5e7eb;
8
+ --color-ink: #1A1A1A;
9
+ --color-ink-muted: #333333;
10
+ --color-accent-lime: #DFFF40;
11
+ --color-accent-blue: #9BB5FC;
12
+ --color-accent-olive: #7C8B00;
13
+ --color-error-red: #DC2626;
14
+ --color-error-bg: #FEF2F2;
15
+ --color-error-border: #FECACA;
16
+ --shadow-card: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
17
+ --shadow-card-hover: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
18
+ --radius-card: 10px;
19
+ }
20
+
21
+ .scrollbar-thin::-webkit-scrollbar {
22
+ width: 6px;
23
+ height: 6px;
24
+ }
25
+ .scrollbar-thin::-webkit-scrollbar-track {
26
+ background: transparent;
27
+ }
28
+ .scrollbar-thin::-webkit-scrollbar-thumb {
29
+ background: #cbd5e1;
30
+ border-radius: 3px;
31
+ }
@@ -0,0 +1,18 @@
1
+ export class ExecutionContext {
2
+ private _promises: Promise<unknown>[] = [];
3
+
4
+ waitUntil(promise: Promise<unknown>): void {
5
+ this._promises.push(promise.catch(err => {
6
+ console.error('[bunflare] waitUntil promise rejected:', err);
7
+ }));
8
+ }
9
+
10
+ passThroughOnException(): void {
11
+ // No origin in local dev — no-op is correct
12
+ }
13
+
14
+ /** Dev-only: await all tracked background promises */
15
+ async _awaitAll(): Promise<void> {
16
+ await Promise.allSettled(this._promises);
17
+ }
18
+ }
@@ -0,0 +1,57 @@
1
+ import { watch, type FSWatcher } from "node:fs";
2
+ import path from "node:path";
3
+
4
+ const WATCH_EXTENSIONS = new Set([".ts", ".js", ".tsx", ".jsx", ".json"]);
5
+ const IGNORE_DIRS = new Set([".bunflare", "node_modules", ".git"]);
6
+
7
+ export class FileWatcher {
8
+ private dir: string;
9
+ private onChange: () => void;
10
+ private debounceMs: number;
11
+ private watcher: FSWatcher | null = null;
12
+ private debounceTimer: ReturnType<typeof setTimeout> | null = null;
13
+
14
+ constructor(dir: string, onChange: () => void, debounceMs = 150) {
15
+ this.dir = dir;
16
+ this.onChange = onChange;
17
+ this.debounceMs = debounceMs;
18
+ }
19
+
20
+ start(): void {
21
+ if (this.watcher) return;
22
+ this.watcher = watch(this.dir, { recursive: true }, (_event, filename) => {
23
+ if (!filename) return;
24
+ if (!this.shouldWatch(filename)) return;
25
+ this.scheduleChange();
26
+ });
27
+ }
28
+
29
+ stop(): void {
30
+ if (this.watcher) {
31
+ this.watcher.close();
32
+ this.watcher = null;
33
+ }
34
+ if (this.debounceTimer) {
35
+ clearTimeout(this.debounceTimer);
36
+ this.debounceTimer = null;
37
+ }
38
+ }
39
+
40
+ private shouldWatch(filename: string): boolean {
41
+ const ext = path.extname(filename);
42
+ if (!WATCH_EXTENSIONS.has(ext)) return false;
43
+ const parts = filename.split(path.sep);
44
+ for (const part of parts) {
45
+ if (IGNORE_DIRS.has(part)) return false;
46
+ }
47
+ return true;
48
+ }
49
+
50
+ private scheduleChange(): void {
51
+ if (this.debounceTimer) clearTimeout(this.debounceTimer);
52
+ this.debounceTimer = setTimeout(() => {
53
+ this.debounceTimer = null;
54
+ this.onChange();
55
+ }, this.debounceMs);
56
+ }
57
+ }
@@ -0,0 +1,230 @@
1
+ import path from "node:path";
2
+ import type { WranglerConfig } from "./config";
3
+ import { buildEnv, wireClassRefs, setGlobalEnv } from "./env";
4
+ import { Generation, type GenerationInfo } from "./generation";
5
+ import { ExecutionContext } from "./execution-context";
6
+ import type { DOExecutorFactory } from "./bindings/do-executor";
7
+ import type { WorkerRegistry } from "./worker-registry";
8
+
9
+ function isEntrypointClass(exp: unknown): exp is new (ctx: ExecutionContext, env: unknown) => Record<string, unknown> {
10
+ return typeof exp === "function" && exp.prototype &&
11
+ typeof exp.prototype.fetch === "function";
12
+ }
13
+
14
+ /**
15
+ * Detect and patch web frameworks (Hono, etc.) that use .then()/.catch()
16
+ * for request dispatch. This breaks async stack traces in Bun because
17
+ * errors that propagate through .then() callbacks lose their async context.
18
+ *
19
+ * err.stack is set once at Error creation and doesn't change, but Bun
20
+ * appends async frames (callers) only when the error is caught via `await`,
21
+ * not via `.catch()`. The user's error handler may re-throw inside .catch(),
22
+ * which creates a new rejection without those async frames.
23
+ *
24
+ * Fix: wrap each route handler with try/catch to snapshot err.stack
25
+ * (which includes async frames at that point) before the error enters
26
+ * the .then()/.catch() chain. If the error handler re-throws, we
27
+ * restore the full stack.
28
+ */
29
+ function patchFrameworkDispatch(defaultExport: Record<string, unknown>): void {
30
+ if (typeof defaultExport.fetch !== "function") return;
31
+
32
+ // Detect Hono by characteristic properties
33
+ const isHono = "routes" in defaultExport && "router" in defaultExport && "_basePath" in defaultExport;
34
+ if (!isHono) return;
35
+
36
+ const app = defaultExport as Record<string, any>;
37
+ const routes: { handler: Function }[] = app.routes;
38
+ if (!Array.isArray(routes)) return;
39
+
40
+ // Wrap each route handler to capture err.stack before .then() destroys it
41
+ for (const route of routes) {
42
+ const orig = route.handler;
43
+ route.handler = async function (this: unknown, c: unknown, next: unknown) {
44
+ try {
45
+ return await orig.call(this, c, next);
46
+ } catch (err: unknown) {
47
+ // Save the full stack (with async frames) before .then()/.catch() strips them
48
+ if (err instanceof Error) {
49
+ (err as any).__asyncStack = err.stack;
50
+ }
51
+ throw err;
52
+ }
53
+ };
54
+ }
55
+
56
+ // Patch error handler: if the user's handler re-throws, restore the saved stack
57
+ const origErrorHandler = app.errorHandler;
58
+ app.errorHandler = (err: unknown, c: unknown) => {
59
+ if (err instanceof Error && (err as any).__asyncStack) {
60
+ err.stack = (err as any).__asyncStack;
61
+ delete (err as any).__asyncStack;
62
+ }
63
+ return origErrorHandler(err, c);
64
+ };
65
+ }
66
+
67
+ export class GenerationManager {
68
+ private generations = new Map<number, Generation>();
69
+ private nextGenId = 1;
70
+ private _activeGenId: number | null = null;
71
+ private _reloading: Promise<Generation> | null = null;
72
+ private _pendingReload = false;
73
+
74
+ gracePeriodMs = 10_000;
75
+
76
+ readonly config: WranglerConfig;
77
+ readonly baseDir: string;
78
+ readonly workerPath: string;
79
+ readonly workerName: string | undefined;
80
+ readonly workerRegistry: WorkerRegistry | undefined;
81
+ readonly isMain: boolean;
82
+ readonly cronEnabled: boolean;
83
+ readonly executorFactory: DOExecutorFactory | undefined;
84
+ readonly browserConfig: { wsEndpoint?: string; executablePath?: string; headless?: boolean } | undefined;
85
+ /** @internal Path to the wrangler config file (for isolated mode worker threads) */
86
+ _configPath: string = "";
87
+
88
+ constructor(config: WranglerConfig, baseDir: string, options?: { workerName?: string; workerRegistry?: WorkerRegistry; isMain?: boolean; cron?: boolean; executorFactory?: DOExecutorFactory; configPath?: string; browserConfig?: { wsEndpoint?: string; executablePath?: string; headless?: boolean } }) {
89
+ this.config = config;
90
+ this.baseDir = baseDir;
91
+ this.workerPath = path.resolve(baseDir, config.main);
92
+ this.workerName = options?.workerName;
93
+ this.workerRegistry = options?.workerRegistry;
94
+ this.isMain = options?.isMain ?? true;
95
+ this.cronEnabled = options?.cron ?? false;
96
+ this.executorFactory = options?.executorFactory;
97
+ this.browserConfig = options?.browserConfig;
98
+ this._configPath = options?.configPath ?? "";
99
+ }
100
+
101
+ /** The currently active generation (receives new requests) */
102
+ get active(): Generation | null {
103
+ if (this._activeGenId === null) return null;
104
+ return this.generations.get(this._activeGenId) ?? null;
105
+ }
106
+
107
+ /**
108
+ * Create a new generation by importing the worker module fresh.
109
+ * Serialized: if called while already reloading, queues one reload.
110
+ */
111
+ async reload(): Promise<Generation> {
112
+ if (this._reloading) {
113
+ this._pendingReload = true;
114
+ // Wait for current reload, then re-trigger
115
+ await this._reloading;
116
+ if (this._pendingReload) {
117
+ this._pendingReload = false;
118
+ return this.reload();
119
+ }
120
+ return this.active!;
121
+ }
122
+
123
+ this._reloading = this._doReload();
124
+ try {
125
+ return await this._reloading;
126
+ } finally {
127
+ this._reloading = null;
128
+ // If another reload was requested while we were reloading, do it now
129
+ if (this._pendingReload) {
130
+ this._pendingReload = false;
131
+ return this.reload();
132
+ }
133
+ }
134
+ }
135
+
136
+ private async _doReload(): Promise<Generation> {
137
+ // 1. Import fresh worker module using cache-busting query string
138
+ const workerModule = await import(`${this.workerPath}?v=${Date.now()}`);
139
+
140
+ // 1b. Configure executor factory with module/config paths (for isolated mode)
141
+ if (this.executorFactory && "configure" in this.executorFactory) {
142
+ (this.executorFactory as any).configure(this.workerPath, this._configPath);
143
+ }
144
+
145
+ // 2. Build new env with fresh binding instances (same underlying DB)
146
+ const { env, registry } = buildEnv(this.config, this.baseDir, this.executorFactory, this.browserConfig);
147
+
148
+ // 3. Wire DO and Workflow class references
149
+ wireClassRefs(registry, workerModule, env, this.workerRegistry);
150
+
151
+ // 4. Update globalEnv for cloudflare:workers env export (main worker only)
152
+ if (this.isMain) {
153
+ setGlobalEnv(env);
154
+ }
155
+
156
+ // 5. Validate default export
157
+ const defaultExport = workerModule.default;
158
+ const classBasedExport = isEntrypointClass(defaultExport);
159
+
160
+ if (!classBasedExport && !defaultExport?.fetch) {
161
+ throw new Error("Worker module must export a default object with a fetch() method, or a class with a fetch() method on its prototype");
162
+ }
163
+
164
+ // 5b. Patch frameworks that use .then()/.catch() for dispatch (e.g. Hono)
165
+ // This destroys async stack traces in Bun. We replace their fetch with an
166
+ // async wrapper so errors propagate through proper await chains.
167
+ if (!classBasedExport) {
168
+ patchFrameworkDispatch(defaultExport);
169
+ }
170
+
171
+ // 6. Create new generation
172
+ const genId = this.nextGenId++;
173
+ const gen = new Generation(genId, workerModule, defaultExport, classBasedExport, env, registry, this.config, this.workerName, this.cronEnabled);
174
+ this.generations.set(genId, gen);
175
+
176
+ // 7. Drain old generation
177
+ const oldGenId = this._activeGenId;
178
+ if (oldGenId !== null) {
179
+ const oldGen = this.generations.get(oldGenId);
180
+ if (oldGen && oldGen.state === "active") {
181
+ oldGen.drain();
182
+ // Schedule force-stop after grace period
183
+ oldGen.drainTimer = setTimeout(() => {
184
+ this._stopGeneration(oldGenId);
185
+ }, this.gracePeriodMs);
186
+ }
187
+ }
188
+
189
+ // 8. Mark new generation as active
190
+ this._activeGenId = genId;
191
+
192
+ // 9. Start consumers + cron on new generation
193
+ gen.startConsumers();
194
+
195
+ return gen;
196
+ }
197
+
198
+ private _stopGeneration(genId: number): void {
199
+ const gen = this.generations.get(genId);
200
+ if (!gen || gen.state === "stopped") return;
201
+ gen.stop();
202
+ // Clean up reference (keep for dashboard listing briefly)
203
+ // Remove after another grace period to let dashboard show it
204
+ setTimeout(() => {
205
+ this.generations.delete(genId);
206
+ }, 60_000);
207
+ }
208
+
209
+ /** Force-drain a specific generation */
210
+ drain(genId: number): void {
211
+ const gen = this.generations.get(genId);
212
+ if (!gen) return;
213
+ gen.drain();
214
+ }
215
+
216
+ /** Force-stop a specific generation */
217
+ stop(genId: number): void {
218
+ this._stopGeneration(genId);
219
+ }
220
+
221
+ /** Update the grace period for future reloads */
222
+ setGracePeriod(ms: number): void {
223
+ this.gracePeriodMs = ms;
224
+ }
225
+
226
+ /** List all generations for dashboard */
227
+ list(): GenerationInfo[] {
228
+ return Array.from(this.generations.values()).map(g => g.getInfo());
229
+ }
230
+ }