@receiz/sdk 94.0.0 → 96.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.
- package/README.md +53 -4
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +238 -0
- package/dist/index.d.ts +236 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +383 -6
- package/docs/cli-and-conformance.md +108 -0
- package/docs/copy-paste-integrations.md +205 -0
- package/docs/proof-memory-and-projections.md +42 -6
- package/fixtures/receiz-asset-manifest.example.json +6 -3
- package/fixtures/receiz-webhook-event.example.json +10 -1
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { buildReceizIdContinueRequest, createReceizIdIdentity, projectReceizIdentityAccount, readReceizIdentityArtifact, signReceizIdentityLoginProof, verifyReceizIdentityLoginProof, } from "./identity.js";
|
|
2
2
|
export * from "./identity.js";
|
|
3
|
-
export const RECEIZ_SDK_VERSION = "
|
|
3
|
+
export const RECEIZ_SDK_VERSION = "96.0.0";
|
|
4
4
|
export const RECEIZ_DEFAULT_BASE_URL = "https://receiz.com";
|
|
5
5
|
export const RECEIZ_WEBHOOK_SIGNATURE_HEADER = "x-receiz-signature";
|
|
6
6
|
export const RECEIZ_WEBHOOK_TIMESTAMP_HEADER = "x-receiz-timestamp";
|
|
@@ -19,12 +19,34 @@ export const RECEIZ_ASSET_MANIFEST_SCHEMA = {
|
|
|
19
19
|
},
|
|
20
20
|
proof: {
|
|
21
21
|
type: "object",
|
|
22
|
-
required: [
|
|
22
|
+
required: [
|
|
23
|
+
"kind",
|
|
24
|
+
"payloadVersion",
|
|
25
|
+
"createdAtMs",
|
|
26
|
+
"ts",
|
|
27
|
+
"code",
|
|
28
|
+
"slug",
|
|
29
|
+
"verifyPath",
|
|
30
|
+
"verifyUrl",
|
|
31
|
+
"kaiPulseEternal",
|
|
32
|
+
"kaiKlok",
|
|
33
|
+
"receizClaimId",
|
|
34
|
+
"sigilClaimSeed",
|
|
35
|
+
],
|
|
23
36
|
additionalProperties: true,
|
|
24
37
|
properties: {
|
|
25
|
-
kind: {
|
|
38
|
+
kind: { const: "receiz.proof_bundle" },
|
|
39
|
+
payloadVersion: { enum: ["v2", "v1"] },
|
|
40
|
+
createdAtMs: { type: "number" },
|
|
41
|
+
ts: { type: "string", minLength: 1 },
|
|
42
|
+
code: { type: "string", minLength: 1 },
|
|
43
|
+
slug: { type: "string", minLength: 1 },
|
|
44
|
+
verifyPath: { type: "string", minLength: 1 },
|
|
26
45
|
verifyUrl: { type: "string" },
|
|
27
|
-
kaiPulseEternal: { type:
|
|
46
|
+
kaiPulseEternal: { type: "string", minLength: 1 },
|
|
47
|
+
kaiKlok: { type: "string", minLength: 1 },
|
|
48
|
+
receizClaimId: { type: "string", minLength: 1 },
|
|
49
|
+
sigilClaimSeed: { type: "string", minLength: 1 },
|
|
28
50
|
},
|
|
29
51
|
},
|
|
30
52
|
owner: { type: ["object", "null"], additionalProperties: true },
|
|
@@ -208,6 +230,79 @@ function failIfIssues(label, issues) {
|
|
|
208
230
|
if (issues.length > 0)
|
|
209
231
|
throw new ReceizValidationError(label, issues);
|
|
210
232
|
}
|
|
233
|
+
function asSdkHex(value, length) {
|
|
234
|
+
if (typeof value !== "string")
|
|
235
|
+
return null;
|
|
236
|
+
const normalized = value.trim().toLowerCase();
|
|
237
|
+
const pattern = new RegExp(`^[0-9a-f]{${length}}$`);
|
|
238
|
+
return pattern.test(normalized) ? normalized : null;
|
|
239
|
+
}
|
|
240
|
+
function validateOptionalSdkHex(record, key, length, issues, path) {
|
|
241
|
+
if (record[key] === undefined)
|
|
242
|
+
return;
|
|
243
|
+
if (!asSdkHex(record[key], length))
|
|
244
|
+
issues.push(`${path}.${key} must be ${length} lowercase hex characters`);
|
|
245
|
+
}
|
|
246
|
+
function validateReceizProofBundleShape(value, path, issues) {
|
|
247
|
+
if (!isRecord(value)) {
|
|
248
|
+
issues.push(`${path} must be an object`);
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
if (value.kind !== "receiz.proof_bundle")
|
|
252
|
+
issues.push(`${path}.kind must be receiz.proof_bundle`);
|
|
253
|
+
if (value.payloadVersion !== "v2" && value.payloadVersion !== "v1") {
|
|
254
|
+
issues.push(`${path}.payloadVersion must be v2 or v1`);
|
|
255
|
+
}
|
|
256
|
+
const createdAtMs = value.createdAtMs;
|
|
257
|
+
if (typeof createdAtMs !== "number" || !Number.isFinite(createdAtMs)) {
|
|
258
|
+
issues.push(`${path}.createdAtMs must be a finite number`);
|
|
259
|
+
}
|
|
260
|
+
for (const key of ["ts", "code", "slug", "verifyPath", "kaiPulseEternal", "kaiKlok", "receizClaimId", "sigilClaimSeed"]) {
|
|
261
|
+
const fieldValue = value[key];
|
|
262
|
+
if (typeof fieldValue !== "string" || !fieldValue.trim())
|
|
263
|
+
issues.push(`${path}.${key} must be a non-empty string`);
|
|
264
|
+
}
|
|
265
|
+
if (typeof value.verifyUrl !== "string")
|
|
266
|
+
issues.push(`${path}.verifyUrl must be a string`);
|
|
267
|
+
if (value.tsDisplay !== undefined && (typeof value.tsDisplay !== "string" || !value.tsDisplay.trim())) {
|
|
268
|
+
issues.push(`${path}.tsDisplay must be a non-empty string when present`);
|
|
269
|
+
}
|
|
270
|
+
if (value.tzMinutesEast !== undefined && (typeof value.tzMinutesEast !== "number" || !Number.isFinite(value.tzMinutesEast))) {
|
|
271
|
+
issues.push(`${path}.tzMinutesEast must be a finite number when present`);
|
|
272
|
+
}
|
|
273
|
+
validateOptionalSdkHex(value, "signerKeyId", 12, issues, path);
|
|
274
|
+
validateOptionalSdkHex(value, "anchorId", 8, issues, path);
|
|
275
|
+
validateOptionalSdkHex(value, "groth16ProofDigest", 64, issues, path);
|
|
276
|
+
validateOptionalSdkHex(value, "artifactSha256Basis", 64, issues, path);
|
|
277
|
+
if (value.zkPoseidonHash !== undefined && (typeof value.zkPoseidonHash !== "string" || !value.zkPoseidonHash.trim())) {
|
|
278
|
+
issues.push(`${path}.zkPoseidonHash must be a non-empty string when present`);
|
|
279
|
+
}
|
|
280
|
+
if (value.signatureV3 !== undefined && !isRecord(value.signatureV3)) {
|
|
281
|
+
issues.push(`${path}.signatureV3 must be an object when present`);
|
|
282
|
+
}
|
|
283
|
+
if (value.signatureV4 !== undefined && !isRecord(value.signatureV4)) {
|
|
284
|
+
issues.push(`${path}.signatureV4 must be an object when present`);
|
|
285
|
+
}
|
|
286
|
+
if (value.wireproof !== undefined) {
|
|
287
|
+
const wireproof = value.wireproof;
|
|
288
|
+
if (!isRecord(wireproof)) {
|
|
289
|
+
issues.push(`${path}.wireproof must be an object when present`);
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
if (wireproof.schema !== "wireproof.v1")
|
|
293
|
+
issues.push(`${path}.wireproof.schema must be wireproof.v1`);
|
|
294
|
+
if (!asSdkHex(wireproof.anchorId, 8))
|
|
295
|
+
issues.push(`${path}.wireproof.anchorId must be 8 lowercase hex characters`);
|
|
296
|
+
if (!asSdkHex(wireproof.claimHashSha256, 64)) {
|
|
297
|
+
issues.push(`${path}.wireproof.claimHashSha256 must be 64 lowercase hex characters`);
|
|
298
|
+
}
|
|
299
|
+
if (typeof wireproof.verifierPath !== "string" || !wireproof.verifierPath.trim()) {
|
|
300
|
+
issues.push(`${path}.wireproof.verifierPath must be a non-empty string`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return value;
|
|
305
|
+
}
|
|
211
306
|
const WEBHOOK_EVENT_TYPES = new Set([
|
|
212
307
|
"asset.created",
|
|
213
308
|
"asset.transferred",
|
|
@@ -330,6 +425,11 @@ export class ReceizClient {
|
|
|
330
425
|
};
|
|
331
426
|
proofMemory = {
|
|
332
427
|
createRegister: createReceizProofRegister,
|
|
428
|
+
createMemory: createReceizProofMemory,
|
|
429
|
+
createInMemoryStorage: createReceizInMemoryProofMemoryStorage,
|
|
430
|
+
createLocalStorage: createReceizLocalStorageProofMemoryStorage,
|
|
431
|
+
assertSnapshot: assertReceizProofRegisterSnapshot,
|
|
432
|
+
additionsQuery: receizProofMemoryAdditionsQuery,
|
|
333
433
|
};
|
|
334
434
|
constructor(options = {}) {
|
|
335
435
|
this.baseUrl = trimTrailingSlash(options.baseUrl ?? RECEIZ_DEFAULT_BASE_URL);
|
|
@@ -441,6 +541,21 @@ export async function loadReceizOpenApi(baseUrl = RECEIZ_DEFAULT_BASE_URL, fetch
|
|
|
441
541
|
throw new ReceizHttpError(response.status, await readPayload(response));
|
|
442
542
|
return response.json();
|
|
443
543
|
}
|
|
544
|
+
export function assertReceizProofBundle(value) {
|
|
545
|
+
const issues = [];
|
|
546
|
+
const proof = validateReceizProofBundleShape(value, "proof", issues);
|
|
547
|
+
failIfIssues("ReceizProofBundle", issues);
|
|
548
|
+
return proof;
|
|
549
|
+
}
|
|
550
|
+
export function isReceizProofBundle(value) {
|
|
551
|
+
try {
|
|
552
|
+
assertReceizProofBundle(value);
|
|
553
|
+
return true;
|
|
554
|
+
}
|
|
555
|
+
catch {
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
444
559
|
export function assertReceizAssetManifest(value) {
|
|
445
560
|
const record = ensureRecord(value, "ReceizAssetManifest");
|
|
446
561
|
const issues = [];
|
|
@@ -449,8 +564,7 @@ export function assertReceizAssetManifest(value) {
|
|
|
449
564
|
ensureString(record, "assetId", issues);
|
|
450
565
|
ensureString(record, "assetType", issues);
|
|
451
566
|
const proof = record.proof;
|
|
452
|
-
|
|
453
|
-
issues.push("proof.kind must be a non-empty string");
|
|
567
|
+
validateReceizProofBundleShape(proof, "proof", issues);
|
|
454
568
|
ensureOptionalRecord(record, "owner", issues);
|
|
455
569
|
ensureOptionalRecord(record, "media", issues);
|
|
456
570
|
ensureOptionalRecord(record, "settlement", issues);
|
|
@@ -754,6 +868,67 @@ function normalizeRegisterEntry(value) {
|
|
|
754
868
|
projection: isRecord(projection) ? projection : null,
|
|
755
869
|
};
|
|
756
870
|
}
|
|
871
|
+
export function assertReceizProofRegisterSnapshot(value) {
|
|
872
|
+
const record = ensureRecord(value, "ReceizProofRegisterSnapshot");
|
|
873
|
+
const issues = [];
|
|
874
|
+
if (record.schema !== "receiz.sdk.proof_register.v1")
|
|
875
|
+
issues.push("schema must be receiz.sdk.proof_register.v1");
|
|
876
|
+
const ownerId = record.ownerId;
|
|
877
|
+
if (ownerId !== null && ownerId !== undefined && typeof ownerId !== "string") {
|
|
878
|
+
issues.push("ownerId must be a string or null when present");
|
|
879
|
+
}
|
|
880
|
+
const createdAt = ensureString(record, "createdAt", issues);
|
|
881
|
+
const updatedAt = ensureString(record, "updatedAt", issues);
|
|
882
|
+
const head = record.head;
|
|
883
|
+
if (!isRecord(head)) {
|
|
884
|
+
issues.push("head must be an object");
|
|
885
|
+
}
|
|
886
|
+
else {
|
|
887
|
+
const entryId = head.entryId;
|
|
888
|
+
if (entryId !== null && entryId !== undefined && typeof entryId !== "string") {
|
|
889
|
+
issues.push("head.entryId must be a string or null");
|
|
890
|
+
}
|
|
891
|
+
const kaiUpulse = head.kaiUpulse;
|
|
892
|
+
if (kaiUpulse !== null && kaiUpulse !== undefined && typeof kaiUpulse !== "string" && typeof kaiUpulse !== "number") {
|
|
893
|
+
issues.push("head.kaiUpulse must be a string, number, or null");
|
|
894
|
+
}
|
|
895
|
+
const headCreatedAt = head.createdAt;
|
|
896
|
+
if (headCreatedAt !== null && headCreatedAt !== undefined && typeof headCreatedAt !== "string") {
|
|
897
|
+
issues.push("head.createdAt must be a string or null");
|
|
898
|
+
}
|
|
899
|
+
if (typeof head.count !== "number" || !Number.isInteger(head.count) || head.count < 0) {
|
|
900
|
+
issues.push("head.count must be a non-negative integer");
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
if (!Array.isArray(record.entries))
|
|
904
|
+
issues.push("entries must be an array");
|
|
905
|
+
failIfIssues("ReceizProofRegisterSnapshot", issues);
|
|
906
|
+
const entries = record.entries.map((entry) => normalizeRegisterEntry(entry));
|
|
907
|
+
return {
|
|
908
|
+
schema: "receiz.sdk.proof_register.v1",
|
|
909
|
+
ownerId: typeof ownerId === "string" || ownerId === null ? ownerId : null,
|
|
910
|
+
createdAt: createdAt ?? "",
|
|
911
|
+
updatedAt: updatedAt ?? "",
|
|
912
|
+
head: {
|
|
913
|
+
entryId: isRecord(head) && typeof head.entryId === "string" ? head.entryId : null,
|
|
914
|
+
kaiUpulse: isRecord(head) && (typeof head.kaiUpulse === "string" || typeof head.kaiUpulse === "number") ? head.kaiUpulse : null,
|
|
915
|
+
createdAt: isRecord(head) && typeof head.createdAt === "string" ? head.createdAt : null,
|
|
916
|
+
count: isRecord(head) && typeof head.count === "number" ? head.count : entries.length,
|
|
917
|
+
},
|
|
918
|
+
entries,
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
function parseProofMemoryStorageValue(value) {
|
|
922
|
+
if (value === null || value === undefined)
|
|
923
|
+
return null;
|
|
924
|
+
if (typeof value === "string") {
|
|
925
|
+
const trimmed = value.trim();
|
|
926
|
+
if (!trimmed)
|
|
927
|
+
return null;
|
|
928
|
+
return assertReceizProofRegisterSnapshot(JSON.parse(trimmed));
|
|
929
|
+
}
|
|
930
|
+
return assertReceizProofRegisterSnapshot(value);
|
|
931
|
+
}
|
|
757
932
|
function mergeRegisterEntry(existing, incoming) {
|
|
758
933
|
return {
|
|
759
934
|
id: existing.id,
|
|
@@ -813,6 +988,11 @@ function registerEntryFromWebhookEvent(event) {
|
|
|
813
988
|
},
|
|
814
989
|
};
|
|
815
990
|
}
|
|
991
|
+
function errorDetail(error) {
|
|
992
|
+
if (error instanceof Error)
|
|
993
|
+
return { name: error.name, message: error.message };
|
|
994
|
+
return { message: String(error) };
|
|
995
|
+
}
|
|
816
996
|
export class ReceizProofRegister {
|
|
817
997
|
ownerId;
|
|
818
998
|
createdAt;
|
|
@@ -829,6 +1009,16 @@ export class ReceizProofRegister {
|
|
|
829
1009
|
this.entriesById.set(normalized.id, existing ? mergeRegisterEntry(existing, normalized) : normalized);
|
|
830
1010
|
return this;
|
|
831
1011
|
}
|
|
1012
|
+
admit(value) {
|
|
1013
|
+
if (isReceizAssetManifest(value))
|
|
1014
|
+
return this.admitAssetManifest(value);
|
|
1015
|
+
if (isReceizSportsCardManifest(value))
|
|
1016
|
+
return this.admitSportsCardManifest(value);
|
|
1017
|
+
if (isReceizWebhookEvent(value))
|
|
1018
|
+
return this.admitWebhookEvent(value);
|
|
1019
|
+
const schema = isRecord(value) && typeof value.schema === "string" ? value.schema : "unknown";
|
|
1020
|
+
throw new ReceizValidationError("ReceizProofAdmission", [`unsupported proof object schema ${schema}`]);
|
|
1021
|
+
}
|
|
832
1022
|
admitAssetManifest(value) {
|
|
833
1023
|
return this.append(registerEntryFromAssetManifest(assertReceizAssetManifest(value)));
|
|
834
1024
|
}
|
|
@@ -868,6 +1058,193 @@ export class ReceizProofRegister {
|
|
|
868
1058
|
export function createReceizProofRegister(input = {}) {
|
|
869
1059
|
return new ReceizProofRegister(input);
|
|
870
1060
|
}
|
|
1061
|
+
export class ReceizProofMemory {
|
|
1062
|
+
register;
|
|
1063
|
+
storage;
|
|
1064
|
+
autoPersist;
|
|
1065
|
+
onPersistError;
|
|
1066
|
+
onObservation;
|
|
1067
|
+
persistChain = Promise.resolve();
|
|
1068
|
+
constructor(register, options = {}) {
|
|
1069
|
+
this.register = register;
|
|
1070
|
+
this.storage = options.storage ?? null;
|
|
1071
|
+
this.autoPersist = options.autoPersist ?? true;
|
|
1072
|
+
this.onPersistError = options.onPersistError ?? null;
|
|
1073
|
+
this.onObservation = options.onObservation ?? null;
|
|
1074
|
+
}
|
|
1075
|
+
append(entry) {
|
|
1076
|
+
const startedAtMs = Date.now();
|
|
1077
|
+
try {
|
|
1078
|
+
this.register.append(entry);
|
|
1079
|
+
this.observe("proof_memory.append", startedAtMs, "ok");
|
|
1080
|
+
return this.schedulePersist();
|
|
1081
|
+
}
|
|
1082
|
+
catch (error) {
|
|
1083
|
+
this.observe("proof_memory.append", startedAtMs, "error", errorDetail(error));
|
|
1084
|
+
throw error;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
admit(value) {
|
|
1088
|
+
const startedAtMs = Date.now();
|
|
1089
|
+
try {
|
|
1090
|
+
this.register.admit(value);
|
|
1091
|
+
this.observe("proof_memory.admit", startedAtMs, "ok");
|
|
1092
|
+
return this.schedulePersist();
|
|
1093
|
+
}
|
|
1094
|
+
catch (error) {
|
|
1095
|
+
this.observe("proof_memory.admit", startedAtMs, "error", errorDetail(error));
|
|
1096
|
+
throw error;
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
admitAssetManifest(value) {
|
|
1100
|
+
return this.admit(value);
|
|
1101
|
+
}
|
|
1102
|
+
admitSportsCardManifest(value) {
|
|
1103
|
+
return this.admit(value);
|
|
1104
|
+
}
|
|
1105
|
+
admitWebhookEvent(value) {
|
|
1106
|
+
return this.admit(value);
|
|
1107
|
+
}
|
|
1108
|
+
has(entryId) {
|
|
1109
|
+
return this.register.has(entryId);
|
|
1110
|
+
}
|
|
1111
|
+
entries() {
|
|
1112
|
+
return this.register.entries();
|
|
1113
|
+
}
|
|
1114
|
+
snapshot() {
|
|
1115
|
+
return this.register.snapshot();
|
|
1116
|
+
}
|
|
1117
|
+
knownHead(limit) {
|
|
1118
|
+
return receizProofMemoryAdditionsQuery(this.snapshot(), limit);
|
|
1119
|
+
}
|
|
1120
|
+
async persist() {
|
|
1121
|
+
const startedAtMs = Date.now();
|
|
1122
|
+
try {
|
|
1123
|
+
const snapshot = this.snapshot();
|
|
1124
|
+
if (this.storage)
|
|
1125
|
+
await this.storage.write(snapshot);
|
|
1126
|
+
this.observe("proof_memory.persist", startedAtMs, "ok", { persisted: Boolean(this.storage) });
|
|
1127
|
+
return snapshot;
|
|
1128
|
+
}
|
|
1129
|
+
catch (error) {
|
|
1130
|
+
this.observe("proof_memory.persist", startedAtMs, "error", errorDetail(error));
|
|
1131
|
+
throw error;
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
async flush() {
|
|
1135
|
+
await this.persistChain;
|
|
1136
|
+
}
|
|
1137
|
+
async clear() {
|
|
1138
|
+
const startedAtMs = Date.now();
|
|
1139
|
+
try {
|
|
1140
|
+
if (this.storage?.remove)
|
|
1141
|
+
await this.storage.remove();
|
|
1142
|
+
this.observe("proof_memory.clear", startedAtMs, "ok", { removed: Boolean(this.storage?.remove) });
|
|
1143
|
+
}
|
|
1144
|
+
catch (error) {
|
|
1145
|
+
this.observe("proof_memory.clear", startedAtMs, "error", errorDetail(error));
|
|
1146
|
+
throw error;
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
toJSON() {
|
|
1150
|
+
return this.snapshot();
|
|
1151
|
+
}
|
|
1152
|
+
schedulePersist() {
|
|
1153
|
+
if (!this.autoPersist || !this.storage)
|
|
1154
|
+
return this;
|
|
1155
|
+
this.persistChain = this.persistChain
|
|
1156
|
+
.then(async () => {
|
|
1157
|
+
await this.persist();
|
|
1158
|
+
})
|
|
1159
|
+
.catch((error) => {
|
|
1160
|
+
this.onPersistError?.(error);
|
|
1161
|
+
});
|
|
1162
|
+
return this;
|
|
1163
|
+
}
|
|
1164
|
+
observe(operation, startedAtMs, status, detail) {
|
|
1165
|
+
if (!this.onObservation)
|
|
1166
|
+
return;
|
|
1167
|
+
const endedAtMs = Date.now();
|
|
1168
|
+
const snapshot = this.snapshot();
|
|
1169
|
+
const observation = {
|
|
1170
|
+
schema: "receiz.sdk.observation.v1",
|
|
1171
|
+
operation,
|
|
1172
|
+
status,
|
|
1173
|
+
startedAt: new Date(startedAtMs).toISOString(),
|
|
1174
|
+
endedAt: new Date(endedAtMs).toISOString(),
|
|
1175
|
+
durationMs: Math.max(0, endedAtMs - startedAtMs),
|
|
1176
|
+
ownerId: this.register.ownerId,
|
|
1177
|
+
entryCount: snapshot.head.count,
|
|
1178
|
+
headEntryId: snapshot.head.entryId,
|
|
1179
|
+
headKaiUpulse: snapshot.head.kaiUpulse,
|
|
1180
|
+
};
|
|
1181
|
+
if (detail)
|
|
1182
|
+
observation.detail = detail;
|
|
1183
|
+
try {
|
|
1184
|
+
this.onObservation(observation);
|
|
1185
|
+
}
|
|
1186
|
+
catch {
|
|
1187
|
+
// Observation callbacks are passive evidence only; they must never affect proof admission.
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
export async function createReceizProofMemory(options = {}) {
|
|
1192
|
+
const storedSnapshot = options.storage ? parseProofMemoryStorageValue(await options.storage.read()) : null;
|
|
1193
|
+
const input = storedSnapshot
|
|
1194
|
+
? {
|
|
1195
|
+
...storedSnapshot,
|
|
1196
|
+
ownerId: options.ownerId ?? storedSnapshot.ownerId ?? null,
|
|
1197
|
+
}
|
|
1198
|
+
: {
|
|
1199
|
+
ownerId: options.ownerId ?? null,
|
|
1200
|
+
...(options.createdAt === undefined ? {} : { createdAt: options.createdAt }),
|
|
1201
|
+
...(options.entries === undefined ? {} : { entries: options.entries }),
|
|
1202
|
+
};
|
|
1203
|
+
return new ReceizProofMemory(createReceizProofRegister(input), options);
|
|
1204
|
+
}
|
|
1205
|
+
export function createReceizInMemoryProofMemoryStorage(initialValue = null) {
|
|
1206
|
+
let storedText = typeof initialValue === "string"
|
|
1207
|
+
? initialValue
|
|
1208
|
+
: initialValue
|
|
1209
|
+
? JSON.stringify(assertReceizProofRegisterSnapshot(initialValue))
|
|
1210
|
+
: null;
|
|
1211
|
+
return {
|
|
1212
|
+
read: () => storedText,
|
|
1213
|
+
write: (snapshot) => {
|
|
1214
|
+
storedText = JSON.stringify(assertReceizProofRegisterSnapshot(snapshot));
|
|
1215
|
+
},
|
|
1216
|
+
remove: () => {
|
|
1217
|
+
storedText = null;
|
|
1218
|
+
},
|
|
1219
|
+
readText: () => storedText,
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1222
|
+
export function createReceizLocalStorageProofMemoryStorage(key = "receiz:proof-memory:v1", storage = globalThis.localStorage) {
|
|
1223
|
+
if (!storage)
|
|
1224
|
+
throw new Error("receiz_local_storage_unavailable");
|
|
1225
|
+
return {
|
|
1226
|
+
read: () => storage.getItem(key),
|
|
1227
|
+
write: (snapshot) => {
|
|
1228
|
+
storage.setItem(key, JSON.stringify(assertReceizProofRegisterSnapshot(snapshot)));
|
|
1229
|
+
},
|
|
1230
|
+
remove: () => {
|
|
1231
|
+
storage.removeItem(key);
|
|
1232
|
+
},
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
1235
|
+
export function receizProofMemoryAdditionsQuery(value, limit) {
|
|
1236
|
+
const snapshot = value instanceof ReceizProofRegister
|
|
1237
|
+
? value.snapshot()
|
|
1238
|
+
: value instanceof ReceizProofMemory
|
|
1239
|
+
? value.snapshot()
|
|
1240
|
+
: assertReceizProofRegisterSnapshot(value);
|
|
1241
|
+
return {
|
|
1242
|
+
afterEntryId: snapshot.head.entryId,
|
|
1243
|
+
afterKaiUpulse: snapshot.head.kaiUpulse,
|
|
1244
|
+
afterCreatedAt: snapshot.head.createdAt,
|
|
1245
|
+
...(limit === undefined ? {} : { limit }),
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
871
1248
|
function bodyToString(body) {
|
|
872
1249
|
if (typeof body === "string")
|
|
873
1250
|
return body;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# CLI And Local Conformance
|
|
2
|
+
|
|
3
|
+
The Receiz SDK ships an executable `receiz` command for local proof inspection, fixture conformance, and starter generation.
|
|
4
|
+
|
|
5
|
+
The CLI is convenience, not authority. It uses the same SDK validators, projections, and durable proof memory APIs that developer apps import from `@receiz/sdk`. It does not create a new conformance system, issuer, verifier, server confirmation, database confirmation, or truth layer.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @receiz/sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Run without a local install:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx @receiz/sdk conformance
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Run Local Fixture Conformance
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx @receiz/sdk conformance
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
This validates the packaged fixtures:
|
|
26
|
+
|
|
27
|
+
- `receiz.asset_manifest.v1`
|
|
28
|
+
- `receiz.sports_arena.card_manifest.v1`
|
|
29
|
+
- `receiz.webhook_event.v1`
|
|
30
|
+
|
|
31
|
+
Each fixture is admitted into durable proof memory, persisted through the same storage interface developer apps use, reopened from the stored proof register, and summarized with its known Kai/proof head.
|
|
32
|
+
|
|
33
|
+
Expected shape:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"ok": true,
|
|
38
|
+
"schema": "receiz.sdk.cli.conformance.v1",
|
|
39
|
+
"summary": {
|
|
40
|
+
"fixtures": 3,
|
|
41
|
+
"proofMemoryEntries": 3,
|
|
42
|
+
"networkCalls": 0,
|
|
43
|
+
"dbCalls": 0
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`networkCalls` and `dbCalls` are zero by design. Local conformance proves the SDK can admit and project proof objects from carried truth without asking a weaker layer whether the proof is true.
|
|
49
|
+
|
|
50
|
+
## Inspect A Proof Payload
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npx @receiz/sdk inspect ./receiz-asset-manifest.json
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The command validates one JSON payload, projects display-ready rows, admits the payload into in-memory proof memory, and prints the known head:
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"ok": true,
|
|
61
|
+
"schema": "receiz.sdk.cli.inspect.v1",
|
|
62
|
+
"kind": "asset_manifest",
|
|
63
|
+
"knownHead": {
|
|
64
|
+
"afterEntryId": "asset:asset_example_01JZ_RECEIZ",
|
|
65
|
+
"afterKaiUpulse": "123456",
|
|
66
|
+
"afterCreatedAt": "2026-06-25T00:00:00.000Z",
|
|
67
|
+
"limit": 100
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The known head is the append-sync boundary. Your app asks for verified additions after this head; it does not rediscover the admitted prefix.
|
|
73
|
+
|
|
74
|
+
## Generate A Starter
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npx @receiz/sdk init ./receiz-integration
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
This writes:
|
|
81
|
+
|
|
82
|
+
```txt
|
|
83
|
+
receiz-proof-memory.ts
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The starter:
|
|
87
|
+
|
|
88
|
+
- opens durable proof memory through `createReceizProofMemory`
|
|
89
|
+
- persists through `createReceizLocalStorageProofMemoryStorage`
|
|
90
|
+
- admits asset manifests, Sports card manifests, and webhook events
|
|
91
|
+
- projects proof objects immediately after admission
|
|
92
|
+
- exposes `knownHead()` for append-only sync
|
|
93
|
+
|
|
94
|
+
It does not include Supabase, service-role keys, or a parallel source of truth.
|
|
95
|
+
|
|
96
|
+
## Production Rule
|
|
97
|
+
|
|
98
|
+
Use the CLI to prove your local integration shape, then use the typed SDK APIs in your app:
|
|
99
|
+
|
|
100
|
+
1. Verify stronger proof truth when bytes come from an untrusted transport.
|
|
101
|
+
2. Validate the manifest or event shape.
|
|
102
|
+
3. Project display-ready rows from the verified object.
|
|
103
|
+
4. Admit the proof object into durable proof memory.
|
|
104
|
+
5. Persist the proof memory snapshot.
|
|
105
|
+
6. Resume from the known Kai/proof head.
|
|
106
|
+
7. Append later verified additions without rediscovering known truth.
|
|
107
|
+
|
|
108
|
+
That is the same law Receiz uses internally: first admission only, then append forever.
|