@reventlessdev/reventless-aws 3.0.0-alpha.217 → 3.0.0-alpha.219

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 (34) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/package.json +4 -4
  3. package/src/Platform.res.mjs +2 -0
  4. package/src/adapter/Api/AppSync_SourceApiAssociation_Retrying.res +436 -0
  5. package/src/adapter/Api/AppSync_SourceApiAssociation_Retrying.res.mjs +340 -0
  6. package/src/adapter/Mcp/MCP_Lambda.res +8 -19
  7. package/src/adapter/Mcp/MCP_Lambda.res.mjs +2 -9
  8. package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +6 -2
  9. package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs +12 -2
  10. package/src/components/Aggregate_Builder_Micro.res.mjs +1 -0
  11. package/src/components/Aggregate_Builder_NoResolver.res.mjs +1 -0
  12. package/src/components/Aggregate_Builder_PerAggregate.res.mjs +1 -0
  13. package/src/components/Aggregate_Builder_Single.res.mjs +1 -0
  14. package/src/components/Aggregate_Builder_Single_Async.res.mjs +1 -0
  15. package/src/components/Api/AppSync_MergedApi.res +19 -16
  16. package/src/components/Api/AppSync_MergedApi.res.mjs +5 -6
  17. package/src/components/AutomationSlice_Builder.res.mjs +1 -0
  18. package/src/components/ExtensionPoint_Builder.res.mjs +1 -0
  19. package/src/components/ForeignReadModel_Builder.res.mjs +2 -0
  20. package/src/components/OutboundTranslationSlice_Builder.res.mjs +1 -0
  21. package/src/components/Plugin.res.mjs +1 -0
  22. package/src/components/ReadModel_Builder_NoResolver.res.mjs +1 -0
  23. package/src/components/ReadModel_Builder_NoResolver_Stream.res.mjs +1 -0
  24. package/src/components/ReadModel_Builder_PerReadModel.res.mjs +1 -0
  25. package/src/components/ReadModel_Builder_Single.res.mjs +1 -0
  26. package/src/components/ReadModel_Builder_Single_Stream.res.mjs +1 -0
  27. package/src/components/SideEffectHandler_PerSideEffectHandler.res.mjs +3 -0
  28. package/src/components/SideEffectHandler_Single.res.mjs +2 -0
  29. package/src/components/StateViewSlice_Builder.res.mjs +1 -0
  30. package/src/components/StateViewSlice_Builder_Stream.res.mjs +1 -0
  31. package/src/components/Task_Builder_PerBucket.res.mjs +2 -0
  32. package/src/plugin/stack/Plugin_ExtensionPoint_Builder.res.mjs +2 -0
  33. package/tests/AppSync_SourceApiAssociation_RetryingTest.res +112 -0
  34. package/tests/AppSync_SourceApiAssociation_RetryingTest.res.mjs +133 -0
@@ -0,0 +1,340 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Alias$Pulumi from "@reventlessdev/rescript-pulumi-pulumi/src/Alias.res.mjs";
4
+ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
5
+ import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
6
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
7
+ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
8
+ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
9
+ import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
10
+ import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
11
+ import * as IndexJs from "@pulumi/pulumi/dynamic/index.js";
12
+
13
+ let log = Logger$ReventlessCore.fromEnv();
14
+
15
+ let newOf1 = ((C, x) => new C(x));
16
+
17
+ let newOf0 = ((C) => new C());
18
+
19
+ let _sdk = {
20
+ contents: undefined
21
+ };
22
+
23
+ let _client = {
24
+ contents: undefined
25
+ };
26
+
27
+ async function getSdk() {
28
+ let m = _sdk.contents;
29
+ if (m !== undefined) {
30
+ return m;
31
+ }
32
+ let m$1 = await import("@aws-sdk/client-appsync");
33
+ _sdk.contents = m$1;
34
+ return m$1;
35
+ }
36
+
37
+ async function getClient() {
38
+ let c = _client.contents;
39
+ if (c !== undefined) {
40
+ return Primitive_option.valFromOption(c);
41
+ }
42
+ let sdk = await getSdk();
43
+ let c$1 = newOf0(sdk.AppSyncClient);
44
+ _client.contents = Primitive_option.some(c$1);
45
+ return c$1;
46
+ }
47
+
48
+ let transientNames = [
49
+ "ConcurrentModificationException",
50
+ "ThrottlingException",
51
+ "TooManyRequestsException",
52
+ "InternalFailureException",
53
+ "ServiceUnavailableException",
54
+ "ServiceUnavailable"
55
+ ];
56
+
57
+ function isConcurrentModificationError(jsErr) {
58
+ let match = jsErr.name;
59
+ let match$1 = Stdlib_JsExn.message(jsErr);
60
+ if (match == null) {
61
+ if (match$1 !== undefined) {
62
+ return match$1.includes("ConcurrentModificationException");
63
+ } else {
64
+ return false;
65
+ }
66
+ } else if (match === "ConcurrentModificationException") {
67
+ return true;
68
+ } else if (match$1 !== undefined) {
69
+ return match$1.includes("ConcurrentModificationException");
70
+ } else {
71
+ return false;
72
+ }
73
+ }
74
+
75
+ function isRetryableAssociationError(jsErr) {
76
+ let match = jsErr.name;
77
+ let match$1 = Stdlib_JsExn.message(jsErr);
78
+ if (!(match == null) && transientNames.includes(match)) {
79
+ return true;
80
+ }
81
+ if (match$1 !== undefined) {
82
+ return transientNames.some(n => match$1.includes(n));
83
+ } else {
84
+ return false;
85
+ }
86
+ }
87
+
88
+ function isAlreadyGoneError(jsErr) {
89
+ let match = jsErr.name;
90
+ let match$1 = Stdlib_JsExn.message(jsErr);
91
+ if (!(match == null) && match === "NotFoundException") {
92
+ return true;
93
+ }
94
+ if (match$1 !== undefined) {
95
+ if (match$1.includes("NotFoundException")) {
96
+ return true;
97
+ } else {
98
+ return match$1.includes("not found");
99
+ }
100
+ } else {
101
+ return false;
102
+ }
103
+ }
104
+
105
+ let jsThrow = (e => { throw e });
106
+
107
+ async function runWithRetry(attemptOpt, maxAttemptsOpt, delayMsOpt, maxDelayMsOpt, makeCall) {
108
+ let attempt = attemptOpt !== undefined ? attemptOpt : 0;
109
+ let maxAttempts = maxAttemptsOpt !== undefined ? maxAttemptsOpt : 8;
110
+ let delayMs = delayMsOpt !== undefined ? delayMsOpt : 2000;
111
+ let maxDelayMs = maxDelayMsOpt !== undefined ? maxDelayMsOpt : 30000;
112
+ try {
113
+ return await makeCall();
114
+ } catch (raw_exn) {
115
+ let exn = Primitive_exceptions.internalToException(raw_exn);
116
+ let jsExn = Stdlib_JsExn.fromException(exn);
117
+ let name = Stdlib_Option.getOr(Stdlib_Option.flatMap(jsExn, prim => Primitive_option.fromNullable(prim.name)), "(no name)");
118
+ let msg = Stdlib_Option.getOr(Stdlib_Option.flatMap(jsExn, Stdlib_JsExn.message), "(no message)");
119
+ let isRetryable = attempt < maxAttempts && Stdlib_Option.mapOr(jsExn, false, isRetryableAssociationError);
120
+ if (isRetryable) {
121
+ log.info("AppSync_SourceApiAssociation_Retrying", undefined, `attempt ` + (attempt + 1 | 0).toString() + `/` + maxAttempts.toString() + ` failed, retrying in ` + delayMs.toString() + `ms: ` + name + `: ` + msg);
122
+ await new Promise((resolve, param) => {
123
+ setTimeout(resolve, delayMs);
124
+ });
125
+ let nextDelay = (delayMs << 1);
126
+ let cappedDelay = nextDelay > maxDelayMs ? maxDelayMs : nextDelay;
127
+ return await runWithRetry(attempt + 1 | 0, maxAttempts, cappedDelay, maxDelayMs, makeCall);
128
+ }
129
+ if (attempt > 0) {
130
+ log.warn("AppSync_SourceApiAssociation_Retrying", undefined, `giving up after ` + (attempt + 1 | 0).toString() + ` attempts: ` + name + `: ` + msg);
131
+ }
132
+ if (jsExn !== undefined) {
133
+ return jsThrow(Primitive_option.valFromOption(jsExn));
134
+ }
135
+ throw exn;
136
+ }
137
+ }
138
+
139
+ function idsFromArn(arn) {
140
+ let parts = arn.split("/");
141
+ let idx = parts.indexOf("sourceApiAssociations");
142
+ let match = idx >= 1;
143
+ let match$1 = parts[idx - 1 | 0];
144
+ let match$2 = parts[idx + 1 | 0];
145
+ if (match && match$1 !== undefined && match$2 !== undefined) {
146
+ return {
147
+ associationId: match$2,
148
+ mergedApiIdentifier: match$1
149
+ };
150
+ }
151
+ }
152
+
153
+ function idsFromComposite(id) {
154
+ let match = id.split(",");
155
+ if (match.length !== 2) {
156
+ return;
157
+ }
158
+ let mergedApiId = match[0];
159
+ let assocId = match[1];
160
+ return {
161
+ associationId: assocId,
162
+ mergedApiIdentifier: mergedApiId
163
+ };
164
+ }
165
+
166
+ function identifiersFrom(id, props) {
167
+ let ids = idsFromArn(id);
168
+ if (ids !== undefined) {
169
+ return ids;
170
+ }
171
+ let ids$1 = idsFromComposite(id);
172
+ if (ids$1 !== undefined) {
173
+ return ids$1;
174
+ }
175
+ let ids$2 = idsFromArn(props.arn);
176
+ if (ids$2 !== undefined) {
177
+ return ids$2;
178
+ }
179
+ }
180
+
181
+ function extractIds(resp) {
182
+ let match = resp.sourceApiAssociation;
183
+ if (match === undefined) {
184
+ return Stdlib_JsError.throwWithMessage("CreateSourceApiAssociation returned no associationArn / associationId");
185
+ }
186
+ let aid = match.associationId;
187
+ if (aid === undefined) {
188
+ return Stdlib_JsError.throwWithMessage("CreateSourceApiAssociation returned no associationArn / associationId");
189
+ }
190
+ let arn = match.associationArn;
191
+ if (arn !== undefined) {
192
+ return [
193
+ arn,
194
+ aid
195
+ ];
196
+ } else {
197
+ return Stdlib_JsError.throwWithMessage("CreateSourceApiAssociation returned no associationArn / associationId");
198
+ }
199
+ }
200
+
201
+ async function create(inputs) {
202
+ let sdk = await getSdk();
203
+ let client = await getClient();
204
+ let match = await runWithRetry(undefined, undefined, undefined, undefined, () => client.send(newOf1(sdk.CreateSourceApiAssociationCommand, {
205
+ mergedApiIdentifier: inputs.mergedApiIdentifier,
206
+ sourceApiIdentifier: inputs.sourceApiIdentifier,
207
+ sourceApiAssociationConfig: {
208
+ mergeType: inputs.mergeType
209
+ }
210
+ })).then(extractIds));
211
+ let arn = match[0];
212
+ return {
213
+ id: arn,
214
+ outs: {
215
+ arn: arn,
216
+ associationId: match[1],
217
+ mergedApiIdentifier: inputs.mergedApiIdentifier,
218
+ sourceApiIdentifier: inputs.sourceApiIdentifier,
219
+ mergeType: inputs.mergeType
220
+ }
221
+ };
222
+ }
223
+
224
+ async function update(_id, olds, _news) {
225
+ return {
226
+ outs: olds
227
+ };
228
+ }
229
+
230
+ async function delete_(id, props) {
231
+ let sdk = await getSdk();
232
+ let client = await getClient();
233
+ let ids = identifiersFrom(id, props);
234
+ if (ids === undefined) {
235
+ return log.warn("AppSync_SourceApiAssociation_Retrying", undefined, `delete: could not derive association identifiers from id "` + id + `"; treating as already gone`);
236
+ }
237
+ try {
238
+ return await runWithRetry(undefined, undefined, undefined, undefined, () => client.send(newOf1(sdk.DeleteSourceApiAssociationCommand, ids)));
239
+ } catch (raw_exn) {
240
+ let exn = Primitive_exceptions.internalToException(raw_exn);
241
+ if (Stdlib_Option.mapOr(Stdlib_JsExn.fromException(exn), false, isAlreadyGoneError)) {
242
+ return;
243
+ }
244
+ throw exn;
245
+ }
246
+ }
247
+
248
+ let isDefined = (x => x !== undefined && x !== null);
249
+
250
+ function diff_(_id, olds, news) {
251
+ let replaces = Stdlib_Array.filterMap([
252
+ isDefined(olds.mergedApiIdentifier) && olds.mergedApiIdentifier !== news.mergedApiIdentifier ? "mergedApiIdentifier" : undefined,
253
+ isDefined(olds.sourceApiIdentifier) && olds.sourceApiIdentifier !== news.sourceApiIdentifier ? "sourceApiIdentifier" : undefined,
254
+ isDefined(olds.mergeType) && olds.mergeType !== news.mergeType ? "mergeType" : undefined
255
+ ], x => x);
256
+ return {
257
+ changes: replaces.length !== 0,
258
+ replaces: replaces,
259
+ deleteBeforeReplace: false
260
+ };
261
+ }
262
+
263
+ async function read_(id, props) {
264
+ let sdk = await getSdk();
265
+ let client = await getClient();
266
+ let ids = identifiersFrom(id, props);
267
+ if (ids === undefined) {
268
+ return {};
269
+ }
270
+ try {
271
+ await client.send(newOf1(sdk.GetSourceApiAssociationCommand, ids));
272
+ return {
273
+ id: id,
274
+ props: props
275
+ };
276
+ } catch (raw_exn) {
277
+ let exn = Primitive_exceptions.internalToException(raw_exn);
278
+ if (Stdlib_Option.mapOr(Stdlib_JsExn.fromException(exn), false, isAlreadyGoneError)) {
279
+ log.info("AppSync_SourceApiAssociation_Retrying", undefined, `association ` + ids.associationId + ` on ` + ids.mergedApiIdentifier + ` missing in AppSync; reporting drift`);
280
+ return {};
281
+ }
282
+ let jsExn = Stdlib_JsExn.fromException(exn);
283
+ if (jsExn !== undefined) {
284
+ return jsThrow(Primitive_option.valFromOption(jsExn));
285
+ }
286
+ throw exn;
287
+ }
288
+ }
289
+
290
+ let provider = {
291
+ create: create,
292
+ update: update,
293
+ delete: delete_,
294
+ diff: diff_,
295
+ read: read_
296
+ };
297
+
298
+ function make(name, props, opts) {
299
+ let migrationAlias = Alias$Pulumi.make(name, "aws:appsync/sourceApiAssociation:SourceApiAssociation", undefined);
300
+ let finalOpts;
301
+ if (opts !== undefined) {
302
+ let newrecord = {...opts};
303
+ newrecord.aliases = [migrationAlias];
304
+ finalOpts = newrecord;
305
+ } else {
306
+ finalOpts = {
307
+ aliases: [migrationAlias]
308
+ };
309
+ }
310
+ return new IndexJs.Resource(provider, name, props, finalOpts);
311
+ }
312
+
313
+ export {
314
+ log,
315
+ newOf1,
316
+ newOf0,
317
+ _sdk,
318
+ _client,
319
+ getSdk,
320
+ getClient,
321
+ transientNames,
322
+ isConcurrentModificationError,
323
+ isRetryableAssociationError,
324
+ isAlreadyGoneError,
325
+ jsThrow,
326
+ runWithRetry,
327
+ idsFromArn,
328
+ idsFromComposite,
329
+ identifiersFrom,
330
+ extractIds,
331
+ create,
332
+ update,
333
+ delete_,
334
+ isDefined,
335
+ diff_,
336
+ read_,
337
+ provider,
338
+ make,
339
+ }
340
+ /* log Not a pure module */
@@ -322,14 +322,6 @@ external makeGenerateCommand: (
322
322
  external sqsPublishJsons: ('queue, string) => ReventlessCore.CommandGenerator.publishJsons =
323
323
  "publishJsons"
324
324
 
325
- @module("@reventlessdev/reventless-core/src/RequestContext.res.mjs")
326
- external requestContextTag: 'a = "tag"
327
-
328
- @module("effect/Effect")
329
- external effectProvideService: ('a, 'b) => 'c = "provideService"
330
-
331
- @send external pipe: ('a, 'b) => 'c = "pipe"
332
-
333
325
  let makeQueueRef: string => 'a = %raw(`(url) => ({ id: url, name: url, arn: "" })`)
334
326
 
335
327
  // Decode the payload section of a JWT (base64url → JSON) without signature verification.
@@ -384,16 +376,6 @@ let extractIdentity = (authHeader: option<string>): Reventless.Identity.t =>
384
376
  }
385
377
  }
386
378
 
387
- let runEffect = (correlationId: option<string>, effect) =>
388
- effect
389
- ->pipe(
390
- effectProvideService(
391
- requestContextTag,
392
- {"correlationId": correlationId->Option.getOr("unknown")},
393
- ),
394
- )
395
- ->pipe(Effect.runPromise)
396
-
397
379
  /** Dispatch an MCP tool call through makeGenerateCommand so the interceptor hook fires.
398
380
  The commandTopicArn from mcpConfig tells us which SQS FIFO queue to target. */
399
381
  let dispatchTool = async (
@@ -416,7 +398,14 @@ let dispatchTool = async (
416
398
  meta: {ip: [], user: identity.userId, info: `mcp/tools/${tool.name}`},
417
399
  identity,
418
400
  }
419
- await runEffect(None, generateCommand(payload))
401
+ // Route through the shared dispatch helper so this tool call gets the same
402
+ // correlationId / comp log annotation and a populated RequestContext (identity
403
+ // included) as every other dispatch boundary.
404
+ await ReventlessCore.Runtime.runEffect(
405
+ ~comp=`Mcp(${tool.name})`,
406
+ ~identity,
407
+ generateCommand(payload),
408
+ )
420
409
  }
421
410
 
422
411
  // ─── Deploy-time infrastructure (placeholder) ─────────────────────────────
@@ -10,13 +10,13 @@ import * as Stream$1 from "effect/Stream";
10
10
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
11
11
  import * as AWS$ReventlessAws from "../AWS.res.mjs";
12
12
  import * as Identity$Reventless from "@reventlessdev/reventless-spec/src/types/Identity.res.mjs";
13
+ import * as Runtime$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/Runtime/Runtime.res.mjs";
13
14
  import * as AdminApi$ReventlessCore from "@reventlessdev/reventless-core/src/admin/AdminApi.res.mjs";
14
15
  import * as DynamoDb_Error$ReventlessAws from "../../errors/DynamoDb_Error.res.mjs";
15
16
  import * as PluginBaseFragment$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/api/PluginBaseFragment.res.mjs";
16
17
  import * as MCP_SchemaGenerator$ReventlessCore from "@reventlessdev/reventless-core/src/components/Api/MCP_SchemaGenerator.res.mjs";
17
18
  import * as Util_DynamoDb_Runtime$ReventlessAws from "../../util/Util_DynamoDb_Runtime.res.mjs";
18
19
  import * as DcbEventLogStorage_DynamoDb_Runtime$ReventlessAws from "../DcbEventLog/DcbEventLogStorage_DynamoDb_Runtime.res.mjs";
19
- import * as RequestContextResMjs from "@reventlessdev/reventless-core/src/RequestContext.res.mjs";
20
20
  import * as CommandTopicChannel_SQS_RuntimeResMjs from "@reventlessdev/reventless-aws/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs";
21
21
  import * as CommandGenerator_CallbackResMjs from "@reventlessdev/reventless-core/src/components/CommandGenerator/CommandGenerator_Callback.res.mjs";
22
22
 
@@ -247,12 +247,6 @@ function extractIdentity(authHeader) {
247
247
  };
248
248
  }
249
249
 
250
- function runEffect(correlationId, effect) {
251
- return effect.pipe(Effect.provideService(RequestContextResMjs.tag, {
252
- correlationId: Stdlib_Option.getOr(correlationId, "unknown")
253
- })).pipe(prim => Effect.runPromise(prim));
254
- }
255
-
256
250
  async function dispatchTool(tool, args, identity) {
257
251
  let queueRef = makeQueueRef(tool.commandTopicArn);
258
252
  let publishJsons = CommandTopicChannel_SQS_RuntimeResMjs.publishJsons(queueRef, AWS$ReventlessAws.SQS_FIFO.service);
@@ -269,7 +263,7 @@ async function dispatchTool(tool, args, identity) {
269
263
  meta: payload_meta,
270
264
  identity: identity
271
265
  };
272
- return await runEffect(undefined, generateCommand(payload));
266
+ return await Runtime$ReventlessCore.runEffect(undefined, undefined, `Mcp(` + tool.name + `)`, undefined, identity, undefined, generateCommand(payload));
273
267
  }
274
268
 
275
269
  export {
@@ -283,7 +277,6 @@ export {
283
277
  makeQueueRef,
284
278
  decodeJwtClaims,
285
279
  extractIdentity,
286
- runEffect,
287
280
  dispatchTool,
288
281
  }
289
282
  /* S Not a pure module */
@@ -284,7 +284,8 @@ let groupBySource = (event: event) => {
284
284
  // SQS records carry a `body` string field not included in the minimal record type.
285
285
  @get external recordBody: PulumiAws.Lambda.CallbackFunction.record => option<string> = "body"
286
286
 
287
- let extractCorrelationId = (event: event) =>
287
+ // Read a single string field off the first SQS record's `meta` object.
288
+ let extractMetaField = (event: event, field) =>
288
289
  event.records
289
290
  ->Array.get(0)
290
291
  ->Option.flatMap(r =>
@@ -298,9 +299,12 @@ let extractCorrelationId = (event: event) =>
298
299
  )
299
300
  ->Option.flatMap(obj => obj->Dict.get("meta"))
300
301
  ->Option.flatMap(JSON.Decode.object)
301
- ->Option.flatMap(meta => meta->Dict.get("correlationId"))
302
+ ->Option.flatMap(meta => meta->Dict.get(field))
302
303
  ->Option.flatMap(JSON.Decode.string)
303
304
 
305
+ let extractCorrelationId = (event: event) => event->extractMetaField("correlationId")
306
+ let extractCausationId = (event: event) => event->extractMetaField("causationId")
307
+
304
308
  external asEventHandler: 'a => ReventlessCore.Runtime.eventHandler<event, context, 'result> =
305
309
  "%identity"
306
310
  external asEffectHandler: 'a => ReventlessCore.Runtime.effectHandler<
@@ -172,14 +172,22 @@ function groupBySource(event) {
172
172
  return dict;
173
173
  }
174
174
 
175
- function extractCorrelationId(event) {
175
+ function extractMetaField(event, field) {
176
176
  return Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(event.Records[0], r => Stdlib_Option.flatMap(r.body, body => {
177
177
  try {
178
178
  return Stdlib_JSON.Decode.object(JSON.parse(body));
179
179
  } catch (exn) {
180
180
  return;
181
181
  }
182
- })), obj => obj["meta"]), Stdlib_JSON.Decode.object), meta => meta["correlationId"]), Stdlib_JSON.Decode.string);
182
+ })), obj => obj["meta"]), Stdlib_JSON.Decode.object), meta => meta[field]), Stdlib_JSON.Decode.string);
183
+ }
184
+
185
+ function extractCorrelationId(event) {
186
+ return extractMetaField(event, "correlationId");
187
+ }
188
+
189
+ function extractCausationId(event) {
190
+ return extractMetaField(event, "causationId");
183
191
  }
184
192
 
185
193
  export {
@@ -188,6 +196,8 @@ export {
188
196
  make,
189
197
  makeFromCodeAsset,
190
198
  groupBySource,
199
+ extractMetaField,
191
200
  extractCorrelationId,
201
+ extractCausationId,
192
202
  }
193
203
  /* @pulumi/aws Not a pure module */
@@ -20,6 +20,7 @@ function Make(Spec) {
20
20
  make: RuntimeEnvironment_Lambda$ReventlessAws.make,
21
21
  groupBySource: RuntimeEnvironment_Lambda$ReventlessAws.groupBySource,
22
22
  extractCorrelationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCorrelationId,
23
+ extractCausationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCausationId,
23
24
  asEventHandler: prim => prim,
24
25
  asEffectHandler: prim => prim
25
26
  })(CommandGeneratorResolvers_AppSync$ReventlessAws)({
@@ -20,6 +20,7 @@ function Make(Spec) {
20
20
  make: RuntimeEnvironment_Lambda$ReventlessAws.make,
21
21
  groupBySource: RuntimeEnvironment_Lambda$ReventlessAws.groupBySource,
22
22
  extractCorrelationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCorrelationId,
23
+ extractCausationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCausationId,
23
24
  asEventHandler: prim => prim,
24
25
  asEffectHandler: prim => prim
25
26
  })(CommandGeneratorResolvers_AppSync_NoOp$ReventlessAws)({
@@ -20,6 +20,7 @@ function Make(Spec) {
20
20
  make: RuntimeEnvironment_Lambda$ReventlessAws.make,
21
21
  groupBySource: RuntimeEnvironment_Lambda$ReventlessAws.groupBySource,
22
22
  extractCorrelationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCorrelationId,
23
+ extractCausationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCausationId,
23
24
  asEventHandler: prim => prim,
24
25
  asEffectHandler: prim => prim
25
26
  })(CommandGeneratorResolvers_AppSync$ReventlessAws)({
@@ -22,6 +22,7 @@ function Make(Spec) {
22
22
  make: RuntimeEnvironment_Lambda$ReventlessAws.make,
23
23
  groupBySource: RuntimeEnvironment_Lambda$ReventlessAws.groupBySource,
24
24
  extractCorrelationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCorrelationId,
25
+ extractCausationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCausationId,
25
26
  asEventHandler: prim => prim,
26
27
  asEffectHandler: prim => prim
27
28
  })(CommandGeneratorResolvers_AppSync$ReventlessAws)({
@@ -22,6 +22,7 @@ function Make(Spec) {
22
22
  make: RuntimeEnvironment_Lambda$ReventlessAws.make,
23
23
  groupBySource: RuntimeEnvironment_Lambda$ReventlessAws.groupBySource,
24
24
  extractCorrelationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCorrelationId,
25
+ extractCausationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCausationId,
25
26
  asEventHandler: prim => prim,
26
27
  asEffectHandler: prim => prim
27
28
  })(CommandGeneratorResolvers_AppSync$ReventlessAws)({
@@ -122,9 +122,11 @@ let make = (~name: string, ~opts: Pulumi.ComponentResource.options): t => {
122
122
 
123
123
  /** Associate a source API with the merged API under AUTO_MERGE. AWS
124
124
  serializes association CREATES per merged API (409
125
- ConcurrentModificationException) the platform stack's own associations
126
- are inherently sequential; concurrent first-time plugin-stack deploys need
127
- retry-with-backoff (Phase 4). */
125
+ ConcurrentModificationException). This platform-stack form stays on the
126
+ classic provider: the platform stack creates all its own associations
127
+ (admin + base) sequentially within one program, so they never race. The
128
+ cross-stack race is on the plugin-stack `associateSourceWithMergedArn`
129
+ path, which uses the retrying provider (Phase 4). */
128
130
  let associateSource = (
129
131
  ~name: string,
130
132
  ~mergedApi: t,
@@ -158,8 +160,15 @@ let associateSource = (
158
160
  /** Associate a source API against a merged API referenced by ARN — the
159
161
  plugin-stack form, where the merged API is not a resource in this stack
160
162
  but the platform's `domainMergedApiArn` / `platformMergedApiArn`
161
- StackReference export. Same AUTO_MERGE + create-time 409 caveat as
162
- `associateSource`. */
163
+ StackReference export.
164
+
165
+ Concurrent first-time plugin deploys race here: AWS serializes association
166
+ writes per merged API and 409s (`ConcurrentModificationException`). This
167
+ goes through `AppSync_SourceApiAssociation_Retrying`, a dynamic provider
168
+ that retries the 409 with backoff (Phase 4), so plugin stacks can deploy in
169
+ parallel without a CI-level serialization. The returned resource keeps the
170
+ `PulumiAws.AppSync.SourceApiAssociation.t` shape, so `mergeStatusGateWith`
171
+ still waits for MERGE_SUCCESS on `.associationId` unchanged. */
163
172
  let associateSourceWithMergedArn = (
164
173
  ~name: string,
165
174
  ~mergedApiArn: Pulumi.Output.t<string>,
@@ -169,20 +178,14 @@ let associateSourceWithMergedArn = (
169
178
  let customOpts: Pulumi.CustomResourceOptions.t = {
170
179
  parent: ?opts.parent,
171
180
  }
172
- AppSync.SourceApiAssociation.make(
181
+ AppSync_SourceApiAssociation_Retrying.make(
173
182
  ~name,
174
- ~args={
175
- mergedApiArn: mergedApiArn->Pulumi.Output.asInput,
176
- sourceApiId: sourceApi
183
+ ~props={
184
+ mergedApiIdentifier: mergedApiArn->Pulumi.Output.asInput,
185
+ sourceApiIdentifier: sourceApi
177
186
  ->Pulumi.Output.flatMap((a: AppSync.GraphQLApi.t) => a.id)
178
187
  ->Pulumi.Output.asInput,
179
- sourceApiAssociationConfigs: [
180
- (
181
- {
182
- mergeType: AppSync.SourceApiAssociation.AUTO_MERGE->Pulumi.Input.make,
183
- }: AppSync.SourceApiAssociation.sourceApiAssociationConfig
184
- )->Pulumi.Input.make,
185
- ]->Pulumi.Input.make,
188
+ mergeType: "AUTO_MERGE"->Pulumi.Input.make,
186
189
  },
187
190
  ~opts=Some(customOpts),
188
191
  )
@@ -7,6 +7,7 @@ import * as Pulumi from "@pulumi/pulumi";
7
7
  import * as PolicyDocument$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/IAM/PolicyDocument.res.mjs";
8
8
  import * as Auth_Cognito$ReventlessAws from "../../adapter/Auth/Auth_Cognito.res.mjs";
9
9
  import * as AppSync_Adapter$ReventlessAws from "./AppSync_Adapter.res.mjs";
10
+ import * as AppSync_SourceApiAssociation_Retrying$ReventlessAws from "../../adapter/Api/AppSync_SourceApiAssociation_Retrying.res.mjs";
10
11
 
11
12
  function authenticationTypeName(t) {
12
13
  switch (t) {
@@ -89,12 +90,10 @@ function associateSourceWithMergedArn(name, mergedApiArn, sourceApi, opts) {
89
90
  let customOpts = {
90
91
  parent: customOpts_parent
91
92
  };
92
- return new (Aws.appsync.SourceApiAssociation)(name, {
93
- mergedApiArn: mergedApiArn,
94
- sourceApiId: Output$Pulumi.flatMap(sourceApi, a => a.id),
95
- sourceApiAssociationConfigs: [{
96
- mergeType: "AUTO_MERGE"
97
- }]
93
+ return AppSync_SourceApiAssociation_Retrying$ReventlessAws.make(name, {
94
+ mergedApiIdentifier: mergedApiArn,
95
+ sourceApiIdentifier: Output$Pulumi.flatMap(sourceApi, a => a.id),
96
+ mergeType: "AUTO_MERGE"
98
97
  }, customOpts);
99
98
  }
100
99
 
@@ -14,6 +14,7 @@ function Make(Api) {
14
14
  make: RuntimeEnvironment_Lambda$ReventlessAws.make,
15
15
  groupBySource: RuntimeEnvironment_Lambda$ReventlessAws.groupBySource,
16
16
  extractCorrelationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCorrelationId,
17
+ extractCausationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCausationId,
17
18
  asEventHandler: prim => prim,
18
19
  asEffectHandler: prim => prim
19
20
  })({
@@ -16,6 +16,7 @@ function Make(Spec) {
16
16
  make: RuntimeEnvironment_Lambda$ReventlessAws.make,
17
17
  groupBySource: RuntimeEnvironment_Lambda$ReventlessAws.groupBySource,
18
18
  extractCorrelationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCorrelationId,
19
+ extractCausationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCausationId,
19
20
  asEventHandler: prim => prim,
20
21
  asEffectHandler: prim => prim
21
22
  })({
@@ -11,6 +11,7 @@ let EventCollectorRuntimeBuilder = EventCollectorRuntime_Builder_PerEventCollect
11
11
  make: RuntimeEnvironment_Lambda$ReventlessAws.make,
12
12
  groupBySource: RuntimeEnvironment_Lambda$ReventlessAws.groupBySource,
13
13
  extractCorrelationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCorrelationId,
14
+ extractCausationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCausationId,
14
15
  asEventHandler: prim => prim,
15
16
  asEffectHandler: prim => prim
16
17
  })({
@@ -23,6 +24,7 @@ function Make(Spec) {
23
24
  make: RuntimeEnvironment_Lambda$ReventlessAws.make,
24
25
  groupBySource: RuntimeEnvironment_Lambda$ReventlessAws.groupBySource,
25
26
  extractCorrelationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCorrelationId,
27
+ extractCausationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCausationId,
26
28
  asEventHandler: prim => prim,
27
29
  asEffectHandler: prim => prim
28
30
  })({
@@ -21,6 +21,7 @@ function Make(Api) {
21
21
  make: RuntimeEnvironment_Lambda$ReventlessAws.make,
22
22
  groupBySource: RuntimeEnvironment_Lambda$ReventlessAws.groupBySource,
23
23
  extractCorrelationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCorrelationId,
24
+ extractCausationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCausationId,
24
25
  asEventHandler: prim => prim,
25
26
  asEffectHandler: prim => prim
26
27
  })({
@@ -33,6 +33,7 @@ function Make(HooksConfig) {
33
33
  make: RuntimeEnvironment_Lambda$ReventlessAws.make,
34
34
  groupBySource: RuntimeEnvironment_Lambda$ReventlessAws.groupBySource,
35
35
  extractCorrelationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCorrelationId,
36
+ extractCausationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCausationId,
36
37
  asEventHandler: prim => prim,
37
38
  asEffectHandler: prim => prim
38
39
  })({