@pikku/kysely 0.13.18 → 0.13.20

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 (66) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/dist/src/index.d.ts +1 -1
  3. package/dist/src/index.js +1 -1
  4. package/dist/src/kysely-agent-run-state-service.js +2 -2
  5. package/dist/src/kysely-agent-storage-service.js +2 -2
  6. package/dist/src/kysely-audit-service.js +2 -2
  7. package/dist/src/kysely-channel-store.js +2 -2
  8. package/dist/src/kysely-credential-service.js +2 -2
  9. package/dist/src/kysely-deployment-service.js +2 -2
  10. package/dist/src/kysely-scope-service.js +2 -2
  11. package/dist/src/kysely-secret-service.js +2 -2
  12. package/dist/src/kysely-session-store.js +2 -2
  13. package/dist/src/kysely-tables.d.ts +28 -0
  14. package/dist/src/kysely-virtual-user-run-store.js +2 -2
  15. package/dist/src/kysely-webhook-service.js +2 -2
  16. package/dist/src/kysely-workflow-mirror.d.ts +1 -1
  17. package/dist/src/kysely-workflow-mirror.js +3 -3
  18. package/dist/src/kysely-workflow-service.d.ts +39 -8
  19. package/dist/src/kysely-workflow-service.js +77 -11
  20. package/dist/src/schema/agent.schema.js +1 -0
  21. package/dist/src/schema/audit.schema.d.ts +5 -4
  22. package/dist/src/schema/audit.schema.js +6 -4
  23. package/dist/src/schema/channel.schema.js +1 -0
  24. package/dist/src/schema/credential.schema.js +1 -0
  25. package/dist/src/schema/index.d.ts +47 -26
  26. package/dist/src/schema/index.js +56 -46
  27. package/dist/src/schema/pikku-schema.types.d.ts +20 -0
  28. package/dist/src/schema/scope.schema.js +1 -0
  29. package/dist/src/schema/virtual-user.schema.js +2 -1
  30. package/dist/src/schema/webhook.schema.js +1 -0
  31. package/dist/src/schema/workflow.schema.js +1 -0
  32. package/package.json +2 -2
  33. package/src/index.ts +12 -2
  34. package/src/kysely-agent-run-state-service.ts +2 -2
  35. package/src/kysely-agent-storage-service.ts +2 -2
  36. package/src/kysely-audit-service.test.ts +2 -10
  37. package/src/kysely-audit-service.ts +2 -2
  38. package/src/kysely-channel-store.ts +2 -2
  39. package/src/kysely-credential-service.ts +2 -2
  40. package/src/kysely-deployment-service.ts +2 -2
  41. package/src/kysely-scope-service.test.ts +2 -0
  42. package/src/kysely-scope-service.ts +2 -2
  43. package/src/kysely-secret-service.ts +2 -2
  44. package/src/kysely-services.test.ts +26 -0
  45. package/src/kysely-session-store.ts +2 -2
  46. package/src/kysely-tables.test.ts +1 -12
  47. package/src/kysely-tables.ts +29 -0
  48. package/src/kysely-virtual-user-run-store.test.ts +22 -6
  49. package/src/kysely-virtual-user-run-store.ts +2 -2
  50. package/src/kysely-webhook-service.test.ts +4 -0
  51. package/src/kysely-webhook-service.ts +2 -2
  52. package/src/kysely-workflow-mirror.test.ts +2 -0
  53. package/src/kysely-workflow-mirror.ts +3 -3
  54. package/src/kysely-workflow-service.test.ts +114 -0
  55. package/src/kysely-workflow-service.ts +99 -11
  56. package/src/schema/agent.schema.ts +1 -0
  57. package/src/schema/audit.schema.ts +6 -4
  58. package/src/schema/channel.schema.ts +1 -0
  59. package/src/schema/credential.schema.ts +1 -0
  60. package/src/schema/index.ts +67 -52
  61. package/src/schema/pikku-schema.types.ts +20 -0
  62. package/src/schema/schema.test.ts +86 -29
  63. package/src/schema/scope.schema.ts +1 -0
  64. package/src/schema/virtual-user.schema.ts +2 -1
  65. package/src/schema/webhook.schema.ts +1 -0
  66. package/src/schema/workflow.schema.ts +1 -0
@@ -7,6 +7,7 @@ import {
7
7
  type SchemaRequirement,
8
8
  } from './pikku-schema.types.js'
9
9
  import { agentSchema } from './agent.schema.js'
10
+ import { auditSchema } from './audit.schema.js'
10
11
  import { channelSchema } from './channel.schema.js'
11
12
  import { credentialSchema } from './credential.schema.js'
12
13
  import { deploymentSchema } from './deployment.schema.js'
@@ -15,6 +16,7 @@ import { secretSchema } from './secret.schema.js'
15
16
  import { sessionSchema } from './session.schema.js'
16
17
  import { webhookSchema } from './webhook.schema.js'
17
18
  import { workflowSchema } from './workflow.schema.js'
19
+ import { virtualUserSchema } from './virtual-user.schema.js'
18
20
 
19
21
  export type {
20
22
  PikkuSchema,
@@ -32,18 +34,18 @@ export {
32
34
  } from './pikku-schema.types.js'
33
35
 
34
36
  /**
35
- * Opt-in, so it is named here rather than folded into `pikkuSchemas` — the
36
- * runtime works without an audit table, and `KyselyAuditService.init()` applies
37
- * this for the projects that wire the sink.
38
- */
39
- export { auditSchema } from './audit.schema.js'
40
-
41
- /**
42
- * Every table the pikku runtime needs, in dependency order.
37
+ * Every table any part of the pikku runtime can need, in dependency order.
43
38
  *
44
39
  * Order is load-bearing, not cosmetic: `scope` has foreign keys onto Better
45
40
  * Auth's `user`, and the tables within each schema reference each other. Sort
46
41
  * this list alphabetically and it stops applying.
42
+ *
43
+ * `audit` and `virtualUser` are in the list now. They used to be left out and
44
+ * created by their own services at boot, which was the only way they ever came
45
+ * into existence — so with the runtime no longer creating anything, leaving
46
+ * them out would mean a wired audit sink starting up against a table that
47
+ * nothing writes a migration for. Both are gated by `ownedBy`, so a project
48
+ * that wires neither still carries neither.
47
49
  */
48
50
  export const pikkuSchemas: PikkuSchema[] = [
49
51
  channelSchema,
@@ -55,8 +57,40 @@ export const pikkuSchemas: PikkuSchema[] = [
55
57
  workflowSchema,
56
58
  agentSchema,
57
59
  scopeSchema,
60
+ auditSchema,
61
+ virtualUserSchema,
58
62
  ]
59
63
 
64
+ export { agentSchema } from './agent.schema.js'
65
+ export { auditSchema } from './audit.schema.js'
66
+ export { channelSchema } from './channel.schema.js'
67
+ export { credentialSchema } from './credential.schema.js'
68
+ export { deploymentSchema } from './deployment.schema.js'
69
+ export { scopeSchema } from './scope.schema.js'
70
+ export { secretSchema } from './secret.schema.js'
71
+ export { sessionSchema } from './session.schema.js'
72
+ export { virtualUserSchema } from './virtual-user.schema.js'
73
+ export { webhookSchema } from './webhook.schema.js'
74
+ export { workflowSchema } from './workflow.schema.js'
75
+
76
+ /**
77
+ * The schemas a project needs, given the services its code actually reaches.
78
+ *
79
+ * `services` is the inspector's `requiredServices` — the same set that drives
80
+ * service tree-shaking, so a schema is written for exactly the projects whose
81
+ * functions, middleware or wirings reach the service that owns its tables.
82
+ *
83
+ * The gate is one-sided: a schema declaring no `ownedBy` is always in, because
84
+ * nothing a project writes implies it and leaving it out would be a guess.
85
+ */
86
+ export const requiredPikkuSchemas = (
87
+ services: ReadonlySet<string>
88
+ ): PikkuSchema[] =>
89
+ pikkuSchemas.filter(
90
+ (schema) =>
91
+ !schema.ownedBy || schema.ownedBy.some((name) => services.has(name))
92
+ )
93
+
60
94
  /**
61
95
  * Bind a declaration to a database.
62
96
  *
@@ -287,9 +321,6 @@ export const declaredTables = (
287
321
  return tables
288
322
  }
289
323
 
290
- /** What `ensurePikkuSchema` found when it looked. */
291
- export type EnsureOutcome = 'present' | 'created'
292
-
293
324
  /**
294
325
  * What `db` already has, indexed both ways.
295
326
  *
@@ -337,58 +368,42 @@ const isPresent = (existing: ExistingTables, table: DeclaredTable): boolean =>
337
368
  : existing.bare.has(table.name)
338
369
 
339
370
  /**
340
- * Make sure one schema's tables are there, creating them only if none are.
371
+ * Refuse to start unless one schema's tables are already there.
341
372
  *
342
- * What a service calls at boot. It looks before it creates, which is the whole
343
- * difference from the `.ifNotExists()` DDL this replaces: that spelling turned
344
- * every failure into a silent no-op, and hid a foreign key onto `user.id` that
345
- * postgres had been rejecting since the day it was written.
373
+ * What a service calls at boot, and it never issues DDL. The runtime is not an
374
+ * author of the schema: `pikku db generate` writes the declaration down as a
375
+ * migration and `pikku db migrate` applies it, and those two are the only way
376
+ * these tables come into existence. A service that finds them missing says so
377
+ * and stops.
346
378
  *
347
- * A partially-present schema is refused rather than completed. Half a schema
348
- * means something else already applied part of it — a migration, an older
349
- * version, a hand-run script — and creating the remainder at boot would leave
350
- * two authorities over one set of tables, which is the condition all of this
351
- * exists to end.
379
+ * This is the whole point of gating what `db generate` writes. While boot could
380
+ * still create, a schema that generation left out was created silently at
381
+ * startup instead — two authorities over one set of tables, which is the
382
+ * condition all of this exists to end. With creation gone, a schema generation
383
+ * missed is a sentence at startup naming the command that fixes it.
352
384
  *
353
- * Creating at boot is the fallback, not the intent. `pikku db generate` writes
354
- * the declaration down as a migration; a project that has done so takes the
355
- * `present` path and this never issues DDL at all.
385
+ * Half-present is not a distinct case any more. Every missing table is reported
386
+ * the same way, because the remedy is the same one either way.
356
387
  */
357
- export const ensurePikkuSchema = async (
388
+ export const requirePikkuSchema = async (
358
389
  db: Kysely<any>,
359
390
  schema: PikkuSchema
360
- ): Promise<EnsureOutcome> => {
391
+ ): Promise<void> => {
361
392
  const existing = await tablesOf(db)
362
393
  const declared = declaredTables(schema, db)
363
394
  const missing = declared.filter((table) => !isPresent(existing, table))
364
395
 
365
- if (missing.length === 0) return 'present'
366
-
367
- if (missing.length < declared.length) {
368
- throw new Error(
369
- `The '${schema.name}' schema is half applied: ${missing.map(displayName).join(', ')} missing, ` +
370
- `${declared
371
- .filter((table) => isPresent(existing, table))
372
- .map(displayName)
373
- .join(', ')} already there. ` +
374
- 'Something else owns part of it. Reconcile it with a migration — ' +
375
- '`pikku db generate` writes the declaration down — rather than creating the rest at boot.'
376
- )
377
- }
396
+ if (missing.length === 0) return
378
397
 
379
- try {
380
- await applyPikkuSchemas(db, [schema])
381
- return 'created'
382
- } catch (error) {
383
- // Two instances booting cold against one database can both see it empty
384
- // before either has finished creating. If the other one got there, say so;
385
- // anything else is a real failure and still throws. This is a courtesy, not
386
- // a guarantee — a boot race is only fully answered by having run the
387
- // migration, which is what `pikku db generate` is for.
388
- const after = await tablesOf(db)
389
- if (declared.every((table) => isPresent(after, table))) return 'present'
390
- throw error
391
- }
398
+ const present = declared.filter((table) => isPresent(existing, table))
399
+ throw new Error(
400
+ `The '${schema.name}' schema is not in this database: ${missing.map(displayName).join(', ')} missing` +
401
+ (present.length > 0
402
+ ? `, ${present.map(displayName).join(', ')} already there`
403
+ : '') +
404
+ '. The runtime does not create these — run `pikku db generate` to write the ' +
405
+ 'migration, then `pikku db migrate` to apply it.'
406
+ )
392
407
  }
393
408
 
394
409
  /**
@@ -101,6 +101,26 @@ export interface PikkuSchema {
101
101
  * database — or, worse, a stub table conjured up to make the error go away.
102
102
  */
103
103
  requires?: SchemaRequirement[]
104
+ /**
105
+ * The singleton services whose `init()` requires these tables, or absent for
106
+ * a schema every project needs.
107
+ *
108
+ * Named as services rather than as wirings because a service is what the
109
+ * tables actually belong to: one `requirePikkuSchema` call site per service,
110
+ * and `pikku db generate` writes the schema for a project whose functions
111
+ * reach any of them. A wiring is a poor proxy — a project can construct
112
+ * `KyselyWorkflowService` and wire no workflows at all.
113
+ *
114
+ * Absent means unconditional. `secrets` is on every project and the session
115
+ * and deployment stores are reached by generated code rather than by
116
+ * functions, so nothing a project writes implies them and their absence
117
+ * cannot be inferred.
118
+ *
119
+ * Under-naming is safe now that the runtime creates nothing: a schema left
120
+ * out of the migration is a sentence at startup, not a table conjured behind
121
+ * your back. Over-naming costs an unused table. Prefer over-naming.
122
+ */
123
+ ownedBy?: string[]
104
124
  }
105
125
 
106
126
  /**
@@ -14,9 +14,10 @@ import {
14
14
  import Database from 'better-sqlite3'
15
15
  import {
16
16
  pikkuSchemas,
17
+ requiredPikkuSchemas,
17
18
  compilePikkuSchemas,
18
19
  applyPikkuSchemas,
19
- ensurePikkuSchema,
20
+ requirePikkuSchema,
20
21
  declaredTables,
21
22
  unsatisfiedRequirements,
22
23
  type PikkuSchema,
@@ -238,17 +239,32 @@ describe('pikku runtime schema — prerequisites', () => {
238
239
  })
239
240
  })
240
241
 
241
- describe('ensurePikkuSchema — what a service does at boot', () => {
242
+ describe('requirePikkuSchema — what a service does at boot', () => {
242
243
  const memoryDb = () =>
243
244
  new Kysely<any>({
244
245
  dialect: new SqliteDialect({ database: new Database(':memory:') }),
245
246
  })
246
247
 
247
- test('creates the tables the first time and finds them the second', async () => {
248
+ test('refuses a database the migration never reached, naming the command', async () => {
248
249
  const db = memoryDb()
249
250
  try {
250
- assert.equal(await ensurePikkuSchema(db, workflowSchema), 'created')
251
- assert.equal(await ensurePikkuSchema(db, workflowSchema), 'present')
251
+ await assert.rejects(
252
+ requirePikkuSchema(db, workflowSchema),
253
+ /The 'workflow' schema is not in this database.*pikku db generate/s
254
+ )
255
+ } finally {
256
+ await db.destroy()
257
+ }
258
+ })
259
+
260
+ test('creates nothing on the way to that refusal', async () => {
261
+ // The whole point: a service that finds its tables missing stops, and the
262
+ // database it stopped against is exactly as it was.
263
+ const db = memoryDb()
264
+ try {
265
+ await assert.rejects(requirePikkuSchema(db, workflowSchema))
266
+ const tables = await db.introspection.getTables()
267
+ assert.deepEqual(tables, [])
252
268
  } finally {
253
269
  await db.destroy()
254
270
  }
@@ -256,21 +272,22 @@ describe('ensurePikkuSchema — what a service does at boot', () => {
256
272
 
257
273
  test('two services declaring the same schema do not fight over it', async () => {
258
274
  // The workflow service and the workflow mirror both own these tables, and
259
- // both call init(). Whoever runs second must find them and stop, rather
260
- // than issuing DDL the database will reject.
275
+ // both call init(). Neither creates anything, so both find the migrated
276
+ // tables and return.
261
277
  const db = memoryDb()
262
278
  try {
263
- assert.equal(await ensurePikkuSchema(db, workflowSchema), 'created')
264
- assert.equal(await ensurePikkuSchema(db, workflowSchema), 'present')
279
+ await applyPikkuSchemas(db, [workflowSchema])
280
+ await assert.doesNotReject(requirePikkuSchema(db, workflowSchema))
281
+ await assert.doesNotReject(requirePikkuSchema(db, workflowSchema))
265
282
  } finally {
266
283
  await db.destroy()
267
284
  }
268
285
  })
269
286
 
270
- test('refuses a half-applied schema rather than filling in the rest', async () => {
287
+ test('a half-applied schema is refused the same way a missing one is', async () => {
271
288
  // Something else already owns part of it — a migration, an older release,
272
- // a hand-run script. Creating the remainder at boot leaves two authorities
273
- // over one set of tables, which is the condition all of this exists to end.
289
+ // a hand-run script. Half-present is not a distinct case: the remedy is the
290
+ // same migration either way, so the sentence names both halves and stops.
274
291
  const db = memoryDb()
275
292
  try {
276
293
  await db.schema
@@ -279,8 +296,8 @@ describe('ensurePikkuSchema — what a service does at boot', () => {
279
296
  .execute()
280
297
 
281
298
  await assert.rejects(
282
- ensurePikkuSchema(db, workflowSchema),
283
- /The 'workflow' schema is half applied/
299
+ requirePikkuSchema(db, workflowSchema),
300
+ /workflow_step.*missing.*workflow_runs already there/s
284
301
  )
285
302
  } finally {
286
303
  await db.destroy()
@@ -288,7 +305,7 @@ describe('ensurePikkuSchema — what a service does at boot', () => {
288
305
  })
289
306
  })
290
307
 
291
- describe('ensurePikkuSchema — a connection bound to a schema', () => {
308
+ describe('requirePikkuSchema — a connection bound to a schema', () => {
292
309
  const WORKFLOW_TABLES = [
293
310
  'workflow_runs',
294
311
  'workflow_step',
@@ -301,7 +318,7 @@ describe('ensurePikkuSchema — a connection bound to a schema', () => {
301
318
  *
302
319
  * Stubbed rather than run against a real database because sqlite has one
303
320
  * schema and nothing else here speaks postgres — and introspection is the
304
- * only thing `ensurePikkuSchema` reads before it decides. DummyDriver takes
321
+ * only thing `requirePikkuSchema` reads before it decides. DummyDriver takes
305
322
  * whatever DDL follows.
306
323
  */
307
324
  const introspecting = (
@@ -352,9 +369,8 @@ describe('ensurePikkuSchema — a connection bound to a schema', () => {
352
369
  WORKFLOW_TABLES.map((name) => ({ schema: 'app', name }))
353
370
  )
354
371
 
355
- assert.equal(
356
- await ensurePikkuSchema(db.withSchema('app'), workflowSchema),
357
- 'present'
372
+ await assert.doesNotReject(
373
+ requirePikkuSchema(db.withSchema('app'), workflowSchema)
358
374
  )
359
375
  })
360
376
 
@@ -365,9 +381,9 @@ describe('ensurePikkuSchema — a connection bound to a schema', () => {
365
381
  WORKFLOW_TABLES.map((name) => ({ schema: 'public', name }))
366
382
  )
367
383
 
368
- assert.equal(
369
- await ensurePikkuSchema(db.withSchema('app'), workflowSchema),
370
- 'created'
384
+ await assert.rejects(
385
+ requirePikkuSchema(db.withSchema('app'), workflowSchema),
386
+ /is not in this database/
371
387
  )
372
388
  })
373
389
 
@@ -376,7 +392,7 @@ describe('ensurePikkuSchema — a connection bound to a schema', () => {
376
392
  WORKFLOW_TABLES.map((name) => ({ schema: 'public', name }))
377
393
  )
378
394
 
379
- assert.equal(await ensurePikkuSchema(db, workflowSchema), 'present')
395
+ await assert.doesNotReject(requirePikkuSchema(db, workflowSchema))
380
396
  })
381
397
 
382
398
  test('matches on the bare name when the engine reports no schemas', async () => {
@@ -387,9 +403,8 @@ describe('ensurePikkuSchema — a connection bound to a schema', () => {
387
403
  WORKFLOW_TABLES.map((name) => ({ schema: undefined, name }))
388
404
  )
389
405
 
390
- assert.equal(
391
- await ensurePikkuSchema(db.withSchema('main'), workflowSchema),
392
- 'present'
406
+ await assert.doesNotReject(
407
+ requirePikkuSchema(db.withSchema('main'), workflowSchema)
393
408
  )
394
409
  })
395
410
 
@@ -414,7 +429,7 @@ describe('ensurePikkuSchema — a connection bound to a schema', () => {
414
429
  })
415
430
 
416
431
  await assert.rejects(
417
- ensurePikkuSchema(db.withSchema('app'), workflowSchema),
432
+ applyPikkuSchemas(db.withSchema('app'), [workflowSchema]),
418
433
  (error: Error) => {
419
434
  assert.equal(error, pgError, 'the engine error must pass through as-is')
420
435
  return true
@@ -431,7 +446,7 @@ describe('ensurePikkuSchema — a connection bound to a schema', () => {
431
446
  })
432
447
  try {
433
448
  await assert.rejects(
434
- ensurePikkuSchema(db.withSchema('main'), workflowSchema),
449
+ applyPikkuSchemas(db.withSchema('main'), [workflowSchema]),
435
450
  (error: Error) => {
436
451
  assert.match(error.message, /bound to a schema/)
437
452
  assert.match(error.message, /takes a bare table name/i)
@@ -448,7 +463,7 @@ describe('ensurePikkuSchema — a connection bound to a schema', () => {
448
463
  const db = introspecting([{ schema: 'app', name: 'workflow_runs' }])
449
464
 
450
465
  await assert.rejects(
451
- ensurePikkuSchema(db.withSchema('app'), workflowSchema),
466
+ requirePikkuSchema(db.withSchema('app'), workflowSchema),
452
467
  /app\.workflow_step.*missing.*app\.workflow_runs already there/s
453
468
  )
454
469
  })
@@ -532,3 +547,45 @@ describe('compiling into a schema', () => {
532
547
  )
533
548
  })
534
549
  })
550
+
551
+ describe('requiredPikkuSchemas', () => {
552
+ test('a project that reaches no service still gets the ungated schemas', () => {
553
+ const names = requiredPikkuSchemas(new Set()).map((s) => s.name)
554
+ assert.deepEqual(names, ['session', 'secret', 'deployment'])
555
+ })
556
+
557
+ test('a service brings in its own schema, in the declared order', () => {
558
+ const names = requiredPikkuSchemas(
559
+ new Set(['channelStore', 'workflowService'])
560
+ ).map((s) => s.name)
561
+ assert.deepEqual(names, [
562
+ 'channel',
563
+ 'session',
564
+ 'secret',
565
+ 'deployment',
566
+ 'workflow',
567
+ ])
568
+ })
569
+
570
+ test('any one of a schema s owners is enough to ask for it', () => {
571
+ // A schema several services share is needed as soon as one of them is.
572
+ assert.ok(
573
+ requiredPikkuSchemas(new Set(['workflowRunService'])).some(
574
+ (s) => s.name === 'workflow'
575
+ )
576
+ )
577
+ assert.ok(
578
+ requiredPikkuSchemas(new Set(['eventHub'])).some(
579
+ (s) => s.name === 'channel'
580
+ )
581
+ )
582
+ })
583
+
584
+ test('naming every owner asks for everything', () => {
585
+ const owners = pikkuSchemas.flatMap((s) => s.ownedBy ?? [])
586
+ assert.equal(
587
+ requiredPikkuSchemas(new Set(owners)).length,
588
+ pikkuSchemas.length
589
+ )
590
+ })
591
+ })
@@ -19,6 +19,7 @@ import { requiredType, type PikkuSchema } from './pikku-schema.types.js'
19
19
  */
20
20
  export const scopeSchema: PikkuSchema = {
21
21
  name: 'scope',
22
+ ownedBy: ['scopeService'],
22
23
  requires: [{ table: 'user', column: 'id', owner: 'Better Auth' }],
23
24
  statements: [
24
25
  (db) =>
@@ -19,7 +19,8 @@ import type { PikkuSchema } from './pikku-schema.types.js'
19
19
  * week's does not depend on which database they were found in.
20
20
  */
21
21
  export const virtualUserSchema: PikkuSchema = {
22
- name: 'virtualUser',
22
+ name: 'virtual-user',
23
+ ownedBy: ['virtualUserRunStore'],
23
24
  statements: [
24
25
  (db) =>
25
26
  db.schema
@@ -4,6 +4,7 @@ import type { PikkuSchema } from './pikku-schema.types.js'
4
4
  /** Webhook deliveries and one row per delivery attempt. */
5
5
  export const webhookSchema: PikkuSchema = {
6
6
  name: 'webhook',
7
+ ownedBy: ['webhookService'],
7
8
  statements: [
8
9
  (db) =>
9
10
  db.schema
@@ -18,6 +18,7 @@ import type { PikkuSchema } from './pikku-schema.types.js'
18
18
  */
19
19
  export const workflowSchema: PikkuSchema = {
20
20
  name: 'workflow',
21
+ ownedBy: ['workflowService', 'workflowRunService'],
21
22
  statements: [
22
23
  (db) =>
23
24
  db.schema