devflare 1.0.0-next.23 → 1.0.0-next.24
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 +77 -2
- package/dist/account-5nm1xn0v.js +475 -0
- package/dist/browser.d.ts +22 -2
- package/dist/browser.d.ts.map +1 -1
- package/dist/build-vctfnmsf.js +54 -0
- package/dist/cli/commands/deploy/prepare.d.ts.map +1 -1
- package/dist/cli/commands/deploy.d.ts.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/config/schema-env.d.ts +22 -2
- package/dist/config/schema-env.d.ts.map +1 -1
- package/dist/config/schema-runtime.d.ts +11 -1
- package/dist/config/schema-runtime.d.ts.map +1 -1
- package/dist/config/schema-types-runtime.d.ts +3 -0
- package/dist/config/schema-types-runtime.d.ts.map +1 -1
- package/dist/config/schema-types.d.ts +2 -2
- package/dist/config/schema.d.ts +33 -3
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config-rq32csms.js +105 -0
- package/dist/deploy-krj3k9zm.js +1055 -0
- package/dist/deploy-mem96qyn.js +1066 -0
- package/dist/dev-apkr7cfv.js +2597 -0
- package/dist/doctor-9asw8x18.js +259 -0
- package/dist/index-4j9ah79n.js +1033 -0
- package/dist/index-bfjpjs07.js +581 -0
- package/dist/index-cna43592.js +1573 -0
- package/dist/index-d00njc1f.js +147 -0
- package/dist/index-dgeamyfk.js +1426 -0
- package/dist/index-ftf7yqhs.js +74 -0
- package/dist/index-h332fg62.js +1205 -0
- package/dist/index-ja2rdbt0.js +476 -0
- package/dist/index-kc207nyr.js +52 -0
- package/dist/index-meq8ydc0.js +895 -0
- package/dist/index-myfjejs0.js +185 -0
- package/dist/index-qkfvd3cs.js +109 -0
- package/dist/index-rnz879kf.js +1426 -0
- package/dist/index-s96e5dd9.js +699 -0
- package/dist/index.js +3 -3
- package/dist/login-g9rb7dj3.js +77 -0
- package/dist/previews-ykamw25e.js +1337 -0
- package/dist/productions-4m1pd6ts.js +505 -0
- package/dist/secrets-gywxctdh.js +91 -0
- package/dist/sveltekit/index.js +3 -3
- package/dist/test/index.js +6 -6
- package/dist/types-17kkqw37.js +705 -0
- package/dist/vite/index.js +4 -4
- package/dist/worker-4fd49jm0.js +513 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1033 @@
|
|
|
1
|
+
import {
|
|
2
|
+
discoverEntrypointsSync,
|
|
3
|
+
resolvePackageSpecifier
|
|
4
|
+
} from "./index-3edvz3hs.js";
|
|
5
|
+
import {
|
|
6
|
+
DEFAULT_DO_PATTERN,
|
|
7
|
+
findFiles,
|
|
8
|
+
findFilesSync
|
|
9
|
+
} from "./index-qwgr4q7s.js";
|
|
10
|
+
import {
|
|
11
|
+
SUPPORTED_WORKER_EXTENSIONS,
|
|
12
|
+
transformWorkerEntrypoint
|
|
13
|
+
} from "./index-aqrwyy57.js";
|
|
14
|
+
import {
|
|
15
|
+
findDurableObjectClasses
|
|
16
|
+
} from "./index-vhqww6tt.js";
|
|
17
|
+
import {
|
|
18
|
+
configSchema,
|
|
19
|
+
getLocalD1DatabaseIdentifier,
|
|
20
|
+
getLocalKVNamespaceIdentifier,
|
|
21
|
+
normalizeArtifactsBinding,
|
|
22
|
+
normalizeDOBinding,
|
|
23
|
+
normalizeDispatchNamespaceBinding,
|
|
24
|
+
normalizeHyperdriveBinding,
|
|
25
|
+
normalizeImagesBinding,
|
|
26
|
+
normalizeMediaBinding,
|
|
27
|
+
normalizeMtlsCertificateBinding,
|
|
28
|
+
normalizePipelineBinding,
|
|
29
|
+
normalizeSecretsStoreBinding,
|
|
30
|
+
normalizeWorkflowBinding
|
|
31
|
+
} from "./index-cna43592.js";
|
|
32
|
+
import {
|
|
33
|
+
__require
|
|
34
|
+
} from "./index-37x76zdn.js";
|
|
35
|
+
|
|
36
|
+
// src/test/resolve-service-bindings.ts
|
|
37
|
+
import { dirname, join, resolve } from "path";
|
|
38
|
+
import { existsSync, readFileSync } from "fs";
|
|
39
|
+
|
|
40
|
+
// src/dev-server/miniflare-bindings.ts
|
|
41
|
+
function buildQueueProducers(bindings) {
|
|
42
|
+
if (!bindings.queues?.producers) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const producers = {};
|
|
46
|
+
for (const [bindingName, queueName] of Object.entries(bindings.queues.producers)) {
|
|
47
|
+
producers[bindingName] = { queueName };
|
|
48
|
+
}
|
|
49
|
+
return producers;
|
|
50
|
+
}
|
|
51
|
+
function buildQueueConsumers(bindings) {
|
|
52
|
+
if (!bindings.queues?.consumers || bindings.queues.consumers.length === 0) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const consumers = {};
|
|
56
|
+
for (const consumer of bindings.queues.consumers) {
|
|
57
|
+
consumers[consumer.queue] = {
|
|
58
|
+
...consumer.maxBatchSize !== undefined && { maxBatchSize: consumer.maxBatchSize },
|
|
59
|
+
...consumer.maxBatchTimeout !== undefined && { maxBatchTimeout: consumer.maxBatchTimeout },
|
|
60
|
+
...consumer.maxRetries !== undefined && { maxRetries: consumer.maxRetries },
|
|
61
|
+
...consumer.deadLetterQueue && { deadLetterQueue: consumer.deadLetterQueue },
|
|
62
|
+
...consumer.maxConcurrency !== undefined && { maxConcurrency: consumer.maxConcurrency },
|
|
63
|
+
...consumer.retryDelay !== undefined && { retryDelay: consumer.retryDelay }
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return consumers;
|
|
67
|
+
}
|
|
68
|
+
function buildRateLimitsConfig(bindings) {
|
|
69
|
+
if (!bindings.rateLimits) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
return Object.fromEntries(Object.entries(bindings.rateLimits).map(([name, binding]) => [
|
|
73
|
+
name,
|
|
74
|
+
{
|
|
75
|
+
simple: {
|
|
76
|
+
limit: binding.simple.limit,
|
|
77
|
+
period: binding.simple.period
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
]));
|
|
81
|
+
}
|
|
82
|
+
function buildVersionMetadataConfig(bindings) {
|
|
83
|
+
return bindings.versionMetadata?.binding;
|
|
84
|
+
}
|
|
85
|
+
function buildWorkerLoadersConfig(bindings) {
|
|
86
|
+
if (!bindings.workerLoaders) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
return Object.fromEntries(Object.keys(bindings.workerLoaders).map((bindingName) => [bindingName, {}]));
|
|
90
|
+
}
|
|
91
|
+
function buildMtlsCertificatesConfig(bindings) {
|
|
92
|
+
if (!bindings.mtlsCertificates) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
return Object.fromEntries(Object.entries(bindings.mtlsCertificates).map(([bindingName, binding]) => {
|
|
96
|
+
const normalized = normalizeMtlsCertificateBinding(binding);
|
|
97
|
+
return [
|
|
98
|
+
bindingName,
|
|
99
|
+
{
|
|
100
|
+
certificate_id: normalized.certificateId
|
|
101
|
+
}
|
|
102
|
+
];
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
function buildDispatchNamespacesConfig(bindings) {
|
|
106
|
+
if (!bindings.dispatchNamespaces) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
return Object.fromEntries(Object.entries(bindings.dispatchNamespaces).map(([bindingName, binding]) => {
|
|
110
|
+
const normalized = normalizeDispatchNamespaceBinding(binding);
|
|
111
|
+
return [
|
|
112
|
+
bindingName,
|
|
113
|
+
{
|
|
114
|
+
namespace: normalized.namespace
|
|
115
|
+
}
|
|
116
|
+
];
|
|
117
|
+
}));
|
|
118
|
+
}
|
|
119
|
+
function buildWorkflowsConfig(bindings) {
|
|
120
|
+
if (!bindings.workflows) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
return Object.fromEntries(Object.entries(bindings.workflows).map(([bindingName, binding]) => {
|
|
124
|
+
const normalized = normalizeWorkflowBinding(binding);
|
|
125
|
+
return [
|
|
126
|
+
bindingName,
|
|
127
|
+
{
|
|
128
|
+
name: normalized.name,
|
|
129
|
+
className: normalized.className,
|
|
130
|
+
...normalized.scriptName && { scriptName: normalized.scriptName },
|
|
131
|
+
...normalized.limits && { stepLimit: normalized.limits.steps }
|
|
132
|
+
}
|
|
133
|
+
];
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
function buildPipelinesConfig(bindings) {
|
|
137
|
+
if (!bindings.pipelines) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
return Object.fromEntries(Object.entries(bindings.pipelines).map(([bindingName, binding]) => {
|
|
141
|
+
const normalized = normalizePipelineBinding(binding);
|
|
142
|
+
return [
|
|
143
|
+
bindingName,
|
|
144
|
+
typeof binding === "string" ? normalized.pipeline : { pipeline: normalized.pipeline }
|
|
145
|
+
];
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
148
|
+
function getHyperdriveLocalConnectionString(bindingName, binding) {
|
|
149
|
+
const cloudflareEnvName = `CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_${bindingName}`;
|
|
150
|
+
const wranglerEnvName = `WRANGLER_HYPERDRIVE_LOCAL_CONNECTION_STRING_${bindingName}`;
|
|
151
|
+
const envValue = process.env[cloudflareEnvName] ?? process.env[wranglerEnvName];
|
|
152
|
+
if (envValue?.trim()) {
|
|
153
|
+
return envValue;
|
|
154
|
+
}
|
|
155
|
+
const normalized = normalizeHyperdriveBinding(binding);
|
|
156
|
+
return normalized.localConnectionString;
|
|
157
|
+
}
|
|
158
|
+
function buildHyperdrivesConfig(bindings) {
|
|
159
|
+
if (!bindings.hyperdrive) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const hyperdrives = Object.fromEntries(Object.entries(bindings.hyperdrive).map(([bindingName, binding]) => {
|
|
163
|
+
const localConnectionString = getHyperdriveLocalConnectionString(bindingName, binding);
|
|
164
|
+
return localConnectionString ? [bindingName, localConnectionString] : null;
|
|
165
|
+
}).filter((entry) => entry !== null));
|
|
166
|
+
return Object.keys(hyperdrives).length > 0 ? hyperdrives : undefined;
|
|
167
|
+
}
|
|
168
|
+
function buildImagesConfig(bindings) {
|
|
169
|
+
if (!bindings.images) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const [entry] = Object.entries(bindings.images);
|
|
173
|
+
if (!entry) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const [bindingName, binding] = entry;
|
|
177
|
+
const normalized = normalizeImagesBinding(bindingName, binding);
|
|
178
|
+
return {
|
|
179
|
+
binding: normalized.binding
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function buildMediaConfig(bindings) {
|
|
183
|
+
if (!bindings.media) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const [entry] = Object.entries(bindings.media);
|
|
187
|
+
if (!entry) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const [bindingName, binding] = entry;
|
|
191
|
+
const normalized = normalizeMediaBinding(bindingName, binding);
|
|
192
|
+
return {
|
|
193
|
+
binding: normalized.binding
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function buildArtifactsConfig(bindings) {
|
|
197
|
+
if (!bindings.artifacts) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
return Object.fromEntries(Object.entries(bindings.artifacts).map(([bindingName, binding]) => {
|
|
201
|
+
const normalized = normalizeArtifactsBinding(binding);
|
|
202
|
+
return [
|
|
203
|
+
bindingName,
|
|
204
|
+
{
|
|
205
|
+
namespace: normalized.namespace
|
|
206
|
+
}
|
|
207
|
+
];
|
|
208
|
+
}));
|
|
209
|
+
}
|
|
210
|
+
function buildAiSearchNamespacesConfig(bindings) {
|
|
211
|
+
if (!bindings.aiSearchNamespaces) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
return Object.fromEntries(Object.entries(bindings.aiSearchNamespaces).map(([bindingName, binding]) => [
|
|
215
|
+
bindingName,
|
|
216
|
+
{
|
|
217
|
+
namespace: binding.namespace
|
|
218
|
+
}
|
|
219
|
+
]));
|
|
220
|
+
}
|
|
221
|
+
function buildAiSearchInstancesConfig(bindings) {
|
|
222
|
+
if (!bindings.aiSearch) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
return Object.fromEntries(Object.entries(bindings.aiSearch).map(([bindingName, binding]) => [
|
|
226
|
+
bindingName,
|
|
227
|
+
{
|
|
228
|
+
instance_name: binding.instanceName
|
|
229
|
+
}
|
|
230
|
+
]));
|
|
231
|
+
}
|
|
232
|
+
function buildSecretsStoreConfig(bindings, defaultSecretsStoreId, excludedBindingNames = new Set) {
|
|
233
|
+
if (!bindings.secretsStore) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const entries = Object.entries(bindings.secretsStore).flatMap(([bindingName, binding]) => {
|
|
237
|
+
if (excludedBindingNames.has(bindingName)) {
|
|
238
|
+
return [];
|
|
239
|
+
}
|
|
240
|
+
const normalized = normalizeSecretsStoreBinding(binding, defaultSecretsStoreId, bindingName);
|
|
241
|
+
return [[
|
|
242
|
+
bindingName,
|
|
243
|
+
{
|
|
244
|
+
store_id: normalized.storeId,
|
|
245
|
+
secret_name: normalized.secretName
|
|
246
|
+
}
|
|
247
|
+
]];
|
|
248
|
+
});
|
|
249
|
+
return entries.length > 0 ? Object.fromEntries(entries) : undefined;
|
|
250
|
+
}
|
|
251
|
+
function buildSendEmailConfig(bindings) {
|
|
252
|
+
if (!bindings.sendEmail) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
send_email: Object.entries(bindings.sendEmail).map(([name, binding]) => ({
|
|
257
|
+
name,
|
|
258
|
+
...binding.destinationAddress && {
|
|
259
|
+
destination_address: binding.destinationAddress
|
|
260
|
+
},
|
|
261
|
+
...binding.allowedDestinationAddresses && {
|
|
262
|
+
allowed_destination_addresses: binding.allowedDestinationAddresses
|
|
263
|
+
},
|
|
264
|
+
...binding.allowedSenderAddresses && {
|
|
265
|
+
allowed_sender_addresses: binding.allowedSenderAddresses
|
|
266
|
+
}
|
|
267
|
+
}))
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// src/test/resolve-service-bindings.ts
|
|
272
|
+
function getBunRuntime() {
|
|
273
|
+
const g = globalThis;
|
|
274
|
+
if (typeof g.Bun === "object" && g.Bun !== null) {
|
|
275
|
+
return g.Bun;
|
|
276
|
+
}
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
function discoverDOFilesSync(dir, pattern = DEFAULT_DO_PATTERN) {
|
|
280
|
+
const classToPath = new Map;
|
|
281
|
+
try {
|
|
282
|
+
const files = findFilesSync(pattern, { cwd: dir });
|
|
283
|
+
for (const filePath of files) {
|
|
284
|
+
try {
|
|
285
|
+
const code = readFileSync(filePath, "utf-8");
|
|
286
|
+
const classNames = findDurableObjectClasses(code);
|
|
287
|
+
for (const className of classNames) {
|
|
288
|
+
if (!classToPath.has(className)) {
|
|
289
|
+
classToPath.set(className, filePath);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
} catch {}
|
|
293
|
+
}
|
|
294
|
+
} catch {}
|
|
295
|
+
return classToPath;
|
|
296
|
+
}
|
|
297
|
+
var bundleCache = new Map;
|
|
298
|
+
function clearBundleCache() {
|
|
299
|
+
bundleCache.clear();
|
|
300
|
+
}
|
|
301
|
+
function findDefaultServiceWorkerEntrypoint(refConfigDir) {
|
|
302
|
+
for (const candidate of ["src/worker.ts", "src/worker.js"]) {
|
|
303
|
+
const absolutePath = resolve(refConfigDir, candidate);
|
|
304
|
+
if (existsSync(absolutePath)) {
|
|
305
|
+
return absolutePath;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
function buildRawServiceBindings(services) {
|
|
311
|
+
if (!services || Object.keys(services).length === 0) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
return Object.fromEntries(Object.entries(services).map(([bindingName, binding]) => [
|
|
315
|
+
bindingName,
|
|
316
|
+
{
|
|
317
|
+
name: binding.service,
|
|
318
|
+
...binding.entrypoint && { entrypoint: binding.entrypoint }
|
|
319
|
+
}
|
|
320
|
+
]));
|
|
321
|
+
}
|
|
322
|
+
function buildReferencedWorkerRuntimeConfig(config) {
|
|
323
|
+
const bindings = config.bindings ?? {};
|
|
324
|
+
const queueProducers = buildQueueProducers(bindings);
|
|
325
|
+
const queueConsumers = buildQueueConsumers(bindings);
|
|
326
|
+
const rateLimits = buildRateLimitsConfig(bindings);
|
|
327
|
+
const versionMetadata = buildVersionMetadataConfig(bindings);
|
|
328
|
+
const workerLoaders = buildWorkerLoadersConfig(bindings);
|
|
329
|
+
const mtlsCertificates = buildMtlsCertificatesConfig(bindings);
|
|
330
|
+
const dispatchNamespaces = buildDispatchNamespacesConfig(bindings);
|
|
331
|
+
const workflows = buildWorkflowsConfig(bindings);
|
|
332
|
+
const pipelines = buildPipelinesConfig(bindings);
|
|
333
|
+
const hyperdrives = buildHyperdrivesConfig(bindings);
|
|
334
|
+
const media = buildMediaConfig(bindings);
|
|
335
|
+
const artifacts = buildArtifactsConfig(bindings);
|
|
336
|
+
const aiSearchNamespaces = buildAiSearchNamespacesConfig(bindings);
|
|
337
|
+
const aiSearchInstances = buildAiSearchInstancesConfig(bindings);
|
|
338
|
+
const secretsStoreSecrets = buildSecretsStoreConfig(bindings, config.secretsStoreId);
|
|
339
|
+
const email = buildSendEmailConfig(bindings);
|
|
340
|
+
const serviceBindings = buildRawServiceBindings(bindings.services);
|
|
341
|
+
return {
|
|
342
|
+
...config.compatibilityFlags && { compatibilityFlags: config.compatibilityFlags },
|
|
343
|
+
...config.vars && { bindings: config.vars },
|
|
344
|
+
...bindings.kv && {
|
|
345
|
+
kvNamespaces: Object.fromEntries(Object.entries(bindings.kv).map(([bindingName, bindingConfig]) => [
|
|
346
|
+
bindingName,
|
|
347
|
+
getLocalKVNamespaceIdentifier(bindingConfig)
|
|
348
|
+
]))
|
|
349
|
+
},
|
|
350
|
+
...bindings.r2 && { r2Buckets: bindings.r2 },
|
|
351
|
+
...bindings.d1 && {
|
|
352
|
+
d1Databases: Object.fromEntries(Object.entries(bindings.d1).map(([bindingName, bindingConfig]) => [
|
|
353
|
+
bindingName,
|
|
354
|
+
getLocalD1DatabaseIdentifier(bindingConfig)
|
|
355
|
+
]))
|
|
356
|
+
},
|
|
357
|
+
...queueProducers && { queueProducers },
|
|
358
|
+
...queueConsumers && { queueConsumers },
|
|
359
|
+
...rateLimits && { ratelimits: rateLimits },
|
|
360
|
+
...versionMetadata && { versionMetadata },
|
|
361
|
+
...workerLoaders && { workerLoaders },
|
|
362
|
+
...mtlsCertificates && { mtlsCertificates },
|
|
363
|
+
...dispatchNamespaces && { dispatchNamespaces },
|
|
364
|
+
...workflows && { workflows },
|
|
365
|
+
...pipelines && { pipelines },
|
|
366
|
+
...hyperdrives && { hyperdrives },
|
|
367
|
+
...media && { media },
|
|
368
|
+
...artifacts && { artifacts },
|
|
369
|
+
...aiSearchNamespaces && { aiSearchNamespaces },
|
|
370
|
+
...aiSearchInstances && { aiSearchInstances },
|
|
371
|
+
...secretsStoreSecrets && { secretsStoreSecrets },
|
|
372
|
+
...email && { email },
|
|
373
|
+
...serviceBindings && { serviceBindings }
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
function normalizeReferencedConfig(config) {
|
|
377
|
+
return configSchema.parse(config);
|
|
378
|
+
}
|
|
379
|
+
function resolveReferencedConfigDir(ref, parentConfigDir) {
|
|
380
|
+
const configPath = ref.configPath;
|
|
381
|
+
if (!configPath || configPath === "<resolved>") {
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
return dirname(resolvePackageSpecifier(configPath, parentConfigDir));
|
|
385
|
+
}
|
|
386
|
+
async function resolveReferencedLocalDurableObjects(config, configDir, workerName, serviceBindings = {}) {
|
|
387
|
+
const doPattern = config.files?.durableObjects;
|
|
388
|
+
const dosConfig = config.bindings?.durableObjects;
|
|
389
|
+
if (typeof doPattern !== "string" || !dosConfig || Object.keys(dosConfig).length === 0) {
|
|
390
|
+
return { workers: [], bindings: {} };
|
|
391
|
+
}
|
|
392
|
+
const discoveredDOs = discoverDOFilesSync(configDir, doPattern);
|
|
393
|
+
const doClasses = [];
|
|
394
|
+
for (const [bindingName, rawDoConfig] of Object.entries(dosConfig)) {
|
|
395
|
+
const doConfig = normalizeDOBinding(rawDoConfig);
|
|
396
|
+
if (doConfig.kind !== "local") {
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
const scriptPath = discoveredDOs.get(doConfig.className);
|
|
400
|
+
if (!scriptPath) {
|
|
401
|
+
console.warn(`[devflare] DO "${bindingName}" (class: ${doConfig.className}) not found in files.durableObjects for "${workerName}"`);
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
doClasses.push({ bindingName, className: doConfig.className, scriptPath });
|
|
405
|
+
}
|
|
406
|
+
if (doClasses.length === 0) {
|
|
407
|
+
return { workers: [], bindings: {} };
|
|
408
|
+
}
|
|
409
|
+
const doWorkerName = `${workerName}-durable-objects`;
|
|
410
|
+
const script = await bundleDOClasses(doClasses, doWorkerName);
|
|
411
|
+
if (!script) {
|
|
412
|
+
return { workers: [], bindings: {} };
|
|
413
|
+
}
|
|
414
|
+
const durableObjects = Object.fromEntries(doClasses.map((do_) => [do_.bindingName, do_.className]));
|
|
415
|
+
const runtimeConfig = buildReferencedWorkerRuntimeConfig(config);
|
|
416
|
+
const doRuntimeConfig = { ...runtimeConfig };
|
|
417
|
+
delete doRuntimeConfig.queueConsumers;
|
|
418
|
+
const mergedServiceBindings = {
|
|
419
|
+
...runtimeConfig.serviceBindings ?? {},
|
|
420
|
+
...serviceBindings
|
|
421
|
+
};
|
|
422
|
+
return {
|
|
423
|
+
workers: [{
|
|
424
|
+
name: doWorkerName,
|
|
425
|
+
script,
|
|
426
|
+
modules: true,
|
|
427
|
+
compatibilityDate: config.compatibilityDate,
|
|
428
|
+
...doRuntimeConfig,
|
|
429
|
+
...Object.keys(mergedServiceBindings).length > 0 && {
|
|
430
|
+
serviceBindings: mergedServiceBindings
|
|
431
|
+
},
|
|
432
|
+
durableObjects
|
|
433
|
+
}],
|
|
434
|
+
bindings: Object.fromEntries(doClasses.map((do_) => [
|
|
435
|
+
do_.bindingName,
|
|
436
|
+
{
|
|
437
|
+
className: do_.className,
|
|
438
|
+
scriptName: doWorkerName
|
|
439
|
+
}
|
|
440
|
+
]))
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
function hasServiceBindings(config) {
|
|
444
|
+
const services = config.bindings?.services;
|
|
445
|
+
if (!services)
|
|
446
|
+
return false;
|
|
447
|
+
return Object.keys(services).length > 0;
|
|
448
|
+
}
|
|
449
|
+
async function resolveServiceBindings(config, configDir, seenWorkers = new Set) {
|
|
450
|
+
const services = config.bindings?.services;
|
|
451
|
+
if (!services) {
|
|
452
|
+
return { workers: [], primaryServiceBindings: {} };
|
|
453
|
+
}
|
|
454
|
+
const workersByName = new Map;
|
|
455
|
+
const primaryServiceBindings = {};
|
|
456
|
+
for (const [bindingName, binding] of Object.entries(services)) {
|
|
457
|
+
const workerBinding = binding;
|
|
458
|
+
const ref = workerBinding.__ref;
|
|
459
|
+
if (ref) {
|
|
460
|
+
if ("__import" in ref && typeof ref.__import === "function") {
|
|
461
|
+
await ref.resolve();
|
|
462
|
+
}
|
|
463
|
+
const workerName = ref.name;
|
|
464
|
+
const entrypoint = workerBinding.entrypoint;
|
|
465
|
+
if (!workersByName.has(workerName) && !seenWorkers.has(workerName)) {
|
|
466
|
+
const refConfig = normalizeReferencedConfig(ref.config);
|
|
467
|
+
const worker = await resolveRefWorker(ref, entrypoint, configDir, refConfig);
|
|
468
|
+
if (worker) {
|
|
469
|
+
const refConfigDir = resolveReferencedConfigDir(ref, configDir);
|
|
470
|
+
if (ref.config && refConfigDir) {
|
|
471
|
+
const nested = await resolveServiceBindings(refConfig, refConfigDir, new Set([...seenWorkers, workerName]));
|
|
472
|
+
worker.serviceBindings = {
|
|
473
|
+
...worker.serviceBindings ?? {},
|
|
474
|
+
...nested.primaryServiceBindings
|
|
475
|
+
};
|
|
476
|
+
for (const nestedWorker of nested.workers) {
|
|
477
|
+
if (!workersByName.has(nestedWorker.name)) {
|
|
478
|
+
workersByName.set(nestedWorker.name, nestedWorker);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
const localDOs = await resolveReferencedLocalDurableObjects(refConfig, refConfigDir, workerName, nested.primaryServiceBindings);
|
|
482
|
+
worker.durableObjects = {
|
|
483
|
+
...worker.durableObjects ?? {},
|
|
484
|
+
...localDOs.bindings
|
|
485
|
+
};
|
|
486
|
+
for (const doWorker of localDOs.workers) {
|
|
487
|
+
if (!workersByName.has(doWorker.name)) {
|
|
488
|
+
workersByName.set(doWorker.name, doWorker);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
workersByName.set(workerName, worker);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
primaryServiceBindings[bindingName] = {
|
|
496
|
+
name: workerName,
|
|
497
|
+
...entrypoint && { entrypoint }
|
|
498
|
+
};
|
|
499
|
+
} else {
|
|
500
|
+
primaryServiceBindings[bindingName] = {
|
|
501
|
+
name: workerBinding.service,
|
|
502
|
+
...workerBinding.entrypoint && { entrypoint: workerBinding.entrypoint }
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
return {
|
|
507
|
+
workers: [...workersByName.values()],
|
|
508
|
+
primaryServiceBindings
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
async function resolveRefWorker(ref, _entrypoint, parentConfigDir, resolvedConfig) {
|
|
512
|
+
const config = resolvedConfig ?? normalizeReferencedConfig(ref.config);
|
|
513
|
+
if (!config)
|
|
514
|
+
return null;
|
|
515
|
+
const refConfigDir = resolveReferencedConfigDir(ref, parentConfigDir);
|
|
516
|
+
if (!refConfigDir) {
|
|
517
|
+
console.warn(`[devflare] Cannot resolve worker "${ref.name}" - configPath not available`);
|
|
518
|
+
return null;
|
|
519
|
+
}
|
|
520
|
+
const entrypoints = [];
|
|
521
|
+
const workerEntrypointPath = findDefaultServiceWorkerEntrypoint(refConfigDir);
|
|
522
|
+
if (workerEntrypointPath) {
|
|
523
|
+
entrypoints.push({
|
|
524
|
+
path: workerEntrypointPath,
|
|
525
|
+
className: "Worker",
|
|
526
|
+
isWorkerTs: true
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
if (config.files?.entrypoints !== false) {
|
|
530
|
+
const discoveredEntrypoints = discoverEntrypointsSync(refConfigDir, typeof config.files?.entrypoints === "string" ? config.files.entrypoints : undefined);
|
|
531
|
+
for (const ep of discoveredEntrypoints) {
|
|
532
|
+
entrypoints.push({
|
|
533
|
+
path: ep.filePath,
|
|
534
|
+
className: ep.className,
|
|
535
|
+
isWorkerTs: false
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
if (entrypoints.length === 0) {
|
|
540
|
+
console.warn(`[devflare] Worker "${ref.name}" has no entry points`);
|
|
541
|
+
return null;
|
|
542
|
+
}
|
|
543
|
+
const script = await bundleAllEntrypoints(entrypoints, ref.name);
|
|
544
|
+
if (!script)
|
|
545
|
+
return null;
|
|
546
|
+
return {
|
|
547
|
+
name: ref.name,
|
|
548
|
+
script,
|
|
549
|
+
modules: true,
|
|
550
|
+
compatibilityDate: config.compatibilityDate ?? "2025-01-01",
|
|
551
|
+
...buildReferencedWorkerRuntimeConfig(config)
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
async function bundleAllEntrypoints(entrypoints, workerName) {
|
|
555
|
+
const cacheKey = entrypoints.map((ep) => `${ep.path}::${ep.className}`).join("|");
|
|
556
|
+
const cached = bundleCache.get(cacheKey);
|
|
557
|
+
if (cached) {
|
|
558
|
+
return cached;
|
|
559
|
+
}
|
|
560
|
+
const bun = getBunRuntime();
|
|
561
|
+
if (!bun) {
|
|
562
|
+
console.warn("[devflare] Bun runtime required for bundling worker scripts");
|
|
563
|
+
return null;
|
|
564
|
+
}
|
|
565
|
+
try {
|
|
566
|
+
const { readFileSync: readFileSync2, writeFileSync, mkdirSync, unlinkSync } = await import("fs");
|
|
567
|
+
const imports = [];
|
|
568
|
+
const exports = [];
|
|
569
|
+
let defaultExportClass = null;
|
|
570
|
+
for (let i = 0;i < entrypoints.length; i++) {
|
|
571
|
+
const ep = entrypoints[i];
|
|
572
|
+
const sourceCode = readFileSync2(ep.path, "utf-8");
|
|
573
|
+
if (ep.isWorkerTs) {
|
|
574
|
+
const result = transformWorkerEntrypoint(sourceCode, ep.path, {
|
|
575
|
+
className: ep.className,
|
|
576
|
+
injectContext: false
|
|
577
|
+
});
|
|
578
|
+
if (result) {
|
|
579
|
+
const tempDir2 = join(dirname(ep.path), ".devflare");
|
|
580
|
+
mkdirSync(tempDir2, { recursive: true });
|
|
581
|
+
const tempPath = join(tempDir2, `__${ep.className}_${i}.ts`);
|
|
582
|
+
writeFileSync(tempPath, result.code);
|
|
583
|
+
imports.push(`import { ${ep.className} } from '${tempPath.replace(/\\/g, "/")}'`);
|
|
584
|
+
exports.push(ep.className);
|
|
585
|
+
if (!defaultExportClass) {
|
|
586
|
+
defaultExportClass = ep.className;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
} else {
|
|
590
|
+
imports.push(`import { ${ep.className} } from '${ep.path.replace(/\\/g, "/")}'`);
|
|
591
|
+
exports.push(ep.className);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
const defaultExport = defaultExportClass ? `
|
|
595
|
+
export default ${defaultExportClass}` : "";
|
|
596
|
+
const entryCode = `
|
|
597
|
+
${imports.join(`
|
|
598
|
+
`)}
|
|
599
|
+
export { ${exports.join(", ")} }${defaultExport}
|
|
600
|
+
`;
|
|
601
|
+
const tempDir = join(dirname(entrypoints[0].path), ".devflare");
|
|
602
|
+
mkdirSync(tempDir, { recursive: true });
|
|
603
|
+
const entryPath = join(tempDir, `__entry_${workerName}.ts`);
|
|
604
|
+
writeFileSync(entryPath, entryCode);
|
|
605
|
+
try {
|
|
606
|
+
const result = await bun.build({
|
|
607
|
+
entrypoints: [entryPath],
|
|
608
|
+
target: "browser",
|
|
609
|
+
format: "esm",
|
|
610
|
+
minify: false,
|
|
611
|
+
external: ["cloudflare:workers", "cloudflare:*"]
|
|
612
|
+
});
|
|
613
|
+
if (!result.success) {
|
|
614
|
+
console.warn(`[devflare] Failed to bundle worker "${workerName}": ${result.logs.join(`
|
|
615
|
+
`)}`);
|
|
616
|
+
return null;
|
|
617
|
+
}
|
|
618
|
+
const bundledCode = await result.outputs[0].text();
|
|
619
|
+
bundleCache.set(cacheKey, bundledCode);
|
|
620
|
+
return bundledCode;
|
|
621
|
+
} finally {
|
|
622
|
+
try {
|
|
623
|
+
unlinkSync(entryPath);
|
|
624
|
+
} catch {}
|
|
625
|
+
}
|
|
626
|
+
} catch (error) {
|
|
627
|
+
console.warn(`[devflare] Error bundling worker "${workerName}":`, error);
|
|
628
|
+
return null;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
function hasCrossWorkerDOs(config) {
|
|
632
|
+
const dos = config.bindings?.durableObjects;
|
|
633
|
+
if (!dos)
|
|
634
|
+
return false;
|
|
635
|
+
for (const doConfig of Object.values(dos)) {
|
|
636
|
+
const normalized = normalizeDOBinding(doConfig);
|
|
637
|
+
if (normalized.__ref)
|
|
638
|
+
return true;
|
|
639
|
+
}
|
|
640
|
+
return false;
|
|
641
|
+
}
|
|
642
|
+
async function resolveDOBindings(config, configDir) {
|
|
643
|
+
const dos = config.bindings?.durableObjects;
|
|
644
|
+
if (!dos) {
|
|
645
|
+
return { workers: [], crossWorkerDOBindings: {} };
|
|
646
|
+
}
|
|
647
|
+
const workersByName = new Map;
|
|
648
|
+
const crossWorkerDOBindings = {};
|
|
649
|
+
for (const [bindingName, rawDoConfig] of Object.entries(dos)) {
|
|
650
|
+
const hasRef = typeof rawDoConfig === "object" && "__ref" in rawDoConfig;
|
|
651
|
+
if (!hasRef) {
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
const ref = rawDoConfig.__ref;
|
|
655
|
+
if ("__import" in ref && typeof ref.__import === "function") {
|
|
656
|
+
await ref.resolve();
|
|
657
|
+
}
|
|
658
|
+
const doConfig = normalizeDOBinding(rawDoConfig);
|
|
659
|
+
const workerName = ref.name;
|
|
660
|
+
if (!workersByName.has(workerName)) {
|
|
661
|
+
const worker = await resolveDORefWorker(ref, configDir);
|
|
662
|
+
if (worker) {
|
|
663
|
+
workersByName.set(workerName, worker);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
crossWorkerDOBindings[bindingName] = {
|
|
667
|
+
className: doConfig.className,
|
|
668
|
+
scriptName: workerName
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
return {
|
|
672
|
+
workers: [...workersByName.values()],
|
|
673
|
+
crossWorkerDOBindings
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
async function resolveDORefWorker(ref, parentConfigDir) {
|
|
677
|
+
const config = ref.config;
|
|
678
|
+
if (!config)
|
|
679
|
+
return null;
|
|
680
|
+
const configPath = ref.configPath;
|
|
681
|
+
if (!configPath || configPath === "<resolved>") {
|
|
682
|
+
console.warn(`[devflare] Cannot resolve DO worker "${ref.name}" - configPath not available`);
|
|
683
|
+
return null;
|
|
684
|
+
}
|
|
685
|
+
const resolvedConfigPath = resolvePackageSpecifier(configPath, parentConfigDir);
|
|
686
|
+
const refConfigDir = dirname(resolvedConfigPath);
|
|
687
|
+
const dosConfig = config.bindings?.durableObjects;
|
|
688
|
+
if (!dosConfig || Object.keys(dosConfig).length === 0) {
|
|
689
|
+
console.warn(`[devflare] Referenced worker "${ref.name}" has no Durable Objects`);
|
|
690
|
+
return null;
|
|
691
|
+
}
|
|
692
|
+
const discoveredDOs = discoverDOFilesSync(refConfigDir);
|
|
693
|
+
const doClasses = [];
|
|
694
|
+
for (const [bindingName, rawDoConfig] of Object.entries(dosConfig)) {
|
|
695
|
+
const doConfig = normalizeDOBinding(rawDoConfig);
|
|
696
|
+
const className = doConfig.className;
|
|
697
|
+
const scriptName = doConfig.scriptName;
|
|
698
|
+
if (scriptName) {
|
|
699
|
+
const scriptPath = resolve(refConfigDir, "src", scriptName);
|
|
700
|
+
if (!existsSync(scriptPath)) {
|
|
701
|
+
const altPath = resolve(refConfigDir, scriptName);
|
|
702
|
+
if (!existsSync(altPath)) {
|
|
703
|
+
console.warn(`[devflare] DO script not found: ${scriptPath} or ${altPath}`);
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
doClasses.push({ bindingName, className, scriptPath: altPath });
|
|
707
|
+
} else {
|
|
708
|
+
doClasses.push({ bindingName, className, scriptPath });
|
|
709
|
+
}
|
|
710
|
+
} else {
|
|
711
|
+
const discoveredPath = discoveredDOs.get(className);
|
|
712
|
+
if (discoveredPath) {
|
|
713
|
+
doClasses.push({ bindingName, className, scriptPath: discoveredPath });
|
|
714
|
+
} else {
|
|
715
|
+
console.warn(`[devflare] DO "${bindingName}" (class: ${className}) not found in do.*.ts files in "${ref.name}"`);
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
if (doClasses.length === 0) {
|
|
721
|
+
console.warn(`[devflare] No valid DO classes found in "${ref.name}"`);
|
|
722
|
+
return null;
|
|
723
|
+
}
|
|
724
|
+
const script = await bundleDOClasses(doClasses, ref.name);
|
|
725
|
+
if (!script)
|
|
726
|
+
return null;
|
|
727
|
+
const durableObjects = {};
|
|
728
|
+
for (const do_ of doClasses) {
|
|
729
|
+
durableObjects[do_.bindingName] = do_.className;
|
|
730
|
+
}
|
|
731
|
+
return {
|
|
732
|
+
name: ref.name,
|
|
733
|
+
script,
|
|
734
|
+
modules: true,
|
|
735
|
+
compatibilityDate: config.compatibilityDate ?? "2025-01-01",
|
|
736
|
+
durableObjects
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
async function bundleDOClasses(doClasses, workerName) {
|
|
740
|
+
const cacheKey = `do:${doClasses.map((d) => `${d.scriptPath}::${d.className}`).join("|")}`;
|
|
741
|
+
const cached = bundleCache.get(cacheKey);
|
|
742
|
+
if (cached)
|
|
743
|
+
return cached;
|
|
744
|
+
const bun = getBunRuntime();
|
|
745
|
+
if (!bun) {
|
|
746
|
+
console.warn("[devflare] Bun runtime required for bundling DO classes");
|
|
747
|
+
return null;
|
|
748
|
+
}
|
|
749
|
+
try {
|
|
750
|
+
const { writeFileSync, mkdirSync, unlinkSync } = await import("fs");
|
|
751
|
+
const imports = doClasses.map((d) => `import { ${d.className} } from '${d.scriptPath.replace(/\\/g, "/")}'`).join(`
|
|
752
|
+
`);
|
|
753
|
+
const exports = doClasses.map((d) => d.className).join(", ");
|
|
754
|
+
const entryCode = `
|
|
755
|
+
${imports}
|
|
756
|
+
|
|
757
|
+
// Re-export DO classes for Miniflare binding
|
|
758
|
+
export { ${exports} }
|
|
759
|
+
|
|
760
|
+
// Default export with fetch handler
|
|
761
|
+
export default {
|
|
762
|
+
async fetch(request, env) {
|
|
763
|
+
return new Response('DO Worker: ${workerName}')
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
`;
|
|
767
|
+
const tempDir = join(dirname(doClasses[0].scriptPath), ".devflare");
|
|
768
|
+
mkdirSync(tempDir, { recursive: true });
|
|
769
|
+
const entryPath = join(tempDir, `__do_entry_${workerName}.ts`);
|
|
770
|
+
writeFileSync(entryPath, entryCode);
|
|
771
|
+
try {
|
|
772
|
+
const result = await bun.build({
|
|
773
|
+
entrypoints: [entryPath],
|
|
774
|
+
target: "browser",
|
|
775
|
+
format: "esm",
|
|
776
|
+
minify: false,
|
|
777
|
+
external: ["cloudflare:workers", "cloudflare:*"]
|
|
778
|
+
});
|
|
779
|
+
if (!result.success) {
|
|
780
|
+
console.warn(`[devflare] Failed to bundle DO worker "${workerName}": ${result.logs.join(`
|
|
781
|
+
`)}`);
|
|
782
|
+
return null;
|
|
783
|
+
}
|
|
784
|
+
const bundledCode = await result.outputs[0].text();
|
|
785
|
+
bundleCache.set(cacheKey, bundledCode);
|
|
786
|
+
return bundledCode;
|
|
787
|
+
} finally {
|
|
788
|
+
try {
|
|
789
|
+
unlinkSync(entryPath);
|
|
790
|
+
} catch {}
|
|
791
|
+
}
|
|
792
|
+
} catch (error) {
|
|
793
|
+
console.warn(`[devflare] Error bundling DO worker "${workerName}":`, error);
|
|
794
|
+
return null;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// src/worker-entry/durable-object-discovery.ts
|
|
799
|
+
async function discoverDurableObjectFiles(cwd, pattern) {
|
|
800
|
+
const result = new Map;
|
|
801
|
+
const matchedFiles = await findFiles(pattern, { cwd });
|
|
802
|
+
const fs = await import("node:fs/promises");
|
|
803
|
+
for (const filePath of matchedFiles) {
|
|
804
|
+
try {
|
|
805
|
+
const code = await fs.readFile(filePath, "utf-8");
|
|
806
|
+
const classNames = findDurableObjectClasses(code);
|
|
807
|
+
if (classNames.length > 0) {
|
|
808
|
+
result.set(filePath, classNames);
|
|
809
|
+
}
|
|
810
|
+
} catch (error) {
|
|
811
|
+
console.warn(`[devflare] Failed to read DO file: ${filePath}`, error);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
return result;
|
|
815
|
+
}
|
|
816
|
+
async function discoverDurableObjects(projectRoot, pattern, workerName) {
|
|
817
|
+
const files = await discoverDurableObjectFiles(projectRoot, pattern);
|
|
818
|
+
return { files, workerName };
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// src/worker-entry/routes.ts
|
|
822
|
+
import { relative, resolve as resolve2 } from "pathe";
|
|
823
|
+
var DEFAULT_ROUTE_DIR = "src/routes";
|
|
824
|
+
var DEFAULT_ROUTE_FILE_PATTERNS = SUPPORTED_WORKER_EXTENSIONS.map((ext) => `**/*${ext}`);
|
|
825
|
+
function normalizeRoutePrefix(prefix) {
|
|
826
|
+
if (!prefix || prefix === "/") {
|
|
827
|
+
return "";
|
|
828
|
+
}
|
|
829
|
+
const normalized = prefix.startsWith("/") ? prefix : `/${prefix}`;
|
|
830
|
+
return normalized.replace(/\/+$/g, "");
|
|
831
|
+
}
|
|
832
|
+
function createStaticSegmentsFromPrefix(prefix) {
|
|
833
|
+
if (!prefix) {
|
|
834
|
+
return [];
|
|
835
|
+
}
|
|
836
|
+
return prefix.split("/").filter(Boolean).map((value) => ({
|
|
837
|
+
type: "static",
|
|
838
|
+
value
|
|
839
|
+
}));
|
|
840
|
+
}
|
|
841
|
+
function shouldIgnoreRouteFile(relativePath) {
|
|
842
|
+
return relativePath.split("/").some((segment) => segment.startsWith("_"));
|
|
843
|
+
}
|
|
844
|
+
function toRoutePath(segments) {
|
|
845
|
+
if (segments.length === 0) {
|
|
846
|
+
return "/";
|
|
847
|
+
}
|
|
848
|
+
return `/${segments.map((segment) => {
|
|
849
|
+
if (segment.type === "static") {
|
|
850
|
+
return segment.value;
|
|
851
|
+
}
|
|
852
|
+
if (segment.type === "param") {
|
|
853
|
+
return `[${segment.name}]`;
|
|
854
|
+
}
|
|
855
|
+
if (segment.type === "rest") {
|
|
856
|
+
return `[...${segment.name}]`;
|
|
857
|
+
}
|
|
858
|
+
return `[[...${segment.name}]]`;
|
|
859
|
+
}).join("/")}`;
|
|
860
|
+
}
|
|
861
|
+
function getRouteSignature(segments) {
|
|
862
|
+
if (segments.length === 0) {
|
|
863
|
+
return "/";
|
|
864
|
+
}
|
|
865
|
+
return segments.map((segment) => {
|
|
866
|
+
if (segment.type === "static") {
|
|
867
|
+
return `static:${segment.value}`;
|
|
868
|
+
}
|
|
869
|
+
if (segment.type === "param") {
|
|
870
|
+
return "param";
|
|
871
|
+
}
|
|
872
|
+
if (segment.type === "rest") {
|
|
873
|
+
return "rest";
|
|
874
|
+
}
|
|
875
|
+
return "optional-rest";
|
|
876
|
+
}).join("/");
|
|
877
|
+
}
|
|
878
|
+
function getSegmentPriority(segment) {
|
|
879
|
+
if (segment.type === "static") {
|
|
880
|
+
return 4;
|
|
881
|
+
}
|
|
882
|
+
if (segment.type === "param") {
|
|
883
|
+
return 3;
|
|
884
|
+
}
|
|
885
|
+
if (segment.type === "rest") {
|
|
886
|
+
return 1;
|
|
887
|
+
}
|
|
888
|
+
return 0;
|
|
889
|
+
}
|
|
890
|
+
function compareRoutes(a, b) {
|
|
891
|
+
const maxLength = Math.max(a.segments.length, b.segments.length);
|
|
892
|
+
for (let index = 0;index < maxLength; index += 1) {
|
|
893
|
+
const left = a.segments[index];
|
|
894
|
+
const right = b.segments[index];
|
|
895
|
+
if (!left && !right) {
|
|
896
|
+
break;
|
|
897
|
+
}
|
|
898
|
+
if (!left) {
|
|
899
|
+
return 1;
|
|
900
|
+
}
|
|
901
|
+
if (!right) {
|
|
902
|
+
return -1;
|
|
903
|
+
}
|
|
904
|
+
const priorityDifference = getSegmentPriority(right) - getSegmentPriority(left);
|
|
905
|
+
if (priorityDifference !== 0) {
|
|
906
|
+
return priorityDifference;
|
|
907
|
+
}
|
|
908
|
+
if (left.type === "static" && right.type === "static") {
|
|
909
|
+
const lexicalDifference = left.value.localeCompare(right.value);
|
|
910
|
+
if (lexicalDifference !== 0) {
|
|
911
|
+
return lexicalDifference;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
return a.filePath.localeCompare(b.filePath);
|
|
916
|
+
}
|
|
917
|
+
function parseRouteSegments(relativePath, prefixSegments) {
|
|
918
|
+
const withoutExtension = relativePath.replace(/\.[^.]+$/u, "");
|
|
919
|
+
const rawSegments = withoutExtension.split("/").filter(Boolean);
|
|
920
|
+
const routeSegments = [...prefixSegments];
|
|
921
|
+
for (let index = 0;index < rawSegments.length; index += 1) {
|
|
922
|
+
const segment = rawSegments[index];
|
|
923
|
+
const isLastSegment = index === rawSegments.length - 1;
|
|
924
|
+
if (segment === "index" && isLastSegment) {
|
|
925
|
+
continue;
|
|
926
|
+
}
|
|
927
|
+
const optionalRestMatch = segment.match(/^\[\[\.\.\.(.+)\]\]$/u);
|
|
928
|
+
if (optionalRestMatch) {
|
|
929
|
+
if (!isLastSegment) {
|
|
930
|
+
throw new Error(`Optional rest segment must be the final segment: ${relativePath}`);
|
|
931
|
+
}
|
|
932
|
+
routeSegments.push({
|
|
933
|
+
type: "optional-rest",
|
|
934
|
+
name: optionalRestMatch[1]
|
|
935
|
+
});
|
|
936
|
+
continue;
|
|
937
|
+
}
|
|
938
|
+
const restMatch = segment.match(/^\[\.\.\.(.+)\]$/u);
|
|
939
|
+
if (restMatch) {
|
|
940
|
+
if (!isLastSegment) {
|
|
941
|
+
throw new Error(`Rest segment must be the final segment: ${relativePath}`);
|
|
942
|
+
}
|
|
943
|
+
routeSegments.push({
|
|
944
|
+
type: "rest",
|
|
945
|
+
name: restMatch[1]
|
|
946
|
+
});
|
|
947
|
+
continue;
|
|
948
|
+
}
|
|
949
|
+
const dynamicMatch = segment.match(/^\[(.+)\]$/u);
|
|
950
|
+
if (dynamicMatch) {
|
|
951
|
+
routeSegments.push({
|
|
952
|
+
type: "param",
|
|
953
|
+
name: dynamicMatch[1]
|
|
954
|
+
});
|
|
955
|
+
continue;
|
|
956
|
+
}
|
|
957
|
+
routeSegments.push({
|
|
958
|
+
type: "static",
|
|
959
|
+
value: segment
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
return routeSegments;
|
|
963
|
+
}
|
|
964
|
+
async function directoryExists(dirPath) {
|
|
965
|
+
const fs = await import("node:fs/promises");
|
|
966
|
+
try {
|
|
967
|
+
const stat = await fs.stat(dirPath);
|
|
968
|
+
return stat.isDirectory();
|
|
969
|
+
} catch {
|
|
970
|
+
return false;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
function getRouteDirectoryCandidate(cwd, config) {
|
|
974
|
+
const routesConfig = config.files?.routes;
|
|
975
|
+
if (routesConfig === false) {
|
|
976
|
+
return null;
|
|
977
|
+
}
|
|
978
|
+
const dir = routesConfig?.dir ?? DEFAULT_ROUTE_DIR;
|
|
979
|
+
return {
|
|
980
|
+
dir,
|
|
981
|
+
absoluteDir: resolve2(cwd, dir),
|
|
982
|
+
prefix: normalizeRoutePrefix(routesConfig?.prefix)
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
async function discoverRoutes(cwd, config) {
|
|
986
|
+
const routeDirectory = getRouteDirectoryCandidate(cwd, config);
|
|
987
|
+
if (!routeDirectory) {
|
|
988
|
+
return null;
|
|
989
|
+
}
|
|
990
|
+
if (!await directoryExists(routeDirectory.absoluteDir)) {
|
|
991
|
+
return null;
|
|
992
|
+
}
|
|
993
|
+
const prefixSegments = createStaticSegmentsFromPrefix(routeDirectory.prefix);
|
|
994
|
+
const files = await findFiles(DEFAULT_ROUTE_FILE_PATTERNS, {
|
|
995
|
+
cwd: routeDirectory.absoluteDir,
|
|
996
|
+
absolute: true
|
|
997
|
+
});
|
|
998
|
+
const discoveredRoutes = [];
|
|
999
|
+
const routeSignatures = new Map;
|
|
1000
|
+
for (const absolutePath of files) {
|
|
1001
|
+
const relativeToRouteDir = relative(routeDirectory.absoluteDir, absolutePath).replace(/\\/g, "/");
|
|
1002
|
+
if (shouldIgnoreRouteFile(relativeToRouteDir)) {
|
|
1003
|
+
continue;
|
|
1004
|
+
}
|
|
1005
|
+
const segments = parseRouteSegments(relativeToRouteDir, prefixSegments);
|
|
1006
|
+
const routePath = toRoutePath(segments);
|
|
1007
|
+
const filePath = relative(cwd, absolutePath).replace(/\\/g, "/");
|
|
1008
|
+
const signature = getRouteSignature(segments);
|
|
1009
|
+
const existingFilePath = routeSignatures.get(signature);
|
|
1010
|
+
if (existingFilePath) {
|
|
1011
|
+
throw new Error(`Conflicting file routes detected for "${routePath}". Both "${existingFilePath}" and "${filePath}" resolve to the same route.`);
|
|
1012
|
+
}
|
|
1013
|
+
routeSignatures.set(signature, filePath);
|
|
1014
|
+
discoveredRoutes.push({
|
|
1015
|
+
absolutePath,
|
|
1016
|
+
filePath,
|
|
1017
|
+
routePath,
|
|
1018
|
+
segments
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
if (discoveredRoutes.length === 0) {
|
|
1022
|
+
return null;
|
|
1023
|
+
}
|
|
1024
|
+
discoveredRoutes.sort(compareRoutes);
|
|
1025
|
+
return {
|
|
1026
|
+
dir: routeDirectory.dir,
|
|
1027
|
+
absoluteDir: routeDirectory.absoluteDir,
|
|
1028
|
+
prefix: routeDirectory.prefix,
|
|
1029
|
+
routes: discoveredRoutes
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
export { discoverDurableObjectFiles, discoverDurableObjects, getRouteDirectoryCandidate, discoverRoutes, buildQueueProducers, buildQueueConsumers, buildRateLimitsConfig, buildVersionMetadataConfig, buildWorkerLoadersConfig, buildMtlsCertificatesConfig, buildDispatchNamespacesConfig, buildWorkflowsConfig, buildPipelinesConfig, buildHyperdrivesConfig, buildImagesConfig, buildMediaConfig, buildArtifactsConfig, buildAiSearchNamespacesConfig, buildAiSearchInstancesConfig, buildSecretsStoreConfig, buildSendEmailConfig, clearBundleCache, hasServiceBindings, resolveServiceBindings, hasCrossWorkerDOs, resolveDOBindings };
|