prisma-ltree 0.1.0 → 0.2.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/README.md +73 -59
- package/package.json +14 -15
package/README.md
CHANGED
|
@@ -8,25 +8,19 @@ them with type-safe, prisma-native operators — ancestor/descendant checks, `lq
|
|
|
8
8
|
pattern matching, path manipulation, and lowest-common-ancestor computation — without dropping to
|
|
9
9
|
raw SQL.
|
|
10
10
|
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
- **`ltree()` / `ltreeArray()` column helpers** — non-parameterized columns with `nativeType`
|
|
20
|
-
`'ltree'` / `'ltree[]'`.
|
|
21
|
-
- **Query operators** — hierarchy, pattern-match, scalar functions, concatenation, conversion, and
|
|
22
|
-
array first-match operators (full list under [Operations](#operations)).
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **`ltree` and `ltree[]` columns** — `ltree()` / `ltreeArray()` column helpers
|
|
14
|
+
- **Hierarchy operators** — ancestor and descendant checks
|
|
15
|
+
- **Pattern matching** — `lquery`, `lquery[]`, and `ltxtquery`
|
|
16
|
+
- **Scalar functions** — depth, subpaths, label index, lowest common ancestor
|
|
17
|
+
- **Concatenation & conversion** — path building and `ltree` ↔ `text` conversion
|
|
18
|
+
- **Array first-match** — find the first matching path in an `ltree[]` column
|
|
23
19
|
- **Baseline migration** — installs the Postgres extension via
|
|
24
|
-
`CREATE EXTENSION IF NOT EXISTS ltree`
|
|
25
|
-
- **Multi-plane entrypoints** — `/control`, `/runtime`, `/column-types`, `/codec-types`,
|
|
26
|
-
`/operation-types`, `/pack`.
|
|
20
|
+
`CREATE EXTENSION IF NOT EXISTS ltree` when the pack is composed
|
|
27
21
|
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
See the [feature support matrix](https://github.com/slovakian/prisma-ltree/blob/main/docs/feature-support.md)
|
|
23
|
+
for what is supported, planned, or out of scope.
|
|
30
24
|
|
|
31
25
|
## Installation
|
|
32
26
|
|
|
@@ -34,9 +28,19 @@ The single source of truth for what is supported, planned, or out-of-scope is
|
|
|
34
28
|
pnpm add prisma-ltree
|
|
35
29
|
```
|
|
36
30
|
|
|
37
|
-
Requires Node `>=24` and `@prisma-next/*@0.14.0
|
|
31
|
+
Requires Node `>=24` and `@prisma-next/*@0.14.0` (exact pin — see
|
|
32
|
+
[versioning & compatibility](https://github.com/slovakian/prisma-ltree/blob/main/docs/prisma-next/versioning-and-compatibility.md)).
|
|
38
33
|
|
|
39
|
-
|
|
34
|
+
### Agent skills (optional)
|
|
35
|
+
|
|
36
|
+
Agent skills for adoption and query patterns ship in the repo under
|
|
37
|
+
[`skills/`](https://github.com/slovakian/prisma-ltree/tree/main/skills):
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pnpm dlx skills add slovakian/prisma-ltree --all
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Database setup
|
|
40
44
|
|
|
41
45
|
The extension ships an on-disk baseline migration that installs `ltree` when the pack is composed
|
|
42
46
|
into an application. `prisma-next db init` / `db update` apply it automatically. The equivalent
|
|
@@ -67,7 +71,35 @@ export default defineConfig({
|
|
|
67
71
|
|
|
68
72
|
## Usage
|
|
69
73
|
|
|
70
|
-
### Contract
|
|
74
|
+
### Contract definition
|
|
75
|
+
|
|
76
|
+
Author ltree columns in either lane — `contract.prisma` (PSL) or `contract.ts`
|
|
77
|
+
(TypeScript). Both emit a byte-identical compiled contract.
|
|
78
|
+
|
|
79
|
+
**PSL lane** — reference the `ltree` namespace constructors (parens required) and compose
|
|
80
|
+
`ltree` into `extensions` in `prisma-next.config.ts`:
|
|
81
|
+
|
|
82
|
+
```prisma
|
|
83
|
+
// contract.prisma — use prisma-next
|
|
84
|
+
|
|
85
|
+
types {
|
|
86
|
+
Path = ltree.Ltree() // → pg/ltree@1 / ltree
|
|
87
|
+
Paths = ltree.LtreeArray() // → pg/ltree-array@1 / ltree[]
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
model Page {
|
|
91
|
+
id String @id @default(uuid())
|
|
92
|
+
path Path
|
|
93
|
+
breadcrumbs Paths
|
|
94
|
+
|
|
95
|
+
@@map("page")
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`@db.Ltree` is not available — `@db.*` is a closed core table with no extension hook; the
|
|
100
|
+
namespace constructor is the sanctioned surface (the same one pgvector and postgis use).
|
|
101
|
+
|
|
102
|
+
**TypeScript lane**:
|
|
71
103
|
|
|
72
104
|
```typescript
|
|
73
105
|
import { int4Column, textColumn } from "@prisma-next/adapter-postgres/column-types";
|
|
@@ -93,7 +125,7 @@ export const contract = defineContract({
|
|
|
93
125
|
});
|
|
94
126
|
```
|
|
95
127
|
|
|
96
|
-
### Runtime
|
|
128
|
+
### Runtime setup
|
|
97
129
|
|
|
98
130
|
```typescript
|
|
99
131
|
import { instantiateExecutionStack } from "@prisma-next/framework-components/execution";
|
|
@@ -111,7 +143,7 @@ const context = createExecutionContext({ contract, stack });
|
|
|
111
143
|
const stackInstance = instantiateExecutionStack(stack);
|
|
112
144
|
```
|
|
113
145
|
|
|
114
|
-
### Query
|
|
146
|
+
### Query usage
|
|
115
147
|
|
|
116
148
|
```typescript
|
|
117
149
|
import { sql, tables } from "../prisma/query";
|
|
@@ -130,9 +162,6 @@ const plan = sql
|
|
|
130
162
|
|
|
131
163
|
## Operations
|
|
132
164
|
|
|
133
|
-
All boolean operators return `pg/bool@1`. See
|
|
134
|
-
[`docs/feature-support.md`](../../docs/feature-support.md) for the authoritative matrix.
|
|
135
|
-
|
|
136
165
|
### Hierarchy (→ boolean)
|
|
137
166
|
|
|
138
167
|
| Method | SQL |
|
|
@@ -150,30 +179,28 @@ All boolean operators return `pg/bool@1`. See
|
|
|
150
179
|
|
|
151
180
|
### Scalar functions
|
|
152
181
|
|
|
153
|
-
| Method | SQL | Returns
|
|
154
|
-
| ---------------------------- | ----------------------------- |
|
|
155
|
-
| `path.nlevel()` | `nlevel(ltree)` | `
|
|
156
|
-
| `path.subltree(start, end)` | `subltree(ltree, start, end)` | `
|
|
157
|
-
| `path.subpath(offset, len?)` | `subpath(ltree, offset, len)` | `
|
|
158
|
-
| `path.indexOf(other, off?)` | `index(ltree, ltree, off)` | `
|
|
159
|
-
| `path.lca(other, ...rest)` | `lca(ltree, ltree, ...)` | `
|
|
182
|
+
| Method | SQL | Returns |
|
|
183
|
+
| ---------------------------- | ----------------------------- | ------- |
|
|
184
|
+
| `path.nlevel()` | `nlevel(ltree)` | `int` |
|
|
185
|
+
| `path.subltree(start, end)` | `subltree(ltree, start, end)` | `ltree` |
|
|
186
|
+
| `path.subpath(offset, len?)` | `subpath(ltree, offset, len)` | `ltree` |
|
|
187
|
+
| `path.indexOf(other, off?)` | `index(ltree, ltree, off)` | `int` |
|
|
188
|
+
| `path.lca(other, ...rest)` | `lca(ltree, ltree, ...)` | `ltree` |
|
|
160
189
|
|
|
161
|
-
`lca`
|
|
162
|
-
common ancestor (see [ADR-001](../../docs/decisions/ADR-001-lca-api-shape.md)).
|
|
190
|
+
`lca` requires **≥2 paths** and returns the proper (strictly shorter) lowest common ancestor.
|
|
163
191
|
|
|
164
192
|
### Concatenation (→ ltree) & conversion
|
|
165
193
|
|
|
166
|
-
| Method | SQL | Returns
|
|
167
|
-
| ------------------------- | ------------------- |
|
|
168
|
-
| `path.concat(rhs)` | `ltree \|\| ltree` | `
|
|
169
|
-
| `path.concatText(label)` | `ltree \|\| text` | `
|
|
170
|
-
| `path.prependText(label)` | `text \|\| ltree` | `
|
|
171
|
-
| `path.toText()` | `ltree2text(ltree)` | `
|
|
172
|
-
| `text.toLtree()` | `text2ltree(text)` | `
|
|
194
|
+
| Method | SQL | Returns |
|
|
195
|
+
| ------------------------- | ------------------- | ------- |
|
|
196
|
+
| `path.concat(rhs)` | `ltree \|\| ltree` | `ltree` |
|
|
197
|
+
| `path.concatText(label)` | `ltree \|\| text` | `ltree` |
|
|
198
|
+
| `path.prependText(label)` | `text \|\| ltree` | `ltree` |
|
|
199
|
+
| `path.toText()` | `ltree2text(ltree)` | `text` |
|
|
200
|
+
| `text.toLtree()` | `text2ltree(text)` | `ltree` |
|
|
173
201
|
|
|
174
|
-
`prependText` keeps the ltree column as the receiver even though it is the right operand
|
|
175
|
-
`toLtree` is
|
|
176
|
-
[ADR-002](../../docs/decisions/ADR-002-free-function-lowering.md)).
|
|
202
|
+
`prependText` keeps the ltree column as the receiver even though it is the right SQL operand.
|
|
203
|
+
`toLtree` is called on a text column.
|
|
177
204
|
|
|
178
205
|
### Array first-match (→ ltree, receiver `ltree[]`)
|
|
179
206
|
|
|
@@ -184,6 +211,8 @@ common ancestor (see [ADR-001](../../docs/decisions/ADR-001-lca-api-shape.md)).
|
|
|
184
211
|
| `paths.firstMatchLquery(pattern)` | `ltree[] ?~ lquery` |
|
|
185
212
|
| `paths.firstMatchLtxtquery(query)` | `ltree[] ?@ ltxtquery` |
|
|
186
213
|
|
|
214
|
+
Use `ltreeArray()` for `ltree[]` columns that expose these methods.
|
|
215
|
+
|
|
187
216
|
## Types
|
|
188
217
|
|
|
189
218
|
```typescript
|
|
@@ -194,21 +223,6 @@ import type { QueryOperationTypes } from "prisma-ltree/operation-types";
|
|
|
194
223
|
// CodecTypes['pg/ltree-array@1']['output'] = readonly string[]
|
|
195
224
|
```
|
|
196
225
|
|
|
197
|
-
## Development
|
|
198
|
-
|
|
199
|
-
This package is built with [Vite+](https://viteplus.dev/):
|
|
200
|
-
|
|
201
|
-
```bash
|
|
202
|
-
vp install # install dependencies
|
|
203
|
-
vp check # format, lint, typecheck
|
|
204
|
-
vp test # run unit + integration + type-level tests
|
|
205
|
-
vp test --coverage # enforce the 95% coverage threshold
|
|
206
|
-
vp run build # build dist/
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
Integration tests run against [PGlite](https://pglite.dev/) with the `ltree` contrib extension —
|
|
210
|
-
every operator is lowered through a composed Postgres runtime adapter and executed for real.
|
|
211
|
-
|
|
212
226
|
## License
|
|
213
227
|
|
|
214
228
|
Apache-2.0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prisma-ltree",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Prisma Next extension pack for PostgreSQL's ltree hierarchical tree data type.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -26,15 +26,6 @@
|
|
|
26
26
|
"publishConfig": {
|
|
27
27
|
"access": "public"
|
|
28
28
|
},
|
|
29
|
-
"scripts": {
|
|
30
|
-
"build": "vp pack",
|
|
31
|
-
"dev": "vp pack --watch",
|
|
32
|
-
"test": "vp test",
|
|
33
|
-
"check": "vp check",
|
|
34
|
-
"build:contract-space": "prisma-next contract emit",
|
|
35
|
-
"check-pins": "prisma-next-check-pins",
|
|
36
|
-
"prepublishOnly": "vp run build"
|
|
37
|
-
},
|
|
38
29
|
"dependencies": {
|
|
39
30
|
"@prisma-next/contract": "0.14.0",
|
|
40
31
|
"@prisma-next/contract-authoring": "0.14.0",
|
|
@@ -50,7 +41,7 @@
|
|
|
50
41
|
"arktype": "^2.2.0"
|
|
51
42
|
},
|
|
52
43
|
"devDependencies": {
|
|
53
|
-
"@electric-sql/pglite": "
|
|
44
|
+
"@electric-sql/pglite": "^0.5.3",
|
|
54
45
|
"@prisma-next/adapter-postgres": "0.14.0",
|
|
55
46
|
"@prisma-next/cli": "0.14.0",
|
|
56
47
|
"@prisma-next/extension-author-tools": "0.14.0",
|
|
@@ -58,11 +49,11 @@
|
|
|
58
49
|
"@prisma-next/postgres": "0.14.0",
|
|
59
50
|
"@prisma-next/sql-contract-ts": "0.14.0",
|
|
60
51
|
"@prisma-next/target-postgres": "0.14.0",
|
|
61
|
-
"@types/node": "
|
|
52
|
+
"@types/node": "^24",
|
|
62
53
|
"@typescript/native-preview": "7.0.0-dev.20260509.2",
|
|
63
54
|
"@vitest/coverage-v8": "4.1.8",
|
|
64
|
-
"typescript": "
|
|
65
|
-
"vite-plus": "
|
|
55
|
+
"typescript": "^5",
|
|
56
|
+
"vite-plus": "0.1.24"
|
|
66
57
|
},
|
|
67
58
|
"peerDependencies": {
|
|
68
59
|
"@prisma-next/adapter-postgres": "0.14.0",
|
|
@@ -85,5 +76,13 @@
|
|
|
85
76
|
"postgres"
|
|
86
77
|
],
|
|
87
78
|
"type": "extension-pack"
|
|
79
|
+
},
|
|
80
|
+
"scripts": {
|
|
81
|
+
"build": "vp pack",
|
|
82
|
+
"dev": "vp pack --watch",
|
|
83
|
+
"test": "vp test",
|
|
84
|
+
"check": "vp check",
|
|
85
|
+
"build:contract-space": "prisma-next contract emit",
|
|
86
|
+
"check-pins": "prisma-next-check-pins"
|
|
88
87
|
}
|
|
89
|
-
}
|
|
88
|
+
}
|