@knkcs/contenthub-ui 0.4.0 → 0.5.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.
@@ -34,8 +34,9 @@ var e = 2e3, t = 200, n = 200, r = 500, i = 200, a = class extends Error {
34
34
  }, d = class extends Error {
35
35
  reason;
36
36
  contentId;
37
- constructor(e, t, n = "") {
38
- super(t), this.name = "DirectEditRefusedError", this.reason = e, this.contentId = n;
37
+ editorId;
38
+ constructor(e, t, n = "", r = "") {
39
+ super(t), this.name = "DirectEditRefusedError", this.reason = e, this.contentId = n, this.editorId = r;
39
40
  }
40
41
  }, f = class extends Error {
41
42
  state;
@@ -135,4 +136,4 @@ function F(e, t) {
135
136
  //#endregion
136
137
  export { r as A, b as C, h as D, v as E, u as F, g as M, c as N, T as O, s as P, C as S, E as T, o as _, A as a, S as b, i as c, n as d, e as f, _ as g, f as h, N as i, l as j, y as k, D as l, t as m, F as n, M as o, w as p, k as r, x as s, P as t, p as u, a as v, O as w, d as x, m as y };
137
138
 
138
- //# sourceMappingURL=heads-DqHmAW0n.js.map
139
+ //# sourceMappingURL=heads-BrvZ_GYT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heads-BrvZ_GYT.js","names":["CHECK_NOTE_MAX","CONTENT_SEARCH_MAX","CHANGE_SET_NAME_MAX","SOURCE_REFERENCE_MAX","BLOCK_REASON_NAME_MAX","ContentBlockedError","contentId","blockReasonId","ContentArchivedError","StatusTransitionRefusedError","input","StaleStatusError","currentStatus","ServiceUnavailableError","message","WithheldWorkError","DirectEditRefusedError","reason","editorId","ChangeSetClosedError","state","BlockReasonNameTakenError","takenName","ContentRefusedError","reasons","PublishRefusedError","blockers","StaleHeadError","CheckResultDeniedError","NameTakenError","RestrictionUnavailableError","GuardianModeRequiredError","feature","AccessDeniedError","CoversReleaseError","coveredReleaseIds","changeSetRequired","EditingModeRefusedError","blueprintAccessor","editingMode","COLLECTION_NAME_MAX","REQUIRED_CHECK_NAME_MAX","MAX_BLUEPRINT_IDS","BLUEPRINT_ACCESSOR_MAX","InvalidInputError","DAY","localToday","pad","n","now","dated","entries","releaseInForceOn","day","last","inForceOnOrAfter","headsOf","out","chain","cs","b","sameHeads","keys","a","k"],"sources":["../src/client/contenthub-client.ts","../src/common/in-force.ts","../src/common/heads.ts"],"sourcesContent":["import type {\n\tBlockReason,\n\tBlueprintEditingOverride,\n\tBlueprintInfo,\n\tBlueprintPage,\n\tBlueprintReleaseInfo,\n\tCarryForwardAnswer,\n\tChainPreview,\n\tChangeSet,\n\tChangeSetKind,\n\tChangeSetList,\n\tChangeSetSource,\n\tChangeSetState,\n\tCheckOutcome,\n\tCheckResult,\n\tCheckStanding,\n\tCollection,\n\tCollectionGrant,\n\tCompareBase,\n\tContent,\n\tContentAttributes,\n\tContentDetail,\n\tContentList,\n\tContentTimeline,\n\tContentView,\n\tCopyFrom,\n\tDirectEditPublishPreview,\n\tEdge,\n\tEditingMode,\n\tEditingSettings,\n\tEffectiveEditingSettings,\n\tFieldResolution,\n\tOutdatedPin,\n\tPendingMerge,\n\tPlannedEnd,\n\tPlannedRelease,\n\tPreviewedContent,\n\tPublishBlocker,\n\tPublishOutcome,\n\tPublishReview,\n\tRecorderGrant,\n\tReferrerList,\n\tRequiredCheck,\n\tRequiredCheckInput,\n\tRequiredCheckRecorders,\n\tRevisionComparison,\n\tRevisionList,\n\tUpdateFromMainOutcome,\n\tValidityPeriod,\n\tWorkspaceEditingSettings,\n} from \"../types\";\n\n/** The longest note on a Check Result, in characters (internal/changesets.MaxCheckNote). */\nexport const CHECK_NOTE_MAX = 2000;\n\n/** The longest Editor Search, in characters (ContentFilter.search). */\nexport const CONTENT_SEARCH_MAX = 200;\n\n/** The longest Change Set name, in characters (internal/changesets.MaxNameLength). */\nexport const CHANGE_SET_NAME_MAX = 200;\n\n/** The longest Source reference, in characters (internal/changesets.MaxReferenceLength). */\nexport const SOURCE_REFERENCE_MAX = 500;\n\n/**\n * The longest Block Reason name, in characters: the service's limit\n * (internal/workspaceconfig.MaxBlockReasonName), checked here too so a form\n * says so before it asks.\n */\nexport const BLOCK_REASON_NAME_MAX = 200;\n\n/** What `updateChangeSet` changes of a Change Set. */\nexport type ChangeSetUpdateField = \"name\" | \"source\";\n\n/**\n * Everything contenthub-ui's screens ask contenthub, and nothing else: the\n * port a host fills (ADR 0020). `createConnectContenthubClient` is the\n * production implementation; `@knkcs/contenthub-ui/testing` has a stub over\n * an in-memory workspace.\n *\n * Errors the screens act on arrive as the typed errors below, never as a\n * ConnectError the screen would have to pick details out of. Anything else\n * (no permission, the service down) arrives as it came.\n *\n * The workspace is never an argument: contenthub reads it from the caller's\n * token (contenthub ADR 0017), so a host cannot ask about another workspace by\n * mistake.\n */\nexport interface ContenthubClient {\n\t/** The workspace's Block Reasons, by name. */\n\tlistBlockReasons(): Promise<BlockReason[]>;\n\t/**\n\t * Adds a Block Reason. Rejects with `BlockReasonNameTakenError` when the\n\t * workspace already has one of that name, and `InvalidInputError` for a\n\t * name the service refuses (blank, too long, more than one line).\n\t */\n\tcreateBlockReason(input: { name: string }): Promise<BlockReason>;\n\t/** Renames a Block Reason; its id stays. Rejects as `createBlockReason` does. */\n\trenameBlockReason(input: { id: string; name: string }): Promise<BlockReason>;\n\t/** Removes a Block Reason. */\n\tdeleteBlockReason(input: { id: string }): Promise<void>;\n\n\t/**\n\t * Change Sets, newest first, without their Chains, dependencies or\n\t * Planned Releases. Each filter given must hold.\n\t */\n\tlistChangeSets(input?: {\n\t\tstate?: ChangeSetState;\n\t\t/** Only those this odon user is assigned to. */\n\t\tassigneeId?: string;\n\t\t/** Only those with a Chain on this Content. */\n\t\tcontentId?: string;\n\t\t/** Only those of this kind: the index asks for \"change_set\", hiding Direct Edits. */\n\t\tkind?: ChangeSetKind;\n\t\t/** Only the Direct Edits of this odon user. */\n\t\teditorId?: string;\n\t\t/** At most 200; 50 when absent. */\n\t\tpageSize?: number;\n\t\tpageToken?: string;\n\t}): Promise<ChangeSetList>;\n\t/**\n\t * Opens a Change Set. Rejects with `InvalidInputError` for a name or\n\t * Source the service refuses.\n\t */\n\tcreateChangeSet(input: { name: string; source?: ChangeSetSource }): Promise<ChangeSet>;\n\t/** A Change Set with its Chains, their Branches' heads and its dependencies. */\n\tgetChangeSet(id: string): Promise<ChangeSet>;\n\t/**\n\t * Renames an open Change Set and corrects its Source, as `updateMask`\n\t * says: a field it doesn't name is left as it is, and a masked `source`\n\t * that is absent clears the Source. Rejects with `InvalidInputError` for\n\t * a name or Source the service refuses, and `ChangeSetClosedError` once\n\t * it is published or discarded: the Source freezes at Publish.\n\t */\n\tupdateChangeSet(input: {\n\t\tid: string;\n\t\tupdateMask: ChangeSetUpdateField[];\n\t\tname?: string;\n\t\tsource?: ChangeSetSource;\n\t\t/** Makes a retry safe (CONTEXT.md, Idempotency Key). */\n\t\tidempotencyKey?: string;\n\t}): Promise<ChangeSet>;\n\t/**\n\t * Moves the status label from `expectedStatus` (the status the caller\n\t * saw, \"\" for none) to `status`. Rejects with\n\t * `StatusTransitionRefusedError` when statushub refuses the move,\n\t * `StaleStatusError` when the status moved meanwhile,\n\t * `ChangeSetClosedError` when the Change Set is published or discarded,\n\t * and `ServiceUnavailableError` while statushub can't be reached.\n\t */\n\tsetChangeSetStatus(input: {\n\t\tid: string;\n\t\texpectedStatus: string;\n\t\tstatus: string;\n\t}): Promise<ChangeSet>;\n\t/** Replaces who is working on it: the whole new set of odon user ids. */\n\tsetChangeSetAssignees(input: { id: string; assigneeIds: string[] }): Promise<ChangeSet>;\n\t/**\n\t * Makes the Change Set depend on another open one. Rejects with\n\t * `InvalidInputError` for one that would close a cycle or is closed, and\n\t * `ChangeSetClosedError` when this Change Set itself is closed.\n\t */\n\taddDependency(input: { id: string; dependsOnId: string }): Promise<ChangeSet>;\n\t/** Undoes `addDependency` while the Change Set is open. */\n\tremoveDependency(input: { id: string; dependsOnId: string }): Promise<ChangeSet>;\n\t/**\n\t * Publish's dry run with `reviewedHeads` (Branch id to head Revision id):\n\t * every blocker a Publish with them would be refused with now, and the\n\t * Releases it would cut. Writes nothing. Rejects with `WithheldWorkError`\n\t * when Publish would, for a caller who can't see all of the Change Set.\n\t */\n\tvalidatePublish(input: {\n\t\tid: string;\n\t\treviewedHeads: Record<string, string>;\n\t}): Promise<PublishReview>;\n\t/**\n\t * Publishes the Change Set with exactly `reviewedHeads`: every Branch at\n\t * the head the caller reviewed. Rejects with `PublishRefusedError`\n\t * carrying every blocker when it is refused, `ChangeSetClosedError`\n\t * when it is closed (published with other heads, or discarded), and\n\t * `WithheldWorkError` when it holds work the caller can't see.\n\t */\n\tpublish(input: { id: string; reviewedHeads: Record<string, string> }): Promise<PublishOutcome>;\n\t/**\n\t * Change validity (ADR 0022): corrects only a published Release's\n\t * Validity Period, in one request and without a Direct Edit. It opens a\n\t * Change Set, plans the date-only Release re-pinning the Release's\n\t * Revision and superseding it, and publishes it with the proposals for\n\t * the neighbouring Releases; the outcome is that Publish's. Refused as a\n\t * whole, it leaves no Change Set behind: rejects with\n\t * `EditingModeRefusedError` where the Content's Blueprint is \"Change Sets\n\t * only\", `CoversReleaseError` (`changeSetRequired`) when the period would\n\t * cover another Release entirely, `PublishRefusedError` with every\n\t * blocker (an outstanding Required Check among them),\n\t * `AccessDeniedError` for a caller who may not publish,\n\t * `ContentBlockedError` and `InvalidInputError` for what else the service\n\t * refuses.\n\t */\n\tchangeValidity(input: {\n\t\treleaseId: string;\n\t\tvalidityPeriod: ValidityPeriod;\n\t\t/** The Change Set's name; the service names it after the Content when absent. */\n\t\tchangeSetName?: string;\n\t\t/** Makes a retry safe (CONTEXT.md, Idempotency Key). */\n\t\tidempotencyKey?: string;\n\t}): Promise<PublishOutcome>;\n\t/**\n\t * Merges what Main (and each dependency) gained since into every Chain\n\t * of the Change Set. A Branch whose merge has Conflicts, or fails its\n\t * Blueprint Release, is left with a Pending Merge, which the Branch\n\t * editor resolves. Rejects with `ChangeSetClosedError` when it is closed,\n\t * and, without `chainId`, `WithheldWorkError` when it holds work the\n\t * caller can't see.\n\t */\n\tupdateFromMain(input: {\n\t\tid: string;\n\t\t/** One of its Chains to update alone; every Chain when absent. */\n\t\tchainId?: string;\n\t\t/** Makes a retry safe (CONTEXT.md, Idempotency Key). */\n\t\tidempotencyKey?: string;\n\t}): Promise<UpdateFromMainOutcome>;\n\t/**\n\t * Closes the Change Set for good without publishing it. Rejects with\n\t * `ChangeSetClosedError` when it is closed already, and\n\t * `WithheldWorkError` when it holds work the caller can't see.\n\t */\n\tdiscardChangeSet(id: string): Promise<ChangeSet>;\n\t/**\n\t * Where every Required Check that applies to the Change Set stands: the\n\t * heads a result recorded now names, its results newest first, and\n\t * whether the latest is stale or clears it.\n\t */\n\tlistCheckResults(changeSetId: string): Promise<CheckStanding[]>;\n\t/**\n\t * Records a Check Result against `heads`, the heads the recorder saw.\n\t * Rejects with `StaleHeadError` when one of them moved,\n\t * `CheckResultDeniedError` when the caller may not record it (an\n\t * independent check's passed or waived from an author of what it\n\t * covers), `InvalidInputError` for what\n\t * the service refuses (a waiver without a note), and\n\t * `ChangeSetClosedError` when the Change Set is closed.\n\t */\n\trecordCheckResult(input: {\n\t\tchangeSetId: string;\n\t\trequiredCheckId: string;\n\t\theads: Record<string, string>;\n\t\toutcome: CheckOutcome;\n\t\tnote: string;\n\t}): Promise<CheckResult>;\n\n\t/** The workspace's Required Checks, by name. */\n\tlistRequiredChecks(): Promise<RequiredCheck[]>;\n\t/**\n\t * Declares a Required Check; it gates every open Change Set at once.\n\t * Rejects with `NameTakenError` when another check has the name, and\n\t * `InvalidInputError` for anything else the service refuses.\n\t */\n\tcreateRequiredCheck(input: RequiredCheckInput): Promise<RequiredCheck>;\n\t/** Replaces every field of a Required Check; its id stays. Rejects as create does. */\n\tupdateRequiredCheck(input: RequiredCheckInput & { id: string }): Promise<RequiredCheck>;\n\t/**\n\t * Removes a Required Check: it stops gating. Its results are kept and\n\t * count for nothing; its recorders' grants go with it. Rejects with\n\t * `ServiceUnavailableError` while Guardian doesn't take that, and nothing\n\t * is removed.\n\t */\n\tdeleteRequiredCheck(input: { id: string }): Promise<void>;\n\t/**\n\t * Who records a Required Check: every member until an admin narrows it\n\t * (ADR 0017's 2026-10-03 amendment). Admin only: rejects with\n\t * `AccessDeniedError` otherwise, and with `GuardianModeRequiredError` in\n\t * `authz.mode: workspace`, where every member records every check and\n\t * nothing narrows it.\n\t */\n\tlistRequiredCheckRecorders(requiredCheckId: string): Promise<RequiredCheckRecorders>;\n\t/**\n\t * Makes a member, or every member, a recorder of a Required Check. The\n\t * caller may grant themselves: an admin records a check only when\n\t * granted (break-glass, announced like any grant). Rejects as\n\t * `listRequiredCheckRecorders` does, with `InvalidInputError` for a\n\t * grantee who is not a member, and `ServiceUnavailableError` while\n\t * Guardian doesn't take the grant.\n\t */\n\tgrantRequiredCheckRecorder(input: {\n\t\trequiredCheckId: string;\n\t\trecorder: RecorderGrant;\n\t}): Promise<void>;\n\t/**\n\t * Takes recorder from a person, or from every member, which narrows the\n\t * check to the people granted by name. Rejects as grant does.\n\t */\n\trevokeRequiredCheckRecorder(input: {\n\t\trequiredCheckId: string;\n\t\trecorder: RecorderGrant;\n\t}): Promise<void>;\n\n\t/**\n\t * How the workspace's Content may be changed (ADR 0022): its own Editing\n\t * Mode and Editing Block, and every per-Blueprint override with the\n\t * values it makes effective. A workspace that never set them reads as\n\t * \"Change Sets only\" without an Editing Block.\n\t */\n\tgetEditingSettings(): Promise<WorkspaceEditingSettings>;\n\t/**\n\t * The editing settings in effect for the Content of the Blueprint with\n\t * `blueprintAccessor`: its override over the workspace's, field by\n\t * field. A host's Blueprint page reads it.\n\t */\n\tgetEffectiveEditingSettings(blueprintAccessor: string): Promise<EffectiveEditingSettings>;\n\t/**\n\t * Replaces the workspace's own editing settings; admin only. It takes\n\t * effect at once. Rejects with `AccessDeniedError` for a non-admin and\n\t * `InvalidInputError` for what the service refuses.\n\t */\n\tsetEditingSettings(settings: EditingSettings): Promise<EditingSettings>;\n\t/**\n\t * Replaces one Blueprint's override; admin only. A null field follows the\n\t * workspace's, and at least one must be set. Rejects as\n\t * `setEditingSettings` does.\n\t */\n\tsetBlueprintEditingOverride(input: {\n\t\tblueprintAccessor: string;\n\t\teditingMode: EditingMode | null;\n\t\teditingBlock: boolean | null;\n\t\t/** null follows the workspace's; [] is the admins alone. */\n\t\tforceGroup: string[] | null;\n\t}): Promise<BlueprintEditingOverride>;\n\t/** Removes one Blueprint's override, so it follows the workspace's; admin only. */\n\tdeleteBlueprintEditingOverride(input: { blueprintAccessor: string }): Promise<void>;\n\n\t/** Every Collection of the workspace, by name, then id: a tree by `parentId`. */\n\tlistCollections(): Promise<Collection[]>;\n\t/**\n\t * Creates a Collection at the root or under `parentId`. Rejects with\n\t * `NameTakenError` for a sibling of the same name, `InvalidInputError` for\n\t * a name the service refuses, and `RestrictionUnavailableError` for a\n\t * restricted one in workspace mode.\n\t */\n\tcreateCollection(input: {\n\t\tname: string;\n\t\tparentId?: string;\n\t\trestricted?: boolean;\n\t}): Promise<Collection>;\n\t/** Renames a Collection, keeping its place and filing. Rejects as create does. */\n\trenameCollection(input: { id: string; name: string }): Promise<Collection>;\n\t/**\n\t * Moves a Collection, with everything under it, under `parentId` (\"\" for\n\t * the root). Rejects with `InvalidInputError` for a move under itself, and\n\t * `NameTakenError` for a sibling of the same name there, whose\n\t * `takenName` is \"\": the request carries no name.\n\t */\n\tmoveCollection(input: { id: string; parentId: string }): Promise<Collection>;\n\t/**\n\t * Removes a Collection and unfiles its Content, answering the Content it\n\t * unfiled. Rejects with `InvalidInputError` while Collections sit under it.\n\t */\n\tremoveCollection(input: { id: string }): Promise<string[]>;\n\t/**\n\t * Restricts a Collection (the caller becomes its owner), or opens it\n\t * again, which discloses its Content to every viewer and so needs\n\t * `acknowledgeDisclosure`; opening it clears its grants. Rejects with\n\t * `RestrictionUnavailableError` in workspace mode, and\n\t * `InvalidInputError` for an unacknowledged disclosure.\n\t */\n\tsetCollectionRestricted(input: {\n\t\tid: string;\n\t\trestricted: boolean;\n\t\tacknowledgeDisclosure?: boolean;\n\t}): Promise<Collection>;\n\t/**\n\t * Who is granted on a Restricted Collection, its owner included. Rejects\n\t * with `AccessDeniedError` unless the caller is its owner or an admin, and\n\t * `RestrictionUnavailableError` in workspace mode.\n\t */\n\tlistCollectionGrants(collectionId: string): Promise<CollectionGrant[]>;\n\t/**\n\t * Grants a member viewer or editor on a Restricted Collection, replacing\n\t * what they held. The caller may grant themselves (break-glass, announced\n\t * like any grant). Rejects as `listCollectionGrants` does, and with\n\t * `InvalidInputError` for a grantee who is not a member or an open\n\t * Collection.\n\t */\n\tgrantCollectionAccess(input: {\n\t\tcollectionId: string;\n\t\tuserId: string;\n\t\tlevel: \"viewer\" | \"editor\";\n\t}): Promise<void>;\n\t/** Takes a person's grant on a Restricted Collection. Rejects as grant does. */\n\trevokeCollectionAccess(input: { collectionId: string; userId: string }): Promise<void>;\n\t/**\n\t * Content in one view, ordered by id (then Branch, for a search), each\n\t * filter given holding. Rejects with `InvalidInputError` for a search in\n\t * the In Force view, which Editor Search never covers (ADR 0019), and for\n\t * a search the service refuses.\n\t */\n\tlistContents(input?: {\n\t\t/** Main when absent. */\n\t\tview?: ContentView;\n\t\t/**\n\t\t * Only Content filed directly in this Collection, not in Collections\n\t\t * under it.\n\t\t */\n\t\tcollectionId?: string;\n\t\t/** Editor Search: a name, number or phrase, letter for letter but ignoring case. */\n\t\tsearch?: string;\n\t\t/**\n\t\t * Only Content of one of these Blueprints, filtered by the service so\n\t\t * that every page but the last is full; empty or absent for every\n\t\t * Blueprint. At most 100.\n\t\t */\n\t\tblueprintIds?: string[];\n\t\t/** Only Content this user maintains: one of its assignees. */\n\t\tassigneeId?: string;\n\t\t/** At most 200; 50 when absent. */\n\t\tpageSize?: number;\n\t\tpageToken?: string;\n\t}): Promise<ContentList>;\n\t/**\n\t * A page of the workspace's content Blueprints Content can be made from:\n\t * not archived, with at least one Release, whose accessor or name holds\n\t * `query`. contenthub asks blueprinthub as the caller (#238). Rejects\n\t * with `ServiceUnavailableError` while blueprinthub gives no answer.\n\t */\n\tsearchBlueprints(input: {\n\t\tquery: string;\n\t\tpageToken?: string;\n\t\tsignal?: AbortSignal;\n\t}): Promise<BlueprintPage>;\n\t/**\n\t * Every Release of a Blueprint, newest first, each with contenthub's\n\t * answer: `refusals` is empty when Content can pin it. Empty for a\n\t * Blueprint the workspace doesn't have. Rejects with\n\t * `ServiceUnavailableError` while blueprinthub gives no answer.\n\t */\n\tlistBlueprintReleases(blueprintId: string): Promise<BlueprintReleaseInfo[]>;\n\t/**\n\t * Names the Blueprints of these ids, archived ones and ones without a\n\t * Release included, so existing Content stays readable after its\n\t * Blueprint is retired (#241). An unknown id, or one of another\n\t * workspace, is left out. At most `MAX_BLUEPRINT_IDS`. Rejects with\n\t * `ServiceUnavailableError` while blueprinthub gives no answer.\n\t */\n\tgetBlueprints(ids: string[]): Promise<BlueprintInfo[]>;\n\t/**\n\t * Creates a Content in an open Change Set, pinning a Blueprint Release,\n\t * with empty data: its one Chain has one Branch, opened in the Branch\n\t * editor. Rejects with `ChangeSetClosedError` for a closed Change Set,\n\t * `ContentRefusedError` for a Release contenthub can't load or empty data\n\t * the Release refuses (a required field), and `InvalidInputError` for\n\t * anything else the service refuses.\n\t */\n\tcreateContent(input: {\n\t\tchangeSetId: string;\n\t\t/** May be \"\" with `copyFrom`, to pin what the source pins. */\n\t\tblueprintReleaseId: string;\n\t\t/** Duplicate: the new Content's first Revision takes this source's data. */\n\t\tcopyFrom?: CopyFrom;\n\t}): Promise<Content>;\n\t/**\n\t * Saves a new Revision on a Branch of an open Change Set, on top of\n\t * `expectedHeadId`, the head the editor loaded (ADR 0003). With\n\t * `fieldMask` the save is partial: each top-level field it names takes\n\t * its value in `data`, or is removed when `data` doesn't hold it, and\n\t * every other field is carried over from the head. Answers the Content\n\t * as read in the Change Set afterwards.\n\t *\n\t * Rejects with `StaleHeadError` when the head moved (nothing is written,\n\t * and nothing is merged: ADR 0020), `ContentRefusedError` with one reason\n\t * per Violation the data breaks, `ContentBlockedError` or\n\t * `ContentArchivedError` for Content closed to the caller,\n\t * `ChangeSetClosedError` for a closed Change Set, and `InvalidInputError`\n\t * for anything else the service refuses.\n\t */\n\tsaveRevision(input: {\n\t\t/** The Branch's Change Set, which a closed refusal names. */\n\t\tchangeSetId: string;\n\t\tbranchId: string;\n\t\texpectedHeadId: string;\n\t\tdata: Record<string, unknown>;\n\t\t/** Top-level field accessors; absent saves `data` as the whole Revision. */\n\t\tfieldMask?: string[];\n\t\t/**\n\t\t * Makes a retry safe (CONTEXT.md, Idempotency Key): sent again with\n\t\t * the same request, it answers the first save and writes nothing.\n\t\t */\n\t\tidempotencyKey?: string;\n\t}): Promise<Content>;\n\n\t/**\n\t * One Content with its Blueprint Release and attributes: on Main, or\n\t * inside `changeSetId`, at the head of its Chain's last Branch, or of\n\t * `branchId`, a Branch of that Chain, when given (#252). Content born in\n\t * an open Change Set exists only there, so read anywhere else it is a\n\t * NotFound ConnectError, as is a Branch outside the Chain.\n\t */\n\tgetContent(input: {\n\t\tid: string;\n\t\tchangeSetId?: string;\n\t\tbranchId?: string;\n\t}): Promise<ContentDetail>;\n\t/** Its Timeline, with the Planned Releases of open Change Sets beside it. */\n\tgetTimeline(contentId: string): Promise<ContentTimeline>;\n\t/**\n\t * Revisions, newest first: every one of a published Content, or those of\n\t * one Branch (the only way to list those of Content born in an open\n\t * Change Set).\n\t */\n\tlistRevisions(input: {\n\t\ttarget: { contentId: string } | { branchId: string };\n\t\tpageSize?: number;\n\t\tpageToken?: string;\n\t}): Promise<RevisionList>;\n\t/** How `revisionId` differs from a base, field by field. */\n\tcompare(input: { revisionId: string; base: CompareBase }): Promise<RevisionComparison>;\n\t/** The outgoing edges of one Revision, its own only. */\n\tlistReferences(input: { contentId: string; revisionId: string }): Promise<Edge[]>;\n\t/**\n\t * One page of the edges pointing at a Content from Releases In Force\n\t * today and, asked, open Branch heads: Releases first, then Change Sets.\n\t */\n\tlistReferrers(input: {\n\t\tcontentId: string;\n\t\tincludeOpenChangeSets?: boolean;\n\t\t/** 100 when absent; above 1000 reads as 1000. */\n\t\tpageSize?: number;\n\t\tpageToken?: string;\n\t}): Promise<ReferrerList>;\n\t/** The pinned edges of a Branch head or a Revision that are outdated today. */\n\tlistOutdatedPins(input: { branchId: string } | { revisionId: string }): Promise<OutdatedPin[]>;\n\n\t/** Files a Content in exactly these Collections: no save, no Revision (ADR 0015). Answers its Collections. */\n\tsetContentCollections(input: { contentId: string; collectionIds: string[] }): Promise<string[]>;\n\t/** Replaces who maintains a Content: no save, no Revision. */\n\tsetContentAssignees(input: {\n\t\tcontentId: string;\n\t\tassigneeIds: string[];\n\t}): Promise<ContentAttributes>;\n\t/** Blocks a Content, replacing any Block it has. */\n\tblockContent(input: {\n\t\tcontentId: string;\n\t\tblockReasonId: string;\n\t\tallowedUserIds: string[];\n\t}): Promise<ContentAttributes>;\n\tunblockContent(contentId: string): Promise<ContentAttributes>;\n\t/**\n\t * Archives a published Content with no Release In Force today or later.\n\t * Rejects with `InvalidInputError` for any other, with the service's reason.\n\t */\n\tarchiveContent(contentId: string): Promise<ContentAttributes>;\n\tunarchiveContent(contentId: string): Promise<ContentAttributes>;\n\n\t/**\n\t * Starts a Chain on a published Content, in `changeSetId` or in a new\n\t * Change Set named `changeSetName` (after the Content when empty).\n\t * Answers the Change Set and the Content as read on the Branch to edit.\n\t * Rejects with `ContentBlockedError`, `ContentArchivedError`,\n\t * `ChangeSetClosedError`, and `InvalidInputError` for anything else the\n\t * service refuses (a missing or needless effective date).\n\t */\n\t/**\n\t * Opens a Direct Edit of a published Content for the caller (ADR 0022),\n\t * or answers the one the caller has open on it, with `reopened`. Answers\n\t * the Direct Edit and the Content as read on its Branch. Rejects with\n\t * `EditingModeRefusedError` where the Editing Mode is \"Change Sets only\",\n\t * `DirectEditRefusedError` where another editor's Direct Edit holds its\n\t * Editing Block (naming them), `ContentBlockedError`, `ContentArchivedError`,\n\t * and `InvalidInputError` for anything else the service refuses (a day\n\t * for undated Content among it).\n\t */\n\topenDirectEdit(input: {\n\t\tcontentId: string;\n\t\t/**\n\t\t * Dated Content only (#270): the day the change takes effect, as\n\t\t * YYYY-MM-DD; absent is today. Its Branch forks from the Release In\n\t\t * Force that day: on today it corrects that Release, on a later day\n\t\t * it is a new wording.\n\t\t */\n\t\teffectiveOn?: string;\n\t\t/** Makes a retry safe (CONTEXT.md, Idempotency Key). */\n\t\tidempotencyKey?: string;\n\t}): Promise<{ changeSet: ChangeSet; content: Content; reopened: boolean }>;\n\t/**\n\t * What a Direct Edit's Publish of Dated Content would plan (#270): its\n\t * Release's Validity Period with `validityLastDay` as Publish takes it,\n\t * the Release it corrects, the date-only Releases contenthub derives and\n\t * the later wordings Publish asks about. Writes nothing. Rejects with\n\t * `CoversReleaseError` (`changeSetRequired`) when the period would cover\n\t * a Release entirely.\n\t */\n\tpreviewDirectEditPublish(input: {\n\t\tid: string;\n\t\tvalidityLastDay?: string;\n\t}): Promise<DirectEditPublishPreview>;\n\t/**\n\t * Publishes a Direct Edit at `expectedHeadId`, the head of its Branch the\n\t * editor saw: one Release, In Force from its Publish, carrying\n\t * `releaseSpec`. Rejects as `publish` does, with `ContentRefusedError`\n\t * for Release Spec values the Blueprint Release refuses (a required one\n\t * missing among them), and with `DirectEditRefusedError` for a Change\n\t * Set that is not a Direct Edit. On Dated Content (#270) it takes the\n\t * Validity Period's last day and one carry-forward answer per later\n\t * wording, and rejects with `CoversReleaseError` (`changeSetRequired`)\n\t * when the period would cover a Release entirely.\n\t */\n\tpublishDirectEdit(input: {\n\t\tid: string;\n\t\texpectedHeadId: string;\n\t\t/** The Release's Release Spec values; absent is none. */\n\t\treleaseSpec?: Record<string, unknown>;\n\t\t/** Dated Content: the period's last day, \"\" for an open end; absent keeps the default. */\n\t\tvalidityLastDay?: string;\n\t\t/** Dated Content: whether the change carries into each later wording. */\n\t\tcarryForward?: CarryForwardAnswer[];\n\t\tidempotencyKey?: string;\n\t}): Promise<PublishOutcome>;\n\t/**\n\t * Saves on a Direct Edit's Branch on top of `expectedHeadId`, as\n\t * `saveRevision` does, and publishes the Revision saved, as\n\t * `publishDirectEdit` does, in one request (ADR 0022). Refused as a\n\t * whole, with nothing saved: rejects as either does (`StaleHeadError`,\n\t * `ContentRefusedError` for the data or the Release Spec values,\n\t * `PublishRefusedError` with every blocker).\n\t */\n\tsaveAndPublishDirectEdit(input: {\n\t\tid: string;\n\t\texpectedHeadId: string;\n\t\tdata: Record<string, unknown>;\n\t\t/** Top-level field accessors; absent saves `data` as the whole Revision. */\n\t\tfieldMask?: string[];\n\t\treleaseSpec?: Record<string, unknown>;\n\t\t/** As `publishDirectEdit`'s. */\n\t\tvalidityLastDay?: string;\n\t\tcarryForward?: CarryForwardAnswer[];\n\t\tidempotencyKey?: string;\n\t}): Promise<{ content: Content; outcome: PublishOutcome }>;\n\t/**\n\t * Discards a Direct Edit: its work is dropped, Main and the Releases stay.\n\t * Rejects as `discardChangeSet` does, and with `DirectEditRefusedError`\n\t * for a Change Set that is not a Direct Edit.\n\t */\n\tdiscardDirectEdit(input: { id: string; idempotencyKey?: string }): Promise<ChangeSet>;\n\t/**\n\t * Forces an Editing Block (ADR 0022): hands the Direct Edit `id` holding\n\t * it, with its work and its Editing Block, to the caller. Answers it with\n\t * the caller as its editor, the Content as read on its Branch, and who\n\t * edited it until now. Rejects with `AccessDeniedError` outside the\n\t * Force Group, `DirectEditRefusedError` where the caller has a Direct\n\t * Edit of the Content of their own or it holds no Editing Block any more,\n\t * and as `discardDirectEdit` does otherwise.\n\t */\n\tforceDirectEdit(input: {\n\t\tid: string;\n\t\tidempotencyKey?: string;\n\t}): Promise<{ changeSet: ChangeSet; content: Content; previousEditorId: string }>;\n\tstartEditing(input: {\n\t\tcontentId: string;\n\t\tchangeSetId?: string;\n\t\t/** YYYY-MM-DD: required for Dated Content, refused for undated. */\n\t\teffectiveOn?: string;\n\t\tchangeSetName?: string;\n\t}): Promise<{ changeSet: ChangeSet; content: Content }>;\n\t/** What a Chain on Dated Content, effective on a date, would fork from and meet. */\n\tpreviewChain(input: {\n\t\tchangeSetId: string;\n\t\tcontentId: string;\n\t\teffectiveOn: string;\n\t\t/** The period to meet the Content's Releases with; from `effectiveOn`, open, when absent. */\n\t\tvalidityPeriod?: ValidityPeriod;\n\t}): Promise<ChainPreview>;\n\t/**\n\t * Plans the Release Publish will cut of an editorial Branch's head, with\n\t * its Validity Period (required for Dated Content, refused for undated)\n\t * and Release Spec values. Rejects with `ContentRefusedError` for Release\n\t * Spec values the Blueprint Release refuses, `CoversReleaseError` when\n\t * the period would cover a Release entirely, and as any edit does.\n\t */\n\tplanRelease(input: {\n\t\tchangeSetId: string;\n\t\tbranchId: string;\n\t\tvalidityPeriod?: ValidityPeriod;\n\t\treleaseSpec?: Record<string, unknown>;\n\t\tsupersedesReleaseIds?: string[];\n\t\tidempotencyKey?: string;\n\t}): Promise<PlannedRelease>;\n\t/**\n\t * Changes a Planned Release: each part given replaces what it had, each\n\t * absent keeps it. The Change Set's proposals are derived again. Rejects\n\t * as `planRelease` does, and with `InvalidInputError` for a proposal.\n\t */\n\tupdatePlannedRelease(input: {\n\t\tchangeSetId: string;\n\t\tplannedReleaseId: string;\n\t\tvalidityPeriod?: ValidityPeriod;\n\t\treleaseSpec?: Record<string, unknown>;\n\t\tsupersedesReleaseIds?: string[];\n\t\tidempotencyKey?: string;\n\t}): Promise<PlannedRelease>;\n\t/**\n\t * Adds an editorial Branch to a Chain on Dated Content, for a later\n\t * date. Answers the Change Set and the new Branch's id. Rejects with\n\t * `InvalidInputError` for a date not after the Chain's last editorial\n\t * Branch's, or a Chain without dates.\n\t */\n\tappendBranch(input: {\n\t\tchangeSetId: string;\n\t\tchainId: string;\n\t\teffectiveOn: string;\n\t\tidempotencyKey?: string;\n\t}): Promise<{ changeSet: ChangeSet; branchId: string }>;\n\t/**\n\t * Removes a Chain from an open Change Set with its Planned Releases; its\n\t * Revisions stay in history. Rejects with `InvalidInputError` for the\n\t * Chain of Content born in the Change Set.\n\t */\n\tdropChain(input: {\n\t\tchangeSetId: string;\n\t\tchainId: string;\n\t\tidempotencyKey?: string;\n\t}): Promise<ChangeSet>;\n\t/** The Pending Merge on a Branch, with what its resolution decides; null for none. */\n\tgetPendingMerge(branchId: string): Promise<PendingMerge | null>;\n\t/**\n\t * Resolves the Pending Merge on a Branch field by field, as a merge\n\t * Revision that becomes its head. Rejects with `StaleHeadError` when the\n\t * head moved, `ContentRefusedError` when the result fails the Blueprint\n\t * Release, and `InvalidInputError` for a field undecided or a Branch\n\t * without a Pending Merge.\n\t */\n\tresolveMerge(input: {\n\t\tchangeSetId: string;\n\t\tbranchId: string;\n\t\texpectedHeadId: string;\n\t\tresolutions: FieldResolution[];\n\t\tidempotencyKey?: string;\n\t}): Promise<Content>;\n\t/** Drops the Pending Merge on a Branch, which stays behind its base. */\n\tabortMerge(input: {\n\t\tchangeSetId: string;\n\t\tbranchId: string;\n\t\tidempotencyKey?: string;\n\t}): Promise<void>;\n\t/**\n\t * Preview: the Content as In Force on `validOn` (today when absent) if\n\t * the open Change Set were published now (ReleaseService's\n\t * preview_change_set); null when nothing is In Force then.\n\t */\n\tpreviewInForce(input: {\n\t\tcontentId: string;\n\t\tchangeSetId: string;\n\t\tvalidOn?: string;\n\t}): Promise<PreviewedContent | null>;\n\t/** Plans a Repeal of Dated Content ending on `lastDay`. */\n\tplanRepeal(input: {\n\t\tchangeSetId: string;\n\t\tcontentId: string;\n\t\tlastDay: string;\n\t}): Promise<PlannedEnd>;\n\t/** Plans a Withdrawal of undated Content. */\n\tplanWithdrawal(input: { changeSetId: string; contentId: string }): Promise<PlannedEnd>;\n}\n\n/** The Content is blocked, and its Block doesn't allow the caller. */\nexport class ContentBlockedError extends Error {\n\treadonly contentId: string;\n\treadonly blockReasonId: string;\n\tconstructor(contentId: string, blockReasonId: string) {\n\t\tsuper(`Content ${contentId} is blocked`);\n\t\tthis.name = \"ContentBlockedError\";\n\t\tthis.contentId = contentId;\n\t\tthis.blockReasonId = blockReasonId;\n\t}\n}\n\n/** The Content is Archived: unarchive it before editing it. */\nexport class ContentArchivedError extends Error {\n\treadonly contentId: string;\n\tconstructor(contentId: string) {\n\t\tsuper(`Content ${contentId} is archived`);\n\t\tthis.name = \"ContentArchivedError\";\n\t\tthis.contentId = contentId;\n\t}\n}\n\n/** statushub refused a status move: its reason is fit to show. */\nexport class StatusTransitionRefusedError extends Error {\n\treadonly fromStatus: string;\n\treadonly toStatus: string;\n\t/** statushub's reason, as it gave it. */\n\treadonly reason: string;\n\t/** A guarded move, which this caller may not make. */\n\treadonly requiresPermission: boolean;\n\tconstructor(input: {\n\t\tfromStatus: string;\n\t\ttoStatus: string;\n\t\treason: string;\n\t\trequiresPermission: boolean;\n\t}) {\n\t\tsuper(input.reason || `the move from \"${input.fromStatus}\" to \"${input.toStatus}\" was refused`);\n\t\tthis.name = \"StatusTransitionRefusedError\";\n\t\tthis.fromStatus = input.fromStatus;\n\t\tthis.toStatus = input.toStatus;\n\t\tthis.reason = input.reason;\n\t\tthis.requiresPermission = input.requiresPermission;\n\t}\n}\n\n/** The status moved since the caller saw it: choose again from `currentStatus`. */\nexport class StaleStatusError extends Error {\n\t/** \"\" for an unknown status. */\n\treadonly currentStatus: string;\n\tconstructor(currentStatus: string) {\n\t\tsuper(`the status is now \"${currentStatus}\"`);\n\t\tthis.name = \"StaleStatusError\";\n\t\tthis.currentStatus = currentStatus;\n\t}\n}\n\n/** A service the call needs can't be reached now: retry it unchanged. */\nexport class ServiceUnavailableError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"ServiceUnavailableError\";\n\t}\n}\n\n/**\n * The Change Set holds work the caller can't see: Content filed in a\n * Restricted Collection they aren't granted on (ADR 0017). Publishing,\n * discarding or updating all of it is refused, naming no Content. Read from\n * the refusal's typed WithheldWork detail, not its text.\n */\nexport class WithheldWorkError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"WithheldWorkError\";\n\t}\n}\n\n/** Why contenthub refused a Direct Edit (`DirectEditRefusedError.reason`). */\nexport type DirectEditRefusal =\n\t/** A Direct Edit takes no second Chain, Branch, dependency or Source. */\n\t| \"one_branch\"\n\t/** The Change Set named is not a Direct Edit. */\n\t| \"not_a_direct_edit\"\n\t/** Another editor's Direct Edit holds the Content's Editing Block. */\n\t| \"editing_blocked\"\n\t/** The Direct Edit is someone else's: `editorId` names them. */\n\t| \"not_the_editor\"\n\t/** Force by a caller who has a Direct Edit of the Content of their own. */\n\t| \"own_direct_edit\"\n\t/** Force of a Direct Edit that holds no Editing Block any more. */\n\t| \"no_editing_block\"\n\t/** A reason this build doesn't know. */\n\t| \"unknown\";\n\n/**\n * A Direct Edit was refused, or a write a Direct Edit can't take. Read from\n * the refusal's typed DirectEditRefused detail, not its text.\n */\nexport class DirectEditRefusedError extends Error {\n\treadonly reason: DirectEditRefusal;\n\t/** The Content it is about; \"\" where the refusal names none. */\n\treadonly contentId: string;\n\t/**\n\t * For `editing_blocked`: the editor holding the Editing Block; for\n\t * `not_the_editor`: the Direct Edit's editor; \"\" otherwise.\n\t */\n\treadonly editorId: string;\n\tconstructor(reason: DirectEditRefusal, message: string, contentId = \"\", editorId = \"\") {\n\t\tsuper(message);\n\t\tthis.name = \"DirectEditRefusedError\";\n\t\tthis.reason = reason;\n\t\tthis.contentId = contentId;\n\t\tthis.editorId = editorId;\n\t}\n}\n\n/** The Change Set is published or discarded, and closed for good. */\nexport class ChangeSetClosedError extends Error {\n\treadonly state: ChangeSetState;\n\tconstructor(state: ChangeSetState) {\n\t\tsuper(`the Change Set is ${state}`);\n\t\tthis.name = \"ChangeSetClosedError\";\n\t\tthis.state = state;\n\t}\n}\n\n/** Another Block Reason of the workspace already has this name. */\nexport class BlockReasonNameTakenError extends Error {\n\t/** The name asked for, which another Block Reason has. */\n\treadonly takenName: string;\n\tconstructor(takenName: string) {\n\t\tsuper(`a Block Reason named \"${takenName}\" already exists`);\n\t\tthis.name = \"BlockReasonNameTakenError\";\n\t\tthis.takenName = takenName;\n\t}\n}\n\n/** One reason contenthub refused a write, as the rule's owner gave it. */\nexport interface RefusalReason {\n\t/** Whose rule: \"fieldkit\", \"knkeditor\", \"format\" or \"contenthub\". */\n\tsource: string;\n\t/** The rule's own code, such as fieldkit's \"required\". */\n\tcode: string;\n\t/** A /-separated location, such as \"/title\"; \"\" for the whole. */\n\tpath: string;\n}\n\n/**\n * contenthub refused a Revision's data, a new Content's first or a save's:\n * it breaks the Blueprint Release's rules, or the Release can't be loaded.\n * `reasons` names each, as its owner gave it.\n */\nexport class ContentRefusedError extends Error {\n\treadonly reasons: RefusalReason[];\n\tconstructor(message: string, reasons: RefusalReason[]) {\n\t\tsuper(message);\n\t\tthis.name = \"ContentRefusedError\";\n\t\tthis.reasons = reasons;\n\t}\n}\n\n/**\n * Publish was refused: nothing was written. `blockers` names every reason\n * at once, so the editor fixes them in one pass (ADR 0015).\n */\nexport class PublishRefusedError extends Error {\n\treadonly blockers: PublishBlocker[];\n\tconstructor(blockers: PublishBlocker[]) {\n\t\tsuper(`Publish was refused for ${blockers.length} reason(s)`);\n\t\tthis.name = \"PublishRefusedError\";\n\t\tthis.blockers = blockers;\n\t}\n}\n\n/** A head the call named has moved since: read again and retry on `currentHeadId`. */\nexport class StaleHeadError extends Error {\n\treadonly contentId: string;\n\t/** \"\" for a Branch-less Planned Release's Revision. */\n\treadonly branchId: string;\n\treadonly expectedHeadId: string;\n\treadonly currentHeadId: string;\n\tconstructor(input: {\n\t\tcontentId: string;\n\t\tbranchId: string;\n\t\texpectedHeadId: string;\n\t\tcurrentHeadId: string;\n\t}) {\n\t\tsuper(`the head moved from ${input.expectedHeadId} to ${input.currentHeadId}`);\n\t\tthis.name = \"StaleHeadError\";\n\t\tthis.contentId = input.contentId;\n\t\tthis.branchId = input.branchId;\n\t\tthis.expectedHeadId = input.expectedHeadId;\n\t\tthis.currentHeadId = input.currentHeadId;\n\t}\n}\n\n/**\n * The caller may not record this result; `message` is the service's reason.\n * Mostly an independent Required Check's passed or waived from someone who\n * authored what it covers (ADR 0014), who may record failed only; also a\n * caller without the right to record the check at all.\n */\nexport class CheckResultDeniedError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"CheckResultDeniedError\";\n\t}\n}\n\n/**\n * Another Required Check, or a sibling Collection, already has this name.\n * The service's refusal carries no detail; the client knows what it sent.\n */\nexport class NameTakenError extends Error {\n\t/** The name asked for, trimmed. */\n\treadonly takenName: string;\n\tconstructor(takenName: string) {\n\t\tsuper(`\"${takenName}\" is taken`);\n\t\tthis.name = \"NameTakenError\";\n\t\tthis.takenName = takenName;\n\t}\n}\n\n/**\n * Restricted Collections need `authz.mode: guardian`: in workspace mode\n * nothing could enforce a restriction, so contenthub refuses every\n * restriction and grant (ADR 0017). No RPC says which mode runs, so the\n * screens learn it from this refusal, read from its GuardianModeRequired\n * detail (feature \"collection_restriction\"), not its text.\n */\nexport class RestrictionUnavailableError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"RestrictionUnavailableError\";\n\t}\n}\n\n/**\n * What was asked needs `authz.mode: guardian`; this contenthub runs in\n * workspace mode (ADR 0017). Read from the refusal's typed\n * GuardianModeRequired detail, not its text. `feature` names what was\n * refused: \"required_check_recorders\" for narrowing who records a check,\n * where every member records every check. Collection restriction's feature\n * comes as `RestrictionUnavailableError` instead.\n */\nexport class GuardianModeRequiredError extends Error {\n\treadonly feature: string;\n\tconstructor(feature: string, message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"GuardianModeRequiredError\";\n\t\tthis.feature = feature;\n\t}\n}\n\n/** The caller may not do this; `message` is the service's reason. */\nexport class AccessDeniedError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"AccessDeniedError\";\n\t}\n}\n\n/**\n * A plan's Validity Period would cover these Releases of its Content\n * entirely: nothing was planned. Supersede them, or change the dates.\n * `changeSetRequired` is set when the request could not Supersede them, as\n * Change validity can't: that decision is made in a Change Set.\n */\nexport class CoversReleaseError extends Error {\n\treadonly contentId: string;\n\treadonly coveredReleaseIds: string[];\n\treadonly changeSetRequired: boolean;\n\tconstructor(contentId: string, coveredReleaseIds: string[], changeSetRequired = false) {\n\t\tsuper(`the Validity Period covers ${coveredReleaseIds.join(\", \")} entirely`);\n\t\tthis.name = \"CoversReleaseError\";\n\t\tthis.contentId = contentId;\n\t\tthis.coveredReleaseIds = coveredReleaseIds;\n\t\tthis.changeSetRequired = changeSetRequired;\n\t}\n}\n\n/**\n * The effective Editing Mode of the Content's Blueprint is \"Change Sets\n * only\" (ADR 0022): the change is made in a Change Set. Nothing was written.\n */\nexport class EditingModeRefusedError extends Error {\n\treadonly contentId: string;\n\treadonly blueprintAccessor: string;\n\t/** The effective Editing Mode that refused it. */\n\treadonly editingMode: EditingMode;\n\tconstructor(\n\t\tcontentId: string,\n\t\tblueprintAccessor: string,\n\t\teditingMode: EditingMode = \"change_sets\",\n\t) {\n\t\tsuper(`Content ${contentId} of Blueprint ${blueprintAccessor} is changed in Change Sets only`);\n\t\tthis.name = \"EditingModeRefusedError\";\n\t\tthis.contentId = contentId;\n\t\tthis.blueprintAccessor = blueprintAccessor;\n\t\tthis.editingMode = editingMode;\n\t}\n}\n\n/** The longest Collection name, in characters (internal/collections.MaxNameLength). */\nexport const COLLECTION_NAME_MAX = 200;\n\n/** The longest Required Check name, in characters (internal/workspaceconfig.MaxRequiredCheckName). */\nexport const REQUIRED_CHECK_NAME_MAX = 200;\n\n/** The most ids one `getBlueprints` takes (internal/blueprintcatalog.MaxBlueprintIDs). */\nexport const MAX_BLUEPRINT_IDS = 1000;\n\n/** The longest Blueprint accessor a check names (internal/workspaceconfig.MaxBlueprintAccessor). */\nexport const BLUEPRINT_ACCESSOR_MAX = 200;\n\n/** The service refused a value; `message` is its guidance, fit to show. */\nexport class InvalidInputError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"InvalidInputError\";\n\t}\n}\n","import type { TimelineEntry } from \"../types\";\n\n/** A day as the service spells it: YYYY-MM-DD. */\nexport const DAY = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/**\n * Today as YYYY-MM-DD in the browser's time zone. The workspace's own time\n * zone decides In Force on the service; the page only uses this to choose\n * what to offer, and the service has the last word.\n */\nexport function localToday(now: Date = new Date()): string {\n\tconst pad = (n: number) => String(n).padStart(2, \"0\");\n\treturn `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;\n}\n\n/** Whether the Timeline is a Dated Content's: its Releases carry Validity Periods. */\nfunction dated(entries: TimelineEntry[]): boolean {\n\treturn entries.some((e) => e.validityPeriod);\n}\n\n/**\n * The Release In Force on `day`, as the Timeline known now gives it: for\n * Dated Content the one whose Validity Period covers the day, for undated\n * Content the latest one, unless a Withdrawal ended its force. Undefined in\n * a gap, before the first Release, or for Content never published.\n */\nexport function releaseInForceOn(entries: TimelineEntry[], day: string): TimelineEntry | undefined {\n\tif (dated(entries)) {\n\t\treturn entries.find(\n\t\t\t(e) =>\n\t\t\t\te.validityPeriod &&\n\t\t\t\te.validityPeriod.firstDay <= day &&\n\t\t\t\t(e.validityPeriod.lastDay === \"\" || day <= e.validityPeriod.lastDay),\n\t\t);\n\t}\n\tconst last = entries.at(-1);\n\treturn last && last.kind !== \"withdrawal\" ? last : undefined;\n}\n\n/**\n * Whether any Release is In Force on `day` or later: what ArchiveContent\n * refuses (ADR 0015), since archiving a live law would hide it from the\n * editors who maintain it. A Repeal is In Force up to its last day.\n */\nexport function inForceOnOrAfter(entries: TimelineEntry[], day: string): boolean {\n\tif (dated(entries)) {\n\t\treturn entries.some(\n\t\t\t(e) =>\n\t\t\t\te.validityPeriod && (e.validityPeriod.lastDay === \"\" || e.validityPeriod.lastDay >= day),\n\t\t);\n\t}\n\treturn releaseInForceOn(entries, day) !== undefined;\n}\n","import type { ChangeSet } from \"../types\";\n\n/**\n * Every Branch of a Change Set at its head, Branch id to Revision id: the\n * heads a Publish reviews, and that `reviewed_heads` must name exactly.\n */\nexport function headsOf(cs: ChangeSet): Record<string, string> {\n\tconst out: Record<string, string> = {};\n\tfor (const chain of cs.chains) {\n\t\tfor (const b of chain.branches) out[b.id] = b.headRevisionId;\n\t}\n\treturn out;\n}\n\n/** The same heads, Branch for Branch. */\nexport function sameHeads(a: Record<string, string>, b: Record<string, string>): boolean {\n\tconst keys = Object.keys(a);\n\treturn keys.length === Object.keys(b).length && keys.every((k) => b[k] === a[k]);\n}\n"],"mappings":"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"}