@starlove/orbit 1.4.0 → 2.0.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.
- package/Dockerfile +10 -8
- package/README.md +146 -164
- package/dist/full/csp.txt +1 -0
- package/dist/full/fonts/JetBrains_Mono/JetBrainsMono-subset.woff2 +0 -0
- package/dist/full/fonts/JetBrains_Mono/README.txt +5 -0
- package/dist/full/fonts/Mona_Sans/MonaSans-subset.woff2 +0 -0
- package/{public/fonts/Open_Sans → dist/full/fonts/Mona_Sans}/OFL.txt +93 -93
- package/dist/full/fonts/Mona_Sans/README.txt +10 -0
- package/{public/fonts/JetBrains_Mono → dist/full/fonts/Newsreader}/OFL.txt +93 -93
- package/dist/full/fonts/Newsreader/README.txt +8 -0
- package/dist/full/fonts/Newsreader/newsreader.woff2 +0 -0
- package/dist/full/index.html +36 -653
- package/dist/full/js/app.BmuponPJ.js +32 -0
- package/dist/full/js/app.BmuponPJ.js.map +1 -0
- package/dist/full/js/chunk.1fJGQGbW.js +2 -0
- package/dist/full/js/chunk.1fJGQGbW.js.map +1 -0
- package/dist/full/js/chunk.5D7JO2zp.js +2 -0
- package/dist/full/js/chunk.5D7JO2zp.js.map +1 -0
- package/dist/full/js/chunk.BrNn_D79.js +2 -0
- package/dist/full/js/chunk.BrNn_D79.js.map +1 -0
- package/dist/full/js/chunk.CokJXIqM.js +2 -0
- package/dist/full/js/chunk.CokJXIqM.js.map +1 -0
- package/dist/full/js/chunk.D_Haqy6R.js +2 -0
- package/dist/full/js/chunk.D_Haqy6R.js.map +1 -0
- package/dist/full/js/chunk.RO0jsH6I.js +15 -0
- package/dist/full/js/chunk.RO0jsH6I.js.map +1 -0
- package/dist/full/js/chunk.dqWOEtBM.js +2 -0
- package/dist/full/js/chunk.dqWOEtBM.js.map +1 -0
- package/dist/full/js/warm.f260bbbc.js +15 -0
- package/dist/full/orbits-favicon.png +0 -0
- package/dist/full/orbits-logo-bg-wide.png +0 -0
- package/dist/full/orbits-logo-bg.png +0 -0
- package/dist/full/orbits-logo.png +0 -0
- package/dist/full/styles.2b154ffe.css +1 -0
- package/dist/full/vendor/starlove-ui/theme-init.js +66 -0
- package/dist/full/version.json +2 -2
- package/docs/AGENT_GUIDE.md +114 -0
- package/docs/UPDATING.md +249 -64
- package/lib/adapters/indexeddb/blobs.d.ts +3 -0
- package/lib/adapters/indexeddb/blobs.js +54 -0
- package/lib/adapters/indexeddb/database.d.ts +60 -0
- package/lib/adapters/indexeddb/database.js +126 -0
- package/lib/adapters/indexeddb/index.d.ts +5 -0
- package/lib/adapters/indexeddb/index.js +3 -0
- package/lib/adapters/indexeddb/locks.d.ts +14 -0
- package/lib/adapters/indexeddb/locks.js +40 -0
- package/lib/adapters/indexeddb/store.d.ts +40 -0
- package/lib/adapters/indexeddb/store.js +424 -0
- package/lib/adapters/indexeddb/tables.d.ts +43 -0
- package/lib/adapters/indexeddb/tables.js +127 -0
- package/lib/adapters/memory/blobs.d.ts +3 -0
- package/lib/adapters/memory/blobs.js +44 -0
- package/lib/adapters/memory/board.d.ts +41 -0
- package/lib/adapters/memory/board.js +521 -0
- package/lib/adapters/memory/index.d.ts +2 -0
- package/lib/adapters/memory/index.js +2 -0
- package/lib/adapters/memory/search.d.ts +6 -0
- package/lib/adapters/memory/search.js +30 -0
- package/lib/adapters/memory/store.d.ts +34 -0
- package/lib/adapters/memory/store.js +266 -0
- package/lib/adapters/sqlite/backups.d.ts +53 -0
- package/lib/adapters/sqlite/backups.js +232 -0
- package/lib/adapters/sqlite/blobs.d.ts +14 -0
- package/lib/adapters/sqlite/blobs.js +109 -0
- package/lib/adapters/sqlite/board.d.ts +77 -0
- package/lib/adapters/sqlite/board.js +328 -0
- package/lib/adapters/sqlite/database.d.ts +67 -0
- package/lib/adapters/sqlite/database.js +285 -0
- package/lib/adapters/sqlite/index.d.ts +3 -0
- package/lib/adapters/sqlite/index.js +2 -0
- package/lib/adapters/sqlite/lock.d.ts +11 -0
- package/lib/adapters/sqlite/lock.js +34 -0
- package/lib/adapters/sqlite/records.d.ts +54 -0
- package/lib/adapters/sqlite/records.js +546 -0
- package/lib/adapters/sqlite/schema.d.ts +54 -0
- package/lib/adapters/sqlite/schema.js +276 -0
- package/lib/adapters/sqlite/store.d.ts +39 -0
- package/lib/adapters/sqlite/store.js +385 -0
- package/lib/browser/in-page.d.ts +31 -0
- package/lib/browser/in-page.js +46 -0
- package/lib/browser/old-preview.d.ts +13 -0
- package/lib/browser/old-preview.js +80 -0
- package/lib/browser/preview-board.d.ts +8 -0
- package/lib/browser/preview-board.js +69 -0
- package/lib/cli/main.d.ts +7 -0
- package/lib/cli/main.js +343 -0
- package/lib/cli/orbit.d.ts +8 -0
- package/lib/cli/orbit.js +25 -0
- package/lib/client/http.d.ts +38 -0
- package/lib/client/http.js +334 -0
- package/lib/client/local.d.ts +26 -0
- package/lib/client/local.js +190 -0
- package/lib/client/object-url.d.ts +8 -0
- package/lib/client/object-url.js +21 -0
- package/lib/client/types.d.ts +268 -0
- package/lib/client/types.js +8 -0
- package/lib/commands/agent-tools.d.ts +31 -0
- package/lib/commands/agent-tools.js +100 -0
- package/lib/commands/index.d.ts +56 -0
- package/lib/commands/index.js +298 -0
- package/lib/commands/parse.d.ts +73 -0
- package/lib/commands/parse.js +231 -0
- package/lib/commands/present.d.ts +134 -0
- package/lib/commands/present.js +198 -0
- package/lib/commands/session.d.ts +85 -0
- package/lib/commands/session.js +138 -0
- package/lib/commands/verbs.d.ts +35 -0
- package/lib/commands/verbs.js +842 -0
- package/lib/core/actors.d.ts +18 -0
- package/lib/core/actors.js +45 -0
- package/lib/core/agent.d.ts +128 -0
- package/lib/core/agent.js +323 -0
- package/lib/core/boards.d.ts +113 -0
- package/lib/core/boards.js +288 -0
- package/lib/core/bootstrap.d.ts +47 -0
- package/lib/core/bootstrap.js +100 -0
- package/lib/core/bytes.d.ts +10 -0
- package/lib/core/bytes.js +52 -0
- package/lib/core/card-paths.d.ts +25 -0
- package/lib/core/card-paths.js +44 -0
- package/lib/core/context.d.ts +60 -0
- package/lib/core/context.js +42 -0
- package/lib/core/cross-board.d.ts +81 -0
- package/lib/core/cross-board.js +228 -0
- package/lib/core/describe.d.ts +21 -0
- package/lib/core/describe.js +81 -0
- package/lib/core/errors.d.ts +159 -0
- package/lib/core/errors.js +160 -0
- package/lib/core/files.d.ts +79 -0
- package/lib/core/files.js +262 -0
- package/lib/core/history.d.ts +20 -0
- package/lib/core/history.js +53 -0
- package/lib/core/image-types.d.ts +16 -0
- package/lib/core/image-types.js +36 -0
- package/lib/core/model.d.ts +263 -0
- package/lib/core/model.js +34 -0
- package/lib/core/nebula-link.d.ts +14 -0
- package/lib/core/nebula-link.js +39 -0
- package/lib/core/relations.d.ts +58 -0
- package/lib/core/relations.js +112 -0
- package/lib/core/search.d.ts +38 -0
- package/lib/core/search.js +92 -0
- package/lib/core/sessions.d.ts +44 -0
- package/lib/core/sessions.js +158 -0
- package/lib/core/snapshot-schema.d.ts +295 -0
- package/lib/core/snapshot-schema.js +82 -0
- package/lib/core/snapshots.d.ts +75 -0
- package/lib/core/snapshots.js +409 -0
- package/lib/core/states.d.ts +25 -0
- package/lib/core/states.js +126 -0
- package/lib/core/text.d.ts +40 -0
- package/lib/core/text.js +113 -0
- package/lib/core/tickets.d.ts +69 -0
- package/lib/core/tickets.js +311 -0
- package/lib/core/trello.d.ts +17 -0
- package/lib/core/trello.js +269 -0
- package/lib/core/verdicts.d.ts +28 -0
- package/lib/core/verdicts.js +92 -0
- package/lib/core/views.d.ts +95 -0
- package/lib/core/views.js +186 -0
- package/lib/http/app.d.ts +46 -0
- package/lib/http/app.js +491 -0
- package/lib/mcp-server.d.ts +10 -0
- package/lib/mcp-server.js +120 -0
- package/lib/node/assets.d.ts +7 -0
- package/lib/node/assets.js +68 -0
- package/lib/node/data-folder.d.ts +21 -0
- package/lib/node/data-folder.js +34 -0
- package/lib/node/env.d.ts +38 -0
- package/lib/node/env.js +49 -0
- package/lib/node/host-check.d.ts +24 -0
- package/lib/node/host-check.js +53 -0
- package/lib/node/host-services.d.ts +9 -0
- package/lib/node/host-services.js +150 -0
- package/lib/node/local.d.ts +22 -0
- package/lib/node/local.js +61 -0
- package/lib/node/migrate.d.ts +53 -0
- package/lib/node/migrate.js +635 -0
- package/lib/node/package.d.ts +7 -0
- package/lib/node/package.js +14 -0
- package/lib/node/repo-board.d.ts +49 -0
- package/lib/node/repo-board.js +245 -0
- package/lib/node/serve.d.ts +8 -0
- package/lib/node/serve.js +106 -0
- package/lib/orbit.d.ts +47 -0
- package/lib/orbit.js +44 -0
- package/lib/quiet-sqlite-warning.d.ts +1 -0
- package/lib/quiet-sqlite-warning.js +18 -0
- package/lib/store/blob-path.d.ts +6 -0
- package/lib/store/blob-path.js +12 -0
- package/lib/store/conformance.d.ts +52 -0
- package/lib/store/conformance.js +1449 -0
- package/lib/store/search-words.d.ts +10 -0
- package/lib/store/search-words.js +19 -0
- package/lib/store/types.d.ts +452 -0
- package/lib/store/types.js +34 -0
- package/llms.txt +63 -0
- package/package.json +109 -25
- package/src/cli/orbit.js +11 -658
- package/src/mcp-server.js +16 -876
- package/SKILL-ORBIT.md +0 -151
- package/build.mjs +0 -210
- package/dist/full/js/app.62905fcb.js +0 -952
- package/dist/full/js/app.62905fcb.js.map +0 -7
- package/dist/full/styles.4e60d824.css +0 -1
- package/docs/AGENT_PROTOCOL.md +0 -424
- package/docs/MCP_SETUP.md +0 -54
- package/docs/ORBIT_DISPATCH.md +0 -280
- package/docs/SS-Orbit-Dark.png +0 -0
- package/docs/SS-Orbit-Light.png +0 -0
- package/docs/mcp-client.example.json +0 -13
- package/docs/setup/orbit-init-wizard.md +0 -174
- package/public/apple-touch-icon.png +0 -0
- package/public/cursors/live_arrow.cur +0 -0
- package/public/cursors/live_link.cur +0 -0
- package/public/cursors/live_move.cur +0 -0
- package/public/cursors/live_select.cur +0 -0
- package/public/cursors/live_unavail.cur +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-Bold.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-BoldItalic.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-Italic.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-Light.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-LightItalic.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-Medium.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-MediumItalic.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-Regular.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-SemiBold.ttf +0 -0
- package/public/fonts/Chakra_Petch/ChakraPetch-SemiBoldItalic.ttf +0 -0
- package/public/fonts/Chakra_Petch/OFL.txt +0 -93
- package/public/fonts/Inter/Inter-Italic-VariableFont_opsz,wght.ttf +0 -0
- package/public/fonts/Inter/Inter-VariableFont_opsz,wght.ttf +0 -0
- package/public/fonts/Inter/OFL.txt +0 -93
- package/public/fonts/Inter/README.txt +0 -118
- package/public/fonts/JetBrains_Mono/JetBrainsMono-Italic-VariableFont_wght.ttf +0 -0
- package/public/fonts/JetBrains_Mono/JetBrainsMono-VariableFont_wght.ttf +0 -0
- package/public/fonts/JetBrains_Mono/README.txt +0 -79
- package/public/fonts/OpenRing-Regular.ttf +0 -0
- package/public/fonts/Open_Sans/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
- package/public/fonts/Open_Sans/OpenSans-VariableFont_wdth,wght.ttf +0 -0
- package/public/fonts/Open_Sans/README.txt +0 -100
- package/public/fonts/Orbitron/OFL.txt +0 -93
- package/public/fonts/Orbitron/Orbitron-VariableFont_wght.ttf +0 -0
- package/public/fonts/Orbitron/README.txt +0 -68
- package/public/fonts/Rajdhani/OFL.txt +0 -93
- package/public/fonts/Rajdhani/Rajdhani-Bold.ttf +0 -0
- package/public/fonts/Rajdhani/Rajdhani-Light.ttf +0 -0
- package/public/fonts/Rajdhani/Rajdhani-Medium.ttf +0 -0
- package/public/fonts/Rajdhani/Rajdhani-Regular.ttf +0 -0
- package/public/fonts/Rajdhani/Rajdhani-SemiBold.ttf +0 -0
- package/public/fonts/runescape-uf.ttf +0 -0
- package/public/index.html +0 -769
- package/public/js/actor-labels.js +0 -65
- package/public/js/api.js +0 -39
- package/public/js/app.js +0 -45
- package/public/js/artifact-drawer.js +0 -79
- package/public/js/board-export.js +0 -161
- package/public/js/board-menu.js +0 -374
- package/public/js/card-actions.js +0 -545
- package/public/js/config.js +0 -20
- package/public/js/create-card.js +0 -190
- package/public/js/dom.js +0 -19
- package/public/js/drawer.js +0 -273
- package/public/js/font-preference.js +0 -229
- package/public/js/format.js +0 -433
- package/public/js/help-menu.js +0 -31
- package/public/js/icons.js +0 -16
- package/public/js/kanban.js +0 -694
- package/public/js/local-backend.js +0 -1015
- package/public/js/main.js +0 -161
- package/public/js/modal.js +0 -33
- package/public/js/motion-preference.js +0 -64
- package/public/js/panel.js +0 -58
- package/public/js/router.js +0 -134
- package/public/js/search.js +0 -56
- package/public/js/settings-tabs.js +0 -7
- package/public/js/settings.js +0 -1634
- package/public/js/sse.js +0 -159
- package/public/js/starscape.js +0 -332
- package/public/js/state.js +0 -129
- package/public/js/text-editing.js +0 -128
- package/public/js/theme-preference.js +0 -95
- package/public/js/ticket-detail.js +0 -1516
- package/public/js/ticket-typeahead.js +0 -49
- package/public/js/toast.js +0 -89
- package/public/js/topbar-notes.js +0 -155
- package/public/js/transport.js +0 -22
- package/public/js/typeahead.js +0 -206
- package/public/js/unread.js +0 -121
- package/public/js/update-check.js +0 -58
- package/public/js/url-routes.js +0 -94
- package/public/orbits-favicon.png +0 -0
- package/public/orbits-logo-bg-wide.png +0 -0
- package/public/orbits-logo-bg.png +0 -0
- package/public/orbits-logo.png +0 -0
- package/public/styles.css +0 -7709
- package/src/core/agent.js +0 -387
- package/src/core/agents-md.js +0 -109
- package/src/core/artifacts.js +0 -149
- package/src/core/attachments.js +0 -238
- package/src/core/auth.js +0 -85
- package/src/core/backups.js +0 -142
- package/src/core/board.js +0 -148
- package/src/core/boards.js +0 -409
- package/src/core/bootstrap.js +0 -170
- package/src/core/css-bundle.js +0 -30
- package/src/core/db.js +0 -315
- package/src/core/delete-board.js +0 -163
- package/src/core/dispatch.js +0 -1007
- package/src/core/events.js +0 -314
- package/src/core/http.js +0 -115
- package/src/core/migrate.js +0 -198
- package/src/core/paths.js +0 -55
- package/src/core/provision-repo-board.js +0 -119
- package/src/core/queries.js +0 -246
- package/src/core/registry.js +0 -211
- package/src/core/relations.js +0 -163
- package/src/core/review-verdicts.js +0 -138
- package/src/core/router.js +0 -556
- package/src/core/search.js +0 -183
- package/src/core/seed.js +0 -234
- package/src/core/snapshots.js +0 -692
- package/src/core/states.js +0 -167
- package/src/core/system-picker.js +0 -109
- package/src/core/tickets.js +0 -400
- package/src/core/util.js +0 -106
- package/src/core/version-check.js +0 -154
- package/src/mcp/orbit-client.js +0 -287
- package/src/server.js +0 -115
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One board's records in memory, and the reader and writer over them. The
|
|
3
|
+
* tables are arrays in the order records were written, so "then by when it
|
|
4
|
+
* was written" is array order, and every sort here is stable.
|
|
5
|
+
*/
|
|
6
|
+
import { OrbitError } from "../../core/errors.js";
|
|
7
|
+
import { matches } from "./search.js";
|
|
8
|
+
export function emptyTables() {
|
|
9
|
+
return {
|
|
10
|
+
board: null,
|
|
11
|
+
states: [],
|
|
12
|
+
labels: [],
|
|
13
|
+
tickets: [],
|
|
14
|
+
ticketLabels: [],
|
|
15
|
+
comments: [],
|
|
16
|
+
relations: [],
|
|
17
|
+
crossBoardRelations: [],
|
|
18
|
+
entries: [],
|
|
19
|
+
verdicts: [],
|
|
20
|
+
attachments: [],
|
|
21
|
+
events: [],
|
|
22
|
+
search: new Map()
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Every call gives other callers a turn first, as a real database's I/O does,
|
|
27
|
+
* so the conformance cases see the interleavings an adapter has to survive.
|
|
28
|
+
*/
|
|
29
|
+
const yieldTurn = () => Promise.resolve();
|
|
30
|
+
const copy = (record) => ({ ...record });
|
|
31
|
+
/** Sets the fields a patch names; a field given as undefined is not named. */
|
|
32
|
+
function apply(record, patch) {
|
|
33
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
34
|
+
if (value !== undefined)
|
|
35
|
+
Object.assign(record, { [key]: value });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const copies = (records) => records.map(copy);
|
|
39
|
+
/** Code-point order, as SQLite compares text. */
|
|
40
|
+
export function byCodePoint(a, b) {
|
|
41
|
+
let at = 0;
|
|
42
|
+
while (at < a.length && at < b.length) {
|
|
43
|
+
const left = a.codePointAt(at) ?? 0;
|
|
44
|
+
const right = b.codePointAt(at) ?? 0;
|
|
45
|
+
if (left !== right)
|
|
46
|
+
return left - right;
|
|
47
|
+
at += left > 0xffff ? 2 : 1;
|
|
48
|
+
}
|
|
49
|
+
return a.length - b.length;
|
|
50
|
+
}
|
|
51
|
+
const byCreated = (records) => [...records].sort((a, b) => (a.created_at < b.created_at ? -1 : a.created_at > b.created_at ? 1 : 0));
|
|
52
|
+
const asciiLower = (text) => text.replace(/[A-Z]/g, (letter) => letter.toLowerCase());
|
|
53
|
+
function find(records, id) {
|
|
54
|
+
return records.find((record) => record.id === id);
|
|
55
|
+
}
|
|
56
|
+
function filterTickets(tickets, filter) {
|
|
57
|
+
const ids = filter.ids ? new Set(filter.ids) : null;
|
|
58
|
+
return tickets
|
|
59
|
+
.filter((ticket) => (filter.archived === undefined || (ticket.archived_at !== null) === filter.archived) &&
|
|
60
|
+
(filter.parent === undefined || ticket.parent_ticket_id === filter.parent) &&
|
|
61
|
+
(filter.state === undefined || ticket.state_id === filter.state) &&
|
|
62
|
+
(ids === null || ids.has(ticket.id)))
|
|
63
|
+
.sort((a, b) => a.number - b.number);
|
|
64
|
+
}
|
|
65
|
+
/** A reader over `tables()`: committed tables for a read, the draft for a write. */
|
|
66
|
+
export function reader(tables, ready = yieldTurn) {
|
|
67
|
+
const t = async () => {
|
|
68
|
+
await ready();
|
|
69
|
+
return tables();
|
|
70
|
+
};
|
|
71
|
+
return {
|
|
72
|
+
board: {
|
|
73
|
+
async get() {
|
|
74
|
+
const { board } = await t();
|
|
75
|
+
if (!board)
|
|
76
|
+
throw new OrbitError("board_not_found");
|
|
77
|
+
return copy(board);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
states: {
|
|
81
|
+
async list() {
|
|
82
|
+
return copies([...(await t()).states].sort((a, b) => a.position - b.position));
|
|
83
|
+
},
|
|
84
|
+
async get(id) {
|
|
85
|
+
const state = find((await t()).states, id);
|
|
86
|
+
return state ? copy(state) : null;
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
labels: {
|
|
90
|
+
async list() {
|
|
91
|
+
return copies([...(await t()).labels].sort((a, b) => byCodePoint(a.name, b.name)));
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
tickets: {
|
|
95
|
+
async get(id) {
|
|
96
|
+
const ticket = find((await t()).tickets, id);
|
|
97
|
+
return ticket ? copy(ticket) : null;
|
|
98
|
+
},
|
|
99
|
+
async byNumber(number) {
|
|
100
|
+
const ticket = (await t()).tickets.find((candidate) => candidate.number === number);
|
|
101
|
+
return ticket ? copy(ticket) : null;
|
|
102
|
+
},
|
|
103
|
+
async byTitle(title) {
|
|
104
|
+
const wanted = asciiLower(title);
|
|
105
|
+
const found = (await t()).tickets
|
|
106
|
+
.filter((ticket) => asciiLower(ticket.title) === wanted)
|
|
107
|
+
.sort((a, b) => Number(a.archived_at !== null) - Number(b.archived_at !== null) || b.number - a.number)[0];
|
|
108
|
+
return found ? copy(found) : null;
|
|
109
|
+
},
|
|
110
|
+
async list(filter = {}) {
|
|
111
|
+
return copies(filterTickets((await t()).tickets, filter));
|
|
112
|
+
},
|
|
113
|
+
async lastNumber() {
|
|
114
|
+
return (await t()).tickets.reduce((top, ticket) => Math.max(top, ticket.number), 0);
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
ticketLabels: {
|
|
118
|
+
async list(ticketIds) {
|
|
119
|
+
const { ticketLabels } = await t();
|
|
120
|
+
const wanted = ticketIds ? new Set(ticketIds) : null;
|
|
121
|
+
return copies(ticketLabels.filter((link) => wanted === null || wanted.has(link.ticket_id)));
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
comments: {
|
|
125
|
+
async get(id) {
|
|
126
|
+
const comment = find((await t()).comments, id);
|
|
127
|
+
return comment ? copy(comment) : null;
|
|
128
|
+
},
|
|
129
|
+
async list(ticketId) {
|
|
130
|
+
const all = (await t()).comments;
|
|
131
|
+
return copies(byCreated(ticketId === undefined ? all : all.filter((comment) => comment.ticket_id === ticketId)));
|
|
132
|
+
},
|
|
133
|
+
async countByTicket(options = {}) {
|
|
134
|
+
const counts = new Map();
|
|
135
|
+
for (const comment of (await t()).comments) {
|
|
136
|
+
if (options.forAgents && comment.omitted_for_ai === 1)
|
|
137
|
+
continue;
|
|
138
|
+
if (options.exceptBody !== undefined && comment.body === options.exceptBody)
|
|
139
|
+
continue;
|
|
140
|
+
counts.set(comment.ticket_id, (counts.get(comment.ticket_id) ?? 0) + 1);
|
|
141
|
+
}
|
|
142
|
+
return [...counts].map(([ticket_id, count]) => ({ ticket_id, count }));
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
relations: {
|
|
146
|
+
async get(id) {
|
|
147
|
+
const relation = find((await t()).relations, id);
|
|
148
|
+
return relation ? copy(relation) : null;
|
|
149
|
+
},
|
|
150
|
+
async list(ticketId) {
|
|
151
|
+
const all = (await t()).relations;
|
|
152
|
+
return copies(byCreated(ticketId === undefined ? all : all.filter((r) => r.source_ticket_id === ticketId || r.target_ticket_id === ticketId)));
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
crossBoardRelations: {
|
|
156
|
+
async list(ticketId) {
|
|
157
|
+
const all = (await t()).crossBoardRelations;
|
|
158
|
+
return copies(byCreated(ticketId === undefined ? all : all.filter((r) => r.source_ticket_id === ticketId)));
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
entries: {
|
|
162
|
+
async get(id) {
|
|
163
|
+
const entry = find((await t()).entries, id);
|
|
164
|
+
return entry ? copy(entry) : null;
|
|
165
|
+
},
|
|
166
|
+
async list() {
|
|
167
|
+
return copies(byCreated((await t()).entries));
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
verdicts: {
|
|
171
|
+
async get(id) {
|
|
172
|
+
const verdict = find((await t()).verdicts, id);
|
|
173
|
+
return verdict ? copy(verdict) : null;
|
|
174
|
+
},
|
|
175
|
+
async list(ticketId) {
|
|
176
|
+
const all = (await t()).verdicts;
|
|
177
|
+
return copies(byCreated(ticketId === undefined ? all : all.filter((v) => v.ticket_id === ticketId)));
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
attachments: {
|
|
181
|
+
async get(id) {
|
|
182
|
+
const attachment = find((await t()).attachments, id);
|
|
183
|
+
return attachment ? copy(attachment) : null;
|
|
184
|
+
},
|
|
185
|
+
async list(ticketId) {
|
|
186
|
+
const all = (await t()).attachments;
|
|
187
|
+
return copies(byCreated(ticketId === undefined ? all : all.filter((a) => a.ticket_id === ticketId)));
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
events: {
|
|
191
|
+
async list(filter = {}) {
|
|
192
|
+
const types = filter.types ? new Set(filter.types) : null;
|
|
193
|
+
return copies(byCreated((await t()).events.filter((event) => (filter.ticketId === undefined || event.ticket_id === filter.ticketId) &&
|
|
194
|
+
(types === null || types.has(event.type)) &&
|
|
195
|
+
(filter.since === undefined || event.created_at >= filter.since))));
|
|
196
|
+
},
|
|
197
|
+
async latest(ticketIds, types) {
|
|
198
|
+
const wanted = new Set(ticketIds);
|
|
199
|
+
const kinds = new Set(types);
|
|
200
|
+
const newest = new Map();
|
|
201
|
+
for (const event of byCreated((await t()).events)) {
|
|
202
|
+
if (event.ticket_id !== null && wanted.has(event.ticket_id) && kinds.has(event.type))
|
|
203
|
+
newest.set(event.ticket_id, event);
|
|
204
|
+
}
|
|
205
|
+
return copies([...newest.values()]);
|
|
206
|
+
},
|
|
207
|
+
async after(id, limit) {
|
|
208
|
+
const { events } = await t();
|
|
209
|
+
const at = events.findIndex((event) => event.id === id);
|
|
210
|
+
return at < 0 ? [] : copies(events.slice(at + 1, at + 1 + limit));
|
|
211
|
+
},
|
|
212
|
+
async last() {
|
|
213
|
+
const { events } = await t();
|
|
214
|
+
const event = events[events.length - 1];
|
|
215
|
+
return event ? copy(event) : null;
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
search: {
|
|
219
|
+
async query(text, options) {
|
|
220
|
+
const { search, tickets } = await t();
|
|
221
|
+
const open = filterTickets(tickets, { archived: false });
|
|
222
|
+
return open
|
|
223
|
+
.filter((ticket) => {
|
|
224
|
+
const document = search.get(ticket.id);
|
|
225
|
+
return document !== undefined && matches(document, text);
|
|
226
|
+
})
|
|
227
|
+
.slice(0, options.limit)
|
|
228
|
+
.map((ticket) => ({ ticket_id: ticket.id, rank: 0 }));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* A writer over a draft. Every check the port names is made here, in the
|
|
235
|
+
* order the port lists them, so a write that breaks one throws before it
|
|
236
|
+
* changes anything.
|
|
237
|
+
*/
|
|
238
|
+
export function writer(boardId, draft, log, checks, guard) {
|
|
239
|
+
const read = reader(() => draft, guard);
|
|
240
|
+
const fresh = (records, record) => {
|
|
241
|
+
if (find(records, record.id))
|
|
242
|
+
throw new OrbitError("duplicate_id");
|
|
243
|
+
};
|
|
244
|
+
const onBoard = (id) => {
|
|
245
|
+
if (id !== boardId)
|
|
246
|
+
throw new OrbitError("invalid_reference");
|
|
247
|
+
};
|
|
248
|
+
const ticketHere = (id) => {
|
|
249
|
+
if (id !== null && !find(draft.tickets, id))
|
|
250
|
+
throw new OrbitError("invalid_reference");
|
|
251
|
+
};
|
|
252
|
+
const stateHere = (id) => {
|
|
253
|
+
if (!find(draft.states, id))
|
|
254
|
+
throw new OrbitError("invalid_reference");
|
|
255
|
+
};
|
|
256
|
+
const setBoard = (board) => {
|
|
257
|
+
onBoard(board.id);
|
|
258
|
+
if (checks.slugTaken(board.slug))
|
|
259
|
+
throw new OrbitError("board_slug_taken");
|
|
260
|
+
draft.board = copy(board);
|
|
261
|
+
};
|
|
262
|
+
function mustFind(records, id, code) {
|
|
263
|
+
const record = find(records, id);
|
|
264
|
+
if (!record)
|
|
265
|
+
throw new OrbitError(code);
|
|
266
|
+
return record;
|
|
267
|
+
}
|
|
268
|
+
function removeTicket(id) {
|
|
269
|
+
mustFind(draft.tickets, id, "ticket_not_found");
|
|
270
|
+
const doomedVerdicts = new Set(draft.verdicts.filter((verdict) => verdict.ticket_id === id).map((verdict) => verdict.id));
|
|
271
|
+
draft.tickets = draft.tickets.filter((ticket) => ticket.id !== id);
|
|
272
|
+
draft.comments = draft.comments.filter((comment) => comment.ticket_id !== id);
|
|
273
|
+
draft.ticketLabels = draft.ticketLabels.filter((link) => link.ticket_id !== id);
|
|
274
|
+
draft.relations = draft.relations.filter((relation) => relation.source_ticket_id !== id && relation.target_ticket_id !== id);
|
|
275
|
+
draft.crossBoardRelations = draft.crossBoardRelations.filter((relation) => relation.source_ticket_id !== id);
|
|
276
|
+
draft.verdicts = draft.verdicts
|
|
277
|
+
.filter((verdict) => !doomedVerdicts.has(verdict.id))
|
|
278
|
+
.map((verdict) => (verdict.supersedes_prior_review_id !== null && doomedVerdicts.has(verdict.supersedes_prior_review_id) ? { ...verdict, supersedes_prior_review_id: null } : verdict));
|
|
279
|
+
draft.attachments = draft.attachments.filter((attachment) => attachment.ticket_id !== id);
|
|
280
|
+
draft.events = draft.events.map((event) => (event.ticket_id === id ? { ...event, ticket_id: null } : event));
|
|
281
|
+
draft.entries = draft.entries.map((entry) => (entry.ticket_id === id ? { ...entry, ticket_id: null } : entry));
|
|
282
|
+
draft.search.delete(id);
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
async clear() {
|
|
286
|
+
await guard();
|
|
287
|
+
Object.assign(draft, emptyTables());
|
|
288
|
+
},
|
|
289
|
+
board: {
|
|
290
|
+
...read.board,
|
|
291
|
+
async set(board) {
|
|
292
|
+
await guard();
|
|
293
|
+
setBoard(board);
|
|
294
|
+
},
|
|
295
|
+
async update(patch) {
|
|
296
|
+
await guard();
|
|
297
|
+
if (!draft.board)
|
|
298
|
+
throw new OrbitError("board_not_found");
|
|
299
|
+
const next = copy(draft.board);
|
|
300
|
+
apply(next, patch);
|
|
301
|
+
setBoard(next);
|
|
302
|
+
return copy(draft.board);
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
states: {
|
|
306
|
+
...read.states,
|
|
307
|
+
async insert(state) {
|
|
308
|
+
await guard();
|
|
309
|
+
fresh(draft.states, state);
|
|
310
|
+
onBoard(state.board_id);
|
|
311
|
+
if (draft.states.some((other) => other.name === state.name))
|
|
312
|
+
throw new OrbitError("state_name_taken");
|
|
313
|
+
draft.states.push(copy(state));
|
|
314
|
+
},
|
|
315
|
+
async update(id, patch) {
|
|
316
|
+
await guard();
|
|
317
|
+
const state = mustFind(draft.states, id, "state_not_found");
|
|
318
|
+
if (patch.name !== undefined && draft.states.some((other) => other.id !== id && other.name === patch.name))
|
|
319
|
+
throw new OrbitError("state_name_taken");
|
|
320
|
+
apply(state, patch);
|
|
321
|
+
return copy(state);
|
|
322
|
+
},
|
|
323
|
+
async remove(id) {
|
|
324
|
+
await guard();
|
|
325
|
+
mustFind(draft.states, id, "state_not_found");
|
|
326
|
+
if (draft.tickets.some((ticket) => ticket.state_id === id))
|
|
327
|
+
throw new OrbitError("state_has_tickets");
|
|
328
|
+
draft.states = draft.states.filter((state) => state.id !== id);
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
labels: {
|
|
332
|
+
...read.labels,
|
|
333
|
+
async insert(label) {
|
|
334
|
+
await guard();
|
|
335
|
+
fresh(draft.labels, label);
|
|
336
|
+
onBoard(label.board_id);
|
|
337
|
+
if (draft.labels.some((other) => other.name === label.name))
|
|
338
|
+
throw new OrbitError("label_name_taken");
|
|
339
|
+
draft.labels.push(copy(label));
|
|
340
|
+
}
|
|
341
|
+
},
|
|
342
|
+
tickets: {
|
|
343
|
+
...read.tickets,
|
|
344
|
+
async create(ticket) {
|
|
345
|
+
await guard();
|
|
346
|
+
const number = draft.tickets.reduce((top, other) => Math.max(top, other.number), 0) + 1;
|
|
347
|
+
const made = { ...ticket, number };
|
|
348
|
+
fresh(draft.tickets, made);
|
|
349
|
+
onBoard(made.board_id);
|
|
350
|
+
stateHere(made.state_id);
|
|
351
|
+
draft.tickets.push(made);
|
|
352
|
+
return copy(made);
|
|
353
|
+
},
|
|
354
|
+
async insert(ticket) {
|
|
355
|
+
await guard();
|
|
356
|
+
fresh(draft.tickets, ticket);
|
|
357
|
+
onBoard(ticket.board_id);
|
|
358
|
+
stateHere(ticket.state_id);
|
|
359
|
+
if (draft.tickets.some((other) => other.number === ticket.number))
|
|
360
|
+
throw new OrbitError("ticket_number_taken");
|
|
361
|
+
draft.tickets.push(copy(ticket));
|
|
362
|
+
},
|
|
363
|
+
async update(id, patch) {
|
|
364
|
+
await guard();
|
|
365
|
+
const ticket = mustFind(draft.tickets, id, "ticket_not_found");
|
|
366
|
+
if (patch.state_id !== undefined)
|
|
367
|
+
stateHere(patch.state_id);
|
|
368
|
+
apply(ticket, patch);
|
|
369
|
+
return copy(ticket);
|
|
370
|
+
},
|
|
371
|
+
async remove(id) {
|
|
372
|
+
await guard();
|
|
373
|
+
removeTicket(id);
|
|
374
|
+
}
|
|
375
|
+
},
|
|
376
|
+
ticketLabels: {
|
|
377
|
+
...read.ticketLabels,
|
|
378
|
+
async set(ticketId, labelIds) {
|
|
379
|
+
await guard();
|
|
380
|
+
ticketHere(ticketId);
|
|
381
|
+
for (const labelId of labelIds)
|
|
382
|
+
if (!find(draft.labels, labelId))
|
|
383
|
+
throw new OrbitError("invalid_reference");
|
|
384
|
+
draft.ticketLabels = draft.ticketLabels.filter((link) => link.ticket_id !== ticketId);
|
|
385
|
+
for (const labelId of new Set(labelIds))
|
|
386
|
+
draft.ticketLabels.push({ ticket_id: ticketId, label_id: labelId });
|
|
387
|
+
},
|
|
388
|
+
async add(ticketId, labelId) {
|
|
389
|
+
await guard();
|
|
390
|
+
ticketHere(ticketId);
|
|
391
|
+
if (!find(draft.labels, labelId))
|
|
392
|
+
throw new OrbitError("invalid_reference");
|
|
393
|
+
if (!draft.ticketLabels.some((link) => link.ticket_id === ticketId && link.label_id === labelId)) {
|
|
394
|
+
draft.ticketLabels.push({ ticket_id: ticketId, label_id: labelId });
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
},
|
|
398
|
+
comments: {
|
|
399
|
+
...read.comments,
|
|
400
|
+
async insert(comment) {
|
|
401
|
+
await guard();
|
|
402
|
+
fresh(draft.comments, comment);
|
|
403
|
+
ticketHere(comment.ticket_id);
|
|
404
|
+
draft.comments.push(copy(comment));
|
|
405
|
+
},
|
|
406
|
+
async update(id, patch) {
|
|
407
|
+
await guard();
|
|
408
|
+
const comment = mustFind(draft.comments, id, "comment_not_found");
|
|
409
|
+
comment.omitted_for_ai = patch.omitted_for_ai;
|
|
410
|
+
return copy(comment);
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
relations: {
|
|
414
|
+
...read.relations,
|
|
415
|
+
async insert(relation) {
|
|
416
|
+
await guard();
|
|
417
|
+
fresh(draft.relations, relation);
|
|
418
|
+
ticketHere(relation.source_ticket_id);
|
|
419
|
+
ticketHere(relation.target_ticket_id);
|
|
420
|
+
const repeat = draft.relations.some((other) => other.source_ticket_id === relation.source_ticket_id && other.target_ticket_id === relation.target_ticket_id && other.type === relation.type);
|
|
421
|
+
if (repeat)
|
|
422
|
+
throw new OrbitError("relation_exists");
|
|
423
|
+
draft.relations.push(copy(relation));
|
|
424
|
+
},
|
|
425
|
+
async remove(id) {
|
|
426
|
+
await guard();
|
|
427
|
+
mustFind(draft.relations, id, "relation_not_found");
|
|
428
|
+
draft.relations = draft.relations.filter((relation) => relation.id !== id);
|
|
429
|
+
}
|
|
430
|
+
},
|
|
431
|
+
crossBoardRelations: {
|
|
432
|
+
...read.crossBoardRelations,
|
|
433
|
+
async insert(relation) {
|
|
434
|
+
await guard();
|
|
435
|
+
fresh(draft.crossBoardRelations, relation);
|
|
436
|
+
ticketHere(relation.source_ticket_id);
|
|
437
|
+
const repeat = draft.crossBoardRelations.some((other) => other.source_ticket_id === relation.source_ticket_id &&
|
|
438
|
+
other.target_board_id === relation.target_board_id &&
|
|
439
|
+
other.target_ticket_id === relation.target_ticket_id &&
|
|
440
|
+
other.type === relation.type);
|
|
441
|
+
if (repeat)
|
|
442
|
+
throw new OrbitError("relation_exists");
|
|
443
|
+
draft.crossBoardRelations.push(copy(relation));
|
|
444
|
+
},
|
|
445
|
+
async remove(id) {
|
|
446
|
+
await guard();
|
|
447
|
+
mustFind(draft.crossBoardRelations, id, "relation_not_found");
|
|
448
|
+
draft.crossBoardRelations = draft.crossBoardRelations.filter((relation) => relation.id !== id);
|
|
449
|
+
}
|
|
450
|
+
},
|
|
451
|
+
entries: {
|
|
452
|
+
...read.entries,
|
|
453
|
+
async insert(entry) {
|
|
454
|
+
await guard();
|
|
455
|
+
fresh(draft.entries, entry);
|
|
456
|
+
onBoard(entry.board_id);
|
|
457
|
+
ticketHere(entry.ticket_id);
|
|
458
|
+
draft.entries.push(copy(entry));
|
|
459
|
+
},
|
|
460
|
+
async update(id, patch) {
|
|
461
|
+
await guard();
|
|
462
|
+
const entry = mustFind(draft.entries, id, "board_entry_not_found");
|
|
463
|
+
apply(entry, patch);
|
|
464
|
+
return copy(entry);
|
|
465
|
+
},
|
|
466
|
+
async remove(id) {
|
|
467
|
+
await guard();
|
|
468
|
+
mustFind(draft.entries, id, "board_entry_not_found");
|
|
469
|
+
draft.entries = draft.entries.filter((entry) => entry.id !== id);
|
|
470
|
+
}
|
|
471
|
+
},
|
|
472
|
+
verdicts: {
|
|
473
|
+
...read.verdicts,
|
|
474
|
+
async insert(verdict) {
|
|
475
|
+
await guard();
|
|
476
|
+
fresh(draft.verdicts, verdict);
|
|
477
|
+
ticketHere(verdict.ticket_id);
|
|
478
|
+
if (verdict.supersedes_prior_review_id !== null && !find(draft.verdicts, verdict.supersedes_prior_review_id))
|
|
479
|
+
throw new OrbitError("invalid_reference");
|
|
480
|
+
draft.verdicts.push(copy(verdict));
|
|
481
|
+
}
|
|
482
|
+
},
|
|
483
|
+
attachments: {
|
|
484
|
+
...read.attachments,
|
|
485
|
+
async insert(attachment) {
|
|
486
|
+
await guard();
|
|
487
|
+
fresh(draft.attachments, attachment);
|
|
488
|
+
onBoard(attachment.board_id);
|
|
489
|
+
ticketHere(attachment.ticket_id);
|
|
490
|
+
draft.attachments.push(copy(attachment));
|
|
491
|
+
},
|
|
492
|
+
async remove(id) {
|
|
493
|
+
await guard();
|
|
494
|
+
mustFind(draft.attachments, id, "attachment_not_found");
|
|
495
|
+
draft.attachments = draft.attachments.filter((attachment) => attachment.id !== id);
|
|
496
|
+
}
|
|
497
|
+
},
|
|
498
|
+
events: {
|
|
499
|
+
...read.events,
|
|
500
|
+
async append(event) {
|
|
501
|
+
await guard();
|
|
502
|
+
fresh(draft.events, event);
|
|
503
|
+
ticketHere(event.ticket_id);
|
|
504
|
+
draft.events.push(copy(event));
|
|
505
|
+
log.events.push(copy(event));
|
|
506
|
+
}
|
|
507
|
+
},
|
|
508
|
+
search: {
|
|
509
|
+
...read.search,
|
|
510
|
+
async index(ticketId, document) {
|
|
511
|
+
await guard();
|
|
512
|
+
ticketHere(ticketId);
|
|
513
|
+
draft.search.set(ticketId, { ...document });
|
|
514
|
+
},
|
|
515
|
+
async remove(ticketId) {
|
|
516
|
+
await guard();
|
|
517
|
+
draft.search.delete(ticketId);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search in memory: the reference for what `SearchReader.query` must match.
|
|
3
|
+
* A real adapter ranks; this one only filters, and lists hits by card number.
|
|
4
|
+
*/
|
|
5
|
+
import type { SearchDocument } from "../../store/types.ts";
|
|
6
|
+
export declare function matches(document: SearchDocument, query: string): boolean;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search in memory: the reference for what `SearchReader.query` must match.
|
|
3
|
+
* A real adapter ranks; this one only filters, and lists hits by card number.
|
|
4
|
+
*/
|
|
5
|
+
import { literalQuery, queryWords } from "../../store/search-words.js";
|
|
6
|
+
/**
|
|
7
|
+
* A document's words: runs of letters, digits and `_`, lowercased, accents
|
|
8
|
+
* folded (as SQLite's unicode61 tokenizer with `_` as a token character).
|
|
9
|
+
*/
|
|
10
|
+
function words(text) {
|
|
11
|
+
return text
|
|
12
|
+
.normalize("NFD")
|
|
13
|
+
.replace(/\p{M}/gu, "")
|
|
14
|
+
.toLowerCase()
|
|
15
|
+
.split(/[^\p{L}\p{N}_]+/u)
|
|
16
|
+
.filter(Boolean);
|
|
17
|
+
}
|
|
18
|
+
const asciiLower = (text) => text.replace(/[A-Z]/g, (letter) => letter.toLowerCase());
|
|
19
|
+
export function matches(document, query) {
|
|
20
|
+
const wanted = queryWords(query);
|
|
21
|
+
if (wanted.length === 0) {
|
|
22
|
+
const text = literalQuery(query);
|
|
23
|
+
if (text === null)
|
|
24
|
+
return false;
|
|
25
|
+
const literal = asciiLower(text);
|
|
26
|
+
return [document.title, document.description, document.details, document.comments].some((field) => asciiLower(field).includes(literal));
|
|
27
|
+
}
|
|
28
|
+
const have = words([document.title, document.description, document.type, document.details, document.comments].join("\n"));
|
|
29
|
+
return wanted.every((word) => have.some((token) => token.startsWith(word)));
|
|
30
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The memory adapter: Orbit's storage port over plain objects. It is the
|
|
3
|
+
* reference the conformance suite is written against, the fast store for the
|
|
4
|
+
* core's own tests, and the smallest example of an adapter.
|
|
5
|
+
*
|
|
6
|
+
* Several stores can share one MemoryData, as several processes share a data
|
|
7
|
+
* folder: each write is serialised across all of them, and each hears the
|
|
8
|
+
* others' changes. A write works on a copy of the board's tables and swaps it
|
|
9
|
+
* in when the body returns, so a read always holds one committed state.
|
|
10
|
+
*/
|
|
11
|
+
import type { BoardChange, Id, RegisteredBoard } from "../../core/model.ts";
|
|
12
|
+
import type { OrbitStore } from "../../store/types.ts";
|
|
13
|
+
import type { Tables, WriteLog } from "./board.ts";
|
|
14
|
+
/** Everything one Orbit keeps, shared by every memory store opened over it. */
|
|
15
|
+
export declare class MemoryData {
|
|
16
|
+
/** In the order boards were registered. */
|
|
17
|
+
directory: RegisteredBoard[];
|
|
18
|
+
readonly settings: Map<string, string>;
|
|
19
|
+
/** Each board's committed tables. A write replaces them whole and never changes them in place. */
|
|
20
|
+
readonly tables: Map<string, Tables>;
|
|
21
|
+
private readonly tails;
|
|
22
|
+
private readonly listeners;
|
|
23
|
+
/** Runs `body` once every earlier body with the same key has finished. */
|
|
24
|
+
exclusive<T>(key: string, body: () => Promise<T>): Promise<T>;
|
|
25
|
+
listen(boardId: Id, listener: (change: BoardChange) => void): () => void;
|
|
26
|
+
/** Tells every listener on the board, after the write that made them has committed. */
|
|
27
|
+
announce(boardId: Id, log: WriteLog): void;
|
|
28
|
+
entry(id: Id): RegisteredBoard | undefined;
|
|
29
|
+
slugTaken(slug: string, exceptId: Id): boolean;
|
|
30
|
+
/** Swaps in a write's tables and, when the write changed the board's record, brings its listing in line (as SQLite does). */
|
|
31
|
+
commit(boardId: Id, draft: Tables): void;
|
|
32
|
+
}
|
|
33
|
+
/** A store over `data`; pass the same MemoryData to two stores to have two views of one Orbit. */
|
|
34
|
+
export declare function createMemoryStore(data?: MemoryData): OrbitStore;
|