@bleedingdev/modern-js-app-tools-extensions 3.9.0-ultramodern.21 → 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.
package/README.md CHANGED
@@ -3,3 +3,26 @@
3
3
  Fork-owned UltraModern.js app tooling for Cloudflare Worker delivery, output verification, and release-envelope identity enforcement.
4
4
 
5
5
  The package is composed into upstream app-tools through existing deploy/plugin extension seams. It must not depend on `@modern-js/app-tools`.
6
+
7
+ ## Private origins and module-scope bindings
8
+
9
+ Private origins behind a Cloudflare Tunnel are first-class on
10
+ `deploy.worker.vpcServices` (`{ binding, serviceId, prefix? }`). Modern.js
11
+ writes them to `wrangler.json` as `vpc_services` (top level and every
12
+ `wrangler.env.<name>`, because Wrangler bindings are not inherited), and a
13
+ binding with `prefix`
14
+ is dispatched with `env[binding].fetch(request)` like a prefixed
15
+ `deploy.worker.services` entry. A VPC binding name may not reuse any other
16
+ Worker binding: any `binding` field or array-entry `name` in the Wrangler
17
+ config (`ASSETS`, services, D1, KV, Hyperdrive, queue producers, Durable
18
+ Objects, `send_email`, rate limits, …), including each `wrangler.env.<name>`'s
19
+ own bindings, or a `vars` key. A VPC prefix must be a decoded path starting with `/` that URL parsing keeps unchanged (no query, fragment,
20
+ `.` or `..` segments), and may not overlap, even as a percent-encoded alias, the
21
+ Effect BFF prefix or any other routed prefix (the worker decodes the request
22
+ path, dispatches the BFF first, matches path segments and takes the first
23
+ match),
24
+ and the typed option excludes a raw `wrangler.vpc_services`.
25
+
26
+ Code that needs a binding outside the request handler, such as a Hyperdrive
27
+ connection string or a VPC fetcher, imports `env` from `cloudflare:workers`,
28
+ which worker bundles externalize like `cloudflare:sockets`.
@@ -31,23 +31,50 @@ __webpack_require__.d(__webpack_exports__, {
31
31
  getBuildConfigEnvironment: ()=>getBuildConfigEnvironment,
32
32
  resolveEffectTsgoCompiler: ()=>resolveEffectTsgoCompiler
33
33
  });
34
- const external_node_child_process_namespaceObject = require("node:child_process");
35
34
  const external_node_crypto_namespaceObject = require("node:crypto");
36
35
  const external_node_fs_namespaceObject = require("node:fs");
37
36
  const external_node_module_namespaceObject = require("node:module");
38
37
  const external_node_os_namespaceObject = require("node:os");
39
38
  const external_node_path_namespaceObject = require("node:path");
40
39
  const EFFECT_TSGO_PACKAGE = '@effect/tsgo';
41
- const EFFECT_TSGO_BIN = 'effect-tsgo';
40
+ const NATIVE_TYPESCRIPT_PACKAGES = [
41
+ "typescript",
42
+ "@typescript/native"
43
+ ];
42
44
  const EFFECT_TSGO_RESOLUTION_ERROR = 'Unable to resolve the Effect TS-Go compiler. Install "@effect/tsgo" and a native TypeScript backend for this build config, or set EFFECT_TSGO_BIN.';
43
45
  const executableEffectTsgoCompilers = new Map();
44
- function resolveEffectTsgoCli(from) {
46
+ function readJson(fileName) {
47
+ return JSON.parse((0, external_node_fs_namespaceObject.readFileSync)(fileName, 'utf-8'));
48
+ }
49
+ function resolvePackageVersion(packageRequire, packageName) {
50
+ let packageJsonPath;
51
+ try {
52
+ packageJsonPath = packageRequire.resolve(`${packageName}/package.json`);
53
+ } catch {
54
+ return;
55
+ }
56
+ const { version } = readJson(packageJsonPath);
57
+ return 'string' == typeof version ? {
58
+ packageJsonPath,
59
+ version
60
+ } : void 0;
61
+ }
62
+ function resolveEffectTsgoTypeScriptArtifact(from) {
45
63
  const projectRequire = (0, external_node_module_namespaceObject.createRequire)(from);
46
- const packageJsonPath = projectRequire.resolve(`${EFFECT_TSGO_PACKAGE}/package.json`);
47
- const packageJson = JSON.parse((0, external_node_fs_namespaceObject.readFileSync)(packageJsonPath, 'utf-8'));
48
- const bin = 'string' == typeof packageJson.bin ? packageJson.bin : packageJson.bin?.[EFFECT_TSGO_BIN];
49
- if (!bin) throw new Error(EFFECT_TSGO_RESOLUTION_ERROR);
50
- return (0, external_node_path_namespaceObject.resolve)((0, external_node_path_namespaceObject.dirname)(packageJsonPath), bin);
64
+ const effectTsgoPackageJsonPath = projectRequire.resolve(`${EFFECT_TSGO_PACKAGE}/package.json`);
65
+ const platformTarget = `${process.platform}-${process.arch}`;
66
+ const effectTsgoPlatformPackageJsonPath = (0, external_node_module_namespaceObject.createRequire)(effectTsgoPackageJsonPath).resolve(`${EFFECT_TSGO_PACKAGE}-${platformTarget}/package.json`);
67
+ const upstream = readJson((0, external_node_path_namespaceObject.join)((0, external_node_path_namespaceObject.dirname)(effectTsgoPlatformPackageJsonPath), 'lib', 'upstream.json'));
68
+ const packagedTypeScriptVersions = upstream.components?.typescript ?? {};
69
+ for (const packageName of NATIVE_TYPESCRIPT_PACKAGES){
70
+ const typescript = resolvePackageVersion(projectRequire, packageName);
71
+ const major = typescript && /^\s*(\d+)/u.exec(typescript.version);
72
+ if (!typescript || !major || Number(major[1]) < 7) continue;
73
+ const typescriptPlatform = resolvePackageVersion((0, external_node_module_namespaceObject.createRequire)(typescript.packageJsonPath), `@typescript/typescript-${platformTarget}`);
74
+ if (!typescriptPlatform || !Object.hasOwn(packagedTypeScriptVersions, typescriptPlatform.version)) break;
75
+ return (0, external_node_path_namespaceObject.join)((0, external_node_path_namespaceObject.dirname)(effectTsgoPlatformPackageJsonPath), 'artifacts', "typescript", typescriptPlatform.version, 'win32' === process.platform ? 'tsc.exe' : 'tsc');
76
+ }
77
+ throw new Error(EFFECT_TSGO_RESOLUTION_ERROR);
51
78
  }
52
79
  function resolveExecutableEffectTsgoCompiler(compilerPath) {
53
80
  if ('win32' === process.platform) return compilerPath;
@@ -108,13 +135,7 @@ function resolveEffectTsgoCompiler(options) {
108
135
  throw new Error(EFFECT_TSGO_RESOLUTION_ERROR);
109
136
  }
110
137
  try {
111
- const compiler = (0, external_node_child_process_namespaceObject.execFileSync)(process.execPath, [
112
- resolveEffectTsgoCli(options.from),
113
- 'get-exe-path'
114
- ], {
115
- encoding: 'utf-8'
116
- }).trim();
117
- if (compiler) return resolveExecutableEffectTsgoCompiler(compiler);
138
+ return resolveExecutableEffectTsgoCompiler(resolveEffectTsgoTypeScriptArtifact(options.from));
118
139
  } catch {}
119
140
  throw new Error(EFFECT_TSGO_RESOLUTION_ERROR);
120
141
  }
@@ -126,13 +126,15 @@ const createWorkerManifest = async (outputDirectory, modernConfig, appContext, d
126
126
  } : {}
127
127
  };
128
128
  }));
129
- const bffPrefix = modernConfig.bff?.prefix;
130
- const primaryBffPrefix = Array.isArray(bffPrefix) ? bffPrefix[0] : bffPrefix;
131
129
  const isEffectApi = Boolean(modernConfig.bff) && modernConfig.bff?.runtimeFramework !== 'hono';
130
+ const effectBffPrefix = (0, external_wrangler_config_js_namespaceObject.getWorkerEffectBffPrefix)(modernConfig);
132
131
  const effectApiWorkerExists = await utils_namespaceObject.fs.pathExists(external_node_path_default().join(outputDirectory, external_constants_js_namespaceObject.BFF_EFFECT_WORKER_ENTRY));
133
- const serviceBindings = (0, external_wrangler_config_js_namespaceObject.createWorkerManifestServiceBindings)((0, external_wrangler_config_js_namespaceObject.createWorkerServiceBindings)(modernConfig, void 0));
132
+ const typedServiceBindings = (0, external_wrangler_config_js_namespaceObject.createWorkerServiceBindings)(modernConfig, void 0);
133
+ const serviceBindings = (0, external_wrangler_config_js_namespaceObject.createWorkerManifestServiceBindings)(typedServiceBindings, (0, external_wrangler_config_js_namespaceObject.createWorkerVpcServiceBindings)(modernConfig, void 0, typedServiceBindings, new Set([
134
+ external_constants_js_namespaceObject.ASSETS_BINDING
135
+ ])));
134
136
  const moduleFederation = await createModuleFederationWorkerManifest(outputDirectory);
135
- if (isEffectApi && primaryBffPrefix && !effectApiWorkerExists) throw createMissingEffectBffWorkerError(outputDirectory, external_constants_js_namespaceObject.BFF_EFFECT_WORKER_ENTRY);
137
+ if (void 0 !== effectBffPrefix && !effectApiWorkerExists) throw createMissingEffectBffWorkerError(outputDirectory, external_constants_js_namespaceObject.BFF_EFFECT_WORKER_ENTRY);
136
138
  const effectBffManifest = isEffectApi ? createEffectBffWorkerManifest(modernConfig) : void 0;
137
139
  return {
138
140
  version: 1,
@@ -169,10 +171,10 @@ const createWorkerManifest = async (outputDirectory, modernConfig, appContext, d
169
171
  deliveryUnit: deliveryUnitStamp
170
172
  } : {},
171
173
  i18n: (0, external_i18n_worker_js_namespaceObject.createI18nWorkerManifest)(routeSpec, appContext),
172
- bff: isEffectApi && primaryBffPrefix && effectApiWorkerExists ? {
174
+ bff: void 0 !== effectBffPrefix && effectApiWorkerExists ? {
173
175
  dispatcherExport: external_constants_js_namespaceObject.BFF_EFFECT_WORKER_DISPATCHER_EXPORT,
174
176
  runtimeFramework: 'effect',
175
- prefix: primaryBffPrefix,
177
+ prefix: effectBffPrefix,
176
178
  worker: external_constants_js_namespaceObject.BFF_EFFECT_WORKER_ENTRY,
177
179
  effect: effectBffManifest
178
180
  } : void 0,
@@ -39,7 +39,9 @@ __webpack_require__.r(__webpack_exports__);
39
39
  __webpack_require__.d(__webpack_exports__, {
40
40
  createWorkerManifestServiceBindings: ()=>createWorkerManifestServiceBindings,
41
41
  createWorkerServiceBindings: ()=>createWorkerServiceBindings,
42
- createWranglerConfig: ()=>createWranglerConfig
42
+ createWorkerVpcServiceBindings: ()=>createWorkerVpcServiceBindings,
43
+ createWranglerConfig: ()=>createWranglerConfig,
44
+ getWorkerEffectBffPrefix: ()=>getWorkerEffectBffPrefix
43
45
  });
44
46
  const external_node_path_namespaceObject = require("node:path");
45
47
  var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
@@ -171,6 +173,129 @@ const createWorkerServiceBindings = (modernConfig, configuredWranglerServices)=>
171
173
  if (!Array.isArray(services)) throw new Error('deploy.worker.services must be an array.');
172
174
  return services.map(normalizeServiceBinding);
173
175
  };
176
+ const normalizeVpcService = (vpcService, index)=>{
177
+ const binding = assertNonEmptyString(vpcService.binding, `deploy.worker.vpcServices[${index}].binding`);
178
+ const serviceId = assertNonEmptyString(vpcService.serviceId, `deploy.worker.vpcServices[${index}].serviceId`);
179
+ const prefix = void 0 === vpcService.prefix ? void 0 : assertNonEmptyString(vpcService.prefix, `deploy.worker.vpcServices[${index}].prefix`);
180
+ return {
181
+ binding,
182
+ serviceId,
183
+ ...void 0 === prefix ? {} : {
184
+ prefix
185
+ }
186
+ };
187
+ };
188
+ const decodeRoutePrefix = (prefix)=>{
189
+ let decoded = prefix;
190
+ for(let round = 0; round < 4; round += 1){
191
+ let next;
192
+ try {
193
+ next = decodeURIComponent(decoded);
194
+ } catch {
195
+ break;
196
+ }
197
+ if (next === decoded) break;
198
+ decoded = next;
199
+ }
200
+ return decoded;
201
+ };
202
+ const normalizeRoutePrefix = (prefix)=>{
203
+ const decoded = decodeRoutePrefix(prefix);
204
+ return '/' === decoded ? decoded : decoded.replace(/\/+$/u, '');
205
+ };
206
+ const isCanonicalRoutePrefix = (prefix)=>prefix.startsWith('/') && !prefix.includes('\\') && decodeRoutePrefix(prefix) === prefix && new URL(prefix, 'https://route.invalid').pathname === prefix;
207
+ const collectWorkerBindingNames = (config)=>{
208
+ const names = new Set();
209
+ const visit = (value, inArray)=>{
210
+ if (Array.isArray(value)) {
211
+ for (const item of value)visit(item, true);
212
+ return;
213
+ }
214
+ if (!(0, external_utils_js_namespaceObject.isJsonRecord)(value)) return;
215
+ for (const field of inArray ? [
216
+ 'binding',
217
+ 'name'
218
+ ] : [
219
+ 'binding'
220
+ ]){
221
+ const name = value[field];
222
+ if ('string' == typeof name) names.add(name.trim());
223
+ }
224
+ for (const child of Object.values(value))visit(child, false);
225
+ };
226
+ for (const [key, value] of Object.entries(config))if ('env' !== key && 'services' !== key && 'vpc_services' !== key) {
227
+ if ('vars' === key && (0, external_utils_js_namespaceObject.isJsonRecord)(value)) {
228
+ for (const name of Object.keys(value))names.add(name);
229
+ continue;
230
+ }
231
+ visit(value, false);
232
+ }
233
+ return names;
234
+ };
235
+ const prefixesOverlap = (left, right)=>{
236
+ const [first, second] = [
237
+ normalizeRoutePrefix(left),
238
+ normalizeRoutePrefix(right)
239
+ ];
240
+ return '/' === first || '/' === second || first === second || first.startsWith(`${second}/`) || second.startsWith(`${first}/`);
241
+ };
242
+ const getWorkerEffectBffPrefix = (modernConfig)=>{
243
+ const bffPrefix = modernConfig.bff?.prefix;
244
+ const primaryBffPrefix = Array.isArray(bffPrefix) ? bffPrefix[0] : bffPrefix;
245
+ const isEffectApi = Boolean(modernConfig.bff) && modernConfig.bff?.runtimeFramework !== 'hono';
246
+ return isEffectApi && primaryBffPrefix ? primaryBffPrefix : void 0;
247
+ };
248
+ const effectiveServiceBindings = (serviceBindings)=>{
249
+ const bindings = Array.isArray(serviceBindings) ? serviceBindings : [];
250
+ return bindings.flatMap((service)=>(0, external_utils_js_namespaceObject.isJsonRecord)(service) && 'string' == typeof service.binding ? [
251
+ {
252
+ binding: service.binding.trim(),
253
+ prefix: 'string' == typeof service.prefix ? service.prefix : void 0
254
+ }
255
+ ] : []);
256
+ };
257
+ const createWorkerVpcServiceBindings = (modernConfig, configuredWranglerVpcServices, serviceBindings, workerBindingNames)=>{
258
+ const vpcServices = modernConfig.deploy?.worker?.vpcServices;
259
+ if (void 0 === vpcServices) return;
260
+ if (void 0 !== configuredWranglerVpcServices) throw new Error('Use deploy.worker.vpcServices or deploy.worker.wrangler.vpc_services, not both.');
261
+ if (!Array.isArray(vpcServices)) throw new Error('deploy.worker.vpcServices must be an array.');
262
+ const services = effectiveServiceBindings(serviceBindings);
263
+ const serviceBindingNames = new Set(services.map(({ binding })=>binding));
264
+ const effectBffPrefix = getWorkerEffectBffPrefix(modernConfig);
265
+ const routes = [
266
+ ...void 0 === effectBffPrefix ? [] : [
267
+ {
268
+ owner: 'the Effect BFF',
269
+ prefix: effectBffPrefix
270
+ }
271
+ ],
272
+ ...services.flatMap(({ binding, prefix })=>void 0 === prefix ? [] : [
273
+ {
274
+ owner: `binding "${binding}"`,
275
+ prefix
276
+ }
277
+ ])
278
+ ];
279
+ const vpcBindingNames = new Set();
280
+ return vpcServices.map((vpcService, index)=>{
281
+ const normalized = normalizeVpcService(vpcService, index);
282
+ if (serviceBindingNames.has(normalized.binding)) throw new Error(`deploy.worker.vpcServices[${index}].binding "${normalized.binding}" is already declared by deploy.worker.services.`);
283
+ if (workerBindingNames.has(normalized.binding)) throw new Error(`deploy.worker.vpcServices[${index}].binding "${normalized.binding}" is already a binding of this Worker.`);
284
+ if (vpcBindingNames.has(normalized.binding)) throw new Error(`deploy.worker.vpcServices[${index}].binding "${normalized.binding}" is declared more than once.`);
285
+ vpcBindingNames.add(normalized.binding);
286
+ if (void 0 !== normalized.prefix) {
287
+ const { prefix } = normalized;
288
+ 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).`);
289
+ const shadow = routes.find((route)=>prefixesOverlap(route.prefix, prefix));
290
+ 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.`);
291
+ routes.push({
292
+ owner: `binding "${normalized.binding}"`,
293
+ prefix
294
+ });
295
+ }
296
+ return normalized;
297
+ });
298
+ };
174
299
  const createWranglerServices = (serviceBindings)=>{
175
300
  if (!Array.isArray(serviceBindings)) return serviceBindings;
176
301
  if (!serviceBindings.every(external_utils_js_namespaceObject.isJsonRecord)) return serviceBindings;
@@ -179,8 +304,16 @@ const createWranglerServices = (serviceBindings)=>{
179
304
  return wranglerService;
180
305
  });
181
306
  };
182
- const createWorkerManifestServiceBindings = (serviceBindings)=>{
183
- if (!Array.isArray(serviceBindings)) return;
307
+ const createWorkerManifestServiceBindings = (serviceBindings, vpcServiceBindings)=>{
308
+ const vpcManifestBindings = (vpcServiceBindings ?? []).flatMap(({ binding, prefix, serviceId })=>void 0 === prefix ? [] : [
309
+ {
310
+ binding,
311
+ interface: 'fetch',
312
+ prefix,
313
+ vpcServiceId: serviceId
314
+ }
315
+ ]);
316
+ if (!Array.isArray(serviceBindings)) return vpcManifestBindings.length > 0 ? vpcManifestBindings : void 0;
184
317
  const bindings = serviceBindings;
185
318
  const manifestBindings = bindings.filter((service)=>(0, external_utils_js_namespaceObject.isJsonRecord)(service) && 'string' == typeof service.binding && 'string' == typeof service.service && ('string' == typeof service.prefix || Array.isArray(service.fragments) && service.fragments.length > 0)).map((service)=>({
186
319
  binding: service.binding,
@@ -193,7 +326,11 @@ const createWorkerManifestServiceBindings = (serviceBindings)=>{
193
326
  fragments: service.fragments
194
327
  }
195
328
  }));
196
- return manifestBindings.length > 0 ? manifestBindings : void 0;
329
+ const allBindings = [
330
+ ...manifestBindings,
331
+ ...vpcManifestBindings
332
+ ];
333
+ return allBindings.length > 0 ? allBindings : void 0;
197
334
  };
198
335
  const createWranglerConfig = (appDirectory, modernConfig)=>{
199
336
  const wrangler = getConfiguredWrangler(modernConfig);
@@ -201,7 +338,7 @@ const createWranglerConfig = (appDirectory, modernConfig)=>{
201
338
  const d1Databases = createWranglerD1Databases(modernConfig, wrangler.d1_databases);
202
339
  const serviceBindings = createWorkerServiceBindings(modernConfig, wrangler.services);
203
340
  const wranglerServices = createWranglerServices(serviceBindings);
204
- return {
341
+ const workerConfig = {
205
342
  $schema: 'node_modules/wrangler/config-schema.json',
206
343
  name: getConfiguredWorkerName(appDirectory, modernConfig),
207
344
  ...wrangler,
@@ -219,14 +356,46 @@ const createWranglerConfig = (appDirectory, modernConfig)=>{
219
356
  services: wranglerServices
220
357
  }
221
358
  };
359
+ const vpcServiceBindings = createWorkerVpcServiceBindings(modernConfig, wrangler.vpc_services, serviceBindings, collectWorkerBindingNames(workerConfig));
360
+ if (void 0 === vpcServiceBindings) return workerConfig;
361
+ const vpcServicesConfig = vpcServiceBindings.map(({ binding, serviceId })=>({
362
+ binding,
363
+ service_id: serviceId
364
+ }));
365
+ return {
366
+ ...workerConfig,
367
+ ...void 0 === environments ? {} : {
368
+ env: Object.fromEntries(Object.entries(environments).map(([name, environment])=>{
369
+ if (void 0 !== environment.vpc_services) throw new Error(`Use deploy.worker.vpcServices or deploy.worker.wrangler.env.${name}.vpc_services, not both.`);
370
+ const environmentBindingNames = new Set([
371
+ ...collectWorkerBindingNames(environment),
372
+ ...effectiveServiceBindings(environment.services).map(({ binding })=>binding)
373
+ ]);
374
+ const collision = vpcServiceBindings.find(({ binding })=>environmentBindingNames.has(binding));
375
+ if (void 0 !== collision) throw new Error(`deploy.worker.vpcServices binding "${collision.binding}" is already a binding of deploy.worker.wrangler.env.${name}.`);
376
+ return [
377
+ name,
378
+ {
379
+ ...environment,
380
+ vpc_services: vpcServicesConfig
381
+ }
382
+ ];
383
+ }))
384
+ },
385
+ vpc_services: vpcServicesConfig
386
+ };
222
387
  };
223
388
  exports.createWorkerManifestServiceBindings = __webpack_exports__.createWorkerManifestServiceBindings;
224
389
  exports.createWorkerServiceBindings = __webpack_exports__.createWorkerServiceBindings;
390
+ exports.createWorkerVpcServiceBindings = __webpack_exports__.createWorkerVpcServiceBindings;
225
391
  exports.createWranglerConfig = __webpack_exports__.createWranglerConfig;
392
+ exports.getWorkerEffectBffPrefix = __webpack_exports__.getWorkerEffectBffPrefix;
226
393
  for(var __rspack_i in __webpack_exports__)if (-1 === [
227
394
  "createWorkerManifestServiceBindings",
228
395
  "createWorkerServiceBindings",
229
- "createWranglerConfig"
396
+ "createWorkerVpcServiceBindings",
397
+ "createWranglerConfig",
398
+ "getWorkerEffectBffPrefix"
230
399
  ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
231
400
  Object.defineProperty(exports, '__esModule', {
232
401
  value: true
@@ -108,7 +108,8 @@ const CLOUDFLARE_WORKER_NODE_BUILTINS = [
108
108
  'zlib'
109
109
  ];
110
110
  const CLOUDFLARE_WORKER_PLATFORM_MODULES = [
111
- 'cloudflare:sockets'
111
+ 'cloudflare:sockets',
112
+ 'cloudflare:workers'
112
113
  ];
113
114
  const CLOUDFLARE_REQUIRED_COMPATIBILITY_FLAGS = [
114
115
  'nodejs_compat',
@@ -1,20 +1,47 @@
1
- import { execFileSync } from "node:child_process";
2
1
  import { createHash } from "node:crypto";
3
2
  import { accessSync, chmodSync, constants, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
4
3
  import { createRequire } from "node:module";
5
4
  import { tmpdir } from "node:os";
6
- import { basename, dirname, join, resolve } from "node:path";
5
+ import { basename, dirname, join } from "node:path";
7
6
  const EFFECT_TSGO_PACKAGE = '@effect/tsgo';
8
- const EFFECT_TSGO_BIN = 'effect-tsgo';
7
+ const NATIVE_TYPESCRIPT_PACKAGES = [
8
+ "typescript",
9
+ "@typescript/native"
10
+ ];
9
11
  const EFFECT_TSGO_RESOLUTION_ERROR = 'Unable to resolve the Effect TS-Go compiler. Install "@effect/tsgo" and a native TypeScript backend for this build config, or set EFFECT_TSGO_BIN.';
10
12
  const executableEffectTsgoCompilers = new Map();
11
- function resolveEffectTsgoCli(from) {
13
+ function readJson(fileName) {
14
+ return JSON.parse(readFileSync(fileName, 'utf-8'));
15
+ }
16
+ function resolvePackageVersion(packageRequire, packageName) {
17
+ let packageJsonPath;
18
+ try {
19
+ packageJsonPath = packageRequire.resolve(`${packageName}/package.json`);
20
+ } catch {
21
+ return;
22
+ }
23
+ const { version } = readJson(packageJsonPath);
24
+ return 'string' == typeof version ? {
25
+ packageJsonPath,
26
+ version
27
+ } : void 0;
28
+ }
29
+ function resolveEffectTsgoTypeScriptArtifact(from) {
12
30
  const projectRequire = createRequire(from);
13
- const packageJsonPath = projectRequire.resolve(`${EFFECT_TSGO_PACKAGE}/package.json`);
14
- const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
15
- const bin = 'string' == typeof packageJson.bin ? packageJson.bin : packageJson.bin?.[EFFECT_TSGO_BIN];
16
- if (!bin) throw new Error(EFFECT_TSGO_RESOLUTION_ERROR);
17
- return resolve(dirname(packageJsonPath), bin);
31
+ const effectTsgoPackageJsonPath = projectRequire.resolve(`${EFFECT_TSGO_PACKAGE}/package.json`);
32
+ const platformTarget = `${process.platform}-${process.arch}`;
33
+ const effectTsgoPlatformPackageJsonPath = createRequire(effectTsgoPackageJsonPath).resolve(`${EFFECT_TSGO_PACKAGE}-${platformTarget}/package.json`);
34
+ const upstream = readJson(join(dirname(effectTsgoPlatformPackageJsonPath), 'lib', 'upstream.json'));
35
+ const packagedTypeScriptVersions = upstream.components?.typescript ?? {};
36
+ for (const packageName of NATIVE_TYPESCRIPT_PACKAGES){
37
+ const typescript = resolvePackageVersion(projectRequire, packageName);
38
+ const major = typescript && /^\s*(\d+)/u.exec(typescript.version);
39
+ if (!typescript || !major || Number(major[1]) < 7) continue;
40
+ const typescriptPlatform = resolvePackageVersion(createRequire(typescript.packageJsonPath), `@typescript/typescript-${platformTarget}`);
41
+ if (!typescriptPlatform || !Object.hasOwn(packagedTypeScriptVersions, typescriptPlatform.version)) break;
42
+ return join(dirname(effectTsgoPlatformPackageJsonPath), 'artifacts', "typescript", typescriptPlatform.version, 'win32' === process.platform ? 'tsc.exe' : 'tsc');
43
+ }
44
+ throw new Error(EFFECT_TSGO_RESOLUTION_ERROR);
18
45
  }
19
46
  function resolveExecutableEffectTsgoCompiler(compilerPath) {
20
47
  if ('win32' === process.platform) return compilerPath;
@@ -75,13 +102,7 @@ function resolveEffectTsgoCompiler(options) {
75
102
  throw new Error(EFFECT_TSGO_RESOLUTION_ERROR);
76
103
  }
77
104
  try {
78
- const compiler = execFileSync(process.execPath, [
79
- resolveEffectTsgoCli(options.from),
80
- 'get-exe-path'
81
- ], {
82
- encoding: 'utf-8'
83
- }).trim();
84
- if (compiler) return resolveExecutableEffectTsgoCompiler(compiler);
105
+ return resolveExecutableEffectTsgoCompiler(resolveEffectTsgoTypeScriptArtifact(options.from));
85
106
  } catch {}
86
107
  throw new Error(EFFECT_TSGO_RESOLUTION_ERROR);
87
108
  }
@@ -6,7 +6,7 @@ import { ASSETS_BINDING, BFF_EFFECT_WORKER_DISPATCHER_EXPORT, BFF_EFFECT_WORKER_
6
6
  import { createI18nWorkerManifest } from "./i18n-worker.mjs";
7
7
  import { createCloudflareWorkerSecurityPolicy } from "./security-policies.mjs";
8
8
  import { isRecord, normalizeRelativePath } from "./utils.mjs";
9
- import { createWorkerManifestServiceBindings, createWorkerServiceBindings } from "./wrangler-config.mjs";
9
+ import { createWorkerManifestServiceBindings, createWorkerServiceBindings, createWorkerVpcServiceBindings, getWorkerEffectBffPrefix } from "./wrangler-config.mjs";
10
10
  const createMissingEffectBffWorkerError = (outputDirectory, worker)=>new Error(`Cloudflare Effect API runtime is configured, but the BFF worker bundle is missing: ${node_path.join(outputDirectory, worker)}. ${EFFECT_BFF_CLOUDFLARE_IMPORT_GUIDANCE}`);
11
11
  const createEffectBffWorkerManifest = (modernConfig)=>{
12
12
  const bff = modernConfig.bff;
@@ -80,13 +80,15 @@ const createWorkerManifest = async (outputDirectory, modernConfig, appContext, d
80
80
  } : {}
81
81
  };
82
82
  }));
83
- const bffPrefix = modernConfig.bff?.prefix;
84
- const primaryBffPrefix = Array.isArray(bffPrefix) ? bffPrefix[0] : bffPrefix;
85
83
  const isEffectApi = Boolean(modernConfig.bff) && modernConfig.bff?.runtimeFramework !== 'hono';
84
+ const effectBffPrefix = getWorkerEffectBffPrefix(modernConfig);
86
85
  const effectApiWorkerExists = await fs.pathExists(node_path.join(outputDirectory, BFF_EFFECT_WORKER_ENTRY));
87
- const serviceBindings = createWorkerManifestServiceBindings(createWorkerServiceBindings(modernConfig, void 0));
86
+ const typedServiceBindings = createWorkerServiceBindings(modernConfig, void 0);
87
+ const serviceBindings = createWorkerManifestServiceBindings(typedServiceBindings, createWorkerVpcServiceBindings(modernConfig, void 0, typedServiceBindings, new Set([
88
+ ASSETS_BINDING
89
+ ])));
88
90
  const moduleFederation = await createModuleFederationWorkerManifest(outputDirectory);
89
- if (isEffectApi && primaryBffPrefix && !effectApiWorkerExists) throw createMissingEffectBffWorkerError(outputDirectory, BFF_EFFECT_WORKER_ENTRY);
91
+ if (void 0 !== effectBffPrefix && !effectApiWorkerExists) throw createMissingEffectBffWorkerError(outputDirectory, BFF_EFFECT_WORKER_ENTRY);
90
92
  const effectBffManifest = isEffectApi ? createEffectBffWorkerManifest(modernConfig) : void 0;
91
93
  return {
92
94
  version: 1,
@@ -123,10 +125,10 @@ const createWorkerManifest = async (outputDirectory, modernConfig, appContext, d
123
125
  deliveryUnit: deliveryUnitStamp
124
126
  } : {},
125
127
  i18n: createI18nWorkerManifest(routeSpec, appContext),
126
- bff: isEffectApi && primaryBffPrefix && effectApiWorkerExists ? {
128
+ bff: void 0 !== effectBffPrefix && effectApiWorkerExists ? {
127
129
  dispatcherExport: BFF_EFFECT_WORKER_DISPATCHER_EXPORT,
128
130
  runtimeFramework: 'effect',
129
- prefix: primaryBffPrefix,
131
+ prefix: effectBffPrefix,
130
132
  worker: BFF_EFFECT_WORKER_ENTRY,
131
133
  effect: effectBffManifest
132
134
  } : void 0,