@01.software/cli 0.10.3 → 0.10.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1103,7 +1103,7 @@ async function swallow(promise) {
1103
1103
  import { z as z3 } from "zod";
1104
1104
 
1105
1105
  // src/lib/client.ts
1106
- import { createServerClient } from "@01.software/sdk";
1106
+ import { createServerClient } from "@01.software/sdk/server";
1107
1107
  var MISSING_HTTP_AUTH_CONTEXT_ERROR2 = "MCP HTTP requests require a validated OAuth tenant context before tool execution.";
1108
1108
  var HTTP_OAUTH_SDK_CLIENT_ERROR = "MCP HTTP OAuth requests cannot use SDK-backed tools. Use reviewed Console service endpoints for OAuth transport.";
1109
1109
  function getClient() {
@@ -1936,18 +1936,22 @@ async function productDetail({
1936
1936
  // src/tools/product-upsert.ts
1937
1937
  import { z as z23 } from "zod";
1938
1938
  var optionValueSchema = z23.object({
1939
- id: z23.string().optional().describe("Existing option-value ID for updates"),
1939
+ id: z23.string().optional().describe("Stable existing option-value ID for rename-safe updates"),
1940
1940
  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"),
1941
+ slug: z23.string().optional().describe(
1942
+ "Optional compatibility value token. The server generates one from value on create when omitted; not canonical identity."
1943
+ ),
1942
1944
  swatchColor: z23.string().nullable().optional(),
1943
1945
  thumbnail: z23.string().nullable().optional(),
1944
1946
  images: z23.array(z23.string()).optional(),
1945
1947
  metadata: z23.unknown().optional()
1946
1948
  });
1947
1949
  var optionSchema = z23.object({
1948
- id: z23.string().optional().describe("Existing option ID for updates"),
1950
+ id: z23.string().optional().describe("Stable existing option ID for rename-safe updates"),
1949
1951
  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"),
1952
+ slug: z23.string().optional().describe(
1953
+ "Optional compatibility option token. The server generates one from title on create when omitted; not canonical identity."
1954
+ ),
1951
1955
  values: z23.array(optionValueSchema).describe("Allowed option values")
1952
1956
  });
1953
1957
  var variantOptionValueSchema = z23.object({
@@ -1961,7 +1965,7 @@ var variantSchema = z23.object({
1961
1965
  z23.record(z23.string(), z23.union([z23.string(), variantOptionValueSchema])),
1962
1966
  z23.array(z23.string())
1963
1967
  ]).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."
1968
+ "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
1969
  ),
1966
1970
  sku: z23.string().nullable().optional(),
1967
1971
  title: z23.string().nullable().optional(),
@@ -1983,10 +1987,10 @@ var schema23 = {
1983
1987
  "Product fields. Include `id` to update an existing product; omit for create (then `title` is required)."
1984
1988
  ),
1985
1989
  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)."
1990
+ "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
1991
  ),
1988
1992
  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)."
1993
+ "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
1994
  )
1991
1995
  };
1992
1996
  var metadata23 = {
@@ -2287,14 +2291,16 @@ var recipes = {
2287
2291
  recommendedSurface: "react-query",
2288
2292
  runtime: "browser",
2289
2293
  code: `import { createClient } from '@01.software/sdk'
2294
+ import { createQueryHooks } from '@01.software/sdk/query'
2290
2295
 
2291
2296
  const client = createClient({
2292
2297
  publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!,
2293
2298
  })
2299
+ const query = createQueryHooks(client)
2294
2300
 
2295
2301
  // React Query hook \u2014 handles caching and background updates
2296
2302
  function ProductList() {
2297
- const { data, isLoading, error } = client.query.useQuery({
2303
+ const { data, isLoading, error } = query.useQuery({
2298
2304
  collection: 'products',
2299
2305
  options: {
2300
2306
  where: { status: { equals: 'published' } },
@@ -2326,7 +2332,7 @@ function ProductList() {
2326
2332
  title: "Fetch collection list (server)",
2327
2333
  recommendedSurface: "query-builder",
2328
2334
  runtime: "server",
2329
- code: `import { createServerClient } from '@01.software/sdk'
2335
+ code: `import { createServerClient } from '@01.software/sdk/server'
2330
2336
 
2331
2337
  const client = createServerClient({
2332
2338
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -2359,13 +2365,15 @@ const result = await client.collections.from('products').find({
2359
2365
  recommendedSurface: "react-query",
2360
2366
  runtime: "browser",
2361
2367
  code: `import { createClient } from '@01.software/sdk'
2368
+ import { createQueryHooks } from '@01.software/sdk/query'
2362
2369
 
2363
2370
  const client = createClient({
2364
2371
  publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!,
2365
2372
  })
2373
+ const query = createQueryHooks(client)
2366
2374
 
2367
2375
  function ProductDetail({ id }: { id: string }) {
2368
- const { data, isLoading } = client.query.useQueryById({
2376
+ const { data, isLoading } = query.useQueryById({
2369
2377
  collection: 'products',
2370
2378
  id,
2371
2379
  })
@@ -2385,7 +2393,7 @@ function ProductDetail({ id }: { id: string }) {
2385
2393
  title: "Fetch single item by ID (server)",
2386
2394
  recommendedSurface: "query-builder",
2387
2395
  runtime: "server",
2388
- code: `import { createServerClient } from '@01.software/sdk'
2396
+ code: `import { createServerClient } from '@01.software/sdk/server'
2389
2397
 
2390
2398
  const client = createServerClient({
2391
2399
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -2408,7 +2416,7 @@ console.log(product.title)`,
2408
2416
  title: "Create a new item (server only)",
2409
2417
  recommendedSurface: "server-api",
2410
2418
  runtime: "server",
2411
- code: `import { createServerClient } from '@01.software/sdk'
2419
+ code: `import { createServerClient } from '@01.software/sdk/server'
2412
2420
 
2413
2421
  const client = createServerClient({
2414
2422
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -2437,7 +2445,7 @@ const result = await client.collections.from('products').create({
2437
2445
  title: "Update an existing item (server only)",
2438
2446
  recommendedSurface: "server-api",
2439
2447
  runtime: "server",
2440
- code: `import { createServerClient } from '@01.software/sdk'
2448
+ code: `import { createServerClient } from '@01.software/sdk/server'
2441
2449
 
2442
2450
  const client = createServerClient({
2443
2451
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -2466,7 +2474,7 @@ const result = await client.collections.from('products').update('product-id', {
2466
2474
  title: "Delete an item (server only)",
2467
2475
  recommendedSurface: "server-api",
2468
2476
  runtime: "server",
2469
- code: `import { createServerClient } from '@01.software/sdk'
2477
+ code: `import { createServerClient } from '@01.software/sdk/server'
2470
2478
 
2471
2479
  const client = createServerClient({
2472
2480
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -2491,10 +2499,12 @@ console.log('Deleted:', deleted.title)`,
2491
2499
  recommendedSurface: "react-query",
2492
2500
  runtime: "browser",
2493
2501
  code: `import { createClient } from '@01.software/sdk'
2502
+ import { createQueryHooks } from '@01.software/sdk/query'
2494
2503
 
2495
2504
  const client = createClient({
2496
2505
  publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!,
2497
2506
  })
2507
+ const query = createQueryHooks(client)
2498
2508
 
2499
2509
  function InfiniteProductList() {
2500
2510
  const {
@@ -2503,7 +2513,7 @@ function InfiniteProductList() {
2503
2513
  hasNextPage,
2504
2514
  isFetchingNextPage,
2505
2515
  isLoading,
2506
- } = client.query.useInfiniteQuery({
2516
+ } = query.useInfiniteQuery({
2507
2517
  collection: 'products',
2508
2518
  options: { where: { status: { equals: 'published' } } },
2509
2519
  pageSize: 20,
@@ -2538,7 +2548,8 @@ function InfiniteProductList() {
2538
2548
  title: "SSR data prefetching (server)",
2539
2549
  recommendedSurface: "react-query",
2540
2550
  runtime: "server",
2541
- code: `import { createServerClient } from '@01.software/sdk'
2551
+ code: `import { createServerClient } from '@01.software/sdk/server'
2552
+ import { createServerQueryHooks, getQueryClient } from '@01.software/sdk/query'
2542
2553
  import { dehydrate, HydrationBoundary } from '@tanstack/react-query'
2543
2554
 
2544
2555
  // In a Next.js Server Component or getServerSideProps:
@@ -2546,27 +2557,29 @@ const client = createServerClient({
2546
2557
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
2547
2558
  secretKey: process.env.SOFTWARE_SECRET_KEY!,
2548
2559
  })
2560
+ const queryClient = getQueryClient()
2561
+ const serverQuery = createServerQueryHooks(client, queryClient)
2549
2562
 
2550
2563
  // Prefetch list \u2014 client hydrates instantly without a loading state
2551
- await client.query.prefetchQuery({
2564
+ await serverQuery.prefetchQuery({
2552
2565
  collection: 'products',
2553
2566
  options: { limit: 20 },
2554
2567
  })
2555
2568
 
2556
2569
  // Prefetch single item
2557
- await client.query.prefetchQueryById({
2570
+ await serverQuery.prefetchQueryById({
2558
2571
  collection: 'products',
2559
2572
  id: 'product-id',
2560
2573
  })
2561
2574
 
2562
2575
  // Prefetch infinite list
2563
- await client.query.prefetchInfiniteQuery({
2576
+ await serverQuery.prefetchInfiniteQuery({
2564
2577
  collection: 'products',
2565
2578
  pageSize: 20,
2566
2579
  })
2567
2580
 
2568
2581
  // Dehydrate and pass to client
2569
- const state = dehydrate(client.query.queryClient)
2582
+ const state = dehydrate(queryClient)
2570
2583
 
2571
2584
  export default function Page() {
2572
2585
  return (
@@ -2636,7 +2649,7 @@ await client.customer.resetPassword(token, 'newPassword123')`,
2636
2649
  title: "File upload pattern (server)",
2637
2650
  recommendedSurface: "server-api",
2638
2651
  runtime: "server",
2639
- code: `import { createServerClient } from '@01.software/sdk'
2652
+ code: `import { createServerClient } from '@01.software/sdk/server'
2640
2653
 
2641
2654
  const client = createServerClient({
2642
2655
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -2667,7 +2680,7 @@ const result = await client.collections.from('images').create(formData as unknow
2667
2680
  title: "Bulk update/delete (server only)",
2668
2681
  recommendedSurface: "server-api",
2669
2682
  runtime: "server",
2670
- code: `import { createServerClient } from '@01.software/sdk'
2683
+ code: `import { createServerClient } from '@01.software/sdk/server'
2671
2684
 
2672
2685
  const client = createServerClient({
2673
2686
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -2819,15 +2832,15 @@ var docIndex = [
2819
2832
  },
2820
2833
  {
2821
2834
  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.",
2835
+ keywords: ["metadata", "generateMetadata", "extractSeo", "next.js metadata", "seo", "open graph", "query builder"],
2836
+ 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
2837
  resourceUri: "docs://sdk/query-builder"
2825
2838
  },
2826
2839
  // React Query Hooks
2827
2840
  {
2828
2841
  title: "React Query \u2014 useQuery()",
2829
2842
  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.",
2843
+ summary: "createQueryHooks(client).useQuery({ collection, options }) \u2014 fetches a list with caching and background updates. Use inside a React component.",
2831
2844
  resourceUri: "docs://sdk/react-query"
2832
2845
  },
2833
2846
  {
@@ -2839,7 +2852,7 @@ var docIndex = [
2839
2852
  {
2840
2853
  title: "React Query \u2014 useInfiniteQuery()",
2841
2854
  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.",
2855
+ summary: "createQueryHooks(client).useInfiniteQuery({ collection, options, pageSize }) \u2014 infinite scrolling. data.pages is an array of pages; flatten with flatMap for all docs.",
2843
2856
  resourceUri: "docs://sdk/react-query"
2844
2857
  },
2845
2858
  {
@@ -2858,7 +2871,7 @@ var docIndex = [
2858
2871
  {
2859
2872
  title: "Cache Invalidation and Manual Cache Management",
2860
2873
  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.",
2874
+ summary: "query.invalidateQueries(collection, type) triggers refetch. getQueryData/setQueryData allow manual optimistic updates.",
2862
2875
  resourceUri: "docs://sdk/react-query"
2863
2876
  },
2864
2877
  // Filtering
@@ -3009,23 +3022,25 @@ var AUTH_GUIDES = {
3009
3022
  title: "Browser Client Setup",
3010
3023
  envVars: ["NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY"],
3011
3024
  code: `import { createClient } from '@01.software/sdk'
3025
+ import { createQueryHooks } from '@01.software/sdk/query'
3012
3026
 
3013
3027
  const client = createClient({
3014
3028
  publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!
3015
3029
  })
3030
+ const query = createQueryHooks(client)
3016
3031
 
3017
3032
  // Read-only operations + React Query hooks + Customer Auth
3018
- const { data } = client.query.useQuery({ collection: 'products' })`,
3033
+ const { data } = query.useQuery({ collection: 'products' })`,
3019
3034
  notes: [
3020
3035
  "Client is read-only \u2014 no create/update/delete operations",
3021
3036
  "publishableKey is safe to expose in browser (prefixed with NEXT_PUBLIC_)",
3022
- "Includes React Query hooks (client.query) and Customer Auth (client.customer)"
3037
+ "Use createQueryHooks(client) for React Query hooks and client.customer for Customer Auth"
3023
3038
  ]
3024
3039
  },
3025
3040
  "server-client": {
3026
3041
  title: "Server Client Setup",
3027
3042
  envVars: ["SOFTWARE_PUBLISHABLE_KEY", "SOFTWARE_SECRET_KEY"],
3028
- code: `import { createServerClient } from '@01.software/sdk'
3043
+ code: `import { createServerClient } from '@01.software/sdk/server'
3029
3044
 
3030
3045
  const client = createServerClient({
3031
3046
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -3038,7 +3053,7 @@ const result = await client.collections.from('products').create({ title: 'New Pr
3038
3053
  "ServerClient has full CRUD access and must run only in trusted server code",
3039
3054
  "Store server credentials in environment variables and rotate them from the Console",
3040
3055
  "Use in API routes, server actions, or backend services only",
3041
- "React Query hooks available for reads (useQuery, prefetchQuery, etc.) + mutations (useCreate, useUpdate, useRemove)"
3056
+ "Use createServerClient in API routes, server actions, or backend services; keep React Query hooks for browser-safe reads and server prefetching"
3042
3057
  ]
3043
3058
  },
3044
3059
  "customer-auth": {
@@ -3176,37 +3191,35 @@ function generatePattern(collection, operation, surface) {
3176
3191
  );
3177
3192
  }
3178
3193
  const parts2 = [];
3179
- if (operation === "read") {
3194
+ if (operation === "read" || operation === "full-crud") {
3180
3195
  parts2.push(
3181
3196
  `import { createClient } from '@01.software/sdk'`,
3182
- ``,
3183
- `const client = createClient({`,
3184
- ` publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!`,
3185
- `})`,
3186
- ``
3197
+ `import { createQueryHooks } from '@01.software/sdk/query'`
3187
3198
  );
3188
- } else {
3199
+ }
3200
+ if (operation === "write" || operation === "full-crud") {
3201
+ parts2.push(`import { createServerClient } from '@01.software/sdk/server'`);
3202
+ }
3203
+ parts2.push(``);
3204
+ if (operation === "read" || operation === "full-crud") {
3189
3205
  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!`,
3206
+ `const client = createClient({`,
3207
+ ` publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!`,
3196
3208
  `})`,
3209
+ `const query = createQueryHooks(client)`,
3197
3210
  ``
3198
3211
  );
3199
3212
  }
3200
3213
  if (operation === "read" || operation === "full-crud") {
3201
3214
  parts2.push(
3202
3215
  `// List query`,
3203
- `const { data, isLoading } = client.query.useQuery({`,
3216
+ `const { data, isLoading } = query.useQuery({`,
3204
3217
  ` collection: '${collection}',`,
3205
3218
  ` options: { limit: 10 }`,
3206
3219
  `})`,
3207
3220
  ``,
3208
3221
  `// Single item`,
3209
- `const { data: item } = client.query.useQueryById({`,
3222
+ `const { data: item } = query.useQueryById({`,
3210
3223
  ` collection: '${collection}',`,
3211
3224
  ` id: itemId`,
3212
3225
  `})`
@@ -3215,31 +3228,28 @@ function generatePattern(collection, operation, surface) {
3215
3228
  if (operation === "write" || operation === "full-crud") {
3216
3229
  parts2.push(
3217
3230
  ``,
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' } })`,
3231
+ `// Writes belong in a server action or API route, not a client component.`,
3232
+ `const server = createServerClient({`,
3233
+ ` publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,`,
3234
+ ` secretKey: process.env.SOFTWARE_SECRET_KEY!`,
3235
+ `})`,
3225
3236
  ``,
3226
- `// Delete`,
3227
- `const { mutate: remove } = client.query.useRemove({ collection: '${collection}' })`,
3228
- `remove(itemId)`
3237
+ `await server.collections.from('${collection}').create({ title: 'New Item' })`,
3238
+ `await server.collections.from('${collection}').update(itemId, { title: 'Updated' })`,
3239
+ `await server.collections.from('${collection}').remove(itemId)`
3229
3240
  );
3230
3241
  }
3231
3242
  return {
3232
3243
  code: parts2.join("\n"),
3233
3244
  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"
3245
+ "React Query hooks provide automatic caching and background updates for browser-safe reads",
3246
+ 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
3247
  ]
3238
3248
  };
3239
3249
  }
3240
3250
  if (surface === "server-api") {
3241
3251
  const parts2 = [
3242
- `import { createServerClient } from '@01.software/sdk'`,
3252
+ `import { createServerClient } from '@01.software/sdk/server'`,
3243
3253
  ``,
3244
3254
  `const client = createServerClient({`,
3245
3255
  ` publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,`,
@@ -3406,8 +3416,12 @@ const result = await client.collections.from('products').find({
3406
3416
  })
3407
3417
 
3408
3418
  // Use React hooks
3419
+ import { createQueryHooks } from '@01.software/sdk/query'
3420
+
3421
+ const query = createQueryHooks(client)
3422
+
3409
3423
  function ProductList() {
3410
- const { data, isLoading } = client.query.useQuery({
3424
+ const { data, isLoading } = query.useQuery({
3411
3425
  collection: 'products',
3412
3426
  options: { limit: 10 }
3413
3427
  })
@@ -3424,7 +3438,7 @@ function ProductList() {
3424
3438
  }
3425
3439
  \`\`\`
3426
3440
 
3427
- ### React Query Hooks (client.query)
3441
+ ### React Query Hooks (@01.software/sdk/query)
3428
3442
 
3429
3443
  | Hook | Description |
3430
3444
  |------|-------------|
@@ -3459,13 +3473,17 @@ await client.collections.from('products').remove('id')
3459
3473
  // count() returns { totalDocs }
3460
3474
  const { totalDocs } = await client.collections.from('products').count()
3461
3475
 
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
- )
3476
+ // Metadata - generate Metadata-shaped SEO objects from fetched documents
3477
+ import { extractSeo, generateMetadata } from '@01.software/sdk/metadata'
3478
+
3479
+ const productResult = await client.collections.from('products').find({
3480
+ where: { slug: { equals: 'my-product' } },
3481
+ limit: 1,
3482
+ depth: 1,
3483
+ })
3484
+ const productMeta = productResult.docs[0]
3485
+ ? generateMetadata(extractSeo(productResult.docs[0]), { siteName: 'My Store' })
3486
+ : null
3469
3487
  \`\`\`
3470
3488
 
3471
3489
  ### Filtering (Payload Query Syntax)
@@ -3488,7 +3506,7 @@ For ecommerce collections, use \`products.listing.*\` for browse/search pricing
3488
3506
  ### Server Client (Server-side)
3489
3507
 
3490
3508
  \`\`\`typescript
3491
- import { createServerClient } from '@01.software/sdk'
3509
+ import { createServerClient } from '@01.software/sdk/server'
3492
3510
 
3493
3511
  const client = createServerClient({
3494
3512
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -3508,7 +3526,7 @@ if (!product) return notFound()
3508
3526
  // product: { product, variants, options, brand, categories, tags, images, videos, listing }
3509
3527
  \`\`\`
3510
3528
 
3511
- For React: \`const { data } = client.query.useProductDetailBySlug(slug)\`.
3529
+ For React: \`const { data } = createQueryHooks(client).useProductDetailBySlug(slug)\`.
3512
3530
 
3513
3531
  ### Product listing (grouped)
3514
3532
 
@@ -3563,7 +3581,8 @@ ${filters}
3563
3581
  ### ${operation === "find" ? "Query" : operation === "create" ? "Create" : operation === "update" ? "Update" : "Delete"} Example
3564
3582
 
3565
3583
  \`\`\`typescript
3566
- import { createClient, createServerClient } from '@01.software/sdk'
3584
+ import { createClient } from '@01.software/sdk'
3585
+ import { createServerClient } from '@01.software/sdk/server'
3567
3586
 
3568
3587
  // Client (read-only public collections)
3569
3588
  const client = createClient({
@@ -3584,14 +3603,18 @@ const result = await ${readClientName}.collections.from('${collection}').find(${
3584
3603
  // result.docs - array of items
3585
3604
  // result.totalDocs, result.page, result.totalPages, result.hasNextPage, ...
3586
3605
 
3587
- ${isPublicCollection ? `// Using React Hook
3588
- const { data, isLoading, error } = client.query.useQuery({
3606
+ ${isPublicCollection ? `// Using React Query hooks
3607
+ import { createQueryHooks } from '@01.software/sdk/query'
3608
+
3609
+ const query = createQueryHooks(client)
3610
+
3611
+ const { data, isLoading, error } = query.useQuery({
3589
3612
  collection: '${collection}',
3590
3613
  options: { limit: 10 }
3591
3614
  })
3592
3615
 
3593
3616
  // With Suspense
3594
- const { data } = client.query.useSuspenseQuery({
3617
+ const { data } = query.useSuspenseQuery({
3595
3618
  collection: '${collection}',
3596
3619
  options: { limit: 10 }
3597
3620
  })` : `// React hooks are browser/public only and do not support '${collection}'.`}` : operation === "create" ? `// Create ${collection} item (ServerClient only)
@@ -3660,7 +3683,7 @@ var SCENARIOS = {
3660
3683
 
3661
3684
  ### Code Example (ServerClient)
3662
3685
  \`\`\`typescript
3663
- import { createServerClient } from '@01.software/sdk'
3686
+ import { createServerClient } from '@01.software/sdk/server'
3664
3687
 
3665
3688
  const client = createServerClient({
3666
3689
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -3885,7 +3908,6 @@ customer-addresses
3885
3908
  ### Optional Collections
3886
3909
 
3887
3910
  - customer-groups \u2014 Console/server-scoped segmentation for VIP coupons and campaigns. Use \`createServerClient().collections.from('customer-groups')\`.
3888
- - customer-profile-lists \u2014 Public profile display/ranking lists for storefronts. Browser reads may use \`client.collections.from('customer-profile-lists')\`.
3889
3911
 
3890
3912
  ### Config
3891
3913
 
@@ -4026,7 +4048,9 @@ comments, reactions, bookmarks, reports, community-bans
4026
4048
 
4027
4049
  ### Optional Collections
4028
4050
 
4029
- post-categories, customer-profile-lists`
4051
+ post-categories, customer-profile-lists
4052
+
4053
+ - \`customer-profile-lists\` is Community-owned public profile curation. It composes \`customer-profiles\`, so effective Community access also requires Customers.`
4030
4054
  };
4031
4055
  function featureSetupGuide({
4032
4056
  feature
@@ -4139,12 +4163,7 @@ var COLLECTIONS_BY_CATEGORY = {
4139
4163
  "shipping-policies",
4140
4164
  "shipping-zones"
4141
4165
  ],
4142
- Customers: [
4143
- "customers",
4144
- "customer-profiles",
4145
- "customer-profile-lists",
4146
- "customer-addresses"
4147
- ],
4166
+ Customers: ["customers", "customer-profiles", "customer-addresses"],
4148
4167
  Carts: ["carts", "cart-items"],
4149
4168
  Discounts: ["discounts"],
4150
4169
  Documents: ["documents", "document-categories", "document-types"],
@@ -4160,7 +4179,8 @@ var COLLECTIONS_BY_CATEGORY = {
4160
4179
  "reactions",
4161
4180
  "reaction-types",
4162
4181
  "bookmarks",
4163
- "post-categories"
4182
+ "post-categories",
4183
+ "customer-profile-lists"
4164
4184
  ],
4165
4185
  Playlists: [
4166
4186
  "playlists",
@@ -4270,6 +4290,22 @@ yarn add @01.software/sdk
4270
4290
  pnpm add @01.software/sdk
4271
4291
  \`\`\`
4272
4292
 
4293
+ ## Optional peers
4294
+
4295
+ You do not need extra packages for the root SDK entry. Install peer
4296
+ dependencies only when you import a feature sub-path:
4297
+
4298
+ - \`@01.software/sdk/query\` -> \`@tanstack/react-query\`, \`react\`, \`react-dom\`
4299
+ - \`@01.software/sdk/realtime\` -> \`@tanstack/react-query\`
4300
+ - \`@01.software/sdk/analytics/react\` -> \`react\`, \`react-dom\`
4301
+ - \`@01.software/sdk/ui/rich-text\` -> \`@payloadcms/richtext-lexical\`
4302
+ - \`@01.software/sdk/ui/form\` -> none
4303
+ - \`@01.software/sdk/ui/code-block\` -> \`shiki\`, \`hast-util-to-jsx-runtime\`
4304
+ - \`@01.software/sdk/ui/canvas\` -> \`@tanstack/react-query\`, \`@xyflow/react\`, \`quickjs-emscripten\`, \`postcss\`, \`sucrase\`
4305
+ - \`@01.software/sdk/ui/canvas/server\` -> none
4306
+ - \`@01.software/sdk/ui/video\` -> \`@mux/mux-player-react\`
4307
+ - \`@01.software/sdk/ui/image\` -> none
4308
+
4273
4309
  ## Basic Usage
4274
4310
 
4275
4311
  \`\`\`typescript
@@ -4307,7 +4343,7 @@ Comprehensive guides to master the 01.software SDK.
4307
4343
 
4308
4344
  ## Data Fetching
4309
4345
 
4310
- Use the Query Builder or React Query hooks to fetch data efficiently.
4346
+ Use the Query Builder or opt-in React Query hooks to fetch data efficiently.
4311
4347
 
4312
4348
  ### Query Builder
4313
4349
  \`\`\`typescript
@@ -4335,7 +4371,10 @@ const { totalDocs } = await client.collections.from('products').count()
4335
4371
 
4336
4372
  ### React Query Hook
4337
4373
  \`\`\`typescript
4338
- const { data, isLoading, error } = client.query.useQuery({
4374
+ import { createQueryHooks } from '@01.software/sdk/query'
4375
+
4376
+ const query = createQueryHooks(client)
4377
+ const { data, isLoading, error } = query.useQuery({
4339
4378
  collection: 'products',
4340
4379
  options: {
4341
4380
  where: { status: { equals: 'published' } },
@@ -4346,7 +4385,7 @@ const { data, isLoading, error } = client.query.useQuery({
4346
4385
 
4347
4386
  ### Suspense Mode
4348
4387
  \`\`\`typescript
4349
- const { data } = client.query.useSuspenseQuery({
4388
+ const { data } = query.useSuspenseQuery({
4350
4389
  collection: 'products',
4351
4390
  options: { limit: 10 }
4352
4391
  })
@@ -4355,7 +4394,7 @@ const { data } = client.query.useSuspenseQuery({
4355
4394
  ### Infinite Scroll
4356
4395
  \`\`\`typescript
4357
4396
  const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
4358
- client.query.useInfiniteQuery({
4397
+ query.useInfiniteQuery({
4359
4398
  collection: 'products',
4360
4399
  pageSize: 20
4361
4400
  })
@@ -4402,19 +4441,18 @@ const result = await serverClient.from('products').removeMany(
4402
4441
  )
4403
4442
  \`\`\`
4404
4443
 
4405
- ### Mutation Hooks (React)
4444
+ ### Server Mutations
4406
4445
  \`\`\`typescript
4407
- // Create with auto cache invalidation
4408
- const { mutate: create } = client.query.useCreate({ collection: 'products' })
4409
- create({ title: 'New', status: 'draft' })
4446
+ import { createServerClient } from '@01.software/sdk/server'
4410
4447
 
4411
- // Update with auto cache invalidation
4412
- const { mutate: update } = client.query.useUpdate({ collection: 'products' })
4413
- update({ id: 'product-id', data: { title: 'Updated' } })
4448
+ const server = createServerClient({
4449
+ publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
4450
+ secretKey: process.env.SOFTWARE_SECRET_KEY!,
4451
+ })
4414
4452
 
4415
- // Remove with auto cache invalidation
4416
- const { mutate: remove } = client.query.useRemove({ collection: 'products' })
4417
- remove('product-id')
4453
+ await server.collections.from('products').create({ title: 'New', status: 'draft' })
4454
+ await server.collections.from('products').update('product-id', { title: 'Updated' })
4455
+ await server.collections.from('products').remove('product-id')
4418
4456
  \`\`\`
4419
4457
 
4420
4458
  ## Caching Strategies
@@ -4424,17 +4462,17 @@ The SDK uses React Query for caching and background updates.
4424
4462
  ### SSR Prefetching
4425
4463
  \`\`\`typescript
4426
4464
  // Prefetch in server component for instant client hydration
4427
- await client.query.prefetchQuery({
4465
+ await query.prefetchQuery({
4428
4466
  collection: 'products',
4429
4467
  options: { limit: 10 }
4430
4468
  })
4431
4469
 
4432
- await client.query.prefetchQueryById({
4470
+ await query.prefetchQueryById({
4433
4471
  collection: 'products',
4434
4472
  id: 'product-id'
4435
4473
  })
4436
4474
 
4437
- await client.query.prefetchInfiniteQuery({
4475
+ await query.prefetchInfiniteQuery({
4438
4476
  collection: 'products',
4439
4477
  pageSize: 20
4440
4478
  })
@@ -4443,19 +4481,19 @@ await client.query.prefetchInfiniteQuery({
4443
4481
  ### Cache Invalidation
4444
4482
  \`\`\`typescript
4445
4483
  // Invalidate list cache for a collection
4446
- client.query.invalidateQueries('products', 'list')
4484
+ query.invalidateQueries('products', 'list')
4447
4485
 
4448
4486
  // Invalidate all caches for a collection
4449
- client.query.invalidateQueries('products')
4487
+ query.invalidateQueries('products')
4450
4488
  \`\`\`
4451
4489
 
4452
4490
  ### Manual Cache Management
4453
4491
  \`\`\`typescript
4454
4492
  // Read cached data
4455
- const cached = client.query.getQueryData('products', 'list')
4493
+ const cached = query.getQueryData('products', 'list')
4456
4494
 
4457
4495
  // Write to cache (optimistic updates)
4458
- client.query.setQueryData('products', 'list', newData)
4496
+ query.setQueryData('products', 'list', newData)
4459
4497
  \`\`\`
4460
4498
 
4461
4499
  ## Error Handling
@@ -4533,7 +4571,7 @@ const client = createClient({
4533
4571
  For server-side operations (full CRUD)
4534
4572
 
4535
4573
  \`\`\`typescript
4536
- import { createServerClient } from '@01.software/sdk'
4574
+ import { createServerClient } from '@01.software/sdk/server'
4537
4575
 
4538
4576
  const client = createServerClient({
4539
4577
  publishableKey: string,
@@ -4612,13 +4650,19 @@ const result = await client.collections.from('collection').removeMany(where)
4612
4650
  // Returns PayloadFindResponse with deleted docs
4613
4651
  \`\`\`
4614
4652
 
4615
- ## React Query Hooks (client.query)
4653
+ ## React Query Hooks (\`@01.software/sdk/query\`)
4654
+
4655
+ \`\`\`typescript
4656
+ import { createQueryHooks } from '@01.software/sdk/query'
4657
+
4658
+ const query = createQueryHooks(client)
4659
+ \`\`\`
4616
4660
 
4617
4661
  ### useQuery()
4618
4662
  Query a collection list.
4619
4663
 
4620
4664
  \`\`\`typescript
4621
- const { data, isLoading, error } = client.query.useQuery({
4665
+ const { data, isLoading, error } = query.useQuery({
4622
4666
  collection: 'products',
4623
4667
  options: { limit: 10, where: { status: { equals: 'published' } } }
4624
4668
  })
@@ -4628,7 +4672,7 @@ const { data, isLoading, error } = client.query.useQuery({
4628
4672
  Suspense mode list query.
4629
4673
 
4630
4674
  \`\`\`typescript
4631
- const { data } = client.query.useSuspenseQuery({
4675
+ const { data } = query.useSuspenseQuery({
4632
4676
  collection: 'products',
4633
4677
  options: { limit: 10 }
4634
4678
  })
@@ -4638,7 +4682,7 @@ const { data } = client.query.useSuspenseQuery({
4638
4682
  Get a single item by ID.
4639
4683
 
4640
4684
  \`\`\`typescript
4641
- const { data } = client.query.useQueryById({
4685
+ const { data } = query.useQueryById({
4642
4686
  collection: 'products',
4643
4687
  id: 'product-id'
4644
4688
  })
@@ -4648,7 +4692,7 @@ const { data } = client.query.useQueryById({
4648
4692
  Suspense mode single item query.
4649
4693
 
4650
4694
  \`\`\`typescript
4651
- const { data } = client.query.useSuspenseQueryById({
4695
+ const { data } = query.useSuspenseQueryById({
4652
4696
  collection: 'products',
4653
4697
  id: 'product-id'
4654
4698
  })
@@ -4658,7 +4702,7 @@ const { data } = client.query.useSuspenseQueryById({
4658
4702
  Infinite scroll.
4659
4703
 
4660
4704
  \`\`\`typescript
4661
- const { data, fetchNextPage, hasNextPage } = client.query.useInfiniteQuery({
4705
+ const { data, fetchNextPage, hasNextPage } = query.useInfiniteQuery({
4662
4706
  collection: 'products',
4663
4707
  options: { limit: 20 },
4664
4708
  pageSize: 20
@@ -4669,52 +4713,43 @@ const { data, fetchNextPage, hasNextPage } = client.query.useInfiniteQuery({
4669
4713
  Suspense mode infinite scroll.
4670
4714
 
4671
4715
  \`\`\`typescript
4672
- const { data, fetchNextPage } = client.query.useSuspenseInfiniteQuery({
4716
+ const { data, fetchNextPage } = query.useSuspenseInfiniteQuery({
4673
4717
  collection: 'products',
4674
4718
  pageSize: 20
4675
4719
  })
4676
4720
  \`\`\`
4677
4721
 
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
- \`\`\`
4722
+ ## Server Mutations
4687
4723
 
4688
- ### useUpdate()
4689
- Update a document with automatic cache invalidation.
4724
+ Writes require trusted server code with \`createServerClient\`; do not run server credentials in React client components.
4690
4725
 
4691
4726
  \`\`\`typescript
4692
- const { mutate } = client.query.useUpdate({ collection: 'products' })
4693
- mutate({ id: 'product-id', data: { title: 'Updated' } })
4694
- \`\`\`
4727
+ import { createServerClient } from '@01.software/sdk/server'
4695
4728
 
4696
- ### useRemove()
4697
- Remove a document with automatic cache invalidation.
4729
+ const server = createServerClient({
4730
+ publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
4731
+ secretKey: process.env.SOFTWARE_SECRET_KEY!,
4732
+ })
4698
4733
 
4699
- \`\`\`typescript
4700
- const { mutate } = client.query.useRemove({ collection: 'products' })
4701
- mutate('product-id')
4734
+ await server.collections.from('products').create({ title: 'New Product', status: 'draft' })
4735
+ await server.collections.from('products').update('product-id', { title: 'Updated' })
4736
+ await server.collections.from('products').remove('product-id')
4702
4737
  \`\`\`
4703
4738
 
4704
4739
  ## Cache Utilities
4705
4740
 
4706
4741
  \`\`\`typescript
4707
4742
  // Invalidate cache
4708
- client.query.invalidateQueries('products', 'list')
4743
+ query.invalidateQueries('products', 'list')
4709
4744
 
4710
4745
  // 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 })
4746
+ await query.prefetchQuery({ collection: 'products', options: { limit: 10 } })
4747
+ await query.prefetchQueryById({ collection: 'products', id: 'id' })
4748
+ await query.prefetchInfiniteQuery({ collection: 'products', pageSize: 20 })
4714
4749
 
4715
4750
  // Get/set cached data
4716
- const cached = client.query.getQueryData('products', 'list')
4717
- client.query.setQueryData('products', 'list', newData)
4751
+ const cached = query.getQueryData('products', 'list')
4752
+ query.setQueryData('products', 'list', newData)
4718
4753
  \`\`\`
4719
4754
 
4720
4755
  For ecommerce reads, prefer \`products.listing.*\` for card/search pricing and keep authoritative sellable pricing on \`product-variants.price\`.
@@ -4961,7 +4996,7 @@ const result3 = await client.collections.from('products').find({ sort: '-isFeatu
4961
4996
  ## Full Example
4962
4997
 
4963
4998
  \`\`\`typescript
4964
- import { createServerClient } from '@01.software/sdk'
4999
+ import { createServerClient } from '@01.software/sdk/server'
4965
5000
 
4966
5001
  const serverClient = createServerClient({
4967
5002
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -4989,14 +5024,22 @@ console.log(result.hasNextPage) // true
4989
5024
  var metadata43 = {
4990
5025
  name: "docs-react-query",
4991
5026
  title: "React Query Hooks",
4992
- description: "01.software SDK React Query hooks reference (client.query)"
5027
+ description: "01.software SDK React Query hooks reference (@01.software/sdk/query)"
4993
5028
  };
4994
5029
  function handler13() {
4995
5030
  return `# React Query Hooks
4996
5031
 
4997
- React Query hooks are available on the browser-side \`Client\` via \`client.query\`. They provide automatic caching, background refetching, and cache invalidation.
5032
+ 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
5033
 
4999
- > React Query hooks are available on \`Client\` (createClient) only. \`ServerClient\` does not include them.
5034
+ Install \`@tanstack/react-query\` and React peers only when importing this sub-path.
5035
+
5036
+ \`\`\`typescript
5037
+ import { createClient } from '@01.software/sdk'
5038
+ import { createQueryHooks } from '@01.software/sdk/query'
5039
+
5040
+ const client = createClient({ publishableKey })
5041
+ const query = createQueryHooks(client)
5042
+ \`\`\`
5000
5043
 
5001
5044
  ## Query Hooks
5002
5045
 
@@ -5004,7 +5047,7 @@ React Query hooks are available on the browser-side \`Client\` via \`client.quer
5004
5047
  Query a collection list with automatic caching.
5005
5048
 
5006
5049
  \`\`\`typescript
5007
- const { data, isLoading, error } = client.query.useQuery({
5050
+ const { data, isLoading, error } = query.useQuery({
5008
5051
  collection: 'products',
5009
5052
  options: {
5010
5053
  where: { status: { equals: 'published' } },
@@ -5022,7 +5065,7 @@ Suspense-mode list query. Throws a promise while loading (use with React Suspens
5022
5065
 
5023
5066
  \`\`\`typescript
5024
5067
  // Inside a Suspense boundary
5025
- const { data } = client.query.useSuspenseQuery({
5068
+ const { data } = query.useSuspenseQuery({
5026
5069
  collection: 'products',
5027
5070
  options: { limit: 10 },
5028
5071
  })
@@ -5033,7 +5076,7 @@ const { data } = client.query.useSuspenseQuery({
5033
5076
  Get a single document by ID.
5034
5077
 
5035
5078
  \`\`\`typescript
5036
- const { data, isLoading } = client.query.useQueryById({
5079
+ const { data, isLoading } = query.useQueryById({
5037
5080
  collection: 'products',
5038
5081
  id: 'product-id',
5039
5082
  })
@@ -5044,7 +5087,7 @@ const { data, isLoading } = client.query.useQueryById({
5044
5087
  Suspense-mode single document query.
5045
5088
 
5046
5089
  \`\`\`typescript
5047
- const { data } = client.query.useSuspenseQueryById({
5090
+ const { data } = query.useSuspenseQueryById({
5048
5091
  collection: 'products',
5049
5092
  id: 'product-id',
5050
5093
  })
@@ -5060,7 +5103,7 @@ const {
5060
5103
  fetchNextPage,
5061
5104
  hasNextPage,
5062
5105
  isFetchingNextPage,
5063
- } = client.query.useInfiniteQuery({
5106
+ } = query.useInfiniteQuery({
5064
5107
  collection: 'products',
5065
5108
  options: {
5066
5109
  where: { status: { equals: 'published' } },
@@ -5076,55 +5119,33 @@ const {
5076
5119
  Suspense-mode infinite scroll.
5077
5120
 
5078
5121
  \`\`\`typescript
5079
- const { data, fetchNextPage, hasNextPage } = client.query.useSuspenseInfiniteQuery({
5122
+ const { data, fetchNextPage, hasNextPage } = query.useSuspenseInfiniteQuery({
5080
5123
  collection: 'products',
5081
5124
  pageSize: 20,
5082
5125
  })
5083
5126
  \`\`\`
5084
5127
 
5085
- ## Mutation Hooks
5086
-
5087
- Mutation hooks automatically invalidate relevant cache keys after success.
5128
+ ## Writes
5088
5129
 
5089
- ### useCreate()
5090
- Create a document and invalidate list cache.
5130
+ 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
5131
 
5092
5132
  \`\`\`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\`.
5133
+ // app/products/actions.ts
5134
+ 'use server'
5106
5135
 
5107
- ### useUpdate()
5108
- Update a document and invalidate list + detail cache.
5136
+ import { createServerClient } from '@01.software/sdk/server'
5109
5137
 
5110
- \`\`\`typescript
5111
- const { mutate, mutateAsync } = client.query.useUpdate({
5112
- collection: 'products',
5138
+ const server = createServerClient({
5139
+ publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
5140
+ secretKey: process.env.SOFTWARE_SECRET_KEY!,
5113
5141
  })
5114
5142
 
5115
- mutate({ id: 'product-id', data: { title: 'Updated Title' } })
5143
+ export async function createProduct(data: { title: string; status: 'draft' | 'published' }) {
5144
+ return server.collections.from('products').create(data)
5145
+ }
5116
5146
  \`\`\`
5117
5147
 
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
- \`\`\`
5148
+ For ecommerce reads, price-oriented product cards should consume \`products.listing.minPrice/maxPrice\`. Authoritative sellable pricing still lives on \`product-variants.price\`.
5128
5149
 
5129
5150
  ## Cache Utilities
5130
5151
 
@@ -5133,10 +5154,10 @@ Manually invalidate cached queries for a collection.
5133
5154
 
5134
5155
  \`\`\`typescript
5135
5156
  // Invalidate all list queries for a collection
5136
- client.query.invalidateQueries('products', 'list')
5157
+ query.invalidateQueries('products', 'list')
5137
5158
 
5138
5159
  // Invalidate all queries for a collection (list + detail)
5139
- client.query.invalidateQueries('products')
5160
+ query.invalidateQueries('products')
5140
5161
  \`\`\`
5141
5162
 
5142
5163
  ### getQueryData() / setQueryData()
@@ -5144,10 +5165,10 @@ Read and write the React Query cache directly.
5144
5165
 
5145
5166
  \`\`\`typescript
5146
5167
  // Read cached data
5147
- const cached = client.query.getQueryData('products', 'list')
5168
+ const cached = query.getQueryData('products', 'list')
5148
5169
 
5149
5170
  // Write to cache (useful for optimistic updates)
5150
- client.query.setQueryData('products', 'list', newData)
5171
+ query.setQueryData('products', 'list', newData)
5151
5172
  \`\`\`
5152
5173
 
5153
5174
  ## SSR Prefetching
@@ -5157,34 +5178,37 @@ Prefetch data in server components for instant hydration on the client.
5157
5178
  \`\`\`typescript
5158
5179
  // app/products/page.tsx (Next.js App Router Server Component)
5159
5180
  import { HydrationBoundary, dehydrate } from '@tanstack/react-query'
5160
- import { createServerClient } from '@01.software/sdk'
5181
+ import { createServerClient } from '@01.software/sdk/server'
5182
+ import { createServerQueryHooks, getQueryClient } from '@01.software/sdk/query'
5161
5183
 
5162
5184
  export default async function ProductsPage() {
5163
5185
  const serverClient = createServerClient({
5164
5186
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
5165
5187
  secretKey: process.env.SOFTWARE_SECRET_KEY!,
5166
5188
  })
5189
+ const queryClient = getQueryClient()
5190
+ const serverQuery = createServerQueryHooks(serverClient, queryClient)
5167
5191
 
5168
5192
  // Prefetch list
5169
- await serverClient.query.prefetchQuery({
5193
+ await serverQuery.prefetchQuery({
5170
5194
  collection: 'products',
5171
5195
  options: { limit: 20, where: { status: { equals: 'published' } } },
5172
5196
  })
5173
5197
 
5174
5198
  // Prefetch single item
5175
- await serverClient.query.prefetchQueryById({
5199
+ await serverQuery.prefetchQueryById({
5176
5200
  collection: 'products',
5177
5201
  id: 'product-id',
5178
5202
  })
5179
5203
 
5180
5204
  // Prefetch infinite query
5181
- await serverClient.query.prefetchInfiniteQuery({
5205
+ await serverQuery.prefetchInfiniteQuery({
5182
5206
  collection: 'products',
5183
5207
  pageSize: 20,
5184
5208
  })
5185
5209
 
5186
5210
  return (
5187
- <HydrationBoundary state={dehydrate(serverClient.query.queryClient)}>
5211
+ <HydrationBoundary state={dehydrate(queryClient)}>
5188
5212
  <ProductList />
5189
5213
  </HydrationBoundary>
5190
5214
  )
@@ -5197,13 +5221,15 @@ export default async function ProductsPage() {
5197
5221
  'use client'
5198
5222
 
5199
5223
  import { createClient } from '@01.software/sdk'
5224
+ import { createQueryHooks } from '@01.software/sdk/query'
5200
5225
 
5201
5226
  const client = createClient({
5202
5227
  publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!,
5203
5228
  })
5229
+ const query = createQueryHooks(client)
5204
5230
 
5205
5231
  export function ProductList() {
5206
- const { data, isLoading, error } = client.query.useQuery({
5232
+ const { data, isLoading, error } = query.useQuery({
5207
5233
  collection: 'products',
5208
5234
  options: {
5209
5235
  where: { status: { equals: 'published' } },
@@ -5212,20 +5238,13 @@ export function ProductList() {
5212
5238
  },
5213
5239
  })
5214
5240
 
5215
- const { mutate: removeProduct } = client.query.useRemove({
5216
- collection: 'products',
5217
- })
5218
-
5219
5241
  if (isLoading) return <div>Loading...</div>
5220
5242
  if (error) return <div>Error: {error.message}</div>
5221
5243
 
5222
5244
  return (
5223
5245
  <ul>
5224
5246
  {data?.docs.map((product) => (
5225
- <li key={product.id}>
5226
- {product.title}
5227
- <button onClick={() => removeProduct(product.id)}>Delete</button>
5228
- </li>
5247
+ <li key={product.id}>{product.title}</li>
5229
5248
  ))}
5230
5249
  </ul>
5231
5250
  )
@@ -5245,7 +5264,7 @@ function handler14() {
5245
5264
  Server-side operations are available via \`client.commerce\` on \`ServerClient\`. Use \`createServerClient\` with both \`publishableKey\` and \`secretKey\`.
5246
5265
 
5247
5266
  \`\`\`typescript
5248
- import { createServerClient } from '@01.software/sdk'
5267
+ import { createServerClient } from '@01.software/sdk/server'
5249
5268
 
5250
5269
  const client = createServerClient({
5251
5270
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -5694,9 +5713,9 @@ The SDK provides two client types for different execution environments.
5694
5713
  | Read (\`find\`, \`findById\`, \`count\`) | Yes | Yes |
5695
5714
  | Write (\`create\`, \`update\`, \`remove\`) | No | Yes |
5696
5715
  | Bulk (\`updateMany\`, \`removeMany\`) | No | Yes |
5697
- | React Query hooks (\`client.query\`) | Yes | Yes (SSR prefetch) |
5716
+ | React Query hooks (\`@01.software/sdk/query\`) | Yes | Yes (SSR prefetch) |
5698
5717
  | Customer auth (\`client.customer\`) | Yes | No |
5699
- | Server API (\`client.api\`) | No | Yes |
5718
+ | Commerce/server APIs (\`server.commerce\`, server CRUD) | No | Yes |
5700
5719
 
5701
5720
  ## Client (createClient)
5702
5721
 
@@ -5704,16 +5723,18 @@ Use in browser code, React client components, and anywhere the secret key must n
5704
5723
 
5705
5724
  \`\`\`typescript
5706
5725
  import { createClient } from '@01.software/sdk'
5726
+ import { createQueryHooks } from '@01.software/sdk/query'
5707
5727
 
5708
5728
  const client = createClient({
5709
5729
  publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!,
5710
5730
  })
5731
+ const query = createQueryHooks(client)
5711
5732
 
5712
5733
  // Read data
5713
5734
  const products = await client.collections.from('products').find({ limit: 10 })
5714
5735
 
5715
5736
  // React Query hooks
5716
- const { data } = client.query.useQuery({ collection: 'products' })
5737
+ const { data } = query.useQuery({ collection: 'products' })
5717
5738
 
5718
5739
  // Customer auth
5719
5740
  await client.customer.login({ email, password })
@@ -5726,7 +5747,7 @@ await client.customer.login({ email, password })
5726
5747
  Use in server components, API routes, server actions, and background jobs. Has full CRUD access.
5727
5748
 
5728
5749
  \`\`\`typescript
5729
- import { createServerClient } from '@01.software/sdk'
5750
+ import { createServerClient } from '@01.software/sdk/server'
5730
5751
 
5731
5752
  const client = createServerClient({
5732
5753
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -5747,11 +5768,14 @@ await client.collections.from('product-variants').create({
5747
5768
  })
5748
5769
  await client.collections.from('products').remove('product-id')
5749
5770
 
5750
- // Server API (orders, carts, etc.)
5771
+ // Commerce API (orders, carts, etc.)
5751
5772
  await client.commerce.orders.create({ ... })
5752
5773
  await client.commerce.orders.checkout({ ... })
5753
5774
  \`\`\`
5754
5775
 
5776
+ Server-only code must import \`createServerClient\` from the \`/server\`
5777
+ sub-path.
5778
+
5755
5779
  **Environment variables**:
5756
5780
  - \`SOFTWARE_PUBLISHABLE_KEY\` \u2014 publishable key (no NEXT_PUBLIC prefix, server-only)
5757
5781
  - \`SOFTWARE_SECRET_KEY\` \u2014 server credential
@@ -5781,7 +5805,7 @@ deploying again.
5781
5805
 
5782
5806
  \`\`\`typescript
5783
5807
  // lib/sdk.ts \u2014 server-only module
5784
- import { createServerClient } from '@01.software/sdk'
5808
+ import { createServerClient } from '@01.software/sdk/server'
5785
5809
 
5786
5810
  export function getServerClient() {
5787
5811
  return createServerClient({
@@ -5794,10 +5818,12 @@ export function getServerClient() {
5794
5818
  \`\`\`typescript
5795
5819
  // lib/sdk-client.ts \u2014 browser-safe module
5796
5820
  import { createClient } from '@01.software/sdk'
5821
+ import { createQueryHooks } from '@01.software/sdk/query'
5797
5822
 
5798
5823
  export const browserClient = createClient({
5799
5824
  publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!,
5800
5825
  })
5826
+ export const browserQuery = createQueryHooks(browserClient)
5801
5827
  \`\`\`
5802
5828
 
5803
5829
  \`\`\`typescript
@@ -5814,10 +5840,10 @@ export default async function ProductsPage() {
5814
5840
  \`\`\`typescript
5815
5841
  // components/product-list.tsx \u2014 Client Component
5816
5842
  'use client'
5817
- import { browserClient } from '@/lib/sdk-client'
5843
+ import { browserQuery } from '@/lib/sdk-client'
5818
5844
 
5819
5845
  export function ProductList() {
5820
- const { data } = browserClient.query.useQuery({ collection: 'products' })
5846
+ const { data } = browserQuery.useQuery({ collection: 'products' })
5821
5847
  return <ul>{data?.docs.map(p => <li key={p.id}>{p.title}</li>)}</ul>
5822
5848
  }
5823
5849
  \`\`\`
@@ -5848,7 +5874,7 @@ Upload files using the \`images\` collection (tenant-scoped, unified image store
5848
5874
  Use \`ServerClient\` with \`FormData\` to upload images server-side.
5849
5875
 
5850
5876
  \`\`\`typescript
5851
- import { createServerClient } from '@01.software/sdk'
5877
+ import { createServerClient } from '@01.software/sdk/server'
5852
5878
 
5853
5879
  const client = createServerClient({
5854
5880
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -5871,7 +5897,7 @@ async function uploadImage(file: File) {
5871
5897
  \`\`\`typescript
5872
5898
  // app/api/upload/route.ts
5873
5899
  import { NextRequest, NextResponse } from 'next/server'
5874
- import { createServerClient } from '@01.software/sdk'
5900
+ import { createServerClient } from '@01.software/sdk/server'
5875
5901
 
5876
5902
  const client = createServerClient({
5877
5903
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -5901,7 +5927,7 @@ export async function POST(req: NextRequest) {
5901
5927
  // actions/upload.ts
5902
5928
  'use server'
5903
5929
 
5904
- import { createServerClient } from '@01.software/sdk'
5930
+ import { createServerClient } from '@01.software/sdk/server'
5905
5931
 
5906
5932
  const client = createServerClient({
5907
5933
  publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
@@ -6124,29 +6150,38 @@ const client = createClient({
6124
6150
  const detail = await client.commerce.product.detail({ slug: 'my-product' })
6125
6151
  if (!detail) return notFound()
6126
6152
  const selection = resolveProductSelection(detail, {
6127
- search: '?opt.color=black&opt.size=large',
6153
+ search: '?opt.option-color=color-black',
6128
6154
  })
6129
6155
  // selection.selectedVariant, selection.price, selection.stock, selection.media
6130
6156
  \`\`\`
6131
6157
 
6132
6158
  ## URL round-trip
6133
6159
 
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.
6160
+ Use the SDK codec for canonical selection URLs. Complete selections use
6161
+ \`variant=<variantId>\`; partial selections use
6162
+ \`opt.<optionId>=<valueId>\`. Older
6163
+ \`opt.<optionSlug>=<valueSlug>\` URLs still decode during Stage 1, but slugs are
6164
+ compatibility metadata rather than canonical identity.
6135
6165
 
6136
6166
  \`\`\`typescript
6137
6167
  import { createProductSelectionCodec } from '@01.software/sdk'
6138
6168
 
6139
6169
  const codec = createProductSelectionCodec(detail)
6140
- const selection = codec.parse('?opt.color=black')
6141
- const href = codec.stringify(selection)
6170
+ const selection = codec.parse('?opt.option-color=color-black')
6171
+ const selectionQuery = codec.stringify(selection)
6142
6172
  \`\`\`
6143
6173
 
6144
- Value-slug-only URLs such as \`?black\` or \`?color=black\` are rejected because option titles are not stable identifiers.
6174
+ 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.
6175
+
6176
+ Value-slug-only URLs such as \`?black\` or \`?color=black\` are rejected because option titles and values are not stable identifiers.
6145
6177
 
6146
6178
  ## React Query hook variant
6147
6179
 
6148
6180
  \`\`\`typescript
6149
- const { data: detail, isLoading } = client.query.useProductDetailBySlug(slug)
6181
+ import { createQueryHooks } from '@01.software/sdk/query'
6182
+
6183
+ const query = createQueryHooks(client)
6184
+ const { data: detail, isLoading } = query.useProductDetailBySlug(slug)
6150
6185
  \`\`\`
6151
6186
 
6152
6187
  Cache invalidates automatically when any of 10 detail-relevant collections is mutated through SDK mutation hooks.
@@ -6540,4 +6575,4 @@ export {
6540
6575
  flushMcpTelemetrySummary,
6541
6576
  createServer
6542
6577
  };
6543
- //# sourceMappingURL=chunk-VSMPWKVX.js.map
6578
+ //# sourceMappingURL=chunk-2EPYMNHW.js.map