localflare-core 0.1.2 ā 0.2.0-beta.0
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/LICENSE +21 -0
- package/dist/index.d.ts +20 -40
- package/dist/index.js +23 -285
- package/dist/index.js.map +1 -1
- package/package.json +5 -6
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Rohan Prasad
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { Miniflare } from 'miniflare';
|
|
2
|
-
|
|
3
1
|
interface WranglerConfig {
|
|
4
2
|
name?: string;
|
|
5
3
|
main?: string;
|
|
@@ -62,56 +60,38 @@ interface DiscoveredBindings {
|
|
|
62
60
|
};
|
|
63
61
|
vars: Record<string, string>;
|
|
64
62
|
}
|
|
65
|
-
interface WorkerLogEntry {
|
|
66
|
-
level: 'log' | 'info' | 'warn' | 'error' | 'debug';
|
|
67
|
-
message: string;
|
|
68
|
-
}
|
|
69
|
-
type WorkerLogCallback = (entry: WorkerLogEntry) => void;
|
|
70
|
-
interface LocalFlareOptions {
|
|
71
|
-
configPath?: string;
|
|
72
|
-
port?: number;
|
|
73
|
-
dashboardPort?: number;
|
|
74
|
-
persistPath?: string;
|
|
75
|
-
verbose?: boolean;
|
|
76
|
-
onWorkerLog?: WorkerLogCallback;
|
|
77
|
-
}
|
|
78
63
|
interface BindingInfo {
|
|
79
64
|
type: 'D1' | 'KV' | 'R2' | 'DO' | 'Queue' | 'Var';
|
|
80
65
|
name: string;
|
|
81
66
|
details: Record<string, unknown>;
|
|
82
67
|
}
|
|
83
68
|
|
|
84
|
-
declare class LocalFlare {
|
|
85
|
-
private mf;
|
|
86
|
-
private config;
|
|
87
|
-
private bindings;
|
|
88
|
-
private options;
|
|
89
|
-
constructor(options?: LocalFlareOptions);
|
|
90
|
-
start(): Promise<void>;
|
|
91
|
-
stop(): Promise<void>;
|
|
92
|
-
getD1Database(bindingName: string): Promise<any>;
|
|
93
|
-
getKVNamespace(bindingName: string): Promise<any>;
|
|
94
|
-
getR2Bucket(bindingName: string): Promise<any>;
|
|
95
|
-
getDurableObjectNamespace(bindingName: string): Promise<any>;
|
|
96
|
-
getQueueProducer<T = unknown>(bindingName: string): Promise<any>;
|
|
97
|
-
getAllBindings<T = Record<string, unknown>>(): Promise<T>;
|
|
98
|
-
getDiscoveredBindings(): DiscoveredBindings | null;
|
|
99
|
-
getConfig(): WranglerConfig | null;
|
|
100
|
-
getOptions(): Required<LocalFlareOptions>;
|
|
101
|
-
getMiniflare(): Miniflare | null;
|
|
102
|
-
isRunning(): boolean;
|
|
103
|
-
setLogCallback(callback: WorkerLogCallback): void;
|
|
104
|
-
private ensureRunning;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
69
|
declare const WRANGLER_CONFIG_FILES: string[];
|
|
108
70
|
/**
|
|
109
71
|
* Find wrangler config file in a directory
|
|
110
|
-
*
|
|
72
|
+
* Searches for config files in priority order: toml > json > jsonc
|
|
73
|
+
* @param directory - Directory to search in
|
|
74
|
+
* @returns Path to the first found config file, or null if none found
|
|
111
75
|
*/
|
|
112
76
|
declare function findWranglerConfig(directory: string): string | null;
|
|
77
|
+
/**
|
|
78
|
+
* Parse wrangler configuration file (TOML, JSON, or JSONC format)
|
|
79
|
+
* @param configPath - Path to the configuration file
|
|
80
|
+
* @returns Parsed configuration object
|
|
81
|
+
* @throws Error if the file doesn't exist or cannot be parsed
|
|
82
|
+
*/
|
|
113
83
|
declare function parseWranglerConfig(configPath: string): WranglerConfig;
|
|
84
|
+
/**
|
|
85
|
+
* Discover and extract all bindings from a wrangler configuration
|
|
86
|
+
* @param config - Parsed wrangler configuration
|
|
87
|
+
* @returns Discovered bindings organized by type
|
|
88
|
+
*/
|
|
114
89
|
declare function discoverBindings(config: WranglerConfig): DiscoveredBindings;
|
|
90
|
+
/**
|
|
91
|
+
* Generate a human-readable summary of discovered bindings
|
|
92
|
+
* @param bindings - Discovered bindings to summarize
|
|
93
|
+
* @returns Array of summary strings, one per binding type
|
|
94
|
+
*/
|
|
115
95
|
declare function getBindingSummary(bindings: DiscoveredBindings): string[];
|
|
116
96
|
|
|
117
|
-
export { type BindingInfo, type D1DatabaseConfig, type DiscoveredBindings, type DurableObjectBinding, type KVNamespaceConfig,
|
|
97
|
+
export { type BindingInfo, type D1DatabaseConfig, type DiscoveredBindings, type DurableObjectBinding, type KVNamespaceConfig, type QueueConsumerConfig, type QueueProducerConfig, type R2BucketConfig, WRANGLER_CONFIG_FILES, type WranglerConfig, discoverBindings, findWranglerConfig, getBindingSummary, parseWranglerConfig };
|
package/dist/index.js
CHANGED
|
@@ -1,78 +1,13 @@
|
|
|
1
|
-
// src/localflare.ts
|
|
2
|
-
import { Miniflare } from "miniflare";
|
|
3
|
-
import { resolve as resolve2, dirname } from "path";
|
|
4
|
-
import * as esbuild from "esbuild";
|
|
5
|
-
import * as readline from "readline";
|
|
6
|
-
|
|
7
1
|
// src/config.ts
|
|
8
2
|
import { readFileSync, existsSync } from "fs";
|
|
9
3
|
import { resolve, extname } from "path";
|
|
10
4
|
import { parse as parseToml } from "toml";
|
|
5
|
+
import parseJsonc from "tiny-jsonc";
|
|
11
6
|
var WRANGLER_CONFIG_FILES = [
|
|
12
7
|
"wrangler.toml",
|
|
13
8
|
"wrangler.json",
|
|
14
9
|
"wrangler.jsonc"
|
|
15
10
|
];
|
|
16
|
-
function stripJsonComments(content) {
|
|
17
|
-
let result = "";
|
|
18
|
-
let inString = false;
|
|
19
|
-
let inSingleLineComment = false;
|
|
20
|
-
let inMultiLineComment = false;
|
|
21
|
-
let i = 0;
|
|
22
|
-
while (i < content.length) {
|
|
23
|
-
const char = content[i];
|
|
24
|
-
const nextChar = content[i + 1];
|
|
25
|
-
if (inSingleLineComment) {
|
|
26
|
-
if (char === "\n") {
|
|
27
|
-
inSingleLineComment = false;
|
|
28
|
-
result += char;
|
|
29
|
-
}
|
|
30
|
-
i++;
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
if (inMultiLineComment) {
|
|
34
|
-
if (char === "*" && nextChar === "/") {
|
|
35
|
-
inMultiLineComment = false;
|
|
36
|
-
i += 2;
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
i++;
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
if (inString) {
|
|
43
|
-
result += char;
|
|
44
|
-
if (char === "\\" && i + 1 < content.length) {
|
|
45
|
-
result += nextChar;
|
|
46
|
-
i += 2;
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
if (char === '"') {
|
|
50
|
-
inString = false;
|
|
51
|
-
}
|
|
52
|
-
i++;
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
if (char === '"') {
|
|
56
|
-
inString = true;
|
|
57
|
-
result += char;
|
|
58
|
-
i++;
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
if (char === "/" && nextChar === "/") {
|
|
62
|
-
inSingleLineComment = true;
|
|
63
|
-
i += 2;
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
if (char === "/" && nextChar === "*") {
|
|
67
|
-
inMultiLineComment = true;
|
|
68
|
-
i += 2;
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
result += char;
|
|
72
|
-
i++;
|
|
73
|
-
}
|
|
74
|
-
return result;
|
|
75
|
-
}
|
|
76
11
|
function findWranglerConfig(directory) {
|
|
77
12
|
for (const filename of WRANGLER_CONFIG_FILES) {
|
|
78
13
|
const configPath = resolve(directory, filename);
|
|
@@ -95,13 +30,14 @@ function parseWranglerConfig(configPath) {
|
|
|
95
30
|
case ".json":
|
|
96
31
|
return JSON.parse(content);
|
|
97
32
|
case ".jsonc":
|
|
98
|
-
return
|
|
99
|
-
default:
|
|
33
|
+
return parseJsonc.parse(content);
|
|
34
|
+
default: {
|
|
100
35
|
const trimmed = content.trim();
|
|
101
36
|
if (trimmed.startsWith("{")) {
|
|
102
|
-
return
|
|
37
|
+
return parseJsonc.parse(content);
|
|
103
38
|
}
|
|
104
39
|
return parseToml(content);
|
|
40
|
+
}
|
|
105
41
|
}
|
|
106
42
|
}
|
|
107
43
|
function discoverBindings(config) {
|
|
@@ -120,239 +56,41 @@ function discoverBindings(config) {
|
|
|
120
56
|
function getBindingSummary(bindings) {
|
|
121
57
|
const summary = [];
|
|
122
58
|
if (bindings.d1.length > 0) {
|
|
123
|
-
summary.push(
|
|
59
|
+
summary.push(
|
|
60
|
+
`D1 Databases: ${bindings.d1.map((d) => d.binding).join(", ")}`
|
|
61
|
+
);
|
|
124
62
|
}
|
|
125
63
|
if (bindings.kv.length > 0) {
|
|
126
|
-
summary.push(
|
|
64
|
+
summary.push(
|
|
65
|
+
`KV Namespaces: ${bindings.kv.map((k) => k.binding).join(", ")}`
|
|
66
|
+
);
|
|
127
67
|
}
|
|
128
68
|
if (bindings.r2.length > 0) {
|
|
129
69
|
summary.push(`R2 Buckets: ${bindings.r2.map((r) => r.binding).join(", ")}`);
|
|
130
70
|
}
|
|
131
71
|
if (bindings.durableObjects.length > 0) {
|
|
132
|
-
summary.push(
|
|
72
|
+
summary.push(
|
|
73
|
+
`Durable Objects: ${bindings.durableObjects.map((d) => d.name).join(", ")}`
|
|
74
|
+
);
|
|
133
75
|
}
|
|
134
76
|
if (bindings.queues.producers.length > 0) {
|
|
135
|
-
summary.push(
|
|
77
|
+
summary.push(
|
|
78
|
+
`Queue Producers: ${bindings.queues.producers.map((q) => q.binding).join(", ")}`
|
|
79
|
+
);
|
|
136
80
|
}
|
|
137
81
|
if (bindings.queues.consumers.length > 0) {
|
|
138
|
-
summary.push(
|
|
82
|
+
summary.push(
|
|
83
|
+
`Queue Consumers: ${bindings.queues.consumers.map((q) => q.queue).join(", ")}`
|
|
84
|
+
);
|
|
139
85
|
}
|
|
140
86
|
if (Object.keys(bindings.vars).length > 0) {
|
|
141
|
-
summary.push(
|
|
87
|
+
summary.push(
|
|
88
|
+
`Environment Variables: ${Object.keys(bindings.vars).join(", ")}`
|
|
89
|
+
);
|
|
142
90
|
}
|
|
143
91
|
return summary;
|
|
144
92
|
}
|
|
145
|
-
|
|
146
|
-
// src/localflare.ts
|
|
147
|
-
var LocalFlare = class {
|
|
148
|
-
mf = null;
|
|
149
|
-
config = null;
|
|
150
|
-
bindings = null;
|
|
151
|
-
options;
|
|
152
|
-
constructor(options = {}) {
|
|
153
|
-
let configPath = options.configPath;
|
|
154
|
-
if (!configPath) {
|
|
155
|
-
const detected = findWranglerConfig(process.cwd());
|
|
156
|
-
configPath = detected ?? "./wrangler.toml";
|
|
157
|
-
}
|
|
158
|
-
this.options = {
|
|
159
|
-
configPath,
|
|
160
|
-
port: options.port ?? 8787,
|
|
161
|
-
dashboardPort: options.dashboardPort ?? 8788,
|
|
162
|
-
persistPath: options.persistPath ?? ".localflare",
|
|
163
|
-
verbose: options.verbose ?? false,
|
|
164
|
-
onWorkerLog: options.onWorkerLog ?? (() => {
|
|
165
|
-
})
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
async start() {
|
|
169
|
-
const configPath = resolve2(this.options.configPath);
|
|
170
|
-
const rootDir = dirname(configPath);
|
|
171
|
-
this.config = parseWranglerConfig(configPath);
|
|
172
|
-
this.bindings = discoverBindings(this.config);
|
|
173
|
-
if (this.options.verbose) {
|
|
174
|
-
console.log("\n\u{1F4E6} Discovered bindings:");
|
|
175
|
-
getBindingSummary(this.bindings).forEach((line) => {
|
|
176
|
-
console.log(` ${line}`);
|
|
177
|
-
});
|
|
178
|
-
console.log("");
|
|
179
|
-
}
|
|
180
|
-
const persistRoot = resolve2(rootDir, this.options.persistPath);
|
|
181
|
-
const entryPoint = resolve2(rootDir, this.config.main || "src/index.ts");
|
|
182
|
-
const buildResult = await esbuild.build({
|
|
183
|
-
entryPoints: [entryPoint],
|
|
184
|
-
bundle: true,
|
|
185
|
-
format: "esm",
|
|
186
|
-
target: "es2022",
|
|
187
|
-
write: false,
|
|
188
|
-
minify: false,
|
|
189
|
-
sourcemap: false,
|
|
190
|
-
conditions: ["workerd", "worker", "browser"],
|
|
191
|
-
external: ["cloudflare:*", "node:*"]
|
|
192
|
-
});
|
|
193
|
-
const scriptContent = buildResult.outputFiles?.[0]?.text;
|
|
194
|
-
if (!scriptContent) {
|
|
195
|
-
throw new Error("Failed to compile worker script");
|
|
196
|
-
}
|
|
197
|
-
const d1Databases = Object.fromEntries(
|
|
198
|
-
this.bindings.d1.map((d) => [d.binding, d.database_id])
|
|
199
|
-
);
|
|
200
|
-
const kvNamespaces = Object.fromEntries(
|
|
201
|
-
this.bindings.kv.map((k) => [k.binding, k.id])
|
|
202
|
-
);
|
|
203
|
-
const r2Buckets = Object.fromEntries(
|
|
204
|
-
this.bindings.r2.map((r) => [r.binding, r.bucket_name])
|
|
205
|
-
);
|
|
206
|
-
const queueProducers = Object.fromEntries(
|
|
207
|
-
this.bindings.queues.producers.map((q) => [q.binding, q.queue])
|
|
208
|
-
);
|
|
209
|
-
const queueConsumers = Object.fromEntries(
|
|
210
|
-
this.bindings.queues.consumers.map((c) => [
|
|
211
|
-
c.queue,
|
|
212
|
-
{
|
|
213
|
-
maxBatchSize: c.max_batch_size ?? 10,
|
|
214
|
-
maxBatchTimeout: c.max_batch_timeout ?? 5,
|
|
215
|
-
maxRetries: c.max_retries ?? 3,
|
|
216
|
-
deadLetterQueue: c.dead_letter_queue
|
|
217
|
-
}
|
|
218
|
-
])
|
|
219
|
-
);
|
|
220
|
-
const durableObjects = Object.fromEntries(
|
|
221
|
-
this.bindings.durableObjects.map((d) => [d.name, d.class_name])
|
|
222
|
-
);
|
|
223
|
-
const handleRuntimeStdio = (stdout, stderr) => {
|
|
224
|
-
if (this.options.verbose) {
|
|
225
|
-
console.log("[LocalFlare] Runtime stdio handler connected");
|
|
226
|
-
}
|
|
227
|
-
const processStream = (stream, level) => {
|
|
228
|
-
const rl = readline.createInterface({ input: stream });
|
|
229
|
-
rl.on("line", (line) => {
|
|
230
|
-
if (!line.trim()) return;
|
|
231
|
-
let logLevel = level === "error" ? "error" : "log";
|
|
232
|
-
if (line.includes("[warn]") || line.includes("WARNING")) {
|
|
233
|
-
logLevel = "warn";
|
|
234
|
-
} else if (line.includes("[info]") || line.includes("INFO")) {
|
|
235
|
-
logLevel = "info";
|
|
236
|
-
} else if (line.includes("[debug]") || line.includes("DEBUG")) {
|
|
237
|
-
logLevel = "debug";
|
|
238
|
-
} else if (line.includes("[error]") || line.includes("ERROR")) {
|
|
239
|
-
logLevel = "error";
|
|
240
|
-
}
|
|
241
|
-
this.options.onWorkerLog({ level: logLevel, message: line });
|
|
242
|
-
});
|
|
243
|
-
};
|
|
244
|
-
processStream(stdout, "log");
|
|
245
|
-
processStream(stderr, "error");
|
|
246
|
-
};
|
|
247
|
-
const miniflareOptions = {
|
|
248
|
-
modules: [
|
|
249
|
-
{
|
|
250
|
-
type: "ESModule",
|
|
251
|
-
path: entryPoint,
|
|
252
|
-
contents: scriptContent
|
|
253
|
-
}
|
|
254
|
-
],
|
|
255
|
-
port: this.options.port,
|
|
256
|
-
verbose: this.options.verbose,
|
|
257
|
-
kvPersist: `${persistRoot}/kv`,
|
|
258
|
-
d1Persist: `${persistRoot}/d1`,
|
|
259
|
-
r2Persist: `${persistRoot}/r2`,
|
|
260
|
-
durableObjectsPersist: `${persistRoot}/do`,
|
|
261
|
-
cachePersist: `${persistRoot}/cache`,
|
|
262
|
-
// Explicit binding configurations
|
|
263
|
-
bindings: this.bindings.vars,
|
|
264
|
-
// Capture worker stdout/stderr for logging
|
|
265
|
-
handleRuntimeStdio,
|
|
266
|
-
// Use plain text logs instead of structured JSON for easier parsing
|
|
267
|
-
structuredWorkerdLogs: false
|
|
268
|
-
};
|
|
269
|
-
if (Object.keys(d1Databases).length > 0) {
|
|
270
|
-
miniflareOptions.d1Databases = d1Databases;
|
|
271
|
-
}
|
|
272
|
-
if (Object.keys(kvNamespaces).length > 0) {
|
|
273
|
-
miniflareOptions.kvNamespaces = kvNamespaces;
|
|
274
|
-
}
|
|
275
|
-
if (Object.keys(r2Buckets).length > 0) {
|
|
276
|
-
miniflareOptions.r2Buckets = r2Buckets;
|
|
277
|
-
}
|
|
278
|
-
if (Object.keys(queueProducers).length > 0) {
|
|
279
|
-
miniflareOptions.queueProducers = queueProducers;
|
|
280
|
-
}
|
|
281
|
-
if (Object.keys(queueConsumers).length > 0) {
|
|
282
|
-
miniflareOptions.queueConsumers = queueConsumers;
|
|
283
|
-
}
|
|
284
|
-
if (Object.keys(durableObjects).length > 0) {
|
|
285
|
-
miniflareOptions.durableObjects = durableObjects;
|
|
286
|
-
}
|
|
287
|
-
this.mf = new Miniflare(miniflareOptions);
|
|
288
|
-
const url = await this.mf.ready;
|
|
289
|
-
console.log(`\u26A1 Worker running at ${url}`);
|
|
290
|
-
}
|
|
291
|
-
async stop() {
|
|
292
|
-
if (this.mf) {
|
|
293
|
-
await this.mf.dispose();
|
|
294
|
-
this.mf = null;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
// Binding accessors - use generic types for portability
|
|
298
|
-
// Consumers can cast to @cloudflare/workers-types if needed
|
|
299
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
300
|
-
async getD1Database(bindingName) {
|
|
301
|
-
this.ensureRunning();
|
|
302
|
-
return this.mf.getD1Database(bindingName);
|
|
303
|
-
}
|
|
304
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
305
|
-
async getKVNamespace(bindingName) {
|
|
306
|
-
this.ensureRunning();
|
|
307
|
-
return this.mf.getKVNamespace(bindingName);
|
|
308
|
-
}
|
|
309
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
310
|
-
async getR2Bucket(bindingName) {
|
|
311
|
-
this.ensureRunning();
|
|
312
|
-
return this.mf.getR2Bucket(bindingName);
|
|
313
|
-
}
|
|
314
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
315
|
-
async getDurableObjectNamespace(bindingName) {
|
|
316
|
-
this.ensureRunning();
|
|
317
|
-
return this.mf.getDurableObjectNamespace(bindingName);
|
|
318
|
-
}
|
|
319
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
320
|
-
async getQueueProducer(bindingName) {
|
|
321
|
-
this.ensureRunning();
|
|
322
|
-
return this.mf.getQueueProducer(bindingName);
|
|
323
|
-
}
|
|
324
|
-
async getAllBindings() {
|
|
325
|
-
this.ensureRunning();
|
|
326
|
-
return this.mf.getBindings();
|
|
327
|
-
}
|
|
328
|
-
// Getters
|
|
329
|
-
getDiscoveredBindings() {
|
|
330
|
-
return this.bindings;
|
|
331
|
-
}
|
|
332
|
-
getConfig() {
|
|
333
|
-
return this.config;
|
|
334
|
-
}
|
|
335
|
-
getOptions() {
|
|
336
|
-
return this.options;
|
|
337
|
-
}
|
|
338
|
-
getMiniflare() {
|
|
339
|
-
return this.mf;
|
|
340
|
-
}
|
|
341
|
-
isRunning() {
|
|
342
|
-
return this.mf !== null;
|
|
343
|
-
}
|
|
344
|
-
// Set the worker log callback (must be called before start())
|
|
345
|
-
setLogCallback(callback) {
|
|
346
|
-
this.options.onWorkerLog = callback;
|
|
347
|
-
}
|
|
348
|
-
ensureRunning() {
|
|
349
|
-
if (!this.mf) {
|
|
350
|
-
throw new Error("LocalFlare is not running. Call start() first.");
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
};
|
|
354
93
|
export {
|
|
355
|
-
LocalFlare,
|
|
356
94
|
WRANGLER_CONFIG_FILES,
|
|
357
95
|
discoverBindings,
|
|
358
96
|
findWranglerConfig,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/localflare.ts","../src/config.ts"],"sourcesContent":["import { Miniflare } from 'miniflare'\nimport { resolve, dirname } from 'node:path'\nimport * as esbuild from 'esbuild'\nimport { Readable } from 'node:stream'\nimport * as readline from 'node:readline'\nimport { parseWranglerConfig, discoverBindings, getBindingSummary, findWranglerConfig } from './config.js'\nimport type { LocalFlareOptions, DiscoveredBindings, WranglerConfig, WorkerLogCallback } from './types.js'\n\nexport class LocalFlare {\n private mf: Miniflare | null = null\n private config: WranglerConfig | null = null\n private bindings: DiscoveredBindings | null = null\n private options: Required<LocalFlareOptions>\n\n constructor(options: LocalFlareOptions = {}) {\n // Auto-detect config if not provided\n let configPath = options.configPath\n if (!configPath) {\n const detected = findWranglerConfig(process.cwd())\n configPath = detected ?? './wrangler.toml' // Fallback for error messaging\n }\n\n this.options = {\n configPath,\n port: options.port ?? 8787,\n dashboardPort: options.dashboardPort ?? 8788,\n persistPath: options.persistPath ?? '.localflare',\n verbose: options.verbose ?? false,\n onWorkerLog: options.onWorkerLog ?? (() => {}),\n }\n }\n\n async start(): Promise<void> {\n const configPath = resolve(this.options.configPath)\n const rootDir = dirname(configPath)\n\n // Parse wrangler.toml\n this.config = parseWranglerConfig(configPath)\n this.bindings = discoverBindings(this.config)\n\n // Log discovered bindings\n if (this.options.verbose) {\n console.log('\\nš¦ Discovered bindings:')\n getBindingSummary(this.bindings).forEach(line => {\n console.log(` ${line}`)\n })\n console.log('')\n }\n\n // Build Miniflare options\n const persistRoot = resolve(rootDir, this.options.persistPath)\n\n // Compile the worker entry point with esbuild\n const entryPoint = resolve(rootDir, this.config.main || 'src/index.ts')\n const buildResult = await esbuild.build({\n entryPoints: [entryPoint],\n bundle: true,\n format: 'esm',\n target: 'es2022',\n write: false,\n minify: false,\n sourcemap: false,\n conditions: ['workerd', 'worker', 'browser'],\n external: ['cloudflare:*', 'node:*'],\n })\n\n const scriptContent = buildResult.outputFiles?.[0]?.text\n if (!scriptContent) {\n throw new Error('Failed to compile worker script')\n }\n\n // Build explicit binding configurations from parsed config\n // Miniflare v3 expects bindings as objects keyed by binding name\n const d1Databases = Object.fromEntries(\n this.bindings.d1.map(d => [d.binding, d.database_id])\n )\n\n const kvNamespaces = Object.fromEntries(\n this.bindings.kv.map(k => [k.binding, k.id])\n )\n\n const r2Buckets = Object.fromEntries(\n this.bindings.r2.map(r => [r.binding, r.bucket_name])\n )\n\n const queueProducers = Object.fromEntries(\n this.bindings.queues.producers.map(q => [q.binding, q.queue])\n )\n\n // Queue consumers configuration for Miniflare\n // Maps queue name to consumer options\n const queueConsumers = Object.fromEntries(\n this.bindings.queues.consumers.map(c => [\n c.queue,\n {\n maxBatchSize: c.max_batch_size ?? 10,\n maxBatchTimeout: c.max_batch_timeout ?? 5,\n maxRetries: c.max_retries ?? 3,\n deadLetterQueue: c.dead_letter_queue,\n },\n ])\n )\n\n const durableObjects = Object.fromEntries(\n this.bindings.durableObjects.map(d => [d.name, d.class_name])\n )\n\n // Create a handler for worker stdout/stderr to capture logs\n // Use arrow function to capture 'this' and access current callback\n const handleRuntimeStdio = (stdout: Readable, stderr: Readable) => {\n if (this.options.verbose) {\n console.log('[LocalFlare] Runtime stdio handler connected')\n }\n\n const processStream = (stream: Readable, level: 'log' | 'error') => {\n const rl = readline.createInterface({ input: stream })\n rl.on('line', (line) => {\n // Skip empty lines\n if (!line.trim()) return\n\n // Determine log level from content\n let logLevel: 'log' | 'info' | 'warn' | 'error' | 'debug' = level === 'error' ? 'error' : 'log'\n if (line.includes('[warn]') || line.includes('WARNING')) {\n logLevel = 'warn'\n } else if (line.includes('[info]') || line.includes('INFO')) {\n logLevel = 'info'\n } else if (line.includes('[debug]') || line.includes('DEBUG')) {\n logLevel = 'debug'\n } else if (line.includes('[error]') || line.includes('ERROR')) {\n logLevel = 'error'\n }\n\n // Access callback via this.options to allow setting it after start()\n this.options.onWorkerLog({ level: logLevel, message: line })\n })\n }\n\n processStream(stdout, 'log')\n processStream(stderr, 'error')\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const miniflareOptions: any = {\n modules: [\n {\n type: 'ESModule',\n path: entryPoint,\n contents: scriptContent,\n },\n ],\n port: this.options.port,\n verbose: this.options.verbose,\n kvPersist: `${persistRoot}/kv`,\n d1Persist: `${persistRoot}/d1`,\n r2Persist: `${persistRoot}/r2`,\n durableObjectsPersist: `${persistRoot}/do`,\n cachePersist: `${persistRoot}/cache`,\n // Explicit binding configurations\n bindings: this.bindings.vars,\n // Capture worker stdout/stderr for logging\n handleRuntimeStdio,\n // Use plain text logs instead of structured JSON for easier parsing\n structuredWorkerdLogs: false,\n }\n\n // Only add bindings if they exist\n if (Object.keys(d1Databases).length > 0) {\n miniflareOptions.d1Databases = d1Databases\n }\n if (Object.keys(kvNamespaces).length > 0) {\n miniflareOptions.kvNamespaces = kvNamespaces\n }\n if (Object.keys(r2Buckets).length > 0) {\n miniflareOptions.r2Buckets = r2Buckets\n }\n if (Object.keys(queueProducers).length > 0) {\n miniflareOptions.queueProducers = queueProducers\n }\n if (Object.keys(queueConsumers).length > 0) {\n miniflareOptions.queueConsumers = queueConsumers\n }\n if (Object.keys(durableObjects).length > 0) {\n miniflareOptions.durableObjects = durableObjects\n }\n\n this.mf = new Miniflare(miniflareOptions)\n\n // Wait for Miniflare to be ready\n const url = await this.mf.ready\n console.log(`ā” Worker running at ${url}`)\n }\n\n async stop(): Promise<void> {\n if (this.mf) {\n await this.mf.dispose()\n this.mf = null\n }\n }\n\n // Binding accessors - use generic types for portability\n // Consumers can cast to @cloudflare/workers-types if needed\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async getD1Database(bindingName: string): Promise<any> {\n this.ensureRunning()\n return this.mf!.getD1Database(bindingName)\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async getKVNamespace(bindingName: string): Promise<any> {\n this.ensureRunning()\n return this.mf!.getKVNamespace(bindingName)\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async getR2Bucket(bindingName: string): Promise<any> {\n this.ensureRunning()\n return this.mf!.getR2Bucket(bindingName)\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async getDurableObjectNamespace(bindingName: string): Promise<any> {\n this.ensureRunning()\n return this.mf!.getDurableObjectNamespace(bindingName)\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async getQueueProducer<T = unknown>(bindingName: string): Promise<any> {\n this.ensureRunning()\n return this.mf!.getQueueProducer<T>(bindingName)\n }\n\n async getAllBindings<T = Record<string, unknown>>(): Promise<T> {\n this.ensureRunning()\n return this.mf!.getBindings() as Promise<T>\n }\n\n // Getters\n getDiscoveredBindings(): DiscoveredBindings | null {\n return this.bindings\n }\n\n getConfig(): WranglerConfig | null {\n return this.config\n }\n\n getOptions(): Required<LocalFlareOptions> {\n return this.options\n }\n\n getMiniflare(): Miniflare | null {\n return this.mf\n }\n\n isRunning(): boolean {\n return this.mf !== null\n }\n\n // Set the worker log callback (must be called before start())\n setLogCallback(callback: WorkerLogCallback): void {\n this.options.onWorkerLog = callback\n }\n\n private ensureRunning(): void {\n if (!this.mf) {\n throw new Error('LocalFlare is not running. Call start() first.')\n }\n }\n}\n","import { readFileSync, existsSync } from 'node:fs'\nimport { resolve, extname } from 'node:path'\nimport { parse as parseToml } from 'toml'\nimport type { WranglerConfig, DiscoveredBindings } from './types.js'\n\n// Supported wrangler config file names in order of priority\nexport const WRANGLER_CONFIG_FILES = [\n 'wrangler.toml',\n 'wrangler.json',\n 'wrangler.jsonc',\n]\n\n/**\n * Strip JSON comments (both // and /* *\\/) for JSONC parsing\n */\nfunction stripJsonComments(content: string): string {\n let result = ''\n let inString = false\n let inSingleLineComment = false\n let inMultiLineComment = false\n let i = 0\n\n while (i < content.length) {\n const char = content[i]\n const nextChar = content[i + 1]\n\n if (inSingleLineComment) {\n if (char === '\\n') {\n inSingleLineComment = false\n result += char\n }\n i++\n continue\n }\n\n if (inMultiLineComment) {\n if (char === '*' && nextChar === '/') {\n inMultiLineComment = false\n i += 2\n continue\n }\n i++\n continue\n }\n\n if (inString) {\n result += char\n if (char === '\\\\' && i + 1 < content.length) {\n result += nextChar\n i += 2\n continue\n }\n if (char === '\"') {\n inString = false\n }\n i++\n continue\n }\n\n if (char === '\"') {\n inString = true\n result += char\n i++\n continue\n }\n\n if (char === '/' && nextChar === '/') {\n inSingleLineComment = true\n i += 2\n continue\n }\n\n if (char === '/' && nextChar === '*') {\n inMultiLineComment = true\n i += 2\n continue\n }\n\n result += char\n i++\n }\n\n return result\n}\n\n/**\n * Find wrangler config file in a directory\n * Returns the path to the first found config file, or null if none found\n */\nexport function findWranglerConfig(directory: string): string | null {\n for (const filename of WRANGLER_CONFIG_FILES) {\n const configPath = resolve(directory, filename)\n if (existsSync(configPath)) {\n return configPath\n }\n }\n return null\n}\n\nexport function parseWranglerConfig(configPath: string): WranglerConfig {\n const fullPath = resolve(configPath)\n\n if (!existsSync(fullPath)) {\n throw new Error(`Wrangler config not found at: ${fullPath}`)\n }\n\n const content = readFileSync(fullPath, 'utf-8')\n const ext = extname(fullPath).toLowerCase()\n\n switch (ext) {\n case '.toml':\n return parseToml(content) as WranglerConfig\n case '.json':\n return JSON.parse(content) as WranglerConfig\n case '.jsonc':\n return JSON.parse(stripJsonComments(content)) as WranglerConfig\n default:\n // Try to detect format from content\n const trimmed = content.trim()\n if (trimmed.startsWith('{')) {\n // Looks like JSON/JSONC\n return JSON.parse(stripJsonComments(content)) as WranglerConfig\n }\n // Default to TOML\n return parseToml(content) as WranglerConfig\n }\n}\n\nexport function discoverBindings(config: WranglerConfig): DiscoveredBindings {\n return {\n d1: config.d1_databases ?? [],\n kv: config.kv_namespaces ?? [],\n r2: config.r2_buckets ?? [],\n durableObjects: config.durable_objects?.bindings ?? [],\n queues: {\n producers: config.queues?.producers ?? [],\n consumers: config.queues?.consumers ?? [],\n },\n vars: config.vars ?? {},\n }\n}\n\nexport function getBindingSummary(bindings: DiscoveredBindings): string[] {\n const summary: string[] = []\n\n if (bindings.d1.length > 0) {\n summary.push(`D1 Databases: ${bindings.d1.map(d => d.binding).join(', ')}`)\n }\n if (bindings.kv.length > 0) {\n summary.push(`KV Namespaces: ${bindings.kv.map(k => k.binding).join(', ')}`)\n }\n if (bindings.r2.length > 0) {\n summary.push(`R2 Buckets: ${bindings.r2.map(r => r.binding).join(', ')}`)\n }\n if (bindings.durableObjects.length > 0) {\n summary.push(`Durable Objects: ${bindings.durableObjects.map(d => d.name).join(', ')}`)\n }\n if (bindings.queues.producers.length > 0) {\n summary.push(`Queue Producers: ${bindings.queues.producers.map(q => q.binding).join(', ')}`)\n }\n if (bindings.queues.consumers.length > 0) {\n summary.push(`Queue Consumers: ${bindings.queues.consumers.map(q => q.queue).join(', ')}`)\n }\n if (Object.keys(bindings.vars).length > 0) {\n summary.push(`Environment Variables: ${Object.keys(bindings.vars).join(', ')}`)\n }\n\n return summary\n}\n"],"mappings":"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","names":["resolve","resolve"]}
|
|
1
|
+
{"version":3,"sources":["../src/config.ts"],"sourcesContent":["import { readFileSync, existsSync } from \"node:fs\";\nimport { resolve, extname } from \"node:path\";\nimport { parse as parseToml } from \"toml\";\nimport parseJsonc from \"tiny-jsonc\";\nimport type { WranglerConfig, DiscoveredBindings } from \"./types.js\";\n\n// Supported wrangler config file names in order of priority\nexport const WRANGLER_CONFIG_FILES = [\n \"wrangler.toml\",\n \"wrangler.json\",\n \"wrangler.jsonc\",\n];\n\n/**\n * Find wrangler config file in a directory\n * Searches for config files in priority order: toml > json > jsonc\n * @param directory - Directory to search in\n * @returns Path to the first found config file, or null if none found\n */\nexport function findWranglerConfig(directory: string): string | null {\n for (const filename of WRANGLER_CONFIG_FILES) {\n const configPath = resolve(directory, filename);\n if (existsSync(configPath)) {\n return configPath;\n }\n }\n return null;\n}\n\n/**\n * Parse wrangler configuration file (TOML, JSON, or JSONC format)\n * @param configPath - Path to the configuration file\n * @returns Parsed configuration object\n * @throws Error if the file doesn't exist or cannot be parsed\n */\nexport function parseWranglerConfig(configPath: string): WranglerConfig {\n const fullPath = resolve(configPath);\n\n if (!existsSync(fullPath)) {\n throw new Error(`Wrangler config not found at: ${fullPath}`);\n }\n\n const content = readFileSync(fullPath, \"utf-8\");\n const ext = extname(fullPath).toLowerCase();\n\n switch (ext) {\n case \".toml\":\n return parseToml(content) as WranglerConfig;\n case \".json\":\n return JSON.parse(content) as WranglerConfig;\n case \".jsonc\":\n return parseJsonc.parse(content) as WranglerConfig;\n default: {\n // Try to detect format from content\n const trimmed = content.trim();\n if (trimmed.startsWith(\"{\")) {\n // Looks like JSON/JSONC - use JSONC parser to handle both\n return parseJsonc.parse(content) as WranglerConfig;\n }\n // Default to TOML\n return parseToml(content) as WranglerConfig;\n }\n }\n}\n\n/**\n * Discover and extract all bindings from a wrangler configuration\n * @param config - Parsed wrangler configuration\n * @returns Discovered bindings organized by type\n */\nexport function discoverBindings(config: WranglerConfig): DiscoveredBindings {\n return {\n d1: config.d1_databases ?? [],\n kv: config.kv_namespaces ?? [],\n r2: config.r2_buckets ?? [],\n durableObjects: config.durable_objects?.bindings ?? [],\n queues: {\n producers: config.queues?.producers ?? [],\n consumers: config.queues?.consumers ?? [],\n },\n vars: config.vars ?? {},\n };\n}\n\n/**\n * Generate a human-readable summary of discovered bindings\n * @param bindings - Discovered bindings to summarize\n * @returns Array of summary strings, one per binding type\n */\nexport function getBindingSummary(bindings: DiscoveredBindings): string[] {\n const summary: string[] = [];\n\n if (bindings.d1.length > 0) {\n summary.push(\n `D1 Databases: ${bindings.d1.map((d) => d.binding).join(\", \")}`\n );\n }\n if (bindings.kv.length > 0) {\n summary.push(\n `KV Namespaces: ${bindings.kv.map((k) => k.binding).join(\", \")}`\n );\n }\n if (bindings.r2.length > 0) {\n summary.push(`R2 Buckets: ${bindings.r2.map((r) => r.binding).join(\", \")}`);\n }\n if (bindings.durableObjects.length > 0) {\n summary.push(\n `Durable Objects: ${bindings.durableObjects\n .map((d) => d.name)\n .join(\", \")}`\n );\n }\n if (bindings.queues.producers.length > 0) {\n summary.push(\n `Queue Producers: ${bindings.queues.producers\n .map((q) => q.binding)\n .join(\", \")}`\n );\n }\n if (bindings.queues.consumers.length > 0) {\n summary.push(\n `Queue Consumers: ${bindings.queues.consumers\n .map((q) => q.queue)\n .join(\", \")}`\n );\n }\n if (Object.keys(bindings.vars).length > 0) {\n summary.push(\n `Environment Variables: ${Object.keys(bindings.vars).join(\", \")}`\n );\n }\n\n return summary;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "localflare-core",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Core
|
|
3
|
+
"version": "0.2.0-beta.0",
|
|
4
|
+
"description": "Core utilities for Localflare - config parsing and binding discovery",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -15,19 +15,18 @@
|
|
|
15
15
|
"dist"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"
|
|
19
|
-
"miniflare": "^3.20241218.0",
|
|
18
|
+
"tiny-jsonc": "^1.0.2",
|
|
20
19
|
"toml": "^3.0.0"
|
|
21
20
|
},
|
|
22
21
|
"devDependencies": {
|
|
23
|
-
"@cloudflare/workers-types": "^4.20241218.0",
|
|
24
22
|
"tsup": "^8.3.5",
|
|
25
23
|
"typescript": "^5.7.2"
|
|
26
24
|
},
|
|
27
25
|
"keywords": [
|
|
28
26
|
"cloudflare",
|
|
29
27
|
"workers",
|
|
30
|
-
"
|
|
28
|
+
"wrangler",
|
|
29
|
+
"config",
|
|
31
30
|
"local",
|
|
32
31
|
"development"
|
|
33
32
|
],
|