graphile-postgis 1.1.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -45
- package/constants.d.ts +1 -0
- package/constants.js +10 -1
- package/esm/constants.d.ts +13 -0
- package/esm/constants.js +9 -0
- package/esm/index.d.ts +24 -0
- package/esm/index.js +25 -33
- package/esm/plugins/codec.d.ts +19 -0
- package/esm/plugins/codec.js +174 -0
- package/esm/plugins/detect-extension.d.ts +14 -0
- package/esm/plugins/detect-extension.js +57 -0
- package/esm/plugins/geometry-fields.d.ts +21 -0
- package/esm/plugins/geometry-fields.js +245 -0
- package/esm/plugins/inflection.d.ts +8 -0
- package/esm/plugins/inflection.js +52 -0
- package/esm/plugins/register-types.d.ts +22 -0
- package/esm/plugins/register-types.js +319 -0
- package/esm/preset.d.ts +18 -0
- package/esm/preset.js +30 -0
- package/esm/types.d.ts +84 -0
- package/esm/utils.d.ts +21 -0
- package/esm/utils.js +18 -7
- package/index.d.ts +24 -15
- package/index.js +39 -47
- package/package.json +23 -18
- package/plugins/codec.d.ts +19 -0
- package/plugins/codec.js +180 -0
- package/plugins/detect-extension.d.ts +14 -0
- package/plugins/detect-extension.js +60 -0
- package/plugins/geometry-fields.d.ts +21 -0
- package/plugins/geometry-fields.js +248 -0
- package/plugins/inflection.d.ts +8 -0
- package/plugins/inflection.js +55 -0
- package/plugins/register-types.d.ts +22 -0
- package/plugins/register-types.js +325 -0
- package/preset.d.ts +18 -0
- package/preset.js +33 -0
- package/types.d.ts +69 -44
- package/utils.d.ts +16 -0
- package/utils.js +17 -6
- package/PostgisExtensionDetectionPlugin.d.ts +0 -3
- package/PostgisExtensionDetectionPlugin.js +0 -28
- package/PostgisInflectionPlugin.d.ts +0 -3
- package/PostgisInflectionPlugin.js +0 -36
- package/PostgisRegisterTypesPlugin.d.ts +0 -3
- package/PostgisRegisterTypesPlugin.js +0 -234
- package/PostgisVersionPlugin.d.ts +0 -3
- package/PostgisVersionPlugin.js +0 -24
- package/Postgis_GeometryCollection_GeometriesPlugin.d.ts +0 -3
- package/Postgis_GeometryCollection_GeometriesPlugin.js +0 -43
- package/Postgis_LineString_PointsPlugin.d.ts +0 -3
- package/Postgis_LineString_PointsPlugin.js +0 -40
- package/Postgis_MultiLineString_LineStringsPlugin.d.ts +0 -3
- package/Postgis_MultiLineString_LineStringsPlugin.js +0 -38
- package/Postgis_MultiPoint_PointsPlugin.d.ts +0 -3
- package/Postgis_MultiPoint_PointsPlugin.js +0 -38
- package/Postgis_MultiPolygon_PolygonsPlugin.d.ts +0 -3
- package/Postgis_MultiPolygon_PolygonsPlugin.js +0 -38
- package/Postgis_Point_LatitudeLongitudePlugin.d.ts +0 -3
- package/Postgis_Point_LatitudeLongitudePlugin.js +0 -43
- package/Postgis_Polygon_RingsPlugin.d.ts +0 -3
- package/Postgis_Polygon_RingsPlugin.js +0 -49
- package/esm/PostgisExtensionDetectionPlugin.js +0 -26
- package/esm/PostgisInflectionPlugin.js +0 -34
- package/esm/PostgisRegisterTypesPlugin.js +0 -229
- package/esm/PostgisVersionPlugin.js +0 -22
- package/esm/Postgis_GeometryCollection_GeometriesPlugin.js +0 -41
- package/esm/Postgis_LineString_PointsPlugin.js +0 -38
- package/esm/Postgis_MultiLineString_LineStringsPlugin.js +0 -36
- package/esm/Postgis_MultiPoint_PointsPlugin.js +0 -36
- package/esm/Postgis_MultiPolygon_PolygonsPlugin.js +0 -36
- package/esm/Postgis_Point_LatitudeLongitudePlugin.js +0 -41
- package/esm/Postgis_Polygon_RingsPlugin.js +0 -47
- package/esm/makeGeoJSONType.js +0 -39
- package/makeGeoJSONType.d.ts +0 -1
- package/makeGeoJSONType.js +0 -42
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.PostgisRegisterTypesPlugin = void 0;
|
|
7
|
+
require("graphile-build");
|
|
8
|
+
require("graphile-build-pg");
|
|
9
|
+
const pg_sql2_1 = __importDefault(require("pg-sql2"));
|
|
10
|
+
const constants_1 = require("../constants");
|
|
11
|
+
const utils_1 = require("../utils");
|
|
12
|
+
// Import types.ts for the Build/Inflection/Scope augmentation side effects
|
|
13
|
+
require("../types");
|
|
14
|
+
/**
|
|
15
|
+
* PostgisRegisterTypesPlugin
|
|
16
|
+
*
|
|
17
|
+
* The core plugin that:
|
|
18
|
+
* 1. Registers a GeoJSON scalar type
|
|
19
|
+
* 2. Creates GraphQL interfaces for geometry/geography base types
|
|
20
|
+
* 3. Creates GraphQL interfaces for each dimension combination (XY, XYZ, XYM, XYZM)
|
|
21
|
+
* 4. Creates concrete GraphQL object types for each subtype/dimension combo
|
|
22
|
+
* 5. Registers codec-to-type mappings so PostGraphile knows how to handle
|
|
23
|
+
* geometry/geography columns
|
|
24
|
+
*
|
|
25
|
+
* In v5, type registration is done during the init hook using
|
|
26
|
+
* build.registerObjectType / build.registerInterfaceType / build.registerScalarType.
|
|
27
|
+
*
|
|
28
|
+
* The SQL tweak wraps geometry/geography values in json_build_object() containing
|
|
29
|
+
* __gisType, __srid, and __geojson fields, which downstream resolvers use.
|
|
30
|
+
*/
|
|
31
|
+
exports.PostgisRegisterTypesPlugin = {
|
|
32
|
+
name: 'PostgisRegisterTypesPlugin',
|
|
33
|
+
version: '2.0.0',
|
|
34
|
+
description: 'Registers PostGIS GeoJSON scalar and geometry/geography types',
|
|
35
|
+
after: ['PostgisExtensionDetectionPlugin', 'PostgisInflectionPlugin'],
|
|
36
|
+
schema: {
|
|
37
|
+
hooks: {
|
|
38
|
+
init(_, build) {
|
|
39
|
+
const postgisInfo = build.pgGISExtensionInfo;
|
|
40
|
+
if (!postgisInfo) {
|
|
41
|
+
return _;
|
|
42
|
+
}
|
|
43
|
+
const { inflection, graphql: { GraphQLInt, GraphQLNonNull, Kind } } = build;
|
|
44
|
+
const constructedTypes = build.pgGISGraphQLTypesByCodecAndSubtype;
|
|
45
|
+
if (!constructedTypes) {
|
|
46
|
+
return _;
|
|
47
|
+
}
|
|
48
|
+
const { geometryCodec, geographyCodec } = postgisInfo;
|
|
49
|
+
// Register the GeoJSON scalar type
|
|
50
|
+
build.registerScalarType('GeoJSON', {}, () => ({
|
|
51
|
+
description: 'The `GeoJSON` scalar type represents GeoJSON values as specified by ' +
|
|
52
|
+
'[RFC 7946](https://tools.ietf.org/html/rfc7946).',
|
|
53
|
+
serialize: (value) => value,
|
|
54
|
+
parseValue: (value) => {
|
|
55
|
+
if (value === null || value === undefined)
|
|
56
|
+
return value;
|
|
57
|
+
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
58
|
+
throw new TypeError('GeoJSON must be an object');
|
|
59
|
+
}
|
|
60
|
+
const obj = value;
|
|
61
|
+
if (typeof obj.type !== 'string') {
|
|
62
|
+
throw new TypeError('GeoJSON must have a "type" string property');
|
|
63
|
+
}
|
|
64
|
+
if (obj.type === 'Feature' || obj.type === 'FeatureCollection') {
|
|
65
|
+
throw new TypeError(`GeoJSON type "${obj.type}" is not supported for PostGIS geometry input. Extract the geometry from your Feature first.`);
|
|
66
|
+
}
|
|
67
|
+
const validTypes = ['Point', 'MultiPoint', 'LineString', 'MultiLineString', 'Polygon', 'MultiPolygon', 'GeometryCollection'];
|
|
68
|
+
if (!validTypes.includes(obj.type)) {
|
|
69
|
+
throw new TypeError(`GeoJSON type "${obj.type}" is not a recognized GeoJSON type`);
|
|
70
|
+
}
|
|
71
|
+
return value;
|
|
72
|
+
},
|
|
73
|
+
parseLiteral(ast, variables) {
|
|
74
|
+
return parseLiteralGeoJSON(ast, variables, Kind);
|
|
75
|
+
}
|
|
76
|
+
}), 'PostgisRegisterTypesPlugin registering GeoJSON scalar');
|
|
77
|
+
// Process geometry and (optionally) geography types
|
|
78
|
+
const gisCodecs = [geometryCodec, geographyCodec].filter((c) => c !== null);
|
|
79
|
+
for (const gisCodec of gisCodecs) {
|
|
80
|
+
const key = gisCodec.name;
|
|
81
|
+
const typeName = gisCodec.name;
|
|
82
|
+
if (!constructedTypes[key]) {
|
|
83
|
+
constructedTypes[key] = {};
|
|
84
|
+
}
|
|
85
|
+
// Register the main interface (no dimensional constraint)
|
|
86
|
+
const mainInterfaceName = inflection.gisInterfaceName(typeName);
|
|
87
|
+
build.registerInterfaceType(mainInterfaceName, {
|
|
88
|
+
isPgGISInterface: true,
|
|
89
|
+
pgGISCodecName: typeName,
|
|
90
|
+
pgGISZMFlag: -1
|
|
91
|
+
}, () => ({
|
|
92
|
+
description: `All ${typeName} types implement this interface`,
|
|
93
|
+
fields: () => {
|
|
94
|
+
const geoJsonType = build.getTypeByName('GeoJSON');
|
|
95
|
+
if (!geoJsonType) {
|
|
96
|
+
throw new Error('PostGIS: GeoJSON scalar type not found.');
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
[inflection.geojsonFieldName()]: {
|
|
100
|
+
type: geoJsonType,
|
|
101
|
+
description: 'Converts the object to GeoJSON'
|
|
102
|
+
},
|
|
103
|
+
srid: {
|
|
104
|
+
type: new GraphQLNonNull(GraphQLInt),
|
|
105
|
+
description: 'Spatial reference identifier (SRID)'
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
},
|
|
109
|
+
resolveType(value) {
|
|
110
|
+
const gisTypeKey = value.__gisType;
|
|
111
|
+
const resolvedTypeName = constructedTypes[key]?.[gisTypeKey];
|
|
112
|
+
if (typeof resolvedTypeName === 'string') {
|
|
113
|
+
return resolvedTypeName;
|
|
114
|
+
}
|
|
115
|
+
throw new Error(`PostGIS: Could not resolve type for __gisType="${gisTypeKey}" on codec="${key}". Known types: ${Object.keys(constructedTypes[key] ?? {}).join(', ')}`);
|
|
116
|
+
}
|
|
117
|
+
}), `PostgisRegisterTypesPlugin registering ${mainInterfaceName} interface`);
|
|
118
|
+
// Register dimension interfaces (XY, XYZ, XYM, XYZM)
|
|
119
|
+
for (const hasZ of [false, true]) {
|
|
120
|
+
for (const hasM of [false, true]) {
|
|
121
|
+
const zmflag = (hasZ ? 2 : 0) + (hasM ? 1 : 0);
|
|
122
|
+
const coords = { 0: 'XY', 1: 'XYM', 2: 'XYZ', 3: 'XYZM' };
|
|
123
|
+
const dimInterfaceName = inflection.gisDimensionInterfaceName(typeName, hasZ, hasM);
|
|
124
|
+
build.registerInterfaceType(dimInterfaceName, {
|
|
125
|
+
isPgGISDimensionInterface: true,
|
|
126
|
+
pgGISCodecName: typeName,
|
|
127
|
+
pgGISZMFlag: zmflag
|
|
128
|
+
}, () => ({
|
|
129
|
+
description: `All ${typeName} ${coords[zmflag]} types implement this interface`,
|
|
130
|
+
fields: () => {
|
|
131
|
+
const geoJsonType = build.getTypeByName('GeoJSON');
|
|
132
|
+
if (!geoJsonType) {
|
|
133
|
+
throw new Error('PostGIS: GeoJSON scalar type not found.');
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
[inflection.geojsonFieldName()]: {
|
|
137
|
+
type: geoJsonType,
|
|
138
|
+
description: 'Converts the object to GeoJSON'
|
|
139
|
+
},
|
|
140
|
+
srid: {
|
|
141
|
+
type: new GraphQLNonNull(GraphQLInt),
|
|
142
|
+
description: 'Spatial reference identifier (SRID)'
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
},
|
|
146
|
+
resolveType(value) {
|
|
147
|
+
const gisTypeKey = value.__gisType;
|
|
148
|
+
const concreteTypeName = constructedTypes[key]?.[gisTypeKey];
|
|
149
|
+
if (typeof concreteTypeName === 'string') {
|
|
150
|
+
return concreteTypeName;
|
|
151
|
+
}
|
|
152
|
+
throw new Error(`PostGIS: Could not resolve type for __gisType="${gisTypeKey}" on codec="${key}". Known types: ${Object.keys(constructedTypes[key] ?? {}).join(', ')}`);
|
|
153
|
+
}
|
|
154
|
+
}), `PostgisRegisterTypesPlugin registering ${dimInterfaceName} interface`);
|
|
155
|
+
// Register concrete object types for each subtype + this dimension
|
|
156
|
+
for (const subtype of constants_1.CONCRETE_SUBTYPES) {
|
|
157
|
+
const concreteTypeName = inflection.gisType(typeName, subtype, hasZ, hasM, 0);
|
|
158
|
+
const typeModifier = (0, utils_1.getGISTypeModifier)(subtype, hasZ, hasM, 0);
|
|
159
|
+
const typeDetails = (0, utils_1.getGISTypeDetails)(typeModifier);
|
|
160
|
+
build.registerObjectType(concreteTypeName, {
|
|
161
|
+
isPgGISType: true,
|
|
162
|
+
pgGISCodecName: typeName,
|
|
163
|
+
pgGISTypeDetails: typeDetails
|
|
164
|
+
}, () => ({
|
|
165
|
+
description: `A PostGIS ${typeName} ${(0, utils_1.getGISTypeName)(subtype, hasZ, hasM)} type`,
|
|
166
|
+
interfaces: () => [
|
|
167
|
+
build.getTypeByName(mainInterfaceName),
|
|
168
|
+
build.getTypeByName(dimInterfaceName)
|
|
169
|
+
].filter(Boolean),
|
|
170
|
+
fields: () => {
|
|
171
|
+
const geoJsonType = build.getTypeByName('GeoJSON');
|
|
172
|
+
if (!geoJsonType) {
|
|
173
|
+
throw new Error('PostGIS: GeoJSON scalar type not found.');
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
[inflection.geojsonFieldName()]: {
|
|
177
|
+
type: geoJsonType,
|
|
178
|
+
description: 'Converts the object to GeoJSON',
|
|
179
|
+
resolve(data) {
|
|
180
|
+
return data.__geojson;
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
srid: {
|
|
184
|
+
type: new GraphQLNonNull(GraphQLInt),
|
|
185
|
+
description: 'Spatial reference identifier (SRID)',
|
|
186
|
+
resolve(data) {
|
|
187
|
+
return data.__srid;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
}), `PostgisRegisterTypesPlugin registering ${concreteTypeName} type`);
|
|
193
|
+
// Track the type by its gisTypeKey for resolveType lookups
|
|
194
|
+
const gisTypeKey = (0, utils_1.getGISTypeName)(subtype, hasZ, hasM);
|
|
195
|
+
constructedTypes[key][gisTypeKey] = concreteTypeName;
|
|
196
|
+
}
|
|
197
|
+
// Map the Geometry subtype (0) for this dimension to the dimension interface
|
|
198
|
+
const geomDimKey = (0, utils_1.getGISTypeName)(constants_1.GisSubtype.Geometry, hasZ, hasM);
|
|
199
|
+
constructedTypes[key][geomDimKey] = dimInterfaceName;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// Map null/unspecified modifier to the main interface
|
|
203
|
+
constructedTypes[key][-1] = mainInterfaceName;
|
|
204
|
+
}
|
|
205
|
+
// Register type mappings so PostGraphile knows what GraphQL types to
|
|
206
|
+
// use for geometry/geography columns.
|
|
207
|
+
//
|
|
208
|
+
// Without BOTH input AND output mappings, PgAttributesPlugin silently
|
|
209
|
+
// omits geometry columns from the schema:
|
|
210
|
+
// - Input: GeoJSON scalar (accepts GeoJSON objects)
|
|
211
|
+
// - Output: The main interface type (e.g. GeometryInterface), which uses
|
|
212
|
+
// resolveType to dispatch to concrete types (GeometryPoint, etc.)
|
|
213
|
+
const { setGraphQLTypeForPgCodec } = build;
|
|
214
|
+
if (typeof setGraphQLTypeForPgCodec === 'function') {
|
|
215
|
+
for (const gisCodec of gisCodecs) {
|
|
216
|
+
const mainInterfaceName = inflection.gisInterfaceName(gisCodec.name);
|
|
217
|
+
setGraphQLTypeForPgCodec(gisCodec, 'input', 'GeoJSON');
|
|
218
|
+
setGraphQLTypeForPgCodec(gisCodec, 'output', mainInterfaceName);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return _;
|
|
222
|
+
},
|
|
223
|
+
build(build) {
|
|
224
|
+
const postgisInfo = build.pgGISExtensionInfo;
|
|
225
|
+
if (!postgisInfo) {
|
|
226
|
+
return build;
|
|
227
|
+
}
|
|
228
|
+
const { schemaName } = postgisInfo;
|
|
229
|
+
const constructedTypes = build.pgGISGraphQLTypesByCodecAndSubtype;
|
|
230
|
+
if (!constructedTypes) {
|
|
231
|
+
return build;
|
|
232
|
+
}
|
|
233
|
+
return build.extend(build, {
|
|
234
|
+
getPostgisTypeByGeometryType(gisCodecName, subtype, hasZ = false, hasM = false, _srid = 0) {
|
|
235
|
+
const gisTypeKey = (0, utils_1.getGISTypeName)(subtype, hasZ, hasM);
|
|
236
|
+
const resolvedTypeName = constructedTypes[gisCodecName]?.[gisTypeKey];
|
|
237
|
+
if (typeof resolvedTypeName === 'string') {
|
|
238
|
+
return build.getTypeByName(resolvedTypeName);
|
|
239
|
+
}
|
|
240
|
+
return undefined;
|
|
241
|
+
},
|
|
242
|
+
pgGISWrapExpression(fragment) {
|
|
243
|
+
// PostGIS function names MUST be lowercase for PostgreSQL identifier matching
|
|
244
|
+
const params = [
|
|
245
|
+
pg_sql2_1.default.literal('__gisType'),
|
|
246
|
+
pg_sql2_1.default.fragment `${pg_sql2_1.default.identifier(schemaName, 'geometrytype')}(${fragment})`,
|
|
247
|
+
pg_sql2_1.default.literal('__srid'),
|
|
248
|
+
pg_sql2_1.default.fragment `${pg_sql2_1.default.identifier(schemaName, 'st_srid')}(${fragment})`,
|
|
249
|
+
pg_sql2_1.default.literal('__geojson'),
|
|
250
|
+
pg_sql2_1.default.fragment `${pg_sql2_1.default.identifier(schemaName, 'st_asgeojson')}(${fragment})::JSON`
|
|
251
|
+
];
|
|
252
|
+
return pg_sql2_1.default.fragment `(case when ${fragment} is null then null else json_build_object(
|
|
253
|
+
${pg_sql2_1.default.join(params, ', ')}
|
|
254
|
+
) end)`;
|
|
255
|
+
},
|
|
256
|
+
pgGISFromGeoJSON(value, codecName) {
|
|
257
|
+
const jsonStr = pg_sql2_1.default.value(JSON.stringify(value));
|
|
258
|
+
if (codecName === 'geography') {
|
|
259
|
+
return pg_sql2_1.default.fragment `${pg_sql2_1.default.identifier(schemaName, 'st_geomfromgeojson')}(${jsonStr}::text)::${pg_sql2_1.default.identifier(schemaName, 'geography')}`;
|
|
260
|
+
}
|
|
261
|
+
return pg_sql2_1.default.fragment `${pg_sql2_1.default.identifier(schemaName, 'st_geomfromgeojson')}(${jsonStr}::text)`;
|
|
262
|
+
}
|
|
263
|
+
}, 'PostgisRegisterTypesPlugin adding PostGIS helpers to build');
|
|
264
|
+
},
|
|
265
|
+
// Add all registered PostGIS concrete types to the schema so they are
|
|
266
|
+
// discoverable for resolveType on the interfaces. Types registered via
|
|
267
|
+
// build.registerObjectType() during init are lazy — they are only
|
|
268
|
+
// materialized when getTypeByName() is called.
|
|
269
|
+
//
|
|
270
|
+
// We MUST use GraphQLSchema_types (not GraphQLSchema) because the
|
|
271
|
+
// GraphQLSchema hook's types property gets overwritten by the
|
|
272
|
+
// GraphQLSchema_types hook that runs immediately after it.
|
|
273
|
+
GraphQLSchema_types(types, build) {
|
|
274
|
+
const constructedTypes = build.pgGISGraphQLTypesByCodecAndSubtype;
|
|
275
|
+
if (!constructedTypes) {
|
|
276
|
+
return types;
|
|
277
|
+
}
|
|
278
|
+
for (const codecTypes of Object.values(constructedTypes)) {
|
|
279
|
+
for (const typeName of Object.values(codecTypes)) {
|
|
280
|
+
if (typeof typeName === 'string') {
|
|
281
|
+
const graphqlType = build.getTypeByName(typeName);
|
|
282
|
+
if (graphqlType && !types.includes(graphqlType)) {
|
|
283
|
+
types.push(graphqlType);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return types;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
/**
|
|
294
|
+
* Recursively parses a GeoJSON literal from a GraphQL AST.
|
|
295
|
+
*/
|
|
296
|
+
function parseLiteralGeoJSON(ast, variables, Kind, depth = 0) {
|
|
297
|
+
if (depth > 32) {
|
|
298
|
+
throw new Error('GeoJSON input exceeds maximum nesting depth');
|
|
299
|
+
}
|
|
300
|
+
switch (ast.kind) {
|
|
301
|
+
case Kind.STRING:
|
|
302
|
+
case Kind.BOOLEAN:
|
|
303
|
+
return ast.value;
|
|
304
|
+
case Kind.INT:
|
|
305
|
+
case Kind.FLOAT:
|
|
306
|
+
return parseFloat(ast.value);
|
|
307
|
+
case Kind.OBJECT: {
|
|
308
|
+
const value = Object.create(null);
|
|
309
|
+
ast.fields.forEach((field) => {
|
|
310
|
+
value[field.name.value] = parseLiteralGeoJSON(field.value, variables, Kind, depth + 1);
|
|
311
|
+
});
|
|
312
|
+
return value;
|
|
313
|
+
}
|
|
314
|
+
case Kind.LIST:
|
|
315
|
+
return ast.values.map((n) => parseLiteralGeoJSON(n, variables, Kind, depth + 1));
|
|
316
|
+
case Kind.NULL:
|
|
317
|
+
return null;
|
|
318
|
+
case Kind.VARIABLE: {
|
|
319
|
+
const variableName = ast.name.value;
|
|
320
|
+
return variables ? variables[variableName] : undefined;
|
|
321
|
+
}
|
|
322
|
+
default:
|
|
323
|
+
return undefined;
|
|
324
|
+
}
|
|
325
|
+
}
|
package/preset.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { GraphileConfig } from 'graphile-config';
|
|
2
|
+
/**
|
|
3
|
+
* GraphilePostgisPreset
|
|
4
|
+
*
|
|
5
|
+
* A preset that includes all PostGIS plugins for PostGraphile v5.
|
|
6
|
+
* Use this as the recommended way to add PostGIS support.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { GraphilePostgisPreset } from 'graphile-postgis';
|
|
11
|
+
*
|
|
12
|
+
* const preset = {
|
|
13
|
+
* extends: [GraphilePostgisPreset]
|
|
14
|
+
* };
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare const GraphilePostgisPreset: GraphileConfig.Preset;
|
|
18
|
+
export default GraphilePostgisPreset;
|
package/preset.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GraphilePostgisPreset = void 0;
|
|
4
|
+
const codec_1 = require("./plugins/codec");
|
|
5
|
+
const inflection_1 = require("./plugins/inflection");
|
|
6
|
+
const detect_extension_1 = require("./plugins/detect-extension");
|
|
7
|
+
const register_types_1 = require("./plugins/register-types");
|
|
8
|
+
const geometry_fields_1 = require("./plugins/geometry-fields");
|
|
9
|
+
/**
|
|
10
|
+
* GraphilePostgisPreset
|
|
11
|
+
*
|
|
12
|
+
* A preset that includes all PostGIS plugins for PostGraphile v5.
|
|
13
|
+
* Use this as the recommended way to add PostGIS support.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* import { GraphilePostgisPreset } from 'graphile-postgis';
|
|
18
|
+
*
|
|
19
|
+
* const preset = {
|
|
20
|
+
* extends: [GraphilePostgisPreset]
|
|
21
|
+
* };
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
exports.GraphilePostgisPreset = {
|
|
25
|
+
plugins: [
|
|
26
|
+
codec_1.PostgisCodecPlugin,
|
|
27
|
+
inflection_1.PostgisInflectionPlugin,
|
|
28
|
+
detect_extension_1.PostgisExtensionDetectionPlugin,
|
|
29
|
+
register_types_1.PostgisRegisterTypesPlugin,
|
|
30
|
+
geometry_fields_1.PostgisGeometryFieldsPlugin
|
|
31
|
+
]
|
|
32
|
+
};
|
|
33
|
+
exports.default = exports.GraphilePostgisPreset;
|
package/types.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { PgExtension, PgIntrospectionResultsByKind, PgType } from 'graphile-build-pg';
|
|
3
|
-
import type { SQL } from 'graphile-build-pg/node8plus/QueryBuilder';
|
|
4
|
-
import type { GraphQLInputType, GraphQLInterfaceType, GraphQLObjectType, GraphQLOutputType, GraphQLType } from 'graphql';
|
|
1
|
+
import type { PgCodec } from '@dataplan/pg';
|
|
5
2
|
import type { Geometry } from 'geojson';
|
|
3
|
+
import type { GraphQLInterfaceType, GraphQLObjectType } from 'graphql';
|
|
4
|
+
import type { SQL } from 'pg-sql2';
|
|
6
5
|
import type { GisSubtype } from './constants';
|
|
7
6
|
export interface GisTypeDetails {
|
|
8
7
|
subtype: GisSubtype;
|
|
@@ -10,50 +9,76 @@ export interface GisTypeDetails {
|
|
|
10
9
|
hasM: boolean;
|
|
11
10
|
srid: number;
|
|
12
11
|
}
|
|
13
|
-
export type GisGraphQLType = GraphQLInterfaceType | GraphQLObjectType;
|
|
14
12
|
export interface GisFieldValue {
|
|
15
13
|
__gisType: string;
|
|
16
14
|
__srid: number;
|
|
17
15
|
__geojson: Geometry;
|
|
18
16
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
/**
|
|
18
|
+
* PostGIS extension detection result stored on the build object.
|
|
19
|
+
*/
|
|
20
|
+
export interface PostgisExtensionInfo {
|
|
21
|
+
/** The schema name where PostGIS is installed (e.g. 'public') */
|
|
22
|
+
schemaName: string;
|
|
23
|
+
/** The geometry codec from the registry */
|
|
24
|
+
geometryCodec: PgCodec;
|
|
25
|
+
/** The geography codec from the registry (optional — not all databases use geography columns) */
|
|
26
|
+
geographyCodec: PgCodec | null;
|
|
27
27
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Module augmentations for PostGraphile v5 types.
|
|
30
|
+
*
|
|
31
|
+
* These declare the custom properties that our PostGIS plugins add to the
|
|
32
|
+
* build object, inflection, and scope interfaces. This allows downstream
|
|
33
|
+
* code (including our own plugins) to use these properties without `any` casts.
|
|
34
|
+
*/
|
|
35
|
+
declare global {
|
|
36
|
+
namespace GraphileBuild {
|
|
37
|
+
interface Build {
|
|
38
|
+
/** PostGIS extension info (set by PostgisExtensionDetectionPlugin) */
|
|
39
|
+
pgGISExtensionInfo?: PostgisExtensionInfo;
|
|
40
|
+
/** Map of codec name -> gisTypeKey -> GraphQL type name (for resolveType) */
|
|
41
|
+
pgGISGraphQLTypesByCodecAndSubtype?: Record<string, Record<string | number, string>>;
|
|
42
|
+
/** Gets a registered PostGIS GraphQL type by geometry type, subtype, and dimension */
|
|
43
|
+
getPostgisTypeByGeometryType?(gisCodecName: string, subtype: GisSubtype, hasZ?: boolean, hasM?: boolean, srid?: number): GraphQLObjectType | GraphQLInterfaceType | undefined;
|
|
44
|
+
/** Wraps a geometry/geography SQL expression in json_build_object() with metadata */
|
|
45
|
+
pgGISWrapExpression?(fragment: SQL): SQL;
|
|
46
|
+
/** Creates an SQL fragment to convert GeoJSON input to a geometry value */
|
|
47
|
+
pgGISFromGeoJSON?(value: Record<string, unknown>, codecName: string): SQL;
|
|
48
|
+
}
|
|
49
|
+
interface Inflection {
|
|
50
|
+
/** Generate GraphQL type name for a PostGIS concrete type */
|
|
51
|
+
gisType(typeName: string, subtype: GisSubtype, hasZ: boolean, hasM: boolean, srid?: number): string;
|
|
52
|
+
/** Generate interface name for a PostGIS base type (e.g. GeometryInterface) */
|
|
53
|
+
gisInterfaceName(typeName: string): string;
|
|
54
|
+
/** Generate dimension interface name (e.g. GeometryGeometryZ) */
|
|
55
|
+
gisDimensionInterfaceName(typeName: string, hasZ: boolean, hasM: boolean): string;
|
|
56
|
+
/** Generate GeoJSON field name */
|
|
57
|
+
geojsonFieldName(): string;
|
|
58
|
+
/** Generate X coordinate field name */
|
|
59
|
+
gisXFieldName(typeName: string): string;
|
|
60
|
+
/** Generate Y coordinate field name */
|
|
61
|
+
gisYFieldName(typeName: string): string;
|
|
62
|
+
/** Generate Z coordinate field name */
|
|
63
|
+
gisZFieldName(typeName: string): string;
|
|
64
|
+
}
|
|
65
|
+
interface ScopeObject {
|
|
66
|
+
/** Whether this is a PostGIS concrete type */
|
|
67
|
+
isPgGISType?: boolean;
|
|
68
|
+
/** The codec name (geometry/geography) this type belongs to */
|
|
69
|
+
pgGISCodecName?: string;
|
|
70
|
+
/** The type details (subtype, hasZ, hasM, srid) */
|
|
71
|
+
pgGISTypeDetails?: GisTypeDetails;
|
|
72
|
+
}
|
|
73
|
+
interface ScopeInterface {
|
|
74
|
+
/** Whether this is a PostGIS base interface */
|
|
75
|
+
isPgGISInterface?: boolean;
|
|
76
|
+
/** Whether this is a PostGIS dimension interface */
|
|
77
|
+
isPgGISDimensionInterface?: boolean;
|
|
78
|
+
/** The codec name (geometry/geography) this interface belongs to */
|
|
79
|
+
pgGISCodecName?: string;
|
|
80
|
+
/** The ZM flag (-1 for base, 0-3 for dimension) */
|
|
81
|
+
pgGISZMFlag?: number;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
59
84
|
}
|
package/utils.d.ts
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import { GisSubtype } from './constants';
|
|
2
2
|
import type { GisTypeDetails } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Decodes a PostGIS type modifier into its component parts.
|
|
5
|
+
*
|
|
6
|
+
* Implements the C macro bit layout from liblwgeom.h:
|
|
7
|
+
* - SRID: bits 8-28
|
|
8
|
+
* - Subtype: bits 2-7
|
|
9
|
+
* - hasZ: bit 1
|
|
10
|
+
* - hasM: bit 0
|
|
11
|
+
*/
|
|
3
12
|
export declare const getGISTypeDetails: (modifier: number) => GisTypeDetails;
|
|
13
|
+
/**
|
|
14
|
+
* Encodes PostGIS type details into a type modifier integer.
|
|
15
|
+
*/
|
|
4
16
|
export declare const getGISTypeModifier: (subtype: GisSubtype, hasZ: boolean, hasM: boolean, srid: number) => number;
|
|
17
|
+
/**
|
|
18
|
+
* Returns the GIS type name string for a given subtype and Z/M flags.
|
|
19
|
+
* E.g. "PointZ", "MultiPolygonZM", "LineString"
|
|
20
|
+
*/
|
|
5
21
|
export declare const getGISTypeName: (subtype: GisSubtype, hasZ: boolean, hasM: boolean) => string;
|
package/utils.js
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getGISTypeName = exports.getGISTypeModifier = exports.getGISTypeDetails = void 0;
|
|
4
4
|
const constants_1 = require("./constants");
|
|
5
|
+
/**
|
|
6
|
+
* Decodes a PostGIS type modifier into its component parts.
|
|
7
|
+
*
|
|
8
|
+
* Implements the C macro bit layout from liblwgeom.h:
|
|
9
|
+
* - SRID: bits 8-28
|
|
10
|
+
* - Subtype: bits 2-7
|
|
11
|
+
* - hasZ: bit 1
|
|
12
|
+
* - hasM: bit 0
|
|
13
|
+
*/
|
|
5
14
|
const getGISTypeDetails = (modifier) => {
|
|
6
15
|
const allZeroesHopefully = modifier >> 24;
|
|
7
16
|
if (allZeroesHopefully !== 0) {
|
|
@@ -27,14 +36,12 @@ const getGISTypeDetails = (modifier) => {
|
|
|
27
36
|
throw new Error(`Unsupported PostGIS modifier, expected 0-7, received ${subtypeNumeric} (${modifier})`);
|
|
28
37
|
}
|
|
29
38
|
const subtype = subtypeNumeric;
|
|
30
|
-
return {
|
|
31
|
-
subtype,
|
|
32
|
-
hasZ,
|
|
33
|
-
hasM,
|
|
34
|
-
srid
|
|
35
|
-
};
|
|
39
|
+
return { subtype, hasZ, hasM, srid };
|
|
36
40
|
};
|
|
37
41
|
exports.getGISTypeDetails = getGISTypeDetails;
|
|
42
|
+
/**
|
|
43
|
+
* Encodes PostGIS type details into a type modifier integer.
|
|
44
|
+
*/
|
|
38
45
|
const getGISTypeModifier = (subtype, hasZ, hasM, srid) => {
|
|
39
46
|
// Ref: https://github.com/postgis/postgis/blob/2.5.2/liblwgeom/liblwgeom.h.in#L156-L173
|
|
40
47
|
// #define TYPMOD_SET_SRID(typmod, srid) ((typmod) = (((typmod) & 0xE00000FF) | ((srid & 0x001FFFFF)<<8)))
|
|
@@ -47,6 +54,10 @@ const getGISTypeModifier = (subtype, hasZ, hasM, srid) => {
|
|
|
47
54
|
(hasM ? 0x00000001 : 0));
|
|
48
55
|
};
|
|
49
56
|
exports.getGISTypeModifier = getGISTypeModifier;
|
|
57
|
+
/**
|
|
58
|
+
* Returns the GIS type name string for a given subtype and Z/M flags.
|
|
59
|
+
* E.g. "PointZ", "MultiPolygonZM", "LineString"
|
|
60
|
+
*/
|
|
50
61
|
const getGISTypeName = (subtype, hasZ, hasM) => {
|
|
51
62
|
return `${constants_1.GIS_SUBTYPE_NAME[subtype]}${hasZ ? 'Z' : ''}${hasM ? 'M' : ''}`;
|
|
52
63
|
};
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const PostgisExtensionDetectionPlugin = (builder) => {
|
|
4
|
-
builder.hook('build', (build) => {
|
|
5
|
-
const postgisBuild = build;
|
|
6
|
-
const { pgIntrospectionResultsByKind: introspectionResultsByKind } = postgisBuild;
|
|
7
|
-
const pgGISExtension = introspectionResultsByKind.extension.find((extension) => extension.name === 'postgis');
|
|
8
|
-
// Check we have the postgis extension
|
|
9
|
-
if (!pgGISExtension) {
|
|
10
|
-
console.warn('PostGIS extension not found in database; skipping');
|
|
11
|
-
return postgisBuild;
|
|
12
|
-
}
|
|
13
|
-
// Extract the geography and geometry types
|
|
14
|
-
const pgGISGeometryType = introspectionResultsByKind.type.find((type) => type.name === 'geometry' && type.namespaceId === pgGISExtension.namespaceId);
|
|
15
|
-
const pgGISGeographyType = introspectionResultsByKind.type.find((type) => type.name === 'geography' && type.namespaceId === pgGISExtension.namespaceId);
|
|
16
|
-
if (!pgGISGeographyType || !pgGISGeometryType) {
|
|
17
|
-
throw new Error("PostGIS is installed, but we couldn't find the geometry/geography types!");
|
|
18
|
-
}
|
|
19
|
-
return postgisBuild.extend(postgisBuild, {
|
|
20
|
-
pgGISGraphQLTypesByTypeAndSubtype: {},
|
|
21
|
-
pgGISGraphQLInterfaceTypesByType: {},
|
|
22
|
-
pgGISGeometryType,
|
|
23
|
-
pgGISGeographyType,
|
|
24
|
-
pgGISExtension
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
};
|
|
28
|
-
exports.default = PostgisExtensionDetectionPlugin;
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const constants_1 = require("./constants");
|
|
4
|
-
const PostgisInflectionPlugin = (builder) => {
|
|
5
|
-
builder.hook('inflection', (inflection) => {
|
|
6
|
-
return {
|
|
7
|
-
...inflection,
|
|
8
|
-
gisType(type, subtype, hasZ, hasM, _srid) {
|
|
9
|
-
return this.upperCamelCase([type.name, constants_1.SUBTYPE_STRING_BY_SUBTYPE[subtype], hasZ ? 'z' : null, hasM ? 'm' : null]
|
|
10
|
-
.filter(Boolean)
|
|
11
|
-
.join('-'));
|
|
12
|
-
},
|
|
13
|
-
gisInterfaceName(type) {
|
|
14
|
-
return this.upperCamelCase(`${type.name}-interface`);
|
|
15
|
-
},
|
|
16
|
-
gisDimensionInterfaceName(type, hasZ, hasM) {
|
|
17
|
-
return this.upperCamelCase([type.name, constants_1.SUBTYPE_STRING_BY_SUBTYPE[constants_1.GisSubtype.Geometry], hasZ ? 'z' : null, hasM ? 'm' : null]
|
|
18
|
-
.filter(Boolean)
|
|
19
|
-
.join('-'));
|
|
20
|
-
},
|
|
21
|
-
geojsonFieldName() {
|
|
22
|
-
return 'geojson';
|
|
23
|
-
},
|
|
24
|
-
gisXFieldName(_type) {
|
|
25
|
-
return _type.name === 'geography' ? 'longitude' : 'x';
|
|
26
|
-
},
|
|
27
|
-
gisYFieldName(_type) {
|
|
28
|
-
return _type.name === 'geography' ? 'latitude' : 'y';
|
|
29
|
-
},
|
|
30
|
-
gisZFieldName(_type) {
|
|
31
|
-
return _type.name === 'geography' ? 'height' : 'z';
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
});
|
|
35
|
-
};
|
|
36
|
-
exports.default = PostgisInflectionPlugin;
|