@supawatch/verify 0.7.1 → 0.9.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/fixture.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import { type Snapshot } from "@supawatch/core";
2
- export declare const FIXTURE_SQL = "\ncreate type parcel_state as enum ('queued', 'shipped', 'lost');\ncreate type dimensions as (width_mm int4, height_mm int4);\ncreate domain tracking_code as text;\ncreate domain weight_grams as int4;\n\ncreate table parcels (\n size dimensions,\n tracking tracking_code not null default 'T-1',\n weight weight_grams not null default 250,\n id serial primary key,\n small int2 not null default 1,\n big int8 not null default 9007199254740993,\n ratio float4 not null default 0.5,\n wide float8 not null default 2.25,\n price numeric(10,2) not null default 19.99,\n ref uuid not null default gen_random_uuid(),\n label text not null default 'x',\n short_code varchar(12) not null default 'abc',\n padded bpchar(4) not null default 'ab',\n active bool not null default true,\n seen_at timestamptz not null default now(),\n local_at timestamp not null default now(),\n blob_j json not null default '{\"a\":1}',\n blob_jb jsonb not null default '{\"b\":2}',\n state parcel_state not null default 'queued',\n shipped_on date not null default '2026-02-03',\n cutoff time not null default '13:45:10',\n cutoff_tz timetz not null default '13:45:10+02',\n transit interval not null default '1 day 02:00:00',\n stamp bytea not null default '\\xdeadbeef',\n source_ip inet not null default '192.168.0.1',\n subnet cidr not null default '10.0.0.0/8',\n device macaddr not null default '08:00:2b:01:02:03',\n tags text[] not null default array['x','y'],\n counts int4[] not null default array[1,2,3],\n amounts numeric[] not null default array['1.50']::numeric[],\n states parcel_state[] not null default array['queued']::parcel_state[],\n grid int4[][] not null default array[array[1,2],array[3,4]],\n note text\n);\n\ninsert into parcels (note, size) values\n ('first', row(100, 40)::dimensions),\n (null, null);\n\ncreate view lost_parcels as\n select id, tracking, state from parcels where state = 'lost';\n";
2
+ export declare const FIXTURE_SQL = "\ncreate type parcel_state as enum ('queued', 'shipped', 'lost');\ncreate type dimensions as (width_mm int4, height_mm int4);\ncreate domain tracking_code as text;\ncreate domain weight_grams as int4;\n\ncreate table parcels (\n size dimensions,\n tracking tracking_code not null default 'T-1',\n weight weight_grams not null default 250,\n id serial primary key,\n small int2 not null default 1,\n big int8 not null default 9007199254740993,\n ratio float4 not null default 0.5,\n wide float8 not null default 2.25,\n price numeric(10,2) not null default 19.99,\n ref uuid not null default gen_random_uuid(),\n label text not null default 'x',\n short_code varchar(12) not null default 'abc',\n padded bpchar(4) not null default 'ab',\n active bool not null default true,\n seen_at timestamptz not null default now(),\n local_at timestamp not null default now(),\n blob_j json not null default '{\"a\":1}',\n blob_jb jsonb not null default '{\"b\":2}',\n state parcel_state not null default 'queued',\n shipped_on date not null default '2026-02-03',\n cutoff time not null default '13:45:10',\n cutoff_tz timetz not null default '13:45:10+02',\n transit interval not null default '1 day 02:00:00',\n stamp bytea not null default '\\xdeadbeef',\n source_ip inet not null default '192.168.0.1',\n subnet cidr not null default '10.0.0.0/8',\n device macaddr not null default '08:00:2b:01:02:03',\n tags text[] not null default array['x','y'],\n counts int4[] not null default array[1,2,3],\n amounts numeric[] not null default array['1.50']::numeric[],\n states parcel_state[] not null default array['queued']::parcel_state[],\n grid int4[][] not null default array[array[1,2],array[3,4]],\n note text\n);\n\ninsert into parcels (note, size) values\n ('first', row(100, 40)::dimensions),\n (null, null);\n\n-- Pathological but REAL values: floats can be NaN and infinities,\n-- temporal columns can be 'infinity' (an Invalid Date at the driver),\n-- and an enum can exist with zero labels. Every target must accept\n-- these rows, because the database holds them.\ncreate type unlabeled as enum ();\ninsert into parcels (note, ratio, wide, seen_at, shipped_on) values\n ('nan-row', 'NaN', 'NaN', 'infinity', 'infinity'),\n ('inf-row', 'Infinity', '-Infinity', '-infinity', '0044-03-15 BC');\nalter table parcels add column phase unlabeled;\n\ncreate view lost_parcels as\n select id, tracking, state from parcels where state = 'lost';\n";
3
3
  export declare function assertFixtureCompleteness(snapshot: Snapshot): void;
package/dist/fixture.js CHANGED
@@ -48,6 +48,16 @@ insert into parcels (note, size) values
48
48
  ('first', row(100, 40)::dimensions),
49
49
  (null, null);
50
50
 
51
+ -- Pathological but REAL values: floats can be NaN and infinities,
52
+ -- temporal columns can be 'infinity' (an Invalid Date at the driver),
53
+ -- and an enum can exist with zero labels. Every target must accept
54
+ -- these rows, because the database holds them.
55
+ create type unlabeled as enum ();
56
+ insert into parcels (note, ratio, wide, seen_at, shipped_on) values
57
+ ('nan-row', 'NaN', 'NaN', 'infinity', 'infinity'),
58
+ ('inf-row', 'Infinity', '-Infinity', '-infinity', '0044-03-15 BC');
59
+ alter table parcels add column phase unlabeled;
60
+
51
61
  create view lost_parcels as
52
62
  select id, tracking, state from parcels where state = 'lost';
53
63
  `;
package/dist/harness.d.ts CHANGED
@@ -38,7 +38,11 @@ export interface HarnessResult {
38
38
  export declare const DRIVER_DELTAS: readonly [{
39
39
  readonly id: "int8-bigint-vs-string";
40
40
  readonly detail: "PGlite parses int8 to a JS BigInt; postgres.js returns its decimal string. Normalized: BigInt -> String(value).";
41
+ }, {
42
+ readonly id: "enum-array-literal-vs-array";
43
+ readonly detail: "PGlite returns enum arrays as the raw pg literal; a fresh postgres.js connection parses them to real arrays (measured; parsers are fetched at connect). Normalized: literal -> parsed array on enum-array columns.";
41
44
  }];
45
+ export declare function parsePgTextArray(literal: string): string[];
42
46
  export declare function runHarness(opts: {
43
47
  targets: HarnessTarget[];
44
48
  workDir: string;
package/dist/harness.js CHANGED
@@ -11,7 +11,57 @@ export const DRIVER_DELTAS = [
11
11
  id: "int8-bigint-vs-string",
12
12
  detail: "PGlite parses int8 to a JS BigInt; postgres.js returns its decimal string. Normalized: BigInt -> String(value).",
13
13
  },
14
+ {
15
+ id: "enum-array-literal-vs-array",
16
+ detail: "PGlite returns enum arrays as the raw pg literal; a fresh postgres.js connection parses them to real arrays (measured; parsers are fetched at connect). Normalized: literal -> parsed array on enum-array columns.",
17
+ },
14
18
  ];
19
+ // Minimal pg array-literal parser for the enum-array delta: handles
20
+ // quoted elements, doubled quotes, and backslash escapes.
21
+ export function parsePgTextArray(literal) {
22
+ const inner = literal.slice(1, -1);
23
+ if (inner === "")
24
+ return [];
25
+ const out = [];
26
+ let cur = "";
27
+ let quoted = false;
28
+ let wasQuoted = false;
29
+ for (let i = 0; i < inner.length; i++) {
30
+ const ch = inner[i];
31
+ if (quoted) {
32
+ if (ch === "\\") {
33
+ cur += inner[++i];
34
+ }
35
+ else if (ch === '"') {
36
+ if (inner[i + 1] === '"') {
37
+ cur += '"';
38
+ i++;
39
+ }
40
+ else {
41
+ quoted = false;
42
+ }
43
+ }
44
+ else {
45
+ cur += ch;
46
+ }
47
+ }
48
+ else if (ch === '"') {
49
+ quoted = true;
50
+ wasQuoted = true;
51
+ }
52
+ else if (ch === ",") {
53
+ out.push(cur);
54
+ cur = "";
55
+ wasQuoted = false;
56
+ }
57
+ else {
58
+ cur += ch;
59
+ }
60
+ }
61
+ out.push(cur);
62
+ void wasQuoted;
63
+ return out;
64
+ }
15
65
  function normalize(value) {
16
66
  if (typeof value === "bigint")
17
67
  return value.toString();
@@ -101,6 +151,19 @@ export async function runHarness(opts) {
101
151
  const parity = [];
102
152
  for (const table of snapshot.tables) {
103
153
  const rows = await query(`select * from "${table.name}" limit 10`);
154
+ // The enum-array-literal-vs-array delta: PGlite hands back the raw
155
+ // literal, generated schemas expect the parsed array that a fresh
156
+ // postgres.js connection returns.
157
+ for (const col of table.columns) {
158
+ if (col.runtime.kind !== "array" || col.runtime.element.kind !== "enum")
159
+ continue;
160
+ for (const row of rows) {
161
+ const v = row[col.name];
162
+ if (typeof v === "string") {
163
+ row[col.name] = parsePgTextArray(v);
164
+ }
165
+ }
166
+ }
104
167
  // Ground truth: every real row must be accepted by every target.
105
168
  for (const [name, { schemaByTable, verifier }] of loaded) {
106
169
  const schema = schemaByTable.get(table.name);
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { runHarness, DRIVER_DELTAS, type AllowedDivergence, type GroundTruthRow, type HarnessResult, type HarnessTarget, type NegativeResult, type ParityCase, } from "./harness.js";
1
+ export { runHarness, DRIVER_DELTAS, parsePgTextArray, type AllowedDivergence, type GroundTruthRow, type HarnessResult, type HarnessTarget, type NegativeResult, type ParityCase, } from "./harness.js";
2
2
  export { FIXTURE_SQL, assertFixtureCompleteness } from "./fixture.js";
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { runHarness, DRIVER_DELTAS, } from "./harness.js";
1
+ export { runHarness, DRIVER_DELTAS, parsePgTextArray, } from "./harness.js";
2
2
  export { FIXTURE_SQL, assertFixtureCompleteness } from "./fixture.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supawatch/verify",
3
- "version": "0.7.1",
3
+ "version": "0.9.0",
4
4
  "description": "The supawatch verification harness: ground truth against real rows, cross-target parity, and a must-fire divergence ledger.",
5
5
  "keywords": [
6
6
  "supabase",
@@ -34,7 +34,7 @@
34
34
  ],
35
35
  "dependencies": {
36
36
  "@electric-sql/pglite": "^0.3.0",
37
- "@supawatch/core": "0.7.1"
37
+ "@supawatch/core": "0.9.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@modelcontextprotocol/sdk": "^1.30.0",
@@ -52,32 +52,33 @@
52
52
  "typescript": "^5.9.0",
53
53
  "valibot": "^1.1.0",
54
54
  "zod": "^4.0.0",
55
- "@supawatch/target-ai-tools": "0.7.1",
56
- "@supawatch/target-arktype": "0.7.1",
57
- "@supawatch/target-dictionary": "0.7.1",
58
- "@supawatch/target-effect": "0.7.1",
59
- "@supawatch/target-erd": "0.7.1",
60
- "@supawatch/target-factories": "0.7.1",
61
- "@supawatch/target-fast-check": "0.7.1",
62
- "@supawatch/target-forms": "0.7.1",
63
- "@supawatch/target-graphql": "0.7.1",
64
- "@supawatch/target-json-schema": "0.7.1",
65
- "@supawatch/target-mcp": "0.7.1",
66
- "@supawatch/target-orpc": "0.7.1",
67
- "@supawatch/target-pgmq": "0.7.1",
68
- "@supawatch/target-pgtap": "0.7.1",
69
- "@supawatch/target-realtime": "0.7.1",
70
- "@supawatch/target-rest": "0.7.1",
71
- "@supawatch/target-rls": "0.7.1",
72
- "@supawatch/target-schema-card": "0.7.1",
73
- "@supawatch/target-schema-lock": "0.7.1",
74
- "@supawatch/target-seed": "0.7.1",
75
- "@supawatch/target-service": "0.7.1",
76
- "@supawatch/target-supabase-types": "0.7.1",
77
- "@supawatch/target-trpc": "0.7.1",
78
- "@supawatch/target-typebox": "0.7.1",
79
- "@supawatch/target-valibot": "0.7.1",
80
- "@supawatch/target-zod": "0.7.1"
55
+ "@supawatch/target-ai-tools": "0.9.0",
56
+ "@supawatch/target-arktype": "0.9.0",
57
+ "@supawatch/target-dictionary": "0.9.0",
58
+ "@supawatch/target-effect": "0.9.0",
59
+ "@supawatch/target-erd": "0.9.0",
60
+ "@supawatch/target-factories": "0.9.0",
61
+ "@supawatch/target-fast-check": "0.9.0",
62
+ "@supawatch/target-forms": "0.9.0",
63
+ "@supawatch/target-graphql": "0.9.0",
64
+ "@supawatch/target-json-schema": "0.9.0",
65
+ "@supawatch/target-mcp": "0.9.0",
66
+ "@supawatch/target-orpc": "0.9.0",
67
+ "@supawatch/target-pgmq": "0.9.0",
68
+ "@supawatch/target-pgtap": "0.9.0",
69
+ "@supawatch/target-realtime": "0.9.0",
70
+ "@supawatch/target-rest": "0.9.0",
71
+ "@supawatch/target-rls": "0.9.0",
72
+ "@supawatch/target-schema-card": "0.9.0",
73
+ "@supawatch/target-schema-lock": "0.9.0",
74
+ "@supawatch/target-seed": "0.9.0",
75
+ "@supawatch/target-service": "0.9.0",
76
+ "@supawatch/target-supabase-types": "0.9.0",
77
+ "@supawatch/target-trpc": "0.9.0",
78
+ "@supawatch/target-typebox": "0.9.0",
79
+ "@supawatch/target-valibot": "0.9.0",
80
+ "@supawatch/target-zod": "0.9.0",
81
+ "@supawatch/watch": "0.9.0"
81
82
  },
82
83
  "scripts": {
83
84
  "build": "tsc -p tsconfig.json"