@axorum/client 0.0.0-stage → 1.0.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.
Files changed (79) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +530 -2
  3. package/dist/agent-uri.d.ts +30 -0
  4. package/dist/agent-uri.d.ts.map +1 -0
  5. package/dist/agent-uri.js +218 -0
  6. package/dist/agent-uri.js.map +1 -0
  7. package/dist/authorize.d.ts +671 -0
  8. package/dist/authorize.d.ts.map +1 -0
  9. package/dist/authorize.js +947 -0
  10. package/dist/authorize.js.map +1 -0
  11. package/dist/capabilities.d.ts +30 -0
  12. package/dist/capabilities.d.ts.map +1 -0
  13. package/dist/capabilities.js +40 -0
  14. package/dist/capabilities.js.map +1 -0
  15. package/dist/chart.d.ts +74 -0
  16. package/dist/chart.d.ts.map +1 -0
  17. package/dist/chart.js +107 -0
  18. package/dist/chart.js.map +1 -0
  19. package/dist/client.d.ts +644 -0
  20. package/dist/client.d.ts.map +1 -0
  21. package/dist/client.js +1302 -0
  22. package/dist/client.js.map +1 -0
  23. package/dist/errors.d.ts +646 -0
  24. package/dist/errors.d.ts.map +1 -0
  25. package/dist/errors.js +928 -0
  26. package/dist/errors.js.map +1 -0
  27. package/dist/failover.d.ts +197 -0
  28. package/dist/failover.d.ts.map +1 -0
  29. package/dist/failover.js +289 -0
  30. package/dist/failover.js.map +1 -0
  31. package/dist/freshness.d.ts +65 -0
  32. package/dist/freshness.d.ts.map +1 -0
  33. package/dist/freshness.js +148 -0
  34. package/dist/freshness.js.map +1 -0
  35. package/dist/generated/agent-plane.d.ts +1863 -0
  36. package/dist/generated/agent-plane.d.ts.map +1 -0
  37. package/dist/generated/agent-plane.js +19 -0
  38. package/dist/generated/agent-plane.js.map +1 -0
  39. package/dist/idempotency.d.ts +160 -0
  40. package/dist/idempotency.d.ts.map +1 -0
  41. package/dist/idempotency.js +208 -0
  42. package/dist/idempotency.js.map +1 -0
  43. package/dist/index.d.ts +89 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +87 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/intent.d.ts +163 -0
  48. package/dist/intent.d.ts.map +1 -0
  49. package/dist/intent.js +82 -0
  50. package/dist/intent.js.map +1 -0
  51. package/dist/json.d.ts +49 -0
  52. package/dist/json.d.ts.map +1 -0
  53. package/dist/json.js +259 -0
  54. package/dist/json.js.map +1 -0
  55. package/dist/key.d.ts +64 -0
  56. package/dist/key.d.ts.map +1 -0
  57. package/dist/key.js +71 -0
  58. package/dist/key.js.map +1 -0
  59. package/dist/payment.d.ts +172 -0
  60. package/dist/payment.d.ts.map +1 -0
  61. package/dist/payment.js +178 -0
  62. package/dist/payment.js.map +1 -0
  63. package/dist/refusal.d.ts +121 -0
  64. package/dist/refusal.d.ts.map +1 -0
  65. package/dist/refusal.js +176 -0
  66. package/dist/refusal.js.map +1 -0
  67. package/dist/retry.d.ts +215 -0
  68. package/dist/retry.d.ts.map +1 -0
  69. package/dist/retry.js +297 -0
  70. package/dist/retry.js.map +1 -0
  71. package/dist/typeid.d.ts +64 -0
  72. package/dist/typeid.d.ts.map +1 -0
  73. package/dist/typeid.js +119 -0
  74. package/dist/typeid.js.map +1 -0
  75. package/dist/types.d.ts +245 -0
  76. package/dist/types.d.ts.map +1 -0
  77. package/dist/types.js +14 -0
  78. package/dist/types.js.map +1 -0
  79. package/package.json +58 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,163 @@
1
+ /**
2
+ * An intent with everything but the policy pin.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import type { ActionTerm, Entry, IntentEnvelope, UsageMove } from "./types.js";
7
+ /**
8
+ * An {@link IntentEnvelope} with everything the agent knows, and **not** the one thing
9
+ * only the service does: which policy is in force.
10
+ *
11
+ * The pin is not the agent's to choose. It is a claim about the *service's* state — "I
12
+ * believe policy P is in force" — and getting it wrong is a `409`. So it is left off
13
+ * here, and {@link AxorumClient.submitPinned} stamps it from the service's own answer,
14
+ * re-stamping it if the policy moves mid-flight.
15
+ *
16
+ * A plain object, not a builder: `entries`, `evidence` and `justification` are
17
+ * optional and default to empty, which is a well-formed thing to *hold* — the ledger,
18
+ * not this type, is the judge of whether it is a well-formed thing to *submit*.
19
+ *
20
+ * ```ts
21
+ * const draft: IntentDraft = {
22
+ * agent: "agent://example.com/agent/clerk_01h455vb4pex5vsknk084sn02q",
23
+ * attestation: paseto,
24
+ * action: { action: "post", bindings: {} },
25
+ * entries: [
26
+ * { account: cash, side: "Debit", amount: { minor: 1_000n, currency: "USD" } },
27
+ * { account: revenue, side: "Credit", amount: { minor: 1_000n, currency: "USD" } },
28
+ * ],
29
+ * justification: "invoice 4471, approved by the purchase order",
30
+ * };
31
+ * ```
32
+ *
33
+ * # Usage moves ride the same draft
34
+ *
35
+ * A metered spend is one intent, not two. The `consume_usage` / `meter_usage` /
36
+ * `bill_usage` moves travel on the *same* draft as the journal legs, are judged in the
37
+ * *same* commit, and are held to the usage-balance conservation law
38
+ * `0 ≤ billed ≤ metered ≤ consumed`. So an agent that consumes an API call, meters it, and
39
+ * bills for it does so atomically — and the three moves are validated **together** against
40
+ * the combined post-state, which is why one intent may lawfully do all three at once:
41
+ *
42
+ * ```ts
43
+ * const metered: IntentDraft = {
44
+ * ...draft,
45
+ * consume_usage: { usage, amount: { minor: 1_000n, currency: "USD" } },
46
+ * meter_usage: { usage, amount: { minor: 1_000n, currency: "USD" } },
47
+ * bill_usage: { usage, amount: { minor: 1_000n, currency: "USD" } },
48
+ * };
49
+ * ```
50
+ *
51
+ * A move that would break the chain — metering past consumption, billing past metering, a
52
+ * correction below zero, a balance the agent's party does not own — is refused, and
53
+ * **nothing posts**: not the usage moves, and not the journal legs that rode with them.
54
+ */
55
+ export interface IntentDraft {
56
+ /** The agent's `agent://` URI. */
57
+ agent: string;
58
+ /** The PASETO v4.public capability attestation proving the agent may act. */
59
+ attestation: string;
60
+ /** The proposed deontic action and its typed bindings. */
61
+ action: ActionTerm;
62
+ /**
63
+ * The client-minted transaction id (`txn_…`) — the substrate's **idempotency key**.
64
+ *
65
+ * Omit it and the client mints one for you, **once**, before its first attempt: every
66
+ * re-pinned retry then carries the same id, so an attempt that in fact landed replays
67
+ * its stored outcome instead of posting twice. That is the entire reason the retry
68
+ * loop is safe.
69
+ *
70
+ * Supply your own when you need to correlate the submission with something outside
71
+ * Axorum, or to deliberately replay a transaction you already submitted. Mint it with
72
+ * {@link newTransactionId}: a UUID v7 id carries its own mint time. An id that does not
73
+ * (one you derived or were given) needs {@link IntentDraft.minted_at_ms} too.
74
+ */
75
+ transaction?: string;
76
+ /**
77
+ * When `transaction` was first issued, in whole milliseconds since the Unix epoch, as
78
+ * the envelope's `minted_at_ms` carries it: the `mintedAtMs` of an
79
+ * {@link IdempotencyKey} you persisted, or the issue time of an id that is not a UUID
80
+ * v7 and so carries none. Leave it out for a UUID v7 id, whose own timestamp is read.
81
+ * Never the time of a retry: the service ages the key from it.
82
+ */
83
+ minted_at_ms?: bigint;
84
+ /** The accounting journal legs. Debits and credits must balance per currency. */
85
+ entries?: Entry[];
86
+ /** Exact distinct obligation IDs to fulfill atomically, at most 64. */
87
+ fulfills?: string[];
88
+ /** Lowercase-hex SHA-256 digests of external evidence the agent references. */
89
+ evidence?: string[];
90
+ /**
91
+ * The agent's stated reason. The gateway hashes it onto the ledger; the text itself
92
+ * stays with you.
93
+ */
94
+ justification?: string;
95
+ /**
96
+ * The **consume** move this intent carries: the ground-truth event that the agent used a
97
+ * metered resource, as a signed delta on a usage balance's `consumed` running total.
98
+ *
99
+ * `consumed` is the top of the chain and takes no ceiling — consumption is a fact, not a
100
+ * recognition, so nothing bounds it from above.
101
+ */
102
+ consume_usage?: UsageMove;
103
+ /**
104
+ * The **meter** move this intent carries: the measurement recognition of consumption, as
105
+ * a signed delta on a usage balance's `metered` running total.
106
+ *
107
+ * Metering past `consumed` is **phantom usage** — measuring more than was used — and the
108
+ * ledger refuses it.
109
+ */
110
+ meter_usage?: UsageMove;
111
+ /**
112
+ * The **bill** move this intent carries: the financial recognition of metering, as a
113
+ * signed delta on a usage balance's `billed` running total.
114
+ *
115
+ * Billing past `metered` is billing with no meter basis, and the ledger refuses it.
116
+ */
117
+ bill_usage?: UsageMove;
118
+ }
119
+ /**
120
+ * A draft whose transaction id and mint time are settled — minted if the caller left the
121
+ * id out.
122
+ *
123
+ * Internal, and the reason it exists is the whole idempotency story: the id and its mint
124
+ * time are settled **once**, before the first attempt, not on each pass through the retry
125
+ * loop. A loop that re-minted would double-post, and one that re-stamped would age the
126
+ * key from the wrong time.
127
+ */
128
+ export type SettledDraft = IntentDraft & {
129
+ transaction: string;
130
+ minted_at_ms: bigint;
131
+ };
132
+ /**
133
+ * Settles a draft's transaction id and mint time: a fresh UUID v7 id minted now when it
134
+ * has none, else the draft's `minted_at_ms`, else the time its UUID v7 id carries.
135
+ *
136
+ * Pure but for the mint. Call it once, at the top of a submission — never inside a
137
+ * retry loop.
138
+ *
139
+ * @throws {ConfigError} If the id is not a UUID v7 and the draft sets no
140
+ * `minted_at_ms`: the client never invents a mint time for an id it did not mint. Or
141
+ * if it sets `minted_at_ms` with no id to belong to.
142
+ */
143
+ export declare function settleDraft(draft: IntentDraft): SettledDraft;
144
+ /**
145
+ * Stamps the policy pin and the id's mint time onto a draft, completing the envelope.
146
+ *
147
+ * **A pure function** — which is the whole reason the draft/envelope split is testable
148
+ * without a service running. Re-pinning the same draft changes the policy and *nothing
149
+ * else*: above all not the transaction id, which is the idempotency key that makes a
150
+ * retry safe rather than a double-post.
151
+ *
152
+ * If the draft has no transaction id, one is minted here; its mint time is settled as
153
+ * {@link settleDraft} does, so a re-pin of a settled draft keeps it. Prefer letting
154
+ * {@link AxorumClient.submitPinned} do the pinning — this is exported for callers who
155
+ * drive the pin themselves, and for the tests that hold the retry loop to account.
156
+ *
157
+ * @param draft - The intent, minus the pin.
158
+ * @param policy - The policy the agent believes is in force (`pol_…`).
159
+ * @returns The complete envelope, ready to submit.
160
+ * @throws {ConfigError} As {@link settleDraft}.
161
+ */
162
+ export declare function pinIntent(draft: IntentDraft, policy: string): IntentEnvelope;
163
+ //# sourceMappingURL=intent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intent.d.ts","sourceRoot":"","sources":["../src/intent.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,MAAM,WAAW,WAAW;IAC1B,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IAEd,6EAA6E;IAC7E,WAAW,EAAE,MAAM,CAAC;IAEpB,0DAA0D;IAC1D,MAAM,EAAE,UAAU,CAAC;IAEnB;;;;;;;;;;;;OAYG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,iFAAiF;IACjF,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC;IAElB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAEpB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAEpB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,SAAS,CAAC;IAE1B;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC;IAExB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvF;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,CAoB5D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,cAAc,CA2B5E"}
package/dist/intent.js ADDED
@@ -0,0 +1,82 @@
1
+ /**
2
+ * An intent with everything but the policy pin.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import { ConfigError } from "./errors.js";
7
+ import { newTransactionId, v7MintedAtMs } from "./typeid.js";
8
+ /**
9
+ * Settles a draft's transaction id and mint time: a fresh UUID v7 id minted now when it
10
+ * has none, else the draft's `minted_at_ms`, else the time its UUID v7 id carries.
11
+ *
12
+ * Pure but for the mint. Call it once, at the top of a submission — never inside a
13
+ * retry loop.
14
+ *
15
+ * @throws {ConfigError} If the id is not a UUID v7 and the draft sets no
16
+ * `minted_at_ms`: the client never invents a mint time for an id it did not mint. Or
17
+ * if it sets `minted_at_ms` with no id to belong to.
18
+ */
19
+ export function settleDraft(draft) {
20
+ if (draft.transaction === undefined) {
21
+ if (draft.minted_at_ms !== undefined) {
22
+ throw new ConfigError("the draft sets minted_at_ms but no transaction: a mint time belongs to the id it " +
23
+ "was issued with, so set both, or neither and let the client mint the id");
24
+ }
25
+ const nowMs = Date.now();
26
+ return { ...draft, transaction: newTransactionId(nowMs), minted_at_ms: BigInt(nowMs) };
27
+ }
28
+ const idTime = v7MintedAtMs(draft.transaction);
29
+ const mintedAtMs = draft.minted_at_ms ?? (idTime === undefined ? undefined : BigInt(idTime));
30
+ if (mintedAtMs === undefined) {
31
+ throw new ConfigError(`transaction '${draft.transaction}' is not a UUID v7 id, so it carries no mint time: ` +
32
+ `set the draft's minted_at_ms to when it was first issued`);
33
+ }
34
+ return { ...draft, transaction: draft.transaction, minted_at_ms: mintedAtMs };
35
+ }
36
+ /**
37
+ * Stamps the policy pin and the id's mint time onto a draft, completing the envelope.
38
+ *
39
+ * **A pure function** — which is the whole reason the draft/envelope split is testable
40
+ * without a service running. Re-pinning the same draft changes the policy and *nothing
41
+ * else*: above all not the transaction id, which is the idempotency key that makes a
42
+ * retry safe rather than a double-post.
43
+ *
44
+ * If the draft has no transaction id, one is minted here; its mint time is settled as
45
+ * {@link settleDraft} does, so a re-pin of a settled draft keeps it. Prefer letting
46
+ * {@link AxorumClient.submitPinned} do the pinning — this is exported for callers who
47
+ * drive the pin themselves, and for the tests that hold the retry loop to account.
48
+ *
49
+ * @param draft - The intent, minus the pin.
50
+ * @param policy - The policy the agent believes is in force (`pol_…`).
51
+ * @returns The complete envelope, ready to submit.
52
+ * @throws {ConfigError} As {@link settleDraft}.
53
+ */
54
+ export function pinIntent(draft, policy) {
55
+ const settled = settleDraft(draft);
56
+ const targets = settled.fulfills;
57
+ if (targets !== undefined && (targets.length > 64 || new Set(targets).size !== targets.length ||
58
+ targets.some((id) => !/^obl_[0-7][0-9a-hjkmnp-tv-z]{25}$/.test(id)))) {
59
+ throw new TypeError("fulfills must contain at most 64 distinct obligation IDs");
60
+ }
61
+ return {
62
+ agent: settled.agent,
63
+ attestation: settled.attestation,
64
+ transaction: settled.transaction,
65
+ entries: settled.entries ?? [],
66
+ action: settled.action,
67
+ evidence: settled.evidence ?? [],
68
+ ...(settled.fulfills === undefined ? {} : { fulfills: [...settled.fulfills].sort() }),
69
+ justification: settled.justification ?? "",
70
+ policy,
71
+ minted_at_ms: settled.minted_at_ms,
72
+ // Spread rather than assign: an intent that carries no usage move omits the key
73
+ // **entirely**, and the contract is explicit that it is never `null`. A
74
+ // present-but-`undefined` key would serialize to nothing today, but it is not what the
75
+ // type says, and `exactOptionalPropertyTypes` is on precisely so the two cannot be
76
+ // confused.
77
+ ...(settled.consume_usage === undefined ? {} : { consume_usage: settled.consume_usage }),
78
+ ...(settled.meter_usage === undefined ? {} : { meter_usage: settled.meter_usage }),
79
+ ...(settled.bill_usage === undefined ? {} : { bill_usage: settled.bill_usage }),
80
+ };
81
+ }
82
+ //# sourceMappingURL=intent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intent.js","sourceRoot":"","sources":["../src/intent.ts"],"names":[],"mappings":"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"}
package/dist/json.d.ts ADDED
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Lossless JSON for a ledger — because `JSON.parse` is not one.
3
+ *
4
+ * Axorum denominates money in **128-bit integer minor units**, and puts them on the
5
+ * wire as bare JSON numbers (`{"minor": 50000, "currency": "USD"}`). The frozen
6
+ * contract names JavaScript explicitly on this point: a parser that decodes numbers
7
+ * as IEEE-754 doubles — `JSON.parse`, notably — silently loses precision on values
8
+ * above `Number.MAX_SAFE_INTEGER`. Silently mis-stating a balance is the one thing a
9
+ * financial-control client must never do, so this client does not use `JSON.parse`.
10
+ *
11
+ * The rule is one line, and it is the same in both directions:
12
+ *
13
+ * > **Every JSON integer is a `bigint`.** Every JSON number with a fraction or an
14
+ * > exponent is a `number`.
15
+ *
16
+ * So `minor`, `balance`, `anchor`, `deadline` and the `Int`/`Tick` literals all
17
+ * arrive as `bigint`, and the generated wire types say so. It is one rule to hold in
18
+ * your head instead of a table of which fields are safe.
19
+ *
20
+ * @packageDocumentation
21
+ */
22
+ /**
23
+ * Parses JSON, decoding every integer as a `bigint` so a 128-bit money value
24
+ * survives the trip.
25
+ *
26
+ * A drop-in replacement for `JSON.parse` in this client, and a pure function: same
27
+ * text, same value, no I/O.
28
+ *
29
+ * @param text - The JSON document.
30
+ * @returns The decoded value: objects, arrays, strings, booleans, `null`, `bigint`
31
+ * for integers, and `number` for fractional or exponent literals.
32
+ * @throws {SyntaxError} If `text` is not one well-formed JSON document.
33
+ */
34
+ export declare function parseJson(text: string): unknown;
35
+ /**
36
+ * Serializes to JSON, writing a `bigint` as a **bare integer literal** — the wire
37
+ * form the ledger reads — rather than as a quoted string (`JSON.stringify` refuses
38
+ * `bigint` outright).
39
+ *
40
+ * Mirrors `JSON.stringify` otherwise: `toJSON()` is honoured, `undefined` object
41
+ * values and function values are dropped, and `undefined` array elements become
42
+ * `null`.
43
+ *
44
+ * @param value - The value to serialize.
45
+ * @returns The JSON text.
46
+ * @throws {TypeError} On a circular structure.
47
+ */
48
+ export declare function stringifyJson(value: unknown): string;
49
+ //# sourceMappingURL=json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAsLH;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEpD"}
package/dist/json.js ADDED
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Lossless JSON for a ledger — because `JSON.parse` is not one.
3
+ *
4
+ * Axorum denominates money in **128-bit integer minor units**, and puts them on the
5
+ * wire as bare JSON numbers (`{"minor": 50000, "currency": "USD"}`). The frozen
6
+ * contract names JavaScript explicitly on this point: a parser that decodes numbers
7
+ * as IEEE-754 doubles — `JSON.parse`, notably — silently loses precision on values
8
+ * above `Number.MAX_SAFE_INTEGER`. Silently mis-stating a balance is the one thing a
9
+ * financial-control client must never do, so this client does not use `JSON.parse`.
10
+ *
11
+ * The rule is one line, and it is the same in both directions:
12
+ *
13
+ * > **Every JSON integer is a `bigint`.** Every JSON number with a fraction or an
14
+ * > exponent is a `number`.
15
+ *
16
+ * So `minor`, `balance`, `anchor`, `deadline` and the `Int`/`Tick` literals all
17
+ * arrive as `bigint`, and the generated wire types say so. It is one rule to hold in
18
+ * your head instead of a table of which fields are safe.
19
+ *
20
+ * @packageDocumentation
21
+ */
22
+ /** Character codes that end a bare JSON token. */
23
+ const STRUCTURAL = new Set([",", "]", "}", " ", "\t", "\n", "\r"]);
24
+ /** A hand-written recursive-descent JSON reader. See the module docs for why. */
25
+ class JsonReader {
26
+ text;
27
+ index = 0;
28
+ constructor(text) {
29
+ this.text = text;
30
+ }
31
+ /** Reads one complete JSON document, rejecting anything trailing it. */
32
+ read() {
33
+ const value = this.readValue();
34
+ this.skipWhitespace();
35
+ if (this.index < this.text.length) {
36
+ this.fail("unexpected trailing content");
37
+ }
38
+ return value;
39
+ }
40
+ readValue() {
41
+ this.skipWhitespace();
42
+ const char = this.text[this.index];
43
+ switch (char) {
44
+ case "{":
45
+ return this.readObject();
46
+ case "[":
47
+ return this.readArray();
48
+ case '"':
49
+ return this.readString();
50
+ case "t":
51
+ return this.readLiteral("true", true);
52
+ case "f":
53
+ return this.readLiteral("false", false);
54
+ case "n":
55
+ return this.readLiteral("null", null);
56
+ default:
57
+ if (char === "-" || (char !== undefined && char >= "0" && char <= "9")) {
58
+ return this.readNumber();
59
+ }
60
+ return this.fail(char === undefined ? "unexpected end of input" : `unexpected '${char}'`);
61
+ }
62
+ }
63
+ readObject() {
64
+ this.index += 1; // '{'
65
+ const object = {};
66
+ this.skipWhitespace();
67
+ if (this.text[this.index] === "}") {
68
+ this.index += 1;
69
+ return object;
70
+ }
71
+ for (;;) {
72
+ this.skipWhitespace();
73
+ if (this.text[this.index] !== '"') {
74
+ this.fail("expected a key");
75
+ }
76
+ const key = this.readString();
77
+ this.skipWhitespace();
78
+ if (this.text[this.index] !== ":") {
79
+ this.fail(`expected ':' after key '${key}'`);
80
+ }
81
+ this.index += 1;
82
+ const value = this.readValue();
83
+ // `defineProperty`, not assignment: a wire body carrying a `__proto__` key must
84
+ // become an ordinary own property, never a prototype swap. This is what
85
+ // `JSON.parse` does, and it is not optional for a body an attacker may shape.
86
+ Object.defineProperty(object, key, {
87
+ value,
88
+ writable: true,
89
+ enumerable: true,
90
+ configurable: true,
91
+ });
92
+ this.skipWhitespace();
93
+ const next = this.text[this.index];
94
+ if (next === ",") {
95
+ this.index += 1;
96
+ continue;
97
+ }
98
+ if (next === "}") {
99
+ this.index += 1;
100
+ return object;
101
+ }
102
+ this.fail("expected ',' or '}'");
103
+ }
104
+ }
105
+ readArray() {
106
+ this.index += 1; // '['
107
+ const array = [];
108
+ this.skipWhitespace();
109
+ if (this.text[this.index] === "]") {
110
+ this.index += 1;
111
+ return array;
112
+ }
113
+ for (;;) {
114
+ array.push(this.readValue());
115
+ this.skipWhitespace();
116
+ const next = this.text[this.index];
117
+ if (next === ",") {
118
+ this.index += 1;
119
+ continue;
120
+ }
121
+ if (next === "]") {
122
+ this.index += 1;
123
+ return array;
124
+ }
125
+ this.fail("expected ',' or ']'");
126
+ }
127
+ }
128
+ readString() {
129
+ const start = this.index;
130
+ this.index += 1; // opening quote
131
+ for (;;) {
132
+ const char = this.text[this.index];
133
+ if (char === undefined) {
134
+ this.fail("unterminated string");
135
+ }
136
+ if (char === '"') {
137
+ this.index += 1;
138
+ // The span is a valid JSON string literal by construction, so the platform
139
+ // decoder is both correct and faster than re-implementing escape handling.
140
+ return JSON.parse(this.text.slice(start, this.index));
141
+ }
142
+ // A backslash escapes the next character, including another backslash. Skipping
143
+ // two keeps `"\\"` from being read as an unterminated string.
144
+ this.index += char === "\\" ? 2 : 1;
145
+ }
146
+ }
147
+ /**
148
+ * Reads a JSON number.
149
+ *
150
+ * The whole point of this module: a literal with no fraction and no exponent is an
151
+ * **integer**, and becomes a `bigint` — exactly, at any magnitude. Anything else is
152
+ * a `number`.
153
+ */
154
+ readNumber() {
155
+ const start = this.index;
156
+ while (this.index < this.text.length) {
157
+ const char = this.text[this.index];
158
+ if (STRUCTURAL.has(char)) {
159
+ break;
160
+ }
161
+ this.index += 1;
162
+ }
163
+ const literal = this.text.slice(start, this.index);
164
+ if (/^-?(?:0|[1-9][0-9]*)$/.test(literal)) {
165
+ return BigInt(literal);
166
+ }
167
+ if (/^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][-+]?[0-9]+)?$/.test(literal)) {
168
+ return Number(literal);
169
+ }
170
+ return this.fail(`'${literal}' is not a number`);
171
+ }
172
+ readLiteral(literal, value) {
173
+ if (this.text.startsWith(literal, this.index)) {
174
+ this.index += literal.length;
175
+ return value;
176
+ }
177
+ return this.fail(`expected '${literal}'`);
178
+ }
179
+ skipWhitespace() {
180
+ while (this.index < this.text.length) {
181
+ const char = this.text[this.index];
182
+ if (char !== " " && char !== "\t" && char !== "\n" && char !== "\r") {
183
+ return;
184
+ }
185
+ this.index += 1;
186
+ }
187
+ }
188
+ fail(reason) {
189
+ throw new SyntaxError(`${reason} at position ${this.index}`);
190
+ }
191
+ }
192
+ /**
193
+ * Parses JSON, decoding every integer as a `bigint` so a 128-bit money value
194
+ * survives the trip.
195
+ *
196
+ * A drop-in replacement for `JSON.parse` in this client, and a pure function: same
197
+ * text, same value, no I/O.
198
+ *
199
+ * @param text - The JSON document.
200
+ * @returns The decoded value: objects, arrays, strings, booleans, `null`, `bigint`
201
+ * for integers, and `number` for fractional or exponent literals.
202
+ * @throws {SyntaxError} If `text` is not one well-formed JSON document.
203
+ */
204
+ export function parseJson(text) {
205
+ return new JsonReader(text).read();
206
+ }
207
+ /**
208
+ * Serializes to JSON, writing a `bigint` as a **bare integer literal** — the wire
209
+ * form the ledger reads — rather than as a quoted string (`JSON.stringify` refuses
210
+ * `bigint` outright).
211
+ *
212
+ * Mirrors `JSON.stringify` otherwise: `toJSON()` is honoured, `undefined` object
213
+ * values and function values are dropped, and `undefined` array elements become
214
+ * `null`.
215
+ *
216
+ * @param value - The value to serialize.
217
+ * @returns The JSON text.
218
+ * @throws {TypeError} On a circular structure.
219
+ */
220
+ export function stringifyJson(value) {
221
+ return write(value, new Set());
222
+ }
223
+ function write(value, seen) {
224
+ if (typeof value === "bigint") {
225
+ // The reason this module exists: a bare integer literal, at full precision.
226
+ return value.toString();
227
+ }
228
+ if (value === null) {
229
+ return "null";
230
+ }
231
+ if (typeof value === "object") {
232
+ const source = value;
233
+ if (typeof source.toJSON === "function") {
234
+ return write(source.toJSON(), seen);
235
+ }
236
+ if (seen.has(value)) {
237
+ throw new TypeError("cannot serialize a circular structure to JSON");
238
+ }
239
+ seen.add(value);
240
+ const json = Array.isArray(value)
241
+ ? `[${value.map((element) => writeOrNull(element, seen)).join(",")}]`
242
+ : `{${writeEntries(value, seen).join(",")}}`;
243
+ seen.delete(value);
244
+ return json;
245
+ }
246
+ // Strings, numbers, and booleans are already spelled correctly by the platform.
247
+ return JSON.stringify(value) ?? "null";
248
+ }
249
+ /** An array hole or an `undefined` element is `null`, as in `JSON.stringify`. */
250
+ function writeOrNull(value, seen) {
251
+ return value === undefined || typeof value === "function" ? "null" : write(value, seen);
252
+ }
253
+ /** An `undefined` or function-valued key is omitted, as in `JSON.stringify`. */
254
+ function writeEntries(object, seen) {
255
+ return Object.entries(object).flatMap(([key, value]) => value === undefined || typeof value === "function"
256
+ ? []
257
+ : [`${JSON.stringify(key)}:${write(value, seen)}`]);
258
+ }
259
+ //# sourceMappingURL=json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.js","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"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"}
package/dist/key.d.ts ADDED
@@ -0,0 +1,64 @@
1
+ /**
2
+ * A caller-chosen payment key, and the transaction id it stands for.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ /** The longest key accepted, in bytes. */
7
+ export declare const MAX_PAYMENT_KEY_BYTES = 255;
8
+ /**
9
+ * The namespace keyed transaction ids are UUID v5s under. Fixed forever: the Rust SDK
10
+ * derives the same id from the same key, and `spec/fixtures/authorize-key-v1.json` pins
11
+ * both.
12
+ */
13
+ export declare const AUTHORIZE_NAMESPACE = "107dbcd8-d7e9-4ddd-8e42-34da893200e4";
14
+ /**
15
+ * Why `key` is not an acceptable payment key, or `undefined`. Pure; the same rules
16
+ * as the Rust SDK's `PaymentKey::new`.
17
+ *
18
+ * One to {@link MAX_PAYMENT_KEY_BYTES} bytes of visible ASCII (`!` through `~`): no
19
+ * space, no control character, nothing a copy through a log or a shell could silently
20
+ * change.
21
+ */
22
+ export declare function paymentKeyProblem(key: string): string | undefined;
23
+ /**
24
+ * A payment key you chose, and when you first issued it.
25
+ *
26
+ * The key names the transaction; the issue time takes no part in the id. It tells the
27
+ * client how old the key is, because the service remembers a transaction id only for
28
+ * its advertised idempotency window, and a key re-sent after that could be applied as a
29
+ * new payment. So persist `issuedAtMs` with the key, and pass the same value every time
30
+ * you rebuild the payment from your records, never the time of the rebuild. A payment
31
+ * whose key is older than the window is reconciled by looking the transaction up, not
32
+ * re-driven.
33
+ *
34
+ * Build one with {@link paymentKey}.
35
+ */
36
+ export interface PaymentKey {
37
+ /** The key: 1 to {@link MAX_PAYMENT_KEY_BYTES} bytes of visible ASCII. */
38
+ readonly key: string;
39
+ /** When the key was first issued, in milliseconds since the Unix epoch. */
40
+ readonly issuedAtMs: number;
41
+ }
42
+ /**
43
+ * Validates a payment key and the time it was first issued. Pure; the same rules as the
44
+ * Rust SDK's `PaymentKey::new`.
45
+ *
46
+ * ```ts
47
+ * import { paymentKey } from "@axorum/client";
48
+ *
49
+ * // `order.approvedAtMs` is stored with the order, so a restart passes the same value.
50
+ * const key = paymentKey("order-8812", order.approvedAtMs);
51
+ * ```
52
+ *
53
+ * @throws {InvalidPaymentError} If the key is empty, over
54
+ * {@link MAX_PAYMENT_KEY_BYTES} bytes or not visible ASCII, or `issuedAtMs` is not a
55
+ * finite number.
56
+ */
57
+ export declare function paymentKey(key: string, issuedAtMs: number): PaymentKey;
58
+ /**
59
+ * The transaction id `agentUri` paying under `key` stands for:
60
+ * `UUIDv5(AUTHORIZE_NAMESPACE, agentUri || 0x1F || key)` as a `txn_` TypeID. Async only
61
+ * because it hashes with WebCrypto. The key must already be valid.
62
+ */
63
+ export declare function keyedTransaction(agentUri: string, key: string): Promise<string>;
64
+ //# sourceMappingURL=key.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key.d.ts","sourceRoot":"","sources":["../src/key.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,0CAA0C;AAC1C,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,yCAAyC,CAAC;AAE1E;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAYjE;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,UAAU;IACzB,0EAA0E;IAC1E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,UAAU,CAWtE;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAErF"}