@reventlessdev/reventless-aws 3.0.0-alpha.231 → 3.0.0-alpha.233

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 (104) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/package.json +7 -7
  3. package/src/adapter/Api/AppSyncEventsSigner_Ops.res +137 -0
  4. package/src/adapter/Api/AppSyncEventsSigner_Ops.res.mjs +121 -0
  5. package/src/adapter/Api/Platform_AdminScan_Ops.res +136 -0
  6. package/src/adapter/Api/Platform_AdminScan_Ops.res.mjs +98 -0
  7. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +12 -90
  8. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res.mjs +11 -84
  9. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res +86 -0
  10. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res.mjs +79 -0
  11. package/src/adapter/Api/Platform_UIFragments_Lambda.res +9 -79
  12. package/src/adapter/Api/Platform_UIFragments_Lambda.res.mjs +7 -143
  13. package/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res +48 -0
  14. package/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res.mjs +63 -0
  15. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res +21 -80
  16. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res.mjs +23 -68
  17. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res +70 -0
  18. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res.mjs +68 -0
  19. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +1 -1
  20. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res.mjs +1 -1
  21. package/src/adapter/Postgres/PgMigration_Builder.res +1 -1
  22. package/src/adapter/Postgres/PgMigration_Builder.res.mjs +1 -1
  23. package/src/adapter/Runtime/AutomationSliceEntryPoint.mjs +42 -126
  24. package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +246 -0
  25. package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs +145 -0
  26. package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +27 -2
  27. package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res.mjs +10 -3
  28. package/src/adapter/Runtime/CounterEntryPoint.mjs +19 -73
  29. package/src/adapter/Runtime/CounterEntryPoint_Ops.res +145 -0
  30. package/src/adapter/Runtime/CounterEntryPoint_Ops.res.mjs +129 -0
  31. package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res +16 -3
  32. package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res.mjs +11 -6
  33. package/src/adapter/Runtime/EventCollectorEntryPoint.mjs +52 -510
  34. package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res +816 -0
  35. package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res.mjs +651 -0
  36. package/src/adapter/Runtime/EventMapperEntryPoint.mjs +23 -67
  37. package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res +126 -0
  38. package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res.mjs +83 -0
  39. package/src/adapter/Runtime/ExtensionPointEntryPoint.mjs +16 -63
  40. package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res +150 -0
  41. package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res.mjs +111 -0
  42. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res +96 -0
  43. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res.mjs +70 -0
  44. package/src/adapter/Runtime/PgMigrationEntryPoint.res +54 -0
  45. package/src/adapter/Runtime/PgMigrationEntryPoint.res.mjs +37 -0
  46. package/src/adapter/Runtime/PgQueryResolverEntryPoint.mjs +20 -55
  47. package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res +155 -0
  48. package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res.mjs +94 -0
  49. package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res +80 -0
  50. package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res.mjs +39 -0
  51. package/src/adapter/Runtime/QueryDbEntryPoint_Ops.res +4 -4
  52. package/src/adapter/Runtime/ReadModelEntryPoint.mjs +34 -153
  53. package/src/adapter/Runtime/ReadModelEntryPoint_Ops.res +114 -0
  54. package/src/adapter/Runtime/ReadModelEntryPoint_Ops.res.mjs +83 -0
  55. package/src/adapter/Runtime/SideEffectEntryPoint.mjs +23 -64
  56. package/src/adapter/Runtime/SideEffectEntryPoint_Ops.res +71 -0
  57. package/src/adapter/Runtime/SideEffectEntryPoint_Ops.res.mjs +57 -0
  58. package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +34 -165
  59. package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res +206 -0
  60. package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res.mjs +105 -0
  61. package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res +156 -0
  62. package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res.mjs +123 -0
  63. package/src/adapter/Runtime/TaskBucketEntryPoint.mjs +18 -138
  64. package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res +198 -0
  65. package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res.mjs +155 -0
  66. package/src/adapter/StateTopic/StateTopic_AppSync.res +12 -170
  67. package/src/adapter/StateTopic/StateTopic_AppSync.res.mjs +7 -167
  68. package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res +208 -0
  69. package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res.mjs +183 -0
  70. package/src/components/AutomationSlice_Builder.res +2 -0
  71. package/src/components/AutomationSlice_Builder.res.mjs +1 -1
  72. package/src/components/OutboundTranslationSlice_Builder.res +1 -0
  73. package/src/components/OutboundTranslationSlice_Builder.res.mjs +1 -1
  74. package/tests/AppSyncEventsSigner_OpsTest.res +75 -0
  75. package/tests/AppSyncEventsSigner_OpsTest.res.mjs +44 -0
  76. package/tests/AppSyncEventsSigner_ParityFixture.mjs +36 -0
  77. package/tests/AutomationSliceEntryPoint_OpsTest.res +165 -0
  78. package/tests/AutomationSliceEntryPoint_OpsTest.res.mjs +280 -0
  79. package/tests/CounterEntryPoint_OpsTest.res +115 -0
  80. package/tests/CounterEntryPoint_OpsTest.res.mjs +150 -0
  81. package/tests/EventCollectorEntryPoint_OpsTest.res +216 -0
  82. package/tests/EventCollectorEntryPoint_OpsTest.res.mjs +339 -0
  83. package/tests/EventMapperEntryPoint_OpsTest.res +103 -0
  84. package/tests/EventMapperEntryPoint_OpsTest.res.mjs +120 -0
  85. package/tests/ExtensionPointEntryPoint_OpsTest.res +57 -0
  86. package/tests/ExtensionPointEntryPoint_OpsTest.res.mjs +54 -0
  87. package/tests/PgMigrationEntryPoint_IntegrationTest.res +22 -31
  88. package/tests/PgMigrationEntryPoint_IntegrationTest.res.mjs +20 -23
  89. package/tests/PgQueryResolverEntryPoint_OpsTest.res +38 -0
  90. package/tests/PgQueryResolverEntryPoint_OpsTest.res.mjs +31 -0
  91. package/tests/Platform_AdminScan_OpsTest.res +80 -0
  92. package/tests/Platform_AdminScan_OpsTest.res.mjs +127 -0
  93. package/tests/ReadModelEntryPoint_OpsTest.res +141 -0
  94. package/tests/ReadModelEntryPoint_OpsTest.res.mjs +267 -0
  95. package/tests/SideEffectEntryPoint_OpsTest.res +79 -0
  96. package/tests/SideEffectEntryPoint_OpsTest.res.mjs +105 -0
  97. package/tests/StateViewSliceEntryPoint_OpsTest.res +167 -0
  98. package/tests/StateViewSliceEntryPoint_OpsTest.res.mjs +296 -0
  99. package/tests/TaskBucketEntryPoint_OpsTest.res +117 -0
  100. package/tests/TaskBucketEntryPoint_OpsTest.res.mjs +165 -0
  101. package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res +42 -13
  102. package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res.mjs +29 -6
  103. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.mjs +0 -57
  104. package/src/adapter/Runtime/PgMigrationEntryPoint.mjs +0 -40
@@ -15,81 +15,6 @@ import * as AppSync_Resolver_Native$ReventlessAws from "./AppSync_Resolver_Nativ
15
15
  import * as Platform_Admin_Structure$ReventlessCore from "@reventlessdev/reventless-core/src/admin/Platform_Admin_Structure.res.mjs";
16
16
  import * as Platform_ComponentDefinitionsApi$ReventlessCore from "@reventlessdev/reventless-core/src/admin/Platform_ComponentDefinitionsApi.res.mjs";
17
17
 
18
- function makeHandlerCode(param, adminEntryJson) {
19
- return `
20
- import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
21
- import { DynamoDBDocumentClient, ScanCommand } from "@aws-sdk/lib-dynamodb";
22
-
23
- const client = DynamoDBDocumentClient.from(new DynamoDBClient({}));
24
- const TABLE = process.env.PLUGIN_RM_TABLE;
25
- const ADMIN_ENTRY = ` + adminEntryJson + `;
26
-
27
- export async function handler() {
28
- if (!TABLE) {
29
- console.error("Platform_ComponentDefinitions: PLUGIN_RM_TABLE env var not set");
30
- return [ADMIN_ENTRY];
31
- }
32
- const items = [];
33
- let exclusiveStartKey;
34
- do {
35
- const out = await client.send(new ScanCommand({
36
- TableName: TABLE,
37
- FilterExpression: "contains(#status, :connected)",
38
- ExpressionAttributeNames: { "#status": "status" },
39
- ExpressionAttributeValues: { ":connected": "Connected" },
40
- Limit: 1000,
41
- ExclusiveStartKey: exclusiveStartKey,
42
- }));
43
- if (out.Items) items.push(...out.Items);
44
- exclusiveStartKey = out.LastEvaluatedKey;
45
- } while (exclusiveStartKey);
46
-
47
- // Platform invariant: one version per plugin at a time, so the UI sees just
48
- // the bare plugin name (mirrors ReventlessCore.Plugin.name). The Connected
49
- // filter above does not enforce single-version: during a rolling deploy or a
50
- // missed retire, several versions of the same plugin sit in Connected at once
51
- // and each would otherwise emit a full duplicate set of AutoUI menu entries.
52
- // Collapse to the highest version per plugin name (mirrors
53
- // ReventlessCore.Plugin.compareVersions).
54
- const cmpVer = (a, b) => {
55
- const pa = String(a).replace(/[-+]/g, ".").split(".");
56
- const pb = String(b).replace(/[-+]/g, ".").split(".");
57
- const len = Math.max(pa.length, pb.length);
58
- for (let i = 0; i < len; i++) {
59
- const sa = pa[i] ?? "", sb = pb[i] ?? "";
60
- const na = Number(sa), nb = Number(sb);
61
- const bothNum = sa !== "" && sb !== "" && !Number.isNaN(na) && !Number.isNaN(nb);
62
- if (bothNum) { if (na !== nb) return na > nb ? 1 : -1; }
63
- else if (sa !== sb) return sa > sb ? 1 : -1;
64
- }
65
- return 0;
66
- };
67
- // The persisted structure carries Internal ReadModels / StateViewSlices for
68
- // developer tooling; they must stay out of the deployed AutoUI. Mirror
69
- // ReventlessCore.Platform_ComponentDefinitionsApi.isPublicQueryable here on the
70
- // read path (the persisted structure is pre-filter — it is NOT re-encoded).
71
- const isPublicQueryable = (q) => q?.visibility !== "Internal";
72
- const filterStructure = (s) => ({
73
- ...s,
74
- readModels: (s.readModels ?? []).filter(isPublicQueryable),
75
- stateViewSlices: (s.stateViewSlices ?? []).filter(isPublicQueryable),
76
- });
77
- const latestByName = new Map();
78
- for (const item of items) {
79
- if (!item || !item.structure) continue;
80
- const name = String(item.name).split("@")[0];
81
- const version = String(item.name).split("@")[1] ?? "";
82
- const prev = latestByName.get(name);
83
- if (!prev || cmpVer(version, prev.version) > 0) {
84
- latestByName.set(name, { version, entry: { pluginId: name, ...filterStructure(item.structure) } });
85
- }
86
- }
87
- const userEntries = [...latestByName.values()].map(v => v.entry);
88
- return [ADMIN_ENTRY, ...userEntries];
89
- }
90
- `;
91
- }
92
-
93
18
  let resolverCode = `
94
19
  import { util } from '@aws-appsync/utils';
95
20
  export function request(ctx) {
@@ -125,17 +50,16 @@ function make(api, pluginReadModelTableName, opts) {
125
50
  }, opts$1);
126
51
  });
127
52
  let adminEntryJson = JSON.stringify(Platform_ComponentDefinitionsApi$ReventlessCore.encodePluginStructureEntry(Platform_Admin_Structure$ReventlessCore.pluginId, Platform_Admin_Structure$ReventlessCore.structure));
128
- let archiveContents = {};
129
- let handlerCodeStub = makeHandlerCode("", adminEntryJson);
130
- archiveContents["index.mjs"] = new (Pulumi.asset.StringAsset)(handlerCodeStub);
131
- let loaderHash = Util_Bundle$ReventlessAws.addEsmLoaderAssets(archiveContents);
132
- let code = new (Pulumi.asset.AssetArchive)(archiveContents);
133
- let sourceCodeHash = Util_Bundle$ReventlessAws.hashString(handlerCodeStub + "\n---\n" + loaderHash);
53
+ let packageDirs = Object.fromEntries([[
54
+ "@reventlessdev/reventless-aws",
55
+ Util_Bundle$ReventlessAws.resolvePackageRoot("@reventlessdev/reventless-aws")
56
+ ]]);
57
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res.mjs", packageDirs, undefined);
134
58
  let layers = Stdlib_Option.getOr(Stdlib_Option.map(Lambda$PulumiAws.reventlessLayerArn, arn => [arn]), []);
135
59
  let lambda = new (Aws.lambda.Function)(name + "Lambda", {
136
60
  handler: "index.handler",
137
61
  runtime: "nodejs22.x",
138
- code: code,
62
+ code: match.code,
139
63
  role: lambdaRole.arn,
140
64
  memorySize: 512,
141
65
  timeout: 30,
@@ -151,6 +75,10 @@ function make(api, pluginReadModelTableName, opts) {
151
75
  "PLUGIN_RM_TABLE",
152
76
  pluginReadModelTableName
153
77
  ],
78
+ [
79
+ "ADMIN_ENTRY_JSON",
80
+ adminEntryJson
81
+ ],
154
82
  [
155
83
  "NODE_OPTIONS",
156
84
  Util_Bundle$ReventlessAws.esmLoaderNodeOptions
@@ -161,7 +89,7 @@ function make(api, pluginReadModelTableName, opts) {
161
89
  ]
162
90
  ])
163
91
  },
164
- sourceCodeHash: sourceCodeHash
92
+ sourceCodeHash: match.sourceCodeHash
165
93
  }, opts$1);
166
94
  let dataSourceRole = IAM$PulumiAws.Role.makeWithDefaultPolicy(name + "DataSource", Pulumi.output(AWS$ReventlessAws.AppSync.principal), AWS_Tags$ReventlessAws.make(name + "DataSource", "Platform", "Identity", "Platform", undefined, undefined, undefined, undefined), opts$1);
167
95
  Pulumi.all([
@@ -190,7 +118,6 @@ function make(api, pluginReadModelTableName, opts) {
190
118
  }
191
119
 
192
120
  export {
193
- makeHandlerCode,
194
121
  resolverCode,
195
122
  make,
196
123
  }
@@ -0,0 +1,86 @@
1
+ // Runtime handler for the `Platform_ComponentDefinitions` admin GraphQL resolver
2
+ // — compiled, type-checked ReScript (replaces the inline JS `makeHandlerCode`
3
+ // string in Platform_ComponentDefinitions_Lambda.res). Runtime-pure (see
4
+ // Platform_AdminScan_Ops); the deploy-time module bundles this into an
5
+ // EntryPoint archive and re-exports `handler`.
6
+ //
7
+ // Scans the Plugin read model for Connected plugins carrying a `structure`,
8
+ // collapses to the highest version per plugin name, re-applies the
9
+ // `isPublicQueryable` filter (the persisted structure is PRE-filter — it still
10
+ // carries Internal ReadModels / StateViewSlices for developer tooling), and
11
+ // prepends the built-in Platform_Admin entry. The persisted structure is
12
+ // sury-encoded with the same shape as the in-memory adapter's
13
+ // encodePluginStructureEntry, so it is wrapped with `pluginId` without decoding.
14
+
15
+ @val @scope("process") external processEnv: dict<string> = "env"
16
+
17
+ let str = (item: dict<JSON.t>, key: string): option<string> =>
18
+ item->Dict.get(key)->Option.flatMap(JSON.Decode.string)
19
+
20
+ // Mirror ReventlessCore.Platform_ComponentDefinitionsApi.isPublicQueryable on
21
+ // the read path: a component is public unless its visibility is "Internal".
22
+ let isPublicQueryable = (q: JSON.t): bool =>
23
+ switch q->JSON.Decode.object->Option.flatMap(o => o->Dict.get("visibility"))->Option.flatMap(JSON.Decode.string) {
24
+ | Some("Internal") => false
25
+ | _ => true
26
+ }
27
+
28
+ // Drop Internal ReadModels / StateViewSlices from a persisted structure. A
29
+ // missing/non-array field defaults to `[]` (mirrors the JS `?? []`).
30
+ let filterStructure = (structure: JSON.t): option<dict<JSON.t>> =>
31
+ switch structure->JSON.Decode.object {
32
+ | None => None
33
+ | Some(obj) =>
34
+ let out = Dict.fromArray(obj->Dict.toArray)
35
+ let filterArr = key =>
36
+ out
37
+ ->Dict.get(key)
38
+ ->Option.flatMap(JSON.Decode.array)
39
+ ->Option.getOr([])
40
+ ->Array.filter(isPublicQueryable)
41
+ ->JSON.Encode.array
42
+ out->Dict.set("readModels", filterArr("readModels"))
43
+ out->Dict.set("stateViewSlices", filterArr("stateViewSlices"))
44
+ Some(out)
45
+ }
46
+
47
+ // `{pluginId: name, ...filterStructure(structure)}` — pluginId first so the
48
+ // structure's fields win on the (never-expected) key collision, matching the JS
49
+ // spread. Drops a row with no decodable structure (former `!item.structure`).
50
+ let toEntry = (item: dict<JSON.t>, ~name: string): option<JSON.t> =>
51
+ switch item->Dict.get("structure")->Option.flatMap(filterStructure) {
52
+ | None => None
53
+ | Some(structureObj) =>
54
+ let entry = Dict.make()
55
+ entry->Dict.set("pluginId", JSON.Encode.string(name))
56
+ structureObj->Dict.toArray->Array.forEach(((k, v)) => entry->Dict.set(k, v))
57
+ Some(JSON.Encode.object(entry))
58
+ }
59
+
60
+ // The built-in Platform_Admin entry, injected at deploy time as the
61
+ // ADMIN_ENTRY_JSON env var (the admin never Connects to itself, so its structure
62
+ // never enters the Plugin read model).
63
+ let adminEntry: option<JSON.t> =
64
+ switch processEnv->Dict.get("ADMIN_ENTRY_JSON") {
65
+ | Some(s) if s != "" => Some(JSON.parseOrThrow(s))
66
+ | _ => None
67
+ }
68
+
69
+ let handler = async (_event: JSON.t): array<JSON.t> => {
70
+ let admin = adminEntry->Option.mapOr([], e => [e])
71
+ switch processEnv->Dict.get("PLUGIN_RM_TABLE") {
72
+ | None | Some("") =>
73
+ Console.error("Platform_ComponentDefinitions: PLUGIN_RM_TABLE env var not set")
74
+ admin
75
+ | Some(table) =>
76
+ let items = await Platform_AdminScan_Ops.scanAll(
77
+ ~tableName=table,
78
+ ~filterExpression="contains(#status, :connected)",
79
+ ~expressionAttributeNames=Dict.fromArray([("#status", "status")]),
80
+ ~expressionAttributeValues=Dict.fromArray([(":connected", JSON.Encode.string("Connected"))]),
81
+ )
82
+ let userEntries =
83
+ Platform_AdminScan_Ops.latestByName(items, ~nameVersionOf=item => item->str("name"), ~toEntry)
84
+ Array.concat(admin, userEntries)
85
+ }
86
+ }
@@ -0,0 +1,79 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
4
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
+ import * as Platform_AdminScan_Ops$ReventlessAws from "./Platform_AdminScan_Ops.res.mjs";
6
+
7
+ function str(item, key) {
8
+ return Stdlib_Option.flatMap(item[key], Stdlib_JSON.Decode.string);
9
+ }
10
+
11
+ function isPublicQueryable(q) {
12
+ let match = Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(q), o => o["visibility"]), Stdlib_JSON.Decode.string);
13
+ if (match !== undefined) {
14
+ return match !== "Internal";
15
+ } else {
16
+ return true;
17
+ }
18
+ }
19
+
20
+ function filterStructure(structure) {
21
+ let obj = Stdlib_JSON.Decode.object(structure);
22
+ if (obj === undefined) {
23
+ return;
24
+ }
25
+ let out = Object.fromEntries(Object.entries(obj));
26
+ let filterArr = key => Stdlib_Option.getOr(Stdlib_Option.flatMap(out[key], Stdlib_JSON.Decode.array), []).filter(isPublicQueryable);
27
+ out["readModels"] = filterArr("readModels");
28
+ out["stateViewSlices"] = filterArr("stateViewSlices");
29
+ return out;
30
+ }
31
+
32
+ function toEntry(item, name) {
33
+ let structureObj = Stdlib_Option.flatMap(item["structure"], filterStructure);
34
+ if (structureObj === undefined) {
35
+ return;
36
+ }
37
+ let entry = {};
38
+ entry["pluginId"] = name;
39
+ Object.entries(structureObj).forEach(param => {
40
+ entry[param[0]] = param[1];
41
+ });
42
+ return entry;
43
+ }
44
+
45
+ let s = process.env["ADMIN_ENTRY_JSON"];
46
+
47
+ let adminEntry = s !== undefined && s !== "" ? JSON.parse(s) : undefined;
48
+
49
+ async function handler(_event) {
50
+ let admin = Stdlib_Option.mapOr(adminEntry, [], e => [e]);
51
+ let table = process.env["PLUGIN_RM_TABLE"];
52
+ if (table !== undefined) {
53
+ if (table === "") {
54
+ console.error("Platform_ComponentDefinitions: PLUGIN_RM_TABLE env var not set");
55
+ return admin;
56
+ }
57
+ let items = await Platform_AdminScan_Ops$ReventlessAws.scanAll(table, "contains(#status, :connected)", Object.fromEntries([[
58
+ "#status",
59
+ "status"
60
+ ]]), Object.fromEntries([[
61
+ ":connected",
62
+ "Connected"
63
+ ]]));
64
+ let userEntries = Platform_AdminScan_Ops$ReventlessAws.latestByName(items, item => str(item, "name"), toEntry);
65
+ return admin.concat(userEntries);
66
+ }
67
+ console.error("Platform_ComponentDefinitions: PLUGIN_RM_TABLE env var not set");
68
+ return admin;
69
+ }
70
+
71
+ export {
72
+ str,
73
+ isPublicQueryable,
74
+ filterStructure,
75
+ toEntry,
76
+ adminEntry,
77
+ handler,
78
+ }
79
+ /* s Not a pure module */
@@ -8,74 +8,6 @@
8
8
 
9
9
  open PulumiAws
10
10
 
11
- let makeHandlerCode = (~tableName as _: string): string =>
12
- `
13
- import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
14
- import { DynamoDBDocumentClient, ScanCommand } from "@aws-sdk/lib-dynamodb";
15
-
16
- const client = DynamoDBDocumentClient.from(new DynamoDBClient({}));
17
- const TABLE = process.env.UI_FRAGMENT_RM_TABLE;
18
-
19
- export async function handler() {
20
- if (!TABLE) {
21
- console.error("Platform_UIFragments: UI_FRAGMENT_RM_TABLE env var not set");
22
- return [];
23
- }
24
- const items = [];
25
- let exclusiveStartKey;
26
- do {
27
- const out = await client.send(new ScanCommand({
28
- TableName: TABLE,
29
- Limit: 1000,
30
- ExclusiveStartKey: exclusiveStartKey,
31
- }));
32
- if (out.Items) items.push(...out.Items);
33
- exclusiveStartKey = out.LastEvaluatedKey;
34
- } while (exclusiveStartKey);
35
-
36
- // Platform invariant: one version per plugin at a time, so the UI sees just
37
- // the bare plugin name (mirrors ReventlessCore.Plugin.name). The registry is
38
- // keyed by bare plugin name (a no-op for the split below), but rows persisted
39
- // by the pre-slice registry were keyed name@version — keep the collapse to the
40
- // highest version per plugin name (mirrors ReventlessCore.Plugin.compareVersions)
41
- // so a mixed table never surfaces duplicate fragments.
42
- const cmpVer = (a, b) => {
43
- const pa = String(a).replace(/[-+]/g, ".").split(".");
44
- const pb = String(b).replace(/[-+]/g, ".").split(".");
45
- const len = Math.max(pa.length, pb.length);
46
- for (let i = 0; i < len; i++) {
47
- const sa = pa[i] ?? "", sb = pb[i] ?? "";
48
- const na = Number(sa), nb = Number(sb);
49
- const bothNum = sa !== "" && sb !== "" && !Number.isNaN(na) && !Number.isNaN(nb);
50
- if (bothNum) { if (na !== nb) return na > nb ? 1 : -1; }
51
- else if (sa !== sb) return sa > sb ? 1 : -1;
52
- }
53
- return 0;
54
- };
55
- const latestByName = new Map();
56
- for (const item of items) {
57
- if (!item || !item.pluginId || !item.remoteEntryUrl) continue;
58
- const name = String(item.pluginId).split("@")[0];
59
- const version = String(item.pluginId).split("@")[1] ?? "";
60
- const prev = latestByName.get(name);
61
- if (!prev || cmpVer(version, prev.version) > 0) {
62
- latestByName.set(name, {
63
- version,
64
- entry: {
65
- pluginId: name,
66
- remoteEntryUrl: item.remoteEntryUrl,
67
- panels: item.panels || [],
68
- pages: item.pages || [],
69
- registeredAt: item.registeredAt || "",
70
- updatedAt: item.updatedAt || "",
71
- },
72
- });
73
- }
74
- }
75
- return [...latestByName.values()].map(v => v.entry);
76
- }
77
- `
78
-
79
11
  let resolverCode = `
80
12
  import { util } from '@aws-appsync/utils';
81
13
  export function request(ctx) {
@@ -143,18 +75,16 @@ let make = (
143
75
  )
144
76
  })
145
77
 
146
- let archiveContents: dict<Pulumi.Archive.assetOrArchive> = Dict.make()
147
- let handlerCodeStub = makeHandlerCode(~tableName="")
148
- archiveContents->Dict.set(
149
- "index.mjs",
150
- Pulumi.Asset.stringAsset(handlerCodeStub)->Pulumi.Archive.assetToAssetOrArchive,
78
+ // Bundle reventless-aws (the compiled `_Ops` handler lives inside it) and
79
+ // re-export its `handler`; buildCodeArchive also ships the ESM resolve-hook so
80
+ // the handler's bare @aws-sdk/* specifiers resolve from the managed runtime.
81
+ let packageDirs = Dict.fromArray([
82
+ ("@reventlessdev/reventless-aws", Util_Bundle.resolvePackageRoot("@reventlessdev/reventless-aws")),
83
+ ])
84
+ let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
85
+ ~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res.mjs",
86
+ ~packageDirs,
151
87
  )
152
- // ESM self-containment: the handler imports @aws-sdk/* bare specifiers the
153
- // nodejs22.x runtime provides only under /var/runtime — unreachable from
154
- // /var/task ESM without the resolver hook. Ship the loader + set its env vars.
155
- let loaderHash = Util_Bundle.addEsmLoaderAssets(archiveContents)
156
- let code = Pulumi.Archive.assetArchive(archiveContents)
157
- let sourceCodeHash = Util_Bundle.hashString(handlerCodeStub ++ "\n---\n" ++ loaderHash)
158
88
 
159
89
  let layers =
160
90
  Lambda.reventlessLayerArn
@@ -13,75 +13,6 @@ import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
13
13
  import * as Util_Pulumi$ReventlessCore from "@reventlessdev/reventless-core/src/util/Util_Pulumi.res.mjs";
14
14
  import * as AppSync_Resolver_Native$ReventlessAws from "./AppSync_Resolver_Native.res.mjs";
15
15
 
16
- function makeHandlerCode(param) {
17
- return `
18
- import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
19
- import { DynamoDBDocumentClient, ScanCommand } from "@aws-sdk/lib-dynamodb";
20
-
21
- const client = DynamoDBDocumentClient.from(new DynamoDBClient({}));
22
- const TABLE = process.env.UI_FRAGMENT_RM_TABLE;
23
-
24
- export async function handler() {
25
- if (!TABLE) {
26
- console.error("Platform_UIFragments: UI_FRAGMENT_RM_TABLE env var not set");
27
- return [];
28
- }
29
- const items = [];
30
- let exclusiveStartKey;
31
- do {
32
- const out = await client.send(new ScanCommand({
33
- TableName: TABLE,
34
- Limit: 1000,
35
- ExclusiveStartKey: exclusiveStartKey,
36
- }));
37
- if (out.Items) items.push(...out.Items);
38
- exclusiveStartKey = out.LastEvaluatedKey;
39
- } while (exclusiveStartKey);
40
-
41
- // Platform invariant: one version per plugin at a time, so the UI sees just
42
- // the bare plugin name (mirrors ReventlessCore.Plugin.name). The registry is
43
- // keyed by bare plugin name (a no-op for the split below), but rows persisted
44
- // by the pre-slice registry were keyed name@version — keep the collapse to the
45
- // highest version per plugin name (mirrors ReventlessCore.Plugin.compareVersions)
46
- // so a mixed table never surfaces duplicate fragments.
47
- const cmpVer = (a, b) => {
48
- const pa = String(a).replace(/[-+]/g, ".").split(".");
49
- const pb = String(b).replace(/[-+]/g, ".").split(".");
50
- const len = Math.max(pa.length, pb.length);
51
- for (let i = 0; i < len; i++) {
52
- const sa = pa[i] ?? "", sb = pb[i] ?? "";
53
- const na = Number(sa), nb = Number(sb);
54
- const bothNum = sa !== "" && sb !== "" && !Number.isNaN(na) && !Number.isNaN(nb);
55
- if (bothNum) { if (na !== nb) return na > nb ? 1 : -1; }
56
- else if (sa !== sb) return sa > sb ? 1 : -1;
57
- }
58
- return 0;
59
- };
60
- const latestByName = new Map();
61
- for (const item of items) {
62
- if (!item || !item.pluginId || !item.remoteEntryUrl) continue;
63
- const name = String(item.pluginId).split("@")[0];
64
- const version = String(item.pluginId).split("@")[1] ?? "";
65
- const prev = latestByName.get(name);
66
- if (!prev || cmpVer(version, prev.version) > 0) {
67
- latestByName.set(name, {
68
- version,
69
- entry: {
70
- pluginId: name,
71
- remoteEntryUrl: item.remoteEntryUrl,
72
- panels: item.panels || [],
73
- pages: item.pages || [],
74
- registeredAt: item.registeredAt || "",
75
- updatedAt: item.updatedAt || "",
76
- },
77
- });
78
- }
79
- }
80
- return [...latestByName.values()].map(v => v.entry);
81
- }
82
- `;
83
- }
84
-
85
16
  let resolverCode = `
86
17
  import { util } from '@aws-appsync/utils';
87
18
  export function request(ctx) {
@@ -116,82 +47,16 @@ function make(api, uiFragmentRegistryTableName, schemaReady, opts) {
116
47
  role: lambdaRole.id
117
48
  }, opts$1);
118
49
  });
119
- let archiveContents = {};
120
- let handlerCodeStub = `
121
- import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
122
- import { DynamoDBDocumentClient, ScanCommand } from "@aws-sdk/lib-dynamodb";
123
-
124
- const client = DynamoDBDocumentClient.from(new DynamoDBClient({}));
125
- const TABLE = process.env.UI_FRAGMENT_RM_TABLE;
126
-
127
- export async function handler() {
128
- if (!TABLE) {
129
- console.error("Platform_UIFragments: UI_FRAGMENT_RM_TABLE env var not set");
130
- return [];
131
- }
132
- const items = [];
133
- let exclusiveStartKey;
134
- do {
135
- const out = await client.send(new ScanCommand({
136
- TableName: TABLE,
137
- Limit: 1000,
138
- ExclusiveStartKey: exclusiveStartKey,
139
- }));
140
- if (out.Items) items.push(...out.Items);
141
- exclusiveStartKey = out.LastEvaluatedKey;
142
- } while (exclusiveStartKey);
143
-
144
- // Platform invariant: one version per plugin at a time, so the UI sees just
145
- // the bare plugin name (mirrors ReventlessCore.Plugin.name). The registry is
146
- // keyed by bare plugin name (a no-op for the split below), but rows persisted
147
- // by the pre-slice registry were keyed name@version — keep the collapse to the
148
- // highest version per plugin name (mirrors ReventlessCore.Plugin.compareVersions)
149
- // so a mixed table never surfaces duplicate fragments.
150
- const cmpVer = (a, b) => {
151
- const pa = String(a).replace(/[-+]/g, ".").split(".");
152
- const pb = String(b).replace(/[-+]/g, ".").split(".");
153
- const len = Math.max(pa.length, pb.length);
154
- for (let i = 0; i < len; i++) {
155
- const sa = pa[i] ?? "", sb = pb[i] ?? "";
156
- const na = Number(sa), nb = Number(sb);
157
- const bothNum = sa !== "" && sb !== "" && !Number.isNaN(na) && !Number.isNaN(nb);
158
- if (bothNum) { if (na !== nb) return na > nb ? 1 : -1; }
159
- else if (sa !== sb) return sa > sb ? 1 : -1;
160
- }
161
- return 0;
162
- };
163
- const latestByName = new Map();
164
- for (const item of items) {
165
- if (!item || !item.pluginId || !item.remoteEntryUrl) continue;
166
- const name = String(item.pluginId).split("@")[0];
167
- const version = String(item.pluginId).split("@")[1] ?? "";
168
- const prev = latestByName.get(name);
169
- if (!prev || cmpVer(version, prev.version) > 0) {
170
- latestByName.set(name, {
171
- version,
172
- entry: {
173
- pluginId: name,
174
- remoteEntryUrl: item.remoteEntryUrl,
175
- panels: item.panels || [],
176
- pages: item.pages || [],
177
- registeredAt: item.registeredAt || "",
178
- updatedAt: item.updatedAt || "",
179
- },
180
- });
181
- }
182
- }
183
- return [...latestByName.values()].map(v => v.entry);
184
- }
185
- `;
186
- archiveContents["index.mjs"] = new (Pulumi.asset.StringAsset)(handlerCodeStub);
187
- let loaderHash = Util_Bundle$ReventlessAws.addEsmLoaderAssets(archiveContents);
188
- let code = new (Pulumi.asset.AssetArchive)(archiveContents);
189
- let sourceCodeHash = Util_Bundle$ReventlessAws.hashString(handlerCodeStub + "\n---\n" + loaderHash);
50
+ let packageDirs = Object.fromEntries([[
51
+ "@reventlessdev/reventless-aws",
52
+ Util_Bundle$ReventlessAws.resolvePackageRoot("@reventlessdev/reventless-aws")
53
+ ]]);
54
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res.mjs", packageDirs, undefined);
190
55
  let layers = Stdlib_Option.getOr(Stdlib_Option.map(Lambda$PulumiAws.reventlessLayerArn, arn => [arn]), []);
191
56
  let lambda = new (Aws.lambda.Function)(name + "Lambda", {
192
57
  handler: "index.handler",
193
58
  runtime: "nodejs22.x",
194
- code: code,
59
+ code: match.code,
195
60
  role: lambdaRole.arn,
196
61
  memorySize: 512,
197
62
  timeout: 30,
@@ -217,7 +82,7 @@ export async function handler() {
217
82
  ]
218
83
  ])
219
84
  },
220
- sourceCodeHash: sourceCodeHash
85
+ sourceCodeHash: match.sourceCodeHash
221
86
  }, opts$1);
222
87
  let dataSourceRole = IAM$PulumiAws.Role.makeWithDefaultPolicy(name + "DataSource", Pulumi.output(AWS$ReventlessAws.AppSync.principal), AWS_Tags$ReventlessAws.make(name + "DataSource", "Platform", "Identity", "Platform", undefined, undefined, undefined, undefined), opts$1);
223
88
  Pulumi.all([
@@ -246,7 +111,6 @@ export async function handler() {
246
111
  }
247
112
 
248
113
  export {
249
- makeHandlerCode,
250
114
  resolverCode,
251
115
  make,
252
116
  }
@@ -0,0 +1,48 @@
1
+ // Runtime handler for the `Platform_UIFragments` admin GraphQL resolver —
2
+ // compiled, type-checked ReScript (replaces the inline JS `makeHandlerCode`
3
+ // string in Platform_UIFragments_Lambda.res). Runtime-pure (see
4
+ // Platform_AdminScan_Ops); the deploy-time module bundles this into an
5
+ // EntryPoint archive and re-exports `handler`.
6
+ //
7
+ // Scans the UiFragments StateViewSlice table (one item per plugin with a
8
+ // registered UI-fragment manifest), collapses to the highest version per plugin
9
+ // name (platform invariant: one version per plugin), and returns the entries.
10
+ // The persisted state is sury-encoded with the same shape the in-memory
11
+ // adapter's Platform_UIFragmentsApi.encodeUIFragmentEntry produces, so rows are
12
+ // returned as-is (bar the name/version collapse).
13
+
14
+ @val @scope("process") external processEnv: dict<string> = "env"
15
+
16
+ let str = (item: dict<JSON.t>, key: string): option<string> =>
17
+ item->Dict.get(key)->Option.flatMap(JSON.Decode.string)
18
+
19
+ // Build one entry, dropping a row missing pluginId or remoteEntryUrl (mirrors
20
+ // the former `!item.pluginId || !item.remoteEntryUrl` guard). `name` is the bare
21
+ // plugin name; panels/pages/timestamps default like the JS `|| []` / `|| ""`.
22
+ let toEntry = (item: dict<JSON.t>, ~name: string): option<JSON.t> =>
23
+ switch (item->str("pluginId"), item->str("remoteEntryUrl")) {
24
+ | (Some(_), Some(remoteEntryUrl)) if remoteEntryUrl != "" =>
25
+ Some(
26
+ JSON.Encode.object(
27
+ Dict.fromArray([
28
+ ("pluginId", JSON.Encode.string(name)),
29
+ ("remoteEntryUrl", JSON.Encode.string(remoteEntryUrl)),
30
+ ("panels", item->Dict.get("panels")->Option.getOr(JSON.Encode.array([]))),
31
+ ("pages", item->Dict.get("pages")->Option.getOr(JSON.Encode.array([]))),
32
+ ("registeredAt", item->Dict.get("registeredAt")->Option.getOr(JSON.Encode.string(""))),
33
+ ("updatedAt", item->Dict.get("updatedAt")->Option.getOr(JSON.Encode.string(""))),
34
+ ]),
35
+ ),
36
+ )
37
+ | _ => None
38
+ }
39
+
40
+ let handler = async (_event: JSON.t): array<JSON.t> =>
41
+ switch processEnv->Dict.get("UI_FRAGMENT_RM_TABLE") {
42
+ | None | Some("") =>
43
+ Console.error("Platform_UIFragments: UI_FRAGMENT_RM_TABLE env var not set")
44
+ []
45
+ | Some(table) =>
46
+ let items = await Platform_AdminScan_Ops.scanAll(~tableName=table)
47
+ Platform_AdminScan_Ops.latestByName(items, ~nameVersionOf=item => item->str("pluginId"), ~toEntry)
48
+ }