@nestos/kanban 0.0.0-stage → 0.5.2

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 (124) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +165 -2
  3. package/dist/activity-BbC4sgKL.d.cts +258 -0
  4. package/dist/activity-Czlan5xW.d.ts +258 -0
  5. package/dist/activity-module-DaO1Rry0.d.cts +22 -0
  6. package/dist/activity-module-T22wliZu.d.ts +22 -0
  7. package/dist/activity-ports-Dqxa86zT.d.cts +12 -0
  8. package/dist/activity-ports-cxfrelaL.d.ts +12 -0
  9. package/dist/activity.cjs +16 -0
  10. package/dist/activity.cjs.map +1 -0
  11. package/dist/activity.d.cts +12 -0
  12. package/dist/activity.d.ts +12 -0
  13. package/dist/activity.js +16 -0
  14. package/dist/activity.js.map +1 -0
  15. package/dist/chunk-3JYLJPE5.js +95 -0
  16. package/dist/chunk-3JYLJPE5.js.map +1 -0
  17. package/dist/chunk-3LEIHB7Y.js +221 -0
  18. package/dist/chunk-3LEIHB7Y.js.map +1 -0
  19. package/dist/chunk-4XF6LQSU.cjs +9 -0
  20. package/dist/chunk-4XF6LQSU.cjs.map +1 -0
  21. package/dist/chunk-5Q7WB63A.cjs +282 -0
  22. package/dist/chunk-5Q7WB63A.cjs.map +1 -0
  23. package/dist/chunk-7KDOTBRX.cjs +58 -0
  24. package/dist/chunk-7KDOTBRX.cjs.map +1 -0
  25. package/dist/chunk-AHHEGTUA.js +148 -0
  26. package/dist/chunk-AHHEGTUA.js.map +1 -0
  27. package/dist/chunk-AOHQCX7K.cjs +95 -0
  28. package/dist/chunk-AOHQCX7K.cjs.map +1 -0
  29. package/dist/chunk-DINLR6E6.cjs +443 -0
  30. package/dist/chunk-DINLR6E6.cjs.map +1 -0
  31. package/dist/chunk-EYTVC2H5.cjs +1612 -0
  32. package/dist/chunk-EYTVC2H5.cjs.map +1 -0
  33. package/dist/chunk-FEE2YBZ7.js +1972 -0
  34. package/dist/chunk-FEE2YBZ7.js.map +1 -0
  35. package/dist/chunk-FNO5K3A7.js +282 -0
  36. package/dist/chunk-FNO5K3A7.js.map +1 -0
  37. package/dist/chunk-IHHDXZPZ.js +382 -0
  38. package/dist/chunk-IHHDXZPZ.js.map +1 -0
  39. package/dist/chunk-J3EAIVA3.js +280 -0
  40. package/dist/chunk-J3EAIVA3.js.map +1 -0
  41. package/dist/chunk-KH3WF3K4.js +443 -0
  42. package/dist/chunk-KH3WF3K4.js.map +1 -0
  43. package/dist/chunk-M35X5PHO.js +196 -0
  44. package/dist/chunk-M35X5PHO.js.map +1 -0
  45. package/dist/chunk-QRGZD2JU.cjs +1972 -0
  46. package/dist/chunk-QRGZD2JU.cjs.map +1 -0
  47. package/dist/chunk-RBOER6EW.cjs +221 -0
  48. package/dist/chunk-RBOER6EW.cjs.map +1 -0
  49. package/dist/chunk-RQP77ZTY.js +9 -0
  50. package/dist/chunk-RQP77ZTY.js.map +1 -0
  51. package/dist/chunk-RSH6QSEC.cjs +196 -0
  52. package/dist/chunk-RSH6QSEC.cjs.map +1 -0
  53. package/dist/chunk-VMRDXOLX.cjs +148 -0
  54. package/dist/chunk-VMRDXOLX.cjs.map +1 -0
  55. package/dist/chunk-X4UHZX6F.js +58 -0
  56. package/dist/chunk-X4UHZX6F.js.map +1 -0
  57. package/dist/chunk-XBSGUVCY.cjs +382 -0
  58. package/dist/chunk-XBSGUVCY.cjs.map +1 -0
  59. package/dist/chunk-XKDZHAUE.js +1612 -0
  60. package/dist/chunk-XKDZHAUE.js.map +1 -0
  61. package/dist/chunk-ZUZGAO6G.cjs +280 -0
  62. package/dist/chunk-ZUZGAO6G.cjs.map +1 -0
  63. package/dist/content-C5aJZFlw.d.cts +35 -0
  64. package/dist/content-hLH5BNud.d.ts +35 -0
  65. package/dist/contracts/index.cjs +31 -0
  66. package/dist/contracts/index.cjs.map +1 -0
  67. package/dist/contracts/index.d.cts +5 -0
  68. package/dist/contracts/index.d.ts +5 -0
  69. package/dist/contracts/index.js +31 -0
  70. package/dist/contracts/index.js.map +1 -0
  71. package/dist/discovery-9E-LfwIw.d.cts +61 -0
  72. package/dist/discovery-D60yrjej.d.ts +61 -0
  73. package/dist/discovery.cjs +377 -0
  74. package/dist/discovery.cjs.map +1 -0
  75. package/dist/discovery.d.cts +22 -0
  76. package/dist/discovery.d.ts +22 -0
  77. package/dist/discovery.js +377 -0
  78. package/dist/discovery.js.map +1 -0
  79. package/dist/execution-bridge-fpCnH6z5.d.cts +52 -0
  80. package/dist/execution-bridge-rk0uVy_n.d.ts +52 -0
  81. package/dist/execution.cjs +18 -0
  82. package/dist/execution.cjs.map +1 -0
  83. package/dist/execution.d.cts +42 -0
  84. package/dist/execution.d.ts +42 -0
  85. package/dist/execution.js +18 -0
  86. package/dist/execution.js.map +1 -0
  87. package/dist/index.cjs +74 -0
  88. package/dist/index.cjs.map +1 -0
  89. package/dist/index.d.cts +54 -0
  90. package/dist/index.d.ts +54 -0
  91. package/dist/index.js +74 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/module-CYfE7YhM.d.ts +11 -0
  94. package/dist/module-Ca9L3qCn.d.cts +11 -0
  95. package/dist/planning-CHrOqQ6b.d.cts +879 -0
  96. package/dist/planning-CHrOqQ6b.d.ts +879 -0
  97. package/dist/ports-B0McVg7B.d.ts +143 -0
  98. package/dist/ports-BeGO1VRI.d.cts +49 -0
  99. package/dist/ports-GQy0x-RI.d.cts +143 -0
  100. package/dist/ports-ykGiNUSx.d.ts +49 -0
  101. package/dist/runtime/index.cjs +205 -0
  102. package/dist/runtime/index.cjs.map +1 -0
  103. package/dist/runtime/index.d.cts +75 -0
  104. package/dist/runtime/index.d.ts +75 -0
  105. package/dist/runtime/index.js +205 -0
  106. package/dist/runtime/index.js.map +1 -0
  107. package/dist/runtime/integration.cjs +423 -0
  108. package/dist/runtime/integration.cjs.map +1 -0
  109. package/dist/runtime/integration.d.cts +658 -0
  110. package/dist/runtime/integration.d.ts +658 -0
  111. package/dist/runtime/integration.js +423 -0
  112. package/dist/runtime/integration.js.map +1 -0
  113. package/dist/storage.cjs +21 -0
  114. package/dist/storage.cjs.map +1 -0
  115. package/dist/storage.d.cts +71 -0
  116. package/dist/storage.d.ts +71 -0
  117. package/dist/storage.js +21 -0
  118. package/dist/storage.js.map +1 -0
  119. package/dist/store-DPwHzGiz.d.cts +28 -0
  120. package/dist/store-DgdrPCV9.d.ts +28 -0
  121. package/dist/task-projection-CSorlMws.d.ts +11 -0
  122. package/dist/task-projection-DKKB_ueb.d.cts +11 -0
  123. package/examples/standalone.ts +22 -0
  124. package/package.json +143 -4
@@ -0,0 +1,282 @@
1
+ import {
2
+ createKanbanModule
3
+ } from "./chunk-KH3WF3K4.js";
4
+ import {
5
+ candidates,
6
+ principal,
7
+ principalKey
8
+ } from "./chunk-3LEIHB7Y.js";
9
+ import {
10
+ KanbanError,
11
+ randomUUID,
12
+ sha256HexSync
13
+ } from "./chunk-M35X5PHO.js";
14
+
15
+ // src/session-activity-query.ts
16
+ async function querySessionItems(core, boardId, input, project) {
17
+ const { execution = "all", ...query } = input;
18
+ if (!["all", "running", "recent", "active"].includes(execution)) throw new KanbanError("INVALID_EXECUTION_FILTER");
19
+ if (execution === "all") {
20
+ const page2 = await core.query(boardId, query);
21
+ return { ...page2, items: await project(page2.items) };
22
+ }
23
+ const { cursor, limit = 50, ...filters } = query;
24
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > 200) throw new KanbanError("INVALID_PAGE_SIZE");
25
+ let page = await core.query(boardId, { ...filters, limit: 200 }), rows = [...page.items];
26
+ const revision = page.revision;
27
+ while (page.nextCursor) {
28
+ if (rows.length >= 1e4) throw new KanbanError("ACTIVITY_QUERY_LIMIT");
29
+ page = await core.query(boardId, { ...filters, limit: 200, cursor: page.nextCursor });
30
+ if (page.revision !== revision) throw new KanbanError("CURSOR_STALE");
31
+ rows.push(...page.items);
32
+ }
33
+ const matching = (await project(rows)).filter((row) => matchesExecution(row, execution));
34
+ if ((await core.query(boardId, { ...filters, limit: 1 })).revision !== revision) throw new KanbanError("CURSOR_STALE");
35
+ const scope = sha256HexSync(JSON.stringify({ filters, execution, limit })), signature = sha256HexSync(JSON.stringify(matching.map((row) => row.id)));
36
+ let start = 0;
37
+ if (cursor) {
38
+ let saved;
39
+ try {
40
+ saved = JSON.parse(cursor);
41
+ } catch {
42
+ throw new KanbanError("INVALID_CURSOR");
43
+ }
44
+ if (saved.scope !== scope) throw new KanbanError("CURSOR_SCOPE_MISMATCH");
45
+ if (saved.revision !== revision || saved.signature !== signature) throw new KanbanError("CURSOR_STALE");
46
+ start = matching.findIndex((row) => row.id === saved.after) + 1;
47
+ if (start < 1) throw new KanbanError("INVALID_CURSOR");
48
+ }
49
+ const items = matching.slice(start, start + limit);
50
+ return { revision, total: matching.length, items, ...start + items.length < matching.length ? { nextCursor: JSON.stringify({ scope, revision, signature, after: items.at(-1).id }) } : {} };
51
+ }
52
+ function matchesExecution(row, filter) {
53
+ const session = row.execution;
54
+ if (!session || session.stale) return false;
55
+ return filter === "running" ? session.state === "running" : filter === "recent" ? session.recent : session.state === "running" || session.recent;
56
+ }
57
+
58
+ // src/session-relations.ts
59
+ function sessionAssociations(item, bindings, workflow) {
60
+ return [...bindings.map((ref) => {
61
+ const dispatch = workflow.dispatches[ref.revision ?? ""];
62
+ const execution = dispatch?.itemId === item.id ? {
63
+ id: dispatch.id,
64
+ purpose: "execution",
65
+ dispatchId: dispatch.id,
66
+ attempt: dispatch.attempt,
67
+ outcome: dispatch.outcome,
68
+ createdAt: dispatch.preparedAt,
69
+ code: dispatch.code,
70
+ ...dispatch.continuation ? { previousDispatchId: dispatch.continuation.previousDispatchId } : {}
71
+ } : void 0;
72
+ return { sessionId: ref.id, purpose: "execution", ...execution ? { execution } : {} };
73
+ }), ...item.participants.filter((row) => !bindings.some((ref) => ref.id === row.sessionId)).map((row) => ({ sessionId: row.sessionId, purpose: "discussion" }))];
74
+ }
75
+ async function projectRelatedSessions(associations, currentId, inspect) {
76
+ const grouped = /* @__PURE__ */ new Map();
77
+ for (const row of associations) {
78
+ let group = grouped.get(row.sessionId);
79
+ if (!group) grouped.set(row.sessionId, group = { purposes: /* @__PURE__ */ new Set(), executions: /* @__PURE__ */ new Map() });
80
+ group.purposes.add(row.purpose);
81
+ if (row.execution) group.executions.set(row.execution.id, { ...group.executions.get(row.execution.id), ...row.execution });
82
+ }
83
+ const rows = [], entries = [...grouped];
84
+ let next = 0;
85
+ await Promise.all(Array.from({ length: Math.min(entries.length, 8) }, async () => {
86
+ while (next < entries.length) {
87
+ const [sessionId, group] = entries[next++], state = await inspect(sessionId);
88
+ rows.push({
89
+ ...state,
90
+ current: sessionId === currentId,
91
+ purposes: [...group.purposes].sort(),
92
+ executions: [...group.executions.values()].sort((a, b) => (a.createdAt ?? 0) - (b.createdAt ?? 0) || a.id.localeCompare(b.id))
93
+ });
94
+ }
95
+ }));
96
+ return rows.sort((a, b) => Number(b.current) - Number(a.current) || a.sessionId.localeCompare(b.sessionId));
97
+ }
98
+
99
+ // src/session-activity.ts
100
+ function executionBindings(refs, referenceSystem) {
101
+ return (refs ?? []).filter((ref) => ref.system === referenceSystem);
102
+ }
103
+ function currentBinding(refs, itemId, workflow, referenceSystem) {
104
+ const binding = executionBindings(refs, referenceSystem).at(-1), latest = Object.values(workflow.dispatches).filter((dispatch) => dispatch.itemId === itemId).sort((a, b) => b.attempt - a.attempt)[0];
105
+ if (!binding || !latest || binding.revision === latest.id) return binding;
106
+ return ["prepared", "running", "unknown", "succeeded"].includes(latest.outcome) || workflow.dispatches[binding.revision ?? ""] ? void 0 : binding;
107
+ }
108
+ function createKanbanSessionActivity(core, options, log, referenceSystem) {
109
+ const inspect = createSessionInspector(options, log);
110
+ const project = async (boardId, rows) => {
111
+ if (!rows.some((row) => executionBindings(row.externalRefs, referenceSystem).length)) return rows;
112
+ const [board, workflow] = await Promise.all([boardMetadata(core, boardId), core.workflow(boardId)]), bindings = new Map(rows.map((row) => [row.id, currentBinding(row.externalRefs, row.id, workflow, referenceSystem)]));
113
+ const ids = [...new Set([...bindings.values()].flatMap((ref) => ref ? [ref.id] : []))], states = /* @__PURE__ */ new Map();
114
+ let next = 0;
115
+ await Promise.all(Array.from({ length: Math.min(ids.length, 8) }, async () => {
116
+ while (next < ids.length) {
117
+ const id = ids[next++];
118
+ states.set(id, await inspect(board, id));
119
+ }
120
+ }));
121
+ return rows.map((row) => {
122
+ const binding = bindings.get(row.id);
123
+ return { ...row, ...binding ? { execution: states.get(binding.id) } : {} };
124
+ });
125
+ };
126
+ return {
127
+ queryActivity: (boardId, query = {}) => querySessionItems(core, boardId, query, (rows) => project(boardId, rows)),
128
+ async executionSession(boardId, itemId, query = {}) {
129
+ const started = Date.now();
130
+ if (!query || typeof query !== "object" || Array.isArray(query) || Object.keys(query).some((key) => key !== "includeRelated") || query.includeRelated !== void 0 && typeof query.includeRelated !== "boolean") throw new KanbanError("INVALID_QUERY");
131
+ const { item } = await core.item(boardId, itemId), bindings = executionBindings(item.externalRefs, referenceSystem);
132
+ if (!bindings.length && !query.includeRelated) return { history: [] };
133
+ const [board, workflow] = await Promise.all([boardMetadata(core, boardId), core.workflow(boardId)]), last = currentBinding(item.externalRefs, itemId, workflow, referenceSystem);
134
+ let sessionsCode;
135
+ const related = query.includeRelated ? await options?.source.related?.(board, item, workflow).catch((error) => {
136
+ const candidate = error?.code;
137
+ sessionsCode = candidate && /^[A-Z][A-Z0-9_]{0,79}$/.test(candidate) ? candidate : "SESSION_RELATIONS_FAILED";
138
+ try {
139
+ log({ operationId: randomUUID(), stage: "session.relations", result: "failure", code: sessionsCode, durationMs: Date.now() - started });
140
+ } catch {
141
+ }
142
+ return [];
143
+ }) : [];
144
+ const sessions = query.includeRelated ? await projectRelatedSessions([
145
+ ...sessionAssociations(item, bindings, workflow),
146
+ ...related ?? []
147
+ ], last?.id, (id) => inspect(board, id)) : void 0;
148
+ return {
149
+ current: last ? sessions?.find((row) => row.sessionId === last.id) ?? await inspect(board, last.id) : void 0,
150
+ history: bindings.map((ref) => ({ sessionId: ref.id, bindingId: ref.revision, current: ref === last })).reverse(),
151
+ ...sessions ? { sessions } : {},
152
+ ...sessionsCode ? { sessionsCode } : {}
153
+ };
154
+ },
155
+ validate: (board, command, actor) => validateBinding(core, options, board, command, actor, referenceSystem)
156
+ };
157
+ }
158
+ function createSessionInspector(options, log) {
159
+ const now = options?.now ?? Date.now, cache = /* @__PURE__ */ new Map();
160
+ return async (board, sessionId) => {
161
+ const started = now(), key = JSON.stringify([board.id, sessionId]);
162
+ try {
163
+ if (!options) throw new KanbanError("EXECUTION_SESSION_UNAVAILABLE");
164
+ const row = await options.source.inspect(board, sessionId);
165
+ if (row.sessionId !== sessionId) throw new KanbanError("SESSION_ACTIVITY_SCOPE_MISMATCH");
166
+ const stale = row.stale || row.observedAt === void 0 || now() - row.observedAt > 9e4;
167
+ const value = {
168
+ sessionId,
169
+ ...typeof row.resourceId === "string" && row.resourceId.length <= 4096 && !row.resourceId.includes("\0") ? { resourceId: row.resourceId } : {},
170
+ title: row.title?.slice(0, 256),
171
+ state: row.state,
172
+ createdAt: row.createdAt,
173
+ lastActivityAt: row.lastActivityAt,
174
+ observedAt: row.observedAt,
175
+ stale,
176
+ recent: !stale && row.lastActivityAt !== void 0 && row.lastActivityAt <= now() && now() - row.lastActivityAt <= 6e5,
177
+ ...row.code && /^[A-Z][A-Z0-9_]{0,79}$/.test(row.code) ? { code: row.code } : {}
178
+ };
179
+ if (cache.size >= 256) cache.delete(cache.keys().next().value);
180
+ cache.set(key, value);
181
+ return value;
182
+ } catch (error) {
183
+ const candidate = error?.code, code = candidate && /^[A-Z][A-Z0-9_]{0,79}$/.test(candidate) ? candidate : "SESSION_ACTIVITY_FAILED";
184
+ try {
185
+ log({ operationId: randomUUID(), sessionId, stage: "session.activity", result: "failure", code, durationMs: now() - started });
186
+ } catch {
187
+ }
188
+ const prior = ["EIO", "ETIMEDOUT", "ECONNRESET", "ECONNREFUSED", "SESSION_ACTIVITY_FAILED"].includes(code) ? cache.get(key) : void 0;
189
+ return { ...prior, sessionId, state: prior?.state ?? "unknown", stale: true, recent: false, code };
190
+ }
191
+ };
192
+ }
193
+ async function boardMetadata(core, boardId) {
194
+ const board = (await core.catalog()).find((board2) => board2.id === boardId) ?? await core.read(boardId);
195
+ return { ...board, items: [] };
196
+ }
197
+ async function validateBinding(core, options, board, command, actor, referenceSystem) {
198
+ if (command?.type !== "link-reference" || command.reference.system !== referenceSystem) return;
199
+ if (!options || !command.reference.revision) throw new KanbanError("EXECUTION_SESSION_UNAVAILABLE");
200
+ const item = board.items.find((item2) => item2.id === command.itemId);
201
+ if (!item) throw new KanbanError("ITEM_NOT_FOUND");
202
+ const key = principalKey(principal(actor)), coordinator = board.members?.some((member) => principalKey(member) === key && member.roles?.includes("coordinator"));
203
+ if (!coordinator && item.owner?.principalId !== key && !(item.owner === void 0 && candidates(board, item).some((candidate) => principalKey(candidate) === key))) throw new KanbanError("EXECUTION_SESSION_ASSIGNMENT_DENIED");
204
+ const active = Object.values((await core.workflow(board.id)).dispatches).filter((dispatch) => dispatch.itemId === item.id).sort((a, b) => b.attempt - a.attempt)[0];
205
+ if (active && command.reference.revision !== active.id) throw new KanbanError("EXECUTION_SESSION_LOCKED");
206
+ if (active && executionBindings(item.externalRefs, referenceSystem).some((ref) => ref.revision === active.id && ref.id !== command.reference.id)) throw new KanbanError("EXECUTION_SESSION_LOCKED");
207
+ if (options.source.authorizeBinding) return options.source.authorizeBinding(board, command.reference.id);
208
+ const state = await options.source.inspect(board, command.reference.id);
209
+ if (state.sessionId !== command.reference.id || state.stale) throw new KanbanError("EXECUTION_SESSION_UNAVAILABLE");
210
+ }
211
+
212
+ // src/activity-module.ts
213
+ function createKanbanActivityModule(options) {
214
+ const { executionSessions, referenceSystem, ...core } = options;
215
+ let activity;
216
+ const service = createKanbanModule({
217
+ ...core,
218
+ authorize: async (input) => {
219
+ await core.authorize(input);
220
+ await activity.validate(input.board, input.command, input.actor);
221
+ }
222
+ });
223
+ activity = createKanbanSessionActivity(service, executionSessions, core.log, referenceSystem);
224
+ let stopping = false;
225
+ const pending = /* @__PURE__ */ new Set();
226
+ const track = (run) => {
227
+ if (stopping) return Promise.reject(new KanbanError("SERVICE_STOPPING"));
228
+ const work = Promise.resolve().then(run).finally(() => {
229
+ pending.delete(work);
230
+ });
231
+ pending.add(work);
232
+ return work;
233
+ };
234
+ const quiesce = async () => {
235
+ stopping = true;
236
+ await Promise.allSettled([...pending]);
237
+ await service.quiesce();
238
+ };
239
+ return {
240
+ ...forwardCore(service, track),
241
+ queryActivity: (...args) => track(() => activity.queryActivity(...args)),
242
+ executionSession: (...args) => track(() => activity.executionSession(...args)),
243
+ related: (sessionId) => track(() => relatedBoards(service, referenceSystem, sessionId)),
244
+ quiesce,
245
+ dispose: quiesce
246
+ };
247
+ }
248
+ function forwardCore(service, track) {
249
+ return {
250
+ create: (input) => track(() => service.create(input)),
251
+ read: (id) => track(() => service.read(id)),
252
+ list: () => track(() => service.list()),
253
+ catalog: () => track(() => service.catalog()),
254
+ execute: (id, request) => track(() => service.execute(id, request)),
255
+ events: (id, after, limit) => track(() => service.events(id, after, limit)),
256
+ query: (id, query) => track(() => service.query(id, query)),
257
+ item: (id, item) => track(() => service.item(id, item)),
258
+ overview: (id) => track(() => service.overview(id)),
259
+ scan: () => track(() => service.scan()),
260
+ workflow: (id) => track(() => service.workflow(id)),
261
+ resolve: (reference) => track(() => service.resolve(reference)),
262
+ suggest: (id, query) => track(() => service.suggest(id, query)),
263
+ records: (id, query) => track(() => service.records(id, query)),
264
+ record: (id, record) => track(() => service.record(id, record)),
265
+ currentPrincipal: () => track(() => service.currentPrincipal())
266
+ };
267
+ }
268
+ async function relatedBoards(service, referenceSystem, sessionId) {
269
+ const boards = await service.related(sessionId);
270
+ for (const board of await service.catalog()) {
271
+ if (boards.some((old) => old.id === board.id)) continue;
272
+ const matches = await service.query(board.id, { view: "all", reference: { system: referenceSystem, id: sessionId }, limit: 1 });
273
+ if (matches.total) boards.push(await service.read(board.id));
274
+ }
275
+ return boards;
276
+ }
277
+
278
+ export {
279
+ querySessionItems,
280
+ createKanbanActivityModule
281
+ };
282
+ //# sourceMappingURL=chunk-FNO5K3A7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/session-activity-query.ts","../src/session-relations.ts","../src/session-activity.ts","../src/activity-module.ts"],"sourcesContent":["import {sha256HexSync} from './utils';\nimport type {KanbanService, KanbanSessionActivityQuery, KanbanSessionItemPage, KanbanSessionItemSummary} from './contracts';\nimport {KanbanError} from './ports';\n/** Filter before pagination; Session activity changes invalidate the filtered keyset. */\nexport async function querySessionItems(core: KanbanService, boardId: string, input: KanbanSessionActivityQuery,\n project: (rows: KanbanSessionItemSummary[])=>Promise<KanbanSessionItemSummary[]>): Promise<KanbanSessionItemPage> {\n const {execution='all',...query}=input;\n if(!['all','running','recent','active'].includes(execution)) throw new KanbanError('INVALID_EXECUTION_FILTER');\n if(execution==='all') {const page=await core.query(boardId,query);return {...page,items:await project(page.items)};}\n const {cursor,limit=50,...filters}=query;\n if(!Number.isSafeInteger(limit)||limit<1||limit>200) throw new KanbanError('INVALID_PAGE_SIZE');\n let page=await core.query(boardId,{...filters,limit:200}), rows=[...page.items];const revision=page.revision;\n while(page.nextCursor) {\n if(rows.length>=10000) throw new KanbanError('ACTIVITY_QUERY_LIMIT');\n page=await core.query(boardId,{...filters,limit:200,cursor:page.nextCursor});\n if(page.revision!==revision) throw new KanbanError('CURSOR_STALE');rows.push(...page.items);\n }\n const matching=(await project(rows)).filter(row=>matchesExecution(row,execution));\n if((await core.query(boardId,{...filters,limit:1})).revision!==revision) throw new KanbanError('CURSOR_STALE');\n const scope=sha256HexSync(JSON.stringify({filters,execution,limit})), signature=sha256HexSync(JSON.stringify(matching.map(row=>row.id)));\n let start=0;\n if(cursor) {\n let saved;try {saved=JSON.parse(cursor);} catch {throw new KanbanError('INVALID_CURSOR');}\n if(saved.scope!==scope) throw new KanbanError('CURSOR_SCOPE_MISMATCH');\n if(saved.revision!==revision||saved.signature!==signature) throw new KanbanError('CURSOR_STALE');\n start=matching.findIndex(row=>row.id===saved.after)+1;if(start<1) throw new KanbanError('INVALID_CURSOR');\n }\n const items=matching.slice(start,start+limit);\n return {revision,total:matching.length,items,...(start+items.length<matching.length?{nextCursor:JSON.stringify({scope,revision,signature,after:items.at(-1)!.id})}: {})};\n}\nfunction matchesExecution(row: KanbanSessionItemSummary, filter: string): boolean {\n const session=row.execution;if(!session||session.stale) return false;\n return filter==='running'?session.state==='running':filter==='recent'?session.recent:session.state==='running'||session.recent;\n}\n","import type {KanbanItem, KanbanExternalReference, KanbanWorkflowState, KanbanSessionAssociation, KanbanRelatedSession, KanbanSessionExecution, KanbanExecutionSession} from './contracts';\n\nexport function sessionAssociations(item: KanbanItem, bindings: KanbanExternalReference[], workflow: KanbanWorkflowState): KanbanSessionAssociation[] {\n return [...bindings.map(ref => {\n const dispatch = workflow.dispatches[ref.revision ?? ''];\n const execution: KanbanSessionExecution | undefined = dispatch?.itemId === item.id ? {\n id: dispatch.id, purpose: 'execution', dispatchId: dispatch.id, attempt: dispatch.attempt,\n outcome: dispatch.outcome, createdAt: dispatch.preparedAt, code: dispatch.code,\n ...(dispatch.continuation ? {previousDispatchId: dispatch.continuation.previousDispatchId} : {}),\n } : undefined;\n return {sessionId: ref.id, purpose: 'execution' as const, ...(execution ? {execution} : {})};\n }), ...item.participants.filter(row => !bindings.some(ref => ref.id === row.sessionId)).map(row => ({sessionId: row.sessionId, purpose: 'discussion' as const}))];\n}\n\n/** Session identity and run identity are independent; neither activity nor a hook selects the primary. */\nexport async function projectRelatedSessions(associations: KanbanSessionAssociation[], currentId: string | undefined,\n inspect: (id: string) => Promise<KanbanExecutionSession>): Promise<KanbanRelatedSession[]> {\n const grouped = new Map<string, {purposes: Set<KanbanSessionAssociation['purpose']>; executions: Map<string, KanbanSessionExecution>}>();\n for (const row of associations) {\n let group = grouped.get(row.sessionId);\n if (!group) grouped.set(row.sessionId, group = {purposes: new Set(), executions: new Map()});\n group.purposes.add(row.purpose);\n if (row.execution) group.executions.set(row.execution.id, {...group.executions.get(row.execution.id), ...row.execution});\n }\n const rows: KanbanRelatedSession[] = [], entries = [...grouped]; let next = 0;\n await Promise.all(Array.from({length: Math.min(entries.length, 8)}, async () => {\n while (next < entries.length) {\n const [sessionId, group] = entries[next++], state = await inspect(sessionId);\n rows.push({...state, current: sessionId === currentId, purposes: [...group.purposes].sort(),\n executions: [...group.executions.values()].sort((a, b) => (a.createdAt ?? 0) - (b.createdAt ?? 0) || a.id.localeCompare(b.id))});\n }\n }));\n return rows.sort((a, b) => Number(b.current) - Number(a.current) || a.sessionId.localeCompare(b.sessionId));\n}\n","import type {KanbanBoard, KanbanCommand, KanbanActor, KanbanService, KanbanExecutionSession, KanbanExternalReference, KanbanSessionItemSummary, KanbanWorkflowState} from './contracts';\nimport type {KanbanSessionSource} from './activity-ports';\nimport {principal,principalKey,candidates} from './identity';\nimport {KanbanError} from './ports';\nimport {querySessionItems} from './session-activity-query';\nimport {randomUUID} from './utils';\nimport {sessionAssociations, projectRelatedSessions} from './session-relations';\nexport interface KanbanSessionActivityOptions {source: KanbanSessionSource; now?:()=>number}\nexport function executionBindings(refs: KanbanExternalReference[] | undefined, referenceSystem: string) {return (refs ?? []).filter(ref=>ref.system===referenceSystem);}\nfunction currentBinding(refs: KanbanExternalReference[]|undefined, itemId:string, workflow:KanbanWorkflowState, referenceSystem: string) {\n const binding=executionBindings(refs, referenceSystem).at(-1),latest=Object.values(workflow.dispatches).filter(dispatch=>dispatch.itemId===itemId).sort((a,b)=>b.attempt-a.attempt)[0];\n if(!binding||!latest||binding.revision===latest.id) return binding;\n return ['prepared','running','unknown','succeeded'].includes(latest.outcome)||workflow.dispatches[binding.revision ?? '']?undefined:binding;\n}\n\n/** Optional resource projection; access checks remain owned by the injected source. */\nexport function createKanbanSessionActivity(core: KanbanService, options: KanbanSessionActivityOptions | undefined, log: (event: {operationId:string;sessionId?:string;stage:string;result:'failure';code:string;durationMs:number})=>void, referenceSystem: string) {\n const inspect=createSessionInspector(options,log);\n const project=async(boardId:string,rows:KanbanSessionItemSummary[])=>{\n if(!rows.some(row=>executionBindings(row.externalRefs,referenceSystem).length))return rows;\n const [board,workflow]=await Promise.all([boardMetadata(core,boardId),core.workflow(boardId)]), bindings=new Map(rows.map(row=>[row.id,currentBinding(row.externalRefs,row.id,workflow,referenceSystem)]));\n const ids=[...new Set([...bindings.values()].flatMap(ref=>ref?[ref.id]:[]))], states=new Map<string,KanbanExecutionSession>();let next=0;\n await Promise.all(Array.from({length:Math.min(ids.length,8)},async()=>{while(next<ids.length) {const id=ids[next++];states.set(id,await inspect(board,id));}}));\n return rows.map(row=>{const binding=bindings.get(row.id);return {...row,...(binding?{execution:states.get(binding.id)}: {})};});\n };\n return {\n queryActivity:(boardId:string,query:import('./contracts').KanbanSessionActivityQuery={})=>querySessionItems(core,boardId,query,rows=>project(boardId,rows)),\n async executionSession(boardId:string,itemId:string, query: import('./contracts').KanbanSessionDetailQuery = {}) {\n const started = Date.now();\n if (!query || typeof query !== 'object' || Array.isArray(query) || Object.keys(query).some(key => key !== 'includeRelated') || query.includeRelated !== undefined && typeof query.includeRelated !== 'boolean') throw new KanbanError('INVALID_QUERY');\n const {item}=await core.item(boardId,itemId),bindings=executionBindings(item.externalRefs,referenceSystem);\n if(!bindings.length && !query.includeRelated)return {history:[]};\n const [board,workflow]=await Promise.all([boardMetadata(core,boardId),core.workflow(boardId)]),last=currentBinding(item.externalRefs,itemId,workflow,referenceSystem);\n let sessionsCode: string | undefined;\n const related = query.includeRelated ? await options?.source.related?.(board, item, workflow).catch(error => {\n const candidate = (error as {code?: string})?.code;\n sessionsCode = candidate && /^[A-Z][A-Z0-9_]{0,79}$/.test(candidate) ? candidate : 'SESSION_RELATIONS_FAILED';\n try {log({operationId: randomUUID(), stage: 'session.relations', result: 'failure', code: sessionsCode, durationMs: Date.now() - started});} catch { /* Diagnostics cannot hide known associations. */ }\n return [];\n }) : [];\n const sessions = query.includeRelated ? await projectRelatedSessions([\n ...sessionAssociations(item, bindings, workflow), ...related ?? [],\n ], last?.id, id => inspect(board, id)) : undefined;\n return {current:last?(sessions?.find(row=>row.sessionId===last.id) ?? await inspect(board,last.id)):undefined,\n history:bindings.map(ref=>({sessionId:ref.id,bindingId:ref.revision,current:ref===last})).reverse(), ...(sessions ? {sessions} : {}), ...(sessionsCode ? {sessionsCode} : {})};\n },\n validate:(board:KanbanBoard,command:KanbanCommand|undefined,actor:KanbanActor)=>validateBinding(core,options,board,command,actor,referenceSystem),\n };\n}\nfunction createSessionInspector(options:KanbanSessionActivityOptions|undefined,log:Parameters<typeof createKanbanSessionActivity>[2]) {\n const now=options?.now ?? Date.now, cache=new Map<string,KanbanExecutionSession>();\n return async(board:KanbanBoard,sessionId:string):Promise<KanbanExecutionSession>=>{\n const started=now(),key=JSON.stringify([board.id,sessionId]);\n try {\n if(!options) throw new KanbanError('EXECUTION_SESSION_UNAVAILABLE');\n const row=await options.source.inspect(board,sessionId);\n if(row.sessionId!==sessionId) throw new KanbanError('SESSION_ACTIVITY_SCOPE_MISMATCH');\n const stale=row.stale||row.observedAt===undefined||now()-row.observedAt>90000;\n const value:KanbanExecutionSession={sessionId,...(typeof row.resourceId==='string'&&row.resourceId.length<=4096&&!row.resourceId.includes('\\0')?{resourceId:row.resourceId}:{}),title:row.title?.slice(0,256),state:row.state,createdAt:row.createdAt,lastActivityAt:row.lastActivityAt,observedAt:row.observedAt,stale,\n recent:!stale&&row.lastActivityAt!==undefined&&row.lastActivityAt<=now()&&now()-row.lastActivityAt<=600000,...(row.code&&/^[A-Z][A-Z0-9_]{0,79}$/.test(row.code)?{code:row.code}:{})};\n if(cache.size>=256) cache.delete(cache.keys().next().value!);cache.set(key,value);return value;\n } catch(error) {\n const candidate=(error as {code?:string})?.code,code=candidate&&/^[A-Z][A-Z0-9_]{0,79}$/.test(candidate)?candidate:'SESSION_ACTIVITY_FAILED';\n try {log({operationId:randomUUID(),sessionId,stage:'session.activity',result:'failure',code,durationMs:now()-started});} catch { /* Observation diagnostics cannot change assignments. */ }\n const prior=['EIO','ETIMEDOUT','ECONNRESET','ECONNREFUSED','SESSION_ACTIVITY_FAILED'].includes(code)?cache.get(key):undefined;\n return {...prior,sessionId,state:prior?.state ?? 'unknown',stale:true,recent:false,code};\n }\n };\n}\nasync function boardMetadata(core: KanbanService, boardId: string): Promise<KanbanBoard> {\n const board=(await core.catalog()).find(board=>board.id===boardId) ?? await core.read(boardId);\n return {...board,items:[]};\n}\nasync function validateBinding(core: KanbanService, options:KanbanSessionActivityOptions|undefined, board:KanbanBoard, command:KanbanCommand|undefined, actor:KanbanActor, referenceSystem: string): Promise<void> {\n if(command?.type!=='link-reference'||command.reference.system!==referenceSystem) return;\n if(!options||!command.reference.revision) throw new KanbanError('EXECUTION_SESSION_UNAVAILABLE');\n const item=board.items.find(item=>item.id===command.itemId);if(!item) throw new KanbanError('ITEM_NOT_FOUND');\n const key=principalKey(principal(actor)), coordinator=board.members?.some(member=>principalKey(member)===key&&member.roles?.includes('coordinator'));\n if(!coordinator&&item.owner?.principalId!==key&&!(item.owner===undefined&&candidates(board,item).some(candidate=>principalKey(candidate)===key))) throw new KanbanError('EXECUTION_SESSION_ASSIGNMENT_DENIED');\n const active=Object.values((await core.workflow(board.id)).dispatches).filter(dispatch=>dispatch.itemId===item.id).sort((a,b)=>b.attempt-a.attempt)[0];\n if(active&&command.reference.revision!==active.id) throw new KanbanError('EXECUTION_SESSION_LOCKED');\n if(active&&executionBindings(item.externalRefs,referenceSystem).some(ref=>ref.revision===active.id&&ref.id!==command.reference.id)) throw new KanbanError('EXECUTION_SESSION_LOCKED');\n if(options.source.authorizeBinding) return options.source.authorizeBinding(board,command.reference.id);\n const state=await options.source.inspect(board,command.reference.id);\n if(state.sessionId!==command.reference.id||state.stale) throw new KanbanError('EXECUTION_SESSION_UNAVAILABLE');\n}\n","import {createKanbanModule as createCore, type KanbanModuleHandle as CoreHandle} from './module';\nimport type {KanbanModuleOptions} from './ports';\nimport {KanbanError} from './ports';\nimport type {KanbanSessionActivityQuery, KanbanSessionItemPage, KanbanExecutionSessionDetail} from './contracts';\nimport {createKanbanSessionActivity, type KanbanSessionActivityOptions} from './session-activity';\nexport interface KanbanActivityModuleOptions extends KanbanModuleOptions {\n executionSessions?: KanbanSessionActivityOptions;\n referenceSystem: string;\n}\nexport interface KanbanActivityHandle extends CoreHandle {\n queryActivity: (boardId: string, query?: KanbanSessionActivityQuery) => Promise<KanbanSessionItemPage>;\n executionSession: (boardId: string, itemId: string, query?: import('./contracts').KanbanSessionDetailQuery) => Promise<KanbanExecutionSessionDetail>;\n}\n\n/** Application integrations stay outside the portable core. */\nexport function createKanbanActivityModule(options: KanbanActivityModuleOptions): KanbanActivityHandle {\n const {executionSessions, referenceSystem, ...core} = options;\n let activity: ReturnType<typeof createKanbanSessionActivity>;\n const service=createCore({...core,\n authorize:async input=>{await core.authorize(input);await activity.validate(input.board,input.command,input.actor);},\n });\n activity=createKanbanSessionActivity(service,executionSessions,core.log,referenceSystem);\n let stopping = false;\n const pending = new Set<Promise<unknown>>();\n const track = <T>(run: () => Promise<T>): Promise<T> => {\n if (stopping) return Promise.reject(new KanbanError('SERVICE_STOPPING'));\n const work = Promise.resolve().then(run).finally(() => {pending.delete(work);});\n pending.add(work); return work;\n };\n const quiesce = async () => {\n stopping = true;\n await Promise.allSettled([...pending]);\n await service.quiesce();\n };\n return {\n ...forwardCore(service, track),\n queryActivity: (...args) => track(() => activity.queryActivity(...args)),\n executionSession: (...args) => track(() => activity.executionSession(...args)),\n related: sessionId => track(() => relatedBoards(service, referenceSystem, sessionId)),\n quiesce, dispose: quiesce,\n };\n}\n\nfunction forwardCore(service: CoreHandle, track: <T>(run: () => Promise<T>) => Promise<T>): Omit<CoreHandle, 'related' | 'quiesce' | 'dispose'> {\n return {\n create: (input) => track(() => service.create(input)),\n read: (id) => track(() => service.read(id)),\n list: () => track(() => service.list()),\n catalog: () => track(() => service.catalog()),\n execute: (id, request) => track(() => service.execute(id, request)),\n events: (id, after, limit) => track(() => service.events(id, after, limit)),\n query: (id, query) => track(() => service.query(id, query)),\n item: (id, item) => track(() => service.item(id, item)),\n overview: (id) => track(() => service.overview(id)),\n scan: () => track(() => service.scan()),\n workflow: (id) => track(() => service.workflow(id)),\n resolve: (reference) => track(() => service.resolve(reference)),\n suggest: (id, query) => track(() => service.suggest(id, query)),\n records: (id, query) => track(() => service.records(id, query)),\n record: (id, record) => track(() => service.record(id, record)),\n currentPrincipal: () => track(() => service.currentPrincipal()),\n };\n}\nasync function relatedBoards(service: CoreHandle, referenceSystem: string, sessionId: string) {\n const boards = await service.related(sessionId);\n for (const board of await service.catalog()) {\n if (boards.some(old => old.id === board.id)) continue;\n const matches = await service.query(board.id, {view: 'all', reference: {system: referenceSystem, id: sessionId}, limit: 1});\n if (matches.total) boards.push(await service.read(board.id));\n }\n return boards;\n}\n"],"mappings":"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","names":["page","board","item"]}