flatsql 0.3.9 → 0.4.2

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/sdm/index.d.ts ADDED
@@ -0,0 +1,88 @@
1
+ /**
2
+ * FlatSQL Spatial Engine — Space Data Module (SDM)
3
+ * TypeScript type definitions
4
+ *
5
+ * @see https://github.com/DigitalArsenal/space-data-module-sdk
6
+ */
7
+
8
+ export interface ECEFPoint {
9
+ x: number;
10
+ y: number;
11
+ z: number;
12
+ }
13
+
14
+ export interface GeodeticPoint {
15
+ lat: number;
16
+ lon: number;
17
+ alt: number;
18
+ }
19
+
20
+ export interface MethodResult {
21
+ status: number;
22
+ output: string;
23
+ }
24
+
25
+ export interface PluginManifest {
26
+ pluginId: string;
27
+ name: string;
28
+ version: string;
29
+ pluginFamily: string;
30
+ description: string;
31
+ capabilities: string[];
32
+ externalInterfaces: string[];
33
+ methods: Array<{
34
+ methodId: string;
35
+ description: string;
36
+ inputPorts: Array<{
37
+ portId: string;
38
+ schemaNames: string[];
39
+ maxItems: number;
40
+ }>;
41
+ outputPorts: Array<{
42
+ portId: string;
43
+ schemaNames: string[];
44
+ maxItems: number;
45
+ }>;
46
+ maxBatch: number;
47
+ drainPolicy: string;
48
+ }>;
49
+ runtimeTargets: string[];
50
+ }
51
+
52
+ export interface FlatSQLSpatialSDM {
53
+ /** Get the embedded plugin manifest */
54
+ getManifest(): PluginManifest;
55
+
56
+ /** Compute haversine distance between two WGS84 points (km) */
57
+ computeDistance(lat1: number, lon1: number, lat2: number, lon2: number): number;
58
+
59
+ /** Compute forward azimuth between two WGS84 points (degrees 0-360) */
60
+ computeBearing(lat1: number, lon1: number, lat2: number, lon2: number): number;
61
+
62
+ /** Test if a WGS84 point is inside a WKT polygon */
63
+ pointInPolygon(wkt: string, lat: number, lon: number): boolean;
64
+
65
+ /** Compute intersection of two WKT polygons */
66
+ polygonIntersection(wkt1: string, wkt2: string): string;
67
+
68
+ /** Compute union of two WKT polygons */
69
+ polygonUnion(wkt1: string, wkt2: string): string;
70
+
71
+ /** Compute Voronoi diagram from multipoint WKT within bounds */
72
+ computeVoronoi(multipointWkt: string, boundsWkt: string): string;
73
+
74
+ /** Compute Delaunay triangulation from multipoint WKT */
75
+ computeDelaunay(multipointWkt: string): string;
76
+
77
+ /** Transform WGS84 geodetic to ECEF */
78
+ toECEF(lat: number, lon: number, alt: number): ECEFPoint;
79
+
80
+ /** Transform ECEF to WGS84 geodetic */
81
+ fromECEF(x: number, y: number, z: number): GeodeticPoint;
82
+
83
+ /** Low-level method call */
84
+ callMethod(methodName: string, input: string): MethodResult;
85
+ }
86
+
87
+ /** Initialize the FlatSQL Spatial SDM module */
88
+ export function initSpatialSDM(): Promise<FlatSQLSpatialSDM>;
package/sdm/index.js ADDED
@@ -0,0 +1,253 @@
1
+ /**
2
+ * FlatSQL Spatial Engine — Space Data Module (SDM)
3
+ *
4
+ * Standalone WASM module compliant with space-data-module-sdk.
5
+ * Provides spatial computation functions without requiring full FlatSQL.
6
+ *
7
+ * @see https://github.com/DigitalArsenal/space-data-module-sdk
8
+ */
9
+
10
+ import { readFile } from "node:fs/promises";
11
+ import { fileURLToPath } from "node:url";
12
+ import { dirname, join } from "node:path";
13
+
14
+ const __dirname = dirname(fileURLToPath(import.meta.url));
15
+
16
+ /**
17
+ * Initialize the FlatSQL Spatial SDM module.
18
+ * @returns {Promise<FlatSQLSpatialSDM>}
19
+ */
20
+ export async function initSpatialSDM() {
21
+ const wasmPath = join(__dirname, "flatsql-spatial.wasm");
22
+ const wasmBytes = await readFile(wasmPath);
23
+ let exports;
24
+ const invoke = () => (fnPtr, ...args) => {
25
+ const table = exports?.__indirect_function_table;
26
+ const fn = table?.get(fnPtr);
27
+ if (typeof fn !== "function") {
28
+ throw new Error(`Missing indirect WASM function: ${fnPtr}`);
29
+ }
30
+ return fn(...args);
31
+ };
32
+ const { instance } = await WebAssembly.instantiate(wasmBytes, {
33
+ env: {
34
+ invoke_v: invoke("v"),
35
+ invoke_i: invoke("i"),
36
+ invoke_ii: invoke("ii"),
37
+ invoke_iii: invoke("iii"),
38
+ invoke_iiii: invoke("iiii"),
39
+ invoke_iiiii: invoke("iiiii"),
40
+ invoke_iiiiii: invoke("iiiiii"),
41
+ invoke_iiiiiii: invoke("iiiiiii"),
42
+ invoke_iiiiiiii: invoke("iiiiiiii"),
43
+ invoke_iiiiiiiiiiii: invoke("iiiiiiiiiiii"),
44
+ invoke_iiiiid: invoke("iiiiid"),
45
+ invoke_di: invoke("di"),
46
+ invoke_dii: invoke("dii"),
47
+ invoke_diii: invoke("diii"),
48
+ invoke_fiii: invoke("fiii"),
49
+ invoke_iid: invoke("iid"),
50
+ invoke_jiiii: invoke("jiiii"),
51
+ invoke_vi: invoke("vi"),
52
+ invoke_vid: invoke("vid"),
53
+ invoke_vii: invoke("vii"),
54
+ invoke_viii: invoke("viii"),
55
+ invoke_viiii: invoke("viiii"),
56
+ invoke_viiiii: invoke("viiiii"),
57
+ invoke_viiiiiii: invoke("viiiiiii"),
58
+ invoke_viiiiiiiiii: invoke("viiiiiiiiii"),
59
+ invoke_viiiiiiiiiiiiiii: invoke("viiiiiiiiiiiiiii"),
60
+ __cxa_begin_catch: (ptr) => ptr,
61
+ __cxa_end_catch: () => {},
62
+ __cxa_find_matching_catch_2: () => 0,
63
+ __cxa_find_matching_catch_3: () => 0,
64
+ __cxa_rethrow: () => {},
65
+ __cxa_throw: () => {},
66
+ __cxa_uncaught_exceptions: () => 0,
67
+ __resumeException: (ptr) => {
68
+ throw new Error(`Unhandled WASM exception: ${ptr}`);
69
+ },
70
+ emscripten_notify_memory_growth: () => {},
71
+ },
72
+ wasi_snapshot_preview1: {
73
+ environ_get: () => 0,
74
+ environ_sizes_get: (environCount, environBufSize) => {
75
+ const memory = exports?.memory;
76
+ if (memory) {
77
+ const view = new DataView(memory.buffer);
78
+ view.setUint32(environCount, 0, true);
79
+ view.setUint32(environBufSize, 0, true);
80
+ }
81
+ return 0;
82
+ },
83
+ proc_exit: () => {},
84
+ fd_write: () => 0,
85
+ fd_seek: () => 0,
86
+ fd_close: () => 0,
87
+ },
88
+ });
89
+ exports = instance.exports;
90
+ const memory = /** @type {WebAssembly.Memory} */ (exports.memory);
91
+
92
+ /**
93
+ * Write a string to the input buffer.
94
+ * @param {string} str
95
+ */
96
+ function writeInput(str) {
97
+ const inputPtr = /** @type {Function} */ (exports.sdm_get_input_buffer)();
98
+ const encoder = new TextEncoder();
99
+ const bytes = encoder.encode(str);
100
+ const view = new Uint8Array(memory.buffer, inputPtr, bytes.length);
101
+ view.set(bytes);
102
+ /** @type {Function} */ (exports.sdm_set_input_length)(bytes.length);
103
+ }
104
+
105
+ /**
106
+ * Read the output buffer as a string.
107
+ * @returns {string}
108
+ */
109
+ function readOutput() {
110
+ const outputLen = /** @type {Function} */ (exports.sdm_get_output_length)();
111
+ const outputPtr = /** @type {Function} */ (exports.sdm_get_output_buffer)();
112
+ const view = new Uint8Array(memory.buffer, outputPtr, outputLen);
113
+ return new TextDecoder().decode(view);
114
+ }
115
+
116
+ /**
117
+ * Call a method with string input, return string output.
118
+ * @param {string} methodName
119
+ * @param {string} input
120
+ * @returns {{ status: number, output: string }}
121
+ */
122
+ function callMethod(methodName, input) {
123
+ writeInput(input);
124
+ const fn = exports[methodName];
125
+ if (!fn) throw new Error(`Unknown method: ${methodName}`);
126
+ const status = /** @type {Function} */ (fn)();
127
+ return { status, output: readOutput() };
128
+ }
129
+
130
+ /**
131
+ * Get the embedded plugin manifest.
132
+ * @returns {object}
133
+ */
134
+ function getManifest() {
135
+ const ptr = /** @type {Function} */ (exports.plugin_get_manifest)();
136
+ const size = /** @type {Function} */ (exports.plugin_get_manifest_size)();
137
+ const view = new Uint8Array(memory.buffer, ptr, size);
138
+ const text = new TextDecoder().decode(view);
139
+ return JSON.parse(text);
140
+ }
141
+
142
+ return {
143
+ /** Get the plugin manifest */
144
+ getManifest,
145
+
146
+ /**
147
+ * Compute haversine distance between two points.
148
+ * @param {number} lat1 @param {number} lon1
149
+ * @param {number} lat2 @param {number} lon2
150
+ * @returns {number} distance in km
151
+ */
152
+ computeDistance(lat1, lon1, lat2, lon2) {
153
+ const { status, output } = callMethod("compute_distance", `${lat1},${lon1},${lat2},${lon2}`);
154
+ if (status !== 0) throw new Error(output);
155
+ return parseFloat(output);
156
+ },
157
+
158
+ /**
159
+ * Compute bearing from point 1 to point 2.
160
+ * @param {number} lat1 @param {number} lon1
161
+ * @param {number} lat2 @param {number} lon2
162
+ * @returns {number} bearing in degrees (0-360)
163
+ */
164
+ computeBearing(lat1, lon1, lat2, lon2) {
165
+ const { status, output } = callMethod("compute_bearing", `${lat1},${lon1},${lat2},${lon2}`);
166
+ if (status !== 0) throw new Error(output);
167
+ return parseFloat(output);
168
+ },
169
+
170
+ /**
171
+ * Test if a point is inside a polygon.
172
+ * @param {string} wkt - WKT polygon
173
+ * @param {number} lat @param {number} lon
174
+ * @returns {boolean}
175
+ */
176
+ pointInPolygon(wkt, lat, lon) {
177
+ const { status, output } = callMethod("point_in_polygon", `${wkt}\n${lat},${lon}`);
178
+ if (status !== 0) throw new Error(output);
179
+ return output === "1";
180
+ },
181
+
182
+ /**
183
+ * Compute polygon intersection.
184
+ * @param {string} wkt1 @param {string} wkt2
185
+ * @returns {string} WKT of intersection
186
+ */
187
+ polygonIntersection(wkt1, wkt2) {
188
+ const { status, output } = callMethod("polygon_intersection", `${wkt1}\n${wkt2}`);
189
+ if (status !== 0) throw new Error(output);
190
+ return output;
191
+ },
192
+
193
+ /**
194
+ * Compute polygon union.
195
+ * @param {string} wkt1 @param {string} wkt2
196
+ * @returns {string} WKT of union
197
+ */
198
+ polygonUnion(wkt1, wkt2) {
199
+ const { status, output } = callMethod("polygon_union", `${wkt1}\n${wkt2}`);
200
+ if (status !== 0) throw new Error(output);
201
+ return output;
202
+ },
203
+
204
+ /**
205
+ * Compute Voronoi diagram.
206
+ * @param {string} multipointWkt @param {string} boundsWkt
207
+ * @returns {string} MULTIPOLYGON WKT
208
+ */
209
+ computeVoronoi(multipointWkt, boundsWkt) {
210
+ const { status, output } = callMethod("compute_voronoi", `${multipointWkt}\n${boundsWkt}`);
211
+ if (status !== 0) throw new Error(output);
212
+ return output;
213
+ },
214
+
215
+ /**
216
+ * Compute Delaunay triangulation.
217
+ * @param {string} multipointWkt
218
+ * @returns {string} MULTIPOLYGON WKT
219
+ */
220
+ computeDelaunay(multipointWkt) {
221
+ const { status, output } = callMethod("compute_delaunay", multipointWkt);
222
+ if (status !== 0) throw new Error(output);
223
+ return output;
224
+ },
225
+
226
+ /**
227
+ * Transform WGS84 to ECEF.
228
+ * @param {number} lat @param {number} lon @param {number} alt
229
+ * @returns {{ x: number, y: number, z: number }}
230
+ */
231
+ toECEF(lat, lon, alt) {
232
+ const { status, output } = callMethod("transform_to_ecef", `${lat},${lon},${alt}`);
233
+ if (status !== 0) throw new Error(output);
234
+ const [x, y, z] = output.split(",").map(Number);
235
+ return { x, y, z };
236
+ },
237
+
238
+ /**
239
+ * Transform ECEF to WGS84.
240
+ * @param {number} x @param {number} y @param {number} z
241
+ * @returns {{ lat: number, lon: number, alt: number }}
242
+ */
243
+ fromECEF(x, y, z) {
244
+ const { status, output } = callMethod("transform_from_ecef", `${x},${y},${z}`);
245
+ if (status !== 0) throw new Error(output);
246
+ const [lat, lon, alt] = output.split(",").map(Number);
247
+ return { lat, lon, alt };
248
+ },
249
+
250
+ /** Low-level method call */
251
+ callMethod,
252
+ };
253
+ }