@jini-ai/server 0.0.0-stage → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,161 @@
1
+ /** @module db/projects
2
+ * CRUD, run-status queries, and awaiting-input detection for the `projects` SQLite table — a
3
+ * generic workspace/container row-store, not an OD-specific concept. Also queries `messages` and
4
+ * `conversations` for run-status and input-gate state. Imports only the core/ kernel (SqliteDb,
5
+ * DbRow); reaches no sibling concern.
6
+ *
7
+ * NOTE (neutral port): drops three OD-product columns with no generic engine equivalent —
8
+ * `skill_id` (OD's agent-skill marketplace selection), `design_system_id` (OD's Design System
9
+ * feature), and `applied_plugin_snapshot_id` (OD's plugin host). The already-ported
10
+ * `schema/migrate.ts` projects table was pre-trimmed to match; see that file and `source-map.md`'s
11
+ * Design Decisions for the same drop list. The kernel-noun exclusion in `extraction-plan.md` §2.1
12
+ * ("projects... conversations" are not kernel nouns) does not block this module for the same reason
13
+ * it didn't block `conversations`: `concern-owned storage` is a generic *optional* storage adapter any
14
+ * consumer may use, not the kernel itself (`@jini-ai/core`/`@jini-ai/daemon` know nothing about this
15
+ * table) — so "project" here is a de-branded generic workspace/container concept, not OD's model.
16
+ */
17
+ import type { SqliteDb } from '@jini-ai/db/sqlite';
18
+ import type { DbRow } from '@jini-ai/db/core';
19
+ /**
20
+ * Returns all projects ordered by most-recently updated first.
21
+ * @param db - Open SQLite connection.
22
+ */
23
+ export declare function listProjects({ db }: {
24
+ db: SqliteDb;
25
+ }): {
26
+ id: any;
27
+ name: any;
28
+ pendingPrompt: any;
29
+ metadata: any;
30
+ customInstructions: any;
31
+ createdAt: number;
32
+ updatedAt: number;
33
+ }[];
34
+ /**
35
+ * Returns the most recent run status for each project, keyed by projectId.
36
+ * Scans messages with a run_status column and collapses to one entry per project,
37
+ * so callers can render project-level status badges without iterating conversations.
38
+ * @param db - Open SQLite connection.
39
+ */
40
+ export declare function listLatestProjectRunStatuses({ db }: {
41
+ db: SqliteDb;
42
+ }): Map<string, DbRow>;
43
+ /**
44
+ * Returns the most recent run status for each conversation, keyed by conversationId.
45
+ * Used to display per-conversation activity state in a project sidebar.
46
+ * @param db - Open SQLite connection.
47
+ */
48
+ export declare function listLatestConversationRunStatuses({ db }: {
49
+ db: SqliteDb;
50
+ }): Map<string, DbRow>;
51
+ /**
52
+ * Returns the earliest run status for each conversation, keyed by conversationId.
53
+ * Useful for determining when a conversation's first agent turn began.
54
+ * @param db - Open SQLite connection.
55
+ */
56
+ export declare function listFirstConversationRunStatuses({ db }: {
57
+ db: SqliteDb;
58
+ }): Map<string, DbRow>;
59
+ /**
60
+ * Returns the most recent run status for each individual run, keyed by runId.
61
+ * Powers per-run status indicators in a conversation thread.
62
+ * @param db - Open SQLite connection.
63
+ */
64
+ export declare function listLatestRunStatuses({ db }: {
65
+ db: SqliteDb;
66
+ }): Map<string, DbRow>;
67
+ /**
68
+ * Returns the set of project IDs whose latest form-bearing assistant message contains a
69
+ * `<question-form>` or `<ask-question>` artifact and has not yet received a user reply.
70
+ * Form filtering deliberately precedes ranking: a newer plain assistant turn does not consume
71
+ * an outstanding question. Keep timestamp/position ordering for replies sharing a timestamp.
72
+ * This is the data source for an "awaiting input" badge on a project list.
73
+ * @param db - Open SQLite connection.
74
+ */
75
+ export declare function listProjectsAwaitingInput({ db }: {
76
+ db: SqliteDb;
77
+ }): Set<any>;
78
+ /**
79
+ * Returns the set of conversation IDs whose latest form-bearing assistant message contains a
80
+ * `<question-form>` or `<ask-question>` artifact and has not yet received a user reply.
81
+ * As in the project query, a newer plain assistant turn leaves an unanswered form outstanding.
82
+ * @param db - Open SQLite connection.
83
+ */
84
+ export declare function listConversationsAwaitingInput({ db }: {
85
+ db: SqliteDb;
86
+ }): Set<any>;
87
+ /**
88
+ * Fetches a single project by id, or returns null if it does not exist.
89
+ * @param db - Open SQLite connection.
90
+ * @param id - Project id.
91
+ */
92
+ export declare function getProject({ db, id }: {
93
+ db: SqliteDb;
94
+ id: string;
95
+ }): {
96
+ id: any;
97
+ name: any;
98
+ pendingPrompt: any;
99
+ metadata: any;
100
+ customInstructions: any;
101
+ createdAt: number;
102
+ updatedAt: number;
103
+ } | null;
104
+ /**
105
+ * Inserts a new project row and returns the freshly read, normalized record.
106
+ * Metadata is serialized to JSON; all nullable fields default to null on omission.
107
+ * @param db - Open SQLite connection.
108
+ * @param project - Project fields to insert.
109
+ */
110
+ export declare function insertProject({ db, project: p }: {
111
+ db: SqliteDb;
112
+ project: DbRow;
113
+ }): {
114
+ id: any;
115
+ name: any;
116
+ pendingPrompt: any;
117
+ metadata: any;
118
+ customInstructions: any;
119
+ createdAt: number;
120
+ updatedAt: number;
121
+ } | null;
122
+ /**
123
+ * Applies a partial patch to an existing project, preserving all unpatched fields.
124
+ * Returns null when the project does not exist; otherwise returns the updated record.
125
+ * updatedAt defaults to Date.now() when the patch omits it.
126
+ * @param db - Open SQLite connection.
127
+ * @param id - Project id to update.
128
+ * @param patch - Fields to overwrite.
129
+ * @param optional.clock - Host clock port; defaults to Date.now() as before. An explicit updatedAt
130
+ * wins without reading the clock, so replayed/imported timestamps remain stable.
131
+ */
132
+ export declare function updateProject({ db, id, patch }: {
133
+ db: SqliteDb;
134
+ id: string;
135
+ patch: DbRow;
136
+ }, optional?: {
137
+ clock?: {
138
+ now(): number;
139
+ };
140
+ }): {
141
+ id: any;
142
+ name: any;
143
+ pendingPrompt: any;
144
+ metadata: any;
145
+ customInstructions: any;
146
+ createdAt: number;
147
+ updatedAt: number;
148
+ } | null;
149
+ /**
150
+ * Permanently removes a project row. Cascading deletes for related rows are
151
+ * handled at the SQLite schema level, not here.
152
+ * @param db - Open SQLite connection.
153
+ * @param id - Project id to delete.
154
+ */
155
+ export declare function deleteProject({ db, id }: {
156
+ db: SqliteDb;
157
+ id: string;
158
+ }): void;
159
+ /** Unchanged local host-project schema; no automatic migration at import. */
160
+ export declare const LEGACY_PROJECTS_DDL = " CREATE TABLE IF NOT EXISTS projects (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n pending_prompt TEXT,\n metadata_json TEXT,\n custom_instructions TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n );\n\n";
161
+ //# sourceMappingURL=sqlite.d.ts.map
@@ -0,0 +1,328 @@
1
+ /** @internal Column alias list used in every SELECT against the projects table. */
2
+ const PROJECT_COLS = `id, name,
3
+ pending_prompt AS pendingPrompt,
4
+ metadata_json AS metadataJson,
5
+ custom_instructions AS customInstructions,
6
+ created_at AS createdAt,
7
+ updated_at AS updatedAt`;
8
+ /**
9
+ * Returns all projects ordered by most-recently updated first.
10
+ * @param db - Open SQLite connection.
11
+ */
12
+ export function listProjects({ db }) {
13
+ const rows = db
14
+ .prepare(`SELECT ${PROJECT_COLS}
15
+ FROM projects
16
+ ORDER BY updated_at DESC`)
17
+ .all();
18
+ return rows.map(normalizeProject);
19
+ }
20
+ /**
21
+ * Returns the most recent run status for each project, keyed by projectId.
22
+ * Scans messages with a run_status column and collapses to one entry per project,
23
+ * so callers can render project-level status badges without iterating conversations.
24
+ * @param db - Open SQLite connection.
25
+ */
26
+ export function listLatestProjectRunStatuses({ db }) {
27
+ const rows = db
28
+ .prepare(`SELECT c.project_id AS projectId,
29
+ m.run_id AS runId,
30
+ m.run_status AS status,
31
+ COALESCE(m.ended_at, m.started_at, m.created_at) AS updatedAt
32
+ FROM messages m
33
+ JOIN conversations c ON c.id = m.conversation_id
34
+ WHERE m.run_status IS NOT NULL
35
+ ORDER BY updatedAt DESC`)
36
+ .all();
37
+ const latestByProject = new Map();
38
+ for (const row of rows) {
39
+ if (!latestByProject.has(row.projectId)) {
40
+ latestByProject.set(row.projectId, {
41
+ value: normalizeProjectRunStatus(row.status),
42
+ updatedAt: Number(row.updatedAt),
43
+ runId: row.runId ?? undefined,
44
+ });
45
+ }
46
+ }
47
+ return latestByProject;
48
+ }
49
+ /**
50
+ * Returns the most recent run status for each conversation, keyed by conversationId.
51
+ * Used to display per-conversation activity state in a project sidebar.
52
+ * @param db - Open SQLite connection.
53
+ */
54
+ export function listLatestConversationRunStatuses({ db }) {
55
+ const rows = db
56
+ .prepare(`SELECT m.conversation_id AS conversationId,
57
+ m.run_id AS runId,
58
+ m.run_status AS status,
59
+ COALESCE(m.ended_at, m.started_at, m.created_at) AS updatedAt,
60
+ m.position AS position
61
+ FROM messages m
62
+ WHERE m.run_status IS NOT NULL
63
+ ORDER BY updatedAt DESC, m.position DESC`)
64
+ .all();
65
+ const latestByConversation = new Map();
66
+ for (const row of rows) {
67
+ if (!latestByConversation.has(row.conversationId)) {
68
+ latestByConversation.set(row.conversationId, {
69
+ value: normalizeProjectRunStatus(row.status),
70
+ updatedAt: Number(row.updatedAt),
71
+ runId: row.runId ?? undefined,
72
+ });
73
+ }
74
+ }
75
+ return latestByConversation;
76
+ }
77
+ /**
78
+ * Returns the earliest run status for each conversation, keyed by conversationId.
79
+ * Useful for determining when a conversation's first agent turn began.
80
+ * @param db - Open SQLite connection.
81
+ */
82
+ export function listFirstConversationRunStatuses({ db }) {
83
+ const rows = db
84
+ .prepare(`SELECT m.conversation_id AS conversationId,
85
+ m.run_id AS runId,
86
+ m.run_status AS status,
87
+ COALESCE(m.ended_at, m.started_at, m.created_at) AS updatedAt,
88
+ m.position AS position
89
+ FROM messages m
90
+ WHERE m.run_status IS NOT NULL
91
+ AND m.run_id IS NOT NULL
92
+ ORDER BY m.position ASC`)
93
+ .all();
94
+ const firstByConversation = new Map();
95
+ for (const row of rows) {
96
+ if (!firstByConversation.has(row.conversationId)) {
97
+ firstByConversation.set(row.conversationId, {
98
+ value: normalizeProjectRunStatus(row.status),
99
+ updatedAt: Number(row.updatedAt),
100
+ runId: row.runId ?? undefined,
101
+ });
102
+ }
103
+ }
104
+ return firstByConversation;
105
+ }
106
+ /**
107
+ * Returns the most recent run status for each individual run, keyed by runId.
108
+ * Powers per-run status indicators in a conversation thread.
109
+ * @param db - Open SQLite connection.
110
+ */
111
+ export function listLatestRunStatuses({ db }) {
112
+ const rows = db
113
+ .prepare(`SELECT m.run_id AS runId,
114
+ m.run_status AS status,
115
+ COALESCE(m.ended_at, m.started_at, m.created_at) AS updatedAt,
116
+ m.position AS position
117
+ FROM messages m
118
+ WHERE m.run_status IS NOT NULL
119
+ AND m.run_id IS NOT NULL
120
+ ORDER BY updatedAt DESC, m.position DESC`)
121
+ .all();
122
+ const latestByRun = new Map();
123
+ for (const row of rows) {
124
+ if (!latestByRun.has(row.runId)) {
125
+ latestByRun.set(row.runId, {
126
+ value: normalizeProjectRunStatus(row.status),
127
+ updatedAt: Number(row.updatedAt),
128
+ runId: row.runId ?? undefined,
129
+ });
130
+ }
131
+ }
132
+ return latestByRun;
133
+ }
134
+ /**
135
+ * Returns the set of project IDs whose latest form-bearing assistant message contains a
136
+ * `<question-form>` or `<ask-question>` artifact and has not yet received a user reply.
137
+ * Form filtering deliberately precedes ranking: a newer plain assistant turn does not consume
138
+ * an outstanding question. Keep timestamp/position ordering for replies sharing a timestamp.
139
+ * This is the data source for an "awaiting input" badge on a project list.
140
+ * @param db - Open SQLite connection.
141
+ */
142
+ export function listProjectsAwaitingInput({ db }) {
143
+ const rows = db
144
+ .prepare(`SELECT latest.projectId
145
+ FROM (
146
+ SELECT c.project_id AS projectId,
147
+ m.conversation_id AS conversationId,
148
+ m.created_at AS createdAt,
149
+ m.position AS position,
150
+ ROW_NUMBER() OVER (
151
+ PARTITION BY c.project_id
152
+ ORDER BY m.created_at DESC, m.position DESC
153
+ ) AS rowNum
154
+ FROM messages m
155
+ JOIN conversations c ON c.id = m.conversation_id
156
+ WHERE m.role = 'assistant'
157
+ -- ask-question is an accepted alias for question-form (host UI parser
158
+ -- + agent open-tag matcher), so an alias-form turn must also count as
159
+ -- awaiting input.
160
+ AND (
161
+ LOWER(m.content) LIKE '%<question-form%'
162
+ OR LOWER(m.content) LIKE '%<ask-question%'
163
+ )
164
+ ) latest
165
+ WHERE latest.rowNum = 1
166
+ AND NOT EXISTS (
167
+ SELECT 1
168
+ FROM messages reply
169
+ WHERE reply.conversation_id = latest.conversationId
170
+ AND reply.role = 'user'
171
+ AND (
172
+ reply.created_at > latest.createdAt
173
+ OR (reply.created_at = latest.createdAt AND reply.position > latest.position)
174
+ )
175
+ )`)
176
+ .all();
177
+ return new Set(rows.map((row) => row.projectId));
178
+ }
179
+ /**
180
+ * Returns the set of conversation IDs whose latest form-bearing assistant message contains a
181
+ * `<question-form>` or `<ask-question>` artifact and has not yet received a user reply.
182
+ * As in the project query, a newer plain assistant turn leaves an unanswered form outstanding.
183
+ * @param db - Open SQLite connection.
184
+ */
185
+ export function listConversationsAwaitingInput({ db }) {
186
+ const rows = db
187
+ .prepare(`SELECT latest.conversationId
188
+ FROM (
189
+ SELECT m.conversation_id AS conversationId,
190
+ m.created_at AS createdAt,
191
+ m.position AS position,
192
+ ROW_NUMBER() OVER (
193
+ PARTITION BY m.conversation_id
194
+ ORDER BY m.created_at DESC, m.position DESC
195
+ ) AS rowNum
196
+ FROM messages m
197
+ WHERE m.role = 'assistant'
198
+ AND (
199
+ LOWER(m.content) LIKE '%<question-form%'
200
+ OR LOWER(m.content) LIKE '%<ask-question%'
201
+ )
202
+ ) latest
203
+ WHERE latest.rowNum = 1
204
+ AND NOT EXISTS (
205
+ SELECT 1
206
+ FROM messages reply
207
+ WHERE reply.conversation_id = latest.conversationId
208
+ AND reply.role = 'user'
209
+ AND (
210
+ reply.created_at > latest.createdAt
211
+ OR (reply.created_at = latest.createdAt AND reply.position > latest.position)
212
+ )
213
+ )`)
214
+ .all();
215
+ return new Set(rows.map((row) => row.conversationId));
216
+ }
217
+ /**
218
+ * Fetches a single project by id, or returns null if it does not exist.
219
+ * @param db - Open SQLite connection.
220
+ * @param id - Project id.
221
+ */
222
+ export function getProject({ db, id }) {
223
+ const row = db
224
+ .prepare(`SELECT ${PROJECT_COLS} FROM projects WHERE id = ?`)
225
+ .get(id);
226
+ return row ? normalizeProject(row) : null;
227
+ }
228
+ /**
229
+ * Inserts a new project row and returns the freshly read, normalized record.
230
+ * Metadata is serialized to JSON; all nullable fields default to null on omission.
231
+ * @param db - Open SQLite connection.
232
+ * @param project - Project fields to insert.
233
+ */
234
+ export function insertProject({ db, project: p }) {
235
+ db.prepare(`INSERT INTO projects
236
+ (id, name, pending_prompt, metadata_json, custom_instructions, created_at, updated_at)
237
+ VALUES (?, ?, ?, ?, ?, ?, ?)`).run(p.id, p.name, p.pendingPrompt ?? null, p.metadata ? JSON.stringify(p.metadata) : null, p.customInstructions ?? null, p.createdAt, p.updatedAt);
238
+ return getProject({ db, id: p.id });
239
+ }
240
+ /**
241
+ * Applies a partial patch to an existing project, preserving all unpatched fields.
242
+ * Returns null when the project does not exist; otherwise returns the updated record.
243
+ * updatedAt defaults to Date.now() when the patch omits it.
244
+ * @param db - Open SQLite connection.
245
+ * @param id - Project id to update.
246
+ * @param patch - Fields to overwrite.
247
+ * @param optional.clock - Host clock port; defaults to Date.now() as before. An explicit updatedAt
248
+ * wins without reading the clock, so replayed/imported timestamps remain stable.
249
+ */
250
+ export function updateProject({ db, id, patch }, optional = {}) {
251
+ const existing = getProject({ db, id });
252
+ if (!existing)
253
+ return null;
254
+ const merged = {
255
+ ...existing,
256
+ ...patch,
257
+ updatedAt: typeof patch.updatedAt === 'number' ? patch.updatedAt : (optional.clock ?? Date).now(),
258
+ };
259
+ db.prepare(`UPDATE projects
260
+ SET name = ?,
261
+ pending_prompt = ?,
262
+ metadata_json = ?,
263
+ custom_instructions = ?,
264
+ updated_at = ?
265
+ WHERE id = ?`).run(merged.name, merged.pendingPrompt ?? null, merged.metadata ? JSON.stringify(merged.metadata) : null, merged.customInstructions ?? null, merged.updatedAt, id);
266
+ return getProject({ db, id });
267
+ }
268
+ /**
269
+ * Permanently removes a project row. Cascading deletes for related rows are
270
+ * handled at the SQLite schema level, not here.
271
+ * @param db - Open SQLite connection.
272
+ * @param id - Project id to delete.
273
+ */
274
+ export function deleteProject({ db, id }) {
275
+ db.prepare(`DELETE FROM projects WHERE id = ?`).run(id);
276
+ }
277
+ /** @internal Converts a raw SQLite row into the typed project shape, parsing
278
+ * metadataJson and casting numeric timestamps. */
279
+ function normalizeProject(row) {
280
+ let metadata;
281
+ if (row.metadataJson) {
282
+ try {
283
+ metadata = JSON.parse(row.metadataJson);
284
+ }
285
+ catch {
286
+ metadata = undefined;
287
+ }
288
+ }
289
+ return {
290
+ id: row.id,
291
+ name: row.name,
292
+ pendingPrompt: row.pendingPrompt ?? undefined,
293
+ metadata,
294
+ customInstructions: row.customInstructions ?? undefined,
295
+ createdAt: Number(row.createdAt),
296
+ updatedAt: Number(row.updatedAt),
297
+ };
298
+ }
299
+ /** @internal Maps raw DB run_status strings to the canonical client-facing enum,
300
+ * collapsing 'starting' → 'running' and 'cancelled' → 'canceled', and defaulting
301
+ * unknown values to 'not_started'. */
302
+ function normalizeProjectRunStatus(status) {
303
+ if (status === 'starting')
304
+ return 'running';
305
+ if (status === 'cancelled')
306
+ return 'canceled';
307
+ if (status === 'queued' ||
308
+ status === 'running' ||
309
+ status === 'succeeded' ||
310
+ status === 'failed' ||
311
+ status === 'canceled') {
312
+ return status;
313
+ }
314
+ return 'not_started';
315
+ }
316
+ /** Unchanged local host-project schema; no automatic migration at import. */
317
+ export const LEGACY_PROJECTS_DDL = ` CREATE TABLE IF NOT EXISTS projects (
318
+ id TEXT PRIMARY KEY,
319
+ name TEXT NOT NULL,
320
+ pending_prompt TEXT,
321
+ metadata_json TEXT,
322
+ custom_instructions TEXT,
323
+ created_at INTEGER NOT NULL,
324
+ updated_at INTEGER NOT NULL
325
+ );
326
+
327
+ `;
328
+ //# sourceMappingURL=sqlite.js.map
package/package.json CHANGED
@@ -1,6 +1,89 @@
1
1
  {
2
2
  "name": "@jini-ai/server",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.5.0",
4
+ "license": "Apache-2.0",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/AINSEP/Jini.git",
8
+ "directory": "packages/node-host"
9
+ },
10
+ "jini": {
11
+ "domain": "server",
12
+ "kind": "host",
13
+ "runtime": "node",
14
+ "admission": "locked"
15
+ },
16
+ "type": "module",
17
+ "main": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.js",
23
+ "default": "./dist/index.js"
24
+ },
25
+ "./storage": {
26
+ "types": "./dist/storage/index.d.ts",
27
+ "import": "./dist/storage/index.js",
28
+ "default": "./dist/storage/index.js"
29
+ },
30
+ "./storage/legacy/sqlite": {
31
+ "types": "./dist/storage/legacy/sqlite.d.ts",
32
+ "import": "./dist/storage/legacy/sqlite.js",
33
+ "default": "./dist/storage/legacy/sqlite.js"
34
+ },
35
+ "./store/projects/sqlite": {
36
+ "types": "./dist/store/projects/sqlite.d.ts",
37
+ "import": "./dist/store/projects/sqlite.js",
38
+ "default": "./dist/store/projects/sqlite.js"
39
+ }
40
+ },
41
+ "files": [
42
+ "dist",
43
+ "README.md",
44
+ "CHANGELOG.md",
45
+ "LICENSE",
46
+ "!dist/**/*.test.js",
47
+ "!dist/**/*.test.d.ts",
48
+ "!dist/**/*.test.js.map",
49
+ "!dist/**/*.test.d.ts.map",
50
+ "!dist/**/__tests__/**",
51
+ "!dist/**/*.typecheck.*"
52
+ ],
53
+ "publishConfig": {
54
+ "access": "public",
55
+ "registry": "https://registry.npmjs.org"
56
+ },
57
+ "dependencies": {
58
+ "express": "^4.21.0",
59
+ "@jini-ai/agent-runtime": "0.4.0",
60
+ "@jini-ai/core": "0.4.0",
61
+ "@jini-ai/http-kit": "0.4.0",
62
+ "@jini-ai/daemon": "0.5.0",
63
+ "@jini-ai/sidecar": "0.4.0",
64
+ "@jini-ai/db": "0.2.0",
65
+ "@jini-ai/registry": "0.5.0",
66
+ "@jini-ai/chat": "0.5.0"
67
+ },
68
+ "devDependencies": {
69
+ "@types/better-sqlite3": "^7.6.11",
70
+ "@types/express": "^4.17.21",
71
+ "@vitest/coverage-v8": "^2.1.9",
72
+ "better-sqlite3": "^13.0.0"
73
+ },
74
+ "peerDependencies": {
75
+ "better-sqlite3": "^13.0.0"
76
+ },
77
+ "peerDependenciesMeta": {
78
+ "better-sqlite3": {
79
+ "optional": true
80
+ }
81
+ },
82
+ "sideEffects": false,
83
+ "scripts": {
84
+ "build": "tsc -p tsconfig.json",
85
+ "typecheck": "tsc -p tsconfig.json --noEmit",
86
+ "test": "vitest run",
87
+ "test:coverage": "vitest run --coverage"
88
+ }
6
89
  }