@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.
- package/README.md +142 -55
- package/dist/analytics/react.cjs +33 -9
- package/dist/analytics/react.cjs.map +1 -1
- package/dist/analytics/react.d.cts +1 -1
- package/dist/analytics/react.d.ts +1 -1
- package/dist/analytics/react.js +33 -9
- package/dist/analytics/react.js.map +1 -1
- package/dist/analytics.cjs +30 -8
- package/dist/analytics.cjs.map +1 -1
- package/dist/analytics.d.cts +6 -0
- package/dist/analytics.d.ts +6 -0
- package/dist/analytics.js +30 -8
- package/dist/analytics.js.map +1 -1
- package/dist/client.cjs +150 -207
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +6 -6
- package/dist/client.d.ts +6 -6
- package/dist/client.js +150 -207
- package/dist/client.js.map +1 -1
- package/dist/{collection-client-Cv0D2w1Q.d.cts → collection-client-B0J9wMNE.d.cts} +5 -5
- package/dist/{collection-client-Bq5Zd7p7.d.ts → collection-client-BroIWHY1.d.ts} +5 -5
- package/dist/const-6XHz_jej.d.ts +32 -0
- package/dist/const-B5KT72c7.d.cts +32 -0
- package/dist/{image-BDz2-AaO.d.cts → image-BDjHp03R.d.cts} +13 -9
- package/dist/{image-BDz2-AaO.d.ts → image-BDjHp03R.d.ts} +13 -9
- package/dist/{index-DTSXUYkr.d.ts → index-BOLQxveo.d.cts} +9 -6
- package/dist/{index-BHDKJ6B3.d.cts → index-CSwR2HSg.d.ts} +9 -6
- package/dist/index.cjs +256 -244
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -11
- package/dist/index.d.ts +12 -11
- package/dist/index.js +256 -244
- package/dist/index.js.map +1 -1
- package/dist/metadata.cjs +5 -3
- package/dist/metadata.cjs.map +1 -1
- package/dist/metadata.js +5 -3
- package/dist/metadata.js.map +1 -1
- package/dist/{payload-types-BCui2Oml.d.cts → payload-types-m3jjhxk9.d.cts} +669 -184
- package/dist/{payload-types-BCui2Oml.d.ts → payload-types-m3jjhxk9.d.ts} +669 -184
- package/dist/query.cjs +3 -1033
- package/dist/query.cjs.map +1 -1
- package/dist/query.d.cts +13 -13
- package/dist/query.d.ts +13 -13
- package/dist/query.js +3 -1033
- package/dist/query.js.map +1 -1
- package/dist/realtime.d.cts +2 -2
- package/dist/realtime.d.ts +2 -2
- package/dist/server.cjs +121 -85
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +7 -7
- package/dist/server.d.ts +7 -7
- package/dist/server.js +121 -85
- package/dist/server.js.map +1 -1
- package/dist/{types-Dib-zdK6.d.cts → types-CIGscmus.d.cts} +1471 -1100
- package/dist/{types-3qV6sY7T.d.ts → types-Cmrd1ezc.d.ts} +1 -15
- package/dist/{types-CEzLf3PX.d.cts → types-D0ubzQw0.d.cts} +1 -15
- package/dist/{types-DK9EnLwJ.d.ts → types-D2xYdz4P.d.ts} +1471 -1100
- package/dist/ui/canvas.cjs +15 -5
- package/dist/ui/canvas.cjs.map +1 -1
- package/dist/ui/canvas.d.cts +1 -1
- package/dist/ui/canvas.d.ts +1 -1
- package/dist/ui/canvas.js +15 -5
- package/dist/ui/canvas.js.map +1 -1
- package/dist/ui/form.d.cts +1 -1
- package/dist/ui/form.d.ts +1 -1
- package/dist/ui/image.cjs +15 -5
- package/dist/ui/image.cjs.map +1 -1
- package/dist/ui/image.d.cts +1 -1
- package/dist/ui/image.d.ts +1 -1
- package/dist/ui/image.js +15 -5
- package/dist/ui/image.js.map +1 -1
- package/dist/ui/video.d.cts +1 -1
- package/dist/ui/video.d.ts +1 -1
- package/dist/webhook.cjs +5 -1
- package/dist/webhook.cjs.map +1 -1
- package/dist/webhook.d.cts +4 -4
- package/dist/webhook.d.ts +4 -4
- package/dist/webhook.js +5 -1
- package/dist/webhook.js.map +1 -1
- package/package.json +3 -3
- package/dist/const-BDUKFP9w.d.ts +0 -32
- 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: {
|
|
144
|
-
|
|
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({
|
|
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(
|
|
346
|
-
|
|
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 {
|
|
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`.
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
551
|
+
await server.collections.from('products').find({
|
|
488
552
|
where: { slug: { equals } },
|
|
489
553
|
joins: {
|
|
490
|
-
variants: { limit: 50, sort: '
|
|
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:
|
|
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
|
|
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:
|
|
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.*`.
|
|
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.
|
|
860
|
-
await server.commerce.orders.
|
|
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({
|
|
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
|
|
917
|
-
|
|
|
918
|
-
| Create
|
|
919
|
-
| Edit graph
|
|
920
|
-
| Edit (legacy) | `product: { id }` + graph only
|
|
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 {
|
|
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`:
|
|
1227
|
+
Source of truth: `packages/sdk/src/core/collection/const.ts` (`COLLECTIONS`: 53).
|
|
1142
1228
|
|
|
1143
|
-
| Category |
|
|
1229
|
+
| Category | Browser-public generic collections |
|
|
1144
1230
|
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
1145
|
-
| Tenant | `tenants`, `tenant-metadata
|
|
1146
|
-
| Products | `products`, `product-variants`, `product-options`, `product-option-values`, `product-categories`, `product-tags`, `product-collections`, `brands
|
|
1147
|
-
|
|
|
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 | `
|
|
1156
|
-
|
|
|
1157
|
-
|
|
|
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
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
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
|
|
1301
|
-
|
|
|
1302
|
-
| Upsert option value
|
|
1303
|
-
| Listing groups
|
|
1304
|
-
| Product detail option value | `swatchColor`, `thumbnail`, `images`
|
|
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
|
|
package/dist/analytics/react.cjs
CHANGED
|
@@ -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 };
|