@proveanything/smartlinks 1.7.9 → 1.8.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.
@@ -1,4 +1,5 @@
1
- import type { UserAccountRegistrationRequest } from "../types/auth";
1
+ import type { UserAccountRegistrationRequest, AccountInfoResponse, AuthLocation, AuthLocationCacheOptions } from "../types/auth";
2
+ export type { AccountInfoResponse, AuthLocation, AuthLocationCacheOptions } from "../types/auth";
2
3
  export type LoginResponse = {
3
4
  id: string;
4
5
  name: string;
@@ -13,40 +14,6 @@ export type VerifyTokenResponse = {
13
14
  email?: string;
14
15
  account?: Record<string, any>;
15
16
  };
16
- export type AccountInfoResponse = {
17
- accessType: string;
18
- analyticsCode: string;
19
- analyticsId: string;
20
- auth_time: number;
21
- baseCollectionId: string;
22
- clientType: string;
23
- email: string;
24
- email_verified: boolean;
25
- features: {
26
- actionLogger: boolean;
27
- adminCollections: boolean;
28
- adminApps: boolean;
29
- apiKeys: boolean;
30
- adminUsers: boolean;
31
- [key: string]: boolean;
32
- };
33
- iat: number;
34
- id: string;
35
- iss: string;
36
- location: string | null;
37
- name: string;
38
- picture: string;
39
- sites: {
40
- [siteName: string]: boolean;
41
- };
42
- sub: string;
43
- uid: string;
44
- userId: string;
45
- contactId: string;
46
- whitelabel: {
47
- [key: string]: any;
48
- };
49
- };
50
17
  export declare namespace auth {
51
18
  /**
52
19
  * Login with email and password.
@@ -89,10 +56,31 @@ export declare namespace auth {
89
56
  }): Promise<{
90
57
  bearerToken: string;
91
58
  }>;
59
+ /**
60
+ * Gets a best-effort coarse location for the current anonymous caller.
61
+ *
62
+ * This endpoint is typically IP-derived and is useful when the user is not
63
+ * logged in but you still want country/location context for content rules,
64
+ * analytics enrichment, or regional defaults.
65
+ *
66
+ * Returns fields such as `country`, `latitude`, `longitude`, and `area`
67
+ * when available.
68
+ *
69
+ * By default the result is cached in session storage for 30 minutes so apps
70
+ * can reuse coarse location context without repeatedly hitting the endpoint.
71
+ */
72
+ function getLocation(options?: AuthLocationCacheOptions): Promise<AuthLocation>;
73
+ /**
74
+ * Clears the cached anonymous auth location, if present.
75
+ */
76
+ function clearCachedLocation(storageKey?: string): void;
92
77
  /**
93
78
  * Gets current account information for the logged in user.
94
79
  * Returns user, owner, account, and location objects.
95
80
  *
81
+ * When the caller is authenticated, prefer `account.location` from this
82
+ * response. For anonymous callers, use `auth.getLocation()` instead.
83
+ *
96
84
  * Short-circuits immediately (no network request) when the SDK has no
97
85
  * bearer token or API key set — the server would return 401 anyway.
98
86
  * Throws a `SmartlinksApiError` with `statusCode 401` and
package/dist/api/auth.js CHANGED
@@ -1,5 +1,69 @@
1
1
  import { post, request, setBearerToken, getApiHeaders, hasAuthCredentials, isProxyEnabled } from "../http";
2
2
  import { SmartlinksApiError } from "../types/error";
3
+ const DEFAULT_AUTH_LOCATION_CACHE_KEY = 'smartlinks.auth.location';
4
+ const DEFAULT_AUTH_LOCATION_TTL_MS = 30 * 60 * 1000;
5
+ let inMemoryAuthLocationCache = null;
6
+ function getSessionStorage() {
7
+ try {
8
+ if (typeof sessionStorage !== 'undefined')
9
+ return sessionStorage;
10
+ }
11
+ catch (_a) {
12
+ }
13
+ return undefined;
14
+ }
15
+ function readCachedLocation(storageKey) {
16
+ const now = Date.now();
17
+ if (inMemoryAuthLocationCache && inMemoryAuthLocationCache.expiresAt > now) {
18
+ return inMemoryAuthLocationCache.value;
19
+ }
20
+ const storage = getSessionStorage();
21
+ if (!storage)
22
+ return null;
23
+ try {
24
+ const raw = storage.getItem(storageKey);
25
+ if (!raw)
26
+ return null;
27
+ const cached = JSON.parse(raw);
28
+ if (!(cached === null || cached === void 0 ? void 0 : cached.value) || typeof cached.expiresAt !== 'number') {
29
+ storage.removeItem(storageKey);
30
+ return null;
31
+ }
32
+ if (cached.expiresAt <= now) {
33
+ storage.removeItem(storageKey);
34
+ return null;
35
+ }
36
+ inMemoryAuthLocationCache = cached;
37
+ return cached.value;
38
+ }
39
+ catch (_a) {
40
+ return null;
41
+ }
42
+ }
43
+ function writeCachedLocation(storageKey, value, ttlMs) {
44
+ const cached = {
45
+ value,
46
+ expiresAt: Date.now() + ttlMs,
47
+ };
48
+ inMemoryAuthLocationCache = cached;
49
+ const storage = getSessionStorage();
50
+ if (!storage)
51
+ return;
52
+ try {
53
+ storage.setItem(storageKey, JSON.stringify(cached));
54
+ }
55
+ catch (_a) {
56
+ }
57
+ }
58
+ function clearCachedLocationInternal(storageKey) {
59
+ inMemoryAuthLocationCache = null;
60
+ const storage = getSessionStorage();
61
+ try {
62
+ storage === null || storage === void 0 ? void 0 : storage.removeItem(storageKey);
63
+ }
64
+ catch (_a) {
65
+ }
66
+ }
3
67
  /*
4
68
  user: Record<string, any>
5
69
  owner: Record<string, any>
@@ -82,10 +146,50 @@ export var auth;
82
146
  return post("/admin/auth/getUserToken", body);
83
147
  }
84
148
  auth.getUserToken = getUserToken;
149
+ /**
150
+ * Gets a best-effort coarse location for the current anonymous caller.
151
+ *
152
+ * This endpoint is typically IP-derived and is useful when the user is not
153
+ * logged in but you still want country/location context for content rules,
154
+ * analytics enrichment, or regional defaults.
155
+ *
156
+ * Returns fields such as `country`, `latitude`, `longitude`, and `area`
157
+ * when available.
158
+ *
159
+ * By default the result is cached in session storage for 30 minutes so apps
160
+ * can reuse coarse location context without repeatedly hitting the endpoint.
161
+ */
162
+ async function getLocation(options = {}) {
163
+ var _a, _b, _c;
164
+ const cache = (_a = options.cache) !== null && _a !== void 0 ? _a : 'session';
165
+ const ttlMs = (_b = options.ttlMs) !== null && _b !== void 0 ? _b : DEFAULT_AUTH_LOCATION_TTL_MS;
166
+ const storageKey = (_c = options.storageKey) !== null && _c !== void 0 ? _c : DEFAULT_AUTH_LOCATION_CACHE_KEY;
167
+ if (cache === 'session' && !options.forceRefresh) {
168
+ const cached = readCachedLocation(storageKey);
169
+ if (cached)
170
+ return cached;
171
+ }
172
+ const location = await request("/public/auth/location");
173
+ if (cache === 'session') {
174
+ writeCachedLocation(storageKey, location, ttlMs);
175
+ }
176
+ return location;
177
+ }
178
+ auth.getLocation = getLocation;
179
+ /**
180
+ * Clears the cached anonymous auth location, if present.
181
+ */
182
+ function clearCachedLocation(storageKey = DEFAULT_AUTH_LOCATION_CACHE_KEY) {
183
+ clearCachedLocationInternal(storageKey);
184
+ }
185
+ auth.clearCachedLocation = clearCachedLocation;
85
186
  /**
86
187
  * Gets current account information for the logged in user.
87
188
  * Returns user, owner, account, and location objects.
88
189
  *
190
+ * When the caller is authenticated, prefer `account.location` from this
191
+ * response. For anonymous callers, use `auth.getLocation()` instead.
192
+ *
89
193
  * Short-circuits immediately (no network request) when the SDK has no
90
194
  * bearer token or API key set — the server would return 401 anyway.
91
195
  * Throws a `SmartlinksApiError` with `statusCode 401` and
@@ -24,6 +24,7 @@ export { journeysAnalytics } from "./journeysAnalytics";
24
24
  export { qr } from "./qr";
25
25
  export { template } from "./template";
26
26
  export { interactions } from "./interactions";
27
+ export { analytics } from "./analytics";
27
28
  export { location } from "./location";
28
29
  export * as realtime from "./realtime";
29
30
  export { tags } from "./tags";
package/dist/api/index.js CHANGED
@@ -26,6 +26,7 @@ export { journeysAnalytics } from "./journeysAnalytics";
26
26
  export { qr } from "./qr";
27
27
  export { template } from "./template";
28
28
  export { interactions } from "./interactions";
29
+ export { analytics } from "./analytics";
29
30
  export { location } from "./location";
30
31
  import * as realtime_1 from "./realtime";
31
32
  export { realtime_1 as realtime };
@@ -252,13 +252,22 @@ export declare namespace order {
252
252
  * includeItems: true
253
253
  * })
254
254
  *
255
- * // Find orders containing a specific product batch
255
+ * // Find orders containing a specific product batch
256
256
  * const batchOrders = await order.query('coll_123', {
257
257
  * query: {
258
- * item: {
259
- * productId: 'prod_789',
260
- * batchId: 'BATCH-2024-001'
261
- * }
258
+ * productId: 'prod_789',
259
+ * batchId: 'BATCH-2024-001'
260
+ * },
261
+ * includeItems: true
262
+ * })
263
+ *
264
+ * // Find an order containing one of several specific items
265
+ * const matched = await order.query('coll_123', {
266
+ * query: {
267
+ * items: [
268
+ * { itemType: 'tag', itemId: 'TAG001' },
269
+ * { itemType: 'serial', itemId: 'SN12345' }
270
+ * ]
262
271
  * },
263
272
  * includeItems: true
264
273
  * })
package/dist/api/order.js CHANGED
@@ -287,9 +287,11 @@ export var order;
287
287
  }
288
288
  order.lookup = lookup;
289
289
  function validateOrderQuery(data) {
290
- var _a;
291
- if (((_a = data.query) === null || _a === void 0 ? void 0 : _a.item) && !data.query.item.productId) {
292
- throw new Error('query.item.productId is required when using item-level order filters');
290
+ const query = data.query;
291
+ if (!query)
292
+ return;
293
+ if ((query.batchId || query.variantId) && !query.productId) {
294
+ throw new Error('query.productId is required when using query.batchId or query.variantId');
293
295
  }
294
296
  }
295
297
  /**
@@ -325,13 +327,22 @@ export var order;
325
327
  * includeItems: true
326
328
  * })
327
329
  *
328
- * // Find orders containing a specific product batch
330
+ * // Find orders containing a specific product batch
329
331
  * const batchOrders = await order.query('coll_123', {
330
332
  * query: {
331
- * item: {
332
- * productId: 'prod_789',
333
- * batchId: 'BATCH-2024-001'
334
- * }
333
+ * productId: 'prod_789',
334
+ * batchId: 'BATCH-2024-001'
335
+ * },
336
+ * includeItems: true
337
+ * })
338
+ *
339
+ * // Find an order containing one of several specific items
340
+ * const matched = await order.query('coll_123', {
341
+ * query: {
342
+ * items: [
343
+ * { itemType: 'tag', itemId: 'TAG001' },
344
+ * { itemType: 'serial', itemId: 'SN12345' }
345
+ * ]
335
346
  * },
336
347
  * includeItems: true
337
348
  * })