@receiz/sdk 96.2.0 → 97.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 +72 -17
- package/dist/cli.js +106 -2
- package/dist/index.d.ts +350 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +578 -5
- package/docs/app-runtime-commerce-cloud.md +127 -0
- package/docs/app-state-public-projections.md +96 -25
- package/docs/cli-and-conformance.md +28 -0
- package/package.json +1 -1
|
@@ -0,0 +1,127 @@
|
|
|
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, 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
|
+
## Media, Domains, Events, Search, And Jobs
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
const logo = await receiz.media.upload(file, {
|
|
77
|
+
tenantHost: "bjklock.receiz.app",
|
|
78
|
+
purpose: "store.logo",
|
|
79
|
+
filename: "logo.png",
|
|
80
|
+
idempotencyKey: "store-logo:v1",
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const tenantUrl = receiz.domains.tenantUrl("bjklock.receiz.app");
|
|
84
|
+
const namespace = receiz.domains.namespace(tenantUrl);
|
|
85
|
+
|
|
86
|
+
await receiz.events.subscribe({
|
|
87
|
+
tenantHost: "bjklock.receiz.app",
|
|
88
|
+
types: ["order.created", "payment.settled", "reward.claimed"],
|
|
89
|
+
webhookUrl: "https://bjklock.receiz.app/api/receiz/webhooks",
|
|
90
|
+
idempotencyKey: "events:v1",
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const orders = await receiz.proof.query({
|
|
94
|
+
namespace,
|
|
95
|
+
tenantHost: "bjklock.receiz.app",
|
|
96
|
+
type: "commerce.order",
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
await receiz.jobs.enqueue({
|
|
100
|
+
tenantHost: "bjklock.receiz.app",
|
|
101
|
+
kind: "media.process",
|
|
102
|
+
payload: { mediaId: logo.media?.id },
|
|
103
|
+
idempotencyKey: "media.process:logo:v1",
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Deterministic Sandbox
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
const store = receiz.sandbox.seedStore({ tenantHost: "demo.receiz.app", products: 3 });
|
|
111
|
+
const wallet = receiz.sandbox.wallet({ balanceUsdCents: "5000" });
|
|
112
|
+
const checkout = receiz.sandbox.checkout({ amountUsd: "19.95", outcome: "success" });
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The sandbox is local and deterministic. Use it for tests, demos, and AI-generated apps before wiring delegated Connect access.
|
|
116
|
+
|
|
117
|
+
## CLI
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
receiz doctor --tenant-host bjklock.receiz.app --access-token "$RECEIZ_ACCESS_TOKEN"
|
|
121
|
+
receiz dev
|
|
122
|
+
receiz deploy-check --tenant-host bjklock.receiz.app
|
|
123
|
+
receiz seed-store --tenant-host demo.receiz.app --products 3
|
|
124
|
+
receiz simulate-checkout --amount-usd 19.95
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
These commands do not make the CLI a truth authority. They are local developer diagnostics and sandbox helpers.
|
|
@@ -7,54 +7,125 @@ Use this for ecommerce storefronts, marketplaces, games, rewards, profiles, dire
|
|
|
7
7
|
## Publish After Admin Save
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
RECEIZ_PUBLIC_STORE_STATE_PROJECTION_SCHEMA,
|
|
12
|
+
createReceizClient,
|
|
13
|
+
createReceizPublicStoreStateRecord,
|
|
14
|
+
} from "@receiz/sdk";
|
|
11
15
|
|
|
12
16
|
const receiz = createReceizClient({
|
|
13
17
|
baseUrl: "https://receiz.com",
|
|
14
18
|
accessToken,
|
|
15
19
|
});
|
|
16
20
|
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
schema: "receiz.app.public_store_state_projection.v1",
|
|
28
|
-
data: {
|
|
29
|
-
storeStateRecord,
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
],
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const published = await receiz.appState.publish(feed);
|
|
21
|
+
const storeProjection = createReceizPublicStoreStateRecord({
|
|
22
|
+
sourceUrl: "bjklock.receiz.app",
|
|
23
|
+
externalCreatorId: "bjklock.receiz.id",
|
|
24
|
+
title: "BJ Klock storefront",
|
|
25
|
+
data: {
|
|
26
|
+
storeStateRecord,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const published = await receiz.appState.publishRecord(storeProjection);
|
|
36
31
|
if (!published.ok) throw new Error(String(published.error ?? "app_state_publish_failed"));
|
|
37
32
|
```
|
|
38
33
|
|
|
39
|
-
`
|
|
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.
|
|
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
|
+
```
|
|
40
74
|
|
|
41
75
|
## Recover On Cold Start
|
|
42
76
|
|
|
43
77
|
```ts
|
|
44
|
-
import {
|
|
78
|
+
import {
|
|
79
|
+
RECEIZ_PUBLIC_STORE_STATE_PROJECTION_SCHEMA,
|
|
80
|
+
createReceizClient,
|
|
81
|
+
} from "@receiz/sdk";
|
|
45
82
|
|
|
46
83
|
const receiz = createReceizClient();
|
|
47
|
-
const restored = await receiz.appState.
|
|
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
|
+
});
|
|
48
91
|
|
|
49
|
-
if (!restored.ok || !restored.
|
|
92
|
+
if (!restored.ok || !restored.data) {
|
|
50
93
|
throw new Error("storefront_state_not_found");
|
|
51
94
|
}
|
|
52
95
|
|
|
53
|
-
const storeStateRecord = restored.
|
|
96
|
+
const storeStateRecord = restored.data.storeStateRecord;
|
|
54
97
|
```
|
|
55
98
|
|
|
56
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.
|
|
57
100
|
|
|
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
|
+
```
|
|
128
|
+
|
|
58
129
|
## Namespace And Creator Reads
|
|
59
130
|
|
|
60
131
|
```ts
|
|
@@ -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:
|
package/package.json
CHANGED