graphile-plugin-utils 1.12.1 → 1.13.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/esm/index.js +1 -0
- package/esm/system-lane.js +40 -0
- package/index.d.ts +2 -0
- package/index.js +4 -1
- package/package.json +2 -2
- package/system-lane.d.ts +46 -0
- package/system-lane.js +44 -0
- package/__tests__/pg-resources.test.d.ts +0 -1
- package/__tests__/pg-resources.test.js +0 -30
- package/esm/__tests__/pg-resources.test.js +0 -28
package/esm/index.js
CHANGED
|
@@ -3,4 +3,5 @@
|
|
|
3
3
|
export { getQueryBuilder } from './query-builder';
|
|
4
4
|
// pg-resource introspection helpers (connection-filter / pg-aggregates)
|
|
5
5
|
export { isComputedScalarAttributeResource } from './pg-resources';
|
|
6
|
+
export { SYSTEM_LANE_ROLE, withSystemLaneClient } from './system-lane';
|
|
6
7
|
export { JSON_SCHEMA_SCALARS, PG_TYPE_SCALARS } from './scalar-maps';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The system lane: server-owned reads and writes that are not a tenant
|
|
3
|
+
* session — schema metadata, module registration, storage config.
|
|
4
|
+
*
|
|
5
|
+
* Grafast's `withPgClient(null, cb)` acquires a client with NO settings, so
|
|
6
|
+
* `cb` runs with whatever role the pool connected as (in practice `postgres`,
|
|
7
|
+
* a superuser that bypasses RLS) and each statement lands in its own implicit
|
|
8
|
+
* transaction. This helper names the role instead: one transaction with a
|
|
9
|
+
* transaction-local `role`, so the lane's reach is the grants of a named role
|
|
10
|
+
* rather than the connection's. It is the server-owned counterpart to the
|
|
11
|
+
* request lane's `withRequestPgClient`.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Role the system lane runs as: a named role instead of whatever the pool
|
|
15
|
+
* connected as.
|
|
16
|
+
*
|
|
17
|
+
* This is NOT an RLS-subject role. `ROLES.md` documents `administrator` as the
|
|
18
|
+
* BYPASSRLS role, so the lane still bypasses RLS — what changes is that it no
|
|
19
|
+
* longer inherits the connection's superuser and its work is one transaction.
|
|
20
|
+
* Narrowing it further needs grants that do not exist yet.
|
|
21
|
+
*/
|
|
22
|
+
export const SYSTEM_LANE_ROLE = 'administrator';
|
|
23
|
+
/**
|
|
24
|
+
* Run `cb` in one transaction under the system lane's role.
|
|
25
|
+
*
|
|
26
|
+
* The role is set with a transaction-local `set_config`, so it is discarded
|
|
27
|
+
* with the transaction and cannot leak to the next borrower of the pooled
|
|
28
|
+
* connection the way a session-level `SET ROLE` would. The client's own
|
|
29
|
+
* `withTransaction` owns commit and rollback.
|
|
30
|
+
*/
|
|
31
|
+
export function withSystemLaneClient(withPgClient, cb, opts = {}) {
|
|
32
|
+
const role = opts.role ?? SYSTEM_LANE_ROLE;
|
|
33
|
+
return withPgClient(null, (client) => client.withTransaction(async (tx) => {
|
|
34
|
+
await tx.query({
|
|
35
|
+
text: 'SELECT set_config($1, $2, true)',
|
|
36
|
+
values: ['role', role],
|
|
37
|
+
});
|
|
38
|
+
return cb(tx);
|
|
39
|
+
}));
|
|
40
|
+
}
|
package/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { getQueryBuilder } from './query-builder';
|
|
2
2
|
export { isComputedScalarAttributeResource } from './pg-resources';
|
|
3
|
+
export type { SystemLaneOptions, SystemLanePgClient, SystemLaneWithPgClient } from './system-lane';
|
|
4
|
+
export { SYSTEM_LANE_ROLE, withSystemLaneClient } from './system-lane';
|
|
3
5
|
export type { GraphQLScalarName } from './scalar-maps';
|
|
4
6
|
export { JSON_SCHEMA_SCALARS, PG_TYPE_SCALARS } from './scalar-maps';
|
package/index.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// Shared internals for Constructive PostGraphile v5 plugins.
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.PG_TYPE_SCALARS = exports.JSON_SCHEMA_SCALARS = exports.isComputedScalarAttributeResource = exports.getQueryBuilder = void 0;
|
|
4
|
+
exports.PG_TYPE_SCALARS = exports.JSON_SCHEMA_SCALARS = exports.withSystemLaneClient = exports.SYSTEM_LANE_ROLE = exports.isComputedScalarAttributeResource = exports.getQueryBuilder = void 0;
|
|
5
5
|
// PgCondition → PgSelectQueryBuilder walker (search / BM25 / pgvector)
|
|
6
6
|
var query_builder_1 = require("./query-builder");
|
|
7
7
|
Object.defineProperty(exports, "getQueryBuilder", { enumerable: true, get: function () { return query_builder_1.getQueryBuilder; } });
|
|
8
8
|
// pg-resource introspection helpers (connection-filter / pg-aggregates)
|
|
9
9
|
var pg_resources_1 = require("./pg-resources");
|
|
10
10
|
Object.defineProperty(exports, "isComputedScalarAttributeResource", { enumerable: true, get: function () { return pg_resources_1.isComputedScalarAttributeResource; } });
|
|
11
|
+
var system_lane_1 = require("./system-lane");
|
|
12
|
+
Object.defineProperty(exports, "SYSTEM_LANE_ROLE", { enumerable: true, get: function () { return system_lane_1.SYSTEM_LANE_ROLE; } });
|
|
13
|
+
Object.defineProperty(exports, "withSystemLaneClient", { enumerable: true, get: function () { return system_lane_1.withSystemLaneClient; } });
|
|
11
14
|
var scalar_maps_1 = require("./scalar-maps");
|
|
12
15
|
Object.defineProperty(exports, "JSON_SCHEMA_SCALARS", { enumerable: true, get: function () { return scalar_maps_1.JSON_SCHEMA_SCALARS; } });
|
|
13
16
|
Object.defineProperty(exports, "PG_TYPE_SCALARS", { enumerable: true, get: function () { return scalar_maps_1.PG_TYPE_SCALARS; } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "graphile-plugin-utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "Shared internals for Constructive PostGraphile v5 plugins - PgCondition query-builder walker and pg-resource introspection helpers",
|
|
5
5
|
"author": "Constructive <developers@constructive.io>",
|
|
6
6
|
"homepage": "https://github.com/constructive-io/constructive",
|
|
@@ -51,5 +51,5 @@
|
|
|
51
51
|
"pg-sql2": "^5.0.1",
|
|
52
52
|
"postgraphile": "^5.1.3"
|
|
53
53
|
},
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "75b888926505b473eeef7a76fea1554e5a1cf4cc"
|
|
55
55
|
}
|
package/system-lane.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The system lane: server-owned reads and writes that are not a tenant
|
|
3
|
+
* session — schema metadata, module registration, storage config.
|
|
4
|
+
*
|
|
5
|
+
* Grafast's `withPgClient(null, cb)` acquires a client with NO settings, so
|
|
6
|
+
* `cb` runs with whatever role the pool connected as (in practice `postgres`,
|
|
7
|
+
* a superuser that bypasses RLS) and each statement lands in its own implicit
|
|
8
|
+
* transaction. This helper names the role instead: one transaction with a
|
|
9
|
+
* transaction-local `role`, so the lane's reach is the grants of a named role
|
|
10
|
+
* rather than the connection's. It is the server-owned counterpart to the
|
|
11
|
+
* request lane's `withRequestPgClient`.
|
|
12
|
+
*/
|
|
13
|
+
/** The subset of grafast's PgClient the system lane needs. */
|
|
14
|
+
export interface SystemLanePgClient {
|
|
15
|
+
query(opts: {
|
|
16
|
+
text: string;
|
|
17
|
+
values?: unknown[];
|
|
18
|
+
}): Promise<{
|
|
19
|
+
rows: Array<Record<string, unknown>>;
|
|
20
|
+
}>;
|
|
21
|
+
withTransaction<T>(cb: (tx: SystemLanePgClient) => Promise<T>): Promise<T>;
|
|
22
|
+
}
|
|
23
|
+
export type SystemLaneWithPgClient = <T>(pgSettings: Record<string, string> | null, cb: (client: SystemLanePgClient) => Promise<T>) => Promise<T>;
|
|
24
|
+
/**
|
|
25
|
+
* Role the system lane runs as: a named role instead of whatever the pool
|
|
26
|
+
* connected as.
|
|
27
|
+
*
|
|
28
|
+
* This is NOT an RLS-subject role. `ROLES.md` documents `administrator` as the
|
|
29
|
+
* BYPASSRLS role, so the lane still bypasses RLS — what changes is that it no
|
|
30
|
+
* longer inherits the connection's superuser and its work is one transaction.
|
|
31
|
+
* Narrowing it further needs grants that do not exist yet.
|
|
32
|
+
*/
|
|
33
|
+
export declare const SYSTEM_LANE_ROLE = "administrator";
|
|
34
|
+
export interface SystemLaneOptions {
|
|
35
|
+
/** Role to run as. Defaults to {@link SYSTEM_LANE_ROLE}. */
|
|
36
|
+
role?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Run `cb` in one transaction under the system lane's role.
|
|
40
|
+
*
|
|
41
|
+
* The role is set with a transaction-local `set_config`, so it is discarded
|
|
42
|
+
* with the transaction and cannot leak to the next borrower of the pooled
|
|
43
|
+
* connection the way a session-level `SET ROLE` would. The client's own
|
|
44
|
+
* `withTransaction` owns commit and rollback.
|
|
45
|
+
*/
|
|
46
|
+
export declare function withSystemLaneClient<T>(withPgClient: SystemLaneWithPgClient, cb: (client: SystemLanePgClient) => Promise<T>, opts?: SystemLaneOptions): Promise<T>;
|
package/system-lane.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The system lane: server-owned reads and writes that are not a tenant
|
|
4
|
+
* session — schema metadata, module registration, storage config.
|
|
5
|
+
*
|
|
6
|
+
* Grafast's `withPgClient(null, cb)` acquires a client with NO settings, so
|
|
7
|
+
* `cb` runs with whatever role the pool connected as (in practice `postgres`,
|
|
8
|
+
* a superuser that bypasses RLS) and each statement lands in its own implicit
|
|
9
|
+
* transaction. This helper names the role instead: one transaction with a
|
|
10
|
+
* transaction-local `role`, so the lane's reach is the grants of a named role
|
|
11
|
+
* rather than the connection's. It is the server-owned counterpart to the
|
|
12
|
+
* request lane's `withRequestPgClient`.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.SYSTEM_LANE_ROLE = void 0;
|
|
16
|
+
exports.withSystemLaneClient = withSystemLaneClient;
|
|
17
|
+
/**
|
|
18
|
+
* Role the system lane runs as: a named role instead of whatever the pool
|
|
19
|
+
* connected as.
|
|
20
|
+
*
|
|
21
|
+
* This is NOT an RLS-subject role. `ROLES.md` documents `administrator` as the
|
|
22
|
+
* BYPASSRLS role, so the lane still bypasses RLS — what changes is that it no
|
|
23
|
+
* longer inherits the connection's superuser and its work is one transaction.
|
|
24
|
+
* Narrowing it further needs grants that do not exist yet.
|
|
25
|
+
*/
|
|
26
|
+
exports.SYSTEM_LANE_ROLE = 'administrator';
|
|
27
|
+
/**
|
|
28
|
+
* Run `cb` in one transaction under the system lane's role.
|
|
29
|
+
*
|
|
30
|
+
* The role is set with a transaction-local `set_config`, so it is discarded
|
|
31
|
+
* with the transaction and cannot leak to the next borrower of the pooled
|
|
32
|
+
* connection the way a session-level `SET ROLE` would. The client's own
|
|
33
|
+
* `withTransaction` owns commit and rollback.
|
|
34
|
+
*/
|
|
35
|
+
function withSystemLaneClient(withPgClient, cb, opts = {}) {
|
|
36
|
+
const role = opts.role ?? exports.SYSTEM_LANE_ROLE;
|
|
37
|
+
return withPgClient(null, (client) => client.withTransaction(async (tx) => {
|
|
38
|
+
await tx.query({
|
|
39
|
+
text: 'SELECT set_config($1, $2, true)',
|
|
40
|
+
values: ['role', role],
|
|
41
|
+
});
|
|
42
|
+
return cb(tx);
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const pg_resources_1 = require("../pg-resources");
|
|
4
|
-
const tableCodec = { attributes: { id: {} } };
|
|
5
|
-
const scalarCodec = {};
|
|
6
|
-
function resource(overrides) {
|
|
7
|
-
return {
|
|
8
|
-
isUnique: true,
|
|
9
|
-
codec: scalarCodec,
|
|
10
|
-
parameters: [{ codec: tableCodec }],
|
|
11
|
-
...overrides
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
describe('isComputedScalarAttributeResource', () => {
|
|
15
|
-
it('accepts a unique scalar function taking a table row', () => {
|
|
16
|
-
expect((0, pg_resources_1.isComputedScalarAttributeResource)(resource({}))).toBe(true);
|
|
17
|
-
});
|
|
18
|
-
it('rejects a resource without parameters', () => {
|
|
19
|
-
expect((0, pg_resources_1.isComputedScalarAttributeResource)(resource({ parameters: [] }))).toBe(false);
|
|
20
|
-
});
|
|
21
|
-
it('rejects a table-returning resource (codec has attributes)', () => {
|
|
22
|
-
expect((0, pg_resources_1.isComputedScalarAttributeResource)(resource({ codec: tableCodec }))).toBe(false);
|
|
23
|
-
});
|
|
24
|
-
it('rejects a non-unique resource', () => {
|
|
25
|
-
expect((0, pg_resources_1.isComputedScalarAttributeResource)(resource({ isUnique: false }))).toBe(false);
|
|
26
|
-
});
|
|
27
|
-
it('rejects when the first parameter is not a table row', () => {
|
|
28
|
-
expect((0, pg_resources_1.isComputedScalarAttributeResource)(resource({ parameters: [{ codec: scalarCodec }] }))).toBe(false);
|
|
29
|
-
});
|
|
30
|
-
});
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { isComputedScalarAttributeResource } from '../pg-resources';
|
|
2
|
-
const tableCodec = { attributes: { id: {} } };
|
|
3
|
-
const scalarCodec = {};
|
|
4
|
-
function resource(overrides) {
|
|
5
|
-
return {
|
|
6
|
-
isUnique: true,
|
|
7
|
-
codec: scalarCodec,
|
|
8
|
-
parameters: [{ codec: tableCodec }],
|
|
9
|
-
...overrides
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
describe('isComputedScalarAttributeResource', () => {
|
|
13
|
-
it('accepts a unique scalar function taking a table row', () => {
|
|
14
|
-
expect(isComputedScalarAttributeResource(resource({}))).toBe(true);
|
|
15
|
-
});
|
|
16
|
-
it('rejects a resource without parameters', () => {
|
|
17
|
-
expect(isComputedScalarAttributeResource(resource({ parameters: [] }))).toBe(false);
|
|
18
|
-
});
|
|
19
|
-
it('rejects a table-returning resource (codec has attributes)', () => {
|
|
20
|
-
expect(isComputedScalarAttributeResource(resource({ codec: tableCodec }))).toBe(false);
|
|
21
|
-
});
|
|
22
|
-
it('rejects a non-unique resource', () => {
|
|
23
|
-
expect(isComputedScalarAttributeResource(resource({ isUnique: false }))).toBe(false);
|
|
24
|
-
});
|
|
25
|
-
it('rejects when the first parameter is not a table row', () => {
|
|
26
|
-
expect(isComputedScalarAttributeResource(resource({ parameters: [{ codec: scalarCodec }] }))).toBe(false);
|
|
27
|
-
});
|
|
28
|
-
});
|