@wocart/storefront 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +2 -0
  4. package/README.md +143 -2
  5. package/USAGE.md +1465 -0
  6. package/dist/abandoned-checkout.d.ts +22 -0
  7. package/dist/abandoned-checkout.js +89 -0
  8. package/dist/actions.d.ts +117 -0
  9. package/dist/actions.js +196 -0
  10. package/dist/astro.d.ts +6 -0
  11. package/dist/astro.js +3 -0
  12. package/dist/basket.d.ts +87 -0
  13. package/dist/basket.js +258 -0
  14. package/dist/client/cart.d.ts +30 -0
  15. package/dist/client/cart.js +26 -0
  16. package/dist/client/categories.d.ts +25 -0
  17. package/dist/client/categories.js +37 -0
  18. package/dist/client/config.d.ts +29 -0
  19. package/dist/client/config.js +41 -0
  20. package/dist/client/core.d.ts +86 -0
  21. package/dist/client/core.js +582 -0
  22. package/dist/client/errors.d.ts +19 -0
  23. package/dist/client/errors.js +31 -0
  24. package/dist/client/geo.d.ts +31 -0
  25. package/dist/client/geo.js +39 -0
  26. package/dist/client/index.d.ts +39 -0
  27. package/dist/client/index.js +80 -0
  28. package/dist/client/orders.d.ts +68 -0
  29. package/dist/client/orders.js +76 -0
  30. package/dist/client/page-data.d.ts +31 -0
  31. package/dist/client/page-data.js +37 -0
  32. package/dist/client/pages.d.ts +33 -0
  33. package/dist/client/pages.js +44 -0
  34. package/dist/client/paths.d.ts +8 -0
  35. package/dist/client/paths.js +10 -0
  36. package/dist/client/products.d.ts +56 -0
  37. package/dist/client/products.js +78 -0
  38. package/dist/client/shipping.d.ts +37 -0
  39. package/dist/client/shipping.js +48 -0
  40. package/dist/data/communes.d.ts +25 -0
  41. package/dist/data/communes.js +1690 -0
  42. package/dist/data/wilayas.d.ts +6 -0
  43. package/dist/data/wilayas.js +64 -0
  44. package/dist/domain.d.ts +332 -0
  45. package/dist/domain.js +214 -0
  46. package/dist/endpoints/proxy.d.ts +111 -0
  47. package/dist/endpoints/proxy.js +161 -0
  48. package/dist/index.d.ts +42 -0
  49. package/dist/index.js +30 -0
  50. package/dist/locale-routing.d.ts +59 -0
  51. package/dist/locale-routing.js +62 -0
  52. package/dist/locales.d.ts +15 -0
  53. package/dist/locales.js +8 -0
  54. package/dist/meta-tracking.d.ts +34 -0
  55. package/dist/meta-tracking.js +72 -0
  56. package/dist/middleware.d.ts +100 -0
  57. package/dist/middleware.js +72 -0
  58. package/dist/offer-view.d.ts +10 -0
  59. package/dist/offer-view.js +13 -0
  60. package/dist/otp.d.ts +80 -0
  61. package/dist/otp.js +295 -0
  62. package/dist/phone.d.ts +2 -0
  63. package/dist/phone.js +34 -0
  64. package/dist/product-view.d.ts +12 -0
  65. package/dist/product-view.js +42 -0
  66. package/dist/routes.d.ts +45 -0
  67. package/dist/routes.js +59 -0
  68. package/dist/schemas.d.ts +753 -0
  69. package/dist/schemas.js +377 -0
  70. package/dist/server.d.ts +4 -0
  71. package/dist/server.js +2 -0
  72. package/dist/types.d.ts +418 -0
  73. package/dist/types.js +1 -0
  74. package/dist/utils/image.d.ts +140 -0
  75. package/dist/utils/image.js +131 -0
  76. package/dist/utils/money.d.ts +39 -0
  77. package/dist/utils/money.js +43 -0
  78. package/dist/utils/product-image.d.ts +37 -0
  79. package/dist/utils/product-image.js +38 -0
  80. package/dist/whatsapp.d.ts +55 -0
  81. package/dist/whatsapp.js +89 -0
  82. package/package.json +76 -4
package/dist/basket.js ADDED
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Browser-side shopping cart / basket engine.
3
+ *
4
+ * Deep module: concentrates state, persistence, event notification,
5
+ * cross-tab synchronization, duplicate merging, and bounds enforcement behind
6
+ * a minimal, testable interface.
7
+ *
8
+ * The basket is intent, not trusted money. It lives in the client environment
9
+ * (or memory fake in server/tests). The server always validates and re-prices
10
+ * every line at checkout.
11
+ */
12
+ import { MAX_BASKET_LINES, MAX_BASKET_LINE_QUANTITY, basketLineKey, } from "./domain.js";
13
+ /** Default key used in browser storage, versioned to prevent schema drift crashes. */
14
+ export const DEFAULT_BASKET_STORAGE_KEY = "cod_cart_v1";
15
+ /**
16
+ * Create a memory storage adapter (ideal for node, tests, and SSR).
17
+ */
18
+ export function createMemoryStorageAdapter() {
19
+ const store = new Map();
20
+ return {
21
+ getItem: (key) => store.get(key) ?? null,
22
+ setItem: (key, value) => {
23
+ store.set(key, value);
24
+ },
25
+ removeItem: (key) => {
26
+ store.delete(key);
27
+ },
28
+ isAvailable: () => true,
29
+ };
30
+ }
31
+ /**
32
+ * Create a safe browser localStorage adapter with storage event subscription.
33
+ */
34
+ export function createLocalStorageAdapter(storageKey = DEFAULT_BASKET_STORAGE_KEY) {
35
+ function checkAvailable() {
36
+ if (typeof window === "undefined" || typeof localStorage === "undefined")
37
+ return false;
38
+ try {
39
+ const probe = "__cod_probe__";
40
+ localStorage.setItem(probe, "1");
41
+ localStorage.removeItem(probe);
42
+ return true;
43
+ }
44
+ catch {
45
+ return false;
46
+ }
47
+ }
48
+ return {
49
+ getItem: (key) => {
50
+ try {
51
+ return localStorage.getItem(key);
52
+ }
53
+ catch {
54
+ return null;
55
+ }
56
+ },
57
+ setItem: (key, value) => {
58
+ try {
59
+ localStorage.setItem(key, value);
60
+ }
61
+ catch {
62
+ // Quota exceeded or private browsing: fail silently, memory state continues
63
+ }
64
+ },
65
+ removeItem: (key) => {
66
+ try {
67
+ localStorage.removeItem(key);
68
+ }
69
+ catch {
70
+ // Fail silently
71
+ }
72
+ },
73
+ isAvailable: checkAvailable,
74
+ subscribe: (onExternalChange) => {
75
+ if (typeof window === "undefined")
76
+ return () => { };
77
+ const onStorage = (event) => {
78
+ if (event.key === storageKey) {
79
+ onExternalChange();
80
+ }
81
+ };
82
+ window.addEventListener("storage", onStorage);
83
+ return () => {
84
+ window.removeEventListener("storage", onStorage);
85
+ };
86
+ },
87
+ };
88
+ }
89
+ function sanitizeLine(raw, maxQuantity) {
90
+ if (typeof raw !== "object" || raw === null)
91
+ return null;
92
+ const line = raw;
93
+ const productId = typeof line.productId === "string" ? line.productId.trim() : "";
94
+ const productName = typeof line.productName === "string" ? line.productName : "";
95
+ if (!productId || !productName)
96
+ return null;
97
+ const quantity = Number(line.quantity);
98
+ if (!Number.isInteger(quantity) || quantity < 1)
99
+ return null;
100
+ const unitPrice = Number(line.unitPrice);
101
+ return {
102
+ productId,
103
+ productName,
104
+ variantId: typeof line.variantId === "string" && line.variantId ? line.variantId : null,
105
+ variantLabel: typeof line.variantLabel === "string" && line.variantLabel ? line.variantLabel : null,
106
+ quantity: Math.min(quantity, maxQuantity),
107
+ unitPrice: Number.isFinite(unitPrice) && unitPrice >= 0 ? unitPrice : 0,
108
+ href: typeof line.href === "string" ? line.href : undefined,
109
+ image: typeof line.image === "string" ? line.image : undefined,
110
+ };
111
+ }
112
+ function normalizeLines(lines, maxLines, maxQuantity) {
113
+ const merged = new Map();
114
+ for (const line of lines) {
115
+ const key = basketLineKey(line.productId, line.variantId);
116
+ const existing = merged.get(key);
117
+ if (existing) {
118
+ existing.quantity = Math.min(existing.quantity + line.quantity, maxQuantity);
119
+ existing.variantLabel ??= line.variantLabel;
120
+ existing.image ??= line.image;
121
+ existing.href ??= line.href;
122
+ }
123
+ else {
124
+ merged.set(key, { ...line });
125
+ }
126
+ }
127
+ return [...merged.values()].slice(0, maxLines);
128
+ }
129
+ /**
130
+ * The server-facing projection of basket lines — the ONE place a browser
131
+ * basket turns into `items[]` for POST /store/cart/validate or an order.
132
+ *
133
+ * Pure and store-free so a caller holding a line snapshot (a theme's
134
+ * debounced validator, a checkout draft) projects it without re-deriving the
135
+ * wire shape; `BasketStore.toOrderItems()` delegates here, and the order
136
+ * action's schema is what finally accepts or rejects it.
137
+ */
138
+ export function toOrderItems(lines) {
139
+ return lines.map((line) => ({
140
+ productId: line.productId,
141
+ productName: line.productName,
142
+ variantId: line.variantId ?? undefined,
143
+ variantLabel: line.variantLabel ?? undefined,
144
+ quantity: line.quantity,
145
+ pricePerUnit: line.unitPrice,
146
+ }));
147
+ }
148
+ /**
149
+ * Create a deep Basket Store.
150
+ */
151
+ export function createBasketStore(options = {}) {
152
+ const storageKey = options.storageKey ?? DEFAULT_BASKET_STORAGE_KEY;
153
+ const maxLines = options.maxLines ?? MAX_BASKET_LINES;
154
+ const maxLineQuantity = options.maxLineQuantity ?? MAX_BASKET_LINE_QUANTITY;
155
+ const storage = options.storage ??
156
+ (typeof window !== "undefined"
157
+ ? createLocalStorageAdapter(storageKey)
158
+ : createMemoryStorageAdapter());
159
+ const listeners = new Set();
160
+ function readFromStorage() {
161
+ try {
162
+ const raw = storage.getItem(storageKey);
163
+ if (!raw)
164
+ return [];
165
+ const parsed = JSON.parse(raw);
166
+ if (!Array.isArray(parsed))
167
+ return [];
168
+ return normalizeLines(parsed
169
+ .map((item) => sanitizeLine(item, maxLineQuantity))
170
+ .filter((line) => line !== null), maxLines, maxLineQuantity);
171
+ }
172
+ catch {
173
+ return [];
174
+ }
175
+ }
176
+ let state = readFromStorage();
177
+ function commit(next) {
178
+ state = normalizeLines(next, maxLines, maxLineQuantity);
179
+ try {
180
+ storage.setItem(storageKey, JSON.stringify(state));
181
+ }
182
+ catch {
183
+ // Ignored: memory state continues
184
+ }
185
+ for (const listener of listeners) {
186
+ try {
187
+ listener(state);
188
+ }
189
+ catch {
190
+ // Listener exception must not break store
191
+ }
192
+ }
193
+ return state;
194
+ }
195
+ // Cross-tab synchronization
196
+ if (storage.subscribe) {
197
+ storage.subscribe(() => {
198
+ state = readFromStorage();
199
+ for (const listener of listeners) {
200
+ try {
201
+ listener(state);
202
+ }
203
+ catch { }
204
+ }
205
+ });
206
+ }
207
+ return {
208
+ getLines() {
209
+ return state;
210
+ },
211
+ getCount() {
212
+ return state.reduce((total, line) => total + line.quantity, 0);
213
+ },
214
+ getSubtotal() {
215
+ return state.reduce((total, line) => total + line.unitPrice * line.quantity, 0);
216
+ },
217
+ addLine(line) {
218
+ const key = basketLineKey(line.productId, line.variantId);
219
+ const isExisting = state.some((l) => basketLineKey(l.productId, l.variantId) === key);
220
+ // Refuse adding a new line when already at cap
221
+ if (state.length >= maxLines && !isExisting) {
222
+ return state;
223
+ }
224
+ return commit([...state, line]);
225
+ },
226
+ setQuantity(key, quantity) {
227
+ if (!Number.isInteger(quantity) || quantity < 1) {
228
+ return this.removeLine(key);
229
+ }
230
+ const capped = Math.min(quantity, maxLineQuantity);
231
+ return commit(state.map((line) => basketLineKey(line.productId, line.variantId) === key
232
+ ? { ...line, quantity: capped }
233
+ : line));
234
+ },
235
+ removeLine(key) {
236
+ return commit(state.filter((line) => basketLineKey(line.productId, line.variantId) !== key));
237
+ },
238
+ clear() {
239
+ try {
240
+ storage.removeItem(storageKey);
241
+ }
242
+ catch { }
243
+ return commit([]);
244
+ },
245
+ toOrderItems() {
246
+ return toOrderItems(state);
247
+ },
248
+ subscribe(listener) {
249
+ listeners.add(listener);
250
+ return () => {
251
+ listeners.delete(listener);
252
+ };
253
+ },
254
+ isPersistent() {
255
+ return storage.isAvailable();
256
+ },
257
+ };
258
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Cart Namespace - Server-side Basket Validation
3
+ *
4
+ * Deep module: hides the wire shape and the read-only semantics of
5
+ * POST /store/cart/validate behind one call. The shopper's basket lives in
6
+ * the browser; every line here is re-priced and re-checked against the live
7
+ * catalog — the client's own price is display-only.
8
+ *
9
+ * Failure contract: returns the error as a
10
+ * result (never throws). The upstream is read-only — nothing is written and
11
+ * no stock is reserved — so a failure is safe to swallow: the cart drawer
12
+ * keeps its optimistic view and checkout re-validates everything anyway.
13
+ */
14
+ import type { ClientCore, ClientResult } from "./core.js";
15
+ import type { CartValidation } from "../types.js";
16
+ import type { BasketLineInput } from "../domain.js";
17
+ export interface CartNamespace {
18
+ /**
19
+ * Re-price and re-check a basket against the live catalog.
20
+ * `items` are the browser's basket lines (productId, quantity, and the
21
+ * optional variant fields); pricing from their side is never trusted, and
22
+ * each line's product name and variant label come back from the catalog in
23
+ * the client's language.
24
+ */
25
+ validate(items: readonly BasketLineInput[]): Promise<ClientResult<CartValidation>>;
26
+ }
27
+ /**
28
+ * Create cart namespace
29
+ */
30
+ export declare function createCartNamespace(core: ClientCore): CartNamespace;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Cart Namespace - Server-side Basket Validation
3
+ *
4
+ * Deep module: hides the wire shape and the read-only semantics of
5
+ * POST /store/cart/validate behind one call. The shopper's basket lives in
6
+ * the browser; every line here is re-priced and re-checked against the live
7
+ * catalog — the client's own price is display-only.
8
+ *
9
+ * Failure contract: returns the error as a
10
+ * result (never throws). The upstream is read-only — nothing is written and
11
+ * no stock is reserved — so a failure is safe to swallow: the cart drawer
12
+ * keeps its optimistic view and checkout re-validates everything anyway.
13
+ */
14
+ import { CartValidationSchema } from "../schemas.js";
15
+ import { STOREFRONT_OPERATIONS } from "../domain.js";
16
+ import { localizedPath } from "./paths.js";
17
+ /**
18
+ * Create cart namespace
19
+ */
20
+ export function createCartNamespace(core) {
21
+ return {
22
+ async validate(items) {
23
+ return core.post(localizedPath(core.config, "/store/cart/validate"), { items }, { schema: CartValidationSchema, operation: STOREFRONT_OPERATIONS.cartValidate });
24
+ },
25
+ };
26
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Categories Namespace - Store Category Tree
3
+ *
4
+ * Deep module: hides URL construction, caching, transport, and response
5
+ * validation behind one call. Categories are read-mostly, so successful
6
+ * lists are cached for the process lifetime; failures are never cached (the
7
+ * next call retries).
8
+ *
9
+ * Failure contract: resolves to `[]`
10
+ * on any failure — never throws. Pass `onError` to the client to see what a
11
+ * degraded read actually was.
12
+ */
13
+ import { type ClientCore } from "./core.js";
14
+ import type { Category } from "../types.js";
15
+ export interface CategoriesNamespace {
16
+ /**
17
+ * List store categories, ordered by position (cached after first success).
18
+ * Resolves to `[]` when the list cannot be fetched.
19
+ */
20
+ list(): Promise<Category[]>;
21
+ }
22
+ /**
23
+ * Create categories namespace
24
+ */
25
+ export declare function createCategoriesNamespace(core: ClientCore): CategoriesNamespace;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Categories Namespace - Store Category Tree
3
+ *
4
+ * Deep module: hides URL construction, caching, transport, and response
5
+ * validation behind one call. Categories are read-mostly, so successful
6
+ * lists are cached for the process lifetime; failures are never cached (the
7
+ * next call retries).
8
+ *
9
+ * Failure contract: resolves to `[]`
10
+ * on any failure — never throws. Pass `onError` to the client to see what a
11
+ * degraded read actually was.
12
+ */
13
+ import { z } from "zod";
14
+ import { CategorySchema } from "../schemas.js";
15
+ import { STOREFRONT_OPERATIONS } from "../domain.js";
16
+ import { localizedPath } from "./paths.js";
17
+ import { reportDegradedRead } from "./core.js";
18
+ /**
19
+ * Create categories namespace
20
+ */
21
+ export function createCategoriesNamespace(core) {
22
+ return {
23
+ async list() {
24
+ try {
25
+ const categories = await core.get(localizedPath(core.config, "/store/categories"), z.array(CategorySchema), {
26
+ cache: true,
27
+ operation: STOREFRONT_OPERATIONS.categoriesList,
28
+ });
29
+ return categories ?? [];
30
+ }
31
+ catch (error) {
32
+ reportDegradedRead(core.config, error, "categories.list");
33
+ return [];
34
+ }
35
+ },
36
+ };
37
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Config Namespace - Store Configuration
3
+ *
4
+ * Deep module: hides caching, retry, transport, and response validation
5
+ * behind one call.
6
+ *
7
+ * Failure contract: resolves to `null`
8
+ * on any failure (HTTP error, network error, absent data, a response that no
9
+ * longer matches the store-config contract) — never throws. Themes fall back
10
+ * to their defaults (`(await store.config.get()) ?? DEFAULTS`). Pass `onError` to the
11
+ * client to see what a `null` actually was.
12
+ *
13
+ * Successful results are cached for the process lifetime (config is
14
+ * read-mostly); failures and null results are never cached, so a transient
15
+ * outage recovers on the next request.
16
+ */
17
+ import { type ClientCore } from "./core.js";
18
+ import type { StoreConfig } from "../types.js";
19
+ export interface ConfigNamespace {
20
+ /**
21
+ * Get store configuration (cached after first success).
22
+ * Resolves to `null` when the config cannot be fetched.
23
+ */
24
+ get(): Promise<StoreConfig | null>;
25
+ }
26
+ /**
27
+ * Create config namespace
28
+ */
29
+ export declare function createConfigNamespace(core: ClientCore): ConfigNamespace;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Config Namespace - Store Configuration
3
+ *
4
+ * Deep module: hides caching, retry, transport, and response validation
5
+ * behind one call.
6
+ *
7
+ * Failure contract: resolves to `null`
8
+ * on any failure (HTTP error, network error, absent data, a response that no
9
+ * longer matches the store-config contract) — never throws. Themes fall back
10
+ * to their defaults (`(await store.config.get()) ?? DEFAULTS`). Pass `onError` to the
11
+ * client to see what a `null` actually was.
12
+ *
13
+ * Successful results are cached for the process lifetime (config is
14
+ * read-mostly); failures and null results are never cached, so a transient
15
+ * outage recovers on the next request.
16
+ */
17
+ import { StoreConfigSchema } from "../schemas.js";
18
+ import { STOREFRONT_OPERATIONS } from "../domain.js";
19
+ import { localizedPath } from "./paths.js";
20
+ import { reportDegradedRead } from "./core.js";
21
+ /**
22
+ * Create config namespace
23
+ */
24
+ export function createConfigNamespace(core) {
25
+ return {
26
+ async get() {
27
+ try {
28
+ const path = localizedPath(core.config, "/store/config", { theme: core.config.theme });
29
+ const config = await core.get(path, StoreConfigSchema.nullable(), {
30
+ cache: true,
31
+ operation: STOREFRONT_OPERATIONS.configGet,
32
+ });
33
+ return config ?? null;
34
+ }
35
+ catch (error) {
36
+ reportDegradedRead(core.config, error, "config.get");
37
+ return null;
38
+ }
39
+ },
40
+ };
41
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Deep API Client - Core Infrastructure
3
+ *
4
+ * Hides: caching, request deduplication, retry logic, validation,
5
+ * header management, cancellation, and URL construction.
6
+ *
7
+ * Deep module principle: Maximum leverage for namespace implementations.
8
+ * One infrastructure layer serves all API methods.
9
+ */
10
+ import type { z } from "zod";
11
+ import type { ClientResult, StoreScope } from "../domain.js";
12
+ export type { ClientResult } from "../domain.js";
13
+ /**
14
+ * Client configuration
15
+ */
16
+ export type ClientConfig = StoreScope;
17
+ /**
18
+ * Options every request accepts.
19
+ *
20
+ * `signal` cancels the request — an aborted call never retries, never coalesces
21
+ * with another caller's request, and returns a REQUEST_ABORTED DomainError
22
+ * (a read throws instead). `timeoutMs` bounds the whole operation, retries
23
+ * included, and reads as REQUEST_TIMEOUT. `operation` names the call in the
24
+ * error it produces; namespaces pass a stable label (`orders.otp.send`) so logs
25
+ * never carry shopper-supplied ids from the URL.
26
+ */
27
+ export interface RequestOptions {
28
+ signal?: AbortSignal;
29
+ timeoutMs?: number;
30
+ operation?: string;
31
+ }
32
+ /**
33
+ * Options for GET requests
34
+ */
35
+ export interface GetOptions extends RequestOptions {
36
+ envelope?: boolean;
37
+ cache?: boolean;
38
+ }
39
+ /**
40
+ * Options for POST requests
41
+ */
42
+ export interface PostOptions<TOutput> extends RequestOptions {
43
+ schema?: z.Schema<TOutput>;
44
+ headers?: Record<string, string>;
45
+ topLevelData?: boolean;
46
+ }
47
+ /**
48
+ * Options for PATCH requests
49
+ */
50
+ export interface PatchOptions<TOutput> extends RequestOptions {
51
+ schema?: z.Schema<TOutput>;
52
+ headers?: Record<string, string>;
53
+ }
54
+ /**
55
+ * Client core infrastructure
56
+ * Exposed only to namespace implementations, not to end users
57
+ */
58
+ export interface ClientCore {
59
+ config: ClientConfig;
60
+ get: <T>(path: string, schema?: z.Schema<T>, opts?: GetOptions) => Promise<T>;
61
+ post: <TInput, TOutput>(path: string, body: TInput, opts?: PostOptions<TOutput>) => Promise<ClientResult<TOutput>>;
62
+ patch: <TInput, TOutput>(path: string, body: TInput, optsOrSchema?: z.Schema<TOutput> | PatchOptions<TOutput>) => Promise<ClientResult<TOutput>>;
63
+ }
64
+ /**
65
+ * Create client core infrastructure
66
+ *
67
+ * @param config - Client configuration
68
+ * @returns Core infrastructure for namespace implementations
69
+ */
70
+ export declare function createClientCore(config: ClientConfig): ClientCore;
71
+ /**
72
+ * Report a read that a namespace swallowed.
73
+ *
74
+ * Namespaces degrade instead of throwing — that is their contract — so this
75
+ * is the operator's only signal that a call did not complete. A failure the
76
+ * core produced already carries its DomainError; anything else (a bug inside
77
+ * a namespace, a storage exception) is normalised to the same shape, so a
78
+ * consumer's handler branches on one contract. A throwing hook never turns a
79
+ * degraded read into a thrown one.
80
+ */
81
+ export declare function reportDegradedRead(config: StoreScope | undefined, error: unknown, operation: string): void;
82
+ /**
83
+ * Clear the request cache
84
+ * Useful for testing or forcing fresh data
85
+ */
86
+ export declare function clearCache(): void;