@a4anthony/proctorkit-types 0.0.0-canary.97dc0c42.20260708235358

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anthony Gakro
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/api.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ export interface SignedUploadUrl {
2
+ url: string;
3
+ method: "PUT";
4
+ headers: Record<string, string>;
5
+ expiresAt: number;
6
+ objectKey: string;
7
+ }
8
+ export interface IngestBatchRequest {
9
+ batchId: string;
10
+ sessionId: string;
11
+ candidate?: import("./worker-protocol.js").CandidateIdentity;
12
+ events: import("./events.js").SessionEvent[];
13
+ }
14
+ export interface IngestBatchResponse {
15
+ batchId: string;
16
+ accepted: string[];
17
+ rejected: string[];
18
+ }
19
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,KAAK,CAAC;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,sBAAsB,EAAE,iBAAiB,CAAC;IAC7D,MAAM,EAAE,OAAO,aAAa,EAAE,YAAY,EAAE,CAAC;CAC9C;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB"}
package/dist/api.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":""}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The canonical list of every event kind, as a runtime value.
3
+ *
4
+ * This is the SINGLE SOURCE OF TRUTH for event kinds. The `EventKind`
5
+ * type is derived from it (`typeof EVENT_KINDS[number]`), and the
6
+ * server's ingest validation builds its zod enum from the same array
7
+ * (`z.enum(EVENT_KINDS)`) so the wire contract can never silently drift
8
+ * from the type. Add a new kind here and both the type and the ingest
9
+ * gate pick it up. (A hand-maintained copy of this list in the ingest
10
+ * DTO is exactly how `session.checkpoint` once 400'd every batch that
11
+ * carried it — don't reintroduce a second list.)
12
+ */
13
+ export declare const EVENT_KINDS: readonly ["session.started", "session.heartbeat", "session.end_requested", "session.ended", "session.resumed", "session.abandoned", "session.checkpoint", "sdk.initialized", "sdk.ready", "sdk.error", "sdk.upload.failed", "sdk.media.failed", "sdk.permission.denied", "sdk.worker.failed", "sdk.storage.fallback", "sdk.recording.buffer-degraded", "sdk.recording.buffer-evicted", "sdk.stop.drain-timeout", "focus.lost", "focus.gained", "tab.hidden", "tab.visible", "page.viewed", "fullscreen.entered", "fullscreen.exited", "clipboard.copy", "clipboard.paste", "clipboard.cut", "contextmenu.opened", "keyboard.blocked", "screenshot.attempted", "screen-share.started", "screen-share.stopped", "screen-share.declined", "screen-share.wrong-surface", "screen-share.recording.started", "screen-share.recording.stopped", "screen-share.recording.unavailable", "screen-share.chunk-uploaded", "screen-share.chunk-dropped", "webcam.started", "webcam.stopped", "webcam.declined", "webcam.unavailable", "webcam.photo.captured", "webcam.photo.dropped", "webcam.recording.started", "webcam.recording.stopped", "webcam.recording.chunk-uploaded", "webcam.recording.chunk-dropped", "video-clip.started", "video-clip.stopped", "video-clip.uploaded", "video-clip.dropped", "audio-clip.started", "audio-clip.stopped", "audio-clip.uploaded", "audio-clip.dropped", "audio-playback.started", "audio-playback.paused", "audio-playback.stopped", "audio-playback.ended", "audio-playback.error", "audio-playback.sink-applied", "audio-playback.sink-unsupported", "audio-playback.sink-failed", "audio-playback.sink-disconnected", "video-playback.started", "video-playback.paused", "video-playback.stopped", "video-playback.ended", "video-playback.error", "video-playback.sink-applied", "video-playback.sink-unsupported", "video-playback.sink-failed", "video-playback.sink-disconnected", "pointer.left-window", "pointer.returned", "idle.started", "idle.ended", "session.fingerprint", "face.detector-ready", "face.detector-failed", "face.detector-degraded", "face.lost", "face.returned", "face.multiple", "face.multiple-cleared", "face.gaze-off-screen", "face.gaze-restored", "screen.window-switched", "screen.scene-stable", "screen.recording-paused", "webcam.phone-detected", "webcam.phone-cleared", "webcam.book-detected", "webcam.book-cleared", "webcam.second-device-detected", "webcam.second-device-cleared", "webcam.second-screen-detected", "webcam.second-screen-cleared", "face.lost-at-snapshot", "face.multiple-at-snapshot", "face.gaze-off-screen-at-snapshot", "face.identity-mismatch-at-snapshot", "webcam.phone-detected-at-snapshot", "webcam.book-detected-at-snapshot", "webcam.second-device-detected-at-snapshot", "webcam.second-screen-detected-at-snapshot", "media.frame.captured", "media.audio.chunk", "network.online", "network.offline", "preflight.started", "preflight.row-checking", "preflight.row-passed", "preflight.row-failed", "preflight.row-retried", "preflight.attestation", "preflight.device-picked", "preflight.devices-enumerated", "preflight.devices-changed", "preflight.face-detected", "preflight.camera-photo", "preflight.completed", "text.started", "text.paste", "text.copy", "text.cut", "text.keystroke-summary", "text.checkpoint", "text.focus-lost", "text.focus-regained", "text.synthetic-input", "text.saved", "text.save-failed", "text.submitted"];
14
+ export type EventKind = (typeof EVENT_KINDS)[number];
15
+ export interface SessionEvent {
16
+ id: string;
17
+ sessionId: string;
18
+ kind: EventKind;
19
+ timestamp: number;
20
+ payload?: Record<string, unknown>;
21
+ /**
22
+ * Per-device fingerprint stamped by the SDK at construction time
23
+ * (see `computeDeviceFingerprint`). Optional so legacy SDK
24
+ * builds + tests can still construct a SessionEvent without one.
25
+ * Promoted to a real column on the server's Event row so the
26
+ * dashboard can render multi-device timelines as parallel swim
27
+ * lanes without scanning the payload JSON.
28
+ */
29
+ fingerprintId?: string;
30
+ }
31
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW,6wGA+Sd,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,SAAS,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB"}
package/dist/events.js ADDED
@@ -0,0 +1,317 @@
1
+ /**
2
+ * The canonical list of every event kind, as a runtime value.
3
+ *
4
+ * This is the SINGLE SOURCE OF TRUTH for event kinds. The `EventKind`
5
+ * type is derived from it (`typeof EVENT_KINDS[number]`), and the
6
+ * server's ingest validation builds its zod enum from the same array
7
+ * (`z.enum(EVENT_KINDS)`) so the wire contract can never silently drift
8
+ * from the type. Add a new kind here and both the type and the ingest
9
+ * gate pick it up. (A hand-maintained copy of this list in the ingest
10
+ * DTO is exactly how `session.checkpoint` once 400'd every batch that
11
+ * carried it — don't reintroduce a second list.)
12
+ */
13
+ export const EVENT_KINDS = [
14
+ "session.started",
15
+ "session.heartbeat",
16
+ "session.end_requested",
17
+ "session.ended",
18
+ /**
19
+ * Emitted once when a candidate resumes a live session after a
20
+ * mid-assessment page refresh — i.e. the attempt resolver returned
21
+ * `reuse_active_attempt` and the resume re-check (camera/mic/speaker/
22
+ * screen-share/fullscreen) passed. The payload carries the per-check
23
+ * verdicts and any device the candidate switched to during the
24
+ * re-check, so a proctor has an audit trail of the interruption
25
+ * (count of `session.resumed` events = number of resumes) and of any
26
+ * device change mid-exam. Distinct from `session.started`, which the
27
+ * SDK also re-emits on resume as a side effect of re-creating the
28
+ * client.
29
+ */
30
+ "session.resumed",
31
+ /**
32
+ * Emitted server-side when a within-session attempt is retired without a
33
+ * clean `session.ended` — the candidate dropped (disconnect, stale backstop,
34
+ * or a different tab/device taking over) rather than finishing. Marks the
35
+ * close of an attempt on the review timeline; a resume opens the next one
36
+ * with `session.resumed`. Distinct from the `session.abandoned` *webhook*,
37
+ * which fires when the whole sitting ends abandoned. Server-derived because
38
+ * an abandoned client can't report its own departure.
39
+ */
40
+ "session.abandoned",
41
+ /**
42
+ * Runtime integrity checkpoint: a re-verification, run on demand or on a
43
+ * schedule, that compares the live environment against the state that
44
+ * passed preflight. The payload carries `{ ok, changes[] }` — each change
45
+ * a drift signal (permission revoked, selected device gone, external
46
+ * monitor connected, camera track ended/black/frozen, mic track
47
+ * ended/muted). `ok: true` means "still matches preflight." A run is
48
+ * emitted every time so the count is an audit trail; the SDK never
49
+ * pauses or fails the session on drift — the host decides.
50
+ */
51
+ "session.checkpoint",
52
+ "sdk.initialized",
53
+ "sdk.ready",
54
+ "sdk.error",
55
+ "sdk.upload.failed",
56
+ "sdk.media.failed",
57
+ "sdk.permission.denied",
58
+ "sdk.worker.failed",
59
+ "sdk.storage.fallback",
60
+ "sdk.recording.buffer-degraded",
61
+ "sdk.recording.buffer-evicted",
62
+ "sdk.stop.drain-timeout",
63
+ "focus.lost",
64
+ "focus.gained",
65
+ "tab.hidden",
66
+ "tab.visible",
67
+ /**
68
+ * Emitted once when the proctored runtime page loads — on first start
69
+ * and again on every resume (the SDK re-boots). Carries the page URL,
70
+ * path, referrer and document title, plus whether the page was
71
+ * backgrounded at load. Records *that the candidate landed on the exam
72
+ * page* (and from where), distinct from the presence signals
73
+ * (`tab.*` / `focus.*`) that track them leaving it. The browser cannot
74
+ * see which external page they navigate to, so this only ever reports
75
+ * the exam's own URL.
76
+ */
77
+ "page.viewed",
78
+ "fullscreen.entered",
79
+ "fullscreen.exited",
80
+ "clipboard.copy",
81
+ "clipboard.paste",
82
+ "clipboard.cut",
83
+ "contextmenu.opened",
84
+ "keyboard.blocked",
85
+ "screenshot.attempted",
86
+ "screen-share.started",
87
+ "screen-share.stopped",
88
+ "screen-share.declined",
89
+ "screen-share.wrong-surface",
90
+ "screen-share.recording.started",
91
+ "screen-share.recording.stopped",
92
+ "screen-share.recording.unavailable",
93
+ "screen-share.chunk-uploaded",
94
+ "screen-share.chunk-dropped",
95
+ "webcam.started",
96
+ "webcam.stopped",
97
+ "webcam.declined",
98
+ "webcam.unavailable",
99
+ "webcam.photo.captured",
100
+ "webcam.photo.dropped",
101
+ "webcam.recording.started",
102
+ "webcam.recording.stopped",
103
+ "webcam.recording.chunk-uploaded",
104
+ "webcam.recording.chunk-dropped",
105
+ "video-clip.started",
106
+ "video-clip.stopped",
107
+ "video-clip.uploaded",
108
+ "video-clip.dropped",
109
+ "audio-clip.started",
110
+ "audio-clip.stopped",
111
+ "audio-clip.uploaded",
112
+ "audio-clip.dropped",
113
+ "audio-playback.started",
114
+ "audio-playback.paused",
115
+ "audio-playback.stopped",
116
+ "audio-playback.ended",
117
+ "audio-playback.error",
118
+ "audio-playback.sink-applied",
119
+ "audio-playback.sink-unsupported",
120
+ "audio-playback.sink-failed",
121
+ "audio-playback.sink-disconnected",
122
+ "video-playback.started",
123
+ "video-playback.paused",
124
+ "video-playback.stopped",
125
+ "video-playback.ended",
126
+ "video-playback.error",
127
+ "video-playback.sink-applied",
128
+ "video-playback.sink-unsupported",
129
+ "video-playback.sink-failed",
130
+ "video-playback.sink-disconnected",
131
+ "pointer.left-window",
132
+ "pointer.returned",
133
+ "idle.started",
134
+ "idle.ended",
135
+ "session.fingerprint",
136
+ "face.detector-ready",
137
+ "face.detector-failed",
138
+ "face.detector-degraded",
139
+ "face.lost",
140
+ "face.returned",
141
+ "face.multiple",
142
+ "face.multiple-cleared",
143
+ "face.gaze-off-screen",
144
+ "face.gaze-restored",
145
+ /**
146
+ * Post-hoc screen-share analysis events. Emitted by the
147
+ * packages/screen-detect Python sidecar after the stitched
148
+ * screen-share recording is assembled at session-end. Source
149
+ * column is set to "analyzer" and the analysisRunId points
150
+ * at the ScreenAnalysisRun row that produced this event,
151
+ * mirroring the face-analysis pattern.
152
+ *
153
+ * Why we need them: client-side focus.lost only fires when
154
+ * the candidate switches AWAY from the exam tab; it misses
155
+ * window-level switches that stay in-browser (a popover
156
+ * ChatGPT in another tab of the same Chrome instance, an
157
+ * OS-level overlay like Copilot, a second monitor in shared
158
+ * view, etc.). The screen recording itself is the only
159
+ * signal here — these events surface visual context changes
160
+ * that the live client never saw.
161
+ */
162
+ "screen.window-switched",
163
+ "screen.scene-stable",
164
+ "screen.recording-paused",
165
+ /**
166
+ * Object-detection signals emitted by the post-hoc YOLO11
167
+ * pass that runs alongside face analysis on the stitched
168
+ * webcam recording. The YOLO model recognises 80 COCO
169
+ * classes; we surface only the subset that maps to
170
+ * "policy violation in a proctored exam":
171
+ *
172
+ * - phone-detected — a cell phone visible in frame
173
+ * - book-detected — a printed book / notebook visible
174
+ * - second-device-detected — a laptop other than the
175
+ * candidate's primary visible in frame
176
+ * - second-screen-detected — a TV / monitor other than
177
+ * the candidate's primary visible in frame
178
+ *
179
+ * The matching `*-cleared` event fires when the object
180
+ * stops being detected for a debounce window. Bounding box
181
+ * + confidence land in the payload.
182
+ *
183
+ * Sourced from the same FaceAnalysisRun as the face events
184
+ * (we run them in one pass over the stitched video) so
185
+ * `analysisRunId` is shared.
186
+ */
187
+ "webcam.phone-detected",
188
+ "webcam.phone-cleared",
189
+ "webcam.book-detected",
190
+ "webcam.book-cleared",
191
+ "webcam.second-device-detected",
192
+ "webcam.second-device-cleared",
193
+ "webcam.second-screen-detected",
194
+ "webcam.second-screen-cleared",
195
+ /**
196
+ * Random-photo analysis events. The SDK uploads JPEG snapshots
197
+ * every 20-60 seconds during a proctored session; the photo-
198
+ * analysis worker runs the same detectors against each photo
199
+ * AFTER session-end as a backstop to the stitched-video pass.
200
+ *
201
+ * Why a separate set of kinds (`-at-snapshot`) rather than
202
+ * reusing `face.lost` / `webcam.phone-detected`:
203
+ *
204
+ * - The cert needs to prefer the photo's actual JPEG over
205
+ * a video-frame extraction as the evidence thumbnail —
206
+ * photos are 1080p singles, video frames are transcoded
207
+ * and compressed.
208
+ * - Reviewers reading the timeline benefit from knowing
209
+ * which signal came from continuous video analysis vs.
210
+ * intermittent snapshot sampling.
211
+ * - Source field is "photo-analyzer" (vs "analyzer" for
212
+ * the video pass) so audit queries can separate them.
213
+ *
214
+ * Payload always carries `photoId` (BigInt as string) so the
215
+ * dashboard can join back to webcam_photos and display the
216
+ * actual JPEG inline.
217
+ */
218
+ "face.lost-at-snapshot",
219
+ "face.multiple-at-snapshot",
220
+ "face.gaze-off-screen-at-snapshot",
221
+ "face.identity-mismatch-at-snapshot",
222
+ "webcam.phone-detected-at-snapshot",
223
+ "webcam.book-detected-at-snapshot",
224
+ "webcam.second-device-detected-at-snapshot",
225
+ "webcam.second-screen-detected-at-snapshot",
226
+ "media.frame.captured",
227
+ "media.audio.chunk",
228
+ "network.online",
229
+ "network.offline",
230
+ // Preflight wizard lifecycle. Emitted by @a4anthony/proctorkit-vue's
231
+ // preflight telemetry composable. The server materialises these into
232
+ // the preflight_outcomes row keyed off the candidate's sessionId
233
+ // (the same opaque id the customer's host page assigns; correlationId
234
+ // on the Vue API surface, sessionId on the wire).
235
+ "preflight.started",
236
+ "preflight.row-checking",
237
+ "preflight.row-passed",
238
+ "preflight.row-failed",
239
+ "preflight.row-retried",
240
+ "preflight.attestation",
241
+ "preflight.device-picked",
242
+ /**
243
+ * Initial enumeration of available media devices for a row's
244
+ * picker. Fires once per row (microphone / speaker / camera)
245
+ * after permission resolves so device labels are populated.
246
+ * Payload: { row, devices: [{ deviceId, label, groupId, virtual }] }
247
+ */
248
+ "preflight.devices-enumerated",
249
+ /**
250
+ * Fired in response to the browser's `navigator.mediaDevices
251
+ * .devicechange` event during preflight. The device list
252
+ * changed (plug/unplug) -- carries the new snapshot. Distinct
253
+ * from devices-enumerated so the dashboard timeline can mark
254
+ * "candidate plugged in headphones mid-flow."
255
+ * Payload: { row?, devices: [{ deviceId, label, groupId, virtual }] }
256
+ */
257
+ "preflight.devices-changed",
258
+ "preflight.face-detected",
259
+ "preflight.camera-photo",
260
+ "preflight.completed",
261
+ /**
262
+ * Proctored writing-answer events, emitted by
263
+ * @a4anthony/proctorkit-vue's <ProctoredTextarea> via
264
+ * client.recordTextAnswer(). They capture HOW a written answer was
265
+ * produced (the integrity signals) — the answer TEXT itself is owned
266
+ * by the integrator (saved through their own backend via the
267
+ * component's saveAnswer hook); the proctoring server stores only
268
+ * these signals plus, when persistAnswer is on, content checkpoints.
269
+ *
270
+ * Every payload carries `questionId` so multiple writing questions in
271
+ * one session stay separable. The server materialises them into a
272
+ * per-answer outcome row (paste ratio, active typing time, synthetic-
273
+ * input flag, checkpoint history).
274
+ *
275
+ * - text.started — the candidate began (or resumed) an answer.
276
+ * - text.paste — a paste into the field. Payload: { questionId,
277
+ * length, pastedRatio, blocked, content? }. `content` only when
278
+ * the host opted into capturePasteContent (PII-gated).
279
+ * - text.copy / text.cut — the candidate copied or cut from the
280
+ * field. Payload: { questionId, length, blocked? } where `length`
281
+ * is the selection size. `blocked` when the host enforced
282
+ * blockCopy / blockCut (the attempt is still recorded).
283
+ * - text.keystroke-summary — a sampled window of typing dynamics.
284
+ * Payload: { questionId, windowMs, keystrokes, backspaces,
285
+ * charsAdded }. Never the keys themselves.
286
+ * - text.checkpoint — a periodic snapshot of the answer so a
287
+ * reviewer can replay how it grew, and so a refresh can rehydrate
288
+ * from the server. Payload: { questionId, savedAt, length,
289
+ * content? } (content only when persistAnswer is on).
290
+ * - text.focus-lost / text.focus-regained — the candidate left /
291
+ * returned to the field mid-answer (distinct from window-level
292
+ * focus). Payload carries { questionId, durationMs? }.
293
+ * - text.synthetic-input — the answer changed without a
294
+ * corresponding keystroke, an untrusted (isTrusted:false) input
295
+ * event fired, or a writing-assistant footprint (e.g. Grammarly's
296
+ * injected DOM) was seen. Records the footprint for review; it is
297
+ * never blocked and is not a guarantee. Payload: { questionId,
298
+ * source: "grammarly" | "no-keystroke" | "untrusted-event" }.
299
+ * - text.saved / text.save-failed — the host's saveAnswer resolved /
300
+ * rejected. save-failed carries a stable { code, reason }.
301
+ * - text.submitted — the candidate submitted. Payload: { questionId,
302
+ * length, pasteCount, typingMs, syntheticDetected }.
303
+ */
304
+ "text.started",
305
+ "text.paste",
306
+ "text.copy",
307
+ "text.cut",
308
+ "text.keystroke-summary",
309
+ "text.checkpoint",
310
+ "text.focus-lost",
311
+ "text.focus-regained",
312
+ "text.synthetic-input",
313
+ "text.saved",
314
+ "text.save-failed",
315
+ "text.submitted",
316
+ ];
317
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export * from "./events.js";
2
+ export * from "./session.js";
3
+ export * from "./api.js";
4
+ export * from "./worker-protocol.js";
5
+ export * from "./production-safety.js";
6
+ export * from "./status.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export * from "./events.js";
2
+ export * from "./session.js";
3
+ export * from "./api.js";
4
+ export * from "./worker-protocol.js";
5
+ export * from "./production-safety.js";
6
+ export * from "./status.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,aAAa,CAAC"}
@@ -0,0 +1,15 @@
1
+ export type EnvLike = Record<string, string | undefined>;
2
+ export declare const PRODUCTION_DESTRUCTIVE_ACTIONS_OVERRIDE = "ALLOW_PRODUCTION_DESTRUCTIVE_ACTIONS";
3
+ export declare const PRODUCTION_PUBLIC_SESSION_RESET_FLAG = "ALLOW_PRODUCTION_PUBLIC_SESSION_RESET";
4
+ export declare const PRODUCTION_PUBLIC_SESSION_DELETE_FLAG = "ALLOW_PRODUCTION_PUBLIC_SESSION_DELETE";
5
+ export declare const PRODUCTION_DASHBOARD_SESSION_DELETE_FLAG = "ALLOW_PRODUCTION_DASHBOARD_SESSION_DELETE";
6
+ export declare const PRODUCTION_ADMIN_DESTRUCTIVE_ACTIONS_FLAG = "ALLOW_PRODUCTION_ADMIN_DESTRUCTIVE_ACTIONS";
7
+ export declare const PRODUCTION_DEMO_SESSION_CLEANUP_FLAG = "ALLOW_PRODUCTION_DEMO_SESSION_CLEANUP";
8
+ export declare const LOCAL_PREVIEW_DATABASE_FLAG = "ALLOW_LOCAL_PREVIEW_DATABASE";
9
+ export declare function isProductionRuntime(env?: EnvLike): boolean;
10
+ export declare function envFlagEnabled(value: string | undefined): boolean;
11
+ export declare function productionActionAllowed(env: EnvLike, featureFlag: string): boolean;
12
+ export declare function productionActionBlockReason(env: EnvLike, action: string, featureFlag: string): string | null;
13
+ export declare function productionDatabaseSafetyIssues(env: EnvLike): string[];
14
+ export declare function assertProductionDatabaseSafety(env: EnvLike): void;
15
+ //# sourceMappingURL=production-safety.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"production-safety.d.ts","sourceRoot":"","sources":["../src/production-safety.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAEzD,eAAO,MAAM,uCAAuC,yCACZ,CAAC;AACzC,eAAO,MAAM,oCAAoC,0CACR,CAAC;AAC1C,eAAO,MAAM,qCAAqC,2CACR,CAAC;AAC3C,eAAO,MAAM,wCAAwC,8CACR,CAAC;AAC9C,eAAO,MAAM,yCAAyC,+CACR,CAAC;AAC/C,eAAO,MAAM,oCAAoC,0CACR,CAAC;AAC1C,eAAO,MAAM,2BAA2B,iCAAiC,CAAC;AAE1E,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,OAAY,GAAG,OAAO,CAE9D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAGjE;AAED,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,OAAO,EACZ,WAAW,EAAE,MAAM,GAClB,OAAO,CAMT;AAED,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,MAAM,GAAG,IAAI,CAOf;AAED,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,EAAE,CAkDrE;AAED,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAIjE"}
@@ -0,0 +1,110 @@
1
+ export const PRODUCTION_DESTRUCTIVE_ACTIONS_OVERRIDE = "ALLOW_PRODUCTION_DESTRUCTIVE_ACTIONS";
2
+ export const PRODUCTION_PUBLIC_SESSION_RESET_FLAG = "ALLOW_PRODUCTION_PUBLIC_SESSION_RESET";
3
+ export const PRODUCTION_PUBLIC_SESSION_DELETE_FLAG = "ALLOW_PRODUCTION_PUBLIC_SESSION_DELETE";
4
+ export const PRODUCTION_DASHBOARD_SESSION_DELETE_FLAG = "ALLOW_PRODUCTION_DASHBOARD_SESSION_DELETE";
5
+ export const PRODUCTION_ADMIN_DESTRUCTIVE_ACTIONS_FLAG = "ALLOW_PRODUCTION_ADMIN_DESTRUCTIVE_ACTIONS";
6
+ export const PRODUCTION_DEMO_SESSION_CLEANUP_FLAG = "ALLOW_PRODUCTION_DEMO_SESSION_CLEANUP";
7
+ export const LOCAL_PREVIEW_DATABASE_FLAG = "ALLOW_LOCAL_PREVIEW_DATABASE";
8
+ export function isProductionRuntime(env = {}) {
9
+ return env["NODE_ENV"] === "production" || env["APP_ENV"] === "production";
10
+ }
11
+ export function envFlagEnabled(value) {
12
+ const v = value?.trim().toLowerCase();
13
+ return v === "true" || v === "1" || v === "yes" || v === "on";
14
+ }
15
+ export function productionActionAllowed(env, featureFlag) {
16
+ if (!isProductionRuntime(env))
17
+ return true;
18
+ return (envFlagEnabled(env[featureFlag]) ||
19
+ envFlagEnabled(env[PRODUCTION_DESTRUCTIVE_ACTIONS_OVERRIDE]));
20
+ }
21
+ export function productionActionBlockReason(env, action, featureFlag) {
22
+ if (productionActionAllowed(env, featureFlag))
23
+ return null;
24
+ return (`${action} is disabled in production. Set ${featureFlag}=true for this ` +
25
+ `specific path, or ${PRODUCTION_DESTRUCTIVE_ACTIONS_OVERRIDE}=true for a ` +
26
+ "short break-glass window.");
27
+ }
28
+ export function productionDatabaseSafetyIssues(env) {
29
+ if (!isProductionRuntime(env))
30
+ return [];
31
+ const raw = env["DATABASE_URL"];
32
+ if (!raw)
33
+ return ["DATABASE_URL must be set in production"];
34
+ const parsed = parseDatabaseUrl(raw);
35
+ if (!parsed) {
36
+ return ["DATABASE_URL must be a parseable PostgreSQL connection URL"];
37
+ }
38
+ const issues = [];
39
+ const host = parsed.hostname.toLowerCase();
40
+ const allowLocalPreviewDatabase = env["APP_ENV"] !== "production" &&
41
+ envFlagEnabled(env[LOCAL_PREVIEW_DATABASE_FLAG]);
42
+ if (!allowLocalPreviewDatabase &&
43
+ (host === "localhost" ||
44
+ host === "127.0.0.1" ||
45
+ host === "0.0.0.0" ||
46
+ host === "::1" ||
47
+ host === "[::1]")) {
48
+ issues.push("DATABASE_URL must not point at localhost in production");
49
+ }
50
+ if (parsed.database.endsWith("_test")) {
51
+ issues.push("DATABASE_URL must not point at a *_test database in production");
52
+ }
53
+ if (!allowLocalPreviewDatabase &&
54
+ parsed.username === "proctorkit" &&
55
+ parsed.password === "proctorkit") {
56
+ issues.push("DATABASE_URL must not use the default local credentials");
57
+ }
58
+ if (!allowLocalPreviewDatabase && envFlagEnabled(env["REQUIRE_DATABASE_SSL"])) {
59
+ const sslMode = parsed.searchParams["sslmode"]?.toLowerCase();
60
+ if (!sslMode || sslMode === "disable") {
61
+ issues.push("DATABASE_URL must include sslmode=require or stronger when REQUIRE_DATABASE_SSL=true");
62
+ }
63
+ }
64
+ return issues;
65
+ }
66
+ export function assertProductionDatabaseSafety(env) {
67
+ const issues = productionDatabaseSafetyIssues(env);
68
+ if (issues.length === 0)
69
+ return;
70
+ throw new Error(`Unsafe production database configuration:\n- ${issues.join("\n- ")}`);
71
+ }
72
+ function parseDatabaseUrl(raw) {
73
+ const match = raw.match(/^[a-z][a-z0-9+.-]*:\/\/(?:(?<auth>[^@/?#]*)@)?(?<host>\[[^\]]+\]|[^/:?#]+)(?::\d+)?\/(?<path>[^?#]*)(?:\?(?<query>[^#]*))?/i);
74
+ if (!match?.groups?.["host"] || !match.groups["path"])
75
+ return null;
76
+ const auth = match.groups["auth"] ?? "";
77
+ const authParts = auth.split(":");
78
+ const username = decodeUrlPart(authParts[0] ?? "");
79
+ const password = decodeUrlPart(authParts.slice(1).join(":"));
80
+ const database = decodeUrlPart(match.groups["path"].replace(/^\/+/, ""));
81
+ return {
82
+ hostname: match.groups["host"],
83
+ username,
84
+ password,
85
+ database,
86
+ searchParams: parseQuery(match.groups["query"] ?? ""),
87
+ };
88
+ }
89
+ function parseQuery(raw) {
90
+ const out = {};
91
+ for (const part of raw.split("&")) {
92
+ if (!part)
93
+ continue;
94
+ const eq = part.indexOf("=");
95
+ const key = decodeUrlPart(eq === -1 ? part : part.slice(0, eq));
96
+ const value = decodeUrlPart(eq === -1 ? "" : part.slice(eq + 1));
97
+ if (key)
98
+ out[key] = value;
99
+ }
100
+ return out;
101
+ }
102
+ function decodeUrlPart(value) {
103
+ try {
104
+ return decodeURIComponent(value);
105
+ }
106
+ catch {
107
+ return value;
108
+ }
109
+ }
110
+ //# sourceMappingURL=production-safety.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"production-safety.js","sourceRoot":"","sources":["../src/production-safety.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export type SessionStatus = "pending" | "active" | "ended" | "abandoned";
2
+ export interface Session {
3
+ id: string;
4
+ candidateId: string;
5
+ orgId: string;
6
+ status: SessionStatus;
7
+ startedAt: number | null;
8
+ endedAt: number | null;
9
+ metadata?: Record<string, unknown>;
10
+ }
11
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,WAAW,CAAC;AAEzE,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":""}