@ottochain/sdk 2.2.2 → 2.2.4

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.
@@ -3,32 +3,41 @@
3
3
  * Normalize OttoChain Messages for Signing
4
4
  *
5
5
  * Converts OttoChain message objects to the wire format expected by the
6
- * Scala metagraph. Specifically, ensures all `Option[A]=None` fields are
7
- * present as explicit `null` values, matching what circe's magnolia encoder
8
- * produces on the Scala side.
6
+ * Scala metagraph. Metakit's JsonBinaryCodec (used by OttoChain for data
7
+ * transaction signing) canonicalizes JSON with dropNullValues = true.
9
8
  *
10
- * This is necessary because metakit's `JsonBinaryCodec.deriveDataUpdate`
11
- * (rc.8) canonicalizes the circe-encoded JSON **including null fields**.
12
- * If the TypeScript client omits optional fields (undefined), the canonical
13
- * JSON won't match and signature verification will fail.
9
+ * NOTE: This is Metakit's codec, NOT tessellation's JsonSerializer (which
10
+ * uses Brotli compression for snapshots/consensus a different layer).
11
+ * Different layers use different byte-sequence encoders.
14
12
  *
15
- * Schema mapping (Scala TypeScript):
16
- * - `Option[A] = None` `null` (must be explicit, not undefined/absent)
13
+ * Because Metakit drops null fields during canonicalization, the canonical
14
+ * JSON used for signature verification **omits** null fields entirely.
15
+ * If the TypeScript client includes explicit nulls (e.g., `"metadata": null`),
16
+ * the canonical form won't match and signature verification will fail.
17
+ *
18
+ * Schema mapping (Scala → TypeScript wire format):
19
+ * - `Option[A] = None` → field OMITTED (not null, not undefined)
17
20
  * - `Option[A] = Some(v)` → `v`
18
- * - `List.empty` → `[]`
21
+ * - `List.empty` / `Set.empty` → `[]`
19
22
  * - `Map.empty` → `{}`
23
+ * - `Boolean = false` → `false` (always include, not a null/Option)
20
24
  */
21
25
  Object.defineProperty(exports, "__esModule", { value: true });
22
26
  exports.normalizeMessage = exports.normalizeArchiveStateMachine = exports.normalizeTransitionStateMachine = exports.normalizeCreateStateMachine = void 0;
23
27
  /**
24
- * Normalize a State object for wire format
28
+ * Normalize a State object for wire format.
29
+ * State: id (required), isFinal (default false), metadata (Option = None)
30
+ * Omit metadata when null/undefined (dropNullValues).
25
31
  */
26
32
  function normalizeState(state) {
27
- return {
33
+ const result = {
28
34
  id: state.id,
29
35
  isFinal: state.isFinal ?? false,
30
- metadata: state.metadata ?? null,
31
36
  };
37
+ if (state.metadata != null) {
38
+ result.metadata = state.metadata;
39
+ }
40
+ return result;
32
41
  }
33
42
  /**
34
43
  * Normalize a Transition object for wire format
@@ -54,8 +63,21 @@ function normalizeTransition(t) {
54
63
  dependencies: t.dependencies ?? [],
55
64
  };
56
65
  }
66
+ /**
67
+ * Check if a value looks like FiberAppMetadata (has 'name' and 'app' fields).
68
+ * These TypeScript-only fields should be stripped before sending to metagraph.
69
+ */
70
+ function isFiberAppMetadata(value) {
71
+ return (typeof value === 'object' &&
72
+ value !== null &&
73
+ 'name' in value &&
74
+ 'app' in value);
75
+ }
57
76
  /**
58
77
  * Normalize a StateMachineDefinition for wire format
78
+ *
79
+ * Strips FiberAppMetadata from the definition — the Scala schema expects
80
+ * `metadata: Option[Json] = None`, not the TypeScript app metadata object.
59
81
  */
60
82
  function normalizeDefinition(def) {
61
83
  const states = def.states;
@@ -66,41 +88,55 @@ function normalizeDefinition(def) {
66
88
  }
67
89
  }
68
90
  const transitions = def.transitions ?? [];
69
- return {
91
+ // Strip FiberAppMetadata if present — it's TypeScript-only, not part of wire format.
92
+ // Omit metadata entirely when null/FiberAppMetadata (dropNullValues = true on Scala side).
93
+ const wireMetadata = isFiberAppMetadata(def.metadata) ? undefined : def.metadata;
94
+ const result = {
70
95
  states: normalizedStates,
71
96
  initialState: def.initialState,
72
97
  transitions: transitions.map(normalizeTransition),
73
- metadata: def.metadata ?? null,
74
98
  };
99
+ if (wireMetadata != null) {
100
+ result.metadata = wireMetadata;
101
+ }
102
+ return result;
75
103
  }
76
104
  /**
77
105
  * Normalize a CreateStateMachine message for wire format
78
106
  *
79
- * Ensures all Option/default fields are explicit in wire format:
80
- * - definition.metadata null when absent
81
- * - definition.states[*].metadata null when absent
82
- * - definition.transitions[*].dependencies → [] when absent
83
- * - parentFiberIdnull when absent
84
- * - participantsnull when absent (Optional Set[Address] for multi-party signing)
107
+ * Metakit's JsonBinaryCodec uses dropNullValues = true for canonicalization.
108
+ * Optional fields at their defaults must be OMITTED (not set to null)
109
+ * to match the canonical JSON used for signature verification.
110
+ *
111
+ * - definition.metadataOMITTED when absent/FiberAppMetadata
112
+ * - definition.states[*].metadataOMITTED when absent
113
+ * - definition.transitions[*].dependencies → [] (Set.empty default, always included)
114
+ * - parentFiberId → OMITTED when absent (Option[UUID] = None)
115
+ * - participants → OMITTED when absent (Option[Set[Address]] = None)
85
116
  *
86
117
  * @example
87
118
  * ```typescript
88
119
  * const message = normalizeCreateStateMachine({
89
120
  * fiberId: '...',
90
- * definition: { states: { INIT: { id: { value: 'INIT' }, isFinal: false } }, ... },
121
+ * definition: { states: { INIT: { id: 'INIT', isFinal: false } }, ... },
91
122
  * initialData: {}
92
123
  * });
93
- * // message now has parentFiberId: null, participants: null, definition.metadata: null, etc.
124
+ * // Optional fields are absent (not null): no parentFiberId, no metadata, etc.
94
125
  * ```
95
126
  */
96
127
  function normalizeCreateStateMachine(msg) {
97
- return {
128
+ const result = {
98
129
  fiberId: msg.fiberId,
99
130
  definition: normalizeDefinition(msg.definition),
100
131
  initialData: msg.initialData ?? {},
101
- parentFiberId: msg.parentFiberId ?? null,
102
- participants: msg.participants ?? null,
103
132
  };
133
+ if (msg.parentFiberId != null) {
134
+ result.parentFiberId = msg.parentFiberId;
135
+ }
136
+ if (msg.participants != null) {
137
+ result.participants = msg.participants;
138
+ }
139
+ return result;
104
140
  }
105
141
  exports.normalizeCreateStateMachine = normalizeCreateStateMachine;
106
142
  /**
@@ -2,30 +2,39 @@
2
2
  * Normalize OttoChain Messages for Signing
3
3
  *
4
4
  * Converts OttoChain message objects to the wire format expected by the
5
- * Scala metagraph. Specifically, ensures all `Option[A]=None` fields are
6
- * present as explicit `null` values, matching what circe's magnolia encoder
7
- * produces on the Scala side.
5
+ * Scala metagraph. Metakit's JsonBinaryCodec (used by OttoChain for data
6
+ * transaction signing) canonicalizes JSON with dropNullValues = true.
8
7
  *
9
- * This is necessary because metakit's `JsonBinaryCodec.deriveDataUpdate`
10
- * (rc.8) canonicalizes the circe-encoded JSON **including null fields**.
11
- * If the TypeScript client omits optional fields (undefined), the canonical
12
- * JSON won't match and signature verification will fail.
8
+ * NOTE: This is Metakit's codec, NOT tessellation's JsonSerializer (which
9
+ * uses Brotli compression for snapshots/consensus a different layer).
10
+ * Different layers use different byte-sequence encoders.
13
11
  *
14
- * Schema mapping (Scala TypeScript):
15
- * - `Option[A] = None` `null` (must be explicit, not undefined/absent)
12
+ * Because Metakit drops null fields during canonicalization, the canonical
13
+ * JSON used for signature verification **omits** null fields entirely.
14
+ * If the TypeScript client includes explicit nulls (e.g., `"metadata": null`),
15
+ * the canonical form won't match and signature verification will fail.
16
+ *
17
+ * Schema mapping (Scala → TypeScript wire format):
18
+ * - `Option[A] = None` → field OMITTED (not null, not undefined)
16
19
  * - `Option[A] = Some(v)` → `v`
17
- * - `List.empty` → `[]`
20
+ * - `List.empty` / `Set.empty` → `[]`
18
21
  * - `Map.empty` → `{}`
22
+ * - `Boolean = false` → `false` (always include, not a null/Option)
19
23
  */
20
24
  /**
21
- * Normalize a State object for wire format
25
+ * Normalize a State object for wire format.
26
+ * State: id (required), isFinal (default false), metadata (Option = None)
27
+ * Omit metadata when null/undefined (dropNullValues).
22
28
  */
23
29
  function normalizeState(state) {
24
- return {
30
+ const result = {
25
31
  id: state.id,
26
32
  isFinal: state.isFinal ?? false,
27
- metadata: state.metadata ?? null,
28
33
  };
34
+ if (state.metadata != null) {
35
+ result.metadata = state.metadata;
36
+ }
37
+ return result;
29
38
  }
30
39
  /**
31
40
  * Normalize a Transition object for wire format
@@ -51,8 +60,21 @@ function normalizeTransition(t) {
51
60
  dependencies: t.dependencies ?? [],
52
61
  };
53
62
  }
63
+ /**
64
+ * Check if a value looks like FiberAppMetadata (has 'name' and 'app' fields).
65
+ * These TypeScript-only fields should be stripped before sending to metagraph.
66
+ */
67
+ function isFiberAppMetadata(value) {
68
+ return (typeof value === 'object' &&
69
+ value !== null &&
70
+ 'name' in value &&
71
+ 'app' in value);
72
+ }
54
73
  /**
55
74
  * Normalize a StateMachineDefinition for wire format
75
+ *
76
+ * Strips FiberAppMetadata from the definition — the Scala schema expects
77
+ * `metadata: Option[Json] = None`, not the TypeScript app metadata object.
56
78
  */
57
79
  function normalizeDefinition(def) {
58
80
  const states = def.states;
@@ -63,41 +85,55 @@ function normalizeDefinition(def) {
63
85
  }
64
86
  }
65
87
  const transitions = def.transitions ?? [];
66
- return {
88
+ // Strip FiberAppMetadata if present — it's TypeScript-only, not part of wire format.
89
+ // Omit metadata entirely when null/FiberAppMetadata (dropNullValues = true on Scala side).
90
+ const wireMetadata = isFiberAppMetadata(def.metadata) ? undefined : def.metadata;
91
+ const result = {
67
92
  states: normalizedStates,
68
93
  initialState: def.initialState,
69
94
  transitions: transitions.map(normalizeTransition),
70
- metadata: def.metadata ?? null,
71
95
  };
96
+ if (wireMetadata != null) {
97
+ result.metadata = wireMetadata;
98
+ }
99
+ return result;
72
100
  }
73
101
  /**
74
102
  * Normalize a CreateStateMachine message for wire format
75
103
  *
76
- * Ensures all Option/default fields are explicit in wire format:
77
- * - definition.metadata null when absent
78
- * - definition.states[*].metadata null when absent
79
- * - definition.transitions[*].dependencies → [] when absent
80
- * - parentFiberIdnull when absent
81
- * - participantsnull when absent (Optional Set[Address] for multi-party signing)
104
+ * Metakit's JsonBinaryCodec uses dropNullValues = true for canonicalization.
105
+ * Optional fields at their defaults must be OMITTED (not set to null)
106
+ * to match the canonical JSON used for signature verification.
107
+ *
108
+ * - definition.metadataOMITTED when absent/FiberAppMetadata
109
+ * - definition.states[*].metadataOMITTED when absent
110
+ * - definition.transitions[*].dependencies → [] (Set.empty default, always included)
111
+ * - parentFiberId → OMITTED when absent (Option[UUID] = None)
112
+ * - participants → OMITTED when absent (Option[Set[Address]] = None)
82
113
  *
83
114
  * @example
84
115
  * ```typescript
85
116
  * const message = normalizeCreateStateMachine({
86
117
  * fiberId: '...',
87
- * definition: { states: { INIT: { id: { value: 'INIT' }, isFinal: false } }, ... },
118
+ * definition: { states: { INIT: { id: 'INIT', isFinal: false } }, ... },
88
119
  * initialData: {}
89
120
  * });
90
- * // message now has parentFiberId: null, participants: null, definition.metadata: null, etc.
121
+ * // Optional fields are absent (not null): no parentFiberId, no metadata, etc.
91
122
  * ```
92
123
  */
93
124
  export function normalizeCreateStateMachine(msg) {
94
- return {
125
+ const result = {
95
126
  fiberId: msg.fiberId,
96
127
  definition: normalizeDefinition(msg.definition),
97
128
  initialData: msg.initialData ?? {},
98
- parentFiberId: msg.parentFiberId ?? null,
99
- participants: msg.participants ?? null,
100
129
  };
130
+ if (msg.parentFiberId != null) {
131
+ result.parentFiberId = msg.parentFiberId;
132
+ }
133
+ if (msg.participants != null) {
134
+ result.participants = msg.participants;
135
+ }
136
+ return result;
101
137
  }
102
138
  /**
103
139
  * Normalize a TransitionStateMachine message for wire format
@@ -2,39 +2,46 @@
2
2
  * Normalize OttoChain Messages for Signing
3
3
  *
4
4
  * Converts OttoChain message objects to the wire format expected by the
5
- * Scala metagraph. Specifically, ensures all `Option[A]=None` fields are
6
- * present as explicit `null` values, matching what circe's magnolia encoder
7
- * produces on the Scala side.
5
+ * Scala metagraph. Metakit's JsonBinaryCodec (used by OttoChain for data
6
+ * transaction signing) canonicalizes JSON with dropNullValues = true.
8
7
  *
9
- * This is necessary because metakit's `JsonBinaryCodec.deriveDataUpdate`
10
- * (rc.8) canonicalizes the circe-encoded JSON **including null fields**.
11
- * If the TypeScript client omits optional fields (undefined), the canonical
12
- * JSON won't match and signature verification will fail.
8
+ * NOTE: This is Metakit's codec, NOT tessellation's JsonSerializer (which
9
+ * uses Brotli compression for snapshots/consensus a different layer).
10
+ * Different layers use different byte-sequence encoders.
13
11
  *
14
- * Schema mapping (Scala TypeScript):
15
- * - `Option[A] = None` `null` (must be explicit, not undefined/absent)
12
+ * Because Metakit drops null fields during canonicalization, the canonical
13
+ * JSON used for signature verification **omits** null fields entirely.
14
+ * If the TypeScript client includes explicit nulls (e.g., `"metadata": null`),
15
+ * the canonical form won't match and signature verification will fail.
16
+ *
17
+ * Schema mapping (Scala → TypeScript wire format):
18
+ * - `Option[A] = None` → field OMITTED (not null, not undefined)
16
19
  * - `Option[A] = Some(v)` → `v`
17
- * - `List.empty` → `[]`
20
+ * - `List.empty` / `Set.empty` → `[]`
18
21
  * - `Map.empty` → `{}`
22
+ * - `Boolean = false` → `false` (always include, not a null/Option)
19
23
  */
20
24
  /**
21
25
  * Normalize a CreateStateMachine message for wire format
22
26
  *
23
- * Ensures all Option/default fields are explicit in wire format:
24
- * - definition.metadata null when absent
25
- * - definition.states[*].metadata null when absent
26
- * - definition.transitions[*].dependencies → [] when absent
27
- * - parentFiberIdnull when absent
28
- * - participantsnull when absent (Optional Set[Address] for multi-party signing)
27
+ * Metakit's JsonBinaryCodec uses dropNullValues = true for canonicalization.
28
+ * Optional fields at their defaults must be OMITTED (not set to null)
29
+ * to match the canonical JSON used for signature verification.
30
+ *
31
+ * - definition.metadataOMITTED when absent/FiberAppMetadata
32
+ * - definition.states[*].metadataOMITTED when absent
33
+ * - definition.transitions[*].dependencies → [] (Set.empty default, always included)
34
+ * - parentFiberId → OMITTED when absent (Option[UUID] = None)
35
+ * - participants → OMITTED when absent (Option[Set[Address]] = None)
29
36
  *
30
37
  * @example
31
38
  * ```typescript
32
39
  * const message = normalizeCreateStateMachine({
33
40
  * fiberId: '...',
34
- * definition: { states: { INIT: { id: { value: 'INIT' }, isFinal: false } }, ... },
41
+ * definition: { states: { INIT: { id: 'INIT', isFinal: false } }, ... },
35
42
  * initialData: {}
36
43
  * });
37
- * // message now has parentFiberId: null, participants: null, definition.metadata: null, etc.
44
+ * // Optional fields are absent (not null): no parentFiberId, no metadata, etc.
38
45
  * ```
39
46
  */
40
47
  export declare function normalizeCreateStateMachine(msg: Record<string, unknown>): Record<string, unknown>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ottochain/sdk",
3
- "version": "2.2.2",
3
+ "version": "2.2.4",
4
4
  "description": "TypeScript SDK for ottochain metagraph operations - signing, encoding, and network interactions",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",