@receiz/sdk 95.0.0 → 96.1.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 +76 -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 +258 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +388 -1
- package/docs/app-registration-token-lifecycle.md +3 -1
- package/docs/cli-and-conformance.md +108 -0
- package/docs/copy-paste-integrations.md +229 -0
- package/docs/proof-memory-and-projections.md +38 -5
- package/docs/twin-and-world.md +163 -0
- package/package.json +5 -2
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.1.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";
|
|
@@ -347,6 +347,29 @@ export class ReceizClient {
|
|
|
347
347
|
publicLedger: (query = {}) => this.publicWalletLedger(query),
|
|
348
348
|
actionLedger: (query = {}) => this.request(appendQuery("/api/ledger/actions/public", query)),
|
|
349
349
|
};
|
|
350
|
+
world = {
|
|
351
|
+
publicSnapshot: () => this.request("/api/world/public"),
|
|
352
|
+
profile: (username, query = {}) => this.request(appendQuery(`/api/world/profile/${encodePathSegment(username)}`, {
|
|
353
|
+
visitorKey: query.visitorKey,
|
|
354
|
+
threadKey: query.threadKey,
|
|
355
|
+
})),
|
|
356
|
+
message: (username, body) => this.request(`/api/world/profile/${encodePathSegment(username)}`, {
|
|
357
|
+
method: "POST",
|
|
358
|
+
body: { ...body, action: body.action ?? "message" },
|
|
359
|
+
}),
|
|
360
|
+
welcome: (username, body = {}) => this.request(`/api/world/profile/${encodePathSegment(username)}`, {
|
|
361
|
+
method: "POST",
|
|
362
|
+
body: { ...body, action: "welcome" },
|
|
363
|
+
}),
|
|
364
|
+
book: (username, body) => this.request(`/api/world/profile/${encodePathSegment(username)}`, {
|
|
365
|
+
method: "POST",
|
|
366
|
+
body: { ...body, action: "book" },
|
|
367
|
+
}),
|
|
368
|
+
streamProfile: (username, body) => this.streamJsonEvents(`/api/world/profile/${encodePathSegment(username)}/stream`, {
|
|
369
|
+
method: "POST",
|
|
370
|
+
body: { ...body, action: body.action === "welcome" ? "welcome" : "message" },
|
|
371
|
+
}),
|
|
372
|
+
};
|
|
350
373
|
connect = {
|
|
351
374
|
wallet: () => this.delegated("/api/connect/wallet/me"),
|
|
352
375
|
record: (body) => this.delegated("/api/connect/record", { method: "POST", body }),
|
|
@@ -359,6 +382,20 @@ export class ReceizClient {
|
|
|
359
382
|
claimNote: (body) => this.delegated("/api/connect/payments/notes/claim", { method: "POST", body }),
|
|
360
383
|
downloadNote: (noteId) => this.delegated(`/api/connect/payments/notes/${encodePathSegment(noteId)}/download`),
|
|
361
384
|
};
|
|
385
|
+
twin = {
|
|
386
|
+
marketMandate: () => this.delegated("/api/connect/twin/market/mandate", { method: "GET" }),
|
|
387
|
+
saveMarketMandate: (body) => this.delegated("/api/connect/twin/market/mandate", { method: "PATCH", body }),
|
|
388
|
+
marketIntents: () => this.delegated("/api/connect/twin/market/intents", { method: "GET" }),
|
|
389
|
+
createMarketIntent: (body) => this.delegated("/api/connect/twin/market/intents", { method: "POST", body }),
|
|
390
|
+
approveMarketIntent: (intentId) => this.delegated(`/api/connect/twin/market/intents/${encodePathSegment(intentId)}/approve`, {
|
|
391
|
+
method: "POST",
|
|
392
|
+
}),
|
|
393
|
+
exportMind: () => this.delegatedBlob("/api/connect/twin/mind/export"),
|
|
394
|
+
importMindSummary: () => this.delegated("/api/connect/twin/mind/import", { method: "GET" }),
|
|
395
|
+
importMind: (file) => this.delegatedTwinMindImport(file),
|
|
396
|
+
approval: () => this.delegated("/api/connect/twin/approval", { method: "GET" }),
|
|
397
|
+
approvePromotion: (body) => this.delegated("/api/connect/twin/approval", { method: "POST", body }),
|
|
398
|
+
};
|
|
362
399
|
identity = {
|
|
363
400
|
createReceizId: createReceizIdIdentity,
|
|
364
401
|
readArtifact: readReceizIdentityArtifact,
|
|
@@ -425,6 +462,11 @@ export class ReceizClient {
|
|
|
425
462
|
};
|
|
426
463
|
proofMemory = {
|
|
427
464
|
createRegister: createReceizProofRegister,
|
|
465
|
+
createMemory: createReceizProofMemory,
|
|
466
|
+
createInMemoryStorage: createReceizInMemoryProofMemoryStorage,
|
|
467
|
+
createLocalStorage: createReceizLocalStorageProofMemoryStorage,
|
|
468
|
+
assertSnapshot: assertReceizProofRegisterSnapshot,
|
|
469
|
+
additionsQuery: receizProofMemoryAdditionsQuery,
|
|
428
470
|
};
|
|
429
471
|
constructor(options = {}) {
|
|
430
472
|
this.baseUrl = trimTrailingSlash(options.baseUrl ?? RECEIZ_DEFAULT_BASE_URL);
|
|
@@ -488,6 +530,88 @@ export class ReceizClient {
|
|
|
488
530
|
throw new Error("receiz_access_token_required");
|
|
489
531
|
return this.request(path, { ...options, bearerToken: this.accessToken });
|
|
490
532
|
}
|
|
533
|
+
async delegatedBlob(path, options = {}) {
|
|
534
|
+
if (!this.accessToken)
|
|
535
|
+
throw new Error("receiz_access_token_required");
|
|
536
|
+
const headers = new Headers(options.headers);
|
|
537
|
+
if (!headers.has("accept"))
|
|
538
|
+
headers.set("accept", "application/octet-stream");
|
|
539
|
+
headers.set("authorization", `Bearer ${this.accessToken}`);
|
|
540
|
+
const response = await this.fetchImpl(`${this.baseUrl}${requestPath(path)}`, {
|
|
541
|
+
method: options.method ?? "GET",
|
|
542
|
+
headers,
|
|
543
|
+
});
|
|
544
|
+
if (!response.ok)
|
|
545
|
+
throw new ReceizHttpError(response.status, await readPayload(response));
|
|
546
|
+
return response.blob();
|
|
547
|
+
}
|
|
548
|
+
async delegatedTwinMindImport(file) {
|
|
549
|
+
const form = new FormData();
|
|
550
|
+
form.set("mind", file);
|
|
551
|
+
return this.delegated("/api/connect/twin/mind/import", { method: "POST", body: form });
|
|
552
|
+
}
|
|
553
|
+
async *streamJsonEvents(path, options = {}) {
|
|
554
|
+
const headers = new Headers(options.headers);
|
|
555
|
+
if (!headers.has("accept"))
|
|
556
|
+
headers.set("accept", "text/event-stream");
|
|
557
|
+
const token = options.bearerToken ?? this.accessToken;
|
|
558
|
+
if (token)
|
|
559
|
+
headers.set("authorization", `Bearer ${token}`);
|
|
560
|
+
let body = options.body;
|
|
561
|
+
if (isJsonRecordBody(body)) {
|
|
562
|
+
if (!headers.has("content-type"))
|
|
563
|
+
headers.set("content-type", "application/json");
|
|
564
|
+
body = JSON.stringify(body);
|
|
565
|
+
}
|
|
566
|
+
const url = path.startsWith("http://") || path.startsWith("https://") ? path : `${this.baseUrl}${requestPath(path)}`;
|
|
567
|
+
const response = await this.fetchImpl(url, {
|
|
568
|
+
method: options.method ?? "GET",
|
|
569
|
+
headers,
|
|
570
|
+
...(body !== undefined ? { body: body } : {}),
|
|
571
|
+
});
|
|
572
|
+
if (!response.ok)
|
|
573
|
+
throw new ReceizHttpError(response.status, await readPayload(response));
|
|
574
|
+
if (!response.body)
|
|
575
|
+
throw new Error("receiz_stream_unavailable");
|
|
576
|
+
const reader = response.body.getReader();
|
|
577
|
+
const decoder = new TextDecoder();
|
|
578
|
+
let buffer = "";
|
|
579
|
+
const parseEvent = (raw) => {
|
|
580
|
+
const data = raw
|
|
581
|
+
.split("\n")
|
|
582
|
+
.filter((line) => line.startsWith("data:"))
|
|
583
|
+
.map((line) => line.slice(5).trimStart())
|
|
584
|
+
.join("\n")
|
|
585
|
+
.trim();
|
|
586
|
+
if (!data)
|
|
587
|
+
return null;
|
|
588
|
+
return JSON.parse(data);
|
|
589
|
+
};
|
|
590
|
+
try {
|
|
591
|
+
for (;;) {
|
|
592
|
+
const { done, value } = await reader.read();
|
|
593
|
+
if (done)
|
|
594
|
+
break;
|
|
595
|
+
buffer += decoder.decode(value, { stream: true }).replace(/\r\n/g, "\n");
|
|
596
|
+
let boundary = buffer.indexOf("\n\n");
|
|
597
|
+
while (boundary >= 0) {
|
|
598
|
+
const raw = buffer.slice(0, boundary);
|
|
599
|
+
buffer = buffer.slice(boundary + 2);
|
|
600
|
+
const parsed = parseEvent(raw);
|
|
601
|
+
if (parsed)
|
|
602
|
+
yield parsed;
|
|
603
|
+
boundary = buffer.indexOf("\n\n");
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
buffer += decoder.decode().replace(/\r\n/g, "\n");
|
|
607
|
+
const parsed = parseEvent(buffer);
|
|
608
|
+
if (parsed)
|
|
609
|
+
yield parsed;
|
|
610
|
+
}
|
|
611
|
+
finally {
|
|
612
|
+
reader.releaseLock();
|
|
613
|
+
}
|
|
614
|
+
}
|
|
491
615
|
async continueReceizId(identity, options = {}) {
|
|
492
616
|
const body = await buildReceizIdContinueRequest(identity, options);
|
|
493
617
|
return this.request("/api/auth/receiz-id/continue", { method: "POST", body });
|
|
@@ -863,6 +987,67 @@ function normalizeRegisterEntry(value) {
|
|
|
863
987
|
projection: isRecord(projection) ? projection : null,
|
|
864
988
|
};
|
|
865
989
|
}
|
|
990
|
+
export function assertReceizProofRegisterSnapshot(value) {
|
|
991
|
+
const record = ensureRecord(value, "ReceizProofRegisterSnapshot");
|
|
992
|
+
const issues = [];
|
|
993
|
+
if (record.schema !== "receiz.sdk.proof_register.v1")
|
|
994
|
+
issues.push("schema must be receiz.sdk.proof_register.v1");
|
|
995
|
+
const ownerId = record.ownerId;
|
|
996
|
+
if (ownerId !== null && ownerId !== undefined && typeof ownerId !== "string") {
|
|
997
|
+
issues.push("ownerId must be a string or null when present");
|
|
998
|
+
}
|
|
999
|
+
const createdAt = ensureString(record, "createdAt", issues);
|
|
1000
|
+
const updatedAt = ensureString(record, "updatedAt", issues);
|
|
1001
|
+
const head = record.head;
|
|
1002
|
+
if (!isRecord(head)) {
|
|
1003
|
+
issues.push("head must be an object");
|
|
1004
|
+
}
|
|
1005
|
+
else {
|
|
1006
|
+
const entryId = head.entryId;
|
|
1007
|
+
if (entryId !== null && entryId !== undefined && typeof entryId !== "string") {
|
|
1008
|
+
issues.push("head.entryId must be a string or null");
|
|
1009
|
+
}
|
|
1010
|
+
const kaiUpulse = head.kaiUpulse;
|
|
1011
|
+
if (kaiUpulse !== null && kaiUpulse !== undefined && typeof kaiUpulse !== "string" && typeof kaiUpulse !== "number") {
|
|
1012
|
+
issues.push("head.kaiUpulse must be a string, number, or null");
|
|
1013
|
+
}
|
|
1014
|
+
const headCreatedAt = head.createdAt;
|
|
1015
|
+
if (headCreatedAt !== null && headCreatedAt !== undefined && typeof headCreatedAt !== "string") {
|
|
1016
|
+
issues.push("head.createdAt must be a string or null");
|
|
1017
|
+
}
|
|
1018
|
+
if (typeof head.count !== "number" || !Number.isInteger(head.count) || head.count < 0) {
|
|
1019
|
+
issues.push("head.count must be a non-negative integer");
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
if (!Array.isArray(record.entries))
|
|
1023
|
+
issues.push("entries must be an array");
|
|
1024
|
+
failIfIssues("ReceizProofRegisterSnapshot", issues);
|
|
1025
|
+
const entries = record.entries.map((entry) => normalizeRegisterEntry(entry));
|
|
1026
|
+
return {
|
|
1027
|
+
schema: "receiz.sdk.proof_register.v1",
|
|
1028
|
+
ownerId: typeof ownerId === "string" || ownerId === null ? ownerId : null,
|
|
1029
|
+
createdAt: createdAt ?? "",
|
|
1030
|
+
updatedAt: updatedAt ?? "",
|
|
1031
|
+
head: {
|
|
1032
|
+
entryId: isRecord(head) && typeof head.entryId === "string" ? head.entryId : null,
|
|
1033
|
+
kaiUpulse: isRecord(head) && (typeof head.kaiUpulse === "string" || typeof head.kaiUpulse === "number") ? head.kaiUpulse : null,
|
|
1034
|
+
createdAt: isRecord(head) && typeof head.createdAt === "string" ? head.createdAt : null,
|
|
1035
|
+
count: isRecord(head) && typeof head.count === "number" ? head.count : entries.length,
|
|
1036
|
+
},
|
|
1037
|
+
entries,
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
function parseProofMemoryStorageValue(value) {
|
|
1041
|
+
if (value === null || value === undefined)
|
|
1042
|
+
return null;
|
|
1043
|
+
if (typeof value === "string") {
|
|
1044
|
+
const trimmed = value.trim();
|
|
1045
|
+
if (!trimmed)
|
|
1046
|
+
return null;
|
|
1047
|
+
return assertReceizProofRegisterSnapshot(JSON.parse(trimmed));
|
|
1048
|
+
}
|
|
1049
|
+
return assertReceizProofRegisterSnapshot(value);
|
|
1050
|
+
}
|
|
866
1051
|
function mergeRegisterEntry(existing, incoming) {
|
|
867
1052
|
return {
|
|
868
1053
|
id: existing.id,
|
|
@@ -922,6 +1107,11 @@ function registerEntryFromWebhookEvent(event) {
|
|
|
922
1107
|
},
|
|
923
1108
|
};
|
|
924
1109
|
}
|
|
1110
|
+
function errorDetail(error) {
|
|
1111
|
+
if (error instanceof Error)
|
|
1112
|
+
return { name: error.name, message: error.message };
|
|
1113
|
+
return { message: String(error) };
|
|
1114
|
+
}
|
|
925
1115
|
export class ReceizProofRegister {
|
|
926
1116
|
ownerId;
|
|
927
1117
|
createdAt;
|
|
@@ -938,6 +1128,16 @@ export class ReceizProofRegister {
|
|
|
938
1128
|
this.entriesById.set(normalized.id, existing ? mergeRegisterEntry(existing, normalized) : normalized);
|
|
939
1129
|
return this;
|
|
940
1130
|
}
|
|
1131
|
+
admit(value) {
|
|
1132
|
+
if (isReceizAssetManifest(value))
|
|
1133
|
+
return this.admitAssetManifest(value);
|
|
1134
|
+
if (isReceizSportsCardManifest(value))
|
|
1135
|
+
return this.admitSportsCardManifest(value);
|
|
1136
|
+
if (isReceizWebhookEvent(value))
|
|
1137
|
+
return this.admitWebhookEvent(value);
|
|
1138
|
+
const schema = isRecord(value) && typeof value.schema === "string" ? value.schema : "unknown";
|
|
1139
|
+
throw new ReceizValidationError("ReceizProofAdmission", [`unsupported proof object schema ${schema}`]);
|
|
1140
|
+
}
|
|
941
1141
|
admitAssetManifest(value) {
|
|
942
1142
|
return this.append(registerEntryFromAssetManifest(assertReceizAssetManifest(value)));
|
|
943
1143
|
}
|
|
@@ -977,6 +1177,193 @@ export class ReceizProofRegister {
|
|
|
977
1177
|
export function createReceizProofRegister(input = {}) {
|
|
978
1178
|
return new ReceizProofRegister(input);
|
|
979
1179
|
}
|
|
1180
|
+
export class ReceizProofMemory {
|
|
1181
|
+
register;
|
|
1182
|
+
storage;
|
|
1183
|
+
autoPersist;
|
|
1184
|
+
onPersistError;
|
|
1185
|
+
onObservation;
|
|
1186
|
+
persistChain = Promise.resolve();
|
|
1187
|
+
constructor(register, options = {}) {
|
|
1188
|
+
this.register = register;
|
|
1189
|
+
this.storage = options.storage ?? null;
|
|
1190
|
+
this.autoPersist = options.autoPersist ?? true;
|
|
1191
|
+
this.onPersistError = options.onPersistError ?? null;
|
|
1192
|
+
this.onObservation = options.onObservation ?? null;
|
|
1193
|
+
}
|
|
1194
|
+
append(entry) {
|
|
1195
|
+
const startedAtMs = Date.now();
|
|
1196
|
+
try {
|
|
1197
|
+
this.register.append(entry);
|
|
1198
|
+
this.observe("proof_memory.append", startedAtMs, "ok");
|
|
1199
|
+
return this.schedulePersist();
|
|
1200
|
+
}
|
|
1201
|
+
catch (error) {
|
|
1202
|
+
this.observe("proof_memory.append", startedAtMs, "error", errorDetail(error));
|
|
1203
|
+
throw error;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
admit(value) {
|
|
1207
|
+
const startedAtMs = Date.now();
|
|
1208
|
+
try {
|
|
1209
|
+
this.register.admit(value);
|
|
1210
|
+
this.observe("proof_memory.admit", startedAtMs, "ok");
|
|
1211
|
+
return this.schedulePersist();
|
|
1212
|
+
}
|
|
1213
|
+
catch (error) {
|
|
1214
|
+
this.observe("proof_memory.admit", startedAtMs, "error", errorDetail(error));
|
|
1215
|
+
throw error;
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
admitAssetManifest(value) {
|
|
1219
|
+
return this.admit(value);
|
|
1220
|
+
}
|
|
1221
|
+
admitSportsCardManifest(value) {
|
|
1222
|
+
return this.admit(value);
|
|
1223
|
+
}
|
|
1224
|
+
admitWebhookEvent(value) {
|
|
1225
|
+
return this.admit(value);
|
|
1226
|
+
}
|
|
1227
|
+
has(entryId) {
|
|
1228
|
+
return this.register.has(entryId);
|
|
1229
|
+
}
|
|
1230
|
+
entries() {
|
|
1231
|
+
return this.register.entries();
|
|
1232
|
+
}
|
|
1233
|
+
snapshot() {
|
|
1234
|
+
return this.register.snapshot();
|
|
1235
|
+
}
|
|
1236
|
+
knownHead(limit) {
|
|
1237
|
+
return receizProofMemoryAdditionsQuery(this.snapshot(), limit);
|
|
1238
|
+
}
|
|
1239
|
+
async persist() {
|
|
1240
|
+
const startedAtMs = Date.now();
|
|
1241
|
+
try {
|
|
1242
|
+
const snapshot = this.snapshot();
|
|
1243
|
+
if (this.storage)
|
|
1244
|
+
await this.storage.write(snapshot);
|
|
1245
|
+
this.observe("proof_memory.persist", startedAtMs, "ok", { persisted: Boolean(this.storage) });
|
|
1246
|
+
return snapshot;
|
|
1247
|
+
}
|
|
1248
|
+
catch (error) {
|
|
1249
|
+
this.observe("proof_memory.persist", startedAtMs, "error", errorDetail(error));
|
|
1250
|
+
throw error;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
async flush() {
|
|
1254
|
+
await this.persistChain;
|
|
1255
|
+
}
|
|
1256
|
+
async clear() {
|
|
1257
|
+
const startedAtMs = Date.now();
|
|
1258
|
+
try {
|
|
1259
|
+
if (this.storage?.remove)
|
|
1260
|
+
await this.storage.remove();
|
|
1261
|
+
this.observe("proof_memory.clear", startedAtMs, "ok", { removed: Boolean(this.storage?.remove) });
|
|
1262
|
+
}
|
|
1263
|
+
catch (error) {
|
|
1264
|
+
this.observe("proof_memory.clear", startedAtMs, "error", errorDetail(error));
|
|
1265
|
+
throw error;
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
toJSON() {
|
|
1269
|
+
return this.snapshot();
|
|
1270
|
+
}
|
|
1271
|
+
schedulePersist() {
|
|
1272
|
+
if (!this.autoPersist || !this.storage)
|
|
1273
|
+
return this;
|
|
1274
|
+
this.persistChain = this.persistChain
|
|
1275
|
+
.then(async () => {
|
|
1276
|
+
await this.persist();
|
|
1277
|
+
})
|
|
1278
|
+
.catch((error) => {
|
|
1279
|
+
this.onPersistError?.(error);
|
|
1280
|
+
});
|
|
1281
|
+
return this;
|
|
1282
|
+
}
|
|
1283
|
+
observe(operation, startedAtMs, status, detail) {
|
|
1284
|
+
if (!this.onObservation)
|
|
1285
|
+
return;
|
|
1286
|
+
const endedAtMs = Date.now();
|
|
1287
|
+
const snapshot = this.snapshot();
|
|
1288
|
+
const observation = {
|
|
1289
|
+
schema: "receiz.sdk.observation.v1",
|
|
1290
|
+
operation,
|
|
1291
|
+
status,
|
|
1292
|
+
startedAt: new Date(startedAtMs).toISOString(),
|
|
1293
|
+
endedAt: new Date(endedAtMs).toISOString(),
|
|
1294
|
+
durationMs: Math.max(0, endedAtMs - startedAtMs),
|
|
1295
|
+
ownerId: this.register.ownerId,
|
|
1296
|
+
entryCount: snapshot.head.count,
|
|
1297
|
+
headEntryId: snapshot.head.entryId,
|
|
1298
|
+
headKaiUpulse: snapshot.head.kaiUpulse,
|
|
1299
|
+
};
|
|
1300
|
+
if (detail)
|
|
1301
|
+
observation.detail = detail;
|
|
1302
|
+
try {
|
|
1303
|
+
this.onObservation(observation);
|
|
1304
|
+
}
|
|
1305
|
+
catch {
|
|
1306
|
+
// Observation callbacks are passive evidence only; they must never affect proof admission.
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
export async function createReceizProofMemory(options = {}) {
|
|
1311
|
+
const storedSnapshot = options.storage ? parseProofMemoryStorageValue(await options.storage.read()) : null;
|
|
1312
|
+
const input = storedSnapshot
|
|
1313
|
+
? {
|
|
1314
|
+
...storedSnapshot,
|
|
1315
|
+
ownerId: options.ownerId ?? storedSnapshot.ownerId ?? null,
|
|
1316
|
+
}
|
|
1317
|
+
: {
|
|
1318
|
+
ownerId: options.ownerId ?? null,
|
|
1319
|
+
...(options.createdAt === undefined ? {} : { createdAt: options.createdAt }),
|
|
1320
|
+
...(options.entries === undefined ? {} : { entries: options.entries }),
|
|
1321
|
+
};
|
|
1322
|
+
return new ReceizProofMemory(createReceizProofRegister(input), options);
|
|
1323
|
+
}
|
|
1324
|
+
export function createReceizInMemoryProofMemoryStorage(initialValue = null) {
|
|
1325
|
+
let storedText = typeof initialValue === "string"
|
|
1326
|
+
? initialValue
|
|
1327
|
+
: initialValue
|
|
1328
|
+
? JSON.stringify(assertReceizProofRegisterSnapshot(initialValue))
|
|
1329
|
+
: null;
|
|
1330
|
+
return {
|
|
1331
|
+
read: () => storedText,
|
|
1332
|
+
write: (snapshot) => {
|
|
1333
|
+
storedText = JSON.stringify(assertReceizProofRegisterSnapshot(snapshot));
|
|
1334
|
+
},
|
|
1335
|
+
remove: () => {
|
|
1336
|
+
storedText = null;
|
|
1337
|
+
},
|
|
1338
|
+
readText: () => storedText,
|
|
1339
|
+
};
|
|
1340
|
+
}
|
|
1341
|
+
export function createReceizLocalStorageProofMemoryStorage(key = "receiz:proof-memory:v1", storage = globalThis.localStorage) {
|
|
1342
|
+
if (!storage)
|
|
1343
|
+
throw new Error("receiz_local_storage_unavailable");
|
|
1344
|
+
return {
|
|
1345
|
+
read: () => storage.getItem(key),
|
|
1346
|
+
write: (snapshot) => {
|
|
1347
|
+
storage.setItem(key, JSON.stringify(assertReceizProofRegisterSnapshot(snapshot)));
|
|
1348
|
+
},
|
|
1349
|
+
remove: () => {
|
|
1350
|
+
storage.removeItem(key);
|
|
1351
|
+
},
|
|
1352
|
+
};
|
|
1353
|
+
}
|
|
1354
|
+
export function receizProofMemoryAdditionsQuery(value, limit) {
|
|
1355
|
+
const snapshot = value instanceof ReceizProofRegister
|
|
1356
|
+
? value.snapshot()
|
|
1357
|
+
: value instanceof ReceizProofMemory
|
|
1358
|
+
? value.snapshot()
|
|
1359
|
+
: assertReceizProofRegisterSnapshot(value);
|
|
1360
|
+
return {
|
|
1361
|
+
afterEntryId: snapshot.head.entryId,
|
|
1362
|
+
afterKaiUpulse: snapshot.head.kaiUpulse,
|
|
1363
|
+
afterCreatedAt: snapshot.head.createdAt,
|
|
1364
|
+
...(limit === undefined ? {} : { limit }),
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
980
1367
|
function bodyToString(body) {
|
|
981
1368
|
if (typeof body === "string")
|
|
982
1369
|
return body;
|
|
@@ -12,7 +12,7 @@ const authorizeUrl = receiz.identity.authorizeUrl({
|
|
|
12
12
|
clientId: process.env.RECEIZ_CLIENT_ID!,
|
|
13
13
|
redirectUri: "https://app.example.com/auth/receiz/callback",
|
|
14
14
|
codeChallenge,
|
|
15
|
-
scope: ["openid", "profile", "receiz:verify", "receiz:wallet.transfer"],
|
|
15
|
+
scope: ["openid", "profile", "receiz:verify", "receiz:wallet.transfer", "receiz:twin.read"],
|
|
16
16
|
state,
|
|
17
17
|
});
|
|
18
18
|
```
|
|
@@ -29,3 +29,5 @@ Lifecycle:
|
|
|
29
29
|
6. Revoke tokens when the user disconnects your app.
|
|
30
30
|
|
|
31
31
|
Use `sub` from OIDC as the stable external user key. Do not key your user model by mutable email.
|
|
32
|
+
|
|
33
|
+
Public Receiz World reads do not require OIDC. Delegated owner actions, including Twin market mandates, Twin intents, Twin mind import/export, wallet transfers, records, seals, verification, payments, and note actions, require a registered OIDC client and a user-granted access token. Business/developer accounts are the correct place to create and manage those clients, redirect URIs, scopes, and token lifecycle.
|
|
@@ -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.
|