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