@01.software/sdk 0.36.0 → 0.38.0

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.
Files changed (82) hide show
  1. package/README.md +142 -55
  2. package/dist/analytics/react.cjs +33 -9
  3. package/dist/analytics/react.cjs.map +1 -1
  4. package/dist/analytics/react.d.cts +1 -1
  5. package/dist/analytics/react.d.ts +1 -1
  6. package/dist/analytics/react.js +33 -9
  7. package/dist/analytics/react.js.map +1 -1
  8. package/dist/analytics.cjs +30 -8
  9. package/dist/analytics.cjs.map +1 -1
  10. package/dist/analytics.d.cts +6 -0
  11. package/dist/analytics.d.ts +6 -0
  12. package/dist/analytics.js +30 -8
  13. package/dist/analytics.js.map +1 -1
  14. package/dist/client.cjs +150 -207
  15. package/dist/client.cjs.map +1 -1
  16. package/dist/client.d.cts +6 -6
  17. package/dist/client.d.ts +6 -6
  18. package/dist/client.js +150 -207
  19. package/dist/client.js.map +1 -1
  20. package/dist/{collection-client-Cv0D2w1Q.d.cts → collection-client-B0J9wMNE.d.cts} +5 -5
  21. package/dist/{collection-client-Bq5Zd7p7.d.ts → collection-client-BroIWHY1.d.ts} +5 -5
  22. package/dist/const-6XHz_jej.d.ts +32 -0
  23. package/dist/const-B5KT72c7.d.cts +32 -0
  24. package/dist/{image-BDz2-AaO.d.cts → image-BDjHp03R.d.cts} +13 -9
  25. package/dist/{image-BDz2-AaO.d.ts → image-BDjHp03R.d.ts} +13 -9
  26. package/dist/{index-DTSXUYkr.d.ts → index-BOLQxveo.d.cts} +9 -6
  27. package/dist/{index-BHDKJ6B3.d.cts → index-CSwR2HSg.d.ts} +9 -6
  28. package/dist/index.cjs +256 -244
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.cts +12 -11
  31. package/dist/index.d.ts +12 -11
  32. package/dist/index.js +256 -244
  33. package/dist/index.js.map +1 -1
  34. package/dist/metadata.cjs +5 -3
  35. package/dist/metadata.cjs.map +1 -1
  36. package/dist/metadata.js +5 -3
  37. package/dist/metadata.js.map +1 -1
  38. package/dist/{payload-types-BCui2Oml.d.cts → payload-types-m3jjhxk9.d.cts} +669 -184
  39. package/dist/{payload-types-BCui2Oml.d.ts → payload-types-m3jjhxk9.d.ts} +669 -184
  40. package/dist/query.cjs +3 -1033
  41. package/dist/query.cjs.map +1 -1
  42. package/dist/query.d.cts +13 -13
  43. package/dist/query.d.ts +13 -13
  44. package/dist/query.js +3 -1033
  45. package/dist/query.js.map +1 -1
  46. package/dist/realtime.d.cts +2 -2
  47. package/dist/realtime.d.ts +2 -2
  48. package/dist/server.cjs +121 -85
  49. package/dist/server.cjs.map +1 -1
  50. package/dist/server.d.cts +7 -7
  51. package/dist/server.d.ts +7 -7
  52. package/dist/server.js +121 -85
  53. package/dist/server.js.map +1 -1
  54. package/dist/{types-Dib-zdK6.d.cts → types-CIGscmus.d.cts} +1471 -1100
  55. package/dist/{types-3qV6sY7T.d.ts → types-Cmrd1ezc.d.ts} +1 -15
  56. package/dist/{types-CEzLf3PX.d.cts → types-D0ubzQw0.d.cts} +1 -15
  57. package/dist/{types-DK9EnLwJ.d.ts → types-D2xYdz4P.d.ts} +1471 -1100
  58. package/dist/ui/canvas.cjs +15 -5
  59. package/dist/ui/canvas.cjs.map +1 -1
  60. package/dist/ui/canvas.d.cts +1 -1
  61. package/dist/ui/canvas.d.ts +1 -1
  62. package/dist/ui/canvas.js +15 -5
  63. package/dist/ui/canvas.js.map +1 -1
  64. package/dist/ui/form.d.cts +1 -1
  65. package/dist/ui/form.d.ts +1 -1
  66. package/dist/ui/image.cjs +15 -5
  67. package/dist/ui/image.cjs.map +1 -1
  68. package/dist/ui/image.d.cts +1 -1
  69. package/dist/ui/image.d.ts +1 -1
  70. package/dist/ui/image.js +15 -5
  71. package/dist/ui/image.js.map +1 -1
  72. package/dist/ui/video.d.cts +1 -1
  73. package/dist/ui/video.d.ts +1 -1
  74. package/dist/webhook.cjs +5 -1
  75. package/dist/webhook.cjs.map +1 -1
  76. package/dist/webhook.d.cts +4 -4
  77. package/dist/webhook.d.ts +4 -4
  78. package/dist/webhook.js +5 -1
  79. package/dist/webhook.js.map +1 -1
  80. package/package.json +3 -3
  81. package/dist/const-BDUKFP9w.d.ts +0 -32
  82. package/dist/const-DVcM7Ac_.d.cts +0 -32
package/README.md CHANGED
@@ -140,8 +140,16 @@ const serverQuery = createServerQueryHooks(server)
140
140
  const order = await server.commerce.orders.create({
141
141
  orderNumber: generateOrderNumber(),
142
142
  customerSnapshot: { email: 'user@example.com' },
143
- shippingAddress: { recipientName: 'John', phone: '010-1234-5678', postalCode: '12345', address: 'Seoul', detailAddress: 'Apt 101' },
144
- orderItems: [{ product: productId, variant: variantId, option: optionId, quantity: 1 }],
143
+ shippingAddress: {
144
+ recipientName: 'John',
145
+ phone: '010-1234-5678',
146
+ postalCode: '12345',
147
+ address: 'Seoul',
148
+ detailAddress: 'Apt 101',
149
+ },
150
+ items: [
151
+ { product: productId, variant: variantId, option: optionId, quantity: 1 },
152
+ ],
145
153
  totalAmount: 10000,
146
154
  pgPaymentId: 'provider-payment-id', // optional (omit for free orders)
147
155
  discountCode: 'WELCOME10', // optional
@@ -154,6 +162,10 @@ await serverQuery.prefetchQuery({
154
162
  })
155
163
  ```
156
164
 
165
+ Create-order inputs prefer `items`; existing `orderItems` callers remain
166
+ supported and are serialized to the same endpoint field. Order reads expose
167
+ joined line items at `order.items.docs`.
168
+
157
169
  Always import `createServerClient` from `@01.software/sdk/server` so generated
158
170
  code and bundlers do not blur the Secret Key boundary.
159
171
 
@@ -215,14 +227,13 @@ catalog/stock split instead of reading `variant.stock` from a cached
215
227
  `detail()` response (inventory in that payload can lag for the catalog TTL).
216
228
 
217
229
  ```typescript
218
- import {
219
- createClient,
220
- mergeProductDetailWithStock,
221
- } from '@01.software/sdk'
230
+ import { createClient, mergeProductDetailWithStock } from '@01.software/sdk'
222
231
 
223
232
  const client = createClient({ publishableKey: '<publishable-key>' })
224
233
 
225
- const catalog = await client.commerce.product.detailCatalog({ slug: 'every-peach-tee' })
234
+ const catalog = await client.commerce.product.detailCatalog({
235
+ slug: 'every-peach-tee',
236
+ })
226
237
  if (!catalog.found) return notFound()
227
238
 
228
239
  const variantIds = catalog.product.variants.map((v) => v.id)
@@ -239,6 +250,51 @@ shell, then `stockSnapshot()` (or `stock-check` at cart/checkout) for live
239
250
  availability. `detail()` and POST detail/listing endpoints are unchanged during
240
251
  the migration window; see ADR 0012 addendum in `docs/decisions/0012-sdk-public-commerce-contract.md`.
241
252
 
253
+ ### Product Listing Pages (PLP) — join-safe queries
254
+
255
+ **Recommended path:** Use `commerce.product.listingGroupsCatalog()` (or the
256
+ `useProductListingGroupsCatalogQuery` hook) together with
257
+ `buildProductListingCard()`. The endpoint applies unlimited join fetches
258
+ server-side and returns pre-grouped `ProductListingGroupsItem[]` data, so color
259
+ swatches are never truncated regardless of how many option values the product
260
+ has.
261
+
262
+ ```typescript
263
+ import {
264
+ buildProductListingCard,
265
+ type ProductListingCard,
266
+ } from '@01.software/sdk'
267
+
268
+ const response = await client.commerce.product.listingGroupsCatalog({
269
+ where: { status: { equals: 'published' } },
270
+ limit: 24,
271
+ })
272
+
273
+ const cards: ProductListingCard[] = response.docs.map((item) =>
274
+ buildProductListingCard(item, { basePath: '/shop' }),
275
+ )
276
+ ```
277
+
278
+ **Escape hatch:** When you need to query the `products` collection directly
279
+ (bulk operations, custom filters, fields the helper does not expose), spread
280
+ `PRODUCT_PLP_FIND_OPTIONS` to raise the default Payload join limit of 10:
281
+
282
+ ```typescript
283
+ import { PRODUCT_PLP_FIND_OPTIONS } from '@01.software/sdk'
284
+
285
+ const { docs } = await client.collections.from('products').find({
286
+ ...PRODUCT_PLP_FIND_OPTIONS,
287
+ where: { status: { equals: 'published' } },
288
+ limit: 24,
289
+ })
290
+ ```
291
+
292
+ `PRODUCT_PLP_FIND_OPTIONS` sets `joins.variants` and `joins.options` to safe
293
+ limits with `sort: '_order'`. It cures top-level `products.options` and
294
+ `products.variants` join truncation but **cannot** cure the nested
295
+ `options[].values.docs` join — the Payload REST `joins` param is flat and
296
+ nested join limits require the listing-groups endpoint.
297
+
242
298
  ### Product selection helpers
243
299
 
244
300
  ```typescript
@@ -342,9 +398,13 @@ By default, partial selections (for example color only) leave
342
398
  `selectedOrFirstAvailableVariant` behavior with `fillDefaults: true`:
343
399
 
344
400
  ```typescript
345
- const resolution = resolveProductSelection(product, codec.parse('?opt.color=ivory'), {
346
- fillDefaults: true,
347
- })
401
+ const resolution = resolveProductSelection(
402
+ product,
403
+ codec.parse('?opt.color=ivory'),
404
+ {
405
+ fillDefaults: true,
406
+ },
407
+ )
348
408
  // resolution.selectedVariant is concrete; unselected options are filled
349
409
  // using the same available-by-order rules as listing selectionHintVariant.
350
410
  ```
@@ -444,7 +504,10 @@ the card should deep-link a complete hint variant.
444
504
 
445
505
  ```ts
446
506
  import { createClient } from '@01.software/sdk'
447
- import { createQueryHooks, getStorefrontQueryClient } from '@01.software/sdk/query'
507
+ import {
508
+ createQueryHooks,
509
+ getStorefrontQueryClient,
510
+ } from '@01.software/sdk/query'
448
511
 
449
512
  const client = createClient({ publishableKey: '...' })
450
513
  const query = createQueryHooks(client, getStorefrontQueryClient())
@@ -456,11 +519,12 @@ Most consumers should use the helper APIs above (`commerce.product.detail`, etc.
456
519
 
457
520
  ### `depth` — how deep to populate relationship fields
458
521
 
459
- `depth` is the primary control for populating relationships like `category`, `images`, `brand`. The configured Payload default applies when unset.
522
+ `depth` is the primary control for populating relationships like `category`, `images`, `brand`. Browser publishable-key raw collection reads are constrained to `depth: 0` with `joins: false`; relationship-rich storefront reads should use shaped helpers such as `commerce.product.detail()` / `listingGroupsCatalog()`, or a `createServerClient()` raw query when server credentials are appropriate. Browser SDK raw reads add those safe defaults automatically.
460
523
 
461
524
  ```typescript
462
525
  const product = await client.collections.from('products').findById(id, {
463
- depth: 2, // populates product.category, product.category.parent, etc.
526
+ depth: 0,
527
+ joins: false,
464
528
  })
465
529
  ```
466
530
 
@@ -469,7 +533,7 @@ const product = await client.collections.from('products').findById(id, {
469
533
  `populate` controls which fields are returned per collection. It does NOT decide which relationships to populate — that is `depth`.
470
534
 
471
535
  ```typescript
472
- await client.collections.from('products').find({
536
+ await server.collections.from('products').find({
473
537
  depth: 2,
474
538
  populate: {
475
539
  categories: { title: true, slug: true },
@@ -480,14 +544,14 @@ await client.collections.from('products').find({
480
544
 
481
545
  ### `joins` — Payload join-field reverse-relations
482
546
 
483
- `joins` is the correct control for Payload `type: 'join'` virtual reverse-relation fields. In this platform's public SDK schema, `products.variants`, `products.options`, `customers.orders`, `customers.addresses`, `posts.comments`, `article-authors.articles`, `orders.{items,transactions,fulfillments,returns}`, and similar reverse-relations are all join fields — you must use `joins` (not `depth`/`populate`) to control their pagination, sorting, filtering, and count. Internal backing joins such as product collection memberships are intentionally omitted from public SDK collection types.
547
+ `joins` is the correct control for Payload `type: 'join'` virtual reverse-relation fields. In this platform's SDK schema, browser-public relations such as `products.variants`, `products.options`, and `article-authors.articles`, plus server-auth relations such as `customers.orders`, `customers.addresses`, `posts.comments`, and `orders.{items,transactions,fulfillments,returns}`, are all join fields — you must use `joins` (not `depth`/`populate`) to control their pagination, sorting, filtering, and count. Internal backing joins such as product collection memberships are intentionally omitted from SDK collection types.
484
548
 
485
549
  ```typescript
486
550
  // Canonical product detail query — variants/options are join fields on Products
487
- await client.collections.from('products').find({
551
+ await server.collections.from('products').find({
488
552
  where: { slug: { equals } },
489
553
  joins: {
490
- variants: { limit: 50, sort: 'sortIndex' },
554
+ variants: { limit: 50, sort: '_order' },
491
555
  options: {},
492
556
  },
493
557
  depth: 2, // also populate normal relationships (category, brand, etc.)
@@ -495,10 +559,15 @@ await client.collections.from('products').find({
495
559
 
496
560
  // Disable all join-field population for a lightweight list query
497
561
  await client.collections.from('products').find({
562
+ depth: 0,
498
563
  joins: false,
499
564
  })
500
565
  ```
501
566
 
567
+ Each join field defaults to **limit 10** when `joins` is omitted. `depth` does not raise that cap — storefront PLPs that call `products.find()` with only `depth` and then `buildProductListingGroupsByOption()` can silently drop color swatches. Prefer `listingGroupsCatalog()` for PLP cards, or spread `PRODUCT_PLP_FIND_OPTIONS` for raw product queries (see [PLP join-safe queries](#product-listing-pages-plp--join-safe-queries) above).
568
+
569
+ Publishable-key browser raw reads must keep `joins: false`; join-expanded public storefront reads belong behind shaped helpers. Use `createServerClient()` for raw `joins` queries that need server credentials.
570
+
502
571
  `joins` does NOT populate normal relationship fields. Keys that do not match a `type: 'join'` field on the queried collection are silently ignored — e.g. `joins: { category: {} }` on Products is a no-op because `category` is not a join field there. For normal relationships use `depth` (and optionally `populate`).
503
572
 
504
573
  ### Filtering by relation
@@ -598,7 +667,7 @@ const { docs, totalDocs, hasNextPage } = await client.collections
598
667
  page: 1,
599
668
  sort: '-createdAt',
600
669
  where: { status: { equals: 'published' } },
601
- depth: 2,
670
+ depth: 0,
602
671
  select: { title: true, slug: true },
603
672
  })
604
673
 
@@ -608,8 +677,8 @@ const { docs } = await client.collections.from('products').find({
608
677
  joins: false, // disable joins for lightweight list
609
678
  })
610
679
 
611
- // Override relationship populate
612
- const product = await client.collections.from('products').findById(id, {
680
+ // Override relationship populate and join expansion (server credentials only)
681
+ const product = await server.collections.from('products').findById(id, {
613
682
  populate: { brands: { name: true, logo: true } },
614
683
  joins: { variants: { limit: 50 } },
615
684
  })
@@ -655,7 +724,7 @@ import { extractSeo, generateMetadata } from '@01.software/sdk/metadata'
655
724
  const { docs } = await client.collections.from('products').find({
656
725
  where: { slug: { equals: 'my-product' } },
657
726
  limit: 1,
658
- depth: 1,
727
+ depth: 0,
659
728
  })
660
729
  const metadata = docs[0]
661
730
  ? generateMetadata(extractSeo(docs[0]), { siteName: 'My Store' })
@@ -840,12 +909,12 @@ await client.customer.auth.changePassword(currentPassword, newPassword)
840
909
 
841
910
  ### Commerce Orders (ServerClient-only writes)
842
911
 
843
- Available on ServerClient via `server.commerce.orders.*`. Only `checkout` and `listMine` are also on Client.
912
+ Available on ServerClient via `server.commerce.orders.*`. `checkout` and `listMine` are also on Client and return sanitized customer-facing order DTOs. Use `server.collections.from('orders')` for raw operational order documents.
844
913
 
845
914
  ```typescript
846
915
  // Orders
847
916
  await server.commerce.orders.create(params)
848
- await server.commerce.orders.update({ orderNumber, status })
917
+ await server.commerce.orders.update({ orderNumber, status: 'confirmed' })
849
918
  await server.commerce.orders.checkout({ cartId, pgPaymentId?, orderNumber, customerSnapshot, discountCode? })
850
919
 
851
920
  // Single-order lookup (getOrder endpoint removed — use collection find)
@@ -856,8 +925,10 @@ const { docs: [order] } = await server.collections.from('orders').find({
856
925
  })
857
926
 
858
927
  // Fulfillment
859
- await server.commerce.orders.createFulfillment({ orderNumber, carrier, trackingNumber, items })
860
- await server.commerce.orders.bulkImportFulfillments({ items: [{ orderNumber, carrier?, trackingNumber? }] })
928
+ await server.commerce.orders.prepareFulfillmentOrder({ orderNumber })
929
+ await server.commerce.orders.createFulfillment({ orderNumber, items })
930
+ await server.commerce.orders.updateFulfillment({ fulfillmentId, carrier, trackingNumber })
931
+ await server.commerce.orders.bulkImportFulfillments({ items: [{ orderNumber, carrier, trackingNumber }] })
861
932
 
862
933
  // Provider-verified payment confirmation
863
934
  // Provider webhook handlers should verify with the provider first, then call:
@@ -872,16 +943,28 @@ await server.commerce.orders.confirmPayment({
872
943
 
873
944
  // Low-level transaction annotation / compatibility path. Prefer confirmPayment()
874
945
  // for normal provider-verified paid transitions.
946
+ // status: 'failed' records a retryable PG failure on the Transaction only; the
947
+ // Order stays pending until verified payment succeeds or the merchant cancels it.
875
948
  await server.commerce.orders.updateTransaction({ pgPaymentId, status, paymentMethod, receiptUrl })
876
949
 
877
950
  // Returns
878
- await server.commerce.orders.createReturn({ orderNumber, returnItems, refundAmount, reason? })
951
+ await server.commerce.orders.createReturn({
952
+ orderNumber,
953
+ returnItems: [{ orderItem, quantity, restockingFee? }],
954
+ refundAmount,
955
+ returnShippingFee?,
956
+ initialShippingRefundAmount?,
957
+ initialShippingRefundOverrideNote?,
958
+ reason?,
959
+ })
879
960
  await server.commerce.orders.updateReturn({ returnId, status })
880
961
  await server.commerce.orders.returnWithRefund({
881
962
  orderNumber,
882
963
  returnItems: [{ orderItem, quantity, restockingFee? }],
883
964
  refundAmount,
884
965
  returnShippingFee?,
966
+ initialShippingRefundAmount?,
967
+ initialShippingRefundOverrideNote?,
885
968
  pgPaymentId,
886
969
  })
887
970
  ```
@@ -913,11 +996,11 @@ not send removed legacy media inputs (`optionValue.thumbnail`,
913
996
  `optionValue.images`, `variant.thumbnail`); use `swatch.mediaItemId` and
914
997
  variant `images[]`. Unknown keys are not part of the published upsert contract.
915
998
 
916
- | Payload | Valid | Invalid |
917
- | ------- | ----- | ------- |
918
- | Create | `product: { title, ... }` + graph | `productId` on create; missing `product.title` |
919
- | Edit graph | `productId` + `graphRevision?` + graph | `product.title` / SEO on upsert; conflicting `productId` vs `product.id` |
920
- | Edit (legacy) | `product: { id }` + graph only | `product: { id, title }` on edit |
999
+ | Payload | Valid | Invalid |
1000
+ | ------------- | -------------------------------------- | ------------------------------------------------------------------------ |
1001
+ | Create | `product: { title, ... }` + graph | `productId` on create; missing `product.title` |
1002
+ | Edit graph | `productId` + `graphRevision?` + graph | `product.title` / SEO on upsert; conflicting `productId` vs `product.id` |
1003
+ | Edit (legacy) | `product: { id }` + graph only | `product: { id, title }` on edit |
921
1004
 
922
1005
  ```typescript
923
1006
  const result = await server.commerce.product.upsert({
@@ -1011,7 +1094,7 @@ for (const item of results) {
1011
1094
 
1012
1095
  ### Commerce Cart
1013
1096
 
1014
- Available on both Client and ServerClient via `commerce.cart.*`.
1097
+ Available on both Client and ServerClient via `commerce.cart.*`. These helpers return sanitized customer-facing cart DTOs; use `server.collections.from('carts' | 'cart-items')` for raw operational cart documents.
1015
1098
 
1016
1099
  ```typescript
1017
1100
  // Add item to cart
@@ -1029,7 +1112,7 @@ await client.commerce.cart.updateItem({ cartItemId, quantity })
1029
1112
  // Remove item from cart
1030
1113
  await client.commerce.cart.removeItem({ cartItemId })
1031
1114
 
1032
- // Other cart operations
1115
+ // Other cart operations return sanitized customer-facing cart DTOs.
1033
1116
  await client.commerce.cart.get(cartId)
1034
1117
  await client.commerce.cart.applyDiscount({ cartId, discountCode })
1035
1118
  await client.commerce.cart.removeDiscount({ cartId })
@@ -1105,7 +1188,10 @@ const customerAuthHandler = createCustomerAuthWebhookHandler({
1105
1188
  // Semantic order-change events keep operation as "update" for compatibility.
1106
1189
  // Use isOrderChangedWebhookEvent when you need to distinguish manual ordering
1107
1190
  // from content field edits.
1108
- import { handleWebhook, isOrderChangedWebhookEvent } from '@01.software/sdk/webhook'
1191
+ import {
1192
+ handleWebhook,
1193
+ isOrderChangedWebhookEvent,
1194
+ } from '@01.software/sdk/webhook'
1109
1195
 
1110
1196
  function getWebhookSecret(): string {
1111
1197
  const secret = process.env.WEBHOOK_SECRET
@@ -1138,31 +1224,32 @@ join-order surface and does not emit a semantic order-change webhook.
1138
1224
 
1139
1225
  ## Supported Collections
1140
1226
 
1141
- Source of truth: `packages/sdk/src/core/collection/const.ts` (`COLLECTIONS`: 73).
1227
+ Source of truth: `packages/sdk/src/core/collection/const.ts` (`COLLECTIONS`: 53).
1142
1228
 
1143
- | Category | Collections |
1229
+ | Category | Browser-public generic collections |
1144
1230
  | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
1145
- | Tenant | `tenants`, `tenant-metadata`, `tenant-logos` |
1146
- | Products | `products`, `product-variants`, `product-options`, `product-option-values`, `product-categories`, `product-tags`, `product-collections`, `brands`, `brand-logos` |
1147
- | Orders | `orders`, `order-items`, `returns`, `return-items`, `fulfillments`, `fulfillment-items`, `transactions` |
1148
- | Customers | `customers`, `customer-profiles`, `customer-addresses` |
1149
- | Carts | `carts`, `cart-items` |
1231
+ | Tenant | `tenants`, `tenant-metadata` |
1232
+ | Products | `products`, `product-variants`, `product-options`, `product-option-values`, `product-categories`, `product-tags`, `product-collections`, `brands` |
1233
+ | Customers | `customer-profiles` |
1150
1234
  | Commerce | `discounts`, `shipping-policies`, `shipping-zones` |
1151
1235
  | Content | `documents`, `document-categories`, `document-types`, `articles`, `article-authors`, `article-categories`, `article-tags`, `links`, `link-categories`, `link-tags` |
1152
1236
  | Playlists / Tracks | `playlists`, `playlist-categories`, `playlist-tags`, `tracks`, `track-categories`, `track-tags` |
1153
1237
  | Galleries | `galleries`, `gallery-categories`, `gallery-tags`, `gallery-items` |
1154
1238
  | Canvas | `canvases`, `canvas-node-types`, `canvas-edge-types`, `canvas-categories`, `canvas-tags`, `canvas-nodes`, `canvas-edges` |
1155
- | Videos | `videos`, `video-categories`, `video-tags` |
1156
- | Live Streams | `live-streams` |
1157
- | Media | `images` |
1158
- | Forms | `forms`, `form-submissions` |
1159
- | Community | `posts`, `comments`, `reactions`, `reaction-types`, `bookmarks`, `post-categories`, `customer-profile-lists` |
1239
+ | Videos | `video-categories`, `video-tags` |
1240
+ | Forms | `forms` |
1241
+ | Community | `reaction-types`, `post-categories`, `post-tags`, `customer-profile-lists` |
1160
1242
  | Events | `event-calendars`, `events`, `event-categories`, `event-occurrences`, `event-tags` |
1161
1243
 
1162
- Server-only collections: `customer-groups`, `reports`, and `community-bans`
1163
- are available from `createServerClient().collections` for segmentation and
1164
- moderation workflows, but are intentionally absent from browser collection
1165
- discovery.
1244
+ Server-only collections include raw media/logo records, customer and order
1245
+ operational records, raw cart/cart-item records, form submissions, raw community documents
1246
+ (`posts`, `comments`, `reactions`, `bookmarks`), live stream provider records,
1247
+ segmentation records, and moderation records. They remain available from
1248
+ `createServerClient().collections` with secret/PAT credentials. Shaped
1249
+ browser/customer helpers such as `commerce.cart.*`,
1250
+ `commerce.orders.listMine()`, and `commerce.orders.checkout()` remain available
1251
+ where customer-facing DTOs are needed, but raw slugs are intentionally absent
1252
+ from browser collection discovery.
1166
1253
 
1167
1254
  ## Utilities
1168
1255
 
@@ -1297,11 +1384,11 @@ API keys created without explicit scopes use the default `['read', 'write']`. Co
1297
1384
  `swatch` (`type`, `color`, `mediaItemId`). Legacy public fields are removed
1298
1385
  from SDK input/output helpers and from listing/detail contracts.
1299
1386
 
1300
- | Surface | Removed | Replacement |
1301
- | ------- | ------- | ----------- |
1302
- | Upsert option value | `swatchColor`, `thumbnail`, `images` | `swatch.type`, `swatch.color`, `swatch.mediaItemId` |
1303
- | Listing groups | `optionValueSwatchColor`, `optionValueThumbnail`, `optionValueImages` | `optionValueSwatch` |
1304
- | Product detail option value | `swatchColor`, `thumbnail`, `images` | `swatch` |
1387
+ | Surface | Removed | Replacement |
1388
+ | --------------------------- | --------------------------------------------------------------------- | --------------------------------------------------- |
1389
+ | Upsert option value | `swatchColor`, `thumbnail`, `images` | `swatch.type`, `swatch.color`, `swatch.mediaItemId` |
1390
+ | Listing groups | `optionValueSwatchColor`, `optionValueThumbnail`, `optionValueImages` | `optionValueSwatch` |
1391
+ | Product detail option value | `swatchColor`, `thumbnail`, `images` | `swatch` |
1305
1392
 
1306
1393
  Migration steps:
1307
1394
 
@@ -56,6 +56,27 @@ function createAnalytics(config) {
56
56
  const nav = navigator;
57
57
  return nav.doNotTrack === "1" || nav.globalPrivacyControl === true;
58
58
  }
59
+ const isLocalHost = (() => {
60
+ try {
61
+ const h = location.hostname;
62
+ return h === "localhost" || h === "127.0.0.1" || h.endsWith(".local");
63
+ } catch {
64
+ return false;
65
+ }
66
+ })();
67
+ const mode = config.mode ?? "auto";
68
+ const sendSuppressed = mode === "development" || mode === "auto" && isLocalHost;
69
+ let suppressNoticeShown = false;
70
+ function devSuppressNotice() {
71
+ if (suppressNoticeShown) return;
72
+ suppressNoticeShown = true;
73
+ try {
74
+ console.info(
75
+ `[01 analytics] events disabled on local host (mode: '${mode}'). Pass mode: 'production' to send while developing.`
76
+ );
77
+ } catch {
78
+ }
79
+ }
59
80
  let lastPath = null;
60
81
  let lastAt = 0;
61
82
  const autoTrack = config.autoTrack !== false;
@@ -86,6 +107,10 @@ function createAnalytics(config) {
86
107
  if (isDntActive()) return;
87
108
  const doc = document;
88
109
  if (doc.prerendering === true || document.visibilityState === "prerender") return;
110
+ if (sendSuppressed) {
111
+ devSuppressNotice();
112
+ return;
113
+ }
89
114
  const now = Date.now();
90
115
  if (pathname === lastPath && now - lastAt < 500) return;
91
116
  lastPath = pathname;
@@ -121,14 +146,7 @@ function createAnalytics(config) {
121
146
  window.addEventListener("load", trackCurrentPath, { once: true });
122
147
  }
123
148
  }
124
- const isProduction = (() => {
125
- try {
126
- const hostname = location.hostname;
127
- return hostname !== "localhost" && hostname !== "127.0.0.1" && !hostname.endsWith(".local");
128
- } catch {
129
- return true;
130
- }
131
- })();
149
+ const isProduction = !isLocalHost;
132
150
  const warnedReasons = /* @__PURE__ */ new Set();
133
151
  function devWarn(name, reason) {
134
152
  if (isProduction) return;
@@ -188,6 +206,10 @@ function createAnalytics(config) {
188
206
  return;
189
207
  }
190
208
  }
209
+ if (sendSuppressed) {
210
+ devSuppressNotice();
211
+ return;
212
+ }
191
213
  const body = JSON.stringify({
192
214
  publishableKey: config.publishableKey,
193
215
  pathname: location.pathname,
@@ -232,6 +254,7 @@ function resolveAnalyticsPublishableKey(explicit, env = readAnalyticsRuntimeEnv(
232
254
  function Analytics({
233
255
  autoTrack,
234
256
  endpoint,
257
+ mode,
235
258
  publishableKey,
236
259
  respectDnt
237
260
  }) {
@@ -241,11 +264,12 @@ function Analytics({
241
264
  const analytics = createAnalytics({
242
265
  autoTrack,
243
266
  endpoint,
267
+ mode,
244
268
  publishableKey: resolvedPublishableKey,
245
269
  respectDnt
246
270
  });
247
271
  return () => analytics.destroy();
248
- }, [autoTrack, endpoint, publishableKey, respectDnt]);
272
+ }, [autoTrack, endpoint, mode, publishableKey, respectDnt]);
249
273
  return null;
250
274
  }
251
275
  var react_default = Analytics;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/analytics/react.tsx","../../src/core/client/types.ts","../../src/analytics.ts","../../src/analytics/env.ts"],"sourcesContent":["'use client'\n\nimport { useEffect } from 'react'\nimport { createAnalytics, type AnalyticsConfig } from '../analytics'\nimport { resolveAnalyticsPublishableKey } from './env'\n\nexport type AnalyticsProps = Omit<AnalyticsConfig, 'publishableKey'> & {\n publishableKey?: string\n}\n\nexport function Analytics({\n autoTrack,\n endpoint,\n publishableKey,\n respectDnt,\n}: AnalyticsProps) {\n useEffect(() => {\n const resolvedPublishableKey =\n resolveAnalyticsPublishableKey(publishableKey)\n if (!resolvedPublishableKey) return\n\n const analytics = createAnalytics({\n autoTrack,\n endpoint,\n publishableKey: resolvedPublishableKey,\n respectDnt,\n })\n\n return () => analytics.destroy()\n }, [autoTrack, endpoint, publishableKey, respectDnt])\n\n return null\n}\n\nexport default Analytics\n","import type {\n Collection,\n PublicCollection,\n ServerCollection,\n ServerOnlyCollection,\n} from '../collection/const'\nimport type { CollectionType } from '../collection/types'\nimport type { CommunityClient } from '../community/community-client'\nimport type {\n BanCustomerParams,\n CommunityBan,\n UnbanCustomerParams,\n} from '../community/moderation-api'\nimport type { CommerceClient } from '../commerce/commerce-client'\nimport type { ServerCommerceClient } from '../commerce/server-commerce-client'\nimport type { CustomerNamespace } from '../customer/customer-namespace'\nimport type { EventsClient } from '../events/events-client'\nimport type { TenantIntrospectionClient } from '../api/tenant-introspection-api'\n\nexport type {\n Collection,\n PublicCollection,\n ServerCollection,\n ServerOnlyCollection,\n}\n\n// ============================================================================\n// API URL Configuration\n// ============================================================================\n\ndeclare const __DEFAULT_API_URL__: string\n\nexport function resolveApiUrl(apiUrl?: string): string {\n if (apiUrl) {\n return apiUrl.replace(/\\/$/, '')\n }\n\n if (typeof process !== 'undefined' && process.env) {\n const envUrl =\n process.env.SOFTWARE_API_URL || process.env.NEXT_PUBLIC_SOFTWARE_API_URL\n if (envUrl) {\n return envUrl.replace(/\\/$/, '')\n }\n }\n return __DEFAULT_API_URL__\n}\n\n// ============================================================================\n// Client Configuration\n// ============================================================================\n\nexport interface ClientConfig {\n publishableKey: string\n /** API base URL for staging, self-hosted, preview, or proxy deployments. */\n apiUrl?: string\n /**\n * Customer authentication options.\n * Used to initialize CustomerAuth on Client.\n */\n customer?: {\n /**\n * Persist token in localStorage. Defaults to `true`.\n * - `true` (default): uses key `'customer-token'`\n * - `string`: uses the given string as localStorage key\n * - `false`: disables persistence (token/onTokenChange used instead)\n *\n * Handles SSR safely (no-op on server).\n * When enabled, `token` and `onTokenChange` are ignored.\n */\n persist?: boolean | string\n /** Initial token (e.g. from SSR cookie) */\n token?: string\n /** Called when token changes (login/logout) — use to persist in localStorage/cookie */\n onTokenChange?: (token: string | null) => void\n }\n}\n\n// Server client: requires both publishableKey (for CDN routing + rate limit +\n// monthly quota enforcement via the edge proxy) and secretKey (sk01_ opaque\n// bearer token, the authentication credential).\n// The proxy keys its tenant lookup off `X-Publishable-Key`, so omitting\n// publishableKey would silently bypass rate limiting and plan-based quota\n// enforcement.\nexport interface ClientServerConfig extends ClientConfig {\n secretKey: string\n}\n\nexport interface ClientMetadata {\n userAgent?: string\n timestamp: number\n}\n\nexport interface ClientState {\n metadata: ClientMetadata\n}\n\nexport interface PaginationMeta {\n page: number\n limit: number\n totalDocs: number\n totalPages: number\n hasNextPage: boolean\n hasPrevPage: boolean\n pagingCounter: number\n prevPage: number | null\n nextPage: number | null\n}\n\n// ============================================================================\n// Payload CMS Native Response Types\n// ============================================================================\n\n/**\n * Payload CMS Find (List) Response\n * GET /api/{collection}\n */\nexport interface PayloadFindResponse<T = unknown> {\n docs: T[]\n totalDocs: number\n limit: number\n totalPages: number\n page: number\n pagingCounter: number\n hasPrevPage: boolean\n hasNextPage: boolean\n prevPage: number | null\n nextPage: number | null\n}\n\n/**\n * Payload CMS Create/Update Response\n * POST /api/{collection}\n * PATCH /api/{collection}/{id}\n */\nexport interface PayloadMutationResponse<T = unknown> {\n message: string\n doc: T\n errors?: unknown[]\n}\n\n// ============================================================================\n// Query Options\n// ============================================================================\n\nexport type Sort = string | string[]\nexport type Where = Record<string, unknown>\n\n/**\n * Do NOT replace with `Pick<FindOptions>` from `payload` or import Payload\n * types here. Payload's generic query types depend on `PayloadTypes` module\n * augmentation; external SDK consumers who only use `createClient` should not\n * install Payload just to type REST query objects. Excluded vs native:\n * Local-API-only fields, `locale`/`fallbackLocale`.\n */\nexport interface ApiQueryOptions {\n page?: number\n limit?: number\n sort?: Sort\n /**\n * Filter documents. Id-based relation filters (`where: { product: { equals: id } }`) are the\n * most reliable pattern. Dotted-path relation filters (`where: { 'product.slug': { equals } }`)\n * are Payload-native but may silently return empty when access control restricts the related\n * document or when the relation is polymorphic. String shorthand (`where: { slug: 'x' }`)\n * silently matches nothing — always use `{ slug: { equals: 'x' } }`.\n */\n where?: Where\n /**\n * Controls how deeply relationship fields are populated. This is the primary control for\n * populating relationships like `category`, `images`, `brand`. The configured Payload default\n * applies when unset.\n */\n depth?: number\n select?: Record<string, boolean>\n /**\n * Controls which fields are returned for already-populated relationships, keyed by collection\n * slug. Does NOT control which relationships to populate — that is `depth`.\n *\n * @example\n * // depth: 2 populates category; populate trims which fields come back\n * populate: { categories: { title: true, slug: true } }\n */\n populate?: Record<string, boolean | Record<string, boolean>>\n /**\n * Controls Payload `type: 'join'` virtual reverse-relation fields only (pagination, sort,\n * filter, count per join field, or `false` to disable all join-field population).\n *\n * Does NOT populate normal relationship fields like `category`, `images`, or `brand`.\n * For normal relationship population use `depth` (and optionally `populate` for field\n * selection).\n *\n * Pass `joins: false` to disable all join-field population — useful for lightweight list\n * queries where join fields are not needed.\n *\n * @example\n * // `article-authors` has a `type: 'join'` field `articles` (reverse-relation)\n * joins: { articles: { limit: 10, sort: '-publishedAt' } }\n *\n * // depth: 2 populates product.category — joins has no effect on this\n * depth: 2\n *\n * // Disable all join-field population\n * joins: false\n */\n joins?:\n | Record<\n string,\n | {\n limit?: number\n page?: number\n sort?: string\n where?: Where\n count?: boolean\n }\n | false\n >\n | false\n /** Set to `false` to skip the count query — returns docs without totalDocs/totalPages */\n pagination?: boolean\n /** Include draft versions (access control still applies on the server) */\n draft?: boolean\n /** Include soft-deleted documents (requires `trash` enabled on the collection) */\n trash?: boolean\n}\n\n// ============================================================================\n// Debug & Retry Configuration\n// ============================================================================\n\nexport interface DebugConfig {\n logRequests?: boolean\n logResponses?: boolean\n logErrors?: boolean\n}\n\nexport interface RetryConfig {\n maxRetries?: number\n retryableStatuses?: number[]\n retryDelay?: (attempt: number) => number\n}\n\n// ============================================================================\n// Lightweight root entry contracts\n// ============================================================================\n\ninterface RootQueryLookup<T extends string> {\n find(\n options?: ApiQueryOptions,\n ): Promise<PayloadFindResponse<CollectionType<T>>>\n findById(\n id: string | number,\n options?: ApiQueryOptions,\n ): Promise<CollectionType<T>>\n count(options?: ApiQueryOptions): Promise<{ totalDocs: number }>\n}\n\nexport type RootReadOnlyQueryBuilder<T extends PublicCollection> =\n RootQueryLookup<T>\n\nexport interface RootServerQueryBuilder<\n T extends ServerCollection,\n> extends RootQueryLookup<T> {\n create(\n data: Partial<CollectionType<T>>,\n options?: { file?: File | Blob; filename?: string },\n ): Promise<PayloadMutationResponse<CollectionType<T>>>\n update(\n id: string,\n data: Partial<CollectionType<T>>,\n options?: { file?: File | Blob; filename?: string },\n ): Promise<PayloadMutationResponse<CollectionType<T>>>\n updateMany(\n where: ApiQueryOptions['where'],\n data: Partial<CollectionType<T>>,\n ): Promise<PayloadFindResponse<CollectionType<T>>>\n remove(id: string): Promise<CollectionType<T>>\n removeMany(\n where: ApiQueryOptions['where'],\n ): Promise<PayloadFindResponse<CollectionType<T>>>\n}\n\nexport interface RootCollectionClient {\n from<T extends PublicCollection>(collection: T): RootReadOnlyQueryBuilder<T>\n}\n\nexport interface RootServerCollectionClient {\n from<T extends ServerCollection>(collection: T): RootServerQueryBuilder<T>\n}\n\nexport interface RootClient {\n commerce: CommerceClient\n community: CommunityClient\n /** Set on {@link createClient} return values; optional for structural mocks. */\n events?: EventsClient\n customer: CustomerNamespace\n collections: RootCollectionClient\n lastRequestId: string | null\n getState(): ClientState\n getConfig(): ClientConfig\n}\n\nexport interface RootServerClient {\n commerce: ServerCommerceClient\n tenant: TenantIntrospectionClient\n /** Set on {@link createServerClient} return values; optional for structural mocks. */\n events?: EventsClient\n community: CommunityClient & {\n moderation: {\n banCustomer: (p: BanCustomerParams) => Promise<CommunityBan>\n unbanCustomer: (p: UnbanCustomerParams) => Promise<{ success: true }>\n }\n }\n collections: RootServerCollectionClient\n lastRequestId: string | null\n getState(): ClientState\n getConfig(): Omit<ClientServerConfig, 'secretKey'>\n}\n\nexport type RootClientWithEvents = RootClient & { events: EventsClient }\n\nexport type RootServerClientWithEvents = RootServerClient & { events: EventsClient }\n\n// ============================================================================\n// Type Utilities\n// ============================================================================\n\nexport type DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]\n}\n\nexport type ExtractArrayType<T> = T extends (infer U)[] ? U : never\n","/**\n * @01.software/sdk — Analytics Helper\n */\n\n/* ANALYTICS INVARIANTS START\n * @01.software/sdk — Analytics Helper\n *\n * ANALYTICS INVARIANTS\n * ====================\n * These invariants are the single source of truth for observable behavior.\n * They are mirrored verbatim in apps/console/src/app/api/analytics/script.js/route.ts.\n * Any change here MUST be reflected there, and vice versa.\n *\n * 1. DNT/GPC respect: when config.respectDnt !== false (default true) AND\n * (navigator.doNotTrack === '1' OR navigator.globalPrivacyControl === true),\n * all methods become no-ops. Zero network requests are made.\n *\n * 2. Prerender skip: when document.prerendering === true OR\n * document.visibilityState === 'prerender', pageview() sends zero requests.\n *\n * 3. 500ms same-path dedup: a pageview for the same pathname within 500ms of\n * the previous send is silently dropped. After 500ms the next call sends.\n *\n * 4. Transport: sendBeacon → fetch keepalive fallback.\n * Primary: navigator.sendBeacon(endpoint, new Blob([json], { type: 'text/plain' })).\n * Fallback (sendBeacon unavailable OR returns false):\n * fetch(endpoint, { method: 'POST', keepalive: true,\n * headers: { 'Content-Type': 'application/json' }, body: json }).catch(() => {})\n *\n * 5. Body-only publishableKey: publishableKey is always in the request body,\n * never in any HTTP header.\n *\n * 6. SSR no-op: when typeof window === 'undefined', createAnalytics() returns\n * a stub where all methods are no-ops. No side effects occur.\n *\n * 7. Error swallowing: all transport errors are caught and swallowed.\n * createAnalytics() and all returned methods never throw into the caller.\n *\n * 8. Client timestamp: every send carries eventTs (milliseconds since epoch)\n * captured with Date.now() immediately before transport. The collect\n * endpoint uses eventTs (a) to bucket the event into the client's\n * tenant-local day and (b) to enforce the late-arrival cutoff; events\n * submitted after the local-day-end grace window are dropped with\n * reason \"late\".\n * ANALYTICS INVARIANTS END */\n\nimport { resolveApiUrl } from './core/client/types'\n\n// ============================================================================\n// Public Types\n// ============================================================================\n\nexport interface AnalyticsConfig {\n publishableKey: string\n /** Override the collect endpoint URL. Defaults to {SDK_BASE_URL}/api/analytics/collect */\n endpoint?: string\n /** Auto-patch history.pushState/replaceState and listen to popstate. Default: true */\n autoTrack?: boolean\n /** Respect navigator.doNotTrack and navigator.globalPrivacyControl. Default: true */\n respectDnt?: boolean\n}\n\nexport interface Analytics {\n pageview(path?: string): void\n track(name: string, props?: Record<string, string | number | boolean>): void\n destroy(): void\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\nexport function createAnalytics(config: AnalyticsConfig): Analytics {\n // INVARIANT 6: SSR no-op\n if (typeof window === 'undefined') {\n return { pageview() {}, track() {}, destroy() {} }\n }\n\n const endpoint =\n config.endpoint ?? `${resolveApiUrl()}/api/analytics/collect`\n\n // INVARIANT 1: DNT/GPC check (evaluated once at init; stays as closure)\n const respectDnt = config.respectDnt !== false\n function isDntActive(): boolean {\n if (!respectDnt) return false\n const nav = navigator as Navigator & { globalPrivacyControl?: boolean }\n return nav.doNotTrack === '1' || nav.globalPrivacyControl === true\n }\n\n // INVARIANT 3: 500ms same-path dedup state\n let lastPath: string | null = null\n let lastAt = 0\n\n // autoTrack state — save originals for destroy()\n const autoTrack = config.autoTrack !== false\n const originalPushState = history.pushState\n const originalReplaceState = history.replaceState\n let destroyed = false\n\n // -------------------------------------------------------------------------\n // Core send logic\n // -------------------------------------------------------------------------\n\n // Generate a unique event ID (crypto.randomUUID when available, Date+Math.random fallback)\n function newEventId(): string {\n return typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'\n ? crypto.randomUUID()\n : String(Date.now()) + String(Math.random())\n }\n\n // INVARIANT 4: sendBeacon → fetch keepalive fallback\n // INVARIANT 5: publishableKey in body only\n function sendBeaconOrFetch(body: string): void {\n try {\n if (typeof navigator.sendBeacon === 'function') {\n const blob = new Blob([body], { type: 'text/plain' })\n const sent = navigator.sendBeacon(endpoint, blob)\n if (sent) return\n // sent === false → fall through to fetch\n }\n // Fetch fallback\n fetch(endpoint, {\n method: 'POST',\n keepalive: true,\n headers: { 'Content-Type': 'application/json' },\n body,\n }).catch(() => {})\n } catch {\n // INVARIANT 7: swallow all errors\n }\n }\n\n function sendPageview(pathname: string): void {\n // INVARIANT 1: DNT/GPC\n if (isDntActive()) return\n\n // INVARIANT 2: prerender skip\n const doc = document as Document & { prerendering?: boolean }\n // visibilityState cast to string to accommodate non-standard 'prerender' value\n if (doc.prerendering === true || (document.visibilityState as string) === 'prerender') return\n\n // INVARIANT 3: 500ms same-path dedup\n const now = Date.now()\n if (pathname === lastPath && now - lastAt < 500) return\n lastPath = pathname\n lastAt = now\n\n const body = JSON.stringify({\n publishableKey: config.publishableKey,\n pathname,\n referrer: document.referrer || '',\n eventId: newEventId(),\n eventTs: Date.now(),\n })\n\n sendBeaconOrFetch(body)\n }\n\n // -------------------------------------------------------------------------\n // autoTrack: patch history methods + listen to popstate\n // -------------------------------------------------------------------------\n function trackCurrentPath(): void {\n if (destroyed) return\n sendPageview(location.pathname)\n }\n\n function patchedPushState(\n this: History,\n data: unknown,\n unused: string,\n url?: string | URL | null,\n ): void {\n originalPushState.apply(this, [data, unused, url] as Parameters<typeof history.pushState>)\n if (!destroyed) setTimeout(trackCurrentPath, 0)\n }\n\n function patchedReplaceState(\n this: History,\n data: unknown,\n unused: string,\n url?: string | URL | null,\n ): void {\n originalReplaceState.apply(this, [data, unused, url] as Parameters<typeof history.replaceState>)\n if (!destroyed) setTimeout(trackCurrentPath, 0)\n }\n\n if (autoTrack) {\n history.pushState = patchedPushState\n history.replaceState = patchedReplaceState\n window.addEventListener('popstate', trackCurrentPath)\n\n // Initial pageview\n if (document.readyState === 'complete') {\n trackCurrentPath()\n } else {\n window.addEventListener('load', trackCurrentPath, { once: true })\n }\n }\n\n // -------------------------------------------------------------------------\n // track() — client-side validation + send\n // -------------------------------------------------------------------------\n\n // Dev-mode detection: warn in dev, silent in production.\n // process.env.NODE_ENV is unreliable in browser bundles (tsup does not replace it\n // by default). Instead we detect production at runtime via hostname heuristics.\n // SSR (window undefined) is caught at the top of createAnalytics and returns a\n // stub, so window is always defined here.\n const isProduction: boolean = (() => {\n try {\n const hostname = location.hostname\n return (\n hostname !== 'localhost' &&\n hostname !== '127.0.0.1' &&\n !hostname.endsWith('.local')\n )\n } catch {\n // hostname access failed (non-browser) — default to silent\n return true\n }\n })()\n\n // One-shot warn dedup per reason per page load (keyed by reason only)\n const warnedReasons = new Set<string>()\n\n function devWarn(name: string, reason: string): void {\n if (isProduction) return\n if (warnedReasons.has(reason)) return\n warnedReasons.add(reason)\n console.warn(`[01 analytics] dropped event ${name}: ${reason}`)\n }\n\n const EVENT_NAME_RE = /^[a-zA-Z][a-zA-Z0-9_:-]{0,49}$/\n const RESERVED_PREFIXES = ['__', '_pv_']\n\n function validateEventName(name: string): string | null {\n if (!name || typeof name !== 'string') return 'name-empty'\n for (const prefix of RESERVED_PREFIXES) {\n if (name.startsWith(prefix)) return 'name-reserved'\n }\n if (!EVENT_NAME_RE.test(name)) return 'name-regex'\n return null\n }\n\n const PROP_KEY_RE = /^[a-zA-Z_][a-zA-Z0-9_]{0,31}$/\n\n function validateEventProps(\n props: Record<string, string | number | boolean> | undefined,\n ): string | null {\n if (props === undefined || props === null) return null\n if (typeof props !== 'object' || Array.isArray(props)) return 'props-value-type'\n const keys = Object.keys(props)\n if (keys.length > 10) return 'props-too-many-keys'\n for (const k of keys) {\n const v = props[k]\n if (!PROP_KEY_RE.test(k)) return 'props-key-regex'\n if (typeof v === 'string') {\n if (v.length > 80) return 'props-value-too-long'\n } else if (typeof v === 'number') {\n if (!isFinite(v)) return 'props-value-not-finite'\n } else if (typeof v === 'boolean') {\n // ok\n } else {\n return 'props-value-type'\n }\n }\n return null\n }\n\n // -------------------------------------------------------------------------\n // Public API\n // -------------------------------------------------------------------------\n return {\n pageview(path?: string): void {\n if (destroyed) return\n sendPageview(path ?? location.pathname)\n },\n\n track(name: string, props?: Record<string, string | number | boolean>): void {\n if (destroyed) return\n\n // INVARIANT 1: DNT/GPC (same as pageview)\n if (isDntActive()) return\n\n // INVARIANT 2: prerender skip\n const doc = document as Document & { prerendering?: boolean }\n if (doc.prerendering === true || (document.visibilityState as string) === 'prerender') return\n\n // Client-side validation\n const nameErr = validateEventName(name)\n if (nameErr) {\n devWarn(name, nameErr)\n return\n }\n\n if (props !== undefined) {\n const propsErr = validateEventProps(props)\n if (propsErr) {\n devWarn(name, propsErr)\n return\n }\n }\n\n // Build body — no dedup for track() events\n const body = JSON.stringify({\n publishableKey: config.publishableKey,\n pathname: location.pathname,\n referrer: document.referrer || '',\n eventId: newEventId(),\n eventName: name,\n eventProps: props,\n eventTs: Date.now(),\n })\n\n sendBeaconOrFetch(body)\n },\n\n destroy(): void {\n if (destroyed) return\n destroyed = true\n\n if (autoTrack) {\n // Restore original history methods\n history.pushState = originalPushState\n history.replaceState = originalReplaceState\n window.removeEventListener('popstate', trackCurrentPath)\n }\n\n // Null out dedup state\n lastPath = null\n lastAt = 0\n },\n }\n}\n","export type AnalyticsRuntimeEnv = {\n nextPublicKey?: string\n vitePublicKey?: string\n}\n\nexport function readAnalyticsRuntimeEnv(): AnalyticsRuntimeEnv {\n const nextPublicKey =\n typeof process !== 'undefined'\n ? process.env?.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY\n : undefined\n\n const viteEnv = (\n import.meta as ImportMeta & {\n env?: Record<string, string | undefined>\n }\n ).env\n\n return {\n nextPublicKey,\n vitePublicKey: viteEnv?.VITE_SOFTWARE_PUBLISHABLE_KEY,\n }\n}\n\nexport function resolveAnalyticsPublishableKey(\n explicit?: string,\n env: AnalyticsRuntimeEnv = readAnalyticsRuntimeEnv(),\n): string | undefined {\n if (explicit !== undefined) return explicit || undefined\n\n return env.nextPublicKey || env.vitePublicKey\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/analytics/react.tsx","../../src/core/client/types.ts","../../src/analytics.ts","../../src/analytics/env.ts"],"sourcesContent":["'use client'\n\nimport { useEffect } from 'react'\nimport { createAnalytics, type AnalyticsConfig } from '../analytics'\nimport { resolveAnalyticsPublishableKey } from './env'\n\nexport type AnalyticsProps = Omit<AnalyticsConfig, 'publishableKey'> & {\n publishableKey?: string\n}\n\nexport function Analytics({\n autoTrack,\n endpoint,\n mode,\n publishableKey,\n respectDnt,\n}: AnalyticsProps) {\n useEffect(() => {\n const resolvedPublishableKey =\n resolveAnalyticsPublishableKey(publishableKey)\n if (!resolvedPublishableKey) return\n\n const analytics = createAnalytics({\n autoTrack,\n endpoint,\n mode,\n publishableKey: resolvedPublishableKey,\n respectDnt,\n })\n\n return () => analytics.destroy()\n }, [autoTrack, endpoint, mode, publishableKey, respectDnt])\n\n return null\n}\n\nexport default Analytics\n","import type {\n Collection,\n PublicCollection,\n ServerCollection,\n ServerOnlyCollection,\n} from '../collection/const'\nimport type { CollectionType } from '../collection/types'\nimport type { CommunityClient } from '../community/community-client'\nimport type {\n BanCustomerParams,\n CommunityBan,\n UnbanCustomerParams,\n} from '../community/moderation-api'\nimport type { CommerceClient } from '../commerce/commerce-client'\nimport type { ServerCommerceClient } from '../commerce/server-commerce-client'\nimport type { CustomerNamespace } from '../customer/customer-namespace'\nimport type { EventsClient } from '../events/events-client'\nimport type { TenantIntrospectionClient } from '../api/tenant-introspection-api'\n\nexport type {\n Collection,\n PublicCollection,\n ServerCollection,\n ServerOnlyCollection,\n}\n\n// ============================================================================\n// API URL Configuration\n// ============================================================================\n\ndeclare const __DEFAULT_API_URL__: string\n\nexport function resolveApiUrl(apiUrl?: string): string {\n if (apiUrl) {\n return apiUrl.replace(/\\/$/, '')\n }\n\n if (typeof process !== 'undefined' && process.env) {\n const envUrl =\n process.env.SOFTWARE_API_URL || process.env.NEXT_PUBLIC_SOFTWARE_API_URL\n if (envUrl) {\n return envUrl.replace(/\\/$/, '')\n }\n }\n return __DEFAULT_API_URL__\n}\n\n// ============================================================================\n// Client Configuration\n// ============================================================================\n\nexport interface ClientConfig {\n publishableKey: string\n /** API base URL for staging, self-hosted, preview, or proxy deployments. */\n apiUrl?: string\n /**\n * Customer authentication options.\n * Used to initialize CustomerAuth on Client.\n */\n customer?: {\n /**\n * Persist token in localStorage. Defaults to `true`.\n * - `true` (default): uses key `'customer-token'`\n * - `string`: uses the given string as localStorage key\n * - `false`: disables persistence (token/onTokenChange used instead)\n *\n * Handles SSR safely (no-op on server).\n * When enabled, `token` and `onTokenChange` are ignored.\n */\n persist?: boolean | string\n /** Initial token (e.g. from SSR cookie) */\n token?: string\n /** Called when token changes (login/logout) — use to persist in localStorage/cookie */\n onTokenChange?: (token: string | null) => void\n }\n}\n\n// Server client: requires both publishableKey (for CDN routing + rate limit +\n// monthly quota enforcement via the edge proxy) and secretKey (sk01_ opaque\n// bearer token, the authentication credential).\n// The proxy keys its tenant lookup off `X-Publishable-Key`, so omitting\n// publishableKey would silently bypass rate limiting and plan-based quota\n// enforcement.\nexport interface ClientServerConfig extends ClientConfig {\n secretKey: string\n}\n\nexport interface ClientMetadata {\n userAgent?: string\n timestamp: number\n}\n\nexport interface ClientState {\n metadata: ClientMetadata\n}\n\nexport interface PaginationMeta {\n page: number\n limit: number\n totalDocs: number\n totalPages: number\n hasNextPage: boolean\n hasPrevPage: boolean\n pagingCounter: number\n prevPage: number | null\n nextPage: number | null\n}\n\n// ============================================================================\n// Payload CMS Native Response Types\n// ============================================================================\n\n/**\n * Payload CMS Find (List) Response\n * GET /api/{collection}\n */\nexport interface PayloadFindResponse<T = unknown> {\n docs: T[]\n totalDocs: number\n limit: number\n totalPages: number\n page: number\n pagingCounter: number\n hasPrevPage: boolean\n hasNextPage: boolean\n prevPage: number | null\n nextPage: number | null\n}\n\n/**\n * Payload CMS Create/Update Response\n * POST /api/{collection}\n * PATCH /api/{collection}/{id}\n */\nexport interface PayloadMutationResponse<T = unknown> {\n message: string\n doc: T\n errors?: unknown[]\n}\n\n// ============================================================================\n// Query Options\n// ============================================================================\n\nexport type Sort = string | string[]\nexport type Where = Record<string, unknown>\n\n/**\n * Do NOT replace with `Pick<FindOptions>` from `payload` or import Payload\n * types here. Payload's generic query types depend on `PayloadTypes` module\n * augmentation; external SDK consumers who only use `createClient` should not\n * install Payload just to type REST query objects. Excluded vs native:\n * Local-API-only fields, `locale`/`fallbackLocale`.\n */\nexport interface ApiQueryOptions {\n page?: number\n limit?: number\n sort?: Sort\n /**\n * Filter documents. Id-based relation filters (`where: { product: { equals: id } }`) are the\n * most reliable pattern. Dotted-path relation filters (`where: { 'product.slug': { equals } }`)\n * are Payload-native but may silently return empty when access control restricts the related\n * document or when the relation is polymorphic. String shorthand (`where: { slug: 'x' }`)\n * silently matches nothing — always use `{ slug: { equals: 'x' } }`.\n */\n where?: Where\n /**\n * Controls how deeply relationship fields are populated. This is the primary control for\n * populating relationships like `category`, `images`, `brand`. The configured Payload default\n * applies when unset.\n */\n depth?: number\n select?: Record<string, boolean>\n /**\n * Controls which fields are returned for already-populated relationships, keyed by collection\n * slug. Does NOT control which relationships to populate — that is `depth`.\n *\n * @example\n * // depth: 2 populates category; populate trims which fields come back\n * populate: { categories: { title: true, slug: true } }\n */\n populate?: Record<string, boolean | Record<string, boolean>>\n /**\n * Controls Payload `type: 'join'` virtual reverse-relation fields only (pagination, sort,\n * filter, count per join field, or `false` to disable all join-field population).\n *\n * Does NOT populate normal relationship fields like `category`, `images`, or `brand`.\n * For normal relationship population use `depth` (and optionally `populate` for field\n * selection).\n *\n * Pass `joins: false` to disable all join-field population — useful for lightweight list\n * queries where join fields are not needed.\n *\n * @example\n * // `article-authors` has a `type: 'join'` field `articles` (reverse-relation)\n * joins: { articles: { limit: 10, sort: '-publishedAt' } }\n *\n * // depth: 2 populates product.category — joins has no effect on this\n * depth: 2\n *\n * // Disable all join-field population\n * joins: false\n */\n joins?:\n | Record<\n string,\n | {\n limit?: number\n page?: number\n sort?: string\n where?: Where\n count?: boolean\n }\n | false\n >\n | false\n /** Set to `false` to skip the count query — returns docs without totalDocs/totalPages */\n pagination?: boolean\n /** Include draft versions (access control still applies on the server) */\n draft?: boolean\n /** Include soft-deleted documents (requires `trash` enabled on the collection) */\n trash?: boolean\n}\n\n// ============================================================================\n// Debug & Retry Configuration\n// ============================================================================\n\nexport interface DebugConfig {\n logRequests?: boolean\n logResponses?: boolean\n logErrors?: boolean\n}\n\nexport interface RetryConfig {\n maxRetries?: number\n retryableStatuses?: number[]\n retryDelay?: (attempt: number) => number\n}\n\n// ============================================================================\n// Lightweight root entry contracts\n// ============================================================================\n\ninterface RootQueryLookup<T extends string> {\n find(\n options?: ApiQueryOptions,\n ): Promise<PayloadFindResponse<CollectionType<T>>>\n findById(\n id: string | number,\n options?: ApiQueryOptions,\n ): Promise<CollectionType<T>>\n count(options?: ApiQueryOptions): Promise<{ totalDocs: number }>\n}\n\nexport type RootReadOnlyQueryBuilder<T extends PublicCollection> =\n RootQueryLookup<T>\n\nexport interface RootServerQueryBuilder<\n T extends ServerCollection,\n> extends RootQueryLookup<T> {\n create(\n data: Partial<CollectionType<T>>,\n options?: { file?: File | Blob; filename?: string },\n ): Promise<PayloadMutationResponse<CollectionType<T>>>\n update(\n id: string,\n data: Partial<CollectionType<T>>,\n options?: { file?: File | Blob; filename?: string },\n ): Promise<PayloadMutationResponse<CollectionType<T>>>\n updateMany(\n where: ApiQueryOptions['where'],\n data: Partial<CollectionType<T>>,\n ): Promise<PayloadFindResponse<CollectionType<T>>>\n remove(id: string): Promise<CollectionType<T>>\n removeMany(\n where: ApiQueryOptions['where'],\n ): Promise<PayloadFindResponse<CollectionType<T>>>\n}\n\nexport interface RootCollectionClient {\n from<T extends PublicCollection>(collection: T): RootReadOnlyQueryBuilder<T>\n}\n\nexport interface RootServerCollectionClient {\n from<T extends ServerCollection>(collection: T): RootServerQueryBuilder<T>\n}\n\nexport interface RootClient {\n commerce: CommerceClient\n community: CommunityClient\n /** Set on {@link createClient} return values; optional for structural mocks. */\n events?: EventsClient\n customer: CustomerNamespace\n collections: RootCollectionClient\n lastRequestId: string | null\n getState(): ClientState\n getConfig(): ClientConfig\n}\n\nexport interface RootServerClient {\n commerce: ServerCommerceClient\n tenant: TenantIntrospectionClient\n /** Set on {@link createServerClient} return values; optional for structural mocks. */\n events?: EventsClient\n community: CommunityClient & {\n moderation: {\n banCustomer: (p: BanCustomerParams) => Promise<CommunityBan>\n unbanCustomer: (p: UnbanCustomerParams) => Promise<{ success: true }>\n }\n }\n collections: RootServerCollectionClient\n lastRequestId: string | null\n getState(): ClientState\n getConfig(): Omit<ClientServerConfig, 'secretKey'>\n}\n\nexport type RootClientWithEvents = RootClient & { events: EventsClient }\n\nexport type RootServerClientWithEvents = RootServerClient & { events: EventsClient }\n\n// ============================================================================\n// Type Utilities\n// ============================================================================\n\nexport type DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]\n}\n\nexport type ExtractArrayType<T> = T extends (infer U)[] ? U : never\n","/**\n * @01.software/sdk — Analytics Helper\n */\n\n/* ANALYTICS INVARIANTS START\n * @01.software/sdk — Analytics Helper\n *\n * ANALYTICS INVARIANTS\n * ====================\n * These invariants are the single source of truth for observable behavior.\n * They are mirrored verbatim in apps/console/src/app/api/analytics/script.js/route.ts.\n * Any change here MUST be reflected there, and vice versa.\n *\n * 1. DNT/GPC respect: when config.respectDnt !== false (default true) AND\n * (navigator.doNotTrack === '1' OR navigator.globalPrivacyControl === true),\n * all methods become no-ops. Zero network requests are made.\n *\n * 2. Prerender skip: when document.prerendering === true OR\n * document.visibilityState === 'prerender', pageview() sends zero requests.\n *\n * 3. 500ms same-path dedup: a pageview for the same pathname within 500ms of\n * the previous send is silently dropped. After 500ms the next call sends.\n *\n * 4. Transport: sendBeacon → fetch keepalive fallback.\n * Primary: navigator.sendBeacon(endpoint, new Blob([json], { type: 'text/plain' })).\n * Fallback (sendBeacon unavailable OR returns false):\n * fetch(endpoint, { method: 'POST', keepalive: true,\n * headers: { 'Content-Type': 'application/json' }, body: json }).catch(() => {})\n *\n * 5. Body-only publishableKey: publishableKey is always in the request body,\n * never in any HTTP header.\n *\n * 6. SSR no-op: when typeof window === 'undefined', createAnalytics() returns\n * a stub where all methods are no-ops. No side effects occur.\n *\n * 7. Error swallowing: all transport errors are caught and swallowed.\n * createAnalytics() and all returned methods never throw into the caller.\n *\n * 8. Client timestamp: every send carries eventTs (milliseconds since epoch)\n * captured with Date.now() immediately before transport. The collect\n * endpoint uses eventTs (a) to bucket the event into the client's\n * tenant-local day and (b) to enforce the late-arrival cutoff; events\n * submitted after the local-day-end grace window are dropped with\n * reason \"late\".\n *\n * 9. Dev / local-host send skip: in 'auto' mode (the default) all send paths\n * become no-ops when the page runs on a local host — location.hostname is\n * 'localhost', '127.0.0.1', or ends with '.local'. Validation and dev\n * warnings still run; only the network send is suppressed, and a one-time\n * console notice is emitted. SSR (6), DNT/GPC (1), and prerender (2) skips\n * are evaluated first. Overrides: the SDK accepts mode 'production' (always\n * send, even locally) and 'development' (never send, on any host); the\n * mirrored hosted snippet accepts captureOnLocalhost === true (via\n * window.__01_analytics__.captureOnLocalhost or the script's\n * data-capture-localhost attribute) to force sending on a local host.\n * ANALYTICS INVARIANTS END */\n\nimport { resolveApiUrl } from './core/client/types'\n\n// ============================================================================\n// Public Types\n// ============================================================================\n\nexport interface AnalyticsConfig {\n publishableKey: string\n /** Override the collect endpoint URL. Defaults to {SDK_BASE_URL}/api/analytics/collect */\n endpoint?: string\n /** Auto-patch history.pushState/replaceState and listen to popstate. Default: true */\n autoTrack?: boolean\n /** Respect navigator.doNotTrack and navigator.globalPrivacyControl. Default: true */\n respectDnt?: boolean\n /**\n * Send mode. 'auto' (default) skips all sends on local hosts\n * (localhost / 127.0.0.1 / *.local). 'production' always sends, even\n * locally. 'development' never sends, on any host. See INVARIANT 9.\n */\n mode?: 'auto' | 'production' | 'development'\n}\n\nexport interface Analytics {\n pageview(path?: string): void\n track(name: string, props?: Record<string, string | number | boolean>): void\n destroy(): void\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\nexport function createAnalytics(config: AnalyticsConfig): Analytics {\n // INVARIANT 6: SSR no-op\n if (typeof window === 'undefined') {\n return { pageview() {}, track() {}, destroy() {} }\n }\n\n const endpoint =\n config.endpoint ?? `${resolveApiUrl()}/api/analytics/collect`\n\n // INVARIANT 1: DNT/GPC check (evaluated once at init; stays as closure)\n const respectDnt = config.respectDnt !== false\n function isDntActive(): boolean {\n if (!respectDnt) return false\n const nav = navigator as Navigator & { globalPrivacyControl?: boolean }\n return nav.doNotTrack === '1' || nav.globalPrivacyControl === true\n }\n\n // INVARIANT 9: local-host detection drives both dev warnings and the\n // auto send-skip. Hostname is the one signal shared with the hosted snippet.\n const isLocalHost: boolean = (() => {\n try {\n const h = location.hostname\n return h === 'localhost' || h === '127.0.0.1' || h.endsWith('.local')\n } catch {\n return false\n }\n })()\n\n const mode = config.mode ?? 'auto'\n const sendSuppressed =\n mode === 'development' || (mode === 'auto' && isLocalHost)\n\n let suppressNoticeShown = false\n function devSuppressNotice(): void {\n if (suppressNoticeShown) return\n suppressNoticeShown = true\n try {\n console.info(\n `[01 analytics] events disabled on local host (mode: '${mode}'). ` +\n `Pass mode: 'production' to send while developing.`,\n )\n } catch {\n // INVARIANT 7: swallow\n }\n }\n\n // INVARIANT 3: 500ms same-path dedup state\n let lastPath: string | null = null\n let lastAt = 0\n\n // autoTrack state — save originals for destroy()\n const autoTrack = config.autoTrack !== false\n const originalPushState = history.pushState\n const originalReplaceState = history.replaceState\n let destroyed = false\n\n // -------------------------------------------------------------------------\n // Core send logic\n // -------------------------------------------------------------------------\n\n // Generate a unique event ID (crypto.randomUUID when available, Date+Math.random fallback)\n function newEventId(): string {\n return typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'\n ? crypto.randomUUID()\n : String(Date.now()) + String(Math.random())\n }\n\n // INVARIANT 4: sendBeacon → fetch keepalive fallback\n // INVARIANT 5: publishableKey in body only\n function sendBeaconOrFetch(body: string): void {\n try {\n if (typeof navigator.sendBeacon === 'function') {\n const blob = new Blob([body], { type: 'text/plain' })\n const sent = navigator.sendBeacon(endpoint, blob)\n if (sent) return\n // sent === false → fall through to fetch\n }\n // Fetch fallback\n fetch(endpoint, {\n method: 'POST',\n keepalive: true,\n headers: { 'Content-Type': 'application/json' },\n body,\n }).catch(() => {})\n } catch {\n // INVARIANT 7: swallow all errors\n }\n }\n\n function sendPageview(pathname: string): void {\n // INVARIANT 1: DNT/GPC\n if (isDntActive()) return\n\n // INVARIANT 2: prerender skip\n const doc = document as Document & { prerendering?: boolean }\n // visibilityState cast to string to accommodate non-standard 'prerender' value\n if (doc.prerendering === true || (document.visibilityState as string) === 'prerender') return\n\n // INVARIANT 9: skip sends on local hosts in auto mode\n if (sendSuppressed) {\n devSuppressNotice()\n return\n }\n\n // INVARIANT 3: 500ms same-path dedup\n const now = Date.now()\n if (pathname === lastPath && now - lastAt < 500) return\n lastPath = pathname\n lastAt = now\n\n const body = JSON.stringify({\n publishableKey: config.publishableKey,\n pathname,\n referrer: document.referrer || '',\n eventId: newEventId(),\n eventTs: Date.now(),\n })\n\n sendBeaconOrFetch(body)\n }\n\n // -------------------------------------------------------------------------\n // autoTrack: patch history methods + listen to popstate\n // -------------------------------------------------------------------------\n function trackCurrentPath(): void {\n if (destroyed) return\n sendPageview(location.pathname)\n }\n\n function patchedPushState(\n this: History,\n data: unknown,\n unused: string,\n url?: string | URL | null,\n ): void {\n originalPushState.apply(this, [data, unused, url] as Parameters<typeof history.pushState>)\n if (!destroyed) setTimeout(trackCurrentPath, 0)\n }\n\n function patchedReplaceState(\n this: History,\n data: unknown,\n unused: string,\n url?: string | URL | null,\n ): void {\n originalReplaceState.apply(this, [data, unused, url] as Parameters<typeof history.replaceState>)\n if (!destroyed) setTimeout(trackCurrentPath, 0)\n }\n\n if (autoTrack) {\n history.pushState = patchedPushState\n history.replaceState = patchedReplaceState\n window.addEventListener('popstate', trackCurrentPath)\n\n // Initial pageview\n if (document.readyState === 'complete') {\n trackCurrentPath()\n } else {\n window.addEventListener('load', trackCurrentPath, { once: true })\n }\n }\n\n // -------------------------------------------------------------------------\n // track() — client-side validation + send\n // -------------------------------------------------------------------------\n\n // Dev-mode detection for warnings: warn in dev, silent in production.\n // Reuses the same local-host signal as the send-skip (INVARIANT 9).\n const isProduction = !isLocalHost\n\n // One-shot warn dedup per reason per page load (keyed by reason only)\n const warnedReasons = new Set<string>()\n\n function devWarn(name: string, reason: string): void {\n if (isProduction) return\n if (warnedReasons.has(reason)) return\n warnedReasons.add(reason)\n console.warn(`[01 analytics] dropped event ${name}: ${reason}`)\n }\n\n const EVENT_NAME_RE = /^[a-zA-Z][a-zA-Z0-9_:-]{0,49}$/\n const RESERVED_PREFIXES = ['__', '_pv_']\n\n function validateEventName(name: string): string | null {\n if (!name || typeof name !== 'string') return 'name-empty'\n for (const prefix of RESERVED_PREFIXES) {\n if (name.startsWith(prefix)) return 'name-reserved'\n }\n if (!EVENT_NAME_RE.test(name)) return 'name-regex'\n return null\n }\n\n const PROP_KEY_RE = /^[a-zA-Z_][a-zA-Z0-9_]{0,31}$/\n\n function validateEventProps(\n props: Record<string, string | number | boolean> | undefined,\n ): string | null {\n if (props === undefined || props === null) return null\n if (typeof props !== 'object' || Array.isArray(props)) return 'props-value-type'\n const keys = Object.keys(props)\n if (keys.length > 10) return 'props-too-many-keys'\n for (const k of keys) {\n const v = props[k]\n if (!PROP_KEY_RE.test(k)) return 'props-key-regex'\n if (typeof v === 'string') {\n if (v.length > 80) return 'props-value-too-long'\n } else if (typeof v === 'number') {\n if (!isFinite(v)) return 'props-value-not-finite'\n } else if (typeof v === 'boolean') {\n // ok\n } else {\n return 'props-value-type'\n }\n }\n return null\n }\n\n // -------------------------------------------------------------------------\n // Public API\n // -------------------------------------------------------------------------\n return {\n pageview(path?: string): void {\n if (destroyed) return\n sendPageview(path ?? location.pathname)\n },\n\n track(name: string, props?: Record<string, string | number | boolean>): void {\n if (destroyed) return\n\n // INVARIANT 1: DNT/GPC (same as pageview)\n if (isDntActive()) return\n\n // INVARIANT 2: prerender skip\n const doc = document as Document & { prerendering?: boolean }\n if (doc.prerendering === true || (document.visibilityState as string) === 'prerender') return\n\n // Client-side validation\n const nameErr = validateEventName(name)\n if (nameErr) {\n devWarn(name, nameErr)\n return\n }\n\n if (props !== undefined) {\n const propsErr = validateEventProps(props)\n if (propsErr) {\n devWarn(name, propsErr)\n return\n }\n }\n\n // INVARIANT 9: skip sends on local hosts in auto mode (validation above\n // still runs so dev warnings fire)\n if (sendSuppressed) {\n devSuppressNotice()\n return\n }\n\n // Build body — no dedup for track() events\n const body = JSON.stringify({\n publishableKey: config.publishableKey,\n pathname: location.pathname,\n referrer: document.referrer || '',\n eventId: newEventId(),\n eventName: name,\n eventProps: props,\n eventTs: Date.now(),\n })\n\n sendBeaconOrFetch(body)\n },\n\n destroy(): void {\n if (destroyed) return\n destroyed = true\n\n if (autoTrack) {\n // Restore original history methods\n history.pushState = originalPushState\n history.replaceState = originalReplaceState\n window.removeEventListener('popstate', trackCurrentPath)\n }\n\n // Null out dedup state\n lastPath = null\n lastAt = 0\n },\n }\n}\n","export type AnalyticsRuntimeEnv = {\n nextPublicKey?: string\n vitePublicKey?: string\n}\n\nexport function readAnalyticsRuntimeEnv(): AnalyticsRuntimeEnv {\n const nextPublicKey =\n typeof process !== 'undefined'\n ? process.env?.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY\n : undefined\n\n const viteEnv = (\n import.meta as ImportMeta & {\n env?: Record<string, string | undefined>\n }\n ).env\n\n return {\n nextPublicKey,\n vitePublicKey: viteEnv?.VITE_SOFTWARE_PUBLISHABLE_KEY,\n }\n}\n\nexport function resolveAnalyticsPublishableKey(\n explicit?: string,\n env: AnalyticsRuntimeEnv = readAnalyticsRuntimeEnv(),\n): string | undefined {\n if (explicit !== undefined) return explicit || undefined\n\n return env.nextPublicKey || env.vitePublicKey\n}\n"],"mappings":"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","names":[]}
@@ -3,6 +3,6 @@ import { AnalyticsConfig } from '../analytics.cjs';
3
3
  type AnalyticsProps = Omit<AnalyticsConfig, 'publishableKey'> & {
4
4
  publishableKey?: string;
5
5
  };
6
- declare function Analytics({ autoTrack, endpoint, publishableKey, respectDnt, }: AnalyticsProps): null;
6
+ declare function Analytics({ autoTrack, endpoint, mode, publishableKey, respectDnt, }: AnalyticsProps): null;
7
7
 
8
8
  export { Analytics, type AnalyticsProps, Analytics as default };