@fora-protocol/sdk 1.0.3

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 (115) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +37 -0
  3. package/gen/ts/vocab/functiontokens.js +78 -0
  4. package/gen/ts/vocab/functiontokens.ts +84 -0
  5. package/gen/ts/vocab/geographytokens.js +30 -0
  6. package/gen/ts/vocab/geographytokens.ts +37 -0
  7. package/gen/ts/vocab/pricingunits.js +56 -0
  8. package/gen/ts/vocab/pricingunits.ts +63 -0
  9. package/gen/ts/vocab/quotametrics.js +40 -0
  10. package/gen/ts/vocab/quotametrics.ts +47 -0
  11. package/gen/ts/vocab/usertypes.js +40 -0
  12. package/gen/ts/vocab/usertypes.ts +46 -0
  13. package/gen/ts/wire/base.js +245 -0
  14. package/gen/ts/wire/base.ts +255 -0
  15. package/gen/ts/wire/names.js +41 -0
  16. package/gen/ts/wire/names.ts +42 -0
  17. package/gen/ts/wire/schemas.js +111 -0
  18. package/gen/ts/wire/schemas.ts +216 -0
  19. package/package.json +164 -0
  20. package/sdk/ts/client/content.d.ts +74 -0
  21. package/sdk/ts/client/content.js +466 -0
  22. package/sdk/ts/client/errors.d.ts +79 -0
  23. package/sdk/ts/client/errors.js +148 -0
  24. package/sdk/ts/client/index.d.ts +241 -0
  25. package/sdk/ts/client/index.js +740 -0
  26. package/sdk/ts/client/route.d.ts +41 -0
  27. package/sdk/ts/client/route.js +107 -0
  28. package/sdk/ts/client/send.d.ts +64 -0
  29. package/sdk/ts/client/send.js +142 -0
  30. package/sdk/ts/client/transport.d.ts +187 -0
  31. package/sdk/ts/client/transport.js +381 -0
  32. package/sdk/ts/core/multisig-parse.d.ts +47 -0
  33. package/sdk/ts/core/multisig-parse.js +234 -0
  34. package/sdk/ts/core/sign-request.d.ts +83 -0
  35. package/sdk/ts/core/sign-request.js +146 -0
  36. package/sdk/ts/core/sign.d.ts +31 -0
  37. package/sdk/ts/core/sign.js +82 -0
  38. package/sdk/ts/core/signing-transport.d.ts +75 -0
  39. package/sdk/ts/core/signing-transport.js +172 -0
  40. package/sdk/ts/core/verifier.d.ts +177 -0
  41. package/sdk/ts/core/verifier.js +269 -0
  42. package/sdk/ts/core/verify-multisig-request.d.ts +72 -0
  43. package/sdk/ts/core/verify-multisig-request.js +112 -0
  44. package/sdk/ts/core/verify-request.d.ts +181 -0
  45. package/sdk/ts/core/verify-request.js +284 -0
  46. package/sdk/ts/core/window.d.ts +22 -0
  47. package/sdk/ts/core/window.js +43 -0
  48. package/sdk/ts/core/wire-canon.d.ts +20 -0
  49. package/sdk/ts/core/wire-canon.js +174 -0
  50. package/sdk/ts/hono/middleware.d.ts +38 -0
  51. package/sdk/ts/hono/middleware.js +64 -0
  52. package/sdk/ts/resolvers/errors.d.ts +130 -0
  53. package/sdk/ts/resolvers/errors.js +150 -0
  54. package/sdk/ts/resolvers/http.d.ts +98 -0
  55. package/sdk/ts/resolvers/http.js +274 -0
  56. package/sdk/ts/resolvers/index.d.ts +9 -0
  57. package/sdk/ts/resolvers/index.js +15 -0
  58. package/sdk/ts/resolvers/jwks.d.ts +2 -0
  59. package/sdk/ts/resolvers/jwks.js +43 -0
  60. package/sdk/ts/resolvers/offer-key-cache.d.ts +68 -0
  61. package/sdk/ts/resolvers/offer-key-cache.js +152 -0
  62. package/sdk/ts/resolvers/registration-requirements.d.ts +47 -0
  63. package/sdk/ts/resolvers/registration-requirements.js +220 -0
  64. package/sdk/ts/resolvers/ssrf.d.ts +28 -0
  65. package/sdk/ts/resolvers/ssrf.js +235 -0
  66. package/sdk/ts/resolvers/static.d.ts +7 -0
  67. package/sdk/ts/resolvers/static.js +16 -0
  68. package/sdk/ts/resolvers/wba.d.ts +134 -0
  69. package/sdk/ts/resolvers/wba.js +506 -0
  70. package/sdk/ts/resolvers/wellknown.d.ts +28 -0
  71. package/sdk/ts/resolvers/wellknown.js +169 -0
  72. package/sdk/ts/src/acceptance.d.ts +46 -0
  73. package/sdk/ts/src/acceptance.js +162 -0
  74. package/sdk/ts/src/base64url.d.ts +19 -0
  75. package/sdk/ts/src/base64url.js +55 -0
  76. package/sdk/ts/src/crossfield.d.ts +28 -0
  77. package/sdk/ts/src/crossfield.js +215 -0
  78. package/sdk/ts/src/endpoint-rule.d.ts +30 -0
  79. package/sdk/ts/src/endpoint-rule.js +63 -0
  80. package/sdk/ts/src/errordetail.d.ts +115 -0
  81. package/sdk/ts/src/errordetail.js +256 -0
  82. package/sdk/ts/src/hashurl.d.ts +5 -0
  83. package/sdk/ts/src/hashurl.js +15 -0
  84. package/sdk/ts/src/host-ref.d.ts +70 -0
  85. package/sdk/ts/src/host-ref.js +320 -0
  86. package/sdk/ts/src/hosts.d.ts +138 -0
  87. package/sdk/ts/src/hosts.js +214 -0
  88. package/sdk/ts/src/idempotency.d.ts +11 -0
  89. package/sdk/ts/src/idempotency.js +27 -0
  90. package/sdk/ts/src/jsondepth.d.ts +27 -0
  91. package/sdk/ts/src/jsondepth.js +77 -0
  92. package/sdk/ts/src/licenseterm.d.ts +113 -0
  93. package/sdk/ts/src/licenseterm.js +433 -0
  94. package/sdk/ts/src/money.d.ts +21 -0
  95. package/sdk/ts/src/money.js +60 -0
  96. package/sdk/ts/src/offer-sign.d.ts +11 -0
  97. package/sdk/ts/src/offer-sign.js +31 -0
  98. package/sdk/ts/src/opaque-url.d.ts +3 -0
  99. package/sdk/ts/src/opaque-url.js +20 -0
  100. package/sdk/ts/src/pop.d.ts +51 -0
  101. package/sdk/ts/src/pop.js +166 -0
  102. package/sdk/ts/src/regschema.d.ts +229 -0
  103. package/sdk/ts/src/regschema.js +1315 -0
  104. package/sdk/ts/src/scopes.d.ts +20 -0
  105. package/sdk/ts/src/scopes.js +45 -0
  106. package/sdk/ts/src/signurl.d.ts +23 -0
  107. package/sdk/ts/src/signurl.js +107 -0
  108. package/sdk/ts/src/thumbprint.d.ts +14 -0
  109. package/sdk/ts/src/thumbprint.js +24 -0
  110. package/sdk/ts/src/verify.d.ts +49 -0
  111. package/sdk/ts/src/verify.js +109 -0
  112. package/sdk/ts/src/wire-names.d.ts +1 -0
  113. package/sdk/ts/src/wire-names.js +16 -0
  114. package/sdk/ts/src/wire.d.ts +68 -0
  115. package/sdk/ts/src/wire.js +114 -0
@@ -0,0 +1,166 @@
1
+ import { thumbprint } from "./thumbprint.js";
2
+ import { AgentKeyHeader } from "./wire.js";
3
+ import { decodeBase64Url, utf8Bytes } from "./base64url.js";
4
+ import { opaqueUrl } from "./opaque-url.js";
5
+ // Proof-of-possession verification for delivery-URL identity binding (ADR-013),
6
+ // relocated from the app edge (src/edge/src/pop.ts) as a pure L1 helper.
7
+ //
8
+ // When a signed URL carries an `agent_id` (the agent's RFC 7638 thumbprint), a
9
+ // code-capable edge requires the fetcher to prove possession of the bound key —
10
+ // fully offline:
11
+ //
12
+ // 1. present its raw Ed25519 public key in `X-FORA-Agent-Key`, and
13
+ // 2. sign the GET with RFC 9421 over `@method` + `@target-uri`,
14
+ //
15
+ // and the edge enforces the 3-way identity:
16
+ //
17
+ // agent_id (URL) == keyid (Signature-Input) == thumbprint(presented key)
18
+ //
19
+ // The Ed25519 verify primitive is INJECTABLE (input.verifyEd25519) so a runtime
20
+ // without WebCrypto Ed25519 (Fastly Compute) can supply its own — the byte
21
+ // contract is the signature base, not the primitive. The default primitive is
22
+ // WebCrypto crypto.subtle, which observes byte-identical output on the default
23
+ // path. Byte-parity guard: the pop vectors are produced by the sdk/go signer
24
+ // over the same @method/@target-uri base (sigbase.go buildSignatureBase).
25
+ export const AGENT_KEY_HEADER = AgentKeyHeader.toLowerCase();
26
+ const ED25519_PUBLIC_KEY_BYTES = 32;
27
+ const okResult = { ok: true };
28
+ const fail = (reason) => ({ ok: false, reason });
29
+ /**
30
+ * Verify the agent's proof of possession of the key bound to `agentId`.
31
+ * Returns `{ ok: true }` only when the presented key, the RFC 9421 signature,
32
+ * and the 3-way identity all check out.
33
+ */
34
+ export async function verifyAgentBinding(input) {
35
+ const presented = readPresentedKey(input.headers);
36
+ if (!presented.ok)
37
+ return presented.result;
38
+ const parsed = parseSignatureInput(input.headers.get("signature-input"));
39
+ if (!parsed)
40
+ return fail("malformed_sig_input");
41
+ // alg is an early reject only; the verify primitive is hard-pinned to Ed25519,
42
+ // so alg is never a key/algorithm SELECTION input (no downgrade possible).
43
+ if (parsed.alg.toLowerCase() !== "ed25519")
44
+ return fail("unsupported_alg");
45
+ if (!coversExactly(parsed.covered))
46
+ return fail("bad_covered_components");
47
+ const sigBytes = parseSignature(input.headers.get("signature"));
48
+ if (!sigBytes)
49
+ return fail("missing_sig");
50
+ // 3-way identity. keyid and the presented-key thumbprint must both equal the
51
+ // URL-bound agent_id before any signature work is trusted.
52
+ if (parsed.keyid !== input.agentId)
53
+ return fail("keyid_mismatch");
54
+ const presentedThumb = await thumbprint(presented.key);
55
+ if (presentedThumb !== input.agentId)
56
+ return fail("thumbprint_mismatch");
57
+ const stale = freshnessFailure(parsed, input.now);
58
+ if (stale)
59
+ return fail(stale);
60
+ // Coerce a URL-like input (Fastly hands a URL object, not a string) to its
61
+ // opaque string form once at the boundary. The @target-uri line must carry the
62
+ // verbatim bytes the agent signed, never a WHATWG-normalized toString(). This
63
+ // is behavior-neutral for the template literal on a string caller
64
+ // (String(s) === s); it makes the opaque-string contract explicit and matches
65
+ // the signed-URL verify boundary.
66
+ const url = opaqueUrl(input.url);
67
+ const base = signatureBase(input.method, url, parsed.rawParams);
68
+ const verify = input.verifyEd25519 ?? defaultVerifyEd25519;
69
+ const valid = await verify(presented.key, sigBytes, utf8Bytes(base));
70
+ return valid ? okResult : fail("pop_sig_invalid");
71
+ }
72
+ function readPresentedKey(headers) {
73
+ const raw = headers.get(AGENT_KEY_HEADER);
74
+ if (!raw)
75
+ return { ok: false, result: fail("missing_agent_key") };
76
+ const bytes = decodeBase64Url(raw);
77
+ if (!bytes || bytes.length !== ED25519_PUBLIC_KEY_BYTES) {
78
+ return { ok: false, result: fail("bad_agent_key") };
79
+ }
80
+ return { ok: true, key: bytes };
81
+ }
82
+ function coversExactly(covered) {
83
+ return covered.length === 2 && covered.includes("@method") && covered.includes("@target-uri");
84
+ }
85
+ // MAX_FUTURE_SKEW_SEC: a proof's created timestamp may not lead the verifier
86
+ // clock by more than this (mirrors the Go service-to-service verifier).
87
+ const MAX_FUTURE_SKEW_SEC = 300;
88
+ function freshnessFailure(parsed, now) {
89
+ const nowSec = Math.floor((now?.() ?? Date.now()) / 1000);
90
+ if (parsed.created === undefined)
91
+ return "pop_missing_created";
92
+ if (parsed.created > nowSec + MAX_FUTURE_SKEW_SEC)
93
+ return "pop_future_created";
94
+ if (parsed.expires === undefined)
95
+ return "pop_missing_exp";
96
+ if (nowSec >= parsed.expires)
97
+ return "pop_expired";
98
+ return undefined;
99
+ }
100
+ /**
101
+ * Rebuild the RFC 9421 signature base for the covered GET components. Order
102
+ * mirrors the inner list (`@method`, `@target-uri`), then `@signature-params`.
103
+ * MUST stay byte-identical to the sdk/go signer (sigbase.go buildSignatureBase).
104
+ */
105
+ export function signatureBase(method, url, rawParams) {
106
+ return [
107
+ `"@method": ${method.toUpperCase()}`,
108
+ `"@target-uri": ${url}`,
109
+ `"@signature-params": ${rawParams}`,
110
+ ].join("\n");
111
+ }
112
+ /** Parse `label=("@method" "@target-uri");keyid="..";alg="..";created=..;expires=..`. */
113
+ export function parseSignatureInput(raw) {
114
+ if (!raw)
115
+ return undefined;
116
+ const eq = raw.indexOf("=");
117
+ if (eq < 0)
118
+ return undefined;
119
+ const rawParams = raw.slice(eq + 1).trim();
120
+ const listMatch = rawParams.match(/^\(([^)]*)\)(.*)$/);
121
+ if (!listMatch)
122
+ return undefined;
123
+ const covered = [...(listMatch[1] ?? "").matchAll(/"([^"]+)"/g)].map((m) => m[1]);
124
+ const tail = listMatch[2] ?? "";
125
+ const keyid = matchParam(tail, /;keyid="([^"]*)"/);
126
+ const alg = matchParam(tail, /;alg="([^"]*)"/);
127
+ if (keyid === undefined || alg === undefined)
128
+ return undefined;
129
+ const created = matchInt(tail, /;created=(\d+)/);
130
+ const expires = matchInt(tail, /;expires=(\d+)/);
131
+ return {
132
+ covered,
133
+ rawParams,
134
+ keyid,
135
+ alg,
136
+ ...(created !== undefined ? { created } : {}),
137
+ ...(expires !== undefined ? { expires } : {}),
138
+ };
139
+ }
140
+ function matchParam(s, re) {
141
+ return s.match(re)?.[1];
142
+ }
143
+ function matchInt(s, re) {
144
+ const m = s.match(re);
145
+ return m ? Number(m[1]) : undefined;
146
+ }
147
+ /** Parse the RFC 9421 `Signature` header value `label=:<base64>:`. */
148
+ export function parseSignature(raw) {
149
+ if (!raw)
150
+ return undefined;
151
+ const m = raw.match(/:([^:]+):/);
152
+ if (!m || !m[1])
153
+ return undefined;
154
+ // The Signature byte string is standard base64; decodeBase64Url's url->std
155
+ // alphabet remap is a no-op on it, so the shared decoder handles it.
156
+ return decodeBase64Url(m[1]);
157
+ }
158
+ const defaultVerifyEd25519 = async (pubkey, sig, message) => {
159
+ try {
160
+ const key = await crypto.subtle.importKey("raw", pubkey, { name: "Ed25519" }, false, ["verify"]);
161
+ return await crypto.subtle.verify("Ed25519", key, sig, message);
162
+ }
163
+ catch {
164
+ return false;
165
+ }
166
+ };
@@ -0,0 +1,229 @@
1
+ import { type ValidateFunction } from "ajv/dist/2020.js";
2
+ import type { RegistrationFieldError as RegistrationFieldErrorShape } from "./errordetail.ts";
3
+ /**
4
+ * maxRegistrationSchemaBytes is the published schema's size cap, measured as the
5
+ * UTF-8 bytes of the data_schema member AS SERVED in fora.json — which is why the
6
+ * compile face takes raw bytes rather than a decoded document. A re-encoding is a
7
+ * different length than what the origin sent, and the cap is defined over what the
8
+ * origin sent.
9
+ */
10
+ export declare const maxRegistrationSchemaBytes = 16384;
11
+ /**
12
+ * maxRegistrationSchemaDepth bounds how deeply the schema document may nest. It
13
+ * counts JSON containers, so a bare `{}` is depth 1. Deep allOf/$ref chains are
14
+ * the cheapest way to make a compile expensive, and a real registration schema is
15
+ * three to five levels deep.
16
+ */
17
+ export declare const maxRegistrationSchemaDepth = 32;
18
+ /**
19
+ * maxRegistrationFieldErrors is the number of member failures a refusal may carry
20
+ * — the wire's own bound (RegistrationFailure.field_errors declares
21
+ * repeated.max_items = 64), restated so the validator never builds a list the
22
+ * contract would reject.
23
+ */
24
+ export declare const maxRegistrationFieldErrors = 64;
25
+ /** The wire bounds on RegistrationFieldError.path and .error. */
26
+ export declare const maxRegistrationFieldErrorPathLen = 255;
27
+ export declare const maxRegistrationFieldErrorTextLen = 255;
28
+ /**
29
+ * registrationSchemaDialect is the only $schema value a published data_schema may
30
+ * name. A document that names none is read as this dialect; one that names another
31
+ * is refused rather than validated under semantics its author did not intend.
32
+ */
33
+ export declare const registrationSchemaDialect = "https://json-schema.org/draft/2020-12/schema";
34
+ /**
35
+ * maxRegistrationSchemaEvaluations bounds the WORK of checking a payload, which the
36
+ * size and depth caps do not: `anyOf` branches multiply along a reference chain, so a
37
+ * schema can be small and shallow and still cost an unbounded amount to evaluate.
38
+ * Cost is linear in this count, so the bound is really a time bound expressed as a
39
+ * number a static walk can compute and a shared corpus can pin, which a stopwatch
40
+ * cannot.
41
+ */
42
+ export declare const maxRegistrationSchemaEvaluations = 10000;
43
+ /**
44
+ * maxRegistrationSchemaRefHops bounds how long a `$ref` chain may be, measured as the
45
+ * longest path of reference hops rather than as the number of references a document
46
+ * contains.
47
+ *
48
+ * A SEPARATE axis from maxRegistrationSchemaDepth, and the shape that forced it shows
49
+ * why: a chain of five hundred definitions, each referring to the next, is three JSON
50
+ * containers deep however long it is, so the depth cap never sees it. The evaluation cap
51
+ * does not see it either — a flat chain costs one evaluation per link.
52
+ *
53
+ * What it bounds is the RECURSION a validator does while resolving that chain. The cost
54
+ * walk here does not care, but the libraries the three SDKs hand an accepted schema to
55
+ * do, and one of them exhausted its interpreter stack at 495 links — raising out of a
56
+ * face documented as returning a verdict, on a document every SDK had just called valid.
57
+ */
58
+ export declare const maxRegistrationSchemaRefHops = 100;
59
+ /**
60
+ * SchemaVerdict is the outcome of compiling a published data_schema. The tokens
61
+ * are the Go `SchemaVerdict.String()` vocabulary verbatim, which is what the
62
+ * shared vectors record — a union of string literals rather than a numeric enum,
63
+ * matching the sibling AudienceVerdict.
64
+ *
65
+ * "no_verdict" is Go's zero value and is never returned here; it is in the
66
+ * vocabulary because the corpus carries the whole vocabulary.
67
+ */
68
+ export declare const schemaVerdicts: readonly ["no_verdict", "accepted", "malformed", "wrong_dialect", "remote_ref", "too_large", "too_deep", "unsafe_pattern", "too_complex", "ref_cycle", "ref_chain_too_long", "compile_timeout", "uncompilable", "not_published"];
69
+ export type SchemaVerdict = (typeof schemaVerdicts)[number];
70
+ export type { RegistrationFieldError } from "./errordetail.ts";
71
+ /** One failure before it is narrowed to the wire's two-field shape. */
72
+ export interface SchemaViolation extends RegistrationFieldErrorShape {
73
+ /**
74
+ * The failed keyword. The shared corpus pins THIS rather than the prose:
75
+ * `error` wording is validator-defined by contract, while the keyword is the
76
+ * same word in every JSON Schema library.
77
+ */
78
+ keyword: string;
79
+ }
80
+ /**
81
+ * The largest {n,m} bound admitted. RE2 refuses a repeat count over 1000 outright
82
+ * while the other two engines expand it, so a larger bound is a pattern one SDK
83
+ * compiles and another does not.
84
+ */
85
+ /**
86
+ * The largest {n,m} bound admitted. RE2 refuses a repeat count over 1000 outright while
87
+ * the other two engines expand it, so a larger bound is a pattern one SDK compiles and
88
+ * another does not. Exported so the parity suite can hold it against the corpus header:
89
+ * it and the escape alphabet are the two values that decide which patterns this port
90
+ * admits, and they were the two the suite did not check.
91
+ */
92
+ export declare const maxPortableRepeat = 1000;
93
+ /**
94
+ * isSafeSchemaPattern reports whether a `pattern` uses only constructs all three SDK
95
+ * languages express identically, and none that make a backtracking engine explode.
96
+ *
97
+ * Draft 2020-12 patterns are ECMA-262, and the three SDKs run three different engines
98
+ * over them. Two distinct failures follow, and this function answers only the first.
99
+ *
100
+ * Some constructs one engine cannot express at all — RE2 has no lookaround, this
101
+ * runtime has no inline flags — so no care at the call site reconciles them and they
102
+ * are refused here. Others every engine compiles and then reads DIFFERENTLY: `\d` is
103
+ * Unicode-aware in Python and ASCII here and in RE2, and Python's `$` also matches
104
+ * before a trailing newline. Those are not refused — they appear in almost every real
105
+ * pattern — they are corrected in the port that diverges, which is Python.
106
+ *
107
+ * The escape rule is an ALLOWLIST rather than a list of the divergent escapes, because
108
+ * the divergent set is open-ended: two successive reviews found new counterexamples by
109
+ * trying, which is the signature of a rule stated from the wrong side. The portable set
110
+ * is small, closed and checkable, and the corpus carries it as data.
111
+ *
112
+ * The last rule is about availability rather than agreement: a quantified group whose
113
+ * body can repeat or branch is refused, because that is what makes backtracking
114
+ * catastrophic and no timer here could stop one.
115
+ */
116
+ export declare function isSafeSchemaPattern(pattern: string): boolean;
117
+ /**
118
+ * A compiled, accepted data_schema. Immutable and safe to share, so a server
119
+ * compiles the operator's schema once at start-up and a client caches one per
120
+ * Exchange.
121
+ */
122
+ export declare class RegistrationSchema {
123
+ #private;
124
+ constructor(validate: ValidateFunction);
125
+ /**
126
+ * Check a registration_data payload and name what failed. An empty array means
127
+ * the payload conforms.
128
+ *
129
+ * The result is ready for the refusal builder: `path` an RFC 6901 pointer
130
+ * relative to registration_data ("" addresses the whole object), `error` the
131
+ * violated CONSTRAINT and never the submitted value.
132
+ *
133
+ * The order is deterministic — entries are deduplicated by pointer and keyword,
134
+ * then sorted by both, before the list is capped. Three validators walk a
135
+ * failing document in three different orders, so an unsorted list is one no
136
+ * shared corpus could pin.
137
+ */
138
+ validate(data: unknown): RegistrationFieldErrorShape[];
139
+ /**
140
+ * The whole answer, keyword included. `validate` narrows it to the wire's
141
+ * two-field shape; the parity suite reads it whole, because the corpus pins the
142
+ * keyword while `error` wording is validator-defined by contract.
143
+ */
144
+ violations(data: unknown): SchemaViolation[];
145
+ }
146
+ /**
147
+ * compileRegistrationSchema checks a published data_schema against every rule and
148
+ * compiles it.
149
+ *
150
+ * `raw` is the schema AS SERVED — the exact UTF-8 bytes of the data_schema member
151
+ * in fora.json — because maxRegistrationSchemaBytes is defined over those bytes.
152
+ *
153
+ * The schema is null unless the verdict is "accepted". Nothing throws: every way
154
+ * this can fail is a property of the schema, and both callers need to know WHICH.
155
+ * They read the same refusal differently, and that difference is the contract. A
156
+ * CLIENT pre-checking a payload treats any non-accepted verdict as "do not
157
+ * pre-check" and sends anyway — the Exchange's enforcement is the deciding one, and
158
+ * a client that refused here would block a payload the Exchange would have taken.
159
+ * An EXCHANGE compiling its OWN configured schema treats the same verdict as an
160
+ * operator misconfiguration, and must not advertise a schema it cannot enforce.
161
+ */
162
+ export declare function compileRegistrationSchema(raw: Uint8Array | string): {
163
+ schema: RegistrationSchema | null;
164
+ verdict: SchemaVerdict;
165
+ };
166
+ /**
167
+ * maxRegistrationDataBytes bounds a submitted registration_data payload, measured as
168
+ * its RFC 8785 canonical JSON encoding.
169
+ *
170
+ * The UNIT has to be named, and that is the whole point of this constant. Every other
171
+ * cap in this module is over bytes a party actually served; registration_data is not
172
+ * served as bytes at all — it arrives as a decoded google.protobuf.Struct — so "16KB"
173
+ * means nothing until an encoding is chosen, and two implementations choosing privately
174
+ * is the disagreement this module exists to remove. JCS is the choice because all three
175
+ * SDKs already compute it for the signing primitive, and because it pins number
176
+ * formatting: a payload carrying 1e300 is seven bytes to one renderer and three hundred
177
+ * to another.
178
+ *
179
+ * It bounds WORK, not storage. The schema's own caps bound the schema; nothing bounded
180
+ * the payload the schema is applied to, and validation cost is roughly the schema's cost
181
+ * multiplied by the elements in the payload — a subschema under `items` is counted once
182
+ * by maxRegistrationSchemaEvaluations and evaluated once per element.
183
+ */
184
+ export declare const maxRegistrationDataBytes = 16384;
185
+ /**
186
+ * maxRegistrationDataMembers bounds the number of members at the TOP LEVEL of a
187
+ * payload. Top level rather than recursive, deliberately: nested bulk is already bounded
188
+ * by the byte cap, and a recursive count would refuse a small document that merely
189
+ * nests, which a business entity legitimately does (an address is an object).
190
+ */
191
+ export declare const maxRegistrationDataMembers = 64;
192
+ /**
193
+ * maxRegistrationDataDepth bounds how deeply a submitted registration_data payload may
194
+ * nest, counting JSON containers so a bare `{}` is depth 1. Same number and same counting
195
+ * rule as maxRegistrationSchemaDepth, because it is the same question asked of the other
196
+ * document.
197
+ *
198
+ * It exists because without it the ANSWER depended on the reader's runtime rather than on
199
+ * the payload. Canonicalising walks the payload recursively, and where that walk runs out
200
+ * of stack differs by language and even by interpreter version — one port refused a
201
+ * payload past about five hundred containers on one Python and accepted nine hundred on
202
+ * the next, while this one and Go accepted every depth tried. A static bound checked
203
+ * first turns that into one verdict every implementation reaches.
204
+ */
205
+ export declare const maxRegistrationDataDepth = 32;
206
+ /**
207
+ * RegistrationDataVerdict is the outcome of checking a submitted registration_data
208
+ * payload. The tokens are the Go oracle's vocabulary verbatim.
209
+ *
210
+ * "no_verdict" is Go's zero value and is never returned here; it is in the vocabulary
211
+ * because the corpus carries the whole vocabulary.
212
+ */
213
+ export declare const registrationDataVerdicts: readonly ["no_verdict", "accepted", "too_large", "too_many_members", "too_deep", "uncanonicalizable"];
214
+ export type RegistrationDataVerdict = (typeof registrationDataVerdicts)[number];
215
+ /**
216
+ * checkRegistrationData bounds a submitted registration_data payload.
217
+ *
218
+ * `data` is the decoded object. A null/undefined or empty payload is accepted: sending
219
+ * no business data is a matter for the published schema's `required` list, not for a
220
+ * size bound.
221
+ *
222
+ * This runs BEFORE RegistrationSchema.validate, for the same reason the schema's own
223
+ * size cap runs before the schema is parsed: the bound exists to stop work, so it has to
224
+ * precede the work. An Exchange refuses an over-bound payload outright — a malformed
225
+ * request rather than a schema failure, so NOT
226
+ * REGISTRATION_FAILURE_REASON_INVALID_REGISTRATION_DATA, which names non-conformance to
227
+ * a published schema and applies only when one is published.
228
+ */
229
+ export declare function checkRegistrationData(data: Record<string, unknown> | null | undefined): RegistrationDataVerdict;