@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/define-config.d.mts +156 -2
- package/dist/define-config.d.ts +156 -2
- package/dist/index.d.mts +513 -91
- package/dist/index.d.ts +513 -91
- package/dist/index.js +1724 -448
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1715 -454
- package/dist/index.mjs.map +1 -1
- package/dist/v4/schema.json +323 -5
- package/package.json +2 -2
- package/src/v4/schema.json +323 -5
package/dist/index.mjs
CHANGED
|
@@ -55,16 +55,862 @@ var StaticAssetManifestV1Schema = Schema2.mutable(
|
|
|
55
55
|
).pipe(Schema2.filter(({ assets }) => new Set(assets.map(({ path: path2 }) => path2)).size === assets.length));
|
|
56
56
|
var parseStaticAssetManifest = (content) => decodeJson(StaticAssetManifestV1Schema, content);
|
|
57
57
|
|
|
58
|
+
// src/collect-asset-files.ts
|
|
59
|
+
import { FileSystem, Path } from "@effect/platform";
|
|
60
|
+
import { Effect } from "effect";
|
|
61
|
+
var MAX_ASSET_DEPTH = 100;
|
|
62
|
+
var DISALLOWED_ASSET_EXTENSIONS = [".htaccess", ".htpasswd"];
|
|
63
|
+
var collectFilesRecursively = Effect.fn("collectFilesRecursively")(
|
|
64
|
+
function* (basePath, relativePath = "", depth = 0) {
|
|
65
|
+
if (depth > MAX_ASSET_DEPTH) return [];
|
|
66
|
+
const fs = yield* FileSystem.FileSystem;
|
|
67
|
+
const pathSvc = yield* Path.Path;
|
|
68
|
+
const currentPath = relativePath ? pathSvc.join(basePath, relativePath) : basePath;
|
|
69
|
+
const entries = yield* fs.readDirectory(currentPath).pipe(Effect.catchAll(() => Effect.succeed([])));
|
|
70
|
+
const result = [];
|
|
71
|
+
for (const name of entries) {
|
|
72
|
+
const fullPath = pathSvc.join(currentPath, name);
|
|
73
|
+
const entryRelative = relativePath ? pathSvc.join(relativePath, name) : name;
|
|
74
|
+
const stat = yield* fs.stat(fullPath).pipe(Effect.catchAll(() => Effect.succeed(null)));
|
|
75
|
+
if (!stat) continue;
|
|
76
|
+
if (stat.type === "Directory") {
|
|
77
|
+
const nested = yield* collectFilesRecursively(basePath, entryRelative, depth + 1);
|
|
78
|
+
result.push(...nested);
|
|
79
|
+
} else {
|
|
80
|
+
result.push(entryRelative);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
var collectAssetFiles = Effect.fn("collectAssetFiles")(function* (projectFolder, assetsDir) {
|
|
87
|
+
const fs = yield* FileSystem.FileSystem;
|
|
88
|
+
const pathService = yield* Path.Path;
|
|
89
|
+
const assetsPath = pathService.join(projectFolder, assetsDir);
|
|
90
|
+
const stat = yield* fs.stat(assetsPath).pipe(Effect.catchAll(() => Effect.succeed(null)));
|
|
91
|
+
if (!stat || stat.type !== "Directory") return [];
|
|
92
|
+
const files = yield* collectFilesRecursively(assetsPath);
|
|
93
|
+
return files.map((file) => file.replaceAll("\\", "/")).filter((file) => !DISALLOWED_ASSET_EXTENSIONS.some((extension) => file.toLowerCase().endsWith(extension))).sort();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// src/containers.ts
|
|
97
|
+
import { FileSystem as FileSystem2, Path as Path2 } from "@effect/platform";
|
|
98
|
+
import { Effect as Effect2, Option as Option2 } from "effect";
|
|
99
|
+
import { posix } from "path";
|
|
100
|
+
import { parse as parseYaml, YAMLError } from "yaml";
|
|
101
|
+
|
|
102
|
+
// src/errors.ts
|
|
103
|
+
import { Schema as Schema3 } from "effect";
|
|
104
|
+
var ConfigFileNotFoundError = class extends Schema3.TaggedError()("ConfigFileNotFoundError", {
|
|
105
|
+
message: Schema3.String,
|
|
106
|
+
filePath: Schema3.String
|
|
107
|
+
}) {
|
|
108
|
+
};
|
|
109
|
+
var ConfigFileEmptyError = class extends Schema3.TaggedError()("ConfigFileEmptyError", {
|
|
110
|
+
message: Schema3.String,
|
|
111
|
+
filePath: Schema3.String
|
|
112
|
+
}) {
|
|
113
|
+
};
|
|
114
|
+
var ConfigFileParseError = class extends Schema3.TaggedError()("ConfigFileParseError", {
|
|
115
|
+
message: Schema3.String,
|
|
116
|
+
filePath: Schema3.String,
|
|
117
|
+
cause: Schema3.optional(Schema3.String)
|
|
118
|
+
}) {
|
|
119
|
+
};
|
|
120
|
+
var ConfigModuleLoadError = class extends Schema3.TaggedError()("ConfigModuleLoadError", {
|
|
121
|
+
message: Schema3.String,
|
|
122
|
+
filePath: Schema3.String,
|
|
123
|
+
cause: Schema3.optional(Schema3.String)
|
|
124
|
+
}) {
|
|
125
|
+
};
|
|
126
|
+
var ConfigVersionError = class extends Schema3.TaggedError()("ConfigVersionError", {
|
|
127
|
+
message: Schema3.String,
|
|
128
|
+
filePath: Schema3.String,
|
|
129
|
+
version: Schema3.optional(Schema3.Number)
|
|
130
|
+
}) {
|
|
131
|
+
};
|
|
132
|
+
var ConfigSchemaValidationError = class extends Schema3.TaggedError()(
|
|
133
|
+
"ConfigSchemaValidationError",
|
|
134
|
+
{
|
|
135
|
+
message: Schema3.String,
|
|
136
|
+
filePath: Schema3.String,
|
|
137
|
+
validationErrors: Schema3.Array(Schema3.String)
|
|
138
|
+
}
|
|
139
|
+
) {
|
|
140
|
+
};
|
|
141
|
+
var FunctionConfigError = class extends Schema3.TaggedError()("FunctionConfigError", {
|
|
142
|
+
message: Schema3.String,
|
|
143
|
+
functionPath: Schema3.String
|
|
144
|
+
}) {
|
|
145
|
+
};
|
|
146
|
+
var ContainerConfigError = class extends Schema3.TaggedError()("ContainerConfigError", {
|
|
147
|
+
message: Schema3.String,
|
|
148
|
+
containerName: Schema3.optional(Schema3.String),
|
|
149
|
+
filePath: Schema3.optional(Schema3.String)
|
|
150
|
+
}) {
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// src/normalized-config.ts
|
|
154
|
+
var DEFAULT_FUNCTION_DURATION_SECONDS = 30;
|
|
155
|
+
var MAX_FUNCTION_DURATION_SECONDS = 28800;
|
|
156
|
+
var CONTAINER_RUNTIME = "docker";
|
|
157
|
+
var CONTAINER_ENTRYPOINT_PREFIX = "container:";
|
|
158
|
+
var DEFAULT_CONTAINER_MEMORY_MB = 512;
|
|
159
|
+
var DEFAULT_SIDECAR_MEMORY_MB = 256;
|
|
160
|
+
var MAX_SIDECARS = 4;
|
|
161
|
+
|
|
162
|
+
// src/containers.ts
|
|
163
|
+
var COMPOSE_FILE_NAMES = ["compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml"];
|
|
164
|
+
var CONTAINER_NAME_PATTERN = /^[a-z][a-z0-9_-]{0,62}$/;
|
|
165
|
+
var CONTAINER_NAME_RULES = "1 to 63 lowercase letters, digits, '-' or '_', starting with a letter, and not 'localhost'";
|
|
166
|
+
var MIN_CONTAINER_MEMORY_MB = 128;
|
|
167
|
+
var MAX_CONTAINER_MEMORY_MB = 3009;
|
|
168
|
+
var MAX_CONTAINER_ENV_VARS = 100;
|
|
169
|
+
var MAX_CONTAINER_ENV_VALUE_LENGTH = 65536;
|
|
170
|
+
var containerEntrypointPath = (name) => `${CONTAINER_ENTRYPOINT_PREFIX}${name}`;
|
|
171
|
+
var toContainerName = (raw) => {
|
|
172
|
+
const cleaned = raw.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^[^a-z0-9]+/, "");
|
|
173
|
+
const name = (cleaned === "" || /^[a-z]/.test(cleaned) ? cleaned : `app-${cleaned}`).slice(0, 63);
|
|
174
|
+
return name === "" || name === "localhost" ? "app" : name;
|
|
175
|
+
};
|
|
176
|
+
var dockerFunctionContainerName = (file) => {
|
|
177
|
+
const { dir, base } = posix.parse(file);
|
|
178
|
+
const variant = base.replace(/^Dockerfile[.-]?/i, "").replace(/[.-]?Dockerfile$/i, "");
|
|
179
|
+
const raw = [posix.basename(dir), variant === base ? posix.parse(base).name : variant].filter((part) => part !== "").join("-");
|
|
180
|
+
return toContainerName(raw);
|
|
181
|
+
};
|
|
182
|
+
var splitCommandWords = (input) => {
|
|
183
|
+
const words = [];
|
|
184
|
+
let current = "";
|
|
185
|
+
let inWord = false;
|
|
186
|
+
let quote;
|
|
187
|
+
for (let index = 0; index < input.length; index += 1) {
|
|
188
|
+
const char = input[index];
|
|
189
|
+
if (quote === "'") {
|
|
190
|
+
if (char === "'") quote = void 0;
|
|
191
|
+
else current += char;
|
|
192
|
+
} else if (char === "\\" && index + 1 < input.length) {
|
|
193
|
+
index += 1;
|
|
194
|
+
current += input[index];
|
|
195
|
+
inWord = true;
|
|
196
|
+
} else if (quote === '"') {
|
|
197
|
+
if (char === '"') quote = void 0;
|
|
198
|
+
else current += char;
|
|
199
|
+
} else if (char === "'" || char === '"') {
|
|
200
|
+
quote = char;
|
|
201
|
+
inWord = true;
|
|
202
|
+
} else if (/\s/.test(char)) {
|
|
203
|
+
if (inWord) words.push(current);
|
|
204
|
+
current = "";
|
|
205
|
+
inWord = false;
|
|
206
|
+
} else {
|
|
207
|
+
current += char;
|
|
208
|
+
inWord = true;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (quote) throw new Error(`Unterminated ${quote} quote`);
|
|
212
|
+
if (inWord) words.push(current);
|
|
213
|
+
return words;
|
|
214
|
+
};
|
|
215
|
+
var normalizeProjectPath = (input) => {
|
|
216
|
+
if (posix.isAbsolute(input)) return void 0;
|
|
217
|
+
const normalized = posix.normalize(input.replace(/\\/g, "/")).replace(/\/+$/, "") || ".";
|
|
218
|
+
if (normalized === ".." || normalized.startsWith("../")) return void 0;
|
|
219
|
+
return normalized;
|
|
220
|
+
};
|
|
221
|
+
var resolveProjectRoot = (projectFolder) => Effect2.flatMap(
|
|
222
|
+
FileSystem2.FileSystem,
|
|
223
|
+
(fs) => fs.realPath(projectFolder).pipe(Effect2.orElseSucceed(() => projectFolder))
|
|
224
|
+
);
|
|
225
|
+
var resolveInsideProject = (projectRoot, projectRelative, subject, context) => Effect2.gen(function* () {
|
|
226
|
+
const fs = yield* FileSystem2.FileSystem;
|
|
227
|
+
const pathSvc = yield* Path2.Path;
|
|
228
|
+
const resolved = yield* fs.realPath(pathSvc.join(projectRoot, projectRelative)).pipe(Effect2.option);
|
|
229
|
+
if (Option2.isNone(resolved)) return resolved;
|
|
230
|
+
const relative = pathSvc.relative(projectRoot, resolved.value);
|
|
231
|
+
if (relative === ".." || relative.startsWith(`..${pathSvc.sep}`) || pathSvc.isAbsolute(relative)) {
|
|
232
|
+
return yield* new ContainerConfigError({
|
|
233
|
+
message: `${subject} leads through a symbolic link to a location outside the project. Only files inside the project can be deployed.`,
|
|
234
|
+
...context
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
return resolved;
|
|
238
|
+
});
|
|
239
|
+
var readInsideProject = (projectRoot, projectRelative, subject, context) => Effect2.gen(function* () {
|
|
240
|
+
const fs = yield* FileSystem2.FileSystem;
|
|
241
|
+
const resolved = yield* resolveInsideProject(projectRoot, projectRelative, subject, context);
|
|
242
|
+
if (Option2.isNone(resolved)) return Option2.none();
|
|
243
|
+
return yield* fs.readFileString(resolved.value).pipe(Effect2.option);
|
|
244
|
+
});
|
|
245
|
+
var toArgv = (name, key, value) => value === void 0 ? Effect2.succeed(void 0) : Array.isArray(value) ? Effect2.succeed(value) : Effect2.try({
|
|
246
|
+
try: () => splitCommandWords(value),
|
|
247
|
+
catch: (error) => new ContainerConfigError({
|
|
248
|
+
message: `Container '${name}' has an invalid ${key}: ${error instanceof Error ? error.message : String(error)}`,
|
|
249
|
+
containerName: name
|
|
250
|
+
})
|
|
251
|
+
});
|
|
252
|
+
var isIntegerBetween = (value, min, max) => Number.isInteger(value) && value >= min && value <= max;
|
|
253
|
+
var validateContainer = (name, container) => {
|
|
254
|
+
const build = typeof container.build === "object" ? container.build : void 0;
|
|
255
|
+
const buildArgs = Object.values(build?.args ?? {});
|
|
256
|
+
const env = Object.entries(container.env ?? {});
|
|
257
|
+
const user = container.user === void 0 ? void 0 : String(container.user);
|
|
258
|
+
const badEnvKey = env.find(([key]) => key === "" || key.includes("=") || key.includes("\0"))?.[0];
|
|
259
|
+
const longEnvKey = env.find(([, value]) => value.length > MAX_CONTAINER_ENV_VALUE_LENGTH)?.[0];
|
|
260
|
+
const problem = [
|
|
261
|
+
[
|
|
262
|
+
container.image !== void 0 && !/^\S{1,512}$/.test(container.image),
|
|
263
|
+
"has an invalid image reference. Use 1 to 512 characters without whitespace."
|
|
264
|
+
],
|
|
265
|
+
[
|
|
266
|
+
container.port !== void 0 && !isIntegerBetween(container.port, 1, 65535),
|
|
267
|
+
`listens on port ${container.port}. Use a whole number from 1 to 65535.`
|
|
268
|
+
],
|
|
269
|
+
[
|
|
270
|
+
container.memory !== void 0 && !isIntegerBetween(container.memory, MIN_CONTAINER_MEMORY_MB, MAX_CONTAINER_MEMORY_MB),
|
|
271
|
+
`asks for ${container.memory} MB of memory. Use a whole number from ${MIN_CONTAINER_MEMORY_MB} to ${MAX_CONTAINER_MEMORY_MB}.`
|
|
272
|
+
],
|
|
273
|
+
[
|
|
274
|
+
container.working_dir !== void 0 && !(container.working_dir.startsWith("/") && container.working_dir.length <= 1024),
|
|
275
|
+
`sets working_dir '${container.working_dir}', which must be an absolute path of at most 1024 characters.`
|
|
276
|
+
],
|
|
277
|
+
[
|
|
278
|
+
typeof container.user === "number" && !isIntegerBetween(container.user, 0, Number.MAX_SAFE_INTEGER),
|
|
279
|
+
`sets user ${container.user}. A numeric user must be a non-negative whole number.`
|
|
280
|
+
],
|
|
281
|
+
[
|
|
282
|
+
user !== void 0 && (user.length === 0 || user.length > 256),
|
|
283
|
+
"sets an empty or overlong user. Use 1 to 256 characters: a name, a uid, name:group or uid:gid."
|
|
284
|
+
],
|
|
285
|
+
[
|
|
286
|
+
build?.target !== void 0 && (build.target.length === 0 || build.target.length > 128),
|
|
287
|
+
"has an empty build target or one longer than 128 characters."
|
|
288
|
+
],
|
|
289
|
+
[
|
|
290
|
+
buildArgs.length > MAX_CONTAINER_ENV_VARS || buildArgs.some((value) => value.length > MAX_CONTAINER_ENV_VALUE_LENGTH),
|
|
291
|
+
`sets more than ${MAX_CONTAINER_ENV_VARS} build args, or one longer than ${MAX_CONTAINER_ENV_VALUE_LENGTH} characters.`
|
|
292
|
+
],
|
|
293
|
+
[
|
|
294
|
+
env.length > MAX_CONTAINER_ENV_VARS,
|
|
295
|
+
`sets ${env.length} environment variables. At most ${MAX_CONTAINER_ENV_VARS} are allowed.`
|
|
296
|
+
],
|
|
297
|
+
[
|
|
298
|
+
badEnvKey !== void 0,
|
|
299
|
+
`sets an environment variable named '${badEnvKey}'. Names must be non-empty and contain no '='.`
|
|
300
|
+
],
|
|
301
|
+
[
|
|
302
|
+
longEnvKey !== void 0,
|
|
303
|
+
`sets environment variable '${longEnvKey}' to a value longer than ${MAX_CONTAINER_ENV_VALUE_LENGTH} characters.`
|
|
304
|
+
]
|
|
305
|
+
].find(([failed]) => failed)?.[1];
|
|
306
|
+
return problem === void 0 ? Effect2.void : Effect2.fail(new ContainerConfigError({ message: `Container '${name}' ${problem}`, containerName: name }));
|
|
307
|
+
};
|
|
308
|
+
var toContainerSpec = Effect2.fn("toContainerSpec")(function* (name, container, projectFolder) {
|
|
309
|
+
yield* validateContainer(name, container);
|
|
310
|
+
let source;
|
|
311
|
+
if (container.image !== void 0 && container.build === void 0) {
|
|
312
|
+
source = { type: "registry", reference: container.image };
|
|
313
|
+
} else if (container.build !== void 0 && container.image === void 0) {
|
|
314
|
+
const build = typeof container.build === "string" ? { context: container.build } : container.build;
|
|
315
|
+
const dockerfile = build.dockerfile ?? "Dockerfile";
|
|
316
|
+
const context = normalizeProjectPath(build.context ?? ".");
|
|
317
|
+
const dockerfilePath = context === void 0 || posix.isAbsolute(dockerfile) ? void 0 : normalizeProjectPath(posix.join(context, dockerfile));
|
|
318
|
+
if (context === void 0 || dockerfilePath === void 0) {
|
|
319
|
+
return yield* new ContainerConfigError({
|
|
320
|
+
message: `Container '${name}' has a build context or Dockerfile outside the project.`,
|
|
321
|
+
containerName: name
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
const projectRoot = yield* resolveProjectRoot(projectFolder);
|
|
325
|
+
yield* resolveInsideProject(projectRoot, context, `The build context '${context}' of container '${name}'`, {
|
|
326
|
+
containerName: name
|
|
327
|
+
});
|
|
328
|
+
const dockerfileReal = yield* resolveInsideProject(
|
|
329
|
+
projectRoot,
|
|
330
|
+
dockerfilePath,
|
|
331
|
+
`The Dockerfile '${dockerfilePath}' of container '${name}'`,
|
|
332
|
+
{ containerName: name }
|
|
333
|
+
);
|
|
334
|
+
if (Option2.isNone(dockerfileReal)) {
|
|
335
|
+
return yield* new ContainerConfigError({
|
|
336
|
+
message: `Container '${name}' builds '${dockerfilePath}', which does not exist.`,
|
|
337
|
+
containerName: name
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
source = {
|
|
341
|
+
type: "dockerfile",
|
|
342
|
+
context,
|
|
343
|
+
dockerfile: posix.relative(context, dockerfilePath),
|
|
344
|
+
...build.target !== void 0 && { target: build.target },
|
|
345
|
+
...build.args !== void 0 && Object.keys(build.args).length > 0 && { buildArgs: { ...build.args } }
|
|
346
|
+
};
|
|
347
|
+
} else {
|
|
348
|
+
return yield* new ContainerConfigError({
|
|
349
|
+
message: `Container '${name}' must set exactly one of 'image' and 'build'.`,
|
|
350
|
+
containerName: name
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
const entrypoint = yield* toArgv(name, "entrypoint", container.entrypoint);
|
|
354
|
+
const command = yield* toArgv(name, "command", container.command);
|
|
355
|
+
const spec = {
|
|
356
|
+
name,
|
|
357
|
+
source,
|
|
358
|
+
...container.port !== void 0 && { port: container.port },
|
|
359
|
+
...entrypoint !== void 0 && { entrypoint },
|
|
360
|
+
...command !== void 0 && { command },
|
|
361
|
+
...container.working_dir !== void 0 && { workingDirectory: container.working_dir },
|
|
362
|
+
...container.user !== void 0 && { user: String(container.user) },
|
|
363
|
+
...container.env !== void 0 && Object.keys(container.env).length > 0 && { environmentVariables: { ...container.env } }
|
|
364
|
+
};
|
|
365
|
+
return spec;
|
|
366
|
+
});
|
|
367
|
+
var normalizeContainers = Effect2.fn("normalizeContainers")(function* (containers, projectFolder) {
|
|
368
|
+
const entrypoints = [];
|
|
369
|
+
const sidecars = [];
|
|
370
|
+
const warnings = [];
|
|
371
|
+
for (const [name, container] of Object.entries(containers)) {
|
|
372
|
+
if (!CONTAINER_NAME_PATTERN.test(name) || name === "localhost") {
|
|
373
|
+
return yield* new ContainerConfigError({
|
|
374
|
+
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)}'.`,
|
|
375
|
+
containerName: name
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
const spec = yield* toContainerSpec(name, container, projectFolder);
|
|
379
|
+
if (container.sidecar === true) {
|
|
380
|
+
const ignored = ["max_duration", "streaming", "schedule"].filter(
|
|
381
|
+
(key) => container[key] !== void 0
|
|
382
|
+
);
|
|
383
|
+
if (ignored.length > 0) {
|
|
384
|
+
warnings.push(
|
|
385
|
+
`Sidecar '${name}' ignores ${ignored.join(", ")}: those settings apply to container functions only.`
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
sidecars.push({ ...spec, memory: container.memory ?? DEFAULT_SIDECAR_MEMORY_MB });
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
entrypoints.push({
|
|
392
|
+
path: containerEntrypointPath(name),
|
|
393
|
+
displayName: name,
|
|
394
|
+
runtime: CONTAINER_RUNTIME,
|
|
395
|
+
memory: container.memory ?? DEFAULT_CONTAINER_MEMORY_MB,
|
|
396
|
+
maxDuration: container.max_duration ?? DEFAULT_FUNCTION_DURATION_SECONDS,
|
|
397
|
+
streaming: container.streaming ?? true,
|
|
398
|
+
...container.schedule !== void 0 && { schedule: container.schedule },
|
|
399
|
+
container: spec
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
if (sidecars.length > MAX_SIDECARS) {
|
|
403
|
+
return yield* new ContainerConfigError({
|
|
404
|
+
message: `A deployment may declare at most ${MAX_SIDECARS} sidecars; this one declares ${sidecars.length}.`
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
const sidecarPorts = /* @__PURE__ */ new Map();
|
|
408
|
+
for (const sidecar of sidecars) {
|
|
409
|
+
if (sidecar.port === void 0) continue;
|
|
410
|
+
const owner = sidecarPorts.get(sidecar.port);
|
|
411
|
+
if (owner !== void 0) {
|
|
412
|
+
return yield* new ContainerConfigError({
|
|
413
|
+
message: `Sidecars '${owner}' and '${sidecar.name}' both listen on port ${sidecar.port}. Sidecars share one network namespace, so their ports must differ.`,
|
|
414
|
+
containerName: sidecar.name
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
sidecarPorts.set(sidecar.port, sidecar.name);
|
|
418
|
+
}
|
|
419
|
+
for (const entrypoint of entrypoints) {
|
|
420
|
+
const port = entrypoint.container?.port;
|
|
421
|
+
const sidecar = port === void 0 ? void 0 : sidecarPorts.get(port);
|
|
422
|
+
if (sidecar !== void 0) {
|
|
423
|
+
return yield* new ContainerConfigError({
|
|
424
|
+
message: `Container '${entrypoint.displayName}' and sidecar '${sidecar}' both listen on port ${port}. Sidecars run inside the function's microVM, so their ports must differ.`,
|
|
425
|
+
containerName: sidecar
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
return { entrypoints, sidecars, warnings };
|
|
430
|
+
});
|
|
431
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
432
|
+
var isStringArray = (value) => Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
433
|
+
var scalarToString = (value) => typeof value === "string" || typeof value === "number" || typeof value === "boolean" ? String(value) : void 0;
|
|
434
|
+
var gigadriveExtension = (service) => isRecord(service["x-gigadrive"]) ? service["x-gigadrive"] : {};
|
|
435
|
+
var COMPOSE_OVERRIDE_FILE_NAMES = [
|
|
436
|
+
"compose.override.yaml",
|
|
437
|
+
"compose.override.yml",
|
|
438
|
+
"docker-compose.override.yaml",
|
|
439
|
+
"docker-compose.override.yml"
|
|
440
|
+
];
|
|
441
|
+
var findClosingQuote = (body, quote) => {
|
|
442
|
+
for (let index = 0; index < body.length; index += 1) {
|
|
443
|
+
if (quote === '"' && body[index] === "\\") index += 1;
|
|
444
|
+
else if (body[index] === quote) return index;
|
|
445
|
+
}
|
|
446
|
+
return -1;
|
|
447
|
+
};
|
|
448
|
+
var DOUBLE_QUOTE_ESCAPES = { n: "\n", r: "\r", t: " " };
|
|
449
|
+
var parseDotEnv = (content) => {
|
|
450
|
+
const values = {};
|
|
451
|
+
const lines = content.split(/\r?\n/);
|
|
452
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
453
|
+
const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_.-]*)\s*=(.*)$/.exec(lines[index]);
|
|
454
|
+
if (!match) continue;
|
|
455
|
+
const [, key, rest] = match;
|
|
456
|
+
const quote = rest.trimStart()[0];
|
|
457
|
+
if (quote === '"' || quote === "'") {
|
|
458
|
+
let body = rest.trimStart().slice(1);
|
|
459
|
+
let end = index;
|
|
460
|
+
let close = findClosingQuote(body, quote);
|
|
461
|
+
while (close === -1 && end + 1 < lines.length) {
|
|
462
|
+
end += 1;
|
|
463
|
+
body += `
|
|
464
|
+
${lines[end]}`;
|
|
465
|
+
close = findClosingQuote(body, quote);
|
|
466
|
+
}
|
|
467
|
+
if (close !== -1) {
|
|
468
|
+
const raw = body.slice(0, close);
|
|
469
|
+
values[key] = quote === '"' ? raw.replace(/\\([nrt"\\])/g, (_match, char) => DOUBLE_QUOTE_ESCAPES[char] ?? char) : raw;
|
|
470
|
+
index = end;
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
values[key] = rest.replace(/\s+#.*$/, "").trim();
|
|
475
|
+
}
|
|
476
|
+
return values;
|
|
477
|
+
};
|
|
478
|
+
var InterpolationError = class extends Error {
|
|
479
|
+
};
|
|
480
|
+
var findClosingBrace = (value, start) => {
|
|
481
|
+
let depth = 0;
|
|
482
|
+
for (let index = start; index < value.length; index += 1) {
|
|
483
|
+
if (value[index] === "$" && (value[index + 1] === "$" || value[index + 1] === "{")) {
|
|
484
|
+
if (value[index + 1] === "{") depth += 1;
|
|
485
|
+
index += 1;
|
|
486
|
+
} else if (value[index] === "}") {
|
|
487
|
+
if (depth === 0) return index;
|
|
488
|
+
depth -= 1;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
return -1;
|
|
492
|
+
};
|
|
493
|
+
var interpolate = (value, variables, missing, where) => {
|
|
494
|
+
let result = "";
|
|
495
|
+
let index = 0;
|
|
496
|
+
while (index < value.length) {
|
|
497
|
+
const dollar = value.indexOf("$", index);
|
|
498
|
+
if (dollar === -1) {
|
|
499
|
+
result += value.slice(index);
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
result += value.slice(index, dollar);
|
|
503
|
+
const next = value[dollar + 1];
|
|
504
|
+
if (next === "$") {
|
|
505
|
+
result += "$";
|
|
506
|
+
index = dollar + 2;
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
if (next === "{") {
|
|
510
|
+
const close = findClosingBrace(value, dollar + 2);
|
|
511
|
+
const expression = close === -1 ? null : /^([A-Za-z_][A-Za-z0-9_]*)(?:(:?[-?+])([\s\S]*))?$/.exec(value.slice(dollar + 2, close));
|
|
512
|
+
if (expression === null) {
|
|
513
|
+
throw new InterpolationError(`has an invalid variable reference in ${where}. Write '$$' for a literal '$'.`);
|
|
514
|
+
}
|
|
515
|
+
const [, name, operator, operand = ""] = expression;
|
|
516
|
+
const current = variables[name];
|
|
517
|
+
const isSet = current !== void 0 && !(operator?.startsWith(":") === true && current === "");
|
|
518
|
+
if (operator === void 0) {
|
|
519
|
+
if (current === void 0) missing.add(name);
|
|
520
|
+
result += current ?? "";
|
|
521
|
+
} else if (operator.endsWith("-")) {
|
|
522
|
+
result += isSet ? current : interpolate(operand, variables, missing, where);
|
|
523
|
+
} else if (operator.endsWith("+")) {
|
|
524
|
+
result += isSet ? interpolate(operand, variables, missing, where) : "";
|
|
525
|
+
} else if (isSet) {
|
|
526
|
+
result += current;
|
|
527
|
+
} else {
|
|
528
|
+
const reason = interpolate(operand, variables, missing, where);
|
|
529
|
+
throw new InterpolationError(
|
|
530
|
+
`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.`
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
index = close + 1;
|
|
534
|
+
continue;
|
|
535
|
+
}
|
|
536
|
+
const bare = /^[A-Za-z_][A-Za-z0-9_]*/.exec(value.slice(dollar + 1))?.[0];
|
|
537
|
+
if (bare === void 0) {
|
|
538
|
+
result += "$";
|
|
539
|
+
index = dollar + 1;
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
if (variables[bare] === void 0) missing.add(bare);
|
|
543
|
+
result += variables[bare] ?? "";
|
|
544
|
+
index = dollar + 1 + bare.length;
|
|
545
|
+
}
|
|
546
|
+
return result;
|
|
547
|
+
};
|
|
548
|
+
var interpolateDeep = (value, variables, missing, where) => {
|
|
549
|
+
if (typeof value === "string") return interpolate(value, variables, missing, where);
|
|
550
|
+
if (Array.isArray(value)) {
|
|
551
|
+
return value.map((item, index) => interpolateDeep(item, variables, missing, `${where}[${index}]`));
|
|
552
|
+
}
|
|
553
|
+
if (isRecord(value)) {
|
|
554
|
+
return Object.fromEntries(
|
|
555
|
+
Object.entries(value).map(([key, item]) => [
|
|
556
|
+
key,
|
|
557
|
+
interpolateDeep(item, variables, missing, where === "" ? key : `${where}.${key}`)
|
|
558
|
+
])
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
return value;
|
|
562
|
+
};
|
|
563
|
+
var parseComposeMemory = (value) => {
|
|
564
|
+
if (typeof value === "number") {
|
|
565
|
+
return Number.isFinite(value) && value >= 0 ? Math.ceil(value / 1024 ** 2) : void 0;
|
|
566
|
+
}
|
|
567
|
+
if (typeof value !== "string") return void 0;
|
|
568
|
+
const match = /^(\d+(?:\.\d+)?) ?([kmgtp])?i?b?$/i.exec(value.trim());
|
|
569
|
+
if (!match) return void 0;
|
|
570
|
+
const exponent = " kmgtp".indexOf((match[2] ?? " ").toLowerCase());
|
|
571
|
+
return Math.ceil(Number(match[1]) * 1024 ** exponent / 1024 ** 2);
|
|
572
|
+
};
|
|
573
|
+
var parseComposePort = (entry) => {
|
|
574
|
+
let port = entry;
|
|
575
|
+
if (isRecord(entry)) {
|
|
576
|
+
if (typeof entry.protocol === "string" && entry.protocol.toLowerCase() !== "tcp") return void 0;
|
|
577
|
+
port = entry.target;
|
|
578
|
+
} else if (typeof entry === "string") {
|
|
579
|
+
const [withoutProtocol, protocol] = entry.split("/");
|
|
580
|
+
if (protocol !== void 0 && protocol.toLowerCase() !== "tcp") return void 0;
|
|
581
|
+
port = (withoutProtocol.split(":").at(-1) ?? "").split("-")[0];
|
|
582
|
+
}
|
|
583
|
+
const number = typeof port === "number" ? port : typeof port === "string" && /^\d+$/.test(port) ? Number(port) : NaN;
|
|
584
|
+
return isIntegerBetween(number, 1, 65535) ? number : void 0;
|
|
585
|
+
};
|
|
586
|
+
var parseComposeEnvironment = (serviceName, value, warnings) => {
|
|
587
|
+
if (value === void 0 || value === null) return void 0;
|
|
588
|
+
const environment = {};
|
|
589
|
+
if (Array.isArray(value)) {
|
|
590
|
+
for (const item of value) {
|
|
591
|
+
if (typeof item !== "string") continue;
|
|
592
|
+
const separator = item.indexOf("=");
|
|
593
|
+
if (separator === -1) {
|
|
594
|
+
warnings.push(
|
|
595
|
+
`Compose service '${serviceName}' takes '${item}' from the host environment, which deployments do not have. Set it in the console instead.`
|
|
596
|
+
);
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
environment[item.slice(0, separator)] = item.slice(separator + 1);
|
|
600
|
+
}
|
|
601
|
+
} else if (isRecord(value)) {
|
|
602
|
+
for (const [key, item] of Object.entries(value)) {
|
|
603
|
+
const scalar = scalarToString(item);
|
|
604
|
+
if (scalar === void 0) {
|
|
605
|
+
warnings.push(
|
|
606
|
+
`Compose service '${serviceName}' takes '${key}' from the host environment, which deployments do not have. Set it in the console instead.`
|
|
607
|
+
);
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
environment[key] = scalar;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
return environment;
|
|
614
|
+
};
|
|
615
|
+
var isPublicMarked = (service) => gigadriveExtension(service).public === true;
|
|
616
|
+
var buildsFromSource = (service) => service.build !== void 0 && gigadriveExtension(service).sidecar !== true;
|
|
617
|
+
var loadComposeServices = Effect2.fn("loadComposeServices")(function* (composePath, projectFolder) {
|
|
618
|
+
const relativePath = normalizeProjectPath(composePath);
|
|
619
|
+
if (relativePath === void 0) {
|
|
620
|
+
return yield* new ContainerConfigError({
|
|
621
|
+
message: `Compose file '${composePath}' is outside the project.`,
|
|
622
|
+
filePath: composePath
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
const projectRoot = yield* resolveProjectRoot(projectFolder);
|
|
626
|
+
const content = yield* readInsideProject(projectRoot, relativePath, `Compose file '${relativePath}'`, {
|
|
627
|
+
filePath: relativePath
|
|
628
|
+
});
|
|
629
|
+
if (Option2.isNone(content)) {
|
|
630
|
+
return yield* new ContainerConfigError({
|
|
631
|
+
message: `Compose file '${relativePath}' could not be read.`,
|
|
632
|
+
filePath: relativePath
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
const document = yield* Effect2.try({
|
|
636
|
+
try: () => parseYaml(content.value),
|
|
637
|
+
catch: (error) => {
|
|
638
|
+
const position = error instanceof YAMLError ? error.linePos?.[0] : void 0;
|
|
639
|
+
return new ContainerConfigError({
|
|
640
|
+
message: `Compose file '${relativePath}' is not valid YAML${position ? ` (line ${position.line}, column ${position.col})` : ""}.`,
|
|
641
|
+
filePath: relativePath
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
if (!isRecord(document) || !isRecord(document.services) || Object.keys(document.services).length === 0) {
|
|
646
|
+
return yield* new ContainerConfigError({
|
|
647
|
+
message: `Compose file '${relativePath}' declares no services.`,
|
|
648
|
+
filePath: relativePath
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
const services = Object.entries(document.services).filter(
|
|
652
|
+
(entry) => isRecord(entry[1]) && !(isStringArray(entry[1].profiles) && entry[1].profiles.length > 0) && gigadriveExtension(entry[1]).skip !== true
|
|
653
|
+
);
|
|
654
|
+
return { relativePath, projectRoot, services };
|
|
655
|
+
});
|
|
656
|
+
var composeDeclaresApp = Effect2.fn("composeDeclaresApp")(function* (composePath, projectFolder) {
|
|
657
|
+
const { services } = yield* loadComposeServices(composePath, projectFolder);
|
|
658
|
+
return services.some(([, service]) => isPublicMarked(service) || buildsFromSource(service));
|
|
659
|
+
});
|
|
660
|
+
var readComposeContainers = Effect2.fn("readComposeContainers")(function* (composePath, projectFolder) {
|
|
661
|
+
const fs = yield* FileSystem2.FileSystem;
|
|
662
|
+
const pathSvc = yield* Path2.Path;
|
|
663
|
+
const { relativePath, projectRoot, services } = yield* loadComposeServices(composePath, projectFolder);
|
|
664
|
+
const composeDirectory = posix.dirname(relativePath);
|
|
665
|
+
const warnings = [];
|
|
666
|
+
for (const [name] of services) {
|
|
667
|
+
if (!CONTAINER_NAME_PATTERN.test(name) || name === "localhost") {
|
|
668
|
+
return yield* new ContainerConfigError({
|
|
669
|
+
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}'.`,
|
|
670
|
+
containerName: name,
|
|
671
|
+
filePath: relativePath
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
const marked = services.filter(([, service]) => isPublicMarked(service)).map(([name]) => name);
|
|
676
|
+
const built = services.filter(([, service]) => buildsFromSource(service)).map(([name]) => name);
|
|
677
|
+
if (marked.length > 1) {
|
|
678
|
+
return yield* new ContainerConfigError({
|
|
679
|
+
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.`,
|
|
680
|
+
filePath: relativePath
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
const publicService = marked[0] ?? (built.length === 1 ? built[0] : void 0);
|
|
684
|
+
if (publicService === void 0) {
|
|
685
|
+
return yield* new ContainerConfigError({
|
|
686
|
+
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 }'.`,
|
|
687
|
+
filePath: relativePath
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
if (COMPOSE_FILE_NAMES.includes(posix.basename(relativePath))) {
|
|
691
|
+
for (const overrideName of COMPOSE_OVERRIDE_FILE_NAMES) {
|
|
692
|
+
const overridePath = posix.join(composeDirectory, overrideName);
|
|
693
|
+
const overrideExists = yield* fs.exists(pathSvc.join(projectRoot, overridePath)).pipe(Effect2.orElseSucceed(() => false));
|
|
694
|
+
if (overrideExists) {
|
|
695
|
+
warnings.push(
|
|
696
|
+
`Compose file '${relativePath}' has the override file '${overridePath}' next to it, which is not merged. Move its settings into '${relativePath}' or gigadrive.yaml.`
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
const dotEnv = yield* readInsideProject(
|
|
702
|
+
projectRoot,
|
|
703
|
+
posix.join(composeDirectory, ".env"),
|
|
704
|
+
`The .env file next to '${relativePath}'`,
|
|
705
|
+
{ filePath: relativePath }
|
|
706
|
+
);
|
|
707
|
+
const variables = Option2.isSome(dotEnv) ? parseDotEnv(dotEnv.value) : {};
|
|
708
|
+
const missing = /* @__PURE__ */ new Set();
|
|
709
|
+
const containers = {};
|
|
710
|
+
for (const [name, rawService] of services) {
|
|
711
|
+
const isPublic = name === publicService;
|
|
712
|
+
const service = yield* Effect2.try({
|
|
713
|
+
try: () => interpolateDeep(rawService, variables, missing, ""),
|
|
714
|
+
catch: (error) => new ContainerConfigError({
|
|
715
|
+
message: `Compose service '${name}' ${error instanceof InterpolationError ? error.message : "could not be interpolated."}`,
|
|
716
|
+
containerName: name,
|
|
717
|
+
filePath: relativePath
|
|
718
|
+
})
|
|
719
|
+
});
|
|
720
|
+
const container = {};
|
|
721
|
+
if (service.extends !== void 0) {
|
|
722
|
+
warnings.push(
|
|
723
|
+
`Compose service '${name}' uses 'extends', which is not supported. The service is deployed as written, without the settings it would inherit.`
|
|
724
|
+
);
|
|
725
|
+
}
|
|
726
|
+
if (typeof service.image === "string" && service.build === void 0) {
|
|
727
|
+
container.image = service.image;
|
|
728
|
+
} else if (typeof service.build === "string" || isRecord(service.build)) {
|
|
729
|
+
const build = typeof service.build === "string" ? { context: service.build } : service.build;
|
|
730
|
+
if (build.dockerfile_inline !== void 0) {
|
|
731
|
+
return yield* new ContainerConfigError({
|
|
732
|
+
message: `Compose service '${name}' uses build.dockerfile_inline, which is not supported. Commit the Dockerfile and point build.dockerfile at it.`,
|
|
733
|
+
containerName: name,
|
|
734
|
+
filePath: relativePath
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
const rawContext = typeof build.context === "string" ? build.context : ".";
|
|
738
|
+
const context = posix.isAbsolute(rawContext) ? rawContext : posix.join(composeDirectory, rawContext);
|
|
739
|
+
const args = Array.isArray(build.args) ? Object.fromEntries(
|
|
740
|
+
build.args.filter((item) => typeof item === "string" && item.includes("=")).map((item) => [item.slice(0, item.indexOf("=")), item.slice(item.indexOf("=") + 1)])
|
|
741
|
+
) : isRecord(build.args) ? Object.fromEntries(Object.entries(build.args).map(([key, item]) => [key, scalarToString(item) ?? ""])) : void 0;
|
|
742
|
+
container.build = {
|
|
743
|
+
context,
|
|
744
|
+
...typeof build.dockerfile === "string" && { dockerfile: build.dockerfile },
|
|
745
|
+
...typeof build.target === "string" && { target: build.target },
|
|
746
|
+
...args !== void 0 && Object.keys(args).length > 0 && { args }
|
|
747
|
+
};
|
|
748
|
+
} else {
|
|
749
|
+
return yield* new ContainerConfigError({
|
|
750
|
+
message: `Compose service '${name}' has neither an image nor a build.`,
|
|
751
|
+
containerName: name,
|
|
752
|
+
filePath: relativePath
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
if (!isPublic) container.sidecar = true;
|
|
756
|
+
const ports = [
|
|
757
|
+
...isPublic && Array.isArray(service.ports) ? service.ports : [],
|
|
758
|
+
...Array.isArray(service.expose) ? service.expose : [],
|
|
759
|
+
...!isPublic && Array.isArray(service.ports) ? service.ports : []
|
|
760
|
+
];
|
|
761
|
+
const port = ports.map(parseComposePort).find((value) => value !== void 0);
|
|
762
|
+
if (port !== void 0) container.port = port;
|
|
763
|
+
for (const key of ["entrypoint", "command"]) {
|
|
764
|
+
const value = service[key];
|
|
765
|
+
if (typeof value === "string" || isStringArray(value)) container[key] = value;
|
|
766
|
+
}
|
|
767
|
+
const environment = {};
|
|
768
|
+
const envFiles = (typeof service.env_file === "string" ? [service.env_file] : Array.isArray(service.env_file) ? service.env_file : []).flatMap(
|
|
769
|
+
(entry) => typeof entry === "string" ? [{ path: entry }] : isRecord(entry) && typeof entry.path === "string" ? [{ path: entry.path, ...typeof entry.required === "boolean" && { required: entry.required } }] : []
|
|
770
|
+
);
|
|
771
|
+
for (const envFile of envFiles) {
|
|
772
|
+
const envFilePath = posix.isAbsolute(envFile.path) ? void 0 : normalizeProjectPath(posix.join(composeDirectory, envFile.path));
|
|
773
|
+
const envContent = envFilePath === void 0 ? Option2.none() : yield* readInsideProject(
|
|
774
|
+
projectRoot,
|
|
775
|
+
envFilePath,
|
|
776
|
+
`The env_file '${envFile.path}' of Compose service '${name}'`,
|
|
777
|
+
{ containerName: name, filePath: relativePath }
|
|
778
|
+
);
|
|
779
|
+
if (Option2.isSome(envContent)) {
|
|
780
|
+
Object.assign(environment, parseDotEnv(envContent.value));
|
|
781
|
+
} else if (envFile.required === true) {
|
|
782
|
+
return yield* new ContainerConfigError({
|
|
783
|
+
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.`,
|
|
784
|
+
containerName: name,
|
|
785
|
+
filePath: relativePath
|
|
786
|
+
});
|
|
787
|
+
} else if (envFile.required === void 0) {
|
|
788
|
+
warnings.push(
|
|
789
|
+
`Compose service '${name}' reads env_file '${envFile.path}', which is not in the deployed source. Set those variables in the console instead.`
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
Object.assign(environment, parseComposeEnvironment(name, service.environment, warnings));
|
|
794
|
+
if (Object.keys(environment).length > 0) container.env = environment;
|
|
795
|
+
if (typeof service.working_dir === "string") container.working_dir = service.working_dir;
|
|
796
|
+
if (typeof service.user === "string" || typeof service.user === "number") container.user = String(service.user);
|
|
797
|
+
const deploy = isRecord(service.deploy) ? service.deploy : {};
|
|
798
|
+
const resources = isRecord(deploy.resources) ? deploy.resources : {};
|
|
799
|
+
const limits = isRecord(resources.limits) ? resources.limits : {};
|
|
800
|
+
const memoryLimit = limits.memory ?? service.mem_limit;
|
|
801
|
+
if (memoryLimit !== void 0 && memoryLimit !== null) {
|
|
802
|
+
const memory = parseComposeMemory(memoryLimit);
|
|
803
|
+
if (memory === void 0) {
|
|
804
|
+
warnings.push(
|
|
805
|
+
`Compose service '${name}' sets the memory limit ${JSON.stringify(memoryLimit)}, which is not a size Docker accepts. The default is used instead.`
|
|
806
|
+
);
|
|
807
|
+
} else if (memory >= MIN_CONTAINER_MEMORY_MB && memory <= MAX_CONTAINER_MEMORY_MB) {
|
|
808
|
+
container.memory = memory;
|
|
809
|
+
} else if (memory > 0) {
|
|
810
|
+
warnings.push(
|
|
811
|
+
`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.`
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
if (service.volumes !== void 0) {
|
|
816
|
+
warnings.push(
|
|
817
|
+
`Compose service '${name}' declares volumes. Containers on Gigadrive Network have no persistent volumes, so anything written there is lost when the instance stops.`
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
containers[name] = container;
|
|
821
|
+
}
|
|
822
|
+
if (missing.size > 0) {
|
|
823
|
+
warnings.push(
|
|
824
|
+
`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.`
|
|
825
|
+
);
|
|
826
|
+
}
|
|
827
|
+
return { containers, publicService, warnings };
|
|
828
|
+
});
|
|
829
|
+
|
|
58
830
|
// src/define-config.ts
|
|
59
831
|
var defineConfig = (config) => config;
|
|
60
832
|
|
|
833
|
+
// src/detection/detect-container-project.ts
|
|
834
|
+
import { FileSystem as FileSystem3, Path as Path3 } from "@effect/platform";
|
|
835
|
+
import { Effect as Effect3 } from "effect";
|
|
836
|
+
|
|
837
|
+
// src/regions.ts
|
|
838
|
+
var REGION_DEFINITIONS = [
|
|
839
|
+
"us-east-1",
|
|
840
|
+
"us-east-2",
|
|
841
|
+
"us-west-1",
|
|
842
|
+
"us-west-2",
|
|
843
|
+
"ap-south-1",
|
|
844
|
+
"ap-northeast-3",
|
|
845
|
+
"ap-northeast-2",
|
|
846
|
+
"ap-southeast-1",
|
|
847
|
+
"ap-southeast-2",
|
|
848
|
+
"ap-northeast-1",
|
|
849
|
+
"ca-central-1",
|
|
850
|
+
"eu-central-1",
|
|
851
|
+
"eu-west-1",
|
|
852
|
+
"eu-west-2",
|
|
853
|
+
"eu-west-3",
|
|
854
|
+
"eu-north-1",
|
|
855
|
+
"sa-east-1"
|
|
856
|
+
];
|
|
857
|
+
var AVAILABLE_REGIONS = [...REGION_DEFINITIONS];
|
|
858
|
+
|
|
859
|
+
// src/detection/detect-container-project.ts
|
|
860
|
+
var detectContainerProject = Effect3.fn("detectContainerProject")(function* (projectFolder) {
|
|
861
|
+
const fs = yield* FileSystem3.FileSystem;
|
|
862
|
+
const pathSvc = yield* Path3.Path;
|
|
863
|
+
const exists = (file) => fs.exists(pathSvc.join(projectFolder, file)).pipe(Effect3.catchAll(() => Effect3.succeed(false)));
|
|
864
|
+
let detection;
|
|
865
|
+
const warnings = [];
|
|
866
|
+
const composeFile = yield* Effect3.findFirst(COMPOSE_FILE_NAMES, exists);
|
|
867
|
+
if (composeFile._tag === "Some" && (yield* composeDeclaresApp(composeFile.value, projectFolder))) {
|
|
868
|
+
const imported = yield* readComposeContainers(composeFile.value, projectFolder);
|
|
869
|
+
warnings.push(...imported.warnings);
|
|
870
|
+
detection = { kind: "compose", file: composeFile.value, containers: imported.containers };
|
|
871
|
+
} else if (yield* exists("Dockerfile")) {
|
|
872
|
+
detection = { kind: "dockerfile", file: "Dockerfile", containers: { app: { build: "." } } };
|
|
873
|
+
} else {
|
|
874
|
+
return void 0;
|
|
875
|
+
}
|
|
876
|
+
const {
|
|
877
|
+
entrypoints,
|
|
878
|
+
sidecars,
|
|
879
|
+
warnings: containerWarnings
|
|
880
|
+
} = yield* normalizeContainers(detection.containers, projectFolder);
|
|
881
|
+
const [publicContainer] = entrypoints;
|
|
882
|
+
const config = {
|
|
883
|
+
regions: [...AVAILABLE_REGIONS],
|
|
884
|
+
environmentVariables: {},
|
|
885
|
+
commands: [],
|
|
886
|
+
entrypoints,
|
|
887
|
+
...sidecars.length > 0 && { sidecars },
|
|
888
|
+
routes: publicContainer ? [
|
|
889
|
+
{
|
|
890
|
+
path: "/*",
|
|
891
|
+
destination: publicContainer.path,
|
|
892
|
+
handler: "SERVERLESS_FUNCTION_STREAMING",
|
|
893
|
+
methods: ["ANY"],
|
|
894
|
+
headers: {}
|
|
895
|
+
}
|
|
896
|
+
] : [],
|
|
897
|
+
warnings: [
|
|
898
|
+
`Auto-detected ${detection.kind === "compose" ? "Docker Compose" : "Dockerfile"} project (${detection.file}). Create a gigadrive.yaml to customize.`,
|
|
899
|
+
...warnings,
|
|
900
|
+
...containerWarnings
|
|
901
|
+
],
|
|
902
|
+
errors: []
|
|
903
|
+
};
|
|
904
|
+
return { kind: detection.kind, file: detection.file, config };
|
|
905
|
+
});
|
|
906
|
+
|
|
61
907
|
// src/detection/detect-framework.ts
|
|
62
|
-
import { FileSystem as
|
|
63
|
-
import { Effect as
|
|
908
|
+
import { FileSystem as FileSystem11, Path as Path9 } from "@effect/platform";
|
|
909
|
+
import { Effect as Effect13 } from "effect";
|
|
64
910
|
|
|
65
911
|
// src/detection/detect-package-manager.ts
|
|
66
|
-
import { FileSystem, Path } from "@effect/platform";
|
|
67
|
-
import { Effect } from "effect";
|
|
912
|
+
import { FileSystem as FileSystem4, Path as Path4 } from "@effect/platform";
|
|
913
|
+
import { Effect as Effect4 } from "effect";
|
|
68
914
|
var LOCKFILE_MAP = [
|
|
69
915
|
{ files: ["bun.lockb", "bun.lock"], pm: "bun" },
|
|
70
916
|
{ files: ["pnpm-lock.yaml"], pm: "pnpm" },
|
|
@@ -72,19 +918,19 @@ var LOCKFILE_MAP = [
|
|
|
72
918
|
{ files: ["package-lock.json"], pm: "npm" },
|
|
73
919
|
{ files: ["composer.lock"], pm: "composer" }
|
|
74
920
|
];
|
|
75
|
-
var detectPackageManager =
|
|
76
|
-
const fs = yield*
|
|
77
|
-
const pathService = yield*
|
|
921
|
+
var detectPackageManager = Effect4.fn("detectPackageManager")(function* (projectFolder) {
|
|
922
|
+
const fs = yield* FileSystem4.FileSystem;
|
|
923
|
+
const pathService = yield* Path4.Path;
|
|
78
924
|
for (const { files, pm } of LOCKFILE_MAP) {
|
|
79
925
|
for (const file of files) {
|
|
80
|
-
const exists = yield* fs.exists(pathService.join(projectFolder, file)).pipe(
|
|
926
|
+
const exists = yield* fs.exists(pathService.join(projectFolder, file)).pipe(Effect4.catchAll(() => Effect4.succeed(false)));
|
|
81
927
|
if (exists) {
|
|
82
|
-
yield*
|
|
928
|
+
yield* Effect4.logDebug(`Detected package manager: ${pm} (via ${file})`);
|
|
83
929
|
return pm;
|
|
84
930
|
}
|
|
85
931
|
}
|
|
86
932
|
}
|
|
87
|
-
yield*
|
|
933
|
+
yield* Effect4.logDebug("No lockfile found, defaulting to npm");
|
|
88
934
|
return "npm";
|
|
89
935
|
});
|
|
90
936
|
|
|
@@ -110,8 +956,8 @@ var astro = {
|
|
|
110
956
|
};
|
|
111
957
|
|
|
112
958
|
// src/detection/frameworks/resolve-node-entrypoint.ts
|
|
113
|
-
import { FileSystem as
|
|
114
|
-
import { Effect as
|
|
959
|
+
import { FileSystem as FileSystem5, Path as Path5 } from "@effect/platform";
|
|
960
|
+
import { Effect as Effect5 } from "effect";
|
|
115
961
|
var SCRIPT_RUNNERS = /* @__PURE__ */ new Set(["node", "bun", "tsx", "ts-node"]);
|
|
116
962
|
var COMMON_ENTRYPOINT_CANDIDATES = [
|
|
117
963
|
"index.js",
|
|
@@ -127,11 +973,11 @@ var COMMON_ENTRYPOINT_CANDIDATES = [
|
|
|
127
973
|
"server.ts",
|
|
128
974
|
"src/server.ts"
|
|
129
975
|
];
|
|
130
|
-
var
|
|
976
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null;
|
|
131
977
|
var parsePackageJson = (content) => {
|
|
132
978
|
try {
|
|
133
979
|
const parsed = JSON.parse(content);
|
|
134
|
-
if (!
|
|
980
|
+
if (!isRecord2(parsed)) return void 0;
|
|
135
981
|
return parsed;
|
|
136
982
|
} catch {
|
|
137
983
|
return void 0;
|
|
@@ -159,14 +1005,14 @@ var applyEntrypoint = (defaults, entrypoint) => ({
|
|
|
159
1005
|
(route) => route.destination === defaults.entrypoint ? { ...route, destination: entrypoint } : route
|
|
160
1006
|
)
|
|
161
1007
|
});
|
|
162
|
-
var resolveNodeServerEntrypoint = (defaults, projectFolder) =>
|
|
163
|
-
const fs = yield*
|
|
164
|
-
const pathService = yield*
|
|
1008
|
+
var resolveNodeServerEntrypoint = (defaults, projectFolder) => Effect5.gen(function* () {
|
|
1009
|
+
const fs = yield* FileSystem5.FileSystem;
|
|
1010
|
+
const pathService = yield* Path5.Path;
|
|
165
1011
|
const isFile = (relativePath) => fs.stat(pathService.join(projectFolder, relativePath)).pipe(
|
|
166
|
-
|
|
167
|
-
|
|
1012
|
+
Effect5.map((info) => info.type === "File"),
|
|
1013
|
+
Effect5.catchAll(() => Effect5.succeed(false))
|
|
168
1014
|
);
|
|
169
|
-
const content = yield* fs.readFileString(pathService.join(projectFolder, "package.json")).pipe(
|
|
1015
|
+
const content = yield* fs.readFileString(pathService.join(projectFolder, "package.json")).pipe(Effect5.catchAll(() => Effect5.succeed(void 0)));
|
|
170
1016
|
const manifest = content ? parsePackageJson(content) : void 0;
|
|
171
1017
|
const startScript = typeof manifest?.scripts?.start === "string" ? manifest.scripts.start : void 0;
|
|
172
1018
|
const candidates = [
|
|
@@ -298,13 +1144,13 @@ var laravel = {
|
|
|
298
1144
|
};
|
|
299
1145
|
|
|
300
1146
|
// src/detection/frameworks/nestjs.ts
|
|
301
|
-
import { FileSystem as
|
|
302
|
-
import { Effect as
|
|
303
|
-
var
|
|
1147
|
+
import { FileSystem as FileSystem6 } from "@effect/platform";
|
|
1148
|
+
import { Effect as Effect6 } from "effect";
|
|
1149
|
+
var isRecord3 = (value) => typeof value === "object" && value !== null;
|
|
304
1150
|
var parseNestCliJson = (content) => {
|
|
305
1151
|
try {
|
|
306
1152
|
const parsed = JSON.parse(content);
|
|
307
|
-
if (!
|
|
1153
|
+
if (!isRecord3(parsed)) return void 0;
|
|
308
1154
|
return parsed;
|
|
309
1155
|
} catch {
|
|
310
1156
|
return void 0;
|
|
@@ -350,9 +1196,9 @@ var nestjs = {
|
|
|
350
1196
|
detectors: [{ matchPackage: "@nestjs/core" }],
|
|
351
1197
|
priority: 90,
|
|
352
1198
|
getDefaultConfig: () => createNestConfig("dist"),
|
|
353
|
-
refineDefaultConfig: (defaults, projectFolder) =>
|
|
354
|
-
const fs = yield*
|
|
355
|
-
const content = yield* fs.readFileString(`${projectFolder}/nest-cli.json`).pipe(
|
|
1199
|
+
refineDefaultConfig: (defaults, projectFolder) => Effect6.gen(function* () {
|
|
1200
|
+
const fs = yield* FileSystem6.FileSystem;
|
|
1201
|
+
const content = yield* fs.readFileString(`${projectFolder}/nest-cli.json`).pipe(Effect6.catchAll(() => Effect6.succeed(void 0)));
|
|
356
1202
|
if (!content) return defaults;
|
|
357
1203
|
const nestConfig = parseNestCliJson(content);
|
|
358
1204
|
const outputPath = nestConfig ? getProjectOutputPath(nestConfig) : void 0;
|
|
@@ -361,28 +1207,28 @@ var nestjs = {
|
|
|
361
1207
|
};
|
|
362
1208
|
|
|
363
1209
|
// src/detection/frameworks/nextjs.ts
|
|
364
|
-
import { FileSystem as
|
|
365
|
-
import { Effect as
|
|
1210
|
+
import { FileSystem as FileSystem8, Path as Path7 } from "@effect/platform";
|
|
1211
|
+
import { Effect as Effect8 } from "effect";
|
|
366
1212
|
|
|
367
1213
|
// src/build-output-v3/get-default-path-map.ts
|
|
368
|
-
import { FileSystem as
|
|
369
|
-
import { Effect as
|
|
370
|
-
var getDefaultPathMap =
|
|
371
|
-
const fs = yield*
|
|
372
|
-
const pathService = yield*
|
|
1214
|
+
import { FileSystem as FileSystem7, Path as Path6 } from "@effect/platform";
|
|
1215
|
+
import { Effect as Effect7 } from "effect";
|
|
1216
|
+
var getDefaultPathMap = Effect7.fn("getDefaultPathMap")(function* (directory) {
|
|
1217
|
+
const fs = yield* FileSystem7.FileSystem;
|
|
1218
|
+
const pathService = yield* Path6.Path;
|
|
373
1219
|
const result = {};
|
|
374
|
-
const exists = yield* fs.exists(directory).pipe(
|
|
1220
|
+
const exists = yield* fs.exists(directory).pipe(Effect7.catchAll(() => Effect7.succeed(false)));
|
|
375
1221
|
if (!exists) {
|
|
376
1222
|
return result;
|
|
377
1223
|
}
|
|
378
1224
|
const MAX_DEPTH = 100;
|
|
379
|
-
const processDirectory = (currentPath, basePath = "", depth = 0) =>
|
|
1225
|
+
const processDirectory = (currentPath, basePath = "", depth = 0) => Effect7.gen(function* () {
|
|
380
1226
|
if (depth > MAX_DEPTH) return;
|
|
381
|
-
const entries = yield* fs.readDirectory(currentPath).pipe(
|
|
1227
|
+
const entries = yield* fs.readDirectory(currentPath).pipe(Effect7.catchAll(() => Effect7.succeed([])));
|
|
382
1228
|
for (const name of entries) {
|
|
383
1229
|
const fullPath = pathService.join(currentPath, name);
|
|
384
1230
|
const relativePath = basePath ? pathService.join(basePath, name) : name;
|
|
385
|
-
const stat = yield* fs.stat(fullPath).pipe(
|
|
1231
|
+
const stat = yield* fs.stat(fullPath).pipe(Effect7.catchAll(() => Effect7.succeed(null)));
|
|
386
1232
|
if (!stat) continue;
|
|
387
1233
|
if (stat.type === "Directory") {
|
|
388
1234
|
yield* processDirectory(fullPath, relativePath, depth + 1);
|
|
@@ -399,291 +1245,269 @@ var getDefaultPathMap = Effect4.fn("getDefaultPathMap")(function* (directory) {
|
|
|
399
1245
|
var MAXIMUM_ENTRY_PAGE_PATHS = 50;
|
|
400
1246
|
|
|
401
1247
|
// src/nextjs-manifest.ts
|
|
402
|
-
import { Schema as
|
|
403
|
-
var StringArraySchema =
|
|
404
|
-
var StringRecordSchema =
|
|
405
|
-
var JsonValueSchema =
|
|
406
|
-
() =>
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
1248
|
+
import { Schema as Schema4 } from "effect";
|
|
1249
|
+
var StringArraySchema = Schema4.mutable(Schema4.Array(Schema4.String));
|
|
1250
|
+
var StringRecordSchema = Schema4.mutable(Schema4.Record({ key: Schema4.String, value: Schema4.String }));
|
|
1251
|
+
var JsonValueSchema = Schema4.suspend(
|
|
1252
|
+
() => Schema4.Union(
|
|
1253
|
+
Schema4.Null,
|
|
1254
|
+
Schema4.Boolean,
|
|
1255
|
+
Schema4.Number,
|
|
1256
|
+
Schema4.String,
|
|
1257
|
+
Schema4.mutable(Schema4.Array(JsonValueSchema)),
|
|
1258
|
+
Schema4.mutable(Schema4.Record({ key: Schema4.String, value: JsonValueSchema }))
|
|
413
1259
|
)
|
|
414
1260
|
);
|
|
415
|
-
var ImageLocalPatternSchema =
|
|
416
|
-
|
|
417
|
-
pathname:
|
|
418
|
-
search:
|
|
1261
|
+
var ImageLocalPatternSchema = Schema4.mutable(
|
|
1262
|
+
Schema4.Struct({
|
|
1263
|
+
pathname: Schema4.optional(Schema4.String),
|
|
1264
|
+
search: Schema4.optional(Schema4.String)
|
|
419
1265
|
})
|
|
420
1266
|
);
|
|
421
|
-
var ImageRemotePatternSchema =
|
|
422
|
-
|
|
423
|
-
protocol:
|
|
424
|
-
hostname:
|
|
425
|
-
port:
|
|
426
|
-
pathname:
|
|
427
|
-
search:
|
|
1267
|
+
var ImageRemotePatternSchema = Schema4.mutable(
|
|
1268
|
+
Schema4.Struct({
|
|
1269
|
+
protocol: Schema4.optional(Schema4.Literal("http", "https")),
|
|
1270
|
+
hostname: Schema4.String,
|
|
1271
|
+
port: Schema4.optional(Schema4.String),
|
|
1272
|
+
pathname: Schema4.optional(Schema4.String),
|
|
1273
|
+
search: Schema4.optional(Schema4.String)
|
|
428
1274
|
})
|
|
429
1275
|
);
|
|
430
|
-
var ImageVariantSchema =
|
|
431
|
-
|
|
432
|
-
width:
|
|
433
|
-
height:
|
|
434
|
-
quality:
|
|
435
|
-
format:
|
|
436
|
-
fit:
|
|
1276
|
+
var ImageVariantSchema = Schema4.mutable(
|
|
1277
|
+
Schema4.Struct({
|
|
1278
|
+
width: Schema4.optional(Schema4.Number),
|
|
1279
|
+
height: Schema4.optional(Schema4.Number),
|
|
1280
|
+
quality: Schema4.optional(Schema4.Number),
|
|
1281
|
+
format: Schema4.optional(Schema4.Literal("image/avif", "image/webp", "image/jpeg", "image/png")),
|
|
1282
|
+
fit: Schema4.optional(Schema4.Literal("contain", "cover", "fill", "inside", "outside"))
|
|
437
1283
|
})
|
|
438
1284
|
);
|
|
439
|
-
var PositiveIntSchema =
|
|
440
|
-
var ImagePolicySchema =
|
|
441
|
-
|
|
442
|
-
localPatterns:
|
|
443
|
-
remotePatterns:
|
|
444
|
-
widths:
|
|
445
|
-
heights:
|
|
446
|
-
qualities:
|
|
447
|
-
formats:
|
|
448
|
-
minimumCacheTTL:
|
|
449
|
-
dangerouslyAllowSVG:
|
|
450
|
-
contentSecurityPolicy:
|
|
451
|
-
contentDispositionType:
|
|
452
|
-
maximumRedirects:
|
|
453
|
-
maximumResponseBody:
|
|
454
|
-
variants:
|
|
1285
|
+
var PositiveIntSchema = Schema4.Int.pipe(Schema4.greaterThan(0));
|
|
1286
|
+
var ImagePolicySchema = Schema4.mutable(
|
|
1287
|
+
Schema4.Struct({
|
|
1288
|
+
localPatterns: Schema4.mutable(Schema4.Array(ImageLocalPatternSchema)),
|
|
1289
|
+
remotePatterns: Schema4.mutable(Schema4.Array(ImageRemotePatternSchema)),
|
|
1290
|
+
widths: Schema4.mutable(Schema4.Array(PositiveIntSchema)),
|
|
1291
|
+
heights: Schema4.mutable(Schema4.Array(PositiveIntSchema)),
|
|
1292
|
+
qualities: Schema4.mutable(Schema4.Array(Schema4.Number.pipe(Schema4.between(1, 100)))),
|
|
1293
|
+
formats: Schema4.mutable(Schema4.Array(Schema4.Literal("image/avif", "image/webp", "image/jpeg", "image/png"))),
|
|
1294
|
+
minimumCacheTTL: Schema4.Number,
|
|
1295
|
+
dangerouslyAllowSVG: Schema4.Boolean,
|
|
1296
|
+
contentSecurityPolicy: Schema4.String,
|
|
1297
|
+
contentDispositionType: Schema4.Literal("inline", "attachment"),
|
|
1298
|
+
maximumRedirects: Schema4.Number,
|
|
1299
|
+
maximumResponseBody: Schema4.Number,
|
|
1300
|
+
variants: Schema4.mutable(Schema4.Record({ key: Schema4.String, value: ImageVariantSchema }))
|
|
455
1301
|
})
|
|
456
1302
|
);
|
|
457
1303
|
var GigadriveNextPrerenderOutputFields = {
|
|
458
|
-
id:
|
|
459
|
-
type:
|
|
1304
|
+
id: Schema4.String,
|
|
1305
|
+
type: Schema4.Literal("PRERENDER"),
|
|
460
1306
|
pathname: UrlPathnameSchema,
|
|
461
|
-
route:
|
|
462
|
-
parentOutputId:
|
|
463
|
-
groupId:
|
|
464
|
-
pprChain:
|
|
465
|
-
parentFallbackMode:
|
|
466
|
-
fallback:
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
filePath:
|
|
470
|
-
initialStatus:
|
|
471
|
-
initialHeaders:
|
|
472
|
-
initialExpiration:
|
|
473
|
-
initialRevalidate:
|
|
474
|
-
postponedState:
|
|
1307
|
+
route: Schema4.optional(UrlPathnameSchema),
|
|
1308
|
+
parentOutputId: Schema4.String,
|
|
1309
|
+
groupId: Schema4.Number,
|
|
1310
|
+
pprChain: Schema4.optional(Schema4.mutable(Schema4.Struct({ headers: HttpSingleValueHeadersSchema }))),
|
|
1311
|
+
parentFallbackMode: Schema4.optional(JsonValueSchema),
|
|
1312
|
+
fallback: Schema4.optional(
|
|
1313
|
+
Schema4.mutable(
|
|
1314
|
+
Schema4.Struct({
|
|
1315
|
+
filePath: Schema4.optional(PortableRelativePathSchema),
|
|
1316
|
+
initialStatus: Schema4.optional(Schema4.Int.pipe(Schema4.between(100, 599))),
|
|
1317
|
+
initialHeaders: Schema4.optional(HttpHeadersSchema),
|
|
1318
|
+
initialExpiration: Schema4.optional(Schema4.Number),
|
|
1319
|
+
initialRevalidate: Schema4.optional(Schema4.Union(Schema4.Number, Schema4.Literal(false))),
|
|
1320
|
+
postponedState: Schema4.optional(Schema4.String)
|
|
475
1321
|
})
|
|
476
1322
|
)
|
|
477
1323
|
),
|
|
478
|
-
config:
|
|
479
|
-
|
|
480
|
-
allowQuery:
|
|
481
|
-
allowHeader:
|
|
482
|
-
bypassFor:
|
|
483
|
-
renderingMode:
|
|
484
|
-
partialFallback:
|
|
485
|
-
bypassToken:
|
|
1324
|
+
config: Schema4.mutable(
|
|
1325
|
+
Schema4.Struct({
|
|
1326
|
+
allowQuery: Schema4.optional(StringArraySchema),
|
|
1327
|
+
allowHeader: Schema4.optional(StringArraySchema),
|
|
1328
|
+
bypassFor: Schema4.optional(Schema4.mutable(Schema4.Array(JsonValueSchema))),
|
|
1329
|
+
renderingMode: Schema4.optional(Schema4.String),
|
|
1330
|
+
partialFallback: Schema4.optional(Schema4.Boolean),
|
|
1331
|
+
bypassToken: Schema4.optional(Schema4.String)
|
|
486
1332
|
})
|
|
487
1333
|
)
|
|
488
1334
|
};
|
|
489
|
-
var GigadriveNextPrerenderClassificationSchema =
|
|
490
|
-
routeType:
|
|
491
|
-
response:
|
|
492
|
-
compute:
|
|
493
|
-
htmlSize:
|
|
1335
|
+
var GigadriveNextPrerenderClassificationSchema = Schema4.Struct({
|
|
1336
|
+
routeType: Schema4.Literal("route", "fallback", "shell", "page"),
|
|
1337
|
+
response: Schema4.Literal("empty", "initial", "complete"),
|
|
1338
|
+
compute: Schema4.Literal("blocking", "resuming", "static"),
|
|
1339
|
+
htmlSize: Schema4.optional(Schema4.Number)
|
|
494
1340
|
});
|
|
495
|
-
var GigadriveNextPrerenderWithoutClassificationSchema =
|
|
496
|
-
routeType:
|
|
497
|
-
response:
|
|
498
|
-
compute:
|
|
499
|
-
htmlSize:
|
|
1341
|
+
var GigadriveNextPrerenderWithoutClassificationSchema = Schema4.Struct({
|
|
1342
|
+
routeType: Schema4.optional(Schema4.Never),
|
|
1343
|
+
response: Schema4.optional(Schema4.Never),
|
|
1344
|
+
compute: Schema4.optional(Schema4.Never),
|
|
1345
|
+
htmlSize: Schema4.optional(Schema4.Never)
|
|
500
1346
|
});
|
|
501
|
-
var GigadriveNextPrerenderOutputSchema =
|
|
502
|
-
|
|
503
|
-
|
|
1347
|
+
var GigadriveNextPrerenderOutputSchema = Schema4.Union(
|
|
1348
|
+
Schema4.mutable(
|
|
1349
|
+
Schema4.Struct({ ...GigadriveNextPrerenderOutputFields, ...GigadriveNextPrerenderClassificationSchema.fields })
|
|
504
1350
|
),
|
|
505
|
-
|
|
506
|
-
|
|
1351
|
+
Schema4.mutable(
|
|
1352
|
+
Schema4.Struct({
|
|
507
1353
|
...GigadriveNextPrerenderOutputFields,
|
|
508
1354
|
...GigadriveNextPrerenderWithoutClassificationSchema.fields
|
|
509
1355
|
})
|
|
510
1356
|
)
|
|
511
1357
|
);
|
|
512
|
-
var GigadriveNextStaticAssetPrefixSchema =
|
|
513
|
-
|
|
1358
|
+
var GigadriveNextStaticAssetPrefixSchema = Schema4.mutable(
|
|
1359
|
+
Schema4.Struct({
|
|
514
1360
|
sourceDir: PortableRelativePathSchema,
|
|
515
1361
|
urlPrefix: PortableRelativePathSchema,
|
|
516
|
-
immutable:
|
|
1362
|
+
immutable: Schema4.Boolean
|
|
517
1363
|
})
|
|
518
1364
|
);
|
|
519
|
-
var GigadriveNextRouteConditionSchema =
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
type:
|
|
523
|
-
key:
|
|
524
|
-
value:
|
|
1365
|
+
var GigadriveNextRouteConditionSchema = Schema4.Union(
|
|
1366
|
+
Schema4.mutable(
|
|
1367
|
+
Schema4.Struct({
|
|
1368
|
+
type: Schema4.Literal("header", "cookie", "query"),
|
|
1369
|
+
key: Schema4.String,
|
|
1370
|
+
value: Schema4.optional(Schema4.String)
|
|
525
1371
|
})
|
|
526
1372
|
),
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
type:
|
|
530
|
-
value:
|
|
1373
|
+
Schema4.mutable(
|
|
1374
|
+
Schema4.Struct({
|
|
1375
|
+
type: Schema4.Literal("host"),
|
|
1376
|
+
value: Schema4.String
|
|
531
1377
|
})
|
|
532
1378
|
)
|
|
533
1379
|
);
|
|
534
|
-
var GigadriveNextMiddlewareMatcherSchema =
|
|
535
|
-
|
|
536
|
-
source:
|
|
537
|
-
sourceRegex:
|
|
538
|
-
has:
|
|
539
|
-
missing:
|
|
1380
|
+
var GigadriveNextMiddlewareMatcherSchema = Schema4.mutable(
|
|
1381
|
+
Schema4.Struct({
|
|
1382
|
+
source: Schema4.String,
|
|
1383
|
+
sourceRegex: Schema4.String,
|
|
1384
|
+
has: Schema4.optional(Schema4.mutable(Schema4.Array(GigadriveNextRouteConditionSchema))),
|
|
1385
|
+
missing: Schema4.optional(Schema4.mutable(Schema4.Array(GigadriveNextRouteConditionSchema)))
|
|
540
1386
|
})
|
|
541
1387
|
);
|
|
542
|
-
var GigadriveNextMiddlewareDescriptorSchema =
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
present:
|
|
546
|
-
matchers:
|
|
1388
|
+
var GigadriveNextMiddlewareDescriptorSchema = Schema4.Union(
|
|
1389
|
+
Schema4.mutable(
|
|
1390
|
+
Schema4.Struct({
|
|
1391
|
+
present: Schema4.Literal(true),
|
|
1392
|
+
matchers: Schema4.mutable(Schema4.Array(GigadriveNextMiddlewareMatcherSchema))
|
|
547
1393
|
})
|
|
548
1394
|
),
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
present:
|
|
552
|
-
matchers:
|
|
1395
|
+
Schema4.mutable(
|
|
1396
|
+
Schema4.Struct({
|
|
1397
|
+
present: Schema4.Literal(false),
|
|
1398
|
+
matchers: Schema4.mutable(Schema4.Tuple())
|
|
553
1399
|
})
|
|
554
1400
|
)
|
|
555
1401
|
);
|
|
556
|
-
var GigadriveNextServerDescriptorSchema =
|
|
557
|
-
|
|
558
|
-
maxDuration:
|
|
559
|
-
preferredRegion:
|
|
560
|
-
env:
|
|
1402
|
+
var GigadriveNextServerDescriptorSchema = Schema4.mutable(
|
|
1403
|
+
Schema4.Struct({
|
|
1404
|
+
maxDuration: Schema4.optional(Schema4.Number),
|
|
1405
|
+
preferredRegion: Schema4.optional(Schema4.Union(Schema4.String, StringArraySchema)),
|
|
1406
|
+
env: Schema4.optional(StringRecordSchema)
|
|
561
1407
|
})
|
|
562
1408
|
);
|
|
563
|
-
var PrerenderArraySchema =
|
|
564
|
-
var StaticAssetArraySchema =
|
|
565
|
-
var JsonValueArraySchema =
|
|
566
|
-
var GigadriveNextPrerenderSeedMethodsSchema =
|
|
567
|
-
|
|
1409
|
+
var PrerenderArraySchema = Schema4.mutable(Schema4.Array(GigadriveNextPrerenderOutputSchema));
|
|
1410
|
+
var StaticAssetArraySchema = Schema4.mutable(Schema4.Array(GigadriveNextStaticAssetPrefixSchema));
|
|
1411
|
+
var JsonValueArraySchema = Schema4.mutable(Schema4.Array(JsonValueSchema));
|
|
1412
|
+
var GigadriveNextPrerenderSeedMethodsSchema = Schema4.mutable(
|
|
1413
|
+
Schema4.Tuple(Schema4.Literal("GET"), Schema4.Literal("HEAD"))
|
|
568
1414
|
);
|
|
569
|
-
var GigadriveNextBuildManifestV1Schema =
|
|
570
|
-
|
|
571
|
-
version:
|
|
572
|
-
output:
|
|
1415
|
+
var GigadriveNextBuildManifestV1Schema = Schema4.mutable(
|
|
1416
|
+
Schema4.Struct({
|
|
1417
|
+
version: Schema4.Literal(1),
|
|
1418
|
+
output: Schema4.Literal("standalone", "export"),
|
|
573
1419
|
distDir: PortableRelativePathSchema,
|
|
574
1420
|
repoRootToProject: RepositoryRelativePathSchema,
|
|
575
|
-
nextVersion:
|
|
576
|
-
buildId:
|
|
1421
|
+
nextVersion: Schema4.String,
|
|
1422
|
+
buildId: Schema4.String
|
|
577
1423
|
})
|
|
578
1424
|
);
|
|
579
|
-
var GigadriveNextBuildManifestV2ExportSchema =
|
|
580
|
-
|
|
581
|
-
version:
|
|
582
|
-
mode:
|
|
1425
|
+
var GigadriveNextBuildManifestV2ExportSchema = Schema4.mutable(
|
|
1426
|
+
Schema4.Struct({
|
|
1427
|
+
version: Schema4.Literal(2),
|
|
1428
|
+
mode: Schema4.Literal("export"),
|
|
583
1429
|
distDir: PortableRelativePathSchema,
|
|
584
1430
|
repoRootToProject: RepositoryRelativePathSchema,
|
|
585
|
-
nextVersion:
|
|
586
|
-
buildId:
|
|
1431
|
+
nextVersion: Schema4.String,
|
|
1432
|
+
buildId: Schema4.String
|
|
587
1433
|
})
|
|
588
1434
|
);
|
|
589
|
-
var GigadriveNextBuildManifestV2StandaloneSchema =
|
|
590
|
-
|
|
591
|
-
version:
|
|
592
|
-
mode:
|
|
1435
|
+
var GigadriveNextBuildManifestV2StandaloneSchema = Schema4.mutable(
|
|
1436
|
+
Schema4.Struct({
|
|
1437
|
+
version: Schema4.Literal(2),
|
|
1438
|
+
mode: Schema4.Literal("standalone-v2"),
|
|
593
1439
|
distDir: PortableRelativePathSchema,
|
|
594
1440
|
repoRootToProject: RepositoryRelativePathSchema,
|
|
595
|
-
nextVersion:
|
|
596
|
-
buildId:
|
|
1441
|
+
nextVersion: Schema4.String,
|
|
1442
|
+
buildId: Schema4.String,
|
|
597
1443
|
server: GigadriveNextServerDescriptorSchema,
|
|
598
|
-
config:
|
|
599
|
-
|
|
600
|
-
basePath:
|
|
601
|
-
trailingSlash:
|
|
602
|
-
cacheComponents:
|
|
603
|
-
i18n:
|
|
604
|
-
images:
|
|
1444
|
+
config: Schema4.mutable(
|
|
1445
|
+
Schema4.Struct({
|
|
1446
|
+
basePath: Schema4.String,
|
|
1447
|
+
trailingSlash: Schema4.Boolean,
|
|
1448
|
+
cacheComponents: Schema4.Boolean,
|
|
1449
|
+
i18n: Schema4.optional(JsonValueSchema),
|
|
1450
|
+
images: Schema4.optional(ImagePolicySchema)
|
|
605
1451
|
})
|
|
606
1452
|
),
|
|
607
|
-
routing:
|
|
608
|
-
|
|
1453
|
+
routing: Schema4.mutable(
|
|
1454
|
+
Schema4.Struct({
|
|
609
1455
|
beforeMiddleware: JsonValueArraySchema,
|
|
610
1456
|
beforeFiles: JsonValueArraySchema,
|
|
611
1457
|
afterFiles: JsonValueArraySchema,
|
|
612
1458
|
dynamicRoutes: JsonValueArraySchema,
|
|
613
1459
|
onMatch: JsonValueArraySchema,
|
|
614
1460
|
fallback: JsonValueArraySchema,
|
|
615
|
-
shouldNormalizeNextData:
|
|
1461
|
+
shouldNormalizeNextData: Schema4.Boolean,
|
|
616
1462
|
rsc: JsonValueSchema
|
|
617
1463
|
})
|
|
618
1464
|
),
|
|
619
|
-
outputs:
|
|
620
|
-
|
|
1465
|
+
outputs: Schema4.mutable(
|
|
1466
|
+
Schema4.Struct({
|
|
621
1467
|
prerenders: PrerenderArraySchema,
|
|
622
|
-
prerenderManifest:
|
|
623
|
-
assetManifest:
|
|
624
|
-
entryPagePaths:
|
|
1468
|
+
prerenderManifest: Schema4.optional(PortableRelativePathSchema),
|
|
1469
|
+
assetManifest: Schema4.optional(PortableRelativePathSchema),
|
|
1470
|
+
entryPagePaths: Schema4.optional(Schema4.mutable(Schema4.Array(UrlPathnameSchema))),
|
|
625
1471
|
staticAssets: StaticAssetArraySchema,
|
|
626
|
-
middleware:
|
|
1472
|
+
middleware: Schema4.optional(GigadriveNextMiddlewareDescriptorSchema)
|
|
627
1473
|
})
|
|
628
1474
|
)
|
|
629
|
-
})
|
|
630
|
-
);
|
|
631
|
-
var GigadriveNextBuildManifestSchema =
|
|
632
|
-
GigadriveNextBuildManifestV1Schema,
|
|
633
|
-
GigadriveNextBuildManifestV2ExportSchema,
|
|
634
|
-
GigadriveNextBuildManifestV2StandaloneSchema
|
|
635
|
-
);
|
|
636
|
-
var GigadriveNextPrerenderManifestV1Schema =
|
|
637
|
-
|
|
638
|
-
version:
|
|
639
|
-
seedMethods:
|
|
640
|
-
prerenders: PrerenderArraySchema
|
|
641
|
-
})
|
|
642
|
-
);
|
|
643
|
-
var parseGigadriveNextBuildManifest = (content) => decodeJson(GigadriveNextBuildManifestSchema, content);
|
|
644
|
-
var parseGigadriveNextPrerenderManifest = (content) => decodeJson(GigadriveNextPrerenderManifestV1Schema, content);
|
|
645
|
-
|
|
646
|
-
// src/regions.ts
|
|
647
|
-
var REGION_DEFINITIONS = [
|
|
648
|
-
"us-east-1",
|
|
649
|
-
"us-east-2",
|
|
650
|
-
"us-west-1",
|
|
651
|
-
"us-west-2",
|
|
652
|
-
"ap-south-1",
|
|
653
|
-
"ap-northeast-3",
|
|
654
|
-
"ap-northeast-2",
|
|
655
|
-
"ap-southeast-1",
|
|
656
|
-
"ap-southeast-2",
|
|
657
|
-
"ap-northeast-1",
|
|
658
|
-
"ca-central-1",
|
|
659
|
-
"eu-central-1",
|
|
660
|
-
"eu-west-1",
|
|
661
|
-
"eu-west-2",
|
|
662
|
-
"eu-west-3",
|
|
663
|
-
"eu-north-1",
|
|
664
|
-
"sa-east-1"
|
|
665
|
-
];
|
|
666
|
-
var AVAILABLE_REGIONS = [...REGION_DEFINITIONS];
|
|
1475
|
+
})
|
|
1476
|
+
);
|
|
1477
|
+
var GigadriveNextBuildManifestSchema = Schema4.Union(
|
|
1478
|
+
GigadriveNextBuildManifestV1Schema,
|
|
1479
|
+
GigadriveNextBuildManifestV2ExportSchema,
|
|
1480
|
+
GigadriveNextBuildManifestV2StandaloneSchema
|
|
1481
|
+
);
|
|
1482
|
+
var GigadriveNextPrerenderManifestV1Schema = Schema4.mutable(
|
|
1483
|
+
Schema4.Struct({
|
|
1484
|
+
version: Schema4.Literal(1),
|
|
1485
|
+
seedMethods: Schema4.optional(GigadriveNextPrerenderSeedMethodsSchema),
|
|
1486
|
+
prerenders: PrerenderArraySchema
|
|
1487
|
+
})
|
|
1488
|
+
);
|
|
1489
|
+
var parseGigadriveNextBuildManifest = (content) => decodeJson(GigadriveNextBuildManifestSchema, content);
|
|
1490
|
+
var parseGigadriveNextPrerenderManifest = (content) => decodeJson(GigadriveNextPrerenderManifestV1Schema, content);
|
|
667
1491
|
|
|
668
1492
|
// src/detection/frameworks/nextjs.ts
|
|
669
1493
|
var toPortablePath = (value) => value.replaceAll("\\", "/");
|
|
670
1494
|
var STANDALONE_SERVER_MEMORY_MB = 1024;
|
|
671
|
-
var resolveStandaloneServer =
|
|
672
|
-
const fs = yield*
|
|
673
|
-
const pathService = yield*
|
|
1495
|
+
var resolveStandaloneServer = Effect8.fn("nextjs.resolveStandaloneServer")(function* (projectFolder, distDir, repoRootToProject) {
|
|
1496
|
+
const fs = yield* FileSystem8.FileSystem;
|
|
1497
|
+
const pathService = yield* Path7.Path;
|
|
674
1498
|
const standaloneRoot = pathService.join(projectFolder, distDir, "standalone");
|
|
675
1499
|
const monorepoServer = pathService.join(standaloneRoot, repoRootToProject, "server.js");
|
|
676
1500
|
const rootServer = pathService.join(standaloneRoot, "server.js");
|
|
677
|
-
const monorepoServerExists = yield* fs.exists(monorepoServer).pipe(
|
|
1501
|
+
const monorepoServerExists = yield* fs.exists(monorepoServer).pipe(Effect8.catchAll(() => Effect8.succeed(false)));
|
|
678
1502
|
const serverPath = monorepoServerExists ? monorepoServer : rootServer;
|
|
679
|
-
const serverExists = yield* fs.exists(serverPath).pipe(
|
|
1503
|
+
const serverExists = yield* fs.exists(serverPath).pipe(Effect8.catchAll(() => Effect8.succeed(false)));
|
|
680
1504
|
if (!serverExists) return void 0;
|
|
681
1505
|
const serverDirectory = pathService.dirname(serverPath);
|
|
682
1506
|
const entrypoint = toPortablePath(pathService.relative(standaloneRoot, serverPath));
|
|
683
1507
|
return { standaloneRoot, serverDirectory, entrypoint };
|
|
684
1508
|
});
|
|
685
|
-
var buildStandalonePublicAssets =
|
|
686
|
-
const pathService = yield*
|
|
1509
|
+
var buildStandalonePublicAssets = Effect8.fn("nextjs.buildStandalonePublicAssets")(function* (projectFolder, standaloneRoot, serverDirectory) {
|
|
1510
|
+
const pathService = yield* Path7.Path;
|
|
687
1511
|
const publicFiles = yield* getDefaultPathMap(pathService.join(projectFolder, "public"));
|
|
688
1512
|
const assetPaths = [];
|
|
689
1513
|
const assetOverrides = {};
|
|
@@ -699,8 +1523,8 @@ var buildStandalonePublicAssets = Effect5.fn("nextjs.buildStandalonePublicAssets
|
|
|
699
1523
|
}
|
|
700
1524
|
return { assetPaths, assetOverrides, filePathMap };
|
|
701
1525
|
});
|
|
702
|
-
var createStaticExportConfig =
|
|
703
|
-
const pathService = yield*
|
|
1526
|
+
var createStaticExportConfig = Effect8.fn("nextjs.createStaticExportConfig")(function* (defaults, projectFolder) {
|
|
1527
|
+
const pathService = yield* Path7.Path;
|
|
704
1528
|
const outputDirectory = pathService.join(projectFolder, "out");
|
|
705
1529
|
const files = yield* getDefaultPathMap(outputDirectory);
|
|
706
1530
|
const assetPaths = [];
|
|
@@ -723,8 +1547,8 @@ var createStaticExportConfig = Effect5.fn("nextjs.createStaticExportConfig")(fun
|
|
|
723
1547
|
package: void 0
|
|
724
1548
|
};
|
|
725
1549
|
});
|
|
726
|
-
var createStandaloneConfig =
|
|
727
|
-
const pathService = yield*
|
|
1550
|
+
var createStandaloneConfig = Effect8.fn("nextjs.createStandaloneConfig")(function* (defaults, projectFolder, manifest) {
|
|
1551
|
+
const pathService = yield* Path7.Path;
|
|
728
1552
|
const resolved = yield* resolveStandaloneServer(projectFolder, manifest.distDir, manifest.repoRootToProject);
|
|
729
1553
|
if (!resolved) return defaults;
|
|
730
1554
|
const { standaloneRoot, serverDirectory, entrypoint } = resolved;
|
|
@@ -757,7 +1581,7 @@ var createStandaloneConfig = Effect5.fn("nextjs.createStandaloneConfig")(functio
|
|
|
757
1581
|
}
|
|
758
1582
|
};
|
|
759
1583
|
});
|
|
760
|
-
var createStandaloneV2Config =
|
|
1584
|
+
var createStandaloneV2Config = Effect8.fn("nextjs.createStandaloneV2Config")(function* (defaults, projectFolder, manifest) {
|
|
761
1585
|
const resolved = yield* resolveStandaloneServer(projectFolder, manifest.distDir, manifest.repoRootToProject);
|
|
762
1586
|
if (!resolved) return defaults;
|
|
763
1587
|
const { standaloneRoot, serverDirectory, entrypoint } = resolved;
|
|
@@ -855,14 +1679,19 @@ var nextjs = {
|
|
|
855
1679
|
streaming: true,
|
|
856
1680
|
commands: ["next build"],
|
|
857
1681
|
entrypoint: ".next/standalone/server.js",
|
|
858
|
-
assetsDir
|
|
1682
|
+
// No `assetsDir`: this is only the fallback for a standalone build whose
|
|
1683
|
+
// Gigadrive manifest is missing, and `.next/static` answers
|
|
1684
|
+
// `/_next/static/...` rather than its own stripped prefix. Every path that
|
|
1685
|
+
// resolves a real Next.js build (`refineDefaultConfig`) maps those chunks
|
|
1686
|
+
// itself, so declaring the directory here would publish them at the wrong
|
|
1687
|
+
// URLs.
|
|
859
1688
|
populateAssetCache: true,
|
|
860
1689
|
routes: [{ source: "/*", destination: ".next/standalone/server.js" }],
|
|
861
1690
|
environmentVariables: { NODE_ENV: "production" }
|
|
862
1691
|
}),
|
|
863
|
-
refineDefaultConfig: (defaults, projectFolder) =>
|
|
864
|
-
const fs = yield*
|
|
865
|
-
const manifestContent = yield* fs.readFileString(`${projectFolder}/.gigadrive/nextjs.json`).pipe(
|
|
1692
|
+
refineDefaultConfig: (defaults, projectFolder) => Effect8.gen(function* () {
|
|
1693
|
+
const fs = yield* FileSystem8.FileSystem;
|
|
1694
|
+
const manifestContent = yield* fs.readFileString(`${projectFolder}/.gigadrive/nextjs.json`).pipe(Effect8.catchAll(() => Effect8.succeed(void 0)));
|
|
866
1695
|
const manifest = manifestContent ? parseGigadriveNextBuildManifest(manifestContent) : void 0;
|
|
867
1696
|
if (!manifest) return defaults;
|
|
868
1697
|
if (manifest.version === 2) {
|
|
@@ -873,8 +1702,8 @@ var nextjs = {
|
|
|
873
1702
|
};
|
|
874
1703
|
|
|
875
1704
|
// src/detection/frameworks/nuxt.ts
|
|
876
|
-
import { FileSystem as
|
|
877
|
-
import { Effect as
|
|
1705
|
+
import { FileSystem as FileSystem9 } from "@effect/platform";
|
|
1706
|
+
import { Effect as Effect9 } from "effect";
|
|
878
1707
|
var isVercelBuildOutputV3 = (content) => {
|
|
879
1708
|
try {
|
|
880
1709
|
const config = JSON.parse(content);
|
|
@@ -901,9 +1730,9 @@ var nuxt = {
|
|
|
901
1730
|
routes: [{ source: "/*", destination: ".output/server/index.mjs" }],
|
|
902
1731
|
environmentVariables: { NODE_ENV: "production", NITRO_PRESET: "node-server" }
|
|
903
1732
|
}),
|
|
904
|
-
refineDefaultConfig: (defaults, projectFolder) =>
|
|
905
|
-
const fs = yield*
|
|
906
|
-
const buildOutputConfig = yield* fs.readFileString(`${projectFolder}/.vercel/output/config.json`).pipe(
|
|
1733
|
+
refineDefaultConfig: (defaults, projectFolder) => Effect9.gen(function* () {
|
|
1734
|
+
const fs = yield* FileSystem9.FileSystem;
|
|
1735
|
+
const buildOutputConfig = yield* fs.readFileString(`${projectFolder}/.vercel/output/config.json`).pipe(Effect9.catchAll(() => Effect9.succeed(void 0)));
|
|
907
1736
|
if (!buildOutputConfig || !isVercelBuildOutputV3(buildOutputConfig)) {
|
|
908
1737
|
return defaults;
|
|
909
1738
|
}
|
|
@@ -1026,7 +1855,7 @@ var FRAMEWORK_DEFINITIONS = [
|
|
|
1026
1855
|
].sort((a, b) => b.priority - a.priority);
|
|
1027
1856
|
|
|
1028
1857
|
// src/detection/generate-config.ts
|
|
1029
|
-
import { Effect as
|
|
1858
|
+
import { Effect as Effect10 } from "effect";
|
|
1030
1859
|
var getInstallCommand = (pm) => {
|
|
1031
1860
|
switch (pm) {
|
|
1032
1861
|
case "bun":
|
|
@@ -1042,11 +1871,11 @@ var getInstallCommand = (pm) => {
|
|
|
1042
1871
|
return "npm install";
|
|
1043
1872
|
}
|
|
1044
1873
|
};
|
|
1045
|
-
var generateConfig =
|
|
1874
|
+
var generateConfig = Effect10.fn("generateConfig")(function* (framework, packageManager, projectFolder, refinedDefaults) {
|
|
1046
1875
|
const defaults = refinedDefaults ?? framework.getDefaultConfig(packageManager);
|
|
1047
1876
|
const installCmd = defaults.installCommand ?? getInstallCommand(packageManager);
|
|
1048
1877
|
const commands = [installCmd, ...defaults.commands];
|
|
1049
|
-
yield*
|
|
1878
|
+
yield* Effect10.logDebug(`Generating config for ${framework.name}`, {
|
|
1050
1879
|
runtime: defaults.runtime,
|
|
1051
1880
|
entrypoint: defaults.entrypoint,
|
|
1052
1881
|
commands
|
|
@@ -1081,10 +1910,26 @@ var generateConfig = Effect7.fn("generateConfig")(function* (framework, packageM
|
|
|
1081
1910
|
...defaults.normalizedConfig
|
|
1082
1911
|
};
|
|
1083
1912
|
if (defaults.assetsDir || defaults.assetPaths || defaults.assetPrefixes || defaults.assetManifests) {
|
|
1913
|
+
const paths = defaults.assetPaths ? [...defaults.assetPaths] : [];
|
|
1914
|
+
const overrides = { ...defaults.assetOverrides };
|
|
1915
|
+
const servesDirectoryIndexes = entrypoints.length === 0 && routes.length === 0;
|
|
1916
|
+
const assetsDir = defaults.assetsDir;
|
|
1917
|
+
if (assetsDir && !defaults.assetPaths && !defaults.assetPrefixes && !defaults.assetManifests) {
|
|
1918
|
+
for (const file of yield* collectAssetFiles(projectFolder, assetsDir)) {
|
|
1919
|
+
if (framework.language === "php" && /\.(php|phtml|phar)$/i.test(file)) continue;
|
|
1920
|
+
paths.push(`${assetsDir}/${file}`);
|
|
1921
|
+
if (!servesDirectoryIndexes || file in overrides) continue;
|
|
1922
|
+
if (file === "index.html") {
|
|
1923
|
+
overrides[file] = { path: "" };
|
|
1924
|
+
} else if (file.endsWith("/index.html")) {
|
|
1925
|
+
overrides[file] = { path: file.slice(0, -"/index.html".length) };
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1084
1929
|
config.assets = {
|
|
1085
|
-
paths
|
|
1930
|
+
paths,
|
|
1086
1931
|
prefixToStrip: defaults.assetsPrefixToStrip ?? (defaults.assetsDir ? defaults.assetsDir + "/" : ""),
|
|
1087
|
-
overrides:
|
|
1932
|
+
overrides: Object.keys(overrides).length > 0 ? overrides : void 0,
|
|
1088
1933
|
prefixes: defaults.assetPrefixes ? defaults.assetPrefixes.map((prefix) => ({ ...prefix })) : void 0,
|
|
1089
1934
|
manifests: defaults.assetManifests ? defaults.assetManifests.map((manifest) => ({ ...manifest })) : void 0,
|
|
1090
1935
|
dynamicRoutes: true,
|
|
@@ -1095,31 +1940,31 @@ var generateConfig = Effect7.fn("generateConfig")(function* (framework, packageM
|
|
|
1095
1940
|
});
|
|
1096
1941
|
|
|
1097
1942
|
// src/detection/read-dependencies.ts
|
|
1098
|
-
import { FileSystem as
|
|
1099
|
-
import { Effect as
|
|
1943
|
+
import { FileSystem as FileSystem10, Path as Path8 } from "@effect/platform";
|
|
1944
|
+
import { Effect as Effect12 } from "effect";
|
|
1100
1945
|
|
|
1101
1946
|
// src/detection/types.ts
|
|
1102
|
-
import { Schema as
|
|
1103
|
-
var ManifestNotFoundError = class extends
|
|
1104
|
-
message:
|
|
1105
|
-
filePath:
|
|
1947
|
+
import { Schema as Schema5 } from "effect";
|
|
1948
|
+
var ManifestNotFoundError = class extends Schema5.TaggedError()("ManifestNotFoundError", {
|
|
1949
|
+
message: Schema5.String,
|
|
1950
|
+
filePath: Schema5.String
|
|
1106
1951
|
}) {
|
|
1107
1952
|
};
|
|
1108
|
-
var ManifestReadError = class extends
|
|
1109
|
-
message:
|
|
1110
|
-
filePath:
|
|
1953
|
+
var ManifestReadError = class extends Schema5.TaggedError()("ManifestReadError", {
|
|
1954
|
+
message: Schema5.String,
|
|
1955
|
+
filePath: Schema5.String
|
|
1111
1956
|
}) {
|
|
1112
1957
|
};
|
|
1113
|
-
var ManifestParseError = class extends
|
|
1114
|
-
message:
|
|
1115
|
-
filePath:
|
|
1958
|
+
var ManifestParseError = class extends Schema5.TaggedError()("ManifestParseError", {
|
|
1959
|
+
message: Schema5.String,
|
|
1960
|
+
filePath: Schema5.String
|
|
1116
1961
|
}) {
|
|
1117
1962
|
};
|
|
1118
|
-
var FrameworkNotDetectedError = class extends
|
|
1963
|
+
var FrameworkNotDetectedError = class extends Schema5.TaggedError()(
|
|
1119
1964
|
"FrameworkNotDetectedError",
|
|
1120
1965
|
{
|
|
1121
|
-
message:
|
|
1122
|
-
directory:
|
|
1966
|
+
message: Schema5.String,
|
|
1967
|
+
directory: Schema5.String
|
|
1123
1968
|
}
|
|
1124
1969
|
) {
|
|
1125
1970
|
};
|
|
@@ -1129,25 +1974,25 @@ var MANIFEST_CONFIG = {
|
|
|
1129
1974
|
node: { file: "package.json", depKeys: ["dependencies", "devDependencies"] },
|
|
1130
1975
|
php: { file: "composer.json", depKeys: ["require", "require-dev"] }
|
|
1131
1976
|
};
|
|
1132
|
-
var readManifest =
|
|
1133
|
-
const fs = yield*
|
|
1134
|
-
const exists = yield* fs.exists(filePath).pipe(
|
|
1977
|
+
var readManifest = Effect12.fn("readManifest")(function* (filePath) {
|
|
1978
|
+
const fs = yield* FileSystem10.FileSystem;
|
|
1979
|
+
const exists = yield* fs.exists(filePath).pipe(Effect12.catchAll(() => Effect12.succeed(false)));
|
|
1135
1980
|
if (!exists) {
|
|
1136
|
-
return yield*
|
|
1981
|
+
return yield* Effect12.fail(new ManifestNotFoundError({ message: "Manifest not found", filePath }));
|
|
1137
1982
|
}
|
|
1138
|
-
const content = yield* fs.readFileString(filePath).pipe(
|
|
1139
|
-
const parsed = yield*
|
|
1983
|
+
const content = yield* fs.readFileString(filePath).pipe(Effect12.mapError(() => new ManifestReadError({ message: "Failed to read manifest", filePath })));
|
|
1984
|
+
const parsed = yield* Effect12.try({
|
|
1140
1985
|
try: () => JSON.parse(content),
|
|
1141
1986
|
catch: () => new ManifestParseError({ message: "Failed to parse manifest JSON", filePath })
|
|
1142
1987
|
});
|
|
1143
1988
|
return parsed;
|
|
1144
1989
|
});
|
|
1145
|
-
var readDependencies =
|
|
1146
|
-
const pathService = yield*
|
|
1990
|
+
var readDependencies = Effect12.fn("readDependencies")(function* (projectFolder, language) {
|
|
1991
|
+
const pathService = yield* Path8.Path;
|
|
1147
1992
|
const config = MANIFEST_CONFIG[language];
|
|
1148
1993
|
const filePath = pathService.join(projectFolder, config.file);
|
|
1149
1994
|
const manifest = yield* readManifest(filePath).pipe(
|
|
1150
|
-
|
|
1995
|
+
Effect12.catchTag("ManifestNotFoundError", () => Effect12.succeed(null))
|
|
1151
1996
|
);
|
|
1152
1997
|
if (!manifest) {
|
|
1153
1998
|
return /* @__PURE__ */ new Set();
|
|
@@ -1165,9 +2010,9 @@ var readDependencies = Effect9.fn("readDependencies")(function* (projectFolder,
|
|
|
1165
2010
|
});
|
|
1166
2011
|
|
|
1167
2012
|
// src/detection/detect-framework.ts
|
|
1168
|
-
var matchesDetectionItem =
|
|
1169
|
-
const fs = yield*
|
|
1170
|
-
const pathService = yield*
|
|
2013
|
+
var matchesDetectionItem = Effect13.fn("matchesDetectionItem")(function* (item, projectFolder, framework, cachedReadDeps) {
|
|
2014
|
+
const fs = yield* FileSystem11.FileSystem;
|
|
2015
|
+
const pathService = yield* Path9.Path;
|
|
1171
2016
|
if (item.matchPackage != null) {
|
|
1172
2017
|
const deps = yield* cachedReadDeps(`${projectFolder}\0${framework.language}`);
|
|
1173
2018
|
if (!deps.has(item.matchPackage)) {
|
|
@@ -1176,12 +2021,12 @@ var matchesDetectionItem = Effect10.fn("matchesDetectionItem")(function* (item,
|
|
|
1176
2021
|
}
|
|
1177
2022
|
if (item.path != null) {
|
|
1178
2023
|
const filePath = pathService.join(projectFolder, item.path);
|
|
1179
|
-
const exists = yield* fs.exists(filePath).pipe(
|
|
2024
|
+
const exists = yield* fs.exists(filePath).pipe(Effect13.catchAll(() => Effect13.succeed(false)));
|
|
1180
2025
|
if (!exists) {
|
|
1181
2026
|
return false;
|
|
1182
2027
|
}
|
|
1183
2028
|
if (item.matchContent != null) {
|
|
1184
|
-
const content = yield* fs.readFileString(filePath).pipe(
|
|
2029
|
+
const content = yield* fs.readFileString(filePath).pipe(Effect13.catchAll(() => Effect13.succeed("")));
|
|
1185
2030
|
let regexMatch = false;
|
|
1186
2031
|
try {
|
|
1187
2032
|
regexMatch = new RegExp(item.matchContent).test(content);
|
|
@@ -1194,7 +2039,7 @@ var matchesDetectionItem = Effect10.fn("matchesDetectionItem")(function* (item,
|
|
|
1194
2039
|
}
|
|
1195
2040
|
return true;
|
|
1196
2041
|
});
|
|
1197
|
-
var matchesFramework =
|
|
2042
|
+
var matchesFramework = Effect13.fn("matchesFramework")(function* (framework, projectFolder, cachedReadDeps) {
|
|
1198
2043
|
for (const detector of framework.detectors) {
|
|
1199
2044
|
const matches = yield* matchesDetectionItem(detector, projectFolder, framework, cachedReadDeps);
|
|
1200
2045
|
if (!matches) {
|
|
@@ -1203,9 +2048,9 @@ var matchesFramework = Effect10.fn("matchesFramework")(function* (framework, pro
|
|
|
1203
2048
|
}
|
|
1204
2049
|
return true;
|
|
1205
2050
|
});
|
|
1206
|
-
var detectFramework =
|
|
1207
|
-
yield*
|
|
1208
|
-
const cachedReadDeps = yield*
|
|
2051
|
+
var detectFramework = Effect13.fn("detectFramework")(function* (projectFolder) {
|
|
2052
|
+
yield* Effect13.logDebug(`Detecting framework in ${projectFolder}`);
|
|
2053
|
+
const cachedReadDeps = yield* Effect13.cachedFunction((key) => {
|
|
1209
2054
|
const sepIndex = key.lastIndexOf("\0");
|
|
1210
2055
|
const folder = key.slice(0, sepIndex);
|
|
1211
2056
|
const language = key.slice(sepIndex + 1);
|
|
@@ -1214,16 +2059,16 @@ var detectFramework = Effect10.fn("detectFramework")(function* (projectFolder) {
|
|
|
1214
2059
|
for (const framework of FRAMEWORK_DEFINITIONS) {
|
|
1215
2060
|
const matches = yield* matchesFramework(framework, projectFolder, cachedReadDeps);
|
|
1216
2061
|
if (matches) {
|
|
1217
|
-
yield*
|
|
2062
|
+
yield* Effect13.log(`Detected framework: ${framework.name}`, { slug: framework.slug });
|
|
1218
2063
|
const detectedPackageManager = yield* detectPackageManager(projectFolder);
|
|
1219
2064
|
const packageManager = framework.language === "php" ? "composer" : detectedPackageManager;
|
|
1220
2065
|
const defaults = framework.getDefaultConfig(packageManager);
|
|
1221
2066
|
const refinedDefaults = framework.refineDefaultConfig ? yield* framework.refineDefaultConfig(defaults, projectFolder) : defaults;
|
|
1222
|
-
const config = yield* generateConfig(framework, packageManager, refinedDefaults);
|
|
2067
|
+
const config = yield* generateConfig(framework, packageManager, projectFolder, refinedDefaults);
|
|
1223
2068
|
return { framework, packageManager, config };
|
|
1224
2069
|
}
|
|
1225
2070
|
}
|
|
1226
|
-
return yield*
|
|
2071
|
+
return yield* Effect13.fail(
|
|
1227
2072
|
new FrameworkNotDetectedError({
|
|
1228
2073
|
message: `No framework detected in project directory`,
|
|
1229
2074
|
directory: projectFolder
|
|
@@ -1232,15 +2077,22 @@ var detectFramework = Effect10.fn("detectFramework")(function* (projectFolder) {
|
|
|
1232
2077
|
});
|
|
1233
2078
|
|
|
1234
2079
|
// src/detection/merge-config.ts
|
|
1235
|
-
import { Effect as
|
|
1236
|
-
var mergeWithFrameworkDefaults =
|
|
1237
|
-
yield*
|
|
2080
|
+
import { Effect as Effect14 } from "effect";
|
|
2081
|
+
var mergeWithFrameworkDefaults = Effect14.fn("mergeWithFrameworkDefaults")(function* (userConfig, frameworkConfig) {
|
|
2082
|
+
yield* Effect14.logDebug("Merging user config with framework defaults");
|
|
1238
2083
|
const userHasAssets = (userConfig.assets?.paths?.length ?? 0) > 0 || (userConfig.assets?.prefixes?.length ?? 0) > 0 || (userConfig.assets?.manifests?.length ?? 0) > 0;
|
|
2084
|
+
const isContainer = (entrypoint) => entrypoint.runtime === CONTAINER_RUNTIME;
|
|
2085
|
+
const userFileFunctions = userConfig.entrypoints.filter((entrypoint) => !isContainer(entrypoint));
|
|
2086
|
+
const containerPaths = new Set(userConfig.entrypoints.filter(isContainer).map((entrypoint) => entrypoint.path));
|
|
2087
|
+
const targetsContainer = (route) => containerPaths.has(route.destination.replace(/^\/+/, ""));
|
|
1239
2088
|
const merged = {
|
|
1240
2089
|
regions: userConfig.regions,
|
|
1241
2090
|
commands: userConfig.commands.length > 0 ? userConfig.commands : frameworkConfig.commands,
|
|
1242
|
-
entrypoints:
|
|
1243
|
-
|
|
2091
|
+
entrypoints: [
|
|
2092
|
+
...userFileFunctions.length > 0 ? userFileFunctions : frameworkConfig.entrypoints,
|
|
2093
|
+
...userConfig.entrypoints.filter(isContainer)
|
|
2094
|
+
],
|
|
2095
|
+
routes: containerPaths.size > 0 && userConfig.routes.every(targetsContainer) ? [...userConfig.routes, ...frameworkConfig.routes] : userConfig.routes.length > 0 ? userConfig.routes : frameworkConfig.routes,
|
|
1244
2096
|
assets: userHasAssets ? userConfig.assets : frameworkConfig.assets,
|
|
1245
2097
|
environmentVariables: {
|
|
1246
2098
|
...frameworkConfig.environmentVariables,
|
|
@@ -1248,6 +2100,7 @@ var mergeWithFrameworkDefaults = Effect11.fn("mergeWithFrameworkDefaults")(funct
|
|
|
1248
2100
|
},
|
|
1249
2101
|
excludeFiles: userConfig.excludeFiles && userConfig.excludeFiles.length > 0 ? userConfig.excludeFiles : frameworkConfig.excludeFiles,
|
|
1250
2102
|
services: userConfig.services,
|
|
2103
|
+
...userConfig.sidecars !== void 0 && { sidecars: userConfig.sidecars },
|
|
1251
2104
|
userArchive: userConfig.userArchive,
|
|
1252
2105
|
warnings: [...frameworkConfig.warnings, ...userConfig.warnings],
|
|
1253
2106
|
errors: [...frameworkConfig.errors, ...userConfig.errors]
|
|
@@ -1255,51 +2108,6 @@ var mergeWithFrameworkDefaults = Effect11.fn("mergeWithFrameworkDefaults")(funct
|
|
|
1255
2108
|
return merged;
|
|
1256
2109
|
});
|
|
1257
2110
|
|
|
1258
|
-
// src/errors.ts
|
|
1259
|
-
import { Schema as Schema5 } from "effect";
|
|
1260
|
-
var ConfigFileNotFoundError = class extends Schema5.TaggedError()("ConfigFileNotFoundError", {
|
|
1261
|
-
message: Schema5.String,
|
|
1262
|
-
filePath: Schema5.String
|
|
1263
|
-
}) {
|
|
1264
|
-
};
|
|
1265
|
-
var ConfigFileEmptyError = class extends Schema5.TaggedError()("ConfigFileEmptyError", {
|
|
1266
|
-
message: Schema5.String,
|
|
1267
|
-
filePath: Schema5.String
|
|
1268
|
-
}) {
|
|
1269
|
-
};
|
|
1270
|
-
var ConfigFileParseError = class extends Schema5.TaggedError()("ConfigFileParseError", {
|
|
1271
|
-
message: Schema5.String,
|
|
1272
|
-
filePath: Schema5.String,
|
|
1273
|
-
cause: Schema5.optional(Schema5.String)
|
|
1274
|
-
}) {
|
|
1275
|
-
};
|
|
1276
|
-
var ConfigModuleLoadError = class extends Schema5.TaggedError()("ConfigModuleLoadError", {
|
|
1277
|
-
message: Schema5.String,
|
|
1278
|
-
filePath: Schema5.String,
|
|
1279
|
-
cause: Schema5.optional(Schema5.String)
|
|
1280
|
-
}) {
|
|
1281
|
-
};
|
|
1282
|
-
var ConfigVersionError = class extends Schema5.TaggedError()("ConfigVersionError", {
|
|
1283
|
-
message: Schema5.String,
|
|
1284
|
-
filePath: Schema5.String,
|
|
1285
|
-
version: Schema5.optional(Schema5.Number)
|
|
1286
|
-
}) {
|
|
1287
|
-
};
|
|
1288
|
-
var ConfigSchemaValidationError = class extends Schema5.TaggedError()(
|
|
1289
|
-
"ConfigSchemaValidationError",
|
|
1290
|
-
{
|
|
1291
|
-
message: Schema5.String,
|
|
1292
|
-
filePath: Schema5.String,
|
|
1293
|
-
validationErrors: Schema5.Array(Schema5.String)
|
|
1294
|
-
}
|
|
1295
|
-
) {
|
|
1296
|
-
};
|
|
1297
|
-
var FunctionConfigError = class extends Schema5.TaggedError()("FunctionConfigError", {
|
|
1298
|
-
message: Schema5.String,
|
|
1299
|
-
functionPath: Schema5.String
|
|
1300
|
-
}) {
|
|
1301
|
-
};
|
|
1302
|
-
|
|
1303
2111
|
// src/filter-functions-from-assets.ts
|
|
1304
2112
|
var filterFunctionsFromAssets = (config) => {
|
|
1305
2113
|
return {
|
|
@@ -1311,28 +2119,24 @@ var filterFunctionsFromAssets = (config) => {
|
|
|
1311
2119
|
};
|
|
1312
2120
|
};
|
|
1313
2121
|
|
|
1314
|
-
// src/normalized-config.ts
|
|
1315
|
-
var DEFAULT_FUNCTION_DURATION_SECONDS = 30;
|
|
1316
|
-
var MAX_FUNCTION_DURATION_SECONDS = 28800;
|
|
1317
|
-
|
|
1318
2122
|
// src/parse-config.ts
|
|
1319
|
-
import { Effect as
|
|
2123
|
+
import { Effect as Effect20 } from "effect";
|
|
1320
2124
|
|
|
1321
2125
|
// src/services/raw-config-reader.ts
|
|
1322
|
-
import { FileSystem as
|
|
1323
|
-
import { Effect as
|
|
1324
|
-
import { parse as
|
|
2126
|
+
import { FileSystem as FileSystem12, Path as Path10 } from "@effect/platform";
|
|
2127
|
+
import { Effect as Effect16 } from "effect";
|
|
2128
|
+
import { parse as parseYaml2 } from "yaml";
|
|
1325
2129
|
|
|
1326
2130
|
// src/services/config-module-loader.ts
|
|
1327
|
-
import { Effect as
|
|
2131
|
+
import { Effect as Effect15 } from "effect";
|
|
1328
2132
|
import { createJiti } from "jiti";
|
|
1329
2133
|
var MISSING_DEFAULT_EXPORT = /* @__PURE__ */ Symbol("MissingDefaultExport");
|
|
1330
2134
|
var STRICT_DEFAULT_EXPORT_EXTENSIONS = [".ts", ".mts", ".mjs"];
|
|
1331
|
-
var ConfigModuleLoader = class extends
|
|
1332
|
-
effect:
|
|
1333
|
-
const loadConfigModule =
|
|
2135
|
+
var ConfigModuleLoader = class extends Effect15.Service()("ConfigModuleLoader", {
|
|
2136
|
+
effect: Effect15.gen(function* () {
|
|
2137
|
+
const loadConfigModule = Effect15.fn("ConfigModuleLoader.loadConfigModule")(function* (filePath) {
|
|
1334
2138
|
const extension = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
1335
|
-
const mod = yield*
|
|
2139
|
+
const mod = yield* Effect15.tryPromise({
|
|
1336
2140
|
try: async () => {
|
|
1337
2141
|
const jiti = createJiti(filePath, { fsCache: false, interopDefault: true, moduleCache: false });
|
|
1338
2142
|
const namespace = await jiti.import(filePath);
|
|
@@ -1351,7 +2155,7 @@ var ConfigModuleLoader = class extends Effect12.Service()("ConfigModuleLoader",
|
|
|
1351
2155
|
})
|
|
1352
2156
|
});
|
|
1353
2157
|
if (mod === MISSING_DEFAULT_EXPORT) {
|
|
1354
|
-
return yield*
|
|
2158
|
+
return yield* Effect15.fail(
|
|
1355
2159
|
new ConfigModuleLoadError({
|
|
1356
2160
|
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.`,
|
|
1357
2161
|
filePath
|
|
@@ -1359,7 +2163,7 @@ var ConfigModuleLoader = class extends Effect12.Service()("ConfigModuleLoader",
|
|
|
1359
2163
|
);
|
|
1360
2164
|
}
|
|
1361
2165
|
if (typeof mod === "function") {
|
|
1362
|
-
return yield*
|
|
2166
|
+
return yield* Effect15.fail(
|
|
1363
2167
|
new ConfigModuleLoadError({
|
|
1364
2168
|
message: `Config module at ${filePath} default-exports a function; config factories are not supported. Export a config object instead (optionally wrapped in defineConfig).`,
|
|
1365
2169
|
filePath
|
|
@@ -1367,7 +2171,7 @@ var ConfigModuleLoader = class extends Effect12.Service()("ConfigModuleLoader",
|
|
|
1367
2171
|
);
|
|
1368
2172
|
}
|
|
1369
2173
|
if (typeof mod !== "object" || mod === null || Array.isArray(mod)) {
|
|
1370
|
-
return yield*
|
|
2174
|
+
return yield* Effect15.fail(
|
|
1371
2175
|
new ConfigModuleLoadError({
|
|
1372
2176
|
message: `Config module at ${filePath} must default-export a config object.`,
|
|
1373
2177
|
filePath
|
|
@@ -1397,36 +2201,36 @@ var ALLOWED_CONFIG_NAMES = [
|
|
|
1397
2201
|
"nebula.json"
|
|
1398
2202
|
];
|
|
1399
2203
|
var MODULE_EXTENSIONS = [".ts", ".mts", ".cts", ".js", ".mjs", ".cjs"];
|
|
1400
|
-
var RawConfigReader = class extends
|
|
2204
|
+
var RawConfigReader = class extends Effect16.Service()("RawConfigReader", {
|
|
1401
2205
|
dependencies: [ConfigModuleLoader.Default],
|
|
1402
|
-
effect:
|
|
1403
|
-
const fs = yield*
|
|
1404
|
-
const pathService = yield*
|
|
2206
|
+
effect: Effect16.gen(function* () {
|
|
2207
|
+
const fs = yield* FileSystem12.FileSystem;
|
|
2208
|
+
const pathService = yield* Path10.Path;
|
|
1405
2209
|
const moduleLoader = yield* ConfigModuleLoader;
|
|
1406
|
-
const findConfig =
|
|
2210
|
+
const findConfig = Effect16.fn("RawConfigReader.findConfig")(function* (projectFolder) {
|
|
1407
2211
|
for (const name of ALLOWED_CONFIG_NAMES) {
|
|
1408
2212
|
const filePath = pathService.join(projectFolder, name);
|
|
1409
|
-
const exists = yield* fs.exists(filePath).pipe(
|
|
2213
|
+
const exists = yield* fs.exists(filePath).pipe(Effect16.catchAll(() => Effect16.succeed(false)));
|
|
1410
2214
|
if (exists) {
|
|
1411
2215
|
return filePath;
|
|
1412
2216
|
}
|
|
1413
2217
|
}
|
|
1414
2218
|
return null;
|
|
1415
2219
|
});
|
|
1416
|
-
const readRawConfig =
|
|
1417
|
-
const exists = yield* fs.exists(filePath).pipe(
|
|
2220
|
+
const readRawConfig = Effect16.fn("RawConfigReader.readRawConfig")(function* (filePath, options) {
|
|
2221
|
+
const exists = yield* fs.exists(filePath).pipe(Effect16.catchAll(() => Effect16.succeed(false)));
|
|
1418
2222
|
if (!exists) {
|
|
1419
|
-
return yield*
|
|
2223
|
+
return yield* Effect16.fail(
|
|
1420
2224
|
new ConfigFileNotFoundError({ message: `Config file not found at ${filePath}`, filePath })
|
|
1421
2225
|
);
|
|
1422
2226
|
}
|
|
1423
2227
|
const fileContents = yield* fs.readFileString(filePath).pipe(
|
|
1424
|
-
|
|
2228
|
+
Effect16.mapError(
|
|
1425
2229
|
() => new ConfigFileParseError({ message: `Failed to read config file at ${filePath}`, filePath })
|
|
1426
2230
|
)
|
|
1427
2231
|
);
|
|
1428
2232
|
if (fileContents.length === 0) {
|
|
1429
|
-
return yield*
|
|
2233
|
+
return yield* Effect16.fail(
|
|
1430
2234
|
new ConfigFileEmptyError({ message: `Config file is empty at ${filePath}`, filePath })
|
|
1431
2235
|
);
|
|
1432
2236
|
}
|
|
@@ -1439,10 +2243,10 @@ var RawConfigReader = class extends Effect13.Service()("RawConfigReader", {
|
|
|
1439
2243
|
if (fileExtension === ".json") {
|
|
1440
2244
|
parsed = JSON.parse(fileContents);
|
|
1441
2245
|
} else {
|
|
1442
|
-
parsed =
|
|
2246
|
+
parsed = parseYaml2(fileContents);
|
|
1443
2247
|
}
|
|
1444
2248
|
} catch (error) {
|
|
1445
|
-
return yield*
|
|
2249
|
+
return yield* Effect16.fail(
|
|
1446
2250
|
new ConfigFileParseError({
|
|
1447
2251
|
message: `Failed to parse config file at ${filePath}: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1448
2252
|
filePath,
|
|
@@ -1452,21 +2256,21 @@ var RawConfigReader = class extends Effect13.Service()("RawConfigReader", {
|
|
|
1452
2256
|
}
|
|
1453
2257
|
}
|
|
1454
2258
|
if (parsed == null) {
|
|
1455
|
-
return yield*
|
|
2259
|
+
return yield* Effect16.fail(
|
|
1456
2260
|
new ConfigFileParseError({ message: `Config file could not be parsed at ${filePath}`, filePath })
|
|
1457
2261
|
);
|
|
1458
2262
|
}
|
|
1459
2263
|
if (!options?.disableVersionCheck && typeof parsed.version !== "number") {
|
|
1460
|
-
return yield*
|
|
2264
|
+
return yield* Effect16.fail(
|
|
1461
2265
|
new ConfigVersionError({ message: `Config file is missing version at ${filePath}`, filePath })
|
|
1462
2266
|
);
|
|
1463
2267
|
}
|
|
1464
2268
|
return parsed;
|
|
1465
2269
|
});
|
|
1466
2270
|
const readConfigFile = (configFilePath) => readRawConfig(configFilePath, { disableVersionCheck: true }).pipe(
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
2271
|
+
Effect16.map((parsed) => parsed),
|
|
2272
|
+
Effect16.catchTag("ConfigFileNotFoundError", () => Effect16.succeed(null)),
|
|
2273
|
+
Effect16.withSpan("RawConfigReader.readConfigFile")
|
|
1470
2274
|
);
|
|
1471
2275
|
return { findConfig, readRawConfig, readConfigFile };
|
|
1472
2276
|
})
|
|
@@ -1476,17 +2280,17 @@ var RawConfigReader = class extends Effect13.Service()("RawConfigReader", {
|
|
|
1476
2280
|
// src/services/schema-validator.ts
|
|
1477
2281
|
import Ajv from "ajv";
|
|
1478
2282
|
import addFormats from "ajv-formats";
|
|
1479
|
-
import { Effect as
|
|
1480
|
-
var SchemaValidator = class extends
|
|
1481
|
-
effect:
|
|
2283
|
+
import { Effect as Effect17 } from "effect";
|
|
2284
|
+
var SchemaValidator = class extends Effect17.Service()("SchemaValidator", {
|
|
2285
|
+
effect: Effect17.gen(function* () {
|
|
1482
2286
|
const ajv = new Ajv();
|
|
1483
2287
|
addFormats(ajv);
|
|
1484
|
-
const validate =
|
|
2288
|
+
const validate = Effect17.fn("SchemaValidator.validate")(function* (config, schema, filePath) {
|
|
1485
2289
|
const compiledValidate = ajv.compile(schema);
|
|
1486
2290
|
const valid = compiledValidate(config);
|
|
1487
2291
|
if (!valid) {
|
|
1488
2292
|
const errors = compiledValidate.errors?.map((e) => `${e.instancePath || "/"} ${e.message ?? ""}`.trim()) ?? [];
|
|
1489
|
-
return yield*
|
|
2293
|
+
return yield* Effect17.fail(
|
|
1490
2294
|
new ConfigSchemaValidationError({
|
|
1491
2295
|
message: `Config file at ${filePath} is invalid: ${ajv.errorsText(compiledValidate.errors)}`,
|
|
1492
2296
|
filePath,
|
|
@@ -1500,9 +2304,6 @@ var SchemaValidator = class extends Effect14.Service()("SchemaValidator", {
|
|
|
1500
2304
|
}) {
|
|
1501
2305
|
};
|
|
1502
2306
|
|
|
1503
|
-
// src/services/v4-config-parser.ts
|
|
1504
|
-
import { FileSystem as FileSystem10, Path as Path8 } from "@effect/platform";
|
|
1505
|
-
|
|
1506
2307
|
// ../build-utils/src/get-files-for-pattern.ts
|
|
1507
2308
|
import { minimatch } from "minimatch";
|
|
1508
2309
|
import { readdir } from "fs/promises";
|
|
@@ -1545,11 +2346,10 @@ var getFilesForPattern = async (pattern, projectFolder, excludeFiles = []) => {
|
|
|
1545
2346
|
};
|
|
1546
2347
|
|
|
1547
2348
|
// src/services/v4-config-parser.ts
|
|
1548
|
-
import { Effect as
|
|
2349
|
+
import { Effect as Effect18 } from "effect";
|
|
1549
2350
|
import { minimatch as minimatch2 } from "minimatch";
|
|
2351
|
+
import { posix as posix2 } from "path";
|
|
1550
2352
|
import safeRegex from "safe-regex2";
|
|
1551
|
-
var MAX_ASSET_DEPTH = 100;
|
|
1552
|
-
var DISALLOWED_ASSET_EXTENSIONS = [".htaccess", ".htpasswd"];
|
|
1553
2353
|
var DEFAULT_FUNCTION_SETTINGS = {
|
|
1554
2354
|
memory: 128,
|
|
1555
2355
|
max_duration: DEFAULT_FUNCTION_DURATION_SECONDS,
|
|
@@ -1581,6 +2381,46 @@ var normalizeImagePolicy = (images) => {
|
|
|
1581
2381
|
variants: images.variants ?? {}
|
|
1582
2382
|
};
|
|
1583
2383
|
};
|
|
2384
|
+
var DURATION_UNIT_SECONDS = { s: 1, m: 60, h: 3600, d: 86400 };
|
|
2385
|
+
var queueDurationSeconds = (value) => {
|
|
2386
|
+
if (value === void 0) return void 0;
|
|
2387
|
+
if (typeof value === "number") return value;
|
|
2388
|
+
const match = /^(\d+(?:\.\d+)?)(s|m|h|d)$/.exec(value);
|
|
2389
|
+
if (!match)
|
|
2390
|
+
throw new Error(`Invalid queue duration "${value}". Use seconds or a string such as 30s, 10m, 1.5h or 4d.`);
|
|
2391
|
+
return Math.ceil(Number(match[1]) * DURATION_UNIT_SECONDS[match[2]]);
|
|
2392
|
+
};
|
|
2393
|
+
var byName = (left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0;
|
|
2394
|
+
var definedOnly = (value) => Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
|
|
2395
|
+
var normalizeQueue = (name, queue) => {
|
|
2396
|
+
if (queue === null) return { name };
|
|
2397
|
+
const schedules = queue.schedules ? Object.entries(queue.schedules).map(
|
|
2398
|
+
([scheduleName, schedule]) => definedOnly({
|
|
2399
|
+
name: scheduleName,
|
|
2400
|
+
cron: schedule.cron,
|
|
2401
|
+
timezone: schedule.timezone,
|
|
2402
|
+
body: schedule.body === void 0 || typeof schedule.body === "string" ? schedule.body : JSON.stringify(schedule.body),
|
|
2403
|
+
// A plain-text body labelled JSON would fail to decode on every fire.
|
|
2404
|
+
contentType: schedule.contentType ?? (typeof schedule.body === "string" ? "text/plain; charset=utf-8" : void 0),
|
|
2405
|
+
headers: schedule.headers,
|
|
2406
|
+
enabled: schedule.enabled
|
|
2407
|
+
})
|
|
2408
|
+
).sort(byName) : void 0;
|
|
2409
|
+
return definedOnly({
|
|
2410
|
+
name,
|
|
2411
|
+
consumer: queue.consumer,
|
|
2412
|
+
visibilityTimeoutSeconds: queueDurationSeconds(queue.visibilityTimeout),
|
|
2413
|
+
retentionSeconds: queueDurationSeconds(queue.retention),
|
|
2414
|
+
maxAttempts: queue.maxAttempts,
|
|
2415
|
+
retryBackoffMinSeconds: queueDurationSeconds(queue.retryBackoff?.min),
|
|
2416
|
+
retryBackoffMaxSeconds: queueDurationSeconds(queue.retryBackoff?.max),
|
|
2417
|
+
deduplicationWindowSeconds: queueDurationSeconds(queue.deduplicationWindow),
|
|
2418
|
+
concurrency: queue.concurrency,
|
|
2419
|
+
rateLimit: queue.rateLimit == null ? queue.rateLimit : { count: queue.rateLimit.count, periodSeconds: queueDurationSeconds(queue.rateLimit.period) ?? 1 },
|
|
2420
|
+
deadLetter: queue.deadLetter,
|
|
2421
|
+
schedules
|
|
2422
|
+
});
|
|
2423
|
+
};
|
|
1584
2424
|
var normalizeServices = (services) => {
|
|
1585
2425
|
if (services == null) return void 0;
|
|
1586
2426
|
const normalized = [];
|
|
@@ -1593,7 +2433,13 @@ var normalizeServices = (services) => {
|
|
|
1593
2433
|
if (services.storage !== void 0) {
|
|
1594
2434
|
normalized.push({
|
|
1595
2435
|
type: "storage",
|
|
1596
|
-
buckets: Object.entries(services.storage.buckets).map(([name, bucket]) => ({ name, visibility: bucket?.visibility ?? "private" })).sort(
|
|
2436
|
+
buckets: Object.entries(services.storage.buckets).map(([name, bucket]) => ({ name, visibility: bucket?.visibility ?? "private" })).sort(byName)
|
|
2437
|
+
});
|
|
2438
|
+
}
|
|
2439
|
+
if (services.queues !== void 0) {
|
|
2440
|
+
normalized.push({
|
|
2441
|
+
type: "queues",
|
|
2442
|
+
queues: Object.entries(services.queues).map(([name, queue]) => normalizeQueue(name, queue)).sort(byName)
|
|
1597
2443
|
});
|
|
1598
2444
|
}
|
|
1599
2445
|
return normalized.length > 0 ? normalized : void 0;
|
|
@@ -1651,9 +2497,10 @@ var mapRoute = (route, entrypoints) => ({
|
|
|
1651
2497
|
negativeRequirements: route.missing,
|
|
1652
2498
|
status: route.statusCode
|
|
1653
2499
|
});
|
|
1654
|
-
var parseEntrypoints =
|
|
2500
|
+
var parseEntrypoints = Effect18.fn("parseEntrypoints")(function* (config, projectFolder) {
|
|
1655
2501
|
const entrypoints = [];
|
|
1656
|
-
|
|
2502
|
+
const dockerFunctions = [];
|
|
2503
|
+
if (config.functions == null) return { entrypoints, dockerFunctions };
|
|
1657
2504
|
for (const [fnPath, func] of Object.entries(config.functions)) {
|
|
1658
2505
|
if (getFunctionSettings(fnPath, config) == null) {
|
|
1659
2506
|
return yield* new FunctionConfigError({
|
|
@@ -1661,7 +2508,7 @@ var parseEntrypoints = Effect15.fn("parseEntrypoints")(function* (config, projec
|
|
|
1661
2508
|
functionPath: fnPath
|
|
1662
2509
|
});
|
|
1663
2510
|
}
|
|
1664
|
-
const matchedFiles = yield*
|
|
2511
|
+
const matchedFiles = yield* Effect18.tryPromise({
|
|
1665
2512
|
try: () => getFilesForPattern(fnPath, projectFolder, func.excludeFiles),
|
|
1666
2513
|
catch: (error) => new FunctionConfigError({
|
|
1667
2514
|
message: `Failed to resolve files for function pattern '${fnPath}': ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -1678,6 +2525,35 @@ var parseEntrypoints = Effect15.fn("parseEntrypoints")(function* (config, projec
|
|
|
1678
2525
|
functionPath: file
|
|
1679
2526
|
});
|
|
1680
2527
|
}
|
|
2528
|
+
if (settings.runtime === CONTAINER_RUNTIME) {
|
|
2529
|
+
const unsupported = ["symlinks", "includeFiles"].filter((key) => settings[key] !== void 0);
|
|
2530
|
+
if (unsupported.length > 0) {
|
|
2531
|
+
return yield* new FunctionConfigError({
|
|
2532
|
+
message: `Function '${file}' runs a Dockerfile, so ${unsupported.join(" and ")} do not apply. Copy files in the Dockerfile instead.`,
|
|
2533
|
+
functionPath: file
|
|
2534
|
+
});
|
|
2535
|
+
}
|
|
2536
|
+
const declared = Object.entries(config.functions).filter(([pattern]) => matchesPattern(file, pattern)).reduce((merged, [, value]) => ({ ...merged, ...value }), {});
|
|
2537
|
+
dockerFunctions.push({
|
|
2538
|
+
file,
|
|
2539
|
+
name: dockerFunctionContainerName(file),
|
|
2540
|
+
container: {
|
|
2541
|
+
build: { context: posix2.dirname(file), dockerfile: posix2.basename(file) },
|
|
2542
|
+
...declared.port !== void 0 && { port: declared.port },
|
|
2543
|
+
...declared.memory !== void 0 && { memory: declared.memory },
|
|
2544
|
+
...declared.max_duration !== void 0 && { max_duration: declared.max_duration },
|
|
2545
|
+
...declared.streaming !== void 0 && { streaming: declared.streaming },
|
|
2546
|
+
...func.schedule !== void 0 && { schedule: func.schedule }
|
|
2547
|
+
}
|
|
2548
|
+
});
|
|
2549
|
+
continue;
|
|
2550
|
+
}
|
|
2551
|
+
if (settings.port !== void 0) {
|
|
2552
|
+
return yield* new FunctionConfigError({
|
|
2553
|
+
message: `Function '${file}' sets port, which only applies to 'runtime: docker'.`,
|
|
2554
|
+
functionPath: file
|
|
2555
|
+
});
|
|
2556
|
+
}
|
|
1681
2557
|
const runtime = settings.runtime ?? "node-20";
|
|
1682
2558
|
const streaming = settings.streaming ?? runtimeStreamsByDefault(runtime);
|
|
1683
2559
|
entrypoints.push({
|
|
@@ -1695,61 +2571,84 @@ var parseEntrypoints = Effect15.fn("parseEntrypoints")(function* (config, projec
|
|
|
1695
2571
|
});
|
|
1696
2572
|
}
|
|
1697
2573
|
}
|
|
1698
|
-
|
|
2574
|
+
dockerFunctions.sort((left, right) => left.file < right.file ? -1 : left.file > right.file ? 1 : 0);
|
|
2575
|
+
for (const [index, { file, name }] of dockerFunctions.entries()) {
|
|
2576
|
+
const clash = dockerFunctions.slice(0, index).find((candidate) => candidate.name === name);
|
|
2577
|
+
if (clash !== void 0) {
|
|
2578
|
+
return yield* new FunctionConfigError({
|
|
2579
|
+
message: `Functions '${clash.file}' and '${file}' would both be named '${name}'. Declare one of them under 'containers' with an explicit name.`,
|
|
2580
|
+
functionPath: file
|
|
2581
|
+
});
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
return { entrypoints, dockerFunctions };
|
|
1699
2585
|
});
|
|
1700
|
-
var collectAssets =
|
|
2586
|
+
var collectAssets = Effect18.fn("collectAssets")(function* (config, projectFolder) {
|
|
1701
2587
|
if (config.assets == null) return [];
|
|
1702
|
-
const
|
|
1703
|
-
|
|
1704
|
-
const assetsPath = pathService.join(projectFolder, config.assets);
|
|
1705
|
-
const assetsExist = yield* fs.exists(assetsPath).pipe(Effect15.catchAll(() => Effect15.succeed(false)));
|
|
1706
|
-
if (!assetsExist) return [];
|
|
1707
|
-
const stat = yield* fs.stat(assetsPath).pipe(Effect15.catchAll(() => Effect15.succeed(null)));
|
|
1708
|
-
if (!stat || stat.type !== "Directory") return [];
|
|
1709
|
-
const allFiles = yield* collectFilesRecursively(assetsPath);
|
|
1710
|
-
const assets = [];
|
|
1711
|
-
for (const assetName of allFiles) {
|
|
1712
|
-
if (DISALLOWED_ASSET_EXTENSIONS.some((ext) => assetName.toLowerCase().endsWith(ext.toLowerCase()))) continue;
|
|
1713
|
-
if (getFunctionSettings(`${config.assets}/${assetName}`, config) != null) continue;
|
|
1714
|
-
assets.push(`${config.assets}/${assetName}`);
|
|
1715
|
-
}
|
|
1716
|
-
return assets;
|
|
2588
|
+
const assetNames = yield* collectAssetFiles(projectFolder, config.assets);
|
|
2589
|
+
return assetNames.filter((assetName) => getFunctionSettings(`${config.assets}/${assetName}`, config) == null).map((assetName) => `${config.assets}/${assetName}`);
|
|
1717
2590
|
});
|
|
1718
|
-
var
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
const
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
2591
|
+
var reservedRuntimePorts = (entrypoint) => entrypoint.runtime === CONTAINER_RUNTIME ? [] : entrypoint.runtime.startsWith("php-") ? [8080, 9e3] : [8080];
|
|
2592
|
+
var parseContainers = Effect18.fn("parseContainers")(function* (config, projectFolder, fileEntrypoints, dockerFunctions) {
|
|
2593
|
+
const warnings = [];
|
|
2594
|
+
let containers = {};
|
|
2595
|
+
if (config.compose != null) {
|
|
2596
|
+
const imported = yield* readComposeContainers(config.compose, projectFolder);
|
|
2597
|
+
containers = imported.containers;
|
|
2598
|
+
warnings.push(...imported.warnings);
|
|
2599
|
+
}
|
|
2600
|
+
containers = { ...containers, ...config.containers ?? {} };
|
|
2601
|
+
for (const { file, name, container } of dockerFunctions) {
|
|
2602
|
+
if (containers[name] !== void 0) {
|
|
2603
|
+
return yield* new ContainerConfigError({
|
|
2604
|
+
message: `Function '${file}' runs as container '${name}', which 'containers' or the Compose file already declares. Rename one of them.`,
|
|
2605
|
+
containerName: name
|
|
2606
|
+
});
|
|
2607
|
+
}
|
|
2608
|
+
containers[name] = container;
|
|
2609
|
+
}
|
|
2610
|
+
const result = yield* normalizeContainers(containers, projectFolder);
|
|
2611
|
+
warnings.push(...result.warnings);
|
|
2612
|
+
for (const entrypoint of fileEntrypoints) {
|
|
2613
|
+
for (const port of reservedRuntimePorts(entrypoint)) {
|
|
2614
|
+
const sidecar = result.sidecars.find((candidate) => candidate.port === port);
|
|
2615
|
+
if (sidecar !== void 0) {
|
|
2616
|
+
return yield* new ContainerConfigError({
|
|
2617
|
+
message: `Sidecar '${sidecar.name}' listens on port ${port}, which the ${entrypoint.runtime} runtime of '${entrypoint.path}' uses inside the same microVM. Choose another port.`,
|
|
2618
|
+
containerName: sidecar.name
|
|
2619
|
+
});
|
|
1736
2620
|
}
|
|
1737
2621
|
}
|
|
1738
|
-
return result;
|
|
1739
2622
|
}
|
|
1740
|
-
|
|
1741
|
-
|
|
2623
|
+
return { ...result, warnings };
|
|
2624
|
+
});
|
|
2625
|
+
var V4ConfigParser = class extends Effect18.Service()("V4ConfigParser", {
|
|
1742
2626
|
accessors: true,
|
|
1743
|
-
effect:
|
|
2627
|
+
effect: Effect18.succeed({
|
|
1744
2628
|
/**
|
|
1745
2629
|
* Parses a ConfigV4 into a NormalizedConfig.
|
|
1746
2630
|
*
|
|
1747
2631
|
* @param config - The raw V4 config object
|
|
1748
2632
|
* @param projectFolder - Absolute path to the project root
|
|
1749
2633
|
*/
|
|
1750
|
-
parse:
|
|
1751
|
-
const entrypoints = yield* parseEntrypoints(config, projectFolder);
|
|
2634
|
+
parse: Effect18.fn("V4ConfigParser.parse")(function* (config, projectFolder) {
|
|
2635
|
+
const { entrypoints: fileEntrypoints, dockerFunctions } = yield* parseEntrypoints(config, projectFolder);
|
|
2636
|
+
const containers = yield* parseContainers(config, projectFolder, fileEntrypoints, dockerFunctions);
|
|
2637
|
+
const entrypoints = [...fileEntrypoints, ...containers.entrypoints];
|
|
1752
2638
|
const assets = yield* collectAssets(config, projectFolder);
|
|
2639
|
+
const dockerFunctionPaths = new Map(
|
|
2640
|
+
dockerFunctions.map(({ file, name }) => [file, containerEntrypointPath(name)])
|
|
2641
|
+
);
|
|
2642
|
+
const routes = (config.routes ?? []).map(
|
|
2643
|
+
(route) => mapRoute(
|
|
2644
|
+
{
|
|
2645
|
+
...route,
|
|
2646
|
+
destination: dockerFunctionPaths.get(normalizeRouteDestination(route.destination)) ?? route.destination
|
|
2647
|
+
},
|
|
2648
|
+
entrypoints
|
|
2649
|
+
)
|
|
2650
|
+
);
|
|
2651
|
+
const warnings = [...containers.warnings];
|
|
1753
2652
|
return {
|
|
1754
2653
|
regions: resolveRegions(config.regions),
|
|
1755
2654
|
assets: {
|
|
@@ -1762,10 +2661,11 @@ var V4ConfigParser = class extends Effect15.Service()("V4ConfigParser", {
|
|
|
1762
2661
|
commands: config.build_commands ?? [],
|
|
1763
2662
|
images: normalizeImagePolicy(config.images),
|
|
1764
2663
|
services: normalizeServices(config.services),
|
|
2664
|
+
...containers.sidecars.length > 0 && { sidecars: containers.sidecars },
|
|
1765
2665
|
entrypoints,
|
|
1766
2666
|
errors: [],
|
|
1767
|
-
warnings
|
|
1768
|
-
routes
|
|
2667
|
+
warnings,
|
|
2668
|
+
routes
|
|
1769
2669
|
};
|
|
1770
2670
|
})
|
|
1771
2671
|
})
|
|
@@ -1773,7 +2673,7 @@ var V4ConfigParser = class extends Effect15.Service()("V4ConfigParser", {
|
|
|
1773
2673
|
};
|
|
1774
2674
|
|
|
1775
2675
|
// src/services/vercel-build-output-parser.ts
|
|
1776
|
-
import { Path as
|
|
2676
|
+
import { Path as Path11 } from "@effect/platform";
|
|
1777
2677
|
|
|
1778
2678
|
// ../commons/src/deep-merge.ts
|
|
1779
2679
|
var deepMerge = (target, ...partials) => {
|
|
@@ -1802,7 +2702,7 @@ var deepMerge = (target, ...partials) => {
|
|
|
1802
2702
|
};
|
|
1803
2703
|
|
|
1804
2704
|
// src/services/vercel-build-output-parser.ts
|
|
1805
|
-
import { Effect as
|
|
2705
|
+
import { Effect as Effect19 } from "effect";
|
|
1806
2706
|
|
|
1807
2707
|
// src/build-output-v3/determine-repo-root.ts
|
|
1808
2708
|
import path from "path";
|
|
@@ -1898,11 +2798,11 @@ var translateVercelRuntime = (runtime) => {
|
|
|
1898
2798
|
var CONFIG_PATH = ".vercel/output/config.json";
|
|
1899
2799
|
var FUNCTIONS_FOLDER = ".vercel/output/functions";
|
|
1900
2800
|
var FUNCTION_CONFIGS_PATTERN = `${FUNCTIONS_FOLDER}/**/*.func/.vc-config.json`;
|
|
1901
|
-
var VercelBuildOutputParser = class extends
|
|
1902
|
-
effect:
|
|
2801
|
+
var VercelBuildOutputParser = class extends Effect19.Service()("VercelBuildOutputParser", {
|
|
2802
|
+
effect: Effect19.gen(function* () {
|
|
1903
2803
|
const rawConfigReader = yield* RawConfigReader;
|
|
1904
|
-
const pathService = yield*
|
|
1905
|
-
const parse =
|
|
2804
|
+
const pathService = yield* Path11.Path;
|
|
2805
|
+
const parse = Effect19.fn("VercelBuildOutputParser.parse")(function* (parseResult, projectFolder) {
|
|
1906
2806
|
const config = yield* rawConfigReader.readConfigFile(pathService.join(projectFolder, CONFIG_PATH));
|
|
1907
2807
|
if (!config || typeof config.version !== "number" || config.version < 3) {
|
|
1908
2808
|
return parseResult;
|
|
@@ -1914,8 +2814,8 @@ var VercelBuildOutputParser = class extends Effect16.Service()("VercelBuildOutpu
|
|
|
1914
2814
|
merged.regions = [...new Set(merged.regions)];
|
|
1915
2815
|
return merged;
|
|
1916
2816
|
});
|
|
1917
|
-
const loadFunctions =
|
|
1918
|
-
const functionConfigs = yield*
|
|
2817
|
+
const loadFunctions = Effect19.fn("VercelBuildOutputParser.loadFunctions")(function* (config, parseResult, projectFolder) {
|
|
2818
|
+
const functionConfigs = yield* Effect19.tryPromise({
|
|
1919
2819
|
try: () => getFilesForPattern(FUNCTION_CONFIGS_PATTERN, projectFolder),
|
|
1920
2820
|
catch: (error) => new ConfigFileParseError({
|
|
1921
2821
|
message: `Failed to scan for function configs: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -2201,17 +3101,22 @@ var schema_default = {
|
|
|
2201
3101
|
minimum: 1
|
|
2202
3102
|
},
|
|
2203
3103
|
runtime: {
|
|
2204
|
-
$ref: "#/$defs/runtimes",
|
|
2205
|
-
description: "The runtime to use for the function."
|
|
3104
|
+
anyOf: [{ $ref: "#/$defs/runtimes" }, { const: "docker" }],
|
|
3105
|
+
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."
|
|
3106
|
+
},
|
|
3107
|
+
port: {
|
|
3108
|
+
type: "integer",
|
|
3109
|
+
minimum: 1,
|
|
3110
|
+
maximum: 65535,
|
|
3111
|
+
description: "TCP port the image listens on. Only for `runtime: docker`. Defaults to the first exposed port of the image, then 8080."
|
|
2206
3112
|
},
|
|
2207
3113
|
streaming: {
|
|
2208
3114
|
type: "boolean",
|
|
2209
3115
|
description: "Enable function response streaming. When omitted, Node and Bun runtimes stream by default."
|
|
2210
3116
|
},
|
|
2211
3117
|
schedule: {
|
|
2212
|
-
|
|
2213
|
-
description: "An expression to schedule the function to run at specific times."
|
|
2214
|
-
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]+))?)*)[)])$"
|
|
3118
|
+
$ref: "#/$defs/schedule",
|
|
3119
|
+
description: "An expression to schedule the function to run at specific times."
|
|
2215
3120
|
},
|
|
2216
3121
|
symlinks: {
|
|
2217
3122
|
type: "object",
|
|
@@ -2303,6 +3208,151 @@ var schema_default = {
|
|
|
2303
3208
|
}
|
|
2304
3209
|
}
|
|
2305
3210
|
},
|
|
3211
|
+
compose: {
|
|
3212
|
+
type: ["string", "null"],
|
|
3213
|
+
minLength: 1,
|
|
3214
|
+
maxLength: 1024,
|
|
3215
|
+
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."
|
|
3216
|
+
},
|
|
3217
|
+
containers: {
|
|
3218
|
+
type: ["object", "null"],
|
|
3219
|
+
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>`.",
|
|
3220
|
+
maxProperties: 16,
|
|
3221
|
+
propertyNames: {
|
|
3222
|
+
pattern: "^[a-z][a-z0-9_-]{0,62}$",
|
|
3223
|
+
not: {
|
|
3224
|
+
enum: ["localhost"]
|
|
3225
|
+
}
|
|
3226
|
+
},
|
|
3227
|
+
additionalProperties: {
|
|
3228
|
+
type: "object",
|
|
3229
|
+
additionalProperties: false,
|
|
3230
|
+
oneOf: [
|
|
3231
|
+
{
|
|
3232
|
+
required: ["image"]
|
|
3233
|
+
},
|
|
3234
|
+
{
|
|
3235
|
+
required: ["build"]
|
|
3236
|
+
}
|
|
3237
|
+
],
|
|
3238
|
+
properties: {
|
|
3239
|
+
image: {
|
|
3240
|
+
type: "string",
|
|
3241
|
+
minLength: 1,
|
|
3242
|
+
maxLength: 512,
|
|
3243
|
+
pattern: "^[^\\s]+$",
|
|
3244
|
+
description: "Registry image reference, e.g. `redis:7-alpine` or `ghcr.io/acme/api:1.4`."
|
|
3245
|
+
},
|
|
3246
|
+
build: {
|
|
3247
|
+
description: "Build context directory, or the full build settings, for a Dockerfile in the repository.",
|
|
3248
|
+
oneOf: [
|
|
3249
|
+
{
|
|
3250
|
+
type: "string",
|
|
3251
|
+
minLength: 1,
|
|
3252
|
+
maxLength: 1024
|
|
3253
|
+
},
|
|
3254
|
+
{
|
|
3255
|
+
type: "object",
|
|
3256
|
+
additionalProperties: false,
|
|
3257
|
+
properties: {
|
|
3258
|
+
context: {
|
|
3259
|
+
type: "string",
|
|
3260
|
+
minLength: 1,
|
|
3261
|
+
maxLength: 1024
|
|
3262
|
+
},
|
|
3263
|
+
dockerfile: {
|
|
3264
|
+
type: "string",
|
|
3265
|
+
minLength: 1,
|
|
3266
|
+
maxLength: 1024
|
|
3267
|
+
},
|
|
3268
|
+
target: {
|
|
3269
|
+
type: "string",
|
|
3270
|
+
minLength: 1,
|
|
3271
|
+
maxLength: 128
|
|
3272
|
+
},
|
|
3273
|
+
args: {
|
|
3274
|
+
type: "object",
|
|
3275
|
+
maxProperties: 100,
|
|
3276
|
+
additionalProperties: {
|
|
3277
|
+
type: "string",
|
|
3278
|
+
maxLength: 65536
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
]
|
|
3284
|
+
},
|
|
3285
|
+
sidecar: {
|
|
3286
|
+
type: "boolean",
|
|
3287
|
+
description: "Run next to every function instance instead of serving routes. Defaults to false."
|
|
3288
|
+
},
|
|
3289
|
+
port: {
|
|
3290
|
+
type: "integer",
|
|
3291
|
+
minimum: 1,
|
|
3292
|
+
maximum: 65535,
|
|
3293
|
+
description: "TCP port the container listens on. Defaults to the first exposed port of the image, then 8080."
|
|
3294
|
+
},
|
|
3295
|
+
entrypoint: {
|
|
3296
|
+
$ref: "#/$defs/stringOrStringArray",
|
|
3297
|
+
description: "Replaces the image ENTRYPOINT. A string is split into words the way Compose splits it."
|
|
3298
|
+
},
|
|
3299
|
+
command: {
|
|
3300
|
+
$ref: "#/$defs/stringOrStringArray",
|
|
3301
|
+
description: "Replaces the image CMD. A string is split into words the way Compose splits it."
|
|
3302
|
+
},
|
|
3303
|
+
env: {
|
|
3304
|
+
type: "object",
|
|
3305
|
+
maxProperties: 100,
|
|
3306
|
+
additionalProperties: {
|
|
3307
|
+
type: "string",
|
|
3308
|
+
maxLength: 65536
|
|
3309
|
+
},
|
|
3310
|
+
description: "Environment variables baked into the container, merged over the image ENV."
|
|
3311
|
+
},
|
|
3312
|
+
working_dir: {
|
|
3313
|
+
type: "string",
|
|
3314
|
+
minLength: 1,
|
|
3315
|
+
maxLength: 1024,
|
|
3316
|
+
pattern: "^/",
|
|
3317
|
+
description: "Replaces the image WORKDIR. Must be absolute."
|
|
3318
|
+
},
|
|
3319
|
+
user: {
|
|
3320
|
+
oneOf: [
|
|
3321
|
+
{
|
|
3322
|
+
type: "string",
|
|
3323
|
+
minLength: 1,
|
|
3324
|
+
maxLength: 256
|
|
3325
|
+
},
|
|
3326
|
+
{
|
|
3327
|
+
type: "integer",
|
|
3328
|
+
minimum: 0
|
|
3329
|
+
}
|
|
3330
|
+
],
|
|
3331
|
+
description: "Replaces the image USER: a name, a uid, `name:group` or `uid:gid`. A bare uid may be a number."
|
|
3332
|
+
},
|
|
3333
|
+
memory: {
|
|
3334
|
+
type: "integer",
|
|
3335
|
+
minimum: 128,
|
|
3336
|
+
maximum: 3009,
|
|
3337
|
+
description: "Memory in MB. Defaults to 512 for a container function and 256 for a sidecar."
|
|
3338
|
+
},
|
|
3339
|
+
max_duration: {
|
|
3340
|
+
type: "number",
|
|
3341
|
+
minimum: 1,
|
|
3342
|
+
maximum: 28800,
|
|
3343
|
+
description: "Maximum lifetime in seconds of one request. Container functions only."
|
|
3344
|
+
},
|
|
3345
|
+
streaming: {
|
|
3346
|
+
type: "boolean",
|
|
3347
|
+
description: "Stream responses. Defaults to true. Container functions only."
|
|
3348
|
+
},
|
|
3349
|
+
schedule: {
|
|
3350
|
+
$ref: "#/$defs/schedule",
|
|
3351
|
+
description: "An expression to run the container function at specific times. Container functions only."
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3355
|
+
},
|
|
2306
3356
|
services: {
|
|
2307
3357
|
type: "object",
|
|
2308
3358
|
description: "Managed services to provision for the deployment environment.",
|
|
@@ -2453,11 +3503,179 @@ var schema_default = {
|
|
|
2453
3503
|
}
|
|
2454
3504
|
}
|
|
2455
3505
|
}
|
|
3506
|
+
},
|
|
3507
|
+
queues: {
|
|
3508
|
+
type: "object",
|
|
3509
|
+
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.",
|
|
3510
|
+
propertyNames: {
|
|
3511
|
+
maxLength: 255,
|
|
3512
|
+
pattern: "^[A-Za-z0-9_][A-Za-z0-9_.~:@+=-]*$"
|
|
3513
|
+
},
|
|
3514
|
+
additionalProperties: {
|
|
3515
|
+
oneOf: [
|
|
3516
|
+
{
|
|
3517
|
+
type: "null"
|
|
3518
|
+
},
|
|
3519
|
+
{
|
|
3520
|
+
type: "object",
|
|
3521
|
+
additionalProperties: false,
|
|
3522
|
+
properties: {
|
|
3523
|
+
consumer: {
|
|
3524
|
+
type: ["string", "null"],
|
|
3525
|
+
pattern: "^/(?!/)\\S*$",
|
|
3526
|
+
maxLength: 2048,
|
|
3527
|
+
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."
|
|
3528
|
+
},
|
|
3529
|
+
visibilityTimeout: {
|
|
3530
|
+
$ref: "#/$defs/queueDuration",
|
|
3531
|
+
description: "How long a push delivery may run, or how long a received message stays leased. Defaults to 60 seconds, at most 12 hours."
|
|
3532
|
+
},
|
|
3533
|
+
retention: {
|
|
3534
|
+
$ref: "#/$defs/queueDuration",
|
|
3535
|
+
description: "How long an undelivered message is kept. Defaults to 4 days, at most 14 days."
|
|
3536
|
+
},
|
|
3537
|
+
maxAttempts: {
|
|
3538
|
+
type: "integer",
|
|
3539
|
+
minimum: 1,
|
|
3540
|
+
maximum: 100,
|
|
3541
|
+
description: "Deliveries per message before it moves to the dead-letter list. Defaults to 10."
|
|
3542
|
+
},
|
|
3543
|
+
retryBackoff: {
|
|
3544
|
+
type: "object",
|
|
3545
|
+
additionalProperties: false,
|
|
3546
|
+
description: "Exponential backoff between failed deliveries. Defaults to 5 seconds, doubling up to 15 minutes.",
|
|
3547
|
+
properties: {
|
|
3548
|
+
min: {
|
|
3549
|
+
$ref: "#/$defs/queueDuration"
|
|
3550
|
+
},
|
|
3551
|
+
max: {
|
|
3552
|
+
$ref: "#/$defs/queueDuration"
|
|
3553
|
+
}
|
|
3554
|
+
}
|
|
3555
|
+
},
|
|
3556
|
+
deduplicationWindow: {
|
|
3557
|
+
$ref: "#/$defs/queueDuration",
|
|
3558
|
+
description: "How long a deduplication key is remembered. Defaults to 24 hours, at most 7 days."
|
|
3559
|
+
},
|
|
3560
|
+
concurrency: {
|
|
3561
|
+
type: ["integer", "null"],
|
|
3562
|
+
minimum: 1,
|
|
3563
|
+
maximum: 1e3,
|
|
3564
|
+
description: "Most push deliveries in flight at once. Defaults to 100."
|
|
3565
|
+
},
|
|
3566
|
+
rateLimit: {
|
|
3567
|
+
oneOf: [
|
|
3568
|
+
{
|
|
3569
|
+
type: "null"
|
|
3570
|
+
},
|
|
3571
|
+
{
|
|
3572
|
+
type: "object",
|
|
3573
|
+
required: ["count"],
|
|
3574
|
+
additionalProperties: false,
|
|
3575
|
+
properties: {
|
|
3576
|
+
count: {
|
|
3577
|
+
type: "integer",
|
|
3578
|
+
minimum: 1,
|
|
3579
|
+
maximum: 1e6
|
|
3580
|
+
},
|
|
3581
|
+
period: {
|
|
3582
|
+
$ref: "#/$defs/queueDuration"
|
|
3583
|
+
}
|
|
3584
|
+
}
|
|
3585
|
+
}
|
|
3586
|
+
],
|
|
3587
|
+
description: "Most deliveries started per period. The period defaults to one second."
|
|
3588
|
+
},
|
|
3589
|
+
deadLetter: {
|
|
3590
|
+
type: "boolean",
|
|
3591
|
+
description: "Keep messages that run out of attempts so you can inspect and redrive them. Defaults to true."
|
|
3592
|
+
},
|
|
3593
|
+
schedules: {
|
|
3594
|
+
type: "object",
|
|
3595
|
+
description: "Cron schedules that send a message into this queue, keyed by schedule name.",
|
|
3596
|
+
propertyNames: {
|
|
3597
|
+
maxLength: 128,
|
|
3598
|
+
pattern: "^[A-Za-z0-9_][A-Za-z0-9_.-]*$"
|
|
3599
|
+
},
|
|
3600
|
+
additionalProperties: {
|
|
3601
|
+
type: "object",
|
|
3602
|
+
required: ["cron"],
|
|
3603
|
+
additionalProperties: false,
|
|
3604
|
+
properties: {
|
|
3605
|
+
cron: {
|
|
3606
|
+
type: "string",
|
|
3607
|
+
minLength: 1,
|
|
3608
|
+
maxLength: 255,
|
|
3609
|
+
description: "Five-field cron expression, or a macro such as @hourly or @daily."
|
|
3610
|
+
},
|
|
3611
|
+
timezone: {
|
|
3612
|
+
type: "string",
|
|
3613
|
+
description: "IANA time zone the expression is evaluated in. Defaults to UTC."
|
|
3614
|
+
},
|
|
3615
|
+
body: {
|
|
3616
|
+
description: "Message body. Strings are sent as is; any other value is sent as JSON."
|
|
3617
|
+
},
|
|
3618
|
+
contentType: {
|
|
3619
|
+
type: "string",
|
|
3620
|
+
description: "Content type of the body. Defaults to text/plain for a string body and application/json otherwise."
|
|
3621
|
+
},
|
|
3622
|
+
headers: {
|
|
3623
|
+
type: "object",
|
|
3624
|
+
additionalProperties: {
|
|
3625
|
+
type: "string"
|
|
3626
|
+
},
|
|
3627
|
+
description: "Headers delivered with every message from this schedule."
|
|
3628
|
+
},
|
|
3629
|
+
enabled: {
|
|
3630
|
+
type: "boolean",
|
|
3631
|
+
description: "Set to false to keep the schedule without sending. Defaults to true."
|
|
3632
|
+
}
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3635
|
+
}
|
|
3636
|
+
}
|
|
3637
|
+
}
|
|
3638
|
+
]
|
|
3639
|
+
}
|
|
2456
3640
|
}
|
|
2457
3641
|
}
|
|
2458
3642
|
}
|
|
2459
3643
|
},
|
|
2460
3644
|
$defs: {
|
|
3645
|
+
schedule: {
|
|
3646
|
+
type: "string",
|
|
3647
|
+
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]+))?)*)[)])$"
|
|
3648
|
+
},
|
|
3649
|
+
stringOrStringArray: {
|
|
3650
|
+
oneOf: [
|
|
3651
|
+
{
|
|
3652
|
+
type: "string",
|
|
3653
|
+
minLength: 1,
|
|
3654
|
+
maxLength: 4096
|
|
3655
|
+
},
|
|
3656
|
+
{
|
|
3657
|
+
type: "array",
|
|
3658
|
+
maxItems: 256,
|
|
3659
|
+
items: {
|
|
3660
|
+
type: "string",
|
|
3661
|
+
maxLength: 4096
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3664
|
+
]
|
|
3665
|
+
},
|
|
3666
|
+
queueDuration: {
|
|
3667
|
+
oneOf: [
|
|
3668
|
+
{
|
|
3669
|
+
type: "integer",
|
|
3670
|
+
minimum: 0
|
|
3671
|
+
},
|
|
3672
|
+
{
|
|
3673
|
+
type: "string",
|
|
3674
|
+
pattern: "^[0-9]+(\\.[0-9]+)?(s|m|h|d)$"
|
|
3675
|
+
}
|
|
3676
|
+
],
|
|
3677
|
+
description: "A duration in seconds, or a string such as 30s, 10m, 1.5h or 4d."
|
|
3678
|
+
},
|
|
2461
3679
|
runtimes: {
|
|
2462
3680
|
type: "string",
|
|
2463
3681
|
enum: ["php-84", "php-83", "php-81", "node-22", "node-20", "node-18", "node-16", "bun-1"]
|
|
@@ -2514,7 +3732,7 @@ var schema_default = {
|
|
|
2514
3732
|
};
|
|
2515
3733
|
|
|
2516
3734
|
// src/parse-config.ts
|
|
2517
|
-
var parseConfigRaw =
|
|
3735
|
+
var parseConfigRaw = Effect20.fn("parseConfigRaw")(function* (filePath, projectFolder) {
|
|
2518
3736
|
const rawReader = yield* RawConfigReader;
|
|
2519
3737
|
const validator = yield* SchemaValidator;
|
|
2520
3738
|
const v4Parser = yield* V4ConfigParser;
|
|
@@ -2525,7 +3743,7 @@ var parseConfigRaw = Effect17.fn("parseConfigRaw")(function* (filePath, projectF
|
|
|
2525
3743
|
};
|
|
2526
3744
|
const schema = schemaMap[version];
|
|
2527
3745
|
if (!schema) {
|
|
2528
|
-
return yield*
|
|
3746
|
+
return yield* Effect20.fail(
|
|
2529
3747
|
new ConfigVersionError({
|
|
2530
3748
|
message: `Config file at ${filePath} has an unsupported version: ${version}`,
|
|
2531
3749
|
filePath,
|
|
@@ -2536,14 +3754,42 @@ var parseConfigRaw = Effect17.fn("parseConfigRaw")(function* (filePath, projectF
|
|
|
2536
3754
|
yield* validator.validate(parsed, schema, filePath);
|
|
2537
3755
|
return yield* v4Parser.parse(parsed, projectFolder);
|
|
2538
3756
|
});
|
|
2539
|
-
var parseConfig =
|
|
3757
|
+
var parseConfig = Effect20.fn("parseConfig")(function* (filePath, projectFolder) {
|
|
2540
3758
|
const parseResult = yield* parseConfigRaw(filePath, projectFolder);
|
|
2541
3759
|
return yield* postProcessConfig(parseResult, projectFolder);
|
|
2542
3760
|
});
|
|
2543
|
-
var postProcessConfig =
|
|
3761
|
+
var postProcessConfig = Effect20.fn("postProcessConfig")(function* (config, projectFolder) {
|
|
2544
3762
|
const vercelParser = yield* VercelBuildOutputParser;
|
|
2545
3763
|
let result = yield* vercelParser.parse(config, projectFolder);
|
|
2546
3764
|
const hasAssets = (result.assets?.paths?.length ?? 0) > 0 || (result.assets?.prefixes?.length ?? 0) > 0 || (result.assets?.manifests?.length ?? 0) > 0;
|
|
3765
|
+
const containerFunctions = result.entrypoints.filter((entrypoint) => entrypoint.runtime === CONTAINER_RUNTIME);
|
|
3766
|
+
const [onlyFunction] = result.entrypoints;
|
|
3767
|
+
if (onlyFunction !== void 0 && onlyFunction.runtime === CONTAINER_RUNTIME && result.entrypoints.length === 1 && result.routes.length === 0 && !hasAssets) {
|
|
3768
|
+
result = {
|
|
3769
|
+
...result,
|
|
3770
|
+
routes: [
|
|
3771
|
+
{
|
|
3772
|
+
path: "/*",
|
|
3773
|
+
destination: onlyFunction.path,
|
|
3774
|
+
handler: onlyFunction.streaming === true ? "SERVERLESS_FUNCTION_STREAMING" : "SERVERLESS_FUNCTION",
|
|
3775
|
+
methods: ["ANY"],
|
|
3776
|
+
headers: {}
|
|
3777
|
+
}
|
|
3778
|
+
]
|
|
3779
|
+
};
|
|
3780
|
+
} else {
|
|
3781
|
+
for (const entrypoint of containerFunctions) {
|
|
3782
|
+
const routed = result.routes.some((route) => route.destination.replace(/^\/+/, "") === entrypoint.path);
|
|
3783
|
+
if (!routed && entrypoint.schedule === void 0) {
|
|
3784
|
+
result.warnings.push(
|
|
3785
|
+
`Container function '${entrypoint.displayName ?? entrypoint.path}' has no route and no schedule, so nothing reaches it. Add a route with destination '${entrypoint.path}'.`
|
|
3786
|
+
);
|
|
3787
|
+
}
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
if ((result.sidecars?.length ?? 0) > 0 && result.entrypoints.length === 0) {
|
|
3791
|
+
result.warnings.push("Sidecars run next to functions, and this deployment has none, so no sidecar will start.");
|
|
3792
|
+
}
|
|
2547
3793
|
if (result.entrypoints.length === 0 && !hasAssets && result.routes.length === 0) {
|
|
2548
3794
|
result.errors.push(
|
|
2549
3795
|
"The current project config does not resolve to any functions, assets or routes and can not be deployed."
|
|
@@ -2581,6 +3827,9 @@ export {
|
|
|
2581
3827
|
ALLOWED_CONFIG_NAMES,
|
|
2582
3828
|
AVAILABLE_REGIONS,
|
|
2583
3829
|
AVAILABLE_RUNTIMES,
|
|
3830
|
+
COMPOSE_FILE_NAMES,
|
|
3831
|
+
CONTAINER_ENTRYPOINT_PREFIX,
|
|
3832
|
+
CONTAINER_RUNTIME,
|
|
2584
3833
|
ConfigFileEmptyError,
|
|
2585
3834
|
ConfigFileNotFoundError,
|
|
2586
3835
|
ConfigFileParseError,
|
|
@@ -2588,7 +3837,10 @@ export {
|
|
|
2588
3837
|
ConfigModuleLoader,
|
|
2589
3838
|
ConfigSchemaValidationError,
|
|
2590
3839
|
ConfigVersionError,
|
|
3840
|
+
ContainerConfigError,
|
|
3841
|
+
DEFAULT_CONTAINER_MEMORY_MB,
|
|
2591
3842
|
DEFAULT_FUNCTION_DURATION_SECONDS,
|
|
3843
|
+
DEFAULT_SIDECAR_MEMORY_MB,
|
|
2592
3844
|
FRAMEWORK_DEFINITIONS,
|
|
2593
3845
|
FrameworkNotDetectedError,
|
|
2594
3846
|
FunctionConfigError,
|
|
@@ -2605,6 +3857,7 @@ export {
|
|
|
2605
3857
|
GigadriveNextServerDescriptorSchema,
|
|
2606
3858
|
GigadriveNextStaticAssetPrefixSchema,
|
|
2607
3859
|
MAX_FUNCTION_DURATION_SECONDS,
|
|
3860
|
+
MAX_SIDECARS,
|
|
2608
3861
|
ManifestNotFoundError,
|
|
2609
3862
|
ManifestParseError,
|
|
2610
3863
|
ManifestReadError,
|
|
@@ -2616,19 +3869,27 @@ export {
|
|
|
2616
3869
|
StaticAssetManifestV1Schema,
|
|
2617
3870
|
V4ConfigParser,
|
|
2618
3871
|
VercelBuildOutputParser,
|
|
3872
|
+
collectAssetFiles,
|
|
3873
|
+
composeDeclaresApp,
|
|
3874
|
+
containerEntrypointPath,
|
|
2619
3875
|
defineConfig,
|
|
3876
|
+
detectContainerProject,
|
|
2620
3877
|
detectFramework,
|
|
2621
3878
|
detectPackageManager,
|
|
3879
|
+
dockerFunctionContainerName,
|
|
2622
3880
|
filterFunctionsFromAssets,
|
|
2623
3881
|
generateConfig,
|
|
2624
3882
|
getFunctionSettings,
|
|
2625
3883
|
mergeWithFrameworkDefaults,
|
|
3884
|
+
normalizeContainers,
|
|
2626
3885
|
parseConfig,
|
|
2627
3886
|
parseConfigRaw,
|
|
2628
3887
|
parseGigadriveNextBuildManifest,
|
|
2629
3888
|
parseGigadriveNextPrerenderManifest,
|
|
2630
3889
|
parseStaticAssetManifest,
|
|
2631
3890
|
postProcessConfig,
|
|
2632
|
-
|
|
3891
|
+
readComposeContainers,
|
|
3892
|
+
readDependencies,
|
|
3893
|
+
splitCommandWords
|
|
2633
3894
|
};
|
|
2634
3895
|
//# sourceMappingURL=index.mjs.map
|