@bluefields/db 0.2.0

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 (76) hide show
  1. package/LICENSE +202 -0
  2. package/dist/client.d.ts +6 -0
  3. package/dist/client.js +13 -0
  4. package/dist/client.js.map +1 -0
  5. package/dist/index.d.ts +19 -0
  6. package/dist/index.js +20 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/migrate.d.ts +11 -0
  9. package/dist/migrate.js +48 -0
  10. package/dist/migrate.js.map +1 -0
  11. package/dist/schema/api-keys.d.ts +145 -0
  12. package/dist/schema/api-keys.js +18 -0
  13. package/dist/schema/api-keys.js.map +1 -0
  14. package/dist/schema/audit-log.d.ts +228 -0
  15. package/dist/schema/audit-log.js +49 -0
  16. package/dist/schema/audit-log.js.map +1 -0
  17. package/dist/schema/cost-records.d.ts +190 -0
  18. package/dist/schema/cost-records.js +55 -0
  19. package/dist/schema/cost-records.js.map +1 -0
  20. package/dist/schema/crawl-job-pages.d.ts +186 -0
  21. package/dist/schema/crawl-job-pages.js +31 -0
  22. package/dist/schema/crawl-job-pages.js.map +1 -0
  23. package/dist/schema/crawl-jobs.d.ts +271 -0
  24. package/dist/schema/crawl-jobs.js +40 -0
  25. package/dist/schema/crawl-jobs.js.map +1 -0
  26. package/dist/schema/customers.d.ts +145 -0
  27. package/dist/schema/customers.js +19 -0
  28. package/dist/schema/customers.js.map +1 -0
  29. package/dist/schema/event-feedback.d.ts +170 -0
  30. package/dist/schema/event-feedback.js +35 -0
  31. package/dist/schema/event-feedback.js.map +1 -0
  32. package/dist/schema/events.d.ts +291 -0
  33. package/dist/schema/events.js +58 -0
  34. package/dist/schema/events.js.map +1 -0
  35. package/dist/schema/extractions.d.ts +327 -0
  36. package/dist/schema/extractions.js +56 -0
  37. package/dist/schema/extractions.js.map +1 -0
  38. package/dist/schema/host-extractors.d.ts +131 -0
  39. package/dist/schema/host-extractors.js +18 -0
  40. package/dist/schema/host-extractors.js.map +1 -0
  41. package/dist/schema/host-records.d.ts +237 -0
  42. package/dist/schema/host-records.js +42 -0
  43. package/dist/schema/host-records.js.map +1 -0
  44. package/dist/schema/notification-preferences.d.ts +195 -0
  45. package/dist/schema/notification-preferences.js +49 -0
  46. package/dist/schema/notification-preferences.js.map +1 -0
  47. package/dist/schema/scrape-cache.d.ts +136 -0
  48. package/dist/schema/scrape-cache.js +23 -0
  49. package/dist/schema/scrape-cache.js.map +1 -0
  50. package/dist/schema/sessions.d.ts +153 -0
  51. package/dist/schema/sessions.js +27 -0
  52. package/dist/schema/sessions.js.map +1 -0
  53. package/dist/schema/snapshots.d.ts +382 -0
  54. package/dist/schema/snapshots.js +60 -0
  55. package/dist/schema/snapshots.js.map +1 -0
  56. package/dist/schema/subscriptions.d.ts +604 -0
  57. package/dist/schema/subscriptions.js +79 -0
  58. package/dist/schema/subscriptions.js.map +1 -0
  59. package/dist/schema/usage-periods.d.ts +446 -0
  60. package/dist/schema/usage-periods.js +71 -0
  61. package/dist/schema/usage-periods.js.map +1 -0
  62. package/dist/tsconfig.tsbuildinfo +1 -0
  63. package/package.json +54 -0
  64. package/src/migrations/0001_initial.sql +398 -0
  65. package/src/migrations/0002_pg_cron.sql +109 -0
  66. package/src/migrations/0003_webhook_secret_cleanup.sql +55 -0
  67. package/src/migrations/0004_events_pg_notify.sql +39 -0
  68. package/src/migrations/0005_subscription_consecutive_failures.sql +19 -0
  69. package/src/migrations/0006_notification_preferences.sql +27 -0
  70. package/src/migrations/0007_crawl_jobs.sql +65 -0
  71. package/src/migrations/0008_sessions.sql +38 -0
  72. package/src/migrations/0009_scrape_cache.sql +34 -0
  73. package/src/migrations/0010_neon_defaults.sql +99 -0
  74. package/src/migrations/0011_credits_consumed_numeric.sql +12 -0
  75. package/src/migrations/0012_crawl_page_attestation.sql +10 -0
  76. package/src/migrations/0013_customer_deleted_at.sql +5 -0
@@ -0,0 +1,19 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
3
+ export const customers = pgTable('customers', {
4
+ id: uuid('id').primaryKey().defaultRandom(),
5
+ email: text('email').notNull().unique(),
6
+ orgName: text('org_name'),
7
+ // plan values: free, starter, growth, scale, enterprise (Review #8)
8
+ plan: text('plan').notNull().default('free'),
9
+ stripeCustomerId: text('stripe_customer_id'),
10
+ createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
11
+ updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
12
+ /**
13
+ * Set when the customer requested account/data deletion (GDPR erasure). The
14
+ * row is KEPT (anonymized in place) for billing/audit integrity; `null` =
15
+ * active. See `deleteAccount` in @bluefields/control.
16
+ */
17
+ deletedAt: timestamp('deleted_at', { withTimezone: true }),
18
+ });
19
+ //# sourceMappingURL=customers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"customers.js","sourceRoot":"","sources":["../../src/schema/customers.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAErE,MAAM,CAAC,MAAM,SAAS,GAAG,OAAO,CAAC,WAAW,EAAE;IAC5C,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC,aAAa,EAAE;IAC3C,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE;IACvC,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC;IACzB,oEAAoE;IACpE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;IAC5C,gBAAgB,EAAE,IAAI,CAAC,oBAAoB,CAAC;IAC5C,SAAS,EAAE,SAAS,CAAC,YAAY,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,UAAU,EAAE;IACjF,SAAS,EAAE,SAAS,CAAC,YAAY,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,UAAU,EAAE;IACjF;;;;OAIG;IACH,SAAS,EAAE,SAAS,CAAC,YAAY,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;CAC3D,CAAC,CAAC"}
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Customer feedback on event classification (Review #5).
3
+ * Drives the regression test set and informs prompt/threshold tuning.
4
+ *
5
+ * SPEC DEVIATION: `event_id` is NOT a foreign key. `events` is partitioned with
6
+ * a composite primary key, so a DB-level FK to `events(id)` alone is not
7
+ * expressible. Referential integrity is enforced by application code.
8
+ */
9
+ export declare const eventFeedback: import("drizzle-orm/pg-core").PgTableWithColumns<{
10
+ name: "event_feedback";
11
+ schema: undefined;
12
+ columns: {
13
+ id: import("drizzle-orm/pg-core").PgColumn<{
14
+ name: "id";
15
+ tableName: "event_feedback";
16
+ dataType: "string";
17
+ columnType: "PgUUID";
18
+ data: string;
19
+ driverParam: string;
20
+ notNull: true;
21
+ hasDefault: true;
22
+ isPrimaryKey: true;
23
+ isAutoincrement: false;
24
+ hasRuntimeDefault: false;
25
+ enumValues: undefined;
26
+ baseColumn: never;
27
+ identity: undefined;
28
+ generated: undefined;
29
+ }, {}, {}>;
30
+ eventId: import("drizzle-orm/pg-core").PgColumn<{
31
+ name: "event_id";
32
+ tableName: "event_feedback";
33
+ dataType: "string";
34
+ columnType: "PgUUID";
35
+ data: string;
36
+ driverParam: string;
37
+ notNull: false;
38
+ hasDefault: false;
39
+ isPrimaryKey: false;
40
+ isAutoincrement: false;
41
+ hasRuntimeDefault: false;
42
+ enumValues: undefined;
43
+ baseColumn: never;
44
+ identity: undefined;
45
+ generated: undefined;
46
+ }, {}, {}>;
47
+ subscriptionId: import("drizzle-orm/pg-core").PgColumn<{
48
+ name: "subscription_id";
49
+ tableName: "event_feedback";
50
+ dataType: "string";
51
+ columnType: "PgUUID";
52
+ data: string;
53
+ driverParam: string;
54
+ notNull: true;
55
+ hasDefault: false;
56
+ isPrimaryKey: false;
57
+ isAutoincrement: false;
58
+ hasRuntimeDefault: false;
59
+ enumValues: undefined;
60
+ baseColumn: never;
61
+ identity: undefined;
62
+ generated: undefined;
63
+ }, {}, {}>;
64
+ customerId: import("drizzle-orm/pg-core").PgColumn<{
65
+ name: "customer_id";
66
+ tableName: "event_feedback";
67
+ dataType: "string";
68
+ columnType: "PgUUID";
69
+ data: string;
70
+ driverParam: string;
71
+ notNull: true;
72
+ hasDefault: false;
73
+ isPrimaryKey: false;
74
+ isAutoincrement: false;
75
+ hasRuntimeDefault: false;
76
+ enumValues: undefined;
77
+ baseColumn: never;
78
+ identity: undefined;
79
+ generated: undefined;
80
+ }, {}, {}>;
81
+ verdict: import("drizzle-orm/pg-core").PgColumn<{
82
+ name: "verdict";
83
+ tableName: "event_feedback";
84
+ dataType: "string";
85
+ columnType: "PgText";
86
+ data: string;
87
+ driverParam: string;
88
+ notNull: true;
89
+ hasDefault: false;
90
+ isPrimaryKey: false;
91
+ isAutoincrement: false;
92
+ hasRuntimeDefault: false;
93
+ enumValues: [string, ...string[]];
94
+ baseColumn: never;
95
+ identity: undefined;
96
+ generated: undefined;
97
+ }, {}, {}>;
98
+ notes: import("drizzle-orm/pg-core").PgColumn<{
99
+ name: "notes";
100
+ tableName: "event_feedback";
101
+ dataType: "string";
102
+ columnType: "PgText";
103
+ data: string;
104
+ driverParam: string;
105
+ notNull: false;
106
+ hasDefault: false;
107
+ isPrimaryKey: false;
108
+ isAutoincrement: false;
109
+ hasRuntimeDefault: false;
110
+ enumValues: [string, ...string[]];
111
+ baseColumn: never;
112
+ identity: undefined;
113
+ generated: undefined;
114
+ }, {}, {}>;
115
+ reviewedByUs: import("drizzle-orm/pg-core").PgColumn<{
116
+ name: "reviewed_by_us";
117
+ tableName: "event_feedback";
118
+ dataType: "boolean";
119
+ columnType: "PgBoolean";
120
+ data: boolean;
121
+ driverParam: boolean;
122
+ notNull: true;
123
+ hasDefault: true;
124
+ isPrimaryKey: false;
125
+ isAutoincrement: false;
126
+ hasRuntimeDefault: false;
127
+ enumValues: undefined;
128
+ baseColumn: never;
129
+ identity: undefined;
130
+ generated: undefined;
131
+ }, {}, {}>;
132
+ followUpAction: import("drizzle-orm/pg-core").PgColumn<{
133
+ name: "follow_up_action";
134
+ tableName: "event_feedback";
135
+ dataType: "string";
136
+ columnType: "PgText";
137
+ data: string;
138
+ driverParam: string;
139
+ notNull: false;
140
+ hasDefault: false;
141
+ isPrimaryKey: false;
142
+ isAutoincrement: false;
143
+ hasRuntimeDefault: false;
144
+ enumValues: [string, ...string[]];
145
+ baseColumn: never;
146
+ identity: undefined;
147
+ generated: undefined;
148
+ }, {}, {}>;
149
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
150
+ name: "created_at";
151
+ tableName: "event_feedback";
152
+ dataType: "date";
153
+ columnType: "PgTimestamp";
154
+ data: Date;
155
+ driverParam: string;
156
+ notNull: true;
157
+ hasDefault: true;
158
+ isPrimaryKey: false;
159
+ isAutoincrement: false;
160
+ hasRuntimeDefault: false;
161
+ enumValues: undefined;
162
+ baseColumn: never;
163
+ identity: undefined;
164
+ generated: undefined;
165
+ }, {}, {}>;
166
+ };
167
+ dialect: "pg";
168
+ }>;
169
+ export type EventFeedback = typeof eventFeedback.$inferSelect;
170
+ export type NewEventFeedback = typeof eventFeedback.$inferInsert;
@@ -0,0 +1,35 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { sql } from 'drizzle-orm';
3
+ import { boolean, index, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
4
+ import { customers } from './customers.js';
5
+ import { subscriptions } from './subscriptions.js';
6
+ /**
7
+ * Customer feedback on event classification (Review #5).
8
+ * Drives the regression test set and informs prompt/threshold tuning.
9
+ *
10
+ * SPEC DEVIATION: `event_id` is NOT a foreign key. `events` is partitioned with
11
+ * a composite primary key, so a DB-level FK to `events(id)` alone is not
12
+ * expressible. Referential integrity is enforced by application code.
13
+ */
14
+ export const eventFeedback = pgTable('event_feedback', {
15
+ id: uuid('id').primaryKey().defaultRandom(),
16
+ // No FK — see spec-deviation note above. Nullable: false_negatives have no event.
17
+ eventId: uuid('event_id'),
18
+ subscriptionId: uuid('subscription_id')
19
+ .notNull()
20
+ .references(() => subscriptions.id),
21
+ customerId: uuid('customer_id')
22
+ .notNull()
23
+ .references(() => customers.id),
24
+ // 'correctly_meaningful' | 'false_positive' | 'false_negative'
25
+ verdict: text('verdict').notNull(),
26
+ notes: text('notes'),
27
+ reviewedByUs: boolean('reviewed_by_us').notNull().default(false),
28
+ // 'adjusted_threshold' | 'added_ignore_rule' | 'updated_prompt' | ...
29
+ followUpAction: text('follow_up_action'),
30
+ createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
31
+ }, (table) => [
32
+ index('idx_feedback_customer').on(table.customerId),
33
+ index('idx_feedback_unreviewed').on(table.createdAt).where(sql `${table.reviewedByUs} = false`),
34
+ ]);
35
+ //# sourceMappingURL=event-feedback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-feedback.js","sourceRoot":"","sources":["../../src/schema/event-feedback.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AACrF,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAClC,gBAAgB,EAChB;IACE,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC,aAAa,EAAE;IAC3C,kFAAkF;IAClF,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC;IACzB,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC;SACpC,OAAO,EAAE;SACT,UAAU,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC;IACrC,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC;SAC5B,OAAO,EAAE;SACT,UAAU,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC;IACjC,+DAA+D;IAC/D,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE;IAClC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAChE,sEAAsE;IACtE,cAAc,EAAE,IAAI,CAAC,kBAAkB,CAAC;IACxC,SAAS,EAAE,SAAS,CAAC,YAAY,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,UAAU,EAAE;CAClF,EACD,CAAC,KAAK,EAAE,EAAE,CAAC;IACT,KAAK,CAAC,uBAAuB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC;IACnD,KAAK,CAAC,yBAAyB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,CAAA,GAAG,KAAK,CAAC,YAAY,UAAU,CAAC;CAC/F,CACF,CAAC"}
@@ -0,0 +1,291 @@
1
+ /**
2
+ * Events — what we deliver to customers.
3
+ *
4
+ * SPEC DEVIATION: The spec writes `id UUID PRIMARY KEY` with `PARTITION BY
5
+ * RANGE (created_at)`. Same fix as `snapshots`: composite primary key
6
+ * `(id, created_at)`. Referential integrity for FKs referencing
7
+ * `events(id)` from non-partitioned tables (`event_feedback.event_id`)
8
+ * is enforced by application code, not the DB.
9
+ */
10
+ export declare const events: import("drizzle-orm/pg-core").PgTableWithColumns<{
11
+ name: "events";
12
+ schema: undefined;
13
+ columns: {
14
+ id: import("drizzle-orm/pg-core").PgColumn<{
15
+ name: "id";
16
+ tableName: "events";
17
+ dataType: "string";
18
+ columnType: "PgUUID";
19
+ data: string;
20
+ driverParam: string;
21
+ notNull: true;
22
+ hasDefault: true;
23
+ isPrimaryKey: false;
24
+ isAutoincrement: false;
25
+ hasRuntimeDefault: false;
26
+ enumValues: undefined;
27
+ baseColumn: never;
28
+ identity: undefined;
29
+ generated: undefined;
30
+ }, {}, {}>;
31
+ subscriptionId: import("drizzle-orm/pg-core").PgColumn<{
32
+ name: "subscription_id";
33
+ tableName: "events";
34
+ dataType: "string";
35
+ columnType: "PgUUID";
36
+ data: string;
37
+ driverParam: string;
38
+ notNull: true;
39
+ hasDefault: false;
40
+ isPrimaryKey: false;
41
+ isAutoincrement: false;
42
+ hasRuntimeDefault: false;
43
+ enumValues: undefined;
44
+ baseColumn: never;
45
+ identity: undefined;
46
+ generated: undefined;
47
+ }, {}, {}>;
48
+ customerId: import("drizzle-orm/pg-core").PgColumn<{
49
+ name: "customer_id";
50
+ tableName: "events";
51
+ dataType: "string";
52
+ columnType: "PgUUID";
53
+ data: string;
54
+ driverParam: string;
55
+ notNull: true;
56
+ hasDefault: false;
57
+ isPrimaryKey: false;
58
+ isAutoincrement: false;
59
+ hasRuntimeDefault: false;
60
+ enumValues: undefined;
61
+ baseColumn: never;
62
+ identity: undefined;
63
+ generated: undefined;
64
+ }, {}, {}>;
65
+ eventType: import("drizzle-orm/pg-core").PgColumn<{
66
+ name: "event_type";
67
+ tableName: "events";
68
+ dataType: "string";
69
+ columnType: "PgText";
70
+ data: string;
71
+ driverParam: string;
72
+ notNull: true;
73
+ hasDefault: false;
74
+ isPrimaryKey: false;
75
+ isAutoincrement: false;
76
+ hasRuntimeDefault: false;
77
+ enumValues: [string, ...string[]];
78
+ baseColumn: never;
79
+ identity: undefined;
80
+ generated: undefined;
81
+ }, {}, {}>;
82
+ previousExtractionId: import("drizzle-orm/pg-core").PgColumn<{
83
+ name: "previous_extraction_id";
84
+ tableName: "events";
85
+ dataType: "string";
86
+ columnType: "PgUUID";
87
+ data: string;
88
+ driverParam: string;
89
+ notNull: false;
90
+ hasDefault: false;
91
+ isPrimaryKey: false;
92
+ isAutoincrement: false;
93
+ hasRuntimeDefault: false;
94
+ enumValues: undefined;
95
+ baseColumn: never;
96
+ identity: undefined;
97
+ generated: undefined;
98
+ }, {}, {}>;
99
+ currentExtractionId: import("drizzle-orm/pg-core").PgColumn<{
100
+ name: "current_extraction_id";
101
+ tableName: "events";
102
+ dataType: "string";
103
+ columnType: "PgUUID";
104
+ data: string;
105
+ driverParam: string;
106
+ notNull: false;
107
+ hasDefault: false;
108
+ isPrimaryKey: false;
109
+ isAutoincrement: false;
110
+ hasRuntimeDefault: false;
111
+ enumValues: undefined;
112
+ baseColumn: never;
113
+ identity: undefined;
114
+ generated: undefined;
115
+ }, {}, {}>;
116
+ diff: import("drizzle-orm/pg-core").PgColumn<{
117
+ name: "diff";
118
+ tableName: "events";
119
+ dataType: "json";
120
+ columnType: "PgJsonb";
121
+ data: unknown;
122
+ driverParam: unknown;
123
+ notNull: false;
124
+ hasDefault: false;
125
+ isPrimaryKey: false;
126
+ isAutoincrement: false;
127
+ hasRuntimeDefault: false;
128
+ enumValues: undefined;
129
+ baseColumn: never;
130
+ identity: undefined;
131
+ generated: undefined;
132
+ }, {}, {}>;
133
+ semanticClassification: import("drizzle-orm/pg-core").PgColumn<{
134
+ name: "semantic_classification";
135
+ tableName: "events";
136
+ dataType: "string";
137
+ columnType: "PgText";
138
+ data: string;
139
+ driverParam: string;
140
+ notNull: false;
141
+ hasDefault: false;
142
+ isPrimaryKey: false;
143
+ isAutoincrement: false;
144
+ hasRuntimeDefault: false;
145
+ enumValues: [string, ...string[]];
146
+ baseColumn: never;
147
+ identity: undefined;
148
+ generated: undefined;
149
+ }, {}, {}>;
150
+ deliveryState: import("drizzle-orm/pg-core").PgColumn<{
151
+ name: "delivery_state";
152
+ tableName: "events";
153
+ dataType: "string";
154
+ columnType: "PgText";
155
+ data: string;
156
+ driverParam: string;
157
+ notNull: true;
158
+ hasDefault: true;
159
+ isPrimaryKey: false;
160
+ isAutoincrement: false;
161
+ hasRuntimeDefault: false;
162
+ enumValues: [string, ...string[]];
163
+ baseColumn: never;
164
+ identity: undefined;
165
+ generated: undefined;
166
+ }, {}, {}>;
167
+ deliveryAttempts: import("drizzle-orm/pg-core").PgColumn<{
168
+ name: "delivery_attempts";
169
+ tableName: "events";
170
+ dataType: "number";
171
+ columnType: "PgInteger";
172
+ data: number;
173
+ driverParam: string | number;
174
+ notNull: true;
175
+ hasDefault: true;
176
+ isPrimaryKey: false;
177
+ isAutoincrement: false;
178
+ hasRuntimeDefault: false;
179
+ enumValues: undefined;
180
+ baseColumn: never;
181
+ identity: undefined;
182
+ generated: undefined;
183
+ }, {}, {}>;
184
+ nextRetryAt: import("drizzle-orm/pg-core").PgColumn<{
185
+ name: "next_retry_at";
186
+ tableName: "events";
187
+ dataType: "date";
188
+ columnType: "PgTimestamp";
189
+ data: Date;
190
+ driverParam: string;
191
+ notNull: false;
192
+ hasDefault: false;
193
+ isPrimaryKey: false;
194
+ isAutoincrement: false;
195
+ hasRuntimeDefault: false;
196
+ enumValues: undefined;
197
+ baseColumn: never;
198
+ identity: undefined;
199
+ generated: undefined;
200
+ }, {}, {}>;
201
+ lastAttemptAt: import("drizzle-orm/pg-core").PgColumn<{
202
+ name: "last_attempt_at";
203
+ tableName: "events";
204
+ dataType: "date";
205
+ columnType: "PgTimestamp";
206
+ data: Date;
207
+ driverParam: string;
208
+ notNull: false;
209
+ hasDefault: false;
210
+ isPrimaryKey: false;
211
+ isAutoincrement: false;
212
+ hasRuntimeDefault: false;
213
+ enumValues: undefined;
214
+ baseColumn: never;
215
+ identity: undefined;
216
+ generated: undefined;
217
+ }, {}, {}>;
218
+ lastResponseStatus: import("drizzle-orm/pg-core").PgColumn<{
219
+ name: "last_response_status";
220
+ tableName: "events";
221
+ dataType: "number";
222
+ columnType: "PgInteger";
223
+ data: number;
224
+ driverParam: string | number;
225
+ notNull: false;
226
+ hasDefault: false;
227
+ isPrimaryKey: false;
228
+ isAutoincrement: false;
229
+ hasRuntimeDefault: false;
230
+ enumValues: undefined;
231
+ baseColumn: never;
232
+ identity: undefined;
233
+ generated: undefined;
234
+ }, {}, {}>;
235
+ lastResponseBodyExcerpt: import("drizzle-orm/pg-core").PgColumn<{
236
+ name: "last_response_body_excerpt";
237
+ tableName: "events";
238
+ dataType: "string";
239
+ columnType: "PgText";
240
+ data: string;
241
+ driverParam: string;
242
+ notNull: false;
243
+ hasDefault: false;
244
+ isPrimaryKey: false;
245
+ isAutoincrement: false;
246
+ hasRuntimeDefault: false;
247
+ enumValues: [string, ...string[]];
248
+ baseColumn: never;
249
+ identity: undefined;
250
+ generated: undefined;
251
+ }, {}, {}>;
252
+ deliveredAt: import("drizzle-orm/pg-core").PgColumn<{
253
+ name: "delivered_at";
254
+ tableName: "events";
255
+ dataType: "date";
256
+ columnType: "PgTimestamp";
257
+ data: Date;
258
+ driverParam: string;
259
+ notNull: false;
260
+ hasDefault: false;
261
+ isPrimaryKey: false;
262
+ isAutoincrement: false;
263
+ hasRuntimeDefault: false;
264
+ enumValues: undefined;
265
+ baseColumn: never;
266
+ identity: undefined;
267
+ generated: undefined;
268
+ }, {}, {}>;
269
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
270
+ name: "created_at";
271
+ tableName: "events";
272
+ dataType: "date";
273
+ columnType: "PgTimestamp";
274
+ data: Date;
275
+ driverParam: string;
276
+ notNull: true;
277
+ hasDefault: true;
278
+ isPrimaryKey: false;
279
+ isAutoincrement: false;
280
+ hasRuntimeDefault: false;
281
+ enumValues: undefined;
282
+ baseColumn: never;
283
+ identity: undefined;
284
+ generated: undefined;
285
+ }, {}, {}>;
286
+ };
287
+ dialect: "pg";
288
+ }>;
289
+ export type Event = typeof events.$inferSelect;
290
+ export type NewEvent = typeof events.$inferInsert;
291
+ export declare const EVENTS_PARTITION_COLUMN = "created_at";
@@ -0,0 +1,58 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { sql } from 'drizzle-orm';
3
+ import { index, integer, jsonb, pgTable, primaryKey, text, timestamp, uuid, } from 'drizzle-orm/pg-core';
4
+ import { customers } from './customers.js';
5
+ import { extractions } from './extractions.js';
6
+ import { subscriptions } from './subscriptions.js';
7
+ /**
8
+ * Events — what we deliver to customers.
9
+ *
10
+ * SPEC DEVIATION: The spec writes `id UUID PRIMARY KEY` with `PARTITION BY
11
+ * RANGE (created_at)`. Same fix as `snapshots`: composite primary key
12
+ * `(id, created_at)`. Referential integrity for FKs referencing
13
+ * `events(id)` from non-partitioned tables (`event_feedback.event_id`)
14
+ * is enforced by application code, not the DB.
15
+ */
16
+ export const events = pgTable('events', {
17
+ id: uuid('id').notNull().defaultRandom(),
18
+ subscriptionId: uuid('subscription_id')
19
+ .notNull()
20
+ .references(() => subscriptions.id),
21
+ // denormalized for per-customer queries (Review #2 isolation invariants)
22
+ customerId: uuid('customer_id')
23
+ .notNull()
24
+ .references(() => customers.id),
25
+ // 'subscription.change', 'subscription.first_observation', 'subscription.error', 'bluefields.webhook.ping' (Review #7)
26
+ eventType: text('event_type').notNull(),
27
+ previousExtractionId: uuid('previous_extraction_id').references(() => extractions.id),
28
+ currentExtractionId: uuid('current_extraction_id').references(() => extractions.id),
29
+ diff: jsonb('diff'),
30
+ // Review #5 taxonomy: 'meaningful', 'meaningful_llm', 'meaningful_baseline_drift', etc.
31
+ semanticClassification: text('semantic_classification'),
32
+ // Delivery state machine (Review #7)
33
+ // 'pending' | 'delivered' | 'dead_lettered' | 'unsubscribed' | 'skipped_no_webhook'
34
+ deliveryState: text('delivery_state').notNull().default('pending'),
35
+ deliveryAttempts: integer('delivery_attempts').notNull().default(0),
36
+ nextRetryAt: timestamp('next_retry_at', { withTimezone: true }),
37
+ lastAttemptAt: timestamp('last_attempt_at', { withTimezone: true }),
38
+ lastResponseStatus: integer('last_response_status'),
39
+ // first 1KB of customer's response; debug aid (Review #7)
40
+ lastResponseBodyExcerpt: text('last_response_body_excerpt'),
41
+ deliveredAt: timestamp('delivered_at', { withTimezone: true }),
42
+ createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
43
+ }, (table) => [
44
+ // Composite PK required for partitioned tables (see spec-deviation note above).
45
+ primaryKey({ columns: [table.id, table.createdAt] }),
46
+ index('idx_events_sub_time').on(table.subscriptionId, table.createdAt.desc()),
47
+ index('idx_events_customer_time').on(table.customerId, table.createdAt.desc()),
48
+ index('idx_events_undelivered').on(table.createdAt).where(sql `${table.deliveredAt} IS NULL`),
49
+ // Retry-due index — hot path for the delivery loop (Review #7)
50
+ index('idx_events_due_for_retry')
51
+ .on(table.nextRetryAt)
52
+ .where(sql `${table.deliveryState} = 'pending' AND ${table.nextRetryAt} IS NOT NULL`),
53
+ index('idx_events_dead_lettered')
54
+ .on(table.customerId, table.createdAt.desc())
55
+ .where(sql `${table.deliveryState} = 'dead_lettered'`),
56
+ ]);
57
+ export const EVENTS_PARTITION_COLUMN = 'created_at';
58
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/schema/events.ts"],"names":[],"mappings":"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"}