@centia-io/sdk 0.0.28 → 0.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +340 -58
- package/dist/centia-io-sdk.cjs +837 -7
- package/dist/centia-io-sdk.d.cts +131 -3
- package/dist/centia-io-sdk.d.cts.map +1 -1
- package/dist/centia-io-sdk.d.ts +131 -3
- package/dist/centia-io-sdk.d.ts.map +1 -1
- package/dist/centia-io-sdk.js +835 -7
- package/dist/centia-io-sdk.js.map +1 -1
- package/dist/centia-io-sdk.umd.js +836 -6
- package/package.json +2 -4
package/dist/centia-io-sdk.js
CHANGED
|
@@ -198,6 +198,9 @@ var Gc2Service = class {
|
|
|
198
198
|
isPasswordFlowOptions(options) {
|
|
199
199
|
return "username" in options;
|
|
200
200
|
}
|
|
201
|
+
isSignUpOptions(options) {
|
|
202
|
+
return "parentDb" in options;
|
|
203
|
+
}
|
|
201
204
|
buildUrl(path) {
|
|
202
205
|
if (path.startsWith("http://") || path.startsWith("https://")) return path;
|
|
203
206
|
return `${this.host}${path}`;
|
|
@@ -262,6 +265,15 @@ var Gc2Service = class {
|
|
|
262
265
|
if (this.options.scope) params.set("scope", this.options.scope);
|
|
263
266
|
return `${base}?${params.toString()}`;
|
|
264
267
|
}
|
|
268
|
+
getSignUpURL() {
|
|
269
|
+
if (!this.isSignUpOptions(this.options)) throw new Error("CodeFlow options required for this operation");
|
|
270
|
+
const base = this.options.authUri ?? `${this.host}/signup/`;
|
|
271
|
+
const params = new URLSearchParams();
|
|
272
|
+
params.set("client_id", this.options.clientId);
|
|
273
|
+
params.set("parentdb", this.options.parentDb);
|
|
274
|
+
params.set("redirect_uri", this.options.redirectUri);
|
|
275
|
+
return `${base}?${params.toString()}`;
|
|
276
|
+
}
|
|
265
277
|
async getAuthorizationCodeToken(code, codeVerifier) {
|
|
266
278
|
let redirectUri;
|
|
267
279
|
if (this.isCodeFlowOptions(this.options)) redirectUri = this.options.redirectUri;
|
|
@@ -501,17 +513,21 @@ var Users = class {
|
|
|
501
513
|
var Ws = class {
|
|
502
514
|
constructor(options) {
|
|
503
515
|
this.options = options;
|
|
516
|
+
this.options.wsClient = this.options?.wsClient ?? WebSocket;
|
|
504
517
|
}
|
|
505
518
|
connect() {
|
|
506
519
|
const me = this;
|
|
507
520
|
const { accessToken } = getTokens();
|
|
508
521
|
const connect = () => {
|
|
509
|
-
|
|
522
|
+
let queryString = `?token=` + accessToken;
|
|
523
|
+
if (this.options?.rel) queryString = queryString + `&rel=` + this.options.rel;
|
|
524
|
+
const WSClass = this.options.wsClient;
|
|
525
|
+
const ws = new WSClass(this.options.host + `/` + queryString);
|
|
510
526
|
ws.onopen = function() {
|
|
511
527
|
console.log("WebSocket connected!");
|
|
512
528
|
};
|
|
513
529
|
ws.onmessage = function(event) {
|
|
514
|
-
me.options
|
|
530
|
+
me.options.callBack(event.data);
|
|
515
531
|
};
|
|
516
532
|
ws.onclose = function(event) {
|
|
517
533
|
if (accessToken !== "") {
|
|
@@ -555,23 +571,835 @@ var Tables = class {
|
|
|
555
571
|
|
|
556
572
|
//#endregion
|
|
557
573
|
//#region src/Api.ts
|
|
558
|
-
|
|
574
|
+
function isPlainObject$1(v) {
|
|
575
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
576
|
+
}
|
|
577
|
+
function validateParamsForMethod(method, params) {
|
|
578
|
+
if (Array.isArray(params)) {
|
|
579
|
+
const badIndex = params.findIndex((p) => !isPlainObject$1(p));
|
|
580
|
+
if (badIndex !== -1) throw new TypeError(`createApi: Invalid argument at index ${badIndex} for RPC method "${method}". Expected a plain object.`);
|
|
581
|
+
return params;
|
|
582
|
+
}
|
|
583
|
+
if (params === void 0) return {};
|
|
584
|
+
if (!isPlainObject$1(params)) throw new TypeError(`createApi: Invalid argument for RPC method "${method}". Expected a plain object.`);
|
|
585
|
+
return params;
|
|
586
|
+
}
|
|
587
|
+
function extractDataFromResponse(method, res) {
|
|
588
|
+
if (!res || typeof res !== "object") throw new TypeError(`createApi: Invalid RPC response for method "${method}". Expected an object.`);
|
|
589
|
+
if (res.jsonrpc !== "2.0") throw new TypeError(`createApi: Invalid RPC response for method "${method}". Missing or invalid jsonrpc version.`);
|
|
590
|
+
const err = res.error;
|
|
591
|
+
if (err !== void 0) {
|
|
592
|
+
if (!err || typeof err !== "object") throw new TypeError(`createApi: Invalid RPC error for method "${method}". Expected 'error' to be an object.`);
|
|
593
|
+
const code = err.code;
|
|
594
|
+
const message = err.message;
|
|
595
|
+
const data$1 = err.data;
|
|
596
|
+
const codeIsNum = typeof code === "number" && Number.isFinite(code);
|
|
597
|
+
const details = typeof message === "string" && message.length > 0 ? message : "Unknown error";
|
|
598
|
+
const e = /* @__PURE__ */ new Error(`createApi: RPC error for method "${method}"${codeIsNum ? ` (${code})` : ""}: ${details}`);
|
|
599
|
+
e.code = code;
|
|
600
|
+
if (data$1 !== void 0) e.data = data$1;
|
|
601
|
+
e.method = method;
|
|
602
|
+
e.name = "JsonRpcError";
|
|
603
|
+
throw e;
|
|
604
|
+
}
|
|
605
|
+
const result = res.result;
|
|
606
|
+
if (!result || typeof result !== "object") throw new TypeError(`createApi: Invalid RPC response for method "${method}". Missing result object.`);
|
|
607
|
+
const data = result.data;
|
|
608
|
+
if (!Array.isArray(data)) throw new TypeError(`createApi: Invalid RPC response for method "${method}". Expected result.data to be an array.`);
|
|
609
|
+
return data;
|
|
610
|
+
}
|
|
611
|
+
async function dispatch(name, paramsLike) {
|
|
612
|
+
if (typeof name !== "string" || name.length === 0) throw new TypeError("createApi: RPC method name must be a non-empty string.");
|
|
613
|
+
const params = validateParamsForMethod(String(name), paramsLike);
|
|
559
614
|
const rpc = new Rpc();
|
|
560
615
|
const request = {
|
|
561
616
|
jsonrpc: "2.0",
|
|
562
617
|
method: name,
|
|
563
618
|
id: 1,
|
|
564
|
-
params
|
|
619
|
+
params
|
|
565
620
|
};
|
|
566
|
-
|
|
621
|
+
const res = await rpc.call(request);
|
|
622
|
+
return extractDataFromResponse(String(name), res);
|
|
567
623
|
}
|
|
568
624
|
function createApi() {
|
|
569
625
|
return new Proxy({}, { get(_target, prop) {
|
|
570
626
|
if (typeof prop !== "string") return void 0;
|
|
571
|
-
return (...args) =>
|
|
627
|
+
return (...args) => {
|
|
628
|
+
return dispatch(prop, args.length === 0 ? {} : args.length === 1 ? args[0] : args);
|
|
629
|
+
};
|
|
572
630
|
} });
|
|
573
631
|
}
|
|
574
632
|
|
|
575
633
|
//#endregion
|
|
576
|
-
|
|
634
|
+
//#region src/SignUp.ts
|
|
635
|
+
var SignUp = class {
|
|
636
|
+
constructor(options) {
|
|
637
|
+
this.options = options;
|
|
638
|
+
this.service = new Gc2Service(options);
|
|
639
|
+
}
|
|
640
|
+
async signUp() {
|
|
641
|
+
window.location = this.service.getSignUpURL();
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
//#endregion
|
|
646
|
+
//#region src/SqlBuilder.ts
|
|
647
|
+
function findTable(schema, name) {
|
|
648
|
+
const tbl = schema.tables.find((t) => t.name === name);
|
|
649
|
+
if (!tbl) throw new Error(`Table not found in schema: ${name}`);
|
|
650
|
+
return tbl;
|
|
651
|
+
}
|
|
652
|
+
function findColumn(table, name) {
|
|
653
|
+
const col = table.columns.find((c) => c.name === name);
|
|
654
|
+
if (!col) throw new Error(`Column not found in table ${table.name}: ${name}`);
|
|
655
|
+
return col;
|
|
656
|
+
}
|
|
657
|
+
function getPrimaryKeyColumns(table) {
|
|
658
|
+
const pk = (table.constraints || []).find((c) => c.constraint === "primary" && Array.isArray(c.columns) && c.columns.length > 0);
|
|
659
|
+
return pk ? pk.columns.map(String) : [];
|
|
660
|
+
}
|
|
661
|
+
function typeNameToHint(typname, isArray) {
|
|
662
|
+
if (!typname) return void 0;
|
|
663
|
+
const base = typname;
|
|
664
|
+
return isArray ? base + "[]" : base;
|
|
665
|
+
}
|
|
666
|
+
function addTypeHintForParam(typeHints, paramName, col, value) {
|
|
667
|
+
let isArr = col._is_array;
|
|
668
|
+
if (Array.isArray(value)) if (isArrayShapedGeomTypename(col._typname)) isArr = value.every(Array.isArray);
|
|
669
|
+
else isArr = true;
|
|
670
|
+
const hint = typeNameToHint(col._typname, isArr);
|
|
671
|
+
if (hint) typeHints[paramName] = hint;
|
|
672
|
+
}
|
|
673
|
+
function expectedScalarKind(typname) {
|
|
674
|
+
const t = typname.toLowerCase();
|
|
675
|
+
if (t === "int2" || t === "int4" || t === "int8" || t === "float4" || t === "float8") return "number";
|
|
676
|
+
if (t === "numeric" || t === "decimal") return "string";
|
|
677
|
+
if (t === "varchar" || t === "text" || t === "bpchar" || t === "char" || t === "date" || t === "time" || t === "timetz" || t === "timestamp" || t === "timestamptz") return "string";
|
|
678
|
+
if (t === "bool") return "boolean";
|
|
679
|
+
if (t === "json" || t === "jsonb") return "json";
|
|
680
|
+
if (t === "int4range" || t === "int8range" || t === "numrange" || t === "tsrange" || t === "tstzrange" || t === "daterange") return "range";
|
|
681
|
+
if (t === "interval") return "interval";
|
|
682
|
+
if (t === "point" || t === "line" || t === "lseg" || t === "box" || t === "path" || t === "polygon" || t === "circle") return "geom";
|
|
683
|
+
return "unknown";
|
|
684
|
+
}
|
|
685
|
+
function isArrayShapedGeomTypename(typname) {
|
|
686
|
+
const t = typname.toLowerCase();
|
|
687
|
+
return t === "path" || t === "polygon";
|
|
688
|
+
}
|
|
689
|
+
function isArrayShapedGeomScalarValue(col, value) {
|
|
690
|
+
if (!Array.isArray(value)) return false;
|
|
691
|
+
if (!isArrayShapedGeomTypename(col._typname)) return false;
|
|
692
|
+
return !value.every(Array.isArray);
|
|
693
|
+
}
|
|
694
|
+
function expectedRangeInnerKind(typname) {
|
|
695
|
+
const t = typname.toLowerCase();
|
|
696
|
+
if (t === "int4range" || t === "int8range") return "number";
|
|
697
|
+
if (t === "numrange" || t === "tsrange" || t === "tstzrange" || t === "daterange") return "string";
|
|
698
|
+
return "unknown";
|
|
699
|
+
}
|
|
700
|
+
function isPlainObject(v) {
|
|
701
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
702
|
+
}
|
|
703
|
+
function isFiniteNumber(n) {
|
|
704
|
+
return typeof n === "number" && Number.isFinite(n);
|
|
705
|
+
}
|
|
706
|
+
function isPointLike(v) {
|
|
707
|
+
if (!isPlainObject(v)) return false;
|
|
708
|
+
const x = v.x;
|
|
709
|
+
const y = v.y;
|
|
710
|
+
return isFiniteNumber(x) && isFiniteNumber(y);
|
|
711
|
+
}
|
|
712
|
+
function validateGeometryForColumn(col, value, context) {
|
|
713
|
+
const t = col._typname.toLowerCase();
|
|
714
|
+
if (t === "point") {
|
|
715
|
+
if (!isPointLike(value)) throw new Error(`Invalid value for ${context}. Expected Point { x: number, y: number }`);
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
if (t === "line") {
|
|
719
|
+
if (!isPlainObject(value)) throw new Error(`Invalid value for ${context}. Expected Line { A: number, B: number, C: number }`);
|
|
720
|
+
const A = value.A, B = value.B, C = value.C;
|
|
721
|
+
if (!isFiniteNumber(A) || !isFiniteNumber(B) || !isFiniteNumber(C)) throw new Error(`Invalid Line for ${context}. A, B, C must be finite numbers`);
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
if (t === "lseg" || t === "box") {
|
|
725
|
+
if (!isPlainObject(value)) throw new Error(`Invalid value for ${context}. Expected ${t.toUpperCase()} { start: Point, end: Point }`);
|
|
726
|
+
const start = value.start;
|
|
727
|
+
const end = value.end;
|
|
728
|
+
if (!isPointLike(start) || !isPointLike(end)) throw new Error(`Invalid ${t} for ${context}. 'start' and 'end' must be Point { x, y }`);
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
if (t === "path") {
|
|
732
|
+
if (!Array.isArray(value) || value.length < 1) throw new Error(`Invalid value for ${context}. Expected Path [isClosed: boolean, ...points: Point[]]`);
|
|
733
|
+
const [isClosed, ...points] = value;
|
|
734
|
+
if (typeof isClosed !== "boolean") throw new Error(`Invalid Path for ${context}. First element must be boolean (isClosed)`);
|
|
735
|
+
for (let i = 0; i < points.length; i++) if (!isPointLike(points[i])) throw new Error(`Invalid Path for ${context}. Element at index ${i + 1} must be Point { x, y }`);
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
if (t === "polygon") {
|
|
739
|
+
if (!Array.isArray(value)) throw new Error(`Invalid value for ${context}. Expected Polygon as Point[]`);
|
|
740
|
+
for (let i = 0; i < value.length; i++) if (!isPointLike(value[i])) throw new Error(`Invalid Polygon for ${context}. Element at index ${i} must be Point { x, y }`);
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
if (t === "circle") {
|
|
744
|
+
if (!isPlainObject(value)) throw new Error(`Invalid value for ${context}. Expected Circle { center: Point, radius: number }`);
|
|
745
|
+
const center = value.center;
|
|
746
|
+
const radius = value.radius;
|
|
747
|
+
if (!isPointLike(center) || !isFiniteNumber(radius)) throw new Error(`Invalid Circle for ${context}. 'center' must be Point and 'radius' must be finite number`);
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
function validateRangeForColumn(col, value, context) {
|
|
752
|
+
if (!isPlainObject(value)) throw new Error(`Invalid value for ${context}. Expected a range object for type ${col._typname}`);
|
|
753
|
+
const r = value;
|
|
754
|
+
const hasLower = Object.prototype.hasOwnProperty.call(r, "lower");
|
|
755
|
+
const hasUpper = Object.prototype.hasOwnProperty.call(r, "upper");
|
|
756
|
+
const hasLi = Object.prototype.hasOwnProperty.call(r, "lowerInclusive");
|
|
757
|
+
const hasUi = Object.prototype.hasOwnProperty.call(r, "upperInclusive");
|
|
758
|
+
if (!hasLower || !hasUpper || !hasLi || !hasUi) throw new Error(`Invalid range for ${context}. Required properties: lower, upper, lowerInclusive, upperInclusive`);
|
|
759
|
+
if (typeof r.lowerInclusive !== "boolean" || typeof r.upperInclusive !== "boolean") throw new Error(`Invalid range for ${context}. lowerInclusive and upperInclusive must be boolean`);
|
|
760
|
+
const inner = expectedRangeInnerKind(col._typname);
|
|
761
|
+
if (inner === "number") {
|
|
762
|
+
if (typeof r.lower !== "number" || !Number.isFinite(r.lower)) throw new Error(`Invalid range.lower for ${context}. Expected number for type ${col._typname}`);
|
|
763
|
+
if (typeof r.upper !== "number" || !Number.isFinite(r.upper)) throw new Error(`Invalid range.upper for ${context}. Expected number for type ${col._typname}`);
|
|
764
|
+
} else if (inner === "string") {
|
|
765
|
+
if (typeof r.lower !== "string") throw new Error(`Invalid range.lower for ${context}. Expected string for type ${col._typname}`);
|
|
766
|
+
if (typeof r.upper !== "string") throw new Error(`Invalid range.upper for ${context}. Expected string for type ${col._typname}`);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
function validateIntervalForColumn(col, value, context) {
|
|
770
|
+
if (!isPlainObject(value)) throw new Error(`Invalid value for ${context}. Expected an interval object for type ${col._typname}`);
|
|
771
|
+
const v = value;
|
|
772
|
+
for (const k of [
|
|
773
|
+
"y",
|
|
774
|
+
"m",
|
|
775
|
+
"d",
|
|
776
|
+
"h",
|
|
777
|
+
"i",
|
|
778
|
+
"s"
|
|
779
|
+
]) {
|
|
780
|
+
if (!Object.prototype.hasOwnProperty.call(v, k)) throw new Error(`Invalid interval for ${context}. Missing property '${k}'`);
|
|
781
|
+
const num = v[k];
|
|
782
|
+
if (typeof num !== "number" || !Number.isFinite(num)) throw new Error(`Invalid interval.${String(k)} for ${context}. Expected finite number`);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
function isValidJsonLike(value) {
|
|
786
|
+
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "object" && value !== null;
|
|
787
|
+
}
|
|
788
|
+
function validateScalarForColumn(col, value, context) {
|
|
789
|
+
const kind = expectedScalarKind(col._typname);
|
|
790
|
+
if (kind === "unknown") return;
|
|
791
|
+
if (kind === "number") {
|
|
792
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`Invalid value for ${context}. Expected number for type ${col._typname}, got ${typeof value}`);
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
if (kind === "string") {
|
|
796
|
+
if (typeof value !== "string") throw new Error(`Invalid value for ${context}. Expected string for type ${col._typname}, got ${typeof value}`);
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
if (kind === "boolean") {
|
|
800
|
+
if (typeof value !== "boolean") throw new Error(`Invalid value for ${context}. Expected boolean for type ${col._typname}, got ${typeof value}`);
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
if (kind === "json") {
|
|
804
|
+
if (!isValidJsonLike(value)) throw new Error(`Invalid value for ${context}. Expected JSON-compatible value for type ${col._typname}`);
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
if (kind === "range") {
|
|
808
|
+
validateRangeForColumn(col, value, context);
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
if (kind === "interval") {
|
|
812
|
+
validateIntervalForColumn(col, value, context);
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
if (kind === "geom") {
|
|
816
|
+
validateGeometryForColumn(col, value, context);
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
function validateComparisonValue(col, key, value, op) {
|
|
821
|
+
if (value === void 0 || value === null) throw new Error(`Operator ${op} on column ${key} requires a non-null value. Use isnull/notnull for null checks.`);
|
|
822
|
+
if (col._is_array) {
|
|
823
|
+
if (!Array.isArray(value)) throw new Error(`Operator ${op} on array column ${key} requires an array value`);
|
|
824
|
+
for (const [i, v] of value.entries()) validateScalarForColumn(col, v, `column ${key}[${i}]`);
|
|
825
|
+
} else {
|
|
826
|
+
if (Array.isArray(value) && !isArrayShapedGeomScalarValue(col, value)) throw new Error(`Operator ${op} on scalar column ${key} cannot accept an array value`);
|
|
827
|
+
validateScalarForColumn(col, value, `column ${key}`);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
function validateInArrayValues(col, key, values, op) {
|
|
831
|
+
if (!col._is_array) {
|
|
832
|
+
let idx = 0;
|
|
833
|
+
for (const v of values) {
|
|
834
|
+
validateScalarForColumn(col, v, `column ${key} (element ${idx})`);
|
|
835
|
+
idx++;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
function findJoinOn(base, target) {
|
|
840
|
+
const bc = base.constraints || [];
|
|
841
|
+
const tc = target.constraints || [];
|
|
842
|
+
for (const c of bc) if (c.constraint === "foreign" && c.referenced_table === target.name && c.columns?.length && c.referenced_columns?.length && c.columns.length === c.referenced_columns.length) return c.columns.map((col, i) => ({
|
|
843
|
+
left: String(col),
|
|
844
|
+
right: String(c.referenced_columns[i])
|
|
845
|
+
}));
|
|
846
|
+
for (const c of tc) if (c.constraint === "foreign" && c.referenced_table === base.name && c.columns?.length && c.referenced_columns?.length && c.columns.length === c.referenced_columns.length) return c.referenced_columns.map((rcol, i) => ({
|
|
847
|
+
left: String(rcol),
|
|
848
|
+
right: String(c.columns[i])
|
|
849
|
+
}));
|
|
850
|
+
return null;
|
|
851
|
+
}
|
|
852
|
+
var TableQueryImpl = class {
|
|
853
|
+
constructor(schema, tableName) {
|
|
854
|
+
this.schema = schema;
|
|
855
|
+
this.table = findTable(schema, tableName);
|
|
856
|
+
}
|
|
857
|
+
select(cols) {
|
|
858
|
+
const table = this.table;
|
|
859
|
+
const schema = this.schema;
|
|
860
|
+
const selected = cols && cols.length ? cols : ["*"];
|
|
861
|
+
const state = {
|
|
862
|
+
table,
|
|
863
|
+
selected,
|
|
864
|
+
where: {},
|
|
865
|
+
orWhereGroups: [],
|
|
866
|
+
andOps: [],
|
|
867
|
+
orOpGroups: [],
|
|
868
|
+
order: [],
|
|
869
|
+
limit: void 0,
|
|
870
|
+
offset: void 0,
|
|
871
|
+
joins: [],
|
|
872
|
+
joinSelections: []
|
|
873
|
+
};
|
|
874
|
+
return new class {
|
|
875
|
+
constructor() {
|
|
876
|
+
this.s = state;
|
|
877
|
+
this.toSql = () => {
|
|
878
|
+
const params = {};
|
|
879
|
+
const type_hints = {};
|
|
880
|
+
let p = 0;
|
|
881
|
+
const parts = [];
|
|
882
|
+
const selectParts = [];
|
|
883
|
+
if (selected.length === 1 && selected[0] === "*") selectParts.push(`"${table.name}".*`);
|
|
884
|
+
else {
|
|
885
|
+
const sel = selected;
|
|
886
|
+
for (const c of sel) findColumn(table, String(c));
|
|
887
|
+
selectParts.push(sel.map((c) => `"${table.name}"."${c}"`).join(", "));
|
|
888
|
+
}
|
|
889
|
+
for (const js of state.joinSelections) if (js.selected.length === 1 && js.selected[0] === "*") selectParts.push(`"${js.target.name}".*`);
|
|
890
|
+
else {
|
|
891
|
+
const cols$1 = js.selected;
|
|
892
|
+
for (const c of cols$1) findColumn(js.target, String(c));
|
|
893
|
+
selectParts.push(cols$1.map((c) => `"${js.target.name}"."${c}"`).join(", "));
|
|
894
|
+
}
|
|
895
|
+
parts.push(`select ${selectParts.join(", ")} from "${schema.name}"."${table.name}"`);
|
|
896
|
+
for (const j of state.joins) {
|
|
897
|
+
const onExpr = j.on.map((p$1) => `"${table.name}"."${p$1.left}" = "${j.target.name}"."${p$1.right}"`).join(" and ");
|
|
898
|
+
parts.push(`${j.type} join "${schema.name}"."${j.target.name}" on ${onExpr}`);
|
|
899
|
+
}
|
|
900
|
+
const andParts = [];
|
|
901
|
+
for (const key in state.where) {
|
|
902
|
+
const value = state.where[key];
|
|
903
|
+
const col = findColumn(table, key);
|
|
904
|
+
p += 1;
|
|
905
|
+
const paramName = `${table.name}_${key}_${p}`;
|
|
906
|
+
if (value === null) {
|
|
907
|
+
if (!col.is_nullable) throw new Error(`Column ${table.name}.${key} is not nullable; cannot compare to null`);
|
|
908
|
+
andParts.push(`"${table.name}"."${key}" is null`);
|
|
909
|
+
} else if (Array.isArray(value) && !isArrayShapedGeomScalarValue(col, value)) {
|
|
910
|
+
validateInArrayValues(col, key, value, "in");
|
|
911
|
+
andParts.push(`"${table.name}"."${key}" = ANY(:${paramName})`);
|
|
912
|
+
params[paramName] = value;
|
|
913
|
+
addTypeHintForParam(type_hints, paramName, col, value);
|
|
914
|
+
} else {
|
|
915
|
+
validateScalarForColumn(col, value, `column ${key}`);
|
|
916
|
+
andParts.push(`"${table.name}"."${key}" = :${paramName}`);
|
|
917
|
+
params[paramName] = value;
|
|
918
|
+
addTypeHintForParam(type_hints, paramName, col, value);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
for (const pred of state.andOps) {
|
|
922
|
+
const key = String(pred.col);
|
|
923
|
+
const op = String(pred.op);
|
|
924
|
+
const col = findColumn(table, key);
|
|
925
|
+
const qualified = `"${table.name}"."${key}"`;
|
|
926
|
+
if (op === "isnull" || op === "notnull") {
|
|
927
|
+
if (pred.value !== void 0) throw new Error(`Operator ${op} does not take a value for column ${key}`);
|
|
928
|
+
andParts.push(`${qualified} is ${op === "isnull" ? "null" : "not null"}`);
|
|
929
|
+
} else if (op === "in" || op === "notin") {
|
|
930
|
+
const val = pred.value;
|
|
931
|
+
if (!Array.isArray(val)) throw new Error(`Operator ${op} requires an array value for column ${key}`);
|
|
932
|
+
validateInArrayValues(col, key, val, op);
|
|
933
|
+
p += 1;
|
|
934
|
+
const paramName = `${table.name}_${key}_${p}`;
|
|
935
|
+
andParts.push(`${qualified} ${op === "in" ? "= ANY" : "!= ALL"}(:${paramName})`);
|
|
936
|
+
params[paramName] = val;
|
|
937
|
+
addTypeHintForParam(type_hints, paramName, col, val);
|
|
938
|
+
} else if (op === "like" || op === "ilike" || op === "notlike" || op === "notilike") {
|
|
939
|
+
const val = pred.value;
|
|
940
|
+
if (typeof val !== "string") throw new Error(`Operator ${op} requires a string value for column ${key}`);
|
|
941
|
+
p += 1;
|
|
942
|
+
const paramName = `${table.name}_${key}_${p}`;
|
|
943
|
+
const sqlOp = op === "like" ? "like" : op === "ilike" ? "ilike" : op === "notlike" ? "not like" : "not ilike";
|
|
944
|
+
andParts.push(`${qualified} ${sqlOp} :${paramName}`);
|
|
945
|
+
params[paramName] = val;
|
|
946
|
+
addTypeHintForParam(type_hints, paramName, col, val);
|
|
947
|
+
} else {
|
|
948
|
+
const val = pred.value;
|
|
949
|
+
if (val === void 0) throw new Error(`Operator ${op} requires a value for column ${key}`);
|
|
950
|
+
validateComparisonValue(col, key, val, op);
|
|
951
|
+
p += 1;
|
|
952
|
+
const paramName = `${table.name}_${key}_${p}`;
|
|
953
|
+
if (op === "=") andParts.push(`${qualified} = :${paramName}`);
|
|
954
|
+
else if (op === "!=") andParts.push(`${qualified} <> :${paramName}`);
|
|
955
|
+
else if (op === "<" || op === "<=" || op === ">" || op === ">=") andParts.push(`${qualified} ${op} :${paramName}`);
|
|
956
|
+
else throw new Error(`Unsupported operator: ${op}`);
|
|
957
|
+
params[paramName] = val;
|
|
958
|
+
addTypeHintForParam(type_hints, paramName, col, val);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
const orGroupSql = [];
|
|
962
|
+
for (const group of state.orWhereGroups) {
|
|
963
|
+
const orParts = [];
|
|
964
|
+
for (const key in group) {
|
|
965
|
+
const value = group[key];
|
|
966
|
+
const col = findColumn(table, key);
|
|
967
|
+
p += 1;
|
|
968
|
+
const paramName = `${table.name}_${key}_${p}`;
|
|
969
|
+
if (value === null) {
|
|
970
|
+
if (!col.is_nullable) throw new Error(`Column ${table.name}.${key} is not nullable; cannot compare to null`);
|
|
971
|
+
orParts.push(`"${table.name}"."${key}" is null`);
|
|
972
|
+
} else if (Array.isArray(value) && !isArrayShapedGeomScalarValue(col, value)) {
|
|
973
|
+
validateInArrayValues(col, key, value, "in");
|
|
974
|
+
orParts.push(`"${table.name}"."${key}" = ANY(:${paramName})`);
|
|
975
|
+
params[paramName] = value;
|
|
976
|
+
addTypeHintForParam(type_hints, paramName, col, value);
|
|
977
|
+
} else {
|
|
978
|
+
validateScalarForColumn(col, value, `column ${key}`);
|
|
979
|
+
orParts.push(`"${table.name}"."${key}" = :${paramName}`);
|
|
980
|
+
params[paramName] = value;
|
|
981
|
+
addTypeHintForParam(type_hints, paramName, col, value);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
if (orParts.length) orGroupSql.push("(" + orParts.join(" or ") + ")");
|
|
985
|
+
}
|
|
986
|
+
for (const group of state.orOpGroups) {
|
|
987
|
+
const orParts = [];
|
|
988
|
+
for (const pred of group) {
|
|
989
|
+
const key = String(pred.col);
|
|
990
|
+
const op = String(pred.op);
|
|
991
|
+
const col = findColumn(table, key);
|
|
992
|
+
const qualified = `"${table.name}"."${key}"`;
|
|
993
|
+
if (op === "isnull" || op === "notnull") {
|
|
994
|
+
if (pred.value !== void 0) throw new Error(`Operator ${op} does not take a value for column ${key}`);
|
|
995
|
+
orParts.push(`${qualified} is ${op === "isnull" ? "null" : "not null"}`);
|
|
996
|
+
} else if (op === "in" || op === "notin") {
|
|
997
|
+
const val = pred.value;
|
|
998
|
+
if (!Array.isArray(val)) throw new Error(`Operator ${op} requires an array value for column ${key}`);
|
|
999
|
+
validateInArrayValues(col, key, val, op);
|
|
1000
|
+
p += 1;
|
|
1001
|
+
const paramName = `${table.name}_${key}_${p}`;
|
|
1002
|
+
orParts.push(`${qualified} ${op === "in" ? "= ANY" : "!= ALL"}(:${paramName})`);
|
|
1003
|
+
params[paramName] = val;
|
|
1004
|
+
addTypeHintForParam(type_hints, paramName, col, val);
|
|
1005
|
+
} else if (op === "like" || op === "ilike" || op === "notlike" || op === "notilike") {
|
|
1006
|
+
const val = pred.value;
|
|
1007
|
+
if (typeof val !== "string") throw new Error(`Operator ${op} requires a string value for column ${key}`);
|
|
1008
|
+
p += 1;
|
|
1009
|
+
const paramName = `${table.name}_${key}_${p}`;
|
|
1010
|
+
const sqlOp = op === "like" ? "like" : op === "ilike" ? "ilike" : op === "notlike" ? "not like" : "not ilike";
|
|
1011
|
+
orParts.push(`${qualified} ${sqlOp} :${paramName}`);
|
|
1012
|
+
params[paramName] = val;
|
|
1013
|
+
addTypeHintForParam(type_hints, paramName, col, val);
|
|
1014
|
+
} else {
|
|
1015
|
+
const val = pred.value;
|
|
1016
|
+
if (val === void 0) throw new Error(`Operator ${op} requires a value for column ${key}`);
|
|
1017
|
+
validateComparisonValue(col, key, val, op);
|
|
1018
|
+
p += 1;
|
|
1019
|
+
const paramName = `${table.name}_${key}_${p}`;
|
|
1020
|
+
if (op === "=") orParts.push(`${qualified} = :${paramName}`);
|
|
1021
|
+
else if (op === "!=") orParts.push(`${qualified} <> :${paramName}`);
|
|
1022
|
+
else if (op === "<" || op === "<=" || op === ">" || op === ">=") orParts.push(`${qualified} ${op} :${paramName}`);
|
|
1023
|
+
else throw new Error(`Unsupported operator: ${op}`);
|
|
1024
|
+
params[paramName] = val;
|
|
1025
|
+
addTypeHintForParam(type_hints, paramName, col, val);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
if (orParts.length) orGroupSql.push("(" + orParts.join(" or ") + ")");
|
|
1029
|
+
}
|
|
1030
|
+
if (orGroupSql.length) {
|
|
1031
|
+
const andSql = andParts.length ? `(${andParts.join(" and ")})` : "";
|
|
1032
|
+
const orSql = orGroupSql.join(" or ");
|
|
1033
|
+
const full = andSql ? `${andSql} or ${orSql}` : orSql;
|
|
1034
|
+
if (full.trim().length) parts.push("where " + full);
|
|
1035
|
+
} else if (andParts.length) parts.push("where " + andParts.join(" and "));
|
|
1036
|
+
if (state.order.length) {
|
|
1037
|
+
const orders = [];
|
|
1038
|
+
for (const o of state.order) orders.push(`"${table.name}"."${o.col}" ${o.dir}`);
|
|
1039
|
+
parts.push("order by " + orders.join(", "));
|
|
1040
|
+
}
|
|
1041
|
+
if (state.limit !== void 0) parts.push("limit " + state.limit);
|
|
1042
|
+
if (state.offset !== void 0) parts.push("offset " + state.offset);
|
|
1043
|
+
return {
|
|
1044
|
+
q: parts.join(" "),
|
|
1045
|
+
params,
|
|
1046
|
+
type_hints: Object.keys(type_hints).length ? type_hints : void 0
|
|
1047
|
+
};
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
selectFrom(tableName, cols$1) {
|
|
1051
|
+
const jtName = String(tableName);
|
|
1052
|
+
const join = this.s.joins.find((j) => j.target.name === jtName);
|
|
1053
|
+
if (!join) throw new Error(`selectFrom('${jtName}') requires a prior join('${jtName}') call`);
|
|
1054
|
+
const sel = !cols$1 || cols$1.length === 0 ? ["*"] : cols$1.map(String);
|
|
1055
|
+
if (!(sel.length === 1 && sel[0] === "*")) for (const c of sel) findColumn(join.target, String(c));
|
|
1056
|
+
const existing = this.s.joinSelections.find((js) => js.target.name === jtName);
|
|
1057
|
+
if (existing) {
|
|
1058
|
+
if (existing.selected.includes("*")) return this;
|
|
1059
|
+
if (sel.length === 1 && sel[0] === "*") existing.selected = ["*"];
|
|
1060
|
+
else {
|
|
1061
|
+
const set = new Set(existing.selected);
|
|
1062
|
+
for (const c of sel) set.add(String(c));
|
|
1063
|
+
existing.selected = Array.from(set);
|
|
1064
|
+
}
|
|
1065
|
+
} else this.s.joinSelections.push({
|
|
1066
|
+
target: join.target,
|
|
1067
|
+
selected: sel
|
|
1068
|
+
});
|
|
1069
|
+
return this;
|
|
1070
|
+
}
|
|
1071
|
+
andWhere(where) {
|
|
1072
|
+
for (const k in where) this.s.where[k] = where[k];
|
|
1073
|
+
return this;
|
|
1074
|
+
}
|
|
1075
|
+
/** @deprecated Use andWhere() instead */
|
|
1076
|
+
where(where) {
|
|
1077
|
+
return this.andWhere(where);
|
|
1078
|
+
}
|
|
1079
|
+
orWhere(where) {
|
|
1080
|
+
this.s.orWhereGroups.push(where);
|
|
1081
|
+
return this;
|
|
1082
|
+
}
|
|
1083
|
+
wherePk(pk) {
|
|
1084
|
+
const pkCols = getPrimaryKeyColumns(table);
|
|
1085
|
+
if (!pkCols || pkCols.length === 0) throw new Error(`Table ${table.name} does not have a primary key`);
|
|
1086
|
+
if (pkCols.length === 1) {
|
|
1087
|
+
const colName = pkCols[0];
|
|
1088
|
+
const colDef = findColumn(table, colName);
|
|
1089
|
+
if (pk === null || pk === void 0) throw new Error(`wherePk on ${table.name} requires a non-null value for primary key column ${colName}`);
|
|
1090
|
+
if (Array.isArray(pk)) throw new Error(`wherePk on ${table.name} expects a scalar for primary key column ${colName}, not an array`);
|
|
1091
|
+
if (isPlainObject(pk)) throw new Error(`wherePk on ${table.name} expects a scalar for primary key column ${colName}`);
|
|
1092
|
+
validateScalarForColumn(colDef, pk, `primary key ${table.name}.${colName}`);
|
|
1093
|
+
this.s.where[colName] = pk;
|
|
1094
|
+
return this;
|
|
1095
|
+
} else {
|
|
1096
|
+
if (!isPlainObject(pk) || pk === null) throw new Error(`wherePk on ${table.name} requires an object with keys: ${pkCols.join(", ")}`);
|
|
1097
|
+
const obj = pk;
|
|
1098
|
+
for (const k of Object.keys(obj)) if (!pkCols.includes(k)) throw new Error(`wherePk received unknown key '${k}'. Expected keys: ${pkCols.join(", ")}`);
|
|
1099
|
+
for (const colName of pkCols) {
|
|
1100
|
+
if (!(colName in obj)) throw new Error(`wherePk missing key '${colName}'. Required keys: ${pkCols.join(", ")}`);
|
|
1101
|
+
const v = obj[colName];
|
|
1102
|
+
if (v === null || v === void 0) throw new Error(`wherePk on ${table.name} requires non-null values for all primary key columns (${pkCols.join(", ")})`);
|
|
1103
|
+
if (Array.isArray(v)) throw new Error(`wherePk on ${table.name} expects scalar values for primary key column ${colName}, not an array`);
|
|
1104
|
+
validateScalarForColumn(findColumn(table, colName), v, `primary key ${table.name}.${colName}`);
|
|
1105
|
+
this.s.where[colName] = v;
|
|
1106
|
+
}
|
|
1107
|
+
return this;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
andWhereOp(col, op, ...args) {
|
|
1111
|
+
const value = args[0];
|
|
1112
|
+
this.s.andOps.push({
|
|
1113
|
+
col,
|
|
1114
|
+
op,
|
|
1115
|
+
value
|
|
1116
|
+
});
|
|
1117
|
+
return this;
|
|
1118
|
+
}
|
|
1119
|
+
orWhereOp(col, op, ...args) {
|
|
1120
|
+
const value = args[0];
|
|
1121
|
+
this.s.orOpGroups.push([{
|
|
1122
|
+
col,
|
|
1123
|
+
op,
|
|
1124
|
+
value
|
|
1125
|
+
}]);
|
|
1126
|
+
return this;
|
|
1127
|
+
}
|
|
1128
|
+
andWhereOpGroup(predicates) {
|
|
1129
|
+
const group = predicates.map((p) => ({
|
|
1130
|
+
col: p[0],
|
|
1131
|
+
op: p[1],
|
|
1132
|
+
value: p[2]
|
|
1133
|
+
}));
|
|
1134
|
+
for (const g of group) this.s.andOps.push(g);
|
|
1135
|
+
return this;
|
|
1136
|
+
}
|
|
1137
|
+
orWhereOpGroup(predicates) {
|
|
1138
|
+
const group = predicates.map((p) => ({
|
|
1139
|
+
col: p[0],
|
|
1140
|
+
op: p[1],
|
|
1141
|
+
value: p[2]
|
|
1142
|
+
}));
|
|
1143
|
+
this.s.orOpGroups.push(group);
|
|
1144
|
+
return this;
|
|
1145
|
+
}
|
|
1146
|
+
orderBy(order) {
|
|
1147
|
+
this.s.order = [];
|
|
1148
|
+
const isValidDir = (d) => d === "asc" || d === "desc";
|
|
1149
|
+
if (typeof order === "string") {
|
|
1150
|
+
findColumn(table, String(order));
|
|
1151
|
+
this.s.order.push({
|
|
1152
|
+
col: order,
|
|
1153
|
+
dir: "asc"
|
|
1154
|
+
});
|
|
1155
|
+
} else for (const item of order) {
|
|
1156
|
+
const col = String(item[0]);
|
|
1157
|
+
const dir = String(item[1]);
|
|
1158
|
+
findColumn(table, col);
|
|
1159
|
+
if (!isValidDir(dir)) throw new Error(`Invalid order direction: ${dir}. Allowed: asc | desc`);
|
|
1160
|
+
this.s.order.push({
|
|
1161
|
+
col: item[0],
|
|
1162
|
+
dir
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
return this;
|
|
1166
|
+
}
|
|
1167
|
+
limit(n) {
|
|
1168
|
+
if (typeof n !== "number" || !Number.isFinite(n) || !Number.isInteger(n) || n < 0) throw new Error(`Invalid limit: ${n}. Limit must be a non-negative integer`);
|
|
1169
|
+
this.s.limit = n;
|
|
1170
|
+
return this;
|
|
1171
|
+
}
|
|
1172
|
+
offset(n) {
|
|
1173
|
+
if (typeof n !== "number" || !Number.isFinite(n) || !Number.isInteger(n) || n < 0) throw new Error(`Invalid offset: ${n}. Offset must be a non-negative integer`);
|
|
1174
|
+
this.s.offset = n;
|
|
1175
|
+
return this;
|
|
1176
|
+
}
|
|
1177
|
+
join(tableName, type = "inner") {
|
|
1178
|
+
const target = findTable(schema, String(tableName));
|
|
1179
|
+
const pairs = findJoinOn(table, target);
|
|
1180
|
+
if (!pairs || !pairs.length) throw new Error(`No foreign key relation found between ${table.name} and ${target.name}`);
|
|
1181
|
+
const jt = String(type);
|
|
1182
|
+
if (jt !== "inner" && jt !== "left" && jt !== "right" && jt !== "full") throw new Error(`Invalid join type: ${jt}. Allowed: inner | left | right | full`);
|
|
1183
|
+
this.s.joins.push({
|
|
1184
|
+
type: jt,
|
|
1185
|
+
target,
|
|
1186
|
+
on: pairs
|
|
1187
|
+
});
|
|
1188
|
+
return this;
|
|
1189
|
+
}
|
|
1190
|
+
}();
|
|
1191
|
+
}
|
|
1192
|
+
insert(values) {
|
|
1193
|
+
const table = this.table;
|
|
1194
|
+
const schema = this.schema;
|
|
1195
|
+
const state = {
|
|
1196
|
+
values,
|
|
1197
|
+
returning: []
|
|
1198
|
+
};
|
|
1199
|
+
return new class {
|
|
1200
|
+
constructor() {
|
|
1201
|
+
this.returning = (cols) => {
|
|
1202
|
+
state.returning = cols || [];
|
|
1203
|
+
return this;
|
|
1204
|
+
};
|
|
1205
|
+
this.toSql = () => {
|
|
1206
|
+
const cols = [];
|
|
1207
|
+
const vals = [];
|
|
1208
|
+
const params = {};
|
|
1209
|
+
const type_hints = {};
|
|
1210
|
+
let p = 0;
|
|
1211
|
+
for (const key in state.values) {
|
|
1212
|
+
const value = state.values[key];
|
|
1213
|
+
const col = findColumn(table, key);
|
|
1214
|
+
p += 1;
|
|
1215
|
+
const paramName = `${table.name}_${key}_${p}`;
|
|
1216
|
+
cols.push(`"${key}"`);
|
|
1217
|
+
vals.push(`:${paramName}`);
|
|
1218
|
+
params[paramName] = value;
|
|
1219
|
+
addTypeHintForParam(type_hints, paramName, col, value);
|
|
1220
|
+
}
|
|
1221
|
+
const parts = [];
|
|
1222
|
+
parts.push(`insert into "${schema.name}"."${table.name}" (${cols.join(", ")}) values (${vals.join(", ")})`);
|
|
1223
|
+
if (state.returning.length) parts.push("returning " + state.returning.join(", "));
|
|
1224
|
+
return {
|
|
1225
|
+
q: parts.join(" "),
|
|
1226
|
+
params,
|
|
1227
|
+
type_hints: Object.keys(type_hints).length ? type_hints : void 0
|
|
1228
|
+
};
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
}();
|
|
1232
|
+
}
|
|
1233
|
+
update(values) {
|
|
1234
|
+
const table = this.table;
|
|
1235
|
+
const schema = this.schema;
|
|
1236
|
+
const state = {
|
|
1237
|
+
values,
|
|
1238
|
+
where: {},
|
|
1239
|
+
returning: []
|
|
1240
|
+
};
|
|
1241
|
+
return new class {
|
|
1242
|
+
constructor() {
|
|
1243
|
+
this.where = (w) => {
|
|
1244
|
+
for (const k in w) state.where[k] = w[k];
|
|
1245
|
+
return this;
|
|
1246
|
+
};
|
|
1247
|
+
this.wherePk = (pk) => {
|
|
1248
|
+
const pkCols = getPrimaryKeyColumns(table);
|
|
1249
|
+
if (!pkCols || pkCols.length === 0) throw new Error(`Table ${table.name} does not have a primary key`);
|
|
1250
|
+
if (pkCols.length === 1) {
|
|
1251
|
+
const colName = pkCols[0];
|
|
1252
|
+
const colDef = findColumn(table, colName);
|
|
1253
|
+
if (pk === null || pk === void 0) throw new Error(`wherePk on ${table.name} requires a non-null value for primary key column ${colName}`);
|
|
1254
|
+
if (Array.isArray(pk)) throw new Error(`wherePk on ${table.name} expects a scalar for primary key column ${colName}, not an array`);
|
|
1255
|
+
if (isPlainObject(pk)) throw new Error(`wherePk on ${table.name} expects a scalar for primary key column ${colName}`);
|
|
1256
|
+
validateScalarForColumn(colDef, pk, `primary key ${table.name}.${colName}`);
|
|
1257
|
+
state.where[colName] = pk;
|
|
1258
|
+
} else {
|
|
1259
|
+
if (!isPlainObject(pk) || pk === null) throw new Error(`wherePk on ${table.name} requires an object with keys: ${pkCols.join(", ")}`);
|
|
1260
|
+
const obj = pk;
|
|
1261
|
+
for (const k of Object.keys(obj)) if (!pkCols.includes(k)) throw new Error(`wherePk received unknown key '${k}'. Expected keys: ${pkCols.join(", ")}`);
|
|
1262
|
+
for (const colName of pkCols) {
|
|
1263
|
+
if (!(colName in obj)) throw new Error(`wherePk missing key '${colName}'. Required keys: ${pkCols.join(", ")}`);
|
|
1264
|
+
const v = obj[colName];
|
|
1265
|
+
if (v === null || v === void 0) throw new Error(`wherePk on ${table.name} requires non-null values for all primary key columns (${pkCols.join(", ")})`);
|
|
1266
|
+
if (Array.isArray(v)) throw new Error(`wherePk on ${table.name} expects scalar values for primary key column ${colName}, not an array`);
|
|
1267
|
+
validateScalarForColumn(findColumn(table, colName), v, `primary key ${table.name}.${colName}`);
|
|
1268
|
+
state.where[colName] = v;
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
return this;
|
|
1272
|
+
};
|
|
1273
|
+
this.returning = (cols) => {
|
|
1274
|
+
state.returning = cols || [];
|
|
1275
|
+
return this;
|
|
1276
|
+
};
|
|
1277
|
+
this.toSql = () => {
|
|
1278
|
+
const params = {};
|
|
1279
|
+
const type_hints = {};
|
|
1280
|
+
let p = 0;
|
|
1281
|
+
const setParts = [];
|
|
1282
|
+
for (const key in state.values) {
|
|
1283
|
+
const value = state.values[key];
|
|
1284
|
+
const col = findColumn(table, key);
|
|
1285
|
+
p += 1;
|
|
1286
|
+
const paramName = `${table.name}_${key}_${p}`;
|
|
1287
|
+
setParts.push(`"${key}" = :${paramName}`);
|
|
1288
|
+
params[paramName] = value;
|
|
1289
|
+
addTypeHintForParam(type_hints, paramName, col, value);
|
|
1290
|
+
}
|
|
1291
|
+
const whereClauses = [];
|
|
1292
|
+
for (const key in state.where) {
|
|
1293
|
+
const value = state.where[key];
|
|
1294
|
+
const col = findColumn(table, key);
|
|
1295
|
+
p += 1;
|
|
1296
|
+
const paramName = `${table.name}_${key}_${p}`;
|
|
1297
|
+
if (value === null) {
|
|
1298
|
+
if (!col.is_nullable) throw new Error(`Column ${table.name}.${key} is not nullable; cannot compare to null`);
|
|
1299
|
+
whereClauses.push(`"${key}" is null`);
|
|
1300
|
+
} else if (Array.isArray(value) && !isArrayShapedGeomScalarValue(col, value)) {
|
|
1301
|
+
validateInArrayValues(col, key, value, "in");
|
|
1302
|
+
whereClauses.push(`"${key}" = ANY(:${paramName})`);
|
|
1303
|
+
params[paramName] = value;
|
|
1304
|
+
addTypeHintForParam(type_hints, paramName, col, value);
|
|
1305
|
+
} else {
|
|
1306
|
+
validateScalarForColumn(col, value, `column ${key}`);
|
|
1307
|
+
whereClauses.push(`"${key}" = :${paramName}`);
|
|
1308
|
+
params[paramName] = value;
|
|
1309
|
+
addTypeHintForParam(type_hints, paramName, col, value);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
const parts = [];
|
|
1313
|
+
parts.push(`update "${schema.name}"."${table.name}" set ${setParts.join(", ")}`);
|
|
1314
|
+
if (whereClauses.length) parts.push("where " + whereClauses.join(" and "));
|
|
1315
|
+
if (state.returning.length) parts.push("returning " + state.returning.join(", "));
|
|
1316
|
+
return {
|
|
1317
|
+
q: parts.join(" "),
|
|
1318
|
+
params,
|
|
1319
|
+
type_hints: Object.keys(type_hints).length ? type_hints : void 0
|
|
1320
|
+
};
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
}();
|
|
1324
|
+
}
|
|
1325
|
+
delete() {
|
|
1326
|
+
const table = this.table;
|
|
1327
|
+
const schema = this.schema;
|
|
1328
|
+
const state = {
|
|
1329
|
+
where: {},
|
|
1330
|
+
returning: []
|
|
1331
|
+
};
|
|
1332
|
+
return new class {
|
|
1333
|
+
constructor() {
|
|
1334
|
+
this.where = (w) => {
|
|
1335
|
+
for (const k in w) state.where[k] = w[k];
|
|
1336
|
+
return this;
|
|
1337
|
+
};
|
|
1338
|
+
this.wherePk = (pk) => {
|
|
1339
|
+
const pkCols = getPrimaryKeyColumns(table);
|
|
1340
|
+
if (!pkCols || pkCols.length === 0) throw new Error(`Table ${table.name} does not have a primary key`);
|
|
1341
|
+
if (pkCols.length === 1) {
|
|
1342
|
+
const col = pkCols[0];
|
|
1343
|
+
if (isPlainObject(pk)) throw new Error(`wherePk on ${table.name} expects a scalar for primary key column ${col}`);
|
|
1344
|
+
state.where[col] = pk;
|
|
1345
|
+
} else {
|
|
1346
|
+
if (!isPlainObject(pk)) throw new Error(`wherePk on ${table.name} requires an object with keys: ${pkCols.join(", ")}`);
|
|
1347
|
+
const obj = pk;
|
|
1348
|
+
for (const k of Object.keys(obj)) if (!pkCols.includes(k)) throw new Error(`wherePk received unknown key '${k}'. Expected keys: ${pkCols.join(", ")}`);
|
|
1349
|
+
for (const col of pkCols) {
|
|
1350
|
+
if (!(col in obj)) throw new Error(`wherePk missing key '${col}'. Required keys: ${pkCols.join(", ")}`);
|
|
1351
|
+
state.where[col] = obj[col];
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
return this;
|
|
1355
|
+
};
|
|
1356
|
+
this.returning = (cols) => {
|
|
1357
|
+
state.returning = cols || [];
|
|
1358
|
+
return this;
|
|
1359
|
+
};
|
|
1360
|
+
this.toSql = () => {
|
|
1361
|
+
const params = {};
|
|
1362
|
+
const type_hints = {};
|
|
1363
|
+
let p = 0;
|
|
1364
|
+
const whereClauses = [];
|
|
1365
|
+
for (const key in state.where) {
|
|
1366
|
+
const value = state.where[key];
|
|
1367
|
+
const col = findColumn(table, key);
|
|
1368
|
+
p += 1;
|
|
1369
|
+
const paramName = `${table.name}_${key}_${p}`;
|
|
1370
|
+
if (value === null) {
|
|
1371
|
+
if (!col.is_nullable) throw new Error(`Column ${table.name}.${key} is not nullable; cannot compare to null`);
|
|
1372
|
+
whereClauses.push(`"${key}" is null`);
|
|
1373
|
+
} else if (Array.isArray(value)) {
|
|
1374
|
+
validateInArrayValues(col, key, value, "in");
|
|
1375
|
+
whereClauses.push(`"${key}" = ANY(:${paramName})`);
|
|
1376
|
+
params[paramName] = value;
|
|
1377
|
+
addTypeHintForParam(type_hints, paramName, col, value);
|
|
1378
|
+
} else {
|
|
1379
|
+
validateScalarForColumn(col, value, `column ${key}`);
|
|
1380
|
+
whereClauses.push(`"${key}" = :${paramName}`);
|
|
1381
|
+
params[paramName] = value;
|
|
1382
|
+
addTypeHintForParam(type_hints, paramName, col, value);
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
const parts = [];
|
|
1386
|
+
parts.push(`delete from "${schema.name}"."${table.name}"`);
|
|
1387
|
+
if (whereClauses.length) parts.push("where " + whereClauses.join(" and "));
|
|
1388
|
+
if (state.returning.length) parts.push("returning " + state.returning.join(", "));
|
|
1389
|
+
return {
|
|
1390
|
+
q: parts.join(" "),
|
|
1391
|
+
params,
|
|
1392
|
+
type_hints: Object.keys(type_hints).length ? type_hints : void 0
|
|
1393
|
+
};
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
}();
|
|
1397
|
+
}
|
|
1398
|
+
};
|
|
1399
|
+
function createSqlBuilder(schema) {
|
|
1400
|
+
return { table: (name) => new TableQueryImpl(schema, String(name)) };
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
//#endregion
|
|
1404
|
+
export { Claims, CodeFlow, Meta, PasswordFlow, Rpc, SignUp, Sql, Stats, Status, Tables, Users, Ws, createApi, createSqlBuilder };
|
|
577
1405
|
//# sourceMappingURL=centia-io-sdk.js.map
|