@bleedingdev/modern-js-app-tools-extensions 3.9.0-ultramodern.22 → 3.9.0-ultramodern.23

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.
@@ -128,6 +128,129 @@ const createWorkerServiceBindings = (modernConfig, configuredWranglerServices)=>
128
128
  if (!Array.isArray(services)) throw new Error('deploy.worker.services must be an array.');
129
129
  return services.map(normalizeServiceBinding);
130
130
  };
131
+ const normalizeVpcService = (vpcService, index)=>{
132
+ const binding = assertNonEmptyString(vpcService.binding, `deploy.worker.vpcServices[${index}].binding`);
133
+ const serviceId = assertNonEmptyString(vpcService.serviceId, `deploy.worker.vpcServices[${index}].serviceId`);
134
+ const prefix = void 0 === vpcService.prefix ? void 0 : assertNonEmptyString(vpcService.prefix, `deploy.worker.vpcServices[${index}].prefix`);
135
+ return {
136
+ binding,
137
+ serviceId,
138
+ ...void 0 === prefix ? {} : {
139
+ prefix
140
+ }
141
+ };
142
+ };
143
+ const decodeRoutePrefix = (prefix)=>{
144
+ let decoded = prefix;
145
+ for(let round = 0; round < 4; round += 1){
146
+ let next;
147
+ try {
148
+ next = decodeURIComponent(decoded);
149
+ } catch {
150
+ break;
151
+ }
152
+ if (next === decoded) break;
153
+ decoded = next;
154
+ }
155
+ return decoded;
156
+ };
157
+ const normalizeRoutePrefix = (prefix)=>{
158
+ const decoded = decodeRoutePrefix(prefix);
159
+ return '/' === decoded ? decoded : decoded.replace(/\/+$/u, '');
160
+ };
161
+ const isCanonicalRoutePrefix = (prefix)=>prefix.startsWith('/') && !prefix.includes('\\') && decodeRoutePrefix(prefix) === prefix && new URL(prefix, 'https://route.invalid').pathname === prefix;
162
+ const collectWorkerBindingNames = (config)=>{
163
+ const names = new Set();
164
+ const visit = (value, inArray)=>{
165
+ if (Array.isArray(value)) {
166
+ for (const item of value)visit(item, true);
167
+ return;
168
+ }
169
+ if (!isJsonRecord(value)) return;
170
+ for (const field of inArray ? [
171
+ 'binding',
172
+ 'name'
173
+ ] : [
174
+ 'binding'
175
+ ]){
176
+ const name = value[field];
177
+ if ('string' == typeof name) names.add(name.trim());
178
+ }
179
+ for (const child of Object.values(value))visit(child, false);
180
+ };
181
+ for (const [key, value] of Object.entries(config))if ('env' !== key && 'services' !== key && 'vpc_services' !== key) {
182
+ if ('vars' === key && isJsonRecord(value)) {
183
+ for (const name of Object.keys(value))names.add(name);
184
+ continue;
185
+ }
186
+ visit(value, false);
187
+ }
188
+ return names;
189
+ };
190
+ const prefixesOverlap = (left, right)=>{
191
+ const [first, second] = [
192
+ normalizeRoutePrefix(left),
193
+ normalizeRoutePrefix(right)
194
+ ];
195
+ return '/' === first || '/' === second || first === second || first.startsWith(`${second}/`) || second.startsWith(`${first}/`);
196
+ };
197
+ const getWorkerEffectBffPrefix = (modernConfig)=>{
198
+ const bffPrefix = modernConfig.bff?.prefix;
199
+ const primaryBffPrefix = Array.isArray(bffPrefix) ? bffPrefix[0] : bffPrefix;
200
+ const isEffectApi = Boolean(modernConfig.bff) && modernConfig.bff?.runtimeFramework !== 'hono';
201
+ return isEffectApi && primaryBffPrefix ? primaryBffPrefix : void 0;
202
+ };
203
+ const effectiveServiceBindings = (serviceBindings)=>{
204
+ const bindings = Array.isArray(serviceBindings) ? serviceBindings : [];
205
+ return bindings.flatMap((service)=>isJsonRecord(service) && 'string' == typeof service.binding ? [
206
+ {
207
+ binding: service.binding.trim(),
208
+ prefix: 'string' == typeof service.prefix ? service.prefix : void 0
209
+ }
210
+ ] : []);
211
+ };
212
+ const createWorkerVpcServiceBindings = (modernConfig, configuredWranglerVpcServices, serviceBindings, workerBindingNames)=>{
213
+ const vpcServices = modernConfig.deploy?.worker?.vpcServices;
214
+ if (void 0 === vpcServices) return;
215
+ if (void 0 !== configuredWranglerVpcServices) throw new Error('Use deploy.worker.vpcServices or deploy.worker.wrangler.vpc_services, not both.');
216
+ if (!Array.isArray(vpcServices)) throw new Error('deploy.worker.vpcServices must be an array.');
217
+ const services = effectiveServiceBindings(serviceBindings);
218
+ const serviceBindingNames = new Set(services.map(({ binding })=>binding));
219
+ const effectBffPrefix = getWorkerEffectBffPrefix(modernConfig);
220
+ const routes = [
221
+ ...void 0 === effectBffPrefix ? [] : [
222
+ {
223
+ owner: 'the Effect BFF',
224
+ prefix: effectBffPrefix
225
+ }
226
+ ],
227
+ ...services.flatMap(({ binding, prefix })=>void 0 === prefix ? [] : [
228
+ {
229
+ owner: `binding "${binding}"`,
230
+ prefix
231
+ }
232
+ ])
233
+ ];
234
+ const vpcBindingNames = new Set();
235
+ return vpcServices.map((vpcService, index)=>{
236
+ const normalized = normalizeVpcService(vpcService, index);
237
+ if (serviceBindingNames.has(normalized.binding)) throw new Error(`deploy.worker.vpcServices[${index}].binding "${normalized.binding}" is already declared by deploy.worker.services.`);
238
+ if (workerBindingNames.has(normalized.binding)) throw new Error(`deploy.worker.vpcServices[${index}].binding "${normalized.binding}" is already a binding of this Worker.`);
239
+ if (vpcBindingNames.has(normalized.binding)) throw new Error(`deploy.worker.vpcServices[${index}].binding "${normalized.binding}" is declared more than once.`);
240
+ vpcBindingNames.add(normalized.binding);
241
+ if (void 0 !== normalized.prefix) {
242
+ const { prefix } = normalized;
243
+ if (!isCanonicalRoutePrefix(prefix)) throw new Error(`deploy.worker.vpcServices[${index}].prefix "${prefix}" must be a decoded URL path that starts with "/" (no query, fragment, "." or ".." segments).`);
244
+ const shadow = routes.find((route)=>prefixesOverlap(route.prefix, prefix));
245
+ if (void 0 !== shadow) throw new Error(`deploy.worker.vpcServices[${index}].prefix "${prefix}" overlaps the prefix "${shadow.prefix}" of ${shadow.owner}; the worker dispatcher would route one of them ambiguously.`);
246
+ routes.push({
247
+ owner: `binding "${normalized.binding}"`,
248
+ prefix
249
+ });
250
+ }
251
+ return normalized;
252
+ });
253
+ };
131
254
  const createWranglerServices = (serviceBindings)=>{
132
255
  if (!Array.isArray(serviceBindings)) return serviceBindings;
133
256
  if (!serviceBindings.every(isJsonRecord)) return serviceBindings;
@@ -136,8 +259,16 @@ const createWranglerServices = (serviceBindings)=>{
136
259
  return wranglerService;
137
260
  });
138
261
  };
139
- const createWorkerManifestServiceBindings = (serviceBindings)=>{
140
- if (!Array.isArray(serviceBindings)) return;
262
+ const createWorkerManifestServiceBindings = (serviceBindings, vpcServiceBindings)=>{
263
+ const vpcManifestBindings = (vpcServiceBindings ?? []).flatMap(({ binding, prefix, serviceId })=>void 0 === prefix ? [] : [
264
+ {
265
+ binding,
266
+ interface: 'fetch',
267
+ prefix,
268
+ vpcServiceId: serviceId
269
+ }
270
+ ]);
271
+ if (!Array.isArray(serviceBindings)) return vpcManifestBindings.length > 0 ? vpcManifestBindings : void 0;
141
272
  const bindings = serviceBindings;
142
273
  const manifestBindings = bindings.filter((service)=>isJsonRecord(service) && 'string' == typeof service.binding && 'string' == typeof service.service && ('string' == typeof service.prefix || Array.isArray(service.fragments) && service.fragments.length > 0)).map((service)=>({
143
274
  binding: service.binding,
@@ -150,7 +281,11 @@ const createWorkerManifestServiceBindings = (serviceBindings)=>{
150
281
  fragments: service.fragments
151
282
  }
152
283
  }));
153
- return manifestBindings.length > 0 ? manifestBindings : void 0;
284
+ const allBindings = [
285
+ ...manifestBindings,
286
+ ...vpcManifestBindings
287
+ ];
288
+ return allBindings.length > 0 ? allBindings : void 0;
154
289
  };
155
290
  const createWranglerConfig = (appDirectory, modernConfig)=>{
156
291
  const wrangler = getConfiguredWrangler(modernConfig);
@@ -158,7 +293,7 @@ const createWranglerConfig = (appDirectory, modernConfig)=>{
158
293
  const d1Databases = createWranglerD1Databases(modernConfig, wrangler.d1_databases);
159
294
  const serviceBindings = createWorkerServiceBindings(modernConfig, wrangler.services);
160
295
  const wranglerServices = createWranglerServices(serviceBindings);
161
- return {
296
+ const workerConfig = {
162
297
  $schema: 'node_modules/wrangler/config-schema.json',
163
298
  name: getConfiguredWorkerName(appDirectory, modernConfig),
164
299
  ...wrangler,
@@ -176,5 +311,33 @@ const createWranglerConfig = (appDirectory, modernConfig)=>{
176
311
  services: wranglerServices
177
312
  }
178
313
  };
314
+ const vpcServiceBindings = createWorkerVpcServiceBindings(modernConfig, wrangler.vpc_services, serviceBindings, collectWorkerBindingNames(workerConfig));
315
+ if (void 0 === vpcServiceBindings) return workerConfig;
316
+ const vpcServicesConfig = vpcServiceBindings.map(({ binding, serviceId })=>({
317
+ binding,
318
+ service_id: serviceId
319
+ }));
320
+ return {
321
+ ...workerConfig,
322
+ ...void 0 === environments ? {} : {
323
+ env: Object.fromEntries(Object.entries(environments).map(([name, environment])=>{
324
+ if (void 0 !== environment.vpc_services) throw new Error(`Use deploy.worker.vpcServices or deploy.worker.wrangler.env.${name}.vpc_services, not both.`);
325
+ const environmentBindingNames = new Set([
326
+ ...collectWorkerBindingNames(environment),
327
+ ...effectiveServiceBindings(environment.services).map(({ binding })=>binding)
328
+ ]);
329
+ const collision = vpcServiceBindings.find(({ binding })=>environmentBindingNames.has(binding));
330
+ if (void 0 !== collision) throw new Error(`deploy.worker.vpcServices binding "${collision.binding}" is already a binding of deploy.worker.wrangler.env.${name}.`);
331
+ return [
332
+ name,
333
+ {
334
+ ...environment,
335
+ vpc_services: vpcServicesConfig
336
+ }
337
+ ];
338
+ }))
339
+ },
340
+ vpc_services: vpcServicesConfig
341
+ };
179
342
  };
180
- export { createWorkerManifestServiceBindings, createWorkerServiceBindings, createWranglerConfig };
343
+ export { createWorkerManifestServiceBindings, createWorkerServiceBindings, createWorkerVpcServiceBindings, createWranglerConfig, getWorkerEffectBffPrefix };
@@ -80,7 +80,8 @@ const CLOUDFLARE_WORKER_NODE_BUILTINS = [
80
80
  'zlib'
81
81
  ];
82
82
  const CLOUDFLARE_WORKER_PLATFORM_MODULES = [
83
- 'cloudflare:sockets'
83
+ 'cloudflare:sockets',
84
+ 'cloudflare:workers'
84
85
  ];
85
86
  const CLOUDFLARE_REQUIRED_COMPATIBILITY_FLAGS = [
86
87
  'nodejs_compat',
@@ -130,7 +130,12 @@ export declare const createWorkerManifest: (outputDirectory: string, modernConfi
130
130
  };
131
131
  } | undefined;
132
132
  } | undefined;
133
- serviceBindings?: {
133
+ serviceBindings?: ({
134
+ binding: string;
135
+ interface: string;
136
+ prefix: string;
137
+ vpcServiceId: string;
138
+ } | {
134
139
  binding: string;
135
140
  service: string;
136
141
  interface: string;
@@ -141,7 +146,7 @@ export declare const createWorkerManifest: (outputDirectory: string, modernConfi
141
146
  boundaryId: string;
142
147
  path: string;
143
148
  }[] | undefined;
144
- }[] | undefined;
149
+ })[] | undefined;
145
150
  }>;
146
151
  export declare const createWorkerModuleLoaders: (manifest: any) => string;
147
152
  export declare const getPublicAssetExcludes: (appDirectory: string, modernConfig: CloudflareModernConfig) => string[];
@@ -11,7 +11,22 @@ export declare const createWorkerServiceBindings: (modernConfig: CloudflareModer
11
11
  path: string;
12
12
  }[] | undefined;
13
13
  }[] | JsonValue | undefined;
14
- export declare const createWorkerManifestServiceBindings: (serviceBindings: ReturnType<typeof createWorkerServiceBindings>) => {
14
+ /**
15
+ * The prefix the worker's Effect BFF dispatcher owns. It runs before any
16
+ * service binding, so it takes part in route-overlap checks.
17
+ */
18
+ export declare const getWorkerEffectBffPrefix: (modernConfig: CloudflareModernConfig) => string | undefined;
19
+ export declare const createWorkerVpcServiceBindings: (modernConfig: CloudflareModernConfig, configuredWranglerVpcServices: JsonValue | undefined, serviceBindings: ReturnType<typeof createWorkerServiceBindings>, workerBindingNames: ReadonlySet<string>) => {
20
+ binding: string;
21
+ serviceId: string;
22
+ prefix?: string | undefined;
23
+ }[] | undefined;
24
+ export declare const createWorkerManifestServiceBindings: (serviceBindings: ReturnType<typeof createWorkerServiceBindings>, vpcServiceBindings: ReturnType<typeof createWorkerVpcServiceBindings>) => ({
25
+ binding: string;
26
+ interface: string;
27
+ prefix: string;
28
+ vpcServiceId: string;
29
+ } | {
15
30
  binding: string;
16
31
  service: string;
17
32
  interface: string;
@@ -22,7 +37,7 @@ export declare const createWorkerManifestServiceBindings: (serviceBindings: Retu
22
37
  boundaryId: string;
23
38
  path: string;
24
39
  }[] | undefined;
25
- }[] | undefined;
40
+ })[] | undefined;
26
41
  export declare const createWranglerConfig: (appDirectory: string, modernConfig: CloudflareModernConfig) => {
27
42
  $schema: string;
28
43
  name: string;
@@ -34,10 +49,38 @@ export declare const createWranglerConfig: (appDirectory: string, modernConfig:
34
49
  binding: string;
35
50
  run_worker_first: boolean;
36
51
  };
37
- env?: {
52
+ env?: Record<string, Record<string, JsonValue>> | undefined;
53
+ d1_databases?: {
54
+ binding: string;
55
+ database_name: string;
56
+ database_id: string;
57
+ migrations_dir?: string | undefined;
58
+ preview_database_id?: string | undefined;
59
+ remote?: boolean | undefined;
60
+ }[] | JsonValue | undefined;
61
+ services?: string | number | boolean | JsonValue[] | {
62
+ binding: string;
63
+ service: string;
64
+ }[] | {
65
+ [key: string]: JsonValue;
66
+ } | null | undefined;
67
+ } | {
68
+ $schema: string;
69
+ name: string;
70
+ compatibility_date: string;
71
+ main: string;
72
+ compatibility_flags: JsonValue[];
73
+ assets: {
74
+ directory: string;
75
+ binding: string;
76
+ run_worker_first: boolean;
77
+ };
78
+ env?: Record<string, Record<string, JsonValue>> | {
38
79
  [k: string]: {
39
- compatibility_date?: string | undefined;
40
- compatibility_flags?: JsonValue[] | undefined;
80
+ vpc_services: {
81
+ binding: string;
82
+ service_id: string;
83
+ }[];
41
84
  };
42
85
  } | undefined;
43
86
  d1_databases?: {
@@ -54,4 +97,8 @@ export declare const createWranglerConfig: (appDirectory: string, modernConfig:
54
97
  }[] | {
55
98
  [key: string]: JsonValue;
56
99
  } | null | undefined;
100
+ vpc_services: {
101
+ binding: string;
102
+ service_id: string;
103
+ }[];
57
104
  };
@@ -13,5 +13,5 @@ export declare const CLOUDFLARE_WORKER_BUNDLE_FORMAT = "commonjs";
13
13
  export declare const CLOUDFLARE_ROUTE_DATA_HANDLER_EXPORT = "handleRouteDataRequest";
14
14
  export declare const getCloudflareWorkerRouteDataEntryName: (entryName: string) => string;
15
15
  export declare const CLOUDFLARE_WORKER_NODE_BUILTINS: readonly ['_http_agent', '_http_client', '_http_common', '_http_incoming', '_http_outgoing', '_http_server', '_tls_common', '_tls_wrap', 'assert', 'assert/strict', 'async_hooks', 'buffer', 'child_process', 'cluster', 'console', 'constants', 'crypto', 'dgram', 'diagnostics_channel', 'dns', 'dns/promises', 'domain', 'events', 'fs', 'fs/promises', 'http', 'http2', 'https', 'inspector', 'inspector/promises', 'module', 'net', 'os', 'path', 'path/posix', 'path/win32', 'perf_hooks', 'process', 'punycode', 'querystring', 'readline', 'readline/promises', 'repl', 'sqlite', 'stream', 'stream/consumers', 'stream/promises', 'stream/web', 'string_decoder', 'sys', 'test', 'timers', 'timers/promises', 'tls', 'trace_events', 'tty', 'url', 'util', 'util/types', 'v8', 'vm', 'wasi', 'worker_threads', 'zlib'];
16
- export declare const CLOUDFLARE_WORKER_PLATFORM_MODULES: readonly ['cloudflare:sockets'];
16
+ export declare const CLOUDFLARE_WORKER_PLATFORM_MODULES: readonly ['cloudflare:sockets', 'cloudflare:workers'];
17
17
  export declare const CLOUDFLARE_REQUIRED_COMPATIBILITY_FLAGS: readonly ['nodejs_compat', 'global_fetch_strictly_public'];
@@ -84,6 +84,18 @@ export interface CloudflareWorkerServiceBindingConfig {
84
84
  */
85
85
  fragments?: CloudflareWorkerServiceBindingFragmentConfig[];
86
86
  }
87
+ export interface CloudflareWorkerVpcServiceConfig {
88
+ /** Worker binding name exposed on the module worker `env` object. */
89
+ binding: string;
90
+ /** Workers VPC service id (`wrangler vpc service create`). */
91
+ serviceId: string;
92
+ /**
93
+ * Optional application path prefix that Modern.js should dispatch to this
94
+ * private origin with `env[binding].fetch(request)`, exactly like a
95
+ * prefixed Worker service binding.
96
+ */
97
+ prefix?: string;
98
+ }
87
99
  export interface CloudflareWorkerServiceBindingFragmentConfig {
88
100
  /** Stable remote id used by the shell's composition contract. */
89
101
  remote: string;
@@ -174,19 +186,64 @@ export interface CloudflareWorkerSecurityConfig {
174
186
  };
175
187
  reason?: string;
176
188
  }
189
+ export interface CloudflareWorkerDeployConfig {
190
+ name?: string;
191
+ /**
192
+ * Cloudflare Workers compatibility date for generated wrangler config.
193
+ * Use YYYY-MM-DD. Defaults to the date validated against the bundled
194
+ * Wrangler version used by UltraModern generated workspaces.
195
+ */
196
+ compatibilityDate?: string;
197
+ ssr?: boolean;
198
+ security?: CloudflareWorkerSecurityConfig;
199
+ /**
200
+ * Raw Wrangler-compatible config merged into `.output/wrangler.json`.
201
+ * Framework-owned worker invariants still win for `main`, the assets
202
+ * binding/directory/run mode, and required compatibility flags.
203
+ */
204
+ wrangler?: Record<string, JsonValue>;
205
+ /**
206
+ * Additional app-root files or directories to stage under `.output`.
207
+ * Use this for provider resources such as migrations or generated config.
208
+ */
209
+ artifacts?: CloudflareWorkerArtifactConfig[];
210
+ /**
211
+ * Additional app-root files or directories to serve as Cloudflare Worker
212
+ * Static Assets under `.output/public`. Use `to: '.'` to copy a
213
+ * source directory's contents into the public asset root.
214
+ */
215
+ publicAssets?: CloudflareWorkerPublicAssetConfig[];
216
+ /**
217
+ * First-class Cloudflare D1 bindings. Modern.js writes these to
218
+ * `wrangler.json` as `d1_databases` and stages configured migration
219
+ * directories into `.output`.
220
+ */
221
+ d1Databases?: CloudflareWorkerD1DatabaseConfig[];
222
+ /**
223
+ * First-class Cloudflare service bindings. Modern.js writes these to
224
+ * `wrangler.json` as `services`; when a binding also has `prefix`,
225
+ * the generated Worker dispatches matching requests through
226
+ * `env[binding].fetch(request)`.
227
+ */
228
+ services?: CloudflareWorkerServiceBindingConfig[];
229
+ /**
230
+ * First-class Workers VPC service bindings to private origins reached
231
+ * through a Cloudflare Tunnel. Modern.js writes these to `wrangler.json`
232
+ * as `vpc_services`; a binding with `prefix` joins the prefix dispatch of
233
+ * `services`.
234
+ */
235
+ vpcServices?: CloudflareWorkerVpcServiceConfig[];
236
+ /**
237
+ * Dist output paths that must not be copied into Cloudflare public assets.
238
+ * Entries are slash-normalized path prefixes relative to the app dist root.
239
+ * Top-level `api` and `shared` dist directories are excluded when matching
240
+ * source directories exist in the app root because they conventionally
241
+ * contain server-only implementation code.
242
+ */
243
+ publicAssetExcludes?: string[];
244
+ }
177
245
  export interface CloudflareDeployConfig {
178
- worker?: {
179
- name?: string;
180
- compatibilityDate?: string;
181
- ssr?: boolean;
182
- security?: CloudflareWorkerSecurityConfig;
183
- wrangler?: Record<string, JsonValue>;
184
- artifacts?: CloudflareWorkerArtifactConfig[];
185
- publicAssets?: CloudflareWorkerPublicAssetConfig[];
186
- d1Databases?: CloudflareWorkerD1DatabaseConfig[];
187
- services?: CloudflareWorkerServiceBindingConfig[];
188
- publicAssetExcludes?: string[];
189
- };
246
+ worker?: CloudflareWorkerDeployConfig;
190
247
  }
191
248
  export interface NodeDeployConfig {
192
249
  node?: {
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "directory": "packages/solutions/app-tools-extensions"
12
12
  },
13
13
  "license": "MIT",
14
- "version": "3.9.0-ultramodern.22",
14
+ "version": "3.9.0-ultramodern.23",
15
15
  "files": [
16
16
  "dist",
17
17
  "src"
@@ -153,16 +153,16 @@
153
153
  "@babel/parser": "^8.0.6",
154
154
  "@babel/traverse": "^8.0.6",
155
155
  "@loadable/component": "5.16.7",
156
- "@modern-js/backend-federation-contracts": "npm:@bleedingdev/modern-js-backend-federation-contracts@3.9.0-ultramodern.22",
157
- "@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.9.0-ultramodern.22",
158
- "@modern-js/prod-server": "npm:@bleedingdev/modern-js-prod-server@3.9.0-ultramodern.22",
159
- "@modern-js/runtime-renderer-extensions": "npm:@bleedingdev/modern-js-runtime-renderer-extensions@3.9.0-ultramodern.22",
160
- "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.0-ultramodern.22",
161
- "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.9.0-ultramodern.22",
162
- "@modern-js/server-runtime-extensions": "npm:@bleedingdev/modern-js-server-runtime-extensions@3.9.0-ultramodern.22",
163
- "@modern-js/surface-resolution": "npm:@bleedingdev/modern-js-surface-resolution@3.9.0-ultramodern.22",
164
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.22",
165
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.22",
156
+ "@modern-js/backend-federation-contracts": "npm:@bleedingdev/modern-js-backend-federation-contracts@3.9.0-ultramodern.23",
157
+ "@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.9.0-ultramodern.23",
158
+ "@modern-js/prod-server": "npm:@bleedingdev/modern-js-prod-server@3.9.0-ultramodern.23",
159
+ "@modern-js/runtime-renderer-extensions": "npm:@bleedingdev/modern-js-runtime-renderer-extensions@3.9.0-ultramodern.23",
160
+ "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.0-ultramodern.23",
161
+ "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.9.0-ultramodern.23",
162
+ "@modern-js/server-runtime-extensions": "npm:@bleedingdev/modern-js-server-runtime-extensions@3.9.0-ultramodern.23",
163
+ "@modern-js/surface-resolution": "npm:@bleedingdev/modern-js-surface-resolution@3.9.0-ultramodern.23",
164
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.23",
165
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.23",
166
166
  "@swc/helpers": "^0.5.23",
167
167
  "@vercel/nft": "^1.11.0",
168
168
  "compression-webpack-plugin": "^12.0.0",
@@ -179,10 +179,10 @@
179
179
  }
180
180
  },
181
181
  "devDependencies": {
182
- "@modern-js/bff-effect": "npm:@bleedingdev/modern-js-bff-effect@3.9.0-ultramodern.22",
183
- "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.0-ultramodern.22",
184
- "@modern-js/plugin-bff-extensions": "npm:@bleedingdev/modern-js-plugin-bff-extensions@3.9.0-ultramodern.22",
185
- "@modern-js/plugin-data-loader": "npm:@bleedingdev/modern-js-plugin-data-loader@3.9.0-ultramodern.22",
182
+ "@modern-js/bff-effect": "npm:@bleedingdev/modern-js-bff-effect@3.9.0-ultramodern.23",
183
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.0-ultramodern.23",
184
+ "@modern-js/plugin-bff-extensions": "npm:@bleedingdev/modern-js-plugin-bff-extensions@3.9.0-ultramodern.23",
185
+ "@modern-js/plugin-data-loader": "npm:@bleedingdev/modern-js-plugin-data-loader@3.9.0-ultramodern.23",
186
186
  "@module-federation/modern-js-v3": "2.9.2",
187
187
  "@module-federation/runtime-tools": "2.9.2",
188
188
  "@rsbuild/core": "2.2.10",
@@ -28,6 +28,8 @@ import { isRecord, normalizeRelativePath } from './utils';
28
28
  import {
29
29
  createWorkerManifestServiceBindings,
30
30
  createWorkerServiceBindings,
31
+ createWorkerVpcServiceBindings,
32
+ getWorkerEffectBffPrefix,
31
33
  } from './wrangler-config';
32
34
 
33
35
  const createMissingEffectBffWorkerError = (
@@ -157,20 +159,31 @@ export const createWorkerManifest = async (
157
159
  }),
158
160
  );
159
161
 
160
- const bffPrefix = modernConfig.bff?.prefix;
161
- const primaryBffPrefix = Array.isArray(bffPrefix) ? bffPrefix[0] : bffPrefix;
162
162
  const isEffectApi =
163
163
  Boolean(modernConfig.bff) && modernConfig.bff?.runtimeFramework !== 'hono';
164
+ const effectBffPrefix = getWorkerEffectBffPrefix(modernConfig);
164
165
  const effectApiWorkerExists = await fse.pathExists(
165
166
  path.join(outputDirectory, BFF_EFFECT_WORKER_ENTRY),
166
167
  );
168
+ const typedServiceBindings = createWorkerServiceBindings(
169
+ modernConfig,
170
+ undefined,
171
+ );
167
172
  const serviceBindings = createWorkerManifestServiceBindings(
168
- createWorkerServiceBindings(modernConfig, undefined),
173
+ typedServiceBindings,
174
+ // The wrangler config checks VPC names against every Worker binding; the
175
+ // manifest only needs the framework-owned assets binding reserved.
176
+ createWorkerVpcServiceBindings(
177
+ modernConfig,
178
+ undefined,
179
+ typedServiceBindings,
180
+ new Set([ASSETS_BINDING]),
181
+ ),
169
182
  );
170
183
  const moduleFederation =
171
184
  await createModuleFederationWorkerManifest(outputDirectory);
172
185
 
173
- if (isEffectApi && primaryBffPrefix && !effectApiWorkerExists) {
186
+ if (effectBffPrefix !== undefined && !effectApiWorkerExists) {
174
187
  throw createMissingEffectBffWorkerError(
175
188
  outputDirectory,
176
189
  BFF_EFFECT_WORKER_ENTRY,
@@ -212,11 +225,11 @@ export const createWorkerManifest = async (
212
225
  ...(deliveryUnitStamp ? { deliveryUnit: deliveryUnitStamp } : {}),
213
226
  i18n: createI18nWorkerManifest(routeSpec, appContext),
214
227
  bff:
215
- isEffectApi && primaryBffPrefix && effectApiWorkerExists
228
+ effectBffPrefix !== undefined && effectApiWorkerExists
216
229
  ? {
217
230
  dispatcherExport: BFF_EFFECT_WORKER_DISPATCHER_EXPORT,
218
231
  runtimeFramework: 'effect',
219
- prefix: primaryBffPrefix,
232
+ prefix: effectBffPrefix,
220
233
  worker: BFF_EFFECT_WORKER_ENTRY,
221
234
  effect: effectBffManifest,
222
235
  }