@cat-factory/worker 0.224.2 → 0.225.2

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.
Files changed (44) hide show
  1. package/dist/index.d.ts.map +1 -1
  2. package/dist/index.js +9 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/infrastructure/config/retention.d.ts.map +1 -1
  5. package/dist/infrastructure/config/retention.js +1 -0
  6. package/dist/infrastructure/config/retention.js.map +1 -1
  7. package/dist/infrastructure/container-assembly.d.ts.map +1 -1
  8. package/dist/infrastructure/container-assembly.js +4 -1
  9. package/dist/infrastructure/container-assembly.js.map +1 -1
  10. package/dist/infrastructure/container-notification-deps.d.ts +6 -0
  11. package/dist/infrastructure/container-notification-deps.d.ts.map +1 -1
  12. package/dist/infrastructure/container-notification-deps.js +23 -1
  13. package/dist/infrastructure/container-notification-deps.js.map +1 -1
  14. package/dist/infrastructure/env.d.ts +6 -0
  15. package/dist/infrastructure/env.d.ts.map +1 -1
  16. package/dist/infrastructure/env.js.map +1 -1
  17. package/dist/infrastructure/repositories/D1DirectoryRepository.d.ts +25 -0
  18. package/dist/infrastructure/repositories/D1DirectoryRepository.d.ts.map +1 -0
  19. package/dist/infrastructure/repositories/D1DirectoryRepository.js +240 -0
  20. package/dist/infrastructure/repositories/D1DirectoryRepository.js.map +1 -0
  21. package/dist/infrastructure/repositories/D1DirectoryWebhookRepository.d.ts +17 -0
  22. package/dist/infrastructure/repositories/D1DirectoryWebhookRepository.d.ts.map +1 -0
  23. package/dist/infrastructure/repositories/D1DirectoryWebhookRepository.js +88 -0
  24. package/dist/infrastructure/repositories/D1DirectoryWebhookRepository.js.map +1 -0
  25. package/dist/infrastructure/repositories/D1MembershipRepository.d.ts +3 -1
  26. package/dist/infrastructure/repositories/D1MembershipRepository.d.ts.map +1 -1
  27. package/dist/infrastructure/repositories/D1MembershipRepository.js +1 -1
  28. package/dist/infrastructure/repositories/D1MembershipRepository.js.map +1 -1
  29. package/dist/infrastructure/repositories/D1PublicApiKeyRepository.d.ts +11 -4
  30. package/dist/infrastructure/repositories/D1PublicApiKeyRepository.d.ts.map +1 -1
  31. package/dist/infrastructure/repositories/D1PublicApiKeyRepository.js +65 -22
  32. package/dist/infrastructure/repositories/D1PublicApiKeyRepository.js.map +1 -1
  33. package/dist/infrastructure/workflows/retention.d.ts +7 -2
  34. package/dist/infrastructure/workflows/retention.d.ts.map +1 -1
  35. package/dist/infrastructure/workflows/retention.js +2 -1
  36. package/dist/infrastructure/workflows/retention.js.map +1 -1
  37. package/migrations/0107_directory_changes.sql +1 -1
  38. package/migrations/0108_public_api_key_workspaces.sql +23 -0
  39. package/migrations/0109_directory_webhooks.sql +18 -0
  40. package/package.json +19 -19
  41. package/dist/infrastructure/repositories/D1DirectoryChangeRepository.d.ts +0 -12
  42. package/dist/infrastructure/repositories/D1DirectoryChangeRepository.d.ts.map +0 -1
  43. package/dist/infrastructure/repositories/D1DirectoryChangeRepository.js +0 -39
  44. package/dist/infrastructure/repositories/D1DirectoryChangeRepository.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"D1DirectoryRepository.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/repositories/D1DirectoryRepository.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,240 @@
1
+ import { directoryChangeFromRow, WORKSPACE_DIRECTORY_ENTITY_TYPES, } from '@cat-factory/kernel';
2
+ import {} from '@cat-factory/contracts';
3
+ import { chunkForIn } from './chunk';
4
+ import { parseRoles } from './D1MembershipRepository';
5
+ const toWorkspace = (row) => ({
6
+ id: row.id,
7
+ name: row.name,
8
+ description: row.description,
9
+ accessMode: row.access_mode === 'restricted' ? 'restricted' : 'account',
10
+ });
11
+ const toUser = (row) => ({
12
+ id: row.id,
13
+ name: row.name,
14
+ email: row.email,
15
+ avatarUrl: row.avatar_url,
16
+ });
17
+ const toAccountMembership = (row) => ({
18
+ userId: row.user_id,
19
+ roles: parseRoles(row.roles),
20
+ createdAt: row.created_at,
21
+ });
22
+ const toWorkspaceMembership = (row) => ({
23
+ workspaceId: row.workspace_id,
24
+ userId: row.user_id,
25
+ role: row.role === 'admin' || row.role === 'member' ? row.role : 'viewer',
26
+ createdAt: row.created_at,
27
+ });
28
+ const toRepo = (row) => ({
29
+ workspaceId: row.workspace_id,
30
+ repoId: row.github_id,
31
+ provider: row.provider === 'gitlab' ? 'gitlab' : 'github',
32
+ owner: row.owner,
33
+ name: row.name,
34
+ defaultBranch: row.default_branch,
35
+ private: row.private === 1,
36
+ monorepo: row.is_monorepo === 1,
37
+ });
38
+ /** `AND <column> IN (...)` for a workspace reach, or nothing for an unrestricted one. */
39
+ function reachClause(column, workspaceIds) {
40
+ if (workspaceIds === null)
41
+ return { sql: '', binds: [] };
42
+ if (workspaceIds.length === 0)
43
+ return { sql: ' AND 0 = 1', binds: [] };
44
+ return {
45
+ sql: ` AND ${column} IN (${workspaceIds.map(() => '?').join(', ')})`,
46
+ binds: workspaceIds,
47
+ };
48
+ }
49
+ export class D1DirectoryRepository {
50
+ db;
51
+ constructor({ db }) {
52
+ this.db = db;
53
+ }
54
+ async listChanges(accountId, afterSeq, upToSeq, limit, workspaceIds) {
55
+ // A restricted key reads its workspaces' changes plus the deletion of ANY workspace that no
56
+ // longer exists: deleting a board also drops the key's grant on it, so without the second arm
57
+ // the key would never learn that a board it mirrored is gone.
58
+ const reach = reachClause('workspace_id', workspaceIds);
59
+ const restricted = workspaceIds === null
60
+ ? { sql: '', binds: [] }
61
+ : {
62
+ sql: ` AND ((entity_type IN (${WORKSPACE_DIRECTORY_ENTITY_TYPES.map(() => '?').join(', ')})${reach.sql})
63
+ OR (entity_type = 'workspace'
64
+ AND NOT EXISTS (SELECT 1 FROM workspaces w WHERE w.id = directory_changes.entity_id)))`,
65
+ binds: [...WORKSPACE_DIRECTORY_ENTITY_TYPES, ...reach.binds],
66
+ };
67
+ const { results } = await this.db
68
+ .prepare(`SELECT * FROM directory_changes
69
+ WHERE account_id = ? AND seq > ? AND seq <= ?${restricted.sql}
70
+ ORDER BY seq ASC LIMIT ?`)
71
+ .bind(accountId, afterSeq, upToSeq, ...restricted.binds, limit)
72
+ .all();
73
+ return results.map(directoryChangeFromRow);
74
+ }
75
+ async headSeq(accountId) {
76
+ const row = await this.db
77
+ .prepare('SELECT MAX(seq) AS head FROM directory_changes WHERE account_id = ?')
78
+ .bind(accountId)
79
+ .first();
80
+ return row?.head ?? 0;
81
+ }
82
+ async headSeqs(accountIds) {
83
+ const heads = new Map(accountIds.map((id) => [id, 0]));
84
+ for (const chunk of chunkForIn(accountIds)) {
85
+ const { results } = await this.db
86
+ .prepare(`SELECT account_id, MAX(seq) AS head FROM directory_changes
87
+ WHERE account_id IN (${chunk.map(() => '?').join(', ')}) GROUP BY account_id`)
88
+ .bind(...chunk)
89
+ .all();
90
+ for (const row of results ?? [])
91
+ heads.set(row.account_id, row.head);
92
+ }
93
+ return heads;
94
+ }
95
+ async oldestSeq(accountId) {
96
+ const row = await this.db
97
+ .prepare('SELECT MIN(seq) AS oldest FROM directory_changes WHERE account_id = ?')
98
+ .bind(accountId)
99
+ .first();
100
+ return row?.oldest ?? null;
101
+ }
102
+ async pruneChanges(before) {
103
+ // A `seq` PREFIX per account, up to its newest row older than the cutoff. `at` is not monotonic
104
+ // in `seq` (a writer stamps it before waiting on the feed's ordering), so deleting by `at` alone
105
+ // could remove a row while keeping an older-`seq` one, leaving a hole behind the oldest row that
106
+ // the reader's expiry check cannot see.
107
+ const result = await this.db
108
+ .prepare(`WITH cut AS (SELECT account_id, MAX(seq) AS upto FROM directory_changes
109
+ WHERE at < ? GROUP BY account_id)
110
+ DELETE FROM directory_changes
111
+ WHERE account_id IN (SELECT account_id FROM cut)
112
+ AND seq <= (SELECT upto FROM cut WHERE cut.account_id = directory_changes.account_id)
113
+ AND seq < (SELECT MAX(d.seq) FROM directory_changes d
114
+ WHERE d.account_id = directory_changes.account_id)`)
115
+ .bind(before)
116
+ .run();
117
+ return result.meta.changes ?? 0;
118
+ }
119
+ async listWorkspaces(accountId, after, limit, workspaceIds) {
120
+ const reach = reachClause('id', workspaceIds);
121
+ const { results } = await this.db
122
+ .prepare(`SELECT id, name, description, access_mode FROM workspaces
123
+ WHERE account_id = ? AND id > ?${reach.sql}
124
+ ORDER BY id ASC LIMIT ?`)
125
+ .bind(accountId, after ?? '', ...reach.binds, limit)
126
+ .all();
127
+ return results.map(toWorkspace);
128
+ }
129
+ async listUsers(accountId, after, limit) {
130
+ const { results } = await this.db
131
+ .prepare(`SELECT u.id, u.name, u.email, u.avatar_url FROM users u
132
+ JOIN memberships m ON m.user_id = u.id
133
+ WHERE m.account_id = ? AND u.id > ?
134
+ ORDER BY u.id ASC LIMIT ?`)
135
+ .bind(accountId, after ?? '', limit)
136
+ .all();
137
+ return results.map(toUser);
138
+ }
139
+ async listAccountMemberships(accountId, after, limit) {
140
+ const { results } = await this.db
141
+ .prepare(`SELECT user_id, roles, created_at FROM memberships
142
+ WHERE account_id = ? AND user_id > ?
143
+ ORDER BY user_id ASC LIMIT ?`)
144
+ .bind(accountId, after ?? '', limit)
145
+ .all();
146
+ return results.map(toAccountMembership);
147
+ }
148
+ async listWorkspaceMemberships(accountId, after, limit, workspaceIds) {
149
+ const reach = reachClause('m.workspace_id', workspaceIds);
150
+ const { results } = await this.db
151
+ .prepare(`SELECT m.workspace_id, m.user_id, m.role, m.created_at
152
+ FROM workspace_members m JOIN workspaces w ON w.id = m.workspace_id
153
+ WHERE w.account_id = ?
154
+ AND (m.workspace_id > ? OR (m.workspace_id = ? AND m.user_id > ?))${reach.sql}
155
+ ORDER BY m.workspace_id ASC, m.user_id ASC LIMIT ?`)
156
+ .bind(accountId, after?.workspaceId ?? '', after?.workspaceId ?? '', after?.userId ?? '', ...reach.binds, limit)
157
+ .all();
158
+ return results.map(toWorkspaceMembership);
159
+ }
160
+ async listRepos(accountId, after, limit, workspaceIds) {
161
+ const reach = reachClause('r.workspace_id', workspaceIds);
162
+ const { results } = await this.db
163
+ .prepare(`SELECT r.workspace_id, r.github_id, r.provider, r.owner, r.name, r.default_branch,
164
+ r.private, r.is_monorepo
165
+ FROM github_repos r JOIN workspaces w ON w.id = r.workspace_id
166
+ WHERE w.account_id = ? AND r.deleted_at IS NULL
167
+ AND (r.workspace_id > ? OR (r.workspace_id = ? AND r.github_id > ?))${reach.sql}
168
+ ORDER BY r.workspace_id ASC, r.github_id ASC LIMIT ?`)
169
+ .bind(accountId, after?.workspaceId ?? '', after?.workspaceId ?? '', after?.repoId ?? -1, ...reach.binds, limit)
170
+ .all();
171
+ return results.map(toRepo);
172
+ }
173
+ async getWorkspaces(accountId, ids) {
174
+ const out = [];
175
+ for (const chunk of chunkForIn(ids)) {
176
+ const { results } = await this.db
177
+ .prepare(`SELECT id, name, description, access_mode FROM workspaces
178
+ WHERE account_id = ? AND id IN (${chunk.map(() => '?').join(', ')})`)
179
+ .bind(accountId, ...chunk)
180
+ .all();
181
+ out.push(...results.map(toWorkspace));
182
+ }
183
+ return out;
184
+ }
185
+ async getUsers(accountId, ids) {
186
+ const out = [];
187
+ for (const chunk of chunkForIn(ids)) {
188
+ const { results } = await this.db
189
+ .prepare(`SELECT u.id, u.name, u.email, u.avatar_url FROM users u
190
+ JOIN memberships m ON m.user_id = u.id
191
+ WHERE m.account_id = ? AND u.id IN (${chunk.map(() => '?').join(', ')})`)
192
+ .bind(accountId, ...chunk)
193
+ .all();
194
+ out.push(...results.map(toUser));
195
+ }
196
+ return out;
197
+ }
198
+ async getAccountMemberships(accountId, userIds) {
199
+ const out = [];
200
+ for (const chunk of chunkForIn(userIds)) {
201
+ const { results } = await this.db
202
+ .prepare(`SELECT user_id, roles, created_at FROM memberships
203
+ WHERE account_id = ? AND user_id IN (${chunk.map(() => '?').join(', ')})`)
204
+ .bind(accountId, ...chunk)
205
+ .all();
206
+ out.push(...results.map(toAccountMembership));
207
+ }
208
+ return out;
209
+ }
210
+ async getWorkspaceMemberships(accountId, keys) {
211
+ const out = [];
212
+ for (const chunk of chunkForIn(keys, 2)) {
213
+ const match = chunk.map(() => '(m.workspace_id = ? AND m.user_id = ?)').join(' OR ');
214
+ const { results } = await this.db
215
+ .prepare(`SELECT m.workspace_id, m.user_id, m.role, m.created_at
216
+ FROM workspace_members m JOIN workspaces w ON w.id = m.workspace_id
217
+ WHERE w.account_id = ? AND (${match})`)
218
+ .bind(accountId, ...chunk.flatMap((k) => [k.workspaceId, k.userId]))
219
+ .all();
220
+ out.push(...results.map(toWorkspaceMembership));
221
+ }
222
+ return out;
223
+ }
224
+ async getRepos(accountId, keys) {
225
+ const out = [];
226
+ for (const chunk of chunkForIn(keys, 2)) {
227
+ const match = chunk.map(() => '(r.workspace_id = ? AND r.github_id = ?)').join(' OR ');
228
+ const { results } = await this.db
229
+ .prepare(`SELECT r.workspace_id, r.github_id, r.provider, r.owner, r.name, r.default_branch,
230
+ r.private, r.is_monorepo
231
+ FROM github_repos r JOIN workspaces w ON w.id = r.workspace_id
232
+ WHERE w.account_id = ? AND r.deleted_at IS NULL AND (${match})`)
233
+ .bind(accountId, ...chunk.flatMap((k) => [k.workspaceId, k.repoId]))
234
+ .all();
235
+ out.push(...results.map(toRepo));
236
+ }
237
+ return out;
238
+ }
239
+ }
240
+ //# sourceMappingURL=D1DirectoryRepository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"D1DirectoryRepository.js","sourceRoot":"","sources":["../../../src/infrastructure/repositories/D1DirectoryRepository.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { DirectoryWebhookRecord, DirectoryWebhookRepository } from '@cat-factory/kernel';
2
+ import type { D1Database } from '@cloudflare/workers-types';
3
+ export declare class D1DirectoryWebhookRepository implements DirectoryWebhookRepository {
4
+ private readonly db;
5
+ constructor({ db }: {
6
+ db: D1Database;
7
+ });
8
+ list(accountId: string): Promise<DirectoryWebhookRecord[]>;
9
+ get(accountId: string, id: string): Promise<DirectoryWebhookRecord | null>;
10
+ put(record: DirectoryWebhookRecord, limit: number): Promise<'stored' | 'limit_reached'>;
11
+ delete(accountId: string, id: string): Promise<void>;
12
+ listEnabled(): Promise<DirectoryWebhookRecord[]>;
13
+ claim(accountId: string, id: string, token: string, now: number, leaseUntil: number): Promise<number | null>;
14
+ complete(accountId: string, id: string, token: string, toSeq: number): Promise<boolean>;
15
+ release(accountId: string, id: string, token: string): Promise<void>;
16
+ }
17
+ //# sourceMappingURL=D1DirectoryWebhookRepository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"D1DirectoryWebhookRepository.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/repositories/D1DirectoryWebhookRepository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAA;AAC7F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AA4B3D,qBAAa,4BAA6B,YAAW,0BAA0B;IAC7E,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAY;IAE/B,YAAY,EAAE,EAAE,EAAE,EAAE;QAAE,EAAE,EAAE,UAAU,CAAA;KAAE,EAErC;IAEK,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAM/D;IAEK,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAM/E;IAEK,GAAG,CAAC,MAAM,EAAE,sBAAsB,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,eAAe,CAAC,CAgC5F;IAEK,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKzD;IAEK,WAAW,IAAI,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAKrD;IAEK,KAAK,CACT,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAWxB;IAEK,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAS5F;IAEK,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQzE;CACF"}
@@ -0,0 +1,88 @@
1
+ function toRecord(row) {
2
+ return {
3
+ accountId: row.account_id,
4
+ id: row.id,
5
+ url: row.url,
6
+ enabled: row.enabled === 1,
7
+ secretSealed: row.secret_sealed,
8
+ deliveredSeq: row.delivered_seq,
9
+ updatedAt: row.updated_at,
10
+ };
11
+ }
12
+ export class D1DirectoryWebhookRepository {
13
+ db;
14
+ constructor({ db }) {
15
+ this.db = db;
16
+ }
17
+ async list(accountId) {
18
+ const { results } = await this.db
19
+ .prepare('SELECT * FROM directory_webhooks WHERE account_id = ? ORDER BY id ASC')
20
+ .bind(accountId)
21
+ .all();
22
+ return results.map(toRecord);
23
+ }
24
+ async get(accountId, id) {
25
+ const row = await this.db
26
+ .prepare('SELECT * FROM directory_webhooks WHERE account_id = ? AND id = ?')
27
+ .bind(accountId, id)
28
+ .first();
29
+ return row ? toRecord(row) : null;
30
+ }
31
+ async put(record, limit) {
32
+ // One conditional statement, so the count and the insert cannot be split by a concurrent
33
+ // registration: an existing endpoint is always admitted (the count includes it), a new one only
34
+ // under the cap. SQLite serializes writers, which is what makes this single statement enough.
35
+ const result = await this.db
36
+ .prepare(`INSERT INTO directory_webhooks
37
+ (account_id, id, url, enabled, secret_sealed, delivered_seq, updated_at)
38
+ SELECT ?, ?, ?, ?, ?, ?, ?
39
+ WHERE EXISTS (SELECT 1 FROM directory_webhooks WHERE account_id = ? AND id = ?)
40
+ OR (SELECT COUNT(*) FROM directory_webhooks WHERE account_id = ?) < ?
41
+ ON CONFLICT (account_id, id) DO UPDATE SET
42
+ url = excluded.url,
43
+ enabled = excluded.enabled,
44
+ secret_sealed = excluded.secret_sealed,
45
+ updated_at = excluded.updated_at`)
46
+ .bind(record.accountId, record.id, record.url, record.enabled ? 1 : 0, record.secretSealed, record.deliveredSeq, record.updatedAt, record.accountId, record.id, record.accountId, limit)
47
+ .run();
48
+ return (result.meta.changes ?? 0) > 0 ? 'stored' : 'limit_reached';
49
+ }
50
+ async delete(accountId, id) {
51
+ await this.db
52
+ .prepare('DELETE FROM directory_webhooks WHERE account_id = ? AND id = ?')
53
+ .bind(accountId, id)
54
+ .run();
55
+ }
56
+ async listEnabled() {
57
+ const { results } = await this.db
58
+ .prepare('SELECT * FROM directory_webhooks WHERE enabled = 1 ORDER BY account_id, id')
59
+ .all();
60
+ return results.map(toRecord);
61
+ }
62
+ async claim(accountId, id, token, now, leaseUntil) {
63
+ const row = await this.db
64
+ .prepare(`UPDATE directory_webhooks SET lease_token = ?, lease_until = ?
65
+ WHERE account_id = ? AND id = ? AND enabled = 1
66
+ AND (lease_until IS NULL OR lease_until <= ?)
67
+ RETURNING delivered_seq`)
68
+ .bind(token, leaseUntil, accountId, id, now)
69
+ .first();
70
+ return row ? row.delivered_seq : null;
71
+ }
72
+ async complete(accountId, id, token, toSeq) {
73
+ const result = await this.db
74
+ .prepare(`UPDATE directory_webhooks SET delivered_seq = ?, lease_token = NULL, lease_until = NULL
75
+ WHERE account_id = ? AND id = ? AND lease_token = ?`)
76
+ .bind(toSeq, accountId, id, token)
77
+ .run();
78
+ return (result.meta.changes ?? 0) > 0;
79
+ }
80
+ async release(accountId, id, token) {
81
+ await this.db
82
+ .prepare(`UPDATE directory_webhooks SET lease_token = NULL, lease_until = NULL
83
+ WHERE account_id = ? AND id = ? AND lease_token = ?`)
84
+ .bind(accountId, id, token)
85
+ .run();
86
+ }
87
+ }
88
+ //# sourceMappingURL=D1DirectoryWebhookRepository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"D1DirectoryWebhookRepository.js","sourceRoot":"","sources":["../../../src/infrastructure/repositories/D1DirectoryWebhookRepository.ts"],"names":[],"mappings":"AAiBA,SAAS,QAAQ,CAAC,GAAQ;IACxB,OAAO;QACL,SAAS,EAAE,GAAG,CAAC,UAAU;QACzB,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,OAAO,EAAE,GAAG,CAAC,OAAO,KAAK,CAAC;QAC1B,YAAY,EAAE,GAAG,CAAC,aAAa;QAC/B,YAAY,EAAE,GAAG,CAAC,aAAa;QAC/B,SAAS,EAAE,GAAG,CAAC,UAAU;KAC1B,CAAA;AACH,CAAC;AAED,MAAM,OAAO,4BAA4B;IACtB,EAAE,CAAY;IAE/B,YAAY,EAAE,EAAE,EAAsB;QACpC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAA;IACd,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,SAAiB;QAC1B,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE;aAC9B,OAAO,CAAC,uEAAuE,CAAC;aAChF,IAAI,CAAC,SAAS,CAAC;aACf,GAAG,EAAO,CAAA;QACb,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IAC9B,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,SAAiB,EAAE,EAAU;QACrC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,EAAE;aACtB,OAAO,CAAC,kEAAkE,CAAC;aAC3E,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;aACnB,KAAK,EAAO,CAAA;QACf,OAAO,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IACnC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,MAA8B,EAAE,KAAa;QACrD,yFAAyF;QACzF,gGAAgG;QAChG,8FAA8F;QAC9F,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE;aACzB,OAAO,CACN;;;;;;;;;4CASoC,CACrC;aACA,IAAI,CACH,MAAM,CAAC,SAAS,EAChB,MAAM,CAAC,EAAE,EACT,MAAM,CAAC,GAAG,EACV,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EACtB,MAAM,CAAC,YAAY,EACnB,MAAM,CAAC,YAAY,EACnB,MAAM,CAAC,SAAS,EAChB,MAAM,CAAC,SAAS,EAChB,MAAM,CAAC,EAAE,EACT,MAAM,CAAC,SAAS,EAChB,KAAK,CACN;aACA,GAAG,EAAE,CAAA;QACR,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,eAAe,CAAA;IACpE,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,SAAiB,EAAE,EAAU;QACxC,MAAM,IAAI,CAAC,EAAE;aACV,OAAO,CAAC,gEAAgE,CAAC;aACzE,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;aACnB,GAAG,EAAE,CAAA;IACV,CAAC;IAED,KAAK,CAAC,WAAW;QACf,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE;aAC9B,OAAO,CAAC,4EAA4E,CAAC;aACrF,GAAG,EAAO,CAAA;QACb,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IAC9B,CAAC;IAED,KAAK,CAAC,KAAK,CACT,SAAiB,EACjB,EAAU,EACV,KAAa,EACb,GAAW,EACX,UAAkB;QAElB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,EAAE;aACtB,OAAO,CACN;;;kCAG0B,CAC3B;aACA,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,EAAE,GAAG,CAAC;aAC3C,KAAK,EAA6B,CAAA;QACrC,OAAO,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAA;IACvC,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,SAAiB,EAAE,EAAU,EAAE,KAAa,EAAE,KAAa;QACxE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE;aACzB,OAAO,CACN;8DACsD,CACvD;aACA,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,CAAC;aACjC,GAAG,EAAE,CAAA;QACR,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;IACvC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,SAAiB,EAAE,EAAU,EAAE,KAAa;QACxD,MAAM,IAAI,CAAC,EAAE;aACV,OAAO,CACN;8DACsD,CACvD;aACA,IAAI,CAAC,SAAS,EAAE,EAAE,EAAE,KAAK,CAAC;aAC1B,GAAG,EAAE,CAAA;IACV,CAAC;CACF"}
@@ -1,5 +1,7 @@
1
- import type { Membership, MembershipRepository } from '@cat-factory/kernel';
1
+ import type { AccountRole, Membership, MembershipRepository } from '@cat-factory/kernel';
2
2
  import type { D1Database } from '@cloudflare/workers-types';
3
+ /** Parse the CSV `roles` column into a non-empty role set (defaults to developer). */
4
+ export declare function parseRoles(csv: string | null): AccountRole[];
3
5
  /** D1-backed store of account memberships (user ↔ account + roles; migration 0043). */
4
6
  export declare class D1MembershipRepository implements MembershipRepository {
5
7
  private readonly db;
@@ -1 +1 @@
1
- {"version":3,"file":"D1MembershipRepository.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/repositories/D1MembershipRepository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,UAAU,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AACxF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AA4B3D,uFAAuF;AACvF,qBAAa,sBAAuB,YAAW,oBAAoB;IACjE,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAY;IAE/B,YAAY,EAAE,EAAE,EAAE,EAAE;QAAE,EAAE,EAAE,UAAU,CAAA;KAAE,EAErC;IAEK,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAMtD;IAEK,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAM5D;IAEK,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAMvE;IAEK,MAAM,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBlD;IAEK,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO7D;CACF"}
1
+ {"version":3,"file":"D1MembershipRepository.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/repositories/D1MembershipRepository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AACxF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAU3D,sFAAsF;AACtF,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,WAAW,EAAE,CAM5D;AAWD,uFAAuF;AACvF,qBAAa,sBAAuB,YAAW,oBAAoB;IACjE,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAY;IAE/B,YAAY,EAAE,EAAE,EAAE,EAAE;QAAE,EAAE,EAAE,UAAU,CAAA;KAAE,EAErC;IAEK,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAMtD;IAEK,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAM5D;IAEK,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAMvE;IAEK,MAAM,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBlD;IAEK,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO7D;CACF"}
@@ -1,6 +1,6 @@
1
1
  import { accountMembershipSource, appendDirectoryChanges } from './directoryFeed';
2
2
  /** Parse the CSV `roles` column into a non-empty role set (defaults to developer). */
3
- function parseRoles(csv) {
3
+ export function parseRoles(csv) {
4
4
  const roles = (csv ?? '')
5
5
  .split(',')
6
6
  .map((r) => r.trim())
@@ -1 +1 @@
1
- {"version":3,"file":"D1MembershipRepository.js","sourceRoot":"","sources":["../../../src/infrastructure/repositories/D1MembershipRepository.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"D1MembershipRepository.js","sourceRoot":"","sources":["../../../src/infrastructure/repositories/D1MembershipRepository.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,10 @@
1
1
  import type { PublicApiKeyRecord, PublicApiKeyRepository } from '@cat-factory/kernel';
2
2
  import type { D1Database } from '@cloudflare/workers-types';
3
- /** D1-backed store of the inbound public-API keys (migrations 0034 + 0053 + 0054 + 0081 + 0086 + 0089). */
3
+ /**
4
+ * D1-backed store of the inbound public-API keys and their workspace grants (migrations 0034,
5
+ * 0053, 0054, 0081, 0086, 0089, 0108). The secret is stored ONLY as a one-way peppered hash: this
6
+ * repo never sees the raw key.
7
+ */
4
8
  export declare class D1PublicApiKeyRepository implements PublicApiKeyRepository {
5
9
  private readonly db;
6
10
  constructor({ db }: {
@@ -8,9 +12,12 @@ export declare class D1PublicApiKeyRepository implements PublicApiKeyRepository
8
12
  });
9
13
  add(record: PublicApiKeyRecord): Promise<void>;
10
14
  getById(id: string): Promise<PublicApiKeyRecord | null>;
11
- listByWorkspace(workspaceId: string): Promise<PublicApiKeyRecord[]>;
15
+ listByAccount(accountId: string): Promise<PublicApiKeyRecord[]>;
16
+ countLiveByAccount(accountId: string): Promise<number>;
12
17
  markUsed(id: string, at: number): Promise<void>;
13
- revoke(workspaceId: string, id: string, at: number): Promise<void>;
14
- revokeMintedBy(workspaceId: string, minterId: string, at: number): Promise<void>;
18
+ revoke(accountId: string, id: string, at: number): Promise<void>;
19
+ revokeMintedBy(accountId: string, minterId: string, at: number): Promise<void>;
20
+ /** The workspace grants of each key, chunked under D1's bound-parameter ceiling. */
21
+ private grantsOf;
15
22
  }
16
23
  //# sourceMappingURL=D1PublicApiKeyRepository.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"D1PublicApiKeyRepository.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/repositories/D1PublicApiKeyRepository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAErF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAoC3D,2GAA2G;AAC3G,qBAAa,wBAAyB,YAAW,sBAAsB;IACrE,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAY;IAE/B,YAAY,EAAE,EAAE,EAAE,EAAE;QAAE,EAAE,EAAE,UAAU,CAAA;KAAE,EAErC;IAEK,GAAG,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBnD;IAEK,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAM5D;IAEK,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAUxE;IAEK,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKpD;IAEK,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOvE;IAEK,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CASrF;CACF"}
1
+ {"version":3,"file":"D1PublicApiKeyRepository.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/repositories/D1PublicApiKeyRepository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAErF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAqC3D;;;;GAIG;AACH,qBAAa,wBAAyB,YAAW,sBAAsB;IACrE,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAY;IAE/B,YAAY,EAAE,EAAE,EAAE,EAAE;QAAE,EAAE,EAAE,UAAU,CAAA;KAAE,EAErC;IAEK,GAAG,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BnD;IAEK,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAU5D;IAEK,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAYpE;IAEK,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAW3D;IAEK,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKpD;IAEK,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOrE;IAEK,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOnF;IAED,oFAAoF;YACtE,QAAQ;CAkBvB"}
@@ -1,8 +1,9 @@
1
- function rowToRecord(row) {
1
+ import { chunkForIn } from './chunk';
2
+ function rowToRecord(row, grants) {
2
3
  return {
3
4
  id: row.id,
4
5
  accountId: row.account_id,
5
- workspaceId: row.workspace_id,
6
+ workspaceIds: row.all_workspaces === 1 ? null : [...grants].sort(),
6
7
  label: row.label,
7
8
  scope: row.scope,
8
9
  secretHash: row.secret_hash,
@@ -15,35 +16,62 @@ function rowToRecord(row) {
15
16
  revokedAt: row.revoked_at,
16
17
  };
17
18
  }
18
- /** D1-backed store of the inbound public-API keys (migrations 0034 + 0053 + 0054 + 0081 + 0086 + 0089). */
19
+ /**
20
+ * D1-backed store of the inbound public-API keys and their workspace grants (migrations 0034,
21
+ * 0053, 0054, 0081, 0086, 0089, 0108). The secret is stored ONLY as a one-way peppered hash: this
22
+ * repo never sees the raw key.
23
+ */
19
24
  export class D1PublicApiKeyRepository {
20
25
  db;
21
26
  constructor({ db }) {
22
27
  this.db = db;
23
28
  }
24
29
  async add(record) {
25
- await this.db
26
- .prepare(`INSERT INTO public_api_keys
27
- (id, account_id, workspace_id, label, scope, secret_hash, created_by_user_id, created_by_key_id, external_identity, acts_as_user_id, created_at, last_used_at, revoked_at)
28
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
29
- .bind(record.id, record.accountId, record.workspaceId, record.label, record.scope, record.secretHash, record.createdByUserId, record.createdByKeyId, record.externalIdentity, record.actsAsUserId, record.createdAt, record.lastUsedAt, record.revokedAt)
30
- .run();
30
+ const grants = (record.workspaceIds ?? []).map((workspaceId) => this.db
31
+ .prepare('INSERT INTO public_api_key_workspaces (key_id, workspace_id) VALUES (?, ?)')
32
+ .bind(record.id, workspaceId));
33
+ await this.db.batch([
34
+ this.db
35
+ .prepare(`INSERT INTO public_api_keys
36
+ (id, account_id, all_workspaces, label, scope, secret_hash, created_by_user_id, created_by_key_id, external_identity, acts_as_user_id, created_at, last_used_at, revoked_at)
37
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
38
+ .bind(record.id, record.accountId, record.workspaceIds === null ? 1 : 0, record.label, record.scope, record.secretHash, record.createdByUserId, record.createdByKeyId, record.externalIdentity, record.actsAsUserId, record.createdAt, record.lastUsedAt, record.revokedAt),
39
+ ...grants,
40
+ ]);
31
41
  }
32
42
  async getById(id) {
33
43
  const row = await this.db
34
44
  .prepare('SELECT * FROM public_api_keys WHERE id = ?')
35
45
  .bind(id)
36
46
  .first();
37
- return row ? rowToRecord(row) : null;
47
+ if (!row)
48
+ return null;
49
+ // An unrestricted key has no grant rows, so the authentication hot path skips the second read.
50
+ if (row.all_workspaces === 1)
51
+ return rowToRecord(row, []);
52
+ const grants = await this.grantsOf([row.id]);
53
+ return rowToRecord(row, grants.get(row.id) ?? []);
38
54
  }
39
- async listByWorkspace(workspaceId) {
55
+ async listByAccount(accountId) {
40
56
  const { results } = await this.db
41
57
  .prepare(`SELECT * FROM public_api_keys
42
- WHERE workspace_id = ? AND revoked_at IS NULL
58
+ WHERE account_id = ? AND revoked_at IS NULL
43
59
  ORDER BY created_at DESC`)
44
- .bind(workspaceId)
60
+ .bind(accountId)
45
61
  .all();
46
- return (results ?? []).map(rowToRecord);
62
+ const rows = results ?? [];
63
+ const grants = await this.grantsOf(rows.filter((r) => r.all_workspaces !== 1).map((r) => r.id));
64
+ return rows.map((row) => rowToRecord(row, grants.get(row.id) ?? []));
65
+ }
66
+ async countLiveByAccount(accountId) {
67
+ const row = await this.db
68
+ .prepare(`SELECT COUNT(*) AS n FROM public_api_keys k
69
+ WHERE k.account_id = ? AND k.revoked_at IS NULL
70
+ AND (k.all_workspaces = 1
71
+ OR EXISTS (SELECT 1 FROM public_api_key_workspaces g WHERE g.key_id = k.id))`)
72
+ .bind(accountId)
73
+ .first();
74
+ return row?.n ?? 0;
47
75
  }
48
76
  async markUsed(id, at) {
49
77
  await this.db
@@ -51,19 +79,34 @@ export class D1PublicApiKeyRepository {
51
79
  .bind(at, id)
52
80
  .run();
53
81
  }
54
- async revoke(workspaceId, id, at) {
82
+ async revoke(accountId, id, at) {
55
83
  await this.db
56
- .prepare('UPDATE public_api_keys SET revoked_at = ? WHERE id = ? AND workspace_id = ? AND revoked_at IS NULL')
57
- .bind(at, id, workspaceId)
84
+ .prepare('UPDATE public_api_keys SET revoked_at = ? WHERE id = ? AND account_id = ? AND revoked_at IS NULL')
85
+ .bind(at, id, accountId)
58
86
  .run();
59
87
  }
60
- async revokeMintedBy(workspaceId, minterId, at) {
61
- // One statement over the minter index, never a list-then-revoke: the set is small, but a
62
- // read-then-write would let a key minted between the two reads survive the cascade.
88
+ async revokeMintedBy(accountId, minterId, at) {
63
89
  await this.db
64
- .prepare('UPDATE public_api_keys SET revoked_at = ? WHERE created_by_key_id = ? AND workspace_id = ? AND revoked_at IS NULL')
65
- .bind(at, minterId, workspaceId)
90
+ .prepare('UPDATE public_api_keys SET revoked_at = ? WHERE created_by_key_id = ? AND account_id = ? AND revoked_at IS NULL')
91
+ .bind(at, minterId, accountId)
66
92
  .run();
67
93
  }
94
+ /** The workspace grants of each key, chunked under D1's bound-parameter ceiling. */
95
+ async grantsOf(keyIds) {
96
+ const out = new Map();
97
+ for (const chunk of chunkForIn(keyIds)) {
98
+ const placeholders = chunk.map(() => '?').join(', ');
99
+ const { results } = await this.db
100
+ .prepare(`SELECT key_id, workspace_id FROM public_api_key_workspaces WHERE key_id IN (${placeholders})`)
101
+ .bind(...chunk)
102
+ .all();
103
+ for (const row of results ?? []) {
104
+ const list = out.get(row.key_id) ?? [];
105
+ list.push(row.workspace_id);
106
+ out.set(row.key_id, list);
107
+ }
108
+ }
109
+ return out;
110
+ }
68
111
  }
69
112
  //# sourceMappingURL=D1PublicApiKeyRepository.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"D1PublicApiKeyRepository.js","sourceRoot":"","sources":["../../../src/infrastructure/repositories/D1PublicApiKeyRepository.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"D1PublicApiKeyRepository.js","sourceRoot":"","sources":["../../../src/infrastructure/repositories/D1PublicApiKeyRepository.ts"],"names":[],"mappings":"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"}