@calmlens/sdk 0.12.0 → 0.14.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 +54 -37
- package/cjs/CalmLensConnection-Bth5fJyp.js +234 -0
- package/cjs/CalmLensUtils-DNdmYeX6.js +699 -0
- package/cjs/{CalmLensError-CKwMKV0S.js → CalmLensVersion-Bs0DreBV.js} +114 -1
- package/cjs/{CalmLensWebhooks-BTPxKvcD.js → CalmLensWebhooks-6thkvr_F.js} +9 -9
- package/cjs/WebhookPayload-vy8HNMaK.d.ts +15834 -0
- package/cjs/app.d.ts +3 -3
- package/cjs/app.js +10 -10
- package/cjs/{index-BgmWj3KL.d.ts → index-Dz340UQ9.d.ts} +29 -1
- package/cjs/index.d.ts +3 -3
- package/cjs/index.js +4 -5
- package/cjs/server.d.ts +16 -7
- package/cjs/server.js +35 -21
- package/cjs/testing.d.ts +21 -42
- package/cjs/testing.js +181 -94
- package/esm/CalmLensConnection-DrCOL9h9.js +193 -0
- package/esm/{CalmLensUtils-Cx32j2zt.js → CalmLensUtils-BQo79TA1.js} +316 -2
- package/esm/{CalmLensError-CihGG26K.js → CalmLensVersion-DPnL_Ebv.js} +91 -2
- package/esm/{CalmLensWebhooks-DShmAal4.js → CalmLensWebhooks-CWlD-f_P.js} +2 -2
- package/esm/WebhookPayload-vy8HNMaK.d.ts +15834 -0
- package/esm/app.d.ts +3 -3
- package/esm/app.js +4 -4
- package/esm/{index-BgmWj3KL.d.ts → index-Dz340UQ9.d.ts} +29 -1
- package/esm/index.d.ts +3 -3
- package/esm/index.js +1 -3
- package/esm/server.d.ts +16 -7
- package/esm/server.js +24 -10
- package/esm/testing.d.ts +21 -42
- package/esm/testing.js +152 -65
- package/package.json +1 -1
- package/cjs/CalmLensConnection-CYtBP-vu.js +0 -516
- package/cjs/CalmLensUtils-Dg3s4loH.js +0 -367
- package/cjs/WebhookPayload-Cgb32aSH.d.ts +0 -10187
- package/cjs/src-Dsc8Pn6v.js +0 -45
- package/esm/CalmLensConnection-wLNy0OIP.js +0 -475
- package/esm/WebhookPayload-Cgb32aSH.d.ts +0 -10187
- package/esm/src-CJhrILvM.js +0 -22
|
@@ -11,6 +11,7 @@ const API_ERROR_CODES = {
|
|
|
11
11
|
MULTIPART_IMAGE_TOO_LARGE: "upload/multipart-image-too-large",
|
|
12
12
|
IMAGE_EXCEEDED_MAX_DIMENSIONS: "upload/image-exceeded-max-dimensions",
|
|
13
13
|
UPLOAD_LIMIT_EXCEEDED: "upload/limit-exceeded",
|
|
14
|
+
SPEND_LIMIT_REACHED: "upload/spend-limit-reached",
|
|
14
15
|
INVALID_UPLOAD_MIME_TYPE: "upload/invalid-mime-type",
|
|
15
16
|
INVALID_EVENT_CURSOR: "events/invalid-cursor",
|
|
16
17
|
INVALID_UPLOAD_PAYLOAD: "upload/invalid-payload",
|
|
@@ -38,6 +39,8 @@ const API_ERROR_CODES = {
|
|
|
38
39
|
UPLOAD_PRESET_NOT_FOUND: "upload-preset/not-found",
|
|
39
40
|
UPLOAD_PRESET_DISABLED: "upload-preset/disabled",
|
|
40
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",
|
|
41
44
|
UPLOAD_PRESET_CONTENT_TYPE_NOT_ALLOWED: "upload-preset/content-type-not-allowed",
|
|
42
45
|
UPLOAD_PRESET_TOO_LARGE: "upload-preset/too-large",
|
|
43
46
|
UPLOAD_PRESET_DAILY_LIMIT_REACHED: "upload-preset/daily-limit-reached",
|
|
@@ -45,7 +48,18 @@ const API_ERROR_CODES = {
|
|
|
45
48
|
UPLOAD_PRESET_FIELD_NOT_ALLOWED: "upload-preset/field-not-allowed",
|
|
46
49
|
UPLOAD_TOKEN_INVALID: "upload-token/invalid",
|
|
47
50
|
UPLOAD_TOKEN_EXPIRED: "upload-token/expired",
|
|
48
|
-
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"
|
|
49
63
|
};
|
|
50
64
|
/** The HTTP status and default message the API returns with each documented error code. */
|
|
51
65
|
const API_ERROR_DEFINITIONS = {
|
|
@@ -89,6 +103,10 @@ const API_ERROR_DEFINITIONS = {
|
|
|
89
103
|
status: 403,
|
|
90
104
|
message: "Upload limit exceeded. You have used all your free uploads. Please upgrade to Plus or Pro for unlimited uploads."
|
|
91
105
|
},
|
|
106
|
+
"upload/spend-limit-reached": {
|
|
107
|
+
status: 402,
|
|
108
|
+
message: "The project owner's spend limit for this billing period has been reached. Raise the limit in Billing to resume uploads."
|
|
109
|
+
},
|
|
92
110
|
"upload/invalid-mime-type": {
|
|
93
111
|
status: 400,
|
|
94
112
|
message: "Upload file type is not supported. Uploads must use a concrete file MIME type that CalmLens can store and process, such as image/png, image/jpeg, application/pdf, or text/plain."
|
|
@@ -197,6 +215,14 @@ const API_ERROR_DEFINITIONS = {
|
|
|
197
215
|
status: 401,
|
|
198
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."
|
|
199
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
|
+
},
|
|
200
226
|
"upload-preset/content-type-not-allowed": {
|
|
201
227
|
status: 415,
|
|
202
228
|
message: "This upload preset doesn't accept this file type."
|
|
@@ -228,6 +254,50 @@ const API_ERROR_DEFINITIONS = {
|
|
|
228
254
|
"upload-token/used": {
|
|
229
255
|
status: 409,
|
|
230
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."
|
|
231
301
|
}
|
|
232
302
|
};
|
|
233
303
|
//#endregion
|
|
@@ -318,6 +388,25 @@ const invalidArgument = (message, details) => new CalmLensError(SDK_ERROR_CODES.
|
|
|
318
388
|
});
|
|
319
389
|
const invalidConfig = (message) => new CalmLensError(SDK_ERROR_CODES.INVALID_CONFIG, { message });
|
|
320
390
|
//#endregion
|
|
391
|
+
//#region src/client/CalmLensVersion.ts
|
|
392
|
+
/** This SDK's version, from its package.json. */
|
|
393
|
+
const SDK_VERSION = "0.14.0";
|
|
394
|
+
const runtimeName = () => {
|
|
395
|
+
const runtime = globalThis;
|
|
396
|
+
if (isReactNativeRuntime()) return "react-native";
|
|
397
|
+
if (runtime.Bun?.version) return `bun/${runtime.Bun.version}`;
|
|
398
|
+
if (runtime.Deno?.version?.deno) return `deno/${runtime.Deno.version.deno}`;
|
|
399
|
+
if (runtime.navigator?.userAgent === "Cloudflare-Workers") return "workerd";
|
|
400
|
+
if (runtime.process?.versions?.node) return `node/${runtime.process.versions.node}`;
|
|
401
|
+
return "unknown";
|
|
402
|
+
};
|
|
403
|
+
/** The `User-Agent` the SDK sends from server runtimes, e.g. `calmlens-js/0.10.0 (node/22.12.0)`. */
|
|
404
|
+
const getUserAgent = () => `calmlens-js/${SDK_VERSION} (${runtimeName()})`;
|
|
405
|
+
/** True in a web page, where API keys would be visible to every visitor. */
|
|
406
|
+
const isBrowserRuntime = () => typeof globalThis.window !== "undefined" && typeof globalThis.document !== "undefined";
|
|
407
|
+
/** True in a React Native app, where an API key ships inside the app for anyone to extract. */
|
|
408
|
+
const isReactNativeRuntime = () => globalThis.navigator?.product === "ReactNative";
|
|
409
|
+
//#endregion
|
|
321
410
|
Object.defineProperty(exports, "API_ERROR_CODES", {
|
|
322
411
|
enumerable: true,
|
|
323
412
|
get: function() {
|
|
@@ -348,6 +437,18 @@ Object.defineProperty(exports, "SDK_ERROR_CODES", {
|
|
|
348
437
|
return SDK_ERROR_CODES;
|
|
349
438
|
}
|
|
350
439
|
});
|
|
440
|
+
Object.defineProperty(exports, "SDK_VERSION", {
|
|
441
|
+
enumerable: true,
|
|
442
|
+
get: function() {
|
|
443
|
+
return SDK_VERSION;
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
Object.defineProperty(exports, "getUserAgent", {
|
|
447
|
+
enumerable: true,
|
|
448
|
+
get: function() {
|
|
449
|
+
return getUserAgent;
|
|
450
|
+
}
|
|
451
|
+
});
|
|
351
452
|
Object.defineProperty(exports, "invalidArgument", {
|
|
352
453
|
enumerable: true,
|
|
353
454
|
get: function() {
|
|
@@ -360,3 +461,15 @@ Object.defineProperty(exports, "invalidConfig", {
|
|
|
360
461
|
return invalidConfig;
|
|
361
462
|
}
|
|
362
463
|
});
|
|
464
|
+
Object.defineProperty(exports, "isBrowserRuntime", {
|
|
465
|
+
enumerable: true,
|
|
466
|
+
get: function() {
|
|
467
|
+
return isBrowserRuntime;
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
Object.defineProperty(exports, "isReactNativeRuntime", {
|
|
471
|
+
enumerable: true,
|
|
472
|
+
get: function() {
|
|
473
|
+
return isReactNativeRuntime;
|
|
474
|
+
}
|
|
475
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const
|
|
2
|
-
const require_CalmLensUtils = require("./CalmLensUtils-
|
|
1
|
+
const require_CalmLensVersion = require("./CalmLensVersion-Bs0DreBV.js");
|
|
2
|
+
const require_CalmLensUtils = require("./CalmLensUtils-DNdmYeX6.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;
|
|
@@ -56,11 +56,11 @@ const signUploadToken = async (claims, apiKey) => {
|
|
|
56
56
|
* `MockCalmLensServerClient` share it so both check options the same way.
|
|
57
57
|
*/
|
|
58
58
|
const signUploadTokenWithApiKey = async (apiKey, options, { now = Date.now(), nonce = crypto.randomUUID() } = {}) => {
|
|
59
|
-
if (!options || typeof options !== "object") throw
|
|
59
|
+
if (!options || typeof options !== "object") throw require_CalmLensVersion.invalidArgument("Options are required");
|
|
60
60
|
const { uploadPresetId, externalId, expiresInSeconds = 600 } = options;
|
|
61
61
|
require_CalmLensUtils.encodePathSegment(uploadPresetId, "Upload preset ID");
|
|
62
|
-
if (!Number.isInteger(expiresInSeconds) || expiresInSeconds < 1 || expiresInSeconds > 3600) throw
|
|
63
|
-
if (externalId !== void 0 && (typeof externalId !== "string" || !externalId)) throw
|
|
62
|
+
if (!Number.isInteger(expiresInSeconds) || expiresInSeconds < 1 || expiresInSeconds > 3600) throw require_CalmLensVersion.invalidArgument(`expiresInSeconds must be a whole number from 1 to ${UPLOAD_TOKEN_MAX_TTL_SECONDS}`);
|
|
63
|
+
if (externalId !== void 0 && (typeof externalId !== "string" || !externalId)) throw require_CalmLensVersion.invalidArgument("externalId must be a non-empty string");
|
|
64
64
|
const expiresAt = now + expiresInSeconds * 1e3;
|
|
65
65
|
return {
|
|
66
66
|
token: await signUploadToken({
|
|
@@ -96,7 +96,7 @@ const payloadToText = (payload) => {
|
|
|
96
96
|
const view = ArrayBuffer.isView(payload) ? new Uint8Array(payload.buffer, payload.byteOffset, payload.byteLength) : new Uint8Array(payload);
|
|
97
97
|
return new TextDecoder("utf-8", { fatal: false }).decode(view);
|
|
98
98
|
}
|
|
99
|
-
throw
|
|
99
|
+
throw require_CalmLensVersion.invalidArgument("Webhook payload must be the raw request body as a string, Uint8Array/Buffer or ArrayBuffer, not parsed JSON");
|
|
100
100
|
};
|
|
101
101
|
/**
|
|
102
102
|
* Verifies a webhook delivery: `x-calmlens-signature` must contain a `v1=` HMAC-SHA256 of
|
|
@@ -130,18 +130,18 @@ async function verifyWebhookSignature(options) {
|
|
|
130
130
|
* so keep a default branch when you switch on `type`.
|
|
131
131
|
*/
|
|
132
132
|
async function parseWebhookEvent(options) {
|
|
133
|
-
if (!await verifyWebhookSignature(options)) throw new
|
|
133
|
+
if (!await verifyWebhookSignature(options)) throw new require_CalmLensVersion.CalmLensError(require_CalmLensVersion.SDK_ERROR_CODES.INVALID_WEBHOOK_SIGNATURE, { message: "Webhook signature or timestamp did not verify" });
|
|
134
134
|
let event;
|
|
135
135
|
try {
|
|
136
136
|
event = JSON.parse(payloadToText(options.payload));
|
|
137
137
|
} catch (cause) {
|
|
138
|
-
throw new
|
|
138
|
+
throw new require_CalmLensVersion.CalmLensError(require_CalmLensVersion.SDK_ERROR_CODES.INVALID_WEBHOOK_PAYLOAD, {
|
|
139
139
|
message: "Webhook body is not JSON",
|
|
140
140
|
cause
|
|
141
141
|
});
|
|
142
142
|
}
|
|
143
143
|
const candidate = event;
|
|
144
|
-
if (!candidate || typeof candidate !== "object" || typeof candidate.id !== "string" || typeof candidate.type !== "string" || typeof candidate.projectId !== "string" || !candidate.data || typeof candidate.data !== "object") throw new
|
|
144
|
+
if (!candidate || typeof candidate !== "object" || typeof candidate.id !== "string" || typeof candidate.type !== "string" || typeof candidate.projectId !== "string" || !candidate.data || typeof candidate.data !== "object") throw new require_CalmLensVersion.CalmLensError(require_CalmLensVersion.SDK_ERROR_CODES.INVALID_WEBHOOK_PAYLOAD, { message: "Webhook body is not a CalmLens event" });
|
|
145
145
|
return event;
|
|
146
146
|
}
|
|
147
147
|
//#endregion
|