devflare 1.0.0-next.4 → 1.0.0-next.6
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/LLM.md +7 -2
- package/README.md +4 -2
- package/dist/browser.d.ts +50 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +149 -0
- package/dist/{dev-pa8dhm20.js → dev-7agn9g5s.js} +319 -63
- package/dist/dev-server/server.d.ts.map +1 -1
- package/dist/index-62b3gt2g.js +12 -0
- package/dist/index-9ats0s83.js +70 -0
- package/dist/{index-pf5s73n9.js → index-ccrh4w3t.js} +1 -281
- package/dist/index-k7r18na8.js +0 -0
- package/dist/{index-m2q41jwa.js → index-n3np2d6t.js} +1 -1
- package/dist/index-npc1c8jx.js +44 -0
- package/dist/index-p7g30wd2.js +281 -0
- package/dist/{index-ep3445yc.js → index-rprrn24p.js} +25 -97
- package/dist/{index-07q6yxyc.js → index-v8vvsn9x.js} +1 -0
- package/dist/index.js +25 -26
- package/dist/runtime/index.d.ts +2 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +73 -0
- package/dist/sveltekit/index.js +5 -3
- package/dist/test/index.js +10 -5
- package/dist/{types-5nyrz1sz.js → types-vss6vrz7.js} +5 -4
- package/package.json +3 -2
- package/dist/{build-mnf6v8gd.js → build-nz5yrj7f.js} +3 -3
- package/dist/{deploy-nhceck39.js → deploy-a5pcxd5w.js} +3 -3
- package/dist/{doctor-fmgb3d28.js → doctor-v7jy4s3r.js} +3 -3
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
normalizeDOBinding
|
|
3
|
-
} from "./index-hcex3rgh.js";
|
|
4
|
-
import {
|
|
5
|
-
__require
|
|
6
|
-
} from "./index-37x76zdn.js";
|
|
7
|
-
|
|
8
1
|
// src/bridge/protocol.ts
|
|
9
2
|
var BinaryKind = {
|
|
10
3
|
StreamChunk: 1,
|
|
@@ -1137,277 +1130,4 @@ function setBindingHints(hints) {
|
|
|
1137
1130
|
globalEnvProxy = null;
|
|
1138
1131
|
}
|
|
1139
1132
|
|
|
1140
|
-
|
|
1141
|
-
function generateGatewayScript() {
|
|
1142
|
-
return `
|
|
1143
|
-
// Gateway Worker — Provides RPC access to all bindings
|
|
1144
|
-
export default {
|
|
1145
|
-
async fetch(request, env, ctx) {
|
|
1146
|
-
const url = new URL(request.url)
|
|
1147
|
-
|
|
1148
|
-
// Health check
|
|
1149
|
-
if (url.pathname === '/_devflare/health') {
|
|
1150
|
-
return new Response(JSON.stringify({ status: 'ok', bindings: Object.keys(env) }), {
|
|
1151
|
-
headers: { 'Content-Type': 'application/json' }
|
|
1152
|
-
})
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
// RPC endpoint
|
|
1156
|
-
if (url.pathname === '/_devflare/rpc' && request.method === 'POST') {
|
|
1157
|
-
try {
|
|
1158
|
-
const { method, params } = await request.json()
|
|
1159
|
-
const result = await executeRpc(env, method, params)
|
|
1160
|
-
return new Response(JSON.stringify({ ok: true, result }), {
|
|
1161
|
-
headers: { 'Content-Type': 'application/json' }
|
|
1162
|
-
})
|
|
1163
|
-
} catch (error) {
|
|
1164
|
-
return new Response(JSON.stringify({
|
|
1165
|
-
ok: false,
|
|
1166
|
-
error: { code: 'RPC_ERROR', message: error.message }
|
|
1167
|
-
}), {
|
|
1168
|
-
status: 500,
|
|
1169
|
-
headers: { 'Content-Type': 'application/json' }
|
|
1170
|
-
})
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
return new Response('Devflare Gateway', { status: 200 })
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
async function executeRpc(env, method, params) {
|
|
1179
|
-
const [bindingName, ...methodPath] = method.split('.')
|
|
1180
|
-
const binding = env[bindingName]
|
|
1181
|
-
|
|
1182
|
-
if (!binding) {
|
|
1183
|
-
throw new Error(\`Binding "\${bindingName}" not found\`)
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
|
-
const methodName = methodPath.join('.')
|
|
1187
|
-
|
|
1188
|
-
// KV operations
|
|
1189
|
-
if (methodName === 'get') return binding.get(params[0], params[1])
|
|
1190
|
-
if (methodName === 'put') return binding.put(params[0], params[1], params[2])
|
|
1191
|
-
if (methodName === 'delete') return binding.delete(params[0])
|
|
1192
|
-
if (methodName === 'list') return binding.list(params[0])
|
|
1193
|
-
if (methodName === 'getWithMetadata') return binding.getWithMetadata(params[0], params[1])
|
|
1194
|
-
|
|
1195
|
-
// R2 operations
|
|
1196
|
-
if (methodName === 'head') return binding.head(params[0])
|
|
1197
|
-
if (methodName === 'r2.get') return serializeR2Object(await binding.get(params[0], params[1]))
|
|
1198
|
-
if (methodName === 'r2.put') return serializeR2Object(await binding.put(params[0], params[1], params[2]))
|
|
1199
|
-
if (methodName === 'r2.delete') return binding.delete(params[0])
|
|
1200
|
-
if (methodName === 'r2.list') return serializeR2Objects(await binding.list(params[0]))
|
|
1201
|
-
|
|
1202
|
-
// D1 operations
|
|
1203
|
-
if (methodName === 'exec') return binding.exec(params[0])
|
|
1204
|
-
if (methodName === 'batch') {
|
|
1205
|
-
const statements = params[0].map(s => binding.prepare(s.sql).bind(...(s.bindings || [])))
|
|
1206
|
-
return binding.batch(statements)
|
|
1207
|
-
}
|
|
1208
|
-
if (methodName.startsWith('stmt.')) {
|
|
1209
|
-
const [, stmtMethod] = methodName.split('.')
|
|
1210
|
-
const [sql, ...bindings] = params
|
|
1211
|
-
const stmt = binding.prepare(sql).bind(...bindings.slice(0, -1))
|
|
1212
|
-
|
|
1213
|
-
if (stmtMethod === 'first') return stmt.first(bindings[bindings.length - 1])
|
|
1214
|
-
if (stmtMethod === 'all') return stmt.all()
|
|
1215
|
-
if (stmtMethod === 'run') return stmt.run()
|
|
1216
|
-
if (stmtMethod === 'raw') return stmt.raw(bindings[bindings.length - 1])
|
|
1217
|
-
}
|
|
1218
|
-
|
|
1219
|
-
// DO operations
|
|
1220
|
-
if (methodName === 'idFromName') {
|
|
1221
|
-
const id = binding.idFromName(params[0])
|
|
1222
|
-
return { __type: 'DOId', hex: id.toString() }
|
|
1223
|
-
}
|
|
1224
|
-
if (methodName === 'idFromString') {
|
|
1225
|
-
const id = binding.idFromString(params[0])
|
|
1226
|
-
return { __type: 'DOId', hex: id.toString() }
|
|
1227
|
-
}
|
|
1228
|
-
if (methodName === 'newUniqueId') {
|
|
1229
|
-
const id = binding.newUniqueId(params[0])
|
|
1230
|
-
return { __type: 'DOId', hex: id.toString() }
|
|
1231
|
-
}
|
|
1232
|
-
if (methodName === 'stub.fetch') {
|
|
1233
|
-
const [, doId, serializedReq] = params
|
|
1234
|
-
const id = binding.idFromString(doId.hex)
|
|
1235
|
-
const stub = binding.get(id)
|
|
1236
|
-
const response = await stub.fetch(new Request(serializedReq.url, {
|
|
1237
|
-
method: serializedReq.method,
|
|
1238
|
-
headers: serializedReq.headers,
|
|
1239
|
-
body: serializedReq.body?.type === 'bytes' ? atob(serializedReq.body.data) : undefined
|
|
1240
|
-
}))
|
|
1241
|
-
return serializeResponse(response)
|
|
1242
|
-
}
|
|
1243
|
-
if (methodName === 'stub.rpc') {
|
|
1244
|
-
// DO RPC: Call a method on the DO instance
|
|
1245
|
-
const [, doId, rpcMethod, rpcParams] = params
|
|
1246
|
-
const id = binding.idFromString(doId.hex)
|
|
1247
|
-
const stub = binding.get(id)
|
|
1248
|
-
|
|
1249
|
-
// Use fetch to call the RPC endpoint
|
|
1250
|
-
const response = await stub.fetch(new Request('http://do/_rpc', {
|
|
1251
|
-
method: 'POST',
|
|
1252
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1253
|
-
body: JSON.stringify({ method: rpcMethod, params: rpcParams })
|
|
1254
|
-
}))
|
|
1255
|
-
|
|
1256
|
-
const result = await response.json()
|
|
1257
|
-
if (!result.ok) throw new Error(result.error?.message || 'RPC failed')
|
|
1258
|
-
return result.result
|
|
1259
|
-
}
|
|
1260
|
-
|
|
1261
|
-
// Queue operations
|
|
1262
|
-
if (methodName === 'send') return binding.send(params[0], params[1])
|
|
1263
|
-
if (methodName === 'sendBatch') return binding.sendBatch(params[0], params[1])
|
|
1264
|
-
|
|
1265
|
-
// Generic fallback
|
|
1266
|
-
if (typeof binding[methodName] === 'function') {
|
|
1267
|
-
return binding[methodName](...params)
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
throw new Error(\`Unknown method: \${method}\`)
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
function serializeResponse(response) {
|
|
1274
|
-
return {
|
|
1275
|
-
status: response.status,
|
|
1276
|
-
statusText: response.statusText,
|
|
1277
|
-
headers: [...response.headers.entries()],
|
|
1278
|
-
body: null // Will be streamed separately for large bodies
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
|
-
function serializeR2Object(obj) {
|
|
1283
|
-
if (!obj) return null
|
|
1284
|
-
return {
|
|
1285
|
-
key: obj.key,
|
|
1286
|
-
version: obj.version,
|
|
1287
|
-
size: obj.size,
|
|
1288
|
-
etag: obj.etag,
|
|
1289
|
-
httpEtag: obj.httpEtag,
|
|
1290
|
-
uploaded: obj.uploaded?.toISOString(),
|
|
1291
|
-
httpMetadata: obj.httpMetadata,
|
|
1292
|
-
customMetadata: obj.customMetadata
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
|
|
1296
|
-
function serializeR2Objects(result) {
|
|
1297
|
-
if (!result) return null
|
|
1298
|
-
return {
|
|
1299
|
-
objects: result.objects.map(serializeR2Object),
|
|
1300
|
-
truncated: result.truncated,
|
|
1301
|
-
cursor: result.cursor,
|
|
1302
|
-
delimitedPrefixes: result.delimitedPrefixes
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
`;
|
|
1306
|
-
}
|
|
1307
|
-
async function startMiniflare(options = {}) {
|
|
1308
|
-
const { Miniflare, Log, LogLevel } = await import("miniflare");
|
|
1309
|
-
const port = options.port ?? 8787;
|
|
1310
|
-
const persistPath = options.persistPath ?? ".devflare/data";
|
|
1311
|
-
const mfConfig = {
|
|
1312
|
-
modules: true,
|
|
1313
|
-
script: generateGatewayScript(),
|
|
1314
|
-
port,
|
|
1315
|
-
host: "127.0.0.1",
|
|
1316
|
-
log: options.verbose ? new Log(LogLevel.DEBUG) : new Log(LogLevel.WARN),
|
|
1317
|
-
compatibilityDate: options.compatibilityDate ?? "2024-01-01",
|
|
1318
|
-
compatibilityFlags: options.compatibilityFlags ?? []
|
|
1319
|
-
};
|
|
1320
|
-
const hasBindings = (val) => {
|
|
1321
|
-
if (!val)
|
|
1322
|
-
return false;
|
|
1323
|
-
if (Array.isArray(val))
|
|
1324
|
-
return val.length > 0;
|
|
1325
|
-
return Object.keys(val).length > 0;
|
|
1326
|
-
};
|
|
1327
|
-
if (hasBindings(options.kvNamespaces)) {
|
|
1328
|
-
mfConfig.kvNamespaces = options.kvNamespaces;
|
|
1329
|
-
if (options.persist) {
|
|
1330
|
-
mfConfig.kvPersist = `${persistPath}/kv`;
|
|
1331
|
-
}
|
|
1332
|
-
}
|
|
1333
|
-
if (hasBindings(options.r2Buckets)) {
|
|
1334
|
-
mfConfig.r2Buckets = options.r2Buckets;
|
|
1335
|
-
if (options.persist) {
|
|
1336
|
-
mfConfig.r2Persist = `${persistPath}/r2`;
|
|
1337
|
-
}
|
|
1338
|
-
}
|
|
1339
|
-
if (hasBindings(options.d1Databases)) {
|
|
1340
|
-
mfConfig.d1Databases = options.d1Databases;
|
|
1341
|
-
if (options.persist) {
|
|
1342
|
-
mfConfig.d1Persist = `${persistPath}/d1`;
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
if (options.durableObjects) {
|
|
1346
|
-
mfConfig.durableObjects = options.durableObjects;
|
|
1347
|
-
if (options.persist) {
|
|
1348
|
-
mfConfig.durableObjectsPersist = `${persistPath}/do`;
|
|
1349
|
-
}
|
|
1350
|
-
}
|
|
1351
|
-
if (options.bindings) {
|
|
1352
|
-
mfConfig.bindings = options.bindings;
|
|
1353
|
-
}
|
|
1354
|
-
if (options.queues?.length) {
|
|
1355
|
-
mfConfig.queueProducers = Object.fromEntries(options.queues.map((q) => [q, { queueName: q }]));
|
|
1356
|
-
}
|
|
1357
|
-
const mf = new Miniflare(mfConfig);
|
|
1358
|
-
await mf.ready;
|
|
1359
|
-
return {
|
|
1360
|
-
ready: Promise.resolve(),
|
|
1361
|
-
async dispose() {
|
|
1362
|
-
await mf.dispose();
|
|
1363
|
-
},
|
|
1364
|
-
async getBindings() {
|
|
1365
|
-
return mf.getBindings();
|
|
1366
|
-
},
|
|
1367
|
-
getKVNamespace: mf.getKVNamespace.bind(mf),
|
|
1368
|
-
getR2Bucket: mf.getR2Bucket.bind(mf),
|
|
1369
|
-
getD1Database: mf.getD1Database.bind(mf),
|
|
1370
|
-
getDurableObjectNamespace: mf.getDurableObjectNamespace.bind(mf),
|
|
1371
|
-
dispatchFetch: mf.dispatchFetch.bind(mf),
|
|
1372
|
-
_mf: mf
|
|
1373
|
-
};
|
|
1374
|
-
}
|
|
1375
|
-
async function startMiniflareFromConfig(config, options = {}) {
|
|
1376
|
-
const bindings = config.bindings ?? {};
|
|
1377
|
-
const mfOptions = {
|
|
1378
|
-
...options,
|
|
1379
|
-
compatibilityDate: config.compatibilityDate,
|
|
1380
|
-
compatibilityFlags: config.compatibilityFlags,
|
|
1381
|
-
kvNamespaces: bindings.kv ? bindings.kv : undefined,
|
|
1382
|
-
r2Buckets: bindings.r2 ? bindings.r2 : undefined,
|
|
1383
|
-
d1Databases: bindings.d1 ? bindings.d1 : undefined,
|
|
1384
|
-
queues: bindings.queues?.consumers?.map((c) => c.queue),
|
|
1385
|
-
bindings: config.vars,
|
|
1386
|
-
durableObjects: bindings.durableObjects ? Object.fromEntries(Object.entries(bindings.durableObjects).map(([bindingName, doConfig]) => {
|
|
1387
|
-
const normalized = normalizeDOBinding(doConfig);
|
|
1388
|
-
return [
|
|
1389
|
-
bindingName,
|
|
1390
|
-
{
|
|
1391
|
-
className: normalized.className,
|
|
1392
|
-
scriptPath: normalized.scriptName
|
|
1393
|
-
}
|
|
1394
|
-
];
|
|
1395
|
-
})) : undefined
|
|
1396
|
-
};
|
|
1397
|
-
return startMiniflare(mfOptions);
|
|
1398
|
-
}
|
|
1399
|
-
var globalMiniflare = null;
|
|
1400
|
-
async function getMiniflare(options) {
|
|
1401
|
-
if (!globalMiniflare) {
|
|
1402
|
-
globalMiniflare = await startMiniflare(options);
|
|
1403
|
-
}
|
|
1404
|
-
return globalMiniflare;
|
|
1405
|
-
}
|
|
1406
|
-
async function stopMiniflare() {
|
|
1407
|
-
if (globalMiniflare) {
|
|
1408
|
-
await globalMiniflare.dispose();
|
|
1409
|
-
globalMiniflare = null;
|
|
1410
|
-
}
|
|
1411
|
-
}
|
|
1412
|
-
|
|
1413
|
-
export { BinaryKind, BinaryFlags, encodeBinaryFrame, decodeBinaryFrame, parseJsonMsg, stringifyJsonMsg, serializeValue, deserializeValue, base64Encode, base64Decode, BridgeClient, getClient, createEnvProxy, bridgeEnv, initEnv, setBindingHints, startMiniflare, startMiniflareFromConfig, getMiniflare, stopMiniflare };
|
|
1133
|
+
export { BinaryKind, BinaryFlags, encodeBinaryFrame, decodeBinaryFrame, parseJsonMsg, stringifyJsonMsg, serializeValue, deserializeValue, base64Encode, base64Decode, BridgeClient, getClient, createEnvProxy, bridgeEnv, initEnv, setBindingHints };
|
|
File without changes
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// src/runtime/context.ts
|
|
2
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
3
|
+
var storage = new AsyncLocalStorage;
|
|
4
|
+
function runWithContext(env, ctx, request, fn, type = "fetch") {
|
|
5
|
+
const context = {
|
|
6
|
+
env,
|
|
7
|
+
ctx,
|
|
8
|
+
request,
|
|
9
|
+
locals: {},
|
|
10
|
+
type
|
|
11
|
+
};
|
|
12
|
+
return storage.run(context, fn);
|
|
13
|
+
}
|
|
14
|
+
function getContext() {
|
|
15
|
+
const context = storage.getStore();
|
|
16
|
+
if (!context) {
|
|
17
|
+
throw new ContextUnavailableError;
|
|
18
|
+
}
|
|
19
|
+
return context;
|
|
20
|
+
}
|
|
21
|
+
function getContextOrNull() {
|
|
22
|
+
const context = storage.getStore();
|
|
23
|
+
return context ?? null;
|
|
24
|
+
}
|
|
25
|
+
function hasContext() {
|
|
26
|
+
return storage.getStore() !== undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
class ContextUnavailableError extends Error {
|
|
30
|
+
code = "CONTEXT_UNAVAILABLE";
|
|
31
|
+
constructor() {
|
|
32
|
+
super(`Context not available. This usually means one of:
|
|
33
|
+
|
|
34
|
+
` + `1. Accessing context at module top-level (runs at cold start, not per-request)
|
|
35
|
+
` + `2. Accessing context in setTimeout/setInterval callbacks
|
|
36
|
+
` + `3. Missing 'nodejs_compat' compatibility flag in your worker config
|
|
37
|
+
|
|
38
|
+
` + `Fix: Move the access inside your fetch handler or middleware.
|
|
39
|
+
` + `Learn more: https://devflare.dev/docs/context-errors`);
|
|
40
|
+
this.name = "ContextUnavailableError";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export { runWithContext, getContext, getContextOrNull, hasContext, ContextUnavailableError };
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import {
|
|
2
|
+
normalizeDOBinding
|
|
3
|
+
} from "./index-hcex3rgh.js";
|
|
4
|
+
import {
|
|
5
|
+
__require
|
|
6
|
+
} from "./index-37x76zdn.js";
|
|
7
|
+
|
|
8
|
+
// src/bridge/miniflare.ts
|
|
9
|
+
function generateGatewayScript() {
|
|
10
|
+
return `
|
|
11
|
+
// Gateway Worker — Provides RPC access to all bindings
|
|
12
|
+
export default {
|
|
13
|
+
async fetch(request, env, ctx) {
|
|
14
|
+
const url = new URL(request.url)
|
|
15
|
+
|
|
16
|
+
// Health check
|
|
17
|
+
if (url.pathname === '/_devflare/health') {
|
|
18
|
+
return new Response(JSON.stringify({ status: 'ok', bindings: Object.keys(env) }), {
|
|
19
|
+
headers: { 'Content-Type': 'application/json' }
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// RPC endpoint
|
|
24
|
+
if (url.pathname === '/_devflare/rpc' && request.method === 'POST') {
|
|
25
|
+
try {
|
|
26
|
+
const { method, params } = await request.json()
|
|
27
|
+
const result = await executeRpc(env, method, params)
|
|
28
|
+
return new Response(JSON.stringify({ ok: true, result }), {
|
|
29
|
+
headers: { 'Content-Type': 'application/json' }
|
|
30
|
+
})
|
|
31
|
+
} catch (error) {
|
|
32
|
+
return new Response(JSON.stringify({
|
|
33
|
+
ok: false,
|
|
34
|
+
error: { code: 'RPC_ERROR', message: error.message }
|
|
35
|
+
}), {
|
|
36
|
+
status: 500,
|
|
37
|
+
headers: { 'Content-Type': 'application/json' }
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return new Response('Devflare Gateway', { status: 200 })
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function executeRpc(env, method, params) {
|
|
47
|
+
const [bindingName, ...methodPath] = method.split('.')
|
|
48
|
+
const binding = env[bindingName]
|
|
49
|
+
|
|
50
|
+
if (!binding) {
|
|
51
|
+
throw new Error(\`Binding "\${bindingName}" not found\`)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const methodName = methodPath.join('.')
|
|
55
|
+
|
|
56
|
+
// KV operations
|
|
57
|
+
if (methodName === 'get') return binding.get(params[0], params[1])
|
|
58
|
+
if (methodName === 'put') return binding.put(params[0], params[1], params[2])
|
|
59
|
+
if (methodName === 'delete') return binding.delete(params[0])
|
|
60
|
+
if (methodName === 'list') return binding.list(params[0])
|
|
61
|
+
if (methodName === 'getWithMetadata') return binding.getWithMetadata(params[0], params[1])
|
|
62
|
+
|
|
63
|
+
// R2 operations
|
|
64
|
+
if (methodName === 'head') return binding.head(params[0])
|
|
65
|
+
if (methodName === 'r2.get') return serializeR2Object(await binding.get(params[0], params[1]))
|
|
66
|
+
if (methodName === 'r2.put') return serializeR2Object(await binding.put(params[0], params[1], params[2]))
|
|
67
|
+
if (methodName === 'r2.delete') return binding.delete(params[0])
|
|
68
|
+
if (methodName === 'r2.list') return serializeR2Objects(await binding.list(params[0]))
|
|
69
|
+
|
|
70
|
+
// D1 operations
|
|
71
|
+
if (methodName === 'exec') return binding.exec(params[0])
|
|
72
|
+
if (methodName === 'batch') {
|
|
73
|
+
const statements = params[0].map(s => binding.prepare(s.sql).bind(...(s.bindings || [])))
|
|
74
|
+
return binding.batch(statements)
|
|
75
|
+
}
|
|
76
|
+
if (methodName.startsWith('stmt.')) {
|
|
77
|
+
const [, stmtMethod] = methodName.split('.')
|
|
78
|
+
const [sql, ...bindings] = params
|
|
79
|
+
const stmt = binding.prepare(sql).bind(...bindings.slice(0, -1))
|
|
80
|
+
|
|
81
|
+
if (stmtMethod === 'first') return stmt.first(bindings[bindings.length - 1])
|
|
82
|
+
if (stmtMethod === 'all') return stmt.all()
|
|
83
|
+
if (stmtMethod === 'run') return stmt.run()
|
|
84
|
+
if (stmtMethod === 'raw') return stmt.raw(bindings[bindings.length - 1])
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// DO operations
|
|
88
|
+
if (methodName === 'idFromName') {
|
|
89
|
+
const id = binding.idFromName(params[0])
|
|
90
|
+
return { __type: 'DOId', hex: id.toString() }
|
|
91
|
+
}
|
|
92
|
+
if (methodName === 'idFromString') {
|
|
93
|
+
const id = binding.idFromString(params[0])
|
|
94
|
+
return { __type: 'DOId', hex: id.toString() }
|
|
95
|
+
}
|
|
96
|
+
if (methodName === 'newUniqueId') {
|
|
97
|
+
const id = binding.newUniqueId(params[0])
|
|
98
|
+
return { __type: 'DOId', hex: id.toString() }
|
|
99
|
+
}
|
|
100
|
+
if (methodName === 'stub.fetch') {
|
|
101
|
+
const [, doId, serializedReq] = params
|
|
102
|
+
const id = binding.idFromString(doId.hex)
|
|
103
|
+
const stub = binding.get(id)
|
|
104
|
+
const response = await stub.fetch(new Request(serializedReq.url, {
|
|
105
|
+
method: serializedReq.method,
|
|
106
|
+
headers: serializedReq.headers,
|
|
107
|
+
body: serializedReq.body?.type === 'bytes' ? atob(serializedReq.body.data) : undefined
|
|
108
|
+
}))
|
|
109
|
+
return serializeResponse(response)
|
|
110
|
+
}
|
|
111
|
+
if (methodName === 'stub.rpc') {
|
|
112
|
+
// DO RPC: Call a method on the DO instance
|
|
113
|
+
const [, doId, rpcMethod, rpcParams] = params
|
|
114
|
+
const id = binding.idFromString(doId.hex)
|
|
115
|
+
const stub = binding.get(id)
|
|
116
|
+
|
|
117
|
+
// Use fetch to call the RPC endpoint
|
|
118
|
+
const response = await stub.fetch(new Request('http://do/_rpc', {
|
|
119
|
+
method: 'POST',
|
|
120
|
+
headers: { 'Content-Type': 'application/json' },
|
|
121
|
+
body: JSON.stringify({ method: rpcMethod, params: rpcParams })
|
|
122
|
+
}))
|
|
123
|
+
|
|
124
|
+
const result = await response.json()
|
|
125
|
+
if (!result.ok) throw new Error(result.error?.message || 'RPC failed')
|
|
126
|
+
return result.result
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Queue operations
|
|
130
|
+
if (methodName === 'send') return binding.send(params[0], params[1])
|
|
131
|
+
if (methodName === 'sendBatch') return binding.sendBatch(params[0], params[1])
|
|
132
|
+
|
|
133
|
+
// Generic fallback
|
|
134
|
+
if (typeof binding[methodName] === 'function') {
|
|
135
|
+
return binding[methodName](...params)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
throw new Error(\`Unknown method: \${method}\`)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function serializeResponse(response) {
|
|
142
|
+
return {
|
|
143
|
+
status: response.status,
|
|
144
|
+
statusText: response.statusText,
|
|
145
|
+
headers: [...response.headers.entries()],
|
|
146
|
+
body: null // Will be streamed separately for large bodies
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function serializeR2Object(obj) {
|
|
151
|
+
if (!obj) return null
|
|
152
|
+
return {
|
|
153
|
+
key: obj.key,
|
|
154
|
+
version: obj.version,
|
|
155
|
+
size: obj.size,
|
|
156
|
+
etag: obj.etag,
|
|
157
|
+
httpEtag: obj.httpEtag,
|
|
158
|
+
uploaded: obj.uploaded?.toISOString(),
|
|
159
|
+
httpMetadata: obj.httpMetadata,
|
|
160
|
+
customMetadata: obj.customMetadata
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function serializeR2Objects(result) {
|
|
165
|
+
if (!result) return null
|
|
166
|
+
return {
|
|
167
|
+
objects: result.objects.map(serializeR2Object),
|
|
168
|
+
truncated: result.truncated,
|
|
169
|
+
cursor: result.cursor,
|
|
170
|
+
delimitedPrefixes: result.delimitedPrefixes
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
`;
|
|
174
|
+
}
|
|
175
|
+
async function startMiniflare(options = {}) {
|
|
176
|
+
const { Miniflare, Log, LogLevel } = await import("miniflare");
|
|
177
|
+
const port = options.port ?? 8787;
|
|
178
|
+
const persistPath = options.persistPath ?? ".devflare/data";
|
|
179
|
+
const mfConfig = {
|
|
180
|
+
modules: true,
|
|
181
|
+
script: generateGatewayScript(),
|
|
182
|
+
port,
|
|
183
|
+
host: "127.0.0.1",
|
|
184
|
+
log: options.verbose ? new Log(LogLevel.DEBUG) : new Log(LogLevel.WARN),
|
|
185
|
+
compatibilityDate: options.compatibilityDate ?? "2024-01-01",
|
|
186
|
+
compatibilityFlags: options.compatibilityFlags ?? []
|
|
187
|
+
};
|
|
188
|
+
const hasBindings = (val) => {
|
|
189
|
+
if (!val)
|
|
190
|
+
return false;
|
|
191
|
+
if (Array.isArray(val))
|
|
192
|
+
return val.length > 0;
|
|
193
|
+
return Object.keys(val).length > 0;
|
|
194
|
+
};
|
|
195
|
+
if (hasBindings(options.kvNamespaces)) {
|
|
196
|
+
mfConfig.kvNamespaces = options.kvNamespaces;
|
|
197
|
+
if (options.persist) {
|
|
198
|
+
mfConfig.kvPersist = `${persistPath}/kv`;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (hasBindings(options.r2Buckets)) {
|
|
202
|
+
mfConfig.r2Buckets = options.r2Buckets;
|
|
203
|
+
if (options.persist) {
|
|
204
|
+
mfConfig.r2Persist = `${persistPath}/r2`;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (hasBindings(options.d1Databases)) {
|
|
208
|
+
mfConfig.d1Databases = options.d1Databases;
|
|
209
|
+
if (options.persist) {
|
|
210
|
+
mfConfig.d1Persist = `${persistPath}/d1`;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (options.durableObjects) {
|
|
214
|
+
mfConfig.durableObjects = options.durableObjects;
|
|
215
|
+
if (options.persist) {
|
|
216
|
+
mfConfig.durableObjectsPersist = `${persistPath}/do`;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (options.bindings) {
|
|
220
|
+
mfConfig.bindings = options.bindings;
|
|
221
|
+
}
|
|
222
|
+
if (options.queues?.length) {
|
|
223
|
+
mfConfig.queueProducers = Object.fromEntries(options.queues.map((q) => [q, { queueName: q }]));
|
|
224
|
+
}
|
|
225
|
+
const mf = new Miniflare(mfConfig);
|
|
226
|
+
await mf.ready;
|
|
227
|
+
return {
|
|
228
|
+
ready: Promise.resolve(),
|
|
229
|
+
async dispose() {
|
|
230
|
+
await mf.dispose();
|
|
231
|
+
},
|
|
232
|
+
async getBindings() {
|
|
233
|
+
return mf.getBindings();
|
|
234
|
+
},
|
|
235
|
+
getKVNamespace: mf.getKVNamespace.bind(mf),
|
|
236
|
+
getR2Bucket: mf.getR2Bucket.bind(mf),
|
|
237
|
+
getD1Database: mf.getD1Database.bind(mf),
|
|
238
|
+
getDurableObjectNamespace: mf.getDurableObjectNamespace.bind(mf),
|
|
239
|
+
dispatchFetch: mf.dispatchFetch.bind(mf),
|
|
240
|
+
_mf: mf
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
async function startMiniflareFromConfig(config, options = {}) {
|
|
244
|
+
const bindings = config.bindings ?? {};
|
|
245
|
+
const mfOptions = {
|
|
246
|
+
...options,
|
|
247
|
+
compatibilityDate: config.compatibilityDate,
|
|
248
|
+
compatibilityFlags: config.compatibilityFlags,
|
|
249
|
+
kvNamespaces: bindings.kv ? bindings.kv : undefined,
|
|
250
|
+
r2Buckets: bindings.r2 ? bindings.r2 : undefined,
|
|
251
|
+
d1Databases: bindings.d1 ? bindings.d1 : undefined,
|
|
252
|
+
queues: bindings.queues?.consumers?.map((c) => c.queue),
|
|
253
|
+
bindings: config.vars,
|
|
254
|
+
durableObjects: bindings.durableObjects ? Object.fromEntries(Object.entries(bindings.durableObjects).map(([bindingName, doConfig]) => {
|
|
255
|
+
const normalized = normalizeDOBinding(doConfig);
|
|
256
|
+
return [
|
|
257
|
+
bindingName,
|
|
258
|
+
{
|
|
259
|
+
className: normalized.className,
|
|
260
|
+
scriptPath: normalized.scriptName
|
|
261
|
+
}
|
|
262
|
+
];
|
|
263
|
+
})) : undefined
|
|
264
|
+
};
|
|
265
|
+
return startMiniflare(mfOptions);
|
|
266
|
+
}
|
|
267
|
+
var globalMiniflare = null;
|
|
268
|
+
async function getMiniflare(options) {
|
|
269
|
+
if (!globalMiniflare) {
|
|
270
|
+
globalMiniflare = await startMiniflare(options);
|
|
271
|
+
}
|
|
272
|
+
return globalMiniflare;
|
|
273
|
+
}
|
|
274
|
+
async function stopMiniflare() {
|
|
275
|
+
if (globalMiniflare) {
|
|
276
|
+
await globalMiniflare.dispose();
|
|
277
|
+
globalMiniflare = null;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export { startMiniflare, startMiniflareFromConfig, getMiniflare, stopMiniflare };
|