@opetope/core 0.1.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 (131) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/LICENSE +21 -0
  3. package/README.md +250 -0
  4. package/README.ru.md +246 -0
  5. package/dist/abort-compat.d.ts +10 -0
  6. package/dist/abort-compat.js +2 -0
  7. package/dist/abort-compat.js.map +1 -0
  8. package/dist/adapter-method.d.ts +27 -0
  9. package/dist/attachment-lifecycle-cleanup.d.ts +3 -0
  10. package/dist/attachment-lifecycle-cleanup.js +2 -0
  11. package/dist/attachment-lifecycle-cleanup.js.map +1 -0
  12. package/dist/attachment-lifecycle-contract.d.ts +260 -0
  13. package/dist/attachment-lifecycle-contract.js +2 -0
  14. package/dist/attachment-lifecycle-contract.js.map +1 -0
  15. package/dist/attachment-lifecycle-helpers.d.ts +34 -0
  16. package/dist/attachment-lifecycle-helpers.js +2 -0
  17. package/dist/attachment-lifecycle-helpers.js.map +1 -0
  18. package/dist/attachment-lifecycle-machine.d.ts +3 -0
  19. package/dist/attachment-lifecycle-machine.js +2 -0
  20. package/dist/attachment-lifecycle-machine.js.map +1 -0
  21. package/dist/attachment-lifecycle-mailbox.d.ts +10 -0
  22. package/dist/attachment-lifecycle-mailbox.js +2 -0
  23. package/dist/attachment-lifecycle-mailbox.js.map +1 -0
  24. package/dist/attachment-lifecycle-open.d.ts +3 -0
  25. package/dist/attachment-lifecycle-open.js +2 -0
  26. package/dist/attachment-lifecycle-open.js.map +1 -0
  27. package/dist/attachment-lifecycle.d.ts +4 -0
  28. package/dist/attachment-lifecycle.js +2 -0
  29. package/dist/attachment-lifecycle.js.map +1 -0
  30. package/dist/attachment.d.ts +49 -0
  31. package/dist/attachment.js +2 -0
  32. package/dist/attachment.js.map +1 -0
  33. package/dist/call-authority.d.ts +26 -0
  34. package/dist/call-controller-support.d.ts +77 -0
  35. package/dist/call-controller-support.js +2 -0
  36. package/dist/call-controller-support.js.map +1 -0
  37. package/dist/call-controller.d.ts +49 -0
  38. package/dist/call-controller.js +2 -0
  39. package/dist/call-controller.js.map +1 -0
  40. package/dist/call-execution-inspection.d.ts +26 -0
  41. package/dist/call-execution-inspection.js +2 -0
  42. package/dist/call-execution-inspection.js.map +1 -0
  43. package/dist/call-executor-factory.d.ts +5 -0
  44. package/dist/call-executor-factory.js +2 -0
  45. package/dist/call-executor-factory.js.map +1 -0
  46. package/dist/call-executor-record.d.ts +66 -0
  47. package/dist/call-executor-record.js +2 -0
  48. package/dist/call-executor-record.js.map +1 -0
  49. package/dist/call-executor-support.d.ts +25 -0
  50. package/dist/call-executor-support.js +2 -0
  51. package/dist/call-executor-support.js.map +1 -0
  52. package/dist/call-executor-types.d.ts +56 -0
  53. package/dist/call-executor-waiters.d.ts +23 -0
  54. package/dist/call-executor-waiters.js +2 -0
  55. package/dist/call-executor-waiters.js.map +1 -0
  56. package/dist/call-executor.d.ts +6 -0
  57. package/dist/call-executor.js +2 -0
  58. package/dist/call-executor.js.map +1 -0
  59. package/dist/call-invocation.d.ts +61 -0
  60. package/dist/call-invocation.js +2 -0
  61. package/dist/call-invocation.js.map +1 -0
  62. package/dist/call-lane.d.ts +28 -0
  63. package/dist/call-lane.js +2 -0
  64. package/dist/call-lane.js.map +1 -0
  65. package/dist/call-target-result.d.ts +22 -0
  66. package/dist/call-target-result.js +2 -0
  67. package/dist/call-target-result.js.map +1 -0
  68. package/dist/call-target.d.ts +42 -0
  69. package/dist/call-target.js +2 -0
  70. package/dist/call-target.js.map +1 -0
  71. package/dist/call.d.ts +11 -0
  72. package/dist/call.js +2 -0
  73. package/dist/call.js.map +1 -0
  74. package/dist/contribution-hub.d.ts +100 -0
  75. package/dist/contribution-hub.js +2 -0
  76. package/dist/contribution-hub.js.map +1 -0
  77. package/dist/contribution-transaction.d.ts +20 -0
  78. package/dist/contribution-transaction.js +2 -0
  79. package/dist/contribution-transaction.js.map +1 -0
  80. package/dist/contribution.d.ts +64 -0
  81. package/dist/contribution.js +2 -0
  82. package/dist/contribution.js.map +1 -0
  83. package/dist/data-scheduler.d.ts +4 -0
  84. package/dist/data-scheduler.js +2 -0
  85. package/dist/data-scheduler.js.map +1 -0
  86. package/dist/errors.d.ts +56 -0
  87. package/dist/errors.js +2 -0
  88. package/dist/errors.js.map +1 -0
  89. package/dist/identity.d.ts +6 -0
  90. package/dist/identity.js +2 -0
  91. package/dist/identity.js.map +1 -0
  92. package/dist/index.d.ts +17 -0
  93. package/dist/index.js +2 -0
  94. package/dist/index.js.map +1 -0
  95. package/dist/internal.d.ts +46 -0
  96. package/dist/internal.js +2 -0
  97. package/dist/internal.js.map +1 -0
  98. package/dist/lifetime.d.ts +19 -0
  99. package/dist/lifetime.js +2 -0
  100. package/dist/lifetime.js.map +1 -0
  101. package/dist/model-context.d.ts +155 -0
  102. package/dist/model.d.ts +21 -0
  103. package/dist/model.js +2 -0
  104. package/dist/model.js.map +1 -0
  105. package/dist/platform-compat.d.ts +9 -0
  106. package/dist/platform-compat.js +2 -0
  107. package/dist/platform-compat.js.map +1 -0
  108. package/dist/port.d.ts +26 -0
  109. package/dist/port.js +2 -0
  110. package/dist/port.js.map +1 -0
  111. package/dist/reactive-collection.d.ts +27 -0
  112. package/dist/reactive-collection.js +2 -0
  113. package/dist/reactive-collection.js.map +1 -0
  114. package/dist/reactive-node.d.ts +62 -0
  115. package/dist/reactive-node.js +2 -0
  116. package/dist/reactive-node.js.map +1 -0
  117. package/dist/reactive-scheduler-control.d.ts +34 -0
  118. package/dist/reactive-scheduler-control.js +2 -0
  119. package/dist/reactive-scheduler-control.js.map +1 -0
  120. package/dist/reactive-scheduler.d.ts +18 -0
  121. package/dist/reactive-scheduler.js +2 -0
  122. package/dist/reactive-scheduler.js.map +1 -0
  123. package/dist/reactive.d.ts +41 -0
  124. package/dist/reactive.js +2 -0
  125. package/dist/reactive.js.map +1 -0
  126. package/dist/readable.d.ts +13 -0
  127. package/dist/readable.js +2 -0
  128. package/dist/readable.js.map +1 -0
  129. package/dist/resource.d.ts +82 -0
  130. package/dist/type-helpers.d.ts +14 -0
  131. package/package.json +66 -0
@@ -0,0 +1,100 @@
1
+ import type { DeclarationId } from './identity.js';
2
+ import type { Readable } from './readable.js';
3
+ interface ContributionEntry<Value> {
4
+ readonly id: DeclarationId;
5
+ readonly priority: number;
6
+ readonly value: Value;
7
+ }
8
+ /**
9
+ * A contribution declared with `when` is present only while its readable holds anything but `false` (D79). The target
10
+ * publishes the visible entries, so `Slot`, `fold`, `select` and emptiness checks never observe a withheld one.
11
+ */
12
+ type ContributionVisibility = Readable<boolean> | undefined;
13
+ interface InternalContributionEntry<Value> {
14
+ readonly entry: ContributionEntry<Value>;
15
+ readonly token: object;
16
+ readonly when: ContributionVisibility;
17
+ }
18
+ interface ContributionHubUpdate<Value> {
19
+ /**
20
+ * The entries this update adds, and the only ones whose `when` it may read: a peer keeps the visibility it was
21
+ * last published with, so one owner's unreadable predicate cannot hold another owner's entries (D225).
22
+ */
23
+ readonly added: readonly InternalContributionEntry<Value>[] | undefined;
24
+ /** What the hub held when this update was prepared: a hub that moved under foreign code no longer matches it. */
25
+ readonly base: readonly InternalContributionEntry<Value>[];
26
+ readonly entries: readonly InternalContributionEntry<Value>[];
27
+ }
28
+ type ContributionWatchRollback = () => void;
29
+ type ValueNormalizer<Value> = (value: unknown) => Value;
30
+ type EntriesValidator<Value> = (entries: readonly ContributionEntry<Value>[]) => void;
31
+ /** Publication and refresh both stop a source that invalidates every projection instead of spinning forever. */
32
+ declare const maxVisibilityRestarts = 100;
33
+ declare function validateUniqueIds<Value>(entries: readonly ContributionEntry<Value>[]): void;
34
+ declare class ContributionHub<Value> {
35
+ readonly readable: Readable<readonly ContributionEntry<Value>[]>;
36
+ /** How many times a watched `when` signalled: a projection taken before a signal is stale, whoever caused it. */
37
+ visibilityRevision: number;
38
+ private entries;
39
+ private readonly listeners;
40
+ private readonly normalize;
41
+ private refreshDirty;
42
+ private refreshing;
43
+ private snapshot;
44
+ private readonly validate;
45
+ private readonly watchers;
46
+ constructor(normalize: ValueNormalizer<Value>, validate: EntriesValidator<Value>);
47
+ /**
48
+ * The mutation half of a publication, and the only half that moves an entry. The subscribe of `watch`, the
49
+ * `getSnapshot` of `project` and the release of a watcher all run outside it, so nothing here calls foreign code
50
+ * and nothing here can throw and leave a hub half-committed (D196).
51
+ */
52
+ commit(update: ContributionHubUpdate<Value>, snapshot: readonly ContributionEntry<Value>[]): void;
53
+ createEntry(id: DeclarationId, priority: number, value: unknown, when: ContributionVisibility): InternalContributionEntry<Value>;
54
+ hasToken(token: object): boolean;
55
+ /** Which of these tokens this hub actually holds. A publication that never committed holds none (D196). */
56
+ knownTokens(tokens: ReadonlySet<object>): ReadonlySet<object>;
57
+ /** Whether this hub still holds exactly what the update was prepared from. */
58
+ matches(update: ContributionHubUpdate<Value>): boolean;
59
+ notify(): void;
60
+ prepareAdd(additions: readonly InternalContributionEntry<Value>[]): ContributionHubUpdate<Value>;
61
+ prepareRemove(tokens: ReadonlySet<object>): ContributionHubUpdate<Value>;
62
+ prepareRemoveOne(token: object): ContributionHubUpdate<Value>;
63
+ /**
64
+ * The visible half of a commit, computed while every hub still holds what it held: `when.getSnapshot()` is foreign
65
+ * code that may throw, or close and withdraw this very publication, and a commit that already moved an entry
66
+ * would have nothing to walk back to (D196).
67
+ */
68
+ project(update: ContributionHubUpdate<Value>): readonly ContributionEntry<Value>[];
69
+ /**
70
+ * Releasing a watcher calls the disposer of a foreign `subscribe`, which may withdraw anything — including the
71
+ * entries of another target. It therefore runs after every hub of the publication is committed, never between two
72
+ * of them (D196).
73
+ */
74
+ pruneWatchers(): void;
75
+ /**
76
+ * The preflight half: subscribing a `when` is the only step of a publication that runs foreign code, so it runs
77
+ * before any entry moves. A subscribe that throws rolls back the watchers this call added and leaves the hub
78
+ * exactly as it was (D196).
79
+ */
80
+ watch(entries: readonly InternalContributionEntry<Value>[]): ContributionWatchRollback;
81
+ private projectVisibleEntries;
82
+ private publishVisibleEntries;
83
+ /**
84
+ * A `when` flip republishes the visible entries in one transaction, so consumers recompute once (D79). A flip
85
+ * performed inside an `entries` listener cannot recurse, but it must not be lost either: the guarded run records
86
+ * it and the loop picks it up. The same bound covers getters and listeners that never let the refresh settle.
87
+ */
88
+ private refreshVisibility;
89
+ private releaseWatcher;
90
+ /**
91
+ * D196: the publication spans several targets, so a hub that subscribed hands back the way to undo exactly what it
92
+ * added; a later hub that refuses then leaves no watcher behind on the hubs before it.
93
+ */
94
+ private rollbackOf;
95
+ private subscribe;
96
+ /** Uniqueness holds over every published contribution, so a `when` flip can never fail validation. */
97
+ private validateAll;
98
+ }
99
+ export { ContributionHub, maxVisibilityRestarts, validateUniqueIds };
100
+ export type { ContributionEntry, ContributionHubUpdate, ContributionVisibility, ContributionWatchRollback, EntriesValidator, InternalContributionEntry, ValueNormalizer, };
@@ -0,0 +1,2 @@
1
+ import{reportDetachedError as a}from"./attachment.js";import{endDataTransaction as b,beginDataTransaction as y}from"./data-scheduler.js";const o=Object.freeze([]),c=100,p=()=>{};function w(i,e){const t=i.entry.priority-e.entry.priority;return t!==0?t:i.entry.id<e.entry.id?-1:i.entry.id>e.entry.id?1:0}function f(i){if(i.length<2)return;const e=new Set;for(const t of i){if(e.has(t.id))throw new TypeError(`Contribution ${t.id} is already published to this target.`);e.add(t.id)}}function l(i){var e;return((e=i.when)==null?void 0:e.getSnapshot())!==!1}function u(i){return i!==void 0&&i.length>1?new Set(i):void 0}function d(i,e,t){return e===void 0?(i==null?void 0:i[0])===t:e.has(t)}function m(i,e){if(i.entries.length===0)return o;const t=i.added,r=u(t),n=u(e),s=[];for(const h of i.entries)(d(t,r,h)?l(h):d(e,n,h.entry))&&s.push(h.entry);return Object.freeze(s)}function v(i,e){if(i.length!==e.length)return!1;for(const[t,r]of i.entries())if(r!==e[t])return!1;return!0}class g{readable;visibilityRevision=0;entries=o;listeners=new Set;normalize;refreshDirty=!1;refreshing=!1;snapshot=o;validate;watchers=new Map;constructor(e,t){this.normalize=e,this.validate=t,this.readable=Object.freeze({getSnapshot:()=>this.snapshot,subscribe:r=>this.subscribe(r)})}commit(e,t){this.entries=e.entries,this.snapshot=t}createEntry(e,t,r,n){return Object.freeze({entry:Object.freeze({id:e,priority:t,value:this.normalize(r)}),token:Object.freeze({}),when:n})}hasToken(e){return this.entries.some(t=>t.token===e)}knownTokens(e){const t=new Set;for(const r of this.entries)e.has(r.token)&&t.add(r.token);return t}matches(e){return this.entries===e.base}notify(){if(this.listeners.size!==0)for(const{listener:e}of[...this.listeners])try{e()}catch(t){a(t)}}prepareAdd(e){const t=this.entries.concat(e);t.sort(w);const r=Object.freeze(t);return this.validateAll(r),Object.freeze({added:e,base:this.entries,entries:r})}prepareRemove(e){let t=0;const r=this.entries.filter(s=>e.has(s.token)?(t+=1,!1):!0);if(t!==e.size)throw new TypeError("Contribution publication no longer matches its exact target state.");const n=Object.freeze(r);return this.validateAll(n),Object.freeze({added:void 0,base:this.entries,entries:n})}prepareRemoveOne(e){var n;if(this.entries.length===1&&((n=this.entries[0])==null?void 0:n.token)===e)return this.validate(o),Object.freeze({added:void 0,base:this.entries,entries:o});const t=this.entries.filter(s=>s.token!==e);if(t.length!==this.entries.length-1)throw new TypeError("Contribution publication no longer matches its exact target state.");const r=Object.freeze(t);return this.validateAll(r),Object.freeze({added:void 0,base:this.entries,entries:r})}project(e){return m(e,this.snapshot)}pruneWatchers(){if(this.watchers.size===0)return;const e=new Set;for(const t of this.entries)t.when!==void 0&&e.add(t.token);for(const t of[...this.watchers.keys()])e.has(t)||this.releaseWatcher(t)}watch(e){let t;try{for(const r of e){if(r.when===void 0||this.watchers.has(r.token))continue;const n=r.when.subscribe(()=>{this.visibilityRevision+=1,this.refreshVisibility()});this.watchers.set(r.token,n),(t??(t=[])).push(r.token)}}catch(r){throw t==null||t.forEach(n=>this.releaseWatcher(n)),r}return this.rollbackOf(t)}projectVisibleEntries(){const e=this.entries,t=this.visibilityRevision;if(e.length===0)return o;const r=[];for(const n of e){const s=l(n);if(this.entries!==e||this.visibilityRevision!==t)return;s&&r.push(n.entry)}return Object.freeze(r)}publishVisibleEntries(){const e=this.projectVisibleEntries();if(e===void 0){this.refreshDirty=!0;return}if(!v(e,this.snapshot)){y();try{this.snapshot=e,this.notify()}finally{try{b()}catch(t){a(t)}}}}refreshVisibility(){if(this.refreshing){this.refreshDirty=!0;return}this.refreshing=!0;try{for(let e=0;;e+=1){if(this.refreshDirty=!1,this.publishVisibleEntries(),!this.refreshDirty)return;if(e>=c)throw new TypeError("Contribution visibility keeps changing while its target refreshes.")}}finally{this.refreshing=!1,this.refreshDirty=!1}}releaseWatcher(e){const t=this.watchers.get(e);if(t!==void 0){this.watchers.delete(e);try{t()}catch(r){a(r)}}}rollbackOf(e){return e===void 0?p:()=>{for(const t of e)this.releaseWatcher(t)}}subscribe(e){const t={listener:e};this.listeners.add(t);let r=!0;return()=>{r&&(r=!1,this.listeners.delete(t))}}validateAll(e){const t=e.map(({entry:r})=>r);f(t),this.validate(t)}}export{g as ContributionHub,c as maxVisibilityRestarts,f as validateUniqueIds};
2
+ //# sourceMappingURL=contribution-hub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contribution-hub.js","sources":["../src/contribution-hub.ts"],"sourcesContent":["import { reportDetachedError } from './attachment';\nimport { beginDataTransaction, endDataTransaction } from './data-scheduler';\nimport type { DeclarationId } from './identity';\nimport type { Readable, ReadableListener } from './readable';\n\ninterface ContributionEntry<Value> {\n readonly id: DeclarationId;\n readonly priority: number;\n readonly value: Value;\n}\n\n/**\n * A contribution declared with `when` is present only while its readable holds anything but `false` (D79). The target\n * publishes the visible entries, so `Slot`, `fold`, `select` and emptiness checks never observe a withheld one.\n */\ntype ContributionVisibility = Readable<boolean> | undefined;\n\ninterface InternalContributionEntry<Value> {\n readonly entry: ContributionEntry<Value>;\n readonly token: object;\n readonly when: ContributionVisibility;\n}\n\ninterface ContributionHubUpdate<Value> {\n /**\n * The entries this update adds, and the only ones whose `when` it may read: a peer keeps the visibility it was\n * last published with, so one owner's unreadable predicate cannot hold another owner's entries (D225).\n */\n readonly added: readonly InternalContributionEntry<Value>[] | undefined;\n /** What the hub held when this update was prepared: a hub that moved under foreign code no longer matches it. */\n readonly base: readonly InternalContributionEntry<Value>[];\n readonly entries: readonly InternalContributionEntry<Value>[];\n}\n\ntype ContributionWatchRollback = () => void;\ntype ListenerRegistration = { readonly listener: ReadableListener };\ntype ValueNormalizer<Value> = (value: unknown) => Value;\ntype EntriesValidator<Value> = (entries: readonly ContributionEntry<Value>[]) => void;\n\nconst emptyEntries: readonly never[] = Object.freeze([]);\n/** Publication and refresh both stop a source that invalidates every projection instead of spinning forever. */\nconst maxVisibilityRestarts = 100;\n/** A hub that added no watcher has nothing to undo, and every publication shares this one empty rollback. */\nconst noWatchRollback: ContributionWatchRollback = (): void => undefined;\n\nfunction compareEntries<Value>(\n left: InternalContributionEntry<Value>,\n right: InternalContributionEntry<Value>,\n): number {\n const priority = left.entry.priority - right.entry.priority;\n\n if (priority !== 0) return priority;\n\n return left.entry.id < right.entry.id ? -1 : left.entry.id > right.entry.id ? 1 : 0;\n}\n\nfunction validateUniqueIds<Value>(entries: readonly ContributionEntry<Value>[]): void {\n if (entries.length < 2) return;\n\n const ids = new Set<DeclarationId>();\n\n for (const entry of entries) {\n if (ids.has(entry.id)) throw new TypeError(`Contribution ${entry.id} is already published to this target.`);\n\n ids.add(entry.id);\n }\n}\n\nfunction isEntryVisible<Value>(entry: InternalContributionEntry<Value>): boolean {\n return entry.when?.getSnapshot() !== false;\n}\n\nfunction indexEntries<Value>(entries: readonly Value[] | undefined): ReadonlySet<Value> | undefined {\n return entries !== undefined && entries.length > 1 ? new Set(entries) : undefined;\n}\n\nfunction includesEntry<Value>(\n entries: readonly Value[] | undefined,\n index: ReadonlySet<Value> | undefined,\n entry: Value,\n): boolean {\n return index === undefined ? entries?.[0] === entry : index.has(entry);\n}\n\n/**\n * What a publication may decide about visibility: its own entries answer their `when`, and every peer keeps the\n * answer it was last projected with. A withdrawal therefore reads nothing at all — a mandatory cleanup cannot be\n * blocked by an unreadable predicate that belongs to somebody else (D196, D220).\n */\nfunction projectUpdateEntries<Value>(\n update: ContributionHubUpdate<Value>,\n published: readonly ContributionEntry<Value>[],\n): readonly ContributionEntry<Value>[] {\n if (update.entries.length === 0) return emptyEntries;\n\n const added = update.added;\n // Singleton publications need no index. Larger snapshots must not search all peers again for every entry.\n const addedSet = indexEntries(added);\n const publishedSet = indexEntries(published);\n const visible: ContributionEntry<Value>[] = [];\n\n for (const entry of update.entries) {\n const admitted = includesEntry(added, addedSet, entry)\n ? isEntryVisible(entry)\n : includesEntry(published, publishedSet, entry.entry);\n\n if (admitted) visible.push(entry.entry);\n }\n\n return Object.freeze(visible);\n}\n\nfunction sameEntrySnapshot<Value>(\n left: readonly ContributionEntry<Value>[],\n right: readonly ContributionEntry<Value>[],\n): boolean {\n if (left.length !== right.length) return false;\n\n for (const [index, entry] of left.entries()) if (entry !== right[index]) return false;\n\n return true;\n}\n\nclass ContributionHub<Value> {\n readonly readable: Readable<readonly ContributionEntry<Value>[]>;\n /** How many times a watched `when` signalled: a projection taken before a signal is stale, whoever caused it. */\n visibilityRevision = 0;\n private entries: readonly InternalContributionEntry<Value>[] = emptyEntries;\n private readonly listeners = new Set<ListenerRegistration>();\n private readonly normalize: ValueNormalizer<Value>;\n private refreshDirty = false;\n private refreshing = false;\n private snapshot: readonly ContributionEntry<Value>[] = emptyEntries;\n private readonly validate: EntriesValidator<Value>;\n private readonly watchers = new Map<object, () => void>();\n\n constructor(normalize: ValueNormalizer<Value>, validate: EntriesValidator<Value>) {\n this.normalize = normalize;\n this.validate = validate;\n this.readable = Object.freeze({\n getSnapshot: (): readonly ContributionEntry<Value>[] => this.snapshot,\n subscribe: (listener: ReadableListener): (() => void) => this.subscribe(listener),\n });\n }\n\n /**\n * The mutation half of a publication, and the only half that moves an entry. The subscribe of `watch`, the\n * `getSnapshot` of `project` and the release of a watcher all run outside it, so nothing here calls foreign code\n * and nothing here can throw and leave a hub half-committed (D196).\n */\n commit(update: ContributionHubUpdate<Value>, snapshot: readonly ContributionEntry<Value>[]): void {\n this.entries = update.entries;\n this.snapshot = snapshot;\n }\n\n createEntry(\n id: DeclarationId,\n priority: number,\n value: unknown,\n when: ContributionVisibility,\n ): InternalContributionEntry<Value> {\n return Object.freeze({\n entry: Object.freeze({ id, priority, value: this.normalize(value) }),\n token: Object.freeze({}),\n when,\n });\n }\n\n hasToken(token: object): boolean {\n return this.entries.some(entry => entry.token === token);\n }\n\n /** Which of these tokens this hub actually holds. A publication that never committed holds none (D196). */\n knownTokens(tokens: ReadonlySet<object>): ReadonlySet<object> {\n const known = new Set<object>();\n\n for (const entry of this.entries) if (tokens.has(entry.token)) known.add(entry.token);\n\n return known;\n }\n\n /** Whether this hub still holds exactly what the update was prepared from. */\n matches(update: ContributionHubUpdate<Value>): boolean {\n return this.entries === update.base;\n }\n\n notify(): void {\n if (this.listeners.size === 0) return;\n\n for (const { listener } of [...this.listeners]) {\n try {\n listener();\n } catch (error) {\n reportDetachedError(error);\n }\n }\n }\n\n prepareAdd(additions: readonly InternalContributionEntry<Value>[]): ContributionHubUpdate<Value> {\n const mutableEntries = this.entries.concat(additions);\n mutableEntries.sort(compareEntries);\n const entries = Object.freeze(mutableEntries);\n this.validateAll(entries);\n\n return Object.freeze({ added: additions, base: this.entries, entries });\n }\n\n prepareRemove(tokens: ReadonlySet<object>): ContributionHubUpdate<Value> {\n let removed = 0;\n const entries = this.entries.filter(entry => {\n if (!tokens.has(entry.token)) return true;\n\n removed += 1;\n\n return false;\n });\n\n if (removed !== tokens.size)\n throw new TypeError('Contribution publication no longer matches its exact target state.');\n\n const frozen = Object.freeze(entries);\n this.validateAll(frozen);\n\n return Object.freeze({ added: undefined, base: this.entries, entries: frozen });\n }\n\n prepareRemoveOne(token: object): ContributionHubUpdate<Value> {\n if (this.entries.length === 1 && this.entries[0]?.token === token) {\n this.validate(emptyEntries);\n\n return Object.freeze({ added: undefined, base: this.entries, entries: emptyEntries });\n }\n\n const entries = this.entries.filter(entry => entry.token !== token);\n\n if (entries.length !== this.entries.length - 1) {\n throw new TypeError('Contribution publication no longer matches its exact target state.');\n }\n\n const frozen = Object.freeze(entries);\n this.validateAll(frozen);\n\n return Object.freeze({ added: undefined, base: this.entries, entries: frozen });\n }\n\n /**\n * The visible half of a commit, computed while every hub still holds what it held: `when.getSnapshot()` is foreign\n * code that may throw, or close and withdraw this very publication, and a commit that already moved an entry\n * would have nothing to walk back to (D196).\n */\n project(update: ContributionHubUpdate<Value>): readonly ContributionEntry<Value>[] {\n return projectUpdateEntries(update, this.snapshot);\n }\n\n /**\n * Releasing a watcher calls the disposer of a foreign `subscribe`, which may withdraw anything — including the\n * entries of another target. It therefore runs after every hub of the publication is committed, never between two\n * of them (D196).\n */\n pruneWatchers(): void {\n if (this.watchers.size === 0) return;\n\n const watched = new Set<object>();\n\n for (const entry of this.entries) if (entry.when !== undefined) watched.add(entry.token);\n\n for (const token of [...this.watchers.keys()]) {\n if (watched.has(token)) continue;\n\n this.releaseWatcher(token);\n }\n }\n\n /**\n * The preflight half: subscribing a `when` is the only step of a publication that runs foreign code, so it runs\n * before any entry moves. A subscribe that throws rolls back the watchers this call added and leaves the hub\n * exactly as it was (D196).\n */\n watch(entries: readonly InternalContributionEntry<Value>[]): ContributionWatchRollback {\n let added: object[] | undefined;\n\n try {\n for (const entry of entries) {\n if (entry.when === undefined || this.watchers.has(entry.token)) continue;\n\n const release = entry.when.subscribe((): void => {\n this.visibilityRevision += 1;\n this.refreshVisibility();\n });\n this.watchers.set(entry.token, release);\n (added ??= []).push(entry.token);\n }\n } catch (error) {\n added?.forEach(token => this.releaseWatcher(token));\n\n throw error;\n }\n\n return this.rollbackOf(added);\n }\n\n private projectVisibleEntries(): readonly ContributionEntry<Value>[] | undefined {\n const entries = this.entries;\n const revision = this.visibilityRevision;\n\n if (entries.length === 0) return emptyEntries;\n\n const visible: ContributionEntry<Value>[] = [];\n\n for (const entry of entries) {\n const admitted = isEntryVisible(entry);\n\n // D225: a getter may replace the publication or invalidate an earlier answer. Stop before reading another\n // entry that may already be withdrawn, and let refresh start over from the current committed entries.\n if (this.entries !== entries || this.visibilityRevision !== revision) return undefined;\n\n if (admitted) visible.push(entry.entry);\n }\n\n return Object.freeze(visible);\n }\n\n private publishVisibleEntries(): void {\n const snapshot = this.projectVisibleEntries();\n\n if (snapshot === undefined) {\n this.refreshDirty = true;\n\n return;\n }\n\n if (sameEntrySnapshot(snapshot, this.snapshot)) return;\n\n beginDataTransaction();\n\n try {\n this.snapshot = snapshot;\n this.notify();\n } finally {\n try {\n endDataTransaction();\n } catch (error) {\n reportDetachedError(error);\n }\n }\n }\n\n /**\n * A `when` flip republishes the visible entries in one transaction, so consumers recompute once (D79). A flip\n * performed inside an `entries` listener cannot recurse, but it must not be lost either: the guarded run records\n * it and the loop picks it up. The same bound covers getters and listeners that never let the refresh settle.\n */\n private refreshVisibility(): void {\n if (this.refreshing) {\n this.refreshDirty = true;\n\n return;\n }\n\n this.refreshing = true;\n\n try {\n for (let restart = 0; ; restart += 1) {\n this.refreshDirty = false;\n this.publishVisibleEntries();\n\n if (!this.refreshDirty) return;\n\n if (restart >= maxVisibilityRestarts) {\n throw new TypeError('Contribution visibility keeps changing while its target refreshes.');\n }\n }\n } finally {\n this.refreshing = false;\n this.refreshDirty = false;\n }\n }\n\n private releaseWatcher(token: object): void {\n const unwatch = this.watchers.get(token);\n\n if (unwatch === undefined) return;\n\n this.watchers.delete(token);\n\n try {\n unwatch();\n } catch (error) {\n reportDetachedError(error);\n }\n }\n\n /**\n * D196: the publication spans several targets, so a hub that subscribed hands back the way to undo exactly what it\n * added; a later hub that refuses then leaves no watcher behind on the hubs before it.\n */\n private rollbackOf(added: readonly object[] | undefined): ContributionWatchRollback {\n if (added === undefined) return noWatchRollback;\n\n return (): void => {\n for (const token of added) this.releaseWatcher(token);\n };\n }\n\n private subscribe(listener: ReadableListener): () => void {\n const registration = { listener };\n this.listeners.add(registration);\n let subscribed = true;\n\n return (): void => {\n if (!subscribed) return;\n\n subscribed = false;\n this.listeners.delete(registration);\n };\n }\n\n /** Uniqueness holds over every published contribution, so a `when` flip can never fail validation. */\n private validateAll(entries: readonly InternalContributionEntry<Value>[]): void {\n const published = entries.map(({ entry }) => entry);\n validateUniqueIds(published);\n this.validate(published);\n }\n}\n\nexport { ContributionHub, maxVisibilityRestarts, validateUniqueIds };\nexport type {\n ContributionEntry,\n ContributionHubUpdate,\n ContributionVisibility,\n ContributionWatchRollback,\n EntriesValidator,\n InternalContributionEntry,\n ValueNormalizer,\n};\n"],"names":["emptyEntries","maxVisibilityRestarts","noWatchRollback","compareEntries","left","right","priority","validateUniqueIds","entries","ids","entry","isEntryVisible","_a","indexEntries","includesEntry","index","projectUpdateEntries","update","published","added","addedSet","publishedSet","visible","sameEntrySnapshot","ContributionHub","normalize","validate","listener","snapshot","id","value","when","token","tokens","known","error","reportDetachedError","additions","mutableEntries","removed","frozen","watched","release","revision","admitted","beginDataTransaction","endDataTransaction","restart","unwatch","registration","subscribed"],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { ContributionHub, ContributionHubUpdate, InternalContributionEntry } from './contribution-hub.js';
2
+ /**
3
+ * A publication is one transaction across targets. Its foreign steps — subscribing every `when`, reading the `when`
4
+ * of its own entries, and releasing the watchers an entry left behind — all run outside the writes: the first two
5
+ * before any hub moves an entry, the last one after every hub of the publication is fixed. A failure, a reentrant
6
+ * withdrawal from a `when` and a reentrant withdrawal from a disposer therefore each walk away leaving no orphan
7
+ * entry and no orphan subscription anywhere (D196).
8
+ */
9
+ interface ContributionPublicationState {
10
+ readonly entries: readonly ErasedInternalContributionEntry[];
11
+ status: 'prepared' | 'published' | 'withdrawn';
12
+ }
13
+ type ErasedInternalContributionEntry = InternalContributionEntry<unknown> & {
14
+ readonly hub: ContributionHub<unknown>;
15
+ };
16
+ declare function commitHubUpdates(updates: ReadonlyMap<ContributionHub<unknown>, ContributionHubUpdate<unknown>>, state: ContributionPublicationState, status: 'published' | 'withdrawn'): void;
17
+ /** A single target needs the same preflight/project/commit/notify phases, without grouping maps or their iterators. */
18
+ declare function commitSingleHubUpdate(hub: ContributionHub<unknown>, update: ContributionHubUpdate<unknown>, state: ContributionPublicationState, status: 'published' | 'withdrawn'): void;
19
+ export { commitHubUpdates, commitSingleHubUpdate };
20
+ export type { ContributionPublicationState, ErasedInternalContributionEntry };
@@ -0,0 +1,2 @@
1
+ import{reportDetachedError as f}from"./attachment.js";import{maxVisibilityRestarts as u}from"./contribution-hub.js";import{endDataTransaction as p,beginDataTransaction as h}from"./data-scheduler.js";const l="Contribution publication no longer matches its exact target state.",y="Contribution visibility keeps changing while its publication projects.";function a(t){var r;for(let i=t.length-1;i>=0;i-=1)try{(r=t[i])==null||r.call(t)}catch(o){f(o)}}function w(t){const r=[];try{for(const[i,o]of t)r.push(i.watch(o.entries))}catch(i){throw a(r),i}return r}function m(t,r){let i=t.visibilityRevision,o=t.project(r);for(let e=0;t.visibilityRevision!==i;e+=1){if(e>=u)throw new TypeError(y);i=t.visibilityRevision,o=t.project(r)}return o}function b(t,r){const i=m(t,r);return{hub:t,revision:t.visibilityRevision,snapshot:i,update:r}}function d(t){let r=t;for(let i=0;r.some(({hub:o,revision:e})=>o.visibilityRevision!==e);i+=1){if(i>=u)throw new TypeError(y);r=r.map(o=>o.hub.visibilityRevision===o.revision?o:b(o.hub,o.update))}return r}function g(t,r){const i=[];try{for(const[o,e]of t)i.push(b(o,e));return d(i)}catch(o){throw a(r),o}}function R(t,r,i,o){if(r.status!==i)return a(o),!0;for(const{hub:e,update:n}of t)if(!e.matches(n))throw a(o),new TypeError(l);return!1}function T(t,r,i){const o=r.status,e=w(t),n=g(t,e);if(!R(n,r,o,e)){h();try{for(const{hub:s,snapshot:c,update:v}of n)s.commit(v,c);r.status=i;for(const{hub:s}of n)s.notify()}finally{try{p()}catch(s){f(s)}}for(const{hub:s}of n)s.pruneWatchers()}}function j(t,r,i,o){const e=i.status,n=t.watch(r.entries);let s;try{s=m(t,r)}catch(c){throw n(),c}if(i.status!==e){n();return}if(!t.matches(r))throw n(),new TypeError(l);h();try{t.commit(r,s),i.status=o,t.notify()}finally{try{p()}catch(c){f(c)}}t.pruneWatchers()}export{T as commitHubUpdates,j as commitSingleHubUpdate};
2
+ //# sourceMappingURL=contribution-transaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contribution-transaction.js","sources":["../src/contribution-transaction.ts"],"sourcesContent":["import { reportDetachedError } from './attachment';\nimport { maxVisibilityRestarts } from './contribution-hub';\nimport type {\n ContributionEntry,\n ContributionHub,\n ContributionHubUpdate,\n ContributionWatchRollback,\n InternalContributionEntry,\n} from './contribution-hub';\nimport { beginDataTransaction, endDataTransaction } from './data-scheduler';\n\n/**\n * A publication is one transaction across targets. Its foreign steps — subscribing every `when`, reading the `when`\n * of its own entries, and releasing the watchers an entry left behind — all run outside the writes: the first two\n * before any hub moves an entry, the last one after every hub of the publication is fixed. A failure, a reentrant\n * withdrawal from a `when` and a reentrant withdrawal from a disposer therefore each walk away leaving no orphan\n * entry and no orphan subscription anywhere (D196).\n */\ninterface ContributionPublicationState {\n readonly entries: readonly ErasedInternalContributionEntry[];\n status: 'prepared' | 'published' | 'withdrawn';\n}\n\ntype ErasedInternalContributionEntry = InternalContributionEntry<unknown> & {\n readonly hub: ContributionHub<unknown>;\n};\n\ninterface HubCommit {\n readonly hub: ContributionHub<unknown>;\n /** What the hub had signalled when this snapshot was taken; anything later makes the snapshot stale. */\n readonly revision: number;\n readonly snapshot: readonly ContributionEntry<unknown>[];\n readonly update: ContributionHubUpdate<unknown>;\n}\n\nconst staleTargetState = 'Contribution publication no longer matches its exact target state.';\nconst unstableVisibility = 'Contribution visibility keeps changing while its publication projects.';\n\n/** D196: one publication is one transaction across targets, so the hubs that subscribed undo in reverse order. */\nfunction releaseWatchers(rollbacks: readonly ContributionWatchRollback[]): void {\n for (let index = rollbacks.length - 1; index >= 0; index -= 1) {\n try {\n rollbacks[index]?.();\n } catch (rollbackError) {\n reportDetachedError(rollbackError);\n }\n }\n}\n\n/**\n * The first step of a publication that runs foreign code is subscribing a `when`, so it runs before any hub moves an\n * entry. A subscribe that throws therefore leaves every hub exactly as it was, and no orphan entry can survive a\n * failed publication (D196).\n */\nfunction watchHubUpdates(\n updates: ReadonlyMap<ContributionHub<unknown>, ContributionHubUpdate<unknown>>,\n): ContributionWatchRollback[] {\n const rollbacks: ContributionWatchRollback[] = [];\n\n try {\n for (const [hub, update] of updates) rollbacks.push(hub.watch(update.entries));\n } catch (error) {\n releaseWatchers(rollbacks);\n\n throw error;\n }\n\n return rollbacks;\n}\n\n/**\n * The second step that runs foreign code: the `when.getSnapshot()` of this publication's own entries, still before\n * any hub moves (D196). A `when` that signals while another one is read invalidates the projection it already\n * produced, so the affected hub is projected again rather than committed with what it answered before the signal.\n */\nfunction projectStable(\n hub: ContributionHub<unknown>,\n update: ContributionHubUpdate<unknown>,\n): readonly ContributionEntry<unknown>[] {\n let revision = hub.visibilityRevision;\n let snapshot = hub.project(update);\n\n for (let restart = 0; hub.visibilityRevision !== revision; restart += 1) {\n if (restart >= maxVisibilityRestarts) throw new TypeError(unstableVisibility);\n\n revision = hub.visibilityRevision;\n snapshot = hub.project(update);\n }\n\n return snapshot;\n}\n\nfunction projectHub(hub: ContributionHub<unknown>, update: ContributionHubUpdate<unknown>): HubCommit {\n const snapshot = projectStable(hub, update);\n\n return { hub, revision: hub.visibilityRevision, snapshot, update };\n}\n\n/** A later hub's `when` can move an earlier hub, so the whole set is projected until nothing signalled. */\nfunction stabilizeCommits(commits: readonly HubCommit[]): readonly HubCommit[] {\n let current = commits;\n\n for (let restart = 0; current.some(({ hub, revision }) => hub.visibilityRevision !== revision); restart += 1) {\n if (restart >= maxVisibilityRestarts) throw new TypeError(unstableVisibility);\n\n current = current.map(commit =>\n commit.hub.visibilityRevision === commit.revision ? commit : projectHub(commit.hub, commit.update),\n );\n }\n\n return current;\n}\n\nfunction projectHubUpdates(\n updates: ReadonlyMap<ContributionHub<unknown>, ContributionHubUpdate<unknown>>,\n rollbacks: readonly ContributionWatchRollback[],\n): readonly HubCommit[] {\n const commits: HubCommit[] = [];\n\n try {\n for (const [hub, update] of updates) commits.push(projectHub(hub, update));\n\n return stabilizeCommits(commits);\n } catch (error) {\n releaseWatchers(rollbacks);\n\n throw error;\n }\n}\n\n/**\n * What the two foreign steps may have done to this publication while they ran. A `when` that withdrew it — by\n * closing its own feature from inside `getSnapshot` — leaves nothing to publish, and a hub that moved no longer\n * matches the update prepared from it; either way the entries of this call never reach a target (D196).\n */\nfunction abandonedCommit(\n commits: readonly HubCommit[],\n state: ContributionPublicationState,\n expected: ContributionPublicationState['status'],\n rollbacks: readonly ContributionWatchRollback[],\n): boolean {\n if (state.status !== expected) {\n releaseWatchers(rollbacks);\n\n return true;\n }\n\n for (const { hub, update } of commits) {\n if (hub.matches(update)) continue;\n\n releaseWatchers(rollbacks);\n\n throw new TypeError(staleTargetState);\n }\n\n return false;\n}\n\nfunction commitHubUpdates(\n updates: ReadonlyMap<ContributionHub<unknown>, ContributionHubUpdate<unknown>>,\n state: ContributionPublicationState,\n status: 'published' | 'withdrawn',\n): void {\n const expected = state.status;\n const rollbacks = watchHubUpdates(updates);\n const commits = projectHubUpdates(updates, rollbacks);\n\n if (abandonedCommit(commits, state, expected, rollbacks)) return;\n\n beginDataTransaction();\n\n try {\n for (const { hub, snapshot, update } of commits) hub.commit(update, snapshot);\n\n state.status = status;\n\n for (const { hub } of commits) hub.notify();\n } finally {\n try {\n endDataTransaction();\n } catch (error) {\n reportDetachedError(error);\n }\n }\n\n // The disposers of the watchers this publication left behind: foreign code, so it runs on the committed state.\n for (const { hub } of commits) hub.pruneWatchers();\n}\n\n/** A single target needs the same preflight/project/commit/notify phases, without grouping maps or their iterators. */\nfunction commitSingleHubUpdate(\n hub: ContributionHub<unknown>,\n update: ContributionHubUpdate<unknown>,\n state: ContributionPublicationState,\n status: 'published' | 'withdrawn',\n): void {\n const expected = state.status;\n const rollback = hub.watch(update.entries);\n let snapshot: readonly ContributionEntry<unknown>[];\n\n try {\n snapshot = projectStable(hub, update);\n } catch (error) {\n rollback();\n\n throw error;\n }\n\n if (state.status !== expected) {\n rollback();\n\n return;\n }\n\n if (!hub.matches(update)) {\n rollback();\n\n throw new TypeError(staleTargetState);\n }\n\n beginDataTransaction();\n\n try {\n hub.commit(update, snapshot);\n state.status = status;\n hub.notify();\n } finally {\n try {\n endDataTransaction();\n } catch (error) {\n reportDetachedError(error);\n }\n }\n\n hub.pruneWatchers();\n}\n\nexport { commitHubUpdates, commitSingleHubUpdate };\nexport type { ContributionPublicationState, ErasedInternalContributionEntry };\n"],"names":["staleTargetState","unstableVisibility","releaseWatchers","rollbacks","index","_a","rollbackError","reportDetachedError","watchHubUpdates","updates","hub","update","error","projectStable","revision","snapshot","restart","maxVisibilityRestarts","projectHub","stabilizeCommits","commits","current","commit","projectHubUpdates","abandonedCommit","state","expected","commitHubUpdates","status","beginDataTransaction","endDataTransaction","commitSingleHubUpdate","rollback"],"mappings":"uMAmCA,MAAMA,EAAmB,qEACnBC,EAAqB,yEAG3B,SAASC,EAAgBC,EAA+C,OACtE,QAASC,EAAQD,EAAU,OAAS,EAAGC,GAAS,EAAGA,GAAS,EAC1D,GAAI,EACFC,EAAAF,EAAUC,KAAV,MAAAC,EAAA,KAAAF,EACF,OAASG,EAAe,CACtBC,EAAoBD,CAAa,CACnC,CAEJ,CAOA,SAASE,EACPC,EAA8E,CAE9E,MAAMN,EAAyC,CAAA,EAE/C,GAAI,CACF,SAAW,CAACO,EAAKC,CAAM,IAAKF,EAASN,EAAU,KAAKO,EAAI,MAAMC,EAAO,OAAO,CAAC,CAC/E,OAASC,EAAO,CACd,MAAAV,EAAgBC,CAAS,EAEnBS,CACR,CAEA,OAAOT,CACT,CAOA,SAASU,EACPH,EACAC,EAAsC,CAEtC,IAAIG,EAAWJ,EAAI,mBACfK,EAAWL,EAAI,QAAQC,CAAM,EAEjC,QAASK,EAAU,EAAGN,EAAI,qBAAuBI,EAAUE,GAAW,EAAG,CACvE,GAAIA,GAAWC,EAAuB,MAAM,IAAI,UAAUhB,CAAkB,EAE5Ea,EAAWJ,EAAI,mBACfK,EAAWL,EAAI,QAAQC,CAAM,CAC/B,CAEA,OAAOI,CACT,CAEA,SAASG,EAAWR,EAA+BC,EAAsC,CACvF,MAAMI,EAAWF,EAAcH,EAAKC,CAAM,EAE1C,MAAO,CAAE,IAAAD,EAAK,SAAUA,EAAI,mBAAoB,SAAAK,EAAU,OAAAJ,CAAM,CAClE,CAGA,SAASQ,EAAiBC,EAA6B,CACrD,IAAIC,EAAUD,EAEd,QAASJ,EAAU,EAAGK,EAAQ,KAAK,CAAC,CAAE,IAAAX,EAAK,SAAAI,CAAQ,IAAOJ,EAAI,qBAAuBI,CAAQ,EAAGE,GAAW,EAAG,CAC5G,GAAIA,GAAWC,EAAuB,MAAM,IAAI,UAAUhB,CAAkB,EAE5EoB,EAAUA,EAAQ,IAAIC,GACpBA,EAAO,IAAI,qBAAuBA,EAAO,SAAWA,EAASJ,EAAWI,EAAO,IAAKA,EAAO,MAAM,CAAC,CAEtG,CAEA,OAAOD,CACT,CAEA,SAASE,EACPd,EACAN,EAA+C,CAE/C,MAAMiB,EAAuB,CAAA,EAE7B,GAAI,CACF,SAAW,CAACV,EAAKC,CAAM,IAAKF,EAASW,EAAQ,KAAKF,EAAWR,EAAKC,CAAM,CAAC,EAEzE,OAAOQ,EAAiBC,CAAO,CACjC,OAASR,EAAO,CACd,MAAAV,EAAgBC,CAAS,EAEnBS,CACR,CACF,CAOA,SAASY,EACPJ,EACAK,EACAC,EACAvB,EAA+C,CAE/C,GAAIsB,EAAM,SAAWC,EACnB,OAAAxB,EAAgBC,CAAS,EAElB,GAGT,SAAW,CAAE,IAAAO,EAAK,OAAAC,CAAM,IAAMS,EAC5B,GAAI,CAAAV,EAAI,QAAQC,CAAM,EAEtB,MAAAT,EAAgBC,CAAS,EAEnB,IAAI,UAAUH,CAAgB,EAGtC,MAAO,EACT,CAEA,SAAS2B,EACPlB,EACAgB,EACAG,EAAiC,CAEjC,MAAMF,EAAWD,EAAM,OACjBtB,EAAYK,EAAgBC,CAAO,EACnCW,EAAUG,EAAkBd,EAASN,CAAS,EAEpD,GAAI,CAAAqB,EAAgBJ,EAASK,EAAOC,EAAUvB,CAAS,EAEvD,CAAA0B,EAAoB,EAEpB,GAAI,CACF,SAAW,CAAE,IAAAnB,EAAK,SAAAK,EAAU,OAAAJ,CAAM,IAAMS,EAASV,EAAI,OAAOC,EAAQI,CAAQ,EAE5EU,EAAM,OAASG,EAEf,SAAW,CAAE,IAAAlB,CAAG,IAAMU,EAASV,EAAI,OAAM,CAC3C,SACE,GAAI,CACFoB,EAAkB,CACpB,OAASlB,EAAO,CACdL,EAAoBK,CAAK,CAC3B,CACF,CAGA,SAAW,CAAE,IAAAF,CAAG,IAAMU,EAASV,EAAI,cAAa,EAClD,CAGA,SAASqB,EACPrB,EACAC,EACAc,EACAG,EAAiC,CAEjC,MAAMF,EAAWD,EAAM,OACjBO,EAAWtB,EAAI,MAAMC,EAAO,OAAO,EACzC,IAAII,EAEJ,GAAI,CACFA,EAAWF,EAAcH,EAAKC,CAAM,CACtC,OAASC,EAAO,CACd,MAAAoB,EAAQ,EAEFpB,CACR,CAEA,GAAIa,EAAM,SAAWC,EAAU,CAC7BM,EAAQ,EAER,MACF,CAEA,GAAI,CAACtB,EAAI,QAAQC,CAAM,EACrB,MAAAqB,EAAQ,EAEF,IAAI,UAAUhC,CAAgB,EAGtC6B,EAAoB,EAEpB,GAAI,CACFnB,EAAI,OAAOC,EAAQI,CAAQ,EAC3BU,EAAM,OAASG,EACflB,EAAI,OAAM,CACZ,SACE,GAAI,CACFoB,EAAkB,CACpB,OAASlB,EAAO,CACdL,EAAoBK,CAAK,CAC3B,CACF,CAEAF,EAAI,cAAa,CACnB"}
@@ -0,0 +1,64 @@
1
+ import type { ContributionEntry, ContributionVisibility } from './contribution-hub.js';
2
+ import type { DeclarationId } from './identity.js';
3
+ import type { Collection, Lookup } from './reactive-collection.js';
4
+ import type { Readable } from './readable.js';
5
+ declare const contributionPublicationBrand: unique symbol;
6
+ declare const contributionTargetIdentityBrand: unique symbol;
7
+ declare const contributionTargetValueBrand: unique symbol;
8
+ type NoExtraKeys<Actual, Shape> = Record<Exclude<keyof Actual, keyof Shape>, never>;
9
+ interface ContributionTargetIdentity {
10
+ readonly [contributionTargetIdentityBrand]: true;
11
+ readonly entries: Readable<readonly ContributionEntry<unknown>[]>;
12
+ readonly id: DeclarationId;
13
+ }
14
+ interface ContributionTarget<Value> extends ContributionTargetIdentity {
15
+ readonly [contributionTargetValueBrand]: (value: Value) => Value;
16
+ readonly entries: Readable<readonly ContributionEntry<Value>[]>;
17
+ }
18
+ type ContributionValue<Target> = Target extends ContributionTarget<infer Value> ? Value : never;
19
+ type PipeRead = <Value>(readable: Readable<Value>) => Value;
20
+ /**
21
+ * The third argument of a pipe handler. `read` reads with tracking when `fold` received a tracking reader, so a
22
+ * `computed` around `fold` subscribes to everything the handlers read; otherwise it reads the current snapshot
23
+ * (D82). The field is named the way every other reactive calculation names it (D222).
24
+ */
25
+ type PipeReadContext = {
26
+ readonly read: PipeRead;
27
+ };
28
+ type PipeHandler<Value, Meta = void> = (value: Value, meta: Meta, context: PipeReadContext) => Value;
29
+ interface Pipe<Value, Meta = void> extends ContributionTarget<PipeHandler<Value, Meta>> {
30
+ readonly fold: (value: Value, meta: Meta, get?: PipeRead) => Value;
31
+ }
32
+ interface KeyedContribution<Key, Value> {
33
+ readonly key: Key;
34
+ readonly value: Value;
35
+ }
36
+ interface Registry<Key, Value> extends Collection<Key, Value>, ContributionTarget<KeyedContribution<Key, Value>> {
37
+ readonly select: (key: Key) => Readable<Lookup<Value>>;
38
+ }
39
+ interface ContributionTargetOptions {
40
+ readonly id: string;
41
+ }
42
+ declare function getContributionOwner(entry: unknown): unknown;
43
+ interface ContributionPublicationInput {
44
+ readonly id: string;
45
+ /** The generation this publication answers for: the renderer grants exactly its models to the mount (D70). */
46
+ readonly owner?: unknown;
47
+ readonly priority: number;
48
+ readonly target: ContributionTargetIdentity;
49
+ readonly value: unknown;
50
+ readonly when?: ContributionVisibility;
51
+ }
52
+ interface ContributionPublication {
53
+ readonly [contributionPublicationBrand]: object;
54
+ }
55
+ declare function createContributionTarget<Value, const Options extends ContributionTargetOptions = ContributionTargetOptions>(options: NoExtraKeys<Options, ContributionTargetOptions> & Options): ContributionTarget<Value>;
56
+ declare function definePipe<Value, Meta = void, const Options extends ContributionTargetOptions = ContributionTargetOptions>(options: NoExtraKeys<Options, ContributionTargetOptions> & Options): Pipe<Value, Meta>;
57
+ declare function defineRegistry<Key, Value, const Options extends ContributionTargetOptions = ContributionTargetOptions>(options: NoExtraKeys<Options, ContributionTargetOptions> & Options): Registry<Key, Value>;
58
+ declare function publishContributions(inputs: readonly ContributionPublicationInput[], capture?: (publication: ContributionPublication) => void): ContributionPublication;
59
+ declare function withdrawContributions(publication: ContributionPublication): boolean;
60
+ declare function isContributionTarget(value: unknown): value is ContributionTargetIdentity;
61
+ export { definePipe, defineRegistry };
62
+ export type { ContributionEntry, ContributionTarget, ContributionValue, KeyedContribution, Pipe, PipeHandler, PipeRead, PipeReadContext, Registry, };
63
+ export { createContributionTarget, getContributionOwner, isContributionTarget, publishContributions, withdrawContributions, };
64
+ export type { ContributionPublication, ContributionPublicationInput };
@@ -0,0 +1,2 @@
1
+ import{ContributionHub as m,validateUniqueIds as c}from"./contribution-hub.js";import{commitHubUpdates as d,commitSingleHubUpdate as h}from"./contribution-transaction.js";import{declarationId as w}from"./identity.js";import{collection as k,selectByKey as C}from"./reactive-collection.js";const T=Symbol("opetope.contribution-publication"),E=Object.freeze({}),y=new WeakMap;function j(t){return typeof t=="object"&&t!==null||typeof t=="function"}function z(t,n){y.set(t,n)}function O(t){if(j(t))return y.get(t)}const a=new WeakMap,l=new WeakMap;function f(t,n){if(typeof t!="object"||t===null)throw new TypeError(`${n} must be an object.`)}function b(t,n){f(t,n);const e=t.id;if(typeof e!="string")throw new TypeError(`${n}.id must be a string.`);return w(e)}function p(t,n,e,r){const o=new m(n,e),i=Object.freeze({entries:o.readable,id:t,...r(o.readable)});return a.set(i,o),i}function S(t){const n=e=>e;return p(b(t,"Contribution target options"),n,c,()=>({}))}const P=Object.freeze({read:t=>t.getSnapshot()});function M(t){const n=e=>{if(typeof e!="function")throw new TypeError("Pipe contributions must be handler functions.");return e};return p(b(t,"Pipe options"),n,c,e=>({fold(r,o,i){const u=i===void 0?P:{read:i};let s=r;for(const g of i===void 0?e.getSnapshot():i(e))s=g.value(s,o,u);return s}}))}function I(t){return f(t,"Keyed contribution"),Object.freeze({key:t.key,value:t.value})}function R(t){c(t);const n=new Set;for(const{value:e}of t){if(n.has(e.key))throw new TypeError("Contribution registry keys must be unique.");n.add(e.key)}}function U(t){return p(b(t,"Contribution registry options"),I,R,n=>{const e=k({from:n,key:r=>r.value.key,project:r=>r.value.value});return{byId:e.byId,getSnapshot:e.getSnapshot,list:e.list,select:r=>C(e,r),subscribe:e.subscribe}})}function q(t){if(typeof t!="object"||t===null)throw new TypeError("Contribution target is not authentic.");const n=a.get(t);if(n===void 0)throw new TypeError("Contribution target is not authentic.");return n}function A(t){if(t!==void 0){if(typeof t!="object"||t===null||typeof t.getSnapshot!="function"||typeof t.subscribe!="function")throw new TypeError("Contribution publication.when must be a readable.");return t}}function H(t){f(t,"Contribution publication");const n=t.target,e=q(n),r=t.id,o=t.priority;if(typeof r!="string")throw new TypeError("Contribution publication.id must be a string.");if(typeof o!="number"||!Number.isFinite(o))throw new TypeError("Contribution publication.priority must be a finite number.");return{hub:e,id:w(r),owner:t.owner,priority:o,target:n,value:t.value,when:A(t.when)}}function W(t,n,e){if(e!==void 0)try{e(t)}catch(r){throw n.status="withdrawn",r}}function x(t){const n=new Map;for(const r of t.entries){const o=n.get(r.hub)??[];o.push(r),n.set(r.hub,o)}const e=new Map;try{for(const[r,o]of n)e.set(r,r.prepareAdd(o))}catch(r){throw t.status="withdrawn",r}return e}function K(t){const n=t.entries.length===1?t.entries[0]:void 0;n!==void 0?h(n.hub,n.hub.prepareAdd([n]),t,"published"):d(x(t),t,"published")}function B(t,n){if(!Array.isArray(t))throw new TypeError("Contribution publications must be an array.");const e=t.map(H),r=[];for(const u of e){const s=u.hub.createEntry(u.id,u.priority,u.value,u.when);u.owner!==void 0&&z(s.entry,u.owner),r.push(Object.freeze({...s,hub:u.hub}))}const o=Object.freeze({[T]:E}),i={entries:Object.freeze(r),status:"prepared"};if(l.set(o,i),W(o,i,n),i.status==="withdrawn")return o;try{K(i)}catch(u){throw i.status="withdrawn",u}return o}function V(t){const n=new Map;for(const e of t.entries){const r=n.get(e.hub)??new Set;r.add(e.token),n.set(e.hub,r)}return n}function $(t){const n=new Map;for(const[e,r]of V(t)){const o=t.status==="published"?r:e.knownTokens(r);o.size!==0&&n.set(e,e.prepareRemove(o))}return n}function F(t,n){n.status==="published"||t.hub.hasToken(t.token)?h(t.hub,t.hub.prepareRemoveOne(t.token),n,"withdrawn"):n.status="withdrawn"}function N(t){const n=l.get(t);if(n===void 0)throw new TypeError("Contribution publication is not authentic.");if(n.status==="withdrawn")return!1;const e=n.entries.length===1?n.entries[0]:void 0;if(e!==void 0)return F(e,n),!0;const r=$(n);return r.size===0?(n.status="withdrawn",!0):(d(r,n,"withdrawn"),!0)}function D(t){return typeof t=="object"&&t!==null&&a.has(t)}export{S as createContributionTarget,M as definePipe,U as defineRegistry,O as getContributionOwner,D as isContributionTarget,B as publishContributions,N as withdrawContributions};
2
+ //# sourceMappingURL=contribution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contribution.js","sources":["../src/contribution.ts"],"sourcesContent":["import { ContributionHub, validateUniqueIds } from './contribution-hub';\nimport type {\n ContributionEntry,\n ContributionHubUpdate,\n ContributionVisibility,\n EntriesValidator,\n InternalContributionEntry,\n ValueNormalizer,\n} from './contribution-hub';\nimport { commitHubUpdates, commitSingleHubUpdate } from './contribution-transaction';\nimport type { ContributionPublicationState, ErasedInternalContributionEntry } from './contribution-transaction';\nimport { declarationId } from './identity';\nimport type { DeclarationId } from './identity';\nimport { collection, selectByKey } from './reactive-collection';\nimport type { Collection, Lookup } from './reactive-collection';\nimport type { Readable } from './readable';\n\nconst contributionPublicationBrand = Symbol('opetope.contribution-publication');\nconst contributionPublicationMarker = Object.freeze({});\ndeclare const contributionTargetIdentityBrand: unique symbol;\ndeclare const contributionTargetValueBrand: unique symbol;\n\ntype NoExtraKeys<Actual, Shape> = Record<Exclude<keyof Actual, keyof Shape>, never>;\n\ninterface ContributionTargetIdentity {\n readonly [contributionTargetIdentityBrand]: true;\n readonly entries: Readable<readonly ContributionEntry<unknown>[]>;\n readonly id: DeclarationId;\n}\n\ninterface ContributionTarget<Value> extends ContributionTargetIdentity {\n readonly [contributionTargetValueBrand]: (value: Value) => Value;\n readonly entries: Readable<readonly ContributionEntry<Value>[]>;\n}\n\ntype ContributionValue<Target> = Target extends ContributionTarget<infer Value> ? Value : never;\n\ntype PipeRead = <Value>(readable: Readable<Value>) => Value;\n\n/**\n * The third argument of a pipe handler. `read` reads with tracking when `fold` received a tracking reader, so a\n * `computed` around `fold` subscribes to everything the handlers read; otherwise it reads the current snapshot\n * (D82). The field is named the way every other reactive calculation names it (D222).\n */\ntype PipeReadContext = { readonly read: PipeRead };\n\ntype PipeHandler<Value, Meta = void> = (value: Value, meta: Meta, context: PipeReadContext) => Value;\n\ninterface Pipe<Value, Meta = void> extends ContributionTarget<PipeHandler<Value, Meta>> {\n readonly fold: (value: Value, meta: Meta, get?: PipeRead) => Value;\n}\n\ninterface KeyedContribution<Key, Value> {\n readonly key: Key;\n readonly value: Value;\n}\n\ninterface Registry<Key, Value> extends Collection<Key, Value>, ContributionTarget<KeyedContribution<Key, Value>> {\n readonly select: (key: Key) => Readable<Lookup<Value>>;\n}\n\ninterface ContributionTargetOptions {\n readonly id: string;\n}\n\n/**\n * The generation that published a contribution. A renderer reads it to give the contribution the authority of its\n * own feature: the models the generation owns, nothing else (D70). The key is the published entry rather than the\n * value the author wrote, because one static contribution object may be published by several features, and each\n * publication answers for its own generation.\n */\nconst contributionOwners = new WeakMap<object, unknown>();\n\nfunction isContributionValueObject(value: unknown): value is object {\n return (typeof value === 'object' && value !== null) || typeof value === 'function';\n}\n\nfunction setContributionOwner(entry: object, owner: unknown): void {\n contributionOwners.set(entry, owner);\n}\n\nfunction getContributionOwner(entry: unknown): unknown {\n if (!isContributionValueObject(entry)) return undefined;\n\n return contributionOwners.get(entry);\n}\n\ninterface ContributionPublicationInput {\n readonly id: string;\n /** The generation this publication answers for: the renderer grants exactly its models to the mount (D70). */\n readonly owner?: unknown;\n readonly priority: number;\n readonly target: ContributionTargetIdentity;\n readonly value: unknown;\n readonly when?: ContributionVisibility;\n}\n\ninterface ContributionPublication {\n readonly [contributionPublicationBrand]: object;\n}\n\nconst targetHubs = new WeakMap<object, ContributionHub<unknown>>();\nconst publicationStates = new WeakMap<ContributionPublication, ContributionPublicationState>();\n\nfunction assertRecord(value: unknown, label: string): asserts value is Readonly<Record<string, unknown>> {\n if (typeof value !== 'object' || value === null) throw new TypeError(`${label} must be an object.`);\n}\n\nfunction targetId(options: unknown, label: string): DeclarationId {\n assertRecord(options, label);\n const id = options['id'];\n\n if (typeof id !== 'string') throw new TypeError(`${label}.id must be a string.`);\n\n return declarationId(id);\n}\n\nfunction createTarget<Value, Extra extends object>(\n id: DeclarationId,\n normalize: ValueNormalizer<Value>,\n validate: EntriesValidator<Value>,\n extend: (entries: Readable<readonly ContributionEntry<Value>[]>) => Extra,\n): ContributionTarget<Value> & Extra {\n const hub = new ContributionHub(normalize, validate);\n const target = Object.freeze({ entries: hub.readable, id, ...extend(hub.readable) }) as ContributionTarget<Value> &\n Extra;\n targetHubs.set(target, hub as ContributionHub<unknown>);\n\n return target;\n}\n\nfunction createContributionTarget<Value, const Options extends ContributionTargetOptions = ContributionTargetOptions>(\n options: NoExtraKeys<Options, ContributionTargetOptions> & Options,\n): ContributionTarget<Value> {\n const normalize = (value: unknown): Value => value as Value;\n\n return createTarget<Value, object>(\n targetId(options, 'Contribution target options'),\n normalize,\n validateUniqueIds,\n () => ({}),\n );\n}\n\nconst untrackedPipeContext: PipeReadContext = Object.freeze({\n read: <Value>(readable: Readable<Value>): Value => readable.getSnapshot(),\n});\n\nfunction definePipe<Value, Meta = void, const Options extends ContributionTargetOptions = ContributionTargetOptions>(\n options: NoExtraKeys<Options, ContributionTargetOptions> & Options,\n): Pipe<Value, Meta> {\n const normalize = (value: unknown): PipeHandler<Value, Meta> => {\n if (typeof value !== 'function') throw new TypeError('Pipe contributions must be handler functions.');\n\n return value as PipeHandler<Value, Meta>;\n };\n\n return createTarget(targetId(options, 'Pipe options'), normalize, validateUniqueIds, entries => ({\n fold(value: Value, meta: Meta, read?: PipeRead): Value {\n // One context per fold, not per handler: the loop stays free of per-call allocations.\n const context = read === undefined ? untrackedPipeContext : { read };\n let current = value;\n\n for (const entry of read === undefined ? entries.getSnapshot() : read(entries)) {\n current = entry.value(current, meta, context);\n }\n\n return current;\n },\n }));\n}\n\nfunction normalizeKeyedContribution<Key, Value>(value: unknown): KeyedContribution<Key, Value> {\n assertRecord(value, 'Keyed contribution');\n\n return Object.freeze({ key: value['key'] as Key, value: value['value'] as Value });\n}\n\nfunction validateRegistryEntries<Key, Value>(\n entries: readonly ContributionEntry<KeyedContribution<Key, Value>>[],\n): void {\n validateUniqueIds(entries);\n const keys = new Set<Key>();\n\n for (const { value } of entries) {\n if (keys.has(value.key)) throw new TypeError('Contribution registry keys must be unique.');\n\n keys.add(value.key);\n }\n}\n\nfunction defineRegistry<Key, Value, const Options extends ContributionTargetOptions = ContributionTargetOptions>(\n options: NoExtraKeys<Options, ContributionTargetOptions> & Options,\n): Registry<Key, Value> {\n return createTarget<\n KeyedContribution<Key, Value>,\n Omit<Registry<Key, Value>, keyof ContributionTarget<KeyedContribution<Key, Value>>>\n >(\n targetId(options, 'Contribution registry options'),\n normalizeKeyedContribution<Key, Value>,\n validateRegistryEntries<Key, Value>,\n entries => {\n const values = collection({\n from: entries,\n key: entry => entry.value.key,\n project: entry => entry.value.value,\n });\n\n return {\n byId: values.byId,\n getSnapshot: values.getSnapshot,\n list: values.list,\n select: (key: Key): Readable<Lookup<Value>> => selectByKey(values, key),\n subscribe: values.subscribe,\n };\n },\n );\n}\n\nfunction requireTarget(value: unknown): ContributionHub<unknown> {\n if (typeof value !== 'object' || value === null) throw new TypeError('Contribution target is not authentic.');\n\n const hub = targetHubs.get(value);\n\n if (hub === undefined) throw new TypeError('Contribution target is not authentic.');\n\n return hub;\n}\n\nfunction requireVisibility(value: unknown): ContributionVisibility {\n if (value === undefined) return undefined;\n\n if (\n typeof value !== 'object' ||\n value === null ||\n typeof (value as Readable<boolean>).getSnapshot !== 'function' ||\n typeof (value as Readable<boolean>).subscribe !== 'function'\n ) {\n throw new TypeError('Contribution publication.when must be a readable.');\n }\n\n return value as Readable<boolean>;\n}\n\nfunction snapshotPublicationInput(value: unknown): {\n readonly hub: ContributionHub<unknown>;\n readonly id: DeclarationId;\n readonly owner: unknown;\n readonly priority: number;\n readonly target: ContributionTargetIdentity;\n readonly value: unknown;\n readonly when: ContributionVisibility;\n} {\n assertRecord(value, 'Contribution publication');\n const target = value['target'];\n const hub = requireTarget(target);\n const id = value['id'];\n const priority = value['priority'];\n\n if (typeof id !== 'string') throw new TypeError('Contribution publication.id must be a string.');\n\n if (typeof priority !== 'number' || !Number.isFinite(priority)) {\n throw new TypeError('Contribution publication.priority must be a finite number.');\n }\n\n return {\n hub,\n id: declarationId(id),\n owner: value['owner'],\n priority,\n target: target as ContributionTargetIdentity,\n value: value['value'],\n when: requireVisibility(value['when']),\n };\n}\n\nfunction capturePublication(\n publication: ContributionPublication,\n state: ContributionPublicationState,\n capture: ((publication: ContributionPublication) => void) | undefined,\n): void {\n if (capture === undefined) return;\n\n try {\n capture(publication);\n } catch (error) {\n state.status = 'withdrawn';\n throw error;\n }\n}\n\nfunction preparePublishUpdates(\n state: ContributionPublicationState,\n): ReadonlyMap<ContributionHub<unknown>, ContributionHubUpdate<unknown>> {\n const grouped = new Map<ContributionHub<unknown>, InternalContributionEntry<unknown>[]>();\n\n for (const entry of state.entries) {\n const groupedEntries = grouped.get(entry.hub) ?? [];\n groupedEntries.push(entry);\n grouped.set(entry.hub, groupedEntries);\n }\n\n const updates = new Map<ContributionHub<unknown>, ContributionHubUpdate<unknown>>();\n\n try {\n for (const [hub, additions] of grouped) updates.set(hub, hub.prepareAdd(additions));\n } catch (error) {\n state.status = 'withdrawn';\n throw error;\n }\n\n return updates;\n}\n\nfunction publishEntries(state: ContributionPublicationState): void {\n const single = state.entries.length === 1 ? state.entries[0] : undefined;\n\n if (single !== undefined) {\n commitSingleHubUpdate(single.hub, single.hub.prepareAdd([single]), state, 'published');\n } else {\n commitHubUpdates(preparePublishUpdates(state), state, 'published');\n }\n}\n\nfunction publishContributions(\n inputs: readonly ContributionPublicationInput[],\n capture?: (publication: ContributionPublication) => void,\n): ContributionPublication {\n if (!Array.isArray(inputs)) throw new TypeError('Contribution publications must be an array.');\n\n const snapshots = inputs.map(snapshotPublicationInput);\n const entries: ErasedInternalContributionEntry[] = [];\n\n for (const snapshot of snapshots) {\n const entry = snapshot.hub.createEntry(snapshot.id, snapshot.priority, snapshot.value, snapshot.when);\n\n if (snapshot.owner !== undefined) setContributionOwner(entry.entry, snapshot.owner);\n\n entries.push(Object.freeze({ ...entry, hub: snapshot.hub }));\n }\n\n const publication = Object.freeze({\n [contributionPublicationBrand]: contributionPublicationMarker,\n }) as ContributionPublication;\n const state: ContributionPublicationState = { entries: Object.freeze(entries), status: 'prepared' };\n publicationStates.set(publication, state);\n capturePublication(publication, state, capture);\n\n if (state.status === 'withdrawn') return publication;\n\n try {\n publishEntries(state);\n } catch (error) {\n state.status = 'withdrawn';\n\n throw error;\n }\n\n return publication;\n}\n\nfunction groupPublicationTokens(\n state: ContributionPublicationState,\n): ReadonlyMap<ContributionHub<unknown>, Set<object>> {\n const grouped = new Map<ContributionHub<unknown>, Set<object>>();\n\n for (const entry of state.entries) {\n const tokens = grouped.get(entry.hub) ?? new Set<object>();\n tokens.add(entry.token);\n grouped.set(entry.hub, tokens);\n }\n\n return grouped;\n}\n\n/**\n * A publication that never reached `published` normally holds nothing, but withdrawing it must still take away\n * whatever did land: a hub without the token is skipped, a hub with it removes exactly that token (D196).\n */\nfunction prepareWithdrawUpdates(\n state: ContributionPublicationState,\n): ReadonlyMap<ContributionHub<unknown>, ContributionHubUpdate<unknown>> {\n const updates = new Map<ContributionHub<unknown>, ContributionHubUpdate<unknown>>();\n\n for (const [hub, tokens] of groupPublicationTokens(state)) {\n const known = state.status === 'published' ? tokens : hub.knownTokens(tokens);\n\n if (known.size === 0) continue;\n\n updates.set(hub, hub.prepareRemove(known));\n }\n\n return updates;\n}\n\nfunction withdrawSingleEntry(single: ErasedInternalContributionEntry, state: ContributionPublicationState): void {\n if (state.status === 'published' || single.hub.hasToken(single.token)) {\n commitSingleHubUpdate(single.hub, single.hub.prepareRemoveOne(single.token), state, 'withdrawn');\n } else {\n state.status = 'withdrawn';\n }\n}\n\nfunction withdrawContributions(publication: ContributionPublication): boolean {\n const state = publicationStates.get(publication);\n\n if (state === undefined) throw new TypeError('Contribution publication is not authentic.');\n\n if (state.status === 'withdrawn') return false;\n\n const single = state.entries.length === 1 ? state.entries[0] : undefined;\n\n if (single !== undefined) {\n withdrawSingleEntry(single, state);\n\n return true;\n }\n\n const updates = prepareWithdrawUpdates(state);\n\n if (updates.size === 0) {\n state.status = 'withdrawn';\n\n return true;\n }\n\n commitHubUpdates(updates, state, 'withdrawn');\n\n return true;\n}\n\nfunction isContributionTarget(value: unknown): value is ContributionTargetIdentity {\n return typeof value === 'object' && value !== null && targetHubs.has(value);\n}\n\nexport { definePipe, defineRegistry };\nexport type {\n ContributionEntry,\n ContributionTarget,\n ContributionValue,\n KeyedContribution,\n Pipe,\n PipeHandler,\n PipeRead,\n PipeReadContext,\n Registry,\n};\nexport {\n createContributionTarget,\n getContributionOwner,\n isContributionTarget,\n publishContributions,\n withdrawContributions,\n};\nexport type { ContributionPublication, ContributionPublicationInput };\n"],"names":["contributionPublicationBrand","contributionPublicationMarker","contributionOwners","isContributionValueObject","value","setContributionOwner","entry","owner","getContributionOwner","targetHubs","publicationStates","assertRecord","label","targetId","options","id","declarationId","createTarget","normalize","validate","extend","hub","ContributionHub","target","createContributionTarget","validateUniqueIds","untrackedPipeContext","readable","definePipe","entries","meta","read","context","current","normalizeKeyedContribution","validateRegistryEntries","keys","defineRegistry","values","collection","key","selectByKey","requireTarget","requireVisibility","snapshotPublicationInput","priority","capturePublication","publication","state","capture","error","preparePublishUpdates","grouped","groupedEntries","updates","additions","publishEntries","single","commitSingleHubUpdate","commitHubUpdates","publishContributions","inputs","snapshots","snapshot","groupPublicationTokens","tokens","prepareWithdrawUpdates","known","withdrawSingleEntry","withdrawContributions","isContributionTarget"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ declare function beginDataTransaction(): void;
2
+ declare function endDataTransaction(): void;
3
+ declare function scheduleDataWork(flush: () => void): void;
4
+ export { beginDataTransaction, endDataTransaction, scheduleDataWork };
@@ -0,0 +1,2 @@
1
+ const n=new Set;let i=!1,t=!1,e=0;function s(){e+=1}function o(){return n.size>0&&!t&&e===0}function u(){if(!(i||e>0)){i=!0,t=!1;try{for(;n.size>0;){const a=[...n];n.clear();for(const r of a)r()}}finally{i=!1,o()&&(t=!0,queueMicrotask(u))}}}function f(){e-=1,e===0&&u()}function c(){t||e>0||(t=!0,queueMicrotask(u))}function l(a){n.add(a),c()}export{s as beginDataTransaction,f as endDataTransaction,l as scheduleDataWork};
2
+ //# sourceMappingURL=data-scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-scheduler.js","sources":["../src/data-scheduler.ts"],"sourcesContent":["const pendingFlushes = new Set<() => void>();\nlet draining = false;\nlet queued = false;\nlet transactionDepth = 0;\n\nfunction beginDataTransaction(): void {\n transactionDepth += 1;\n}\n\nfunction shouldQueuePendingDataWork(): boolean {\n return pendingFlushes.size > 0 && !queued && transactionDepth === 0;\n}\n\nfunction drainDataWork(): void {\n if (draining || transactionDepth > 0) return;\n\n draining = true;\n queued = false;\n\n try {\n while (pendingFlushes.size > 0) {\n const flushes = [...pendingFlushes];\n pendingFlushes.clear();\n\n for (const flush of flushes) flush();\n }\n } finally {\n draining = false;\n\n if (shouldQueuePendingDataWork()) {\n queued = true;\n queueMicrotask(drainDataWork);\n }\n }\n}\n\nfunction endDataTransaction(): void {\n transactionDepth -= 1;\n\n if (transactionDepth === 0) drainDataWork();\n}\n\nfunction requestDataDrain(): void {\n if (queued || transactionDepth > 0) return;\n\n queued = true;\n queueMicrotask(drainDataWork);\n}\n\nfunction scheduleDataWork(flush: () => void): void {\n pendingFlushes.add(flush);\n requestDataDrain();\n}\n\nexport { beginDataTransaction, endDataTransaction, scheduleDataWork };\n"],"names":["pendingFlushes","draining","queued","transactionDepth","beginDataTransaction","shouldQueuePendingDataWork","drainDataWork","flushes","flush","endDataTransaction","requestDataDrain","scheduleDataWork"],"mappings":"AAAA,MAAMA,EAAiB,IAAI,IAC3B,IAAIC,EAAW,GACXC,EAAS,GACTC,EAAmB,EAEvB,SAASC,GAAoB,CAC3BD,GAAoB,CACtB,CAEA,SAASE,GAA0B,CACjC,OAAOL,EAAe,KAAO,GAAK,CAACE,GAAUC,IAAqB,CACpE,CAEA,SAASG,GAAa,CACpB,GAAI,EAAAL,GAAYE,EAAmB,GAEnC,CAAAF,EAAW,GACXC,EAAS,GAET,GAAI,CACF,KAAOF,EAAe,KAAO,GAAG,CAC9B,MAAMO,EAAU,CAAC,GAAGP,CAAc,EAClCA,EAAe,MAAK,EAEpB,UAAWQ,KAASD,EAASC,EAAK,CACpC,CACF,SACEP,EAAW,GAEPI,EAA0B,IAC5BH,EAAS,GACT,eAAeI,CAAa,EAEhC,EACF,CAEA,SAASG,GAAkB,CACzBN,GAAoB,EAEhBA,IAAqB,GAAGG,EAAa,CAC3C,CAEA,SAASI,GAAgB,CACnBR,GAAUC,EAAmB,IAEjCD,EAAS,GACT,eAAeI,CAAa,EAC9B,CAEA,SAASK,EAAiBH,EAAiB,CACzCR,EAAe,IAAIQ,CAAK,EACxBE,EAAgB,CAClB"}
@@ -0,0 +1,56 @@
1
+ import type { DeclarationId } from './identity.js';
2
+ declare const cancellationBrand: unique symbol;
3
+ /**
4
+ * Cancellation is a property of the failure, not of a class tree: one subject error carries several codes and only
5
+ * some of them cancel. The marker is a brand so `isCancellation` stays a single check for every subject.
6
+ */
7
+ declare class CancellationError extends Error {
8
+ readonly [cancellationBrand] = true;
9
+ readonly name: string;
10
+ }
11
+ declare function markCancellation<Failure extends Error>(error: Failure): Failure;
12
+ declare function isCancellation(error: unknown): boolean;
13
+ type FeatureErrorCode = 'cleanup-failed' | 'not-ready' | 'quarantined' | 'retired';
14
+ type FeatureErrorDetails = {
15
+ readonly cause?: unknown;
16
+ readonly failure?: unknown;
17
+ readonly failures?: readonly unknown[];
18
+ readonly message?: string;
19
+ readonly retryCleanup?: () => Promise<void>;
20
+ };
21
+ /** One class per author-vocabulary subject (D69): the state that used to be a class name is now `code`. */
22
+ declare class FeatureError extends Error {
23
+ readonly declarationId: DeclarationId;
24
+ readonly cause: unknown;
25
+ readonly code: FeatureErrorCode;
26
+ readonly failure: unknown;
27
+ readonly failures: readonly unknown[];
28
+ readonly name = "FeatureError";
29
+ /** Present only when this quarantined cleanup has a recovery operation. */
30
+ readonly retryCleanup?: () => Promise<void>;
31
+ constructor(code: FeatureErrorCode, declarationId: DeclarationId, details?: FeatureErrorDetails);
32
+ }
33
+ /**
34
+ * Cancellation is a code, not a class: `cancelled` and `closed` mean nobody is waiting for this call any more,
35
+ * while `unavailable` and `publication-rejected` are answers a product has to show. Branding the whole subject made
36
+ * a weak port with no provider look like an abandoned call, so `useCommand` reported `cancelled` and a UI could not
37
+ * tell absence from cancellation (D138 refines D69 and D105).
38
+ */
39
+ declare class CallError extends Error {
40
+ readonly code: 'cancelled' | 'closed' | 'publication-rejected' | 'unavailable';
41
+ readonly name = "CallError";
42
+ readonly reason: unknown;
43
+ constructor(code: 'cancelled' | 'closed' | 'publication-rejected' | 'unavailable', message: string, reason?: unknown);
44
+ }
45
+ declare class ReadableError extends Error {
46
+ readonly code: 'closed';
47
+ readonly name = "ReadableError";
48
+ constructor(code: 'closed', message: string);
49
+ }
50
+ declare class DeclarationError extends Error {
51
+ readonly code: 'invalid-id';
52
+ readonly name = "DeclarationError";
53
+ constructor(code: 'invalid-id', message: string);
54
+ }
55
+ export { CallError, CancellationError, DeclarationError, FeatureError, isCancellation, markCancellation };
56
+ export { ReadableError };
package/dist/errors.js ADDED
@@ -0,0 +1,2 @@
1
+ const t=Symbol("opetope.cancellation");class u extends Error{[t]=!0;name="CancellationError"}function s(r){return Object.defineProperty(r,t,{value:!0}),r}function o(r){return r instanceof Error&&r[t]===!0}const i=Object.freeze([]);function c(r,e){return r==="retired"?`Feature ${e} instance is retired.`:r==="quarantined"?`Feature ${e} cleanup is quarantined.`:r==="cleanup-failed"?`Feature ${e} cleanup failed and was reported.`:`Feature ${e} is not ready.`}class l extends Error{declarationId;cause;code;failure;failures;name="FeatureError";retryCleanup;constructor(e,a,n={}){super(n.message??c(e,a)),this.declarationId=a,this.cause=n.cause,this.code=e,this.failure=n.failure,this.failures=n.failures??i,e==="quarantined"&&n.retryCleanup!==void 0&&(this.retryCleanup=n.retryCleanup),e==="retired"&&s(this)}}const f=new Set(["cancelled","closed"]);class d extends Error{code;name="CallError";reason;constructor(e,a,n){super(a),this.code=e,this.reason=n,f.has(e)&&s(this)}}class p extends Error{code;name="ReadableError";constructor(e,a){super(a),this.code=e}}class E extends Error{code;name="DeclarationError";constructor(e,a){super(a),this.code=e}}export{d as CallError,u as CancellationError,E as DeclarationError,l as FeatureError,p as ReadableError,o as isCancellation,s as markCancellation};
2
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sources":["../src/errors.ts"],"sourcesContent":["import type { DeclarationId } from './identity';\n\nconst cancellationBrand = Symbol('opetope.cancellation');\n\ntype CancellationMarker = { readonly [cancellationBrand]?: true };\n\n/**\n * Cancellation is a property of the failure, not of a class tree: one subject error carries several codes and only\n * some of them cancel. The marker is a brand so `isCancellation` stays a single check for every subject.\n */\nclass CancellationError extends Error {\n readonly [cancellationBrand] = true;\n\n override readonly name: string = 'CancellationError';\n}\n\nfunction markCancellation<Failure extends Error>(error: Failure): Failure {\n Object.defineProperty(error, cancellationBrand, { value: true });\n\n return error;\n}\n\nfunction isCancellation(error: unknown): boolean {\n return error instanceof Error && (error as CancellationMarker)[cancellationBrand] === true;\n}\n\ntype FeatureErrorCode = 'cleanup-failed' | 'not-ready' | 'quarantined' | 'retired';\n\ntype FeatureErrorDetails = {\n readonly cause?: unknown;\n readonly failure?: unknown;\n readonly failures?: readonly unknown[];\n readonly message?: string;\n readonly retryCleanup?: () => Promise<void>;\n};\n\nconst noFeatureFailures: readonly unknown[] = Object.freeze([]);\n\nfunction defaultFeatureMessage(code: FeatureErrorCode, declarationId: DeclarationId): string {\n if (code === 'retired') return `Feature ${declarationId} instance is retired.`;\n\n if (code === 'quarantined') return `Feature ${declarationId} cleanup is quarantined.`;\n\n if (code === 'cleanup-failed') return `Feature ${declarationId} cleanup failed and was reported.`;\n\n return `Feature ${declarationId} is not ready.`;\n}\n\n/** One class per author-vocabulary subject (D69): the state that used to be a class name is now `code`. */\nclass FeatureError extends Error {\n readonly cause: unknown;\n\n readonly code: FeatureErrorCode;\n\n readonly failure: unknown;\n\n readonly failures: readonly unknown[];\n\n override readonly name = 'FeatureError';\n\n /** Present only when this quarantined cleanup has a recovery operation. */\n readonly retryCleanup?: () => Promise<void>;\n\n constructor(\n code: FeatureErrorCode,\n readonly declarationId: DeclarationId,\n details: FeatureErrorDetails = {},\n ) {\n super(details.message ?? defaultFeatureMessage(code, declarationId));\n this.cause = details.cause;\n this.code = code;\n this.failure = details.failure;\n this.failures = details.failures ?? noFeatureFailures;\n\n if (code === 'quarantined' && details.retryCleanup !== undefined) this.retryCleanup = details.retryCleanup;\n\n if (code === 'retired') markCancellation(this);\n }\n}\n\nconst cancellingCallCodes: ReadonlySet<string> = new Set(['cancelled', 'closed']);\n\n/**\n * Cancellation is a code, not a class: `cancelled` and `closed` mean nobody is waiting for this call any more,\n * while `unavailable` and `publication-rejected` are answers a product has to show. Branding the whole subject made\n * a weak port with no provider look like an abandoned call, so `useCommand` reported `cancelled` and a UI could not\n * tell absence from cancellation (D138 refines D69 and D105).\n */\nclass CallError extends Error {\n override readonly name = 'CallError';\n\n readonly reason: unknown;\n\n constructor(\n readonly code: 'cancelled' | 'closed' | 'publication-rejected' | 'unavailable',\n message: string,\n reason?: unknown,\n ) {\n super(message);\n this.reason = reason;\n\n if (cancellingCallCodes.has(code)) markCancellation(this);\n }\n}\n\nclass ReadableError extends Error {\n override readonly name = 'ReadableError';\n\n constructor(\n readonly code: 'closed',\n message: string,\n ) {\n super(message);\n }\n}\n\nclass DeclarationError extends Error {\n override readonly name = 'DeclarationError';\n\n constructor(\n readonly code: 'invalid-id',\n message: string,\n ) {\n super(message);\n }\n}\n\nexport { CallError, CancellationError, DeclarationError, FeatureError, isCancellation, markCancellation };\nexport { ReadableError };\n"],"names":["cancellationBrand","CancellationError","markCancellation","error","isCancellation","noFeatureFailures","defaultFeatureMessage","code","declarationId","FeatureError","details","cancellingCallCodes","CallError","message","reason","ReadableError","DeclarationError"],"mappings":"AAEA,MAAMA,EAAoB,OAAO,sBAAsB,EAQvD,MAAMC,UAA0B,KAAK,CAC1B,CAACD,CAAiB,EAAI,GAEb,KAAe,mBAClC,CAED,SAASE,EAAwCC,EAAc,CAC7D,cAAO,eAAeA,EAAOH,EAAmB,CAAE,MAAO,GAAM,EAExDG,CACT,CAEA,SAASC,EAAeD,EAAc,CACpC,OAAOA,aAAiB,OAAUA,EAA6BH,CAAiB,IAAM,EACxF,CAYA,MAAMK,EAAwC,OAAO,OAAO,EAAE,EAE9D,SAASC,EAAsBC,EAAwBC,EAA4B,CACjF,OAAID,IAAS,UAAkB,WAAWC,CAAa,wBAEnDD,IAAS,cAAsB,WAAWC,CAAa,2BAEvDD,IAAS,iBAAyB,WAAWC,CAAa,oCAEvD,WAAWA,CAAa,gBACjC,CAGA,MAAMC,UAAqB,KAAK,CAgBnB,cAfF,MAEA,KAEA,QAEA,SAES,KAAO,eAGhB,aAET,YACEF,EACSC,EACTE,EAA+B,CAAA,EAAE,CAEjC,MAAMA,EAAQ,SAAWJ,EAAsBC,EAAMC,CAAa,CAAC,EAH1D,KAAA,cAAAA,EAIT,KAAK,MAAQE,EAAQ,MACrB,KAAK,KAAOH,EACZ,KAAK,QAAUG,EAAQ,QACvB,KAAK,SAAWA,EAAQ,UAAYL,EAEhCE,IAAS,eAAiBG,EAAQ,eAAiB,SAAW,KAAK,aAAeA,EAAQ,cAE1FH,IAAS,WAAWL,EAAiB,IAAI,CAC/C,CACD,CAED,MAAMS,EAA2C,IAAI,IAAI,CAAC,YAAa,QAAQ,CAAC,EAQhF,MAAMC,UAAkB,KAAK,CAMhB,KALO,KAAO,YAEhB,OAET,YACWL,EACTM,EACAC,EAAgB,CAEhB,MAAMD,CAAO,EAJJ,KAAA,KAAAN,EAKT,KAAK,OAASO,EAEVH,EAAoB,IAAIJ,CAAI,GAAGL,EAAiB,IAAI,CAC1D,CACD,CAED,MAAMa,UAAsB,KAAK,CAIpB,KAHO,KAAO,gBAEzB,YACWR,EACTM,EAAe,CAEf,MAAMA,CAAO,EAHJ,KAAA,KAAAN,CAIX,CACD,CAED,MAAMS,UAAyB,KAAK,CAIvB,KAHO,KAAO,mBAEzB,YACWT,EACTM,EAAe,CAEf,MAAMA,CAAO,EAHJ,KAAA,KAAAN,CAIX,CACD"}
@@ -0,0 +1,6 @@
1
+ declare const declarationIdBrand: unique symbol;
2
+ export type DeclarationId = string & {
3
+ readonly [declarationIdBrand]: true;
4
+ };
5
+ export declare function declarationId(value: string): DeclarationId;
6
+ export {};
@@ -0,0 +1,2 @@
1
+ import{DeclarationError as r}from"./errors.js";const i=/^[A-Za-z][A-Za-z0-9]*(?:[./:-][A-Za-z0-9]+)*$/;function n(t){if(t.length>160||!i.test(t))throw new r("invalid-id",`Invalid Opetope declaration id: ${JSON.stringify(t)}.`);return t}export{n as declarationId};
2
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sources":["../src/identity.ts"],"sourcesContent":["import { DeclarationError } from './errors';\n\nconst declarationIdPattern = /^[A-Za-z][A-Za-z0-9]*(?:[./:-][A-Za-z0-9]+)*$/;\n\ndeclare const declarationIdBrand: unique symbol;\n\nexport type DeclarationId = string & { readonly [declarationIdBrand]: true };\n\nexport function declarationId(value: string): DeclarationId {\n if (value.length > 160 || !declarationIdPattern.test(value)) {\n throw new DeclarationError('invalid-id', `Invalid Opetope declaration id: ${JSON.stringify(value)}.`);\n }\n\n return value as DeclarationId;\n}\n"],"names":["declarationIdPattern","declarationId","value","DeclarationError"],"mappings":"+CAEA,MAAMA,EAAuB,gDAMvB,SAAUC,EAAcC,EAAa,CACzC,GAAIA,EAAM,OAAS,KAAO,CAACF,EAAqB,KAAKE,CAAK,EACxD,MAAM,IAAIC,EAAiB,aAAc,mCAAmC,KAAK,UAAUD,CAAK,CAAC,GAAG,EAGtG,OAAOA,CACT"}
@@ -0,0 +1,17 @@
1
+ export type { Call } from './call-target.js';
2
+ export { definePipe, defineRegistry } from './contribution.js';
3
+ export type { Pipe, Registry } from './contribution.js';
4
+ export { CallError, CancellationError, DeclarationError, FeatureError, ReadableError } from './errors.js';
5
+ export { declarationId } from './identity.js';
6
+ export type { DeclarationId } from './identity.js';
7
+ export { defineModel } from './model.js';
8
+ export type { Model, ModelOf } from './model.js';
9
+ export type { ModelContext, OwnedState } from './model-context.js';
10
+ export { definePort } from './port.js';
11
+ export type { Port, PortRef } from './port.js';
12
+ export { computed, derive, externalReadable, fromOptional } from './reactive.js';
13
+ export type { Equality } from './reactive.js';
14
+ export { collection, selectByKey } from './reactive-collection.js';
15
+ export type { Collection, Lookup } from './reactive-collection.js';
16
+ export type { Readable, State } from './readable.js';
17
+ export type { LatestBackpressure, Resource, ResourceSnapshot, ScopedRetention } from './resource.js';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import{definePipe as o,defineRegistry as t}from"./contribution.js";import{CallError as l,CancellationError as f,DeclarationError as i,FeatureError as d,ReadableError as n}from"./errors.js";import{declarationId as m}from"./identity.js";import{defineModel as c}from"./model.js";import{definePort as y}from"./port.js";import{computed as b,derive as s,externalReadable as u,fromOptional as C}from"./reactive.js";import{collection as g,selectByKey as v}from"./reactive-collection.js";export{l as CallError,f as CancellationError,i as DeclarationError,d as FeatureError,n as ReadableError,g as collection,b as computed,m as declarationId,c as defineModel,o as definePipe,y as definePort,t as defineRegistry,s as derive,u as externalReadable,C as fromOptional,v as selectByKey};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,46 @@
1
+ export { abortWithReason, combineAbortSignals, compatibleAbortReason, throwIfSignalAborted } from './abort-compat.js';
2
+ export type { CombinedAbortSignal } from './abort-compat.js';
3
+ export type { AdapterMethodKey, AdapterMethodSignature, UniqueExposeKeys } from './adapter-method.js';
4
+ export { bindCallTargetToAttachment, createAttachmentReadinessController, mintAttachmentCallTarget, reportDetachedError, } from './attachment.js';
5
+ export type { AttachmentReadinessController, AttachmentReadinessDiagnostics, AttachmentReadinessPermit, } from './attachment.js';
6
+ export type { AttachmentReadinessRef, AttachmentReadinessSnapshot } from './attachment.js';
7
+ export { createAttachmentLifecycleController } from './attachment-lifecycle.js';
8
+ export type { AttachmentCleanupContext, AttachmentCleanupFailure, AttachmentCleanupOutcome, AttachmentDrainOutcome, AttachmentFailureRecord, AttachmentIntentOutcome, AttachmentLifecycleController, AttachmentLifecycleDiagnostics, AttachmentLifecycleIntent, AttachmentLifecycleOptions, AttachmentLifecycleSnapshot, AttachmentOpenContext, AttachmentOpenStep, AttachmentRetirement, AttachmentRetryOutcome, } from './attachment-lifecycle.js';
9
+ export { createCallController } from './call.js';
10
+ export type { CallController, CallControllerOptions } from './call.js';
11
+ export { mapCallTarget } from './call.js';
12
+ export type { CallDiagnostics, CallRunOptions } from './call.js';
13
+ export type { CallAuthority, CallAuthoritySource, CallAuthoritySourceEvent, CallAuthoritySourceSnapshot, } from './call-authority.js';
14
+ export type { CallExecutionFact, CallExecutionFacts, CallObservationSlot } from './call-execution-inspection.js';
15
+ export { createCallExecutor } from './call-executor-factory.js';
16
+ export { inheritCallExecutionMetadata, releaseCallExecutionMetadata } from './call-executor-support.js';
17
+ export type { CallExecutionContext, CallExecutor, CallExecutorOptions, CallStartOptions } from './call-executor-types.js';
18
+ export type { CallKey, CallScheduling } from './call-executor-types.js';
19
+ export type { CallInvocation, CallInvocationExecution, CallInvocationSnapshot, CallSettlement, } from './call-invocation.js';
20
+ export type { CallLaneRequest } from './call-lane.js';
21
+ export { defineCallLane } from './call-lane.js';
22
+ export type { CallLane } from './call-lane.js';
23
+ export { assertCallContextCurrent, inheritCallContextLifetime, invokeCallTarget, invokeCallTargetWithGuard, isCallTarget, retireCallContext, revokeCallTarget, settleGuardedResult, withCallTargetResultFinalizer, } from './call-target.js';
24
+ export { defineCallTarget } from './call-target.js';
25
+ export type { CallContext } from './call-target.js';
26
+ export { createContributionTarget, getContributionOwner, isContributionTarget, publishContributions, withdrawContributions, } from './contribution.js';
27
+ export type { ContributionPublication, ContributionPublicationInput } from './contribution.js';
28
+ export type { ContributionEntry, ContributionTarget, ContributionValue, KeyedContribution, PipeHandler, PipeRead, PipeReadContext, } from './contribution.js';
29
+ export { isCancellation, markCancellation } from './errors.js';
30
+ export { createLifetimeController } from './lifetime.js';
31
+ export type { LifetimeController, LifetimeLease } from './lifetime.js';
32
+ export type { ModelField, ModelReadable, ModelShape } from './model.js';
33
+ export { isModel } from './model.js';
34
+ export type { ModelIdentity } from './model.js';
35
+ export type { ModelLane } from './model-context.js';
36
+ export { createAggregateError, createErrorWithCause, hasOwn } from './platform-compat.js';
37
+ export { bindPortRef, isPort, isPortRefFor } from './port.js';
38
+ export type { PortIdentity } from './port.js';
39
+ export type { PortInput, PortOutput, PortRefOf } from './port.js';
40
+ export { isReadable } from './reactive.js';
41
+ export type { ComputedOptions, DeriveManyOptions, DeriveOptions, ExternalReadableSource, ReactiveRead, ReadableValue, ReadableValues, } from './reactive.js';
42
+ export type { CollectionOptions, CollectionSnapshot, ProjectedCollectionOptions } from './reactive-collection.js';
43
+ export { createState } from './readable.js';
44
+ export type { ReadableListener } from './readable.js';
45
+ export type { ObserverRetention, ResourceRequestKey, ResourceRetention, ResourceRetry, StreamBackpressure, } from './resource.js';
46
+ export type { Awaitable, ExactInput, ExactKeys, ExactType, ExactVoidResult, IsAny, NoExtraKeys } from './type-helpers.js';
@@ -0,0 +1,2 @@
1
+ import{abortWithReason as r,combineAbortSignals as o,compatibleAbortReason as a,throwIfSignalAborted as l}from"./abort-compat.js";import{bindCallTargetToAttachment as n,createAttachmentReadinessController as C,mintAttachmentCallTarget as m,reportDetachedError as f}from"./attachment.js";import{createAttachmentLifecycleController as s}from"./attachment-lifecycle.js";import{createCallController as p,mapCallTarget as g}from"./call.js";import{createCallExecutor as d}from"./call-executor-factory.js";import{inheritCallExecutionMetadata as u,releaseCallExecutionMetadata as T}from"./call-executor-support.js";import{defineCallLane as R}from"./call-lane.js";import{assertCallContextCurrent as E,defineCallTarget as k,inheritCallContextLifetime as L,invokeCallTarget as v,invokeCallTargetWithGuard as M,isCallTarget as P,retireCallContext as S,revokeCallTarget as W}from"./call-target.js";import{createContributionTarget as G,getContributionOwner as O,isContributionTarget as y,publishContributions as z,withdrawContributions as D}from"./contribution.js";import{isCancellation as j,markCancellation as q}from"./errors.js";import{createLifetimeController as H}from"./lifetime.js";import{isModel as K}from"./model.js";import{createAggregateError as Q,createErrorWithCause as U,hasOwn as V}from"./platform-compat.js";import{bindPortRef as Y,isPort as Z,isPortRefFor as _}from"./port.js";import{isReadable as ee}from"./reactive.js";import{createState as re}from"./readable.js";import{settleGuardedResult as ae,withCallTargetResultFinalizer as le}from"./call-target-result.js";export{r as abortWithReason,E as assertCallContextCurrent,n as bindCallTargetToAttachment,Y as bindPortRef,o as combineAbortSignals,a as compatibleAbortReason,Q as createAggregateError,s as createAttachmentLifecycleController,C as createAttachmentReadinessController,p as createCallController,d as createCallExecutor,G as createContributionTarget,U as createErrorWithCause,H as createLifetimeController,re as createState,R as defineCallLane,k as defineCallTarget,O as getContributionOwner,V as hasOwn,L as inheritCallContextLifetime,u as inheritCallExecutionMetadata,v as invokeCallTarget,M as invokeCallTargetWithGuard,P as isCallTarget,j as isCancellation,y as isContributionTarget,K as isModel,Z as isPort,_ as isPortRefFor,ee as isReadable,g as mapCallTarget,q as markCancellation,m as mintAttachmentCallTarget,z as publishContributions,T as releaseCallExecutionMetadata,f as reportDetachedError,S as retireCallContext,W as revokeCallTarget,ae as settleGuardedResult,l as throwIfSignalAborted,le as withCallTargetResultFinalizer,D as withdrawContributions};
2
+ //# sourceMappingURL=internal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,19 @@
1
+ import type { DeclarationId } from './identity.js';
2
+ declare const lifetimeLeaseBrand: unique symbol;
3
+ interface LifetimeLease {
4
+ assertCurrent(): void;
5
+ isCurrent(): boolean;
6
+ readonly [lifetimeLeaseBrand]: true;
7
+ readonly ownerId: DeclarationId;
8
+ readonly signal: AbortSignal;
9
+ }
10
+ interface LifetimeController {
11
+ close(): void;
12
+ current(): LifetimeLease | undefined;
13
+ open(): LifetimeLease;
14
+ }
15
+ declare function createLifetimeController(options: {
16
+ readonly id: string;
17
+ }): LifetimeController;
18
+ export { createLifetimeController };
19
+ export type { LifetimeController, LifetimeLease };
@@ -0,0 +1,2 @@
1
+ import{abortWithReason as u}from"./abort-compat.js";import{FeatureError as a}from"./errors.js";import{declarationId as d}from"./identity.js";const f=Symbol("opetope.lifetime-lease");function l(s){const t=d(s.id);let r;const c=e=>Object.freeze({assertCurrent(){if(!e.active||r!==e)throw new a("retired",t)},isCurrent(){return e.active&&r===e},[f]:!0,ownerId:t,signal:e.abort.signal}),o=e=>{e.active=!1,e.lease=void 0,u(e.abort,new a("retired",t))};return Object.freeze({close:()=>{const e=r;e!==void 0&&(r=void 0,o(e))},current(){const e=r;if(e!==void 0)return e.lease},open(){const e=r,n={abort:new AbortController,active:!0,lease:void 0},i=c(n);return n.lease=i,r=n,e!==void 0&&o(e),i}})}export{l as createLifetimeController};
2
+ //# sourceMappingURL=lifetime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifetime.js","sources":["../src/lifetime.ts"],"sourcesContent":["import { abortWithReason } from './abort-compat';\nimport { FeatureError } from './errors';\nimport { declarationId } from './identity';\nimport type { DeclarationId } from './identity';\n\nconst lifetimeLeaseBrand = Symbol('opetope.lifetime-lease');\n\ninterface LifetimeLease {\n assertCurrent(): void;\n isCurrent(): boolean;\n readonly [lifetimeLeaseBrand]: true;\n readonly ownerId: DeclarationId;\n readonly signal: AbortSignal;\n}\n\ninterface LifetimeController {\n close(): void;\n current(): LifetimeLease | undefined;\n open(): LifetimeLease;\n}\n\ntype LeaseState = {\n readonly abort: AbortController;\n active: boolean;\n lease: LifetimeLease | undefined;\n};\n\nfunction createLifetimeController(options: { readonly id: string }): LifetimeController {\n const ownerId = declarationId(options.id);\n let currentState: LeaseState | undefined;\n\n const makeLease = (state: LeaseState): LifetimeLease =>\n Object.freeze({\n assertCurrent(): void {\n if (!state.active || currentState !== state) throw new FeatureError('retired', ownerId);\n },\n isCurrent(): boolean {\n return state.active && currentState === state;\n },\n [lifetimeLeaseBrand]: true as const,\n ownerId,\n signal: state.abort.signal,\n });\n\n const retire = (state: LeaseState): void => {\n state.active = false;\n state.lease = undefined;\n abortWithReason(state.abort, new FeatureError('retired', ownerId));\n };\n\n const close = (): void => {\n const state = currentState;\n\n if (state === undefined) return;\n\n currentState = undefined;\n retire(state);\n };\n\n return Object.freeze({\n close,\n current(): LifetimeLease | undefined {\n const state = currentState;\n\n if (state === undefined) return undefined;\n\n return state.lease;\n },\n open(): LifetimeLease {\n const previous = currentState;\n const state: LeaseState = {\n abort: new AbortController(),\n active: true,\n lease: undefined,\n };\n const lease = makeLease(state);\n state.lease = lease;\n currentState = state;\n\n if (previous !== undefined) retire(previous);\n\n return lease;\n },\n });\n}\n\nexport { createLifetimeController };\nexport type { LifetimeController, LifetimeLease };\n"],"names":["lifetimeLeaseBrand","createLifetimeController","options","ownerId","declarationId","currentState","makeLease","state","FeatureError","retire","abortWithReason","previous","lease"],"mappings":"6IAKA,MAAMA,EAAqB,OAAO,wBAAwB,EAsB1D,SAASC,EAAyBC,EAAgC,CAChE,MAAMC,EAAUC,EAAcF,EAAQ,EAAE,EACxC,IAAIG,EAEJ,MAAMC,EAAaC,GACjB,OAAO,OAAO,CACZ,eAAa,CACX,GAAI,CAACA,EAAM,QAAUF,IAAiBE,EAAO,MAAM,IAAIC,EAAa,UAAWL,CAAO,CACxF,EACA,WAAS,CACP,OAAOI,EAAM,QAAUF,IAAiBE,CAC1C,EACA,CAACP,CAAkB,EAAG,GACtB,QAAAG,EACA,OAAQI,EAAM,MAAM,MACrB,CAAA,EAEGE,EAAUF,GAA2B,CACzCA,EAAM,OAAS,GACfA,EAAM,MAAQ,OACdG,EAAgBH,EAAM,MAAO,IAAIC,EAAa,UAAWL,CAAO,CAAC,CACnE,EAWA,OAAO,OAAO,OAAO,CACnB,MAVY,IAAW,CACvB,MAAMI,EAAQF,EAEVE,IAAU,SAEdF,EAAe,OACfI,EAAOF,CAAK,EACd,EAIE,SAAO,CACL,MAAMA,EAAQF,EAEd,GAAIE,IAAU,OAEd,OAAOA,EAAM,KACf,EACA,MAAI,CACF,MAAMI,EAAWN,EACXE,EAAoB,CACxB,MAAO,IAAI,gBACX,OAAQ,GACR,MAAO,QAEHK,EAAQN,EAAUC,CAAK,EAC7B,OAAAA,EAAM,MAAQK,EACdP,EAAeE,EAEXI,IAAa,QAAWF,EAAOE,CAAQ,EAEpCC,CACT,CACD,CAAA,CACH"}