@leuduan/block 0.0.0-stage → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +84 -2
- package/dist/build.d.ts +40 -0
- package/dist/build.js +171 -0
- package/dist/define.d.ts +8 -0
- package/dist/define.js +25 -0
- package/dist/descriptor.d.ts +66 -0
- package/dist/descriptor.js +19 -0
- package/dist/expr.d.ts +37 -0
- package/dist/expr.js +159 -0
- package/dist/helpers.d.ts +28 -0
- package/dist/helpers.js +98 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +19 -0
- package/dist/line.d.ts +79 -0
- package/dist/line.js +450 -0
- package/dist/pipe.d.ts +39 -0
- package/dist/pipe.js +106 -0
- package/dist/problems.d.ts +7 -0
- package/dist/problems.js +129 -0
- package/dist/query.d.ts +37 -0
- package/dist/query.js +164 -0
- package/dist/sql.d.ts +72 -0
- package/dist/sql.js +169 -0
- package/dist/t.d.ts +8 -0
- package/dist/t.js +17 -0
- package/package.json +36 -3
package/dist/sql.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// Port of py/dbox/dbox/block/sql.py
|
|
2
|
+
// The immutable query model (SqlSegment, Pipe, TableExpression, QueryExpression and its queries) and the `sql` tag.
|
|
3
|
+
import * as l from './line.js';
|
|
4
|
+
import { DEFAULT_OPTIONS } from './line.js';
|
|
5
|
+
import * as h from './helpers.js';
|
|
6
|
+
export function mapEntries(m) {
|
|
7
|
+
if (m instanceof Map)
|
|
8
|
+
return [...m];
|
|
9
|
+
return Object.entries(m);
|
|
10
|
+
}
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Segments: one class each for pipes and table expressions, both just a line
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
export class SqlSegment {
|
|
15
|
+
format(options = DEFAULT_OPTIONS) {
|
|
16
|
+
return l.formatLine(this.toLine(), options);
|
|
17
|
+
}
|
|
18
|
+
toString() {
|
|
19
|
+
return this.format();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** One `|>` step. Built by the `p` factories. */
|
|
23
|
+
export class Pipe extends SqlSegment {
|
|
24
|
+
line;
|
|
25
|
+
constructor(line) {
|
|
26
|
+
super();
|
|
27
|
+
this.line = line;
|
|
28
|
+
}
|
|
29
|
+
toLine() {
|
|
30
|
+
return this.line;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Something that goes after FROM or JOIN. Built by the `t` factories. */
|
|
34
|
+
export class TableExpression extends SqlSegment {
|
|
35
|
+
line;
|
|
36
|
+
constructor(line) {
|
|
37
|
+
super();
|
|
38
|
+
this.line = line;
|
|
39
|
+
}
|
|
40
|
+
toLine() {
|
|
41
|
+
return this.line;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// QueryExpression — base query class
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
export class QueryExpression extends SqlSegment {
|
|
48
|
+
/** Default toTableExpression wraps in a parenthesized subquery. */
|
|
49
|
+
toTableExpression() {
|
|
50
|
+
return new TableExpression(l.paren(this.toLine()));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// Concrete QueryExpression classes
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
export class RawQuery extends QueryExpression {
|
|
57
|
+
sql;
|
|
58
|
+
constructor(sql) {
|
|
59
|
+
super();
|
|
60
|
+
this.sql = sql;
|
|
61
|
+
}
|
|
62
|
+
toLine() {
|
|
63
|
+
return this.sql;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export class NamedQueryExpression extends QueryExpression {
|
|
67
|
+
name;
|
|
68
|
+
constructor(name) {
|
|
69
|
+
super();
|
|
70
|
+
this.name = name;
|
|
71
|
+
}
|
|
72
|
+
toLine() {
|
|
73
|
+
return l.lit(this.name);
|
|
74
|
+
}
|
|
75
|
+
/** Named expressions refer to CTEs/tables — no subquery wrapping. */
|
|
76
|
+
toTableExpression() {
|
|
77
|
+
return new TableExpression(this.toLine());
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
class FromQuery extends QueryExpression {
|
|
81
|
+
tableExpression;
|
|
82
|
+
constructor(tableExpression) {
|
|
83
|
+
super();
|
|
84
|
+
this.tableExpression = tableExpression;
|
|
85
|
+
}
|
|
86
|
+
toLine() {
|
|
87
|
+
return l.indentNext([l.keyword('FROM'), this.tableExpression.toLine()]);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** `FROM table`; a string is emitted as given, so quote identifiers with `t.path(id, true)` or `e.ident`. */
|
|
91
|
+
export function from(tableExpression) {
|
|
92
|
+
return new FromQuery(typeof tableExpression === 'string' ? new TableExpression(l.lit(tableExpression)) : tableExpression);
|
|
93
|
+
}
|
|
94
|
+
export class GoogleSqlQuery extends QueryExpression {
|
|
95
|
+
ctes;
|
|
96
|
+
queryExpression;
|
|
97
|
+
orderByLine;
|
|
98
|
+
limitOffset;
|
|
99
|
+
pipes;
|
|
100
|
+
constructor({ ctes = new Map(), queryExpression, orderByLine, limitOffset, pipes = [] }) {
|
|
101
|
+
super();
|
|
102
|
+
this.ctes = ctes;
|
|
103
|
+
this.queryExpression = queryExpression;
|
|
104
|
+
this.orderByLine = orderByLine;
|
|
105
|
+
this.limitOffset = limitOffset;
|
|
106
|
+
this.pipes = pipes;
|
|
107
|
+
}
|
|
108
|
+
toLine() {
|
|
109
|
+
// A nested GoogleSqlQuery is parenthesized (query.py:126-127)
|
|
110
|
+
const queryLine = this.queryExpression instanceof GoogleSqlQuery
|
|
111
|
+
? l.paren(this.queryExpression.toLine())
|
|
112
|
+
: this.queryExpression.toLine();
|
|
113
|
+
return l.stack([
|
|
114
|
+
this.ctes.size > 0 ? h.ctes(this.ctes) : null,
|
|
115
|
+
queryLine,
|
|
116
|
+
this.orderByLine,
|
|
117
|
+
this.limitOffset,
|
|
118
|
+
...this.pipes.map((pipe) => pipe.toLine()),
|
|
119
|
+
]);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Apply `|>` steps to a query. Pipe syntax is GoogleSQL's, so the result is a GoogleSqlQuery. A GoogleSqlQuery takes
|
|
124
|
+
* the steps on its own pipe list (only its outer wrapper is flattened); anything else is wrapped. The input is not
|
|
125
|
+
* changed.
|
|
126
|
+
*/
|
|
127
|
+
export function pipe(query, ...steps) {
|
|
128
|
+
if (query instanceof GoogleSqlQuery) {
|
|
129
|
+
return new GoogleSqlQuery({
|
|
130
|
+
ctes: query.ctes,
|
|
131
|
+
queryExpression: query.queryExpression,
|
|
132
|
+
orderByLine: query.orderByLine,
|
|
133
|
+
limitOffset: query.limitOffset,
|
|
134
|
+
pipes: [...query.pipes, ...steps],
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return new GoogleSqlQuery({ queryExpression: query, pipes: steps });
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Tagged template for raw SQL fragments.
|
|
141
|
+
* - string/number interpolations inline as raw SQL text (no quoting).
|
|
142
|
+
* - Line interpolations embed structurally (breaker recurses into them).
|
|
143
|
+
* - SqlSegment (QueryExpression etc.) interpolations use .toLine().
|
|
144
|
+
* For quoted identifiers use ident(name); for string literals use e.literalString(x).
|
|
145
|
+
*/
|
|
146
|
+
export function sql(strings, ...values) {
|
|
147
|
+
const parts = [];
|
|
148
|
+
strings.forEach((chunk, i) => {
|
|
149
|
+
if (chunk.length > 0)
|
|
150
|
+
parts.push(l.ensure(chunk));
|
|
151
|
+
if (i < values.length)
|
|
152
|
+
parts.push(interpToLine(values[i]));
|
|
153
|
+
});
|
|
154
|
+
const clean = parts.filter((p) => p != null);
|
|
155
|
+
if (clean.length === 0)
|
|
156
|
+
return l.lit('');
|
|
157
|
+
if (clean.length === 1)
|
|
158
|
+
return clean[0];
|
|
159
|
+
return l.combine(clean, { sep: l.BLANK });
|
|
160
|
+
}
|
|
161
|
+
function interpToLine(v) {
|
|
162
|
+
if (v instanceof l.Line)
|
|
163
|
+
return v;
|
|
164
|
+
if (v instanceof SqlSegment)
|
|
165
|
+
return v.toLine();
|
|
166
|
+
if (typeof v === 'number')
|
|
167
|
+
return l.lit(String(v));
|
|
168
|
+
return l.lit(v); // plain string → raw SQL fragment
|
|
169
|
+
}
|
package/dist/t.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type LineOrStr } from './line.js';
|
|
2
|
+
import { type QueryExpression, TableExpression } from './sql.js';
|
|
3
|
+
/** A table path; as given unless `quote`, which quotes the whole path as one identifier (see `e.ident`). */
|
|
4
|
+
export declare function path(id: string, quote?: boolean): TableExpression;
|
|
5
|
+
/** A query in parentheses. */
|
|
6
|
+
export declare function subquery(query: QueryExpression): TableExpression;
|
|
7
|
+
export declare function raw(line: LineOrStr): TableExpression;
|
|
8
|
+
//# sourceMappingURL=t.d.ts.map
|
package/dist/t.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Port of py/dbox/dbox/block/t.py
|
|
2
|
+
// Table expressions: what goes after FROM or JOIN.
|
|
3
|
+
import { ident } from './expr.js';
|
|
4
|
+
import * as l from './line.js';
|
|
5
|
+
import {} from './line.js';
|
|
6
|
+
import { TableExpression } from './sql.js';
|
|
7
|
+
/** A table path; as given unless `quote`, which quotes the whole path as one identifier (see `e.ident`). */
|
|
8
|
+
export function path(id, quote = false) {
|
|
9
|
+
return new TableExpression(quote ? ident(id) : l.lit(id));
|
|
10
|
+
}
|
|
11
|
+
/** A query in parentheses. */
|
|
12
|
+
export function subquery(query) {
|
|
13
|
+
return new TableExpression(l.paren(query.toLine()));
|
|
14
|
+
}
|
|
15
|
+
export function raw(line) {
|
|
16
|
+
return new TableExpression(l.ensure(line));
|
|
17
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leuduan/block",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "SQL builder and renderer for GoogleSQL",
|
|
5
|
+
"license": "ISC",
|
|
6
|
+
"author": "Duan Leu",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"!dist/**/*.map"
|
|
12
|
+
],
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "22.19.1",
|
|
21
|
+
"typescript": "^7.0.2",
|
|
22
|
+
"vitest": "4.1.5",
|
|
23
|
+
"@leuduan/tsconfig": "^1.0.0"
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/leuduan/luna.git",
|
|
31
|
+
"directory": "js/packages/block"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"clean": "rm -rf dist tsconfig.tsbuildinfo tsconfig.build.tsbuildinfo",
|
|
35
|
+
"build": "tsc --build tsconfig.build.json",
|
|
36
|
+
"typecheck": "tsc --noEmit",
|
|
37
|
+
"test": "vitest run"
|
|
38
|
+
}
|
|
6
39
|
}
|