pulse-updates 1.3.7 → 1.3.9
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 +35 -2
- package/android/src/main/java/app/pulse/updates/PulseController.kt +29 -0
- package/ios/PulseUpdates/PulseController.swift +10 -0
- package/lib/commonjs/config.js.map +1 -1
- package/lib/commonjs/init.js +13 -1
- package/lib/commonjs/init.js.map +1 -1
- package/lib/commonjs/links.js +698 -73
- package/lib/commonjs/links.js.map +1 -1
- package/lib/commonjs/track.js +198 -75
- package/lib/commonjs/track.js.map +1 -1
- package/lib/module/config.js.map +1 -1
- package/lib/module/init.js +14 -2
- package/lib/module/init.js.map +1 -1
- package/lib/module/links.js +697 -73
- package/lib/module/links.js.map +1 -1
- package/lib/module/track.js +197 -75
- package/lib/module/track.js.map +1 -1
- package/lib/typescript/config.d.ts +6 -0
- package/lib/typescript/config.d.ts.map +1 -1
- package/lib/typescript/init.d.ts +1 -0
- package/lib/typescript/init.d.ts.map +1 -1
- package/lib/typescript/links.d.ts +63 -0
- package/lib/typescript/links.d.ts.map +1 -1
- package/lib/typescript/track.d.ts +17 -6
- package/lib/typescript/track.d.ts.map +1 -1
- package/package.json +4 -2
- package/scripts/publish.mjs +227 -16
- package/src/config.ts +6 -0
- package/src/init.ts +16 -1
- package/src/links.ts +983 -51
- package/src/track.ts +283 -81
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"links.d.ts","sourceRoot":"","sources":["../../src/links.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"links.d.ts","sourceRoot":"","sources":["../../src/links.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,MAAM,MAAM,kBAAkB,GAC1B,WAAW,GACX,cAAc,GACd,qBAAqB,GACrB,qBAAqB,CAAC;AAE1B,MAAM,MAAM,sBAAsB,GAC9B,cAAc,GACd,0BAA0B,GAC1B,gBAAgB,GAChB,eAAe,GACf,sBAAsB,GACtB,WAAW,CAAC;AAEhB,MAAM,MAAM,wBAAwB,GAAG,OAAO,CAC5C,sBAAsB,EACtB,cAAc,GAAG,0BAA0B,GAAG,gBAAgB,CAC/D,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAC1B,MAAM,GACN,SAAS,GACT,WAAW,GACX,qBAAqB,GACrB,iBAAiB,GACjB,SAAS,GACT,gBAAgB,CAAC;AAErB,MAAM,MAAM,uBAAuB,GAC/B,oBAAoB,GACpB,wBAAwB,GACxB,gBAAgB,CAAC;AAErB,MAAM,WAAW,oBAAoB,CAAC,MAAM,SAAS,MAAM,GAAG,kBAAkB;IAC9E,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,sBAAsB,CAAC;IACnC,eAAe,EAAE,OAAO,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,wBAAwB;IACvC,8FAA8F;IAC9F,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,wBAAwB,CAAC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,0BAA0B;IACzC,gGAAgG;IAChG,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,wBAAwB,GAAG,IAAI,CAAC;IACzC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,4CAA4C;IAC5C,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,SAAS,EAAE,0BAA0B,CAAC;CACvC;AAED,MAAM,WAAW,yBAAyB,CAAC,MAAM,SAAS,MAAM,GAAG,kBAAkB;IACnF,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;IACpE,OAAO,IAAI,OAAO,CAAC,oBAAoB,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;CACzD;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,uBAAuB,CAAC;IAC9B,UAAU,EAAE,sBAAsB,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,4BAA4B,GACpC,OAAO,GACP,QAAQ,GACR,IAAI,GACJ,SAAS,GACT,gBAAgB,GAChB,SAAS,CAAC;AAEd,MAAM,WAAW,yBAAyB;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,KAAK,CAAC;IAChB,yFAAyF;IACzF,WAAW,EAAE,MAAM,CAAC;IACpB,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wFAAwF;IACxF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,4BAA4B,CAAC;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sFAAsF;IACtF,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,wBAAwB,GAChC,SAAS,GACT,UAAU,GACV,YAAY,GACZ,uBAAuB,GACvB,mBAAmB,GACnB,SAAS,GACT,iBAAiB,GACjB,gBAAgB,CAAC;AAErB,MAAM,MAAM,gCAAgC,GAAG,OAAO,GAAG,WAAW,GAAG,SAAS,CAAC;AAEjF,oEAAoE;AACpE,MAAM,MAAM,qCAAqC,GAAG,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC;AAElF,MAAM,MAAM,gCAAgC,GACxC,SAAS,GACT,WAAW,GACX,WAAW,GACX,SAAS,GACT,gBAAgB,GAChB,oBAAoB,GACpB,mBAAmB,GACnB,sBAAsB,GACtB,gBAAgB,GAChB,UAAU,GACV,gBAAgB,GAChB,iBAAiB,GACjB,kBAAkB,GAClB,iBAAiB,CAAC;AAEtB;;;;GAIG;AACH,MAAM,WAAW,iCAAiC;IAChD,+EAA+E;IAC/E,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,gCAAgC,CAAC;IACzC,IAAI,EAAE,YAAY,GAAG,UAAU,CAAC;IAChC,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,KAAK,CAAC;IACjB,MAAM,EAAE,gCAAgC,CAAC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,qFAAqF;IACrF,UAAU,CAAC,EAAE,sBAAsB,CAAC;IACpC,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6FAA6F;IAC7F,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,MAAM,4BAA4B,GACpC,IAAI,GACJ,UAAU,GACV,uBAAuB,GACvB,qBAAqB,GACrB,iBAAiB,GACjB,sBAAsB,CAAC;AAE3B,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,4BAA4B,CAAC;IACrC,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,oEAAoE;AACpE,MAAM,WAAW,4BAA4B;IAC3C,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,yBAAyB,CAAC,MAAM,SAAS,MAAM,GAAG,kBAAkB;IACnF,qFAAqF;IACrF,eAAe,EAAE,MAAM,CAAC;IACxB,4EAA4E;IAC5E,uBAAuB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,oEAAoE;IACpE,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,sFAAsF;IACtF,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC;;;;OAIG;IACH,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,CAAC,MAAM,CAAC,KAAK,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAC/G,qDAAqD;IACrD,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,CAAC,MAAM,CAAC,KAAK,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAC5F,aAAa,CAAC,EAAE,yBAAyB,CAAC,MAAM,CAAC,CAAC;IAClD,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC;IAC/B,6FAA6F;IAC7F,qBAAqB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IAC/D,+DAA+D;IAC/D,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,sGAAsG;IACtG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6FAA6F;IAC7F,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE;;;OAGG;IACH,0BAA0B,CAAC,EAAE,MAAM,OAAO,CAAC;IAC3C;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,CAClB,OAAO,EAAE,iCAAiC,KACvC,qCAAqC,GACtC,OAAO,GACP,OAAO,CAAC,qCAAqC,GAAG,OAAO,CAAC,CAAC;IAC7D,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACnD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,MAAM,CAAC;IAC1B,iGAAiG;IACjG,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;CAC5C;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC;IACjE,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,cAAc,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;IACtF,6BAA6B,CAAC,MAAM,EAAE,4BAA4B,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAAC;IAC3G,QAAQ,IAAI,iBAAiB,CAAC;IAC9B,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACpE,OAAO,IAAI,IAAI,CAAC;IAChB,mGAAmG;IACnG,KAAK,IAAI,IAAI,CAAC;CACf;AA0KD,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEzE;AAED,wBAAgB,6BAA6B,CAC3C,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC9B,gBAAgB,GAAE,SAAS,MAAM,EAAqB,GACrD,MAAM,GAAG,IAAI,CAUf;AAED,qFAAqF;AACrF,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA4BtE;AAED,4FAA4F;AAC5F,wBAAgB,qCAAqC,CAAC,GAAG,EAAE,OAAO,GAAG,4BAA4B,CAqBhG;AAED,wBAAgB,qBAAqB,CAAC,MAAM,SAAS,MAAM,GAAG,kBAAkB,EAC9E,OAAO,EAAE,yBAAyB,CAAC,MAAM,CAAC,GACzC,kBAAkB,CAm0CpB;AAED,0EAA0E;AAC1E,eAAO,MAAM,wBAAwB,8BAAwB,CAAC;AA6O9D;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAOnF"}
|
|
@@ -45,7 +45,7 @@ export interface TrackOptions {
|
|
|
45
45
|
flushIntervalMs?: number;
|
|
46
46
|
/** Events per request. The server refuses more than 100. */
|
|
47
47
|
maxBatch?: number;
|
|
48
|
-
/**
|
|
48
|
+
/** Best-effort events held before the oldest are dropped. Durable idempotent rows are never evicted. Default 500. */
|
|
49
49
|
maxQueue?: number;
|
|
50
50
|
/** Persists the outbox across process death. Use the same storage passed to initPulse. */
|
|
51
51
|
storage?: ConfigStorage;
|
|
@@ -53,9 +53,12 @@ export interface TrackOptions {
|
|
|
53
53
|
storageKey?: string;
|
|
54
54
|
/** Per-request timeout. Default 10s. */
|
|
55
55
|
timeoutMs?: number;
|
|
56
|
-
/** Maximum delivery attempts before
|
|
56
|
+
/** Maximum delivery attempts before a best-effort event is dropped. Durable idempotent events retry until server ACK. Default 10. */
|
|
57
57
|
maxAttempts?: number;
|
|
58
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* Return false until analytics consent exists. No event is retained before consent. Consent is
|
|
60
|
+
* sampled at enqueue; an event already durably accepted remains eligible for later delivery.
|
|
61
|
+
*/
|
|
59
62
|
hasConsent?: () => boolean;
|
|
60
63
|
/** Only these property keys may leave the process. */
|
|
61
64
|
propertyAllowlist?: readonly string[];
|
|
@@ -84,12 +87,20 @@ export declare function configureTracking(opts: TrackOptions): void;
|
|
|
84
87
|
* caller has to wrap.
|
|
85
88
|
*/
|
|
86
89
|
export declare function track(event: string, props?: TrackedEventInput['props'], time?: Date): void;
|
|
90
|
+
/**
|
|
91
|
+
* Durably enqueue one logical event with a caller-owned UUID. Replaying the same id is safe:
|
|
92
|
+
* Pulse's server/read model deduplicates it, while the local outbox avoids duplicate rows that
|
|
93
|
+
* are still pending. `true` means the event is present in persistent storage, not delivered yet;
|
|
94
|
+
* once acknowledged, Track keeps it in that outbox through retries and process restarts until a
|
|
95
|
+
* successful server response removes it transactionally.
|
|
96
|
+
*/
|
|
97
|
+
export declare function trackIdempotent(eventId: string, event: string, props?: TrackedEventInput['props'], time?: Date): boolean;
|
|
87
98
|
/**
|
|
88
99
|
* Send what is queued. Returns how many events the server accepted.
|
|
89
100
|
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* after
|
|
101
|
+
* The batch remains in the durable queue while HTTP is in flight and is removed only after a
|
|
102
|
+
* successful response is also persisted locally. This intentionally permits duplicate replay
|
|
103
|
+
* after a crash, while a caller-owned event id lets the server/read model deduplicate it.
|
|
93
104
|
*/
|
|
94
105
|
export declare function flushEvents(): Promise<number>;
|
|
95
106
|
/** How many events are waiting — for a caller that wants to flush before backgrounding. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"track.d.ts","sourceRoot":"","sources":["../../src/track.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE7D,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,aAAa,CAAC;IAEjC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,SAAS,GAAG,wBAAwB,CAAC;IAEpD,wDAAwD;IACxD,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,
|
|
1
|
+
{"version":3,"file":"track.d.ts","sourceRoot":"","sources":["../../src/track.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE7D,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,aAAa,CAAC;IAEjC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,SAAS,GAAG,wBAAwB,CAAC;IAEpD,wDAAwD;IACxD,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,qHAAqH;IACrH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,0FAA0F;IAC1F,OAAO,CAAC,EAAE,aAAa,CAAC;IAExB,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,qIAAqI;IACrI,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC;IAE3B,sDAAsD;IACtD,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAEtC,+DAA+D;IAC/D,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAErC,0EAA0E;IAC1E,QAAQ,CAAC,EAAE;QAAE,gBAAgB,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,KAAK;YAAE,MAAM,EAAE,MAAM,IAAI,CAAA;SAAE,CAAA;KAAE,CAAC;IAE9G,+DAA+D;IAC/D,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAEvC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,cAAc,GAAG,YAAY,KAAK,IAAI,CAAC;IAE1F,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACpC;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACrE,IAAI,CAAC,EAAE,IAAI,CAAC;CACb;AAuCD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAY1D;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAE1F;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAClC,IAAI,CAAC,EAAE,IAAI,GACV,OAAO,CAIT;AA2GD;;;;;;GAMG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CAiEnD;AAED,2FAA2F;AAC3F,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,eAAe,IAAI;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB,CAEA;AAED,qEAAqE;AACrE,wBAAgB,eAAe,IAAI,IAAI,CAStC;AAED,wBAAgB,YAAY,IAAI,IAAI,CAOnC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pulse-updates",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.9",
|
|
4
4
|
"description": "Pulse app-experience SDK for React Native: updates, config, experiments, events, decisions and first-party links",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -54,7 +54,9 @@
|
|
|
54
54
|
"react-native": ">=0.76.0"
|
|
55
55
|
},
|
|
56
56
|
"peerDependenciesMeta": {
|
|
57
|
-
"@expo/config-plugins": {
|
|
57
|
+
"@expo/config-plugins": {
|
|
58
|
+
"optional": true
|
|
59
|
+
}
|
|
58
60
|
},
|
|
59
61
|
"dependencies": {
|
|
60
62
|
"tweetnacl": "1.0.3"
|
package/scripts/publish.mjs
CHANGED
|
@@ -119,6 +119,122 @@ async function hashFileSHA256(filePath) {
|
|
|
119
119
|
});
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
const NATIVE_MODULE_EVIDENCE_VERSION = 1;
|
|
123
|
+
const NATIVE_MODULE_NAME = /^[\w.$-]{1,128}$/;
|
|
124
|
+
const SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
125
|
+
|
|
126
|
+
function validateNativeModules(value, source) {
|
|
127
|
+
if (!Array.isArray(value) || value.length > 4096) {
|
|
128
|
+
throw new Error(`Invalid native-module evidence in ${source}: nativeModules must be an array`);
|
|
129
|
+
}
|
|
130
|
+
if (!value.every((name) => typeof name === 'string' && NATIVE_MODULE_NAME.test(name))) {
|
|
131
|
+
throw new Error(`Invalid native-module evidence in ${source}: nativeModules contains an invalid name`);
|
|
132
|
+
}
|
|
133
|
+
const canonical = [...new Set(value)].sort();
|
|
134
|
+
if (canonical.length !== value.length || canonical.some((name, index) => name !== value[index])) {
|
|
135
|
+
throw new Error(`Invalid native-module evidence in ${source}: nativeModules must be sorted and unique`);
|
|
136
|
+
}
|
|
137
|
+
return [...value];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function validateEvidenceContext(value, config, source) {
|
|
141
|
+
if (value.schemaVersion !== NATIVE_MODULE_EVIDENCE_VERSION
|
|
142
|
+
|| value.platform !== config.platform
|
|
143
|
+
|| value.runtimeVersion !== config.runtimeVersion
|
|
144
|
+
|| value.channel !== config.channel) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
`Invalid native-module evidence in ${source}: release context does not match ` +
|
|
147
|
+
`${config.runtimeVersion}/${config.platform}/${config.channel}`
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
if (!SHA256_HEX.test(value.scannedBundleSha256 ?? '')
|
|
151
|
+
|| !SHA256_HEX.test(value.uploadBundleSha256 ?? '')) {
|
|
152
|
+
throw new Error(`Invalid native-module evidence in ${source}: bundle SHA-256 is missing or invalid`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Persist the capability scan beside a hash of the exact post-Hermes artifact. A wrapper that
|
|
158
|
+
* deliberately transforms that artifact (for example Bastion) must first verify
|
|
159
|
+
* scannedBundleSha256, then replace uploadBundleSha256 with the transformed artifact hash. The
|
|
160
|
+
* --skip-bundle pass verifies the latter before it creates anything server-side.
|
|
161
|
+
*/
|
|
162
|
+
export async function writeNativeModulesEvidence(evidencePath, config, bundlePath, nativeModules) {
|
|
163
|
+
const resolved = path.resolve(evidencePath);
|
|
164
|
+
const canonicalModules = validateNativeModules(nativeModules, resolved);
|
|
165
|
+
const bundleSha256 = await hashFileSHA256(bundlePath);
|
|
166
|
+
const evidence = {
|
|
167
|
+
schemaVersion: NATIVE_MODULE_EVIDENCE_VERSION,
|
|
168
|
+
platform: config.platform,
|
|
169
|
+
runtimeVersion: config.runtimeVersion,
|
|
170
|
+
channel: config.channel,
|
|
171
|
+
scannedBundleSha256: bundleSha256,
|
|
172
|
+
uploadBundleSha256: bundleSha256,
|
|
173
|
+
nativeModules: canonicalModules,
|
|
174
|
+
};
|
|
175
|
+
fs.mkdirSync(path.dirname(resolved), { recursive: true });
|
|
176
|
+
const temporary = `${resolved}.${process.pid}.${crypto.randomBytes(4).toString('hex')}.tmp`;
|
|
177
|
+
try {
|
|
178
|
+
fs.writeFileSync(temporary, `${JSON.stringify(evidence)}\n`, { mode: 0o600 });
|
|
179
|
+
fs.renameSync(temporary, resolved);
|
|
180
|
+
} finally {
|
|
181
|
+
// renameSync removes the source on success. On every failure path, including a destination
|
|
182
|
+
// that becomes invalid concurrently, do not strand capability evidence in a readable temp file.
|
|
183
|
+
try { fs.unlinkSync(temporary); } catch (error) {
|
|
184
|
+
if (error?.code !== 'ENOENT') throw error;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return evidence;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Read and bind capability evidence to the exact artifact about to be uploaded. */
|
|
191
|
+
export async function readNativeModulesEvidence(evidencePath, config, bundlePath) {
|
|
192
|
+
const resolved = path.resolve(evidencePath);
|
|
193
|
+
let evidence;
|
|
194
|
+
try {
|
|
195
|
+
evidence = JSON.parse(fs.readFileSync(resolved, 'utf8'));
|
|
196
|
+
} catch (error) {
|
|
197
|
+
throw new Error(`Native-module evidence is missing or unreadable at ${resolved}: ${error.message}`);
|
|
198
|
+
}
|
|
199
|
+
if (!evidence || typeof evidence !== 'object' || Array.isArray(evidence)) {
|
|
200
|
+
throw new Error(`Invalid native-module evidence in ${resolved}: expected an object`);
|
|
201
|
+
}
|
|
202
|
+
validateEvidenceContext(evidence, config, resolved);
|
|
203
|
+
const nativeModules = validateNativeModules(evidence.nativeModules, resolved);
|
|
204
|
+
const uploadBundleSha256 = await hashFileSHA256(bundlePath);
|
|
205
|
+
if (uploadBundleSha256 !== evidence.uploadBundleSha256) {
|
|
206
|
+
throw new Error(
|
|
207
|
+
`Native-module evidence does not describe the upload artifact: expected ` +
|
|
208
|
+
`${evidence.uploadBundleSha256}, got ${uploadBundleSha256}`
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
return { ...evidence, nativeModules };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function createReleaseRequest(config, metadata, nativeModules, nativeModulesVerified) {
|
|
215
|
+
return {
|
|
216
|
+
runtimeVersion: config.runtimeVersion,
|
|
217
|
+
platform: config.platform,
|
|
218
|
+
channel: config.channel,
|
|
219
|
+
metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
|
|
220
|
+
// A verified empty array is evidence too. Legacy --skip-bundle calls without the handoff file
|
|
221
|
+
// retain their old omitted-field behaviour, while guarded wrappers cannot silently bypass ABI
|
|
222
|
+
// preflight by turning a real scan into an absent fingerprint.
|
|
223
|
+
nativeModules: nativeModulesVerified
|
|
224
|
+
? nativeModules
|
|
225
|
+
: (nativeModules.length ? nativeModules : undefined),
|
|
226
|
+
// Which binary this bundle is for, when it is only for one. A runtime version is not always a
|
|
227
|
+
// single build — on iOS it is MARKETING_VERSION, which stays put across re-submissions — and a
|
|
228
|
+
// bundle wrapped with one build's key cannot be opened by another, which stops that app from
|
|
229
|
+
// starting rather than merely leaving it un-updated. Naming the build keeps the manifest from
|
|
230
|
+
// handing it to the wrong one. Omitted for unwrapped bundles, which every build can load.
|
|
231
|
+
// Spread rather than assign: an explicit `nativeBuild: undefined` is invisible over the wire
|
|
232
|
+
// but not to a caller comparing the request it built, and unwrapped bundles have no build to
|
|
233
|
+
// name.
|
|
234
|
+
...(config.nativeBuild ? { nativeBuild: config.nativeBuild } : {}),
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
122
238
|
/**
|
|
123
239
|
* Calculate MD5 hash of a file (for asset key - matches Metro/expo format)
|
|
124
240
|
*/
|
|
@@ -826,6 +942,10 @@ function loadConfig(options) {
|
|
|
826
942
|
apiKey: options['api-key'] || process.env.PULSE_API_KEY || fileConfig.apiKey,
|
|
827
943
|
runtimeVersion,
|
|
828
944
|
platform,
|
|
945
|
+
// Only ever what the operator states. It could be read from the pbxproj or build.gradle, but a
|
|
946
|
+
// guess here is the failure it exists to prevent: the checkout is not reliably the build the
|
|
947
|
+
// update is for, which is the whole reason a runtime version is not enough on its own.
|
|
948
|
+
nativeBuild: options['native-build'] || process.env.PULSE_NATIVE_BUILD || fileConfig.nativeBuild,
|
|
829
949
|
channel: options.channel || fileConfig.channel || 'production',
|
|
830
950
|
bundleDir: options['bundle-dir'] || fileConfig.bundleDir || './dist',
|
|
831
951
|
entryFile: options['entry-file'] || fileConfig.entryFile || 'index.ts',
|
|
@@ -844,7 +964,7 @@ function loadConfig(options) {
|
|
|
844
964
|
/**
|
|
845
965
|
* Main publish command
|
|
846
966
|
*/
|
|
847
|
-
async function publish(options) {
|
|
967
|
+
export async function publish(options) {
|
|
848
968
|
const config = loadConfig(options);
|
|
849
969
|
|
|
850
970
|
// Validate required config
|
|
@@ -873,25 +993,60 @@ async function publish(options) {
|
|
|
873
993
|
log(`Bundle Dir: ${bundleDir}\n`);
|
|
874
994
|
|
|
875
995
|
let nativeModules = [];
|
|
996
|
+
let nativeModulesVerified = false;
|
|
997
|
+
const nativeModulesFileOption = options['native-modules-file'];
|
|
998
|
+
if (nativeModulesFileOption !== undefined
|
|
999
|
+
&& (typeof nativeModulesFileOption !== 'string' || !nativeModulesFileOption.trim())) {
|
|
1000
|
+
throw new Error('--native-modules-file requires a path');
|
|
1001
|
+
}
|
|
1002
|
+
const nativeModulesFile = nativeModulesFileOption
|
|
1003
|
+
? path.resolve(nativeModulesFileOption)
|
|
1004
|
+
: null;
|
|
1005
|
+
const bundlePath = path.join(bundleDir, config.platform, 'index.bundle');
|
|
876
1006
|
|
|
877
1007
|
// Step 1: Create bundle (unless skipped)
|
|
878
1008
|
if (!config.skipBundle) {
|
|
879
1009
|
logStep('1/6', 'Creating bundle...');
|
|
880
|
-
const
|
|
881
|
-
logSuccess(`Bundle created: ${
|
|
1010
|
+
const createdBundlePath = createBundle(config.platform, bundleDir, config.entryFile);
|
|
1011
|
+
logSuccess(`Bundle created: ${createdBundlePath}`);
|
|
882
1012
|
|
|
883
1013
|
// Crash-prediction fingerprint: extract native module references from the plain JS bundle
|
|
884
1014
|
// (must happen BEFORE Hermes bytecode compilation, while the bundle is still readable JS).
|
|
885
|
-
nativeModules = extractNativeModules(
|
|
1015
|
+
nativeModules = extractNativeModules(createdBundlePath);
|
|
1016
|
+
nativeModulesVerified = true;
|
|
886
1017
|
log(`Native modules referenced: ${nativeModules.length}${nativeModules.length ? ` (${nativeModules.join(', ')})` : ''}`);
|
|
887
1018
|
|
|
888
1019
|
// Step 2: Compile with Hermes
|
|
889
1020
|
logStep('2/6', 'Compiling with Hermes...');
|
|
890
|
-
compileWithHermes(
|
|
1021
|
+
compileWithHermes(createdBundlePath, config.platform, { requireHermes: config.requireHermes });
|
|
891
1022
|
logSuccess('Hermes compilation complete');
|
|
1023
|
+
if (nativeModulesFile) {
|
|
1024
|
+
await writeNativeModulesEvidence(
|
|
1025
|
+
nativeModulesFile,
|
|
1026
|
+
config,
|
|
1027
|
+
createdBundlePath,
|
|
1028
|
+
nativeModules,
|
|
1029
|
+
);
|
|
1030
|
+
log(`Native-module evidence written: ${nativeModulesFile}`);
|
|
1031
|
+
}
|
|
892
1032
|
} else {
|
|
893
1033
|
logStep('1/6', 'Skipping bundle creation (--skip-bundle)');
|
|
894
1034
|
logStep('2/6', 'Skipping Hermes compilation');
|
|
1035
|
+
if (nativeModulesFile) {
|
|
1036
|
+
const evidence = await readNativeModulesEvidence(
|
|
1037
|
+
nativeModulesFile,
|
|
1038
|
+
config,
|
|
1039
|
+
bundlePath,
|
|
1040
|
+
);
|
|
1041
|
+
nativeModules = evidence.nativeModules;
|
|
1042
|
+
nativeModulesVerified = true;
|
|
1043
|
+
log(`Native-module evidence verified: ${nativeModules.length}${nativeModules.length ? ` (${nativeModules.join(', ')})` : ''}`);
|
|
1044
|
+
} else {
|
|
1045
|
+
logWarning(
|
|
1046
|
+
'--skip-bundle has no --native-modules-file; preserving legacy behaviour without a ' +
|
|
1047
|
+
'capability fingerprint. Guarded release wrappers should fail before reaching this path.'
|
|
1048
|
+
);
|
|
1049
|
+
}
|
|
895
1050
|
}
|
|
896
1051
|
|
|
897
1052
|
// Step 3: Collect assets and calculate hashes
|
|
@@ -929,13 +1084,12 @@ async function publish(options) {
|
|
|
929
1084
|
'Content-Type': 'application/json',
|
|
930
1085
|
'X-API-Key': config.apiKey,
|
|
931
1086
|
},
|
|
932
|
-
body: JSON.stringify(
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
}),
|
|
1087
|
+
body: JSON.stringify(createReleaseRequest(
|
|
1088
|
+
config,
|
|
1089
|
+
metadata,
|
|
1090
|
+
nativeModules,
|
|
1091
|
+
nativeModulesVerified,
|
|
1092
|
+
)),
|
|
939
1093
|
});
|
|
940
1094
|
|
|
941
1095
|
if (!createResponse.ok) {
|
|
@@ -963,13 +1117,63 @@ async function publish(options) {
|
|
|
963
1117
|
// modules that weren't in the previous good release for this runtime version — native code can't ship
|
|
964
1118
|
// over the air, so it would crash the installed cohort. Override with --allow-native-change.
|
|
965
1119
|
let preflight = null;
|
|
1120
|
+
// Any verified capability scan (a direct build or a two-pass evidence handoff) requires a valid
|
|
1121
|
+
// server answer: an unavailable or malformed preflight is not evidence that the OTA is safe, so
|
|
1122
|
+
// it must stop before any asset upload/finalize. Only legacy --skip-bundle without evidence has
|
|
1123
|
+
// no capability set to enforce; --no-native-check remains the one explicit escape hatch.
|
|
1124
|
+
const requiresVerifiedPreflight = nativeModulesVerified && !options['no-native-check'];
|
|
1125
|
+
let pfRes = null;
|
|
966
1126
|
try {
|
|
967
|
-
|
|
1127
|
+
pfRes = await fetch(`${config.apiUrl}/api/releases/${release.id}/preflight`, {
|
|
968
1128
|
headers: { 'X-API-Key': config.apiKey },
|
|
969
1129
|
});
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1130
|
+
} catch (error) {
|
|
1131
|
+
if (requiresVerifiedPreflight) {
|
|
1132
|
+
throw new Error(
|
|
1133
|
+
`Native ABI preflight could not reach the server; refusing capability-verified publish: ${
|
|
1134
|
+
error instanceof Error ? error.message : String(error)
|
|
1135
|
+
}`,
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1138
|
+
logWarning('Preflight check could not run (server unreachable); continuing legacy publish.');
|
|
1139
|
+
}
|
|
1140
|
+
if (pfRes && !pfRes.ok) {
|
|
1141
|
+
if (requiresVerifiedPreflight) {
|
|
1142
|
+
throw new Error(`Native ABI preflight failed with HTTP ${pfRes.status}; refusing capability-verified publish.`);
|
|
1143
|
+
}
|
|
1144
|
+
logWarning(`Preflight check returned HTTP ${pfRes.status}; continuing legacy publish.`);
|
|
1145
|
+
}
|
|
1146
|
+
if (pfRes?.ok) {
|
|
1147
|
+
let decodedPreflight = false;
|
|
1148
|
+
try {
|
|
1149
|
+
preflight = await pfRes.json();
|
|
1150
|
+
decodedPreflight = true;
|
|
1151
|
+
} catch (error) {
|
|
1152
|
+
if (requiresVerifiedPreflight) {
|
|
1153
|
+
throw new Error(
|
|
1154
|
+
`Native ABI preflight returned invalid JSON; refusing capability-verified publish: ${
|
|
1155
|
+
error instanceof Error ? error.message : String(error)
|
|
1156
|
+
}`,
|
|
1157
|
+
);
|
|
1158
|
+
}
|
|
1159
|
+
logWarning('Preflight check returned invalid JSON; continuing legacy publish.');
|
|
1160
|
+
}
|
|
1161
|
+
const validPreflight = decodedPreflight
|
|
1162
|
+
&& preflight !== null
|
|
1163
|
+
&& typeof preflight === 'object'
|
|
1164
|
+
&& !Array.isArray(preflight)
|
|
1165
|
+
&& typeof preflight.ok === 'boolean'
|
|
1166
|
+
&& Array.isArray(preflight.warnings)
|
|
1167
|
+
&& preflight.warnings.every((warning) => typeof warning === 'string')
|
|
1168
|
+
&& Array.isArray(preflight.newNativeModules)
|
|
1169
|
+
&& preflight.newNativeModules.every((moduleName) => typeof moduleName === 'string');
|
|
1170
|
+
if (decodedPreflight && !validPreflight) {
|
|
1171
|
+
preflight = null;
|
|
1172
|
+
if (requiresVerifiedPreflight) {
|
|
1173
|
+
throw new Error('Native ABI preflight returned an invalid response schema; refusing capability-verified publish.');
|
|
1174
|
+
}
|
|
1175
|
+
logWarning('Preflight check returned an invalid response schema; continuing legacy publish.');
|
|
1176
|
+
}
|
|
973
1177
|
}
|
|
974
1178
|
if (preflight) {
|
|
975
1179
|
for (const w of preflight.warnings || []) log(`⚠ ${w}`);
|
|
@@ -1456,8 +1660,15 @@ ${colors.cyan}Options:${colors.reset}
|
|
|
1456
1660
|
--channel <channel> Release channel (default: production)
|
|
1457
1661
|
--bundle-dir <dir> Output directory (default: ./dist)
|
|
1458
1662
|
--bundle <file> Embedded bundle for register-capabilities (otherwise built automatically)
|
|
1663
|
+
--native-build <n> The build this bundle is for (iOS CFBundleVersion, Android versionCode).
|
|
1664
|
+
Required when the bundle is wrapped with a per-build key: without it the
|
|
1665
|
+
manifest serves the bundle to every build sharing the runtime version,
|
|
1666
|
+
and the ones holding a different key cannot start.
|
|
1459
1667
|
--entry-file <file> Entry file (default: index.ts)
|
|
1460
1668
|
--skip-bundle Skip bundle creation (use existing)
|
|
1669
|
+
--native-modules-file <file>
|
|
1670
|
+
Capability evidence handoff for a two-pass/--skip-bundle publish. The
|
|
1671
|
+
build pass writes it; the upload pass validates its context and SHA-256.
|
|
1461
1672
|
--dry-run Build + validate locally without creating a release on the server
|
|
1462
1673
|
--allow-native-change Publish even though new native modules were detected (you certify they're
|
|
1463
1674
|
already in the installed binary). Default is FAIL-CLOSED: a new native module
|
package/src/config.ts
CHANGED
|
@@ -50,6 +50,12 @@ export type ConfigSignatureStatus = 'not-configured' | 'verified' | 'cached-veri
|
|
|
50
50
|
export interface ConfigStorage {
|
|
51
51
|
getString(key: string): string | null | undefined;
|
|
52
52
|
set(key: string, value: string): void;
|
|
53
|
+
/**
|
|
54
|
+
* False when `set` only mirrors a write and schedules asynchronous persistence. Durable
|
|
55
|
+
* acknowledgement APIs must fail closed because process death may still lose that write.
|
|
56
|
+
* Omitted means the historical synchronous-storage contract.
|
|
57
|
+
*/
|
|
58
|
+
readonly supportsDurableSyncWrites?: boolean;
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
/**
|
package/src/init.ts
CHANGED
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
flushEvents,
|
|
46
46
|
getTrackingInfo,
|
|
47
47
|
track,
|
|
48
|
+
trackIdempotent,
|
|
48
49
|
type TrackedEventInput,
|
|
49
50
|
} from './track';
|
|
50
51
|
|
|
@@ -140,6 +141,12 @@ export interface PulseClient {
|
|
|
140
141
|
configUrl: string;
|
|
141
142
|
trackUrl: string;
|
|
142
143
|
track: (event: string, props?: TrackedEventInput['props'], time?: Date) => void;
|
|
144
|
+
trackIdempotent: (
|
|
145
|
+
eventId: string,
|
|
146
|
+
event: string,
|
|
147
|
+
props?: TrackedEventInput['props'],
|
|
148
|
+
time?: Date,
|
|
149
|
+
) => boolean;
|
|
143
150
|
flush: () => Promise<number>;
|
|
144
151
|
setUser: (userId?: string, attributes?: Record<string, string>) => void;
|
|
145
152
|
clearUser: () => void;
|
|
@@ -261,6 +268,7 @@ export function initPulse(opts: InitPulseOptions): PulseClient {
|
|
|
261
268
|
configUrl,
|
|
262
269
|
trackUrl,
|
|
263
270
|
track,
|
|
271
|
+
trackIdempotent,
|
|
264
272
|
flush: flushEvents,
|
|
265
273
|
setUser: (nextUserId, attributes) => {
|
|
266
274
|
userId = nextUserId?.trim() || undefined;
|
|
@@ -328,10 +336,17 @@ export async function initPulseAsync(
|
|
|
328
336
|
const loaded = await Promise.all(keys.map(async (key) => [key, await opts.storage.getItem(key)] as const));
|
|
329
337
|
const memory = new Map(loaded.filter((entry): entry is readonly [string, string] => entry[1] !== null));
|
|
330
338
|
const storage: ConfigStorage = {
|
|
339
|
+
supportsDurableSyncWrites: false,
|
|
331
340
|
getString: (key) => memory.get(key),
|
|
332
341
|
set: (key, value) => {
|
|
333
342
|
memory.set(key, value);
|
|
334
|
-
|
|
343
|
+
try {
|
|
344
|
+
void Promise.resolve(opts.storage.setItem(key, value)).catch((error) => {
|
|
345
|
+
try { opts.onError?.(error); } catch { /* diagnostics never break persistence */ }
|
|
346
|
+
});
|
|
347
|
+
} catch (error) {
|
|
348
|
+
try { opts.onError?.(error); } catch { /* diagnostics never break persistence */ }
|
|
349
|
+
}
|
|
335
350
|
},
|
|
336
351
|
};
|
|
337
352
|
return initPulse({ ...opts, storage });
|