graphile-ltree 1.1.0 → 1.3.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.d.ts +5 -10
- package/esm/index.js +8 -11
- package/esm/plugins/ltree-codec.d.ts +23 -0
- package/esm/plugins/ltree-codec.js +70 -6
- package/esm/preset.d.ts +11 -15
- package/esm/preset.js +14 -30
- package/index.d.ts +5 -10
- package/index.js +12 -13
- package/package.json +5 -5
- package/plugins/ltree-codec.d.ts +23 -0
- package/plugins/ltree-codec.js +72 -6
- package/preset.d.ts +11 -15
- package/preset.js +14 -30
package/esm/index.d.ts
CHANGED
|
@@ -1,24 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* graphile-ltree — PostGraphile v5 ltree Plugin
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* -
|
|
7
|
-
* - `GraphileFolderPreset` — folder-oriented layer (within, ancestorOf, glob) + pathFolder fields
|
|
4
|
+
* File-path syntax for ltree columns. The LTree scalar auto-converts between
|
|
5
|
+
* slash-delimited file paths ("/projects/alpha/docs") in the GraphQL API and
|
|
6
|
+
* dot-delimited ltree ("projects.alpha.docs") in PostgreSQL.
|
|
8
7
|
*
|
|
9
8
|
* @example
|
|
10
9
|
* ```typescript
|
|
11
|
-
* // For folder-style interface (recommended):
|
|
12
|
-
* import { GraphileFolderPreset } from 'graphile-ltree';
|
|
13
|
-
*
|
|
14
|
-
* // For raw ltree operators only:
|
|
15
10
|
* import { GraphileLtreePreset } from 'graphile-ltree';
|
|
16
11
|
* ```
|
|
17
12
|
*/
|
|
18
13
|
export { GraphileFolderPreset, GraphileLtreePreset } from './preset';
|
|
19
14
|
export type { LtreeExtensionInfo } from './plugins/detect-ltree';
|
|
20
15
|
export { LtreeExtensionDetectionPlugin } from './plugins/detect-ltree';
|
|
16
|
+
export { ltreeToSlash, LTREE_SCALAR_NAME, LtreeCodecPlugin, slashToLtree } from './plugins/ltree-codec';
|
|
17
|
+
export { createFolderOperatorFactory } from './plugins/folder-filter-operators';
|
|
21
18
|
export { LtreeFolderFieldPlugin } from './plugins/folder-field';
|
|
22
|
-
export { LTREE_SCALAR_NAME, LtreeCodecPlugin } from './plugins/ltree-codec';
|
|
23
19
|
export { createLtreeOperatorFactory } from './plugins/connection-filter-operators';
|
|
24
|
-
export { createFolderOperatorFactory } from './plugins/folder-filter-operators';
|
package/esm/index.js
CHANGED
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* graphile-ltree — PostGraphile v5 ltree Plugin
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* -
|
|
7
|
-
* - `GraphileFolderPreset` — folder-oriented layer (within, ancestorOf, glob) + pathFolder fields
|
|
4
|
+
* File-path syntax for ltree columns. The LTree scalar auto-converts between
|
|
5
|
+
* slash-delimited file paths ("/projects/alpha/docs") in the GraphQL API and
|
|
6
|
+
* dot-delimited ltree ("projects.alpha.docs") in PostgreSQL.
|
|
8
7
|
*
|
|
9
8
|
* @example
|
|
10
9
|
* ```typescript
|
|
11
|
-
* // For folder-style interface (recommended):
|
|
12
|
-
* import { GraphileFolderPreset } from 'graphile-ltree';
|
|
13
|
-
*
|
|
14
|
-
* // For raw ltree operators only:
|
|
15
10
|
* import { GraphileLtreePreset } from 'graphile-ltree';
|
|
16
11
|
* ```
|
|
17
12
|
*/
|
|
18
13
|
// Presets
|
|
19
14
|
export { GraphileFolderPreset, GraphileLtreePreset } from './preset';
|
|
20
15
|
export { LtreeExtensionDetectionPlugin } from './plugins/detect-ltree';
|
|
21
|
-
export {
|
|
22
|
-
export { LTREE_SCALAR_NAME, LtreeCodecPlugin } from './plugins/ltree-codec';
|
|
16
|
+
export { ltreeToSlash, LTREE_SCALAR_NAME, LtreeCodecPlugin, slashToLtree } from './plugins/ltree-codec';
|
|
23
17
|
// Connection filter operator factories
|
|
24
|
-
export { createLtreeOperatorFactory } from './plugins/connection-filter-operators';
|
|
25
18
|
export { createFolderOperatorFactory } from './plugins/folder-filter-operators';
|
|
19
|
+
// Deprecated — folder field plugin is no longer needed (scalar handles conversion)
|
|
20
|
+
export { LtreeFolderFieldPlugin } from './plugins/folder-field';
|
|
21
|
+
// Deprecated — use createFolderOperatorFactory instead (better operator names)
|
|
22
|
+
export { createLtreeOperatorFactory } from './plugins/connection-filter-operators';
|
|
@@ -7,10 +7,33 @@
|
|
|
7
7
|
* This plugin:
|
|
8
8
|
* 1. Falls back to registering codecs if PostGraphile's native handling misses them
|
|
9
9
|
* 2. Re-enables ltree columns for select/filterBy (overrides rc.8 HIDE_BY_DEFAULT)
|
|
10
|
+
* 3. Converts between slash-delimited file paths (external API) and dot-delimited
|
|
11
|
+
* ltree (internal storage) at the GraphQL boundary via the codec's fromPg/toPg.
|
|
12
|
+
*
|
|
13
|
+
* External API: "/projects/alpha/docs" (slash-delimited file paths)
|
|
14
|
+
* Internal DB: "projects.alpha.docs" (ltree native format)
|
|
15
|
+
*
|
|
16
|
+
* The conversion happens at the codec level (fromPg/toPg) so it works regardless
|
|
17
|
+
* of whether PostGraphile registers its own LTree scalar or we register ours.
|
|
10
18
|
*
|
|
11
19
|
* The native scalar name is "LTree" (capital T). All operators and downstream
|
|
12
20
|
* plugins should reference LTREE_SCALAR_NAME for consistency.
|
|
13
21
|
*/
|
|
14
22
|
import type { GraphileConfig } from 'graphile-config';
|
|
15
23
|
export declare const LTREE_SCALAR_NAME = "LTree";
|
|
24
|
+
/**
|
|
25
|
+
* Convert a slash-delimited file path to ltree format.
|
|
26
|
+
* Idempotent on ltree values (no slashes → no change).
|
|
27
|
+
*
|
|
28
|
+
* "/projects/alpha/docs" → "projects.alpha.docs"
|
|
29
|
+
* "projects.alpha.docs" → "projects.alpha.docs" (no-op)
|
|
30
|
+
*/
|
|
31
|
+
export declare function slashToLtree(value: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Convert an ltree value to a slash-delimited file path.
|
|
34
|
+
*
|
|
35
|
+
* "projects.alpha.docs" → "/projects/alpha/docs"
|
|
36
|
+
* "" → "/"
|
|
37
|
+
*/
|
|
38
|
+
export declare function ltreeToSlash(value: string): string;
|
|
16
39
|
export declare const LtreeCodecPlugin: GraphileConfig.Plugin;
|
|
@@ -7,6 +7,14 @@
|
|
|
7
7
|
* This plugin:
|
|
8
8
|
* 1. Falls back to registering codecs if PostGraphile's native handling misses them
|
|
9
9
|
* 2. Re-enables ltree columns for select/filterBy (overrides rc.8 HIDE_BY_DEFAULT)
|
|
10
|
+
* 3. Converts between slash-delimited file paths (external API) and dot-delimited
|
|
11
|
+
* ltree (internal storage) at the GraphQL boundary via the codec's fromPg/toPg.
|
|
12
|
+
*
|
|
13
|
+
* External API: "/projects/alpha/docs" (slash-delimited file paths)
|
|
14
|
+
* Internal DB: "projects.alpha.docs" (ltree native format)
|
|
15
|
+
*
|
|
16
|
+
* The conversion happens at the codec level (fromPg/toPg) so it works regardless
|
|
17
|
+
* of whether PostGraphile registers its own LTree scalar or we register ours.
|
|
10
18
|
*
|
|
11
19
|
* The native scalar name is "LTree" (capital T). All operators and downstream
|
|
12
20
|
* plugins should reference LTREE_SCALAR_NAME for consistency.
|
|
@@ -14,10 +22,31 @@
|
|
|
14
22
|
import { GraphQLString } from 'graphql';
|
|
15
23
|
import sql from 'pg-sql2';
|
|
16
24
|
export const LTREE_SCALAR_NAME = 'LTree';
|
|
25
|
+
/**
|
|
26
|
+
* Convert a slash-delimited file path to ltree format.
|
|
27
|
+
* Idempotent on ltree values (no slashes → no change).
|
|
28
|
+
*
|
|
29
|
+
* "/projects/alpha/docs" → "projects.alpha.docs"
|
|
30
|
+
* "projects.alpha.docs" → "projects.alpha.docs" (no-op)
|
|
31
|
+
*/
|
|
32
|
+
export function slashToLtree(value) {
|
|
33
|
+
return value.replace(/^\//, '').replace(/\//g, '.');
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Convert an ltree value to a slash-delimited file path.
|
|
37
|
+
*
|
|
38
|
+
* "projects.alpha.docs" → "/projects/alpha/docs"
|
|
39
|
+
* "" → "/"
|
|
40
|
+
*/
|
|
41
|
+
export function ltreeToSlash(value) {
|
|
42
|
+
if (value === '')
|
|
43
|
+
return '/';
|
|
44
|
+
return '/' + value.replace(/\./g, '/');
|
|
45
|
+
}
|
|
17
46
|
export const LtreeCodecPlugin = {
|
|
18
47
|
name: 'LtreeCodecPlugin',
|
|
19
|
-
version: '
|
|
20
|
-
description: 'Ensures ltree/lquery codecs are
|
|
48
|
+
version: '2.0.0',
|
|
49
|
+
description: 'Ensures ltree/lquery codecs are registered; converts between file paths and ltree at the GraphQL boundary',
|
|
21
50
|
gather: {
|
|
22
51
|
hooks: {
|
|
23
52
|
async pgCodecs_findPgCodec(info, event) {
|
|
@@ -52,6 +81,37 @@ export const LtreeCodecPlugin = {
|
|
|
52
81
|
},
|
|
53
82
|
schema: {
|
|
54
83
|
hooks: {
|
|
84
|
+
/**
|
|
85
|
+
* Patch the ltree codec's fromPg/toPg to convert between file paths
|
|
86
|
+
* and ltree. This runs in the `build` hook (before type registration)
|
|
87
|
+
* so it works regardless of whether PostGraphile registers its own
|
|
88
|
+
* LTree scalar or we register ours.
|
|
89
|
+
*
|
|
90
|
+
* fromPg: "projects.alpha.docs" → "/projects/alpha/docs"
|
|
91
|
+
* toPg: "/projects/alpha/docs" → "projects.alpha.docs"
|
|
92
|
+
*/
|
|
93
|
+
build(build) {
|
|
94
|
+
for (const codec of Object.values(build.input.pgRegistry.pgCodecs)) {
|
|
95
|
+
if (codec.name === 'ltree') {
|
|
96
|
+
const c = codec;
|
|
97
|
+
const origFromPg = c.fromPg;
|
|
98
|
+
c.fromPg = (value) => {
|
|
99
|
+
const raw = origFromPg ? origFromPg(value) : value;
|
|
100
|
+
if (raw == null)
|
|
101
|
+
return raw;
|
|
102
|
+
return ltreeToSlash(String(raw));
|
|
103
|
+
};
|
|
104
|
+
const origToPg = c.toPg;
|
|
105
|
+
c.toPg = (value) => {
|
|
106
|
+
if (value == null)
|
|
107
|
+
return origToPg ? origToPg(value) : value;
|
|
108
|
+
const converted = slashToLtree(String(value));
|
|
109
|
+
return origToPg ? origToPg(converted) : converted;
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return build;
|
|
114
|
+
},
|
|
55
115
|
init: {
|
|
56
116
|
before: ['PgCodecs', 'PgConnectionArgFilterPlugin'],
|
|
57
117
|
callback(_, build) {
|
|
@@ -60,14 +120,18 @@ export const LtreeCodecPlugin = {
|
|
|
60
120
|
if (codec.name === 'ltree') {
|
|
61
121
|
if (!hasGraphQLTypeForPgCodec(codec, 'input')) {
|
|
62
122
|
build.registerScalarType(LTREE_SCALAR_NAME, {}, () => ({
|
|
63
|
-
description: 'A
|
|
123
|
+
description: 'A hierarchical file path. Accepts slash-delimited paths (e.g. "/projects/alpha/docs"). ' +
|
|
124
|
+
'Stored internally as PostgreSQL ltree.',
|
|
64
125
|
serialize(value) {
|
|
126
|
+
if (value == null)
|
|
127
|
+
return null;
|
|
65
128
|
return String(value);
|
|
66
129
|
},
|
|
67
130
|
parseValue(value) {
|
|
68
|
-
if (typeof value
|
|
69
|
-
|
|
70
|
-
|
|
131
|
+
if (typeof value !== 'string') {
|
|
132
|
+
throw new Error(`${LTREE_SCALAR_NAME} must be a string`);
|
|
133
|
+
}
|
|
134
|
+
return value;
|
|
71
135
|
},
|
|
72
136
|
parseLiteral(lit) {
|
|
73
137
|
if (lit.kind === 'NullValue')
|
package/esm/preset.d.ts
CHANGED
|
@@ -2,27 +2,23 @@ import type { GraphileConfig } from 'graphile-config';
|
|
|
2
2
|
/**
|
|
3
3
|
* GraphileLtreePreset
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* Full ltree support with file-path syntax.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
|
|
10
|
-
export declare const GraphileLtreePreset: GraphileConfig.Preset;
|
|
11
|
-
/**
|
|
12
|
-
* GraphileFolderPreset
|
|
13
|
-
*
|
|
14
|
-
* Folder-oriented layer on top of the base ltree preset.
|
|
15
|
-
*
|
|
16
|
-
* Adds:
|
|
17
|
-
* - Virtual `{column}Folder` fields with slash-delimited paths
|
|
18
|
-
* - Folder operators: within, ancestorOf, glob (always slash-delimited)
|
|
7
|
+
* - LTree scalar auto-converts between slash-delimited file paths (API) and
|
|
8
|
+
* dot-delimited ltree (DB). External API always uses "/projects/alpha/docs".
|
|
9
|
+
* - Filter operators: within, ancestorOf, glob (all accept slash-delimited paths)
|
|
19
10
|
*
|
|
20
11
|
* @example
|
|
21
12
|
* ```graphql
|
|
22
13
|
* allFiles(where: { path: { within: "/projects/alpha" } }) {
|
|
23
|
-
* nodes {
|
|
14
|
+
* nodes { path filename }
|
|
24
15
|
* }
|
|
25
16
|
* ```
|
|
26
17
|
*/
|
|
18
|
+
export declare const GraphileLtreePreset: GraphileConfig.Preset;
|
|
19
|
+
/**
|
|
20
|
+
* @deprecated Use GraphileLtreePreset instead. The folder field plugin is no
|
|
21
|
+
* longer needed — the LTree scalar now handles slash↔ltree conversion directly.
|
|
22
|
+
*/
|
|
27
23
|
export declare const GraphileFolderPreset: GraphileConfig.Preset;
|
|
28
|
-
export default
|
|
24
|
+
export default GraphileLtreePreset;
|
package/esm/preset.js
CHANGED
|
@@ -1,47 +1,26 @@
|
|
|
1
|
-
import { createLtreeOperatorFactory } from './plugins/connection-filter-operators';
|
|
2
1
|
import { LtreeExtensionDetectionPlugin } from './plugins/detect-ltree';
|
|
3
|
-
import { LtreeFolderFieldPlugin } from './plugins/folder-field';
|
|
4
2
|
import { createFolderOperatorFactory } from './plugins/folder-filter-operators';
|
|
5
3
|
import { LtreeCodecPlugin } from './plugins/ltree-codec';
|
|
6
4
|
/**
|
|
7
5
|
* GraphileLtreePreset
|
|
8
6
|
*
|
|
9
|
-
*
|
|
7
|
+
* Full ltree support with file-path syntax.
|
|
10
8
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
|
|
14
|
-
export const GraphileLtreePreset = {
|
|
15
|
-
plugins: [
|
|
16
|
-
LtreeExtensionDetectionPlugin,
|
|
17
|
-
LtreeCodecPlugin
|
|
18
|
-
],
|
|
19
|
-
schema: {
|
|
20
|
-
connectionFilterOperatorFactories: [
|
|
21
|
-
createLtreeOperatorFactory()
|
|
22
|
-
]
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
/**
|
|
26
|
-
* GraphileFolderPreset
|
|
27
|
-
*
|
|
28
|
-
* Folder-oriented layer on top of the base ltree preset.
|
|
29
|
-
*
|
|
30
|
-
* Adds:
|
|
31
|
-
* - Virtual `{column}Folder` fields with slash-delimited paths
|
|
32
|
-
* - Folder operators: within, ancestorOf, glob (always slash-delimited)
|
|
9
|
+
* - LTree scalar auto-converts between slash-delimited file paths (API) and
|
|
10
|
+
* dot-delimited ltree (DB). External API always uses "/projects/alpha/docs".
|
|
11
|
+
* - Filter operators: within, ancestorOf, glob (all accept slash-delimited paths)
|
|
33
12
|
*
|
|
34
13
|
* @example
|
|
35
14
|
* ```graphql
|
|
36
15
|
* allFiles(where: { path: { within: "/projects/alpha" } }) {
|
|
37
|
-
* nodes {
|
|
16
|
+
* nodes { path filename }
|
|
38
17
|
* }
|
|
39
18
|
* ```
|
|
40
19
|
*/
|
|
41
|
-
export const
|
|
42
|
-
extends: [GraphileLtreePreset],
|
|
20
|
+
export const GraphileLtreePreset = {
|
|
43
21
|
plugins: [
|
|
44
|
-
|
|
22
|
+
LtreeExtensionDetectionPlugin,
|
|
23
|
+
LtreeCodecPlugin
|
|
45
24
|
],
|
|
46
25
|
schema: {
|
|
47
26
|
connectionFilterOperatorFactories: [
|
|
@@ -49,4 +28,9 @@ export const GraphileFolderPreset = {
|
|
|
49
28
|
]
|
|
50
29
|
}
|
|
51
30
|
};
|
|
52
|
-
|
|
31
|
+
/**
|
|
32
|
+
* @deprecated Use GraphileLtreePreset instead. The folder field plugin is no
|
|
33
|
+
* longer needed — the LTree scalar now handles slash↔ltree conversion directly.
|
|
34
|
+
*/
|
|
35
|
+
export const GraphileFolderPreset = GraphileLtreePreset;
|
|
36
|
+
export default GraphileLtreePreset;
|
package/index.d.ts
CHANGED
|
@@ -1,24 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* graphile-ltree — PostGraphile v5 ltree Plugin
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* -
|
|
7
|
-
* - `GraphileFolderPreset` — folder-oriented layer (within, ancestorOf, glob) + pathFolder fields
|
|
4
|
+
* File-path syntax for ltree columns. The LTree scalar auto-converts between
|
|
5
|
+
* slash-delimited file paths ("/projects/alpha/docs") in the GraphQL API and
|
|
6
|
+
* dot-delimited ltree ("projects.alpha.docs") in PostgreSQL.
|
|
8
7
|
*
|
|
9
8
|
* @example
|
|
10
9
|
* ```typescript
|
|
11
|
-
* // For folder-style interface (recommended):
|
|
12
|
-
* import { GraphileFolderPreset } from 'graphile-ltree';
|
|
13
|
-
*
|
|
14
|
-
* // For raw ltree operators only:
|
|
15
10
|
* import { GraphileLtreePreset } from 'graphile-ltree';
|
|
16
11
|
* ```
|
|
17
12
|
*/
|
|
18
13
|
export { GraphileFolderPreset, GraphileLtreePreset } from './preset';
|
|
19
14
|
export type { LtreeExtensionInfo } from './plugins/detect-ltree';
|
|
20
15
|
export { LtreeExtensionDetectionPlugin } from './plugins/detect-ltree';
|
|
16
|
+
export { ltreeToSlash, LTREE_SCALAR_NAME, LtreeCodecPlugin, slashToLtree } from './plugins/ltree-codec';
|
|
17
|
+
export { createFolderOperatorFactory } from './plugins/folder-filter-operators';
|
|
21
18
|
export { LtreeFolderFieldPlugin } from './plugins/folder-field';
|
|
22
|
-
export { LTREE_SCALAR_NAME, LtreeCodecPlugin } from './plugins/ltree-codec';
|
|
23
19
|
export { createLtreeOperatorFactory } from './plugins/connection-filter-operators';
|
|
24
|
-
export { createFolderOperatorFactory } from './plugins/folder-filter-operators';
|
package/index.js
CHANGED
|
@@ -2,35 +2,34 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* graphile-ltree — PostGraphile v5 ltree Plugin
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* -
|
|
8
|
-
* - `GraphileFolderPreset` — folder-oriented layer (within, ancestorOf, glob) + pathFolder fields
|
|
5
|
+
* File-path syntax for ltree columns. The LTree scalar auto-converts between
|
|
6
|
+
* slash-delimited file paths ("/projects/alpha/docs") in the GraphQL API and
|
|
7
|
+
* dot-delimited ltree ("projects.alpha.docs") in PostgreSQL.
|
|
9
8
|
*
|
|
10
9
|
* @example
|
|
11
10
|
* ```typescript
|
|
12
|
-
* // For folder-style interface (recommended):
|
|
13
|
-
* import { GraphileFolderPreset } from 'graphile-ltree';
|
|
14
|
-
*
|
|
15
|
-
* // For raw ltree operators only:
|
|
16
11
|
* import { GraphileLtreePreset } from 'graphile-ltree';
|
|
17
12
|
* ```
|
|
18
13
|
*/
|
|
19
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.createFolderOperatorFactory = exports.
|
|
15
|
+
exports.createLtreeOperatorFactory = exports.LtreeFolderFieldPlugin = exports.createFolderOperatorFactory = exports.slashToLtree = exports.LtreeCodecPlugin = exports.LTREE_SCALAR_NAME = exports.ltreeToSlash = exports.LtreeExtensionDetectionPlugin = exports.GraphileLtreePreset = exports.GraphileFolderPreset = void 0;
|
|
21
16
|
// Presets
|
|
22
17
|
var preset_1 = require("./preset");
|
|
23
18
|
Object.defineProperty(exports, "GraphileFolderPreset", { enumerable: true, get: function () { return preset_1.GraphileFolderPreset; } });
|
|
24
19
|
Object.defineProperty(exports, "GraphileLtreePreset", { enumerable: true, get: function () { return preset_1.GraphileLtreePreset; } });
|
|
25
20
|
var detect_ltree_1 = require("./plugins/detect-ltree");
|
|
26
21
|
Object.defineProperty(exports, "LtreeExtensionDetectionPlugin", { enumerable: true, get: function () { return detect_ltree_1.LtreeExtensionDetectionPlugin; } });
|
|
27
|
-
var folder_field_1 = require("./plugins/folder-field");
|
|
28
|
-
Object.defineProperty(exports, "LtreeFolderFieldPlugin", { enumerable: true, get: function () { return folder_field_1.LtreeFolderFieldPlugin; } });
|
|
29
22
|
var ltree_codec_1 = require("./plugins/ltree-codec");
|
|
23
|
+
Object.defineProperty(exports, "ltreeToSlash", { enumerable: true, get: function () { return ltree_codec_1.ltreeToSlash; } });
|
|
30
24
|
Object.defineProperty(exports, "LTREE_SCALAR_NAME", { enumerable: true, get: function () { return ltree_codec_1.LTREE_SCALAR_NAME; } });
|
|
31
25
|
Object.defineProperty(exports, "LtreeCodecPlugin", { enumerable: true, get: function () { return ltree_codec_1.LtreeCodecPlugin; } });
|
|
26
|
+
Object.defineProperty(exports, "slashToLtree", { enumerable: true, get: function () { return ltree_codec_1.slashToLtree; } });
|
|
32
27
|
// Connection filter operator factories
|
|
33
|
-
var connection_filter_operators_1 = require("./plugins/connection-filter-operators");
|
|
34
|
-
Object.defineProperty(exports, "createLtreeOperatorFactory", { enumerable: true, get: function () { return connection_filter_operators_1.createLtreeOperatorFactory; } });
|
|
35
28
|
var folder_filter_operators_1 = require("./plugins/folder-filter-operators");
|
|
36
29
|
Object.defineProperty(exports, "createFolderOperatorFactory", { enumerable: true, get: function () { return folder_filter_operators_1.createFolderOperatorFactory; } });
|
|
30
|
+
// Deprecated — folder field plugin is no longer needed (scalar handles conversion)
|
|
31
|
+
var folder_field_1 = require("./plugins/folder-field");
|
|
32
|
+
Object.defineProperty(exports, "LtreeFolderFieldPlugin", { enumerable: true, get: function () { return folder_field_1.LtreeFolderFieldPlugin; } });
|
|
33
|
+
// Deprecated — use createFolderOperatorFactory instead (better operator names)
|
|
34
|
+
var connection_filter_operators_1 = require("./plugins/connection-filter-operators");
|
|
35
|
+
Object.defineProperty(exports, "createLtreeOperatorFactory", { enumerable: true, get: function () { return connection_filter_operators_1.createLtreeOperatorFactory; } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "graphile-ltree",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "PostGraphile v5 ltree plugin — auto-detects ltree columns, exposes slash-path folder fields, and provides containment/glob filter operators",
|
|
5
5
|
"author": "Constructive <developers@constructive.io>",
|
|
6
6
|
"homepage": "https://github.com/constructive-io/constructive",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"graphile-build": "5.0.0",
|
|
35
35
|
"graphile-build-pg": "5.0.0",
|
|
36
36
|
"graphile-config": "1.0.0",
|
|
37
|
-
"graphile-connection-filter": "^1.
|
|
37
|
+
"graphile-connection-filter": "^1.6.0",
|
|
38
38
|
"graphql": "16.13.0",
|
|
39
39
|
"pg-sql2": "5.0.0",
|
|
40
40
|
"postgraphile": "5.0.0"
|
|
@@ -46,9 +46,9 @@
|
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/node": "^22.19.11",
|
|
49
|
-
"graphile-test": "^4.
|
|
49
|
+
"graphile-test": "^4.10.0",
|
|
50
50
|
"makage": "^0.3.0",
|
|
51
|
-
"pgsql-test": "^4.
|
|
51
|
+
"pgsql-test": "^4.10.0"
|
|
52
52
|
},
|
|
53
53
|
"keywords": [
|
|
54
54
|
"postgraphile",
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
"folder",
|
|
64
64
|
"tree"
|
|
65
65
|
],
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "7409479a981ff63a0937b5406a1c206a07b264ad"
|
|
67
67
|
}
|
package/plugins/ltree-codec.d.ts
CHANGED
|
@@ -7,10 +7,33 @@
|
|
|
7
7
|
* This plugin:
|
|
8
8
|
* 1. Falls back to registering codecs if PostGraphile's native handling misses them
|
|
9
9
|
* 2. Re-enables ltree columns for select/filterBy (overrides rc.8 HIDE_BY_DEFAULT)
|
|
10
|
+
* 3. Converts between slash-delimited file paths (external API) and dot-delimited
|
|
11
|
+
* ltree (internal storage) at the GraphQL boundary via the codec's fromPg/toPg.
|
|
12
|
+
*
|
|
13
|
+
* External API: "/projects/alpha/docs" (slash-delimited file paths)
|
|
14
|
+
* Internal DB: "projects.alpha.docs" (ltree native format)
|
|
15
|
+
*
|
|
16
|
+
* The conversion happens at the codec level (fromPg/toPg) so it works regardless
|
|
17
|
+
* of whether PostGraphile registers its own LTree scalar or we register ours.
|
|
10
18
|
*
|
|
11
19
|
* The native scalar name is "LTree" (capital T). All operators and downstream
|
|
12
20
|
* plugins should reference LTREE_SCALAR_NAME for consistency.
|
|
13
21
|
*/
|
|
14
22
|
import type { GraphileConfig } from 'graphile-config';
|
|
15
23
|
export declare const LTREE_SCALAR_NAME = "LTree";
|
|
24
|
+
/**
|
|
25
|
+
* Convert a slash-delimited file path to ltree format.
|
|
26
|
+
* Idempotent on ltree values (no slashes → no change).
|
|
27
|
+
*
|
|
28
|
+
* "/projects/alpha/docs" → "projects.alpha.docs"
|
|
29
|
+
* "projects.alpha.docs" → "projects.alpha.docs" (no-op)
|
|
30
|
+
*/
|
|
31
|
+
export declare function slashToLtree(value: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Convert an ltree value to a slash-delimited file path.
|
|
34
|
+
*
|
|
35
|
+
* "projects.alpha.docs" → "/projects/alpha/docs"
|
|
36
|
+
* "" → "/"
|
|
37
|
+
*/
|
|
38
|
+
export declare function ltreeToSlash(value: string): string;
|
|
16
39
|
export declare const LtreeCodecPlugin: GraphileConfig.Plugin;
|
package/plugins/ltree-codec.js
CHANGED
|
@@ -8,6 +8,14 @@
|
|
|
8
8
|
* This plugin:
|
|
9
9
|
* 1. Falls back to registering codecs if PostGraphile's native handling misses them
|
|
10
10
|
* 2. Re-enables ltree columns for select/filterBy (overrides rc.8 HIDE_BY_DEFAULT)
|
|
11
|
+
* 3. Converts between slash-delimited file paths (external API) and dot-delimited
|
|
12
|
+
* ltree (internal storage) at the GraphQL boundary via the codec's fromPg/toPg.
|
|
13
|
+
*
|
|
14
|
+
* External API: "/projects/alpha/docs" (slash-delimited file paths)
|
|
15
|
+
* Internal DB: "projects.alpha.docs" (ltree native format)
|
|
16
|
+
*
|
|
17
|
+
* The conversion happens at the codec level (fromPg/toPg) so it works regardless
|
|
18
|
+
* of whether PostGraphile registers its own LTree scalar or we register ours.
|
|
11
19
|
*
|
|
12
20
|
* The native scalar name is "LTree" (capital T). All operators and downstream
|
|
13
21
|
* plugins should reference LTREE_SCALAR_NAME for consistency.
|
|
@@ -17,13 +25,36 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
17
25
|
};
|
|
18
26
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
27
|
exports.LtreeCodecPlugin = exports.LTREE_SCALAR_NAME = void 0;
|
|
28
|
+
exports.slashToLtree = slashToLtree;
|
|
29
|
+
exports.ltreeToSlash = ltreeToSlash;
|
|
20
30
|
const graphql_1 = require("graphql");
|
|
21
31
|
const pg_sql2_1 = __importDefault(require("pg-sql2"));
|
|
22
32
|
exports.LTREE_SCALAR_NAME = 'LTree';
|
|
33
|
+
/**
|
|
34
|
+
* Convert a slash-delimited file path to ltree format.
|
|
35
|
+
* Idempotent on ltree values (no slashes → no change).
|
|
36
|
+
*
|
|
37
|
+
* "/projects/alpha/docs" → "projects.alpha.docs"
|
|
38
|
+
* "projects.alpha.docs" → "projects.alpha.docs" (no-op)
|
|
39
|
+
*/
|
|
40
|
+
function slashToLtree(value) {
|
|
41
|
+
return value.replace(/^\//, '').replace(/\//g, '.');
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Convert an ltree value to a slash-delimited file path.
|
|
45
|
+
*
|
|
46
|
+
* "projects.alpha.docs" → "/projects/alpha/docs"
|
|
47
|
+
* "" → "/"
|
|
48
|
+
*/
|
|
49
|
+
function ltreeToSlash(value) {
|
|
50
|
+
if (value === '')
|
|
51
|
+
return '/';
|
|
52
|
+
return '/' + value.replace(/\./g, '/');
|
|
53
|
+
}
|
|
23
54
|
exports.LtreeCodecPlugin = {
|
|
24
55
|
name: 'LtreeCodecPlugin',
|
|
25
|
-
version: '
|
|
26
|
-
description: 'Ensures ltree/lquery codecs are
|
|
56
|
+
version: '2.0.0',
|
|
57
|
+
description: 'Ensures ltree/lquery codecs are registered; converts between file paths and ltree at the GraphQL boundary',
|
|
27
58
|
gather: {
|
|
28
59
|
hooks: {
|
|
29
60
|
async pgCodecs_findPgCodec(info, event) {
|
|
@@ -58,6 +89,37 @@ exports.LtreeCodecPlugin = {
|
|
|
58
89
|
},
|
|
59
90
|
schema: {
|
|
60
91
|
hooks: {
|
|
92
|
+
/**
|
|
93
|
+
* Patch the ltree codec's fromPg/toPg to convert between file paths
|
|
94
|
+
* and ltree. This runs in the `build` hook (before type registration)
|
|
95
|
+
* so it works regardless of whether PostGraphile registers its own
|
|
96
|
+
* LTree scalar or we register ours.
|
|
97
|
+
*
|
|
98
|
+
* fromPg: "projects.alpha.docs" → "/projects/alpha/docs"
|
|
99
|
+
* toPg: "/projects/alpha/docs" → "projects.alpha.docs"
|
|
100
|
+
*/
|
|
101
|
+
build(build) {
|
|
102
|
+
for (const codec of Object.values(build.input.pgRegistry.pgCodecs)) {
|
|
103
|
+
if (codec.name === 'ltree') {
|
|
104
|
+
const c = codec;
|
|
105
|
+
const origFromPg = c.fromPg;
|
|
106
|
+
c.fromPg = (value) => {
|
|
107
|
+
const raw = origFromPg ? origFromPg(value) : value;
|
|
108
|
+
if (raw == null)
|
|
109
|
+
return raw;
|
|
110
|
+
return ltreeToSlash(String(raw));
|
|
111
|
+
};
|
|
112
|
+
const origToPg = c.toPg;
|
|
113
|
+
c.toPg = (value) => {
|
|
114
|
+
if (value == null)
|
|
115
|
+
return origToPg ? origToPg(value) : value;
|
|
116
|
+
const converted = slashToLtree(String(value));
|
|
117
|
+
return origToPg ? origToPg(converted) : converted;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return build;
|
|
122
|
+
},
|
|
61
123
|
init: {
|
|
62
124
|
before: ['PgCodecs', 'PgConnectionArgFilterPlugin'],
|
|
63
125
|
callback(_, build) {
|
|
@@ -66,14 +128,18 @@ exports.LtreeCodecPlugin = {
|
|
|
66
128
|
if (codec.name === 'ltree') {
|
|
67
129
|
if (!hasGraphQLTypeForPgCodec(codec, 'input')) {
|
|
68
130
|
build.registerScalarType(exports.LTREE_SCALAR_NAME, {}, () => ({
|
|
69
|
-
description: 'A
|
|
131
|
+
description: 'A hierarchical file path. Accepts slash-delimited paths (e.g. "/projects/alpha/docs"). ' +
|
|
132
|
+
'Stored internally as PostgreSQL ltree.',
|
|
70
133
|
serialize(value) {
|
|
134
|
+
if (value == null)
|
|
135
|
+
return null;
|
|
71
136
|
return String(value);
|
|
72
137
|
},
|
|
73
138
|
parseValue(value) {
|
|
74
|
-
if (typeof value
|
|
75
|
-
|
|
76
|
-
|
|
139
|
+
if (typeof value !== 'string') {
|
|
140
|
+
throw new Error(`${exports.LTREE_SCALAR_NAME} must be a string`);
|
|
141
|
+
}
|
|
142
|
+
return value;
|
|
77
143
|
},
|
|
78
144
|
parseLiteral(lit) {
|
|
79
145
|
if (lit.kind === 'NullValue')
|
package/preset.d.ts
CHANGED
|
@@ -2,27 +2,23 @@ import type { GraphileConfig } from 'graphile-config';
|
|
|
2
2
|
/**
|
|
3
3
|
* GraphileLtreePreset
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* Full ltree support with file-path syntax.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
|
|
10
|
-
export declare const GraphileLtreePreset: GraphileConfig.Preset;
|
|
11
|
-
/**
|
|
12
|
-
* GraphileFolderPreset
|
|
13
|
-
*
|
|
14
|
-
* Folder-oriented layer on top of the base ltree preset.
|
|
15
|
-
*
|
|
16
|
-
* Adds:
|
|
17
|
-
* - Virtual `{column}Folder` fields with slash-delimited paths
|
|
18
|
-
* - Folder operators: within, ancestorOf, glob (always slash-delimited)
|
|
7
|
+
* - LTree scalar auto-converts between slash-delimited file paths (API) and
|
|
8
|
+
* dot-delimited ltree (DB). External API always uses "/projects/alpha/docs".
|
|
9
|
+
* - Filter operators: within, ancestorOf, glob (all accept slash-delimited paths)
|
|
19
10
|
*
|
|
20
11
|
* @example
|
|
21
12
|
* ```graphql
|
|
22
13
|
* allFiles(where: { path: { within: "/projects/alpha" } }) {
|
|
23
|
-
* nodes {
|
|
14
|
+
* nodes { path filename }
|
|
24
15
|
* }
|
|
25
16
|
* ```
|
|
26
17
|
*/
|
|
18
|
+
export declare const GraphileLtreePreset: GraphileConfig.Preset;
|
|
19
|
+
/**
|
|
20
|
+
* @deprecated Use GraphileLtreePreset instead. The folder field plugin is no
|
|
21
|
+
* longer needed — the LTree scalar now handles slash↔ltree conversion directly.
|
|
22
|
+
*/
|
|
27
23
|
export declare const GraphileFolderPreset: GraphileConfig.Preset;
|
|
28
|
-
export default
|
|
24
|
+
export default GraphileLtreePreset;
|
package/preset.js
CHANGED
|
@@ -1,50 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.GraphileFolderPreset = exports.GraphileLtreePreset = void 0;
|
|
4
|
-
const connection_filter_operators_1 = require("./plugins/connection-filter-operators");
|
|
5
4
|
const detect_ltree_1 = require("./plugins/detect-ltree");
|
|
6
|
-
const folder_field_1 = require("./plugins/folder-field");
|
|
7
5
|
const folder_filter_operators_1 = require("./plugins/folder-filter-operators");
|
|
8
6
|
const ltree_codec_1 = require("./plugins/ltree-codec");
|
|
9
7
|
/**
|
|
10
8
|
* GraphileLtreePreset
|
|
11
9
|
*
|
|
12
|
-
*
|
|
10
|
+
* Full ltree support with file-path syntax.
|
|
13
11
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
|
|
17
|
-
exports.GraphileLtreePreset = {
|
|
18
|
-
plugins: [
|
|
19
|
-
detect_ltree_1.LtreeExtensionDetectionPlugin,
|
|
20
|
-
ltree_codec_1.LtreeCodecPlugin
|
|
21
|
-
],
|
|
22
|
-
schema: {
|
|
23
|
-
connectionFilterOperatorFactories: [
|
|
24
|
-
(0, connection_filter_operators_1.createLtreeOperatorFactory)()
|
|
25
|
-
]
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
/**
|
|
29
|
-
* GraphileFolderPreset
|
|
30
|
-
*
|
|
31
|
-
* Folder-oriented layer on top of the base ltree preset.
|
|
32
|
-
*
|
|
33
|
-
* Adds:
|
|
34
|
-
* - Virtual `{column}Folder` fields with slash-delimited paths
|
|
35
|
-
* - Folder operators: within, ancestorOf, glob (always slash-delimited)
|
|
12
|
+
* - LTree scalar auto-converts between slash-delimited file paths (API) and
|
|
13
|
+
* dot-delimited ltree (DB). External API always uses "/projects/alpha/docs".
|
|
14
|
+
* - Filter operators: within, ancestorOf, glob (all accept slash-delimited paths)
|
|
36
15
|
*
|
|
37
16
|
* @example
|
|
38
17
|
* ```graphql
|
|
39
18
|
* allFiles(where: { path: { within: "/projects/alpha" } }) {
|
|
40
|
-
* nodes {
|
|
19
|
+
* nodes { path filename }
|
|
41
20
|
* }
|
|
42
21
|
* ```
|
|
43
22
|
*/
|
|
44
|
-
exports.
|
|
45
|
-
extends: [exports.GraphileLtreePreset],
|
|
23
|
+
exports.GraphileLtreePreset = {
|
|
46
24
|
plugins: [
|
|
47
|
-
|
|
25
|
+
detect_ltree_1.LtreeExtensionDetectionPlugin,
|
|
26
|
+
ltree_codec_1.LtreeCodecPlugin
|
|
48
27
|
],
|
|
49
28
|
schema: {
|
|
50
29
|
connectionFilterOperatorFactories: [
|
|
@@ -52,4 +31,9 @@ exports.GraphileFolderPreset = {
|
|
|
52
31
|
]
|
|
53
32
|
}
|
|
54
33
|
};
|
|
55
|
-
|
|
34
|
+
/**
|
|
35
|
+
* @deprecated Use GraphileLtreePreset instead. The folder field plugin is no
|
|
36
|
+
* longer needed — the LTree scalar now handles slash↔ltree conversion directly.
|
|
37
|
+
*/
|
|
38
|
+
exports.GraphileFolderPreset = exports.GraphileLtreePreset;
|
|
39
|
+
exports.default = exports.GraphileLtreePreset;
|