@relyio/sdk 0.17.0 → 0.19.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.
Files changed (74) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/automations/index.cjs +71 -0
  3. package/dist/automations/index.d.cts +1590 -0
  4. package/dist/automations/index.d.ts +1590 -0
  5. package/dist/automations/index.js +6 -0
  6. package/dist/{chunk-RSHMA4KT.cjs → chunk-2SPPU2WG.cjs} +68 -68
  7. package/dist/{chunk-VS2DEUTN.js → chunk-356SF5Y2.js} +463 -26
  8. package/dist/chunk-5F3YYPLZ.cjs +19237 -0
  9. package/dist/chunk-7I3SC7SV.cjs +40 -0
  10. package/dist/chunk-DBYMG6MQ.cjs +535 -0
  11. package/dist/chunk-DI6KFDBO.js +43 -0
  12. package/dist/chunk-HDHBYYEY.js +36 -0
  13. package/dist/chunk-L6DXCKVQ.cjs +50 -0
  14. package/dist/{chunk-TZL374LS.cjs → chunk-M7MWNV3Q.cjs} +21 -21
  15. package/dist/{chunk-H6IBQSMM.js → chunk-NJQK5STK.js} +3 -3
  16. package/dist/chunk-NN45CSPZ.cjs +872 -0
  17. package/dist/{chunk-6E22GYPB.cjs → chunk-P23G5NXD.cjs} +781 -337
  18. package/dist/chunk-PZCDJFMA.js +19195 -0
  19. package/dist/chunk-T7LGREB3.js +848 -0
  20. package/dist/{chunk-BBUB7C2Z.js → chunk-W2A4OTCI.js} +3 -3
  21. package/dist/chunk-W2GRVXDG.js +520 -0
  22. package/dist/{chunk-5UMY6CS4.js → chunk-ZAKEY6DY.js} +3 -3
  23. package/dist/{chunk-LSRPMNPT.cjs → chunk-ZO6QDYJG.cjs} +7 -7
  24. package/dist/client/index.cjs +8 -7
  25. package/dist/client/index.d.cts +4 -3
  26. package/dist/client/index.d.ts +4 -3
  27. package/dist/client/index.js +6 -5
  28. package/dist/{define-content-type-AuL9-oz6.d.cts → define-content-type-BZ6X8GvL.d.cts} +4547 -842
  29. package/dist/{define-content-type-BGoQxqLf.d.ts → define-content-type-C12jh8cY.d.ts} +4547 -842
  30. package/dist/{define-schema-DgCX9YB7.d.ts → define-schema-BPlZl4a2.d.cts} +258 -10
  31. package/dist/{define-schema-DkXwpp0I.d.cts → define-schema-CK7TtYot.d.ts} +258 -10
  32. package/dist/dev-handshake/index.cjs +9 -9
  33. package/dist/dev-handshake/index.js +2 -2
  34. package/dist/extensions/index.cjs +100 -0
  35. package/dist/extensions/index.d.cts +6 -0
  36. package/dist/extensions/index.d.ts +6 -0
  37. package/dist/extensions/index.js +96 -0
  38. package/dist/functions/index.cjs +71 -0
  39. package/dist/functions/index.d.cts +6 -0
  40. package/dist/functions/index.d.ts +6 -0
  41. package/dist/functions/index.js +62 -0
  42. package/dist/{index-BBp8_tn9.d.ts → index-BxNnlc5D.d.ts} +167 -494
  43. package/dist/{index-DaMZ4FQ_.d.ts → index-C-MymZTe.d.ts} +1 -1
  44. package/dist/{index-DxRmKPrR.d.cts → index-D5jyIeQ5.d.cts} +167 -494
  45. package/dist/{index-Bp4AiNT3.d.cts → index-DN0l8RHw.d.cts} +1 -1
  46. package/dist/index.cjs +208 -185
  47. package/dist/index.d.cts +147 -17
  48. package/dist/index.d.ts +147 -17
  49. package/dist/index.js +11 -16
  50. package/dist/markdown/index.cjs +382 -382
  51. package/dist/markdown/index.js +1 -1
  52. package/dist/preview/index.cjs +11 -10
  53. package/dist/preview/index.js +3 -2
  54. package/dist/register-BiMQKVp-.d.ts +693 -0
  55. package/dist/register-DeMsaurp.d.cts +693 -0
  56. package/dist/rich-text/index.cjs +12 -12
  57. package/dist/rich-text/index.d.cts +2 -2
  58. package/dist/rich-text/index.d.ts +2 -2
  59. package/dist/rich-text/index.js +2 -2
  60. package/dist/schema/index.cjs +63 -34
  61. package/dist/schema/index.d.cts +14 -5
  62. package/dist/schema/index.d.ts +14 -5
  63. package/dist/schema/index.js +3 -2
  64. package/dist/types/index.d.cts +1 -1
  65. package/dist/types/index.d.ts +1 -1
  66. package/dist/utils/index.cjs +20 -20
  67. package/dist/utils/index.js +2 -2
  68. package/package.json +23 -5
  69. package/dist/chunk-6PTRZP5U.cjs +0 -433
  70. package/dist/chunk-DIZY6CKM.js +0 -950
  71. package/dist/chunk-GHYB6IZK.cjs +0 -983
  72. package/dist/chunk-MWMSFIWN.cjs +0 -17
  73. package/dist/chunk-OVMIHAYK.js +0 -411
  74. package/dist/chunk-XQ4QHWZE.js +0 -13
@@ -1,7 +1,8 @@
1
- import { parseDevEnv, DEV_CONTEXT_DIR, DEV_CONTEXT_FILENAME, parseDevContextFile } from './chunk-BBUB7C2Z.js';
2
- import { VISITOR_CLOSE_CODES, HEADER_RETRY_AFTER, HEADER_IDEMPOTENCY_KEY, UNKNOWN_PREVIEW_RECHECK_MS, entityTag, HEADER_IF_MATCH, FRESH_PARAMETER, VISITOR_KEEPALIVE, parseVisitorFrame, VISITOR_FRAMES, VISITOR_POLL_INTERVAL_MS, VISITOR_CLOSE_RETRY, readVisitorPollAnswer, VISITOR_POLL_RETRY, VISITOR_LIVE_PATH, VISITOR_QUERY, previewAtRequest, previewDatasetSlug } from './chunk-OVMIHAYK.js';
3
- import { isBrowser } from './chunk-H6IBQSMM.js';
4
- import { __name, __publicField } from './chunk-XQ4QHWZE.js';
1
+ import { isBrowser } from './chunk-NJQK5STK.js';
2
+ import { parseDevEnv, DEV_CONTEXT_DIR, DEV_CONTEXT_FILENAME, parseDevContextFile } from './chunk-W2A4OTCI.js';
3
+ import { UNKNOWN_PREVIEW_RECHECK_MS, previewAtRequest } from './chunk-HDHBYYEY.js';
4
+ import { HEADER_END_USER, VISITOR_CLOSE_CODES, HEADER_RETRY_AFTER, END_USER_TOKEN_REFUSAL_REASONS, HEADER_IDEMPOTENCY_KEY, entityTag, HEADER_IF_MATCH, FRESH_PARAMETER, VISITOR_KEEPALIVE, parseVisitorFrame, VISITOR_FRAMES, VISITOR_POLL_INTERVAL_MS, VISITOR_CLOSE_RETRY, readVisitorPollAnswer, VISITOR_POLL_RETRY, VISITOR_LIVE_PATH, VISITOR_QUERY, previewDatasetSlug } from './chunk-T7LGREB3.js';
5
+ import { __name, __publicField } from './chunk-DI6KFDBO.js';
5
6
 
6
7
  /* RelyIO SDK - Built with ❤️ by the RelyIO Team */
7
8
 
@@ -167,6 +168,70 @@ var _ReadOnlyKeyError = class _ReadOnlyKeyError extends SDKError {
167
168
  };
168
169
  __name(_ReadOnlyKeyError, "ReadOnlyKeyError");
169
170
  var ReadOnlyKeyError = _ReadOnlyKeyError;
171
+ var _PublishableKeyRefusedError = class _PublishableKeyRefusedError extends SDKError {
172
+ constructor(message = "A publishable key writes a Record only for a signed-in End user", context) {
173
+ super(message, 403, void 0, context);
174
+ __publicField(this, "code", "PUBLISHABLE_KEY_REFUSED");
175
+ __publicField(this, "retryable", false);
176
+ }
177
+ getUserMessage() {
178
+ return "A publishable key writes a Record only with a signed-in End user's token, and runs only a Function declaring endUsers. Send the End user's token, declare endUsers on the Function, or call it from your server with a secret key.";
179
+ }
180
+ };
181
+ __name(_PublishableKeyRefusedError, "PublishableKeyRefusedError");
182
+ var PublishableKeyRefusedError = _PublishableKeyRefusedError;
183
+ var _FunctionRunFailedError = class _FunctionRunFailedError extends SDKError {
184
+ constructor(message = "The Function ended without an answer", run = {}, context) {
185
+ super(message, 500, void 0, context);
186
+ __publicField(this, "code", "FUNCTION_RUN_FAILED");
187
+ __publicField(this, "retryable", false);
188
+ __publicField(this, "runId");
189
+ __publicField(this, "failure");
190
+ __publicField(this, "runError");
191
+ this.runId = run.runId;
192
+ this.failure = run.failure;
193
+ this.runError = run.runError;
194
+ }
195
+ getUserMessage() {
196
+ return this.runId === void 0 ? "The Function ended without an answer." : `The Function ended without an answer; its run is ${this.runId}.`;
197
+ }
198
+ };
199
+ __name(_FunctionRunFailedError, "FunctionRunFailedError");
200
+ var FunctionRunFailedError = _FunctionRunFailedError;
201
+ var _FunctionContainersBusyError = class _FunctionContainersBusyError extends SDKError {
202
+ constructor(message = "The project is running as many Containers as its plan includes", name, context, retryAfterMs) {
203
+ super(message, 429, void 0, context);
204
+ __publicField(this, "code", "FUNCTION_CONTAINERS_BUSY");
205
+ __publicField(this, "retryable", false);
206
+ /** The Function that was not started. */
207
+ __publicField(this, "function");
208
+ /** `Retry-After`, in milliseconds, where the response carried a readable one. */
209
+ __publicField(this, "retryAfterMs");
210
+ this.function = name;
211
+ this.retryAfterMs = retryAfterMs;
212
+ }
213
+ getUserMessage() {
214
+ return "Every Container this project may run at once is busy. Call again shortly.";
215
+ }
216
+ };
217
+ __name(_FunctionContainersBusyError, "FunctionContainersBusyError");
218
+ var FunctionContainersBusyError = _FunctionContainersBusyError;
219
+ var _FunctionRunTimedOutError = class _FunctionRunTimedOutError extends SDKError {
220
+ constructor(message = "The Function had not answered within the wait", run = {}, context) {
221
+ super(message, 504, void 0, context);
222
+ __publicField(this, "code", "FUNCTION_RUN_TIMED_OUT");
223
+ __publicField(this, "retryable", false);
224
+ __publicField(this, "runId");
225
+ __publicField(this, "waitedMs");
226
+ this.runId = run.runId;
227
+ this.waitedMs = run.waitedMs;
228
+ }
229
+ getUserMessage() {
230
+ return this.runId === void 0 ? "The Function had not answered within the wait; it runs on." : `The Function had not answered within the wait; run ${this.runId} runs on.`;
231
+ }
232
+ };
233
+ __name(_FunctionRunTimedOutError, "FunctionRunTimedOutError");
234
+ var FunctionRunTimedOutError = _FunctionRunTimedOutError;
170
235
  var _PlanFeatureUnavailableError = class _PlanFeatureUnavailableError extends SDKError {
171
236
  constructor(message = "This feature is not available on the current plan", feature, context) {
172
237
  super(message, 403, void 0, context);
@@ -327,6 +392,19 @@ var _InvalidApiKeyError = class _InvalidApiKeyError extends SDKError {
327
392
  };
328
393
  __name(_InvalidApiKeyError, "InvalidApiKeyError");
329
394
  var InvalidApiKeyError = _InvalidApiKeyError;
395
+ var _EndUserTokenInvalidError = class _EndUserTokenInvalidError extends SDKError {
396
+ constructor(message, reason, context) {
397
+ super(message, 401, void 0, context);
398
+ this.reason = reason;
399
+ __publicField(this, "code", "END_USER_TOKEN_INVALID");
400
+ __publicField(this, "retryable", false);
401
+ }
402
+ getUserMessage() {
403
+ return "Your session is not valid. Please sign in again.";
404
+ }
405
+ };
406
+ __name(_EndUserTokenInvalidError, "EndUserTokenInvalidError");
407
+ var EndUserTokenInvalidError = _EndUserTokenInvalidError;
330
408
 
331
409
  // src/errors/network-errors.ts
332
410
  var _NetworkError = class _NetworkError extends SDKError {
@@ -528,12 +606,18 @@ var _ErrorMapper = class _ErrorMapper {
528
606
  case "INVALID_API_KEY": {
529
607
  return new InvalidApiKeyError(message, void 0, context);
530
608
  }
609
+ case "END_USER_TOKEN_INVALID": {
610
+ return new EndUserTokenInvalidError(message, endUserTokenReasonOf(refusal), context);
611
+ }
531
612
  case "ACCESS_DENIED": {
532
613
  return new AuthorizationError(message, 403, void 0, context);
533
614
  }
534
615
  case "READ_ONLY_KEY": {
535
616
  return new ReadOnlyKeyError(message, context);
536
617
  }
618
+ case "PUBLISHABLE_KEY_REFUSED": {
619
+ return new PublishableKeyRefusedError(message, context);
620
+ }
537
621
  case "PLAN_FEATURE_UNAVAILABLE": {
538
622
  return new PlanFeatureUnavailableError(message, detailString(refusal, "feature"), context);
539
623
  }
@@ -577,7 +661,8 @@ var _ErrorMapper = class _ErrorMapper {
577
661
  return new ResourceConflictError(guidance, 409, void 0, {
578
662
  ...context,
579
663
  // What stands in the way, as a declared kind a program switches on: `draft-exists`,
580
- // `release-locked` or `slug-taken` from the Content API (REL-2446).
664
+ // `release-locked`, `slug-taken` or `value-taken` from the Content API (REL-2446,
665
+ // REL-3767).
581
666
  blockage,
582
667
  expectedVersion,
583
668
  actualVersion: detailNumber(refusal, "actualVersion"),
@@ -614,6 +699,32 @@ var _ErrorMapper = class _ErrorMapper {
614
699
  retryAfterMs
615
700
  );
616
701
  }
702
+ case "FUNCTION_RUN_FAILED": {
703
+ return new FunctionRunFailedError(
704
+ message,
705
+ {
706
+ runId: detailString(refusal, "runId"),
707
+ failure: detailString(refusal, "failure"),
708
+ runError: runErrorOf(refusal)
709
+ },
710
+ context
711
+ );
712
+ }
713
+ case "FUNCTION_RUN_TIMED_OUT": {
714
+ return new FunctionRunTimedOutError(
715
+ message,
716
+ { runId: detailString(refusal, "runId"), waitedMs: detailNumber(refusal, "waitedMs") },
717
+ context
718
+ );
719
+ }
720
+ case "FUNCTION_CONTAINERS_BUSY": {
721
+ return new FunctionContainersBusyError(
722
+ message,
723
+ detailString(refusal, "function"),
724
+ context,
725
+ retryAfterMs
726
+ );
727
+ }
617
728
  case "INTERNAL_ERROR": {
618
729
  return new ServerError(message, status, context);
619
730
  }
@@ -678,6 +789,13 @@ function detailNumber(refusal, key) {
678
789
  return typeof value === "number" ? value : void 0;
679
790
  }
680
791
  __name(detailNumber, "detailNumber");
792
+ function runErrorOf(refusal) {
793
+ const error = detailBag(refusal)?.error;
794
+ if (typeof error !== "object" || error === null) return void 0;
795
+ const { name, message } = error;
796
+ return typeof name === "string" && typeof message === "string" ? { name, message } : void 0;
797
+ }
798
+ __name(runErrorOf, "runErrorOf");
681
799
  function blockageOf(refusal) {
682
800
  const blockage = detailBag(refusal)?.blockage;
683
801
  if (typeof blockage !== "object" || blockage === null) return void 0;
@@ -698,6 +816,15 @@ function violationsOf(refusal) {
698
816
  }));
699
817
  }
700
818
  __name(violationsOf, "violationsOf");
819
+ function endUserTokenReasonOf(refusal) {
820
+ const reason = detailString(refusal, "reason");
821
+ return isEndUserTokenReason(reason) ? reason : void 0;
822
+ }
823
+ __name(endUserTokenReasonOf, "endUserTokenReasonOf");
824
+ function isEndUserTokenReason(value) {
825
+ return value !== void 0 && END_USER_TOKEN_REFUSAL_REASONS.includes(value);
826
+ }
827
+ __name(isEndUserTokenReason, "isEndUserTokenReason");
701
828
 
702
829
  // src/http/api-routes.ts
703
830
  function fillPath(template, params) {
@@ -725,6 +852,17 @@ var DECLARED_PATHS = {
725
852
  "/health/cache": true,
726
853
  "/health/live": true,
727
854
  "/health/ready": true,
855
+ "/internal/functions/runs/{runId}/actions": true,
856
+ "/internal/functions/runs/{runId}/bundle": true,
857
+ "/internal/functions/runs/{runId}/content": true,
858
+ "/internal/functions/runs/{runId}/outcome": true,
859
+ "/internal/functions/runs/{runId}/recorded": true,
860
+ "/internal/functions/runs/{runId}/records/batch": true,
861
+ "/internal/functions/runs/{runId}/records/file": true,
862
+ "/internal/functions/runs/{runId}/records/read": true,
863
+ "/internal/functions/tasks/{taskId}/run": true,
864
+ "/internal/functions/test-runs/{projectId}/{name}": true,
865
+ "/internal/functions/tool-calls/{projectId}/{name}": true,
728
866
  "/internal/live/visitor-feed/{projectId}/{datasetId}/changes": true,
729
867
  "/internal/live/visitor-key": true,
730
868
  "/internal/media/claim": true,
@@ -761,6 +899,11 @@ var DECLARED_PATHS = {
761
899
  "/v1/project": true,
762
900
  "/v1/queries/{name}": true,
763
901
  "/v1/query": true,
902
+ "/v1/functions/{name}": true,
903
+ "/v1/records/batch": true,
904
+ "/v1/records/{recordType}": true,
905
+ "/v1/records/{recordType}/files": true,
906
+ "/v1/records/{recordType}/{uid}": true,
764
907
  "/v1/search": true,
765
908
  "/v1/search/autocomplete": true,
766
909
  "/v1/search/similar": true,
@@ -1054,22 +1197,33 @@ var _HttpClient = class _HttpClient {
1054
1197
  * constructor accepts one so that a module-scope client can be evaluated by a
1055
1198
  * build that holds no key (REL-1120) — and so does one whose `setApiKey` was
1056
1199
  * given none.
1200
+ *
1201
+ * The End user's token is asked for once, after the key is known to be there and before the first
1202
+ * attempt — of `getToken`, the nearest view's function, or the configuration's — so every retry
1203
+ * of one call sends the token its first attempt sent. See `RelyIOConfig.getToken`.
1057
1204
  */
1058
- async request(config) {
1205
+ async request(config, getToken) {
1059
1206
  if (this.view !== void 0) {
1060
- return this.view.client.request({
1061
- ...config,
1062
- headers: layerHeaders(this.view.headers, config.headers)
1063
- });
1207
+ return this.view.client.request(
1208
+ { ...config, headers: layerHeaders(this.view.headers, config.headers) },
1209
+ getToken ?? this.view.getToken
1210
+ );
1064
1211
  }
1065
1212
  if (config.timeout !== void 0) assertRequestTimeout(config.timeout);
1066
1213
  const mergedConfig = this.mergeRequestConfig(config);
1067
- if (presentKey(headerValue(mergedConfig.headers, API_KEY_HEADER)) === void 0) {
1214
+ if (presentCredential(headerValue(mergedConfig.headers, API_KEY_HEADER)) === void 0) {
1068
1215
  throw new MissingApiKeyError(
1069
1216
  "`apiKey` is missing: this client holds no API key, so the request was not sent. Pass `apiKey` to createClient \u2014 the documented setup reads it from RELYIO_API_KEY.",
1070
1217
  { url: mergedConfig.url, method: mergedConfig.method, sent: false }
1071
1218
  );
1072
1219
  }
1220
+ const tokenSource = getToken ?? this.connection.config.getToken;
1221
+ if (tokenSource !== void 0) {
1222
+ const token = presentCredential(await tokenSource());
1223
+ mergedConfig.headers = layerHeaders(mergedConfig.headers ?? {}, {
1224
+ [END_USER_HEADER]: token ?? null
1225
+ });
1226
+ }
1073
1227
  this.log("debug", "Making request", {
1074
1228
  method: mergedConfig.method,
1075
1229
  url: mergedConfig.url
@@ -1113,7 +1267,7 @@ var _HttpClient = class _HttpClient {
1113
1267
  });
1114
1268
  if (preview !== void 0 && answersUnknownDataset(sdkError, preview)) {
1115
1269
  rememberUnknownPreview(preview);
1116
- return this.request(config);
1270
+ return this.request(config, getToken);
1117
1271
  }
1118
1272
  this.log("error", "Request failed after all retries", {
1119
1273
  error: sdkError.toJSON(),
@@ -1334,10 +1488,17 @@ var _ControlledHttpClient = class _ControlledHttpClient extends HttpClient {
1334
1488
  * retries and `fetch` are this client's as they are when each request is sent, so a later
1335
1489
  * `configure()` or `setApiKey()` on this client reaches the view, and one on the view reaches
1336
1490
  * this client. `client.with({ dataset })` is one (REL-2417).
1491
+ *
1492
+ * `options.getToken` is the End user's token function the view asks in place of this client's,
1493
+ * and of any view it is made from: `client.with({ getToken })` (REL-3882).
1337
1494
  */
1338
- withHeaders(headers) {
1495
+ withHeaders(headers, options = {}) {
1339
1496
  const view = new _ControlledHttpClient(this.connection.config);
1340
- view.view = { client: this, headers: { ...headers } };
1497
+ view.view = {
1498
+ client: this,
1499
+ headers: { ...headers },
1500
+ ...options.getToken === void 0 ? {} : { getToken: options.getToken }
1501
+ };
1341
1502
  return view;
1342
1503
  }
1343
1504
  /**
@@ -1359,7 +1520,7 @@ var _ControlledHttpClient = class _ControlledHttpClient extends HttpClient {
1359
1520
  * Replace the key every later request sends as `X-API-Key`, and the `apiKey` the configuration
1360
1521
  * reports.
1361
1522
  *
1362
- * The key is read the way construction reads it (`presentKey`): an absent, empty or blank key is
1523
+ * The key is read the way construction reads it (`presentCredential`): an absent, empty or blank key is
1363
1524
  * no key, so the header is dropped and the next request is refused with `MissingApiKeyError`
1364
1525
  * before anything is sent. A key header of any other spelling — one given in `headers` — goes
1365
1526
  * too, from the headers sent and from the headers the configuration reports, so the old key is
@@ -1380,7 +1541,7 @@ var _ControlledHttpClient = class _ControlledHttpClient extends HttpClient {
1380
1541
  this.connection = {
1381
1542
  ...this.connection,
1382
1543
  defaultHeaders: layerHeaders(this.connection.defaultHeaders, {
1383
- [API_KEY_HEADER]: presentKey(apiKey) ?? null
1544
+ [API_KEY_HEADER]: presentCredential(apiKey) ?? null
1384
1545
  })
1385
1546
  };
1386
1547
  }
@@ -1389,6 +1550,7 @@ __name(_ControlledHttpClient, "ControlledHttpClient");
1389
1550
  var ControlledHttpClient = _ControlledHttpClient;
1390
1551
  var API_KEY_HEADER = "X-API-Key";
1391
1552
  var DATASET_HEADER = "X-RelyIO-Dataset";
1553
+ var END_USER_HEADER = HEADER_END_USER.name;
1392
1554
  function derivedPreviewIn(headers) {
1393
1555
  const dataset = headerValue(headers, DATASET_HEADER);
1394
1556
  return dataset !== void 0 && isDerivedPreview(dataset) ? dataset : void 0;
@@ -1407,7 +1569,7 @@ function connectionFor(config) {
1407
1569
  const defaultHeaders = layerHeaders(
1408
1570
  layerHeaders({ "Content-Type": "application/json" }, config.headers),
1409
1571
  {
1410
- [API_KEY_HEADER]: presentKey(config.apiKey),
1572
+ [API_KEY_HEADER]: presentCredential(config.apiKey),
1411
1573
  [DATASET_HEADER]: config.dataset,
1412
1574
  // Only outside a browser, which controls this header. "A browser" is the core's one
1413
1575
  // definition, `isBrowser()` — every environment question in this file asks it.
@@ -1439,10 +1601,10 @@ function layerHeaders(base, over) {
1439
1601
  return layered;
1440
1602
  }
1441
1603
  __name(layerHeaders, "layerHeaders");
1442
- function presentKey(value) {
1604
+ function presentCredential(value) {
1443
1605
  return typeof value === "string" && value.trim() !== "" ? value : void 0;
1444
1606
  }
1445
- __name(presentKey, "presentKey");
1607
+ __name(presentCredential, "presentCredential");
1446
1608
  function credentialsModeFor(includeCredentials) {
1447
1609
  return includeCredentials ? "include" : "same-origin";
1448
1610
  }
@@ -2744,6 +2906,200 @@ var _SettingsClient = class _SettingsClient extends DocumentHandle {
2744
2906
  __name(_SettingsClient, "SettingsClient");
2745
2907
  var SettingsClient = _SettingsClient;
2746
2908
 
2909
+ // src/content/records-client.ts
2910
+ function isMissingRecord(error) {
2911
+ return error instanceof ResourceNotFoundError && error.context?.resourceType === "record";
2912
+ }
2913
+ __name(isMissingRecord, "isMissingRecord");
2914
+ var _RecordsClient = class _RecordsClient {
2915
+ constructor(httpClient, recordType) {
2916
+ this.httpClient = httpClient;
2917
+ this.recordType = recordType;
2918
+ }
2919
+ /**
2920
+ * Store a Record, live the moment it lands. Values that break the Record type's fields are
2921
+ * refused with `ValidationError` naming each field, and nothing is stored. Values a unique set of
2922
+ * the type claims, held by another Record, are refused with `ResourceConflictError` — its
2923
+ * `blockage` is `{ kind: 'value-taken', facts: { fields, recordId } }` (REL-3782) — with no
2924
+ * `recordId` for an End user's write, whose read Policy may hide the holder (REL-3863).
2925
+ */
2926
+ async create(fields, options = {}) {
2927
+ return this.httpClient.fetchPayload(
2928
+ "/v1/records/{recordType}",
2929
+ "post",
2930
+ {
2931
+ path: { recordType: this.recordType },
2932
+ body: { fields },
2933
+ headers: readHeaders(options),
2934
+ ...signalOf(options)
2935
+ }
2936
+ );
2937
+ }
2938
+ /** One page of the Records `where` keeps, newest first unless `sort` names one key. */
2939
+ async list(options = {}) {
2940
+ const response = await this.httpClient.send(
2941
+ "/v1/records/{recordType}",
2942
+ "get",
2943
+ {
2944
+ path: { recordType: this.recordType },
2945
+ query: {
2946
+ ...options.where === void 0 ? {} : { filters: JSON.stringify(options.where) },
2947
+ ...options.sort === void 0 ? {} : { sort: options.sort },
2948
+ ...options.limit === void 0 ? {} : { limit: options.limit },
2949
+ ...options.cursor === void 0 ? {} : { cursor: options.cursor },
2950
+ ...options.count === true ? { count: "true" } : {},
2951
+ ...options.facets === void 0 || options.facets.length === 0 ? {} : { facets: options.facets.join(",") },
2952
+ ...includeQuery(options)
2953
+ },
2954
+ headers: readHeaders(options),
2955
+ ...signalOf(options)
2956
+ }
2957
+ );
2958
+ return response.body;
2959
+ }
2960
+ /**
2961
+ * Upload a File for one of this Record type's file fields (REL-3824), and answer what a create or
2962
+ * update sets the field to. The bytes go straight to object storage at the address the upload
2963
+ * names, never through RelyIO; a File outside the field's `accept` or over its `maxSize` is
2964
+ * refused with `ValidationError` naming the field before any byte is sent. An upload no write
2965
+ * names within a day is deleted with its bytes. Server-side only, as every Record call is.
2966
+ *
2967
+ * @example
2968
+ * ```typescript
2969
+ * const invoice = await relyio.records('order').upload('invoice', pdfBytes, {
2970
+ * name: 'invoice-1001.pdf',
2971
+ * type: 'application/pdf',
2972
+ * })
2973
+ * await relyio.records('order').update(id, { invoice }, { revision })
2974
+ * ```
2975
+ */
2976
+ async upload(field, bytes, options) {
2977
+ const size = bytes instanceof Blob ? bytes.size : bytes.byteLength;
2978
+ const upload = await this.httpClient.fetchPayload("/v1/records/{recordType}/files", "post", {
2979
+ path: { recordType: this.recordType },
2980
+ body: { field, name: options.name, type: options.type, size },
2981
+ headers: readHeaders(options),
2982
+ ...signalOf(options)
2983
+ });
2984
+ const response = await HttpClient.transfer(this.httpClient, upload.url, {
2985
+ method: upload.method,
2986
+ headers: upload.headers,
2987
+ body: bytes,
2988
+ ...signalOf(options)
2989
+ });
2990
+ if (!response.ok) {
2991
+ throw new FileUploadError(
2992
+ "INTERNAL",
2993
+ `Upload failed (${response.status} ${response.statusText}) for ${this.recordType}.${field}`,
2994
+ { retriable: response.status >= 500, statusCode: response.status }
2995
+ );
2996
+ }
2997
+ return { upload: upload.id };
2998
+ }
2999
+ /**
3000
+ * One Record by its id, or `null` when none is stored. A Record type nobody deployed throws
3001
+ * `ResourceNotFoundError` naming it, so it never reads as a missing Record.
3002
+ */
3003
+ async get(id, options = {}) {
3004
+ try {
3005
+ return await this.httpClient.fetchPayload(
3006
+ "/v1/records/{recordType}/{uid}",
3007
+ "get",
3008
+ {
3009
+ path: { recordType: this.recordType, uid: id },
3010
+ query: includeQuery(options),
3011
+ headers: readHeaders(options),
3012
+ ...signalOf(options)
3013
+ }
3014
+ );
3015
+ } catch (error) {
3016
+ if (isMissingRecord(error)) return null;
3017
+ throw error;
3018
+ }
3019
+ }
3020
+ /**
3021
+ * Change the fields named, against the revision the change was made on, and answer the Record at
3022
+ * its next revision. A field given `null` is removed; one not named keeps its value. A Record
3023
+ * written since `revision` is refused with `ResourceConflictError` — its `blockage` is
3024
+ * `{ kind: 'revision-stale', facts: { changedFields } }` — and nothing is written. Values that
3025
+ * break the Record type are refused with `ValidationError` naming each field, and values another
3026
+ * Record holds under a unique set with `ResourceConflictError`, its blockage `value-taken`.
3027
+ *
3028
+ * @example
3029
+ * ```typescript
3030
+ * const order = await relyio.records('order').get(id)
3031
+ * await relyio.records('order').update(id, { status: 'shipped' }, {
3032
+ * revision: order._metadata.revision,
3033
+ * })
3034
+ * ```
3035
+ */
3036
+ async update(id, fields, options) {
3037
+ return this.httpClient.fetchPayload(
3038
+ "/v1/records/{recordType}/{uid}",
3039
+ "patch",
3040
+ {
3041
+ path: { recordType: this.recordType, uid: id },
3042
+ body: { fields },
3043
+ headers: { ...readHeaders(options), "If-Match": `"${options.revision}"` },
3044
+ ...signalOf(options)
3045
+ }
3046
+ );
3047
+ }
3048
+ /**
3049
+ * Remove a Record. Another Record holding it through a reference declared to block its delete —
3050
+ * the default — is refused with `ResourceConflictError`, its `blockage`
3051
+ * (`{ kind: 'referenced', facts: { referrers } }`) naming each; one declared `nullify` is cleared.
3052
+ */
3053
+ async delete(id, options = {}) {
3054
+ await this.httpClient.send(
3055
+ "/v1/records/{recordType}/{uid}",
3056
+ "delete",
3057
+ {
3058
+ path: { recordType: this.recordType, uid: id },
3059
+ headers: readHeaders(options),
3060
+ ...signalOf(options)
3061
+ }
3062
+ );
3063
+ }
3064
+ };
3065
+ __name(_RecordsClient, "RecordsClient");
3066
+ var RecordsClient = _RecordsClient;
3067
+ function includeQuery(options) {
3068
+ return {
3069
+ ...options.include === void 0 || options.include.length === 0 ? {} : { include: options.include.join(",") },
3070
+ ...options.locale === void 0 ? {} : { locale: options.locale }
3071
+ };
3072
+ }
3073
+ __name(includeQuery, "includeQuery");
3074
+
3075
+ // src/content/record-batch.ts
3076
+ async function sendRecordBatch(httpClient, operations, options = {}) {
3077
+ const { idempotencyKey, ...call } = options;
3078
+ return httpClient.fetchPayload(
3079
+ "/v1/records/batch",
3080
+ "post",
3081
+ {
3082
+ body: { operations },
3083
+ headers: { ...readHeaders(call), ...writeHeaders({ idempotencyKey }) },
3084
+ ...signalOf(call)
3085
+ }
3086
+ );
3087
+ }
3088
+ __name(sendRecordBatch, "sendRecordBatch");
3089
+ var OPERATION_PATH = /^operations\[(\d+)\]/;
3090
+ function refusedOperationOf(error) {
3091
+ if (typeof error !== "object" || error === null) return void 0;
3092
+ const { context, violations } = error;
3093
+ const operation = context?.blockage?.facts?.operation;
3094
+ if (typeof operation === "number") return operation;
3095
+ for (const violation of violations ?? []) {
3096
+ const match = typeof violation.field === "string" ? OPERATION_PATH.exec(violation.field) : null;
3097
+ if (match?.[1] !== void 0) return Number(match[1]);
3098
+ }
3099
+ return void 0;
3100
+ }
3101
+ __name(refusedOperationOf, "refusedOperationOf");
3102
+
2747
3103
  // src/changes/changes.ts
2748
3104
  var CHANGES_POLL_TIMEOUT_MS = 25e3 + 5e3;
2749
3105
  var EXPIRED_REASONS2 = {
@@ -3362,6 +3718,32 @@ function hasOwn(object, key) {
3362
3718
  }
3363
3719
  __name(hasOwn, "hasOwn");
3364
3720
 
3721
+ // src/content/functions-client.ts
3722
+ var FUNCTION_CALL_TIMEOUT_MS = 31e4;
3723
+ async function callFunction(http, name, input, options) {
3724
+ return await http.fetchPayload("/v1/functions/{name}", "post", {
3725
+ path: { name },
3726
+ body: { input },
3727
+ headers: {
3728
+ ...readHeaders({ dataset: options.dataset }),
3729
+ ...options.idempotencyKey === void 0 ? {} : { [HEADER_IDEMPOTENCY_KEY.name]: options.idempotencyKey }
3730
+ },
3731
+ timeout: options.timeout ?? FUNCTION_CALL_TIMEOUT_MS,
3732
+ ...signalOf(options)
3733
+ });
3734
+ }
3735
+ __name(callFunction, "callFunction");
3736
+ function functionsAccessor(http) {
3737
+ return new Proxy({}, {
3738
+ get: /* @__PURE__ */ __name((_target, property) => {
3739
+ if (typeof property !== "string" || property === "then") return void 0;
3740
+ const call = /* @__PURE__ */ __name((input = {}, options = {}) => callFunction(http(), property, input, options), "call");
3741
+ return call;
3742
+ }, "get")
3743
+ });
3744
+ }
3745
+ __name(functionsAccessor, "functionsAccessor");
3746
+
3365
3747
  // src/search/search-options.ts
3366
3748
  function scopeOf(contentType) {
3367
3749
  if (contentType === void 0) return { contentTypes: "*" };
@@ -3780,6 +4162,9 @@ function validateConfig(config) {
3780
4162
  if (config.webSocket != null && typeof config.webSocket !== "function") {
3781
4163
  throw new Error("webSocket must be a WebSocket class");
3782
4164
  }
4165
+ if (config.getToken != null && typeof config.getToken !== "function") {
4166
+ throw new Error("getToken must be a function that answers the End user\u2019s token, or null");
4167
+ }
3783
4168
  if (config.timeout != null) assertRequestTimeout(config.timeout);
3784
4169
  if (config.retryAttempts != null && !(Number.isInteger(config.retryAttempts) && config.retryAttempts >= 1)) {
3785
4170
  throw new Error(
@@ -3908,10 +4293,57 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
3908
4293
  * `createClient()` made, which reads its own.
3909
4294
  */
3910
4295
  __publicField(this, "parent");
3911
- /** What `with()` gave this client over its parent's: the locale and dataset it names, and no other. */
4296
+ /**
4297
+ * What `with()` gave this client over its parent's: the locale, dataset and End user's token
4298
+ * function it names, and no other.
4299
+ */
3912
4300
  __publicField(this, "defaults", {});
3913
4301
  /** The live edge's poll client id: chosen once, when first needed, and kept for this client's life. */
3914
4302
  __publicField(this, "_liveClientId");
4303
+ /**
4304
+ * The Records of one Record type (spec 1, REL-3771): `create`, `list`, `get`, `update`, `delete`.
4305
+ * Type-safe when a schema is provided — the names autocomplete its `recordTypes` and the fields
4306
+ * are the type's. `relyio.records.batch([...])` writes Records of any types in one transaction,
4307
+ * all or none (REL-3774).
4308
+ *
4309
+ * A Record is an app's data, written by your server with a secret key and live the moment it
4310
+ * lands. A publishable key reads the Records a Record type's read Policy grants it (spec 4,
4311
+ * REL-3883), and writes them only for the End user whose token it carries, as the type's write
4312
+ * Policy allows (REL-3884); with no token it writes nothing (`PUBLISHABLE_KEY_REFUSED`).
4313
+ *
4314
+ * @example
4315
+ * ```typescript
4316
+ * const order = await relyio.records('order').create({ shopperEmail: 'ada@example.com', total: 19 })
4317
+ * const { data: orders } = await relyio.records('order').list({ where: { status: 'paid' } })
4318
+ * await relyio.records.batch([
4319
+ * { op: 'decrement', type: 'inventory', where: { product: mugId }, field: 'available', by: 1 },
4320
+ * { op: 'create', type: 'order', fields: { shopperEmail: 'ada@example.com', total: 19 } },
4321
+ * ])
4322
+ * ```
4323
+ */
4324
+ __publicField(this, "records", Object.assign(
4325
+ (recordType) => new RecordsClient(this.httpClient, recordType),
4326
+ {
4327
+ // Read when called, never when made: `with()` and `updateConfig()` move `httpClient`
4328
+ batch: /* @__PURE__ */ __name((operations, options) => sendRecordBatch(this.httpClient, operations, options), "batch")
4329
+ }
4330
+ // The one function both call signatures describe; the cast restates the schema's typing of its
4331
+ // argument and answer, as `content()` does, and widens nothing it is called with.
4332
+ ));
4333
+ /**
4334
+ * The project's Functions, by name (spec 2, REL-3821): `relyio.functions.checkout({ cartId })`
4335
+ * runs the Function `checkout` that the key's Branch runs, on the key's Dataset, and answers its
4336
+ * output. Call it from your server with a secret key; a run that failed throws
4337
+ * `FunctionRunFailedError` and one past five minutes `FunctionRunTimedOutError`, each naming the
4338
+ * run. Never retried: a Function may write.
4339
+ *
4340
+ * @example
4341
+ * ```typescript
4342
+ * const { orderId } = (await relyio.functions.checkout({ cartId })) as { orderId: string }
4343
+ * ```
4344
+ */
4345
+ // Read when called, never when made: `with()` and `updateConfig()` move `httpClient`
4346
+ __publicField(this, "functions", functionsAccessor(() => this.httpClient));
3915
4347
  const { schema, ...clientConfig } = config;
3916
4348
  this._schema = schema;
3917
4349
  const resolved = resolveConfig(clientConfig);
@@ -4361,6 +4793,9 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4361
4793
  * const { data } = await staging.content('post').list({ limit: 10 })
4362
4794
  *
4363
4795
  * const french = relyio.with({ locale: 'fr-FR' }) // ← autocompletes the schema's locales
4796
+ *
4797
+ * // Every request this one sends carries the signed-in End user's token (REL-3882)
4798
+ * const visitor = relyio.with({ getToken: () => session.getToken() })
4364
4799
  * ```
4365
4800
  */
4366
4801
  with(defaults) {
@@ -4374,14 +4809,16 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4374
4809
  }
4375
4810
  /** Make this client one `with()` made from `parent`: see {@link with}. */
4376
4811
  follow(parent, defaults) {
4377
- const { locale, dataset } = defaults;
4812
+ const { locale, dataset, getToken } = defaults;
4378
4813
  this.parent = parent;
4379
4814
  this.defaults = {
4380
4815
  ...locale === void 0 ? {} : { locale },
4381
- ...dataset === void 0 ? {} : { dataset }
4816
+ ...dataset === void 0 ? {} : { dataset },
4817
+ ...getToken === void 0 ? {} : { getToken }
4382
4818
  };
4383
4819
  this.httpClient = parent.httpClient.withHeaders(
4384
- dataset === void 0 ? {} : { [DATASET_HEADER]: dataset }
4820
+ dataset === void 0 ? {} : { [DATASET_HEADER]: dataset },
4821
+ getToken === void 0 ? {} : { getToken }
4385
4822
  );
4386
4823
  }
4387
4824
  /**
@@ -4432,6 +4869,6 @@ function randomClientId() {
4432
4869
  }
4433
4870
  __name(randomClientId, "randomClientId");
4434
4871
 
4435
- export { AssetClient, AssetFileBuilder, AssetUploadBuilder, AuthorizationError, BaseValidationError, ChangesExpiredError, ConnectionError, ContentClient, DefaultRetryPolicy, ErrorMapper, FileUploadError, FileValidationError, HttpClient, InvalidApiKeyError, InvalidPayloadError, LIVE_KEEPALIVE_INTERVAL_MS, LIVE_SOCKET_OPEN_TIMEOUT_MS, LiveEdgeError, MethodNotAllowedError, MissingApiKeyError, NetworkError, PREVIEW_KEY_PREFIX, PUBLISHABLE_KEY_PREFIX, PayloadTooLargeError, PlanFeatureUnavailableError, RateLimitError, ReadOnlyKeyError, ResourceConflictError, ResourceExpiredError, ResourceNotFoundError, SDKError, ServerError, ServiceIndeterminateError, SettingsClient, SingletonClient, TimeoutError, TransactionRolledBackError, TypedRelyIOClient, UnknownContractError, UsageQuotaExceededError, ValidationError, createClient, isAborted, isAssetEntry, isMissingEntry, isPreviewKey, isPublishableKey, isReplaySafe, isResourceNotFound, matchRoute, retryPolicyFor, serviceInstructedDelayMs, sleep, validateFile, versionCheckOf };
4436
- //# sourceMappingURL=chunk-VS2DEUTN.js.map
4437
- //# sourceMappingURL=chunk-VS2DEUTN.js.map
4872
+ export { AssetClient, AssetFileBuilder, AssetUploadBuilder, AuthorizationError, BaseValidationError, ChangesExpiredError, ConnectionError, ContentClient, DefaultRetryPolicy, EndUserTokenInvalidError, ErrorMapper, FileUploadError, FileValidationError, FunctionContainersBusyError, FunctionRunFailedError, FunctionRunTimedOutError, HttpClient, InvalidApiKeyError, InvalidPayloadError, LIVE_KEEPALIVE_INTERVAL_MS, LIVE_SOCKET_OPEN_TIMEOUT_MS, LiveEdgeError, MethodNotAllowedError, MissingApiKeyError, NetworkError, PREVIEW_KEY_PREFIX, PUBLISHABLE_KEY_PREFIX, PayloadTooLargeError, PlanFeatureUnavailableError, PublishableKeyRefusedError, RateLimitError, ReadOnlyKeyError, RecordsClient, ResourceConflictError, ResourceExpiredError, ResourceNotFoundError, SDKError, ServerError, ServiceIndeterminateError, SettingsClient, SingletonClient, TimeoutError, TransactionRolledBackError, TypedRelyIOClient, UnknownContractError, UsageQuotaExceededError, ValidationError, createClient, isAborted, isAssetEntry, isMissingEntry, isPreviewKey, isPublishableKey, isReplaySafe, isResourceNotFound, matchRoute, refusedOperationOf, retryPolicyFor, serviceInstructedDelayMs, sleep, validateFile, versionCheckOf };
4873
+ //# sourceMappingURL=chunk-356SF5Y2.js.map
4874
+ //# sourceMappingURL=chunk-356SF5Y2.js.map