@algovoi/substrate 0.2.1 → 0.3.0

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/dist/index.d.ts CHANGED
@@ -21,4 +21,5 @@ export { ActionRefError, type ActionRefInput, type ActionRefObject, actionRef, a
21
21
  export { CompositeTrustQueryError, type EmitterRow, compositeTrustQueryHash, } from './composite-trust-query.js';
22
22
  export { SCREEN_RESULTS, type ScreenResult, ComplianceReceiptError, type ComplianceReceipt, type BuildComplianceReceiptInput, buildComplianceReceipt, } from './compliance-receipt.js';
23
23
  export { AuditChainError, type AuditChainRow, appendToChain, verifyAuditChain, } from './audit-chain.js';
24
+ export { TransactionalError, type TransitionPreimage, type TransitionInput, type TransitionRecord, type TransactionalChain, type TransactionalChainInput, transitionPreimage, transitionHash, buildTransactionalActionChain, } from './transactional.js';
24
25
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,SAAS,GACV,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,SAAS,EACT,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,wBAAwB,EACxB,KAAK,UAAU,EACf,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACL,cAAc,EACd,KAAK,YAAY,EACjB,sBAAsB,EACtB,KAAK,iBAAiB,EACtB,KAAK,2BAA2B,EAChC,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,eAAe,EACf,KAAK,aAAa,EAClB,aAAa,EACb,gBAAgB,GACjB,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,SAAS,GACV,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,SAAS,EACT,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,wBAAwB,EACxB,KAAK,UAAU,EACf,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACL,cAAc,EACd,KAAK,YAAY,EACjB,sBAAsB,EACtB,KAAK,iBAAiB,EACtB,KAAK,2BAA2B,EAChC,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,eAAe,EACf,KAAK,aAAa,EAClB,aAAa,EACb,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,kBAAkB,EAClB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,kBAAkB,EAClB,cAAc,EACd,6BAA6B,GAC9B,MAAM,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -21,4 +21,5 @@ export { ActionRefError, actionRef, actionRefObject, } from './action-ref.js';
21
21
  export { CompositeTrustQueryError, compositeTrustQueryHash, } from './composite-trust-query.js';
22
22
  export { SCREEN_RESULTS, ComplianceReceiptError, buildComplianceReceipt, } from './compliance-receipt.js';
23
23
  export { AuditChainError, appendToChain, verifyAuditChain, } from './audit-chain.js';
24
+ export { TransactionalError, transitionPreimage, transitionHash, buildTransactionalActionChain, } from './transactional.js';
24
25
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,SAAS,GACV,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,cAAc,EAGd,SAAS,EACT,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,wBAAwB,EAExB,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACL,cAAc,EAEd,sBAAsB,EAGtB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,eAAe,EAEf,aAAa,EACb,gBAAgB,GACjB,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,SAAS,GACV,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,cAAc,EAGd,SAAS,EACT,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,wBAAwB,EAExB,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACL,cAAc,EAEd,sBAAsB,EAGtB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,eAAe,EAEf,aAAa,EACb,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,kBAAkB,EAMlB,kBAAkB,EAClB,cAAc,EACd,6BAA6B,GAC9B,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Transactional action_ref lifecycle (non-normative substrate extension).
3
+ *
4
+ * For actions that traverse multiple state transitions
5
+ * (authorisation -> settlement -> refund; issuance -> execution ->
6
+ * revocation; admission -> review -> close), the action_ref four-field
7
+ * preimage is the STABLE identity anchor across the full lifecycle, and
8
+ * per-transition lifecycle metadata sits OUTSIDE the action_ref preimage.
9
+ *
10
+ * Invariants:
11
+ *
12
+ * 1. action_ref is byte-stable across every transition of the same action.
13
+ * 2. transition_hash is byte-stable for identical preimage tuples.
14
+ * 3. transition_hash differs per state for the same action_ref + timestamps.
15
+ * 4. action_ref and any transition_hash are byte-distinct.
16
+ * 5. All timestamp fields MUST be epoch-millisecond integers per
17
+ * Substrate Rule 2. RFC 3339 string forms are rejected.
18
+ *
19
+ * State value is a non-empty string with no closed enum at the
20
+ * canonicalisation layer, consistent with the scope-field treatment.
21
+ */
22
+ export declare class TransactionalError extends Error {
23
+ constructor(message: string);
24
+ }
25
+ export interface TransitionPreimage {
26
+ action_ref: string;
27
+ state: string;
28
+ transition_timestamp_ms: number;
29
+ authority_verified_at_ms: number;
30
+ revocation_check_at_ms: number;
31
+ }
32
+ export interface TransitionInput {
33
+ state: string;
34
+ transition_timestamp_ms: number;
35
+ authority_verified_at_ms: number;
36
+ revocation_check_at_ms: number;
37
+ }
38
+ export interface TransitionRecord extends TransitionInput {
39
+ transition_hash: string;
40
+ }
41
+ export interface TransactionalChain {
42
+ action_ref: string;
43
+ transitions: TransitionRecord[];
44
+ }
45
+ export interface TransactionalChainInput {
46
+ agent_id: string;
47
+ action_type: string;
48
+ scope: string;
49
+ timestamp_ms: number;
50
+ transitions: TransitionInput[];
51
+ }
52
+ /**
53
+ * Return the validated preimage object for a single transition.
54
+ *
55
+ * The five-field shape is fixed at the substrate layer; the action_ref
56
+ * field cryptographically binds the transition to its action identity.
57
+ */
58
+ export declare function transitionPreimage(input: {
59
+ action_ref: string;
60
+ state: string;
61
+ transition_timestamp_ms: number;
62
+ authority_verified_at_ms: number;
63
+ revocation_check_at_ms: number;
64
+ }): TransitionPreimage;
65
+ /**
66
+ * Return the lowercase hex SHA-256 transition_hash.
67
+ *
68
+ * transition_hash = SHA-256(JCS({action_ref, state,
69
+ * transition_timestamp_ms,
70
+ * authority_verified_at_ms,
71
+ * revocation_check_at_ms}))
72
+ */
73
+ export declare function transitionHash(input: {
74
+ action_ref: string;
75
+ state: string;
76
+ transition_timestamp_ms: number;
77
+ authority_verified_at_ms: number;
78
+ revocation_check_at_ms: number;
79
+ }): string;
80
+ /**
81
+ * Build a transactional action chain.
82
+ *
83
+ * Returns the chain shape:
84
+ * {
85
+ * action_ref: "<lowercase 64-char hex>",
86
+ * transitions: [
87
+ * {
88
+ * state, transition_timestamp_ms,
89
+ * authority_verified_at_ms, revocation_check_at_ms,
90
+ * transition_hash
91
+ * },
92
+ * ...
93
+ * ]
94
+ * }
95
+ *
96
+ * The action_ref is computed once and is byte-stable across every
97
+ * transition. Each transition_hash is bound to the action_ref by
98
+ * including it in the transition preimage. Transitions are processed
99
+ * in the order supplied; the substrate verifies determinism, not
100
+ * lifecycle semantics.
101
+ */
102
+ export declare function buildTransactionalActionChain(input: TransactionalChainInput): TransactionalChain;
103
+ //# sourceMappingURL=transactional.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transactional.d.ts","sourceRoot":"","sources":["../src/transactional.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAOH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,uBAAuB,EAAE,MAAM,CAAC;IAChC,wBAAwB,EAAE,MAAM,CAAC;IACjC,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,uBAAuB,EAAE,MAAM,CAAC;IAChC,wBAAwB,EAAE,MAAM,CAAC;IACjC,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,gBAAiB,SAAQ,eAAe;IACvD,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,gBAAgB,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,eAAe,EAAE,CAAC;CAChC;AAgDD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,uBAAuB,EAAE,MAAM,CAAC;IAChC,wBAAwB,EAAE,MAAM,CAAC;IACjC,sBAAsB,EAAE,MAAM,CAAC;CAChC,GAAG,kBAAkB,CAiBrB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,uBAAuB,EAAE,MAAM,CAAC;IAChC,wBAAwB,EAAE,MAAM,CAAC;IACjC,sBAAsB,EAAE,MAAM,CAAC;CAChC,GAAG,MAAM,CAGT;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,uBAAuB,GAC7B,kBAAkB,CAsDpB"}
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Transactional action_ref lifecycle (non-normative substrate extension).
3
+ *
4
+ * For actions that traverse multiple state transitions
5
+ * (authorisation -> settlement -> refund; issuance -> execution ->
6
+ * revocation; admission -> review -> close), the action_ref four-field
7
+ * preimage is the STABLE identity anchor across the full lifecycle, and
8
+ * per-transition lifecycle metadata sits OUTSIDE the action_ref preimage.
9
+ *
10
+ * Invariants:
11
+ *
12
+ * 1. action_ref is byte-stable across every transition of the same action.
13
+ * 2. transition_hash is byte-stable for identical preimage tuples.
14
+ * 3. transition_hash differs per state for the same action_ref + timestamps.
15
+ * 4. action_ref and any transition_hash are byte-distinct.
16
+ * 5. All timestamp fields MUST be epoch-millisecond integers per
17
+ * Substrate Rule 2. RFC 3339 string forms are rejected.
18
+ *
19
+ * State value is a non-empty string with no closed enum at the
20
+ * canonicalisation layer, consistent with the scope-field treatment.
21
+ */
22
+ import { actionRef } from './action-ref.js';
23
+ import { sha256Jcs } from './canonicalize.js';
24
+ const HEX64_RE = /^[0-9a-f]{64}$/;
25
+ export class TransactionalError extends Error {
26
+ constructor(message) {
27
+ super(message);
28
+ this.name = 'TransactionalError';
29
+ }
30
+ }
31
+ function requireActionRefHex(value) {
32
+ if (typeof value !== 'string') {
33
+ throw new TransactionalError(`action_ref must be string, got ${typeof value}`);
34
+ }
35
+ if (!HEX64_RE.test(value)) {
36
+ throw new TransactionalError('action_ref must be a lowercase 64-character SHA-256 hex string');
37
+ }
38
+ return value;
39
+ }
40
+ function requireState(value) {
41
+ if (typeof value !== 'string') {
42
+ throw new TransactionalError(`state must be string, got ${typeof value}`);
43
+ }
44
+ if (value.length === 0) {
45
+ throw new TransactionalError('state must be a non-empty string');
46
+ }
47
+ return value;
48
+ }
49
+ function requireIntMs(field, value) {
50
+ if (typeof value !== 'number') {
51
+ throw new TransactionalError(`${field} must be epoch-millisecond integer (Substrate Rule 2), got ${typeof value}`);
52
+ }
53
+ if (!Number.isFinite(value)) {
54
+ throw new TransactionalError(`${field} must be a finite number, got ${value}`);
55
+ }
56
+ if (!Number.isInteger(value)) {
57
+ throw new TransactionalError(`${field} must be epoch-millisecond integer (Substrate Rule 2), got float ${value}`);
58
+ }
59
+ if (value < 0) {
60
+ throw new TransactionalError(`${field} must be non-negative, got ${value}`);
61
+ }
62
+ return value;
63
+ }
64
+ /**
65
+ * Return the validated preimage object for a single transition.
66
+ *
67
+ * The five-field shape is fixed at the substrate layer; the action_ref
68
+ * field cryptographically binds the transition to its action identity.
69
+ */
70
+ export function transitionPreimage(input) {
71
+ return {
72
+ action_ref: requireActionRefHex(input.action_ref),
73
+ state: requireState(input.state),
74
+ transition_timestamp_ms: requireIntMs('transition_timestamp_ms', input.transition_timestamp_ms),
75
+ authority_verified_at_ms: requireIntMs('authority_verified_at_ms', input.authority_verified_at_ms),
76
+ revocation_check_at_ms: requireIntMs('revocation_check_at_ms', input.revocation_check_at_ms),
77
+ };
78
+ }
79
+ /**
80
+ * Return the lowercase hex SHA-256 transition_hash.
81
+ *
82
+ * transition_hash = SHA-256(JCS({action_ref, state,
83
+ * transition_timestamp_ms,
84
+ * authority_verified_at_ms,
85
+ * revocation_check_at_ms}))
86
+ */
87
+ export function transitionHash(input) {
88
+ const obj = transitionPreimage(input);
89
+ return sha256Jcs(obj);
90
+ }
91
+ /**
92
+ * Build a transactional action chain.
93
+ *
94
+ * Returns the chain shape:
95
+ * {
96
+ * action_ref: "<lowercase 64-char hex>",
97
+ * transitions: [
98
+ * {
99
+ * state, transition_timestamp_ms,
100
+ * authority_verified_at_ms, revocation_check_at_ms,
101
+ * transition_hash
102
+ * },
103
+ * ...
104
+ * ]
105
+ * }
106
+ *
107
+ * The action_ref is computed once and is byte-stable across every
108
+ * transition. Each transition_hash is bound to the action_ref by
109
+ * including it in the transition preimage. Transitions are processed
110
+ * in the order supplied; the substrate verifies determinism, not
111
+ * lifecycle semantics.
112
+ */
113
+ export function buildTransactionalActionChain(input) {
114
+ if (!Array.isArray(input.transitions)) {
115
+ throw new TransactionalError('transitions must be an array');
116
+ }
117
+ if (input.transitions.length === 0) {
118
+ throw new TransactionalError('transitions array must be non-empty');
119
+ }
120
+ const ar = actionRef({
121
+ agent_id: input.agent_id,
122
+ action_type: input.action_type,
123
+ scope: input.scope,
124
+ timestamp_ms: input.timestamp_ms,
125
+ });
126
+ const records = input.transitions.map((t, i) => {
127
+ if (t === null || typeof t !== 'object') {
128
+ throw new TransactionalError(`transitions[${i}] must be object, got ${typeof t}`);
129
+ }
130
+ const required = [
131
+ 'state',
132
+ 'transition_timestamp_ms',
133
+ 'authority_verified_at_ms',
134
+ 'revocation_check_at_ms',
135
+ ];
136
+ for (const k of required) {
137
+ if (!(k in t)) {
138
+ throw new TransactionalError(`transitions[${i}] missing required field '${k}'`);
139
+ }
140
+ }
141
+ const th = transitionHash({
142
+ action_ref: ar,
143
+ state: t.state,
144
+ transition_timestamp_ms: t.transition_timestamp_ms,
145
+ authority_verified_at_ms: t.authority_verified_at_ms,
146
+ revocation_check_at_ms: t.revocation_check_at_ms,
147
+ });
148
+ return {
149
+ state: t.state,
150
+ transition_timestamp_ms: t.transition_timestamp_ms,
151
+ authority_verified_at_ms: t.authority_verified_at_ms,
152
+ revocation_check_at_ms: t.revocation_check_at_ms,
153
+ transition_hash: th,
154
+ };
155
+ });
156
+ return {
157
+ action_ref: ar,
158
+ transitions: records,
159
+ };
160
+ }
161
+ //# sourceMappingURL=transactional.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transactional.js","sourceRoot":"","sources":["../src/transactional.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@algovoi/substrate",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "AlgoVoi agentic-payments substrate -- JCS canonicalisation, action_ref, composite trust-query, compliance receipts, audit chain",
5
5
  "keywords": [
6
6
  "x402",
package/src/index.ts CHANGED
@@ -54,3 +54,15 @@ export {
54
54
  appendToChain,
55
55
  verifyAuditChain,
56
56
  } from './audit-chain.js';
57
+
58
+ export {
59
+ TransactionalError,
60
+ type TransitionPreimage,
61
+ type TransitionInput,
62
+ type TransitionRecord,
63
+ type TransactionalChain,
64
+ type TransactionalChainInput,
65
+ transitionPreimage,
66
+ transitionHash,
67
+ buildTransactionalActionChain,
68
+ } from './transactional.js';
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Transactional action_ref lifecycle (non-normative substrate extension).
3
+ *
4
+ * For actions that traverse multiple state transitions
5
+ * (authorisation -> settlement -> refund; issuance -> execution ->
6
+ * revocation; admission -> review -> close), the action_ref four-field
7
+ * preimage is the STABLE identity anchor across the full lifecycle, and
8
+ * per-transition lifecycle metadata sits OUTSIDE the action_ref preimage.
9
+ *
10
+ * Invariants:
11
+ *
12
+ * 1. action_ref is byte-stable across every transition of the same action.
13
+ * 2. transition_hash is byte-stable for identical preimage tuples.
14
+ * 3. transition_hash differs per state for the same action_ref + timestamps.
15
+ * 4. action_ref and any transition_hash are byte-distinct.
16
+ * 5. All timestamp fields MUST be epoch-millisecond integers per
17
+ * Substrate Rule 2. RFC 3339 string forms are rejected.
18
+ *
19
+ * State value is a non-empty string with no closed enum at the
20
+ * canonicalisation layer, consistent with the scope-field treatment.
21
+ */
22
+
23
+ import { actionRef } from './action-ref.js';
24
+ import { sha256Jcs } from './canonicalize.js';
25
+
26
+ const HEX64_RE = /^[0-9a-f]{64}$/;
27
+
28
+ export class TransactionalError extends Error {
29
+ constructor(message: string) {
30
+ super(message);
31
+ this.name = 'TransactionalError';
32
+ }
33
+ }
34
+
35
+ export interface TransitionPreimage {
36
+ action_ref: string;
37
+ state: string;
38
+ transition_timestamp_ms: number;
39
+ authority_verified_at_ms: number;
40
+ revocation_check_at_ms: number;
41
+ }
42
+
43
+ export interface TransitionInput {
44
+ state: string;
45
+ transition_timestamp_ms: number;
46
+ authority_verified_at_ms: number;
47
+ revocation_check_at_ms: number;
48
+ }
49
+
50
+ export interface TransitionRecord extends TransitionInput {
51
+ transition_hash: string;
52
+ }
53
+
54
+ export interface TransactionalChain {
55
+ action_ref: string;
56
+ transitions: TransitionRecord[];
57
+ }
58
+
59
+ export interface TransactionalChainInput {
60
+ agent_id: string;
61
+ action_type: string;
62
+ scope: string;
63
+ timestamp_ms: number;
64
+ transitions: TransitionInput[];
65
+ }
66
+
67
+ function requireActionRefHex(value: unknown): string {
68
+ if (typeof value !== 'string') {
69
+ throw new TransactionalError(
70
+ `action_ref must be string, got ${typeof value}`,
71
+ );
72
+ }
73
+ if (!HEX64_RE.test(value)) {
74
+ throw new TransactionalError(
75
+ 'action_ref must be a lowercase 64-character SHA-256 hex string',
76
+ );
77
+ }
78
+ return value;
79
+ }
80
+
81
+ function requireState(value: unknown): string {
82
+ if (typeof value !== 'string') {
83
+ throw new TransactionalError(`state must be string, got ${typeof value}`);
84
+ }
85
+ if (value.length === 0) {
86
+ throw new TransactionalError('state must be a non-empty string');
87
+ }
88
+ return value;
89
+ }
90
+
91
+ function requireIntMs(field: string, value: unknown): number {
92
+ if (typeof value !== 'number') {
93
+ throw new TransactionalError(
94
+ `${field} must be epoch-millisecond integer (Substrate Rule 2), got ${typeof value}`,
95
+ );
96
+ }
97
+ if (!Number.isFinite(value)) {
98
+ throw new TransactionalError(
99
+ `${field} must be a finite number, got ${value}`,
100
+ );
101
+ }
102
+ if (!Number.isInteger(value)) {
103
+ throw new TransactionalError(
104
+ `${field} must be epoch-millisecond integer (Substrate Rule 2), got float ${value}`,
105
+ );
106
+ }
107
+ if (value < 0) {
108
+ throw new TransactionalError(`${field} must be non-negative, got ${value}`);
109
+ }
110
+ return value;
111
+ }
112
+
113
+ /**
114
+ * Return the validated preimage object for a single transition.
115
+ *
116
+ * The five-field shape is fixed at the substrate layer; the action_ref
117
+ * field cryptographically binds the transition to its action identity.
118
+ */
119
+ export function transitionPreimage(input: {
120
+ action_ref: string;
121
+ state: string;
122
+ transition_timestamp_ms: number;
123
+ authority_verified_at_ms: number;
124
+ revocation_check_at_ms: number;
125
+ }): TransitionPreimage {
126
+ return {
127
+ action_ref: requireActionRefHex(input.action_ref),
128
+ state: requireState(input.state),
129
+ transition_timestamp_ms: requireIntMs(
130
+ 'transition_timestamp_ms',
131
+ input.transition_timestamp_ms,
132
+ ),
133
+ authority_verified_at_ms: requireIntMs(
134
+ 'authority_verified_at_ms',
135
+ input.authority_verified_at_ms,
136
+ ),
137
+ revocation_check_at_ms: requireIntMs(
138
+ 'revocation_check_at_ms',
139
+ input.revocation_check_at_ms,
140
+ ),
141
+ };
142
+ }
143
+
144
+ /**
145
+ * Return the lowercase hex SHA-256 transition_hash.
146
+ *
147
+ * transition_hash = SHA-256(JCS({action_ref, state,
148
+ * transition_timestamp_ms,
149
+ * authority_verified_at_ms,
150
+ * revocation_check_at_ms}))
151
+ */
152
+ export function transitionHash(input: {
153
+ action_ref: string;
154
+ state: string;
155
+ transition_timestamp_ms: number;
156
+ authority_verified_at_ms: number;
157
+ revocation_check_at_ms: number;
158
+ }): string {
159
+ const obj = transitionPreimage(input);
160
+ return sha256Jcs(obj);
161
+ }
162
+
163
+ /**
164
+ * Build a transactional action chain.
165
+ *
166
+ * Returns the chain shape:
167
+ * {
168
+ * action_ref: "<lowercase 64-char hex>",
169
+ * transitions: [
170
+ * {
171
+ * state, transition_timestamp_ms,
172
+ * authority_verified_at_ms, revocation_check_at_ms,
173
+ * transition_hash
174
+ * },
175
+ * ...
176
+ * ]
177
+ * }
178
+ *
179
+ * The action_ref is computed once and is byte-stable across every
180
+ * transition. Each transition_hash is bound to the action_ref by
181
+ * including it in the transition preimage. Transitions are processed
182
+ * in the order supplied; the substrate verifies determinism, not
183
+ * lifecycle semantics.
184
+ */
185
+ export function buildTransactionalActionChain(
186
+ input: TransactionalChainInput,
187
+ ): TransactionalChain {
188
+ if (!Array.isArray(input.transitions)) {
189
+ throw new TransactionalError('transitions must be an array');
190
+ }
191
+ if (input.transitions.length === 0) {
192
+ throw new TransactionalError('transitions array must be non-empty');
193
+ }
194
+
195
+ const ar = actionRef({
196
+ agent_id: input.agent_id,
197
+ action_type: input.action_type,
198
+ scope: input.scope,
199
+ timestamp_ms: input.timestamp_ms,
200
+ });
201
+
202
+ const records: TransitionRecord[] = input.transitions.map((t, i) => {
203
+ if (t === null || typeof t !== 'object') {
204
+ throw new TransactionalError(
205
+ `transitions[${i}] must be object, got ${typeof t}`,
206
+ );
207
+ }
208
+ const required = [
209
+ 'state',
210
+ 'transition_timestamp_ms',
211
+ 'authority_verified_at_ms',
212
+ 'revocation_check_at_ms',
213
+ ] as const;
214
+ for (const k of required) {
215
+ if (!(k in t)) {
216
+ throw new TransactionalError(
217
+ `transitions[${i}] missing required field '${k}'`,
218
+ );
219
+ }
220
+ }
221
+ const th = transitionHash({
222
+ action_ref: ar,
223
+ state: t.state,
224
+ transition_timestamp_ms: t.transition_timestamp_ms,
225
+ authority_verified_at_ms: t.authority_verified_at_ms,
226
+ revocation_check_at_ms: t.revocation_check_at_ms,
227
+ });
228
+ return {
229
+ state: t.state,
230
+ transition_timestamp_ms: t.transition_timestamp_ms,
231
+ authority_verified_at_ms: t.authority_verified_at_ms,
232
+ revocation_check_at_ms: t.revocation_check_at_ms,
233
+ transition_hash: th,
234
+ };
235
+ });
236
+
237
+ return {
238
+ action_ref: ar,
239
+ transitions: records,
240
+ };
241
+ }