@corbado/observe 1.7.2 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/analysis/client.mjs +1851 -0
  2. package/dist/analysis/config-manager.mjs +83 -0
  3. package/dist/analysis/config.mjs +216 -0
  4. package/dist/analysis/device-info-manager.mjs +153 -0
  5. package/dist/analysis/device.mjs +562 -0
  6. package/dist/analysis/index.mjs +118 -0
  7. package/dist/analysis/logger.mjs +50 -0
  8. package/dist/analysis/low-events/capture-sequence.mjs +11 -0
  9. package/dist/analysis/low-events/ceremony-initiator.mjs +26 -0
  10. package/dist/analysis/low-events/form-input-binding.mjs +46 -0
  11. package/dist/analysis/low-events/grouped-input-tracker.mjs +133 -0
  12. package/dist/analysis/low-events/input-change-recorder.mjs +200 -0
  13. package/dist/analysis/low-events/input-paste-observer.mjs +91 -0
  14. package/dist/analysis/low-events/input-tracker.mjs +223 -0
  15. package/dist/analysis/low-events/otp-input-binding.mjs +67 -0
  16. package/dist/analysis/low-events/shared-window-observer.mjs +225 -0
  17. package/dist/analysis/low-events/sink.mjs +0 -0
  18. package/dist/analysis/low-events/viewport-batcher.mjs +42 -0
  19. package/dist/analysis/low-events/window-tracker.mjs +40 -0
  20. package/dist/analysis/queue.mjs +887 -0
  21. package/dist/analysis/readers/device.mjs +73 -0
  22. package/dist/analysis/readers/meta.mjs +18 -0
  23. package/dist/analysis/readers/password-manager-dom.mjs +112 -0
  24. package/dist/analysis/readers/password-manager-extensions.mjs +247 -0
  25. package/dist/analysis/readers/private-mode.mjs +9 -0
  26. package/dist/analysis/storage/index.mjs +197 -0
  27. package/dist/analysis/storage/migration.mjs +56 -0
  28. package/dist/analysis/store/event-store.mjs +114 -0
  29. package/dist/analysis/transport.mjs +0 -0
  30. package/dist/analysis/transports/fetch.mjs +108 -0
  31. package/dist/analysis/types.mjs +25 -0
  32. package/dist/analysis/utils/appconfirmation-operation-full.mjs +19 -0
  33. package/dist/analysis/utils/blocked-user-agents.mjs +37 -0
  34. package/dist/analysis/utils/captcha-operation-full.mjs +17 -0
  35. package/dist/analysis/utils/email-link-continuity.mjs +50 -0
  36. package/dist/analysis/utils/email-link-operation-full.mjs +142 -0
  37. package/dist/analysis/utils/email-otp-operation-full.mjs +39 -0
  38. package/dist/analysis/utils/form-operation-full.mjs +81 -0
  39. package/dist/analysis/utils/key-operation-full.mjs +38 -0
  40. package/dist/analysis/utils/normalize-error.mjs +29 -0
  41. package/dist/analysis/utils/operation-full-provide-identifier-with-cui.mjs +46 -0
  42. package/dist/analysis/utils/operation-full.mjs +235 -0
  43. package/dist/analysis/utils/passkey-deletion-operation-full.mjs +17 -0
  44. package/dist/analysis/utils/passkey-enrollment-operation-full.mjs +32 -0
  45. package/dist/analysis/utils/passkey-login-operation-full.mjs +34 -0
  46. package/dist/analysis/utils/password-enrollment-operation-full.mjs +27 -0
  47. package/dist/analysis/utils/password-login-operation-full.mjs +61 -0
  48. package/dist/analysis/utils/password-manager-sampler.mjs +130 -0
  49. package/dist/analysis/utils/provide-data-operation-full.mjs +28 -0
  50. package/dist/analysis/utils/serialize-raw-error.mjs +205 -0
  51. package/dist/analysis/utils/sms-otp-operation-full.mjs +39 -0
  52. package/dist/analysis/utils/social-operation-login.mjs +17 -0
  53. package/dist/analysis/utils/source-path.mjs +39 -0
  54. package/dist/analysis/utils/sso-operation-login.mjs +37 -0
  55. package/dist/analysis/utils/totp-operation-full.mjs +35 -0
  56. package/dist/analysis/utils/trusted-device-operation-full.mjs +100 -0
  57. package/dist/analysis/utils/web-lock.mjs +52 -0
  58. package/dist/analysis/utils/webauthn-payloads.mjs +101 -0
  59. package/dist/analysis/version.mjs +7 -0
  60. package/dist/cdn/script.global.js +4 -4
  61. package/dist/index.d.mts +156 -4
  62. package/dist/index.d.ts +156 -4
  63. package/dist/index.js +4 -4
  64. package/dist/index.mjs +4 -4
  65. package/package.json +7 -2
@@ -0,0 +1,81 @@
1
+ // src/utils/form-operation-full.ts
2
+ import { FormInputBinding } from "../low-events/form-input-binding.mjs";
3
+ import { OperationFull } from "./operation-full.mjs";
4
+ var FORM_FIELDS_MAX_COUNT = 50;
5
+ var FORM_STARTED_AT_MAX_AGE_MS = 6e4;
6
+ var FORM_FIELD_NAME_MAX_LENGTH = 40;
7
+ var FORM_FIELD_NAME_PATTERN = /^[A-Za-z0-9._-]+$/;
8
+ var FormOperationFull = class extends OperationFull {
9
+ /** The application accepts the submitted form. */
10
+ submit;
11
+ /** The user leaves the form forward without submitting it. */
12
+ skip;
13
+ inputBinding;
14
+ constructor(tracker, config) {
15
+ super(tracker, "form");
16
+ this.submit = this.defineStep("submit");
17
+ this.skip = this.defineStep("skip");
18
+ void this.subflowStart(formStartData(tracker, config), formStartOptions(tracker, config));
19
+ this.inputBinding = this.createLowCollector(
20
+ () => new FormInputBinding(tracker.getLogger(), () => this.lowEventSink("input", "data"))
21
+ );
22
+ this.registerLowFlusher(() => this.inputBinding?.flushPending());
23
+ if (config?.inputHtmlFields?.length) this.setInputFields(config.inputHtmlFields);
24
+ }
25
+ /** Bind the fields the form shows now, without starting a new subflow. An empty list detaches all. */
26
+ setInputFields(fields) {
27
+ this.inputBinding?.setFields(fields);
28
+ }
29
+ destroy() {
30
+ this.inputBinding?.destroy();
31
+ }
32
+ };
33
+ function formStartOptions(tracker, config) {
34
+ const startedAt = config?.startedAt;
35
+ if (startedAt === void 0) return void 0;
36
+ const now = Date.now();
37
+ if (typeof startedAt !== "number" || !Number.isFinite(startedAt) || startedAt > now || now - startedAt > FORM_STARTED_AT_MAX_AGE_MS) {
38
+ warn(tracker, "Form: implausible startedAt ignored; using the current time");
39
+ return void 0;
40
+ }
41
+ return { explicitTimestamp: startedAt };
42
+ }
43
+ function formStartData(tracker, config) {
44
+ const data = {};
45
+ const name = config?.name;
46
+ if (typeof name === "string" && name.trim() !== "") {
47
+ data.name = name;
48
+ } else {
49
+ warn(tracker, "Form: missing name; the form will not be counted");
50
+ }
51
+ const fields = config?.fields;
52
+ if (fields === void 0) {
53
+ return data;
54
+ }
55
+ if (!Array.isArray(fields)) {
56
+ warn(tracker, "Form: fields must be an array of field names; omitted from telemetry");
57
+ return data;
58
+ }
59
+ const valid = fields.filter(
60
+ (field) => typeof field === "string" && field.length <= FORM_FIELD_NAME_MAX_LENGTH && FORM_FIELD_NAME_PATTERN.test(field)
61
+ );
62
+ if (valid.length < fields.length) {
63
+ warn(tracker, "Form: unsupported field names omitted from telemetry");
64
+ }
65
+ if (valid.length > FORM_FIELDS_MAX_COUNT) {
66
+ warn(tracker, `Form: more than ${FORM_FIELDS_MAX_COUNT} field names; the rest is omitted from telemetry`);
67
+ }
68
+ if (valid.length > 0) {
69
+ data.fields = valid.slice(0, FORM_FIELDS_MAX_COUNT);
70
+ }
71
+ return data;
72
+ }
73
+ function warn(tracker, message) {
74
+ try {
75
+ tracker.getLogger().warn(message);
76
+ } catch {
77
+ }
78
+ }
79
+ export {
80
+ FormOperationFull
81
+ };
@@ -0,0 +1,38 @@
1
+ // src/utils/key-operation-full.ts
2
+ import { OperationFull } from "./operation-full.mjs";
3
+ function specType(tracker, config) {
4
+ if (config.explicitSpecType === void 0) return void 0;
5
+ try {
6
+ tracker.getLogger().warn("Unsupported key operation spec type; omitting it");
7
+ } catch {
8
+ }
9
+ return void 0;
10
+ }
11
+ var KeySigningOperationFull = class extends OperationFull {
12
+ /** Resolve an eligible key and prepare the input for an actual signing attempt. */
13
+ prepare;
14
+ /** Sign and assemble the usable signed artifact. Does not include submitting it. */
15
+ sign;
16
+ constructor(tracker, config = {}) {
17
+ super(tracker, "key-signing");
18
+ this.prepare = this.defineStep("prepare");
19
+ this.sign = this.defineStep("sign");
20
+ void this.subflowStart({ actor: "system", explicitSpecType: specType(tracker, config) });
21
+ }
22
+ };
23
+ var KeyRegistrationOperationFull = class extends OperationFull {
24
+ /** Build the request that will establish the key's authority. */
25
+ prepare;
26
+ /** Submit and await acceptance: finish only when the key is authorized and usable. */
27
+ postResponse;
28
+ constructor(tracker, config = {}) {
29
+ super(tracker, "key-registration");
30
+ this.prepare = this.defineStep("prepare");
31
+ this.postResponse = this.defineStep("post-response");
32
+ void this.subflowStart({ actor: "system", explicitSpecType: specType(tracker, config) });
33
+ }
34
+ };
35
+ export {
36
+ KeyRegistrationOperationFull,
37
+ KeySigningOperationFull
38
+ };
@@ -0,0 +1,29 @@
1
+ // src/utils/normalize-error.ts
2
+ function normalizeError(error) {
3
+ if (error === void 0 || error === null) {
4
+ return void 0;
5
+ }
6
+ if (error instanceof Error) {
7
+ return {
8
+ name: error.name,
9
+ message: error.message
10
+ };
11
+ }
12
+ if (typeof error === "string") {
13
+ return { message: error };
14
+ }
15
+ if (typeof error === "object") {
16
+ const obj = error;
17
+ if (typeof obj.message === "string" || typeof obj.code === "string") {
18
+ return {
19
+ name: typeof obj.name === "string" ? obj.name : void 0,
20
+ message: typeof obj.message === "string" ? obj.message : void 0,
21
+ code: typeof obj.code === "string" ? obj.code : void 0
22
+ };
23
+ }
24
+ }
25
+ return { message: String(error) };
26
+ }
27
+ export {
28
+ normalizeError
29
+ };
@@ -0,0 +1,46 @@
1
+ // src/utils/operation-full-provide-identifier-with-cui.ts
2
+ import { LowEventInputTracker } from "../low-events/input-tracker.mjs";
3
+ import { OperationFull } from "./operation-full.mjs";
4
+ var OperationFullProvideIdentifierWithCUI = class extends OperationFull {
5
+ lowEventTracker;
6
+ cui;
7
+ provideIdentifier;
8
+ constructor(tracker, config) {
9
+ super(tracker, "provide-identifier");
10
+ this.cui = {
11
+ trigger: (data, options) => {
12
+ this.trigger(data, options);
13
+ },
14
+ // getOptions only fetches assertion options; it is not a user interaction on its own.
15
+ getOptions: this.defineStep(
16
+ "cui-get-options",
17
+ true
18
+ ),
19
+ ceremony: this.defineStep(
20
+ "cui-ceremony"
21
+ ),
22
+ postResponse: this.defineStep("cui-post-response")
23
+ };
24
+ this.provideIdentifier = {
25
+ trigger: (data, options) => {
26
+ this.trigger(data, options);
27
+ },
28
+ clientValidation: this.defineStep("pi-client-validation"),
29
+ postResponse: this.defineStep("pi-post-response")
30
+ };
31
+ void this.subflowStart({ explicitSpecType: config?.explicitSpecType });
32
+ if (!config?.inputHtmlField) {
33
+ return;
34
+ }
35
+ this.lowEventTracker = this.createLowCollector(
36
+ () => new LowEventInputTracker(tracker.getLogger(), config.inputHtmlField, this.lowEventSink("input", "identifier"))
37
+ );
38
+ this.registerLowFlusher(() => this.lowEventTracker?.flushPending());
39
+ }
40
+ destroy() {
41
+ this.lowEventTracker?.destroy();
42
+ }
43
+ };
44
+ export {
45
+ OperationFullProvideIdentifierWithCUI
46
+ };
@@ -0,0 +1,235 @@
1
+ // src/utils/operation-full.ts
2
+ import { reserveCaptureSeq } from "../low-events/capture-sequence.mjs";
3
+ import { inferCeremonyInitiator } from "../low-events/ceremony-initiator.mjs";
4
+ import { v7 as uuidv7 } from "uuid";
5
+ import { normalizeError } from "./normalize-error.mjs";
6
+ import { serializeRawError } from "./serialize-raw-error.mjs";
7
+ var CEREMONY_LOW_EVENT_TAIL_MS = 1500;
8
+ var OperationFull = class {
9
+ constructor(tracker, operationName) {
10
+ this.tracker = tracker;
11
+ this.operationName = operationName;
12
+ }
13
+ startReference;
14
+ captureSpecType;
15
+ captureSessionId;
16
+ lowFlushers = [];
17
+ lowPrefillCapturers = [];
18
+ lowEventSink(sourceKind, fieldRole) {
19
+ return {
20
+ reserveCaptureSeq,
21
+ enqueueLowEvent: (low) => this.tracker.enqueueLowEvent(
22
+ {
23
+ ...low,
24
+ captureSeq: low.captureSeq ?? reserveCaptureSeq(),
25
+ subflowStartEventId: this.startReference,
26
+ sourceKind,
27
+ fieldRole,
28
+ initiator: low.initiator ?? (fieldRole ? `input-${fieldRole}` : "unknown")
29
+ },
30
+ this.captureSessionId
31
+ ),
32
+ flushKeepalive: () => this.tracker.lowEventFlushKeepalive(),
33
+ passwordManagerDomScan: () => this.tracker.isPasswordManagerDomScanEnabled(),
34
+ registerInputs: (fields, flush) => this.tracker.getWindowObserver().registerInputs(
35
+ fields,
36
+ () => ({
37
+ subflowStartEventId: this.startReference,
38
+ fieldRole,
39
+ initiator: fieldRole ? `input-${fieldRole}` : "unknown",
40
+ sessionId: this.captureSessionId
41
+ }),
42
+ flush
43
+ ),
44
+ beginCeremony: (initiator) => this.tracker.getWindowObserver().beginCeremony({
45
+ subflowStartEventId: this.startReference,
46
+ initiator: initiator ?? "unknown",
47
+ sessionId: this.captureSessionId
48
+ })
49
+ };
50
+ }
51
+ createLowCollector(create) {
52
+ try {
53
+ const collector = create();
54
+ this.lowPrefillCapturers.push((newCapture) => collector.capturePrefill(newCapture));
55
+ return collector;
56
+ } catch (error) {
57
+ this.tracker.getLogger().error("Failed to attach low-event collector", error);
58
+ return void 0;
59
+ }
60
+ }
61
+ registerLowFlusher(flush) {
62
+ this.lowFlushers.push(flush);
63
+ }
64
+ captureOptions(options, reference = this.startReference, sessionId = this.captureSessionId, initiator) {
65
+ if (!reference && !initiator) return options;
66
+ const context = {};
67
+ if (reference) {
68
+ context.subflowStartEventId = options?.subflowStartEventId ?? reference;
69
+ context.captureSessionId = options?.captureSessionId ?? sessionId;
70
+ }
71
+ if (initiator) context.initiator = options?.initiator ?? initiator;
72
+ return Object.defineProperties(
73
+ {},
74
+ {
75
+ ...Object.getOwnPropertyDescriptors(options ?? {}),
76
+ ...Object.getOwnPropertyDescriptors(context)
77
+ }
78
+ );
79
+ }
80
+ /** Anchor (subflowStartEventId) of the current start; undefined before the first start. */
81
+ get captureReference() {
82
+ return this.startReference;
83
+ }
84
+ /** Session the current start was tracked under. */
85
+ get captureSession() {
86
+ return this.captureSessionId;
87
+ }
88
+ /**
89
+ * Continue an attempt started elsewhere (e.g. on another page of the same session) without
90
+ * emitting a subflow_started: later steps and low events carry the given anchor and session.
91
+ */
92
+ adoptCapture(reference, sessionId, specType) {
93
+ this.startReference = reference;
94
+ this.captureSessionId = sessionId;
95
+ this.captureSpecType = specType;
96
+ }
97
+ /**
98
+ * Emits an additional subflow_started; construction already emits the initial start.
99
+ * `data` supports SubflowTrigger fields (`actor`, `explicitSpecType`, `ignoreAsInteraction`)
100
+ * plus free-form extras; see SubflowTrigger for programmatic interaction suppression.
101
+ */
102
+ subflowStart(data, options) {
103
+ try {
104
+ for (const flush of this.lowFlushers) {
105
+ try {
106
+ flush();
107
+ } catch (error) {
108
+ this.tracker.getLogger().error("Failed to flush previous low-event context", error);
109
+ }
110
+ }
111
+ this.captureSpecType = data?.explicitSpecType;
112
+ this.startReference = uuidv7();
113
+ this.captureSessionId = this.tracker.getSessionId();
114
+ this.tracker.trackSubflowStarted(this.operationName, data, this.captureOptions(options), this.startReference);
115
+ this.captureInputPrefills(true);
116
+ } catch (error) {
117
+ this.tracker.getLogger().error("Failed to start low-event capture context", error);
118
+ }
119
+ }
120
+ /**
121
+ * Capture values initialized by the application after constructing this operation, without
122
+ * dispatching DOM input events. Call immediately after initialization, before user editing.
123
+ * Each field/group emits at most one prefill per subflow start; values are never recorded.
124
+ */
125
+ capturePrefill() {
126
+ this.captureInputPrefills(false);
127
+ }
128
+ captureInputPrefills(newCapture) {
129
+ for (const capture of this.lowPrefillCapturers) {
130
+ try {
131
+ capture(newCapture);
132
+ } catch (error) {
133
+ this.tracker.getLogger().error("Failed to capture input prefill", error);
134
+ }
135
+ }
136
+ }
137
+ /**
138
+ * @deprecated `subflow_trigger` will be removed in the next major version.
139
+ */
140
+ trigger(data, options) {
141
+ void this.tracker.trackSubflowTrigger(this.operationName, data, this.captureOptions(options));
142
+ }
143
+ customStep(stepName) {
144
+ return this.defineStep(stepName);
145
+ }
146
+ subflowStepStarted(stepName, data, options, ignoreAsInteraction) {
147
+ this.captureSpecType = data?.explicitSpecType ?? this.captureSpecType;
148
+ void this.tracker.trackSubflowStepStarted(
149
+ this.operationName,
150
+ stepName,
151
+ data,
152
+ this.captureOptions(options),
153
+ ignoreAsInteraction
154
+ );
155
+ }
156
+ subflowStepFinished(stepName, data, options) {
157
+ if (!options?.subflowStartEventId || options.subflowStartEventId === this.startReference) {
158
+ this.captureSpecType = data?.explicitSpecType ?? this.captureSpecType;
159
+ }
160
+ void this.tracker.trackSubflowStepFinished(this.operationName, stepName, data, this.captureOptions(options));
161
+ }
162
+ subflowStepError(stepName, data, options) {
163
+ let payload = data;
164
+ try {
165
+ const rawError = this.tracker.getSdkConfig().rawErrors ? options?.rawError : void 0;
166
+ if (rawError !== void 0) payload = { ...data, rawError: serializeRawError(rawError, options?.rawErrorLimits) };
167
+ } catch {
168
+ }
169
+ void this.tracker.trackSubflowStepError(this.operationName, stepName, payload, this.captureOptions(options));
170
+ }
171
+ /**
172
+ * Decorate a (ceremony) step so window low-event collection follows its lifecycle: `start` arms
173
+ * the tracker, `finished`/`error`/`errorTyped` schedule disarming after
174
+ * {@link CEREMONY_LOW_EVENT_TAIL_MS}. A `start` during the tail (ceremony retry) keeps the
175
+ * tracker armed.
176
+ */
177
+ withWindowLowEvents(step, windowTracker) {
178
+ let ceremonyReference;
179
+ const settle = (options) => {
180
+ if (options?.subflowStartEventId && options.subflowStartEventId !== ceremonyReference) return;
181
+ windowTracker.scheduleDisarm(CEREMONY_LOW_EVENT_TAIL_MS);
182
+ };
183
+ const inner = step;
184
+ return {
185
+ ...step,
186
+ start: (data, options) => {
187
+ ceremonyReference = options?.subflowStartEventId ?? this.startReference;
188
+ const spec = data?.explicitSpecType ?? this.captureSpecType;
189
+ const initiator = options?.initiator ?? inferCeremonyInitiator(this.operationName, spec);
190
+ windowTracker.arm(initiator, spec === "passkey-cui");
191
+ inner.start(data, initiator ? { ...options, initiator } : options);
192
+ },
193
+ finished: (data, options) => {
194
+ inner.finished(data, options);
195
+ settle(options);
196
+ },
197
+ error: (error, options) => {
198
+ inner.error(error, options);
199
+ settle(options);
200
+ },
201
+ errorTyped: (error, options) => {
202
+ inner.errorTyped?.(error, options);
203
+ settle(options);
204
+ }
205
+ };
206
+ }
207
+ defineStep(stepName, ignoreAsInteractionOnStart) {
208
+ let stepReference;
209
+ let stepSessionId;
210
+ let stepInitiator;
211
+ const stepOptions = (options) => this.captureOptions(options, stepReference, stepSessionId, stepInitiator);
212
+ const step = {
213
+ start: (data, options) => {
214
+ stepReference = this.startReference;
215
+ stepSessionId = this.captureSessionId;
216
+ stepInitiator = options?.initiator;
217
+ this.subflowStepStarted(stepName, data, stepOptions(options), ignoreAsInteractionOnStart);
218
+ },
219
+ finished: (data, options) => {
220
+ this.subflowStepFinished(stepName, data, stepOptions(options));
221
+ },
222
+ error: (error, options) => {
223
+ this.subflowStepError(stepName, { error: normalizeError(error) }, stepOptions(options));
224
+ },
225
+ errorTyped: (error, options) => {
226
+ this.subflowStepError(stepName, error, stepOptions(options));
227
+ }
228
+ };
229
+ return step;
230
+ }
231
+ };
232
+ export {
233
+ CEREMONY_LOW_EVENT_TAIL_MS,
234
+ OperationFull
235
+ };
@@ -0,0 +1,17 @@
1
+ // src/utils/passkey-deletion-operation-full.ts
2
+ import { OperationFull } from "./operation-full.mjs";
3
+ var PasskeyDeletionOperationFull = class extends OperationFull {
4
+ /** Optional confirmation dialog: start when it opens, finish when the user confirms. */
5
+ userConfirmation;
6
+ /** The deletion request: finish only when the passkey was actually deleted. */
7
+ deletion;
8
+ constructor(tracker, _config = {}) {
9
+ super(tracker, "passkey-deletion");
10
+ this.userConfirmation = this.defineStep("user-confirmation");
11
+ this.deletion = this.defineStep("deletion");
12
+ void this.subflowStart({});
13
+ }
14
+ };
15
+ export {
16
+ PasskeyDeletionOperationFull
17
+ };
@@ -0,0 +1,32 @@
1
+ // src/utils/passkey-enrollment-operation-full.ts
2
+ import { LowEventWindowTracker } from "../low-events/window-tracker.mjs";
3
+ import { OperationFull } from "./operation-full.mjs";
4
+ var PasskeyEnrollmentOperationFull = class extends OperationFull {
5
+ getOptions;
6
+ ceremony;
7
+ postResponse;
8
+ lowEventTracker;
9
+ constructor(tracker, config) {
10
+ super(tracker, "passkey-enrollment");
11
+ this.lowEventTracker = new LowEventWindowTracker(tracker.getLogger(), this.lowEventSink("ceremony"));
12
+ this.registerLowFlusher(() => this.lowEventTracker.disarm());
13
+ this.getOptions = this.defineStep(
14
+ "get-options"
15
+ );
16
+ this.ceremony = this.withWindowLowEvents(
17
+ this.defineStep("ceremony"),
18
+ this.lowEventTracker
19
+ );
20
+ this.postResponse = this.defineStep("post-response");
21
+ void this.subflowStart({
22
+ explicitSpecType: config?.explicitSpecType,
23
+ ...config?.ignoreAsInteraction ? { ignoreAsInteraction: true } : {}
24
+ });
25
+ }
26
+ destroy() {
27
+ this.lowEventTracker.destroy();
28
+ }
29
+ };
30
+ export {
31
+ PasskeyEnrollmentOperationFull
32
+ };
@@ -0,0 +1,34 @@
1
+ // src/utils/passkey-login-operation-full.ts
2
+ import { LowEventWindowTracker } from "../low-events/window-tracker.mjs";
3
+ import { OperationFull } from "./operation-full.mjs";
4
+ var PasskeyLoginOperationFull = class extends OperationFull {
5
+ getOptions;
6
+ ceremony;
7
+ postResponse;
8
+ lowEventTracker;
9
+ constructor(tracker, config) {
10
+ super(tracker, "passkey-login");
11
+ this.lowEventTracker = new LowEventWindowTracker(tracker.getLogger(), this.lowEventSink("ceremony"));
12
+ this.registerLowFlusher(() => this.lowEventTracker.disarm());
13
+ this.getOptions = this.defineStep("get-options");
14
+ this.ceremony = this.withWindowLowEvents(
15
+ this.defineStep("ceremony"),
16
+ this.lowEventTracker
17
+ );
18
+ this.postResponse = this.defineStep("post-response");
19
+ void this.subflowStart({ explicitSpecType: config?.explicitSpecType });
20
+ }
21
+ /**
22
+ * @deprecated `subflow_trigger` is not supported for passkey-login and will be removed in the
23
+ * next major version.
24
+ */
25
+ trigger(data, options) {
26
+ super.trigger(data, options);
27
+ }
28
+ destroy() {
29
+ this.lowEventTracker.destroy();
30
+ }
31
+ };
32
+ export {
33
+ PasskeyLoginOperationFull
34
+ };
@@ -0,0 +1,27 @@
1
+ // src/utils/password-enrollment-operation-full.ts
2
+ import { LowEventInputTracker } from "../low-events/input-tracker.mjs";
3
+ import { OperationFull } from "./operation-full.mjs";
4
+ var PasswordEnrollmentOperationFull = class extends OperationFull {
5
+ clientValidation;
6
+ postResponse;
7
+ lowEventTracker;
8
+ constructor(tracker, config) {
9
+ super(tracker, "password-enrollment");
10
+ this.clientValidation = this.defineStep("client-validation");
11
+ this.postResponse = this.defineStep("post-response");
12
+ void this.subflowStart({ explicitSpecType: config?.explicitSpecType });
13
+ if (!config?.inputHtmlField) {
14
+ return;
15
+ }
16
+ this.lowEventTracker = this.createLowCollector(
17
+ () => new LowEventInputTracker(tracker.getLogger(), config.inputHtmlField, this.lowEventSink("input", "password"))
18
+ );
19
+ this.registerLowFlusher(() => this.lowEventTracker?.flushPending());
20
+ }
21
+ destroy() {
22
+ this.lowEventTracker?.destroy();
23
+ }
24
+ };
25
+ export {
26
+ PasswordEnrollmentOperationFull
27
+ };
@@ -0,0 +1,61 @@
1
+ // src/utils/password-login-operation-full.ts
2
+ import { LowEventInputTracker } from "../low-events/input-tracker.mjs";
3
+ import { OperationFull } from "./operation-full.mjs";
4
+ var PasswordLoginOperationFull = class extends OperationFull {
5
+ clientValidation;
6
+ postResponse;
7
+ /**
8
+ * Conditional-UI passkey ceremony that runs while the password form is mounted. The three steps
9
+ * are emitted under the same `password-login` subflow so a CUI passkey login on this screen is
10
+ * attributed to password-login rather than a separate subflow.
11
+ */
12
+ cui;
13
+ lowEventTracker;
14
+ identifierLowEventTracker;
15
+ constructor(tracker, config) {
16
+ super(tracker, "password-login");
17
+ this.clientValidation = this.defineStep("client-validation");
18
+ this.postResponse = this.defineStep("post-response");
19
+ this.cui = {
20
+ // getOptions only fetches assertion options; it is not a user interaction on its own.
21
+ getOptions: this.defineStep(
22
+ "cui-get-options",
23
+ true
24
+ ),
25
+ ceremony: this.defineStep("cui-ceremony"),
26
+ postResponse: this.defineStep("cui-post-response")
27
+ };
28
+ void this.subflowStart({ explicitSpecType: config?.explicitSpecType });
29
+ if (config?.identifierInputHtmlField) {
30
+ this.identifierLowEventTracker = this.createLowCollector(
31
+ () => new LowEventInputTracker(
32
+ tracker.getLogger(),
33
+ config.identifierInputHtmlField,
34
+ this.lowEventSink("input", "identifier")
35
+ )
36
+ );
37
+ this.registerLowFlusher(() => this.identifierLowEventTracker?.flushPending());
38
+ }
39
+ if (!config?.inputHtmlField) {
40
+ return;
41
+ }
42
+ this.lowEventTracker = this.createLowCollector(
43
+ () => new LowEventInputTracker(tracker.getLogger(), config.inputHtmlField, this.lowEventSink("input", "password"))
44
+ );
45
+ this.registerLowFlusher(() => this.lowEventTracker?.flushPending());
46
+ }
47
+ /**
48
+ * @deprecated `subflow_trigger` is not supported for password-login and will be removed in the
49
+ * next major version.
50
+ */
51
+ trigger(data, options) {
52
+ super.trigger(data, options);
53
+ }
54
+ destroy() {
55
+ this.lowEventTracker?.destroy();
56
+ this.identifierLowEventTracker?.destroy();
57
+ }
58
+ };
59
+ export {
60
+ PasswordLoginOperationFull
61
+ };