@saeris/hanko 0.0.0 → 0.2.1

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 (57) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/LICENSE.md +21 -0
  3. package/README.md +346 -0
  4. package/dist/approve/index.d.mts +318 -0
  5. package/dist/approve/index.d.mts.map +1 -0
  6. package/dist/approve/index.mjs +393 -0
  7. package/dist/approve/index.mjs.map +1 -0
  8. package/dist/client/index.d.mts +101 -0
  9. package/dist/client/index.d.mts.map +1 -0
  10. package/dist/client/index.mjs +215 -0
  11. package/dist/client/index.mjs.map +1 -0
  12. package/dist/codes-Ba_qYH6u.mjs +93 -0
  13. package/dist/codes-Ba_qYH6u.mjs.map +1 -0
  14. package/dist/handlers.d.mts +113 -0
  15. package/dist/handlers.d.mts.map +1 -0
  16. package/dist/handlers.mjs +194 -0
  17. package/dist/handlers.mjs.map +1 -0
  18. package/dist/index.d.mts +5 -0
  19. package/dist/index.mjs +345 -0
  20. package/dist/index.mjs.map +1 -0
  21. package/dist/linking-DcQSMgem.mjs +177 -0
  22. package/dist/linking-DcQSMgem.mjs.map +1 -0
  23. package/dist/linking-nKoayyHf.d.mts +133 -0
  24. package/dist/linking-nKoayyHf.d.mts.map +1 -0
  25. package/dist/machine-CRHKjtoP.d.mts +223 -0
  26. package/dist/machine-CRHKjtoP.d.mts.map +1 -0
  27. package/dist/machine-D_5DAFxi.mjs +155 -0
  28. package/dist/machine-D_5DAFxi.mjs.map +1 -0
  29. package/dist/qr.d.mts +58 -0
  30. package/dist/qr.d.mts.map +1 -0
  31. package/dist/qr.mjs +27 -0
  32. package/dist/qr.mjs.map +1 -0
  33. package/dist/scan/index.d.mts +384 -0
  34. package/dist/scan/index.d.mts.map +1 -0
  35. package/dist/scan/index.mjs +409 -0
  36. package/dist/scan/index.mjs.map +1 -0
  37. package/dist/scan/worker.d.mts +2 -0
  38. package/dist/scan/worker.mjs +2 -0
  39. package/dist/server-BhoYRkCm.d.mts +257 -0
  40. package/dist/server-BhoYRkCm.d.mts.map +1 -0
  41. package/dist/stores/kv.d.mts +64 -0
  42. package/dist/stores/kv.d.mts.map +1 -0
  43. package/dist/stores/kv.mjs +87 -0
  44. package/dist/stores/kv.mjs.map +1 -0
  45. package/dist/stores/memory.d.mts +22 -0
  46. package/dist/stores/memory.d.mts.map +1 -0
  47. package/dist/stores/memory.mjs +42 -0
  48. package/dist/stores/memory.mjs.map +1 -0
  49. package/dist/types-BvBIFPH6.mjs +7 -0
  50. package/dist/types-BvBIFPH6.mjs.map +1 -0
  51. package/dist/types-C82lb-zX.d.mts +82 -0
  52. package/dist/types-C82lb-zX.d.mts.map +1 -0
  53. package/dist/worker-BdwaK1uX.mjs +5291 -0
  54. package/dist/worker-BdwaK1uX.mjs.map +1 -0
  55. package/dist/worker-DxbdBA2z.d.mts +164 -0
  56. package/dist/worker-DxbdBA2z.d.mts.map +1 -0
  57. package/package.json +116 -3
@@ -0,0 +1,164 @@
1
+ //#region src/scan/types.d.ts
2
+ /**
3
+ * The vocabulary every symbology decoder shares.
4
+ *
5
+ * Kept separate from any one decoder so that adding DataMatrix or Aztec later
6
+ * means writing a `SymbolDecoder`, not restructuring the session around it.
7
+ */
8
+ /**
9
+ * A greyscale image.
10
+ *
11
+ * Greyscale rather than RGBA because every stage after acquisition works on
12
+ * luminance: binarization, finder detection, and sampling all discard colour
13
+ * immediately. Converting once at the boundary means the pipeline never
14
+ * carries three redundant channels through its hot loops.
15
+ */
16
+ interface GrayImage {
17
+ /** One byte per pixel, row-major, `width * height` long. */
18
+ readonly data: Uint8ClampedArray;
19
+ readonly width: number;
20
+ readonly height: number;
21
+ }
22
+ /**
23
+ * A bit matrix — the output of binarization and the input to every decoder.
24
+ *
25
+ * `true` is a *set* module, which by QR convention means DARK. Polarity is
26
+ * resolved during binarization, not here, so a decoder never has to ask which
27
+ * way round its input is. That question is exactly what broke the approval
28
+ * screen against an inverted symbol.
29
+ */
30
+ interface BitMatrix {
31
+ /** Row-major bits, `width * height` long. */
32
+ readonly bits: Uint8Array;
33
+ readonly width: number;
34
+ readonly height: number;
35
+ }
36
+ /** A point in image space. Sub-pixel, because finder centres rarely land on one. */
37
+ interface Point {
38
+ readonly x: number;
39
+ readonly y: number;
40
+ }
41
+ /** What a successful decode yields. */
42
+ interface DecodedSymbol {
43
+ /** The decoded payload. */
44
+ readonly value: string;
45
+ /**
46
+ * The symbology that produced it, as a BarcodeDetector-compatible name.
47
+ *
48
+ * Shaped to match that API without depending on it: if it ever ships, a
49
+ * compatibility layer is a rename, not a redesign.
50
+ */
51
+ readonly format: `qr_code`;
52
+ /** Corners in image space, clockwise from top-left. */
53
+ readonly cornerPoints: readonly Point[];
54
+ }
55
+ /**
56
+ * What the session needs from a decoder.
57
+ *
58
+ * One method, deliberately. A decoder that also owned frame acquisition or
59
+ * camera lifecycle could not be tested without a browser — and the whole point
60
+ * of this seam is that everything below it is pure.
61
+ *
62
+ * Returns `null` rather than throwing when no symbol is present: a camera
63
+ * pointed at a room sees frames without codes constantly, and that is a
64
+ * non-event, not an error. Throwing is reserved for a genuine fault.
65
+ */
66
+ interface SymbolDecoder {
67
+ decode(image: GrayImage): DecodedSymbol | null;
68
+ }
69
+ //#endregion
70
+ //#region src/scan/qr/decoder.d.ts
71
+ /** Options for {@link createQrDecoder}. */
72
+ interface QrDecoderOptions {
73
+ /**
74
+ * Which polarities to attempt.
75
+ *
76
+ * `both` by default. `dark-on-light` is the conventional rendering and is
77
+ * marginally faster; choose it only when you control every symbol the
78
+ * scanner will ever see.
79
+ */
80
+ polarity?: `both` | `dark-on-light` | `light-on-dark`;
81
+ /**
82
+ * Milliseconds to spend before giving up, or `0` for no limit.
83
+ *
84
+ * The retry ladder is what reads difficult images, and it is priced for a
85
+ * still photograph rather than a viewfinder. Measured on 1024x768 frames, a
86
+ * symbol that decodes costs 12 to 32ms while one that cannot be read at all
87
+ * costs 570 to 700 — roughly 25 times more, and the early exit cannot help
88
+ * because a finder pattern genuinely is present.
89
+ *
90
+ * That asymmetry is backwards for a camera: the expensive case happens
91
+ * exactly when someone is holding the phone at a bad angle and moving it, so
92
+ * a scanner that reads 43% of stills would stutter at under two frames a
93
+ * second precisely when the user is trying to line up a shot. The next frame
94
+ * is nearly free and probably better, so spending 700ms on this one is a bad
95
+ * trade in a live loop and a good one for a single still.
96
+ *
97
+ * Defaults to 120ms, which fits comfortably inside a frame at 5 scans per
98
+ * second. Set `0` when decoding a still image, where the whole ladder is
99
+ * worth running.
100
+ */
101
+ timeBudgetMs?: number;
102
+ /**
103
+ * Retry with a low-pass filter when the sharp pass finds nothing.
104
+ *
105
+ * On by default, and it is what makes a code on a SCREEN readable: moire
106
+ * banding is frequency aliasing between the camera sensor and the display's
107
+ * sub-pixel grid, so it sits at a higher spatial frequency than the modules
108
+ * and blurs away while they survive. Measured on the benchmark corpus this
109
+ * takes the `monitor` category from 0 of 25 to 10 of 25 — and since this
110
+ * library's own device screen is scanned off a TV, that is not a niche case.
111
+ *
112
+ * A retry rather than a default pass, because the same filter destroys
113
+ * already-blurry photographs: `blurred` drops from 5 of 14 to 1, `nominal`
114
+ * from 3 to 0. Costs nothing on images that decode sharp.
115
+ */
116
+ retryBlurred?: boolean;
117
+ }
118
+ /**
119
+ * Create a QR decoder.
120
+ *
121
+ * The returned decoder is pure and synchronous: it holds no camera, no canvas,
122
+ * and no DOM reference, which is what lets the same instance run on a worker,
123
+ * a server, or in a test with no browser at all.
124
+ */
125
+ declare const createQrDecoder: ({ polarity, retryBlurred, timeBudgetMs }?: QrDecoderOptions) => SymbolDecoder;
126
+ //#endregion
127
+ //#region src/scan/worker.d.ts
128
+ /** A frame handed to the worker. */
129
+ interface DecodeRequest {
130
+ /** Correlates the reply, since frames may be in flight concurrently. */
131
+ readonly id: number;
132
+ readonly data: Uint8ClampedArray;
133
+ readonly width: number;
134
+ readonly height: number;
135
+ }
136
+ /** The worker's reply. */
137
+ interface DecodeResponse {
138
+ readonly id: number;
139
+ readonly result: DecodedSymbol | null;
140
+ /** Wall-clock time the decode took, for callers pacing their own requests. */
141
+ readonly elapsedMs: number;
142
+ }
143
+ /**
144
+ * Wire a worker scope to a decoder.
145
+ *
146
+ * Call from a worker entry module:
147
+ *
148
+ * ```ts
149
+ * import { serveDecoder } from "@saeris/hanko/scan/worker";
150
+ * serveDecoder(self);
151
+ * ```
152
+ *
153
+ * The scope is passed in rather than reached for, so this stays testable
154
+ * without a worker and works under any host that provides the same two
155
+ * methods — a DOM `Worker`, a Node `parentPort`, or a stub.
156
+ */
157
+ declare const serveDecoder: (scope: {
158
+ addEventListener?: (type: string, listener: (event: unknown) => void) => void;
159
+ on?: (type: string, listener: (message: unknown) => void) => void;
160
+ postMessage: (message: unknown) => void;
161
+ }, options?: QrDecoderOptions) => void;
162
+ //#endregion
163
+ export { createQrDecoder as a, GrayImage as c, QrDecoderOptions as i, Point as l, DecodeResponse as n, BitMatrix as o, serveDecoder as r, DecodedSymbol as s, DecodeRequest as t, SymbolDecoder as u };
164
+ //# sourceMappingURL=worker-DxbdBA2z.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-DxbdBA2z.d.mts","names":[],"sources":["../src/scan/types.ts","../src/scan/qr/decoder.ts","../src/scan/worker.ts"],"mappings":";;;;;;;;;;;;;;;UAeiB;;WAEN,MAAM;WACN;WACA;;;;;;;;;;UAWM;;WAEN,MAAM;WACN;WACA;;;UAIM;WACN;WACA;;;UAIM;;WAEN;;;;;;;WAOA;;WAEA,uBAAuB;;;;;;;;;;;;;UAcjB;EACf,OAAO,OAAO,YAAY;;;;;UCRX;;;;;;;;EAQf;;;;;;;;;;;;;;;;;;;;;EAsBA;;;;;;;;;;;;;;;EAgBA;;;;;;;;;cA2eW,oBAAmB,UAAA,cAAA,iBAI7B,qBAAwB;;;;UC/jBV;;WAEN;WACA,MAAM;WACN;WACA;;;UAIM;WACN;WACA,QAAQ;;WAER;;;;;;;;;;;;;;;;cAiBE,eACX;EACE,oBACE,cACA,WAAW;EAEb,MAAM,cAAc,WAAW;EAC/B,cAAc;GAEhB,UAAS"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saeris/hanko",
3
- "version": "0.0.0",
3
+ "version": "0.2.1",
4
4
  "description": "QR-assisted device sign-in (OAuth 2.0 Device Authorization Grant, RFC 8628) for TVs, kiosks, and anything without a keyboard",
5
5
  "keywords": [
6
6
  "oauth",
@@ -14,11 +14,124 @@
14
14
  "tv",
15
15
  "kiosk"
16
16
  ],
17
+ "homepage": "https://github.com/Saeris/hanko#readme",
18
+ "bugs": "https://github.com/Saeris/hanko/issues",
17
19
  "license": "MIT",
18
20
  "author": "Drake Costa <drake@saeris.gg> (https://saeris.gg)",
19
- "homepage": "https://github.com/Saeris/hanko#readme",
20
21
  "repository": {
21
22
  "type": "git",
22
23
  "url": "https://github.com/Saeris/hanko.git"
24
+ },
25
+ "workspaces": [
26
+ ".",
27
+ "examples/*"
28
+ ],
29
+ "files": [
30
+ "dist/**/*"
31
+ ],
32
+ "type": "module",
33
+ "sideEffects": false,
34
+ "exports": {
35
+ ".": {
36
+ "import": {
37
+ "development": "./src/index.ts",
38
+ "types": "./dist/index.d.mts",
39
+ "default": "./dist/index.mjs"
40
+ }
41
+ },
42
+ "./qr": {
43
+ "import": {
44
+ "development": "./src/qr.ts",
45
+ "types": "./dist/qr.d.mts",
46
+ "default": "./dist/qr.mjs"
47
+ }
48
+ },
49
+ "./client": {
50
+ "import": {
51
+ "development": "./src/client/index.ts",
52
+ "types": "./dist/client/index.d.mts",
53
+ "default": "./dist/client/index.mjs"
54
+ }
55
+ },
56
+ "./approve": {
57
+ "import": {
58
+ "development": "./src/approve/index.ts",
59
+ "types": "./dist/approve/index.d.mts",
60
+ "default": "./dist/approve/index.mjs"
61
+ }
62
+ },
63
+ "./scan": {
64
+ "import": {
65
+ "development": "./src/scan/index.ts",
66
+ "types": "./dist/scan/index.d.mts",
67
+ "default": "./dist/scan/index.mjs"
68
+ }
69
+ },
70
+ "./scan/worker": {
71
+ "import": {
72
+ "development": "./src/scan/worker.ts",
73
+ "types": "./dist/scan/worker.d.mts",
74
+ "default": "./dist/scan/worker.mjs"
75
+ }
76
+ },
77
+ "./handlers": {
78
+ "import": {
79
+ "development": "./src/handlers.ts",
80
+ "types": "./dist/handlers.d.mts",
81
+ "default": "./dist/handlers.mjs"
82
+ }
83
+ },
84
+ "./stores/memory": {
85
+ "import": {
86
+ "development": "./src/stores/memory.ts",
87
+ "types": "./dist/stores/memory.d.mts",
88
+ "default": "./dist/stores/memory.mjs"
89
+ }
90
+ },
91
+ "./stores/kv": {
92
+ "import": {
93
+ "development": "./src/stores/kv.ts",
94
+ "types": "./dist/stores/kv.d.mts",
95
+ "default": "./dist/stores/kv.mjs"
96
+ }
97
+ },
98
+ "./package.json": "./package.json"
99
+ },
100
+ "publishConfig": {
101
+ "access": "public",
102
+ "registry": "https://registry.npmjs.org"
103
+ },
104
+ "scripts": {
105
+ "bench": "node bench/decode.mjs",
106
+ "bench:corpus": "node bench/corpus.mjs",
107
+ "build": "vp pack",
108
+ "check": "vp check",
109
+ "ci": "vp pack && vp check && vp test",
110
+ "corpus": "node scripts/fetch-corpus.mjs",
111
+ "demo": "vp pack && yarn workspace @saeris/hanko-example-astro run dev",
112
+ "demo:share": "vp pack && yarn workspace @saeris/hanko-example-astro run share",
113
+ "format": "vp check --fix",
114
+ "test": "vp test --fileParallelism"
115
+ },
116
+ "devDependencies": {
117
+ "@saeris/configs": "1.6.1",
118
+ "@types/node": "^25.9.1",
119
+ "@varlock/bumpy": "^1.7.0",
120
+ "etiket": "^0.12.0",
121
+ "typescript": "^6.0.3",
122
+ "vite-plus": "0.2.9",
123
+ "vitest": "4.1.10"
124
+ },
125
+ "engines": {
126
+ "node": ">=22"
127
+ },
128
+ "packageManager": "yarn@4.18.0",
129
+ "peerDependencies": {
130
+ "etiket": "^0.12.0"
131
+ },
132
+ "peerDependenciesMeta": {
133
+ "etiket": {
134
+ "optional": true
135
+ }
23
136
  }
24
- }
137
+ }