@nestos/kanban 0.5.1 → 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 (147) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +73 -29
  3. package/dist/{activity-C6xDM-cg.d.cts → activity-BbC4sgKL.d.cts} +28 -2
  4. package/dist/{activity-Dw8DOw0L.d.ts → activity-Czlan5xW.d.ts} +28 -2
  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 +10 -282
  10. package/dist/activity.cjs.map +1 -1
  11. package/dist/activity.d.cts +8 -30
  12. package/dist/activity.d.ts +8 -30
  13. package/dist/activity.js +9 -281
  14. package/dist/activity.js.map +1 -1
  15. package/dist/{chunk-O4OGAAV4.js → chunk-3JYLJPE5.js} +29 -5
  16. package/dist/chunk-3JYLJPE5.js.map +1 -0
  17. package/dist/{chunk-ATUFFNLV.js → chunk-3LEIHB7Y.js} +5 -5
  18. package/dist/chunk-4XF6LQSU.cjs.map +1 -1
  19. package/dist/chunk-5Q7WB63A.cjs +282 -0
  20. package/dist/chunk-5Q7WB63A.cjs.map +1 -0
  21. package/dist/{chunk-PSKQAFDF.cjs → chunk-7KDOTBRX.cjs} +1 -1
  22. package/dist/chunk-7KDOTBRX.cjs.map +1 -0
  23. package/dist/{chunk-5DLZWW5Y.js → chunk-AHHEGTUA.js} +3 -3
  24. package/dist/chunk-AOHQCX7K.cjs +95 -0
  25. package/dist/chunk-AOHQCX7K.cjs.map +1 -0
  26. package/dist/{chunk-IUI5CI2S.cjs → chunk-DINLR6E6.cjs} +96 -92
  27. package/dist/chunk-DINLR6E6.cjs.map +1 -0
  28. package/dist/{chunk-L44TM4EE.cjs → chunk-EYTVC2H5.cjs} +568 -454
  29. package/dist/chunk-EYTVC2H5.cjs.map +1 -0
  30. package/dist/chunk-FEE2YBZ7.js +1972 -0
  31. package/dist/chunk-FEE2YBZ7.js.map +1 -0
  32. package/dist/chunk-FNO5K3A7.js +282 -0
  33. package/dist/chunk-FNO5K3A7.js.map +1 -0
  34. package/dist/chunk-IHHDXZPZ.js +382 -0
  35. package/dist/chunk-IHHDXZPZ.js.map +1 -0
  36. package/dist/chunk-J3EAIVA3.js +280 -0
  37. package/dist/chunk-J3EAIVA3.js.map +1 -0
  38. package/dist/{chunk-DFTOTBGF.js → chunk-KH3WF3K4.js} +20 -16
  39. package/dist/chunk-KH3WF3K4.js.map +1 -0
  40. package/dist/{chunk-TB2RZGDV.js → chunk-M35X5PHO.js} +14 -1
  41. package/dist/chunk-M35X5PHO.js.map +1 -0
  42. package/dist/chunk-QRGZD2JU.cjs +1972 -0
  43. package/dist/chunk-QRGZD2JU.cjs.map +1 -0
  44. package/dist/{chunk-4KF7KUAI.cjs → chunk-RBOER6EW.cjs} +6 -6
  45. package/dist/chunk-RBOER6EW.cjs.map +1 -0
  46. package/dist/{chunk-6MN3H3YF.cjs → chunk-RSH6QSEC.cjs} +16 -3
  47. package/dist/chunk-RSH6QSEC.cjs.map +1 -0
  48. package/dist/{chunk-T7P5EIC4.cjs → chunk-VMRDXOLX.cjs} +33 -33
  49. package/dist/chunk-VMRDXOLX.cjs.map +1 -0
  50. package/dist/{chunk-5XKOCKY2.js → chunk-X4UHZX6F.js} +1 -1
  51. package/dist/chunk-XBSGUVCY.cjs +382 -0
  52. package/dist/chunk-XBSGUVCY.cjs.map +1 -0
  53. package/dist/{chunk-OHQTKGQN.js → chunk-XKDZHAUE.js} +242 -128
  54. package/dist/chunk-XKDZHAUE.js.map +1 -0
  55. package/dist/chunk-ZUZGAO6G.cjs +280 -0
  56. package/dist/chunk-ZUZGAO6G.cjs.map +1 -0
  57. package/dist/{content-CAIp1njn.d.cts → content-C5aJZFlw.d.cts} +1 -1
  58. package/dist/{content-CGMJdyH_.d.ts → content-hLH5BNud.d.ts} +1 -1
  59. package/dist/contracts/index.cjs +13 -3
  60. package/dist/contracts/index.cjs.map +1 -1
  61. package/dist/contracts/index.d.cts +5 -4
  62. package/dist/contracts/index.d.ts +5 -4
  63. package/dist/contracts/index.js +14 -4
  64. package/dist/{discovery-DxpirOz3.d.cts → discovery-9E-LfwIw.d.cts} +1 -1
  65. package/dist/{discovery-gSqWpuRm.d.ts → discovery-D60yrjej.d.ts} +1 -1
  66. package/dist/discovery.cjs +63 -65
  67. package/dist/discovery.cjs.map +1 -1
  68. package/dist/discovery.d.cts +4 -4
  69. package/dist/discovery.d.ts +4 -4
  70. package/dist/discovery.js +5 -7
  71. package/dist/discovery.js.map +1 -1
  72. package/dist/execution-bridge-fpCnH6z5.d.cts +52 -0
  73. package/dist/execution-bridge-rk0uVy_n.d.ts +52 -0
  74. package/dist/execution.cjs +6 -356
  75. package/dist/execution.cjs.map +1 -1
  76. package/dist/execution.d.cts +4 -51
  77. package/dist/execution.d.ts +4 -51
  78. package/dist/execution.js +9 -359
  79. package/dist/execution.js.map +1 -1
  80. package/dist/index.cjs +10 -12
  81. package/dist/index.cjs.map +1 -1
  82. package/dist/index.d.cts +9 -16
  83. package/dist/index.d.ts +9 -16
  84. package/dist/index.js +10 -12
  85. package/dist/index.js.map +1 -1
  86. package/dist/{module-CWNK2BMj.d.ts → module-CYfE7YhM.d.ts} +2 -2
  87. package/dist/{module-BbvwdCDF.d.cts → module-Ca9L3qCn.d.cts} +2 -2
  88. package/dist/{planning-CcFZYdIL.d.cts → planning-CHrOqQ6b.d.cts} +45 -10
  89. package/dist/{planning-CcFZYdIL.d.ts → planning-CHrOqQ6b.d.ts} +45 -10
  90. package/dist/{ports-Bn6ro2FB.d.ts → ports-B0McVg7B.d.ts} +4 -3
  91. package/dist/ports-BeGO1VRI.d.cts +49 -0
  92. package/dist/{ports-CaL6geqR.d.cts → ports-GQy0x-RI.d.cts} +4 -3
  93. package/dist/ports-ykGiNUSx.d.ts +49 -0
  94. package/dist/runtime/index.cjs +205 -0
  95. package/dist/runtime/index.cjs.map +1 -0
  96. package/dist/runtime/index.d.cts +75 -0
  97. package/dist/runtime/index.d.ts +75 -0
  98. package/dist/runtime/index.js +205 -0
  99. package/dist/runtime/index.js.map +1 -0
  100. package/dist/runtime/integration.cjs +423 -0
  101. package/dist/runtime/integration.cjs.map +1 -0
  102. package/dist/runtime/integration.d.cts +658 -0
  103. package/dist/runtime/integration.d.ts +658 -0
  104. package/dist/runtime/integration.js +423 -0
  105. package/dist/runtime/integration.js.map +1 -0
  106. package/dist/storage.cjs +9 -277
  107. package/dist/storage.cjs.map +1 -1
  108. package/dist/storage.d.cts +6 -6
  109. package/dist/storage.d.ts +6 -6
  110. package/dist/storage.js +11 -279
  111. package/dist/storage.js.map +1 -1
  112. package/dist/{store-q63eSMoT.d.cts → store-DPwHzGiz.d.cts} +2 -2
  113. package/dist/{store-Ct0gOAAg.d.ts → store-DgdrPCV9.d.ts} +2 -2
  114. package/dist/task-projection-CSorlMws.d.ts +11 -0
  115. package/dist/task-projection-DKKB_ueb.d.cts +11 -0
  116. package/examples/standalone.ts +22 -0
  117. package/package.json +117 -87
  118. package/dist/chunk-2YFPV5GF.cjs +0 -16
  119. package/dist/chunk-2YFPV5GF.cjs.map +0 -1
  120. package/dist/chunk-4KF7KUAI.cjs.map +0 -1
  121. package/dist/chunk-6MN3H3YF.cjs.map +0 -1
  122. package/dist/chunk-DFTOTBGF.js.map +0 -1
  123. package/dist/chunk-G6FCDLDF.js +0 -16
  124. package/dist/chunk-G6FCDLDF.js.map +0 -1
  125. package/dist/chunk-IUI5CI2S.cjs.map +0 -1
  126. package/dist/chunk-KYNYPKKH.cjs +0 -71
  127. package/dist/chunk-KYNYPKKH.cjs.map +0 -1
  128. package/dist/chunk-L44TM4EE.cjs.map +0 -1
  129. package/dist/chunk-LSE6JG5I.cjs +0 -20
  130. package/dist/chunk-LSE6JG5I.cjs.map +0 -1
  131. package/dist/chunk-O4OGAAV4.js.map +0 -1
  132. package/dist/chunk-OHQTKGQN.js.map +0 -1
  133. package/dist/chunk-PSKQAFDF.cjs.map +0 -1
  134. package/dist/chunk-QTMTBEBA.js +0 -20
  135. package/dist/chunk-QTMTBEBA.js.map +0 -1
  136. package/dist/chunk-T7P5EIC4.cjs.map +0 -1
  137. package/dist/chunk-TB2RZGDV.js.map +0 -1
  138. package/dist/ui/index.cjs +0 -1961
  139. package/dist/ui/index.cjs.map +0 -1
  140. package/dist/ui/index.d.cts +0 -209
  141. package/dist/ui/index.d.ts +0 -209
  142. package/dist/ui/index.js +0 -1961
  143. package/dist/ui/index.js.map +0 -1
  144. package/dist/ui/kanban.css +0 -194
  145. /package/dist/{chunk-ATUFFNLV.js.map → chunk-3LEIHB7Y.js.map} +0 -0
  146. /package/dist/{chunk-5DLZWW5Y.js.map → chunk-AHHEGTUA.js.map} +0 -0
  147. /package/dist/{chunk-5XKOCKY2.js.map → chunk-X4UHZX6F.js.map} +0 -0
@@ -17,10 +17,10 @@
17
17
 
18
18
 
19
19
 
20
- var _chunkL44TM4EEcjs = require('./chunk-L44TM4EE.cjs');
20
+ var _chunkEYTVC2H5cjs = require('./chunk-EYTVC2H5.cjs');
21
21
 
22
22
 
23
- var _chunk2YFPV5GFcjs = require('./chunk-2YFPV5GF.cjs');
23
+ var _chunkRSH6QSECcjs = require('./chunk-RSH6QSEC.cjs');
24
24
 
25
25
  // src/store.ts
26
26
  var _vfscore = require('@nestos/vfs-core');
@@ -31,14 +31,14 @@ var SeqKanbanStore = class _SeqKanbanStore {
31
31
  this.bodies = _nullishCoalesce(bodies, () => ( { read: async (hash) => {
32
32
  const body = await fs.meta.assets.getAsset(path, `${hash}.json`);
33
33
  if (body === null || body instanceof ArrayBuffer) return body;
34
- throw new (0, _chunk2YFPV5GFcjs.KanbanError)("BODY_INTEGRITY");
34
+ throw new (0, _chunkRSH6QSECcjs.KanbanError)("BODY_INTEGRITY");
35
35
  }, write: async (hash, bytes) => {
36
36
  await fs.meta.assets.putAsset(path, `${hash}.json`, bytes);
37
37
  } }));
38
38
  }
39
39
 
40
40
  static async open(fs, directory = "/kanban", bodies) {
41
- if (!_optionalChain([fs, 'access', _2 => _2.meta, 'access', _3 => _3.seq, 'optionalAccess', _4 => _4.transaction])) throw new (0, _chunk2YFPV5GFcjs.KanbanError)("TRANSACTION_REQUIRED");
41
+ if (!_optionalChain([fs, 'access', _2 => _2.meta, 'access', _3 => _3.seq, 'optionalAccess', _4 => _4.transaction])) throw new (0, _chunkRSH6QSECcjs.KanbanError)("TRANSACTION_REQUIRED");
42
42
  try {
43
43
  await fs.driver.createFile({ parentPath: directory, name: "boards.seq", type: "seqfile", recursive: true });
44
44
  } catch (error) {
@@ -47,7 +47,7 @@ var SeqKanbanStore = class _SeqKanbanStore {
47
47
  return new _SeqKanbanStore(fs, `${directory}/boards.seq`, bodies);
48
48
  }
49
49
  async boardId(number) {
50
- return this.fs.meta.seq.getEntry(this.path, _chunkL44TM4EEcjs.boardNumberKey.call(void 0, number));
50
+ return this.fs.meta.seq.getEntry(this.path, _chunkEYTVC2H5cjs.boardNumberKey.call(void 0, number));
51
51
  }
52
52
  async ids() {
53
53
  return JSON.parse(await _asyncNullishCoalesce(await this.fs.meta.seq.getEntry(this.path, "index"), async () => ( "[]")));
@@ -60,61 +60,61 @@ var SeqKanbanStore = class _SeqKanbanStore {
60
60
  return keys.filter((key) => rows[key] !== void 0).map((key) => JSON.parse(rows[key]));
61
61
  }
62
62
  async archived(boardId, itemId) {
63
- const raw = await this.fs.meta.seq.getEntry(this.path, _chunkL44TM4EEcjs.archiveKey.call(void 0, boardId, itemId));
63
+ const raw = await this.fs.meta.seq.getEntry(this.path, _chunkEYTVC2H5cjs.archiveKey.call(void 0, boardId, itemId));
64
64
  if (!raw) return null;
65
- const record = await _chunkL44TM4EEcjs.decodeRow.call(void 0, raw, this.bodies);
65
+ const record = await _chunkEYTVC2H5cjs.decodeRow.call(void 0, raw, this.bodies);
66
66
  record.item.number ??= await _asyncOptionalChain([(await this.view(boardId)), 'optionalAccess', async _5 => _5.state, 'access', async _6 => _6.archiveIndex, 'optionalAccess', async _7 => _7.find, 'call', async _8 => _8((item) => item.id === itemId), 'optionalAccess', async _9 => _9.number]);
67
67
  return record;
68
68
  }
69
69
  async load(id, receiptKey) {
70
70
  return this.fs.meta.seq.transaction(async (tx) => {
71
- const raw = await tx.getEntry(this.path, _chunkL44TM4EEcjs.boardKey.call(void 0, id));
72
- return { raw, rows: await tx.getEntries(this.path, _chunkL44TM4EEcjs.rowKeys.call(void 0, raw)), receipt: receiptKey ? await tx.getEntry(this.path, receiptKey) : void 0 };
71
+ const raw = await tx.getEntry(this.path, _chunkEYTVC2H5cjs.boardKey.call(void 0, id));
72
+ return { raw, rows: await tx.getEntries(this.path, _chunkEYTVC2H5cjs.rowKeys.call(void 0, raw)), receipt: receiptKey ? await tx.getEntry(this.path, receiptKey) : void 0 };
73
73
  });
74
74
  }
75
75
  async view(id) {
76
- const raw = await this.fs.meta.seq.getEntry(this.path, _chunkL44TM4EEcjs.boardKey.call(void 0, id));
77
- const indexed = _chunkL44TM4EEcjs.indexedView.call(void 0, raw);
76
+ const raw = await this.fs.meta.seq.getEntry(this.path, _chunkEYTVC2H5cjs.boardKey.call(void 0, id));
77
+ const indexed = _chunkEYTVC2H5cjs.indexedView.call(void 0, raw);
78
78
  if (indexed) return indexed;
79
79
  const state = await this.read(id);
80
- return state ? _chunkL44TM4EEcjs.viewFromState.call(void 0, state) : null;
80
+ return state ? _chunkEYTVC2H5cjs.viewFromState.call(void 0, state) : null;
81
81
  }
82
82
  async read(id) {
83
- return _chunkL44TM4EEcjs.hydrate.call(void 0, await this.load(id), this.bodies);
83
+ return _chunkEYTVC2H5cjs.hydrate.call(void 0, await this.load(id), this.bodies);
84
84
  }
85
85
  async update(id, operationId, fingerprint, change) {
86
86
  const receiptKey = `receipt/${encodeURIComponent(id)}/${encodeURIComponent(operationId)}`;
87
- const before = await this.load(id, receiptKey), current = await _chunkL44TM4EEcjs.hydrate.call(void 0, before, this.bodies), receipt = before.receipt;
87
+ const before = await this.load(id, receiptKey), current = await _chunkEYTVC2H5cjs.hydrate.call(void 0, before, this.bodies), receipt = before.receipt;
88
88
  if (receipt) {
89
- _chunkL44TM4EEcjs.verifyReceipt.call(void 0, receipt, fingerprint);
90
- if (!current) throw new (0, _chunk2YFPV5GFcjs.KanbanError)("CORRUPT_RECEIPT");
89
+ _chunkEYTVC2H5cjs.verifyReceipt.call(void 0, receipt, fingerprint);
90
+ if (!current) throw new (0, _chunkRSH6QSECcjs.KanbanError)("CORRUPT_RECEIPT");
91
91
  return current;
92
92
  }
93
93
  const original = JSON.stringify(current), next = change(current);
94
- _chunkL44TM4EEcjs.numberEntities.call(void 0, next);
94
+ _chunkEYTVC2H5cjs.numberEntities.call(void 0, next);
95
95
  if (JSON.stringify(next) === original) return next;
96
96
  return this.persist(id, receiptKey, fingerprint, before, next);
97
97
  }
98
98
  async persist(id, receiptKey, fingerprint, before, next) {
99
- const events = [...next.events], archives = await Promise.all((_nullishCoalesce(next.archiveWrites, () => ( []))).map(async (write) => ({ key: _chunkL44TM4EEcjs.archiveKey.call(void 0, id, write.id), value: write.value ? await _chunkL44TM4EEcjs.encodeRow.call(void 0, write.value, this.bodies) : null })));
99
+ const events = [...next.events], archives = await Promise.all((_nullishCoalesce(next.archiveWrites, () => ( []))).map(async (write) => ({ key: _chunkEYTVC2H5cjs.archiveKey.call(void 0, id, write.id), value: write.value ? await _chunkEYTVC2H5cjs.encodeRow.call(void 0, write.value, this.bodies) : null })));
100
100
  delete next.archiveWrites;
101
101
  next.events = [];
102
- const encoded = await _chunkL44TM4EEcjs.encodeBoard.call(void 0, next, this.bodies);
102
+ const encoded = await _chunkEYTVC2H5cjs.encodeBoard.call(void 0, next, this.bodies);
103
103
  const committed = await this.fs.meta.seq.transaction(async (tx) => {
104
104
  const latestReceipt = await tx.getEntry(this.path, receiptKey);
105
105
  if (latestReceipt) {
106
- _chunkL44TM4EEcjs.verifyReceipt.call(void 0, latestReceipt, fingerprint);
106
+ _chunkEYTVC2H5cjs.verifyReceipt.call(void 0, latestReceipt, fingerprint);
107
107
  return null;
108
108
  }
109
- if (await tx.getEntry(this.path, _chunkL44TM4EEcjs.boardKey.call(void 0, id)) !== before.raw) throw new (0, _chunk2YFPV5GFcjs.KanbanError)("REVISION_CONFLICT");
109
+ if (await tx.getEntry(this.path, _chunkEYTVC2H5cjs.boardKey.call(void 0, id)) !== before.raw) throw new (0, _chunkRSH6QSECcjs.KanbanError)("REVISION_CONFLICT");
110
110
  await commitBoardNumber(tx, this.path, id, before, next, encoded);
111
111
  const eventRows = events.map((event) => ({ key: `event/${encodeURIComponent(id)}/${String(event.sequence).padStart(16, "0")}`, value: JSON.stringify(event) }));
112
112
  await writeRows(tx, this.path, [
113
113
  ...eventRows,
114
- ..._chunkL44TM4EEcjs.rowChanges.call(void 0, before, encoded),
114
+ ..._chunkEYTVC2H5cjs.rowChanges.call(void 0, before, encoded),
115
115
  ...archives,
116
- { key: _chunkL44TM4EEcjs.boardKey.call(void 0, id), value: encoded.raw },
117
- { key: receiptKey, value: _chunkL44TM4EEcjs.receiptDigest.call(void 0, fingerprint) }
116
+ { key: _chunkEYTVC2H5cjs.boardKey.call(void 0, id), value: encoded.raw },
117
+ { key: receiptKey, value: _chunkEYTVC2H5cjs.receiptDigest.call(void 0, fingerprint) }
118
118
  ]);
119
119
  const ids = JSON.parse(await _asyncNullishCoalesce(await tx.getEntry(this.path, "index"), async () => ( "[]")));
120
120
  if (!ids.includes(id)) await tx.setEntry(this.path, "index", JSON.stringify([...ids, id]));
@@ -122,7 +122,7 @@ var SeqKanbanStore = class _SeqKanbanStore {
122
122
  });
123
123
  if (committed) return committed;
124
124
  const replay = await this.read(id);
125
- if (!replay) throw new (0, _chunk2YFPV5GFcjs.KanbanError)("CORRUPT_RECEIPT");
125
+ if (!replay) throw new (0, _chunkRSH6QSECcjs.KanbanError)("CORRUPT_RECEIPT");
126
126
  return replay;
127
127
  }
128
128
  };
@@ -133,16 +133,16 @@ async function writeRows(tx, path, rows) {
133
133
  }
134
134
  }
135
135
  async function commitBoardNumber(tx, path, id, before, next, encoded) {
136
- const previous = _chunkL44TM4EEcjs.counter.call(void 0, await tx.getEntry(path, _chunkL44TM4EEcjs.boardCounterKey));
137
- next.board.number = _chunkL44TM4EEcjs.allocateBoardNumber.call(void 0, before.raw, previous);
138
- const owner = await tx.getEntry(path, _chunkL44TM4EEcjs.boardNumberKey.call(void 0, next.board.number));
139
- if (owner !== null && owner !== id) throw new (0, _chunk2YFPV5GFcjs.KanbanError)("INVALID_NUMBERING");
140
- encoded.raw = _chunkL44TM4EEcjs.patchBoardNumber.call(void 0, encoded.raw, next.board.number);
141
- await tx.setEntry(path, _chunkL44TM4EEcjs.boardCounterKey, String(Math.max(previous, next.board.number)));
142
- await tx.setEntry(path, _chunkL44TM4EEcjs.boardNumberKey.call(void 0, next.board.number), id);
136
+ const previous = _chunkEYTVC2H5cjs.counter.call(void 0, await tx.getEntry(path, _chunkEYTVC2H5cjs.boardCounterKey));
137
+ next.board.number = _chunkEYTVC2H5cjs.allocateBoardNumber.call(void 0, before.raw, previous);
138
+ const owner = await tx.getEntry(path, _chunkEYTVC2H5cjs.boardNumberKey.call(void 0, next.board.number));
139
+ if (owner !== null && owner !== id) throw new (0, _chunkRSH6QSECcjs.KanbanError)("INVALID_NUMBERING");
140
+ encoded.raw = _chunkEYTVC2H5cjs.patchBoardNumber.call(void 0, encoded.raw, next.board.number);
141
+ await tx.setEntry(path, _chunkEYTVC2H5cjs.boardCounterKey, String(Math.max(previous, next.board.number)));
142
+ await tx.setEntry(path, _chunkEYTVC2H5cjs.boardNumberKey.call(void 0, next.board.number), id);
143
143
  }
144
144
 
145
145
 
146
146
 
147
147
  exports.SeqKanbanStore = SeqKanbanStore;
148
- //# sourceMappingURL=chunk-T7P5EIC4.cjs.map
148
+ //# sourceMappingURL=chunk-VMRDXOLX.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/li/share/prj/x1/libraries/kanban/dist/chunk-VMRDXOLX.cjs","../src/store.ts"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACF,wDAA6B;AAC7B;AACA;ACtBA,2CAA0C;AAKnC,IAAM,eAAA,EAAN,MAAM,gBAAsC;AAAA,EAEvC,WAAA,CAA6B,EAAA,EAAkC,IAAA,EAAc,MAAA,EAA0B;AAA1E,IAAA,IAAA,CAAA,GAAA,EAAA,EAAA;AAAkC,IAAA,IAAA,CAAA,KAAA,EAAA,IAAA;AACnE,IAAA,IAAA,CAAK,OAAA,mBAAS,MAAA,UAAU,EAAC,IAAA,EAAM,MAAA,CAAM,IAAA,EAAA,GAAQ;AACzC,MAAA,MAAM,KAAA,EAAO,MAAM,EAAA,CAAG,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS,IAAA,EAAM,CAAA,EAAA;AACC,MAAA;AACZ,MAAA;AACT,IAAA;AAA4C,MAAA;AAAiB,IAAA;AAClG,EAAA;AAPiB,EAAA;AAQ4F,EAAA;AAChE,IAAA;AACrC,IAAA;AAA2C,MAAA;AACjC,IAAA;AAAoD,MAAA;AAAuB,IAAA;AAC/C,IAAA;AAC9C,EAAA;AACsD,EAAA;AAA8C,IAAA;AAAwB,EAAA;AAC7F,EAAA;AACwB,IAAA;AACvD,EAAA;AACgG,EAAA;AACjD,IAAA;AACQ,IAAA;AAEzB,IAAA;AAE2B,IAAA;AACF,IAAA;AACvD,EAAA;AAEoF,EAAA;AAC9B,IAAA;AACjC,IAAA;AAAyE,IAAA;AACvC,IAAA;AAAqE,IAAA;AAC5H,EAAA;AACsG,EAAA;AAChD,IAAA;AACD,MAAA;AACD,MAAA;AAC/C,IAAA;AACL,EAAA;AACuB,EAAA;AAC+B,IAAA;AACnB,IAAA;AAAuB,IAAA;AACtB,IAAA;AAAyC,IAAA;AAC7E,EAAA;AACoD,EAAA;AAAgD,IAAA;AAAE,EAAA;AAExB,EAAA;AACtB,IAAA;AACJ,IAAA;AACnC,IAAA;AAAmC,MAAA;AAAuC,MAAA;AAA2B,MAAA;AAAQ,IAAA;AACzE,IAAA;AAC9B,IAAA;AAC2B,IAAA;AACG,IAAA;AACrD,EAAA;AACgG,EAAA;AAC1C,IAAA;AAEtC,IAAA;AAA8B,IAAA;AAES,IAAA;AACT,IAAA;AACO,MAAA;AAC1B,MAAA;AAAyC,QAAA;AAAU,QAAA;AAAK,MAAA;AACnC,MAAA;AACG,MAAA;AACE,MAAA;AACd,MAAA;AAAI,QAAA;AAAwC,QAAA;AAAM,QAAA;AAC3C,QAAA;AAA0C,QAAA;AAAc,MAAA;AACtC,MAAA;AACV,MAAA;AACvC,MAAA;AACV,IAAA;AACqB,IAAA;AACW,IAAA;AAAuD,IAAA;AAAU,IAAA;AACtG,EAAA;AACJ;AAC4J;AAChI,EAAA;AAC+B,IAAA;AACJ,IAAA;AACnD,EAAA;AACJ;AAEoI;AAC/E,EAAA;AACG,EAAA;AACC,EAAA;AACP,EAAA;AACI,EAAA;AACG,EAAA;AACH,EAAA;AACtD;AD2CwD;AACA;AACA;AACA","file":"/home/li/share/prj/x1/libraries/kanban/dist/chunk-VMRDXOLX.cjs","sourcesContent":[null,"import {numberEntities, boardCounterKey, boardNumberKey, counter, allocateBoardNumber, patchBoardNumber} from './numbering';\nimport type { KanbanEvent, KanbanArchivedItem } from './contracts';\nimport { FSError, type IFileSystem } from '@nestos/vfs-core';\nimport { KanbanError, type KanbanState, type KanbanStore } from './ports';\nimport {indexedView, viewFromState, archiveKey, boardKey as key, rowKeys, hydrate, encodeBoard, rowChanges, encodeRow, decodeRow, receiptDigest, verifyReceipt, type StoredBoard, type KanbanBodyStore} from './storage-codec';\n\n/** One user-scoped journal, with atomic board/index/receipt commits. */\nexport class SeqKanbanStore implements KanbanStore {\n private readonly bodies: KanbanBodyStore;\n private constructor(private readonly fs: IFileSystem, private readonly path: string, bodies?: KanbanBodyStore) {\n this.bodies = bodies ?? {read: async hash => {\n const body = await fs.meta.assets.getAsset(path, `${hash}.json`);\n if (body === null || body instanceof ArrayBuffer) return body;\n throw new KanbanError('BODY_INTEGRITY');\n }, write: async (hash, bytes) => {await fs.meta.assets.putAsset(path, `${hash}.json`, bytes);}};\n }\n static async open(fs: IFileSystem, directory = '/kanban', bodies?: KanbanBodyStore): Promise<SeqKanbanStore> {\n if (!fs.meta.seq?.transaction) throw new KanbanError('TRANSACTION_REQUIRED');\n try { await fs.driver.createFile({ parentPath: directory, name: 'boards.seq', type: 'seqfile', recursive: true }); }\n catch (error) { if (!(error instanceof FSError) || error.code !== 'EEXIST') throw error; }\n return new SeqKanbanStore(fs, `${directory}/boards.seq`, bodies);\n }\n async boardId(number: number): Promise<string | null> {return this.fs.meta.seq!.getEntry(this.path, boardNumberKey(number));}\n async ids(): Promise<string[]> {\n return JSON.parse(await this.fs.meta.seq!.getEntry(this.path, 'index') ?? '[]');\n }\n async events(id: string, after: number, limit: number, through: number): Promise<KanbanEvent[]> {\n const end = Math.min(through, after + limit);\n const keys = Array.from({length: Math.max(0, end - after)}, (_, index) =>\n `event/${encodeURIComponent(id)}/${String(after + index + 1).padStart(16, '0')}`);\n if (!keys.length) return [];\n // The committed sequence freezes this immutable prefix; bulk key reads avoid history scans.\n const rows = await this.fs.meta.seq!.getEntries(this.path, keys);\n return keys.filter(key => rows[key] !== undefined).map(key => JSON.parse(rows[key]) as KanbanEvent);\n }\n\n async archived(boardId: string, itemId: string): Promise<KanbanArchivedItem | null> {\n const raw = await this.fs.meta.seq!.getEntry(this.path, archiveKey(boardId, itemId));\n if (!raw) return null; const record = await decodeRow<KanbanArchivedItem>(raw, this.bodies);\n record.item.number ??= (await this.view(boardId))?.state.archiveIndex?.find(item => item.id === itemId)?.number; return record;\n }\n private async load(id: string, receiptKey?: string): Promise<StoredBoard & {receipt?: string | null}> {\n return this.fs.meta.seq!.transaction!(async tx => {\n const raw = await tx.getEntry(this.path, key(id));\n return {raw, rows: await tx.getEntries(this.path, rowKeys(raw)), receipt: receiptKey ? await tx.getEntry(this.path, receiptKey) : undefined};\n });\n }\n async view(id: string) {\n const raw = await this.fs.meta.seq!.getEntry(this.path, key(id));\n const indexed = indexedView(raw); if (indexed) return indexed;\n const state = await this.read(id); return state ? viewFromState(state) : null;\n }\n async read(id: string): Promise<KanbanState | null> {return hydrate(await this.load(id), this.bodies);}\n async update(id: string, operationId: string, fingerprint: string,\n change: (state: KanbanState | null) => KanbanState): Promise<KanbanState> {\n const receiptKey = `receipt/${encodeURIComponent(id)}/${encodeURIComponent(operationId)}`;\n const before = await this.load(id, receiptKey), current = await hydrate(before, this.bodies), receipt = before.receipt;\n if (receipt) {verifyReceipt(receipt, fingerprint); if (!current) throw new KanbanError('CORRUPT_RECEIPT'); return current;}\n const original = JSON.stringify(current), next = change(current);\n numberEntities(next);\n if (JSON.stringify(next) === original) return next;\n return this.persist(id, receiptKey, fingerprint, before, next);\n }\n private async persist(id: string, receiptKey: string, fingerprint: string, before: StoredBoard, next: KanbanState): Promise<KanbanState> {\n const events = [...next.events], archives = await Promise.all((next.archiveWrites ?? []).map(async write =>\n ({key: archiveKey(id, write.id), value: write.value ? await encodeRow(write.value, this.bodies) : null})));\n delete next.archiveWrites; next.events = [];\n // No asset I/O runs under the SeqFile transaction: it may use the same backend.\n const encoded = await encodeBoard(next, this.bodies);\n const committed = await this.fs.meta.seq!.transaction!(async tx => {\n const latestReceipt = await tx.getEntry(this.path, receiptKey);\n if (latestReceipt) {verifyReceipt(latestReceipt, fingerprint); return null;}\n if (await tx.getEntry(this.path, key(id)) !== before.raw) throw new KanbanError('REVISION_CONFLICT');\n await commitBoardNumber(tx, this.path, id, before, next, encoded);\n const eventRows = events.map(event => ({key: `event/${encodeURIComponent(id)}/${String(event.sequence).padStart(16, '0')}`, value: JSON.stringify(event)}));\n await writeRows(tx, this.path, [...eventRows, ...rowChanges(before, encoded), ...archives,\n {key: key(id), value: encoded.raw!}, {key: receiptKey, value: receiptDigest(fingerprint)}]);\n const ids: string[] = JSON.parse(await tx.getEntry(this.path, 'index') ?? '[]');\n if (!ids.includes(id)) await tx.setEntry(this.path, 'index', JSON.stringify([...ids, id]));\n return next;\n });\n if (committed) return committed;\n const replay = await this.read(id); if (!replay) throw new KanbanError('CORRUPT_RECEIPT'); return replay;\n }\n}\nasync function writeRows(tx: import('@nestos/vfs-core').ISeqFileTransaction, path: string, rows: Array<{key: string; value: string | null}>): Promise<void> {\n for (const row of rows) {\n if (row.value === null) await tx.deleteEntry(path, row.key);\n else await tx.setEntry(path, row.key, row.value);\n }\n}\n\nasync function commitBoardNumber(tx: import('@nestos/vfs-core').ISeqFileTransaction, path: string, id: string, before: StoredBoard, next: KanbanState, encoded: StoredBoard): Promise<void> {\n const previous = counter(await tx.getEntry(path, boardCounterKey));\n next.board.number = allocateBoardNumber(before.raw, previous);\n const owner = await tx.getEntry(path, boardNumberKey(next.board.number));\n if (owner !== null && owner !== id) throw new KanbanError('INVALID_NUMBERING');\n encoded.raw = patchBoardNumber(encoded.raw!, next.board.number);\n await tx.setEntry(path, boardCounterKey, String(Math.max(previous, next.board.number)));\n await tx.setEntry(path, boardNumberKey(next.board.number), id);\n}\n"]}
@@ -55,4 +55,4 @@ export {
55
55
  DEFAULT_KANBAN_SCAN_POLICY,
56
56
  sameAutomationBinding
57
57
  };
58
- //# sourceMappingURL=chunk-5XKOCKY2.js.map
58
+ //# sourceMappingURL=chunk-X4UHZX6F.js.map
@@ -0,0 +1,382 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } async function _asyncOptionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = await fn(value); } else if (op === 'call' || op === 'optionalCall') { value = await fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
+
3
+ var _chunk4XF6LQSUcjs = require('./chunk-4XF6LQSU.cjs');
4
+
5
+
6
+
7
+
8
+ var _chunkRSH6QSECcjs = require('./chunk-RSH6QSEC.cjs');
9
+
10
+
11
+
12
+ var _chunkAOHQCX7Kcjs = require('./chunk-AOHQCX7K.cjs');
13
+
14
+ // src/execution-bridge.ts
15
+ async function createKanbanExecutionBridge(options) {
16
+ const path = `${options.directory}/executions.seq`;
17
+ try {
18
+ await options.fs.driver.createFile({ parentPath: options.directory, name: "executions.seq", type: "seqfile", recursive: true });
19
+ } catch (error) {
20
+ if (_optionalChain([error, 'optionalAccess', _ => _.code]) !== "EEXIST") throw error;
21
+ }
22
+ if (!_optionalChain([options, 'access', _2 => _2.fs, 'access', _3 => _3.meta, 'access', _4 => _4.seq, 'optionalAccess', _5 => _5.transaction])) throw new (0, _chunkRSH6QSECcjs.KanbanError)("TRANSACTION_REQUIRED");
23
+ let stopped = false, again = false, pending;
24
+ const writes = /* @__PURE__ */ new Set();
25
+ const failures = /* @__PURE__ */ new Map();
26
+ const track = (operation) => {
27
+ const tracked = operation.finally(() => {
28
+ writes.delete(tracked);
29
+ });
30
+ writes.add(tracked);
31
+ return tracked;
32
+ };
33
+ const active = () => !stopped;
34
+ const scan = async () => {
35
+ if (stopped) return;
36
+ if (pending) {
37
+ again = true;
38
+ return pending;
39
+ }
40
+ const started = Date.now();
41
+ pending = runBridge(options, path, active, track, failures).catch((error) => {
42
+ try {
43
+ options.log({ operationId: _chunkRSH6QSECcjs.randomUUID.call(void 0, ), stage: "bridge.scan", result: "failure", code: safeCode(error), durationMs: Date.now() - started });
44
+ } catch (e) {
45
+ }
46
+ throw error;
47
+ }).finally(() => {
48
+ pending = void 0;
49
+ if (again && !stopped) {
50
+ again = false;
51
+ void scan().catch(() => {
52
+ });
53
+ }
54
+ });
55
+ return pending;
56
+ };
57
+ const unsubscribe = _optionalChain([options, 'access', _6 => _6.subscribe, 'optionalCall', _7 => _7(() => {
58
+ void scan().catch(() => {
59
+ });
60
+ })]);
61
+ return { scan, tick: scan, async quiesce() {
62
+ stopped = true;
63
+ _optionalChain([unsubscribe, 'optionalCall', _8 => _8()]);
64
+ if (!pending) return;
65
+ let timer;
66
+ try {
67
+ await Promise.race([pending, new Promise((resolve) => {
68
+ timer = setTimeout(resolve, _nullishCoalesce(options.shutdownTimeoutMs, () => ( 5e3)));
69
+ })]);
70
+ } finally {
71
+ if (timer) clearTimeout(timer);
72
+ await Promise.allSettled([...writes]);
73
+ }
74
+ } };
75
+ }
76
+ async function runBridge(options, path, active, track, failures) {
77
+ const workflow = await options.service.workflow(options.boardId);
78
+ const live = new Set(Object.values(workflow.dispatches).filter((dispatch) => ["prepared", "running", "unknown"].includes(dispatch.outcome)).map((dispatch) => dispatch.id));
79
+ for (const id of failures.keys()) if (!live.has(id)) failures.delete(id);
80
+ for (const dispatch of Object.values(workflow.dispatches)) {
81
+ if (!active()) return;
82
+ if (live.has(dispatch.id) && (_nullishCoalesce(_optionalChain([failures, 'access', _9 => _9.get, 'call', _10 => _10(dispatch.id), 'optionalAccess', _11 => _11.retryAt]), () => ( 0))) <= Date.now()) await runDispatch(options, path, dispatch, active, track, failures);
83
+ }
84
+ const automatic = typeof options.autoDispatch === "function" ? await options.autoDispatch() : options.autoDispatch;
85
+ if (!automatic || !active()) return;
86
+ for (const id of (await options.service.workflow(options.boardId)).ready) {
87
+ if (!active()) return;
88
+ const { item } = await options.service.item(options.boardId, id), owner = _optionalChain([item, 'access', _12 => _12.eligiblePrincipals, 'optionalAccess', _13 => _13[0]]);
89
+ if (!owner || !_chunkAOHQCX7Kcjs.taskDue.call(void 0, item, (_nullishCoalesce(options.now, () => ( Date.now)))())) continue;
90
+ const worker = await options.worker(owner), board = await worker.read(options.boardId);
91
+ if (!active()) return;
92
+ if (!_chunkAOHQCX7Kcjs.boardAllowsStart.call(void 0, board, item)) continue;
93
+ try {
94
+ const dispatchId = _chunkRSH6QSECcjs.randomUUID.call(void 0, );
95
+ const estimate = await _optionalChain([options, 'access', _14 => _14.estimate, 'optionalCall', _15 => _15(item, owner)]);
96
+ if (!active()) return;
97
+ await worker.execute(board.id, { operationId: "dispatch:" + dispatchId, expectedRevision: board.revision, command: { type: "prepare-dispatch", itemId: id, dispatchId, estimate, trigger: "automatic" } });
98
+ const dispatch = (await options.service.workflow(board.id)).dispatches[dispatchId];
99
+ if (active()) await runDispatch(options, path, dispatch, active, track, failures);
100
+ } catch (error) {
101
+ if (!["WIP_LIMIT", "ASSIGNEE_WIP_LIMIT", "READY_LIMIT", "REVISION_CONFLICT"].includes(safeCode(error))) throw error;
102
+ }
103
+ }
104
+ }
105
+ async function runDispatch(options, path, dispatch, active, track, failures) {
106
+ const start = Date.now();
107
+ let stage = "inspect", result;
108
+ const previousFailure = failures.has(dispatch.id);
109
+ try {
110
+ let record = await loadRecord(options, path, dispatch);
111
+ if (!active()) return;
112
+ const current = await options.service.item(options.boardId, dispatch.itemId);
113
+ if ((_optionalChain([current, 'access', _16 => _16.item, 'access', _17 => _17.control, 'optionalAccess', _18 => _18.action]) === "cancel" || (await options.service.read(options.boardId)).lifecycle) && await track(preventSubmission(options, path, dispatch))) {
114
+ await settle(options, dispatch, { outcome: "cancelled", code: "CANCELLED_BEFORE_START" });
115
+ return;
116
+ }
117
+ if ((!record || record.phase === "prepared") && (await _asyncOptionalChain([(await options.service.read(options.boardId)), 'access', async _19 => _19.executionControl, 'optionalAccess', async _20 => _20.paused]) || _optionalChain([current, 'access', _21 => _21.item, 'access', _22 => _22.control, 'optionalAccess', _23 => _23.action]) === "pause")) return;
118
+ if (!record) {
119
+ stage = "prepare";
120
+ await options.authorize(stage, dispatch);
121
+ if (!active()) return;
122
+ const item = (await options.service.item(options.boardId, dispatch.itemId)).item;
123
+ if (!active()) return;
124
+ const request = await prepareRequest(options, path, dispatch, item);
125
+ if (!active()) return;
126
+ record = await track(savePrepared(options, path, dispatch, request));
127
+ }
128
+ if (!active()) return;
129
+ const submit = record.phase === "prepared" && await track(claimSubmission(options, path, dispatch));
130
+ if (!submit && await options.fs.meta.seq.getEntry(path, "cancelled:" + dispatch.id)) {
131
+ await settle(options, dispatch, { outcome: "cancelled", code: "CANCELLED_BEFORE_START" });
132
+ return;
133
+ }
134
+ if (!active()) return;
135
+ stage = submit ? "start" : "inspect";
136
+ await options.authorize(stage, dispatch);
137
+ if (!active()) return;
138
+ result = await (submit ? options.executor.start(record.request) : options.executor.inspect(record.request));
139
+ failures.delete(dispatch.id);
140
+ } catch (error) {
141
+ const notStarted = _optionalChain([error, 'optionalAccess', _24 => _24.executionOutcome]) === "not-started" && error.retryable === true;
142
+ if (notStarted && stage === "start") await track(releaseUnstartedSubmission(options, path, dispatch));
143
+ result = { outcome: stage === "prepare" && !notStarted ? "failed" : "unknown", code: safeCode(error) };
144
+ if (stage === "inspect" || notStarted) {
145
+ const previous = failures.get(dispatch.id), count = previous && previous.code === result.code ? previous.count + 1 : 1;
146
+ failures.set(dispatch.id, { code: result.code, count, retryAt: Date.now() + Math.min(6e4, 1e3 * 2 ** Math.min(count - 1, 6)) });
147
+ }
148
+ }
149
+ if (stage !== "inspect" || _chunk4XF6LQSUcjs.executionObservationChanged.call(void 0, dispatch, result) || previousFailure && !failures.has(dispatch.id))
150
+ try {
151
+ options.log({ operationId: dispatch.id, stage: `bridge.${stage}`, result: ["unknown", "failed", "cancelled"].includes(result.outcome) ? "failure" : "success", code: result.code, ...failures.has(dispatch.id) ? { retryAfterMs: Math.max(0, failures.get(dispatch.id).retryAt - Date.now()) } : {}, durationMs: Date.now() - start });
152
+ } catch (e2) {
153
+ }
154
+ if (active()) try {
155
+ await linkSession(options, dispatch, result.executionSessionId);
156
+ if (active()) await settle(options, dispatch, result);
157
+ } catch (error) {
158
+ if (safeCode(error) !== "REVISION_CONFLICT") throw error;
159
+ }
160
+ }
161
+ async function releaseUnstartedSubmission(options, path, dispatch) {
162
+ await options.fs.meta.seq.transaction(async (tx) => {
163
+ const raw = await tx.getEntry(path, dispatch.id);
164
+ if (!raw) throw new (0, _chunkRSH6QSECcjs.KanbanError)("EXECUTION_JOURNAL_MISSING");
165
+ const record = decodeRecord(raw, dispatch);
166
+ if (record.phase === "submitting") await tx.setEntry(path, dispatch.id, JSON.stringify({ ...record, phase: "prepared" }));
167
+ });
168
+ }
169
+ async function prepareRequest(options, path, dispatch, item) {
170
+ if (!dispatch.continuation) return options.executor.prepare(dispatch, item);
171
+ if (!options.executor.prepareContinuation) throw new (0, _chunkRSH6QSECcjs.KanbanError)("CONTINUATION_UNSUPPORTED");
172
+ const previous = (await options.service.workflow(options.boardId)).dispatches[dispatch.continuation.previousDispatchId];
173
+ if (!previous || previous.itemId !== dispatch.itemId) throw new (0, _chunkRSH6QSECcjs.KanbanError)("CONTINUATION_SCOPE_MISMATCH");
174
+ const record = await loadRecord(options, path, previous);
175
+ if (!record) throw new (0, _chunkRSH6QSECcjs.KanbanError)("EXECUTION_JOURNAL_MISSING");
176
+ return options.executor.prepareContinuation(dispatch, item, record.request);
177
+ }
178
+ async function loadRecord(options, path, dispatch) {
179
+ const raw = await options.fs.meta.seq.getEntry(path, dispatch.id);
180
+ if (!raw) return;
181
+ return decodeRecord(raw, dispatch);
182
+ }
183
+ async function savePrepared(options, path, dispatch, request) {
184
+ const rawRequest = JSON.stringify(request);
185
+ if (typeof rawRequest !== "string" || new TextEncoder().encode(rawRequest).byteLength > 1024 * 1024) throw new (0, _chunkRSH6QSECcjs.KanbanError)("INVALID_EXECUTION_REQUEST");
186
+ return options.fs.meta.seq.transaction(async (tx) => {
187
+ const raw = await tx.getEntry(path, dispatch.id);
188
+ if (raw) return decodeRecord(raw, dispatch);
189
+ const record = { schema: 1, fence: dispatch.fence, request: JSON.parse(rawRequest), digest: _chunkRSH6QSECcjs.sha256HexSync.call(void 0, rawRequest), phase: "prepared" };
190
+ await tx.setEntry(path, dispatch.id, JSON.stringify(record));
191
+ return record;
192
+ });
193
+ }
194
+ async function claimSubmission(options, path, dispatch) {
195
+ return options.fs.meta.seq.transaction(async (tx) => {
196
+ const raw = await tx.getEntry(path, dispatch.id);
197
+ if (!raw) throw new (0, _chunkRSH6QSECcjs.KanbanError)("EXECUTION_JOURNAL_MISSING");
198
+ const record = decodeRecord(raw, dispatch);
199
+ if (record.phase !== "prepared" || await tx.getEntry(path, "cancelled:" + dispatch.id)) return false;
200
+ await tx.setEntry(path, dispatch.id, JSON.stringify({ ...record, phase: "submitting" }));
201
+ return true;
202
+ });
203
+ }
204
+ function decodeRecord(raw, dispatch) {
205
+ const record = JSON.parse(raw);
206
+ if (record.schema !== 1 || record.fence !== dispatch.fence || !["prepared", "submitting"].includes(record.phase) || record.digest !== _chunkRSH6QSECcjs.sha256HexSync.call(void 0, JSON.stringify(record.request))) throw new (0, _chunkRSH6QSECcjs.KanbanError)("INVALID_EXECUTION_JOURNAL");
207
+ return record;
208
+ }
209
+ async function settle(options, dispatch, result) {
210
+ const current = (await options.service.workflow(options.boardId)).dispatches[dispatch.id];
211
+ if (!current || !["prepared", "running", "unknown"].includes(current.outcome)) return;
212
+ const board = await options.service.read(options.boardId), item = board.items.find((item2) => item2.id === dispatch.itemId);
213
+ const renew = ["running", "unknown"].includes(result.outcome) && (_nullishCoalesce(_optionalChain([item, 'optionalAccess', _25 => _25.owner, 'optionalAccess', _26 => _26.leaseUntil]), () => ( 0))) <= Date.now() + board.policy.leaseMs / 2;
214
+ if (!renew && !_chunk4XF6LQSUcjs.executionObservationChanged.call(void 0, current, result)) return;
215
+ const { outcome, outputs, code, executionSessionId, controlCapabilities } = result, observation = { outcome, ...controlCapabilities ? { controlCapabilities } : {}, ...executionSessionId ? { executionSessionId } : {}, ...outputs ? { outputs } : {}, ...code ? { code } : {} };
216
+ const digest = _chunkRSH6QSECcjs.sha256HexSync.call(void 0, JSON.stringify(observation));
217
+ await options.service.execute(board.id, {
218
+ operationId: `settle:${dispatch.id}:${digest}:${board.revision}`,
219
+ expectedRevision: board.revision,
220
+ command: { type: "observe-execution", dispatchId: dispatch.id, fence: dispatch.fence, ...observation }
221
+ });
222
+ }
223
+ async function linkSession(options, dispatch, sessionId) {
224
+ if (!sessionId || !options.executionReference) return;
225
+ const reference = options.executionReference(sessionId, dispatch.id);
226
+ const board = await options.service.read(options.boardId), item = board.items.find((item2) => item2.id === dispatch.itemId);
227
+ if (_optionalChain([item, 'optionalAccess', _27 => _27.owner, 'optionalAccess', _28 => _28.token]) !== dispatch.fence) throw new (0, _chunkRSH6QSECcjs.KanbanError)("STALE_EXECUTION_RESULT");
228
+ const current = _optionalChain([item, 'access', _29 => _29.externalRefs, 'optionalAccess', _30 => _30.filter, 'call', _31 => _31((ref) => ref.system === reference.system), 'access', _32 => _32.at, 'call', _33 => _33(-1)]);
229
+ if (_optionalChain([current, 'optionalAccess', _34 => _34.id]) === reference.id && current.revision === reference.revision) return;
230
+ await options.service.execute(board.id, {
231
+ operationId: `execution-session:${dispatch.id}:${_chunkRSH6QSECcjs.sha256HexSync.call(void 0, sessionId)}:${board.revision}`,
232
+ expectedRevision: board.revision,
233
+ command: { type: "link-reference", itemId: dispatch.itemId, reference }
234
+ });
235
+ }
236
+ function safeCode(error) {
237
+ const code = _nullishCoalesce(_optionalChain([error, 'optionalAccess', _35 => _35.reasonCode]), () => ( _optionalChain([error, 'optionalAccess', _36 => _36.code])));
238
+ return typeof code === "string" && /^[A-Z][A-Z0-9_]{0,79}$/.test(code) ? code : "EXECUTION_OBSERVATION_FAILED";
239
+ }
240
+ async function preventSubmission(options, path, dispatch) {
241
+ return options.fs.meta.seq.transaction(async (tx) => {
242
+ const raw = await tx.getEntry(path, dispatch.id);
243
+ if (raw && decodeRecord(raw, dispatch).phase === "submitting") return false;
244
+ await tx.setEntry(path, "cancelled:" + dispatch.id, dispatch.fence);
245
+ return true;
246
+ });
247
+ }
248
+
249
+ // src/monitor.ts
250
+ function startKanbanMonitor(options) {
251
+ let stopped = false, pending;
252
+ const tick = () => {
253
+ if (stopped || pending) return;
254
+ pending = Promise.resolve().then(() => stopped ? void 0 : options.service.scan()).then(() => stopped ? void 0 : _optionalChain([options, 'access', _37 => _37.onSuccess, 'optionalCall', _38 => _38()])).catch(async (error) => {
255
+ if (stopped) return;
256
+ try {
257
+ await options.onError(error);
258
+ } catch (e3) {
259
+ }
260
+ }).finally(() => {
261
+ pending = void 0;
262
+ });
263
+ };
264
+ const unsubscribe = options.subscribeTick(tick), unwatch = _optionalChain([options, 'access', _39 => _39.subscribeChanges, 'optionalCall', _40 => _40(tick)]);
265
+ if (options.initialScan !== false) tick();
266
+ return { async dispose() {
267
+ if (stopped) {
268
+ await pending;
269
+ return;
270
+ }
271
+ stopped = true;
272
+ try {
273
+ unsubscribe();
274
+ _optionalChain([unwatch, 'optionalCall', _41 => _41()]);
275
+ } finally {
276
+ await pending;
277
+ }
278
+ } };
279
+ }
280
+
281
+ // src/work-messages.ts
282
+ var _vfscore = require('@nestos/vfs-core');
283
+ async function consumeWorkMessages(fs, directory, scope, journal, deliver, decode, publishReply) {
284
+ const publications = await readPublications(fs, directory, scope, decode), indexKey = "workspace-message-index:" + scope.attemptId;
285
+ const known = await _asyncNullishCoalesce(await journal.get(indexKey), async () => ( []));
286
+ if (known.some((id) => !publications.some((p) => p.message.id === id))) throw protocolError("WORK_MESSAGE_MISSING", "Published workspace message is missing", "ECONFLICT");
287
+ unresolvedWorkIssues(publications.map((p) => p.message));
288
+ publications.sort((a, b) => Number(a.message.type === "resolved") - Number(b.message.type === "resolved"));
289
+ for (const { message, bytes } of publications) {
290
+ const digest = _chunkRSH6QSECcjs.sha256HexSync.call(void 0, bytes);
291
+ const delivered = await deliverPublication(journal, indexKey, message, digest, deliver);
292
+ if (delivered) await publishReply(directory + "/outbox", "ack-" + message.id + ".yaml", JSON.stringify({ schema: "work-reply/1", ...scope, id: "ack-" + message.id, type: "accepted", replyTo: message.id, content: "Accepted", digest }));
293
+ }
294
+ return publications.map((p) => p.message);
295
+ }
296
+ async function readPublications(fs, directory, scope, decode) {
297
+ const files = await fs.driver.getChildren(directory + "/inbox", { fields: "entry" }), publications = [];
298
+ if (files.length > 200) throw protocolError("WORK_MESSAGE_COUNT_LIMIT", "Workspace inbox exceeds 200 entries");
299
+ for (const file of files.sort((a, b) => a.name.localeCompare(b.name))) {
300
+ if (file.type !== "file" || !/^[a-zA-Z0-9_-]{1,128}\.(yaml|yml|json)$/.test(file.name)) continue;
301
+ const bytes = await readMessageBytes(fs, directory + "/inbox/" + file.name);
302
+ const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes), envelope = decode(text);
303
+ if (_optionalChain([envelope, 'optionalAccess', _42 => _42.schema]) === "work-message/2" && Object.hasOwn(envelope, "scopeId")) continue;
304
+ const message = parseWorkMessage(text, scope, decode);
305
+ if (file.name.slice(0, file.name.lastIndexOf(".")) !== message.id) throw protocolError("WORK_MESSAGE_FILENAME_MISMATCH", "Workspace message filename does not match its identity");
306
+ publications.push({ message, bytes });
307
+ }
308
+ return publications;
309
+ }
310
+ async function deliverPublication(journal, indexKey, message, digest, deliver) {
311
+ const key = "workspace-message:" + message.attemptId + ":" + message.id;
312
+ const receipt = await journal.update(key, (old) => {
313
+ if (old && old.digest !== digest) throw protocolError("WORK_MESSAGE_CHANGED", "Published workspace message changed", "ECONFLICT");
314
+ return _nullishCoalesce(old, () => ( { digest, message, delivered: false }));
315
+ });
316
+ await journal.update(indexKey, (old) => _optionalChain([old, 'optionalAccess', _43 => _43.includes, 'call', _44 => _44(message.id)]) ? old : [..._nullishCoalesce(old, () => ( [])), message.id]);
317
+ if (receipt.delivered) return true;
318
+ const outcome = await deliver(receipt.message);
319
+ if (_optionalChain([outcome, 'optionalAccess', _45 => _45.pending])) return false;
320
+ await journal.update(key, (old) => ({ ...old, delivered: true }));
321
+ return true;
322
+ }
323
+ function parseWorkMessage(text, scope, decode) {
324
+ let value;
325
+ try {
326
+ value = decode(text);
327
+ } catch (e4) {
328
+ throw protocolError("WORK_MESSAGE_YAML_INVALID", "Workspace message is not valid bounded YAML");
329
+ }
330
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw protocolError("WORK_MESSAGE_SCHEMA_INVALID", "Workspace message schema is invalid");
331
+ if ("body" in value) {
332
+ if (Object.hasOwn(value, "content")) throw protocolError("WORK_MESSAGE_FIELDS_INVALID", "Workspace message must not contain both body and content");
333
+ value = { ...value, content: value.body };
334
+ }
335
+ if (Object.keys(value).some((key) => !["schema", "runId", "itemId", "attemptId", "id", "type", "content", "body", "replyTo"].includes(key)))
336
+ throw protocolError("WORK_MESSAGE_FIELDS_INVALID", "Workspace message contains undeclared fields");
337
+ validateEnvelope(value, scope);
338
+ return value;
339
+ }
340
+ function validateEnvelope(value, scope) {
341
+ if (!["work-message/1", "work-message/2"].includes(value.schema)) throw protocolError("WORK_MESSAGE_SCHEMA_INVALID", "Workspace message schema is unsupported");
342
+ if (!["progress", "clarification", "error", "result", ...value.schema === "work-message/2" ? ["resolved"] : []].includes(value.type))
343
+ throw protocolError("WORK_MESSAGE_TYPE_INVALID", "Workspace message schema does not permit this type");
344
+ if (!validId(value.id)) throw protocolError("WORK_MESSAGE_ID_INVALID", "Workspace message identity is invalid");
345
+ if (["runId", "itemId", "attemptId"].some((key) => value[key] !== scope[key]))
346
+ throw protocolError("WORK_MESSAGE_SCOPE_MISMATCH", "Workspace message execution scope is invalid");
347
+ if (typeof value.content !== "string" || !value.content.trim() || value.content.length > 8e3) throw protocolError("WORK_MESSAGE_CONTENT_INVALID", "Workspace message content is invalid");
348
+ if (value.replyTo !== void 0 && !validId(value.replyTo) || value.type === "resolved" && !value.replyTo)
349
+ throw protocolError("WORK_MESSAGE_REPLY_INVALID", "Workspace message reply target is invalid");
350
+ }
351
+ function validId(value) {
352
+ return typeof value === "string" && /^[a-zA-Z0-9_-]{1,128}$/.test(value);
353
+ }
354
+ function protocolError(reasonCode, message, code = "EINVAL") {
355
+ return Object.assign(new (0, _vfscore.FSError)(code, message), { reasonCode });
356
+ }
357
+ async function readMessageBytes(fs, path) {
358
+ const before = await fs.driver.getNode(path);
359
+ if (_optionalChain([before, 'optionalAccess', _46 => _46.type]) !== "file" || !Number.isSafeInteger(before.size) || before.size < 1) throw protocolError("WORK_MESSAGE_FILE_INVALID", "Workspace message is not a nonempty regular file");
360
+ if (before.size > 65536) throw protocolError("WORK_MESSAGE_SIZE_LIMIT", "Workspace message exceeds 64 KiB");
361
+ const bytes = await fs.driver.readContent(path, { encoding: "binary", length: before.size });
362
+ const after = await fs.driver.getNode(path);
363
+ if (bytes.byteLength !== before.size || _optionalChain([after, 'optionalAccess', _47 => _47.type]) !== "file" || after.size !== before.size || after.revision !== before.revision || after.modifiedAt !== before.modifiedAt)
364
+ throw protocolError("WORK_MESSAGE_READ_CONFLICT", "Workspace message changed while reading", "ECONFLICT");
365
+ return bytes;
366
+ }
367
+ function unresolvedWorkIssues(messages) {
368
+ const issues = messages.filter((m) => m.type === "clarification" || m.type === "error");
369
+ for (const resolution of messages.filter((m) => m.type === "resolved")) {
370
+ if (!issues.some((issue) => issue.id === resolution.replyTo)) throw Object.assign(new Error("Resolution target is not an issue in this attempt"), { code: "WORKSPACE_RESOLUTION_INVALID" });
371
+ }
372
+ return issues.filter((issue) => !messages.some((m) => m.type === "resolved" && m.replyTo === issue.id));
373
+ }
374
+
375
+
376
+
377
+
378
+
379
+
380
+
381
+ exports.createKanbanExecutionBridge = createKanbanExecutionBridge; exports.startKanbanMonitor = startKanbanMonitor; exports.consumeWorkMessages = consumeWorkMessages; exports.parseWorkMessage = parseWorkMessage; exports.unresolvedWorkIssues = unresolvedWorkIssues;
382
+ //# sourceMappingURL=chunk-XBSGUVCY.cjs.map