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

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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,20 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # 3.0.0-alpha.312 (2026-08-19)
7
+
8
+ ### Bug Fixes
9
+
10
+ * **aws:** build the platform's definition asset from the shared value ([c0e432d](https://github.com/ReventlessDev/reventless-core/commit/c0e432d98d96af3ff4c305b62f658ae102c5d142))
11
+
12
+
13
+ # 3.0.0-alpha.311 (2026-08-19)
14
+
15
+ ### Bug Fixes
16
+
17
+ * **aws:** decode the plugin definition asset instead of casting it ([5ac8a75](https://github.com/ReventlessDev/reventless-core/commit/5ac8a758673da63479049ba17658c8f91c5d72c5))
18
+
19
+
6
20
  # 3.0.0-alpha.310 (2026-08-18)
7
21
 
8
22
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-aws",
3
- "version": "3.0.0-alpha.310",
3
+ "version": "3.0.0-alpha.312",
4
4
  "description": "AWS adapters for Reventless",
5
5
  "license": "Apache-2.0",
6
6
  "dependencies": {
@@ -13,17 +13,17 @@
13
13
  "sury": "11.0.0-rc.1",
14
14
  "uuid": "^13.0.0",
15
15
  "@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.12",
16
- "@reventlessdev/rescript-jest": "1.0.0-alpha.10",
17
16
  "@reventlessdev/rescript-effect": "0.1.0-alpha.32",
17
+ "@reventlessdev/rescript-jest": "1.0.0-alpha.10",
18
18
  "@reventlessdev/rescript-node": "2.0.0-alpha.8",
19
19
  "@reventlessdev/rescript-pulumi-aws": "3.0.0-alpha.1",
20
20
  "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.19",
21
21
  "@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
22
- "@reventlessdev/reventless-core": "3.0.0-alpha.240",
22
+ "@reventlessdev/reventless-core": "3.0.0-alpha.241",
23
23
  "@reventlessdev/reventless-infra": "3.0.0-alpha.146",
24
- "@reventlessdev/reventless-postgres": "3.0.0-alpha.104",
25
- "@reventlessdev/reventless-interop": "3.0.0-alpha.32",
26
- "@reventlessdev/reventless-spec": "3.0.0-alpha.118"
24
+ "@reventlessdev/reventless-postgres": "3.0.0-alpha.105",
25
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.118",
26
+ "@reventlessdev/reventless-interop": "3.0.0-alpha.32"
27
27
  },
28
28
  "devDependencies": {
29
29
  "rescript": "12.3.0",
@@ -12,8 +12,8 @@
12
12
  // Plugin_Callback wiring, and the Lambda dispatch boundary.
13
13
  //
14
14
  // Runtime-pure: no `open PulumiAws` values — Pulumi appears in type positions
15
- // only (erased). The two `%identity` casts below sit on documented JSON
16
- // contracts (same pattern as AggregateEntryPoint_Ops.asConnectionConfig).
15
+ // only (erased). The one `%identity` cast below sits on a documented JSON
16
+ // contract (same pattern as AggregateEntryPoint_Ops.asConnectionConfig).
17
17
 
18
18
  // ── Shim bindings (HandlerFactoryHelpers.mjs) ───────────────────────────────
19
19
  // The structured-log + Effect dispatch boundary shared by every deployed entry
@@ -325,8 +325,8 @@ let projectPluginRow = (row: JSON.t): option<pluginProjection> =>
325
325
 
326
326
  // A pluginDefinition viewed as a projection (the manage logic reads only the
327
327
  // shared subset; `status` is never read for the acting plugin). Array/option
328
- // fields are js_nullable-normalized the definition may come from a plain
329
- // JSON.parse of pluginDefinition.json.
328
+ // fields stay js_nullable-normalized: a definition arriving over the wire, rather
329
+ // than from loadPluginDefinition's decode, still carries null for an absent field.
330
330
  let projectionOfDefinition = (d: Reventless.Plugin.pluginDefinition): pluginProjection => {
331
331
  id: d.id,
332
332
  status: "",
@@ -671,20 +671,24 @@ let warnSkippedExtension = (ext: extensionConfig) =>
671
671
 
672
672
  let assetPath = (fileName: string): string => NodePath.join([NodeProcess.cwd(), fileName])
673
673
 
674
- // pluginDefinition.json is written at deploy time as a stable JSON literal
675
- // matching Reventless.Plugin.pluginDefinitionSchema (PluginRuntime_Builder), so
676
- // the parsed object IS the record shape cast at the documented contract (the
677
- // AggregateEntryPoint_Ops.asConnectionConfig pattern). js_nullable option
678
- // fields may hold null; consumers normalize via jsOption.
679
- external asPluginDefinition: JSON.t => Reventless.Plugin.pluginDefinition = "%identity"
680
-
674
+ // pluginDefinition.json is written through the schema's encoder (Plugin_Helpers),
675
+ // so it holds the wire form, which is not the record's runtime shape: an @offload
676
+ // field is {"$offload": …} on the wire and Offloaded(…) in ReScript. Decode it
677
+ // back through the same schema — a cast leaves the wire form in a value typed as
678
+ // the record, and the next encode of it (the Connect handshake) throws.
681
679
  let loadPluginDefinition = (): Reventless.Plugin.pluginDefinition =>
682
680
  try {
683
- assetPath("pluginDefinition.json")->NodeFs.readFileSync->JSON.parseOrThrow->asPluginDefinition
681
+ assetPath("pluginDefinition.json")
682
+ ->NodeFs.readFileSync
683
+ ->Reventless.Util_Sury.fromJsonString(Reventless.Plugin.pluginDefinitionSchema)
684
684
  } catch {
685
685
  | exn =>
686
+ // Util_Sury.exnMessage, not the local one: sury raises S.Exn, which is not a
687
+ // JsExn, so the local reader yields "unknown error" and loses the field path —
688
+ // the whole diagnosis for a decode failure here.
686
689
  JsError.throwWithMessage(
687
- "Failed to load pluginDefinition.json from asset zip: " ++ exnMessage(exn),
690
+ "Failed to load pluginDefinition.json from asset zip: " ++
691
+ Reventless.Util_Sury.exnMessage(exn),
688
692
  )
689
693
  }
690
694
 
@@ -8,8 +8,10 @@ import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
8
8
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
9
9
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
10
10
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
11
+ import * as Plugin$Reventless from "@reventlessdev/reventless-spec/src/components/Plugin.res.mjs";
11
12
  import * as SNS_Helpers$AwsSdk from "@reventlessdev/rescript-aws-sdk/src/SNS_Helpers.res.mjs";
12
13
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
14
+ import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
13
15
  import * as HandlerFactoryHelpersMjs from "./HandlerFactoryHelpers.mjs";
14
16
  import * as Plugin_Callback$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/component/Plugin_Callback.res.mjs";
15
17
  import * as Util_PluginMessage_Runtime$ReventlessAws from "../../plugin/runtime/Util_PluginMessage_Runtime.res.mjs";
@@ -519,10 +521,10 @@ function assetPath(fileName) {
519
521
 
520
522
  function loadPluginDefinition() {
521
523
  try {
522
- return JSON.parse(Nodefs.readFileSync(assetPath("pluginDefinition.json"), "utf8"));
524
+ return Util_Sury$Reventless.fromJsonString(Nodefs.readFileSync(assetPath("pluginDefinition.json"), "utf8"), Plugin$Reventless.pluginDefinitionSchema);
523
525
  } catch (raw_exn) {
524
526
  let exn = Primitive_exceptions.internalToException(raw_exn);
525
- return Stdlib_JsError.throwWithMessage("Failed to load pluginDefinition.json from asset zip: " + exnMessage(exn));
527
+ return Stdlib_JsError.throwWithMessage("Failed to load pluginDefinition.json from asset zip: " + Util_Sury$Reventless.exnMessage(exn, undefined));
526
528
  }
527
529
  }
528
530
 
@@ -268,12 +268,13 @@ module Make = (
268
268
  // keeps the JSON shape consistent so the entry point can parse uniformly.
269
269
  let synthesizeAdminContext = (): ReventlessCore.Plugin_Helpers.eventCollectorContext => {
270
270
  let config = configRef.contents
271
- // Stable JSON literal matches Reventless.Plugin.pluginDefinitionSchema
272
- // (optional fields encoded as null via js_nullable). Must stay byte-aligned with
273
- // the ReScript-built twin in `Platform_Admin.construct`'s `fakePluginDefinition`.
274
- let platformId = ReventlessCore.Platform_Admin_Structure.pluginId
271
+ // Encoded from the one placeholder value, through the same schema the entry
272
+ // point decodes with. It was a hand-written JSON literal, which drifted: the
273
+ // schema gained `dcbEventLog` and `kind` and the literal did not follow.
275
274
  let fakePluginDefinitionJson =
276
- `{"id":"${platformId}@INTERNAL","name":"${platformId}","version":"INTERNAL","extensionPoints":[],"extensions":[],"eventCollector":"NOT-SET","extensionProtocols":[],"apiSchemaFragment":null,"apiTarget":null,"structure":null}`->Pulumi.Output.make
275
+ ReventlessCore.Platform_Admin_Structure.internalPluginDefinition
276
+ ->Reventless.Util_Sury.toJsonString(Reventless.Plugin.pluginDefinitionSchema)
277
+ ->Pulumi.Output.make
277
278
  let adminEpEventTopicArn = switch config.eventTopicArn {
278
279
  | Some(arn) => arn
279
280
  | None => Pulumi.Output.make("NOT_AVAILABLE")
@@ -5,6 +5,8 @@ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
5
5
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
6
  import * as Pulumi from "@pulumi/pulumi";
7
7
  import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
8
+ import * as Plugin$Reventless from "@reventlessdev/reventless-spec/src/components/Plugin.res.mjs";
9
+ import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
8
10
  import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
9
11
  import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/components/Component.res.mjs";
10
12
  import * as DcbBackend$ReventlessAws from "../../adapter/DcbEventLog/DcbBackend.res.mjs";
@@ -202,7 +204,7 @@ function Make(EventCollectorChannel) {
202
204
  };
203
205
  let synthesizeAdminContext = () => {
204
206
  let config = configRef.contents;
205
- let fakePluginDefinitionJson = Pulumi.output(`{"id":"` + Platform_Admin_Structure$ReventlessCore.pluginId + `@INTERNAL","name":"` + Platform_Admin_Structure$ReventlessCore.pluginId + `","version":"INTERNAL","extensionPoints":[],"extensions":[],"eventCollector":"NOT-SET","extensionProtocols":[],"apiSchemaFragment":null,"apiTarget":null,"structure":null}`);
207
+ let fakePluginDefinitionJson = Pulumi.output(Util_Sury$Reventless.toJsonString(Platform_Admin_Structure$ReventlessCore.internalPluginDefinition, Plugin$Reventless.pluginDefinitionSchema, undefined));
206
208
  let arn = config.eventTopicArn;
207
209
  let adminEpEventTopicArn = arn !== undefined ? arn : Pulumi.output("NOT_AVAILABLE");
208
210
  return {
@@ -0,0 +1,115 @@
1
+ // Guards the write/read symmetry of the `pluginDefinition.json` asset.
2
+ //
3
+ // The deploy side writes it with the schema's encoder (Plugin_Helpers); the
4
+ // runtime side (EventCollectorEntryPoint_Ops.loadPluginDefinition) reads it back
5
+ // and republishes the value inside the Connect handshake. Those two have to be
6
+ // the same schema in opposite directions.
7
+ //
8
+ // They stopped being that once a field carried @offload, whose wire form is
9
+ // deliberately untagged: {"$offload": …} on the wire, Offloaded(…) in ReScript.
10
+ // The read side cast the parsed JSON instead of decoding it, so the wire form
11
+ // survived inside a value typed as the record and the *next* encode — publishing
12
+ // ConnectPlugin — threw. Registration then froze at the previous version with the
13
+ // deploy reporting success, so this asserts the round trip rather than the read.
14
+
15
+ open JestGlobals
16
+
17
+ let offloadedFragment: Reventless.Plugin.pluginDefinition = {
18
+ id: "Catalog@1.0.0-alpha.215",
19
+ name: "Catalog",
20
+ version: "1.0.0-alpha.215",
21
+ extensionPoints: [],
22
+ extensions: [],
23
+ eventCollector: "arn:aws:sqs:eu-west-1:1:CatalogPluginEventColl",
24
+ extensionProtocols: [],
25
+ apiSchemaFragment: Some(
26
+ Offloaded({
27
+ store: "pluginApiFragments",
28
+ key: "sha256/a3db053a9ca51d63982811b0c1faa42cbb83ef6011ea140841375c7b1c9e9695",
29
+ hash: "a3db053a9ca51d63982811b0c1faa42cbb83ef6011ea140841375c7b1c9e9695",
30
+ bytes: 14594,
31
+ }),
32
+ ),
33
+ apiTarget: Some("Domain"),
34
+ structure: None,
35
+ dcbEventLog: None,
36
+ kind: Domain,
37
+ }
38
+
39
+ let inlineFragment: Reventless.Plugin.pluginDefinition = {
40
+ ...offloadedFragment,
41
+ apiSchemaFragment: Some(Inline({encoded: "{\"types\":[]}", protocol: "1"})),
42
+ }
43
+
44
+ // The asset as it lands in the code archive — the deploy side, verbatim.
45
+ let writeAsset = (def: Reventless.Plugin.pluginDefinition): string =>
46
+ def->Reventless.Util_Sury.toJsonString(Reventless.Plugin.pluginDefinitionSchema)
47
+
48
+ // The runtime side, through the real entry point: loadPluginDefinition resolves
49
+ // the asset against process.cwd(), so the file has to be laid down where it looks
50
+ // rather than the decode re-implemented here — re-implementing it is what would
51
+ // let a cast come back green.
52
+ let readAsset = (json: string): Reventless.Plugin.pluginDefinition => {
53
+ let dir = NodeFs.mkdtempSync(NodePath.join([NodeOs.tmpdir(), "plugindef-"]))
54
+ let previousCwd = NodeProcess.cwd()
55
+ NodeFs.writeFileSync(NodePath.join([dir, "pluginDefinition.json"]), json)
56
+ NodeProcess.chdir(dir)
57
+ let restore = () => {
58
+ NodeProcess.chdir(previousCwd)
59
+ NodeFs.rmSync(dir, {recursive: true, force: true})
60
+ }
61
+ switch EventCollectorEntryPoint_Ops.loadPluginDefinition() {
62
+ | def =>
63
+ restore()
64
+ def
65
+ | exception exn =>
66
+ restore()
67
+ throw(exn)
68
+ }
69
+ }
70
+
71
+ describe("pluginDefinition.json write/read symmetry", () => {
72
+ testSync("an offloaded field reaches the runtime as Offloaded, not as its wire form", () => {
73
+ let def = offloadedFragment->writeAsset->readAsset
74
+ switch def.apiSchemaFragment {
75
+ | Some(Offloaded({store, bytes})) =>
76
+ expect(store)->toBe("pluginApiFragments")
77
+ expect(bytes)->toBe(14594)
78
+ | Some(Inline(_)) => fail("offloaded fragment decoded as Inline")
79
+ | None => fail("offloaded fragment decoded as None")
80
+ }
81
+ })
82
+
83
+ testSync("a definition read back from the asset can be published to the Connect handshake", () => {
84
+ // The encode the old cast broke. Both arms, because only Offloaded diverges
85
+ // between wire and runtime shape and an Inline-only test would stay green.
86
+ [offloadedFragment, inlineFragment]->Array.forEach(def => {
87
+ let published =
88
+ ReventlessInfra.PluginExtensionPointSpec.ConnectPlugin(def->writeAsset->readAsset)
89
+ ->Reventless.Util_Sury.toJson(ReventlessInfra.PluginExtensionPointSpec.commandSchema)
90
+ expect(published->JSON.Decode.object->Option.isSome)->toBe(true)
91
+ })
92
+ })
93
+
94
+ testSync("the asset round-trips byte-identically, so no stored message shifts", () => {
95
+ let once = offloadedFragment->writeAsset
96
+ expect(once->readAsset->writeAsset)->toBe(once)
97
+ })
98
+
99
+ testSync("an absent optional reaches the runtime as None, not as null", () => {
100
+ let def = offloadedFragment->writeAsset->readAsset
101
+ expect(def.structure->Option.isNone)->toBe(true)
102
+ expect(def.dcbEventLog->Option.isNone)->toBe(true)
103
+ })
104
+
105
+ // The platform's own EventCollector ships a placeholder rather than a real
106
+ // definition — the admin IS the Plugin extension point — but it is read back on
107
+ // this same path, so it has to satisfy the same schema. Its asset used to be a
108
+ // hand-written JSON literal in PluginRuntime_Builder and fell two fields behind
109
+ // (dcbEventLog, kind) with nothing to notice.
110
+ testSync("the platform's own placeholder asset survives the same round trip", () => {
111
+ let def = ReventlessCore.Platform_Admin_Structure.internalPluginDefinition->writeAsset->readAsset
112
+ expect(def.id)->toBe(ReventlessCore.Platform_Admin_Structure.pluginId ++ "@INTERNAL")
113
+ expect(def.kind)->toBe(Reventless.Plugin.Domain)
114
+ })
115
+ })
@@ -0,0 +1,135 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Nodefs from "node:fs";
4
+ import * as Nodeos from "node:os";
5
+ import * as Nodepath from "node:path";
6
+ import * as JestGlobals from "@reventlessdev/rescript-jest/src/JestGlobals.res.mjs";
7
+ import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
8
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
9
+ import * as Plugin$Reventless from "@reventlessdev/reventless-spec/src/components/Plugin.res.mjs";
10
+ import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
11
+ import * as Platform_Admin_Structure$ReventlessCore from "@reventlessdev/reventless-core/src/admin/Platform_Admin_Structure.res.mjs";
12
+ import * as PluginExtensionPointSpec$ReventlessInfra from "@reventlessdev/reventless-infra/src/types/PluginExtensionPointSpec.res.mjs";
13
+ import * as EventCollectorEntryPoint_Ops$ReventlessAws from "../src/adapter/Runtime/EventCollectorEntryPoint_Ops.res.mjs";
14
+
15
+ let offloadedFragment = {
16
+ id: "Catalog@1.0.0-alpha.215",
17
+ name: "Catalog",
18
+ version: "1.0.0-alpha.215",
19
+ extensionPoints: [],
20
+ extensions: [],
21
+ eventCollector: "arn:aws:sqs:eu-west-1:1:CatalogPluginEventColl",
22
+ extensionProtocols: [],
23
+ apiSchemaFragment: {
24
+ TAG: "Offloaded",
25
+ _0: {
26
+ store: "pluginApiFragments",
27
+ key: "sha256/a3db053a9ca51d63982811b0c1faa42cbb83ef6011ea140841375c7b1c9e9695",
28
+ hash: "a3db053a9ca51d63982811b0c1faa42cbb83ef6011ea140841375c7b1c9e9695",
29
+ bytes: 14594
30
+ }
31
+ },
32
+ apiTarget: "Domain",
33
+ structure: undefined,
34
+ dcbEventLog: undefined,
35
+ kind: "Domain"
36
+ };
37
+
38
+ let inlineFragment = {
39
+ id: offloadedFragment.id,
40
+ name: offloadedFragment.name,
41
+ version: offloadedFragment.version,
42
+ extensionPoints: offloadedFragment.extensionPoints,
43
+ extensions: offloadedFragment.extensions,
44
+ eventCollector: offloadedFragment.eventCollector,
45
+ extensionProtocols: offloadedFragment.extensionProtocols,
46
+ apiSchemaFragment: {
47
+ TAG: "Inline",
48
+ _0: {
49
+ encoded: "{\"types\":[]}",
50
+ protocol: "1"
51
+ }
52
+ },
53
+ apiTarget: offloadedFragment.apiTarget,
54
+ structure: offloadedFragment.structure,
55
+ dcbEventLog: offloadedFragment.dcbEventLog,
56
+ kind: offloadedFragment.kind
57
+ };
58
+
59
+ function writeAsset(def) {
60
+ return Util_Sury$Reventless.toJsonString(def, Plugin$Reventless.pluginDefinitionSchema, undefined);
61
+ }
62
+
63
+ function readAsset(json) {
64
+ let dir = Nodefs.mkdtempSync(Nodepath.join(Nodeos.tmpdir(), "plugindef-"));
65
+ let previousCwd = process.cwd();
66
+ Nodefs.writeFileSync(Nodepath.join(dir, "pluginDefinition.json"), json, "utf8");
67
+ process.chdir(dir);
68
+ let restore = () => {
69
+ process.chdir(previousCwd);
70
+ Nodefs.rmSync(dir, {
71
+ recursive: true,
72
+ force: true
73
+ });
74
+ };
75
+ let def;
76
+ try {
77
+ def = EventCollectorEntryPoint_Ops$ReventlessAws.loadPluginDefinition();
78
+ } catch (exn) {
79
+ restore();
80
+ throw exn;
81
+ }
82
+ restore();
83
+ return def;
84
+ }
85
+
86
+ globalThis.describe("pluginDefinition.json write/read symmetry", () => {
87
+ globalThis.test("an offloaded field reaches the runtime as Offloaded, not as its wire form", () => {
88
+ let def = readAsset(Util_Sury$Reventless.toJsonString(offloadedFragment, Plugin$Reventless.pluginDefinitionSchema, undefined));
89
+ let match = def.apiSchemaFragment;
90
+ if (match === undefined) {
91
+ return JestGlobals.fail("offloaded fragment decoded as None");
92
+ }
93
+ if (match.TAG === "Inline") {
94
+ return JestGlobals.fail("offloaded fragment decoded as Inline");
95
+ }
96
+ let match$1 = match._0;
97
+ globalThis.expect(match$1.store).toBe("pluginApiFragments");
98
+ globalThis.expect(match$1.bytes).toBe(14594);
99
+ });
100
+ globalThis.test("a definition read back from the asset can be published to the Connect handshake", () => {
101
+ [
102
+ offloadedFragment,
103
+ inlineFragment
104
+ ].forEach(def => {
105
+ let published = Util_Sury$Reventless.toJson({
106
+ TAG: "ConnectPlugin",
107
+ _0: readAsset(Util_Sury$Reventless.toJsonString(def, Plugin$Reventless.pluginDefinitionSchema, undefined))
108
+ }, PluginExtensionPointSpec$ReventlessInfra.commandSchema);
109
+ globalThis.expect(Stdlib_Option.isSome(Stdlib_JSON.Decode.object(published))).toBe(true);
110
+ });
111
+ });
112
+ globalThis.test("the asset round-trips byte-identically, so no stored message shifts", () => {
113
+ let once = Util_Sury$Reventless.toJsonString(offloadedFragment, Plugin$Reventless.pluginDefinitionSchema, undefined);
114
+ let def = readAsset(once);
115
+ globalThis.expect(Util_Sury$Reventless.toJsonString(def, Plugin$Reventless.pluginDefinitionSchema, undefined)).toBe(once);
116
+ });
117
+ globalThis.test("an absent optional reaches the runtime as None, not as null", () => {
118
+ let def = readAsset(Util_Sury$Reventless.toJsonString(offloadedFragment, Plugin$Reventless.pluginDefinitionSchema, undefined));
119
+ globalThis.expect(Stdlib_Option.isNone(def.structure)).toBe(true);
120
+ globalThis.expect(Stdlib_Option.isNone(def.dcbEventLog)).toBe(true);
121
+ });
122
+ globalThis.test("the platform's own placeholder asset survives the same round trip", () => {
123
+ let def = readAsset(Util_Sury$Reventless.toJsonString(Platform_Admin_Structure$ReventlessCore.internalPluginDefinition, Plugin$Reventless.pluginDefinitionSchema, undefined));
124
+ globalThis.expect(def.id).toBe(Platform_Admin_Structure$ReventlessCore.pluginId + "@INTERNAL");
125
+ globalThis.expect(def.kind).toBe("Domain");
126
+ });
127
+ });
128
+
129
+ export {
130
+ offloadedFragment,
131
+ inlineFragment,
132
+ writeAsset,
133
+ readAsset,
134
+ }
135
+ /* Not a pure module */