pgsql-template-tag 0.0.6 → 0.0.8
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/dist/src/core.d.ts +119 -16
- package/dist/src/core.d.ts.map +1 -1
- package/dist/src/core.js +114 -58
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/sql.d.ts +11 -13
- package/dist/src/sql.d.ts.map +1 -1
- package/dist/src/sql.js +2 -4
- package/package.json +3 -2
- package/src/core.ts +237 -41
- package/src/index.ts +1 -1
- package/src/sql.ts +17 -17
package/dist/src/core.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { UnionToTuple } from "type-fest";
|
|
1
2
|
/**
|
|
2
3
|
* SQL クエリー内で使用される値の型定義です。
|
|
3
4
|
*/
|
|
@@ -6,34 +7,122 @@ export type Value = unknown;
|
|
|
6
7
|
* SQL クエリーの構築に使用できる生の値、または別の Sql インスタンスを表す型です。
|
|
7
8
|
*/
|
|
8
9
|
export type RawValue = Value | Slot | Sql;
|
|
10
|
+
/**
|
|
11
|
+
* Slot クラスを一意に識別するためのシンボルです。
|
|
12
|
+
*/
|
|
13
|
+
declare const SLOT_SYMBOL: unique symbol;
|
|
14
|
+
/**
|
|
15
|
+
* Slot クラスの基底となる型定義です。
|
|
16
|
+
*/
|
|
17
|
+
declare const SlotTypes: {
|
|
18
|
+
new (): {
|
|
19
|
+
/**
|
|
20
|
+
* このプロパティーは、TypeScript の `extends Slot` で `Slot` インスタンスのみに一致させるためにあります。そのため、`Slot` と同じプロパティーを持つオブジェクトに対して一致することはありません。
|
|
21
|
+
*/
|
|
22
|
+
readonly ["~kind"]: typeof SLOT_SYMBOL;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
9
25
|
/**
|
|
10
26
|
* スロットを表すクラスです。
|
|
11
27
|
*
|
|
12
28
|
* スロットは後から値を注入可能なプレースホルダーです。
|
|
29
|
+
*
|
|
30
|
+
* @template TName スロットの名前となる文字列リテラル型です。
|
|
31
|
+
* @template TValue スロットに許容される値の型です。
|
|
13
32
|
*/
|
|
14
|
-
export declare class Slot {
|
|
33
|
+
export declare class Slot<const TName extends string = string, TValue extends RawValue = RawValue> extends SlotTypes {
|
|
15
34
|
/**
|
|
16
35
|
* スロット名です。
|
|
17
36
|
*/
|
|
18
|
-
readonly name:
|
|
37
|
+
readonly name: TName;
|
|
19
38
|
/**
|
|
20
39
|
* デフォルト値です。
|
|
21
40
|
*/
|
|
22
|
-
readonly defaultValue:
|
|
41
|
+
readonly defaultValue: TValue;
|
|
23
42
|
/**
|
|
24
43
|
* 新しい Slot インスタンスを初期化します。
|
|
25
44
|
*
|
|
26
45
|
* @param name スロット名です。
|
|
27
46
|
* @param defaultValue デフォルト値です。
|
|
28
47
|
*/
|
|
29
|
-
constructor(name:
|
|
48
|
+
constructor(...args: null extends TValue ? [name: TName, defaultValue?: TValue] : [name: TName, defaultValue: TValue]);
|
|
30
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* オブジェクトのバリューの型を抽出するヘルパー型です。
|
|
52
|
+
*
|
|
53
|
+
* @template T 対象となるオブジェクト型です。
|
|
54
|
+
*/
|
|
55
|
+
type $ValueOf<T> = T[keyof T];
|
|
56
|
+
/**
|
|
57
|
+
* スロットの配列から、再帰的に値をマージして型を決定します。
|
|
58
|
+
*
|
|
59
|
+
* @template TSlots スロットのタプル型です。
|
|
60
|
+
*/
|
|
61
|
+
type $MergeSlotValue<TSlots> = TSlots extends [
|
|
62
|
+
Slot<string, infer TValue>,
|
|
63
|
+
infer TSlot,
|
|
64
|
+
...infer TOtherSlots
|
|
65
|
+
] ? TValue & $MergeSlotValue<[TSlot, ...TOtherSlots]> : TSlots extends [Slot<string, infer TValue>] ? TValue : never;
|
|
66
|
+
/**
|
|
67
|
+
* RawValue の配列からスロット情報を抽出し、名前ごとのマップ型に変換します。
|
|
68
|
+
*
|
|
69
|
+
* @template TValues RawValue の読み取り専用配列型です。
|
|
70
|
+
*/
|
|
71
|
+
type $MapSlotValue<TValues extends readonly RawValue[]> = TValues extends readonly (infer TSlot extends Slot)[] ? {
|
|
72
|
+
[TName in TSlot["name"]]: $MergeSlotValue<UnionToTuple<Extract<TSlot, Slot<TName>>>>;
|
|
73
|
+
} : {};
|
|
74
|
+
/**
|
|
75
|
+
* 指定された値のリストに対して、スロットを実際の内容で置き換えた型を生成します。
|
|
76
|
+
*
|
|
77
|
+
* @template TValues 置き換え対象の配列型です。
|
|
78
|
+
* @template TSlots スロット名と値のマップ型です。
|
|
79
|
+
*/
|
|
80
|
+
type $FillSlots<TValues, TSlots> = TValues extends [infer TValue, ...infer TOtherValues] ? [
|
|
81
|
+
TValue extends Slot<infer TName extends Extract<keyof TSlots, string>, infer TSlotValue> ? TSlotValue extends TSlots[TName] ? TSlotValue : TValue : TValue,
|
|
82
|
+
...$FillSlots<TOtherValues, TSlots>
|
|
83
|
+
] : [];
|
|
84
|
+
/**
|
|
85
|
+
* スロットを埋めるための部分的な引数型を定義します。
|
|
86
|
+
*
|
|
87
|
+
* @template TSlots スロット名と値のマップ型です。
|
|
88
|
+
*/
|
|
89
|
+
type _FillSlots<TSlots> = {
|
|
90
|
+
readonly [TName in Extract<keyof TSlots, string>]?: TSlots[TName];
|
|
91
|
+
} | Iterable<readonly [
|
|
92
|
+
name: $ValueOf<{
|
|
93
|
+
[TName in Extract<keyof TSlots, string>]: TName | Slot<TName, TSlots[TName]>;
|
|
94
|
+
}>,
|
|
95
|
+
value: $ValueOf<TSlots>
|
|
96
|
+
]>;
|
|
97
|
+
/**
|
|
98
|
+
* すべてのスロットを埋めるために必要な引数型を定義します。
|
|
99
|
+
*
|
|
100
|
+
* @template TSlots スロット名と値のマップ型です。
|
|
101
|
+
*/
|
|
102
|
+
type _FillAllSlots<TSlots> = {
|
|
103
|
+
readonly [TName in Extract<keyof TSlots, string>]: TSlots[TName];
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* スロットの部分的な補完に使用する外部向けの型定義です。
|
|
107
|
+
*
|
|
108
|
+
* @template TValues RawValue の配列です。
|
|
109
|
+
*/
|
|
110
|
+
export type FillSlots<TValues extends readonly RawValue[] = readonly RawValue[]> = _FillSlots<$MapSlotValue<TValues>>;
|
|
111
|
+
/**
|
|
112
|
+
* すべてのスロットの強制的な補完に使用する外部向けの型定義です。
|
|
113
|
+
*
|
|
114
|
+
* @template TValues RawValue の配列です。
|
|
115
|
+
*/
|
|
116
|
+
export type FillAllSlots<TValues extends readonly RawValue[] = readonly RawValue[]> = _FillAllSlots<$MapSlotValue<TValues>>;
|
|
31
117
|
/**
|
|
32
118
|
* 安全な SQL クエリーを構築するためのクラスです。
|
|
33
119
|
*
|
|
34
120
|
* プレースホルダーを使用したパラメーター化クエリーを生成します。
|
|
121
|
+
*
|
|
122
|
+
* @template TRawBindings クエリーに渡される生の値のタプル型です。
|
|
35
123
|
*/
|
|
36
|
-
export declare class Sql {
|
|
124
|
+
export declare class Sql<const TRawBindings extends readonly RawValue[] = readonly RawValue[]> {
|
|
125
|
+
#private;
|
|
37
126
|
/**
|
|
38
127
|
* 構築された SQL クエリーテキストを取得します。
|
|
39
128
|
*
|
|
@@ -46,26 +135,29 @@ export declare class Sql {
|
|
|
46
135
|
* クエリーに使用されるパラメーター値の配列です。
|
|
47
136
|
*/
|
|
48
137
|
readonly values: readonly Value[];
|
|
49
|
-
/**
|
|
50
|
-
* 内部状態を保持するためのプロパティーです。
|
|
51
|
-
*/
|
|
52
|
-
private readonly _;
|
|
53
138
|
/**
|
|
54
139
|
* 新しい Sql インスタンスを初期化します。
|
|
55
140
|
*
|
|
56
141
|
* @param rawStrings SQL の断片となる文字列の配列です。
|
|
57
142
|
* @param rawBindings 文字列の間に挿入される値の配列です。
|
|
58
143
|
*/
|
|
59
|
-
constructor(rawStrings: readonly string[], rawBindings:
|
|
144
|
+
constructor(rawStrings: readonly string[], rawBindings: TRawBindings);
|
|
60
145
|
/**
|
|
61
146
|
* スロットを値で埋めます。
|
|
62
147
|
*
|
|
148
|
+
* @template TSlots 指定されたスロットのマップ型です。
|
|
149
|
+
* @param slots スロットの値です。
|
|
150
|
+
* @returns スロットが埋められた新しい Sql インスタンスです。
|
|
151
|
+
*/
|
|
152
|
+
fill<TSlots extends FillSlots<TRawBindings>>(slots: TSlots): Sql<$FillSlots<TRawBindings, TSlots>>;
|
|
153
|
+
/**
|
|
154
|
+
* すべてのスロットを値で埋めます。
|
|
155
|
+
*
|
|
156
|
+
* @template TSlots 全てのスロットをカバーするマップ型です。
|
|
63
157
|
* @param slots スロットの値です。
|
|
64
158
|
* @returns スロットが埋められた新しい Sql インスタンスです。
|
|
65
159
|
*/
|
|
66
|
-
|
|
67
|
-
readonly [name: string]: Value;
|
|
68
|
-
} | Iterable<readonly [string | Slot, Value]>): Sql;
|
|
160
|
+
fillAll<TSlots extends FillAllSlots<TRawBindings>>(slots: TSlots): Sql<$FillSlots<TRawBindings, TSlots>>;
|
|
69
161
|
/**
|
|
70
162
|
* オブジェクトを JSON 形式に変換可能な形式で返します。
|
|
71
163
|
*
|
|
@@ -90,11 +182,11 @@ export declare class Sql {
|
|
|
90
182
|
* @param value SQL に含める生の文字列です。
|
|
91
183
|
* @returns 指定された文字列を持つ Sql インスタンスを返します。
|
|
92
184
|
*/
|
|
93
|
-
export declare function raw(value: string): Sql
|
|
185
|
+
export declare function raw(value: string): Sql<[]>;
|
|
94
186
|
/**
|
|
95
187
|
* 空の SQL インスタンスを表す定数です。
|
|
96
188
|
*/
|
|
97
|
-
export declare const empty: Sql
|
|
189
|
+
export declare const empty: Sql<[]>;
|
|
98
190
|
/**
|
|
99
191
|
* 複数の SQL 断片や値を、指定されたセパレーターで結合します。
|
|
100
192
|
*
|
|
@@ -104,7 +196,7 @@ export declare const empty: Sql;
|
|
|
104
196
|
* @param separator 結合時に挿入される文字列です。デフォルトはカンマ(,)です。
|
|
105
197
|
* @returns 結合された新しい Sql インスタンスを返します。
|
|
106
198
|
*/
|
|
107
|
-
export declare function join
|
|
199
|
+
export declare function join<const TValues extends readonly RawValue[]>(values: TValues, separator?: string | undefined): Sql<TValues>;
|
|
108
200
|
/**
|
|
109
201
|
* 文字列を SQL の識別子(テーブル名やカラム名など)として安全にエスケープします。
|
|
110
202
|
*
|
|
@@ -123,4 +215,15 @@ export declare function ident(value: string): string;
|
|
|
123
215
|
* @returns エスケープ済みのリテラル文字列を返します。
|
|
124
216
|
*/
|
|
125
217
|
export declare function literal(value: string): string;
|
|
218
|
+
/**
|
|
219
|
+
* 新しい Slot インスタンスを作成します。
|
|
220
|
+
*
|
|
221
|
+
* @template TName スロットの名前となる文字列リテラル型です。
|
|
222
|
+
* @template TValue スロットに許容される値の型です。
|
|
223
|
+
* @param name スロット名です。
|
|
224
|
+
* @param defaultValue デフォルト値です。
|
|
225
|
+
* @returns 作成された新しい Slot インスタンスです。
|
|
226
|
+
*/
|
|
227
|
+
export declare function slot<const TName extends string, TValue extends RawValue = RawValue>(name: TName, defaultValue?: TValue): Slot<TName, TValue>;
|
|
228
|
+
export {};
|
|
126
229
|
//# sourceMappingURL=core.d.ts.map
|
package/dist/src/core.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/core.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/core.ts"],"names":[],"mappings":"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"}
|
package/dist/src/core.js
CHANGED
|
@@ -1,17 +1,32 @@
|
|
|
1
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
+
};
|
|
6
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
7
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
10
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
11
|
+
};
|
|
12
|
+
var _Sql_instances, _a, _Sql_state, _Sql_fill;
|
|
1
13
|
import { isPlainObject } from "es-toolkit/predicate";
|
|
14
|
+
/**
|
|
15
|
+
* Slot クラスの基底となる型定義です。
|
|
16
|
+
*/
|
|
17
|
+
const SlotTypes = class {
|
|
18
|
+
};
|
|
2
19
|
/**
|
|
3
20
|
* スロットを表すクラスです。
|
|
4
21
|
*
|
|
5
22
|
* スロットは後から値を注入可能なプレースホルダーです。
|
|
23
|
+
*
|
|
24
|
+
* @template TName スロットの名前となる文字列リテラル型です。
|
|
25
|
+
* @template TValue スロットに許容される値の型です。
|
|
6
26
|
*/
|
|
7
|
-
export class Slot {
|
|
8
|
-
/**
|
|
9
|
-
* 新しい Slot インスタンスを初期化します。
|
|
10
|
-
*
|
|
11
|
-
* @param name スロット名です。
|
|
12
|
-
* @param defaultValue デフォルト値です。
|
|
13
|
-
*/
|
|
27
|
+
export class Slot extends SlotTypes {
|
|
14
28
|
constructor(name, defaultValue) {
|
|
29
|
+
super();
|
|
15
30
|
if (arguments.length < 2) {
|
|
16
31
|
defaultValue = null;
|
|
17
32
|
}
|
|
@@ -23,6 +38,8 @@ export class Slot {
|
|
|
23
38
|
* 安全な SQL クエリーを構築するためのクラスです。
|
|
24
39
|
*
|
|
25
40
|
* プレースホルダーを使用したパラメーター化クエリーを生成します。
|
|
41
|
+
*
|
|
42
|
+
* @template TRawBindings クエリーに渡される生の値のタプル型です。
|
|
26
43
|
*/
|
|
27
44
|
export class Sql {
|
|
28
45
|
/**
|
|
@@ -34,15 +51,15 @@ export class Sql {
|
|
|
34
51
|
*/
|
|
35
52
|
get text() {
|
|
36
53
|
// キャッシュが存在しない場合にのみ、文字列を構築します。
|
|
37
|
-
if (this.
|
|
38
|
-
let i = 0, text = this.
|
|
54
|
+
if (__classPrivateFieldGet(this, _Sql_state, "f").text === undefined) {
|
|
55
|
+
let i = 0, text = __classPrivateFieldGet(this, _Sql_state, "f").parts[0];
|
|
39
56
|
// 文字列の断片とプレースホルダー($1, $2...)を交互に結合します。
|
|
40
|
-
for (; i < this.
|
|
41
|
-
text += "$" + this.
|
|
57
|
+
for (; i < __classPrivateFieldGet(this, _Sql_state, "f").phIds.length; i++) {
|
|
58
|
+
text += "$" + __classPrivateFieldGet(this, _Sql_state, "f").phIds[i] + __classPrivateFieldGet(this, _Sql_state, "f").parts[i + 1];
|
|
42
59
|
}
|
|
43
|
-
this.
|
|
60
|
+
__classPrivateFieldGet(this, _Sql_state, "f").text = text;
|
|
44
61
|
}
|
|
45
|
-
return this.
|
|
62
|
+
return __classPrivateFieldGet(this, _Sql_state, "f").text;
|
|
46
63
|
}
|
|
47
64
|
/**
|
|
48
65
|
* 新しい Sql インスタンスを初期化します。
|
|
@@ -51,6 +68,11 @@ export class Sql {
|
|
|
51
68
|
* @param rawBindings 文字列の間に挿入される値の配列です。
|
|
52
69
|
*/
|
|
53
70
|
constructor(rawStrings, rawBindings) {
|
|
71
|
+
_Sql_instances.add(this);
|
|
72
|
+
/**
|
|
73
|
+
* 内部状態を保持するためのプロパティーです。
|
|
74
|
+
*/
|
|
75
|
+
_Sql_state.set(this, void 0);
|
|
54
76
|
if (rawStrings.length === 0) {
|
|
55
77
|
throw new TypeError("Expected at least 1 string");
|
|
56
78
|
}
|
|
@@ -103,17 +125,17 @@ export class Sql {
|
|
|
103
125
|
const child = rawBindings[i];
|
|
104
126
|
const rawString = rawStrings[i + 1];
|
|
105
127
|
// バインディング値が Sql インスタンス(ネストされたクエリー)の場合の処理です。
|
|
106
|
-
if (child instanceof
|
|
128
|
+
if (child instanceof _a) {
|
|
107
129
|
// 現在の最後の文字列断片に、ネストされた Sql の最初の断片を結合します。
|
|
108
|
-
strings[strings.length - 1] += child.
|
|
130
|
+
strings[strings.length - 1] += __classPrivateFieldGet(child, _Sql_state, "f").parts[0];
|
|
109
131
|
// ネストされた Sql のプレースホルダーと値を再マッピングします。
|
|
110
|
-
for (let j = 0; j < child.
|
|
111
|
-
const childPlaceholderId = child.
|
|
132
|
+
for (let j = 0; j < __classPrivateFieldGet(child, _Sql_state, "f").phIds.length; j++) {
|
|
133
|
+
const childPlaceholderId = __classPrivateFieldGet(child, _Sql_state, "f").phIds[j];
|
|
112
134
|
const valueIndex = childPlaceholderId - 1;
|
|
113
135
|
const value = child.values[valueIndex];
|
|
114
|
-
const slot = child.
|
|
136
|
+
const slot = __classPrivateFieldGet(child, _Sql_state, "f").idx2slot.get(valueIndex);
|
|
115
137
|
const placeholderId = slot !== undefined ? registerSlot(slot) : registerValue(value);
|
|
116
|
-
strings.push(child.
|
|
138
|
+
strings.push(__classPrivateFieldGet(child, _Sql_state, "f").parts[j + 1]);
|
|
117
139
|
placeholderIds.push(placeholderId);
|
|
118
140
|
}
|
|
119
141
|
// ネストされた Sql の展開が終わった後に、後続の生の文字列を結合します。
|
|
@@ -126,53 +148,32 @@ export class Sql {
|
|
|
126
148
|
}
|
|
127
149
|
}
|
|
128
150
|
this.values = bindings;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
slot2idx,
|
|
136
|
-
},
|
|
137
|
-
});
|
|
151
|
+
__classPrivateFieldSet(this, _Sql_state, {
|
|
152
|
+
parts: strings,
|
|
153
|
+
phIds: placeholderIds,
|
|
154
|
+
idx2slot,
|
|
155
|
+
slot2idx,
|
|
156
|
+
}, "f");
|
|
138
157
|
}
|
|
139
158
|
/**
|
|
140
159
|
* スロットを値で埋めます。
|
|
141
160
|
*
|
|
161
|
+
* @template TSlots 指定されたスロットのマップ型です。
|
|
142
162
|
* @param slots スロットの値です。
|
|
143
163
|
* @returns スロットが埋められた新しい Sql インスタンスです。
|
|
144
164
|
*/
|
|
145
165
|
fill(slots) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
idxes = this._.slot2idx.get(target);
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
for (const [idx, slot] of this._.idx2slot) {
|
|
161
|
-
if (slot === target) {
|
|
162
|
-
idxes || (idxes = new Set());
|
|
163
|
-
idxes.add(idx);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
if (idxes === undefined) {
|
|
168
|
-
// スロットが見つからない場合は無視します。
|
|
169
|
-
continue;
|
|
170
|
-
}
|
|
171
|
-
for (const idx of idxes) {
|
|
172
|
-
values[idx] = value;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
return new Sql(this._.parts, values);
|
|
166
|
+
return __classPrivateFieldGet(this, _Sql_instances, "m", _Sql_fill).call(this, slots, false);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* すべてのスロットを値で埋めます。
|
|
170
|
+
*
|
|
171
|
+
* @template TSlots 全てのスロットをカバーするマップ型です。
|
|
172
|
+
* @param slots スロットの値です。
|
|
173
|
+
* @returns スロットが埋められた新しい Sql インスタンスです。
|
|
174
|
+
*/
|
|
175
|
+
fillAll(slots) {
|
|
176
|
+
return __classPrivateFieldGet(this, _Sql_instances, "m", _Sql_fill).call(this, slots, true);
|
|
176
177
|
}
|
|
177
178
|
/**
|
|
178
179
|
* オブジェクトを JSON 形式に変換可能な形式で返します。
|
|
@@ -194,6 +195,58 @@ export class Sql {
|
|
|
194
195
|
return this.text;
|
|
195
196
|
}
|
|
196
197
|
}
|
|
198
|
+
_a = Sql, _Sql_state = new WeakMap(), _Sql_instances = new WeakSet(), _Sql_fill = function _Sql_fill(slots, all) {
|
|
199
|
+
if (isPlainObject(slots)) {
|
|
200
|
+
slots = Object.entries(slots);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
// 一旦 Map のインスタンスにすることで、重複する名前またはスロットを 1 つに絞ります。
|
|
204
|
+
slots = new Map(slots);
|
|
205
|
+
}
|
|
206
|
+
const { parts, idx2slot, slot2idx } = __classPrivateFieldGet(this, _Sql_state, "f");
|
|
207
|
+
const filled = new Set();
|
|
208
|
+
const values = this.values.slice();
|
|
209
|
+
for (const [target, value] of new Map(slots)) {
|
|
210
|
+
let idxes;
|
|
211
|
+
if (typeof target === "string") {
|
|
212
|
+
idxes = slot2idx.get(target);
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
// インスタンスが直接指定された場合、全インデックスから一致するものを探します。
|
|
216
|
+
for (const [idx, slot] of idx2slot) {
|
|
217
|
+
if (slot === target) {
|
|
218
|
+
idxes || (idxes = new Set());
|
|
219
|
+
idxes.add(idx);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (idxes === undefined) {
|
|
224
|
+
// スロットが見つからない場合は無視します。
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
// 該当するすべてのプレースホルダーインデックスを新しい値で更新します。
|
|
228
|
+
for (const idx of idxes) {
|
|
229
|
+
values[idx] = value;
|
|
230
|
+
filled.add(idx);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// 全て埋める必要がある場合、未解決のスロットが残っていないか検証します。
|
|
234
|
+
if (all) {
|
|
235
|
+
for (let idx = 0; idx < values.length; idx++) {
|
|
236
|
+
if (idx2slot.has(idx) && !filled.has(idx)) {
|
|
237
|
+
const missingSlots = new Set();
|
|
238
|
+
missingSlots.add(idx2slot.get(idx).name);
|
|
239
|
+
for (; idx < values.length; idx++) {
|
|
240
|
+
if (idx2slot.has(idx)) {
|
|
241
|
+
missingSlots.add(idx2slot.get(idx).name);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
throw new Error(`Not all slots are filled. Missing: ${[...missingSlots].join(", ")}`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return new _a(parts, values);
|
|
249
|
+
};
|
|
197
250
|
/**
|
|
198
251
|
* 生の文字列を SQL 断片として扱います。
|
|
199
252
|
*
|
|
@@ -254,3 +307,6 @@ const SINGLE_QUOTE_REGEX = /'/g;
|
|
|
254
307
|
export function literal(value) {
|
|
255
308
|
return "'" + value.replace(SINGLE_QUOTE_REGEX, "''") + "'";
|
|
256
309
|
}
|
|
310
|
+
export function slot(...args) {
|
|
311
|
+
return new Slot(...args);
|
|
312
|
+
}
|
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEhG,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/src/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { Sql, Slot, empty, join, raw, ident } from "./core.js";
|
|
1
|
+
export { Sql, Slot, empty, join, raw, ident, slot } from "./core.js";
|
|
2
2
|
export { sql } from "./sql.js";
|
package/dist/src/sql.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { join, raw, Sql, ident, literal, Slot } from "./core.js";
|
|
1
|
+
import { join, raw, Sql, ident, literal, Slot, RawValue, slot } from "./core.js";
|
|
2
2
|
declare namespace sql {
|
|
3
3
|
/**
|
|
4
4
|
* SQL クエリーの構築に使用できる生の値、または別の Sql インスタンスを表す型です。
|
|
@@ -12,24 +12,22 @@ declare namespace sql {
|
|
|
12
12
|
* スロットを表すクラスです。
|
|
13
13
|
*
|
|
14
14
|
* スロットは後から値を注入可能なプレースホルダーです。
|
|
15
|
+
*
|
|
16
|
+
* @template TName スロットの名前となる文字列リテラル型です。
|
|
17
|
+
* @template TValue スロットに許容される値の型です。
|
|
15
18
|
*/
|
|
16
|
-
type Slot = import("./core.js").Slot
|
|
19
|
+
type Slot<TName extends string = string, TValue extends RawValue = RawValue> = import("./core.js").Slot<TName, TValue>;
|
|
17
20
|
/**
|
|
18
21
|
* 安全な SQL クエリーを構築するためのクラス型です。
|
|
22
|
+
*
|
|
23
|
+
* @template TBindings クエリーに渡される生の値のタプル型です。
|
|
19
24
|
*/
|
|
20
|
-
type Sql = import("./core.js").Sql
|
|
25
|
+
type Sql<TBindings extends readonly RawValue[] = readonly RawValue[]> = import("./core.js").Sql<TBindings>;
|
|
21
26
|
}
|
|
22
|
-
/**
|
|
23
|
-
* 新しい Slot インスタンスを作成します。
|
|
24
|
-
*
|
|
25
|
-
* @param name スロット名です。
|
|
26
|
-
* @param defaultValue デフォルト値です。
|
|
27
|
-
* @returns 作成された新しい Slot インスタンスです。
|
|
28
|
-
*/
|
|
29
|
-
declare function slot(name: string, defaultValue?: sql.Value): sql.Slot;
|
|
30
27
|
/**
|
|
31
28
|
* テンプレートリテラルを使用して SQL クエリーを安全に構築するためのタグ関数です。
|
|
32
29
|
*
|
|
30
|
+
* @template TBindings クエリーに渡される生の値のタプル型です。
|
|
33
31
|
* @param strings テンプレートリテラルの静的な文字列部分の配列です。
|
|
34
32
|
* @param bindings テンプレートリテラルに埋め込まれた動的な値の配列です。
|
|
35
33
|
* @returns パラメーター化された SQL 情報を保持する Sql インスタンスを返します。
|
|
@@ -38,7 +36,7 @@ declare function slot(name: string, defaultValue?: sql.Value): sql.Slot;
|
|
|
38
36
|
* const query = sql`SELECT * FROM users WHERE id = ${1}`;
|
|
39
37
|
* ```
|
|
40
38
|
*/
|
|
41
|
-
declare const sql: ((strings: TemplateStringsArray, ...bindings:
|
|
39
|
+
declare const sql: (<const TBindings extends readonly RawValue[]>(strings: TemplateStringsArray, ...bindings: TBindings) => sql.Sql<TBindings>) & {
|
|
42
40
|
/**
|
|
43
41
|
* Sql クラスです。
|
|
44
42
|
*/
|
|
@@ -64,7 +62,7 @@ declare const sql: ((strings: TemplateStringsArray, ...bindings: readonly sql.Ra
|
|
|
64
62
|
/**
|
|
65
63
|
* 空の SQL クエリーを表す定数です。
|
|
66
64
|
*/
|
|
67
|
-
readonly empty: Sql
|
|
65
|
+
readonly empty: Sql<[]>;
|
|
68
66
|
/**
|
|
69
67
|
* 識別子(テーブル名等)を安全にエスケープするための関数です。
|
|
70
68
|
*/
|
package/dist/src/sql.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../../src/sql.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../../src/sql.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAExF,kBAAU,GAAG,CAAC;IACZ;;OAEG;IACH,KAAY,QAAQ,GAAG,OAAO,WAAW,EAAE,QAAQ,CAAC;IAEpD;;OAEG;IACH,KAAY,KAAK,GAAG,OAAO,WAAW,EAAE,KAAK,CAAC;IAE9C;;;;;;;OAOG;IACH,KAAY,IAAI,CACd,KAAK,SAAS,MAAM,GAAG,MAAM,EAC7B,MAAM,SAAS,QAAQ,GAAG,QAAQ,IAChC,OAAO,WAAW,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAE5C;;;;OAIG;IACH,KAAY,GAAG,CAAC,SAAS,SAAS,SAAS,QAAQ,EAAE,GAAG,SAAS,QAAQ,EAAE,IACzE,OAAO,WAAW,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;CACtC;AAED;;;;;;;;;;;GAWG;AACH,QAAA,MAAM,GAAG,UACY,SAAS,SAAS,SAAS,QAAQ,EAAE,WAC7C,oBAAoB,eAChB,SAAS,KACrB,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC;IAInB;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;;;OAIG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;CAGN,CAAC;AAEF,OAAO,EAAE,GAAG,EAAE,CAAC"}
|
package/dist/src/sql.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { empty, join, raw, Sql, ident, literal, Slot } from "./core.js";
|
|
2
|
-
function slot(...args) {
|
|
3
|
-
return new sql.Slot(...args);
|
|
4
|
-
}
|
|
1
|
+
import { empty, join, raw, Sql, ident, literal, Slot, slot } from "./core.js";
|
|
5
2
|
/**
|
|
6
3
|
* テンプレートリテラルを使用して SQL クエリーを安全に構築するためのタグ関数です。
|
|
7
4
|
*
|
|
5
|
+
* @template TBindings クエリーに渡される生の値のタプル型です。
|
|
8
6
|
* @param strings テンプレートリテラルの静的な文字列部分の配列です。
|
|
9
7
|
* @param bindings テンプレートリテラルに埋め込まれた動的な値の配列です。
|
|
10
8
|
* @returns パラメーター化された SQL 情報を保持する Sql インスタンスを返します。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pgsql-template-tag",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "",
|
|
5
5
|
"homepage": "https://github.com/tai-kun/pgsql-template-tag",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"es-toolkit": "^1.46.1"
|
|
29
|
+
"es-toolkit": "^1.46.1",
|
|
30
|
+
"type-fest": "^5.6.0"
|
|
30
31
|
},
|
|
31
32
|
"devDependencies": {
|
|
32
33
|
"@tsconfig/node24": "^24.0.4",
|
package/src/core.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isPlainObject } from "es-toolkit/predicate";
|
|
2
|
+
import type { UnionToTuple } from "type-fest";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* SQL クエリー内で使用される値の型定義です。
|
|
@@ -11,21 +12,44 @@ export type Value = unknown;
|
|
|
11
12
|
// oxlint-disable-next-line typescript/no-redundant-type-constituents
|
|
12
13
|
export type RawValue = Value | Slot | Sql;
|
|
13
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Slot クラスを一意に識別するためのシンボルです。
|
|
17
|
+
*/
|
|
18
|
+
declare const SLOT_SYMBOL: unique symbol;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Slot クラスの基底となる型定義です。
|
|
22
|
+
*/
|
|
23
|
+
const SlotTypes = class {} as {
|
|
24
|
+
new (): {
|
|
25
|
+
/**
|
|
26
|
+
* このプロパティーは、TypeScript の `extends Slot` で `Slot` インスタンスのみに一致させるためにあります。そのため、`Slot` と同じプロパティーを持つオブジェクトに対して一致することはありません。
|
|
27
|
+
*/
|
|
28
|
+
readonly ["~kind"]: typeof SLOT_SYMBOL;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
14
32
|
/**
|
|
15
33
|
* スロットを表すクラスです。
|
|
16
34
|
*
|
|
17
35
|
* スロットは後から値を注入可能なプレースホルダーです。
|
|
36
|
+
*
|
|
37
|
+
* @template TName スロットの名前となる文字列リテラル型です。
|
|
38
|
+
* @template TValue スロットに許容される値の型です。
|
|
18
39
|
*/
|
|
19
|
-
export class Slot
|
|
40
|
+
export class Slot<
|
|
41
|
+
const TName extends string = string,
|
|
42
|
+
TValue extends RawValue = RawValue,
|
|
43
|
+
> extends SlotTypes {
|
|
20
44
|
/**
|
|
21
45
|
* スロット名です。
|
|
22
46
|
*/
|
|
23
|
-
public readonly name:
|
|
47
|
+
public readonly name: TName;
|
|
24
48
|
|
|
25
49
|
/**
|
|
26
50
|
* デフォルト値です。
|
|
27
51
|
*/
|
|
28
|
-
public readonly defaultValue:
|
|
52
|
+
public readonly defaultValue: TValue;
|
|
29
53
|
|
|
30
54
|
/**
|
|
31
55
|
* 新しい Slot インスタンスを初期化します。
|
|
@@ -33,16 +57,121 @@ export class Slot {
|
|
|
33
57
|
* @param name スロット名です。
|
|
34
58
|
* @param defaultValue デフォルト値です。
|
|
35
59
|
*/
|
|
36
|
-
public constructor(
|
|
60
|
+
public constructor(
|
|
61
|
+
...args: null extends TValue
|
|
62
|
+
? [name: TName, defaultValue?: TValue]
|
|
63
|
+
: [name: TName, defaultValue: TValue]
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
public constructor(name: TName, defaultValue?: RawValue) {
|
|
67
|
+
super();
|
|
68
|
+
|
|
37
69
|
if (arguments.length < 2) {
|
|
38
70
|
defaultValue = null;
|
|
39
71
|
}
|
|
40
72
|
|
|
41
73
|
this.name = name;
|
|
42
|
-
this.defaultValue = defaultValue;
|
|
74
|
+
this.defaultValue = defaultValue as TValue;
|
|
43
75
|
}
|
|
44
76
|
}
|
|
45
77
|
|
|
78
|
+
/**
|
|
79
|
+
* オブジェクトのバリューの型を抽出するヘルパー型です。
|
|
80
|
+
*
|
|
81
|
+
* @template T 対象となるオブジェクト型です。
|
|
82
|
+
*/
|
|
83
|
+
type $ValueOf<T> = T[keyof T];
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* スロットの配列から、再帰的に値をマージして型を決定します。
|
|
87
|
+
*
|
|
88
|
+
* @template TSlots スロットのタプル型です。
|
|
89
|
+
*/
|
|
90
|
+
type $MergeSlotValue<TSlots> = TSlots extends [
|
|
91
|
+
Slot<string, infer TValue>,
|
|
92
|
+
infer TSlot,
|
|
93
|
+
...infer TOtherSlots,
|
|
94
|
+
]
|
|
95
|
+
? TValue & $MergeSlotValue<[TSlot, ...TOtherSlots]>
|
|
96
|
+
: TSlots extends [Slot<string, infer TValue>]
|
|
97
|
+
? TValue
|
|
98
|
+
: never;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* RawValue の配列からスロット情報を抽出し、名前ごとのマップ型に変換します。
|
|
102
|
+
*
|
|
103
|
+
* @template TValues RawValue の読み取り専用配列型です。
|
|
104
|
+
*/
|
|
105
|
+
type $MapSlotValue<TValues extends readonly RawValue[]> =
|
|
106
|
+
TValues extends readonly (infer TSlot extends Slot)[]
|
|
107
|
+
? {
|
|
108
|
+
// `Slot<"id", string | number> | Slot<"id", string>` の場合、`(string | number) & (string)` となるように、各スロットの積集合をとります。
|
|
109
|
+
[TName in TSlot["name"]]: $MergeSlotValue<UnionToTuple<Extract<TSlot, Slot<TName>>>>;
|
|
110
|
+
}
|
|
111
|
+
: {};
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 指定された値のリストに対して、スロットを実際の内容で置き換えた型を生成します。
|
|
115
|
+
*
|
|
116
|
+
* @template TValues 置き換え対象の配列型です。
|
|
117
|
+
* @template TSlots スロット名と値のマップ型です。
|
|
118
|
+
*/
|
|
119
|
+
type $FillSlots<TValues, TSlots> = TValues extends [infer TValue, ...infer TOtherValues]
|
|
120
|
+
? [
|
|
121
|
+
TValue extends Slot<infer TName extends Extract<keyof TSlots, string>, infer TSlotValue>
|
|
122
|
+
? TSlotValue extends TSlots[TName]
|
|
123
|
+
? TSlotValue
|
|
124
|
+
: TValue
|
|
125
|
+
: TValue,
|
|
126
|
+
...$FillSlots<TOtherValues, TSlots>,
|
|
127
|
+
]
|
|
128
|
+
: [];
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* スロットを埋めるための部分的な引数型を定義します。
|
|
132
|
+
*
|
|
133
|
+
* @template TSlots スロット名と値のマップ型です。
|
|
134
|
+
*/
|
|
135
|
+
type _FillSlots<TSlots> =
|
|
136
|
+
| {
|
|
137
|
+
readonly [TName in Extract<keyof TSlots, string>]?: TSlots[TName];
|
|
138
|
+
}
|
|
139
|
+
| Iterable<
|
|
140
|
+
readonly [
|
|
141
|
+
name: $ValueOf<{
|
|
142
|
+
[TName in Extract<keyof TSlots, string>]: TName | Slot<TName, TSlots[TName]>;
|
|
143
|
+
}>,
|
|
144
|
+
value: $ValueOf<TSlots>,
|
|
145
|
+
]
|
|
146
|
+
>;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* すべてのスロットを埋めるために必要な引数型を定義します。
|
|
150
|
+
*
|
|
151
|
+
* @template TSlots スロット名と値のマップ型です。
|
|
152
|
+
*/
|
|
153
|
+
type _FillAllSlots<TSlots> = {
|
|
154
|
+
readonly [TName in Extract<keyof TSlots, string>]: TSlots[TName];
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* スロットの部分的な補完に使用する外部向けの型定義です。
|
|
159
|
+
*
|
|
160
|
+
* @template TValues RawValue の配列です。
|
|
161
|
+
*/
|
|
162
|
+
export type FillSlots<TValues extends readonly RawValue[] = readonly RawValue[]> = _FillSlots<
|
|
163
|
+
$MapSlotValue<TValues>
|
|
164
|
+
>;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* すべてのスロットの強制的な補完に使用する外部向けの型定義です。
|
|
168
|
+
*
|
|
169
|
+
* @template TValues RawValue の配列です。
|
|
170
|
+
*/
|
|
171
|
+
export type FillAllSlots<TValues extends readonly RawValue[] = readonly RawValue[]> = _FillAllSlots<
|
|
172
|
+
$MapSlotValue<TValues>
|
|
173
|
+
>;
|
|
174
|
+
|
|
46
175
|
/**
|
|
47
176
|
* Sql クラスの内部状態を管理するためのプライベートな型定義です。
|
|
48
177
|
*/
|
|
@@ -77,8 +206,10 @@ type PrivateState = {
|
|
|
77
206
|
* 安全な SQL クエリーを構築するためのクラスです。
|
|
78
207
|
*
|
|
79
208
|
* プレースホルダーを使用したパラメーター化クエリーを生成します。
|
|
209
|
+
*
|
|
210
|
+
* @template TRawBindings クエリーに渡される生の値のタプル型です。
|
|
80
211
|
*/
|
|
81
|
-
export class Sql {
|
|
212
|
+
export class Sql<const TRawBindings extends readonly RawValue[] = readonly RawValue[]> {
|
|
82
213
|
/**
|
|
83
214
|
* 構築された SQL クエリーテキストを取得します。
|
|
84
215
|
*
|
|
@@ -88,19 +219,19 @@ export class Sql {
|
|
|
88
219
|
*/
|
|
89
220
|
public get text(): string {
|
|
90
221
|
// キャッシュが存在しない場合にのみ、文字列を構築します。
|
|
91
|
-
if (this.
|
|
222
|
+
if (this.#state.text === undefined) {
|
|
92
223
|
let i = 0,
|
|
93
|
-
text = this.
|
|
224
|
+
text = this.#state.parts[0];
|
|
94
225
|
|
|
95
226
|
// 文字列の断片とプレースホルダー($1, $2...)を交互に結合します。
|
|
96
|
-
for (; i < this.
|
|
97
|
-
text += "$" + this.
|
|
227
|
+
for (; i < this.#state.phIds.length; i++) {
|
|
228
|
+
text += "$" + this.#state.phIds[i] + this.#state.parts[i + 1];
|
|
98
229
|
}
|
|
99
230
|
|
|
100
|
-
this.
|
|
231
|
+
this.#state.text = text;
|
|
101
232
|
}
|
|
102
233
|
|
|
103
|
-
return this.
|
|
234
|
+
return this.#state.text;
|
|
104
235
|
}
|
|
105
236
|
|
|
106
237
|
/**
|
|
@@ -111,7 +242,7 @@ export class Sql {
|
|
|
111
242
|
/**
|
|
112
243
|
* 内部状態を保持するためのプロパティーです。
|
|
113
244
|
*/
|
|
114
|
-
|
|
245
|
+
readonly #state: PrivateState;
|
|
115
246
|
|
|
116
247
|
/**
|
|
117
248
|
* 新しい Sql インスタンスを初期化します。
|
|
@@ -119,7 +250,7 @@ export class Sql {
|
|
|
119
250
|
* @param rawStrings SQL の断片となる文字列の配列です。
|
|
120
251
|
* @param rawBindings 文字列の間に挿入される値の配列です。
|
|
121
252
|
*/
|
|
122
|
-
public constructor(rawStrings: readonly string[], rawBindings:
|
|
253
|
+
public constructor(rawStrings: readonly string[], rawBindings: TRawBindings) {
|
|
123
254
|
if (rawStrings.length === 0) {
|
|
124
255
|
throw new TypeError("Expected at least 1 string");
|
|
125
256
|
}
|
|
@@ -187,19 +318,19 @@ export class Sql {
|
|
|
187
318
|
// バインディング値が Sql インスタンス(ネストされたクエリー)の場合の処理です。
|
|
188
319
|
if (child instanceof Sql) {
|
|
189
320
|
// 現在の最後の文字列断片に、ネストされた Sql の最初の断片を結合します。
|
|
190
|
-
strings[strings.length - 1] += child.
|
|
321
|
+
strings[strings.length - 1] += child.#state.parts[0];
|
|
191
322
|
|
|
192
323
|
// ネストされた Sql のプレースホルダーと値を再マッピングします。
|
|
193
|
-
for (let j = 0; j < child.
|
|
194
|
-
const childPlaceholderId = child.
|
|
324
|
+
for (let j = 0; j < child.#state.phIds.length; j++) {
|
|
325
|
+
const childPlaceholderId = child.#state.phIds[j]!;
|
|
195
326
|
const valueIndex = childPlaceholderId - 1;
|
|
196
327
|
const value = child.values[valueIndex]!;
|
|
197
328
|
|
|
198
|
-
const slot = child.
|
|
329
|
+
const slot = child.#state.idx2slot.get(valueIndex);
|
|
199
330
|
|
|
200
331
|
const placeholderId = slot !== undefined ? registerSlot(slot) : registerValue(value);
|
|
201
332
|
|
|
202
|
-
strings.push(child.
|
|
333
|
+
strings.push(child.#state.parts[j + 1]!);
|
|
203
334
|
placeholderIds.push(placeholderId);
|
|
204
335
|
}
|
|
205
336
|
|
|
@@ -215,26 +346,22 @@ export class Sql {
|
|
|
215
346
|
|
|
216
347
|
this.values = bindings;
|
|
217
348
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
slot2idx,
|
|
225
|
-
} satisfies PrivateState,
|
|
226
|
-
});
|
|
349
|
+
this.#state = {
|
|
350
|
+
parts: strings,
|
|
351
|
+
phIds: placeholderIds,
|
|
352
|
+
idx2slot,
|
|
353
|
+
slot2idx,
|
|
354
|
+
};
|
|
227
355
|
}
|
|
228
356
|
|
|
229
357
|
/**
|
|
230
|
-
*
|
|
358
|
+
* スロットを値で埋める内部メソッドです。
|
|
231
359
|
*
|
|
232
|
-
* @param slots
|
|
233
|
-
* @
|
|
360
|
+
* @param slots スロットのマップまたはエントリーの配列です。
|
|
361
|
+
* @param all 全てのスロットが埋まっているかチェックするかどうかです。
|
|
362
|
+
* @returns 新しい Sql インスタンスを返します。
|
|
234
363
|
*/
|
|
235
|
-
|
|
236
|
-
slots: { readonly [name: string]: Value } | Iterable<readonly [string | Slot, Value]>,
|
|
237
|
-
): Sql {
|
|
364
|
+
#fill(slots: FillSlots<Slot[]>, all: boolean): Sql {
|
|
238
365
|
if (isPlainObject(slots)) {
|
|
239
366
|
slots = Object.entries(slots);
|
|
240
367
|
} else {
|
|
@@ -242,13 +369,16 @@ export class Sql {
|
|
|
242
369
|
slots = new Map(slots);
|
|
243
370
|
}
|
|
244
371
|
|
|
245
|
-
const
|
|
372
|
+
const { parts, idx2slot, slot2idx } = this.#state;
|
|
373
|
+
const filled = new Set<number>();
|
|
374
|
+
const values = this.values.slice();
|
|
246
375
|
for (const [target, value] of new Map(slots)) {
|
|
247
376
|
let idxes: ReadonlySet<number> | undefined;
|
|
248
377
|
if (typeof target === "string") {
|
|
249
|
-
idxes =
|
|
378
|
+
idxes = slot2idx.get(target);
|
|
250
379
|
} else {
|
|
251
|
-
|
|
380
|
+
// インスタンスが直接指定された場合、全インデックスから一致するものを探します。
|
|
381
|
+
for (const [idx, slot] of idx2slot) {
|
|
252
382
|
if (slot === target) {
|
|
253
383
|
idxes ||= new Set();
|
|
254
384
|
(idxes as Set<number>).add(idx);
|
|
@@ -261,12 +391,57 @@ export class Sql {
|
|
|
261
391
|
continue;
|
|
262
392
|
}
|
|
263
393
|
|
|
394
|
+
// 該当するすべてのプレースホルダーインデックスを新しい値で更新します。
|
|
264
395
|
for (const idx of idxes) {
|
|
265
396
|
values[idx] = value;
|
|
397
|
+
filled.add(idx);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// 全て埋める必要がある場合、未解決のスロットが残っていないか検証します。
|
|
402
|
+
if (all) {
|
|
403
|
+
for (let idx = 0; idx < values.length; idx++) {
|
|
404
|
+
if (idx2slot.has(idx) && !filled.has(idx)) {
|
|
405
|
+
const missingSlots = new Set<string>();
|
|
406
|
+
missingSlots.add(idx2slot.get(idx)!.name);
|
|
407
|
+
for (; idx < values.length; idx++) {
|
|
408
|
+
if (idx2slot.has(idx)) {
|
|
409
|
+
missingSlots.add(idx2slot.get(idx)!.name);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
throw new Error(`Not all slots are filled. Missing: ${[...missingSlots].join(", ")}`);
|
|
414
|
+
}
|
|
266
415
|
}
|
|
267
416
|
}
|
|
268
417
|
|
|
269
|
-
return new Sql(
|
|
418
|
+
return new Sql(parts, values);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* スロットを値で埋めます。
|
|
423
|
+
*
|
|
424
|
+
* @template TSlots 指定されたスロットのマップ型です。
|
|
425
|
+
* @param slots スロットの値です。
|
|
426
|
+
* @returns スロットが埋められた新しい Sql インスタンスです。
|
|
427
|
+
*/
|
|
428
|
+
public fill<TSlots extends FillSlots<TRawBindings>>(
|
|
429
|
+
slots: TSlots,
|
|
430
|
+
): Sql<$FillSlots<TRawBindings, TSlots>> {
|
|
431
|
+
return this.#fill(slots, false);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* すべてのスロットを値で埋めます。
|
|
436
|
+
*
|
|
437
|
+
* @template TSlots 全てのスロットをカバーするマップ型です。
|
|
438
|
+
* @param slots スロットの値です。
|
|
439
|
+
* @returns スロットが埋められた新しい Sql インスタンスです。
|
|
440
|
+
*/
|
|
441
|
+
public fillAll<TSlots extends FillAllSlots<TRawBindings>>(
|
|
442
|
+
slots: TSlots,
|
|
443
|
+
): Sql<$FillSlots<TRawBindings, TSlots>> {
|
|
444
|
+
return this.#fill(slots, true);
|
|
270
445
|
}
|
|
271
446
|
|
|
272
447
|
/**
|
|
@@ -302,14 +477,14 @@ export class Sql {
|
|
|
302
477
|
* @param value SQL に含める生の文字列です。
|
|
303
478
|
* @returns 指定された文字列を持つ Sql インスタンスを返します。
|
|
304
479
|
*/
|
|
305
|
-
export function raw(value: string): Sql {
|
|
480
|
+
export function raw(value: string): Sql<[]> {
|
|
306
481
|
return new Sql([value], []);
|
|
307
482
|
}
|
|
308
483
|
|
|
309
484
|
/**
|
|
310
485
|
* 空の SQL インスタンスを表す定数です。
|
|
311
486
|
*/
|
|
312
|
-
export const empty: Sql = raw("");
|
|
487
|
+
export const empty: Sql<[]> = raw("");
|
|
313
488
|
|
|
314
489
|
/**
|
|
315
490
|
* 複数の SQL 断片や値を、指定されたセパレーターで結合します。
|
|
@@ -320,7 +495,10 @@ export const empty: Sql = raw("");
|
|
|
320
495
|
* @param separator 結合時に挿入される文字列です。デフォルトはカンマ(,)です。
|
|
321
496
|
* @returns 結合された新しい Sql インスタンスを返します。
|
|
322
497
|
*/
|
|
323
|
-
export function join
|
|
498
|
+
export function join<const TValues extends readonly RawValue[]>(
|
|
499
|
+
values: TValues,
|
|
500
|
+
separator: string | undefined = ",",
|
|
501
|
+
): Sql<TValues> {
|
|
324
502
|
if (values.length === 0) {
|
|
325
503
|
return empty;
|
|
326
504
|
}
|
|
@@ -361,3 +539,21 @@ const SINGLE_QUOTE_REGEX = /'/g;
|
|
|
361
539
|
export function literal(value: string): string {
|
|
362
540
|
return "'" + value.replace(SINGLE_QUOTE_REGEX, "''") + "'";
|
|
363
541
|
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* 新しい Slot インスタンスを作成します。
|
|
545
|
+
*
|
|
546
|
+
* @template TName スロットの名前となる文字列リテラル型です。
|
|
547
|
+
* @template TValue スロットに許容される値の型です。
|
|
548
|
+
* @param name スロット名です。
|
|
549
|
+
* @param defaultValue デフォルト値です。
|
|
550
|
+
* @returns 作成された新しい Slot インスタンスです。
|
|
551
|
+
*/
|
|
552
|
+
export function slot<const TName extends string, TValue extends RawValue = RawValue>(
|
|
553
|
+
name: TName,
|
|
554
|
+
defaultValue?: TValue,
|
|
555
|
+
): Slot<TName, TValue>;
|
|
556
|
+
|
|
557
|
+
export function slot(...args: [any]): Slot {
|
|
558
|
+
return new Slot(...args);
|
|
559
|
+
}
|
package/src/index.ts
CHANGED
package/src/sql.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { empty, join, raw, Sql, ident, literal, Slot } from "./core.js";
|
|
1
|
+
import { empty, join, raw, Sql, ident, literal, Slot, RawValue, slot } from "./core.js";
|
|
2
2
|
|
|
3
3
|
namespace sql {
|
|
4
4
|
/**
|
|
@@ -15,31 +15,28 @@ namespace sql {
|
|
|
15
15
|
* スロットを表すクラスです。
|
|
16
16
|
*
|
|
17
17
|
* スロットは後から値を注入可能なプレースホルダーです。
|
|
18
|
+
*
|
|
19
|
+
* @template TName スロットの名前となる文字列リテラル型です。
|
|
20
|
+
* @template TValue スロットに許容される値の型です。
|
|
18
21
|
*/
|
|
19
|
-
export type Slot
|
|
22
|
+
export type Slot<
|
|
23
|
+
TName extends string = string,
|
|
24
|
+
TValue extends RawValue = RawValue,
|
|
25
|
+
> = import("./core.js").Slot<TName, TValue>;
|
|
20
26
|
|
|
21
27
|
/**
|
|
22
28
|
* 安全な SQL クエリーを構築するためのクラス型です。
|
|
29
|
+
*
|
|
30
|
+
* @template TBindings クエリーに渡される生の値のタプル型です。
|
|
23
31
|
*/
|
|
24
|
-
export type Sql =
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* 新しい Slot インスタンスを作成します。
|
|
29
|
-
*
|
|
30
|
-
* @param name スロット名です。
|
|
31
|
-
* @param defaultValue デフォルト値です。
|
|
32
|
-
* @returns 作成された新しい Slot インスタンスです。
|
|
33
|
-
*/
|
|
34
|
-
function slot(name: string, defaultValue?: sql.Value): sql.Slot;
|
|
35
|
-
|
|
36
|
-
function slot(...args: [any]): sql.Slot {
|
|
37
|
-
return new sql.Slot(...args);
|
|
32
|
+
export type Sql<TBindings extends readonly RawValue[] = readonly RawValue[]> =
|
|
33
|
+
import("./core.js").Sql<TBindings>;
|
|
38
34
|
}
|
|
39
35
|
|
|
40
36
|
/**
|
|
41
37
|
* テンプレートリテラルを使用して SQL クエリーを安全に構築するためのタグ関数です。
|
|
42
38
|
*
|
|
39
|
+
* @template TBindings クエリーに渡される生の値のタプル型です。
|
|
43
40
|
* @param strings テンプレートリテラルの静的な文字列部分の配列です。
|
|
44
41
|
* @param bindings テンプレートリテラルに埋め込まれた動的な値の配列です。
|
|
45
42
|
* @returns パラメーター化された SQL 情報を保持する Sql インスタンスを返します。
|
|
@@ -49,7 +46,10 @@ function slot(...args: [any]): sql.Slot {
|
|
|
49
46
|
* ```
|
|
50
47
|
*/
|
|
51
48
|
const sql = /*#__PURE__*/ Object.assign(
|
|
52
|
-
function sql
|
|
49
|
+
function sql<const TBindings extends readonly RawValue[]>(
|
|
50
|
+
strings: TemplateStringsArray,
|
|
51
|
+
...bindings: TBindings
|
|
52
|
+
): sql.Sql<TBindings> {
|
|
53
53
|
return new Sql(strings, bindings);
|
|
54
54
|
},
|
|
55
55
|
{
|