@reventlessdev/reventless-aws 3.0.0-alpha.308 → 3.0.0-alpha.310

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 (56) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/package.json +11 -11
  3. package/src/Platform.res +1 -1
  4. package/src/Platform.res.mjs +3 -3
  5. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res +1 -1
  6. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res.mjs +2 -2
  7. package/src/adapter/Counter/CounterHandler_DynamoDbStream_Runtime.res +1 -1
  8. package/src/adapter/Counter/CounterHandler_DynamoDbStream_Runtime.res.mjs +6 -5
  9. package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb_Runtime.res +1 -1
  10. package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb_Runtime.res.mjs +3 -3
  11. package/src/adapter/Mcp/MCP_Lambda.res +2 -2
  12. package/src/adapter/Mcp/MCP_Lambda.res.mjs +4 -4
  13. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +1 -1
  14. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res.mjs +3 -3
  15. package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res +1 -1
  16. package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res.mjs +3 -3
  17. package/src/adapter/Postgres/PgConnection.res.mjs +7 -7
  18. package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +2 -2
  19. package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs +4 -4
  20. package/src/adapter/Runtime/CounterEntryPoint_Ops.res +1 -1
  21. package/src/adapter/Runtime/CounterEntryPoint_Ops.res.mjs +6 -5
  22. package/src/adapter/Runtime/DcbCommandTopicEntryPoint.mjs +3 -1
  23. package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res +1 -1
  24. package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res.mjs +3 -3
  25. package/src/adapter/Runtime/HeartbeatEntryPoint.res +1 -1
  26. package/src/adapter/Runtime/HeartbeatEntryPoint.res.mjs +2 -2
  27. package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +11 -1
  28. package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs +1 -1
  29. package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res +1 -1
  30. package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res.mjs +3 -3
  31. package/src/util/Util_DeadLetterQueue.res +10 -5
  32. package/src/util/Util_DeadLetterQueue.res.mjs +4 -1
  33. package/src/util/Util_DynamoDb.res +7 -2
  34. package/src/util/Util_DynamoDb.res.mjs +2 -2
  35. package/src/util/Util_LambdaLogging.res +28 -0
  36. package/src/util/Util_LambdaLogging.res.mjs +15 -0
  37. package/tests/AppSync_AdapterTest.res.mjs +13 -13
  38. package/tests/AutomationSliceEntryPoint_OpsTest.res.mjs +5 -5
  39. package/tests/EpInboundTestSlice.res.mjs +6 -6
  40. package/tests/PgChangeFeedRelay_IntegrationTest.res +1 -1
  41. package/tests/PgChangeFeedRelay_IntegrationTest.res.mjs +2 -2
  42. package/tests/PgChangeFeedRelay_RuntimeTest.res +2 -2
  43. package/tests/PgChangeFeedRelay_RuntimeTest.res.mjs +3 -3
  44. package/tests/PgPipeline_IntegrationTest.res +1 -1
  45. package/tests/PgPipeline_IntegrationTest.res.mjs +8 -7
  46. package/tests/StateViewSliceEntryPoint_OpsTest.res.mjs +3 -3
  47. package/tests/Util_DynamoDbTest.res +44 -0
  48. package/tests/Util_DynamoDbTest.res.mjs +54 -0
  49. package/tests/Util_LambdaLoggingTest.res +27 -0
  50. package/tests/Util_LambdaLoggingTest.res.mjs +9 -0
  51. package/tests/integration/AggTestAggregate.res.mjs +6 -6
  52. package/tests/integration/EpCompositeSlice.res.mjs +17 -17
  53. package/tests/integration/EpTestSlice.res.mjs +6 -6
  54. package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res +1 -1
  55. package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res.mjs +4 -4
  56. package/tests/integration/SvsTestSlice.res.mjs +9 -9
@@ -1,12 +1,12 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
- import * as S from "sury/src/S.res.mjs";
4
3
  import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
5
4
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
6
5
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
7
6
  import * as Effect from "effect/Effect";
8
7
  import * as Stream from "effect/Stream";
9
8
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
9
+ import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
10
10
  import * as Projection$ReventlessCore from "@reventlessdev/reventless-core/src/Projection.res.mjs";
11
11
  import * as ProjectionEntryPoint_Ops$ReventlessAws from "./ProjectionEntryPoint_Ops.res.mjs";
12
12
  import * as StreamRoutedEntryPoint_Ops$ReventlessAws from "./StreamRoutedEntryPoint_Ops.res.mjs";
@@ -68,7 +68,7 @@ function makeJsonEventsHandler(sliceName, eventSchema, project, queryDbOps, subI
68
68
  try {
69
69
  let match = decodeEnvelope(json);
70
70
  return project({
71
- event: S.parseJsonOrThrow(match.event, eventSchema),
71
+ event: Util_Sury$Reventless.fromJson(match.event, eventSchema),
72
72
  meta: match.meta,
73
73
  recordedAt: match.recordedAt
74
74
  });
@@ -102,4 +102,4 @@ export {
102
102
  makeJsonEventsHandler,
103
103
  makeRegisteredHandler,
104
104
  }
105
- /* S Not a pure module */
105
+ /* effect/Effect Not a pure module */
@@ -113,15 +113,20 @@ let handler = Lambda.Function.make(
113
113
  ~opts,
114
114
  )
115
115
 
116
- // Second Monitoring provisioning site (§2): the dead-letter mechanism. Role-based
116
+ // Second Monitoring provisioning site: the dead-letter mechanism. Role-based
117
117
  // kind, mechanism-agnostic resource — no-op unless a backend is registered.
118
+ let deadLetterResource = Util_Lambda.functionToResource(
119
+ handler,
120
+ ~tags=AWS.Tags.make(~name, ~kind=ReventlessCore.ComponentType.Plugin, ~role=DeadLetter, ~scope=Plugin)->Pulumi.Output.fromInput,
121
+ )
122
+
118
123
  ReventlessCore.Monitoring.notify(
119
124
  ~kind=DeadLetterSink,
120
125
  ~name,
121
- ~component=Util_Lambda.functionToResource(
122
- handler,
123
- ~tags=AWS.Tags.make(~name, ~kind=ReventlessCore.ComponentType.Plugin, ~role=DeadLetter, ~scope=Plugin)->Pulumi.Output.fromInput,
124
- ),
126
+ ~component=deadLetterResource,
127
+ // This handler is built without a managed group, so its logs are in the one
128
+ // Lambda auto-creates from the physical name.
129
+ ~logLocator=Util_LambdaLogging.logLocatorFor(~logGroup=None, ~physicalName=deadLetterResource.name),
125
130
  )
126
131
 
127
132
  let lambda = handler->Pulumi.Output.make
@@ -85,7 +85,9 @@ let handler = new (Aws.lambda.Function)(name, {
85
85
  sourceCodeHash: sourceCodeHash
86
86
  }, opts);
87
87
 
88
- Monitoring$ReventlessCore.notify("DeadLetterSink", name, Util_Lambda$ReventlessAws.functionToResource(AWS_Tags$ReventlessAws.make(name, "Plugin", "DeadLetter", "Plugin", undefined, undefined, undefined, undefined), handler));
88
+ let deadLetterResource = Util_Lambda$ReventlessAws.functionToResource(AWS_Tags$ReventlessAws.make(name, "Plugin", "DeadLetter", "Plugin", undefined, undefined, undefined, undefined), handler);
89
+
90
+ Monitoring$ReventlessCore.notify("DeadLetterSink", name, deadLetterResource, Util_LambdaLogging$ReventlessAws.logLocatorFor(undefined, deadLetterResource.name));
89
91
 
90
92
  let lambda = Pulumi.output(handler);
91
93
 
@@ -177,6 +179,7 @@ export {
177
179
  sourceCodeHash,
178
180
  layers,
179
181
  handler,
182
+ deadLetterResource,
180
183
  lambda,
181
184
  esmTags,
182
185
  _subscription,
@@ -7,7 +7,12 @@ let log = ReventlessCore.Logger.fromEnv()
7
7
  let toResourceInfo: table => Pulumi.Output.t<ReventlessInfra.Adapter.resourceInfo> = ({hashKey, rangeKey}) =>
8
8
  (hashKey, rangeKey)
9
9
  ->Pulumi.Output.all2
10
- ->Pulumi.Output.apply(((hashKey, rangeKey)) => ReventlessInfra.Adapter.StorageKeys({partitionKey: hashKey, sortKey: rangeKey}))
10
+ ->Pulumi.Output.apply(((hashKey, rangeKey)) => ReventlessInfra.Adapter.StorageKeys({
11
+ partitionKey: hashKey,
12
+ // Normalize here: a null reaching the plugin-structure export fails sury's
13
+ // encode-side validation, which expects `option`'s undefined.
14
+ sortKey: rangeKey->Nullable.toOption,
15
+ }))
11
16
 
12
17
  let toResolvedTableOutput = ({name, id, arn, hashKey, rangeKey}) =>
13
18
  (name, id, arn, hashKey, rangeKey)
@@ -17,7 +22,7 @@ let toResolvedTableOutput = ({name, id, arn, hashKey, rangeKey}) =>
17
22
  name,
18
23
  arn,
19
24
  hashKey,
20
- rangeKey: ?(rangeKey->Option.map(rangeKey => rangeKey->Nullable.make)),
25
+ rangeKey: ?(rangeKey->Nullable.toOption->Option.map(Nullable.make)),
21
26
  })
22
27
 
23
28
  let toResource = (~tags=?, {id, name, arn} as table) =>
@@ -24,7 +24,7 @@ function toResourceInfo(param) {
24
24
  ]).apply(param => ({
25
25
  TAG: "StorageKeys",
26
26
  partitionKey: param[0],
27
- sortKey: param[1]
27
+ sortKey: Primitive_option.fromNullable(param[1])
28
28
  }));
29
29
  }
30
30
 
@@ -40,7 +40,7 @@ function toResolvedTableOutput(param) {
40
40
  name: param[0],
41
41
  arn: param[2],
42
42
  hashKey: param[3],
43
- rangeKey: Stdlib_Option.map(param[4], rangeKey => rangeKey)
43
+ rangeKey: Stdlib_Option.map(Primitive_option.fromNullable(param[4]), prim => prim)
44
44
  }));
45
45
  }
46
46
 
@@ -124,6 +124,34 @@ let makeManagedLogGroup = (
124
124
  )
125
125
  }
126
126
 
127
+ /** The group Lambda auto-creates for a function it was given no `loggingConfig`
128
+ for — keyed on the function's PHYSICAL name, suffix and all, which is the
129
+ whole reason it differs from the managed name above.
130
+
131
+ Kept here, beside `logGroupNameFor`, so both naming shapes a Lambda's logs can
132
+ take live in the one module that owns log-group naming and neither is
133
+ reconstructed at a call site. */
134
+ let autoCreatedLogGroupNameFor = (~physicalName: string): string =>
135
+ `/aws/lambda/${physicalName}`
136
+
137
+ /** Where a function's logs actually land, for a caller that wants to point
138
+ somebody at them (a monitoring backend stamping an address into an alert).
139
+
140
+ Which of the two shapes applies is decided by `makeManagedLogGroup`, from
141
+ stack configuration, and is invisible to anyone downstream — a consumer that
142
+ derives the name instead of asking would be right only on the stacks that
143
+ manage their groups and would send an operator to a group that does not exist
144
+ on the ones that do not. So the answer comes from the site that took the
145
+ branch, and the caller passes what it got back from `makeManagedLogGroup`. */
146
+ let logLocatorFor = (
147
+ ~logGroup: option<Cloudwatch.LogGroup.t>,
148
+ ~physicalName: Pulumi.Output.t<string>,
149
+ ): Pulumi.Output.t<string> =>
150
+ switch logGroup {
151
+ | Some(group) => group.name
152
+ | None => physicalName->Pulumi.Output.map(n => autoCreatedLogGroupNameFor(~physicalName=n))
153
+ }
154
+
127
155
  // The `loggingConfig` that points a function at the group `makeManagedLogGroup`
128
156
  // just created. Reading `logGroup.name` is also what orders the two: Pulumi sees
129
157
  // the function depend on the group, so the group exists first and the function
@@ -2,6 +2,7 @@
2
2
 
3
3
  import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
4
4
  import * as Aws from "@pulumi/aws";
5
+ import * as Output$Pulumi from "@reventlessdev/rescript-pulumi-pulumi/src/Output.res.mjs";
5
6
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
7
  import * as Pulumi from "@pulumi/pulumi";
7
8
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
@@ -54,6 +55,18 @@ function makeManagedLogGroup(name, retentionDaysOverride, tags, opts, param) {
54
55
  }, opts !== undefined ? Primitive_option.valFromOption(opts) : undefined));
55
56
  }
56
57
 
58
+ function autoCreatedLogGroupNameFor(physicalName) {
59
+ return `/aws/lambda/` + physicalName;
60
+ }
61
+
62
+ function logLocatorFor(logGroup, physicalName) {
63
+ if (logGroup !== undefined) {
64
+ return logGroup.name;
65
+ } else {
66
+ return Output$Pulumi.map(physicalName, autoCreatedLogGroupNameFor);
67
+ }
68
+ }
69
+
57
70
  function loggingConfigFor(logGroup) {
58
71
  return Stdlib_Option.map(logGroup, group => ({
59
72
  logFormat: "Text",
@@ -67,6 +80,8 @@ export {
67
80
  applyLogLevelDefault,
68
81
  logGroupNameFor,
69
82
  makeManagedLogGroup,
83
+ autoCreatedLogGroupNameFor,
84
+ logLocatorFor,
70
85
  loggingConfigFor,
71
86
  }
72
87
  /* @pulumi/aws Not a pure module */
@@ -1,6 +1,6 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
- import * as S from "sury/src/S.res.mjs";
3
+ import * as Sury from "sury";
4
4
  import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
5
5
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
6
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
@@ -118,14 +118,14 @@ globalThis.describe("AppSync_Adapter.injectAwsAuth — Stage E2 permission lifti
118
118
  });
119
119
  let mutationEntry = (fieldNames, fieldPermissions) => ({
120
120
  fieldNames: fieldNames,
121
- commandSchema: S.unknown,
121
+ commandSchema: Sury.unknown,
122
122
  fieldPermissions: fieldPermissions
123
123
  });
124
124
  let queryEntry = (single, list, permission) => ({
125
125
  singleFieldName: single,
126
126
  listFieldName: list,
127
127
  returnTypeName: single,
128
- stateSchema: S.unknown,
128
+ stateSchema: Sury.unknown,
129
129
  authorization: undefined,
130
130
  permission: permission
131
131
  });
@@ -261,7 +261,7 @@ globalThis.describe("AppSync_Adapter.injectAwsAuth — Stage E2 permission lifti
261
261
  let entry_fieldPermissions = fp;
262
262
  let entry = {
263
263
  fieldNames: entry_fieldNames,
264
- commandSchema: S.unknown,
264
+ commandSchema: Sury.unknown,
265
265
  authorization: entry_authorization,
266
266
  fieldPermissions: entry_fieldPermissions
267
267
  };
@@ -285,7 +285,7 @@ globalThis.describe("AppSync_Adapter.injectAwsAuth — Stage E2 permission lifti
285
285
  let entry_fieldPermissions = fp;
286
286
  let entry = {
287
287
  fieldNames: entry_fieldNames,
288
- commandSchema: S.unknown,
288
+ commandSchema: Sury.unknown,
289
289
  authorization: entry_authorization,
290
290
  fieldPermissions: entry_fieldPermissions
291
291
  };
@@ -318,7 +318,7 @@ globalThis.describe("AppSync_Adapter.injectAwsAuth — systemCallable dual-auth"
318
318
  let entry_systemCallable = true;
319
319
  let entry = {
320
320
  fieldNames: entry_fieldNames,
321
- commandSchema: S.unknown,
321
+ commandSchema: Sury.unknown,
322
322
  fieldPermissions: entry_fieldPermissions,
323
323
  systemCallable: entry_systemCallable
324
324
  };
@@ -338,7 +338,7 @@ globalThis.describe("AppSync_Adapter.injectAwsAuth — systemCallable dual-auth"
338
338
  let entry_systemCallable = true;
339
339
  let entry = {
340
340
  fieldNames: entry_fieldNames,
341
- commandSchema: S.unknown,
341
+ commandSchema: Sury.unknown,
342
342
  fieldPermissions: entry_fieldPermissions,
343
343
  systemCallable: entry_systemCallable
344
344
  };
@@ -373,7 +373,7 @@ globalThis.describe("AppSync_Adapter.injectAwsAuth — systemCallable dual-auth"
373
373
  let entry_systemCallable = false;
374
374
  let entry = {
375
375
  fieldNames: entry_fieldNames,
376
- commandSchema: S.unknown,
376
+ commandSchema: Sury.unknown,
377
377
  fieldPermissions: entry_fieldPermissions,
378
378
  systemCallable: entry_systemCallable
379
379
  };
@@ -388,7 +388,7 @@ globalThis.describe("AppSync_Adapter.injectAwsAuth — systemCallable dual-auth"
388
388
  let iamEntry_systemCallable = true;
389
389
  let iamEntry = {
390
390
  fieldNames: iamEntry_fieldNames,
391
- commandSchema: S.unknown,
391
+ commandSchema: Sury.unknown,
392
392
  fieldPermissions: iamEntry_fieldPermissions,
393
393
  systemCallable: iamEntry_systemCallable
394
394
  };
@@ -420,7 +420,7 @@ globalThis.describe("AppSync_Adapter.injectAwsAuth — systemCallable dual-auth"
420
420
  singleFieldName: "p_Item",
421
421
  listFieldName: "p_Items",
422
422
  returnTypeName: "p_Item",
423
- stateSchema: S.unknown,
423
+ stateSchema: Sury.unknown,
424
424
  authorization: undefined,
425
425
  permission: entry_permission,
426
426
  systemCallable: entry_systemCallable
@@ -453,7 +453,7 @@ globalThis.describe("AppSync_Adapter.injectAwsAuth — systemCallable dual-auth"
453
453
  singleFieldName: "p_Item",
454
454
  listFieldName: "p_Items",
455
455
  returnTypeName: "p_Item",
456
- stateSchema: S.unknown,
456
+ stateSchema: Sury.unknown,
457
457
  authorization: undefined,
458
458
  permission: entry_permission,
459
459
  systemCallable: entry_systemCallable
@@ -493,7 +493,7 @@ globalThis.describe("AppSync_Adapter — type-level dual-auth", () => {
493
493
  singleFieldName: "p_Item",
494
494
  listFieldName: "p_Items",
495
495
  returnTypeName: "p_Item",
496
- stateSchema: S.unknown,
496
+ stateSchema: Sury.unknown,
497
497
  authorization: undefined,
498
498
  permission: callableEntry_permission,
499
499
  systemCallable: callableEntry_systemCallable
@@ -761,7 +761,7 @@ globalThis.describe("AppSync_Adapter.injectAwsAuth — subscriptions", () => {
761
761
  });
762
762
  let entry = (fieldNames, fieldPermissions) => ({
763
763
  fieldNames: fieldNames,
764
- commandSchema: S.unknown,
764
+ commandSchema: Sury.unknown,
765
765
  fieldPermissions: fieldPermissions
766
766
  });
767
767
  globalThis.test("an unannotated subscription gets the group-less Cognito directive", () => {
@@ -1,6 +1,6 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
- import * as S from "sury/src/S.res.mjs";
3
+ import * as Sury from "sury";
4
4
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
5
  import * as Effect from "effect/Effect";
6
6
  import * as Stream from "effect/Stream";
@@ -246,12 +246,12 @@ globalThis.describe("AutomationSliceEntryPoint_Ops.runSweeps", () => {
246
246
  });
247
247
  });
248
248
 
249
- let outboundEventSchema = S.union([
250
- S.schema(s => ({
249
+ let outboundEventSchema = Sury.union([
250
+ Sury.$schema(s => ({
251
251
  TAG: "OrderPlaced",
252
- orderId: s.m(S.string)
252
+ orderId: s.m(Sury.string)
253
253
  })),
254
- S.literal("OrderArchived")
254
+ Sury.literal("OrderArchived")
255
255
  ]);
256
256
 
257
257
  globalThis.describe("AutomationSliceEntryPoint_Ops.makeOutboundJsonEventsHandler", () => {
@@ -1,15 +1,15 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
- import * as S from "sury/src/S.res.mjs";
3
+ import * as Sury from "sury";
4
4
 
5
- let externalInputSchema = S.schema(s => ({
6
- sku: s.m(S.string),
7
- currency: s.m(S.string)
5
+ let externalInputSchema = Sury.$schema(s => ({
6
+ sku: s.m(Sury.string),
7
+ currency: s.m(Sury.string)
8
8
  }));
9
9
 
10
- let commandSchema = S.schema(s => ({
10
+ let commandSchema = Sury.$schema(s => ({
11
11
  TAG: "AddThing",
12
- thingId: s.m(S.string)
12
+ thingId: s.m(Sury.string)
13
13
  }));
14
14
 
15
15
  let name = "EpInboundTest";
@@ -239,7 +239,7 @@ switch NodeProcess.env->Dict.get("PG_URL") {
239
239
 
240
240
  // The relay receives the sury-encoded partition tag exactly as the builder emits it.
241
241
  let partitionTagJson =
242
- DcbTag.Simple({key: "customerId"})->S.reverseConvertToJsonOrThrow(
242
+ DcbTag.Simple({key: "customerId"})->Reventless.Util_Sury.toJson(
243
243
  Reventless.DcbTag.derivedPartitionTagSchema,
244
244
  )
245
245
 
@@ -1,9 +1,9 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
- import * as S from "sury/src/S.res.mjs";
4
3
  import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
5
4
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
5
  import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
6
+ import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
7
7
  import * as Message$ReventlessCore from "@reventlessdev/reventless-core/src/Message.res.mjs";
8
8
  import * as PgDriver$ReventlessPostgres from "@reventlessdev/reventless-postgres/src/PgDriver.res.mjs";
9
9
  import * as PgSchema$ReventlessPostgres from "@reventlessdev/reventless-postgres/src/PgSchema.res.mjs";
@@ -209,7 +209,7 @@ if (url !== undefined) {
209
209
  1
210
210
  ]]));
211
211
  await match[1].append([ev], undefined);
212
- let partitionTagJson = S.reverseConvertToJsonOrThrow({
212
+ let partitionTagJson = Util_Sury$Reventless.toJson({
213
213
  TAG: "Simple",
214
214
  _0: {
215
215
  key: "customerId"
@@ -67,8 +67,8 @@ describe("PgChangeFeedRelay_Runtime partitionTag (B2.3d)", () => {
67
67
  let composite =
68
68
  Reventless.DcbTag.Composite({keys: ["studentId", "courseId"], seps: [":"]})
69
69
  [simple, composite]->Array.forEach(pt => {
70
- let wire = pt->S.reverseConvertToJsonOrThrow(Reventless.DcbTag.derivedPartitionTagSchema)
71
- let back = wire->S.parseJsonOrThrow(Reventless.DcbTag.derivedPartitionTagSchema)
70
+ let wire = pt->Reventless.Util_Sury.toJson(Reventless.DcbTag.derivedPartitionTagSchema)
71
+ let back = wire->Reventless.Util_Sury.fromJson(Reventless.DcbTag.derivedPartitionTagSchema)
72
72
  expect(back)->toEqual(pt)
73
73
  })
74
74
  })
@@ -1,9 +1,9 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
- import * as S from "sury/src/S.res.mjs";
4
3
  import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
5
4
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
5
  import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
6
+ import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
7
7
  import * as PgChangeFeedRelay_Runtime$ReventlessAws from "../src/adapter/Postgres/PgChangeFeedRelay_Runtime.res.mjs";
8
8
 
9
9
  let sampleEvent_data = Object.fromEntries([[
@@ -122,8 +122,8 @@ globalThis.describe("PgChangeFeedRelay_Runtime partitionTag (B2.3d)", () => {
122
122
  },
123
123
  composite
124
124
  ].forEach(pt => {
125
- let wire = S.reverseConvertToJsonOrThrow(pt, DcbTag$Reventless.derivedPartitionTagSchema);
126
- let back = S.parseJsonOrThrow(wire, DcbTag$Reventless.derivedPartitionTagSchema);
125
+ let wire = Util_Sury$Reventless.toJson(pt, DcbTag$Reventless.derivedPartitionTagSchema);
126
+ let back = Util_Sury$Reventless.fromJson(wire, DcbTag$Reventless.derivedPartitionTagSchema);
127
127
  globalThis.expect(back).toEqual(pt);
128
128
  });
129
129
  });
@@ -68,7 +68,7 @@ module OrdersMappings = {
68
68
  let flatItem = (~id, ~seq, ~service, ~event: OrderSource.event) => {
69
69
  let (eventType, data) =
70
70
  event
71
- ->S.reverseConvertToJsonOrThrow(OrderSource.eventSchema)
71
+ ->Reventless.Util_Sury.toJson(OrderSource.eventSchema)
72
72
  ->Message.splitMessage
73
73
  [
74
74
  ("id", JSON.Encode.string(id)),
@@ -1,11 +1,12 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
- import * as S from "sury/src/S.res.mjs";
3
+ import * as Sury from "sury";
4
4
  import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
5
5
  import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
6
6
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
7
7
  import * as Effect from "effect/Effect";
8
8
  import * as ReadModel$Reventless from "@reventlessdev/reventless-spec/src/components/ReadModel.res.mjs";
9
+ import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
9
10
  import * as Projection$Reventless from "@reventlessdev/reventless-spec/src/types/Projection.res.mjs";
10
11
  import * as Message$ReventlessCore from "@reventlessdev/reventless-core/src/Message.res.mjs";
11
12
  import * as PgDriver$ReventlessPostgres from "@reventlessdev/reventless-postgres/src/PgDriver.res.mjs";
@@ -18,9 +19,9 @@ import * as EventCollectorChannel_DynamoDbStream_Runtime$ReventlessAws from "../
18
19
 
19
20
  let name = "Order";
20
21
 
21
- let eventSchema = S.schema(s => ({
22
+ let eventSchema = Sury.$schema(s => ({
22
23
  TAG: "OrderPlaced",
23
- product: s.m(S.string)
24
+ product: s.m(Sury.string)
24
25
  }));
25
26
 
26
27
  let OrderSource = {
@@ -33,9 +34,9 @@ let name$1 = "PipelineOrders";
33
34
 
34
35
  let moduleUrl = "";
35
36
 
36
- let stateSchema = S.schema(s => ({
37
- id: s.m(S.string),
38
- product: s.m(S.string)
37
+ let stateSchema = Sury.$schema(s => ({
38
+ id: s.m(Sury.string),
39
+ product: s.m(Sury.string)
39
40
  }));
40
41
 
41
42
  let config = ReadModel$Reventless.config(undefined, undefined, undefined);
@@ -113,7 +114,7 @@ let OrdersMappings = {
113
114
  };
114
115
 
115
116
  function flatItem(id, seq, service, event) {
116
- let match = Message$ReventlessCore.splitMessage(S.reverseConvertToJsonOrThrow(event, eventSchema));
117
+ let match = Message$ReventlessCore.splitMessage(Util_Sury$Reventless.toJson(event, eventSchema));
117
118
  return Object.fromEntries([
118
119
  [
119
120
  "id",
@@ -1,6 +1,6 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
- import * as S from "sury/src/S.res.mjs";
3
+ import * as Sury from "sury";
4
4
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
5
  import * as Effect from "effect/Effect";
6
6
  import * as Stream from "effect/Stream";
@@ -249,7 +249,7 @@ globalThis.describe("StateViewSliceEntryPoint_Ops.makeJsonEventsHandler", () =>
249
249
  }];
250
250
  globalThis.test("decodes envelopes, projects, and runs actions against the ops", async () => {
251
251
  let saved = [];
252
- let handler = StateViewSliceEntryPoint_Ops$ReventlessAws.makeJsonEventsHandler("TestSlice", S.string, project, makeStub(saved), undefined);
252
+ let handler = StateViewSliceEntryPoint_Ops$ReventlessAws.makeJsonEventsHandler("TestSlice", Sury.string, project, makeStub(saved), undefined);
253
253
  let envelope = Object.fromEntries([
254
254
  [
255
255
  "event",
@@ -277,7 +277,7 @@ globalThis.describe("StateViewSliceEntryPoint_Ops.makeJsonEventsHandler", () =>
277
277
  });
278
278
  globalThis.test("a decode failure yields no actions and does not wedge the batch", async () => {
279
279
  let saved = [];
280
- let handler = StateViewSliceEntryPoint_Ops$ReventlessAws.makeJsonEventsHandler("TestSlice", S.string, project, makeStub(saved), undefined);
280
+ let handler = StateViewSliceEntryPoint_Ops$ReventlessAws.makeJsonEventsHandler("TestSlice", Sury.string, project, makeStub(saved), undefined);
281
281
  await Effect.runPromise(handler(Stream.fromIterable([
282
282
  42,
283
283
  "row-after"
@@ -0,0 +1,44 @@
1
+ open JestGlobals
2
+
3
+ // Guards the null a table without a sort key resolves to.
4
+ //
5
+ // Pulumi resolves an absent `rangeKey` to `null`, and ReScript's `option` reads
6
+ // None as undefined — so the null travelled into `resourceInfo` unconverted and
7
+ // the plugin-structure export threw `Expected string | undefined, received null`
8
+ // out of sury's encode-side validation, failing the deploy after it had already
9
+ // applied part of the stack.
10
+
11
+ let resolve = (output: Pulumi.Output.t<'a>): promise<'a> =>
12
+ Promise.make((resolve, _) => {
13
+ let _ = output->Pulumi.Output.apply(value => resolve(value))
14
+ })
15
+
16
+ let table = (~rangeKey): PulumiAws.DynamoDb.Table.t => {
17
+ arn: "arn:aws:dynamodb:eu-west-1:123456789012:table/UiFragments-abc123"->Pulumi.Output.make,
18
+ name: "UiFragments-abc123"->Pulumi.Output.make,
19
+ id: "UiFragments-abc123"->Pulumi.Output.make,
20
+ hashKey: "id"->Pulumi.Output.make,
21
+ rangeKey: rangeKey->Pulumi.Output.make,
22
+ streamEnabled: None->Pulumi.Output.make,
23
+ streamArn: ""->Pulumi.Output.make,
24
+ streamLabel: ""->Pulumi.Output.make,
25
+ ttl: {PulumiAws.DynamoDb.Table.attributeName: ""}->Pulumi.Output.make,
26
+ pointInTimeRecovery: {PulumiAws.DynamoDb.Table.enabled: true}->Pulumi.Output.make,
27
+ }
28
+
29
+ describe("Util_DynamoDb.toResourceInfo", () => {
30
+ test("reads a table without a sort key as None", async () => {
31
+ let resourceInfo = await table(~rangeKey=Nullable.null)->Util_DynamoDb.toResourceInfo->resolve
32
+ expect(resourceInfo)->toEqual(
33
+ ReventlessInfra.Adapter.StorageKeys({partitionKey: "id", sortKey: None}),
34
+ )
35
+ })
36
+
37
+ test("carries a sort key when the table has one", async () => {
38
+ let resourceInfo =
39
+ await table(~rangeKey=Nullable.make("seq"))->Util_DynamoDb.toResourceInfo->resolve
40
+ expect(resourceInfo)->toEqual(
41
+ ReventlessInfra.Adapter.StorageKeys({partitionKey: "id", sortKey: Some("seq")}),
42
+ )
43
+ })
44
+ })
@@ -0,0 +1,54 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Pulumi from "@pulumi/pulumi";
4
+ import * as Util_DynamoDb$ReventlessAws from "../src/util/Util_DynamoDb.res.mjs";
5
+
6
+ function resolve(output) {
7
+ return new Promise((resolve, param) => {
8
+ output.apply(value => resolve(value));
9
+ });
10
+ }
11
+
12
+ function table(rangeKey) {
13
+ return {
14
+ arn: Pulumi.output("arn:aws:dynamodb:eu-west-1:123456789012:table/UiFragments-abc123"),
15
+ name: Pulumi.output("UiFragments-abc123"),
16
+ id: Pulumi.output("UiFragments-abc123"),
17
+ hashKey: Pulumi.output("id"),
18
+ rangeKey: Pulumi.output(rangeKey),
19
+ streamEnabled: Pulumi.output(undefined),
20
+ streamArn: Pulumi.output(""),
21
+ streamLabel: Pulumi.output(""),
22
+ ttl: Pulumi.output({
23
+ attributeName: ""
24
+ }),
25
+ pointInTimeRecovery: Pulumi.output({
26
+ enabled: true
27
+ })
28
+ };
29
+ }
30
+
31
+ globalThis.describe("Util_DynamoDb.toResourceInfo", () => {
32
+ globalThis.test("reads a table without a sort key as None", async () => {
33
+ let resourceInfo = await resolve(Util_DynamoDb$ReventlessAws.toResourceInfo(table(null)));
34
+ globalThis.expect(resourceInfo).toEqual({
35
+ TAG: "StorageKeys",
36
+ partitionKey: "id",
37
+ sortKey: undefined
38
+ });
39
+ });
40
+ globalThis.test("carries a sort key when the table has one", async () => {
41
+ let resourceInfo = await resolve(Util_DynamoDb$ReventlessAws.toResourceInfo(table("seq")));
42
+ globalThis.expect(resourceInfo).toEqual({
43
+ TAG: "StorageKeys",
44
+ partitionKey: "id",
45
+ sortKey: "seq"
46
+ });
47
+ });
48
+ });
49
+
50
+ export {
51
+ resolve,
52
+ table,
53
+ }
54
+ /* Not a pure module */
@@ -46,3 +46,30 @@ describe("Util_LambdaLogging.logGroupNameFor", () => {
46
46
  expect(nameFor(~name="AllReadModels"))->not_->toBe(autoCreated)
47
47
  })
48
48
  })
49
+
50
+ // A Lambda's logs land in one of two differently-shaped groups, and which one is
51
+ // a stack-configuration decision invisible outside this module. Anything that
52
+ // wants to point a human at those logs has to be told which shape applied, so
53
+ // both are pinned here — a consumer deriving one of them would be right on half
54
+ // the estate and send an operator to a nonexistent group on the other half.
55
+ describe("Util_LambdaLogging.autoCreatedLogGroupNameFor", () => {
56
+ testSync("is the group Lambda makes from the PHYSICAL name, suffix and all", () =>
57
+ expect(Util_LambdaLogging.autoCreatedLogGroupNameFor(~physicalName="AllReadModels-0287438"))
58
+ ->toBe("/aws/lambda/AllReadModels-0287438")
59
+ )
60
+
61
+ // The two shapes must not converge: the managed name is chosen from the static
62
+ // name so it survives a replacement, the auto-created one is not. If these ever
63
+ // matched, the managed group would be racing the auto-create it exists to avoid.
64
+ testSync("never equals the managed name for the same unit", () =>
65
+ expect(
66
+ Util_LambdaLogging.autoCreatedLogGroupNameFor(~physicalName="AllReadModels-0287438"),
67
+ )->not_->toBe(
68
+ Util_LambdaLogging.logGroupNameFor(
69
+ ~project="online-shop-catalog-aws",
70
+ ~stack="alpha",
71
+ ~name="AllReadModels",
72
+ ),
73
+ )
74
+ )
75
+ })
@@ -27,4 +27,13 @@ globalThis.describe("Util_LambdaLogging.logGroupNameFor", () => {
27
27
  });
28
28
  });
29
29
 
30
+ globalThis.describe("Util_LambdaLogging.autoCreatedLogGroupNameFor", () => {
31
+ globalThis.test("is the group Lambda makes from the PHYSICAL name, suffix and all", () => {
32
+ globalThis.expect(Util_LambdaLogging$ReventlessAws.autoCreatedLogGroupNameFor("AllReadModels-0287438")).toBe("/aws/lambda/AllReadModels-0287438");
33
+ });
34
+ globalThis.test("never equals the managed name for the same unit", () => {
35
+ globalThis.expect(Util_LambdaLogging$ReventlessAws.autoCreatedLogGroupNameFor("AllReadModels-0287438")).not.toBe(Util_LambdaLogging$ReventlessAws.logGroupNameFor("online-shop-catalog-aws", "alpha", "AllReadModels"));
36
+ });
37
+ });
38
+
30
39
  /* Not a pure module */