@hogsend/db 0.0.1
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.
- package/LICENSE +93 -0
- package/README.md +14 -0
- package/drizzle/0000_nifty_songbird.sql +188 -0
- package/drizzle/0001_minor_shockwave.sql +13 -0
- package/drizzle/0002_early_owl.sql +1 -0
- package/drizzle/0003_bizarre_annihilus.sql +9 -0
- package/drizzle/0004_brave_betty_brant.sql +1 -0
- package/drizzle/0005_groovy_princess_powerful.sql +8 -0
- package/drizzle/0006_groovy_charles_xavier.sql +100 -0
- package/drizzle/0007_serious_captain_universe.sql +1 -0
- package/drizzle/0008_demonic_agent_brand.sql +5 -0
- package/drizzle/meta/0000_snapshot.json +1264 -0
- package/drizzle/meta/0001_snapshot.json +1353 -0
- package/drizzle/meta/0002_snapshot.json +1380 -0
- package/drizzle/meta/0003_snapshot.json +1443 -0
- package/drizzle/meta/0004_snapshot.json +1464 -0
- package/drizzle/meta/0005_snapshot.json +1588 -0
- package/drizzle/meta/0006_snapshot.json +2331 -0
- package/drizzle/meta/0007_snapshot.json +2346 -0
- package/drizzle/meta/0008_snapshot.json +2449 -0
- package/drizzle/meta/_journal.json +69 -0
- package/package.json +49 -0
- package/src/index.ts +35 -0
- package/src/migrate-client.ts +56 -0
- package/src/migrate.ts +173 -0
- package/src/schema/_shared.ts +10 -0
- package/src/schema/alert-history.ts +21 -0
- package/src/schema/alert-rules.ts +36 -0
- package/src/schema/api-keys.ts +30 -0
- package/src/schema/audit-logs.ts +22 -0
- package/src/schema/auth.ts +89 -0
- package/src/schema/contacts.ts +31 -0
- package/src/schema/dead-letter-queue.ts +31 -0
- package/src/schema/email-preferences.ts +35 -0
- package/src/schema/email-sends.ts +34 -0
- package/src/schema/enums.ts +47 -0
- package/src/schema/import-jobs.ts +26 -0
- package/src/schema/index.ts +18 -0
- package/src/schema/journey-configs.ts +15 -0
- package/src/schema/journey-logs.ts +21 -0
- package/src/schema/journey-states.ts +54 -0
- package/src/schema/link-clicks.ts +21 -0
- package/src/schema/relations.ts +160 -0
- package/src/schema/tracked-links.ts +17 -0
- package/src/schema/user-events.ts +35 -0
- package/src/seed.ts +91 -0
- package/src/version.ts +162 -0
package/src/version.ts
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { type SQL, sql } from "drizzle-orm";
|
|
2
|
+
// The Drizzle journal lists every migration bundled with this build. Importing
|
|
3
|
+
// it as JSON lets esbuild/tsup inline it into the API bundle (where the
|
|
4
|
+
// `drizzle/` folder isn't shipped) while still working under tsx in dev.
|
|
5
|
+
import journal from "../drizzle/meta/_journal.json" with { type: "json" };
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Minimal shape needed to read the migrations table — satisfied by both the
|
|
9
|
+
* schema-aware container `Database` and the schema-less client the migrator
|
|
10
|
+
* builds, so callers don't have to share an exact drizzle generic.
|
|
11
|
+
*/
|
|
12
|
+
interface ExecutableDb {
|
|
13
|
+
execute: (query: SQL) => Promise<unknown>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface MigrationEntry {
|
|
17
|
+
/** Sequential index assigned by drizzle-kit (0, 1, 2, ...). */
|
|
18
|
+
idx: number;
|
|
19
|
+
/** Migration filename without extension, e.g. `0007_serious_captain_universe`. */
|
|
20
|
+
tag: string;
|
|
21
|
+
/** Creation timestamp (ms). Matches `__drizzle_migrations.created_at`. */
|
|
22
|
+
when: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface SchemaVersion {
|
|
26
|
+
/** Latest migration tag bundled with this build (what the code requires). */
|
|
27
|
+
required: string | null;
|
|
28
|
+
/** Latest migration tag actually applied to the database. */
|
|
29
|
+
applied: string | null;
|
|
30
|
+
/** Bundled migrations not yet applied to the database, in order. */
|
|
31
|
+
pending: string[];
|
|
32
|
+
/** True when every bundled migration has been applied. */
|
|
33
|
+
inSync: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The relevant slice of a Drizzle `meta/_journal.json` — the entries a track's
|
|
38
|
+
* code requires. Both the engine (bundled journal) and the client (its own repo
|
|
39
|
+
* journal, supplied by the caller) are described by this shape.
|
|
40
|
+
*/
|
|
41
|
+
export interface JournalShape {
|
|
42
|
+
entries: Array<{ idx: number; tag: string; when: number }>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// --- Two-track ledger constants -------------------------------------------
|
|
46
|
+
//
|
|
47
|
+
// Each migration track records what it has applied in its own ledger table,
|
|
48
|
+
// both living in the `drizzle` schema (D4: two ledgers, one schema). The engine
|
|
49
|
+
// values MUST match Drizzle's defaults so the existing populated
|
|
50
|
+
// `drizzle.__drizzle_migrations` ledger keeps working with zero re-stamping.
|
|
51
|
+
|
|
52
|
+
/** Engine ledger schema (Drizzle default). */
|
|
53
|
+
export const ENGINE_MIGRATIONS_SCHEMA = "drizzle";
|
|
54
|
+
/** Engine ledger table (Drizzle default). */
|
|
55
|
+
export const ENGINE_MIGRATIONS_TABLE = "__drizzle_migrations";
|
|
56
|
+
/** Client ledger schema — sibling of the engine ledger in the same schema. */
|
|
57
|
+
export const CLIENT_MIGRATIONS_SCHEMA = "drizzle";
|
|
58
|
+
/** Client ledger table. */
|
|
59
|
+
export const CLIENT_MIGRATIONS_TABLE = "__client_migrations";
|
|
60
|
+
|
|
61
|
+
/** Source describing one track: its required journal + where it records state. */
|
|
62
|
+
export interface VersionSource {
|
|
63
|
+
/** Journal entries (idx/tag/when) that this track's code requires. */
|
|
64
|
+
journal: JournalShape;
|
|
65
|
+
/** Ledger schema, e.g. "drizzle". */
|
|
66
|
+
ledgerSchema: string;
|
|
67
|
+
/** Ledger table, e.g. "__drizzle_migrations" / "__client_migrations". */
|
|
68
|
+
ledgerTable: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Migration entries for a journal, ordered by index. */
|
|
72
|
+
function getJournalEntries(j: JournalShape): MigrationEntry[] {
|
|
73
|
+
return j.entries
|
|
74
|
+
.map((e) => ({ idx: e.idx, tag: e.tag, when: e.when }))
|
|
75
|
+
.sort((a, b) => a.idx - b.idx);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Migrations bundled into this build (engine track), ordered by index. */
|
|
79
|
+
export function getBundledMigrations(): MigrationEntry[] {
|
|
80
|
+
return getJournalEntries(journal as JournalShape);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Compare a track's required migrations against those recorded in its ledger.
|
|
85
|
+
* Count-based: Drizzle applies migrations in journal order and inserts one row
|
|
86
|
+
* per applied migration, so the ledger row count is the length of the applied
|
|
87
|
+
* prefix. Robust to how Drizzle stamps `created_at` and to a DB ahead of build.
|
|
88
|
+
*
|
|
89
|
+
* Note: a database *ahead* of the build (more migrations applied than bundled —
|
|
90
|
+
* e.g. during a rollback to older code) still reports `inSync: true`, because
|
|
91
|
+
* every migration the code requires is present. That is the expand/contract
|
|
92
|
+
* contract: old code is compatible with a newer schema.
|
|
93
|
+
*/
|
|
94
|
+
async function readSchemaVersion(
|
|
95
|
+
db: ExecutableDb,
|
|
96
|
+
source: VersionSource,
|
|
97
|
+
): Promise<SchemaVersion> {
|
|
98
|
+
const bundled = getJournalEntries(source.journal);
|
|
99
|
+
const required = bundled.at(-1)?.tag ?? null;
|
|
100
|
+
|
|
101
|
+
let appliedCount = 0;
|
|
102
|
+
try {
|
|
103
|
+
const rows = (await db.execute(
|
|
104
|
+
sql`SELECT count(*)::int AS count FROM ${sql.identifier(
|
|
105
|
+
source.ledgerSchema,
|
|
106
|
+
)}.${sql.identifier(source.ledgerTable)}`,
|
|
107
|
+
)) as unknown as Array<{ count: number | string }>;
|
|
108
|
+
appliedCount = Number(rows[0]?.count ?? 0);
|
|
109
|
+
} catch {
|
|
110
|
+
// The migrations table doesn't exist yet → nothing has been applied.
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const applied =
|
|
114
|
+
appliedCount > 0
|
|
115
|
+
? (bundled[appliedCount - 1]?.tag ?? bundled.at(-1)?.tag ?? null)
|
|
116
|
+
: null;
|
|
117
|
+
const pending = bundled.slice(appliedCount).map((e) => e.tag);
|
|
118
|
+
|
|
119
|
+
return { required, applied, pending, inSync: appliedCount >= bundled.length };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Engine track version — bundled journal vs `drizzle.__drizzle_migrations`.
|
|
124
|
+
* Drives the migrator's logging, the API boot guard, and the engine portion of
|
|
125
|
+
* the `schema` block of `GET /v1/health`.
|
|
126
|
+
*/
|
|
127
|
+
export async function getEngineSchemaVersion(
|
|
128
|
+
db: ExecutableDb,
|
|
129
|
+
): Promise<SchemaVersion> {
|
|
130
|
+
return readSchemaVersion(db, {
|
|
131
|
+
journal: journal as JournalShape,
|
|
132
|
+
ledgerSchema: ENGINE_MIGRATIONS_SCHEMA,
|
|
133
|
+
ledgerTable: ENGINE_MIGRATIONS_TABLE,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Client track version — the caller supplies its own journal (the client repo's
|
|
139
|
+
* `migrations/meta/_journal.json`), recorded in `drizzle.__client_migrations`.
|
|
140
|
+
* An empty journal (`{ entries: [] }`) is trivially in sync.
|
|
141
|
+
*/
|
|
142
|
+
export async function getClientSchemaVersion(
|
|
143
|
+
db: ExecutableDb,
|
|
144
|
+
clientJournal: JournalShape,
|
|
145
|
+
): Promise<SchemaVersion> {
|
|
146
|
+
return readSchemaVersion(db, {
|
|
147
|
+
journal: clientJournal,
|
|
148
|
+
ledgerSchema: CLIENT_MIGRATIONS_SCHEMA,
|
|
149
|
+
ledgerTable: CLIENT_MIGRATIONS_TABLE,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* BACK-COMPAT default — equals the engine track. The boot guard, `/v1/health`,
|
|
155
|
+
* and existing tests import this; keep it as a one-line delegate so their
|
|
156
|
+
* behavior is unchanged.
|
|
157
|
+
*/
|
|
158
|
+
export async function getSchemaVersion(
|
|
159
|
+
db: ExecutableDb,
|
|
160
|
+
): Promise<SchemaVersion> {
|
|
161
|
+
return getEngineSchemaVersion(db);
|
|
162
|
+
}
|