@revealui/db 0.3.5 → 0.3.7
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 +4 -4
- package/dist/audit-store.d.ts +1 -1
- package/dist/audit-store.js +1 -1
- package/dist/cleanup/rag-site-cleanup.d.ts +2 -2
- package/dist/cleanup/rag-site-cleanup.js +2 -2
- package/dist/cleanup/stale-tokens.d.ts +6 -6
- package/dist/cleanup/stale-tokens.js +6 -6
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +20 -8
- package/dist/client/index.js.map +1 -1
- package/dist/crypto.d.ts +1 -1
- package/dist/crypto.js +3 -3
- package/dist/crypto.js.map +1 -1
- package/dist/log-transport.d.ts +1 -1
- package/dist/log-transport.js +2 -2
- package/dist/log-transport.js.map +1 -1
- package/dist/queries/boards.d.ts +13 -13
- package/dist/queries/boards.d.ts.map +1 -1
- package/dist/queries/boards.js +1 -1
- package/dist/queries/boards.js.map +1 -1
- package/dist/queries/code-provenance.d.ts +13 -13
- package/dist/queries/code-provenance.d.ts.map +1 -1
- package/dist/queries/code-provenance.js.map +1 -1
- package/dist/queries/conversations.js +1 -1
- package/dist/queries/conversations.js.map +1 -1
- package/dist/queries/media.d.ts +12 -7
- package/dist/queries/media.d.ts.map +1 -1
- package/dist/queries/media.js +16 -2
- package/dist/queries/media.js.map +1 -1
- package/dist/queries/oauth-accounts.d.ts +9 -0
- package/dist/queries/oauth-accounts.d.ts.map +1 -0
- package/dist/queries/oauth-accounts.js +15 -0
- package/dist/queries/oauth-accounts.js.map +1 -0
- package/dist/queries/orders.d.ts +10 -5
- package/dist/queries/orders.d.ts.map +1 -1
- package/dist/queries/orders.js +14 -1
- package/dist/queries/orders.js.map +1 -1
- package/dist/queries/pages.d.ts +8 -8
- package/dist/queries/pages.d.ts.map +1 -1
- package/dist/queries/pages.js.map +1 -1
- package/dist/queries/passkeys.d.ts +21 -0
- package/dist/queries/passkeys.d.ts.map +1 -0
- package/dist/queries/passkeys.js +19 -0
- package/dist/queries/passkeys.js.map +1 -0
- package/dist/queries/posts.d.ts +16 -11
- package/dist/queries/posts.d.ts.map +1 -1
- package/dist/queries/posts.js +16 -2
- package/dist/queries/posts.js.map +1 -1
- package/dist/queries/products.d.ts +13 -8
- package/dist/queries/products.d.ts.map +1 -1
- package/dist/queries/products.js +15 -1
- package/dist/queries/products.js.map +1 -1
- package/dist/queries/sessions.d.ts +30 -0
- package/dist/queries/sessions.d.ts.map +1 -0
- package/dist/queries/sessions.js +37 -0
- package/dist/queries/sessions.js.map +1 -0
- package/dist/queries/sites.d.ts +18 -12
- package/dist/queries/sites.d.ts.map +1 -1
- package/dist/queries/sites.js +15 -1
- package/dist/queries/sites.js.map +1 -1
- package/dist/queries/ticket-comments.d.ts +8 -8
- package/dist/queries/ticket-comments.d.ts.map +1 -1
- package/dist/queries/ticket-comments.js +2 -2
- package/dist/queries/ticket-comments.js.map +1 -1
- package/dist/queries/ticket-labels.d.ts +9 -9
- package/dist/queries/ticket-labels.d.ts.map +1 -1
- package/dist/queries/ticket-labels.js.map +1 -1
- package/dist/queries/tickets.d.ts +13 -13
- package/dist/queries/tickets.d.ts.map +1 -1
- package/dist/queries/tickets.js +1 -1
- package/dist/queries/tickets.js.map +1 -1
- package/dist/queries/user-api-keys.d.ts +28 -0
- package/dist/queries/user-api-keys.d.ts.map +1 -0
- package/dist/queries/user-api-keys.js +49 -0
- package/dist/queries/user-api-keys.js.map +1 -0
- package/dist/queries/users.d.ts +87 -10
- package/dist/queries/users.d.ts.map +1 -1
- package/dist/queries/users.js +46 -1
- package/dist/queries/users.js.map +1 -1
- package/dist/saga/crdt-resolver.d.ts +4 -4
- package/dist/saga/crdt-resolver.js +10 -10
- package/dist/saga/crdt-resolver.js.map +1 -1
- package/dist/saga/idempotent-operation.d.ts +1 -1
- package/dist/saga/idempotent-operation.js +2 -2
- package/dist/saga/idempotent-operation.js.map +1 -1
- package/dist/saga/index.d.ts +1 -1
- package/dist/saga/index.js +1 -1
- package/dist/saga/neon-saga.d.ts +2 -2
- package/dist/saga/neon-saga.js +9 -9
- package/dist/saga/neon-saga.js.map +1 -1
- package/dist/saga/recovery.d.ts +2 -2
- package/dist/saga/recovery.js +2 -2
- package/dist/saga/resilient-step.d.ts +1 -1
- package/dist/saga/resilient-step.js +2 -2
- package/dist/saga/resilient-step.js.map +1 -1
- package/dist/saga/types.d.ts +2 -2
- package/dist/schema/accounts.d.ts.map +1 -1
- package/dist/schema/accounts.js +29 -6
- package/dist/schema/accounts.js.map +1 -1
- package/dist/schema/{cms.d.ts → admin.d.ts} +3 -3
- package/dist/schema/admin.d.ts.map +1 -0
- package/dist/schema/{cms.js → admin.js} +26 -11
- package/dist/schema/admin.js.map +1 -0
- package/dist/schema/agents.d.ts +1 -1
- package/dist/schema/agents.d.ts.map +1 -1
- package/dist/schema/agents.js +39 -15
- package/dist/schema/agents.js.map +1 -1
- package/dist/schema/api-keys.d.ts +1 -1
- package/dist/schema/api-keys.d.ts.map +1 -1
- package/dist/schema/api-keys.js +10 -4
- package/dist/schema/api-keys.js.map +1 -1
- package/dist/schema/app-logs.d.ts +1 -1
- package/dist/schema/app-logs.js +2 -2
- package/dist/schema/app-logs.js.map +1 -1
- package/dist/schema/audit-log.d.ts +17 -0
- package/dist/schema/audit-log.d.ts.map +1 -1
- package/dist/schema/audit-log.js +2 -0
- package/dist/schema/audit-log.js.map +1 -1
- package/dist/schema/circuit-breaker.d.ts.map +1 -1
- package/dist/schema/circuit-breaker.js +4 -1
- package/dist/schema/circuit-breaker.js.map +1 -1
- package/dist/schema/code-provenance.d.ts.map +1 -1
- package/dist/schema/code-provenance.js +4 -1
- package/dist/schema/code-provenance.js.map +1 -1
- package/dist/schema/coordination.d.ts +2 -2
- package/dist/schema/coordination.d.ts.map +1 -1
- package/dist/schema/coordination.js +6 -3
- package/dist/schema/coordination.js.map +1 -1
- package/dist/schema/error-events.d.ts +2 -2
- package/dist/schema/error-events.js +5 -5
- package/dist/schema/error-events.js.map +1 -1
- package/dist/schema/index.d.ts +17 -0
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +44 -5
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/jobs.d.ts +1 -1
- package/dist/schema/jobs.js +1 -1
- package/dist/schema/licenses.d.ts.map +1 -1
- package/dist/schema/licenses.js +19 -4
- package/dist/schema/licenses.js.map +1 -1
- package/dist/schema/marketplace.d.ts +1 -1
- package/dist/schema/marketplace.d.ts.map +1 -1
- package/dist/schema/marketplace.js +8 -5
- package/dist/schema/marketplace.js.map +1 -1
- package/dist/schema/node-ids.d.ts.map +1 -1
- package/dist/schema/node-ids.js +4 -1
- package/dist/schema/node-ids.js.map +1 -1
- package/dist/schema/oauth-accounts.d.ts +1 -1
- package/dist/schema/oauth-accounts.d.ts.map +1 -1
- package/dist/schema/oauth-accounts.js +5 -2
- package/dist/schema/oauth-accounts.js.map +1 -1
- package/dist/schema/pages.d.ts.map +1 -1
- package/dist/schema/pages.js +5 -2
- package/dist/schema/pages.js.map +1 -1
- package/dist/schema/products.d.ts.map +1 -1
- package/dist/schema/products.js +13 -4
- package/dist/schema/products.js.map +1 -1
- package/dist/schema/rag.d.ts +1 -1
- package/dist/schema/rag.d.ts.map +1 -1
- package/dist/schema/rag.js +12 -9
- package/dist/schema/rag.js.map +1 -1
- package/dist/schema/rate-limits.d.ts.map +1 -1
- package/dist/schema/rate-limits.js +8 -2
- package/dist/schema/rate-limits.js.map +1 -1
- package/dist/schema/rest.d.ts +3 -2
- package/dist/schema/rest.d.ts.map +1 -1
- package/dist/schema/rest.js +5 -4
- package/dist/schema/rest.js.map +1 -1
- package/dist/schema/revealcoin.js +1 -1
- package/dist/schema/revealcoin.js.map +1 -1
- package/dist/schema/revmarket.d.ts +971 -0
- package/dist/schema/revmarket.d.ts.map +1 -0
- package/dist/schema/revmarket.js +166 -0
- package/dist/schema/revmarket.js.map +1 -0
- package/dist/schema/sites.d.ts.map +1 -1
- package/dist/schema/sites.js +5 -2
- package/dist/schema/sites.js.map +1 -1
- package/dist/schema/tenants.d.ts.map +1 -1
- package/dist/schema/tenants.js +4 -1
- package/dist/schema/tenants.js.map +1 -1
- package/dist/schema/tickets.d.ts.map +1 -1
- package/dist/schema/tickets.js +20 -5
- package/dist/schema/tickets.js.map +1 -1
- package/dist/schema/users.d.ts.map +1 -1
- package/dist/schema/users.js +11 -5
- package/dist/schema/users.js.map +1 -1
- package/dist/schema/webhook-events.js +1 -1
- package/dist/schema/webhook-events.js.map +1 -1
- package/dist/schema/yjs-documents.d.ts.map +1 -1
- package/dist/schema/yjs-documents.js +4 -1
- package/dist/schema/yjs-documents.js.map +1 -1
- package/dist/scripts/cleanup-expired.d.ts +3 -3
- package/dist/scripts/cleanup-expired.js +4 -4
- package/dist/scripts/cleanup-expired.js.map +1 -1
- package/dist/types/database.d.ts +81 -1
- package/dist/types/database.d.ts.map +1 -1
- package/dist/types/database.js +18 -0
- package/dist/types/database.js.map +1 -1
- package/dist/types/discover.js +1 -1
- package/dist/types/discover.js.map +1 -1
- package/dist/validation/cross-db.d.ts +1 -1
- package/dist/validation/cross-db.js +3 -3
- package/dist/validation/cross-db.js.map +1 -1
- package/package.json +42 -7
- package/dist/schema/cms.d.ts.map +0 -1
- package/dist/schema/cms.js.map +0 -1
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User API key database queries
|
|
3
|
+
*/
|
|
4
|
+
import { and, eq } from 'drizzle-orm';
|
|
5
|
+
import { userApiKeys } from '../schema/api-keys.js';
|
|
6
|
+
/** Get a user's API key metadata (provider + hint, no encrypted key) */
|
|
7
|
+
export async function getApiKeyMetadata(db, userId) {
|
|
8
|
+
const result = await db
|
|
9
|
+
.select({ provider: userApiKeys.provider, keyHint: userApiKeys.keyHint })
|
|
10
|
+
.from(userApiKeys)
|
|
11
|
+
.where(eq(userApiKeys.userId, userId))
|
|
12
|
+
.limit(1);
|
|
13
|
+
return result[0] ?? null;
|
|
14
|
+
}
|
|
15
|
+
/** Get a user's encrypted API key for decryption */
|
|
16
|
+
export async function getEncryptedApiKey(db, userId) {
|
|
17
|
+
const result = await db
|
|
18
|
+
.select({
|
|
19
|
+
id: userApiKeys.id,
|
|
20
|
+
provider: userApiKeys.provider,
|
|
21
|
+
encryptedKey: userApiKeys.encryptedKey,
|
|
22
|
+
})
|
|
23
|
+
.from(userApiKeys)
|
|
24
|
+
.where(eq(userApiKeys.userId, userId))
|
|
25
|
+
.limit(1);
|
|
26
|
+
return result[0] ?? null;
|
|
27
|
+
}
|
|
28
|
+
/** Upsert an API key (delete existing for provider, then insert new) */
|
|
29
|
+
export async function upsertApiKey(db, values) {
|
|
30
|
+
// Delete existing key for this user+provider
|
|
31
|
+
await db
|
|
32
|
+
.delete(userApiKeys)
|
|
33
|
+
.where(and(eq(userApiKeys.userId, values.userId), eq(userApiKeys.provider, values.provider)));
|
|
34
|
+
const now = new Date();
|
|
35
|
+
await db.insert(userApiKeys).values({
|
|
36
|
+
...values,
|
|
37
|
+
createdAt: now,
|
|
38
|
+
updatedAt: now,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/** Delete all API keys for a user */
|
|
42
|
+
export async function deleteApiKeys(db, userId) {
|
|
43
|
+
await db.delete(userApiKeys).where(eq(userApiKeys.userId, userId));
|
|
44
|
+
}
|
|
45
|
+
/** Update lastUsedAt timestamp (fire-and-forget) */
|
|
46
|
+
export async function touchApiKeyUsage(db, keyId) {
|
|
47
|
+
await db.update(userApiKeys).set({ lastUsedAt: new Date() }).where(eq(userApiKeys.id, keyId));
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=user-api-keys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-api-keys.js","sourceRoot":"","sources":["../../src/queries/user-api-keys.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEtC,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD,wEAAwE;AACxE,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,EAAY,EAAE,MAAc;IAClE,MAAM,MAAM,GAAG,MAAM,EAAE;SACpB,MAAM,CAAC,EAAE,QAAQ,EAAE,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC;SACxE,IAAI,CAAC,WAAW,CAAC;SACjB,KAAK,CAAC,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;SACrC,KAAK,CAAC,CAAC,CAAC,CAAC;IACZ,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC3B,CAAC;AAED,oDAAoD;AACpD,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,EAAY,EAAE,MAAc;IACnE,MAAM,MAAM,GAAG,MAAM,EAAE;SACpB,MAAM,CAAC;QACN,EAAE,EAAE,WAAW,CAAC,EAAE;QAClB,QAAQ,EAAE,WAAW,CAAC,QAAQ;QAC9B,YAAY,EAAE,WAAW,CAAC,YAAY;KACvC,CAAC;SACD,IAAI,CAAC,WAAW,CAAC;SACjB,KAAK,CAAC,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;SACrC,KAAK,CAAC,CAAC,CAAC,CAAC;IACZ,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC3B,CAAC;AAED,wEAAwE;AACxE,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,EAAY,EACZ,MAMC;IAED,6CAA6C;IAC7C,MAAM,EAAE;SACL,MAAM,CAAC,WAAW,CAAC;SACnB,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAEhG,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,MAAM,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC;QAClC,GAAG,MAAM;QACT,SAAS,EAAE,GAAG;QACd,SAAS,EAAE,GAAG;KACf,CAAC,CAAC;AACL,CAAC;AAED,qCAAqC;AACrC,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,EAAY,EAAE,MAAc;IAC9D,MAAM,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,oDAAoD;AACpD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,EAAY,EAAE,KAAa;IAChE,MAAM,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,EAAE,UAAU,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC;AAChG,CAAC"}
|
package/dist/queries/users.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* User database queries with soft-delete support
|
|
3
3
|
*/
|
|
4
|
-
import type {
|
|
4
|
+
import type { Database } from '../client/index.js';
|
|
5
5
|
import { users } from '../schema/users.js';
|
|
6
6
|
export interface ListUsersOptions {
|
|
7
7
|
status?: string;
|
|
@@ -11,7 +11,7 @@ export interface ListUsersOptions {
|
|
|
11
11
|
offset?: number;
|
|
12
12
|
}
|
|
13
13
|
/** List users with optional filters and pagination */
|
|
14
|
-
export declare function getAllUsers(db:
|
|
14
|
+
export declare function getAllUsers(db: Database, options?: ListUsersOptions): Promise<{
|
|
15
15
|
id: string;
|
|
16
16
|
schemaVersion: string;
|
|
17
17
|
type: string;
|
|
@@ -46,9 +46,9 @@ export declare function getAllUsers(db: DatabaseClient, options?: ListUsersOptio
|
|
|
46
46
|
_json: unknown;
|
|
47
47
|
}[]>;
|
|
48
48
|
/** Count users matching filters (for pagination) */
|
|
49
|
-
export declare function countUsers(db:
|
|
49
|
+
export declare function countUsers(db: Database, options?: ListUsersOptions): Promise<number>;
|
|
50
50
|
/** Update a user's fields */
|
|
51
|
-
export declare function updateUser(db:
|
|
51
|
+
export declare function updateUser(db: Database, id: string, data: Partial<typeof users.$inferInsert>): Promise<{
|
|
52
52
|
id: string;
|
|
53
53
|
schemaVersion: string;
|
|
54
54
|
type: string;
|
|
@@ -83,7 +83,7 @@ export declare function updateUser(db: DatabaseClient, id: string, data: Partial
|
|
|
83
83
|
_json: unknown;
|
|
84
84
|
} | null>;
|
|
85
85
|
/** Batch-load multiple users by ID in a single query (prevents N+1) */
|
|
86
|
-
export declare function getUsersByIds(db:
|
|
86
|
+
export declare function getUsersByIds(db: Database, ids: string[]): Promise<{
|
|
87
87
|
id: string;
|
|
88
88
|
schemaVersion: string;
|
|
89
89
|
type: string;
|
|
@@ -117,7 +117,7 @@ export declare function getUsersByIds(db: DatabaseClient, ids: string[]): Promis
|
|
|
117
117
|
anonymizedAt: Date | null;
|
|
118
118
|
_json: unknown;
|
|
119
119
|
}[]>;
|
|
120
|
-
export declare function getUserById(db:
|
|
120
|
+
export declare function getUserById(db: Database, id: string): Promise<{
|
|
121
121
|
id: string;
|
|
122
122
|
schemaVersion: string;
|
|
123
123
|
type: string;
|
|
@@ -151,7 +151,7 @@ export declare function getUserById(db: DatabaseClient, id: string): Promise<{
|
|
|
151
151
|
anonymizedAt: Date | null;
|
|
152
152
|
_json: unknown;
|
|
153
153
|
} | null>;
|
|
154
|
-
export declare function getUserByEmail(db:
|
|
154
|
+
export declare function getUserByEmail(db: Database, email: string): Promise<{
|
|
155
155
|
id: string;
|
|
156
156
|
schemaVersion: string;
|
|
157
157
|
type: string;
|
|
@@ -186,9 +186,79 @@ export declare function getUserByEmail(db: DatabaseClient, email: string): Promi
|
|
|
186
186
|
_json: unknown;
|
|
187
187
|
} | null>;
|
|
188
188
|
/** Soft-delete: sets deletedAt timestamp instead of removing the row */
|
|
189
|
-
export declare function deleteUser(db:
|
|
189
|
+
export declare function deleteUser(db: Database, id: string): Promise<void>;
|
|
190
190
|
/** Restore a soft-deleted user */
|
|
191
|
-
export declare function restoreUser(db:
|
|
191
|
+
export declare function restoreUser(db: Database, id: string): Promise<{
|
|
192
|
+
id: string;
|
|
193
|
+
schemaVersion: string;
|
|
194
|
+
type: string;
|
|
195
|
+
name: string;
|
|
196
|
+
email: string | null;
|
|
197
|
+
avatarUrl: string | null;
|
|
198
|
+
password: string | null;
|
|
199
|
+
role: string;
|
|
200
|
+
status: string;
|
|
201
|
+
agentModel: string | null;
|
|
202
|
+
agentCapabilities: string[] | null;
|
|
203
|
+
agentConfig: unknown;
|
|
204
|
+
emailVerified: boolean;
|
|
205
|
+
emailVerificationToken: string | null;
|
|
206
|
+
emailVerificationTokenExpiresAt: Date | null;
|
|
207
|
+
emailVerifiedAt: Date | null;
|
|
208
|
+
tosAcceptedAt: Date | null;
|
|
209
|
+
tosVersion: string | null;
|
|
210
|
+
stripeCustomerId: string | null;
|
|
211
|
+
mfaEnabled: boolean;
|
|
212
|
+
mfaSecret: string | null;
|
|
213
|
+
mfaBackupCodes: string[] | null;
|
|
214
|
+
mfaVerifiedAt: Date | null;
|
|
215
|
+
mfaLastUsedCounter: number | null;
|
|
216
|
+
sshKeyFingerprint: string | null;
|
|
217
|
+
preferences: unknown;
|
|
218
|
+
createdAt: Date;
|
|
219
|
+
updatedAt: Date;
|
|
220
|
+
lastActiveAt: Date | null;
|
|
221
|
+
deletedAt: Date | null;
|
|
222
|
+
anonymizedAt: Date | null;
|
|
223
|
+
_json: unknown;
|
|
224
|
+
} | null>;
|
|
225
|
+
/** Create a new user and return the inserted row */
|
|
226
|
+
export declare function createUser(db: Database, data: typeof users.$inferInsert): Promise<{
|
|
227
|
+
id: string;
|
|
228
|
+
name: string;
|
|
229
|
+
schemaVersion: string;
|
|
230
|
+
type: string;
|
|
231
|
+
email: string | null;
|
|
232
|
+
avatarUrl: string | null;
|
|
233
|
+
password: string | null;
|
|
234
|
+
role: string;
|
|
235
|
+
status: string;
|
|
236
|
+
agentModel: string | null;
|
|
237
|
+
agentCapabilities: string[] | null;
|
|
238
|
+
agentConfig: unknown;
|
|
239
|
+
emailVerified: boolean;
|
|
240
|
+
emailVerificationToken: string | null;
|
|
241
|
+
emailVerificationTokenExpiresAt: Date | null;
|
|
242
|
+
emailVerifiedAt: Date | null;
|
|
243
|
+
tosAcceptedAt: Date | null;
|
|
244
|
+
tosVersion: string | null;
|
|
245
|
+
stripeCustomerId: string | null;
|
|
246
|
+
mfaEnabled: boolean;
|
|
247
|
+
mfaSecret: string | null;
|
|
248
|
+
mfaBackupCodes: string[] | null;
|
|
249
|
+
mfaVerifiedAt: Date | null;
|
|
250
|
+
mfaLastUsedCounter: number | null;
|
|
251
|
+
sshKeyFingerprint: string | null;
|
|
252
|
+
preferences: unknown;
|
|
253
|
+
createdAt: Date;
|
|
254
|
+
updatedAt: Date;
|
|
255
|
+
lastActiveAt: Date | null;
|
|
256
|
+
deletedAt: Date | null;
|
|
257
|
+
anonymizedAt: Date | null;
|
|
258
|
+
_json: unknown;
|
|
259
|
+
} | null>;
|
|
260
|
+
/** Anonymize PII for GDPR right-to-erasure. Soft-deletes if not already deleted. */
|
|
261
|
+
export declare function anonymizeUser(db: Database, id: string): Promise<{
|
|
192
262
|
id: string;
|
|
193
263
|
schemaVersion: string;
|
|
194
264
|
type: string;
|
|
@@ -223,5 +293,12 @@ export declare function restoreUser(db: DatabaseClient, id: string): Promise<{
|
|
|
223
293
|
_json: unknown;
|
|
224
294
|
} | null>;
|
|
225
295
|
/** Permanently remove a soft-deleted user (GDPR compliance / admin cleanup) */
|
|
226
|
-
export declare function purgeUser(db:
|
|
296
|
+
export declare function purgeUser(db: Database, id: string): Promise<void>;
|
|
297
|
+
/** Count active (non-deleted, status='active') users */
|
|
298
|
+
export declare function countActiveUsers(db: Database): Promise<number>;
|
|
299
|
+
/** Look up a user by their email verification token hash (non-expired only) */
|
|
300
|
+
export declare function getUserByVerificationToken(db: Database, tokenHash: string): Promise<{
|
|
301
|
+
id: string;
|
|
302
|
+
emailVerified: boolean;
|
|
303
|
+
} | null>;
|
|
227
304
|
//# sourceMappingURL=users.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/queries/users.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/queries/users.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAK3C,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,sDAAsD;AACtD,wBAAsB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,GAAE,gBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAe7E;AAED,oDAAoD;AACpD,wBAAsB,UAAU,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,GAAE,gBAAqB,mBAa5E;AAED,6BAA6B;AAC7B,wBAAsB,UAAU,CAC9B,EAAE,EAAE,QAAQ,EACZ,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,OAAO,CAAC,OAAO,KAAK,CAAC,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAQzC;AAED,uEAAuE;AACvE,wBAAsB,aAAa,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAM9D;AAED,wBAAsB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAOzD;AAED,wBAAsB,cAAc,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAO/D;AAED,wEAAwE;AACxE,wBAAsB,UAAU,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,iBAKxD;AAED,kCAAkC;AAClC,wBAAsB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAOzD;AAED,oDAAoD;AACpD,wBAAsB,UAAU,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,KAAK,CAAC,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAG7E;AAED,oFAAoF;AACpF,wBAAsB,aAAa,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAwB3D;AAED,+EAA+E;AAC/E,wBAAsB,SAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,iBAEvD;AAED,wDAAwD;AACxD,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAGpE;AAED,+EAA+E;AAC/E,wBAAsB,0BAA0B,CAAC,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM;;;UAe/E"}
|
package/dist/queries/users.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* User database queries with soft-delete support
|
|
3
3
|
*/
|
|
4
|
-
import { and, count, desc, eq, ilike, inArray, isNull } from 'drizzle-orm';
|
|
4
|
+
import { and, count, desc, eq, gt, ilike, inArray, isNull, or } from 'drizzle-orm';
|
|
5
5
|
import { users } from '../schema/users.js';
|
|
6
6
|
/** Condition that excludes soft-deleted users */
|
|
7
7
|
const notDeleted = isNull(users.deletedAt);
|
|
@@ -87,8 +87,53 @@ export async function restoreUser(db, id) {
|
|
|
87
87
|
.returning();
|
|
88
88
|
return result[0] ?? null;
|
|
89
89
|
}
|
|
90
|
+
/** Create a new user and return the inserted row */
|
|
91
|
+
export async function createUser(db, data) {
|
|
92
|
+
const result = await db.insert(users).values(data).returning();
|
|
93
|
+
return result[0] ?? null;
|
|
94
|
+
}
|
|
95
|
+
/** Anonymize PII for GDPR right-to-erasure. Soft-deletes if not already deleted. */
|
|
96
|
+
export async function anonymizeUser(db, id) {
|
|
97
|
+
const now = new Date();
|
|
98
|
+
const result = await db
|
|
99
|
+
.update(users)
|
|
100
|
+
.set({
|
|
101
|
+
name: 'Deleted User',
|
|
102
|
+
email: null,
|
|
103
|
+
avatarUrl: null,
|
|
104
|
+
password: null,
|
|
105
|
+
mfaSecret: null,
|
|
106
|
+
mfaBackupCodes: null,
|
|
107
|
+
sshKeyFingerprint: null,
|
|
108
|
+
preferences: null,
|
|
109
|
+
emailVerificationToken: null,
|
|
110
|
+
emailVerificationTokenExpiresAt: null,
|
|
111
|
+
agentConfig: null,
|
|
112
|
+
anonymizedAt: now,
|
|
113
|
+
deletedAt: now,
|
|
114
|
+
updatedAt: now,
|
|
115
|
+
status: 'deleted',
|
|
116
|
+
})
|
|
117
|
+
.where(eq(users.id, id))
|
|
118
|
+
.returning();
|
|
119
|
+
return result[0] ?? null;
|
|
120
|
+
}
|
|
90
121
|
/** Permanently remove a soft-deleted user (GDPR compliance / admin cleanup) */
|
|
91
122
|
export async function purgeUser(db, id) {
|
|
92
123
|
await db.delete(users).where(eq(users.id, id));
|
|
93
124
|
}
|
|
125
|
+
/** Count active (non-deleted, status='active') users */
|
|
126
|
+
export async function countActiveUsers(db) {
|
|
127
|
+
const result = await db.select({ total: count() }).from(users).where(eq(users.status, 'active'));
|
|
128
|
+
return result[0]?.total ?? 0;
|
|
129
|
+
}
|
|
130
|
+
/** Look up a user by their email verification token hash (non-expired only) */
|
|
131
|
+
export async function getUserByVerificationToken(db, tokenHash) {
|
|
132
|
+
const result = await db
|
|
133
|
+
.select({ id: users.id, emailVerified: users.emailVerified })
|
|
134
|
+
.from(users)
|
|
135
|
+
.where(and(eq(users.emailVerificationToken, tokenHash), or(isNull(users.emailVerificationTokenExpiresAt), gt(users.emailVerificationTokenExpiresAt, new Date()))))
|
|
136
|
+
.limit(1);
|
|
137
|
+
return result[0] ?? null;
|
|
138
|
+
}
|
|
94
139
|
//# sourceMappingURL=users.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"users.js","sourceRoot":"","sources":["../../src/queries/users.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"users.js","sourceRoot":"","sources":["../../src/queries/users.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEnF,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,iDAAiD;AACjD,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;AAU3C,sDAAsD;AACtD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,EAAY,EAAE,UAA4B,EAAE;IAC5E,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,GAAG,EAAE,EAAE,MAAM,GAAG,CAAC,EAAE,GAAG,OAAO,CAAC;IACjE,MAAM,UAAU,GAAG;QACjB,UAAU;QACV,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7C,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KACvD,CAAC;IACF,OAAO,EAAE;SACN,MAAM,EAAE;SACR,IAAI,CAAC,KAAK,CAAC;SACX,KAAK,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC;SACzB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;SAC9B,KAAK,CAAC,KAAK,CAAC;SACZ,MAAM,CAAC,MAAM,CAAC,CAAC;AACpB,CAAC;AAED,oDAAoD;AACpD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,EAAY,EAAE,UAA4B,EAAE;IAC3E,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IACzC,MAAM,UAAU,GAAG;QACjB,UAAU;QACV,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7C,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KACvD,CAAC;IACF,MAAM,MAAM,GAAG,MAAM,EAAE;SACpB,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC;SAC1B,IAAI,CAAC,KAAK,CAAC;SACX,KAAK,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;IAC7B,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,CAAC;AAC/B,CAAC;AAED,6BAA6B;AAC7B,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,EAAY,EACZ,EAAU,EACV,IAAwC;IAExC,MAAM,MAAM,GAAG,MAAM,EAAE;SACpB,MAAM,CAAC,KAAK,CAAC;SACb,GAAG,CAAC,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC;SACvC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,UAAU,CAAC,CAAC;SACxC,SAAS,EAAE,CAAC;IACf,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC3B,CAAC;AAED,uEAAuE;AACvE,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,EAAY,EAAE,GAAa;IAC7D,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAChC,OAAO,EAAE;SACN,MAAM,EAAE;SACR,IAAI,CAAC,KAAK,CAAC;SACX,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,EAAY,EAAE,EAAU;IACxD,MAAM,MAAM,GAAG,MAAM,EAAE;SACpB,MAAM,EAAE;SACR,IAAI,CAAC,KAAK,CAAC;SACX,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,UAAU,CAAC,CAAC;SACxC,KAAK,CAAC,CAAC,CAAC,CAAC;IACZ,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC3B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,EAAY,EAAE,KAAa;IAC9D,MAAM,MAAM,GAAG,MAAM,EAAE;SACpB,MAAM,EAAE;SACR,IAAI,CAAC,KAAK,CAAC;SACX,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC;SAC9C,KAAK,CAAC,CAAC,CAAC,CAAC;IACZ,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC3B,CAAC;AAED,wEAAwE;AACxE,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,EAAY,EAAE,EAAU;IACvD,MAAM,EAAE;SACL,MAAM,CAAC,KAAK,CAAC;SACb,GAAG,CAAC,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;SACxE,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED,kCAAkC;AAClC,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,EAAY,EAAE,EAAU;IACxD,MAAM,MAAM,GAAG,MAAM,EAAE;SACpB,MAAM,CAAC,KAAK,CAAC;SACb,GAAG,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;SACjE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;SACvB,SAAS,EAAE,CAAC;IACf,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC3B,CAAC;AAED,oDAAoD;AACpD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,EAAY,EAAE,IAA+B;IAC5E,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC;IAC/D,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC3B,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,EAAY,EAAE,EAAU;IAC1D,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,MAAM,MAAM,GAAG,MAAM,EAAE;SACpB,MAAM,CAAC,KAAK,CAAC;SACb,GAAG,CAAC;QACH,IAAI,EAAE,cAAc;QACpB,KAAK,EAAE,IAAI;QACX,SAAS,EAAE,IAAI;QACf,QAAQ,EAAE,IAAI;QACd,SAAS,EAAE,IAAI;QACf,cAAc,EAAE,IAAI;QACpB,iBAAiB,EAAE,IAAI;QACvB,WAAW,EAAE,IAAI;QACjB,sBAAsB,EAAE,IAAI;QAC5B,+BAA+B,EAAE,IAAI;QACrC,WAAW,EAAE,IAAI;QACjB,YAAY,EAAE,GAAG;QACjB,SAAS,EAAE,GAAG;QACd,SAAS,EAAE,GAAG;QACd,MAAM,EAAE,SAAS;KAClB,CAAC;SACD,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;SACvB,SAAS,EAAE,CAAC;IACf,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC3B,CAAC;AAED,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,EAAY,EAAE,EAAU;IACtD,MAAM,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;AACjD,CAAC;AAED,wDAAwD;AACxD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,EAAY;IACjD,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IACjG,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,CAAC;AAC/B,CAAC;AAED,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAAC,EAAY,EAAE,SAAiB;IAC9E,MAAM,MAAM,GAAG,MAAM,EAAE;SACpB,MAAM,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE,CAAC;SAC5D,IAAI,CAAC,KAAK,CAAC;SACX,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,KAAK,CAAC,sBAAsB,EAAE,SAAS,CAAC,EAC3C,EAAE,CACA,MAAM,CAAC,KAAK,CAAC,+BAA+B,CAAC,EAC7C,EAAE,CAAC,KAAK,CAAC,+BAA+B,EAAE,IAAI,IAAI,EAAE,CAAC,CACtD,CACF,CACF;SACA,KAAK,CAAC,CAAC,CAAC,CAAC;IACZ,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC3B,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CRDT Resolver
|
|
2
|
+
* CRDT Resolver - Conflict Resolution Bridge
|
|
3
3
|
*
|
|
4
4
|
* Bridges the existing CRDT classes from @revealui/ai/memory/crdt with
|
|
5
5
|
* Drizzle ORM operations for conflict-free concurrent writes over NeonDB.
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* 1. PNCounter for commutative balance updates (agent credits, usage metrics)
|
|
9
9
|
* 2. LWWRegister for last-writer-wins value updates with optimistic concurrency
|
|
10
10
|
*
|
|
11
|
-
* These patterns handle concurrent writes without coordination
|
|
11
|
+
* These patterns handle concurrent writes without coordination - the CRDT
|
|
12
12
|
* merge function resolves conflicts deterministically.
|
|
13
13
|
*
|
|
14
14
|
* @example
|
|
@@ -38,7 +38,7 @@ export interface CRDTSetResult<T> {
|
|
|
38
38
|
*
|
|
39
39
|
* This is naturally idempotent-safe when combined with an idempotency key.
|
|
40
40
|
* Under the hood it uses `SET column = column + delta` which is atomic in
|
|
41
|
-
* a single SQL statement
|
|
41
|
+
* a single SQL statement - no CRDT serialization needed for simple counters.
|
|
42
42
|
*
|
|
43
43
|
* For distributed multi-node scenarios where each node tracks its own
|
|
44
44
|
* counter state, use the full PNCounter class from @revealui/ai/memory/crdt.
|
|
@@ -58,7 +58,7 @@ export declare function crdtIncrement(db: Database, table: PgTable<TableConfig>,
|
|
|
58
58
|
* SELECT FOR UPDATE inside a real transaction for true atomic check-and-set.
|
|
59
59
|
*
|
|
60
60
|
* When running on NeonDB HTTP (no transaction support), falls back to a
|
|
61
|
-
* best-effort single-statement UPDATE
|
|
61
|
+
* best-effort single-statement UPDATE - still safe for single-row writes
|
|
62
62
|
* but without the isolation guarantee of FOR UPDATE.
|
|
63
63
|
*
|
|
64
64
|
* @param db - Database client (pg Pool for true locking, NeonDB HTTP for best-effort)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CRDT Resolver
|
|
2
|
+
* CRDT Resolver - Conflict Resolution Bridge
|
|
3
3
|
*
|
|
4
4
|
* Bridges the existing CRDT classes from @revealui/ai/memory/crdt with
|
|
5
5
|
* Drizzle ORM operations for conflict-free concurrent writes over NeonDB.
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* 1. PNCounter for commutative balance updates (agent credits, usage metrics)
|
|
9
9
|
* 2. LWWRegister for last-writer-wins value updates with optimistic concurrency
|
|
10
10
|
*
|
|
11
|
-
* These patterns handle concurrent writes without coordination
|
|
11
|
+
* These patterns handle concurrent writes without coordination - the CRDT
|
|
12
12
|
* merge function resolves conflicts deterministically.
|
|
13
13
|
*
|
|
14
14
|
* @example
|
|
@@ -27,14 +27,14 @@ import { sql } from 'drizzle-orm';
|
|
|
27
27
|
/** Maximum number of optimistic concurrency retries before giving up */
|
|
28
28
|
const MAX_RETRIES = 5;
|
|
29
29
|
// =============================================================================
|
|
30
|
-
// PNCounter Pattern
|
|
30
|
+
// PNCounter Pattern - Commutative Increments/Decrements
|
|
31
31
|
// =============================================================================
|
|
32
32
|
/**
|
|
33
33
|
* Atomically increment (or decrement) a numeric column using SQL arithmetic.
|
|
34
34
|
*
|
|
35
35
|
* This is naturally idempotent-safe when combined with an idempotency key.
|
|
36
36
|
* Under the hood it uses `SET column = column + delta` which is atomic in
|
|
37
|
-
* a single SQL statement
|
|
37
|
+
* a single SQL statement - no CRDT serialization needed for simple counters.
|
|
38
38
|
*
|
|
39
39
|
* For distributed multi-node scenarios where each node tracks its own
|
|
40
40
|
* counter state, use the full PNCounter class from @revealui/ai/memory/crdt.
|
|
@@ -63,7 +63,7 @@ export async function crdtIncrement(db, table, whereClause, column, delta) {
|
|
|
63
63
|
return { previousValue, newValue, retries: 0 };
|
|
64
64
|
}
|
|
65
65
|
// =============================================================================
|
|
66
|
-
// LWW Register Pattern
|
|
66
|
+
// LWW Register Pattern - Last-Writer-Wins with Optimistic Concurrency
|
|
67
67
|
// =============================================================================
|
|
68
68
|
/**
|
|
69
69
|
* Detect whether a Database instance supports real transactions (pg Pool).
|
|
@@ -80,7 +80,7 @@ function supportsTransactions(db) {
|
|
|
80
80
|
* SELECT FOR UPDATE inside a real transaction for true atomic check-and-set.
|
|
81
81
|
*
|
|
82
82
|
* When running on NeonDB HTTP (no transaction support), falls back to a
|
|
83
|
-
* best-effort single-statement UPDATE
|
|
83
|
+
* best-effort single-statement UPDATE - still safe for single-row writes
|
|
84
84
|
* but without the isolation guarantee of FOR UPDATE.
|
|
85
85
|
*
|
|
86
86
|
* @param db - Database client (pg Pool for true locking, NeonDB HTTP for best-effort)
|
|
@@ -91,11 +91,11 @@ function supportsTransactions(db) {
|
|
|
91
91
|
* @returns Retry count and success status
|
|
92
92
|
*/
|
|
93
93
|
export async function crdtSetWithOptimisticLock(db, table, whereClause, updates, timestampColumn = 'updatedAt') {
|
|
94
|
-
// pg Pool path
|
|
94
|
+
// pg Pool path - true transactional SELECT FOR UPDATE
|
|
95
95
|
if (supportsTransactions(db)) {
|
|
96
96
|
return crdtSetTransactional(db, table, whereClause, updates, timestampColumn);
|
|
97
97
|
}
|
|
98
|
-
// NeonDB HTTP fallback
|
|
98
|
+
// NeonDB HTTP fallback - best-effort single-statement update
|
|
99
99
|
return crdtSetBestEffort(db, table, whereClause, updates, timestampColumn);
|
|
100
100
|
}
|
|
101
101
|
/**
|
|
@@ -107,7 +107,7 @@ async function crdtSetTransactional(db, table, whereClause, updates, timestampCo
|
|
|
107
107
|
while (retries < MAX_RETRIES) {
|
|
108
108
|
try {
|
|
109
109
|
const success = await db.transaction(async (tx) => {
|
|
110
|
-
// Lock the row
|
|
110
|
+
// Lock the row - blocks concurrent writers until this tx commits
|
|
111
111
|
const rows = await tx.select().from(table).where(whereClause).for('update').limit(1);
|
|
112
112
|
if (rows.length === 0) {
|
|
113
113
|
throw new Error('CRDT set: row not found');
|
|
@@ -147,7 +147,7 @@ async function crdtSetTransactional(db, table, whereClause, updates, timestampCo
|
|
|
147
147
|
}
|
|
148
148
|
/**
|
|
149
149
|
* Best-effort single-statement UPDATE for NeonDB HTTP.
|
|
150
|
-
* No true isolation
|
|
150
|
+
* No true isolation - relies on the write being a single atomic statement.
|
|
151
151
|
*/
|
|
152
152
|
async function crdtSetBestEffort(db, table, whereClause, updates, timestampColumn) {
|
|
153
153
|
const rows = await db.select().from(table).where(whereClause).limit(1);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crdt-resolver.js","sourceRoot":"","sources":["../../src/saga/crdt-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAY,GAAG,EAAE,MAAM,aAAa,CAAC;AAS5C,gFAAgF;AAChF,QAAQ;AACR,gFAAgF;AAEhF,wEAAwE;AACxE,MAAM,WAAW,GAAG,CAAC,CAAC;AActB,gFAAgF;AAChF,
|
|
1
|
+
{"version":3,"file":"crdt-resolver.js","sourceRoot":"","sources":["../../src/saga/crdt-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* );
|
|
19
19
|
*
|
|
20
20
|
* if (alreadyProcessed) {
|
|
21
|
-
* // Email was already sent
|
|
21
|
+
* // Email was already sent - skip
|
|
22
22
|
* }
|
|
23
23
|
* ```
|
|
24
24
|
*/
|
|
@@ -53,7 +53,7 @@ export async function idempotentWrite(db, key, operationType, operation, options
|
|
|
53
53
|
if (!row.expiresAt || row.expiresAt >= new Date()) {
|
|
54
54
|
return { alreadyProcessed: true };
|
|
55
55
|
}
|
|
56
|
-
// Expired
|
|
56
|
+
// Expired - clean up and proceed
|
|
57
57
|
await db.delete(idempotencyKeys).where(eq(idempotencyKeys.key, key));
|
|
58
58
|
}
|
|
59
59
|
// Step 2: Execute the operation
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"idempotent-operation.js","sourceRoot":"","sources":["../../src/saga/idempotent-operation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEjC,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE3D,wBAAwB;AACxB,MAAM,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAgB3C;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,EAAY,EACZ,GAAW,EACX,aAAqB,EACrB,SAA2B,EAC3B,OAAgC;IAEhC,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,cAAc,CAAC;IAE/C,qCAAqC;IACrC,MAAM,QAAQ,GAAG,MAAM,EAAE;SACtB,MAAM,CAAC,EAAE,GAAG,EAAE,eAAe,CAAC,GAAG,EAAE,SAAS,EAAE,eAAe,CAAC,SAAS,EAAE,CAAC;SAC1E,IAAI,CAAC,eAAe,CAAC;SACrB,KAAK,CAAC,EAAE,CAAC,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;SACnC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEZ,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACxB,IAAI,GAAG,EAAE,CAAC;QACR,mBAAmB;QACnB,IAAI,CAAC,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,SAAS,IAAI,IAAI,IAAI,EAAE,EAAE,CAAC;YAClD,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC;QACpC,CAAC;QACD,
|
|
1
|
+
{"version":3,"file":"idempotent-operation.js","sourceRoot":"","sources":["../../src/saga/idempotent-operation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEjC,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE3D,wBAAwB;AACxB,MAAM,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAgB3C;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,EAAY,EACZ,GAAW,EACX,aAAqB,EACrB,SAA2B,EAC3B,OAAgC;IAEhC,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,cAAc,CAAC;IAE/C,qCAAqC;IACrC,MAAM,QAAQ,GAAG,MAAM,EAAE;SACtB,MAAM,CAAC,EAAE,GAAG,EAAE,eAAe,CAAC,GAAG,EAAE,SAAS,EAAE,eAAe,CAAC,SAAS,EAAE,CAAC;SAC1E,IAAI,CAAC,eAAe,CAAC;SACrB,KAAK,CAAC,EAAE,CAAC,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;SACnC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEZ,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACxB,IAAI,GAAG,EAAE,CAAC;QACR,mBAAmB;QACnB,IAAI,CAAC,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,SAAS,IAAI,IAAI,IAAI,EAAE,EAAE,CAAC;YAClD,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC;QACpC,CAAC;QACD,mCAAmC;QACnC,MAAM,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;IACvE,CAAC;IAED,gCAAgC;IAChC,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;IAEjC,qCAAqC;IACrC,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC;IAC/C,MAAM,UAAU,GACd,OAAO,EAAE,WAAW,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ;QACnE,CAAC,CAAE,MAAkC;QACrC,CAAC,CAAC,SAAS,CAAC;IAEhB,MAAM,EAAE;SACL,MAAM,CAAC,eAAe,CAAC;SACvB,MAAM,CAAC;QACN,GAAG;QACH,aAAa;QACb,MAAM,EAAE,UAAU;QAClB,SAAS,EAAE,IAAI,IAAI,EAAE;QACrB,SAAS;KACV,CAAC;SACD,mBAAmB,EAAE,CAAC;IAEzB,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,CAAC;AAC7C,CAAC"}
|
package/dist/saga/index.d.ts
CHANGED
package/dist/saga/index.js
CHANGED
package/dist/saga/neon-saga.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* NeonSaga
|
|
2
|
+
* NeonSaga - Saga Executor for NeonDB HTTP Driver
|
|
3
3
|
*
|
|
4
4
|
* Provides transaction-like guarantees over NeonDB's stateless HTTP driver
|
|
5
5
|
* by modeling multi-step writes as a saga with compensating actions.
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
*/
|
|
37
37
|
import type { SagaOptions, SagaResult, SagaStep } from './types.js';
|
|
38
38
|
/**
|
|
39
|
-
* Execute a saga
|
|
39
|
+
* Execute a saga - a sequence of individually-atomic steps with compensating
|
|
40
40
|
* actions for rollback.
|
|
41
41
|
*
|
|
42
42
|
* @param db - Database client (works with both NeonDB HTTP and pg Pool)
|
package/dist/saga/neon-saga.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* NeonSaga
|
|
2
|
+
* NeonSaga - Saga Executor for NeonDB HTTP Driver
|
|
3
3
|
*
|
|
4
4
|
* Provides transaction-like guarantees over NeonDB's stateless HTTP driver
|
|
5
5
|
* by modeling multi-step writes as a saga with compensating actions.
|
|
@@ -40,7 +40,7 @@ import { jobs } from '../schema/jobs.js';
|
|
|
40
40
|
// Default idempotency TTL: 24 hours
|
|
41
41
|
const DEFAULT_IDEMPOTENCY_TTL_MS = 24 * 60 * 60 * 1000;
|
|
42
42
|
/**
|
|
43
|
-
* Execute a saga
|
|
43
|
+
* Execute a saga - a sequence of individually-atomic steps with compensating
|
|
44
44
|
* actions for rollback.
|
|
45
45
|
*
|
|
46
46
|
* @param db - Database client (works with both NeonDB HTTP and pg Pool)
|
|
@@ -54,7 +54,7 @@ export async function executeSaga(db, sagaName, sagaKey, steps, options) {
|
|
|
54
54
|
const idempotencyKey = options?.idempotencyKey ?? `${sagaName}:${sagaKey}`;
|
|
55
55
|
const sagaId = `saga-${sagaName}-${sagaKey}-${Date.now()}`;
|
|
56
56
|
// -------------------------------------------------------------------------
|
|
57
|
-
// 1. Idempotency check
|
|
57
|
+
// 1. Idempotency check - skip if already processed
|
|
58
58
|
// -------------------------------------------------------------------------
|
|
59
59
|
const alreadyProcessed = await checkIdempotency(db, idempotencyKey);
|
|
60
60
|
if (alreadyProcessed) {
|
|
@@ -66,7 +66,7 @@ export async function executeSaga(db, sagaName, sagaKey, steps, options) {
|
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
68
|
// -------------------------------------------------------------------------
|
|
69
|
-
// 2. Create job record (outbox entry)
|
|
69
|
+
// 2. Create job record (outbox entry) - records intent before mutations
|
|
70
70
|
// -------------------------------------------------------------------------
|
|
71
71
|
const checkpointData = {
|
|
72
72
|
sagaName,
|
|
@@ -120,7 +120,7 @@ export async function executeSaga(db, sagaName, sagaKey, steps, options) {
|
|
|
120
120
|
lastOutput = output;
|
|
121
121
|
}
|
|
122
122
|
// -----------------------------------------------------------------------
|
|
123
|
-
// 4. All steps succeeded
|
|
123
|
+
// 4. All steps succeeded - mark completed and record idempotency key
|
|
124
124
|
// -----------------------------------------------------------------------
|
|
125
125
|
await db
|
|
126
126
|
.update(jobs)
|
|
@@ -137,7 +137,7 @@ export async function executeSaga(db, sagaName, sagaKey, steps, options) {
|
|
|
137
137
|
}
|
|
138
138
|
catch (executeError) {
|
|
139
139
|
// -----------------------------------------------------------------------
|
|
140
|
-
// 5. Step failed
|
|
140
|
+
// 5. Step failed - compensate completed steps in reverse order
|
|
141
141
|
// -----------------------------------------------------------------------
|
|
142
142
|
const errorMessage = executeError instanceof Error ? executeError.message : String(executeError);
|
|
143
143
|
const compensationErrors = [];
|
|
@@ -153,7 +153,7 @@ export async function executeSaga(db, sagaName, sagaKey, steps, options) {
|
|
|
153
153
|
await step.compensate(ctx, completed.output);
|
|
154
154
|
}
|
|
155
155
|
catch (compensateError) {
|
|
156
|
-
// Log but don't throw
|
|
156
|
+
// Log but don't throw - compensations are best-effort
|
|
157
157
|
const msg = compensateError instanceof Error ? compensateError.message : String(compensateError);
|
|
158
158
|
compensationErrors.push(`${completed.name}: ${msg}`);
|
|
159
159
|
}
|
|
@@ -199,7 +199,7 @@ async function checkIdempotency(db, key) {
|
|
|
199
199
|
return false;
|
|
200
200
|
const expiresAt = rows[0]?.expiresAt;
|
|
201
201
|
if (expiresAt && expiresAt < new Date()) {
|
|
202
|
-
// Expired
|
|
202
|
+
// Expired - delete and treat as not processed
|
|
203
203
|
await db.delete(idempotencyKeys).where(eq(idempotencyKeys.key, key));
|
|
204
204
|
return false;
|
|
205
205
|
}
|
|
@@ -227,7 +227,7 @@ async function recordIdempotency(db, key, operationType, ttlMs) {
|
|
|
227
227
|
.onConflictDoNothing();
|
|
228
228
|
}
|
|
229
229
|
catch {
|
|
230
|
-
// Best-effort
|
|
230
|
+
// Best-effort - if this fails, the saga still completed successfully.
|
|
231
231
|
// The next execution will just re-run (which is fine since steps are idempotent).
|
|
232
232
|
}
|
|
233
233
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neon-saga.js","sourceRoot":"","sources":["../../src/saga/neon-saga.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AASzC,oCAAoC;AACpC,MAAM,0BAA0B,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,EAA4C,EAC5C,QAAgB,EAChB,OAAe,EACf,KAAiB,EACjB,OAAqB;IAErB,MAAM,cAAc,GAAG,OAAO,EAAE,cAAc,IAAI,GAAG,QAAQ,IAAI,OAAO,EAAE,CAAC;IAC3E,MAAM,MAAM,GAAG,QAAQ,QAAQ,IAAI,OAAO,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IAE3D,4EAA4E;IAC5E,
|
|
1
|
+
{"version":3,"file":"neon-saga.js","sourceRoot":"","sources":["../../src/saga/neon-saga.ts"],"names":[],"mappings":"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"}
|
package/dist/saga/recovery.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Saga Recovery
|
|
2
|
+
* Saga Recovery - Sweep and Recover Stuck Sagas
|
|
3
3
|
*
|
|
4
4
|
* Finds saga jobs stuck in 'active' state (indicating the process crashed
|
|
5
5
|
* mid-execution) and marks them as failed. Compensation is not attempted
|
|
6
6
|
* automatically because the step functions are in code, not serialized
|
|
7
|
-
* in the job data
|
|
7
|
+
* in the job data - recovery requires re-invoking the saga definition.
|
|
8
8
|
*
|
|
9
9
|
* Designed to run as a periodic cron job or be called manually.
|
|
10
10
|
*
|