{"_id":"@akalforge/pg-conformance","_rev":"6-ba081d5a896146cf5a4bf6c1a772b88f","name":"@akalforge/pg-conformance","dist-tags":{"latest":"0.0.6"},"versions":{"0.0.1":{"name":"@akalforge/pg-conformance","version":"0.0.1","keywords":["postgresql","postgres","schema","migration","diff","conformance","fingerprint"],"license":"MIT","_id":"@akalforge/pg-conformance@0.0.1","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/pg-conformance#readme","bugs":{"url":"https://github.com/akalforge/pg-conformance/issues"},"dist":{"shasum":"873990ff6a70c4b6441538366e6ce2690131191e","tarball":"https://registry.npmjs.org/@akalforge/pg-conformance/-/pg-conformance-0.0.1.tgz","fileCount":10,"integrity":"sha512-xqgfmSugVyB2gdttse/M9dutj75XQabqm4mvWyLKC4MeXiaPzINe9hDJY0NkV59FqXFs/F0B+jxdP3rlMont4A==","signatures":[{"sig":"MEQCIHtdhUWU9POPwfm2euRh65dC78ZOhuMymyyOtjb8/Cx+AiBxosgd9i6hkY3h62aGGJOeDyzg4yafyvFO+oy/HoakfA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":42332},"main":"index.js","type":"module","types":"index.d.ts","engines":{"node":">=18"},"exports":{".":"./index.js","./corpus/*.json":"./corpus/*.json","./fingerprint.sql":"./fingerprint.sql"},"gitHead":"004cfa02a5f5854e5b44527a440fd81dda1c4b76","scripts":{"test":"node --test test/*.test.js"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"repository":{"url":"git+https://github.com/akalforge/pg-conformance.git","type":"git"},"_npmVersion":"10.9.8","description":"A PostgreSQL schema fingerprint and DDL conformance corpus, shared by tools that need to agree on whether two schemas are the same.","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/pg-conformance_0.0.1_1787687254624_0.13161647088413342","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@akalforge/pg-conformance","version":"0.0.2","keywords":["postgresql","postgres","schema","migration","diff","conformance","fingerprint"],"license":"MIT","_id":"@akalforge/pg-conformance@0.0.2","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/pg-conformance#readme","bugs":{"url":"https://github.com/akalforge/pg-conformance/issues"},"dist":{"shasum":"f169eb9ecb671c8aadd816ca5d8fc7bd22dfff12","tarball":"https://registry.npmjs.org/@akalforge/pg-conformance/-/pg-conformance-0.0.2.tgz","fileCount":10,"integrity":"sha512-0Iyztj0ZuNrDZJ5d/q5Rxg0oZ3tFpaz/Pn6ENQvmPgSOeQvj1IrJGShIrj+mplHXYVy/YYsZ89B2gn8pEh2qUQ==","signatures":[{"sig":"MEUCID0daLzetqAFlgN0t6OLzEUrR+RUo7jqzdPKZRW925QMAiEAsBGpFulR46mLFu9V9qF5EolNA3I6EqIykGatz0km3sY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":54852},"main":"index.js","type":"module","types":"index.d.ts","engines":{"node":">=18"},"exports":{".":"./index.js","./corpus/*.json":"./corpus/*.json","./fingerprint.sql":"./fingerprint.sql"},"gitHead":"03cf5ea724be858c39dd23a9d8435546e52b78b3","scripts":{"test":"node --test test/*.test.js"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"repository":{"url":"git+https://github.com/akalforge/pg-conformance.git","type":"git"},"_npmVersion":"10.9.8","description":"A PostgreSQL schema fingerprint and DDL conformance corpus, shared by tools that need to agree on whether two schemas are the same.","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/pg-conformance_0.0.2_1788212621604_0.7340286369367488","host":"s3://npm-registry-packages-npm-production"}},"0.0.3":{"name":"@akalforge/pg-conformance","version":"0.0.3","keywords":["postgresql","postgres","schema","migration","diff","conformance","fingerprint"],"license":"MIT","_id":"@akalforge/pg-conformance@0.0.3","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/pg-conformance#readme","bugs":{"url":"https://github.com/akalforge/pg-conformance/issues"},"dist":{"shasum":"3abe92a376e3cd0ec9840ac00a6282b40fef0e19","tarball":"https://registry.npmjs.org/@akalforge/pg-conformance/-/pg-conformance-0.0.3.tgz","fileCount":11,"integrity":"sha512-TFQKXVe+d6Rzc8XAoP8pMHw5+DgRh+1cf6Sa9LrXz6k8I90GOLQtQj+bw/u+PBxZo6134aZ9uEWx8Fkr887A/w==","signatures":[{"sig":"MEYCIQDcZRTdKJG+5T3CKniuqLjeCVRt3b5oZJAaPhgVcKQt7AIhAIh2NA5cZ64VbfAUHip1stZgJ/Cwo6l+DwVeH8o55DGI","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":73508},"main":"index.js","type":"module","types":"index.d.ts","engines":{"node":">=18"},"exports":{".":"./index.js","./corpus/*.json":"./corpus/*.json","./fingerprint.sql":"./fingerprint.sql"},"gitHead":"50725f92d188ee2d3b05b67663739d167abb326a","scripts":{"test":"node --test test/*.test.js"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"repository":{"url":"git+https://github.com/akalforge/pg-conformance.git","type":"git"},"_npmVersion":"10.9.8","description":"A PostgreSQL schema fingerprint and DDL conformance corpus, shared by tools that need to agree on whether two schemas are the same.","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/pg-conformance_0.0.3_1788215529690_0.6142546977112686","host":"s3://npm-registry-packages-npm-production"}},"0.0.4":{"name":"@akalforge/pg-conformance","version":"0.0.4","keywords":["postgresql","postgres","schema","migration","diff","conformance","fingerprint"],"license":"MIT","_id":"@akalforge/pg-conformance@0.0.4","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/pg-conformance#readme","bugs":{"url":"https://github.com/akalforge/pg-conformance/issues"},"dist":{"shasum":"342db80ec4a9633af9ef15bc0e019172f4675290","tarball":"https://registry.npmjs.org/@akalforge/pg-conformance/-/pg-conformance-0.0.4.tgz","fileCount":11,"integrity":"sha512-QbRm5nHngZZZVvhOGh6vyDw4jyt6VTUCHbtM/Ykdhr03M4evcppFOBd5AFA4AYOuq4Neml7GOu5JUyi58CKmmA==","signatures":[{"sig":"MEUCIQDPYbmh4mEnqh0NIdsgAsLZPHNBdfGeqhOjNAdH38RKogIgZnTmMVgaYNiT7p1QEzCHBMPGK9S3C5BM/tyT0P/fcgU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":76083},"main":"index.js","type":"module","types":"index.d.ts","engines":{"node":">=18"},"exports":{".":"./index.js","./corpus/*.json":"./corpus/*.json","./fingerprint.sql":"./fingerprint.sql"},"gitHead":"3bc4ae6a8095482d46bb48f60e27ae8f7672dc00","scripts":{"test":"node --test test/*.test.js"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"repository":{"url":"git+https://github.com/akalforge/pg-conformance.git","type":"git"},"_npmVersion":"10.9.8","description":"A PostgreSQL schema fingerprint and DDL conformance corpus, shared by tools that need to agree on whether two schemas are the same.","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/pg-conformance_0.0.4_1789416274831_0.3654756583106369","host":"s3://npm-registry-packages-npm-production"}},"0.0.5":{"name":"@akalforge/pg-conformance","version":"0.0.5","keywords":["postgresql","postgres","schema","migration","diff","conformance","fingerprint"],"license":"MIT","_id":"@akalforge/pg-conformance@0.0.5","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/pg-conformance#readme","bugs":{"url":"https://github.com/akalforge/pg-conformance/issues"},"dist":{"shasum":"a9e0652cfdc3da1e7a3b48d4759c263ba967d282","tarball":"https://registry.npmjs.org/@akalforge/pg-conformance/-/pg-conformance-0.0.5.tgz","fileCount":11,"integrity":"sha512-vlS65JEq6y9A4qkJa6tfSMkxkrvMWe1dLWabX/3jd0KzenkolAJits1RjnE46356ywB5tK3SeJ2WZljQwHLgow==","signatures":[{"sig":"MEQCIHS+Qv38e2z9S5BvymLhab3W4HyOKkdu4ASLrbsPH26+AiAbGynr+zQFR+elFxHyroPy+lCXX0wPFJ70kSf5VJzAWw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQC/z3ni6473S4n+MQ65qlXunUvJgjj384EigwCvozCwpgIgUIYuPuzva+Zl1GljWDYaW1a7KM9ahiL7EaXjE1Z0rkc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":76090},"main":"index.js","type":"module","types":"index.d.ts","engines":{"node":">=18"},"exports":{".":"./index.js","./corpus/*.json":"./corpus/*.json","./fingerprint.sql":"./fingerprint.sql"},"gitHead":"3eb89c66f582b943ef36f05b967bcce77b6cd45a","scripts":{"test":"node --test test/*.test.js"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"repository":{"url":"git+https://github.com/akalforge/pg-conformance.git","type":"git"},"_npmVersion":"10.9.8","description":"A PostgreSQL schema fingerprint and DDL conformance corpus, shared by tools that need to agree on whether two schemas are the same.","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/pg-conformance_0.0.5_1790115263925_0.018481834663016894","host":"s3://npm-registry-packages-npm-production"}},"0.0.6":{"_id":"@akalforge/pg-conformance@0.0.6","bugs":{"url":"https://github.com/akalforge/pg-conformance/issues"},"dist":{"shasum":"712b858700534c8652a0518c7154bbe025511cf4","tarball":"https://registry.npmjs.org/@akalforge/pg-conformance/-/pg-conformance-0.0.6.tgz","fileCount":11,"integrity":"sha512-6Xcl9RsbPelLq2Rt2F9Zz1hOg4FfHrhN1fQtairKSV1IN8yXs0alaSDih+rXPPdSBSDHIotfB+zN2I2h4qPJhA==","signatures":[{"sig":"MEUCIQClhDDLNZJT9wtpZNaHInmWFsJKlTgv54FIydBpZZWbGwIgEyw2L1IiPreMDzlLYftN6rGwPmmlLKQU86zKqz56ZL0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQD0/5/1O5/4bje3NAlGm7EQhdgBQJK0ISyMXloigUqEPwIhAK4yY1PEXH9N+7jizL2rAZEJb57Zcdv6m0lSSaB9aLbh"}],"unpackedSize":76988},"main":"index.js","name":"@akalforge/pg-conformance","type":"module","types":"index.d.ts","engines":{"node":">=18"},"exports":{".":"./index.js","./corpus/*.json":"./corpus/*.json","./fingerprint.sql":"./fingerprint.sql"},"gitHead":"de08446ec1782b5fdb35114df9634c07ed7ecaa5","license":"MIT","scripts":{"test":"node --test test/*.test.js"},"version":"0.0.6","_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"homepage":"https://github.com/akalforge/pg-conformance#readme","keywords":["postgresql","postgres","schema","migration","diff","conformance","fingerprint"],"repository":{"url":"git+https://github.com/akalforge/pg-conformance.git","type":"git"},"_npmVersion":"10.9.8","description":"A PostgreSQL schema fingerprint and DDL conformance corpus, shared by tools that need to agree on whether two schemas are the same.","directories":{},"maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"_nodeVersion":"22.23.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pg-conformance_0.0.6_1790120286848_0.16488725608560828"}}},"time":{"created":"2026-08-25T19:47:34.511Z","modified":"2026-09-22T23:38:07.157Z","0.0.1":"2026-08-25T19:47:34.761Z","0.0.2":"2026-08-31T21:43:41.771Z","0.0.3":"2026-08-31T22:32:09.828Z","0.0.4":"2026-09-14T20:04:34.969Z","0.0.5":"2026-09-22T22:14:24.053Z","0.0.6":"2026-09-22T23:38:06.943Z"},"bugs":{"url":"https://github.com/akalforge/pg-conformance/issues"},"license":"MIT","homepage":"https://github.com/akalforge/pg-conformance#readme","keywords":["postgresql","postgres","schema","migration","diff","conformance","fingerprint"],"repository":{"url":"git+https://github.com/akalforge/pg-conformance.git","type":"git"},"description":"A PostgreSQL schema fingerprint and DDL conformance corpus, shared by tools that need to agree on whether two schemas are the same.","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"readme":"# @akalforge/pg-conformance\n\nA PostgreSQL schema **fingerprint** and a **DDL conformance corpus**, shared by tools that need to agree on whether two schemas are the same.\n\nData only. No runner, no database driver, no opinion about how you test.\n\n## Why this exists\n\nTwo tools that compare PostgreSQL schemas each had their own answer to \"are these the same?\". They diverged, quietly, and one of them ended up reporting these pairs as identical:\n\n| | one side | the other |\n| --- | --- | --- |\n| view | `SELECT n FROM t WHERE n > 0` | `... WHERE n < 0` |\n| function | `RETURNS int AS $$ SELECT 1 $$` | `... SELECT 999 $$` |\n| trigger | `AFTER INSERT ON t` | `BEFORE UPDATE ON t` |\n| identity | `GENERATED ALWAYS AS IDENTITY (INCREMENT 10 START 100)` | `GENERATED ALWAYS AS IDENTITY` |\n\nNone of those is exotic. Each was invisible because that fingerprint compared objects by *name* and read columns from `information_schema`, which cannot express identity options, generated columns, storage, compression or collation — and has nowhere to put a partition bound.\n\nThe fix is not a better fingerprint in each tool. It is one fingerprint.\n\n## Install\n\n```bash\nnpm install @akalforge/pg-conformance\n```\n\n**PHP consumers install the same npm package.** This is not published to\nPackagist, so `composer require` will not find it — the npm tarball ships\n`src/Conformance.php`, which is how DBDiff consumes it:\n\n```php\nrequire_once 'node_modules/@akalforge/pg-conformance/src/Conformance.php';\n```\n\nOne package means one version number for both languages, which matters more\nhere than idiomatic installation: a corpus whose whole job is to be the single\nshared answer to \"are these schemas the same?\" should not be publishable at two\ndifferent versions at once. `composer.json` is kept for its autoload map and for\nrequiring this from git if you need to.\n\n## Use\n\n```js\nimport { fingerprintSql, loadCorpus } from '@akalforge/pg-conformance'\n\nconst sql = fingerprintSql(['public'])\nconst a = await query(sourceDb, sql)\nconst b = await query(targetDb, sql)\nif (a !== b) { /* the schemas differ */ }\n\nfor (const testCase of loadCorpus('hard-cases')) {\n  // testCase.sql builds the objects; testCase.minPgVersion gates it\n}\n```\n\n```php\nuse Akal\\PgConformance\\Conformance;\n\n$sql = Conformance::fingerprintSql(['public']);\n$cases = Conformance::loadCorpus('hard-cases');\n```\n\nSchema names are quoted by the accessor, not by you — the query embeds them as SQL literals, so that is the one place an injection could enter. A name that is not a plain identifier is rejected rather than escaped.\n\nIf you shell out to `psql`, use `fingerprintSqlPath` and substitute `__SCHEMAS__` yourself.\n\n## Schema state\n\n`fingerprintSql()` answers *are these the same?*. `stateSql()` answers *what is there?* — the same catalog knowledge shaped as a JSON document, so a consumer can compute its own diff instead of trusting someone else's idea of what changed.\n\n```js\nimport { stateSql } from '@akalforge/pg-conformance'\n\nconst before = JSON.parse(await query(db, stateSql(['public'])))\n// ... apply a migration ...\nconst after  = JSON.parse(await query(db, stateSql(['public'])))\n```\n\n```json\n{\n  \"meta\": { \"server_version_num\": 170011, \"schemas\": [\"public\"] },\n  \"tables\": [{\n    \"schema\": \"public\", \"name\": \"orders\", \"kind\": \"partitioned_table\",\n    \"unlogged\": false, \"partition_by\": \"RANGE (created_at)\",\n    \"options\": [\"fillfactor=70\"], \"rls_enabled\": true,\n    \"columns\": [{\n      \"name\": \"id\", \"type\": \"bigint\", \"not_null\": true,\n      \"identity\": \"always\",\n      \"identity_options\": { \"start\": 100, \"increment\": 10, \"cycle\": false },\n      \"storage\": \"plain\", \"compression\": null, \"collation\": null\n    }],\n    \"constraints\": [...], \"indexes\": [...], \"policies\": [...], \"triggers\": [...]\n  }],\n  \"views\": [...], \"sequences\": [...], \"routines\": [...],\n  \"types\": [...], \"extensions\": [...]\n}\n```\n\nTwo conventions, both learned from getting them wrong:\n\n**Values are semantic, not catalog shorthand.** `attstorage` `'x'` is reported as `\"extended\"`, `attcompression` `'l'` as `\"lz4\"`, `attidentity` `'a'` as `\"always\"`. A consumer should not have to memorise single letters.\n\n**Inherited defaults are `null`, not spelled out.** A column that merely uses the database collation reports `null` rather than `\"default\"` — otherwise every text column in an unchanged schema reads as different.\n\nThe state document is verified to distinguish **every pair of corpus schemas the fingerprint distinguishes** — 4005 pairs, zero misses — so adopting it loses nothing the fingerprint already caught. It is also byte-stable: identical schemas produce identical documents.\n\nExisting schema APIs are not a substitute. `information_schema` cannot express a partition bound, identity sequence options, storage, compression or collation, and `postgres-meta` reads `relkind`/`relrowsecurity` but not `relpartbound`, `relpersistence` or `reloptions`, no identity options, and does not model sequences at all.\n\n## What the fingerprint covers\n\nRelations (kind, persistence, partition bound, storage options, RLS) · columns (type, nullability, default, identity, generated, storage, compression, collation) · sequence options · constraints (definition, validated, deferrable) · indexes, per relation · views and materialised views, **by body** · routines, **by body** · triggers, **by definition** · policies (command, roles, `USING`, `WITH CHECK`) · enums, domains and composite types · inheritance · comments.\n\nTwo principles decide the content:\n\n**Read the catalog, not `information_schema`.** The standard has no concept of most of what matters here.\n\n**Compare bodies, not names.** Where PostgreSQL can render an object canonically — `pg_get_viewdef`, `pg_get_constraintdef`, `pg_get_functiondef`, `pg_get_triggerdef` — that rendering is what gets compared, so both sides come from the same server code and formatting can never manufacture a difference.\n\nDefinitions are flattened to one line, because entries are newline-joined and a multi-line body would otherwise arrive as several unattributed entries.\n\n## The corpora\n\n| corpus | cases | what it is for |\n| --- | --- | --- |\n| `objects` | 20 | creating each object kind from an empty schema |\n| `hard-cases` | 90 | DDL that is awkward to reproduce — identity options, generated columns, exclusion constraints, partitioning of all three strategies and multi-level, inheritance, collations, storage and TOAST parameters, compression, every index method, interval and range types, domains over domains, function overloads, `INSTEAD OF` and constraint triggers, restrictive policies |\n| `ordering` | 12 | dependency ordering, with names chosen to defeat text matching |\n\n`ordering` cases give statements in an order that does **not** apply, plus the precedences any correct order must satisfy — a property rather than one expected permutation, so a sorter's tie-breaking can change without invalidating the case.\n\nCases carry `minPgVersion` where they need a particular server.\n\n## Requirements\n\nPostgreSQL 14 or newer. Tested against 14, 15, 16, 17 and 18 on every change.\n\n## Releases\n\nEvery merge to `main` publishes a patch automatically, so consumers track it without ceremony. `[minor]` or `[major]` in the merge commit subject, or a `release:minor` / `release:major` label on the PR, bumps further. `[skip release]` opts out.\n\nThis package is `0.x` while the fingerprint settles. From `0.1.0` onwards a\nrange behaves as you would expect — `^0.1.0` and `~0.1.0` both track the\n`0.1.x` line:\n\n```jsonc\n\"@akalforge/pg-conformance\": \"~0.1.0\"   // tracks 0.1.1, 0.1.2, ...\n```\n\nWhile the package was on `0.0.z` that was not true, and it is worth knowing if\nyou find an old pin: under semver a caret on a `0.0.z` version allows no updates\nat all, so `^0.0.1` was an exact pin that would never receive a release. Those\nranges wanted `~0.0.1`.\n\nA minor bump is how a breaking change is signalled on `0.x`, so a range pinned\nto one minor line will not cross it on its own. `0.1.0` renamed the PHP\nnamespace to `Akal\\PgConformance`; a consumer on `~0.0.x` keeps the old one\nuntil it widens the range deliberately.\n\nConsumers pin through their lockfile as usual; a bot bumps that lockfile, so `npm ci` stays reproducible and still moves.\n\n## Local development\n\n```bash\nnpm link                                  # in this repo\nnpm link @akalforge/pg-conformance        # in the consumer\n```\n\nRun the package's own tests against a real server:\n\n```bash\nPGURL=postgresql://user:pass@127.0.0.1:5432/postgres npm test\n```\n\nWithout `PGURL` the database half skips and only the accessors are checked. The database half is the half that matters: it asserts the fingerprint *discriminates*, since one that returned a constant would pass every consumer's suite while proving nothing.\n\n## Licence\n\nMIT\n","readmeFilename":"README.md"}