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