@prisma-next/extension-postgis 0.0.1

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.
Files changed (54) hide show
  1. package/README.md +283 -0
  2. package/dist/codec-types-DODPC0GY.d.mts +59 -0
  3. package/dist/codec-types-DODPC0GY.d.mts.map +1 -0
  4. package/dist/codec-types.d.mts +2 -0
  5. package/dist/codec-types.mjs +1 -0
  6. package/dist/column-types.d.mts +26 -0
  7. package/dist/column-types.d.mts.map +1 -0
  8. package/dist/column-types.mjs +28 -0
  9. package/dist/column-types.mjs.map +1 -0
  10. package/dist/constants-dLvIWSgv.mjs +6 -0
  11. package/dist/constants-dLvIWSgv.mjs.map +1 -0
  12. package/dist/control.d.mts +7 -0
  13. package/dist/control.d.mts.map +1 -0
  14. package/dist/control.mjs +189 -0
  15. package/dist/control.mjs.map +1 -0
  16. package/dist/descriptor-meta-DUKzIH9c.mjs +516 -0
  17. package/dist/descriptor-meta-DUKzIH9c.mjs.map +1 -0
  18. package/dist/geojson-Cj4ldHQ0.d.mts +61 -0
  19. package/dist/geojson-Cj4ldHQ0.d.mts.map +1 -0
  20. package/dist/geojson.d.mts +2 -0
  21. package/dist/geojson.mjs +60 -0
  22. package/dist/geojson.mjs.map +1 -0
  23. package/dist/operation-types-C2s9tY0P.d.mts +123 -0
  24. package/dist/operation-types-C2s9tY0P.d.mts.map +1 -0
  25. package/dist/operation-types.d.mts +2 -0
  26. package/dist/operation-types.mjs +1 -0
  27. package/dist/pack.d.mts +75 -0
  28. package/dist/pack.d.mts.map +1 -0
  29. package/dist/pack.mjs +2 -0
  30. package/dist/runtime.d.mts +19 -0
  31. package/dist/runtime.d.mts.map +1 -0
  32. package/dist/runtime.mjs +22 -0
  33. package/dist/runtime.mjs.map +1 -0
  34. package/package.json +64 -0
  35. package/src/contract.d.ts +91 -0
  36. package/src/contract.json +40 -0
  37. package/src/contract.ts +61 -0
  38. package/src/core/authoring.ts +18 -0
  39. package/src/core/codecs.ts +193 -0
  40. package/src/core/constants.ts +3 -0
  41. package/src/core/contract-space-constants.ts +30 -0
  42. package/src/core/descriptor-meta.ts +186 -0
  43. package/src/core/ewkb.ts +284 -0
  44. package/src/core/geojson.ts +131 -0
  45. package/src/core/registry.ts +14 -0
  46. package/src/exports/codec-types.ts +7 -0
  47. package/src/exports/column-types.ts +38 -0
  48. package/src/exports/control.ts +101 -0
  49. package/src/exports/geojson.ts +19 -0
  50. package/src/exports/operation-types.ts +7 -0
  51. package/src/exports/pack.ts +1 -0
  52. package/src/exports/runtime.ts +34 -0
  53. package/src/types/codec-types.ts +16 -0
  54. package/src/types/operation-types.ts +81 -0
package/README.md ADDED
@@ -0,0 +1,283 @@
1
+ # @prisma-next/extension-postgis
2
+
3
+ Geospatial data for Prisma Next on PostgreSQL, powered by [PostGIS](https://postgis.net).
4
+
5
+ Model points, lines, and polygons as first-class columns, query them with a type-safe DSL (`distance`, `containment`, `intersection`, `bounding-box`), and let the framework handle the wire format, SRID metadata, and `CREATE EXTENSION postgis` for you.
6
+
7
+ ## What you get
8
+
9
+ - **`Geometry` column type** — store Points, LineStrings, Polygons, and their Multi-\* variants as a single `geometry` column. Optional SRID parameter (e.g. `Geometry(4326)` for WGS84 lng/lat) flows through to `contract.d.ts` and DDL.
10
+ - **GeoJSON-shaped runtime values** — read and write geometries as plain `{ type, coordinates }` objects. No PostGIS-specific client APIs to learn.
11
+ - **Seven query operations** on geometry columns: `distance`, `distanceSphere`, `dwithin`, `contains`, `within`, `intersects`, `intersectsBbox`.
12
+ - **Automatic `CREATE EXTENSION`** — the control descriptor declares `postgis` as a database dependency, so `prisma-next db init` ensures the server has it enabled before the first migration runs.
13
+ - **Both authoring paths** — works with PSL schemas and the TypeScript contract builder.
14
+
15
+ ## Prerequisites
16
+
17
+ The PostGIS extension must be installable on your PostgreSQL server. Most managed providers (RDS, Cloud SQL, Supabase, Neon, …) include it; for local development the easiest route is the `postgis/postgis` Docker image, or a multi-arch fork like `imresamu/postgis` on Apple Silicon hosts.
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ pnpm add @prisma-next/extension-postgis
23
+ ```
24
+
25
+ ## Quick start
26
+
27
+ A complete five-step example — see [`examples/prisma-next-postgis-demo`](../../../examples/prisma-next-postgis-demo) for the full version with a seeded database, a Next.js UI, and e2e tests.
28
+
29
+ **1. Register the extension in `prisma-next.config.ts`:**
30
+
31
+ ```typescript
32
+ import { defineConfig } from '@prisma-next/cli/config-types';
33
+ import postgresAdapter from '@prisma-next/adapter-postgres/control';
34
+ import sql from '@prisma-next/family-sql/control';
35
+ import postgres from '@prisma-next/target-postgres/control';
36
+ import postgis from '@prisma-next/extension-postgis/control';
37
+
38
+ export default defineConfig({
39
+ family: sql,
40
+ target: postgres,
41
+ adapter: postgresAdapter,
42
+ extensionPacks: [postgis],
43
+ });
44
+ ```
45
+
46
+ **2. Declare a geometry column in your schema (PSL):**
47
+
48
+ ```prisma
49
+ // use prisma-next
50
+
51
+ types {
52
+ WgsGeometry = postgis.Geometry(4326)
53
+ }
54
+
55
+ model Cafe {
56
+ id String @id @default(uuid())
57
+ name String
58
+ location WgsGeometry
59
+ @@map("cafe")
60
+ }
61
+ ```
62
+
63
+ **3. Emit the contract and apply the migration:**
64
+
65
+ ```bash
66
+ pnpm prisma-next contract emit # generates contract.json + contract.d.ts
67
+ pnpm prisma-next db init # CREATE EXTENSION postgis + CREATE TABLE
68
+ ```
69
+
70
+ **4. Wire the extension into the runtime:**
71
+
72
+ ```typescript
73
+ // src/prisma/db.ts
74
+ import postgres from '@prisma-next/postgres/runtime';
75
+ import postgis from '@prisma-next/extension-postgis/runtime';
76
+ import type { Contract } from './contract.d';
77
+ import contractJson from './contract.json' with { type: 'json' };
78
+
79
+ export const db = postgres<Contract>({
80
+ contractJson,
81
+ extensions: [postgis],
82
+ url: process.env['DATABASE_URL']!,
83
+ });
84
+ ```
85
+
86
+ **5. Query:**
87
+
88
+ ```typescript
89
+ import { point } from '@prisma-next/extension-postgis/geojson';
90
+ import { db } from './prisma/db';
91
+
92
+ const ferryBuilding = point(-122.3937, 37.7955, 4326);
93
+
94
+ // Five nearest cafes, with their distance in metres.
95
+ const closest = await db.runtime().execute(
96
+ db.sql.cafe
97
+ .select('id', 'name')
98
+ .select('meters', (f, fns) => fns.distanceSphere(f.location, ferryBuilding))
99
+ .orderBy((f, fns) => fns.distanceSphere(f.location, ferryBuilding), { direction: 'asc' })
100
+ .limit(5)
101
+ .build(),
102
+ );
103
+ ```
104
+
105
+ ## Authoring schemas
106
+
107
+ ### PSL
108
+
109
+ `postgis.Geometry(srid)` declares an SRID-constrained `geometry` column. Aliasing it via a `types {}` block keeps the SRID in one place when you have several geometry fields.
110
+
111
+ ```prisma
112
+ types {
113
+ WgsGeometry = postgis.Geometry(4326)
114
+ }
115
+
116
+ model Route {
117
+ id String @id @default(uuid())
118
+ name String
119
+ path WgsGeometry // LineStrings, polygons, points — all valid runtime values
120
+ @@map("route")
121
+ }
122
+ ```
123
+
124
+ `Geometry` without an SRID (`postgis.Geometry()`) is also valid for schemas that mix SRIDs at the row level.
125
+
126
+ ### TypeScript contract builder
127
+
128
+ ```typescript
129
+ import { textColumn, varcharColumn } from '@prisma-next/adapter-postgres/column-types';
130
+ import sqlFamily from '@prisma-next/family-sql/pack';
131
+ import { defineContract, field, model } from '@prisma-next/sql-contract-ts/contract-builder';
132
+ import { geometry, geometryColumn } from '@prisma-next/extension-postgis/column-types';
133
+ import postgis from '@prisma-next/extension-postgis/pack';
134
+ import postgres from '@prisma-next/target-postgres/pack';
135
+
136
+ export const contract = defineContract({
137
+ family: sqlFamily,
138
+ target: postgres,
139
+ extensionPacks: { postgis },
140
+ models: {
141
+ Cafe: model('Cafe', {
142
+ fields: {
143
+ id: field.column(varcharColumn).id(),
144
+ name: field.column(textColumn),
145
+ location: field.column(geometry({ srid: 4326 })), // SRID-constrained
146
+ },
147
+ }).sql({ table: 'cafe' }),
148
+ },
149
+ });
150
+ ```
151
+
152
+ `geometry({ srid })` is the dimensioned form (emits `geometry(Geometry, 4326)` in DDL). `geometryColumn` is the unconstrained form for schemas that need flexibility.
153
+
154
+ ## Geometry values
155
+
156
+ Runtime geometries are GeoJSON-shaped objects: `{ type, coordinates, srid? }`. Build them with the constructors from `@prisma-next/extension-postgis/geojson` rather than constructing the literals by hand — the constructors keep the coordinate ordering straight (`[lng, lat]`) and attach SRID metadata in one place.
157
+
158
+ ```typescript
159
+ import { bboxPolygon, point, polygon } from '@prisma-next/extension-postgis/geojson';
160
+
161
+ // Point — note: longitude first, latitude second.
162
+ const sightglass = point(-122.4106, 37.7765, 4326);
163
+
164
+ // Polygon — outer ring, optionally followed by holes. First point must equal last.
165
+ const soma = polygon(
166
+ [
167
+ [-122.418, 37.77],
168
+ [-122.4, 37.77],
169
+ [-122.4, 37.785],
170
+ [-122.418, 37.785],
171
+ [-122.418, 37.77],
172
+ ],
173
+ 4326,
174
+ );
175
+
176
+ // Axis-aligned bbox polygon — handy for "what's in this map viewport?" queries.
177
+ const downtownViewport = bboxPolygon([-122.425, 37.775, -122.4, 37.8], 4326);
178
+ ```
179
+
180
+ For `LineString`, `MultiPoint`, `MultiLineString`, and `MultiPolygon`, build the object literally:
181
+
182
+ ```typescript
183
+ const route: Geometry = {
184
+ type: 'LineString',
185
+ coordinates: [[-122.4194, 37.7749], [-122.4106, 37.7765]],
186
+ srid: 4326,
187
+ };
188
+ ```
189
+
190
+ ## Querying
191
+
192
+ All seven operations are available on any `geometry` column inside the SQL DSL's `.where()`, `.select()`, and `.orderBy()` callbacks via the `fns` argument:
193
+
194
+ ```typescript
195
+ // Within radius — uses ST_DistanceSphere for sphere-accurate metres.
196
+ db.sql.cafe
197
+ .select('id', 'name')
198
+ .where((f, fns) => fns.distanceSphere(f.location, ferryBuilding) <= 2_000)
199
+ .build();
200
+
201
+ // Point-in-polygon — "which neighborhood contains this point?"
202
+ db.sql.neighborhood
203
+ .select('id', 'name')
204
+ .where((f, fns) => fns.contains(f.boundary, sightglass))
205
+ .build();
206
+
207
+ // Index-friendly bounding-box filter for map viewport queries.
208
+ db.sql.cafe
209
+ .select('id', 'name')
210
+ .where((f, fns) => fns.intersectsBbox(f.location, downtownViewport))
211
+ .build();
212
+
213
+ // Routes that cross an arbitrary geometry (e.g. a road-closure polygon).
214
+ db.sql.route
215
+ .select('id', 'name')
216
+ .where((f, fns) => fns.intersects(f.path, closurePolygon))
217
+ .build();
218
+ ```
219
+
220
+ User-supplied geometries (the second argument to each operation) are bound as parameters, not interpolated. The codec encodes them as EWKT on the wire — see [Wire format](#wire-format) below.
221
+
222
+ ## Operations reference
223
+
224
+ | Method | SQL | Returns | Use when |
225
+ | --- | --- | --- | --- |
226
+ | `distance(other)` | `ST_Distance(self, other)` | `float8` | Cartesian distance in the geometry's native units (degrees for SRID 4326). |
227
+ | `distanceSphere(other)` | `ST_DistanceSphere(self, other)` | `float8` | Sphere-accurate metres between two lng/lat geometries. |
228
+ | `dwithin(other, distance)` | `ST_DWithin(self, other, distance)` | `boolean` | Index-friendly "are these within X of each other?" — `distance` is in the geometry's native units. |
229
+ | `contains(other)` | `ST_Contains(self, other)` | `boolean` | Point-in-polygon and polygon-contains-polygon checks. |
230
+ | `within(other)` | `ST_Within(self, other)` | `boolean` | The inverse of `contains` — `A within B` ⇔ `B contains A`. |
231
+ | `intersects(other)` | `ST_Intersects(self, other)` | `boolean` | Any kind of overlap between two geometries. |
232
+ | `intersectsBbox(other)` | `self && other` | `boolean` | Cheap 2-D bounding-box overlap — fast viewport filtering. |
233
+
234
+ ### Picking the right distance op
235
+
236
+ For SRID 4326 (WGS84) lng/lat data, `distance` returns **degrees**, which is rarely what you want. Use `distanceSphere` for human-friendly metres, or cast to `geography` upstream if you need ellipsoidal accuracy. `dwithin` interprets its `distance` argument in whatever units the inputs use — pair it with a projected SRS (or use `geography`) if you want metres directly.
237
+
238
+ ## SRID and units
239
+
240
+ - Declare an SRID at the column level (`postgis.Geometry(4326)` / `geometry({ srid: 4326 })`) to constrain the column and have the runtime preserve the SRID through writes.
241
+ - `point(lng, lat, srid)`, `polygon(rings, srid)`, and `bboxPolygon(box, srid)` attach the SRID to the value; the codec emits it as `SRID=4326;…` on the wire.
242
+ - `distance` and `dwithin` are not unit-aware — they return whatever the SRS uses. If you need metres on WGS84 data, prefer `distanceSphere`.
243
+
244
+ ## Wire format
245
+
246
+ - **JS → SQL**: values are emitted as EWKT (`SRID=4326;POINT(-122.39 37.79)`) and cast to `::geometry`. SRID is preserved through the `SRID=…;` prefix.
247
+ - **SQL → JS**: `node-postgres` returns `geometry` columns as hex-encoded EWKB. The codec parses Point, LineString, Polygon, MultiPoint, MultiLineString, and MultiPolygon. Z and M coordinates are **not** supported in this release; if a column carries them, decoding throws so the mismatch is visible rather than silent.
248
+
249
+ ## Capabilities
250
+
251
+ The extension declares a single capability flag:
252
+
253
+ - `postgis.geometry` — gating signal for the `geometry` codec and the seven operations above.
254
+
255
+ Features that require it should declare a `requires: ['postgis.geometry']` constraint in their capability gate.
256
+
257
+ ## Types
258
+
259
+ For consumers that need to reference the value or operation shapes directly:
260
+
261
+ ```typescript
262
+ import type { CodecTypes, Geometry } from '@prisma-next/extension-postgis/codec-types';
263
+ import type { OperationTypes, QueryOperationTypes } from '@prisma-next/extension-postgis/operation-types';
264
+
265
+ // CodecTypes['pg/geometry@1']['output'] = Geometry (the GeoJSON union)
266
+ // Geometry<4326> is the SRID-branded form rendered into contract.d.ts.
267
+ ```
268
+
269
+ ## Demo
270
+
271
+ The end-to-end demo in [`examples/prisma-next-postgis-demo`](../../../examples/prisma-next-postgis-demo) walks through:
272
+
273
+ - Schema with three geometry shapes (`Point`, `LineString`, `Polygon`).
274
+ - Seeded data (five SF cafes, two delivery routes, three neighborhood polygons).
275
+ - One query file per operation — distance, radius, point-in-polygon, intersection, and bbox.
276
+ - A Next.js App Router UI that runs the queries as server components and renders the results on a Leaflet map.
277
+ - Vitest e2e tests that exercise the full pipeline against a local PostGIS container.
278
+
279
+ ## References
280
+
281
+ - [PostGIS documentation](https://postgis.net/docs/)
282
+ - [Prisma Next Architecture Overview](../../../docs/Architecture%20Overview.md)
283
+ - [Extension Packs Guide](../../../docs/reference/Extension-Packs-Naming-and-Layout.md)
@@ -0,0 +1,59 @@
1
+ import { t as Geometry$1 } from "./geojson-Cj4ldHQ0.mjs";
2
+ import { AnyCodecDescriptor, CodecCallContext, CodecDescriptorImpl, CodecImpl, CodecInstanceContext } from "@prisma-next/framework-components/codec";
3
+ import { JsonValue } from "@prisma-next/contract/types";
4
+ import { ExtractCodecTypes } from "@prisma-next/sql-relational-core/ast";
5
+ import { StandardSchemaV1 } from "@standard-schema/spec";
6
+
7
+ //#region src/core/constants.d.ts
8
+ declare const POSTGIS_GEOMETRY_CODEC_ID: "pg/geometry@1";
9
+ //#endregion
10
+ //#region src/core/codecs.d.ts
11
+ type GeometryParams = {
12
+ readonly srid: number;
13
+ };
14
+ declare class PostgisGeometryCodec extends CodecImpl<typeof POSTGIS_GEOMETRY_CODEC_ID, readonly ['equality'], string, Geometry$1> {
15
+ constructor(descriptor: AnyCodecDescriptor);
16
+ encode(value: Geometry$1, _ctx: CodecCallContext): Promise<string>;
17
+ decode(wire: string, _ctx: CodecCallContext): Promise<Geometry$1>;
18
+ encodeJson(value: Geometry$1): JsonValue;
19
+ decodeJson(json: JsonValue): Geometry$1;
20
+ }
21
+ declare class PostgisGeometryDescriptor extends CodecDescriptorImpl<GeometryParams> {
22
+ readonly codecId: "pg/geometry@1";
23
+ readonly traits: readonly ["equality"];
24
+ readonly targetTypes: readonly ["geometry"];
25
+ readonly meta: {
26
+ readonly db: {
27
+ readonly sql: {
28
+ readonly postgres: {
29
+ readonly nativeType: "geometry";
30
+ };
31
+ };
32
+ };
33
+ };
34
+ readonly paramsSchema: StandardSchemaV1<GeometryParams>;
35
+ renderOutputType(params: GeometryParams): string;
36
+ /**
37
+ * The runtime calls `factory(undefined)(ctx)` to materialize a
38
+ * representative codec for parameterised descriptors that ship a
39
+ * no-params column variant (here, `geometryColumn` vs `geometry({ srid })`).
40
+ * The runtime cast widens `params` to `unknown`, so guarding with an
41
+ * optional read keeps the typed call site (`factory({ srid })`)
42
+ * strict while still producing an SRID-agnostic codec for
43
+ * representative use. Encode/decode for an unparameterised column
44
+ * runs through this representative; the wire format already carries
45
+ * SRID inside the EWKT/EWKB payload, so it's dimension-independent.
46
+ */
47
+ factory(_params: GeometryParams): (ctx: CodecInstanceContext) => PostgisGeometryCodec;
48
+ }
49
+ declare const codecDescriptorMap: {
50
+ readonly geometry: PostgisGeometryDescriptor;
51
+ };
52
+ type CodecTypes$1 = ExtractCodecTypes<typeof codecDescriptorMap>;
53
+ //#endregion
54
+ //#region src/types/codec-types.d.ts
55
+ type Geometry<_Srid extends number = number> = Geometry$1;
56
+ type CodecTypes = CodecTypes$1;
57
+ //#endregion
58
+ export { Geometry as n, CodecTypes as t };
59
+ //# sourceMappingURL=codec-types-DODPC0GY.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec-types-DODPC0GY.d.mts","names":[],"sources":["../src/core/constants.ts","../src/core/codecs.ts","../src/types/codec-types.ts"],"mappings":";;;;;;;cAAa,yBAAA;;;KCmDR,cAAA;EAAA,SAA4B,IAAA;AAAA;AAAA,cA2CpB,oBAAA,SAA6B,SAAA,QACjC,yBAAA,iCAGP,UAAA;cAEY,UAAA,EAAY,kBAAA;EAIlB,MAAA,CAAO,KAAA,EAAO,UAAA,EAAU,IAAA,EAAM,gBAAA,GAAmB,OAAA;EAKjD,MAAA,CAAO,IAAA,UAAc,IAAA,EAAM,gBAAA,GAAmB,OAAA,CAAQ,UAAA;EAO5D,UAAA,CAAW,KAAA,EAAO,UAAA,GAAW,SAAA;EAK7B,UAAA,CAAW,IAAA,EAAM,SAAA,GAAY,UAAA;AAAA;AAAA,cAMlB,yBAAA,SAAkC,mBAAA,CAAoB,cAAA;EAAA,SAC/C,OAAA;EAAA,SACA,MAAA;EAAA,SACA,WAAA;EAAA,SACA,IAAA;IAAA;;;;;;;;WACA,YAAA,EAAc,gBAAA,CAAiB,cAAA;EACxC,gBAAA,CAAiB,MAAA,EAAQ,cAAA;EAZvB;;;;AAMb;;;;;;;EAsBW,OAAA,CAAQ,OAAA,EAAS,cAAA,IAAkB,GAAA,EAAK,oBAAA,KAAyB,oBAAA;AAAA;AAAA,cAqCtE,kBAAA;EAAA,mBAEI,yBAAA;AAAA;AAAA,KAEE,YAAA,GAAa,iBAAA,QAAyB,kBAAA;;;KCjLtC,QAAA,kCAA0C,UAAA;AAAA,KAE1C,UAAA,GAAa,YAAA"}
@@ -0,0 +1,2 @@
1
+ import { n as Geometry, t as CodecTypes } from "./codec-types-DODPC0GY.mjs";
2
+ export { type CodecTypes, type Geometry };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ import { ColumnTypeDescriptor } from "@prisma-next/framework-components/codec";
2
+
3
+ //#region src/exports/column-types.d.ts
4
+ declare const geometryColumn: {
5
+ readonly codecId: "pg/geometry@1";
6
+ readonly nativeType: "geometry";
7
+ };
8
+ /**
9
+ * Build an SRID-constrained geometry column descriptor.
10
+ *
11
+ * @example
12
+ * .column('location', { type: geometry({ srid: 4326 }), nullable: false })
13
+ * // Produces: nativeType: 'geometry', typeParams: { srid: 4326 }
14
+ *
15
+ * @throws {RangeError} If `srid` is not a non-negative integer.
16
+ */
17
+ declare function geometry<S extends number>(options: {
18
+ readonly srid: S;
19
+ }): ColumnTypeDescriptor & {
20
+ readonly typeParams: {
21
+ readonly srid: S;
22
+ };
23
+ };
24
+ //#endregion
25
+ export { geometry, geometryColumn };
26
+ //# sourceMappingURL=column-types.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"column-types.d.mts","names":[],"sources":["../src/exports/column-types.ts"],"mappings":";;;cAWa,cAAA;EAAA,SAG4B,OAAA;EAAA,SAAA,UAAA;AAAA;;;;;;;;;;iBAWzB,QAAA,kBAAA,CAA2B,OAAA;EAAA,SAChC,IAAA,EAAM,CAAA;AAAA,IACb,oBAAA;EAAA,SAAkC,UAAA;IAAA,SAAuB,IAAA,EAAM,CAAA;EAAA;AAAA"}
@@ -0,0 +1,28 @@
1
+ import { t as POSTGIS_GEOMETRY_CODEC_ID } from "./constants-dLvIWSgv.mjs";
2
+ //#region src/exports/column-types.ts
3
+ const geometryColumn = {
4
+ codecId: POSTGIS_GEOMETRY_CODEC_ID,
5
+ nativeType: "geometry"
6
+ };
7
+ /**
8
+ * Build an SRID-constrained geometry column descriptor.
9
+ *
10
+ * @example
11
+ * .column('location', { type: geometry({ srid: 4326 }), nullable: false })
12
+ * // Produces: nativeType: 'geometry', typeParams: { srid: 4326 }
13
+ *
14
+ * @throws {RangeError} If `srid` is not a non-negative integer.
15
+ */
16
+ function geometry(options) {
17
+ const { srid } = options;
18
+ if (!Number.isInteger(srid) || srid < 0) throw new RangeError(`postgis: srid must be a non-negative integer, got ${srid}`);
19
+ return {
20
+ codecId: POSTGIS_GEOMETRY_CODEC_ID,
21
+ nativeType: "geometry",
22
+ typeParams: { srid }
23
+ };
24
+ }
25
+ //#endregion
26
+ export { geometry, geometryColumn };
27
+
28
+ //# sourceMappingURL=column-types.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"column-types.mjs","names":[],"sources":["../src/exports/column-types.ts"],"sourcesContent":["/**\n * Column type descriptors for the PostGIS extension.\n *\n * Use `geometryColumn` for an untyped `geometry` column, or\n * `geometry({ srid })` to declare an SRID-constrained column whose DDL\n * comes out as `geometry(Geometry, <srid>)`.\n */\n\nimport type { ColumnTypeDescriptor } from '@prisma-next/framework-components/codec';\nimport { POSTGIS_GEOMETRY_CODEC_ID } from '../core/constants';\n\nexport const geometryColumn = {\n codecId: POSTGIS_GEOMETRY_CODEC_ID,\n nativeType: 'geometry',\n} as const satisfies ColumnTypeDescriptor;\n\n/**\n * Build an SRID-constrained geometry column descriptor.\n *\n * @example\n * .column('location', { type: geometry({ srid: 4326 }), nullable: false })\n * // Produces: nativeType: 'geometry', typeParams: { srid: 4326 }\n *\n * @throws {RangeError} If `srid` is not a non-negative integer.\n */\nexport function geometry<S extends number>(options: {\n readonly srid: S;\n}): ColumnTypeDescriptor & { readonly typeParams: { readonly srid: S } } {\n const { srid } = options;\n if (!Number.isInteger(srid) || srid < 0) {\n throw new RangeError(`postgis: srid must be a non-negative integer, got ${srid}`);\n }\n return {\n codecId: POSTGIS_GEOMETRY_CODEC_ID,\n nativeType: 'geometry',\n typeParams: { srid },\n } as const;\n}\n"],"mappings":";;AAWA,MAAa,iBAAiB;CAC5B,SAAS;CACT,YAAY;CACb;;;;;;;;;;AAWD,SAAgB,SAA2B,SAE8B;CACvE,MAAM,EAAE,SAAS;CACjB,IAAI,CAAC,OAAO,UAAU,KAAK,IAAI,OAAO,GACpC,MAAM,IAAI,WAAW,qDAAqD,OAAO;CAEnF,OAAO;EACL,SAAS;EACT,YAAY;EACZ,YAAY,EAAE,MAAM;EACrB"}
@@ -0,0 +1,6 @@
1
+ //#region src/core/constants.ts
2
+ const POSTGIS_GEOMETRY_CODEC_ID = "pg/geometry@1";
3
+ //#endregion
4
+ export { POSTGIS_GEOMETRY_CODEC_ID as t };
5
+
6
+ //# sourceMappingURL=constants-dLvIWSgv.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants-dLvIWSgv.mjs","names":[],"sources":["../src/core/constants.ts"],"sourcesContent":["export const POSTGIS_GEOMETRY_CODEC_ID = 'pg/geometry@1' as const;\n\nexport const SRID_WGS84 = 4326 as const;\n"],"mappings":";AAAA,MAAa,4BAA4B"}
@@ -0,0 +1,7 @@
1
+ import { SqlControlExtensionDescriptor } from "@prisma-next/family-sql/control";
2
+
3
+ //#region src/exports/control.d.ts
4
+ declare const postgisExtensionDescriptor: SqlControlExtensionDescriptor<'postgres'>;
5
+ //#endregion
6
+ export { postgisExtensionDescriptor as default, postgisExtensionDescriptor };
7
+ //# sourceMappingURL=control.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control.d.mts","names":[],"sources":["../src/exports/control.ts"],"mappings":";;;cA+EM,0BAAA,EAA4B,6BAAA"}
@@ -0,0 +1,189 @@
1
+ import { t as POSTGIS_GEOMETRY_CODEC_ID } from "./constants-dLvIWSgv.mjs";
2
+ import { n as postgisQueryOperations, t as postgisPackMeta } from "./descriptor-meta-DUKzIH9c.mjs";
3
+ import { contractSpaceFromJson } from "@prisma-next/migration-tools/spaces";
4
+ //#region migrations/20260601T0000_install_postgis_extension/migration.json
5
+ var migration_default = {
6
+ from: null,
7
+ to: "sha256:01622b3970e0ee2a582cc4c857bca7f6ed970f21b23f44e3f2781eea0be7d5eb",
8
+ labels: [],
9
+ providedInvariants: ["postgis:install-postgis-v1"],
10
+ createdAt: "2026-06-01T00:00:00.000Z",
11
+ fromContract: null,
12
+ toContract: {
13
+ "schemaVersion": "1",
14
+ "targetFamily": "sql",
15
+ "target": "postgres",
16
+ "profileHash": "sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e",
17
+ "roots": {},
18
+ "models": {},
19
+ "storage": {
20
+ "storageHash": "sha256:01622b3970e0ee2a582cc4c857bca7f6ed970f21b23f44e3f2781eea0be7d5eb",
21
+ "tables": {},
22
+ "types": { "geometry": {
23
+ "codecId": "pg/geometry@1",
24
+ "nativeType": "geometry",
25
+ "typeParams": {}
26
+ } }
27
+ },
28
+ "capabilities": {
29
+ "postgres": {
30
+ "jsonAgg": true,
31
+ "lateral": true,
32
+ "limit": true,
33
+ "orderBy": true,
34
+ "returning": true
35
+ },
36
+ "sql": {
37
+ "defaultInInsert": true,
38
+ "enums": true,
39
+ "returning": true
40
+ }
41
+ },
42
+ "extensionPacks": {},
43
+ "meta": {},
44
+ "_generated": {
45
+ "warning": "⚠️ GENERATED FILE - DO NOT EDIT",
46
+ "message": "This file is automatically generated by \"prisma-next contract emit\".",
47
+ "regenerate": "To regenerate, run: prisma-next contract emit"
48
+ }
49
+ },
50
+ hints: {
51
+ "used": [],
52
+ "applied": [],
53
+ "plannerVersion": "2.0.0"
54
+ },
55
+ migrationHash: "sha256:87029e185c726572b12a1383e3687dd9e3b19261f70bf75bbb3b76f884608597"
56
+ };
57
+ //#endregion
58
+ //#region migrations/20260601T0000_install_postgis_extension/ops.json
59
+ var ops_default = [{
60
+ "id": "postgis.install-postgis-extension",
61
+ "label": "Enable extension \"postgis\"",
62
+ "operationClass": "additive",
63
+ "invariantId": "postgis:install-postgis-v1",
64
+ "target": {
65
+ "id": "postgres",
66
+ "details": {
67
+ "schema": "public",
68
+ "objectType": "dependency",
69
+ "name": "postgis"
70
+ }
71
+ },
72
+ "precheck": [{
73
+ "description": "verify extension \"postgis\" is not already enabled",
74
+ "sql": "SELECT NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'postgis')"
75
+ }],
76
+ "execute": [{
77
+ "description": "create extension \"postgis\"",
78
+ "sql": "CREATE EXTENSION IF NOT EXISTS postgis"
79
+ }],
80
+ "postcheck": [{
81
+ "description": "confirm extension \"postgis\" is enabled",
82
+ "sql": "SELECT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'postgis')"
83
+ }]
84
+ }];
85
+ //#endregion
86
+ //#region migrations/refs/head.json
87
+ var head_default = {
88
+ hash: "sha256:01622b3970e0ee2a582cc4c857bca7f6ed970f21b23f44e3f2781eea0be7d5eb",
89
+ invariants: ["postgis:install-postgis-v1"]
90
+ };
91
+ //#endregion
92
+ //#region src/contract.json
93
+ var contract_default = {
94
+ schemaVersion: "1",
95
+ targetFamily: "sql",
96
+ target: "postgres",
97
+ profileHash: "sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e",
98
+ roots: {},
99
+ models: {},
100
+ storage: {
101
+ "storageHash": "sha256:01622b3970e0ee2a582cc4c857bca7f6ed970f21b23f44e3f2781eea0be7d5eb",
102
+ "tables": {},
103
+ "types": { "geometry": {
104
+ "codecId": "pg/geometry@1",
105
+ "nativeType": "geometry",
106
+ "typeParams": {}
107
+ } }
108
+ },
109
+ capabilities: {
110
+ "postgres": {
111
+ "jsonAgg": true,
112
+ "lateral": true,
113
+ "limit": true,
114
+ "orderBy": true,
115
+ "returning": true
116
+ },
117
+ "sql": {
118
+ "defaultInInsert": true,
119
+ "enums": true,
120
+ "returning": true
121
+ }
122
+ },
123
+ extensionPacks: {},
124
+ meta: {},
125
+ _generated: {
126
+ "warning": "⚠️ GENERATED FILE - DO NOT EDIT",
127
+ "message": "This file is automatically generated by \"prisma-next contract emit\".",
128
+ "regenerate": "To regenerate, run: prisma-next contract emit"
129
+ }
130
+ };
131
+ //#endregion
132
+ //#region src/core/contract-space-constants.ts
133
+ /**
134
+ * Static names and identifiers used across postgis's contract space.
135
+ *
136
+ * Centralised here so the contract IR (`./contract`), the baseline
137
+ * migration ops (`./migrations`), the head ref, and the descriptor
138
+ * (`../exports/control`) all reference the same values without typos.
139
+ *
140
+ * The space identifier `'postgis'` is what the framework writes to
141
+ * `migrations/` in the user's repo and what the marker table's
142
+ * `space` column carries for postgis-owned rows.
143
+ *
144
+ * The `postgis:*` invariantId namespace is locked here — once
145
+ * published, an invariantId is immutable so downstream consumers can
146
+ * reference it by literal string match.
147
+ */
148
+ const POSTGIS_SPACE_ID = "postgis";
149
+ const POSTGIS_BASELINE_MIGRATION_NAME = "20260601T0000_install_postgis_extension";
150
+ //#endregion
151
+ //#region src/exports/control.ts
152
+ const geometryControlPlaneHooks = {
153
+ expandNativeType: ({ nativeType, typeParams }) => {
154
+ const srid = typeParams?.["srid"];
155
+ if (typeof srid === "number" && Number.isInteger(srid) && srid >= 0) return `${nativeType}(Geometry,${srid})`;
156
+ return nativeType;
157
+ },
158
+ resolveIdentityValue: () => null
159
+ };
160
+ const postgisContractSpace = contractSpaceFromJson({
161
+ contractJson: contract_default,
162
+ migrations: [{
163
+ dirName: POSTGIS_BASELINE_MIGRATION_NAME,
164
+ metadata: migration_default,
165
+ ops: ops_default
166
+ }],
167
+ headRef: head_default
168
+ });
169
+ const postgisExtensionDescriptor = {
170
+ ...postgisPackMeta,
171
+ id: POSTGIS_SPACE_ID,
172
+ contractSpace: postgisContractSpace,
173
+ types: {
174
+ ...postgisPackMeta.types,
175
+ codecTypes: {
176
+ ...postgisPackMeta.types.codecTypes,
177
+ controlPlaneHooks: { [POSTGIS_GEOMETRY_CODEC_ID]: geometryControlPlaneHooks }
178
+ }
179
+ },
180
+ queryOperations: () => postgisQueryOperations(),
181
+ create: () => ({
182
+ familyId: "sql",
183
+ targetId: "postgres"
184
+ })
185
+ };
186
+ //#endregion
187
+ export { postgisExtensionDescriptor as default, postgisExtensionDescriptor };
188
+
189
+ //# sourceMappingURL=control.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control.mjs","names":["baselineMetadata","baselineOps"],"sources":["../migrations/20260601T0000_install_postgis_extension/migration.json","../migrations/20260601T0000_install_postgis_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 postgis'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 `'postgis'` is what the framework writes to\n * `migrations/` in the user's repo and what the marker table's\n * `space` column carries for postgis-owned rows.\n *\n * The `postgis:*` 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 POSTGIS_SPACE_ID = 'postgis' as const;\n\nexport const POSTGIS_NATIVE_TYPE = 'geometry' as const;\n\nexport const POSTGIS_BASELINE_MIGRATION_NAME = '20260601T0000_install_postgis_extension' as const;\n\n/**\n * `postgis:*` 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 POSTGIS_INVARIANTS = {\n installPostgis: 'postgis:install-postgis-v1',\n} as const;\n","/**\n * Control-plane descriptor for the postgis 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 * `MigrationPackage` shape for the framework's runner / verifier to\n * consume.\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[POSTGIS_GEOMETRY_CODEC_ID]` —\n * codec control hooks (`expandNativeType`, `resolveIdentityValue`)\n * the SQL planner extracts via `extractCodecControlHooks` and uses\n * to render `geometry(Geometry,${srid})` column types.\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_postgis_extension/migration.json' with {\n type: 'json',\n};\nimport baselineOps from '../../migrations/20260601T0000_install_postgis_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 { POSTGIS_GEOMETRY_CODEC_ID } from '../core/constants';\nimport {\n POSTGIS_BASELINE_MIGRATION_NAME,\n POSTGIS_SPACE_ID,\n} from '../core/contract-space-constants';\nimport { postgisPackMeta, postgisQueryOperations } from '../core/descriptor-meta';\n\nconst geometryControlPlaneHooks: CodecControlHooks = {\n expandNativeType: ({ nativeType, typeParams }) => {\n const srid = typeParams?.['srid'];\n if (typeof srid === 'number' && Number.isInteger(srid) && srid >= 0) {\n // PostGIS prints the type-modifier list without a space — match\n // it here so the verifier doesn't see `geometry(Geometry, 4326)`\n // (DDL) mismatch `geometry(Geometry,4326)` (introspected).\n return `${nativeType}(Geometry,${srid})`;\n }\n return nativeType;\n },\n // PostGIS has no canonical \"identity\" geometry; backfilling a\n // non-null column requires the user to supply a valid value, so we\n // don't synthesise one here.\n resolveIdentityValue: () => null,\n};\n\nconst postgisContractSpace = contractSpaceFromJson<Contract<SqlStorage>>({\n contractJson,\n migrations: [\n {\n dirName: POSTGIS_BASELINE_MIGRATION_NAME,\n metadata: baselineMetadata,\n ops: baselineOps,\n },\n ],\n headRef,\n});\n\nconst postgisExtensionDescriptor: SqlControlExtensionDescriptor<'postgres'> = {\n ...postgisPackMeta,\n id: POSTGIS_SPACE_ID,\n contractSpace: postgisContractSpace,\n types: {\n ...postgisPackMeta.types,\n codecTypes: {\n ...postgisPackMeta.types.codecTypes,\n controlPlaneHooks: {\n [POSTGIS_GEOMETRY_CODEC_ID]: geometryControlPlaneHooks,\n },\n },\n },\n queryOperations: () => postgisQueryOperations(),\n create: () => ({\n familyId: 'sql' as const,\n targetId: 'postgres' as const,\n }),\n};\n\nexport { postgisExtensionDescriptor };\nexport default postgisExtensionDescriptor;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AIgBA,MAAa,mBAAmB;AAIhC,MAAa,kCAAkC;;;AC8B/C,MAAM,4BAA+C;CACnD,mBAAmB,EAAE,YAAY,iBAAiB;EAChD,MAAM,OAAO,aAAa;EAC1B,IAAI,OAAO,SAAS,YAAY,OAAO,UAAU,KAAK,IAAI,QAAQ,GAIhE,OAAO,GAAG,WAAW,YAAY,KAAK;EAExC,OAAO;;CAKT,4BAA4B;CAC7B;AAED,MAAM,uBAAuB,sBAA4C;CACvE,cAAA;CACA,YAAY,CACV;EACE,SAAS;EACT,UAAUA;EACV,KAAKC;EACN,CACF;CACD,SAAA;CACD,CAAC;AAEF,MAAM,6BAAwE;CAC5E,GAAG;CACH,IAAI;CACJ,eAAe;CACf,OAAO;EACL,GAAG,gBAAgB;EACnB,YAAY;GACV,GAAG,gBAAgB,MAAM;GACzB,mBAAmB,GAChB,4BAA4B,2BAC9B;GACF;EACF;CACD,uBAAuB,wBAAwB;CAC/C,eAAe;EACb,UAAU;EACV,UAAU;EACX;CACF"}