@calmlens/sdk 0.10.1 → 0.12.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.
- package/README.md +60 -16
- package/cjs/CalmLensConnection-CYtBP-vu.js +516 -0
- package/cjs/CalmLensError-CKwMKV0S.js +362 -0
- package/cjs/CalmLensUtils-Dg3s4loH.js +367 -0
- package/cjs/CalmLensWebhooks-BTPxKvcD.js +201 -0
- package/cjs/{CalmLensTypes-DjYFaTyh.d.ts → WebhookPayload-Cgb32aSH.d.ts} +5423 -3844
- package/cjs/app.d.ts +43 -0
- package/cjs/app.js +91 -0
- package/cjs/index-BgmWj3KL.d.ts +350 -0
- package/cjs/index.d.ts +3 -382
- package/cjs/index.js +5 -816
- package/cjs/server.d.ts +202 -0
- package/cjs/server.js +371 -0
- package/cjs/src-Dsc8Pn6v.js +45 -0
- package/cjs/testing.d.ts +54 -18
- package/cjs/testing.js +191 -62
- package/esm/CalmLensConnection-wLNy0OIP.js +475 -0
- package/esm/CalmLensError-CihGG26K.js +321 -0
- package/esm/CalmLensUtils-Cx32j2zt.js +260 -0
- package/esm/CalmLensWebhooks-DShmAal4.js +148 -0
- package/esm/{CalmLensTypes-DjYFaTyh.d.ts → WebhookPayload-Cgb32aSH.d.ts} +5423 -3844
- package/esm/app.d.ts +43 -0
- package/esm/app.js +87 -0
- package/esm/index-BgmWj3KL.d.ts +350 -0
- package/esm/index.d.ts +3 -382
- package/esm/index.js +3 -807
- package/esm/server.d.ts +202 -0
- package/esm/server.js +361 -0
- package/esm/src-CJhrILvM.js +22 -0
- package/esm/testing.d.ts +54 -18
- package/esm/testing.js +142 -13
- package/package.json +23 -1
- package/cjs/CalmLensWebhooks-DJnaLpRy.js +0 -741
- package/esm/CalmLensWebhooks-DLUozliX.js +0 -556
package/esm/testing.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
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, U as ListAssetsOptions, W as ListEventsOptions, Z as RetrieveAssetOptions, _t as TypedEvent, at as UpdateFolderOptions, d as Asset, et as SignUploadTokenOptions, ft as GetEventsResponse, g as AssetSearchResponse, h as AssetPage, ht as Event, it as UpdateAssetOptions, j as DeleteAssetOptions, kt as AssetBinaryPayloadInput, mt as Folder, ot as UploadAssetPartOptions, p as AssetChildrenPage, pt as PostAssetSignedUrlResponse, q as MoveFolderOptions, r as ClassificationCategory, rt as SubmitAssetOptions, t as WebhookEventPayload, tt as SignedUploadToken, v as AssetStatus, w as CdnServeOptions, z as IterateAssetsOptions } from "./WebhookPayload-Cgb32aSH.js";
|
|
2
2
|
import * as zod from "zod";
|
|
3
3
|
//#region ../shared/src/TestTriggers.d.ts
|
|
4
4
|
type ClassificationTestSignal = ClassificationCategory | "sexual.involving-minors" | "violence.graphic";
|
|
@@ -34,6 +34,8 @@ declare const GRANT_SCHEMA: zod.ZodObject<{
|
|
|
34
34
|
"project:manage": "project:manage";
|
|
35
35
|
"settings:read": "settings:read";
|
|
36
36
|
"settings:write": "settings:write";
|
|
37
|
+
"upload-presets:read": "upload-presets:read";
|
|
38
|
+
"upload-presets:write": "upload-presets:write";
|
|
37
39
|
}>>;
|
|
38
40
|
folderId: zod.ZodNullable<zod.ZodString>;
|
|
39
41
|
includeSubfolders: zod.ZodBoolean;
|
|
@@ -102,8 +104,8 @@ export declare class InMemoryMockDatabaseStorage implements MockDatabaseStorage
|
|
|
102
104
|
deleteFolder(folderId: string): Promise<void>;
|
|
103
105
|
}
|
|
104
106
|
//#endregion
|
|
105
|
-
//#region src/testing/
|
|
106
|
-
interface
|
|
107
|
+
//#region src/testing/MockCalmLensServerClient.d.ts
|
|
108
|
+
interface MockCalmLensServerClientOptions extends CalmLensServerClientOptions {
|
|
107
109
|
mockOptions?: {
|
|
108
110
|
databaseStorage?: MockDatabaseStorage;
|
|
109
111
|
imageStorage?: MockImageStorage;
|
|
@@ -116,13 +118,44 @@ interface MockCalmLensClientOptions extends CalmLensClientOptions {
|
|
|
116
118
|
signedUrlBaseUrl?: string;
|
|
117
119
|
};
|
|
118
120
|
}
|
|
119
|
-
/**
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
/** The options each `MockCalmLensServerClient` method was given, by operation name. */
|
|
122
|
+
interface MockCalmLensOperationOptions {
|
|
123
|
+
"assets.list": ListAssetsOptions;
|
|
124
|
+
"assets.search": SearchAssetsOptions;
|
|
125
|
+
"assets.retrieve": RetrieveAssetOptions;
|
|
126
|
+
"assets.update": UpdateAssetOptions;
|
|
127
|
+
"assets.listChildren": ListAssetChildrenOptions;
|
|
128
|
+
"assets.create": SubmitAssetOptions;
|
|
129
|
+
"assets.moderate": SubmitAssetOptions;
|
|
130
|
+
"assets.uploadPart": UploadAssetPartOptions;
|
|
131
|
+
"assets.createSignedUrl": CreateAssetSignedUrlOptions;
|
|
132
|
+
"assets.move": MoveAssetOptions;
|
|
133
|
+
"assets.retry": RetryAssetOptions;
|
|
134
|
+
"assets.deleteContent": DeleteAssetContentOptions;
|
|
135
|
+
"assets.delete": DeleteAssetOptions;
|
|
136
|
+
"text.moderate": ModerateTextOptions;
|
|
137
|
+
"cdn.serve": CdnServeOptions;
|
|
138
|
+
"events.list": ListEventsOptions;
|
|
139
|
+
"folders.list": undefined;
|
|
140
|
+
"folders.retrieve": FolderIdOptions;
|
|
141
|
+
"folders.create": CreateFolderOptions;
|
|
142
|
+
"folders.update": UpdateFolderOptions;
|
|
143
|
+
"folders.delete": FolderIdOptions;
|
|
144
|
+
"folders.move": MoveFolderOptions;
|
|
145
|
+
ensureLens: EnsureLensOptions;
|
|
146
|
+
ensureLenses: EnsureLensesOptions;
|
|
147
|
+
"uploadTokens.sign": SignUploadTokenOptions;
|
|
125
148
|
}
|
|
149
|
+
/** A `MockCalmLensServerClient` method, named like its path on the client. */
|
|
150
|
+
type MockCalmLensOperation = keyof MockCalmLensOperationOptions;
|
|
151
|
+
/**
|
|
152
|
+
* One recorded call: the method and the options object it was given, exactly as passed. Narrow
|
|
153
|
+
* on `operation` (or use `callsTo`) to get its options type.
|
|
154
|
+
*/
|
|
155
|
+
type MockCalmLensCall<TOperation extends MockCalmLensOperation = MockCalmLensOperation> = { [Operation in TOperation]: {
|
|
156
|
+
operation: Operation;
|
|
157
|
+
options: MockCalmLensOperationOptions[Operation];
|
|
158
|
+
}; }[TOperation];
|
|
126
159
|
/**
|
|
127
160
|
* What a simulated request failure throws: `"transport-error"` and `"timeout"` throw the same
|
|
128
161
|
* `CalmLensError` the real client throws for a network failure (`sdk/transport-error`) or a
|
|
@@ -134,11 +167,11 @@ type MockEventInput = Partial<Omit<TypedEvent, "type">> & {
|
|
|
134
167
|
type: TypedEvent["type"];
|
|
135
168
|
};
|
|
136
169
|
/**
|
|
137
|
-
* An in-memory stand-in for `
|
|
170
|
+
* An in-memory stand-in for `CalmLensServerClient` with the same methods, validation and error
|
|
138
171
|
* codes. API errors carry the HTTP status, a `requestId` and response headers like the real
|
|
139
172
|
* client's, so error handling can be tested without a network.
|
|
140
173
|
*/
|
|
141
|
-
export declare class
|
|
174
|
+
export declare class MockCalmLensServerClient {
|
|
142
175
|
readonly databaseStorage: MockDatabaseStorage;
|
|
143
176
|
readonly imageStorage: MockImageStorage;
|
|
144
177
|
private readonly createId;
|
|
@@ -149,6 +182,7 @@ export declare class MockCalmLensClient {
|
|
|
149
182
|
private readonly signedUrlBaseUrl;
|
|
150
183
|
private readonly cdnBaseUrl;
|
|
151
184
|
private readonly projectId;
|
|
185
|
+
private readonly apiKey;
|
|
152
186
|
private readonly lenses;
|
|
153
187
|
private readonly eventFeed;
|
|
154
188
|
/** The queue mode each open multipart upload was started with, as the API saves it. */
|
|
@@ -158,8 +192,8 @@ export declare class MockCalmLensClient {
|
|
|
158
192
|
private readonly pendingFailures;
|
|
159
193
|
/** Every call made through this client, oldest first. */
|
|
160
194
|
readonly calls: MockCalmLensCall[];
|
|
161
|
-
/** Accepts the same options as `
|
|
162
|
-
constructor(options:
|
|
195
|
+
/** Accepts the same options as `CalmLensServerClient` (validated the same way), plus `mockOptions`. */
|
|
196
|
+
constructor(options: MockCalmLensServerClientOptions);
|
|
163
197
|
/**
|
|
164
198
|
* Makes the next call (or the next call to `operation`) throw `failure` instead of running,
|
|
165
199
|
* as if the request never completed. Queue several to fail several calls in a row.
|
|
@@ -168,7 +202,7 @@ export declare class MockCalmLensClient {
|
|
|
168
202
|
operation?: MockCalmLensOperation;
|
|
169
203
|
}): void;
|
|
170
204
|
/** Calls recorded for one operation, oldest first. */
|
|
171
|
-
callsTo(operation:
|
|
205
|
+
callsTo<TOperation extends MockCalmLensOperation>(operation: TOperation): MockCalmLensCall<TOperation>[];
|
|
172
206
|
/**
|
|
173
207
|
* Adds an event to the feed `events.list` reads, as if CalmLens had recorded it. Returns the
|
|
174
208
|
* stored event; pass it to `toWebhookEventPayload` to build a matching webhook body.
|
|
@@ -211,9 +245,7 @@ export declare class MockCalmLensClient {
|
|
|
211
245
|
* description and tags, best match first. Mock scores are 1 for an exact match and 0.5
|
|
212
246
|
* otherwise; `mode` is accepted but there is no semantic search.
|
|
213
247
|
*/
|
|
214
|
-
search: (options:
|
|
215
|
-
query: string;
|
|
216
|
-
}) => Promise<AssetSearchResponse>;
|
|
248
|
+
search: (options: SearchAssetsOptions) => Promise<AssetSearchResponse>;
|
|
217
249
|
/** Lists assets with the real client's paging, `ordering`, `folderId` and `filter`. */
|
|
218
250
|
list: (options?: ListAssetsOptions) => Promise<AssetPage>;
|
|
219
251
|
iterate: (options?: IterateAssetsOptions) => AsyncGenerator<Asset, void, undefined>;
|
|
@@ -231,6 +263,10 @@ export declare class MockCalmLensClient {
|
|
|
231
263
|
deleteContent: (options: DeleteAssetContentOptions) => Promise<Asset>;
|
|
232
264
|
delete: (options: DeleteAssetOptions) => Promise<void>;
|
|
233
265
|
};
|
|
266
|
+
/** Signs real upload tokens with the mock's API key, like the real client, without a request. */
|
|
267
|
+
get uploadTokens(): {
|
|
268
|
+
sign: (options: SignUploadTokenOptions) => Promise<SignedUploadToken>;
|
|
269
|
+
};
|
|
234
270
|
get folders(): {
|
|
235
271
|
list: () => Promise<Folder[]>;
|
|
236
272
|
retrieve: (options: FolderIdOptions) => Promise<Folder>;
|
|
@@ -265,4 +301,4 @@ export declare const createWebhookHeaders: (options: {
|
|
|
265
301
|
timestampSeconds?: number;
|
|
266
302
|
}) => Promise<Record<string, string>>;
|
|
267
303
|
//#endregion
|
|
268
|
-
export type { MockCalmLensCall,
|
|
304
|
+
export type { MockCalmLensCall, MockCalmLensOperation, MockCalmLensOperationOptions, MockCalmLensServerClientOptions, MockDatabaseStorage, MockEventInput, MockImageStorage, MockRequestFailure, MockStoredContent };
|
package/esm/testing.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as invalidArgument, n as CalmLensError, o as API_ERROR_CODES, r as SDK_ERROR_CODES, s as API_ERROR_DEFINITIONS } from "./CalmLensError-CihGG26K.js";
|
|
2
|
+
import { c as hmacSha256Hex, i as WEBHOOK_TIMESTAMP_HEADER, l as stringifyJsonCanonical, n as WEBHOOK_SIGNATURE_HEADER, s as signUploadTokenWithApiKey, t as WEBHOOK_DELIVERY_HEADER } from "./CalmLensWebhooks-DShmAal4.js";
|
|
3
|
+
import { _ as getRejectedUploadMimeTypeError, a as toTextAssetOptions, d as assetToData, f as getAssetPayloadByteLength, l as validateServerClientOptions, n as encodePathSegment, o as transformSelector, p as validateAssetPayload, t as appendCdnSelector, u as assetDataToUint8Array, v as isRejectedUploadMimeType } from "./CalmLensUtils-Cx32j2zt.js";
|
|
2
4
|
import * as zod from "zod";
|
|
3
5
|
import "zod/v4";
|
|
4
6
|
//#region ../shared/src/ModerationCategoryDefinitions.ts
|
|
@@ -88,6 +90,7 @@ const SHARED_QUALIFIERS = [
|
|
|
88
90
|
[...SHARED_QUALIFIERS], [...SHARED_QUALIFIERS], [...SHARED_QUALIFIERS], [...SHARED_QUALIFIERS], [...SHARED_QUALIFIERS], [...SHARED_QUALIFIERS];
|
|
89
91
|
//#endregion
|
|
90
92
|
//#region ../../node_modules/zod-meta/esm/index.js
|
|
93
|
+
const ZOD_META_KEY = "__zod_meta__";
|
|
91
94
|
const createMetaType = (type) => {
|
|
92
95
|
return Object.assign((value) => {
|
|
93
96
|
return {
|
|
@@ -96,7 +99,117 @@ const createMetaType = (type) => {
|
|
|
96
99
|
};
|
|
97
100
|
}, type);
|
|
98
101
|
};
|
|
99
|
-
|
|
102
|
+
const metaStore = (meta) => {
|
|
103
|
+
const metaMap = meta.reduce((acc, meta) => {
|
|
104
|
+
if (meta) acc[meta.type.id] = meta;
|
|
105
|
+
return acc;
|
|
106
|
+
}, {});
|
|
107
|
+
return { [ZOD_META_KEY]: {
|
|
108
|
+
itemMap: metaMap,
|
|
109
|
+
get itemList() {
|
|
110
|
+
return Object.values(metaMap);
|
|
111
|
+
}
|
|
112
|
+
} };
|
|
113
|
+
};
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region ../shared/src/DocMetaTypes.ts
|
|
116
|
+
const docPropertyInfo = createMetaType({ id: "docPropertyInfo" });
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region ../shared/src/ProcessingFeatures.ts
|
|
119
|
+
/**
|
|
120
|
+
* Optional analyses a request can turn on with `processingOptions.features.<id>`. The catalog is
|
|
121
|
+
* the single source for the request and result schemas, cache keys and metering. Features are
|
|
122
|
+
* chosen per request only; there is no project-level default.
|
|
123
|
+
*/
|
|
124
|
+
/** How mature a feature is. Beta results may change shape. */
|
|
125
|
+
const PROCESSING_FEATURE_STAGES = ["beta", "ga"];
|
|
126
|
+
const createFeatureOptionsSchemas = (booleanSchema) => {
|
|
127
|
+
const enabled = booleanSchema.meta(metaStore([docPropertyInfo({ description: "Whether to run the feature. Every feature is off unless this is true." })]));
|
|
128
|
+
return {
|
|
129
|
+
ocr: zod.object({ enabled }).strict().meta(metaStore([docPropertyInfo({
|
|
130
|
+
description: "Extract image text into a child asset and run normal text moderation. Applies to images, including those extracted from multimedia documents and websites.",
|
|
131
|
+
namedType: "OcrFeatureOptions"
|
|
132
|
+
})])),
|
|
133
|
+
personDetection: zod.object({ enabled }).strict().meta(metaStore([docPropertyInfo({
|
|
134
|
+
description: "Beta. Locate people in images, including people whose faces are not visible. Results are informational and never change the moderation decision.",
|
|
135
|
+
namedType: "PersonDetectionFeatureOptions"
|
|
136
|
+
})])),
|
|
137
|
+
faceDetection: zod.object({
|
|
138
|
+
enabled,
|
|
139
|
+
ageEstimation: booleanSchema.optional().meta(metaStore([docPropertyInfo({
|
|
140
|
+
description: "Also estimate an age range for each face",
|
|
141
|
+
defaultValue: false
|
|
142
|
+
})]))
|
|
143
|
+
}).strict().meta(metaStore([docPropertyInfo({
|
|
144
|
+
description: "Beta. Locate faces in images and optionally estimate an age range for each. Faces are not identified or stored as biometric templates. Results are informational and never change the moderation decision.",
|
|
145
|
+
namedType: "FaceDetectionFeatureOptions"
|
|
146
|
+
})]))
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
const createProcessingFeaturesSchema = (booleanSchema) => {
|
|
150
|
+
const options = createFeatureOptionsSchemas(booleanSchema);
|
|
151
|
+
return zod.object({
|
|
152
|
+
ocr: options.ocr.nullish(),
|
|
153
|
+
personDetection: options.personDetection.nullish(),
|
|
154
|
+
faceDetection: options.faceDetection.nullish()
|
|
155
|
+
}).strict().meta(metaStore([docPropertyInfo({
|
|
156
|
+
description: "Optional analyses to run on this asset, keyed by feature. Unknown features are rejected.",
|
|
157
|
+
namedType: "ProcessingFeatures"
|
|
158
|
+
})]));
|
|
159
|
+
};
|
|
160
|
+
createProcessingFeaturesSchema(zod.boolean());
|
|
161
|
+
/** Position and size as fractions of the image width and height, from 0 to 1. */
|
|
162
|
+
const BOUNDING_BOX_SCHEMA = zod.object({
|
|
163
|
+
left: zod.number(),
|
|
164
|
+
top: zod.number(),
|
|
165
|
+
width: zod.number().min(0),
|
|
166
|
+
height: zod.number().min(0)
|
|
167
|
+
}).strict().meta(metaStore([docPropertyInfo({
|
|
168
|
+
description: "Position and size as fractions of the image width and height, from 0 to 1. `left` and `top` can be slightly negative when the subject extends past the edge.",
|
|
169
|
+
namedType: "BoundingBox"
|
|
170
|
+
})]));
|
|
171
|
+
const DETECTION_CONFIDENCE_SCHEMA = zod.number().min(0).max(1).describe("Detection confidence from 0 to 1");
|
|
172
|
+
const AGE_RANGE_SCHEMA = zod.object({
|
|
173
|
+
low: zod.number().int().nonnegative(),
|
|
174
|
+
high: zod.number().int().nonnegative()
|
|
175
|
+
}).strict().meta(metaStore([docPropertyInfo({
|
|
176
|
+
description: "Estimated age range in years. Estimates can be off by several years.",
|
|
177
|
+
namedType: "AgeRange"
|
|
178
|
+
})]));
|
|
179
|
+
const DETECTED_PERSON_SCHEMA = zod.object({
|
|
180
|
+
box: BOUNDING_BOX_SCHEMA,
|
|
181
|
+
confidence: DETECTION_CONFIDENCE_SCHEMA
|
|
182
|
+
}).strict().meta(metaStore([docPropertyInfo({
|
|
183
|
+
description: "A detected person",
|
|
184
|
+
namedType: "DetectedPerson"
|
|
185
|
+
})]));
|
|
186
|
+
const DETECTED_FACE_SCHEMA = zod.object({
|
|
187
|
+
box: BOUNDING_BOX_SCHEMA,
|
|
188
|
+
confidence: DETECTION_CONFIDENCE_SCHEMA,
|
|
189
|
+
ageRange: AGE_RANGE_SCHEMA.optional()
|
|
190
|
+
}).strict().meta(metaStore([docPropertyInfo({
|
|
191
|
+
description: "A detected face",
|
|
192
|
+
namedType: "DetectedFace"
|
|
193
|
+
})]));
|
|
194
|
+
const NOT_EVALUATED_FEATURE_RESULT_SCHEMA = zod.object({
|
|
195
|
+
status: zod.literal("not-evaluated"),
|
|
196
|
+
stage: zod.enum(PROCESSING_FEATURE_STAGES),
|
|
197
|
+
reason: zod.enum(["not-supported", "unavailable"])
|
|
198
|
+
}).strict();
|
|
199
|
+
const createFeatureResultSchema = (shape) => zod.discriminatedUnion("status", [zod.object({
|
|
200
|
+
status: zod.literal("evaluated"),
|
|
201
|
+
stage: zod.enum(PROCESSING_FEATURE_STAGES),
|
|
202
|
+
...shape
|
|
203
|
+
}).strict(), NOT_EVALUATED_FEATURE_RESULT_SCHEMA]);
|
|
204
|
+
const PERSON_DETECTION_RESULT_SCHEMA = createFeatureResultSchema({ people: zod.array(DETECTED_PERSON_SCHEMA) });
|
|
205
|
+
const FACE_DETECTION_RESULT_SCHEMA = createFeatureResultSchema({ faces: zod.array(DETECTED_FACE_SCHEMA) });
|
|
206
|
+
const PROCESSING_FEATURE_RESULTS_SCHEMA = zod.object({
|
|
207
|
+
personDetection: PERSON_DETECTION_RESULT_SCHEMA.optional(),
|
|
208
|
+
faceDetection: FACE_DETECTION_RESULT_SCHEMA.optional()
|
|
209
|
+
}).strict().meta(metaStore([docPropertyInfo({
|
|
210
|
+
description: "Results of the optional features requested in `processingOptions.features`, keyed by feature",
|
|
211
|
+
namedType: "ProcessingFeatureResults"
|
|
212
|
+
})]));
|
|
100
213
|
//#endregion
|
|
101
214
|
//#region ../shared/src/ZodUtils.ts
|
|
102
215
|
const SIZE_LIMITS = {
|
|
@@ -209,7 +322,8 @@ zod.object({
|
|
|
209
322
|
categories: CATEGORIES_SCHEMA
|
|
210
323
|
}).strict())
|
|
211
324
|
}).strict().optional(),
|
|
212
|
-
labels: zod.array(CLASSIFICATION_LABEL_SCHEMA).nullish()
|
|
325
|
+
labels: zod.array(CLASSIFICATION_LABEL_SCHEMA).nullish(),
|
|
326
|
+
features: PROCESSING_FEATURE_RESULTS_SCHEMA.optional()
|
|
213
327
|
}).strict();
|
|
214
328
|
//#endregion
|
|
215
329
|
//#region ../shared/src/MediaTestTriggers.ts
|
|
@@ -264,6 +378,8 @@ const ACTION_SCHEMA = zod.enum([
|
|
|
264
378
|
"metadata:write",
|
|
265
379
|
"lenses:read",
|
|
266
380
|
"lenses:write",
|
|
381
|
+
"upload-presets:read",
|
|
382
|
+
"upload-presets:write",
|
|
267
383
|
"project:manage"
|
|
268
384
|
]);
|
|
269
385
|
const PROJECT_ACTIONS = [...[
|
|
@@ -272,7 +388,8 @@ const PROJECT_ACTIONS = [...[
|
|
|
272
388
|
"api-keys",
|
|
273
389
|
"connectors",
|
|
274
390
|
"metadata",
|
|
275
|
-
"lenses"
|
|
391
|
+
"lenses",
|
|
392
|
+
"upload-presets"
|
|
276
393
|
].flatMap((feature) => [`${feature}:read`, `${feature}:write`]), "project:manage"];
|
|
277
394
|
const isProjectAction = (action) => PROJECT_ACTIONS.includes(action);
|
|
278
395
|
/** The actions that include `action`: its feature's write action and project:manage. */
|
|
@@ -475,7 +592,7 @@ const parseFilterValue = (raw) => {
|
|
|
475
592
|
return value;
|
|
476
593
|
};
|
|
477
594
|
const readField = (item, path) => path.split(".").reduce((value, key) => value && typeof value === "object" ? value[key] : void 0, item);
|
|
478
|
-
const unsupportedFilter = (expression) => invalidArgument(`
|
|
595
|
+
const unsupportedFilter = (expression) => invalidArgument(`MockCalmLensServerClient cannot evaluate the filter "${expression}". It supports ==, !=, <, <=, >, >=, =in= and =out=, joined by ; (and) or , (or), without grouping parentheses.`);
|
|
479
596
|
const matchesComparison = (item, comparison) => {
|
|
480
597
|
const match = COMPARISON.exec(comparison.trim());
|
|
481
598
|
if (!match) throw unsupportedFilter(comparison);
|
|
@@ -532,7 +649,7 @@ const filterAssets = (items, filter) => {
|
|
|
532
649
|
return items.filter((item) => alternatives.some((conditions) => conditions.every((condition) => matchesComparison(item, condition))));
|
|
533
650
|
};
|
|
534
651
|
//#endregion
|
|
535
|
-
//#region src/testing/
|
|
652
|
+
//#region src/testing/MockCalmLensServerClient.ts
|
|
536
653
|
const DEFAULT_EVENTS_LIMIT = 50;
|
|
537
654
|
/** Stands in for binary payloads when comparing idempotent requests, like the API's size check. */
|
|
538
655
|
const describeBinary = (value) => {
|
|
@@ -558,12 +675,12 @@ const toFailureError = (failure) => {
|
|
|
558
675
|
return failure;
|
|
559
676
|
};
|
|
560
677
|
/**
|
|
561
|
-
* An in-memory stand-in for `
|
|
678
|
+
* An in-memory stand-in for `CalmLensServerClient` with the same methods, validation and error
|
|
562
679
|
* codes. API errors carry the HTTP status, a `requestId` and response headers like the real
|
|
563
680
|
* client's, so error handling can be tested without a network.
|
|
564
681
|
*/
|
|
565
|
-
var
|
|
566
|
-
/** Accepts the same options as `
|
|
682
|
+
var MockCalmLensServerClient = class {
|
|
683
|
+
/** Accepts the same options as `CalmLensServerClient` (validated the same way), plus `mockOptions`. */
|
|
567
684
|
constructor(options) {
|
|
568
685
|
this.lenses = /* @__PURE__ */ new Map();
|
|
569
686
|
this.eventFeed = [];
|
|
@@ -572,8 +689,9 @@ var MockCalmLensClient = class {
|
|
|
572
689
|
this.requestCount = 0;
|
|
573
690
|
this.pendingFailures = [];
|
|
574
691
|
this.calls = [];
|
|
575
|
-
|
|
692
|
+
validateServerClientOptions(options);
|
|
576
693
|
this.projectId = options.projectId;
|
|
694
|
+
this.apiKey = options.apiKey;
|
|
577
695
|
this.databaseStorage = options.mockOptions?.databaseStorage ?? new InMemoryMockDatabaseStorage();
|
|
578
696
|
this.imageStorage = options.mockOptions?.imageStorage ?? new InMemoryMockImageStorage();
|
|
579
697
|
this.createId = options.mockOptions?.createId ?? crypto.randomUUID.bind(crypto);
|
|
@@ -586,7 +704,7 @@ var MockCalmLensClient = class {
|
|
|
586
704
|
}
|
|
587
705
|
/** Keeps the API key out of `console.log` and `util.inspect`. */
|
|
588
706
|
[Symbol.for("nodejs.util.inspect.custom")]() {
|
|
589
|
-
return `
|
|
707
|
+
return `MockCalmLensServerClient { projectId: ${JSON.stringify(this.projectId)} }`;
|
|
590
708
|
}
|
|
591
709
|
/**
|
|
592
710
|
* Makes the next call (or the next call to `operation`) throw `failure` instead of running,
|
|
@@ -912,7 +1030,8 @@ var MockCalmLensClient = class {
|
|
|
912
1030
|
if (options.lens && options.transform) throw invalidArgument("Specify either a lens or a transform, not both");
|
|
913
1031
|
const expiresInSeconds = options.expiresInSeconds ?? 900;
|
|
914
1032
|
const resource = options.resource ?? "content";
|
|
915
|
-
const
|
|
1033
|
+
const transform = transformSelector(options.transform);
|
|
1034
|
+
const selector = options.lens ? `&lens=${encodeURIComponent(options.lens)}` : transform ? `&transform=${encodeURIComponent(transform)}` : "";
|
|
916
1035
|
return {
|
|
917
1036
|
url: `${this.signedUrlBaseUrl}/assets/${asset.id}/${resource}?expiresInSeconds=${expiresInSeconds}${selector}`,
|
|
918
1037
|
expiresAt: this.now() + expiresInSeconds * 1e3
|
|
@@ -961,6 +1080,16 @@ var MockCalmLensClient = class {
|
|
|
961
1080
|
}
|
|
962
1081
|
};
|
|
963
1082
|
}
|
|
1083
|
+
/** Signs real upload tokens with the mock's API key, like the real client, without a request. */
|
|
1084
|
+
get uploadTokens() {
|
|
1085
|
+
return { sign: async (options) => {
|
|
1086
|
+
this.begin("uploadTokens.sign", options);
|
|
1087
|
+
return signUploadTokenWithApiKey(this.apiKey, options, {
|
|
1088
|
+
now: this.now(),
|
|
1089
|
+
nonce: this.createId()
|
|
1090
|
+
});
|
|
1091
|
+
} };
|
|
1092
|
+
}
|
|
964
1093
|
get folders() {
|
|
965
1094
|
return {
|
|
966
1095
|
list: async () => {
|
|
@@ -1203,4 +1332,4 @@ const createWebhookHeaders = async (options) => {
|
|
|
1203
1332
|
};
|
|
1204
1333
|
};
|
|
1205
1334
|
//#endregion
|
|
1206
|
-
export { InMemoryMockDatabaseStorage, InMemoryMockImageStorage, MEDIA_TEST_SIGNALS, MOCK_API_KEY_PREFIX,
|
|
1335
|
+
export { InMemoryMockDatabaseStorage, InMemoryMockImageStorage, MEDIA_TEST_SIGNALS, MOCK_API_KEY_PREFIX, MockCalmLensServerClient, createMockApiKey, createWebhookHeaders, createWebhookSignatureHeader, generateMediaTestCode, toWebhookEventPayload };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@calmlens/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Official CalmLens SDK for JavaScript and TypeScript: content moderation, asset storage and CDN delivery.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://calmlens.com/docs/sdk/javascript",
|
|
@@ -72,6 +72,28 @@
|
|
|
72
72
|
},
|
|
73
73
|
"default": "./esm/index.js"
|
|
74
74
|
},
|
|
75
|
+
"./server": {
|
|
76
|
+
"import": {
|
|
77
|
+
"types": "./esm/server.d.ts",
|
|
78
|
+
"default": "./esm/server.js"
|
|
79
|
+
},
|
|
80
|
+
"require": {
|
|
81
|
+
"types": "./cjs/server.d.ts",
|
|
82
|
+
"default": "./cjs/server.js"
|
|
83
|
+
},
|
|
84
|
+
"default": "./esm/server.js"
|
|
85
|
+
},
|
|
86
|
+
"./app": {
|
|
87
|
+
"import": {
|
|
88
|
+
"types": "./esm/app.d.ts",
|
|
89
|
+
"default": "./esm/app.js"
|
|
90
|
+
},
|
|
91
|
+
"require": {
|
|
92
|
+
"types": "./cjs/app.d.ts",
|
|
93
|
+
"default": "./cjs/app.js"
|
|
94
|
+
},
|
|
95
|
+
"default": "./esm/app.js"
|
|
96
|
+
},
|
|
75
97
|
"./testing": {
|
|
76
98
|
"import": {
|
|
77
99
|
"types": "./esm/testing.d.ts",
|