@scanly/browser 2.0.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 (142) hide show
  1. package/README.md +3 -0
  2. package/dist/batch/batch-controller.d.ts +71 -0
  3. package/dist/batch/batch-controller.d.ts.map +1 -0
  4. package/dist/batch/batch-controller.js +422 -0
  5. package/dist/batch/batch-controller.js.map +1 -0
  6. package/dist/batch/batch-session.d.ts +53 -0
  7. package/dist/batch/batch-session.d.ts.map +1 -0
  8. package/dist/batch/batch-session.js +267 -0
  9. package/dist/batch/batch-session.js.map +1 -0
  10. package/dist/batch/index.d.ts +4 -0
  11. package/dist/batch/index.d.ts.map +1 -0
  12. package/dist/batch/index.js +4 -0
  13. package/dist/batch/index.js.map +1 -0
  14. package/dist/batch/types.d.ts +101 -0
  15. package/dist/batch/types.d.ts.map +1 -0
  16. package/dist/batch/types.js +2 -0
  17. package/dist/batch/types.js.map +1 -0
  18. package/dist/browser-session.d.ts +51 -0
  19. package/dist/browser-session.d.ts.map +1 -0
  20. package/dist/browser-session.js +168 -0
  21. package/dist/browser-session.js.map +1 -0
  22. package/dist/camera-source.d.ts +89 -0
  23. package/dist/camera-source.d.ts.map +1 -0
  24. package/dist/camera-source.js +366 -0
  25. package/dist/camera-source.js.map +1 -0
  26. package/dist/camera-strategy.d.ts +36 -0
  27. package/dist/camera-strategy.d.ts.map +1 -0
  28. package/dist/camera-strategy.js +94 -0
  29. package/dist/camera-strategy.js.map +1 -0
  30. package/dist/decode-upload.d.ts +13 -0
  31. package/dist/decode-upload.d.ts.map +1 -0
  32. package/dist/decode-upload.js +23 -0
  33. package/dist/decode-upload.js.map +1 -0
  34. package/dist/image-loader.d.ts +11 -0
  35. package/dist/image-loader.d.ts.map +1 -0
  36. package/dist/image-loader.js +63 -0
  37. package/dist/image-loader.js.map +1 -0
  38. package/dist/index.d.ts +13 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +13 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/runtime.d.ts +8 -0
  43. package/dist/runtime.d.ts.map +1 -0
  44. package/dist/runtime.js +14 -0
  45. package/dist/runtime.js.map +1 -0
  46. package/dist/scanner/camera-capabilities.d.ts +20 -0
  47. package/dist/scanner/camera-capabilities.d.ts.map +1 -0
  48. package/dist/scanner/camera-capabilities.js +181 -0
  49. package/dist/scanner/camera-capabilities.js.map +1 -0
  50. package/dist/scanner/camera-platform.d.ts +75 -0
  51. package/dist/scanner/camera-platform.d.ts.map +1 -0
  52. package/dist/scanner/camera-platform.js +145 -0
  53. package/dist/scanner/camera-platform.js.map +1 -0
  54. package/dist/scanner/decode-escalation.d.ts +20 -0
  55. package/dist/scanner/decode-escalation.d.ts.map +1 -0
  56. package/dist/scanner/decode-escalation.js +40 -0
  57. package/dist/scanner/decode-escalation.js.map +1 -0
  58. package/dist/scanner/frame-quality.d.ts +24 -0
  59. package/dist/scanner/frame-quality.d.ts.map +1 -0
  60. package/dist/scanner/frame-quality.js +90 -0
  61. package/dist/scanner/frame-quality.js.map +1 -0
  62. package/dist/scanner/frame-scheduler.d.ts +59 -0
  63. package/dist/scanner/frame-scheduler.d.ts.map +1 -0
  64. package/dist/scanner/frame-scheduler.js +148 -0
  65. package/dist/scanner/frame-scheduler.js.map +1 -0
  66. package/dist/scanner/frame-source.d.ts +70 -0
  67. package/dist/scanner/frame-source.d.ts.map +1 -0
  68. package/dist/scanner/frame-source.js +263 -0
  69. package/dist/scanner/frame-source.js.map +1 -0
  70. package/dist/scanner/index.d.ts +12 -0
  71. package/dist/scanner/index.d.ts.map +1 -0
  72. package/dist/scanner/index.js +12 -0
  73. package/dist/scanner/index.js.map +1 -0
  74. package/dist/scanner/repeat-suppressor.d.ts +23 -0
  75. package/dist/scanner/repeat-suppressor.d.ts.map +1 -0
  76. package/dist/scanner/repeat-suppressor.js +69 -0
  77. package/dist/scanner/repeat-suppressor.js.map +1 -0
  78. package/dist/scanner/scanner-session.d.ts +160 -0
  79. package/dist/scanner/scanner-session.d.ts.map +1 -0
  80. package/dist/scanner/scanner-session.js +766 -0
  81. package/dist/scanner/scanner-session.js.map +1 -0
  82. package/dist/scanner/temporal-confirmation.d.ts +30 -0
  83. package/dist/scanner/temporal-confirmation.d.ts.map +1 -0
  84. package/dist/scanner/temporal-confirmation.js +71 -0
  85. package/dist/scanner/temporal-confirmation.js.map +1 -0
  86. package/dist/scanner/temporal-roi.d.ts +27 -0
  87. package/dist/scanner/temporal-roi.d.ts.map +1 -0
  88. package/dist/scanner/temporal-roi.js +50 -0
  89. package/dist/scanner/temporal-roi.js.map +1 -0
  90. package/dist/scanner/types.d.ts +232 -0
  91. package/dist/scanner/types.d.ts.map +1 -0
  92. package/dist/scanner/types.js +2 -0
  93. package/dist/scanner/types.js.map +1 -0
  94. package/dist/tracking/barcode-tracker.d.ts +28 -0
  95. package/dist/tracking/barcode-tracker.d.ts.map +1 -0
  96. package/dist/tracking/barcode-tracker.js +279 -0
  97. package/dist/tracking/barcode-tracker.js.map +1 -0
  98. package/dist/tracking/geometry.d.ts +13 -0
  99. package/dist/tracking/geometry.d.ts.map +1 -0
  100. package/dist/tracking/geometry.js +51 -0
  101. package/dist/tracking/geometry.js.map +1 -0
  102. package/dist/tracking/index.d.ts +8 -0
  103. package/dist/tracking/index.d.ts.map +1 -0
  104. package/dist/tracking/index.js +8 -0
  105. package/dist/tracking/index.js.map +1 -0
  106. package/dist/tracking/scanner-tracking-runtime.d.ts +45 -0
  107. package/dist/tracking/scanner-tracking-runtime.d.ts.map +1 -0
  108. package/dist/tracking/scanner-tracking-runtime.js +93 -0
  109. package/dist/tracking/scanner-tracking-runtime.js.map +1 -0
  110. package/dist/tracking/track-association.d.ts +19 -0
  111. package/dist/tracking/track-association.d.ts.map +1 -0
  112. package/dist/tracking/track-association.js +191 -0
  113. package/dist/tracking/track-association.js.map +1 -0
  114. package/dist/tracking/track-overlay.d.ts +4 -0
  115. package/dist/tracking/track-overlay.d.ts.map +1 -0
  116. package/dist/tracking/track-overlay.js +16 -0
  117. package/dist/tracking/track-overlay.js.map +1 -0
  118. package/dist/tracking/track-roi-set.d.ts +17 -0
  119. package/dist/tracking/track-roi-set.d.ts.map +1 -0
  120. package/dist/tracking/track-roi-set.js +119 -0
  121. package/dist/tracking/track-roi-set.js.map +1 -0
  122. package/dist/tracking/types.d.ts +154 -0
  123. package/dist/tracking/types.d.ts.map +1 -0
  124. package/dist/tracking/types.js +2 -0
  125. package/dist/tracking/types.js.map +1 -0
  126. package/dist/worker/decode-worker.d.ts +2 -0
  127. package/dist/worker/decode-worker.d.ts.map +1 -0
  128. package/dist/worker/decode-worker.js +84 -0
  129. package/dist/worker/decode-worker.js.map +1 -0
  130. package/dist/worker/transferable-buffer.d.ts +18 -0
  131. package/dist/worker/transferable-buffer.d.ts.map +1 -0
  132. package/dist/worker/transferable-buffer.js +24 -0
  133. package/dist/worker/transferable-buffer.js.map +1 -0
  134. package/dist/worker/worker-client.d.ts +86 -0
  135. package/dist/worker/worker-client.d.ts.map +1 -0
  136. package/dist/worker/worker-client.js +286 -0
  137. package/dist/worker/worker-client.js.map +1 -0
  138. package/dist/worker/worker-messages.d.ts +75 -0
  139. package/dist/worker/worker-messages.d.ts.map +1 -0
  140. package/dist/worker/worker-messages.js +244 -0
  141. package/dist/worker/worker-messages.js.map +1 -0
  142. package/package.json +30 -0
@@ -0,0 +1,267 @@
1
+ import { BarcodeTracker } from "../tracking/barcode-tracker.js";
2
+ import { BatchController } from "./batch-controller.js";
3
+ /**
4
+ * ScannerSession + BarcodeTracker + BatchController composition. React and
5
+ * other UI layers only subscribe to the resulting SDK state.
6
+ */
7
+ export class BatchScanSession {
8
+ scanner;
9
+ tracker;
10
+ controller;
11
+ disposeScanner;
12
+ disposeTracker;
13
+ subscriptions;
14
+ pendingScannerFrames = new Map();
15
+ generation = 0;
16
+ stopRequested = false;
17
+ disposed = false;
18
+ constructor(options) {
19
+ this.scanner = options.scanner;
20
+ this.tracker = options.tracker ?? new BarcodeTracker(options.trackerOptions);
21
+ this.controller = new BatchController(options);
22
+ this.disposeScanner = options.disposeScanner ?? true;
23
+ this.disposeTracker = options.disposeTracker ?? true;
24
+ const observationSubscription = typeof this.scanner.onObservations === "function"
25
+ ? this.scanner.onObservations((set) => this.observeScannerObservationSet(set))
26
+ : this.scanner.onDiagnostics((diagnostic) => this.observeScannerDiagnostic(diagnostic));
27
+ this.subscriptions = [observationSubscription, this.scanner.onStateChange((state) => this.observeScannerState(state))];
28
+ }
29
+ async start() {
30
+ this.assertUsable();
31
+ const state = this.scanner.getState();
32
+ if (state === "starting" || state === "scanning" || state === "paused")
33
+ return;
34
+ this.generation += 1;
35
+ this.pendingScannerFrames.clear();
36
+ this.tracker.reset();
37
+ this.controller.reset();
38
+ this.stopRequested = false;
39
+ try {
40
+ await this.scanner.start();
41
+ }
42
+ catch (error) {
43
+ this.controller.fail(error);
44
+ throw error;
45
+ }
46
+ }
47
+ pause() {
48
+ if (this.disposed || this.scanner.getState() !== "scanning")
49
+ return;
50
+ this.invalidatePendingFrames();
51
+ this.scanner.pause();
52
+ }
53
+ resume() {
54
+ if (this.disposed || this.scanner.getState() !== "paused")
55
+ return;
56
+ this.scanner.resume();
57
+ }
58
+ async stop() {
59
+ if (this.disposed)
60
+ return;
61
+ this.stopRequested = true;
62
+ this.invalidatePendingFrames();
63
+ const errors = [];
64
+ try {
65
+ await this.scanner.stop();
66
+ }
67
+ catch (error) {
68
+ errors.push(error);
69
+ this.controller.fail(error);
70
+ }
71
+ finally {
72
+ // A ScannerSession implementation may reject before changing state.
73
+ // Keep the generation closed so late observation callbacks cannot mutate
74
+ // the terminal batch state.
75
+ this.invalidatePendingFrames();
76
+ try {
77
+ this.tracker.reset();
78
+ }
79
+ catch (error) {
80
+ errors.push(error);
81
+ this.controller.fail(error);
82
+ }
83
+ if (errors.length === 0 && this.controller.getState().status === "collecting")
84
+ this.controller.cancel();
85
+ try {
86
+ this.controller.releaseRetainedState();
87
+ }
88
+ catch (error) {
89
+ errors.push(error);
90
+ }
91
+ }
92
+ if (errors.length === 1)
93
+ throw errors[0];
94
+ if (errors.length > 1)
95
+ throw new AggregateError(errors, "BatchScanSession stop failed after attempting runtime cleanup.");
96
+ }
97
+ async dispose() {
98
+ if (this.disposed)
99
+ return;
100
+ // Close callbacks before awaiting fallible delegated lifecycle methods.
101
+ this.disposed = true;
102
+ this.stopRequested = true;
103
+ this.invalidatePendingFrames();
104
+ const errors = [];
105
+ try {
106
+ await this.scanner.stop();
107
+ }
108
+ catch (error) {
109
+ errors.push(error);
110
+ }
111
+ finally {
112
+ if (this.controller.getState().status === "collecting")
113
+ this.controller.cancel();
114
+ for (const unsubscribe of this.subscriptions.splice(0)) {
115
+ try {
116
+ unsubscribe();
117
+ }
118
+ catch (error) {
119
+ errors.push(error);
120
+ }
121
+ }
122
+ if (this.disposeTracker) {
123
+ // Reset first so an injected/fallible dispose implementation cannot
124
+ // strand live identities owned by this composition.
125
+ try {
126
+ this.tracker.reset();
127
+ }
128
+ catch (error) {
129
+ errors.push(error);
130
+ }
131
+ try {
132
+ this.tracker.dispose();
133
+ }
134
+ catch (error) {
135
+ errors.push(error);
136
+ }
137
+ }
138
+ this.pendingScannerFrames.clear();
139
+ try {
140
+ this.controller.clear();
141
+ }
142
+ catch (error) {
143
+ errors.push(error);
144
+ }
145
+ if (this.disposeScanner) {
146
+ try {
147
+ await this.scanner.dispose();
148
+ }
149
+ catch (error) {
150
+ errors.push(error);
151
+ }
152
+ }
153
+ }
154
+ if (errors.length === 1)
155
+ throw errors[0];
156
+ if (errors.length > 1)
157
+ throw new AggregateError(errors, "BatchScanSession disposal failed after completing all cleanup steps.");
158
+ }
159
+ getTracks() {
160
+ return this.tracker.getTracks();
161
+ }
162
+ getBatchState() {
163
+ return this.controller.getState();
164
+ }
165
+ getStatistics() {
166
+ return this.controller.getStatistics();
167
+ }
168
+ onTrack(listener) {
169
+ return this.controller.onTrack(listener);
170
+ }
171
+ onBatchEvent(listener) {
172
+ return this.controller.onEvent(listener);
173
+ }
174
+ /** Explicit frame API used by deterministic runtimes and custom capture adapters. */
175
+ processFrame(observations, frame) {
176
+ this.assertUsable();
177
+ const update = this.tracker.observeFrame(observations, frame);
178
+ this.controller.applyTrackerUpdate(update);
179
+ return update;
180
+ }
181
+ /** Accept an update from an externally-driven instance of the same tracker. */
182
+ applyTrackerUpdate(update) {
183
+ this.assertUsable();
184
+ this.controller.applyTrackerUpdate(update);
185
+ }
186
+ observeScannerDiagnostic(diagnostic) {
187
+ if (this.disposed || this.stopRequested || !diagnostic.event || diagnostic.frameId === undefined)
188
+ return;
189
+ if (diagnostic.event.type !== "detected" && diagnostic.event.type !== "lost")
190
+ return;
191
+ if (this.scanner.getState() !== "starting" && this.scanner.getState() !== "scanning")
192
+ return;
193
+ const frameId = diagnostic.frameId;
194
+ const pending = this.pendingScannerFrames.get(frameId) ?? {
195
+ timestamp: diagnostic.timestamp,
196
+ observations: [],
197
+ generation: this.generation,
198
+ scheduled: false,
199
+ };
200
+ pending.timestamp = Math.max(pending.timestamp, diagnostic.timestamp);
201
+ const event = diagnostic.event;
202
+ if (event.type === "detected" && event.geometry) {
203
+ pending.observations.push({
204
+ payload: event.barcode.text,
205
+ format: event.barcode.format,
206
+ geometry: event.geometry,
207
+ });
208
+ }
209
+ this.pendingScannerFrames.set(frameId, pending);
210
+ if (pending.scheduled)
211
+ return;
212
+ pending.scheduled = true;
213
+ queueMicrotask(() => this.flushScannerFrame(frameId, pending));
214
+ }
215
+ observeScannerObservationSet(set) {
216
+ if (this.disposed || this.stopRequested || (this.scanner.getState() !== "starting" && this.scanner.getState() !== "scanning"))
217
+ return;
218
+ const observations = set.observations.flatMap((observation) => observation.geometry ? [{
219
+ payload: observation.barcode.text,
220
+ format: observation.barcode.format,
221
+ geometry: observation.geometry,
222
+ }] : []);
223
+ try {
224
+ this.processFrame(observations, { frameId: set.frameId, timestamp: set.timestamp });
225
+ }
226
+ catch (error) {
227
+ this.controller.fail(error);
228
+ }
229
+ }
230
+ flushScannerFrame(frameId, pending) {
231
+ if (this.pendingScannerFrames.get(frameId) !== pending)
232
+ return;
233
+ this.pendingScannerFrames.delete(frameId);
234
+ if (this.disposed || this.stopRequested || pending.generation !== this.generation)
235
+ return;
236
+ if (this.scanner.getState() !== "starting" && this.scanner.getState() !== "scanning")
237
+ return;
238
+ try {
239
+ this.processFrame(pending.observations, { frameId, timestamp: pending.timestamp });
240
+ }
241
+ catch (error) {
242
+ this.controller.fail(error);
243
+ }
244
+ }
245
+ observeScannerState(state) {
246
+ if (this.disposed)
247
+ return;
248
+ if (state === "failed") {
249
+ this.invalidatePendingFrames();
250
+ this.controller.fail(new Error("ScannerSession failed while collecting a batch."));
251
+ return;
252
+ }
253
+ if (state === "stopped" && !this.stopRequested && this.controller.getState().status === "collecting") {
254
+ this.invalidatePendingFrames();
255
+ this.controller.cancel();
256
+ }
257
+ }
258
+ invalidatePendingFrames() {
259
+ this.generation += 1;
260
+ this.pendingScannerFrames.clear();
261
+ }
262
+ assertUsable() {
263
+ if (this.disposed)
264
+ throw new Error("BatchScanSession has been disposed.");
265
+ }
266
+ }
267
+ //# sourceMappingURL=batch-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch-session.js","sourceRoot":"","sources":["../../src/batch/batch-session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export * from "./types.js";
2
+ export * from "./batch-controller.js";
3
+ export * from "./batch-session.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/batch/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export * from "./types.js";
2
+ export * from "./batch-controller.js";
3
+ export * from "./batch-session.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/batch/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC"}
@@ -0,0 +1,101 @@
1
+ import type { BarcodeFormat } from "@scanly/core";
2
+ import type { BarcodeTrack } from "../tracking/types.js";
3
+ /** Batch behavior is orthogonal to camera capture and frame scheduling. */
4
+ export type BatchMode = "continuous" | "expected-count" | "checklist" | "unique-physical-instance";
5
+ export type BatchStatus = "collecting" | "complete" | "failed" | "cancelled";
6
+ export interface ExpectedBatchItem {
7
+ payload: string;
8
+ format?: BarcodeFormat;
9
+ /** Defaults to one. Quantities are physical instances, not decoder events. */
10
+ quantity?: number;
11
+ }
12
+ export interface BatchMatchedItem {
13
+ item: ExpectedBatchItem;
14
+ track: BarcodeTrack;
15
+ }
16
+ export interface BatchMissingItem {
17
+ item: ExpectedBatchItem;
18
+ quantity: number;
19
+ }
20
+ export interface BatchState {
21
+ mode: BatchMode;
22
+ status: BatchStatus;
23
+ expectedCount?: number;
24
+ confirmedPhysicalInstanceCount: number;
25
+ matched: readonly BatchMatchedItem[];
26
+ missing: readonly BatchMissingItem[];
27
+ unexpected: readonly BarcodeTrack[];
28
+ duplicate: readonly BarcodeTrack[];
29
+ completedAt?: number;
30
+ failureReason?: string;
31
+ }
32
+ export type TrackEvent = {
33
+ type: "track-added";
34
+ track: BarcodeTrack;
35
+ } | {
36
+ type: "track-updated";
37
+ track: BarcodeTrack;
38
+ } | {
39
+ type: "track-lost";
40
+ trackId: string;
41
+ track: BarcodeTrack;
42
+ } | {
43
+ type: "track-restored";
44
+ track: BarcodeTrack;
45
+ } | {
46
+ type: "track-retired";
47
+ trackId: string;
48
+ track: BarcodeTrack;
49
+ };
50
+ export type BatchEvent = TrackEvent | {
51
+ type: "item-matched";
52
+ item: ExpectedBatchItem;
53
+ track: BarcodeTrack;
54
+ } | {
55
+ type: "unexpected-item";
56
+ track: BarcodeTrack;
57
+ } | {
58
+ type: "duplicate-item";
59
+ item: ExpectedBatchItem;
60
+ track: BarcodeTrack;
61
+ } | {
62
+ type: "batch-completed";
63
+ state: BatchState;
64
+ } | {
65
+ type: "batch-cancelled";
66
+ state: BatchState;
67
+ } | {
68
+ type: "batch-failed";
69
+ state: BatchState;
70
+ error: unknown;
71
+ };
72
+ export interface BatchStatistics {
73
+ confirmedPhysicalInstanceCount: number;
74
+ maxRetainedTracks: number;
75
+ maxRetainedPhysicalInstances: number;
76
+ /** Current bounded controller snapshots, exposed for memory evidence. */
77
+ retainedTrackCount: number;
78
+ retainedPhysicalInstanceCount: number;
79
+ peakRetainedTrackCount: number;
80
+ peakRetainedPhysicalInstanceCount: number;
81
+ peakUnexpectedQuantity: number;
82
+ peakDuplicateQuantity: number;
83
+ retentionRejectedTrackCount: number;
84
+ retentionRejectedPhysicalInstanceCount: number;
85
+ retentionEvictedPhysicalInstanceCount: number;
86
+ matchedQuantity: number;
87
+ missingQuantity: number;
88
+ unexpectedQuantity: number;
89
+ duplicateQuantity: number;
90
+ trackAddedEvents: number;
91
+ trackUpdatedEvents: number;
92
+ trackLostEvents: number;
93
+ trackRestoredEvents: number;
94
+ trackRetiredEvents: number;
95
+ batchCompletedEvents: number;
96
+ completionAccuracy: number;
97
+ }
98
+ export type TrackListener = (event: TrackEvent) => void;
99
+ export type BatchEventListener = (event: BatchEvent) => void;
100
+ export type BatchUnsubscribe = () => void;
101
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/batch/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/batch/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,51 @@
1
+ import { CaptureRouter, type BarcodeFormat, type ConcurrentCallPolicy, type FormatSelection, type RecoveryBudget, type RecoveryProfile, type RecoveryRouteId, type ScanOutcome } from "@scanly/core";
2
+ import { type ScenarioDefinition } from "@scanly/scenario-schema";
3
+ import { type DecodeWorkerFactory, type WorkerScanOptions } from "./worker/worker-client.js";
4
+ export type BrowserCaptureSessionState = "idle" | "initialized" | "running" | "stopped" | "disposed";
5
+ export interface BrowserScanFileOptions extends WorkerScanOptions {
6
+ forceMainThread?: boolean;
7
+ }
8
+ export interface BrowserCaptureSessionOptions {
9
+ scenario?: ScenarioDefinition;
10
+ formats?: FormatSelection | readonly BarcodeFormat[];
11
+ concurrentCallPolicy?: ConcurrentCallPolicy;
12
+ workerFactory?: DecodeWorkerFactory;
13
+ router?: CaptureRouter;
14
+ disposeRouter?: boolean;
15
+ /** Static industrial recovery is explicit; normal upload behavior is unchanged. */
16
+ recovery?: false | BrowserStaticIndustrialRecoveryOptions;
17
+ }
18
+ export interface BrowserStaticIndustrialRecoveryOptions {
19
+ profile?: RecoveryProfile;
20
+ budget?: RecoveryBudget;
21
+ dpmExperimental?: boolean;
22
+ excludedRoutes?: readonly RecoveryRouteId[];
23
+ }
24
+ export declare class BrowserCaptureSession {
25
+ private state;
26
+ private scenario;
27
+ private readonly concurrentPolicy;
28
+ private readonly worker;
29
+ private readonly router;
30
+ private readonly ownsRouter;
31
+ private controller;
32
+ private owner;
33
+ private recovery;
34
+ private readonly recoveryPipeline;
35
+ constructor(options?: BrowserCaptureSessionOptions);
36
+ getState(): BrowserCaptureSessionState;
37
+ initialize(): void;
38
+ start(): void;
39
+ stop(): void;
40
+ cancel(): void;
41
+ updateConfiguration(scenario: ScenarioDefinition): void;
42
+ updateFormats(selection: FormatSelection | readonly BarcodeFormat[]): void;
43
+ scanFile(file: File, options?: BrowserScanFileOptions): Promise<ScanOutcome>;
44
+ updateRecovery(recovery: false | BrowserStaticIndustrialRecoveryOptions): void;
45
+ private workerRecovery;
46
+ private decodeOnMain;
47
+ dispose(): Promise<void>;
48
+ private assertNotDisposed;
49
+ private failure;
50
+ }
51
+ //# sourceMappingURL=browser-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-session.d.ts","sourceRoot":"","sources":["../src/browser-session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAgH,KAAK,aAAa,EAAE,KAAK,oBAAoB,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,KAAK,eAAe,EAAoB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AACrU,OAAO,EAAwC,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAGxG,OAAO,EAAsC,KAAK,mBAAmB,EAAE,KAAK,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAEjI,MAAM,MAAM,0BAA0B,GAAG,MAAM,GAAG,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;AACrG,MAAM,WAAW,sBAAuB,SAAQ,iBAAiB;IAAG,eAAe,CAAC,EAAE,OAAO,CAAA;CAAE;AAC/F,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,aAAa,EAAE,CAAC;IACrD,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mFAAmF;IACnF,QAAQ,CAAC,EAAE,KAAK,GAAG,sCAAsC,CAAC;CAC3D;AAED,MAAM,WAAW,sCAAsC;IACrD,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;CAC7C;AAID,qBAAa,qBAAqB;IAChC,OAAO,CAAC,KAAK,CAAsC;IACnD,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAuB;IACxD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAU;IACrC,OAAO,CAAC,UAAU,CAAgC;IAClD,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,QAAQ,CAAiD;IACjE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAoC;gBAEzD,OAAO,GAAE,4BAAiC;IAatD,QAAQ,IAAI,0BAA0B;IACtC,UAAU,IAAI,IAAI;IAClB,KAAK,IAAI,IAAI;IACb,IAAI,IAAI,IAAI;IACZ,MAAM,IAAI,IAAI;IAEd,mBAAmB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI;IASvD,aAAa,CAAC,SAAS,EAAE,eAAe,GAAG,SAAS,aAAa,EAAE,GAAG,IAAI;IAKpE,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,GAAE,sBAA2B,GAAG,OAAO,CAAC,WAAW,CAAC;IAyEtF,cAAc,CAAC,QAAQ,EAAE,KAAK,GAAG,sCAAsC,GAAG,IAAI;IAM9E,OAAO,CAAC,cAAc;YAYR,YAAY;IASpB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ9B,OAAO,CAAC,iBAAiB;IACzB,OAAO,CAAC,OAAO;CAGhB"}
@@ -0,0 +1,168 @@
1
+ import { CaptureRouter, IndustrialRecoveryPipeline, createRecoveryProbeScenario, createRgbaFrame, normalizeFormatSelection, sdkError } from "@scanly/core";
2
+ import { getBuiltinScenario, validateScenario } from "@scanly/scenario-schema";
3
+ import { loadPixelBufferFromFile } from "./image-loader.js";
4
+ import { createBrowserCaptureRouter } from "./runtime.js";
5
+ import { DecodeWorkerClient, markDecodePath } from "./worker/worker-client.js";
6
+ let browserFrameSequence = 0;
7
+ export class BrowserCaptureSession {
8
+ state = "idle";
9
+ scenario;
10
+ concurrentPolicy;
11
+ worker;
12
+ router;
13
+ ownsRouter;
14
+ controller = null;
15
+ owner = 0;
16
+ recovery;
17
+ recoveryPipeline = new IndustrialRecoveryPipeline();
18
+ constructor(options = {}) {
19
+ const initial = options.scenario ?? getBuiltinScenario("balanced");
20
+ const configured = options.formats ? { ...initial, acceptedFormats: [...normalizeFormatSelection(options.formats).formats] } : initial;
21
+ const validation = validateScenario(configured);
22
+ if (!validation.ok)
23
+ throw Object.assign(new Error(validation.message), { code: "malformed_scenario", issues: validation.issues });
24
+ this.scenario = validation.value;
25
+ this.concurrentPolicy = options.concurrentCallPolicy ?? "replace";
26
+ this.worker = new DecodeWorkerClient(options.workerFactory);
27
+ this.router = options.router ?? createBrowserCaptureRouter({ scenario: this.scenario });
28
+ this.ownsRouter = options.disposeRouter ?? !options.router;
29
+ this.recovery = options.recovery ?? false;
30
+ }
31
+ getState() { return this.state; }
32
+ initialize() { this.assertNotDisposed(); if (this.state === "idle" || this.state === "stopped")
33
+ this.state = "initialized"; }
34
+ start() { this.assertNotDisposed(); if (this.state === "idle")
35
+ this.initialize(); this.state = "running"; }
36
+ stop() { if (this.state === "disposed")
37
+ return; this.cancel(); this.state = "stopped"; }
38
+ cancel() { this.owner += 1; this.controller?.abort(); this.controller = null; this.worker.cancel(); }
39
+ updateConfiguration(scenario) {
40
+ this.assertNotDisposed();
41
+ const validation = validateScenario(scenario);
42
+ if (!validation.ok)
43
+ throw Object.assign(new Error(validation.message), { code: "malformed_scenario", issues: validation.issues });
44
+ this.cancel();
45
+ this.router.updateScenario(validation.value);
46
+ this.scenario = validation.value;
47
+ }
48
+ updateFormats(selection) {
49
+ const formats = normalizeFormatSelection(selection).formats;
50
+ this.updateConfiguration({ ...this.scenario, acceptedFormats: [...formats] });
51
+ }
52
+ async scanFile(file, options = {}) {
53
+ const frameId = `browser-frame-${Date.now()}-${++browserFrameSequence}`;
54
+ if (this.state === "disposed")
55
+ return this.failure(frameId, "session_disposed", "Browser capture session has been disposed.");
56
+ if (this.state !== "running")
57
+ return this.failure(frameId, "session_not_running", "Browser capture session must be started before scanFile().");
58
+ if (this.controller && this.concurrentPolicy === "reject")
59
+ return this.failure(frameId, "concurrent_call_rejected", "This session allows one active scan.");
60
+ if (this.controller)
61
+ this.cancel();
62
+ const owner = ++this.owner;
63
+ const controller = new AbortController();
64
+ this.controller = controller;
65
+ const onAbort = () => controller.abort();
66
+ options.signal?.addEventListener("abort", onAbort, { once: true });
67
+ try {
68
+ options.onStage?.("Loading image...");
69
+ const pixels = await loadPixelBufferFromFile(file);
70
+ if (controller.signal.aborted)
71
+ return this.failure(frameId, "cancelled", "Decode cancelled.");
72
+ const workerPath = !options.forceMainThread && typeof Worker !== "undefined";
73
+ const frame = createRgbaFrame(pixels.data, pixels.width, pixels.height, { id: frameId, sourceType: "upload", ownership: workerPath ? "transferred" : "owned" });
74
+ let outcome;
75
+ if (workerPath) {
76
+ const decodeStartedAt = Date.now();
77
+ const scenario = this.scenario;
78
+ markDecodePath("worker");
79
+ outcome = await this.worker.scan(frame, scenario, { signal: controller.signal, preserveSourceForFallback: true, onStage: options.onStage, onProgress: options.onProgress, ...(this.workerRecovery() ? { recovery: this.workerRecovery() } : {}) });
80
+ if (!outcome.ok && ["worker_initialization_failure", "engine_execution_failure"].includes(outcome.error.code) && !controller.signal.aborted && owner === this.owner) {
81
+ const workerOutcome = outcome;
82
+ const workerElapsedMs = Math.max(Date.now() - decodeStartedAt, workerOutcome.timing.totalMs);
83
+ const remainingExecutionMs = Math.floor(scenario.budgets.maxExecutionMs - workerElapsedMs);
84
+ const remainingAttempts = scenario.budgets.maxAttempts - workerOutcome.attemptCount;
85
+ if (remainingExecutionMs > 0 && remainingAttempts > 0) {
86
+ markDecodePath("main-thread");
87
+ options.onStage?.("Worker unavailable; retrying on main thread within the remaining scan budget...");
88
+ const fallbackScenario = {
89
+ ...scenario,
90
+ multiCode: { ...scenario.multiCode, maxResults: Math.min(scenario.multiCode.maxResults, remainingAttempts) },
91
+ budgets: { ...scenario.budgets, maxAttempts: remainingAttempts, maxExecutionMs: remainingExecutionMs },
92
+ };
93
+ const fallback = await this.decodeOnMain(createRgbaFrame(pixels.data, pixels.width, pixels.height, { id: frameId, sourceType: "upload", ownership: "owned" }), fallbackScenario, controller.signal);
94
+ const totalMs = Math.max(Date.now() - decodeStartedAt, workerElapsedMs + fallback.timing.totalMs);
95
+ outcome = {
96
+ ...fallback,
97
+ attemptCount: workerOutcome.attemptCount + fallback.attemptCount,
98
+ timing: {
99
+ ...fallback.timing,
100
+ totalMs,
101
+ ...(workerOutcome.timing.workerSetupMs === undefined ? {} : { workerSetupMs: workerOutcome.timing.workerSetupMs }),
102
+ ...(workerOutcome.timing.workerTransferMs === undefined ? {} : { workerTransferMs: workerOutcome.timing.workerTransferMs }),
103
+ },
104
+ };
105
+ options.onProgress?.({ attemptCount: outcome.attemptCount });
106
+ }
107
+ }
108
+ }
109
+ else {
110
+ markDecodePath("main-thread");
111
+ options.onStage?.("Routing normalized frame...");
112
+ outcome = await this.decodeOnMain(frame, this.scenario, controller.signal);
113
+ options.onProgress?.({ attemptCount: outcome.attemptCount });
114
+ }
115
+ if (owner !== this.owner)
116
+ return this.failure(frameId, "cancelled", "Result belongs to a superseded browser job.");
117
+ return outcome;
118
+ }
119
+ catch (error) {
120
+ const code = error && typeof error === "object" && "code" in error ? String(error.code) : "unsupported_image";
121
+ const mapped = code === "image_too_large" ? "resource_limit_exceeded" : code === "invalid_file" || code === "unsupported_image" || code === "empty_image" || code === "invalid_image" ? "invalid_image" : controller.signal.aborted ? "cancelled" : "engine_execution_failure";
122
+ return this.failure(frameId, mapped, error instanceof Error ? error.message : String(error));
123
+ }
124
+ finally {
125
+ options.signal?.removeEventListener("abort", onAbort);
126
+ if (this.controller === controller)
127
+ this.controller = null;
128
+ }
129
+ }
130
+ updateRecovery(recovery) {
131
+ this.assertNotDisposed();
132
+ this.cancel();
133
+ this.recovery = recovery;
134
+ }
135
+ workerRecovery() {
136
+ if (this.recovery === false)
137
+ return undefined;
138
+ const profile = this.recovery.profile ?? "industrial";
139
+ return {
140
+ profile,
141
+ sourceMode: "static",
142
+ ...(this.recovery.budget ? { budget: this.recovery.budget } : {}),
143
+ dpmExperimental: this.recovery.dpmExperimental === true || profile === "dpm-experimental",
144
+ ...(this.recovery.excludedRoutes ? { excludedRoutes: [...this.recovery.excludedRoutes] } : {}),
145
+ };
146
+ }
147
+ async decodeOnMain(frame, scenario, signal) {
148
+ const recovery = this.workerRecovery();
149
+ if (!recovery)
150
+ return this.router.scan(frame, { signal, scenario });
151
+ return (await this.recoveryPipeline.run(frame, (candidate, request) => this.router.scan({ ...candidate, ownership: "borrowed", dispose: undefined }, { signal: request.signal, scenario: request.routeId === "general" ? scenario : createRecoveryProbeScenario(scenario, request.routeId) }), { profile: recovery.profile, sourceMode: "static", ...(recovery.budget ? { budget: recovery.budget } : {}), signal, dpmExperimental: recovery.dpmExperimental, excludedRoutes: recovery.excludedRoutes })).outcome;
152
+ }
153
+ async dispose() {
154
+ if (this.state === "disposed")
155
+ return;
156
+ this.cancel();
157
+ this.worker.dispose();
158
+ this.state = "disposed";
159
+ if (this.ownsRouter)
160
+ await this.router.dispose();
161
+ }
162
+ assertNotDisposed() { if (this.state === "disposed")
163
+ throw Object.assign(new Error("Browser capture session has been disposed."), { code: "session_disposed" }); }
164
+ failure(frameId, code, message) {
165
+ return { ok: false, error: sdkError(code, message), frameId, scenarioId: this.scenario.id, attemptCount: 0, timing: { totalMs: 0 } };
166
+ }
167
+ }
168
+ //# sourceMappingURL=browser-session.js.map