@awsless/awsless 0.0.208 → 0.0.210
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.MD +34 -20
- package/dist/app.json +1 -1
- package/dist/bin.js +336 -119
- package/dist/build-json-schema.js +125 -105
- package/dist/stack.json +1 -1
- package/package.json +6 -6
package/dist/bin.js
CHANGED
|
@@ -69,7 +69,7 @@ var debug = (...parts) => {
|
|
|
69
69
|
};
|
|
70
70
|
|
|
71
71
|
// src/config/app.ts
|
|
72
|
-
import { z as
|
|
72
|
+
import { z as z15 } from "zod";
|
|
73
73
|
|
|
74
74
|
// src/config/schema/resource-id.ts
|
|
75
75
|
import { paramCase } from "change-case";
|
|
@@ -565,25 +565,45 @@ var HttpDefaultSchema = z12.record(
|
|
|
565
565
|
).optional().describe("Define your global HTTP API's.");
|
|
566
566
|
var HttpSchema = z12.record(ResourceIdSchema, z12.record(RouteSchema, FunctionSchema)).optional().describe("Define routes in your stack for your global HTTP API.");
|
|
567
567
|
|
|
568
|
+
// src/feature/rest/schema.ts
|
|
569
|
+
import { z as z14 } from "zod";
|
|
570
|
+
|
|
571
|
+
// src/config/schema/route.ts
|
|
572
|
+
import { z as z13 } from "zod";
|
|
573
|
+
var RouteSchema2 = z13.union([
|
|
574
|
+
z13.string().regex(/^(POST|GET|PUT|DELETE|HEAD|OPTIONS)(\s\/[a-z0-9\+\_\-\/\{\}]*)$/gi, "Invalid route"),
|
|
575
|
+
z13.literal("$default")
|
|
576
|
+
]);
|
|
577
|
+
|
|
578
|
+
// src/feature/rest/schema.ts
|
|
579
|
+
var RestDefaultSchema = z14.record(
|
|
580
|
+
ResourceIdSchema,
|
|
581
|
+
z14.object({
|
|
582
|
+
domain: ResourceIdSchema.describe("The domain id to link your API with.").optional(),
|
|
583
|
+
subDomain: z14.string().optional()
|
|
584
|
+
})
|
|
585
|
+
).optional().describe("Define your global REST API's.");
|
|
586
|
+
var RestSchema = z14.record(ResourceIdSchema, z14.record(RouteSchema2, FunctionSchema)).optional().describe("Define routes in your stack for your global REST API.");
|
|
587
|
+
|
|
568
588
|
// src/config/app.ts
|
|
569
|
-
var AppSchema =
|
|
570
|
-
$schema:
|
|
589
|
+
var AppSchema = z15.object({
|
|
590
|
+
$schema: z15.string().optional(),
|
|
571
591
|
name: ResourceIdSchema.describe("App name."),
|
|
572
592
|
region: RegionSchema.describe("The AWS region to deploy to."),
|
|
573
|
-
profile:
|
|
593
|
+
profile: z15.string().describe("The AWS profile to deploy to."),
|
|
574
594
|
// stage: z
|
|
575
595
|
// .string()
|
|
576
596
|
// .regex(/^[a-z]+$/)
|
|
577
597
|
// .default('prod')
|
|
578
598
|
// .describe('The deployment stage.'),
|
|
579
|
-
defaults:
|
|
599
|
+
defaults: z15.object({
|
|
580
600
|
auth: AuthDefaultSchema,
|
|
581
601
|
domains: DomainsDefaultSchema,
|
|
582
602
|
function: FunctionDefaultSchema,
|
|
583
603
|
queue: QueueDefaultSchema,
|
|
584
604
|
graphql: GraphQLDefaultSchema,
|
|
585
|
-
http: HttpDefaultSchema
|
|
586
|
-
|
|
605
|
+
http: HttpDefaultSchema,
|
|
606
|
+
rest: RestDefaultSchema
|
|
587
607
|
}).default({}).describe("Default properties")
|
|
588
608
|
});
|
|
589
609
|
|
|
@@ -591,7 +611,7 @@ var AppSchema = z13.object({
|
|
|
591
611
|
import { glob } from "glob";
|
|
592
612
|
|
|
593
613
|
// src/config/stack.ts
|
|
594
|
-
import { z as
|
|
614
|
+
import { z as z28 } from "zod";
|
|
595
615
|
|
|
596
616
|
// src/feature/on-failure/schema.ts
|
|
597
617
|
var OnFailureSchema = FunctionSchema.optional().describe(
|
|
@@ -599,41 +619,41 @@ var OnFailureSchema = FunctionSchema.optional().describe(
|
|
|
599
619
|
);
|
|
600
620
|
|
|
601
621
|
// src/feature/config/schema.ts
|
|
602
|
-
import { z as
|
|
603
|
-
var ConfigNameSchema =
|
|
604
|
-
var ConfigsSchema =
|
|
622
|
+
import { z as z16 } from "zod";
|
|
623
|
+
var ConfigNameSchema = z16.string().regex(/[a-z0-9\-]/g, "Invalid config name");
|
|
624
|
+
var ConfigsSchema = z16.array(ConfigNameSchema).optional().describe("Define the config values for your stack.");
|
|
605
625
|
|
|
606
626
|
// src/feature/table/schema.ts
|
|
607
|
-
import { z as
|
|
608
|
-
var KeySchema =
|
|
609
|
-
var TablesSchema =
|
|
627
|
+
import { z as z17 } from "zod";
|
|
628
|
+
var KeySchema = z17.string().min(1).max(255);
|
|
629
|
+
var TablesSchema = z17.record(
|
|
610
630
|
ResourceIdSchema,
|
|
611
|
-
|
|
631
|
+
z17.object({
|
|
612
632
|
hash: KeySchema.describe(
|
|
613
633
|
"Specifies the name of the partition / hash key that makes up the primary key for the table."
|
|
614
634
|
),
|
|
615
635
|
sort: KeySchema.optional().describe(
|
|
616
636
|
"Specifies the name of the range / sort key that makes up the primary key for the table."
|
|
617
637
|
),
|
|
618
|
-
fields:
|
|
638
|
+
fields: z17.record(z17.string(), z17.enum(["string", "number", "binary"])).optional().describe(
|
|
619
639
|
'A list of attributes that describe the key schema for the table and indexes. If no attribute field is defined we default to "string".'
|
|
620
640
|
),
|
|
621
|
-
class:
|
|
622
|
-
pointInTimeRecovery:
|
|
641
|
+
class: z17.enum(["standard", "standard-infrequent-access"]).default("standard").describe("The table class of the table."),
|
|
642
|
+
pointInTimeRecovery: z17.boolean().default(false).describe("Indicates whether point in time recovery is enabled on the table."),
|
|
623
643
|
timeToLiveAttribute: KeySchema.optional().describe(
|
|
624
644
|
"The name of the TTL attribute used to store the expiration time for items in the table. To update this property, you must first disable TTL and then enable TTL with the new attribute name."
|
|
625
645
|
),
|
|
626
|
-
stream:
|
|
627
|
-
type:
|
|
646
|
+
stream: z17.object({
|
|
647
|
+
type: z17.enum(["keys-only", "new-image", "old-image", "new-and-old-images"]).describe(
|
|
628
648
|
"When an item in the table is modified, stream.type determines what information is written to the stream for this table. Valid values are:\n- keys-only - Only the key attributes of the modified item are written to the stream.\n- new-image - The entire item, as it appears after it was modified, is written to the stream.\n- old-image - The entire item, as it appeared before it was modified, is written to the stream.\n- new-and-old-images - Both the new and the old item images of the item are written to the stream."
|
|
629
649
|
),
|
|
630
650
|
consumer: FunctionSchema.describe("The consuming lambda function for the stream")
|
|
631
651
|
}).optional().describe(
|
|
632
652
|
"The settings for the DynamoDB table stream, which capture changes to items stored in the table."
|
|
633
653
|
),
|
|
634
|
-
indexes:
|
|
635
|
-
|
|
636
|
-
|
|
654
|
+
indexes: z17.record(
|
|
655
|
+
z17.string(),
|
|
656
|
+
z17.object({
|
|
637
657
|
/** Specifies the name of the partition / hash key that makes up the primary key for the global secondary index. */
|
|
638
658
|
hash: KeySchema,
|
|
639
659
|
/** Specifies the name of the range / sort key that makes up the primary key for the global secondary index. */
|
|
@@ -643,34 +663,34 @@ var TablesSchema = z15.record(
|
|
|
643
663
|
* - keys-only - Only the index and primary keys are projected into the index.
|
|
644
664
|
* @default 'all'
|
|
645
665
|
*/
|
|
646
|
-
projection:
|
|
666
|
+
projection: z17.enum(["all", "keys-only"]).default("all")
|
|
647
667
|
})
|
|
648
668
|
).optional().describe("Specifies the global secondary indexes to be created on the table.")
|
|
649
669
|
})
|
|
650
670
|
).optional().describe("Define the tables in your stack.");
|
|
651
671
|
|
|
652
672
|
// src/feature/store/schema.ts
|
|
653
|
-
import { z as
|
|
654
|
-
var StoresSchema =
|
|
673
|
+
import { z as z18 } from "zod";
|
|
674
|
+
var StoresSchema = z18.array(ResourceIdSchema).optional().describe("Define the stores in your stack.");
|
|
655
675
|
|
|
656
676
|
// src/feature/pubsub/schema.ts
|
|
657
|
-
import { z as
|
|
658
|
-
var PubSubSchema =
|
|
677
|
+
import { z as z19 } from "zod";
|
|
678
|
+
var PubSubSchema = z19.record(
|
|
659
679
|
ResourceIdSchema,
|
|
660
|
-
|
|
661
|
-
sql:
|
|
662
|
-
sqlVersion:
|
|
680
|
+
z19.object({
|
|
681
|
+
sql: z19.string().describe("The SQL statement used to query the IOT topic."),
|
|
682
|
+
sqlVersion: z19.enum(["2015-10-08", "2016-03-23", "beta"]).default("2016-03-23").describe("The version of the SQL rules engine to use when evaluating the rule."),
|
|
663
683
|
consumer: FunctionSchema.describe("The consuming lambda function properties.")
|
|
664
684
|
})
|
|
665
685
|
).optional().describe("Define the pubsub subscriber in your stack.");
|
|
666
686
|
|
|
667
687
|
// src/feature/test/schema.ts
|
|
668
|
-
import { z as
|
|
688
|
+
import { z as z21 } from "zod";
|
|
669
689
|
|
|
670
690
|
// src/config/schema/local-directory.ts
|
|
671
691
|
import { stat as stat2 } from "fs/promises";
|
|
672
|
-
import { z as
|
|
673
|
-
var LocalDirectorySchema =
|
|
692
|
+
import { z as z20 } from "zod";
|
|
693
|
+
var LocalDirectorySchema = z20.string().transform((path) => resolvePath(path)).refine(async (path) => {
|
|
674
694
|
try {
|
|
675
695
|
const s = await stat2(path);
|
|
676
696
|
return s.isDirectory();
|
|
@@ -680,26 +700,26 @@ var LocalDirectorySchema = z18.string().transform((path) => resolvePath(path)).r
|
|
|
680
700
|
}, `Directory doesn't exist`);
|
|
681
701
|
|
|
682
702
|
// src/feature/test/schema.ts
|
|
683
|
-
var TestsSchema =
|
|
703
|
+
var TestsSchema = z21.union([LocalDirectorySchema.transform((v) => [v]), LocalDirectorySchema.array()]).describe("Define the location of your tests for your stack.").optional();
|
|
684
704
|
|
|
685
705
|
// src/feature/topic/schema.ts
|
|
686
706
|
import { paramCase as paramCase2 } from "change-case";
|
|
687
|
-
import { z as
|
|
688
|
-
var TopicNameSchema =
|
|
689
|
-
var TopicsSchema =
|
|
707
|
+
import { z as z22 } from "zod";
|
|
708
|
+
var TopicNameSchema = z22.string().min(3).max(256).regex(/^[a-z0-9\-]+$/i, "Invalid topic name").transform((value) => paramCase2(value)).describe("Define event topic name.");
|
|
709
|
+
var TopicsSchema = z22.array(TopicNameSchema).refine((topics) => {
|
|
690
710
|
return topics.length === new Set(topics).size;
|
|
691
711
|
}, "Must be a list of unique topic names").optional().describe("Define the event topics to publish too in your stack.");
|
|
692
|
-
var SubscribersSchema =
|
|
712
|
+
var SubscribersSchema = z22.record(TopicNameSchema, z22.union([EmailSchema, FunctionSchema])).optional().describe("Define the events to subscribe too in your stack.");
|
|
693
713
|
|
|
694
714
|
// src/feature/cron/schema/index.ts
|
|
695
|
-
import { z as
|
|
715
|
+
import { z as z24 } from "zod";
|
|
696
716
|
|
|
697
717
|
// src/feature/cron/schema/schedule.ts
|
|
698
|
-
import { z as
|
|
718
|
+
import { z as z23 } from "zod";
|
|
699
719
|
import { awsCronExpressionValidator } from "aws-cron-expression-validator";
|
|
700
|
-
var RateExpressionSchema =
|
|
720
|
+
var RateExpressionSchema = z23.custom(
|
|
701
721
|
(value) => {
|
|
702
|
-
return
|
|
722
|
+
return z23.string().regex(/^[0-9]+ (seconds?|minutes?|hours?|days?)$/).refine((rate) => {
|
|
703
723
|
const [str] = rate.split(" ");
|
|
704
724
|
const number = parseInt(str);
|
|
705
725
|
return number > 0;
|
|
@@ -715,9 +735,9 @@ var RateExpressionSchema = z21.custom(
|
|
|
715
735
|
}
|
|
716
736
|
return `rate(${rate})`;
|
|
717
737
|
});
|
|
718
|
-
var CronExpressionSchema =
|
|
738
|
+
var CronExpressionSchema = z23.custom(
|
|
719
739
|
(value) => {
|
|
720
|
-
return
|
|
740
|
+
return z23.string().safeParse(value).success;
|
|
721
741
|
},
|
|
722
742
|
{ message: "Invalid cron expression" }
|
|
723
743
|
).superRefine((value, ctx) => {
|
|
@@ -726,12 +746,12 @@ var CronExpressionSchema = z21.custom(
|
|
|
726
746
|
} catch (error) {
|
|
727
747
|
if (error instanceof Error) {
|
|
728
748
|
ctx.addIssue({
|
|
729
|
-
code:
|
|
749
|
+
code: z23.ZodIssueCode.custom,
|
|
730
750
|
message: `Invalid cron expression: ${error.message}`
|
|
731
751
|
});
|
|
732
752
|
} else {
|
|
733
753
|
ctx.addIssue({
|
|
734
|
-
code:
|
|
754
|
+
code: z23.ZodIssueCode.custom,
|
|
735
755
|
message: "Invalid cron expression"
|
|
736
756
|
});
|
|
737
757
|
}
|
|
@@ -742,21 +762,21 @@ var CronExpressionSchema = z21.custom(
|
|
|
742
762
|
var ScheduleExpressionSchema = RateExpressionSchema.or(CronExpressionSchema);
|
|
743
763
|
|
|
744
764
|
// src/feature/cron/schema/index.ts
|
|
745
|
-
var CronsSchema =
|
|
765
|
+
var CronsSchema = z24.record(
|
|
746
766
|
ResourceIdSchema,
|
|
747
|
-
|
|
748
|
-
enabled:
|
|
767
|
+
z24.object({
|
|
768
|
+
enabled: z24.boolean().default(true).describe("If the cron is enabled."),
|
|
749
769
|
consumer: FunctionSchema.describe("The consuming lambda function properties."),
|
|
750
770
|
schedule: ScheduleExpressionSchema.describe(
|
|
751
771
|
'The scheduling expression.\n\nexample: "0 20 * * ? *"\nexample: "5 minutes"'
|
|
752
772
|
),
|
|
753
|
-
payload:
|
|
773
|
+
payload: z24.unknown().optional().describe("The JSON payload that will be passed to the consumer.")
|
|
754
774
|
})
|
|
755
775
|
).optional();
|
|
756
776
|
|
|
757
777
|
// src/feature/cache/schema.ts
|
|
758
|
-
import { z as
|
|
759
|
-
var TypeSchema =
|
|
778
|
+
import { z as z25 } from "zod";
|
|
779
|
+
var TypeSchema = z25.enum([
|
|
760
780
|
"t4g.small",
|
|
761
781
|
"t4g.medium",
|
|
762
782
|
"r6g.large",
|
|
@@ -771,27 +791,27 @@ var TypeSchema = z23.enum([
|
|
|
771
791
|
"r6gd.4xlarge",
|
|
772
792
|
"r6gd.8xlarge"
|
|
773
793
|
]);
|
|
774
|
-
var PortSchema =
|
|
775
|
-
var ShardsSchema =
|
|
776
|
-
var ReplicasPerShardSchema =
|
|
777
|
-
var EngineSchema =
|
|
778
|
-
var CachesSchema =
|
|
794
|
+
var PortSchema = z25.number().int().min(1).max(5e4);
|
|
795
|
+
var ShardsSchema = z25.number().int().min(0).max(100);
|
|
796
|
+
var ReplicasPerShardSchema = z25.number().int().min(0).max(5);
|
|
797
|
+
var EngineSchema = z25.enum(["7.0", "6.2"]);
|
|
798
|
+
var CachesSchema = z25.record(
|
|
779
799
|
ResourceIdSchema,
|
|
780
|
-
|
|
800
|
+
z25.object({
|
|
781
801
|
type: TypeSchema.default("t4g.small"),
|
|
782
802
|
port: PortSchema.default(6379),
|
|
783
803
|
shards: ShardsSchema.default(1),
|
|
784
804
|
replicasPerShard: ReplicasPerShardSchema.default(1),
|
|
785
805
|
engine: EngineSchema.default("7.0"),
|
|
786
|
-
dataTiering:
|
|
806
|
+
dataTiering: z25.boolean().default(false)
|
|
787
807
|
})
|
|
788
808
|
).optional().describe("Define the caches in your stack. For access to the cache put your functions inside the global VPC.");
|
|
789
809
|
|
|
790
810
|
// src/feature/search/schema.ts
|
|
791
|
-
import { z as
|
|
811
|
+
import { z as z26 } from "zod";
|
|
792
812
|
import { gibibytes as gibibytes2 } from "@awsless/size";
|
|
793
|
-
var VersionSchema =
|
|
794
|
-
var TypeSchema2 =
|
|
813
|
+
var VersionSchema = z26.enum(["2.11", "2.9", "2.7", "2.5", "2.3", "1.3"]);
|
|
814
|
+
var TypeSchema2 = z26.enum([
|
|
795
815
|
"t3.small",
|
|
796
816
|
"t3.medium",
|
|
797
817
|
"t3.large",
|
|
@@ -892,41 +912,41 @@ var TypeSchema2 = z24.enum([
|
|
|
892
912
|
"r6gd.16xlarge"
|
|
893
913
|
]);
|
|
894
914
|
var StorageSizeSchema = SizeSchema.refine(sizeMin(gibibytes2(10)), "Minimum storage size is 10 GB").refine(sizeMax(gibibytes2(100)), "Maximum storage size is 100 GB").describe("The size of the function's /tmp directory. You can specify a size value from 512 MB to 10 GiB.");
|
|
895
|
-
var SearchsSchema =
|
|
915
|
+
var SearchsSchema = z26.record(
|
|
896
916
|
ResourceIdSchema,
|
|
897
|
-
|
|
917
|
+
z26.object({
|
|
898
918
|
type: TypeSchema2.default("t3.small"),
|
|
899
|
-
count:
|
|
919
|
+
count: z26.number().int().min(1).default(1),
|
|
900
920
|
version: VersionSchema.default("2.11"),
|
|
901
921
|
storage: StorageSizeSchema.default("10 GB"),
|
|
902
|
-
vpc:
|
|
922
|
+
vpc: z26.boolean().default(false)
|
|
903
923
|
})
|
|
904
924
|
).optional().describe("Define the search instances in your stack. Backed by OpenSearch.");
|
|
905
925
|
|
|
906
926
|
// src/feature/site/schema.ts
|
|
907
|
-
import { z as
|
|
908
|
-
var ErrorResponsePathSchema =
|
|
927
|
+
import { z as z27 } from "zod";
|
|
928
|
+
var ErrorResponsePathSchema = z27.string().describe(
|
|
909
929
|
"The path to the custom error page that you want to return to the viewer when your origin returns the HTTP status code specified.\n - We recommend that you store custom error pages in an Amazon S3 bucket. If you store custom error pages on an HTTP server and the server starts to return 5xx errors, CloudFront can't get the files that you want to return to viewers because the origin server is unavailable."
|
|
910
930
|
);
|
|
911
|
-
var StatusCodeSchema =
|
|
931
|
+
var StatusCodeSchema = z27.number().int().positive().optional().describe(
|
|
912
932
|
"The HTTP status code that you want CloudFront to return to the viewer along with the custom error page. There are a variety of reasons that you might want CloudFront to return a status code different from the status code that your origin returned to CloudFront, for example:\n- Some Internet devices (some firewalls and corporate proxies, for example) intercept HTTP 4xx and 5xx and prevent the response from being returned to the viewer. If you substitute 200, the response typically won't be intercepted.\n- If you don't care about distinguishing among different client errors or server errors, you can specify 400 or 500 as the ResponseCode for all 4xx or 5xx errors.\n- You might want to return a 200 status code (OK) and static website so your customers don't know that your website is down."
|
|
913
933
|
);
|
|
914
934
|
var MinTTLSchema = DurationSchema.describe(
|
|
915
935
|
"The minimum amount of time, that you want to cache the error response. When this time period has elapsed, CloudFront queries your origin to see whether the problem that caused the error has been resolved and the requested object is now available."
|
|
916
936
|
);
|
|
917
|
-
var ErrorResponseSchema =
|
|
937
|
+
var ErrorResponseSchema = z27.union([
|
|
918
938
|
ErrorResponsePathSchema,
|
|
919
|
-
|
|
939
|
+
z27.object({
|
|
920
940
|
path: ErrorResponsePathSchema,
|
|
921
941
|
statusCode: StatusCodeSchema.optional(),
|
|
922
942
|
minTTL: MinTTLSchema.optional()
|
|
923
943
|
})
|
|
924
944
|
]).optional();
|
|
925
|
-
var SitesSchema =
|
|
945
|
+
var SitesSchema = z27.record(
|
|
926
946
|
ResourceIdSchema,
|
|
927
|
-
|
|
947
|
+
z27.object({
|
|
928
948
|
domain: ResourceIdSchema.describe("The domain id to link your site with."),
|
|
929
|
-
subDomain:
|
|
949
|
+
subDomain: z27.string().optional(),
|
|
930
950
|
static: LocalDirectorySchema.optional().describe("Specifies the path to the static files directory."),
|
|
931
951
|
ssr: FunctionSchema.optional().describe("Specifies the ssr file."),
|
|
932
952
|
// bind: z.object({
|
|
@@ -941,7 +961,7 @@ var SitesSchema = z25.record(
|
|
|
941
961
|
// build: z.string().optional(),
|
|
942
962
|
// }),
|
|
943
963
|
// ]),
|
|
944
|
-
errors:
|
|
964
|
+
errors: z27.object({
|
|
945
965
|
400: ErrorResponseSchema.describe("Customize a `400 Bad Request` response."),
|
|
946
966
|
403: ErrorResponseSchema.describe("Customize a `403 Forbidden` response."),
|
|
947
967
|
404: ErrorResponseSchema.describe("Customize a `404 Not Found` response."),
|
|
@@ -954,16 +974,16 @@ var SitesSchema = z25.record(
|
|
|
954
974
|
503: ErrorResponseSchema.describe("Customize a `503 Service Unavailable` response."),
|
|
955
975
|
504: ErrorResponseSchema.describe("Customize a `504 Gateway Timeout` response.")
|
|
956
976
|
}).optional().describe("Customize the error responses for specific HTTP status codes."),
|
|
957
|
-
cors:
|
|
958
|
-
override:
|
|
977
|
+
cors: z27.object({
|
|
978
|
+
override: z27.boolean().default(false),
|
|
959
979
|
maxAge: DurationSchema.default("365 days"),
|
|
960
|
-
exposeHeaders:
|
|
961
|
-
credentials:
|
|
962
|
-
headers:
|
|
963
|
-
origins:
|
|
964
|
-
methods:
|
|
980
|
+
exposeHeaders: z27.string().array().optional(),
|
|
981
|
+
credentials: z27.boolean().default(false),
|
|
982
|
+
headers: z27.string().array().default(["*"]),
|
|
983
|
+
origins: z27.string().array().default(["*"]),
|
|
984
|
+
methods: z27.enum(["GET", "DELETE", "HEAD", "OPTIONS", "PATCH", "POST", "PUT", "ALL"]).array().default(["ALL"])
|
|
965
985
|
}).optional().describe("Define the cors headers."),
|
|
966
|
-
security:
|
|
986
|
+
security: z27.object({
|
|
967
987
|
// contentSecurityPolicy: z.object({
|
|
968
988
|
// override: z.boolean().default(false),
|
|
969
989
|
// policy: z.string(),
|
|
@@ -1005,10 +1025,10 @@ var SitesSchema = z25.record(
|
|
|
1005
1025
|
// reportUri?: string
|
|
1006
1026
|
// }
|
|
1007
1027
|
}).optional().describe("Define the security policy."),
|
|
1008
|
-
cache:
|
|
1009
|
-
cookies:
|
|
1010
|
-
headers:
|
|
1011
|
-
queries:
|
|
1028
|
+
cache: z27.object({
|
|
1029
|
+
cookies: z27.string().array().optional().describe("Specifies the cookies that CloudFront includes in the cache key."),
|
|
1030
|
+
headers: z27.string().array().optional().describe("Specifies the headers that CloudFront includes in the cache key."),
|
|
1031
|
+
queries: z27.string().array().optional().describe("Specifies the query values that CloudFront includes in the cache key.")
|
|
1012
1032
|
}).optional().describe(
|
|
1013
1033
|
"Specifies the cookies, headers, and query values that CloudFront includes in the cache key."
|
|
1014
1034
|
)
|
|
@@ -1020,15 +1040,15 @@ var DependsSchema = ResourceIdSchema.array().optional().describe("Define the sta
|
|
|
1020
1040
|
var NameSchema = ResourceIdSchema.refine((name) => !["base"].includes(name), {
|
|
1021
1041
|
message: `Stack name can't be a reserved name.`
|
|
1022
1042
|
}).describe("Stack name.");
|
|
1023
|
-
var StackSchema =
|
|
1024
|
-
$schema:
|
|
1043
|
+
var StackSchema = z28.object({
|
|
1044
|
+
$schema: z28.string().optional(),
|
|
1025
1045
|
name: NameSchema,
|
|
1026
1046
|
depends: DependsSchema,
|
|
1027
1047
|
onFailure: OnFailureSchema,
|
|
1028
1048
|
auth: AuthSchema,
|
|
1029
1049
|
graphql: GraphQLSchema,
|
|
1030
1050
|
http: HttpSchema,
|
|
1031
|
-
|
|
1051
|
+
rest: RestSchema,
|
|
1032
1052
|
configs: ConfigsSchema,
|
|
1033
1053
|
crons: CronsSchema,
|
|
1034
1054
|
caches: CachesSchema,
|
|
@@ -1099,13 +1119,13 @@ var readConfigWithStage = async (file, stage) => {
|
|
|
1099
1119
|
};
|
|
1100
1120
|
|
|
1101
1121
|
// src/config/load/validate.ts
|
|
1102
|
-
import { z as
|
|
1122
|
+
import { z as z29 } from "zod";
|
|
1103
1123
|
var validateConfig = async (schema, file, data) => {
|
|
1104
1124
|
try {
|
|
1105
1125
|
const result = await schema.parseAsync(data);
|
|
1106
1126
|
return result;
|
|
1107
1127
|
} catch (error) {
|
|
1108
|
-
if (error instanceof
|
|
1128
|
+
if (error instanceof z29.ZodError) {
|
|
1109
1129
|
throw new ConfigError(file, error, data);
|
|
1110
1130
|
}
|
|
1111
1131
|
throw error;
|
|
@@ -1164,9 +1184,9 @@ var wrap = (lines, options) => {
|
|
|
1164
1184
|
return wrapAnsi(typeof lines === "string" ? lines : lines.join("\n"), process.stdout.columns - 8, options);
|
|
1165
1185
|
};
|
|
1166
1186
|
var padText = (texts) => {
|
|
1167
|
-
const size = Math.max(...texts.map((
|
|
1168
|
-
return (
|
|
1169
|
-
return
|
|
1187
|
+
const size = Math.max(...texts.map((text3) => text3.length));
|
|
1188
|
+
return (text3, padding = 0, fill) => {
|
|
1189
|
+
return text3.padEnd(size + padding, fill);
|
|
1170
1190
|
};
|
|
1171
1191
|
};
|
|
1172
1192
|
var task = async (message, cb) => {
|
|
@@ -1784,14 +1804,9 @@ var cacheFeature = defineFeature({
|
|
|
1784
1804
|
...props
|
|
1785
1805
|
});
|
|
1786
1806
|
ctx.onFunction(({ lambda }) => {
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
);
|
|
1791
|
-
lambda.addEnvironment(
|
|
1792
|
-
`CACHE_${constantCase2(ctx.stack.name)}_${constantCase2(id)}_PORT`,
|
|
1793
|
-
props.port.toString()
|
|
1794
|
-
);
|
|
1807
|
+
const prefix = `CACHE_${constantCase2(ctx.stack.name)}_${constantCase2(id)}`;
|
|
1808
|
+
lambda.addEnvironment(`${prefix}_HOST`, cluster.address);
|
|
1809
|
+
lambda.addEnvironment(`${prefix}_PORT`, props.port.toString());
|
|
1795
1810
|
});
|
|
1796
1811
|
}
|
|
1797
1812
|
}
|
|
@@ -2046,6 +2061,7 @@ var createLambdaFunction = (group, ctx, ns, id, local2) => {
|
|
|
2046
2061
|
}
|
|
2047
2062
|
const props = deepmerge(ctx.appConfig.defaults.function, local2);
|
|
2048
2063
|
ctx.registerBuild("function", name, async (build3) => {
|
|
2064
|
+
props.file;
|
|
2049
2065
|
const version = await fingerprintFromFile(props.file);
|
|
2050
2066
|
return build3(version, async (write) => {
|
|
2051
2067
|
const bundle = await bundleTypeScript({ file: props.file });
|
|
@@ -3906,6 +3922,90 @@ var siteFeature = defineFeature({
|
|
|
3906
3922
|
}
|
|
3907
3923
|
});
|
|
3908
3924
|
|
|
3925
|
+
// src/feature/rest/index.ts
|
|
3926
|
+
import { Node as Node18, aws as aws18 } from "@awsless/formation";
|
|
3927
|
+
var restFeature = defineFeature({
|
|
3928
|
+
name: "rest",
|
|
3929
|
+
onApp(ctx) {
|
|
3930
|
+
for (const [id, props] of Object.entries(ctx.appConfig.defaults?.rest ?? {})) {
|
|
3931
|
+
const group = new Node18(ctx.base, "rest", id);
|
|
3932
|
+
const api = new aws18.apiGatewayV2.Api(group, "api", {
|
|
3933
|
+
name: formatGlobalResourceName(ctx.app.name, "rest", id),
|
|
3934
|
+
protocolType: "HTTP"
|
|
3935
|
+
});
|
|
3936
|
+
const stage = new aws18.apiGatewayV2.Stage(group, "stage", {
|
|
3937
|
+
name: "v1",
|
|
3938
|
+
apiId: api.id
|
|
3939
|
+
});
|
|
3940
|
+
ctx.shared.set(`rest-${id}-id`, api.id);
|
|
3941
|
+
if (props.domain) {
|
|
3942
|
+
const domainName = formatFullDomainName(ctx.appConfig, props.domain, props.subDomain);
|
|
3943
|
+
const hostedZoneId = ctx.shared.get(`hosted-zone-${props.domain}-id`);
|
|
3944
|
+
const certificateArn = ctx.shared.get(`certificate-${props.domain}-arn`);
|
|
3945
|
+
const domain = new aws18.apiGatewayV2.DomainName(group, "domain", {
|
|
3946
|
+
name: domainName,
|
|
3947
|
+
certificates: [
|
|
3948
|
+
{
|
|
3949
|
+
certificateArn
|
|
3950
|
+
}
|
|
3951
|
+
]
|
|
3952
|
+
});
|
|
3953
|
+
const mapping = new aws18.apiGatewayV2.ApiMapping(group, "mapping", {
|
|
3954
|
+
apiId: api.id,
|
|
3955
|
+
domainName: domain.name,
|
|
3956
|
+
stage: stage.name
|
|
3957
|
+
});
|
|
3958
|
+
const record = new aws18.route53.RecordSet(group, "record", {
|
|
3959
|
+
hostedZoneId,
|
|
3960
|
+
type: "A",
|
|
3961
|
+
name: domainName,
|
|
3962
|
+
alias: {
|
|
3963
|
+
dnsName: domain.regionalDomainName,
|
|
3964
|
+
hostedZoneId: domain.regionalHostedZoneId,
|
|
3965
|
+
evaluateTargetHealth: false
|
|
3966
|
+
}
|
|
3967
|
+
});
|
|
3968
|
+
record.dependsOn(domain, mapping);
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3971
|
+
},
|
|
3972
|
+
onStack(ctx) {
|
|
3973
|
+
for (const [id, routes] of Object.entries(ctx.stackConfig.rest ?? {})) {
|
|
3974
|
+
const restGroup = new Node18(ctx.stack, "rest", id);
|
|
3975
|
+
for (const [routeKey, props] of Object.entries(routes)) {
|
|
3976
|
+
const group = new Node18(restGroup, "route", routeKey);
|
|
3977
|
+
const apiId = ctx.shared.get(`rest-${id}-id`);
|
|
3978
|
+
const routeId = shortId(routeKey);
|
|
3979
|
+
const { lambda } = createLambdaFunction(group, ctx, "rest", `${id}-${routeId}`, {
|
|
3980
|
+
...props,
|
|
3981
|
+
description: `${id} ${routeKey}`
|
|
3982
|
+
});
|
|
3983
|
+
const permission = new aws18.lambda.Permission(group, "permission", {
|
|
3984
|
+
action: "lambda:InvokeFunction",
|
|
3985
|
+
principal: "apigateway.amazonaws.com",
|
|
3986
|
+
functionArn: lambda.arn
|
|
3987
|
+
});
|
|
3988
|
+
const integration = new aws18.apiGatewayV2.Integration(group, "integration", {
|
|
3989
|
+
apiId,
|
|
3990
|
+
description: `${id} ${routeKey}`,
|
|
3991
|
+
method: "POST",
|
|
3992
|
+
payloadFormatVersion: "2.0",
|
|
3993
|
+
type: "AWS_PROXY",
|
|
3994
|
+
uri: lambda.arn.apply((arn) => {
|
|
3995
|
+
return `arn:aws:apigateway:${ctx.appConfig.region}:lambda:path/2015-03-31/functions/${arn}/invocations`;
|
|
3996
|
+
})
|
|
3997
|
+
});
|
|
3998
|
+
const route = new aws18.apiGatewayV2.Route(group, "route", {
|
|
3999
|
+
apiId,
|
|
4000
|
+
routeKey,
|
|
4001
|
+
target: integration.id.apply((id2) => `integrations/${id2}`)
|
|
4002
|
+
});
|
|
4003
|
+
route.dependsOn(lambda, permission);
|
|
4004
|
+
}
|
|
4005
|
+
}
|
|
4006
|
+
}
|
|
4007
|
+
});
|
|
4008
|
+
|
|
3909
4009
|
// src/feature/index.ts
|
|
3910
4010
|
var features = [
|
|
3911
4011
|
// 1
|
|
@@ -3928,6 +4028,7 @@ var features = [
|
|
|
3928
4028
|
cronFeature,
|
|
3929
4029
|
cacheFeature,
|
|
3930
4030
|
httpFeature,
|
|
4031
|
+
restFeature,
|
|
3931
4032
|
siteFeature
|
|
3932
4033
|
];
|
|
3933
4034
|
|
|
@@ -3971,6 +4072,8 @@ var createApp = (props, filters = []) => {
|
|
|
3971
4072
|
const builders = [];
|
|
3972
4073
|
const allFunctions = [];
|
|
3973
4074
|
const globalListeners = [];
|
|
4075
|
+
const allLocalListeners = {};
|
|
4076
|
+
const allLocalFunctions = {};
|
|
3974
4077
|
for (const feature of features) {
|
|
3975
4078
|
feature.onApp?.({
|
|
3976
4079
|
...props,
|
|
@@ -4000,6 +4103,8 @@ var createApp = (props, filters = []) => {
|
|
|
4000
4103
|
const localListeners = [];
|
|
4001
4104
|
const localFunctions = [];
|
|
4002
4105
|
const stack = new Stack(app, stackConfig.name);
|
|
4106
|
+
allLocalListeners[stack.name] = localListeners;
|
|
4107
|
+
allLocalFunctions[stack.name] = localFunctions;
|
|
4003
4108
|
for (const feature of features) {
|
|
4004
4109
|
feature.onStack?.({
|
|
4005
4110
|
...props,
|
|
@@ -4037,10 +4142,22 @@ var createApp = (props, filters = []) => {
|
|
|
4037
4142
|
listener(fn);
|
|
4038
4143
|
}
|
|
4039
4144
|
}
|
|
4145
|
+
for (const stackConfig of filterdStacks) {
|
|
4146
|
+
const functions = allLocalFunctions[stackConfig.name];
|
|
4147
|
+
for (const dependency of stackConfig.depends ?? []) {
|
|
4148
|
+
const listeners = allLocalListeners[dependency];
|
|
4149
|
+
for (const fn of functions) {
|
|
4150
|
+
for (const listener of listeners) {
|
|
4151
|
+
listener(fn);
|
|
4152
|
+
}
|
|
4153
|
+
}
|
|
4154
|
+
}
|
|
4155
|
+
}
|
|
4040
4156
|
return {
|
|
4041
4157
|
app,
|
|
4042
4158
|
base,
|
|
4043
4159
|
tests,
|
|
4160
|
+
shared,
|
|
4044
4161
|
configs,
|
|
4045
4162
|
builders
|
|
4046
4163
|
// deploymentLine,
|
|
@@ -4219,7 +4336,7 @@ var commands = [
|
|
|
4219
4336
|
list2
|
|
4220
4337
|
];
|
|
4221
4338
|
var config = (program2) => {
|
|
4222
|
-
const command = program2.command("config").description(`Manage app config
|
|
4339
|
+
const command = program2.command("config").description(`Manage app config parameters`);
|
|
4223
4340
|
commands.forEach((cb) => cb(command));
|
|
4224
4341
|
};
|
|
4225
4342
|
|
|
@@ -4227,20 +4344,20 @@ var config = (program2) => {
|
|
|
4227
4344
|
import { confirm as confirm3 } from "@clack/prompts";
|
|
4228
4345
|
|
|
4229
4346
|
// src/util/workspace.ts
|
|
4230
|
-
import { WorkSpace, aws as
|
|
4347
|
+
import { WorkSpace, aws as aws19, local } from "@awsless/formation";
|
|
4231
4348
|
import { minutes as minutes4 } from "@awsless/duration";
|
|
4232
4349
|
import { dirname as dirname8, join as join9 } from "path";
|
|
4233
4350
|
import { mkdir as mkdir2, readFile as readFile6, rm, writeFile as writeFile2 } from "fs/promises";
|
|
4234
4351
|
var createWorkSpace = (props) => {
|
|
4235
|
-
const lockProvider = new
|
|
4352
|
+
const lockProvider = new aws19.dynamodb.LockProvider({
|
|
4236
4353
|
...props,
|
|
4237
4354
|
tableName: "awsless-locks"
|
|
4238
4355
|
});
|
|
4239
|
-
const stateProvider = new
|
|
4356
|
+
const stateProvider = new aws19.s3.StateProvider({
|
|
4240
4357
|
...props,
|
|
4241
4358
|
bucket: "awsless-state"
|
|
4242
4359
|
});
|
|
4243
|
-
const cloudProviders =
|
|
4360
|
+
const cloudProviders = aws19.createCloudProviders({
|
|
4244
4361
|
...props,
|
|
4245
4362
|
timeout: minutes4(60)
|
|
4246
4363
|
});
|
|
@@ -4416,11 +4533,15 @@ var startTest = async (props) => {
|
|
|
4416
4533
|
include: ["**/*.{js,jsx,ts,tsx}"],
|
|
4417
4534
|
exclude: ["**/_*", "**/_*/**", ...configDefaults.exclude],
|
|
4418
4535
|
globals: true,
|
|
4419
|
-
reporters: props.reporter
|
|
4420
|
-
typecheck: {
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4536
|
+
reporters: props.reporter
|
|
4537
|
+
// typecheck: {
|
|
4538
|
+
// enabled: true,
|
|
4539
|
+
// // ignoreSourceErrors: false,
|
|
4540
|
+
// // checker: 'tsc',
|
|
4541
|
+
// // include: ['**/*.{js,jsx,ts,tsx}'],
|
|
4542
|
+
// // only: true,
|
|
4543
|
+
// // allowJs: true,
|
|
4544
|
+
// },
|
|
4424
4545
|
// outputFile: {
|
|
4425
4546
|
// json: './.awsless/test/output.json',
|
|
4426
4547
|
// },
|
|
@@ -4612,7 +4733,7 @@ var deploy = (program2) => {
|
|
|
4612
4733
|
};
|
|
4613
4734
|
|
|
4614
4735
|
// src/cli/command/diff.ts
|
|
4615
|
-
import { WorkSpace as WorkSpace2, aws as
|
|
4736
|
+
import { WorkSpace as WorkSpace2, aws as aws20 } from "@awsless/formation";
|
|
4616
4737
|
import chalk7 from "chalk";
|
|
4617
4738
|
var diff = (program2) => {
|
|
4618
4739
|
program2.command("diff").description("Diff your app with AWS").action(async (filters) => {
|
|
@@ -4624,12 +4745,12 @@ var diff = (program2) => {
|
|
|
4624
4745
|
const { app, builders } = createApp({ appConfig, stackConfigs, accountId }, filters);
|
|
4625
4746
|
await buildAssets(builders);
|
|
4626
4747
|
const workspace = new WorkSpace2({
|
|
4627
|
-
stateProvider: new
|
|
4748
|
+
stateProvider: new aws20.dynamodb.DynamoDBStateProvider({
|
|
4628
4749
|
credentials,
|
|
4629
4750
|
region,
|
|
4630
4751
|
tableName: "awsless-state"
|
|
4631
4752
|
}),
|
|
4632
|
-
cloudProviders:
|
|
4753
|
+
cloudProviders: aws20.createCloudProviders({
|
|
4633
4754
|
credentials,
|
|
4634
4755
|
region: appConfig.region
|
|
4635
4756
|
})
|
|
@@ -4879,8 +5000,103 @@ var state = (program2) => {
|
|
|
4879
5000
|
commands3.forEach((cb) => cb(command));
|
|
4880
5001
|
};
|
|
4881
5002
|
|
|
5003
|
+
// src/cli/command/auth/user/create.ts
|
|
5004
|
+
import { unwrap } from "@awsless/formation";
|
|
5005
|
+
import {
|
|
5006
|
+
AdminCreateUserCommand,
|
|
5007
|
+
AdminSetUserPasswordCommand,
|
|
5008
|
+
CognitoIdentityProviderClient
|
|
5009
|
+
} from "@aws-sdk/client-cognito-identity-provider";
|
|
5010
|
+
import { password, select, text as text2 } from "@clack/prompts";
|
|
5011
|
+
var create = (program2) => {
|
|
5012
|
+
program2.command("create").argument("[name]", "The name of the auth instance").description("Create an user for your userpool").action(async (name) => {
|
|
5013
|
+
await layout("auth user create", async ({ appConfig, stackConfigs }) => {
|
|
5014
|
+
const region = appConfig.region;
|
|
5015
|
+
const credentials = getCredentials(appConfig.profile);
|
|
5016
|
+
const accountId = await getAccountId(credentials, region);
|
|
5017
|
+
if (!name) {
|
|
5018
|
+
name = await select({
|
|
5019
|
+
message: "Select the auth userpool:",
|
|
5020
|
+
options: Object.keys(appConfig.defaults.auth).map((name2) => ({
|
|
5021
|
+
label: name2,
|
|
5022
|
+
value: name2
|
|
5023
|
+
}))
|
|
5024
|
+
});
|
|
5025
|
+
}
|
|
5026
|
+
if (!(name in appConfig.defaults.auth)) {
|
|
5027
|
+
throw new Error(`Provided auth name doesn't exist inside your app config.`);
|
|
5028
|
+
}
|
|
5029
|
+
const { shared, app } = createApp({ appConfig, stackConfigs, accountId });
|
|
5030
|
+
const { workspace } = createWorkSpace({
|
|
5031
|
+
credentials,
|
|
5032
|
+
region
|
|
5033
|
+
});
|
|
5034
|
+
await workspace.hydrate(app);
|
|
5035
|
+
let userPoolId;
|
|
5036
|
+
try {
|
|
5037
|
+
userPoolId = unwrap(shared.get(`auth-${name}-user-pool-id`));
|
|
5038
|
+
} catch (_) {
|
|
5039
|
+
throw new Error(`The auth userpool hasn't been deployed yet.`);
|
|
5040
|
+
}
|
|
5041
|
+
const user2 = await text2({
|
|
5042
|
+
message: "Username:",
|
|
5043
|
+
validate(value) {
|
|
5044
|
+
if (!value) {
|
|
5045
|
+
return "Required";
|
|
5046
|
+
}
|
|
5047
|
+
return;
|
|
5048
|
+
}
|
|
5049
|
+
});
|
|
5050
|
+
const pass = await password({
|
|
5051
|
+
message: "Password:",
|
|
5052
|
+
mask: "*",
|
|
5053
|
+
validate(value) {
|
|
5054
|
+
if (!value) {
|
|
5055
|
+
return "Required";
|
|
5056
|
+
}
|
|
5057
|
+
return;
|
|
5058
|
+
}
|
|
5059
|
+
});
|
|
5060
|
+
const client = new CognitoIdentityProviderClient({
|
|
5061
|
+
region,
|
|
5062
|
+
credentials
|
|
5063
|
+
});
|
|
5064
|
+
await client.send(
|
|
5065
|
+
new AdminCreateUserCommand({
|
|
5066
|
+
UserPoolId: userPoolId,
|
|
5067
|
+
Username: user2.toString(),
|
|
5068
|
+
TemporaryPassword: pass.toString()
|
|
5069
|
+
})
|
|
5070
|
+
);
|
|
5071
|
+
await client.send(
|
|
5072
|
+
new AdminSetUserPasswordCommand({
|
|
5073
|
+
UserPoolId: userPoolId,
|
|
5074
|
+
Username: user2.toString(),
|
|
5075
|
+
Password: pass.toString(),
|
|
5076
|
+
Permanent: true
|
|
5077
|
+
})
|
|
5078
|
+
);
|
|
5079
|
+
return "User created.";
|
|
5080
|
+
});
|
|
5081
|
+
});
|
|
5082
|
+
};
|
|
5083
|
+
|
|
5084
|
+
// src/cli/command/auth/user/index.ts
|
|
5085
|
+
var commands4 = [create];
|
|
5086
|
+
var user = (program2) => {
|
|
5087
|
+
const command = program2.command("user").description(`Manage auth users`);
|
|
5088
|
+
commands4.forEach((cb) => cb(command));
|
|
5089
|
+
};
|
|
5090
|
+
|
|
5091
|
+
// src/cli/command/auth/index.ts
|
|
5092
|
+
var commands5 = [user];
|
|
5093
|
+
var auth = (program2) => {
|
|
5094
|
+
const command = program2.command("auth").description(`Manage auth`);
|
|
5095
|
+
commands5.forEach((cb) => cb(command));
|
|
5096
|
+
};
|
|
5097
|
+
|
|
4882
5098
|
// src/cli/command/index.ts
|
|
4883
|
-
var
|
|
5099
|
+
var commands6 = [
|
|
4884
5100
|
bootstrap,
|
|
4885
5101
|
types,
|
|
4886
5102
|
build2,
|
|
@@ -4889,6 +5105,7 @@ var commands4 = [
|
|
|
4889
5105
|
del2,
|
|
4890
5106
|
dev,
|
|
4891
5107
|
// bind,
|
|
5108
|
+
auth,
|
|
4892
5109
|
state,
|
|
4893
5110
|
resource,
|
|
4894
5111
|
config,
|
|
@@ -4915,7 +5132,7 @@ program.on("option:skip-prompt", () => {
|
|
|
4915
5132
|
program.on("option:no-cache", () => {
|
|
4916
5133
|
process.env.NO_CACHE = program.opts().noCache ? "1" : void 0;
|
|
4917
5134
|
});
|
|
4918
|
-
|
|
5135
|
+
commands6.forEach((fn) => fn(program));
|
|
4919
5136
|
|
|
4920
5137
|
// src/bin.ts
|
|
4921
5138
|
program.parse(process.argv);
|