udbx4ts 0.3.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.
@@ -0,0 +1,4101 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
31
+
32
+ // src/runtime-browser/index.ts
33
+ var runtime_browser_exports = {};
34
+ __export(runtime_browser_exports, {
35
+ BrowserSqliteDriver: () => BrowserSqliteDriver,
36
+ createBrowserUdbx: () => createBrowserUdbx,
37
+ createBrowserWorkerRuntime: () => createBrowserWorkerRuntime,
38
+ createSqliteWasmAdapter: () => createSqliteWasmAdapter,
39
+ createUdbxBlob: () => createUdbxBlob,
40
+ downloadBinaryFile: () => downloadBinaryFile,
41
+ downloadUdbxFile: () => downloadUdbxFile,
42
+ exportUdbxToBlob: () => exportUdbxToBlob,
43
+ importUdbxFromFile: () => importUdbxFromFile,
44
+ installBrowserWorkerRuntime: () => installBrowserWorkerRuntime,
45
+ installDefaultBrowserWorkerRuntime: () => installDefaultBrowserWorkerRuntime,
46
+ installSqliteWasmWorkerRuntime: () => installSqliteWasmWorkerRuntime,
47
+ isOpfsAvailable: () => isOpfsAvailable,
48
+ readBlobAsUint8Array: () => readBlobAsUint8Array,
49
+ resolveBrowserSqlOpenTarget: () => resolveBrowserSqlOpenTarget,
50
+ saveBinaryWithPickerOrDownload: () => saveBinaryWithPickerOrDownload,
51
+ saveUdbxToFileHandle: () => saveUdbxToFileHandle,
52
+ saveUdbxWithPickerOrDownload: () => saveUdbxWithPickerOrDownload,
53
+ supportsFileSystemAccess: () => supportsFileSystemAccess,
54
+ writeBinaryToFileHandle: () => writeBinaryToFileHandle
55
+ });
56
+ module.exports = __toCommonJS(runtime_browser_exports);
57
+
58
+ // src/shared-runtime/rpc/methods.ts
59
+ var RPC_METHODS = {
60
+ udbxOpen: "udbx.open",
61
+ udbxClose: "udbx.close",
62
+ udbxListDatasets: "udbx.listDatasets",
63
+ udbxGetDatasetInfo: "udbx.getDatasetInfo",
64
+ udbxExportDatabase: "udbx.exportDatabase",
65
+ udbxImportDatabase: "udbx.importDatabase",
66
+ udbxCreatePointDataset: "udbx.createPointDataset",
67
+ udbxCreateLineDataset: "udbx.createLineDataset",
68
+ udbxCreateRegionDataset: "udbx.createRegionDataset",
69
+ udbxCreatePointZDataset: "udbx.createPointZDataset",
70
+ udbxCreateLineZDataset: "udbx.createLineZDataset",
71
+ udbxCreateRegionZDataset: "udbx.createRegionZDataset",
72
+ udbxCreateTabularDataset: "udbx.createTabularDataset",
73
+ udbxCreateCadDataset: "udbx.createCadDataset",
74
+ datasetGetFields: "dataset.getFields",
75
+ datasetGetById: "dataset.getById",
76
+ datasetList: "dataset.list",
77
+ datasetInsert: "dataset.insert",
78
+ datasetInsertMany: "dataset.insertMany",
79
+ datasetCount: "dataset.count",
80
+ datasetUpdate: "dataset.update",
81
+ datasetDelete: "dataset.delete"
82
+ };
83
+
84
+ // src/runtime-browser/client/BrowserDatasetClient.ts
85
+ var BrowserDatasetClient = class {
86
+ constructor(transport, info) {
87
+ this.transport = transport;
88
+ this.info = info;
89
+ }
90
+ async getFields() {
91
+ return this.transport.request(
92
+ RPC_METHODS.datasetGetFields,
93
+ { datasetName: this.info.name }
94
+ );
95
+ }
96
+ async getById(id) {
97
+ return this.transport.request(RPC_METHODS.datasetGetById, { datasetName: this.info.name, id });
98
+ }
99
+ async list(options) {
100
+ const params = options === void 0 ? { datasetName: this.info.name } : { datasetName: this.info.name, options };
101
+ return this.transport.request(RPC_METHODS.datasetList, params);
102
+ }
103
+ async *iterate(options) {
104
+ const rows = await this.list(options);
105
+ for (const row of rows) {
106
+ yield row;
107
+ }
108
+ }
109
+ async insert(feature) {
110
+ await this.transport.request(
111
+ RPC_METHODS.datasetInsert,
112
+ { datasetName: this.info.name, feature }
113
+ );
114
+ }
115
+ async insertMany(features) {
116
+ const buffer = [];
117
+ for await (const feature of features) {
118
+ buffer.push(feature);
119
+ }
120
+ await this.transport.request(RPC_METHODS.datasetInsertMany, {
121
+ datasetName: this.info.name,
122
+ features: buffer
123
+ });
124
+ }
125
+ async count() {
126
+ return this.transport.request(
127
+ RPC_METHODS.datasetCount,
128
+ { datasetName: this.info.name }
129
+ );
130
+ }
131
+ async update(id, changes) {
132
+ await this.transport.request(RPC_METHODS.datasetUpdate, { datasetName: this.info.name, id, changes });
133
+ }
134
+ async delete(id) {
135
+ await this.transport.request(RPC_METHODS.datasetDelete, { datasetName: this.info.name, id });
136
+ }
137
+ };
138
+
139
+ // src/runtime-browser/client/BrowserUdbxClient.ts
140
+ var BrowserUdbxClient = class _BrowserUdbxClient {
141
+ constructor(transport, runtime) {
142
+ this.transport = transport;
143
+ __publicField(this, "runtime");
144
+ this.runtime = runtime;
145
+ }
146
+ static async connect(transport, options = {}) {
147
+ const response = await transport.request(
148
+ RPC_METHODS.udbxOpen,
149
+ options
150
+ );
151
+ return new _BrowserUdbxClient(transport, response.runtime);
152
+ }
153
+ async listDatasets() {
154
+ return this.transport.request(RPC_METHODS.udbxListDatasets);
155
+ }
156
+ async getDataset(name) {
157
+ const info = await this.transport.request(RPC_METHODS.udbxGetDatasetInfo, { name });
158
+ if (!info) {
159
+ return null;
160
+ }
161
+ return new BrowserDatasetClient(this.transport, info);
162
+ }
163
+ async createPointDataset(name, srid, fields) {
164
+ return this.createDatasetByMethod(RPC_METHODS.udbxCreatePointDataset, name, srid, fields);
165
+ }
166
+ async createLineDataset(name, srid, fields) {
167
+ return this.createDatasetByMethod(RPC_METHODS.udbxCreateLineDataset, name, srid, fields);
168
+ }
169
+ async createRegionDataset(name, srid, fields) {
170
+ return this.createDatasetByMethod(RPC_METHODS.udbxCreateRegionDataset, name, srid, fields);
171
+ }
172
+ async createTabularDataset(name, fields) {
173
+ const params = {
174
+ name,
175
+ ...fields === void 0 ? {} : { fields }
176
+ };
177
+ const info = await this.transport.request(RPC_METHODS.udbxCreateTabularDataset, params);
178
+ return new BrowserDatasetClient(this.transport, info);
179
+ }
180
+ async close() {
181
+ await this.transport.request(RPC_METHODS.udbxClose);
182
+ await this.transport.close();
183
+ }
184
+ async exportDatabase() {
185
+ return this.transport.request(RPC_METHODS.udbxExportDatabase);
186
+ }
187
+ async importDatabase(binary, options = {}) {
188
+ const params = options.preferOpfs === void 0 ? { binary } : { binary, preferOpfs: options.preferOpfs };
189
+ await this.transport.request(
190
+ RPC_METHODS.udbxImportDatabase,
191
+ params
192
+ );
193
+ }
194
+ async createDatasetByMethod(method, name, srid, fields) {
195
+ const params = {
196
+ name,
197
+ srid,
198
+ ...fields === void 0 ? {} : { fields }
199
+ };
200
+ const info = await this.transport.request(method, params);
201
+ return new BrowserDatasetClient(this.transport, info);
202
+ }
203
+ };
204
+
205
+ // src/runtime-browser/client/BrowserWorkerTransport.ts
206
+ function isRuntimeResponse(value) {
207
+ if (typeof value !== "object" || value === null) {
208
+ return false;
209
+ }
210
+ return "id" in value && "ok" in value;
211
+ }
212
+ function toErrorMessage(response) {
213
+ return `${response.error.code}: ${response.error.message}`;
214
+ }
215
+ var BrowserWorkerTransport = class {
216
+ constructor(worker) {
217
+ this.worker = worker;
218
+ __publicField(this, "requestId", 0);
219
+ __publicField(this, "pending", /* @__PURE__ */ new Map());
220
+ __publicField(this, "onMessage", (event) => {
221
+ const response = event.data;
222
+ if (!isRuntimeResponse(response)) {
223
+ return;
224
+ }
225
+ const resolver = this.pending.get(response.id);
226
+ if (!resolver) {
227
+ return;
228
+ }
229
+ this.pending.delete(response.id);
230
+ if (response.ok) {
231
+ resolver.resolve(response.result);
232
+ return;
233
+ }
234
+ resolver.reject(new Error(toErrorMessage(response)));
235
+ });
236
+ this.worker.addEventListener("message", this.onMessage);
237
+ }
238
+ async request(method, params) {
239
+ const id = `${Date.now()}-${this.requestId++}`;
240
+ const request = params === void 0 ? { id, method } : { id, method, params };
241
+ const promise = new Promise((resolve, reject) => {
242
+ this.pending.set(id, {
243
+ resolve: (value) => resolve(value),
244
+ reject
245
+ });
246
+ });
247
+ this.worker.postMessage(request);
248
+ return promise;
249
+ }
250
+ async close() {
251
+ this.worker.removeEventListener("message", this.onMessage);
252
+ for (const [, pending] of this.pending) {
253
+ pending.reject(new Error("Transport closed before response was received."));
254
+ }
255
+ this.pending.clear();
256
+ this.worker.terminate();
257
+ }
258
+ };
259
+
260
+ // src/runtime-browser/sqlite/BrowserSqliteDriver.ts
261
+ function normalizeSqlValues(params) {
262
+ if (!params) {
263
+ return [];
264
+ }
265
+ return params.map((value) => {
266
+ if (value instanceof ArrayBuffer) {
267
+ return new Uint8Array(value);
268
+ }
269
+ return value;
270
+ });
271
+ }
272
+ var BrowserSqliteStatement = class {
273
+ constructor(adapter) {
274
+ this.adapter = adapter;
275
+ __publicField(this, "boundParams");
276
+ }
277
+ async bind(params) {
278
+ this.boundParams = params;
279
+ await this.adapter.bind(normalizeSqlValues(params));
280
+ }
281
+ async step() {
282
+ return this.adapter.step();
283
+ }
284
+ async getRow() {
285
+ return this.adapter.getRow();
286
+ }
287
+ async reset() {
288
+ await this.adapter.reset();
289
+ this.boundParams = void 0;
290
+ }
291
+ async finalize() {
292
+ await this.adapter.finalize();
293
+ this.boundParams = void 0;
294
+ }
295
+ };
296
+ var BrowserSqliteDriver = class {
297
+ constructor(adapter) {
298
+ this.adapter = adapter;
299
+ __publicField(this, "connection", null);
300
+ __publicField(this, "transactionDepth", 0);
301
+ }
302
+ async open(target) {
303
+ if (this.connection) {
304
+ throw new Error("Database is already open.");
305
+ }
306
+ this.connection = await this.adapter.open(target);
307
+ }
308
+ async close() {
309
+ if (!this.connection) {
310
+ return;
311
+ }
312
+ await this.connection.close();
313
+ this.connection = null;
314
+ this.transactionDepth = 0;
315
+ }
316
+ async exec(sql) {
317
+ await this.assertOpen().exec(sql);
318
+ }
319
+ async prepare(sql) {
320
+ const statement = await this.assertOpen().prepare(sql);
321
+ return new BrowserSqliteStatement(statement);
322
+ }
323
+ async transaction(operation) {
324
+ const nested = this.transactionDepth > 0;
325
+ const savepointName = `udbx_sp_${this.transactionDepth}`;
326
+ if (nested) {
327
+ await this.exec(`SAVEPOINT ${savepointName}`);
328
+ } else {
329
+ await this.exec("BEGIN");
330
+ }
331
+ this.transactionDepth += 1;
332
+ try {
333
+ const result = await operation();
334
+ this.transactionDepth -= 1;
335
+ if (nested) {
336
+ await this.exec(`RELEASE SAVEPOINT ${savepointName}`);
337
+ } else {
338
+ await this.exec("COMMIT");
339
+ }
340
+ return result;
341
+ } catch (error) {
342
+ this.transactionDepth -= 1;
343
+ if (nested) {
344
+ await this.exec(`ROLLBACK TO SAVEPOINT ${savepointName}`);
345
+ await this.exec(`RELEASE SAVEPOINT ${savepointName}`);
346
+ } else {
347
+ await this.exec("ROLLBACK");
348
+ }
349
+ throw error;
350
+ }
351
+ }
352
+ async exportDatabase() {
353
+ const connection = this.assertOpen();
354
+ if (typeof connection.exportDatabase !== "function") {
355
+ throw new Error("Database export is not supported by current browser driver.");
356
+ }
357
+ return connection.exportDatabase();
358
+ }
359
+ async importDatabase(target, binary) {
360
+ if (typeof this.adapter.importDatabase !== "function") {
361
+ throw new Error("Database import is not supported by current browser driver.");
362
+ }
363
+ await this.adapter.importDatabase(target, binary);
364
+ }
365
+ assertOpen() {
366
+ if (!this.connection) {
367
+ throw new Error("Database is not open.");
368
+ }
369
+ return this.connection;
370
+ }
371
+ };
372
+
373
+ // src/runtime-browser/sqlite/createSqliteWasmAdapter.ts
374
+ async function defaultInitSqlite3() {
375
+ const module2 = await import("@sqlite.org/sqlite-wasm");
376
+ return module2.default();
377
+ }
378
+ function ensureOpfsSupported(sqlite3) {
379
+ if (typeof sqlite3.oo1.OpfsDb !== "function") {
380
+ throw new Error("OPFS VFS is unavailable in current browser runtime.");
381
+ }
382
+ }
383
+ function toFilename(target) {
384
+ switch (target.kind) {
385
+ case "memory":
386
+ return { filename: ":memory:", flags: "c" };
387
+ case "file":
388
+ return { filename: target.path, flags: "c" };
389
+ case "opfs":
390
+ return { filename: target.path, flags: "c", vfs: "opfs" };
391
+ case "buffer":
392
+ throw new Error("Buffer open target is not supported by sqlite-wasm adapter yet.");
393
+ }
394
+ }
395
+ var SqliteWasmStatementAdapter = class {
396
+ constructor(statement) {
397
+ this.statement = statement;
398
+ }
399
+ bind(params) {
400
+ this.statement.bind(params);
401
+ }
402
+ step() {
403
+ return this.statement.step();
404
+ }
405
+ getRow() {
406
+ return this.statement.get({});
407
+ }
408
+ reset() {
409
+ this.statement.reset();
410
+ }
411
+ finalize() {
412
+ this.statement.finalize();
413
+ }
414
+ };
415
+ var SqliteWasmConnectionAdapter = class {
416
+ constructor(sqlite3, db) {
417
+ this.sqlite3 = sqlite3;
418
+ this.db = db;
419
+ }
420
+ exec(sql) {
421
+ this.db.exec(sql);
422
+ }
423
+ prepare(sql) {
424
+ return new SqliteWasmStatementAdapter(this.db.prepare(sql));
425
+ }
426
+ close() {
427
+ this.db.close();
428
+ }
429
+ exportDatabase() {
430
+ return this.sqlite3.capi.sqlite3_js_db_export(this.db, "main");
431
+ }
432
+ };
433
+ function createSqliteWasmAdapter(options = {}) {
434
+ const initSqlite3 = options.initSqlite3 ?? defaultInitSqlite3;
435
+ let sqlite3Promise = null;
436
+ const getSqlite3 = async () => {
437
+ sqlite3Promise ?? (sqlite3Promise = initSqlite3());
438
+ return sqlite3Promise;
439
+ };
440
+ return {
441
+ importDatabase: async (target, binary) => {
442
+ if (target.kind !== "opfs") {
443
+ throw new Error(
444
+ "sqlite-wasm adapter currently supports import only for OPFS targets."
445
+ );
446
+ }
447
+ const sqlite3 = await getSqlite3();
448
+ ensureOpfsSupported(sqlite3);
449
+ await sqlite3.oo1.OpfsDb.importDb(target.path, binary);
450
+ },
451
+ open: async (target) => {
452
+ const sqlite3 = await getSqlite3();
453
+ const { filename, flags, vfs } = toFilename(target);
454
+ if (target.kind === "opfs") {
455
+ ensureOpfsSupported(sqlite3);
456
+ return new SqliteWasmConnectionAdapter(
457
+ sqlite3,
458
+ new sqlite3.oo1.OpfsDb(filename, flags)
459
+ );
460
+ }
461
+ return new SqliteWasmConnectionAdapter(
462
+ sqlite3,
463
+ new sqlite3.oo1.DB(filename, flags, vfs)
464
+ );
465
+ }
466
+ };
467
+ }
468
+
469
+ // src/runtime-browser/sqlite/resolveBrowserSqlOpenTarget.ts
470
+ function isOpfsAvailable(probe = globalThis) {
471
+ return typeof probe.navigator?.storage?.getDirectory === "function";
472
+ }
473
+ function resolveBrowserSqlOpenTarget(options = {}) {
474
+ const capabilities = options.capabilities ?? {
475
+ hasOpfs: isOpfsAvailable(options.capabilityProbe)
476
+ };
477
+ const preferOpfs = options.preferOpfs ?? true;
478
+ if (preferOpfs && capabilities.hasOpfs) {
479
+ return {
480
+ kind: "opfs",
481
+ path: options.opfsPath ?? "udbx/default.udbx"
482
+ };
483
+ }
484
+ if (options.memoryName === void 0) {
485
+ return { kind: "memory" };
486
+ }
487
+ return {
488
+ kind: "memory",
489
+ name: options.memoryName
490
+ };
491
+ }
492
+
493
+ // src/core/sql/SqlHelpers.ts
494
+ async function queryAll(driver, sql, params) {
495
+ const statement = await driver.prepare(sql);
496
+ try {
497
+ if (params) {
498
+ await statement.bind(params);
499
+ }
500
+ const rows = [];
501
+ while (await statement.step()) {
502
+ rows.push(await statement.getRow());
503
+ }
504
+ return rows;
505
+ } finally {
506
+ await statement.finalize();
507
+ }
508
+ }
509
+ async function queryOne(driver, sql, params) {
510
+ const rows = await queryAll(driver, sql, params);
511
+ return rows[0] ?? null;
512
+ }
513
+ async function executeStatement(statement, params) {
514
+ try {
515
+ if (params) {
516
+ await statement.bind(params);
517
+ }
518
+ await statement.step();
519
+ } finally {
520
+ await statement.finalize();
521
+ }
522
+ }
523
+ async function executeSql(driver, sql, params) {
524
+ const statement = await driver.prepare(sql);
525
+ await executeStatement(statement, params);
526
+ }
527
+
528
+ // src/core/schema/UdbxTypeMappings.ts
529
+ var datasetKindToValueMap = {
530
+ tabular: 0,
531
+ point: 1,
532
+ line: 3,
533
+ region: 5,
534
+ pointZ: 101,
535
+ lineZ: 103,
536
+ regionZ: 105,
537
+ text: 7,
538
+ cad: 149
539
+ };
540
+ var datasetValueToKindMap = new Map(
541
+ Object.entries(datasetKindToValueMap).map(([kind, value]) => [
542
+ value,
543
+ kind
544
+ ])
545
+ );
546
+ var fieldTypeToValueMap = {
547
+ boolean: 1,
548
+ byte: 2,
549
+ int16: 3,
550
+ int32: 4,
551
+ int64: 5,
552
+ single: 6,
553
+ double: 7,
554
+ date: 8,
555
+ binary: 9,
556
+ geometry: 10,
557
+ char: 11,
558
+ ntext: 127,
559
+ text: 128,
560
+ time: 16
561
+ };
562
+ var fieldValueToTypeMap = /* @__PURE__ */ new Map([
563
+ [1, "boolean"],
564
+ [2, "byte"],
565
+ [3, "int16"],
566
+ [4, "int32"],
567
+ [5, "int64"],
568
+ [6, "single"],
569
+ [7, "double"],
570
+ [8, "date"],
571
+ [9, "binary"],
572
+ [10, "geometry"],
573
+ [11, "char"],
574
+ [16, "time"],
575
+ [127, "ntext"],
576
+ [128, "text"]
577
+ ]);
578
+ function datasetKindToValue(kind) {
579
+ return datasetKindToValueMap[kind];
580
+ }
581
+ function datasetValueToKind(value) {
582
+ const kind = datasetValueToKindMap.get(value);
583
+ if (!kind) {
584
+ throw new Error(`Unsupported SmDatasetType value: ${value}.`);
585
+ }
586
+ return kind;
587
+ }
588
+ function fieldTypeToValue(type) {
589
+ return fieldTypeToValueMap[type];
590
+ }
591
+ function fieldValueToType(value) {
592
+ const type = fieldValueToTypeMap.get(value);
593
+ if (!type) {
594
+ throw new Error(`Unsupported SmFieldType value: ${value}.`);
595
+ }
596
+ return type;
597
+ }
598
+
599
+ // src/core/schema/SmFieldInfoRepository.ts
600
+ function mapRow(row) {
601
+ return {
602
+ name: row.SmFieldName,
603
+ fieldType: fieldValueToType(row.SmFieldType),
604
+ nullable: row.SmFieldbRequired !== 1,
605
+ defaultValue: row.SmFieldDefaultValue ?? void 0
606
+ };
607
+ }
608
+ var SmFieldInfoRepository = class {
609
+ constructor(driver) {
610
+ this.driver = driver;
611
+ }
612
+ async findByDatasetId(datasetId) {
613
+ const rows = await queryAll(
614
+ this.driver,
615
+ `SELECT
616
+ SmFieldName,
617
+ SmFieldType,
618
+ SmFieldbRequired,
619
+ SmFieldDefaultValue
620
+ FROM SmFieldInfo
621
+ WHERE SmDatasetID = ?
622
+ ORDER BY SmID`,
623
+ [datasetId]
624
+ );
625
+ return rows.map(mapRow);
626
+ }
627
+ async insertAll(datasetId, fields) {
628
+ for (const field of fields) {
629
+ const statement = await this.driver.prepare(
630
+ `INSERT INTO SmFieldInfo (
631
+ SmDatasetID,
632
+ SmFieldName,
633
+ SmFieldType,
634
+ SmFieldCaption,
635
+ SmFieldbRequired,
636
+ SmFieldDefaultValue
637
+ ) VALUES (?, ?, ?, ?, ?, ?)`
638
+ );
639
+ await executeStatement(statement, [
640
+ datasetId,
641
+ field.name,
642
+ fieldTypeToValue(field.fieldType),
643
+ field.name,
644
+ field.nullable ? 0 : 1,
645
+ field.defaultValue == null ? null : String(field.defaultValue)
646
+ ]);
647
+ }
648
+ }
649
+ };
650
+
651
+ // src/core/dataset/BaseDataset.ts
652
+ var BaseDataset = class {
653
+ constructor(driver, info) {
654
+ this.driver = driver;
655
+ this.info = info;
656
+ __publicField(this, "fieldInfoRepository");
657
+ this.fieldInfoRepository = new SmFieldInfoRepository(driver);
658
+ }
659
+ getFields() {
660
+ return this.fieldInfoRepository.findByDatasetId(this.info.id);
661
+ }
662
+ };
663
+
664
+ // src/core/utils/errors.ts
665
+ var NotImplementedError = class extends Error {
666
+ constructor(message) {
667
+ super(message);
668
+ this.name = "NotImplementedError";
669
+ }
670
+ };
671
+ function createNotImplementedError(scope) {
672
+ return new NotImplementedError(`${scope} is not implemented yet.`);
673
+ }
674
+
675
+ // src/core/dataset/CadDataset.ts
676
+ var CadDataset = class extends BaseDataset {
677
+ async getById() {
678
+ throw createNotImplementedError("CadDataset.getById");
679
+ }
680
+ async list() {
681
+ throw createNotImplementedError("CadDataset.list");
682
+ }
683
+ async *iterate() {
684
+ throw createNotImplementedError("CadDataset.iterate");
685
+ }
686
+ async count() {
687
+ throw createNotImplementedError("CadDataset.count");
688
+ }
689
+ async insert() {
690
+ throw createNotImplementedError("CadDataset.insert");
691
+ }
692
+ async insertMany() {
693
+ throw createNotImplementedError("CadDataset.insertMany");
694
+ }
695
+ async update() {
696
+ throw createNotImplementedError("CadDataset.update");
697
+ }
698
+ async delete() {
699
+ throw createNotImplementedError("CadDataset.delete");
700
+ }
701
+ };
702
+
703
+ // src/core/utils/BinaryCursor.ts
704
+ function toUint8Array(input) {
705
+ if (input instanceof Uint8Array) {
706
+ return input;
707
+ }
708
+ if (ArrayBuffer.isView(input)) {
709
+ return new Uint8Array(input.buffer, input.byteOffset, input.byteLength);
710
+ }
711
+ return new Uint8Array(input);
712
+ }
713
+ var BinaryCursor = class {
714
+ constructor(input) {
715
+ __publicField(this, "bytes");
716
+ __publicField(this, "view");
717
+ __publicField(this, "offset", 0);
718
+ this.bytes = toUint8Array(input);
719
+ this.view = new DataView(
720
+ this.bytes.buffer,
721
+ this.bytes.byteOffset,
722
+ this.bytes.byteLength
723
+ );
724
+ }
725
+ get length() {
726
+ return this.bytes.byteLength;
727
+ }
728
+ get position() {
729
+ return this.offset;
730
+ }
731
+ get remaining() {
732
+ return this.length - this.offset;
733
+ }
734
+ seek(position) {
735
+ if (position < 0 || position > this.length) {
736
+ throw new RangeError(
737
+ `Cannot seek to ${position}; valid range is 0..${this.length}.`
738
+ );
739
+ }
740
+ this.offset = position;
741
+ }
742
+ skip(length) {
743
+ this.seek(this.offset + length);
744
+ }
745
+ readUint8() {
746
+ this.ensureAvailable(1);
747
+ const value = this.view.getUint8(this.offset);
748
+ this.offset += 1;
749
+ return value;
750
+ }
751
+ readInt32(littleEndian = true) {
752
+ this.ensureAvailable(4);
753
+ const value = this.view.getInt32(this.offset, littleEndian);
754
+ this.offset += 4;
755
+ return value;
756
+ }
757
+ readFloat64(littleEndian = true) {
758
+ this.ensureAvailable(8);
759
+ const value = this.view.getFloat64(this.offset, littleEndian);
760
+ this.offset += 8;
761
+ return value;
762
+ }
763
+ readBytes(length) {
764
+ this.ensureAvailable(length);
765
+ const value = this.bytes.slice(this.offset, this.offset + length);
766
+ this.offset += length;
767
+ return value;
768
+ }
769
+ peekUint8() {
770
+ this.ensureAvailable(1);
771
+ return this.view.getUint8(this.offset);
772
+ }
773
+ ensureAvailable(length) {
774
+ if (length < 0) {
775
+ throw new RangeError("Length must be non-negative.");
776
+ }
777
+ if (this.offset + length > this.length) {
778
+ throw new RangeError(
779
+ `Cannot read ${length} byte(s) from offset ${this.offset}; only ${this.remaining} byte(s) remain.`
780
+ );
781
+ }
782
+ }
783
+ };
784
+
785
+ // src/core/utils/BinaryWriter.ts
786
+ var BinaryWriter = class {
787
+ constructor(initialCapacity = 64) {
788
+ __publicField(this, "buffer");
789
+ __publicField(this, "view");
790
+ __publicField(this, "bytes");
791
+ __publicField(this, "offset", 0);
792
+ if (initialCapacity <= 0) {
793
+ throw new RangeError("Initial capacity must be greater than 0.");
794
+ }
795
+ this.buffer = new ArrayBuffer(initialCapacity);
796
+ this.view = new DataView(this.buffer);
797
+ this.bytes = new Uint8Array(this.buffer);
798
+ }
799
+ get length() {
800
+ return this.offset;
801
+ }
802
+ writeUint8(value) {
803
+ this.ensureCapacity(1);
804
+ this.view.setUint8(this.offset, value);
805
+ this.offset += 1;
806
+ }
807
+ writeInt32(value, littleEndian = true) {
808
+ this.ensureCapacity(4);
809
+ this.view.setInt32(this.offset, value, littleEndian);
810
+ this.offset += 4;
811
+ }
812
+ writeFloat64(value, littleEndian = true) {
813
+ this.ensureCapacity(8);
814
+ this.view.setFloat64(this.offset, value, littleEndian);
815
+ this.offset += 8;
816
+ }
817
+ writeBytes(value) {
818
+ const bytes = value instanceof Uint8Array ? value : ArrayBuffer.isView(value) ? new Uint8Array(value.buffer, value.byteOffset, value.byteLength) : new Uint8Array(value);
819
+ this.ensureCapacity(bytes.byteLength);
820
+ this.bytes.set(bytes, this.offset);
821
+ this.offset += bytes.byteLength;
822
+ }
823
+ toUint8Array() {
824
+ return this.bytes.slice(0, this.offset);
825
+ }
826
+ ensureCapacity(required) {
827
+ if (this.offset + required <= this.buffer.byteLength) {
828
+ return;
829
+ }
830
+ let nextCapacity = this.buffer.byteLength;
831
+ while (this.offset + required > nextCapacity) {
832
+ nextCapacity *= 2;
833
+ }
834
+ const nextBuffer = new ArrayBuffer(nextCapacity);
835
+ const nextBytes = new Uint8Array(nextBuffer);
836
+ nextBytes.set(this.bytes.subarray(0, this.offset));
837
+ this.buffer = nextBuffer;
838
+ this.view = new DataView(this.buffer);
839
+ this.bytes = nextBytes;
840
+ }
841
+ };
842
+
843
+ // src/core/geometry/gaia/GaiaConstants.ts
844
+ var GAIA_START = 0;
845
+ var GAIA_BYTE_ORDER_LE = 1;
846
+ var GAIA_MBR = 124;
847
+ var GAIA_END = 254;
848
+ var GAIA_GEOMETRY_DATA_OFFSET = 43;
849
+ var GEO_TYPE_POINT = 1;
850
+ var GEO_TYPE_POINTZ = 1001;
851
+ var GEO_TYPE_MULTILINESTRING = 5;
852
+ var GEO_TYPE_MULTILINESTRINGZ = 1005;
853
+ var GEO_TYPE_MULTIPOLYGON = 6;
854
+ var GEO_TYPE_MULTIPOLYGONZ = 1006;
855
+
856
+ // src/core/geometry/gaia/GaiaErrors.ts
857
+ var GaiaError = class extends Error {
858
+ constructor(message) {
859
+ super(message);
860
+ this.name = "GaiaError";
861
+ }
862
+ };
863
+ var GaiaFormatError = class extends GaiaError {
864
+ constructor(message) {
865
+ super(message);
866
+ this.name = "GaiaFormatError";
867
+ }
868
+ };
869
+ var GaiaGeoTypeMismatchError = class extends GaiaError {
870
+ constructor(expected, actual) {
871
+ super(`Expected geoType=${expected}, got ${actual}.`);
872
+ this.name = "GaiaGeoTypeMismatchError";
873
+ }
874
+ };
875
+
876
+ // src/core/geometry/gaia/GaiaHeader.ts
877
+ function readGaiaHeader(cursor, expectedGeoType) {
878
+ cursor.seek(0);
879
+ const gaiaStart = cursor.readUint8();
880
+ if (gaiaStart !== GAIA_START) {
881
+ throw new GaiaFormatError(
882
+ `Invalid GAIA start marker: expected 0x${GAIA_START.toString(16)}, got 0x${gaiaStart.toString(16)}.`
883
+ );
884
+ }
885
+ const byteOrder = cursor.readUint8();
886
+ if (byteOrder !== GAIA_BYTE_ORDER_LE) {
887
+ throw new GaiaFormatError(
888
+ `Unsupported byte order: expected 0x${GAIA_BYTE_ORDER_LE.toString(16)}, got 0x${byteOrder.toString(16)}.`
889
+ );
890
+ }
891
+ const srid = cursor.readInt32(true);
892
+ const minX = cursor.readFloat64(true);
893
+ const minY = cursor.readFloat64(true);
894
+ const maxX = cursor.readFloat64(true);
895
+ const maxY = cursor.readFloat64(true);
896
+ const gaiaMbr = cursor.readUint8();
897
+ if (gaiaMbr !== GAIA_MBR) {
898
+ throw new GaiaFormatError(
899
+ `Invalid GAIA MBR marker: expected 0x${GAIA_MBR.toString(16)}, got 0x${gaiaMbr.toString(16)}.`
900
+ );
901
+ }
902
+ const geoType = cursor.readInt32(true);
903
+ if (expectedGeoType !== void 0 && geoType !== expectedGeoType) {
904
+ throw new GaiaGeoTypeMismatchError(expectedGeoType, geoType);
905
+ }
906
+ return {
907
+ byteOrder,
908
+ srid,
909
+ mbr: [minX, minY, maxX, maxY],
910
+ geoType,
911
+ geometryDataOffset: GAIA_GEOMETRY_DATA_OFFSET
912
+ };
913
+ }
914
+ function validateGaiaEnd(cursor) {
915
+ const gaiaEnd = cursor.readUint8();
916
+ if (gaiaEnd !== GAIA_END) {
917
+ throw new GaiaFormatError(
918
+ `Invalid GAIA end marker: expected 0x${GAIA_END.toString(16)}, got 0x${gaiaEnd.toString(16)}.`
919
+ );
920
+ }
921
+ }
922
+
923
+ // src/core/geometry/gaia/GaiaPointCodec.ts
924
+ function isPoint3D(geometry) {
925
+ return geometry.coordinates.length === 3;
926
+ }
927
+ function createPointMbr(coordinates) {
928
+ return [coordinates[0], coordinates[1], coordinates[0], coordinates[1]];
929
+ }
930
+ function writeGaiaHeader(writer, srid, mbr, geoType) {
931
+ writer.writeUint8(GAIA_START);
932
+ writer.writeUint8(GAIA_BYTE_ORDER_LE);
933
+ writer.writeInt32(srid, true);
934
+ writer.writeFloat64(mbr[0], true);
935
+ writer.writeFloat64(mbr[1], true);
936
+ writer.writeFloat64(mbr[2], true);
937
+ writer.writeFloat64(mbr[3], true);
938
+ writer.writeUint8(GAIA_MBR);
939
+ writer.writeInt32(geoType, true);
940
+ }
941
+ var GaiaPointCodec = class {
942
+ static readPoint(input) {
943
+ const cursor = new BinaryCursor(input);
944
+ const header = readGaiaHeader(cursor, GEO_TYPE_POINT);
945
+ const x = cursor.readFloat64(true);
946
+ const y = cursor.readFloat64(true);
947
+ validateGaiaEnd(cursor);
948
+ return {
949
+ type: "Point",
950
+ coordinates: [x, y],
951
+ srid: header.srid,
952
+ bbox: header.mbr,
953
+ hasZ: false,
954
+ geoType: header.geoType
955
+ };
956
+ }
957
+ static readPointZ(input) {
958
+ const cursor = new BinaryCursor(input);
959
+ const header = readGaiaHeader(cursor, GEO_TYPE_POINTZ);
960
+ const x = cursor.readFloat64(true);
961
+ const y = cursor.readFloat64(true);
962
+ const z = cursor.readFloat64(true);
963
+ validateGaiaEnd(cursor);
964
+ return {
965
+ type: "Point",
966
+ coordinates: [x, y, z],
967
+ srid: header.srid,
968
+ bbox: header.mbr,
969
+ hasZ: true,
970
+ geoType: header.geoType
971
+ };
972
+ }
973
+ static writePoint(geometry, srid) {
974
+ if (isPoint3D(geometry)) {
975
+ throw new GaiaFormatError("writePoint expects a 2D Point geometry.");
976
+ }
977
+ const writer = new BinaryWriter(60);
978
+ const mbr = createPointMbr(geometry.coordinates);
979
+ writeGaiaHeader(writer, srid, mbr, GEO_TYPE_POINT);
980
+ writer.writeFloat64(geometry.coordinates[0], true);
981
+ writer.writeFloat64(geometry.coordinates[1], true);
982
+ writer.writeUint8(GAIA_END);
983
+ return writer.toUint8Array();
984
+ }
985
+ static writePointZ(geometry, srid) {
986
+ if (!isPoint3D(geometry)) {
987
+ throw new GaiaFormatError("writePointZ expects a 3D Point geometry.");
988
+ }
989
+ const writer = new BinaryWriter(68);
990
+ const mbr = createPointMbr(geometry.coordinates);
991
+ writeGaiaHeader(writer, srid, mbr, GEO_TYPE_POINTZ);
992
+ writer.writeFloat64(geometry.coordinates[0], true);
993
+ writer.writeFloat64(geometry.coordinates[1], true);
994
+ writer.writeFloat64(geometry.coordinates[2], true);
995
+ writer.writeUint8(GAIA_END);
996
+ return writer.toUint8Array();
997
+ }
998
+ };
999
+
1000
+ // src/core/geometry/gaia/GaiaLineCodec.ts
1001
+ var GAIA_ENTITY_MARK = 105;
1002
+ var GEO_TYPE_LINESTRING = 2;
1003
+ var GEO_TYPE_LINESTRINGZ = 1002;
1004
+ function is3DCoordinates(coordinates) {
1005
+ return coordinates[0]?.[0]?.length === 3;
1006
+ }
1007
+ function createLineMbr(coordinates) {
1008
+ const xs = [];
1009
+ const ys = [];
1010
+ for (const line of coordinates) {
1011
+ for (const point of line) {
1012
+ xs.push(point[0]);
1013
+ ys.push(point[1]);
1014
+ }
1015
+ }
1016
+ if (xs.length === 0) {
1017
+ throw new GaiaFormatError("MultiLineString must contain at least one point.");
1018
+ }
1019
+ return [
1020
+ Math.min(...xs),
1021
+ Math.min(...ys),
1022
+ Math.max(...xs),
1023
+ Math.max(...ys)
1024
+ ];
1025
+ }
1026
+ var GaiaLineCodec = class {
1027
+ static readMultiLineString(input) {
1028
+ const cursor = new BinaryCursor(input);
1029
+ const header = readGaiaHeader(cursor, GEO_TYPE_MULTILINESTRING);
1030
+ const lineCount = cursor.readInt32(true);
1031
+ const coordinates = [];
1032
+ for (let lineIndex = 0; lineIndex < lineCount; lineIndex += 1) {
1033
+ const entityMark = cursor.readUint8();
1034
+ if (entityMark !== GAIA_ENTITY_MARK) {
1035
+ throw new GaiaFormatError(
1036
+ `Invalid LineString entity mark: expected 0x${GAIA_ENTITY_MARK.toString(16)}, got 0x${entityMark.toString(16)}.`
1037
+ );
1038
+ }
1039
+ const lineGeoType = cursor.readInt32(true);
1040
+ if (lineGeoType !== GEO_TYPE_LINESTRING) {
1041
+ throw new GaiaFormatError(
1042
+ `Invalid LineString geoType: expected ${GEO_TYPE_LINESTRING}, got ${lineGeoType}.`
1043
+ );
1044
+ }
1045
+ const pointCount = cursor.readInt32(true);
1046
+ const line = [];
1047
+ for (let pointIndex = 0; pointIndex < pointCount; pointIndex += 1) {
1048
+ line.push([cursor.readFloat64(true), cursor.readFloat64(true)]);
1049
+ }
1050
+ coordinates.push(line);
1051
+ }
1052
+ validateGaiaEnd(cursor);
1053
+ return {
1054
+ type: "MultiLineString",
1055
+ coordinates,
1056
+ srid: header.srid,
1057
+ bbox: header.mbr,
1058
+ hasZ: false,
1059
+ geoType: header.geoType
1060
+ };
1061
+ }
1062
+ static readMultiLineStringZ(input) {
1063
+ const cursor = new BinaryCursor(input);
1064
+ const header = readGaiaHeader(cursor, GEO_TYPE_MULTILINESTRINGZ);
1065
+ const lineCount = cursor.readInt32(true);
1066
+ const coordinates = [];
1067
+ for (let lineIndex = 0; lineIndex < lineCount; lineIndex += 1) {
1068
+ const entityMark = cursor.readUint8();
1069
+ if (entityMark !== GAIA_ENTITY_MARK) {
1070
+ throw new GaiaFormatError(
1071
+ `Invalid LineString entity mark: expected 0x${GAIA_ENTITY_MARK.toString(16)}, got 0x${entityMark.toString(16)}.`
1072
+ );
1073
+ }
1074
+ const lineGeoType = cursor.readInt32(true);
1075
+ if (lineGeoType !== GEO_TYPE_LINESTRINGZ) {
1076
+ throw new GaiaFormatError(
1077
+ `Invalid LineStringZ geoType: expected ${GEO_TYPE_LINESTRINGZ}, got ${lineGeoType}.`
1078
+ );
1079
+ }
1080
+ const pointCount = cursor.readInt32(true);
1081
+ const line = [];
1082
+ for (let pointIndex = 0; pointIndex < pointCount; pointIndex += 1) {
1083
+ line.push([
1084
+ cursor.readFloat64(true),
1085
+ cursor.readFloat64(true),
1086
+ cursor.readFloat64(true)
1087
+ ]);
1088
+ }
1089
+ coordinates.push(line);
1090
+ }
1091
+ validateGaiaEnd(cursor);
1092
+ return {
1093
+ type: "MultiLineString",
1094
+ coordinates,
1095
+ srid: header.srid,
1096
+ bbox: header.mbr,
1097
+ hasZ: true,
1098
+ geoType: header.geoType
1099
+ };
1100
+ }
1101
+ static writeMultiLineString(geometry, srid) {
1102
+ if (is3DCoordinates(geometry.coordinates)) {
1103
+ throw new GaiaFormatError(
1104
+ "writeMultiLineString expects 2D coordinates."
1105
+ );
1106
+ }
1107
+ const writer = new BinaryWriter();
1108
+ writeGaiaHeader(
1109
+ writer,
1110
+ srid,
1111
+ createLineMbr(geometry.coordinates),
1112
+ GEO_TYPE_MULTILINESTRING
1113
+ );
1114
+ writer.writeInt32(geometry.coordinates.length, true);
1115
+ for (const line of geometry.coordinates) {
1116
+ writer.writeUint8(GAIA_ENTITY_MARK);
1117
+ writer.writeInt32(GEO_TYPE_LINESTRING, true);
1118
+ writer.writeInt32(line.length, true);
1119
+ for (const point of line) {
1120
+ writer.writeFloat64(point[0], true);
1121
+ writer.writeFloat64(point[1], true);
1122
+ }
1123
+ }
1124
+ writer.writeUint8(GAIA_END);
1125
+ return writer.toUint8Array();
1126
+ }
1127
+ static writeMultiLineStringZ(geometry, srid) {
1128
+ if (!is3DCoordinates(geometry.coordinates)) {
1129
+ throw new GaiaFormatError(
1130
+ "writeMultiLineStringZ expects 3D coordinates."
1131
+ );
1132
+ }
1133
+ const writer = new BinaryWriter();
1134
+ writeGaiaHeader(
1135
+ writer,
1136
+ srid,
1137
+ createLineMbr(geometry.coordinates),
1138
+ GEO_TYPE_MULTILINESTRINGZ
1139
+ );
1140
+ writer.writeInt32(geometry.coordinates.length, true);
1141
+ for (const line of geometry.coordinates) {
1142
+ writer.writeUint8(GAIA_ENTITY_MARK);
1143
+ writer.writeInt32(GEO_TYPE_LINESTRINGZ, true);
1144
+ writer.writeInt32(line.length, true);
1145
+ for (const point of line) {
1146
+ writer.writeFloat64(point[0], true);
1147
+ writer.writeFloat64(point[1], true);
1148
+ writer.writeFloat64(point[2], true);
1149
+ }
1150
+ }
1151
+ writer.writeUint8(GAIA_END);
1152
+ return writer.toUint8Array();
1153
+ }
1154
+ };
1155
+
1156
+ // src/core/schema/SmRegisterRepository.ts
1157
+ function mapRow2(row) {
1158
+ return {
1159
+ id: row.SmDatasetID,
1160
+ name: row.SmDatasetName,
1161
+ kind: datasetValueToKind(row.SmDatasetType),
1162
+ tableName: row.SmTableName,
1163
+ srid: row.SmSRID ?? null,
1164
+ objectCount: row.SmObjectCount,
1165
+ geometryType: row.geometryType ?? null
1166
+ };
1167
+ }
1168
+ var SmRegisterRepository = class {
1169
+ constructor(driver) {
1170
+ this.driver = driver;
1171
+ }
1172
+ async findAll() {
1173
+ const rows = await queryAll(
1174
+ this.driver,
1175
+ `SELECT
1176
+ SmDatasetID,
1177
+ SmDatasetName,
1178
+ SmTableName,
1179
+ SmDatasetType,
1180
+ SmObjectCount,
1181
+ SmSRID
1182
+ FROM SmRegister
1183
+ ORDER BY SmDatasetID`
1184
+ );
1185
+ return rows.map(mapRow2);
1186
+ }
1187
+ async findByName(name) {
1188
+ const row = await queryOne(
1189
+ this.driver,
1190
+ `SELECT
1191
+ SmDatasetID,
1192
+ SmDatasetName,
1193
+ SmTableName,
1194
+ SmDatasetType,
1195
+ SmObjectCount,
1196
+ SmSRID
1197
+ FROM SmRegister
1198
+ WHERE SmDatasetName = ?`,
1199
+ [name]
1200
+ );
1201
+ return row ? mapRow2(row) : null;
1202
+ }
1203
+ async nextDatasetId() {
1204
+ const row = await queryOne(
1205
+ this.driver,
1206
+ "SELECT COALESCE(MAX(SmDatasetID), 0) + 1 AS nextId FROM SmRegister"
1207
+ );
1208
+ return row?.nextId ?? 1;
1209
+ }
1210
+ async insert(params) {
1211
+ const datasetId = await this.nextDatasetId();
1212
+ const statement = await this.driver.prepare(
1213
+ `INSERT INTO SmRegister (
1214
+ SmDatasetID,
1215
+ SmDatasetName,
1216
+ SmTableName,
1217
+ SmDatasetType,
1218
+ SmObjectCount,
1219
+ SmSRID,
1220
+ SmIDColName,
1221
+ SmGeoColName,
1222
+ SmMaxGeometrySize,
1223
+ SmCreateTime,
1224
+ SmLastUpdateTime
1225
+ ) VALUES (?, ?, ?, ?, 0, ?, ?, ?, 0, datetime('now'), datetime('now'))`
1226
+ );
1227
+ await executeStatement(statement, [
1228
+ datasetId,
1229
+ params.name,
1230
+ params.name,
1231
+ datasetKindToValue(params.kind),
1232
+ params.srid,
1233
+ params.idColumnName,
1234
+ params.geometryColumnName
1235
+ ]);
1236
+ return datasetId;
1237
+ }
1238
+ async incrementObjectCount(datasetId, geometrySize) {
1239
+ if (geometrySize === void 0) {
1240
+ const statement2 = await this.driver.prepare(
1241
+ "UPDATE SmRegister SET SmObjectCount = SmObjectCount + 1 WHERE SmDatasetID = ?"
1242
+ );
1243
+ await executeStatement(statement2, [datasetId]);
1244
+ return;
1245
+ }
1246
+ const statement = await this.driver.prepare(
1247
+ `UPDATE SmRegister
1248
+ SET SmObjectCount = SmObjectCount + 1,
1249
+ SmMaxGeometrySize = CASE
1250
+ WHEN SmMaxGeometrySize < ? THEN ?
1251
+ ELSE SmMaxGeometrySize
1252
+ END
1253
+ WHERE SmDatasetID = ?`
1254
+ );
1255
+ await executeStatement(statement, [geometrySize, geometrySize, datasetId]);
1256
+ }
1257
+ async incrementObjectCountBatch(datasetId, count, maxGeometrySize) {
1258
+ const statement = await this.driver.prepare(
1259
+ `UPDATE SmRegister
1260
+ SET SmObjectCount = SmObjectCount + ?,
1261
+ SmMaxGeometrySize = CASE
1262
+ WHEN SmMaxGeometrySize < ? THEN ?
1263
+ ELSE SmMaxGeometrySize
1264
+ END
1265
+ WHERE SmDatasetID = ?`
1266
+ );
1267
+ await executeStatement(statement, [
1268
+ count,
1269
+ maxGeometrySize,
1270
+ maxGeometrySize,
1271
+ datasetId
1272
+ ]);
1273
+ }
1274
+ async decrementObjectCount(datasetId) {
1275
+ const statement = await this.driver.prepare(
1276
+ "UPDATE SmRegister SET SmObjectCount = SmObjectCount - 1 WHERE SmDatasetID = ? AND SmObjectCount > 0"
1277
+ );
1278
+ await executeStatement(statement, [datasetId]);
1279
+ }
1280
+ };
1281
+
1282
+ // src/core/dataset/vectorDatasetShared.ts
1283
+ function normalizeGeometryBlob(value) {
1284
+ return value instanceof Uint8Array ? value : new Uint8Array(value);
1285
+ }
1286
+ function buildListSql(tableName, options) {
1287
+ const params = [];
1288
+ const clauses = [];
1289
+ if (options?.ids?.length) {
1290
+ const placeholders = options.ids.map(() => "?").join(", ");
1291
+ clauses.push(`SmID IN (${placeholders})`);
1292
+ params.push(...options.ids);
1293
+ }
1294
+ const whereClause = clauses.length > 0 ? ` WHERE ${clauses.join(" AND ")}` : "";
1295
+ let sql = `SELECT * FROM "${tableName}"${whereClause} ORDER BY SmID`;
1296
+ if (options?.limit !== void 0) {
1297
+ sql += " LIMIT ?";
1298
+ params.push(options.limit);
1299
+ }
1300
+ if (options?.offset !== void 0) {
1301
+ if (options.limit === void 0) {
1302
+ sql += " LIMIT -1";
1303
+ }
1304
+ sql += " OFFSET ?";
1305
+ params.push(options.offset);
1306
+ }
1307
+ return { sql, params };
1308
+ }
1309
+ function sqliteColumnType(field) {
1310
+ switch (field.fieldType) {
1311
+ case "boolean":
1312
+ case "byte":
1313
+ case "int16":
1314
+ case "int32":
1315
+ case "int64":
1316
+ return "INTEGER";
1317
+ case "single":
1318
+ case "double":
1319
+ return "REAL";
1320
+ case "binary":
1321
+ case "geometry":
1322
+ return "BLOB";
1323
+ case "date":
1324
+ case "char":
1325
+ case "ntext":
1326
+ case "text":
1327
+ case "time":
1328
+ default:
1329
+ return "TEXT";
1330
+ }
1331
+ }
1332
+
1333
+ // src/core/dataset/LineDataset.ts
1334
+ function mapLineRow(row) {
1335
+ const { SmID, SmGeometry, ...attributes } = row;
1336
+ return {
1337
+ id: SmID,
1338
+ geometry: GaiaLineCodec.readMultiLineString(normalizeGeometryBlob(SmGeometry)),
1339
+ attributes
1340
+ };
1341
+ }
1342
+ var LineDataset = class _LineDataset extends BaseDataset {
1343
+ constructor(driver, info) {
1344
+ super(driver, info);
1345
+ __publicField(this, "registerRepository");
1346
+ this.registerRepository = new SmRegisterRepository(driver);
1347
+ }
1348
+ async getById(id) {
1349
+ const row = await queryOne(
1350
+ this.driver,
1351
+ `SELECT * FROM "${this.info.tableName}" WHERE SmID = ?`,
1352
+ [id]
1353
+ );
1354
+ return row ? mapLineRow(row) : null;
1355
+ }
1356
+ async list(options) {
1357
+ const { sql, params } = buildListSql(this.info.tableName, options);
1358
+ const rows = await queryAll(this.driver, sql, params);
1359
+ return rows.map((row) => mapLineRow(row));
1360
+ }
1361
+ async *iterate(options) {
1362
+ const { sql, params } = buildListSql(this.info.tableName, options);
1363
+ const statement = await this.driver.prepare(sql);
1364
+ try {
1365
+ if (params.length > 0) {
1366
+ await statement.bind(params);
1367
+ }
1368
+ while (await statement.step()) {
1369
+ yield mapLineRow(await statement.getRow());
1370
+ }
1371
+ } finally {
1372
+ await statement.finalize();
1373
+ }
1374
+ }
1375
+ async insert(feature) {
1376
+ const userFields = await this.getFields();
1377
+ const columnNames = ["SmID", "SmUserID", "SmGeometry", ...userFields.map((field) => field.name)];
1378
+ const placeholders = columnNames.map(() => "?").join(", ");
1379
+ const geometry = GaiaLineCodec.writeMultiLineString(
1380
+ feature.geometry,
1381
+ feature.geometry.srid ?? this.info.srid ?? 0
1382
+ );
1383
+ const sql = `INSERT INTO "${this.info.tableName}" (${columnNames.map((column) => `"${column}"`).join(", ")}) VALUES (${placeholders})`;
1384
+ const params = [
1385
+ feature.id,
1386
+ 0,
1387
+ geometry,
1388
+ ...userFields.map((field) => feature.attributes[field.name] ?? null)
1389
+ ];
1390
+ await this.driver.transaction(async () => {
1391
+ await executeSql(this.driver, sql, params);
1392
+ await this.registerRepository.incrementObjectCount(this.info.id, geometry.byteLength);
1393
+ });
1394
+ }
1395
+ async insertMany(features) {
1396
+ const userFields = await this.getFields();
1397
+ const columnNames = ["SmID", "SmUserID", "SmGeometry", ...userFields.map((field) => field.name)];
1398
+ const placeholders = columnNames.map(() => "?").join(", ");
1399
+ const sql = `INSERT INTO "${this.info.tableName}" (${columnNames.map((column) => `"${column}"`).join(", ")}) VALUES (${placeholders})`;
1400
+ await this.driver.transaction(async () => {
1401
+ const statement = await this.driver.prepare(sql);
1402
+ try {
1403
+ let count = 0;
1404
+ let maxGeometrySize = 0;
1405
+ for await (const feature of features) {
1406
+ const geometry = GaiaLineCodec.writeMultiLineString(
1407
+ feature.geometry,
1408
+ feature.geometry.srid ?? this.info.srid ?? 0
1409
+ );
1410
+ maxGeometrySize = Math.max(maxGeometrySize, geometry.byteLength);
1411
+ const params = [
1412
+ feature.id,
1413
+ 0,
1414
+ geometry,
1415
+ ...userFields.map(
1416
+ (field) => feature.attributes[field.name] ?? null
1417
+ )
1418
+ ];
1419
+ await statement.bind(params);
1420
+ await statement.step();
1421
+ await statement.reset();
1422
+ count++;
1423
+ }
1424
+ if (count > 0) {
1425
+ await this.registerRepository.incrementObjectCountBatch(
1426
+ this.info.id,
1427
+ count,
1428
+ maxGeometrySize
1429
+ );
1430
+ }
1431
+ } finally {
1432
+ await statement.finalize();
1433
+ }
1434
+ });
1435
+ }
1436
+ async count() {
1437
+ const row = await queryOne(
1438
+ this.driver,
1439
+ `SELECT COUNT(*) AS count FROM "${this.info.tableName}"`,
1440
+ []
1441
+ );
1442
+ return row?.count ?? 0;
1443
+ }
1444
+ async update(id, changes) {
1445
+ if (!changes.geometry && !changes.attributes) {
1446
+ return;
1447
+ }
1448
+ const setClauses = [];
1449
+ const params = [];
1450
+ if (changes.geometry) {
1451
+ const geometry = GaiaLineCodec.writeMultiLineString(
1452
+ changes.geometry,
1453
+ changes.geometry.srid ?? this.info.srid ?? 0
1454
+ );
1455
+ setClauses.push('"SmGeometry" = ?');
1456
+ params.push(geometry);
1457
+ }
1458
+ if (changes.attributes) {
1459
+ const userFields = await this.getFields();
1460
+ const fieldNames = new Set(userFields.map((f) => f.name));
1461
+ const validEntries = Object.entries(changes.attributes).filter(
1462
+ ([key]) => fieldNames.has(key)
1463
+ );
1464
+ for (const [key, value] of validEntries) {
1465
+ setClauses.push(`"${key}" = ?`);
1466
+ params.push(value);
1467
+ }
1468
+ }
1469
+ if (setClauses.length === 0) {
1470
+ return;
1471
+ }
1472
+ const sql = `UPDATE "${this.info.tableName}" SET ${setClauses.join(", ")} WHERE SmID = ?`;
1473
+ params.push(id);
1474
+ await this.driver.transaction(async () => {
1475
+ await executeSql(this.driver, sql, params);
1476
+ });
1477
+ }
1478
+ async delete(id) {
1479
+ await this.driver.transaction(async () => {
1480
+ await executeSql(
1481
+ this.driver,
1482
+ `DELETE FROM "${this.info.tableName}" WHERE SmID = ?`,
1483
+ [id]
1484
+ );
1485
+ await this.registerRepository.decrementObjectCount(this.info.id);
1486
+ });
1487
+ }
1488
+ static async create(driver, registerRepository, params) {
1489
+ const fields = params.fields ?? [];
1490
+ const datasetId = await registerRepository.insert({
1491
+ name: params.name,
1492
+ kind: "line",
1493
+ srid: params.srid,
1494
+ idColumnName: "SmID",
1495
+ geometryColumnName: "SmGeometry"
1496
+ });
1497
+ const userColumnDefinitions = fields.map((field) => {
1498
+ const nullability = field.nullable ? "" : " NOT NULL";
1499
+ return `"${field.name}" ${sqliteColumnType(field)}${nullability}`;
1500
+ });
1501
+ const createTableParts = [
1502
+ `"SmID" INTEGER NOT NULL PRIMARY KEY`,
1503
+ `"SmUserID" INTEGER DEFAULT 0 NOT NULL`,
1504
+ `"SmGeometry" BLOB NOT NULL`,
1505
+ ...userColumnDefinitions
1506
+ ];
1507
+ await driver.exec(
1508
+ `CREATE TABLE "${params.name}" (${createTableParts.join(", ")})`
1509
+ );
1510
+ await executeSql(
1511
+ driver,
1512
+ `INSERT INTO geometry_columns (
1513
+ f_table_name,
1514
+ f_geometry_column,
1515
+ geometry_type,
1516
+ coord_dimension,
1517
+ srid,
1518
+ spatial_index_enabled
1519
+ ) VALUES (?, ?, ?, ?, ?, ?)`,
1520
+ [params.name, "SmGeometry", 5, 2, params.srid, 0]
1521
+ );
1522
+ if (fields.length > 0) {
1523
+ const fieldInfoRepository = new SmFieldInfoRepository(driver);
1524
+ await fieldInfoRepository.insertAll(datasetId, fields);
1525
+ }
1526
+ return new _LineDataset(driver, {
1527
+ id: datasetId,
1528
+ name: params.name,
1529
+ kind: "line",
1530
+ tableName: params.name,
1531
+ srid: params.srid,
1532
+ objectCount: 0,
1533
+ geometryType: 5
1534
+ });
1535
+ }
1536
+ };
1537
+
1538
+ // src/core/dataset/LineZDataset.ts
1539
+ function mapLineZRow(row) {
1540
+ const { SmID, SmGeometry, ...attributes } = row;
1541
+ return {
1542
+ id: SmID,
1543
+ geometry: GaiaLineCodec.readMultiLineStringZ(normalizeGeometryBlob(SmGeometry)),
1544
+ attributes
1545
+ };
1546
+ }
1547
+ var LineZDataset = class _LineZDataset extends BaseDataset {
1548
+ constructor(driver, info) {
1549
+ super(driver, info);
1550
+ __publicField(this, "registerRepository");
1551
+ this.registerRepository = new SmRegisterRepository(driver);
1552
+ }
1553
+ async getById(id) {
1554
+ const row = await queryOne(
1555
+ this.driver,
1556
+ `SELECT * FROM "${this.info.tableName}" WHERE SmID = ?`,
1557
+ [id]
1558
+ );
1559
+ return row ? mapLineZRow(row) : null;
1560
+ }
1561
+ async list(options) {
1562
+ const { sql, params } = buildListSql(this.info.tableName, options);
1563
+ const rows = await queryAll(this.driver, sql, params);
1564
+ return rows.map((row) => mapLineZRow(row));
1565
+ }
1566
+ async *iterate(options) {
1567
+ const { sql, params } = buildListSql(this.info.tableName, options);
1568
+ const statement = await this.driver.prepare(sql);
1569
+ try {
1570
+ if (params.length > 0) {
1571
+ await statement.bind(params);
1572
+ }
1573
+ while (await statement.step()) {
1574
+ yield mapLineZRow(await statement.getRow());
1575
+ }
1576
+ } finally {
1577
+ await statement.finalize();
1578
+ }
1579
+ }
1580
+ async count() {
1581
+ const row = await queryOne(
1582
+ this.driver,
1583
+ `SELECT COUNT(*) AS count FROM "${this.info.tableName}"`,
1584
+ []
1585
+ );
1586
+ return row?.count ?? 0;
1587
+ }
1588
+ async insert(feature) {
1589
+ const userFields = await this.getFields();
1590
+ const columnNames = ["SmID", "SmUserID", "SmGeometry", ...userFields.map((field) => field.name)];
1591
+ const placeholders = columnNames.map(() => "?").join(", ");
1592
+ const geometry = GaiaLineCodec.writeMultiLineStringZ(
1593
+ feature.geometry,
1594
+ feature.geometry.srid ?? this.info.srid ?? 0
1595
+ );
1596
+ const sql = `INSERT INTO "${this.info.tableName}" (${columnNames.map((column) => `"${column}"`).join(", ")}) VALUES (${placeholders})`;
1597
+ const params = [
1598
+ feature.id,
1599
+ 0,
1600
+ geometry,
1601
+ ...userFields.map((field) => feature.attributes[field.name] ?? null)
1602
+ ];
1603
+ await this.driver.transaction(async () => {
1604
+ await executeSql(this.driver, sql, params);
1605
+ await this.registerRepository.incrementObjectCount(this.info.id, geometry.byteLength);
1606
+ });
1607
+ }
1608
+ async insertMany(features) {
1609
+ const userFields = await this.getFields();
1610
+ const columnNames = ["SmID", "SmUserID", "SmGeometry", ...userFields.map((field) => field.name)];
1611
+ const placeholders = columnNames.map(() => "?").join(", ");
1612
+ const sql = `INSERT INTO "${this.info.tableName}" (${columnNames.map((column) => `"${column}"`).join(", ")}) VALUES (${placeholders})`;
1613
+ await this.driver.transaction(async () => {
1614
+ const statement = await this.driver.prepare(sql);
1615
+ try {
1616
+ let count = 0;
1617
+ let maxGeometrySize = 0;
1618
+ for await (const feature of features) {
1619
+ const geometry = GaiaLineCodec.writeMultiLineStringZ(
1620
+ feature.geometry,
1621
+ feature.geometry.srid ?? this.info.srid ?? 0
1622
+ );
1623
+ maxGeometrySize = Math.max(maxGeometrySize, geometry.byteLength);
1624
+ const params = [
1625
+ feature.id,
1626
+ 0,
1627
+ geometry,
1628
+ ...userFields.map(
1629
+ (field) => feature.attributes[field.name] ?? null
1630
+ )
1631
+ ];
1632
+ await statement.bind(params);
1633
+ await statement.step();
1634
+ await statement.reset();
1635
+ count++;
1636
+ }
1637
+ if (count > 0) {
1638
+ await this.registerRepository.incrementObjectCountBatch(
1639
+ this.info.id,
1640
+ count,
1641
+ maxGeometrySize
1642
+ );
1643
+ }
1644
+ } finally {
1645
+ await statement.finalize();
1646
+ }
1647
+ });
1648
+ }
1649
+ async update(id, changes) {
1650
+ if (!changes.geometry && !changes.attributes) {
1651
+ return;
1652
+ }
1653
+ const setClauses = [];
1654
+ const params = [];
1655
+ if (changes.geometry) {
1656
+ const geometry = GaiaLineCodec.writeMultiLineStringZ(
1657
+ changes.geometry,
1658
+ changes.geometry.srid ?? this.info.srid ?? 0
1659
+ );
1660
+ setClauses.push('"SmGeometry" = ?');
1661
+ params.push(geometry);
1662
+ }
1663
+ if (changes.attributes) {
1664
+ const userFields = await this.getFields();
1665
+ const fieldNames = new Set(userFields.map((f) => f.name));
1666
+ const validEntries = Object.entries(changes.attributes).filter(
1667
+ ([key]) => fieldNames.has(key)
1668
+ );
1669
+ for (const [key, value] of validEntries) {
1670
+ setClauses.push(`"${key}" = ?`);
1671
+ params.push(value);
1672
+ }
1673
+ }
1674
+ if (setClauses.length === 0) {
1675
+ return;
1676
+ }
1677
+ const sql = `UPDATE "${this.info.tableName}" SET ${setClauses.join(", ")} WHERE SmID = ?`;
1678
+ params.push(id);
1679
+ await this.driver.transaction(async () => {
1680
+ await executeSql(this.driver, sql, params);
1681
+ });
1682
+ }
1683
+ async delete(id) {
1684
+ await this.driver.transaction(async () => {
1685
+ await executeSql(
1686
+ this.driver,
1687
+ `DELETE FROM "${this.info.tableName}" WHERE SmID = ?`,
1688
+ [id]
1689
+ );
1690
+ await this.registerRepository.decrementObjectCount(this.info.id);
1691
+ });
1692
+ }
1693
+ static async create(driver, registerRepository, params) {
1694
+ const fields = params.fields ?? [];
1695
+ const datasetId = await registerRepository.insert({
1696
+ name: params.name,
1697
+ kind: "lineZ",
1698
+ srid: params.srid,
1699
+ idColumnName: "SmID",
1700
+ geometryColumnName: "SmGeometry"
1701
+ });
1702
+ const userColumnDefinitions = fields.map((field) => {
1703
+ const nullability = field.nullable ? "" : " NOT NULL";
1704
+ return `"${field.name}" ${sqliteColumnType(field)}${nullability}`;
1705
+ });
1706
+ const createTableParts = [
1707
+ `"SmID" INTEGER NOT NULL PRIMARY KEY`,
1708
+ `"SmUserID" INTEGER DEFAULT 0 NOT NULL`,
1709
+ `"SmGeometry" BLOB NOT NULL`,
1710
+ ...userColumnDefinitions
1711
+ ];
1712
+ await driver.exec(
1713
+ `CREATE TABLE "${params.name}" (${createTableParts.join(", ")})`
1714
+ );
1715
+ await executeSql(
1716
+ driver,
1717
+ `INSERT INTO geometry_columns (
1718
+ f_table_name,
1719
+ f_geometry_column,
1720
+ geometry_type,
1721
+ coord_dimension,
1722
+ srid,
1723
+ spatial_index_enabled
1724
+ ) VALUES (?, ?, ?, ?, ?, ?)`,
1725
+ [params.name, "SmGeometry", 1005, 3, params.srid, 0]
1726
+ );
1727
+ if (fields.length > 0) {
1728
+ const fieldInfoRepository = new SmFieldInfoRepository(driver);
1729
+ await fieldInfoRepository.insertAll(datasetId, fields);
1730
+ }
1731
+ return new _LineZDataset(driver, {
1732
+ id: datasetId,
1733
+ name: params.name,
1734
+ kind: "lineZ",
1735
+ tableName: params.name,
1736
+ srid: params.srid,
1737
+ objectCount: 0,
1738
+ geometryType: 1005
1739
+ });
1740
+ }
1741
+ };
1742
+
1743
+ // src/core/dataset/PointDataset.ts
1744
+ function mapPointRow(row) {
1745
+ const { SmID, SmGeometry, ...attributes } = row;
1746
+ return {
1747
+ id: SmID,
1748
+ geometry: GaiaPointCodec.readPoint(normalizeGeometryBlob(SmGeometry)),
1749
+ attributes
1750
+ };
1751
+ }
1752
+ var PointDataset = class _PointDataset extends BaseDataset {
1753
+ constructor(driver, info) {
1754
+ super(driver, info);
1755
+ __publicField(this, "registerRepository");
1756
+ this.registerRepository = new SmRegisterRepository(driver);
1757
+ }
1758
+ async getById(id) {
1759
+ const row = await queryOne(
1760
+ this.driver,
1761
+ `SELECT * FROM "${this.info.tableName}" WHERE SmID = ?`,
1762
+ [id]
1763
+ );
1764
+ return row ? mapPointRow(row) : null;
1765
+ }
1766
+ async list(options) {
1767
+ const { sql, params } = buildListSql(this.info.tableName, options);
1768
+ const rows = await queryAll(this.driver, sql, params);
1769
+ return rows.map((row) => mapPointRow(row));
1770
+ }
1771
+ async *iterate(options) {
1772
+ const { sql, params } = buildListSql(this.info.tableName, options);
1773
+ const statement = await this.driver.prepare(sql);
1774
+ try {
1775
+ if (params.length > 0) {
1776
+ await statement.bind(params);
1777
+ }
1778
+ while (await statement.step()) {
1779
+ yield mapPointRow(await statement.getRow());
1780
+ }
1781
+ } finally {
1782
+ await statement.finalize();
1783
+ }
1784
+ }
1785
+ async insert(feature) {
1786
+ const userFields = await this.getFields();
1787
+ const columnNames = ["SmID", "SmUserID", "SmGeometry", ...userFields.map((field) => field.name)];
1788
+ const placeholders = columnNames.map(() => "?").join(", ");
1789
+ const geometry = GaiaPointCodec.writePoint(feature.geometry, feature.geometry.srid ?? this.info.srid ?? 0);
1790
+ const sql = `INSERT INTO "${this.info.tableName}" (${columnNames.map((column) => `"${column}"`).join(", ")}) VALUES (${placeholders})`;
1791
+ const params = [
1792
+ feature.id,
1793
+ 0,
1794
+ geometry,
1795
+ ...userFields.map((field) => feature.attributes[field.name] ?? null)
1796
+ ];
1797
+ await this.driver.transaction(async () => {
1798
+ await executeSql(this.driver, sql, params);
1799
+ await this.registerRepository.incrementObjectCount(this.info.id, geometry.byteLength);
1800
+ });
1801
+ }
1802
+ async insertMany(features) {
1803
+ const userFields = await this.getFields();
1804
+ const columnNames = ["SmID", "SmUserID", "SmGeometry", ...userFields.map((field) => field.name)];
1805
+ const placeholders = columnNames.map(() => "?").join(", ");
1806
+ const sql = `INSERT INTO "${this.info.tableName}" (${columnNames.map((column) => `"${column}"`).join(", ")}) VALUES (${placeholders})`;
1807
+ await this.driver.transaction(async () => {
1808
+ const statement = await this.driver.prepare(sql);
1809
+ try {
1810
+ let count = 0;
1811
+ let maxGeometrySize = 0;
1812
+ for await (const feature of features) {
1813
+ const geometry = GaiaPointCodec.writePoint(
1814
+ feature.geometry,
1815
+ feature.geometry.srid ?? this.info.srid ?? 0
1816
+ );
1817
+ maxGeometrySize = Math.max(maxGeometrySize, geometry.byteLength);
1818
+ const params = [
1819
+ feature.id,
1820
+ 0,
1821
+ geometry,
1822
+ ...userFields.map(
1823
+ (field) => feature.attributes[field.name] ?? null
1824
+ )
1825
+ ];
1826
+ await statement.bind(params);
1827
+ await statement.step();
1828
+ await statement.reset();
1829
+ count++;
1830
+ }
1831
+ if (count > 0) {
1832
+ await this.registerRepository.incrementObjectCountBatch(
1833
+ this.info.id,
1834
+ count,
1835
+ maxGeometrySize
1836
+ );
1837
+ }
1838
+ } finally {
1839
+ await statement.finalize();
1840
+ }
1841
+ });
1842
+ }
1843
+ async count() {
1844
+ const row = await queryOne(
1845
+ this.driver,
1846
+ `SELECT COUNT(*) AS count FROM "${this.info.tableName}"`,
1847
+ []
1848
+ );
1849
+ return row?.count ?? 0;
1850
+ }
1851
+ async update(id, changes) {
1852
+ if (!changes.geometry && !changes.attributes) {
1853
+ return;
1854
+ }
1855
+ const setClauses = [];
1856
+ const params = [];
1857
+ if (changes.geometry) {
1858
+ const geometry = GaiaPointCodec.writePoint(
1859
+ changes.geometry,
1860
+ changes.geometry.srid ?? this.info.srid ?? 0
1861
+ );
1862
+ setClauses.push('"SmGeometry" = ?');
1863
+ params.push(geometry);
1864
+ }
1865
+ if (changes.attributes) {
1866
+ const userFields = await this.getFields();
1867
+ const fieldNames = new Set(userFields.map((f) => f.name));
1868
+ const validEntries = Object.entries(changes.attributes).filter(
1869
+ ([key]) => fieldNames.has(key)
1870
+ );
1871
+ for (const [key, value] of validEntries) {
1872
+ setClauses.push(`"${key}" = ?`);
1873
+ params.push(value);
1874
+ }
1875
+ }
1876
+ if (setClauses.length === 0) {
1877
+ return;
1878
+ }
1879
+ const sql = `UPDATE "${this.info.tableName}" SET ${setClauses.join(", ")} WHERE SmID = ?`;
1880
+ params.push(id);
1881
+ await this.driver.transaction(async () => {
1882
+ await executeSql(this.driver, sql, params);
1883
+ });
1884
+ }
1885
+ async delete(id) {
1886
+ await this.driver.transaction(async () => {
1887
+ await executeSql(
1888
+ this.driver,
1889
+ `DELETE FROM "${this.info.tableName}" WHERE SmID = ?`,
1890
+ [id]
1891
+ );
1892
+ await this.registerRepository.decrementObjectCount(this.info.id);
1893
+ });
1894
+ }
1895
+ static async create(driver, registerRepository, params) {
1896
+ const fields = params.fields ?? [];
1897
+ const datasetId = await registerRepository.insert({
1898
+ name: params.name,
1899
+ kind: "point",
1900
+ srid: params.srid,
1901
+ idColumnName: "SmID",
1902
+ geometryColumnName: "SmGeometry"
1903
+ });
1904
+ const userColumnDefinitions = fields.map((field) => {
1905
+ const nullability = field.nullable ? "" : " NOT NULL";
1906
+ return `"${field.name}" ${sqliteColumnType(field)}${nullability}`;
1907
+ });
1908
+ const createTableParts = [
1909
+ `"SmID" INTEGER NOT NULL PRIMARY KEY`,
1910
+ `"SmUserID" INTEGER DEFAULT 0 NOT NULL`,
1911
+ `"SmGeometry" BLOB NOT NULL`,
1912
+ ...userColumnDefinitions
1913
+ ];
1914
+ await driver.exec(
1915
+ `CREATE TABLE "${params.name}" (${createTableParts.join(", ")})`
1916
+ );
1917
+ await executeSql(
1918
+ driver,
1919
+ `INSERT INTO geometry_columns (
1920
+ f_table_name,
1921
+ f_geometry_column,
1922
+ geometry_type,
1923
+ coord_dimension,
1924
+ srid,
1925
+ spatial_index_enabled
1926
+ ) VALUES (?, ?, ?, ?, ?, ?)`,
1927
+ [params.name, "SmGeometry", 1, 2, params.srid, 0]
1928
+ );
1929
+ if (fields.length > 0) {
1930
+ const fieldInfoRepository = new SmFieldInfoRepository(driver);
1931
+ await fieldInfoRepository.insertAll(datasetId, fields);
1932
+ }
1933
+ return new _PointDataset(driver, {
1934
+ id: datasetId,
1935
+ name: params.name,
1936
+ kind: "point",
1937
+ tableName: params.name,
1938
+ srid: params.srid,
1939
+ objectCount: 0,
1940
+ geometryType: 1
1941
+ });
1942
+ }
1943
+ };
1944
+
1945
+ // src/core/dataset/PointZDataset.ts
1946
+ function mapPointZRow(row) {
1947
+ const { SmID, SmGeometry, ...attributes } = row;
1948
+ return {
1949
+ id: SmID,
1950
+ geometry: GaiaPointCodec.readPointZ(normalizeGeometryBlob(SmGeometry)),
1951
+ attributes
1952
+ };
1953
+ }
1954
+ var PointZDataset = class _PointZDataset extends BaseDataset {
1955
+ constructor(driver, info) {
1956
+ super(driver, info);
1957
+ __publicField(this, "registerRepository");
1958
+ this.registerRepository = new SmRegisterRepository(driver);
1959
+ }
1960
+ async getById(id) {
1961
+ const row = await queryOne(
1962
+ this.driver,
1963
+ `SELECT * FROM "${this.info.tableName}" WHERE SmID = ?`,
1964
+ [id]
1965
+ );
1966
+ return row ? mapPointZRow(row) : null;
1967
+ }
1968
+ async list(options) {
1969
+ const { sql, params } = buildListSql(this.info.tableName, options);
1970
+ const rows = await queryAll(this.driver, sql, params);
1971
+ return rows.map((row) => mapPointZRow(row));
1972
+ }
1973
+ async *iterate(options) {
1974
+ const { sql, params } = buildListSql(this.info.tableName, options);
1975
+ const statement = await this.driver.prepare(sql);
1976
+ try {
1977
+ if (params.length > 0) {
1978
+ await statement.bind(params);
1979
+ }
1980
+ while (await statement.step()) {
1981
+ yield mapPointZRow(await statement.getRow());
1982
+ }
1983
+ } finally {
1984
+ await statement.finalize();
1985
+ }
1986
+ }
1987
+ async count() {
1988
+ const row = await queryOne(
1989
+ this.driver,
1990
+ `SELECT COUNT(*) AS count FROM "${this.info.tableName}"`,
1991
+ []
1992
+ );
1993
+ return row?.count ?? 0;
1994
+ }
1995
+ async insert(feature) {
1996
+ const userFields = await this.getFields();
1997
+ const columnNames = ["SmID", "SmUserID", "SmGeometry", ...userFields.map((field) => field.name)];
1998
+ const placeholders = columnNames.map(() => "?").join(", ");
1999
+ const geometry = GaiaPointCodec.writePointZ(
2000
+ feature.geometry,
2001
+ feature.geometry.srid ?? this.info.srid ?? 0
2002
+ );
2003
+ const sql = `INSERT INTO "${this.info.tableName}" (${columnNames.map((column) => `"${column}"`).join(", ")}) VALUES (${placeholders})`;
2004
+ const params = [
2005
+ feature.id,
2006
+ 0,
2007
+ geometry,
2008
+ ...userFields.map((field) => feature.attributes[field.name] ?? null)
2009
+ ];
2010
+ await this.driver.transaction(async () => {
2011
+ await executeSql(this.driver, sql, params);
2012
+ await this.registerRepository.incrementObjectCount(this.info.id, geometry.byteLength);
2013
+ });
2014
+ }
2015
+ async insertMany(features) {
2016
+ const userFields = await this.getFields();
2017
+ const columnNames = ["SmID", "SmUserID", "SmGeometry", ...userFields.map((field) => field.name)];
2018
+ const placeholders = columnNames.map(() => "?").join(", ");
2019
+ const sql = `INSERT INTO "${this.info.tableName}" (${columnNames.map((column) => `"${column}"`).join(", ")}) VALUES (${placeholders})`;
2020
+ await this.driver.transaction(async () => {
2021
+ const statement = await this.driver.prepare(sql);
2022
+ try {
2023
+ let count = 0;
2024
+ let maxGeometrySize = 0;
2025
+ for await (const feature of features) {
2026
+ const geometry = GaiaPointCodec.writePointZ(
2027
+ feature.geometry,
2028
+ feature.geometry.srid ?? this.info.srid ?? 0
2029
+ );
2030
+ maxGeometrySize = Math.max(maxGeometrySize, geometry.byteLength);
2031
+ const params = [
2032
+ feature.id,
2033
+ 0,
2034
+ geometry,
2035
+ ...userFields.map(
2036
+ (field) => feature.attributes[field.name] ?? null
2037
+ )
2038
+ ];
2039
+ await statement.bind(params);
2040
+ await statement.step();
2041
+ await statement.reset();
2042
+ count++;
2043
+ }
2044
+ if (count > 0) {
2045
+ await this.registerRepository.incrementObjectCountBatch(
2046
+ this.info.id,
2047
+ count,
2048
+ maxGeometrySize
2049
+ );
2050
+ }
2051
+ } finally {
2052
+ await statement.finalize();
2053
+ }
2054
+ });
2055
+ }
2056
+ async update(id, changes) {
2057
+ if (!changes.geometry && !changes.attributes) {
2058
+ return;
2059
+ }
2060
+ const setClauses = [];
2061
+ const params = [];
2062
+ if (changes.geometry) {
2063
+ const geometry = GaiaPointCodec.writePointZ(
2064
+ changes.geometry,
2065
+ changes.geometry.srid ?? this.info.srid ?? 0
2066
+ );
2067
+ setClauses.push('"SmGeometry" = ?');
2068
+ params.push(geometry);
2069
+ }
2070
+ if (changes.attributes) {
2071
+ const userFields = await this.getFields();
2072
+ const fieldNames = new Set(userFields.map((f) => f.name));
2073
+ const validEntries = Object.entries(changes.attributes).filter(
2074
+ ([key]) => fieldNames.has(key)
2075
+ );
2076
+ for (const [key, value] of validEntries) {
2077
+ setClauses.push(`"${key}" = ?`);
2078
+ params.push(value);
2079
+ }
2080
+ }
2081
+ if (setClauses.length === 0) {
2082
+ return;
2083
+ }
2084
+ const sql = `UPDATE "${this.info.tableName}" SET ${setClauses.join(", ")} WHERE SmID = ?`;
2085
+ params.push(id);
2086
+ await this.driver.transaction(async () => {
2087
+ await executeSql(this.driver, sql, params);
2088
+ });
2089
+ }
2090
+ async delete(id) {
2091
+ await this.driver.transaction(async () => {
2092
+ await executeSql(
2093
+ this.driver,
2094
+ `DELETE FROM "${this.info.tableName}" WHERE SmID = ?`,
2095
+ [id]
2096
+ );
2097
+ await this.registerRepository.decrementObjectCount(this.info.id);
2098
+ });
2099
+ }
2100
+ static async create(driver, registerRepository, params) {
2101
+ const fields = params.fields ?? [];
2102
+ const datasetId = await registerRepository.insert({
2103
+ name: params.name,
2104
+ kind: "pointZ",
2105
+ srid: params.srid,
2106
+ idColumnName: "SmID",
2107
+ geometryColumnName: "SmGeometry"
2108
+ });
2109
+ const userColumnDefinitions = fields.map((field) => {
2110
+ const nullability = field.nullable ? "" : " NOT NULL";
2111
+ return `"${field.name}" ${sqliteColumnType(field)}${nullability}`;
2112
+ });
2113
+ const createTableParts = [
2114
+ `"SmID" INTEGER NOT NULL PRIMARY KEY`,
2115
+ `"SmUserID" INTEGER DEFAULT 0 NOT NULL`,
2116
+ `"SmGeometry" BLOB NOT NULL`,
2117
+ ...userColumnDefinitions
2118
+ ];
2119
+ await driver.exec(
2120
+ `CREATE TABLE "${params.name}" (${createTableParts.join(", ")})`
2121
+ );
2122
+ await executeSql(
2123
+ driver,
2124
+ `INSERT INTO geometry_columns (
2125
+ f_table_name,
2126
+ f_geometry_column,
2127
+ geometry_type,
2128
+ coord_dimension,
2129
+ srid,
2130
+ spatial_index_enabled
2131
+ ) VALUES (?, ?, ?, ?, ?, ?)`,
2132
+ [params.name, "SmGeometry", 1001, 3, params.srid, 0]
2133
+ );
2134
+ if (fields.length > 0) {
2135
+ const fieldInfoRepository = new SmFieldInfoRepository(driver);
2136
+ await fieldInfoRepository.insertAll(datasetId, fields);
2137
+ }
2138
+ return new _PointZDataset(driver, {
2139
+ id: datasetId,
2140
+ name: params.name,
2141
+ kind: "pointZ",
2142
+ tableName: params.name,
2143
+ srid: params.srid,
2144
+ objectCount: 0,
2145
+ geometryType: 1001
2146
+ });
2147
+ }
2148
+ };
2149
+
2150
+ // src/core/geometry/gaia/GaiaPolygonCodec.ts
2151
+ var GAIA_ENTITY_MARK2 = 105;
2152
+ var GEO_TYPE_POLYGON = 3;
2153
+ var GEO_TYPE_POLYGONZ = 1003;
2154
+ function is3DCoordinates2(coordinates) {
2155
+ return coordinates[0]?.[0]?.[0]?.length === 3;
2156
+ }
2157
+ function createPolygonMbr(coordinates) {
2158
+ const xs = [];
2159
+ const ys = [];
2160
+ for (const polygon of coordinates) {
2161
+ for (const ring of polygon) {
2162
+ for (const point of ring) {
2163
+ xs.push(point[0]);
2164
+ ys.push(point[1]);
2165
+ }
2166
+ }
2167
+ }
2168
+ if (xs.length === 0) {
2169
+ throw new GaiaFormatError("MultiPolygon must contain at least one point.");
2170
+ }
2171
+ return [
2172
+ Math.min(...xs),
2173
+ Math.min(...ys),
2174
+ Math.max(...xs),
2175
+ Math.max(...ys)
2176
+ ];
2177
+ }
2178
+ function writeRing2D(writer, ring) {
2179
+ writer.writeInt32(ring.length, true);
2180
+ for (const point of ring) {
2181
+ writer.writeFloat64(point[0], true);
2182
+ writer.writeFloat64(point[1], true);
2183
+ }
2184
+ }
2185
+ function writeRing3D(writer, ring) {
2186
+ writer.writeInt32(ring.length, true);
2187
+ for (const point of ring) {
2188
+ writer.writeFloat64(point[0], true);
2189
+ writer.writeFloat64(point[1], true);
2190
+ writer.writeFloat64(point[2], true);
2191
+ }
2192
+ }
2193
+ function readRing2D(cursor) {
2194
+ const pointCount = cursor.readInt32(true);
2195
+ const ring = [];
2196
+ for (let pointIndex = 0; pointIndex < pointCount; pointIndex += 1) {
2197
+ ring.push([cursor.readFloat64(true), cursor.readFloat64(true)]);
2198
+ }
2199
+ return ring;
2200
+ }
2201
+ function readRing3D(cursor) {
2202
+ const pointCount = cursor.readInt32(true);
2203
+ const ring = [];
2204
+ for (let pointIndex = 0; pointIndex < pointCount; pointIndex += 1) {
2205
+ ring.push([
2206
+ cursor.readFloat64(true),
2207
+ cursor.readFloat64(true),
2208
+ cursor.readFloat64(true)
2209
+ ]);
2210
+ }
2211
+ return ring;
2212
+ }
2213
+ var GaiaPolygonCodec = class {
2214
+ static readMultiPolygon(input) {
2215
+ const cursor = new BinaryCursor(input);
2216
+ const header = readGaiaHeader(cursor, GEO_TYPE_MULTIPOLYGON);
2217
+ const polygonCount = cursor.readInt32(true);
2218
+ const coordinates = [];
2219
+ for (let polygonIndex = 0; polygonIndex < polygonCount; polygonIndex += 1) {
2220
+ const entityMark = cursor.readUint8();
2221
+ if (entityMark !== GAIA_ENTITY_MARK2) {
2222
+ throw new GaiaFormatError(
2223
+ `Invalid Polygon entity mark: expected 0x${GAIA_ENTITY_MARK2.toString(16)}, got 0x${entityMark.toString(16)}.`
2224
+ );
2225
+ }
2226
+ const polygonGeoType = cursor.readInt32(true);
2227
+ if (polygonGeoType !== GEO_TYPE_POLYGON) {
2228
+ throw new GaiaFormatError(
2229
+ `Invalid Polygon geoType: expected ${GEO_TYPE_POLYGON}, got ${polygonGeoType}.`
2230
+ );
2231
+ }
2232
+ const ringCount = cursor.readInt32(true);
2233
+ if (ringCount < 1) {
2234
+ throw new GaiaFormatError(
2235
+ `Polygon must contain at least one ring, got ${ringCount}.`
2236
+ );
2237
+ }
2238
+ const polygon = [];
2239
+ for (let ringIndex = 0; ringIndex < ringCount; ringIndex += 1) {
2240
+ polygon.push(readRing2D(cursor));
2241
+ }
2242
+ coordinates.push(polygon);
2243
+ }
2244
+ validateGaiaEnd(cursor);
2245
+ return {
2246
+ type: "MultiPolygon",
2247
+ coordinates,
2248
+ srid: header.srid,
2249
+ bbox: header.mbr,
2250
+ hasZ: false,
2251
+ geoType: header.geoType
2252
+ };
2253
+ }
2254
+ static readMultiPolygonZ(input) {
2255
+ const cursor = new BinaryCursor(input);
2256
+ const header = readGaiaHeader(cursor, GEO_TYPE_MULTIPOLYGONZ);
2257
+ const polygonCount = cursor.readInt32(true);
2258
+ const coordinates = [];
2259
+ for (let polygonIndex = 0; polygonIndex < polygonCount; polygonIndex += 1) {
2260
+ const entityMark = cursor.readUint8();
2261
+ if (entityMark !== GAIA_ENTITY_MARK2) {
2262
+ throw new GaiaFormatError(
2263
+ `Invalid Polygon entity mark: expected 0x${GAIA_ENTITY_MARK2.toString(16)}, got 0x${entityMark.toString(16)}.`
2264
+ );
2265
+ }
2266
+ const polygonGeoType = cursor.readInt32(true);
2267
+ if (polygonGeoType !== GEO_TYPE_POLYGONZ) {
2268
+ throw new GaiaFormatError(
2269
+ `Invalid PolygonZ geoType: expected ${GEO_TYPE_POLYGONZ}, got ${polygonGeoType}.`
2270
+ );
2271
+ }
2272
+ const ringCount = cursor.readInt32(true);
2273
+ if (ringCount < 1) {
2274
+ throw new GaiaFormatError(
2275
+ `Polygon must contain at least one ring, got ${ringCount}.`
2276
+ );
2277
+ }
2278
+ const polygon = [];
2279
+ for (let ringIndex = 0; ringIndex < ringCount; ringIndex += 1) {
2280
+ polygon.push(readRing3D(cursor));
2281
+ }
2282
+ coordinates.push(polygon);
2283
+ }
2284
+ validateGaiaEnd(cursor);
2285
+ return {
2286
+ type: "MultiPolygon",
2287
+ coordinates,
2288
+ srid: header.srid,
2289
+ bbox: header.mbr,
2290
+ hasZ: true,
2291
+ geoType: header.geoType
2292
+ };
2293
+ }
2294
+ static writeMultiPolygon(geometry, srid) {
2295
+ if (is3DCoordinates2(geometry.coordinates)) {
2296
+ throw new GaiaFormatError("writeMultiPolygon expects 2D coordinates.");
2297
+ }
2298
+ const writer = new BinaryWriter();
2299
+ writeGaiaHeader(
2300
+ writer,
2301
+ srid,
2302
+ createPolygonMbr(geometry.coordinates),
2303
+ GEO_TYPE_MULTIPOLYGON
2304
+ );
2305
+ writer.writeInt32(geometry.coordinates.length, true);
2306
+ for (const polygon of geometry.coordinates) {
2307
+ writer.writeUint8(GAIA_ENTITY_MARK2);
2308
+ writer.writeInt32(GEO_TYPE_POLYGON, true);
2309
+ writer.writeInt32(polygon.length, true);
2310
+ for (const ring of polygon) {
2311
+ writeRing2D(writer, ring);
2312
+ }
2313
+ }
2314
+ writer.writeUint8(GAIA_END);
2315
+ return writer.toUint8Array();
2316
+ }
2317
+ static writeMultiPolygonZ(geometry, srid) {
2318
+ if (!is3DCoordinates2(geometry.coordinates)) {
2319
+ throw new GaiaFormatError("writeMultiPolygonZ expects 3D coordinates.");
2320
+ }
2321
+ const writer = new BinaryWriter();
2322
+ writeGaiaHeader(
2323
+ writer,
2324
+ srid,
2325
+ createPolygonMbr(geometry.coordinates),
2326
+ GEO_TYPE_MULTIPOLYGONZ
2327
+ );
2328
+ writer.writeInt32(geometry.coordinates.length, true);
2329
+ for (const polygon of geometry.coordinates) {
2330
+ writer.writeUint8(GAIA_ENTITY_MARK2);
2331
+ writer.writeInt32(GEO_TYPE_POLYGONZ, true);
2332
+ writer.writeInt32(polygon.length, true);
2333
+ for (const ring of polygon) {
2334
+ writeRing3D(writer, ring);
2335
+ }
2336
+ }
2337
+ writer.writeUint8(GAIA_END);
2338
+ return writer.toUint8Array();
2339
+ }
2340
+ };
2341
+
2342
+ // src/core/dataset/RegionDataset.ts
2343
+ function mapRegionRow(row) {
2344
+ const { SmID, SmGeometry, ...attributes } = row;
2345
+ return {
2346
+ id: SmID,
2347
+ geometry: GaiaPolygonCodec.readMultiPolygon(normalizeGeometryBlob(SmGeometry)),
2348
+ attributes
2349
+ };
2350
+ }
2351
+ var RegionDataset = class _RegionDataset extends BaseDataset {
2352
+ constructor(driver, info) {
2353
+ super(driver, info);
2354
+ __publicField(this, "registerRepository");
2355
+ this.registerRepository = new SmRegisterRepository(driver);
2356
+ }
2357
+ async getById(id) {
2358
+ const row = await queryOne(
2359
+ this.driver,
2360
+ `SELECT * FROM "${this.info.tableName}" WHERE SmID = ?`,
2361
+ [id]
2362
+ );
2363
+ return row ? mapRegionRow(row) : null;
2364
+ }
2365
+ async list(options) {
2366
+ const { sql, params } = buildListSql(this.info.tableName, options);
2367
+ const rows = await queryAll(this.driver, sql, params);
2368
+ return rows.map((row) => mapRegionRow(row));
2369
+ }
2370
+ async *iterate(options) {
2371
+ const { sql, params } = buildListSql(this.info.tableName, options);
2372
+ const statement = await this.driver.prepare(sql);
2373
+ try {
2374
+ if (params.length > 0) {
2375
+ await statement.bind(params);
2376
+ }
2377
+ while (await statement.step()) {
2378
+ yield mapRegionRow(
2379
+ await statement.getRow()
2380
+ );
2381
+ }
2382
+ } finally {
2383
+ await statement.finalize();
2384
+ }
2385
+ }
2386
+ async insert(feature) {
2387
+ const userFields = await this.getFields();
2388
+ const columnNames = ["SmID", "SmUserID", "SmGeometry", ...userFields.map((field) => field.name)];
2389
+ const placeholders = columnNames.map(() => "?").join(", ");
2390
+ const geometry = GaiaPolygonCodec.writeMultiPolygon(
2391
+ feature.geometry,
2392
+ feature.geometry.srid ?? this.info.srid ?? 0
2393
+ );
2394
+ const sql = `INSERT INTO "${this.info.tableName}" (${columnNames.map((column) => `"${column}"`).join(", ")}) VALUES (${placeholders})`;
2395
+ const params = [
2396
+ feature.id,
2397
+ 0,
2398
+ geometry,
2399
+ ...userFields.map((field) => feature.attributes[field.name] ?? null)
2400
+ ];
2401
+ await this.driver.transaction(async () => {
2402
+ await executeSql(this.driver, sql, params);
2403
+ await this.registerRepository.incrementObjectCount(this.info.id, geometry.byteLength);
2404
+ });
2405
+ }
2406
+ async insertMany(features) {
2407
+ const userFields = await this.getFields();
2408
+ const columnNames = ["SmID", "SmUserID", "SmGeometry", ...userFields.map((field) => field.name)];
2409
+ const placeholders = columnNames.map(() => "?").join(", ");
2410
+ const sql = `INSERT INTO "${this.info.tableName}" (${columnNames.map((column) => `"${column}"`).join(", ")}) VALUES (${placeholders})`;
2411
+ await this.driver.transaction(async () => {
2412
+ const statement = await this.driver.prepare(sql);
2413
+ try {
2414
+ let count = 0;
2415
+ let maxGeometrySize = 0;
2416
+ for await (const feature of features) {
2417
+ const geometry = GaiaPolygonCodec.writeMultiPolygon(
2418
+ feature.geometry,
2419
+ feature.geometry.srid ?? this.info.srid ?? 0
2420
+ );
2421
+ maxGeometrySize = Math.max(maxGeometrySize, geometry.byteLength);
2422
+ const params = [
2423
+ feature.id,
2424
+ 0,
2425
+ geometry,
2426
+ ...userFields.map(
2427
+ (field) => feature.attributes[field.name] ?? null
2428
+ )
2429
+ ];
2430
+ await statement.bind(params);
2431
+ await statement.step();
2432
+ await statement.reset();
2433
+ count++;
2434
+ }
2435
+ if (count > 0) {
2436
+ await this.registerRepository.incrementObjectCountBatch(
2437
+ this.info.id,
2438
+ count,
2439
+ maxGeometrySize
2440
+ );
2441
+ }
2442
+ } finally {
2443
+ await statement.finalize();
2444
+ }
2445
+ });
2446
+ }
2447
+ async count() {
2448
+ const row = await queryOne(
2449
+ this.driver,
2450
+ `SELECT COUNT(*) AS count FROM "${this.info.tableName}"`,
2451
+ []
2452
+ );
2453
+ return row?.count ?? 0;
2454
+ }
2455
+ async update(id, changes) {
2456
+ if (!changes.geometry && !changes.attributes) {
2457
+ return;
2458
+ }
2459
+ const setClauses = [];
2460
+ const params = [];
2461
+ if (changes.geometry) {
2462
+ const geometry = GaiaPolygonCodec.writeMultiPolygon(
2463
+ changes.geometry,
2464
+ changes.geometry.srid ?? this.info.srid ?? 0
2465
+ );
2466
+ setClauses.push('"SmGeometry" = ?');
2467
+ params.push(geometry);
2468
+ }
2469
+ if (changes.attributes) {
2470
+ const userFields = await this.getFields();
2471
+ const fieldNames = new Set(userFields.map((f) => f.name));
2472
+ const validEntries = Object.entries(changes.attributes).filter(
2473
+ ([key]) => fieldNames.has(key)
2474
+ );
2475
+ for (const [key, value] of validEntries) {
2476
+ setClauses.push(`"${key}" = ?`);
2477
+ params.push(value);
2478
+ }
2479
+ }
2480
+ if (setClauses.length === 0) {
2481
+ return;
2482
+ }
2483
+ const sql = `UPDATE "${this.info.tableName}" SET ${setClauses.join(", ")} WHERE SmID = ?`;
2484
+ params.push(id);
2485
+ await this.driver.transaction(async () => {
2486
+ await executeSql(this.driver, sql, params);
2487
+ });
2488
+ }
2489
+ async delete(id) {
2490
+ await this.driver.transaction(async () => {
2491
+ await executeSql(
2492
+ this.driver,
2493
+ `DELETE FROM "${this.info.tableName}" WHERE SmID = ?`,
2494
+ [id]
2495
+ );
2496
+ await this.registerRepository.decrementObjectCount(this.info.id);
2497
+ });
2498
+ }
2499
+ static async create(driver, registerRepository, params) {
2500
+ const fields = params.fields ?? [];
2501
+ const datasetId = await registerRepository.insert({
2502
+ name: params.name,
2503
+ kind: "region",
2504
+ srid: params.srid,
2505
+ idColumnName: "SmID",
2506
+ geometryColumnName: "SmGeometry"
2507
+ });
2508
+ const userColumnDefinitions = fields.map((field) => {
2509
+ const nullability = field.nullable ? "" : " NOT NULL";
2510
+ return `"${field.name}" ${sqliteColumnType(field)}${nullability}`;
2511
+ });
2512
+ const createTableParts = [
2513
+ `"SmID" INTEGER NOT NULL PRIMARY KEY`,
2514
+ `"SmUserID" INTEGER DEFAULT 0 NOT NULL`,
2515
+ `"SmGeometry" BLOB NOT NULL`,
2516
+ ...userColumnDefinitions
2517
+ ];
2518
+ await driver.exec(
2519
+ `CREATE TABLE "${params.name}" (${createTableParts.join(", ")})`
2520
+ );
2521
+ await executeSql(
2522
+ driver,
2523
+ `INSERT INTO geometry_columns (
2524
+ f_table_name,
2525
+ f_geometry_column,
2526
+ geometry_type,
2527
+ coord_dimension,
2528
+ srid,
2529
+ spatial_index_enabled
2530
+ ) VALUES (?, ?, ?, ?, ?, ?)`,
2531
+ [params.name, "SmGeometry", 6, 2, params.srid, 0]
2532
+ );
2533
+ if (fields.length > 0) {
2534
+ const fieldInfoRepository = new SmFieldInfoRepository(driver);
2535
+ await fieldInfoRepository.insertAll(datasetId, fields);
2536
+ }
2537
+ return new _RegionDataset(driver, {
2538
+ id: datasetId,
2539
+ name: params.name,
2540
+ kind: "region",
2541
+ tableName: params.name,
2542
+ srid: params.srid,
2543
+ objectCount: 0,
2544
+ geometryType: 6
2545
+ });
2546
+ }
2547
+ };
2548
+
2549
+ // src/core/dataset/RegionZDataset.ts
2550
+ function mapRegionZRow(row) {
2551
+ const { SmID, SmGeometry, ...attributes } = row;
2552
+ return {
2553
+ id: SmID,
2554
+ geometry: GaiaPolygonCodec.readMultiPolygonZ(normalizeGeometryBlob(SmGeometry)),
2555
+ attributes
2556
+ };
2557
+ }
2558
+ var RegionZDataset = class _RegionZDataset extends BaseDataset {
2559
+ constructor(driver, info) {
2560
+ super(driver, info);
2561
+ __publicField(this, "registerRepository");
2562
+ this.registerRepository = new SmRegisterRepository(driver);
2563
+ }
2564
+ async getById(id) {
2565
+ const row = await queryOne(
2566
+ this.driver,
2567
+ `SELECT * FROM "${this.info.tableName}" WHERE SmID = ?`,
2568
+ [id]
2569
+ );
2570
+ return row ? mapRegionZRow(row) : null;
2571
+ }
2572
+ async list(options) {
2573
+ const { sql, params } = buildListSql(this.info.tableName, options);
2574
+ const rows = await queryAll(this.driver, sql, params);
2575
+ return rows.map((row) => mapRegionZRow(row));
2576
+ }
2577
+ async *iterate(options) {
2578
+ const { sql, params } = buildListSql(this.info.tableName, options);
2579
+ const statement = await this.driver.prepare(sql);
2580
+ try {
2581
+ if (params.length > 0) {
2582
+ await statement.bind(params);
2583
+ }
2584
+ while (await statement.step()) {
2585
+ yield mapRegionZRow(
2586
+ await statement.getRow()
2587
+ );
2588
+ }
2589
+ } finally {
2590
+ await statement.finalize();
2591
+ }
2592
+ }
2593
+ async count() {
2594
+ const row = await queryOne(
2595
+ this.driver,
2596
+ `SELECT COUNT(*) AS count FROM "${this.info.tableName}"`,
2597
+ []
2598
+ );
2599
+ return row?.count ?? 0;
2600
+ }
2601
+ async insert(feature) {
2602
+ const userFields = await this.getFields();
2603
+ const columnNames = ["SmID", "SmUserID", "SmGeometry", ...userFields.map((field) => field.name)];
2604
+ const placeholders = columnNames.map(() => "?").join(", ");
2605
+ const geometry = GaiaPolygonCodec.writeMultiPolygonZ(
2606
+ feature.geometry,
2607
+ feature.geometry.srid ?? this.info.srid ?? 0
2608
+ );
2609
+ const sql = `INSERT INTO "${this.info.tableName}" (${columnNames.map((column) => `"${column}"`).join(", ")}) VALUES (${placeholders})`;
2610
+ const params = [
2611
+ feature.id,
2612
+ 0,
2613
+ geometry,
2614
+ ...userFields.map((field) => feature.attributes[field.name] ?? null)
2615
+ ];
2616
+ await this.driver.transaction(async () => {
2617
+ await executeSql(this.driver, sql, params);
2618
+ await this.registerRepository.incrementObjectCount(this.info.id, geometry.byteLength);
2619
+ });
2620
+ }
2621
+ async insertMany(features) {
2622
+ const userFields = await this.getFields();
2623
+ const columnNames = ["SmID", "SmUserID", "SmGeometry", ...userFields.map((field) => field.name)];
2624
+ const placeholders = columnNames.map(() => "?").join(", ");
2625
+ const sql = `INSERT INTO "${this.info.tableName}" (${columnNames.map((column) => `"${column}"`).join(", ")}) VALUES (${placeholders})`;
2626
+ await this.driver.transaction(async () => {
2627
+ const statement = await this.driver.prepare(sql);
2628
+ try {
2629
+ let count = 0;
2630
+ let maxGeometrySize = 0;
2631
+ for await (const feature of features) {
2632
+ const geometry = GaiaPolygonCodec.writeMultiPolygonZ(
2633
+ feature.geometry,
2634
+ feature.geometry.srid ?? this.info.srid ?? 0
2635
+ );
2636
+ maxGeometrySize = Math.max(maxGeometrySize, geometry.byteLength);
2637
+ const params = [
2638
+ feature.id,
2639
+ 0,
2640
+ geometry,
2641
+ ...userFields.map(
2642
+ (field) => feature.attributes[field.name] ?? null
2643
+ )
2644
+ ];
2645
+ await statement.bind(params);
2646
+ await statement.step();
2647
+ await statement.reset();
2648
+ count++;
2649
+ }
2650
+ if (count > 0) {
2651
+ await this.registerRepository.incrementObjectCountBatch(
2652
+ this.info.id,
2653
+ count,
2654
+ maxGeometrySize
2655
+ );
2656
+ }
2657
+ } finally {
2658
+ await statement.finalize();
2659
+ }
2660
+ });
2661
+ }
2662
+ async update(id, changes) {
2663
+ if (!changes.geometry && !changes.attributes) {
2664
+ return;
2665
+ }
2666
+ const setClauses = [];
2667
+ const params = [];
2668
+ if (changes.geometry) {
2669
+ const geometry = GaiaPolygonCodec.writeMultiPolygonZ(
2670
+ changes.geometry,
2671
+ changes.geometry.srid ?? this.info.srid ?? 0
2672
+ );
2673
+ setClauses.push('"SmGeometry" = ?');
2674
+ params.push(geometry);
2675
+ }
2676
+ if (changes.attributes) {
2677
+ const userFields = await this.getFields();
2678
+ const fieldNames = new Set(userFields.map((f) => f.name));
2679
+ const validEntries = Object.entries(changes.attributes).filter(
2680
+ ([key]) => fieldNames.has(key)
2681
+ );
2682
+ for (const [key, value] of validEntries) {
2683
+ setClauses.push(`"${key}" = ?`);
2684
+ params.push(value);
2685
+ }
2686
+ }
2687
+ if (setClauses.length === 0) {
2688
+ return;
2689
+ }
2690
+ const sql = `UPDATE "${this.info.tableName}" SET ${setClauses.join(", ")} WHERE SmID = ?`;
2691
+ params.push(id);
2692
+ await this.driver.transaction(async () => {
2693
+ await executeSql(this.driver, sql, params);
2694
+ });
2695
+ }
2696
+ async delete(id) {
2697
+ await this.driver.transaction(async () => {
2698
+ await executeSql(
2699
+ this.driver,
2700
+ `DELETE FROM "${this.info.tableName}" WHERE SmID = ?`,
2701
+ [id]
2702
+ );
2703
+ await this.registerRepository.decrementObjectCount(this.info.id);
2704
+ });
2705
+ }
2706
+ static async create(driver, registerRepository, params) {
2707
+ const fields = params.fields ?? [];
2708
+ const datasetId = await registerRepository.insert({
2709
+ name: params.name,
2710
+ kind: "regionZ",
2711
+ srid: params.srid,
2712
+ idColumnName: "SmID",
2713
+ geometryColumnName: "SmGeometry"
2714
+ });
2715
+ const userColumnDefinitions = fields.map((field) => {
2716
+ const nullability = field.nullable ? "" : " NOT NULL";
2717
+ return `"${field.name}" ${sqliteColumnType(field)}${nullability}`;
2718
+ });
2719
+ const createTableParts = [
2720
+ `"SmID" INTEGER NOT NULL PRIMARY KEY`,
2721
+ `"SmUserID" INTEGER DEFAULT 0 NOT NULL`,
2722
+ `"SmGeometry" BLOB NOT NULL`,
2723
+ ...userColumnDefinitions
2724
+ ];
2725
+ await driver.exec(
2726
+ `CREATE TABLE "${params.name}" (${createTableParts.join(", ")})`
2727
+ );
2728
+ await executeSql(
2729
+ driver,
2730
+ `INSERT INTO geometry_columns (
2731
+ f_table_name,
2732
+ f_geometry_column,
2733
+ geometry_type,
2734
+ coord_dimension,
2735
+ srid,
2736
+ spatial_index_enabled
2737
+ ) VALUES (?, ?, ?, ?, ?, ?)`,
2738
+ [params.name, "SmGeometry", 1006, 3, params.srid, 0]
2739
+ );
2740
+ if (fields.length > 0) {
2741
+ const fieldInfoRepository = new SmFieldInfoRepository(driver);
2742
+ await fieldInfoRepository.insertAll(datasetId, fields);
2743
+ }
2744
+ return new _RegionZDataset(driver, {
2745
+ id: datasetId,
2746
+ name: params.name,
2747
+ kind: "regionZ",
2748
+ tableName: params.name,
2749
+ srid: params.srid,
2750
+ objectCount: 0,
2751
+ geometryType: 1006
2752
+ });
2753
+ }
2754
+ };
2755
+
2756
+ // src/core/dataset/TabularDataset.ts
2757
+ var SYSTEM_COLUMN_PREFIX = "Sm";
2758
+ function mapTabularRow(row) {
2759
+ const { SmID, ...rest } = row;
2760
+ const attributes = {};
2761
+ for (const [key, value] of Object.entries(rest)) {
2762
+ if (!key.startsWith(SYSTEM_COLUMN_PREFIX)) {
2763
+ attributes[key] = value;
2764
+ }
2765
+ }
2766
+ return {
2767
+ id: SmID,
2768
+ attributes
2769
+ };
2770
+ }
2771
+ var TabularDataset = class _TabularDataset extends BaseDataset {
2772
+ constructor(driver, info) {
2773
+ super(driver, info);
2774
+ __publicField(this, "registerRepository");
2775
+ this.registerRepository = new SmRegisterRepository(driver);
2776
+ }
2777
+ async getById(id) {
2778
+ const row = await queryOne(
2779
+ this.driver,
2780
+ `SELECT * FROM "${this.info.tableName}" WHERE SmID = ?`,
2781
+ [id]
2782
+ );
2783
+ return row ? mapTabularRow(row) : null;
2784
+ }
2785
+ async list(options) {
2786
+ const { sql, params } = buildListSql(this.info.tableName, options);
2787
+ const rows = await queryAll(this.driver, sql, params);
2788
+ return rows.map((row) => mapTabularRow(row));
2789
+ }
2790
+ async *iterate(options) {
2791
+ const { sql, params } = buildListSql(this.info.tableName, options);
2792
+ const statement = await this.driver.prepare(sql);
2793
+ try {
2794
+ if (params.length > 0) {
2795
+ await statement.bind(params);
2796
+ }
2797
+ while (await statement.step()) {
2798
+ yield mapTabularRow(
2799
+ await statement.getRow()
2800
+ );
2801
+ }
2802
+ } finally {
2803
+ await statement.finalize();
2804
+ }
2805
+ }
2806
+ async count() {
2807
+ const row = await queryOne(
2808
+ this.driver,
2809
+ `SELECT COUNT(*) AS count FROM "${this.info.tableName}"`,
2810
+ []
2811
+ );
2812
+ return row?.count ?? 0;
2813
+ }
2814
+ async insert(record) {
2815
+ const userFields = await this.getFields();
2816
+ const columnNames = [
2817
+ "SmID",
2818
+ "SmUserID",
2819
+ ...userFields.map((field) => field.name)
2820
+ ];
2821
+ const placeholders = columnNames.map(() => "?").join(", ");
2822
+ const sql = `INSERT INTO "${this.info.tableName}" (${columnNames.map((column) => `"${column}"`).join(", ")}) VALUES (${placeholders})`;
2823
+ const params = [
2824
+ record.id,
2825
+ 0,
2826
+ ...userFields.map(
2827
+ (field) => record.attributes[field.name] ?? null
2828
+ )
2829
+ ];
2830
+ await this.driver.transaction(async () => {
2831
+ await executeSql(this.driver, sql, params);
2832
+ await this.registerRepository.incrementObjectCount(this.info.id);
2833
+ });
2834
+ }
2835
+ async insertMany(records) {
2836
+ const userFields = await this.getFields();
2837
+ const columnNames = [
2838
+ "SmID",
2839
+ "SmUserID",
2840
+ ...userFields.map((field) => field.name)
2841
+ ];
2842
+ const placeholders = columnNames.map(() => "?").join(", ");
2843
+ const sql = `INSERT INTO "${this.info.tableName}" (${columnNames.map((column) => `"${column}"`).join(", ")}) VALUES (${placeholders})`;
2844
+ await this.driver.transaction(async () => {
2845
+ const statement = await this.driver.prepare(sql);
2846
+ try {
2847
+ let count = 0;
2848
+ for await (const record of records) {
2849
+ const params = [
2850
+ record.id,
2851
+ 0,
2852
+ ...userFields.map(
2853
+ (field) => record.attributes[field.name] ?? null
2854
+ )
2855
+ ];
2856
+ await statement.bind(params);
2857
+ await statement.step();
2858
+ await statement.reset();
2859
+ count++;
2860
+ }
2861
+ if (count > 0) {
2862
+ await this.registerRepository.incrementObjectCountBatch(
2863
+ this.info.id,
2864
+ count,
2865
+ 0
2866
+ );
2867
+ }
2868
+ } finally {
2869
+ await statement.finalize();
2870
+ }
2871
+ });
2872
+ }
2873
+ async update(id, attributes) {
2874
+ const userFields = await this.getFields();
2875
+ const fieldNames = new Set(userFields.map((f) => f.name));
2876
+ const validEntries = Object.entries(attributes).filter(
2877
+ ([key]) => fieldNames.has(key)
2878
+ );
2879
+ if (validEntries.length === 0) return;
2880
+ const keys = validEntries.map(([key]) => key);
2881
+ const setClauses = keys.map((key) => `"${key}" = ?`).join(", ");
2882
+ const sql = `UPDATE "${this.info.tableName}" SET ${setClauses} WHERE SmID = ?`;
2883
+ const params = [
2884
+ ...keys.map((key) => attributes[key]),
2885
+ id
2886
+ ];
2887
+ await this.driver.transaction(async () => {
2888
+ await executeSql(this.driver, sql, params);
2889
+ });
2890
+ }
2891
+ async delete(id) {
2892
+ await this.driver.transaction(async () => {
2893
+ await executeSql(
2894
+ this.driver,
2895
+ `DELETE FROM "${this.info.tableName}" WHERE SmID = ?`,
2896
+ [id]
2897
+ );
2898
+ await this.registerRepository.decrementObjectCount(this.info.id);
2899
+ });
2900
+ }
2901
+ static async create(driver, registerRepository, params) {
2902
+ const fields = params.fields ?? [];
2903
+ const datasetId = await registerRepository.insert({
2904
+ name: params.name,
2905
+ kind: "tabular",
2906
+ srid: 0,
2907
+ idColumnName: "SmID",
2908
+ geometryColumnName: null
2909
+ });
2910
+ const userColumnDefinitions = fields.map((field) => {
2911
+ const nullability = field.nullable ? "" : " NOT NULL";
2912
+ return `"${field.name}" ${sqliteColumnType(field)}${nullability}`;
2913
+ });
2914
+ const createTableParts = [
2915
+ `"SmID" INTEGER NOT NULL PRIMARY KEY`,
2916
+ `"SmUserID" INTEGER DEFAULT 0 NOT NULL`,
2917
+ ...userColumnDefinitions
2918
+ ];
2919
+ await driver.exec(
2920
+ `CREATE TABLE "${params.name}" (${createTableParts.join(", ")})`
2921
+ );
2922
+ if (fields.length > 0) {
2923
+ const fieldInfoRepository = new SmFieldInfoRepository(driver);
2924
+ await fieldInfoRepository.insertAll(datasetId, fields);
2925
+ }
2926
+ return new _TabularDataset(driver, {
2927
+ id: datasetId,
2928
+ name: params.name,
2929
+ kind: "tabular",
2930
+ tableName: params.name,
2931
+ srid: 0,
2932
+ objectCount: 0,
2933
+ geometryType: null
2934
+ });
2935
+ }
2936
+ };
2937
+
2938
+ // src/core/dataset/TextDataset.ts
2939
+ var TextDataset = class extends BaseDataset {
2940
+ async getById() {
2941
+ throw createNotImplementedError("TextDataset.getById");
2942
+ }
2943
+ async list(_options) {
2944
+ throw createNotImplementedError("TextDataset.list");
2945
+ }
2946
+ async *iterate(_options) {
2947
+ throw createNotImplementedError("TextDataset.iterate");
2948
+ }
2949
+ async count() {
2950
+ throw createNotImplementedError("TextDataset.count");
2951
+ }
2952
+ async insert() {
2953
+ throw createNotImplementedError("TextDataset.insert");
2954
+ }
2955
+ async insertMany() {
2956
+ throw createNotImplementedError("TextDataset.insertMany");
2957
+ }
2958
+ async update() {
2959
+ throw createNotImplementedError("TextDataset.update");
2960
+ }
2961
+ async delete() {
2962
+ throw createNotImplementedError("TextDataset.delete");
2963
+ }
2964
+ };
2965
+
2966
+ // src/core/schema/UdbxSchemaInitializer.ts
2967
+ var UDBX_SCHEMA_STATEMENTS = [
2968
+ `CREATE TABLE spatial_ref_sys (
2969
+ srid INTEGER NOT NULL PRIMARY KEY,
2970
+ auth_name TEXT NOT NULL,
2971
+ auth_srid INTEGER NOT NULL,
2972
+ ref_sys_name TEXT NOT NULL DEFAULT 'Unknown',
2973
+ proj4text TEXT NOT NULL,
2974
+ srtext TEXT NOT NULL DEFAULT 'Undefined'
2975
+ )`,
2976
+ `CREATE TABLE geometry_columns (
2977
+ f_table_name TEXT NOT NULL,
2978
+ f_geometry_column TEXT NOT NULL,
2979
+ geometry_type INTEGER NOT NULL,
2980
+ coord_dimension INTEGER NOT NULL,
2981
+ srid INTEGER NOT NULL,
2982
+ spatial_index_enabled INTEGER NOT NULL,
2983
+ CONSTRAINT pk_geom_cols PRIMARY KEY (f_table_name, f_geometry_column)
2984
+ )`,
2985
+ `CREATE TABLE SmDataSourceInfo (
2986
+ SmFlag INTEGER DEFAULT 0 NOT NULL PRIMARY KEY AUTOINCREMENT,
2987
+ SmVersion INTEGER,
2988
+ SmDsDescription TEXT,
2989
+ SmProjectInfo BLOB,
2990
+ SmLastUpdateTime DATE DEFAULT CURRENT_TIMESTAMP NOT NULL,
2991
+ SmDataFormat INTEGER DEFAULT 0 NOT NULL
2992
+ )`,
2993
+ `CREATE TABLE SmRegister (
2994
+ SmDatasetID INTEGER DEFAULT 0 NOT NULL PRIMARY KEY,
2995
+ SmDatasetName TEXT,
2996
+ SmTableName TEXT,
2997
+ SmOption INTEGER,
2998
+ SmEncType INTEGER,
2999
+ SmParentDTID INTEGER DEFAULT 0 NOT NULL,
3000
+ SmDatasetType INTEGER,
3001
+ SmObjectCount INTEGER DEFAULT 0 NOT NULL,
3002
+ SmLeft REAL,
3003
+ SmRight REAL,
3004
+ SmTop REAL,
3005
+ SmBottom REAL,
3006
+ SmIDColName TEXT,
3007
+ SmGeoColName TEXT,
3008
+ SmMinZ REAL,
3009
+ SmMaxZ REAL,
3010
+ SmSRID INTEGER DEFAULT 0,
3011
+ SmIndexType INTEGER DEFAULT 1,
3012
+ SmToleRanceFuzzy REAL,
3013
+ SmToleranceDAngle REAL,
3014
+ SmToleranceNodeSnap REAL,
3015
+ SmToleranceSmallPolygon REAL,
3016
+ SmToleranceGrain REAL,
3017
+ SmMaxGeometrySize INTEGER DEFAULT 0 NOT NULL,
3018
+ SmOptimizeCount INTEGER DEFAULT 0 NOT NULL,
3019
+ SmOptimizeRatio REAL,
3020
+ SmDescription TEXT,
3021
+ SmExtInfo TEXT,
3022
+ SmCreateTime TEXT,
3023
+ SmLastUpdateTime TEXT,
3024
+ SmProjectInfo BLOB
3025
+ )`,
3026
+ `CREATE TABLE SmFieldInfo (
3027
+ SmID INTEGER DEFAULT 0 NOT NULL PRIMARY KEY AUTOINCREMENT,
3028
+ SmDatasetID INTEGER,
3029
+ SmFieldName TEXT,
3030
+ SmFieldCaption TEXT,
3031
+ SmFieldType INTEGER,
3032
+ SmFieldFormat TEXT,
3033
+ SmFieldSign INTEGER,
3034
+ SmFieldDomain TEXT,
3035
+ SmFieldUpdatable INTEGER,
3036
+ SmFieldbRequired INTEGER,
3037
+ SmFieldDefaultValue TEXT,
3038
+ SmFieldSize INTEGER
3039
+ )`,
3040
+ "INSERT INTO SmDataSourceInfo (SmVersion, SmDataFormat) VALUES (0, 1)",
3041
+ "INSERT INTO spatial_ref_sys (srid, auth_name, auth_srid, ref_sys_name, proj4text, srtext) VALUES (0, 'none', 0, 'Undefined', '', 'Undefined')",
3042
+ `INSERT INTO spatial_ref_sys (srid, auth_name, auth_srid, ref_sys_name, proj4text, srtext)
3043
+ VALUES (
3044
+ 4326,
3045
+ 'epsg',
3046
+ 4326,
3047
+ 'WGS 84',
3048
+ '+proj=longlat +datum=WGS84 +no_defs',
3049
+ 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433],AUTHORITY["EPSG","4326"]]'
3050
+ )`
3051
+ ];
3052
+ var UdbxSchemaInitializer = class {
3053
+ static async isInitialized(driver) {
3054
+ const statement = await driver.prepare(
3055
+ "SELECT name FROM sqlite_master WHERE type = 'table' AND name = ? LIMIT 1"
3056
+ );
3057
+ try {
3058
+ await statement.bind(["SmDataSourceInfo"]);
3059
+ return statement.step();
3060
+ } finally {
3061
+ await statement.finalize();
3062
+ }
3063
+ }
3064
+ static async ensureInitialized(driver) {
3065
+ const initialized = await this.isInitialized(driver);
3066
+ if (initialized) {
3067
+ return false;
3068
+ }
3069
+ await this.initialize(driver);
3070
+ return true;
3071
+ }
3072
+ static async initialize(driver) {
3073
+ await driver.exec("PRAGMA journal_mode = WAL");
3074
+ await driver.transaction(async () => {
3075
+ for (const statement of UDBX_SCHEMA_STATEMENTS) {
3076
+ await driver.exec(statement);
3077
+ }
3078
+ });
3079
+ }
3080
+ };
3081
+
3082
+ // src/core/datasource/UdbxDataSource.ts
3083
+ var UdbxDataSource = class _UdbxDataSource {
3084
+ constructor(driver, runtime = "unknown") {
3085
+ this.driver = driver;
3086
+ this.runtime = runtime;
3087
+ __publicField(this, "registerRepository");
3088
+ this.registerRepository = new SmRegisterRepository(driver);
3089
+ }
3090
+ static async open(params) {
3091
+ await params.driver.open(params.target);
3092
+ return new _UdbxDataSource(params.driver, params.runtime ?? "unknown");
3093
+ }
3094
+ static async create(params) {
3095
+ await params.driver.open(params.target);
3096
+ await UdbxSchemaInitializer.initialize(params.driver);
3097
+ return new _UdbxDataSource(params.driver, params.runtime ?? "unknown");
3098
+ }
3099
+ async listDatasets() {
3100
+ return this.registerRepository.findAll();
3101
+ }
3102
+ async getDataset(name) {
3103
+ const info = await this.registerRepository.findByName(name);
3104
+ if (!info) {
3105
+ return null;
3106
+ }
3107
+ switch (info.kind) {
3108
+ case "point":
3109
+ return new PointDataset(this.driver, info);
3110
+ case "pointZ":
3111
+ return new PointZDataset(this.driver, info);
3112
+ case "line":
3113
+ return new LineDataset(this.driver, info);
3114
+ case "lineZ":
3115
+ return new LineZDataset(this.driver, info);
3116
+ case "region":
3117
+ return new RegionDataset(this.driver, info);
3118
+ case "regionZ":
3119
+ return new RegionZDataset(this.driver, info);
3120
+ case "tabular":
3121
+ return new TabularDataset(this.driver, info);
3122
+ case "text":
3123
+ return new TextDataset(this.driver, info);
3124
+ case "cad":
3125
+ return new CadDataset(this.driver, info);
3126
+ default:
3127
+ return null;
3128
+ }
3129
+ }
3130
+ async createPointDataset(name, srid, fields) {
3131
+ return this.driver.transaction(async () => {
3132
+ const params = fields === void 0 ? { name, srid } : { name, srid, fields };
3133
+ return PointDataset.create(this.driver, this.registerRepository, params);
3134
+ });
3135
+ }
3136
+ async createLineDataset(name, srid, fields) {
3137
+ return this.driver.transaction(async () => {
3138
+ const params = fields === void 0 ? { name, srid } : { name, srid, fields };
3139
+ return LineDataset.create(this.driver, this.registerRepository, params);
3140
+ });
3141
+ }
3142
+ async createRegionDataset(name, srid, fields) {
3143
+ return this.driver.transaction(async () => {
3144
+ const params = fields === void 0 ? { name, srid } : { name, srid, fields };
3145
+ return RegionDataset.create(this.driver, this.registerRepository, params);
3146
+ });
3147
+ }
3148
+ async createPointZDataset(name, srid, fields) {
3149
+ return this.driver.transaction(async () => {
3150
+ const params = fields === void 0 ? { name, srid } : { name, srid, fields };
3151
+ return PointZDataset.create(this.driver, this.registerRepository, params);
3152
+ });
3153
+ }
3154
+ async createLineZDataset(name, srid, fields) {
3155
+ return this.driver.transaction(async () => {
3156
+ const params = fields === void 0 ? { name, srid } : { name, srid, fields };
3157
+ return LineZDataset.create(this.driver, this.registerRepository, params);
3158
+ });
3159
+ }
3160
+ async createRegionZDataset(name, srid, fields) {
3161
+ return this.driver.transaction(async () => {
3162
+ const params = fields === void 0 ? { name, srid } : { name, srid, fields };
3163
+ return RegionZDataset.create(this.driver, this.registerRepository, params);
3164
+ });
3165
+ }
3166
+ async createTabularDataset(name, fields) {
3167
+ return this.driver.transaction(async () => {
3168
+ const params = fields === void 0 ? { name } : { name, fields };
3169
+ return TabularDataset.create(this.driver, this.registerRepository, params);
3170
+ });
3171
+ }
3172
+ async createTextDataset(name, srid, fields) {
3173
+ return this.driver.transaction(async () => {
3174
+ const fieldList = fields ?? [];
3175
+ const datasetId = await this.registerRepository.insert({
3176
+ name,
3177
+ kind: "text",
3178
+ srid,
3179
+ idColumnName: "SmID",
3180
+ geometryColumnName: "SmGeometry"
3181
+ });
3182
+ const userColumnDefinitions = fieldList.map((field) => {
3183
+ const nullability = field.nullable ? "" : " NOT NULL";
3184
+ return `"${field.name}" TEXT${nullability}`;
3185
+ });
3186
+ const createTableParts = [
3187
+ `"SmID" INTEGER NOT NULL PRIMARY KEY`,
3188
+ `"SmUserID" INTEGER DEFAULT 0 NOT NULL`,
3189
+ `"SmGeometry" BLOB`,
3190
+ `"SmIndexKey" POLYGON`,
3191
+ ...userColumnDefinitions
3192
+ ];
3193
+ await this.driver.exec(
3194
+ `CREATE TABLE "${name}" (${createTableParts.join(", ")})`
3195
+ );
3196
+ if (fieldList.length > 0) {
3197
+ const fieldInfoRepository = new SmFieldInfoRepository(this.driver);
3198
+ await fieldInfoRepository.insertAll(datasetId, fieldList);
3199
+ }
3200
+ return new TextDataset(this.driver, {
3201
+ id: datasetId,
3202
+ name,
3203
+ kind: "text",
3204
+ tableName: name,
3205
+ srid,
3206
+ objectCount: 0,
3207
+ geometryType: null
3208
+ });
3209
+ });
3210
+ }
3211
+ async createCadDataset(name, fields) {
3212
+ return this.driver.transaction(async () => {
3213
+ const fieldList = fields ?? [];
3214
+ const datasetId = await this.registerRepository.insert({
3215
+ name,
3216
+ kind: "cad",
3217
+ srid: 0,
3218
+ idColumnName: "SmID",
3219
+ geometryColumnName: "SmGeometry"
3220
+ });
3221
+ const userColumnDefinitions = fieldList.map((field) => {
3222
+ const nullability = field.nullable ? "" : " NOT NULL";
3223
+ return `"${field.name}" TEXT${nullability}`;
3224
+ });
3225
+ const createTableParts = [
3226
+ `"SmID" INTEGER NOT NULL PRIMARY KEY`,
3227
+ `"SmUserID" INTEGER DEFAULT 0 NOT NULL`,
3228
+ `"SmGeometry" BLOB`,
3229
+ ...userColumnDefinitions
3230
+ ];
3231
+ await this.driver.exec(
3232
+ `CREATE TABLE "${name}" (${createTableParts.join(", ")})`
3233
+ );
3234
+ if (fieldList.length > 0) {
3235
+ const fieldInfoRepository = new SmFieldInfoRepository(this.driver);
3236
+ await fieldInfoRepository.insertAll(datasetId, fieldList);
3237
+ }
3238
+ return new CadDataset(this.driver, {
3239
+ id: datasetId,
3240
+ name,
3241
+ kind: "cad",
3242
+ tableName: name,
3243
+ srid: 0,
3244
+ objectCount: 0,
3245
+ geometryType: null
3246
+ });
3247
+ });
3248
+ }
3249
+ async close() {
3250
+ await this.driver.close();
3251
+ }
3252
+ };
3253
+
3254
+ // src/runtime-browser/worker/BrowserWorkerRuntime.ts
3255
+ function asReadableDataset(dataset) {
3256
+ return dataset;
3257
+ }
3258
+ function asWritableDataset(dataset) {
3259
+ return dataset;
3260
+ }
3261
+ function createSuccess(id, result) {
3262
+ return { id, ok: true, result };
3263
+ }
3264
+ function createFailure(id, code, message) {
3265
+ return {
3266
+ id,
3267
+ ok: false,
3268
+ error: { code, message }
3269
+ };
3270
+ }
3271
+ function asObject(value) {
3272
+ if (typeof value === "object" && value !== null) {
3273
+ return value;
3274
+ }
3275
+ return {};
3276
+ }
3277
+ function parseDatasetName(params) {
3278
+ const value = asObject(params).datasetName;
3279
+ return typeof value === "string" && value.length > 0 ? value : null;
3280
+ }
3281
+ var VALID_FIELD_TYPES = /* @__PURE__ */ new Set([
3282
+ "boolean",
3283
+ "byte",
3284
+ "int16",
3285
+ "int32",
3286
+ "int64",
3287
+ "single",
3288
+ "double",
3289
+ "date",
3290
+ "binary",
3291
+ "geometry",
3292
+ "char",
3293
+ "ntext",
3294
+ "text",
3295
+ "time"
3296
+ ]);
3297
+ var BrowserWorkerRuntime = class {
3298
+ constructor(options) {
3299
+ this.options = options;
3300
+ __publicField(this, "dataSource", null);
3301
+ }
3302
+ async handle(request) {
3303
+ try {
3304
+ return await this.dispatch(request);
3305
+ } catch (error) {
3306
+ const message = error instanceof Error ? error.message : "Unexpected runtime error.";
3307
+ return createFailure(request.id, "internal_error", message);
3308
+ }
3309
+ }
3310
+ async dispatch(request) {
3311
+ switch (request.method) {
3312
+ case RPC_METHODS.udbxOpen:
3313
+ return this.open(request);
3314
+ case RPC_METHODS.udbxClose:
3315
+ return this.close(request.id);
3316
+ case RPC_METHODS.udbxListDatasets:
3317
+ return this.listDatasets(request.id);
3318
+ case RPC_METHODS.udbxGetDatasetInfo:
3319
+ return this.getDatasetInfo(request);
3320
+ case RPC_METHODS.udbxExportDatabase:
3321
+ return this.exportDatabase(request.id);
3322
+ case RPC_METHODS.udbxImportDatabase:
3323
+ return this.importDatabase(request);
3324
+ case RPC_METHODS.udbxCreatePointDataset:
3325
+ return this.createPointDataset(request);
3326
+ case RPC_METHODS.udbxCreateLineDataset:
3327
+ return this.createLineDataset(request);
3328
+ case RPC_METHODS.udbxCreateRegionDataset:
3329
+ return this.createRegionDataset(request);
3330
+ case RPC_METHODS.udbxCreatePointZDataset:
3331
+ return this.createPointZDataset(request);
3332
+ case RPC_METHODS.udbxCreateLineZDataset:
3333
+ return this.createLineZDataset(request);
3334
+ case RPC_METHODS.udbxCreateRegionZDataset:
3335
+ return this.createRegionZDataset(request);
3336
+ case RPC_METHODS.udbxCreateTabularDataset:
3337
+ return this.createTabularDataset(request);
3338
+ case RPC_METHODS.udbxCreateCadDataset:
3339
+ return this.createCadDataset(request);
3340
+ case RPC_METHODS.datasetGetFields:
3341
+ return this.getFields(request);
3342
+ case RPC_METHODS.datasetGetById:
3343
+ return this.getById(request);
3344
+ case RPC_METHODS.datasetList:
3345
+ return this.listRows(request);
3346
+ case RPC_METHODS.datasetInsert:
3347
+ return this.insert(request);
3348
+ case RPC_METHODS.datasetInsertMany:
3349
+ return this.insertMany(request);
3350
+ case RPC_METHODS.datasetCount:
3351
+ return this.count(request);
3352
+ case RPC_METHODS.datasetUpdate:
3353
+ return this.update(request);
3354
+ case RPC_METHODS.datasetDelete:
3355
+ return this.delete(request);
3356
+ default:
3357
+ return createFailure(
3358
+ request.id,
3359
+ "unsupported_method",
3360
+ `Unsupported RPC method: ${request.method}`
3361
+ );
3362
+ }
3363
+ }
3364
+ async open(request) {
3365
+ if (this.dataSource) {
3366
+ await this.dataSource.close();
3367
+ this.dataSource = null;
3368
+ }
3369
+ const params = request.params ?? {};
3370
+ this.dataSource = await this.options.createDataSource(params);
3371
+ return createSuccess(request.id, { runtime: this.dataSource.runtime });
3372
+ }
3373
+ async close(id) {
3374
+ if (this.dataSource) {
3375
+ await this.dataSource.close();
3376
+ this.dataSource = null;
3377
+ }
3378
+ return createSuccess(id, void 0);
3379
+ }
3380
+ async listDatasets(id) {
3381
+ const ds = this.requireDataSource(id);
3382
+ if (!ds) {
3383
+ return createFailure(id, "not_open", "Datasource has not been opened.");
3384
+ }
3385
+ const rows = await ds.listDatasets();
3386
+ return createSuccess(id, rows);
3387
+ }
3388
+ async getDatasetInfo(request) {
3389
+ const ds = this.requireDataSource(request.id);
3390
+ if (!ds) {
3391
+ return createFailure(
3392
+ request.id,
3393
+ "not_open",
3394
+ "Datasource has not been opened."
3395
+ );
3396
+ }
3397
+ const name = asObject(request.params).name;
3398
+ if (typeof name !== "string" || name.length === 0) {
3399
+ return createFailure(
3400
+ request.id,
3401
+ "invalid_params",
3402
+ "Dataset name is required."
3403
+ );
3404
+ }
3405
+ const dataset = await ds.getDataset(name);
3406
+ return createSuccess(request.id, dataset?.info ?? null);
3407
+ }
3408
+ async exportDatabase(id) {
3409
+ const ds = this.requireDataSource(id);
3410
+ if (!ds) {
3411
+ return createFailure(id, "not_open", "Datasource has not been opened.");
3412
+ }
3413
+ if (typeof ds.__exportDatabase !== "function") {
3414
+ return createFailure(
3415
+ id,
3416
+ "unsupported_operation",
3417
+ "Current runtime does not support database export."
3418
+ );
3419
+ }
3420
+ const binary = await ds.__exportDatabase();
3421
+ return createSuccess(id, binary);
3422
+ }
3423
+ async importDatabase(request) {
3424
+ if (typeof this.options.replaceDataSource !== "function") {
3425
+ return createFailure(
3426
+ request.id,
3427
+ "unsupported_operation",
3428
+ "Current runtime does not support database import."
3429
+ );
3430
+ }
3431
+ const params = asObject(request.params);
3432
+ const binaryRaw = params.binary;
3433
+ let binary;
3434
+ if (binaryRaw instanceof Uint8Array) {
3435
+ binary = binaryRaw;
3436
+ } else if (Array.isArray(binaryRaw)) {
3437
+ binary = new Uint8Array(binaryRaw);
3438
+ } else {
3439
+ return createFailure(
3440
+ request.id,
3441
+ "invalid_params",
3442
+ "`binary` must be a Uint8Array or byte array."
3443
+ );
3444
+ }
3445
+ const preferOpfs = typeof params.preferOpfs === "boolean" ? params.preferOpfs : void 0;
3446
+ if (this.dataSource) {
3447
+ await this.dataSource.close();
3448
+ this.dataSource = null;
3449
+ }
3450
+ this.dataSource = await this.options.replaceDataSource({
3451
+ binary,
3452
+ ...preferOpfs === void 0 ? {} : { preferOpfs }
3453
+ });
3454
+ return createSuccess(request.id, { runtime: this.dataSource.runtime });
3455
+ }
3456
+ async createPointDataset(request) {
3457
+ const ds = this.requireDataSource(request.id);
3458
+ if (!ds) {
3459
+ return createFailure(
3460
+ request.id,
3461
+ "not_open",
3462
+ "Datasource has not been opened."
3463
+ );
3464
+ }
3465
+ const parsed = this.parseCreateDatasetParams(request);
3466
+ if ("error" in parsed) {
3467
+ return parsed.error;
3468
+ }
3469
+ const dataset = await ds.createPointDataset(
3470
+ parsed.params.name,
3471
+ parsed.params.srid,
3472
+ parsed.params.fields
3473
+ );
3474
+ return createSuccess(request.id, dataset.info);
3475
+ }
3476
+ async createLineDataset(request) {
3477
+ const ds = this.requireDataSource(request.id);
3478
+ if (!ds) {
3479
+ return createFailure(
3480
+ request.id,
3481
+ "not_open",
3482
+ "Datasource has not been opened."
3483
+ );
3484
+ }
3485
+ const parsed = this.parseCreateDatasetParams(request);
3486
+ if ("error" in parsed) {
3487
+ return parsed.error;
3488
+ }
3489
+ const dataset = await ds.createLineDataset(
3490
+ parsed.params.name,
3491
+ parsed.params.srid,
3492
+ parsed.params.fields
3493
+ );
3494
+ return createSuccess(request.id, dataset.info);
3495
+ }
3496
+ async createRegionDataset(request) {
3497
+ const ds = this.requireDataSource(request.id);
3498
+ if (!ds) {
3499
+ return createFailure(
3500
+ request.id,
3501
+ "not_open",
3502
+ "Datasource has not been opened."
3503
+ );
3504
+ }
3505
+ const parsed = this.parseCreateDatasetParams(request);
3506
+ if ("error" in parsed) {
3507
+ return parsed.error;
3508
+ }
3509
+ const dataset = await ds.createRegionDataset(
3510
+ parsed.params.name,
3511
+ parsed.params.srid,
3512
+ parsed.params.fields
3513
+ );
3514
+ return createSuccess(request.id, dataset.info);
3515
+ }
3516
+ async createTabularDataset(request) {
3517
+ const ds = this.requireDataSource(request.id);
3518
+ if (!ds) {
3519
+ return createFailure(
3520
+ request.id,
3521
+ "not_open",
3522
+ "Datasource has not been opened."
3523
+ );
3524
+ }
3525
+ const params = asObject(request.params);
3526
+ const name = params.name;
3527
+ if (typeof name !== "string" || name.length === 0) {
3528
+ return createFailure(
3529
+ request.id,
3530
+ "invalid_params",
3531
+ "Dataset name is required."
3532
+ );
3533
+ }
3534
+ const fields = params.fields;
3535
+ const parsedFields = this.parseFieldInfos(request.id, fields);
3536
+ if ("error" in parsedFields) {
3537
+ return parsedFields.error;
3538
+ }
3539
+ const dataset = await ds.createTabularDataset(
3540
+ name,
3541
+ parsedFields.fields
3542
+ );
3543
+ return createSuccess(request.id, dataset.info);
3544
+ }
3545
+ async createPointZDataset(request) {
3546
+ const ds = this.requireDataSource(request.id);
3547
+ if (!ds) {
3548
+ return createFailure(
3549
+ request.id,
3550
+ "not_open",
3551
+ "Datasource has not been opened."
3552
+ );
3553
+ }
3554
+ const parsed = this.parseCreateDatasetParams(request);
3555
+ if ("error" in parsed) {
3556
+ return parsed.error;
3557
+ }
3558
+ const dataset = await ds.createPointZDataset(
3559
+ parsed.params.name,
3560
+ parsed.params.srid,
3561
+ parsed.params.fields
3562
+ );
3563
+ return createSuccess(request.id, dataset.info);
3564
+ }
3565
+ async createLineZDataset(request) {
3566
+ const ds = this.requireDataSource(request.id);
3567
+ if (!ds) {
3568
+ return createFailure(
3569
+ request.id,
3570
+ "not_open",
3571
+ "Datasource has not been opened."
3572
+ );
3573
+ }
3574
+ const parsed = this.parseCreateDatasetParams(request);
3575
+ if ("error" in parsed) {
3576
+ return parsed.error;
3577
+ }
3578
+ const dataset = await ds.createLineZDataset(
3579
+ parsed.params.name,
3580
+ parsed.params.srid,
3581
+ parsed.params.fields
3582
+ );
3583
+ return createSuccess(request.id, dataset.info);
3584
+ }
3585
+ async createRegionZDataset(request) {
3586
+ const ds = this.requireDataSource(request.id);
3587
+ if (!ds) {
3588
+ return createFailure(
3589
+ request.id,
3590
+ "not_open",
3591
+ "Datasource has not been opened."
3592
+ );
3593
+ }
3594
+ const parsed = this.parseCreateDatasetParams(request);
3595
+ if ("error" in parsed) {
3596
+ return parsed.error;
3597
+ }
3598
+ const dataset = await ds.createRegionZDataset(
3599
+ parsed.params.name,
3600
+ parsed.params.srid,
3601
+ parsed.params.fields
3602
+ );
3603
+ return createSuccess(request.id, dataset.info);
3604
+ }
3605
+ async createCadDataset(request) {
3606
+ const ds = this.requireDataSource(request.id);
3607
+ if (!ds) {
3608
+ return createFailure(
3609
+ request.id,
3610
+ "not_open",
3611
+ "Datasource has not been opened."
3612
+ );
3613
+ }
3614
+ const params = asObject(request.params);
3615
+ const name = params.name;
3616
+ if (typeof name !== "string" || name.length === 0) {
3617
+ return createFailure(
3618
+ request.id,
3619
+ "invalid_params",
3620
+ "Dataset name is required."
3621
+ );
3622
+ }
3623
+ const fields = params.fields;
3624
+ const parsedFields = this.parseFieldInfos(request.id, fields);
3625
+ if ("error" in parsedFields) {
3626
+ return parsedFields.error;
3627
+ }
3628
+ const dataset = await ds.createCadDataset(
3629
+ name,
3630
+ parsedFields.fields
3631
+ );
3632
+ return createSuccess(request.id, dataset.info);
3633
+ }
3634
+ async getFields(request) {
3635
+ const dataset = await this.requireDataset(request);
3636
+ if ("error" in dataset) {
3637
+ return dataset.error;
3638
+ }
3639
+ const readable = asReadableDataset(dataset.dataset);
3640
+ const fields = await readable.getFields();
3641
+ return createSuccess(request.id, fields);
3642
+ }
3643
+ async getById(request) {
3644
+ const dataset = await this.requireDataset(request);
3645
+ if ("error" in dataset) {
3646
+ return dataset.error;
3647
+ }
3648
+ const id = asObject(request.params).id;
3649
+ if (typeof id !== "number") {
3650
+ return createFailure(request.id, "invalid_params", "Feature id is required.");
3651
+ }
3652
+ const readable = asReadableDataset(dataset.dataset);
3653
+ const row = await readable.getById(id);
3654
+ return createSuccess(request.id, row);
3655
+ }
3656
+ async listRows(request) {
3657
+ const dataset = await this.requireDataset(request);
3658
+ if ("error" in dataset) {
3659
+ return dataset.error;
3660
+ }
3661
+ const params = asObject(request.params);
3662
+ const options = "options" in params && params.options !== void 0 ? params.options : void 0;
3663
+ const readable = asReadableDataset(dataset.dataset);
3664
+ const rows = await readable.list(options);
3665
+ return createSuccess(request.id, rows);
3666
+ }
3667
+ async insert(request) {
3668
+ const dataset = await this.requireDataset(request);
3669
+ if ("error" in dataset) {
3670
+ return dataset.error;
3671
+ }
3672
+ const writable = asWritableDataset(dataset.dataset);
3673
+ if (typeof writable.insert !== "function") {
3674
+ return createFailure(
3675
+ request.id,
3676
+ "not_writable",
3677
+ `Dataset "${dataset.dataset.info.name}" is not writable.`
3678
+ );
3679
+ }
3680
+ const feature = asObject(request.params).feature;
3681
+ await writable.insert(feature);
3682
+ return createSuccess(request.id, void 0);
3683
+ }
3684
+ async insertMany(request) {
3685
+ const dataset = await this.requireDataset(request);
3686
+ if ("error" in dataset) {
3687
+ return dataset.error;
3688
+ }
3689
+ const writable = asWritableDataset(dataset.dataset);
3690
+ if (typeof writable.insertMany !== "function") {
3691
+ return createFailure(
3692
+ request.id,
3693
+ "not_writable",
3694
+ `Dataset "${dataset.dataset.info.name}" is not writable.`
3695
+ );
3696
+ }
3697
+ const features = asObject(request.params).features;
3698
+ if (!Array.isArray(features)) {
3699
+ return createFailure(
3700
+ request.id,
3701
+ "invalid_params",
3702
+ "`features` must be an array."
3703
+ );
3704
+ }
3705
+ await writable.insertMany(features);
3706
+ return createSuccess(request.id, void 0);
3707
+ }
3708
+ async count(request) {
3709
+ const dataset = await this.requireDataset(request);
3710
+ if ("error" in dataset) {
3711
+ return dataset.error;
3712
+ }
3713
+ const readable = asReadableDataset(dataset.dataset);
3714
+ if (typeof readable.count !== "function") {
3715
+ return createFailure(
3716
+ request.id,
3717
+ "unsupported_operation",
3718
+ `Dataset "${dataset.dataset.info.name}" does not support count.`
3719
+ );
3720
+ }
3721
+ const result = await readable.count();
3722
+ return createSuccess(request.id, result);
3723
+ }
3724
+ async update(request) {
3725
+ const dataset = await this.requireDataset(request);
3726
+ if ("error" in dataset) {
3727
+ return dataset.error;
3728
+ }
3729
+ const writable = asWritableDataset(dataset.dataset);
3730
+ if (typeof writable.update !== "function") {
3731
+ return createFailure(
3732
+ request.id,
3733
+ "not_writable",
3734
+ `Dataset "${dataset.dataset.info.name}" is not writable.`
3735
+ );
3736
+ }
3737
+ const params = asObject(request.params);
3738
+ const feature = params.feature;
3739
+ if (typeof feature !== "object" || feature === null) {
3740
+ return createFailure(
3741
+ request.id,
3742
+ "invalid_params",
3743
+ "`feature` is required."
3744
+ );
3745
+ }
3746
+ const id = feature.id;
3747
+ if (typeof id !== "number") {
3748
+ return createFailure(
3749
+ request.id,
3750
+ "invalid_params",
3751
+ "Feature `id` is required for update."
3752
+ );
3753
+ }
3754
+ const changes = {};
3755
+ if ("geometry" in feature) {
3756
+ changes.geometry = feature.geometry;
3757
+ }
3758
+ if ("attributes" in feature) {
3759
+ changes.attributes = feature.attributes;
3760
+ }
3761
+ await writable.update(id, changes);
3762
+ return createSuccess(request.id, void 0);
3763
+ }
3764
+ async delete(request) {
3765
+ const dataset = await this.requireDataset(request);
3766
+ if ("error" in dataset) {
3767
+ return dataset.error;
3768
+ }
3769
+ const writable = asWritableDataset(dataset.dataset);
3770
+ if (typeof writable.delete !== "function") {
3771
+ return createFailure(
3772
+ request.id,
3773
+ "not_writable",
3774
+ `Dataset "${dataset.dataset.info.name}" is not writable.`
3775
+ );
3776
+ }
3777
+ const id = asObject(request.params).id;
3778
+ if (typeof id !== "number") {
3779
+ return createFailure(request.id, "invalid_params", "Feature id is required.");
3780
+ }
3781
+ await writable.delete(id);
3782
+ return createSuccess(request.id, void 0);
3783
+ }
3784
+ requireDataSource(id) {
3785
+ if (!this.dataSource) {
3786
+ return null;
3787
+ }
3788
+ return this.dataSource;
3789
+ }
3790
+ async requireDataset(request) {
3791
+ const ds = this.requireDataSource(request.id);
3792
+ if (!ds) {
3793
+ return {
3794
+ error: createFailure(
3795
+ request.id,
3796
+ "not_open",
3797
+ "Datasource has not been opened."
3798
+ )
3799
+ };
3800
+ }
3801
+ const datasetName = parseDatasetName(request.params);
3802
+ if (!datasetName) {
3803
+ return {
3804
+ error: createFailure(
3805
+ request.id,
3806
+ "invalid_params",
3807
+ "Dataset name is required."
3808
+ )
3809
+ };
3810
+ }
3811
+ const dataset = await ds.getDataset(datasetName);
3812
+ if (!dataset) {
3813
+ return {
3814
+ error: createFailure(
3815
+ request.id,
3816
+ "dataset_not_found",
3817
+ `Dataset "${datasetName}" does not exist.`
3818
+ )
3819
+ };
3820
+ }
3821
+ return { dataset };
3822
+ }
3823
+ parseCreateDatasetParams(request) {
3824
+ const params = asObject(request.params);
3825
+ const name = params.name;
3826
+ const srid = params.srid;
3827
+ if (typeof name !== "string" || name.length === 0) {
3828
+ return {
3829
+ error: createFailure(
3830
+ request.id,
3831
+ "invalid_params",
3832
+ "Dataset name is required."
3833
+ )
3834
+ };
3835
+ }
3836
+ if (typeof srid !== "number") {
3837
+ return {
3838
+ error: createFailure(request.id, "invalid_params", "Dataset SRID is required.")
3839
+ };
3840
+ }
3841
+ const fields = params.fields;
3842
+ const parsedFields = this.parseFieldInfos(request.id, fields);
3843
+ if ("error" in parsedFields) {
3844
+ return parsedFields;
3845
+ }
3846
+ return {
3847
+ params: {
3848
+ name,
3849
+ srid,
3850
+ ...parsedFields.fields === void 0 ? {} : { fields: parsedFields.fields }
3851
+ }
3852
+ };
3853
+ }
3854
+ parseFieldInfos(id, fields) {
3855
+ if (fields === void 0) {
3856
+ return {};
3857
+ }
3858
+ if (!Array.isArray(fields)) {
3859
+ return {
3860
+ error: createFailure(id, "invalid_params", "`fields` must be an array when provided.")
3861
+ };
3862
+ }
3863
+ const parsed = [];
3864
+ for (const item of fields) {
3865
+ const row = asObject(item);
3866
+ const name = row.name;
3867
+ const fieldType = row.fieldType;
3868
+ if (typeof name !== "string" || name.length === 0) {
3869
+ return {
3870
+ error: createFailure(id, "invalid_params", "Field name is required.")
3871
+ };
3872
+ }
3873
+ if (typeof fieldType !== "string" || !VALID_FIELD_TYPES.has(fieldType)) {
3874
+ return {
3875
+ error: createFailure(id, "invalid_params", `Unsupported field type: ${String(fieldType)}`)
3876
+ };
3877
+ }
3878
+ const info = {
3879
+ name,
3880
+ fieldType
3881
+ };
3882
+ if ("alias" in row && typeof row.alias === "string") {
3883
+ info.alias = row.alias;
3884
+ }
3885
+ if ("required" in row && typeof row.required === "boolean") {
3886
+ info.required = row.required;
3887
+ }
3888
+ if ("nullable" in row && typeof row.nullable === "boolean") {
3889
+ info.nullable = row.nullable;
3890
+ }
3891
+ if ("defaultValue" in row) {
3892
+ info.defaultValue = row.defaultValue;
3893
+ }
3894
+ parsed.push(info);
3895
+ }
3896
+ return { fields: parsed };
3897
+ }
3898
+ };
3899
+ function createWorkerMessageHandler(runtime, host) {
3900
+ return async (event) => {
3901
+ const data = event.data;
3902
+ if (typeof data !== "object" || data === null) {
3903
+ return;
3904
+ }
3905
+ if (!("id" in data) || !("method" in data)) {
3906
+ return;
3907
+ }
3908
+ const response = await runtime.handle(data);
3909
+ host.postMessage(response);
3910
+ };
3911
+ }
3912
+
3913
+ // src/runtime-browser/worker/createBrowserWorkerRuntime.ts
3914
+ function createBrowserWorkerRuntime(options) {
3915
+ const createManagedDataSource = async (params) => {
3916
+ const driver = await options.createDriver();
3917
+ const target = resolveBrowserSqlOpenTarget({
3918
+ ...options.openTarget,
3919
+ ...params.preferOpfs === void 0 ? {} : { preferOpfs: params.preferOpfs }
3920
+ });
3921
+ if (params.binary !== void 0) {
3922
+ if (!("importDatabase" in driver) || typeof driver.importDatabase !== "function") {
3923
+ throw new Error("Current browser SQL driver does not support import.");
3924
+ }
3925
+ await driver.importDatabase(target, params.binary);
3926
+ }
3927
+ await driver.open(target);
3928
+ if (params.binary === void 0) {
3929
+ await UdbxSchemaInitializer.ensureInitialized(driver);
3930
+ }
3931
+ const dataSource = new UdbxDataSource(driver, "browser");
3932
+ if ("exportDatabase" in driver && typeof driver.exportDatabase === "function") {
3933
+ dataSource.__exportDatabase = () => driver.exportDatabase();
3934
+ }
3935
+ return dataSource;
3936
+ };
3937
+ return new BrowserWorkerRuntime({
3938
+ createDataSource: ({ preferOpfs }) => createManagedDataSource({
3939
+ ...preferOpfs === void 0 ? {} : { preferOpfs }
3940
+ }),
3941
+ replaceDataSource: ({ preferOpfs, binary }) => createManagedDataSource({
3942
+ binary: binary instanceof Uint8Array ? binary : new Uint8Array(binary),
3943
+ ...preferOpfs === void 0 ? {} : { preferOpfs }
3944
+ })
3945
+ });
3946
+ }
3947
+
3948
+ // src/runtime-browser/worker/installSqliteWasmWorkerRuntime.ts
3949
+ function installSqliteWasmWorkerRuntime(globalScope, options = {}) {
3950
+ const runtime = createBrowserWorkerRuntime({
3951
+ createDriver: () => new BrowserSqliteDriver(createSqliteWasmAdapter(options.sqlite)),
3952
+ ...options.openTarget === void 0 ? {} : { openTarget: options.openTarget }
3953
+ });
3954
+ const handler = createWorkerMessageHandler(runtime, {
3955
+ postMessage: (message) => globalScope.postMessage(message)
3956
+ });
3957
+ globalScope.addEventListener("message", (event) => {
3958
+ void handler(event);
3959
+ });
3960
+ }
3961
+
3962
+ // src/runtime-browser/worker/index.ts
3963
+ function installBrowserWorkerRuntime(globalScope, runtime) {
3964
+ const handler = createWorkerMessageHandler(runtime, {
3965
+ postMessage: (message) => globalScope.postMessage(message)
3966
+ });
3967
+ globalScope.addEventListener("message", (event) => {
3968
+ void handler(event);
3969
+ });
3970
+ }
3971
+ function installDefaultBrowserWorkerRuntime(globalScope, options) {
3972
+ const runtime = createBrowserWorkerRuntime(options);
3973
+ installBrowserWorkerRuntime(globalScope, runtime);
3974
+ return runtime;
3975
+ }
3976
+
3977
+ // src/runtime-browser/fs/browserFileIO.ts
3978
+ function toArrayBufferView(binary) {
3979
+ const view = new Uint8Array(binary.byteLength);
3980
+ view.set(binary);
3981
+ return view;
3982
+ }
3983
+ function supportsFileSystemAccess(probe = globalThis) {
3984
+ return typeof probe.showOpenFilePicker === "function" && typeof probe.showSaveFilePicker === "function";
3985
+ }
3986
+ async function readBlobAsUint8Array(blob) {
3987
+ return new Uint8Array(await blob.arrayBuffer());
3988
+ }
3989
+ function createUdbxBlob(binary) {
3990
+ return new Blob([toArrayBufferView(binary)], { type: "application/octet-stream" });
3991
+ }
3992
+ async function writeBinaryToFileHandle(handle, binary) {
3993
+ const writable = await handle.createWritable();
3994
+ try {
3995
+ await writable.write(toArrayBufferView(binary));
3996
+ } finally {
3997
+ await writable.close();
3998
+ }
3999
+ }
4000
+ function downloadBinaryFile(binary, filename, env = {
4001
+ document: globalThis.document,
4002
+ URL: globalThis.URL
4003
+ }) {
4004
+ const blob = createUdbxBlob(binary);
4005
+ const url = env.URL.createObjectURL(blob);
4006
+ const anchor = env.document.createElement("a");
4007
+ anchor.href = url;
4008
+ anchor.download = filename;
4009
+ anchor.style.display = "none";
4010
+ env.document.body.appendChild(anchor);
4011
+ anchor.click();
4012
+ env.document.body.removeChild(anchor);
4013
+ env.URL.revokeObjectURL(url);
4014
+ }
4015
+ async function saveBinaryWithPickerOrDownload(binary, filename, options = {}) {
4016
+ const probe = options.pickerProbe ?? globalThis;
4017
+ if (typeof probe.showSaveFilePicker === "function") {
4018
+ const handle = await probe.showSaveFilePicker({
4019
+ suggestedName: filename
4020
+ });
4021
+ await writeBinaryToFileHandle(handle, binary);
4022
+ return "picker";
4023
+ }
4024
+ if (options.downloadEnv === void 0) {
4025
+ downloadBinaryFile(binary, filename);
4026
+ } else {
4027
+ downloadBinaryFile(binary, filename, options.downloadEnv);
4028
+ }
4029
+ return "download";
4030
+ }
4031
+
4032
+ // src/runtime-browser/fs/browserWorkspace.ts
4033
+ async function importUdbxFromFile(dataSource, file, options = {}) {
4034
+ const binary = await readBlobAsUint8Array(file);
4035
+ await dataSource.importDatabase(binary, options);
4036
+ }
4037
+ async function exportUdbxToBlob(dataSource) {
4038
+ const binary = await dataSource.exportDatabase();
4039
+ return createUdbxBlob(binary);
4040
+ }
4041
+ async function saveUdbxToFileHandle(dataSource, handle) {
4042
+ const binary = await dataSource.exportDatabase();
4043
+ await writeBinaryToFileHandle(handle, binary);
4044
+ }
4045
+ async function downloadUdbxFile(dataSource, filename) {
4046
+ const binary = await dataSource.exportDatabase();
4047
+ downloadBinaryFile(binary, filename);
4048
+ }
4049
+ async function saveUdbxWithPickerOrDownload(dataSource, filename) {
4050
+ const binary = await dataSource.exportDatabase();
4051
+ return saveBinaryWithPickerOrDownload(binary, filename);
4052
+ }
4053
+
4054
+ // src/runtime-browser/index.ts
4055
+ function resolveWorker(options) {
4056
+ if (options.workerFactory) {
4057
+ return options.workerFactory();
4058
+ }
4059
+ if (!options.workerUrl) {
4060
+ throw new Error(
4061
+ "Browser worker URL is required. Provide `workerUrl` or `workerFactory`."
4062
+ );
4063
+ }
4064
+ if (typeof Worker === "undefined") {
4065
+ throw new Error("Worker API is unavailable in this environment.");
4066
+ }
4067
+ return new Worker(options.workerUrl, { type: "module" });
4068
+ }
4069
+ async function createBrowserUdbx(options = {}) {
4070
+ const worker = resolveWorker(options);
4071
+ const transport = new BrowserWorkerTransport(worker);
4072
+ const connectOptions = options.preferOpfs === void 0 ? {} : { preferOpfs: options.preferOpfs };
4073
+ const client = await BrowserUdbxClient.connect(transport, {
4074
+ ...connectOptions
4075
+ });
4076
+ return client;
4077
+ }
4078
+ // Annotate the CommonJS export names for ESM import in node:
4079
+ 0 && (module.exports = {
4080
+ BrowserSqliteDriver,
4081
+ createBrowserUdbx,
4082
+ createBrowserWorkerRuntime,
4083
+ createSqliteWasmAdapter,
4084
+ createUdbxBlob,
4085
+ downloadBinaryFile,
4086
+ downloadUdbxFile,
4087
+ exportUdbxToBlob,
4088
+ importUdbxFromFile,
4089
+ installBrowserWorkerRuntime,
4090
+ installDefaultBrowserWorkerRuntime,
4091
+ installSqliteWasmWorkerRuntime,
4092
+ isOpfsAvailable,
4093
+ readBlobAsUint8Array,
4094
+ resolveBrowserSqlOpenTarget,
4095
+ saveBinaryWithPickerOrDownload,
4096
+ saveUdbxToFileHandle,
4097
+ saveUdbxWithPickerOrDownload,
4098
+ supportsFileSystemAccess,
4099
+ writeBinaryToFileHandle
4100
+ });
4101
+ //# sourceMappingURL=index.cjs.map