@gobing-ai/ts-db 0.2.8 → 0.2.9
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/README.md +52 -1
- package/dist/drizzle-builders.d.ts +96 -0
- package/dist/drizzle-builders.d.ts.map +1 -0
- package/dist/drizzle-builders.js +17 -0
- package/dist/embedded-migrations.d.ts.map +1 -1
- package/dist/embedded-migrations.js +10 -0
- package/dist/entity-dao.d.ts.map +1 -1
- package/dist/inbox-message-dao.d.ts +19 -0
- package/dist/inbox-message-dao.d.ts.map +1 -0
- package/dist/inbox-message-dao.js +75 -0
- package/dist/inbox.d.ts +3 -0
- package/dist/inbox.d.ts.map +1 -0
- package/dist/inbox.js +2 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/migrate.d.ts +12 -5
- package/dist/migrate.d.ts.map +1 -1
- package/dist/migrate.js +19 -25
- package/dist/queue-job-dao.d.ts.map +1 -1
- package/dist/schema/ddl.d.ts.map +1 -1
- package/dist/schema/ddl.js +4 -36
- package/dist/schema/drizzle-internals.d.ts +25 -0
- package/dist/schema/drizzle-internals.d.ts.map +1 -0
- package/dist/schema/drizzle-internals.js +47 -0
- package/dist/schema/inbox-messages.d.ts +212 -0
- package/dist/schema/inbox-messages.d.ts.map +1 -0
- package/dist/schema/inbox-messages.js +17 -0
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +1 -0
- package/dist/schema/runtime.d.ts +1 -0
- package/dist/schema/runtime.d.ts.map +1 -1
- package/dist/schema/runtime.js +1 -0
- package/package.json +7 -2
- package/src/drizzle-builders.ts +98 -0
- package/src/embedded-migrations.ts +10 -0
- package/src/entity-dao.ts +2 -21
- package/src/inbox-message-dao.ts +91 -0
- package/src/inbox.ts +2 -0
- package/src/index.ts +3 -1
- package/src/migrate.ts +34 -22
- package/src/queue-job-dao.ts +14 -59
- package/src/schema/ddl.ts +4 -40
- package/src/schema/drizzle-internals.ts +55 -0
- package/src/schema/inbox-messages.ts +22 -0
- package/src/schema/index.ts +1 -0
- package/src/schema/runtime.ts +1 -0
package/src/migrate.ts
CHANGED
|
@@ -5,13 +5,16 @@ import type { DbAdapter } from './adapter';
|
|
|
5
5
|
import { embeddedMigrations } from './embedded-migrations';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* Minimal structural logger for migration progress.
|
|
9
|
+
*
|
|
10
|
+
* Structurally compatible with `@gobing-ai/ts-infra`'s `Logger` so consumers can
|
|
11
|
+
* pass theirs directly — ts-db never imports ts-infra (keeps the package
|
|
12
|
+
* boundary). Defaults to `console` when absent.
|
|
11
13
|
*/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
export interface MigrationLogger {
|
|
15
|
+
info(msg: string): void;
|
|
16
|
+
warn(msg: string): void;
|
|
17
|
+
error(msg: string): void;
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
/**
|
|
@@ -24,6 +27,8 @@ export interface MigrationOptions {
|
|
|
24
27
|
migrationsTable?: string;
|
|
25
28
|
/** File system abstraction for path resolution. */
|
|
26
29
|
fs?: FileSystem;
|
|
30
|
+
/** Logger for migration progress. Default: `console`. */
|
|
31
|
+
logger?: MigrationLogger;
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
/**
|
|
@@ -56,7 +61,11 @@ function validateMigrationTableName(table: string): string {
|
|
|
56
61
|
* Checks the journal table and applies only migrations that haven't run yet.
|
|
57
62
|
* Each migration is executed with adapter.exec() for file-based or adapter.run() for journal tracking.
|
|
58
63
|
*/
|
|
59
|
-
async function applyEmbeddedMigrations(
|
|
64
|
+
async function applyEmbeddedMigrations(
|
|
65
|
+
adapter: DbAdapter,
|
|
66
|
+
journalTable: string,
|
|
67
|
+
logger: MigrationLogger,
|
|
68
|
+
): Promise<void> {
|
|
60
69
|
// Validate journal table name — this is an internal constant, never user input.
|
|
61
70
|
if (!/^__[a-z_]+$/.test(journalTable)) {
|
|
62
71
|
throw new Error(`Invalid migration journal table name: ${journalTable}`);
|
|
@@ -76,7 +85,7 @@ async function applyEmbeddedMigrations(adapter: DbAdapter, journalTable: string)
|
|
|
76
85
|
for (const migration of embeddedMigrations) {
|
|
77
86
|
if (appliedHashes.has(migration.hash)) continue;
|
|
78
87
|
|
|
79
|
-
|
|
88
|
+
logger.info(`Applying embedded migration: ${migration.tag}`);
|
|
80
89
|
|
|
81
90
|
// Split on semicolons and execute each non-empty statement
|
|
82
91
|
const statements = migration.sql
|
|
@@ -94,7 +103,7 @@ async function applyEmbeddedMigrations(adapter: DbAdapter, journalTable: string)
|
|
|
94
103
|
}
|
|
95
104
|
|
|
96
105
|
if (applied > 0) {
|
|
97
|
-
|
|
106
|
+
logger.info(`Applied ${applied} embedded migration(s)`);
|
|
98
107
|
}
|
|
99
108
|
}
|
|
100
109
|
|
|
@@ -115,9 +124,10 @@ async function applyEmbeddedMigrations(adapter: DbAdapter, journalTable: string)
|
|
|
115
124
|
* @param options - Optional migration folder and table name overrides.
|
|
116
125
|
*/
|
|
117
126
|
export async function applyMigrations(adapter: DbAdapter, options?: MigrationOptions): Promise<void> {
|
|
127
|
+
const logger = options?.logger ?? console;
|
|
118
128
|
const { BunSqliteAdapter } = await import('./adapters/bun-sqlite');
|
|
119
129
|
if (!(adapter instanceof BunSqliteAdapter)) {
|
|
120
|
-
|
|
130
|
+
logger.warn('Skipping in-app migrations: only supported for bun-sqlite adapter');
|
|
121
131
|
return;
|
|
122
132
|
}
|
|
123
133
|
|
|
@@ -125,39 +135,41 @@ export async function applyMigrations(adapter: DbAdapter, options?: MigrationOpt
|
|
|
125
135
|
|
|
126
136
|
await ensureJournalTable(adapter, table);
|
|
127
137
|
|
|
128
|
-
const folder = options?.migrationsFolder ?? resolve(
|
|
138
|
+
const folder = options?.migrationsFolder ?? resolve(process.cwd(), 'drizzle');
|
|
129
139
|
|
|
130
|
-
// File-based migrations: attempt if drizzle/ folder is
|
|
131
|
-
//
|
|
140
|
+
// File-based migrations: attempt only if the drizzle/ folder is present.
|
|
141
|
+
// With an injected fs we get a definitive answer (await the Promise — a bare
|
|
142
|
+
// `fs.exists(...)` is always truthy and silently disables the check); without
|
|
143
|
+
// one we attempt optimistically and fall back on the migrator's own error.
|
|
132
144
|
const fs = options?.fs;
|
|
133
|
-
const tryFileBased = fs
|
|
145
|
+
const tryFileBased = fs ? await fs.exists(folder) : true;
|
|
134
146
|
|
|
135
147
|
if (tryFileBased) {
|
|
136
148
|
try {
|
|
137
149
|
const { migrate: drizzleMigrate } = await import('drizzle-orm/bun-sqlite/migrator');
|
|
138
150
|
|
|
139
|
-
|
|
151
|
+
logger.info(`Applying database migrations from ${folder}`);
|
|
140
152
|
|
|
141
153
|
await drizzleMigrate(adapter.getDrizzleDb(), {
|
|
142
154
|
migrationsFolder: folder,
|
|
143
155
|
...(options?.migrationsTable !== undefined ? { migrationsTable: options.migrationsTable } : {}),
|
|
144
156
|
});
|
|
145
|
-
|
|
157
|
+
logger.info('Database migrations complete');
|
|
146
158
|
return;
|
|
147
159
|
} catch (error) {
|
|
148
|
-
//
|
|
149
|
-
// Any other
|
|
160
|
+
// A missing/empty migrations folder is expected in compiled binaries —
|
|
161
|
+
// fall through to embedded. Any other failure is real; rethrow it.
|
|
150
162
|
const message = error instanceof Error ? error.message : String(error);
|
|
151
163
|
if (message.includes('journal') || message.includes('ENOENT') || message.includes('meta')) {
|
|
152
|
-
|
|
164
|
+
logger.info(`File-based migrations unavailable, using embedded: ${message}`);
|
|
153
165
|
} else {
|
|
154
|
-
|
|
166
|
+
logger.error(`[MIGRATE] drizzleMigrate failed: ${message}`);
|
|
155
167
|
throw error;
|
|
156
168
|
}
|
|
157
169
|
}
|
|
158
170
|
}
|
|
159
171
|
|
|
160
172
|
// Fallback: embedded migrations (for compiled binaries)
|
|
161
|
-
|
|
162
|
-
await applyEmbeddedMigrations(adapter, table);
|
|
173
|
+
logger.info('No drizzle/ folder found — applying embedded migrations');
|
|
174
|
+
await applyEmbeddedMigrations(adapter, table, logger);
|
|
163
175
|
}
|
package/src/queue-job-dao.ts
CHANGED
|
@@ -1,60 +1,15 @@
|
|
|
1
1
|
import { and, eq, inArray, sql } from 'drizzle-orm';
|
|
2
2
|
import type { DbAdapter } from './adapter';
|
|
3
|
+
import type {
|
|
4
|
+
SelectOrderedLimitDb,
|
|
5
|
+
SelectProjectionDb,
|
|
6
|
+
UpdateChangesDb,
|
|
7
|
+
UpdateReturningDb,
|
|
8
|
+
UpdateVoidDb,
|
|
9
|
+
} from './drizzle-builders';
|
|
3
10
|
import { EntityDao } from './entity-dao';
|
|
4
11
|
import { queueJobs } from './schema/queue-jobs';
|
|
5
12
|
|
|
6
|
-
type SelectGroupByQuery = {
|
|
7
|
-
groupBy: (group: unknown) => Promise<unknown[]>;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
type SelectWhereQuery = {
|
|
11
|
-
where: (where: unknown) => Promise<unknown[]>;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
type SelectReadyQuery = {
|
|
15
|
-
where: (where: unknown) => {
|
|
16
|
-
orderBy: (order: unknown) => { limit: (limit: number) => Promise<unknown[]> };
|
|
17
|
-
};
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
type QueueSelectDb = {
|
|
21
|
-
select: (projection: unknown) => {
|
|
22
|
-
from: (table: unknown) => SelectGroupByQuery & SelectWhereQuery;
|
|
23
|
-
};
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
type QueueReadyDb = {
|
|
27
|
-
select: () => {
|
|
28
|
-
from: (table: unknown) => SelectReadyQuery;
|
|
29
|
-
};
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
type QueueUpdateReturningDb = {
|
|
33
|
-
update: (table: unknown) => {
|
|
34
|
-
set: (value: unknown) => {
|
|
35
|
-
where: (where: unknown) => {
|
|
36
|
-
returning: () => Promise<unknown[]>;
|
|
37
|
-
};
|
|
38
|
-
};
|
|
39
|
-
};
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
type QueueUpdateVoidDb = {
|
|
43
|
-
update: (table: unknown) => {
|
|
44
|
-
set: (value: unknown) => {
|
|
45
|
-
where: (where: unknown) => Promise<unknown>;
|
|
46
|
-
};
|
|
47
|
-
};
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
type QueueUpdateChangesDb = {
|
|
51
|
-
update: (table: unknown) => {
|
|
52
|
-
set: (value: unknown) => {
|
|
53
|
-
where: (where: unknown) => Promise<{ changes: number }>;
|
|
54
|
-
};
|
|
55
|
-
};
|
|
56
|
-
};
|
|
57
|
-
|
|
58
13
|
/**
|
|
59
14
|
* Aggregate queue statistics by job status.
|
|
60
15
|
*/
|
|
@@ -153,7 +108,7 @@ export class QueueJobDao extends EntityDao<typeof queueJobs, typeof queueJobs.id
|
|
|
153
108
|
* Get aggregate job counts by status.
|
|
154
109
|
*/
|
|
155
110
|
async getStats(): Promise<QueueStats> {
|
|
156
|
-
const result = await (this.db as
|
|
111
|
+
const result = await (this.db as SelectProjectionDb)
|
|
157
112
|
.select({
|
|
158
113
|
status: queueJobs.status,
|
|
159
114
|
count: sql`count(*)`,
|
|
@@ -176,7 +131,7 @@ export class QueueJobDao extends EntityDao<typeof queueJobs, typeof queueJobs.id
|
|
|
176
131
|
* Count jobs by status.
|
|
177
132
|
*/
|
|
178
133
|
async countByStatus(status: string): Promise<number> {
|
|
179
|
-
const result = await (this.db as
|
|
134
|
+
const result = await (this.db as SelectProjectionDb)
|
|
180
135
|
.select({ value: sql`count(*)` })
|
|
181
136
|
.from(queueJobs)
|
|
182
137
|
.where(sql`${queueJobs.status} = ${status}`);
|
|
@@ -190,7 +145,7 @@ export class QueueJobDao extends EntityDao<typeof queueJobs, typeof queueJobs.id
|
|
|
190
145
|
async findPending(batchSize: number): Promise<QueueJobRecord[]> {
|
|
191
146
|
const now = this.now();
|
|
192
147
|
|
|
193
|
-
const result = await (this.db as
|
|
148
|
+
const result = await (this.db as SelectOrderedLimitDb)
|
|
194
149
|
.select()
|
|
195
150
|
.from(queueJobs)
|
|
196
151
|
.where(
|
|
@@ -214,7 +169,7 @@ export class QueueJobDao extends EntityDao<typeof queueJobs, typeof queueJobs.id
|
|
|
214
169
|
|
|
215
170
|
const now = this.now();
|
|
216
171
|
|
|
217
|
-
const result = await (this.db as
|
|
172
|
+
const result = await (this.db as UpdateReturningDb)
|
|
218
173
|
.update(queueJobs)
|
|
219
174
|
.set({ status: 'processing', processingAt: now, updatedAt: now })
|
|
220
175
|
.where(
|
|
@@ -241,7 +196,7 @@ export class QueueJobDao extends EntityDao<typeof queueJobs, typeof queueJobs.id
|
|
|
241
196
|
|
|
242
197
|
const now = this.now();
|
|
243
198
|
|
|
244
|
-
await (this.db as
|
|
199
|
+
await (this.db as UpdateVoidDb)
|
|
245
200
|
.update(queueJobs)
|
|
246
201
|
.set({ status: 'processing', processingAt: now, updatedAt: now })
|
|
247
202
|
.where(and(inArray(queueJobs.id, ids), eq(queueJobs.status, 'pending')));
|
|
@@ -288,7 +243,7 @@ export class QueueJobDao extends EntityDao<typeof queueJobs, typeof queueJobs.id
|
|
|
288
243
|
async resetStuckJobs(visibilityTimeout: number): Promise<number> {
|
|
289
244
|
const cutoff = this.now() - visibilityTimeout;
|
|
290
245
|
|
|
291
|
-
const result = await (this.db as
|
|
246
|
+
const result = await (this.db as UpdateChangesDb)
|
|
292
247
|
.update(queueJobs)
|
|
293
248
|
.set({ status: 'pending', processingAt: null, updatedAt: this.now() })
|
|
294
249
|
.where(
|
|
@@ -307,7 +262,7 @@ export class QueueJobDao extends EntityDao<typeof queueJobs, typeof queueJobs.id
|
|
|
307
262
|
async failExpiredJobs(): Promise<number> {
|
|
308
263
|
const now = this.now();
|
|
309
264
|
|
|
310
|
-
const result = await (this.db as
|
|
265
|
+
const result = await (this.db as UpdateChangesDb)
|
|
311
266
|
.update(queueJobs)
|
|
312
267
|
.set({
|
|
313
268
|
status: 'failed',
|
package/src/schema/ddl.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getTableConfig, type SQLiteTable } from 'drizzle-orm/sqlite-core';
|
|
2
|
+
import { getDrizzleTableName, sqlExpressionToText } from './drizzle-internals';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Quote an identifier for use in SQL (double-quoted for SQLite compatibility).
|
|
@@ -7,26 +8,6 @@ function quoteIdent(name: string): string {
|
|
|
7
8
|
return `"${name.replace(/"/g, '""')}"`;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
/**
|
|
11
|
-
* Extract the SQL string from a drizzle-orm SQL expression by walking its
|
|
12
|
-
* internal `queryChunks` — StringChunk values plus Param placeholders.
|
|
13
|
-
*/
|
|
14
|
-
function sqlToString(chunks: Array<{ value?: unknown; input?: unknown }>): string {
|
|
15
|
-
return chunks
|
|
16
|
-
.map((chunk) => {
|
|
17
|
-
// StringChunk — the literal SQL fragment
|
|
18
|
-
if ('value' in chunk && typeof chunk.value === 'string') {
|
|
19
|
-
return chunk.value;
|
|
20
|
-
}
|
|
21
|
-
// Param — use the input value if available
|
|
22
|
-
if ('input' in chunk && chunk.input !== undefined) {
|
|
23
|
-
return String(chunk.input);
|
|
24
|
-
}
|
|
25
|
-
return String(chunk.value ?? '?');
|
|
26
|
-
})
|
|
27
|
-
.join('');
|
|
28
|
-
}
|
|
29
|
-
|
|
30
11
|
/**
|
|
31
12
|
* Map a column default value to its SQL literal representation.
|
|
32
13
|
*
|
|
@@ -48,25 +29,8 @@ function defaultToSql(value: unknown): string | undefined {
|
|
|
48
29
|
if (typeof value === 'boolean') {
|
|
49
30
|
return value ? '1' : '0';
|
|
50
31
|
}
|
|
51
|
-
// drizzle-orm SQL expression (
|
|
52
|
-
|
|
53
|
-
const chunks = (value as Record<string, unknown>).queryChunks as
|
|
54
|
-
| Array<{ value?: unknown; input?: unknown }>
|
|
55
|
-
| undefined;
|
|
56
|
-
if (chunks) {
|
|
57
|
-
return sqlToString(chunks);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return String(value);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Resolve a drizzle table object to its string name.
|
|
65
|
-
*/
|
|
66
|
-
function getTableName(table: Record<string, unknown>): string {
|
|
67
|
-
// Drizzle tables store name at Symbol.for('drizzle:Name')
|
|
68
|
-
const nameSym = Symbol.for('drizzle:Name');
|
|
69
|
-
return String((table as unknown as Record<symbol, unknown>)[nameSym]);
|
|
32
|
+
// drizzle-orm SQL expression (sql`...`) — rendered via the internals quarantine.
|
|
33
|
+
return sqlExpressionToText(value) ?? String(value);
|
|
70
34
|
}
|
|
71
35
|
|
|
72
36
|
/**
|
|
@@ -157,7 +121,7 @@ export function generateCreateTableSql(table: SQLiteTable): string {
|
|
|
157
121
|
const ref = fk.reference();
|
|
158
122
|
const localCols = ref.columns.map((c) => quoteIdent(c.name)).join(', ');
|
|
159
123
|
const foreignCols = ref.foreignColumns.map((c) => quoteIdent(c.name)).join(', ');
|
|
160
|
-
const foreignTableName =
|
|
124
|
+
const foreignTableName = getDrizzleTableName(ref.foreignTable);
|
|
161
125
|
|
|
162
126
|
let constraint = `FOREIGN KEY (${localCols}) REFERENCES ${quoteIdent(foreignTableName)} (${foreignCols})`;
|
|
163
127
|
if (fk.onDelete) {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quarantine for the *unversioned* drizzle-orm internal shapes ts-db reaches into
|
|
3
|
+
* for DDL generation.
|
|
4
|
+
*
|
|
5
|
+
* Everything else in ts-db consumes drizzle's public API (`getTableConfig`,
|
|
6
|
+
* column accessors). These two helpers are the only places that touch private
|
|
7
|
+
* structure — the SQL-expression `queryChunks` array and the
|
|
8
|
+
* `Symbol.for('drizzle:Name')` table-name slot — neither of which is covered by
|
|
9
|
+
* drizzle's semver contract. Keeping both here means a drizzle bump that changes
|
|
10
|
+
* an internal shape breaks in ONE file with ONE focused test, instead of silently
|
|
11
|
+
* mis-generating DDL across `ddl.ts`. If this module starts failing after an
|
|
12
|
+
* upgrade, the fix lives here.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** A drizzle SQL expression's internal chunk: a literal fragment or a bound param. */
|
|
16
|
+
interface SqlChunk {
|
|
17
|
+
value?: unknown;
|
|
18
|
+
input?: unknown;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Render a drizzle `sql\`...\`` expression to its literal SQL text by walking its
|
|
23
|
+
* internal `queryChunks`. Used to emit SQL-level column defaults
|
|
24
|
+
* (e.g. `DEFAULT (unixepoch())`) into generated DDL.
|
|
25
|
+
*
|
|
26
|
+
* Returns `undefined` when `value` is not a drizzle SQL expression with chunks,
|
|
27
|
+
* so callers can fall through to their primitive/`String()` handling.
|
|
28
|
+
*/
|
|
29
|
+
export function sqlExpressionToText(value: unknown): string | undefined {
|
|
30
|
+
if (typeof value !== 'object' || value === null || !('queryChunks' in value)) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
const chunks = (value as Record<string, unknown>).queryChunks as SqlChunk[] | undefined;
|
|
34
|
+
if (!chunks) return undefined;
|
|
35
|
+
|
|
36
|
+
return chunks
|
|
37
|
+
.map((chunk) => {
|
|
38
|
+
// StringChunk — the literal SQL fragment
|
|
39
|
+
if ('value' in chunk && typeof chunk.value === 'string') {
|
|
40
|
+
return chunk.value;
|
|
41
|
+
}
|
|
42
|
+
// Param — use the input value if available
|
|
43
|
+
if ('input' in chunk && chunk.input !== undefined) {
|
|
44
|
+
return String(chunk.input);
|
|
45
|
+
}
|
|
46
|
+
return String(chunk.value ?? '?');
|
|
47
|
+
})
|
|
48
|
+
.join('');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Resolve a drizzle table object to its declared name (stored at a private symbol). */
|
|
52
|
+
export function getDrizzleTableName(table: object): string {
|
|
53
|
+
const nameSym = Symbol.for('drizzle:Name');
|
|
54
|
+
return String((table as Record<symbol, unknown>)[nameSym]);
|
|
55
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { index, integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Drizzle schema definition for durable inter-agent inbox messages.
|
|
5
|
+
*/
|
|
6
|
+
export const inboxMessages = sqliteTable(
|
|
7
|
+
'inbox_messages',
|
|
8
|
+
{
|
|
9
|
+
id: text('id').primaryKey(),
|
|
10
|
+
fromId: text('from_id'),
|
|
11
|
+
toId: text('to_id').notNull(),
|
|
12
|
+
body: text('body').notNull(),
|
|
13
|
+
status: text('status').notNull().default('queued'),
|
|
14
|
+
inReplyTo: text('in_reply_to'),
|
|
15
|
+
createdAt: integer('created_at').notNull(),
|
|
16
|
+
updatedAt: integer('updated_at').notNull(),
|
|
17
|
+
deliveredAt: integer('delivered_at'),
|
|
18
|
+
injectAttempts: integer('inject_attempts').notNull().default(0),
|
|
19
|
+
injectError: text('inject_error'),
|
|
20
|
+
},
|
|
21
|
+
(table) => [index('idx_inbox_messages_to_status').on(table.toId, table.status)],
|
|
22
|
+
);
|
package/src/schema/index.ts
CHANGED
|
@@ -2,4 +2,5 @@ export { index, integer, text } from 'drizzle-orm/sqlite-core';
|
|
|
2
2
|
export * from './common';
|
|
3
3
|
export { generateCreateTableSql } from './ddl';
|
|
4
4
|
export { type DefinedTable, defineTable } from './define-table';
|
|
5
|
+
export { inboxMessages } from './inbox-messages';
|
|
5
6
|
export { queueJobs } from './queue-jobs';
|
package/src/schema/runtime.ts
CHANGED