@verentis/sdk 0.2.6 → 1.0.1

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/dist/index.js CHANGED
@@ -259,6 +259,7 @@ var ContextModule = class {
259
259
  };
260
260
 
261
261
  // src/modules/executions.ts
262
+ var SCRIPT_ARGUMENTS_SCHEMA = "verentis.io/script-arguments/v1";
262
263
  var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
263
264
  "Completed",
264
265
  "Failed",
@@ -289,7 +290,8 @@ var ExecutionsModule = class {
289
290
  tool: options?.tool,
290
291
  mode: options?.mode,
291
292
  arguments: options?.arguments ?? [],
292
- parameters: serializeParameters(options?.parameters)
293
+ parameters: serializeParameters(options?.parameters),
294
+ ...options?.invocationKey ? { invocationKey: options.invocationKey } : {}
293
295
  }),
294
296
  headers: { "Content-Type": "application/json" }
295
297
  });
@@ -314,10 +316,30 @@ var ExecutionsModule = class {
314
316
  headers: { "Content-Type": "application/json" }
315
317
  });
316
318
  }
319
+ /**
320
+ * Read the arguments a script declares (its exported `<script>.arguments.json`), e.g. to build a form
321
+ * before {@link run}. Returns `null` for scripts that declare none. Reading never runs the script.
322
+ */
323
+ async getArguments(filePath, options) {
324
+ const path = `${filePath}.arguments.json`.replace(/\/+/g, "/").replace(/^\//, "");
325
+ const response = await this.http.requestRaw("GET", `/files/raw/${path}`, {
326
+ params: { branch: options?.branch ?? "main" },
327
+ headers: { ...this.workspaceId ? { "X-Workspace-Id": this.workspaceId } : {}, Accept: "application/json" }
328
+ });
329
+ if (response.status === 404) return null;
330
+ if (!response.ok) {
331
+ throw new Error(`Failed to read script arguments: ${response.status} ${response.statusText}`);
332
+ }
333
+ const descriptor = await response.json();
334
+ if (descriptor?.schema !== SCRIPT_ARGUMENTS_SCHEMA || !Array.isArray(descriptor.arguments)) {
335
+ throw new Error(`Unsupported script argument descriptor for ${filePath}`);
336
+ }
337
+ return descriptor;
338
+ }
317
339
  /** Get an execution by id. */
318
- async get(id, options) {
340
+ async get(id2, options) {
319
341
  const workspaceId = this.requireWorkspaceId(options?.workspaceId);
320
- return this.http.request("GET", `/executions/${encodeURIComponent(id)}`, {
342
+ return this.http.request("GET", `/executions/${encodeURIComponent(id2)}`, {
321
343
  params: { workspaceId }
322
344
  });
323
345
  }
@@ -342,17 +364,17 @@ var ExecutionsModule = class {
342
364
  });
343
365
  }
344
366
  /** Get the captured stdout/stderr of an execution. */
345
- async logs(id, options) {
367
+ async logs(id2, options) {
346
368
  const workspaceId = this.requireWorkspaceId(options?.workspaceId);
347
- return this.http.request("GET", `/executions/${encodeURIComponent(id)}/logs`, {
369
+ return this.http.request("GET", `/executions/${encodeURIComponent(id2)}/logs`, {
348
370
  params: { workspaceId }
349
371
  });
350
372
  }
351
373
  /** Cancel a pending or running execution. */
352
- async cancel(id, options) {
374
+ async cancel(id2, options) {
353
375
  const workspaceId = this.requireWorkspaceId(options?.workspaceId);
354
- return this.http.request("POST", `/executions/${encodeURIComponent(id)}/cancel`, {
355
- body: JSON.stringify({ id, workspaceId }),
376
+ return this.http.request("POST", `/executions/${encodeURIComponent(id2)}/cancel`, {
377
+ body: JSON.stringify({ id: id2, workspaceId }),
356
378
  headers: { "Content-Type": "application/json" }
357
379
  });
358
380
  }
@@ -360,16 +382,16 @@ var ExecutionsModule = class {
360
382
  * Poll an execution until it reaches a terminal status. Returns immediately if it is already
361
383
  * terminal. Throws if `timeoutMs` elapses or the supplied signal aborts.
362
384
  */
363
- async wait(id, options) {
385
+ async wait(id2, options) {
364
386
  const interval = options?.intervalMs ?? 1e3;
365
387
  const deadline = options?.timeoutMs ? Date.now() + options.timeoutMs : void 0;
366
388
  for (; ; ) {
367
- const execution = await this.get(id, { workspaceId: options?.workspaceId });
389
+ const execution = await this.get(id2, { workspaceId: options?.workspaceId });
368
390
  if (isTerminalStatus(execution.status)) {
369
391
  return execution;
370
392
  }
371
393
  if (deadline !== void 0 && Date.now() >= deadline) {
372
- throw new Error(`Timed out waiting for execution '${id}' to complete`);
394
+ throw new Error(`Timed out waiting for execution '${id2}' to complete`);
373
395
  }
374
396
  await delay(interval, options?.signal);
375
397
  }
@@ -386,11 +408,11 @@ var ExecutionsModule = class {
386
408
  return this.wait(execution.id, options);
387
409
  }
388
410
  requireWorkspaceId(override) {
389
- const id = override ?? this.workspaceId;
390
- if (!id) {
411
+ const id2 = override ?? this.workspaceId;
412
+ if (!id2) {
391
413
  throw new Error("workspaceId is required: provide it in options or when creating the client");
392
414
  }
393
- return id;
415
+ return id2;
394
416
  }
395
417
  };
396
418
  function serializeParameters(parameters) {
@@ -431,8 +453,8 @@ var EnginesModule = class {
431
453
  }
432
454
  });
433
455
  }
434
- async get(id, options) {
435
- return this.http.request("GET", `/engines/${encodeURIComponent(id)}`, {
456
+ async get(id2, options) {
457
+ return this.http.request("GET", `/engines/${encodeURIComponent(id2)}`, {
436
458
  params: { workspaceId: this.requireWorkspaceId(options?.workspaceId) }
437
459
  });
438
460
  }
@@ -653,11 +675,11 @@ var FilesModule = class {
653
675
  });
654
676
  }
655
677
  workspaceHeaders() {
656
- const headers = {};
678
+ const headers2 = {};
657
679
  if (this.workspaceId) {
658
- headers["X-Workspace-Id"] = this.workspaceId;
680
+ headers2["X-Workspace-Id"] = this.workspaceId;
659
681
  }
660
- return headers;
682
+ return headers2;
661
683
  }
662
684
  };
663
685
  function requireNonBlank(value, name) {
@@ -749,14 +771,14 @@ var InstallationsModule = class {
749
771
  }
750
772
  });
751
773
  }
752
- async update(id, options) {
774
+ async update(id2, options) {
753
775
  const workspaceId = this.requireWorkspaceId(options?.workspaceId);
754
776
  return this.http.request(
755
777
  "PUT",
756
- `/installations/${encodeURIComponent(id)}`,
778
+ `/installations/${encodeURIComponent(id2)}`,
757
779
  {
758
780
  body: JSON.stringify({
759
- id,
781
+ id: id2,
760
782
  workspaceId,
761
783
  packageVersion: options?.packageVersion,
762
784
  installMode: options?.installMode
@@ -765,8 +787,8 @@ var InstallationsModule = class {
765
787
  }
766
788
  );
767
789
  }
768
- async uninstall(id, options) {
769
- await this.http.request("DELETE", `/installations/${encodeURIComponent(id)}`, {
790
+ async uninstall(id2, options) {
791
+ await this.http.request("DELETE", `/installations/${encodeURIComponent(id2)}`, {
770
792
  params: { workspaceId: this.requireWorkspaceId(options?.workspaceId) }
771
793
  });
772
794
  }
@@ -792,10 +814,10 @@ var ResourcesModule = class {
792
814
  params: { workspaceId: this.requireWorkspaceId(options?.workspaceId) }
793
815
  });
794
816
  }
795
- async get(id, options) {
817
+ async get(id2, options) {
796
818
  return this.http.request(
797
819
  "GET",
798
- `/resources/${encodeURIComponent(id)}`,
820
+ `/resources/${encodeURIComponent(id2)}`,
799
821
  { params: { workspaceId: this.requireWorkspaceId(options?.workspaceId) } }
800
822
  );
801
823
  }
@@ -847,7 +869,7 @@ var SettingsModule = class {
847
869
  }
848
870
  });
849
871
  }
850
- /** Upsert a setting value by its full key. Requires the `fabric.setting.update` scope. */
872
+ /** Upsert a setting value by its full key. Requires the `setting.write` scope. */
851
873
  async set(key, value, options) {
852
874
  await this.http.request("PUT", `/settings/${encodeURIComponent(key)}`, {
853
875
  params: {
@@ -858,7 +880,7 @@ var SettingsModule = class {
858
880
  headers: { "Content-Type": "application/json" }
859
881
  });
860
882
  }
861
- /** Delete a stored value (reverting to the definition default). Requires `fabric.setting.delete`. */
883
+ /** Delete a stored value (reverting to the definition default). Requires `setting.delete`. */
862
884
  async reset(key, options) {
863
885
  await this.http.request("DELETE", `/settings/${encodeURIComponent(key)}`, {
864
886
  params: {
@@ -905,104 +927,78 @@ var SettingsModule = class {
905
927
  }
906
928
  };
907
929
 
908
- // src/modules/email.ts
909
- var EmailModule = class {
930
+ // src/types.ts
931
+ var ApiError = class extends Error {
932
+ constructor(status, detail, title, code, retryAfterMs) {
933
+ super(title ?? `API error ${status}: ${detail}`);
934
+ this.status = status;
935
+ this.detail = detail;
936
+ this.title = title;
937
+ this.code = code;
938
+ this.retryAfterMs = retryAfterMs;
939
+ this.name = "ApiError";
940
+ }
941
+ status;
942
+ detail;
943
+ title;
944
+ code;
945
+ retryAfterMs;
946
+ };
947
+
948
+ // src/modules/notifications.ts
949
+ var NotificationsModule = class {
910
950
  constructor(http, workspaceId) {
911
951
  this.http = http;
912
952
  this.workspaceId = workspaceId;
913
953
  }
914
954
  http;
915
955
  workspaceId;
916
- async preview(installationId, options) {
917
- return this.http.request("GET", `/installations/${encodeURIComponent(installationId)}/email-consent-preview`, {
918
- params: { workspaceId: this.workspace(options) }
919
- });
920
- }
921
- async consents(options) {
922
- return this.http.request("GET", "/email/consents", { params: { workspaceId: this.workspace(options) } });
923
- }
924
- /** Approve exactly the reviewed signed candidate; tracking stays off unless explicitly opted in. */
925
- async consent(preview, approval, options) {
926
- if (approval !== true) throw new Error("Explicit email consent is required");
927
- if (!options?.senderGrants?.length) throw new Error("Explicit purpose/sender grants are required");
928
- return this.http.request("PUT", `/email/consents/${encodeURIComponent(preview.installationId)}`, {
929
- params: { workspaceId: preview.workspaceId },
956
+ async send(options) {
957
+ const request = {
930
958
  body: JSON.stringify({
931
- candidateDigest: preview.candidateDigest,
932
- enabled: true,
933
- allowOpenTracking: options?.allowOpenTracking ?? false,
934
- allowClickTracking: options?.allowClickTracking ?? false,
935
- senderGrants: options.senderGrants.map((grant) => ({ purpose: grant.purpose, senderId: grant.senderId }))
936
- }),
937
- headers: { "Content-Type": "application/json" }
938
- });
939
- }
940
- async revoke(consent, options) {
941
- return this.http.request("PUT", `/email/consents/${encodeURIComponent(consent.installationId)}`, {
942
- params: { workspaceId: this.workspace(options) },
943
- body: JSON.stringify({ candidateDigest: consent.candidateDigest, enabled: false, allowOpenTracking: false, allowClickTracking: false }),
944
- headers: { "Content-Type": "application/json" }
945
- });
946
- }
947
- async senders(options) {
948
- return this.http.request("GET", "/email/senders", { params: { workspaceId: this.workspace(options) } });
949
- }
950
- async invoicePdf(recordId, installationId, options) {
951
- this.mainBranch(options);
952
- return this.http.request("POST", "/email/artifacts/invoice", {
953
- body: JSON.stringify({ workspaceId: this.workspace(options), recordId, installationId }),
954
- headers: { "Content-Type": "application/json" }
955
- });
956
- }
957
- /** Authenticated immutable bytes; no public URL. The caller owns any local object URL it creates. */
958
- async artifactContent(artifactId, options) {
959
- this.mainBranch(options);
960
- return this.http.request("GET", `/email/artifacts/${encodeURIComponent(artifactId)}/content`, {
961
- params: { workspaceId: this.workspace(options), branch: "main" }
962
- });
963
- }
964
- /** A stable key binds this exact request. After a lost response inspect status; never automatically resend. */
965
- async dispatch(options) {
966
- this.mainBranch(options);
967
- return this.http.request("POST", "/email/dispatches", {
968
- body: JSON.stringify({
969
- workspaceId: this.workspace(options),
959
+ workspaceId: this.requireWorkspaceId(options.workspaceId),
970
960
  installationId: options.installationId,
971
- recordId: options.recordId,
972
- purpose: options.purpose,
973
- templateId: options.templateId,
974
- senderId: options.senderId,
975
- recipients: options.recipients,
976
- artifactIds: options.artifactIds,
961
+ typeKey: options.typeKey,
977
962
  idempotencyKey: options.idempotencyKey,
978
- replyTo: options.replyTo
963
+ userIds: options.userIds,
964
+ audienceScope: options.audienceScope,
965
+ emailAddresses: options.emailAddresses,
966
+ parameters: options.parameters
979
967
  }),
980
968
  headers: { "Content-Type": "application/json" }
981
- });
982
- }
983
- async outcome(id, options) {
984
- this.mainBranch(options);
985
- return this.http.request("GET", `/email/dispatches/${encodeURIComponent(id)}`, {
986
- params: { workspaceId: this.workspace(options) }
987
- });
988
- }
989
- async history(options) {
990
- this.mainBranch(options);
991
- return this.http.request("GET", "/email/dispatches", {
992
- params: { workspaceId: this.workspace(options), recordId: options?.recordId, installationId: options?.installationId }
993
- });
994
- }
995
- async status(id, options) {
996
- return this.outcome(id, options);
969
+ };
970
+ const deadline = Date.now() + 1e4;
971
+ for (; ; ) {
972
+ try {
973
+ return await this.http.request("POST", "/notifications/send", request);
974
+ } catch (error) {
975
+ if (!(error instanceof ApiError) || error.status !== 409 || error.code !== "execution_not_ready") {
976
+ throw error;
977
+ }
978
+ const delay2 = Math.max(250, error.retryAfterMs ?? 1e3);
979
+ if (Date.now() + delay2 > deadline) throw error;
980
+ await new Promise((resolve) => setTimeout(resolve, delay2));
981
+ }
982
+ }
997
983
  }
998
- workspace(options) {
999
- const value = options?.workspaceId ?? this.workspaceId;
1000
- if (!value) throw new Error("A workspace is required for transactional email");
1001
- return value;
984
+ async request(id2, options) {
985
+ return this.http.request(
986
+ "GET",
987
+ `/notifications/requests/${encodeURIComponent(id2)}`,
988
+ {
989
+ params: {
990
+ workspaceId: this.requireWorkspaceId(options?.workspaceId),
991
+ installationId: options?.installationId
992
+ }
993
+ }
994
+ );
1002
995
  }
1003
- mainBranch(options) {
1004
- if (options?.branch !== void 0 && options.branch !== "main")
1005
- throw new Error("Transactional email currently supports only the main business branch");
996
+ requireWorkspaceId(override) {
997
+ const workspaceId = override ?? this.workspaceId;
998
+ if (!workspaceId) {
999
+ throw new Error("workspaceId is required: provide it in options or when creating the client");
1000
+ }
1001
+ return workspaceId;
1006
1002
  }
1007
1003
  };
1008
1004
 
@@ -1032,8 +1028,8 @@ var BusinessModule = class _BusinessModule {
1032
1028
  body: JSON.stringify(command)
1033
1029
  });
1034
1030
  }
1035
- async read(id) {
1036
- return this.http.request("GET", `/business/records/${encodeURIComponent(id)}`, { headers: this.headers() });
1031
+ async read(id2) {
1032
+ return this.http.request("GET", `/business/records/${encodeURIComponent(id2)}`, { headers: this.headers() });
1037
1033
  }
1038
1034
  async list(kind) {
1039
1035
  return this.http.request("GET", "/business/records", {
@@ -1075,6 +1071,201 @@ var BusinessModule = class _BusinessModule {
1075
1071
  }
1076
1072
  };
1077
1073
 
1074
+ // src/modules/chat.ts
1075
+ var JSON_HEADERS = { "Content-Type": "application/json" };
1076
+ var ChatModule = class {
1077
+ constructor(http, bridge = null, executions) {
1078
+ this.http = http;
1079
+ this.bridge = bridge;
1080
+ this.executions = executions;
1081
+ }
1082
+ http;
1083
+ bridge;
1084
+ executions;
1085
+ // ── Conversations ─────────────────────────────────────────────────
1086
+ listConversations(options) {
1087
+ return this.http.request("GET", "/chat/conversations", { params: page(options) });
1088
+ }
1089
+ createConversation(options) {
1090
+ const { idempotencyKey, ...body } = options;
1091
+ return this.http.request("POST", "/chat/conversations", { body: JSON.stringify(body), headers: headers(idempotencyKey) });
1092
+ }
1093
+ /** Opens (or returns the existing) direct conversation with one participant. */
1094
+ openDirect(participant) {
1095
+ return this.createConversation({ kind: "direct", participants: [participant] });
1096
+ }
1097
+ getConversation(conversationId) {
1098
+ return this.http.request("GET", `/chat/conversations/${id(conversationId)}`);
1099
+ }
1100
+ updateConversation(conversationId, changes) {
1101
+ return this.http.request("PATCH", `/chat/conversations/${id(conversationId)}`, { body: JSON.stringify(changes), headers: JSON_HEADERS });
1102
+ }
1103
+ join(conversationId) {
1104
+ return this.http.request("POST", `/chat/conversations/${id(conversationId)}/join`, { body: "{}", headers: JSON_HEADERS });
1105
+ }
1106
+ listPublicChannels(options) {
1107
+ return this.http.request("GET", "/chat/channels", { params: { ...page(options), query: options?.query } });
1108
+ }
1109
+ // ── Members ───────────────────────────────────────────────────────
1110
+ listMembers(conversationId) {
1111
+ return this.http.request("GET", `/chat/conversations/${id(conversationId)}/members`);
1112
+ }
1113
+ addMembers(conversationId, participants, role) {
1114
+ return this.http.request("POST", `/chat/conversations/${id(conversationId)}/members`, {
1115
+ body: JSON.stringify({ participants, role }),
1116
+ headers: JSON_HEADERS
1117
+ });
1118
+ }
1119
+ /** Removes a member; pass yourself to leave. */
1120
+ async removeMember(conversationId, participant) {
1121
+ await this.http.request("DELETE", `/chat/conversations/${id(conversationId)}/members/${participant.kind}/${id(participant.id)}`);
1122
+ }
1123
+ changeRole(conversationId, participant, role) {
1124
+ return this.http.request("PUT", `/chat/conversations/${id(conversationId)}/members/${participant.kind}/${id(participant.id)}/role`, {
1125
+ body: JSON.stringify({ role }),
1126
+ headers: JSON_HEADERS
1127
+ });
1128
+ }
1129
+ // ── Messages ──────────────────────────────────────────────────────
1130
+ listMessages(conversationId, options) {
1131
+ return this.http.request("GET", `/chat/conversations/${id(conversationId)}/messages`, {
1132
+ params: { before: options?.before, limit: options?.limit }
1133
+ });
1134
+ }
1135
+ getMessage(conversationId, messageId) {
1136
+ return this.http.request("GET", `/chat/conversations/${id(conversationId)}/messages/${id(messageId)}`);
1137
+ }
1138
+ send(conversationId, parts, options) {
1139
+ return this.http.request("POST", `/chat/conversations/${id(conversationId)}/messages`, {
1140
+ body: JSON.stringify({ parts, replyTo: options?.replyTo }),
1141
+ headers: headers(options?.idempotencyKey)
1142
+ });
1143
+ }
1144
+ /** Sends Markdown text (rendered without raw HTML by every reader). */
1145
+ sendText(conversationId, markdown, options) {
1146
+ return this.send(conversationId, [{ type: "text", data: { markdown } }], options);
1147
+ }
1148
+ /** Edits your own message; pass the revision you saw to reject a concurrent edit. */
1149
+ edit(conversationId, messageId, parts, options) {
1150
+ return this.http.request("PATCH", `/chat/conversations/${id(conversationId)}/messages/${id(messageId)}`, {
1151
+ body: JSON.stringify({ parts }),
1152
+ headers: { ...JSON_HEADERS, ...options?.revision ? { "If-Match": `"${options.revision}"` } : {} }
1153
+ });
1154
+ }
1155
+ delete(conversationId, messageId) {
1156
+ return this.http.request("DELETE", `/chat/conversations/${id(conversationId)}/messages/${id(messageId)}`);
1157
+ }
1158
+ /**
1159
+ * Durable changes after a position: `afterSeq` is a snapshot's `highWatermark` (or the last sequence you applied);
1160
+ * `after` is the `next` cursor of a previous page.
1161
+ */
1162
+ events(conversationId, options) {
1163
+ return this.http.request("GET", `/chat/conversations/${id(conversationId)}/events`, {
1164
+ params: { after: options?.after, afterSeq: options?.afterSeq, limit: options?.limit }
1165
+ });
1166
+ }
1167
+ // ── Read state and preferences ────────────────────────────────────
1168
+ /** Marks messages read through a sequence; never moves back and never past what the server has. */
1169
+ markRead(conversationId, readThroughSeq) {
1170
+ return this.http.request("PUT", `/chat/conversations/${id(conversationId)}/read-state`, {
1171
+ body: JSON.stringify({ readThroughSeq }),
1172
+ headers: JSON_HEADERS
1173
+ });
1174
+ }
1175
+ setPreferences(conversationId, preferences) {
1176
+ return this.http.request("PUT", `/chat/conversations/${id(conversationId)}/preferences`, {
1177
+ body: JSON.stringify(preferences),
1178
+ headers: JSON_HEADERS
1179
+ });
1180
+ }
1181
+ // ── People, bots and commands ─────────────────────────────────────
1182
+ /** People in your workspace (name and avatar only). */
1183
+ searchPeople(options) {
1184
+ return this.http.request("GET", "/chat/people", { params: { ...page(options), query: options?.query } });
1185
+ }
1186
+ listBots() {
1187
+ return this.http.request("GET", "/chat/bots");
1188
+ }
1189
+ /** Workspace managers (`chat.manage`) decide whether a bot may message people directly. */
1190
+ updateBotSettings(botId, settings) {
1191
+ return this.http.request("PUT", `/chat/bots/${id(botId)}/settings`, { body: JSON.stringify(settings), headers: JSON_HEADERS });
1192
+ }
1193
+ listCommands() {
1194
+ return this.http.request("GET", "/chat/commands");
1195
+ }
1196
+ /** Validates and records a command invocation, returning the Execution request to run as you. */
1197
+ invokeCommand(conversationId, command, args, options) {
1198
+ return this.http.request("POST", `/chat/conversations/${id(conversationId)}/commands`, {
1199
+ body: JSON.stringify({ command, arguments: args ?? {} }),
1200
+ headers: headers(options?.idempotencyKey)
1201
+ });
1202
+ }
1203
+ /**
1204
+ * Invokes an installed app's command and runs its script with your own authorisation. Retrying with the same
1205
+ * idempotency key reuses the invocation key, so Execution never runs it twice.
1206
+ */
1207
+ async runCommand(conversationId, command, args, options) {
1208
+ if (!this.executions) throw new Error("Running commands needs the executions module.");
1209
+ const invocation = await this.invokeCommand(conversationId, command, args, options);
1210
+ const execution = await this.executions.run(invocation.filePath, {
1211
+ workspaceId: invocation.workspaceId,
1212
+ branch: invocation.branch,
1213
+ parameters: JSON.parse(invocation.parameters),
1214
+ invocationKey: invocation.invocationKey
1215
+ });
1216
+ return { invocation, execution };
1217
+ }
1218
+ /**
1219
+ * Posts as the installed app's bot from a running script. The bot is derived from the script's execution; the
1220
+ * conversation must have added the bot (or, for `userId`, the workspace must allow it to message people).
1221
+ * Retries briefly while Execution's start event is still on its way.
1222
+ */
1223
+ async postAsBot(options) {
1224
+ const { idempotencyKey, ...body } = options;
1225
+ const request = { body: JSON.stringify(body), headers: headers(idempotencyKey ?? globalThis.crypto?.randomUUID?.()) };
1226
+ const deadline = Date.now() + 1e4;
1227
+ for (; ; ) {
1228
+ try {
1229
+ return await this.http.request("POST", "/chat/bot-messages", request);
1230
+ } catch (error) {
1231
+ if (!(error instanceof ApiError) || error.status !== 409 || error.code !== "execution_not_ready") throw error;
1232
+ const delay2 = Math.max(250, error.retryAfterMs ?? 1e3);
1233
+ if (Date.now() + delay2 > deadline) throw error;
1234
+ await new Promise((resolve) => setTimeout(resolve, delay2));
1235
+ }
1236
+ }
1237
+ }
1238
+ // ── Live hints ────────────────────────────────────────────────────
1239
+ /**
1240
+ * Subscribes to live change hints through the host (installed apps only). Returns an unsubscribe function.
1241
+ * Hints carry ids only; duplicates and reordering are normal, and hints are lost while disconnected.
1242
+ */
1243
+ subscribeChanges(handlers) {
1244
+ if (!this.bridge) throw new Error("Live chat hints are only available to apps running inside the workspace.");
1245
+ return this.bridge.subscribeChat(handlers);
1246
+ }
1247
+ };
1248
+ function id(value) {
1249
+ return encodeURIComponent(value);
1250
+ }
1251
+ function page(options) {
1252
+ return { cursor: options?.cursor, limit: options?.limit };
1253
+ }
1254
+ function headers(idempotencyKey) {
1255
+ return idempotencyKey ? { ...JSON_HEADERS, "Idempotency-Key": idempotencyKey } : JSON_HEADERS;
1256
+ }
1257
+
1258
+ // src/transport/app-commands.ts
1259
+ var APP_COMMAND_BATCH_SIZE = 200;
1260
+ var APP_COMMANDS_CAPABILITY = "commands";
1261
+ var APP_COMMAND_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:/#@-]{0,255}$/;
1262
+ function isAppCommand(value) {
1263
+ if (!value || typeof value !== "object") return false;
1264
+ const c = value;
1265
+ const optionalText = (key, max) => c[key] === void 0 || typeof c[key] === "string" && c[key].length <= max;
1266
+ return typeof c.id === "string" && APP_COMMAND_ID_PATTERN.test(c.id) && typeof c.label === "string" && c.label.trim().length > 0 && c.label.length <= 200 && optionalText("description", 500) && optionalText("group", 100) && optionalText("disabledReason", 200) && (c.icon === void 0 || typeof c.icon === "string" && /^lucide:[a-z0-9-]{1,64}$/.test(c.icon)) && (c.keywords === void 0 || Array.isArray(c.keywords) && c.keywords.length <= 16 && c.keywords.every((k) => typeof k === "string" && k.length <= 64)) && (c.variant === void 0 || ["default", "outline", "destructive"].includes(String(c.variant))) && ["enabled", "busy"].every((key) => c[key] === void 0 || typeof c[key] === "boolean");
1267
+ }
1268
+
1078
1269
  // src/transport/header-actions.ts
1079
1270
  function isHeaderAction(value) {
1080
1271
  if (!value || typeof value !== "object") return false;
@@ -1107,7 +1298,7 @@ function element(value) {
1107
1298
  return record(value) && identifier(value.id) && natural(value.version) && (value.versionNonce === void 0 || natural(value.versionNonce)) && (value.isDeleted === void 0 || typeof value.isDeleted === "boolean");
1108
1299
  }
1109
1300
  function files(value) {
1110
- return record(value) && Object.keys(value).length <= 1e4 && Object.entries(value).every(([id, file]) => record(file) && identifier(id) && file.id === id && typeof file.dataURL === "string" && file.dataURL.length <= 10 * 1024 * 1024 && typeof file.mimeType === "string" && file.mimeType.length > 0 && file.mimeType.length <= 128 && natural(file.created) && (file.lastRetrieved === void 0 || natural(file.lastRetrieved)));
1301
+ return record(value) && Object.keys(value).length <= 1e4 && Object.entries(value).every(([id2, file]) => record(file) && identifier(id2) && file.id === id2 && typeof file.dataURL === "string" && file.dataURL.length <= 10 * 1024 * 1024 && typeof file.mimeType === "string" && file.mimeType.length > 0 && file.mimeType.length <= 128 && natural(file.created) && (file.lastRetrieved === void 0 || natural(file.lastRetrieved)));
1111
1302
  }
1112
1303
  function isNativeSnapshot(value) {
1113
1304
  if (!record(value) || !natural(value.sequence) || !natural(value.savedSequence) || value.savedSequence > value.sequence || typeof value.revision !== "string" || !value.revision || value.revision.length > 1024 || typeof value.readOnly !== "boolean" || !record(value.scene) || !Array.isArray(value.scene.elements) || value.scene.elements.length > 1e4 || !value.scene.elements.every(element) || !files(value.scene.files) || !record(value.scene.appState) || !Array.isArray(value.participants) || value.participants.length > 20)
@@ -1215,6 +1406,14 @@ var Bridge = class {
1215
1406
  sessionId = null;
1216
1407
  actions = /* @__PURE__ */ new Map();
1217
1408
  requests = /* @__PURE__ */ new Set();
1409
+ commands = /* @__PURE__ */ new Map();
1410
+ commandUpserts = /* @__PURE__ */ new Set();
1411
+ commandRemovals = /* @__PURE__ */ new Set();
1412
+ commandReset = false;
1413
+ commandFlushScheduled = false;
1414
+ commandRequests = /* @__PURE__ */ new Set();
1415
+ _hostCapabilities = [];
1416
+ chatSubscribers = /* @__PURE__ */ new Set();
1218
1417
  hostMessageHandlers = /* @__PURE__ */ new Map();
1219
1418
  hostMessageRequests = /* @__PURE__ */ new Set();
1220
1419
  wopiRequests = /* @__PURE__ */ new Map();
@@ -1243,6 +1442,17 @@ var Bridge = class {
1243
1442
  get hostOrigin() {
1244
1443
  return this._hostOrigin;
1245
1444
  }
1445
+ /** Capabilities the host announced in `verentis:init`. */
1446
+ get hostCapabilities() {
1447
+ return this._hostCapabilities;
1448
+ }
1449
+ /**
1450
+ * Whether the host exports page commands to its command registry. Older hosts ignore registrations;
1451
+ * commands still run locally, they are just not discoverable in the workspace palette.
1452
+ */
1453
+ get supportsCommands() {
1454
+ return this._hostCapabilities.includes(APP_COMMANDS_CAPABILITY);
1455
+ }
1246
1456
  /** Send a message to the host */
1247
1457
  postToHost(message) {
1248
1458
  if (this._destroyed) throw new Error("Bridge has been destroyed.");
@@ -1381,6 +1591,113 @@ var Bridge = class {
1381
1591
  }
1382
1592
  };
1383
1593
  }
1594
+ /**
1595
+ * Export a page command to the workspace command registry while it is active. There is no count limit;
1596
+ * dispose the registration when the control unmounts. May be called before the init handshake.
1597
+ */
1598
+ registerCommand(descriptor, handler) {
1599
+ if (this._destroyed) throw new Error("Bridge has been destroyed.");
1600
+ if (!isAppCommand(descriptor)) throw new Error("Invalid command.");
1601
+ if (this.commands.has(descriptor.id)) throw new Error(`Command '${descriptor.id}' is already registered.`);
1602
+ const command = { descriptor: cloneCommand(descriptor), handler, busy: false };
1603
+ this.commands.set(descriptor.id, command);
1604
+ this.queueCommand(descriptor.id, "upsert");
1605
+ return {
1606
+ update: (next) => {
1607
+ if (this.commands.get(descriptor.id) !== command) throw new Error("Command has been disposed.");
1608
+ if (!isAppCommand(next) || next.id !== descriptor.id) throw new Error("Invalid command update.");
1609
+ if (sameCommand(command.descriptor, next)) return;
1610
+ command.descriptor = cloneCommand(next);
1611
+ this.queueCommand(descriptor.id, "upsert");
1612
+ },
1613
+ dispose: () => {
1614
+ if (this.commands.get(descriptor.id) !== command) return;
1615
+ this.commands.delete(descriptor.id);
1616
+ this.queueCommand(descriptor.id, "remove");
1617
+ }
1618
+ };
1619
+ }
1620
+ queueCommand(id2, change) {
1621
+ if (change === "upsert") {
1622
+ this.commandRemovals.delete(id2);
1623
+ this.commandUpserts.add(id2);
1624
+ } else {
1625
+ this.commandUpserts.delete(id2);
1626
+ this.commandRemovals.add(id2);
1627
+ }
1628
+ this.scheduleCommandFlush();
1629
+ }
1630
+ scheduleCommandFlush() {
1631
+ if (this.commandFlushScheduled) return;
1632
+ this.commandFlushScheduled = true;
1633
+ queueMicrotask(() => {
1634
+ this.commandFlushScheduled = false;
1635
+ this.flushCommands();
1636
+ });
1637
+ }
1638
+ commandSnapshot(id2) {
1639
+ const command = this.commands.get(id2);
1640
+ if (!command) return null;
1641
+ return command.busy ? { ...command.descriptor, busy: true } : command.descriptor;
1642
+ }
1643
+ flushCommands() {
1644
+ if (!this.sessionId || !this._hostOrigin || this._destroyed) return;
1645
+ const upsert = [...this.commandUpserts].map((id2) => this.commandSnapshot(id2)).filter((c) => !!c);
1646
+ const remove = this.commandReset ? [] : [...this.commandRemovals];
1647
+ let reset = this.commandReset;
1648
+ this.commandUpserts.clear();
1649
+ this.commandRemovals.clear();
1650
+ this.commandReset = false;
1651
+ if (!reset && !upsert.length && !remove.length) return;
1652
+ let offset = 0;
1653
+ let removeOffset = 0;
1654
+ do {
1655
+ const batchUpsert = upsert.slice(offset, offset + APP_COMMAND_BATCH_SIZE);
1656
+ offset += batchUpsert.length;
1657
+ const room = APP_COMMAND_BATCH_SIZE - batchUpsert.length;
1658
+ const batchRemove = remove.slice(removeOffset, removeOffset + room);
1659
+ removeOffset += batchRemove.length;
1660
+ this.postToHost({
1661
+ type: "verentis:commands:update",
1662
+ version: 1,
1663
+ sessionId: this.sessionId,
1664
+ ...reset ? { reset: true } : {},
1665
+ upsert: batchUpsert,
1666
+ remove: batchRemove
1667
+ });
1668
+ reset = false;
1669
+ } while (offset < upsert.length || removeOffset < remove.length);
1670
+ }
1671
+ async invokeCommand(message) {
1672
+ if (message.sessionId !== this.sessionId || typeof message.requestId !== "string" || message.requestId.length > 128 || !message.requestId || this.commandRequests.has(message.requestId) || typeof message.commandId !== "string") return;
1673
+ this.commandRequests.add(message.requestId);
1674
+ if (this.commandRequests.size > 1e3) this.commandRequests.delete(this.commandRequests.values().next().value);
1675
+ const command = this.commands.get(message.commandId);
1676
+ const reply = {
1677
+ type: "verentis:command:result",
1678
+ version: 1,
1679
+ sessionId: message.sessionId,
1680
+ requestId: message.requestId,
1681
+ commandId: message.commandId
1682
+ };
1683
+ if (!command || command.busy || command.descriptor.busy || command.descriptor.enabled === false) {
1684
+ this.postToHost({ ...reply, error: "Command is unavailable." });
1685
+ return;
1686
+ }
1687
+ command.busy = true;
1688
+ this.queueCommand(message.commandId, "upsert");
1689
+ try {
1690
+ await command.handler();
1691
+ if (!this._destroyed && this.sessionId === message.sessionId) this.postToHost(reply);
1692
+ } catch (error) {
1693
+ if (!this._destroyed && this.sessionId === message.sessionId) {
1694
+ this.postToHost({ ...reply, error: (error instanceof Error ? error.message : "Command failed.").slice(0, 500) || "Command failed." });
1695
+ }
1696
+ } finally {
1697
+ command.busy = false;
1698
+ if (this.commands.get(message.commandId) === command) this.queueCommand(message.commandId, "upsert");
1699
+ }
1700
+ }
1384
1701
  publishActions() {
1385
1702
  if (!this.sessionId || this._destroyed) return;
1386
1703
  this.postToHost({
@@ -1465,6 +1782,41 @@ var Bridge = class {
1465
1782
  this.postToHost({ ...reply, error: "The reply could not be sent to the workspace." });
1466
1783
  }
1467
1784
  }
1785
+ /**
1786
+ * Subscribes to the host's chat live hints (see `ChatModule.subscribeChanges`). The first subscriber asks the host
1787
+ * to relay; the last one to leave stops it. Messages from another session are ignored.
1788
+ */
1789
+ subscribeChat(handlers) {
1790
+ if (this._destroyed) throw new Error("Bridge has been destroyed.");
1791
+ this.chatSubscribers.add(handlers);
1792
+ const subscribe = () => {
1793
+ if (this.sessionId && this._hostOrigin && this.chatSubscribers.size > 0)
1794
+ this.postToHost({ type: "verentis:chat:subscribe", version: 1, sessionId: this.sessionId });
1795
+ };
1796
+ if (this.chatSubscribers.size === 1) {
1797
+ if (this._hostOrigin) subscribe();
1798
+ else void this._initPromise.then(subscribe, () => void 0);
1799
+ }
1800
+ return () => {
1801
+ if (!this.chatSubscribers.delete(handlers) || this.chatSubscribers.size > 0) return;
1802
+ if (!this._destroyed && this.sessionId && this._hostOrigin)
1803
+ this.postToHost({ type: "verentis:chat:unsubscribe", version: 1, sessionId: this.sessionId });
1804
+ };
1805
+ }
1806
+ receiveChat(data) {
1807
+ if (data.type !== "verentis:chat:changed" && data.type !== "verentis:chat:connection" && data.type !== "verentis:chat:subscribed" && data.type !== "verentis:chat:error") return false;
1808
+ if (!this._hostOrigin || data.sessionId !== this.sessionId) return true;
1809
+ for (const subscriber of [...this.chatSubscribers]) {
1810
+ try {
1811
+ if (data.type === "verentis:chat:changed") subscriber.onChange(data.hint);
1812
+ else if (data.type === "verentis:chat:error") subscriber.onError?.(String(data.reason).slice(0, 500));
1813
+ else subscriber.onConnection?.(data.status);
1814
+ } catch (error) {
1815
+ console.error("[Verentis SDK] Chat subscriber failed", error);
1816
+ }
1817
+ }
1818
+ return true;
1819
+ }
1468
1820
  /** Subscribe to a specific message type from the host */
1469
1821
  on(type, listener) {
1470
1822
  if (!this.listeners.has(type)) {
@@ -1483,8 +1835,13 @@ var Bridge = class {
1483
1835
  if (!this._hostOrigin) this._initReject(new Error("Bridge has been destroyed."));
1484
1836
  this.actions.clear();
1485
1837
  this.requests.clear();
1838
+ this.commands.clear();
1839
+ this.commandUpserts.clear();
1840
+ this.commandRemovals.clear();
1841
+ this.commandRequests.clear();
1486
1842
  this.hostMessageHandlers.clear();
1487
1843
  this.hostMessageRequests.clear();
1844
+ this.chatSubscribers.clear();
1488
1845
  this.listeners.clear();
1489
1846
  for (const pending of this.wopiRequests.values()) {
1490
1847
  clearTimeout(pending.timer);
@@ -1513,6 +1870,13 @@ var Bridge = class {
1513
1870
  this._initTimer = void 0;
1514
1871
  this._hostOrigin = event.origin;
1515
1872
  this.sessionId = typeof data.sessionId === "string" ? data.sessionId : null;
1873
+ this._hostCapabilities = Array.isArray(data.capabilities) ? data.capabilities.filter((c) => typeof c === "string").slice(0, 64) : [];
1874
+ if (this.sessionId && this.commands.size > 0) {
1875
+ this.commandReset = true;
1876
+ this.commandRemovals.clear();
1877
+ for (const id2 of this.commands.keys()) this.commandUpserts.add(id2);
1878
+ this.scheduleCommandFlush();
1879
+ }
1516
1880
  console.info("[Verentis SDK] Received verentis:init from host", {
1517
1881
  origin: event.origin,
1518
1882
  workspaceId: data.context.workspace.id,
@@ -1550,10 +1914,15 @@ var Bridge = class {
1550
1914
  void this.invokeAction(data);
1551
1915
  return;
1552
1916
  }
1917
+ if (data.type === "verentis:command:invoke") {
1918
+ void this.invokeCommand(data);
1919
+ return;
1920
+ }
1553
1921
  if (data.type === "verentis:host:message") {
1554
1922
  void this.receiveHostMessage(data);
1555
1923
  return;
1556
1924
  }
1925
+ if (this.receiveChat(data)) return;
1557
1926
  const typeListeners = this.listeners.get(data.type);
1558
1927
  if (typeListeners) {
1559
1928
  for (const listener of typeListeners) {
@@ -1562,22 +1931,18 @@ var Bridge = class {
1562
1931
  }
1563
1932
  }
1564
1933
  };
1565
-
1566
- // src/types.ts
1567
- var ApiError = class extends Error {
1568
- constructor(status, detail, title, code) {
1569
- super(title ?? `API error ${status}: ${detail}`);
1570
- this.status = status;
1571
- this.detail = detail;
1572
- this.title = title;
1573
- this.code = code;
1574
- this.name = "ApiError";
1575
- }
1576
- status;
1577
- detail;
1578
- title;
1579
- code;
1580
- };
1934
+ function cloneCommand(command) {
1935
+ return { ...command, ...command.keywords ? { keywords: [...command.keywords] } : {} };
1936
+ }
1937
+ function sameCommand(a, b) {
1938
+ const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
1939
+ for (const key of keys) {
1940
+ if (key === "keywords") {
1941
+ if ((a.keywords ?? []).join("\0") !== (b.keywords ?? []).join("\0")) return false;
1942
+ } else if (a[key] !== b[key]) return false;
1943
+ }
1944
+ return true;
1945
+ }
1581
1946
 
1582
1947
  // src/transport/http.transport.ts
1583
1948
  var HttpTransport = class {
@@ -1592,17 +1957,17 @@ var HttpTransport = class {
1592
1957
  async request(method, path, options) {
1593
1958
  const url = this.buildUrl(path, options?.params);
1594
1959
  console.info("[Verentis SDK] Request started", { method, path, url });
1595
- const headers = {};
1960
+ const headers2 = {};
1596
1961
  const authHeader = await this.auth.getAuthHeader();
1597
1962
  if (authHeader) {
1598
- headers["Authorization"] = authHeader;
1963
+ headers2["Authorization"] = authHeader;
1599
1964
  }
1600
1965
  if (options?.headers) {
1601
- Object.assign(headers, options.headers);
1966
+ Object.assign(headers2, options.headers);
1602
1967
  }
1603
1968
  const response = await fetch(url, {
1604
1969
  method,
1605
- headers,
1970
+ headers: headers2,
1606
1971
  body: options?.body
1607
1972
  });
1608
1973
  await this.ensureSuccess(response);
@@ -1621,15 +1986,15 @@ var HttpTransport = class {
1621
1986
  async requestRaw(method, path, options) {
1622
1987
  const url = this.buildUrl(path, options?.params);
1623
1988
  console.info("[Verentis SDK] Raw request started", { method, path, url });
1624
- const headers = {};
1989
+ const headers2 = {};
1625
1990
  const authHeader = await this.auth.getAuthHeader();
1626
1991
  if (authHeader) {
1627
- headers["Authorization"] = authHeader;
1992
+ headers2["Authorization"] = authHeader;
1628
1993
  }
1629
1994
  if (options?.headers) {
1630
- Object.assign(headers, options.headers);
1995
+ Object.assign(headers2, options.headers);
1631
1996
  }
1632
- const response = await fetch(url, { method, headers, body: options?.body });
1997
+ const response = await fetch(url, { method, headers: headers2, body: options?.body });
1633
1998
  console.info("[Verentis SDK] Raw request completed", {
1634
1999
  method,
1635
2000
  path,
@@ -1657,7 +2022,15 @@ var HttpTransport = class {
1657
2022
  if (!(error instanceof SyntaxError)) throw error;
1658
2023
  }
1659
2024
  console.error("[Verentis SDK] Request failed", { status: response.status, code });
1660
- throw new ApiError(response.status, detail, title, code);
2025
+ const retryAfter = response.headers.get("Retry-After");
2026
+ const retryAfterMs = retryAfter === null ? void 0 : /^\d+$/.test(retryAfter) ? Number(retryAfter) * 1e3 : Date.parse(retryAfter) - Date.now();
2027
+ throw new ApiError(
2028
+ response.status,
2029
+ detail,
2030
+ title,
2031
+ code,
2032
+ retryAfterMs !== void 0 && Number.isFinite(retryAfterMs) ? Math.max(0, retryAfterMs) : void 0
2033
+ );
1661
2034
  }
1662
2035
  buildUrl(path, params) {
1663
2036
  const base = `${this.baseUrl}/${this.apiVersion}${path}`;
@@ -1688,8 +2061,9 @@ var VerentisClient = class {
1688
2061
  installations;
1689
2062
  resources;
1690
2063
  settings;
1691
- email;
2064
+ notifications;
1692
2065
  business;
2066
+ chat;
1693
2067
  context;
1694
2068
  bridge;
1695
2069
  collaboration;
@@ -1724,8 +2098,9 @@ var VerentisClient = class {
1724
2098
  this.installations = new InstallationsModule(this._http, options?.workspaceId);
1725
2099
  this.resources = new ResourcesModule(this._http, options?.workspaceId);
1726
2100
  this.settings = new SettingsModule(this._http, options?.workspaceId);
1727
- this.email = new EmailModule(this._http, options?.workspaceId);
2101
+ this.notifications = new NotificationsModule(this._http, options?.workspaceId);
1728
2102
  this.business = new BusinessModule(this._http);
2103
+ this.chat = new ChatModule(this._http, null, this.executions);
1729
2104
  this._ready = Promise.resolve();
1730
2105
  return;
1731
2106
  }
@@ -1774,8 +2149,9 @@ var VerentisClient = class {
1774
2149
  this.installations = new InstallationsModule(http, context.workspace.id);
1775
2150
  this.resources = new ResourcesModule(http, context.workspace.id);
1776
2151
  this.settings = new SettingsModule(http, context.workspace.id);
1777
- this.email = new EmailModule(http, context.workspace.id);
2152
+ this.notifications = new NotificationsModule(http, context.workspace.id);
1778
2153
  this.business = new BusinessModule(http);
2154
+ this.chat = new ChatModule(http, this.bridge, this.executions);
1779
2155
  }
1780
2156
  };
1781
2157
  function detectEnvironment() {
@@ -1871,6 +2247,6 @@ function createVerentisClient(options) {
1871
2247
  return new VerentisClient(options);
1872
2248
  }
1873
2249
 
1874
- export { ApiError, ApiKeyAuth, Bridge, BridgeAuth, BusinessModule, ContextModule, EmailModule, EnginesModule, ExecutionsModule, FilesModule, HOST_MESSAGE_CHANNEL_PATTERN, HttpTransport, InstallationsModule, NativeCollaborationClient, NativeCollaborationError, ResourcesModule, SettingsModule, TokenAuth, VerentisClient, createVerentisClient, exactHttpsOrigin, findBestMatch, isDurableSaveConfirmed, isHeaderAction, isNativeSaveResult, isNativeSnapshot, isTerminalStatus, isTrustedMessage, matchMimeType, validateCollaborationDocument, validateNativeCommand };
2250
+ export { APP_COMMANDS_CAPABILITY, APP_COMMAND_BATCH_SIZE, APP_COMMAND_ID_PATTERN, ApiError, ApiKeyAuth, Bridge, BridgeAuth, BusinessModule, ChatModule, ContextModule, EnginesModule, ExecutionsModule, FilesModule, HOST_MESSAGE_CHANNEL_PATTERN, HttpTransport, InstallationsModule, NativeCollaborationClient, NativeCollaborationError, NotificationsModule, ResourcesModule, SCRIPT_ARGUMENTS_SCHEMA, SettingsModule, TokenAuth, VerentisClient, createVerentisClient, exactHttpsOrigin, findBestMatch, isAppCommand, isDurableSaveConfirmed, isHeaderAction, isNativeSaveResult, isNativeSnapshot, isTerminalStatus, isTrustedMessage, matchMimeType, validateCollaborationDocument, validateNativeCommand };
1875
2251
  //# sourceMappingURL=index.js.map
1876
2252
  //# sourceMappingURL=index.js.map