@reventlessdev/reventless-aws 3.0.0-alpha.251 → 3.0.0-alpha.253

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.
@@ -59,6 +59,7 @@ import * as Geocoder_AwsLocation$ReventlessAws from "./adapter/Geocoder/Geocoder
59
59
  import * as QueryEngine_DynamoDb$ReventlessAws from "./adapter/QueryEngine/QueryEngine_DynamoDb.res.mjs";
60
60
  import * as PluginRuntime_Builder$ReventlessAws from "./plugin/runtime/PluginRuntime_Builder.res.mjs";
61
61
  import * as PluginsReadModelSpec$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/lifecycle/PluginsReadModelSpec.res.mjs";
62
+ import * as StorePrefixCollision$ReventlessCore from "@reventlessdev/reventless-core/src/util/StorePrefixCollision.res.mjs";
62
63
  import * as ExtensionPoint_Builder$ReventlessAws from "./components/ExtensionPoint_Builder.res.mjs";
63
64
  import * as StateViewSlice_Builder$ReventlessAws from "./components/StateViewSlice_Builder.res.mjs";
64
65
  import * as Task_Builder_PerBucket$ReventlessAws from "./components/Task_Builder_PerBucket.res.mjs";
@@ -156,8 +157,8 @@ function MakeWithConfig(Config) {
156
157
  };
157
158
  let platformStackRef = Stdlib_Option.map(new Pulumi.Config("platform").get("stack"), stack => new Pulumi.StackReference(stack));
158
159
  let domainBaseFragment = GraphQL_Stitcher$ReventlessCore.encode({
159
- types: [],
160
- mutations: [],
160
+ types: Platform_AdminApi$ReventlessCore.uploadTypes,
161
+ mutations: Platform_AdminApi$ReventlessCore.uploadMutationFields,
161
162
  queries: [" Platform_ping: String"],
162
163
  subscriptions: [],
163
164
  subscriptionSources: []
@@ -904,6 +905,17 @@ function MakeWithConfig(Config) {
904
905
  ]]);
905
906
  }
906
907
  });
908
+ let collisions = StorePrefixCollision$ReventlessCore.collisionsFor(declaredStores.map(param => {
909
+ let store = param[1];
910
+ let plugin = param[0];
911
+ return {
912
+ qualified: plugin + `.` + store,
913
+ prefix: Util_StoreLayout$ReventlessAws.keyPrefixFor(plugin, store)
914
+ };
915
+ }));
916
+ if (collisions.length !== 0) {
917
+ Stdlib_JsError.throwWithMessage(collisions.map(StorePrefixCollision$ReventlessCore.collisionMessage).join("\n\n"));
918
+ }
907
919
  if (declaredStores.length !== 0) {
908
920
  let tmp;
909
921
  tmp = storeLayout === "PerStore" ? "bucket per store" : "shared bucket, {store}/… prefixes";
@@ -917,7 +929,7 @@ function MakeWithConfig(Config) {
917
929
  let store = param[1];
918
930
  let plugin = param[0];
919
931
  let bucketName = Util_StoreLayout$ReventlessAws.bucketNameFor(storeLayout, stackName, plugin, store);
920
- let keyPrefix = Util_StoreLayout$ReventlessAws.keyPrefixFor(store);
932
+ let keyPrefix = Util_StoreLayout$ReventlessAws.keyPrefixFor(plugin, store);
921
933
  let b = storeBuckets[bucketName];
922
934
  let bucket;
923
935
  if (b !== undefined) {
@@ -931,13 +943,11 @@ function MakeWithConfig(Config) {
931
943
  bucket = b$1;
932
944
  }
933
945
  let storeHandle = Capability_ObjectStore_S3$ReventlessAws.underPrefix(bucket, keyPrefix);
934
- let presign = Upload_Presign_S3$ReventlessAws.make(storeHandle.bucketName, undefined, storeHandle.keyPrefix, `UploadPresign-` + plugin + `-` + store, undefined);
935
946
  return [
936
947
  plugin + `.` + store,
937
948
  keyPrefix,
938
949
  bucketName,
939
- storeHandle.bucketName,
940
- presign.url
950
+ storeHandle.bucketName
941
951
  ];
942
952
  });
943
953
  let declaredServedBuckets = Stdlib_Array.filterMap(storeBucketOrder, bucketName => Stdlib_Option.map(storeBuckets[bucketName], b => ({
@@ -966,15 +976,23 @@ function MakeWithConfig(Config) {
966
976
  store: param[0],
967
977
  keyPrefix: param[1],
968
978
  bucketName: param[3],
969
- uploadUrl: param[4],
970
979
  baseUrl: storeServingBaseUrl
971
980
  }));
972
981
  objectStoreEndpointsRef.contents = declaredStoreEndpoints;
982
+ let store = Stdlib_Option.flatMap(hostUiBundle, c => c.uploadBucket);
983
+ let uploadServiceStores = declaredStoreServices.map(param => ({
984
+ qualified: param[0],
985
+ bucketName: param[3],
986
+ servedPrefix: param[1]
987
+ })).concat(store !== undefined ? [{
988
+ qualified: store.keyPrefix,
989
+ bucketName: store.bucketName,
990
+ servedPrefix: store.keyPrefix
991
+ }] : []);
992
+ if (uploadServiceStores.length !== 0) {
993
+ Upload_Presign_S3$ReventlessAws.make(domainApi, uploadServiceStores, undefined, undefined, {});
994
+ }
973
995
  if (declaredStoreEndpoints.length !== 0) {
974
- Pulumi$Pulumi.$$export("uploadEndpoints", Pulumi.all(declaredStoreEndpoints.map(e => e.uploadUrl.apply(u => [
975
- e.store,
976
- u
977
- ]))).apply(pairs => Object.fromEntries(pairs)));
978
996
  Pulumi$Pulumi.$$export("objectStores", Pulumi.all(declaredStoreEndpoints.map(e => Pulumi.all([
979
997
  e.bucketName,
980
998
  Output$Pulumi.allOpt(e.baseUrl)
@@ -1019,14 +1037,14 @@ function MakeWithConfig(Config) {
1019
1037
  } else {
1020
1038
  customDomain = undefined;
1021
1039
  }
1022
- let store = hostUiBundle.uploadBucket;
1040
+ let store$1 = hostUiBundle.uploadBucket;
1023
1041
  let servedBuckets = (
1024
- store !== undefined ? [{
1025
- id: store.keyPrefix,
1026
- prefixes: [store.keyPrefix],
1027
- bucketId: store.bucketId,
1028
- bucketArn: store.bucketArn,
1029
- bucketRegionalDomainName: store.bucketRegionalDomainName
1042
+ store$1 !== undefined ? [{
1043
+ id: store$1.keyPrefix,
1044
+ prefixes: [store$1.keyPrefix],
1045
+ bucketId: store$1.bucketId,
1046
+ bucketArn: store$1.bucketArn,
1047
+ bucketRegionalDomainName: store$1.bucketRegionalDomainName
1030
1048
  }] : []
1031
1049
  ).concat(declaredServedBuckets);
1032
1050
  let match$4 = Plugin_Stack$ReventlessAws.makeUiBundleDistribution("host-ui", Stdlib_Option.getOr(hostUiBundle.bundleVersion, version), Stdlib_Option.getOr(hostUiBundle.assetsDir, Util_Bundle$ReventlessAws.resolvePackageRoot(true, "@reventlessdev/reventless-host-shell") + "/dist"), true, undefined, true, [
@@ -1039,15 +1057,6 @@ function MakeWithConfig(Config) {
1039
1057
  let domainEventsEndpointOutput = domainEventsApiOpt !== undefined ? AppSync_EventsApi$ReventlessAws.httpEndpoint(domainEventsApiOpt).apply(ep => ep + "/event") : Pulumi.output(undefined);
1040
1058
  let index = hostUiBundle.geocoderPlaceIndex;
1041
1059
  let geocoderEndpointOutput = index !== undefined ? Geocoder_AwsLocation$ReventlessAws.make(index.indexName, undefined, undefined).url.apply(u => u) : Pulumi.output(undefined);
1042
- let store$1 = hostUiBundle.uploadBucket;
1043
- let uploadEndpointOutput = store$1 !== undefined ? Upload_Presign_S3$ReventlessAws.make(store$1.bucketName, undefined, undefined, undefined, undefined).url.apply(u => u) : Pulumi.output(undefined);
1044
- let storeUploadEndpointsOutput = Pulumi.all(declaredStoreServices.map(param => {
1045
- let qualified = param[0];
1046
- return param[4].apply(u => [
1047
- qualified,
1048
- u
1049
- ]);
1050
- }));
1051
1060
  let configJsonContent = Pulumi.all([
1052
1061
  Pulumi.all([
1053
1062
  resolvedDomainApiEndpoint,
@@ -1056,26 +1065,19 @@ function MakeWithConfig(Config) {
1056
1065
  cognitoPool.clientId
1057
1066
  ]),
1058
1067
  domainEventsEndpointOutput,
1059
- geocoderEndpointOutput,
1060
- Pulumi.all([
1061
- uploadEndpointOutput,
1062
- storeUploadEndpointsOutput
1063
- ])
1068
+ geocoderEndpointOutput
1064
1069
  ]).apply(param => {
1065
- let match = param[3];
1066
- let storeUploadEps = match[1];
1067
- let uploadEpOpt = match[0];
1068
1070
  let geocoderEpOpt = param[2];
1069
1071
  let eventsEpOpt = param[1];
1070
- let match$1 = param[0];
1072
+ let match = param[0];
1071
1073
  let fields = [
1072
1074
  [
1073
1075
  "apiEndpoint",
1074
- match$1[0]
1076
+ match[0]
1075
1077
  ],
1076
1078
  [
1077
1079
  "platformApiEndpoint",
1078
- match$1[1]
1080
+ match[1]
1079
1081
  ],
1080
1082
  [
1081
1083
  "region",
@@ -1087,11 +1089,11 @@ function MakeWithConfig(Config) {
1087
1089
  ],
1088
1090
  [
1089
1091
  "cognitoUserPoolId",
1090
- match$1[2]
1092
+ match[2]
1091
1093
  ],
1092
1094
  [
1093
1095
  "cognitoClientId",
1094
- match$1[3]
1096
+ match[3]
1095
1097
  ],
1096
1098
  [
1097
1099
  "liveUpdates",
@@ -1116,18 +1118,7 @@ function MakeWithConfig(Config) {
1116
1118
  "geocoderEndpoint",
1117
1119
  geocoderEpOpt
1118
1120
  ]]) : withEvents;
1119
- let withUpload = uploadEpOpt !== undefined ? withGeocoder.concat([[
1120
- "uploadEndpoint",
1121
- uploadEpOpt
1122
- ]]) : withGeocoder;
1123
- let withStoreUploads = storeUploadEps.length !== 0 ? withUpload.concat([[
1124
- "uploadEndpoints",
1125
- Object.fromEntries(storeUploadEps.map(param => [
1126
- param[0],
1127
- param[1]
1128
- ]))
1129
- ]]) : withUpload;
1130
- return JSON.stringify(Object.fromEntries(withStoreUploads));
1121
+ return JSON.stringify(Object.fromEntries(withGeocoder));
1131
1122
  });
1132
1123
  new (Aws.s3.BucketObject)("host-ui-config-json", {
1133
1124
  bucket: bucketName,
@@ -1334,8 +1325,8 @@ function Make($star) {
1334
1325
  };
1335
1326
  let platformStackRef = Stdlib_Option.map(new Pulumi.Config("platform").get("stack"), stack => new Pulumi.StackReference(stack));
1336
1327
  let domainBaseFragment = GraphQL_Stitcher$ReventlessCore.encode({
1337
- types: [],
1338
- mutations: [],
1328
+ types: Platform_AdminApi$ReventlessCore.uploadTypes,
1329
+ mutations: Platform_AdminApi$ReventlessCore.uploadMutationFields,
1339
1330
  queries: [" Platform_ping: String"],
1340
1331
  subscriptions: [],
1341
1332
  subscriptionSources: []
@@ -2059,6 +2050,17 @@ function Make($star) {
2059
2050
  ]]);
2060
2051
  }
2061
2052
  });
2053
+ let collisions = StorePrefixCollision$ReventlessCore.collisionsFor(declaredStores.map(param => {
2054
+ let store = param[1];
2055
+ let plugin = param[0];
2056
+ return {
2057
+ qualified: plugin + `.` + store,
2058
+ prefix: Util_StoreLayout$ReventlessAws.keyPrefixFor(plugin, store)
2059
+ };
2060
+ }));
2061
+ if (collisions.length !== 0) {
2062
+ Stdlib_JsError.throwWithMessage(collisions.map(StorePrefixCollision$ReventlessCore.collisionMessage).join("\n\n"));
2063
+ }
2062
2064
  if (declaredStores.length !== 0) {
2063
2065
  let tmp;
2064
2066
  tmp = storeLayout === "PerStore" ? "bucket per store" : "shared bucket, {store}/… prefixes";
@@ -2072,7 +2074,7 @@ function Make($star) {
2072
2074
  let store = param[1];
2073
2075
  let plugin = param[0];
2074
2076
  let bucketName = Util_StoreLayout$ReventlessAws.bucketNameFor(storeLayout, stackName, plugin, store);
2075
- let keyPrefix = Util_StoreLayout$ReventlessAws.keyPrefixFor(store);
2077
+ let keyPrefix = Util_StoreLayout$ReventlessAws.keyPrefixFor(plugin, store);
2076
2078
  let b = storeBuckets[bucketName];
2077
2079
  let bucket;
2078
2080
  if (b !== undefined) {
@@ -2086,13 +2088,11 @@ function Make($star) {
2086
2088
  bucket = b$1;
2087
2089
  }
2088
2090
  let storeHandle = Capability_ObjectStore_S3$ReventlessAws.underPrefix(bucket, keyPrefix);
2089
- let presign = Upload_Presign_S3$ReventlessAws.make(storeHandle.bucketName, undefined, storeHandle.keyPrefix, `UploadPresign-` + plugin + `-` + store, undefined);
2090
2091
  return [
2091
2092
  plugin + `.` + store,
2092
2093
  keyPrefix,
2093
2094
  bucketName,
2094
- storeHandle.bucketName,
2095
- presign.url
2095
+ storeHandle.bucketName
2096
2096
  ];
2097
2097
  });
2098
2098
  let declaredServedBuckets = Stdlib_Array.filterMap(storeBucketOrder, bucketName => Stdlib_Option.map(storeBuckets[bucketName], b => ({
@@ -2121,15 +2121,23 @@ function Make($star) {
2121
2121
  store: param[0],
2122
2122
  keyPrefix: param[1],
2123
2123
  bucketName: param[3],
2124
- uploadUrl: param[4],
2125
2124
  baseUrl: storeServingBaseUrl
2126
2125
  }));
2127
2126
  objectStoreEndpointsRef.contents = declaredStoreEndpoints;
2127
+ let store = Stdlib_Option.flatMap(hostUiBundle, c => c.uploadBucket);
2128
+ let uploadServiceStores = declaredStoreServices.map(param => ({
2129
+ qualified: param[0],
2130
+ bucketName: param[3],
2131
+ servedPrefix: param[1]
2132
+ })).concat(store !== undefined ? [{
2133
+ qualified: store.keyPrefix,
2134
+ bucketName: store.bucketName,
2135
+ servedPrefix: store.keyPrefix
2136
+ }] : []);
2137
+ if (uploadServiceStores.length !== 0) {
2138
+ Upload_Presign_S3$ReventlessAws.make(domainApi, uploadServiceStores, undefined, undefined, {});
2139
+ }
2128
2140
  if (declaredStoreEndpoints.length !== 0) {
2129
- Pulumi$Pulumi.$$export("uploadEndpoints", Pulumi.all(declaredStoreEndpoints.map(e => e.uploadUrl.apply(u => [
2130
- e.store,
2131
- u
2132
- ]))).apply(pairs => Object.fromEntries(pairs)));
2133
2141
  Pulumi$Pulumi.$$export("objectStores", Pulumi.all(declaredStoreEndpoints.map(e => Pulumi.all([
2134
2142
  e.bucketName,
2135
2143
  Output$Pulumi.allOpt(e.baseUrl)
@@ -2174,14 +2182,14 @@ function Make($star) {
2174
2182
  } else {
2175
2183
  customDomain = undefined;
2176
2184
  }
2177
- let store = hostUiBundle.uploadBucket;
2185
+ let store$1 = hostUiBundle.uploadBucket;
2178
2186
  let servedBuckets = (
2179
- store !== undefined ? [{
2180
- id: store.keyPrefix,
2181
- prefixes: [store.keyPrefix],
2182
- bucketId: store.bucketId,
2183
- bucketArn: store.bucketArn,
2184
- bucketRegionalDomainName: store.bucketRegionalDomainName
2187
+ store$1 !== undefined ? [{
2188
+ id: store$1.keyPrefix,
2189
+ prefixes: [store$1.keyPrefix],
2190
+ bucketId: store$1.bucketId,
2191
+ bucketArn: store$1.bucketArn,
2192
+ bucketRegionalDomainName: store$1.bucketRegionalDomainName
2185
2193
  }] : []
2186
2194
  ).concat(declaredServedBuckets);
2187
2195
  let match$4 = Plugin_Stack$ReventlessAws.makeUiBundleDistribution("host-ui", Stdlib_Option.getOr(hostUiBundle.bundleVersion, version), Stdlib_Option.getOr(hostUiBundle.assetsDir, Util_Bundle$ReventlessAws.resolvePackageRoot(true, "@reventlessdev/reventless-host-shell") + "/dist"), true, undefined, true, [
@@ -2194,15 +2202,6 @@ function Make($star) {
2194
2202
  let domainEventsEndpointOutput = domainEventsApiOpt !== undefined ? AppSync_EventsApi$ReventlessAws.httpEndpoint(domainEventsApiOpt).apply(ep => ep + "/event") : Pulumi.output(undefined);
2195
2203
  let index = hostUiBundle.geocoderPlaceIndex;
2196
2204
  let geocoderEndpointOutput = index !== undefined ? Geocoder_AwsLocation$ReventlessAws.make(index.indexName, undefined, undefined).url.apply(u => u) : Pulumi.output(undefined);
2197
- let store$1 = hostUiBundle.uploadBucket;
2198
- let uploadEndpointOutput = store$1 !== undefined ? Upload_Presign_S3$ReventlessAws.make(store$1.bucketName, undefined, undefined, undefined, undefined).url.apply(u => u) : Pulumi.output(undefined);
2199
- let storeUploadEndpointsOutput = Pulumi.all(declaredStoreServices.map(param => {
2200
- let qualified = param[0];
2201
- return param[4].apply(u => [
2202
- qualified,
2203
- u
2204
- ]);
2205
- }));
2206
2205
  let configJsonContent = Pulumi.all([
2207
2206
  Pulumi.all([
2208
2207
  resolvedDomainApiEndpoint,
@@ -2211,26 +2210,19 @@ function Make($star) {
2211
2210
  cognitoPool.clientId
2212
2211
  ]),
2213
2212
  domainEventsEndpointOutput,
2214
- geocoderEndpointOutput,
2215
- Pulumi.all([
2216
- uploadEndpointOutput,
2217
- storeUploadEndpointsOutput
2218
- ])
2213
+ geocoderEndpointOutput
2219
2214
  ]).apply(param => {
2220
- let match = param[3];
2221
- let storeUploadEps = match[1];
2222
- let uploadEpOpt = match[0];
2223
2215
  let geocoderEpOpt = param[2];
2224
2216
  let eventsEpOpt = param[1];
2225
- let match$1 = param[0];
2217
+ let match = param[0];
2226
2218
  let fields = [
2227
2219
  [
2228
2220
  "apiEndpoint",
2229
- match$1[0]
2221
+ match[0]
2230
2222
  ],
2231
2223
  [
2232
2224
  "platformApiEndpoint",
2233
- match$1[1]
2225
+ match[1]
2234
2226
  ],
2235
2227
  [
2236
2228
  "region",
@@ -2242,11 +2234,11 @@ function Make($star) {
2242
2234
  ],
2243
2235
  [
2244
2236
  "cognitoUserPoolId",
2245
- match$1[2]
2237
+ match[2]
2246
2238
  ],
2247
2239
  [
2248
2240
  "cognitoClientId",
2249
- match$1[3]
2241
+ match[3]
2250
2242
  ],
2251
2243
  [
2252
2244
  "liveUpdates",
@@ -2271,18 +2263,7 @@ function Make($star) {
2271
2263
  "geocoderEndpoint",
2272
2264
  geocoderEpOpt
2273
2265
  ]]) : withEvents;
2274
- let withUpload = uploadEpOpt !== undefined ? withGeocoder.concat([[
2275
- "uploadEndpoint",
2276
- uploadEpOpt
2277
- ]]) : withGeocoder;
2278
- let withStoreUploads = storeUploadEps.length !== 0 ? withUpload.concat([[
2279
- "uploadEndpoints",
2280
- Object.fromEntries(storeUploadEps.map(param => [
2281
- param[0],
2282
- param[1]
2283
- ]))
2284
- ]]) : withUpload;
2285
- return JSON.stringify(Object.fromEntries(withStoreUploads));
2266
+ return JSON.stringify(Object.fromEntries(withGeocoder));
2286
2267
  });
2287
2268
  new (Aws.s3.BucketObject)("host-ui-config-json", {
2288
2269
  bucket: bucketName,
@@ -60,14 +60,16 @@ type response = {
60
60
  body: string,
61
61
  }
62
62
 
63
- // Permissive CORS so the browser demo can call the endpoint from any origin.
64
- let corsHeaders = () =>
65
- Dict.fromArray([
66
- ("content-type", "application/json"),
67
- ("access-control-allow-origin", "*"),
68
- ("access-control-allow-methods", "GET,OPTIONS"),
69
- ("access-control-allow-headers", "*"),
70
- ])
63
+ // CORS belongs to the Function URL's own `cors` configuration and to nothing
64
+ // else. AWS injects the allow-origin header itself whenever the request carries
65
+ // an `Origin`, so a handler that also sets one sends the header *twice* — and a
66
+ // browser rejects `Access-Control-Allow-Origin: *, *` outright, failing every
67
+ // cross-origin call while leaving `curl` (which sends no `Origin`, so AWS adds
68
+ // nothing) working perfectly.
69
+ //
70
+ // It is also the only way `~corsOrigins` can mean anything: a hardcoded `*`
71
+ // here would keep answering `*` for a deployment that narrowed the allow-list.
72
+ let jsonHeaders = () => Dict.fromArray([("content-type", "application/json")])
71
73
 
72
74
  // Pull `q` from the parsed query-string params, falling back to the raw string.
73
75
  let readQueryParam = (event: functionUrlEvent): option<string> =>
@@ -93,7 +95,7 @@ let handler = async (event: functionUrlEvent): response => {
93
95
  let indexName = getEnv("PLACE_INDEX_NAME")->Option.getOr("")
94
96
  let q = readQueryParam(event)->Option.getOr("")
95
97
  if indexName == "" || q == "" {
96
- {statusCode: 200, headers: corsHeaders(), body: "[]"}
98
+ {statusCode: 200, headers: jsonHeaders(), body: "[]"}
97
99
  } else {
98
100
  let client = makeLocationClient()
99
101
  let resp = await client->send(
@@ -124,13 +126,13 @@ let handler = async (event: functionUrlEvent): response => {
124
126
  )
125
127
  {
126
128
  statusCode: 200,
127
- headers: corsHeaders(),
129
+ headers: jsonHeaders(),
128
130
  body: results->JSON.Encode.array->JSON.stringify,
129
131
  }
130
132
  }
131
133
  } catch {
132
134
  | exn =>
133
135
  Console.error2("Geocoder: search failed", exn)
134
- {statusCode: 200, headers: corsHeaders(), body: "[]"}
136
+ {statusCode: 200, headers: jsonHeaders(), body: "[]"}
135
137
  }
136
138
  }
@@ -12,25 +12,11 @@ function getEnv(k) {
12
12
  }
13
13
  }
14
14
 
15
- function corsHeaders() {
16
- return Object.fromEntries([
17
- [
15
+ function jsonHeaders() {
16
+ return Object.fromEntries([[
18
17
  "content-type",
19
18
  "application/json"
20
- ],
21
- [
22
- "access-control-allow-origin",
23
- "*"
24
- ],
25
- [
26
- "access-control-allow-methods",
27
- "GET,OPTIONS"
28
- ],
29
- [
30
- "access-control-allow-headers",
31
- "*"
32
- ]
33
- ]);
19
+ ]]);
34
20
  }
35
21
 
36
22
  function readQueryParam(event) {
@@ -59,7 +45,10 @@ async function handler(event) {
59
45
  if (indexName === "" || q === "") {
60
46
  return {
61
47
  statusCode: 200,
62
- headers: corsHeaders(),
48
+ headers: Object.fromEntries([[
49
+ "content-type",
50
+ "application/json"
51
+ ]]),
63
52
  body: "[]"
64
53
  };
65
54
  }
@@ -101,7 +90,10 @@ async function handler(event) {
101
90
  });
102
91
  return {
103
92
  statusCode: 200,
104
- headers: corsHeaders(),
93
+ headers: Object.fromEntries([[
94
+ "content-type",
95
+ "application/json"
96
+ ]]),
105
97
  body: JSON.stringify(results)
106
98
  };
107
99
  } catch (raw_exn) {
@@ -109,7 +101,10 @@ async function handler(event) {
109
101
  console.error("Geocoder: search failed", exn);
110
102
  return {
111
103
  statusCode: 200,
112
- headers: corsHeaders(),
104
+ headers: Object.fromEntries([[
105
+ "content-type",
106
+ "application/json"
107
+ ]]),
113
108
  body: "[]"
114
109
  };
115
110
  }
@@ -117,7 +112,7 @@ async function handler(event) {
117
112
 
118
113
  export {
119
114
  getEnv,
120
- corsHeaders,
115
+ jsonHeaders,
121
116
  readQueryParam,
122
117
  handler,
123
118
  }
@@ -125,10 +125,21 @@ let connect = (
125
125
  let make: ReventlessCore.Task_Adapter.bucketMaker<bucketParts> = (~name, ~opts) => {
126
126
  let opts = opts->ReventlessCore.Util.Pulumi.ComponentResourceOptions.toCustomResourceOptions
127
127
 
128
+ // A task bucket holds transient input a slice consumes, so disposability
129
+ // follows the stack, exactly as it does for declared stores. It matters
130
+ // because a bucket name change is a *replace*, and a replace of a non-empty
131
+ // bucket fails the deploy with `BucketNotEmpty` — which would wedge precisely
132
+ // the throwaway stacks that are recreated most often. Protected stacks keep
133
+ // the safe default and drain by hand.
134
+ let forceDestroy =
135
+ Util_StoreLayout.protectionFor(~stack=Pulumi.Pulumi.getStackName()) ==
136
+ Util_StoreLayout.Unprotected
137
+
128
138
  let bucket = {
129
139
  PulumiAws.S3.Bucket.make(
130
140
  ~name,
131
141
  ~args={
142
+ forceDestroy: forceDestroy->Pulumi.Input.make,
132
143
  corsRules: [
133
144
  {
134
145
  PulumiAws.S3.Bucket.allowedHeaders: ["*"],
@@ -11,6 +11,7 @@ import * as Adapter$ReventlessCore from "@reventlessdev/reventless-core/src/adap
11
11
  import * as PolicyDocument$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/IAM/PolicyDocument.res.mjs";
12
12
  import * as Util_Pulumi$ReventlessCore from "@reventlessdev/reventless-core/src/util/Util_Pulumi.res.mjs";
13
13
  import * as Adapter_Helpers$ReventlessAws from "../Adapter_Helpers.res.mjs";
14
+ import * as Util_StoreLayout$ReventlessAws from "../../util/Util_StoreLayout.res.mjs";
14
15
  import * as TaskBucket_S3_Runtime$ReventlessAws from "./TaskBucket_S3_Runtime.res.mjs";
15
16
 
16
17
  function subscribeLambda2S3Bucket(lambda, name, bucket, opts) {
@@ -75,6 +76,7 @@ function connect(name, bucket, bucketMode, commandTopics, runtime, opts) {
75
76
 
76
77
  function make(name, opts) {
77
78
  let opts$1 = Util_Pulumi$ReventlessCore.ComponentResourceOptions.toCustomResourceOptions(opts);
79
+ let forceDestroy = Util_StoreLayout$ReventlessAws.protectionFor(Pulumi.getStack(), undefined) === "Unprotected";
78
80
  let bucket = new (Aws.s3.Bucket)(name, {
79
81
  corsRules: [{
80
82
  allowedHeaders: ["*"],
@@ -93,6 +95,7 @@ function make(name, opts) {
93
95
  ],
94
96
  maxAgeSeconds: 3000
95
97
  }],
98
+ forceDestroy: forceDestroy,
96
99
  tags: AWS_Tags$ReventlessAws.make(name, Task$ReventlessCore.componentType, {
97
100
  TAG: "Other",
98
101
  _0: "Bucket"