@pikku/kysely 0.13.24 → 0.13.28

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 (80) hide show
  1. package/CHANGELOG.md +271 -0
  2. package/dist/src/index.d.ts +4 -1
  3. package/dist/src/index.js +3 -1
  4. package/dist/src/kysely-analytics-service.d.ts +30 -0
  5. package/dist/src/kysely-analytics-service.js +80 -0
  6. package/dist/src/kysely-feature-flag-store.d.ts +37 -0
  7. package/dist/src/kysely-feature-flag-store.js +239 -0
  8. package/dist/src/kysely-scope-service.d.ts +1 -0
  9. package/dist/src/kysely-scope-service.js +29 -0
  10. package/dist/src/kysely-tables.d.ts +45 -0
  11. package/dist/src/schema/analytics.schema.d.ts +17 -0
  12. package/dist/src/schema/analytics.schema.js +48 -0
  13. package/dist/src/schema/flag.schema.d.ts +16 -0
  14. package/dist/src/schema/flag.schema.js +62 -0
  15. package/dist/src/schema/index.d.ts +2 -0
  16. package/dist/src/schema/index.js +6 -0
  17. package/package.json +11 -6
  18. package/run-tests.sh +9 -1
  19. package/bin/pikku-kysely-pure.ts +0 -59
  20. package/dist/src/kysely-lock-vault.d.ts +0 -34
  21. package/dist/src/kysely-lock-vault.js +0 -77
  22. package/dist/src/schema/data-lock.schema.d.ts +0 -15
  23. package/dist/src/schema/data-lock.schema.js +0 -27
  24. package/dist/tsconfig.tsbuildinfo +0 -1
  25. package/src/classification-crypto.test.ts +0 -143
  26. package/src/classification-crypto.ts +0 -163
  27. package/src/classification-plugin.pglite.test.ts +0 -226
  28. package/src/classification-plugin.test.ts +0 -265
  29. package/src/classification-plugin.ts +0 -249
  30. package/src/coercion-plugin.test.ts +0 -160
  31. package/src/coercion-plugin.ts +0 -187
  32. package/src/create-audited-kysely.test.ts +0 -560
  33. package/src/create-audited-kysely.ts +0 -254
  34. package/src/index.ts +0 -81
  35. package/src/kysely-agent-run-scores.ts +0 -47
  36. package/src/kysely-agent-run-service.ts +0 -244
  37. package/src/kysely-agent-run-state-service.ts +0 -200
  38. package/src/kysely-agent-storage-service.ts +0 -632
  39. package/src/kysely-audit-service.test.ts +0 -352
  40. package/src/kysely-audit-service.ts +0 -237
  41. package/src/kysely-channel-store.ts +0 -107
  42. package/src/kysely-credential-service.ts +0 -286
  43. package/src/kysely-deployment-service.ts +0 -163
  44. package/src/kysely-eventhub-store.ts +0 -53
  45. package/src/kysely-json.ts +0 -5
  46. package/src/kysely-scope-service.test.ts +0 -418
  47. package/src/kysely-scope-service.ts +0 -425
  48. package/src/kysely-secret-service.ts +0 -242
  49. package/src/kysely-services.test.ts +0 -405
  50. package/src/kysely-session-store.ts +0 -57
  51. package/src/kysely-tables.test.ts +0 -110
  52. package/src/kysely-tables.ts +0 -434
  53. package/src/kysely-virtual-user-run-store.test.ts +0 -369
  54. package/src/kysely-virtual-user-run-store.ts +0 -235
  55. package/src/kysely-virtual-user-schedule-store.test.ts +0 -187
  56. package/src/kysely-virtual-user-schedule-store.ts +0 -187
  57. package/src/kysely-webhook-service.test.ts +0 -153
  58. package/src/kysely-webhook-service.ts +0 -139
  59. package/src/kysely-workflow-mirror.test.ts +0 -259
  60. package/src/kysely-workflow-mirror.ts +0 -374
  61. package/src/kysely-workflow-run-service.ts +0 -305
  62. package/src/kysely-workflow-service.test.ts +0 -544
  63. package/src/kysely-workflow-service.ts +0 -820
  64. package/src/schema/agent.schema.ts +0 -162
  65. package/src/schema/audit.schema.ts +0 -57
  66. package/src/schema/channel.schema.ts +0 -38
  67. package/src/schema/credential.schema.ts +0 -58
  68. package/src/schema/deployment.schema.ts +0 -47
  69. package/src/schema/index.ts +0 -441
  70. package/src/schema/pikku-schema.types.ts +0 -169
  71. package/src/schema/schema.test.ts +0 -591
  72. package/src/schema/scope.schema.ts +0 -110
  73. package/src/schema/secret.schema.ts +0 -49
  74. package/src/schema/session.schema.ts +0 -20
  75. package/src/schema/virtual-user-schedule.schema.ts +0 -48
  76. package/src/schema/virtual-user.schema.ts +0 -106
  77. package/src/schema/webhook.schema.ts +0 -58
  78. package/src/schema/workflow.schema.ts +0 -150
  79. package/src/serialize-plugin.ts +0 -233
  80. package/tsconfig.json +0 -17
@@ -1,49 +0,0 @@
1
- import { sql } from 'kysely'
2
- import type { PikkuSchema } from './pikku-schema.types.js'
3
-
4
- /**
5
- * Encrypted secrets and their audit trail.
6
- *
7
- * The audit table is declared unconditionally, where `init()` created it only
8
- * when the service was constructed with auditing on. A migration cannot depend
9
- * on runtime configuration, and the asymmetry favours declaring it: an unused
10
- * table costs nothing, while turning auditing on against a database that never
11
- * got the table fails on the first write.
12
- */
13
- export const secretSchema: PikkuSchema = {
14
- name: 'secret',
15
- statements: [
16
- (db) =>
17
- db.schema
18
- .createTable('secrets')
19
- .addColumn('key', 'varchar(255)', (col) => col.primaryKey())
20
- .addColumn('ciphertext', 'text', (col) => col.notNull())
21
- .addColumn('wrappedDek', 'text', (col) => col.notNull())
22
- .addColumn('keyVersion', 'integer', (col) => col.notNull())
23
- .addColumn('createdAt', 'timestamp', (col) =>
24
- col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
25
- )
26
- .addColumn('updatedAt', 'timestamp', (col) =>
27
- col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
28
- ),
29
-
30
- (db) =>
31
- db.schema
32
- .createTable('secretKekSalts')
33
- .addColumn('keyVersion', 'integer', (col) => col.primaryKey())
34
- .addColumn('salt', 'varchar(64)', (col) => col.notNull())
35
- .addColumn('createdAt', 'timestamp', (col) =>
36
- col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
37
- ),
38
-
39
- (db) =>
40
- db.schema
41
- .createTable('secretsAudit')
42
- .addColumn('id', 'varchar(36)', (col) => col.primaryKey())
43
- .addColumn('secretKey', 'varchar(255)', (col) => col.notNull())
44
- .addColumn('action', 'varchar(20)', (col) => col.notNull())
45
- .addColumn('performedAt', 'timestamp', (col) =>
46
- col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
47
- ),
48
- ],
49
- }
@@ -1,20 +0,0 @@
1
- import { sql } from 'kysely'
2
- import type { PikkuSchema } from './pikku-schema.types.js'
3
-
4
- /** Server-side session storage. */
5
- export const sessionSchema: PikkuSchema = {
6
- name: 'session',
7
- statements: [
8
- (db) =>
9
- db.schema
10
- .createTable('pikkuUserSessions')
11
- .addColumn('pikkuUserId', 'text', (col) => col.primaryKey())
12
- .addColumn('session', 'text', (col) => col.notNull())
13
- .addColumn('createdAt', 'timestamp', (col) =>
14
- col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
15
- )
16
- .addColumn('updatedAt', 'timestamp', (col) =>
17
- col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
18
- ),
19
- ],
20
- }
@@ -1,48 +0,0 @@
1
- import type { PikkuSchema } from './pikku-schema.types.js'
2
-
3
- /**
4
- * The `virtualUserSchedule` table {@link KyselyVirtualUserScheduleStore} writes
5
- * to: one row per persona that keeps using the app on its own.
6
- *
7
- * Its own schema rather than a third table in {@link virtualUserSchema}, and
8
- * owned by its own store: recording what a persona did and deciding that it
9
- * keeps going on its own are separate opt-ins, and a project that only wants
10
- * the first should not be handed a cadence table it never writes to.
11
- *
12
- * `persona` is the primary key: a persona has one cadence, and a second row for
13
- * the same one would mean two copies of the same user acting at once — not a
14
- * heavier test, a different and unreproducible one.
15
- */
16
- export const virtualUserScheduleSchema: PikkuSchema = {
17
- name: 'virtual-user-schedule',
18
- ownedBy: ['virtualUserScheduleStore'],
19
- statements: [
20
- (db) =>
21
- db.schema
22
- .createTable('virtualUserSchedule')
23
- .addColumn('persona', 'varchar(255)', (col) => col.primaryKey())
24
- // 0 or 1 rather than a boolean, matching the run tables: a bare sqlite
25
- // driver cannot bind one, and `SerializePlugin` is not installed
26
- // everywhere. Off by default, so creating the table spends nothing.
27
- .addColumn('enabled', 'integer', (col) => col.defaultTo(0).notNull())
28
- .addColumn('disposition', 'varchar(50)', (col) => col.notNull())
29
- .addColumn('goals', 'text', (col) => col.defaultTo('[]').notNull())
30
- .addColumn('budget', 'text')
31
- // The gap to the next run is drawn between these rather than fixed, so
32
- // the persona does not keep an appointment.
33
- .addColumn('minIntervalMs', 'bigint', (col) => col.notNull())
34
- .addColumn('maxIntervalMs', 'bigint', (col) => col.notNull())
35
- // The entire schedule. Written before a run is dispatched, so a tick
36
- // that dies halfway cannot leave the row due for the next one.
37
- .addColumn('nextRunAt', 'timestamp', (col) => col.notNull())
38
- .addColumn('lastRunId', 'varchar(36)')
39
- .addColumn('lastRunAt', 'timestamp'),
40
-
41
- // The only read that matters at speed: which rows are due, on every tick.
42
- (db) =>
43
- db.schema
44
- .createIndex('idx_virtual_user_schedule_due')
45
- .on('virtualUserSchedule')
46
- .columns(['enabled', 'nextRunAt']),
47
- ],
48
- }
@@ -1,106 +0,0 @@
1
- import { sql } from 'kysely'
2
- import type { PikkuSchema } from './pikku-schema.types.js'
3
-
4
- /**
5
- * The `virtualUserRun` and `virtualUserRunStep` tables
6
- * {@link KyselyVirtualUserRunStore} writes to.
7
- *
8
- * Deliberately not in `pikkuSchemas`: the runtime never needs this table, only
9
- * a project that runs virtual users does, and creating it for everyone would
10
- * put an empty table in every database. `KyselyVirtualUserRunStore.init()`
11
- * applies it, so it arrives exactly when the thing that fills it does.
12
- *
13
- * `persona` is text rather than a foreign key: personas are declared in code
14
- * with `definePersonas()`, not rows, and a run has to outlive the deletion of
15
- * the persona it ran as.
16
- *
17
- * The JSON columns are text on every engine, like the audit table's, because
18
- * the store serialises them itself — a project on SQLite and one on Postgres
19
- * then hold the same bytes, and comparing this week's findings against last
20
- * week's does not depend on which database they were found in.
21
- */
22
- export const virtualUserSchema: PikkuSchema = {
23
- name: 'virtual-user',
24
- ownedBy: ['virtualUserRunStore'],
25
- statements: [
26
- (db) =>
27
- db.schema
28
- .createTable('virtualUserRun')
29
- .addColumn('runId', 'varchar(36)', (col) => col.primaryKey())
30
- .addColumn('persona', 'varchar(255)', (col) => col.notNull())
31
- .addColumn('disposition', 'varchar(50)', (col) => col.notNull())
32
- .addColumn('seed', 'bigint', (col) => col.notNull())
33
- // running | completed | failed. Not derived from `finishedAt` being
34
- // null: a crashed run has no finish time either, and "crashed" and
35
- // "still going" are not the same answer.
36
- .addColumn('status', 'varchar(50)', (col) =>
37
- col.defaultTo('running').notNull()
38
- )
39
- .addColumn('goals', 'text', (col) => col.defaultTo('[]').notNull())
40
- .addColumn('memory', 'text', (col) => col.defaultTo('{}').notNull())
41
- .addColumn('findings', 'text', (col) => col.defaultTo('[]').notNull())
42
- // What the user set out to do and how far each one got. On the run row
43
- // rather than beside the steps: there are a handful of them, and every
44
- // read of the run wants them.
45
- .addColumn('intents', 'text', (col) => col.defaultTo('[]').notNull())
46
- // Counts the engine kept: steps, calls, mutations, tokens, elapsed.
47
- .addColumn('tally', 'text')
48
- .addColumn('stoppedBy', 'varchar(255)')
49
- // Why the run itself failed, as opposed to what it found.
50
- .addColumn('error', 'text')
51
- .addColumn('startedBy', 'varchar(255)')
52
- .addColumn('createdAt', 'timestamp', (col) =>
53
- col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
54
- )
55
- .addColumn('finishedAt', 'timestamp'),
56
-
57
- // The two reads: the run list newest-first, and one persona's history when
58
- // a finding needs comparing against what that persona found before.
59
- (db) =>
60
- db.schema
61
- .createIndex('idx_virtual_user_run_created')
62
- .on('virtualUserRun')
63
- .column('createdAt'),
64
-
65
- (db) =>
66
- db.schema
67
- .createIndex('idx_virtual_user_run_persona')
68
- .on('virtualUserRun')
69
- .columns(['persona', 'createdAt']),
70
-
71
- // One row per turn, kept out of `virtualUserRun` so that listing runs does
72
- // not drag a budget's worth of transcript along with it. No foreign key
73
- // onto the run for the same reason `persona` is not one: this table is
74
- // created by the store at boot on whatever engine the project uses, and a
75
- // cascade that half the engines enforce differently is worse than the read
76
- // rule that a step with no run is orphaned.
77
- (db) =>
78
- db.schema
79
- .createTable('virtualUserRunStep')
80
- .addColumn('runId', 'varchar(36)', (col) => col.notNull())
81
- // The engine's own step number, which is what the intent records point
82
- // at — not a surrogate key, so a transcript reads the same after paging.
83
- // Spelled `stepIndex` because `index` is a reserved word in mysql.
84
- .addColumn('stepIndex', 'integer', (col) => col.notNull())
85
- .addColumn('intentId', 'varchar(255)')
86
- // The action as the engine scheduled it, including the `invalid` shape
87
- // for a turn the model got wrong — that turn is the interesting one.
88
- .addColumn('action', 'text', (col) => col.notNull())
89
- .addColumn('status', 'integer')
90
- // 0 or 1 rather than a boolean, because a bare sqlite driver cannot
91
- // bind one at all and `SerializePlugin` is not installed everywhere.
92
- .addColumn('ok', 'integer')
93
- // Already truncated by the engine, and stored JSON-encoded rather than
94
- // raw: a truncated API response usually starts with a brace, which
95
- // `SerializePlugin` would read back as an object rather than the string
96
- // the engine actually saw.
97
- .addColumn('response', 'text')
98
- .addColumn('findingKinds', 'text')
99
- .addColumn('tokensIn', 'integer', (col) => col.defaultTo(0).notNull())
100
- .addColumn('tokensOut', 'integer', (col) => col.defaultTo(0).notNull())
101
- .addPrimaryKeyConstraint('pk_virtual_user_run_step', [
102
- 'runId',
103
- 'stepIndex',
104
- ]),
105
- ],
106
- }
@@ -1,58 +0,0 @@
1
- import { sql } from 'kysely'
2
- import type { PikkuSchema } from './pikku-schema.types.js'
3
-
4
- /** Webhook deliveries and one row per delivery attempt. */
5
- export const webhookSchema: PikkuSchema = {
6
- name: 'webhook',
7
- ownedBy: ['webhookService'],
8
- statements: [
9
- (db) =>
10
- db.schema
11
- .createTable('webhookDelivery')
12
- .addColumn('deliveryId', 'text', (col) => col.primaryKey())
13
- .addColumn('organizationId', 'text')
14
- .addColumn('url', 'text', (col) => col.notNull())
15
- .addColumn('event', 'text')
16
- .addColumn('status', 'text', (col) =>
17
- col.defaultTo('pending').notNull()
18
- )
19
- .addColumn('attempts', 'integer', (col) => col.defaultTo(0).notNull())
20
- .addColumn('createdAt', 'timestamp', (col) =>
21
- col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
22
- )
23
- .addColumn('updatedAt', 'timestamp', (col) =>
24
- col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
25
- )
26
- .addColumn('deliveredAt', 'timestamp'),
27
-
28
- (db) =>
29
- db.schema
30
- .createTable('webhookDeliveryAttempt')
31
- .addColumn('attemptId', 'text', (col) => col.primaryKey())
32
- .addColumn('deliveryId', 'text', (col) =>
33
- col
34
- .notNull()
35
- .references('webhookDelivery.deliveryId')
36
- .onDelete('cascade')
37
- )
38
- .addColumn('attemptNumber', 'integer', (col) => col.notNull())
39
- .addColumn('statusCode', 'integer')
40
- .addColumn('responseBody', 'text')
41
- .addColumn('error', 'text')
42
- .addColumn('createdAt', 'timestamp', (col) =>
43
- col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
44
- ),
45
-
46
- (db) =>
47
- db.schema
48
- .createIndex('idx_webhook_delivery_org')
49
- .on('webhookDelivery')
50
- .column('organizationId'),
51
-
52
- (db) =>
53
- db.schema
54
- .createIndex('idx_webhook_delivery_attempt_delivery')
55
- .on('webhookDeliveryAttempt')
56
- .column('deliveryId'),
57
- ],
58
- }
@@ -1,150 +0,0 @@
1
- import { sql } from 'kysely'
2
- import type { PikkuSchema } from './pikku-schema.types.js'
3
-
4
- /**
5
- * Workflow runs, their steps, per-step history and the versioned graphs.
6
- *
7
- * Two things from the boot-time DDL are deliberately not carried over. The
8
- * primary keys had `defaultTo(sql.raw("'" + crypto.randomUUID() + "'"))`, which
9
- * evaluates once while the statement is built — every row would have taken the
10
- * same default, so it was never a generator; the services supply the id. And
11
- * `workflowStep.fromStepName`, `workflowStep.currentAttempt` and
12
- * `workflowStepHistory.attempt` were each backfilled by an
13
- * `alterTable(...).catch(() => {})` bolted on after the fact, which is the job a
14
- * migration does — they are declared here and nowhere else.
15
- *
16
- * `KyselyWorkflowMirror` created these same four tables separately. There is one
17
- * declaration now, and both services read it.
18
- */
19
- export const workflowSchema: PikkuSchema = {
20
- name: 'workflow',
21
- ownedBy: ['workflowService', 'workflowRunService'],
22
- statements: [
23
- (db) =>
24
- db.schema
25
- .createTable('workflowRuns')
26
- .addColumn('workflowRunId', 'text', (col) => col.primaryKey())
27
- .addColumn('workflow', 'text', (col) => col.notNull())
28
- .addColumn('status', 'text', (col) => col.notNull())
29
- .addColumn('input', 'text', (col) => col.notNull())
30
- .addColumn('output', 'text')
31
- .addColumn('error', 'text')
32
- .addColumn('state', 'text', (col) => col.defaultTo('{}'))
33
- .addColumn('inline', 'boolean', (col) => col.defaultTo(false))
34
- .addColumn('graphHash', 'text')
35
- .addColumn('deterministic', 'boolean', (col) => col.defaultTo(false))
36
- .addColumn('plannedSteps', 'text')
37
- .addColumn('wire', 'text')
38
- .addColumn('createdAt', 'timestamp', (col) =>
39
- col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
40
- )
41
- .addColumn('updatedAt', 'timestamp', (col) =>
42
- col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
43
- ),
44
-
45
- (db) =>
46
- db.schema
47
- .createTable('workflowStep')
48
- .addColumn('workflowStepId', 'text', (col) => col.primaryKey())
49
- .addColumn('workflowRunId', 'text', (col) =>
50
- col
51
- .notNull()
52
- .references('workflowRuns.workflowRunId')
53
- .onDelete('cascade')
54
- )
55
- .addColumn('stepName', 'text', (col) => col.notNull())
56
- .addColumn('rpcName', 'text')
57
- .addColumn('data', 'text')
58
- .addColumn('status', 'text', (col) =>
59
- col.notNull().defaultTo('pending')
60
- )
61
- .addColumn('result', 'text')
62
- .addColumn('error', 'text')
63
- .addColumn('childRunId', 'text')
64
- .addColumn('branchTaken', 'text')
65
- .addColumn('retries', 'integer')
66
- .addColumn('retryDelay', 'text')
67
- .addColumn('fromStepName', 'text')
68
- .addColumn('currentAttempt', 'integer')
69
- .addColumn('createdAt', 'timestamp', (col) =>
70
- col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
71
- )
72
- .addColumn('updatedAt', 'timestamp', (col) =>
73
- col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
74
- )
75
- .addUniqueConstraint('workflow_step_run_name_unique', [
76
- 'workflowRunId',
77
- 'stepName',
78
- ]),
79
-
80
- (db) =>
81
- db.schema
82
- .createTable('workflowStepHistory')
83
- .addColumn('historyId', 'text', (col) => col.primaryKey())
84
- .addColumn('workflowStepId', 'text', (col) =>
85
- col
86
- .notNull()
87
- .references('workflowStep.workflowStepId')
88
- .onDelete('cascade')
89
- )
90
- .addColumn('status', 'text', (col) => col.notNull())
91
- .addColumn('result', 'text')
92
- .addColumn('error', 'text')
93
- .addColumn('attempt', 'integer')
94
- .addColumn('createdAt', 'timestamp', (col) =>
95
- col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
96
- )
97
- .addColumn('runningAt', 'timestamp')
98
- .addColumn('scheduledAt', 'timestamp')
99
- .addColumn('succeededAt', 'timestamp')
100
- .addColumn('failedAt', 'timestamp'),
101
-
102
- (db) =>
103
- db.schema
104
- .createTable('workflowVersions')
105
- .addColumn('workflowName', 'text', (col) => col.notNull())
106
- .addColumn('graphHash', 'text', (col) => col.notNull())
107
- .addColumn('graph', 'text', (col) => col.notNull())
108
- .addColumn('source', 'text', (col) => col.notNull())
109
- .addColumn('status', 'text', (col) => col.notNull().defaultTo('active'))
110
- .addColumn('createdAt', 'timestamp', (col) =>
111
- col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
112
- )
113
- .addPrimaryKeyConstraint('workflow_versions_pk', [
114
- 'workflowName',
115
- 'graphHash',
116
- ]),
117
-
118
- // The indexes trail the tables so that a table is never indexed before it
119
- // exists, whatever order the declarations are read in.
120
- (db) =>
121
- db.schema
122
- .createIndex('idx_workflow_runs_status_created')
123
- .on('workflowRuns')
124
- .columns(['status', 'createdAt']),
125
-
126
- (db) =>
127
- db.schema
128
- .createIndex('idx_workflow_runs_workflow_created')
129
- .on('workflowRuns')
130
- .columns(['workflow', 'createdAt']),
131
-
132
- (db) =>
133
- db.schema
134
- .createIndex('idx_workflow_step_run_status')
135
- .on('workflowStep')
136
- .columns(['workflowRunId', 'status']),
137
-
138
- (db) =>
139
- db.schema
140
- .createIndex('idx_workflow_step_history_step')
141
- .on('workflowStepHistory')
142
- .columns(['workflowStepId', 'createdAt']),
143
-
144
- (db) =>
145
- db.schema
146
- .createIndex('idx_workflow_versions_source_status')
147
- .on('workflowVersions')
148
- .columns(['source', 'status']),
149
- ],
150
- }
@@ -1,233 +0,0 @@
1
- /**
2
- * Serialize / deserialize plugin for Kysely.
3
- *
4
- * Adapted from `kysely-plugin-serialize` (MIT, by subframe7536 —
5
- * https://github.com/subframe7536/kysely-plugin-serialize), which is
6
- * unmaintained and ships no `exports` map, so modern Node16/ESM resolution
7
- * falls back to its CommonJS build and cannot import ESM-only kysely (>= 0.29).
8
- * Maintained here directly as part of @pikku/kysely so we publish it ourselves.
9
- *
10
- * Serializes object/array/Date/boolean parameters to strings on the way into
11
- * SQLite-style dialects, and deserializes them back on the way out.
12
- */
13
- import {
14
- OperationNodeTransformer,
15
- type ColumnUpdateNode,
16
- type KyselyPlugin,
17
- type PluginTransformQueryArgs,
18
- type PluginTransformResultArgs,
19
- type PrimitiveValueListNode,
20
- type QueryId,
21
- type QueryResult,
22
- type RootOperationNode,
23
- type UnknownRow,
24
- type ValueNode,
25
- } from 'kysely'
26
-
27
- export type Serializer = (parameter: unknown) => unknown
28
- export type Deserializer = (parameter: unknown) => unknown
29
-
30
- class SerializeParametersTransformer extends OperationNodeTransformer {
31
- private readonly serializer: Serializer
32
-
33
- constructor(serializer: Serializer) {
34
- super()
35
- this.serializer = serializer
36
- }
37
-
38
- protected override transformPrimitiveValueList(
39
- node: PrimitiveValueListNode
40
- ): PrimitiveValueListNode {
41
- return {
42
- ...node,
43
- values: node.values.map(this.serializer),
44
- }
45
- }
46
-
47
- // https://www.npmjs.com/package/zodsql
48
- protected override transformColumnUpdate(
49
- node: ColumnUpdateNode
50
- ): ColumnUpdateNode {
51
- const { value: valueNode } = node
52
- if (valueNode.kind !== 'ValueNode') {
53
- return super.transformColumnUpdate(node)
54
- }
55
- const { value, ...item } = valueNode as ValueNode
56
- const serializedValue = this.serializer(value)
57
- if (value === serializedValue) {
58
- return super.transformColumnUpdate(node)
59
- }
60
- const serializedNode: ValueNode = { ...item, value: serializedValue }
61
- return super.transformColumnUpdate({ ...node, value: serializedNode })
62
- }
63
-
64
- protected override transformValue(node: ValueNode): ValueNode {
65
- return {
66
- ...node,
67
- value: this.serializer(node.value),
68
- }
69
- }
70
- }
71
-
72
- export const defaultSerializer: Serializer = (parameter) => {
73
- if (skipTransform(parameter) || typeof parameter === 'string') {
74
- return parameter
75
- } else if (typeof parameter === 'boolean') {
76
- return '' + parameter
77
- } else if (parameter instanceof Date) {
78
- return parameter.toISOString()
79
- } else {
80
- try {
81
- return JSON.stringify(parameter)
82
- } catch {
83
- return parameter
84
- }
85
- }
86
- }
87
-
88
- export const dateRegex = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?$/
89
-
90
- export const defaultDeserializer: Deserializer = (parameter) => {
91
- if (skipTransform(parameter)) {
92
- return parameter
93
- }
94
- if (typeof parameter === 'string') {
95
- if (parameter === 'true') {
96
- return true
97
- } else if (parameter === 'false') {
98
- return false
99
- } else if (dateRegex.test(parameter)) {
100
- return new Date(parameter)
101
- } else if (maybeJson(parameter)) {
102
- try {
103
- return JSON.parse(parameter)
104
- } catch {
105
- // not valid JSON — fall through and return the raw string
106
- }
107
- }
108
- return parameter
109
- }
110
- return parameter
111
- }
112
-
113
- /**
114
- * Checks if a given string parameter is a JSON-like string.
115
- *
116
- * Determines whether the input starts and ends with curly braces `{}` or
117
- * square brackets `[]`, the typical indicators of JSON objects and arrays.
118
- */
119
- export function maybeJson(parameter: string): boolean {
120
- return (
121
- (parameter.startsWith('{') && parameter.endsWith('}')) ||
122
- (parameter.startsWith('[') && parameter.endsWith(']'))
123
- )
124
- }
125
-
126
- /**
127
- * Determines whether a given parameter should be skipped during transformation.
128
- *
129
- * Skipped when it is `undefined`, `null`, a `bigint`, a `number`, or an object
130
- * that exposes a `buffer` property (e.g. a typed array / Buffer).
131
- */
132
- export function skipTransform(parameter: unknown): boolean {
133
- return (
134
- parameter === undefined ||
135
- parameter === null ||
136
- typeof parameter === 'bigint' ||
137
- typeof parameter === 'number' ||
138
- (typeof parameter === 'object' && 'buffer' in parameter)
139
- )
140
- }
141
-
142
- export class BaseSerializePlugin implements KyselyPlugin {
143
- private readonly transformer: SerializeParametersTransformer
144
- private readonly deserializer: Deserializer
145
- private readonly skipNodeSet?: Set<RootOperationNode['kind']>
146
- private readonly ctx?: WeakSet<QueryId>
147
-
148
- /**
149
- * Base class for {@link SerializePlugin}, without default options.
150
- */
151
- constructor(
152
- serializer: Serializer,
153
- deserializer: Deserializer,
154
- skipNodeKind: Array<RootOperationNode['kind']>
155
- ) {
156
- this.transformer = new SerializeParametersTransformer(serializer)
157
- this.deserializer = deserializer
158
- if (skipNodeKind.length) {
159
- this.skipNodeSet = new Set(skipNodeKind)
160
- this.ctx = new WeakSet()
161
- }
162
- }
163
-
164
- transformQuery({
165
- node,
166
- queryId,
167
- }: PluginTransformQueryArgs): RootOperationNode {
168
- if (this.skipNodeSet?.has(node.kind)) {
169
- this.ctx?.add(queryId)
170
- return node
171
- }
172
- return this.transformer.transformNode(node)
173
- }
174
-
175
- async transformResult({
176
- result,
177
- queryId,
178
- }: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>> {
179
- return this.ctx?.has(queryId)
180
- ? result
181
- : { ...result, rows: this.parseRows(result.rows) }
182
- }
183
-
184
- private parseRows(rows: UnknownRow[]): UnknownRow[] {
185
- const result: UnknownRow[] = []
186
- for (const row of rows) {
187
- if (!row) {
188
- continue
189
- }
190
- const parsedRow: UnknownRow = {}
191
- for (const [key, value] of Object.entries(row)) {
192
- parsedRow[key] = this.deserializer(value)
193
- }
194
- result.push(parsedRow)
195
- }
196
- return result
197
- }
198
- }
199
-
200
- export interface SerializePluginOptions {
201
- /**
202
- * serialize params
203
- */
204
- serializer?: Serializer
205
- /**
206
- * deserialize params
207
- */
208
- deserializer?: Deserializer
209
- /**
210
- * node kind to skip transform
211
- */
212
- skipNodeKind?: Array<RootOperationNode['kind']>
213
- }
214
-
215
- export class SerializePlugin extends BaseSerializePlugin {
216
- /**
217
- * _**THIS PLUGIN SHOULD BE PLACED AT THE END OF PLUGINS ARRAY !!!**_
218
- *
219
- * reference from https://github.com/koskimas/kysely/pull/138
220
- *
221
- * Serializes object/array/Date/boolean column values to strings for
222
- * SQLite-style dialects and deserializes them on the way back out. Pass a
223
- * custom `serializer`/`deserializer` to override the default behaviour.
224
- */
225
- constructor(options: SerializePluginOptions = {}) {
226
- const {
227
- deserializer = defaultDeserializer,
228
- serializer = defaultSerializer,
229
- skipNodeKind = [],
230
- } = options
231
- super(serializer, deserializer, skipNodeKind)
232
- }
233
- }
package/tsconfig.json DELETED
@@ -1,17 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "rootDir": ".",
5
- "module": "Node18",
6
- "outDir": "dist",
7
- "target": "esnext",
8
- "declaration": true
9
- },
10
- "include": ["src/**/*.ts", "bin/**/*.ts", "bin/pikku-kysely-pure.ts"],
11
- "exclude": ["**/*.test.ts", "node_modules", "bin/dist"],
12
- "references": [
13
- {
14
- "path": "../../core/tsconfig.json"
15
- }
16
- ]
17
- }