History

Use Schema.Struct for parsing

Griffin Evans committed 6 months ago

Commit 66420b81 parent 4c9c3eb1 file · +42 −37Browse files

Unified · Split

apps/local/src/server/executor.ts+42−37
@@ -1,7 +1,7 @@
11 import { Database } from "bun:sqlite";
22 import { drizzle } from "drizzle-orm/bun-sqlite";
33 import { migrate } from "drizzle-orm/bun-sqlite/migrator";
4−import { Context, Data, Effect, Layer, ManagedRuntime } from "effect";
4+import { Context, Data, Effect, Layer, ManagedRuntime, Schema } from "effect";
55 import { createHash } from "node:crypto";
66 import * as fs from "node:fs";
77 import { homedir, tmpdir } from "node:os";
@@ -165,21 +165,25 @@ export const readAppliedDrizzleMigrationHashes = (sqlite: Database): ReadonlyArr · 157 unmodified lines
165165 .map((row) => row.hash);
166166 };
167167
168−interface DrizzleJournal {
169− readonly entries: ReadonlyArray<{
170− readonly idx: number;
171− readonly tag: string;
172− }>;
173−}
168+const DrizzleJournal = Schema.Struct({
169+ entries: Schema.Array(
170+ Schema.Struct({
171+ idx: Schema.Number,
172+ tag: Schema.String,
173+ }),
174+ ),
175+});
176+
177+const decodeDrizzleJournal = Schema.decodeUnknownSync(Schema.fromJsonString(DrizzleJournal));
174178
175179 export const readBundledDrizzleMigrationHashes = (
176180 migrationsFolder: string,
177181 ): ReadonlyArray<string> => {
178182 // Keep this in sync with drizzle-orm/src/migrator.ts: Drizzle hashes the raw
179183 // migration file contents before splitting on statement breakpoints.
180− const journal = JSON.parse(
184+ const journal = decodeDrizzleJournal(
181185 fs.readFileSync(join(migrationsFolder, "meta", "_journal.json")).toString(),
182− ) as DrizzleJournal;
186+ );
183187
184188 return [...journal.entries]
185189 .sort((left, right) => left.idx - right.idx)
@@ -207,36 +211,37 @@ export const checkDrizzleMigrationCompatibility = (input: { · 21 unmodified lines
207211 readonly sqlite: Database;
208212 readonly dbPath: string;
209213 readonly migrationsFolder: string;
210−}): void => {
211− // Before running migrations, ensure the DB history is a prefix of the
212− // migrations bundled with this binary. This catches newer or divergent schemas
213− // before startup reaches arbitrary schema-dependent queries.
214− if (!drizzleMigrationsTableExists(input.sqlite)) return;
215−
216− const applied = readAppliedDrizzleMigrationHashes(input.sqlite);
217− const bundled = readBundledDrizzleMigrationHashes(input.migrationsFolder);
218−
219− if (applied.length > bundled.length) {
220− throw new LocalDatabaseSchemaTooNew({
221− message: schemaTooNewMessage(input.dbPath),
222− dbPath: input.dbPath,
223− appliedMigrationCount: applied.length,
224− knownMigrationCount: bundled.length,
225− });
226− }
227−
228− for (let index = 0; index < applied.length; index += 1) {
229− if (applied[index] !== bundled[index]) {
230− throw new LocalDatabaseMigrationHistoryMismatch({
231− message: migrationHistoryMismatchMessage(input.dbPath),
214+}): Effect.Effect<void, LocalDatabaseSchemaTooNew | LocalDatabaseMigrationHistoryMismatch> =>
215+ Effect.gen(function* () {
216+ // Before running migrations, ensure the DB history is a prefix of the
217+ // migrations bundled with this binary. This catches newer or divergent schemas
218+ // before startup reaches arbitrary schema-dependent queries.
219+ if (!drizzleMigrationsTableExists(input.sqlite)) return;
220+
221+ const applied = readAppliedDrizzleMigrationHashes(input.sqlite);
222+ const bundled = readBundledDrizzleMigrationHashes(input.migrationsFolder);
223+
224+ if (applied.length > bundled.length) {
225+ return yield* new LocalDatabaseSchemaTooNew({
226+ message: schemaTooNewMessage(input.dbPath),
232227 dbPath: input.dbPath,
233− migrationIndex: index,
234− appliedHash: applied[index],
235− knownHash: bundled[index],
228+ appliedMigrationCount: applied.length,
229+ knownMigrationCount: bundled.length,
236230 });
237231 }
238− }
239−};
232+
233+ for (let index = 0; index < applied.length; index += 1) {
234+ if (applied[index] !== bundled[index]) {
235+ return yield* new LocalDatabaseMigrationHistoryMismatch({
236+ message: migrationHistoryMismatchMessage(input.dbPath),
237+ dbPath: input.dbPath,
238+ migrationIndex: index,
239+ appliedHash: applied[index],
240+ knownHash: bundled[index],
241+ });
242+ }
243+ }
244+ });
240245
241246 const createLocalExecutorLayer = () => {
242247 const { path: dbPath, legacySecrets } = resolveDbPath();
@@ -247,7 +252,7 @@ const createLocalExecutorLayer = () => { · 4 unmodified lines
247252 Effect.sync(() => new Database(dbPath)),
248253 (conn) => Effect.sync(() => conn.close()),
249254 );
250− checkDrizzleMigrationCompatibility({
255+ yield* checkDrizzleMigrationCompatibility({
251256 sqlite,
252257 dbPath,
253258 migrationsFolder: MIGRATIONS_FOLDER,