@rocapine/rocalytics-sdk 0.0.0-stage → 0.2.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/CHANGELOG.md +79 -0
- package/LICENSE +21 -0
- package/README.md +447 -2
- package/client/package.json +1 -0
- package/core/package.json +4 -0
- package/dist/client/client.d.ts +136 -0
- package/dist/client/client.d.ts.map +1 -0
- package/dist/client/client.js +373 -0
- package/dist/client/client.js.map +1 -0
- package/dist/client/index.d.ts +13 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +35 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/native.d.ts +68 -0
- package/dist/client/native.d.ts.map +1 -0
- package/dist/client/native.js +64 -0
- package/dist/client/native.js.map +1 -0
- package/dist/client/onboardingSink.d.ts +63 -0
- package/dist/client/onboardingSink.d.ts.map +1 -0
- package/dist/client/onboardingSink.js +126 -0
- package/dist/client/onboardingSink.js.map +1 -0
- package/dist/client/paywallSink.d.ts +13 -0
- package/dist/client/paywallSink.d.ts.map +1 -0
- package/dist/client/paywallSink.js +26 -0
- package/dist/client/paywallSink.js.map +1 -0
- package/dist/client/requests.d.ts +66 -0
- package/dist/client/requests.d.ts.map +1 -0
- package/dist/client/requests.js +90 -0
- package/dist/client/requests.js.map +1 -0
- package/dist/client/types.d.ts +180 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +4 -0
- package/dist/client/types.js.map +1 -0
- package/dist/core/context.d.ts +39 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +45 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/delivery.d.ts +63 -0
- package/dist/core/delivery.d.ts.map +1 -0
- package/dist/core/delivery.js +133 -0
- package/dist/core/delivery.js.map +1 -0
- package/dist/core/diagnostics.d.ts +16 -0
- package/dist/core/diagnostics.d.ts.map +1 -0
- package/dist/core/diagnostics.js +24 -0
- package/dist/core/diagnostics.js.map +1 -0
- package/dist/core/httpSink.d.ts +35 -0
- package/dist/core/httpSink.d.ts.map +1 -0
- package/dist/core/httpSink.js +65 -0
- package/dist/core/httpSink.js.map +1 -0
- package/dist/core/ids.d.ts +16 -0
- package/dist/core/ids.d.ts.map +1 -0
- package/dist/core/ids.js +51 -0
- package/dist/core/ids.js.map +1 -0
- package/dist/core/index.d.ts +17 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +32 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/sink.d.ts +39 -0
- package/dist/core/sink.d.ts.map +1 -0
- package/dist/core/sink.js +16 -0
- package/dist/core/sink.js.map +1 -0
- package/dist/core/storage.d.ts +52 -0
- package/dist/core/storage.d.ts.map +1 -0
- package/dist/core/storage.js +79 -0
- package/dist/core/storage.js.map +1 -0
- package/dist/core/text.d.ts +9 -0
- package/dist/core/text.d.ts.map +1 -0
- package/dist/core/text.js +47 -0
- package/dist/core/text.js.map +1 -0
- package/dist/core/time.d.ts +18 -0
- package/dist/core/time.d.ts.map +1 -0
- package/dist/core/time.js +18 -0
- package/dist/core/time.js.map +1 -0
- package/dist/onboarding/contract.d.ts +138 -0
- package/dist/onboarding/contract.d.ts.map +1 -0
- package/dist/onboarding/contract.js +11 -0
- package/dist/onboarding/contract.js.map +1 -0
- package/dist/onboarding/index.d.ts +15 -0
- package/dist/onboarding/index.d.ts.map +1 -0
- package/dist/onboarding/index.js +22 -0
- package/dist/onboarding/index.js.map +1 -0
- package/dist/onboarding/input.d.ts +102 -0
- package/dist/onboarding/input.d.ts.map +1 -0
- package/dist/onboarding/input.js +216 -0
- package/dist/onboarding/input.js.map +1 -0
- package/dist/onboarding/persisted.d.ts +20 -0
- package/dist/onboarding/persisted.d.ts.map +1 -0
- package/dist/onboarding/persisted.js +75 -0
- package/dist/onboarding/persisted.js.map +1 -0
- package/dist/onboarding/state.d.ts +65 -0
- package/dist/onboarding/state.d.ts.map +1 -0
- package/dist/onboarding/state.js +207 -0
- package/dist/onboarding/state.js.map +1 -0
- package/dist/onboarding/tracker.d.ts +135 -0
- package/dist/onboarding/tracker.d.ts.map +1 -0
- package/dist/onboarding/tracker.js +734 -0
- package/dist/onboarding/tracker.js.map +1 -0
- package/dist/paywall/contract.d.ts +67 -0
- package/dist/paywall/contract.d.ts.map +1 -0
- package/dist/paywall/contract.js +3 -0
- package/dist/paywall/contract.js.map +1 -0
- package/dist/paywall/index.d.ts +14 -0
- package/dist/paywall/index.d.ts.map +1 -0
- package/dist/paywall/index.js +19 -0
- package/dist/paywall/index.js.map +1 -0
- package/dist/paywall/observer.d.ts +38 -0
- package/dist/paywall/observer.d.ts.map +1 -0
- package/dist/paywall/observer.js +6 -0
- package/dist/paywall/observer.js.map +1 -0
- package/dist/paywall/state.d.ts +26 -0
- package/dist/paywall/state.d.ts.map +1 -0
- package/dist/paywall/state.js +132 -0
- package/dist/paywall/state.js.map +1 -0
- package/dist/paywall/tracker.d.ts +36 -0
- package/dist/paywall/tracker.d.ts.map +1 -0
- package/dist/paywall/tracker.js +180 -0
- package/dist/paywall/tracker.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +6 -0
- package/dist/version.js.map +1 -0
- package/docs/onboarding-run-contract.md +1013 -0
- package/docs/onboarding-run.examples.ts +306 -0
- package/docs/onboarding-run.schema.json +224 -0
- package/docs/onboarding-run.types.ts +144 -0
- package/docs/paywall-presentation-contract.md +139 -0
- package/docs/paywall-presentation.examples.ts +42 -0
- package/docs/paywall-presentation.schema.json +268 -0
- package/docs/paywall-presentation.types.ts +73 -0
- package/onboarding/package.json +4 -0
- package/package.json +120 -4
- package/paywall/package.json +1 -0
- package/src/client/client.ts +405 -0
- package/src/client/index.ts +54 -0
- package/src/client/native.ts +109 -0
- package/src/client/onboardingSink.ts +121 -0
- package/src/client/paywallSink.ts +25 -0
- package/src/client/requests.ts +126 -0
- package/src/client/types.ts +198 -0
- package/src/core/context.ts +69 -0
- package/src/core/delivery.ts +181 -0
- package/src/core/diagnostics.ts +30 -0
- package/src/core/httpSink.ts +70 -0
- package/src/core/ids.ts +46 -0
- package/src/core/index.ts +20 -0
- package/src/core/sink.ts +46 -0
- package/src/core/storage.ts +112 -0
- package/src/core/text.ts +37 -0
- package/src/core/time.ts +26 -0
- package/src/onboarding/contract.ts +144 -0
- package/src/onboarding/index.ts +39 -0
- package/src/onboarding/input.ts +246 -0
- package/src/onboarding/persisted.ts +93 -0
- package/src/onboarding/state.ts +233 -0
- package/src/onboarding/tracker.ts +855 -0
- package/src/paywall/contract.ts +73 -0
- package/src/paywall/index.ts +36 -0
- package/src/paywall/observer.ts +43 -0
- package/src/paywall/state.ts +125 -0
- package/src/paywall/tracker.ts +200 -0
- package/src/version.ts +2 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persisted.js","sourceRoot":"","sources":["../../src/onboarding/persisted.ts"],"names":[],"mappings":";;AAgCA,gCAoBC;AAGD,gCAUC;AAGD,wCAwBC;AA5ED,MAAM,IAAI,GAAG,gEAAgE,CAAC;AAC9E,MAAM,SAAS,GAAG,oEAAoE,CAAC;AAGvF,MAAM,KAAK,GAAG,CAAC,CAAU,EAAY,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAC1F,MAAM,WAAW,GAAG,CAAC,CAAU,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC/E,MAAM,KAAK,GAAG,CAAC,CAAU,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAErF,MAAM,OAAO,GAAG,CAAC,CAAU,EAAE,EAAE,CAC7B,KAAK,CAAC,CAAC,CAAC;IACR,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ;IAC9B,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC;IACzB,CAAC,CAAC,CAAC,SAAS,KAAK,IAAI,IAAI,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAClD,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;IACxB,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAEzB,SAAgB,UAAU,CAAC,CAAU;IACnC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5B,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC5C,OAAO,CACL,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;QACjD,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC;QAC9E,KAAK,CAAC,UAAU,CAAC,IAAI,OAAO,UAAU,CAAC,GAAG,KAAK,QAAQ,IAAI,OAAO,UAAU,CAAC,OAAO,KAAK,QAAQ;QACjG,KAAK,CAAC,OAAO,CAAC;QACd,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;QAC7E,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC;QACvE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC;QAChD,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC,UAAU,KAAK,SAAS,IAAI,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;QACnD,OAAO,CAAC,CAAC,eAAe,KAAK,SAAS;QACtC,OAAO,CAAC,CAAC,SAAS,KAAK,SAAS;QAChC,CAAC,CAAC,CAAC,MAAM,KAAK,aAAa,IAAI,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC;QACxD,CAAC,CAAC,CAAC,WAAW,KAAK,IAAI,IAAI,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;QACtD,OAAO,CAAC,CAAC,YAAY,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC;QACrE,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CACxD,CAAC;AACJ,CAAC;AAED,4GAA4G;AAC5G,SAAgB,UAAU,CAAC,KAAa,EAAE,CAAU;IAClD,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/D,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;IACjB,OAAO,CACL,CAAC,CAAC,MAAM,KAAK,KAAK;QAClB,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG;QACf,CAAC,CAAC,CAAC,MAAM,KAAK,aAAa,IAAI,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC;QACxD,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;QAC5C,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAC7D,CAAC;AACJ,CAAC;AAED,gEAAgE;AAChE,SAAgB,cAAc,CAAC,GAAY;IAKzC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,2BAA2B,CAAC,EAAE,CAAC;IAClF,CAAC;IACD,IAAI,OAAO,GAAoB,IAAI,CAAC;IACpC,IAAI,GAAG,CAAC,OAAO,KAAK,IAAI,IAAI,GAAG,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACtD,IAAI,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;;YAC9C,QAAQ,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IACpD,CAAC;IACD,MAAM,QAAQ,GAAgD,EAAE,CAAC;IACjE,IAAI,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxB,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzD,IAAI,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC;gBAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;;gBACrD,QAAQ,CAAC,IAAI,CAAC,+BAA+B,KAAK,gBAAgB,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;SAAM,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACtC,QAAQ,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { WireRunContext } from "../core/context";
|
|
2
|
+
import type { Answer, Manifest, OnboardingIdentity, OnboardingRunSnapshot, Properties, StepEntry, StudioLinks } from "./contract";
|
|
3
|
+
/** 256 KiB minus 1 KiB of headroom for what lands after the measure (D26). */
|
|
4
|
+
export declare const RECORDING_BUDGET = 261120;
|
|
5
|
+
/** The most entries a snapshot holds (D8). */
|
|
6
|
+
export declare const MAX_ENTRIES = 500;
|
|
7
|
+
export interface RunState {
|
|
8
|
+
runId: string;
|
|
9
|
+
/** The last `seq` assigned to a send. 0 before the first. */
|
|
10
|
+
lastSeq: number;
|
|
11
|
+
onboarding: OnboardingIdentity;
|
|
12
|
+
studio?: StudioLinks;
|
|
13
|
+
context: WireRunContext;
|
|
14
|
+
manifest: Manifest;
|
|
15
|
+
properties?: Properties;
|
|
16
|
+
/** Entries as recorded. The last one's exit may be provisional, see below. */
|
|
17
|
+
steps: StepEntry[];
|
|
18
|
+
/**
|
|
19
|
+
* The last entry's `exited_at` was set by exitStep while the run goes on.
|
|
20
|
+
* An in-progress snapshot of an untruncated run still sends it as null
|
|
21
|
+
* (rule 6: only the current screen is open), until the next entry closes it
|
|
22
|
+
* for good or completion replaces it with `completed_at` (rule 7).
|
|
23
|
+
*/
|
|
24
|
+
provisionalExit: boolean;
|
|
25
|
+
truncated: boolean;
|
|
26
|
+
status: "in_progress" | "completed";
|
|
27
|
+
completedAt: string | null;
|
|
28
|
+
/** Last moment the app was known to be in the foreground on the current screen (3.2). Epoch ms. */
|
|
29
|
+
lastActiveAt: number;
|
|
30
|
+
/** A recorded change has not gone into a send yet (it is waiting on the debounce). */
|
|
31
|
+
dirty?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/** What an operation did, for the tracker to report. */
|
|
34
|
+
export type Note = "truncated" | "too-many-answers";
|
|
35
|
+
export interface Result {
|
|
36
|
+
state: RunState;
|
|
37
|
+
changed: boolean;
|
|
38
|
+
notes: Note[];
|
|
39
|
+
}
|
|
40
|
+
/** The snapshot `state` sends with `seq` at `nowMs`, in the key order of the contract's examples. */
|
|
41
|
+
export declare function toSnapshot(state: RunState, seq: number, nowMs: number): OnboardingRunSnapshot;
|
|
42
|
+
/** Completion as the contract defines it: every open exit closed at `completed_at`, a truncated run's closed exit kept. */
|
|
43
|
+
export declare function completeState(state: RunState, nowMs: number): RunState;
|
|
44
|
+
/** Whether `state` stays within the recording budget, measured on its completion form with the next seq (D26). */
|
|
45
|
+
export declare function fits(state: RunState, nowMs: number): boolean;
|
|
46
|
+
/** A new screen is shown. */
|
|
47
|
+
export declare function enter(state: RunState, stepKey: string, nowMs: number): Result;
|
|
48
|
+
/**
|
|
49
|
+
* The user leaves the screen at `index` (the current entry, or the one just
|
|
50
|
+
* before it when the next screen was entered first), with what they answered.
|
|
51
|
+
*/
|
|
52
|
+
export declare function exit(state: RunState, index: number, answers: Answer[], nowMs: number): Result;
|
|
53
|
+
/** A new or changed property set: `merged` is the whole map after the change. */
|
|
54
|
+
export declare function setProperties(state: RunState, merged: Properties, nowMs: number): Result;
|
|
55
|
+
/**
|
|
56
|
+
* Restore after relaunch (3.2): close a still-open pre-kill entry at
|
|
57
|
+
* last_active_at, or at the exit exitStep recorded if that is later (the
|
|
58
|
+
* user is known to have left then); never rewrite an exit already final. Then append a new
|
|
59
|
+
* entry for the restored screen at `nowMs`, unless the run is truncated or the
|
|
60
|
+
* entry would break a limit, in which case recording stops instead.
|
|
61
|
+
*/
|
|
62
|
+
export declare function restore(state: RunState, nowMs: number): Result;
|
|
63
|
+
/** The latest moment recorded in the run, so a clock set backwards cannot make timestamps go back. */
|
|
64
|
+
export declare function floorMs(state: RunState): number;
|
|
65
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/onboarding/state.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EACV,MAAM,EACN,QAAQ,EACR,kBAAkB,EAClB,qBAAqB,EACrB,UAAU,EACV,SAAS,EACT,WAAW,EACZ,MAAM,YAAY,CAAC;AAGpB,8EAA8E;AAC9E,eAAO,MAAM,gBAAgB,SAAU,CAAC;AACxC,8CAA8C;AAC9C,eAAO,MAAM,WAAW,MAAM,CAAC;AAE/B,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,kBAAkB,CAAC;IAC/B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,OAAO,EAAE,cAAc,CAAC;IACxB,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,8EAA8E;IAC9E,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB;;;;;OAKG;IACH,eAAe,EAAE,OAAO,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,aAAa,GAAG,WAAW,CAAC;IACpC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,mGAAmG;IACnG,YAAY,EAAE,MAAM,CAAC;IACrB,sFAAsF;IACtF,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wDAAwD;AACxD,MAAM,MAAM,IAAI,GAAG,WAAW,GAAG,kBAAkB,CAAC;AACpD,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,QAAQ,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,IAAI,EAAE,CAAC;CACf;AAID,qGAAqG;AACrG,wBAAgB,UAAU,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,qBAAqB,CAwB7F;AAED,2HAA2H;AAC3H,wBAAgB,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ,CAUtE;AAED,kHAAkH;AAClH,wBAAgB,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAG5D;AAkBD,6BAA6B;AAC7B,wBAAgB,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAW7E;AAED;;;GAGG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CA6C7F;AAED,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAMxF;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAmB9D;AAED,sGAAsG;AACtG,wBAAgB,OAAO,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAQ/C"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MAX_ENTRIES = exports.RECORDING_BUDGET = void 0;
|
|
4
|
+
exports.toSnapshot = toSnapshot;
|
|
5
|
+
exports.completeState = completeState;
|
|
6
|
+
exports.fits = fits;
|
|
7
|
+
exports.enter = enter;
|
|
8
|
+
exports.exit = exit;
|
|
9
|
+
exports.setProperties = setProperties;
|
|
10
|
+
exports.restore = restore;
|
|
11
|
+
exports.floorMs = floorMs;
|
|
12
|
+
// The run as the tracker records it, and the pure operations on it. Every
|
|
13
|
+
// function returns a new state (or the same one when nothing changed); none
|
|
14
|
+
// reads the clock or does I/O, so each rule of the contract is testable here.
|
|
15
|
+
const text_1 = require("../core/text");
|
|
16
|
+
const time_1 = require("../core/time");
|
|
17
|
+
const input_1 = require("./input");
|
|
18
|
+
/** 256 KiB minus 1 KiB of headroom for what lands after the measure (D26). */
|
|
19
|
+
exports.RECORDING_BUDGET = 261120;
|
|
20
|
+
/** The most entries a snapshot holds (D8). */
|
|
21
|
+
exports.MAX_ENTRIES = 500;
|
|
22
|
+
const unchanged = (state, notes = []) => ({ state, changed: false, notes });
|
|
23
|
+
/** The snapshot `state` sends with `seq` at `nowMs`, in the key order of the contract's examples. */
|
|
24
|
+
function toSnapshot(state, seq, nowMs) {
|
|
25
|
+
var _a, _b;
|
|
26
|
+
const steps = state.steps.map((e) => ({ ...e, answers: e.answers.slice() }));
|
|
27
|
+
if (state.status === "in_progress" && !state.truncated && state.provisionalExit && steps.length) {
|
|
28
|
+
steps[steps.length - 1].exited_at = null;
|
|
29
|
+
}
|
|
30
|
+
const head = {
|
|
31
|
+
schema_version: 1,
|
|
32
|
+
run_id: state.runId,
|
|
33
|
+
seq,
|
|
34
|
+
};
|
|
35
|
+
const tail = {
|
|
36
|
+
sent_at: (0, time_1.toTimestamp)(nowMs),
|
|
37
|
+
onboarding: state.onboarding,
|
|
38
|
+
...(state.studio ? { studio: state.studio } : {}),
|
|
39
|
+
context: state.context,
|
|
40
|
+
manifest: state.manifest,
|
|
41
|
+
...(state.properties ? { properties: state.properties } : {}),
|
|
42
|
+
...(state.truncated ? { truncated: true } : {}),
|
|
43
|
+
steps,
|
|
44
|
+
};
|
|
45
|
+
const started_at = (_b = (_a = steps[0]) === null || _a === void 0 ? void 0 : _a.entered_at) !== null && _b !== void 0 ? _b : (0, time_1.toTimestamp)(nowMs);
|
|
46
|
+
return state.status === "completed"
|
|
47
|
+
? { ...head, status: "completed", started_at, completed_at: state.completedAt, ...tail }
|
|
48
|
+
: { ...head, status: "in_progress", started_at, completed_at: null, ...tail };
|
|
49
|
+
}
|
|
50
|
+
/** Completion as the contract defines it: every open exit closed at `completed_at`, a truncated run's closed exit kept. */
|
|
51
|
+
function completeState(state, nowMs) {
|
|
52
|
+
const completedAt = (0, time_1.toTimestamp)(nowMs);
|
|
53
|
+
const steps = state.steps.slice();
|
|
54
|
+
const lastIndex = steps.length - 1;
|
|
55
|
+
if (lastIndex >= 0) {
|
|
56
|
+
const last = steps[lastIndex];
|
|
57
|
+
const keepExit = state.truncated && last.exited_at !== null;
|
|
58
|
+
if (!keepExit)
|
|
59
|
+
steps[lastIndex] = { ...last, exited_at: completedAt };
|
|
60
|
+
}
|
|
61
|
+
return { ...state, steps, provisionalExit: false, status: "completed", completedAt };
|
|
62
|
+
}
|
|
63
|
+
/** Whether `state` stays within the recording budget, measured on its completion form with the next seq (D26). */
|
|
64
|
+
function fits(state, nowMs) {
|
|
65
|
+
const form = toSnapshot(completeState(state, nowMs), state.lastSeq + 1, nowMs);
|
|
66
|
+
return (0, text_1.utf8ByteLength)(JSON.stringify(form)) <= exports.RECORDING_BUDGET;
|
|
67
|
+
}
|
|
68
|
+
const lastOf = (s) => s.steps[s.steps.length - 1];
|
|
69
|
+
/** Closes the current entry for good: at its provisional exit if exitStep gave one, else at `at`. */
|
|
70
|
+
function closeCurrent(state, at) {
|
|
71
|
+
var _a;
|
|
72
|
+
const last = lastOf(state);
|
|
73
|
+
if (!last)
|
|
74
|
+
return state;
|
|
75
|
+
if (last.exited_at !== null && !state.provisionalExit)
|
|
76
|
+
return state;
|
|
77
|
+
const steps = state.steps.slice();
|
|
78
|
+
steps[steps.length - 1] = { ...last, exited_at: (_a = last.exited_at) !== null && _a !== void 0 ? _a : at };
|
|
79
|
+
return { ...state, steps, provisionalExit: false };
|
|
80
|
+
}
|
|
81
|
+
function stopRecording(state) {
|
|
82
|
+
return { ...state, truncated: true };
|
|
83
|
+
}
|
|
84
|
+
/** A new screen is shown. */
|
|
85
|
+
function enter(state, stepKey, nowMs) {
|
|
86
|
+
const at = (0, time_1.toTimestamp)(nowMs);
|
|
87
|
+
const closed = { ...closeCurrent(state, at), lastActiveAt: nowMs };
|
|
88
|
+
if (state.truncated)
|
|
89
|
+
return { state: closed, changed: closed.steps !== state.steps, notes: ["truncated"] };
|
|
90
|
+
if (state.steps.length >= exports.MAX_ENTRIES)
|
|
91
|
+
return { state: stopRecording(closed), changed: true, notes: ["truncated"] };
|
|
92
|
+
const next = {
|
|
93
|
+
...closed,
|
|
94
|
+
steps: [...closed.steps, { step_key: stepKey, entered_at: at, exited_at: null, answers: [] }],
|
|
95
|
+
};
|
|
96
|
+
if (!fits(next, nowMs))
|
|
97
|
+
return { state: stopRecording(closed), changed: true, notes: ["truncated"] };
|
|
98
|
+
return { state: next, changed: true, notes: [] };
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The user leaves the screen at `index` (the current entry, or the one just
|
|
102
|
+
* before it when the next screen was entered first), with what they answered.
|
|
103
|
+
*/
|
|
104
|
+
function exit(state, index, answers, nowMs) {
|
|
105
|
+
const notes = [];
|
|
106
|
+
let current = state;
|
|
107
|
+
let changed = false;
|
|
108
|
+
const isLast = index === state.steps.length - 1;
|
|
109
|
+
if (current.truncated) {
|
|
110
|
+
if (answers.length)
|
|
111
|
+
notes.push("truncated");
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
for (const answer of answers) {
|
|
115
|
+
const entry = current.steps[index];
|
|
116
|
+
const at = entry.answers.findIndex((a) => a.question_key === answer.question_key);
|
|
117
|
+
if (at < 0 && entry.answers.length >= input_1.MAX_ANSWERS) {
|
|
118
|
+
notes.push("too-many-answers");
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const list = entry.answers.slice();
|
|
122
|
+
if (at < 0)
|
|
123
|
+
list.push(answer);
|
|
124
|
+
else
|
|
125
|
+
list[at] = answer;
|
|
126
|
+
const steps = current.steps.slice();
|
|
127
|
+
steps[index] = { ...entry, answers: list };
|
|
128
|
+
const next = { ...current, steps };
|
|
129
|
+
if (!fits(next, nowMs)) {
|
|
130
|
+
current = stopRecording(current);
|
|
131
|
+
notes.push("truncated");
|
|
132
|
+
changed = true;
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
current = next;
|
|
136
|
+
changed = true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Record when the user left. In a truncated run that exit is final at once.
|
|
140
|
+
const entry = current.steps[index];
|
|
141
|
+
if (isLast && entry.exited_at === null) {
|
|
142
|
+
const steps = current.steps.slice();
|
|
143
|
+
steps[index] = { ...entry, exited_at: (0, time_1.toTimestamp)(nowMs) };
|
|
144
|
+
current = { ...current, steps, provisionalExit: !current.truncated };
|
|
145
|
+
changed = true;
|
|
146
|
+
}
|
|
147
|
+
else if (isLast && current.truncated && current.provisionalExit) {
|
|
148
|
+
current = { ...current, provisionalExit: false };
|
|
149
|
+
changed = true;
|
|
150
|
+
}
|
|
151
|
+
return changed ? { state: current, changed, notes } : unchanged(state, notes);
|
|
152
|
+
}
|
|
153
|
+
/** A new or changed property set: `merged` is the whole map after the change. */
|
|
154
|
+
function setProperties(state, merged, nowMs) {
|
|
155
|
+
var _a;
|
|
156
|
+
if (JSON.stringify(merged) === JSON.stringify((_a = state.properties) !== null && _a !== void 0 ? _a : {}))
|
|
157
|
+
return unchanged(state);
|
|
158
|
+
if (state.truncated)
|
|
159
|
+
return unchanged(state, ["truncated"]);
|
|
160
|
+
const next = { ...state, properties: Object.keys(merged).length ? merged : undefined };
|
|
161
|
+
if (!fits(next, nowMs))
|
|
162
|
+
return { state: stopRecording(state), changed: true, notes: ["truncated"] };
|
|
163
|
+
return { state: next, changed: true, notes: [] };
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Restore after relaunch (3.2): close a still-open pre-kill entry at
|
|
167
|
+
* last_active_at, or at the exit exitStep recorded if that is later (the
|
|
168
|
+
* user is known to have left then); never rewrite an exit already final. Then append a new
|
|
169
|
+
* entry for the restored screen at `nowMs`, unless the run is truncated or the
|
|
170
|
+
* entry would break a limit, in which case recording stops instead.
|
|
171
|
+
*/
|
|
172
|
+
function restore(state, nowMs) {
|
|
173
|
+
const last = lastOf(state);
|
|
174
|
+
if (!last)
|
|
175
|
+
return unchanged(state);
|
|
176
|
+
let current = state;
|
|
177
|
+
if (last.exited_at === null || (state.provisionalExit && !state.truncated)) {
|
|
178
|
+
const provisional = last.exited_at !== null ? Date.parse(last.exited_at) : -Infinity;
|
|
179
|
+
const steps = state.steps.slice();
|
|
180
|
+
steps[steps.length - 1] = { ...last, exited_at: (0, time_1.toTimestamp)(Math.max(state.lastActiveAt, provisional)) };
|
|
181
|
+
current = { ...state, steps, provisionalExit: false };
|
|
182
|
+
}
|
|
183
|
+
current = { ...current, lastActiveAt: nowMs };
|
|
184
|
+
if (current.truncated)
|
|
185
|
+
return { state: current, changed: true, notes: [] };
|
|
186
|
+
if (current.steps.length >= exports.MAX_ENTRIES)
|
|
187
|
+
return { state: stopRecording(current), changed: true, notes: ["truncated"] };
|
|
188
|
+
const next = {
|
|
189
|
+
...current,
|
|
190
|
+
steps: [...current.steps, { step_key: last.step_key, entered_at: (0, time_1.toTimestamp)(nowMs), exited_at: null, answers: [] }],
|
|
191
|
+
};
|
|
192
|
+
if (!fits(next, nowMs))
|
|
193
|
+
return { state: stopRecording(current), changed: true, notes: ["truncated"] };
|
|
194
|
+
return { state: next, changed: true, notes: [] };
|
|
195
|
+
}
|
|
196
|
+
/** The latest moment recorded in the run, so a clock set backwards cannot make timestamps go back. */
|
|
197
|
+
function floorMs(state) {
|
|
198
|
+
const last = lastOf(state);
|
|
199
|
+
let floor = 0;
|
|
200
|
+
if (last) {
|
|
201
|
+
floor = Date.parse(last.entered_at);
|
|
202
|
+
if (last.exited_at)
|
|
203
|
+
floor = Math.max(floor, Date.parse(last.exited_at));
|
|
204
|
+
}
|
|
205
|
+
return floor;
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/onboarding/state.ts"],"names":[],"mappings":";;;AA4DA,gCAwBC;AAGD,sCAUC;AAGD,oBAGC;AAmBD,sBAWC;AAMD,oBA6CC;AAGD,sCAMC;AASD,0BAmBC;AAGD,0BAQC;AAxOD,0EAA0E;AAC1E,4EAA4E;AAC5E,8EAA8E;AAC9E,uCAA8C;AAC9C,uCAA2C;AAW3C,mCAAsC;AAEtC,8EAA8E;AACjE,QAAA,gBAAgB,GAAG,MAAO,CAAC;AACxC,8CAA8C;AACjC,QAAA,WAAW,GAAG,GAAG,CAAC;AAqC/B,MAAM,SAAS,GAAG,CAAC,KAAe,EAAE,QAAgB,EAAE,EAAU,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;AAEtG,qGAAqG;AACrG,SAAgB,UAAU,CAAC,KAAe,EAAE,GAAW,EAAE,KAAa;;IACpE,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;IAC7E,IAAI,KAAK,CAAC,MAAM,KAAK,aAAa,IAAI,CAAC,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,eAAe,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QAChG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC;IAC3C,CAAC;IACD,MAAM,IAAI,GAAG;QACX,cAAc,EAAE,CAAU;QAC1B,MAAM,EAAE,KAAK,CAAC,KAAK;QACnB,GAAG;KACJ,CAAC;IACF,MAAM,IAAI,GAAG;QACX,OAAO,EAAE,IAAA,kBAAW,EAAC,KAAK,CAAC;QAC3B,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7D,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACxD,KAAK;KACN,CAAC;IACF,MAAM,UAAU,GAAG,MAAA,MAAA,KAAK,CAAC,CAAC,CAAC,0CAAE,UAAU,mCAAI,IAAA,kBAAW,EAAC,KAAK,CAAC,CAAC;IAC9D,OAAO,KAAK,CAAC,MAAM,KAAK,WAAW;QACjC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,KAAK,CAAC,WAAqB,EAAE,GAAG,IAAI,EAAE;QAClG,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC;AAClF,CAAC;AAED,2HAA2H;AAC3H,SAAgB,aAAa,CAAC,KAAe,EAAE,KAAa;IAC1D,MAAM,WAAW,GAAG,IAAA,kBAAW,EAAC,KAAK,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IAClC,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IACnC,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;QACnB,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC;QAC9B,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC;QAC5D,IAAI,CAAC,QAAQ;YAAE,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC;IACxE,CAAC;IACD,OAAO,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;AACvF,CAAC;AAED,kHAAkH;AAClH,SAAgB,IAAI,CAAC,KAAe,EAAE,KAAa;IACjD,MAAM,IAAI,GAAG,UAAU,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,OAAO,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;IAC/E,OAAO,IAAA,qBAAc,EAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,wBAAgB,CAAC;AAClE,CAAC;AAED,MAAM,MAAM,GAAG,CAAC,CAAW,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAA0B,CAAC;AAErF,qGAAqG;AACrG,SAAS,YAAY,CAAC,KAAe,EAAE,EAAU;;IAC/C,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3B,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,eAAe;QAAE,OAAO,KAAK,CAAC;IACpE,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IAClC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,MAAA,IAAI,CAAC,SAAS,mCAAI,EAAE,EAAE,CAAC;IACvE,OAAO,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC;AACrD,CAAC;AAED,SAAS,aAAa,CAAC,KAAe;IACpC,OAAO,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AACvC,CAAC;AAED,6BAA6B;AAC7B,SAAgB,KAAK,CAAC,KAAe,EAAE,OAAe,EAAE,KAAa;IACnE,MAAM,EAAE,GAAG,IAAA,kBAAW,EAAC,KAAK,CAAC,CAAC;IAC9B,MAAM,MAAM,GAAG,EAAE,GAAG,YAAY,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;IACnE,IAAI,KAAK,CAAC,SAAS;QAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC;IAC3G,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,IAAI,mBAAW;QAAE,OAAO,EAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC;IACpH,MAAM,IAAI,GAAa;QACrB,GAAG,MAAM;QACT,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;KAC9F,CAAC;IACF,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC;IACrG,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;AACnD,CAAC;AAED;;;GAGG;AACH,SAAgB,IAAI,CAAC,KAAe,EAAE,KAAa,EAAE,OAAiB,EAAE,KAAa;IACnF,MAAM,KAAK,GAAW,EAAE,CAAC;IACzB,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,MAAM,MAAM,GAAG,KAAK,KAAK,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IAEhD,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;QACtB,IAAI,OAAO,CAAC,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC9C,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,MAAM,CAAC,YAAY,CAAC,CAAC;YAClF,IAAI,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,IAAI,mBAAW,EAAE,CAAC;gBAClD,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;gBAC/B,SAAS;YACX,CAAC;YACD,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACnC,IAAI,EAAE,GAAG,CAAC;gBAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;;gBACzB,IAAI,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC;YACvB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACpC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;YAC3C,MAAM,IAAI,GAAG,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,CAAC;YACnC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;gBACvB,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;gBACjC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;gBACxB,OAAO,GAAG,IAAI,CAAC;gBACf,MAAM;YACR,CAAC;YACD,OAAO,GAAG,IAAI,CAAC;YACf,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,MAAM,IAAI,KAAK,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACpC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,IAAA,kBAAW,EAAC,KAAK,CAAC,EAAE,CAAC;QAC3D,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;QACrE,OAAO,GAAG,IAAI,CAAC;IACjB,CAAC;SAAM,IAAI,MAAM,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;QAClE,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC;QACjD,OAAO,GAAG,IAAI,CAAC;IACjB,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAChF,CAAC;AAED,iFAAiF;AACjF,SAAgB,aAAa,CAAC,KAAe,EAAE,MAAkB,EAAE,KAAa;;IAC9E,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,MAAA,KAAK,CAAC,UAAU,mCAAI,EAAE,CAAC;QAAE,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;IAC/F,IAAI,KAAK,CAAC,SAAS;QAAE,OAAO,SAAS,CAAC,KAAK,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;IAC5D,MAAM,IAAI,GAAG,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;IACvF,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC;IACpG,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;AACnD,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,OAAO,CAAC,KAAe,EAAE,KAAa;IACpD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3B,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,eAAe,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3E,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QACrF,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAClC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,IAAA,kBAAW,EAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC;QACzG,OAAO,GAAG,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC;IACxD,CAAC;IACD,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;IAC9C,IAAI,OAAO,CAAC,SAAS;QAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IAC3E,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,IAAI,mBAAW;QAAE,OAAO,EAAE,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC;IACvH,MAAM,IAAI,GAAa;QACrB,GAAG,OAAO;QACV,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAA,kBAAW,EAAC,KAAK,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;KACrH,CAAC;IACF,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC;IACtG,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;AACnD,CAAC;AAED,sGAAsG;AACtG,SAAgB,OAAO,CAAC,KAAe;IACrC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,IAAI,EAAE,CAAC;QACT,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACpC,IAAI,IAAI,CAAC,SAAS;YAAE,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { type RunContextInput } from "../core/context";
|
|
2
|
+
import { type RetryPolicy } from "../core/delivery";
|
|
3
|
+
import { type DiagnosticHandler } from "../core/diagnostics";
|
|
4
|
+
import type { Sink } from "../core/sink";
|
|
5
|
+
import { type KeyValueStorage } from "../core/storage";
|
|
6
|
+
import { type Clock, type Timers } from "../core/time";
|
|
7
|
+
import type { OnboardingRunSnapshot, Properties } from "./contract";
|
|
8
|
+
import { type AnswerInput, type StartOptions } from "./input";
|
|
9
|
+
export interface TrackerConfig {
|
|
10
|
+
/** Where snapshots go. `createHttpSink` is the stock one. */
|
|
11
|
+
sink: Sink<OnboardingRunSnapshot>;
|
|
12
|
+
/** Device and app facts, or a function returning them, read once per run at start. */
|
|
13
|
+
context: RunContextInput | (() => RunContextInput);
|
|
14
|
+
/**
|
|
15
|
+
* Persists the run so it can be resumed after the app is killed, and keeps an
|
|
16
|
+
* unsent snapshot across restarts. AsyncStorage fits as is. Without it,
|
|
17
|
+
* nothing survives a restart, and `resume()` resolves null unless a tracker
|
|
18
|
+
* disposed in this process on the same `storageKey` handed it a run.
|
|
19
|
+
*/
|
|
20
|
+
storage?: KeyValueStorage;
|
|
21
|
+
/** Storage key. Default `rocalytics-sdk:onboarding-run`. One restorable run per key. */
|
|
22
|
+
storageKey?: string;
|
|
23
|
+
clock?: Clock;
|
|
24
|
+
timers?: Timers;
|
|
25
|
+
/** Run id minter. Must return a lowercase UUID. Default: UUIDv7. */
|
|
26
|
+
uuid?: () => string;
|
|
27
|
+
/**
|
|
28
|
+
* Changes within this window go out as one send. Default 500 ms. Completion
|
|
29
|
+
* and background skip the debounce, but with `storage` every send, these
|
|
30
|
+
* included, first waits for its snapshot to be written: up to
|
|
31
|
+
* `persistTimeoutMs` (default 1,000 ms) later.
|
|
32
|
+
*/
|
|
33
|
+
debounceMs?: number;
|
|
34
|
+
retry?: RetryPolicy;
|
|
35
|
+
/** A send with no answer after this long is retried. Default 30,000 ms. */
|
|
36
|
+
attemptTimeoutMs?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Each snapshot is stored (with its seq) before it is handed to the sink, so
|
|
39
|
+
* a kill in between can never make a resumed run reuse that seq for a
|
|
40
|
+
* different body. A storage slower than this does not hold the send back any
|
|
41
|
+
* longer: it goes out anyway, and only then can a kill in that window cost
|
|
42
|
+
* the guarantee. Default 1,000 ms. Ignored without `storage`.
|
|
43
|
+
*/
|
|
44
|
+
persistTimeoutMs?: number;
|
|
45
|
+
/**
|
|
46
|
+
* How long a storage read may take before it is reported as slow. It does
|
|
47
|
+
* not cut `resume()` short: `resume()` waits for the read however long it
|
|
48
|
+
* takes. It bounds `idle()`, which waits at most this long for the read and
|
|
49
|
+
* as long again for queued writes, so up to about twice this; and with it,
|
|
50
|
+
* how long a tracker created after a `dispose()` on the same `storageKey`
|
|
51
|
+
* waits for the disposed one's writes before it reads. `start()` never waits
|
|
52
|
+
* for the read. Default 5,000 ms.
|
|
53
|
+
*/
|
|
54
|
+
storageReadTimeoutMs?: number;
|
|
55
|
+
/** Receives what the tracker declined to do. Default: `console.warn`. */
|
|
56
|
+
onDiagnostic?: DiagnosticHandler;
|
|
57
|
+
}
|
|
58
|
+
export interface ExitOptions {
|
|
59
|
+
/** What was answered on this visit. Values are stable option keys, never displayed labels. */
|
|
60
|
+
answers?: AnswerInput[];
|
|
61
|
+
}
|
|
62
|
+
/** One pass through an onboarding. Every method is safe to call at any time and never throws. */
|
|
63
|
+
export interface OnboardingRun {
|
|
64
|
+
/** Lowercase UUID, minted when the run starts. */
|
|
65
|
+
readonly runId: string;
|
|
66
|
+
/**
|
|
67
|
+
* The step of the last RECORDED entry; null before the first step. In a
|
|
68
|
+
* truncated run recording has stopped, so this can be an earlier screen than
|
|
69
|
+
* the one the user was on: restore the position from the app's own
|
|
70
|
+
* navigation state, not from this.
|
|
71
|
+
*/
|
|
72
|
+
readonly currentStepKey: string | null;
|
|
73
|
+
/** A screen is shown. Call it again for the same step after going back to it. */
|
|
74
|
+
enterStep(stepKey: string): void;
|
|
75
|
+
/** The user leaves a screen, with what they answered there. */
|
|
76
|
+
exitStep(stepKey: string, options?: ExitOptions): void;
|
|
77
|
+
/** Adds or changes run properties. */
|
|
78
|
+
setProperties(properties: Properties): void;
|
|
79
|
+
/** The onboarding is finished. Final: the run records nothing after it. */
|
|
80
|
+
complete(): void;
|
|
81
|
+
/** The app moved to the background: records the moment and sends now. */
|
|
82
|
+
background(): void;
|
|
83
|
+
}
|
|
84
|
+
export interface OnboardingRunTracker {
|
|
85
|
+
/** Starts a new run. A run already in progress is left as it is, and stays in progress (a replay is a new run). */
|
|
86
|
+
start(options: StartOptions): OnboardingRun;
|
|
87
|
+
/**
|
|
88
|
+
* Resumes the run that was in progress when the app was killed, or resolves
|
|
89
|
+
* null. Call it only when the app restores the user's position; otherwise
|
|
90
|
+
* call `start`. The restored screen is recorded as a new entry for the last
|
|
91
|
+
* recorded step. A truncated run is still returned (so it can complete) but
|
|
92
|
+
* records no new entry. Null once a run was started this session.
|
|
93
|
+
*
|
|
94
|
+
* After a reconfigure, the run is the one the disposed tracker on this
|
|
95
|
+
* `storageKey` was recording, in its newest state, with storage or without;
|
|
96
|
+
* null if that tracker completed it or started another.
|
|
97
|
+
*
|
|
98
|
+
* It waits for the stored state to be read, however long that takes: a
|
|
99
|
+
* storage that never answers the read means it never resolves. An app that
|
|
100
|
+
* cannot wait should race it with its own timeout and call `start` instead.
|
|
101
|
+
*/
|
|
102
|
+
resume(): Promise<OnboardingRun | null>;
|
|
103
|
+
/** Resolves once queued storage writes are done. */
|
|
104
|
+
idle(): Promise<void>;
|
|
105
|
+
/**
|
|
106
|
+
* Stops recording without dropping what was recorded: a change on the
|
|
107
|
+
* debounce is sent, writes already queued land, and each unsent snapshot
|
|
108
|
+
* gets one last attempt (no retry timers). One the sink does not take is
|
|
109
|
+
* handed, in memory, to the next tracker created on the same `storageKey`
|
|
110
|
+
* (as `configure()` does), which sends it with its own sink and retries,
|
|
111
|
+
* with or without storage, provided its sink has the same `destination`
|
|
112
|
+
* (or, without one, is the same object). That tracker keeps this one's
|
|
113
|
+
* unsent snapshots in its writes from the start, sends none of them until
|
|
114
|
+
* these last attempts are answered, and drops any they delivered. A tracker
|
|
115
|
+
* disposed without a run of its own passes on what it was handed. With
|
|
116
|
+
* working storage the snapshot also stays stored for the next launch;
|
|
117
|
+
* without it, a snapshot is lost if the app is killed before a next tracker
|
|
118
|
+
* is created. Safe to call twice.
|
|
119
|
+
*/
|
|
120
|
+
dispose(): void;
|
|
121
|
+
}
|
|
122
|
+
export declare function createOnboardingRunTracker(config: TrackerConfig): OnboardingRunTracker;
|
|
123
|
+
/**
|
|
124
|
+
* The app-wide tracker. Call `configure` once at startup, then `start` (or
|
|
125
|
+
* `resume`) for each run. Before `configure`, `start` returns a run that
|
|
126
|
+
* records nothing, and warns.
|
|
127
|
+
*/
|
|
128
|
+
export declare const onboardingRun: {
|
|
129
|
+
configure(config: TrackerConfig): void;
|
|
130
|
+
start(options: StartOptions): OnboardingRun;
|
|
131
|
+
resume(): Promise<OnboardingRun | null>;
|
|
132
|
+
idle(): Promise<void>;
|
|
133
|
+
dispose(): void;
|
|
134
|
+
};
|
|
135
|
+
//# sourceMappingURL=tracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../src/onboarding/tracker.ts"],"names":[],"mappings":"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"}
|