@prisma-next/extension-pgvector 0.5.0 → 0.6.0-dev.3
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 +18 -4
- package/dist/{codec-types-yMSpEJJM.d.mts → codec-types-CQubO6uQ.d.mts} +2 -2
- package/dist/{codec-types-yMSpEJJM.d.mts.map → codec-types-CQubO6uQ.d.mts.map} +1 -1
- package/dist/codec-types.d.mts +1 -1
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +140 -154
- package/dist/control.mjs.map +1 -1
- package/dist/pack.d.mts +1 -1
- package/package.json +25 -13
- package/src/contract.d.ts +91 -0
- package/src/contract.json +40 -0
- package/src/contract.ts +67 -0
- package/src/core/contract-space-constants.ts +1 -1
- package/src/exports/control.ts +47 -24
- package/src/core/contract.ts +0 -74
- package/src/core/migrations.ts +0 -125
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ This extension pack adds support for the `vector` data type and vector similarit
|
|
|
13
13
|
- **CLI Integration**: Provides extension descriptor for `prisma-next.config.ts` configuration
|
|
14
14
|
- **Runtime Extension**: Registers codecs and operations at runtime for vector column operations
|
|
15
15
|
- **Pack Ref Export**: Ships a pure `/pack` entrypoint for TypeScript contract authoring without runtime filesystem access
|
|
16
|
-
- **
|
|
16
|
+
- **Baseline Migration**: Ships an on-disk baseline migration in its contract space that installs the `vector` Postgres extension (`CREATE EXTENSION IF NOT EXISTS vector`) when the extension is composed into an application
|
|
17
17
|
|
|
18
18
|
## Dependencies
|
|
19
19
|
|
|
@@ -30,15 +30,15 @@ pnpm add @prisma-next/extension-pgvector
|
|
|
30
30
|
|
|
31
31
|
## Database Setup
|
|
32
32
|
|
|
33
|
-
The pgvector extension
|
|
33
|
+
The pgvector extension ships an on-disk baseline migration in its contract space; applying that migration installs pgvector with `CREATE EXTENSION IF NOT EXISTS vector`. When the extension is composed into an application via `extensionPacks`, `prisma-next db init` and `prisma-next db update` apply the baseline (and any subsequent migrations) automatically.
|
|
34
34
|
|
|
35
|
-
For manual database setup,
|
|
35
|
+
For manual database setup, the equivalent DDL is:
|
|
36
36
|
|
|
37
37
|
```sql
|
|
38
38
|
CREATE EXTENSION IF NOT EXISTS vector;
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
Ensure the baseline migration (or equivalent DDL) has been applied before running workloads that use vector columns.
|
|
42
42
|
|
|
43
43
|
## Configuration
|
|
44
44
|
|
|
@@ -198,10 +198,24 @@ The extension declares the following capabilities:
|
|
|
198
198
|
|
|
199
199
|
- `pgvector.cosine`: Indicates support for cosine distance and similarity operations
|
|
200
200
|
|
|
201
|
+
## Authoring (maintainers)
|
|
202
|
+
|
|
203
|
+
The extension's contract + baseline migration are emitted on-disk inside this package using the same pipeline application authors use:
|
|
204
|
+
|
|
205
|
+
- `pnpm build:contract-space` — runs `prisma-next contract emit` to produce `src/contract.{json,d.ts}` from the TS source at `src/contract.ts`.
|
|
206
|
+
- `pnpm exec prisma-next migration plan --name <slug>` (run from this package directory) — scaffolds a new migration directory under `migrations/<dirName>/` for schema changes that touch tables / models. **Not chained into `pnpm build`**: `migration plan` is non-idempotent (each invocation generates a new timestamped directory), so it runs manually when the contract source changes. Note: pgvector's contract declares only the parameterised `vector` native type under `storage.types` (no tables / models), so the planner currently refuses to scaffold the baseline migration with `PN-CLI-4020 Contract changed but planner produced no operations` (this is **Path B** authoring per [ADR 212](../../../docs/architecture%20docs/adrs/ADR%20212%20-%20Contract%20spaces.md#contract-space-package-layout)). That directory was hand-authored once (Migration subclass + seed `migration.json` preserving the full `toContract`) and `pnpm tsx migrations/<dirName>/migration.ts` re-emits `ops.json` + `migration.json` deterministically. Future migrations that add tables / models can use `migration plan` directly (Path A).
|
|
207
|
+
- `pnpm tsx migrations/<dirName>/migration.ts` (run from this package directory) — re-emits `ops.json` + `migration.json` from the hand-edited subclass. Use `tsx`, not bare `node`, because the Migration subclass imports relative TypeScript siblings which Node's native loader can't resolve without a TS-aware loader.
|
|
208
|
+
- `migrations/refs/head.json` is hand-pinned with the latest migration's `to` hash + `providedInvariants`.
|
|
209
|
+
|
|
210
|
+
The descriptor at `src/exports/control.ts` then JSON-imports those artefacts and synthesises the framework's `MigrationPackage` shape.
|
|
211
|
+
|
|
212
|
+
See [ADR 212 — Contract spaces](../../../docs/architecture%20docs/adrs/ADR%20212%20-%20Contract%20spaces.md) ("Contract-space package layout") for the canonical layout and rationale.
|
|
213
|
+
|
|
201
214
|
## References
|
|
202
215
|
|
|
203
216
|
- [pgvector documentation](https://github.com/pgvector/pgvector)
|
|
204
217
|
- [Prisma Next Architecture Overview](../../../docs/Architecture%20Overview.md)
|
|
205
218
|
- [Extension Packs Guide](../../../docs/reference/Extension-Packs-Naming-and-Layout.md)
|
|
219
|
+
- [ADR 212 — Contract spaces](../../../docs/architecture%20docs/adrs/ADR%20212%20-%20Contract%20spaces.md)
|
|
206
220
|
|
|
207
221
|
Pack refs (`@prisma-next/extension-pgvector/pack`) are pure data objects generated from the hydrated manifest (`src/core/manifest.ts`), so TypeScript contract builders can enable the pgvector namespace in both emit and no-emit workflows without touching the filesystem.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { JsonValue } from "@prisma-next/contract/types";
|
|
2
1
|
import { AnyCodecDescriptor, CodecCallContext, CodecDescriptorImpl, CodecImpl, CodecInstanceContext } from "@prisma-next/framework-components/codec";
|
|
2
|
+
import { JsonValue } from "@prisma-next/contract/types";
|
|
3
3
|
import { ExtractCodecTypes } from "@prisma-next/sql-relational-core/ast";
|
|
4
4
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
5
5
|
|
|
@@ -60,4 +60,4 @@ type Vector<N extends number = number> = number[] & {
|
|
|
60
60
|
type CodecTypes = CodecTypes$1;
|
|
61
61
|
//#endregion
|
|
62
62
|
export { Vector as n, CodecTypes as t };
|
|
63
|
-
//# sourceMappingURL=codec-types-
|
|
63
|
+
//# sourceMappingURL=codec-types-CQubO6uQ.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codec-types-
|
|
1
|
+
{"version":3,"file":"codec-types-CQubO6uQ.d.mts","names":[],"sources":["../src/core/constants.ts","../src/core/codecs.ts","../src/types/codec-types.ts"],"mappings":";;;;;;;;;cAGa,eAAA;;;KCyBR,YAAA;EAAA,SAA0B,MAAA;AAAA;AAAA,cAiBlB,aAAA,SAAsB,SAAA,QAC1B,eAAA;EAAA,SAKE,MAAA;cAEG,UAAA,EAAY,kBAAA,EAAoB,MAAA;EAK5C,YAAA,CAAa,KAAA,oBAAyB,KAAA;EAYhC,MAAA,CAAO,KAAA,YAAiB,IAAA,EAAM,gBAAA,GAAmB,OAAA;EAKjD,MAAA,CAAO,IAAA,UAAc,IAAA,EAAM,gBAAA,GAAmB,OAAA;EAsBpD,UAAA,CAAW,KAAA,aAAkB,SAAA;EAK7B,UAAA,CAAW,IAAA,EAAM,SAAA;AAAA;AAAA,cAMN,kBAAA,SAA2B,mBAAA,CAAoB,YAAA;EAAA,SACxC,OAAA;EAAA,SACA,MAAA;EAAA,SACA,WAAA;EAAA,SACA,IAAA;IAAA;;;;;;;;WACA,YAAA,EAAc,gBAAA,CAAiB,YAAA;EACxC,gBAAA,CAAiB,MAAA,EAAQ,YAAA;EA5C5B;;;EAkDG,OAAA,CAAQ,MAAA,EAAQ,YAAA,IAAgB,GAAA,EAAK,oBAAA,KAAyB,aAAA;AAAA;AAAA,cAkBnE,kBAAA;EAAA,iBAEI,kBAAA;AAAA;AAAA,KAEE,YAAA,GAAa,iBAAA,QAAyB,kBAAA;;;;;;;;AApHY;KCTlD,MAAA;EAAA,SAA0D,cAAA,GAAiB,CAAA;AAAA;AAAA,KAE3E,UAAA,GAAa,YAAA"}
|
package/dist/codec-types.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as Vector, t as CodecTypes } from "./codec-types-
|
|
1
|
+
import { n as Vector, t as CodecTypes } from "./codec-types-CQubO6uQ.mjs";
|
|
2
2
|
export { type CodecTypes, type Vector };
|
package/dist/control.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control.d.mts","names":[],"sources":["../src/exports/control.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"control.d.mts","names":[],"sources":["../src/exports/control.ts"],"mappings":";;;cAsFM,2BAAA,EAA6B,6BAAA"}
|
package/dist/control.mjs
CHANGED
|
@@ -1,8 +1,133 @@
|
|
|
1
|
-
import { t as VECTOR_CODEC_ID } from "./constants-DX-00vYk.mjs";
|
|
2
1
|
import { n as pgvectorQueryOperations, t as pgvectorPackMeta } from "./descriptor-meta-DEgJjLLi.mjs";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import { contractSpaceFromJson } from "@prisma-next/migration-tools/spaces";
|
|
3
|
+
//#region migrations/20260601T0000_install_vector_extension/migration.json
|
|
4
|
+
var migration_default = {
|
|
5
|
+
from: null,
|
|
6
|
+
to: "sha256:382dae5bb1548e62cbc449530ea08a9ce0a0dbb280401e9588642223f33783ae",
|
|
7
|
+
labels: [],
|
|
8
|
+
providedInvariants: ["pgvector:install-vector-v1"],
|
|
9
|
+
createdAt: "2026-06-01T00:00:00.000Z",
|
|
10
|
+
fromContract: null,
|
|
11
|
+
toContract: {
|
|
12
|
+
"schemaVersion": "1",
|
|
13
|
+
"targetFamily": "sql",
|
|
14
|
+
"target": "postgres",
|
|
15
|
+
"profileHash": "sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e",
|
|
16
|
+
"roots": {},
|
|
17
|
+
"models": {},
|
|
18
|
+
"storage": {
|
|
19
|
+
"storageHash": "sha256:382dae5bb1548e62cbc449530ea08a9ce0a0dbb280401e9588642223f33783ae",
|
|
20
|
+
"tables": {},
|
|
21
|
+
"types": { "vector": {
|
|
22
|
+
"codecId": "pg/vector@1",
|
|
23
|
+
"nativeType": "vector",
|
|
24
|
+
"typeParams": {}
|
|
25
|
+
} }
|
|
26
|
+
},
|
|
27
|
+
"capabilities": {
|
|
28
|
+
"postgres": {
|
|
29
|
+
"jsonAgg": true,
|
|
30
|
+
"lateral": true,
|
|
31
|
+
"limit": true,
|
|
32
|
+
"orderBy": true,
|
|
33
|
+
"returning": true
|
|
34
|
+
},
|
|
35
|
+
"sql": {
|
|
36
|
+
"defaultInInsert": true,
|
|
37
|
+
"enums": true,
|
|
38
|
+
"returning": true
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"extensionPacks": {},
|
|
42
|
+
"meta": {},
|
|
43
|
+
"_generated": {
|
|
44
|
+
"warning": "⚠️ GENERATED FILE - DO NOT EDIT",
|
|
45
|
+
"message": "This file is automatically generated by \"prisma-next contract emit\".",
|
|
46
|
+
"regenerate": "To regenerate, run: prisma-next contract emit"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
hints: {
|
|
50
|
+
"used": [],
|
|
51
|
+
"applied": [],
|
|
52
|
+
"plannerVersion": "2.0.0"
|
|
53
|
+
},
|
|
54
|
+
migrationHash: "sha256:7419a7bfdac708cff9db1b35e48b035b09f3a883f099483e41385738d146622a"
|
|
55
|
+
};
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region migrations/20260601T0000_install_vector_extension/ops.json
|
|
58
|
+
var ops_default = [{
|
|
59
|
+
"id": "pgvector.install-vector-extension",
|
|
60
|
+
"label": "Enable extension \"vector\"",
|
|
61
|
+
"operationClass": "additive",
|
|
62
|
+
"invariantId": "pgvector:install-vector-v1",
|
|
63
|
+
"target": {
|
|
64
|
+
"id": "postgres",
|
|
65
|
+
"details": {
|
|
66
|
+
"schema": "public",
|
|
67
|
+
"objectType": "dependency",
|
|
68
|
+
"name": "vector"
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"precheck": [{
|
|
72
|
+
"description": "verify extension \"vector\" is not already enabled",
|
|
73
|
+
"sql": "SELECT NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector')"
|
|
74
|
+
}],
|
|
75
|
+
"execute": [{
|
|
76
|
+
"description": "create extension \"vector\"",
|
|
77
|
+
"sql": "CREATE EXTENSION IF NOT EXISTS vector"
|
|
78
|
+
}],
|
|
79
|
+
"postcheck": [{
|
|
80
|
+
"description": "confirm extension \"vector\" is enabled",
|
|
81
|
+
"sql": "SELECT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector')"
|
|
82
|
+
}]
|
|
83
|
+
}];
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region migrations/refs/head.json
|
|
86
|
+
var head_default = {
|
|
87
|
+
hash: "sha256:382dae5bb1548e62cbc449530ea08a9ce0a0dbb280401e9588642223f33783ae",
|
|
88
|
+
invariants: ["pgvector:install-vector-v1"]
|
|
89
|
+
};
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/contract.json
|
|
92
|
+
var contract_default = {
|
|
93
|
+
schemaVersion: "1",
|
|
94
|
+
targetFamily: "sql",
|
|
95
|
+
target: "postgres",
|
|
96
|
+
profileHash: "sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e",
|
|
97
|
+
roots: {},
|
|
98
|
+
models: {},
|
|
99
|
+
storage: {
|
|
100
|
+
"storageHash": "sha256:382dae5bb1548e62cbc449530ea08a9ce0a0dbb280401e9588642223f33783ae",
|
|
101
|
+
"tables": {},
|
|
102
|
+
"types": { "vector": {
|
|
103
|
+
"codecId": "pg/vector@1",
|
|
104
|
+
"nativeType": "vector",
|
|
105
|
+
"typeParams": {}
|
|
106
|
+
} }
|
|
107
|
+
},
|
|
108
|
+
capabilities: {
|
|
109
|
+
"postgres": {
|
|
110
|
+
"jsonAgg": true,
|
|
111
|
+
"lateral": true,
|
|
112
|
+
"limit": true,
|
|
113
|
+
"orderBy": true,
|
|
114
|
+
"returning": true
|
|
115
|
+
},
|
|
116
|
+
"sql": {
|
|
117
|
+
"defaultInInsert": true,
|
|
118
|
+
"enums": true,
|
|
119
|
+
"returning": true
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
extensionPacks: {},
|
|
123
|
+
meta: {},
|
|
124
|
+
_generated: {
|
|
125
|
+
"warning": "⚠️ GENERATED FILE - DO NOT EDIT",
|
|
126
|
+
"message": "This file is automatically generated by \"prisma-next contract emit\".",
|
|
127
|
+
"regenerate": "To regenerate, run: prisma-next contract emit"
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
//#endregion
|
|
6
131
|
//#region src/core/contract-space-constants.ts
|
|
7
132
|
/**
|
|
8
133
|
* Static names and identifiers used across pgvector's contract space.
|
|
@@ -12,7 +137,7 @@ import { computeMigrationHash } from "@prisma-next/migration-tools/hash";
|
|
|
12
137
|
* (`../exports/control`) all reference the same values without typos.
|
|
13
138
|
*
|
|
14
139
|
* The space identifier `'pgvector'` is what the framework writes to
|
|
15
|
-
* `migrations
|
|
140
|
+
* `migrations/` in the user's repo and what the marker table's
|
|
16
141
|
* `space` column carries for pgvector-owned rows.
|
|
17
142
|
*
|
|
18
143
|
* The `pgvector:*` invariantId namespace is locked here — once
|
|
@@ -20,153 +145,10 @@ import { computeMigrationHash } from "@prisma-next/migration-tools/hash";
|
|
|
20
145
|
* reference it by literal string match.
|
|
21
146
|
*/
|
|
22
147
|
const PGVECTOR_SPACE_ID = "pgvector";
|
|
23
|
-
const PGVECTOR_NATIVE_TYPE = "vector";
|
|
24
|
-
const PGVECTOR_BASELINE_MIGRATION_NAME = "20260601T0000_install_vector_extension";
|
|
25
|
-
/**
|
|
26
|
-
* `pgvector:*` invariantIds emitted by the baseline migration. Each id,
|
|
27
|
-
* once published, is immutable: downstream consumers (other extensions,
|
|
28
|
-
* the marker table) reference them by literal string match.
|
|
29
|
-
*/
|
|
30
|
-
const PGVECTOR_INVARIANTS = { installVector: "pgvector:install-vector-v1" };
|
|
31
|
-
//#endregion
|
|
32
|
-
//#region src/core/contract.ts
|
|
33
|
-
/**
|
|
34
|
-
* pgvector contract space — declares the parameterised native type
|
|
35
|
-
* `vector(N)` that user columns can name as `nativeType`.
|
|
36
|
-
*
|
|
37
|
-
* Unlike CipherStash's typed objects (composite types / domains / enums
|
|
38
|
-
* — deferred behind `meta.cipherstashFutureIR` until the IR vocabulary
|
|
39
|
-
* gains first-class support), pgvector's `vector` is a parameterised
|
|
40
|
-
* native type and *is* representable in today's IR via
|
|
41
|
-
* {@link StorageTypeInstance}: `{ codecId, nativeType, typeParams }`.
|
|
42
|
-
* The contract registers a representative instance under
|
|
43
|
-
* `storage.types.vector` so the verifier sees the type as part of
|
|
44
|
-
* pgvector's space contribution and so the pinned `contract.json` on
|
|
45
|
-
* disk is materially distinct from an empty space.
|
|
46
|
-
*
|
|
47
|
-
* Per-column instances on the user's side carry concrete
|
|
48
|
-
* `typeParams.length` (e.g. `vector(1536)`); the registration here
|
|
49
|
-
* declares the parameterised shape — it is not consumed as a literal
|
|
50
|
-
* column type by any user table.
|
|
51
|
-
*/
|
|
52
|
-
const TARGET = "postgres";
|
|
53
|
-
const TARGET_FAMILY = "sql";
|
|
54
|
-
/**
|
|
55
|
-
* Storage body for the contract — pgvector ships no tables of its own;
|
|
56
|
-
* the `vector` parameterised native type is registered under
|
|
57
|
-
* `storage.types` so pgvector's IR contribution is non-empty and the
|
|
58
|
-
* pinned `contract.json` on disk differs materially from an empty
|
|
59
|
-
* extension space.
|
|
60
|
-
*
|
|
61
|
-
* Authored without `storageHash` here so {@link computeStorageHash} can
|
|
62
|
-
* digest the canonical body (the hashing pipeline panics if asked to
|
|
63
|
-
* hash an object that already carries its own output — see
|
|
64
|
-
* `assertDescriptorSelfConsistency`'s storage-hash strip).
|
|
65
|
-
*/
|
|
66
|
-
const storageBody = {
|
|
67
|
-
tables: {},
|
|
68
|
-
types: { [PGVECTOR_NATIVE_TYPE]: {
|
|
69
|
-
codecId: VECTOR_CODEC_ID,
|
|
70
|
-
nativeType: PGVECTOR_NATIVE_TYPE,
|
|
71
|
-
typeParams: {}
|
|
72
|
-
} }
|
|
73
|
-
};
|
|
74
|
-
/** Content-addressed hash of pgvector's storage IR. */
|
|
75
|
-
const PGVECTOR_STORAGE_HASH = computeStorageHash({
|
|
76
|
-
target: TARGET,
|
|
77
|
-
targetFamily: TARGET_FAMILY,
|
|
78
|
-
storage: storageBody
|
|
79
|
-
});
|
|
80
|
-
/** pgvector's contract value, exposed via the descriptor's `contractSpace.contractJson`. */
|
|
81
|
-
const pgvectorContract = {
|
|
82
|
-
target: TARGET,
|
|
83
|
-
targetFamily: TARGET_FAMILY,
|
|
84
|
-
roots: {},
|
|
85
|
-
models: {},
|
|
86
|
-
capabilities: {},
|
|
87
|
-
extensionPacks: {},
|
|
88
|
-
meta: {},
|
|
89
|
-
profileHash: profileHash("pgvector-extension-profile-v1"),
|
|
90
|
-
storage: {
|
|
91
|
-
...storageBody,
|
|
92
|
-
storageHash: coreHash(PGVECTOR_STORAGE_HASH)
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
//#endregion
|
|
96
|
-
//#region src/core/migrations.ts
|
|
97
|
-
const pgvectorBaselineOps = [{
|
|
98
|
-
id: "pgvector.install-vector-extension",
|
|
99
|
-
label: "Enable extension \"vector\"",
|
|
100
|
-
operationClass: "additive",
|
|
101
|
-
invariantId: PGVECTOR_INVARIANTS.installVector,
|
|
102
|
-
target: {
|
|
103
|
-
id: "postgres",
|
|
104
|
-
details: {
|
|
105
|
-
schema: "public",
|
|
106
|
-
objectType: "extension",
|
|
107
|
-
name: "vector"
|
|
108
|
-
}
|
|
109
|
-
},
|
|
110
|
-
precheck: [{
|
|
111
|
-
description: "verify extension \"vector\" is not already enabled",
|
|
112
|
-
sql: "SELECT NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector')"
|
|
113
|
-
}],
|
|
114
|
-
execute: [{
|
|
115
|
-
description: "create extension \"vector\"",
|
|
116
|
-
sql: "CREATE EXTENSION IF NOT EXISTS vector"
|
|
117
|
-
}],
|
|
118
|
-
postcheck: [{
|
|
119
|
-
description: "confirm extension \"vector\" is enabled",
|
|
120
|
-
sql: "SELECT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector')"
|
|
121
|
-
}]
|
|
122
|
-
}];
|
|
123
|
-
/** Sorted list of invariantIds the baseline migration provides. */
|
|
124
|
-
const PGVECTOR_BASELINE_INVARIANTS = (() => {
|
|
125
|
-
const ids = pgvectorBaselineOps.map((op) => op.invariantId).filter((id) => typeof id === "string");
|
|
126
|
-
return [...new Set(ids)].sort();
|
|
127
|
-
})();
|
|
128
|
-
const baselineMetadataWithoutHash = {
|
|
129
|
-
from: null,
|
|
130
|
-
to: PGVECTOR_STORAGE_HASH,
|
|
131
|
-
fromContract: null,
|
|
132
|
-
toContract: pgvectorContract,
|
|
133
|
-
hints: {
|
|
134
|
-
used: [],
|
|
135
|
-
applied: [],
|
|
136
|
-
plannerVersion: "2.0.0"
|
|
137
|
-
},
|
|
138
|
-
labels: [],
|
|
139
|
-
providedInvariants: PGVECTOR_BASELINE_INVARIANTS,
|
|
140
|
-
createdAt: "2026-06-01T00:00:00.000Z"
|
|
141
|
-
};
|
|
142
|
-
/**
|
|
143
|
-
* Baseline migration package the descriptor publishes via
|
|
144
|
-
* `contractSpace.migrations`. The framework's emitter writes this to
|
|
145
|
-
* `migrations/pgvector/<dirName>/{manifest,ops,contract}.json` in the
|
|
146
|
-
* user's repo at `migrate` time.
|
|
147
|
-
*/
|
|
148
|
-
const pgvectorBaselineMigration = {
|
|
149
|
-
dirName: PGVECTOR_BASELINE_MIGRATION_NAME,
|
|
150
|
-
metadata: {
|
|
151
|
-
...baselineMetadataWithoutHash,
|
|
152
|
-
migrationHash: computeMigrationHash(baselineMetadataWithoutHash, pgvectorBaselineOps)
|
|
153
|
-
},
|
|
154
|
-
ops: pgvectorBaselineOps
|
|
155
|
-
};
|
|
156
|
-
/**
|
|
157
|
-
* Pinned head ref the descriptor publishes. The framework writes this
|
|
158
|
-
* verbatim to `migrations/pgvector/refs/head.json` (with `invariants`
|
|
159
|
-
* sorted alphabetically per the canonicalisation rules); the runner's
|
|
160
|
-
* `findPathWithDecision` step consults `head.json` to decide which
|
|
161
|
-
* migrations need to apply.
|
|
162
|
-
*/
|
|
163
|
-
const pgvectorHeadRef = {
|
|
164
|
-
hash: PGVECTOR_STORAGE_HASH,
|
|
165
|
-
invariants: PGVECTOR_BASELINE_INVARIANTS
|
|
166
|
-
};
|
|
167
148
|
//#endregion
|
|
168
149
|
//#region src/exports/control.ts
|
|
169
150
|
const PGVECTOR_CODEC_ID = "pg/vector@1";
|
|
151
|
+
const BASELINE_DIR_NAME = "20260601T0000_install_vector_extension";
|
|
170
152
|
function buildVectorIdentityValue(typeParams) {
|
|
171
153
|
const length = typeParams?.["length"];
|
|
172
154
|
if (typeof length !== "number" || !Number.isInteger(length) || length <= 0) return null;
|
|
@@ -180,11 +162,15 @@ const vectorControlPlaneHooks = {
|
|
|
180
162
|
},
|
|
181
163
|
resolveIdentityValue: ({ typeParams }) => buildVectorIdentityValue(typeParams)
|
|
182
164
|
};
|
|
183
|
-
const pgvectorContractSpace = {
|
|
184
|
-
contractJson:
|
|
185
|
-
migrations: [
|
|
186
|
-
|
|
187
|
-
|
|
165
|
+
const pgvectorContractSpace = contractSpaceFromJson({
|
|
166
|
+
contractJson: contract_default,
|
|
167
|
+
migrations: [{
|
|
168
|
+
dirName: BASELINE_DIR_NAME,
|
|
169
|
+
metadata: migration_default,
|
|
170
|
+
ops: ops_default
|
|
171
|
+
}],
|
|
172
|
+
headRef: head_default
|
|
173
|
+
});
|
|
188
174
|
const pgvectorExtensionDescriptor = {
|
|
189
175
|
...pgvectorPackMeta,
|
|
190
176
|
id: PGVECTOR_SPACE_ID,
|
package/dist/control.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control.mjs","names":[],"sources":["../src/core/contract-space-constants.ts","../src/core/contract.ts","../src/core/migrations.ts","../src/exports/control.ts"],"sourcesContent":["/**\n * Static names and identifiers used across pgvector's contract space.\n *\n * Centralised here so the contract IR (`./contract`), the baseline\n * migration ops (`./migrations`), the head ref, and the descriptor\n * (`../exports/control`) all reference the same values without typos.\n *\n * The space identifier `'pgvector'` is what the framework writes to\n * `migrations/pgvector/` in the user's repo and what the marker table's\n * `space` column carries for pgvector-owned rows.\n *\n * The `pgvector:*` invariantId namespace is locked here — once\n * published, an invariantId is immutable so downstream consumers can\n * reference it by literal string match.\n */\n\nexport const PGVECTOR_SPACE_ID = 'pgvector' as const;\n\nexport const PGVECTOR_NATIVE_TYPE = 'vector' as const;\n\nexport const PGVECTOR_BASELINE_MIGRATION_NAME = '20260601T0000_install_vector_extension' as const;\n\n/**\n * `pgvector:*` invariantIds emitted by the baseline migration. Each id,\n * once published, is immutable: downstream consumers (other extensions,\n * the marker table) reference them by literal string match.\n */\nexport const PGVECTOR_INVARIANTS = {\n installVector: 'pgvector:install-vector-v1',\n} as const;\n","/**\n * pgvector contract space — declares the parameterised native type\n * `vector(N)` that user columns can name as `nativeType`.\n *\n * Unlike CipherStash's typed objects (composite types / domains / enums\n * — deferred behind `meta.cipherstashFutureIR` until the IR vocabulary\n * gains first-class support), pgvector's `vector` is a parameterised\n * native type and *is* representable in today's IR via\n * {@link StorageTypeInstance}: `{ codecId, nativeType, typeParams }`.\n * The contract registers a representative instance under\n * `storage.types.vector` so the verifier sees the type as part of\n * pgvector's space contribution and so the pinned `contract.json` on\n * disk is materially distinct from an empty space.\n *\n * Per-column instances on the user's side carry concrete\n * `typeParams.length` (e.g. `vector(1536)`); the registration here\n * declares the parameterised shape — it is not consumed as a literal\n * column type by any user table.\n */\n\nimport { computeStorageHash } from '@prisma-next/contract/hashing';\nimport { type Contract, coreHash, profileHash } from '@prisma-next/contract/types';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\nimport { VECTOR_CODEC_ID } from './constants';\nimport { PGVECTOR_NATIVE_TYPE } from './contract-space-constants';\n\nconst TARGET = 'postgres' as const;\nconst TARGET_FAMILY = 'sql' as const;\n\n/**\n * Storage body for the contract — pgvector ships no tables of its own;\n * the `vector` parameterised native type is registered under\n * `storage.types` so pgvector's IR contribution is non-empty and the\n * pinned `contract.json` on disk differs materially from an empty\n * extension space.\n *\n * Authored without `storageHash` here so {@link computeStorageHash} can\n * digest the canonical body (the hashing pipeline panics if asked to\n * hash an object that already carries its own output — see\n * `assertDescriptorSelfConsistency`'s storage-hash strip).\n */\nconst storageBody = {\n tables: {},\n types: {\n [PGVECTOR_NATIVE_TYPE]: {\n codecId: VECTOR_CODEC_ID,\n nativeType: PGVECTOR_NATIVE_TYPE,\n typeParams: {},\n },\n },\n};\n\n/** Content-addressed hash of pgvector's storage IR. */\nexport const PGVECTOR_STORAGE_HASH = computeStorageHash({\n target: TARGET,\n targetFamily: TARGET_FAMILY,\n storage: storageBody,\n});\n\n/** pgvector's contract value, exposed via the descriptor's `contractSpace.contractJson`. */\nexport const pgvectorContract: Contract<SqlStorage> = {\n target: TARGET,\n targetFamily: TARGET_FAMILY,\n roots: {},\n models: {},\n capabilities: {},\n extensionPacks: {},\n meta: {},\n profileHash: profileHash('pgvector-extension-profile-v1'),\n storage: {\n ...storageBody,\n storageHash: coreHash(PGVECTOR_STORAGE_HASH),\n },\n};\n","/**\n * pgvector contract space — baseline migration package.\n *\n * An extension's `contractSpace.migrations` is a list of in-memory\n * `MigrationPackage` values whose `ops` carry framework-level\n * `MigrationPlanOperation`s. The SQL family runner reads the additional\n * runtime fields (`target`, `precheck`, `execute`, `postcheck`) at\n * apply time.\n *\n * Ships a single baseline migration whose only op is\n * {@link installVectorExtensionOp} — it carries the\n * `CREATE EXTENSION IF NOT EXISTS vector` DDL plus a postcondition that\n * confirms the extension landed. Mirrors the prior\n * `databaseDependencies.init[0]` shape (precheck / execute / postcheck)\n * but as a `MigrationPackage` op so the framework's per-space runner /\n * verifier can manage it the same way it manages an application's own\n * migrations.\n *\n * The op carries the stable `pgvector:install-vector-v1` invariantId —\n * once published it is immutable.\n */\n\nimport type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';\nimport type {\n ContractSpaceHeadRef,\n MigrationPackage,\n MigrationPlanOperation,\n} from '@prisma-next/framework-components/control';\nimport { computeMigrationHash } from '@prisma-next/migration-tools/hash';\nimport { PGVECTOR_STORAGE_HASH, pgvectorContract } from './contract';\nimport { PGVECTOR_BASELINE_MIGRATION_NAME, PGVECTOR_INVARIANTS } from './contract-space-constants';\n\n/**\n * Postgres-style `target.details` shape the SQL runner consumes when\n * executing extension-space ops. pgvector targets only Postgres;\n * locking the target id here keeps the per-op `target` literal narrow\n * without coupling to the Postgres adapter package's\n * `PostgresPlanTargetDetails`.\n */\ntype PostgresTargetDetails = {\n readonly schema: string;\n readonly objectType: 'extension';\n readonly name: string;\n};\n\nconst installVectorExtensionOp: SqlMigrationPlanOperation<unknown> = {\n id: 'pgvector.install-vector-extension',\n label: 'Enable extension \"vector\"',\n operationClass: 'additive',\n invariantId: PGVECTOR_INVARIANTS.installVector,\n target: {\n id: 'postgres',\n details: {\n schema: 'public',\n objectType: 'extension',\n name: 'vector',\n } satisfies PostgresTargetDetails,\n },\n precheck: [\n {\n description: 'verify extension \"vector\" is not already enabled',\n sql: \"SELECT NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector')\",\n },\n ],\n execute: [\n {\n description: 'create extension \"vector\"',\n sql: 'CREATE EXTENSION IF NOT EXISTS vector',\n },\n ],\n postcheck: [\n {\n description: 'confirm extension \"vector\" is enabled',\n sql: \"SELECT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector')\",\n },\n ],\n};\n\nconst pgvectorBaselineOps: readonly MigrationPlanOperation[] = [installVectorExtensionOp];\n\n/** Sorted list of invariantIds the baseline migration provides. */\nexport const PGVECTOR_BASELINE_INVARIANTS: readonly string[] = (() => {\n const ids = pgvectorBaselineOps\n .map((op) => op.invariantId)\n .filter((id): id is string => typeof id === 'string');\n return [...new Set(ids)].sort();\n})();\n\nconst baselineMetadataWithoutHash: Omit<MigrationPackage['metadata'], 'migrationHash'> = {\n from: null,\n to: PGVECTOR_STORAGE_HASH,\n fromContract: null,\n toContract: pgvectorContract,\n hints: { used: [], applied: [], plannerVersion: '2.0.0' },\n labels: [],\n providedInvariants: PGVECTOR_BASELINE_INVARIANTS,\n createdAt: '2026-06-01T00:00:00.000Z',\n};\n\n/**\n * Baseline migration package the descriptor publishes via\n * `contractSpace.migrations`. The framework's emitter writes this to\n * `migrations/pgvector/<dirName>/{manifest,ops,contract}.json` in the\n * user's repo at `migrate` time.\n */\nexport const pgvectorBaselineMigration: MigrationPackage = {\n dirName: PGVECTOR_BASELINE_MIGRATION_NAME,\n metadata: {\n ...baselineMetadataWithoutHash,\n migrationHash: computeMigrationHash(baselineMetadataWithoutHash, pgvectorBaselineOps),\n },\n ops: pgvectorBaselineOps,\n};\n\n/**\n * Pinned head ref the descriptor publishes. The framework writes this\n * verbatim to `migrations/pgvector/refs/head.json` (with `invariants`\n * sorted alphabetically per the canonicalisation rules); the runner's\n * `findPathWithDecision` step consults `head.json` to decide which\n * migrations need to apply.\n */\nexport const pgvectorHeadRef: ContractSpaceHeadRef = {\n hash: PGVECTOR_STORAGE_HASH,\n invariants: PGVECTOR_BASELINE_INVARIANTS,\n};\n","/**\n * Control-plane descriptor for the pgvector extension.\n *\n * Exposes a `contractSpace` so the framework's per-space planner /\n * runner / verifier (project: extension-contract-spaces, M1+M2)\n * manages the pgvector extension's database scaffolding the same way\n * it manages an application's own schema. The descriptor is consumed\n * by the framework only at authoring time (`migrate`); apply / verify\n * paths read the user's repo (`migrations/pgvector/...`) instead — see\n * project spec NFR3 / FR2 / FR10.\n *\n * `databaseDependencies` is intentionally absent — pgvector was\n * migrated off the legacy `databaseDependencies.init` mechanism in M4\n * (project spec FR13). The `CREATE EXTENSION IF NOT EXISTS vector`\n * DDL the legacy entry carried now lives as the body of the\n * `installVectorExtension` op inside the baseline migration package\n * (`../core/migrations.ts`). Presence of `contractSpace` is the\n * shipping-strategy gate: the framework loads the contract space and\n * ignores any `databaseDependencies` block (project plan §\n * \"Shipping Strategy\"). M5 removes the field at the framework level.\n */\n\nimport type { Contract } from '@prisma-next/contract/types';\nimport type {\n CodecControlHooks,\n SqlControlExtensionDescriptor,\n} from '@prisma-next/family-sql/control';\nimport type { ContractSpace } from '@prisma-next/framework-components/control';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\nimport { pgvectorContract } from '../core/contract';\nimport { PGVECTOR_SPACE_ID } from '../core/contract-space-constants';\nimport { pgvectorPackMeta, pgvectorQueryOperations } from '../core/descriptor-meta';\nimport { pgvectorBaselineMigration, pgvectorHeadRef } from '../core/migrations';\n\nconst PGVECTOR_CODEC_ID = 'pg/vector@1' as const;\n\nfunction buildVectorIdentityValue(typeParams: Record<string, unknown> | undefined): string | null {\n const length = typeParams?.['length'];\n if (typeof length !== 'number' || !Number.isInteger(length) || length <= 0) {\n return null;\n }\n\n const zeroVector = `[${new Array(length).fill('0').join(',')}]`;\n return `'${zeroVector}'::vector`;\n}\n\nconst vectorControlPlaneHooks: CodecControlHooks = {\n expandNativeType: ({ nativeType, typeParams }) => {\n const length = typeParams?.['length'];\n if (typeof length === 'number' && Number.isInteger(length) && length > 0) {\n return `${nativeType}(${length})`;\n }\n return nativeType;\n },\n resolveIdentityValue: ({ typeParams }) => buildVectorIdentityValue(typeParams),\n};\n\nconst pgvectorContractSpace: ContractSpace<Contract<SqlStorage>> = {\n contractJson: pgvectorContract,\n migrations: [pgvectorBaselineMigration],\n headRef: pgvectorHeadRef,\n};\n\nconst pgvectorExtensionDescriptor: SqlControlExtensionDescriptor<'postgres'> = {\n ...pgvectorPackMeta,\n id: PGVECTOR_SPACE_ID,\n contractSpace: pgvectorContractSpace,\n types: {\n ...pgvectorPackMeta.types,\n codecTypes: {\n ...pgvectorPackMeta.types.codecTypes,\n controlPlaneHooks: {\n [PGVECTOR_CODEC_ID]: vectorControlPlaneHooks,\n },\n },\n },\n queryOperations: () => pgvectorQueryOperations(),\n create: () => ({\n familyId: 'sql' as const,\n targetId: 'postgres' as const,\n }),\n};\n\nexport { pgvectorExtensionDescriptor };\nexport default pgvectorExtensionDescriptor;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAgBA,MAAa,oBAAoB;AAEjC,MAAa,uBAAuB;AAEpC,MAAa,mCAAmC;;;;;;AAOhD,MAAa,sBAAsB,EACjC,eAAe,8BAChB;;;;;;;;;;;;;;;;;;;;;;ACHD,MAAM,SAAS;AACf,MAAM,gBAAgB;;;;;;;;;;;;;AActB,MAAM,cAAc;CAClB,QAAQ,EAAE;CACV,OAAO,GACJ,uBAAuB;EACtB,SAAS;EACT,YAAY;EACZ,YAAY,EAAE;EACf,EACF;CACF;;AAGD,MAAa,wBAAwB,mBAAmB;CACtD,QAAQ;CACR,cAAc;CACd,SAAS;CACV,CAAC;;AAGF,MAAa,mBAAyC;CACpD,QAAQ;CACR,cAAc;CACd,OAAO,EAAE;CACT,QAAQ,EAAE;CACV,cAAc,EAAE;CAChB,gBAAgB,EAAE;CAClB,MAAM,EAAE;CACR,aAAa,YAAY,gCAAgC;CACzD,SAAS;EACP,GAAG;EACH,aAAa,SAAS,sBAAsB;EAC7C;CACF;;;ACKD,MAAM,sBAAyD,CAAC;CAhC9D,IAAI;CACJ,OAAO;CACP,gBAAgB;CAChB,aAAa,oBAAoB;CACjC,QAAQ;EACN,IAAI;EACJ,SAAS;GACP,QAAQ;GACR,YAAY;GACZ,MAAM;GACP;EACF;CACD,UAAU,CACR;EACE,aAAa;EACb,KAAK;EACN,CACF;CACD,SAAS,CACP;EACE,aAAa;EACb,KAAK;EACN,CACF;CACD,WAAW,CACT;EACE,aAAa;EACb,KAAK;EACN,CACF;CAGqF,CAAC;;AAGzF,MAAa,sCAAyD;CACpE,MAAM,MAAM,oBACT,KAAK,OAAO,GAAG,YAAY,CAC3B,QAAQ,OAAqB,OAAO,OAAO,SAAS;CACvD,OAAO,CAAC,GAAG,IAAI,IAAI,IAAI,CAAC,CAAC,MAAM;IAC7B;AAEJ,MAAM,8BAAmF;CACvF,MAAM;CACN,IAAI;CACJ,cAAc;CACd,YAAY;CACZ,OAAO;EAAE,MAAM,EAAE;EAAE,SAAS,EAAE;EAAE,gBAAgB;EAAS;CACzD,QAAQ,EAAE;CACV,oBAAoB;CACpB,WAAW;CACZ;;;;;;;AAQD,MAAa,4BAA8C;CACzD,SAAS;CACT,UAAU;EACR,GAAG;EACH,eAAe,qBAAqB,6BAA6B,oBAAoB;EACtF;CACD,KAAK;CACN;;;;;;;;AASD,MAAa,kBAAwC;CACnD,MAAM;CACN,YAAY;CACb;;;AC1FD,MAAM,oBAAoB;AAE1B,SAAS,yBAAyB,YAAgE;CAChG,MAAM,SAAS,aAAa;CAC5B,IAAI,OAAO,WAAW,YAAY,CAAC,OAAO,UAAU,OAAO,IAAI,UAAU,GACvE,OAAO;CAIT,OAAO,IAAI,IADY,IAAI,MAAM,OAAO,CAAC,KAAK,IAAI,CAAC,KAAK,IAAI,CAAC,GACvC;;AAGxB,MAAM,0BAA6C;CACjD,mBAAmB,EAAE,YAAY,iBAAiB;EAChD,MAAM,SAAS,aAAa;EAC5B,IAAI,OAAO,WAAW,YAAY,OAAO,UAAU,OAAO,IAAI,SAAS,GACrE,OAAO,GAAG,WAAW,GAAG,OAAO;EAEjC,OAAO;;CAET,uBAAuB,EAAE,iBAAiB,yBAAyB,WAAW;CAC/E;AAED,MAAM,wBAA6D;CACjE,cAAc;CACd,YAAY,CAAC,0BAA0B;CACvC,SAAS;CACV;AAED,MAAM,8BAAyE;CAC7E,GAAG;CACH,IAAI;CACJ,eAAe;CACf,OAAO;EACL,GAAG,iBAAiB;EACpB,YAAY;GACV,GAAG,iBAAiB,MAAM;GAC1B,mBAAmB,GAChB,oBAAoB,yBACtB;GACF;EACF;CACD,uBAAuB,yBAAyB;CAChD,eAAe;EACb,UAAU;EACV,UAAU;EACX;CACF"}
|
|
1
|
+
{"version":3,"file":"control.mjs","names":["baselineMetadata","baselineOps"],"sources":["../migrations/20260601T0000_install_vector_extension/migration.json","../migrations/20260601T0000_install_vector_extension/ops.json","../migrations/refs/head.json","../src/contract.json","../src/core/contract-space-constants.ts","../src/exports/control.ts"],"sourcesContent":["","","","","/**\n * Static names and identifiers used across pgvector's contract space.\n *\n * Centralised here so the contract IR (`./contract`), the baseline\n * migration ops (`./migrations`), the head ref, and the descriptor\n * (`../exports/control`) all reference the same values without typos.\n *\n * The space identifier `'pgvector'` is what the framework writes to\n * `migrations/` in the user's repo and what the marker table's\n * `space` column carries for pgvector-owned rows.\n *\n * The `pgvector:*` invariantId namespace is locked here — once\n * published, an invariantId is immutable so downstream consumers can\n * reference it by literal string match.\n */\n\nexport const PGVECTOR_SPACE_ID = 'pgvector' as const;\n\nexport const PGVECTOR_NATIVE_TYPE = 'vector' as const;\n\nexport const PGVECTOR_BASELINE_MIGRATION_NAME = '20260601T0000_install_vector_extension' as const;\n\n/**\n * `pgvector:*` invariantIds emitted by the baseline migration. Each id,\n * once published, is immutable: downstream consumers (other extensions,\n * the marker table) reference them by literal string match.\n */\nexport const PGVECTOR_INVARIANTS = {\n installVector: 'pgvector:install-vector-v1',\n} as const;\n","/**\n * Control-plane descriptor for the pgvector extension.\n *\n * **Contract-space package layout.** The extension's contract\n * + migrations are emitted by the same pipeline application authors use:\n *\n * `prisma-next contract emit` → `<package>/src/contract.{json,d.ts}`\n * `prisma-next migration plan` → `<package>/migrations/<dir>/...`\n *\n * The descriptor wires those JSON artefacts via JSON-import declarations\n * so they flow through the consuming application's module resolver\n * without filesystem assumptions, and synthesises the canonical\n * {@link import('@prisma-next/framework-components/control').MigrationPackage}\n * shape for the framework's runner / verifier to consume. Readers in\n * `@prisma-next/migration-tools` add `dirPath` when loading from disk\n * (`OnDiskMigrationPackage`); descriptor-bundled packages do not need\n * it because the framework reads them directly from the descriptor.\n *\n * Wired surfaces:\n *\n * - `contractSpace.{contractJson,migrations,headRef}` — sourced from\n * the on-disk artefacts emitted by `build:contract-space`.\n * - `types.codecTypes.controlPlaneHooks[PGVECTOR_CODEC_ID]` — codec\n * control hooks (`expandNativeType`, `resolveIdentityValue`) the\n * SQL planner extracts via `extractCodecControlHooks` and uses to\n * render `vector(N)` column types and the canonical zero-vector\n * identity literal.\n *\n * @see docs/architecture docs/adrs/ADR 212 - Contract spaces.md\n * (contract-space package layout convention).\n */\n\nimport type { Contract } from '@prisma-next/contract/types';\nimport type {\n CodecControlHooks,\n SqlControlExtensionDescriptor,\n} from '@prisma-next/family-sql/control';\nimport { contractSpaceFromJson } from '@prisma-next/migration-tools/spaces';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\nimport baselineMetadata from '../../migrations/20260601T0000_install_vector_extension/migration.json' with {\n type: 'json',\n};\nimport baselineOps from '../../migrations/20260601T0000_install_vector_extension/ops.json' with {\n type: 'json',\n};\nimport headRef from '../../migrations/refs/head.json' with { type: 'json' };\nimport contractJson from '../contract.json' with { type: 'json' };\nimport { PGVECTOR_SPACE_ID } from '../core/contract-space-constants';\nimport { pgvectorPackMeta, pgvectorQueryOperations } from '../core/descriptor-meta';\n\nconst PGVECTOR_CODEC_ID = 'pg/vector@1' as const;\nconst BASELINE_DIR_NAME = '20260601T0000_install_vector_extension';\n\nfunction buildVectorIdentityValue(typeParams: Record<string, unknown> | undefined): string | null {\n const length = typeParams?.['length'];\n if (typeof length !== 'number' || !Number.isInteger(length) || length <= 0) {\n return null;\n }\n\n const zeroVector = `[${new Array(length).fill('0').join(',')}]`;\n return `'${zeroVector}'::vector`;\n}\n\nconst vectorControlPlaneHooks: CodecControlHooks = {\n expandNativeType: ({ nativeType, typeParams }) => {\n const length = typeParams?.['length'];\n if (typeof length === 'number' && Number.isInteger(length) && length > 0) {\n return `${nativeType}(${length})`;\n }\n return nativeType;\n },\n resolveIdentityValue: ({ typeParams }) => buildVectorIdentityValue(typeParams),\n};\n\nconst pgvectorContractSpace = contractSpaceFromJson<Contract<SqlStorage>>({\n contractJson,\n migrations: [\n {\n dirName: BASELINE_DIR_NAME,\n metadata: baselineMetadata,\n ops: baselineOps,\n },\n ],\n headRef,\n});\n\nconst pgvectorExtensionDescriptor: SqlControlExtensionDescriptor<'postgres'> = {\n ...pgvectorPackMeta,\n id: PGVECTOR_SPACE_ID,\n contractSpace: pgvectorContractSpace,\n types: {\n ...pgvectorPackMeta.types,\n codecTypes: {\n ...pgvectorPackMeta.types.codecTypes,\n controlPlaneHooks: {\n [PGVECTOR_CODEC_ID]: vectorControlPlaneHooks,\n },\n },\n },\n queryOperations: () => pgvectorQueryOperations(),\n create: () => ({\n familyId: 'sql' as const,\n targetId: 'postgres' as const,\n }),\n};\n\nexport { pgvectorExtensionDescriptor };\nexport default pgvectorExtensionDescriptor;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AIgBA,MAAa,oBAAoB;;;ACkCjC,MAAM,oBAAoB;AAC1B,MAAM,oBAAoB;AAE1B,SAAS,yBAAyB,YAAgE;CAChG,MAAM,SAAS,aAAa;CAC5B,IAAI,OAAO,WAAW,YAAY,CAAC,OAAO,UAAU,OAAO,IAAI,UAAU,GACvE,OAAO;CAIT,OAAO,IAAI,IADY,IAAI,MAAM,OAAO,CAAC,KAAK,IAAI,CAAC,KAAK,IAAI,CAAC,GACvC;;AAGxB,MAAM,0BAA6C;CACjD,mBAAmB,EAAE,YAAY,iBAAiB;EAChD,MAAM,SAAS,aAAa;EAC5B,IAAI,OAAO,WAAW,YAAY,OAAO,UAAU,OAAO,IAAI,SAAS,GACrE,OAAO,GAAG,WAAW,GAAG,OAAO;EAEjC,OAAO;;CAET,uBAAuB,EAAE,iBAAiB,yBAAyB,WAAW;CAC/E;AAED,MAAM,wBAAwB,sBAA4C;CACxE,cAAA;CACA,YAAY,CACV;EACE,SAAS;EACT,UAAUA;EACV,KAAKC;EACN,CACF;CACD,SAAA;CACD,CAAC;AAEF,MAAM,8BAAyE;CAC7E,GAAG;CACH,IAAI;CACJ,eAAe;CACf,OAAO;EACL,GAAG,iBAAiB;EACpB,YAAY;GACV,GAAG,iBAAiB,MAAM;GAC1B,mBAAmB,GAChB,oBAAoB,yBACtB;GACF;EACF;CACD,uBAAuB,yBAAyB;CAChD,eAAe;EACb,UAAU;EACV,UAAU;EACX;CACF"}
|
package/dist/pack.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as CodecTypes } from "./codec-types-
|
|
1
|
+
import { t as CodecTypes } from "./codec-types-CQubO6uQ.mjs";
|
|
2
2
|
import * as _$_prisma_next_framework_components_codec0 from "@prisma-next/framework-components/codec";
|
|
3
3
|
|
|
4
4
|
//#region src/core/descriptor-meta.d.ts
|
package/package.json
CHANGED
|
@@ -1,33 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/extension-pgvector",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0-dev.3",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"@standard-schema/spec": "^1.1.0",
|
|
9
9
|
"arktype": "^2.1.29",
|
|
10
|
-
"@prisma-next/contract": "0.
|
|
11
|
-
"@prisma-next/family-sql": "0.
|
|
12
|
-
"@prisma-next/
|
|
13
|
-
"@prisma-next/
|
|
14
|
-
"@prisma-next/
|
|
15
|
-
"@prisma-next/sql-
|
|
16
|
-
"@prisma-next/sql-
|
|
17
|
-
"@prisma-next/sql-runtime": "0.
|
|
18
|
-
"@prisma-next/
|
|
19
|
-
"@prisma-next/sql-
|
|
10
|
+
"@prisma-next/contract": "0.6.0-dev.3",
|
|
11
|
+
"@prisma-next/family-sql": "0.6.0-dev.3",
|
|
12
|
+
"@prisma-next/contract-authoring": "0.6.0-dev.3",
|
|
13
|
+
"@prisma-next/framework-components": "0.6.0-dev.3",
|
|
14
|
+
"@prisma-next/migration-tools": "0.6.0-dev.3",
|
|
15
|
+
"@prisma-next/sql-contract": "0.6.0-dev.3",
|
|
16
|
+
"@prisma-next/sql-operations": "0.6.0-dev.3",
|
|
17
|
+
"@prisma-next/sql-runtime": "0.6.0-dev.3",
|
|
18
|
+
"@prisma-next/sql-schema-ir": "0.6.0-dev.3",
|
|
19
|
+
"@prisma-next/sql-relational-core": "0.6.0-dev.3"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"tsdown": "0.22.0",
|
|
23
23
|
"typescript": "5.9.3",
|
|
24
24
|
"vitest": "4.1.5",
|
|
25
|
-
"@prisma-next/
|
|
26
|
-
"@prisma-next/
|
|
25
|
+
"@prisma-next/adapter-postgres": "0.6.0-dev.3",
|
|
26
|
+
"@prisma-next/operations": "0.6.0-dev.3",
|
|
27
|
+
"@prisma-next/cli": "0.6.0-dev.3",
|
|
28
|
+
"@prisma-next/sql-contract-ts": "0.6.0-dev.3",
|
|
29
|
+
"@prisma-next/target-postgres": "0.6.0-dev.3",
|
|
27
30
|
"@prisma-next/test-utils": "0.0.1",
|
|
28
31
|
"@prisma-next/tsconfig": "0.0.0",
|
|
29
32
|
"@prisma-next/tsdown": "0.0.0"
|
|
30
33
|
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@prisma-next/adapter-postgres": "0.6.0-dev.3"
|
|
36
|
+
},
|
|
37
|
+
"peerDependenciesMeta": {
|
|
38
|
+
"@prisma-next/adapter-postgres": {
|
|
39
|
+
"optional": true
|
|
40
|
+
}
|
|
41
|
+
},
|
|
31
42
|
"files": [
|
|
32
43
|
"dist",
|
|
33
44
|
"src"
|
|
@@ -47,6 +58,7 @@
|
|
|
47
58
|
"directory": "packages/3-extensions/pgvector"
|
|
48
59
|
},
|
|
49
60
|
"scripts": {
|
|
61
|
+
"build:contract-space": "prisma-next contract emit",
|
|
50
62
|
"build": "tsdown",
|
|
51
63
|
"test": "vitest run",
|
|
52
64
|
"test:coverage": "vitest run --coverage",
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// ⚠️ GENERATED FILE - DO NOT EDIT
|
|
2
|
+
// This file is automatically generated by 'prisma-next contract emit'.
|
|
3
|
+
// To regenerate, run: prisma-next contract emit
|
|
4
|
+
import type { CodecTypes as PgTypes } from '@prisma-next/target-postgres/codec-types';
|
|
5
|
+
import type { JsonValue } from '@prisma-next/target-postgres/codec-types';
|
|
6
|
+
import type { Char } from '@prisma-next/target-postgres/codec-types';
|
|
7
|
+
import type { Varchar } from '@prisma-next/target-postgres/codec-types';
|
|
8
|
+
import type { Numeric } from '@prisma-next/target-postgres/codec-types';
|
|
9
|
+
import type { Bit } from '@prisma-next/target-postgres/codec-types';
|
|
10
|
+
import type { VarBit } from '@prisma-next/target-postgres/codec-types';
|
|
11
|
+
import type { Timestamp } from '@prisma-next/target-postgres/codec-types';
|
|
12
|
+
import type { Timestamptz } from '@prisma-next/target-postgres/codec-types';
|
|
13
|
+
import type { Time } from '@prisma-next/target-postgres/codec-types';
|
|
14
|
+
import type { Timetz } from '@prisma-next/target-postgres/codec-types';
|
|
15
|
+
import type { Interval } from '@prisma-next/target-postgres/codec-types';
|
|
16
|
+
import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types';
|
|
17
|
+
|
|
18
|
+
import type {
|
|
19
|
+
ContractWithTypeMaps,
|
|
20
|
+
TypeMaps as TypeMapsType,
|
|
21
|
+
} from '@prisma-next/sql-contract/types';
|
|
22
|
+
import type {
|
|
23
|
+
Contract as ContractType,
|
|
24
|
+
ExecutionHashBase,
|
|
25
|
+
ProfileHashBase,
|
|
26
|
+
StorageHashBase,
|
|
27
|
+
} from '@prisma-next/contract/types';
|
|
28
|
+
|
|
29
|
+
export type StorageHash =
|
|
30
|
+
StorageHashBase<'sha256:382dae5bb1548e62cbc449530ea08a9ce0a0dbb280401e9588642223f33783ae'>;
|
|
31
|
+
export type ExecutionHash = ExecutionHashBase<string>;
|
|
32
|
+
export type ProfileHash =
|
|
33
|
+
ProfileHashBase<'sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e'>;
|
|
34
|
+
|
|
35
|
+
export type CodecTypes = PgTypes;
|
|
36
|
+
export type LaneCodecTypes = CodecTypes;
|
|
37
|
+
export type QueryOperationTypes = PgAdapterQueryOps<CodecTypes>;
|
|
38
|
+
type DefaultLiteralValue<CodecId extends string, _Encoded> = CodecId extends keyof CodecTypes
|
|
39
|
+
? CodecTypes[CodecId]['output']
|
|
40
|
+
: _Encoded;
|
|
41
|
+
|
|
42
|
+
export type FieldOutputTypes = Record<string, never>;
|
|
43
|
+
export type FieldInputTypes = Record<string, never>;
|
|
44
|
+
export type TypeMaps = TypeMapsType<
|
|
45
|
+
CodecTypes,
|
|
46
|
+
QueryOperationTypes,
|
|
47
|
+
FieldOutputTypes,
|
|
48
|
+
FieldInputTypes
|
|
49
|
+
>;
|
|
50
|
+
|
|
51
|
+
type ContractBase = ContractType<
|
|
52
|
+
{
|
|
53
|
+
readonly tables: {};
|
|
54
|
+
readonly types: {
|
|
55
|
+
readonly vector: {
|
|
56
|
+
readonly codecId: 'pg/vector@1';
|
|
57
|
+
readonly nativeType: 'vector';
|
|
58
|
+
readonly typeParams: Record<string, never>;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
readonly storageHash: StorageHash;
|
|
62
|
+
},
|
|
63
|
+
Record<string, never>
|
|
64
|
+
> & {
|
|
65
|
+
readonly target: 'postgres';
|
|
66
|
+
readonly targetFamily: 'sql';
|
|
67
|
+
readonly roots: Record<string, string>;
|
|
68
|
+
readonly capabilities: {
|
|
69
|
+
readonly postgres: {
|
|
70
|
+
readonly jsonAgg: true;
|
|
71
|
+
readonly lateral: true;
|
|
72
|
+
readonly limit: true;
|
|
73
|
+
readonly orderBy: true;
|
|
74
|
+
readonly returning: true;
|
|
75
|
+
};
|
|
76
|
+
readonly sql: {
|
|
77
|
+
readonly defaultInInsert: true;
|
|
78
|
+
readonly enums: true;
|
|
79
|
+
readonly returning: true;
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
readonly extensionPacks: {};
|
|
83
|
+
readonly meta: {};
|
|
84
|
+
|
|
85
|
+
readonly profileHash: ProfileHash;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export type Contract = ContractWithTypeMaps<ContractBase, TypeMaps>;
|
|
89
|
+
|
|
90
|
+
export type Tables = Contract['storage']['tables'];
|
|
91
|
+
export type Models = Contract['models'];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "1",
|
|
3
|
+
"targetFamily": "sql",
|
|
4
|
+
"target": "postgres",
|
|
5
|
+
"profileHash": "sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e",
|
|
6
|
+
"roots": {},
|
|
7
|
+
"models": {},
|
|
8
|
+
"storage": {
|
|
9
|
+
"storageHash": "sha256:382dae5bb1548e62cbc449530ea08a9ce0a0dbb280401e9588642223f33783ae",
|
|
10
|
+
"tables": {},
|
|
11
|
+
"types": {
|
|
12
|
+
"vector": {
|
|
13
|
+
"codecId": "pg/vector@1",
|
|
14
|
+
"nativeType": "vector",
|
|
15
|
+
"typeParams": {}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"capabilities": {
|
|
20
|
+
"postgres": {
|
|
21
|
+
"jsonAgg": true,
|
|
22
|
+
"lateral": true,
|
|
23
|
+
"limit": true,
|
|
24
|
+
"orderBy": true,
|
|
25
|
+
"returning": true
|
|
26
|
+
},
|
|
27
|
+
"sql": {
|
|
28
|
+
"defaultInInsert": true,
|
|
29
|
+
"enums": true,
|
|
30
|
+
"returning": true
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"extensionPacks": {},
|
|
34
|
+
"meta": {},
|
|
35
|
+
"_generated": {
|
|
36
|
+
"warning": "⚠️ GENERATED FILE - DO NOT EDIT",
|
|
37
|
+
"message": "This file is automatically generated by \"prisma-next contract emit\".",
|
|
38
|
+
"regenerate": "To regenerate, run: prisma-next contract emit"
|
|
39
|
+
}
|
|
40
|
+
}
|
package/src/contract.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TS contract source for the `extension-pgvector` package.
|
|
3
|
+
*
|
|
4
|
+
* Authored against the contract-space package layout convention. The
|
|
5
|
+
* same emit pipeline application authors use is applied here:
|
|
6
|
+
*
|
|
7
|
+
* `prisma-next contract emit` → `<package>/src/contract.{json,d.ts}`
|
|
8
|
+
* `prisma-next migration plan` → `<package>/migrations/<dirName>/`
|
|
9
|
+
*
|
|
10
|
+
* The descriptor at `src/exports/control.ts` then wires the emitted
|
|
11
|
+
* JSON artefacts via JSON-import declarations.
|
|
12
|
+
*
|
|
13
|
+
* ## IR coverage
|
|
14
|
+
*
|
|
15
|
+
* pgvector ships **no tables** of its own. The single object the
|
|
16
|
+
* extension contributes to the contract IR is the parameterised native
|
|
17
|
+
* type `vector(N)`, registered under `storage.types`. Per-column
|
|
18
|
+
* instances on the user's side carry concrete `typeParams.length`
|
|
19
|
+
* (e.g. `vector(1536)`); the registration here declares the
|
|
20
|
+
* parameterised shape so the verifier sees `vector` as part of
|
|
21
|
+
* pgvector's space contribution and so the pinned `contract.json` on
|
|
22
|
+
* disk is materially distinct from an empty space.
|
|
23
|
+
*
|
|
24
|
+
* Unlike CipherStash's deferred typed objects (composite types /
|
|
25
|
+
* domains / enums — IR vocabulary deferral, see
|
|
26
|
+
* `packages/3-extensions/cipherstash/src/contract.prisma`),
|
|
27
|
+
* pgvector's `vector` IS representable in today's IR via
|
|
28
|
+
* {@link StorageTypeInstance}.
|
|
29
|
+
*
|
|
30
|
+
* ## Why TS, not PSL
|
|
31
|
+
*
|
|
32
|
+
* The contract-space package layout convention prefers PSL
|
|
33
|
+
* (`src/contract.prisma`). pgvector is the narrow exception called out
|
|
34
|
+
* in the convention: PSL's `types {}` block instantiates parameterised
|
|
35
|
+
* types at app authoring time (`Vector1536 = pgvector.Vector(1536)`)
|
|
36
|
+
* but has no surface for an extension to register the parameterised
|
|
37
|
+
* BASE type itself (the `storage.types.vector` entry with empty
|
|
38
|
+
* `typeParams` shown below). Until PSL grows that surface, this
|
|
39
|
+
* extension keeps its contract source in TS.
|
|
40
|
+
*
|
|
41
|
+
* @see docs/architecture docs/adrs/ADR 212 - Contract spaces.md
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
import sqlFamily from '@prisma-next/family-sql/pack';
|
|
45
|
+
import { defineContract } from '@prisma-next/sql-contract-ts/contract-builder';
|
|
46
|
+
import postgresPack from '@prisma-next/target-postgres/pack';
|
|
47
|
+
import { VECTOR_CODEC_ID } from './core/constants';
|
|
48
|
+
import { PGVECTOR_NATIVE_TYPE } from './core/contract-space-constants';
|
|
49
|
+
|
|
50
|
+
export const contract = defineContract(
|
|
51
|
+
{
|
|
52
|
+
family: sqlFamily,
|
|
53
|
+
target: postgresPack,
|
|
54
|
+
},
|
|
55
|
+
() => ({
|
|
56
|
+
types: {
|
|
57
|
+
[PGVECTOR_NATIVE_TYPE]: {
|
|
58
|
+
codecId: VECTOR_CODEC_ID,
|
|
59
|
+
nativeType: PGVECTOR_NATIVE_TYPE,
|
|
60
|
+
typeParams: {},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
models: {},
|
|
64
|
+
}),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
export default contract;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* (`../exports/control`) all reference the same values without typos.
|
|
7
7
|
*
|
|
8
8
|
* The space identifier `'pgvector'` is what the framework writes to
|
|
9
|
-
* `migrations
|
|
9
|
+
* `migrations/` in the user's repo and what the marker table's
|
|
10
10
|
* `space` column carries for pgvector-owned rows.
|
|
11
11
|
*
|
|
12
12
|
* The `pgvector:*` invariantId namespace is locked here — once
|
package/src/exports/control.ts
CHANGED
|
@@ -1,23 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Control-plane descriptor for the pgvector extension.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* manages the pgvector extension's database scaffolding the same way
|
|
7
|
-
* it manages an application's own schema. The descriptor is consumed
|
|
8
|
-
* by the framework only at authoring time (`migrate`); apply / verify
|
|
9
|
-
* paths read the user's repo (`migrations/pgvector/...`) instead — see
|
|
10
|
-
* project spec NFR3 / FR2 / FR10.
|
|
4
|
+
* **Contract-space package layout.** The extension's contract
|
|
5
|
+
* + migrations are emitted by the same pipeline application authors use:
|
|
11
6
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
7
|
+
* `prisma-next contract emit` → `<package>/src/contract.{json,d.ts}`
|
|
8
|
+
* `prisma-next migration plan` → `<package>/migrations/<dir>/...`
|
|
9
|
+
*
|
|
10
|
+
* The descriptor wires those JSON artefacts via JSON-import declarations
|
|
11
|
+
* so they flow through the consuming application's module resolver
|
|
12
|
+
* without filesystem assumptions, and synthesises the canonical
|
|
13
|
+
* {@link import('@prisma-next/framework-components/control').MigrationPackage}
|
|
14
|
+
* shape for the framework's runner / verifier to consume. Readers in
|
|
15
|
+
* `@prisma-next/migration-tools` add `dirPath` when loading from disk
|
|
16
|
+
* (`OnDiskMigrationPackage`); descriptor-bundled packages do not need
|
|
17
|
+
* it because the framework reads them directly from the descriptor.
|
|
18
|
+
*
|
|
19
|
+
* Wired surfaces:
|
|
20
|
+
*
|
|
21
|
+
* - `contractSpace.{contractJson,migrations,headRef}` — sourced from
|
|
22
|
+
* the on-disk artefacts emitted by `build:contract-space`.
|
|
23
|
+
* - `types.codecTypes.controlPlaneHooks[PGVECTOR_CODEC_ID]` — codec
|
|
24
|
+
* control hooks (`expandNativeType`, `resolveIdentityValue`) the
|
|
25
|
+
* SQL planner extracts via `extractCodecControlHooks` and uses to
|
|
26
|
+
* render `vector(N)` column types and the canonical zero-vector
|
|
27
|
+
* identity literal.
|
|
28
|
+
*
|
|
29
|
+
* @see docs/architecture docs/adrs/ADR 212 - Contract spaces.md
|
|
30
|
+
* (contract-space package layout convention).
|
|
21
31
|
*/
|
|
22
32
|
|
|
23
33
|
import type { Contract } from '@prisma-next/contract/types';
|
|
@@ -25,14 +35,21 @@ import type {
|
|
|
25
35
|
CodecControlHooks,
|
|
26
36
|
SqlControlExtensionDescriptor,
|
|
27
37
|
} from '@prisma-next/family-sql/control';
|
|
28
|
-
import
|
|
38
|
+
import { contractSpaceFromJson } from '@prisma-next/migration-tools/spaces';
|
|
29
39
|
import type { SqlStorage } from '@prisma-next/sql-contract/types';
|
|
30
|
-
import
|
|
40
|
+
import baselineMetadata from '../../migrations/20260601T0000_install_vector_extension/migration.json' with {
|
|
41
|
+
type: 'json',
|
|
42
|
+
};
|
|
43
|
+
import baselineOps from '../../migrations/20260601T0000_install_vector_extension/ops.json' with {
|
|
44
|
+
type: 'json',
|
|
45
|
+
};
|
|
46
|
+
import headRef from '../../migrations/refs/head.json' with { type: 'json' };
|
|
47
|
+
import contractJson from '../contract.json' with { type: 'json' };
|
|
31
48
|
import { PGVECTOR_SPACE_ID } from '../core/contract-space-constants';
|
|
32
49
|
import { pgvectorPackMeta, pgvectorQueryOperations } from '../core/descriptor-meta';
|
|
33
|
-
import { pgvectorBaselineMigration, pgvectorHeadRef } from '../core/migrations';
|
|
34
50
|
|
|
35
51
|
const PGVECTOR_CODEC_ID = 'pg/vector@1' as const;
|
|
52
|
+
const BASELINE_DIR_NAME = '20260601T0000_install_vector_extension';
|
|
36
53
|
|
|
37
54
|
function buildVectorIdentityValue(typeParams: Record<string, unknown> | undefined): string | null {
|
|
38
55
|
const length = typeParams?.['length'];
|
|
@@ -55,11 +72,17 @@ const vectorControlPlaneHooks: CodecControlHooks = {
|
|
|
55
72
|
resolveIdentityValue: ({ typeParams }) => buildVectorIdentityValue(typeParams),
|
|
56
73
|
};
|
|
57
74
|
|
|
58
|
-
const pgvectorContractSpace
|
|
59
|
-
contractJson
|
|
60
|
-
migrations: [
|
|
61
|
-
|
|
62
|
-
|
|
75
|
+
const pgvectorContractSpace = contractSpaceFromJson<Contract<SqlStorage>>({
|
|
76
|
+
contractJson,
|
|
77
|
+
migrations: [
|
|
78
|
+
{
|
|
79
|
+
dirName: BASELINE_DIR_NAME,
|
|
80
|
+
metadata: baselineMetadata,
|
|
81
|
+
ops: baselineOps,
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
headRef,
|
|
85
|
+
});
|
|
63
86
|
|
|
64
87
|
const pgvectorExtensionDescriptor: SqlControlExtensionDescriptor<'postgres'> = {
|
|
65
88
|
...pgvectorPackMeta,
|
package/src/core/contract.ts
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* pgvector contract space — declares the parameterised native type
|
|
3
|
-
* `vector(N)` that user columns can name as `nativeType`.
|
|
4
|
-
*
|
|
5
|
-
* Unlike CipherStash's typed objects (composite types / domains / enums
|
|
6
|
-
* — deferred behind `meta.cipherstashFutureIR` until the IR vocabulary
|
|
7
|
-
* gains first-class support), pgvector's `vector` is a parameterised
|
|
8
|
-
* native type and *is* representable in today's IR via
|
|
9
|
-
* {@link StorageTypeInstance}: `{ codecId, nativeType, typeParams }`.
|
|
10
|
-
* The contract registers a representative instance under
|
|
11
|
-
* `storage.types.vector` so the verifier sees the type as part of
|
|
12
|
-
* pgvector's space contribution and so the pinned `contract.json` on
|
|
13
|
-
* disk is materially distinct from an empty space.
|
|
14
|
-
*
|
|
15
|
-
* Per-column instances on the user's side carry concrete
|
|
16
|
-
* `typeParams.length` (e.g. `vector(1536)`); the registration here
|
|
17
|
-
* declares the parameterised shape — it is not consumed as a literal
|
|
18
|
-
* column type by any user table.
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import { computeStorageHash } from '@prisma-next/contract/hashing';
|
|
22
|
-
import { type Contract, coreHash, profileHash } from '@prisma-next/contract/types';
|
|
23
|
-
import type { SqlStorage } from '@prisma-next/sql-contract/types';
|
|
24
|
-
import { VECTOR_CODEC_ID } from './constants';
|
|
25
|
-
import { PGVECTOR_NATIVE_TYPE } from './contract-space-constants';
|
|
26
|
-
|
|
27
|
-
const TARGET = 'postgres' as const;
|
|
28
|
-
const TARGET_FAMILY = 'sql' as const;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Storage body for the contract — pgvector ships no tables of its own;
|
|
32
|
-
* the `vector` parameterised native type is registered under
|
|
33
|
-
* `storage.types` so pgvector's IR contribution is non-empty and the
|
|
34
|
-
* pinned `contract.json` on disk differs materially from an empty
|
|
35
|
-
* extension space.
|
|
36
|
-
*
|
|
37
|
-
* Authored without `storageHash` here so {@link computeStorageHash} can
|
|
38
|
-
* digest the canonical body (the hashing pipeline panics if asked to
|
|
39
|
-
* hash an object that already carries its own output — see
|
|
40
|
-
* `assertDescriptorSelfConsistency`'s storage-hash strip).
|
|
41
|
-
*/
|
|
42
|
-
const storageBody = {
|
|
43
|
-
tables: {},
|
|
44
|
-
types: {
|
|
45
|
-
[PGVECTOR_NATIVE_TYPE]: {
|
|
46
|
-
codecId: VECTOR_CODEC_ID,
|
|
47
|
-
nativeType: PGVECTOR_NATIVE_TYPE,
|
|
48
|
-
typeParams: {},
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
/** Content-addressed hash of pgvector's storage IR. */
|
|
54
|
-
export const PGVECTOR_STORAGE_HASH = computeStorageHash({
|
|
55
|
-
target: TARGET,
|
|
56
|
-
targetFamily: TARGET_FAMILY,
|
|
57
|
-
storage: storageBody,
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
/** pgvector's contract value, exposed via the descriptor's `contractSpace.contractJson`. */
|
|
61
|
-
export const pgvectorContract: Contract<SqlStorage> = {
|
|
62
|
-
target: TARGET,
|
|
63
|
-
targetFamily: TARGET_FAMILY,
|
|
64
|
-
roots: {},
|
|
65
|
-
models: {},
|
|
66
|
-
capabilities: {},
|
|
67
|
-
extensionPacks: {},
|
|
68
|
-
meta: {},
|
|
69
|
-
profileHash: profileHash('pgvector-extension-profile-v1'),
|
|
70
|
-
storage: {
|
|
71
|
-
...storageBody,
|
|
72
|
-
storageHash: coreHash(PGVECTOR_STORAGE_HASH),
|
|
73
|
-
},
|
|
74
|
-
};
|
package/src/core/migrations.ts
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* pgvector contract space — baseline migration package.
|
|
3
|
-
*
|
|
4
|
-
* An extension's `contractSpace.migrations` is a list of in-memory
|
|
5
|
-
* `MigrationPackage` values whose `ops` carry framework-level
|
|
6
|
-
* `MigrationPlanOperation`s. The SQL family runner reads the additional
|
|
7
|
-
* runtime fields (`target`, `precheck`, `execute`, `postcheck`) at
|
|
8
|
-
* apply time.
|
|
9
|
-
*
|
|
10
|
-
* Ships a single baseline migration whose only op is
|
|
11
|
-
* {@link installVectorExtensionOp} — it carries the
|
|
12
|
-
* `CREATE EXTENSION IF NOT EXISTS vector` DDL plus a postcondition that
|
|
13
|
-
* confirms the extension landed. Mirrors the prior
|
|
14
|
-
* `databaseDependencies.init[0]` shape (precheck / execute / postcheck)
|
|
15
|
-
* but as a `MigrationPackage` op so the framework's per-space runner /
|
|
16
|
-
* verifier can manage it the same way it manages an application's own
|
|
17
|
-
* migrations.
|
|
18
|
-
*
|
|
19
|
-
* The op carries the stable `pgvector:install-vector-v1` invariantId —
|
|
20
|
-
* once published it is immutable.
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';
|
|
24
|
-
import type {
|
|
25
|
-
ContractSpaceHeadRef,
|
|
26
|
-
MigrationPackage,
|
|
27
|
-
MigrationPlanOperation,
|
|
28
|
-
} from '@prisma-next/framework-components/control';
|
|
29
|
-
import { computeMigrationHash } from '@prisma-next/migration-tools/hash';
|
|
30
|
-
import { PGVECTOR_STORAGE_HASH, pgvectorContract } from './contract';
|
|
31
|
-
import { PGVECTOR_BASELINE_MIGRATION_NAME, PGVECTOR_INVARIANTS } from './contract-space-constants';
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Postgres-style `target.details` shape the SQL runner consumes when
|
|
35
|
-
* executing extension-space ops. pgvector targets only Postgres;
|
|
36
|
-
* locking the target id here keeps the per-op `target` literal narrow
|
|
37
|
-
* without coupling to the Postgres adapter package's
|
|
38
|
-
* `PostgresPlanTargetDetails`.
|
|
39
|
-
*/
|
|
40
|
-
type PostgresTargetDetails = {
|
|
41
|
-
readonly schema: string;
|
|
42
|
-
readonly objectType: 'extension';
|
|
43
|
-
readonly name: string;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const installVectorExtensionOp: SqlMigrationPlanOperation<unknown> = {
|
|
47
|
-
id: 'pgvector.install-vector-extension',
|
|
48
|
-
label: 'Enable extension "vector"',
|
|
49
|
-
operationClass: 'additive',
|
|
50
|
-
invariantId: PGVECTOR_INVARIANTS.installVector,
|
|
51
|
-
target: {
|
|
52
|
-
id: 'postgres',
|
|
53
|
-
details: {
|
|
54
|
-
schema: 'public',
|
|
55
|
-
objectType: 'extension',
|
|
56
|
-
name: 'vector',
|
|
57
|
-
} satisfies PostgresTargetDetails,
|
|
58
|
-
},
|
|
59
|
-
precheck: [
|
|
60
|
-
{
|
|
61
|
-
description: 'verify extension "vector" is not already enabled',
|
|
62
|
-
sql: "SELECT NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector')",
|
|
63
|
-
},
|
|
64
|
-
],
|
|
65
|
-
execute: [
|
|
66
|
-
{
|
|
67
|
-
description: 'create extension "vector"',
|
|
68
|
-
sql: 'CREATE EXTENSION IF NOT EXISTS vector',
|
|
69
|
-
},
|
|
70
|
-
],
|
|
71
|
-
postcheck: [
|
|
72
|
-
{
|
|
73
|
-
description: 'confirm extension "vector" is enabled',
|
|
74
|
-
sql: "SELECT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector')",
|
|
75
|
-
},
|
|
76
|
-
],
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
const pgvectorBaselineOps: readonly MigrationPlanOperation[] = [installVectorExtensionOp];
|
|
80
|
-
|
|
81
|
-
/** Sorted list of invariantIds the baseline migration provides. */
|
|
82
|
-
export const PGVECTOR_BASELINE_INVARIANTS: readonly string[] = (() => {
|
|
83
|
-
const ids = pgvectorBaselineOps
|
|
84
|
-
.map((op) => op.invariantId)
|
|
85
|
-
.filter((id): id is string => typeof id === 'string');
|
|
86
|
-
return [...new Set(ids)].sort();
|
|
87
|
-
})();
|
|
88
|
-
|
|
89
|
-
const baselineMetadataWithoutHash: Omit<MigrationPackage['metadata'], 'migrationHash'> = {
|
|
90
|
-
from: null,
|
|
91
|
-
to: PGVECTOR_STORAGE_HASH,
|
|
92
|
-
fromContract: null,
|
|
93
|
-
toContract: pgvectorContract,
|
|
94
|
-
hints: { used: [], applied: [], plannerVersion: '2.0.0' },
|
|
95
|
-
labels: [],
|
|
96
|
-
providedInvariants: PGVECTOR_BASELINE_INVARIANTS,
|
|
97
|
-
createdAt: '2026-06-01T00:00:00.000Z',
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Baseline migration package the descriptor publishes via
|
|
102
|
-
* `contractSpace.migrations`. The framework's emitter writes this to
|
|
103
|
-
* `migrations/pgvector/<dirName>/{manifest,ops,contract}.json` in the
|
|
104
|
-
* user's repo at `migrate` time.
|
|
105
|
-
*/
|
|
106
|
-
export const pgvectorBaselineMigration: MigrationPackage = {
|
|
107
|
-
dirName: PGVECTOR_BASELINE_MIGRATION_NAME,
|
|
108
|
-
metadata: {
|
|
109
|
-
...baselineMetadataWithoutHash,
|
|
110
|
-
migrationHash: computeMigrationHash(baselineMetadataWithoutHash, pgvectorBaselineOps),
|
|
111
|
-
},
|
|
112
|
-
ops: pgvectorBaselineOps,
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Pinned head ref the descriptor publishes. The framework writes this
|
|
117
|
-
* verbatim to `migrations/pgvector/refs/head.json` (with `invariants`
|
|
118
|
-
* sorted alphabetically per the canonicalisation rules); the runner's
|
|
119
|
-
* `findPathWithDecision` step consults `head.json` to decide which
|
|
120
|
-
* migrations need to apply.
|
|
121
|
-
*/
|
|
122
|
-
export const pgvectorHeadRef: ContractSpaceHeadRef = {
|
|
123
|
-
hash: PGVECTOR_STORAGE_HASH,
|
|
124
|
-
invariants: PGVECTOR_BASELINE_INVARIANTS,
|
|
125
|
-
};
|