@01.software/cli 0.10.3 → 0.10.4
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 +485 -37
- package/dist/index.js.map +1 -1
- package/dist/mcp/{chunk-VSMPWKVX.js → chunk-F5VI4HQM.js} +252 -218
- package/dist/mcp/chunk-F5VI4HQM.js.map +1 -0
- package/dist/mcp/http.js +1 -1
- package/dist/mcp/stdio.js +1 -1
- package/dist/mcp/vercel.js +251 -217
- package/package.json +2 -2
- package/dist/mcp/chunk-VSMPWKVX.js.map +0 -1
|
@@ -88,8 +88,9 @@ var MCP_CONSOLE_SERVICE_AUDIENCE = "https://api.01.software/internal/mcp";
|
|
|
88
88
|
var MCP_CONSOLE_SERVICE_SCOPE = "console:mcp_proxy";
|
|
89
89
|
var MCP_SERVICE_TOKEN_LIFETIME_SECONDS = 60;
|
|
90
90
|
|
|
91
|
-
// ../../packages/contracts/
|
|
91
|
+
// ../../packages/contracts/dist/index.js
|
|
92
92
|
import { z } from "zod";
|
|
93
|
+
import { z as z2 } from "zod";
|
|
93
94
|
var tenantFieldConfigStateSchema = z.object({
|
|
94
95
|
hiddenFields: z.array(z.string()),
|
|
95
96
|
isHidden: z.boolean()
|
|
@@ -238,9 +239,6 @@ var collectionSchemaResponseSchema = z.object({
|
|
|
238
239
|
fields: z.array(collectionFieldSchema)
|
|
239
240
|
}).strict()
|
|
240
241
|
}).strict();
|
|
241
|
-
|
|
242
|
-
// ../../packages/contracts/src/ecommerce/index.ts
|
|
243
|
-
import { z as z2 } from "zod";
|
|
244
242
|
var transactionStatusSchema = z2.enum([
|
|
245
243
|
"pending",
|
|
246
244
|
"paid",
|
|
@@ -307,8 +305,6 @@ var returnWithRefundSchema = z2.object({
|
|
|
307
305
|
refundReceiptUrl: z2.string().optional().describe("Refund receipt URL (optional)")
|
|
308
306
|
}).strict();
|
|
309
307
|
var ReturnWithRefundSchema = returnWithRefundSchema;
|
|
310
|
-
|
|
311
|
-
// ../../packages/contracts/src/mcp/index.ts
|
|
312
308
|
var MCP_TOOL_CONTRACT = {
|
|
313
309
|
"query-collection": {
|
|
314
310
|
consoleRole: "tenant-viewer",
|
|
@@ -1103,7 +1099,7 @@ async function swallow(promise) {
|
|
|
1103
1099
|
import { z as z3 } from "zod";
|
|
1104
1100
|
|
|
1105
1101
|
// src/lib/client.ts
|
|
1106
|
-
import { createServerClient } from "@01.software/sdk";
|
|
1102
|
+
import { createServerClient } from "@01.software/sdk/server";
|
|
1107
1103
|
var MISSING_HTTP_AUTH_CONTEXT_ERROR2 = "MCP HTTP requests require a validated OAuth tenant context before tool execution.";
|
|
1108
1104
|
var HTTP_OAUTH_SDK_CLIENT_ERROR = "MCP HTTP OAuth requests cannot use SDK-backed tools. Use reviewed Console service endpoints for OAuth transport.";
|
|
1109
1105
|
function getClient() {
|
|
@@ -1936,18 +1932,22 @@ async function productDetail({
|
|
|
1936
1932
|
// src/tools/product-upsert.ts
|
|
1937
1933
|
import { z as z23 } from "zod";
|
|
1938
1934
|
var optionValueSchema = z23.object({
|
|
1939
|
-
id: z23.string().optional().describe("
|
|
1935
|
+
id: z23.string().optional().describe("Stable existing option-value ID for rename-safe updates"),
|
|
1940
1936
|
value: z23.string().describe("Display label (e.g. Black, S)"),
|
|
1941
|
-
slug: z23.string().optional().describe(
|
|
1937
|
+
slug: z23.string().optional().describe(
|
|
1938
|
+
"Optional compatibility value token. The server generates one from value on create when omitted; not canonical identity."
|
|
1939
|
+
),
|
|
1942
1940
|
swatchColor: z23.string().nullable().optional(),
|
|
1943
1941
|
thumbnail: z23.string().nullable().optional(),
|
|
1944
1942
|
images: z23.array(z23.string()).optional(),
|
|
1945
1943
|
metadata: z23.unknown().optional()
|
|
1946
1944
|
});
|
|
1947
1945
|
var optionSchema = z23.object({
|
|
1948
|
-
id: z23.string().optional().describe("
|
|
1946
|
+
id: z23.string().optional().describe("Stable existing option ID for rename-safe updates"),
|
|
1949
1947
|
title: z23.string().describe("Option name (e.g. Color, Size)"),
|
|
1950
|
-
slug: z23.string().optional().describe(
|
|
1948
|
+
slug: z23.string().optional().describe(
|
|
1949
|
+
"Optional compatibility option token. The server generates one from title on create when omitted; not canonical identity."
|
|
1950
|
+
),
|
|
1951
1951
|
values: z23.array(optionValueSchema).describe("Allowed option values")
|
|
1952
1952
|
});
|
|
1953
1953
|
var variantOptionValueSchema = z23.object({
|
|
@@ -1961,7 +1961,7 @@ var variantSchema = z23.object({
|
|
|
1961
1961
|
z23.record(z23.string(), z23.union([z23.string(), variantOptionValueSchema])),
|
|
1962
1962
|
z23.array(z23.string())
|
|
1963
1963
|
]).optional().describe(
|
|
1964
|
-
"Option-value selection. Prefer
|
|
1964
|
+
"Option-value selection. Prefer stable option-value IDs, either as an array or object values using { valueId }. Slug maps and exact { OptionTitle: ValueLabel } maps remain compatibility-only and fail when labels are ambiguous."
|
|
1965
1965
|
),
|
|
1966
1966
|
sku: z23.string().nullable().optional(),
|
|
1967
1967
|
title: z23.string().nullable().optional(),
|
|
@@ -1983,10 +1983,10 @@ var schema23 = {
|
|
|
1983
1983
|
"Product fields. Include `id` to update an existing product; omit for create (then `title` is required)."
|
|
1984
1984
|
),
|
|
1985
1985
|
options: z23.array(optionSchema).optional().describe(
|
|
1986
|
-
"Option definitions.
|
|
1986
|
+
"Option definitions. Include stable option/value IDs when updating or renaming existing rows. Slugs are optional compatibility metadata generated from title/value on create when omitted; omitted options on an existing product are deleted (with their values)."
|
|
1987
1987
|
),
|
|
1988
1988
|
variants: z23.array(variantSchema).optional().describe(
|
|
1989
|
-
"Variant rows. Prefer
|
|
1989
|
+
"Variant rows. Prefer stable option-value IDs in optionValues. Slug/title maps are compatibility inputs. Omitted variants on an existing product are deleted, unless referenced by an active cart or non-terminal order \u2014 those are soft-deactivated (isActive: false)."
|
|
1990
1990
|
)
|
|
1991
1991
|
};
|
|
1992
1992
|
var metadata23 = {
|
|
@@ -2287,14 +2287,16 @@ var recipes = {
|
|
|
2287
2287
|
recommendedSurface: "react-query",
|
|
2288
2288
|
runtime: "browser",
|
|
2289
2289
|
code: `import { createClient } from '@01.software/sdk'
|
|
2290
|
+
import { createQueryHooks } from '@01.software/sdk/query'
|
|
2290
2291
|
|
|
2291
2292
|
const client = createClient({
|
|
2292
2293
|
publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!,
|
|
2293
2294
|
})
|
|
2295
|
+
const query = createQueryHooks(client)
|
|
2294
2296
|
|
|
2295
2297
|
// React Query hook \u2014 handles caching and background updates
|
|
2296
2298
|
function ProductList() {
|
|
2297
|
-
const { data, isLoading, error } =
|
|
2299
|
+
const { data, isLoading, error } = query.useQuery({
|
|
2298
2300
|
collection: 'products',
|
|
2299
2301
|
options: {
|
|
2300
2302
|
where: { status: { equals: 'published' } },
|
|
@@ -2326,7 +2328,7 @@ function ProductList() {
|
|
|
2326
2328
|
title: "Fetch collection list (server)",
|
|
2327
2329
|
recommendedSurface: "query-builder",
|
|
2328
2330
|
runtime: "server",
|
|
2329
|
-
code: `import { createServerClient } from '@01.software/sdk'
|
|
2331
|
+
code: `import { createServerClient } from '@01.software/sdk/server'
|
|
2330
2332
|
|
|
2331
2333
|
const client = createServerClient({
|
|
2332
2334
|
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
@@ -2359,13 +2361,15 @@ const result = await client.collections.from('products').find({
|
|
|
2359
2361
|
recommendedSurface: "react-query",
|
|
2360
2362
|
runtime: "browser",
|
|
2361
2363
|
code: `import { createClient } from '@01.software/sdk'
|
|
2364
|
+
import { createQueryHooks } from '@01.software/sdk/query'
|
|
2362
2365
|
|
|
2363
2366
|
const client = createClient({
|
|
2364
2367
|
publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!,
|
|
2365
2368
|
})
|
|
2369
|
+
const query = createQueryHooks(client)
|
|
2366
2370
|
|
|
2367
2371
|
function ProductDetail({ id }: { id: string }) {
|
|
2368
|
-
const { data, isLoading } =
|
|
2372
|
+
const { data, isLoading } = query.useQueryById({
|
|
2369
2373
|
collection: 'products',
|
|
2370
2374
|
id,
|
|
2371
2375
|
})
|
|
@@ -2385,7 +2389,7 @@ function ProductDetail({ id }: { id: string }) {
|
|
|
2385
2389
|
title: "Fetch single item by ID (server)",
|
|
2386
2390
|
recommendedSurface: "query-builder",
|
|
2387
2391
|
runtime: "server",
|
|
2388
|
-
code: `import { createServerClient } from '@01.software/sdk'
|
|
2392
|
+
code: `import { createServerClient } from '@01.software/sdk/server'
|
|
2389
2393
|
|
|
2390
2394
|
const client = createServerClient({
|
|
2391
2395
|
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
@@ -2408,7 +2412,7 @@ console.log(product.title)`,
|
|
|
2408
2412
|
title: "Create a new item (server only)",
|
|
2409
2413
|
recommendedSurface: "server-api",
|
|
2410
2414
|
runtime: "server",
|
|
2411
|
-
code: `import { createServerClient } from '@01.software/sdk'
|
|
2415
|
+
code: `import { createServerClient } from '@01.software/sdk/server'
|
|
2412
2416
|
|
|
2413
2417
|
const client = createServerClient({
|
|
2414
2418
|
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
@@ -2437,7 +2441,7 @@ const result = await client.collections.from('products').create({
|
|
|
2437
2441
|
title: "Update an existing item (server only)",
|
|
2438
2442
|
recommendedSurface: "server-api",
|
|
2439
2443
|
runtime: "server",
|
|
2440
|
-
code: `import { createServerClient } from '@01.software/sdk'
|
|
2444
|
+
code: `import { createServerClient } from '@01.software/sdk/server'
|
|
2441
2445
|
|
|
2442
2446
|
const client = createServerClient({
|
|
2443
2447
|
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
@@ -2466,7 +2470,7 @@ const result = await client.collections.from('products').update('product-id', {
|
|
|
2466
2470
|
title: "Delete an item (server only)",
|
|
2467
2471
|
recommendedSurface: "server-api",
|
|
2468
2472
|
runtime: "server",
|
|
2469
|
-
code: `import { createServerClient } from '@01.software/sdk'
|
|
2473
|
+
code: `import { createServerClient } from '@01.software/sdk/server'
|
|
2470
2474
|
|
|
2471
2475
|
const client = createServerClient({
|
|
2472
2476
|
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
@@ -2491,10 +2495,12 @@ console.log('Deleted:', deleted.title)`,
|
|
|
2491
2495
|
recommendedSurface: "react-query",
|
|
2492
2496
|
runtime: "browser",
|
|
2493
2497
|
code: `import { createClient } from '@01.software/sdk'
|
|
2498
|
+
import { createQueryHooks } from '@01.software/sdk/query'
|
|
2494
2499
|
|
|
2495
2500
|
const client = createClient({
|
|
2496
2501
|
publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!,
|
|
2497
2502
|
})
|
|
2503
|
+
const query = createQueryHooks(client)
|
|
2498
2504
|
|
|
2499
2505
|
function InfiniteProductList() {
|
|
2500
2506
|
const {
|
|
@@ -2503,7 +2509,7 @@ function InfiniteProductList() {
|
|
|
2503
2509
|
hasNextPage,
|
|
2504
2510
|
isFetchingNextPage,
|
|
2505
2511
|
isLoading,
|
|
2506
|
-
} =
|
|
2512
|
+
} = query.useInfiniteQuery({
|
|
2507
2513
|
collection: 'products',
|
|
2508
2514
|
options: { where: { status: { equals: 'published' } } },
|
|
2509
2515
|
pageSize: 20,
|
|
@@ -2538,7 +2544,8 @@ function InfiniteProductList() {
|
|
|
2538
2544
|
title: "SSR data prefetching (server)",
|
|
2539
2545
|
recommendedSurface: "react-query",
|
|
2540
2546
|
runtime: "server",
|
|
2541
|
-
code: `import { createServerClient } from '@01.software/sdk'
|
|
2547
|
+
code: `import { createServerClient } from '@01.software/sdk/server'
|
|
2548
|
+
import { createServerQueryHooks, getQueryClient } from '@01.software/sdk/query'
|
|
2542
2549
|
import { dehydrate, HydrationBoundary } from '@tanstack/react-query'
|
|
2543
2550
|
|
|
2544
2551
|
// In a Next.js Server Component or getServerSideProps:
|
|
@@ -2546,27 +2553,29 @@ const client = createServerClient({
|
|
|
2546
2553
|
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
2547
2554
|
secretKey: process.env.SOFTWARE_SECRET_KEY!,
|
|
2548
2555
|
})
|
|
2556
|
+
const queryClient = getQueryClient()
|
|
2557
|
+
const serverQuery = createServerQueryHooks(client, queryClient)
|
|
2549
2558
|
|
|
2550
2559
|
// Prefetch list \u2014 client hydrates instantly without a loading state
|
|
2551
|
-
await
|
|
2560
|
+
await serverQuery.prefetchQuery({
|
|
2552
2561
|
collection: 'products',
|
|
2553
2562
|
options: { limit: 20 },
|
|
2554
2563
|
})
|
|
2555
2564
|
|
|
2556
2565
|
// Prefetch single item
|
|
2557
|
-
await
|
|
2566
|
+
await serverQuery.prefetchQueryById({
|
|
2558
2567
|
collection: 'products',
|
|
2559
2568
|
id: 'product-id',
|
|
2560
2569
|
})
|
|
2561
2570
|
|
|
2562
2571
|
// Prefetch infinite list
|
|
2563
|
-
await
|
|
2572
|
+
await serverQuery.prefetchInfiniteQuery({
|
|
2564
2573
|
collection: 'products',
|
|
2565
2574
|
pageSize: 20,
|
|
2566
2575
|
})
|
|
2567
2576
|
|
|
2568
2577
|
// Dehydrate and pass to client
|
|
2569
|
-
const state = dehydrate(
|
|
2578
|
+
const state = dehydrate(queryClient)
|
|
2570
2579
|
|
|
2571
2580
|
export default function Page() {
|
|
2572
2581
|
return (
|
|
@@ -2636,7 +2645,7 @@ await client.customer.resetPassword(token, 'newPassword123')`,
|
|
|
2636
2645
|
title: "File upload pattern (server)",
|
|
2637
2646
|
recommendedSurface: "server-api",
|
|
2638
2647
|
runtime: "server",
|
|
2639
|
-
code: `import { createServerClient } from '@01.software/sdk'
|
|
2648
|
+
code: `import { createServerClient } from '@01.software/sdk/server'
|
|
2640
2649
|
|
|
2641
2650
|
const client = createServerClient({
|
|
2642
2651
|
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
@@ -2667,7 +2676,7 @@ const result = await client.collections.from('images').create(formData as unknow
|
|
|
2667
2676
|
title: "Bulk update/delete (server only)",
|
|
2668
2677
|
recommendedSurface: "server-api",
|
|
2669
2678
|
runtime: "server",
|
|
2670
|
-
code: `import { createServerClient } from '@01.software/sdk'
|
|
2679
|
+
code: `import { createServerClient } from '@01.software/sdk/server'
|
|
2671
2680
|
|
|
2672
2681
|
const client = createServerClient({
|
|
2673
2682
|
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
@@ -2819,15 +2828,15 @@ var docIndex = [
|
|
|
2819
2828
|
},
|
|
2820
2829
|
{
|
|
2821
2830
|
title: "Query Builder \u2014 Metadata Generation",
|
|
2822
|
-
keywords: ["metadata", "
|
|
2823
|
-
summary: "
|
|
2831
|
+
keywords: ["metadata", "generateMetadata", "extractSeo", "next.js metadata", "seo", "open graph", "query builder"],
|
|
2832
|
+
summary: "Use @01.software/sdk/metadata helpers with fetched documents to generate Metadata-shaped SEO objects without pulling Next.js into the root SDK entry.",
|
|
2824
2833
|
resourceUri: "docs://sdk/query-builder"
|
|
2825
2834
|
},
|
|
2826
2835
|
// React Query Hooks
|
|
2827
2836
|
{
|
|
2828
2837
|
title: "React Query \u2014 useQuery()",
|
|
2829
2838
|
keywords: ["useQuery", "react query", "hook", "list", "fetch", "collection", "cache", "react"],
|
|
2830
|
-
summary: "client.
|
|
2839
|
+
summary: "createQueryHooks(client).useQuery({ collection, options }) \u2014 fetches a list with caching and background updates. Use inside a React component.",
|
|
2831
2840
|
resourceUri: "docs://sdk/react-query"
|
|
2832
2841
|
},
|
|
2833
2842
|
{
|
|
@@ -2839,7 +2848,7 @@ var docIndex = [
|
|
|
2839
2848
|
{
|
|
2840
2849
|
title: "React Query \u2014 useInfiniteQuery()",
|
|
2841
2850
|
keywords: ["useInfiniteQuery", "useSuspenseInfiniteQuery", "infinite scroll", "load more", "pagination", "react query", "hook"],
|
|
2842
|
-
summary: "client.
|
|
2851
|
+
summary: "createQueryHooks(client).useInfiniteQuery({ collection, options, pageSize }) \u2014 infinite scrolling. data.pages is an array of pages; flatten with flatMap for all docs.",
|
|
2843
2852
|
resourceUri: "docs://sdk/react-query"
|
|
2844
2853
|
},
|
|
2845
2854
|
{
|
|
@@ -2858,7 +2867,7 @@ var docIndex = [
|
|
|
2858
2867
|
{
|
|
2859
2868
|
title: "Cache Invalidation and Manual Cache Management",
|
|
2860
2869
|
keywords: ["invalidateQueries", "getQueryData", "setQueryData", "cache", "invalidate", "optimistic update", "react query"],
|
|
2861
|
-
summary: "
|
|
2870
|
+
summary: "query.invalidateQueries(collection, type) triggers refetch. getQueryData/setQueryData allow manual optimistic updates.",
|
|
2862
2871
|
resourceUri: "docs://sdk/react-query"
|
|
2863
2872
|
},
|
|
2864
2873
|
// Filtering
|
|
@@ -3009,23 +3018,25 @@ var AUTH_GUIDES = {
|
|
|
3009
3018
|
title: "Browser Client Setup",
|
|
3010
3019
|
envVars: ["NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY"],
|
|
3011
3020
|
code: `import { createClient } from '@01.software/sdk'
|
|
3021
|
+
import { createQueryHooks } from '@01.software/sdk/query'
|
|
3012
3022
|
|
|
3013
3023
|
const client = createClient({
|
|
3014
3024
|
publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!
|
|
3015
3025
|
})
|
|
3026
|
+
const query = createQueryHooks(client)
|
|
3016
3027
|
|
|
3017
3028
|
// Read-only operations + React Query hooks + Customer Auth
|
|
3018
|
-
const { data } =
|
|
3029
|
+
const { data } = query.useQuery({ collection: 'products' })`,
|
|
3019
3030
|
notes: [
|
|
3020
3031
|
"Client is read-only \u2014 no create/update/delete operations",
|
|
3021
3032
|
"publishableKey is safe to expose in browser (prefixed with NEXT_PUBLIC_)",
|
|
3022
|
-
"
|
|
3033
|
+
"Use createQueryHooks(client) for React Query hooks and client.customer for Customer Auth"
|
|
3023
3034
|
]
|
|
3024
3035
|
},
|
|
3025
3036
|
"server-client": {
|
|
3026
3037
|
title: "Server Client Setup",
|
|
3027
3038
|
envVars: ["SOFTWARE_PUBLISHABLE_KEY", "SOFTWARE_SECRET_KEY"],
|
|
3028
|
-
code: `import { createServerClient } from '@01.software/sdk'
|
|
3039
|
+
code: `import { createServerClient } from '@01.software/sdk/server'
|
|
3029
3040
|
|
|
3030
3041
|
const client = createServerClient({
|
|
3031
3042
|
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
@@ -3038,7 +3049,7 @@ const result = await client.collections.from('products').create({ title: 'New Pr
|
|
|
3038
3049
|
"ServerClient has full CRUD access and must run only in trusted server code",
|
|
3039
3050
|
"Store server credentials in environment variables and rotate them from the Console",
|
|
3040
3051
|
"Use in API routes, server actions, or backend services only",
|
|
3041
|
-
"
|
|
3052
|
+
"Use createServerClient in API routes, server actions, or backend services; keep React Query hooks for browser-safe reads and server prefetching"
|
|
3042
3053
|
]
|
|
3043
3054
|
},
|
|
3044
3055
|
"customer-auth": {
|
|
@@ -3176,37 +3187,35 @@ function generatePattern(collection, operation, surface) {
|
|
|
3176
3187
|
);
|
|
3177
3188
|
}
|
|
3178
3189
|
const parts2 = [];
|
|
3179
|
-
if (operation === "read") {
|
|
3190
|
+
if (operation === "read" || operation === "full-crud") {
|
|
3180
3191
|
parts2.push(
|
|
3181
3192
|
`import { createClient } from '@01.software/sdk'`,
|
|
3182
|
-
|
|
3183
|
-
`const client = createClient({`,
|
|
3184
|
-
` publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!`,
|
|
3185
|
-
`})`,
|
|
3186
|
-
``
|
|
3193
|
+
`import { createQueryHooks } from '@01.software/sdk/query'`
|
|
3187
3194
|
);
|
|
3188
|
-
}
|
|
3195
|
+
}
|
|
3196
|
+
if (operation === "write" || operation === "full-crud") {
|
|
3197
|
+
parts2.push(`import { createServerClient } from '@01.software/sdk/server'`);
|
|
3198
|
+
}
|
|
3199
|
+
parts2.push(``);
|
|
3200
|
+
if (operation === "read" || operation === "full-crud") {
|
|
3189
3201
|
parts2.push(
|
|
3190
|
-
`
|
|
3191
|
-
|
|
3192
|
-
`// Mutation hooks require ServerClient`,
|
|
3193
|
-
`const client = createServerClient({`,
|
|
3194
|
-
` publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,`,
|
|
3195
|
-
` secretKey: process.env.SOFTWARE_SECRET_KEY!`,
|
|
3202
|
+
`const client = createClient({`,
|
|
3203
|
+
` publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!`,
|
|
3196
3204
|
`})`,
|
|
3205
|
+
`const query = createQueryHooks(client)`,
|
|
3197
3206
|
``
|
|
3198
3207
|
);
|
|
3199
3208
|
}
|
|
3200
3209
|
if (operation === "read" || operation === "full-crud") {
|
|
3201
3210
|
parts2.push(
|
|
3202
3211
|
`// List query`,
|
|
3203
|
-
`const { data, isLoading } =
|
|
3212
|
+
`const { data, isLoading } = query.useQuery({`,
|
|
3204
3213
|
` collection: '${collection}',`,
|
|
3205
3214
|
` options: { limit: 10 }`,
|
|
3206
3215
|
`})`,
|
|
3207
3216
|
``,
|
|
3208
3217
|
`// Single item`,
|
|
3209
|
-
`const { data: item } =
|
|
3218
|
+
`const { data: item } = query.useQueryById({`,
|
|
3210
3219
|
` collection: '${collection}',`,
|
|
3211
3220
|
` id: itemId`,
|
|
3212
3221
|
`})`
|
|
@@ -3215,31 +3224,28 @@ function generatePattern(collection, operation, surface) {
|
|
|
3215
3224
|
if (operation === "write" || operation === "full-crud") {
|
|
3216
3225
|
parts2.push(
|
|
3217
3226
|
``,
|
|
3218
|
-
`//
|
|
3219
|
-
`const
|
|
3220
|
-
`
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
`const { mutate: update } = client.query.useUpdate({ collection: '${collection}' })`,
|
|
3224
|
-
`update({ id: itemId, data: { title: 'Updated' } })`,
|
|
3227
|
+
`// Writes belong in a server action or API route, not a client component.`,
|
|
3228
|
+
`const server = createServerClient({`,
|
|
3229
|
+
` publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,`,
|
|
3230
|
+
` secretKey: process.env.SOFTWARE_SECRET_KEY!`,
|
|
3231
|
+
`})`,
|
|
3225
3232
|
``,
|
|
3226
|
-
|
|
3227
|
-
`
|
|
3228
|
-
`remove(itemId)`
|
|
3233
|
+
`await server.collections.from('${collection}').create({ title: 'New Item' })`,
|
|
3234
|
+
`await server.collections.from('${collection}').update(itemId, { title: 'Updated' })`,
|
|
3235
|
+
`await server.collections.from('${collection}').remove(itemId)`
|
|
3229
3236
|
);
|
|
3230
3237
|
}
|
|
3231
3238
|
return {
|
|
3232
3239
|
code: parts2.join("\n"),
|
|
3233
3240
|
notes: [
|
|
3234
|
-
"React Query hooks provide automatic caching and background updates",
|
|
3235
|
-
"
|
|
3236
|
-
operation === "write" || operation === "full-crud" ? "Mutation hooks (useCreate, useUpdate, useRemove) require ServerClient with SOFTWARE_PUBLISHABLE_KEY + SOFTWARE_SECRET_KEY" : "Read hooks work in browser components"
|
|
3241
|
+
"React Query hooks provide automatic caching and background updates for browser-safe reads",
|
|
3242
|
+
operation === "write" || operation === "full-crud" ? "Writes require trusted server code with SOFTWARE_PUBLISHABLE_KEY + SOFTWARE_SECRET_KEY; invalidate browser caches after the server action returns" : "Read hooks work in browser components"
|
|
3237
3243
|
]
|
|
3238
3244
|
};
|
|
3239
3245
|
}
|
|
3240
3246
|
if (surface === "server-api") {
|
|
3241
3247
|
const parts2 = [
|
|
3242
|
-
`import { createServerClient } from '@01.software/sdk'`,
|
|
3248
|
+
`import { createServerClient } from '@01.software/sdk/server'`,
|
|
3243
3249
|
``,
|
|
3244
3250
|
`const client = createServerClient({`,
|
|
3245
3251
|
` publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,`,
|
|
@@ -3406,8 +3412,12 @@ const result = await client.collections.from('products').find({
|
|
|
3406
3412
|
})
|
|
3407
3413
|
|
|
3408
3414
|
// Use React hooks
|
|
3415
|
+
import { createQueryHooks } from '@01.software/sdk/query'
|
|
3416
|
+
|
|
3417
|
+
const query = createQueryHooks(client)
|
|
3418
|
+
|
|
3409
3419
|
function ProductList() {
|
|
3410
|
-
const { data, isLoading } =
|
|
3420
|
+
const { data, isLoading } = query.useQuery({
|
|
3411
3421
|
collection: 'products',
|
|
3412
3422
|
options: { limit: 10 }
|
|
3413
3423
|
})
|
|
@@ -3424,7 +3434,7 @@ function ProductList() {
|
|
|
3424
3434
|
}
|
|
3425
3435
|
\`\`\`
|
|
3426
3436
|
|
|
3427
|
-
### React Query Hooks (
|
|
3437
|
+
### React Query Hooks (@01.software/sdk/query)
|
|
3428
3438
|
|
|
3429
3439
|
| Hook | Description |
|
|
3430
3440
|
|------|-------------|
|
|
@@ -3459,13 +3469,17 @@ await client.collections.from('products').remove('id')
|
|
|
3459
3469
|
// count() returns { totalDocs }
|
|
3460
3470
|
const { totalDocs } = await client.collections.from('products').count()
|
|
3461
3471
|
|
|
3462
|
-
// Metadata - generate
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
const
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3472
|
+
// Metadata - generate Metadata-shaped SEO objects from fetched documents
|
|
3473
|
+
import { extractSeo, generateMetadata } from '@01.software/sdk/metadata'
|
|
3474
|
+
|
|
3475
|
+
const productResult = await client.collections.from('products').find({
|
|
3476
|
+
where: { slug: { equals: 'my-product' } },
|
|
3477
|
+
limit: 1,
|
|
3478
|
+
depth: 1,
|
|
3479
|
+
})
|
|
3480
|
+
const productMeta = productResult.docs[0]
|
|
3481
|
+
? generateMetadata(extractSeo(productResult.docs[0]), { siteName: 'My Store' })
|
|
3482
|
+
: null
|
|
3469
3483
|
\`\`\`
|
|
3470
3484
|
|
|
3471
3485
|
### Filtering (Payload Query Syntax)
|
|
@@ -3488,7 +3502,7 @@ For ecommerce collections, use \`products.listing.*\` for browse/search pricing
|
|
|
3488
3502
|
### Server Client (Server-side)
|
|
3489
3503
|
|
|
3490
3504
|
\`\`\`typescript
|
|
3491
|
-
import { createServerClient } from '@01.software/sdk'
|
|
3505
|
+
import { createServerClient } from '@01.software/sdk/server'
|
|
3492
3506
|
|
|
3493
3507
|
const client = createServerClient({
|
|
3494
3508
|
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
@@ -3508,7 +3522,7 @@ if (!product) return notFound()
|
|
|
3508
3522
|
// product: { product, variants, options, brand, categories, tags, images, videos, listing }
|
|
3509
3523
|
\`\`\`
|
|
3510
3524
|
|
|
3511
|
-
For React: \`const { data } = client.
|
|
3525
|
+
For React: \`const { data } = createQueryHooks(client).useProductDetailBySlug(slug)\`.
|
|
3512
3526
|
|
|
3513
3527
|
### Product listing (grouped)
|
|
3514
3528
|
|
|
@@ -3563,7 +3577,8 @@ ${filters}
|
|
|
3563
3577
|
### ${operation === "find" ? "Query" : operation === "create" ? "Create" : operation === "update" ? "Update" : "Delete"} Example
|
|
3564
3578
|
|
|
3565
3579
|
\`\`\`typescript
|
|
3566
|
-
import { createClient
|
|
3580
|
+
import { createClient } from '@01.software/sdk'
|
|
3581
|
+
import { createServerClient } from '@01.software/sdk/server'
|
|
3567
3582
|
|
|
3568
3583
|
// Client (read-only public collections)
|
|
3569
3584
|
const client = createClient({
|
|
@@ -3584,14 +3599,18 @@ const result = await ${readClientName}.collections.from('${collection}').find(${
|
|
|
3584
3599
|
// result.docs - array of items
|
|
3585
3600
|
// result.totalDocs, result.page, result.totalPages, result.hasNextPage, ...
|
|
3586
3601
|
|
|
3587
|
-
${isPublicCollection ? `// Using React
|
|
3588
|
-
|
|
3602
|
+
${isPublicCollection ? `// Using React Query hooks
|
|
3603
|
+
import { createQueryHooks } from '@01.software/sdk/query'
|
|
3604
|
+
|
|
3605
|
+
const query = createQueryHooks(client)
|
|
3606
|
+
|
|
3607
|
+
const { data, isLoading, error } = query.useQuery({
|
|
3589
3608
|
collection: '${collection}',
|
|
3590
3609
|
options: { limit: 10 }
|
|
3591
3610
|
})
|
|
3592
3611
|
|
|
3593
3612
|
// With Suspense
|
|
3594
|
-
const { data } =
|
|
3613
|
+
const { data } = query.useSuspenseQuery({
|
|
3595
3614
|
collection: '${collection}',
|
|
3596
3615
|
options: { limit: 10 }
|
|
3597
3616
|
})` : `// React hooks are browser/public only and do not support '${collection}'.`}` : operation === "create" ? `// Create ${collection} item (ServerClient only)
|
|
@@ -3660,7 +3679,7 @@ var SCENARIOS = {
|
|
|
3660
3679
|
|
|
3661
3680
|
### Code Example (ServerClient)
|
|
3662
3681
|
\`\`\`typescript
|
|
3663
|
-
import { createServerClient } from '@01.software/sdk'
|
|
3682
|
+
import { createServerClient } from '@01.software/sdk/server'
|
|
3664
3683
|
|
|
3665
3684
|
const client = createServerClient({
|
|
3666
3685
|
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
@@ -4270,6 +4289,22 @@ yarn add @01.software/sdk
|
|
|
4270
4289
|
pnpm add @01.software/sdk
|
|
4271
4290
|
\`\`\`
|
|
4272
4291
|
|
|
4292
|
+
## Optional peers
|
|
4293
|
+
|
|
4294
|
+
You do not need extra packages for the root SDK entry. Install peer
|
|
4295
|
+
dependencies only when you import a feature sub-path:
|
|
4296
|
+
|
|
4297
|
+
- \`@01.software/sdk/query\` -> \`@tanstack/react-query\`, \`react\`, \`react-dom\`
|
|
4298
|
+
- \`@01.software/sdk/realtime\` -> \`@tanstack/react-query\`
|
|
4299
|
+
- \`@01.software/sdk/analytics/react\` -> \`react\`, \`react-dom\`
|
|
4300
|
+
- \`@01.software/sdk/ui/rich-text\` -> \`@payloadcms/richtext-lexical\`
|
|
4301
|
+
- \`@01.software/sdk/ui/form\` -> none
|
|
4302
|
+
- \`@01.software/sdk/ui/code-block\` -> \`shiki\`, \`hast-util-to-jsx-runtime\`
|
|
4303
|
+
- \`@01.software/sdk/ui/canvas\` -> \`@tanstack/react-query\`, \`@xyflow/react\`, \`quickjs-emscripten\`, \`postcss\`, \`sucrase\`
|
|
4304
|
+
- \`@01.software/sdk/ui/canvas/server\` -> none
|
|
4305
|
+
- \`@01.software/sdk/ui/video\` -> \`@mux/mux-player-react\`
|
|
4306
|
+
- \`@01.software/sdk/ui/image\` -> none
|
|
4307
|
+
|
|
4273
4308
|
## Basic Usage
|
|
4274
4309
|
|
|
4275
4310
|
\`\`\`typescript
|
|
@@ -4307,7 +4342,7 @@ Comprehensive guides to master the 01.software SDK.
|
|
|
4307
4342
|
|
|
4308
4343
|
## Data Fetching
|
|
4309
4344
|
|
|
4310
|
-
Use the Query Builder or React Query hooks to fetch data efficiently.
|
|
4345
|
+
Use the Query Builder or opt-in React Query hooks to fetch data efficiently.
|
|
4311
4346
|
|
|
4312
4347
|
### Query Builder
|
|
4313
4348
|
\`\`\`typescript
|
|
@@ -4335,7 +4370,10 @@ const { totalDocs } = await client.collections.from('products').count()
|
|
|
4335
4370
|
|
|
4336
4371
|
### React Query Hook
|
|
4337
4372
|
\`\`\`typescript
|
|
4338
|
-
|
|
4373
|
+
import { createQueryHooks } from '@01.software/sdk/query'
|
|
4374
|
+
|
|
4375
|
+
const query = createQueryHooks(client)
|
|
4376
|
+
const { data, isLoading, error } = query.useQuery({
|
|
4339
4377
|
collection: 'products',
|
|
4340
4378
|
options: {
|
|
4341
4379
|
where: { status: { equals: 'published' } },
|
|
@@ -4346,7 +4384,7 @@ const { data, isLoading, error } = client.query.useQuery({
|
|
|
4346
4384
|
|
|
4347
4385
|
### Suspense Mode
|
|
4348
4386
|
\`\`\`typescript
|
|
4349
|
-
const { data } =
|
|
4387
|
+
const { data } = query.useSuspenseQuery({
|
|
4350
4388
|
collection: 'products',
|
|
4351
4389
|
options: { limit: 10 }
|
|
4352
4390
|
})
|
|
@@ -4355,7 +4393,7 @@ const { data } = client.query.useSuspenseQuery({
|
|
|
4355
4393
|
### Infinite Scroll
|
|
4356
4394
|
\`\`\`typescript
|
|
4357
4395
|
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
|
|
4358
|
-
|
|
4396
|
+
query.useInfiniteQuery({
|
|
4359
4397
|
collection: 'products',
|
|
4360
4398
|
pageSize: 20
|
|
4361
4399
|
})
|
|
@@ -4402,19 +4440,18 @@ const result = await serverClient.from('products').removeMany(
|
|
|
4402
4440
|
)
|
|
4403
4441
|
\`\`\`
|
|
4404
4442
|
|
|
4405
|
-
###
|
|
4443
|
+
### Server Mutations
|
|
4406
4444
|
\`\`\`typescript
|
|
4407
|
-
|
|
4408
|
-
const { mutate: create } = client.query.useCreate({ collection: 'products' })
|
|
4409
|
-
create({ title: 'New', status: 'draft' })
|
|
4445
|
+
import { createServerClient } from '@01.software/sdk/server'
|
|
4410
4446
|
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4447
|
+
const server = createServerClient({
|
|
4448
|
+
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
4449
|
+
secretKey: process.env.SOFTWARE_SECRET_KEY!,
|
|
4450
|
+
})
|
|
4414
4451
|
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
remove('product-id')
|
|
4452
|
+
await server.collections.from('products').create({ title: 'New', status: 'draft' })
|
|
4453
|
+
await server.collections.from('products').update('product-id', { title: 'Updated' })
|
|
4454
|
+
await server.collections.from('products').remove('product-id')
|
|
4418
4455
|
\`\`\`
|
|
4419
4456
|
|
|
4420
4457
|
## Caching Strategies
|
|
@@ -4424,17 +4461,17 @@ The SDK uses React Query for caching and background updates.
|
|
|
4424
4461
|
### SSR Prefetching
|
|
4425
4462
|
\`\`\`typescript
|
|
4426
4463
|
// Prefetch in server component for instant client hydration
|
|
4427
|
-
await
|
|
4464
|
+
await query.prefetchQuery({
|
|
4428
4465
|
collection: 'products',
|
|
4429
4466
|
options: { limit: 10 }
|
|
4430
4467
|
})
|
|
4431
4468
|
|
|
4432
|
-
await
|
|
4469
|
+
await query.prefetchQueryById({
|
|
4433
4470
|
collection: 'products',
|
|
4434
4471
|
id: 'product-id'
|
|
4435
4472
|
})
|
|
4436
4473
|
|
|
4437
|
-
await
|
|
4474
|
+
await query.prefetchInfiniteQuery({
|
|
4438
4475
|
collection: 'products',
|
|
4439
4476
|
pageSize: 20
|
|
4440
4477
|
})
|
|
@@ -4443,19 +4480,19 @@ await client.query.prefetchInfiniteQuery({
|
|
|
4443
4480
|
### Cache Invalidation
|
|
4444
4481
|
\`\`\`typescript
|
|
4445
4482
|
// Invalidate list cache for a collection
|
|
4446
|
-
|
|
4483
|
+
query.invalidateQueries('products', 'list')
|
|
4447
4484
|
|
|
4448
4485
|
// Invalidate all caches for a collection
|
|
4449
|
-
|
|
4486
|
+
query.invalidateQueries('products')
|
|
4450
4487
|
\`\`\`
|
|
4451
4488
|
|
|
4452
4489
|
### Manual Cache Management
|
|
4453
4490
|
\`\`\`typescript
|
|
4454
4491
|
// Read cached data
|
|
4455
|
-
const cached =
|
|
4492
|
+
const cached = query.getQueryData('products', 'list')
|
|
4456
4493
|
|
|
4457
4494
|
// Write to cache (optimistic updates)
|
|
4458
|
-
|
|
4495
|
+
query.setQueryData('products', 'list', newData)
|
|
4459
4496
|
\`\`\`
|
|
4460
4497
|
|
|
4461
4498
|
## Error Handling
|
|
@@ -4533,7 +4570,7 @@ const client = createClient({
|
|
|
4533
4570
|
For server-side operations (full CRUD)
|
|
4534
4571
|
|
|
4535
4572
|
\`\`\`typescript
|
|
4536
|
-
import { createServerClient } from '@01.software/sdk'
|
|
4573
|
+
import { createServerClient } from '@01.software/sdk/server'
|
|
4537
4574
|
|
|
4538
4575
|
const client = createServerClient({
|
|
4539
4576
|
publishableKey: string,
|
|
@@ -4612,13 +4649,19 @@ const result = await client.collections.from('collection').removeMany(where)
|
|
|
4612
4649
|
// Returns PayloadFindResponse with deleted docs
|
|
4613
4650
|
\`\`\`
|
|
4614
4651
|
|
|
4615
|
-
## React Query Hooks (
|
|
4652
|
+
## React Query Hooks (\`@01.software/sdk/query\`)
|
|
4653
|
+
|
|
4654
|
+
\`\`\`typescript
|
|
4655
|
+
import { createQueryHooks } from '@01.software/sdk/query'
|
|
4656
|
+
|
|
4657
|
+
const query = createQueryHooks(client)
|
|
4658
|
+
\`\`\`
|
|
4616
4659
|
|
|
4617
4660
|
### useQuery()
|
|
4618
4661
|
Query a collection list.
|
|
4619
4662
|
|
|
4620
4663
|
\`\`\`typescript
|
|
4621
|
-
const { data, isLoading, error } =
|
|
4664
|
+
const { data, isLoading, error } = query.useQuery({
|
|
4622
4665
|
collection: 'products',
|
|
4623
4666
|
options: { limit: 10, where: { status: { equals: 'published' } } }
|
|
4624
4667
|
})
|
|
@@ -4628,7 +4671,7 @@ const { data, isLoading, error } = client.query.useQuery({
|
|
|
4628
4671
|
Suspense mode list query.
|
|
4629
4672
|
|
|
4630
4673
|
\`\`\`typescript
|
|
4631
|
-
const { data } =
|
|
4674
|
+
const { data } = query.useSuspenseQuery({
|
|
4632
4675
|
collection: 'products',
|
|
4633
4676
|
options: { limit: 10 }
|
|
4634
4677
|
})
|
|
@@ -4638,7 +4681,7 @@ const { data } = client.query.useSuspenseQuery({
|
|
|
4638
4681
|
Get a single item by ID.
|
|
4639
4682
|
|
|
4640
4683
|
\`\`\`typescript
|
|
4641
|
-
const { data } =
|
|
4684
|
+
const { data } = query.useQueryById({
|
|
4642
4685
|
collection: 'products',
|
|
4643
4686
|
id: 'product-id'
|
|
4644
4687
|
})
|
|
@@ -4648,7 +4691,7 @@ const { data } = client.query.useQueryById({
|
|
|
4648
4691
|
Suspense mode single item query.
|
|
4649
4692
|
|
|
4650
4693
|
\`\`\`typescript
|
|
4651
|
-
const { data } =
|
|
4694
|
+
const { data } = query.useSuspenseQueryById({
|
|
4652
4695
|
collection: 'products',
|
|
4653
4696
|
id: 'product-id'
|
|
4654
4697
|
})
|
|
@@ -4658,7 +4701,7 @@ const { data } = client.query.useSuspenseQueryById({
|
|
|
4658
4701
|
Infinite scroll.
|
|
4659
4702
|
|
|
4660
4703
|
\`\`\`typescript
|
|
4661
|
-
const { data, fetchNextPage, hasNextPage } =
|
|
4704
|
+
const { data, fetchNextPage, hasNextPage } = query.useInfiniteQuery({
|
|
4662
4705
|
collection: 'products',
|
|
4663
4706
|
options: { limit: 20 },
|
|
4664
4707
|
pageSize: 20
|
|
@@ -4669,52 +4712,43 @@ const { data, fetchNextPage, hasNextPage } = client.query.useInfiniteQuery({
|
|
|
4669
4712
|
Suspense mode infinite scroll.
|
|
4670
4713
|
|
|
4671
4714
|
\`\`\`typescript
|
|
4672
|
-
const { data, fetchNextPage } =
|
|
4715
|
+
const { data, fetchNextPage } = query.useSuspenseInfiniteQuery({
|
|
4673
4716
|
collection: 'products',
|
|
4674
4717
|
pageSize: 20
|
|
4675
4718
|
})
|
|
4676
4719
|
\`\`\`
|
|
4677
4720
|
|
|
4678
|
-
##
|
|
4679
|
-
|
|
4680
|
-
### useCreate()
|
|
4681
|
-
Create a document with automatic cache invalidation.
|
|
4682
|
-
|
|
4683
|
-
\`\`\`typescript
|
|
4684
|
-
const { mutate } = client.query.useCreate({ collection: 'products' })
|
|
4685
|
-
mutate({ title: 'New Product', status: 'draft' })
|
|
4686
|
-
\`\`\`
|
|
4721
|
+
## Server Mutations
|
|
4687
4722
|
|
|
4688
|
-
|
|
4689
|
-
Update a document with automatic cache invalidation.
|
|
4723
|
+
Writes require trusted server code with \`createServerClient\`; do not run server credentials in React client components.
|
|
4690
4724
|
|
|
4691
4725
|
\`\`\`typescript
|
|
4692
|
-
|
|
4693
|
-
mutate({ id: 'product-id', data: { title: 'Updated' } })
|
|
4694
|
-
\`\`\`
|
|
4726
|
+
import { createServerClient } from '@01.software/sdk/server'
|
|
4695
4727
|
|
|
4696
|
-
|
|
4697
|
-
|
|
4728
|
+
const server = createServerClient({
|
|
4729
|
+
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
4730
|
+
secretKey: process.env.SOFTWARE_SECRET_KEY!,
|
|
4731
|
+
})
|
|
4698
4732
|
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4733
|
+
await server.collections.from('products').create({ title: 'New Product', status: 'draft' })
|
|
4734
|
+
await server.collections.from('products').update('product-id', { title: 'Updated' })
|
|
4735
|
+
await server.collections.from('products').remove('product-id')
|
|
4702
4736
|
\`\`\`
|
|
4703
4737
|
|
|
4704
4738
|
## Cache Utilities
|
|
4705
4739
|
|
|
4706
4740
|
\`\`\`typescript
|
|
4707
4741
|
// Invalidate cache
|
|
4708
|
-
|
|
4742
|
+
query.invalidateQueries('products', 'list')
|
|
4709
4743
|
|
|
4710
4744
|
// SSR prefetch
|
|
4711
|
-
await
|
|
4712
|
-
await
|
|
4713
|
-
await
|
|
4745
|
+
await query.prefetchQuery({ collection: 'products', options: { limit: 10 } })
|
|
4746
|
+
await query.prefetchQueryById({ collection: 'products', id: 'id' })
|
|
4747
|
+
await query.prefetchInfiniteQuery({ collection: 'products', pageSize: 20 })
|
|
4714
4748
|
|
|
4715
4749
|
// Get/set cached data
|
|
4716
|
-
const cached =
|
|
4717
|
-
|
|
4750
|
+
const cached = query.getQueryData('products', 'list')
|
|
4751
|
+
query.setQueryData('products', 'list', newData)
|
|
4718
4752
|
\`\`\`
|
|
4719
4753
|
|
|
4720
4754
|
For ecommerce reads, prefer \`products.listing.*\` for card/search pricing and keep authoritative sellable pricing on \`product-variants.price\`.
|
|
@@ -4961,7 +4995,7 @@ const result3 = await client.collections.from('products').find({ sort: '-isFeatu
|
|
|
4961
4995
|
## Full Example
|
|
4962
4996
|
|
|
4963
4997
|
\`\`\`typescript
|
|
4964
|
-
import { createServerClient } from '@01.software/sdk'
|
|
4998
|
+
import { createServerClient } from '@01.software/sdk/server'
|
|
4965
4999
|
|
|
4966
5000
|
const serverClient = createServerClient({
|
|
4967
5001
|
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
@@ -4989,14 +5023,22 @@ console.log(result.hasNextPage) // true
|
|
|
4989
5023
|
var metadata43 = {
|
|
4990
5024
|
name: "docs-react-query",
|
|
4991
5025
|
title: "React Query Hooks",
|
|
4992
|
-
description: "01.software SDK React Query hooks reference (
|
|
5026
|
+
description: "01.software SDK React Query hooks reference (@01.software/sdk/query)"
|
|
4993
5027
|
};
|
|
4994
5028
|
function handler13() {
|
|
4995
5029
|
return `# React Query Hooks
|
|
4996
5030
|
|
|
4997
|
-
React Query hooks are
|
|
5031
|
+
React Query hooks are opt-in through \`@01.software/sdk/query\`. They provide automatic caching, background refetching, and cache invalidation without making root \`createClient\` consumers install React Query.
|
|
4998
5032
|
|
|
4999
|
-
|
|
5033
|
+
Install \`@tanstack/react-query\` and React peers only when importing this sub-path.
|
|
5034
|
+
|
|
5035
|
+
\`\`\`typescript
|
|
5036
|
+
import { createClient } from '@01.software/sdk'
|
|
5037
|
+
import { createQueryHooks } from '@01.software/sdk/query'
|
|
5038
|
+
|
|
5039
|
+
const client = createClient({ publishableKey })
|
|
5040
|
+
const query = createQueryHooks(client)
|
|
5041
|
+
\`\`\`
|
|
5000
5042
|
|
|
5001
5043
|
## Query Hooks
|
|
5002
5044
|
|
|
@@ -5004,7 +5046,7 @@ React Query hooks are available on the browser-side \`Client\` via \`client.quer
|
|
|
5004
5046
|
Query a collection list with automatic caching.
|
|
5005
5047
|
|
|
5006
5048
|
\`\`\`typescript
|
|
5007
|
-
const { data, isLoading, error } =
|
|
5049
|
+
const { data, isLoading, error } = query.useQuery({
|
|
5008
5050
|
collection: 'products',
|
|
5009
5051
|
options: {
|
|
5010
5052
|
where: { status: { equals: 'published' } },
|
|
@@ -5022,7 +5064,7 @@ Suspense-mode list query. Throws a promise while loading (use with React Suspens
|
|
|
5022
5064
|
|
|
5023
5065
|
\`\`\`typescript
|
|
5024
5066
|
// Inside a Suspense boundary
|
|
5025
|
-
const { data } =
|
|
5067
|
+
const { data } = query.useSuspenseQuery({
|
|
5026
5068
|
collection: 'products',
|
|
5027
5069
|
options: { limit: 10 },
|
|
5028
5070
|
})
|
|
@@ -5033,7 +5075,7 @@ const { data } = client.query.useSuspenseQuery({
|
|
|
5033
5075
|
Get a single document by ID.
|
|
5034
5076
|
|
|
5035
5077
|
\`\`\`typescript
|
|
5036
|
-
const { data, isLoading } =
|
|
5078
|
+
const { data, isLoading } = query.useQueryById({
|
|
5037
5079
|
collection: 'products',
|
|
5038
5080
|
id: 'product-id',
|
|
5039
5081
|
})
|
|
@@ -5044,7 +5086,7 @@ const { data, isLoading } = client.query.useQueryById({
|
|
|
5044
5086
|
Suspense-mode single document query.
|
|
5045
5087
|
|
|
5046
5088
|
\`\`\`typescript
|
|
5047
|
-
const { data } =
|
|
5089
|
+
const { data } = query.useSuspenseQueryById({
|
|
5048
5090
|
collection: 'products',
|
|
5049
5091
|
id: 'product-id',
|
|
5050
5092
|
})
|
|
@@ -5060,7 +5102,7 @@ const {
|
|
|
5060
5102
|
fetchNextPage,
|
|
5061
5103
|
hasNextPage,
|
|
5062
5104
|
isFetchingNextPage,
|
|
5063
|
-
} =
|
|
5105
|
+
} = query.useInfiniteQuery({
|
|
5064
5106
|
collection: 'products',
|
|
5065
5107
|
options: {
|
|
5066
5108
|
where: { status: { equals: 'published' } },
|
|
@@ -5076,55 +5118,33 @@ const {
|
|
|
5076
5118
|
Suspense-mode infinite scroll.
|
|
5077
5119
|
|
|
5078
5120
|
\`\`\`typescript
|
|
5079
|
-
const { data, fetchNextPage, hasNextPage } =
|
|
5121
|
+
const { data, fetchNextPage, hasNextPage } = query.useSuspenseInfiniteQuery({
|
|
5080
5122
|
collection: 'products',
|
|
5081
5123
|
pageSize: 20,
|
|
5082
5124
|
})
|
|
5083
5125
|
\`\`\`
|
|
5084
5126
|
|
|
5085
|
-
##
|
|
5086
|
-
|
|
5087
|
-
Mutation hooks automatically invalidate relevant cache keys after success.
|
|
5127
|
+
## Writes
|
|
5088
5128
|
|
|
5089
|
-
|
|
5090
|
-
Create a document and invalidate list cache.
|
|
5129
|
+
Keep writes in trusted server code. Use a server action, API route, or backend service with \`createServerClient\`, then invalidate browser React Query caches after the action completes.
|
|
5091
5130
|
|
|
5092
5131
|
\`\`\`typescript
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
})
|
|
5096
|
-
|
|
5097
|
-
// Fire and forget
|
|
5098
|
-
mutate({ title: 'New Product', status: 'draft' })
|
|
5099
|
-
|
|
5100
|
-
// Await result
|
|
5101
|
-
const result = await mutateAsync({ title: 'New Product', status: 'draft' })
|
|
5102
|
-
// result.doc - created document
|
|
5103
|
-
\`\`\`
|
|
5104
|
-
|
|
5105
|
-
For ecommerce reads, price-oriented product cards should consume \`products.listing.minPrice/maxPrice\`. Authoritative sellable pricing still lives on \`product-variants.price\`.
|
|
5132
|
+
// app/products/actions.ts
|
|
5133
|
+
'use server'
|
|
5106
5134
|
|
|
5107
|
-
|
|
5108
|
-
Update a document and invalidate list + detail cache.
|
|
5135
|
+
import { createServerClient } from '@01.software/sdk/server'
|
|
5109
5136
|
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5137
|
+
const server = createServerClient({
|
|
5138
|
+
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
5139
|
+
secretKey: process.env.SOFTWARE_SECRET_KEY!,
|
|
5113
5140
|
})
|
|
5114
5141
|
|
|
5115
|
-
|
|
5142
|
+
export async function createProduct(data: { title: string; status: 'draft' | 'published' }) {
|
|
5143
|
+
return server.collections.from('products').create(data)
|
|
5144
|
+
}
|
|
5116
5145
|
\`\`\`
|
|
5117
5146
|
|
|
5118
|
-
|
|
5119
|
-
Remove a document and invalidate list cache.
|
|
5120
|
-
|
|
5121
|
-
\`\`\`typescript
|
|
5122
|
-
const { mutate, mutateAsync } = client.query.useRemove({
|
|
5123
|
-
collection: 'products',
|
|
5124
|
-
})
|
|
5125
|
-
|
|
5126
|
-
mutate('product-id')
|
|
5127
|
-
\`\`\`
|
|
5147
|
+
For ecommerce reads, price-oriented product cards should consume \`products.listing.minPrice/maxPrice\`. Authoritative sellable pricing still lives on \`product-variants.price\`.
|
|
5128
5148
|
|
|
5129
5149
|
## Cache Utilities
|
|
5130
5150
|
|
|
@@ -5133,10 +5153,10 @@ Manually invalidate cached queries for a collection.
|
|
|
5133
5153
|
|
|
5134
5154
|
\`\`\`typescript
|
|
5135
5155
|
// Invalidate all list queries for a collection
|
|
5136
|
-
|
|
5156
|
+
query.invalidateQueries('products', 'list')
|
|
5137
5157
|
|
|
5138
5158
|
// Invalidate all queries for a collection (list + detail)
|
|
5139
|
-
|
|
5159
|
+
query.invalidateQueries('products')
|
|
5140
5160
|
\`\`\`
|
|
5141
5161
|
|
|
5142
5162
|
### getQueryData() / setQueryData()
|
|
@@ -5144,10 +5164,10 @@ Read and write the React Query cache directly.
|
|
|
5144
5164
|
|
|
5145
5165
|
\`\`\`typescript
|
|
5146
5166
|
// Read cached data
|
|
5147
|
-
const cached =
|
|
5167
|
+
const cached = query.getQueryData('products', 'list')
|
|
5148
5168
|
|
|
5149
5169
|
// Write to cache (useful for optimistic updates)
|
|
5150
|
-
|
|
5170
|
+
query.setQueryData('products', 'list', newData)
|
|
5151
5171
|
\`\`\`
|
|
5152
5172
|
|
|
5153
5173
|
## SSR Prefetching
|
|
@@ -5157,34 +5177,37 @@ Prefetch data in server components for instant hydration on the client.
|
|
|
5157
5177
|
\`\`\`typescript
|
|
5158
5178
|
// app/products/page.tsx (Next.js App Router Server Component)
|
|
5159
5179
|
import { HydrationBoundary, dehydrate } from '@tanstack/react-query'
|
|
5160
|
-
import { createServerClient } from '@01.software/sdk'
|
|
5180
|
+
import { createServerClient } from '@01.software/sdk/server'
|
|
5181
|
+
import { createServerQueryHooks, getQueryClient } from '@01.software/sdk/query'
|
|
5161
5182
|
|
|
5162
5183
|
export default async function ProductsPage() {
|
|
5163
5184
|
const serverClient = createServerClient({
|
|
5164
5185
|
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
5165
5186
|
secretKey: process.env.SOFTWARE_SECRET_KEY!,
|
|
5166
5187
|
})
|
|
5188
|
+
const queryClient = getQueryClient()
|
|
5189
|
+
const serverQuery = createServerQueryHooks(serverClient, queryClient)
|
|
5167
5190
|
|
|
5168
5191
|
// Prefetch list
|
|
5169
|
-
await
|
|
5192
|
+
await serverQuery.prefetchQuery({
|
|
5170
5193
|
collection: 'products',
|
|
5171
5194
|
options: { limit: 20, where: { status: { equals: 'published' } } },
|
|
5172
5195
|
})
|
|
5173
5196
|
|
|
5174
5197
|
// Prefetch single item
|
|
5175
|
-
await
|
|
5198
|
+
await serverQuery.prefetchQueryById({
|
|
5176
5199
|
collection: 'products',
|
|
5177
5200
|
id: 'product-id',
|
|
5178
5201
|
})
|
|
5179
5202
|
|
|
5180
5203
|
// Prefetch infinite query
|
|
5181
|
-
await
|
|
5204
|
+
await serverQuery.prefetchInfiniteQuery({
|
|
5182
5205
|
collection: 'products',
|
|
5183
5206
|
pageSize: 20,
|
|
5184
5207
|
})
|
|
5185
5208
|
|
|
5186
5209
|
return (
|
|
5187
|
-
<HydrationBoundary state={dehydrate(
|
|
5210
|
+
<HydrationBoundary state={dehydrate(queryClient)}>
|
|
5188
5211
|
<ProductList />
|
|
5189
5212
|
</HydrationBoundary>
|
|
5190
5213
|
)
|
|
@@ -5197,13 +5220,15 @@ export default async function ProductsPage() {
|
|
|
5197
5220
|
'use client'
|
|
5198
5221
|
|
|
5199
5222
|
import { createClient } from '@01.software/sdk'
|
|
5223
|
+
import { createQueryHooks } from '@01.software/sdk/query'
|
|
5200
5224
|
|
|
5201
5225
|
const client = createClient({
|
|
5202
5226
|
publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!,
|
|
5203
5227
|
})
|
|
5228
|
+
const query = createQueryHooks(client)
|
|
5204
5229
|
|
|
5205
5230
|
export function ProductList() {
|
|
5206
|
-
const { data, isLoading, error } =
|
|
5231
|
+
const { data, isLoading, error } = query.useQuery({
|
|
5207
5232
|
collection: 'products',
|
|
5208
5233
|
options: {
|
|
5209
5234
|
where: { status: { equals: 'published' } },
|
|
@@ -5212,20 +5237,13 @@ export function ProductList() {
|
|
|
5212
5237
|
},
|
|
5213
5238
|
})
|
|
5214
5239
|
|
|
5215
|
-
const { mutate: removeProduct } = client.query.useRemove({
|
|
5216
|
-
collection: 'products',
|
|
5217
|
-
})
|
|
5218
|
-
|
|
5219
5240
|
if (isLoading) return <div>Loading...</div>
|
|
5220
5241
|
if (error) return <div>Error: {error.message}</div>
|
|
5221
5242
|
|
|
5222
5243
|
return (
|
|
5223
5244
|
<ul>
|
|
5224
5245
|
{data?.docs.map((product) => (
|
|
5225
|
-
<li key={product.id}>
|
|
5226
|
-
{product.title}
|
|
5227
|
-
<button onClick={() => removeProduct(product.id)}>Delete</button>
|
|
5228
|
-
</li>
|
|
5246
|
+
<li key={product.id}>{product.title}</li>
|
|
5229
5247
|
))}
|
|
5230
5248
|
</ul>
|
|
5231
5249
|
)
|
|
@@ -5245,7 +5263,7 @@ function handler14() {
|
|
|
5245
5263
|
Server-side operations are available via \`client.commerce\` on \`ServerClient\`. Use \`createServerClient\` with both \`publishableKey\` and \`secretKey\`.
|
|
5246
5264
|
|
|
5247
5265
|
\`\`\`typescript
|
|
5248
|
-
import { createServerClient } from '@01.software/sdk'
|
|
5266
|
+
import { createServerClient } from '@01.software/sdk/server'
|
|
5249
5267
|
|
|
5250
5268
|
const client = createServerClient({
|
|
5251
5269
|
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
@@ -5694,9 +5712,9 @@ The SDK provides two client types for different execution environments.
|
|
|
5694
5712
|
| Read (\`find\`, \`findById\`, \`count\`) | Yes | Yes |
|
|
5695
5713
|
| Write (\`create\`, \`update\`, \`remove\`) | No | Yes |
|
|
5696
5714
|
| Bulk (\`updateMany\`, \`removeMany\`) | No | Yes |
|
|
5697
|
-
| React Query hooks (
|
|
5715
|
+
| React Query hooks (\`@01.software/sdk/query\`) | Yes | Yes (SSR prefetch) |
|
|
5698
5716
|
| Customer auth (\`client.customer\`) | Yes | No |
|
|
5699
|
-
|
|
|
5717
|
+
| Commerce/server APIs (\`server.commerce\`, server CRUD) | No | Yes |
|
|
5700
5718
|
|
|
5701
5719
|
## Client (createClient)
|
|
5702
5720
|
|
|
@@ -5704,16 +5722,18 @@ Use in browser code, React client components, and anywhere the secret key must n
|
|
|
5704
5722
|
|
|
5705
5723
|
\`\`\`typescript
|
|
5706
5724
|
import { createClient } from '@01.software/sdk'
|
|
5725
|
+
import { createQueryHooks } from '@01.software/sdk/query'
|
|
5707
5726
|
|
|
5708
5727
|
const client = createClient({
|
|
5709
5728
|
publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!,
|
|
5710
5729
|
})
|
|
5730
|
+
const query = createQueryHooks(client)
|
|
5711
5731
|
|
|
5712
5732
|
// Read data
|
|
5713
5733
|
const products = await client.collections.from('products').find({ limit: 10 })
|
|
5714
5734
|
|
|
5715
5735
|
// React Query hooks
|
|
5716
|
-
const { data } =
|
|
5736
|
+
const { data } = query.useQuery({ collection: 'products' })
|
|
5717
5737
|
|
|
5718
5738
|
// Customer auth
|
|
5719
5739
|
await client.customer.login({ email, password })
|
|
@@ -5726,7 +5746,7 @@ await client.customer.login({ email, password })
|
|
|
5726
5746
|
Use in server components, API routes, server actions, and background jobs. Has full CRUD access.
|
|
5727
5747
|
|
|
5728
5748
|
\`\`\`typescript
|
|
5729
|
-
import { createServerClient } from '@01.software/sdk'
|
|
5749
|
+
import { createServerClient } from '@01.software/sdk/server'
|
|
5730
5750
|
|
|
5731
5751
|
const client = createServerClient({
|
|
5732
5752
|
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
@@ -5747,11 +5767,14 @@ await client.collections.from('product-variants').create({
|
|
|
5747
5767
|
})
|
|
5748
5768
|
await client.collections.from('products').remove('product-id')
|
|
5749
5769
|
|
|
5750
|
-
//
|
|
5770
|
+
// Commerce API (orders, carts, etc.)
|
|
5751
5771
|
await client.commerce.orders.create({ ... })
|
|
5752
5772
|
await client.commerce.orders.checkout({ ... })
|
|
5753
5773
|
\`\`\`
|
|
5754
5774
|
|
|
5775
|
+
Server-only code must import \`createServerClient\` from the \`/server\`
|
|
5776
|
+
sub-path.
|
|
5777
|
+
|
|
5755
5778
|
**Environment variables**:
|
|
5756
5779
|
- \`SOFTWARE_PUBLISHABLE_KEY\` \u2014 publishable key (no NEXT_PUBLIC prefix, server-only)
|
|
5757
5780
|
- \`SOFTWARE_SECRET_KEY\` \u2014 server credential
|
|
@@ -5781,7 +5804,7 @@ deploying again.
|
|
|
5781
5804
|
|
|
5782
5805
|
\`\`\`typescript
|
|
5783
5806
|
// lib/sdk.ts \u2014 server-only module
|
|
5784
|
-
import { createServerClient } from '@01.software/sdk'
|
|
5807
|
+
import { createServerClient } from '@01.software/sdk/server'
|
|
5785
5808
|
|
|
5786
5809
|
export function getServerClient() {
|
|
5787
5810
|
return createServerClient({
|
|
@@ -5794,10 +5817,12 @@ export function getServerClient() {
|
|
|
5794
5817
|
\`\`\`typescript
|
|
5795
5818
|
// lib/sdk-client.ts \u2014 browser-safe module
|
|
5796
5819
|
import { createClient } from '@01.software/sdk'
|
|
5820
|
+
import { createQueryHooks } from '@01.software/sdk/query'
|
|
5797
5821
|
|
|
5798
5822
|
export const browserClient = createClient({
|
|
5799
5823
|
publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!,
|
|
5800
5824
|
})
|
|
5825
|
+
export const browserQuery = createQueryHooks(browserClient)
|
|
5801
5826
|
\`\`\`
|
|
5802
5827
|
|
|
5803
5828
|
\`\`\`typescript
|
|
@@ -5814,10 +5839,10 @@ export default async function ProductsPage() {
|
|
|
5814
5839
|
\`\`\`typescript
|
|
5815
5840
|
// components/product-list.tsx \u2014 Client Component
|
|
5816
5841
|
'use client'
|
|
5817
|
-
import {
|
|
5842
|
+
import { browserQuery } from '@/lib/sdk-client'
|
|
5818
5843
|
|
|
5819
5844
|
export function ProductList() {
|
|
5820
|
-
const { data } =
|
|
5845
|
+
const { data } = browserQuery.useQuery({ collection: 'products' })
|
|
5821
5846
|
return <ul>{data?.docs.map(p => <li key={p.id}>{p.title}</li>)}</ul>
|
|
5822
5847
|
}
|
|
5823
5848
|
\`\`\`
|
|
@@ -5848,7 +5873,7 @@ Upload files using the \`images\` collection (tenant-scoped, unified image store
|
|
|
5848
5873
|
Use \`ServerClient\` with \`FormData\` to upload images server-side.
|
|
5849
5874
|
|
|
5850
5875
|
\`\`\`typescript
|
|
5851
|
-
import { createServerClient } from '@01.software/sdk'
|
|
5876
|
+
import { createServerClient } from '@01.software/sdk/server'
|
|
5852
5877
|
|
|
5853
5878
|
const client = createServerClient({
|
|
5854
5879
|
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
@@ -5871,7 +5896,7 @@ async function uploadImage(file: File) {
|
|
|
5871
5896
|
\`\`\`typescript
|
|
5872
5897
|
// app/api/upload/route.ts
|
|
5873
5898
|
import { NextRequest, NextResponse } from 'next/server'
|
|
5874
|
-
import { createServerClient } from '@01.software/sdk'
|
|
5899
|
+
import { createServerClient } from '@01.software/sdk/server'
|
|
5875
5900
|
|
|
5876
5901
|
const client = createServerClient({
|
|
5877
5902
|
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
@@ -5901,7 +5926,7 @@ export async function POST(req: NextRequest) {
|
|
|
5901
5926
|
// actions/upload.ts
|
|
5902
5927
|
'use server'
|
|
5903
5928
|
|
|
5904
|
-
import { createServerClient } from '@01.software/sdk'
|
|
5929
|
+
import { createServerClient } from '@01.software/sdk/server'
|
|
5905
5930
|
|
|
5906
5931
|
const client = createServerClient({
|
|
5907
5932
|
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
@@ -6124,29 +6149,38 @@ const client = createClient({
|
|
|
6124
6149
|
const detail = await client.commerce.product.detail({ slug: 'my-product' })
|
|
6125
6150
|
if (!detail) return notFound()
|
|
6126
6151
|
const selection = resolveProductSelection(detail, {
|
|
6127
|
-
search: '?opt.color=black
|
|
6152
|
+
search: '?opt.option-color=color-black',
|
|
6128
6153
|
})
|
|
6129
6154
|
// selection.selectedVariant, selection.price, selection.stock, selection.media
|
|
6130
6155
|
\`\`\`
|
|
6131
6156
|
|
|
6132
6157
|
## URL round-trip
|
|
6133
6158
|
|
|
6134
|
-
Use the SDK codec for
|
|
6159
|
+
Use the SDK codec for canonical selection URLs. Complete selections use
|
|
6160
|
+
\`variant=<variantId>\`; partial selections use
|
|
6161
|
+
\`opt.<optionId>=<valueId>\`. Older
|
|
6162
|
+
\`opt.<optionSlug>=<valueSlug>\` URLs still decode during Stage 1, but slugs are
|
|
6163
|
+
compatibility metadata rather than canonical identity.
|
|
6135
6164
|
|
|
6136
6165
|
\`\`\`typescript
|
|
6137
6166
|
import { createProductSelectionCodec } from '@01.software/sdk'
|
|
6138
6167
|
|
|
6139
6168
|
const codec = createProductSelectionCodec(detail)
|
|
6140
|
-
const selection = codec.parse('?opt.color=black')
|
|
6141
|
-
const
|
|
6169
|
+
const selection = codec.parse('?opt.option-color=color-black')
|
|
6170
|
+
const selectionQuery = codec.stringify(selection)
|
|
6142
6171
|
\`\`\`
|
|
6143
6172
|
|
|
6144
|
-
|
|
6173
|
+
Use IDs from \`detail.options[].id\` and \`detail.options[].values[].id\` when building new selection links. Slugs remain useful for display and older inbound URLs, but new outbound URLs should use the codec output.
|
|
6174
|
+
|
|
6175
|
+
Value-slug-only URLs such as \`?black\` or \`?color=black\` are rejected because option titles and values are not stable identifiers.
|
|
6145
6176
|
|
|
6146
6177
|
## React Query hook variant
|
|
6147
6178
|
|
|
6148
6179
|
\`\`\`typescript
|
|
6149
|
-
|
|
6180
|
+
import { createQueryHooks } from '@01.software/sdk/query'
|
|
6181
|
+
|
|
6182
|
+
const query = createQueryHooks(client)
|
|
6183
|
+
const { data: detail, isLoading } = query.useProductDetailBySlug(slug)
|
|
6150
6184
|
\`\`\`
|
|
6151
6185
|
|
|
6152
6186
|
Cache invalidates automatically when any of 10 detail-relevant collections is mutated through SDK mutation hooks.
|
|
@@ -6540,4 +6574,4 @@ export {
|
|
|
6540
6574
|
flushMcpTelemetrySummary,
|
|
6541
6575
|
createServer
|
|
6542
6576
|
};
|
|
6543
|
-
//# sourceMappingURL=chunk-
|
|
6577
|
+
//# sourceMappingURL=chunk-F5VI4HQM.js.map
|