@01.software/cli 0.10.3 → 0.10.5

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.
@@ -1106,7 +1106,7 @@ async function swallow(promise) {
1106
1106
  import { z as z3 } from "zod";
1107
1107
 
1108
1108
  // src/lib/client.ts
1109
- import { createServerClient } from "@01.software/sdk";
1109
+ import { createServerClient } from "@01.software/sdk/server";
1110
1110
  var MISSING_HTTP_AUTH_CONTEXT_ERROR2 = "MCP HTTP requests require a validated OAuth tenant context before tool execution.";
1111
1111
  var HTTP_OAUTH_SDK_CLIENT_ERROR = "MCP HTTP OAuth requests cannot use SDK-backed tools. Use reviewed Console service endpoints for OAuth transport.";
1112
1112
  function getClient() {
@@ -1939,18 +1939,22 @@ async function productDetail({
1939
1939
  // src/tools/product-upsert.ts
1940
1940
  import { z as z23 } from "zod";
1941
1941
  var optionValueSchema = z23.object({
1942
- id: z23.string().optional().describe("Existing option-value ID for updates"),
1942
+ id: z23.string().optional().describe("Stable existing option-value ID for rename-safe updates"),
1943
1943
  value: z23.string().describe("Display label (e.g. Black, S)"),
1944
- slug: z23.string().optional().describe("Canonical value token used in variant maps and URLs"),
1944
+ slug: z23.string().optional().describe(
1945
+ "Optional compatibility value token. The server generates one from value on create when omitted; not canonical identity."
1946
+ ),
1945
1947
  swatchColor: z23.string().nullable().optional(),
1946
1948
  thumbnail: z23.string().nullable().optional(),
1947
1949
  images: z23.array(z23.string()).optional(),
1948
1950
  metadata: z23.unknown().optional()
1949
1951
  });
1950
1952
  var optionSchema = z23.object({
1951
- id: z23.string().optional().describe("Existing option ID for updates"),
1953
+ id: z23.string().optional().describe("Stable existing option ID for rename-safe updates"),
1952
1954
  title: z23.string().describe("Option name (e.g. Color, Size)"),
1953
- slug: z23.string().optional().describe("Canonical option token used in variant maps and URLs"),
1955
+ slug: z23.string().optional().describe(
1956
+ "Optional compatibility option token. The server generates one from title on create when omitted; not canonical identity."
1957
+ ),
1954
1958
  values: z23.array(optionValueSchema).describe("Allowed option values")
1955
1959
  });
1956
1960
  var variantOptionValueSchema = z23.object({
@@ -1964,7 +1968,7 @@ var variantSchema = z23.object({
1964
1968
  z23.record(z23.string(), z23.union([z23.string(), variantOptionValueSchema])),
1965
1969
  z23.array(z23.string())
1966
1970
  ]).optional().describe(
1967
- "Option-value selection. Prefer canonical { optionSlug: valueSlug } maps. Object values may use { valueSlug, valueId, value }. Exact { OptionTitle: ValueLabel } maps remain compatibility-only and fail when labels are ambiguous. Arrays of option-value IDs are also accepted."
1971
+ "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."
1968
1972
  ),
1969
1973
  sku: z23.string().nullable().optional(),
1970
1974
  title: z23.string().nullable().optional(),
@@ -1986,10 +1990,10 @@ var schema23 = {
1986
1990
  "Product fields. Include `id` to update an existing product; omit for create (then `title` is required)."
1987
1991
  ),
1988
1992
  options: z23.array(optionSchema).optional().describe(
1989
- "Option definitions. Prefer explicit option slug and value slugs. Omitted options on an existing product are deleted (with their values)."
1993
+ "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)."
1990
1994
  ),
1991
1995
  variants: z23.array(variantSchema).optional().describe(
1992
- "Variant rows. Prefer canonical { optionSlug: valueSlug } optionValues maps. 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)."
1996
+ "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)."
1993
1997
  )
1994
1998
  };
1995
1999
  var metadata23 = {
@@ -2290,14 +2294,16 @@ var recipes = {
2290
2294
  recommendedSurface: "react-query",
2291
2295
  runtime: "browser",
2292
2296
  code: `import { createClient } from '@01.software/sdk'
2297
+ import { createQueryHooks } from '@01.software/sdk/query'
2293
2298
 
2294
2299
  const client = createClient({
2295
2300
  publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!,
2296
2301
  })
2302
+ const query = createQueryHooks(client)
2297
2303
 
2298
2304
  // React Query hook \u2014 handles caching and background updates
2299
2305
  function ProductList() {
2300
- const { data, isLoading, error } = client.query.useQuery({
2306
+ const { data, isLoading, error } = query.useQuery({
2301
2307
  collection: 'products',
2302
2308
  options: {
2303
2309
  where: { status: { equals: 'published' } },
@@ -2329,7 +2335,7 @@ function ProductList() {
2329
2335
  title: "Fetch collection list (server)",
2330
2336
  recommendedSurface: "query-builder",
2331
2337
  runtime: "server",
2332
- code: `import { createServerClient } from '@01.software/sdk'
2338
+ code: `import { createServerClient } from '@01.software/sdk/server'
2333
2339
 
2334
2340
  const client = createServerClient({
2335
2341
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -2362,13 +2368,15 @@ const result = await client.collections.from('products').find({
2362
2368
  recommendedSurface: "react-query",
2363
2369
  runtime: "browser",
2364
2370
  code: `import { createClient } from '@01.software/sdk'
2371
+ import { createQueryHooks } from '@01.software/sdk/query'
2365
2372
 
2366
2373
  const client = createClient({
2367
2374
  publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!,
2368
2375
  })
2376
+ const query = createQueryHooks(client)
2369
2377
 
2370
2378
  function ProductDetail({ id }: { id: string }) {
2371
- const { data, isLoading } = client.query.useQueryById({
2379
+ const { data, isLoading } = query.useQueryById({
2372
2380
  collection: 'products',
2373
2381
  id,
2374
2382
  })
@@ -2388,7 +2396,7 @@ function ProductDetail({ id }: { id: string }) {
2388
2396
  title: "Fetch single item by ID (server)",
2389
2397
  recommendedSurface: "query-builder",
2390
2398
  runtime: "server",
2391
- code: `import { createServerClient } from '@01.software/sdk'
2399
+ code: `import { createServerClient } from '@01.software/sdk/server'
2392
2400
 
2393
2401
  const client = createServerClient({
2394
2402
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -2411,7 +2419,7 @@ console.log(product.title)`,
2411
2419
  title: "Create a new item (server only)",
2412
2420
  recommendedSurface: "server-api",
2413
2421
  runtime: "server",
2414
- code: `import { createServerClient } from '@01.software/sdk'
2422
+ code: `import { createServerClient } from '@01.software/sdk/server'
2415
2423
 
2416
2424
  const client = createServerClient({
2417
2425
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -2440,7 +2448,7 @@ const result = await client.collections.from('products').create({
2440
2448
  title: "Update an existing item (server only)",
2441
2449
  recommendedSurface: "server-api",
2442
2450
  runtime: "server",
2443
- code: `import { createServerClient } from '@01.software/sdk'
2451
+ code: `import { createServerClient } from '@01.software/sdk/server'
2444
2452
 
2445
2453
  const client = createServerClient({
2446
2454
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -2469,7 +2477,7 @@ const result = await client.collections.from('products').update('product-id', {
2469
2477
  title: "Delete an item (server only)",
2470
2478
  recommendedSurface: "server-api",
2471
2479
  runtime: "server",
2472
- code: `import { createServerClient } from '@01.software/sdk'
2480
+ code: `import { createServerClient } from '@01.software/sdk/server'
2473
2481
 
2474
2482
  const client = createServerClient({
2475
2483
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -2494,10 +2502,12 @@ console.log('Deleted:', deleted.title)`,
2494
2502
  recommendedSurface: "react-query",
2495
2503
  runtime: "browser",
2496
2504
  code: `import { createClient } from '@01.software/sdk'
2505
+ import { createQueryHooks } from '@01.software/sdk/query'
2497
2506
 
2498
2507
  const client = createClient({
2499
2508
  publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!,
2500
2509
  })
2510
+ const query = createQueryHooks(client)
2501
2511
 
2502
2512
  function InfiniteProductList() {
2503
2513
  const {
@@ -2506,7 +2516,7 @@ function InfiniteProductList() {
2506
2516
  hasNextPage,
2507
2517
  isFetchingNextPage,
2508
2518
  isLoading,
2509
- } = client.query.useInfiniteQuery({
2519
+ } = query.useInfiniteQuery({
2510
2520
  collection: 'products',
2511
2521
  options: { where: { status: { equals: 'published' } } },
2512
2522
  pageSize: 20,
@@ -2541,7 +2551,8 @@ function InfiniteProductList() {
2541
2551
  title: "SSR data prefetching (server)",
2542
2552
  recommendedSurface: "react-query",
2543
2553
  runtime: "server",
2544
- code: `import { createServerClient } from '@01.software/sdk'
2554
+ code: `import { createServerClient } from '@01.software/sdk/server'
2555
+ import { createServerQueryHooks, getQueryClient } from '@01.software/sdk/query'
2545
2556
  import { dehydrate, HydrationBoundary } from '@tanstack/react-query'
2546
2557
 
2547
2558
  // In a Next.js Server Component or getServerSideProps:
@@ -2549,27 +2560,29 @@ const client = createServerClient({
2549
2560
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
2550
2561
  secretKey: process.env.SOFTWARE_SECRET_KEY!,
2551
2562
  })
2563
+ const queryClient = getQueryClient()
2564
+ const serverQuery = createServerQueryHooks(client, queryClient)
2552
2565
 
2553
2566
  // Prefetch list \u2014 client hydrates instantly without a loading state
2554
- await client.query.prefetchQuery({
2567
+ await serverQuery.prefetchQuery({
2555
2568
  collection: 'products',
2556
2569
  options: { limit: 20 },
2557
2570
  })
2558
2571
 
2559
2572
  // Prefetch single item
2560
- await client.query.prefetchQueryById({
2573
+ await serverQuery.prefetchQueryById({
2561
2574
  collection: 'products',
2562
2575
  id: 'product-id',
2563
2576
  })
2564
2577
 
2565
2578
  // Prefetch infinite list
2566
- await client.query.prefetchInfiniteQuery({
2579
+ await serverQuery.prefetchInfiniteQuery({
2567
2580
  collection: 'products',
2568
2581
  pageSize: 20,
2569
2582
  })
2570
2583
 
2571
2584
  // Dehydrate and pass to client
2572
- const state = dehydrate(client.query.queryClient)
2585
+ const state = dehydrate(queryClient)
2573
2586
 
2574
2587
  export default function Page() {
2575
2588
  return (
@@ -2639,7 +2652,7 @@ await client.customer.resetPassword(token, 'newPassword123')`,
2639
2652
  title: "File upload pattern (server)",
2640
2653
  recommendedSurface: "server-api",
2641
2654
  runtime: "server",
2642
- code: `import { createServerClient } from '@01.software/sdk'
2655
+ code: `import { createServerClient } from '@01.software/sdk/server'
2643
2656
 
2644
2657
  const client = createServerClient({
2645
2658
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -2670,7 +2683,7 @@ const result = await client.collections.from('images').create(formData as unknow
2670
2683
  title: "Bulk update/delete (server only)",
2671
2684
  recommendedSurface: "server-api",
2672
2685
  runtime: "server",
2673
- code: `import { createServerClient } from '@01.software/sdk'
2686
+ code: `import { createServerClient } from '@01.software/sdk/server'
2674
2687
 
2675
2688
  const client = createServerClient({
2676
2689
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -2822,15 +2835,15 @@ var docIndex = [
2822
2835
  },
2823
2836
  {
2824
2837
  title: "Query Builder \u2014 Metadata Generation",
2825
- keywords: ["metadata", "findMetadata", "findMetadataById", "next.js metadata", "seo", "open graph", "query builder"],
2826
- summary: "client.collections.from(collection).findMetadata(options, config) and findMetadataById(id, config) generate Next.js Metadata objects from collection fields.",
2838
+ keywords: ["metadata", "generateMetadata", "extractSeo", "next.js metadata", "seo", "open graph", "query builder"],
2839
+ 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.",
2827
2840
  resourceUri: "docs://sdk/query-builder"
2828
2841
  },
2829
2842
  // React Query Hooks
2830
2843
  {
2831
2844
  title: "React Query \u2014 useQuery()",
2832
2845
  keywords: ["useQuery", "react query", "hook", "list", "fetch", "collection", "cache", "react"],
2833
- summary: "client.query.useQuery({ collection, options }) \u2014 fetches a list with caching and background updates. Use inside a React component.",
2846
+ summary: "createQueryHooks(client).useQuery({ collection, options }) \u2014 fetches a list with caching and background updates. Use inside a React component.",
2834
2847
  resourceUri: "docs://sdk/react-query"
2835
2848
  },
2836
2849
  {
@@ -2842,7 +2855,7 @@ var docIndex = [
2842
2855
  {
2843
2856
  title: "React Query \u2014 useInfiniteQuery()",
2844
2857
  keywords: ["useInfiniteQuery", "useSuspenseInfiniteQuery", "infinite scroll", "load more", "pagination", "react query", "hook"],
2845
- summary: "client.query.useInfiniteQuery({ collection, options, pageSize }) \u2014 infinite scrolling. data.pages is an array of pages; flatten with flatMap for all docs.",
2858
+ summary: "createQueryHooks(client).useInfiniteQuery({ collection, options, pageSize }) \u2014 infinite scrolling. data.pages is an array of pages; flatten with flatMap for all docs.",
2846
2859
  resourceUri: "docs://sdk/react-query"
2847
2860
  },
2848
2861
  {
@@ -2861,7 +2874,7 @@ var docIndex = [
2861
2874
  {
2862
2875
  title: "Cache Invalidation and Manual Cache Management",
2863
2876
  keywords: ["invalidateQueries", "getQueryData", "setQueryData", "cache", "invalidate", "optimistic update", "react query"],
2864
- summary: "client.query.invalidateQueries(collection, type) triggers refetch. getQueryData/setQueryData allow manual optimistic updates.",
2877
+ summary: "query.invalidateQueries(collection, type) triggers refetch. getQueryData/setQueryData allow manual optimistic updates.",
2865
2878
  resourceUri: "docs://sdk/react-query"
2866
2879
  },
2867
2880
  // Filtering
@@ -3012,23 +3025,25 @@ var AUTH_GUIDES = {
3012
3025
  title: "Browser Client Setup",
3013
3026
  envVars: ["NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY"],
3014
3027
  code: `import { createClient } from '@01.software/sdk'
3028
+ import { createQueryHooks } from '@01.software/sdk/query'
3015
3029
 
3016
3030
  const client = createClient({
3017
3031
  publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!
3018
3032
  })
3033
+ const query = createQueryHooks(client)
3019
3034
 
3020
3035
  // Read-only operations + React Query hooks + Customer Auth
3021
- const { data } = client.query.useQuery({ collection: 'products' })`,
3036
+ const { data } = query.useQuery({ collection: 'products' })`,
3022
3037
  notes: [
3023
3038
  "Client is read-only \u2014 no create/update/delete operations",
3024
3039
  "publishableKey is safe to expose in browser (prefixed with NEXT_PUBLIC_)",
3025
- "Includes React Query hooks (client.query) and Customer Auth (client.customer)"
3040
+ "Use createQueryHooks(client) for React Query hooks and client.customer for Customer Auth"
3026
3041
  ]
3027
3042
  },
3028
3043
  "server-client": {
3029
3044
  title: "Server Client Setup",
3030
3045
  envVars: ["SOFTWARE_PUBLISHABLE_KEY", "SOFTWARE_SECRET_KEY"],
3031
- code: `import { createServerClient } from '@01.software/sdk'
3046
+ code: `import { createServerClient } from '@01.software/sdk/server'
3032
3047
 
3033
3048
  const client = createServerClient({
3034
3049
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -3041,7 +3056,7 @@ const result = await client.collections.from('products').create({ title: 'New Pr
3041
3056
  "ServerClient has full CRUD access and must run only in trusted server code",
3042
3057
  "Store server credentials in environment variables and rotate them from the Console",
3043
3058
  "Use in API routes, server actions, or backend services only",
3044
- "React Query hooks available for reads (useQuery, prefetchQuery, etc.) + mutations (useCreate, useUpdate, useRemove)"
3059
+ "Use createServerClient in API routes, server actions, or backend services; keep React Query hooks for browser-safe reads and server prefetching"
3045
3060
  ]
3046
3061
  },
3047
3062
  "customer-auth": {
@@ -3179,37 +3194,35 @@ function generatePattern(collection, operation, surface) {
3179
3194
  );
3180
3195
  }
3181
3196
  const parts2 = [];
3182
- if (operation === "read") {
3197
+ if (operation === "read" || operation === "full-crud") {
3183
3198
  parts2.push(
3184
3199
  `import { createClient } from '@01.software/sdk'`,
3185
- ``,
3186
- `const client = createClient({`,
3187
- ` publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!`,
3188
- `})`,
3189
- ``
3200
+ `import { createQueryHooks } from '@01.software/sdk/query'`
3190
3201
  );
3191
- } else {
3202
+ }
3203
+ if (operation === "write" || operation === "full-crud") {
3204
+ parts2.push(`import { createServerClient } from '@01.software/sdk/server'`);
3205
+ }
3206
+ parts2.push(``);
3207
+ if (operation === "read" || operation === "full-crud") {
3192
3208
  parts2.push(
3193
- `import { createServerClient } from '@01.software/sdk'`,
3194
- ``,
3195
- `// Mutation hooks require ServerClient`,
3196
- `const client = createServerClient({`,
3197
- ` publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,`,
3198
- ` secretKey: process.env.SOFTWARE_SECRET_KEY!`,
3209
+ `const client = createClient({`,
3210
+ ` publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!`,
3199
3211
  `})`,
3212
+ `const query = createQueryHooks(client)`,
3200
3213
  ``
3201
3214
  );
3202
3215
  }
3203
3216
  if (operation === "read" || operation === "full-crud") {
3204
3217
  parts2.push(
3205
3218
  `// List query`,
3206
- `const { data, isLoading } = client.query.useQuery({`,
3219
+ `const { data, isLoading } = query.useQuery({`,
3207
3220
  ` collection: '${collection}',`,
3208
3221
  ` options: { limit: 10 }`,
3209
3222
  `})`,
3210
3223
  ``,
3211
3224
  `// Single item`,
3212
- `const { data: item } = client.query.useQueryById({`,
3225
+ `const { data: item } = query.useQueryById({`,
3213
3226
  ` collection: '${collection}',`,
3214
3227
  ` id: itemId`,
3215
3228
  `})`
@@ -3218,31 +3231,28 @@ function generatePattern(collection, operation, surface) {
3218
3231
  if (operation === "write" || operation === "full-crud") {
3219
3232
  parts2.push(
3220
3233
  ``,
3221
- `// Create (auto cache invalidation)`,
3222
- `const { mutate: create } = client.query.useCreate({ collection: '${collection}' })`,
3223
- `create({ title: 'New Item' })`,
3224
- ``,
3225
- `// Update`,
3226
- `const { mutate: update } = client.query.useUpdate({ collection: '${collection}' })`,
3227
- `update({ id: itemId, data: { title: 'Updated' } })`,
3234
+ `// Writes belong in a server action or API route, not a client component.`,
3235
+ `const server = createServerClient({`,
3236
+ ` publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,`,
3237
+ ` secretKey: process.env.SOFTWARE_SECRET_KEY!`,
3238
+ `})`,
3228
3239
  ``,
3229
- `// Delete`,
3230
- `const { mutate: remove } = client.query.useRemove({ collection: '${collection}' })`,
3231
- `remove(itemId)`
3240
+ `await server.collections.from('${collection}').create({ title: 'New Item' })`,
3241
+ `await server.collections.from('${collection}').update(itemId, { title: 'Updated' })`,
3242
+ `await server.collections.from('${collection}').remove(itemId)`
3232
3243
  );
3233
3244
  }
3234
3245
  return {
3235
3246
  code: parts2.join("\n"),
3236
3247
  notes: [
3237
- "React Query hooks provide automatic caching and background updates",
3238
- "Mutation hooks auto-invalidate related query caches",
3239
- 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"
3248
+ "React Query hooks provide automatic caching and background updates for browser-safe reads",
3249
+ 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"
3240
3250
  ]
3241
3251
  };
3242
3252
  }
3243
3253
  if (surface === "server-api") {
3244
3254
  const parts2 = [
3245
- `import { createServerClient } from '@01.software/sdk'`,
3255
+ `import { createServerClient } from '@01.software/sdk/server'`,
3246
3256
  ``,
3247
3257
  `const client = createServerClient({`,
3248
3258
  ` publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,`,
@@ -3409,8 +3419,12 @@ const result = await client.collections.from('products').find({
3409
3419
  })
3410
3420
 
3411
3421
  // Use React hooks
3422
+ import { createQueryHooks } from '@01.software/sdk/query'
3423
+
3424
+ const query = createQueryHooks(client)
3425
+
3412
3426
  function ProductList() {
3413
- const { data, isLoading } = client.query.useQuery({
3427
+ const { data, isLoading } = query.useQuery({
3414
3428
  collection: 'products',
3415
3429
  options: { limit: 10 }
3416
3430
  })
@@ -3427,7 +3441,7 @@ function ProductList() {
3427
3441
  }
3428
3442
  \`\`\`
3429
3443
 
3430
- ### React Query Hooks (client.query)
3444
+ ### React Query Hooks (@01.software/sdk/query)
3431
3445
 
3432
3446
  | Hook | Description |
3433
3447
  |------|-------------|
@@ -3462,13 +3476,17 @@ await client.collections.from('products').remove('id')
3462
3476
  // count() returns { totalDocs }
3463
3477
  const { totalDocs } = await client.collections.from('products').count()
3464
3478
 
3465
- // Metadata - generate Next.js Metadata from collection fields
3466
- // Auto-maps per-collection fields (e.g. articles: description\u2192description, thumbnail\u2192image)
3467
- const articleMeta = await client.collections.from('articles').findMetadataById(id, { siteName: 'My Blog' })
3468
- const productMeta = await client.collections.from('products').findMetadata(
3469
- { where: { slug: { equals: 'my-product' } } },
3470
- { siteName: 'My Store' }
3471
- )
3479
+ // Metadata - generate Metadata-shaped SEO objects from fetched documents
3480
+ import { extractSeo, generateMetadata } from '@01.software/sdk/metadata'
3481
+
3482
+ const productResult = await client.collections.from('products').find({
3483
+ where: { slug: { equals: 'my-product' } },
3484
+ limit: 1,
3485
+ depth: 1,
3486
+ })
3487
+ const productMeta = productResult.docs[0]
3488
+ ? generateMetadata(extractSeo(productResult.docs[0]), { siteName: 'My Store' })
3489
+ : null
3472
3490
  \`\`\`
3473
3491
 
3474
3492
  ### Filtering (Payload Query Syntax)
@@ -3491,7 +3509,7 @@ For ecommerce collections, use \`products.listing.*\` for browse/search pricing
3491
3509
  ### Server Client (Server-side)
3492
3510
 
3493
3511
  \`\`\`typescript
3494
- import { createServerClient } from '@01.software/sdk'
3512
+ import { createServerClient } from '@01.software/sdk/server'
3495
3513
 
3496
3514
  const client = createServerClient({
3497
3515
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -3511,7 +3529,7 @@ if (!product) return notFound()
3511
3529
  // product: { product, variants, options, brand, categories, tags, images, videos, listing }
3512
3530
  \`\`\`
3513
3531
 
3514
- For React: \`const { data } = client.query.useProductDetailBySlug(slug)\`.
3532
+ For React: \`const { data } = createQueryHooks(client).useProductDetailBySlug(slug)\`.
3515
3533
 
3516
3534
  ### Product listing (grouped)
3517
3535
 
@@ -3566,7 +3584,8 @@ ${filters}
3566
3584
  ### ${operation === "find" ? "Query" : operation === "create" ? "Create" : operation === "update" ? "Update" : "Delete"} Example
3567
3585
 
3568
3586
  \`\`\`typescript
3569
- import { createClient, createServerClient } from '@01.software/sdk'
3587
+ import { createClient } from '@01.software/sdk'
3588
+ import { createServerClient } from '@01.software/sdk/server'
3570
3589
 
3571
3590
  // Client (read-only public collections)
3572
3591
  const client = createClient({
@@ -3587,14 +3606,18 @@ const result = await ${readClientName}.collections.from('${collection}').find(${
3587
3606
  // result.docs - array of items
3588
3607
  // result.totalDocs, result.page, result.totalPages, result.hasNextPage, ...
3589
3608
 
3590
- ${isPublicCollection ? `// Using React Hook
3591
- const { data, isLoading, error } = client.query.useQuery({
3609
+ ${isPublicCollection ? `// Using React Query hooks
3610
+ import { createQueryHooks } from '@01.software/sdk/query'
3611
+
3612
+ const query = createQueryHooks(client)
3613
+
3614
+ const { data, isLoading, error } = query.useQuery({
3592
3615
  collection: '${collection}',
3593
3616
  options: { limit: 10 }
3594
3617
  })
3595
3618
 
3596
3619
  // With Suspense
3597
- const { data } = client.query.useSuspenseQuery({
3620
+ const { data } = query.useSuspenseQuery({
3598
3621
  collection: '${collection}',
3599
3622
  options: { limit: 10 }
3600
3623
  })` : `// React hooks are browser/public only and do not support '${collection}'.`}` : operation === "create" ? `// Create ${collection} item (ServerClient only)
@@ -3663,7 +3686,7 @@ var SCENARIOS = {
3663
3686
 
3664
3687
  ### Code Example (ServerClient)
3665
3688
  \`\`\`typescript
3666
- import { createServerClient } from '@01.software/sdk'
3689
+ import { createServerClient } from '@01.software/sdk/server'
3667
3690
 
3668
3691
  const client = createServerClient({
3669
3692
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -3888,7 +3911,6 @@ customer-addresses
3888
3911
  ### Optional Collections
3889
3912
 
3890
3913
  - customer-groups \u2014 Console/server-scoped segmentation for VIP coupons and campaigns. Use \`createServerClient().collections.from('customer-groups')\`.
3891
- - customer-profile-lists \u2014 Public profile display/ranking lists for storefronts. Browser reads may use \`client.collections.from('customer-profile-lists')\`.
3892
3914
 
3893
3915
  ### Config
3894
3916
 
@@ -4029,7 +4051,9 @@ comments, reactions, bookmarks, reports, community-bans
4029
4051
 
4030
4052
  ### Optional Collections
4031
4053
 
4032
- post-categories, customer-profile-lists`
4054
+ post-categories, customer-profile-lists
4055
+
4056
+ - \`customer-profile-lists\` is Community-owned public profile curation. It composes \`customer-profiles\`, so effective Community access also requires Customers.`
4033
4057
  };
4034
4058
  function featureSetupGuide({
4035
4059
  feature
@@ -4142,12 +4166,7 @@ var COLLECTIONS_BY_CATEGORY = {
4142
4166
  "shipping-policies",
4143
4167
  "shipping-zones"
4144
4168
  ],
4145
- Customers: [
4146
- "customers",
4147
- "customer-profiles",
4148
- "customer-profile-lists",
4149
- "customer-addresses"
4150
- ],
4169
+ Customers: ["customers", "customer-profiles", "customer-addresses"],
4151
4170
  Carts: ["carts", "cart-items"],
4152
4171
  Discounts: ["discounts"],
4153
4172
  Documents: ["documents", "document-categories", "document-types"],
@@ -4163,7 +4182,8 @@ var COLLECTIONS_BY_CATEGORY = {
4163
4182
  "reactions",
4164
4183
  "reaction-types",
4165
4184
  "bookmarks",
4166
- "post-categories"
4185
+ "post-categories",
4186
+ "customer-profile-lists"
4167
4187
  ],
4168
4188
  Playlists: [
4169
4189
  "playlists",
@@ -4273,6 +4293,22 @@ yarn add @01.software/sdk
4273
4293
  pnpm add @01.software/sdk
4274
4294
  \`\`\`
4275
4295
 
4296
+ ## Optional peers
4297
+
4298
+ You do not need extra packages for the root SDK entry. Install peer
4299
+ dependencies only when you import a feature sub-path:
4300
+
4301
+ - \`@01.software/sdk/query\` -> \`@tanstack/react-query\`, \`react\`, \`react-dom\`
4302
+ - \`@01.software/sdk/realtime\` -> \`@tanstack/react-query\`
4303
+ - \`@01.software/sdk/analytics/react\` -> \`react\`, \`react-dom\`
4304
+ - \`@01.software/sdk/ui/rich-text\` -> \`@payloadcms/richtext-lexical\`
4305
+ - \`@01.software/sdk/ui/form\` -> none
4306
+ - \`@01.software/sdk/ui/code-block\` -> \`shiki\`, \`hast-util-to-jsx-runtime\`
4307
+ - \`@01.software/sdk/ui/canvas\` -> \`@tanstack/react-query\`, \`@xyflow/react\`, \`quickjs-emscripten\`, \`postcss\`, \`sucrase\`
4308
+ - \`@01.software/sdk/ui/canvas/server\` -> none
4309
+ - \`@01.software/sdk/ui/video\` -> \`@mux/mux-player-react\`
4310
+ - \`@01.software/sdk/ui/image\` -> none
4311
+
4276
4312
  ## Basic Usage
4277
4313
 
4278
4314
  \`\`\`typescript
@@ -4310,7 +4346,7 @@ Comprehensive guides to master the 01.software SDK.
4310
4346
 
4311
4347
  ## Data Fetching
4312
4348
 
4313
- Use the Query Builder or React Query hooks to fetch data efficiently.
4349
+ Use the Query Builder or opt-in React Query hooks to fetch data efficiently.
4314
4350
 
4315
4351
  ### Query Builder
4316
4352
  \`\`\`typescript
@@ -4338,7 +4374,10 @@ const { totalDocs } = await client.collections.from('products').count()
4338
4374
 
4339
4375
  ### React Query Hook
4340
4376
  \`\`\`typescript
4341
- const { data, isLoading, error } = client.query.useQuery({
4377
+ import { createQueryHooks } from '@01.software/sdk/query'
4378
+
4379
+ const query = createQueryHooks(client)
4380
+ const { data, isLoading, error } = query.useQuery({
4342
4381
  collection: 'products',
4343
4382
  options: {
4344
4383
  where: { status: { equals: 'published' } },
@@ -4349,7 +4388,7 @@ const { data, isLoading, error } = client.query.useQuery({
4349
4388
 
4350
4389
  ### Suspense Mode
4351
4390
  \`\`\`typescript
4352
- const { data } = client.query.useSuspenseQuery({
4391
+ const { data } = query.useSuspenseQuery({
4353
4392
  collection: 'products',
4354
4393
  options: { limit: 10 }
4355
4394
  })
@@ -4358,7 +4397,7 @@ const { data } = client.query.useSuspenseQuery({
4358
4397
  ### Infinite Scroll
4359
4398
  \`\`\`typescript
4360
4399
  const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
4361
- client.query.useInfiniteQuery({
4400
+ query.useInfiniteQuery({
4362
4401
  collection: 'products',
4363
4402
  pageSize: 20
4364
4403
  })
@@ -4405,19 +4444,18 @@ const result = await serverClient.from('products').removeMany(
4405
4444
  )
4406
4445
  \`\`\`
4407
4446
 
4408
- ### Mutation Hooks (React)
4447
+ ### Server Mutations
4409
4448
  \`\`\`typescript
4410
- // Create with auto cache invalidation
4411
- const { mutate: create } = client.query.useCreate({ collection: 'products' })
4412
- create({ title: 'New', status: 'draft' })
4449
+ import { createServerClient } from '@01.software/sdk/server'
4413
4450
 
4414
- // Update with auto cache invalidation
4415
- const { mutate: update } = client.query.useUpdate({ collection: 'products' })
4416
- update({ id: 'product-id', data: { title: 'Updated' } })
4451
+ const server = createServerClient({
4452
+ publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
4453
+ secretKey: process.env.SOFTWARE_SECRET_KEY!,
4454
+ })
4417
4455
 
4418
- // Remove with auto cache invalidation
4419
- const { mutate: remove } = client.query.useRemove({ collection: 'products' })
4420
- remove('product-id')
4456
+ await server.collections.from('products').create({ title: 'New', status: 'draft' })
4457
+ await server.collections.from('products').update('product-id', { title: 'Updated' })
4458
+ await server.collections.from('products').remove('product-id')
4421
4459
  \`\`\`
4422
4460
 
4423
4461
  ## Caching Strategies
@@ -4427,17 +4465,17 @@ The SDK uses React Query for caching and background updates.
4427
4465
  ### SSR Prefetching
4428
4466
  \`\`\`typescript
4429
4467
  // Prefetch in server component for instant client hydration
4430
- await client.query.prefetchQuery({
4468
+ await query.prefetchQuery({
4431
4469
  collection: 'products',
4432
4470
  options: { limit: 10 }
4433
4471
  })
4434
4472
 
4435
- await client.query.prefetchQueryById({
4473
+ await query.prefetchQueryById({
4436
4474
  collection: 'products',
4437
4475
  id: 'product-id'
4438
4476
  })
4439
4477
 
4440
- await client.query.prefetchInfiniteQuery({
4478
+ await query.prefetchInfiniteQuery({
4441
4479
  collection: 'products',
4442
4480
  pageSize: 20
4443
4481
  })
@@ -4446,19 +4484,19 @@ await client.query.prefetchInfiniteQuery({
4446
4484
  ### Cache Invalidation
4447
4485
  \`\`\`typescript
4448
4486
  // Invalidate list cache for a collection
4449
- client.query.invalidateQueries('products', 'list')
4487
+ query.invalidateQueries('products', 'list')
4450
4488
 
4451
4489
  // Invalidate all caches for a collection
4452
- client.query.invalidateQueries('products')
4490
+ query.invalidateQueries('products')
4453
4491
  \`\`\`
4454
4492
 
4455
4493
  ### Manual Cache Management
4456
4494
  \`\`\`typescript
4457
4495
  // Read cached data
4458
- const cached = client.query.getQueryData('products', 'list')
4496
+ const cached = query.getQueryData('products', 'list')
4459
4497
 
4460
4498
  // Write to cache (optimistic updates)
4461
- client.query.setQueryData('products', 'list', newData)
4499
+ query.setQueryData('products', 'list', newData)
4462
4500
  \`\`\`
4463
4501
 
4464
4502
  ## Error Handling
@@ -4536,7 +4574,7 @@ const client = createClient({
4536
4574
  For server-side operations (full CRUD)
4537
4575
 
4538
4576
  \`\`\`typescript
4539
- import { createServerClient } from '@01.software/sdk'
4577
+ import { createServerClient } from '@01.software/sdk/server'
4540
4578
 
4541
4579
  const client = createServerClient({
4542
4580
  publishableKey: string,
@@ -4615,13 +4653,19 @@ const result = await client.collections.from('collection').removeMany(where)
4615
4653
  // Returns PayloadFindResponse with deleted docs
4616
4654
  \`\`\`
4617
4655
 
4618
- ## React Query Hooks (client.query)
4656
+ ## React Query Hooks (\`@01.software/sdk/query\`)
4657
+
4658
+ \`\`\`typescript
4659
+ import { createQueryHooks } from '@01.software/sdk/query'
4660
+
4661
+ const query = createQueryHooks(client)
4662
+ \`\`\`
4619
4663
 
4620
4664
  ### useQuery()
4621
4665
  Query a collection list.
4622
4666
 
4623
4667
  \`\`\`typescript
4624
- const { data, isLoading, error } = client.query.useQuery({
4668
+ const { data, isLoading, error } = query.useQuery({
4625
4669
  collection: 'products',
4626
4670
  options: { limit: 10, where: { status: { equals: 'published' } } }
4627
4671
  })
@@ -4631,7 +4675,7 @@ const { data, isLoading, error } = client.query.useQuery({
4631
4675
  Suspense mode list query.
4632
4676
 
4633
4677
  \`\`\`typescript
4634
- const { data } = client.query.useSuspenseQuery({
4678
+ const { data } = query.useSuspenseQuery({
4635
4679
  collection: 'products',
4636
4680
  options: { limit: 10 }
4637
4681
  })
@@ -4641,7 +4685,7 @@ const { data } = client.query.useSuspenseQuery({
4641
4685
  Get a single item by ID.
4642
4686
 
4643
4687
  \`\`\`typescript
4644
- const { data } = client.query.useQueryById({
4688
+ const { data } = query.useQueryById({
4645
4689
  collection: 'products',
4646
4690
  id: 'product-id'
4647
4691
  })
@@ -4651,7 +4695,7 @@ const { data } = client.query.useQueryById({
4651
4695
  Suspense mode single item query.
4652
4696
 
4653
4697
  \`\`\`typescript
4654
- const { data } = client.query.useSuspenseQueryById({
4698
+ const { data } = query.useSuspenseQueryById({
4655
4699
  collection: 'products',
4656
4700
  id: 'product-id'
4657
4701
  })
@@ -4661,7 +4705,7 @@ const { data } = client.query.useSuspenseQueryById({
4661
4705
  Infinite scroll.
4662
4706
 
4663
4707
  \`\`\`typescript
4664
- const { data, fetchNextPage, hasNextPage } = client.query.useInfiniteQuery({
4708
+ const { data, fetchNextPage, hasNextPage } = query.useInfiniteQuery({
4665
4709
  collection: 'products',
4666
4710
  options: { limit: 20 },
4667
4711
  pageSize: 20
@@ -4672,52 +4716,43 @@ const { data, fetchNextPage, hasNextPage } = client.query.useInfiniteQuery({
4672
4716
  Suspense mode infinite scroll.
4673
4717
 
4674
4718
  \`\`\`typescript
4675
- const { data, fetchNextPage } = client.query.useSuspenseInfiniteQuery({
4719
+ const { data, fetchNextPage } = query.useSuspenseInfiniteQuery({
4676
4720
  collection: 'products',
4677
4721
  pageSize: 20
4678
4722
  })
4679
4723
  \`\`\`
4680
4724
 
4681
- ## Mutation Hooks (client.query)
4682
-
4683
- ### useCreate()
4684
- Create a document with automatic cache invalidation.
4685
-
4686
- \`\`\`typescript
4687
- const { mutate } = client.query.useCreate({ collection: 'products' })
4688
- mutate({ title: 'New Product', status: 'draft' })
4689
- \`\`\`
4725
+ ## Server Mutations
4690
4726
 
4691
- ### useUpdate()
4692
- Update a document with automatic cache invalidation.
4727
+ Writes require trusted server code with \`createServerClient\`; do not run server credentials in React client components.
4693
4728
 
4694
4729
  \`\`\`typescript
4695
- const { mutate } = client.query.useUpdate({ collection: 'products' })
4696
- mutate({ id: 'product-id', data: { title: 'Updated' } })
4697
- \`\`\`
4730
+ import { createServerClient } from '@01.software/sdk/server'
4698
4731
 
4699
- ### useRemove()
4700
- Remove a document with automatic cache invalidation.
4732
+ const server = createServerClient({
4733
+ publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
4734
+ secretKey: process.env.SOFTWARE_SECRET_KEY!,
4735
+ })
4701
4736
 
4702
- \`\`\`typescript
4703
- const { mutate } = client.query.useRemove({ collection: 'products' })
4704
- mutate('product-id')
4737
+ await server.collections.from('products').create({ title: 'New Product', status: 'draft' })
4738
+ await server.collections.from('products').update('product-id', { title: 'Updated' })
4739
+ await server.collections.from('products').remove('product-id')
4705
4740
  \`\`\`
4706
4741
 
4707
4742
  ## Cache Utilities
4708
4743
 
4709
4744
  \`\`\`typescript
4710
4745
  // Invalidate cache
4711
- client.query.invalidateQueries('products', 'list')
4746
+ query.invalidateQueries('products', 'list')
4712
4747
 
4713
4748
  // SSR prefetch
4714
- await client.query.prefetchQuery({ collection: 'products', options: { limit: 10 } })
4715
- await client.query.prefetchQueryById({ collection: 'products', id: 'id' })
4716
- await client.query.prefetchInfiniteQuery({ collection: 'products', pageSize: 20 })
4749
+ await query.prefetchQuery({ collection: 'products', options: { limit: 10 } })
4750
+ await query.prefetchQueryById({ collection: 'products', id: 'id' })
4751
+ await query.prefetchInfiniteQuery({ collection: 'products', pageSize: 20 })
4717
4752
 
4718
4753
  // Get/set cached data
4719
- const cached = client.query.getQueryData('products', 'list')
4720
- client.query.setQueryData('products', 'list', newData)
4754
+ const cached = query.getQueryData('products', 'list')
4755
+ query.setQueryData('products', 'list', newData)
4721
4756
  \`\`\`
4722
4757
 
4723
4758
  For ecommerce reads, prefer \`products.listing.*\` for card/search pricing and keep authoritative sellable pricing on \`product-variants.price\`.
@@ -4964,7 +4999,7 @@ const result3 = await client.collections.from('products').find({ sort: '-isFeatu
4964
4999
  ## Full Example
4965
5000
 
4966
5001
  \`\`\`typescript
4967
- import { createServerClient } from '@01.software/sdk'
5002
+ import { createServerClient } from '@01.software/sdk/server'
4968
5003
 
4969
5004
  const serverClient = createServerClient({
4970
5005
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -4992,14 +5027,22 @@ console.log(result.hasNextPage) // true
4992
5027
  var metadata43 = {
4993
5028
  name: "docs-react-query",
4994
5029
  title: "React Query Hooks",
4995
- description: "01.software SDK React Query hooks reference (client.query)"
5030
+ description: "01.software SDK React Query hooks reference (@01.software/sdk/query)"
4996
5031
  };
4997
5032
  function handler13() {
4998
5033
  return `# React Query Hooks
4999
5034
 
5000
- React Query hooks are available on the browser-side \`Client\` via \`client.query\`. They provide automatic caching, background refetching, and cache invalidation.
5035
+ 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.
5001
5036
 
5002
- > React Query hooks are available on \`Client\` (createClient) only. \`ServerClient\` does not include them.
5037
+ Install \`@tanstack/react-query\` and React peers only when importing this sub-path.
5038
+
5039
+ \`\`\`typescript
5040
+ import { createClient } from '@01.software/sdk'
5041
+ import { createQueryHooks } from '@01.software/sdk/query'
5042
+
5043
+ const client = createClient({ publishableKey })
5044
+ const query = createQueryHooks(client)
5045
+ \`\`\`
5003
5046
 
5004
5047
  ## Query Hooks
5005
5048
 
@@ -5007,7 +5050,7 @@ React Query hooks are available on the browser-side \`Client\` via \`client.quer
5007
5050
  Query a collection list with automatic caching.
5008
5051
 
5009
5052
  \`\`\`typescript
5010
- const { data, isLoading, error } = client.query.useQuery({
5053
+ const { data, isLoading, error } = query.useQuery({
5011
5054
  collection: 'products',
5012
5055
  options: {
5013
5056
  where: { status: { equals: 'published' } },
@@ -5025,7 +5068,7 @@ Suspense-mode list query. Throws a promise while loading (use with React Suspens
5025
5068
 
5026
5069
  \`\`\`typescript
5027
5070
  // Inside a Suspense boundary
5028
- const { data } = client.query.useSuspenseQuery({
5071
+ const { data } = query.useSuspenseQuery({
5029
5072
  collection: 'products',
5030
5073
  options: { limit: 10 },
5031
5074
  })
@@ -5036,7 +5079,7 @@ const { data } = client.query.useSuspenseQuery({
5036
5079
  Get a single document by ID.
5037
5080
 
5038
5081
  \`\`\`typescript
5039
- const { data, isLoading } = client.query.useQueryById({
5082
+ const { data, isLoading } = query.useQueryById({
5040
5083
  collection: 'products',
5041
5084
  id: 'product-id',
5042
5085
  })
@@ -5047,7 +5090,7 @@ const { data, isLoading } = client.query.useQueryById({
5047
5090
  Suspense-mode single document query.
5048
5091
 
5049
5092
  \`\`\`typescript
5050
- const { data } = client.query.useSuspenseQueryById({
5093
+ const { data } = query.useSuspenseQueryById({
5051
5094
  collection: 'products',
5052
5095
  id: 'product-id',
5053
5096
  })
@@ -5063,7 +5106,7 @@ const {
5063
5106
  fetchNextPage,
5064
5107
  hasNextPage,
5065
5108
  isFetchingNextPage,
5066
- } = client.query.useInfiniteQuery({
5109
+ } = query.useInfiniteQuery({
5067
5110
  collection: 'products',
5068
5111
  options: {
5069
5112
  where: { status: { equals: 'published' } },
@@ -5079,55 +5122,33 @@ const {
5079
5122
  Suspense-mode infinite scroll.
5080
5123
 
5081
5124
  \`\`\`typescript
5082
- const { data, fetchNextPage, hasNextPage } = client.query.useSuspenseInfiniteQuery({
5125
+ const { data, fetchNextPage, hasNextPage } = query.useSuspenseInfiniteQuery({
5083
5126
  collection: 'products',
5084
5127
  pageSize: 20,
5085
5128
  })
5086
5129
  \`\`\`
5087
5130
 
5088
- ## Mutation Hooks
5089
-
5090
- Mutation hooks automatically invalidate relevant cache keys after success.
5131
+ ## Writes
5091
5132
 
5092
- ### useCreate()
5093
- Create a document and invalidate list cache.
5133
+ 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.
5094
5134
 
5095
5135
  \`\`\`typescript
5096
- const { mutate, mutateAsync, isPending } = client.query.useCreate({
5097
- collection: 'products',
5098
- })
5099
-
5100
- // Fire and forget
5101
- mutate({ title: 'New Product', status: 'draft' })
5102
-
5103
- // Await result
5104
- const result = await mutateAsync({ title: 'New Product', status: 'draft' })
5105
- // result.doc - created document
5106
- \`\`\`
5107
-
5108
- For ecommerce reads, price-oriented product cards should consume \`products.listing.minPrice/maxPrice\`. Authoritative sellable pricing still lives on \`product-variants.price\`.
5136
+ // app/products/actions.ts
5137
+ 'use server'
5109
5138
 
5110
- ### useUpdate()
5111
- Update a document and invalidate list + detail cache.
5139
+ import { createServerClient } from '@01.software/sdk/server'
5112
5140
 
5113
- \`\`\`typescript
5114
- const { mutate, mutateAsync } = client.query.useUpdate({
5115
- collection: 'products',
5141
+ const server = createServerClient({
5142
+ publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
5143
+ secretKey: process.env.SOFTWARE_SECRET_KEY!,
5116
5144
  })
5117
5145
 
5118
- mutate({ id: 'product-id', data: { title: 'Updated Title' } })
5146
+ export async function createProduct(data: { title: string; status: 'draft' | 'published' }) {
5147
+ return server.collections.from('products').create(data)
5148
+ }
5119
5149
  \`\`\`
5120
5150
 
5121
- ### useRemove()
5122
- Remove a document and invalidate list cache.
5123
-
5124
- \`\`\`typescript
5125
- const { mutate, mutateAsync } = client.query.useRemove({
5126
- collection: 'products',
5127
- })
5128
-
5129
- mutate('product-id')
5130
- \`\`\`
5151
+ For ecommerce reads, price-oriented product cards should consume \`products.listing.minPrice/maxPrice\`. Authoritative sellable pricing still lives on \`product-variants.price\`.
5131
5152
 
5132
5153
  ## Cache Utilities
5133
5154
 
@@ -5136,10 +5157,10 @@ Manually invalidate cached queries for a collection.
5136
5157
 
5137
5158
  \`\`\`typescript
5138
5159
  // Invalidate all list queries for a collection
5139
- client.query.invalidateQueries('products', 'list')
5160
+ query.invalidateQueries('products', 'list')
5140
5161
 
5141
5162
  // Invalidate all queries for a collection (list + detail)
5142
- client.query.invalidateQueries('products')
5163
+ query.invalidateQueries('products')
5143
5164
  \`\`\`
5144
5165
 
5145
5166
  ### getQueryData() / setQueryData()
@@ -5147,10 +5168,10 @@ Read and write the React Query cache directly.
5147
5168
 
5148
5169
  \`\`\`typescript
5149
5170
  // Read cached data
5150
- const cached = client.query.getQueryData('products', 'list')
5171
+ const cached = query.getQueryData('products', 'list')
5151
5172
 
5152
5173
  // Write to cache (useful for optimistic updates)
5153
- client.query.setQueryData('products', 'list', newData)
5174
+ query.setQueryData('products', 'list', newData)
5154
5175
  \`\`\`
5155
5176
 
5156
5177
  ## SSR Prefetching
@@ -5160,34 +5181,37 @@ Prefetch data in server components for instant hydration on the client.
5160
5181
  \`\`\`typescript
5161
5182
  // app/products/page.tsx (Next.js App Router Server Component)
5162
5183
  import { HydrationBoundary, dehydrate } from '@tanstack/react-query'
5163
- import { createServerClient } from '@01.software/sdk'
5184
+ import { createServerClient } from '@01.software/sdk/server'
5185
+ import { createServerQueryHooks, getQueryClient } from '@01.software/sdk/query'
5164
5186
 
5165
5187
  export default async function ProductsPage() {
5166
5188
  const serverClient = createServerClient({
5167
5189
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
5168
5190
  secretKey: process.env.SOFTWARE_SECRET_KEY!,
5169
5191
  })
5192
+ const queryClient = getQueryClient()
5193
+ const serverQuery = createServerQueryHooks(serverClient, queryClient)
5170
5194
 
5171
5195
  // Prefetch list
5172
- await serverClient.query.prefetchQuery({
5196
+ await serverQuery.prefetchQuery({
5173
5197
  collection: 'products',
5174
5198
  options: { limit: 20, where: { status: { equals: 'published' } } },
5175
5199
  })
5176
5200
 
5177
5201
  // Prefetch single item
5178
- await serverClient.query.prefetchQueryById({
5202
+ await serverQuery.prefetchQueryById({
5179
5203
  collection: 'products',
5180
5204
  id: 'product-id',
5181
5205
  })
5182
5206
 
5183
5207
  // Prefetch infinite query
5184
- await serverClient.query.prefetchInfiniteQuery({
5208
+ await serverQuery.prefetchInfiniteQuery({
5185
5209
  collection: 'products',
5186
5210
  pageSize: 20,
5187
5211
  })
5188
5212
 
5189
5213
  return (
5190
- <HydrationBoundary state={dehydrate(serverClient.query.queryClient)}>
5214
+ <HydrationBoundary state={dehydrate(queryClient)}>
5191
5215
  <ProductList />
5192
5216
  </HydrationBoundary>
5193
5217
  )
@@ -5200,13 +5224,15 @@ export default async function ProductsPage() {
5200
5224
  'use client'
5201
5225
 
5202
5226
  import { createClient } from '@01.software/sdk'
5227
+ import { createQueryHooks } from '@01.software/sdk/query'
5203
5228
 
5204
5229
  const client = createClient({
5205
5230
  publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!,
5206
5231
  })
5232
+ const query = createQueryHooks(client)
5207
5233
 
5208
5234
  export function ProductList() {
5209
- const { data, isLoading, error } = client.query.useQuery({
5235
+ const { data, isLoading, error } = query.useQuery({
5210
5236
  collection: 'products',
5211
5237
  options: {
5212
5238
  where: { status: { equals: 'published' } },
@@ -5215,20 +5241,13 @@ export function ProductList() {
5215
5241
  },
5216
5242
  })
5217
5243
 
5218
- const { mutate: removeProduct } = client.query.useRemove({
5219
- collection: 'products',
5220
- })
5221
-
5222
5244
  if (isLoading) return <div>Loading...</div>
5223
5245
  if (error) return <div>Error: {error.message}</div>
5224
5246
 
5225
5247
  return (
5226
5248
  <ul>
5227
5249
  {data?.docs.map((product) => (
5228
- <li key={product.id}>
5229
- {product.title}
5230
- <button onClick={() => removeProduct(product.id)}>Delete</button>
5231
- </li>
5250
+ <li key={product.id}>{product.title}</li>
5232
5251
  ))}
5233
5252
  </ul>
5234
5253
  )
@@ -5248,7 +5267,7 @@ function handler14() {
5248
5267
  Server-side operations are available via \`client.commerce\` on \`ServerClient\`. Use \`createServerClient\` with both \`publishableKey\` and \`secretKey\`.
5249
5268
 
5250
5269
  \`\`\`typescript
5251
- import { createServerClient } from '@01.software/sdk'
5270
+ import { createServerClient } from '@01.software/sdk/server'
5252
5271
 
5253
5272
  const client = createServerClient({
5254
5273
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -5697,9 +5716,9 @@ The SDK provides two client types for different execution environments.
5697
5716
  | Read (\`find\`, \`findById\`, \`count\`) | Yes | Yes |
5698
5717
  | Write (\`create\`, \`update\`, \`remove\`) | No | Yes |
5699
5718
  | Bulk (\`updateMany\`, \`removeMany\`) | No | Yes |
5700
- | React Query hooks (\`client.query\`) | Yes | Yes (SSR prefetch) |
5719
+ | React Query hooks (\`@01.software/sdk/query\`) | Yes | Yes (SSR prefetch) |
5701
5720
  | Customer auth (\`client.customer\`) | Yes | No |
5702
- | Server API (\`client.api\`) | No | Yes |
5721
+ | Commerce/server APIs (\`server.commerce\`, server CRUD) | No | Yes |
5703
5722
 
5704
5723
  ## Client (createClient)
5705
5724
 
@@ -5707,16 +5726,18 @@ Use in browser code, React client components, and anywhere the secret key must n
5707
5726
 
5708
5727
  \`\`\`typescript
5709
5728
  import { createClient } from '@01.software/sdk'
5729
+ import { createQueryHooks } from '@01.software/sdk/query'
5710
5730
 
5711
5731
  const client = createClient({
5712
5732
  publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!,
5713
5733
  })
5734
+ const query = createQueryHooks(client)
5714
5735
 
5715
5736
  // Read data
5716
5737
  const products = await client.collections.from('products').find({ limit: 10 })
5717
5738
 
5718
5739
  // React Query hooks
5719
- const { data } = client.query.useQuery({ collection: 'products' })
5740
+ const { data } = query.useQuery({ collection: 'products' })
5720
5741
 
5721
5742
  // Customer auth
5722
5743
  await client.customer.login({ email, password })
@@ -5729,7 +5750,7 @@ await client.customer.login({ email, password })
5729
5750
  Use in server components, API routes, server actions, and background jobs. Has full CRUD access.
5730
5751
 
5731
5752
  \`\`\`typescript
5732
- import { createServerClient } from '@01.software/sdk'
5753
+ import { createServerClient } from '@01.software/sdk/server'
5733
5754
 
5734
5755
  const client = createServerClient({
5735
5756
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -5750,11 +5771,14 @@ await client.collections.from('product-variants').create({
5750
5771
  })
5751
5772
  await client.collections.from('products').remove('product-id')
5752
5773
 
5753
- // Server API (orders, carts, etc.)
5774
+ // Commerce API (orders, carts, etc.)
5754
5775
  await client.commerce.orders.create({ ... })
5755
5776
  await client.commerce.orders.checkout({ ... })
5756
5777
  \`\`\`
5757
5778
 
5779
+ Server-only code must import \`createServerClient\` from the \`/server\`
5780
+ sub-path.
5781
+
5758
5782
  **Environment variables**:
5759
5783
  - \`SOFTWARE_PUBLISHABLE_KEY\` \u2014 publishable key (no NEXT_PUBLIC prefix, server-only)
5760
5784
  - \`SOFTWARE_SECRET_KEY\` \u2014 server credential
@@ -5784,7 +5808,7 @@ deploying again.
5784
5808
 
5785
5809
  \`\`\`typescript
5786
5810
  // lib/sdk.ts \u2014 server-only module
5787
- import { createServerClient } from '@01.software/sdk'
5811
+ import { createServerClient } from '@01.software/sdk/server'
5788
5812
 
5789
5813
  export function getServerClient() {
5790
5814
  return createServerClient({
@@ -5797,10 +5821,12 @@ export function getServerClient() {
5797
5821
  \`\`\`typescript
5798
5822
  // lib/sdk-client.ts \u2014 browser-safe module
5799
5823
  import { createClient } from '@01.software/sdk'
5824
+ import { createQueryHooks } from '@01.software/sdk/query'
5800
5825
 
5801
5826
  export const browserClient = createClient({
5802
5827
  publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!,
5803
5828
  })
5829
+ export const browserQuery = createQueryHooks(browserClient)
5804
5830
  \`\`\`
5805
5831
 
5806
5832
  \`\`\`typescript
@@ -5817,10 +5843,10 @@ export default async function ProductsPage() {
5817
5843
  \`\`\`typescript
5818
5844
  // components/product-list.tsx \u2014 Client Component
5819
5845
  'use client'
5820
- import { browserClient } from '@/lib/sdk-client'
5846
+ import { browserQuery } from '@/lib/sdk-client'
5821
5847
 
5822
5848
  export function ProductList() {
5823
- const { data } = browserClient.query.useQuery({ collection: 'products' })
5849
+ const { data } = browserQuery.useQuery({ collection: 'products' })
5824
5850
  return <ul>{data?.docs.map(p => <li key={p.id}>{p.title}</li>)}</ul>
5825
5851
  }
5826
5852
  \`\`\`
@@ -5851,7 +5877,7 @@ Upload files using the \`images\` collection (tenant-scoped, unified image store
5851
5877
  Use \`ServerClient\` with \`FormData\` to upload images server-side.
5852
5878
 
5853
5879
  \`\`\`typescript
5854
- import { createServerClient } from '@01.software/sdk'
5880
+ import { createServerClient } from '@01.software/sdk/server'
5855
5881
 
5856
5882
  const client = createServerClient({
5857
5883
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -5874,7 +5900,7 @@ async function uploadImage(file: File) {
5874
5900
  \`\`\`typescript
5875
5901
  // app/api/upload/route.ts
5876
5902
  import { NextRequest, NextResponse } from 'next/server'
5877
- import { createServerClient } from '@01.software/sdk'
5903
+ import { createServerClient } from '@01.software/sdk/server'
5878
5904
 
5879
5905
  const client = createServerClient({
5880
5906
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -5904,7 +5930,7 @@ export async function POST(req: NextRequest) {
5904
5930
  // actions/upload.ts
5905
5931
  'use server'
5906
5932
 
5907
- import { createServerClient } from '@01.software/sdk'
5933
+ import { createServerClient } from '@01.software/sdk/server'
5908
5934
 
5909
5935
  const client = createServerClient({
5910
5936
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -6127,29 +6153,38 @@ const client = createClient({
6127
6153
  const detail = await client.commerce.product.detail({ slug: 'my-product' })
6128
6154
  if (!detail) return notFound()
6129
6155
  const selection = resolveProductSelection(detail, {
6130
- search: '?opt.color=black&opt.size=large',
6156
+ search: '?opt.option-color=color-black',
6131
6157
  })
6132
6158
  // selection.selectedVariant, selection.price, selection.stock, selection.media
6133
6159
  \`\`\`
6134
6160
 
6135
6161
  ## URL round-trip
6136
6162
 
6137
- Use the SDK codec for one canonical selection URL format: \`opt.<optionSlug>=<valueSlug>\`. Option and value slugs are the stable public tokens exposed by product detail.
6163
+ Use the SDK codec for canonical selection URLs. Complete selections use
6164
+ \`variant=<variantId>\`; partial selections use
6165
+ \`opt.<optionId>=<valueId>\`. Older
6166
+ \`opt.<optionSlug>=<valueSlug>\` URLs still decode during Stage 1, but slugs are
6167
+ compatibility metadata rather than canonical identity.
6138
6168
 
6139
6169
  \`\`\`typescript
6140
6170
  import { createProductSelectionCodec } from '@01.software/sdk'
6141
6171
 
6142
6172
  const codec = createProductSelectionCodec(detail)
6143
- const selection = codec.parse('?opt.color=black')
6144
- const href = codec.stringify(selection)
6173
+ const selection = codec.parse('?opt.option-color=color-black')
6174
+ const selectionQuery = codec.stringify(selection)
6145
6175
  \`\`\`
6146
6176
 
6147
- Value-slug-only URLs such as \`?black\` or \`?color=black\` are rejected because option titles are not stable identifiers.
6177
+ 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.
6178
+
6179
+ Value-slug-only URLs such as \`?black\` or \`?color=black\` are rejected because option titles and values are not stable identifiers.
6148
6180
 
6149
6181
  ## React Query hook variant
6150
6182
 
6151
6183
  \`\`\`typescript
6152
- const { data: detail, isLoading } = client.query.useProductDetailBySlug(slug)
6184
+ import { createQueryHooks } from '@01.software/sdk/query'
6185
+
6186
+ const query = createQueryHooks(client)
6187
+ const { data: detail, isLoading } = query.useProductDetailBySlug(slug)
6153
6188
  \`\`\`
6154
6189
 
6155
6190
  Cache invalidates automatically when any of 10 detail-relevant collections is mutated through SDK mutation hooks.