@inspira-npm/events 0.0.0-stage → 0.1.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/dist/index.js ADDED
@@ -0,0 +1,579 @@
1
+ import { n as SDK_VERSION, t as SDK_NAME } from "./version-Cqps0u2Y.js";
2
+ // src/click-id.ts
3
+ /** Query param que lleva el click id de un ad hasta el sitio destino. */
4
+ const CLICK_ID_PARAM = "ipc";
5
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
6
+ function isClickId(value) {
7
+ return typeof value === "string" && UUID.test(value);
8
+ }
9
+ /**
10
+ * Agrega `?ipc=<clickId>` a una URL conservando el resto de la query y el hash.
11
+ * Las URLs relativas siguen siendo relativas. Si la URL no se puede parsear se devuelve igual.
12
+ */
13
+ function withClickId(url, clickId, param = "ipc") {
14
+ const isAbsolute = /^[a-z][a-z\d+.-]*:/i.test(url) || url.startsWith("//");
15
+ try {
16
+ const base = typeof location !== "undefined" ? location.href : "https://placeholder.invalid/";
17
+ const parsed = new URL(url, base);
18
+ const kept = parsed.search.slice(1).split("&").filter((pair) => pair !== "" && pair.split("=", 1)[0] !== param);
19
+ kept.push(`${encodeURIComponent(param)}=${encodeURIComponent(clickId)}`);
20
+ parsed.search = kept.join("&");
21
+ return isAbsolute ? parsed.toString() : `${parsed.pathname}${parsed.search}${parsed.hash}`;
22
+ } catch {
23
+ return url;
24
+ }
25
+ }
26
+ /** Lee un click id válido de una URL (o null). */
27
+ function readClickIdFromUrl(href, param = "ipc") {
28
+ try {
29
+ const value = new URL(href).searchParams.get(param);
30
+ return isClickId(value) ? value : null;
31
+ } catch {
32
+ return null;
33
+ }
34
+ }
35
+ // src/consent.ts
36
+ const DEFAULT_CONSENT_COOKIE = "CookieConsent";
37
+ /** Evento que dispara el banner de inspira-frontend-v2 al guardar el consentimiento. */
38
+ const CONSENT_CHANGE_EVENT = "cookie-consent-change";
39
+ const ANALYTICS_CATEGORY = /^(all|statistic|estad[íi]stic|analytic|anal[íi]tic)/;
40
+ /** Interpreta el valor crudo de la cookie de consentimiento (puede venir URL-encoded). */
41
+ function parseConsentValue(raw) {
42
+ if (!raw) return "unknown";
43
+ let decoded = raw;
44
+ try {
45
+ decoded = decodeURIComponent(raw);
46
+ } catch {}
47
+ return decoded.split(",").map((name) => name.trim().toLowerCase()).some((name) => ANALYTICS_CATEGORY.test(name)) ? "granted" : "denied";
48
+ }
49
+ /**
50
+ * Lector de consentimiento basado en la cookie del banner de Inspira. Pásalo como
51
+ * `consent` a `createTracker`. Se evalúa en cada evento, así que un cambio de
52
+ * consentimiento aplica al siguiente evento sin recargar.
53
+ */
54
+ function cookieConsent(cookieName = DEFAULT_CONSENT_COOKIE) {
55
+ const escaped = cookieName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
56
+ const pattern = new RegExp(`(?:^|;\\s*)${escaped}=([^;]*)`);
57
+ return () => {
58
+ if (typeof document === "undefined") return "unknown";
59
+ return parseConsentValue(pattern.exec(document.cookie)?.[1]);
60
+ };
61
+ }
62
+ // src/context.ts
63
+ /**
64
+ * Query params que SÍ se conservan en `ctx.url`. El resto se descarta: las URLs pueden
65
+ * traer tokens, emails o ids de reserva.
66
+ */
67
+ const DEFAULT_URL_PARAM_ALLOWLIST = [
68
+ "utm_source",
69
+ "utm_medium",
70
+ "utm_campaign",
71
+ "utm_term",
72
+ "utm_content",
73
+ "ipc",
74
+ "gclid",
75
+ "fbclid"
76
+ ];
77
+ function sanitizeUrl(href, allowlist) {
78
+ try {
79
+ const url = new URL(href);
80
+ const kept = new URLSearchParams();
81
+ for (const [key, value] of url.searchParams) if (allowlist.includes(key)) kept.append(key, value);
82
+ const query = kept.toString();
83
+ return `${url.origin}${url.pathname}${query ? `?${query}` : ""}`;
84
+ } catch {
85
+ return "";
86
+ }
87
+ }
88
+ function originAndPath(href) {
89
+ if (!href) return null;
90
+ try {
91
+ const url = new URL(href);
92
+ return `${url.origin}${url.pathname}`;
93
+ } catch {
94
+ return null;
95
+ }
96
+ }
97
+ function collectContext(allowlist) {
98
+ let tz = null;
99
+ try {
100
+ tz = Intl.DateTimeFormat().resolvedOptions().timeZone ?? null;
101
+ } catch {
102
+ tz = null;
103
+ }
104
+ return {
105
+ url: sanitizeUrl(location.href, allowlist),
106
+ path: location.pathname,
107
+ referrer: originAndPath(document.referrer),
108
+ locale: document.documentElement.lang || navigator.language || null,
109
+ tz,
110
+ screen: typeof screen !== "undefined" ? {
111
+ w: screen.width,
112
+ h: screen.height
113
+ } : null,
114
+ viewport: {
115
+ w: window.innerWidth,
116
+ h: window.innerHeight
117
+ }
118
+ };
119
+ }
120
+ // src/storage.ts
121
+ /**
122
+ * localStorage/sessionStorage que nunca lanza: en Safari privado, con storage bloqueado o
123
+ * cuota llena cae a memoria (los ids duran lo que dure la página).
124
+ */
125
+ function safeStorage(kind) {
126
+ const memory = /* @__PURE__ */ new Map();
127
+ const backend = () => {
128
+ try {
129
+ return kind === "local" ? window.localStorage : window.sessionStorage;
130
+ } catch {
131
+ return null;
132
+ }
133
+ };
134
+ return {
135
+ get(key) {
136
+ try {
137
+ const value = backend()?.getItem(key);
138
+ if (value !== null && value !== void 0) return value;
139
+ } catch {}
140
+ return memory.get(key) ?? null;
141
+ },
142
+ set(key, value) {
143
+ try {
144
+ const storage = backend();
145
+ if (storage) {
146
+ storage.setItem(key, value);
147
+ memory.delete(key);
148
+ return;
149
+ }
150
+ } catch {}
151
+ memory.set(key, value);
152
+ },
153
+ remove(key) {
154
+ memory.delete(key);
155
+ try {
156
+ backend()?.removeItem(key);
157
+ } catch {}
158
+ }
159
+ };
160
+ }
161
+ // src/uuid.ts
162
+ /** UUID v4. `crypto.randomUUID` solo existe en contextos seguros (https/localhost). */
163
+ function uuid() {
164
+ const cryptoApi = globalThis.crypto;
165
+ if (typeof cryptoApi?.randomUUID === "function") return cryptoApi.randomUUID();
166
+ const bytes = /* @__PURE__ */ new Uint8Array(16);
167
+ if (typeof cryptoApi?.getRandomValues === "function") cryptoApi.getRandomValues(bytes);
168
+ else for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256);
169
+ bytes[6] = (bytes[6] ?? 0) & 15 | 64;
170
+ bytes[8] = (bytes[8] ?? 0) & 63 | 128;
171
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
172
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
173
+ }
174
+ // src/identity.ts
175
+ const ANON_KEY = "ievt_aid";
176
+ const SESSION_KEY = "ievt_sid";
177
+ const SESSION_IDLE_MS = 18e5;
178
+ /**
179
+ * Ids first-party. SOLO se llama con consentimiento `granted`: sin consentimiento el SDK
180
+ * no escribe nada en storage y el collector calcula un hash diario sin cookie.
181
+ * - anon id: persistente por sitio (localStorage).
182
+ * - session id: por pestaña (sessionStorage), rota tras 30 min de inactividad.
183
+ */
184
+ function createIdentity(now = Date.now) {
185
+ const local = safeStorage("local");
186
+ const session = safeStorage("session");
187
+ return {
188
+ ensure() {
189
+ let anonId = local.get(ANON_KEY);
190
+ if (!anonId) {
191
+ anonId = uuid();
192
+ local.set(ANON_KEY, anonId);
193
+ }
194
+ const t = now();
195
+ let current = null;
196
+ try {
197
+ current = JSON.parse(session.get(SESSION_KEY) ?? "null");
198
+ } catch {
199
+ current = null;
200
+ }
201
+ if (!current || typeof current.id !== "string" || t - current.last > SESSION_IDLE_MS) current = {
202
+ id: uuid(),
203
+ last: t
204
+ };
205
+ else current.last = t;
206
+ session.set(SESSION_KEY, JSON.stringify(current));
207
+ return {
208
+ anonId,
209
+ sessionId: current.id
210
+ };
211
+ },
212
+ /** Borra los ids (consentimiento retirado o `reset()` al cerrar sesión). */
213
+ clear() {
214
+ if (local.get(ANON_KEY) !== null) local.remove(ANON_KEY);
215
+ if (session.get(SESSION_KEY) !== null) session.remove(SESSION_KEY);
216
+ }
217
+ };
218
+ }
219
+ // src/impressions.ts
220
+ const STEPS = Array.from({ length: 11 }, (_, i) => i / 10);
221
+ const EPSILON = .001;
222
+ function isViewable(entry, threshold) {
223
+ if (!entry.isIntersecting) return false;
224
+ if (entry.intersectionRatio >= threshold - EPSILON) return true;
225
+ const root = entry.rootBounds;
226
+ if (!root || root.width === 0 || root.height === 0) return false;
227
+ return entry.intersectionRect.width * entry.intersectionRect.height / (root.width * root.height) >= threshold - EPSILON;
228
+ }
229
+ function createImpressionObserver(log) {
230
+ let observer = null;
231
+ const targets = /* @__PURE__ */ new Map();
232
+ const stop = (target) => {
233
+ if (target.timer !== null) {
234
+ clearTimeout(target.timer);
235
+ target.timer = null;
236
+ }
237
+ };
238
+ const remove = (target) => {
239
+ stop(target);
240
+ const set = targets.get(target.element);
241
+ if (!set?.delete(target)) return;
242
+ if (set.size === 0) {
243
+ targets.delete(target.element);
244
+ observer?.unobserve(target.element);
245
+ }
246
+ };
247
+ const start = (target) => {
248
+ if (target.done || target.timer !== null || document.visibilityState === "hidden") return;
249
+ target.timer = setTimeout(() => {
250
+ target.timer = null;
251
+ if (target.done || !target.inView) return;
252
+ target.done = true;
253
+ if (target.once) remove(target);
254
+ target.fire();
255
+ }, target.minVisibleMs);
256
+ };
257
+ const onEntries = (entries) => {
258
+ for (const entry of entries) {
259
+ const set = targets.get(entry.target);
260
+ if (!set) continue;
261
+ for (const target of set) {
262
+ target.inView = isViewable(entry, target.threshold);
263
+ if (target.inView) start(target);
264
+ else {
265
+ stop(target);
266
+ if (!target.once) target.done = false;
267
+ }
268
+ }
269
+ }
270
+ };
271
+ const onVisibilityChange = () => {
272
+ const hidden = document.visibilityState === "hidden";
273
+ for (const set of targets.values()) for (const target of set) if (hidden) stop(target);
274
+ else if (target.inView) start(target);
275
+ };
276
+ const ensureObserver = () => {
277
+ if (!observer) {
278
+ observer = new IntersectionObserver(onEntries, { threshold: STEPS });
279
+ document.addEventListener("visibilitychange", onVisibilityChange);
280
+ }
281
+ return observer;
282
+ };
283
+ return {
284
+ /** Observa `element` y llama `fire` cuando cumple la regla de visibilidad. Devuelve el cleanup. */
285
+ observe(element, fire, options = {}) {
286
+ if (typeof IntersectionObserver === "undefined") {
287
+ log("IntersectionObserver no disponible: impresión no medida");
288
+ return () => {};
289
+ }
290
+ const target = {
291
+ element,
292
+ threshold: Math.min(Math.max(options.threshold ?? .5, 0), 1),
293
+ minVisibleMs: Math.max(options.minVisibleMs ?? 1e3, 0),
294
+ once: options.once ?? true,
295
+ fire,
296
+ inView: false,
297
+ timer: null,
298
+ done: false
299
+ };
300
+ const io = ensureObserver();
301
+ let set = targets.get(element);
302
+ if (!set) {
303
+ set = /* @__PURE__ */ new Set();
304
+ targets.set(element, set);
305
+ } else io.unobserve(element);
306
+ set.add(target);
307
+ io.observe(element);
308
+ return () => remove(target);
309
+ },
310
+ disconnect() {
311
+ for (const set of targets.values()) for (const target of set) stop(target);
312
+ targets.clear();
313
+ observer?.disconnect();
314
+ observer = null;
315
+ document.removeEventListener("visibilitychange", onVisibilityChange);
316
+ }
317
+ };
318
+ }
319
+ // src/transport.ts
320
+ const CONTENT_TYPE = "text/plain;charset=UTF-8";
321
+ const fetchTransport = { send(url, body, { beacon, debug }) {
322
+ if (beacon && typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") try {
323
+ if (navigator.sendBeacon(url, new Blob([body], { type: CONTENT_TYPE }))) return "sent";
324
+ } catch {}
325
+ return fetch(url, {
326
+ method: "POST",
327
+ body,
328
+ headers: { "Content-Type": CONTENT_TYPE },
329
+ keepalive: true,
330
+ credentials: "omit",
331
+ mode: "cors"
332
+ }).then(async (response) => {
333
+ if (response.ok) {
334
+ if (debug) await logRejected(response);
335
+ return "sent";
336
+ }
337
+ if (debug) console.warn("[inspira-events] collector respondió", response.status);
338
+ return response.status === 429 || response.status >= 500 ? "retry" : "drop";
339
+ }, () => "retry");
340
+ } };
341
+ async function logRejected(response) {
342
+ try {
343
+ const data = await response.json();
344
+ if (Array.isArray(data.rejected) && data.rejected.length > 0) console.warn("[inspira-events] eventos rechazados por el collector", data.rejected);
345
+ } catch {}
346
+ }
347
+ // src/tracker.ts
348
+ const MAX_ATTEMPTS = 3;
349
+ const CLICK_ID_STORAGE_KEY = "ievt_ipc";
350
+ const MAX_DESTINATION_URL_LENGTH = 2e3;
351
+ const DAY_MS = 864e5;
352
+ function createNoopTracker() {
353
+ return {
354
+ track: () => null,
355
+ observeImpression: () => () => {},
356
+ trackAdClick: (_props, url) => url,
357
+ captureClickId: () => null,
358
+ getClickId: () => null,
359
+ identify: () => {},
360
+ setWl: () => {},
361
+ reset: () => {},
362
+ flush: () => Promise.resolve(),
363
+ shutdown: () => {}
364
+ };
365
+ }
366
+ /**
367
+ * Crea el tracker. En el servidor (SSR, sin `window`) devuelve un tracker no-op, así que es
368
+ * seguro crearlo a nivel de módulo en un client component de Next.js.
369
+ */
370
+ function createTracker(config) {
371
+ if (typeof window === "undefined" || typeof document === "undefined") return createNoopTracker();
372
+ const flushIntervalMs = config.flushIntervalMs ?? 5e3;
373
+ const maxBatchSize = Math.max(config.maxBatchSize ?? 20, 1);
374
+ const maxQueueSize = Math.max(config.maxQueueSize ?? 500, maxBatchSize);
375
+ const allowlist = config.urlParamAllowlist ?? DEFAULT_URL_PARAM_ALLOWLIST;
376
+ const transport = config.transport ?? fetchTransport;
377
+ const debug = config.debug ?? false;
378
+ const url = `${config.endpoint.replace(/\/+$/, "")}/e/v1/batch?k=${encodeURIComponent(config.siteKey)}`;
379
+ const identity = createIdentity();
380
+ const clickStore = safeStorage("local");
381
+ const log = (...args) => {
382
+ if (debug) console.debug("[inspira-events]", ...args);
383
+ };
384
+ const impressions = createImpressionObserver(log);
385
+ let wl = config.wl ?? null;
386
+ let userId = null;
387
+ let queue = [];
388
+ let timer = null;
389
+ let inflight = null;
390
+ let closed = false;
391
+ const consent = () => {
392
+ try {
393
+ return config.consent?.() ?? "unknown";
394
+ } catch {
395
+ return "unknown";
396
+ }
397
+ };
398
+ const schedule = (delay) => {
399
+ if (timer === null && !closed) timer = setTimeout(() => {
400
+ timer = null;
401
+ flush();
402
+ }, delay);
403
+ };
404
+ const trimQueue = () => {
405
+ const overflow = queue.length - maxQueueSize;
406
+ if (overflow > 0) {
407
+ queue.splice(0, overflow);
408
+ log(`cola llena: ${overflow} eventos descartados`);
409
+ }
410
+ };
411
+ const payload = (batch) => {
412
+ const body = {
413
+ sdk: {
414
+ name: SDK_NAME,
415
+ version: SDK_VERSION
416
+ },
417
+ sent_at: (/* @__PURE__ */ new Date()).toISOString(),
418
+ events: batch.map((q) => q.event)
419
+ };
420
+ return JSON.stringify(body);
421
+ };
422
+ const drain = async () => {
423
+ while (queue.length > 0) {
424
+ const batch = queue.splice(0, maxBatchSize);
425
+ let result;
426
+ try {
427
+ result = await transport.send(url, payload(batch), {
428
+ beacon: false,
429
+ debug
430
+ });
431
+ } catch {
432
+ result = "retry";
433
+ }
434
+ if (result === "sent") continue;
435
+ if (result === "drop") {
436
+ log(`lote de ${batch.length} eventos descartado por el collector`);
437
+ continue;
438
+ }
439
+ queue = [...batch.filter((q) => ++q.attempts < MAX_ATTEMPTS), ...queue];
440
+ trimQueue();
441
+ schedule(flushIntervalMs * 2);
442
+ return;
443
+ }
444
+ };
445
+ const flush = () => {
446
+ if (timer !== null) {
447
+ clearTimeout(timer);
448
+ timer = null;
449
+ }
450
+ if (!inflight) inflight = drain().finally(() => {
451
+ inflight = null;
452
+ if (queue.length > 0) schedule(flushIntervalMs);
453
+ });
454
+ return inflight;
455
+ };
456
+ const flushBeacon = () => {
457
+ if (timer !== null) {
458
+ clearTimeout(timer);
459
+ timer = null;
460
+ }
461
+ while (queue.length > 0) {
462
+ const batch = queue.splice(0, maxBatchSize);
463
+ try {
464
+ Promise.resolve(transport.send(url, payload(batch), {
465
+ beacon: true,
466
+ debug
467
+ })).catch(() => {});
468
+ } catch {}
469
+ }
470
+ };
471
+ const onVisibilityChange = () => {
472
+ if (document.visibilityState === "hidden") flushBeacon();
473
+ };
474
+ document.addEventListener("visibilitychange", onVisibilityChange);
475
+ window.addEventListener("pagehide", flushBeacon);
476
+ const enqueue = (event) => {
477
+ queue.push({
478
+ event,
479
+ attempts: 0
480
+ });
481
+ trimQueue();
482
+ if (queue.length >= maxBatchSize) flush();
483
+ else schedule(flushIntervalMs);
484
+ };
485
+ const forgetStoredIds = () => {
486
+ identity.clear();
487
+ if (clickStore.get(CLICK_ID_STORAGE_KEY) !== null) clickStore.remove(CLICK_ID_STORAGE_KEY);
488
+ };
489
+ const captureClickId = () => {
490
+ const clickId = readClickIdFromUrl(location.href);
491
+ if (clickId && consent() === "granted") clickStore.set(CLICK_ID_STORAGE_KEY, JSON.stringify({
492
+ id: clickId,
493
+ ts: Date.now()
494
+ }));
495
+ return clickId;
496
+ };
497
+ const onConsentChange = () => {
498
+ if (consent() === "granted") captureClickId();
499
+ else forgetStoredIds();
500
+ };
501
+ window.addEventListener(CONSENT_CHANGE_EVENT, onConsentChange);
502
+ const track = (name, props) => {
503
+ if (closed) return null;
504
+ let snapshot;
505
+ try {
506
+ snapshot = JSON.parse(JSON.stringify(props ?? {}));
507
+ } catch (error) {
508
+ log("props no serializables: evento descartado", name, error);
509
+ return null;
510
+ }
511
+ const state = consent();
512
+ const granted = state === "granted";
513
+ if (!granted) forgetStoredIds();
514
+ const ids = granted ? identity.ensure() : null;
515
+ const event = {
516
+ id: uuid(),
517
+ name,
518
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
519
+ props: snapshot,
520
+ ctx: collectContext(allowlist),
521
+ wl,
522
+ consent: state,
523
+ anon_id: ids?.anonId ?? null,
524
+ session_id: ids?.sessionId ?? null,
525
+ user_id: granted ? userId : null
526
+ };
527
+ log("track", event.name, event);
528
+ enqueue(event);
529
+ return event.id;
530
+ };
531
+ return {
532
+ track,
533
+ observeImpression(element, name, props, options) {
534
+ if (closed) return () => {};
535
+ return impressions.observe(element, () => track(name, props), options);
536
+ },
537
+ trackAdClick(props, destination) {
538
+ const clickId = uuid();
539
+ track("ad_click", {
540
+ ...props,
541
+ click_id: clickId,
542
+ destination_url: destination.slice(0, MAX_DESTINATION_URL_LENGTH)
543
+ });
544
+ return withClickId(destination, clickId);
545
+ },
546
+ captureClickId,
547
+ getClickId({ maxAgeDays = 7 } = {}) {
548
+ const fromUrl = readClickIdFromUrl(location.href);
549
+ if (fromUrl) return fromUrl;
550
+ if (consent() !== "granted") return null;
551
+ try {
552
+ const stored = JSON.parse(clickStore.get(CLICK_ID_STORAGE_KEY) ?? "null");
553
+ if (stored && isClickId(stored.id) && typeof stored.ts === "number" && Date.now() - stored.ts <= maxAgeDays * DAY_MS) return stored.id;
554
+ } catch {}
555
+ return null;
556
+ },
557
+ identify(id) {
558
+ userId = id === null ? null : String(id);
559
+ },
560
+ setWl(next) {
561
+ wl = next;
562
+ },
563
+ reset() {
564
+ userId = null;
565
+ forgetStoredIds();
566
+ },
567
+ flush,
568
+ shutdown() {
569
+ if (closed) return;
570
+ flushBeacon();
571
+ closed = true;
572
+ impressions.disconnect();
573
+ document.removeEventListener("visibilitychange", onVisibilityChange);
574
+ window.removeEventListener("pagehide", flushBeacon);
575
+ window.removeEventListener(CONSENT_CHANGE_EVENT, onConsentChange);
576
+ }
577
+ };
578
+ }
579
+ export { CLICK_ID_PARAM, CONSENT_CHANGE_EVENT, DEFAULT_CONSENT_COOKIE, DEFAULT_URL_PARAM_ALLOWLIST, SDK_NAME, SDK_VERSION, cookieConsent, createNoopTracker, createTracker, fetchTransport, isClickId, uuid as newId, parseConsentValue, readClickIdFromUrl, sanitizeUrl, withClickId };
@@ -0,0 +1,4 @@
1
+ // src/version.ts
2
+ const SDK_NAME = "@inspira-npm/events";
3
+ const SDK_VERSION = "0.1.0";
4
+ export { SDK_VERSION as n, SDK_NAME as t };
package/package.json CHANGED
@@ -1,6 +1,37 @@
1
1
  {
2
2
  "name": "@inspira-npm/events",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "SDK de tracking de eventos de Inspira (ads, cards, mapas, búsquedas) para navegador.",
5
+ "license": "UNLICENSED",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "files": [
9
+ "dist",
10
+ "!dist/**/*.map"
11
+ ],
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ },
17
+ "./catalog": {
18
+ "types": "./dist/catalog.d.ts",
19
+ "import": "./dist/catalog.js"
20
+ },
21
+ "./catalog.json": "./dist/catalog.json",
22
+ "./package.json": "./package.json"
23
+ },
24
+ "types": "./dist/index.d.ts",
25
+ "scripts": {
26
+ "build": "tsdown && node scripts/build-catalog.mjs && node scripts/build-catalog-docs.mjs",
27
+ "test": "vitest run",
28
+ "typecheck": "tsc -p tsconfig.json",
29
+ "prepublishOnly": "npm run typecheck && npm run test && npm run build"
30
+ },
31
+ "dependencies": {
32
+ "zod": "^4.6.7"
33
+ },
34
+ "publishConfig": {
35
+ "access": "public"
36
+ }
37
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.