@receiz/sdk 97.1.0 → 97.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,12 +1,46 @@
1
1
  import { buildReceizIdContinueRequest, createReceizIdIdentity, projectReceizIdentityAccount, readReceizIdentityArtifact, signReceizIdentityLoginProof, verifyReceizIdentityLoginProof, } from "./identity.js";
2
2
  export * from "./identity.js";
3
- export const RECEIZ_SDK_VERSION = "97.1.0";
3
+ export const RECEIZ_SDK_VERSION = "97.3.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";
7
7
  export const RECEIZ_APP_STATE_FEED_SCHEMA = "receiz.app.public_state_registry_feed.v1";
8
8
  export const RECEIZ_PUBLIC_STORE_STATE_FEED_SCHEMA = "receiz.app.public_store_state_registry_feed.v1";
9
9
  export const RECEIZ_PUBLIC_STORE_STATE_PROJECTION_SCHEMA = "receiz.app.public_store_state_projection.v1";
10
+ export const RECEIZ_OIDC_SCOPES_BY_RAIL = {
11
+ identity: ["openid", "profile", "email"],
12
+ wallet: ["receiz:wallet.read", "receiz:wallet.transfer"],
13
+ payments: ["receiz:payments.read", "receiz:payments.create"],
14
+ proofStore: ["receiz:record", "receiz:seal", "receiz:verify"],
15
+ media: ["receiz:record"],
16
+ domains: ["receiz:record"],
17
+ twin: ["receiz:twin.read", "receiz:twin.write"],
18
+ commerce: ["receiz:payments.read", "receiz:payments.create", "receiz:wallet.read", "receiz:record"],
19
+ rewards: ["receiz:record", "receiz:wallet.read"],
20
+ customers: ["openid", "profile", "email", "offline_access", "receiz:wallet.read", "receiz:payments.read"],
21
+ merchants: ["openid", "profile", "email", "offline_access", "receiz:record", "receiz:wallet.read", "receiz:payments.read", "receiz:payments.create"],
22
+ events: ["receiz:record"],
23
+ search: ["receiz:record"],
24
+ permissions: ["receiz:record"],
25
+ jobs: ["receiz:record"],
26
+ audit: ["receiz:record"],
27
+ risk: ["receiz:record"],
28
+ compliance: ["receiz:record"],
29
+ portability: ["receiz:record"],
30
+ notifications: ["receiz:record"],
31
+ offline: ["receiz:record"],
32
+ releases: ["receiz:record"],
33
+ appState: ["receiz:record"],
34
+ publicStore: ["receiz:record"],
35
+ };
36
+ export function receizOidcScopesForRails(...rails) {
37
+ const scopes = new Set();
38
+ for (const rail of rails) {
39
+ for (const scope of RECEIZ_OIDC_SCOPES_BY_RAIL[rail] ?? [])
40
+ scopes.add(scope);
41
+ }
42
+ return [...scopes];
43
+ }
10
44
  export const RECEIZ_ASSET_MANIFEST_SCHEMA = {
11
45
  $schema: "https://json-schema.org/draft/2020-12/schema",
12
46
  $id: "https://receiz.com/standards/receiz.asset-manifest.schema.v1.json",
@@ -268,6 +302,19 @@ function headersWithIdempotency(headers, idempotencyKey) {
268
302
  next.set("idempotency-key", idempotencyKey);
269
303
  return next;
270
304
  }
305
+ function queryParams(input) {
306
+ const params = {};
307
+ for (const [key, value] of Object.entries(input)) {
308
+ if (value === null ||
309
+ value === undefined ||
310
+ typeof value === "string" ||
311
+ typeof value === "number" ||
312
+ typeof value === "boolean") {
313
+ params[key] = value;
314
+ }
315
+ }
316
+ return params;
317
+ }
271
318
  function usdToCents(value) {
272
319
  const trimmed = value.trim();
273
320
  if (!/^\d+(\.\d{1,2})?$/.test(trimmed))
@@ -577,6 +624,12 @@ export class ReceizClient {
577
624
  extractData: extractReceizAppStateData,
578
625
  restoreProjection: restoreReceizAppStateProjection,
579
626
  };
627
+ publicStore = {
628
+ publish: (input, options = {}) => this.publishPublicStore(input, options),
629
+ resolve: (input) => this.resolvePublicStore(input),
630
+ createRecord: createReceizPublicStoreStateRecord,
631
+ scopes: RECEIZ_OIDC_SCOPES_BY_RAIL.publicStore,
632
+ };
580
633
  sports = {
581
634
  conformance: () => this.sportsConformance(),
582
635
  conformanceHistory: () => this.request("/api/game/sports/conformance/history"),
@@ -584,10 +637,16 @@ export class ReceizClient {
584
637
  badge: () => this.request("/api/game/sports/conformance/badge"),
585
638
  eventProofUrl: (eventProofId) => `${this.baseUrl}/game/sports/event/${encodePathSegment(eventProofId)}`,
586
639
  assertCardManifest: assertReceizSportsCardManifest,
640
+ resolveCardMemory: (input) => this.resolveLocalFirstSurface("sports.card_memory", input),
641
+ resolvePitchDayProof: (input) => this.resolveLocalFirstSurface("sports.pitch_day_proof", input),
587
642
  };
588
643
  wallet = {
589
644
  publicLedger: (query = {}) => this.publicWalletLedger(query),
590
645
  actionLedger: (query = {}) => this.request(appendQuery("/api/ledger/actions/public", query)),
646
+ resolveLedger: (input) => this.resolveLocalFirstSurface("wallet.ledger", input),
647
+ };
648
+ profile = {
649
+ resolveLocalFirst: (input) => this.resolveLocalFirstSurface("profile", input),
591
650
  };
592
651
  world = {
593
652
  publicSnapshot: () => this.request("/api/world/public"),
@@ -676,9 +735,61 @@ export class ReceizClient {
676
735
  }),
677
736
  bootstrap: (username) => this.request(`/api/connect/login/bootstrap/${encodePathSegment(username)}`),
678
737
  authorizeUrl: (options) => this.authorizeUrl(options),
738
+ ensureTenantSession: (input) => this.ensureTenantSession(input),
739
+ };
740
+ customers = {
741
+ bootstrapSession: (body, options = {}) => this.bootstrapCustomerSession(body, options),
742
+ bootstrap: (body, options = {}) => this.bootstrapCustomerSession(body, options),
743
+ session: (body, options = {}) => this.delegatedWrite("/api/connect/customers/session", body, options),
744
+ portal: (body) => this.delegatedWrite("/api/connect/customers/portal", body),
745
+ orders: (query = {}) => this.delegated(appendQuery("/api/connect/customers/orders", queryParams(query))),
746
+ rewards: (query = {}) => this.delegated(appendQuery("/api/connect/customers/rewards", queryParams(query))),
747
+ assets: (query = {}) => this.delegated(appendQuery("/api/connect/customers/assets", queryParams(query))),
748
+ };
749
+ merchants = {
750
+ onboard: (body, options = {}) => this.delegatedWrite("/api/connect/merchants/onboard", body, options),
751
+ profile: (query = {}) => this.delegated(appendQuery("/api/connect/merchants/profile", query)),
752
+ capabilities: (query = {}) => this.delegated(appendQuery("/api/connect/merchants/capabilities", query)),
679
753
  };
680
754
  commerce = {
681
755
  oneClickCheckout: (body) => this.oneClickCheckout(body),
756
+ refunds: {
757
+ create: (body, options = {}) => this.delegatedWrite("/api/connect/commerce/refunds", body, options),
758
+ },
759
+ subscriptions: {
760
+ create: (body, options = {}) => this.delegatedWrite("/api/connect/commerce/subscriptions", body, options),
761
+ cancel: (subscriptionId, body, options = {}) => this.delegatedWrite(`/api/connect/commerce/subscriptions/${encodePathSegment(subscriptionId)}/cancel`, body, options),
762
+ },
763
+ shipping: {
764
+ quote: (body) => this.delegatedWrite("/api/connect/commerce/shipping/quote", body),
765
+ update: (body, options = {}) => this.delegatedWrite("/api/connect/commerce/shipping/update", body, options),
766
+ },
767
+ tax: {
768
+ quote: (body) => this.delegatedWrite("/api/connect/commerce/tax/quote", body),
769
+ },
770
+ discounts: {
771
+ create: (body, options = {}) => this.delegatedWrite("/api/connect/commerce/discounts", body, options),
772
+ redeem: (body, options = {}) => this.delegatedWrite("/api/connect/commerce/discounts/redeem", body, options),
773
+ },
774
+ giftCards: {
775
+ issue: (body, options = {}) => this.delegatedWrite("/api/connect/commerce/gift-cards", body, options),
776
+ redeem: (body, options = {}) => this.delegatedWrite("/api/connect/commerce/gift-cards/redeem", body, options),
777
+ },
778
+ accessPasses: {
779
+ issue: (body, options = {}) => this.delegatedWrite("/api/connect/commerce/access-passes", body, options),
780
+ verify: (body) => this.delegatedWrite("/api/connect/commerce/access-passes/verify", body),
781
+ },
782
+ inventory: {
783
+ reserve: (body, options = {}) => this.delegatedWrite("/api/connect/commerce/inventory/reserve", body, options),
784
+ adjust: (body, options = {}) => this.delegatedWrite("/api/connect/commerce/inventory/adjust", body, options),
785
+ },
786
+ fulfillment: {
787
+ update: (body, options = {}) => this.delegatedWrite("/api/connect/commerce/fulfillment", body, options),
788
+ },
789
+ payouts: {
790
+ create: (body, options = {}) => this.delegatedWrite("/api/connect/commerce/payouts", body, options),
791
+ status: (query = {}) => this.delegated(appendQuery("/api/connect/commerce/payouts/status", query)),
792
+ },
682
793
  };
683
794
  payments = {
684
795
  embeddedCheckout: (body) => this.request("/api/payments/embed/checkout", { method: "POST", body }),
@@ -686,6 +797,7 @@ export class ReceizClient {
686
797
  };
687
798
  media = {
688
799
  upload: (file, options = {}) => this.uploadMedia(file, options),
800
+ transform: (body, options = {}) => this.delegatedWrite("/api/connect/media/transform", body, options),
689
801
  objectUrl: (query) => appendQuery(`${this.baseUrl}/api/storage/object`, query),
690
802
  };
691
803
  domains = {
@@ -708,6 +820,9 @@ export class ReceizClient {
708
820
  };
709
821
  events = {
710
822
  subscribe: (body) => this.eventSubscribe(body),
823
+ replay: (body) => this.delegatedWrite("/api/connect/events/replay", body),
824
+ list: (query = {}) => this.delegated(appendQuery("/api/connect/events", queryParams(query))),
825
+ ack: (eventId, body = {}) => this.delegatedWrite(`/api/connect/events/${encodePathSegment(eventId)}/ack`, body),
711
826
  };
712
827
  proof = {
713
828
  query: (query) => this.delegated("/api/connect/proof/query", { method: "POST", body: query }),
@@ -718,6 +833,56 @@ export class ReceizClient {
718
833
  body: bodyWithIdempotency(body, body.idempotencyKey),
719
834
  headers: headersWithIdempotency(undefined, body.idempotencyKey),
720
835
  }),
836
+ status: (jobId) => this.delegated(`/api/connect/jobs/${encodePathSegment(jobId)}`),
837
+ cancel: (jobId, body = {}) => this.delegatedWrite(`/api/connect/jobs/${encodePathSegment(jobId)}/cancel`, body),
838
+ list: (query = {}) => this.delegated(appendQuery("/api/connect/jobs", queryParams(query))),
839
+ };
840
+ permissions = {
841
+ grant: (body, options = {}) => this.delegatedWrite("/api/connect/permissions/grants", body, options),
842
+ revoke: (body, options = {}) => this.delegatedWrite("/api/connect/permissions/revoke", body, options),
843
+ check: (body) => this.delegatedWrite("/api/connect/permissions/check", body),
844
+ roles: (query = {}) => this.delegated(appendQuery("/api/connect/permissions/roles", query)),
845
+ };
846
+ audit = {
847
+ append: (body, options = {}) => this.delegatedWrite("/api/connect/audit/events", body, options),
848
+ query: (query = {}) => this.delegated(appendQuery("/api/connect/audit/events", queryParams(query))),
849
+ export: (query = {}) => this.delegated(appendQuery("/api/connect/audit/export", queryParams(query))),
850
+ };
851
+ risk = {
852
+ scorePayment: (body) => this.delegatedWrite("/api/connect/risk/payment", body),
853
+ scoreAccountRecovery: (body) => this.delegatedWrite("/api/connect/risk/account-recovery", body),
854
+ velocity: (body) => this.delegatedWrite("/api/connect/risk/velocity", body),
855
+ proofActivity: (body) => this.delegatedWrite("/api/connect/risk/proof-activity", body),
856
+ };
857
+ compliance = {
858
+ exportOrders: (query) => this.delegated(appendQuery("/api/connect/compliance/orders", queryParams(query))),
859
+ exportTaxes: (query) => this.delegated(appendQuery("/api/connect/compliance/taxes", queryParams(query))),
860
+ exportPayouts: (query) => this.delegated(appendQuery("/api/connect/compliance/payouts", queryParams(query))),
861
+ exportCustomers: (query) => this.delegated(appendQuery("/api/connect/compliance/customers", queryParams(query))),
862
+ exportAudit: (query) => this.delegated(appendQuery("/api/connect/compliance/audit", queryParams(query))),
863
+ };
864
+ portability = {
865
+ exportStore: (query) => this.delegated(appendQuery("/api/connect/portability/store/export", query)),
866
+ importStore: (body, options = {}) => this.delegatedWrite("/api/connect/portability/store/import", body, options),
867
+ };
868
+ search = {
869
+ query: (body) => this.delegatedWrite("/api/connect/search/query", body),
870
+ products: (body) => this.delegatedWrite("/api/connect/search/products", body),
871
+ pages: (body) => this.delegatedWrite("/api/connect/search/pages", body),
872
+ blog: (body) => this.delegatedWrite("/api/connect/search/blog", body),
873
+ orders: (body) => this.delegatedWrite("/api/connect/search/orders", body),
874
+ customers: (body) => this.delegatedWrite("/api/connect/search/customers", body),
875
+ proofObjects: (body) => this.delegatedWrite("/api/connect/search/proof-objects", body),
876
+ };
877
+ notifications = {
878
+ send: (body, options = {}) => this.delegatedWrite("/api/connect/notifications/send", body, options),
879
+ subscribe: (body, options = {}) => this.delegatedWrite("/api/connect/notifications/subscribe", body, options),
880
+ templates: (query = {}) => this.delegated(appendQuery("/api/connect/notifications/templates", query)),
881
+ };
882
+ releases = {
883
+ pin: (body, options = {}) => this.delegatedWrite("/api/connect/releases/pin", body, options),
884
+ check: (query = {}) => this.delegated(appendQuery("/api/connect/releases/check", query)),
885
+ supported: () => this.request("/api/releases/rails/supported"),
721
886
  };
722
887
  webhooks = {
723
888
  sign: createReceizWebhookSignature,
@@ -775,6 +940,16 @@ export class ReceizClient {
775
940
  createLocalStorage: createReceizLocalStorageProofMemoryStorage,
776
941
  assertSnapshot: assertReceizProofRegisterSnapshot,
777
942
  additionsQuery: receizProofMemoryAdditionsQuery,
943
+ syncAdditions: (input) => this.syncProofMemoryAdditions(input),
944
+ };
945
+ runtime = {
946
+ localFirst: (input = {}) => this.runtimeLocalFirst(input),
947
+ };
948
+ offline = {
949
+ createQueue: createReceizOfflineProofQueue,
950
+ createInMemoryStorage: createReceizInMemoryOfflineProofQueueStorage,
951
+ createLocalStorage: createReceizLocalStorageOfflineProofQueueStorage,
952
+ assertSnapshot: assertReceizOfflineProofQueueSnapshot,
778
953
  };
779
954
  constructor(options = {}) {
780
955
  this.baseUrl = trimTrailingSlash(options.baseUrl ?? RECEIZ_DEFAULT_BASE_URL);
@@ -831,12 +1006,14 @@ export class ReceizClient {
831
1006
  const tenantHost = options.tenantHost ? normalizeReceizHost(options.tenantHost) : undefined;
832
1007
  const scopes = options.scopes ?? [];
833
1008
  const delegatedAvailable = Boolean(this.accessToken);
834
- const configured = (available, reason) => ({
1009
+ const configured = (available, reason, requirements) => ({
835
1010
  available,
836
1011
  status: available ? "available" : "missing",
837
1012
  ...(reason ? { reason } : {}),
838
1013
  ...(!available ? { fixUrl: `${this.baseUrl}/developers` } : {}),
1014
+ ...(requirements ? { requirements } : {}),
839
1015
  });
1016
+ const tenantAndBearer = { tenantHost: true, accessToken: true };
840
1017
  return {
841
1018
  ok: true,
842
1019
  schema: "receiz.sdk.capabilities.v1",
@@ -853,10 +1030,19 @@ export class ReceizClient {
853
1030
  twin: configured(delegatedAvailable, "Connect bearer token required for Twin routes."),
854
1031
  commerce: configured(delegatedAvailable && Boolean(tenantHost), "Connect bearer token and tenantHost are required for commerce helpers."),
855
1032
  rewards: configured(delegatedAvailable && Boolean(tenantHost), "Connect bearer token and tenantHost are required for reward routes."),
1033
+ customers: configured(delegatedAvailable && Boolean(tenantHost), "Connect bearer token and tenantHost are required for customer sessions.", tenantAndBearer),
1034
+ merchants: configured(delegatedAvailable && Boolean(tenantHost), "Connect bearer token and tenantHost are required for merchant rails.", tenantAndBearer),
856
1035
  events: configured(delegatedAvailable, "Connect bearer token required for event subscriptions."),
857
1036
  search: configured(delegatedAvailable, "Connect bearer token required for private proof search."),
858
1037
  permissions: configured(delegatedAvailable && Boolean(tenantHost), "Connect bearer token and tenantHost are required for tenant permissions."),
859
1038
  jobs: configured(delegatedAvailable, "Connect bearer token required for durable jobs."),
1039
+ audit: configured(delegatedAvailable && Boolean(tenantHost), "Connect bearer token and tenantHost are required for sealed tenant audit events."),
1040
+ risk: configured(delegatedAvailable && Boolean(tenantHost), "Connect bearer token and tenantHost are required for risk signals."),
1041
+ compliance: configured(delegatedAvailable && Boolean(tenantHost), "Connect bearer token and tenantHost are required for compliance exports."),
1042
+ portability: configured(delegatedAvailable && Boolean(tenantHost), "Connect bearer token and tenantHost are required for store portability."),
1043
+ notifications: configured(delegatedAvailable && Boolean(tenantHost), "Connect bearer token and tenantHost are required for notifications."),
1044
+ offline: configured(true),
1045
+ releases: configured(delegatedAvailable && Boolean(tenantHost), "Connect bearer token and tenantHost are required for release rail pinning."),
860
1046
  },
861
1047
  };
862
1048
  }
@@ -1011,6 +1197,167 @@ export class ReceizClient {
1011
1197
  }
1012
1198
  throw new ReceizValidationError("receiz.appState.resolve", ["id, url, host, tenantHost, namespace, creatorReceizId, or receizId is required"]);
1013
1199
  }
1200
+ async publishPublicStore(input, options = {}) {
1201
+ const tenantHost = normalizeReceizHost(input.tenantHost);
1202
+ const sourceUrl = input.sourceUrl ?? receizAppStateUrlFromHost(tenantHost);
1203
+ const publicStoreRecordInput = {
1204
+ sourceUrl,
1205
+ externalCreatorId: input.merchantReceizId,
1206
+ title: input.title ?? `${tenantHost} storefront`,
1207
+ namespace: input.namespace ?? tenantHost,
1208
+ state: input.projectionState ?? "published",
1209
+ schema: input.schema ?? RECEIZ_PUBLIC_STORE_STATE_PROJECTION_SCHEMA,
1210
+ platform: input.platform ?? "Receiz.app Commerce Cloud",
1211
+ record: input.record ?? input.state,
1212
+ data: input.data ?? {
1213
+ storeStateRecord: input.state,
1214
+ tenantHost,
1215
+ merchantReceizId: input.merchantReceizId,
1216
+ },
1217
+ };
1218
+ if (input.id)
1219
+ publicStoreRecordInput.id = input.id;
1220
+ const record = createReceizPublicStoreStateRecord(publicStoreRecordInput);
1221
+ const feedOptions = {
1222
+ schema: RECEIZ_PUBLIC_STORE_STATE_FEED_SCHEMA,
1223
+ externalCreatorId: input.merchantReceizId,
1224
+ };
1225
+ if (record.namespace)
1226
+ feedOptions.namespace = record.namespace;
1227
+ const publishOptions = {};
1228
+ const idempotencyKey = options.idempotencyKey ?? input.idempotencyKey;
1229
+ if (idempotencyKey)
1230
+ publishOptions.idempotencyKey = idempotencyKey;
1231
+ return this.publishAppState(createReceizAppStateFeed([record], feedOptions), publishOptions);
1232
+ }
1233
+ async resolvePublicStore(input) {
1234
+ const options = {
1235
+ schema: RECEIZ_PUBLIC_STORE_STATE_PROJECTION_SCHEMA,
1236
+ state: "published",
1237
+ requiredDataKey: "storeStateRecord",
1238
+ };
1239
+ let restored;
1240
+ if (input.id) {
1241
+ restored = await this.restoreAppStateById(input.id, options);
1242
+ }
1243
+ else if (input.url) {
1244
+ restored = await this.restoreAppStateByUrl(input.url, options);
1245
+ }
1246
+ else {
1247
+ const host = input.host ?? input.tenantHost;
1248
+ if (!host)
1249
+ throw new ReceizValidationError("receiz.publicStore.resolve", ["host, tenantHost, url, or id is required"]);
1250
+ restored = await this.restoreAppStateByHost(host, options);
1251
+ }
1252
+ const storeStateRecord = restored.data?.storeStateRecord ?? null;
1253
+ return {
1254
+ ...restored,
1255
+ state: storeStateRecord,
1256
+ storeStateRecord,
1257
+ };
1258
+ }
1259
+ async syncProofMemoryAdditions(input) {
1260
+ const knownHead = input.memory.knownHead(input.limit);
1261
+ const batch = await input.fetchAdditions(knownHead);
1262
+ let admitted = 0;
1263
+ for (const addition of proofMemoryAdditionValues(batch)) {
1264
+ admitProofMemoryAddition(input.memory, addition);
1265
+ admitted += 1;
1266
+ }
1267
+ await input.memory.flush();
1268
+ return {
1269
+ ok: true,
1270
+ schema: "receiz.sdk.proof_memory.sync.v1",
1271
+ knownHead,
1272
+ admitted,
1273
+ snapshot: input.memory.snapshot(),
1274
+ };
1275
+ }
1276
+ async resolveLocalFirstSurface(surface, input) {
1277
+ const local = input.local ?? null;
1278
+ const snapshot = input.memory?.snapshot() ?? null;
1279
+ const knownHead = input.memory ? input.memory.knownHead(input.limit) : null;
1280
+ const sync = input.memory && input.sync
1281
+ ? this.syncProofMemoryAdditions({
1282
+ memory: input.memory,
1283
+ fetchAdditions: input.sync,
1284
+ ...(input.limit === undefined ? {} : { limit: input.limit }),
1285
+ })
1286
+ : null;
1287
+ if (local !== null) {
1288
+ return {
1289
+ ok: true,
1290
+ schema: "receiz.sdk.local_first.v1",
1291
+ surface,
1292
+ source: "local",
1293
+ value: local,
1294
+ local,
1295
+ knownHead,
1296
+ snapshot,
1297
+ sync,
1298
+ };
1299
+ }
1300
+ const remote = input.remote ? await input.remote() : null;
1301
+ return {
1302
+ ok: true,
1303
+ schema: "receiz.sdk.local_first.v1",
1304
+ surface,
1305
+ source: remote !== null ? "remote" : "empty",
1306
+ value: remote,
1307
+ local,
1308
+ knownHead,
1309
+ snapshot,
1310
+ sync,
1311
+ };
1312
+ }
1313
+ async runtimeLocalFirst(input = {}) {
1314
+ const memoryOptions = {
1315
+ ...(input.ownerId === undefined ? {} : { ownerId: input.ownerId }),
1316
+ ...(input.storage === undefined ? {} : { storage: input.storage }),
1317
+ };
1318
+ const memory = await createReceizProofMemory(memoryOptions);
1319
+ for (const proofObject of input.seed ?? [])
1320
+ memory.admit(proofObject);
1321
+ await memory.flush();
1322
+ const knownHead = memory.knownHead(input.limit);
1323
+ const sync = input.sync
1324
+ ? this.syncProofMemoryAdditions({
1325
+ memory,
1326
+ fetchAdditions: input.sync,
1327
+ ...(input.limit === undefined ? {} : { limit: input.limit }),
1328
+ })
1329
+ : null;
1330
+ return {
1331
+ ok: true,
1332
+ schema: "receiz.sdk.runtime.local_first.v1",
1333
+ memory,
1334
+ snapshot: memory.snapshot(),
1335
+ knownHead,
1336
+ sync,
1337
+ };
1338
+ }
1339
+ async bootstrapCustomerSession(body, options = {}) {
1340
+ const tenantHost = normalizeReceizHost(body.tenantHost);
1341
+ const requestBody = {
1342
+ ...body,
1343
+ tenantHost,
1344
+ };
1345
+ const session = await this.delegatedWrite("/api/connect/customers/session", requestBody, options);
1346
+ const wallet = body.includeWallet === false ? null : await this.delegated("/api/connect/wallet/me");
1347
+ const customer = body.includeCustomer === false ? null : await this.delegatedWrite("/api/connect/customers/portal", requestBody);
1348
+ const permissions = body.includePermissions === false
1349
+ ? null
1350
+ : await this.delegated(appendQuery("/api/connect/permissions/roles", { tenantHost }));
1351
+ return {
1352
+ ok: true,
1353
+ schema: "receiz.sdk.customer_session.bootstrap.v1",
1354
+ tenantHost,
1355
+ session,
1356
+ wallet,
1357
+ customer,
1358
+ permissions,
1359
+ };
1360
+ }
1014
1361
  async oneClickCheckout(body) {
1015
1362
  const tenantHost = normalizeReceizHost(body.tenantHost);
1016
1363
  const totalUsdCents = usdToCents(body.amountUsd);
@@ -1123,6 +1470,14 @@ export class ReceizClient {
1123
1470
  throw new Error("receiz_access_token_required");
1124
1471
  return this.request(path, { ...options, bearerToken: this.accessToken });
1125
1472
  }
1473
+ async delegatedWrite(path, body = {}, options = {}) {
1474
+ const idempotencyKey = options.idempotencyKey ?? (typeof body.idempotencyKey === "string" ? body.idempotencyKey : undefined);
1475
+ return this.delegated(path, {
1476
+ method: "POST",
1477
+ body: bodyWithIdempotency(body, idempotencyKey),
1478
+ headers: headersWithIdempotency(undefined, idempotencyKey),
1479
+ });
1480
+ }
1126
1481
  async delegatedBlob(path, options = {}) {
1127
1482
  if (!this.accessToken)
1128
1483
  throw new Error("receiz_access_token_required");
@@ -1224,6 +1579,48 @@ export class ReceizClient {
1224
1579
  url.searchParams.set("login_hint", options.usernameHint);
1225
1580
  return url.toString();
1226
1581
  }
1582
+ ensureTenantSession(input) {
1583
+ const tenantHost = normalizeReceizHost(input.tenantHost);
1584
+ const fallback = input.fallback ?? "artifact_upload";
1585
+ const scope = Array.isArray(input.scope)
1586
+ ? input.scope
1587
+ : typeof input.scope === "string" && input.scope.trim()
1588
+ ? input.scope.trim().split(/\s+/)
1589
+ : receizOidcScopesForRails("identity", "customers", "publicStore");
1590
+ let url;
1591
+ if (input.clientId && input.codeChallenge) {
1592
+ const authorizeOptions = {
1593
+ clientId: input.clientId,
1594
+ redirectUri: input.redirectUri ?? input.returnTo ?? `${this.baseUrl}/developers/connect`,
1595
+ codeChallenge: input.codeChallenge,
1596
+ scope,
1597
+ };
1598
+ if (input.codeChallengeMethod)
1599
+ authorizeOptions.codeChallengeMethod = input.codeChallengeMethod;
1600
+ if (input.state)
1601
+ authorizeOptions.state = input.state;
1602
+ if (input.usernameHint)
1603
+ authorizeOptions.usernameHint = input.usernameHint;
1604
+ url = new URL(this.authorizeUrl(authorizeOptions));
1605
+ }
1606
+ else {
1607
+ url = new URL(`${this.baseUrl}/signin`);
1608
+ url.searchParams.set("lane", "connect");
1609
+ }
1610
+ url.searchParams.set("tenantHost", tenantHost);
1611
+ if (input.returnTo)
1612
+ url.searchParams.set("returnTo", input.returnTo);
1613
+ url.searchParams.set("fallback", fallback);
1614
+ return {
1615
+ ok: true,
1616
+ schema: "receiz.sdk.identity.tenant_session.ensure.v1",
1617
+ tenantHost,
1618
+ fallback,
1619
+ url: url.toString(),
1620
+ scope,
1621
+ ...(input.returnTo ? { returnTo: input.returnTo } : {}),
1622
+ };
1623
+ }
1227
1624
  async delegatedVerifyArtifact(file) {
1228
1625
  const form = new FormData();
1229
1626
  form.set("file", file);
@@ -1705,6 +2102,33 @@ function errorDetail(error) {
1705
2102
  return { name: error.name, message: error.message };
1706
2103
  return { message: String(error) };
1707
2104
  }
2105
+ function isProofRegisterEntry(value) {
2106
+ return isRecord(value) && typeof value.id === "string" && typeof value.kind === "string" && isRecord(value.payload);
2107
+ }
2108
+ function proofMemoryAdditionValues(batch) {
2109
+ if (Array.isArray(batch))
2110
+ return batch;
2111
+ if (isProofRegisterEntry(batch))
2112
+ return [batch];
2113
+ const out = [];
2114
+ for (const key of ["entries", "additions", "proofs", "records"]) {
2115
+ const value = batch[key];
2116
+ if (Array.isArray(value))
2117
+ out.push(...value);
2118
+ }
2119
+ return out;
2120
+ }
2121
+ function admitProofMemoryAddition(memory, addition) {
2122
+ if (isProofRegisterEntry(addition)) {
2123
+ memory.append(addition);
2124
+ return;
2125
+ }
2126
+ if (isRecord(addition) && isProofRegisterEntry(addition.entry)) {
2127
+ memory.append(addition.entry);
2128
+ return;
2129
+ }
2130
+ memory.admit(addition);
2131
+ }
1708
2132
  export class ReceizProofRegister {
1709
2133
  ownerId;
1710
2134
  createdAt;
@@ -1957,6 +2381,213 @@ export function receizProofMemoryAdditionsQuery(value, limit) {
1957
2381
  ...(limit === undefined ? {} : { limit }),
1958
2382
  };
1959
2383
  }
2384
+ function normalizeOfflineQueueItem(value) {
2385
+ const record = ensureRecord(value, "ReceizOfflineProofQueueItem");
2386
+ const issues = [];
2387
+ const id = ensureString(record, "id", issues);
2388
+ const kind = ensureString(record, "kind", issues);
2389
+ if (!isRecord(record.payload))
2390
+ issues.push("payload must be an object");
2391
+ const attempts = record.attempts;
2392
+ if (attempts !== undefined && (typeof attempts !== "number" || !Number.isInteger(attempts) || attempts < 0)) {
2393
+ issues.push("attempts must be a non-negative integer when present");
2394
+ }
2395
+ const item = {
2396
+ id: id ?? "",
2397
+ kind: (kind ?? ""),
2398
+ payload: isRecord(record.payload) ? record.payload : {},
2399
+ createdAt: typeof record.createdAt === "string" && record.createdAt.trim() ? record.createdAt : new Date().toISOString(),
2400
+ attempts: typeof attempts === "number" ? attempts : 0,
2401
+ };
2402
+ if (typeof record.idempotencyKey === "string" && record.idempotencyKey.trim())
2403
+ item.idempotencyKey = record.idempotencyKey;
2404
+ if (record.lastError === null || isRecord(record.lastError))
2405
+ item.lastError = record.lastError;
2406
+ failIfIssues("ReceizOfflineProofQueueItem", issues);
2407
+ return item;
2408
+ }
2409
+ export function assertReceizOfflineProofQueueSnapshot(value) {
2410
+ const record = ensureRecord(value, "ReceizOfflineProofQueueSnapshot");
2411
+ const issues = [];
2412
+ if (record.schema !== "receiz.sdk.offline_proof_queue.v1")
2413
+ issues.push("schema must be receiz.sdk.offline_proof_queue.v1");
2414
+ const ownerId = record.ownerId;
2415
+ if (ownerId !== null && ownerId !== undefined && typeof ownerId !== "string") {
2416
+ issues.push("ownerId must be a string or null when present");
2417
+ }
2418
+ const createdAt = ensureString(record, "createdAt", issues);
2419
+ const updatedAt = ensureString(record, "updatedAt", issues);
2420
+ if (!Array.isArray(record.pending))
2421
+ issues.push("pending must be an array");
2422
+ if (!Array.isArray(record.settled))
2423
+ issues.push("settled must be an array");
2424
+ if (!Array.isArray(record.failed))
2425
+ issues.push("failed must be an array");
2426
+ failIfIssues("ReceizOfflineProofQueueSnapshot", issues);
2427
+ return {
2428
+ schema: "receiz.sdk.offline_proof_queue.v1",
2429
+ ownerId: typeof ownerId === "string" || ownerId === null ? ownerId : null,
2430
+ createdAt: createdAt ?? "",
2431
+ updatedAt: updatedAt ?? "",
2432
+ pending: Array.isArray(record.pending) ? record.pending.map(normalizeOfflineQueueItem) : [],
2433
+ settled: Array.isArray(record.settled) ? record.settled.map(normalizeOfflineQueueItem) : [],
2434
+ failed: Array.isArray(record.failed) ? record.failed.map(normalizeOfflineQueueItem) : [],
2435
+ };
2436
+ }
2437
+ function parseOfflineProofQueueStorageValue(value) {
2438
+ if (value === null || value === undefined)
2439
+ return null;
2440
+ if (typeof value === "string") {
2441
+ const trimmed = value.trim();
2442
+ if (!trimmed)
2443
+ return null;
2444
+ return assertReceizOfflineProofQueueSnapshot(JSON.parse(trimmed));
2445
+ }
2446
+ return assertReceizOfflineProofQueueSnapshot(value);
2447
+ }
2448
+ export class ReceizOfflineProofQueue {
2449
+ ownerId;
2450
+ createdAt;
2451
+ pendingItems;
2452
+ settledItems;
2453
+ failedItems;
2454
+ storage;
2455
+ constructor(options = {}) {
2456
+ const snapshot = options.snapshot ? assertReceizOfflineProofQueueSnapshot(options.snapshot) : null;
2457
+ this.ownerId = options.ownerId ?? snapshot?.ownerId ?? null;
2458
+ this.createdAt = snapshot?.createdAt ?? new Date().toISOString();
2459
+ this.pendingItems = snapshot?.pending ? [...snapshot.pending] : [];
2460
+ this.settledItems = snapshot?.settled ? [...snapshot.settled] : [];
2461
+ this.failedItems = snapshot?.failed ? [...snapshot.failed] : [];
2462
+ this.storage = options.storage ?? null;
2463
+ }
2464
+ enqueue(item) {
2465
+ const normalized = normalizeOfflineQueueItem(item);
2466
+ if (this.pendingItems.some((pending) => pending.id === normalized.id))
2467
+ return this;
2468
+ if (this.settledItems.some((settled) => settled.id === normalized.id))
2469
+ return this;
2470
+ this.pendingItems.push(normalized);
2471
+ return this;
2472
+ }
2473
+ snapshot() {
2474
+ return {
2475
+ schema: "receiz.sdk.offline_proof_queue.v1",
2476
+ ownerId: this.ownerId,
2477
+ createdAt: this.createdAt,
2478
+ updatedAt: new Date().toISOString(),
2479
+ pending: [...this.pendingItems],
2480
+ settled: [...this.settledItems],
2481
+ failed: [...this.failedItems],
2482
+ };
2483
+ }
2484
+ async flush() {
2485
+ const snapshot = this.snapshot();
2486
+ if (this.storage)
2487
+ await this.storage.write(snapshot);
2488
+ return snapshot;
2489
+ }
2490
+ async clear() {
2491
+ this.pendingItems = [];
2492
+ this.settledItems = [];
2493
+ this.failedItems = [];
2494
+ if (this.storage?.remove)
2495
+ await this.storage.remove();
2496
+ }
2497
+ async replay(client) {
2498
+ const originalPending = [...this.pendingItems];
2499
+ let settled = 0;
2500
+ let failed = 0;
2501
+ this.pendingItems = [];
2502
+ for (const item of originalPending) {
2503
+ try {
2504
+ await replayOfflineProofQueueItem(client, item);
2505
+ this.settledItems.push({ ...item, attempts: (item.attempts ?? 0) + 1, lastError: null });
2506
+ settled += 1;
2507
+ }
2508
+ catch (error) {
2509
+ const failedItem = {
2510
+ ...item,
2511
+ attempts: (item.attempts ?? 0) + 1,
2512
+ lastError: errorDetail(error),
2513
+ };
2514
+ this.failedItems.push(failedItem);
2515
+ failed += 1;
2516
+ }
2517
+ }
2518
+ await this.flush();
2519
+ return { ok: failed === 0, settled, failed };
2520
+ }
2521
+ toJSON() {
2522
+ return this.snapshot();
2523
+ }
2524
+ }
2525
+ async function replayOfflineProofQueueItem(client, item) {
2526
+ if (item.kind === "app_state.publish") {
2527
+ return client.appState.publish(item.payload, item.idempotencyKey ? { idempotencyKey: item.idempotencyKey } : {});
2528
+ }
2529
+ if (item.kind === "connect.record") {
2530
+ return client.request("/api/connect/record", {
2531
+ method: "POST",
2532
+ body: bodyWithIdempotency(item.payload, item.idempotencyKey),
2533
+ headers: headersWithIdempotency(undefined, item.idempotencyKey),
2534
+ });
2535
+ }
2536
+ if (item.kind === "webhook.event") {
2537
+ return client.request("/api/connect/webhooks/replay", {
2538
+ method: "POST",
2539
+ body: bodyWithIdempotency(item.payload, item.idempotencyKey),
2540
+ headers: headersWithIdempotency(undefined, item.idempotencyKey),
2541
+ });
2542
+ }
2543
+ return client.request("/api/connect/offline/replay", {
2544
+ method: "POST",
2545
+ body: bodyWithIdempotency({ kind: item.kind, payload: item.payload }, item.idempotencyKey),
2546
+ headers: headersWithIdempotency(undefined, item.idempotencyKey),
2547
+ });
2548
+ }
2549
+ export async function createReceizOfflineProofQueue(options = {}) {
2550
+ const storedSnapshot = options.storage ? parseOfflineProofQueueStorageValue(await options.storage.read()) : null;
2551
+ const queueOptions = {
2552
+ ownerId: options.ownerId ?? storedSnapshot?.ownerId ?? null,
2553
+ };
2554
+ if (options.storage)
2555
+ queueOptions.storage = options.storage;
2556
+ const snapshot = options.snapshot ?? storedSnapshot;
2557
+ if (snapshot)
2558
+ queueOptions.snapshot = snapshot;
2559
+ return new ReceizOfflineProofQueue(queueOptions);
2560
+ }
2561
+ export function createReceizInMemoryOfflineProofQueueStorage(initialValue = null) {
2562
+ let storedText = typeof initialValue === "string"
2563
+ ? initialValue
2564
+ : initialValue
2565
+ ? JSON.stringify(assertReceizOfflineProofQueueSnapshot(initialValue))
2566
+ : null;
2567
+ return {
2568
+ read: () => storedText,
2569
+ write: (snapshot) => {
2570
+ storedText = JSON.stringify(assertReceizOfflineProofQueueSnapshot(snapshot));
2571
+ },
2572
+ remove: () => {
2573
+ storedText = null;
2574
+ },
2575
+ readText: () => storedText,
2576
+ };
2577
+ }
2578
+ export function createReceizLocalStorageOfflineProofQueueStorage(key = "receiz:offline-proof-queue:v1", storage = globalThis.localStorage) {
2579
+ if (!storage)
2580
+ throw new Error("receiz_local_storage_unavailable");
2581
+ return {
2582
+ read: () => storage.getItem(key),
2583
+ write: (snapshot) => {
2584
+ storage.setItem(key, JSON.stringify(assertReceizOfflineProofQueueSnapshot(snapshot)));
2585
+ },
2586
+ remove: () => {
2587
+ storage.removeItem(key);
2588
+ },
2589
+ };
2590
+ }
1960
2591
  function bodyToString(body) {
1961
2592
  if (typeof body === "string")
1962
2593
  return body;