@calmlens/sdk 0.13.0 → 0.15.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 +44 -40
- package/cjs/{CalmLensConnection-DzRDCMOP.js → CalmLensConnection-DoemramF.js} +4 -3
- package/cjs/{CalmLensUtils-vX5H_vMn.js → CalmLensUtils-Cv1Ylp9-.js} +10 -10
- package/cjs/{CalmLensVersion-DjhsJ1gj.js → CalmLensVersion-CuxKdijk.js} +67 -2
- package/cjs/{CalmLensWebhooks-hVquX5L1.js → CalmLensWebhooks-qrL-Zdu3.js} +2 -2
- package/cjs/{WebhookPayload-B7CgSmI5.d.ts → WebhookPayload-vy8HNMaK.d.ts} +4355 -449
- package/cjs/app.d.ts +3 -3
- package/cjs/app.js +3 -3
- package/cjs/{index-BywTQbRq.d.ts → index-Dz340UQ9.d.ts} +27 -1
- package/cjs/index.d.ts +3 -3
- package/cjs/index.js +1 -1
- package/cjs/server.d.ts +9 -8
- package/cjs/server.js +10 -9
- package/cjs/testing.d.ts +19 -45
- package/cjs/testing.js +99 -30
- package/esm/{CalmLensConnection-D8VsTdsA.js → CalmLensConnection-CQLoUoBd.js} +4 -3
- package/esm/{CalmLensUtils-B4i3Z2He.js → CalmLensUtils-B3S--hBG.js} +10 -10
- package/esm/{CalmLensVersion-DK4BMEST.js → CalmLensVersion-N71PMJAz.js} +67 -2
- package/esm/{CalmLensWebhooks-DyCoEvpf.js → CalmLensWebhooks-BxMSx1Vm.js} +2 -2
- package/esm/{WebhookPayload-B7CgSmI5.d.ts → WebhookPayload-vy8HNMaK.d.ts} +4355 -449
- package/esm/app.d.ts +3 -3
- package/esm/app.js +3 -3
- package/esm/{index-BywTQbRq.d.ts → index-Dz340UQ9.d.ts} +27 -1
- package/esm/index.d.ts +3 -3
- package/esm/index.js +1 -1
- package/esm/server.d.ts +9 -8
- package/esm/server.js +10 -9
- package/esm/testing.d.ts +19 -45
- package/esm/testing.js +99 -30
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,8 +38,8 @@ const client = new CalmLensServerClient({
|
|
|
38
38
|
// Text (comments, captions, bios): waits for the decision
|
|
39
39
|
const comment = await client.text.moderate({ text: "Great photo!", externalId: "comment-42" });
|
|
40
40
|
|
|
41
|
-
if (comment.status === "approved") {
|
|
42
|
-
// publish it; if comment.
|
|
41
|
+
if (comment.moderation.status === "approved") {
|
|
42
|
+
// publish it; if comment.moderation.action is "alert", also queue it for review
|
|
43
43
|
} else {
|
|
44
44
|
// "rejected": don't publish it
|
|
45
45
|
}
|
|
@@ -50,16 +50,19 @@ const image = await client.assets.create({
|
|
|
50
50
|
content: { data: file, type: "image/jpeg" },
|
|
51
51
|
externalId: "post-7",
|
|
52
52
|
});
|
|
53
|
-
console.log(image.status); // "pending"
|
|
53
|
+
console.log(image.processing.status, image.moderation.status); // "processing" "pending"
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
| `status` | `
|
|
57
|
-
| --- | --- | --- |
|
|
58
|
-
| `approved` | `
|
|
59
|
-
| `approved` | `
|
|
60
|
-
| `rejected` | `
|
|
61
|
-
| `pending` |
|
|
62
|
-
| `
|
|
56
|
+
| `processing.status` | `moderation.status` | `moderation.action` | Meaning |
|
|
57
|
+
| --- | --- | --- | --- |
|
|
58
|
+
| `done` | `approved` | `null` | Safe to show. |
|
|
59
|
+
| `done` | `approved` | `alert` | Show it, but your policy asked to be alerted. |
|
|
60
|
+
| `done` | `rejected` | `hide` or `purge` | Your policy hid or purged it. Don't show it. |
|
|
61
|
+
| `uploading` or `processing` | `pending` | `null` | Still being received or classified. |
|
|
62
|
+
| `failed` | `pending` | `null` | Processing failed; see `assets.retry`. |
|
|
63
|
+
|
|
64
|
+
A reviewer's decision (`moderation.reviewerAction`) takes precedence over the automated one in
|
|
65
|
+
`moderation.status`; `moderation.action` keeps what your policy did.
|
|
63
66
|
|
|
64
67
|
See [How CalmLens works](https://calmlens.com/docs/how-it-works) and [Testing your integration](https://calmlens.com/docs/guides/testing) for test inputs that trigger rejections safely.
|
|
65
68
|
|
|
@@ -146,16 +149,18 @@ Both resolve to the stored `Asset`, a plain object with camelCase keys exactly a
|
|
|
146
149
|
```typescript
|
|
147
150
|
const asset = await client.text.moderate({ text: "Great photo!", externalId: "comment-42" });
|
|
148
151
|
asset.id; // string
|
|
149
|
-
asset.status;
|
|
150
|
-
asset.
|
|
151
|
-
asset.
|
|
152
|
-
asset.report?.score; // number, the overall score
|
|
153
|
-
asset.report?.categories.harassment.status; // "evaluated" | "partial" | "not-evaluated"
|
|
154
|
-
asset.
|
|
155
|
-
asset.
|
|
152
|
+
asset.processing.status; // "uploading" | "processing" | "done" | "failed"; audio and video keep processing in the background
|
|
153
|
+
asset.moderation.status; // "pending" | "approved" | "rejected"
|
|
154
|
+
asset.moderation.action; // "alert" | "hide" | "purge", what your policy did with flagged content; null when nothing was flagged
|
|
155
|
+
asset.moderation.report?.score; // number, the overall score
|
|
156
|
+
asset.moderation.report?.categories.harassment.status; // "evaluated" | "partial" | "not-evaluated"
|
|
157
|
+
asset.content.status; // "temporary" | "stored" | "removed"
|
|
158
|
+
asset.content.removalReason; // "policy" | "retention" | "requested" | "parent-removed" | "disposed" | null
|
|
159
|
+
asset.type; // "plaintext-document" for text
|
|
160
|
+
asset.createdAt; // milliseconds since the Unix epoch
|
|
156
161
|
```
|
|
157
162
|
|
|
158
|
-
Always present: `id`, `projectId`, `name`, `type`, `
|
|
163
|
+
Always present: `id`, `projectId`, `name`, `type`, `createdAt`, `updatedAt`, `processing`, `moderation` and `content` (with their `status` fields). Other fields may be `null`: `moderation.report` until classification finishes (and when processing failed), `moderation.action` when nothing was flagged, the `moderation.reviewer*` fields until a reviewer decides, `externalId`, `description`, `metadata`, `folderId`, `visibility` and `processingOptions` when you did not set them, and `content.format`, `content.sizeInBytes`, `content.image`, `content.previewImage` and `content.extractedText` when they do not apply to the content. Content is `temporary` until processing finishes and then `removed` (reason `retention`) unless you set `processingOptions.keepContentAfterProcessing`. Inside a report, a category's `score` and `likelihood` exist only when its `status` is `evaluated` or `partial` (see [Moderation Reports](#moderation-reports)).
|
|
159
164
|
|
|
160
165
|
##### assets.retrieve(options)
|
|
161
166
|
|
|
@@ -168,8 +173,8 @@ async assets.retrieve(options: { assetId: string }): Promise<Asset>
|
|
|
168
173
|
**Example:**
|
|
169
174
|
```typescript
|
|
170
175
|
const asset = await client.assets.retrieve({ assetId: "asset-uuid-here" });
|
|
171
|
-
console.log(asset.status);
|
|
172
|
-
console.log(asset.report); // Classification results
|
|
176
|
+
console.log(asset.moderation.status);
|
|
177
|
+
console.log(asset.moderation.report); // Classification results
|
|
173
178
|
```
|
|
174
179
|
|
|
175
180
|
##### ensureLens(options)
|
|
@@ -233,14 +238,14 @@ const result = await client.assets.list({
|
|
|
233
238
|
|
|
234
239
|
console.log(`Found ${result.total} assets`);
|
|
235
240
|
result.items.forEach(asset => {
|
|
236
|
-
console.log(`${asset.name}: ${asset.status}`);
|
|
241
|
+
console.log(`${asset.name}: ${asset.moderation.status}`);
|
|
237
242
|
});
|
|
238
243
|
```
|
|
239
244
|
|
|
240
245
|
To read every matching asset, iterate instead of paging by hand. Pages are fetched as you go; assets created or deleted meanwhile can shift pages, so one may be skipped or repeated.
|
|
241
246
|
|
|
242
247
|
```typescript
|
|
243
|
-
for await (const asset of client.assets.iterate({ filter: "status==rejected", pageSize: 100 })) {
|
|
248
|
+
for await (const asset of client.assets.iterate({ filter: "moderation.status==rejected", pageSize: 100 })) {
|
|
244
249
|
console.log(asset.id);
|
|
245
250
|
}
|
|
246
251
|
```
|
|
@@ -294,26 +299,26 @@ await client.assets.move({
|
|
|
294
299
|
|
|
295
300
|
##### assets.retry(options)
|
|
296
301
|
|
|
297
|
-
Process an asset
|
|
298
|
-
new upload, so handle the result the same way (webhook or polling). Its content must still be stored:
|
|
302
|
+
Process an asset whose processing failed (`processing.status` `failed`) again. It returns to
|
|
303
|
+
`processing` and is moderated like a new upload, so handle the result the same way (webhook or polling). Its content must still be stored:
|
|
299
304
|
otherwise this throws `asset/content-unavailable`, and you need to upload it again.
|
|
300
305
|
|
|
301
306
|
```typescript
|
|
302
307
|
const asset = await client.assets.retry({ assetId: "asset-uuid" });
|
|
303
|
-
// asset.status === "
|
|
308
|
+
// asset.processing.status === "processing"
|
|
304
309
|
```
|
|
305
310
|
|
|
306
311
|
##### assets.waitForDecision(options)
|
|
307
312
|
|
|
308
|
-
Wait for an asset's moderation decision and get the decided asset:
|
|
309
|
-
The SDK checks the asset every few seconds and rejects with `sdk/timeout` if there's no decision within
|
|
313
|
+
Wait for an asset's moderation decision and get the decided asset: it returns once `moderation.status`
|
|
314
|
+
is `approved` or `rejected`, or `processing.status` is `failed`. The SDK checks the asset every few seconds and rejects with `sdk/timeout` if there's no decision within
|
|
310
315
|
`timeoutMs` (default 60000). Pass `signal` to stop waiting early (`sdk/cancelled`). Use it after
|
|
311
316
|
`assets.create`, or with an asset ID you stored earlier. Webhooks report decisions without polling.
|
|
312
317
|
|
|
313
318
|
```typescript
|
|
314
319
|
const queued = await client.assets.create({ name: "photo.jpg", content: { blob, type: "image/jpeg" } });
|
|
315
320
|
const decided = await client.assets.waitForDecision({ assetId: queued.id, timeoutMs: 30_000 });
|
|
316
|
-
if (decided.status === "rejected") hidePhoto(decided.id);
|
|
321
|
+
if (decided.moderation.status === "rejected") hidePhoto(decided.id);
|
|
317
322
|
```
|
|
318
323
|
|
|
319
324
|
##### folders.list()
|
|
@@ -461,7 +466,7 @@ const analyzed = await client.assets.moderate({
|
|
|
461
466
|
isFinal: true
|
|
462
467
|
}
|
|
463
468
|
});
|
|
464
|
-
console.log(analyzed.status);
|
|
469
|
+
console.log(analyzed.moderation.status);
|
|
465
470
|
```
|
|
466
471
|
|
|
467
472
|
#### Static Methods
|
|
@@ -499,7 +504,7 @@ const client = new CalmLensAppClient({
|
|
|
499
504
|
});
|
|
500
505
|
|
|
501
506
|
const asset = await client.assets.moderate({ name: file.name, content: { data: file, type: file.type } });
|
|
502
|
-
// asset.status is present when the preset shows decisions to uploaders
|
|
507
|
+
// asset.status (the moderation status) is present when the preset shows decisions to uploaders
|
|
503
508
|
```
|
|
504
509
|
|
|
505
510
|
**Options:** `uploadPresetId` (required), `token` (a string, or a function that returns one or a promise of one), plus `baseUrl`, `fetch`, `uploadFetch`, `timeoutMs`, `uploadTimeoutMs`, `maxRetries`, `maxResponseBytes` and `signal` as for `CalmLensServerClient`. Passing `apiKey` throws `sdk/invalid-config`.
|
|
@@ -533,12 +538,11 @@ Assets can be of the following types:
|
|
|
533
538
|
|
|
534
539
|
## Asset Status
|
|
535
540
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
- `
|
|
539
|
-
- `approved`
|
|
540
|
-
- `
|
|
541
|
-
- `error` - Processing failed
|
|
541
|
+
An asset's state is split in three:
|
|
542
|
+
|
|
543
|
+
- `processing.status`: `uploading` (a multipart upload is accepting more parts), `processing`, then `done` or `failed`
|
|
544
|
+
- `moderation.status`: `pending` until moderation finishes, then `approved` or `rejected`. A reviewer's decision (`moderation.reviewerAction`) takes precedence over the automated one. `moderation.action` is what your policy did with flagged content (`alert`, `hide` or `purge`), or `null` when nothing was flagged; an `alert` leaves the asset `approved`.
|
|
545
|
+
- `content.status`: `temporary` (kept only until processing finishes), `stored` or `removed`, with `content.removalReason` `policy` (purged), `retention` (`keepContentAfterProcessing` off), `requested`, `parent-removed` or `disposed`
|
|
542
546
|
|
|
543
547
|
## Moderation Reports
|
|
544
548
|
|
|
@@ -623,11 +627,11 @@ The API keeps a response for 24 hours. Reusing a key for a different request fai
|
|
|
623
627
|
|
|
624
628
|
## Forward compatibility
|
|
625
629
|
|
|
626
|
-
The SDK does not validate responses against its types, so fields and values added to the API later reach your code unchanged. `
|
|
630
|
+
The SDK does not validate responses against its types, so fields and values added to the API later reach your code unchanged. `AssetProcessingStatus`, `AssetModerationStatus`, `AssetModerationAction`, `AssetReviewerAction`, `AssetContentStatus`, `AssetContentRemovalReason`, `AssetType` and `AssetVisibility` include `string` for that reason: handle values you don't know (for example, treat an unknown status as "not ready"). Event types and fact keys are typed for the ones this version knows; keep a default branch when you switch on them.
|
|
627
631
|
|
|
628
632
|
## Testing
|
|
629
633
|
|
|
630
|
-
`@calmlens/sdk/testing` exports `MockCalmLensServerClient`, an in-memory stand-in for `CalmLensServerClient`,, in-memory storage implementations, mock API key helpers, webhook delivery builders and `generateMediaTestCode`, for testing without network requests.
|
|
634
|
+
`@calmlens/sdk/testing` exports `MockCalmLensServerClient`, an in-memory stand-in for `CalmLensServerClient`,, in-memory storage implementations, mock API key helpers, webhook delivery builders and `generateMediaTestCode`, for testing without network requests. `generateMediaTestCode(signal, testInputKey)` returns the spoken or on-screen test phrase for your project's secret test input key, shown under **Assets → Try test inputs** in the portal; a phrase with another project's key is moderated normally.
|
|
631
635
|
|
|
632
636
|
The `@calmlens/sdk`, `/server` and `/app` entry points do not load or export testing helpers, to keep production bundles small. Keep `/testing` imports in test files.
|
|
633
637
|
|
|
@@ -642,7 +646,7 @@ const client = new MockCalmLensServerClient({
|
|
|
642
646
|
|
|
643
647
|
`MockCalmLensServerClient` accepts the same options as `CalmLensServerClient` (`apiKey`, `projectId`, `baseUrl`, `cdnBaseUrl`, `timeoutMs`, ...) and validates them the same way, plus `mockOptions`. `cdn.getAssetUrl` uses `cdnBaseUrl` (default `mock://calmlens/v1`).
|
|
644
648
|
|
|
645
|
-
It behaves like the API where tests depend on it: errors carry the API's code, HTTP `status`, a `requestId` and `headers`; `assets.list` applies `ordering`, `folderId` and simple `filter` expressions (comparisons joined by `;` and `,`; anything else throws `sdk/invalid-argument` rather than returning wrong results); `assets.search` matches names, IDs, external IDs, descriptions and tags; multipart parts must arrive in order and the queue mode chosen at the start applies to the final part; a reused `idempotencyKey` replays the first result; and `cdn.serve` returns CDN-like responses with JSON error bodies. `client.enqueueEvent({ type, ... })` adds an event that `events.list` and `events.pages` then return.
|
|
649
|
+
It behaves like the API where tests depend on it: errors carry the API's code, HTTP `status`, a `requestId` and `headers`; `assets.list` applies `ordering`, `folderId` and simple `filter` expressions (comparisons joined by `;` and `,`; anything else throws `sdk/invalid-argument` rather than returning wrong results); `assets.search` matches names, IDs, external IDs, descriptions and tags; multipart parts must arrive in order and the queue mode chosen at the start applies to the final part; `mockOptions.queuedOutcome` (default `pending`) and `mockOptions.analyzedOutcome` (default `approved`) set where queued and waited-for uploads end up (`pending`, `approved`, `rejected` or `failed`); content is removed with reason `retention` once processing is done unless `keepContentAfterProcessing` is set, and `assets.deleteContent` removes it with reason `requested`; a reused `idempotencyKey` replays the first result; and `cdn.serve` returns CDN-like responses with JSON error bodies. `client.enqueueEvent({ type, ... })` adds an event that `events.list` and `events.pages` then return.
|
|
646
650
|
|
|
647
651
|
Every call is recorded in `client.calls` as `{ operation, options }`, where `operation` is the method path (`"text.moderate"`, `"assets.retrieve"`, `"events.list"`, ...) and `options` is the object you passed. `client.callsTo(operation)` filters them. To test failure handling, `client.failNextRequest()` makes the next call throw the same `CalmLensError` the real client throws when the network fails (`sdk/transport-error`); pass `"timeout"` for `sdk/timeout`, or any `Error` to throw it as is, and `{ operation }` to fail only that method. Each queued failure is used once.
|
|
648
652
|
|
|
@@ -661,7 +665,7 @@ To test a webhook handler, build a signed delivery the way CalmLens sends it:
|
|
|
661
665
|
```typescript
|
|
662
666
|
import { createWebhookHeaders, toWebhookEventPayload } from "@calmlens/sdk/testing";
|
|
663
667
|
|
|
664
|
-
const event = client.enqueueEvent({ type: "asset.
|
|
668
|
+
const event = client.enqueueEvent({ type: "asset.approved", assetId: asset.id });
|
|
665
669
|
const body = JSON.stringify(toWebhookEventPayload(event));
|
|
666
670
|
const headers = await createWebhookHeaders({ secret, body, deliveryId: "delivery-1" });
|
|
667
671
|
const response = await app.request("/api/webhooks/calmlens", { method: "POST", body, headers });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const require_CalmLensVersion = require("./CalmLensVersion-
|
|
2
|
-
const require_CalmLensUtils = require("./CalmLensUtils-
|
|
1
|
+
const require_CalmLensVersion = require("./CalmLensVersion-CuxKdijk.js");
|
|
2
|
+
const require_CalmLensUtils = require("./CalmLensUtils-Cv1Ylp9-.js");
|
|
3
3
|
//#region src/client/CalmLensConnection.ts
|
|
4
4
|
const DEFAULT_TIMEOUT_MS = 12e4;
|
|
5
5
|
const DEFAULT_MAX_RETRIES = 2;
|
|
@@ -62,7 +62,8 @@ const prepareSubmitAssetOptions = (options) => {
|
|
|
62
62
|
const newIdempotencyKey = () => require_CalmLensUtils.randomUuid();
|
|
63
63
|
/** Text content is small, so it keeps the API deadline; binary files may be arbitrarily large. */
|
|
64
64
|
const isTextPayload = (payload) => "plainText" in payload;
|
|
65
|
-
const
|
|
65
|
+
const hasStringStatus = (value) => !!value && typeof value === "object" && typeof value.status === "string";
|
|
66
|
+
const isAsset = (data) => !!data && typeof data === "object" && typeof data.id === "string" && !!data.id && hasStringStatus(data.processing) && hasStringStatus(data.moderation);
|
|
66
67
|
const isUploadedAsset = (data) => !!data && typeof data === "object" && typeof data.id === "string" && !!data.id;
|
|
67
68
|
const expectUploadedAsset = (data) => {
|
|
68
69
|
if (!isUploadedAsset(data)) throw new require_CalmLensVersion.CalmLensError(require_CalmLensVersion.SDK_ERROR_CODES.INVALID_RESPONSE, {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_CalmLensVersion = require("./CalmLensVersion-
|
|
1
|
+
const require_CalmLensVersion = require("./CalmLensVersion-CuxKdijk.js");
|
|
2
2
|
const MAX_IMAGE_UPLOAD_BYTES = /* @__PURE__ */ (() => 20971520)();
|
|
3
3
|
const MIN_MULTIPART_IMAGE_UPLOAD_BYTES = /* @__PURE__ */ (() => 10485760)();
|
|
4
4
|
const REJECTED_UPLOAD_MIME_TYPE_SET = /* @__PURE__ */ new Set([
|
|
@@ -537,12 +537,11 @@ const stringifyQuery = (query) => {
|
|
|
537
537
|
for (const [key, value] of Object.entries(query)) appendQueryValue(parts, key, value);
|
|
538
538
|
return parts.join("&");
|
|
539
539
|
};
|
|
540
|
-
/**
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
];
|
|
540
|
+
/**
|
|
541
|
+
* Whether an asset has a moderation decision (`moderation.status` `approved` or `rejected`) or
|
|
542
|
+
* failed processing; `assets.waitForDecision` returns at the first such state.
|
|
543
|
+
*/
|
|
544
|
+
const isAssetDecided = (asset) => asset.moderation.status === "approved" || asset.moderation.status === "rejected" || asset.processing.status === "failed";
|
|
546
545
|
const DECISION_POLL_FIRST_DELAY_MS = 500;
|
|
547
546
|
const DECISION_POLL_MAX_DELAY_MS = 5e3;
|
|
548
547
|
const DEFAULT_DECISION_TIMEOUT_MS = 6e4;
|
|
@@ -557,13 +556,14 @@ const pollForDecision = async (retrieve, { timeoutMs = DEFAULT_DECISION_TIMEOUT_
|
|
|
557
556
|
for (;;) {
|
|
558
557
|
if (signal?.aborted) throw new require_CalmLensVersion.CalmLensError(require_CalmLensVersion.SDK_ERROR_CODES.CANCELLED, { message: "Request cancelled" });
|
|
559
558
|
const asset = await retrieve();
|
|
560
|
-
if (
|
|
559
|
+
if (isAssetDecided(asset)) return asset;
|
|
561
560
|
const remaining = deadline - now();
|
|
562
561
|
if (remaining <= 0) throw new require_CalmLensVersion.CalmLensError(require_CalmLensVersion.SDK_ERROR_CODES.TIMEOUT, {
|
|
563
|
-
message: `Asset ${asset.id} had no moderation decision after ${timeoutMs}ms (status ${asset.status})`,
|
|
562
|
+
message: `Asset ${asset.id} had no moderation decision after ${timeoutMs}ms (processing ${asset.processing.status}, moderation ${asset.moderation.status})`,
|
|
564
563
|
details: {
|
|
565
564
|
assetId: asset.id,
|
|
566
|
-
|
|
565
|
+
processingStatus: asset.processing.status,
|
|
566
|
+
moderationStatus: asset.moderation.status
|
|
567
567
|
}
|
|
568
568
|
});
|
|
569
569
|
await sleep(Math.min(delay, remaining), signal);
|
|
@@ -39,6 +39,8 @@ const API_ERROR_CODES = {
|
|
|
39
39
|
UPLOAD_PRESET_NOT_FOUND: "upload-preset/not-found",
|
|
40
40
|
UPLOAD_PRESET_DISABLED: "upload-preset/disabled",
|
|
41
41
|
UPLOAD_PRESET_TOKEN_REQUIRED: "upload-preset/token-required",
|
|
42
|
+
REMOTE_URL_NOT_ALLOWED: "upload/remote-url-not-allowed",
|
|
43
|
+
WEBSITE_RATE_LIMIT_EXCEEDED: "upload/website-rate-limit-exceeded",
|
|
42
44
|
UPLOAD_PRESET_CONTENT_TYPE_NOT_ALLOWED: "upload-preset/content-type-not-allowed",
|
|
43
45
|
UPLOAD_PRESET_TOO_LARGE: "upload-preset/too-large",
|
|
44
46
|
UPLOAD_PRESET_DAILY_LIMIT_REACHED: "upload-preset/daily-limit-reached",
|
|
@@ -46,7 +48,18 @@ const API_ERROR_CODES = {
|
|
|
46
48
|
UPLOAD_PRESET_FIELD_NOT_ALLOWED: "upload-preset/field-not-allowed",
|
|
47
49
|
UPLOAD_TOKEN_INVALID: "upload-token/invalid",
|
|
48
50
|
UPLOAD_TOKEN_EXPIRED: "upload-token/expired",
|
|
49
|
-
UPLOAD_TOKEN_USED: "upload-token/used"
|
|
51
|
+
UPLOAD_TOKEN_USED: "upload-token/used",
|
|
52
|
+
INVALID_REMOTE_URL: "asset/invalid-remote-url",
|
|
53
|
+
REMOTE_URL_UNAVAILABLE: "asset/remote-url-unavailable",
|
|
54
|
+
APPEAL_NOT_FOUND: "appeal/not-found",
|
|
55
|
+
APPEAL_JUDGMENT_NOT_FOUND: "appeal/judgment-not-found",
|
|
56
|
+
APPEAL_ASSET_NOT_FOUND: "appeal/asset-not-found",
|
|
57
|
+
APPEAL_PERMISSION_DENIED: "appeal/permission-denied",
|
|
58
|
+
APPEAL_ALREADY_REVIEWED: "appeal/already-reviewed",
|
|
59
|
+
APPEAL_DECISION_CHANGED: "appeal/decision-changed",
|
|
60
|
+
APPEAL_INVALID_ACTION: "appeal/invalid-action",
|
|
61
|
+
APPEAL_SAFETY_HELD: "appeal/safety-held",
|
|
62
|
+
APPEAL_CONTENT_UNAVAILABLE: "appeal/content-unavailable"
|
|
50
63
|
};
|
|
51
64
|
/** The HTTP status and default message the API returns with each documented error code. */
|
|
52
65
|
const API_ERROR_DEFINITIONS = {
|
|
@@ -202,6 +215,14 @@ const API_ERROR_DEFINITIONS = {
|
|
|
202
215
|
status: 401,
|
|
203
216
|
message: "This upload preset only accepts uploads with an upload token. Sign one on your server with uploadTokens.sign and pass it to the client."
|
|
204
217
|
},
|
|
218
|
+
"upload/remote-url-not-allowed": {
|
|
219
|
+
status: 403,
|
|
220
|
+
message: "This project doesn't accept URLs from this domain. Change the allowed or blocked domains under Project settings."
|
|
221
|
+
},
|
|
222
|
+
"upload/website-rate-limit-exceeded": {
|
|
223
|
+
status: 429,
|
|
224
|
+
message: "This project has submitted too many websites in the last minute. Wait for the Retry-After period, or lower the pace of submissions."
|
|
225
|
+
},
|
|
205
226
|
"upload-preset/content-type-not-allowed": {
|
|
206
227
|
status: 415,
|
|
207
228
|
message: "This upload preset doesn't accept this file type."
|
|
@@ -233,6 +254,50 @@ const API_ERROR_DEFINITIONS = {
|
|
|
233
254
|
"upload-token/used": {
|
|
234
255
|
status: 409,
|
|
235
256
|
message: "The upload token has already been used. Each token uploads one asset."
|
|
257
|
+
},
|
|
258
|
+
"asset/invalid-remote-url": {
|
|
259
|
+
status: 400,
|
|
260
|
+
message: "Remote URLs must use HTTP or HTTPS and cannot target private networks, localhost names or URL credentials."
|
|
261
|
+
},
|
|
262
|
+
"asset/remote-url-unavailable": {
|
|
263
|
+
status: 422,
|
|
264
|
+
message: "The URL responded with an error status. Check that it is publicly reachable."
|
|
265
|
+
},
|
|
266
|
+
"appeal/not-found": {
|
|
267
|
+
status: 404,
|
|
268
|
+
message: "The appeal does not exist in this project."
|
|
269
|
+
},
|
|
270
|
+
"appeal/judgment-not-found": {
|
|
271
|
+
status: 404,
|
|
272
|
+
message: "The judgment event is unavailable, belongs to another project or cannot supply an asset decision. Supply explicit context instead."
|
|
273
|
+
},
|
|
274
|
+
"appeal/asset-not-found": {
|
|
275
|
+
status: 404,
|
|
276
|
+
message: "The linked asset does not exist in this project."
|
|
277
|
+
},
|
|
278
|
+
"appeal/permission-denied": {
|
|
279
|
+
status: 403,
|
|
280
|
+
message: "You cannot review this project's appeals."
|
|
281
|
+
},
|
|
282
|
+
"appeal/already-reviewed": {
|
|
283
|
+
status: 409,
|
|
284
|
+
message: "A reviewer already resolved this appeal."
|
|
285
|
+
},
|
|
286
|
+
"appeal/decision-changed": {
|
|
287
|
+
status: 409,
|
|
288
|
+
message: "The linked asset's decision changed after the appealed decision. Submit a new appeal for the current decision."
|
|
289
|
+
},
|
|
290
|
+
"appeal/invalid-action": {
|
|
291
|
+
status: 400,
|
|
292
|
+
message: "The decision's action is not valid for this appeal."
|
|
293
|
+
},
|
|
294
|
+
"appeal/safety-held": {
|
|
295
|
+
status: 409,
|
|
296
|
+
message: "A safety hold prevents this appeal from being submitted or applied."
|
|
297
|
+
},
|
|
298
|
+
"appeal/content-unavailable": {
|
|
299
|
+
status: 409,
|
|
300
|
+
message: "The appeal's content is unavailable, or approval would require restoring removed content."
|
|
236
301
|
}
|
|
237
302
|
};
|
|
238
303
|
//#endregion
|
|
@@ -325,7 +390,7 @@ const invalidConfig = (message) => new CalmLensError(SDK_ERROR_CODES.INVALID_CON
|
|
|
325
390
|
//#endregion
|
|
326
391
|
//#region src/client/CalmLensVersion.ts
|
|
327
392
|
/** This SDK's version, from its package.json. */
|
|
328
|
-
const SDK_VERSION = "0.
|
|
393
|
+
const SDK_VERSION = "0.15.0";
|
|
329
394
|
const runtimeName = () => {
|
|
330
395
|
const runtime = globalThis;
|
|
331
396
|
if (isReactNativeRuntime()) return "react-native";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const require_CalmLensVersion = require("./CalmLensVersion-
|
|
2
|
-
const require_CalmLensUtils = require("./CalmLensUtils-
|
|
1
|
+
const require_CalmLensVersion = require("./CalmLensVersion-CuxKdijk.js");
|
|
2
|
+
const require_CalmLensUtils = require("./CalmLensUtils-Cv1Ylp9-.js");
|
|
3
3
|
//#region ../shared/src/JsonUtils.ts
|
|
4
4
|
const stringifyJsonCanonical = (value) => JSON.stringify(value, (_key, nestedValue) => {
|
|
5
5
|
if (!nestedValue || typeof nestedValue !== "object" || Array.isArray(nestedValue)) return nestedValue;
|