@receiz/sdk 97.0.0 → 97.2.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.
@@ -0,0 +1,80 @@
1
+ import { type ComponentPropsWithoutRef, type ReactElement, type ReactNode } from "react";
2
+ import { type JsonObject, type ReceizAppStatePublishInput, type ReceizAppStateResolveInput, type ReceizAppStateResolveResult, type ReceizClient, type ReceizClientOptions, type ReceizMediaUploadOptions, type ReceizOneClickCheckoutRequest, type ReceizOneClickCheckoutResponse, type TwinPromotionApprovalInput } from "./index.js";
3
+ export type ReceizReactClientOptions = ReceizClientOptions & {
4
+ client?: ReceizClient;
5
+ };
6
+ export type ReceizAsyncState<T> = {
7
+ loading: boolean;
8
+ error: Error | null;
9
+ data: T | null;
10
+ };
11
+ export type ReceizIdentityHook = {
12
+ client: ReceizClient;
13
+ login: () => void;
14
+ logout: () => void;
15
+ authorizeUrl: string | null;
16
+ };
17
+ export type ReceizIdentityHookOptions = ReceizReactClientOptions & {
18
+ authorize?: {
19
+ clientId: string;
20
+ redirectUri: string;
21
+ codeChallenge: string;
22
+ codeChallengeMethod?: "S256" | "plain";
23
+ state?: string;
24
+ scope?: string | string[];
25
+ usernameHint?: string;
26
+ };
27
+ logoutUrl?: string;
28
+ };
29
+ export type ReceizCheckoutHook = ReceizAsyncState<ReceizOneClickCheckoutResponse> & {
30
+ checkout: (request: ReceizOneClickCheckoutRequest) => Promise<ReceizOneClickCheckoutResponse>;
31
+ };
32
+ export type ReceizAppStateHook<TData extends JsonObject = JsonObject> = ReceizAsyncState<ReceizAppStateResolveResult<TData>> & {
33
+ resolve: (input?: ReceizAppStateResolveInput) => Promise<ReceizAppStateResolveResult<TData>>;
34
+ publish: (input: ReceizAppStatePublishInput) => Promise<JsonObject>;
35
+ };
36
+ export type ReceizAppStateHookOptions<TData extends JsonObject = JsonObject> = ReceizReactClientOptions & {
37
+ resolve?: ReceizAppStateResolveInput;
38
+ initial?: ReceizAppStateResolveResult<TData> | null;
39
+ autoResolve?: boolean;
40
+ };
41
+ export type ReceizMediaHook = ReceizAsyncState<JsonObject> & {
42
+ upload: (file: Blob, options?: ReceizMediaUploadOptions) => Promise<JsonObject>;
43
+ };
44
+ type ReceizButtonProps = Omit<ComponentPropsWithoutRef<"button">, "onClick"> & {
45
+ children?: ReactNode;
46
+ };
47
+ type ReceizIdentityButtonProps = ReceizButtonProps & ReceizIdentityHookOptions;
48
+ type ReceizCheckoutButtonProps = ReceizButtonProps & ReceizReactClientOptions & {
49
+ request: ReceizOneClickCheckoutRequest;
50
+ onCheckout?: (response: ReceizOneClickCheckoutResponse) => void;
51
+ };
52
+ type ReceizWalletBalanceProps = ReceizReactClientOptions & {
53
+ tenantHost?: string;
54
+ fallback?: ReactNode;
55
+ };
56
+ type ReceizMediaUploaderProps = Omit<ComponentPropsWithoutRef<"input">, "type" | "onChange"> & ReceizReactClientOptions & {
57
+ uploadOptions?: ReceizMediaUploadOptions;
58
+ onUploaded?: (response: JsonObject) => void;
59
+ };
60
+ type ReceizDomainConnectorProps = ReceizButtonProps & ReceizReactClientOptions & {
61
+ host: string;
62
+ onStatus?: (response: JsonObject) => void;
63
+ };
64
+ type ReceizTwinWriterProps = Omit<ComponentPropsWithoutRef<"button">, "onClick"> & ReceizReactClientOptions & {
65
+ body: TwinPromotionApprovalInput;
66
+ onSaved?: (response: JsonObject) => void;
67
+ children?: ReactNode;
68
+ };
69
+ export declare function useReceizIdentity(options?: ReceizIdentityHookOptions): ReceizIdentityHook;
70
+ export declare function useReceizCheckout(options?: ReceizReactClientOptions): ReceizCheckoutHook;
71
+ export declare function useReceizAppState<TData extends JsonObject = JsonObject>(options?: ReceizAppStateHookOptions<TData>): ReceizAppStateHook<TData>;
72
+ export declare function useReceizMedia(options?: ReceizReactClientOptions): ReceizMediaHook;
73
+ export declare function ReceizIdentityButton(props: ReceizIdentityButtonProps): ReactElement;
74
+ export declare function ReceizCheckoutButton(props: ReceizCheckoutButtonProps): ReactElement;
75
+ export declare function ReceizWalletBalance(props: ReceizWalletBalanceProps): ReactElement;
76
+ export declare function ReceizMediaUploader(props: ReceizMediaUploaderProps): ReactElement;
77
+ export declare function ReceizDomainConnector(props: ReceizDomainConnectorProps): ReactElement;
78
+ export declare function ReceizTwinWriter(props: ReceizTwinWriterProps): ReactElement;
79
+ export {};
80
+ //# sourceMappingURL=react.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"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"}
package/dist/react.js ADDED
@@ -0,0 +1,200 @@
1
+ import { createElement, useCallback, useEffect, useMemo, useState, } from "react";
2
+ import { createReceizClient, } from "./index.js";
3
+ function useReceizClient(options = {}) {
4
+ const { client, baseUrl, accessToken, fetchImpl } = options;
5
+ return useMemo(() => client ?? createReceizClient(compactClientOptions({ baseUrl, accessToken, fetchImpl })), [client, baseUrl, accessToken, fetchImpl]);
6
+ }
7
+ function compactClientOptions(options) {
8
+ const next = {};
9
+ if (options.baseUrl !== undefined)
10
+ next.baseUrl = options.baseUrl;
11
+ if (options.accessToken !== undefined)
12
+ next.accessToken = options.accessToken;
13
+ if (options.fetchImpl !== undefined)
14
+ next.fetchImpl = options.fetchImpl;
15
+ return next;
16
+ }
17
+ function compactReactClientOptions(options) {
18
+ const next = compactClientOptions(options);
19
+ if (options.client !== undefined)
20
+ next.client = options.client;
21
+ return next;
22
+ }
23
+ function compactIdentityOptions(options) {
24
+ const next = compactReactClientOptions(options);
25
+ if (options.authorize !== undefined)
26
+ next.authorize = options.authorize;
27
+ if (options.logoutUrl !== undefined)
28
+ next.logoutUrl = options.logoutUrl;
29
+ return next;
30
+ }
31
+ function errorFromUnknown(error) {
32
+ return error instanceof Error ? error : new Error(String(error));
33
+ }
34
+ export function useReceizIdentity(options = {}) {
35
+ const client = useReceizClient(options);
36
+ const authorizeUrl = options.authorize ? client.identity.authorizeUrl(options.authorize) : null;
37
+ const login = useCallback(() => {
38
+ if (!authorizeUrl || typeof window === "undefined")
39
+ return;
40
+ window.location.assign(authorizeUrl);
41
+ }, [authorizeUrl]);
42
+ const logout = useCallback(() => {
43
+ if (typeof window === "undefined")
44
+ return;
45
+ window.location.assign(options.logoutUrl ?? "/logout");
46
+ }, [options.logoutUrl]);
47
+ return { client, login, logout, authorizeUrl };
48
+ }
49
+ export function useReceizCheckout(options = {}) {
50
+ const client = useReceizClient(options);
51
+ const [state, setState] = useState({
52
+ loading: false,
53
+ error: null,
54
+ data: null,
55
+ });
56
+ const checkout = useCallback(async (request) => {
57
+ setState((current) => ({ ...current, loading: true, error: null }));
58
+ try {
59
+ const response = await client.commerce.oneClickCheckout(request);
60
+ setState({ loading: false, error: null, data: response });
61
+ return response;
62
+ }
63
+ catch (error) {
64
+ const nextError = errorFromUnknown(error);
65
+ setState({ loading: false, error: nextError, data: null });
66
+ throw nextError;
67
+ }
68
+ }, [client]);
69
+ return { ...state, checkout };
70
+ }
71
+ export function useReceizAppState(options = {}) {
72
+ const client = useReceizClient(options);
73
+ const [state, setState] = useState({
74
+ loading: false,
75
+ error: null,
76
+ data: options.initial ?? null,
77
+ });
78
+ const resolve = useCallback(async (input) => {
79
+ const target = input ?? options.resolve;
80
+ if (!target)
81
+ throw new Error("receiz_app_state_resolve_input_required");
82
+ setState((current) => ({ ...current, loading: true, error: null }));
83
+ try {
84
+ const response = await client.appState.resolve(target);
85
+ setState({ loading: false, error: null, data: response });
86
+ return response;
87
+ }
88
+ catch (error) {
89
+ const nextError = errorFromUnknown(error);
90
+ setState({ loading: false, error: nextError, data: null });
91
+ throw nextError;
92
+ }
93
+ }, [client, options.resolve]);
94
+ const publish = useCallback(async (input) => {
95
+ const response = await client.appState.publish(input);
96
+ return response;
97
+ }, [client]);
98
+ useEffect(() => {
99
+ if (options.autoResolve === false || !options.resolve || options.initial)
100
+ return;
101
+ let active = true;
102
+ const target = options.resolve;
103
+ queueMicrotask(() => {
104
+ if (!active)
105
+ return;
106
+ void resolve(target);
107
+ });
108
+ return () => {
109
+ active = false;
110
+ };
111
+ }, [options.autoResolve, options.initial, options.resolve, resolve]);
112
+ return { ...state, resolve, publish };
113
+ }
114
+ export function useReceizMedia(options = {}) {
115
+ const client = useReceizClient(options);
116
+ const [state, setState] = useState({
117
+ loading: false,
118
+ error: null,
119
+ data: null,
120
+ });
121
+ const upload = useCallback(async (file, uploadOptions = {}) => {
122
+ setState((current) => ({ ...current, loading: true, error: null }));
123
+ try {
124
+ const response = await client.media.upload(file, uploadOptions);
125
+ const data = response;
126
+ setState({ loading: false, error: null, data });
127
+ return data;
128
+ }
129
+ catch (error) {
130
+ const nextError = errorFromUnknown(error);
131
+ setState({ loading: false, error: nextError, data: null });
132
+ throw nextError;
133
+ }
134
+ }, [client]);
135
+ return { ...state, upload };
136
+ }
137
+ export function ReceizIdentityButton(props) {
138
+ const { children, client, baseUrl, accessToken, fetchImpl, authorize, logoutUrl, ...buttonProps } = props;
139
+ const identity = useReceizIdentity(compactIdentityOptions({ client, baseUrl, accessToken, fetchImpl, authorize, logoutUrl }));
140
+ return createElement("button", { ...buttonProps, type: buttonProps.type ?? "button", onClick: identity.login }, children ?? "Continue with Receiz");
141
+ }
142
+ export function ReceizCheckoutButton(props) {
143
+ const { children, client, baseUrl, accessToken, fetchImpl, request, onCheckout, ...buttonProps } = props;
144
+ const checkout = useReceizCheckout(compactReactClientOptions({ client, baseUrl, accessToken, fetchImpl }));
145
+ const onClick = useCallback(async () => {
146
+ const response = await checkout.checkout(request);
147
+ onCheckout?.(response);
148
+ }, [checkout, onCheckout, request]);
149
+ return createElement("button", { ...buttonProps, type: buttonProps.type ?? "button", disabled: buttonProps.disabled || checkout.loading, onClick }, children ?? (checkout.loading ? "Opening..." : "Checkout with Receiz"));
150
+ }
151
+ export function ReceizWalletBalance(props) {
152
+ const { client: providedClient, baseUrl, accessToken, fetchImpl, fallback = "Wallet unavailable" } = props;
153
+ const client = useReceizClient(compactReactClientOptions({ client: providedClient, baseUrl, accessToken, fetchImpl }));
154
+ const [balance, setBalance] = useState(null);
155
+ useEffect(() => {
156
+ let active = true;
157
+ void client.connect.wallet().then((wallet) => {
158
+ if (!active)
159
+ return;
160
+ setBalance(typeof wallet.balanceUsdCents === "string" ? `$${(Number(wallet.balanceUsdCents) / 100).toFixed(2)}` : null);
161
+ }).catch(() => {
162
+ if (active)
163
+ setBalance(null);
164
+ });
165
+ return () => {
166
+ active = false;
167
+ };
168
+ }, [client]);
169
+ return createElement("span", null, balance ?? fallback);
170
+ }
171
+ export function ReceizMediaUploader(props) {
172
+ const { client, baseUrl, accessToken, fetchImpl, uploadOptions, onUploaded, ...inputProps } = props;
173
+ const media = useReceizMedia(compactReactClientOptions({ client, baseUrl, accessToken, fetchImpl }));
174
+ const onChange = useCallback(async (event) => {
175
+ const file = event.currentTarget.files?.[0];
176
+ if (!file)
177
+ return;
178
+ const response = await media.upload(file, uploadOptions);
179
+ onUploaded?.(response);
180
+ }, [media, onUploaded, uploadOptions]);
181
+ return createElement("input", { ...inputProps, type: "file", onChange });
182
+ }
183
+ export function ReceizDomainConnector(props) {
184
+ const { children, client: providedClient, baseUrl, accessToken, fetchImpl, host, onStatus, ...buttonProps } = props;
185
+ const client = useReceizClient(compactReactClientOptions({ client: providedClient, baseUrl, accessToken, fetchImpl }));
186
+ const onClick = useCallback(async () => {
187
+ const response = await client.domains.status({ host });
188
+ onStatus?.(response);
189
+ }, [client, host, onStatus]);
190
+ return createElement("button", { ...buttonProps, type: buttonProps.type ?? "button", onClick }, children ?? "Check Receiz Domain");
191
+ }
192
+ export function ReceizTwinWriter(props) {
193
+ const { children, client: providedClient, baseUrl, accessToken, fetchImpl, body, onSaved, ...buttonProps } = props;
194
+ const client = useReceizClient(compactReactClientOptions({ client: providedClient, baseUrl, accessToken, fetchImpl }));
195
+ const onClick = useCallback(async () => {
196
+ const response = await client.twin.approvePromotion(body);
197
+ onSaved?.(response);
198
+ }, [body, client, onSaved]);
199
+ return createElement("button", { ...buttonProps, type: buttonProps.type ?? "button", onClick }, children ?? "Write Twin Proof");
200
+ }
@@ -0,0 +1,236 @@
1
+ # Receiz App Runtime And Commerce Cloud Rails
2
+
3
+ `@receiz/sdk` exposes typed rails for apps that want Receiz identity, public app-state, commerce, media, domains, events, proof search, jobs, permissions, audit, risk, compliance, portability, notifications, release pinning, offline proof queues, and diagnostics without stitching a separate app backend together first.
4
+
5
+ The SDK is convenience. It does not become proof authority. Proof objects, verified local truth, app-state projections, Connect delegated routes, wallet settlement, identity artifacts, and durable append rails remain the source of truth.
6
+
7
+ ## Self-Debug First
8
+
9
+ ```ts
10
+ import { createReceizClient } from "@receiz/sdk";
11
+
12
+ const receiz = createReceizClient({ accessToken });
13
+
14
+ const doctor = await receiz.doctor({
15
+ tenantHost: "bjklock.receiz.app",
16
+ callbackUrl: "https://bjklock.receiz.app/api/auth/receiz/callback",
17
+ scopes: ["openid", "profile", "receiz:record", "receiz:wallet.read"],
18
+ });
19
+
20
+ if (!doctor.ok) {
21
+ console.table(doctor.fixes);
22
+ }
23
+
24
+ const caps = await receiz.capabilities({ tenantHost: "bjklock.receiz.app" });
25
+ if (!caps.capabilities.commerce.available) {
26
+ // Hide checkout setup or show the exact Connect fix.
27
+ }
28
+ ```
29
+
30
+ ## Publish And Resolve Tenant State
31
+
32
+ ```ts
33
+ await receiz.appState.publish({
34
+ tenantHost: "bjklock.receiz.app",
35
+ creatorReceizId: "bjklock.receiz.id",
36
+ title: "BJ Klock storefront",
37
+ state: { storeStateRecord },
38
+ idempotencyKey: `storefront:${storeStateRecord.updatedKaiUpulse}`,
39
+ });
40
+
41
+ const restored = await createReceizClient().appState.resolve<{
42
+ storeStateRecord: StoreStateRecord;
43
+ }>({
44
+ host: "bjklock.receiz.app",
45
+ requiredDataKey: "storeStateRecord",
46
+ });
47
+
48
+ if (restored.ok && restored.data) {
49
+ renderStorefront(restored.data.storeStateRecord);
50
+ }
51
+ ```
52
+
53
+ ## One-Click Checkout
54
+
55
+ ```ts
56
+ const checkout = await receiz.commerce.oneClickCheckout({
57
+ tenantHost: "bjklock.receiz.app",
58
+ orderId: "order_123",
59
+ amountUsd: "42.00",
60
+ walletFirst: true,
61
+ cardFallback: true,
62
+ idempotencyKey: "order_123",
63
+ cart,
64
+ });
65
+
66
+ if (checkout.checkoutSession?.clientSecret) {
67
+ openEmbeddedCardDeltaPanel(checkout.checkoutSession.clientSecret);
68
+ }
69
+ ```
70
+
71
+ Wallet-first checkout reads the delegated wallet summary, computes the card delta, and creates an embedded Connect checkout session only when a card delta remains. Settlement truth still belongs to the Connect/payment and wallet rails returned by the backend.
72
+
73
+ ## Enterprise Commerce
74
+
75
+ ```ts
76
+ await receiz.customers.session({
77
+ tenantHost: "bjklock.receiz.app",
78
+ customerReceizId: "customer.receiz.id",
79
+ scopes: ["orders", "rewards", "assets"],
80
+ }, { idempotencyKey: "customer-session:v1" });
81
+
82
+ await receiz.merchants.onboard({
83
+ tenantHost: "bjklock.receiz.app",
84
+ receizId: "bjklock.receiz.id",
85
+ displayName: "BJ Klock",
86
+ defaultStore: { title: "BJ Klock Store" },
87
+ }, { idempotencyKey: "merchant:bjklock:v1" });
88
+
89
+ await receiz.commerce.inventory.reserve({
90
+ tenantHost: "bjklock.receiz.app",
91
+ sku: "hat-black",
92
+ quantity: 2,
93
+ }, { idempotencyKey: "inventory:hat-black:cart-123" });
94
+
95
+ await receiz.commerce.fulfillment.update({
96
+ tenantHost: "bjklock.receiz.app",
97
+ orderId: "order_123",
98
+ status: "shipped",
99
+ }, { idempotencyKey: "fulfillment:order_123:shipped" });
100
+ ```
101
+
102
+ Additional commerce rails include refunds, subscriptions, shipping quotes, tax quotes, discounts, gift cards, access passes, inventory adjustment, and merchant payouts. Every write accepts an idempotency key so retries append once.
103
+
104
+ ## Media, Domains, Events, Search, And Jobs
105
+
106
+ ```ts
107
+ const logo = await receiz.media.upload(file, {
108
+ tenantHost: "bjklock.receiz.app",
109
+ purpose: "store.logo",
110
+ filename: "logo.png",
111
+ idempotencyKey: "store-logo:v1",
112
+ });
113
+
114
+ const tenantUrl = receiz.domains.tenantUrl("bjklock.receiz.app");
115
+ const namespace = receiz.domains.namespace(tenantUrl);
116
+
117
+ await receiz.events.subscribe({
118
+ tenantHost: "bjklock.receiz.app",
119
+ types: ["order.created", "payment.settled", "reward.claimed"],
120
+ webhookUrl: "https://bjklock.receiz.app/api/receiz/webhooks",
121
+ idempotencyKey: "events:v1",
122
+ });
123
+
124
+ const orders = await receiz.proof.query({
125
+ namespace,
126
+ tenantHost: "bjklock.receiz.app",
127
+ type: "commerce.order",
128
+ });
129
+
130
+ await receiz.jobs.enqueue({
131
+ tenantHost: "bjklock.receiz.app",
132
+ kind: "media.process",
133
+ payload: { mediaId: logo.media?.id },
134
+ idempotencyKey: "media.process:logo:v1",
135
+ });
136
+
137
+ await receiz.media.transform({
138
+ tenantHost: "bjklock.receiz.app",
139
+ mediaId: String(logo.media?.id),
140
+ resize: { width: 1200 },
141
+ optimize: { format: "webp" },
142
+ blurPlaceholder: true,
143
+ altText: "Store logo",
144
+ }, { idempotencyKey: "media.transform:logo:v1" });
145
+
146
+ await receiz.events.replay({
147
+ tenantHost: "bjklock.receiz.app",
148
+ types: ["order.created", "payment.settled"],
149
+ since: "2026-06-01T00:00:00.000Z",
150
+ });
151
+ ```
152
+
153
+ ## Permissions, Audit, Risk, Compliance, And Portability
154
+
155
+ ```ts
156
+ await receiz.permissions.grant({
157
+ tenantHost: "bjklock.receiz.app",
158
+ actorReceizId: "staff.receiz.id",
159
+ role: "fulfillment",
160
+ }, { idempotencyKey: "grant:staff:fulfillment" });
161
+
162
+ await receiz.audit.append({
163
+ tenantHost: "bjklock.receiz.app",
164
+ action: "product.updated",
165
+ actorReceizId: "bjklock.receiz.id",
166
+ }, { idempotencyKey: "audit:product:update:123" });
167
+
168
+ const risk = await receiz.risk.scorePayment({
169
+ tenantHost: "bjklock.receiz.app",
170
+ orderId: "order_123",
171
+ amountUsd: "42.00",
172
+ });
173
+
174
+ const orders = await receiz.compliance.exportOrders({
175
+ tenantHost: "bjklock.receiz.app",
176
+ from: "2026-06-01",
177
+ to: "2026-06-30",
178
+ });
179
+
180
+ const portableStore = await receiz.portability.exportStore({
181
+ tenantHost: "bjklock.receiz.app",
182
+ });
183
+ ```
184
+
185
+ Audit, risk, compliance, and portability are projections over proof-native activity. They help applications operate; they do not outrank the proof objects, wallet settlement, or verified appends they project.
186
+
187
+ ## Offline Queue And Release Pinning
188
+
189
+ ```ts
190
+ const storage = receiz.offline.createLocalStorage("receiz:queue:bjklock");
191
+ const queue = await receiz.offline.createQueue({
192
+ ownerId: "bjklock.receiz.id",
193
+ storage,
194
+ });
195
+
196
+ queue.enqueue({
197
+ id: "store-state:offline-1",
198
+ kind: "app_state.publish",
199
+ payload: {
200
+ tenantHost: "bjklock.receiz.app",
201
+ title: "Offline storefront append",
202
+ state: { storeStateRecord },
203
+ },
204
+ idempotencyKey: "store-state:offline-1",
205
+ });
206
+ await queue.flush();
207
+
208
+ await queue.replay(receiz);
209
+
210
+ await receiz.releases.pin({
211
+ tenantHost: "bjklock.receiz.app",
212
+ rails: { appState: "v1", commerce: "v1", media: "v1" },
213
+ }, { idempotencyKey: "rails:pin:v1" });
214
+ ```
215
+
216
+ ## Deterministic Sandbox
217
+
218
+ ```ts
219
+ const store = receiz.sandbox.seedStore({ tenantHost: "demo.receiz.app", products: 3 });
220
+ const wallet = receiz.sandbox.wallet({ balanceUsdCents: "5000" });
221
+ const checkout = receiz.sandbox.checkout({ amountUsd: "19.95", outcome: "success" });
222
+ ```
223
+
224
+ The sandbox is local and deterministic. Use it for tests, demos, and AI-generated apps before wiring delegated Connect access.
225
+
226
+ ## CLI
227
+
228
+ ```bash
229
+ receiz doctor --tenant-host bjklock.receiz.app --access-token "$RECEIZ_ACCESS_TOKEN"
230
+ receiz dev
231
+ receiz deploy-check --tenant-host bjklock.receiz.app
232
+ receiz seed-store --tenant-host demo.receiz.app --products 3
233
+ receiz simulate-checkout --amount-usd 19.95
234
+ ```
235
+
236
+ These commands do not make the CLI a truth authority. They are local developer diagnostics and sandbox helpers.
@@ -10,6 +10,7 @@ Use this for ecommerce storefronts, marketplaces, games, rewards, profiles, dire
10
10
  import {
11
11
  RECEIZ_PUBLIC_STORE_STATE_PROJECTION_SCHEMA,
12
12
  createReceizClient,
13
+ createReceizPublicStoreStateRecord,
13
14
  } from "@receiz/sdk";
14
15
 
15
16
  const receiz = createReceizClient({
@@ -17,39 +18,113 @@ const receiz = createReceizClient({
17
18
  accessToken,
18
19
  });
19
20
 
20
- const published = await receiz.appState.publishRecord({
21
+ const storeProjection = createReceizPublicStoreStateRecord({
21
22
  sourceUrl: "bjklock.receiz.app",
22
23
  externalCreatorId: "bjklock.receiz.id",
23
24
  title: "BJ Klock storefront",
24
- platform: "Receiz.app Commerce Cloud",
25
- schema: RECEIZ_PUBLIC_STORE_STATE_PROJECTION_SCHEMA,
26
25
  data: {
27
26
  storeStateRecord,
28
27
  },
29
28
  });
29
+
30
+ const published = await receiz.appState.publishRecord(storeProjection);
30
31
  if (!published.ok) throw new Error(String(published.error ?? "app_state_publish_failed"));
31
32
  ```
32
33
 
33
34
  `publishRecord()` wraps a single durable projection into the canonical feed envelope, derives the default object id from the URL, and normalizes a bare host such as `bjklock.receiz.app` to `https://bjklock.receiz.app`. Use `receiz.appState.publish(feed)` for batch writes. `receiz.publicProof.registryFeed(feed)` remains supported and aliases the same publish path.
34
35
 
36
+ For Commerce Cloud and tenant apps, the shortest path is the tenant publish helper:
37
+
38
+ ```ts
39
+ await receiz.appState.publish({
40
+ tenantHost: "bjklock.receiz.app",
41
+ creatorReceizId: "bjklock.receiz.id",
42
+ title: "BJ Klock storefront",
43
+ state: { storeStateRecord },
44
+ idempotencyKey: `storefront:${storeStateRecord.updatedKaiUpulse}`,
45
+ });
46
+ ```
47
+
48
+ The SDK turns that into the canonical public app-state feed, derives the normalized URL, derives the object id, carries the `idempotency-key` header, and keeps the projection under the public proof registry rail.
49
+
50
+ For batch writes, build the canonical feed first:
51
+
52
+ ```ts
53
+ import { createReceizAppStateFeed } from "@receiz/sdk";
54
+
55
+ const feed = createReceizAppStateFeed([storeProjection], {
56
+ schema: "receiz.app.public_store_state_registry_feed.v1",
57
+ externalCreatorId: "bjklock.receiz.id",
58
+ });
59
+
60
+ await receiz.appState.publish(feed);
61
+ ```
62
+
63
+ The same factories are available from the client namespace:
64
+
65
+ ```ts
66
+ const objectId = receiz.appState.objectIdFromUrl("https://bjklock.receiz.app");
67
+ const namespace = receiz.appState.namespaceFromUrl("https://bjklock.receiz.app");
68
+ const url = receiz.appState.urlFromHost("bjklock.receiz.app");
69
+ const record = receiz.appState.createPublicStoreRecord({
70
+ sourceUrl: url,
71
+ data: { storeStateRecord },
72
+ });
73
+ ```
74
+
35
75
  ## Recover On Cold Start
36
76
 
37
77
  ```ts
38
- import { createReceizClient } from "@receiz/sdk";
78
+ import {
79
+ RECEIZ_PUBLIC_STORE_STATE_PROJECTION_SCHEMA,
80
+ createReceizClient,
81
+ } from "@receiz/sdk";
39
82
 
40
83
  const receiz = createReceizClient();
41
- const restored = await receiz.appState.byHost("bjklock.receiz.app");
84
+ const restored = await receiz.appState.restoreByHost<{
85
+ storeStateRecord: StoreStateRecord;
86
+ }>("bjklock.receiz.app", {
87
+ schema: RECEIZ_PUBLIC_STORE_STATE_PROJECTION_SCHEMA,
88
+ state: "published",
89
+ requiredDataKey: "storeStateRecord",
90
+ });
42
91
 
43
- if (!restored.ok || !restored.record?.data) {
92
+ if (!restored.ok || !restored.data) {
44
93
  throw new Error("storefront_state_not_found");
45
94
  }
46
95
 
47
- const storeStateRecord = restored.record.data.storeStateRecord;
96
+ const storeStateRecord = restored.data.storeStateRecord;
48
97
  ```
49
98
 
50
99
  `https://bjklock.receiz.app` and `https://bjklock.receiz.app/` resolve to the same public projection. Subdomains and custom domains work through the same normalized URL key.
51
100
 
52
101
  Use `byUrl()` when the app already has a full URL. Use `byHost()` when the current request gives you only the tenant host or custom domain.
102
+ Use `restoreByUrl()`, `restoreByHost()`, or `restoreById()` when the app should receive the typed projection data directly for first paint.
103
+ Use `resolve()` when your app accepts host, URL, id, namespace, or creator input and wants one typed API:
104
+
105
+ ```ts
106
+ const restored = await receiz.appState.resolve<{
107
+ storeStateRecord: StoreStateRecord;
108
+ }>({
109
+ host: "bjklock.receiz.app",
110
+ schema: RECEIZ_PUBLIC_STORE_STATE_PROJECTION_SCHEMA,
111
+ state: "published",
112
+ requiredDataKey: "storeStateRecord",
113
+ });
114
+ ```
115
+
116
+ When your app already has a public projection response, unwrap it without another network call:
117
+
118
+ ```ts
119
+ import { extractReceizAppStateData } from "@receiz/sdk";
120
+
121
+ const data = extractReceizAppStateData<{
122
+ storeStateRecord: StoreStateRecord;
123
+ }>(restored, {
124
+ schema: RECEIZ_PUBLIC_STORE_STATE_PROJECTION_SCHEMA,
125
+ requiredDataKey: "storeStateRecord",
126
+ });
127
+ ```
53
128
 
54
129
  ## Namespace And Creator Reads
55
130
 
@@ -93,6 +93,34 @@ The starter:
93
93
 
94
94
  It does not include Supabase, service-role keys, or a parallel source of truth.
95
95
 
96
+ ## Diagnose App Runtime Wiring
97
+
98
+ ```bash
99
+ npx @receiz/sdk doctor \
100
+ --tenant-host bjklock.receiz.app \
101
+ --access-token "$RECEIZ_ACCESS_TOKEN" \
102
+ --callback-url https://bjklock.receiz.app/api/auth/receiz/callback \
103
+ --scopes openid,profile,receiz:record,receiz:wallet.read
104
+ ```
105
+
106
+ The doctor command prints missing token, tenant host, callback URL, and scope evidence without making network or database calls.
107
+
108
+ ```bash
109
+ npx @receiz/sdk deploy-check --tenant-host bjklock.receiz.app
110
+ npx @receiz/sdk dev
111
+ ```
112
+
113
+ Use these commands before deploy to check that the app is passing the tenant coordinate and that the SDK version is pinned.
114
+
115
+ ## Deterministic Sandbox Commands
116
+
117
+ ```bash
118
+ npx @receiz/sdk seed-store --tenant-host demo.receiz.app --products 3
119
+ npx @receiz/sdk simulate-checkout --amount-usd 19.95
120
+ ```
121
+
122
+ These commands emit deterministic sandbox app-state and checkout payloads for tests, demos, and AI-generated app scaffolds. They do not settle payments or create proof authority.
123
+
96
124
  ## Production Rule
97
125
 
98
126
  Use the CLI to prove your local integration shape, then use the typed SDK APIs in your app: