@doswiftly/storefront-sdk 20.2.0 → 21.0.1
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/CHANGELOG.md +38 -0
- package/README.md +776 -529
- package/dist/core/auth/handlers.d.ts +10 -9
- package/dist/core/auth/handlers.d.ts.map +1 -1
- package/dist/core/auth/handlers.js +10 -9
- package/dist/core/auth/session-events.d.ts +2 -2
- package/dist/core/auth/session-events.js +2 -2
- package/dist/core/cart/cart-client.d.ts +23 -24
- package/dist/core/cart/cart-client.d.ts.map +1 -1
- package/dist/core/cart/cart-client.js +24 -25
- package/dist/core/generated/operation-types.d.ts +102 -108
- package/dist/core/generated/operation-types.d.ts.map +1 -1
- package/dist/core/middleware/session-retry.d.ts +5 -6
- package/dist/core/middleware/session-retry.d.ts.map +1 -1
- package/dist/core/middleware/session-retry.js +7 -8
- package/dist/core/operations/auth.d.ts.map +1 -1
- package/dist/core/operations/auth.js +4 -0
- package/dist/core/operations/cart.d.ts +11 -10
- package/dist/core/operations/cart.d.ts.map +1 -1
- package/dist/core/operations/cart.js +14 -11
- package/dist/react/components/PaymentInstrumentSection.d.ts +24 -24
- package/dist/react/components/PaymentInstrumentSection.d.ts.map +1 -1
- package/dist/react/components/PaymentInstrumentSection.js +15 -15
- package/dist/react/components/PaymentInstrumentTile.d.ts +19 -20
- package/dist/react/components/PaymentInstrumentTile.d.ts.map +1 -1
- package/dist/react/components/PaymentInstrumentTile.js +15 -16
- package/dist/react/helpers/browser-data.d.ts +30 -33
- package/dist/react/helpers/browser-data.d.ts.map +1 -1
- package/dist/react/helpers/browser-data.js +26 -29
- package/dist/react/hooks/use-cart-manager.d.ts +1 -1
- package/dist/react/hooks/use-cart-manager.js +1 -1
- package/dist/react/hooks/use-cart.d.ts +2 -2
- package/dist/react/hooks/use-cart.js +3 -3
- package/dist/react/hooks/use-session-expired.d.ts +6 -5
- package/dist/react/hooks/use-session-expired.d.ts.map +1 -1
- package/dist/react/hooks/use-session-expired.js +6 -5
- package/dist/react/stores/auth.store.d.ts.map +1 -1
- package/dist/react/stores/auth.store.js +13 -10
- package/dist/react/stores/cart.store.d.ts +1 -1
- package/dist/react/stores/cart.store.js +1 -1
- package/package.json +1 -1
|
@@ -1,36 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `getBrowserDataForPayment()` — collects browser context required by PSD2/3DS2
|
|
3
|
-
* authentication flows (card-on-file
|
|
4
|
-
* Apple/Google Pay
|
|
3
|
+
* authentication flows (card-on-file with a 3DS challenge, BLIK confirmation,
|
|
4
|
+
* Apple/Google Pay with risk scoring).
|
|
5
5
|
*
|
|
6
|
-
* **Browser-only** — throws
|
|
7
|
-
* rendering).
|
|
8
|
-
* code path. NEVER call this
|
|
6
|
+
* **Browser-only** — throws when `typeof window === 'undefined'` (server-side
|
|
7
|
+
* rendering). Callers MUST gate the call inside a useEffect / event handler /
|
|
8
|
+
* browser-only code path. NEVER call this in a Server Component or Route Handler.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* Values come from standard Web APIs:
|
|
11
11
|
* - `userAgent` — `navigator.userAgent`.
|
|
12
12
|
* - `language` — `navigator.language` (BCP 47, fallback `en-US`).
|
|
13
13
|
* - `screen{Width,Height}` — `window.screen.{width,height}`.
|
|
14
14
|
* - `colorDepth` — `window.screen.colorDepth`.
|
|
15
15
|
* - `timezoneOffset` — `new Date().getTimezoneOffset()` (signed integer, minutes,
|
|
16
|
-
* reverse signed per ECMA:
|
|
17
|
-
* - `javaEnabled` — `navigator.javaEnabled?.()` (deprecated
|
|
18
|
-
* specification;
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
16
|
+
* reverse signed per ECMA: positive = behind UTC, negative = ahead of UTC).
|
|
17
|
+
* - `javaEnabled` — `navigator.javaEnabled?.()` (deprecated but required by the
|
|
18
|
+
* PSD2/EMV specification; falls back to `false` when the browser does not
|
|
19
|
+
* expose it).
|
|
20
|
+
* - `acceptHeader` — NOT available client-side (`navigator` does not expose
|
|
21
|
+
* request headers). Callers pass it from server context or omit it
|
|
22
|
+
* (gateway-dependent requirement). The helper returns undefined.
|
|
22
23
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* Mollie `cardToken` z 3DS lookup).
|
|
24
|
+
* The shape matches the PSD2/3DS2 BrowserData specification (EMVCo), so it can
|
|
25
|
+
* be merged into gateway-specific request bodies (gateway-specific field names
|
|
26
|
+
* vary).
|
|
27
27
|
*
|
|
28
28
|
* @example
|
|
29
29
|
* ```tsx
|
|
30
|
-
* //
|
|
30
|
+
* // In an event handler (browser-only):
|
|
31
31
|
* function handleCheckoutSubmit() {
|
|
32
32
|
* const browserData = getBrowserDataForPayment();
|
|
33
|
-
*
|
|
33
|
+
* // pass to your payment integration where required
|
|
34
34
|
* }
|
|
35
35
|
*
|
|
36
36
|
* // SSR-safe wrap:
|
|
@@ -39,15 +39,11 @@
|
|
|
39
39
|
* ...
|
|
40
40
|
* }
|
|
41
41
|
* ```
|
|
42
|
-
*
|
|
43
|
-
* Added by payment-instrument-preselection-advanced sub-sprint Adv-2 Req 9.5
|
|
44
|
-
* (carry-over z Adv-1 plan — moved here as standalone utility, no backend
|
|
45
|
-
* mutation impact yet — Adv-3 będzie consumer'em w `paymentCreate` input).
|
|
46
42
|
*/
|
|
47
43
|
/**
|
|
48
|
-
*
|
|
49
|
-
* Node
|
|
50
|
-
*
|
|
44
|
+
* Thrown when the helper is called in an SSR context (Server Component, Route
|
|
45
|
+
* Handler, Node without JSDOM). Callers MUST guard with a `typeof window` check
|
|
46
|
+
* or call it only inside an event handler / `useEffect`.
|
|
51
47
|
*/
|
|
52
48
|
export class BrowserDataNotAvailableError extends Error {
|
|
53
49
|
constructor() {
|
|
@@ -59,11 +55,12 @@ export function getBrowserDataForPayment() {
|
|
|
59
55
|
if (typeof window === 'undefined' || typeof navigator === 'undefined' || typeof window.screen === 'undefined') {
|
|
60
56
|
throw new BrowserDataNotAvailableError();
|
|
61
57
|
}
|
|
62
|
-
// `navigator.javaEnabled`
|
|
63
|
-
//
|
|
58
|
+
// `navigator.javaEnabled` is deprecated but still present in major browsers — guard
|
|
59
|
+
// against future removal. Falls back to `false` when missing (the PSD2 spec requires
|
|
60
|
+
// a boolean, not undefined).
|
|
64
61
|
const javaEnabled = typeof navigator.javaEnabled === 'function' ? Boolean(navigator.javaEnabled()) : false;
|
|
65
|
-
// `Intl.DateTimeFormat`
|
|
66
|
-
//
|
|
62
|
+
// `Intl.DateTimeFormat` is available in all modern browsers — optional capture for
|
|
63
|
+
// gateways that require the IANA name instead of the numeric offset.
|
|
67
64
|
let timezone;
|
|
68
65
|
try {
|
|
69
66
|
timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
* function CartUI() {
|
|
63
63
|
* const { addItem, onExpired } = useCartManager();
|
|
64
64
|
*
|
|
65
|
-
* useEffect(() => onExpired(() => toast('
|
|
65
|
+
* useEffect(() => onExpired(() => toast('Your cart expired — please add the items again')), [onExpired]);
|
|
66
66
|
*
|
|
67
67
|
* return <button onClick={() => addItem([{ variantId, quantity: 1 }])}>Add</button>;
|
|
68
68
|
* }
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
* function CartUI() {
|
|
63
63
|
* const { addItem, onExpired } = useCartManager();
|
|
64
64
|
*
|
|
65
|
-
* useEffect(() => onExpired(() => toast('
|
|
65
|
+
* useEffect(() => onExpired(() => toast('Your cart expired — please add the items again')), [onExpired]);
|
|
66
66
|
*
|
|
67
67
|
* return <button onClick={() => addItem([{ variantId, quantity: 1 }])}>Add</button>;
|
|
68
68
|
* }
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
* UIs need.
|
|
10
10
|
*
|
|
11
11
|
* State management follows the platform's store pattern: a vanilla Zustand
|
|
12
|
-
* store is created per hook mount via `useMemo` (component-scoped,
|
|
13
|
-
* module-level
|
|
12
|
+
* store is created per hook mount via `useMemo` (stores are component-scoped,
|
|
13
|
+
* never module-level singletons), and React subscribes through
|
|
14
14
|
* `useStore`. Changing `cartId` recreates the store; refetches are explicit
|
|
15
15
|
* (`refetch()` or any mutation triggers one).
|
|
16
16
|
*
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
* UIs need.
|
|
10
10
|
*
|
|
11
11
|
* State management follows the platform's store pattern: a vanilla Zustand
|
|
12
|
-
* store is created per hook mount via `useMemo` (component-scoped,
|
|
13
|
-
* module-level
|
|
12
|
+
* store is created per hook mount via `useMemo` (stores are component-scoped,
|
|
13
|
+
* never module-level singletons), and React subscribes through
|
|
14
14
|
* `useStore`. Changing `cartId` recreates the store; refetches are explicit
|
|
15
15
|
* (`refetch()` or any mutation triggers one).
|
|
16
16
|
*
|
|
@@ -84,7 +84,7 @@ export function useCart(cartId, options = {}) {
|
|
|
84
84
|
const { autoFetch = true, initialCart } = options;
|
|
85
85
|
// Recreate the store when `cartId` or the underlying client changes. The
|
|
86
86
|
// useMemo dependency list captures store identity (component-scoped, not
|
|
87
|
-
// module-level
|
|
87
|
+
// module-level). Mutations capture the store instance for stable
|
|
88
88
|
// references via the api object.
|
|
89
89
|
const api = useMemo(() => createServerCartStore({ cartClient, cartId, initialCart }),
|
|
90
90
|
// initialCart deliberately excluded — it only seeds the FIRST mount; we do
|
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
* useSessionExpired — subscribe to the global session-expired signal.
|
|
3
3
|
*
|
|
4
4
|
* Fired when the SDK can no longer keep the customer session alive: a proactive
|
|
5
|
-
* refresh failed on tab wake
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* refresh failed on tab wake, or a reactive refresh after a 401 also failed.
|
|
6
|
+
* Use once near the app root to react globally — show a notice and redirect
|
|
7
|
+
* to sign-in. No-op outside `StorefrontProvider`.
|
|
8
8
|
*/
|
|
9
9
|
import type { SessionExpiredEmitter, SessionExpiredEvent } from '../../core/auth/session-events';
|
|
10
10
|
/**
|
|
11
|
-
* Context carrying the provider-scoped session-expired emitter. Created
|
|
12
|
-
* `StorefrontProvider` via `useRef`
|
|
11
|
+
* Context carrying the provider-scoped session-expired emitter. Created per
|
|
12
|
+
* provider mount in `StorefrontProvider` via `useRef` — never a module-level
|
|
13
|
+
* singleton.
|
|
13
14
|
*/
|
|
14
15
|
export declare const SessionExpiredContext: import("react").Context<SessionExpiredEmitter | null>;
|
|
15
16
|
export declare function useSessionExpired(listener: (event: SessionExpiredEvent) => void): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-session-expired.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/use-session-expired.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAEjG
|
|
1
|
+
{"version":3,"file":"use-session-expired.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/use-session-expired.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAEjG;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,uDAAoD,CAAC;AAEvF,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,GAAG,IAAI,CAUtF"}
|
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
* useSessionExpired — subscribe to the global session-expired signal.
|
|
3
3
|
*
|
|
4
4
|
* Fired when the SDK can no longer keep the customer session alive: a proactive
|
|
5
|
-
* refresh failed on tab wake
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* refresh failed on tab wake, or a reactive refresh after a 401 also failed.
|
|
6
|
+
* Use once near the app root to react globally — show a notice and redirect
|
|
7
|
+
* to sign-in. No-op outside `StorefrontProvider`.
|
|
8
8
|
*/
|
|
9
9
|
'use client';
|
|
10
10
|
import { createContext, useContext, useEffect, useRef } from 'react';
|
|
11
11
|
/**
|
|
12
|
-
* Context carrying the provider-scoped session-expired emitter. Created
|
|
13
|
-
* `StorefrontProvider` via `useRef`
|
|
12
|
+
* Context carrying the provider-scoped session-expired emitter. Created per
|
|
13
|
+
* provider mount in `StorefrontProvider` via `useRef` — never a module-level
|
|
14
|
+
* singleton.
|
|
14
15
|
*/
|
|
15
16
|
export const SessionExpiredContext = createContext(null);
|
|
16
17
|
export function useSessionExpired(listener) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.store.d.ts","sourceRoot":"","sources":["../../../src/react/stores/auth.store.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,iBAAiB,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IAExB,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;;;OAKG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,eAAe,EAAE,OAAO,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;IAGnB,OAAO,EAAE,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACjG,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB;;;;OAIG;IACH,YAAY,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACjD,cAAc,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;IACzD,UAAU,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;CAC1C;AAED,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC;;;;;;;;OAQG;IACH,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,eAAO,MAAM,eAAe,GAAI,UAAU,sBAAsB;;;;;
|
|
1
|
+
{"version":3,"file":"auth.store.d.ts","sourceRoot":"","sources":["../../../src/react/stores/auth.store.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,iBAAiB,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IAExB,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;;;OAKG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,eAAe,EAAE,OAAO,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;IAGnB,OAAO,EAAE,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACjG,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB;;;;OAIG;IACH,YAAY,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACjD,cAAc,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;IACzD,UAAU,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;CAC1C;AAED,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC;;;;;;;;OAQG;IACH,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,eAAO,MAAM,eAAe,GAAI,UAAU,sBAAsB;;;;;sBA4ExC,YAAY,GAAG,IAAI;6BACZ,OAAO;;;;;;;;sBADd,YAAY,GAAG,IAAI;6BACZ,OAAO;;;CAUnC,CAAC"}
|
|
@@ -42,10 +42,11 @@ export const createAuthStore = (options) => createStore()(persist((set) => ({
|
|
|
42
42
|
setLoading: (isLoading) => set({ isLoading }),
|
|
43
43
|
}), {
|
|
44
44
|
name: AUTH_STORAGE_KEY,
|
|
45
|
-
version: 3, // v3 (
|
|
46
|
-
// persistence.
|
|
47
|
-
// Non-browser
|
|
48
|
-
//
|
|
45
|
+
version: 3, // v3 (XSS hardening): accessToken dropped from localStorage
|
|
46
|
+
// persistence. The token lives only in memory + the httpOnly cookie
|
|
47
|
+
// (browser auto-sent). Non-browser clients (mobile native, server-to-server)
|
|
48
|
+
// set the token explicitly via setAuth() — it is never persisted to the
|
|
49
|
+
// SDK's localStorage slice.
|
|
49
50
|
partialize: (state) => ({
|
|
50
51
|
customer: state.customer,
|
|
51
52
|
isAuthenticated: state.isAuthenticated,
|
|
@@ -63,18 +64,20 @@ export const createAuthStore = (options) => createStore()(persist((set) => ({
|
|
|
63
64
|
return {
|
|
64
65
|
...currentState,
|
|
65
66
|
customer: persisted.customer ?? currentState.customer,
|
|
66
|
-
// accessToken
|
|
67
|
-
// `...currentState`
|
|
68
|
-
//
|
|
67
|
+
// accessToken is never persisted to localStorage (XSS hardening) — the
|
|
68
|
+
// `...currentState` spread propagates the factory value: `null` (default)
|
|
69
|
+
// or the seed from `options.initialAccessToken` when the consumer provided
|
|
70
|
+
// a server-side token.
|
|
69
71
|
// Server cookie is the authority — never let stale localStorage override it.
|
|
70
72
|
isAuthenticated: currentState.isAuthenticated,
|
|
71
73
|
};
|
|
72
74
|
},
|
|
73
75
|
migrate: (persistedState, version) => {
|
|
74
76
|
if (version < 3) {
|
|
75
|
-
// v1→v2: Turbopack duplication cleanup; v2→v3: XSS
|
|
76
|
-
//
|
|
77
|
-
// cookie hydration (BFF /api/auth/whoami)
|
|
77
|
+
// v1→v2: Turbopack duplication cleanup; v2→v3: XSS hardening — accessToken
|
|
78
|
+
// removed from localStorage. After the migration the store starts fresh;
|
|
79
|
+
// the user re-authenticates via cookie hydration (BFF /api/auth/whoami)
|
|
80
|
+
// or the login flow.
|
|
78
81
|
return { customer: null, isAuthenticated: false };
|
|
79
82
|
}
|
|
80
83
|
return persistedState;
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
*
|
|
38
38
|
* const store = createCartStore({
|
|
39
39
|
* getActions: () => actions,
|
|
40
|
-
* onExpired: (e) => toast.error('
|
|
40
|
+
* onExpired: (e) => toast.error('Your cart expired — please add the items again'),
|
|
41
41
|
* });
|
|
42
42
|
* ```
|
|
43
43
|
*/
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
*
|
|
38
38
|
* const store = createCartStore({
|
|
39
39
|
* getActions: () => actions,
|
|
40
|
-
* onExpired: (e) => toast.error('
|
|
40
|
+
* onExpired: (e) => toast.error('Your cart expired — please add the items again'),
|
|
41
41
|
* });
|
|
42
42
|
* ```
|
|
43
43
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doswiftly/storefront-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "21.0.1",
|
|
4
4
|
"description": "Storefront runtime SDK for DoSwiftly Commerce — layered transport, middleware pipeline, React providers, Zustand stores, cache strategies. 0 runtime dependencies in core.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|