@bleedingdev/modern-js-app-tools 3.4.0-ultramodern.11 → 3.4.0-ultramodern.13
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/dist/cjs/plugins/deploy/platforms/cloudflare.js +106 -21
- package/dist/esm/plugins/deploy/platforms/cloudflare.mjs +106 -21
- package/dist/esm-node/plugins/deploy/platforms/cloudflare.mjs +106 -21
- package/dist/types/types/config/cloudflareDeploy.d.ts +14 -0
- package/dist/types/types/config/deploy.d.ts +21 -2
- package/package.json +11 -11
|
@@ -45,6 +45,8 @@ const utils_namespaceObject = require("@modern-js/utils");
|
|
|
45
45
|
const index_js_namespaceObject = require("../utils/index.js");
|
|
46
46
|
const WORKER_ENTRY = 'server/index.mjs';
|
|
47
47
|
const WORKER_MANIFEST = 'server/modern-worker-manifest.json';
|
|
48
|
+
const WRANGLER_CONFIG_FILE = 'wrangler.json';
|
|
49
|
+
const OUTPUT_PACKAGE_FILE = 'package.json';
|
|
48
50
|
const ASSETS_BINDING = 'ASSETS';
|
|
49
51
|
const ROUTE_SPEC_FILE = 'route.json';
|
|
50
52
|
const ROUTE_SPEC_OUTPUT = `server/${ROUTE_SPEC_FILE}`;
|
|
@@ -53,10 +55,28 @@ const ROUTE_MANIFEST_FILE = 'routes-manifest.json';
|
|
|
53
55
|
const PUBLIC_ASSETS_DIRECTORY = 'public';
|
|
54
56
|
const WORKER_BUNDLE_DIRECTORY = 'worker';
|
|
55
57
|
const SERVER_BUNDLE_DIRECTORY = 'bundles';
|
|
58
|
+
const SERVER_OUTPUT_DIRECTORY = 'server';
|
|
59
|
+
const DEFAULT_SERVER_ONLY_PUBLIC_ASSET_EXCLUDES = [
|
|
60
|
+
'api',
|
|
61
|
+
'shared'
|
|
62
|
+
];
|
|
56
63
|
const BFF_EFFECT_WORKER_ENTRY = `${WORKER_BUNDLE_DIRECTORY}/__modern_bff_effect.js`;
|
|
57
64
|
const EFFECT_BFF_CLOUDFLARE_IMPORT_GUIDANCE = 'Ensure the Effect BFF entry exists at api/effect/index.ts or bff.effect.entry, and import Cloudflare edge handlers from @modern-js/plugin-bff/effect-edge instead of lambda/Hono server helpers.';
|
|
58
65
|
const DEFAULT_COMPATIBILITY_DATE = '2026-06-02';
|
|
59
66
|
const COMPATIBILITY_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/u;
|
|
67
|
+
const REQUIRED_COMPATIBILITY_FLAGS = [
|
|
68
|
+
'nodejs_compat',
|
|
69
|
+
'global_fetch_strictly_public'
|
|
70
|
+
];
|
|
71
|
+
const RESERVED_ARTIFACT_DESTINATION_FILES = new Set([
|
|
72
|
+
WRANGLER_CONFIG_FILE,
|
|
73
|
+
OUTPUT_PACKAGE_FILE
|
|
74
|
+
]);
|
|
75
|
+
const RESERVED_ARTIFACT_DESTINATION_DIRECTORIES = new Set([
|
|
76
|
+
PUBLIC_ASSETS_DIRECTORY,
|
|
77
|
+
SERVER_OUTPUT_DIRECTORY,
|
|
78
|
+
WORKER_BUNDLE_DIRECTORY
|
|
79
|
+
]);
|
|
60
80
|
const DEFAULT_SECURITY_HEADERS = {
|
|
61
81
|
referrerPolicy: 'strict-origin-when-cross-origin',
|
|
62
82
|
contentTypeOptions: 'nosniff',
|
|
@@ -145,6 +165,13 @@ const getConfiguredWorkerName = (appDirectory, modernConfig)=>{
|
|
|
145
165
|
const configuredName = modernConfig.deploy?.worker?.name?.trim();
|
|
146
166
|
return configuredName || getWorkerName(appDirectory);
|
|
147
167
|
};
|
|
168
|
+
const normalizeRelativePath = (value, label, scope = 'app output')=>{
|
|
169
|
+
if ('string' != typeof value) throw new Error(`${label} must be a relative path inside the ${scope}.`);
|
|
170
|
+
const normalized = value.trim().replace(/\\/g, '/').replace(/^\.\/+/u, '').replace(/\/+$/u, '');
|
|
171
|
+
const segments = normalized.split('/');
|
|
172
|
+
if (!normalized || external_node_path_default().isAbsolute(normalized) || '.' === normalized || segments.includes('..')) throw new Error(`${label} must be a relative path inside the ${scope}.`);
|
|
173
|
+
return normalized;
|
|
174
|
+
};
|
|
148
175
|
const normalizeDirectiveValues = (value)=>{
|
|
149
176
|
const values = Array.isArray(value) ? value : [
|
|
150
177
|
value
|
|
@@ -237,6 +264,66 @@ const createCloudflareWorkerSecurityPolicy = (modernConfig)=>{
|
|
|
237
264
|
reason: security?.reason
|
|
238
265
|
};
|
|
239
266
|
};
|
|
267
|
+
const isJsonRecord = (value)=>'object' == typeof value && null !== value && !Array.isArray(value);
|
|
268
|
+
const getConfiguredWrangler = (modernConfig)=>{
|
|
269
|
+
const wrangler = modernConfig.deploy?.worker?.wrangler;
|
|
270
|
+
if (void 0 === wrangler) return {};
|
|
271
|
+
if (!isJsonRecord(wrangler)) throw new Error('deploy.worker.wrangler must be a JSON object.');
|
|
272
|
+
return wrangler;
|
|
273
|
+
};
|
|
274
|
+
const createWranglerCompatibilityFlags = (configuredFlags)=>{
|
|
275
|
+
if (void 0 === configuredFlags) return [
|
|
276
|
+
...REQUIRED_COMPATIBILITY_FLAGS
|
|
277
|
+
];
|
|
278
|
+
if (!Array.isArray(configuredFlags) || configuredFlags.some((flag)=>'string' != typeof flag)) throw new Error('deploy.worker.wrangler.compatibility_flags must be an array of strings.');
|
|
279
|
+
return [
|
|
280
|
+
...new Set([
|
|
281
|
+
...configuredFlags,
|
|
282
|
+
...REQUIRED_COMPATIBILITY_FLAGS
|
|
283
|
+
])
|
|
284
|
+
];
|
|
285
|
+
};
|
|
286
|
+
const createWranglerAssetsConfig = (configuredAssets)=>{
|
|
287
|
+
if (void 0 !== configuredAssets && !isJsonRecord(configuredAssets)) throw new Error('deploy.worker.wrangler.assets must be an object.');
|
|
288
|
+
return {
|
|
289
|
+
...isJsonRecord(configuredAssets) ? configuredAssets : {},
|
|
290
|
+
directory: `./${PUBLIC_ASSETS_DIRECTORY}`,
|
|
291
|
+
binding: ASSETS_BINDING,
|
|
292
|
+
run_worker_first: true
|
|
293
|
+
};
|
|
294
|
+
};
|
|
295
|
+
const createWranglerConfig = (appDirectory, modernConfig)=>{
|
|
296
|
+
const wrangler = getConfiguredWrangler(modernConfig);
|
|
297
|
+
return {
|
|
298
|
+
$schema: 'node_modules/wrangler/config-schema.json',
|
|
299
|
+
name: getConfiguredWorkerName(appDirectory, modernConfig),
|
|
300
|
+
compatibility_date: getCompatibilityDate(modernConfig),
|
|
301
|
+
...wrangler,
|
|
302
|
+
main: WORKER_ENTRY,
|
|
303
|
+
compatibility_flags: createWranglerCompatibilityFlags(wrangler.compatibility_flags),
|
|
304
|
+
assets: createWranglerAssetsConfig(wrangler.assets)
|
|
305
|
+
};
|
|
306
|
+
};
|
|
307
|
+
const normalizeCloudflareArtifact = (artifact, index)=>{
|
|
308
|
+
const from = normalizeRelativePath(artifact.from, `deploy.worker.artifacts[${index}].from`, 'app root');
|
|
309
|
+
const to = normalizeRelativePath(artifact.to, `deploy.worker.artifacts[${index}].to`, 'Cloudflare output');
|
|
310
|
+
const [topLevelDestination] = to.split('/');
|
|
311
|
+
const reservedDestination = RESERVED_ARTIFACT_DESTINATION_FILES.has(to) ? to : topLevelDestination;
|
|
312
|
+
if (RESERVED_ARTIFACT_DESTINATION_FILES.has(to) || RESERVED_ARTIFACT_DESTINATION_DIRECTORIES.has(topLevelDestination)) throw new Error(`deploy.worker.artifacts[${index}].to must not target generated Cloudflare output path ${JSON.stringify(reservedDestination)}.`);
|
|
313
|
+
return {
|
|
314
|
+
from,
|
|
315
|
+
to,
|
|
316
|
+
index
|
|
317
|
+
};
|
|
318
|
+
};
|
|
319
|
+
const getCloudflareArtifacts = (modernConfig)=>(modernConfig.deploy?.worker?.artifacts ?? []).map(normalizeCloudflareArtifact);
|
|
320
|
+
const copyCloudflareArtifacts = async (appDirectory, outputDirectory, artifacts)=>{
|
|
321
|
+
for (const artifact of artifacts){
|
|
322
|
+
const sourcePath = external_node_path_default().join(appDirectory, artifact.from);
|
|
323
|
+
if (!await utils_namespaceObject.fs.pathExists(sourcePath)) throw new Error(`deploy.worker.artifacts[${artifact.index}].from does not exist: ${artifact.from}`);
|
|
324
|
+
await utils_namespaceObject.fs.copy(sourcePath, external_node_path_default().join(outputDirectory, artifact.to));
|
|
325
|
+
}
|
|
326
|
+
};
|
|
240
327
|
const readRouteSpec = async (outputDirectory)=>{
|
|
241
328
|
const routeSpecPath = external_node_path_default().join(outputDirectory, ROUTE_SPEC_OUTPUT);
|
|
242
329
|
if (!await utils_namespaceObject.fs.pathExists(routeSpecPath)) return {
|
|
@@ -318,12 +405,22 @@ const createWorkerModuleLoaders = (manifest)=>{
|
|
|
318
405
|
].map(([worker, loader])=>`${JSON.stringify(worker)}: ${loader}`);
|
|
319
406
|
return `{\n${loaders.join(',\n')}\n}`;
|
|
320
407
|
};
|
|
321
|
-
const
|
|
408
|
+
const getPublicAssetExcludes = (appDirectory, modernConfig)=>[
|
|
409
|
+
...DEFAULT_SERVER_ONLY_PUBLIC_ASSET_EXCLUDES.filter((directory)=>{
|
|
410
|
+
try {
|
|
411
|
+
return utils_namespaceObject.fs.statSync(external_node_path_default().join(appDirectory, directory)).isDirectory();
|
|
412
|
+
} catch {
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
}),
|
|
416
|
+
...modernConfig.deploy?.worker?.publicAssetExcludes ?? []
|
|
417
|
+
].map((entry)=>normalizeRelativePath(entry, 'deploy.worker.publicAssetExcludes'));
|
|
418
|
+
const shouldCopyToPublicAssets = (src, distDirectory, publicAssetExcludes)=>{
|
|
322
419
|
const relativePath = external_node_path_default().relative(distDirectory, src);
|
|
323
420
|
if (!relativePath) return true;
|
|
324
421
|
const normalizedRelativePath = relativePath.replace(/\\/g, '/');
|
|
325
422
|
const [topLevelDirectory] = normalizedRelativePath.split('/');
|
|
326
|
-
return normalizedRelativePath !== ROUTE_SPEC_FILE && topLevelDirectory !== WORKER_BUNDLE_DIRECTORY && topLevelDirectory !== SERVER_BUNDLE_DIRECTORY;
|
|
423
|
+
return normalizedRelativePath !== ROUTE_SPEC_FILE && topLevelDirectory !== WORKER_BUNDLE_DIRECTORY && topLevelDirectory !== SERVER_BUNDLE_DIRECTORY && !publicAssetExcludes.some((exclude)=>normalizedRelativePath === exclude || normalizedRelativePath.startsWith(`${exclude}/`));
|
|
327
424
|
};
|
|
328
425
|
const shouldCopyToWorkerBundle = (src, workerBundleDirectory)=>{
|
|
329
426
|
const relativePath = external_node_path_default().relative(workerBundleDirectory, src);
|
|
@@ -345,15 +442,16 @@ const createCloudflarePreset = ({ appContext, modernConfig })=>{
|
|
|
345
442
|
const workerEntryPath = external_node_path_default().join(outputDirectory, WORKER_ENTRY);
|
|
346
443
|
const workerManifestPath = external_node_path_default().join(outputDirectory, WORKER_MANIFEST);
|
|
347
444
|
const routeSpecOutputPath = external_node_path_default().join(outputDirectory, ROUTE_SPEC_OUTPUT);
|
|
348
|
-
const wranglerConfigPath = external_node_path_default().join(outputDirectory,
|
|
349
|
-
const
|
|
445
|
+
const wranglerConfigPath = external_node_path_default().join(outputDirectory, WRANGLER_CONFIG_FILE);
|
|
446
|
+
const cloudflareArtifacts = getCloudflareArtifacts(modernConfig);
|
|
447
|
+
const publicAssetExcludes = getPublicAssetExcludes(appDirectory, modernConfig);
|
|
350
448
|
return {
|
|
351
449
|
async prepare () {
|
|
352
450
|
await utils_namespaceObject.fs.remove(outputDirectory);
|
|
353
451
|
},
|
|
354
452
|
async writeOutput () {
|
|
355
453
|
await utils_namespaceObject.fs.copy(distDirectory, publicDirectory, {
|
|
356
|
-
filter: (src)=>shouldCopyToPublicAssets(src, distDirectory)
|
|
454
|
+
filter: (src)=>shouldCopyToPublicAssets(src, distDirectory, publicAssetExcludes)
|
|
357
455
|
});
|
|
358
456
|
await utils_namespaceObject.fs.ensureDir(external_node_path_default().dirname(workerEntryPath));
|
|
359
457
|
await utils_namespaceObject.fs.ensureDir(external_node_path_default().dirname(workerManifestPath));
|
|
@@ -369,27 +467,14 @@ const createCloudflarePreset = ({ appContext, modernConfig })=>{
|
|
|
369
467
|
type: 'commonjs'
|
|
370
468
|
});
|
|
371
469
|
}
|
|
372
|
-
await
|
|
373
|
-
|
|
374
|
-
name: workerName,
|
|
375
|
-
main: WORKER_ENTRY,
|
|
376
|
-
compatibility_date: getCompatibilityDate(modernConfig),
|
|
377
|
-
compatibility_flags: [
|
|
378
|
-
'nodejs_compat',
|
|
379
|
-
'global_fetch_strictly_public'
|
|
380
|
-
],
|
|
381
|
-
assets: {
|
|
382
|
-
directory: `./${PUBLIC_ASSETS_DIRECTORY}`,
|
|
383
|
-
binding: ASSETS_BINDING,
|
|
384
|
-
run_worker_first: true
|
|
385
|
-
}
|
|
386
|
-
}, {
|
|
470
|
+
await copyCloudflareArtifacts(appDirectory, outputDirectory, cloudflareArtifacts);
|
|
471
|
+
await utils_namespaceObject.fs.writeJSON(wranglerConfigPath, createWranglerConfig(appDirectory, modernConfig), {
|
|
387
472
|
spaces: 2
|
|
388
473
|
});
|
|
389
474
|
await utils_namespaceObject.fs.writeJSON(workerManifestPath, await createWorkerManifest(outputDirectory, modernConfig), {
|
|
390
475
|
spaces: 2
|
|
391
476
|
});
|
|
392
|
-
await utils_namespaceObject.fs.writeJSON(external_node_path_default().join(outputDirectory,
|
|
477
|
+
await utils_namespaceObject.fs.writeJSON(external_node_path_default().join(outputDirectory, OUTPUT_PACKAGE_FILE), {
|
|
393
478
|
type: 'module'
|
|
394
479
|
});
|
|
395
480
|
},
|
|
@@ -3,6 +3,8 @@ import { fs } from "@modern-js/utils";
|
|
|
3
3
|
import { readTemplate } from "../utils/index.mjs";
|
|
4
4
|
const WORKER_ENTRY = 'server/index.mjs';
|
|
5
5
|
const WORKER_MANIFEST = 'server/modern-worker-manifest.json';
|
|
6
|
+
const WRANGLER_CONFIG_FILE = 'wrangler.json';
|
|
7
|
+
const OUTPUT_PACKAGE_FILE = 'package.json';
|
|
6
8
|
const ASSETS_BINDING = 'ASSETS';
|
|
7
9
|
const ROUTE_SPEC_FILE = 'route.json';
|
|
8
10
|
const ROUTE_SPEC_OUTPUT = `server/${ROUTE_SPEC_FILE}`;
|
|
@@ -11,10 +13,28 @@ const ROUTE_MANIFEST_FILE = 'routes-manifest.json';
|
|
|
11
13
|
const PUBLIC_ASSETS_DIRECTORY = 'public';
|
|
12
14
|
const WORKER_BUNDLE_DIRECTORY = 'worker';
|
|
13
15
|
const SERVER_BUNDLE_DIRECTORY = 'bundles';
|
|
16
|
+
const SERVER_OUTPUT_DIRECTORY = 'server';
|
|
17
|
+
const DEFAULT_SERVER_ONLY_PUBLIC_ASSET_EXCLUDES = [
|
|
18
|
+
'api',
|
|
19
|
+
'shared'
|
|
20
|
+
];
|
|
14
21
|
const BFF_EFFECT_WORKER_ENTRY = `${WORKER_BUNDLE_DIRECTORY}/__modern_bff_effect.js`;
|
|
15
22
|
const EFFECT_BFF_CLOUDFLARE_IMPORT_GUIDANCE = 'Ensure the Effect BFF entry exists at api/effect/index.ts or bff.effect.entry, and import Cloudflare edge handlers from @modern-js/plugin-bff/effect-edge instead of lambda/Hono server helpers.';
|
|
16
23
|
const DEFAULT_COMPATIBILITY_DATE = '2026-06-02';
|
|
17
24
|
const COMPATIBILITY_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/u;
|
|
25
|
+
const REQUIRED_COMPATIBILITY_FLAGS = [
|
|
26
|
+
'nodejs_compat',
|
|
27
|
+
'global_fetch_strictly_public'
|
|
28
|
+
];
|
|
29
|
+
const RESERVED_ARTIFACT_DESTINATION_FILES = new Set([
|
|
30
|
+
WRANGLER_CONFIG_FILE,
|
|
31
|
+
OUTPUT_PACKAGE_FILE
|
|
32
|
+
]);
|
|
33
|
+
const RESERVED_ARTIFACT_DESTINATION_DIRECTORIES = new Set([
|
|
34
|
+
PUBLIC_ASSETS_DIRECTORY,
|
|
35
|
+
SERVER_OUTPUT_DIRECTORY,
|
|
36
|
+
WORKER_BUNDLE_DIRECTORY
|
|
37
|
+
]);
|
|
18
38
|
const DEFAULT_SECURITY_HEADERS = {
|
|
19
39
|
referrerPolicy: 'strict-origin-when-cross-origin',
|
|
20
40
|
contentTypeOptions: 'nosniff',
|
|
@@ -103,6 +123,13 @@ const getConfiguredWorkerName = (appDirectory, modernConfig)=>{
|
|
|
103
123
|
const configuredName = modernConfig.deploy?.worker?.name?.trim();
|
|
104
124
|
return configuredName || getWorkerName(appDirectory);
|
|
105
125
|
};
|
|
126
|
+
const normalizeRelativePath = (value, label, scope = 'app output')=>{
|
|
127
|
+
if ('string' != typeof value) throw new Error(`${label} must be a relative path inside the ${scope}.`);
|
|
128
|
+
const normalized = value.trim().replace(/\\/g, '/').replace(/^\.\/+/u, '').replace(/\/+$/u, '');
|
|
129
|
+
const segments = normalized.split('/');
|
|
130
|
+
if (!normalized || node_path.isAbsolute(normalized) || '.' === normalized || segments.includes('..')) throw new Error(`${label} must be a relative path inside the ${scope}.`);
|
|
131
|
+
return normalized;
|
|
132
|
+
};
|
|
106
133
|
const normalizeDirectiveValues = (value)=>{
|
|
107
134
|
const values = Array.isArray(value) ? value : [
|
|
108
135
|
value
|
|
@@ -195,6 +222,66 @@ const createCloudflareWorkerSecurityPolicy = (modernConfig)=>{
|
|
|
195
222
|
reason: security?.reason
|
|
196
223
|
};
|
|
197
224
|
};
|
|
225
|
+
const isJsonRecord = (value)=>'object' == typeof value && null !== value && !Array.isArray(value);
|
|
226
|
+
const getConfiguredWrangler = (modernConfig)=>{
|
|
227
|
+
const wrangler = modernConfig.deploy?.worker?.wrangler;
|
|
228
|
+
if (void 0 === wrangler) return {};
|
|
229
|
+
if (!isJsonRecord(wrangler)) throw new Error('deploy.worker.wrangler must be a JSON object.');
|
|
230
|
+
return wrangler;
|
|
231
|
+
};
|
|
232
|
+
const createWranglerCompatibilityFlags = (configuredFlags)=>{
|
|
233
|
+
if (void 0 === configuredFlags) return [
|
|
234
|
+
...REQUIRED_COMPATIBILITY_FLAGS
|
|
235
|
+
];
|
|
236
|
+
if (!Array.isArray(configuredFlags) || configuredFlags.some((flag)=>'string' != typeof flag)) throw new Error('deploy.worker.wrangler.compatibility_flags must be an array of strings.');
|
|
237
|
+
return [
|
|
238
|
+
...new Set([
|
|
239
|
+
...configuredFlags,
|
|
240
|
+
...REQUIRED_COMPATIBILITY_FLAGS
|
|
241
|
+
])
|
|
242
|
+
];
|
|
243
|
+
};
|
|
244
|
+
const createWranglerAssetsConfig = (configuredAssets)=>{
|
|
245
|
+
if (void 0 !== configuredAssets && !isJsonRecord(configuredAssets)) throw new Error('deploy.worker.wrangler.assets must be an object.');
|
|
246
|
+
return {
|
|
247
|
+
...isJsonRecord(configuredAssets) ? configuredAssets : {},
|
|
248
|
+
directory: `./${PUBLIC_ASSETS_DIRECTORY}`,
|
|
249
|
+
binding: ASSETS_BINDING,
|
|
250
|
+
run_worker_first: true
|
|
251
|
+
};
|
|
252
|
+
};
|
|
253
|
+
const createWranglerConfig = (appDirectory, modernConfig)=>{
|
|
254
|
+
const wrangler = getConfiguredWrangler(modernConfig);
|
|
255
|
+
return {
|
|
256
|
+
$schema: 'node_modules/wrangler/config-schema.json',
|
|
257
|
+
name: getConfiguredWorkerName(appDirectory, modernConfig),
|
|
258
|
+
compatibility_date: getCompatibilityDate(modernConfig),
|
|
259
|
+
...wrangler,
|
|
260
|
+
main: WORKER_ENTRY,
|
|
261
|
+
compatibility_flags: createWranglerCompatibilityFlags(wrangler.compatibility_flags),
|
|
262
|
+
assets: createWranglerAssetsConfig(wrangler.assets)
|
|
263
|
+
};
|
|
264
|
+
};
|
|
265
|
+
const normalizeCloudflareArtifact = (artifact, index)=>{
|
|
266
|
+
const from = normalizeRelativePath(artifact.from, `deploy.worker.artifacts[${index}].from`, 'app root');
|
|
267
|
+
const to = normalizeRelativePath(artifact.to, `deploy.worker.artifacts[${index}].to`, 'Cloudflare output');
|
|
268
|
+
const [topLevelDestination] = to.split('/');
|
|
269
|
+
const reservedDestination = RESERVED_ARTIFACT_DESTINATION_FILES.has(to) ? to : topLevelDestination;
|
|
270
|
+
if (RESERVED_ARTIFACT_DESTINATION_FILES.has(to) || RESERVED_ARTIFACT_DESTINATION_DIRECTORIES.has(topLevelDestination)) throw new Error(`deploy.worker.artifacts[${index}].to must not target generated Cloudflare output path ${JSON.stringify(reservedDestination)}.`);
|
|
271
|
+
return {
|
|
272
|
+
from,
|
|
273
|
+
to,
|
|
274
|
+
index
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
const getCloudflareArtifacts = (modernConfig)=>(modernConfig.deploy?.worker?.artifacts ?? []).map(normalizeCloudflareArtifact);
|
|
278
|
+
const copyCloudflareArtifacts = async (appDirectory, outputDirectory, artifacts)=>{
|
|
279
|
+
for (const artifact of artifacts){
|
|
280
|
+
const sourcePath = node_path.join(appDirectory, artifact.from);
|
|
281
|
+
if (!await fs.pathExists(sourcePath)) throw new Error(`deploy.worker.artifacts[${artifact.index}].from does not exist: ${artifact.from}`);
|
|
282
|
+
await fs.copy(sourcePath, node_path.join(outputDirectory, artifact.to));
|
|
283
|
+
}
|
|
284
|
+
};
|
|
198
285
|
const readRouteSpec = async (outputDirectory)=>{
|
|
199
286
|
const routeSpecPath = node_path.join(outputDirectory, ROUTE_SPEC_OUTPUT);
|
|
200
287
|
if (!await fs.pathExists(routeSpecPath)) return {
|
|
@@ -276,12 +363,22 @@ const createWorkerModuleLoaders = (manifest)=>{
|
|
|
276
363
|
].map(([worker, loader])=>`${JSON.stringify(worker)}: ${loader}`);
|
|
277
364
|
return `{\n${loaders.join(',\n')}\n}`;
|
|
278
365
|
};
|
|
279
|
-
const
|
|
366
|
+
const getPublicAssetExcludes = (appDirectory, modernConfig)=>[
|
|
367
|
+
...DEFAULT_SERVER_ONLY_PUBLIC_ASSET_EXCLUDES.filter((directory)=>{
|
|
368
|
+
try {
|
|
369
|
+
return fs.statSync(node_path.join(appDirectory, directory)).isDirectory();
|
|
370
|
+
} catch {
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
}),
|
|
374
|
+
...modernConfig.deploy?.worker?.publicAssetExcludes ?? []
|
|
375
|
+
].map((entry)=>normalizeRelativePath(entry, 'deploy.worker.publicAssetExcludes'));
|
|
376
|
+
const shouldCopyToPublicAssets = (src, distDirectory, publicAssetExcludes)=>{
|
|
280
377
|
const relativePath = node_path.relative(distDirectory, src);
|
|
281
378
|
if (!relativePath) return true;
|
|
282
379
|
const normalizedRelativePath = relativePath.replace(/\\/g, '/');
|
|
283
380
|
const [topLevelDirectory] = normalizedRelativePath.split('/');
|
|
284
|
-
return normalizedRelativePath !== ROUTE_SPEC_FILE && topLevelDirectory !== WORKER_BUNDLE_DIRECTORY && topLevelDirectory !== SERVER_BUNDLE_DIRECTORY;
|
|
381
|
+
return normalizedRelativePath !== ROUTE_SPEC_FILE && topLevelDirectory !== WORKER_BUNDLE_DIRECTORY && topLevelDirectory !== SERVER_BUNDLE_DIRECTORY && !publicAssetExcludes.some((exclude)=>normalizedRelativePath === exclude || normalizedRelativePath.startsWith(`${exclude}/`));
|
|
285
382
|
};
|
|
286
383
|
const shouldCopyToWorkerBundle = (src, workerBundleDirectory)=>{
|
|
287
384
|
const relativePath = node_path.relative(workerBundleDirectory, src);
|
|
@@ -303,15 +400,16 @@ const createCloudflarePreset = ({ appContext, modernConfig })=>{
|
|
|
303
400
|
const workerEntryPath = node_path.join(outputDirectory, WORKER_ENTRY);
|
|
304
401
|
const workerManifestPath = node_path.join(outputDirectory, WORKER_MANIFEST);
|
|
305
402
|
const routeSpecOutputPath = node_path.join(outputDirectory, ROUTE_SPEC_OUTPUT);
|
|
306
|
-
const wranglerConfigPath = node_path.join(outputDirectory,
|
|
307
|
-
const
|
|
403
|
+
const wranglerConfigPath = node_path.join(outputDirectory, WRANGLER_CONFIG_FILE);
|
|
404
|
+
const cloudflareArtifacts = getCloudflareArtifacts(modernConfig);
|
|
405
|
+
const publicAssetExcludes = getPublicAssetExcludes(appDirectory, modernConfig);
|
|
308
406
|
return {
|
|
309
407
|
async prepare () {
|
|
310
408
|
await fs.remove(outputDirectory);
|
|
311
409
|
},
|
|
312
410
|
async writeOutput () {
|
|
313
411
|
await fs.copy(distDirectory, publicDirectory, {
|
|
314
|
-
filter: (src)=>shouldCopyToPublicAssets(src, distDirectory)
|
|
412
|
+
filter: (src)=>shouldCopyToPublicAssets(src, distDirectory, publicAssetExcludes)
|
|
315
413
|
});
|
|
316
414
|
await fs.ensureDir(node_path.dirname(workerEntryPath));
|
|
317
415
|
await fs.ensureDir(node_path.dirname(workerManifestPath));
|
|
@@ -327,27 +425,14 @@ const createCloudflarePreset = ({ appContext, modernConfig })=>{
|
|
|
327
425
|
type: 'commonjs'
|
|
328
426
|
});
|
|
329
427
|
}
|
|
330
|
-
await
|
|
331
|
-
|
|
332
|
-
name: workerName,
|
|
333
|
-
main: WORKER_ENTRY,
|
|
334
|
-
compatibility_date: getCompatibilityDate(modernConfig),
|
|
335
|
-
compatibility_flags: [
|
|
336
|
-
'nodejs_compat',
|
|
337
|
-
'global_fetch_strictly_public'
|
|
338
|
-
],
|
|
339
|
-
assets: {
|
|
340
|
-
directory: `./${PUBLIC_ASSETS_DIRECTORY}`,
|
|
341
|
-
binding: ASSETS_BINDING,
|
|
342
|
-
run_worker_first: true
|
|
343
|
-
}
|
|
344
|
-
}, {
|
|
428
|
+
await copyCloudflareArtifacts(appDirectory, outputDirectory, cloudflareArtifacts);
|
|
429
|
+
await fs.writeJSON(wranglerConfigPath, createWranglerConfig(appDirectory, modernConfig), {
|
|
345
430
|
spaces: 2
|
|
346
431
|
});
|
|
347
432
|
await fs.writeJSON(workerManifestPath, await createWorkerManifest(outputDirectory, modernConfig), {
|
|
348
433
|
spaces: 2
|
|
349
434
|
});
|
|
350
|
-
await fs.writeJSON(node_path.join(outputDirectory,
|
|
435
|
+
await fs.writeJSON(node_path.join(outputDirectory, OUTPUT_PACKAGE_FILE), {
|
|
351
436
|
type: 'module'
|
|
352
437
|
});
|
|
353
438
|
},
|
|
@@ -4,6 +4,8 @@ import { fs } from "@modern-js/utils";
|
|
|
4
4
|
import { readTemplate } from "../utils/index.mjs";
|
|
5
5
|
const WORKER_ENTRY = 'server/index.mjs';
|
|
6
6
|
const WORKER_MANIFEST = 'server/modern-worker-manifest.json';
|
|
7
|
+
const WRANGLER_CONFIG_FILE = 'wrangler.json';
|
|
8
|
+
const OUTPUT_PACKAGE_FILE = 'package.json';
|
|
7
9
|
const ASSETS_BINDING = 'ASSETS';
|
|
8
10
|
const ROUTE_SPEC_FILE = 'route.json';
|
|
9
11
|
const ROUTE_SPEC_OUTPUT = `server/${ROUTE_SPEC_FILE}`;
|
|
@@ -12,10 +14,28 @@ const ROUTE_MANIFEST_FILE = 'routes-manifest.json';
|
|
|
12
14
|
const PUBLIC_ASSETS_DIRECTORY = 'public';
|
|
13
15
|
const WORKER_BUNDLE_DIRECTORY = 'worker';
|
|
14
16
|
const SERVER_BUNDLE_DIRECTORY = 'bundles';
|
|
17
|
+
const SERVER_OUTPUT_DIRECTORY = 'server';
|
|
18
|
+
const DEFAULT_SERVER_ONLY_PUBLIC_ASSET_EXCLUDES = [
|
|
19
|
+
'api',
|
|
20
|
+
'shared'
|
|
21
|
+
];
|
|
15
22
|
const BFF_EFFECT_WORKER_ENTRY = `${WORKER_BUNDLE_DIRECTORY}/__modern_bff_effect.js`;
|
|
16
23
|
const EFFECT_BFF_CLOUDFLARE_IMPORT_GUIDANCE = 'Ensure the Effect BFF entry exists at api/effect/index.ts or bff.effect.entry, and import Cloudflare edge handlers from @modern-js/plugin-bff/effect-edge instead of lambda/Hono server helpers.';
|
|
17
24
|
const DEFAULT_COMPATIBILITY_DATE = '2026-06-02';
|
|
18
25
|
const COMPATIBILITY_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/u;
|
|
26
|
+
const REQUIRED_COMPATIBILITY_FLAGS = [
|
|
27
|
+
'nodejs_compat',
|
|
28
|
+
'global_fetch_strictly_public'
|
|
29
|
+
];
|
|
30
|
+
const RESERVED_ARTIFACT_DESTINATION_FILES = new Set([
|
|
31
|
+
WRANGLER_CONFIG_FILE,
|
|
32
|
+
OUTPUT_PACKAGE_FILE
|
|
33
|
+
]);
|
|
34
|
+
const RESERVED_ARTIFACT_DESTINATION_DIRECTORIES = new Set([
|
|
35
|
+
PUBLIC_ASSETS_DIRECTORY,
|
|
36
|
+
SERVER_OUTPUT_DIRECTORY,
|
|
37
|
+
WORKER_BUNDLE_DIRECTORY
|
|
38
|
+
]);
|
|
19
39
|
const DEFAULT_SECURITY_HEADERS = {
|
|
20
40
|
referrerPolicy: 'strict-origin-when-cross-origin',
|
|
21
41
|
contentTypeOptions: 'nosniff',
|
|
@@ -104,6 +124,13 @@ const getConfiguredWorkerName = (appDirectory, modernConfig)=>{
|
|
|
104
124
|
const configuredName = modernConfig.deploy?.worker?.name?.trim();
|
|
105
125
|
return configuredName || getWorkerName(appDirectory);
|
|
106
126
|
};
|
|
127
|
+
const normalizeRelativePath = (value, label, scope = 'app output')=>{
|
|
128
|
+
if ('string' != typeof value) throw new Error(`${label} must be a relative path inside the ${scope}.`);
|
|
129
|
+
const normalized = value.trim().replace(/\\/g, '/').replace(/^\.\/+/u, '').replace(/\/+$/u, '');
|
|
130
|
+
const segments = normalized.split('/');
|
|
131
|
+
if (!normalized || node_path.isAbsolute(normalized) || '.' === normalized || segments.includes('..')) throw new Error(`${label} must be a relative path inside the ${scope}.`);
|
|
132
|
+
return normalized;
|
|
133
|
+
};
|
|
107
134
|
const normalizeDirectiveValues = (value)=>{
|
|
108
135
|
const values = Array.isArray(value) ? value : [
|
|
109
136
|
value
|
|
@@ -196,6 +223,66 @@ const createCloudflareWorkerSecurityPolicy = (modernConfig)=>{
|
|
|
196
223
|
reason: security?.reason
|
|
197
224
|
};
|
|
198
225
|
};
|
|
226
|
+
const isJsonRecord = (value)=>'object' == typeof value && null !== value && !Array.isArray(value);
|
|
227
|
+
const getConfiguredWrangler = (modernConfig)=>{
|
|
228
|
+
const wrangler = modernConfig.deploy?.worker?.wrangler;
|
|
229
|
+
if (void 0 === wrangler) return {};
|
|
230
|
+
if (!isJsonRecord(wrangler)) throw new Error('deploy.worker.wrangler must be a JSON object.');
|
|
231
|
+
return wrangler;
|
|
232
|
+
};
|
|
233
|
+
const createWranglerCompatibilityFlags = (configuredFlags)=>{
|
|
234
|
+
if (void 0 === configuredFlags) return [
|
|
235
|
+
...REQUIRED_COMPATIBILITY_FLAGS
|
|
236
|
+
];
|
|
237
|
+
if (!Array.isArray(configuredFlags) || configuredFlags.some((flag)=>'string' != typeof flag)) throw new Error('deploy.worker.wrangler.compatibility_flags must be an array of strings.');
|
|
238
|
+
return [
|
|
239
|
+
...new Set([
|
|
240
|
+
...configuredFlags,
|
|
241
|
+
...REQUIRED_COMPATIBILITY_FLAGS
|
|
242
|
+
])
|
|
243
|
+
];
|
|
244
|
+
};
|
|
245
|
+
const createWranglerAssetsConfig = (configuredAssets)=>{
|
|
246
|
+
if (void 0 !== configuredAssets && !isJsonRecord(configuredAssets)) throw new Error('deploy.worker.wrangler.assets must be an object.');
|
|
247
|
+
return {
|
|
248
|
+
...isJsonRecord(configuredAssets) ? configuredAssets : {},
|
|
249
|
+
directory: `./${PUBLIC_ASSETS_DIRECTORY}`,
|
|
250
|
+
binding: ASSETS_BINDING,
|
|
251
|
+
run_worker_first: true
|
|
252
|
+
};
|
|
253
|
+
};
|
|
254
|
+
const createWranglerConfig = (appDirectory, modernConfig)=>{
|
|
255
|
+
const wrangler = getConfiguredWrangler(modernConfig);
|
|
256
|
+
return {
|
|
257
|
+
$schema: 'node_modules/wrangler/config-schema.json',
|
|
258
|
+
name: getConfiguredWorkerName(appDirectory, modernConfig),
|
|
259
|
+
compatibility_date: getCompatibilityDate(modernConfig),
|
|
260
|
+
...wrangler,
|
|
261
|
+
main: WORKER_ENTRY,
|
|
262
|
+
compatibility_flags: createWranglerCompatibilityFlags(wrangler.compatibility_flags),
|
|
263
|
+
assets: createWranglerAssetsConfig(wrangler.assets)
|
|
264
|
+
};
|
|
265
|
+
};
|
|
266
|
+
const normalizeCloudflareArtifact = (artifact, index)=>{
|
|
267
|
+
const from = normalizeRelativePath(artifact.from, `deploy.worker.artifacts[${index}].from`, 'app root');
|
|
268
|
+
const to = normalizeRelativePath(artifact.to, `deploy.worker.artifacts[${index}].to`, 'Cloudflare output');
|
|
269
|
+
const [topLevelDestination] = to.split('/');
|
|
270
|
+
const reservedDestination = RESERVED_ARTIFACT_DESTINATION_FILES.has(to) ? to : topLevelDestination;
|
|
271
|
+
if (RESERVED_ARTIFACT_DESTINATION_FILES.has(to) || RESERVED_ARTIFACT_DESTINATION_DIRECTORIES.has(topLevelDestination)) throw new Error(`deploy.worker.artifacts[${index}].to must not target generated Cloudflare output path ${JSON.stringify(reservedDestination)}.`);
|
|
272
|
+
return {
|
|
273
|
+
from,
|
|
274
|
+
to,
|
|
275
|
+
index
|
|
276
|
+
};
|
|
277
|
+
};
|
|
278
|
+
const getCloudflareArtifacts = (modernConfig)=>(modernConfig.deploy?.worker?.artifacts ?? []).map(normalizeCloudflareArtifact);
|
|
279
|
+
const copyCloudflareArtifacts = async (appDirectory, outputDirectory, artifacts)=>{
|
|
280
|
+
for (const artifact of artifacts){
|
|
281
|
+
const sourcePath = node_path.join(appDirectory, artifact.from);
|
|
282
|
+
if (!await fs.pathExists(sourcePath)) throw new Error(`deploy.worker.artifacts[${artifact.index}].from does not exist: ${artifact.from}`);
|
|
283
|
+
await fs.copy(sourcePath, node_path.join(outputDirectory, artifact.to));
|
|
284
|
+
}
|
|
285
|
+
};
|
|
199
286
|
const readRouteSpec = async (outputDirectory)=>{
|
|
200
287
|
const routeSpecPath = node_path.join(outputDirectory, ROUTE_SPEC_OUTPUT);
|
|
201
288
|
if (!await fs.pathExists(routeSpecPath)) return {
|
|
@@ -277,12 +364,22 @@ const createWorkerModuleLoaders = (manifest)=>{
|
|
|
277
364
|
].map(([worker, loader])=>`${JSON.stringify(worker)}: ${loader}`);
|
|
278
365
|
return `{\n${loaders.join(',\n')}\n}`;
|
|
279
366
|
};
|
|
280
|
-
const
|
|
367
|
+
const getPublicAssetExcludes = (appDirectory, modernConfig)=>[
|
|
368
|
+
...DEFAULT_SERVER_ONLY_PUBLIC_ASSET_EXCLUDES.filter((directory)=>{
|
|
369
|
+
try {
|
|
370
|
+
return fs.statSync(node_path.join(appDirectory, directory)).isDirectory();
|
|
371
|
+
} catch {
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
}),
|
|
375
|
+
...modernConfig.deploy?.worker?.publicAssetExcludes ?? []
|
|
376
|
+
].map((entry)=>normalizeRelativePath(entry, 'deploy.worker.publicAssetExcludes'));
|
|
377
|
+
const shouldCopyToPublicAssets = (src, distDirectory, publicAssetExcludes)=>{
|
|
281
378
|
const relativePath = node_path.relative(distDirectory, src);
|
|
282
379
|
if (!relativePath) return true;
|
|
283
380
|
const normalizedRelativePath = relativePath.replace(/\\/g, '/');
|
|
284
381
|
const [topLevelDirectory] = normalizedRelativePath.split('/');
|
|
285
|
-
return normalizedRelativePath !== ROUTE_SPEC_FILE && topLevelDirectory !== WORKER_BUNDLE_DIRECTORY && topLevelDirectory !== SERVER_BUNDLE_DIRECTORY;
|
|
382
|
+
return normalizedRelativePath !== ROUTE_SPEC_FILE && topLevelDirectory !== WORKER_BUNDLE_DIRECTORY && topLevelDirectory !== SERVER_BUNDLE_DIRECTORY && !publicAssetExcludes.some((exclude)=>normalizedRelativePath === exclude || normalizedRelativePath.startsWith(`${exclude}/`));
|
|
286
383
|
};
|
|
287
384
|
const shouldCopyToWorkerBundle = (src, workerBundleDirectory)=>{
|
|
288
385
|
const relativePath = node_path.relative(workerBundleDirectory, src);
|
|
@@ -304,15 +401,16 @@ const createCloudflarePreset = ({ appContext, modernConfig })=>{
|
|
|
304
401
|
const workerEntryPath = node_path.join(outputDirectory, WORKER_ENTRY);
|
|
305
402
|
const workerManifestPath = node_path.join(outputDirectory, WORKER_MANIFEST);
|
|
306
403
|
const routeSpecOutputPath = node_path.join(outputDirectory, ROUTE_SPEC_OUTPUT);
|
|
307
|
-
const wranglerConfigPath = node_path.join(outputDirectory,
|
|
308
|
-
const
|
|
404
|
+
const wranglerConfigPath = node_path.join(outputDirectory, WRANGLER_CONFIG_FILE);
|
|
405
|
+
const cloudflareArtifacts = getCloudflareArtifacts(modernConfig);
|
|
406
|
+
const publicAssetExcludes = getPublicAssetExcludes(appDirectory, modernConfig);
|
|
309
407
|
return {
|
|
310
408
|
async prepare () {
|
|
311
409
|
await fs.remove(outputDirectory);
|
|
312
410
|
},
|
|
313
411
|
async writeOutput () {
|
|
314
412
|
await fs.copy(distDirectory, publicDirectory, {
|
|
315
|
-
filter: (src)=>shouldCopyToPublicAssets(src, distDirectory)
|
|
413
|
+
filter: (src)=>shouldCopyToPublicAssets(src, distDirectory, publicAssetExcludes)
|
|
316
414
|
});
|
|
317
415
|
await fs.ensureDir(node_path.dirname(workerEntryPath));
|
|
318
416
|
await fs.ensureDir(node_path.dirname(workerManifestPath));
|
|
@@ -328,27 +426,14 @@ const createCloudflarePreset = ({ appContext, modernConfig })=>{
|
|
|
328
426
|
type: 'commonjs'
|
|
329
427
|
});
|
|
330
428
|
}
|
|
331
|
-
await
|
|
332
|
-
|
|
333
|
-
name: workerName,
|
|
334
|
-
main: WORKER_ENTRY,
|
|
335
|
-
compatibility_date: getCompatibilityDate(modernConfig),
|
|
336
|
-
compatibility_flags: [
|
|
337
|
-
'nodejs_compat',
|
|
338
|
-
'global_fetch_strictly_public'
|
|
339
|
-
],
|
|
340
|
-
assets: {
|
|
341
|
-
directory: `./${PUBLIC_ASSETS_DIRECTORY}`,
|
|
342
|
-
binding: ASSETS_BINDING,
|
|
343
|
-
run_worker_first: true
|
|
344
|
-
}
|
|
345
|
-
}, {
|
|
429
|
+
await copyCloudflareArtifacts(appDirectory, outputDirectory, cloudflareArtifacts);
|
|
430
|
+
await fs.writeJSON(wranglerConfigPath, createWranglerConfig(appDirectory, modernConfig), {
|
|
346
431
|
spaces: 2
|
|
347
432
|
});
|
|
348
433
|
await fs.writeJSON(workerManifestPath, await createWorkerManifest(outputDirectory, modernConfig), {
|
|
349
434
|
spaces: 2
|
|
350
435
|
});
|
|
351
|
-
await fs.writeJSON(node_path.join(outputDirectory,
|
|
436
|
+
await fs.writeJSON(node_path.join(outputDirectory, OUTPUT_PACKAGE_FILE), {
|
|
352
437
|
type: 'module'
|
|
353
438
|
});
|
|
354
439
|
},
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
export type DeployTarget = 'node' | 'vercel' | 'netlify' | 'ghPages' | 'cloudflare';
|
|
2
2
|
export type CloudflareWorkerSecurityCspMode = 'enforce' | 'report-only' | 'off';
|
|
3
|
+
export type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
4
|
+
[key: string]: JsonValue;
|
|
5
|
+
};
|
|
6
|
+
export interface CloudflareWorkerArtifactConfig {
|
|
7
|
+
/**
|
|
8
|
+
* Source file or directory, relative to the app root, to copy into
|
|
9
|
+
* `.output` after framework output has been staged.
|
|
10
|
+
*/
|
|
11
|
+
from: string;
|
|
12
|
+
/**
|
|
13
|
+
* Destination path relative to `.output`.
|
|
14
|
+
*/
|
|
15
|
+
to: string;
|
|
16
|
+
}
|
|
3
17
|
export interface CloudflareWorkerSecurityCspConfig {
|
|
4
18
|
mode?: CloudflareWorkerSecurityCspMode;
|
|
5
19
|
directives?: Record<string, string[] | string | false>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { CloudflareWorkerSecurityConfig, DeployTarget } from './cloudflareDeploy';
|
|
2
|
-
export type { CloudflareWorkerSecurityConfig, CloudflareWorkerSecurityCorsConfig, CloudflareWorkerSecurityCspConfig, CloudflareWorkerSecurityCspMode, CloudflareWorkerSecurityNoindexConfig, DeployTarget, } from './cloudflareDeploy';
|
|
1
|
+
import type { CloudflareWorkerArtifactConfig, CloudflareWorkerSecurityConfig, DeployTarget, JsonValue } from './cloudflareDeploy';
|
|
2
|
+
export type { CloudflareWorkerArtifactConfig, CloudflareWorkerSecurityConfig, CloudflareWorkerSecurityCorsConfig, CloudflareWorkerSecurityCspConfig, CloudflareWorkerSecurityCspMode, CloudflareWorkerSecurityNoindexConfig, DeployTarget, JsonValue, } from './cloudflareDeploy';
|
|
3
3
|
export interface MicroFrontend {
|
|
4
4
|
/**
|
|
5
5
|
* Specifies whether to enable the HTML entry.
|
|
@@ -36,5 +36,24 @@ export interface DeployUserConfig {
|
|
|
36
36
|
compatibilityDate?: string;
|
|
37
37
|
ssr?: boolean;
|
|
38
38
|
security?: CloudflareWorkerSecurityConfig;
|
|
39
|
+
/**
|
|
40
|
+
* Raw Wrangler-compatible config merged into `.output/wrangler.json`.
|
|
41
|
+
* Framework-owned worker invariants still win for `main`, the assets
|
|
42
|
+
* binding/directory/run mode, and required compatibility flags.
|
|
43
|
+
*/
|
|
44
|
+
wrangler?: Record<string, JsonValue>;
|
|
45
|
+
/**
|
|
46
|
+
* Additional app-root files or directories to stage under `.output`.
|
|
47
|
+
* Use this for provider resources such as migrations or generated config.
|
|
48
|
+
*/
|
|
49
|
+
artifacts?: CloudflareWorkerArtifactConfig[];
|
|
50
|
+
/**
|
|
51
|
+
* Dist output paths that must not be copied into Cloudflare public assets.
|
|
52
|
+
* Entries are slash-normalized path prefixes relative to the app dist root.
|
|
53
|
+
* Top-level `api` and `shared` dist directories are excluded when matching
|
|
54
|
+
* source directories exist in the app root because they conventionally
|
|
55
|
+
* contain server-only implementation code.
|
|
56
|
+
*/
|
|
57
|
+
publicAssetExcludes?: string[];
|
|
39
58
|
};
|
|
40
59
|
}
|
package/package.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"modern",
|
|
18
18
|
"modern.js"
|
|
19
19
|
],
|
|
20
|
-
"version": "3.4.0-ultramodern.
|
|
20
|
+
"version": "3.4.0-ultramodern.13",
|
|
21
21
|
"types": "./dist/types/index.d.ts",
|
|
22
22
|
"main": "./dist/cjs/index.js",
|
|
23
23
|
"exports": {
|
|
@@ -90,16 +90,16 @@
|
|
|
90
90
|
"ndepe": "^0.1.13",
|
|
91
91
|
"pkg-types": "^2.3.1",
|
|
92
92
|
"std-env": "4.1.0",
|
|
93
|
-
"@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.4.0-ultramodern.
|
|
94
|
-
"@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.4.0-ultramodern.
|
|
95
|
-
"@modern-js/plugin-data-loader": "npm:@bleedingdev/modern-js-plugin-data-loader@3.4.0-ultramodern.
|
|
96
|
-
"@modern-js/
|
|
97
|
-
"@modern-js/
|
|
98
|
-
"@modern-js/server
|
|
99
|
-
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.4.0-ultramodern.
|
|
100
|
-
"@modern-js/server-
|
|
101
|
-
"@modern-js/types": "npm:@bleedingdev/modern-js-types@3.4.0-ultramodern.
|
|
102
|
-
"@modern-js/
|
|
93
|
+
"@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.4.0-ultramodern.13",
|
|
94
|
+
"@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.4.0-ultramodern.13",
|
|
95
|
+
"@modern-js/plugin-data-loader": "npm:@bleedingdev/modern-js-plugin-data-loader@3.4.0-ultramodern.13",
|
|
96
|
+
"@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.4.0-ultramodern.13",
|
|
97
|
+
"@modern-js/prod-server": "npm:@bleedingdev/modern-js-prod-server@3.4.0-ultramodern.13",
|
|
98
|
+
"@modern-js/server": "npm:@bleedingdev/modern-js-server@3.4.0-ultramodern.13",
|
|
99
|
+
"@modern-js/server-utils": "npm:@bleedingdev/modern-js-server-utils@3.4.0-ultramodern.13",
|
|
100
|
+
"@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.4.0-ultramodern.13",
|
|
101
|
+
"@modern-js/types": "npm:@bleedingdev/modern-js-types@3.4.0-ultramodern.13",
|
|
102
|
+
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.4.0-ultramodern.13"
|
|
103
103
|
},
|
|
104
104
|
"devDependencies": {
|
|
105
105
|
"@rslib/core": "0.23.0",
|