@awsless/awsless 0.0.367 → 0.0.369

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/bin.js CHANGED
@@ -596,9 +596,9 @@ var require_Alias = __commonJS({
596
596
  var anchors = require_anchors();
597
597
  var visit = require_visit();
598
598
  var identity = require_identity();
599
- var Node24 = require_Node();
599
+ var Node25 = require_Node();
600
600
  var toJS = require_toJS();
601
- var Alias = class extends Node24.NodeBase {
601
+ var Alias = class extends Node25.NodeBase {
602
602
  constructor(source) {
603
603
  super(identity.ALIAS);
604
604
  this.source = source;
@@ -696,10 +696,10 @@ var require_Scalar = __commonJS({
696
696
  "../../node_modules/.pnpm/yaml@2.5.0/node_modules/yaml/dist/nodes/Scalar.js"(exports) {
697
697
  "use strict";
698
698
  var identity = require_identity();
699
- var Node24 = require_Node();
699
+ var Node25 = require_Node();
700
700
  var toJS = require_toJS();
701
701
  var isScalarValue = (value) => !value || typeof value !== "function" && typeof value !== "object";
702
- var Scalar = class extends Node24.NodeBase {
702
+ var Scalar = class extends Node25.NodeBase {
703
703
  constructor(value) {
704
704
  super(identity.SCALAR);
705
705
  this.value = value;
@@ -803,7 +803,7 @@ var require_Collection = __commonJS({
803
803
  "use strict";
804
804
  var createNode = require_createNode();
805
805
  var identity = require_identity();
806
- var Node24 = require_Node();
806
+ var Node25 = require_Node();
807
807
  function collectionFromPath(schema, path, value) {
808
808
  let v = value;
809
809
  for (let i = path.length - 1; i >= 0; --i) {
@@ -827,7 +827,7 @@ var require_Collection = __commonJS({
827
827
  });
828
828
  }
829
829
  var isEmptyPath = (path) => path == null || typeof path === "object" && !!path[Symbol.iterator]().next().done;
830
- var Collection = class extends Node24.NodeBase {
830
+ var Collection = class extends Node25.NodeBase {
831
831
  constructor(type, schema) {
832
832
  super(type);
833
833
  Object.defineProperty(this, "schema", {
@@ -7680,10 +7680,22 @@ var LogRetentionSchema = DurationSchema.refine(
7680
7680
  },
7681
7681
  `Invalid log retention. Valid days are: ${validLogRetentionDays.map((days7) => `${days7}`).join(", ")}`
7682
7682
  ).describe("The log retention duration.");
7683
+ var LogSubscriptionSchema = z5.union([
7684
+ LocalFileSchema.transform((file) => ({
7685
+ file
7686
+ })),
7687
+ z5.object({
7688
+ subscriber: LocalFileSchema,
7689
+ filter: z5.string().optional()
7690
+ })
7691
+ ]).describe(
7692
+ "Log Subscription allow you to subscribe to a real-time stream of log events and have them delivered to a specific destination"
7693
+ );
7683
7694
  var LogSchema = z5.union([
7684
7695
  z5.boolean().transform((enabled) => ({ retention: enabled ? days(7) : days(0) })),
7685
7696
  LogRetentionSchema.transform((retention) => ({ retention })),
7686
7697
  z5.object({
7698
+ subscription: LogSubscriptionSchema.optional(),
7687
7699
  retention: LogRetentionSchema.optional(),
7688
7700
  format: z5.enum(["text", "json"]).describe(
7689
7701
  `The format in which Lambda sends your function's application and system logs to CloudWatch. Select between plain text and structured JSON.`
@@ -7981,6 +7993,11 @@ var InstancesSchema = z12.record(
7981
7993
  })
7982
7994
  ).optional().describe("Define the instances in your stack.");
7983
7995
 
7996
+ // src/feature/log-subscription/schema.ts
7997
+ var LogSubscriptionSchema2 = FunctionSchema.optional().describe(
7998
+ "Log Subscription allow you to subscribe to a real-time stream of log events and have them delivered to a specific destination."
7999
+ );
8000
+
7984
8001
  // src/feature/pubsub/schema.ts
7985
8002
  import { z as z13 } from "zod";
7986
8003
  var DomainSchema = ResourceIdSchema.describe("The domain id to link your Pubsub API with.");
@@ -8012,9 +8029,9 @@ var PubSubSchema = z13.record(
8012
8029
  ).optional().describe("Define the pubsub subscriber in your stack.");
8013
8030
 
8014
8031
  // src/feature/queue/schema.ts
8015
- import { z as z14 } from "zod";
8016
8032
  import { days as days2, hours, minutes as minutes3, seconds as seconds2 } from "@awsless/duration";
8017
8033
  import { kibibytes } from "@awsless/size";
8034
+ import { z as z14 } from "zod";
8018
8035
  var RetentionPeriodSchema = DurationSchema.refine(
8019
8036
  durationMin(minutes3(1)),
8020
8037
  "Minimum retention period is 1 minute"
@@ -8073,7 +8090,7 @@ var QueuesSchema = z14.record(
8073
8090
  }
8074
8091
  })),
8075
8092
  z14.object({
8076
- consumer: FunctionSchema.describe("he consuming lambda function properties."),
8093
+ consumer: FunctionSchema.describe("The consuming lambda function properties."),
8077
8094
  retentionPeriod: RetentionPeriodSchema.optional(),
8078
8095
  visibilityTimeout: VisibilityTimeoutSchema.optional(),
8079
8096
  deliveryDelay: DeliveryDelaySchema.optional(),
@@ -8264,6 +8281,8 @@ var AppSchema = z21.object({
8264
8281
  // .regex(/^[a-z]+$/)
8265
8282
  // .default('prod')
8266
8283
  // .describe('The deployment stage.'),
8284
+ // onFailure: OnFailureSchema,
8285
+ logSubscription: LogSubscriptionSchema2,
8267
8286
  defaults: z21.object({
8268
8287
  auth: AuthDefaultSchema,
8269
8288
  domains: DomainsDefaultSchema,
@@ -8797,411 +8816,143 @@ var loadStackConfigs = async (options) => {
8797
8816
  return stacks;
8798
8817
  };
8799
8818
 
8800
- // src/cli/ui/app.ts
8801
- import { note } from "@clack/prompts";
8802
- var logApp = (app, opt) => {
8803
- const data = {
8804
- App: app.name,
8805
- Region: app.region,
8806
- Profile: app.profile
8807
- };
8808
- if (opt.stage) {
8809
- data.Stage = color.warning(opt.stage);
8810
- }
8811
- note(wrap(list(data)), "App Config");
8812
- };
8813
-
8814
- // src/cli/ui/error/error.ts
8815
- import { AppError as AppError2, StackError as StackError3 } from "@awsless/formation";
8816
- import { log as log6 } from "@clack/prompts";
8819
+ // src/feature/auth/index.ts
8820
+ import { aws as aws3, Node as Node2 } from "@awsless/formation";
8821
+ import { constantCase as constantCase2 } from "change-case";
8817
8822
 
8818
- // src/cli/ui/error/app-error.ts
8819
- import { StackError as StackError2 } from "@awsless/formation";
8820
- import { log as log3 } from "@clack/prompts";
8823
+ // src/feature.ts
8824
+ var defineFeature = (feature) => feature;
8821
8825
 
8822
- // src/cli/ui/error/stack-error.ts
8823
- import { ResourceError } from "@awsless/formation";
8824
- import { log as log2 } from "@clack/prompts";
8825
- import { capitalCase as capitalCase2 } from "change-case";
8826
- var formatOperation = (operation) => {
8827
- const value = ` ${capitalCase2(operation)} `;
8828
- switch (operation) {
8829
- case "create":
8830
- return color.success.bold.inverse(value);
8831
- case "update":
8832
- return color.warning.bold.inverse(value);
8833
- case "delete":
8834
- return color.error.bold.inverse(value);
8835
- case "heal":
8836
- return color.warning.bold.inverse(value);
8837
- case "get":
8838
- return color.info.bold.inverse(value);
8826
+ // src/type-gen/file.ts
8827
+ import { camelCase } from "change-case";
8828
+ var TypeFile = class {
8829
+ constructor(module) {
8830
+ this.module = module;
8839
8831
  }
8840
- };
8841
- var logStackError = (error) => {
8842
- log2.message(
8843
- wrap([color.error(error.message), `Stack: ${error.stack}`].join("\n"), {
8844
- hard: true
8845
- }),
8846
- { symbol: color.error(icon.error) }
8847
- );
8848
- for (const issue of error.issues) {
8849
- if (issue instanceof ResourceError) {
8850
- log2.message(
8851
- [
8852
- formatOperation(issue.operation),
8853
- "\n",
8854
- wrap("URN: " + issue.urn, { hard: true }),
8855
- "\n",
8856
- wrap("ID: " + issue.id, { hard: true }),
8857
- "\n\n",
8858
- wrap(color.error(issue.message), { hard: true })
8859
- // , '\n', color.error(issue.message)
8860
- ].join(""),
8861
- { symbol: color.error(icon.error) }
8832
+ codes = /* @__PURE__ */ new Set();
8833
+ interfaces = /* @__PURE__ */ new Map();
8834
+ imports = /* @__PURE__ */ new Map();
8835
+ addImport(varName, path) {
8836
+ this.imports.set(varName, path);
8837
+ return this;
8838
+ }
8839
+ addCode(code) {
8840
+ this.codes.add(code);
8841
+ return this;
8842
+ }
8843
+ addInterface(name, type) {
8844
+ const value = type.toString();
8845
+ if (value) {
8846
+ this.interfaces.set(name, value);
8847
+ }
8848
+ return this;
8849
+ }
8850
+ toString() {
8851
+ if (this.interfaces.size === 0) {
8852
+ return;
8853
+ }
8854
+ const lines = [];
8855
+ if (this.imports.size > 0) {
8856
+ lines.push(
8857
+ ...[
8858
+ "// Imports",
8859
+ ...Array.from(this.imports.entries()).map(([varName, path]) => {
8860
+ if (typeof varName === "string") {
8861
+ return `import ${camelCase(varName)} from '${path}'`;
8862
+ }
8863
+ return `import { ${Object.entries(varName).map(([key, alias]) => `${key} as ${camelCase(alias)}`).join(", ")} } from '${path}'`;
8864
+ }),
8865
+ ""
8866
+ ]
8862
8867
  );
8863
- } else if (issue instanceof Error) {
8864
- log2.message(wrap(color.error(issue.message), { hard: true }), {
8865
- symbol: color.error(icon.error)
8866
- });
8867
8868
  }
8869
+ if (this.codes.size > 0) {
8870
+ lines.push(...["// Types", ...Array.from(this.codes).map((v) => v.trim()), ""]);
8871
+ }
8872
+ return [
8873
+ ...lines,
8874
+ "// Extend module",
8875
+ `declare module '${this.module}' {`,
8876
+ Array.from(this.interfaces).map(([name, type]) => {
8877
+ return ` interface ${name} ${type}`;
8878
+ }).join("\n\n"),
8879
+ // ...Array.from(this.types.entries()).map(([propName, type]) => { // `\tinterface ${this.interfaceName} {`,
8880
+ // return `\t\t${this.readonly ? 'readonly ' : ''}${propName}: ${type}`
8881
+ // }),
8882
+ // `\t}`,
8883
+ `}`,
8884
+ "",
8885
+ "// Export fix",
8886
+ `export {}`
8887
+ ].join("\n");
8868
8888
  }
8869
8889
  };
8870
8890
 
8871
- // src/cli/ui/error/app-error.ts
8872
- var logAppError = (error) => {
8873
- log3.message(
8874
- wrap([color.error(error.message), `App: ${error.app}`].join("\n"), {
8875
- hard: true
8876
- }),
8877
- { symbol: color.error(icon.error) }
8878
- );
8879
- for (const issue of error.issues) {
8880
- if (issue instanceof StackError2) {
8881
- logStackError(issue);
8882
- } else if (issue instanceof Error) {
8883
- log3.message(wrap(color.error(issue.message), { hard: true }), {
8884
- symbol: color.error(icon.error)
8885
- });
8891
+ // src/type-gen/object.ts
8892
+ import { camelCase as camelCase2, constantCase } from "change-case";
8893
+ var TypeObject = class {
8894
+ constructor(level, readonly = true) {
8895
+ this.level = level;
8896
+ this.readonly = readonly;
8897
+ }
8898
+ types = /* @__PURE__ */ new Map();
8899
+ add(name, type) {
8900
+ const value = type.toString();
8901
+ if (value) {
8902
+ this.types.set(name, value);
8903
+ }
8904
+ return this;
8905
+ }
8906
+ addType(name, type) {
8907
+ return this.add(camelCase2(name), type);
8908
+ }
8909
+ addConst(name, type) {
8910
+ return this.add(constantCase(name), type);
8911
+ }
8912
+ toString() {
8913
+ if (!this.types.size) {
8914
+ return "";
8886
8915
  }
8916
+ return [
8917
+ "{",
8918
+ ...Array.from(this.types.entries()).map(([propName, type]) => {
8919
+ return [
8920
+ " ".repeat(this.level + 1),
8921
+ this.readonly ? "readonly" : "",
8922
+ " ",
8923
+ propName,
8924
+ ": ",
8925
+ type
8926
+ ].join("");
8927
+ }),
8928
+ `${" ".repeat(this.level)}}`
8929
+ ].join("\n");
8887
8930
  }
8888
8931
  };
8889
8932
 
8890
- // src/cli/ui/error/config-error.ts
8891
- import * as p from "@clack/prompts";
8892
- var codeLine = (value, level = 0) => {
8933
+ // src/util/name.ts
8934
+ import { paramCase as paramCase3 } from "change-case";
8935
+ import { createHmac } from "crypto";
8936
+ var formatGlobalResourceName = (opt) => {
8893
8937
  return [
8894
8938
  //
8895
- " ".repeat(level),
8896
- value
8897
- ].join("");
8939
+ opt.prefix,
8940
+ opt.appName,
8941
+ opt.resourceType,
8942
+ opt.resourceName,
8943
+ opt.postfix
8944
+ ].filter((v) => typeof v === "string").map((v) => paramCase3(v)).join(opt.seperator ?? "--");
8898
8945
  };
8899
- var format = (value) => {
8900
- if (Array.isArray(value)) {
8901
- return "[ ... ]";
8902
- }
8903
- if (value === null) {
8904
- return "null";
8905
- }
8906
- switch (typeof value) {
8907
- case "function":
8908
- return "() => { ... }";
8909
- case "bigint":
8910
- return `${value}n`;
8911
- case "symbol":
8912
- return "Symbol()";
8913
- case "object":
8914
- return "{ ... }";
8915
- case "undefined":
8916
- return "undefined";
8917
- case "string":
8918
- case "number":
8919
- case "boolean":
8920
- return JSON.stringify(value);
8921
- }
8922
- return "";
8923
- };
8924
- var logConfigError = (error) => {
8925
- for (const issue of error.error.errors) {
8926
- const message = [color.error(issue.message), color.dim(error.file), "\n{"];
8927
- let context = error.data;
8928
- const inStack = issue.path[0] === "stacks" && typeof issue.path[1] === "number";
8929
- const length = issue.path.length;
8930
- const end = ["}"];
8931
- issue.path.forEach((path, i) => {
8932
- const index = i + 1;
8933
- context = context[path];
8934
- if (typeof path === "string") {
8935
- const key = path + `: `;
8936
- if (index === length) {
8937
- const space = " ".repeat(key.length);
8938
- const value = format(context);
8939
- const error2 = icon.arrow.top.repeat(value.length);
8940
- message.push(codeLine(key + color.warning(value), index));
8941
- message.push(codeLine(space + color.error(error2), index));
8942
- } else if (Array.isArray(context)) {
8943
- message.push(codeLine(key + "[", index));
8944
- end.unshift(codeLine("]", index));
8945
- } else if (typeof context === "object") {
8946
- if (inStack && index === 3) {
8947
- const name = error.data.stacks[issue.path[1]].name;
8948
- message.push(codeLine("name: " + color.info(`"${name}"`) + ",", index));
8949
- }
8950
- message.push(codeLine(key + "{", index));
8951
- end.unshift(codeLine("}", index));
8952
- }
8953
- } else if (typeof context === "object") {
8954
- message.push(codeLine("{", index));
8955
- end.unshift(codeLine("}", index));
8956
- } else if (typeof context === "string") {
8957
- message.push(codeLine(color.warning(`"${context}"`), index));
8958
- const error2 = icon.arrow.top.repeat(context.length + 2);
8959
- message.push(codeLine(color.error(error2), index));
8960
- }
8961
- });
8962
- p.log.message(
8963
- wrap([...message, ...end], {
8964
- trim: false
8965
- }),
8966
- {
8967
- symbol: color.error`×`
8968
- }
8969
- );
8970
- }
8971
- };
8972
-
8973
- // src/cli/ui/error/file-error.ts
8974
- import { log as log5 } from "@clack/prompts";
8975
- var logFileError = (error) => {
8976
- log5.message(
8977
- wrap([color.error(error.message), color.dim(error.file)].join("\n"), {
8978
- hard: true
8979
- }),
8980
- { symbol: color.error(icon.error) }
8981
- );
8982
- };
8983
-
8984
- // src/cli/ui/error/error.ts
8985
- var logError = (error) => {
8986
- if (error instanceof ConfigError) {
8987
- logConfigError(error);
8988
- } else if (error instanceof Cancelled) {
8989
- log6.message(color.error("Cancelled."), {
8990
- symbol: color.error(icon.error)
8991
- });
8992
- } else if (error instanceof ExpectedError) {
8993
- log6.message(color.error(error.message), {
8994
- symbol: color.error(icon.error)
8995
- });
8996
- } else if (error instanceof AppError2) {
8997
- logAppError(error);
8998
- } else if (error instanceof StackError3) {
8999
- logStackError(error);
9000
- } else if (error instanceof FileError) {
9001
- logFileError(error);
9002
- } else if (error instanceof Error) {
9003
- const message = `${error.name}: ${error.message}`;
9004
- const stack = error.stack ? color.dim(error.stack.replace(message, "")) : "";
9005
- log6.message(
9006
- wrap([color.error(message), stack], {
9007
- hard: true
9008
- }),
9009
- { symbol: color.error(icon.error) }
9010
- );
9011
- } else if (typeof error === "string") {
9012
- log6.message(wrap(color.error(error)), {
9013
- symbol: color.error(icon.error)
9014
- });
9015
- } else {
9016
- log6.message(wrap(color.error("Unknown error!")), {
9017
- symbol: color.error(icon.error)
9018
- });
9019
- }
9020
- };
9021
-
9022
- // src/cli/ui/logo.ts
9023
- var logo = () => {
9024
- return `${color.primary`AWS`}${color.primary.dim`LESS`}`;
9025
- };
9026
-
9027
- // src/cli/ui/complex/layout.ts
9028
- var layout = async (command, cb) => {
9029
- console.log();
9030
- intro(`${logo()} ${color.dim(command)}`);
9031
- try {
9032
- const options = program.optsWithGlobals();
9033
- const appConfig = await loadAppConfig(options);
9034
- logApp(appConfig, options);
9035
- const stackConfigs = await loadStackConfigs(options);
9036
- const result = await cb({
9037
- options,
9038
- appConfig,
9039
- stackConfigs
9040
- });
9041
- outro(result ?? void 0);
9042
- } catch (error) {
9043
- logError(error);
9044
- outro();
9045
- process.exit(1);
9046
- }
9047
- };
9048
-
9049
- // src/cli/command/bootstrap.ts
9050
- var bootstrap = (program2) => {
9051
- program2.command("bootstrap").description("Create the awsless bootstrap stack").action(async () => {
9052
- await layout("bootstrap", async ({ appConfig }) => {
9053
- const credentials = getCredentials(appConfig.profile);
9054
- const accountId = await getAccountId(credentials, appConfig.region);
9055
- await bootstrapAwsless({
9056
- credentials,
9057
- region: appConfig.region,
9058
- accountId
9059
- });
9060
- return "Ready to go!";
9061
- });
9062
- });
9063
- };
9064
-
9065
- // src/app.ts
9066
- import { App as App2, Stack } from "@awsless/formation";
9067
-
9068
- // src/feature/auth/index.ts
9069
- import { aws as aws3, Node as Node2 } from "@awsless/formation";
9070
- import { constantCase as constantCase2 } from "change-case";
9071
-
9072
- // src/feature.ts
9073
- var defineFeature = (feature) => feature;
9074
-
9075
- // src/type-gen/file.ts
9076
- import { camelCase } from "change-case";
9077
- var TypeFile = class {
9078
- constructor(module) {
9079
- this.module = module;
9080
- }
9081
- codes = /* @__PURE__ */ new Set();
9082
- interfaces = /* @__PURE__ */ new Map();
9083
- imports = /* @__PURE__ */ new Map();
9084
- addImport(varName, path) {
9085
- this.imports.set(varName, path);
9086
- return this;
9087
- }
9088
- addCode(code) {
9089
- this.codes.add(code);
9090
- return this;
9091
- }
9092
- addInterface(name, type) {
9093
- const value = type.toString();
9094
- if (value) {
9095
- this.interfaces.set(name, value);
9096
- }
9097
- return this;
9098
- }
9099
- toString() {
9100
- if (this.interfaces.size === 0) {
9101
- return;
9102
- }
9103
- const lines = [];
9104
- if (this.imports.size > 0) {
9105
- lines.push(
9106
- ...[
9107
- "// Imports",
9108
- ...Array.from(this.imports.entries()).map(([varName, path]) => {
9109
- if (typeof varName === "string") {
9110
- return `import ${camelCase(varName)} from '${path}'`;
9111
- }
9112
- return `import { ${Object.entries(varName).map(([key, alias]) => `${key} as ${camelCase(alias)}`).join(", ")} } from '${path}'`;
9113
- }),
9114
- ""
9115
- ]
9116
- );
9117
- }
9118
- if (this.codes.size > 0) {
9119
- lines.push(...["// Types", ...Array.from(this.codes).map((v) => v.trim()), ""]);
9120
- }
9121
- return [
9122
- ...lines,
9123
- "// Extend module",
9124
- `declare module '${this.module}' {`,
9125
- Array.from(this.interfaces).map(([name, type]) => {
9126
- return ` interface ${name} ${type}`;
9127
- }).join("\n\n"),
9128
- // ...Array.from(this.types.entries()).map(([propName, type]) => { // `\tinterface ${this.interfaceName} {`,
9129
- // return `\t\t${this.readonly ? 'readonly ' : ''}${propName}: ${type}`
9130
- // }),
9131
- // `\t}`,
9132
- `}`,
9133
- "",
9134
- "// Export fix",
9135
- `export {}`
9136
- ].join("\n");
9137
- }
9138
- };
9139
-
9140
- // src/type-gen/object.ts
9141
- import { camelCase as camelCase2, constantCase } from "change-case";
9142
- var TypeObject = class {
9143
- constructor(level, readonly = true) {
9144
- this.level = level;
9145
- this.readonly = readonly;
9146
- }
9147
- types = /* @__PURE__ */ new Map();
9148
- add(name, type) {
9149
- const value = type.toString();
9150
- if (value) {
9151
- this.types.set(name, value);
9152
- }
9153
- return this;
9154
- }
9155
- addType(name, type) {
9156
- return this.add(camelCase2(name), type);
9157
- }
9158
- addConst(name, type) {
9159
- return this.add(constantCase(name), type);
9160
- }
9161
- toString() {
9162
- if (!this.types.size) {
9163
- return "";
9164
- }
9165
- return [
9166
- "{",
9167
- ...Array.from(this.types.entries()).map(([propName, type]) => {
9168
- return [
9169
- " ".repeat(this.level + 1),
9170
- this.readonly ? "readonly" : "",
9171
- " ",
9172
- propName,
9173
- ": ",
9174
- type
9175
- ].join("");
9176
- }),
9177
- `${" ".repeat(this.level)}}`
9178
- ].join("\n");
9179
- }
9180
- };
9181
-
9182
- // src/util/name.ts
9183
- import { paramCase as paramCase3 } from "change-case";
9184
- import { createHmac } from "crypto";
9185
- var formatGlobalResourceName = (opt) => {
9186
- return [
9187
- //
9188
- opt.prefix,
9189
- opt.appName,
9190
- opt.resourceType,
9191
- opt.resourceName,
9192
- opt.postfix
9193
- ].filter((v) => typeof v === "string").map((v) => paramCase3(v)).join(opt.seperator ?? "--");
9194
- };
9195
- var formatLocalResourceName = (opt) => {
9196
- return [
9197
- //
9198
- opt.prefix,
9199
- opt.appName,
9200
- opt.stackName,
9201
- opt.resourceType,
9202
- opt.resourceName,
9203
- opt.postfix
9204
- ].filter((v) => typeof v === "string").map((v) => paramCase3(v)).join(opt.seperator ?? "--");
8946
+ var formatLocalResourceName = (opt) => {
8947
+ return [
8948
+ //
8949
+ opt.prefix,
8950
+ opt.appName,
8951
+ opt.stackName,
8952
+ opt.resourceType,
8953
+ opt.resourceName,
8954
+ opt.postfix
8955
+ ].filter((v) => typeof v === "string").map((v) => paramCase3(v)).join(opt.seperator ?? "--");
9205
8956
  };
9206
8957
  var generateGlobalAppId = (opt) => {
9207
8958
  return createHmac("sha1", "awsless").update(opt.accountId).update(opt.region).update(opt.appName).digest("hex").substring(0, 8);
@@ -10929,6 +10680,14 @@ var createLambdaFunction = (group, ctx, ns, id, local2) => {
10929
10680
  resources: [logGroup.arn.apply((arn) => `${arn}:*`)]
10930
10681
  }
10931
10682
  );
10683
+ const logSubscriptionArn = ctx.shared.get("log-subscription-destination-arn");
10684
+ if (logSubscriptionArn) {
10685
+ new aws2.cloudWatch.SubscriptionFilter(group, `log-subscription`, {
10686
+ destinationArn: logSubscriptionArn,
10687
+ logGroupName: logGroup.name,
10688
+ filterPattern: "{$.level = ERROR}"
10689
+ });
10690
+ }
10932
10691
  }
10933
10692
  if (ctx.appConfig.defaults.function.permissions) {
10934
10693
  policy.addStatement(...ctx.appConfig.defaults.function.permissions);
@@ -11196,14 +10955,20 @@ var cacheFeature = defineFeature({
11196
10955
  // src/feature/command/index.ts
11197
10956
  var commandFeature = defineFeature({
11198
10957
  name: "command",
11199
- onStack(ctx) {
10958
+ onValidate(ctx) {
11200
10959
  const names = /* @__PURE__ */ new Set();
11201
- for (const [name, props] of Object.entries(ctx.stackConfig.commands ?? {})) {
11202
- if (!names.has(name)) {
11203
- names.add(name);
11204
- } else {
11205
- throw new FileError(ctx.stackConfig.file, `Duplicate command names aren't allowed: ${name}`);
10960
+ for (const stack of ctx.stackConfigs) {
10961
+ for (const name of Object.keys(stack.commands ?? {})) {
10962
+ if (!names.has(name)) {
10963
+ names.add(name);
10964
+ } else {
10965
+ throw new FileError(stack.file, `Duplicate command names aren't allowed: ${name}`);
10966
+ }
11206
10967
  }
10968
+ }
10969
+ },
10970
+ onStack(ctx) {
10971
+ for (const [name, props] of Object.entries(ctx.stackConfig.commands ?? {})) {
11207
10972
  ctx.registerCommand({ name, ...props });
11208
10973
  }
11209
10974
  }
@@ -12302,8 +12067,75 @@ var instanceFeature = defineFeature({
12302
12067
  }
12303
12068
  });
12304
12069
 
12305
- // src/feature/on-failure/index.ts
12070
+ // src/feature/log-subscription/index.ts
12306
12071
  import { Node as Node10, aws as aws11 } from "@awsless/formation";
12072
+ var logSubscriptionFeature = defineFeature({
12073
+ name: "log-subscription",
12074
+ onApp(ctx) {
12075
+ if (!ctx.appConfig.logSubscription) {
12076
+ return;
12077
+ }
12078
+ const group = new Node10(ctx.base, "log-subscription", "main");
12079
+ const { lambda, policy } = createLambdaFunction(
12080
+ group,
12081
+ ctx,
12082
+ "log-subscription",
12083
+ "main",
12084
+ ctx.appConfig.logSubscription
12085
+ );
12086
+ new aws11.lambda.Permission(group, "log-subscription-permission", {
12087
+ action: "lambda:InvokeFunction",
12088
+ principal: "logs.amazonaws.com",
12089
+ functionArn: lambda.arn,
12090
+ sourceArn: `arn:aws:logs:${ctx.appConfig.region}:${ctx.accountId}:log-group:/aws/lambda/app-kennedy--*`
12091
+ });
12092
+ ctx.shared.set("log-subscription-destination-arn", lambda.arn);
12093
+ for (const stack of ctx.stackConfigs) {
12094
+ for (const id of stack.topics ?? []) {
12095
+ policy.addStatement({
12096
+ actions: ["sns:Publish"],
12097
+ resources: [ctx.shared.get(`topic-${id}-arn`)]
12098
+ });
12099
+ }
12100
+ for (const [id, props] of Object.entries(stack.tables ?? {})) {
12101
+ const tableName = formatLocalResourceName({
12102
+ appName: ctx.app.name,
12103
+ stackName: stack.name,
12104
+ resourceType: "table",
12105
+ resourceName: id
12106
+ });
12107
+ policy.addStatement({
12108
+ actions: [
12109
+ "dynamodb:DescribeTable",
12110
+ "dynamodb:PutItem",
12111
+ "dynamodb:GetItem",
12112
+ "dynamodb:UpdateItem",
12113
+ "dynamodb:DeleteItem",
12114
+ "dynamodb:TransactWrite",
12115
+ "dynamodb:BatchWriteItem",
12116
+ "dynamodb:BatchGetItem",
12117
+ "dynamodb:ConditionCheckItem",
12118
+ "dynamodb:Query",
12119
+ "dynamodb:Scan"
12120
+ ],
12121
+ resources: [`arn:aws:dynamodb:${ctx.appConfig.region}:${ctx.accountId}:table/${tableName}`]
12122
+ });
12123
+ const indexes = Object.keys(props.indexes ?? {});
12124
+ if (indexes.length) {
12125
+ policy.addStatement({
12126
+ actions: ["dynamodb:Query"],
12127
+ resources: indexes.map(
12128
+ (indexName) => `arn:aws:dynamodb:${ctx.appConfig.region}:${ctx.accountId}:table/${tableName}/index/${indexName}`
12129
+ )
12130
+ });
12131
+ }
12132
+ }
12133
+ }
12134
+ }
12135
+ });
12136
+
12137
+ // src/feature/on-failure/index.ts
12138
+ import { Node as Node11, aws as aws12 } from "@awsless/formation";
12307
12139
  var onFailureFeature = defineFeature({
12308
12140
  name: "on-failure",
12309
12141
  onApp(ctx) {
@@ -12314,7 +12146,7 @@ var onFailureFeature = defineFeature({
12314
12146
  if (count > 1) {
12315
12147
  throw new TypeError("Only 1 onFailure configuration is allowed in your app.");
12316
12148
  }
12317
- const queue2 = new aws11.sqs.Queue(ctx.base, "on-failure", {
12149
+ const queue2 = new aws12.sqs.Queue(ctx.base, "on-failure", {
12318
12150
  name: formatGlobalResourceName({
12319
12151
  appName: ctx.app.name,
12320
12152
  resourceType: "on-failure",
@@ -12329,9 +12161,9 @@ var onFailureFeature = defineFeature({
12329
12161
  return;
12330
12162
  }
12331
12163
  const queueArn = ctx.shared.get("on-failure-queue-arn");
12332
- const group = new Node10(ctx.stack, "on-failure", "failure");
12164
+ const group = new Node11(ctx.stack, "on-failure", "failure");
12333
12165
  const { lambda, policy } = createLambdaFunction(group, ctx, "on-failure", "failure", onFailure);
12334
- const source = new aws11.lambda.EventSourceMapping(group, "on-failure", {
12166
+ const source = new aws12.lambda.EventSourceMapping(group, "on-failure", {
12335
12167
  functionArn: lambda.arn,
12336
12168
  sourceArn: queueArn,
12337
12169
  batchSize: 10
@@ -12351,13 +12183,13 @@ var onFailureFeature = defineFeature({
12351
12183
  });
12352
12184
 
12353
12185
  // src/feature/pubsub/index.ts
12354
- import { aws as aws12, Node as Node11 } from "@awsless/formation";
12186
+ import { aws as aws13, Node as Node12 } from "@awsless/formation";
12355
12187
  import { constantCase as constantCase6 } from "change-case";
12356
12188
  var pubsubFeature = defineFeature({
12357
12189
  name: "pubsub",
12358
12190
  onApp(ctx) {
12359
12191
  for (const [id, props] of Object.entries(ctx.appConfig.defaults.pubsub ?? {})) {
12360
- const group = new Node11(ctx.base, "pubsub", id);
12192
+ const group = new Node12(ctx.base, "pubsub", id);
12361
12193
  const functionProps = typeof props.auth === "string" ? { file: "" } : props.auth.authorizer;
12362
12194
  const { lambda } = createLambdaFunction(group, ctx, "pubsub-authorizer", id, functionProps);
12363
12195
  lambda.addEnvironment("PUBSUB_POLICY", JSON.stringify(props.policy));
@@ -12367,23 +12199,23 @@ var pubsubFeature = defineFeature({
12367
12199
  resourceType: "pubsub",
12368
12200
  resourceName: id
12369
12201
  });
12370
- const authorizer = new aws12.iot.Authorizer(group, "authorizer", {
12202
+ const authorizer = new aws13.iot.Authorizer(group, "authorizer", {
12371
12203
  name,
12372
12204
  functionArn: lambda.arn
12373
12205
  });
12374
- new aws12.lambda.Permission(group, "permission", {
12206
+ new aws13.lambda.Permission(group, "permission", {
12375
12207
  functionArn: lambda.arn,
12376
12208
  principal: "iot.amazonaws.com",
12377
12209
  sourceArn: authorizer.arn,
12378
12210
  action: "lambda:InvokeFunction"
12379
12211
  });
12380
12212
  ctx.bind(`PUBSUB_${constantCase6(id)}_AUTHORIZER`, name);
12381
- const endpoint = new aws12.iot.Endpoint(group, "endpoint", {
12213
+ const endpoint = new aws13.iot.Endpoint(group, "endpoint", {
12382
12214
  type: "data-ats"
12383
12215
  });
12384
12216
  if (props.domain) {
12385
12217
  const domainName = formatFullDomainName(ctx.appConfig, props.domain, props.subDomain);
12386
- new aws12.iot.DomainConfiguration(group, "domain", {
12218
+ new aws13.iot.DomainConfiguration(group, "domain", {
12387
12219
  name,
12388
12220
  domainName,
12389
12221
  certificates: [ctx.shared.get(`local-certificate-${props.domain}-arn`)],
@@ -12392,7 +12224,7 @@ var pubsubFeature = defineFeature({
12392
12224
  }
12393
12225
  // validationCertificate: ctx.shared.get(`global-certificate-${props.domain}-arn`),
12394
12226
  });
12395
- new aws12.route53.RecordSet(group, "record", {
12227
+ new aws13.route53.RecordSet(group, "record", {
12396
12228
  hostedZoneId: ctx.shared.get(`hosted-zone-${props.domain}-id`),
12397
12229
  name: domainName,
12398
12230
  type: "CNAME",
@@ -12413,7 +12245,7 @@ var pubsubFeature = defineFeature({
12413
12245
  },
12414
12246
  onStack(ctx) {
12415
12247
  for (const [id, props] of Object.entries(ctx.stackConfig.pubsub ?? {})) {
12416
- const group = new Node11(ctx.stack, "pubsub", id);
12248
+ const group = new Node12(ctx.stack, "pubsub", id);
12417
12249
  const { lambda } = createAsyncLambdaFunction(group, ctx, `pubsub`, id, props.consumer);
12418
12250
  const name = formatLocalResourceName({
12419
12251
  appName: ctx.app.name,
@@ -12421,13 +12253,13 @@ var pubsubFeature = defineFeature({
12421
12253
  resourceType: "pubsub",
12422
12254
  resourceName: id
12423
12255
  });
12424
- const topic = new aws12.iot.TopicRule(group, "rule", {
12256
+ const topic = new aws13.iot.TopicRule(group, "rule", {
12425
12257
  name: name.replaceAll("-", "_"),
12426
12258
  sql: props.sql,
12427
12259
  sqlVersion: props.sqlVersion,
12428
12260
  actions: [{ lambda: { functionArn: lambda.arn } }]
12429
12261
  });
12430
- new aws12.lambda.Permission(group, "permission", {
12262
+ new aws13.lambda.Permission(group, "permission", {
12431
12263
  action: "lambda:InvokeFunction",
12432
12264
  principal: "iot.amazonaws.com",
12433
12265
  functionArn: lambda.arn,
@@ -12438,7 +12270,7 @@ var pubsubFeature = defineFeature({
12438
12270
  });
12439
12271
 
12440
12272
  // src/feature/queue/index.ts
12441
- import { aws as aws13, Node as Node12 } from "@awsless/formation";
12273
+ import { aws as aws14, Node as Node13 } from "@awsless/formation";
12442
12274
  import { camelCase as camelCase5, constantCase as constantCase7 } from "change-case";
12443
12275
  import deepmerge3 from "deepmerge";
12444
12276
  import { relative as relative4 } from "path";
@@ -12498,21 +12330,21 @@ var queueFeature = defineFeature({
12498
12330
  onStack(ctx) {
12499
12331
  for (const [id, local2] of Object.entries(ctx.stackConfig.queues || {})) {
12500
12332
  const props = deepmerge3(ctx.appConfig.defaults.queue, local2);
12501
- const group = new Node12(ctx.stack, "queue", id);
12333
+ const group = new Node13(ctx.stack, "queue", id);
12502
12334
  const name = formatLocalResourceName({
12503
12335
  appName: ctx.app.name,
12504
12336
  stackName: ctx.stack.name,
12505
12337
  resourceType: "queue",
12506
12338
  resourceName: id
12507
12339
  });
12508
- const queue2 = new aws13.sqs.Queue(group, "queue", {
12340
+ const queue2 = new aws14.sqs.Queue(group, "queue", {
12509
12341
  name,
12510
12342
  deadLetterArn: getGlobalOnFailure(ctx),
12511
12343
  ...props
12512
12344
  });
12513
12345
  const { lambda, policy } = createLambdaFunction(group, ctx, `queue`, id, props.consumer);
12514
12346
  lambda.addEnvironment("LOG_VIEWABLE_ERROR", "1");
12515
- new aws13.lambda.EventSourceMapping(group, "event", {
12347
+ new aws14.lambda.EventSourceMapping(group, "event", {
12516
12348
  functionArn: lambda.arn,
12517
12349
  sourceArn: queue2.arn,
12518
12350
  batchSize: props.batchSize,
@@ -12532,23 +12364,23 @@ var queueFeature = defineFeature({
12532
12364
  });
12533
12365
 
12534
12366
  // src/feature/rest/index.ts
12535
- import { aws as aws14, Node as Node13 } from "@awsless/formation";
12367
+ import { aws as aws15, Node as Node14 } from "@awsless/formation";
12536
12368
  import { constantCase as constantCase8 } from "change-case";
12537
12369
  var restFeature = defineFeature({
12538
12370
  name: "rest",
12539
12371
  onApp(ctx) {
12540
12372
  for (const [id, props] of Object.entries(ctx.appConfig.defaults?.rest ?? {})) {
12541
- const group = new Node13(ctx.base, "rest", id);
12373
+ const group = new Node14(ctx.base, "rest", id);
12542
12374
  const name = formatGlobalResourceName({
12543
12375
  appName: ctx.app.name,
12544
12376
  resourceType: "rest",
12545
12377
  resourceName: id
12546
12378
  });
12547
- const api = new aws14.apiGatewayV2.Api(group, "api", {
12379
+ const api = new aws15.apiGatewayV2.Api(group, "api", {
12548
12380
  name,
12549
12381
  protocolType: "HTTP"
12550
12382
  });
12551
- const stage = new aws14.apiGatewayV2.Stage(group, "stage", {
12383
+ const stage = new aws15.apiGatewayV2.Stage(group, "stage", {
12552
12384
  name: "v1",
12553
12385
  apiId: api.id
12554
12386
  });
@@ -12557,7 +12389,7 @@ var restFeature = defineFeature({
12557
12389
  const domainName = formatFullDomainName(ctx.appConfig, props.domain, props.subDomain);
12558
12390
  const hostedZoneId = ctx.shared.get(`hosted-zone-${props.domain}-id`);
12559
12391
  const certificateArn = ctx.shared.get(`certificate-${props.domain}-arn`);
12560
- const domain = new aws14.apiGatewayV2.DomainName(group, "domain", {
12392
+ const domain = new aws15.apiGatewayV2.DomainName(group, "domain", {
12561
12393
  name: domainName,
12562
12394
  certificates: [
12563
12395
  {
@@ -12565,12 +12397,12 @@ var restFeature = defineFeature({
12565
12397
  }
12566
12398
  ]
12567
12399
  });
12568
- const mapping = new aws14.apiGatewayV2.ApiMapping(group, "mapping", {
12400
+ const mapping = new aws15.apiGatewayV2.ApiMapping(group, "mapping", {
12569
12401
  apiId: api.id,
12570
12402
  domainName: domain.name,
12571
12403
  stage: stage.name
12572
12404
  });
12573
- const record = new aws14.route53.RecordSet(group, "record", {
12405
+ const record = new aws15.route53.RecordSet(group, "record", {
12574
12406
  hostedZoneId,
12575
12407
  type: "A",
12576
12408
  name: domainName,
@@ -12588,21 +12420,21 @@ var restFeature = defineFeature({
12588
12420
  },
12589
12421
  onStack(ctx) {
12590
12422
  for (const [id, routes] of Object.entries(ctx.stackConfig.rest ?? {})) {
12591
- const restGroup = new Node13(ctx.stack, "rest", id);
12423
+ const restGroup = new Node14(ctx.stack, "rest", id);
12592
12424
  for (const [routeKey, props] of Object.entries(routes)) {
12593
- const group = new Node13(restGroup, "route", routeKey);
12425
+ const group = new Node14(restGroup, "route", routeKey);
12594
12426
  const apiId = ctx.shared.get(`rest-${id}-id`);
12595
12427
  const routeId = shortId(routeKey);
12596
12428
  const { lambda } = createLambdaFunction(group, ctx, "rest", `${id}-${routeId}`, {
12597
12429
  ...props,
12598
12430
  description: `${id} ${routeKey}`
12599
12431
  });
12600
- const permission = new aws14.lambda.Permission(group, "permission", {
12432
+ const permission = new aws15.lambda.Permission(group, "permission", {
12601
12433
  action: "lambda:InvokeFunction",
12602
12434
  principal: "apigateway.amazonaws.com",
12603
12435
  functionArn: lambda.arn
12604
12436
  });
12605
- const integration = new aws14.apiGatewayV2.Integration(group, "integration", {
12437
+ const integration = new aws15.apiGatewayV2.Integration(group, "integration", {
12606
12438
  apiId,
12607
12439
  description: `${id} ${routeKey}`,
12608
12440
  method: "POST",
@@ -12612,7 +12444,7 @@ var restFeature = defineFeature({
12612
12444
  return `arn:aws:apigateway:${ctx.appConfig.region}:lambda:path/2015-03-31/functions/${arn}/invocations`;
12613
12445
  })
12614
12446
  });
12615
- const route = new aws14.apiGatewayV2.Route(group, "route", {
12447
+ const route = new aws15.apiGatewayV2.Route(group, "route", {
12616
12448
  apiId,
12617
12449
  routeKey,
12618
12450
  target: integration.id.apply((id2) => `integrations/${id2}`)
@@ -12625,13 +12457,13 @@ var restFeature = defineFeature({
12625
12457
 
12626
12458
  // src/feature/rpc/index.ts
12627
12459
  import { camelCase as camelCase6, constantCase as constantCase9, paramCase as paramCase6 } from "change-case";
12628
- import { aws as aws16, Node as Node15, Output as Output3 } from "@awsless/formation";
12460
+ import { aws as aws17, Node as Node16, Output as Output3 } from "@awsless/formation";
12629
12461
  import { mebibytes as mebibytes2 } from "@awsless/size";
12630
12462
  import { dirname as dirname10, join as join8, relative as relative5 } from "path";
12631
12463
  import { fileURLToPath } from "node:url";
12632
12464
 
12633
12465
  // src/feature/function/prebuild.ts
12634
- import { Asset as Asset4, aws as aws15 } from "@awsless/formation";
12466
+ import { Asset as Asset4, aws as aws16 } from "@awsless/formation";
12635
12467
  var createPrebuildLambdaFunction = (group, ctx, ns, id, local2) => {
12636
12468
  let name;
12637
12469
  if ("stack" in ctx) {
@@ -12653,21 +12485,21 @@ var createPrebuildLambdaFunction = (group, ctx, ns, id, local2) => {
12653
12485
  runtime: "nodejs20.x",
12654
12486
  ...local2
12655
12487
  };
12656
- const code = new aws15.s3.BucketObject(group, "code", {
12488
+ const code = new aws16.s3.BucketObject(group, "code", {
12657
12489
  bucket: ctx.shared.get("function-bucket-name"),
12658
12490
  key: `/lambda/${name}.zip`,
12659
12491
  body: Asset4.fromFile(props.bundleFile)
12660
12492
  });
12661
- const role = new aws15.iam.Role(group, "role", {
12493
+ const role = new aws16.iam.Role(group, "role", {
12662
12494
  name,
12663
12495
  assumedBy: "lambda.amazonaws.com"
12664
12496
  });
12665
- const policy = new aws15.iam.RolePolicy(group, "policy", {
12497
+ const policy = new aws16.iam.RolePolicy(group, "policy", {
12666
12498
  role: role.name,
12667
12499
  name: "lambda-policy",
12668
12500
  version: "2012-10-17"
12669
12501
  });
12670
- const lambda = new aws15.lambda.Function(group, `function`, {
12502
+ const lambda = new aws16.lambda.Function(group, `function`, {
12671
12503
  ...props,
12672
12504
  name,
12673
12505
  role: role.arn,
@@ -12677,7 +12509,7 @@ var createPrebuildLambdaFunction = (group, ctx, ns, id, local2) => {
12677
12509
  vpc: void 0,
12678
12510
  log: props.log
12679
12511
  });
12680
- new aws15.lambda.SourceCodeUpdate(group, "update", {
12512
+ new aws16.lambda.SourceCodeUpdate(group, "update", {
12681
12513
  functionName: lambda.name,
12682
12514
  version: Asset4.fromFile(props.bundleHash),
12683
12515
  architecture: props.architecture,
@@ -12693,7 +12525,7 @@ var createPrebuildLambdaFunction = (group, ctx, ns, id, local2) => {
12693
12525
  lambda.addEnvironment("STACK", ctx.stackConfig.name);
12694
12526
  }
12695
12527
  if (props.log?.retention && props.log?.retention?.value > 0n) {
12696
- const logGroup = new aws15.cloudWatch.LogGroup(group, "log", {
12528
+ const logGroup = new aws16.cloudWatch.LogGroup(group, "log", {
12697
12529
  name: lambda.name.apply((name2) => `/aws/lambda/${name2}`),
12698
12530
  retention: props.log.retention
12699
12531
  });
@@ -12712,7 +12544,7 @@ var createPrebuildLambdaFunction = (group, ctx, ns, id, local2) => {
12712
12544
  policy.addStatement(...local2.permissions);
12713
12545
  }
12714
12546
  if (props.warm) {
12715
- const rule = new aws15.events.Rule(group, "warm", {
12547
+ const rule = new aws16.events.Rule(group, "warm", {
12716
12548
  name: `${name}--warm`,
12717
12549
  schedule: "rate(5 minutes)",
12718
12550
  enabled: true,
@@ -12727,7 +12559,7 @@ var createPrebuildLambdaFunction = (group, ctx, ns, id, local2) => {
12727
12559
  }
12728
12560
  ]
12729
12561
  });
12730
- new aws15.lambda.Permission(group, `warm`, {
12562
+ new aws16.lambda.Permission(group, `warm`, {
12731
12563
  action: "lambda:InvokeFunction",
12732
12564
  principal: "events.amazonaws.com",
12733
12565
  functionArn: lambda.arn,
@@ -12742,7 +12574,7 @@ var createPrebuildLambdaFunction = (group, ctx, ns, id, local2) => {
12742
12574
  ctx.shared.get(`vpc-public-subnet-id-2`)
12743
12575
  ]
12744
12576
  });
12745
- const vpcPolicy = new aws15.iam.RolePolicy(group, "vpc-policy", {
12577
+ const vpcPolicy = new aws16.iam.RolePolicy(group, "vpc-policy", {
12746
12578
  role: role.name,
12747
12579
  name: "lambda-vpc-policy",
12748
12580
  version: "2012-10-17",
@@ -12776,6 +12608,9 @@ var rpcFeature = defineFeature({
12776
12608
  name: "rpc",
12777
12609
  async onTypeGen(ctx) {
12778
12610
  const types2 = new TypeFile("@awsless/awsless/client");
12611
+ types2.addCode(`type Input<T> = Parameters<T>[0]`);
12612
+ types2.addCode(`type Output<T> = Promise<ReturnType<T>>`);
12613
+ types2.addCode(`type Handle<T> = (input:Input<T>) => Output<T>`);
12779
12614
  const schemas = new TypeObject(1);
12780
12615
  for (const id of Object.keys(ctx.appConfig.defaults.rpc ?? {})) {
12781
12616
  const schema = new TypeObject(2);
@@ -12784,7 +12619,7 @@ var rpcFeature = defineFeature({
12784
12619
  const relFile = relative5(directories.types, props.file);
12785
12620
  const varName = camelCase6(`${stack.name}-${name}`);
12786
12621
  types2.addImport(varName, relFile);
12787
- schema.addType(name, `Infer<typeof ${varName}>`);
12622
+ schema.addType(name, `Handle<typeof ${varName}>`);
12788
12623
  }
12789
12624
  }
12790
12625
  schemas.addType(id, schema);
@@ -12792,9 +12627,27 @@ var rpcFeature = defineFeature({
12792
12627
  types2.addInterface("RpcSchema", schemas);
12793
12628
  await ctx.write("rpc.d.ts", types2, true);
12794
12629
  },
12630
+ onValidate(ctx) {
12631
+ const names = {};
12632
+ for (const id of Object.keys(ctx.appConfig.defaults.rpc ?? {})) {
12633
+ names[id] = /* @__PURE__ */ new Set();
12634
+ }
12635
+ for (const stack of ctx.stackConfigs) {
12636
+ for (const [id, queries] of Object.entries(stack.rpc ?? {})) {
12637
+ const list4 = names[id];
12638
+ for (const name of Object.keys(queries ?? {})) {
12639
+ if (list4.has(name)) {
12640
+ throw new FileError(stack.file, `Double RPC API function "${id}.${name}"`);
12641
+ } else {
12642
+ list4.add(name);
12643
+ }
12644
+ }
12645
+ }
12646
+ }
12647
+ },
12795
12648
  onApp(ctx) {
12796
12649
  for (const [id, props] of Object.entries(ctx.appConfig.defaults.rpc ?? {})) {
12797
- const group = new Node15(ctx.base, "rpc", id);
12650
+ const group = new Node16(ctx.base, "rpc", id);
12798
12651
  const name = formatGlobalResourceName({
12799
12652
  appName: ctx.app.name,
12800
12653
  resourceType: "rpc",
@@ -12803,7 +12656,8 @@ var rpcFeature = defineFeature({
12803
12656
  const { lambda } = createPrebuildLambdaFunction(group, ctx, "rpc", id, {
12804
12657
  bundleFile: join8(__dirname, "/prebuild/rpc/bundle.zip"),
12805
12658
  bundleHash: join8(__dirname, "/prebuild/rpc/HASH"),
12806
- memorySize: mebibytes2(256)
12659
+ memorySize: mebibytes2(256),
12660
+ warm: 3
12807
12661
  });
12808
12662
  const schema = {};
12809
12663
  lambda.addEnvironment(
@@ -12816,40 +12670,49 @@ var rpcFeature = defineFeature({
12816
12670
  );
12817
12671
  ctx.shared.set(`rpc-${id}-schema`, schema);
12818
12672
  if (props.auth) {
12819
- const authGroup = new Node15(group, "auth", "authorizer");
12673
+ const authGroup = new Node16(group, "auth", "authorizer");
12820
12674
  const auth2 = createLambdaFunction(authGroup, ctx, "rpc", `${id}-auth`, props.auth);
12821
12675
  lambda.addEnvironment("AUTH", auth2.lambda.name);
12822
12676
  }
12823
- const permission = new aws16.lambda.Permission(group, "permission", {
12677
+ const permission = new aws17.lambda.Permission(group, "permission", {
12824
12678
  principal: "*",
12825
12679
  action: "lambda:InvokeFunctionUrl",
12826
12680
  functionArn: lambda.arn,
12827
12681
  urlAuthType: "none"
12828
12682
  });
12829
- const url = new aws16.lambda.Url(group, "url", {
12683
+ const url = new aws17.lambda.Url(group, "url-2", {
12830
12684
  targetArn: lambda.arn,
12831
- authType: "none"
12685
+ authType: "none",
12686
+ cors: {
12687
+ allow: {
12688
+ origins: ["*"],
12689
+ methods: ["*"],
12690
+ headers: ["Authentication", "Content-Type"]
12691
+ // credentials: true,
12692
+ }
12693
+ }
12832
12694
  }).dependsOn(permission);
12833
12695
  const domainName = props.domain ? formatFullDomainName(ctx.appConfig, props.domain, props.subDomain) : void 0;
12834
12696
  const certificateArn = props.domain ? ctx.shared.get(`global-certificate-${props.domain}-arn`) : void 0;
12835
- const cache = new aws16.cloudFront.CachePolicy(group, "cache", {
12697
+ const cache = new aws17.cloudFront.CachePolicy(group, "cache", {
12836
12698
  name,
12837
12699
  minTtl: seconds3(1),
12838
12700
  maxTtl: days5(365),
12839
12701
  defaultTtl: days5(1)
12840
12702
  });
12841
- const originRequest = new aws16.cloudFront.OriginRequestPolicy(group, "request", {
12703
+ const originRequest = new aws17.cloudFront.OriginRequestPolicy(group, "request", {
12842
12704
  name,
12843
12705
  header: {
12844
12706
  behavior: "all-except",
12845
- values: ["host", "authentication"]
12707
+ values: ["Host"]
12846
12708
  }
12847
12709
  });
12848
- const cdn = new aws16.cloudFront.Distribution(group, "cdn", {
12710
+ const cdn = new aws17.cloudFront.Distribution(group, "cdn-2", {
12849
12711
  name,
12850
12712
  compress: true,
12851
12713
  certificateArn,
12852
12714
  viewerProtocol: "https-only",
12715
+ allowMethod: ["GET", "HEAD", "OPTIONS", "PUT", "PATCH", "POST", "DELETE"],
12853
12716
  aliases: domainName ? [domainName] : void 0,
12854
12717
  origins: [
12855
12718
  {
@@ -12861,11 +12724,10 @@ var rpcFeature = defineFeature({
12861
12724
  targetOriginId: "default",
12862
12725
  cachePolicyId: cache.id,
12863
12726
  originRequestPolicyId: originRequest.id
12864
- // responseHeadersPolicyId: responseHeaders.id,
12865
12727
  });
12866
12728
  if (props.domain) {
12867
12729
  const fullDomainName = formatFullDomainName(ctx.appConfig, props.domain, props.subDomain);
12868
- new aws16.route53.RecordSet(group, "record", {
12730
+ new aws17.route53.RecordSet(group, "record", {
12869
12731
  hostedZoneId: ctx.shared.get(`hosted-zone-${props.domain}-id`),
12870
12732
  type: "A",
12871
12733
  name: fullDomainName,
@@ -12884,9 +12746,9 @@ var rpcFeature = defineFeature({
12884
12746
  throw new FileError(ctx.stackConfig.file, `RPC definition is not defined on app level for "${id}"`);
12885
12747
  }
12886
12748
  const schema = ctx.shared.get(`rpc-${id}-schema`);
12887
- const group = new Node15(ctx.stack, "rpc", id);
12749
+ const group = new Node16(ctx.stack, "rpc", id);
12888
12750
  for (const [name, props] of Object.entries(queries ?? {})) {
12889
- const queryGroup = new Node15(group, "query", name);
12751
+ const queryGroup = new Node16(group, "query", name);
12890
12752
  const entryId = paramCase6(`${id}-${shortId(name)}`);
12891
12753
  const lambda = createLambdaFunction(queryGroup, ctx, `rpc`, entryId, {
12892
12754
  ...props,
@@ -12899,7 +12761,7 @@ var rpcFeature = defineFeature({
12899
12761
  });
12900
12762
 
12901
12763
  // src/feature/search/index.ts
12902
- import { aws as aws17, Node as Node16 } from "@awsless/formation";
12764
+ import { aws as aws18, Node as Node17 } from "@awsless/formation";
12903
12765
  import { constantCase as constantCase10 } from "change-case";
12904
12766
  var typeGenCode4 = `
12905
12767
  import { AnyStruct, Table } from '@awsless/open-search'
@@ -12927,9 +12789,9 @@ var searchFeature = defineFeature({
12927
12789
  },
12928
12790
  onStack(ctx) {
12929
12791
  for (const [id, props] of Object.entries(ctx.stackConfig.searchs ?? {})) {
12930
- const group = new Node16(ctx.stack, "search", id);
12792
+ const group = new Node17(ctx.stack, "search", id);
12931
12793
  const name = `${id}-${shortId([ctx.app.name, ctx.stack.name, this.name, id].join("--"))}`;
12932
- const openSearch = new aws17.openSearch.Domain(group, "domain", {
12794
+ const openSearch = new aws18.openSearch.Domain(group, "domain", {
12933
12795
  name,
12934
12796
  version: props.version,
12935
12797
  storageSize: props.storage,
@@ -12969,7 +12831,7 @@ var searchFeature = defineFeature({
12969
12831
 
12970
12832
  // src/feature/site/index.ts
12971
12833
  import { days as days6, seconds as seconds4 } from "@awsless/duration";
12972
- import { Asset as Asset5, aws as aws18, Node as Node17 } from "@awsless/formation";
12834
+ import { Asset as Asset5, aws as aws19, Node as Node18 } from "@awsless/formation";
12973
12835
  import { glob as glob2 } from "glob";
12974
12836
  import { join as join9 } from "path";
12975
12837
 
@@ -12999,7 +12861,7 @@ var siteFeature = defineFeature({
12999
12861
  name: "site",
13000
12862
  onStack(ctx) {
13001
12863
  for (const [id, props] of Object.entries(ctx.stackConfig.sites ?? {})) {
13002
- const group = new Node17(ctx.stack, "site", id);
12864
+ const group = new Node18(ctx.stack, "site", id);
13003
12865
  const name = formatLocalResourceName({
13004
12866
  appName: ctx.app.name,
13005
12867
  stackName: ctx.stack.name,
@@ -13018,7 +12880,7 @@ var siteFeature = defineFeature({
13018
12880
  ctx.onBind((name2, value) => {
13019
12881
  lambda.addEnvironment(name2, value);
13020
12882
  });
13021
- new aws18.lambda.Permission(group, "permission", {
12883
+ new aws19.lambda.Permission(group, "permission", {
13022
12884
  principal: "*",
13023
12885
  // principal: 'cloudfront.amazonaws.com',
13024
12886
  action: "lambda:InvokeFunctionUrl",
@@ -13027,7 +12889,7 @@ var siteFeature = defineFeature({
13027
12889
  // urlAuthType: 'aws-iam',
13028
12890
  // sourceArn: distribution.arn,
13029
12891
  });
13030
- const url = new aws18.lambda.Url(group, "url", {
12892
+ const url = new aws19.lambda.Url(group, "url", {
13031
12893
  targetArn: lambda.arn,
13032
12894
  authType: "none"
13033
12895
  // authType: 'aws-iam',
@@ -13039,7 +12901,7 @@ var siteFeature = defineFeature({
13039
12901
  });
13040
12902
  }
13041
12903
  if (props.static) {
13042
- bucket = new aws18.s3.Bucket(group, "bucket", {
12904
+ bucket = new aws19.s3.Bucket(group, "bucket", {
13043
12905
  name: formatLocalResourceName({
13044
12906
  appName: ctx.app.name,
13045
12907
  stackName: ctx.stack.name,
@@ -13065,7 +12927,7 @@ var siteFeature = defineFeature({
13065
12927
  policy.addStatement(bucket.permissions);
13066
12928
  });
13067
12929
  bucket.deletionPolicy = "after-deployment";
13068
- const accessControl = new aws18.cloudFront.OriginAccessControl(group, `access`, {
12930
+ const accessControl = new aws19.cloudFront.OriginAccessControl(group, `access`, {
13069
12931
  name,
13070
12932
  type: "s3",
13071
12933
  behavior: "always",
@@ -13077,7 +12939,7 @@ var siteFeature = defineFeature({
13077
12939
  nodir: true
13078
12940
  });
13079
12941
  for (const file of files) {
13080
- const object = new aws18.s3.BucketObject(group, file, {
12942
+ const object = new aws19.s3.BucketObject(group, file, {
13081
12943
  bucket: bucket.name,
13082
12944
  key: file,
13083
12945
  body: Asset5.fromFile(join9(props.static, file)),
@@ -13100,21 +12962,21 @@ var siteFeature = defineFeature({
13100
12962
  statusCodes: [403, 404]
13101
12963
  });
13102
12964
  }
13103
- const cache = new aws18.cloudFront.CachePolicy(group, "cache", {
12965
+ const cache = new aws19.cloudFront.CachePolicy(group, "cache", {
13104
12966
  name,
13105
12967
  minTtl: seconds4(1),
13106
12968
  maxTtl: days6(365),
13107
12969
  defaultTtl: days6(1),
13108
12970
  ...props.cache
13109
12971
  });
13110
- const originRequest = new aws18.cloudFront.OriginRequestPolicy(group, "request", {
12972
+ const originRequest = new aws19.cloudFront.OriginRequestPolicy(group, "request", {
13111
12973
  name,
13112
12974
  header: {
13113
12975
  behavior: "all-except",
13114
12976
  values: ["host", "authorization"]
13115
12977
  }
13116
12978
  });
13117
- const responseHeaders = new aws18.cloudFront.ResponseHeadersPolicy(group, "response", {
12979
+ const responseHeaders = new aws19.cloudFront.ResponseHeadersPolicy(group, "response", {
13118
12980
  name,
13119
12981
  cors: props.cors,
13120
12982
  remove: ["server"]
@@ -13124,7 +12986,7 @@ var siteFeature = defineFeature({
13124
12986
  });
13125
12987
  const domainName = props.domain ? formatFullDomainName(ctx.appConfig, props.domain, props.subDomain) : void 0;
13126
12988
  const certificateArn = props.domain ? ctx.shared.get(`global-certificate-${props.domain}-arn`) : void 0;
13127
- const distribution = new aws18.cloudFront.Distribution(group, "distribution", {
12989
+ const distribution = new aws19.cloudFront.Distribution(group, "distribution", {
13128
12990
  name,
13129
12991
  compress: true,
13130
12992
  certificateArn,
@@ -13152,13 +13014,13 @@ var siteFeature = defineFeature({
13152
13014
  };
13153
13015
  })
13154
13016
  });
13155
- new aws18.cloudFront.InvalidateCache(group, "invalidate", {
13017
+ new aws19.cloudFront.InvalidateCache(group, "invalidate", {
13156
13018
  distributionId: distribution.id,
13157
13019
  paths: ["/*"],
13158
13020
  versions
13159
13021
  });
13160
13022
  if (props.static) {
13161
- new aws18.s3.BucketPolicy(group, `policy`, {
13023
+ new aws19.s3.BucketPolicy(group, `policy`, {
13162
13024
  bucketName: bucket.name,
13163
13025
  statements: [
13164
13026
  {
@@ -13185,7 +13047,7 @@ var siteFeature = defineFeature({
13185
13047
  });
13186
13048
  }
13187
13049
  if (domainName) {
13188
- new aws18.route53.RecordSet(group, `record`, {
13050
+ new aws19.route53.RecordSet(group, `record`, {
13189
13051
  hostedZoneId: ctx.shared.get(`hosted-zone-${props.domain}-id`),
13190
13052
  type: "A",
13191
13053
  name: domainName,
@@ -13201,7 +13063,7 @@ var siteFeature = defineFeature({
13201
13063
  });
13202
13064
 
13203
13065
  // src/feature/store/index.ts
13204
- import { aws as aws19, Node as Node18 } from "@awsless/formation";
13066
+ import { aws as aws20, Node as Node19 } from "@awsless/formation";
13205
13067
  import { paramCase as paramCase7 } from "change-case";
13206
13068
  var typeGenCode5 = `
13207
13069
  import { Body, PutObjectProps, BodyStream } from '@awsless/s3'
@@ -13238,7 +13100,7 @@ var storeFeature = defineFeature({
13238
13100
  },
13239
13101
  onStack(ctx) {
13240
13102
  for (const [id, props] of Object.entries(ctx.stackConfig.stores ?? {})) {
13241
- const group = new Node18(ctx.stack, "store", id);
13103
+ const group = new Node19(ctx.stack, "store", id);
13242
13104
  const name = formatLocalResourceName({
13243
13105
  appName: ctx.app.name,
13244
13106
  stackName: ctx.stack.name,
@@ -13258,13 +13120,13 @@ var storeFeature = defineFeature({
13258
13120
  "removed:marker": "s3:ObjectRemoved:DeleteMarkerCreated"
13259
13121
  };
13260
13122
  for (const [event, funcProps] of Object.entries(props.events ?? {})) {
13261
- const eventGroup = new Node18(group, "event", event);
13123
+ const eventGroup = new Node19(group, "event", event);
13262
13124
  const eventId = paramCase7(`${id}-${shortId(event)}`);
13263
13125
  const { lambda } = createAsyncLambdaFunction(eventGroup, ctx, `store`, eventId, {
13264
13126
  ...funcProps,
13265
13127
  description: `${id} event "${event}"`
13266
13128
  });
13267
- new aws19.lambda.Permission(eventGroup, "permission", {
13129
+ new aws20.lambda.Permission(eventGroup, "permission", {
13268
13130
  action: "lambda:InvokeFunction",
13269
13131
  principal: "s3.amazonaws.com",
13270
13132
  functionArn: lambda.arn,
@@ -13275,7 +13137,7 @@ var storeFeature = defineFeature({
13275
13137
  function: lambda.arn
13276
13138
  });
13277
13139
  }
13278
- const bucket = new aws19.s3.Bucket(group, "store", {
13140
+ const bucket = new aws20.s3.Bucket(group, "store", {
13279
13141
  name,
13280
13142
  versioning: props.versioning,
13281
13143
  forceDelete: true,
@@ -13303,24 +13165,24 @@ var storeFeature = defineFeature({
13303
13165
  });
13304
13166
 
13305
13167
  // src/feature/stream/index.ts
13306
- import { aws as aws20, Node as Node19 } from "@awsless/formation";
13168
+ import { aws as aws21, Node as Node20 } from "@awsless/formation";
13307
13169
  import { constantCase as constantCase12 } from "change-case";
13308
13170
  var streamFeature = defineFeature({
13309
13171
  name: "stream",
13310
13172
  onStack(ctx) {
13311
13173
  for (const [id, props] of Object.entries(ctx.stackConfig.streams ?? {})) {
13312
- const group = new Node19(ctx.stack, "stream", id);
13174
+ const group = new Node20(ctx.stack, "stream", id);
13313
13175
  const name = formatLocalResourceName({
13314
13176
  appName: ctx.app.name,
13315
13177
  stackName: ctx.stack.name,
13316
13178
  resourceType: "stream",
13317
13179
  resourceName: id
13318
13180
  });
13319
- const channel = new aws20.ivs.Channel(group, "channel", {
13181
+ const channel = new aws21.ivs.Channel(group, "channel", {
13320
13182
  name,
13321
13183
  ...props
13322
13184
  });
13323
- const streamKey = new aws20.ivs.StreamKey(group, "key", {
13185
+ const streamKey = new aws21.ivs.StreamKey(group, "key", {
13324
13186
  channel: channel.arn
13325
13187
  });
13326
13188
  const prefix = `STREAM_${constantCase12(ctx.stack.name)}_${constantCase12(id)}`;
@@ -13332,7 +13194,7 @@ var streamFeature = defineFeature({
13332
13194
  });
13333
13195
 
13334
13196
  // src/feature/table/index.ts
13335
- import { aws as aws21, Node as Node20 } from "@awsless/formation";
13197
+ import { aws as aws22, Node as Node21 } from "@awsless/formation";
13336
13198
  var tableFeature = defineFeature({
13337
13199
  name: "table",
13338
13200
  async onTypeGen(ctx) {
@@ -13356,7 +13218,7 @@ var tableFeature = defineFeature({
13356
13218
  },
13357
13219
  onStack(ctx) {
13358
13220
  for (const [id, props] of Object.entries(ctx.stackConfig.tables ?? {})) {
13359
- const group = new Node20(ctx.stack, "table", id);
13221
+ const group = new Node21(ctx.stack, "table", id);
13360
13222
  const name = formatLocalResourceName({
13361
13223
  appName: ctx.app.name,
13362
13224
  stackName: ctx.stack.name,
@@ -13364,7 +13226,7 @@ var tableFeature = defineFeature({
13364
13226
  resourceName: id
13365
13227
  });
13366
13228
  const deletionProtection = props.deletionProtection ?? ctx.appConfig.defaults.table?.deletionProtection;
13367
- const table2 = new aws21.dynamodb.Table(group, "table", {
13229
+ const table2 = new aws22.dynamodb.Table(group, "table", {
13368
13230
  ...props,
13369
13231
  name,
13370
13232
  stream: props.stream?.type,
@@ -13377,7 +13239,7 @@ var tableFeature = defineFeature({
13377
13239
  const { lambda, policy } = createLambdaFunction(group, ctx, "table", id, props.stream.consumer);
13378
13240
  lambda.addEnvironment("LOG_VIEWABLE_ERROR", "1");
13379
13241
  const onFailure = getGlobalOnFailure(ctx);
13380
- const source = new aws21.lambda.EventSourceMapping(group, id, {
13242
+ const source = new aws22.lambda.EventSourceMapping(group, id, {
13381
13243
  functionArn: lambda.arn,
13382
13244
  sourceArn: table2.streamArn,
13383
13245
  batchSize: 100,
@@ -13404,7 +13266,7 @@ var tableFeature = defineFeature({
13404
13266
  });
13405
13267
 
13406
13268
  // src/feature/task/index.ts
13407
- import { Node as Node21 } from "@awsless/formation";
13269
+ import { Node as Node22 } from "@awsless/formation";
13408
13270
  import { camelCase as camelCase7 } from "change-case";
13409
13271
  import { relative as relative6 } from "path";
13410
13272
  var typeGenCode6 = `
@@ -13466,7 +13328,7 @@ var taskFeature = defineFeature({
13466
13328
  },
13467
13329
  onStack(ctx) {
13468
13330
  for (const [id, props] of Object.entries(ctx.stackConfig.tasks ?? {})) {
13469
- const group = new Node21(ctx.stack, "task", id);
13331
+ const group = new Node22(ctx.stack, "task", id);
13470
13332
  createAsyncLambdaFunction(group, ctx, "task", id, props.consumer);
13471
13333
  }
13472
13334
  }
@@ -13483,7 +13345,7 @@ var testFeature = defineFeature({
13483
13345
  });
13484
13346
 
13485
13347
  // src/feature/topic/index.ts
13486
- import { aws as aws22, Node as Node22 } from "@awsless/formation";
13348
+ import { aws as aws23, Node as Node23 } from "@awsless/formation";
13487
13349
  var typeGenCode7 = `
13488
13350
  import type { PublishOptions } from '@awsless/sns'
13489
13351
  import type { Mock } from 'vitest'
@@ -13524,154 +13386,436 @@ var topicFeature = defineFeature({
13524
13386
  onApp(ctx) {
13525
13387
  for (const stack of ctx.stackConfigs) {
13526
13388
  for (const id of stack.topics ?? []) {
13527
- const group = new Node22(ctx.base, "topic", id);
13389
+ const group = new Node23(ctx.base, "topic", id);
13528
13390
  const name = formatGlobalResourceName({
13529
13391
  appName: ctx.appConfig.name,
13530
13392
  resourceType: "topic",
13531
13393
  resourceName: id
13532
13394
  });
13533
- const topic = new aws22.sns.Topic(group, "topic", {
13395
+ const topic = new aws23.sns.Topic(group, "topic", {
13534
13396
  name
13535
13397
  });
13536
13398
  ctx.shared.set(`topic-${id}-arn`, topic.arn);
13537
13399
  }
13538
- }
13539
- },
13540
- onStack(ctx) {
13541
- for (const id of ctx.stackConfig.topics ?? []) {
13542
- ctx.onPolicy((policy) => {
13543
- policy.addStatement({
13544
- actions: ["sns:Publish"],
13545
- resources: [ctx.shared.get(`topic-${id}-arn`)]
13546
- });
13547
- });
13548
- }
13549
- for (const [id, props] of Object.entries(ctx.stackConfig.subscribers ?? {})) {
13550
- const group = new Node22(ctx.stack, "topic", id);
13551
- const topicArn = ctx.shared.get(`topic-${id}-arn`);
13552
- if (typeof props === "string" && isEmail(props)) {
13553
- new aws22.sns.Subscription(group, id, {
13554
- topicArn,
13555
- protocol: "email",
13556
- endpoint: props
13557
- });
13558
- } else if (typeof props === "object") {
13559
- const { lambda } = createAsyncLambdaFunction(group, ctx, `topic`, id, props);
13560
- new aws22.sns.Subscription(group, id, {
13561
- topicArn,
13562
- protocol: "lambda",
13563
- endpoint: lambda.arn
13564
- });
13565
- new aws22.lambda.Permission(group, id, {
13566
- action: "lambda:InvokeFunction",
13567
- principal: "sns.amazonaws.com",
13568
- functionArn: lambda.arn,
13569
- sourceArn: topicArn
13570
- });
13400
+ }
13401
+ },
13402
+ onStack(ctx) {
13403
+ for (const id of ctx.stackConfig.topics ?? []) {
13404
+ ctx.onPolicy((policy) => {
13405
+ policy.addStatement({
13406
+ actions: ["sns:Publish"],
13407
+ resources: [ctx.shared.get(`topic-${id}-arn`)]
13408
+ });
13409
+ });
13410
+ }
13411
+ for (const [id, props] of Object.entries(ctx.stackConfig.subscribers ?? {})) {
13412
+ const group = new Node23(ctx.stack, "topic", id);
13413
+ const topicArn = ctx.shared.get(`topic-${id}-arn`);
13414
+ if (typeof props === "string" && isEmail(props)) {
13415
+ new aws23.sns.Subscription(group, id, {
13416
+ topicArn,
13417
+ protocol: "email",
13418
+ endpoint: props
13419
+ });
13420
+ } else if (typeof props === "object") {
13421
+ const { lambda } = createAsyncLambdaFunction(group, ctx, `topic`, id, props);
13422
+ new aws23.sns.Subscription(group, id, {
13423
+ topicArn,
13424
+ protocol: "lambda",
13425
+ endpoint: lambda.arn
13426
+ });
13427
+ new aws23.lambda.Permission(group, id, {
13428
+ action: "lambda:InvokeFunction",
13429
+ principal: "sns.amazonaws.com",
13430
+ functionArn: lambda.arn,
13431
+ sourceArn: topicArn
13432
+ });
13433
+ }
13434
+ }
13435
+ }
13436
+ });
13437
+
13438
+ // src/feature/vpc/index.ts
13439
+ import { aws as aws24, combine as combine2, Node as Node24 } from "@awsless/formation";
13440
+ var vpcFeature = defineFeature({
13441
+ name: "vpc",
13442
+ onApp(ctx) {
13443
+ const group = new Node24(ctx.base, "vpc", "main");
13444
+ const vpc = new aws24.ec2.Vpc(group, "vpc", {
13445
+ name: ctx.app.name,
13446
+ cidrBlock: aws24.ec2.Peer.ipv4("10.0.0.0/16")
13447
+ // cidrBlock: aws.ec2.Peer.ipv6('fd00:10:20::/48'),
13448
+ // cidrBlock: aws.ec2.Peer.ipv6('2a05:d018:c69:6600::/56'),
13449
+ // enableDnsSupport: true,
13450
+ // enableDnsHostnames: true,
13451
+ });
13452
+ const privateRouteTable = new aws24.ec2.RouteTable(group, "private", {
13453
+ vpcId: vpc.id,
13454
+ name: "private"
13455
+ });
13456
+ const publicRouteTable = new aws24.ec2.RouteTable(group, "public", {
13457
+ vpcId: vpc.id,
13458
+ name: "public"
13459
+ });
13460
+ const gateway = new aws24.ec2.InternetGateway(group, "gateway");
13461
+ const attachment = new aws24.ec2.VPCGatewayAttachment(group, "attachment", {
13462
+ vpcId: vpc.id,
13463
+ internetGatewayId: gateway.id
13464
+ });
13465
+ new aws24.ec2.Route(group, "route", {
13466
+ gatewayId: gateway.id,
13467
+ routeTableId: publicRouteTable.id,
13468
+ destination: aws24.ec2.Peer.anyIpv4()
13469
+ // destination: aws.ec2.Peer.anyIpv6(),
13470
+ });
13471
+ ctx.shared.set(
13472
+ "vpc-id",
13473
+ // Some resources require the internet gateway to be attached.
13474
+ combine2([vpc.id, attachment.internetGatewayId]).apply(([id]) => id)
13475
+ );
13476
+ ctx.shared.set("vpc-security-group-id", vpc.defaultSecurityGroup);
13477
+ const zones = ["a", "b"];
13478
+ const tables = [privateRouteTable, publicRouteTable];
13479
+ let block = 0n;
13480
+ for (const table2 of tables) {
13481
+ for (const i in zones) {
13482
+ const index = Number(i) + 1;
13483
+ const id = `${table2.identifier}-${index}`;
13484
+ const subnet = new aws24.ec2.Subnet(group, id, {
13485
+ name: `${ctx.app.name}--${table2.identifier}-${index}`,
13486
+ vpcId: vpc.id,
13487
+ cidrBlock: aws24.ec2.Peer.ipv4(`10.0.${block++}.0/24`),
13488
+ // ipv6CidrBlock: aws.ec2.Peer.ipv6(`fd00:10:20:${++block}::/64`),
13489
+ // ipv6CidrBlock: aws.ec2.Peer.ipv6(`2a05:d018:c69:660${++block}::/64`),
13490
+ // ipv6CidrBlock: ipv6CidrBlock.ipv6CidrBlock.apply(ip => ),
13491
+ // ipv6CidrBlock: slices.apply(list => aws.ec2.Peer.ipv6(list.get(block++).toString())),
13492
+ // assignIpv6AddressOnCreation: true,
13493
+ // ipv6Native: true,
13494
+ mapPublicIpOnLaunch: table2.identifier === "public",
13495
+ availabilityZone: ctx.appConfig.region + zones[i]
13496
+ });
13497
+ new aws24.ec2.SubnetRouteTableAssociation(group, id, {
13498
+ routeTableId: table2.id,
13499
+ subnetId: subnet.id
13500
+ });
13501
+ ctx.shared.set(`vpc-${table2.identifier}-subnet-id-${index}`, subnet.id);
13502
+ }
13503
+ }
13504
+ }
13505
+ });
13506
+
13507
+ // src/feature/index.ts
13508
+ var features = [
13509
+ // 1
13510
+ vpcFeature,
13511
+ domainFeature,
13512
+ commandFeature,
13513
+ onFailureFeature,
13514
+ // 2
13515
+ authFeature,
13516
+ // 3
13517
+ functionFeature,
13518
+ instanceFeature,
13519
+ graphqlFeature,
13520
+ configFeature,
13521
+ searchFeature,
13522
+ pubsubFeature,
13523
+ streamFeature,
13524
+ tableFeature,
13525
+ topicFeature,
13526
+ queueFeature,
13527
+ storeFeature,
13528
+ cacheFeature,
13529
+ taskFeature,
13530
+ testFeature,
13531
+ cronFeature,
13532
+ httpFeature,
13533
+ restFeature,
13534
+ siteFeature,
13535
+ // 4
13536
+ logSubscriptionFeature,
13537
+ // I think needs to be after s3 feature
13538
+ rpcFeature
13539
+ ];
13540
+
13541
+ // src/feature/validate.ts
13542
+ var validateFeatures = (props) => {
13543
+ for (const feature of features) {
13544
+ feature.onValidate?.(props);
13545
+ }
13546
+ };
13547
+
13548
+ // src/cli/ui/app.ts
13549
+ import { note } from "@clack/prompts";
13550
+ var logApp = (app, opt) => {
13551
+ const data = {
13552
+ App: app.name,
13553
+ Region: app.region,
13554
+ Profile: app.profile
13555
+ };
13556
+ if (opt.stage) {
13557
+ data.Stage = color.warning(opt.stage);
13558
+ }
13559
+ note(wrap(list(data)), "App Config");
13560
+ };
13561
+
13562
+ // src/cli/ui/error/error.ts
13563
+ import { AppError as AppError2, StackError as StackError3 } from "@awsless/formation";
13564
+ import { log as log6 } from "@clack/prompts";
13565
+
13566
+ // src/cli/ui/error/app-error.ts
13567
+ import { StackError as StackError2 } from "@awsless/formation";
13568
+ import { log as log3 } from "@clack/prompts";
13569
+
13570
+ // src/cli/ui/error/stack-error.ts
13571
+ import { ResourceError } from "@awsless/formation";
13572
+ import { log as log2 } from "@clack/prompts";
13573
+ import { capitalCase as capitalCase2 } from "change-case";
13574
+ var formatOperation = (operation) => {
13575
+ const value = ` ${capitalCase2(operation)} `;
13576
+ switch (operation) {
13577
+ case "create":
13578
+ return color.success.bold.inverse(value);
13579
+ case "update":
13580
+ return color.warning.bold.inverse(value);
13581
+ case "delete":
13582
+ return color.error.bold.inverse(value);
13583
+ case "heal":
13584
+ return color.warning.bold.inverse(value);
13585
+ case "get":
13586
+ return color.info.bold.inverse(value);
13587
+ }
13588
+ };
13589
+ var logStackError = (error) => {
13590
+ log2.message(
13591
+ wrap([color.error(error.message), `Stack: ${error.stack}`].join("\n"), {
13592
+ hard: true
13593
+ }),
13594
+ { symbol: color.error(icon.error) }
13595
+ );
13596
+ for (const issue of error.issues) {
13597
+ if (issue instanceof ResourceError) {
13598
+ log2.message(
13599
+ [
13600
+ formatOperation(issue.operation),
13601
+ "\n",
13602
+ wrap("URN: " + issue.urn, { hard: true }),
13603
+ "\n",
13604
+ wrap("ID: " + issue.id, { hard: true }),
13605
+ "\n\n",
13606
+ wrap(color.error(issue.message), { hard: true })
13607
+ // , '\n', color.error(issue.message)
13608
+ ].join(""),
13609
+ { symbol: color.error(icon.error) }
13610
+ );
13611
+ } else if (issue instanceof Error) {
13612
+ log2.message(wrap(color.error(issue.message), { hard: true }), {
13613
+ symbol: color.error(icon.error)
13614
+ });
13615
+ }
13616
+ }
13617
+ };
13618
+
13619
+ // src/cli/ui/error/app-error.ts
13620
+ var logAppError = (error) => {
13621
+ log3.message(
13622
+ wrap([color.error(error.message), `App: ${error.app}`].join("\n"), {
13623
+ hard: true
13624
+ }),
13625
+ { symbol: color.error(icon.error) }
13626
+ );
13627
+ for (const issue of error.issues) {
13628
+ if (issue instanceof StackError2) {
13629
+ logStackError(issue);
13630
+ } else if (issue instanceof Error) {
13631
+ log3.message(wrap(color.error(issue.message), { hard: true }), {
13632
+ symbol: color.error(icon.error)
13633
+ });
13634
+ }
13635
+ }
13636
+ };
13637
+
13638
+ // src/cli/ui/error/config-error.ts
13639
+ import * as p from "@clack/prompts";
13640
+ var codeLine = (value, level = 0) => {
13641
+ return [
13642
+ //
13643
+ " ".repeat(level),
13644
+ value
13645
+ ].join("");
13646
+ };
13647
+ var format = (value) => {
13648
+ if (Array.isArray(value)) {
13649
+ return "[ ... ]";
13650
+ }
13651
+ if (value === null) {
13652
+ return "null";
13653
+ }
13654
+ switch (typeof value) {
13655
+ case "function":
13656
+ return "() => { ... }";
13657
+ case "bigint":
13658
+ return `${value}n`;
13659
+ case "symbol":
13660
+ return "Symbol()";
13661
+ case "object":
13662
+ return "{ ... }";
13663
+ case "undefined":
13664
+ return "undefined";
13665
+ case "string":
13666
+ case "number":
13667
+ case "boolean":
13668
+ return JSON.stringify(value);
13669
+ }
13670
+ return "";
13671
+ };
13672
+ var logConfigError = (error) => {
13673
+ for (const issue of error.error.errors) {
13674
+ const message = [color.error(issue.message), color.dim(error.file), "\n{"];
13675
+ let context = error.data;
13676
+ const inStack = issue.path[0] === "stacks" && typeof issue.path[1] === "number";
13677
+ const length = issue.path.length;
13678
+ const end = ["}"];
13679
+ issue.path.forEach((path, i) => {
13680
+ const index = i + 1;
13681
+ context = context[path];
13682
+ if (typeof path === "string") {
13683
+ const key = path + `: `;
13684
+ if (index === length) {
13685
+ const space = " ".repeat(key.length);
13686
+ const value = format(context);
13687
+ const error2 = icon.arrow.top.repeat(value.length);
13688
+ message.push(codeLine(key + color.warning(value), index));
13689
+ message.push(codeLine(space + color.error(error2), index));
13690
+ } else if (Array.isArray(context)) {
13691
+ message.push(codeLine(key + "[", index));
13692
+ end.unshift(codeLine("]", index));
13693
+ } else if (typeof context === "object") {
13694
+ if (inStack && index === 3) {
13695
+ const name = error.data.stacks[issue.path[1]].name;
13696
+ message.push(codeLine("name: " + color.info(`"${name}"`) + ",", index));
13697
+ }
13698
+ message.push(codeLine(key + "{", index));
13699
+ end.unshift(codeLine("}", index));
13700
+ }
13701
+ } else if (typeof context === "object") {
13702
+ message.push(codeLine("{", index));
13703
+ end.unshift(codeLine("}", index));
13704
+ } else if (typeof context === "string") {
13705
+ message.push(codeLine(color.warning(`"${context}"`), index));
13706
+ const error2 = icon.arrow.top.repeat(context.length + 2);
13707
+ message.push(codeLine(color.error(error2), index));
13708
+ }
13709
+ });
13710
+ p.log.message(
13711
+ wrap([...message, ...end], {
13712
+ trim: false
13713
+ }),
13714
+ {
13715
+ symbol: color.error`×`
13571
13716
  }
13572
- }
13717
+ );
13573
13718
  }
13574
- });
13719
+ };
13575
13720
 
13576
- // src/feature/vpc/index.ts
13577
- import { aws as aws23, combine as combine2, Node as Node23 } from "@awsless/formation";
13578
- var vpcFeature = defineFeature({
13579
- name: "vpc",
13580
- onApp(ctx) {
13581
- const group = new Node23(ctx.base, "vpc", "main");
13582
- const vpc = new aws23.ec2.Vpc(group, "vpc", {
13583
- name: ctx.app.name,
13584
- cidrBlock: aws23.ec2.Peer.ipv4("10.0.0.0/16")
13585
- // cidrBlock: aws.ec2.Peer.ipv6('fd00:10:20::/48'),
13586
- // cidrBlock: aws.ec2.Peer.ipv6('2a05:d018:c69:6600::/56'),
13587
- // enableDnsSupport: true,
13588
- // enableDnsHostnames: true,
13721
+ // src/cli/ui/error/file-error.ts
13722
+ import { log as log5 } from "@clack/prompts";
13723
+ var logFileError = (error) => {
13724
+ log5.message(
13725
+ wrap([color.error(error.message), color.dim(error.file)].join("\n"), {
13726
+ hard: true
13727
+ }),
13728
+ { symbol: color.error(icon.error) }
13729
+ );
13730
+ };
13731
+
13732
+ // src/cli/ui/error/error.ts
13733
+ var logError = (error) => {
13734
+ if (error instanceof ConfigError) {
13735
+ logConfigError(error);
13736
+ } else if (error instanceof Cancelled) {
13737
+ log6.message(color.error("Cancelled."), {
13738
+ symbol: color.error(icon.error)
13589
13739
  });
13590
- const privateRouteTable = new aws23.ec2.RouteTable(group, "private", {
13591
- vpcId: vpc.id,
13592
- name: "private"
13740
+ } else if (error instanceof ExpectedError) {
13741
+ log6.message(color.error(error.message), {
13742
+ symbol: color.error(icon.error)
13593
13743
  });
13594
- const publicRouteTable = new aws23.ec2.RouteTable(group, "public", {
13595
- vpcId: vpc.id,
13596
- name: "public"
13744
+ } else if (error instanceof AppError2) {
13745
+ logAppError(error);
13746
+ } else if (error instanceof StackError3) {
13747
+ logStackError(error);
13748
+ } else if (error instanceof FileError) {
13749
+ logFileError(error);
13750
+ } else if (error instanceof Error) {
13751
+ const message = `${error.name}: ${error.message}`;
13752
+ const stack = error.stack ? color.dim(error.stack.replace(message, "")) : "";
13753
+ log6.message(
13754
+ wrap([color.error(message), stack], {
13755
+ hard: true
13756
+ }),
13757
+ { symbol: color.error(icon.error) }
13758
+ );
13759
+ } else if (typeof error === "string") {
13760
+ log6.message(wrap(color.error(error)), {
13761
+ symbol: color.error(icon.error)
13597
13762
  });
13598
- const gateway = new aws23.ec2.InternetGateway(group, "gateway");
13599
- const attachment = new aws23.ec2.VPCGatewayAttachment(group, "attachment", {
13600
- vpcId: vpc.id,
13601
- internetGatewayId: gateway.id
13763
+ } else {
13764
+ log6.message(wrap(color.error("Unknown error!")), {
13765
+ symbol: color.error(icon.error)
13602
13766
  });
13603
- new aws23.ec2.Route(group, "route", {
13604
- gatewayId: gateway.id,
13605
- routeTableId: publicRouteTable.id,
13606
- destination: aws23.ec2.Peer.anyIpv4()
13607
- // destination: aws.ec2.Peer.anyIpv6(),
13767
+ }
13768
+ };
13769
+
13770
+ // src/cli/ui/logo.ts
13771
+ var logo = () => {
13772
+ return `${color.primary`AWS`}${color.primary.dim`LESS`}`;
13773
+ };
13774
+
13775
+ // src/cli/ui/complex/layout.ts
13776
+ var layout = async (command, cb) => {
13777
+ console.log();
13778
+ intro(`${logo()} ${color.dim(command)}`);
13779
+ try {
13780
+ const options = program.optsWithGlobals();
13781
+ const appConfig = await loadAppConfig(options);
13782
+ logApp(appConfig, options);
13783
+ const stackConfigs = await loadStackConfigs(options);
13784
+ validateFeatures({
13785
+ appConfig,
13786
+ stackConfigs
13608
13787
  });
13609
- ctx.shared.set(
13610
- "vpc-id",
13611
- // Some resources require the internet gateway to be attached.
13612
- combine2([vpc.id, attachment.internetGatewayId]).apply(([id]) => id)
13613
- );
13614
- ctx.shared.set("vpc-security-group-id", vpc.defaultSecurityGroup);
13615
- const zones = ["a", "b"];
13616
- const tables = [privateRouteTable, publicRouteTable];
13617
- let block = 0n;
13618
- for (const table2 of tables) {
13619
- for (const i in zones) {
13620
- const index = Number(i) + 1;
13621
- const id = `${table2.identifier}-${index}`;
13622
- const subnet = new aws23.ec2.Subnet(group, id, {
13623
- name: `${ctx.app.name}--${table2.identifier}-${index}`,
13624
- vpcId: vpc.id,
13625
- cidrBlock: aws23.ec2.Peer.ipv4(`10.0.${block++}.0/24`),
13626
- // ipv6CidrBlock: aws.ec2.Peer.ipv6(`fd00:10:20:${++block}::/64`),
13627
- // ipv6CidrBlock: aws.ec2.Peer.ipv6(`2a05:d018:c69:660${++block}::/64`),
13628
- // ipv6CidrBlock: ipv6CidrBlock.ipv6CidrBlock.apply(ip => ),
13629
- // ipv6CidrBlock: slices.apply(list => aws.ec2.Peer.ipv6(list.get(block++).toString())),
13630
- // assignIpv6AddressOnCreation: true,
13631
- // ipv6Native: true,
13632
- mapPublicIpOnLaunch: table2.identifier === "public",
13633
- availabilityZone: ctx.appConfig.region + zones[i]
13634
- });
13635
- new aws23.ec2.SubnetRouteTableAssociation(group, id, {
13636
- routeTableId: table2.id,
13637
- subnetId: subnet.id
13638
- });
13639
- ctx.shared.set(`vpc-${table2.identifier}-subnet-id-${index}`, subnet.id);
13640
- }
13641
- }
13788
+ const result = await cb({
13789
+ options,
13790
+ appConfig,
13791
+ stackConfigs
13792
+ });
13793
+ outro(result ?? void 0);
13794
+ } catch (error) {
13795
+ logError(error);
13796
+ outro();
13797
+ process.exit(1);
13642
13798
  }
13643
- });
13799
+ };
13644
13800
 
13645
- // src/feature/index.ts
13646
- var features = [
13647
- // 1
13648
- vpcFeature,
13649
- domainFeature,
13650
- commandFeature,
13651
- onFailureFeature,
13652
- // 2
13653
- authFeature,
13654
- // 3
13655
- functionFeature,
13656
- instanceFeature,
13657
- graphqlFeature,
13658
- configFeature,
13659
- searchFeature,
13660
- pubsubFeature,
13661
- streamFeature,
13662
- tableFeature,
13663
- topicFeature,
13664
- queueFeature,
13665
- storeFeature,
13666
- cacheFeature,
13667
- taskFeature,
13668
- testFeature,
13669
- cronFeature,
13670
- httpFeature,
13671
- restFeature,
13672
- siteFeature,
13673
- rpcFeature
13674
- ];
13801
+ // src/cli/command/bootstrap.ts
13802
+ var bootstrap = (program2) => {
13803
+ program2.command("bootstrap").description("Create the awsless bootstrap stack").action(async () => {
13804
+ await layout("bootstrap", async ({ appConfig }) => {
13805
+ const credentials = getCredentials(appConfig.profile);
13806
+ const accountId = await getAccountId(credentials, appConfig.region);
13807
+ await bootstrapAwsless({
13808
+ credentials,
13809
+ region: appConfig.region,
13810
+ accountId
13811
+ });
13812
+ return "Ready to go!";
13813
+ });
13814
+ });
13815
+ };
13816
+
13817
+ // src/app.ts
13818
+ import { App as App2, Stack } from "@awsless/formation";
13675
13819
 
13676
13820
  // src/shared.ts
13677
13821
  var SharedData = class {
@@ -14694,10 +14838,8 @@ var watchConfig = async (options, resolve2, reject) => {
14694
14838
  try {
14695
14839
  const appConfig = await loadAppConfig(options);
14696
14840
  const stackConfigs = await loadStackConfigs(options);
14697
- resolve2({
14698
- appConfig,
14699
- stackConfigs
14700
- });
14841
+ validateFeatures({ appConfig, stackConfigs });
14842
+ resolve2({ appConfig, stackConfigs });
14701
14843
  } catch (error) {
14702
14844
  reject(error);
14703
14845
  }