@relyio/sdk 0.18.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 (50) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/automations/index.cjs +18 -17
  3. package/dist/automations/index.d.cts +289 -8
  4. package/dist/automations/index.d.ts +289 -8
  5. package/dist/automations/index.js +3 -2
  6. package/dist/{chunk-ZHTJ7GXH.js → chunk-356SF5Y2.js} +84 -26
  7. package/dist/{chunk-ZAMAR3X2.cjs → chunk-5F3YYPLZ.cjs} +11249 -10771
  8. package/dist/{chunk-LSI26ZN2.cjs → chunk-DBYMG6MQ.cjs} +5 -5
  9. package/dist/{chunk-SHSK7UUY.cjs → chunk-NN45CSPZ.cjs} +177 -11
  10. package/dist/{chunk-IFKF72VJ.cjs → chunk-P23G5NXD.cjs} +122 -63
  11. package/dist/{chunk-W7JFHSFM.js → chunk-PZCDJFMA.js} +11249 -10772
  12. package/dist/{chunk-EDE2H37D.js → chunk-T7LGREB3.js} +174 -12
  13. package/dist/{chunk-R6E4HE7R.js → chunk-W2GRVXDG.js} +3 -3
  14. package/dist/client/index.cjs +4 -4
  15. package/dist/client/index.d.cts +4 -4
  16. package/dist/client/index.d.ts +4 -4
  17. package/dist/client/index.js +2 -2
  18. package/dist/{define-content-type-NEQDrvdq.d.cts → define-content-type-BZ6X8GvL.d.cts} +1324 -156
  19. package/dist/{define-content-type-fb-WCD5e.d.ts → define-content-type-C12jh8cY.d.ts} +1324 -156
  20. package/dist/{define-schema-Dlwh6l_J.d.ts → define-schema-BPlZl4a2.d.cts} +104 -3
  21. package/dist/{define-schema-DZhtvLiK.d.cts → define-schema-CK7TtYot.d.ts} +104 -3
  22. package/dist/extensions/index.cjs +26 -6
  23. package/dist/extensions/index.d.cts +5 -791
  24. package/dist/extensions/index.d.ts +5 -791
  25. package/dist/extensions/index.js +24 -4
  26. package/dist/functions/index.cjs +7 -4
  27. package/dist/functions/index.d.cts +4 -4
  28. package/dist/functions/index.d.ts +4 -4
  29. package/dist/functions/index.js +5 -2
  30. package/dist/{index-M3luH06A.d.ts → index-BxNnlc5D.d.ts} +91 -73
  31. package/dist/{index-C4LqrNd1.d.ts → index-C-MymZTe.d.ts} +1 -1
  32. package/dist/{index-DwHcL6x5.d.cts → index-D5jyIeQ5.d.cts} +91 -73
  33. package/dist/{index-Dnmt_UOo.d.cts → index-DN0l8RHw.d.cts} +1 -1
  34. package/dist/index.cjs +88 -84
  35. package/dist/index.d.cts +64 -14
  36. package/dist/index.d.ts +64 -14
  37. package/dist/index.js +4 -4
  38. package/dist/preview/index.cjs +3 -3
  39. package/dist/preview/index.js +1 -1
  40. package/dist/{register-B53i97A9.d.ts → register-BiMQKVp-.d.ts} +24 -8
  41. package/dist/{register-BYCs0FpF.d.cts → register-DeMsaurp.d.cts} +24 -8
  42. package/dist/rich-text/index.d.cts +2 -2
  43. package/dist/rich-text/index.d.ts +2 -2
  44. package/dist/schema/index.cjs +41 -40
  45. package/dist/schema/index.d.cts +7 -120
  46. package/dist/schema/index.d.ts +7 -120
  47. package/dist/schema/index.js +2 -1
  48. package/dist/types/index.d.cts +1 -1
  49. package/dist/types/index.d.ts +1 -1
  50. package/package.json +2 -2
@@ -3,7 +3,7 @@
3
3
  var chunkM7MWNV3Q_cjs = require('./chunk-M7MWNV3Q.cjs');
4
4
  var chunkZO6QDYJG_cjs = require('./chunk-ZO6QDYJG.cjs');
5
5
  var chunk7I3SC7SV_cjs = require('./chunk-7I3SC7SV.cjs');
6
- var chunkSHSK7UUY_cjs = require('./chunk-SHSK7UUY.cjs');
6
+ var chunkNN45CSPZ_cjs = require('./chunk-NN45CSPZ.cjs');
7
7
  var chunkL6DXCKVQ_cjs = require('./chunk-L6DXCKVQ.cjs');
8
8
 
9
9
  /* RelyIO SDK - Built with ❤️ by the RelyIO Team */
@@ -171,13 +171,13 @@ var _ReadOnlyKeyError = class _ReadOnlyKeyError extends SDKError {
171
171
  chunkL6DXCKVQ_cjs.__name(_ReadOnlyKeyError, "ReadOnlyKeyError");
172
172
  var ReadOnlyKeyError = _ReadOnlyKeyError;
173
173
  var _PublishableKeyRefusedError = class _PublishableKeyRefusedError extends SDKError {
174
- constructor(message = "A publishable key cannot read or write Records or run Functions", context) {
174
+ constructor(message = "A publishable key writes a Record only for a signed-in End user", context) {
175
175
  super(message, 403, void 0, context);
176
176
  chunkL6DXCKVQ_cjs.__publicField(this, "code", "PUBLISHABLE_KEY_REFUSED");
177
177
  chunkL6DXCKVQ_cjs.__publicField(this, "retryable", false);
178
178
  }
179
179
  getUserMessage() {
180
- return "A publishable key cannot reach Records or Functions. Call them from your server with a secret key.";
180
+ 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.";
181
181
  }
182
182
  };
183
183
  chunkL6DXCKVQ_cjs.__name(_PublishableKeyRefusedError, "PublishableKeyRefusedError");
@@ -394,6 +394,19 @@ var _InvalidApiKeyError = class _InvalidApiKeyError extends SDKError {
394
394
  };
395
395
  chunkL6DXCKVQ_cjs.__name(_InvalidApiKeyError, "InvalidApiKeyError");
396
396
  var InvalidApiKeyError = _InvalidApiKeyError;
397
+ var _EndUserTokenInvalidError = class _EndUserTokenInvalidError extends SDKError {
398
+ constructor(message, reason, context) {
399
+ super(message, 401, void 0, context);
400
+ this.reason = reason;
401
+ chunkL6DXCKVQ_cjs.__publicField(this, "code", "END_USER_TOKEN_INVALID");
402
+ chunkL6DXCKVQ_cjs.__publicField(this, "retryable", false);
403
+ }
404
+ getUserMessage() {
405
+ return "Your session is not valid. Please sign in again.";
406
+ }
407
+ };
408
+ chunkL6DXCKVQ_cjs.__name(_EndUserTokenInvalidError, "EndUserTokenInvalidError");
409
+ var EndUserTokenInvalidError = _EndUserTokenInvalidError;
397
410
 
398
411
  // src/errors/network-errors.ts
399
412
  var _NetworkError = class _NetworkError extends SDKError {
@@ -581,7 +594,7 @@ var _ErrorMapper = class _ErrorMapper {
581
594
  return new NetworkError(`HTTP ${status}: ${statusText}`.trim(), status, void 0, context);
582
595
  }
583
596
  const retryAfterMs = parseRetryAfterMs(
584
- headerValue(headers, chunkSHSK7UUY_cjs.HEADER_RETRY_AFTER.name),
597
+ headerValue(headers, chunkNN45CSPZ_cjs.HEADER_RETRY_AFTER.name),
585
598
  Date.now()
586
599
  );
587
600
  return this.fromContractCode(refusal, status, context, retryAfterMs);
@@ -595,6 +608,9 @@ var _ErrorMapper = class _ErrorMapper {
595
608
  case "INVALID_API_KEY": {
596
609
  return new InvalidApiKeyError(message, void 0, context);
597
610
  }
611
+ case "END_USER_TOKEN_INVALID": {
612
+ return new EndUserTokenInvalidError(message, endUserTokenReasonOf(refusal), context);
613
+ }
598
614
  case "ACCESS_DENIED": {
599
615
  return new AuthorizationError(message, 403, void 0, context);
600
616
  }
@@ -802,6 +818,15 @@ function violationsOf(refusal) {
802
818
  }));
803
819
  }
804
820
  chunkL6DXCKVQ_cjs.__name(violationsOf, "violationsOf");
821
+ function endUserTokenReasonOf(refusal) {
822
+ const reason = detailString(refusal, "reason");
823
+ return isEndUserTokenReason(reason) ? reason : void 0;
824
+ }
825
+ chunkL6DXCKVQ_cjs.__name(endUserTokenReasonOf, "endUserTokenReasonOf");
826
+ function isEndUserTokenReason(value) {
827
+ return value !== void 0 && chunkNN45CSPZ_cjs.END_USER_TOKEN_REFUSAL_REASONS.includes(value);
828
+ }
829
+ chunkL6DXCKVQ_cjs.__name(isEndUserTokenReason, "isEndUserTokenReason");
805
830
 
806
831
  // src/http/api-routes.ts
807
832
  function fillPath(template, params) {
@@ -977,7 +1002,7 @@ var IDEMPOTENT_METHODS = /* @__PURE__ */ new Set([
977
1002
  ]);
978
1003
  function isReplaySafe(request) {
979
1004
  if (IDEMPOTENT_METHODS.has(request.method.toUpperCase())) return true;
980
- return headerValue(request.headers, chunkSHSK7UUY_cjs.HEADER_IDEMPOTENCY_KEY.name) !== void 0;
1005
+ return headerValue(request.headers, chunkNN45CSPZ_cjs.HEADER_IDEMPOTENCY_KEY.name) !== void 0;
981
1006
  }
982
1007
  chunkL6DXCKVQ_cjs.__name(isReplaySafe, "isReplaySafe");
983
1008
  var UNREACHABLE_CODES = /* @__PURE__ */ new Set(["ECONNREFUSED", "ENOTFOUND"]);
@@ -1174,22 +1199,33 @@ var _HttpClient = class _HttpClient {
1174
1199
  * constructor accepts one so that a module-scope client can be evaluated by a
1175
1200
  * build that holds no key (REL-1120) — and so does one whose `setApiKey` was
1176
1201
  * given none.
1202
+ *
1203
+ * The End user's token is asked for once, after the key is known to be there and before the first
1204
+ * attempt — of `getToken`, the nearest view's function, or the configuration's — so every retry
1205
+ * of one call sends the token its first attempt sent. See `RelyIOConfig.getToken`.
1177
1206
  */
1178
- async request(config) {
1207
+ async request(config, getToken) {
1179
1208
  if (this.view !== void 0) {
1180
- return this.view.client.request({
1181
- ...config,
1182
- headers: layerHeaders(this.view.headers, config.headers)
1183
- });
1209
+ return this.view.client.request(
1210
+ { ...config, headers: layerHeaders(this.view.headers, config.headers) },
1211
+ getToken ?? this.view.getToken
1212
+ );
1184
1213
  }
1185
1214
  if (config.timeout !== void 0) assertRequestTimeout(config.timeout);
1186
1215
  const mergedConfig = this.mergeRequestConfig(config);
1187
- if (presentKey(headerValue(mergedConfig.headers, API_KEY_HEADER)) === void 0) {
1216
+ if (presentCredential(headerValue(mergedConfig.headers, API_KEY_HEADER)) === void 0) {
1188
1217
  throw new MissingApiKeyError(
1189
1218
  "`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.",
1190
1219
  { url: mergedConfig.url, method: mergedConfig.method, sent: false }
1191
1220
  );
1192
1221
  }
1222
+ const tokenSource = getToken ?? this.connection.config.getToken;
1223
+ if (tokenSource !== void 0) {
1224
+ const token = presentCredential(await tokenSource());
1225
+ mergedConfig.headers = layerHeaders(mergedConfig.headers ?? {}, {
1226
+ [END_USER_HEADER]: token ?? null
1227
+ });
1228
+ }
1193
1229
  this.log("debug", "Making request", {
1194
1230
  method: mergedConfig.method,
1195
1231
  url: mergedConfig.url
@@ -1233,7 +1269,7 @@ var _HttpClient = class _HttpClient {
1233
1269
  });
1234
1270
  if (preview !== void 0 && answersUnknownDataset(sdkError, preview)) {
1235
1271
  rememberUnknownPreview(preview);
1236
- return this.request(config);
1272
+ return this.request(config, getToken);
1237
1273
  }
1238
1274
  this.log("error", "Request failed after all retries", {
1239
1275
  error: sdkError.toJSON(),
@@ -1454,10 +1490,17 @@ var _ControlledHttpClient = class _ControlledHttpClient extends HttpClient {
1454
1490
  * retries and `fetch` are this client's as they are when each request is sent, so a later
1455
1491
  * `configure()` or `setApiKey()` on this client reaches the view, and one on the view reaches
1456
1492
  * this client. `client.with({ dataset })` is one (REL-2417).
1493
+ *
1494
+ * `options.getToken` is the End user's token function the view asks in place of this client's,
1495
+ * and of any view it is made from: `client.with({ getToken })` (REL-3882).
1457
1496
  */
1458
- withHeaders(headers) {
1497
+ withHeaders(headers, options = {}) {
1459
1498
  const view = new _ControlledHttpClient(this.connection.config);
1460
- view.view = { client: this, headers: { ...headers } };
1499
+ view.view = {
1500
+ client: this,
1501
+ headers: { ...headers },
1502
+ ...options.getToken === void 0 ? {} : { getToken: options.getToken }
1503
+ };
1461
1504
  return view;
1462
1505
  }
1463
1506
  /**
@@ -1479,7 +1522,7 @@ var _ControlledHttpClient = class _ControlledHttpClient extends HttpClient {
1479
1522
  * Replace the key every later request sends as `X-API-Key`, and the `apiKey` the configuration
1480
1523
  * reports.
1481
1524
  *
1482
- * The key is read the way construction reads it (`presentKey`): an absent, empty or blank key is
1525
+ * The key is read the way construction reads it (`presentCredential`): an absent, empty or blank key is
1483
1526
  * no key, so the header is dropped and the next request is refused with `MissingApiKeyError`
1484
1527
  * before anything is sent. A key header of any other spelling — one given in `headers` — goes
1485
1528
  * too, from the headers sent and from the headers the configuration reports, so the old key is
@@ -1500,7 +1543,7 @@ var _ControlledHttpClient = class _ControlledHttpClient extends HttpClient {
1500
1543
  this.connection = {
1501
1544
  ...this.connection,
1502
1545
  defaultHeaders: layerHeaders(this.connection.defaultHeaders, {
1503
- [API_KEY_HEADER]: presentKey(apiKey) ?? null
1546
+ [API_KEY_HEADER]: presentCredential(apiKey) ?? null
1504
1547
  })
1505
1548
  };
1506
1549
  }
@@ -1509,6 +1552,7 @@ chunkL6DXCKVQ_cjs.__name(_ControlledHttpClient, "ControlledHttpClient");
1509
1552
  var ControlledHttpClient = _ControlledHttpClient;
1510
1553
  var API_KEY_HEADER = "X-API-Key";
1511
1554
  var DATASET_HEADER = "X-RelyIO-Dataset";
1555
+ var END_USER_HEADER = chunkNN45CSPZ_cjs.HEADER_END_USER.name;
1512
1556
  function derivedPreviewIn(headers) {
1513
1557
  const dataset = headerValue(headers, DATASET_HEADER);
1514
1558
  return dataset !== void 0 && isDerivedPreview(dataset) ? dataset : void 0;
@@ -1527,7 +1571,7 @@ function connectionFor(config) {
1527
1571
  const defaultHeaders = layerHeaders(
1528
1572
  layerHeaders({ "Content-Type": "application/json" }, config.headers),
1529
1573
  {
1530
- [API_KEY_HEADER]: presentKey(config.apiKey),
1574
+ [API_KEY_HEADER]: presentCredential(config.apiKey),
1531
1575
  [DATASET_HEADER]: config.dataset,
1532
1576
  // Only outside a browser, which controls this header. "A browser" is the core's one
1533
1577
  // definition, `isBrowser()` — every environment question in this file asks it.
@@ -1559,10 +1603,10 @@ function layerHeaders(base, over) {
1559
1603
  return layered;
1560
1604
  }
1561
1605
  chunkL6DXCKVQ_cjs.__name(layerHeaders, "layerHeaders");
1562
- function presentKey(value) {
1606
+ function presentCredential(value) {
1563
1607
  return typeof value === "string" && value.trim() !== "" ? value : void 0;
1564
1608
  }
1565
- chunkL6DXCKVQ_cjs.__name(presentKey, "presentKey");
1609
+ chunkL6DXCKVQ_cjs.__name(presentCredential, "presentCredential");
1566
1610
  function credentialsModeFor(includeCredentials) {
1567
1611
  return includeCredentials ? "include" : "same-origin";
1568
1612
  }
@@ -1571,7 +1615,7 @@ chunkL6DXCKVQ_cjs.__name(credentialsModeFor, "credentialsModeFor");
1571
1615
  // src/http/write-options.ts
1572
1616
  function versionCheckOf(entry) {
1573
1617
  const { id, locale, version, sharedVersion } = entry._metadata;
1574
- return { id, locale, ifMatch: chunkSHSK7UUY_cjs.entityTag({ entryUid: id, locale, version, sharedVersion }) };
1618
+ return { id, locale, ifMatch: chunkNN45CSPZ_cjs.entityTag({ entryUid: id, locale, version, sharedVersion }) };
1575
1619
  }
1576
1620
  chunkL6DXCKVQ_cjs.__name(versionCheckOf, "versionCheckOf");
1577
1621
  function mintIdempotencyKey() {
@@ -1583,11 +1627,11 @@ chunkL6DXCKVQ_cjs.__name(mintIdempotencyKey, "mintIdempotencyKey");
1583
1627
  function writeHeaders(options) {
1584
1628
  const headers = {};
1585
1629
  if (options?.ifMatch !== void 0) {
1586
- headers[chunkSHSK7UUY_cjs.HEADER_IF_MATCH.name] = options.ifMatch;
1630
+ headers[chunkNN45CSPZ_cjs.HEADER_IF_MATCH.name] = options.ifMatch;
1587
1631
  }
1588
1632
  const idempotencyKey = options?.idempotencyKey ?? mintIdempotencyKey();
1589
1633
  if (idempotencyKey !== void 0) {
1590
- headers[chunkSHSK7UUY_cjs.HEADER_IDEMPOTENCY_KEY.name] = idempotencyKey;
1634
+ headers[chunkNN45CSPZ_cjs.HEADER_IDEMPOTENCY_KEY.name] = idempotencyKey;
1591
1635
  }
1592
1636
  return Object.keys(headers).length > 0 ? headers : void 0;
1593
1637
  }
@@ -1684,7 +1728,7 @@ function writeDocumentTerms(query, options, clientLocale) {
1684
1728
  query.include = options.include.join(",");
1685
1729
  }
1686
1730
  writeLocale(query, options.locale ?? clientLocale);
1687
- if (typeof options.fresh === "string") query[chunkSHSK7UUY_cjs.FRESH_PARAMETER] = options.fresh;
1731
+ if (typeof options.fresh === "string") query[chunkNN45CSPZ_cjs.FRESH_PARAMETER] = options.fresh;
1688
1732
  }
1689
1733
  chunkL6DXCKVQ_cjs.__name(writeDocumentTerms, "writeDocumentTerms");
1690
1734
  function writeLocale(query, locale) {
@@ -2878,7 +2922,8 @@ var _RecordsClient = class _RecordsClient {
2878
2922
  * Store a Record, live the moment it lands. Values that break the Record type's fields are
2879
2923
  * refused with `ValidationError` naming each field, and nothing is stored. Values a unique set of
2880
2924
  * the type claims, held by another Record, are refused with `ResourceConflictError` — its
2881
- * `blockage` is `{ kind: 'value-taken', facts: { fields, recordId } }` (REL-3782).
2925
+ * `blockage` is `{ kind: 'value-taken', facts: { fields, recordId } }` (REL-3782) — with no
2926
+ * `recordId` for an End user's write, whose read Policy may hide the holder (REL-3863).
2882
2927
  */
2883
2928
  async create(fields, options = {}) {
2884
2929
  return this.httpClient.fetchPayload(
@@ -3272,7 +3317,7 @@ chunkL6DXCKVQ_cjs.__name(originOf, "originOf");
3272
3317
 
3273
3318
  // src/changes/live-edge.ts
3274
3319
  var LIVE_KEEPALIVE_INTERVAL_MS = 3e4;
3275
- var LIVE_SOCKET_OPEN_TIMEOUT_MS = chunkSHSK7UUY_cjs.VISITOR_POLL_INTERVAL_MS;
3320
+ var LIVE_SOCKET_OPEN_TIMEOUT_MS = chunkNN45CSPZ_cjs.VISITOR_POLL_INTERVAL_MS;
3276
3321
  var LIVE_POLLS_BEFORE_SOCKET = 4;
3277
3322
  async function* subscribeLiveEdge(client, options = {}) {
3278
3323
  const { signal } = options;
@@ -3342,12 +3387,12 @@ async function* followSocket(socket, reach, options) {
3342
3387
  return "unopened";
3343
3388
  }
3344
3389
  case "message": {
3345
- if (event.data === chunkSHSK7UUY_cjs.VISITOR_KEEPALIVE.response) continue;
3346
- const frame = typeof event.data === "string" ? chunkSHSK7UUY_cjs.parseVisitorFrame(event.data) : null;
3390
+ if (event.data === chunkNN45CSPZ_cjs.VISITOR_KEEPALIVE.response) continue;
3391
+ const frame = typeof event.data === "string" ? chunkNN45CSPZ_cjs.parseVisitorFrame(event.data) : null;
3347
3392
  if (frame === null)
3348
3393
  throw unreadable(`a frame the wire does not declare: ${sample(event.data)}`);
3349
3394
  switch (frame.type) {
3350
- case chunkSHSK7UUY_cjs.VISITOR_FRAMES.opened: {
3395
+ case chunkNN45CSPZ_cjs.VISITOR_FRAMES.opened: {
3351
3396
  caughtUp = true;
3352
3397
  inbox.caughtUp();
3353
3398
  reach.position = frame.cursor;
@@ -3355,7 +3400,7 @@ async function* followSocket(socket, reach, options) {
3355
3400
  options.reached?.(frame.cursor);
3356
3401
  continue;
3357
3402
  }
3358
- case chunkSHSK7UUY_cjs.VISITOR_FRAMES.changes: {
3403
+ case chunkNN45CSPZ_cjs.VISITOR_FRAMES.changes: {
3359
3404
  const prior = reach.position;
3360
3405
  if (prior === void 0) throw unreadable("changes before the socket had a position");
3361
3406
  for (const change of changesOf(frame.changes, frame.cursor, prior)) {
@@ -3366,7 +3411,7 @@ async function* followSocket(socket, reach, options) {
3366
3411
  if (frame.changes.length === 0) options.reached?.(frame.cursor);
3367
3412
  continue;
3368
3413
  }
3369
- case chunkSHSK7UUY_cjs.VISITOR_FRAMES.resync: {
3414
+ case chunkNN45CSPZ_cjs.VISITOR_FRAMES.resync: {
3370
3415
  await expire(frame.reason, frame.cursor, options);
3371
3416
  if (isAborted(signal)) return "aborted";
3372
3417
  reach.position = frame.cursor;
@@ -3438,7 +3483,7 @@ function socketInbox(socket, signal) {
3438
3483
  return;
3439
3484
  }
3440
3485
  heard = false;
3441
- if (socket.readyState === SOCKET_OPEN) socket.send(chunkSHSK7UUY_cjs.VISITOR_KEEPALIVE.request);
3486
+ if (socket.readyState === SOCKET_OPEN) socket.send(chunkNN45CSPZ_cjs.VISITOR_KEEPALIVE.request);
3442
3487
  }, LIVE_KEEPALIVE_INTERVAL_MS);
3443
3488
  },
3444
3489
  caughtUp() {
@@ -3474,19 +3519,19 @@ var AFTER_A_CLOSE = {
3474
3519
  "back-off": "Retrying after a back-off of at least a minute, without polling, which the same admission refuses."
3475
3520
  };
3476
3521
  function closeRetryOf(code) {
3477
- if (hasOwn(chunkSHSK7UUY_cjs.VISITOR_CLOSE_RETRY, code)) return chunkSHSK7UUY_cjs.VISITOR_CLOSE_RETRY[code];
3522
+ if (hasOwn(chunkNN45CSPZ_cjs.VISITOR_CLOSE_RETRY, code)) return chunkNN45CSPZ_cjs.VISITOR_CLOSE_RETRY[code];
3478
3523
  return code < 4e3 ? "retry" : "stop";
3479
3524
  }
3480
3525
  chunkL6DXCKVQ_cjs.__name(closeRetryOf, "closeRetryOf");
3481
3526
  var CLOSE_MEANING = {
3482
- [chunkSHSK7UUY_cjs.VISITOR_CLOSE_CODES.requestInvalid]: "the edge could not read the request \u2014 a malformed cursor or filter",
3483
- [chunkSHSK7UUY_cjs.VISITOR_CLOSE_CODES.keyRefused]: "the key is not a publishable key the edge accepts \u2014 unknown, revoked or expired",
3484
- [chunkSHSK7UUY_cjs.VISITOR_CLOSE_CODES.originRefused]: "this page\u2019s origin is not on the publishable key\u2019s allowed origins",
3485
- [chunkSHSK7UUY_cjs.VISITOR_CLOSE_CODES.datasetRefused]: "`dataset` names no dataset the key reaches",
3486
- [chunkSHSK7UUY_cjs.VISITOR_CLOSE_CODES.unavailable]: "the edge could not serve this connection just now \u2014 it could not reach what it needed",
3487
- [chunkSHSK7UUY_cjs.VISITOR_CLOSE_CODES.allowanceReached]: "the project holds as many live connections as its plan allows",
3488
- [chunkSHSK7UUY_cjs.VISITOR_CLOSE_CODES.addressLimitReached]: "this address holds as many live connections to the project as one address may",
3489
- [chunkSHSK7UUY_cjs.VISITOR_CLOSE_CODES.ceilingReached]: "the project is at its safety ceiling of live connections"
3527
+ [chunkNN45CSPZ_cjs.VISITOR_CLOSE_CODES.requestInvalid]: "the edge could not read the request \u2014 a malformed cursor or filter",
3528
+ [chunkNN45CSPZ_cjs.VISITOR_CLOSE_CODES.keyRefused]: "the key is not a publishable key the edge accepts \u2014 unknown, revoked or expired",
3529
+ [chunkNN45CSPZ_cjs.VISITOR_CLOSE_CODES.originRefused]: "this page\u2019s origin is not on the publishable key\u2019s allowed origins",
3530
+ [chunkNN45CSPZ_cjs.VISITOR_CLOSE_CODES.datasetRefused]: "`dataset` names no dataset the key reaches",
3531
+ [chunkNN45CSPZ_cjs.VISITOR_CLOSE_CODES.unavailable]: "the edge could not serve this connection just now \u2014 it could not reach what it needed",
3532
+ [chunkNN45CSPZ_cjs.VISITOR_CLOSE_CODES.allowanceReached]: "the project holds as many live connections as its plan allows",
3533
+ [chunkNN45CSPZ_cjs.VISITOR_CLOSE_CODES.addressLimitReached]: "this address holds as many live connections to the project as one address may",
3534
+ [chunkNN45CSPZ_cjs.VISITOR_CLOSE_CODES.ceilingReached]: "the project is at its safety ceiling of live connections"
3490
3535
  };
3491
3536
  async function* followPolls(client, request, reach, options) {
3492
3537
  const { signal } = options;
@@ -3514,7 +3559,7 @@ async function* followPolls(client, request, reach, options) {
3514
3559
  if (starting) options.reached?.(answer.cursor);
3515
3560
  }
3516
3561
  starting = false;
3517
- await sleep(chunkSHSK7UUY_cjs.VISITOR_POLL_INTERVAL_MS, signal);
3562
+ await sleep(chunkNN45CSPZ_cjs.VISITOR_POLL_INTERVAL_MS, signal);
3518
3563
  }
3519
3564
  return isAborted(signal) ? "aborted" : "socket";
3520
3565
  }
@@ -3556,7 +3601,7 @@ async function pollOnce2(client, request, since, signal) {
3556
3601
  }
3557
3602
  const body = parseJson(text);
3558
3603
  if (!response.ok) throw refusedPoll(response, body, context);
3559
- const answer = chunkSHSK7UUY_cjs.readVisitorPollAnswer(isRecord(body) ? body["data"] : void 0);
3604
+ const answer = chunkNN45CSPZ_cjs.readVisitorPollAnswer(isRecord(body) ? body["data"] : void 0);
3560
3605
  if (answer === null)
3561
3606
  throw unreadable(`a poll answer the wire does not declare: ${sample(text)}`);
3562
3607
  return answer;
@@ -3576,13 +3621,13 @@ function refusedPoll(response, body, context) {
3576
3621
  { ...context, method: "GET" }
3577
3622
  );
3578
3623
  const refusal = readError(body);
3579
- if (refusal === void 0 || !hasOwn(chunkSHSK7UUY_cjs.VISITOR_POLL_RETRY, refusal.code)) return mapped;
3624
+ if (refusal === void 0 || !hasOwn(chunkNN45CSPZ_cjs.VISITOR_POLL_RETRY, refusal.code)) return mapped;
3580
3625
  const code = refusal.code;
3581
3626
  const retryAfterMs = parseRetryAfterMs(headerValue(headers, "Retry-After"), Date.now());
3582
3627
  return new LiveEdgeError(
3583
3628
  `The live edge refused a poll (${code}): ${refusal.messages[0] ?? mapped.message}`,
3584
3629
  { kind: "refused", code },
3585
- chunkSHSK7UUY_cjs.VISITOR_POLL_RETRY[code],
3630
+ chunkNN45CSPZ_cjs.VISITOR_POLL_RETRY[code],
3586
3631
  {
3587
3632
  statusCode: response.status,
3588
3633
  cause: mapped,
@@ -3610,17 +3655,17 @@ function edgeRequest(client, filter) {
3610
3655
  chunkL6DXCKVQ_cjs.__name(edgeRequest, "edgeRequest");
3611
3656
  function edgeUrl(request, since, transport, clientId) {
3612
3657
  const url = new URL(request.base.href);
3613
- url.pathname = `${url.pathname.replace(/\/+$/, "")}${chunkSHSK7UUY_cjs.VISITOR_LIVE_PATH}`;
3658
+ url.pathname = `${url.pathname.replace(/\/+$/, "")}${chunkNN45CSPZ_cjs.VISITOR_LIVE_PATH}`;
3614
3659
  if (transport === "socket") url.protocol = url.protocol === "http:" ? "ws:" : "wss:";
3615
3660
  const query = new URLSearchParams();
3616
- if (request.key !== void 0) query.set(chunkSHSK7UUY_cjs.VISITOR_QUERY.key, request.key);
3661
+ if (request.key !== void 0) query.set(chunkNN45CSPZ_cjs.VISITOR_QUERY.key, request.key);
3617
3662
  if (request.dataset !== void 0 && request.dataset !== "") {
3618
- query.set(chunkSHSK7UUY_cjs.VISITOR_QUERY.dataset, request.dataset);
3663
+ query.set(chunkNN45CSPZ_cjs.VISITOR_QUERY.dataset, request.dataset);
3619
3664
  }
3620
- if (since !== void 0) query.set(chunkSHSK7UUY_cjs.VISITOR_QUERY.since, since);
3621
- if (request.types !== void 0) query.set(chunkSHSK7UUY_cjs.VISITOR_QUERY.types, request.types.join(","));
3622
- if (request.ids !== void 0) query.set(chunkSHSK7UUY_cjs.VISITOR_QUERY.ids, request.ids.join(","));
3623
- if (transport === "poll" && clientId !== void 0) query.set(chunkSHSK7UUY_cjs.VISITOR_QUERY.client, clientId);
3665
+ if (since !== void 0) query.set(chunkNN45CSPZ_cjs.VISITOR_QUERY.since, since);
3666
+ if (request.types !== void 0) query.set(chunkNN45CSPZ_cjs.VISITOR_QUERY.types, request.types.join(","));
3667
+ if (request.ids !== void 0) query.set(chunkNN45CSPZ_cjs.VISITOR_QUERY.ids, request.ids.join(","));
3668
+ if (transport === "poll" && clientId !== void 0) query.set(chunkNN45CSPZ_cjs.VISITOR_QUERY.client, clientId);
3624
3669
  url.search = query.toString();
3625
3670
  return url.href;
3626
3671
  }
@@ -3683,7 +3728,7 @@ async function callFunction(http, name, input, options) {
3683
3728
  body: { input },
3684
3729
  headers: {
3685
3730
  ...readHeaders({ dataset: options.dataset }),
3686
- ...options.idempotencyKey === void 0 ? {} : { [chunkSHSK7UUY_cjs.HEADER_IDEMPOTENCY_KEY.name]: options.idempotencyKey }
3731
+ ...options.idempotencyKey === void 0 ? {} : { [chunkNN45CSPZ_cjs.HEADER_IDEMPOTENCY_KEY.name]: options.idempotencyKey }
3687
3732
  },
3688
3733
  timeout: options.timeout ?? FUNCTION_CALL_TIMEOUT_MS,
3689
3734
  ...signalOf(options)
@@ -3733,7 +3778,7 @@ function transportOf(options) {
3733
3778
  return {
3734
3779
  ...signalOf(options),
3735
3780
  ...headers === void 0 ? {} : { headers },
3736
- ...typeof options.fresh === "string" ? { query: { [chunkSHSK7UUY_cjs.FRESH_PARAMETER]: options.fresh } } : {}
3781
+ ...typeof options.fresh === "string" ? { query: { [chunkNN45CSPZ_cjs.FRESH_PARAMETER]: options.fresh } } : {}
3737
3782
  };
3738
3783
  }
3739
3784
  chunkL6DXCKVQ_cjs.__name(transportOf, "transportOf");
@@ -3914,7 +3959,7 @@ async function runAdhocQuery(http, query, clientLocale) {
3914
3959
  const terms = {};
3915
3960
  if (query.cursor !== void 0) terms.cursor = query.cursor;
3916
3961
  writeLocale(terms, query.locale ?? clientLocale);
3917
- if (typeof query.fresh === "string") terms[chunkSHSK7UUY_cjs.FRESH_PARAMETER] = query.fresh;
3962
+ if (typeof query.fresh === "string") terms[chunkNN45CSPZ_cjs.FRESH_PARAMETER] = query.fresh;
3918
3963
  const parameters = { ...query.params };
3919
3964
  const body = {
3920
3965
  query: {
@@ -3949,7 +3994,7 @@ async function runNamedQuery(http, name, options, clientLocale) {
3949
3994
  const locale = options.locale ?? clientLocale;
3950
3995
  const terms = {};
3951
3996
  if (locale !== void 0 && locale !== "") terms.locale = locale;
3952
- if (typeof options.fresh === "string") terms[chunkSHSK7UUY_cjs.FRESH_PARAMETER] = options.fresh;
3997
+ if (typeof options.fresh === "string") terms[chunkNN45CSPZ_cjs.FRESH_PARAMETER] = options.fresh;
3953
3998
  const headers = readHeaders(options);
3954
3999
  const response = await http.send(
3955
4000
  "/v1/queries/{name}",
@@ -4119,6 +4164,9 @@ function validateConfig(config) {
4119
4164
  if (config.webSocket != null && typeof config.webSocket !== "function") {
4120
4165
  throw new Error("webSocket must be a WebSocket class");
4121
4166
  }
4167
+ if (config.getToken != null && typeof config.getToken !== "function") {
4168
+ throw new Error("getToken must be a function that answers the End user\u2019s token, or null");
4169
+ }
4122
4170
  if (config.timeout != null) assertRequestTimeout(config.timeout);
4123
4171
  if (config.retryAttempts != null && !(Number.isInteger(config.retryAttempts) && config.retryAttempts >= 1)) {
4124
4172
  throw new Error(
@@ -4180,7 +4228,7 @@ function withPreview(configured) {
4180
4228
  if (configured.dataset !== void 0 || !isPreviewKey(configured.apiKey)) return configured;
4181
4229
  const preview = readPreviewAtRequest();
4182
4230
  if (preview.state !== "preview") return configured;
4183
- const dataset = chunkSHSK7UUY_cjs.previewDatasetSlug(preview.branch);
4231
+ const dataset = chunkNN45CSPZ_cjs.previewDatasetSlug(preview.branch);
4184
4232
  noteDerivedPreview(dataset);
4185
4233
  if (!previewNotified) {
4186
4234
  previewNotified = true;
@@ -4247,7 +4295,10 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4247
4295
  * `createClient()` made, which reads its own.
4248
4296
  */
4249
4297
  chunkL6DXCKVQ_cjs.__publicField(this, "parent");
4250
- /** What `with()` gave this client over its parent's: the locale and dataset it names, and no other. */
4298
+ /**
4299
+ * What `with()` gave this client over its parent's: the locale, dataset and End user's token
4300
+ * function it names, and no other.
4301
+ */
4251
4302
  chunkL6DXCKVQ_cjs.__publicField(this, "defaults", {});
4252
4303
  /** The live edge's poll client id: chosen once, when first needed, and kept for this client's life. */
4253
4304
  chunkL6DXCKVQ_cjs.__publicField(this, "_liveClientId");
@@ -4258,7 +4309,9 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4258
4309
  * all or none (REL-3774).
4259
4310
  *
4260
4311
  * A Record is an app's data, written by your server with a secret key and live the moment it
4261
- * lands; a publishable key is refused on every Record route (`PUBLISHABLE_KEY_REFUSED`).
4312
+ * lands. A publishable key reads the Records a Record type's read Policy grants it (spec 4,
4313
+ * REL-3883), and writes them only for the End user whose token it carries, as the type's write
4314
+ * Policy allows (REL-3884); with no token it writes nothing (`PUBLISHABLE_KEY_REFUSED`).
4262
4315
  *
4263
4316
  * @example
4264
4317
  * ```typescript
@@ -4742,6 +4795,9 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4742
4795
  * const { data } = await staging.content('post').list({ limit: 10 })
4743
4796
  *
4744
4797
  * const french = relyio.with({ locale: 'fr-FR' }) // ← autocompletes the schema's locales
4798
+ *
4799
+ * // Every request this one sends carries the signed-in End user's token (REL-3882)
4800
+ * const visitor = relyio.with({ getToken: () => session.getToken() })
4745
4801
  * ```
4746
4802
  */
4747
4803
  with(defaults) {
@@ -4755,14 +4811,16 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4755
4811
  }
4756
4812
  /** Make this client one `with()` made from `parent`: see {@link with}. */
4757
4813
  follow(parent, defaults) {
4758
- const { locale, dataset } = defaults;
4814
+ const { locale, dataset, getToken } = defaults;
4759
4815
  this.parent = parent;
4760
4816
  this.defaults = {
4761
4817
  ...locale === void 0 ? {} : { locale },
4762
- ...dataset === void 0 ? {} : { dataset }
4818
+ ...dataset === void 0 ? {} : { dataset },
4819
+ ...getToken === void 0 ? {} : { getToken }
4763
4820
  };
4764
4821
  this.httpClient = parent.httpClient.withHeaders(
4765
- dataset === void 0 ? {} : { [DATASET_HEADER]: dataset }
4822
+ dataset === void 0 ? {} : { [DATASET_HEADER]: dataset },
4823
+ getToken === void 0 ? {} : { getToken }
4766
4824
  );
4767
4825
  }
4768
4826
  /**
@@ -4822,6 +4880,7 @@ exports.ChangesExpiredError = ChangesExpiredError;
4822
4880
  exports.ConnectionError = ConnectionError;
4823
4881
  exports.ContentClient = ContentClient;
4824
4882
  exports.DefaultRetryPolicy = DefaultRetryPolicy;
4883
+ exports.EndUserTokenInvalidError = EndUserTokenInvalidError;
4825
4884
  exports.ErrorMapper = ErrorMapper;
4826
4885
  exports.FileUploadError = FileUploadError;
4827
4886
  exports.FileValidationError = FileValidationError;
@@ -4874,5 +4933,5 @@ exports.serviceInstructedDelayMs = serviceInstructedDelayMs;
4874
4933
  exports.sleep = sleep;
4875
4934
  exports.validateFile = validateFile;
4876
4935
  exports.versionCheckOf = versionCheckOf;
4877
- //# sourceMappingURL=chunk-IFKF72VJ.cjs.map
4878
- //# sourceMappingURL=chunk-IFKF72VJ.cjs.map
4936
+ //# sourceMappingURL=chunk-P23G5NXD.cjs.map
4937
+ //# sourceMappingURL=chunk-P23G5NXD.cjs.map