@calmlens/sdk 0.17.1 → 0.18.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 +46 -37
- package/bin/calmlens.js +16016 -0
- package/bin/package.json +1 -0
- package/{esm/BrowserUpload-uZjVFGfP.d.ts → cjs/BrowserUpload-BT091eOB.d.ts} +2 -2
- package/cjs/{BrowserUpload-DZ0Vbmxi.js → BrowserUpload-CQwXEGFv.js} +1 -1
- package/cjs/{CalmLensAppClient-DBGmaDlG.js → CalmLensAppClient-CHQGu-Do.js} +2 -2
- package/cjs/{CalmLensConnection-K1TAD1Z-.js → CalmLensConnection-B8Bu2j3y.js} +2 -2
- package/cjs/{CalmLensServerClient-C-pxiz0C.js → CalmLensServerClient-C5o2A8MW.js} +3 -3
- package/cjs/{ModerationContextLimits-Dj1YAl3G.js → ModerationContextLimits-DelWfmiJ.js} +1 -1
- package/cjs/{ReactUpload-nZahpmyg.d.ts → ReactUpload-CAyCsyG6.d.ts} +1 -1
- package/cjs/{ReactUpload-DxtFMr06.js → ReactUpload-Cfg5JYUq.js} +1 -1
- package/cjs/{TestTriggers-DcArt0Nu-CvoaONUB.js → TestTriggers-DTc_iMJF-BjMPq4_3.js} +10 -9
- package/cjs/{UploadController-BBkN4Ni6.js → UploadController-BGO1eyzb.js} +2 -2
- package/cjs/app.js +2 -2
- package/cjs/elements.d.ts +2 -2
- package/cjs/elements.js +3 -3
- package/cjs/index.js +1 -1
- package/cjs/react-native.d.ts +2 -2
- package/cjs/react-native.js +3 -3
- package/cjs/react.d.ts +2 -2
- package/cjs/react.js +4 -4
- package/cjs/server.js +2 -2
- package/cjs/test-inputs.js +9 -9
- package/cjs/testing.d.ts +1 -1
- package/cjs/testing.js +8 -8
- package/cjs/{ui-KceuoVCM.js → ui-CC1GOoMg.js} +4 -4
- package/{esm/ui-CgZpgpNo.d.ts → cjs/ui-wIiMfqMB.d.ts} +3 -3
- package/cjs/ui.d.ts +1 -1
- package/cjs/ui.js +2 -2
- package/cjs/vue.d.ts +2 -2
- package/cjs/vue.js +3 -3
- package/{cjs/BrowserUpload-uZjVFGfP.d.ts → esm/BrowserUpload-BT091eOB.d.ts} +2 -2
- package/esm/{BrowserUpload-D7-zNmc0.js → BrowserUpload-D2xBWqhm.js} +1 -1
- package/esm/{CalmLensAppClient-FVMwrHlH.js → CalmLensAppClient-DuFas_dW.js} +2 -2
- package/esm/{CalmLensConnection-iyNQHA8L.js → CalmLensConnection-MxwwLXdl.js} +2 -2
- package/esm/{CalmLensServerClient-fQEYKLw4.js → CalmLensServerClient-D80_mKpW.js} +3 -3
- package/esm/{ModerationContextLimits-D1x_WAtP.js → ModerationContextLimits-BaX2QnU8.js} +1 -1
- package/esm/{ReactUpload-nZahpmyg.d.ts → ReactUpload-CAyCsyG6.d.ts} +1 -1
- package/esm/{ReactUpload-D6yBCkcw.js → ReactUpload-CL2XC8WS.js} +1 -1
- package/esm/{TestTriggers-DcArt0Nu-s0AQZuWE.js → TestTriggers-DTc_iMJF-HfB533Gx.js} +10 -9
- package/esm/{UploadController-txp5NuzH.js → UploadController-qjoYGCwx.js} +2 -2
- package/esm/app.js +2 -2
- package/esm/elements.d.ts +2 -2
- package/esm/elements.js +3 -3
- package/esm/index.js +1 -1
- package/esm/react-native.d.ts +2 -2
- package/esm/react-native.js +3 -3
- package/esm/react.d.ts +2 -2
- package/esm/react.js +4 -4
- package/esm/server.js +2 -2
- package/esm/test-inputs.js +1 -1
- package/esm/testing.d.ts +1 -1
- package/esm/testing.js +6 -6
- package/esm/{ui-Ca_SpJXf.js → ui-CCY3yXFn.js} +4 -4
- package/{cjs/ui-CgZpgpNo.d.ts → esm/ui-wIiMfqMB.d.ts} +3 -3
- package/esm/ui.d.ts +1 -1
- package/esm/ui.js +2 -2
- package/esm/vue.d.ts +2 -2
- package/esm/vue.js +3 -3
- package/package.json +9 -3
package/README.md
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
#
|
|
1
|
+
# CalmLens JavaScript SDK
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
CalmLens provides content moderation and classification for text, images, documents and websites, plus optional storage and CDN delivery. This SDK makes it easy to integrate CalmLens into your JavaScript/TypeScript applications.
|
|
3
|
+
The official JavaScript and TypeScript SDK for [CalmLens](https://calmlens.com): content moderation and classification for text, images, documents and websites, plus optional storage and CDN delivery.
|
|
6
4
|
|
|
7
5
|
## Installation
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
|
-
npm install
|
|
8
|
+
npm install calmlens
|
|
11
9
|
```
|
|
12
10
|
|
|
11
|
+
The same package is also published as `@calmlens/sdk`, at the same version. Import from the name you installed: `calmlens/server` with `calmlens`, or `@calmlens/sdk/server` with `@calmlens/sdk`.
|
|
12
|
+
|
|
13
13
|
The package has two clients, each with its own import:
|
|
14
14
|
|
|
15
|
-
| Import
|
|
16
|
-
|
|
|
17
|
-
|
|
|
18
|
-
|
|
|
15
|
+
| Import | Client | Runs | Authenticates with |
|
|
16
|
+
| ----------------- | ---------------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
|
|
17
|
+
| `calmlens/server` | `CalmLensServerClient` | On your server: Node.js 22 or later, Bun, Deno, Cloudflare Workers | A project API key |
|
|
18
|
+
| `calmlens/app` | `CalmLensAppClient` | In browsers and React Native apps | An [upload preset](#browser-and-app-uploads-with-upload-presets), with an optional upload token from your server |
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
`calmlens` itself exports the shared types, `CalmLensError`, the error codes and `SDK_VERSION`. The package ships ES modules and CommonJS with type declarations and loads no other packages at runtime: importing only `verifyWebhookSignature` adds about 5 KB (minified) to a bundle, `CalmLensAppClient` about 19 KB and `CalmLensServerClient` about 28 KB.
|
|
21
21
|
|
|
22
22
|
API keys are secret, so `CalmLensServerClient` refuses to start in a web page or app. Upload from browsers and apps with `CalmLensAppClient`, and serve private content to them with [signed URLs](#assetscreatesignedurloptions).
|
|
23
23
|
|
|
@@ -28,7 +28,7 @@ Every public SDK operation accepts one options object. This keeps call sites sta
|
|
|
28
28
|
Create an API key under **Developer → API Keys** in the CalmLens portal. The same page shows your **Project ID**. Keep both on your server.
|
|
29
29
|
|
|
30
30
|
```typescript
|
|
31
|
-
import { CalmLensServerClient } from "
|
|
31
|
+
import { CalmLensServerClient } from "calmlens/server";
|
|
32
32
|
|
|
33
33
|
const client = new CalmLensServerClient({
|
|
34
34
|
apiKey: process.env.CALMLENS_API_KEY!,
|
|
@@ -72,7 +72,7 @@ See [How CalmLens works](https://calmlens.com/docs/how-it-works) and [Testing yo
|
|
|
72
72
|
|
|
73
73
|
### CalmLensServerClient
|
|
74
74
|
|
|
75
|
-
The client for your server, with every API method. Import it from
|
|
75
|
+
The client for your server, with every API method. Import it from `calmlens/server`.
|
|
76
76
|
|
|
77
77
|
#### Constructor
|
|
78
78
|
|
|
@@ -510,11 +510,11 @@ age-restricted image. It takes no `lens` or `transform`; passing one throws `sdk
|
|
|
510
510
|
|
|
511
511
|
##### Building CDN URLs in the browser
|
|
512
512
|
|
|
513
|
-
`getAssetUrl`, exported from
|
|
513
|
+
`getAssetUrl`, exported from `calmlens/app` and `calmlens`, builds the same URLs as `cdn.getAssetUrl` from
|
|
514
514
|
the project ID alone. It takes no API key and sends no request.
|
|
515
515
|
|
|
516
516
|
```typescript
|
|
517
|
-
import { getAssetUrl } from "
|
|
517
|
+
import { getAssetUrl } from "calmlens/app";
|
|
518
518
|
|
|
519
519
|
const placeholder = getAssetUrl({ projectId, assetIdentifier: assetId, resource: "placeholder" });
|
|
520
520
|
const thumbnail = getAssetUrl({ projectId, assetIdentifier: assetId, lens: "thumbnail" });
|
|
@@ -607,7 +607,7 @@ It returns `true` only when the `x-calmlens-signature` header contains a `v1=` H
|
|
|
607
607
|
`CalmLensAppClient` uploads from a browser or app through an upload preset, without an API key. Create presets in the portal under Developer → Upload presets.
|
|
608
608
|
|
|
609
609
|
```ts
|
|
610
|
-
import { CalmLensAppClient } from "
|
|
610
|
+
import { CalmLensAppClient } from "calmlens/app";
|
|
611
611
|
|
|
612
612
|
// In the browser: no API key
|
|
613
613
|
const client = new CalmLensAppClient({
|
|
@@ -648,20 +648,20 @@ Server uploads can also pass `uploadPresetId` to `assets.create` or `assets.clas
|
|
|
648
648
|
|
|
649
649
|
Ready-made front-end components for each UI library. They share one core, so they behave the same everywhere:
|
|
650
650
|
|
|
651
|
-
| Import
|
|
652
|
-
|
|
|
653
|
-
|
|
|
654
|
-
|
|
|
655
|
-
|
|
|
656
|
-
|
|
|
657
|
-
|
|
|
651
|
+
| Import | Components |
|
|
652
|
+
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
653
|
+
| `calmlens/react` | `CalmLensUploader`, `CalmLensMedia`, `useCalmLensUpload` (also works in Preact through `preact/compat`) |
|
|
654
|
+
| `calmlens/vue` | `CalmLensUploader`, `CalmLensMedia`, `useCalmLensUpload` |
|
|
655
|
+
| `calmlens/svelte` | `CalmLensUploader`, `CalmLensMedia` (Svelte 5) |
|
|
656
|
+
| `calmlens/elements` | `<calmlens-uploader>`, `<calmlens-media>` for plain HTML, Angular, Solid, Lit and anything else that renders custom elements |
|
|
657
|
+
| `calmlens/ui` | The framework-free core: `createUploadController`, `getMediaState`, messages and styles |
|
|
658
658
|
|
|
659
659
|
`react`, `vue` and `svelte` are optional peer dependencies: install the one you use.
|
|
660
660
|
|
|
661
661
|
**The uploader** is a drop zone with a file list. It uploads each file through `CalmLensAppClient`, shows byte progress, checks the upload until it has a moderation decision (for about 15 seconds; anything still pending is your webhook's to settle), and shows the decision or a message for each error code.
|
|
662
662
|
|
|
663
663
|
```tsx
|
|
664
|
-
import { CalmLensUploader } from "
|
|
664
|
+
import { CalmLensUploader } from "calmlens/react";
|
|
665
665
|
|
|
666
666
|
<CalmLensUploader
|
|
667
667
|
uploadPresetId="preset_4Lk8Qw2Xm9Rt5Vb3Nc7P"
|
|
@@ -680,7 +680,7 @@ To upload through your own API instead of a preset, pass `upload(file, { signal,
|
|
|
680
680
|
**The media component** shows an asset from the CDN's `rendered` resource in a single `<img>`, so the CDN picks the best format the browser accepts. It adds `srcset` widths when you pass `widths` and `sizes`, shows the blurred placeholder while the image loads, and fades the image in. Video and audio play the `original` file, with a rendered poster for video. Age-restricted assets stay behind the placeholder and a notice with a reveal button until you pass `viewerClaims: { ageConfirmed: true }`. Blocked and purged assets, and images that fail to load, show an unavailable notice.
|
|
681
681
|
|
|
682
682
|
```tsx
|
|
683
|
-
import { CalmLensMedia } from "
|
|
683
|
+
import { CalmLensMedia } from "calmlens/react";
|
|
684
684
|
|
|
685
685
|
<CalmLensMedia
|
|
686
686
|
projectId="proj_8fK2mQx7Lp4NvB3tRz9Y"
|
|
@@ -699,7 +699,7 @@ Instead of `projectId` and `assetId`, you can pass an asset from the API as `ass
|
|
|
699
699
|
|
|
700
700
|
It also takes `type` (`image`, `video` or `audio`), `resource`, `lens`, `transform`, `url` (a signed URL from your server), `placeholderHash` (the asset's `content.image.thumbhash`, drawn in the browser so no placeholder request is needed), `placeholderUrl`, `averageColor`, `fit`, `position`, `lazy`, `fetchPriority` and `controls`.
|
|
701
701
|
|
|
702
|
-
The React components render their stylesheet with them: React places it in the page's head once, on the server too. With Vue, Svelte or the web components, import
|
|
702
|
+
The React components render their stylesheet with them: React places it in the page's head once, on the server too. With Vue, Svelte or the web components, import `calmlens/styles.css` once (or link it). Nothing is added to the page at runtime. Theme the components with `--calmlens-accent`, `--calmlens-radius` and the other `--calmlens-*` properties, which take precedence on any element. To style the `calmlens-*` classes yourself, pass `unstyled` to the React components, or leave out the stylesheet import. All text can be replaced or translated with `messages`, keyed by message name or error code.
|
|
703
703
|
|
|
704
704
|
The [widget playground](https://github.com/CalmLens/sdks/tree/main/examples/widget-playground) runs every component against a mock API.
|
|
705
705
|
|
|
@@ -748,7 +748,7 @@ See the [classification results guide](https://calmlens.com/docs/guides/classifi
|
|
|
748
748
|
Every failure is a `CalmLensError`, whether the API returned an error or the request never completed. Branch on its stable `code`:
|
|
749
749
|
|
|
750
750
|
```typescript
|
|
751
|
-
import { CALMLENS_ERROR_CODES, CalmLensError } from "
|
|
751
|
+
import { CALMLENS_ERROR_CODES, CalmLensError } from "calmlens";
|
|
752
752
|
|
|
753
753
|
try {
|
|
754
754
|
await client.assets.retrieve({ assetId });
|
|
@@ -810,12 +810,12 @@ The SDK does not validate responses against its types, so fields and values adde
|
|
|
810
810
|
|
|
811
811
|
## Testing
|
|
812
812
|
|
|
813
|
-
|
|
813
|
+
`calmlens/testing` exports `MockCalmLensServerClient` and `MockCalmLensAppClient`, which run the real clients against an in-memory CalmLens API and CDN, in-memory storage implementations, mock API key helpers, webhook delivery builders and `generateMediaTestCode`, for testing without network requests. `generateMediaTestCode(signal, testInputKey)` asynchronously returns the spoken or on-screen test phrase authenticated with your project's secret test input key, shown under **Assets → Try it → Test inputs** in the portal; a phrase with another project's key is moderated normally.
|
|
814
814
|
|
|
815
|
-
The
|
|
815
|
+
The `calmlens`, `/server` and `/app` entry points do not load or export testing helpers, to keep production bundles small. Keep `/testing` imports in test files.
|
|
816
816
|
|
|
817
817
|
```typescript
|
|
818
|
-
import { MockCalmLensServerClient, createMockApiKey } from "
|
|
818
|
+
import { MockCalmLensServerClient, createMockApiKey } from "calmlens/testing";
|
|
819
819
|
|
|
820
820
|
const client = new MockCalmLensServerClient({
|
|
821
821
|
apiKey: createMockApiKey(),
|
|
@@ -832,7 +832,7 @@ The in-memory API behaves like CalmLens where tests depend on it: `assets.list`
|
|
|
832
832
|
`mockOptions.moderate` decides each asset the mock moderates. It receives the stored `asset`, its `content` (`bytes`, and `text` for a plain-text upload), the `trigger` (`upload`, `retry`, `rerun` or `complete`) and whether the API would `queued` the work, and returns a status (`pending`, `failed`, `allowed`, `limited`, `age-restricted`, `blocked` or `purged`) or a decision built with `mockDecision`. A decision fills the asset's report as CalmLens does: every category (unflagged ones evaluated with score 0), the policy's actions and `flaggedCategories`. Without `moderate`, queued uploads, retries and reruns stay `pending` and everything else is `allowed`. `client.completeProcessing(assetId, decision?)` finishes a pending asset as the pipeline would, with the decision given or the one `moderate` returns for `complete`:
|
|
833
833
|
|
|
834
834
|
```typescript
|
|
835
|
-
import { MockCalmLensServerClient, createMockApiKey, mockDecision } from "
|
|
835
|
+
import { MockCalmLensServerClient, createMockApiKey, mockDecision } from "calmlens/testing";
|
|
836
836
|
|
|
837
837
|
const client = new MockCalmLensServerClient({
|
|
838
838
|
apiKey: createMockApiKey(),
|
|
@@ -878,7 +878,7 @@ expect(client.callsTo("text.classify").at(-1)).toMatchObject({ body: "hello", st
|
|
|
878
878
|
To test a webhook handler, build a signed delivery the way CalmLens sends it:
|
|
879
879
|
|
|
880
880
|
```typescript
|
|
881
|
-
import { createWebhookHeaders, toWebhookEventPayload } from "
|
|
881
|
+
import { createWebhookHeaders, toWebhookEventPayload } from "calmlens/testing";
|
|
882
882
|
|
|
883
883
|
const event = await client.enqueueEvent({ type: "asset.allowed", assetId: asset.id });
|
|
884
884
|
const body = JSON.stringify(toWebhookEventPayload(event));
|
|
@@ -928,7 +928,7 @@ The result is typed as an extensible `WebhookEventPayload`. Event types and fact
|
|
|
928
928
|
**Fetch API** (Next.js route handlers, Remix, Cloudflare Workers, Bun, Deno):
|
|
929
929
|
|
|
930
930
|
```typescript
|
|
931
|
-
import { CalmLensError, parseWebhookEvent } from "
|
|
931
|
+
import { CalmLensError, parseWebhookEvent } from "calmlens/server";
|
|
932
932
|
|
|
933
933
|
export async function POST(request: Request) {
|
|
934
934
|
let event;
|
|
@@ -972,13 +972,20 @@ app.post("/api/webhooks/calmlens", express.raw({ type: "application/json" }), as
|
|
|
972
972
|
|
|
973
973
|
## Command-line tool
|
|
974
974
|
|
|
975
|
-
|
|
975
|
+
This package includes the `calmlens` [CLI](https://calmlens.com/docs/guides/local-development). Because the package is named `calmlens`, you can run it without adding a dependency:
|
|
976
976
|
|
|
977
977
|
```bash
|
|
978
978
|
npx calmlens listen --forward-to http://localhost:3000/api/webhooks/calmlens --env-file .env.local
|
|
979
979
|
```
|
|
980
980
|
|
|
981
|
-
|
|
981
|
+
Install it globally with `npm install -g calmlens`. In a project that depends on the SDK, `npx calmlens` runs the installed version. Standalone executables for macOS, Linux and Windows, which need no Node.js, are attached to the [CLI releases](https://github.com/CalmLens/sdks/releases?q=cli-v&expanded=true) with a `SHA256SUMS` file, and Homebrew installs them:
|
|
982
|
+
|
|
983
|
+
```bash
|
|
984
|
+
brew tap calmlens/sdks https://github.com/CalmLens/sdks
|
|
985
|
+
brew install calmlens/sdks/calmlens
|
|
986
|
+
```
|
|
987
|
+
|
|
988
|
+
Webhook connectors must use public HTTPS URLs, so during development use `listen` to forward events to a local handler instead. Each event is sent with the same body, headers and signature as a real delivery. The CLI reads `CALMLENS_API_KEY`, `CALMLENS_PROJECT_ID` and `CALMLENS_WEBHOOK_SECRET` from the environment or `--env-file`, or uses the key from `calmlens login`; without a secret it prints a generated one for your app. `npx calmlens trigger [harassment|hate|criminal-activity|clean]` moderates a harmless test input so events appear (one upload each). Run `npx calmlens --help` for all options. The CLI's version is the SDK's version.
|
|
982
989
|
|
|
983
990
|
The same feed is available in code with [`events.list`](#events-feed).
|
|
984
991
|
|
|
@@ -1029,11 +1036,13 @@ Requests send `User-Agent: calmlens-js/<version> (<runtime>/<version>)` from ser
|
|
|
1029
1036
|
|
|
1030
1037
|
## Releasing
|
|
1031
1038
|
|
|
1032
|
-
|
|
1039
|
+
Run `bun run changeset` to choose the release type and write release notes. Merging the Version SDKs pull request releases the version and updates this package's `CHANGELOG.md`. See the repository's [.changeset release guide](../.changeset/README.md).
|
|
1040
|
+
|
|
1041
|
+
Once the release commit's SDK Tests and SDK examples runs pass, `.github/workflows/release-npm.yml` publishes one build to npm with provenance, as `calmlens` and as `@calmlens/sdk`. The CLI's standalone executables and Homebrew formula are released in this repository under `cli-v<version>` by `.github/workflows/release-cli.yml`. `bun run build:cli` builds the npm entry, `bin/calmlens.js` (the package build includes it), and `bun run build:cli:binary` builds a standalone executable in `dist/`; set `CALMLENS_BUILD_TARGET`, such as `bun-linux-x64`, to cross-compile it.
|
|
1033
1042
|
|
|
1034
1043
|
### Test-input images
|
|
1035
1044
|
|
|
1036
|
-
The optional
|
|
1045
|
+
The optional `calmlens/test-inputs` entry generates and recognises the same
|
|
1037
1046
|
project-specific colour grids used by the CalmLens portal:
|
|
1038
1047
|
|
|
1039
1048
|
```ts
|
|
@@ -1042,7 +1051,7 @@ import {
|
|
|
1042
1051
|
detectTestImage,
|
|
1043
1052
|
generateTestTextCode,
|
|
1044
1053
|
TEST_INPUT_PRESETS,
|
|
1045
|
-
} from "
|
|
1054
|
+
} from "calmlens/test-inputs";
|
|
1046
1055
|
|
|
1047
1056
|
const input = TEST_INPUT_PRESETS.mixed;
|
|
1048
1057
|
const image = await paintTestImage(input, projectTestInputKey);
|