@fjall/util 0.99.4 → 0.100.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.
- package/dist/.minified +1 -1
- package/dist/migration/index.d.ts +1 -0
- package/dist/migration/index.js +1 -1
- package/dist/migration/verifyExpectedSchemaVersion.d.ts +57 -0
- package/dist/migration/verifyExpectedSchemaVersion.js +1 -0
- package/dist/migration/verifyExpectedSchemaVersion.test.d.ts +1 -0
- package/dist/migration/verifyExpectedSchemaVersion.test.js +1 -0
- package/package.json +2 -2
package/dist/.minified
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
55 files minified at 2026-05-23T06:07:02.579Z
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { MIGRATION_SNAPSHOT_NAME_PREFIX, EXPECTED_SCHEMA_VERSION_ENV, EXPECTED_SCHEMA_VERSION_TOOL_ENV, PRISMA_MIGRATION_DIR_RE } from "./constants.js";
|
|
2
2
|
export { pickLatestPrismaMigration } from "./pickLatestPrismaMigration.js";
|
|
3
|
+
export { type MigrationsSqlClient, type VerifyExpectedSchemaVersionOpts, type VerifyExpectedSchemaVersionResult, verifyExpectedSchemaVersion } from "./verifyExpectedSchemaVersion.js";
|
|
3
4
|
export { CLICKHOUSE_MANAGED_USERS_ENV, MANAGED_USER_NAME_PATTERN, userPasswordEnvName, ManagedUserNameSchema, ManagedUserNamesSchema, type ManagedUserName, type ManagedUserNames } from "./clickhouseSqlUsers.js";
|
package/dist/migration/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{MIGRATION_SNAPSHOT_NAME_PREFIX as
|
|
1
|
+
import{MIGRATION_SNAPSHOT_NAME_PREFIX as _,EXPECTED_SCHEMA_VERSION_ENV as r,EXPECTED_SCHEMA_VERSION_TOOL_ENV as N,PRISMA_MIGRATION_DIR_RE as a}from"./constants.js";import{pickLatestPrismaMigration as A}from"./pickLatestPrismaMigration.js";import{verifyExpectedSchemaVersion as m}from"./verifyExpectedSchemaVersion.js";import{CLICKHOUSE_MANAGED_USERS_ENV as R,MANAGED_USER_NAME_PATTERN as I,userPasswordEnvName as s,ManagedUserNameSchema as t,ManagedUserNamesSchema as O}from"./clickhouseSqlUsers.js";export{R as CLICKHOUSE_MANAGED_USERS_ENV,r as EXPECTED_SCHEMA_VERSION_ENV,N as EXPECTED_SCHEMA_VERSION_TOOL_ENV,I as MANAGED_USER_NAME_PATTERN,_ as MIGRATION_SNAPSHOT_NAME_PREFIX,t as ManagedUserNameSchema,O as ManagedUserNamesSchema,a as PRISMA_MIGRATION_DIR_RE,A as pickLatestPrismaMigration,s as userPasswordEnvName,m as verifyExpectedSchemaVersion};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool-discriminated boot-time gate: verify a relational database's most
|
|
3
|
+
* recently applied migration matches the expected version baked into the
|
|
4
|
+
* deployed image. Companion to {@link pickLatestPrismaMigration} (which
|
|
5
|
+
* resolves the expected version FROM disk at synth/runner time); this
|
|
6
|
+
* helper closes the loop by reading what the DB actually has APPLIED.
|
|
7
|
+
*
|
|
8
|
+
* Consumer supplies the driver via the {@link MigrationsSqlClient} shim so
|
|
9
|
+
* the package stays driver-agnostic (pg, mysql2, postgres.js, Drizzle
|
|
10
|
+
* session, raw Prisma `$queryRawUnsafe`, …). The shim's single `query(sql)`
|
|
11
|
+
* surface is intentionally narrower than any real driver — callers wrap.
|
|
12
|
+
*
|
|
13
|
+
* Two tool branches today:
|
|
14
|
+
* - `tool: "prisma"` reads `_prisma_migrations.migration_name` (most-recent
|
|
15
|
+
* `finished_at IS NOT NULL`). Stable across all Prisma DB engines.
|
|
16
|
+
* - `tool: "custom"` lets a caller pass a `{ sql, column }` pair when the
|
|
17
|
+
* migration tool's metadata table doesn't match the Prisma shape (e.g.
|
|
18
|
+
* Drizzle's `__drizzle_migrations.hash`, Knex's `knex_migrations.name`).
|
|
19
|
+
*
|
|
20
|
+
* Returns `{ matches, expected, actual }` rather than throwing on mismatch:
|
|
21
|
+
* the boot gate's caller owns the exit-code / log shape so the helper can
|
|
22
|
+
* be reused by integration tests, dashboards, and CLI checks alike.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Minimal SQL-driver shim. Compatible with `pg.Client.query`, `mysql2.query`,
|
|
26
|
+
* `postgres()(sql)`, and Drizzle's `db.session.execute()` via a thin adapter.
|
|
27
|
+
*/
|
|
28
|
+
export interface MigrationsSqlClient {
|
|
29
|
+
query(sql: string): Promise<{
|
|
30
|
+
rows: ReadonlyArray<Record<string, unknown>>;
|
|
31
|
+
}>;
|
|
32
|
+
}
|
|
33
|
+
export interface VerifyExpectedSchemaVersionOpts {
|
|
34
|
+
/** Migration tool whose metadata table to inspect. */
|
|
35
|
+
tool: "prisma" | "custom";
|
|
36
|
+
/** Expected version (typically read from an env var baked into the image). */
|
|
37
|
+
expected: string;
|
|
38
|
+
/** SQL driver shim. Consumer owns connection lifecycle. */
|
|
39
|
+
client: MigrationsSqlClient;
|
|
40
|
+
/** Abort signal honoured before the query is issued. */
|
|
41
|
+
signal?: AbortSignal;
|
|
42
|
+
/**
|
|
43
|
+
* Required when `tool: "custom"`. `sql` MUST return a single row whose
|
|
44
|
+
* `column` holds the latest applied migration's name/hash.
|
|
45
|
+
*/
|
|
46
|
+
customQuery?: {
|
|
47
|
+
sql: string;
|
|
48
|
+
column: string;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export interface VerifyExpectedSchemaVersionResult {
|
|
52
|
+
matches: boolean;
|
|
53
|
+
expected: string;
|
|
54
|
+
/** `null` when no migrations have been applied (empty result set). */
|
|
55
|
+
actual: string | null;
|
|
56
|
+
}
|
|
57
|
+
export declare function verifyExpectedSchemaVersion(opts: VerifyExpectedSchemaVersionOpts): Promise<VerifyExpectedSchemaVersionResult>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const c="SELECT migration_name FROM _prisma_migrations WHERE finished_at IS NOT NULL ORDER BY finished_at DESC LIMIT 1",s="migration_name";async function a(e){if(e.signal?.aborted)throw new Error("verifyExpectedSchemaVersion: aborted by signal before query");let r,n;if(e.tool==="prisma")r=c,n=s;else if(e.tool==="custom"){if(e.customQuery===void 0)throw new Error('verifyExpectedSchemaVersion: tool="custom" requires customQuery');r=e.customQuery.sql,n=e.customQuery.column}else throw new Error(`verifyExpectedSchemaVersion: unknown tool ${String(e.tool)}`);const t=(await e.client.query(r)).rows[0],o=t!==void 0?t[n]:void 0,i=typeof o=="string"?o:null;return{matches:i===e.expected,expected:e.expected,actual:i}}export{a as verifyExpectedSchemaVersion};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{describe as r,expect as a,it as o,vi as l}from"vitest";import{verifyExpectedSchemaVersion as i}from"./verifyExpectedSchemaVersion.js";function n(t){const e=[];return{client:{query:l.fn(async c=>(e.push(c),{rows:t}))},queries:e}}r("verifyExpectedSchemaVersion",()=>{r('tool: "prisma"',()=>{o("returns matches=true when actual === expected",async()=>{const{client:t,queries:e}=n([{migration_name:"20260520000000_add_widgets"}]),s=await i({tool:"prisma",expected:"20260520000000_add_widgets",client:t});a(s).toEqual({matches:!0,expected:"20260520000000_add_widgets",actual:"20260520000000_add_widgets"}),a(e).toHaveLength(1),a(e[0]).toContain("_prisma_migrations"),a(e[0]).toContain("finished_at IS NOT NULL"),a(e[0]).toContain("ORDER BY finished_at DESC LIMIT 1")}),o("returns matches=false with actual when DB trails image",async()=>{const{client:t}=n([{migration_name:"20260519000000_old"}]),e=await i({tool:"prisma",expected:"20260520000000_add_widgets",client:t});a(e).toEqual({matches:!1,expected:"20260520000000_add_widgets",actual:"20260519000000_old"})}),o("returns actual=null on empty result set",async()=>{const{client:t}=n([]),e=await i({tool:"prisma",expected:"20260520000000_add_widgets",client:t});a(e).toEqual({matches:!1,expected:"20260520000000_add_widgets",actual:null})}),o("returns actual=null when row's migration_name is not a string",async()=>{const{client:t}=n([{migration_name:12345}]),e=await i({tool:"prisma",expected:"x",client:t});a(e.actual).toBeNull(),a(e.matches).toBe(!1)})}),r('tool: "custom"',()=>{o("uses customQuery sql and column",async()=>{const{client:t,queries:e}=n([{hash:"deadbeef"}]),s=await i({tool:"custom",expected:"deadbeef",client:t,customQuery:{sql:"SELECT hash FROM __drizzle_migrations ORDER BY created_at DESC LIMIT 1",column:"hash"}});a(s.matches).toBe(!0),a(s.actual).toBe("deadbeef"),a(e[0]).toBe("SELECT hash FROM __drizzle_migrations ORDER BY created_at DESC LIMIT 1")}),o("throws when customQuery is omitted",async()=>{const{client:t}=n([]);await a(i({tool:"custom",expected:"x",client:t})).rejects.toThrow(/tool="custom" requires customQuery/)})}),r("abort signal",()=>{o("short-circuits before query when signal is already aborted",async()=>{const{client:t,queries:e}=n([{migration_name:"irrelevant"}]),s=new AbortController;s.abort(),await a(i({tool:"prisma",expected:"x",client:t,signal:s.signal})).rejects.toThrow(/aborted by signal before query/),a(e).toEqual([])})})});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fjall/util",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.100.0",
|
|
4
4
|
"description": "Common utility methods",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -117,5 +117,5 @@
|
|
|
117
117
|
"engines": {
|
|
118
118
|
"node": ">=22.0.0"
|
|
119
119
|
},
|
|
120
|
-
"gitHead": "
|
|
120
|
+
"gitHead": "9eb16c56de49e6757cfd6352ad860dd125c6c21e"
|
|
121
121
|
}
|