@tinycloud/cli 1.0.0-beta.13 → 1.0.0-beta.15

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
@@ -13682,16 +13682,17 @@ var init_dist2 = __esm({
13682
13682
  ...options?.ifNoneMatch === void 0 ? {} : { "if-none-match": options.ifNoneMatch }
13683
13683
  }
13684
13684
  );
13685
+ if (response.status === 401 || response.status === 403) {
13686
+ const errorText = await response.text();
13687
+ const message = errorText.trim().length > 0 ? errorText : `Failed to put key "${key}": ${response.status} - ${response.statusText || "authorization failed"}`;
13688
+ const { resource, action: requiredAction } = parseAuthError(errorText);
13689
+ return err(authUnauthorizedError("kv", message, {
13690
+ status: response.status,
13691
+ ...requiredAction && { requiredAction },
13692
+ ...resource && { resource }
13693
+ }));
13694
+ }
13685
13695
  if (!response.ok) {
13686
- if (response.status === 401) {
13687
- const errorText2 = await response.text();
13688
- const { resource, action } = parseAuthError(errorText2);
13689
- return err(authUnauthorizedError("kv", errorText2, {
13690
- status: response.status,
13691
- ...action && { requiredAction: action },
13692
- ...resource && { resource }
13693
- }));
13694
- }
13695
13696
  const errorText = await response.text();
13696
13697
  if (response.status === 412) {
13697
13698
  return err(serviceError(
@@ -17970,6 +17971,7 @@ function redactPublishedShare(result) {
17970
17971
  target: { ...result.metadata.target },
17971
17972
  resource: { ...result.metadata.resource },
17972
17973
  actions: [...result.metadata.actions],
17974
+ ...result.metadata.expiryClamped === void 0 ? {} : { expiryClamped: result.metadata.expiryClamped },
17973
17975
  expiresAt: result.metadata.expiresAt,
17974
17976
  display: { ...result.metadata.display }
17975
17977
  }
@@ -18329,6 +18331,7 @@ async function publishTargetShare(input) {
18329
18331
  ...input.actions === void 0 ? {} : { actions: input.actions },
18330
18332
  target,
18331
18333
  expiresAt,
18334
+ expiryWasExplicit: input.expiryWasExplicit ?? input.expiresAt !== void 0,
18332
18335
  origin: input.origin,
18333
18336
  ...input.notify === void 0 ? {} : { notify: input.notify }
18334
18337
  });
@@ -18945,7 +18948,11 @@ function canonicalViewerUrl(viewerOrigin) {
18945
18948
  async function createNativeShare(sharing, input) {
18946
18949
  if (!input.path || input.path.startsWith("/") || input.path.endsWith("/") || input.path.includes("..") || input.path.includes("//")) throw new TypeError("native share path must be one canonical TinyCloud KV key");
18947
18950
  const generated = await sharing.generate({ path: input.path, actions: ["tinycloud.kv/get"], expiry: input.expiresAt });
18948
- if (generated.ok !== true) throw new Error(typeof generated.error?.message === "string" ? generated.error.message : "TinyCloud sharing service rejected delegation generation");
18951
+ if (generated.ok !== true) {
18952
+ const error = new Error(typeof generated.error?.message === "string" ? generated.error.message : "TinyCloud sharing service rejected delegation generation");
18953
+ if (typeof generated.error?.code === "string") Object.assign(error, { code: generated.error.code });
18954
+ throw error;
18955
+ }
18949
18956
  if (typeof generated.data?.token !== "string" || typeof generated.data.delegation?.cid !== "string" || !(generated.data.expiresAt instanceof Date)) throw new Error("TinyCloud sharing service returned incomplete delegation metadata");
18950
18957
  const delegation = sharing.decodeLink(generated.data.token);
18951
18958
  if (typeof delegation.spaceId !== "string" || delegation.spaceId.length === 0 || typeof delegation.path !== "string" || delegation.path.length === 0) throw new Error("TinyCloud sharing service returned a non-canonical delegation authority");
@@ -27126,7 +27133,7 @@ var init_dist4 = __esm({
27126
27133
  });
27127
27134
  EPHEMERAL_MS = 60 * 60 * 1e3;
27128
27135
  SIGNED_READ_URL_MS = 5 * 60 * 1e3;
27129
- SESSION_MS = 7 * 24 * 60 * 60 * 1e3;
27136
+ SESSION_MS = 30 * 24 * 60 * 60 * 1e3;
27130
27137
  SHARE_MS = 7 * 24 * 60 * 60 * 1e3;
27131
27138
  APP_MS = 30 * 24 * 60 * 60 * 1e3;
27132
27139
  MAX_MS = 10 * 365 * 24 * 60 * 60 * 1e3;
@@ -29393,6 +29400,15 @@ async function writeShareOutput(directory, filename, bytes, force) {
29393
29400
  return join4(outputDirectory, safeName);
29394
29401
  }
29395
29402
 
29403
+ // src/share/errors.ts
29404
+ var SharePublishAuthorityError = class extends Error {
29405
+ constructor(failure) {
29406
+ super(failure.kind);
29407
+ this.failure = failure;
29408
+ this.name = "SharePublishAuthorityError";
29409
+ }
29410
+ };
29411
+
29396
29412
  // src/commands/share.ts
29397
29413
  var SHARE_ORIGIN = "https://share.tinycloud.xyz";
29398
29414
  var shareServices = {};
@@ -29409,6 +29425,33 @@ function parseShareTarget(value) {
29409
29425
  }
29410
29426
  function shareCliError(error) {
29411
29427
  if (error instanceof CLIError) return error;
29428
+ if (error instanceof SharePublishAuthorityError) {
29429
+ const failure = error.failure;
29430
+ const profileName = "profileName" in failure ? failure.profileName : void 0;
29431
+ const localKey = "localKey" in failure && failure.localKey === true;
29432
+ const profileHint = profileName === void 0 ? "" : `--profile ${profileName} `;
29433
+ const loginHint = `tc ${profileHint}auth login${localKey ? " --method local" : ""}`;
29434
+ if (failure.kind === "owner-space-unresolved") {
29435
+ return new CLIError("AUTH_REQUIRED", `a valid signed TinyCloud session is required; run \`${loginHint}\``, 3);
29436
+ }
29437
+ if (failure.kind === "scope-denied") {
29438
+ const requiredAction = failure.requiredAction === void 0 ? "" : ` (${failure.requiredAction})`;
29439
+ return new CLIError("PERMISSION_DENIED", `the session lacks ${failure.capability} authority${requiredAction}; renew it with \`${loginHint}\` using the required capability`, 5);
29440
+ }
29441
+ if (failure.kind === "lifetime-exceeds-session") {
29442
+ const expiresAt = failure.sessionExpiresAt.toISOString();
29443
+ if (failure.reason === "session-too-close") {
29444
+ return new CLIError("AUTH_REQUIRED", `the signed session expires too soon (${expiresAt}); log in again with \`${loginHint}\``, 3);
29445
+ }
29446
+ if (failure.reason === "below-minimum") {
29447
+ return new CLIError("SESSION_LIFETIME_EXCEEDED", "share expiry must be at least 60 seconds from now; use a longer --expires value", 2);
29448
+ }
29449
+ return new CLIError("SESSION_LIFETIME_EXCEEDED", `requested share lifetime exceeds session expiry ${expiresAt}; use a shorter --expires value or renew the session with \`${loginHint}\``, 2);
29450
+ }
29451
+ if (failure.kind === "origin-mismatch") {
29452
+ return new CLIError("ORIGIN_MISMATCH", "share origin does not match the configured service", 2);
29453
+ }
29454
+ }
29412
29455
  if (error instanceof SharePublishError) {
29413
29456
  const exit = error.code === "authority-required" ? 3 : error.code === "max-bytes-exceeded" ? 7 : 2;
29414
29457
  const code4 = error.code === "authority-required" ? "AUTH_REQUIRED" : error.code === "max-bytes-exceeded" ? "MAX_BYTES_EXCEEDED" : "INVALID_ARGUMENT";
@@ -29491,7 +29534,7 @@ function assertAggregateInputLimit(inputs, maxBytes) {
29491
29534
  }
29492
29535
  function registerShareCommand(program2) {
29493
29536
  const share = program2.command("share").description("Publish and consume TinyCloud Share links");
29494
- share.command("publish <files...>").description("Publish one or more bounded files as a Share").option("--name <filename>", "Filename for stdin input").option("--to <target>", "Share target", "anyone").option("--notify", "Request idempotent email delivery for addressed targets").option("--expires <duration>", "Share lifetime", "7d").option("--max-bytes <bytes>", "Bound input bytes").option("--media-type <type>", "Media type for a single input").option("--action <actions...>", "Addressed permission: read, list, or edit").option("--prefix", "Publish multiple inputs beneath one addressed prefix").option("--binary", "Allow non-UTF-8 bearer content").option("--json", "Print versioned redacted JSON").option("--viewer-origin <origin>", "Canonical HTTPS viewer origin", SHARE_ORIGIN).action(async (files, options, command) => {
29537
+ share.command("publish <files...>").description("Publish one or more bounded files as a Share").option("--name <filename>", "Filename for stdin input").option("--to <target>", "Share target", "anyone").option("--notify", "Request idempotent email delivery for addressed targets").option("--expires <duration>", "Share lifetime").option("--max-bytes <bytes>", "Bound input bytes").option("--media-type <type>", "Media type for a single input").option("--action <actions...>", "Addressed permission: read, list, or edit").option("--prefix", "Publish multiple inputs beneath one addressed prefix").option("--binary", "Allow non-UTF-8 bearer content").option("--json", "Print versioned redacted JSON").option("--viewer-origin <origin>", "Canonical HTTPS viewer origin", SHARE_ORIGIN).action(async (files, options, command) => {
29495
29538
  try {
29496
29539
  const json = jsonOutput(options, command);
29497
29540
  const maxBytes = byteLimit(options.maxBytes);
@@ -29513,7 +29556,8 @@ function registerShareCommand(program2) {
29513
29556
  target,
29514
29557
  resourceKind: options.prefix || inputs.length > 1 ? "prefix" : "exact",
29515
29558
  actions,
29516
- expiresAt: expires(options.expires),
29559
+ expiresAt: expires(options.expires ?? "7d"),
29560
+ expiryWasExplicit: options.expires !== void 0,
29517
29561
  origin: options.viewerOrigin,
29518
29562
  ...maxBytes === void 0 ? {} : { maxBytes },
29519
29563
  notify: options.notify === true,
@@ -29533,7 +29577,9 @@ function registerShareCommand(program2) {
29533
29577
  const delivery = await notifyShare({ shareId: record.shareId, recipient: target.address, record, adapter: shareServices.delivery });
29534
29578
  if (delivery.state === "partial-failure") process.exitCode = 9;
29535
29579
  }
29536
- if (json) writeJson2(redactPublishedShare(result));
29580
+ if (result.metadata.expiryClamped === true) process.stderr.write(`Share expiry clamped to session expiry (${result.metadata.expiresAt}).
29581
+ `);
29582
+ if (json) writeJson2({ ...redactPublishedShare(result), expiryClamped: result.metadata.expiryClamped === true });
29537
29583
  else publishHuman(result);
29538
29584
  } catch (error) {
29539
29585
  handleError(shareCliError(error));
@@ -31015,6 +31061,12 @@ var MAX_INLINE_BYTES2 = 256 * 1024;
31015
31061
 
31016
31062
  // src/share/adapters.ts
31017
31063
  init_dist4();
31064
+ import { extractSiweExpiration, InvalidRestoredSessionError } from "@tinycloud/node-sdk";
31065
+ function requiredKvAction(meta) {
31066
+ if (meta === null || typeof meta !== "object" || !("requiredAction" in meta)) return void 0;
31067
+ const action = meta.requiredAction;
31068
+ return action === "tinycloud.kv/put" || action === "tinycloud.kv/get" ? action : void 0;
31069
+ }
31018
31070
  var DEFAULT_SHARE_ORIGIN = "https://share.tinycloud.xyz";
31019
31071
  function createEncryptedSessionHistory() {
31020
31072
  const records = /* @__PURE__ */ new Map();
@@ -31170,16 +31222,76 @@ function createShareAuthorityAdapters(input = {}) {
31170
31222
  };
31171
31223
  })();
31172
31224
  let nodePromise;
31225
+ let activeProfileName2;
31173
31226
  const authenticatedNode = async () => nodePromise ??= (async () => {
31174
31227
  const profile = await (input.profileName?.() ?? selectedProfileName());
31228
+ activeProfileName2 = profile;
31175
31229
  const context = await ProfileManager.resolveContext({ profile, ...input.nodeOrigin === void 0 ? {} : { host: input.nodeOrigin } });
31176
31230
  const { ensureAuthenticated: ensureAuthenticated2 } = await Promise.resolve().then(() => (init_sdk(), sdk_exports));
31177
- return ensureAuthenticated2(context);
31231
+ try {
31232
+ return await ensureAuthenticated2(context);
31233
+ } catch (error) {
31234
+ const profileConfig = await ProfileManager.getProfile(profile).catch(() => void 0);
31235
+ const code4 = typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
31236
+ if (error instanceof InvalidRestoredSessionError || code4 === "AUTH_EXPIRED") {
31237
+ throw new SharePublishAuthorityError({
31238
+ kind: "owner-space-unresolved",
31239
+ localKey: profileConfig?.authMethod === "local",
31240
+ profileName: profile
31241
+ });
31242
+ }
31243
+ throw error;
31244
+ }
31178
31245
  })();
31179
31246
  const targetAdapter = { async publish(targetInput) {
31180
31247
  if (input.publishTarget !== void 0) return input.publishTarget(targetInput);
31181
31248
  const [config, node] = await Promise.all([publicConfig(), authenticatedNode()]);
31182
- if (targetInput.origin !== config.shareOrigin || node.spaceId === void 0) throw new Error("share publication is not bound to the configured Share service");
31249
+ const session = node.restorableSession;
31250
+ const ownerSpaceId = session?.spaceId;
31251
+ const localKey = !node.isSessionOnly;
31252
+ let sessionExpiresAt;
31253
+ try {
31254
+ sessionExpiresAt = node.isSessionOnly && session?.siwe ? extractSiweExpiration(session.siwe) : void 0;
31255
+ } catch {
31256
+ throw new SharePublishAuthorityError({ kind: "owner-space-unresolved", localKey, profileName: activeProfileName2 });
31257
+ }
31258
+ if (ownerSpaceId === void 0 || node.isSessionOnly && (!session?.siwe || !session.signature || sessionExpiresAt === void 0)) {
31259
+ throw new SharePublishAuthorityError({ kind: "owner-space-unresolved", localKey, profileName: activeProfileName2 });
31260
+ }
31261
+ if (targetInput.origin !== config.shareOrigin) throw new SharePublishAuthorityError({ kind: "origin-mismatch" });
31262
+ if (node.isSessionOnly && sessionExpiresAt !== void 0) {
31263
+ const roundedSessionExpiry = new Date(Math.floor(sessionExpiresAt.getTime() / 1e3) * 1e3);
31264
+ if (roundedSessionExpiry.getTime() <= Date.now() + 6e4) {
31265
+ throw new SharePublishAuthorityError({
31266
+ kind: "lifetime-exceeds-session",
31267
+ sessionExpiresAt,
31268
+ reason: "session-too-close",
31269
+ localKey,
31270
+ profileName: activeProfileName2
31271
+ });
31272
+ }
31273
+ }
31274
+ const expiryClamped = node.isSessionOnly && sessionExpiresAt !== void 0 && targetInput.expiresAt > sessionExpiresAt;
31275
+ if (expiryClamped && targetInput.expiryWasExplicit) {
31276
+ throw new SharePublishAuthorityError({
31277
+ kind: "lifetime-exceeds-session",
31278
+ sessionExpiresAt,
31279
+ reason: "beyond-session",
31280
+ localKey,
31281
+ profileName: activeProfileName2
31282
+ });
31283
+ }
31284
+ const effectiveExpiry = expiryClamped ? sessionExpiresAt : targetInput.expiresAt;
31285
+ const expiresAt = new Date(Math.floor(effectiveExpiry.getTime() / 1e3) * 1e3);
31286
+ if (expiresAt.getTime() <= Date.now() + 6e4) {
31287
+ throw new SharePublishAuthorityError({
31288
+ kind: "lifetime-exceeds-session",
31289
+ sessionExpiresAt: sessionExpiresAt ?? expiresAt,
31290
+ reason: node.isSessionOnly && expiryClamped && !targetInput.expiryWasExplicit ? "session-too-close" : "below-minimum",
31291
+ localKey,
31292
+ profileName: activeProfileName2
31293
+ });
31294
+ }
31183
31295
  const activeNode = await node.activeNodeIdentity();
31184
31296
  const shareId = crypto.randomUUID().replaceAll("-", "");
31185
31297
  const files = targetInput.files === void 0 || targetInput.files.length === 0 ? [{ bytes: targetInput.source, filename: targetInput.filename, mediaType: targetInput.mediaType }] : targetInput.files;
@@ -31188,16 +31300,43 @@ function createShareAuthorityAdapters(input = {}) {
31188
31300
  if (resourceKind !== "exact" || files.length !== 1) throw new Error("native bearer publication requires one exact source file");
31189
31301
  const file2 = files[0];
31190
31302
  const resourcePath2 = `xyz.tinycloud.share/shares/${shareId}/${targetInput.filename}`;
31191
- const written = await node.kvForSpace(node.spaceId).put(resourcePath2, file2.bytes.slice(), {
31303
+ const written = await node.kvForSpace(ownerSpaceId).put(resourcePath2, file2.bytes.slice(), {
31192
31304
  contentType: targetInput.mediaType ?? file2.mediaType ?? "application/octet-stream"
31193
31305
  });
31194
- if (!written.ok) throw new Error("native bearer source upload was rejected");
31195
- const native = await createNativeShare(node.sharing, {
31196
- path: resourcePath2,
31197
- expiresAt: targetInput.expiresAt,
31198
- viewerOrigin: config.shareOrigin
31199
- });
31200
- if (native.spaceId !== node.spaceId) throw new Error("native bearer delegation authority does not match the authenticated owner space");
31306
+ if (!written.ok) {
31307
+ const code4 = typeof written.error === "object" && written.error !== null && "code" in written.error ? written.error.code : void 0;
31308
+ if (code4 === "AUTH_UNAUTHORIZED" || code4 === "PERMISSION_DENIED") {
31309
+ const requiredAction = requiredKvAction(written.error.meta);
31310
+ throw new SharePublishAuthorityError({
31311
+ kind: "scope-denied",
31312
+ capability: "KV upload",
31313
+ ...requiredAction === void 0 ? {} : { requiredAction },
31314
+ localKey,
31315
+ profileName: activeProfileName2
31316
+ });
31317
+ }
31318
+ throw new Error("native bearer source upload failed");
31319
+ }
31320
+ let native;
31321
+ try {
31322
+ native = await createNativeShare(node.sharing, {
31323
+ path: resourcePath2,
31324
+ expiresAt,
31325
+ viewerOrigin: config.shareOrigin
31326
+ });
31327
+ } catch (error) {
31328
+ const code4 = typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
31329
+ if (code4 === "AUTH_UNAUTHORIZED" || code4 === "PERMISSION_DENIED") {
31330
+ throw new SharePublishAuthorityError({
31331
+ kind: "scope-denied",
31332
+ capability: "sharing delegation",
31333
+ localKey,
31334
+ profileName: activeProfileName2
31335
+ });
31336
+ }
31337
+ throw error;
31338
+ }
31339
+ if (native.spaceId !== ownerSpaceId) throw new Error("native bearer delegation authority does not match the authenticated owner space");
31201
31340
  const result = {
31202
31341
  protocol: "tinycloud-share",
31203
31342
  version: SHARE_PUBLISH_RESULT_VERSION,
@@ -31211,6 +31350,7 @@ function createShareAuthorityAdapters(input = {}) {
31211
31350
  target: { kind: "bearer", origin: activeNode.origin, nodeAudience: activeNode.nodeDid, spaceId: native.spaceId },
31212
31351
  resource: { kind: "exact", path: resourcePath2 },
31213
31352
  actions: ["read"],
31353
+ ...expiryClamped ? { expiryClamped: true } : {},
31214
31354
  expiresAt: native.expiresAt.toISOString(),
31215
31355
  display: { filename: targetInput.filename },
31216
31356
  recipientMatcher: { kind: "bearer" },
@@ -31227,15 +31367,28 @@ function createShareAuthorityAdapters(input = {}) {
31227
31367
  const byteLength = file.bytes.byteLength;
31228
31368
  if (!Number.isSafeInteger(byteLength) || byteLength > 100 * 1024 * 1024) throw new Error("addressed publication exceeds the combined byte limit");
31229
31369
  const mediaType = targetInput.mediaType ?? file.mediaType ?? "application/octet-stream";
31230
- const encryptionNetwork = node.getEncryptionNetworkIdForSpace(node.spaceId);
31370
+ const encryptionNetwork = node.getEncryptionNetworkIdForSpace(ownerSpaceId);
31231
31371
  const encrypted = await node.encryption.encryptToNetwork(encryptionNetwork, file.bytes, { metadata: { contentType: mediaType } });
31232
31372
  if (!encrypted.ok) throw new Error("addressed source encryption was rejected");
31233
31373
  const storedBytes = new TextEncoder().encode(canonicalize3(encrypted.data));
31234
- const stored = await node.kvForSpace(node.spaceId).put(resourcePath, storedBytes, { contentType: "application/vnd.tinycloud.encrypted-envelope+json" });
31235
- if (!stored.ok) throw new Error("addressed source upload was rejected");
31374
+ const stored = await node.kvForSpace(ownerSpaceId).put(resourcePath, storedBytes, { contentType: "application/vnd.tinycloud.encrypted-envelope+json" });
31375
+ if (!stored.ok) {
31376
+ const code4 = typeof stored.error === "object" && stored.error !== null && "code" in stored.error ? stored.error.code : void 0;
31377
+ if (code4 === "AUTH_UNAUTHORIZED" || code4 === "PERMISSION_DENIED") {
31378
+ const requiredAction = requiredKvAction(stored.error.meta);
31379
+ throw new SharePublishAuthorityError({
31380
+ kind: "scope-denied",
31381
+ capability: "KV upload",
31382
+ ...requiredAction === void 0 ? {} : { requiredAction },
31383
+ localKey,
31384
+ profileName: activeProfileName2
31385
+ });
31386
+ }
31387
+ throw new Error("addressed source upload failed");
31388
+ }
31236
31389
  const contentSource = {
31237
31390
  shareId,
31238
- kvResource: `${node.spaceId}/kv/${resourcePath}`,
31391
+ kvResource: `${ownerSpaceId}/kv/${resourcePath}`,
31239
31392
  selector: resourceKind,
31240
31393
  encryptionNetwork: encrypted.data.networkId,
31241
31394
  encryptedSymmetricKeyDigestHex: encrypted.data.encryptedSymmetricKeyHash,
@@ -31245,13 +31398,13 @@ function createShareAuthorityAdapters(input = {}) {
31245
31398
  };
31246
31399
  const actions = targetInput.actions === void 0 || targetInput.actions.length === 0 ? ["read"] : targetInput.actions;
31247
31400
  const policyActions = [...new Set(actions.flatMap((action) => action === "read" ? ["tinycloud.kv/get", "tinycloud.kv/metadata"] : action === "list" ? ["tinycloud.kv/list"] : ["tinycloud.kv/put"]))];
31248
- return publishAddressedShare({
31401
+ const published = await publishAddressedShare({
31249
31402
  shareId,
31250
31403
  shareOrigin: config.shareOrigin,
31251
31404
  nodeOrigin: activeNode.origin,
31252
31405
  nodeAudience: activeNode.nodeDid,
31253
31406
  enforcerDid: activeNode.nodeDid,
31254
- spaceId: node.spaceId,
31407
+ spaceId: ownerSpaceId,
31255
31408
  target: targetInput.target,
31256
31409
  resource: { kind: resourceKind, path: resourcePath },
31257
31410
  actions,
@@ -31260,7 +31413,7 @@ function createShareAuthorityAdapters(input = {}) {
31260
31413
  filename: targetInput.filename,
31261
31414
  mediaType,
31262
31415
  byteLength,
31263
- expiresAt: targetInput.expiresAt,
31416
+ expiresAt,
31264
31417
  // App-neutral owner authority: the Node SDK owns every Policy/v3
31265
31418
  // transport hop, so the CLI supplies only owner signing material.
31266
31419
  authority: {
@@ -31270,6 +31423,8 @@ function createShareAuthorityAdapters(input = {}) {
31270
31423
  registerPolicy: (request) => node.registerPolicy(request)
31271
31424
  }
31272
31425
  });
31426
+ if (expiryClamped) Object.defineProperty(published.metadata, "expiryClamped", { value: true, enumerable: true });
31427
+ return published;
31273
31428
  } };
31274
31429
  const delivery = { deliver: input.deliver ?? (async (request) => {
31275
31430
  const record = request.record;