@reventlessdev/reventless-seed-aws 1.0.0-alpha.10

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.
Files changed (87) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/LICENSE +202 -0
  3. package/lib/bs/.compiler.log +2 -0
  4. package/lib/bs/.sourcedirs.json +1 -0
  5. package/lib/bs/build.ninja +0 -0
  6. package/lib/bs/compiler-info.json +8 -0
  7. package/lib/bs/src/ReventlessSeedAws.ast +0 -0
  8. package/lib/bs/src/ReventlessSeedAws.cmi +0 -0
  9. package/lib/bs/src/ReventlessSeedAws.cmj +0 -0
  10. package/lib/bs/src/ReventlessSeedAws.cmt +0 -0
  11. package/lib/bs/src/ReventlessSeedAws.res +328 -0
  12. package/lib/bs/src/ReventlessSeedAws.res.mjs +309 -0
  13. package/lib/bs/src/ReventlessSeedAws_Reconcile.ast +0 -0
  14. package/lib/bs/src/ReventlessSeedAws_Reconcile.cmi +0 -0
  15. package/lib/bs/src/ReventlessSeedAws_Reconcile.cmj +0 -0
  16. package/lib/bs/src/ReventlessSeedAws_Reconcile.cmt +0 -0
  17. package/lib/bs/src/ReventlessSeedAws_Reconcile.res +389 -0
  18. package/lib/bs/src/ReventlessSeedAws_Reconcile.res.mjs +318 -0
  19. package/lib/bs/src/ReventlessSeedAws_Reset.ast +0 -0
  20. package/lib/bs/src/ReventlessSeedAws_Reset.cmi +0 -0
  21. package/lib/bs/src/ReventlessSeedAws_Reset.cmj +0 -0
  22. package/lib/bs/src/ReventlessSeedAws_Reset.cmt +0 -0
  23. package/lib/bs/src/ReventlessSeedAws_Reset.res +948 -0
  24. package/lib/bs/src/ReventlessSeedAws_Reset.res.mjs +861 -0
  25. package/lib/bs/tests/EndpointResolutionTest.ast +0 -0
  26. package/lib/bs/tests/EndpointResolutionTest.cmi +0 -0
  27. package/lib/bs/tests/EndpointResolutionTest.cmj +0 -0
  28. package/lib/bs/tests/EndpointResolutionTest.cmt +0 -0
  29. package/lib/bs/tests/EndpointResolutionTest.res +83 -0
  30. package/lib/bs/tests/EndpointResolutionTest.res.mjs +115 -0
  31. package/lib/bs/tests/ObjectStoreReconcileTest.ast +0 -0
  32. package/lib/bs/tests/ObjectStoreReconcileTest.cmi +0 -0
  33. package/lib/bs/tests/ObjectStoreReconcileTest.cmj +0 -0
  34. package/lib/bs/tests/ObjectStoreReconcileTest.cmt +0 -0
  35. package/lib/bs/tests/ObjectStoreReconcileTest.res +84 -0
  36. package/lib/bs/tests/ObjectStoreReconcileTest.res.mjs +102 -0
  37. package/lib/bs/tests/ObjectStoreResetTest.ast +0 -0
  38. package/lib/bs/tests/ObjectStoreResetTest.cmi +0 -0
  39. package/lib/bs/tests/ObjectStoreResetTest.cmj +0 -0
  40. package/lib/bs/tests/ObjectStoreResetTest.cmt +0 -0
  41. package/lib/bs/tests/ObjectStoreResetTest.res +170 -0
  42. package/lib/bs/tests/ObjectStoreResetTest.res.mjs +211 -0
  43. package/lib/ocaml/.compiler.log +2 -0
  44. package/lib/ocaml/EndpointResolutionTest.ast +0 -0
  45. package/lib/ocaml/EndpointResolutionTest.cmi +0 -0
  46. package/lib/ocaml/EndpointResolutionTest.cmj +0 -0
  47. package/lib/ocaml/EndpointResolutionTest.cmt +0 -0
  48. package/lib/ocaml/EndpointResolutionTest.res +83 -0
  49. package/lib/ocaml/ObjectStoreReconcileTest.ast +0 -0
  50. package/lib/ocaml/ObjectStoreReconcileTest.cmi +0 -0
  51. package/lib/ocaml/ObjectStoreReconcileTest.cmj +0 -0
  52. package/lib/ocaml/ObjectStoreReconcileTest.cmt +0 -0
  53. package/lib/ocaml/ObjectStoreReconcileTest.res +84 -0
  54. package/lib/ocaml/ObjectStoreResetTest.ast +0 -0
  55. package/lib/ocaml/ObjectStoreResetTest.cmi +0 -0
  56. package/lib/ocaml/ObjectStoreResetTest.cmj +0 -0
  57. package/lib/ocaml/ObjectStoreResetTest.cmt +0 -0
  58. package/lib/ocaml/ObjectStoreResetTest.res +170 -0
  59. package/lib/ocaml/ReventlessSeedAws.ast +0 -0
  60. package/lib/ocaml/ReventlessSeedAws.cmi +0 -0
  61. package/lib/ocaml/ReventlessSeedAws.cmj +0 -0
  62. package/lib/ocaml/ReventlessSeedAws.cmt +0 -0
  63. package/lib/ocaml/ReventlessSeedAws.res +328 -0
  64. package/lib/ocaml/ReventlessSeedAws_Reconcile.ast +0 -0
  65. package/lib/ocaml/ReventlessSeedAws_Reconcile.cmi +0 -0
  66. package/lib/ocaml/ReventlessSeedAws_Reconcile.cmj +0 -0
  67. package/lib/ocaml/ReventlessSeedAws_Reconcile.cmt +0 -0
  68. package/lib/ocaml/ReventlessSeedAws_Reconcile.res +389 -0
  69. package/lib/ocaml/ReventlessSeedAws_Reset.ast +0 -0
  70. package/lib/ocaml/ReventlessSeedAws_Reset.cmi +0 -0
  71. package/lib/ocaml/ReventlessSeedAws_Reset.cmj +0 -0
  72. package/lib/ocaml/ReventlessSeedAws_Reset.cmt +0 -0
  73. package/lib/ocaml/ReventlessSeedAws_Reset.res +948 -0
  74. package/package.json +32 -0
  75. package/rescript.json +29 -0
  76. package/src/ReventlessSeedAws.res +328 -0
  77. package/src/ReventlessSeedAws.res.mjs +309 -0
  78. package/src/ReventlessSeedAws_Reconcile.res +389 -0
  79. package/src/ReventlessSeedAws_Reconcile.res.mjs +318 -0
  80. package/src/ReventlessSeedAws_Reset.res +948 -0
  81. package/src/ReventlessSeedAws_Reset.res.mjs +861 -0
  82. package/tests/EndpointResolutionTest.res +83 -0
  83. package/tests/EndpointResolutionTest.res.mjs +115 -0
  84. package/tests/ObjectStoreReconcileTest.res +84 -0
  85. package/tests/ObjectStoreReconcileTest.res.mjs +102 -0
  86. package/tests/ObjectStoreResetTest.res +170 -0
  87. package/tests/ObjectStoreResetTest.res.mjs +211 -0
@@ -0,0 +1,861 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Nodefs from "node:fs";
4
+ import * as S3$AwsSdk from "@reventlessdev/rescript-aws-sdk/src/S3.res.mjs";
5
+ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
6
+ import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
7
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
8
+ import * as Stdlib_String from "@rescript/runtime/lib/es6/Stdlib_String.js";
9
+ import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
10
+ import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
11
+ import * as ReventlessSeedAws from "./ReventlessSeedAws.res.mjs";
12
+ import * as ClientS3 from "@aws-sdk/client-s3";
13
+ import * as Seed$ReventlessSeed from "@reventlessdev/reventless-seed/src/Seed.res.mjs";
14
+ import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
15
+ import * as LibDynamodb from "@aws-sdk/lib-dynamodb";
16
+ import * as ClientDynamodb from "@aws-sdk/client-dynamodb";
17
+ import * as DynamoDb_DynamoDb$AwsSdk from "@reventlessdev/rescript-aws-sdk/src/DynamoDb_DynamoDb.res.mjs";
18
+ import * as Seed_Prompt$ReventlessSeed from "@reventlessdev/reventless-seed/src/Seed_Prompt.res.mjs";
19
+ import * as Seed_Runner$ReventlessSeed from "@reventlessdev/reventless-seed/src/Seed_Runner.res.mjs";
20
+ import * as DynamoDb_DocumentClient$AwsSdk from "@reventlessdev/rescript-aws-sdk/src/DynamoDb_DocumentClient.res.mjs";
21
+ import * as ResourceGroupsTaggingApi$AwsSdk from "@reventlessdev/rescript-aws-sdk/src/ResourceGroupsTaggingApi.res.mjs";
22
+ import * as ClientResourceGroupsTaggingApi from "@aws-sdk/client-resource-groups-tagging-api";
23
+
24
+ function projectName(projectDir) {
25
+ let path = projectDir + `/Pulumi.yaml`;
26
+ let raw;
27
+ try {
28
+ raw = Nodefs.readFileSync(path, "utf8");
29
+ } catch (exn) {
30
+ throw {
31
+ RE_EXN_ID: Seed$ReventlessSeed.Failed,
32
+ _1: `could not read ` + path + ` to scope the wipe to this Pulumi project.`,
33
+ Error: new Error()
34
+ };
35
+ }
36
+ let line = raw.split("\n").find(line => line.trim().startsWith("name:"));
37
+ if (line !== undefined) {
38
+ let trimmed = line.trim();
39
+ return trimmed.slice("name:".length, trimmed.length).trim().replace(/^[\"']|[\"']$/g, "");
40
+ }
41
+ throw {
42
+ RE_EXN_ID: Seed$ReventlessSeed.Failed,
43
+ _1: `could not find a \`name:\` field in ` + path + `.`,
44
+ Error: new Error()
45
+ };
46
+ }
47
+
48
+ function field(json, key) {
49
+ if (typeof json === "object" && json !== null && !Array.isArray(json)) {
50
+ return json[key];
51
+ }
52
+ }
53
+
54
+ function asString(json) {
55
+ if (typeof json === "string") {
56
+ return json;
57
+ }
58
+ }
59
+
60
+ function chunk(arr, size) {
61
+ let out = [];
62
+ let i = 0;
63
+ while (i < arr.length) {
64
+ out.push(arr.slice(i, i + size | 0));
65
+ i = i + size | 0;
66
+ };
67
+ return out;
68
+ }
69
+
70
+ let nameAllowlist = /^(alpha|dev|pr-.+)$/;
71
+
72
+ function readStackConfig(projectDir, backend, stack) {
73
+ let raw;
74
+ try {
75
+ raw = ReventlessSeedAws.pulumi(projectDir, backend, [
76
+ "config",
77
+ "--json",
78
+ "--stack",
79
+ stack
80
+ ]);
81
+ } catch (exn) {
82
+ raw = undefined;
83
+ }
84
+ if (raw === undefined) {
85
+ return {
86
+ TAG: "Error",
87
+ _0: `could not read the Pulumi config for stack "` + stack + `" — is pulumi logged in to the right backend, and is the stack deployed?`
88
+ };
89
+ }
90
+ let match;
91
+ try {
92
+ match = JSON.parse(raw);
93
+ } catch (exn$1) {
94
+ match = undefined;
95
+ }
96
+ if (typeof match === "object" && match !== null && !Array.isArray(match)) {
97
+ return {
98
+ TAG: "Ok",
99
+ _0: match
100
+ };
101
+ }
102
+ return {
103
+ TAG: "Error",
104
+ _0: `could not parse \`pulumi config --json\` output for stack "` + stack + `".`
105
+ };
106
+ }
107
+
108
+ function configValue(cfg, key) {
109
+ return Stdlib_Option.flatMap(Stdlib_Option.flatMap(cfg[key], v => field(v, "value")), asString);
110
+ }
111
+
112
+ function classify(arn) {
113
+ if (!arn.startsWith("arn:aws:dynamodb:")) {
114
+ if (arn.startsWith("arn:aws:s3:::")) {
115
+ return {
116
+ TAG: "Bucket",
117
+ _0: arn.slice("arn:aws:s3:::".length, arn.length)
118
+ };
119
+ } else {
120
+ return "Other";
121
+ }
122
+ }
123
+ let match = arn.split(":table/");
124
+ if (match.length !== 2) {
125
+ return "Other";
126
+ }
127
+ let name = match[1];
128
+ return {
129
+ TAG: "Table",
130
+ _0: name
131
+ };
132
+ }
133
+
134
+ function tagValue(tags, key) {
135
+ return Stdlib_Array.findMap(tags, t => {
136
+ if (t.Key === key) {
137
+ return t.Value;
138
+ }
139
+ });
140
+ }
141
+
142
+ async function discover(region, stack, platform) {
143
+ let client = ResourceGroupsTaggingApi$AwsSdk.client(region, undefined);
144
+ let tables = [];
145
+ let buckets = [];
146
+ let loop = async token => {
147
+ let out = await client.send(new ClientResourceGroupsTaggingApi.GetResourcesCommand({
148
+ TagFilters: [
149
+ {
150
+ Key: "reventless:platform",
151
+ Values: [platform]
152
+ },
153
+ {
154
+ Key: "reventless:environment",
155
+ Values: [stack]
156
+ }
157
+ ],
158
+ ResourceTypeFilters: [
159
+ "dynamodb:table",
160
+ "s3"
161
+ ],
162
+ ResourcesPerPage: 100,
163
+ PaginationToken: token
164
+ }));
165
+ Stdlib_Option.getOr(out.ResourceTagMappingList, []).forEach(m => {
166
+ let tags = Stdlib_Option.getOr(m.Tags, []);
167
+ let assertTag = (key, expected) => {
168
+ let v = tagValue(tags, key);
169
+ if (v !== undefined && v === expected) {
170
+ return;
171
+ }
172
+ throw {
173
+ RE_EXN_ID: Seed$ReventlessSeed.Failed,
174
+ _1: `refusing: discovered resource ` + m.ResourceARN + ` carries ` + key + `=` + Stdlib_Option.getOr(v, "<none>") + `, not "` + expected + `".`,
175
+ Error: new Error()
176
+ };
177
+ };
178
+ assertTag("reventless:platform", platform);
179
+ assertTag("reventless:environment", stack);
180
+ let name = classify(m.ResourceARN);
181
+ if (typeof name !== "object") {
182
+ return;
183
+ }
184
+ if (name.TAG === "Table") {
185
+ tables.push(name._0);
186
+ return;
187
+ }
188
+ buckets.push(name._0);
189
+ });
190
+ let t = out.PaginationToken;
191
+ if (t !== undefined && t !== "") {
192
+ return await loop(t);
193
+ }
194
+ };
195
+ await loop(undefined);
196
+ return [
197
+ tables,
198
+ buckets
199
+ ];
200
+ }
201
+
202
+ function splitQualified(key) {
203
+ return Stdlib_Option.map(Stdlib_String.indexOfOpt(key, "."), i => [
204
+ key.slice(0, i),
205
+ key.slice(i + 1 | 0, key.length)
206
+ ]);
207
+ }
208
+
209
+ function parseObjectStores(json) {
210
+ if (json !== undefined) {
211
+ if (typeof json === "object" && json !== null && !Array.isArray(json)) {
212
+ return Stdlib_Array.reduce(Object.entries(json), {
213
+ TAG: "Ok",
214
+ _0: []
215
+ }, (acc, param) => {
216
+ let entry = param[1];
217
+ let qualified = param[0];
218
+ if (acc.TAG !== "Ok") {
219
+ return acc;
220
+ }
221
+ let match = splitQualified(qualified);
222
+ let match$1 = Stdlib_Option.flatMap(field(entry, "bucketName"), asString);
223
+ let match$2 = Stdlib_Option.flatMap(field(entry, "keyPrefix"), asString);
224
+ if (match !== undefined && match$1 !== undefined && match$2 !== undefined) {
225
+ return {
226
+ TAG: "Ok",
227
+ _0: acc._0.concat([{
228
+ qualified: qualified,
229
+ plugin: match[0],
230
+ store: match[1],
231
+ bucketName: match$1,
232
+ keyPrefix: match$2
233
+ }])
234
+ };
235
+ }
236
+ return {
237
+ TAG: "Error",
238
+ _0: `the platform stack's \`objectStores\` output has a malformed entry for "` + qualified + `" — expected a {plugin}.{store} key carrying bucketName and keyPrefix.`
239
+ };
240
+ });
241
+ } else {
242
+ return {
243
+ TAG: "Error",
244
+ _0: "the platform stack's `objectStores` output is not an object."
245
+ };
246
+ }
247
+ } else {
248
+ return {
249
+ TAG: "Ok",
250
+ _0: []
251
+ };
252
+ }
253
+ }
254
+
255
+ function validateStores(stores) {
256
+ let s = stores.find(s => {
257
+ if (s.keyPrefix === "") {
258
+ return true;
259
+ } else {
260
+ return s.store.includes("/");
261
+ }
262
+ });
263
+ if (s !== undefined) {
264
+ return {
265
+ TAG: "Error",
266
+ _0: `store "` + s.qualified + `" has an unusable key prefix ("` + s.keyPrefix + `") — a store name may not be empty or contain "/".`
267
+ };
268
+ }
269
+ let message = Stdlib_Array.findMap(stores, a => Stdlib_Array.findMap(stores, b => {
270
+ if (a.qualified === b.qualified || a.bucketName !== b.bucketName) {
271
+ return;
272
+ } else if (a.keyPrefix === b.keyPrefix) {
273
+ return `stores "` + a.qualified + `" and "` + b.qualified + `" both live at ` + (a.bucketName + `/` + a.keyPrefix + `/ — a prefix-scoped wipe cannot tell their objects `) + `apart. Rename one store, or qualify the \`@storageRef\` annotation if they were meant to be one shared store.`;
274
+ } else if (b.keyPrefix.startsWith(a.keyPrefix + "/")) {
275
+ return `store "` + a.qualified + `" (` + a.bucketName + `/` + a.keyPrefix + `/) encloses "` + b.qualified + `" ` + (`(` + b.keyPrefix + `/) — wiping the first would delete the second's objects. Rename one.`);
276
+ } else {
277
+ return;
278
+ }
279
+ }));
280
+ if (message !== undefined) {
281
+ return {
282
+ TAG: "Error",
283
+ _0: message
284
+ };
285
+ } else {
286
+ return {
287
+ TAG: "Ok",
288
+ _0: undefined
289
+ };
290
+ }
291
+ }
292
+
293
+ async function countTable(table) {
294
+ let loop = async (start, acc) => {
295
+ let out = await DynamoDb_DocumentClient$AwsSdk.ScanCommand.send(new LibDynamodb.ScanCommand({
296
+ TableName: table,
297
+ ExclusiveStartKey: start,
298
+ Select: "COUNT"
299
+ }));
300
+ let acc$1 = acc + Stdlib_Option.getOr(out.Count, 0) | 0;
301
+ let k = out.LastEvaluatedKey;
302
+ if (k !== undefined) {
303
+ return await loop(k, acc$1);
304
+ } else {
305
+ return acc$1;
306
+ }
307
+ };
308
+ return await loop(undefined, 0);
309
+ }
310
+
311
+ async function countBucket(bucket, prefix) {
312
+ let loop = async (keyMarker, versionMarker, acc) => {
313
+ let out = await S3$AwsSdk.ListObjectVersionsCommand.send(new ClientS3.ListObjectVersionsCommand({
314
+ Bucket: bucket,
315
+ Prefix: prefix,
316
+ KeyMarker: keyMarker,
317
+ VersionIdMarker: versionMarker
318
+ }));
319
+ let n = Stdlib_Option.getOr(out.Versions, []).length + Stdlib_Option.getOr(out.DeleteMarkers, []).length | 0;
320
+ if (Stdlib_Option.getOr(out.IsTruncated, false)) {
321
+ return await loop(out.NextKeyMarker, out.NextVersionIdMarker, acc + n | 0);
322
+ } else {
323
+ return acc + n | 0;
324
+ }
325
+ };
326
+ return await loop(undefined, undefined, 0);
327
+ }
328
+
329
+ async function sendBatch(table, requests, attempt) {
330
+ if (requests.length === 0) {
331
+ return;
332
+ }
333
+ if (attempt > 8) {
334
+ throw {
335
+ RE_EXN_ID: Seed$ReventlessSeed.Failed,
336
+ _1: `table ` + table + `: ` + requests.length.toString() + ` item(s) still unprocessed after 8 retries.`,
337
+ Error: new Error()
338
+ };
339
+ }
340
+ let out = await DynamoDb_DocumentClient$AwsSdk.BatchWriteCommand.send(new LibDynamodb.BatchWriteCommand({
341
+ RequestItems: Object.fromEntries([[
342
+ table,
343
+ requests
344
+ ]])
345
+ }));
346
+ let unprocessed = Stdlib_Option.getOr(Stdlib_Option.flatMap(out.UnprocessedItems, d => d[table]), []);
347
+ if (unprocessed.length !== 0) {
348
+ return await sendBatch(table, unprocessed, attempt + 1 | 0);
349
+ }
350
+ }
351
+
352
+ async function truncateTable(table) {
353
+ let desc = await DynamoDb_DynamoDb$AwsSdk.DescribeTableCommand.send(new ClientDynamodb.DescribeTableCommand({
354
+ TableName: table
355
+ }));
356
+ let keyAttrs = Stdlib_Option.getOr(Stdlib_Option.flatMap(desc.Table, t => t.KeySchema), []).map(k => k.AttributeName);
357
+ if (keyAttrs.length === 0) {
358
+ throw {
359
+ RE_EXN_ID: Seed$ReventlessSeed.Failed,
360
+ _1: `could not read a key schema for table ` + table + `.`,
361
+ Error: new Error()
362
+ };
363
+ }
364
+ let names = keyAttrs.map((name, i) => [
365
+ `#k` + i.toString(),
366
+ name
367
+ ]);
368
+ let projection = names.map(param => param[0]).join(", ");
369
+ let loop = async start => {
370
+ let out = await DynamoDb_DocumentClient$AwsSdk.ScanCommand.send(new LibDynamodb.ScanCommand({
371
+ TableName: table,
372
+ ExclusiveStartKey: start,
373
+ ExpressionAttributeNames: Object.fromEntries(names),
374
+ ProjectionExpression: projection
375
+ }));
376
+ let requests = Stdlib_Option.getOr(out.Items, []).map(item => {
377
+ let key = Object.fromEntries(Stdlib_Array.filterMap(keyAttrs, attr => Stdlib_Option.map(field(item, attr), v => [
378
+ attr,
379
+ v
380
+ ])));
381
+ return {
382
+ DeleteRequest: {
383
+ Key: key
384
+ }
385
+ };
386
+ });
387
+ let batches = chunk(requests, DynamoDb_DocumentClient$AwsSdk.BatchWriteCommand.maxBatchSize);
388
+ for (let i = 0, i_finish = batches.length; i < i_finish; ++i) {
389
+ let b = batches[i];
390
+ if (b !== undefined) {
391
+ await sendBatch(table, b, 1);
392
+ }
393
+ }
394
+ let k = out.LastEvaluatedKey;
395
+ if (k !== undefined) {
396
+ return await loop(k);
397
+ }
398
+ };
399
+ return await loop(undefined);
400
+ }
401
+
402
+ async function emptyBucket(bucket, prefix) {
403
+ let loop = async (keyMarker, versionMarker) => {
404
+ let out = await S3$AwsSdk.ListObjectVersionsCommand.send(new ClientS3.ListObjectVersionsCommand({
405
+ Bucket: bucket,
406
+ Prefix: prefix,
407
+ KeyMarker: keyMarker,
408
+ VersionIdMarker: versionMarker
409
+ }));
410
+ let ids = Stdlib_Option.getOr(out.Versions, []).concat(Stdlib_Option.getOr(out.DeleteMarkers, [])).map(v => ({
411
+ Key: v.Key,
412
+ VersionId: v.VersionId
413
+ }));
414
+ if (ids.length !== 0) {
415
+ let res = await S3$AwsSdk.DeleteObjectsCommand.send(new ClientS3.DeleteObjectsCommand({
416
+ Bucket: bucket,
417
+ Delete: {
418
+ Objects: ids,
419
+ Quiet: true
420
+ }
421
+ }));
422
+ let errs = res.Errors;
423
+ if (errs !== undefined && errs.length !== 0) {
424
+ throw {
425
+ RE_EXN_ID: Seed$ReventlessSeed.Failed,
426
+ _1: `failed to delete ` + errs.length.toString() + ` object(s) from ` + bucket + `: ` + Stdlib_Option.getOr(Stdlib_Option.flatMap(errs[0], e => e.Message), "unknown"),
427
+ Error: new Error()
428
+ };
429
+ }
430
+ }
431
+ if (Stdlib_Option.getOr(out.IsTruncated, false)) {
432
+ return await loop(out.NextKeyMarker, out.NextVersionIdMarker);
433
+ }
434
+ };
435
+ return await loop(undefined, undefined);
436
+ }
437
+
438
+ function pluginOf(t) {
439
+ return Stdlib_Option.getOr(t.plugin, t.label);
440
+ }
441
+
442
+ async function chooseScope(targets) {
443
+ let domain = targets.filter(t => t.group === "Domain");
444
+ let platform = targets.filter(t => t.group === "Platform");
445
+ let labelsOf = ts => ts.map(t => t.label).join(", ");
446
+ let other = Stdlib_Option.map(Seed_Prompt$ReventlessSeed.envValue("SEED_RESET_SCOPE"), prim => prim.toLowerCase());
447
+ if (other !== undefined) {
448
+ switch (other) {
449
+ case "domain" :
450
+ return domain;
451
+ case "all" :
452
+ case "both" :
453
+ case "everything" :
454
+ return targets;
455
+ case "platform" :
456
+ return platform;
457
+ default:
458
+ let t = targets.find(t => t.label.toLowerCase() === other);
459
+ if (t !== undefined) {
460
+ return [t];
461
+ }
462
+ throw {
463
+ RE_EXN_ID: Seed$ReventlessSeed.Failed,
464
+ _1: `SEED_RESET_SCOPE="` + other + `" is not a scope — use domain, platform, everything, or a plugin label (` + labelsOf(domain) + `).`,
465
+ Error: new Error()
466
+ };
467
+ }
468
+ } else {
469
+ let options = [];
470
+ if (domain.length !== 0) {
471
+ options.push([
472
+ `domain — ` + labelsOf(domain),
473
+ domain
474
+ ]);
475
+ if (domain.length > 1) {
476
+ domain.forEach(t => {
477
+ options.push([
478
+ t.label,
479
+ [t]
480
+ ]);
481
+ });
482
+ }
483
+ }
484
+ if (platform.length !== 0) {
485
+ options.push([
486
+ `platform — ` + labelsOf(platform),
487
+ platform
488
+ ]);
489
+ }
490
+ if (domain.length !== 0 && platform.length !== 0) {
491
+ options.push([
492
+ `everything — ` + labelsOf(targets),
493
+ targets
494
+ ]);
495
+ }
496
+ return await Seed_Prompt$ReventlessSeed.select("Reset scope:", options, undefined);
497
+ }
498
+ }
499
+
500
+ function gateTarget(target, backend, stack) {
501
+ let c = readStackConfig(target.projectDir, backend, stack);
502
+ let cfg;
503
+ if (c.TAG === "Ok") {
504
+ cfg = c._0;
505
+ } else {
506
+ throw {
507
+ RE_EXN_ID: Seed$ReventlessSeed.Failed,
508
+ _1: c._0,
509
+ Error: new Error()
510
+ };
511
+ }
512
+ let match = Stdlib_Option.map(configValue(cfg, "reventless:wipeable"), v => v.trim().toLowerCase());
513
+ let exit = 0;
514
+ if (match !== "true") {
515
+ exit = 1;
516
+ }
517
+ if (exit === 1) {
518
+ throw {
519
+ RE_EXN_ID: Seed$ReventlessSeed.Failed,
520
+ _1: target.label + `: stack "` + stack + `" does not declare \`reventless:wipeable: "true"\` in its Pulumi.` + stack + `.yaml — refusing. Add that line only on disposable dev stacks (see the alpha example configs).`,
521
+ Error: new Error()
522
+ };
523
+ }
524
+ let r = Seed_Prompt$ReventlessSeed.envValue("AWS_REGION");
525
+ let exit$1 = 0;
526
+ if (r !== undefined) {
527
+ if (r !== "") {
528
+ return r;
529
+ }
530
+ exit$1 = 1;
531
+ } else {
532
+ exit$1 = 1;
533
+ }
534
+ if (exit$1 === 1) {
535
+ let r$1 = configValue(cfg, "aws:region");
536
+ let exit$2 = 0;
537
+ if (r$1 !== undefined) {
538
+ if (r$1 !== "") {
539
+ return r$1;
540
+ }
541
+ exit$2 = 2;
542
+ } else {
543
+ exit$2 = 2;
544
+ }
545
+ if (exit$2 === 2) {
546
+ throw {
547
+ RE_EXN_ID: Seed$ReventlessSeed.Failed,
548
+ _1: target.label + `: could not resolve the AWS region — set AWS_REGION or \`aws:region\` in the stack config.`,
549
+ Error: new Error()
550
+ };
551
+ }
552
+ }
553
+ }
554
+
555
+ function reportAll(resolvedList, stack, region) {
556
+ Seed_Runner$ReventlessSeed.heading(`Reset target: stack "` + stack + `" in ` + region);
557
+ let total = {
558
+ contents: 0
559
+ };
560
+ resolvedList.forEach(r => {
561
+ console.log("");
562
+ console.log(` ` + r.target.label + ` (` + r.platform + `)`);
563
+ console.log(" DynamoDB tables:");
564
+ r.tables.forEach((t, i) => {
565
+ let c = Stdlib_Option.getOr(r.tableCounts[i], 0);
566
+ total.contents = total.contents + c | 0;
567
+ console.log(` ` + c.toString().padStart(8, " ") + ` ` + t);
568
+ });
569
+ if (r.tables.length === 0) {
570
+ console.log(" (none)");
571
+ }
572
+ console.log(" S3 buckets:");
573
+ r.bucketCounts.forEach(param => {
574
+ let c = param[1];
575
+ total.contents = total.contents + c | 0;
576
+ console.log(` ` + c.toString().padStart(8, " ") + ` ` + param[0]);
577
+ });
578
+ if (r.bucketCounts.length === 0) {
579
+ console.log(" (none)");
580
+ }
581
+ if (r.storeCounts.length !== 0) {
582
+ console.log(" Object stores:");
583
+ r.storeCounts.forEach(param => {
584
+ let c = param[1];
585
+ let s = param[0];
586
+ total.contents = total.contents + c | 0;
587
+ console.log(` ` + c.toString().padStart(8, " ") + ` ` + s.qualified + ` ` + (s.bucketName + `/` + s.keyPrefix + `/`));
588
+ });
589
+ return;
590
+ }
591
+ });
592
+ return total.contents;
593
+ }
594
+
595
+ function run(stack, backend, targets, param) {
596
+ let go = async () => {
597
+ try {
598
+ if (targets.length === 0) {
599
+ throw {
600
+ RE_EXN_ID: Seed$ReventlessSeed.Failed,
601
+ _1: "no targets were declared — nothing to reset.",
602
+ Error: new Error()
603
+ };
604
+ }
605
+ let url = Seed_Prompt$ReventlessSeed.envValue("SEED_PULUMI_BACKEND");
606
+ let backend$1 = url !== undefined ? url : backend;
607
+ let baseTarget = Stdlib_Option.getOr(targets.find(t => t.group === "Platform"), targets[0]);
608
+ let stack$1 = await ReventlessSeedAws.resolveStack(baseTarget.projectDir, backend$1, stack);
609
+ if (!nameAllowlist.test(stack$1)) {
610
+ throw {
611
+ RE_EXN_ID: Seed$ReventlessSeed.Failed,
612
+ _1: `stack "` + stack$1 + `" is not on the wipe name-allowlist (alpha, dev, pr-*) — refusing.`,
613
+ Error: new Error()
614
+ };
615
+ }
616
+ let selected = await chooseScope(targets);
617
+ let platformTarget = targets.find(t => t.group === "Platform");
618
+ let allStores;
619
+ if (platformTarget !== undefined) {
620
+ let output = ReventlessSeedAws.stackOutputs(platformTarget.projectDir, backend$1, stack$1);
621
+ let stores = parseObjectStores(field(output, "objectStores"));
622
+ if (stores.TAG === "Ok") {
623
+ allStores = stores._0;
624
+ } else {
625
+ throw {
626
+ RE_EXN_ID: Seed$ReventlessSeed.Failed,
627
+ _1: stores._0,
628
+ Error: new Error()
629
+ };
630
+ }
631
+ } else {
632
+ console.log("");
633
+ console.log("Note: no `platform` target is declared, so declared object stores could not be resolved — any uploaded objects will be left in place.");
634
+ allStores = [];
635
+ }
636
+ let message = validateStores(allStores);
637
+ if (message.TAG !== "Ok") {
638
+ throw {
639
+ RE_EXN_ID: Seed$ReventlessSeed.Failed,
640
+ _1: `refusing: ` + message._0,
641
+ Error: new Error()
642
+ };
643
+ }
644
+ let storeBucketNames = allStores.map(s => s.bucketName);
645
+ let selectedStores = allStores.filter(s => selected.some(t => pluginOf(t) === s.plugin));
646
+ let match = selectedStores.length !== 0;
647
+ let gated = match && platformTarget !== undefined && !selected.some(t => t.projectDir === platformTarget.projectDir) ? selected.concat([platformTarget]) : selected;
648
+ let regions = gated.map(t => gateTarget(t, backend$1, stack$1));
649
+ let region = regions[0];
650
+ if (regions.some(r => r !== region)) {
651
+ throw {
652
+ RE_EXN_ID: Seed$ReventlessSeed.Failed,
653
+ _1: `selected targets span more than one region (` + regions.join(", ") + `) — reset them one region at a time.`,
654
+ Error: new Error()
655
+ };
656
+ }
657
+ process.env["AWS_REGION"] = region;
658
+ if (selectedStores.length !== 0 && platformTarget !== undefined) {
659
+ let platformProject = projectName(platformTarget.projectDir);
660
+ let match$1 = await discover(region, stack$1, platformProject);
661
+ let platformBuckets = match$1[1];
662
+ selectedStores.forEach(s => {
663
+ if (platformBuckets.includes(s.bucketName)) {
664
+ return;
665
+ }
666
+ throw {
667
+ RE_EXN_ID: Seed$ReventlessSeed.Failed,
668
+ _1: `refusing: store "` + s.qualified + `" names bucket ` + s.bucketName + `, which does not ` + (`carry reventless:platform=` + platformProject + ` + reventless:environment=` + stack$1 + `.`),
669
+ Error: new Error()
670
+ };
671
+ });
672
+ }
673
+ let resolvedList = [];
674
+ for (let i = 0, i_finish = selected.length; i < i_finish; ++i) {
675
+ let target = selected[i];
676
+ if (target !== undefined) {
677
+ let platform = projectName(target.projectDir);
678
+ let match$2 = await discover(region, stack$1, platform);
679
+ let tables = match$2[0].toSorted(Primitive_string.compare);
680
+ let buckets = match$2[1].filter(b => !storeBucketNames.includes(b)).toSorted(Primitive_string.compare);
681
+ let tableCounts = [];
682
+ for (let j = 0, j_finish = tables.length; j < j_finish; ++j) {
683
+ let t = tables[j];
684
+ if (t !== undefined) {
685
+ tableCounts.push(await countTable(t));
686
+ }
687
+ }
688
+ let bucketCounts = [];
689
+ for (let j$1 = 0, j_finish$1 = buckets.length; j$1 < j_finish$1; ++j$1) {
690
+ let b = buckets[j$1];
691
+ if (b !== undefined) {
692
+ bucketCounts.push([
693
+ b,
694
+ await countBucket(b, undefined)
695
+ ]);
696
+ }
697
+ }
698
+ let stores$1 = selectedStores.filter(s => s.plugin === pluginOf(target));
699
+ let storeCounts = [];
700
+ for (let j$2 = 0, j_finish$2 = stores$1.length; j$2 < j_finish$2; ++j$2) {
701
+ let s = stores$1[j$2];
702
+ if (s !== undefined) {
703
+ storeCounts.push([
704
+ s,
705
+ await countBucket(s.bucketName, s.keyPrefix + `/`)
706
+ ]);
707
+ }
708
+ }
709
+ resolvedList.push({
710
+ target: target,
711
+ platform: platform,
712
+ tables: tables,
713
+ tableCounts: tableCounts,
714
+ bucketCounts: bucketCounts,
715
+ storeCounts: storeCounts
716
+ });
717
+ }
718
+ }
719
+ let total = reportAll(resolvedList, stack$1, region);
720
+ if (total === 0) {
721
+ Seed_Prompt$ReventlessSeed.close();
722
+ console.log("");
723
+ console.log(`Nothing to reset — the selected scope already reads empty in ` + region + `.`);
724
+ process.exit(0);
725
+ }
726
+ let interactive = Stdlib_Option.getOr(process.stdin.isTTY, false);
727
+ let confirmed;
728
+ if (interactive) {
729
+ let typed = await Seed_Prompt$ReventlessSeed.ask(`About to permanently empty ` + total.toString() + ` item(s)/object(s) across the selected scope of "` + stack$1 + `". Type the stack name to confirm, or press Enter to keep this a dry run: `);
730
+ confirmed = typed.trim() === stack$1;
731
+ } else {
732
+ confirmed = Primitive_object.equal(Seed_Prompt$ReventlessSeed.envValue("REVENTLESS_WIPE_CONFIRM"), stack$1);
733
+ }
734
+ Seed_Prompt$ReventlessSeed.close();
735
+ if (!confirmed) {
736
+ console.log("");
737
+ console.log(interactive ? "Dry run — nothing was deleted." : `Dry run — nothing was deleted. Set REVENTLESS_WIPE_CONFIRM=` + stack$1 + ` to empty this scope non-interactively.`);
738
+ process.exit(0);
739
+ }
740
+ for (let i$1 = 0, i_finish$1 = resolvedList.length; i$1 < i_finish$1; ++i$1) {
741
+ let r = resolvedList[i$1];
742
+ if (r !== undefined) {
743
+ Seed_Runner$ReventlessSeed.heading(`Emptying ` + r.target.label + ` …`);
744
+ for (let j$3 = 0, j_finish$3 = r.tables.length; j$3 < j_finish$3; ++j$3) {
745
+ let t$1 = r.tables[j$3];
746
+ if (t$1 !== undefined) {
747
+ await truncateTable(t$1);
748
+ console.log(` truncated ` + t$1);
749
+ }
750
+ }
751
+ for (let j$4 = 0, j_finish$4 = r.bucketCounts.length; j$4 < j_finish$4; ++j$4) {
752
+ let match$3 = r.bucketCounts[j$4];
753
+ if (match$3 !== undefined) {
754
+ let b$1 = match$3[0];
755
+ await emptyBucket(b$1, undefined);
756
+ console.log(` emptied ` + b$1);
757
+ }
758
+ }
759
+ for (let j$5 = 0, j_finish$5 = r.storeCounts.length; j$5 < j_finish$5; ++j$5) {
760
+ let match$4 = r.storeCounts[j$5];
761
+ if (match$4 !== undefined) {
762
+ let s$1 = match$4[0];
763
+ await emptyBucket(s$1.bucketName, s$1.keyPrefix + `/`);
764
+ console.log(` emptied ` + s$1.qualified + ` — ` + match$4[1].toString() + ` object(s) removed from ` + (s$1.bucketName + `/` + s$1.keyPrefix + `/`));
765
+ }
766
+ }
767
+ }
768
+ }
769
+ let remaining = 0;
770
+ for (let i$2 = 0, i_finish$2 = resolvedList.length; i$2 < i_finish$2; ++i$2) {
771
+ let r$1 = resolvedList[i$2];
772
+ if (r$1 !== undefined) {
773
+ for (let j$6 = 0, j_finish$6 = r$1.tables.length; j$6 < j_finish$6; ++j$6) {
774
+ let t$2 = r$1.tables[j$6];
775
+ if (t$2 !== undefined) {
776
+ remaining = remaining + await countTable(t$2) | 0;
777
+ }
778
+ }
779
+ for (let j$7 = 0, j_finish$7 = r$1.bucketCounts.length; j$7 < j_finish$7; ++j$7) {
780
+ let match$5 = r$1.bucketCounts[j$7];
781
+ if (match$5 !== undefined) {
782
+ remaining = remaining + await countBucket(match$5[0], undefined) | 0;
783
+ }
784
+ }
785
+ for (let j$8 = 0, j_finish$8 = r$1.storeCounts.length; j$8 < j_finish$8; ++j$8) {
786
+ let match$6 = r$1.storeCounts[j$8];
787
+ if (match$6 !== undefined) {
788
+ let s$2 = match$6[0];
789
+ let left = await countBucket(s$2.bucketName, s$2.keyPrefix + `/`);
790
+ remaining = remaining + left | 0;
791
+ console.log(` verified empty: ` + s$2.qualified + ` — ` + left.toString() + ` object(s) remain under ` + (s$2.bucketName + `/` + s$2.keyPrefix + `/`));
792
+ }
793
+ }
794
+ }
795
+ }
796
+ if (remaining !== 0) {
797
+ throw {
798
+ RE_EXN_ID: Seed$ReventlessSeed.Failed,
799
+ _1: remaining.toString() + ` item(s)/object(s) remain after the wipe — re-run to finish.`,
800
+ Error: new Error()
801
+ };
802
+ }
803
+ console.log("");
804
+ console.log(`Reset complete — the selected scope of "` + stack$1 + `" reads empty and is re-seedable.`);
805
+ process.exit(0);
806
+ return;
807
+ } catch (raw_message) {
808
+ let message$1 = Primitive_exceptions.internalToException(raw_message);
809
+ if (message$1.RE_EXN_ID === Seed$ReventlessSeed.Failed) {
810
+ Seed_Prompt$ReventlessSeed.close();
811
+ console.error("");
812
+ console.error(`Reset aborted — ` + message$1._1);
813
+ process.exit(1);
814
+ } else {
815
+ Seed_Prompt$ReventlessSeed.close();
816
+ console.error("");
817
+ console.error("Reset aborted with an unexpected error:");
818
+ console.error(Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_JsExn.fromException(message$1), Stdlib_JsExn.message), "unknown"));
819
+ process.exit(1);
820
+ }
821
+ return;
822
+ }
823
+ };
824
+ go();
825
+ }
826
+
827
+ let Ddb;
828
+
829
+ let Rgt;
830
+
831
+ let S3;
832
+
833
+ export {
834
+ Ddb,
835
+ Rgt,
836
+ S3,
837
+ projectName,
838
+ field,
839
+ asString,
840
+ chunk,
841
+ nameAllowlist,
842
+ readStackConfig,
843
+ configValue,
844
+ classify,
845
+ tagValue,
846
+ discover,
847
+ splitQualified,
848
+ parseObjectStores,
849
+ validateStores,
850
+ countTable,
851
+ countBucket,
852
+ sendBatch,
853
+ truncateTable,
854
+ emptyBucket,
855
+ pluginOf,
856
+ chooseScope,
857
+ gateTarget,
858
+ reportAll,
859
+ run,
860
+ }
861
+ /* node:fs Not a pure module */