feat: npm package @crudy/pgmorbac ships the extension SQL
The package carries the install and upgrade scripts, built from src/ on prepack (sql/ is not committed), with extensionSql(), upgradeScripts(), version and sqlDir.
This commit is contained in:
@@ -20,3 +20,6 @@ Thumbs.db
|
|||||||
node_modules/
|
node_modules/
|
||||||
dist/
|
dist/
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
|
||||||
|
# npm package build output (scripts/build-package-sql.mjs)
|
||||||
|
/sql/
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
## [1.0.0] - 2026-07-25
|
## [1.0.0] - 2026-07-25
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- npm package `@crudy/pgmorbac`: the install and upgrade scripts with `extensionSql()`, `upgradeScripts()`, `version` and `sqlDir`
|
||||||
- First stable release of the Multi-OrBAC PostgreSQL extension
|
- First stable release of the Multi-OrBAC PostgreSQL extension
|
||||||
- Complete Multi-OrBAC implementation based on CNRS research paper
|
- Complete Multi-OrBAC implementation based on CNRS research paper
|
||||||
- Core features:
|
- Core features:
|
||||||
|
|||||||
@@ -77,6 +77,24 @@ sudo cp pgmorbac--1.0.0.sql $(pg_config --sharedir)/extension/
|
|||||||
psql -d mydb -c "CREATE EXTENSION pgmorbac;"
|
psql -d mydb -c "CREATE EXTENSION pgmorbac;"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### From Node.js (npm)
|
||||||
|
|
||||||
|
`@crudy/pgmorbac` ships the same install and upgrade scripts for projects that
|
||||||
|
apply SQL through a migration runner instead of `CREATE EXTENSION`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @crudy/pgmorbac
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { extensionSql, upgradeScripts, version, sqlDir } from '@crudy/pgmorbac';
|
||||||
|
|
||||||
|
await client.query(extensionSql()); // pgmorbac--<version>.sql
|
||||||
|
for (const u of upgradeScripts()) { // pgmorbac--<from>--<to>.sql, in order
|
||||||
|
await client.query(u.sql);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
|
|||||||
Vendored
+16
@@ -0,0 +1,16 @@
|
|||||||
|
/** Absolute path to pgmorbac.control. */
|
||||||
|
export declare const controlFile: string;
|
||||||
|
/** Absolute path to the directory holding the install and upgrade scripts. */
|
||||||
|
export declare const sqlDir: string;
|
||||||
|
/** The extension version (default_version in pgmorbac.control). */
|
||||||
|
export declare const version: string;
|
||||||
|
/** The install script pgmorbac--<version>.sql, as CREATE EXTENSION would run it. */
|
||||||
|
export declare function extensionSql(): string;
|
||||||
|
export interface UpgradeScript {
|
||||||
|
from: string;
|
||||||
|
to: string;
|
||||||
|
filename: string;
|
||||||
|
sql: string;
|
||||||
|
}
|
||||||
|
/** Upgrade scripts pgmorbac--<from>--<to>.sql, in version order. */
|
||||||
|
export declare function upgradeScripts(): UpgradeScript[];
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { readFileSync, readdirSync } from 'node:fs';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import { dirname, join } from 'node:path';
|
||||||
|
|
||||||
|
const root = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
|
export const controlFile = join(root, 'pgmorbac.control');
|
||||||
|
export const sqlDir = join(root, 'sql');
|
||||||
|
|
||||||
|
const control = readFileSync(controlFile, 'utf8').match(/default_version\s*=\s*'([^']+)'/);
|
||||||
|
if (!control) throw new Error('[pgmorbac] pgmorbac.control has no default_version');
|
||||||
|
export const version = control[1];
|
||||||
|
|
||||||
|
/** The install script pgmorbac--<version>.sql, as CREATE EXTENSION would run it. */
|
||||||
|
export function extensionSql() {
|
||||||
|
return readFileSync(join(sqlDir, `pgmorbac--${version}.sql`), 'utf8');
|
||||||
|
}
|
||||||
|
|
||||||
|
const semver = (v) => v.split('.').map(Number);
|
||||||
|
const byFrom = (a, b) => {
|
||||||
|
const [x, y] = [semver(a.from), semver(b.from)];
|
||||||
|
return x[0] - y[0] || x[1] - y[1] || x[2] - y[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Upgrade scripts pgmorbac--<from>--<to>.sql, in version order. */
|
||||||
|
export function upgradeScripts() {
|
||||||
|
return readdirSync(sqlDir)
|
||||||
|
.map((filename) => ({ filename, m: /^pgmorbac--(\d+\.\d+\.\d+)--(\d+\.\d+\.\d+)\.sql$/.exec(filename) }))
|
||||||
|
.filter(({ m }) => m)
|
||||||
|
.map(({ filename, m }) => ({ from: m[1], to: m[2], filename, sql: readFileSync(join(sqlDir, filename), 'utf8') }))
|
||||||
|
.sort(byFrom);
|
||||||
|
}
|
||||||
+48
-3
@@ -1,13 +1,58 @@
|
|||||||
{
|
{
|
||||||
"name": "pgmorbac-release-tools",
|
"name": "@crudy/pgmorbac",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Release tooling for the pgmorbac PostgreSQL extension (build + sign + publish the PGXN distribution).",
|
"description": "Multi-OrBAC for PostgreSQL: the pgmorbac extension SQL, packaged for Node.js projects and migration runners",
|
||||||
|
"keywords": [
|
||||||
|
"pgmorbac",
|
||||||
|
"morbac",
|
||||||
|
"orbac",
|
||||||
|
"rbac",
|
||||||
|
"authorization",
|
||||||
|
"access-control",
|
||||||
|
"postgresql",
|
||||||
|
"postgresql-extension",
|
||||||
|
"row-level-security",
|
||||||
|
"multi-tenant"
|
||||||
|
],
|
||||||
|
"author": "Marc Villain",
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://pgmorbac.crudy.fr",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/crudylabs/pgmorbac.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/crudylabs/pgmorbac/issues"
|
||||||
|
},
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./index.d.ts",
|
||||||
|
"default": "./index.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"main": "./index.js",
|
||||||
|
"types": "./index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"index.js",
|
||||||
|
"index.d.ts",
|
||||||
|
"sql",
|
||||||
|
"pgmorbac.control",
|
||||||
|
"README.md",
|
||||||
|
"CHANGELOG.md",
|
||||||
|
"LICENSE"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"prepack": "node scripts/build-package-sql.mjs",
|
||||||
"dist": "node scripts/build-dist.mjs",
|
"dist": "node scripts/build-dist.mjs",
|
||||||
"release": "node scripts/build-dist.mjs && node scripts/gitea-release.mjs"
|
"release": "node scripts/build-dist.mjs && node scripts/gitea-release.mjs"
|
||||||
},
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"jose": "^5"
|
"jose": "^5"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
// Builds sql/ for the npm package: the install script from src/ (tools/build.sh)
|
||||||
|
// plus any upgrade scripts. Runs on prepack; sql/ is never committed.
|
||||||
|
import { execFileSync } from 'node:child_process';
|
||||||
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync } from 'node:fs';
|
||||||
|
import { dirname, join } from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
const root = join(dirname(fileURLToPath(import.meta.url)), '..');
|
||||||
|
const control = readFileSync(join(root, 'pgmorbac.control'), 'utf8').match(/default_version\s*=\s*'([^']+)'/);
|
||||||
|
if (!control) throw new Error('pgmorbac.control has no default_version');
|
||||||
|
const version = control[1];
|
||||||
|
const pkg = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));
|
||||||
|
if (pkg.version !== version) throw new Error(`package.json ${pkg.version} != pgmorbac.control ${version}`);
|
||||||
|
|
||||||
|
const sqlDir = join(root, 'sql');
|
||||||
|
rmSync(sqlDir, { recursive: true, force: true });
|
||||||
|
mkdirSync(sqlDir);
|
||||||
|
execFileSync(join(root, 'tools', 'build.sh'), ['src', join(sqlDir, `pgmorbac--${version}.sql`)], { cwd: root, stdio: 'inherit' });
|
||||||
|
const upgrades = join(root, 'src', 'upgrades');
|
||||||
|
if (existsSync(upgrades)) {
|
||||||
|
for (const f of readdirSync(upgrades).filter((n) => n.endsWith('.sql'))) copyFileSync(join(upgrades, f), join(sqlDir, f));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user