@calmlens/sdk 0.16.2 → 0.17.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 (112) hide show
  1. package/README.md +399 -162
  2. package/cjs/BrowserUpload-DZ0Vbmxi.js +10 -0
  3. package/cjs/BrowserUpload-uZjVFGfP.d.ts +11 -0
  4. package/cjs/CalmLensAppClient-CV_K26Ce.d.ts +40 -0
  5. package/cjs/CalmLensAppClient-DBGmaDlG.js +89 -0
  6. package/cjs/CalmLensConnection-K1TAD1Z-.js +659 -0
  7. package/cjs/CalmLensServerClient-C-pxiz0C.js +645 -0
  8. package/cjs/CalmLensServerClient-JtP687a3.d.ts +235 -0
  9. package/cjs/CalmLensTypes-9Vt16pc7.d.ts +40400 -0
  10. package/cjs/Classification-94kSYWZw-CMnQKW13.d.ts +5873 -0
  11. package/cjs/CryptoUtils-B_x8lMIx-YBW1-bvL.js +42 -0
  12. package/cjs/FileIcons-Ob003MdS.js +175 -0
  13. package/cjs/Jurisdiction-FusEXb4U-zVs21tDv.js +91 -0
  14. package/cjs/ModerationContext-9lGm-Mna-CcmbRnOy.d.ts +67 -0
  15. package/cjs/ModerationContextLimits-Dj1YAl3G.js +1311 -0
  16. package/cjs/ReactUpload-DxtFMr06.js +54 -0
  17. package/cjs/ReactUpload-nZahpmyg.d.ts +17 -0
  18. package/cjs/TestTriggers-By07LR31-CmK0yTyR.d.ts +271 -0
  19. package/cjs/TestTriggers-DcArt0Nu-CvoaONUB.js +1326 -0
  20. package/cjs/UploadController-BBkN4Ni6.js +618 -0
  21. package/{esm/index-C9Xw1aid.d.ts → cjs/WebhookPayload-2aL31v0J.d.ts} +131 -155
  22. package/cjs/app.d.ts +7 -44
  23. package/cjs/app.js +11 -89
  24. package/cjs/elements.d.ts +88 -0
  25. package/cjs/elements.js +555 -0
  26. package/cjs/file-icons.d.ts +67 -0
  27. package/cjs/file-icons.js +10 -0
  28. package/cjs/index-CI4BzN4W.d.ts +252 -0
  29. package/cjs/index.d.ts +6 -4
  30. package/cjs/index.js +9 -4
  31. package/cjs/react-native.d.ts +56 -0
  32. package/cjs/react-native.js +495 -0
  33. package/cjs/react.d.ts +30 -0
  34. package/cjs/react.js +370 -0
  35. package/cjs/server.d.ts +12 -191
  36. package/cjs/server.js +18 -378
  37. package/cjs/test-inputs.d.ts +2 -2
  38. package/cjs/test-inputs.js +9 -264
  39. package/cjs/testing.d.ts +285 -190
  40. package/cjs/testing.js +2393 -1203
  41. package/cjs/ui-CgZpgpNo.d.ts +526 -0
  42. package/cjs/ui-KceuoVCM.js +583 -0
  43. package/cjs/ui.d.ts +2 -0
  44. package/cjs/ui.js +27 -0
  45. package/cjs/vue.d.ts +490 -0
  46. package/cjs/vue.js +419 -0
  47. package/esm/BrowserUpload-D7-zNmc0.js +5 -0
  48. package/esm/BrowserUpload-uZjVFGfP.d.ts +11 -0
  49. package/esm/CalmLensAppClient-CV_K26Ce.d.ts +40 -0
  50. package/esm/CalmLensAppClient-FVMwrHlH.js +84 -0
  51. package/esm/CalmLensConnection-iyNQHA8L.js +582 -0
  52. package/esm/CalmLensServerClient-JtP687a3.d.ts +235 -0
  53. package/esm/CalmLensServerClient-fQEYKLw4.js +586 -0
  54. package/esm/CalmLensTypes-9Vt16pc7.d.ts +40400 -0
  55. package/esm/Classification-94kSYWZw-CMnQKW13.d.ts +5873 -0
  56. package/esm/CryptoUtils-B_x8lMIx-DhuWGzqn.js +25 -0
  57. package/esm/FileIcons-M6dzjFxd.js +128 -0
  58. package/esm/Jurisdiction-FusEXb4U-1ov6m5yE.js +46 -0
  59. package/esm/ModerationContext-9lGm-Mna-CcmbRnOy.d.ts +67 -0
  60. package/esm/ModerationContextLimits-D1x_WAtP.js +1114 -0
  61. package/esm/ReactUpload-D6yBCkcw.js +37 -0
  62. package/esm/ReactUpload-nZahpmyg.d.ts +17 -0
  63. package/esm/TestTriggers-By07LR31-CmK0yTyR.d.ts +271 -0
  64. package/esm/TestTriggers-DcArt0Nu-s0AQZuWE.js +1266 -0
  65. package/esm/UploadController-txp5NuzH.js +589 -0
  66. package/{cjs/index-C9Xw1aid.d.ts → esm/WebhookPayload-2aL31v0J.d.ts} +131 -155
  67. package/esm/app.d.ts +7 -44
  68. package/esm/app.js +3 -86
  69. package/esm/elements.d.ts +88 -0
  70. package/esm/elements.js +528 -0
  71. package/esm/file-icons.d.ts +67 -0
  72. package/esm/file-icons.js +1 -0
  73. package/esm/index-CI4BzN4W.d.ts +252 -0
  74. package/esm/index.d.ts +6 -4
  75. package/esm/index.js +1 -1
  76. package/esm/react-native.d.ts +56 -0
  77. package/esm/react-native.js +467 -0
  78. package/esm/react.d.ts +30 -0
  79. package/esm/react.js +343 -0
  80. package/esm/server.d.ts +12 -191
  81. package/esm/server.js +3 -369
  82. package/esm/styles.css +147 -0
  83. package/esm/svelte/CalmLensMedia.svelte +134 -0
  84. package/esm/svelte/CalmLensUploader.svelte +175 -0
  85. package/esm/svelte/Icon.svelte +7 -0
  86. package/esm/svelte/index.d.ts +37 -0
  87. package/esm/svelte/index.js +6 -0
  88. package/esm/test-inputs.d.ts +2 -2
  89. package/esm/test-inputs.js +1 -256
  90. package/esm/testing.d.ts +285 -190
  91. package/esm/testing.js +2329 -1122
  92. package/esm/ui-Ca_SpJXf.js +458 -0
  93. package/esm/ui-CgZpgpNo.d.ts +526 -0
  94. package/esm/ui.d.ts +2 -0
  95. package/esm/ui.js +3 -0
  96. package/esm/vue.d.ts +490 -0
  97. package/esm/vue.js +392 -0
  98. package/package.json +110 -11
  99. package/cjs/CalmLensConnection-5Hj3trGr.js +0 -234
  100. package/cjs/CalmLensUtils-BeIl1N6N.js +0 -699
  101. package/cjs/CalmLensVersion-BGpB9t4-.js +0 -480
  102. package/cjs/CalmLensWebhooks-CogUYk8-.js +0 -201
  103. package/cjs/Classification-BNhgwGEm-DmA9VEuE.d.ts +0 -4806
  104. package/cjs/TestTriggers-CRKtulyp-vrzEq2wm.d.ts +0 -44
  105. package/cjs/WebhookPayload-CJoIkGBD.d.ts +0 -11753
  106. package/esm/CalmLensConnection-BAJdoZHy.js +0 -193
  107. package/esm/CalmLensUtils-DqniTKJ8.js +0 -574
  108. package/esm/CalmLensVersion-l_khh4nS.js +0 -415
  109. package/esm/CalmLensWebhooks-BtrKKLvg.js +0 -148
  110. package/esm/Classification-BNhgwGEm-DmA9VEuE.d.ts +0 -4806
  111. package/esm/TestTriggers-CRKtulyp-vrzEq2wm.d.ts +0 -44
  112. package/esm/WebhookPayload-CJoIkGBD.d.ts +0 -11753
package/esm/testing.d.ts CHANGED
@@ -1,17 +1,125 @@
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
5
- export declare const MEDIA_TEST_SIGNALS: readonly ["sexual", "violence", "self-harm", "harassment", "hate", "criminal-activity", "violence.graphic"];
1
+ import { Ft as TypedEvent, Ht as PermissionGrant, T as CalmLensServerClientOptions, Ut as Folder, Yt as AssetBinaryPayloadInput, _t as SignedUploadToken, f as AssetModeration, gt as SignUploadTokenOptions, n as Asset, w as CalmLensAppClientOptions } from "./CalmLensTypes-9Vt16pc7.js";
2
+ import { t as CalmLensAppClient } from "./CalmLensAppClient-CV_K26Ce.js";
3
+ import { l as ApiErrorCode, n as WebhookEventPayload } from "./WebhookPayload-2aL31v0J.js";
4
+ import { t as CalmLensServerClient } from "./CalmLensServerClient-JtP687a3.js";
5
+ import { t as ClassificationTestSignal } from "./TestTriggers-By07LR31-CmK0yTyR.js";
6
+ import "zod";
7
+ /**
8
+ * Signs a single-use upload token with `apiKey`, locally. `CalmLensServerClient` and
9
+ * `MockCalmLensServerClient` share it so both check options the same way. `@calmlens/sdk/testing`
10
+ * exports it as `signUploadToken`, whose fixed `now` (milliseconds since the epoch) and `nonce`
11
+ * give the same token every time, for comparing with known tokens in tests.
12
+ */
13
+ declare const signUploadTokenWithApiKey: (apiKey: string, options: SignUploadTokenOptions, { now, nonce }?: {
14
+ now?: number;
15
+ nonce?: string;
16
+ }) => Promise<SignedUploadToken>;
17
+ export declare const MEDIA_TEST_SIGNALS: readonly ["sexual", "violence", "self-harm", "harassment", "hate", "criminal-activity", "personal-information", "profanity", "violence.graphic"];
6
18
  /** Read this aloud, or render it as large, high-contrast text in a video frame. */
7
- export declare function generateMediaTestCode(signal: ClassificationTestSignal, testInputKey: string): string;
8
- //#endregion
9
- //#endregion
10
- //#region src/testing/MockApiKey.d.ts
19
+ export declare function generateMediaTestCode(signal: ClassificationTestSignal, testInputKey: string): Promise<string>;
11
20
  export declare const MOCK_API_KEY_PREFIX = "sk_test_";
12
21
  export declare const createMockApiKey: (grants?: PermissionGrant[]) => string;
13
- //#endregion
14
- //#region src/testing/MockStorage.d.ts
22
+ /**
23
+ * The prefix of every kind of object customers can see, so an ID says what it identifies:
24
+ * `proj_8fK2mQx7Lp4NvB3tRz9Y` is a project. Rows that never leave the backend (usage meters, upload
25
+ * token uses, retained variants, terms acceptances, notices) keep their own internal IDs.
26
+ */
27
+ declare const ID_PREFIXES: {
28
+ readonly organisation: "org";
29
+ readonly project: "proj";
30
+ readonly asset: "asset";
31
+ readonly folder: "fldr";
32
+ readonly lens: "lens";
33
+ readonly apiKey: "key";
34
+ readonly uploadPreset: "preset";
35
+ readonly connector: "conn";
36
+ readonly connectorDelivery: "dlv";
37
+ readonly hashList: "hl";
38
+ readonly hashListEntry: "hle";
39
+ readonly policy: "pol";
40
+ readonly event: "evt";
41
+ readonly user: "user";
42
+ readonly member: "mem";
43
+ readonly invitation: "inv";
44
+ readonly supportTicket: "tkt";
45
+ readonly supportTicketMessage: "msg";
46
+ readonly humanReview: "rev";
47
+ readonly appeal: "apl";
48
+ readonly complianceCase: "case";
49
+ readonly safetyHold: "hold";
50
+ readonly creditPurchase: "cp";
51
+ readonly oauthClient: "oac";
52
+ readonly waitlistEntry: "wl";
53
+ };
54
+ type IdKind = keyof typeof ID_PREFIXES;
55
+ type IdPrefix = (typeof ID_PREFIXES)[IdKind];
56
+ /** The parent categories, in the API's order. */
57
+ declare const MOCK_CATEGORIES: readonly ["sexual", "violence", "self-harm", "harassment", "hate", "criminal-activity", "personal-information", "profanity"];
58
+ /**
59
+ * Where the mock's moderation leaves an upload: `pending` (still processing), `failed`
60
+ * (processing failed, retryable), or a decision with that moderation status (`purged` also
61
+ * removes the content).
62
+ */
63
+ type MockAssetOutcome = "pending" | "failed" | "allowed" | "limited" | "age-restricted" | "blocked" | "purged";
64
+ type Category = (typeof MOCK_CATEGORIES)[number];
65
+ /**
66
+ * A moderation decision for `mockOptions.moderate` or `completeProcessing`, built with
67
+ * `mockDecision(status)`: flag the categories it found, with their scores and qualifiers, and
68
+ * mark the categories it couldn't assess. Each call checks its names and scores and throws
69
+ * `sdk/invalid-argument` for one CalmLens doesn't have.
70
+ */
71
+ export declare class MockDecision {
72
+ #private;
73
+ readonly status: MockAssetOutcome;
74
+ constructor(status: MockAssetOutcome);
75
+ /**
76
+ * Records findings in `category` with its `score` (0 to 1) and, optionally, scores for its
77
+ * qualifiers, such as `{ "violence-threat": 0.9 }`. The policy acts on a category at 0.5 and
78
+ * above, so `flaggedCategories` lists it there unless the status is `allowed`.
79
+ */
80
+ flag(category: Category, score: number, qualifiers?: Record<string, number>): this;
81
+ /** Marks `category` as not assessed (`not-evaluated` for reason `not-supported`). */
82
+ notEvaluated(category: Category): this;
83
+ /**
84
+ * The report a decided asset gets, as the API sends it: every category (unflagged ones
85
+ * evaluated with score 0), policy-adjusted results and the automated action and
86
+ * `flaggedCategories`. `undefined` for `pending` and `failed`.
87
+ */
88
+ report(): Omit<AssetModeration, "status" | "manualReviews"> | undefined;
89
+ }
90
+ /** Starts a moderation decision with this status; see `MockDecision`. */
91
+ export declare const mockDecision: (status: MockAssetOutcome) => MockDecision;
92
+ /** Why the mock is moderating an asset, for `mockOptions.moderate`. */
93
+ type MockModerationTrigger =
94
+ /** An upload completed: `assets.create`, `assets.classify`, `text.classify` or a final part. */
95
+ "upload" |
96
+ /** `assets.retry` queued a failed asset again. */
97
+ "retry" |
98
+ /** `assets.explain` with `rerun: true` queued the asset again. */
99
+ "rerun" |
100
+ /** `completeProcessing(assetId)` finished processing without a decision of its own. */
101
+ "complete";
102
+ /** What `mockOptions.moderate` receives for each asset the mock moderates. */
103
+ interface MockModerationInput {
104
+ /** The asset as stored, still processing. */
105
+ asset: Asset;
106
+ /** The uploaded bytes (`null` without stored content, such as a website), and the text of a plain-text upload. */
107
+ content: {
108
+ bytes: Uint8Array | null;
109
+ text: string | null;
110
+ };
111
+ trigger: MockModerationTrigger;
112
+ /**
113
+ * `true` when the API would queue the work rather than answer with the decision: queued
114
+ * uploads, retries, reruns and `completeProcessing`.
115
+ */
116
+ queued: boolean;
117
+ }
118
+ /**
119
+ * Decides an asset the mock moderates: a status or a `mockDecision(...)`, or a promise of one.
120
+ * Without it, queued uploads, retries and reruns stay `pending` and everything else is `allowed`.
121
+ */
122
+ type MockModerate = (input: MockModerationInput) => MockAssetOutcome | MockDecision | Promise<MockAssetOutcome | MockDecision>;
15
123
  interface MockStoredContent {
16
124
  assetId: string;
17
125
  parts: Map<number, Uint8Array>;
@@ -68,208 +176,196 @@ export declare class InMemoryMockDatabaseStorage implements MockDatabaseStorage
68
176
  listFolders(projectId: string): Promise<Folder[]>;
69
177
  deleteFolder(folderId: string): Promise<void>;
70
178
  }
71
- //#endregion
72
- //#region src/testing/MockCalmLensServerClient.d.ts
73
- interface MockCalmLensServerClientOptions extends CalmLensServerClientOptions {
74
- mockOptions?: {
75
- databaseStorage?: MockDatabaseStorage;
76
- imageStorage?: MockImageStorage;
77
- createId?: () => string;
78
- now?: () => number;
79
- /** Outcome of `assets.create` (and queued multipart uploads). Defaults to `pending`. */
80
- queuedOutcome?: MockAssetOutcome;
81
- /** Outcome of `assets.moderate` and `text.moderate`. Defaults to `approved`. */
82
- analyzedOutcome?: MockAssetOutcome;
83
- signedUrlBaseUrl?: string;
84
- };
85
- }
179
+ /** A reviewer's decision for `decide`: the moderation status it gives the asset. */
180
+ type MockReviewDecision = "allowed" | "limited" | "age-restricted" | "blocked" | "purged";
86
181
  /**
87
- * Where a mock upload ends up: `pending` (still processing), `approved` or `rejected` (processing
88
- * done with that decision), or `failed` (processing failed, retryable).
182
+ * A client method, named like its path on the client. A request is recorded under the first
183
+ * method that sends it; `callsTo` and `failNextRequest` also match the others (see
184
+ * `MockCalmLensCall`).
89
185
  */
90
- type MockAssetOutcome = "pending" | "approved" | "rejected" | "failed";
91
- /** The options each `MockCalmLensServerClient` method was given, by operation name. */
92
- interface MockCalmLensOperationOptions {
93
- "assets.list": ListAssetsOptions;
94
- "assets.search": SearchAssetsOptions;
95
- "assets.retrieve": RetrieveAssetOptions;
96
- "assets.update": UpdateAssetOptions;
97
- "assets.listChildren": ListAssetChildrenOptions;
98
- "assets.create": SubmitAssetOptions;
99
- "assets.moderate": SubmitAssetOptions;
100
- "assets.uploadPart": UploadAssetPartOptions;
101
- "assets.createSignedUrl": CreateAssetSignedUrlOptions;
102
- "assets.move": MoveAssetOptions;
103
- "assets.retry": RetryAssetOptions;
104
- "assets.waitForDecision": WaitForAssetDecisionOptions;
105
- "assets.deleteContent": DeleteAssetContentOptions;
106
- "assets.delete": DeleteAssetOptions;
107
- "text.moderate": ModerateTextOptions;
108
- "cdn.serve": CdnServeOptions;
109
- "events.list": ListEventsOptions;
110
- "folders.list": undefined;
111
- "folders.retrieve": FolderIdOptions;
112
- "folders.create": CreateFolderOptions;
113
- "folders.update": UpdateFolderOptions;
114
- "folders.delete": FolderIdOptions;
115
- "folders.move": MoveFolderOptions;
116
- ensureLens: EnsureLensOptions;
117
- ensureLenses: EnsureLensesOptions;
118
- "uploadTokens.sign": SignUploadTokenOptions;
119
- }
120
- /** A `MockCalmLensServerClient` method, named like its path on the client. */
121
- type MockCalmLensOperation = keyof MockCalmLensOperationOptions;
186
+ type MockCalmLensOperation = "assets.list" | "assets.iterate" | "assets.search" | "assets.retrieve" | "assets.waitForDecision" | "assets.update" | "assets.listChildren" | "assets.iterateChildren" | "assets.create" | "assets.classify" | "assets.uploadPart" | "assets.createSignedUrl" | "assets.move" | "assets.retry" | "assets.explain" | "assets.decide" | "assets.deleteContent" | "assets.delete" | "assets.batch" | "text.classify" | "cdn.serve" | "events.list" | "events.pages" | "folders.list" | "folders.retrieve" | "folders.create" | "folders.update" | "folders.delete" | "folders.move" | "hashLists.list" | "hashLists.retrieve" | "hashLists.create" | "hashLists.update" | "hashLists.delete" | "hashLists.listEntries" | "hashLists.addEntry" | "hashLists.removeEntry" | "ensureLens" | "ensureLenses";
122
187
  /**
123
- * One recorded call: the method and the options object it was given, exactly as passed. Narrow
124
- * on `operation` (or use `callsTo`) to get its options type.
188
+ * One request a mock client sent, as the in-memory API received it. `operation` is the client
189
+ * method that sends it; requests several methods send are recorded under the first: `text.classify`
190
+ * as `assets.classify`, `ensureLens` as `ensureLenses`, `assets.waitForDecision` polls as
191
+ * `assets.retrieve`, `assets.iterate` pages as `assets.list`, and so on. `callsTo` matches all of
192
+ * them. Methods that send nothing, such as `uploadTokens.sign` or a cached `ensureLens`, are not
193
+ * recorded.
125
194
  */
126
- type MockCalmLensCall<TOperation extends MockCalmLensOperation = MockCalmLensOperation> = { [Operation in TOperation]: {
127
- operation: Operation;
128
- options: MockCalmLensOperationOptions[Operation];
129
- }; }[TOperation];
195
+ interface MockCalmLensCall {
196
+ operation: MockCalmLensOperation;
197
+ method: string;
198
+ /** The request URL. */
199
+ url: string;
200
+ /** The path after the API (or CDN) base URL, with each segment decoded, such as `/assets/a1`. */
201
+ path: string;
202
+ /** Query parameters as sent, such as `{ "ordering[0][field]": "createdAt" }`. */
203
+ query: Record<string, string>;
204
+ /** Request headers, with lowercase names. */
205
+ headers: Record<string, string>;
206
+ /** The JSON body, the text of a `text/*` upload, the bytes of another upload, or `undefined`. */
207
+ body: unknown;
208
+ /** The HTTP status the mock answered with, or `undefined` when the request failed without one. */
209
+ status: number | undefined;
210
+ }
130
211
  /**
131
- * What a simulated request failure throws: `"transport-error"` and `"timeout"` throw the same
132
- * `CalmLensError` the real client throws for a network failure (`sdk/transport-error`) or a
133
- * deadline (`sdk/timeout`); any `Error` is thrown as given.
212
+ * What a simulated request failure does: `"transport-error"` and `"timeout"` fail the request
213
+ * like a network failure (`sdk/transport-error`) or a deadline (`sdk/timeout`); an API error
214
+ * code, such as `"upload/limit-exceeded"`, answers with that API error and its HTTP status; a
215
+ * `CalmLensError` is thrown as is and any other `Error` as the cause of `sdk/transport-error`,
216
+ * like an exception from `fetch`.
134
217
  */
135
- type MockRequestFailure = "transport-error" | "timeout" | Error;
218
+ type MockRequestFailure = "transport-error" | "timeout" | ApiErrorCode | Error;
219
+ /** Options for a simulated API error: its `details` and the `Retry-After` it answers with. */
220
+ interface MockFailureOptions {
221
+ operation?: MockCalmLensOperation;
222
+ /** The error's `details`, for an API error code. */
223
+ details?: Record<string, unknown>;
224
+ /** Seconds for the `Retry-After` header, for an API error code. */
225
+ retryAfter?: number;
226
+ }
136
227
  /** An event to add to the mock feed; omitted fields get defaults. */
137
228
  type MockEventInput = Partial<Omit<TypedEvent, "type">> & {
138
229
  type: TypedEvent["type"];
139
230
  };
231
+ /** The upload preset settings a `MockCalmLensAppClient` applies, as the API would. */
232
+ interface MockUploadPreset {
233
+ /** Refuse uploads without a token, like **Users your server approves**. Defaults to `false`. */
234
+ requireToken?: boolean;
235
+ /** Accepted content types, such as `["image/*"]`. Defaults to any type. */
236
+ allowedContentTypes?: string[];
237
+ maxFileSizeInBytes?: number;
238
+ /** Return `status` to the uploader. Defaults to `true`. */
239
+ showDecisions?: boolean;
240
+ /** Defaults to `public`. */
241
+ visibility?: "public" | "private";
242
+ /** Defaults to `true`, so the CDN can serve the upload. */
243
+ keepContentAfterProcessing?: boolean;
244
+ folderId?: string | null;
245
+ }
246
+ interface MockCalmLensServerClientOptions extends CalmLensServerClientOptions {
247
+ mockOptions?: {
248
+ databaseStorage?: MockDatabaseStorage;
249
+ imageStorage?: MockImageStorage;
250
+ /**
251
+ * Returns a new ID for an object of the kind `prefix` names (`asset`, `fldr`, `hl`, `hle`,
252
+ * `lens`, `evt` or `rev`). The default is like the API's: the prefix, `_` and 20 random
253
+ * base62 characters, such as `asset_5Gm2Rk8XqT3wLp9VbN4c`.
254
+ */
255
+ createId?: (prefix: IdPrefix) => string;
256
+ /**
257
+ * The clock, in epoch milliseconds. The default is the system clock, moved on by at least a
258
+ * millisecond per reading, so assets created in a row have increasing `createdAt` values and
259
+ * orderings by `createdAt` don't depend on IDs.
260
+ */
261
+ now?: () => number;
262
+ /**
263
+ * Decides each asset the mock moderates, from the stored asset, its content (bytes, and the
264
+ * text of a plain-text upload) and why it is moderated (`upload`, `retry`, `rerun` or
265
+ * `complete`). Return a status or a `mockDecision(...)`, whose report fills the asset's
266
+ * categories and policy. Without it, queued uploads, retries and reruns stay `pending` until
267
+ * `completeProcessing`, and everything else is `allowed`.
268
+ */
269
+ moderate?: MockModerate;
270
+ signedUrlBaseUrl?: string;
271
+ };
272
+ }
140
273
  /**
141
- * An in-memory stand-in for `CalmLensServerClient` with the same methods, validation and error
142
- * codes. API errors carry the HTTP status, a `requestId` and response headers like the real
143
- * client's, so error handling can be tested without a network.
274
+ * The real `CalmLensServerClient`, answered by an in-memory CalmLens API and CDN instead of the
275
+ * network: every method runs the real client's code, and the in-memory API answers its requests
276
+ * with the API's statuses, bodies, error bodies and headers. Mock helpers decide assets, add
277
+ * events, change the API key's grants, fail requests and list the requests sent.
144
278
  */
145
- export declare class MockCalmLensServerClient {
279
+ export declare class MockCalmLensServerClient extends CalmLensServerClient {
280
+ #private;
146
281
  readonly databaseStorage: MockDatabaseStorage;
147
282
  readonly imageStorage: MockImageStorage;
148
- private readonly createId;
149
- private readonly now;
150
- private readonly queuedOutcome;
151
- private readonly analyzedOutcome;
152
- private readonly apiKeyGrants;
153
- private readonly signedUrlBaseUrl;
154
- private readonly cdnBaseUrl;
155
- private readonly projectId;
156
- private readonly apiKey;
157
- private readonly lenses;
158
- private readonly eventFeed;
159
- /** The queue mode each open multipart upload was started with, as the API saves it. */
160
- private readonly uploadQueues;
161
- private readonly idempotentResults;
162
- private requestCount;
163
- private readonly pendingFailures;
164
- /** Every call made through this client, oldest first. */
165
- readonly calls: MockCalmLensCall[];
166
- /** Accepts the same options as `CalmLensServerClient` (validated the same way), plus `mockOptions`. */
283
+ /**
284
+ * Accepts the same options as `CalmLensServerClient` (validated the same way), plus
285
+ * `mockOptions`. `maxRetries` defaults to 0, so a failed request fails once; retry backoff and
286
+ * `assets.waitForDecision` take no real time.
287
+ */
167
288
  constructor(options: MockCalmLensServerClientOptions);
168
289
  /**
169
- * Makes the next call (or the next call to `operation`) throw `failure` instead of running,
170
- * as if the request never completed. Queue several to fail several calls in a row.
290
+ * Every request sent to the mock's API and CDN, oldest first, by this client and the
291
+ * `MockCalmLensAppClient`s that use it, each with the client method that sent it.
171
292
  */
172
- failNextRequest(failure?: MockRequestFailure, { operation }?: {
173
- operation?: MockCalmLensOperation;
174
- }): void;
175
- /** Calls recorded for one operation, oldest first. */
176
- callsTo<TOperation extends MockCalmLensOperation>(operation: TOperation): MockCalmLensCall<TOperation>[];
293
+ get calls(): MockCalmLensCall[];
294
+ /**
295
+ * Requests `operation` sends, oldest first. A request several methods send matches each of
296
+ * them: `callsTo("text.classify")` also lists `assets.classify` text uploads, and
297
+ * `callsTo("assets.waitForDecision")` the `assets.retrieve` requests it polls with.
298
+ */
299
+ callsTo(operation: MockCalmLensOperation): MockCalmLensCall[];
300
+ /** Forgets the requests recorded so far, so `calls` lists only later ones. */
301
+ clearCalls(): void;
302
+ /**
303
+ * Makes the next request (or the next one `operation` sends) fail instead of reaching the
304
+ * in-memory API. Each queued failure fails one request, so a retry takes the next queued
305
+ * failure or reaches the API. An API error code answers with that error and its HTTP status,
306
+ * with `details` and a `Retry-After` of `retryAfter` seconds when given.
307
+ */
308
+ failNextRequest(failure?: MockRequestFailure, options?: MockFailureOptions): void;
309
+ /**
310
+ * Changes the grants of the mock's API key, as editing the key in the portal would: later
311
+ * requests, such as `cdn.serve`, use them. The key itself stays the same, so idempotency keys
312
+ * it used still replay.
313
+ */
314
+ setApiKeyGrants(grants: PermissionGrant[]): void;
177
315
  /**
178
316
  * Adds an event to the feed `events.list` reads, as if CalmLens had recorded it. Returns the
179
317
  * stored event; pass it to `toWebhookEventPayload` to build a matching webhook body.
180
318
  */
181
- enqueueEvent(input: MockEventInput): TypedEvent;
182
- /** An API error as the real client throws it: status, default message and request ID. */
183
- private apiError;
184
- private assertValidUploadMimeType;
185
- private invalidPayload;
186
- private findOwnAsset;
187
- private begin;
188
- private jsonResponse;
189
- get cdn(): {
190
- getAssetUrl: ({ assetIdentifier, ...options }: CdnAssetUrlOptions) => string;
191
- /**
192
- * Serves stored content like the CDN: `200` with `Content-Type`, `Content-Length`,
193
- * `ETag` and `X-Request-Id`, or a JSON error body with `404`/`403`. Never throws for
194
- * HTTP errors, like the real client.
195
- */
196
- serve: (serveOptions: CdnServeOptions) => Promise<Response>;
197
- };
198
- get events(): {
199
- /**
200
- * Reads the mock feed like the API: without `after`, no events and a cursor for "now";
201
- * with `after`, up to `limit` events added since, oldest first. Add events with
202
- * `enqueueEvent`.
203
- */
204
- list: (options?: ListEventsOptions) => Promise<GetEventsResponse>;
205
- pages: (options?: ListEventsOptions) => AsyncGenerator<GetEventsResponse, void, undefined>;
206
- };
207
- private listEvents;
208
- private iterateEventPages;
209
- private iteratePages;
210
- get text(): {
211
- moderate: (options: ModerateTextOptions) => Promise<Asset>;
212
- };
213
- get assets(): {
319
+ enqueueEvent(input: MockEventInput): Promise<TypedEvent>;
320
+ /**
321
+ * Applies a reviewer's decision to a stored asset, as the portal or API would: the moderation
322
+ * status changes (`purged` also removes the content, with reason `requested` and an
323
+ * `asset.content-removed` event), the review joins `manualReviews`, and an `asset.<decision>`
324
+ * event with a `decided-by: reviewer` fact joins the events feed. Rejects with
325
+ * `asset/not-found` for an unknown asset. It sends no request.
326
+ */
327
+ decide(assetId: string, decision: MockReviewDecision): Promise<Asset>;
328
+ /**
329
+ * Finishes processing a pending asset as the moderation pipeline would: with `decision`, or
330
+ * else what `mockOptions.moderate` returns for the `complete` trigger (`allowed` by default).
331
+ * The asset gets the decision's report, and `asset.classified` and the automated
332
+ * `asset.<status>` (or `asset.failed`) join the events feed. No review is recorded, so the
333
+ * asset can still be moderated again. Rejects with `asset/not-found` for an unknown asset and
334
+ * `sdk/invalid-argument` for one that isn't processing. It sends no request.
335
+ */
336
+ completeProcessing(assetId: string, decision?: MockAssetOutcome | MockDecision): Promise<Asset>;
337
+ }
338
+ interface MockCalmLensAppClientOptions extends CalmLensAppClientOptions {
339
+ mockOptions?: {
214
340
  /**
215
- * Matches `query` (case-insensitively) against each asset's name, ID, external ID,
216
- * description and tags, best match first. Mock scores are 1 for an exact match and 0.5
217
- * otherwise; `mode` is accepted but there is no semantic search.
341
+ * The mock project the uploads go into. Pass the `MockCalmLensServerClient` your server code
342
+ * uses to see the uploads there and serve them from its CDN. Without one, the client creates
343
+ * its own, available as `server`.
218
344
  */
219
- search: (options: SearchAssetsOptions) => Promise<AssetSearchResponse>;
220
- /** Lists assets with the real client's paging, `ordering`, `folderId` and `filter`. */
221
- list: (options?: ListAssetsOptions) => Promise<AssetPage>;
222
- iterate: (options?: IterateAssetsOptions) => AsyncGenerator<Asset, void, undefined>;
223
- retrieve: (options: RetrieveAssetOptions) => Promise<Asset>;
224
- listChildren: (childrenOptions?: ListAssetChildrenOptions) => Promise<AssetChildrenPage>;
225
- iterateChildren: (options: IterateAssetChildrenOptions) => AsyncGenerator<Asset, void, undefined>;
226
- update: (options: UpdateAssetOptions) => Promise<Asset>;
227
- create: (options: SubmitAssetOptions) => Promise<Asset>;
228
- moderate: (options: SubmitAssetOptions) => Promise<Asset>;
229
- /** Continues a multipart upload: parts must arrive in order while it is `uploading`. */
230
- uploadPart: (options: UploadAssetPartOptions) => Promise<Asset>;
231
- createSignedUrl: (signedUrlOptions: CreateAssetSignedUrlOptions) => Promise<PostAssetSignedUrlResponse>;
232
- move: (options: MoveAssetOptions) => Promise<Asset>;
233
- retry: (options: RetryAssetOptions) => Promise<Asset>;
234
- /** Waits like the real client, checking the mock's storage for a decision. */
235
- waitForDecision: (options: WaitForAssetDecisionOptions) => Promise<Asset>;
236
- deleteContent: (options: DeleteAssetContentOptions) => Promise<Asset>;
237
- delete: (options: DeleteAssetOptions) => Promise<void>;
345
+ server?: MockCalmLensServerClient;
346
+ preset?: MockUploadPreset;
347
+ /** The server's `mockOptions.moderate` when the client creates its own server. */
348
+ moderate?: MockModerate;
238
349
  };
239
- /** Signs real upload tokens with the mock's API key, like the real client, without a request. */
240
- get uploadTokens(): {
241
- sign: (options: SignUploadTokenOptions) => Promise<SignedUploadToken>;
242
- };
243
- get folders(): {
244
- list: () => Promise<Folder[]>;
245
- retrieve: (options: FolderIdOptions) => Promise<Folder>;
246
- create: (options: CreateFolderOptions) => Promise<Folder>;
247
- update: (options: UpdateFolderOptions) => Promise<Folder>;
248
- delete: (options: FolderIdOptions) => Promise<void>;
249
- move: (options: MoveFolderOptions) => Promise<Folder>;
250
- };
251
- ensureLens(options: EnsureLensOptions): Promise<Lens>;
252
- private ensureLensDefinition;
253
- ensureLenses(options: EnsureLensesOptions): Promise<Lens[]>;
254
- /** Replays the first result for a reused idempotency key, like the API. */
255
- private withIdempotency;
256
- private moderateAsset;
257
- private createAsset;
258
- /**
259
- * Moves an asset to `outcome` like the pipeline would: a decision finishes processing, and
260
- * finished processing removes content kept only temporarily (`keepContentAfterProcessing` off).
261
- */
262
- private applyOutcome;
263
- private resolveFolder;
264
- private findOwnFolder;
265
- private assertUniqueFolderName;
266
- private getContentFormat;
267
- private getSizeInBytes;
268
350
  }
269
- //#endregion
270
- //#region src/testing/MockWebhooks.d.ts
271
- /** The JSON body of a webhook delivery for a stored event (or an event from `events.list`). */
272
- export declare const toWebhookEventPayload: (event: Event) => WebhookEventPayload;
351
+ /**
352
+ * The real `CalmLensAppClient`, answered by the in-memory API of a `MockCalmLensServerClient`.
353
+ * The API applies `mockOptions.preset` to this client's uploads like CalmLens applies an upload
354
+ * preset: its token, content type and size rules with the API's error codes, and its visibility,
355
+ * folder, content retention and decision display. The client can check and continue only the
356
+ * uploads it made.
357
+ */
358
+ export declare class MockCalmLensAppClient extends CalmLensAppClient {
359
+ #private;
360
+ /** The mock project the uploads are stored in. */
361
+ readonly server: MockCalmLensServerClient;
362
+ constructor(options: MockCalmLensAppClientOptions);
363
+ }
364
+ /**
365
+ * The JSON body of a webhook delivery for an event, such as one from `events.list`: the event in
366
+ * the same shape `events.list` returns it. Fields that are not part of that shape are left out.
367
+ */
368
+ export declare const toWebhookEventPayload: (event: TypedEvent) => WebhookEventPayload;
273
369
  /** `v1=<hex HMAC-SHA256 of "<timestamp>.<body>">`, the `x-calmlens-signature` header value. */
274
370
  export declare const createWebhookSignatureHeader: (secret: string, timestamp: number | string, body: string) => Promise<string>;
275
371
  /** Headers for one signed delivery of `body`, as CalmLens sends them. */
@@ -279,5 +375,4 @@ export declare const createWebhookHeaders: (options: {
279
375
  deliveryId: string;
280
376
  timestampSeconds?: number;
281
377
  }) => Promise<Record<string, string>>;
282
- //#endregion
283
- export type { MockCalmLensCall, MockCalmLensOperation, MockCalmLensOperationOptions, MockCalmLensServerClientOptions, MockDatabaseStorage, MockEventInput, MockImageStorage, MockRequestFailure, MockStoredContent };
378
+ export { type MockAssetOutcome, type MockCalmLensAppClientOptions, type MockCalmLensCall, type MockCalmLensOperation, type MockCalmLensServerClientOptions, type MockDatabaseStorage, type MockEventInput, type MockFailureOptions, type MockImageStorage, type MockModerate, type MockModerationInput, type MockModerationTrigger, type MockRequestFailure, type MockReviewDecision, type MockStoredContent, type MockUploadPreset, signUploadTokenWithApiKey as signUploadToken };