@gobing-ai/ts-db 0.4.63 → 0.4.64

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.
@@ -36,12 +36,20 @@ export interface FailedMessageDetail {
36
36
  error: string;
37
37
  timestamp: number;
38
38
  }
39
+ /** Payload for a `message.requeued` event (one per row returned by `release`). */
40
+ export interface RequeuedMessageDetail {
41
+ id: string;
42
+ fromId: string | null;
43
+ toId: string;
44
+ timestamp: number;
45
+ }
39
46
  /** Typed event map for inbox message lifecycle transitions. */
40
47
  export type InboxMessageEvents = {
41
48
  'message.enqueued': (detail: EnqueuedMessageDetail) => void;
42
49
  'message.injected': (detail: InjectedMessageDetail) => void;
43
50
  'message.delivered': (detail: DeliveredMessageDetail) => void;
44
51
  'message.failed': (detail: FailedMessageDetail) => void;
52
+ 'message.requeued': (detail: RequeuedMessageDetail) => void;
45
53
  };
46
54
  /**
47
55
  * Zero-dependency structural event sink.
@@ -68,6 +76,15 @@ export declare class InboxMessageDao extends EntityDao<typeof inboxMessages, typ
68
76
  }): Promise<InboxMessage[]>;
69
77
  markDelivered(msgId: string): Promise<void>;
70
78
  markFailed(msgId: string, error: string): Promise<void>;
79
+ /**
80
+ * Release claimed (`injected`) messages back to `queued` so a drain whose
81
+ * invocation never started stays redeliverable. Only rows currently in
82
+ * `status = 'injected'` are touched — a `queued` or `delivered` row is
83
+ * left alone, so double-release is a no-op. `injectAttempts` and
84
+ * `injectError` are left untouched: the claim counter IS the budget.
85
+ * Emits one `message.requeued` event per released row.
86
+ */
87
+ release(msgIds: string[]): Promise<number>;
71
88
  inbox(toId: string, limit?: number, offset?: number): Promise<InboxMessage[]>;
72
89
  countPending(toId: string): Promise<number>;
73
90
  getById(id: string): Promise<InboxMessage | undefined>;
@@ -1 +1 @@
1
- {"version":3,"file":"inbox-message-dao.d.ts","sourceRoot":"","sources":["../src/inbox-message-dao.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"inbox-message-dao.d.ts","sourceRoot":"","sources":["../src/inbox-message-dao.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { sql } from 'drizzle-orm';
1
+ import { and, eq, inArray, sql } from 'drizzle-orm';
2
2
  import { EntityDao } from './entity-dao.js';
3
3
  import { inboxMessages } from './schema/inbox-messages.js';
4
4
  function isPromiseLike(value) {
@@ -109,6 +109,33 @@ export class InboxMessageDao extends EntityDao {
109
109
  });
110
110
  }
111
111
  }
112
+ /**
113
+ * Release claimed (`injected`) messages back to `queued` so a drain whose
114
+ * invocation never started stays redeliverable. Only rows currently in
115
+ * `status = 'injected'` are touched — a `queued` or `delivered` row is
116
+ * left alone, so double-release is a no-op. `injectAttempts` and
117
+ * `injectError` are left untouched: the claim counter IS the budget.
118
+ * Emits one `message.requeued` event per released row.
119
+ */
120
+ async release(msgIds) {
121
+ if (msgIds.length === 0)
122
+ return 0;
123
+ const now = this.now();
124
+ const rows = (await this.db
125
+ .update(inboxMessages)
126
+ .set({ status: 'queued', updatedAt: now })
127
+ .where(and(inArray(inboxMessages.id, msgIds), eq(inboxMessages.status, 'injected')))
128
+ .returning());
129
+ for (const row of rows) {
130
+ this.emitEvent('message.requeued', {
131
+ id: row.id,
132
+ fromId: row.fromId,
133
+ toId: row.toId,
134
+ timestamp: now,
135
+ });
136
+ }
137
+ return rows.length;
138
+ }
112
139
  async inbox(toId, limit, offset) {
113
140
  return this.list({
114
141
  where: { col: inboxMessages.toId, op: 'eq', value: toId },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/ts-db",
3
- "version": "0.4.63",
3
+ "version": "0.4.64",
4
4
  "description": "@gobing-ai/ts-db — a drizzle-free database facade: typed DAOs over Bun SQLite / Cloudflare D1, a small predicate query spec, single-source-of-truth tables, and migrations. Drizzle stays an internal detail.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -1,4 +1,4 @@
1
- import { sql } from 'drizzle-orm';
1
+ import { and, eq, inArray, sql } from 'drizzle-orm';
2
2
  import type { DbAdapter } from './adapter';
3
3
  import type { UpdateReturningDb } from './drizzle-builders';
4
4
  import { EntityDao } from './entity-dao';
@@ -48,12 +48,21 @@ export interface FailedMessageDetail {
48
48
  timestamp: number;
49
49
  }
50
50
 
51
+ /** Payload for a `message.requeued` event (one per row returned by `release`). */
52
+ export interface RequeuedMessageDetail {
53
+ id: string;
54
+ fromId: string | null;
55
+ toId: string;
56
+ timestamp: number;
57
+ }
58
+
51
59
  /** Typed event map for inbox message lifecycle transitions. */
52
60
  export type InboxMessageEvents = {
53
61
  'message.enqueued': (detail: EnqueuedMessageDetail) => void;
54
62
  'message.injected': (detail: InjectedMessageDetail) => void;
55
63
  'message.delivered': (detail: DeliveredMessageDetail) => void;
56
64
  'message.failed': (detail: FailedMessageDetail) => void;
65
+ 'message.requeued': (detail: RequeuedMessageDetail) => void;
57
66
  };
58
67
 
59
68
  /**
@@ -193,6 +202,35 @@ export class InboxMessageDao extends EntityDao<typeof inboxMessages, typeof inbo
193
202
  }
194
203
  }
195
204
 
205
+ /**
206
+ * Release claimed (`injected`) messages back to `queued` so a drain whose
207
+ * invocation never started stays redeliverable. Only rows currently in
208
+ * `status = 'injected'` are touched — a `queued` or `delivered` row is
209
+ * left alone, so double-release is a no-op. `injectAttempts` and
210
+ * `injectError` are left untouched: the claim counter IS the budget.
211
+ * Emits one `message.requeued` event per released row.
212
+ */
213
+ async release(msgIds: string[]): Promise<number> {
214
+ if (msgIds.length === 0) return 0;
215
+ const now = this.now();
216
+ const rows = (await (this.db as UpdateReturningDb)
217
+ .update(inboxMessages)
218
+ .set({ status: 'queued', updatedAt: now })
219
+ .where(and(inArray(inboxMessages.id, msgIds), eq(inboxMessages.status, 'injected')))
220
+ .returning()) as InboxMessage[];
221
+
222
+ for (const row of rows) {
223
+ this.emitEvent('message.requeued', {
224
+ id: row.id,
225
+ fromId: row.fromId,
226
+ toId: row.toId,
227
+ timestamp: now,
228
+ });
229
+ }
230
+
231
+ return rows.length;
232
+ }
233
+
196
234
  async inbox(toId: string, limit?: number, offset?: number): Promise<InboxMessage[]> {
197
235
  return this.list({
198
236
  where: { col: inboxMessages.toId, op: 'eq', value: toId },