@effortless-aws/cli 0.2.3 → 0.4.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/cli/index.js +473 -586
- package/package.json +2 -2
package/dist/cli/index.js
CHANGED
|
@@ -14,7 +14,7 @@ import { createRequire as createRequire2 } from "module";
|
|
|
14
14
|
// src/cli/commands/deploy.ts
|
|
15
15
|
import { Args, Command } from "@effect/cli";
|
|
16
16
|
import { Effect as Effect38, Console as Console3, Logger, LogLevel, Option } from "effect";
|
|
17
|
-
import * as
|
|
17
|
+
import * as path10 from "path";
|
|
18
18
|
|
|
19
19
|
// src/deploy/deploy.ts
|
|
20
20
|
import { Effect as Effect35, Console as Console2 } from "effect";
|
|
@@ -1933,6 +1933,11 @@ var publishVersion = (functionName) => Effect13.gen(function* () {
|
|
|
1933
1933
|
version: result.Version
|
|
1934
1934
|
};
|
|
1935
1935
|
});
|
|
1936
|
+
var DEFAULT_CORS = {
|
|
1937
|
+
AllowOrigins: ["*"],
|
|
1938
|
+
AllowMethods: ["*"],
|
|
1939
|
+
AllowHeaders: ["*"]
|
|
1940
|
+
};
|
|
1936
1941
|
var ensureFunctionUrl = (functionName) => Effect13.gen(function* () {
|
|
1937
1942
|
const existing = yield* lambda_exports.make("get_function_url_config", {
|
|
1938
1943
|
FunctionName: functionName
|
|
@@ -1945,7 +1950,8 @@ var ensureFunctionUrl = (functionName) => Effect13.gen(function* () {
|
|
|
1945
1950
|
if (existing) {
|
|
1946
1951
|
yield* lambda_exports.make("update_function_url_config", {
|
|
1947
1952
|
FunctionName: functionName,
|
|
1948
|
-
AuthType: "NONE"
|
|
1953
|
+
AuthType: "NONE",
|
|
1954
|
+
Cors: DEFAULT_CORS
|
|
1949
1955
|
});
|
|
1950
1956
|
return { functionUrl: existing.FunctionUrl };
|
|
1951
1957
|
}
|
|
@@ -1953,7 +1959,8 @@ var ensureFunctionUrl = (functionName) => Effect13.gen(function* () {
|
|
|
1953
1959
|
const result = yield* lambda_exports.make("create_function_url_config", {
|
|
1954
1960
|
FunctionName: functionName,
|
|
1955
1961
|
AuthType: "NONE",
|
|
1956
|
-
InvokeMode: "BUFFERED"
|
|
1962
|
+
InvokeMode: "BUFFERED",
|
|
1963
|
+
Cors: DEFAULT_CORS
|
|
1957
1964
|
});
|
|
1958
1965
|
return { functionUrl: result.FunctionUrl };
|
|
1959
1966
|
});
|
|
@@ -2419,133 +2426,6 @@ var deleteTable = (tableName) => Effect16.gen(function* () {
|
|
|
2419
2426
|
|
|
2420
2427
|
// src/aws/apigateway.ts
|
|
2421
2428
|
import { Effect as Effect17 } from "effect";
|
|
2422
|
-
var ensureProjectApi = (config) => Effect17.gen(function* () {
|
|
2423
|
-
const apiName = `${config.projectName}-${config.stage}`;
|
|
2424
|
-
const existingApis = yield* apigatewayv2_exports.make("get_apis", {});
|
|
2425
|
-
const existingApi = existingApis.Items?.find((api) => api.Name === apiName);
|
|
2426
|
-
let apiId;
|
|
2427
|
-
if (existingApi) {
|
|
2428
|
-
yield* Effect17.logDebug(`Using existing API Gateway: ${apiName}`);
|
|
2429
|
-
apiId = existingApi.ApiId;
|
|
2430
|
-
if (config.tags) {
|
|
2431
|
-
const apiArn = `arn:aws:apigateway:${config.region}::/apis/${apiId}`;
|
|
2432
|
-
yield* apigatewayv2_exports.make("tag_resource", {
|
|
2433
|
-
ResourceArn: apiArn,
|
|
2434
|
-
Tags: config.tags
|
|
2435
|
-
});
|
|
2436
|
-
}
|
|
2437
|
-
} else {
|
|
2438
|
-
yield* Effect17.logDebug(`Creating API Gateway: ${apiName}`);
|
|
2439
|
-
const createResult = yield* apigatewayv2_exports.make("create_api", {
|
|
2440
|
-
Name: apiName,
|
|
2441
|
-
ProtocolType: "HTTP",
|
|
2442
|
-
CorsConfiguration: {
|
|
2443
|
-
AllowOrigins: ["*"],
|
|
2444
|
-
AllowMethods: ["*"],
|
|
2445
|
-
AllowHeaders: ["*"]
|
|
2446
|
-
},
|
|
2447
|
-
Tags: config.tags
|
|
2448
|
-
});
|
|
2449
|
-
apiId = createResult.ApiId;
|
|
2450
|
-
yield* apigatewayv2_exports.make("create_stage", {
|
|
2451
|
-
ApiId: apiId,
|
|
2452
|
-
StageName: "$default",
|
|
2453
|
-
AutoDeploy: true
|
|
2454
|
-
});
|
|
2455
|
-
}
|
|
2456
|
-
return { apiId };
|
|
2457
|
-
});
|
|
2458
|
-
var addRouteToApi = (config) => Effect17.gen(function* () {
|
|
2459
|
-
const integrationUri = `arn:aws:apigateway:${config.region}:lambda:path/2015-03-31/functions/${config.functionArn}/invocations`;
|
|
2460
|
-
const existingIntegrations = yield* apigatewayv2_exports.make("get_integrations", { ApiId: config.apiId });
|
|
2461
|
-
let integrationId = existingIntegrations.Items?.find(
|
|
2462
|
-
(i) => i.IntegrationUri === integrationUri
|
|
2463
|
-
)?.IntegrationId;
|
|
2464
|
-
if (!integrationId) {
|
|
2465
|
-
yield* Effect17.logDebug("Creating integration");
|
|
2466
|
-
const integrationResult = yield* apigatewayv2_exports.make("create_integration", {
|
|
2467
|
-
ApiId: config.apiId,
|
|
2468
|
-
IntegrationType: "AWS_PROXY",
|
|
2469
|
-
IntegrationUri: integrationUri,
|
|
2470
|
-
IntegrationMethod: "POST",
|
|
2471
|
-
PayloadFormatVersion: "2.0"
|
|
2472
|
-
});
|
|
2473
|
-
integrationId = integrationResult.IntegrationId;
|
|
2474
|
-
}
|
|
2475
|
-
const routeKey = `${config.method} ${config.path}`;
|
|
2476
|
-
const existingRoutes = yield* apigatewayv2_exports.make("get_routes", { ApiId: config.apiId });
|
|
2477
|
-
const existingRoute = existingRoutes.Items?.find((r) => r.RouteKey === routeKey);
|
|
2478
|
-
const target = `integrations/${integrationId}`;
|
|
2479
|
-
if (!existingRoute) {
|
|
2480
|
-
yield* Effect17.logDebug(`Creating route: ${routeKey}`);
|
|
2481
|
-
yield* apigatewayv2_exports.make("create_route", {
|
|
2482
|
-
ApiId: config.apiId,
|
|
2483
|
-
RouteKey: routeKey,
|
|
2484
|
-
Target: target
|
|
2485
|
-
});
|
|
2486
|
-
} else if (existingRoute.Target !== target) {
|
|
2487
|
-
yield* Effect17.logDebug(`Updating route target: ${routeKey}`);
|
|
2488
|
-
yield* apigatewayv2_exports.make("update_route", {
|
|
2489
|
-
ApiId: config.apiId,
|
|
2490
|
-
RouteId: existingRoute.RouteId,
|
|
2491
|
-
RouteKey: routeKey,
|
|
2492
|
-
Target: target
|
|
2493
|
-
});
|
|
2494
|
-
} else {
|
|
2495
|
-
yield* Effect17.logDebug(`Route already exists: ${routeKey}`);
|
|
2496
|
-
}
|
|
2497
|
-
yield* addLambdaPermission(config.functionArn, config.apiId, config.region);
|
|
2498
|
-
const apiUrl = `https://${config.apiId}.execute-api.${config.region}.amazonaws.com${config.path}`;
|
|
2499
|
-
return { apiUrl };
|
|
2500
|
-
});
|
|
2501
|
-
var addLambdaPermission = (functionArn, apiId, region) => Effect17.gen(function* () {
|
|
2502
|
-
const statementId = `apigateway-${apiId}`;
|
|
2503
|
-
const accountId = functionArn.split(":")[4];
|
|
2504
|
-
const sourceArn = `arn:aws:execute-api:${region}:${accountId}:${apiId}/*/*`;
|
|
2505
|
-
yield* lambda_exports.make("add_permission", {
|
|
2506
|
-
FunctionName: functionArn,
|
|
2507
|
-
StatementId: statementId,
|
|
2508
|
-
Action: "lambda:InvokeFunction",
|
|
2509
|
-
Principal: "apigateway.amazonaws.com",
|
|
2510
|
-
SourceArn: sourceArn
|
|
2511
|
-
}).pipe(
|
|
2512
|
-
Effect17.catchIf(
|
|
2513
|
-
(e) => e._tag === "LambdaError" && e.is("ResourceConflictException"),
|
|
2514
|
-
() => {
|
|
2515
|
-
return Effect17.logDebug("Permission already exists");
|
|
2516
|
-
}
|
|
2517
|
-
)
|
|
2518
|
-
);
|
|
2519
|
-
});
|
|
2520
|
-
var removeStaleRoutes = (apiId, activeRouteKeys) => Effect17.gen(function* () {
|
|
2521
|
-
const existingRoutes = yield* apigatewayv2_exports.make("get_routes", { ApiId: apiId });
|
|
2522
|
-
for (const route of existingRoutes.Items ?? []) {
|
|
2523
|
-
if (route.RouteKey && !activeRouteKeys.has(route.RouteKey)) {
|
|
2524
|
-
yield* Effect17.logDebug(`Removing stale route: ${route.RouteKey}`);
|
|
2525
|
-
yield* apigatewayv2_exports.make("delete_route", {
|
|
2526
|
-
ApiId: apiId,
|
|
2527
|
-
RouteId: route.RouteId
|
|
2528
|
-
});
|
|
2529
|
-
const integrationId = route.Target?.replace("integrations/", "");
|
|
2530
|
-
if (integrationId) {
|
|
2531
|
-
const remainingRoutes = yield* apigatewayv2_exports.make("get_routes", { ApiId: apiId });
|
|
2532
|
-
const stillUsed = remainingRoutes.Items?.some((r) => r.Target === route.Target);
|
|
2533
|
-
if (!stillUsed) {
|
|
2534
|
-
yield* Effect17.logDebug(`Removing orphaned integration: ${integrationId}`);
|
|
2535
|
-
yield* apigatewayv2_exports.make("delete_integration", {
|
|
2536
|
-
ApiId: apiId,
|
|
2537
|
-
IntegrationId: integrationId
|
|
2538
|
-
}).pipe(
|
|
2539
|
-
Effect17.catchIf(
|
|
2540
|
-
(e) => e._tag === "ApiGatewayV2Error" && e.is("NotFoundException"),
|
|
2541
|
-
() => Effect17.logDebug(`Integration ${integrationId} already removed`)
|
|
2542
|
-
)
|
|
2543
|
-
);
|
|
2544
|
-
}
|
|
2545
|
-
}
|
|
2546
|
-
}
|
|
2547
|
-
}
|
|
2548
|
-
});
|
|
2549
2429
|
var deleteApi = (apiId) => Effect17.gen(function* () {
|
|
2550
2430
|
yield* Effect17.logDebug(`Deleting API Gateway: ${apiId}`);
|
|
2551
2431
|
yield* apigatewayv2_exports.make("delete_api", {
|
|
@@ -3080,6 +2960,7 @@ var syncFiles = (input) => Effect19.gen(function* () {
|
|
|
3080
2960
|
walkDir(sourceDir, "");
|
|
3081
2961
|
let uploaded = 0;
|
|
3082
2962
|
let unchanged = 0;
|
|
2963
|
+
const uploadedKeys = [];
|
|
3083
2964
|
for (const [key, filePath] of localFiles) {
|
|
3084
2965
|
const content = fs2.readFileSync(filePath);
|
|
3085
2966
|
const md5 = crypto3.createHash("md5").update(content).digest("hex");
|
|
@@ -3100,8 +2981,9 @@ var syncFiles = (input) => Effect19.gen(function* () {
|
|
|
3100
2981
|
CacheControl: cacheControl
|
|
3101
2982
|
});
|
|
3102
2983
|
uploaded++;
|
|
2984
|
+
uploadedKeys.push(key);
|
|
3103
2985
|
}
|
|
3104
|
-
const keysToDelete = [...existingObjects.keys()].filter((k) => !localFiles.has(k));
|
|
2986
|
+
const keysToDelete = [...existingObjects.keys()].filter((k) => !localFiles.has(k) && !k.startsWith("_effortless/"));
|
|
3105
2987
|
let deleted = 0;
|
|
3106
2988
|
if (keysToDelete.length > 0) {
|
|
3107
2989
|
for (let i = 0; i < keysToDelete.length; i += 1e3) {
|
|
@@ -3117,7 +2999,7 @@ var syncFiles = (input) => Effect19.gen(function* () {
|
|
|
3117
2999
|
}
|
|
3118
3000
|
}
|
|
3119
3001
|
yield* Effect19.logDebug(`S3 sync: ${uploaded} uploaded, ${deleted} deleted, ${unchanged} unchanged`);
|
|
3120
|
-
return { uploaded, deleted, unchanged };
|
|
3002
|
+
return { uploaded, deleted, unchanged, uploadedKeys };
|
|
3121
3003
|
});
|
|
3122
3004
|
var putObject = (input) => s3_exports.make("put_object", {
|
|
3123
3005
|
Bucket: input.bucketName,
|
|
@@ -4125,7 +4007,7 @@ var parseSource = (source) => {
|
|
|
4125
4007
|
const project = new Project({ useInMemoryFileSystem: true });
|
|
4126
4008
|
return project.createSourceFile("input.ts", source);
|
|
4127
4009
|
};
|
|
4128
|
-
var RUNTIME_PROPS = ["
|
|
4010
|
+
var RUNTIME_PROPS = ["onRecord", "onBatchComplete", "onBatch", "onMessage", "onObjectCreated", "onObjectRemoved", "setup", "schema", "onError", "deps", "config", "static", "middleware", "routes", "get", "post"];
|
|
4129
4011
|
var evalConfig = (configText, exportName) => {
|
|
4130
4012
|
try {
|
|
4131
4013
|
return new Function(`return ${configText}`)();
|
|
@@ -4250,12 +4132,6 @@ var extractRoutePatterns = (obj) => {
|
|
|
4250
4132
|
}).filter(Boolean);
|
|
4251
4133
|
};
|
|
4252
4134
|
var handlerRegistry = {
|
|
4253
|
-
http: {
|
|
4254
|
-
defineFn: "defineHttp",
|
|
4255
|
-
handlerProps: ["onRequest"],
|
|
4256
|
-
wrapperFn: "wrapHttp",
|
|
4257
|
-
wrapperPath: "~/runtime/wrap-http"
|
|
4258
|
-
},
|
|
4259
4135
|
table: {
|
|
4260
4136
|
defineFn: "defineTable",
|
|
4261
4137
|
handlerProps: ["onRecord", "onBatch"],
|
|
@@ -4362,7 +4238,6 @@ export const handler = ${wrapperFn}(${importName});
|
|
|
4362
4238
|
};
|
|
4363
4239
|
|
|
4364
4240
|
// src/build/bundle.ts
|
|
4365
|
-
var extractConfigs = (source) => extractHandlerConfigs(source, "http");
|
|
4366
4241
|
var extractTableConfigs = (source) => extractHandlerConfigs(source, "table");
|
|
4367
4242
|
var extractAppConfigs = (source) => extractHandlerConfigs(source, "app");
|
|
4368
4243
|
var extractStaticSiteConfigs = (source) => extractHandlerConfigs(source, "staticSite");
|
|
@@ -4374,7 +4249,7 @@ var _require = createRequire(import.meta.url);
|
|
|
4374
4249
|
var runtimeDir = path3.join(path3.dirname(_require.resolve("effortless-aws/package.json")), "dist/runtime");
|
|
4375
4250
|
var bundle = (input) => Effect24.gen(function* () {
|
|
4376
4251
|
const exportName = input.exportName ?? "default";
|
|
4377
|
-
const type = input.type ?? "
|
|
4252
|
+
const type = input.type ?? "api";
|
|
4378
4253
|
const externals = input.external ?? [];
|
|
4379
4254
|
const sourcePath = path3.isAbsolute(input.file) ? input.file : `./${input.file}`;
|
|
4380
4255
|
const entryPoint = generateEntryPoint(sourcePath, exportName, type, runtimeDir);
|
|
@@ -4463,7 +4338,6 @@ var findHandlerFiles = (patterns, cwd) => {
|
|
|
4463
4338
|
return Array.from(files);
|
|
4464
4339
|
};
|
|
4465
4340
|
var discoverHandlers = (files) => {
|
|
4466
|
-
const httpHandlers = [];
|
|
4467
4341
|
const tableHandlers = [];
|
|
4468
4342
|
const appHandlers = [];
|
|
4469
4343
|
const staticSiteHandlers = [];
|
|
@@ -4474,7 +4348,6 @@ var discoverHandlers = (files) => {
|
|
|
4474
4348
|
for (const file of files) {
|
|
4475
4349
|
if (!fsSync2.statSync(file).isFile()) continue;
|
|
4476
4350
|
const source = fsSync2.readFileSync(file, "utf-8");
|
|
4477
|
-
const http = extractConfigs(source);
|
|
4478
4351
|
const table = extractTableConfigs(source);
|
|
4479
4352
|
const app = extractAppConfigs(source);
|
|
4480
4353
|
const staticSite = extractStaticSiteConfigs(source);
|
|
@@ -4482,7 +4355,6 @@ var discoverHandlers = (files) => {
|
|
|
4482
4355
|
const bucket = extractBucketConfigs(source);
|
|
4483
4356
|
const mailer = extractMailerConfigs(source);
|
|
4484
4357
|
const api = extractApiConfigs(source);
|
|
4485
|
-
if (http.length > 0) httpHandlers.push({ file, exports: http });
|
|
4486
4358
|
if (table.length > 0) tableHandlers.push({ file, exports: table });
|
|
4487
4359
|
if (app.length > 0) appHandlers.push({ file, exports: app });
|
|
4488
4360
|
if (staticSite.length > 0) staticSiteHandlers.push({ file, exports: staticSite });
|
|
@@ -4491,12 +4363,11 @@ var discoverHandlers = (files) => {
|
|
|
4491
4363
|
if (mailer.length > 0) mailerHandlers.push({ file, exports: mailer });
|
|
4492
4364
|
if (api.length > 0) apiHandlers.push({ file, exports: api });
|
|
4493
4365
|
}
|
|
4494
|
-
return {
|
|
4366
|
+
return { tableHandlers, appHandlers, staticSiteHandlers, fifoQueueHandlers, bucketHandlers, mailerHandlers, apiHandlers };
|
|
4495
4367
|
};
|
|
4496
4368
|
var flattenHandlers = (discovered) => {
|
|
4497
4369
|
const entries = (type, handlers) => handlers.flatMap((h) => h.exports.map((e) => ({ exportName: e.exportName, file: h.file, type })));
|
|
4498
4370
|
return [
|
|
4499
|
-
...entries("http", discovered.httpHandlers),
|
|
4500
4371
|
...entries("table", discovered.tableHandlers),
|
|
4501
4372
|
...entries("app", discovered.appHandlers),
|
|
4502
4373
|
...entries("site", discovered.staticSiteHandlers),
|
|
@@ -4528,7 +4399,6 @@ var collectRequiredParams = (handlers, project, stage) => {
|
|
|
4528
4399
|
}
|
|
4529
4400
|
}
|
|
4530
4401
|
};
|
|
4531
|
-
collect(handlers.httpHandlers);
|
|
4532
4402
|
collect(handlers.tableHandlers);
|
|
4533
4403
|
collect(handlers.fifoQueueHandlers);
|
|
4534
4404
|
collect(handlers.bucketHandlers);
|
|
@@ -4560,7 +4430,7 @@ var checkMissingParams = (params) => Effect25.gen(function* () {
|
|
|
4560
4430
|
return { existing, missing };
|
|
4561
4431
|
});
|
|
4562
4432
|
|
|
4563
|
-
// src/deploy/deploy-
|
|
4433
|
+
// src/deploy/deploy-table.ts
|
|
4564
4434
|
import { Effect as Effect27 } from "effect";
|
|
4565
4435
|
|
|
4566
4436
|
// src/deploy/shared.ts
|
|
@@ -4657,151 +4527,9 @@ var deployCoreLambda = ({
|
|
|
4657
4527
|
return { functionArn, status, tagCtx };
|
|
4658
4528
|
});
|
|
4659
4529
|
|
|
4660
|
-
// src/deploy/deploy-http.ts
|
|
4661
|
-
var deployLambda = ({ input, fn: fn13, layerArn, external, depsEnv, depsPermissions, staticGlobs }) => Effect27.gen(function* () {
|
|
4662
|
-
const { exportName, config } = fn13;
|
|
4663
|
-
const handlerName = exportName;
|
|
4664
|
-
const { functionArn, status } = yield* deployCoreLambda({
|
|
4665
|
-
input,
|
|
4666
|
-
exportName,
|
|
4667
|
-
handlerName,
|
|
4668
|
-
bundleType: "http",
|
|
4669
|
-
...config.permissions ? { permissions: config.permissions } : {},
|
|
4670
|
-
...config.memory ? { memory: config.memory } : {},
|
|
4671
|
-
...config.timeout ? { timeout: config.timeout } : {},
|
|
4672
|
-
...layerArn ? { layerArn } : {},
|
|
4673
|
-
...external ? { external } : {},
|
|
4674
|
-
...depsEnv ? { depsEnv } : {},
|
|
4675
|
-
...depsPermissions ? { depsPermissions } : {},
|
|
4676
|
-
...staticGlobs && staticGlobs.length > 0 ? { staticGlobs } : {}
|
|
4677
|
-
});
|
|
4678
|
-
return { exportName, functionArn, status, config, handlerName };
|
|
4679
|
-
});
|
|
4680
|
-
var deploy = (input) => Effect27.gen(function* () {
|
|
4681
|
-
const source = yield* readSource(input);
|
|
4682
|
-
const configs = extractConfigs(source);
|
|
4683
|
-
if (configs.length === 0) {
|
|
4684
|
-
return yield* Effect27.fail(new Error("Could not extract defineHttp config from source"));
|
|
4685
|
-
}
|
|
4686
|
-
const targetExport = input.exportName ?? "default";
|
|
4687
|
-
const fn13 = configs.find((c2) => c2.exportName === targetExport) ?? configs[0];
|
|
4688
|
-
const config = fn13.config;
|
|
4689
|
-
const handlerName = fn13.exportName;
|
|
4690
|
-
const tagCtx = {
|
|
4691
|
-
project: input.project,
|
|
4692
|
-
stage: resolveStage(input.stage),
|
|
4693
|
-
handler: handlerName
|
|
4694
|
-
};
|
|
4695
|
-
yield* Effect27.logDebug(`Deploying ${handlerName} to ${input.region} (${tagCtx.project}/${tagCtx.stage})`);
|
|
4696
|
-
const { layerArn, external } = yield* ensureLayerAndExternal({
|
|
4697
|
-
project: input.project,
|
|
4698
|
-
stage: tagCtx.stage,
|
|
4699
|
-
region: input.region,
|
|
4700
|
-
packageDir: input.packageDir ?? input.projectDir
|
|
4701
|
-
});
|
|
4702
|
-
const { functionArn } = yield* deployLambda({
|
|
4703
|
-
input,
|
|
4704
|
-
fn: fn13,
|
|
4705
|
-
...layerArn ? { layerArn } : {},
|
|
4706
|
-
...external.length > 0 ? { external } : {}
|
|
4707
|
-
});
|
|
4708
|
-
yield* Effect27.logDebug("Setting up API Gateway...");
|
|
4709
|
-
const { apiId } = yield* ensureProjectApi({
|
|
4710
|
-
projectName: input.project,
|
|
4711
|
-
stage: tagCtx.stage,
|
|
4712
|
-
region: input.region,
|
|
4713
|
-
tags: makeTags(tagCtx, "api-gateway")
|
|
4714
|
-
});
|
|
4715
|
-
const { apiUrl } = yield* addRouteToApi({
|
|
4716
|
-
apiId,
|
|
4717
|
-
region: input.region,
|
|
4718
|
-
functionArn,
|
|
4719
|
-
method: config.method,
|
|
4720
|
-
path: config.path
|
|
4721
|
-
});
|
|
4722
|
-
yield* Effect27.logDebug(`Deployment complete! URL: ${apiUrl}`);
|
|
4723
|
-
return {
|
|
4724
|
-
exportName: fn13.exportName,
|
|
4725
|
-
url: apiUrl,
|
|
4726
|
-
functionArn
|
|
4727
|
-
};
|
|
4728
|
-
}).pipe(
|
|
4729
|
-
Effect27.provide(
|
|
4730
|
-
clients_exports.makeClients({
|
|
4731
|
-
lambda: { region: input.region },
|
|
4732
|
-
iam: { region: input.region },
|
|
4733
|
-
apigatewayv2: { region: input.region }
|
|
4734
|
-
})
|
|
4735
|
-
)
|
|
4736
|
-
);
|
|
4737
|
-
var deployAll = (input) => Effect27.gen(function* () {
|
|
4738
|
-
const source = yield* readSource(input);
|
|
4739
|
-
const functions = extractConfigs(source);
|
|
4740
|
-
if (functions.length === 0) {
|
|
4741
|
-
return yield* Effect27.fail(new Error("No defineHttp exports found in source"));
|
|
4742
|
-
}
|
|
4743
|
-
yield* Effect27.logDebug(`Found ${functions.length} HTTP handler(s) to deploy`);
|
|
4744
|
-
const tagCtx = {
|
|
4745
|
-
project: input.project,
|
|
4746
|
-
stage: resolveStage(input.stage),
|
|
4747
|
-
handler: "api"
|
|
4748
|
-
};
|
|
4749
|
-
const { layerArn, external } = yield* ensureLayerAndExternal({
|
|
4750
|
-
project: input.project,
|
|
4751
|
-
stage: tagCtx.stage,
|
|
4752
|
-
region: input.region,
|
|
4753
|
-
packageDir: input.packageDir ?? input.projectDir
|
|
4754
|
-
});
|
|
4755
|
-
yield* Effect27.logDebug("Setting up API Gateway...");
|
|
4756
|
-
const { apiId } = yield* ensureProjectApi({
|
|
4757
|
-
projectName: input.project,
|
|
4758
|
-
stage: tagCtx.stage,
|
|
4759
|
-
region: input.region,
|
|
4760
|
-
tags: makeTags(tagCtx, "api-gateway")
|
|
4761
|
-
});
|
|
4762
|
-
const apiUrl = `https://${apiId}.execute-api.${input.region}.amazonaws.com`;
|
|
4763
|
-
const results = [];
|
|
4764
|
-
const activeRouteKeys = /* @__PURE__ */ new Set();
|
|
4765
|
-
for (const fn13 of functions) {
|
|
4766
|
-
const { exportName, functionArn, config, handlerName: fnName } = yield* deployLambda({
|
|
4767
|
-
input,
|
|
4768
|
-
fn: fn13,
|
|
4769
|
-
...layerArn ? { layerArn } : {},
|
|
4770
|
-
...external.length > 0 ? { external } : {}
|
|
4771
|
-
});
|
|
4772
|
-
const routeKey = `${config.method} ${config.path}`;
|
|
4773
|
-
activeRouteKeys.add(routeKey);
|
|
4774
|
-
const { apiUrl: handlerUrl } = yield* addRouteToApi({
|
|
4775
|
-
apiId,
|
|
4776
|
-
region: input.region,
|
|
4777
|
-
functionArn,
|
|
4778
|
-
method: config.method,
|
|
4779
|
-
path: config.path
|
|
4780
|
-
});
|
|
4781
|
-
results.push({ exportName, url: handlerUrl, functionArn });
|
|
4782
|
-
yield* Effect27.logDebug(` ${config.method} ${config.path} \u2192 ${fn13.exportName}`);
|
|
4783
|
-
}
|
|
4784
|
-
yield* removeStaleRoutes(apiId, activeRouteKeys);
|
|
4785
|
-
yield* Effect27.logDebug(`Deployment complete! API: ${apiUrl}`);
|
|
4786
|
-
return {
|
|
4787
|
-
apiId,
|
|
4788
|
-
apiUrl,
|
|
4789
|
-
handlers: results
|
|
4790
|
-
};
|
|
4791
|
-
}).pipe(
|
|
4792
|
-
Effect27.provide(
|
|
4793
|
-
clients_exports.makeClients({
|
|
4794
|
-
lambda: { region: input.region },
|
|
4795
|
-
iam: { region: input.region },
|
|
4796
|
-
apigatewayv2: { region: input.region }
|
|
4797
|
-
})
|
|
4798
|
-
)
|
|
4799
|
-
);
|
|
4800
|
-
|
|
4801
4530
|
// src/deploy/deploy-table.ts
|
|
4802
|
-
import { Effect as Effect28 } from "effect";
|
|
4803
4531
|
var TABLE_DEFAULT_PERMISSIONS = ["dynamodb:*", "logs:*"];
|
|
4804
|
-
var deployTableFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsPermissions, staticGlobs }) =>
|
|
4532
|
+
var deployTableFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsPermissions, staticGlobs }) => Effect27.gen(function* () {
|
|
4805
4533
|
const { exportName, config } = fn13;
|
|
4806
4534
|
const handlerName = exportName;
|
|
4807
4535
|
const tagCtx = {
|
|
@@ -4809,7 +4537,7 @@ var deployTableFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsP
|
|
|
4809
4537
|
stage: resolveStage(input.stage),
|
|
4810
4538
|
handler: handlerName
|
|
4811
4539
|
};
|
|
4812
|
-
yield*
|
|
4540
|
+
yield* Effect27.logDebug("Creating DynamoDB table...");
|
|
4813
4541
|
const tableName = `${input.project}-${tagCtx.stage}-${handlerName}`;
|
|
4814
4542
|
const { tableArn, streamArn } = yield* ensureTable({
|
|
4815
4543
|
name: tableName,
|
|
@@ -4833,7 +4561,7 @@ var deployTableFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsP
|
|
|
4833
4561
|
...depsPermissions ? { depsPermissions } : {},
|
|
4834
4562
|
...staticGlobs && staticGlobs.length > 0 ? { staticGlobs } : {}
|
|
4835
4563
|
});
|
|
4836
|
-
yield*
|
|
4564
|
+
yield* Effect27.logDebug("Setting up event source mapping...");
|
|
4837
4565
|
yield* ensureEventSourceMapping({
|
|
4838
4566
|
functionArn,
|
|
4839
4567
|
streamArn,
|
|
@@ -4841,7 +4569,7 @@ var deployTableFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsP
|
|
|
4841
4569
|
batchWindow: config.batchWindow ?? 2,
|
|
4842
4570
|
startingPosition: config.startingPosition ?? "LATEST"
|
|
4843
4571
|
});
|
|
4844
|
-
yield*
|
|
4572
|
+
yield* Effect27.logDebug(`Table deployment complete! Table: ${tableArn}`);
|
|
4845
4573
|
return {
|
|
4846
4574
|
exportName,
|
|
4847
4575
|
functionArn,
|
|
@@ -4850,11 +4578,11 @@ var deployTableFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsP
|
|
|
4850
4578
|
streamArn
|
|
4851
4579
|
};
|
|
4852
4580
|
});
|
|
4853
|
-
var deployTable = (input) =>
|
|
4581
|
+
var deployTable = (input) => Effect27.gen(function* () {
|
|
4854
4582
|
const source = yield* readSource(input);
|
|
4855
4583
|
const configs = extractTableConfigs(source);
|
|
4856
4584
|
if (configs.length === 0) {
|
|
4857
|
-
return yield*
|
|
4585
|
+
return yield* Effect27.fail(new Error("No defineTable exports found in source"));
|
|
4858
4586
|
}
|
|
4859
4587
|
const targetExport = input.exportName ?? "default";
|
|
4860
4588
|
const fn13 = configs.find((c2) => c2.exportName === targetExport) ?? configs[0];
|
|
@@ -4872,7 +4600,7 @@ var deployTable = (input) => Effect28.gen(function* () {
|
|
|
4872
4600
|
});
|
|
4873
4601
|
return result;
|
|
4874
4602
|
}).pipe(
|
|
4875
|
-
|
|
4603
|
+
Effect27.provide(
|
|
4876
4604
|
clients_exports.makeClients({
|
|
4877
4605
|
lambda: { region: input.region },
|
|
4878
4606
|
iam: { region: input.region },
|
|
@@ -4880,20 +4608,20 @@ var deployTable = (input) => Effect28.gen(function* () {
|
|
|
4880
4608
|
})
|
|
4881
4609
|
)
|
|
4882
4610
|
);
|
|
4883
|
-
var deployAllTables = (input) =>
|
|
4611
|
+
var deployAllTables = (input) => Effect27.gen(function* () {
|
|
4884
4612
|
const source = yield* readSource(input);
|
|
4885
4613
|
const functions = extractTableConfigs(source);
|
|
4886
4614
|
if (functions.length === 0) {
|
|
4887
|
-
return yield*
|
|
4615
|
+
return yield* Effect27.fail(new Error("No defineTable exports found in source"));
|
|
4888
4616
|
}
|
|
4889
|
-
yield*
|
|
4617
|
+
yield* Effect27.logDebug(`Found ${functions.length} table handler(s) to deploy`);
|
|
4890
4618
|
const { layerArn, external } = yield* ensureLayerAndExternal({
|
|
4891
4619
|
project: input.project,
|
|
4892
4620
|
stage: resolveStage(input.stage),
|
|
4893
4621
|
region: input.region,
|
|
4894
4622
|
packageDir: input.packageDir ?? input.projectDir
|
|
4895
4623
|
});
|
|
4896
|
-
const results = yield*
|
|
4624
|
+
const results = yield* Effect27.forEach(
|
|
4897
4625
|
functions,
|
|
4898
4626
|
(fn13) => deployTableFunction({
|
|
4899
4627
|
input,
|
|
@@ -4905,7 +4633,7 @@ var deployAllTables = (input) => Effect28.gen(function* () {
|
|
|
4905
4633
|
);
|
|
4906
4634
|
return results;
|
|
4907
4635
|
}).pipe(
|
|
4908
|
-
|
|
4636
|
+
Effect27.provide(
|
|
4909
4637
|
clients_exports.makeClients({
|
|
4910
4638
|
lambda: { region: input.region },
|
|
4911
4639
|
iam: { region: input.region },
|
|
@@ -4914,6 +4642,72 @@ var deployAllTables = (input) => Effect28.gen(function* () {
|
|
|
4914
4642
|
)
|
|
4915
4643
|
);
|
|
4916
4644
|
|
|
4645
|
+
// src/deploy/deploy-api.ts
|
|
4646
|
+
import { Effect as Effect28 } from "effect";
|
|
4647
|
+
var deployApiFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsPermissions, staticGlobs }) => Effect28.gen(function* () {
|
|
4648
|
+
const { exportName, config } = fn13;
|
|
4649
|
+
const handlerName = exportName;
|
|
4650
|
+
const { functionArn, status } = yield* deployCoreLambda({
|
|
4651
|
+
input,
|
|
4652
|
+
exportName,
|
|
4653
|
+
handlerName,
|
|
4654
|
+
bundleType: "api",
|
|
4655
|
+
...config.permissions ? { permissions: config.permissions } : {},
|
|
4656
|
+
...config.memory ? { memory: config.memory } : {},
|
|
4657
|
+
...config.timeout ? { timeout: config.timeout } : {},
|
|
4658
|
+
...layerArn ? { layerArn } : {},
|
|
4659
|
+
...external ? { external } : {},
|
|
4660
|
+
...depsEnv ? { depsEnv } : {},
|
|
4661
|
+
...depsPermissions ? { depsPermissions } : {},
|
|
4662
|
+
...staticGlobs && staticGlobs.length > 0 ? { staticGlobs } : {}
|
|
4663
|
+
});
|
|
4664
|
+
return { exportName, functionArn, status, config, handlerName };
|
|
4665
|
+
});
|
|
4666
|
+
var deploy = (input) => Effect28.gen(function* () {
|
|
4667
|
+
const source = yield* readSource(input);
|
|
4668
|
+
const configs = extractApiConfigs(source);
|
|
4669
|
+
if (configs.length === 0) {
|
|
4670
|
+
return yield* Effect28.fail(new Error("Could not extract defineApi config from source"));
|
|
4671
|
+
}
|
|
4672
|
+
const targetExport = input.exportName ?? "default";
|
|
4673
|
+
const fn13 = configs.find((c2) => c2.exportName === targetExport) ?? configs[0];
|
|
4674
|
+
const handlerName = fn13.exportName;
|
|
4675
|
+
const tagCtx = {
|
|
4676
|
+
project: input.project,
|
|
4677
|
+
stage: resolveStage(input.stage),
|
|
4678
|
+
handler: handlerName
|
|
4679
|
+
};
|
|
4680
|
+
yield* Effect28.logDebug(`Deploying API handler ${handlerName} to ${input.region}`);
|
|
4681
|
+
const { layerArn, external } = yield* ensureLayerAndExternal({
|
|
4682
|
+
project: input.project,
|
|
4683
|
+
stage: tagCtx.stage,
|
|
4684
|
+
region: input.region,
|
|
4685
|
+
packageDir: input.packageDir ?? input.projectDir
|
|
4686
|
+
});
|
|
4687
|
+
const { functionArn } = yield* deployApiFunction({
|
|
4688
|
+
input,
|
|
4689
|
+
fn: fn13,
|
|
4690
|
+
...layerArn ? { layerArn } : {},
|
|
4691
|
+
...external.length > 0 ? { external } : {}
|
|
4692
|
+
});
|
|
4693
|
+
const lambdaName = `${input.project}-${tagCtx.stage}-${handlerName}`;
|
|
4694
|
+
const { functionUrl } = yield* ensureFunctionUrl(lambdaName);
|
|
4695
|
+
yield* addFunctionUrlPublicAccess(lambdaName);
|
|
4696
|
+
yield* Effect28.logDebug(`Deployment complete! URL: ${functionUrl}`);
|
|
4697
|
+
return {
|
|
4698
|
+
exportName: fn13.exportName,
|
|
4699
|
+
url: functionUrl,
|
|
4700
|
+
functionArn
|
|
4701
|
+
};
|
|
4702
|
+
}).pipe(
|
|
4703
|
+
Effect28.provide(
|
|
4704
|
+
clients_exports.makeClients({
|
|
4705
|
+
lambda: { region: input.region },
|
|
4706
|
+
iam: { region: input.region }
|
|
4707
|
+
})
|
|
4708
|
+
)
|
|
4709
|
+
);
|
|
4710
|
+
|
|
4917
4711
|
// src/deploy/deploy-app.ts
|
|
4918
4712
|
import { Effect as Effect29 } from "effect";
|
|
4919
4713
|
import { execSync } from "child_process";
|
|
@@ -4928,7 +4722,7 @@ var deployApp = (input) => Effect29.gen(function* () {
|
|
|
4928
4722
|
if (routePatterns.length > 0 && !input.apiOriginDomain) {
|
|
4929
4723
|
return yield* Effect29.fail(
|
|
4930
4724
|
new Error(
|
|
4931
|
-
`App "${exportName}" has routes but no API
|
|
4725
|
+
`App "${exportName}" has routes but no API handler was deployed. Ensure defineApi() handlers are included in the discovery patterns.`
|
|
4932
4726
|
)
|
|
4933
4727
|
);
|
|
4934
4728
|
}
|
|
@@ -5031,14 +4825,217 @@ var deployApp = (input) => Effect29.gen(function* () {
|
|
|
5031
4825
|
});
|
|
5032
4826
|
|
|
5033
4827
|
// src/deploy/deploy-static-site.ts
|
|
5034
|
-
import { Effect as
|
|
4828
|
+
import { Effect as Effect31 } from "effect";
|
|
5035
4829
|
import { Architecture as Architecture3 } from "@aws-sdk/client-lambda";
|
|
5036
4830
|
import { execSync as execSync2 } from "child_process";
|
|
4831
|
+
import * as fs5 from "fs";
|
|
4832
|
+
import * as path7 from "path";
|
|
4833
|
+
|
|
4834
|
+
// src/deploy/seo.ts
|
|
4835
|
+
import { Effect as Effect30 } from "effect";
|
|
4836
|
+
import * as fs4 from "fs";
|
|
5037
4837
|
import * as path6 from "path";
|
|
5038
|
-
|
|
4838
|
+
import * as crypto4 from "crypto";
|
|
4839
|
+
import * as os from "os";
|
|
4840
|
+
var collectHtmlPaths = (sourceDir) => {
|
|
4841
|
+
const paths = [];
|
|
4842
|
+
const walk = (dir, prefix) => {
|
|
4843
|
+
for (const entry of fs4.readdirSync(dir, { withFileTypes: true })) {
|
|
4844
|
+
const key = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
4845
|
+
if (entry.isDirectory()) {
|
|
4846
|
+
walk(path6.join(dir, entry.name), key);
|
|
4847
|
+
} else if (entry.name.endsWith(".html") || entry.name.endsWith(".htm")) {
|
|
4848
|
+
if (entry.name === "404.html" || entry.name === "500.html") continue;
|
|
4849
|
+
let urlPath = "/" + key;
|
|
4850
|
+
if (urlPath.endsWith("/index.html")) {
|
|
4851
|
+
urlPath = urlPath.slice(0, -"index.html".length);
|
|
4852
|
+
} else if (urlPath.endsWith("/index.htm")) {
|
|
4853
|
+
urlPath = urlPath.slice(0, -"index.htm".length);
|
|
4854
|
+
}
|
|
4855
|
+
paths.push(urlPath);
|
|
4856
|
+
}
|
|
4857
|
+
}
|
|
4858
|
+
};
|
|
4859
|
+
walk(sourceDir, "");
|
|
4860
|
+
return paths.sort();
|
|
4861
|
+
};
|
|
4862
|
+
var generateSitemap = (siteUrl, sourceDir) => {
|
|
4863
|
+
const baseUrl = siteUrl.replace(/\/$/, "");
|
|
4864
|
+
const paths = collectHtmlPaths(sourceDir);
|
|
4865
|
+
const urls = paths.map((urlPath) => ` <url>
|
|
4866
|
+
<loc>${baseUrl}${urlPath}</loc>
|
|
4867
|
+
</url>`).join("\n");
|
|
4868
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
4869
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
4870
|
+
${urls}
|
|
4871
|
+
</urlset>
|
|
4872
|
+
`;
|
|
4873
|
+
};
|
|
4874
|
+
var generateRobots = (siteUrl, sitemapName = "sitemap.xml") => {
|
|
4875
|
+
const baseUrl = siteUrl.replace(/\/$/, "");
|
|
4876
|
+
return `User-agent: *
|
|
4877
|
+
Allow: /
|
|
4878
|
+
|
|
4879
|
+
Sitemap: ${baseUrl}/${sitemapName}
|
|
4880
|
+
`;
|
|
4881
|
+
};
|
|
4882
|
+
var createJwt = (serviceAccount) => {
|
|
4883
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
4884
|
+
const header = Buffer.from(JSON.stringify({ alg: "RS256", typ: "JWT" })).toString("base64url");
|
|
4885
|
+
const payload = Buffer.from(
|
|
4886
|
+
JSON.stringify({
|
|
4887
|
+
iss: serviceAccount.client_email,
|
|
4888
|
+
scope: "https://www.googleapis.com/auth/indexing",
|
|
4889
|
+
aud: "https://oauth2.googleapis.com/token",
|
|
4890
|
+
iat: now,
|
|
4891
|
+
exp: now + 3600
|
|
4892
|
+
})
|
|
4893
|
+
).toString("base64url");
|
|
4894
|
+
const signInput = `${header}.${payload}`;
|
|
4895
|
+
const signature = crypto4.createSign("RSA-SHA256").update(signInput).sign(serviceAccount.private_key, "base64url");
|
|
4896
|
+
return `${signInput}.${signature}`;
|
|
4897
|
+
};
|
|
4898
|
+
var getAccessToken = async (serviceAccount) => {
|
|
4899
|
+
const jwt = createJwt(serviceAccount);
|
|
4900
|
+
const response = await fetch("https://oauth2.googleapis.com/token", {
|
|
4901
|
+
method: "POST",
|
|
4902
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
4903
|
+
body: `grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=${jwt}`
|
|
4904
|
+
});
|
|
4905
|
+
if (!response.ok) {
|
|
4906
|
+
const text = await response.text();
|
|
4907
|
+
throw new Error(`Failed to get Google access token: ${response.status} ${text}`);
|
|
4908
|
+
}
|
|
4909
|
+
const data = await response.json();
|
|
4910
|
+
return data.access_token;
|
|
4911
|
+
};
|
|
4912
|
+
var publishUrl = async (accessToken, url) => {
|
|
4913
|
+
const response = await fetch("https://indexing.googleapis.com/v3/urlNotifications:publish", {
|
|
4914
|
+
method: "POST",
|
|
4915
|
+
headers: {
|
|
4916
|
+
"Content-Type": "application/json",
|
|
4917
|
+
Authorization: `Bearer ${accessToken}`
|
|
4918
|
+
},
|
|
4919
|
+
body: JSON.stringify({ url, type: "URL_UPDATED" })
|
|
4920
|
+
});
|
|
4921
|
+
if (!response.ok) {
|
|
4922
|
+
const text = await response.text();
|
|
4923
|
+
return { url, ok: false, error: `${response.status} ${text}` };
|
|
4924
|
+
}
|
|
4925
|
+
return { url, ok: true };
|
|
4926
|
+
};
|
|
4927
|
+
var collectHtmlKeys = (sourceDir) => {
|
|
4928
|
+
const keys = [];
|
|
4929
|
+
const walk = (dir, prefix) => {
|
|
4930
|
+
for (const entry of fs4.readdirSync(dir, { withFileTypes: true })) {
|
|
4931
|
+
const fullPath = path6.join(dir, entry.name);
|
|
4932
|
+
const key = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
4933
|
+
if (entry.isDirectory()) {
|
|
4934
|
+
walk(fullPath, key);
|
|
4935
|
+
} else if (entry.name.endsWith(".html") || entry.name.endsWith(".htm")) {
|
|
4936
|
+
if (entry.name === "404.html" || entry.name === "500.html") continue;
|
|
4937
|
+
keys.push(key);
|
|
4938
|
+
}
|
|
4939
|
+
}
|
|
4940
|
+
};
|
|
4941
|
+
walk(sourceDir, "");
|
|
4942
|
+
return keys;
|
|
4943
|
+
};
|
|
4944
|
+
var keysToUrls = (siteUrl, keys) => {
|
|
4945
|
+
const baseUrl = siteUrl.replace(/\/$/, "");
|
|
4946
|
+
return keys.map((key) => {
|
|
4947
|
+
let urlPath = "/" + key;
|
|
4948
|
+
if (urlPath.endsWith("/index.html")) {
|
|
4949
|
+
urlPath = urlPath.slice(0, -"index.html".length);
|
|
4950
|
+
} else if (urlPath.endsWith("/index.htm")) {
|
|
4951
|
+
urlPath = urlPath.slice(0, -"index.htm".length);
|
|
4952
|
+
}
|
|
4953
|
+
return `${baseUrl}${urlPath}`;
|
|
4954
|
+
});
|
|
4955
|
+
};
|
|
4956
|
+
var INDEXED_URLS_KEY = "_effortless/indexed-urls.json";
|
|
4957
|
+
var loadIndexedUrls = (bucketName) => Effect30.gen(function* () {
|
|
4958
|
+
const result = yield* s3_exports.make("get_object", {
|
|
4959
|
+
Bucket: bucketName,
|
|
4960
|
+
Key: INDEXED_URLS_KEY
|
|
4961
|
+
}).pipe(Effect30.option);
|
|
4962
|
+
if (result._tag === "None") return /* @__PURE__ */ new Set();
|
|
4963
|
+
const body = yield* Effect30.tryPromise({
|
|
4964
|
+
try: () => result.value.Body?.transformToString("utf-8") ?? Promise.resolve("[]"),
|
|
4965
|
+
catch: () => new Error("Failed to read indexed URLs from S3")
|
|
4966
|
+
});
|
|
4967
|
+
const urls = JSON.parse(body);
|
|
4968
|
+
return new Set(urls);
|
|
4969
|
+
});
|
|
4970
|
+
var saveIndexedUrls = (bucketName, urls) => s3_exports.make("put_object", {
|
|
4971
|
+
Bucket: bucketName,
|
|
4972
|
+
Key: INDEXED_URLS_KEY,
|
|
4973
|
+
Body: JSON.stringify([...urls].sort(), null, 2),
|
|
4974
|
+
ContentType: "application/json; charset=utf-8"
|
|
4975
|
+
});
|
|
4976
|
+
var submitToGoogleIndexing = (input) => Effect30.gen(function* () {
|
|
4977
|
+
const { serviceAccountPath, projectDir, bucketName, allPageUrls } = input;
|
|
4978
|
+
const indexedUrls = yield* loadIndexedUrls(bucketName);
|
|
4979
|
+
const urlsToSubmit = allPageUrls.filter((url) => !indexedUrls.has(url));
|
|
4980
|
+
const currentUrlSet = new Set(allPageUrls);
|
|
4981
|
+
for (const url of indexedUrls) {
|
|
4982
|
+
if (!currentUrlSet.has(url)) {
|
|
4983
|
+
indexedUrls.delete(url);
|
|
4984
|
+
}
|
|
4985
|
+
}
|
|
4986
|
+
if (urlsToSubmit.length === 0) {
|
|
4987
|
+
yield* Effect30.logDebug("All pages already indexed, skipping Google Indexing API");
|
|
4988
|
+
return { submitted: 0, failed: 0, skipped: allPageUrls.length };
|
|
4989
|
+
}
|
|
4990
|
+
const expanded = serviceAccountPath.startsWith("~/") ? path6.join(os.homedir(), serviceAccountPath.slice(2)) : serviceAccountPath;
|
|
4991
|
+
const keyPath = path6.resolve(projectDir, expanded);
|
|
4992
|
+
if (!fs4.existsSync(keyPath)) {
|
|
4993
|
+
return yield* Effect30.fail(
|
|
4994
|
+
new Error(`Google service account key not found: ${keyPath}`)
|
|
4995
|
+
);
|
|
4996
|
+
}
|
|
4997
|
+
const serviceAccount = JSON.parse(fs4.readFileSync(keyPath, "utf-8"));
|
|
4998
|
+
if (!serviceAccount.client_email || !serviceAccount.private_key) {
|
|
4999
|
+
return yield* Effect30.fail(
|
|
5000
|
+
new Error(`Invalid service account key: missing client_email or private_key`)
|
|
5001
|
+
);
|
|
5002
|
+
}
|
|
5003
|
+
yield* Effect30.logDebug(`Authenticating with Google as ${serviceAccount.client_email}`);
|
|
5004
|
+
const accessToken = yield* Effect30.tryPromise({
|
|
5005
|
+
try: () => getAccessToken(serviceAccount),
|
|
5006
|
+
catch: (error) => new Error(`Google auth failed: ${error}`)
|
|
5007
|
+
});
|
|
5008
|
+
const maxUrls = Math.min(urlsToSubmit.length, 200);
|
|
5009
|
+
if (urlsToSubmit.length > 200) {
|
|
5010
|
+
yield* Effect30.logDebug(
|
|
5011
|
+
`Google Indexing API daily quota is 200. Submitting first 200 of ${urlsToSubmit.length} URLs.`
|
|
5012
|
+
);
|
|
5013
|
+
}
|
|
5014
|
+
let submitted = 0;
|
|
5015
|
+
let failed = 0;
|
|
5016
|
+
for (const url of urlsToSubmit.slice(0, maxUrls)) {
|
|
5017
|
+
const result = yield* Effect30.tryPromise({
|
|
5018
|
+
try: () => publishUrl(accessToken, url),
|
|
5019
|
+
catch: (error) => new Error(`Failed to submit ${url}: ${error}`)
|
|
5020
|
+
});
|
|
5021
|
+
if (result.ok) {
|
|
5022
|
+
submitted++;
|
|
5023
|
+
indexedUrls.add(url);
|
|
5024
|
+
} else {
|
|
5025
|
+
failed++;
|
|
5026
|
+
yield* Effect30.logDebug(`Failed to index ${result.url}: ${result.error}`);
|
|
5027
|
+
}
|
|
5028
|
+
}
|
|
5029
|
+
yield* saveIndexedUrls(bucketName, indexedUrls);
|
|
5030
|
+
yield* Effect30.logDebug(`Google Indexing: ${submitted} submitted, ${failed} failed, ${allPageUrls.length - urlsToSubmit.length} already indexed`);
|
|
5031
|
+
return { submitted, failed, skipped: allPageUrls.length - urlsToSubmit.length };
|
|
5032
|
+
});
|
|
5033
|
+
|
|
5034
|
+
// src/deploy/deploy-static-site.ts
|
|
5035
|
+
var deployMiddlewareLambda = (input) => Effect31.gen(function* () {
|
|
5039
5036
|
const { projectDir, project, stage, handlerName, file, exportName, tagCtx } = input;
|
|
5040
5037
|
const middlewareName = `${handlerName}-middleware`;
|
|
5041
|
-
yield*
|
|
5038
|
+
yield* Effect31.logDebug(`Deploying middleware Lambda@Edge: ${middlewareName}`);
|
|
5042
5039
|
const roleArn = yield* ensureEdgeRole(
|
|
5043
5040
|
project,
|
|
5044
5041
|
stage,
|
|
@@ -5064,14 +5061,14 @@ var deployMiddlewareLambda = (input) => Effect30.gen(function* () {
|
|
|
5064
5061
|
architecture: Architecture3.x86_64,
|
|
5065
5062
|
tags: makeTags(tagCtx, "lambda")
|
|
5066
5063
|
}).pipe(
|
|
5067
|
-
|
|
5064
|
+
Effect31.provide(clients_exports.makeClients({ lambda: { region: "us-east-1" } }))
|
|
5068
5065
|
);
|
|
5069
5066
|
const { versionArn } = yield* publishVersion(
|
|
5070
5067
|
`${project}-${stage}-${middlewareName}`
|
|
5071
5068
|
).pipe(
|
|
5072
|
-
|
|
5069
|
+
Effect31.provide(clients_exports.makeClients({ lambda: { region: "us-east-1" } }))
|
|
5073
5070
|
);
|
|
5074
|
-
yield*
|
|
5071
|
+
yield* Effect31.logDebug(`Middleware deployed: ${versionArn}`);
|
|
5075
5072
|
return { versionArn };
|
|
5076
5073
|
});
|
|
5077
5074
|
var ERROR_PAGE_KEY = "_effortless/404.html";
|
|
@@ -5109,7 +5106,7 @@ var generateErrorPageHtml = () => `<!DOCTYPE html>
|
|
|
5109
5106
|
</div>
|
|
5110
5107
|
</body>
|
|
5111
5108
|
</html>`;
|
|
5112
|
-
var deployStaticSite = (input) =>
|
|
5109
|
+
var deployStaticSite = (input) => Effect31.gen(function* () {
|
|
5113
5110
|
const { projectDir, project, region, fn: fn13 } = input;
|
|
5114
5111
|
const { exportName, config } = fn13;
|
|
5115
5112
|
const stage = resolveStage(input.stage);
|
|
@@ -5118,16 +5115,16 @@ var deployStaticSite = (input) => Effect30.gen(function* () {
|
|
|
5118
5115
|
const tagCtx = { project, stage, handler: handlerName };
|
|
5119
5116
|
const routePatterns = fn13.routePatterns;
|
|
5120
5117
|
if (routePatterns.length > 0 && !input.apiOriginDomain) {
|
|
5121
|
-
return yield*
|
|
5118
|
+
return yield* Effect31.fail(
|
|
5122
5119
|
new Error(
|
|
5123
|
-
`Static site "${exportName}" has routes but no API
|
|
5120
|
+
`Static site "${exportName}" has routes but no API handler was deployed. Ensure defineApi() handlers are included in the discovery patterns.`
|
|
5124
5121
|
)
|
|
5125
5122
|
);
|
|
5126
5123
|
}
|
|
5127
5124
|
if (config.build) {
|
|
5128
|
-
yield*
|
|
5125
|
+
yield* Effect31.logDebug(`Building site: ${config.build}`);
|
|
5129
5126
|
const buildStart = Date.now();
|
|
5130
|
-
yield*
|
|
5127
|
+
yield* Effect31.try({
|
|
5131
5128
|
try: () => execSync2(config.build, {
|
|
5132
5129
|
cwd: projectDir,
|
|
5133
5130
|
stdio: input.verbose ? "inherit" : "pipe"
|
|
@@ -5140,7 +5137,7 @@ var deployStaticSite = (input) => Effect30.gen(function* () {
|
|
|
5140
5137
|
return new Error(`Site build failed: ${config.build}`);
|
|
5141
5138
|
}
|
|
5142
5139
|
});
|
|
5143
|
-
yield*
|
|
5140
|
+
yield* Effect31.logDebug(`Site built in ${((Date.now() - buildStart) / 1e3).toFixed(1)}s`);
|
|
5144
5141
|
}
|
|
5145
5142
|
const bucketName = `${project}-${stage}-${handlerName}-site`.toLowerCase();
|
|
5146
5143
|
yield* ensureBucket({
|
|
@@ -5164,10 +5161,10 @@ var deployStaticSite = (input) => Effect30.gen(function* () {
|
|
|
5164
5161
|
if (certCoversWww) {
|
|
5165
5162
|
aliases = [domain, wwwCandidate];
|
|
5166
5163
|
wwwDomain = wwwCandidate;
|
|
5167
|
-
yield*
|
|
5164
|
+
yield* Effect31.logDebug(`ACM certificate covers ${wwwCandidate}, enabling www \u2192 non-www redirect`);
|
|
5168
5165
|
} else {
|
|
5169
5166
|
aliases = [domain];
|
|
5170
|
-
yield*
|
|
5167
|
+
yield* Effect31.logWarning(
|
|
5171
5168
|
`ACM certificate does not cover ${wwwCandidate}. For SEO, add ${wwwCandidate} to your ACM certificate in us-east-1 to enable www \u2192 non-www redirect.`
|
|
5172
5169
|
);
|
|
5173
5170
|
}
|
|
@@ -5188,7 +5185,7 @@ var deployStaticSite = (input) => Effect30.gen(function* () {
|
|
|
5188
5185
|
exportName,
|
|
5189
5186
|
tagCtx
|
|
5190
5187
|
}).pipe(
|
|
5191
|
-
|
|
5188
|
+
Effect31.provide(clients_exports.makeClients({ iam: { region: "us-east-1" } }))
|
|
5192
5189
|
);
|
|
5193
5190
|
lambdaEdgeArn = result.versionArn;
|
|
5194
5191
|
} else {
|
|
@@ -5223,8 +5220,32 @@ var deployStaticSite = (input) => Effect30.gen(function* () {
|
|
|
5223
5220
|
...input.apiOriginDomain && routePatterns.length > 0 ? { apiOriginDomain: input.apiOriginDomain, routePatterns } : {}
|
|
5224
5221
|
});
|
|
5225
5222
|
yield* putBucketPolicyForOAC(bucketName, distributionArn);
|
|
5226
|
-
const sourceDir =
|
|
5223
|
+
const sourceDir = path7.resolve(projectDir, config.dir);
|
|
5227
5224
|
yield* syncFiles({ bucketName, sourceDir });
|
|
5225
|
+
const seo = config.seo;
|
|
5226
|
+
const siteUrl = domain ? `https://${domain}` : `https://${domainName}`;
|
|
5227
|
+
const seoGenerated = [];
|
|
5228
|
+
if (seo) {
|
|
5229
|
+
const sitemapName = seo.sitemap;
|
|
5230
|
+
if (!fs5.existsSync(path7.join(sourceDir, sitemapName))) {
|
|
5231
|
+
const sitemap = generateSitemap(siteUrl, sourceDir);
|
|
5232
|
+
yield* putObject({
|
|
5233
|
+
bucketName,
|
|
5234
|
+
key: sitemapName,
|
|
5235
|
+
body: sitemap,
|
|
5236
|
+
contentType: "application/xml; charset=utf-8"
|
|
5237
|
+
});
|
|
5238
|
+
seoGenerated.push(sitemapName);
|
|
5239
|
+
}
|
|
5240
|
+
const robots = generateRobots(siteUrl, sitemapName);
|
|
5241
|
+
yield* putObject({
|
|
5242
|
+
bucketName,
|
|
5243
|
+
key: "robots.txt",
|
|
5244
|
+
body: robots,
|
|
5245
|
+
contentType: "text/plain; charset=utf-8"
|
|
5246
|
+
});
|
|
5247
|
+
seoGenerated.push("robots.txt");
|
|
5248
|
+
}
|
|
5228
5249
|
if (!isSpa && !config.errorPage) {
|
|
5229
5250
|
yield* putObject({
|
|
5230
5251
|
bucketName,
|
|
@@ -5234,21 +5255,33 @@ var deployStaticSite = (input) => Effect30.gen(function* () {
|
|
|
5234
5255
|
});
|
|
5235
5256
|
}
|
|
5236
5257
|
yield* invalidateDistribution(distributionId);
|
|
5237
|
-
|
|
5238
|
-
|
|
5258
|
+
let indexingResult;
|
|
5259
|
+
if (seo?.googleIndexing) {
|
|
5260
|
+
const allHtmlKeys = collectHtmlKeys(sourceDir);
|
|
5261
|
+
const allPageUrls = keysToUrls(siteUrl, allHtmlKeys);
|
|
5262
|
+
indexingResult = yield* submitToGoogleIndexing({
|
|
5263
|
+
serviceAccountPath: seo.googleIndexing,
|
|
5264
|
+
projectDir,
|
|
5265
|
+
bucketName,
|
|
5266
|
+
allPageUrls
|
|
5267
|
+
});
|
|
5268
|
+
}
|
|
5269
|
+
yield* Effect31.logDebug(`Static site deployed: ${siteUrl}`);
|
|
5239
5270
|
return {
|
|
5240
5271
|
exportName,
|
|
5241
5272
|
handlerName,
|
|
5242
|
-
url,
|
|
5273
|
+
url: siteUrl,
|
|
5243
5274
|
distributionId,
|
|
5244
|
-
bucketName
|
|
5275
|
+
bucketName,
|
|
5276
|
+
seoGenerated: seoGenerated.length > 0 ? seoGenerated : void 0,
|
|
5277
|
+
indexingResult
|
|
5245
5278
|
};
|
|
5246
5279
|
});
|
|
5247
5280
|
|
|
5248
5281
|
// src/deploy/deploy-fifo-queue.ts
|
|
5249
|
-
import { Effect as
|
|
5282
|
+
import { Effect as Effect32 } from "effect";
|
|
5250
5283
|
var FIFO_QUEUE_DEFAULT_PERMISSIONS = ["sqs:*", "logs:*"];
|
|
5251
|
-
var deployFifoQueueFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsPermissions, staticGlobs }) =>
|
|
5284
|
+
var deployFifoQueueFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsPermissions, staticGlobs }) => Effect32.gen(function* () {
|
|
5252
5285
|
const { exportName, config } = fn13;
|
|
5253
5286
|
const handlerName = exportName;
|
|
5254
5287
|
const tagCtx = {
|
|
@@ -5256,7 +5289,7 @@ var deployFifoQueueFunction = ({ input, fn: fn13, layerArn, external, depsEnv, d
|
|
|
5256
5289
|
stage: resolveStage(input.stage),
|
|
5257
5290
|
handler: handlerName
|
|
5258
5291
|
};
|
|
5259
|
-
yield*
|
|
5292
|
+
yield* Effect32.logDebug("Creating SQS FIFO queue...");
|
|
5260
5293
|
const queueName = `${input.project}-${tagCtx.stage}-${handlerName}`;
|
|
5261
5294
|
const timeout = config.timeout ?? 30;
|
|
5262
5295
|
const { queueUrl, queueArn } = yield* ensureFifoQueue({
|
|
@@ -5286,14 +5319,14 @@ var deployFifoQueueFunction = ({ input, fn: fn13, layerArn, external, depsEnv, d
|
|
|
5286
5319
|
...depsPermissions ? { depsPermissions } : {},
|
|
5287
5320
|
...staticGlobs && staticGlobs.length > 0 ? { staticGlobs } : {}
|
|
5288
5321
|
});
|
|
5289
|
-
yield*
|
|
5322
|
+
yield* Effect32.logDebug("Setting up SQS event source mapping...");
|
|
5290
5323
|
yield* ensureSqsEventSourceMapping({
|
|
5291
5324
|
functionArn,
|
|
5292
5325
|
queueArn,
|
|
5293
5326
|
batchSize: config.batchSize ?? 10,
|
|
5294
5327
|
batchWindow: config.batchWindow
|
|
5295
5328
|
});
|
|
5296
|
-
yield*
|
|
5329
|
+
yield* Effect32.logDebug(`FIFO queue deployment complete! Queue: ${queueUrl}`);
|
|
5297
5330
|
return {
|
|
5298
5331
|
exportName,
|
|
5299
5332
|
functionArn,
|
|
@@ -5304,9 +5337,9 @@ var deployFifoQueueFunction = ({ input, fn: fn13, layerArn, external, depsEnv, d
|
|
|
5304
5337
|
});
|
|
5305
5338
|
|
|
5306
5339
|
// src/deploy/deploy-bucket.ts
|
|
5307
|
-
import { Effect as
|
|
5340
|
+
import { Effect as Effect33 } from "effect";
|
|
5308
5341
|
var BUCKET_DEFAULT_PERMISSIONS = ["s3:*", "logs:*"];
|
|
5309
|
-
var deployBucketFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsPermissions, staticGlobs }) =>
|
|
5342
|
+
var deployBucketFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsPermissions, staticGlobs }) => Effect33.gen(function* () {
|
|
5310
5343
|
const { exportName, config, hasHandler } = fn13;
|
|
5311
5344
|
const handlerName = exportName;
|
|
5312
5345
|
const tagCtx = {
|
|
@@ -5314,7 +5347,7 @@ var deployBucketFunction = ({ input, fn: fn13, layerArn, external, depsEnv, deps
|
|
|
5314
5347
|
stage: resolveStage(input.stage),
|
|
5315
5348
|
handler: handlerName
|
|
5316
5349
|
};
|
|
5317
|
-
yield*
|
|
5350
|
+
yield* Effect33.logDebug("Creating S3 bucket...");
|
|
5318
5351
|
const bucketName = `${input.project}-${tagCtx.stage}-${handlerName}`;
|
|
5319
5352
|
const { bucketArn } = yield* ensureBucket({
|
|
5320
5353
|
name: bucketName,
|
|
@@ -5322,7 +5355,7 @@ var deployBucketFunction = ({ input, fn: fn13, layerArn, external, depsEnv, deps
|
|
|
5322
5355
|
tags: makeTags(tagCtx, "s3-bucket")
|
|
5323
5356
|
});
|
|
5324
5357
|
if (!hasHandler) {
|
|
5325
|
-
yield*
|
|
5358
|
+
yield* Effect33.logDebug(`Bucket deployment complete (resource-only)! Bucket: ${bucketName}`);
|
|
5326
5359
|
return {
|
|
5327
5360
|
exportName,
|
|
5328
5361
|
status: "resource-only",
|
|
@@ -5354,7 +5387,7 @@ var deployBucketFunction = ({ input, fn: fn13, layerArn, external, depsEnv, deps
|
|
|
5354
5387
|
prefix: config.prefix,
|
|
5355
5388
|
suffix: config.suffix
|
|
5356
5389
|
});
|
|
5357
|
-
yield*
|
|
5390
|
+
yield* Effect33.logDebug(`Bucket deployment complete! Bucket: ${bucketName}`);
|
|
5358
5391
|
return {
|
|
5359
5392
|
exportName,
|
|
5360
5393
|
functionArn,
|
|
@@ -5365,8 +5398,8 @@ var deployBucketFunction = ({ input, fn: fn13, layerArn, external, depsEnv, deps
|
|
|
5365
5398
|
});
|
|
5366
5399
|
|
|
5367
5400
|
// src/deploy/deploy-mailer.ts
|
|
5368
|
-
import { Effect as
|
|
5369
|
-
var deployMailer = ({ project, stage, region, fn: fn13 }) =>
|
|
5401
|
+
import { Effect as Effect34, Console } from "effect";
|
|
5402
|
+
var deployMailer = ({ project, stage, region, fn: fn13 }) => Effect34.gen(function* () {
|
|
5370
5403
|
const { exportName, config } = fn13;
|
|
5371
5404
|
const handlerName = exportName;
|
|
5372
5405
|
const resolvedStage = resolveStage(stage);
|
|
@@ -5375,7 +5408,7 @@ var deployMailer = ({ project, stage, region, fn: fn13 }) => Effect33.gen(functi
|
|
|
5375
5408
|
stage: resolvedStage,
|
|
5376
5409
|
handler: handlerName
|
|
5377
5410
|
};
|
|
5378
|
-
yield*
|
|
5411
|
+
yield* Effect34.logDebug(`Ensuring SES identity for ${config.domain}...`);
|
|
5379
5412
|
const { domain, verified, dkimRecords } = yield* ensureSesIdentity({
|
|
5380
5413
|
domain: config.domain,
|
|
5381
5414
|
tags: makeTags(tagCtx, "ses")
|
|
@@ -5398,28 +5431,6 @@ var deployMailer = ({ project, stage, region, fn: fn13 }) => Effect33.gen(functi
|
|
|
5398
5431
|
};
|
|
5399
5432
|
});
|
|
5400
5433
|
|
|
5401
|
-
// src/deploy/deploy-api.ts
|
|
5402
|
-
import { Effect as Effect34 } from "effect";
|
|
5403
|
-
var deployApiFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsPermissions, staticGlobs }) => Effect34.gen(function* () {
|
|
5404
|
-
const { exportName, config } = fn13;
|
|
5405
|
-
const handlerName = exportName;
|
|
5406
|
-
const { functionArn, status } = yield* deployCoreLambda({
|
|
5407
|
-
input,
|
|
5408
|
-
exportName,
|
|
5409
|
-
handlerName,
|
|
5410
|
-
bundleType: "api",
|
|
5411
|
-
...config.permissions ? { permissions: config.permissions } : {},
|
|
5412
|
-
...config.memory ? { memory: config.memory } : {},
|
|
5413
|
-
...config.timeout ? { timeout: config.timeout } : {},
|
|
5414
|
-
...layerArn ? { layerArn } : {},
|
|
5415
|
-
...external ? { external } : {},
|
|
5416
|
-
...depsEnv ? { depsEnv } : {},
|
|
5417
|
-
...depsPermissions ? { depsPermissions } : {},
|
|
5418
|
-
...staticGlobs && staticGlobs.length > 0 ? { staticGlobs } : {}
|
|
5419
|
-
});
|
|
5420
|
-
return { exportName, functionArn, status, config, handlerName };
|
|
5421
|
-
});
|
|
5422
|
-
|
|
5423
5434
|
// src/deploy/deploy.ts
|
|
5424
5435
|
var statusLabel = (status) => {
|
|
5425
5436
|
switch (status) {
|
|
@@ -5560,7 +5571,6 @@ var buildBucketNameMap = (bucketHandlers, project, stage) => {
|
|
|
5560
5571
|
var validateDeps = (discovered, tableNameMap, bucketNameMap, mailerDomainMap) => {
|
|
5561
5572
|
const errors = [];
|
|
5562
5573
|
const allGroups = [
|
|
5563
|
-
...discovered.httpHandlers,
|
|
5564
5574
|
...discovered.apiHandlers,
|
|
5565
5575
|
...discovered.tableHandlers,
|
|
5566
5576
|
...discovered.fifoQueueHandlers,
|
|
@@ -5650,38 +5660,6 @@ var resolveHandlerEnv = (depsKeys, paramEntries, ctx) => {
|
|
|
5650
5660
|
depsPermissions: resolved?.depsPermissions ?? []
|
|
5651
5661
|
};
|
|
5652
5662
|
};
|
|
5653
|
-
var buildHttpTasks = (ctx, handlers, apiId, results) => {
|
|
5654
|
-
const tasks = [];
|
|
5655
|
-
const { region } = ctx.input;
|
|
5656
|
-
for (const { file, exports } of handlers) {
|
|
5657
|
-
for (const fn13 of exports) {
|
|
5658
|
-
tasks.push(
|
|
5659
|
-
Effect35.gen(function* () {
|
|
5660
|
-
const env = resolveHandlerEnv(fn13.depsKeys, fn13.paramEntries, ctx);
|
|
5661
|
-
const { exportName, functionArn, status, config, handlerName } = yield* deployLambda({
|
|
5662
|
-
input: makeDeployInput(ctx, file),
|
|
5663
|
-
fn: fn13,
|
|
5664
|
-
...ctx.layerArn ? { layerArn: ctx.layerArn } : {},
|
|
5665
|
-
...ctx.external.length > 0 ? { external: ctx.external } : {},
|
|
5666
|
-
depsEnv: env.depsEnv,
|
|
5667
|
-
depsPermissions: env.depsPermissions,
|
|
5668
|
-
...fn13.staticGlobs.length > 0 ? { staticGlobs: fn13.staticGlobs } : {}
|
|
5669
|
-
}).pipe(Effect35.provide(clients_exports.makeClients({ lambda: { region }, iam: { region } })));
|
|
5670
|
-
const { apiUrl: handlerUrl } = yield* addRouteToApi({
|
|
5671
|
-
apiId,
|
|
5672
|
-
region,
|
|
5673
|
-
functionArn,
|
|
5674
|
-
method: config.method,
|
|
5675
|
-
path: config.path
|
|
5676
|
-
}).pipe(Effect35.provide(clients_exports.makeClients({ lambda: { region }, apigatewayv2: { region } })));
|
|
5677
|
-
results.push({ exportName, url: handlerUrl, functionArn });
|
|
5678
|
-
yield* ctx.logComplete(exportName, "http", status);
|
|
5679
|
-
})
|
|
5680
|
-
);
|
|
5681
|
-
}
|
|
5682
|
-
}
|
|
5683
|
-
return tasks;
|
|
5684
|
-
};
|
|
5685
5663
|
var buildTableTasks = (ctx, handlers, results) => {
|
|
5686
5664
|
const tasks = [];
|
|
5687
5665
|
const { region } = ctx.input;
|
|
@@ -5707,10 +5685,9 @@ var buildTableTasks = (ctx, handlers, results) => {
|
|
|
5707
5685
|
}
|
|
5708
5686
|
return tasks;
|
|
5709
5687
|
};
|
|
5710
|
-
var buildAppTasks = (ctx, handlers, results,
|
|
5688
|
+
var buildAppTasks = (ctx, handlers, results, apiOriginDomain) => {
|
|
5711
5689
|
const tasks = [];
|
|
5712
5690
|
const { region } = ctx.input;
|
|
5713
|
-
const apiOriginDomain = apiId ? `${apiId}.execute-api.${region}.amazonaws.com` : void 0;
|
|
5714
5691
|
for (const { exports } of handlers) {
|
|
5715
5692
|
for (const fn13 of exports) {
|
|
5716
5693
|
tasks.push(
|
|
@@ -5739,10 +5716,9 @@ var buildAppTasks = (ctx, handlers, results, apiId) => {
|
|
|
5739
5716
|
}
|
|
5740
5717
|
return tasks;
|
|
5741
5718
|
};
|
|
5742
|
-
var buildStaticSiteTasks = (ctx, handlers, results,
|
|
5719
|
+
var buildStaticSiteTasks = (ctx, handlers, results, apiOriginDomain) => {
|
|
5743
5720
|
const tasks = [];
|
|
5744
5721
|
const { region } = ctx.input;
|
|
5745
|
-
const apiOriginDomain = apiId ? `${apiId}.execute-api.${region}.amazonaws.com` : void 0;
|
|
5746
5722
|
for (const { file, exports } of handlers) {
|
|
5747
5723
|
for (const fn13 of exports) {
|
|
5748
5724
|
tasks.push(
|
|
@@ -5842,7 +5818,7 @@ var buildMailerTasks = (ctx, handlers, results) => {
|
|
|
5842
5818
|
}
|
|
5843
5819
|
return tasks;
|
|
5844
5820
|
};
|
|
5845
|
-
var buildApiTasks = (ctx, handlers,
|
|
5821
|
+
var buildApiTasks = (ctx, handlers, results) => {
|
|
5846
5822
|
const tasks = [];
|
|
5847
5823
|
const { region } = ctx.input;
|
|
5848
5824
|
for (const { file, exports } of handlers) {
|
|
@@ -5850,7 +5826,7 @@ var buildApiTasks = (ctx, handlers, apiId, results) => {
|
|
|
5850
5826
|
tasks.push(
|
|
5851
5827
|
Effect35.gen(function* () {
|
|
5852
5828
|
const env = resolveHandlerEnv(fn13.depsKeys, fn13.paramEntries, ctx);
|
|
5853
|
-
const { exportName, functionArn, status,
|
|
5829
|
+
const { exportName, functionArn, status, handlerName } = yield* deployApiFunction({
|
|
5854
5830
|
input: makeDeployInput(ctx, file),
|
|
5855
5831
|
fn: fn13,
|
|
5856
5832
|
...ctx.layerArn ? { layerArn: ctx.layerArn } : {},
|
|
@@ -5859,21 +5835,14 @@ var buildApiTasks = (ctx, handlers, apiId, results) => {
|
|
|
5859
5835
|
depsPermissions: env.depsPermissions,
|
|
5860
5836
|
...fn13.staticGlobs.length > 0 ? { staticGlobs: fn13.staticGlobs } : {}
|
|
5861
5837
|
}).pipe(Effect35.provide(clients_exports.makeClients({ lambda: { region }, iam: { region } })));
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
region
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
apiId,
|
|
5871
|
-
region,
|
|
5872
|
-
functionArn,
|
|
5873
|
-
method: "ANY",
|
|
5874
|
-
path: `${config.basePath}/{proxy+}`
|
|
5875
|
-
}).pipe(Effect35.provide(clients_exports.makeClients({ lambda: { region }, apigatewayv2: { region } })));
|
|
5876
|
-
results.push({ exportName, url: handlerUrl, functionArn });
|
|
5838
|
+
const lambdaName = `${ctx.input.project}-${ctx.stage}-${handlerName}`;
|
|
5839
|
+
const { functionUrl } = yield* ensureFunctionUrl(lambdaName).pipe(
|
|
5840
|
+
Effect35.provide(clients_exports.makeClients({ lambda: { region } }))
|
|
5841
|
+
);
|
|
5842
|
+
yield* addFunctionUrlPublicAccess(lambdaName).pipe(
|
|
5843
|
+
Effect35.provide(clients_exports.makeClients({ lambda: { region } }))
|
|
5844
|
+
);
|
|
5845
|
+
results.push({ exportName, url: functionUrl, functionArn });
|
|
5877
5846
|
yield* ctx.logComplete(exportName, "api", status);
|
|
5878
5847
|
})
|
|
5879
5848
|
);
|
|
@@ -5888,8 +5857,7 @@ var deployProject = (input) => Effect35.gen(function* () {
|
|
|
5888
5857
|
return yield* Effect35.fail(new Error(`No files match patterns: ${input.patterns.join(", ")}`));
|
|
5889
5858
|
}
|
|
5890
5859
|
yield* Effect35.logDebug(`Found ${files.length} file(s) matching patterns`);
|
|
5891
|
-
const {
|
|
5892
|
-
const totalHttpHandlers = httpHandlers.reduce((acc, h) => acc + h.exports.length, 0);
|
|
5860
|
+
const { tableHandlers, appHandlers, staticSiteHandlers, fifoQueueHandlers, bucketHandlers, mailerHandlers, apiHandlers } = discoverHandlers(files);
|
|
5893
5861
|
const totalTableHandlers = tableHandlers.reduce((acc, h) => acc + h.exports.length, 0);
|
|
5894
5862
|
const totalAppHandlers = appHandlers.reduce((acc, h) => acc + h.exports.length, 0);
|
|
5895
5863
|
const totalStaticSiteHandlers = input.noSites ? 0 : staticSiteHandlers.reduce((acc, h) => acc + h.exports.length, 0);
|
|
@@ -5897,12 +5865,11 @@ var deployProject = (input) => Effect35.gen(function* () {
|
|
|
5897
5865
|
const totalBucketHandlers = bucketHandlers.reduce((acc, h) => acc + h.exports.length, 0);
|
|
5898
5866
|
const totalMailerHandlers = mailerHandlers.reduce((acc, h) => acc + h.exports.length, 0);
|
|
5899
5867
|
const totalApiHandlers = apiHandlers.reduce((acc, h) => acc + h.exports.length, 0);
|
|
5900
|
-
const totalAllHandlers =
|
|
5868
|
+
const totalAllHandlers = totalTableHandlers + totalAppHandlers + totalStaticSiteHandlers + totalFifoQueueHandlers + totalBucketHandlers + totalMailerHandlers + totalApiHandlers;
|
|
5901
5869
|
if (totalAllHandlers === 0) {
|
|
5902
5870
|
return yield* Effect35.fail(new Error("No handlers found in matched files"));
|
|
5903
5871
|
}
|
|
5904
5872
|
const parts = [];
|
|
5905
|
-
if (totalHttpHandlers > 0) parts.push(`${totalHttpHandlers} http`);
|
|
5906
5873
|
if (totalTableHandlers > 0) parts.push(`${totalTableHandlers} table`);
|
|
5907
5874
|
if (totalAppHandlers > 0) parts.push(`${totalAppHandlers} app`);
|
|
5908
5875
|
if (totalStaticSiteHandlers > 0) parts.push(`${totalStaticSiteHandlers} site`);
|
|
@@ -5912,7 +5879,7 @@ var deployProject = (input) => Effect35.gen(function* () {
|
|
|
5912
5879
|
if (totalApiHandlers > 0) parts.push(`${totalApiHandlers} api`);
|
|
5913
5880
|
yield* Console2.log(`
|
|
5914
5881
|
${c.dim("Handlers:")} ${parts.join(", ")}`);
|
|
5915
|
-
const discovered = {
|
|
5882
|
+
const discovered = { tableHandlers, appHandlers, staticSiteHandlers, fifoQueueHandlers, bucketHandlers, mailerHandlers, apiHandlers };
|
|
5916
5883
|
const requiredParams = collectRequiredParams(discovered, input.project, stage);
|
|
5917
5884
|
if (requiredParams.length > 0) {
|
|
5918
5885
|
const { missing } = yield* checkMissingParams(requiredParams).pipe(
|
|
@@ -5940,50 +5907,19 @@ var deployProject = (input) => Effect35.gen(function* () {
|
|
|
5940
5907
|
}
|
|
5941
5908
|
return yield* Effect35.fail(new Error("Unresolved deps \u2014 aborting deploy"));
|
|
5942
5909
|
}
|
|
5943
|
-
const
|
|
5910
|
+
const needsLambda = totalTableHandlers + totalAppHandlers + totalFifoQueueHandlers + totalBucketHandlers + totalMailerHandlers + totalApiHandlers > 0;
|
|
5911
|
+
const { layerArn, layerVersion, layerStatus, external } = needsLambda ? yield* prepareLayer({
|
|
5944
5912
|
project: input.project,
|
|
5945
5913
|
stage,
|
|
5946
5914
|
region: input.region,
|
|
5947
5915
|
packageDir: input.packageDir ?? input.projectDir
|
|
5948
|
-
});
|
|
5916
|
+
}) : { layerArn: void 0, layerVersion: void 0, layerStatus: void 0, external: [] };
|
|
5949
5917
|
if (layerArn && layerStatus) {
|
|
5950
5918
|
const status = layerStatus === "cached" ? c.dim("cached") : c.green("created");
|
|
5951
5919
|
yield* Console2.log(` ${c.dim("Layer:")} ${status} ${c.dim(`v${layerVersion}`)} (${external.length} packages)`);
|
|
5952
5920
|
}
|
|
5953
|
-
let apiId;
|
|
5954
|
-
let apiUrl;
|
|
5955
|
-
const staticSitesNeedApi = !input.noSites && staticSiteHandlers.some(
|
|
5956
|
-
({ exports }) => exports.some((fn13) => fn13.routePatterns.length > 0)
|
|
5957
|
-
);
|
|
5958
|
-
const appsNeedApi = appHandlers.some(
|
|
5959
|
-
({ exports }) => exports.some((fn13) => fn13.routePatterns.length > 0)
|
|
5960
|
-
);
|
|
5961
|
-
if (totalHttpHandlers > 0 || totalApiHandlers > 0 || staticSitesNeedApi || appsNeedApi) {
|
|
5962
|
-
const tagCtx = {
|
|
5963
|
-
project: input.project,
|
|
5964
|
-
stage,
|
|
5965
|
-
handler: "api"
|
|
5966
|
-
};
|
|
5967
|
-
const api = yield* ensureProjectApi({
|
|
5968
|
-
projectName: input.project,
|
|
5969
|
-
stage: tagCtx.stage,
|
|
5970
|
-
region: input.region,
|
|
5971
|
-
tags: makeTags(tagCtx, "api-gateway")
|
|
5972
|
-
}).pipe(
|
|
5973
|
-
Effect35.provide(
|
|
5974
|
-
clients_exports.makeClients({
|
|
5975
|
-
apigatewayv2: { region: input.region }
|
|
5976
|
-
})
|
|
5977
|
-
)
|
|
5978
|
-
);
|
|
5979
|
-
apiId = api.apiId;
|
|
5980
|
-
apiUrl = `https://${apiId}.execute-api.${input.region}.amazonaws.com`;
|
|
5981
|
-
yield* Console2.log(` ${c.dim("API Gateway:")} ${apiId}`);
|
|
5982
|
-
}
|
|
5983
5921
|
yield* Console2.log("");
|
|
5984
5922
|
const manifest = [];
|
|
5985
|
-
for (const { exports } of httpHandlers)
|
|
5986
|
-
for (const fn13 of exports) manifest.push({ name: fn13.exportName, type: "http" });
|
|
5987
5923
|
for (const { exports } of tableHandlers)
|
|
5988
5924
|
for (const fn13 of exports) manifest.push({ name: fn13.exportName, type: "table" });
|
|
5989
5925
|
for (const { exports } of appHandlers)
|
|
@@ -6012,7 +5948,6 @@ var deployProject = (input) => Effect35.gen(function* () {
|
|
|
6012
5948
|
mailerDomainMap,
|
|
6013
5949
|
logComplete
|
|
6014
5950
|
};
|
|
6015
|
-
const httpResults = [];
|
|
6016
5951
|
const tableResults = [];
|
|
6017
5952
|
const appResults = [];
|
|
6018
5953
|
const staticSiteResults = [];
|
|
@@ -6020,17 +5955,43 @@ var deployProject = (input) => Effect35.gen(function* () {
|
|
|
6020
5955
|
const bucketResults = [];
|
|
6021
5956
|
const mailerResults = [];
|
|
6022
5957
|
const apiResults = [];
|
|
6023
|
-
const
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
5958
|
+
const staticSitesNeedApi = !input.noSites && staticSiteHandlers.some(
|
|
5959
|
+
({ exports }) => exports.some((fn13) => fn13.routePatterns.length > 0)
|
|
5960
|
+
);
|
|
5961
|
+
const appsNeedApi = appHandlers.some(
|
|
5962
|
+
({ exports }) => exports.some((fn13) => fn13.routePatterns.length > 0)
|
|
5963
|
+
);
|
|
5964
|
+
const needsTwoPhase = (staticSitesNeedApi || appsNeedApi) && totalApiHandlers > 0;
|
|
5965
|
+
if (needsTwoPhase) {
|
|
5966
|
+
const phase1Tasks = [
|
|
5967
|
+
...buildApiTasks(ctx, apiHandlers, apiResults),
|
|
5968
|
+
...buildTableTasks(ctx, tableHandlers, tableResults),
|
|
5969
|
+
...buildFifoQueueTasks(ctx, fifoQueueHandlers, fifoQueueResults),
|
|
5970
|
+
...buildBucketTasks(ctx, bucketHandlers, bucketResults),
|
|
5971
|
+
...buildMailerTasks(ctx, mailerHandlers, mailerResults)
|
|
5972
|
+
];
|
|
5973
|
+
yield* Effect35.all(phase1Tasks, { concurrency: DEPLOY_CONCURRENCY, discard: true });
|
|
5974
|
+
const firstApiUrl = apiResults[0]?.url;
|
|
5975
|
+
const apiOriginDomain = firstApiUrl ? firstApiUrl.replace("https://", "").replace(/\/$/, "") : void 0;
|
|
5976
|
+
const phase2Tasks = [
|
|
5977
|
+
...buildAppTasks(ctx, appHandlers, appResults, apiOriginDomain),
|
|
5978
|
+
...input.noSites ? [] : buildStaticSiteTasks(ctx, staticSiteHandlers, staticSiteResults, apiOriginDomain)
|
|
5979
|
+
];
|
|
5980
|
+
if (phase2Tasks.length > 0) {
|
|
5981
|
+
yield* Effect35.all(phase2Tasks, { concurrency: DEPLOY_CONCURRENCY, discard: true });
|
|
5982
|
+
}
|
|
5983
|
+
} else {
|
|
5984
|
+
const tasks = [
|
|
5985
|
+
...buildApiTasks(ctx, apiHandlers, apiResults),
|
|
5986
|
+
...buildTableTasks(ctx, tableHandlers, tableResults),
|
|
5987
|
+
...buildAppTasks(ctx, appHandlers, appResults),
|
|
5988
|
+
...input.noSites ? [] : buildStaticSiteTasks(ctx, staticSiteHandlers, staticSiteResults),
|
|
5989
|
+
...buildFifoQueueTasks(ctx, fifoQueueHandlers, fifoQueueResults),
|
|
5990
|
+
...buildBucketTasks(ctx, bucketHandlers, bucketResults),
|
|
5991
|
+
...buildMailerTasks(ctx, mailerHandlers, mailerResults)
|
|
5992
|
+
];
|
|
5993
|
+
yield* Effect35.all(tasks, { concurrency: DEPLOY_CONCURRENCY, discard: true });
|
|
5994
|
+
}
|
|
6034
5995
|
if (!input.noSites && staticSiteResults.length > 0 || appResults.length > 0) {
|
|
6035
5996
|
yield* cleanupOrphanedFunctions(input.project, stage).pipe(
|
|
6036
5997
|
Effect35.provide(clients_exports.makeClients({
|
|
@@ -6042,43 +6003,19 @@ var deployProject = (input) => Effect35.gen(function* () {
|
|
|
6042
6003
|
)
|
|
6043
6004
|
);
|
|
6044
6005
|
}
|
|
6045
|
-
|
|
6046
|
-
const activeRouteKeys = /* @__PURE__ */ new Set();
|
|
6047
|
-
for (const { exports } of httpHandlers) {
|
|
6048
|
-
for (const fn13 of exports) {
|
|
6049
|
-
activeRouteKeys.add(`${fn13.config.method} ${fn13.config.path}`);
|
|
6050
|
-
}
|
|
6051
|
-
}
|
|
6052
|
-
for (const { exports } of apiHandlers) {
|
|
6053
|
-
for (const fn13 of exports) {
|
|
6054
|
-
activeRouteKeys.add(`ANY ${fn13.config.basePath}`);
|
|
6055
|
-
activeRouteKeys.add(`ANY ${fn13.config.basePath}/{proxy+}`);
|
|
6056
|
-
}
|
|
6057
|
-
}
|
|
6058
|
-
yield* removeStaleRoutes(apiId, activeRouteKeys).pipe(
|
|
6059
|
-
Effect35.provide(
|
|
6060
|
-
clients_exports.makeClients({
|
|
6061
|
-
apigatewayv2: { region: input.region }
|
|
6062
|
-
})
|
|
6063
|
-
)
|
|
6064
|
-
);
|
|
6065
|
-
}
|
|
6066
|
-
if (apiUrl) {
|
|
6067
|
-
yield* Effect35.logDebug(`Deployment complete! API: ${apiUrl}`);
|
|
6068
|
-
}
|
|
6069
|
-
return { apiId, apiUrl, httpResults, tableResults, appResults, staticSiteResults, fifoQueueResults, bucketResults, mailerResults, apiResults };
|
|
6006
|
+
return { tableResults, appResults, staticSiteResults, fifoQueueResults, bucketResults, mailerResults, apiResults };
|
|
6070
6007
|
});
|
|
6071
6008
|
|
|
6072
6009
|
// src/cli/config.ts
|
|
6073
6010
|
import { Options } from "@effect/cli";
|
|
6074
|
-
import * as
|
|
6075
|
-
import * as
|
|
6011
|
+
import * as path8 from "path";
|
|
6012
|
+
import * as fs6 from "fs";
|
|
6076
6013
|
import { pathToFileURL } from "url";
|
|
6077
6014
|
import * as esbuild2 from "esbuild";
|
|
6078
6015
|
import { Effect as Effect36 } from "effect";
|
|
6079
6016
|
var loadConfig = Effect36.fn("loadConfig")(function* () {
|
|
6080
|
-
const configPath =
|
|
6081
|
-
if (!
|
|
6017
|
+
const configPath = path8.resolve(process.cwd(), "effortless.config.ts");
|
|
6018
|
+
if (!fs6.existsSync(configPath)) {
|
|
6082
6019
|
return null;
|
|
6083
6020
|
}
|
|
6084
6021
|
const result = yield* Effect36.tryPromise({
|
|
@@ -6097,12 +6034,12 @@ var loadConfig = Effect36.fn("loadConfig")(function* () {
|
|
|
6097
6034
|
return null;
|
|
6098
6035
|
}
|
|
6099
6036
|
const code = output.text;
|
|
6100
|
-
const tempFile =
|
|
6101
|
-
|
|
6037
|
+
const tempFile = path8.join(process.cwd(), ".effortless-config.mjs");
|
|
6038
|
+
fs6.writeFileSync(tempFile, code);
|
|
6102
6039
|
const mod = yield* Effect36.tryPromise({
|
|
6103
6040
|
try: () => import(pathToFileURL(tempFile).href),
|
|
6104
6041
|
catch: (error) => new Error(`Failed to load config: ${error}`)
|
|
6105
|
-
}).pipe(Effect36.ensuring(Effect36.sync(() =>
|
|
6042
|
+
}).pipe(Effect36.ensuring(Effect36.sync(() => fs6.unlinkSync(tempFile))));
|
|
6106
6043
|
return mod.default;
|
|
6107
6044
|
});
|
|
6108
6045
|
var projectOption = Options.text("project").pipe(
|
|
@@ -6154,14 +6091,14 @@ var getPatternsFromConfig = (config) => {
|
|
|
6154
6091
|
import * as Context13 from "effect/Context";
|
|
6155
6092
|
import * as Layer14 from "effect/Layer";
|
|
6156
6093
|
import * as Effect37 from "effect/Effect";
|
|
6157
|
-
import * as
|
|
6094
|
+
import * as path9 from "path";
|
|
6158
6095
|
var ProjectConfig = class _ProjectConfig extends Context13.Tag("ProjectConfig")() {
|
|
6159
6096
|
static Live = Layer14.effect(
|
|
6160
6097
|
_ProjectConfig,
|
|
6161
6098
|
Effect37.gen(function* () {
|
|
6162
6099
|
const config = yield* loadConfig();
|
|
6163
6100
|
const cwd = process.cwd();
|
|
6164
|
-
const projectDir = config?.root ?
|
|
6101
|
+
const projectDir = config?.root ? path9.resolve(cwd, config.root) : cwd;
|
|
6165
6102
|
return { config, cwd, projectDir };
|
|
6166
6103
|
})
|
|
6167
6104
|
);
|
|
@@ -6190,7 +6127,6 @@ var deployCommand = Command.make(
|
|
|
6190
6127
|
const clientsLayer = clients_exports.makeClients({
|
|
6191
6128
|
lambda: { region: finalRegion },
|
|
6192
6129
|
iam: { region: finalRegion },
|
|
6193
|
-
apigatewayv2: { region: finalRegion },
|
|
6194
6130
|
dynamodb: { region: finalRegion },
|
|
6195
6131
|
resource_groups_tagging_api: { region: finalRegion },
|
|
6196
6132
|
s3: { region: finalRegion },
|
|
@@ -6215,31 +6151,35 @@ var deployCommand = Command.make(
|
|
|
6215
6151
|
noSites,
|
|
6216
6152
|
verbose
|
|
6217
6153
|
});
|
|
6218
|
-
const total = results.
|
|
6154
|
+
const total = results.tableResults.length + results.appResults.length + results.staticSiteResults.length + results.apiResults.length;
|
|
6219
6155
|
yield* Console3.log(`
|
|
6220
6156
|
${c.green(`Deployed ${total} handler(s):`)}`);
|
|
6221
|
-
if (results.apiUrl) {
|
|
6222
|
-
yield* Console3.log(`
|
|
6223
|
-
API: ${c.cyan(results.apiUrl)}`);
|
|
6224
|
-
}
|
|
6225
6157
|
const summaryLines = [];
|
|
6226
|
-
for (const r of results.httpResults) {
|
|
6227
|
-
const pathPart = results.apiUrl ? r.url.replace(results.apiUrl, "") : r.url;
|
|
6228
|
-
summaryLines.push({ name: r.exportName, line: ` ${c.cyan("[http]")} ${c.bold(r.exportName)} ${c.dim(pathPart)}` });
|
|
6229
|
-
}
|
|
6230
6158
|
for (const r of results.appResults) {
|
|
6231
|
-
|
|
6232
|
-
summaryLines.push({ name: r.exportName, line: ` ${c.cyan("[app]")} ${c.bold(r.exportName)} ${c.dim(pathPart)}` });
|
|
6159
|
+
summaryLines.push({ name: r.exportName, line: ` ${c.cyan("[app]")} ${c.bold(r.exportName)} ${c.dim(r.url)}` });
|
|
6233
6160
|
}
|
|
6234
6161
|
for (const r of results.tableResults) {
|
|
6235
6162
|
summaryLines.push({ name: r.exportName, line: ` ${c.cyan("[table]")} ${c.bold(r.exportName)}` });
|
|
6236
6163
|
}
|
|
6237
6164
|
for (const r of results.apiResults) {
|
|
6238
|
-
|
|
6239
|
-
summaryLines.push({ name: r.exportName, line: ` ${c.cyan("[api]")} ${c.bold(r.exportName)} ${c.dim(pathPart)}` });
|
|
6165
|
+
summaryLines.push({ name: r.exportName, line: ` ${c.cyan("[api]")} ${c.bold(r.exportName)} ${c.dim(r.url)}` });
|
|
6240
6166
|
}
|
|
6241
6167
|
for (const r of results.staticSiteResults) {
|
|
6242
|
-
|
|
6168
|
+
let line = ` ${c.cyan("[site]")} ${c.bold(r.exportName)}: ${c.cyan(r.url)}`;
|
|
6169
|
+
const extras = [];
|
|
6170
|
+
if (r.seoGenerated) extras.push(`seo: ${r.seoGenerated.join(", ")}`);
|
|
6171
|
+
if (r.indexingResult) {
|
|
6172
|
+
const { submitted, skipped, failed } = r.indexingResult;
|
|
6173
|
+
if (submitted > 0 || failed > 0) {
|
|
6174
|
+
const parts = [`${submitted} submitted`];
|
|
6175
|
+
if (failed > 0) parts.push(c.red(`${failed} failed`));
|
|
6176
|
+
extras.push(`indexing: ${parts.join(", ")}`);
|
|
6177
|
+
} else {
|
|
6178
|
+
extras.push(`indexing: all ${skipped} pages already indexed`);
|
|
6179
|
+
}
|
|
6180
|
+
}
|
|
6181
|
+
if (extras.length > 0) line += ` ${c.dim(extras.join(" | "))}`;
|
|
6182
|
+
summaryLines.push({ name: r.exportName, line });
|
|
6243
6183
|
}
|
|
6244
6184
|
summaryLines.sort((a, b) => a.name.localeCompare(b.name));
|
|
6245
6185
|
for (const { line } of summaryLines) {
|
|
@@ -6248,7 +6188,7 @@ ${c.green(`Deployed ${total} handler(s):`)}`);
|
|
|
6248
6188
|
}),
|
|
6249
6189
|
onSome: (targetValue) => Effect38.gen(function* () {
|
|
6250
6190
|
if (isFilePath(targetValue)) {
|
|
6251
|
-
const fullPath =
|
|
6191
|
+
const fullPath = path10.isAbsolute(targetValue) ? targetValue : path10.resolve(projectDir, targetValue);
|
|
6252
6192
|
const input = {
|
|
6253
6193
|
projectDir,
|
|
6254
6194
|
packageDir: cwd,
|
|
@@ -6257,30 +6197,16 @@ ${c.green(`Deployed ${total} handler(s):`)}`);
|
|
|
6257
6197
|
stage: finalStage,
|
|
6258
6198
|
region: finalRegion
|
|
6259
6199
|
};
|
|
6260
|
-
const httpResult = yield* deployAll(input).pipe(
|
|
6261
|
-
Effect38.catchIf(
|
|
6262
|
-
(e) => e instanceof Error && e.message.includes("No defineHttp"),
|
|
6263
|
-
() => Effect38.succeed(null)
|
|
6264
|
-
)
|
|
6265
|
-
);
|
|
6266
6200
|
const tableResults = yield* deployAllTables(input).pipe(
|
|
6267
6201
|
Effect38.catchIf(
|
|
6268
6202
|
(e) => e instanceof Error && e.message.includes("No defineTable"),
|
|
6269
6203
|
() => Effect38.succeed([])
|
|
6270
6204
|
)
|
|
6271
6205
|
);
|
|
6272
|
-
if (
|
|
6206
|
+
if (tableResults.length === 0) {
|
|
6273
6207
|
yield* Console3.error("No handlers found in file");
|
|
6274
6208
|
return;
|
|
6275
6209
|
}
|
|
6276
|
-
if (httpResult) {
|
|
6277
|
-
yield* Console3.log(`
|
|
6278
|
-
API Gateway: ${c.cyan(httpResult.apiUrl)}`);
|
|
6279
|
-
yield* Console3.log(c.green(`Deployed ${httpResult.handlers.length} HTTP handler(s):`));
|
|
6280
|
-
for (const r of httpResult.handlers) {
|
|
6281
|
-
yield* Console3.log(` ${c.bold(r.exportName)}: ${c.cyan(r.url)}`);
|
|
6282
|
-
}
|
|
6283
|
-
}
|
|
6284
6210
|
if (tableResults.length > 0) {
|
|
6285
6211
|
yield* Console3.log(c.green(`
|
|
6286
6212
|
Deployed ${tableResults.length} table handler(s):`));
|
|
@@ -6309,7 +6235,7 @@ Deployed ${tableResults.length} table handler(s):`));
|
|
|
6309
6235
|
const foundFile = found.file;
|
|
6310
6236
|
const foundExport = found.exportName;
|
|
6311
6237
|
const handlerType = found.type;
|
|
6312
|
-
yield* Console3.log(`Found handler ${c.bold(targetValue)} in ${c.dim(
|
|
6238
|
+
yield* Console3.log(`Found handler ${c.bold(targetValue)} in ${c.dim(path10.relative(projectDir, foundFile))}`);
|
|
6313
6239
|
const input = {
|
|
6314
6240
|
projectDir,
|
|
6315
6241
|
packageDir: cwd,
|
|
@@ -6340,16 +6266,12 @@ ${c.green("Deployed:")} ${c.cyan(result.url)}`);
|
|
|
6340
6266
|
// src/cli/commands/status.ts
|
|
6341
6267
|
import { Command as Command2 } from "@effect/cli";
|
|
6342
6268
|
import { Effect as Effect39, Console as Console4, Logger as Logger2, LogLevel as LogLevel2, Option as Option2 } from "effect";
|
|
6343
|
-
var { lambda
|
|
6269
|
+
var { lambda } = clients_exports;
|
|
6344
6270
|
var INTERNAL_HANDLERS = /* @__PURE__ */ new Set(["api", "platform"]);
|
|
6345
6271
|
var extractFunctionName = (arn) => {
|
|
6346
6272
|
const match = arn.match(/:function:([^:]+)$/);
|
|
6347
6273
|
return match?.[1];
|
|
6348
6274
|
};
|
|
6349
|
-
var extractApiId = (arn) => {
|
|
6350
|
-
const match = arn.match(/\/apis\/([a-z0-9]+)$/);
|
|
6351
|
-
return match?.[1];
|
|
6352
|
-
};
|
|
6353
6275
|
var formatDate = (date) => {
|
|
6354
6276
|
if (!date) return "";
|
|
6355
6277
|
const d = typeof date === "string" ? new Date(date) : date;
|
|
@@ -6376,12 +6298,6 @@ var getLambdaDetails = (functionName) => Effect39.gen(function* () {
|
|
|
6376
6298
|
}).pipe(
|
|
6377
6299
|
Effect39.catchAll(() => Effect39.succeed({}))
|
|
6378
6300
|
);
|
|
6379
|
-
var getApiUrl = (apiId) => Effect39.gen(function* () {
|
|
6380
|
-
const api = yield* apigateway.make("get_api", { ApiId: apiId });
|
|
6381
|
-
return api.ApiEndpoint;
|
|
6382
|
-
}).pipe(
|
|
6383
|
-
Effect39.catchAll(() => Effect39.succeed(void 0))
|
|
6384
|
-
);
|
|
6385
6301
|
var discoverCodeHandlers = (projectDir, patterns) => {
|
|
6386
6302
|
const files = findHandlerFiles(patterns, projectDir);
|
|
6387
6303
|
const discovered = discoverHandlers(files);
|
|
@@ -6398,22 +6314,17 @@ var discoverAwsHandlers = (resources) => {
|
|
|
6398
6314
|
const lambdaResource = handlerResources.find(
|
|
6399
6315
|
(r) => r.Tags?.find((t) => t.Key === "effortless:type" && t.Value === "lambda")
|
|
6400
6316
|
);
|
|
6401
|
-
const apiResource = handlerResources.find(
|
|
6402
|
-
(r) => r.Tags?.find((t) => t.Key === "effortless:type" && t.Value === "api-gateway")
|
|
6403
|
-
);
|
|
6404
6317
|
const typeTag = handlerResources[0]?.Tags?.find((t) => t.Key === "effortless:type");
|
|
6405
6318
|
const type = typeTag?.Value ?? "unknown";
|
|
6406
6319
|
handlers.push({
|
|
6407
6320
|
name,
|
|
6408
6321
|
type,
|
|
6409
|
-
lambdaArn: lambdaResource?.ResourceARN ?? void 0
|
|
6410
|
-
apiArn: apiResource?.ResourceARN ?? void 0
|
|
6322
|
+
lambdaArn: lambdaResource?.ResourceARN ?? void 0
|
|
6411
6323
|
});
|
|
6412
6324
|
}
|
|
6413
6325
|
return handlers;
|
|
6414
6326
|
};
|
|
6415
6327
|
var TYPE_LABELS = {
|
|
6416
|
-
http: "http",
|
|
6417
6328
|
table: "table",
|
|
6418
6329
|
app: "app",
|
|
6419
6330
|
api: "api",
|
|
@@ -6437,9 +6348,9 @@ var STATUS_COLORS = {
|
|
|
6437
6348
|
var formatStatus = (status) => {
|
|
6438
6349
|
return STATUS_COLORS[status](status.padEnd(10));
|
|
6439
6350
|
};
|
|
6440
|
-
var formatRoute = (method,
|
|
6441
|
-
if (method &&
|
|
6442
|
-
if (
|
|
6351
|
+
var formatRoute = (method, path12) => {
|
|
6352
|
+
if (method && path12) return `${method.padEnd(5)} ${path12}`;
|
|
6353
|
+
if (path12) return path12;
|
|
6443
6354
|
return "";
|
|
6444
6355
|
};
|
|
6445
6356
|
var formatEntry = (entry) => {
|
|
@@ -6472,7 +6383,6 @@ var statusCommand = Command2.make(
|
|
|
6472
6383
|
}
|
|
6473
6384
|
const clientsLayer = clients_exports.makeClients({
|
|
6474
6385
|
lambda: { region: finalRegion },
|
|
6475
|
-
apigatewayv2: { region: finalRegion },
|
|
6476
6386
|
resource_groups_tagging_api: { region: finalRegion }
|
|
6477
6387
|
});
|
|
6478
6388
|
const logLevel = verbose ? LogLevel2.Debug : LogLevel2.Info;
|
|
@@ -6486,25 +6396,6 @@ Status for ${c.bold(project + "/" + finalStage)}:
|
|
|
6486
6396
|
const resources = yield* getAllResourcesByTags(project, finalStage, finalRegion);
|
|
6487
6397
|
const awsHandlers = discoverAwsHandlers(resources);
|
|
6488
6398
|
const awsHandlerNames = new Set(awsHandlers.map((h) => h.name));
|
|
6489
|
-
let apiUrl;
|
|
6490
|
-
for (const handler of awsHandlers) {
|
|
6491
|
-
if (handler.apiArn) {
|
|
6492
|
-
const apiId = extractApiId(handler.apiArn);
|
|
6493
|
-
if (apiId) {
|
|
6494
|
-
apiUrl = yield* getApiUrl(apiId);
|
|
6495
|
-
break;
|
|
6496
|
-
}
|
|
6497
|
-
}
|
|
6498
|
-
}
|
|
6499
|
-
const apiResource = resources.find(
|
|
6500
|
-
(r) => r.Tags?.find((t) => t.Key === "effortless:handler" && t.Value === "api") && r.Tags?.find((t) => t.Key === "effortless:type" && t.Value === "api-gateway")
|
|
6501
|
-
);
|
|
6502
|
-
if (!apiUrl && apiResource?.ResourceARN) {
|
|
6503
|
-
const apiId = extractApiId(apiResource.ResourceARN);
|
|
6504
|
-
if (apiId) {
|
|
6505
|
-
apiUrl = yield* getApiUrl(apiId);
|
|
6506
|
-
}
|
|
6507
|
-
}
|
|
6508
6399
|
const entries = [];
|
|
6509
6400
|
for (const handler of codeHandlers) {
|
|
6510
6401
|
const inAws = awsHandlers.find((h) => h.name === handler.name);
|
|
@@ -6560,10 +6451,6 @@ Status for ${c.bold(project + "/" + finalStage)}:
|
|
|
6560
6451
|
for (const entry of entries) {
|
|
6561
6452
|
yield* Console4.log(formatEntry(entry));
|
|
6562
6453
|
}
|
|
6563
|
-
if (apiUrl) {
|
|
6564
|
-
yield* Console4.log(`
|
|
6565
|
-
API: ${c.cyan(apiUrl)}`);
|
|
6566
|
-
}
|
|
6567
6454
|
const counts = {
|
|
6568
6455
|
new: entries.filter((e) => e.status === "new").length,
|
|
6569
6456
|
deployed: entries.filter((e) => e.status === "deployed").length,
|
|
@@ -7100,8 +6987,8 @@ var logsCommand = Command4.make(
|
|
|
7100
6987
|
// src/cli/commands/layer.ts
|
|
7101
6988
|
import { Command as Command5, Options as Options4 } from "@effect/cli";
|
|
7102
6989
|
import { Effect as Effect43, Console as Console7 } from "effect";
|
|
7103
|
-
import * as
|
|
7104
|
-
import * as
|
|
6990
|
+
import * as path11 from "path";
|
|
6991
|
+
import * as fs7 from "fs";
|
|
7105
6992
|
var buildOption = Options4.boolean("build").pipe(
|
|
7106
6993
|
Options4.withDescription("Build layer directory locally (for debugging)")
|
|
7107
6994
|
);
|
|
@@ -7179,13 +7066,13 @@ Layer name: ${projectName}-deps`);
|
|
|
7179
7066
|
}
|
|
7180
7067
|
});
|
|
7181
7068
|
var buildLayer = (projectDir, output, verbose) => Effect43.gen(function* () {
|
|
7182
|
-
const outputDir =
|
|
7183
|
-
const layerDir =
|
|
7184
|
-
const layerRoot =
|
|
7185
|
-
if (
|
|
7186
|
-
|
|
7069
|
+
const outputDir = path11.isAbsolute(output) ? output : path11.resolve(projectDir, output);
|
|
7070
|
+
const layerDir = path11.join(outputDir, "nodejs", "node_modules");
|
|
7071
|
+
const layerRoot = path11.join(outputDir, "nodejs");
|
|
7072
|
+
if (fs7.existsSync(layerRoot)) {
|
|
7073
|
+
fs7.rmSync(layerRoot, { recursive: true });
|
|
7187
7074
|
}
|
|
7188
|
-
|
|
7075
|
+
fs7.mkdirSync(layerDir, { recursive: true });
|
|
7189
7076
|
yield* Console7.log(`
|
|
7190
7077
|
${c.bold("=== Building Layer Locally ===")}
|
|
7191
7078
|
`);
|
|
@@ -7240,14 +7127,14 @@ Collected ${allPackages.length} packages for layer`);
|
|
|
7240
7127
|
}
|
|
7241
7128
|
continue;
|
|
7242
7129
|
}
|
|
7243
|
-
const destPath =
|
|
7130
|
+
const destPath = path11.join(layerDir, pkgName);
|
|
7244
7131
|
if (pkgName.startsWith("@")) {
|
|
7245
|
-
const scopeDir =
|
|
7246
|
-
if (!
|
|
7247
|
-
|
|
7132
|
+
const scopeDir = path11.join(layerDir, pkgName.split("/")[0] ?? pkgName);
|
|
7133
|
+
if (!fs7.existsSync(scopeDir)) {
|
|
7134
|
+
fs7.mkdirSync(scopeDir, { recursive: true });
|
|
7248
7135
|
}
|
|
7249
7136
|
}
|
|
7250
|
-
|
|
7137
|
+
fs7.cpSync(srcPath, destPath, { recursive: true });
|
|
7251
7138
|
copied++;
|
|
7252
7139
|
}
|
|
7253
7140
|
yield* Console7.log(c.green(`
|