@calmlens/sdk 0.5.0 → 0.6.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/esm/testing.js CHANGED
@@ -1,4 +1,4 @@
1
- import { _ as validateAssetPayload, a as calmLensError, d as PUBLIC_ERROR_CODES, f as stringifyJsonCanonical, h as getAssetPayloadByteLength, l as getRejectedUploadMimeTypeError, m as assetToData, n as _objectWithoutProperties, p as assetDataToUint8Array, t as toTextAssetOptions, u as isRejectedUploadMimeType, v as _asyncToGenerator, y as _objectSpread2 } from "./CalmLensUtils-BQEgxGWk.js";
1
+ import { b as _objectSpread2, d as isRejectedUploadMimeType, f as PUBLIC_ERROR_CODES, g as getAssetPayloadByteLength, h as assetToData, m as assetDataToUint8Array, n as validateClientOptions, o as calmLensError, p as stringifyJsonCanonical, r as _objectWithoutProperties, t as toTextAssetOptions, u as getRejectedUploadMimeTypeError, v as validateAssetPayload, y as _asyncToGenerator } from "./CalmLensUtils-UthMaLx_.js";
2
2
  import * as zod from "zod";
3
3
  //#region ../shared/src/Permissions.ts
4
4
  const ACTION_SCHEMA = zod.enum([
@@ -12,13 +12,36 @@ const ACTION_SCHEMA = zod.enum([
12
12
  "folders:create",
13
13
  "folders:update",
14
14
  "folders:delete",
15
+ "settings:read",
16
+ "settings:write",
17
+ "members:read",
18
+ "members:write",
19
+ "api-keys:read",
20
+ "api-keys:write",
21
+ "connectors:read",
22
+ "connectors:write",
23
+ "metadata:read",
24
+ "metadata:write",
25
+ "lenses:read",
26
+ "lenses:write",
15
27
  "project:manage"
16
28
  ]);
29
+ const PROJECT_ACTIONS = [...[
30
+ "settings",
31
+ "members",
32
+ "api-keys",
33
+ "connectors",
34
+ "metadata",
35
+ "lenses"
36
+ ].flatMap((feature) => [`${feature}:read`, `${feature}:write`]), "project:manage"];
37
+ const isProjectAction = (action) => PROJECT_ACTIONS.includes(action);
38
+ /** The actions that include `action`: its feature's write action and project:manage. */
39
+ const actionsImplying = (action) => !isProjectAction(action) || action === "project:manage" ? [] : action.endsWith(":read") ? [action.replace(/:read$/, ":write"), "project:manage"] : ["project:manage"];
17
40
  const GRANT_SCHEMA = zod.object({
18
41
  actions: zod.array(ACTION_SCHEMA),
19
42
  folderId: zod.string().uuid().nullable(),
20
43
  includeSubfolders: zod.boolean()
21
- }).strict().refine((g) => g.folderId === null || !g.actions.includes("project:manage"), "Project management requires project scope");
44
+ }).strict().refine((g) => g.folderId === null || !g.actions.some(isProjectAction), "Project configuration permissions require project scope");
22
45
  const GRANTS_SCHEMA = zod.array(GRANT_SCHEMA).max(100);
23
46
  zod.enum([
24
47
  "admin",
@@ -27,7 +50,7 @@ zod.enum([
27
50
  "custom"
28
51
  ]);
29
52
  const permits = (grants, action, folderId, folders) => grants.some((grant) => {
30
- if (!grant.actions.includes(action)) return false;
53
+ if (![action, ...actionsImplying(action)].some((held) => grant.actions.includes(held))) return false;
31
54
  if (grant.folderId === null) return true;
32
55
  let current = folderId;
33
56
  const visited = /* @__PURE__ */ new Set();
@@ -189,7 +212,7 @@ var InMemoryMockDatabaseStorage = class {
189
212
  //#endregion
190
213
  //#region src/testing/MockUtils.ts
191
214
  const appendCdnSelector = (url, options) => {
192
- if (options.lens && options.transform) throw new Error("Specify either a lens or a transform, not both");
215
+ if (options.lens && options.transform) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "Specify either a lens or a transform, not both" });
193
216
  if (options.lens) return `${url}/@${encodeURIComponent(options.lens)}`;
194
217
  if (!options.transform) return url;
195
218
  return `${url}/${encodeURI(options.transform.replace(/^\/+/, ""))}`;
@@ -235,20 +258,34 @@ const _excluded = ["assetIdentifier"];
235
258
  const _excluded2 = ["assetIdentifier"];
236
259
  const _excluded3 = ["assetId"];
237
260
  const _excluded4 = ["assetId"];
238
- const _excluded5 = ["folderId"];
261
+ const _excluded5 = ["assetId"];
262
+ const _excluded6 = ["folderId"];
239
263
  const assertValidUploadMimeType = (mimeType) => {
240
264
  if (isRejectedUploadMimeType(mimeType)) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_MIME_TYPE, {
241
265
  message: getRejectedUploadMimeTypeError(mimeType),
242
266
  details: { mimeType }
243
267
  });
244
268
  };
269
+ const toFailureError = (failure) => {
270
+ if (failure === "transport-error") return calmLensError({ id: "sdk/transport-error" }, {
271
+ message: "Request failed",
272
+ cause: /* @__PURE__ */ new TypeError("Simulated network failure")
273
+ });
274
+ if (failure === "timeout") return calmLensError({ id: "sdk/timeout" }, {
275
+ message: "Request aborted",
276
+ cause: new DOMException("The operation timed out.", "TimeoutError")
277
+ });
278
+ return failure;
279
+ };
245
280
  var MockCalmLensClient = class {
281
+ /** Accepts the same options as `CalmLensClient` (validated the same way), plus `mockOptions`. */
246
282
  constructor(options) {
247
- var _options$mockOptions$, _options$mockOptions, _options$mockOptions$2, _options$mockOptions2, _options$mockOptions$3, _options$mockOptions3, _options$mockOptions$4, _options$mockOptions4, _options$mockOptions$5, _options$mockOptions5, _options$mockOptions$6, _options$mockOptions6, _options$mockOptions$7, _options$mockOptions7, _options$mockOptions$8, _options$mockOptions8;
283
+ var _options$mockOptions$, _options$mockOptions, _options$mockOptions$2, _options$mockOptions2, _options$mockOptions$3, _options$mockOptions3, _options$mockOptions$4, _options$mockOptions4, _options$mockOptions$5, _options$mockOptions5, _options$mockOptions$6, _options$mockOptions6, _options$mockOptions$7, _options$mockOptions7, _options$cdnBaseUrl;
248
284
  this.options = options;
249
285
  this.lenses = /* @__PURE__ */ new Map();
250
- if (!options.apiKey) throw new Error("API key is required");
251
- if (!options.projectId) throw new Error("Project ID is required");
286
+ this.pendingFailures = [];
287
+ this.calls = [];
288
+ validateClientOptions(options);
252
289
  this.databaseStorage = (_options$mockOptions$ = (_options$mockOptions = options.mockOptions) === null || _options$mockOptions === void 0 ? void 0 : _options$mockOptions.databaseStorage) !== null && _options$mockOptions$ !== void 0 ? _options$mockOptions$ : new InMemoryMockDatabaseStorage();
253
290
  this.imageStorage = (_options$mockOptions$2 = (_options$mockOptions2 = options.mockOptions) === null || _options$mockOptions2 === void 0 ? void 0 : _options$mockOptions2.imageStorage) !== null && _options$mockOptions$2 !== void 0 ? _options$mockOptions$2 : new InMemoryMockImageStorage();
254
291
  this.createId = (_options$mockOptions$3 = (_options$mockOptions3 = options.mockOptions) === null || _options$mockOptions3 === void 0 ? void 0 : _options$mockOptions3.createId) !== null && _options$mockOptions$3 !== void 0 ? _options$mockOptions$3 : crypto.randomUUID.bind(crypto);
@@ -257,7 +294,32 @@ var MockCalmLensClient = class {
257
294
  this.analyzedStatus = (_options$mockOptions$6 = (_options$mockOptions6 = options.mockOptions) === null || _options$mockOptions6 === void 0 ? void 0 : _options$mockOptions6.analyzedStatus) !== null && _options$mockOptions$6 !== void 0 ? _options$mockOptions$6 : "approved";
258
295
  this.apiKeyGrants = getMockApiKeyGrants(options.apiKey);
259
296
  this.signedUrlBaseUrl = (_options$mockOptions$7 = (_options$mockOptions7 = options.mockOptions) === null || _options$mockOptions7 === void 0 ? void 0 : _options$mockOptions7.signedUrlBaseUrl) !== null && _options$mockOptions$7 !== void 0 ? _options$mockOptions$7 : "mock://calmlens";
260
- this.cdnBaseUrl = (_options$mockOptions$8 = (_options$mockOptions8 = options.mockOptions) === null || _options$mockOptions8 === void 0 ? void 0 : _options$mockOptions8.cdnBaseUrl) !== null && _options$mockOptions$8 !== void 0 ? _options$mockOptions$8 : "mock://calmlens/v1";
297
+ this.cdnBaseUrl = (_options$cdnBaseUrl = options.cdnBaseUrl) !== null && _options$cdnBaseUrl !== void 0 ? _options$cdnBaseUrl : "mock://calmlens/v1";
298
+ }
299
+ /**
300
+ * Makes the next call (or the next call to `operation`) throw `failure` instead of running,
301
+ * as if the request never completed. Queue several to fail several calls in a row.
302
+ */
303
+ failNextRequest(failure = "transport-error", { operation } = {}) {
304
+ this.pendingFailures.push({
305
+ failure,
306
+ operation
307
+ });
308
+ }
309
+ /** Calls recorded for one operation, oldest first. */
310
+ callsTo(operation) {
311
+ return this.calls.filter((call) => call.operation === operation);
312
+ }
313
+ begin(operation, options) {
314
+ this.calls.push({
315
+ operation,
316
+ options
317
+ });
318
+ const index = this.pendingFailures.findIndex((pending) => pending.operation === void 0 || pending.operation === operation);
319
+ if (index !== -1) {
320
+ const [pending] = this.pendingFailures.splice(index, 1);
321
+ throw toFailureError(pending.failure);
322
+ }
261
323
  }
262
324
  get cdn() {
263
325
  var _this = this;
@@ -265,16 +327,17 @@ var MockCalmLensClient = class {
265
327
  getAssetUrl: (_ref) => {
266
328
  var _options$resource;
267
329
  let { assetIdentifier } = _ref, options = _objectWithoutProperties(_ref, _excluded);
268
- if (!assetIdentifier) throw new Error("Asset identifier is required");
330
+ if (!assetIdentifier) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "Asset identifier is required" });
269
331
  const resource = (_options$resource = options.resource) !== null && _options$resource !== void 0 ? _options$resource : "content";
270
332
  const url = `${this.cdnBaseUrl.replace(/\/$/, "")}/assets/${encodeURIComponent(this.options.projectId)}/${encodeURIComponent(assetIdentifier)}/${resource}`;
271
333
  return appendCdnSelector(url, options);
272
334
  },
273
335
  serve: function() {
274
- var _ref3 = _asyncToGenerator(function* (_ref2) {
336
+ var _ref2 = _asyncToGenerator(function* (serveOptions) {
275
337
  var _asset$folderId, _await$this$databaseS, _this$databaseStorage, _this$databaseStorage2, _storedContent$type;
276
- let { assetIdentifier } = _ref2, options = _objectWithoutProperties(_ref2, _excluded2);
277
- if (!assetIdentifier) throw new Error("Asset identifier is required");
338
+ _this.begin("cdn.serve", serveOptions);
339
+ const { assetIdentifier } = serveOptions, options = _objectWithoutProperties(serveOptions, _excluded2);
340
+ if (!assetIdentifier) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "Asset identifier is required" });
278
341
  const asset = (yield _this.databaseStorage.listAssets(_this.options.projectId)).find((asset) => asset.id === assetIdentifier || asset.externalId === assetIdentifier);
279
342
  if (!asset) return new Response("Asset not found", { status: 404 });
280
343
  if (asset.visibility !== "public" && !permits(_this.apiKeyGrants, "assets:download", (_asset$folderId = asset.folderId) !== null && _asset$folderId !== void 0 ? _asset$folderId : null, (_await$this$databaseS = yield (_this$databaseStorage = (_this$databaseStorage2 = _this.databaseStorage).listFolders) === null || _this$databaseStorage === void 0 ? void 0 : _this$databaseStorage.call(_this$databaseStorage2, _this.options.projectId)) !== null && _await$this$databaseS !== void 0 ? _await$this$databaseS : [])) return new Response(JSON.stringify({
@@ -303,7 +366,7 @@ var MockCalmLensClient = class {
303
366
  });
304
367
  });
305
368
  return function serve(_x) {
306
- return _ref3.apply(this, arguments);
369
+ return _ref2.apply(this, arguments);
307
370
  };
308
371
  }()
309
372
  };
@@ -313,8 +376,9 @@ var MockCalmLensClient = class {
313
376
  return {
314
377
  /** The mock does not record events; it always returns an empty page at "now". */
315
378
  list: function() {
316
- var _ref4 = _asyncToGenerator(function* (options = {}) {
379
+ var _ref3 = _asyncToGenerator(function* (options = {}) {
317
380
  var _options$after;
381
+ _this2.begin("events.list", options);
318
382
  return {
319
383
  events: [],
320
384
  cursor: (_options$after = options.after) !== null && _options$after !== void 0 ? _options$after : `${_this2.now()}_`,
@@ -322,18 +386,19 @@ list: function() {
322
386
  };
323
387
  });
324
388
  return function list() {
325
- return _ref4.apply(this, arguments);
389
+ return _ref3.apply(this, arguments);
326
390
  };
327
391
  }() };
328
392
  }
329
393
  get text() {
330
394
  var _this3 = this;
331
395
  return { moderate: function() {
332
- var _ref5 = _asyncToGenerator(function* (options) {
333
- return _this3.assets.moderate(toTextAssetOptions(options));
396
+ var _ref4 = _asyncToGenerator(function* (options) {
397
+ _this3.begin("text.moderate", options);
398
+ return _this3.moderateAsset(toTextAssetOptions(options));
334
399
  });
335
400
  return function moderate(_x2) {
336
- return _ref5.apply(this, arguments);
401
+ return _ref4.apply(this, arguments);
337
402
  };
338
403
  }() };
339
404
  }
@@ -341,62 +406,83 @@ list: function() {
341
406
  var _this4 = this;
342
407
  return {
343
408
  list: function() {
344
- var _ref6 = _asyncToGenerator(function* (options) {
409
+ var _ref5 = _asyncToGenerator(function* (options) {
345
410
  var _options$pageIndex;
411
+ _this4.begin("assets.list", options);
346
412
  const assets = yield _this4.databaseStorage.listAssets(_this4.options.projectId);
347
413
  return createPage(assets, _objectSpread2(_objectSpread2({}, options), {}, { pageIndex: (_options$pageIndex = options.pageIndex) !== null && _options$pageIndex !== void 0 ? _options$pageIndex : 0 }));
348
414
  });
349
415
  return function list(_x3) {
350
- return _ref6.apply(this, arguments);
416
+ return _ref5.apply(this, arguments);
351
417
  };
352
418
  }(),
353
419
  retrieve: function() {
354
- var _ref7 = _asyncToGenerator(function* ({ assetId }) {
355
- if (!assetId) throw new Error("Asset ID is required");
420
+ var _ref6 = _asyncToGenerator(function* (options) {
421
+ _this4.begin("assets.retrieve", options);
422
+ const { assetId } = options;
423
+ if (!assetId) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "Asset ID is required" });
356
424
  const asset = yield _this4.databaseStorage.getAsset(assetId);
357
- if (!asset) throw new Error("Asset not found");
425
+ if (!asset) throw calmLensError(PUBLIC_ERROR_CODES.ASSET_NOT_FOUND);
358
426
  return asset;
359
427
  });
360
428
  return function retrieve(_x4) {
361
- return _ref7.apply(this, arguments);
429
+ return _ref6.apply(this, arguments);
362
430
  };
363
431
  }(),
364
432
  listChildren: function() {
365
- var _ref9 = _asyncToGenerator(function* (_ref8) {
433
+ var _ref7 = _asyncToGenerator(function* (childrenOptions) {
366
434
  var _options$pageIndex2;
367
- let { assetId } = _ref8, options = _objectWithoutProperties(_ref8, _excluded3);
368
- if (!assetId) throw new Error("Asset ID is required");
435
+ _this4.begin("assets.listChildren", childrenOptions);
436
+ const { assetId } = childrenOptions, options = _objectWithoutProperties(childrenOptions, _excluded3);
437
+ if (!assetId) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "Asset ID is required" });
369
438
  const assets = yield _this4.databaseStorage.listAssets(_this4.options.projectId);
370
439
  return createPage(assets.filter((asset) => asset.parentId === assetId), _objectSpread2(_objectSpread2({}, options), {}, { pageIndex: (_options$pageIndex2 = options.pageIndex) !== null && _options$pageIndex2 !== void 0 ? _options$pageIndex2 : 0 }));
371
440
  });
372
441
  return function listChildren(_x5) {
373
- return _ref9.apply(this, arguments);
442
+ return _ref7.apply(this, arguments);
443
+ };
444
+ }(),
445
+ update: function() {
446
+ var _ref8 = _asyncToGenerator(function* (options) {
447
+ _this4.begin("assets.update", options);
448
+ const { assetId } = options, body = _objectWithoutProperties(options, _excluded4);
449
+ if (!assetId) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "Asset ID is required" });
450
+ const asset = yield _this4.databaseStorage.getAsset(assetId);
451
+ if (!asset || asset.projectId !== _this4.options.projectId) throw calmLensError(PUBLIC_ERROR_CODES.ASSET_NOT_FOUND);
452
+ const changes = Object.fromEntries(Object.entries(body).filter(([, value]) => value !== void 0));
453
+ return _this4.databaseStorage.updateAsset(_objectSpread2(_objectSpread2(_objectSpread2({}, asset), changes), {}, { updatedAt: _this4.now() }));
454
+ });
455
+ return function update(_x6) {
456
+ return _ref8.apply(this, arguments);
374
457
  };
375
458
  }(),
376
459
  create: function() {
377
- var _ref10 = _asyncToGenerator(function* (options) {
460
+ var _ref9 = _asyncToGenerator(function* (options) {
461
+ _this4.begin("assets.create", options);
378
462
  return _this4.createAsset(options, _this4.queuedStatus);
379
463
  });
380
- return function create(_x6) {
381
- return _ref10.apply(this, arguments);
464
+ return function create(_x7) {
465
+ return _ref9.apply(this, arguments);
382
466
  };
383
467
  }(),
384
468
  moderate: function() {
385
- var _ref11 = _asyncToGenerator(function* (options) {
386
- if ("content" in options && "plainText" in options.content && (typeof options.content.plainText !== "string" || !options.content.plainText.trim())) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "Text to moderate must be a non-empty string" });
387
- return _this4.createAsset(options, _this4.analyzedStatus);
469
+ var _ref10 = _asyncToGenerator(function* (options) {
470
+ _this4.begin("assets.moderate", options);
471
+ return _this4.moderateAsset(options);
388
472
  });
389
- return function moderate(_x7) {
390
- return _ref11.apply(this, arguments);
473
+ return function moderate(_x8) {
474
+ return _ref10.apply(this, arguments);
391
475
  };
392
476
  }(),
393
477
  uploadPart: function() {
394
- var _ref12 = _asyncToGenerator(function* ({ assetId, part }) {
478
+ var _ref11 = _asyncToGenerator(function* (options) {
395
479
  var _asset$uploadPartsRec;
396
- if (!assetId) throw new Error("Asset ID is required");
397
- if (!part) throw new Error("Part options are required");
480
+ _this4.begin("assets.uploadPart", options);
481
+ const { assetId, part } = options;
482
+ if (!assetId) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "Asset ID is required" });
483
+ if (!part) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "Part options are required" });
398
484
  const asset = yield _this4.databaseStorage.getAsset(assetId);
399
- if (!asset) throw new Error("Asset not found");
485
+ if (!asset) throw calmLensError(PUBLIC_ERROR_CODES.ASSET_NOT_FOUND);
400
486
  const validation = validateAssetPayload(part, {
401
487
  multipart: true,
402
488
  isFinal: part.isFinal
@@ -410,32 +496,35 @@ list: function() {
410
496
  });
411
497
  return _this4.databaseStorage.updateAsset(updated);
412
498
  });
413
- return function uploadPart(_x8) {
414
- return _ref12.apply(this, arguments);
499
+ return function uploadPart(_x9) {
500
+ return _ref11.apply(this, arguments);
415
501
  };
416
502
  }(),
417
503
  createSignedUrl: function() {
418
- var _ref14 = _asyncToGenerator(function* (_ref13) {
504
+ var _ref12 = _asyncToGenerator(function* (signedUrlOptions) {
419
505
  var _options$expiresInSec, _options$resource2;
420
- let { assetId } = _ref13, options = _objectWithoutProperties(_ref13, _excluded4);
421
- if (!assetId) throw new Error("Asset ID is required");
422
- if (!(yield _this4.databaseStorage.getAsset(assetId))) throw new Error("Asset not found");
506
+ _this4.begin("assets.createSignedUrl", signedUrlOptions);
507
+ const { assetId } = signedUrlOptions, options = _objectWithoutProperties(signedUrlOptions, _excluded5);
508
+ if (!assetId) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "Asset ID is required" });
509
+ if (!(yield _this4.databaseStorage.getAsset(assetId))) throw calmLensError(PUBLIC_ERROR_CODES.ASSET_NOT_FOUND);
423
510
  const expiresInSeconds = (_options$expiresInSec = options.expiresInSeconds) !== null && _options$expiresInSec !== void 0 ? _options$expiresInSec : 900;
424
511
  const resource = (_options$resource2 = options.resource) !== null && _options$resource2 !== void 0 ? _options$resource2 : "content";
425
- if (options.lens && options.transform) throw new Error("Specify either a lens or a transform, not both");
512
+ if (options.lens && options.transform) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "Specify either a lens or a transform, not both" });
426
513
  const selector = options.lens ? `&lens=${encodeURIComponent(options.lens)}` : options.transform ? `&transform=${encodeURIComponent(options.transform)}` : "";
427
514
  return {
428
515
  url: `${_this4.signedUrlBaseUrl}/assets/${assetId}/${resource}?expiresInSeconds=${expiresInSeconds}${selector}`,
429
516
  expiresAt: _this4.now() + expiresInSeconds * 1e3
430
517
  };
431
518
  });
432
- return function createSignedUrl(_x9) {
433
- return _ref14.apply(this, arguments);
519
+ return function createSignedUrl(_x10) {
520
+ return _ref12.apply(this, arguments);
434
521
  };
435
522
  }(),
436
523
  move: function() {
437
- var _ref15 = _asyncToGenerator(function* ({ assetId, folderId }) {
438
- if (!assetId) throw new Error("Asset ID is required");
524
+ var _ref13 = _asyncToGenerator(function* (options) {
525
+ _this4.begin("assets.move", options);
526
+ const { assetId, folderId } = options;
527
+ if (!assetId) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "Asset ID is required" });
439
528
  const asset = yield _this4.databaseStorage.getAsset(assetId);
440
529
  if (!asset || asset.projectId !== _this4.options.projectId) throw calmLensError(PUBLIC_ERROR_CODES.ASSET_NOT_FOUND);
441
530
  if (folderId !== null && folderId !== void 0) {
@@ -447,13 +536,15 @@ list: function() {
447
536
  });
448
537
  return _this4.databaseStorage.updateAsset(updated);
449
538
  });
450
- return function move(_x10) {
451
- return _ref15.apply(this, arguments);
539
+ return function move(_x11) {
540
+ return _ref13.apply(this, arguments);
452
541
  };
453
542
  }(),
454
543
  retry: function() {
455
- var _ref16 = _asyncToGenerator(function* ({ assetId }) {
456
- if (!assetId) throw new Error("Asset ID is required");
544
+ var _ref14 = _asyncToGenerator(function* (options) {
545
+ _this4.begin("assets.retry", options);
546
+ const { assetId } = options;
547
+ if (!assetId) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "Asset ID is required" });
457
548
  const asset = yield _this4.databaseStorage.getAsset(assetId);
458
549
  if (!asset || asset.projectId !== _this4.options.projectId) throw calmLensError(PUBLIC_ERROR_CODES.ASSET_NOT_FOUND);
459
550
  if (asset.status !== "error") throw calmLensError(PUBLIC_ERROR_CODES.ASSET_NOT_RETRYABLE);
@@ -462,8 +553,8 @@ list: function() {
462
553
  updatedAt: _this4.now()
463
554
  }));
464
555
  });
465
- return function retry(_x11) {
466
- return _ref16.apply(this, arguments);
556
+ return function retry(_x12) {
557
+ return _ref14.apply(this, arguments);
467
558
  };
468
559
  }()
469
560
  };
@@ -472,28 +563,32 @@ list: function() {
472
563
  var _this5 = this;
473
564
  return {
474
565
  list: function() {
475
- var _ref17 = _asyncToGenerator(function* () {
566
+ var _ref15 = _asyncToGenerator(function* () {
567
+ _this5.begin("folders.list", void 0);
476
568
  return [..._this5.databaseStorage.listFolders ? yield _this5.databaseStorage.listFolders(_this5.options.projectId) : []].sort((a, b) => a.name.localeCompare(b.name));
477
569
  });
478
570
  return function list() {
479
- return _ref17.apply(this, arguments);
571
+ return _ref15.apply(this, arguments);
480
572
  };
481
573
  }(),
482
574
  retrieve: function() {
483
- var _ref18 = _asyncToGenerator(function* ({ folderId }) {
484
- if (!folderId) throw new Error("Folder ID is required");
575
+ var _ref16 = _asyncToGenerator(function* (options) {
576
+ _this5.begin("folders.retrieve", options);
577
+ const { folderId } = options;
578
+ if (!folderId) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "Folder ID is required" });
485
579
  const folder = yield _this5.resolveFolder(folderId);
486
580
  if (!folder) throw calmLensError(PUBLIC_ERROR_CODES.FOLDER_NOT_FOUND);
487
581
  return folder;
488
582
  });
489
- return function retrieve(_x12) {
490
- return _ref18.apply(this, arguments);
583
+ return function retrieve(_x13) {
584
+ return _ref16.apply(this, arguments);
491
585
  };
492
586
  }(),
493
587
  create: function() {
494
- var _ref19 = _asyncToGenerator(function* (options) {
588
+ var _ref17 = _asyncToGenerator(function* (options) {
495
589
  var _options$parentId, _options$parentId2;
496
- if (!options.name) throw new Error("Folder name is required");
590
+ _this5.begin("folders.create", options);
591
+ if (!options.name) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "Folder name is required" });
497
592
  if (options.parentId) {
498
593
  if (!(yield _this5.resolveFolder(options.parentId))) throw calmLensError(PUBLIC_ERROR_CODES.FOLDER_NOT_FOUND);
499
594
  }
@@ -509,14 +604,15 @@ list: function() {
509
604
  if (_this5.databaseStorage.createFolder) return yield _this5.databaseStorage.createFolder(folder);
510
605
  return folder;
511
606
  });
512
- return function create(_x13) {
513
- return _ref19.apply(this, arguments);
607
+ return function create(_x14) {
608
+ return _ref17.apply(this, arguments);
514
609
  };
515
610
  }(),
516
611
  update: function() {
517
- var _ref21 = _asyncToGenerator(function* (_ref20) {
518
- let { folderId } = _ref20, body = _objectWithoutProperties(_ref20, _excluded5);
519
- if (!folderId) throw new Error("Folder ID is required");
612
+ var _ref18 = _asyncToGenerator(function* (options) {
613
+ _this5.begin("folders.update", options);
614
+ const { folderId } = options, body = _objectWithoutProperties(options, _excluded6);
615
+ if (!folderId) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "Folder ID is required" });
520
616
  const folder = yield _this5.resolveFolder(folderId);
521
617
  if (!folder) throw calmLensError(PUBLIC_ERROR_CODES.FOLDER_NOT_FOUND);
522
618
  if (body.name !== void 0 && body.name !== folder.name) {
@@ -527,13 +623,15 @@ list: function() {
527
623
  if (_this5.databaseStorage.updateFolder) return yield _this5.databaseStorage.updateFolder(updated);
528
624
  return updated;
529
625
  });
530
- return function update(_x14) {
531
- return _ref21.apply(this, arguments);
626
+ return function update(_x15) {
627
+ return _ref18.apply(this, arguments);
532
628
  };
533
629
  }(),
534
630
  delete: function() {
535
- var _ref22 = _asyncToGenerator(function* ({ folderId }) {
536
- if (!folderId) throw new Error("Folder ID is required");
631
+ var _ref19 = _asyncToGenerator(function* (options) {
632
+ _this5.begin("folders.delete", options);
633
+ const { folderId } = options;
634
+ if (!folderId) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "Folder ID is required" });
537
635
  if (!(yield _this5.resolveFolder(folderId))) throw calmLensError(PUBLIC_ERROR_CODES.FOLDER_NOT_FOUND);
538
636
  const hasAssets = (yield _this5.databaseStorage.listAssets(_this5.options.projectId)).some((asset) => asset.folderId === folderId);
539
637
  const hasChildren = (_this5.databaseStorage.listFolders ? yield _this5.databaseStorage.listFolders(_this5.options.projectId) : []).some((f) => f.parentId === folderId);
@@ -543,13 +641,15 @@ list: function() {
543
641
  } });
544
642
  if (_this5.databaseStorage.deleteFolder) yield _this5.databaseStorage.deleteFolder(folderId);
545
643
  });
546
- return function _delete(_x15) {
547
- return _ref22.apply(this, arguments);
644
+ return function _delete(_x16) {
645
+ return _ref19.apply(this, arguments);
548
646
  };
549
647
  }(),
550
648
  move: function() {
551
- var _ref23 = _asyncToGenerator(function* ({ folderId, parentId }) {
552
- if (!folderId) throw new Error("Folder ID is required");
649
+ var _ref20 = _asyncToGenerator(function* (options) {
650
+ _this5.begin("folders.move", options);
651
+ const { folderId, parentId } = options;
652
+ if (!folderId) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "Folder ID is required" });
553
653
  const folder = yield _this5.resolveFolder(folderId);
554
654
  if (!folder) throw calmLensError(PUBLIC_ERROR_CODES.FOLDER_NOT_FOUND);
555
655
  const targetParentId = parentId !== null && parentId !== void 0 ? parentId : null;
@@ -572,8 +672,8 @@ list: function() {
572
672
  if (_this5.databaseStorage.updateFolder) return yield _this5.databaseStorage.updateFolder(updated);
573
673
  return updated;
574
674
  });
575
- return function move(_x16) {
576
- return _ref23.apply(this, arguments);
675
+ return function move(_x17) {
676
+ return _ref20.apply(this, arguments);
577
677
  };
578
678
  }()
579
679
  };
@@ -581,46 +681,58 @@ list: function() {
581
681
  ensureLens(options) {
582
682
  var _this6 = this;
583
683
  return _asyncToGenerator(function* () {
584
- var _existing$description, _options$description, _existing$transforms, _options$transforms;
585
- const existing = _this6.lenses.get(options.identifier);
586
- if (!existing) {
587
- const lens = _objectSpread2(_objectSpread2({
588
- id: _this6.createId(),
589
- projectId: _this6.options.projectId,
590
- userId: null
591
- }, options), {}, {
592
- createdAt: _this6.now(),
593
- updatedAt: _this6.now()
594
- });
595
- _this6.lenses.set(lens.identifier, lens);
596
- return lens;
597
- }
598
- if (existing.name === options.name && ((_existing$description = existing.description) !== null && _existing$description !== void 0 ? _existing$description : null) === ((_options$description = options.description) !== null && _options$description !== void 0 ? _options$description : null) && existing.type === options.type && existing.active === options.active && stringifyJsonCanonical((_existing$transforms = existing.transforms) !== null && _existing$transforms !== void 0 ? _existing$transforms : null) === stringifyJsonCanonical((_options$transforms = options.transforms) !== null && _options$transforms !== void 0 ? _options$transforms : null) && existing.version === options.version) return existing;
599
- if (options.version > existing.version) {
600
- const lens = _objectSpread2(_objectSpread2(_objectSpread2({}, existing), options), {}, { updatedAt: _this6.now() });
601
- _this6.lenses.set(lens.identifier, lens);
602
- return lens;
603
- }
604
- throw calmLensError(PUBLIC_ERROR_CODES.LENS_VERSION_CONFLICT, { details: {
605
- currentVersion: existing.version,
606
- minimumVersion: existing.version + 1,
607
- requestedVersion: options.version
608
- } });
684
+ _this6.begin("ensureLens", options);
685
+ return _this6.ensureLensDefinition(options);
609
686
  })();
610
687
  }
611
- ensureLenses({ lenses }) {
688
+ ensureLensDefinition(options) {
689
+ var _existing$description, _options$description, _existing$transforms, _options$transforms;
690
+ const existing = this.lenses.get(options.identifier);
691
+ if (!existing) {
692
+ const lens = _objectSpread2(_objectSpread2({
693
+ id: this.createId(),
694
+ projectId: this.options.projectId,
695
+ userId: null
696
+ }, options), {}, {
697
+ createdAt: this.now(),
698
+ updatedAt: this.now()
699
+ });
700
+ this.lenses.set(lens.identifier, lens);
701
+ return lens;
702
+ }
703
+ if (existing.name === options.name && ((_existing$description = existing.description) !== null && _existing$description !== void 0 ? _existing$description : null) === ((_options$description = options.description) !== null && _options$description !== void 0 ? _options$description : null) && existing.type === options.type && existing.active === options.active && stringifyJsonCanonical((_existing$transforms = existing.transforms) !== null && _existing$transforms !== void 0 ? _existing$transforms : null) === stringifyJsonCanonical((_options$transforms = options.transforms) !== null && _options$transforms !== void 0 ? _options$transforms : null) && existing.version === options.version) return existing;
704
+ if (options.version > existing.version) {
705
+ const lens = _objectSpread2(_objectSpread2(_objectSpread2({}, existing), options), {}, { updatedAt: this.now() });
706
+ this.lenses.set(lens.identifier, lens);
707
+ return lens;
708
+ }
709
+ throw calmLensError(PUBLIC_ERROR_CODES.LENS_VERSION_CONFLICT, { details: {
710
+ currentVersion: existing.version,
711
+ minimumVersion: existing.version + 1,
712
+ requestedVersion: options.version
713
+ } });
714
+ }
715
+ ensureLenses(options) {
612
716
  var _this7 = this;
613
717
  return _asyncToGenerator(function* () {
614
- if (lenses.length === 0) throw new Error("At least one lens is required");
615
- return yield Promise.all(lenses.map((option) => _this7.ensureLens(option)));
718
+ _this7.begin("ensureLenses", options);
719
+ if (options.lenses.length === 0) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "At least one lens is required" });
720
+ return options.lenses.map((option) => _this7.ensureLensDefinition(option));
616
721
  })();
617
722
  }
618
- createAsset(options, status) {
723
+ moderateAsset(options) {
619
724
  var _this8 = this;
725
+ return _asyncToGenerator(function* () {
726
+ if ("content" in options && "plainText" in options.content && (typeof options.content.plainText !== "string" || !options.content.plainText.trim())) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "Text to moderate must be a non-empty string" });
727
+ return _this8.createAsset(options, _this8.analyzedStatus);
728
+ })();
729
+ }
730
+ createAsset(options, status) {
731
+ var _this9 = this;
620
732
  return _asyncToGenerator(function* () {
621
733
  var _options$folderId, _options$visibility, _options$part$partNum;
622
- if (!("content" in options) && !("url" in options) && !("part" in options)) throw new Error("Either file content, URL, or multipart part is required");
623
- if (!options.name) throw new Error("Asset name is required");
734
+ if (!("content" in options) && !("url" in options) && !("part" in options)) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "Either file content, URL, or multipart part is required" });
735
+ if (!options.name) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "Asset name is required" });
624
736
  if (!("part" in options) && "isFinal" in options) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: "isFinal is only valid for multipart uploads" });
625
737
  if ("part" in options && options.part.partNumber && options.part.partNumber > 1) throw calmLensError(PUBLIC_ERROR_CODES.USE_UPLOAD_PART);
626
738
  if ("content" in options) {
@@ -638,13 +750,13 @@ list: function() {
638
750
  });
639
751
  if (!validation.ok) throw calmLensError(PUBLIC_ERROR_CODES.INVALID_UPLOAD_PAYLOAD, { message: validation.error });
640
752
  }
641
- const id = _this8.createId();
642
- const now = _this8.now();
753
+ const id = _this9.createId();
754
+ const now = _this9.now();
643
755
  const type = inferAssetType(options);
644
- const sizeInBytes = _this8.getSizeInBytes(options);
756
+ const sizeInBytes = _this9.getSizeInBytes(options);
645
757
  const asset = {
646
758
  id,
647
- projectId: _this8.options.projectId,
759
+ projectId: _this9.options.projectId,
648
760
  folderId: (_options$folderId = options.folderId) !== null && _options$folderId !== void 0 ? _options$folderId : null,
649
761
  createdAt: now,
650
762
  updatedAt: now,
@@ -661,24 +773,24 @@ list: function() {
661
773
  uploadPartsReceived: "part" in options ? (_options$part$partNum = options.part.partNumber) !== null && _options$part$partNum !== void 0 ? _options$part$partNum : 1 : void 0
662
774
  };
663
775
  if ("content" in options) {
664
- if (!("plainText" in options.content)) yield _this8.imageStorage.putContent(id, options.content);
665
- } else if ("part" in options) yield _this8.imageStorage.putPart(id, options.part);
666
- return _this8.databaseStorage.createAsset(asset);
776
+ if (!("plainText" in options.content)) yield _this9.imageStorage.putContent(id, options.content);
777
+ } else if ("part" in options) yield _this9.imageStorage.putPart(id, options.part);
778
+ return _this9.databaseStorage.createAsset(asset);
667
779
  })();
668
780
  }
669
781
  resolveFolder(folderId) {
670
- var _this9 = this;
782
+ var _this10 = this;
671
783
  return _asyncToGenerator(function* () {
672
- if (!_this9.databaseStorage.getFolder) return void 0;
673
- const folder = yield _this9.databaseStorage.getFolder(folderId);
674
- if (!folder || folder.projectId !== _this9.options.projectId) return;
784
+ if (!_this10.databaseStorage.getFolder) return void 0;
785
+ const folder = yield _this10.databaseStorage.getFolder(folderId);
786
+ if (!folder || folder.projectId !== _this10.options.projectId) return;
675
787
  return folder;
676
788
  })();
677
789
  }
678
790
  assertUniqueFolderName(name, parentId, excludeFolderId) {
679
- var _this10 = this;
791
+ var _this11 = this;
680
792
  return _asyncToGenerator(function* () {
681
- if ((_this10.databaseStorage.listFolders ? yield _this10.databaseStorage.listFolders(_this10.options.projectId) : []).find((f) => f.id !== excludeFolderId && f.parentId === (parentId !== null && parentId !== void 0 ? parentId : null) && f.name === name)) throw calmLensError(PUBLIC_ERROR_CODES.DUPLICATE_FOLDER_NAME, { message: `A folder named "${name}" already exists in this location.` });
793
+ if ((_this11.databaseStorage.listFolders ? yield _this11.databaseStorage.listFolders(_this11.options.projectId) : []).find((f) => f.id !== excludeFolderId && f.parentId === (parentId !== null && parentId !== void 0 ? parentId : null) && f.name === name)) throw calmLensError(PUBLIC_ERROR_CODES.DUPLICATE_FOLDER_NAME, { message: `A folder named "${name}" already exists in this location.` });
682
794
  })();
683
795
  }
684
796
  getSizeInBytes(options) {