@pikku/kysely 0.12.6 → 0.12.8

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.
@@ -57,9 +57,9 @@ export class KyselyChannelStore extends ChannelStore {
57
57
  await this.db
58
58
  .insertInto('channels')
59
59
  .values({
60
- channel_id: channelId,
61
- channel_name: channelName,
62
- opening_data: JSON.stringify(openingData || {}),
60
+ channelId: channelId,
61
+ channelName: channelName,
62
+ openingData: JSON.stringify(openingData || {}),
63
63
  })
64
64
  .execute()
65
65
  }
@@ -71,7 +71,7 @@ export class KyselyChannelStore extends ChannelStore {
71
71
 
72
72
  await this.db
73
73
  .deleteFrom('channels')
74
- .where('channel_id', 'in', channelIds)
74
+ .where('channelId', 'in', channelIds)
75
75
  .execute()
76
76
  }
77
77
 
@@ -81,8 +81,8 @@ export class KyselyChannelStore extends ChannelStore {
81
81
  ): Promise<void> {
82
82
  await this.db
83
83
  .updateTable('channels')
84
- .set({ user_session: session ? JSON.stringify(session) : null })
85
- .where('channel_id', '=', channelId)
84
+ .set({ userSession: session ? JSON.stringify(session) : null })
85
+ .where('channelId', '=', channelId)
86
86
  .execute()
87
87
  }
88
88
 
@@ -91,8 +91,8 @@ export class KyselyChannelStore extends ChannelStore {
91
91
  ): Promise<Channel & { session: CoreUserSession }> {
92
92
  const row = await this.db
93
93
  .selectFrom('channels')
94
- .select(['channel_id', 'channel_name', 'opening_data', 'user_session'])
95
- .where('channel_id', '=', channelId)
94
+ .select(['channelId', 'channelName', 'openingData', 'userSession'])
95
+ .where('channelId', '=', channelId)
96
96
  .executeTakeFirst()
97
97
 
98
98
  if (!row) {
@@ -100,10 +100,10 @@ export class KyselyChannelStore extends ChannelStore {
100
100
  }
101
101
 
102
102
  return {
103
- channelId: row.channel_id,
104
- channelName: row.channel_name,
105
- openingData: parseJson(row.opening_data) ?? {},
106
- session: (parseJson(row.user_session) ?? {}) as CoreUserSession,
103
+ channelId: row.channelId,
104
+ channelName: row.channelName,
105
+ openingData: parseJson(row.openingData) ?? {},
106
+ session: (parseJson(row.userSession) ?? {}) as CoreUserSession,
107
107
  }
108
108
  }
109
109
 
@@ -84,13 +84,13 @@ export class KyselyCredentialService implements CredentialService {
84
84
  if (action === 'read' && !this.auditReads) return
85
85
 
86
86
  await this.db
87
- .insertInto('credentials_audit')
87
+ .insertInto('credentialsAudit')
88
88
  .values({
89
89
  id: crypto.randomUUID(),
90
- credential_name: name,
91
- user_id: userId ?? null,
90
+ credentialName: name,
91
+ userId: userId ?? null,
92
92
  action,
93
- performed_at: new Date().toISOString() as any,
93
+ performedAt: new Date().toISOString() as unknown as Date,
94
94
  })
95
95
  .execute()
96
96
  }
@@ -103,25 +103,25 @@ export class KyselyCredentialService implements CredentialService {
103
103
 
104
104
  private whereUserId(qb: any, userId?: string) {
105
105
  return userId
106
- ? qb.where('user_id', '=', userId)
107
- : qb.where('user_id', 'is', null)
106
+ ? qb.where('userId', '=', userId)
107
+ : qb.where('userId', 'is', null)
108
108
  }
109
109
 
110
110
  async get<T = unknown>(name: string, userId?: string): Promise<T | null> {
111
111
  let qb = this.db
112
112
  .selectFrom('credentials')
113
- .select(['ciphertext', 'wrapped_dek', 'key_version'])
113
+ .select(['ciphertext', 'wrappedDek', 'keyVersion'])
114
114
  .where('name', '=', name)
115
115
  qb = this.whereUserId(qb, userId)
116
116
 
117
117
  const row = await qb.executeTakeFirst()
118
118
  if (!row) return null
119
119
 
120
- const kek = this.getKEK(row.key_version)
120
+ const kek = this.getKEK(row.keyVersion)
121
121
  const plaintext = await envelopeDecrypt<string>(
122
122
  kek,
123
123
  row.ciphertext,
124
- row.wrapped_dek
124
+ row.wrappedDek
125
125
  )
126
126
  await this.logAudit(name, userId, 'read')
127
127
 
@@ -146,9 +146,9 @@ export class KyselyCredentialService implements CredentialService {
146
146
  .updateTable('credentials')
147
147
  .set({
148
148
  ciphertext,
149
- wrapped_dek: wrappedDEK,
150
- key_version: this.keyVersion,
151
- updated_at: now as any,
149
+ wrappedDek: wrappedDEK,
150
+ keyVersion: this.keyVersion,
151
+ updatedAt: now as unknown as Date,
152
152
  })
153
153
  .where('name', '=', name)
154
154
  qb = this.whereUserId(qb, userId)
@@ -158,12 +158,12 @@ export class KyselyCredentialService implements CredentialService {
158
158
  .insertInto('credentials')
159
159
  .values({
160
160
  name,
161
- user_id: userId ?? null,
161
+ userId: userId ?? null,
162
162
  ciphertext,
163
- wrapped_dek: wrappedDEK,
164
- key_version: this.keyVersion,
165
- created_at: now as any,
166
- updated_at: now as any,
163
+ wrappedDek: wrappedDEK,
164
+ keyVersion: this.keyVersion,
165
+ createdAt: now as unknown as Date,
166
+ updatedAt: now as unknown as Date,
167
167
  })
168
168
  .execute()
169
169
  }
@@ -193,17 +193,17 @@ export class KyselyCredentialService implements CredentialService {
193
193
  async getAll(userId: string): Promise<Record<string, unknown>> {
194
194
  const rows = await this.db
195
195
  .selectFrom('credentials')
196
- .select(['name', 'ciphertext', 'wrapped_dek', 'key_version'])
197
- .where('user_id', '=', userId)
196
+ .select(['name', 'ciphertext', 'wrappedDek', 'keyVersion'])
197
+ .where('userId', '=', userId)
198
198
  .execute()
199
199
 
200
200
  const result: Record<string, unknown> = {}
201
201
  for (const row of rows) {
202
- const kek = this.getKEK(row.key_version)
202
+ const kek = this.getKEK(row.keyVersion)
203
203
  const plaintext = await envelopeDecrypt<string>(
204
204
  kek,
205
205
  row.ciphertext,
206
- row.wrapped_dek
206
+ row.wrappedDek
207
207
  )
208
208
  try {
209
209
  result[row.name] = JSON.parse(plaintext)
@@ -216,6 +216,28 @@ export class KyselyCredentialService implements CredentialService {
216
216
  return result
217
217
  }
218
218
 
219
+ async getUsersWithCredential(name: string): Promise<string[]> {
220
+ const rows = await this.db
221
+ .selectFrom('credentials')
222
+ .select('userId')
223
+ .where('name', '=', name)
224
+ .where('userId', 'is not', null)
225
+ .execute()
226
+
227
+ return rows.map((row) => row.userId!).filter(Boolean)
228
+ }
229
+
230
+ async getAllUsers(): Promise<string[]> {
231
+ const rows = await this.db
232
+ .selectFrom('credentials')
233
+ .select('userId')
234
+ .distinct()
235
+ .where('userId', 'is not', null)
236
+ .execute()
237
+
238
+ return rows.map((row) => row.userId!).filter(Boolean)
239
+ }
240
+
219
241
  async rotateKEK(): Promise<number> {
220
242
  if (!this.previousKey) {
221
243
  throw new Error('No previousKey configured — nothing to rotate from')
@@ -223,28 +245,28 @@ export class KyselyCredentialService implements CredentialService {
223
245
 
224
246
  const rows = await this.db
225
247
  .selectFrom('credentials')
226
- .select(['name', 'user_id', 'wrapped_dek'])
227
- .where('key_version', '<', this.keyVersion)
248
+ .select(['name', 'userId', 'wrappedDek'])
249
+ .where('keyVersion', '<', this.keyVersion)
228
250
  .execute()
229
251
 
230
252
  for (const row of rows) {
231
253
  const newWrappedDEK = await envelopeRewrap(
232
254
  this.previousKey,
233
255
  this.key,
234
- row.wrapped_dek
256
+ row.wrappedDek
235
257
  )
236
258
  let qb = this.db
237
259
  .updateTable('credentials')
238
260
  .set({
239
- wrapped_dek: newWrappedDEK,
240
- key_version: this.keyVersion,
241
- updated_at: new Date().toISOString() as any,
261
+ wrappedDek: newWrappedDEK,
262
+ keyVersion: this.keyVersion,
263
+ updatedAt: new Date().toISOString() as unknown as Date,
242
264
  })
243
265
  .where('name', '=', row.name)
244
- qb = this.whereUserId(qb, row.user_id ?? undefined)
266
+ qb = this.whereUserId(qb, row.userId ?? undefined)
245
267
  await qb.execute()
246
268
 
247
- await this.logAudit(row.name, row.user_id ?? undefined, 'rotate')
269
+ await this.logAudit(row.name, row.userId ?? undefined, 'rotate')
248
270
  }
249
271
 
250
272
  return rows.length
@@ -83,32 +83,32 @@ export class KyselyDeploymentService implements DeploymentService {
83
83
 
84
84
  await this.db.transaction().execute(async (trx) => {
85
85
  await trx
86
- .insertInto('pikku_deployments')
86
+ .insertInto('pikkuDeployments')
87
87
  .values({
88
- deployment_id: config.deploymentId,
88
+ deploymentId: config.deploymentId,
89
89
  endpoint: config.endpoint,
90
- last_heartbeat: new Date(),
90
+ lastHeartbeat: new Date(),
91
91
  })
92
92
  .onConflict((oc) =>
93
- oc.column('deployment_id').doUpdateSet({
93
+ oc.column('deploymentId').doUpdateSet({
94
94
  endpoint: config.endpoint,
95
- last_heartbeat: new Date(),
95
+ lastHeartbeat: new Date(),
96
96
  })
97
97
  )
98
98
  .execute()
99
99
 
100
100
  await trx
101
- .deleteFrom('pikku_deployment_functions')
102
- .where('deployment_id', '=', config.deploymentId)
101
+ .deleteFrom('pikkuDeploymentFunctions')
102
+ .where('deploymentId', '=', config.deploymentId)
103
103
  .execute()
104
104
 
105
105
  if (functions.length > 0) {
106
106
  await trx
107
- .insertInto('pikku_deployment_functions')
107
+ .insertInto('pikkuDeploymentFunctions')
108
108
  .values(
109
109
  functions.map((fn) => ({
110
- deployment_id: config.deploymentId,
111
- function_name: fn,
110
+ deploymentId: config.deploymentId,
111
+ functionName: fn,
112
112
  }))
113
113
  )
114
114
  .execute()
@@ -129,8 +129,8 @@ export class KyselyDeploymentService implements DeploymentService {
129
129
 
130
130
  if (this.deploymentConfig) {
131
131
  await this.db
132
- .deleteFrom('pikku_deployments')
133
- .where('deployment_id', '=', this.deploymentConfig.deploymentId)
132
+ .deleteFrom('pikkuDeployments')
133
+ .where('deploymentId', '=', this.deploymentConfig.deploymentId)
134
134
  .execute()
135
135
  }
136
136
  }
@@ -140,20 +140,20 @@ export class KyselyDeploymentService implements DeploymentService {
140
140
  const cutoff = new Date(Date.now() - ttlMs)
141
141
 
142
142
  const result = await this.db
143
- .selectFrom('pikku_deployments as d')
143
+ .selectFrom('pikkuDeployments as d')
144
144
  .innerJoin(
145
- 'pikku_deployment_functions as f',
146
- 'f.deployment_id',
147
- 'd.deployment_id'
145
+ 'pikkuDeploymentFunctions as f',
146
+ 'f.deploymentId',
147
+ 'd.deploymentId'
148
148
  )
149
- .select(['d.deployment_id', 'd.endpoint'])
150
- .where('f.function_name', '=', name)
151
- .where('d.last_heartbeat', '>', cutoff)
152
- .orderBy('d.last_heartbeat', 'desc')
149
+ .select(['d.deploymentId', 'd.endpoint'])
150
+ .where('f.functionName', '=', name)
151
+ .where('d.lastHeartbeat', '>', cutoff)
152
+ .orderBy('d.lastHeartbeat', 'desc')
153
153
  .execute()
154
154
 
155
155
  return result.map((row) => ({
156
- deploymentId: row.deployment_id,
156
+ deploymentId: row.deploymentId,
157
157
  endpoint: row.endpoint,
158
158
  }))
159
159
  }
@@ -176,9 +176,9 @@ export class KyselyDeploymentService implements DeploymentService {
176
176
 
177
177
  try {
178
178
  await this.db
179
- .updateTable('pikku_deployments')
180
- .set({ last_heartbeat: new Date() })
181
- .where('deployment_id', '=', this.deploymentConfig.deploymentId)
179
+ .updateTable('pikkuDeployments')
180
+ .set({ lastHeartbeat: new Date() })
181
+ .where('deploymentId', '=', this.deploymentConfig.deploymentId)
182
182
  .execute()
183
183
  } catch {
184
184
  // Heartbeat failed, will retry on next interval
@@ -18,20 +18,20 @@ export class KyselyEventHubStore extends EventHubStore {
18
18
 
19
19
  public async getChannelIdsForTopic(topic: string): Promise<string[]> {
20
20
  const result = await this.db
21
- .selectFrom('channel_subscriptions')
22
- .select('channel_id')
21
+ .selectFrom('channelSubscriptions')
22
+ .select('channelId')
23
23
  .where('topic', '=', topic)
24
24
  .execute()
25
25
 
26
- return result.map((row) => row.channel_id)
26
+ return result.map((row) => row.channelId)
27
27
  }
28
28
 
29
29
  public async subscribe(topic: string, channelId: string): Promise<boolean> {
30
30
  try {
31
31
  await this.db
32
- .insertInto('channel_subscriptions')
33
- .values({ channel_id: channelId, topic: topic as string })
34
- .onConflict((oc) => oc.columns(['channel_id', 'topic']).doNothing())
32
+ .insertInto('channelSubscriptions')
33
+ .values({ channelId: channelId, topic: topic as string })
34
+ .onConflict((oc) => oc.columns(['channelId', 'topic']).doNothing())
35
35
  .execute()
36
36
  return true
37
37
  } catch {
@@ -41,8 +41,8 @@ export class KyselyEventHubStore extends EventHubStore {
41
41
 
42
42
  public async unsubscribe(topic: string, channelId: string): Promise<boolean> {
43
43
  const result = await this.db
44
- .deleteFrom('channel_subscriptions')
45
- .where('channel_id', '=', channelId)
44
+ .deleteFrom('channelSubscriptions')
45
+ .where('channelId', '=', channelId)
46
46
  .where('topic', '=', topic as string)
47
47
  .executeTakeFirst()
48
48
 
@@ -77,12 +77,12 @@ export class KyselySecretService implements SecretService {
77
77
  if (action === 'read' && !this.auditReads) return
78
78
 
79
79
  await this.db
80
- .insertInto('secrets_audit')
80
+ .insertInto('secretsAudit')
81
81
  .values({
82
82
  id: crypto.randomUUID(),
83
- secret_key: secretKey,
83
+ secretKey: secretKey,
84
84
  action,
85
- performed_at: new Date().toISOString() as any,
85
+ performedAt: new Date().toISOString() as unknown as Date,
86
86
  })
87
87
  .execute()
88
88
  }
@@ -96,17 +96,17 @@ export class KyselySecretService implements SecretService {
96
96
  async getSecret(key: string): Promise<string> {
97
97
  const row = await this.db
98
98
  .selectFrom('secrets')
99
- .select(['ciphertext', 'wrapped_dek', 'key_version'])
99
+ .select(['ciphertext', 'wrappedDek', 'keyVersion'])
100
100
  .where('key', '=', key)
101
101
  .executeTakeFirst()
102
102
 
103
103
  if (!row) throw new Error('Requested secret not found')
104
104
 
105
- const kek = this.getKEK(row.key_version)
105
+ const kek = this.getKEK(row.keyVersion)
106
106
  const result = await envelopeDecrypt<string>(
107
107
  kek,
108
108
  row.ciphertext,
109
- row.wrapped_dek
109
+ row.wrappedDek
110
110
  )
111
111
  await this.logAudit(key, 'read')
112
112
  return result
@@ -139,17 +139,17 @@ export class KyselySecretService implements SecretService {
139
139
  .values({
140
140
  key,
141
141
  ciphertext,
142
- wrapped_dek: wrappedDEK,
143
- key_version: this.keyVersion,
144
- created_at: now as any,
145
- updated_at: now as any,
142
+ wrappedDek: wrappedDEK,
143
+ keyVersion: this.keyVersion,
144
+ createdAt: now as unknown as Date,
145
+ updatedAt: now as unknown as Date,
146
146
  })
147
147
  .onConflict((oc) =>
148
148
  oc.column('key').doUpdateSet({
149
149
  ciphertext,
150
- wrapped_dek: wrappedDEK,
151
- key_version: this.keyVersion,
152
- updated_at: now as any,
150
+ wrappedDek: wrappedDEK,
151
+ keyVersion: this.keyVersion,
152
+ updatedAt: now as unknown as Date,
153
153
  })
154
154
  )
155
155
  .execute()
@@ -169,22 +169,22 @@ export class KyselySecretService implements SecretService {
169
169
 
170
170
  const rows = await this.db
171
171
  .selectFrom('secrets')
172
- .select(['key', 'wrapped_dek'])
173
- .where('key_version', '<', this.keyVersion)
172
+ .select(['key', 'wrappedDek'])
173
+ .where('keyVersion', '<', this.keyVersion)
174
174
  .execute()
175
175
 
176
176
  for (const row of rows) {
177
177
  const newWrappedDEK = await envelopeRewrap(
178
178
  this.previousKey,
179
179
  this.key,
180
- row.wrapped_dek
180
+ row.wrappedDek
181
181
  )
182
182
  await this.db
183
183
  .updateTable('secrets')
184
184
  .set({
185
- wrapped_dek: newWrappedDEK,
186
- key_version: this.keyVersion,
187
- updated_at: new Date().toISOString() as any,
185
+ wrappedDek: newWrappedDEK,
186
+ keyVersion: this.keyVersion,
187
+ updatedAt: new Date().toISOString() as unknown as Date,
188
188
  })
189
189
  .where('key', '=', row.key)
190
190
  .execute()
@@ -1,6 +1,6 @@
1
1
  import { describe, test, before, after } from 'node:test'
2
2
  import assert from 'node:assert/strict'
3
- import { Kysely, SqliteDialect } from 'kysely'
3
+ import { CamelCasePlugin, Kysely, SqliteDialect } from 'kysely'
4
4
  import { SerializePlugin } from 'kysely-plugin-serialize'
5
5
  import Database from 'better-sqlite3'
6
6
  import { PostgresJSDialect } from 'kysely-postgres-js'
@@ -23,7 +23,7 @@ function createSqliteDb(): Kysely<KyselyPikkuDB> {
23
23
  dialect: new SqliteDialect({
24
24
  database: new Database(':memory:'),
25
25
  }),
26
- plugins: [new SerializePlugin()],
26
+ plugins: [new CamelCasePlugin(), new SerializePlugin()],
27
27
  })
28
28
  }
29
29
 
@@ -33,6 +33,7 @@ function createPostgresDb(): Kysely<KyselyPikkuDB> | null {
33
33
 
34
34
  return new Kysely<KyselyPikkuDB>({
35
35
  dialect: new PostgresJSDialect({ postgres: postgres(url) }),
36
+ plugins: [new CamelCasePlugin()],
36
37
  })
37
38
  }
38
39
 
@@ -126,10 +127,10 @@ function registerTests(
126
127
  await service.deleteSecret('audit-test')
127
128
 
128
129
  const logs = await getDb()
129
- .selectFrom('secrets_audit')
130
- .select(['secret_key', 'action'])
131
- .where('secret_key', '=', 'audit-test')
132
- .orderBy('performed_at', 'asc')
130
+ .selectFrom('secretsAudit')
131
+ .select(['secretKey', 'action'])
132
+ .where('secretKey', '=', 'audit-test')
133
+ .orderBy('performedAt', 'asc')
133
134
  .execute()
134
135
 
135
136
  assert.equal(logs.length, 3)
@@ -149,9 +150,9 @@ function registerTests(
149
150
  await service.getSecret('no-read-audit')
150
151
 
151
152
  const logs = await getDb()
152
- .selectFrom('secrets_audit')
153
+ .selectFrom('secretsAudit')
153
154
  .select(['action'])
154
- .where('secret_key', '=', 'no-read-audit')
155
+ .where('secretKey', '=', 'no-read-audit')
155
156
  .execute()
156
157
 
157
158
  assert.equal(logs.length, 1)
@@ -174,15 +175,15 @@ function registerTests(
174
175
  await service.delete('audit-cred', 'user-1')
175
176
 
176
177
  const logs = await getDb()
177
- .selectFrom('credentials_audit')
178
- .select(['credential_name', 'user_id', 'action'])
179
- .where('credential_name', '=', 'audit-cred')
180
- .orderBy('performed_at', 'asc')
178
+ .selectFrom('credentialsAudit')
179
+ .select(['credentialName', 'userId', 'action'])
180
+ .where('credentialName', '=', 'audit-cred')
181
+ .orderBy('performedAt', 'asc')
181
182
  .execute()
182
183
 
183
184
  assert.equal(logs.length, 3)
184
185
  assert.equal(logs[0]!.action, 'write')
185
- assert.equal(logs[0]!.user_id, 'user-1')
186
+ assert.equal(logs[0]!.userId, 'user-1')
186
187
  assert.equal(logs[1]!.action, 'read')
187
188
  assert.equal(logs[2]!.action, 'delete')
188
189
  })
@@ -198,14 +199,14 @@ function registerTests(
198
199
  await service.get('global-cred')
199
200
 
200
201
  const logs = await getDb()
201
- .selectFrom('credentials_audit')
202
- .select(['credential_name', 'user_id', 'action'])
203
- .where('credential_name', '=', 'global-cred')
202
+ .selectFrom('credentialsAudit')
203
+ .select(['credentialName', 'userId', 'action'])
204
+ .where('credentialName', '=', 'global-cred')
204
205
  .execute()
205
206
 
206
207
  assert.equal(logs.length, 2)
207
- assert.equal(logs[0]!.user_id, null)
208
- assert.equal(logs[1]!.user_id, null)
208
+ assert.equal(logs[0]!.userId, null)
209
+ assert.equal(logs[1]!.userId, null)
209
210
  })
210
211
  })
211
212
  }