@calmlens/sdk 0.15.0 → 0.16.2

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 (38) hide show
  1. package/README.md +30 -10
  2. package/cjs/{CalmLensConnection-DoemramF.js → CalmLensConnection-5Hj3trGr.js} +2 -2
  3. package/cjs/{CalmLensUtils-Cv1Ylp9-.js → CalmLensUtils-BeIl1N6N.js} +7 -7
  4. package/cjs/{CalmLensVersion-CuxKdijk.js → CalmLensVersion-BGpB9t4-.js} +6 -1
  5. package/cjs/{CalmLensWebhooks-qrL-Zdu3.js → CalmLensWebhooks-CogUYk8-.js} +7 -7
  6. package/cjs/Classification-BNhgwGEm-DmA9VEuE.d.ts +4806 -0
  7. package/cjs/TestTriggers-CRKtulyp-vrzEq2wm.d.ts +44 -0
  8. package/cjs/{WebhookPayload-vy8HNMaK.d.ts → WebhookPayload-CJoIkGBD.d.ts} +1019 -5100
  9. package/cjs/app.d.ts +4 -3
  10. package/cjs/app.js +3 -3
  11. package/cjs/{index-Dz340UQ9.d.ts → index-C9Xw1aid.d.ts} +139 -141
  12. package/cjs/index.d.ts +4 -3
  13. package/cjs/index.js +1 -1
  14. package/cjs/server.d.ts +8 -7
  15. package/cjs/server.js +17 -22
  16. package/cjs/test-inputs.d.ts +2 -0
  17. package/cjs/test-inputs.js +265 -0
  18. package/cjs/testing.d.ts +5 -5
  19. package/cjs/testing.js +7943 -101
  20. package/esm/{CalmLensConnection-CQLoUoBd.js → CalmLensConnection-BAJdoZHy.js} +2 -2
  21. package/esm/{CalmLensUtils-B3S--hBG.js → CalmLensUtils-DqniTKJ8.js} +7 -7
  22. package/esm/{CalmLensVersion-N71PMJAz.js → CalmLensVersion-l_khh4nS.js} +6 -1
  23. package/esm/{CalmLensWebhooks-BxMSx1Vm.js → CalmLensWebhooks-BtrKKLvg.js} +7 -7
  24. package/esm/Classification-BNhgwGEm-DmA9VEuE.d.ts +4806 -0
  25. package/esm/TestTriggers-CRKtulyp-vrzEq2wm.d.ts +44 -0
  26. package/esm/{WebhookPayload-vy8HNMaK.d.ts → WebhookPayload-CJoIkGBD.d.ts} +1019 -5100
  27. package/esm/app.d.ts +4 -3
  28. package/esm/app.js +3 -3
  29. package/esm/{index-Dz340UQ9.d.ts → index-C9Xw1aid.d.ts} +139 -141
  30. package/esm/index.d.ts +4 -3
  31. package/esm/index.js +1 -1
  32. package/esm/server.d.ts +8 -7
  33. package/esm/server.js +17 -22
  34. package/esm/test-inputs.d.ts +2 -0
  35. package/esm/test-inputs.js +256 -0
  36. package/esm/testing.d.ts +5 -5
  37. package/esm/testing.js +7922 -80
  38. package/package.json +19 -7
@@ -0,0 +1,256 @@
1
+ //#region src/contract/TestTriggers-7yylawYV.js
2
+ const TEST_IMAGE_PALETTES = {
3
+ sexual: [
4
+ [
5
+ 255,
6
+ 20,
7
+ 147,
8
+ 255
9
+ ],
10
+ [
11
+ 255,
12
+ 105,
13
+ 180,
14
+ 255
15
+ ],
16
+ [
17
+ 199,
18
+ 21,
19
+ 133,
20
+ 255
21
+ ],
22
+ [
23
+ 255,
24
+ 69,
25
+ 0,
26
+ 255
27
+ ]
28
+ ],
29
+ violence: [
30
+ [
31
+ 139,
32
+ 0,
33
+ 0,
34
+ 255
35
+ ],
36
+ [
37
+ 220,
38
+ 20,
39
+ 60,
40
+ 255
41
+ ],
42
+ [
43
+ 178,
44
+ 34,
45
+ 34,
46
+ 255
47
+ ],
48
+ [
49
+ 255,
50
+ 0,
51
+ 0,
52
+ 255
53
+ ]
54
+ ],
55
+ harassment: [
56
+ [
57
+ 180,
58
+ 83,
59
+ 9,
60
+ 255
61
+ ],
62
+ [
63
+ 217,
64
+ 119,
65
+ 6,
66
+ 255
67
+ ],
68
+ [
69
+ 245,
70
+ 158,
71
+ 11,
72
+ 255
73
+ ],
74
+ [
75
+ 251,
76
+ 191,
77
+ 36,
78
+ 255
79
+ ]
80
+ ],
81
+ hate: [
82
+ [
83
+ 30,
84
+ 64,
85
+ 175,
86
+ 255
87
+ ],
88
+ [
89
+ 37,
90
+ 99,
91
+ 235,
92
+ 255
93
+ ],
94
+ [
95
+ 59,
96
+ 130,
97
+ 246,
98
+ 255
99
+ ],
100
+ [
101
+ 96,
102
+ 165,
103
+ 250,
104
+ 255
105
+ ]
106
+ ],
107
+ "criminal-activity": [
108
+ [
109
+ 6,
110
+ 95,
111
+ 70,
112
+ 255
113
+ ],
114
+ [
115
+ 5,
116
+ 150,
117
+ 105,
118
+ 255
119
+ ],
120
+ [
121
+ 16,
122
+ 185,
123
+ 129,
124
+ 255
125
+ ],
126
+ [
127
+ 52,
128
+ 211,
129
+ 153,
130
+ 255
131
+ ]
132
+ ],
133
+ "self-harm": [
134
+ [
135
+ 75,
136
+ 0,
137
+ 130,
138
+ 255
139
+ ],
140
+ [
141
+ 138,
142
+ 43,
143
+ 226,
144
+ 255
145
+ ],
146
+ [
147
+ 72,
148
+ 61,
149
+ 139,
150
+ 255
151
+ ],
152
+ [
153
+ 147,
154
+ 112,
155
+ 219,
156
+ 255
157
+ ]
158
+ ]
159
+ };
160
+ /** A project's test input key: eight hexadecimal characters only its members can see. */
161
+ const TEST_INPUT_KEY_PATTERN = /^[a-f0-9]{8}$/;
162
+ const TEXT_CODE_SIGNALS = [
163
+ "harassment",
164
+ "hate",
165
+ "criminal-activity",
166
+ "sexual.involving-minors"
167
+ ];
168
+ /**
169
+ * The text code that CalmLens classifies as `category` for the project with this test input key.
170
+ * @returns The code, or an empty string for a category without a text code
171
+ */
172
+ const generateTestTextCode = (category, testInputKey) => TEXT_CODE_SIGNALS.includes(category) ? `<!calmlens-test ${testInputKey} ${category}>` : "";
173
+ const TEST_TRIGGER_PATTERN = /<!calmlens-test\s+([a-f0-9]{8})\s+(harassment|hate|criminal-activity|sexual\.involving-minors)\s*>/gi;
174
+ /**
175
+ * The test signals in `text` whose codes carry the project's test input key. Codes with another
176
+ * key are ignored, so a code can only be used by the project it belongs to.
177
+ */
178
+ const detectTestTrigger = (text, testInputKey) => {
179
+ const signals = [];
180
+ for (const [, key, signal] of text.matchAll(TEST_TRIGGER_PATTERN)) {
181
+ if (key.toLowerCase() !== testInputKey.toLowerCase()) continue;
182
+ const testSignal = signal.toLowerCase();
183
+ if (!signals.includes(testSignal)) signals.push(testSignal);
184
+ }
185
+ return {
186
+ isTestTrigger: signals.length > 0,
187
+ signals
188
+ };
189
+ };
190
+ /** Sixteen cells encode two bits each using the category's four existing colours. */
191
+ const getTestImageKeyCells = (width, height) => {
192
+ return Array.from({ length: 16 }, (_, index) => {
193
+ const column = index % 4;
194
+ const row = Math.floor(index / 4);
195
+ const x = Math.floor(column * width / 4);
196
+ const y = Math.floor(row * height / 4);
197
+ return {
198
+ x,
199
+ y,
200
+ width: Math.floor((column + 1) * width / 4) - x,
201
+ height: Math.floor((row + 1) * height / 4) - y
202
+ };
203
+ });
204
+ };
205
+ const getTestImageKeyColors = (signal, testInputKey) => {
206
+ const colors = TEST_IMAGE_PALETTES[signal];
207
+ if (!colors || !TEST_INPUT_KEY_PATTERN.test(testInputKey)) throw new Error("Invalid image test signal or key");
208
+ return [...testInputKey].flatMap((character) => {
209
+ const value = Number.parseInt(character, 16);
210
+ return [colors[value >> 2], colors[value & 3]];
211
+ });
212
+ };
213
+ /**
214
+ * Gets all test trigger categories that support images
215
+ */
216
+ const getImageTestCategories = () => {
217
+ return Object.keys(TEST_IMAGE_PALETTES);
218
+ };
219
+ /** Paint a full-image 4×4 grid as RGBA pixels; callers choose their image encoder. */
220
+ const paintTestImage = (signal, testInputKey, width = 512, height = width) => {
221
+ if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height) || width < 24 || height < 24) throw new RangeError("Test image dimensions must be integers of at least 24 pixels");
222
+ const colors = getTestImageKeyColors(signal, testInputKey);
223
+ const rgba = new Uint8Array(width * height * 4);
224
+ getTestImageKeyCells(width, height).forEach((cell, index) => {
225
+ for (let y = cell.y; y < cell.y + cell.height; y++) for (let x = cell.x; x < cell.x + cell.width; x++) rgba.set(colors[index], (y * width + x) * 4);
226
+ });
227
+ return rgba;
228
+ };
229
+ /** Decode the project key from pixels, tolerating resizing and lossy compression. */
230
+ const detectTestImage = (image, testInputKey) => {
231
+ const { width, height, rgba } = image;
232
+ if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height) || width < 24 || height < 24 || rgba.length !== width * height * 4 || !TEST_INPUT_KEY_PATTERN.test(testInputKey)) return void 0;
233
+ const cells = getTestImageKeyCells(width, height);
234
+ for (const category of getImageTestCategories()) {
235
+ const colors = getTestImageKeyColors(category, testInputKey);
236
+ if (cells.every((cell, index) => {
237
+ const sample = [
238
+ 0,
239
+ 0,
240
+ 0
241
+ ];
242
+ let count = 0;
243
+ for (let y = cell.y + Math.ceil(cell.height / 4); y < cell.y + cell.height * 3 / 4; y++) for (let x = cell.x + Math.ceil(cell.width / 4); x < cell.x + cell.width * 3 / 4; x++) {
244
+ const offset = (y * width + x) * 4;
245
+ if (rgba[offset + 3] < 250) return false;
246
+ for (let c = 0; c < 3; c++) sample[c] += rgba[offset + c];
247
+ count++;
248
+ }
249
+ const distances = TEST_IMAGE_PALETTES[category].map((color) => sample.reduce((sum, value, c) => sum + (value / count - color[c]) ** 2, 0));
250
+ const nearest = distances.indexOf(Math.min(...distances));
251
+ return distances[nearest] <= 900 && TEST_IMAGE_PALETTES[category][nearest] === colors[index];
252
+ })) return category;
253
+ }
254
+ };
255
+ //#endregion
256
+ export { TEST_IMAGE_PALETTES, TEST_INPUT_KEY_PATTERN, detectTestImage, detectTestTrigger, generateTestTextCode, getImageTestCategories, getTestImageKeyCells, getTestImageKeyColors, paintTestImage };
package/esm/testing.d.ts CHANGED
@@ -1,12 +1,12 @@
1
- import { $ as ModerateTextOptions, A as CalmLensServerClientOptions, B as EnsureLensOptions, Ct as Event, F as CreateAssetSignedUrlOptions, H as FolderIdOptions, I as CreateFolderOptions, J as Lens, K as IterateAssetChildrenOptions, M as CdnServeOptions, Mt as PermissionGrant, Nt as Folder, Q as ListEventsOptions, R as DeleteAssetContentOptions, St as PostAssetSignedUrlResponse, T as AssetSearchResponse, Tt as TypedEvent, V as EnsureLensesOptions, X as ListAssetChildrenOptions, Z as ListAssetsOptions, at as RetrieveAssetOptions, ct as SignUploadTokenOptions, d as Asset, dt as SubmitAssetOptions, et as MoveAssetOptions, ft as UpdateAssetOptions, j as CdnAssetUrlOptions, lt as SignedUploadToken, mt as UploadAssetPartOptions, ot as RetryAssetOptions, p as AssetChildrenPage, pt as UpdateFolderOptions, q as IterateAssetsOptions, r as ClassificationCategory, st as SearchAssetsOptions, t as WebhookEventPayload, tt as MoveFolderOptions, vt as WaitForAssetDecisionOptions, x as AssetPage, xt as GetEventsResponse, z as DeleteAssetOptions, zt as AssetBinaryPayloadInput } from "./WebhookPayload-vy8HNMaK.js";
2
- //#region ../shared/src/TestTriggers.d.ts
3
- type ClassificationTestSignal = ClassificationCategory | "sexual.involving-minors" | "violence.graphic";
4
- //#endregion
5
- //#region ../shared/src/MediaTestTriggers.d.ts
1
+ import { $ as SearchAssetsOptions, A as DeleteAssetContentOptions, B as Lens, C as CdnAssetUrlOptions, D as CreateAssetSignedUrlOptions, G as ModerateTextOptions, H as ListAssetChildrenOptions, K as MoveAssetOptions, M as EnsureLensOptions, N as EnsureLensesOptions, O as CreateFolderOptions, P as FolderIdOptions, Q as RetryAssetOptions, R as IterateAssetChildrenOptions, S as CalmLensServerClientOptions, Tt as Folder, U as ListAssetsOptions, W as ListEventsOptions, Z as RetrieveAssetOptions, _ as AssetSearchResponse, _t as TypedEvent, a as AssetChildrenPage, at as UpdateFolderOptions, et as SignUploadTokenOptions, ht as Event, it as UpdateAssetOptions, j as DeleteAssetOptions, jt as AssetBinaryPayloadInput, mt as PostAssetSignedUrlResponse, n as WebhookEventPayload, ot as UploadAssetPartOptions, p as AssetPage, pt as GetEventsResponse, q as MoveFolderOptions, r as Asset, rt as SubmitAssetOptions, tt as SignedUploadToken, ut as WaitForAssetDecisionOptions, w as CdnServeOptions, wt as PermissionGrant, z as IterateAssetsOptions } from "./WebhookPayload-CJoIkGBD.js";
2
+ import { t as ClassificationTestSignal } from "./TestTriggers-CRKtulyp-vrzEq2wm.js";
3
+ //#region src/contract/MediaTestTriggers.d.ts
4
+ //#region packages/shared/src/MediaTestTriggers.d.ts
6
5
  export declare const MEDIA_TEST_SIGNALS: readonly ["sexual", "violence", "self-harm", "harassment", "hate", "criminal-activity", "violence.graphic"];
7
6
  /** Read this aloud, or render it as large, high-contrast text in a video frame. */
8
7
  export declare function generateMediaTestCode(signal: ClassificationTestSignal, testInputKey: string): string;
9
8
  //#endregion
9
+ //#endregion
10
10
  //#region src/testing/MockApiKey.d.ts
11
11
  export declare const MOCK_API_KEY_PREFIX = "sk_test_";
12
12
  export declare const createMockApiKey: (grants?: PermissionGrant[]) => string;