@gigadrive/network-config 4.6.1 → 5.1.0

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/index.js CHANGED
@@ -33,6 +33,9 @@ __export(src_exports, {
33
33
  ALLOWED_CONFIG_NAMES: () => ALLOWED_CONFIG_NAMES,
34
34
  AVAILABLE_REGIONS: () => AVAILABLE_REGIONS,
35
35
  AVAILABLE_RUNTIMES: () => AVAILABLE_RUNTIMES,
36
+ COMPOSE_FILE_NAMES: () => COMPOSE_FILE_NAMES,
37
+ CONTAINER_ENTRYPOINT_PREFIX: () => CONTAINER_ENTRYPOINT_PREFIX,
38
+ CONTAINER_RUNTIME: () => CONTAINER_RUNTIME,
36
39
  ConfigFileEmptyError: () => ConfigFileEmptyError,
37
40
  ConfigFileNotFoundError: () => ConfigFileNotFoundError,
38
41
  ConfigFileParseError: () => ConfigFileParseError,
@@ -40,7 +43,10 @@ __export(src_exports, {
40
43
  ConfigModuleLoader: () => ConfigModuleLoader,
41
44
  ConfigSchemaValidationError: () => ConfigSchemaValidationError,
42
45
  ConfigVersionError: () => ConfigVersionError,
46
+ ContainerConfigError: () => ContainerConfigError,
47
+ DEFAULT_CONTAINER_MEMORY_MB: () => DEFAULT_CONTAINER_MEMORY_MB,
43
48
  DEFAULT_FUNCTION_DURATION_SECONDS: () => DEFAULT_FUNCTION_DURATION_SECONDS,
49
+ DEFAULT_SIDECAR_MEMORY_MB: () => DEFAULT_SIDECAR_MEMORY_MB,
44
50
  FRAMEWORK_DEFINITIONS: () => FRAMEWORK_DEFINITIONS,
45
51
  FrameworkNotDetectedError: () => FrameworkNotDetectedError,
46
52
  FunctionConfigError: () => FunctionConfigError,
@@ -57,6 +63,7 @@ __export(src_exports, {
57
63
  GigadriveNextServerDescriptorSchema: () => GigadriveNextServerDescriptorSchema,
58
64
  GigadriveNextStaticAssetPrefixSchema: () => GigadriveNextStaticAssetPrefixSchema,
59
65
  MAX_FUNCTION_DURATION_SECONDS: () => MAX_FUNCTION_DURATION_SECONDS,
66
+ MAX_SIDECARS: () => MAX_SIDECARS,
60
67
  ManifestNotFoundError: () => ManifestNotFoundError,
61
68
  ManifestParseError: () => ManifestParseError,
62
69
  ManifestReadError: () => ManifestReadError,
@@ -68,20 +75,28 @@ __export(src_exports, {
68
75
  StaticAssetManifestV1Schema: () => StaticAssetManifestV1Schema,
69
76
  V4ConfigParser: () => V4ConfigParser,
70
77
  VercelBuildOutputParser: () => VercelBuildOutputParser,
78
+ collectAssetFiles: () => collectAssetFiles,
79
+ composeDeclaresApp: () => composeDeclaresApp,
80
+ containerEntrypointPath: () => containerEntrypointPath,
71
81
  defineConfig: () => defineConfig,
82
+ detectContainerProject: () => detectContainerProject,
72
83
  detectFramework: () => detectFramework,
73
84
  detectPackageManager: () => detectPackageManager,
85
+ dockerFunctionContainerName: () => dockerFunctionContainerName,
74
86
  filterFunctionsFromAssets: () => filterFunctionsFromAssets,
75
87
  generateConfig: () => generateConfig,
76
88
  getFunctionSettings: () => getFunctionSettings,
77
89
  mergeWithFrameworkDefaults: () => mergeWithFrameworkDefaults,
90
+ normalizeContainers: () => normalizeContainers,
78
91
  parseConfig: () => parseConfig,
79
92
  parseConfigRaw: () => parseConfigRaw,
80
93
  parseGigadriveNextBuildManifest: () => parseGigadriveNextBuildManifest,
81
94
  parseGigadriveNextPrerenderManifest: () => parseGigadriveNextPrerenderManifest,
82
95
  parseStaticAssetManifest: () => parseStaticAssetManifest,
83
96
  postProcessConfig: () => postProcessConfig,
84
- readDependencies: () => readDependencies
97
+ readComposeContainers: () => readComposeContainers,
98
+ readDependencies: () => readDependencies,
99
+ splitCommandWords: () => splitCommandWords
85
100
  });
86
101
  module.exports = __toCommonJS(src_exports);
87
102
 
@@ -142,16 +157,862 @@ var StaticAssetManifestV1Schema = import_effect2.Schema.mutable(
142
157
  ).pipe(import_effect2.Schema.filter(({ assets }) => new Set(assets.map(({ path: path2 }) => path2)).size === assets.length));
143
158
  var parseStaticAssetManifest = (content) => decodeJson(StaticAssetManifestV1Schema, content);
144
159
 
160
+ // src/collect-asset-files.ts
161
+ var import_platform = require("@effect/platform");
162
+ var import_effect3 = require("effect");
163
+ var MAX_ASSET_DEPTH = 100;
164
+ var DISALLOWED_ASSET_EXTENSIONS = [".htaccess", ".htpasswd"];
165
+ var collectFilesRecursively = import_effect3.Effect.fn("collectFilesRecursively")(
166
+ function* (basePath, relativePath = "", depth = 0) {
167
+ if (depth > MAX_ASSET_DEPTH) return [];
168
+ const fs = yield* import_platform.FileSystem.FileSystem;
169
+ const pathSvc = yield* import_platform.Path.Path;
170
+ const currentPath = relativePath ? pathSvc.join(basePath, relativePath) : basePath;
171
+ const entries = yield* fs.readDirectory(currentPath).pipe(import_effect3.Effect.catchAll(() => import_effect3.Effect.succeed([])));
172
+ const result = [];
173
+ for (const name of entries) {
174
+ const fullPath = pathSvc.join(currentPath, name);
175
+ const entryRelative = relativePath ? pathSvc.join(relativePath, name) : name;
176
+ const stat = yield* fs.stat(fullPath).pipe(import_effect3.Effect.catchAll(() => import_effect3.Effect.succeed(null)));
177
+ if (!stat) continue;
178
+ if (stat.type === "Directory") {
179
+ const nested = yield* collectFilesRecursively(basePath, entryRelative, depth + 1);
180
+ result.push(...nested);
181
+ } else {
182
+ result.push(entryRelative);
183
+ }
184
+ }
185
+ return result;
186
+ }
187
+ );
188
+ var collectAssetFiles = import_effect3.Effect.fn("collectAssetFiles")(function* (projectFolder, assetsDir) {
189
+ const fs = yield* import_platform.FileSystem.FileSystem;
190
+ const pathService = yield* import_platform.Path.Path;
191
+ const assetsPath = pathService.join(projectFolder, assetsDir);
192
+ const stat = yield* fs.stat(assetsPath).pipe(import_effect3.Effect.catchAll(() => import_effect3.Effect.succeed(null)));
193
+ if (!stat || stat.type !== "Directory") return [];
194
+ const files = yield* collectFilesRecursively(assetsPath);
195
+ return files.map((file) => file.replaceAll("\\", "/")).filter((file) => !DISALLOWED_ASSET_EXTENSIONS.some((extension) => file.toLowerCase().endsWith(extension))).sort();
196
+ });
197
+
198
+ // src/containers.ts
199
+ var import_platform2 = require("@effect/platform");
200
+ var import_effect5 = require("effect");
201
+ var import_node_path = require("path");
202
+ var import_yaml = require("yaml");
203
+
204
+ // src/errors.ts
205
+ var import_effect4 = require("effect");
206
+ var ConfigFileNotFoundError = class extends import_effect4.Schema.TaggedError()("ConfigFileNotFoundError", {
207
+ message: import_effect4.Schema.String,
208
+ filePath: import_effect4.Schema.String
209
+ }) {
210
+ };
211
+ var ConfigFileEmptyError = class extends import_effect4.Schema.TaggedError()("ConfigFileEmptyError", {
212
+ message: import_effect4.Schema.String,
213
+ filePath: import_effect4.Schema.String
214
+ }) {
215
+ };
216
+ var ConfigFileParseError = class extends import_effect4.Schema.TaggedError()("ConfigFileParseError", {
217
+ message: import_effect4.Schema.String,
218
+ filePath: import_effect4.Schema.String,
219
+ cause: import_effect4.Schema.optional(import_effect4.Schema.String)
220
+ }) {
221
+ };
222
+ var ConfigModuleLoadError = class extends import_effect4.Schema.TaggedError()("ConfigModuleLoadError", {
223
+ message: import_effect4.Schema.String,
224
+ filePath: import_effect4.Schema.String,
225
+ cause: import_effect4.Schema.optional(import_effect4.Schema.String)
226
+ }) {
227
+ };
228
+ var ConfigVersionError = class extends import_effect4.Schema.TaggedError()("ConfigVersionError", {
229
+ message: import_effect4.Schema.String,
230
+ filePath: import_effect4.Schema.String,
231
+ version: import_effect4.Schema.optional(import_effect4.Schema.Number)
232
+ }) {
233
+ };
234
+ var ConfigSchemaValidationError = class extends import_effect4.Schema.TaggedError()(
235
+ "ConfigSchemaValidationError",
236
+ {
237
+ message: import_effect4.Schema.String,
238
+ filePath: import_effect4.Schema.String,
239
+ validationErrors: import_effect4.Schema.Array(import_effect4.Schema.String)
240
+ }
241
+ ) {
242
+ };
243
+ var FunctionConfigError = class extends import_effect4.Schema.TaggedError()("FunctionConfigError", {
244
+ message: import_effect4.Schema.String,
245
+ functionPath: import_effect4.Schema.String
246
+ }) {
247
+ };
248
+ var ContainerConfigError = class extends import_effect4.Schema.TaggedError()("ContainerConfigError", {
249
+ message: import_effect4.Schema.String,
250
+ containerName: import_effect4.Schema.optional(import_effect4.Schema.String),
251
+ filePath: import_effect4.Schema.optional(import_effect4.Schema.String)
252
+ }) {
253
+ };
254
+
255
+ // src/normalized-config.ts
256
+ var DEFAULT_FUNCTION_DURATION_SECONDS = 30;
257
+ var MAX_FUNCTION_DURATION_SECONDS = 28800;
258
+ var CONTAINER_RUNTIME = "docker";
259
+ var CONTAINER_ENTRYPOINT_PREFIX = "container:";
260
+ var DEFAULT_CONTAINER_MEMORY_MB = 512;
261
+ var DEFAULT_SIDECAR_MEMORY_MB = 256;
262
+ var MAX_SIDECARS = 4;
263
+
264
+ // src/containers.ts
265
+ var COMPOSE_FILE_NAMES = ["compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml"];
266
+ var CONTAINER_NAME_PATTERN = /^[a-z][a-z0-9_-]{0,62}$/;
267
+ var CONTAINER_NAME_RULES = "1 to 63 lowercase letters, digits, '-' or '_', starting with a letter, and not 'localhost'";
268
+ var MIN_CONTAINER_MEMORY_MB = 128;
269
+ var MAX_CONTAINER_MEMORY_MB = 3009;
270
+ var MAX_CONTAINER_ENV_VARS = 100;
271
+ var MAX_CONTAINER_ENV_VALUE_LENGTH = 65536;
272
+ var containerEntrypointPath = (name) => `${CONTAINER_ENTRYPOINT_PREFIX}${name}`;
273
+ var toContainerName = (raw) => {
274
+ const cleaned = raw.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^[^a-z0-9]+/, "");
275
+ const name = (cleaned === "" || /^[a-z]/.test(cleaned) ? cleaned : `app-${cleaned}`).slice(0, 63);
276
+ return name === "" || name === "localhost" ? "app" : name;
277
+ };
278
+ var dockerFunctionContainerName = (file) => {
279
+ const { dir, base } = import_node_path.posix.parse(file);
280
+ const variant = base.replace(/^Dockerfile[.-]?/i, "").replace(/[.-]?Dockerfile$/i, "");
281
+ const raw = [import_node_path.posix.basename(dir), variant === base ? import_node_path.posix.parse(base).name : variant].filter((part) => part !== "").join("-");
282
+ return toContainerName(raw);
283
+ };
284
+ var splitCommandWords = (input) => {
285
+ const words = [];
286
+ let current = "";
287
+ let inWord = false;
288
+ let quote;
289
+ for (let index = 0; index < input.length; index += 1) {
290
+ const char = input[index];
291
+ if (quote === "'") {
292
+ if (char === "'") quote = void 0;
293
+ else current += char;
294
+ } else if (char === "\\" && index + 1 < input.length) {
295
+ index += 1;
296
+ current += input[index];
297
+ inWord = true;
298
+ } else if (quote === '"') {
299
+ if (char === '"') quote = void 0;
300
+ else current += char;
301
+ } else if (char === "'" || char === '"') {
302
+ quote = char;
303
+ inWord = true;
304
+ } else if (/\s/.test(char)) {
305
+ if (inWord) words.push(current);
306
+ current = "";
307
+ inWord = false;
308
+ } else {
309
+ current += char;
310
+ inWord = true;
311
+ }
312
+ }
313
+ if (quote) throw new Error(`Unterminated ${quote} quote`);
314
+ if (inWord) words.push(current);
315
+ return words;
316
+ };
317
+ var normalizeProjectPath = (input) => {
318
+ if (import_node_path.posix.isAbsolute(input)) return void 0;
319
+ const normalized = import_node_path.posix.normalize(input.replace(/\\/g, "/")).replace(/\/+$/, "") || ".";
320
+ if (normalized === ".." || normalized.startsWith("../")) return void 0;
321
+ return normalized;
322
+ };
323
+ var resolveProjectRoot = (projectFolder) => import_effect5.Effect.flatMap(
324
+ import_platform2.FileSystem.FileSystem,
325
+ (fs) => fs.realPath(projectFolder).pipe(import_effect5.Effect.orElseSucceed(() => projectFolder))
326
+ );
327
+ var resolveInsideProject = (projectRoot, projectRelative, subject, context) => import_effect5.Effect.gen(function* () {
328
+ const fs = yield* import_platform2.FileSystem.FileSystem;
329
+ const pathSvc = yield* import_platform2.Path.Path;
330
+ const resolved = yield* fs.realPath(pathSvc.join(projectRoot, projectRelative)).pipe(import_effect5.Effect.option);
331
+ if (import_effect5.Option.isNone(resolved)) return resolved;
332
+ const relative = pathSvc.relative(projectRoot, resolved.value);
333
+ if (relative === ".." || relative.startsWith(`..${pathSvc.sep}`) || pathSvc.isAbsolute(relative)) {
334
+ return yield* new ContainerConfigError({
335
+ message: `${subject} leads through a symbolic link to a location outside the project. Only files inside the project can be deployed.`,
336
+ ...context
337
+ });
338
+ }
339
+ return resolved;
340
+ });
341
+ var readInsideProject = (projectRoot, projectRelative, subject, context) => import_effect5.Effect.gen(function* () {
342
+ const fs = yield* import_platform2.FileSystem.FileSystem;
343
+ const resolved = yield* resolveInsideProject(projectRoot, projectRelative, subject, context);
344
+ if (import_effect5.Option.isNone(resolved)) return import_effect5.Option.none();
345
+ return yield* fs.readFileString(resolved.value).pipe(import_effect5.Effect.option);
346
+ });
347
+ var toArgv = (name, key, value) => value === void 0 ? import_effect5.Effect.succeed(void 0) : Array.isArray(value) ? import_effect5.Effect.succeed(value) : import_effect5.Effect.try({
348
+ try: () => splitCommandWords(value),
349
+ catch: (error) => new ContainerConfigError({
350
+ message: `Container '${name}' has an invalid ${key}: ${error instanceof Error ? error.message : String(error)}`,
351
+ containerName: name
352
+ })
353
+ });
354
+ var isIntegerBetween = (value, min, max) => Number.isInteger(value) && value >= min && value <= max;
355
+ var validateContainer = (name, container) => {
356
+ const build = typeof container.build === "object" ? container.build : void 0;
357
+ const buildArgs = Object.values(build?.args ?? {});
358
+ const env = Object.entries(container.env ?? {});
359
+ const user = container.user === void 0 ? void 0 : String(container.user);
360
+ const badEnvKey = env.find(([key]) => key === "" || key.includes("=") || key.includes("\0"))?.[0];
361
+ const longEnvKey = env.find(([, value]) => value.length > MAX_CONTAINER_ENV_VALUE_LENGTH)?.[0];
362
+ const problem = [
363
+ [
364
+ container.image !== void 0 && !/^\S{1,512}$/.test(container.image),
365
+ "has an invalid image reference. Use 1 to 512 characters without whitespace."
366
+ ],
367
+ [
368
+ container.port !== void 0 && !isIntegerBetween(container.port, 1, 65535),
369
+ `listens on port ${container.port}. Use a whole number from 1 to 65535.`
370
+ ],
371
+ [
372
+ container.memory !== void 0 && !isIntegerBetween(container.memory, MIN_CONTAINER_MEMORY_MB, MAX_CONTAINER_MEMORY_MB),
373
+ `asks for ${container.memory} MB of memory. Use a whole number from ${MIN_CONTAINER_MEMORY_MB} to ${MAX_CONTAINER_MEMORY_MB}.`
374
+ ],
375
+ [
376
+ container.working_dir !== void 0 && !(container.working_dir.startsWith("/") && container.working_dir.length <= 1024),
377
+ `sets working_dir '${container.working_dir}', which must be an absolute path of at most 1024 characters.`
378
+ ],
379
+ [
380
+ typeof container.user === "number" && !isIntegerBetween(container.user, 0, Number.MAX_SAFE_INTEGER),
381
+ `sets user ${container.user}. A numeric user must be a non-negative whole number.`
382
+ ],
383
+ [
384
+ user !== void 0 && (user.length === 0 || user.length > 256),
385
+ "sets an empty or overlong user. Use 1 to 256 characters: a name, a uid, name:group or uid:gid."
386
+ ],
387
+ [
388
+ build?.target !== void 0 && (build.target.length === 0 || build.target.length > 128),
389
+ "has an empty build target or one longer than 128 characters."
390
+ ],
391
+ [
392
+ buildArgs.length > MAX_CONTAINER_ENV_VARS || buildArgs.some((value) => value.length > MAX_CONTAINER_ENV_VALUE_LENGTH),
393
+ `sets more than ${MAX_CONTAINER_ENV_VARS} build args, or one longer than ${MAX_CONTAINER_ENV_VALUE_LENGTH} characters.`
394
+ ],
395
+ [
396
+ env.length > MAX_CONTAINER_ENV_VARS,
397
+ `sets ${env.length} environment variables. At most ${MAX_CONTAINER_ENV_VARS} are allowed.`
398
+ ],
399
+ [
400
+ badEnvKey !== void 0,
401
+ `sets an environment variable named '${badEnvKey}'. Names must be non-empty and contain no '='.`
402
+ ],
403
+ [
404
+ longEnvKey !== void 0,
405
+ `sets environment variable '${longEnvKey}' to a value longer than ${MAX_CONTAINER_ENV_VALUE_LENGTH} characters.`
406
+ ]
407
+ ].find(([failed]) => failed)?.[1];
408
+ return problem === void 0 ? import_effect5.Effect.void : import_effect5.Effect.fail(new ContainerConfigError({ message: `Container '${name}' ${problem}`, containerName: name }));
409
+ };
410
+ var toContainerSpec = import_effect5.Effect.fn("toContainerSpec")(function* (name, container, projectFolder) {
411
+ yield* validateContainer(name, container);
412
+ let source;
413
+ if (container.image !== void 0 && container.build === void 0) {
414
+ source = { type: "registry", reference: container.image };
415
+ } else if (container.build !== void 0 && container.image === void 0) {
416
+ const build = typeof container.build === "string" ? { context: container.build } : container.build;
417
+ const dockerfile = build.dockerfile ?? "Dockerfile";
418
+ const context = normalizeProjectPath(build.context ?? ".");
419
+ const dockerfilePath = context === void 0 || import_node_path.posix.isAbsolute(dockerfile) ? void 0 : normalizeProjectPath(import_node_path.posix.join(context, dockerfile));
420
+ if (context === void 0 || dockerfilePath === void 0) {
421
+ return yield* new ContainerConfigError({
422
+ message: `Container '${name}' has a build context or Dockerfile outside the project.`,
423
+ containerName: name
424
+ });
425
+ }
426
+ const projectRoot = yield* resolveProjectRoot(projectFolder);
427
+ yield* resolveInsideProject(projectRoot, context, `The build context '${context}' of container '${name}'`, {
428
+ containerName: name
429
+ });
430
+ const dockerfileReal = yield* resolveInsideProject(
431
+ projectRoot,
432
+ dockerfilePath,
433
+ `The Dockerfile '${dockerfilePath}' of container '${name}'`,
434
+ { containerName: name }
435
+ );
436
+ if (import_effect5.Option.isNone(dockerfileReal)) {
437
+ return yield* new ContainerConfigError({
438
+ message: `Container '${name}' builds '${dockerfilePath}', which does not exist.`,
439
+ containerName: name
440
+ });
441
+ }
442
+ source = {
443
+ type: "dockerfile",
444
+ context,
445
+ dockerfile: import_node_path.posix.relative(context, dockerfilePath),
446
+ ...build.target !== void 0 && { target: build.target },
447
+ ...build.args !== void 0 && Object.keys(build.args).length > 0 && { buildArgs: { ...build.args } }
448
+ };
449
+ } else {
450
+ return yield* new ContainerConfigError({
451
+ message: `Container '${name}' must set exactly one of 'image' and 'build'.`,
452
+ containerName: name
453
+ });
454
+ }
455
+ const entrypoint = yield* toArgv(name, "entrypoint", container.entrypoint);
456
+ const command = yield* toArgv(name, "command", container.command);
457
+ const spec = {
458
+ name,
459
+ source,
460
+ ...container.port !== void 0 && { port: container.port },
461
+ ...entrypoint !== void 0 && { entrypoint },
462
+ ...command !== void 0 && { command },
463
+ ...container.working_dir !== void 0 && { workingDirectory: container.working_dir },
464
+ ...container.user !== void 0 && { user: String(container.user) },
465
+ ...container.env !== void 0 && Object.keys(container.env).length > 0 && { environmentVariables: { ...container.env } }
466
+ };
467
+ return spec;
468
+ });
469
+ var normalizeContainers = import_effect5.Effect.fn("normalizeContainers")(function* (containers, projectFolder) {
470
+ const entrypoints = [];
471
+ const sidecars = [];
472
+ const warnings = [];
473
+ for (const [name, container] of Object.entries(containers)) {
474
+ if (!CONTAINER_NAME_PATTERN.test(name) || name === "localhost") {
475
+ return yield* new ContainerConfigError({
476
+ message: `Container name '${name}' is invalid. The name is also the hostname the function reaches the container by, so it must be ${CONTAINER_NAME_RULES}. Try '${toContainerName(name)}'.`,
477
+ containerName: name
478
+ });
479
+ }
480
+ const spec = yield* toContainerSpec(name, container, projectFolder);
481
+ if (container.sidecar === true) {
482
+ const ignored = ["max_duration", "streaming", "schedule"].filter(
483
+ (key) => container[key] !== void 0
484
+ );
485
+ if (ignored.length > 0) {
486
+ warnings.push(
487
+ `Sidecar '${name}' ignores ${ignored.join(", ")}: those settings apply to container functions only.`
488
+ );
489
+ }
490
+ sidecars.push({ ...spec, memory: container.memory ?? DEFAULT_SIDECAR_MEMORY_MB });
491
+ continue;
492
+ }
493
+ entrypoints.push({
494
+ path: containerEntrypointPath(name),
495
+ displayName: name,
496
+ runtime: CONTAINER_RUNTIME,
497
+ memory: container.memory ?? DEFAULT_CONTAINER_MEMORY_MB,
498
+ maxDuration: container.max_duration ?? DEFAULT_FUNCTION_DURATION_SECONDS,
499
+ streaming: container.streaming ?? true,
500
+ ...container.schedule !== void 0 && { schedule: container.schedule },
501
+ container: spec
502
+ });
503
+ }
504
+ if (sidecars.length > MAX_SIDECARS) {
505
+ return yield* new ContainerConfigError({
506
+ message: `A deployment may declare at most ${MAX_SIDECARS} sidecars; this one declares ${sidecars.length}.`
507
+ });
508
+ }
509
+ const sidecarPorts = /* @__PURE__ */ new Map();
510
+ for (const sidecar of sidecars) {
511
+ if (sidecar.port === void 0) continue;
512
+ const owner = sidecarPorts.get(sidecar.port);
513
+ if (owner !== void 0) {
514
+ return yield* new ContainerConfigError({
515
+ message: `Sidecars '${owner}' and '${sidecar.name}' both listen on port ${sidecar.port}. Sidecars share one network namespace, so their ports must differ.`,
516
+ containerName: sidecar.name
517
+ });
518
+ }
519
+ sidecarPorts.set(sidecar.port, sidecar.name);
520
+ }
521
+ for (const entrypoint of entrypoints) {
522
+ const port = entrypoint.container?.port;
523
+ const sidecar = port === void 0 ? void 0 : sidecarPorts.get(port);
524
+ if (sidecar !== void 0) {
525
+ return yield* new ContainerConfigError({
526
+ message: `Container '${entrypoint.displayName}' and sidecar '${sidecar}' both listen on port ${port}. Sidecars run inside the function's microVM, so their ports must differ.`,
527
+ containerName: sidecar
528
+ });
529
+ }
530
+ }
531
+ return { entrypoints, sidecars, warnings };
532
+ });
533
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
534
+ var isStringArray = (value) => Array.isArray(value) && value.every((item) => typeof item === "string");
535
+ var scalarToString = (value) => typeof value === "string" || typeof value === "number" || typeof value === "boolean" ? String(value) : void 0;
536
+ var gigadriveExtension = (service) => isRecord(service["x-gigadrive"]) ? service["x-gigadrive"] : {};
537
+ var COMPOSE_OVERRIDE_FILE_NAMES = [
538
+ "compose.override.yaml",
539
+ "compose.override.yml",
540
+ "docker-compose.override.yaml",
541
+ "docker-compose.override.yml"
542
+ ];
543
+ var findClosingQuote = (body, quote) => {
544
+ for (let index = 0; index < body.length; index += 1) {
545
+ if (quote === '"' && body[index] === "\\") index += 1;
546
+ else if (body[index] === quote) return index;
547
+ }
548
+ return -1;
549
+ };
550
+ var DOUBLE_QUOTE_ESCAPES = { n: "\n", r: "\r", t: " " };
551
+ var parseDotEnv = (content) => {
552
+ const values = {};
553
+ const lines = content.split(/\r?\n/);
554
+ for (let index = 0; index < lines.length; index += 1) {
555
+ const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_.-]*)\s*=(.*)$/.exec(lines[index]);
556
+ if (!match) continue;
557
+ const [, key, rest] = match;
558
+ const quote = rest.trimStart()[0];
559
+ if (quote === '"' || quote === "'") {
560
+ let body = rest.trimStart().slice(1);
561
+ let end = index;
562
+ let close = findClosingQuote(body, quote);
563
+ while (close === -1 && end + 1 < lines.length) {
564
+ end += 1;
565
+ body += `
566
+ ${lines[end]}`;
567
+ close = findClosingQuote(body, quote);
568
+ }
569
+ if (close !== -1) {
570
+ const raw = body.slice(0, close);
571
+ values[key] = quote === '"' ? raw.replace(/\\([nrt"\\])/g, (_match, char) => DOUBLE_QUOTE_ESCAPES[char] ?? char) : raw;
572
+ index = end;
573
+ continue;
574
+ }
575
+ }
576
+ values[key] = rest.replace(/\s+#.*$/, "").trim();
577
+ }
578
+ return values;
579
+ };
580
+ var InterpolationError = class extends Error {
581
+ };
582
+ var findClosingBrace = (value, start) => {
583
+ let depth = 0;
584
+ for (let index = start; index < value.length; index += 1) {
585
+ if (value[index] === "$" && (value[index + 1] === "$" || value[index + 1] === "{")) {
586
+ if (value[index + 1] === "{") depth += 1;
587
+ index += 1;
588
+ } else if (value[index] === "}") {
589
+ if (depth === 0) return index;
590
+ depth -= 1;
591
+ }
592
+ }
593
+ return -1;
594
+ };
595
+ var interpolate = (value, variables, missing, where) => {
596
+ let result = "";
597
+ let index = 0;
598
+ while (index < value.length) {
599
+ const dollar = value.indexOf("$", index);
600
+ if (dollar === -1) {
601
+ result += value.slice(index);
602
+ break;
603
+ }
604
+ result += value.slice(index, dollar);
605
+ const next = value[dollar + 1];
606
+ if (next === "$") {
607
+ result += "$";
608
+ index = dollar + 2;
609
+ continue;
610
+ }
611
+ if (next === "{") {
612
+ const close = findClosingBrace(value, dollar + 2);
613
+ const expression = close === -1 ? null : /^([A-Za-z_][A-Za-z0-9_]*)(?:(:?[-?+])([\s\S]*))?$/.exec(value.slice(dollar + 2, close));
614
+ if (expression === null) {
615
+ throw new InterpolationError(`has an invalid variable reference in ${where}. Write '$$' for a literal '$'.`);
616
+ }
617
+ const [, name, operator, operand = ""] = expression;
618
+ const current = variables[name];
619
+ const isSet = current !== void 0 && !(operator?.startsWith(":") === true && current === "");
620
+ if (operator === void 0) {
621
+ if (current === void 0) missing.add(name);
622
+ result += current ?? "";
623
+ } else if (operator.endsWith("-")) {
624
+ result += isSet ? current : interpolate(operand, variables, missing, where);
625
+ } else if (operator.endsWith("+")) {
626
+ result += isSet ? interpolate(operand, variables, missing, where) : "";
627
+ } else if (isSet) {
628
+ result += current;
629
+ } else {
630
+ const reason = interpolate(operand, variables, missing, where);
631
+ throw new InterpolationError(
632
+ `needs variable '${name}' in ${where}, which is ${current === void 0 ? "not set" : "empty"}${reason === "" ? "" : `: ${reason}`}. Compose variables come only from the .env file next to the Compose file.`
633
+ );
634
+ }
635
+ index = close + 1;
636
+ continue;
637
+ }
638
+ const bare = /^[A-Za-z_][A-Za-z0-9_]*/.exec(value.slice(dollar + 1))?.[0];
639
+ if (bare === void 0) {
640
+ result += "$";
641
+ index = dollar + 1;
642
+ continue;
643
+ }
644
+ if (variables[bare] === void 0) missing.add(bare);
645
+ result += variables[bare] ?? "";
646
+ index = dollar + 1 + bare.length;
647
+ }
648
+ return result;
649
+ };
650
+ var interpolateDeep = (value, variables, missing, where) => {
651
+ if (typeof value === "string") return interpolate(value, variables, missing, where);
652
+ if (Array.isArray(value)) {
653
+ return value.map((item, index) => interpolateDeep(item, variables, missing, `${where}[${index}]`));
654
+ }
655
+ if (isRecord(value)) {
656
+ return Object.fromEntries(
657
+ Object.entries(value).map(([key, item]) => [
658
+ key,
659
+ interpolateDeep(item, variables, missing, where === "" ? key : `${where}.${key}`)
660
+ ])
661
+ );
662
+ }
663
+ return value;
664
+ };
665
+ var parseComposeMemory = (value) => {
666
+ if (typeof value === "number") {
667
+ return Number.isFinite(value) && value >= 0 ? Math.ceil(value / 1024 ** 2) : void 0;
668
+ }
669
+ if (typeof value !== "string") return void 0;
670
+ const match = /^(\d+(?:\.\d+)?) ?([kmgtp])?i?b?$/i.exec(value.trim());
671
+ if (!match) return void 0;
672
+ const exponent = " kmgtp".indexOf((match[2] ?? " ").toLowerCase());
673
+ return Math.ceil(Number(match[1]) * 1024 ** exponent / 1024 ** 2);
674
+ };
675
+ var parseComposePort = (entry) => {
676
+ let port = entry;
677
+ if (isRecord(entry)) {
678
+ if (typeof entry.protocol === "string" && entry.protocol.toLowerCase() !== "tcp") return void 0;
679
+ port = entry.target;
680
+ } else if (typeof entry === "string") {
681
+ const [withoutProtocol, protocol] = entry.split("/");
682
+ if (protocol !== void 0 && protocol.toLowerCase() !== "tcp") return void 0;
683
+ port = (withoutProtocol.split(":").at(-1) ?? "").split("-")[0];
684
+ }
685
+ const number = typeof port === "number" ? port : typeof port === "string" && /^\d+$/.test(port) ? Number(port) : NaN;
686
+ return isIntegerBetween(number, 1, 65535) ? number : void 0;
687
+ };
688
+ var parseComposeEnvironment = (serviceName, value, warnings) => {
689
+ if (value === void 0 || value === null) return void 0;
690
+ const environment = {};
691
+ if (Array.isArray(value)) {
692
+ for (const item of value) {
693
+ if (typeof item !== "string") continue;
694
+ const separator = item.indexOf("=");
695
+ if (separator === -1) {
696
+ warnings.push(
697
+ `Compose service '${serviceName}' takes '${item}' from the host environment, which deployments do not have. Set it in the console instead.`
698
+ );
699
+ continue;
700
+ }
701
+ environment[item.slice(0, separator)] = item.slice(separator + 1);
702
+ }
703
+ } else if (isRecord(value)) {
704
+ for (const [key, item] of Object.entries(value)) {
705
+ const scalar = scalarToString(item);
706
+ if (scalar === void 0) {
707
+ warnings.push(
708
+ `Compose service '${serviceName}' takes '${key}' from the host environment, which deployments do not have. Set it in the console instead.`
709
+ );
710
+ continue;
711
+ }
712
+ environment[key] = scalar;
713
+ }
714
+ }
715
+ return environment;
716
+ };
717
+ var isPublicMarked = (service) => gigadriveExtension(service).public === true;
718
+ var buildsFromSource = (service) => service.build !== void 0 && gigadriveExtension(service).sidecar !== true;
719
+ var loadComposeServices = import_effect5.Effect.fn("loadComposeServices")(function* (composePath, projectFolder) {
720
+ const relativePath = normalizeProjectPath(composePath);
721
+ if (relativePath === void 0) {
722
+ return yield* new ContainerConfigError({
723
+ message: `Compose file '${composePath}' is outside the project.`,
724
+ filePath: composePath
725
+ });
726
+ }
727
+ const projectRoot = yield* resolveProjectRoot(projectFolder);
728
+ const content = yield* readInsideProject(projectRoot, relativePath, `Compose file '${relativePath}'`, {
729
+ filePath: relativePath
730
+ });
731
+ if (import_effect5.Option.isNone(content)) {
732
+ return yield* new ContainerConfigError({
733
+ message: `Compose file '${relativePath}' could not be read.`,
734
+ filePath: relativePath
735
+ });
736
+ }
737
+ const document = yield* import_effect5.Effect.try({
738
+ try: () => (0, import_yaml.parse)(content.value),
739
+ catch: (error) => {
740
+ const position = error instanceof import_yaml.YAMLError ? error.linePos?.[0] : void 0;
741
+ return new ContainerConfigError({
742
+ message: `Compose file '${relativePath}' is not valid YAML${position ? ` (line ${position.line}, column ${position.col})` : ""}.`,
743
+ filePath: relativePath
744
+ });
745
+ }
746
+ });
747
+ if (!isRecord(document) || !isRecord(document.services) || Object.keys(document.services).length === 0) {
748
+ return yield* new ContainerConfigError({
749
+ message: `Compose file '${relativePath}' declares no services.`,
750
+ filePath: relativePath
751
+ });
752
+ }
753
+ const services = Object.entries(document.services).filter(
754
+ (entry) => isRecord(entry[1]) && !(isStringArray(entry[1].profiles) && entry[1].profiles.length > 0) && gigadriveExtension(entry[1]).skip !== true
755
+ );
756
+ return { relativePath, projectRoot, services };
757
+ });
758
+ var composeDeclaresApp = import_effect5.Effect.fn("composeDeclaresApp")(function* (composePath, projectFolder) {
759
+ const { services } = yield* loadComposeServices(composePath, projectFolder);
760
+ return services.some(([, service]) => isPublicMarked(service) || buildsFromSource(service));
761
+ });
762
+ var readComposeContainers = import_effect5.Effect.fn("readComposeContainers")(function* (composePath, projectFolder) {
763
+ const fs = yield* import_platform2.FileSystem.FileSystem;
764
+ const pathSvc = yield* import_platform2.Path.Path;
765
+ const { relativePath, projectRoot, services } = yield* loadComposeServices(composePath, projectFolder);
766
+ const composeDirectory = import_node_path.posix.dirname(relativePath);
767
+ const warnings = [];
768
+ for (const [name] of services) {
769
+ if (!CONTAINER_NAME_PATTERN.test(name) || name === "localhost") {
770
+ return yield* new ContainerConfigError({
771
+ message: `Compose service '${name}' cannot be imported under that name. The service name becomes the hostname the function reaches it by, so it must be ${CONTAINER_NAME_RULES}. Rename the service to '${toContainerName(name)}' in '${relativePath}'.`,
772
+ containerName: name,
773
+ filePath: relativePath
774
+ });
775
+ }
776
+ }
777
+ const marked = services.filter(([, service]) => isPublicMarked(service)).map(([name]) => name);
778
+ const built = services.filter(([, service]) => buildsFromSource(service)).map(([name]) => name);
779
+ if (marked.length > 1) {
780
+ return yield* new ContainerConfigError({
781
+ message: `Compose file '${relativePath}' marks ${marked.map((name) => `'${name}'`).join(", ")} as public. One service serves HTTP: keep 'x-gigadrive: { public: true }' on that one only.`,
782
+ filePath: relativePath
783
+ });
784
+ }
785
+ const publicService = marked[0] ?? (built.length === 1 ? built[0] : void 0);
786
+ if (publicService === void 0) {
787
+ return yield* new ContainerConfigError({
788
+ message: built.length === 0 ? `Compose file '${relativePath}' has no service that builds from source, so it is unclear which service serves HTTP. Mark it with 'x-gigadrive: { public: true }'.` : `Compose file '${relativePath}' builds ${built.map((name) => `'${name}'`).join(", ")} from source, so it is unclear which service serves HTTP. Mark it with 'x-gigadrive: { public: true }'.`,
789
+ filePath: relativePath
790
+ });
791
+ }
792
+ if (COMPOSE_FILE_NAMES.includes(import_node_path.posix.basename(relativePath))) {
793
+ for (const overrideName of COMPOSE_OVERRIDE_FILE_NAMES) {
794
+ const overridePath = import_node_path.posix.join(composeDirectory, overrideName);
795
+ const overrideExists = yield* fs.exists(pathSvc.join(projectRoot, overridePath)).pipe(import_effect5.Effect.orElseSucceed(() => false));
796
+ if (overrideExists) {
797
+ warnings.push(
798
+ `Compose file '${relativePath}' has the override file '${overridePath}' next to it, which is not merged. Move its settings into '${relativePath}' or gigadrive.yaml.`
799
+ );
800
+ }
801
+ }
802
+ }
803
+ const dotEnv = yield* readInsideProject(
804
+ projectRoot,
805
+ import_node_path.posix.join(composeDirectory, ".env"),
806
+ `The .env file next to '${relativePath}'`,
807
+ { filePath: relativePath }
808
+ );
809
+ const variables = import_effect5.Option.isSome(dotEnv) ? parseDotEnv(dotEnv.value) : {};
810
+ const missing = /* @__PURE__ */ new Set();
811
+ const containers = {};
812
+ for (const [name, rawService] of services) {
813
+ const isPublic = name === publicService;
814
+ const service = yield* import_effect5.Effect.try({
815
+ try: () => interpolateDeep(rawService, variables, missing, ""),
816
+ catch: (error) => new ContainerConfigError({
817
+ message: `Compose service '${name}' ${error instanceof InterpolationError ? error.message : "could not be interpolated."}`,
818
+ containerName: name,
819
+ filePath: relativePath
820
+ })
821
+ });
822
+ const container = {};
823
+ if (service.extends !== void 0) {
824
+ warnings.push(
825
+ `Compose service '${name}' uses 'extends', which is not supported. The service is deployed as written, without the settings it would inherit.`
826
+ );
827
+ }
828
+ if (typeof service.image === "string" && service.build === void 0) {
829
+ container.image = service.image;
830
+ } else if (typeof service.build === "string" || isRecord(service.build)) {
831
+ const build = typeof service.build === "string" ? { context: service.build } : service.build;
832
+ if (build.dockerfile_inline !== void 0) {
833
+ return yield* new ContainerConfigError({
834
+ message: `Compose service '${name}' uses build.dockerfile_inline, which is not supported. Commit the Dockerfile and point build.dockerfile at it.`,
835
+ containerName: name,
836
+ filePath: relativePath
837
+ });
838
+ }
839
+ const rawContext = typeof build.context === "string" ? build.context : ".";
840
+ const context = import_node_path.posix.isAbsolute(rawContext) ? rawContext : import_node_path.posix.join(composeDirectory, rawContext);
841
+ const args = Array.isArray(build.args) ? Object.fromEntries(
842
+ build.args.filter((item) => typeof item === "string" && item.includes("=")).map((item) => [item.slice(0, item.indexOf("=")), item.slice(item.indexOf("=") + 1)])
843
+ ) : isRecord(build.args) ? Object.fromEntries(Object.entries(build.args).map(([key, item]) => [key, scalarToString(item) ?? ""])) : void 0;
844
+ container.build = {
845
+ context,
846
+ ...typeof build.dockerfile === "string" && { dockerfile: build.dockerfile },
847
+ ...typeof build.target === "string" && { target: build.target },
848
+ ...args !== void 0 && Object.keys(args).length > 0 && { args }
849
+ };
850
+ } else {
851
+ return yield* new ContainerConfigError({
852
+ message: `Compose service '${name}' has neither an image nor a build.`,
853
+ containerName: name,
854
+ filePath: relativePath
855
+ });
856
+ }
857
+ if (!isPublic) container.sidecar = true;
858
+ const ports = [
859
+ ...isPublic && Array.isArray(service.ports) ? service.ports : [],
860
+ ...Array.isArray(service.expose) ? service.expose : [],
861
+ ...!isPublic && Array.isArray(service.ports) ? service.ports : []
862
+ ];
863
+ const port = ports.map(parseComposePort).find((value) => value !== void 0);
864
+ if (port !== void 0) container.port = port;
865
+ for (const key of ["entrypoint", "command"]) {
866
+ const value = service[key];
867
+ if (typeof value === "string" || isStringArray(value)) container[key] = value;
868
+ }
869
+ const environment = {};
870
+ const envFiles = (typeof service.env_file === "string" ? [service.env_file] : Array.isArray(service.env_file) ? service.env_file : []).flatMap(
871
+ (entry) => typeof entry === "string" ? [{ path: entry }] : isRecord(entry) && typeof entry.path === "string" ? [{ path: entry.path, ...typeof entry.required === "boolean" && { required: entry.required } }] : []
872
+ );
873
+ for (const envFile of envFiles) {
874
+ const envFilePath = import_node_path.posix.isAbsolute(envFile.path) ? void 0 : normalizeProjectPath(import_node_path.posix.join(composeDirectory, envFile.path));
875
+ const envContent = envFilePath === void 0 ? import_effect5.Option.none() : yield* readInsideProject(
876
+ projectRoot,
877
+ envFilePath,
878
+ `The env_file '${envFile.path}' of Compose service '${name}'`,
879
+ { containerName: name, filePath: relativePath }
880
+ );
881
+ if (import_effect5.Option.isSome(envContent)) {
882
+ Object.assign(environment, parseDotEnv(envContent.value));
883
+ } else if (envFile.required === true) {
884
+ return yield* new ContainerConfigError({
885
+ message: `Compose service '${name}' requires env_file '${envFile.path}', which is not in the deployed source. Commit it, mark it 'required: false', or set those variables in the console.`,
886
+ containerName: name,
887
+ filePath: relativePath
888
+ });
889
+ } else if (envFile.required === void 0) {
890
+ warnings.push(
891
+ `Compose service '${name}' reads env_file '${envFile.path}', which is not in the deployed source. Set those variables in the console instead.`
892
+ );
893
+ }
894
+ }
895
+ Object.assign(environment, parseComposeEnvironment(name, service.environment, warnings));
896
+ if (Object.keys(environment).length > 0) container.env = environment;
897
+ if (typeof service.working_dir === "string") container.working_dir = service.working_dir;
898
+ if (typeof service.user === "string" || typeof service.user === "number") container.user = String(service.user);
899
+ const deploy = isRecord(service.deploy) ? service.deploy : {};
900
+ const resources = isRecord(deploy.resources) ? deploy.resources : {};
901
+ const limits = isRecord(resources.limits) ? resources.limits : {};
902
+ const memoryLimit = limits.memory ?? service.mem_limit;
903
+ if (memoryLimit !== void 0 && memoryLimit !== null) {
904
+ const memory = parseComposeMemory(memoryLimit);
905
+ if (memory === void 0) {
906
+ warnings.push(
907
+ `Compose service '${name}' sets the memory limit ${JSON.stringify(memoryLimit)}, which is not a size Docker accepts. The default is used instead.`
908
+ );
909
+ } else if (memory >= MIN_CONTAINER_MEMORY_MB && memory <= MAX_CONTAINER_MEMORY_MB) {
910
+ container.memory = memory;
911
+ } else if (memory > 0) {
912
+ warnings.push(
913
+ `Compose service '${name}' asks for ${memory} MB of memory, outside the supported ${MIN_CONTAINER_MEMORY_MB} to ${MAX_CONTAINER_MEMORY_MB} MB. The default is used instead.`
914
+ );
915
+ }
916
+ }
917
+ if (service.volumes !== void 0) {
918
+ warnings.push(
919
+ `Compose service '${name}' declares volumes. Containers on Gigadrive Network have no persistent volumes, so anything written there is lost when the instance stops.`
920
+ );
921
+ }
922
+ containers[name] = container;
923
+ }
924
+ if (missing.size > 0) {
925
+ warnings.push(
926
+ `Compose file '${relativePath}' uses ${[...missing].sort().join(", ")}, which ${missing.size === 1 ? "is" : "are"} not set. ${missing.size === 1 ? "It was" : "They were"} replaced with an empty string.`
927
+ );
928
+ }
929
+ return { containers, publicService, warnings };
930
+ });
931
+
145
932
  // src/define-config.ts
146
933
  var defineConfig = (config) => config;
147
934
 
935
+ // src/detection/detect-container-project.ts
936
+ var import_platform3 = require("@effect/platform");
937
+ var import_effect6 = require("effect");
938
+
939
+ // src/regions.ts
940
+ var REGION_DEFINITIONS = [
941
+ "us-east-1",
942
+ "us-east-2",
943
+ "us-west-1",
944
+ "us-west-2",
945
+ "ap-south-1",
946
+ "ap-northeast-3",
947
+ "ap-northeast-2",
948
+ "ap-southeast-1",
949
+ "ap-southeast-2",
950
+ "ap-northeast-1",
951
+ "ca-central-1",
952
+ "eu-central-1",
953
+ "eu-west-1",
954
+ "eu-west-2",
955
+ "eu-west-3",
956
+ "eu-north-1",
957
+ "sa-east-1"
958
+ ];
959
+ var AVAILABLE_REGIONS = [...REGION_DEFINITIONS];
960
+
961
+ // src/detection/detect-container-project.ts
962
+ var detectContainerProject = import_effect6.Effect.fn("detectContainerProject")(function* (projectFolder) {
963
+ const fs = yield* import_platform3.FileSystem.FileSystem;
964
+ const pathSvc = yield* import_platform3.Path.Path;
965
+ const exists = (file) => fs.exists(pathSvc.join(projectFolder, file)).pipe(import_effect6.Effect.catchAll(() => import_effect6.Effect.succeed(false)));
966
+ let detection;
967
+ const warnings = [];
968
+ const composeFile = yield* import_effect6.Effect.findFirst(COMPOSE_FILE_NAMES, exists);
969
+ if (composeFile._tag === "Some" && (yield* composeDeclaresApp(composeFile.value, projectFolder))) {
970
+ const imported = yield* readComposeContainers(composeFile.value, projectFolder);
971
+ warnings.push(...imported.warnings);
972
+ detection = { kind: "compose", file: composeFile.value, containers: imported.containers };
973
+ } else if (yield* exists("Dockerfile")) {
974
+ detection = { kind: "dockerfile", file: "Dockerfile", containers: { app: { build: "." } } };
975
+ } else {
976
+ return void 0;
977
+ }
978
+ const {
979
+ entrypoints,
980
+ sidecars,
981
+ warnings: containerWarnings
982
+ } = yield* normalizeContainers(detection.containers, projectFolder);
983
+ const [publicContainer] = entrypoints;
984
+ const config = {
985
+ regions: [...AVAILABLE_REGIONS],
986
+ environmentVariables: {},
987
+ commands: [],
988
+ entrypoints,
989
+ ...sidecars.length > 0 && { sidecars },
990
+ routes: publicContainer ? [
991
+ {
992
+ path: "/*",
993
+ destination: publicContainer.path,
994
+ handler: "SERVERLESS_FUNCTION_STREAMING",
995
+ methods: ["ANY"],
996
+ headers: {}
997
+ }
998
+ ] : [],
999
+ warnings: [
1000
+ `Auto-detected ${detection.kind === "compose" ? "Docker Compose" : "Dockerfile"} project (${detection.file}). Create a gigadrive.yaml to customize.`,
1001
+ ...warnings,
1002
+ ...containerWarnings
1003
+ ],
1004
+ errors: []
1005
+ };
1006
+ return { kind: detection.kind, file: detection.file, config };
1007
+ });
1008
+
148
1009
  // src/detection/detect-framework.ts
149
- var import_platform8 = require("@effect/platform");
150
- var import_effect13 = require("effect");
1010
+ var import_platform11 = require("@effect/platform");
1011
+ var import_effect17 = require("effect");
151
1012
 
152
1013
  // src/detection/detect-package-manager.ts
153
- var import_platform = require("@effect/platform");
154
- var import_effect3 = require("effect");
1014
+ var import_platform4 = require("@effect/platform");
1015
+ var import_effect7 = require("effect");
155
1016
  var LOCKFILE_MAP = [
156
1017
  { files: ["bun.lockb", "bun.lock"], pm: "bun" },
157
1018
  { files: ["pnpm-lock.yaml"], pm: "pnpm" },
@@ -159,19 +1020,19 @@ var LOCKFILE_MAP = [
159
1020
  { files: ["package-lock.json"], pm: "npm" },
160
1021
  { files: ["composer.lock"], pm: "composer" }
161
1022
  ];
162
- var detectPackageManager = import_effect3.Effect.fn("detectPackageManager")(function* (projectFolder) {
163
- const fs = yield* import_platform.FileSystem.FileSystem;
164
- const pathService = yield* import_platform.Path.Path;
1023
+ var detectPackageManager = import_effect7.Effect.fn("detectPackageManager")(function* (projectFolder) {
1024
+ const fs = yield* import_platform4.FileSystem.FileSystem;
1025
+ const pathService = yield* import_platform4.Path.Path;
165
1026
  for (const { files, pm } of LOCKFILE_MAP) {
166
1027
  for (const file of files) {
167
- const exists = yield* fs.exists(pathService.join(projectFolder, file)).pipe(import_effect3.Effect.catchAll(() => import_effect3.Effect.succeed(false)));
1028
+ const exists = yield* fs.exists(pathService.join(projectFolder, file)).pipe(import_effect7.Effect.catchAll(() => import_effect7.Effect.succeed(false)));
168
1029
  if (exists) {
169
- yield* import_effect3.Effect.logDebug(`Detected package manager: ${pm} (via ${file})`);
1030
+ yield* import_effect7.Effect.logDebug(`Detected package manager: ${pm} (via ${file})`);
170
1031
  return pm;
171
1032
  }
172
1033
  }
173
1034
  }
174
- yield* import_effect3.Effect.logDebug("No lockfile found, defaulting to npm");
1035
+ yield* import_effect7.Effect.logDebug("No lockfile found, defaulting to npm");
175
1036
  return "npm";
176
1037
  });
177
1038
 
@@ -197,8 +1058,8 @@ var astro = {
197
1058
  };
198
1059
 
199
1060
  // src/detection/frameworks/resolve-node-entrypoint.ts
200
- var import_platform2 = require("@effect/platform");
201
- var import_effect4 = require("effect");
1061
+ var import_platform5 = require("@effect/platform");
1062
+ var import_effect8 = require("effect");
202
1063
  var SCRIPT_RUNNERS = /* @__PURE__ */ new Set(["node", "bun", "tsx", "ts-node"]);
203
1064
  var COMMON_ENTRYPOINT_CANDIDATES = [
204
1065
  "index.js",
@@ -214,11 +1075,11 @@ var COMMON_ENTRYPOINT_CANDIDATES = [
214
1075
  "server.ts",
215
1076
  "src/server.ts"
216
1077
  ];
217
- var isRecord = (value) => typeof value === "object" && value !== null;
1078
+ var isRecord2 = (value) => typeof value === "object" && value !== null;
218
1079
  var parsePackageJson = (content) => {
219
1080
  try {
220
1081
  const parsed = JSON.parse(content);
221
- if (!isRecord(parsed)) return void 0;
1082
+ if (!isRecord2(parsed)) return void 0;
222
1083
  return parsed;
223
1084
  } catch {
224
1085
  return void 0;
@@ -246,14 +1107,14 @@ var applyEntrypoint = (defaults, entrypoint) => ({
246
1107
  (route) => route.destination === defaults.entrypoint ? { ...route, destination: entrypoint } : route
247
1108
  )
248
1109
  });
249
- var resolveNodeServerEntrypoint = (defaults, projectFolder) => import_effect4.Effect.gen(function* () {
250
- const fs = yield* import_platform2.FileSystem.FileSystem;
251
- const pathService = yield* import_platform2.Path.Path;
1110
+ var resolveNodeServerEntrypoint = (defaults, projectFolder) => import_effect8.Effect.gen(function* () {
1111
+ const fs = yield* import_platform5.FileSystem.FileSystem;
1112
+ const pathService = yield* import_platform5.Path.Path;
252
1113
  const isFile = (relativePath) => fs.stat(pathService.join(projectFolder, relativePath)).pipe(
253
- import_effect4.Effect.map((info) => info.type === "File"),
254
- import_effect4.Effect.catchAll(() => import_effect4.Effect.succeed(false))
1114
+ import_effect8.Effect.map((info) => info.type === "File"),
1115
+ import_effect8.Effect.catchAll(() => import_effect8.Effect.succeed(false))
255
1116
  );
256
- const content = yield* fs.readFileString(pathService.join(projectFolder, "package.json")).pipe(import_effect4.Effect.catchAll(() => import_effect4.Effect.succeed(void 0)));
1117
+ const content = yield* fs.readFileString(pathService.join(projectFolder, "package.json")).pipe(import_effect8.Effect.catchAll(() => import_effect8.Effect.succeed(void 0)));
257
1118
  const manifest = content ? parsePackageJson(content) : void 0;
258
1119
  const startScript = typeof manifest?.scripts?.start === "string" ? manifest.scripts.start : void 0;
259
1120
  const candidates = [
@@ -385,13 +1246,13 @@ var laravel = {
385
1246
  };
386
1247
 
387
1248
  // src/detection/frameworks/nestjs.ts
388
- var import_platform3 = require("@effect/platform");
389
- var import_effect5 = require("effect");
390
- var isRecord2 = (value) => typeof value === "object" && value !== null;
1249
+ var import_platform6 = require("@effect/platform");
1250
+ var import_effect9 = require("effect");
1251
+ var isRecord3 = (value) => typeof value === "object" && value !== null;
391
1252
  var parseNestCliJson = (content) => {
392
1253
  try {
393
1254
  const parsed = JSON.parse(content);
394
- if (!isRecord2(parsed)) return void 0;
1255
+ if (!isRecord3(parsed)) return void 0;
395
1256
  return parsed;
396
1257
  } catch {
397
1258
  return void 0;
@@ -437,9 +1298,9 @@ var nestjs = {
437
1298
  detectors: [{ matchPackage: "@nestjs/core" }],
438
1299
  priority: 90,
439
1300
  getDefaultConfig: () => createNestConfig("dist"),
440
- refineDefaultConfig: (defaults, projectFolder) => import_effect5.Effect.gen(function* () {
441
- const fs = yield* import_platform3.FileSystem.FileSystem;
442
- const content = yield* fs.readFileString(`${projectFolder}/nest-cli.json`).pipe(import_effect5.Effect.catchAll(() => import_effect5.Effect.succeed(void 0)));
1301
+ refineDefaultConfig: (defaults, projectFolder) => import_effect9.Effect.gen(function* () {
1302
+ const fs = yield* import_platform6.FileSystem.FileSystem;
1303
+ const content = yield* fs.readFileString(`${projectFolder}/nest-cli.json`).pipe(import_effect9.Effect.catchAll(() => import_effect9.Effect.succeed(void 0)));
443
1304
  if (!content) return defaults;
444
1305
  const nestConfig = parseNestCliJson(content);
445
1306
  const outputPath = nestConfig ? getProjectOutputPath(nestConfig) : void 0;
@@ -448,28 +1309,28 @@ var nestjs = {
448
1309
  };
449
1310
 
450
1311
  // src/detection/frameworks/nextjs.ts
451
- var import_platform5 = require("@effect/platform");
452
- var import_effect8 = require("effect");
1312
+ var import_platform8 = require("@effect/platform");
1313
+ var import_effect12 = require("effect");
453
1314
 
454
1315
  // src/build-output-v3/get-default-path-map.ts
455
- var import_platform4 = require("@effect/platform");
456
- var import_effect6 = require("effect");
457
- var getDefaultPathMap = import_effect6.Effect.fn("getDefaultPathMap")(function* (directory) {
458
- const fs = yield* import_platform4.FileSystem.FileSystem;
459
- const pathService = yield* import_platform4.Path.Path;
1316
+ var import_platform7 = require("@effect/platform");
1317
+ var import_effect10 = require("effect");
1318
+ var getDefaultPathMap = import_effect10.Effect.fn("getDefaultPathMap")(function* (directory) {
1319
+ const fs = yield* import_platform7.FileSystem.FileSystem;
1320
+ const pathService = yield* import_platform7.Path.Path;
460
1321
  const result = {};
461
- const exists = yield* fs.exists(directory).pipe(import_effect6.Effect.catchAll(() => import_effect6.Effect.succeed(false)));
1322
+ const exists = yield* fs.exists(directory).pipe(import_effect10.Effect.catchAll(() => import_effect10.Effect.succeed(false)));
462
1323
  if (!exists) {
463
1324
  return result;
464
1325
  }
465
1326
  const MAX_DEPTH = 100;
466
- const processDirectory = (currentPath, basePath = "", depth = 0) => import_effect6.Effect.gen(function* () {
1327
+ const processDirectory = (currentPath, basePath = "", depth = 0) => import_effect10.Effect.gen(function* () {
467
1328
  if (depth > MAX_DEPTH) return;
468
- const entries = yield* fs.readDirectory(currentPath).pipe(import_effect6.Effect.catchAll(() => import_effect6.Effect.succeed([])));
1329
+ const entries = yield* fs.readDirectory(currentPath).pipe(import_effect10.Effect.catchAll(() => import_effect10.Effect.succeed([])));
469
1330
  for (const name of entries) {
470
1331
  const fullPath = pathService.join(currentPath, name);
471
1332
  const relativePath = basePath ? pathService.join(basePath, name) : name;
472
- const stat = yield* fs.stat(fullPath).pipe(import_effect6.Effect.catchAll(() => import_effect6.Effect.succeed(null)));
1333
+ const stat = yield* fs.stat(fullPath).pipe(import_effect10.Effect.catchAll(() => import_effect10.Effect.succeed(null)));
473
1334
  if (!stat) continue;
474
1335
  if (stat.type === "Directory") {
475
1336
  yield* processDirectory(fullPath, relativePath, depth + 1);
@@ -486,291 +1347,269 @@ var getDefaultPathMap = import_effect6.Effect.fn("getDefaultPathMap")(function*
486
1347
  var MAXIMUM_ENTRY_PAGE_PATHS = 50;
487
1348
 
488
1349
  // src/nextjs-manifest.ts
489
- var import_effect7 = require("effect");
490
- var StringArraySchema = import_effect7.Schema.mutable(import_effect7.Schema.Array(import_effect7.Schema.String));
491
- var StringRecordSchema = import_effect7.Schema.mutable(import_effect7.Schema.Record({ key: import_effect7.Schema.String, value: import_effect7.Schema.String }));
492
- var JsonValueSchema = import_effect7.Schema.suspend(
493
- () => import_effect7.Schema.Union(
494
- import_effect7.Schema.Null,
495
- import_effect7.Schema.Boolean,
496
- import_effect7.Schema.Number,
497
- import_effect7.Schema.String,
498
- import_effect7.Schema.mutable(import_effect7.Schema.Array(JsonValueSchema)),
499
- import_effect7.Schema.mutable(import_effect7.Schema.Record({ key: import_effect7.Schema.String, value: JsonValueSchema }))
1350
+ var import_effect11 = require("effect");
1351
+ var StringArraySchema = import_effect11.Schema.mutable(import_effect11.Schema.Array(import_effect11.Schema.String));
1352
+ var StringRecordSchema = import_effect11.Schema.mutable(import_effect11.Schema.Record({ key: import_effect11.Schema.String, value: import_effect11.Schema.String }));
1353
+ var JsonValueSchema = import_effect11.Schema.suspend(
1354
+ () => import_effect11.Schema.Union(
1355
+ import_effect11.Schema.Null,
1356
+ import_effect11.Schema.Boolean,
1357
+ import_effect11.Schema.Number,
1358
+ import_effect11.Schema.String,
1359
+ import_effect11.Schema.mutable(import_effect11.Schema.Array(JsonValueSchema)),
1360
+ import_effect11.Schema.mutable(import_effect11.Schema.Record({ key: import_effect11.Schema.String, value: JsonValueSchema }))
500
1361
  )
501
1362
  );
502
- var ImageLocalPatternSchema = import_effect7.Schema.mutable(
503
- import_effect7.Schema.Struct({
504
- pathname: import_effect7.Schema.optional(import_effect7.Schema.String),
505
- search: import_effect7.Schema.optional(import_effect7.Schema.String)
1363
+ var ImageLocalPatternSchema = import_effect11.Schema.mutable(
1364
+ import_effect11.Schema.Struct({
1365
+ pathname: import_effect11.Schema.optional(import_effect11.Schema.String),
1366
+ search: import_effect11.Schema.optional(import_effect11.Schema.String)
506
1367
  })
507
1368
  );
508
- var ImageRemotePatternSchema = import_effect7.Schema.mutable(
509
- import_effect7.Schema.Struct({
510
- protocol: import_effect7.Schema.optional(import_effect7.Schema.Literal("http", "https")),
511
- hostname: import_effect7.Schema.String,
512
- port: import_effect7.Schema.optional(import_effect7.Schema.String),
513
- pathname: import_effect7.Schema.optional(import_effect7.Schema.String),
514
- search: import_effect7.Schema.optional(import_effect7.Schema.String)
1369
+ var ImageRemotePatternSchema = import_effect11.Schema.mutable(
1370
+ import_effect11.Schema.Struct({
1371
+ protocol: import_effect11.Schema.optional(import_effect11.Schema.Literal("http", "https")),
1372
+ hostname: import_effect11.Schema.String,
1373
+ port: import_effect11.Schema.optional(import_effect11.Schema.String),
1374
+ pathname: import_effect11.Schema.optional(import_effect11.Schema.String),
1375
+ search: import_effect11.Schema.optional(import_effect11.Schema.String)
515
1376
  })
516
1377
  );
517
- var ImageVariantSchema = import_effect7.Schema.mutable(
518
- import_effect7.Schema.Struct({
519
- width: import_effect7.Schema.optional(import_effect7.Schema.Number),
520
- height: import_effect7.Schema.optional(import_effect7.Schema.Number),
521
- quality: import_effect7.Schema.optional(import_effect7.Schema.Number),
522
- format: import_effect7.Schema.optional(import_effect7.Schema.Literal("image/avif", "image/webp", "image/jpeg", "image/png")),
523
- fit: import_effect7.Schema.optional(import_effect7.Schema.Literal("contain", "cover", "fill", "inside", "outside"))
1378
+ var ImageVariantSchema = import_effect11.Schema.mutable(
1379
+ import_effect11.Schema.Struct({
1380
+ width: import_effect11.Schema.optional(import_effect11.Schema.Number),
1381
+ height: import_effect11.Schema.optional(import_effect11.Schema.Number),
1382
+ quality: import_effect11.Schema.optional(import_effect11.Schema.Number),
1383
+ format: import_effect11.Schema.optional(import_effect11.Schema.Literal("image/avif", "image/webp", "image/jpeg", "image/png")),
1384
+ fit: import_effect11.Schema.optional(import_effect11.Schema.Literal("contain", "cover", "fill", "inside", "outside"))
524
1385
  })
525
1386
  );
526
- var PositiveIntSchema = import_effect7.Schema.Int.pipe(import_effect7.Schema.greaterThan(0));
527
- var ImagePolicySchema = import_effect7.Schema.mutable(
528
- import_effect7.Schema.Struct({
529
- localPatterns: import_effect7.Schema.mutable(import_effect7.Schema.Array(ImageLocalPatternSchema)),
530
- remotePatterns: import_effect7.Schema.mutable(import_effect7.Schema.Array(ImageRemotePatternSchema)),
531
- widths: import_effect7.Schema.mutable(import_effect7.Schema.Array(PositiveIntSchema)),
532
- heights: import_effect7.Schema.mutable(import_effect7.Schema.Array(PositiveIntSchema)),
533
- qualities: import_effect7.Schema.mutable(import_effect7.Schema.Array(import_effect7.Schema.Number.pipe(import_effect7.Schema.between(1, 100)))),
534
- formats: import_effect7.Schema.mutable(import_effect7.Schema.Array(import_effect7.Schema.Literal("image/avif", "image/webp", "image/jpeg", "image/png"))),
535
- minimumCacheTTL: import_effect7.Schema.Number,
536
- dangerouslyAllowSVG: import_effect7.Schema.Boolean,
537
- contentSecurityPolicy: import_effect7.Schema.String,
538
- contentDispositionType: import_effect7.Schema.Literal("inline", "attachment"),
539
- maximumRedirects: import_effect7.Schema.Number,
540
- maximumResponseBody: import_effect7.Schema.Number,
541
- variants: import_effect7.Schema.mutable(import_effect7.Schema.Record({ key: import_effect7.Schema.String, value: ImageVariantSchema }))
1387
+ var PositiveIntSchema = import_effect11.Schema.Int.pipe(import_effect11.Schema.greaterThan(0));
1388
+ var ImagePolicySchema = import_effect11.Schema.mutable(
1389
+ import_effect11.Schema.Struct({
1390
+ localPatterns: import_effect11.Schema.mutable(import_effect11.Schema.Array(ImageLocalPatternSchema)),
1391
+ remotePatterns: import_effect11.Schema.mutable(import_effect11.Schema.Array(ImageRemotePatternSchema)),
1392
+ widths: import_effect11.Schema.mutable(import_effect11.Schema.Array(PositiveIntSchema)),
1393
+ heights: import_effect11.Schema.mutable(import_effect11.Schema.Array(PositiveIntSchema)),
1394
+ qualities: import_effect11.Schema.mutable(import_effect11.Schema.Array(import_effect11.Schema.Number.pipe(import_effect11.Schema.between(1, 100)))),
1395
+ formats: import_effect11.Schema.mutable(import_effect11.Schema.Array(import_effect11.Schema.Literal("image/avif", "image/webp", "image/jpeg", "image/png"))),
1396
+ minimumCacheTTL: import_effect11.Schema.Number,
1397
+ dangerouslyAllowSVG: import_effect11.Schema.Boolean,
1398
+ contentSecurityPolicy: import_effect11.Schema.String,
1399
+ contentDispositionType: import_effect11.Schema.Literal("inline", "attachment"),
1400
+ maximumRedirects: import_effect11.Schema.Number,
1401
+ maximumResponseBody: import_effect11.Schema.Number,
1402
+ variants: import_effect11.Schema.mutable(import_effect11.Schema.Record({ key: import_effect11.Schema.String, value: ImageVariantSchema }))
542
1403
  })
543
1404
  );
544
1405
  var GigadriveNextPrerenderOutputFields = {
545
- id: import_effect7.Schema.String,
546
- type: import_effect7.Schema.Literal("PRERENDER"),
1406
+ id: import_effect11.Schema.String,
1407
+ type: import_effect11.Schema.Literal("PRERENDER"),
547
1408
  pathname: UrlPathnameSchema,
548
- route: import_effect7.Schema.optional(UrlPathnameSchema),
549
- parentOutputId: import_effect7.Schema.String,
550
- groupId: import_effect7.Schema.Number,
551
- pprChain: import_effect7.Schema.optional(import_effect7.Schema.mutable(import_effect7.Schema.Struct({ headers: HttpSingleValueHeadersSchema }))),
552
- parentFallbackMode: import_effect7.Schema.optional(JsonValueSchema),
553
- fallback: import_effect7.Schema.optional(
554
- import_effect7.Schema.mutable(
555
- import_effect7.Schema.Struct({
556
- filePath: import_effect7.Schema.optional(PortableRelativePathSchema),
557
- initialStatus: import_effect7.Schema.optional(import_effect7.Schema.Int.pipe(import_effect7.Schema.between(100, 599))),
558
- initialHeaders: import_effect7.Schema.optional(HttpHeadersSchema),
559
- initialExpiration: import_effect7.Schema.optional(import_effect7.Schema.Number),
560
- initialRevalidate: import_effect7.Schema.optional(import_effect7.Schema.Union(import_effect7.Schema.Number, import_effect7.Schema.Literal(false))),
561
- postponedState: import_effect7.Schema.optional(import_effect7.Schema.String)
1409
+ route: import_effect11.Schema.optional(UrlPathnameSchema),
1410
+ parentOutputId: import_effect11.Schema.String,
1411
+ groupId: import_effect11.Schema.Number,
1412
+ pprChain: import_effect11.Schema.optional(import_effect11.Schema.mutable(import_effect11.Schema.Struct({ headers: HttpSingleValueHeadersSchema }))),
1413
+ parentFallbackMode: import_effect11.Schema.optional(JsonValueSchema),
1414
+ fallback: import_effect11.Schema.optional(
1415
+ import_effect11.Schema.mutable(
1416
+ import_effect11.Schema.Struct({
1417
+ filePath: import_effect11.Schema.optional(PortableRelativePathSchema),
1418
+ initialStatus: import_effect11.Schema.optional(import_effect11.Schema.Int.pipe(import_effect11.Schema.between(100, 599))),
1419
+ initialHeaders: import_effect11.Schema.optional(HttpHeadersSchema),
1420
+ initialExpiration: import_effect11.Schema.optional(import_effect11.Schema.Number),
1421
+ initialRevalidate: import_effect11.Schema.optional(import_effect11.Schema.Union(import_effect11.Schema.Number, import_effect11.Schema.Literal(false))),
1422
+ postponedState: import_effect11.Schema.optional(import_effect11.Schema.String)
562
1423
  })
563
1424
  )
564
1425
  ),
565
- config: import_effect7.Schema.mutable(
566
- import_effect7.Schema.Struct({
567
- allowQuery: import_effect7.Schema.optional(StringArraySchema),
568
- allowHeader: import_effect7.Schema.optional(StringArraySchema),
569
- bypassFor: import_effect7.Schema.optional(import_effect7.Schema.mutable(import_effect7.Schema.Array(JsonValueSchema))),
570
- renderingMode: import_effect7.Schema.optional(import_effect7.Schema.String),
571
- partialFallback: import_effect7.Schema.optional(import_effect7.Schema.Boolean),
572
- bypassToken: import_effect7.Schema.optional(import_effect7.Schema.String)
1426
+ config: import_effect11.Schema.mutable(
1427
+ import_effect11.Schema.Struct({
1428
+ allowQuery: import_effect11.Schema.optional(StringArraySchema),
1429
+ allowHeader: import_effect11.Schema.optional(StringArraySchema),
1430
+ bypassFor: import_effect11.Schema.optional(import_effect11.Schema.mutable(import_effect11.Schema.Array(JsonValueSchema))),
1431
+ renderingMode: import_effect11.Schema.optional(import_effect11.Schema.String),
1432
+ partialFallback: import_effect11.Schema.optional(import_effect11.Schema.Boolean),
1433
+ bypassToken: import_effect11.Schema.optional(import_effect11.Schema.String)
573
1434
  })
574
1435
  )
575
1436
  };
576
- var GigadriveNextPrerenderClassificationSchema = import_effect7.Schema.Struct({
577
- routeType: import_effect7.Schema.Literal("route", "fallback", "shell", "page"),
578
- response: import_effect7.Schema.Literal("empty", "initial", "complete"),
579
- compute: import_effect7.Schema.Literal("blocking", "resuming", "static"),
580
- htmlSize: import_effect7.Schema.optional(import_effect7.Schema.Number)
1437
+ var GigadriveNextPrerenderClassificationSchema = import_effect11.Schema.Struct({
1438
+ routeType: import_effect11.Schema.Literal("route", "fallback", "shell", "page"),
1439
+ response: import_effect11.Schema.Literal("empty", "initial", "complete"),
1440
+ compute: import_effect11.Schema.Literal("blocking", "resuming", "static"),
1441
+ htmlSize: import_effect11.Schema.optional(import_effect11.Schema.Number)
581
1442
  });
582
- var GigadriveNextPrerenderWithoutClassificationSchema = import_effect7.Schema.Struct({
583
- routeType: import_effect7.Schema.optional(import_effect7.Schema.Never),
584
- response: import_effect7.Schema.optional(import_effect7.Schema.Never),
585
- compute: import_effect7.Schema.optional(import_effect7.Schema.Never),
586
- htmlSize: import_effect7.Schema.optional(import_effect7.Schema.Never)
1443
+ var GigadriveNextPrerenderWithoutClassificationSchema = import_effect11.Schema.Struct({
1444
+ routeType: import_effect11.Schema.optional(import_effect11.Schema.Never),
1445
+ response: import_effect11.Schema.optional(import_effect11.Schema.Never),
1446
+ compute: import_effect11.Schema.optional(import_effect11.Schema.Never),
1447
+ htmlSize: import_effect11.Schema.optional(import_effect11.Schema.Never)
587
1448
  });
588
- var GigadriveNextPrerenderOutputSchema = import_effect7.Schema.Union(
589
- import_effect7.Schema.mutable(
590
- import_effect7.Schema.Struct({ ...GigadriveNextPrerenderOutputFields, ...GigadriveNextPrerenderClassificationSchema.fields })
1449
+ var GigadriveNextPrerenderOutputSchema = import_effect11.Schema.Union(
1450
+ import_effect11.Schema.mutable(
1451
+ import_effect11.Schema.Struct({ ...GigadriveNextPrerenderOutputFields, ...GigadriveNextPrerenderClassificationSchema.fields })
591
1452
  ),
592
- import_effect7.Schema.mutable(
593
- import_effect7.Schema.Struct({
1453
+ import_effect11.Schema.mutable(
1454
+ import_effect11.Schema.Struct({
594
1455
  ...GigadriveNextPrerenderOutputFields,
595
1456
  ...GigadriveNextPrerenderWithoutClassificationSchema.fields
596
1457
  })
597
1458
  )
598
1459
  );
599
- var GigadriveNextStaticAssetPrefixSchema = import_effect7.Schema.mutable(
600
- import_effect7.Schema.Struct({
1460
+ var GigadriveNextStaticAssetPrefixSchema = import_effect11.Schema.mutable(
1461
+ import_effect11.Schema.Struct({
601
1462
  sourceDir: PortableRelativePathSchema,
602
1463
  urlPrefix: PortableRelativePathSchema,
603
- immutable: import_effect7.Schema.Boolean
1464
+ immutable: import_effect11.Schema.Boolean
604
1465
  })
605
1466
  );
606
- var GigadriveNextRouteConditionSchema = import_effect7.Schema.Union(
607
- import_effect7.Schema.mutable(
608
- import_effect7.Schema.Struct({
609
- type: import_effect7.Schema.Literal("header", "cookie", "query"),
610
- key: import_effect7.Schema.String,
611
- value: import_effect7.Schema.optional(import_effect7.Schema.String)
1467
+ var GigadriveNextRouteConditionSchema = import_effect11.Schema.Union(
1468
+ import_effect11.Schema.mutable(
1469
+ import_effect11.Schema.Struct({
1470
+ type: import_effect11.Schema.Literal("header", "cookie", "query"),
1471
+ key: import_effect11.Schema.String,
1472
+ value: import_effect11.Schema.optional(import_effect11.Schema.String)
612
1473
  })
613
1474
  ),
614
- import_effect7.Schema.mutable(
615
- import_effect7.Schema.Struct({
616
- type: import_effect7.Schema.Literal("host"),
617
- value: import_effect7.Schema.String
1475
+ import_effect11.Schema.mutable(
1476
+ import_effect11.Schema.Struct({
1477
+ type: import_effect11.Schema.Literal("host"),
1478
+ value: import_effect11.Schema.String
618
1479
  })
619
1480
  )
620
1481
  );
621
- var GigadriveNextMiddlewareMatcherSchema = import_effect7.Schema.mutable(
622
- import_effect7.Schema.Struct({
623
- source: import_effect7.Schema.String,
624
- sourceRegex: import_effect7.Schema.String,
625
- has: import_effect7.Schema.optional(import_effect7.Schema.mutable(import_effect7.Schema.Array(GigadriveNextRouteConditionSchema))),
626
- missing: import_effect7.Schema.optional(import_effect7.Schema.mutable(import_effect7.Schema.Array(GigadriveNextRouteConditionSchema)))
1482
+ var GigadriveNextMiddlewareMatcherSchema = import_effect11.Schema.mutable(
1483
+ import_effect11.Schema.Struct({
1484
+ source: import_effect11.Schema.String,
1485
+ sourceRegex: import_effect11.Schema.String,
1486
+ has: import_effect11.Schema.optional(import_effect11.Schema.mutable(import_effect11.Schema.Array(GigadriveNextRouteConditionSchema))),
1487
+ missing: import_effect11.Schema.optional(import_effect11.Schema.mutable(import_effect11.Schema.Array(GigadriveNextRouteConditionSchema)))
627
1488
  })
628
1489
  );
629
- var GigadriveNextMiddlewareDescriptorSchema = import_effect7.Schema.Union(
630
- import_effect7.Schema.mutable(
631
- import_effect7.Schema.Struct({
632
- present: import_effect7.Schema.Literal(true),
633
- matchers: import_effect7.Schema.mutable(import_effect7.Schema.Array(GigadriveNextMiddlewareMatcherSchema))
1490
+ var GigadriveNextMiddlewareDescriptorSchema = import_effect11.Schema.Union(
1491
+ import_effect11.Schema.mutable(
1492
+ import_effect11.Schema.Struct({
1493
+ present: import_effect11.Schema.Literal(true),
1494
+ matchers: import_effect11.Schema.mutable(import_effect11.Schema.Array(GigadriveNextMiddlewareMatcherSchema))
634
1495
  })
635
1496
  ),
636
- import_effect7.Schema.mutable(
637
- import_effect7.Schema.Struct({
638
- present: import_effect7.Schema.Literal(false),
639
- matchers: import_effect7.Schema.mutable(import_effect7.Schema.Tuple())
1497
+ import_effect11.Schema.mutable(
1498
+ import_effect11.Schema.Struct({
1499
+ present: import_effect11.Schema.Literal(false),
1500
+ matchers: import_effect11.Schema.mutable(import_effect11.Schema.Tuple())
640
1501
  })
641
1502
  )
642
1503
  );
643
- var GigadriveNextServerDescriptorSchema = import_effect7.Schema.mutable(
644
- import_effect7.Schema.Struct({
645
- maxDuration: import_effect7.Schema.optional(import_effect7.Schema.Number),
646
- preferredRegion: import_effect7.Schema.optional(import_effect7.Schema.Union(import_effect7.Schema.String, StringArraySchema)),
647
- env: import_effect7.Schema.optional(StringRecordSchema)
1504
+ var GigadriveNextServerDescriptorSchema = import_effect11.Schema.mutable(
1505
+ import_effect11.Schema.Struct({
1506
+ maxDuration: import_effect11.Schema.optional(import_effect11.Schema.Number),
1507
+ preferredRegion: import_effect11.Schema.optional(import_effect11.Schema.Union(import_effect11.Schema.String, StringArraySchema)),
1508
+ env: import_effect11.Schema.optional(StringRecordSchema)
648
1509
  })
649
1510
  );
650
- var PrerenderArraySchema = import_effect7.Schema.mutable(import_effect7.Schema.Array(GigadriveNextPrerenderOutputSchema));
651
- var StaticAssetArraySchema = import_effect7.Schema.mutable(import_effect7.Schema.Array(GigadriveNextStaticAssetPrefixSchema));
652
- var JsonValueArraySchema = import_effect7.Schema.mutable(import_effect7.Schema.Array(JsonValueSchema));
653
- var GigadriveNextPrerenderSeedMethodsSchema = import_effect7.Schema.mutable(
654
- import_effect7.Schema.Tuple(import_effect7.Schema.Literal("GET"), import_effect7.Schema.Literal("HEAD"))
1511
+ var PrerenderArraySchema = import_effect11.Schema.mutable(import_effect11.Schema.Array(GigadriveNextPrerenderOutputSchema));
1512
+ var StaticAssetArraySchema = import_effect11.Schema.mutable(import_effect11.Schema.Array(GigadriveNextStaticAssetPrefixSchema));
1513
+ var JsonValueArraySchema = import_effect11.Schema.mutable(import_effect11.Schema.Array(JsonValueSchema));
1514
+ var GigadriveNextPrerenderSeedMethodsSchema = import_effect11.Schema.mutable(
1515
+ import_effect11.Schema.Tuple(import_effect11.Schema.Literal("GET"), import_effect11.Schema.Literal("HEAD"))
655
1516
  );
656
- var GigadriveNextBuildManifestV1Schema = import_effect7.Schema.mutable(
657
- import_effect7.Schema.Struct({
658
- version: import_effect7.Schema.Literal(1),
659
- output: import_effect7.Schema.Literal("standalone", "export"),
1517
+ var GigadriveNextBuildManifestV1Schema = import_effect11.Schema.mutable(
1518
+ import_effect11.Schema.Struct({
1519
+ version: import_effect11.Schema.Literal(1),
1520
+ output: import_effect11.Schema.Literal("standalone", "export"),
660
1521
  distDir: PortableRelativePathSchema,
661
1522
  repoRootToProject: RepositoryRelativePathSchema,
662
- nextVersion: import_effect7.Schema.String,
663
- buildId: import_effect7.Schema.String
1523
+ nextVersion: import_effect11.Schema.String,
1524
+ buildId: import_effect11.Schema.String
664
1525
  })
665
1526
  );
666
- var GigadriveNextBuildManifestV2ExportSchema = import_effect7.Schema.mutable(
667
- import_effect7.Schema.Struct({
668
- version: import_effect7.Schema.Literal(2),
669
- mode: import_effect7.Schema.Literal("export"),
1527
+ var GigadriveNextBuildManifestV2ExportSchema = import_effect11.Schema.mutable(
1528
+ import_effect11.Schema.Struct({
1529
+ version: import_effect11.Schema.Literal(2),
1530
+ mode: import_effect11.Schema.Literal("export"),
670
1531
  distDir: PortableRelativePathSchema,
671
1532
  repoRootToProject: RepositoryRelativePathSchema,
672
- nextVersion: import_effect7.Schema.String,
673
- buildId: import_effect7.Schema.String
1533
+ nextVersion: import_effect11.Schema.String,
1534
+ buildId: import_effect11.Schema.String
674
1535
  })
675
1536
  );
676
- var GigadriveNextBuildManifestV2StandaloneSchema = import_effect7.Schema.mutable(
677
- import_effect7.Schema.Struct({
678
- version: import_effect7.Schema.Literal(2),
679
- mode: import_effect7.Schema.Literal("standalone-v2"),
1537
+ var GigadriveNextBuildManifestV2StandaloneSchema = import_effect11.Schema.mutable(
1538
+ import_effect11.Schema.Struct({
1539
+ version: import_effect11.Schema.Literal(2),
1540
+ mode: import_effect11.Schema.Literal("standalone-v2"),
680
1541
  distDir: PortableRelativePathSchema,
681
1542
  repoRootToProject: RepositoryRelativePathSchema,
682
- nextVersion: import_effect7.Schema.String,
683
- buildId: import_effect7.Schema.String,
1543
+ nextVersion: import_effect11.Schema.String,
1544
+ buildId: import_effect11.Schema.String,
684
1545
  server: GigadriveNextServerDescriptorSchema,
685
- config: import_effect7.Schema.mutable(
686
- import_effect7.Schema.Struct({
687
- basePath: import_effect7.Schema.String,
688
- trailingSlash: import_effect7.Schema.Boolean,
689
- cacheComponents: import_effect7.Schema.Boolean,
690
- i18n: import_effect7.Schema.optional(JsonValueSchema),
691
- images: import_effect7.Schema.optional(ImagePolicySchema)
1546
+ config: import_effect11.Schema.mutable(
1547
+ import_effect11.Schema.Struct({
1548
+ basePath: import_effect11.Schema.String,
1549
+ trailingSlash: import_effect11.Schema.Boolean,
1550
+ cacheComponents: import_effect11.Schema.Boolean,
1551
+ i18n: import_effect11.Schema.optional(JsonValueSchema),
1552
+ images: import_effect11.Schema.optional(ImagePolicySchema)
692
1553
  })
693
1554
  ),
694
- routing: import_effect7.Schema.mutable(
695
- import_effect7.Schema.Struct({
1555
+ routing: import_effect11.Schema.mutable(
1556
+ import_effect11.Schema.Struct({
696
1557
  beforeMiddleware: JsonValueArraySchema,
697
1558
  beforeFiles: JsonValueArraySchema,
698
1559
  afterFiles: JsonValueArraySchema,
699
1560
  dynamicRoutes: JsonValueArraySchema,
700
1561
  onMatch: JsonValueArraySchema,
701
1562
  fallback: JsonValueArraySchema,
702
- shouldNormalizeNextData: import_effect7.Schema.Boolean,
1563
+ shouldNormalizeNextData: import_effect11.Schema.Boolean,
703
1564
  rsc: JsonValueSchema
704
1565
  })
705
1566
  ),
706
- outputs: import_effect7.Schema.mutable(
707
- import_effect7.Schema.Struct({
1567
+ outputs: import_effect11.Schema.mutable(
1568
+ import_effect11.Schema.Struct({
708
1569
  prerenders: PrerenderArraySchema,
709
- prerenderManifest: import_effect7.Schema.optional(PortableRelativePathSchema),
710
- assetManifest: import_effect7.Schema.optional(PortableRelativePathSchema),
711
- entryPagePaths: import_effect7.Schema.optional(import_effect7.Schema.mutable(import_effect7.Schema.Array(UrlPathnameSchema))),
1570
+ prerenderManifest: import_effect11.Schema.optional(PortableRelativePathSchema),
1571
+ assetManifest: import_effect11.Schema.optional(PortableRelativePathSchema),
1572
+ entryPagePaths: import_effect11.Schema.optional(import_effect11.Schema.mutable(import_effect11.Schema.Array(UrlPathnameSchema))),
712
1573
  staticAssets: StaticAssetArraySchema,
713
- middleware: import_effect7.Schema.optional(GigadriveNextMiddlewareDescriptorSchema)
1574
+ middleware: import_effect11.Schema.optional(GigadriveNextMiddlewareDescriptorSchema)
714
1575
  })
715
1576
  )
716
1577
  })
717
1578
  );
718
- var GigadriveNextBuildManifestSchema = import_effect7.Schema.Union(
1579
+ var GigadriveNextBuildManifestSchema = import_effect11.Schema.Union(
719
1580
  GigadriveNextBuildManifestV1Schema,
720
1581
  GigadriveNextBuildManifestV2ExportSchema,
721
1582
  GigadriveNextBuildManifestV2StandaloneSchema
722
1583
  );
723
- var GigadriveNextPrerenderManifestV1Schema = import_effect7.Schema.mutable(
724
- import_effect7.Schema.Struct({
725
- version: import_effect7.Schema.Literal(1),
726
- seedMethods: import_effect7.Schema.optional(GigadriveNextPrerenderSeedMethodsSchema),
1584
+ var GigadriveNextPrerenderManifestV1Schema = import_effect11.Schema.mutable(
1585
+ import_effect11.Schema.Struct({
1586
+ version: import_effect11.Schema.Literal(1),
1587
+ seedMethods: import_effect11.Schema.optional(GigadriveNextPrerenderSeedMethodsSchema),
727
1588
  prerenders: PrerenderArraySchema
728
1589
  })
729
1590
  );
730
1591
  var parseGigadriveNextBuildManifest = (content) => decodeJson(GigadriveNextBuildManifestSchema, content);
731
1592
  var parseGigadriveNextPrerenderManifest = (content) => decodeJson(GigadriveNextPrerenderManifestV1Schema, content);
732
1593
 
733
- // src/regions.ts
734
- var REGION_DEFINITIONS = [
735
- "us-east-1",
736
- "us-east-2",
737
- "us-west-1",
738
- "us-west-2",
739
- "ap-south-1",
740
- "ap-northeast-3",
741
- "ap-northeast-2",
742
- "ap-southeast-1",
743
- "ap-southeast-2",
744
- "ap-northeast-1",
745
- "ca-central-1",
746
- "eu-central-1",
747
- "eu-west-1",
748
- "eu-west-2",
749
- "eu-west-3",
750
- "eu-north-1",
751
- "sa-east-1"
752
- ];
753
- var AVAILABLE_REGIONS = [...REGION_DEFINITIONS];
754
-
755
1594
  // src/detection/frameworks/nextjs.ts
756
1595
  var toPortablePath = (value) => value.replaceAll("\\", "/");
757
- var STANDALONE_SERVER_MEMORY_MB = 1024;
758
- var resolveStandaloneServer = import_effect8.Effect.fn("nextjs.resolveStandaloneServer")(function* (projectFolder, distDir, repoRootToProject) {
759
- const fs = yield* import_platform5.FileSystem.FileSystem;
760
- const pathService = yield* import_platform5.Path.Path;
1596
+ var STANDALONE_SERVER_MEMORY_MB = 1024;
1597
+ var resolveStandaloneServer = import_effect12.Effect.fn("nextjs.resolveStandaloneServer")(function* (projectFolder, distDir, repoRootToProject) {
1598
+ const fs = yield* import_platform8.FileSystem.FileSystem;
1599
+ const pathService = yield* import_platform8.Path.Path;
761
1600
  const standaloneRoot = pathService.join(projectFolder, distDir, "standalone");
762
1601
  const monorepoServer = pathService.join(standaloneRoot, repoRootToProject, "server.js");
763
1602
  const rootServer = pathService.join(standaloneRoot, "server.js");
764
- const monorepoServerExists = yield* fs.exists(monorepoServer).pipe(import_effect8.Effect.catchAll(() => import_effect8.Effect.succeed(false)));
1603
+ const monorepoServerExists = yield* fs.exists(monorepoServer).pipe(import_effect12.Effect.catchAll(() => import_effect12.Effect.succeed(false)));
765
1604
  const serverPath = monorepoServerExists ? monorepoServer : rootServer;
766
- const serverExists = yield* fs.exists(serverPath).pipe(import_effect8.Effect.catchAll(() => import_effect8.Effect.succeed(false)));
1605
+ const serverExists = yield* fs.exists(serverPath).pipe(import_effect12.Effect.catchAll(() => import_effect12.Effect.succeed(false)));
767
1606
  if (!serverExists) return void 0;
768
1607
  const serverDirectory = pathService.dirname(serverPath);
769
1608
  const entrypoint = toPortablePath(pathService.relative(standaloneRoot, serverPath));
770
1609
  return { standaloneRoot, serverDirectory, entrypoint };
771
1610
  });
772
- var buildStandalonePublicAssets = import_effect8.Effect.fn("nextjs.buildStandalonePublicAssets")(function* (projectFolder, standaloneRoot, serverDirectory) {
773
- const pathService = yield* import_platform5.Path.Path;
1611
+ var buildStandalonePublicAssets = import_effect12.Effect.fn("nextjs.buildStandalonePublicAssets")(function* (projectFolder, standaloneRoot, serverDirectory) {
1612
+ const pathService = yield* import_platform8.Path.Path;
774
1613
  const publicFiles = yield* getDefaultPathMap(pathService.join(projectFolder, "public"));
775
1614
  const assetPaths = [];
776
1615
  const assetOverrides = {};
@@ -786,8 +1625,8 @@ var buildStandalonePublicAssets = import_effect8.Effect.fn("nextjs.buildStandalo
786
1625
  }
787
1626
  return { assetPaths, assetOverrides, filePathMap };
788
1627
  });
789
- var createStaticExportConfig = import_effect8.Effect.fn("nextjs.createStaticExportConfig")(function* (defaults, projectFolder) {
790
- const pathService = yield* import_platform5.Path.Path;
1628
+ var createStaticExportConfig = import_effect12.Effect.fn("nextjs.createStaticExportConfig")(function* (defaults, projectFolder) {
1629
+ const pathService = yield* import_platform8.Path.Path;
791
1630
  const outputDirectory = pathService.join(projectFolder, "out");
792
1631
  const files = yield* getDefaultPathMap(outputDirectory);
793
1632
  const assetPaths = [];
@@ -810,8 +1649,8 @@ var createStaticExportConfig = import_effect8.Effect.fn("nextjs.createStaticExpo
810
1649
  package: void 0
811
1650
  };
812
1651
  });
813
- var createStandaloneConfig = import_effect8.Effect.fn("nextjs.createStandaloneConfig")(function* (defaults, projectFolder, manifest) {
814
- const pathService = yield* import_platform5.Path.Path;
1652
+ var createStandaloneConfig = import_effect12.Effect.fn("nextjs.createStandaloneConfig")(function* (defaults, projectFolder, manifest) {
1653
+ const pathService = yield* import_platform8.Path.Path;
815
1654
  const resolved = yield* resolveStandaloneServer(projectFolder, manifest.distDir, manifest.repoRootToProject);
816
1655
  if (!resolved) return defaults;
817
1656
  const { standaloneRoot, serverDirectory, entrypoint } = resolved;
@@ -844,7 +1683,7 @@ var createStandaloneConfig = import_effect8.Effect.fn("nextjs.createStandaloneCo
844
1683
  }
845
1684
  };
846
1685
  });
847
- var createStandaloneV2Config = import_effect8.Effect.fn("nextjs.createStandaloneV2Config")(function* (defaults, projectFolder, manifest) {
1686
+ var createStandaloneV2Config = import_effect12.Effect.fn("nextjs.createStandaloneV2Config")(function* (defaults, projectFolder, manifest) {
848
1687
  const resolved = yield* resolveStandaloneServer(projectFolder, manifest.distDir, manifest.repoRootToProject);
849
1688
  if (!resolved) return defaults;
850
1689
  const { standaloneRoot, serverDirectory, entrypoint } = resolved;
@@ -942,14 +1781,19 @@ var nextjs = {
942
1781
  streaming: true,
943
1782
  commands: ["next build"],
944
1783
  entrypoint: ".next/standalone/server.js",
945
- assetsDir: ".next/static",
1784
+ // No `assetsDir`: this is only the fallback for a standalone build whose
1785
+ // Gigadrive manifest is missing, and `.next/static` answers
1786
+ // `/_next/static/...` rather than its own stripped prefix. Every path that
1787
+ // resolves a real Next.js build (`refineDefaultConfig`) maps those chunks
1788
+ // itself, so declaring the directory here would publish them at the wrong
1789
+ // URLs.
946
1790
  populateAssetCache: true,
947
1791
  routes: [{ source: "/*", destination: ".next/standalone/server.js" }],
948
1792
  environmentVariables: { NODE_ENV: "production" }
949
1793
  }),
950
- refineDefaultConfig: (defaults, projectFolder) => import_effect8.Effect.gen(function* () {
951
- const fs = yield* import_platform5.FileSystem.FileSystem;
952
- const manifestContent = yield* fs.readFileString(`${projectFolder}/.gigadrive/nextjs.json`).pipe(import_effect8.Effect.catchAll(() => import_effect8.Effect.succeed(void 0)));
1794
+ refineDefaultConfig: (defaults, projectFolder) => import_effect12.Effect.gen(function* () {
1795
+ const fs = yield* import_platform8.FileSystem.FileSystem;
1796
+ const manifestContent = yield* fs.readFileString(`${projectFolder}/.gigadrive/nextjs.json`).pipe(import_effect12.Effect.catchAll(() => import_effect12.Effect.succeed(void 0)));
953
1797
  const manifest = manifestContent ? parseGigadriveNextBuildManifest(manifestContent) : void 0;
954
1798
  if (!manifest) return defaults;
955
1799
  if (manifest.version === 2) {
@@ -960,8 +1804,8 @@ var nextjs = {
960
1804
  };
961
1805
 
962
1806
  // src/detection/frameworks/nuxt.ts
963
- var import_platform6 = require("@effect/platform");
964
- var import_effect9 = require("effect");
1807
+ var import_platform9 = require("@effect/platform");
1808
+ var import_effect13 = require("effect");
965
1809
  var isVercelBuildOutputV3 = (content) => {
966
1810
  try {
967
1811
  const config = JSON.parse(content);
@@ -988,9 +1832,9 @@ var nuxt = {
988
1832
  routes: [{ source: "/*", destination: ".output/server/index.mjs" }],
989
1833
  environmentVariables: { NODE_ENV: "production", NITRO_PRESET: "node-server" }
990
1834
  }),
991
- refineDefaultConfig: (defaults, projectFolder) => import_effect9.Effect.gen(function* () {
992
- const fs = yield* import_platform6.FileSystem.FileSystem;
993
- const buildOutputConfig = yield* fs.readFileString(`${projectFolder}/.vercel/output/config.json`).pipe(import_effect9.Effect.catchAll(() => import_effect9.Effect.succeed(void 0)));
1835
+ refineDefaultConfig: (defaults, projectFolder) => import_effect13.Effect.gen(function* () {
1836
+ const fs = yield* import_platform9.FileSystem.FileSystem;
1837
+ const buildOutputConfig = yield* fs.readFileString(`${projectFolder}/.vercel/output/config.json`).pipe(import_effect13.Effect.catchAll(() => import_effect13.Effect.succeed(void 0)));
994
1838
  if (!buildOutputConfig || !isVercelBuildOutputV3(buildOutputConfig)) {
995
1839
  return defaults;
996
1840
  }
@@ -1113,7 +1957,7 @@ var FRAMEWORK_DEFINITIONS = [
1113
1957
  ].sort((a, b) => b.priority - a.priority);
1114
1958
 
1115
1959
  // src/detection/generate-config.ts
1116
- var import_effect10 = require("effect");
1960
+ var import_effect14 = require("effect");
1117
1961
  var getInstallCommand = (pm) => {
1118
1962
  switch (pm) {
1119
1963
  case "bun":
@@ -1129,11 +1973,11 @@ var getInstallCommand = (pm) => {
1129
1973
  return "npm install";
1130
1974
  }
1131
1975
  };
1132
- var generateConfig = import_effect10.Effect.fn("generateConfig")(function* (framework, packageManager, refinedDefaults) {
1976
+ var generateConfig = import_effect14.Effect.fn("generateConfig")(function* (framework, packageManager, projectFolder, refinedDefaults) {
1133
1977
  const defaults = refinedDefaults ?? framework.getDefaultConfig(packageManager);
1134
1978
  const installCmd = defaults.installCommand ?? getInstallCommand(packageManager);
1135
1979
  const commands = [installCmd, ...defaults.commands];
1136
- yield* import_effect10.Effect.logDebug(`Generating config for ${framework.name}`, {
1980
+ yield* import_effect14.Effect.logDebug(`Generating config for ${framework.name}`, {
1137
1981
  runtime: defaults.runtime,
1138
1982
  entrypoint: defaults.entrypoint,
1139
1983
  commands
@@ -1168,10 +2012,26 @@ var generateConfig = import_effect10.Effect.fn("generateConfig")(function* (fram
1168
2012
  ...defaults.normalizedConfig
1169
2013
  };
1170
2014
  if (defaults.assetsDir || defaults.assetPaths || defaults.assetPrefixes || defaults.assetManifests) {
2015
+ const paths = defaults.assetPaths ? [...defaults.assetPaths] : [];
2016
+ const overrides = { ...defaults.assetOverrides };
2017
+ const servesDirectoryIndexes = entrypoints.length === 0 && routes.length === 0;
2018
+ const assetsDir = defaults.assetsDir;
2019
+ if (assetsDir && !defaults.assetPaths && !defaults.assetPrefixes && !defaults.assetManifests) {
2020
+ for (const file of yield* collectAssetFiles(projectFolder, assetsDir)) {
2021
+ if (framework.language === "php" && /\.(php|phtml|phar)$/i.test(file)) continue;
2022
+ paths.push(`${assetsDir}/${file}`);
2023
+ if (!servesDirectoryIndexes || file in overrides) continue;
2024
+ if (file === "index.html") {
2025
+ overrides[file] = { path: "" };
2026
+ } else if (file.endsWith("/index.html")) {
2027
+ overrides[file] = { path: file.slice(0, -"/index.html".length) };
2028
+ }
2029
+ }
2030
+ }
1171
2031
  config.assets = {
1172
- paths: defaults.assetPaths ? [...defaults.assetPaths] : [],
2032
+ paths,
1173
2033
  prefixToStrip: defaults.assetsPrefixToStrip ?? (defaults.assetsDir ? defaults.assetsDir + "/" : ""),
1174
- overrides: defaults.assetOverrides ? { ...defaults.assetOverrides } : void 0,
2034
+ overrides: Object.keys(overrides).length > 0 ? overrides : void 0,
1175
2035
  prefixes: defaults.assetPrefixes ? defaults.assetPrefixes.map((prefix) => ({ ...prefix })) : void 0,
1176
2036
  manifests: defaults.assetManifests ? defaults.assetManifests.map((manifest) => ({ ...manifest })) : void 0,
1177
2037
  dynamicRoutes: true,
@@ -1182,31 +2042,31 @@ var generateConfig = import_effect10.Effect.fn("generateConfig")(function* (fram
1182
2042
  });
1183
2043
 
1184
2044
  // src/detection/read-dependencies.ts
1185
- var import_platform7 = require("@effect/platform");
1186
- var import_effect12 = require("effect");
2045
+ var import_platform10 = require("@effect/platform");
2046
+ var import_effect16 = require("effect");
1187
2047
 
1188
2048
  // src/detection/types.ts
1189
- var import_effect11 = require("effect");
1190
- var ManifestNotFoundError = class extends import_effect11.Schema.TaggedError()("ManifestNotFoundError", {
1191
- message: import_effect11.Schema.String,
1192
- filePath: import_effect11.Schema.String
2049
+ var import_effect15 = require("effect");
2050
+ var ManifestNotFoundError = class extends import_effect15.Schema.TaggedError()("ManifestNotFoundError", {
2051
+ message: import_effect15.Schema.String,
2052
+ filePath: import_effect15.Schema.String
1193
2053
  }) {
1194
2054
  };
1195
- var ManifestReadError = class extends import_effect11.Schema.TaggedError()("ManifestReadError", {
1196
- message: import_effect11.Schema.String,
1197
- filePath: import_effect11.Schema.String
2055
+ var ManifestReadError = class extends import_effect15.Schema.TaggedError()("ManifestReadError", {
2056
+ message: import_effect15.Schema.String,
2057
+ filePath: import_effect15.Schema.String
1198
2058
  }) {
1199
2059
  };
1200
- var ManifestParseError = class extends import_effect11.Schema.TaggedError()("ManifestParseError", {
1201
- message: import_effect11.Schema.String,
1202
- filePath: import_effect11.Schema.String
2060
+ var ManifestParseError = class extends import_effect15.Schema.TaggedError()("ManifestParseError", {
2061
+ message: import_effect15.Schema.String,
2062
+ filePath: import_effect15.Schema.String
1203
2063
  }) {
1204
2064
  };
1205
- var FrameworkNotDetectedError = class extends import_effect11.Schema.TaggedError()(
2065
+ var FrameworkNotDetectedError = class extends import_effect15.Schema.TaggedError()(
1206
2066
  "FrameworkNotDetectedError",
1207
2067
  {
1208
- message: import_effect11.Schema.String,
1209
- directory: import_effect11.Schema.String
2068
+ message: import_effect15.Schema.String,
2069
+ directory: import_effect15.Schema.String
1210
2070
  }
1211
2071
  ) {
1212
2072
  };
@@ -1216,25 +2076,25 @@ var MANIFEST_CONFIG = {
1216
2076
  node: { file: "package.json", depKeys: ["dependencies", "devDependencies"] },
1217
2077
  php: { file: "composer.json", depKeys: ["require", "require-dev"] }
1218
2078
  };
1219
- var readManifest = import_effect12.Effect.fn("readManifest")(function* (filePath) {
1220
- const fs = yield* import_platform7.FileSystem.FileSystem;
1221
- const exists = yield* fs.exists(filePath).pipe(import_effect12.Effect.catchAll(() => import_effect12.Effect.succeed(false)));
2079
+ var readManifest = import_effect16.Effect.fn("readManifest")(function* (filePath) {
2080
+ const fs = yield* import_platform10.FileSystem.FileSystem;
2081
+ const exists = yield* fs.exists(filePath).pipe(import_effect16.Effect.catchAll(() => import_effect16.Effect.succeed(false)));
1222
2082
  if (!exists) {
1223
- return yield* import_effect12.Effect.fail(new ManifestNotFoundError({ message: "Manifest not found", filePath }));
2083
+ return yield* import_effect16.Effect.fail(new ManifestNotFoundError({ message: "Manifest not found", filePath }));
1224
2084
  }
1225
- const content = yield* fs.readFileString(filePath).pipe(import_effect12.Effect.mapError(() => new ManifestReadError({ message: "Failed to read manifest", filePath })));
1226
- const parsed = yield* import_effect12.Effect.try({
2085
+ const content = yield* fs.readFileString(filePath).pipe(import_effect16.Effect.mapError(() => new ManifestReadError({ message: "Failed to read manifest", filePath })));
2086
+ const parsed = yield* import_effect16.Effect.try({
1227
2087
  try: () => JSON.parse(content),
1228
2088
  catch: () => new ManifestParseError({ message: "Failed to parse manifest JSON", filePath })
1229
2089
  });
1230
2090
  return parsed;
1231
2091
  });
1232
- var readDependencies = import_effect12.Effect.fn("readDependencies")(function* (projectFolder, language) {
1233
- const pathService = yield* import_platform7.Path.Path;
2092
+ var readDependencies = import_effect16.Effect.fn("readDependencies")(function* (projectFolder, language) {
2093
+ const pathService = yield* import_platform10.Path.Path;
1234
2094
  const config = MANIFEST_CONFIG[language];
1235
2095
  const filePath = pathService.join(projectFolder, config.file);
1236
2096
  const manifest = yield* readManifest(filePath).pipe(
1237
- import_effect12.Effect.catchTag("ManifestNotFoundError", () => import_effect12.Effect.succeed(null))
2097
+ import_effect16.Effect.catchTag("ManifestNotFoundError", () => import_effect16.Effect.succeed(null))
1238
2098
  );
1239
2099
  if (!manifest) {
1240
2100
  return /* @__PURE__ */ new Set();
@@ -1252,9 +2112,9 @@ var readDependencies = import_effect12.Effect.fn("readDependencies")(function* (
1252
2112
  });
1253
2113
 
1254
2114
  // src/detection/detect-framework.ts
1255
- var matchesDetectionItem = import_effect13.Effect.fn("matchesDetectionItem")(function* (item, projectFolder, framework, cachedReadDeps) {
1256
- const fs = yield* import_platform8.FileSystem.FileSystem;
1257
- const pathService = yield* import_platform8.Path.Path;
2115
+ var matchesDetectionItem = import_effect17.Effect.fn("matchesDetectionItem")(function* (item, projectFolder, framework, cachedReadDeps) {
2116
+ const fs = yield* import_platform11.FileSystem.FileSystem;
2117
+ const pathService = yield* import_platform11.Path.Path;
1258
2118
  if (item.matchPackage != null) {
1259
2119
  const deps = yield* cachedReadDeps(`${projectFolder}\0${framework.language}`);
1260
2120
  if (!deps.has(item.matchPackage)) {
@@ -1263,12 +2123,12 @@ var matchesDetectionItem = import_effect13.Effect.fn("matchesDetectionItem")(fun
1263
2123
  }
1264
2124
  if (item.path != null) {
1265
2125
  const filePath = pathService.join(projectFolder, item.path);
1266
- const exists = yield* fs.exists(filePath).pipe(import_effect13.Effect.catchAll(() => import_effect13.Effect.succeed(false)));
2126
+ const exists = yield* fs.exists(filePath).pipe(import_effect17.Effect.catchAll(() => import_effect17.Effect.succeed(false)));
1267
2127
  if (!exists) {
1268
2128
  return false;
1269
2129
  }
1270
2130
  if (item.matchContent != null) {
1271
- const content = yield* fs.readFileString(filePath).pipe(import_effect13.Effect.catchAll(() => import_effect13.Effect.succeed("")));
2131
+ const content = yield* fs.readFileString(filePath).pipe(import_effect17.Effect.catchAll(() => import_effect17.Effect.succeed("")));
1272
2132
  let regexMatch = false;
1273
2133
  try {
1274
2134
  regexMatch = new RegExp(item.matchContent).test(content);
@@ -1281,7 +2141,7 @@ var matchesDetectionItem = import_effect13.Effect.fn("matchesDetectionItem")(fun
1281
2141
  }
1282
2142
  return true;
1283
2143
  });
1284
- var matchesFramework = import_effect13.Effect.fn("matchesFramework")(function* (framework, projectFolder, cachedReadDeps) {
2144
+ var matchesFramework = import_effect17.Effect.fn("matchesFramework")(function* (framework, projectFolder, cachedReadDeps) {
1285
2145
  for (const detector of framework.detectors) {
1286
2146
  const matches = yield* matchesDetectionItem(detector, projectFolder, framework, cachedReadDeps);
1287
2147
  if (!matches) {
@@ -1290,9 +2150,9 @@ var matchesFramework = import_effect13.Effect.fn("matchesFramework")(function* (
1290
2150
  }
1291
2151
  return true;
1292
2152
  });
1293
- var detectFramework = import_effect13.Effect.fn("detectFramework")(function* (projectFolder) {
1294
- yield* import_effect13.Effect.logDebug(`Detecting framework in ${projectFolder}`);
1295
- const cachedReadDeps = yield* import_effect13.Effect.cachedFunction((key) => {
2153
+ var detectFramework = import_effect17.Effect.fn("detectFramework")(function* (projectFolder) {
2154
+ yield* import_effect17.Effect.logDebug(`Detecting framework in ${projectFolder}`);
2155
+ const cachedReadDeps = yield* import_effect17.Effect.cachedFunction((key) => {
1296
2156
  const sepIndex = key.lastIndexOf("\0");
1297
2157
  const folder = key.slice(0, sepIndex);
1298
2158
  const language = key.slice(sepIndex + 1);
@@ -1301,16 +2161,16 @@ var detectFramework = import_effect13.Effect.fn("detectFramework")(function* (pr
1301
2161
  for (const framework of FRAMEWORK_DEFINITIONS) {
1302
2162
  const matches = yield* matchesFramework(framework, projectFolder, cachedReadDeps);
1303
2163
  if (matches) {
1304
- yield* import_effect13.Effect.log(`Detected framework: ${framework.name}`, { slug: framework.slug });
2164
+ yield* import_effect17.Effect.log(`Detected framework: ${framework.name}`, { slug: framework.slug });
1305
2165
  const detectedPackageManager = yield* detectPackageManager(projectFolder);
1306
2166
  const packageManager = framework.language === "php" ? "composer" : detectedPackageManager;
1307
2167
  const defaults = framework.getDefaultConfig(packageManager);
1308
2168
  const refinedDefaults = framework.refineDefaultConfig ? yield* framework.refineDefaultConfig(defaults, projectFolder) : defaults;
1309
- const config = yield* generateConfig(framework, packageManager, refinedDefaults);
2169
+ const config = yield* generateConfig(framework, packageManager, projectFolder, refinedDefaults);
1310
2170
  return { framework, packageManager, config };
1311
2171
  }
1312
2172
  }
1313
- return yield* import_effect13.Effect.fail(
2173
+ return yield* import_effect17.Effect.fail(
1314
2174
  new FrameworkNotDetectedError({
1315
2175
  message: `No framework detected in project directory`,
1316
2176
  directory: projectFolder
@@ -1319,15 +2179,22 @@ var detectFramework = import_effect13.Effect.fn("detectFramework")(function* (pr
1319
2179
  });
1320
2180
 
1321
2181
  // src/detection/merge-config.ts
1322
- var import_effect14 = require("effect");
1323
- var mergeWithFrameworkDefaults = import_effect14.Effect.fn("mergeWithFrameworkDefaults")(function* (userConfig, frameworkConfig) {
1324
- yield* import_effect14.Effect.logDebug("Merging user config with framework defaults");
2182
+ var import_effect18 = require("effect");
2183
+ var mergeWithFrameworkDefaults = import_effect18.Effect.fn("mergeWithFrameworkDefaults")(function* (userConfig, frameworkConfig) {
2184
+ yield* import_effect18.Effect.logDebug("Merging user config with framework defaults");
1325
2185
  const userHasAssets = (userConfig.assets?.paths?.length ?? 0) > 0 || (userConfig.assets?.prefixes?.length ?? 0) > 0 || (userConfig.assets?.manifests?.length ?? 0) > 0;
2186
+ const isContainer = (entrypoint) => entrypoint.runtime === CONTAINER_RUNTIME;
2187
+ const userFileFunctions = userConfig.entrypoints.filter((entrypoint) => !isContainer(entrypoint));
2188
+ const containerPaths = new Set(userConfig.entrypoints.filter(isContainer).map((entrypoint) => entrypoint.path));
2189
+ const targetsContainer = (route) => containerPaths.has(route.destination.replace(/^\/+/, ""));
1326
2190
  const merged = {
1327
2191
  regions: userConfig.regions,
1328
2192
  commands: userConfig.commands.length > 0 ? userConfig.commands : frameworkConfig.commands,
1329
- entrypoints: userConfig.entrypoints.length > 0 ? userConfig.entrypoints : frameworkConfig.entrypoints,
1330
- routes: userConfig.routes.length > 0 ? userConfig.routes : frameworkConfig.routes,
2193
+ entrypoints: [
2194
+ ...userFileFunctions.length > 0 ? userFileFunctions : frameworkConfig.entrypoints,
2195
+ ...userConfig.entrypoints.filter(isContainer)
2196
+ ],
2197
+ routes: containerPaths.size > 0 && userConfig.routes.every(targetsContainer) ? [...userConfig.routes, ...frameworkConfig.routes] : userConfig.routes.length > 0 ? userConfig.routes : frameworkConfig.routes,
1331
2198
  assets: userHasAssets ? userConfig.assets : frameworkConfig.assets,
1332
2199
  environmentVariables: {
1333
2200
  ...frameworkConfig.environmentVariables,
@@ -1335,6 +2202,7 @@ var mergeWithFrameworkDefaults = import_effect14.Effect.fn("mergeWithFrameworkDe
1335
2202
  },
1336
2203
  excludeFiles: userConfig.excludeFiles && userConfig.excludeFiles.length > 0 ? userConfig.excludeFiles : frameworkConfig.excludeFiles,
1337
2204
  services: userConfig.services,
2205
+ ...userConfig.sidecars !== void 0 && { sidecars: userConfig.sidecars },
1338
2206
  userArchive: userConfig.userArchive,
1339
2207
  warnings: [...frameworkConfig.warnings, ...userConfig.warnings],
1340
2208
  errors: [...frameworkConfig.errors, ...userConfig.errors]
@@ -1342,51 +2210,6 @@ var mergeWithFrameworkDefaults = import_effect14.Effect.fn("mergeWithFrameworkDe
1342
2210
  return merged;
1343
2211
  });
1344
2212
 
1345
- // src/errors.ts
1346
- var import_effect15 = require("effect");
1347
- var ConfigFileNotFoundError = class extends import_effect15.Schema.TaggedError()("ConfigFileNotFoundError", {
1348
- message: import_effect15.Schema.String,
1349
- filePath: import_effect15.Schema.String
1350
- }) {
1351
- };
1352
- var ConfigFileEmptyError = class extends import_effect15.Schema.TaggedError()("ConfigFileEmptyError", {
1353
- message: import_effect15.Schema.String,
1354
- filePath: import_effect15.Schema.String
1355
- }) {
1356
- };
1357
- var ConfigFileParseError = class extends import_effect15.Schema.TaggedError()("ConfigFileParseError", {
1358
- message: import_effect15.Schema.String,
1359
- filePath: import_effect15.Schema.String,
1360
- cause: import_effect15.Schema.optional(import_effect15.Schema.String)
1361
- }) {
1362
- };
1363
- var ConfigModuleLoadError = class extends import_effect15.Schema.TaggedError()("ConfigModuleLoadError", {
1364
- message: import_effect15.Schema.String,
1365
- filePath: import_effect15.Schema.String,
1366
- cause: import_effect15.Schema.optional(import_effect15.Schema.String)
1367
- }) {
1368
- };
1369
- var ConfigVersionError = class extends import_effect15.Schema.TaggedError()("ConfigVersionError", {
1370
- message: import_effect15.Schema.String,
1371
- filePath: import_effect15.Schema.String,
1372
- version: import_effect15.Schema.optional(import_effect15.Schema.Number)
1373
- }) {
1374
- };
1375
- var ConfigSchemaValidationError = class extends import_effect15.Schema.TaggedError()(
1376
- "ConfigSchemaValidationError",
1377
- {
1378
- message: import_effect15.Schema.String,
1379
- filePath: import_effect15.Schema.String,
1380
- validationErrors: import_effect15.Schema.Array(import_effect15.Schema.String)
1381
- }
1382
- ) {
1383
- };
1384
- var FunctionConfigError = class extends import_effect15.Schema.TaggedError()("FunctionConfigError", {
1385
- message: import_effect15.Schema.String,
1386
- functionPath: import_effect15.Schema.String
1387
- }) {
1388
- };
1389
-
1390
2213
  // src/filter-functions-from-assets.ts
1391
2214
  var filterFunctionsFromAssets = (config) => {
1392
2215
  return {
@@ -1398,28 +2221,24 @@ var filterFunctionsFromAssets = (config) => {
1398
2221
  };
1399
2222
  };
1400
2223
 
1401
- // src/normalized-config.ts
1402
- var DEFAULT_FUNCTION_DURATION_SECONDS = 30;
1403
- var MAX_FUNCTION_DURATION_SECONDS = 28800;
1404
-
1405
2224
  // src/parse-config.ts
1406
- var import_effect21 = require("effect");
2225
+ var import_effect24 = require("effect");
1407
2226
 
1408
2227
  // src/services/raw-config-reader.ts
1409
- var import_platform9 = require("@effect/platform");
1410
- var import_effect17 = require("effect");
1411
- var import_yaml = require("yaml");
2228
+ var import_platform12 = require("@effect/platform");
2229
+ var import_effect20 = require("effect");
2230
+ var import_yaml2 = require("yaml");
1412
2231
 
1413
2232
  // src/services/config-module-loader.ts
1414
- var import_effect16 = require("effect");
2233
+ var import_effect19 = require("effect");
1415
2234
  var import_jiti = require("jiti");
1416
2235
  var MISSING_DEFAULT_EXPORT = /* @__PURE__ */ Symbol("MissingDefaultExport");
1417
2236
  var STRICT_DEFAULT_EXPORT_EXTENSIONS = [".ts", ".mts", ".mjs"];
1418
- var ConfigModuleLoader = class extends import_effect16.Effect.Service()("ConfigModuleLoader", {
1419
- effect: import_effect16.Effect.gen(function* () {
1420
- const loadConfigModule = import_effect16.Effect.fn("ConfigModuleLoader.loadConfigModule")(function* (filePath) {
2237
+ var ConfigModuleLoader = class extends import_effect19.Effect.Service()("ConfigModuleLoader", {
2238
+ effect: import_effect19.Effect.gen(function* () {
2239
+ const loadConfigModule = import_effect19.Effect.fn("ConfigModuleLoader.loadConfigModule")(function* (filePath) {
1421
2240
  const extension = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
1422
- const mod = yield* import_effect16.Effect.tryPromise({
2241
+ const mod = yield* import_effect19.Effect.tryPromise({
1423
2242
  try: async () => {
1424
2243
  const jiti = (0, import_jiti.createJiti)(filePath, { fsCache: false, interopDefault: true, moduleCache: false });
1425
2244
  const namespace = await jiti.import(filePath);
@@ -1438,7 +2257,7 @@ var ConfigModuleLoader = class extends import_effect16.Effect.Service()("ConfigM
1438
2257
  })
1439
2258
  });
1440
2259
  if (mod === MISSING_DEFAULT_EXPORT) {
1441
- return yield* import_effect16.Effect.fail(
2260
+ return yield* import_effect19.Effect.fail(
1442
2261
  new ConfigModuleLoadError({
1443
2262
  message: `Config module at ${filePath} does not have a default export. Export the config object as the default export (or via module.exports for CommonJS files), optionally wrapped in defineConfig.`,
1444
2263
  filePath
@@ -1446,7 +2265,7 @@ var ConfigModuleLoader = class extends import_effect16.Effect.Service()("ConfigM
1446
2265
  );
1447
2266
  }
1448
2267
  if (typeof mod === "function") {
1449
- return yield* import_effect16.Effect.fail(
2268
+ return yield* import_effect19.Effect.fail(
1450
2269
  new ConfigModuleLoadError({
1451
2270
  message: `Config module at ${filePath} default-exports a function; config factories are not supported. Export a config object instead (optionally wrapped in defineConfig).`,
1452
2271
  filePath
@@ -1454,7 +2273,7 @@ var ConfigModuleLoader = class extends import_effect16.Effect.Service()("ConfigM
1454
2273
  );
1455
2274
  }
1456
2275
  if (typeof mod !== "object" || mod === null || Array.isArray(mod)) {
1457
- return yield* import_effect16.Effect.fail(
2276
+ return yield* import_effect19.Effect.fail(
1458
2277
  new ConfigModuleLoadError({
1459
2278
  message: `Config module at ${filePath} must default-export a config object.`,
1460
2279
  filePath
@@ -1484,36 +2303,36 @@ var ALLOWED_CONFIG_NAMES = [
1484
2303
  "nebula.json"
1485
2304
  ];
1486
2305
  var MODULE_EXTENSIONS = [".ts", ".mts", ".cts", ".js", ".mjs", ".cjs"];
1487
- var RawConfigReader = class extends import_effect17.Effect.Service()("RawConfigReader", {
2306
+ var RawConfigReader = class extends import_effect20.Effect.Service()("RawConfigReader", {
1488
2307
  dependencies: [ConfigModuleLoader.Default],
1489
- effect: import_effect17.Effect.gen(function* () {
1490
- const fs = yield* import_platform9.FileSystem.FileSystem;
1491
- const pathService = yield* import_platform9.Path.Path;
2308
+ effect: import_effect20.Effect.gen(function* () {
2309
+ const fs = yield* import_platform12.FileSystem.FileSystem;
2310
+ const pathService = yield* import_platform12.Path.Path;
1492
2311
  const moduleLoader = yield* ConfigModuleLoader;
1493
- const findConfig = import_effect17.Effect.fn("RawConfigReader.findConfig")(function* (projectFolder) {
2312
+ const findConfig = import_effect20.Effect.fn("RawConfigReader.findConfig")(function* (projectFolder) {
1494
2313
  for (const name of ALLOWED_CONFIG_NAMES) {
1495
2314
  const filePath = pathService.join(projectFolder, name);
1496
- const exists = yield* fs.exists(filePath).pipe(import_effect17.Effect.catchAll(() => import_effect17.Effect.succeed(false)));
2315
+ const exists = yield* fs.exists(filePath).pipe(import_effect20.Effect.catchAll(() => import_effect20.Effect.succeed(false)));
1497
2316
  if (exists) {
1498
2317
  return filePath;
1499
2318
  }
1500
2319
  }
1501
2320
  return null;
1502
2321
  });
1503
- const readRawConfig = import_effect17.Effect.fn("RawConfigReader.readRawConfig")(function* (filePath, options) {
1504
- const exists = yield* fs.exists(filePath).pipe(import_effect17.Effect.catchAll(() => import_effect17.Effect.succeed(false)));
2322
+ const readRawConfig = import_effect20.Effect.fn("RawConfigReader.readRawConfig")(function* (filePath, options) {
2323
+ const exists = yield* fs.exists(filePath).pipe(import_effect20.Effect.catchAll(() => import_effect20.Effect.succeed(false)));
1505
2324
  if (!exists) {
1506
- return yield* import_effect17.Effect.fail(
2325
+ return yield* import_effect20.Effect.fail(
1507
2326
  new ConfigFileNotFoundError({ message: `Config file not found at ${filePath}`, filePath })
1508
2327
  );
1509
2328
  }
1510
2329
  const fileContents = yield* fs.readFileString(filePath).pipe(
1511
- import_effect17.Effect.mapError(
2330
+ import_effect20.Effect.mapError(
1512
2331
  () => new ConfigFileParseError({ message: `Failed to read config file at ${filePath}`, filePath })
1513
2332
  )
1514
2333
  );
1515
2334
  if (fileContents.length === 0) {
1516
- return yield* import_effect17.Effect.fail(
2335
+ return yield* import_effect20.Effect.fail(
1517
2336
  new ConfigFileEmptyError({ message: `Config file is empty at ${filePath}`, filePath })
1518
2337
  );
1519
2338
  }
@@ -1526,10 +2345,10 @@ var RawConfigReader = class extends import_effect17.Effect.Service()("RawConfigR
1526
2345
  if (fileExtension === ".json") {
1527
2346
  parsed = JSON.parse(fileContents);
1528
2347
  } else {
1529
- parsed = (0, import_yaml.parse)(fileContents);
2348
+ parsed = (0, import_yaml2.parse)(fileContents);
1530
2349
  }
1531
2350
  } catch (error) {
1532
- return yield* import_effect17.Effect.fail(
2351
+ return yield* import_effect20.Effect.fail(
1533
2352
  new ConfigFileParseError({
1534
2353
  message: `Failed to parse config file at ${filePath}: ${error instanceof Error ? error.message : "Unknown error"}`,
1535
2354
  filePath,
@@ -1539,21 +2358,21 @@ var RawConfigReader = class extends import_effect17.Effect.Service()("RawConfigR
1539
2358
  }
1540
2359
  }
1541
2360
  if (parsed == null) {
1542
- return yield* import_effect17.Effect.fail(
2361
+ return yield* import_effect20.Effect.fail(
1543
2362
  new ConfigFileParseError({ message: `Config file could not be parsed at ${filePath}`, filePath })
1544
2363
  );
1545
2364
  }
1546
2365
  if (!options?.disableVersionCheck && typeof parsed.version !== "number") {
1547
- return yield* import_effect17.Effect.fail(
2366
+ return yield* import_effect20.Effect.fail(
1548
2367
  new ConfigVersionError({ message: `Config file is missing version at ${filePath}`, filePath })
1549
2368
  );
1550
2369
  }
1551
2370
  return parsed;
1552
2371
  });
1553
2372
  const readConfigFile = (configFilePath) => readRawConfig(configFilePath, { disableVersionCheck: true }).pipe(
1554
- import_effect17.Effect.map((parsed) => parsed),
1555
- import_effect17.Effect.catchTag("ConfigFileNotFoundError", () => import_effect17.Effect.succeed(null)),
1556
- import_effect17.Effect.withSpan("RawConfigReader.readConfigFile")
2373
+ import_effect20.Effect.map((parsed) => parsed),
2374
+ import_effect20.Effect.catchTag("ConfigFileNotFoundError", () => import_effect20.Effect.succeed(null)),
2375
+ import_effect20.Effect.withSpan("RawConfigReader.readConfigFile")
1557
2376
  );
1558
2377
  return { findConfig, readRawConfig, readConfigFile };
1559
2378
  })
@@ -1563,17 +2382,17 @@ var RawConfigReader = class extends import_effect17.Effect.Service()("RawConfigR
1563
2382
  // src/services/schema-validator.ts
1564
2383
  var import_ajv = __toESM(require("ajv"));
1565
2384
  var import_ajv_formats = __toESM(require("ajv-formats"));
1566
- var import_effect18 = require("effect");
1567
- var SchemaValidator = class extends import_effect18.Effect.Service()("SchemaValidator", {
1568
- effect: import_effect18.Effect.gen(function* () {
2385
+ var import_effect21 = require("effect");
2386
+ var SchemaValidator = class extends import_effect21.Effect.Service()("SchemaValidator", {
2387
+ effect: import_effect21.Effect.gen(function* () {
1569
2388
  const ajv = new import_ajv.default();
1570
2389
  (0, import_ajv_formats.default)(ajv);
1571
- const validate = import_effect18.Effect.fn("SchemaValidator.validate")(function* (config, schema, filePath) {
2390
+ const validate = import_effect21.Effect.fn("SchemaValidator.validate")(function* (config, schema, filePath) {
1572
2391
  const compiledValidate = ajv.compile(schema);
1573
2392
  const valid = compiledValidate(config);
1574
2393
  if (!valid) {
1575
2394
  const errors = compiledValidate.errors?.map((e) => `${e.instancePath || "/"} ${e.message ?? ""}`.trim()) ?? [];
1576
- return yield* import_effect18.Effect.fail(
2395
+ return yield* import_effect21.Effect.fail(
1577
2396
  new ConfigSchemaValidationError({
1578
2397
  message: `Config file at ${filePath} is invalid: ${ajv.errorsText(compiledValidate.errors)}`,
1579
2398
  filePath,
@@ -1587,9 +2406,6 @@ var SchemaValidator = class extends import_effect18.Effect.Service()("SchemaVali
1587
2406
  }) {
1588
2407
  };
1589
2408
 
1590
- // src/services/v4-config-parser.ts
1591
- var import_platform10 = require("@effect/platform");
1592
-
1593
2409
  // ../build-utils/src/get-files-for-pattern.ts
1594
2410
  var import_minimatch = require("minimatch");
1595
2411
  var import_promises = require("fs/promises");
@@ -1632,11 +2448,10 @@ var getFilesForPattern = async (pattern, projectFolder, excludeFiles = []) => {
1632
2448
  };
1633
2449
 
1634
2450
  // src/services/v4-config-parser.ts
1635
- var import_effect19 = require("effect");
2451
+ var import_effect22 = require("effect");
1636
2452
  var import_minimatch2 = require("minimatch");
2453
+ var import_node_path2 = require("path");
1637
2454
  var import_safe_regex2 = __toESM(require("safe-regex2"));
1638
- var MAX_ASSET_DEPTH = 100;
1639
- var DISALLOWED_ASSET_EXTENSIONS = [".htaccess", ".htpasswd"];
1640
2455
  var DEFAULT_FUNCTION_SETTINGS = {
1641
2456
  memory: 128,
1642
2457
  max_duration: DEFAULT_FUNCTION_DURATION_SECONDS,
@@ -1668,6 +2483,46 @@ var normalizeImagePolicy = (images) => {
1668
2483
  variants: images.variants ?? {}
1669
2484
  };
1670
2485
  };
2486
+ var DURATION_UNIT_SECONDS = { s: 1, m: 60, h: 3600, d: 86400 };
2487
+ var queueDurationSeconds = (value) => {
2488
+ if (value === void 0) return void 0;
2489
+ if (typeof value === "number") return value;
2490
+ const match = /^(\d+(?:\.\d+)?)(s|m|h|d)$/.exec(value);
2491
+ if (!match)
2492
+ throw new Error(`Invalid queue duration "${value}". Use seconds or a string such as 30s, 10m, 1.5h or 4d.`);
2493
+ return Math.ceil(Number(match[1]) * DURATION_UNIT_SECONDS[match[2]]);
2494
+ };
2495
+ var byName = (left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0;
2496
+ var definedOnly = (value) => Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
2497
+ var normalizeQueue = (name, queue) => {
2498
+ if (queue === null) return { name };
2499
+ const schedules = queue.schedules ? Object.entries(queue.schedules).map(
2500
+ ([scheduleName, schedule]) => definedOnly({
2501
+ name: scheduleName,
2502
+ cron: schedule.cron,
2503
+ timezone: schedule.timezone,
2504
+ body: schedule.body === void 0 || typeof schedule.body === "string" ? schedule.body : JSON.stringify(schedule.body),
2505
+ // A plain-text body labelled JSON would fail to decode on every fire.
2506
+ contentType: schedule.contentType ?? (typeof schedule.body === "string" ? "text/plain; charset=utf-8" : void 0),
2507
+ headers: schedule.headers,
2508
+ enabled: schedule.enabled
2509
+ })
2510
+ ).sort(byName) : void 0;
2511
+ return definedOnly({
2512
+ name,
2513
+ consumer: queue.consumer,
2514
+ visibilityTimeoutSeconds: queueDurationSeconds(queue.visibilityTimeout),
2515
+ retentionSeconds: queueDurationSeconds(queue.retention),
2516
+ maxAttempts: queue.maxAttempts,
2517
+ retryBackoffMinSeconds: queueDurationSeconds(queue.retryBackoff?.min),
2518
+ retryBackoffMaxSeconds: queueDurationSeconds(queue.retryBackoff?.max),
2519
+ deduplicationWindowSeconds: queueDurationSeconds(queue.deduplicationWindow),
2520
+ concurrency: queue.concurrency,
2521
+ rateLimit: queue.rateLimit == null ? queue.rateLimit : { count: queue.rateLimit.count, periodSeconds: queueDurationSeconds(queue.rateLimit.period) ?? 1 },
2522
+ deadLetter: queue.deadLetter,
2523
+ schedules
2524
+ });
2525
+ };
1671
2526
  var normalizeServices = (services) => {
1672
2527
  if (services == null) return void 0;
1673
2528
  const normalized = [];
@@ -1680,7 +2535,13 @@ var normalizeServices = (services) => {
1680
2535
  if (services.storage !== void 0) {
1681
2536
  normalized.push({
1682
2537
  type: "storage",
1683
- buckets: Object.entries(services.storage.buckets).map(([name, bucket]) => ({ name, visibility: bucket?.visibility ?? "private" })).sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0)
2538
+ buckets: Object.entries(services.storage.buckets).map(([name, bucket]) => ({ name, visibility: bucket?.visibility ?? "private" })).sort(byName)
2539
+ });
2540
+ }
2541
+ if (services.queues !== void 0) {
2542
+ normalized.push({
2543
+ type: "queues",
2544
+ queues: Object.entries(services.queues).map(([name, queue]) => normalizeQueue(name, queue)).sort(byName)
1684
2545
  });
1685
2546
  }
1686
2547
  return normalized.length > 0 ? normalized : void 0;
@@ -1738,9 +2599,10 @@ var mapRoute = (route, entrypoints) => ({
1738
2599
  negativeRequirements: route.missing,
1739
2600
  status: route.statusCode
1740
2601
  });
1741
- var parseEntrypoints = import_effect19.Effect.fn("parseEntrypoints")(function* (config, projectFolder) {
2602
+ var parseEntrypoints = import_effect22.Effect.fn("parseEntrypoints")(function* (config, projectFolder) {
1742
2603
  const entrypoints = [];
1743
- if (config.functions == null) return entrypoints;
2604
+ const dockerFunctions = [];
2605
+ if (config.functions == null) return { entrypoints, dockerFunctions };
1744
2606
  for (const [fnPath, func] of Object.entries(config.functions)) {
1745
2607
  if (getFunctionSettings(fnPath, config) == null) {
1746
2608
  return yield* new FunctionConfigError({
@@ -1748,7 +2610,7 @@ var parseEntrypoints = import_effect19.Effect.fn("parseEntrypoints")(function* (
1748
2610
  functionPath: fnPath
1749
2611
  });
1750
2612
  }
1751
- const matchedFiles = yield* import_effect19.Effect.tryPromise({
2613
+ const matchedFiles = yield* import_effect22.Effect.tryPromise({
1752
2614
  try: () => getFilesForPattern(fnPath, projectFolder, func.excludeFiles),
1753
2615
  catch: (error) => new FunctionConfigError({
1754
2616
  message: `Failed to resolve files for function pattern '${fnPath}': ${error instanceof Error ? error.message : String(error)}`,
@@ -1765,6 +2627,35 @@ var parseEntrypoints = import_effect19.Effect.fn("parseEntrypoints")(function* (
1765
2627
  functionPath: file
1766
2628
  });
1767
2629
  }
2630
+ if (settings.runtime === CONTAINER_RUNTIME) {
2631
+ const unsupported = ["symlinks", "includeFiles"].filter((key) => settings[key] !== void 0);
2632
+ if (unsupported.length > 0) {
2633
+ return yield* new FunctionConfigError({
2634
+ message: `Function '${file}' runs a Dockerfile, so ${unsupported.join(" and ")} do not apply. Copy files in the Dockerfile instead.`,
2635
+ functionPath: file
2636
+ });
2637
+ }
2638
+ const declared = Object.entries(config.functions).filter(([pattern]) => matchesPattern(file, pattern)).reduce((merged, [, value]) => ({ ...merged, ...value }), {});
2639
+ dockerFunctions.push({
2640
+ file,
2641
+ name: dockerFunctionContainerName(file),
2642
+ container: {
2643
+ build: { context: import_node_path2.posix.dirname(file), dockerfile: import_node_path2.posix.basename(file) },
2644
+ ...declared.port !== void 0 && { port: declared.port },
2645
+ ...declared.memory !== void 0 && { memory: declared.memory },
2646
+ ...declared.max_duration !== void 0 && { max_duration: declared.max_duration },
2647
+ ...declared.streaming !== void 0 && { streaming: declared.streaming },
2648
+ ...func.schedule !== void 0 && { schedule: func.schedule }
2649
+ }
2650
+ });
2651
+ continue;
2652
+ }
2653
+ if (settings.port !== void 0) {
2654
+ return yield* new FunctionConfigError({
2655
+ message: `Function '${file}' sets port, which only applies to 'runtime: docker'.`,
2656
+ functionPath: file
2657
+ });
2658
+ }
1768
2659
  const runtime = settings.runtime ?? "node-20";
1769
2660
  const streaming = settings.streaming ?? runtimeStreamsByDefault(runtime);
1770
2661
  entrypoints.push({
@@ -1782,61 +2673,84 @@ var parseEntrypoints = import_effect19.Effect.fn("parseEntrypoints")(function* (
1782
2673
  });
1783
2674
  }
1784
2675
  }
1785
- return entrypoints;
2676
+ dockerFunctions.sort((left, right) => left.file < right.file ? -1 : left.file > right.file ? 1 : 0);
2677
+ for (const [index, { file, name }] of dockerFunctions.entries()) {
2678
+ const clash = dockerFunctions.slice(0, index).find((candidate) => candidate.name === name);
2679
+ if (clash !== void 0) {
2680
+ return yield* new FunctionConfigError({
2681
+ message: `Functions '${clash.file}' and '${file}' would both be named '${name}'. Declare one of them under 'containers' with an explicit name.`,
2682
+ functionPath: file
2683
+ });
2684
+ }
2685
+ }
2686
+ return { entrypoints, dockerFunctions };
1786
2687
  });
1787
- var collectAssets = import_effect19.Effect.fn("collectAssets")(function* (config, projectFolder) {
2688
+ var collectAssets = import_effect22.Effect.fn("collectAssets")(function* (config, projectFolder) {
1788
2689
  if (config.assets == null) return [];
1789
- const fs = yield* import_platform10.FileSystem.FileSystem;
1790
- const pathService = yield* import_platform10.Path.Path;
1791
- const assetsPath = pathService.join(projectFolder, config.assets);
1792
- const assetsExist = yield* fs.exists(assetsPath).pipe(import_effect19.Effect.catchAll(() => import_effect19.Effect.succeed(false)));
1793
- if (!assetsExist) return [];
1794
- const stat = yield* fs.stat(assetsPath).pipe(import_effect19.Effect.catchAll(() => import_effect19.Effect.succeed(null)));
1795
- if (!stat || stat.type !== "Directory") return [];
1796
- const allFiles = yield* collectFilesRecursively(assetsPath);
1797
- const assets = [];
1798
- for (const assetName of allFiles) {
1799
- if (DISALLOWED_ASSET_EXTENSIONS.some((ext) => assetName.toLowerCase().endsWith(ext.toLowerCase()))) continue;
1800
- if (getFunctionSettings(`${config.assets}/${assetName}`, config) != null) continue;
1801
- assets.push(`${config.assets}/${assetName}`);
1802
- }
1803
- return assets;
2690
+ const assetNames = yield* collectAssetFiles(projectFolder, config.assets);
2691
+ return assetNames.filter((assetName) => getFunctionSettings(`${config.assets}/${assetName}`, config) == null).map((assetName) => `${config.assets}/${assetName}`);
1804
2692
  });
1805
- var collectFilesRecursively = import_effect19.Effect.fn("collectFilesRecursively")(
1806
- function* (basePath, relativePath = "", depth = 0) {
1807
- if (depth > MAX_ASSET_DEPTH) return [];
1808
- const fs = yield* import_platform10.FileSystem.FileSystem;
1809
- const pathSvc = yield* import_platform10.Path.Path;
1810
- const currentPath = relativePath ? pathSvc.join(basePath, relativePath) : basePath;
1811
- const entries = yield* fs.readDirectory(currentPath).pipe(import_effect19.Effect.catchAll(() => import_effect19.Effect.succeed([])));
1812
- const result = [];
1813
- for (const name of entries) {
1814
- const fullPath = pathSvc.join(currentPath, name);
1815
- const entryRelative = relativePath ? pathSvc.join(relativePath, name) : name;
1816
- const stat = yield* fs.stat(fullPath).pipe(import_effect19.Effect.catchAll(() => import_effect19.Effect.succeed(null)));
1817
- if (!stat) continue;
1818
- if (stat.type === "Directory") {
1819
- const nested = yield* collectFilesRecursively(basePath, entryRelative, depth + 1);
1820
- result.push(...nested);
1821
- } else {
1822
- result.push(entryRelative);
2693
+ var reservedRuntimePorts = (entrypoint) => entrypoint.runtime === CONTAINER_RUNTIME ? [] : entrypoint.runtime.startsWith("php-") ? [8080, 9e3] : [8080];
2694
+ var parseContainers = import_effect22.Effect.fn("parseContainers")(function* (config, projectFolder, fileEntrypoints, dockerFunctions) {
2695
+ const warnings = [];
2696
+ let containers = {};
2697
+ if (config.compose != null) {
2698
+ const imported = yield* readComposeContainers(config.compose, projectFolder);
2699
+ containers = imported.containers;
2700
+ warnings.push(...imported.warnings);
2701
+ }
2702
+ containers = { ...containers, ...config.containers ?? {} };
2703
+ for (const { file, name, container } of dockerFunctions) {
2704
+ if (containers[name] !== void 0) {
2705
+ return yield* new ContainerConfigError({
2706
+ message: `Function '${file}' runs as container '${name}', which 'containers' or the Compose file already declares. Rename one of them.`,
2707
+ containerName: name
2708
+ });
2709
+ }
2710
+ containers[name] = container;
2711
+ }
2712
+ const result = yield* normalizeContainers(containers, projectFolder);
2713
+ warnings.push(...result.warnings);
2714
+ for (const entrypoint of fileEntrypoints) {
2715
+ for (const port of reservedRuntimePorts(entrypoint)) {
2716
+ const sidecar = result.sidecars.find((candidate) => candidate.port === port);
2717
+ if (sidecar !== void 0) {
2718
+ return yield* new ContainerConfigError({
2719
+ message: `Sidecar '${sidecar.name}' listens on port ${port}, which the ${entrypoint.runtime} runtime of '${entrypoint.path}' uses inside the same microVM. Choose another port.`,
2720
+ containerName: sidecar.name
2721
+ });
1823
2722
  }
1824
2723
  }
1825
- return result;
1826
2724
  }
1827
- );
1828
- var V4ConfigParser = class extends import_effect19.Effect.Service()("V4ConfigParser", {
2725
+ return { ...result, warnings };
2726
+ });
2727
+ var V4ConfigParser = class extends import_effect22.Effect.Service()("V4ConfigParser", {
1829
2728
  accessors: true,
1830
- effect: import_effect19.Effect.succeed({
2729
+ effect: import_effect22.Effect.succeed({
1831
2730
  /**
1832
2731
  * Parses a ConfigV4 into a NormalizedConfig.
1833
2732
  *
1834
2733
  * @param config - The raw V4 config object
1835
2734
  * @param projectFolder - Absolute path to the project root
1836
2735
  */
1837
- parse: import_effect19.Effect.fn("V4ConfigParser.parse")(function* (config, projectFolder) {
1838
- const entrypoints = yield* parseEntrypoints(config, projectFolder);
2736
+ parse: import_effect22.Effect.fn("V4ConfigParser.parse")(function* (config, projectFolder) {
2737
+ const { entrypoints: fileEntrypoints, dockerFunctions } = yield* parseEntrypoints(config, projectFolder);
2738
+ const containers = yield* parseContainers(config, projectFolder, fileEntrypoints, dockerFunctions);
2739
+ const entrypoints = [...fileEntrypoints, ...containers.entrypoints];
1839
2740
  const assets = yield* collectAssets(config, projectFolder);
2741
+ const dockerFunctionPaths = new Map(
2742
+ dockerFunctions.map(({ file, name }) => [file, containerEntrypointPath(name)])
2743
+ );
2744
+ const routes = (config.routes ?? []).map(
2745
+ (route) => mapRoute(
2746
+ {
2747
+ ...route,
2748
+ destination: dockerFunctionPaths.get(normalizeRouteDestination(route.destination)) ?? route.destination
2749
+ },
2750
+ entrypoints
2751
+ )
2752
+ );
2753
+ const warnings = [...containers.warnings];
1840
2754
  return {
1841
2755
  regions: resolveRegions(config.regions),
1842
2756
  assets: {
@@ -1849,10 +2763,11 @@ var V4ConfigParser = class extends import_effect19.Effect.Service()("V4ConfigPar
1849
2763
  commands: config.build_commands ?? [],
1850
2764
  images: normalizeImagePolicy(config.images),
1851
2765
  services: normalizeServices(config.services),
2766
+ ...containers.sidecars.length > 0 && { sidecars: containers.sidecars },
1852
2767
  entrypoints,
1853
2768
  errors: [],
1854
- warnings: [],
1855
- routes: (config.routes ?? []).map((route) => mapRoute(route, entrypoints))
2769
+ warnings,
2770
+ routes
1856
2771
  };
1857
2772
  })
1858
2773
  })
@@ -1860,7 +2775,7 @@ var V4ConfigParser = class extends import_effect19.Effect.Service()("V4ConfigPar
1860
2775
  };
1861
2776
 
1862
2777
  // src/services/vercel-build-output-parser.ts
1863
- var import_platform11 = require("@effect/platform");
2778
+ var import_platform13 = require("@effect/platform");
1864
2779
 
1865
2780
  // ../commons/src/deep-merge.ts
1866
2781
  var deepMerge = (target, ...partials) => {
@@ -1889,7 +2804,7 @@ var deepMerge = (target, ...partials) => {
1889
2804
  };
1890
2805
 
1891
2806
  // src/services/vercel-build-output-parser.ts
1892
- var import_effect20 = require("effect");
2807
+ var import_effect23 = require("effect");
1893
2808
 
1894
2809
  // src/build-output-v3/determine-repo-root.ts
1895
2810
  var import_path = __toESM(require("path"));
@@ -1985,11 +2900,11 @@ var translateVercelRuntime = (runtime) => {
1985
2900
  var CONFIG_PATH = ".vercel/output/config.json";
1986
2901
  var FUNCTIONS_FOLDER = ".vercel/output/functions";
1987
2902
  var FUNCTION_CONFIGS_PATTERN = `${FUNCTIONS_FOLDER}/**/*.func/.vc-config.json`;
1988
- var VercelBuildOutputParser = class extends import_effect20.Effect.Service()("VercelBuildOutputParser", {
1989
- effect: import_effect20.Effect.gen(function* () {
2903
+ var VercelBuildOutputParser = class extends import_effect23.Effect.Service()("VercelBuildOutputParser", {
2904
+ effect: import_effect23.Effect.gen(function* () {
1990
2905
  const rawConfigReader = yield* RawConfigReader;
1991
- const pathService = yield* import_platform11.Path.Path;
1992
- const parse = import_effect20.Effect.fn("VercelBuildOutputParser.parse")(function* (parseResult, projectFolder) {
2906
+ const pathService = yield* import_platform13.Path.Path;
2907
+ const parse = import_effect23.Effect.fn("VercelBuildOutputParser.parse")(function* (parseResult, projectFolder) {
1993
2908
  const config = yield* rawConfigReader.readConfigFile(pathService.join(projectFolder, CONFIG_PATH));
1994
2909
  if (!config || typeof config.version !== "number" || config.version < 3) {
1995
2910
  return parseResult;
@@ -2001,8 +2916,8 @@ var VercelBuildOutputParser = class extends import_effect20.Effect.Service()("Ve
2001
2916
  merged.regions = [...new Set(merged.regions)];
2002
2917
  return merged;
2003
2918
  });
2004
- const loadFunctions = import_effect20.Effect.fn("VercelBuildOutputParser.loadFunctions")(function* (config, parseResult, projectFolder) {
2005
- const functionConfigs = yield* import_effect20.Effect.tryPromise({
2919
+ const loadFunctions = import_effect23.Effect.fn("VercelBuildOutputParser.loadFunctions")(function* (config, parseResult, projectFolder) {
2920
+ const functionConfigs = yield* import_effect23.Effect.tryPromise({
2006
2921
  try: () => getFilesForPattern(FUNCTION_CONFIGS_PATTERN, projectFolder),
2007
2922
  catch: (error) => new ConfigFileParseError({
2008
2923
  message: `Failed to scan for function configs: ${error instanceof Error ? error.message : String(error)}`,
@@ -2288,17 +3203,22 @@ var schema_default = {
2288
3203
  minimum: 1
2289
3204
  },
2290
3205
  runtime: {
2291
- $ref: "#/$defs/runtimes",
2292
- description: "The runtime to use for the function."
3206
+ anyOf: [{ $ref: "#/$defs/runtimes" }, { const: "docker" }],
3207
+ description: "The runtime to use for the function. `docker` builds the matched file as a Dockerfile, with its directory as the build context, and runs the image as the function."
3208
+ },
3209
+ port: {
3210
+ type: "integer",
3211
+ minimum: 1,
3212
+ maximum: 65535,
3213
+ description: "TCP port the image listens on. Only for `runtime: docker`. Defaults to the first exposed port of the image, then 8080."
2293
3214
  },
2294
3215
  streaming: {
2295
3216
  type: "boolean",
2296
3217
  description: "Enable function response streaming. When omitted, Node and Bun runtimes stream by default."
2297
3218
  },
2298
3219
  schedule: {
2299
- type: "string",
2300
- description: "An expression to schedule the function to run at specific times.",
2301
- pattern: "^(?:rate[(](?:(?:1[ ]+(hour|minute|day))|(?:[0-9]+[ ]+(hours|minutes|days)))[)])|(?:cron[(](?:(?:(?:[0-5]?[0-9])|[*])(?:(?:[-](?:(?:[0-5]?[0-9])|[*]))|(?:[/][0-9]+))?(?:[,](?:(?:[0-5]?[0-9])|[*])(?:(?:[-](?:(?:[0-5]?[0-9])|[*]))|(?:[/][0-9]+))?)*)[ ]+(?:(?:(?:[0-2]?[0-9])|[*])(?:(?:[-](?:(?:[0-2]?[0-9])|[*]))|(?:[/][0-9]+))?(?:[,](?:(?:[0-2]?[0-9])|[*])(?:(?:[-](?:(?:[0-2]?[0-9])|[*]))|(?:[/][0-9]+))?)*)[ ]+(?:(?:[?][ ]+(?:(?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:(?:[-](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:[/][0-9]+)?)|(?:[/][0-9]+))?(?:[,](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:(?:[-](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:[/][0-9]+)?)|(?:[/][0-9]+))?)*)[ ]+(?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))[#][0-5])|(?:(?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))L?)|[L*])(?:(?:[-](?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))L?)|[L*]))|(?:[/][0-9]+))?(?:[,](?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))L?)|[L*])(?:(?:[-](?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))L?)|[L*]))|(?:[/][0-9]+))?)*)))|(?:(?:(?:(?:(?:[1-3]?[0-9])W?)|LW|[L*])(?:(?:[-](?:(?:(?:[1-3]?[0-9])W?)|LW|[L*]))|(?:[/][0-9]+))?(?:[,](?:(?:(?:[1-3]?[0-9])W?)|LW|[L*])(?:(?:[-](?:(?:(?:[1-3]?[0-9])W?)|LW|[L*]))|(?:[/][0-9]+))?)*)[ ]+(?:(?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:(?:[-](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:[/][0-9]+)?)|(?:[/][0-9]+))?(?:[,](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:(?:[-](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:[/][0-9]+)?)|(?:[/][0-9]+))?)*)[ ]+[?]))[ ]+(?:(?:(?:[12][0-9]{3})|[*])(?:(?:[-](?:(?:[12][0-9]{3})|[*]))|(?:[/][0-9]+))?(?:[,](?:(?:[12][0-9]{3})|[*])(?:(?:[-](?:(?:[12][0-9]{3})|[*]))|(?:[/][0-9]+))?)*)[)])$"
3220
+ $ref: "#/$defs/schedule",
3221
+ description: "An expression to schedule the function to run at specific times."
2302
3222
  },
2303
3223
  symlinks: {
2304
3224
  type: "object",
@@ -2390,6 +3310,151 @@ var schema_default = {
2390
3310
  }
2391
3311
  }
2392
3312
  },
3313
+ compose: {
3314
+ type: ["string", "null"],
3315
+ minLength: 1,
3316
+ maxLength: 1024,
3317
+ description: "Project-relative path of a Compose file whose services are imported as containers. Entries in `containers` win over imported services of the same name."
3318
+ },
3319
+ containers: {
3320
+ type: ["object", "null"],
3321
+ description: "Container images to run, keyed by name. A container runs as a function unless `sidecar` is true; routes target it with `destination: container:<name>`. A sidecar runs next to every function instance and answers at `<name>:<port>`.",
3322
+ maxProperties: 16,
3323
+ propertyNames: {
3324
+ pattern: "^[a-z][a-z0-9_-]{0,62}$",
3325
+ not: {
3326
+ enum: ["localhost"]
3327
+ }
3328
+ },
3329
+ additionalProperties: {
3330
+ type: "object",
3331
+ additionalProperties: false,
3332
+ oneOf: [
3333
+ {
3334
+ required: ["image"]
3335
+ },
3336
+ {
3337
+ required: ["build"]
3338
+ }
3339
+ ],
3340
+ properties: {
3341
+ image: {
3342
+ type: "string",
3343
+ minLength: 1,
3344
+ maxLength: 512,
3345
+ pattern: "^[^\\s]+$",
3346
+ description: "Registry image reference, e.g. `redis:7-alpine` or `ghcr.io/acme/api:1.4`."
3347
+ },
3348
+ build: {
3349
+ description: "Build context directory, or the full build settings, for a Dockerfile in the repository.",
3350
+ oneOf: [
3351
+ {
3352
+ type: "string",
3353
+ minLength: 1,
3354
+ maxLength: 1024
3355
+ },
3356
+ {
3357
+ type: "object",
3358
+ additionalProperties: false,
3359
+ properties: {
3360
+ context: {
3361
+ type: "string",
3362
+ minLength: 1,
3363
+ maxLength: 1024
3364
+ },
3365
+ dockerfile: {
3366
+ type: "string",
3367
+ minLength: 1,
3368
+ maxLength: 1024
3369
+ },
3370
+ target: {
3371
+ type: "string",
3372
+ minLength: 1,
3373
+ maxLength: 128
3374
+ },
3375
+ args: {
3376
+ type: "object",
3377
+ maxProperties: 100,
3378
+ additionalProperties: {
3379
+ type: "string",
3380
+ maxLength: 65536
3381
+ }
3382
+ }
3383
+ }
3384
+ }
3385
+ ]
3386
+ },
3387
+ sidecar: {
3388
+ type: "boolean",
3389
+ description: "Run next to every function instance instead of serving routes. Defaults to false."
3390
+ },
3391
+ port: {
3392
+ type: "integer",
3393
+ minimum: 1,
3394
+ maximum: 65535,
3395
+ description: "TCP port the container listens on. Defaults to the first exposed port of the image, then 8080."
3396
+ },
3397
+ entrypoint: {
3398
+ $ref: "#/$defs/stringOrStringArray",
3399
+ description: "Replaces the image ENTRYPOINT. A string is split into words the way Compose splits it."
3400
+ },
3401
+ command: {
3402
+ $ref: "#/$defs/stringOrStringArray",
3403
+ description: "Replaces the image CMD. A string is split into words the way Compose splits it."
3404
+ },
3405
+ env: {
3406
+ type: "object",
3407
+ maxProperties: 100,
3408
+ additionalProperties: {
3409
+ type: "string",
3410
+ maxLength: 65536
3411
+ },
3412
+ description: "Environment variables baked into the container, merged over the image ENV."
3413
+ },
3414
+ working_dir: {
3415
+ type: "string",
3416
+ minLength: 1,
3417
+ maxLength: 1024,
3418
+ pattern: "^/",
3419
+ description: "Replaces the image WORKDIR. Must be absolute."
3420
+ },
3421
+ user: {
3422
+ oneOf: [
3423
+ {
3424
+ type: "string",
3425
+ minLength: 1,
3426
+ maxLength: 256
3427
+ },
3428
+ {
3429
+ type: "integer",
3430
+ minimum: 0
3431
+ }
3432
+ ],
3433
+ description: "Replaces the image USER: a name, a uid, `name:group` or `uid:gid`. A bare uid may be a number."
3434
+ },
3435
+ memory: {
3436
+ type: "integer",
3437
+ minimum: 128,
3438
+ maximum: 3009,
3439
+ description: "Memory in MB. Defaults to 512 for a container function and 256 for a sidecar."
3440
+ },
3441
+ max_duration: {
3442
+ type: "number",
3443
+ minimum: 1,
3444
+ maximum: 28800,
3445
+ description: "Maximum lifetime in seconds of one request. Container functions only."
3446
+ },
3447
+ streaming: {
3448
+ type: "boolean",
3449
+ description: "Stream responses. Defaults to true. Container functions only."
3450
+ },
3451
+ schedule: {
3452
+ $ref: "#/$defs/schedule",
3453
+ description: "An expression to run the container function at specific times. Container functions only."
3454
+ }
3455
+ }
3456
+ }
3457
+ },
2393
3458
  services: {
2394
3459
  type: "object",
2395
3460
  description: "Managed services to provision for the deployment environment.",
@@ -2540,11 +3605,179 @@ var schema_default = {
2540
3605
  }
2541
3606
  }
2542
3607
  }
3608
+ },
3609
+ queues: {
3610
+ type: "object",
3611
+ description: "Queues to provision in the deployment environment, keyed by name. Use null for a pull queue with default settings. Queues you stop declaring are kept, with their messages.",
3612
+ propertyNames: {
3613
+ maxLength: 255,
3614
+ pattern: "^[A-Za-z0-9_][A-Za-z0-9_.~:@+=-]*$"
3615
+ },
3616
+ additionalProperties: {
3617
+ oneOf: [
3618
+ {
3619
+ type: "null"
3620
+ },
3621
+ {
3622
+ type: "object",
3623
+ additionalProperties: false,
3624
+ properties: {
3625
+ consumer: {
3626
+ type: ["string", "null"],
3627
+ pattern: "^/(?!/)\\S*$",
3628
+ maxLength: 2048,
3629
+ description: "Deployment-relative path that receives each message as a signed POST, for example /api/queues/emails. Omit it for a pull queue that you drain with receive."
3630
+ },
3631
+ visibilityTimeout: {
3632
+ $ref: "#/$defs/queueDuration",
3633
+ description: "How long a push delivery may run, or how long a received message stays leased. Defaults to 60 seconds, at most 12 hours."
3634
+ },
3635
+ retention: {
3636
+ $ref: "#/$defs/queueDuration",
3637
+ description: "How long an undelivered message is kept. Defaults to 4 days, at most 14 days."
3638
+ },
3639
+ maxAttempts: {
3640
+ type: "integer",
3641
+ minimum: 1,
3642
+ maximum: 100,
3643
+ description: "Deliveries per message before it moves to the dead-letter list. Defaults to 10."
3644
+ },
3645
+ retryBackoff: {
3646
+ type: "object",
3647
+ additionalProperties: false,
3648
+ description: "Exponential backoff between failed deliveries. Defaults to 5 seconds, doubling up to 15 minutes.",
3649
+ properties: {
3650
+ min: {
3651
+ $ref: "#/$defs/queueDuration"
3652
+ },
3653
+ max: {
3654
+ $ref: "#/$defs/queueDuration"
3655
+ }
3656
+ }
3657
+ },
3658
+ deduplicationWindow: {
3659
+ $ref: "#/$defs/queueDuration",
3660
+ description: "How long a deduplication key is remembered. Defaults to 24 hours, at most 7 days."
3661
+ },
3662
+ concurrency: {
3663
+ type: ["integer", "null"],
3664
+ minimum: 1,
3665
+ maximum: 1e3,
3666
+ description: "Most push deliveries in flight at once. Defaults to 100."
3667
+ },
3668
+ rateLimit: {
3669
+ oneOf: [
3670
+ {
3671
+ type: "null"
3672
+ },
3673
+ {
3674
+ type: "object",
3675
+ required: ["count"],
3676
+ additionalProperties: false,
3677
+ properties: {
3678
+ count: {
3679
+ type: "integer",
3680
+ minimum: 1,
3681
+ maximum: 1e6
3682
+ },
3683
+ period: {
3684
+ $ref: "#/$defs/queueDuration"
3685
+ }
3686
+ }
3687
+ }
3688
+ ],
3689
+ description: "Most deliveries started per period. The period defaults to one second."
3690
+ },
3691
+ deadLetter: {
3692
+ type: "boolean",
3693
+ description: "Keep messages that run out of attempts so you can inspect and redrive them. Defaults to true."
3694
+ },
3695
+ schedules: {
3696
+ type: "object",
3697
+ description: "Cron schedules that send a message into this queue, keyed by schedule name.",
3698
+ propertyNames: {
3699
+ maxLength: 128,
3700
+ pattern: "^[A-Za-z0-9_][A-Za-z0-9_.-]*$"
3701
+ },
3702
+ additionalProperties: {
3703
+ type: "object",
3704
+ required: ["cron"],
3705
+ additionalProperties: false,
3706
+ properties: {
3707
+ cron: {
3708
+ type: "string",
3709
+ minLength: 1,
3710
+ maxLength: 255,
3711
+ description: "Five-field cron expression, or a macro such as @hourly or @daily."
3712
+ },
3713
+ timezone: {
3714
+ type: "string",
3715
+ description: "IANA time zone the expression is evaluated in. Defaults to UTC."
3716
+ },
3717
+ body: {
3718
+ description: "Message body. Strings are sent as is; any other value is sent as JSON."
3719
+ },
3720
+ contentType: {
3721
+ type: "string",
3722
+ description: "Content type of the body. Defaults to text/plain for a string body and application/json otherwise."
3723
+ },
3724
+ headers: {
3725
+ type: "object",
3726
+ additionalProperties: {
3727
+ type: "string"
3728
+ },
3729
+ description: "Headers delivered with every message from this schedule."
3730
+ },
3731
+ enabled: {
3732
+ type: "boolean",
3733
+ description: "Set to false to keep the schedule without sending. Defaults to true."
3734
+ }
3735
+ }
3736
+ }
3737
+ }
3738
+ }
3739
+ }
3740
+ ]
3741
+ }
2543
3742
  }
2544
3743
  }
2545
3744
  }
2546
3745
  },
2547
3746
  $defs: {
3747
+ schedule: {
3748
+ type: "string",
3749
+ pattern: "^(?:rate[(](?:(?:1[ ]+(hour|minute|day))|(?:[0-9]+[ ]+(hours|minutes|days)))[)])|(?:cron[(](?:(?:(?:[0-5]?[0-9])|[*])(?:(?:[-](?:(?:[0-5]?[0-9])|[*]))|(?:[/][0-9]+))?(?:[,](?:(?:[0-5]?[0-9])|[*])(?:(?:[-](?:(?:[0-5]?[0-9])|[*]))|(?:[/][0-9]+))?)*)[ ]+(?:(?:(?:[0-2]?[0-9])|[*])(?:(?:[-](?:(?:[0-2]?[0-9])|[*]))|(?:[/][0-9]+))?(?:[,](?:(?:[0-2]?[0-9])|[*])(?:(?:[-](?:(?:[0-2]?[0-9])|[*]))|(?:[/][0-9]+))?)*)[ ]+(?:(?:[?][ ]+(?:(?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:(?:[-](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:[/][0-9]+)?)|(?:[/][0-9]+))?(?:[,](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:(?:[-](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:[/][0-9]+)?)|(?:[/][0-9]+))?)*)[ ]+(?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))[#][0-5])|(?:(?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))L?)|[L*])(?:(?:[-](?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))L?)|[L*]))|(?:[/][0-9]+))?(?:[,](?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))L?)|[L*])(?:(?:[-](?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))L?)|[L*]))|(?:[/][0-9]+))?)*)))|(?:(?:(?:(?:(?:[1-3]?[0-9])W?)|LW|[L*])(?:(?:[-](?:(?:(?:[1-3]?[0-9])W?)|LW|[L*]))|(?:[/][0-9]+))?(?:[,](?:(?:(?:[1-3]?[0-9])W?)|LW|[L*])(?:(?:[-](?:(?:(?:[1-3]?[0-9])W?)|LW|[L*]))|(?:[/][0-9]+))?)*)[ ]+(?:(?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:(?:[-](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:[/][0-9]+)?)|(?:[/][0-9]+))?(?:[,](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:(?:[-](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:[/][0-9]+)?)|(?:[/][0-9]+))?)*)[ ]+[?]))[ ]+(?:(?:(?:[12][0-9]{3})|[*])(?:(?:[-](?:(?:[12][0-9]{3})|[*]))|(?:[/][0-9]+))?(?:[,](?:(?:[12][0-9]{3})|[*])(?:(?:[-](?:(?:[12][0-9]{3})|[*]))|(?:[/][0-9]+))?)*)[)])$"
3750
+ },
3751
+ stringOrStringArray: {
3752
+ oneOf: [
3753
+ {
3754
+ type: "string",
3755
+ minLength: 1,
3756
+ maxLength: 4096
3757
+ },
3758
+ {
3759
+ type: "array",
3760
+ maxItems: 256,
3761
+ items: {
3762
+ type: "string",
3763
+ maxLength: 4096
3764
+ }
3765
+ }
3766
+ ]
3767
+ },
3768
+ queueDuration: {
3769
+ oneOf: [
3770
+ {
3771
+ type: "integer",
3772
+ minimum: 0
3773
+ },
3774
+ {
3775
+ type: "string",
3776
+ pattern: "^[0-9]+(\\.[0-9]+)?(s|m|h|d)$"
3777
+ }
3778
+ ],
3779
+ description: "A duration in seconds, or a string such as 30s, 10m, 1.5h or 4d."
3780
+ },
2548
3781
  runtimes: {
2549
3782
  type: "string",
2550
3783
  enum: ["php-84", "php-83", "php-81", "node-22", "node-20", "node-18", "node-16", "bun-1"]
@@ -2601,7 +3834,7 @@ var schema_default = {
2601
3834
  };
2602
3835
 
2603
3836
  // src/parse-config.ts
2604
- var parseConfigRaw = import_effect21.Effect.fn("parseConfigRaw")(function* (filePath, projectFolder) {
3837
+ var parseConfigRaw = import_effect24.Effect.fn("parseConfigRaw")(function* (filePath, projectFolder) {
2605
3838
  const rawReader = yield* RawConfigReader;
2606
3839
  const validator = yield* SchemaValidator;
2607
3840
  const v4Parser = yield* V4ConfigParser;
@@ -2612,7 +3845,7 @@ var parseConfigRaw = import_effect21.Effect.fn("parseConfigRaw")(function* (file
2612
3845
  };
2613
3846
  const schema = schemaMap[version];
2614
3847
  if (!schema) {
2615
- return yield* import_effect21.Effect.fail(
3848
+ return yield* import_effect24.Effect.fail(
2616
3849
  new ConfigVersionError({
2617
3850
  message: `Config file at ${filePath} has an unsupported version: ${version}`,
2618
3851
  filePath,
@@ -2623,14 +3856,42 @@ var parseConfigRaw = import_effect21.Effect.fn("parseConfigRaw")(function* (file
2623
3856
  yield* validator.validate(parsed, schema, filePath);
2624
3857
  return yield* v4Parser.parse(parsed, projectFolder);
2625
3858
  });
2626
- var parseConfig = import_effect21.Effect.fn("parseConfig")(function* (filePath, projectFolder) {
3859
+ var parseConfig = import_effect24.Effect.fn("parseConfig")(function* (filePath, projectFolder) {
2627
3860
  const parseResult = yield* parseConfigRaw(filePath, projectFolder);
2628
3861
  return yield* postProcessConfig(parseResult, projectFolder);
2629
3862
  });
2630
- var postProcessConfig = import_effect21.Effect.fn("postProcessConfig")(function* (config, projectFolder) {
3863
+ var postProcessConfig = import_effect24.Effect.fn("postProcessConfig")(function* (config, projectFolder) {
2631
3864
  const vercelParser = yield* VercelBuildOutputParser;
2632
3865
  let result = yield* vercelParser.parse(config, projectFolder);
2633
3866
  const hasAssets = (result.assets?.paths?.length ?? 0) > 0 || (result.assets?.prefixes?.length ?? 0) > 0 || (result.assets?.manifests?.length ?? 0) > 0;
3867
+ const containerFunctions = result.entrypoints.filter((entrypoint) => entrypoint.runtime === CONTAINER_RUNTIME);
3868
+ const [onlyFunction] = result.entrypoints;
3869
+ if (onlyFunction !== void 0 && onlyFunction.runtime === CONTAINER_RUNTIME && result.entrypoints.length === 1 && result.routes.length === 0 && !hasAssets) {
3870
+ result = {
3871
+ ...result,
3872
+ routes: [
3873
+ {
3874
+ path: "/*",
3875
+ destination: onlyFunction.path,
3876
+ handler: onlyFunction.streaming === true ? "SERVERLESS_FUNCTION_STREAMING" : "SERVERLESS_FUNCTION",
3877
+ methods: ["ANY"],
3878
+ headers: {}
3879
+ }
3880
+ ]
3881
+ };
3882
+ } else {
3883
+ for (const entrypoint of containerFunctions) {
3884
+ const routed = result.routes.some((route) => route.destination.replace(/^\/+/, "") === entrypoint.path);
3885
+ if (!routed && entrypoint.schedule === void 0) {
3886
+ result.warnings.push(
3887
+ `Container function '${entrypoint.displayName ?? entrypoint.path}' has no route and no schedule, so nothing reaches it. Add a route with destination '${entrypoint.path}'.`
3888
+ );
3889
+ }
3890
+ }
3891
+ }
3892
+ if ((result.sidecars?.length ?? 0) > 0 && result.entrypoints.length === 0) {
3893
+ result.warnings.push("Sidecars run next to functions, and this deployment has none, so no sidecar will start.");
3894
+ }
2634
3895
  if (result.entrypoints.length === 0 && !hasAssets && result.routes.length === 0) {
2635
3896
  result.errors.push(
2636
3897
  "The current project config does not resolve to any functions, assets or routes and can not be deployed."
@@ -2656,19 +3917,22 @@ var RUNTIME_DEFINITIONS = [
2656
3917
  var AVAILABLE_RUNTIMES = [...RUNTIME_DEFINITIONS];
2657
3918
 
2658
3919
  // src/services/index.ts
2659
- var import_effect22 = require("effect");
2660
- var BaseLayers = import_effect22.Layer.mergeAll(
3920
+ var import_effect25 = require("effect");
3921
+ var BaseLayers = import_effect25.Layer.mergeAll(
2661
3922
  ConfigModuleLoader.Default,
2662
3923
  RawConfigReader.Default,
2663
3924
  SchemaValidator.Default,
2664
3925
  V4ConfigParser.Default
2665
3926
  );
2666
- var NetworkConfigLive = VercelBuildOutputParser.Default.pipe(import_effect22.Layer.provideMerge(BaseLayers));
3927
+ var NetworkConfigLive = VercelBuildOutputParser.Default.pipe(import_effect25.Layer.provideMerge(BaseLayers));
2667
3928
  // Annotate the CommonJS export names for ESM import in node:
2668
3929
  0 && (module.exports = {
2669
3930
  ALLOWED_CONFIG_NAMES,
2670
3931
  AVAILABLE_REGIONS,
2671
3932
  AVAILABLE_RUNTIMES,
3933
+ COMPOSE_FILE_NAMES,
3934
+ CONTAINER_ENTRYPOINT_PREFIX,
3935
+ CONTAINER_RUNTIME,
2672
3936
  ConfigFileEmptyError,
2673
3937
  ConfigFileNotFoundError,
2674
3938
  ConfigFileParseError,
@@ -2676,7 +3940,10 @@ var NetworkConfigLive = VercelBuildOutputParser.Default.pipe(import_effect22.Lay
2676
3940
  ConfigModuleLoader,
2677
3941
  ConfigSchemaValidationError,
2678
3942
  ConfigVersionError,
3943
+ ContainerConfigError,
3944
+ DEFAULT_CONTAINER_MEMORY_MB,
2679
3945
  DEFAULT_FUNCTION_DURATION_SECONDS,
3946
+ DEFAULT_SIDECAR_MEMORY_MB,
2680
3947
  FRAMEWORK_DEFINITIONS,
2681
3948
  FrameworkNotDetectedError,
2682
3949
  FunctionConfigError,
@@ -2693,6 +3960,7 @@ var NetworkConfigLive = VercelBuildOutputParser.Default.pipe(import_effect22.Lay
2693
3960
  GigadriveNextServerDescriptorSchema,
2694
3961
  GigadriveNextStaticAssetPrefixSchema,
2695
3962
  MAX_FUNCTION_DURATION_SECONDS,
3963
+ MAX_SIDECARS,
2696
3964
  ManifestNotFoundError,
2697
3965
  ManifestParseError,
2698
3966
  ManifestReadError,
@@ -2704,19 +3972,27 @@ var NetworkConfigLive = VercelBuildOutputParser.Default.pipe(import_effect22.Lay
2704
3972
  StaticAssetManifestV1Schema,
2705
3973
  V4ConfigParser,
2706
3974
  VercelBuildOutputParser,
3975
+ collectAssetFiles,
3976
+ composeDeclaresApp,
3977
+ containerEntrypointPath,
2707
3978
  defineConfig,
3979
+ detectContainerProject,
2708
3980
  detectFramework,
2709
3981
  detectPackageManager,
3982
+ dockerFunctionContainerName,
2710
3983
  filterFunctionsFromAssets,
2711
3984
  generateConfig,
2712
3985
  getFunctionSettings,
2713
3986
  mergeWithFrameworkDefaults,
3987
+ normalizeContainers,
2714
3988
  parseConfig,
2715
3989
  parseConfigRaw,
2716
3990
  parseGigadriveNextBuildManifest,
2717
3991
  parseGigadriveNextPrerenderManifest,
2718
3992
  parseStaticAssetManifest,
2719
3993
  postProcessConfig,
2720
- readDependencies
3994
+ readComposeContainers,
3995
+ readDependencies,
3996
+ splitCommandWords
2721
3997
  });
2722
3998
  //# sourceMappingURL=index.js.map