@telorun/sql 0.8.0 → 0.9.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/README.md +2 -141
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/sql-command-controller.js +2 -1
- package/dist/sql-connection-ref.d.ts +10 -7
- package/dist/sql-connection-ref.js +13 -22
- package/dist/sql-migrations-controller.js +1 -1
- package/dist/sql-query-controller.js +5 -3
- package/dist/sql-selection-controller.js +2 -1
- package/dist/sql-transaction-controller.js +1 -2
- package/package.json +2 -2
- package/src/index.ts +1 -1
- package/src/sql-command-controller.ts +3 -1
- package/src/sql-connection-ref.ts +14 -33
- package/src/sql-migrations-controller.ts +5 -1
- package/src/sql-query-controller.ts +10 -2
- package/src/sql-selection-controller.ts +3 -1
- package/src/sql-transaction-controller.ts +5 -2
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Built to be language-agnostic and infinitely extensible.
|
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
# Reconcile your manifest into a running backend
|
|
26
|
-
$ telo ./examples/hello-api
|
|
26
|
+
$ telo ./examples/hello-api
|
|
27
27
|
|
|
28
28
|
{"level":30,"time":1771610393008,"pid":1310178,"hostname":"dev","msg":"Server listening at http://127.0.0.1:8844"}
|
|
29
29
|
```
|
|
@@ -44,146 +44,7 @@ $ telo ./examples/hello-api.yaml
|
|
|
44
44
|
|
|
45
45
|
## Example manifest
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
```yaml
|
|
50
|
-
kind: Telo.Application
|
|
51
|
-
metadata:
|
|
52
|
-
name: feedback
|
|
53
|
-
version: 1.0.0
|
|
54
|
-
description: |
|
|
55
|
-
A complete feedback collection REST API — no code, pure YAML.
|
|
56
|
-
Persists entries to SQLite and serves them over HTTP.
|
|
57
|
-
imports:
|
|
58
|
-
Http: std/http-server@0.12.0
|
|
59
|
-
Sql: std/sql@0.9.2
|
|
60
|
-
targets:
|
|
61
|
-
- !ref Migrations
|
|
62
|
-
- !ref Server
|
|
63
|
-
---
|
|
64
|
-
# SQLite database — swap driver/host/database for PostgreSQL with zero YAML changes
|
|
65
|
-
kind: Sql.Connection
|
|
66
|
-
metadata:
|
|
67
|
-
name: Db
|
|
68
|
-
driver: sqlite
|
|
69
|
-
file: ./tmp/feedback.db
|
|
70
|
-
---
|
|
71
|
-
# Migrations: applied automatically before the server starts
|
|
72
|
-
kind: Sql.Migrations
|
|
73
|
-
metadata:
|
|
74
|
-
name: Migrations
|
|
75
|
-
connection: !ref Db
|
|
76
|
-
---
|
|
77
|
-
kind: Sql.Migration
|
|
78
|
-
metadata:
|
|
79
|
-
name: Migration_20260413_182154_CreateFeedback
|
|
80
|
-
version: 20260413_182154_CreateFeedback
|
|
81
|
-
sql: |
|
|
82
|
-
CREATE TABLE IF NOT EXISTS feedback (
|
|
83
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
84
|
-
text TEXT NOT NULL,
|
|
85
|
-
source TEXT,
|
|
86
|
-
score INTEGER NOT NULL DEFAULT 0,
|
|
87
|
-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
88
|
-
)
|
|
89
|
-
---
|
|
90
|
-
kind: Http.Server
|
|
91
|
-
metadata:
|
|
92
|
-
name: Server
|
|
93
|
-
baseUrl: http://localhost:8844
|
|
94
|
-
port: 8844
|
|
95
|
-
logger: true
|
|
96
|
-
openapi:
|
|
97
|
-
info:
|
|
98
|
-
title: Feedback API
|
|
99
|
-
version: 1.0.0
|
|
100
|
-
mounts:
|
|
101
|
-
- path: /v1
|
|
102
|
-
mount: !ref FeedbackRoutes
|
|
103
|
-
---
|
|
104
|
-
kind: Http.Api
|
|
105
|
-
metadata:
|
|
106
|
-
name: FeedbackRoutes
|
|
107
|
-
routes:
|
|
108
|
-
# POST /v1/feedback — insert a new entry, score derived from body length heuristic
|
|
109
|
-
- request:
|
|
110
|
-
path: /feedback
|
|
111
|
-
method: POST
|
|
112
|
-
schema:
|
|
113
|
-
body:
|
|
114
|
-
type: object
|
|
115
|
-
properties:
|
|
116
|
-
text:
|
|
117
|
-
type: string
|
|
118
|
-
minLength: 1
|
|
119
|
-
source:
|
|
120
|
-
type: string
|
|
121
|
-
required: [ text ]
|
|
122
|
-
handler:
|
|
123
|
-
kind: Sql.Exec
|
|
124
|
-
connection: !ref Db
|
|
125
|
-
inputs:
|
|
126
|
-
sql: "INSERT INTO feedback (text, source, score) VALUES (?, ?, ?)"
|
|
127
|
-
bindings:
|
|
128
|
-
- "${{ request.body.text }}"
|
|
129
|
-
- "${{ request.body.source }}"
|
|
130
|
-
- "${{ size(request.body.text) }}"
|
|
131
|
-
response:
|
|
132
|
-
- status: 201
|
|
133
|
-
headers:
|
|
134
|
-
Content-Type: application/json
|
|
135
|
-
body:
|
|
136
|
-
ok: true
|
|
137
|
-
message: Feedback received
|
|
138
|
-
|
|
139
|
-
# GET /v1/feedback — list all entries, newest first
|
|
140
|
-
- request:
|
|
141
|
-
path: /feedback
|
|
142
|
-
method: GET
|
|
143
|
-
handler:
|
|
144
|
-
kind: Sql.Select
|
|
145
|
-
connection: !ref Db
|
|
146
|
-
from: feedback
|
|
147
|
-
columns: [ id, text, source, score, created_at ]
|
|
148
|
-
orderBy:
|
|
149
|
-
- { column: created_at, direction: desc }
|
|
150
|
-
response:
|
|
151
|
-
- status: 200
|
|
152
|
-
headers:
|
|
153
|
-
Content-Type: application/json
|
|
154
|
-
body: "${{ result.rows }}"
|
|
155
|
-
|
|
156
|
-
# GET /v1/feedback/{id} — fetch a single entry
|
|
157
|
-
- request:
|
|
158
|
-
path: /feedback/{id}
|
|
159
|
-
method: GET
|
|
160
|
-
schema:
|
|
161
|
-
params:
|
|
162
|
-
type: object
|
|
163
|
-
properties:
|
|
164
|
-
id:
|
|
165
|
-
type: integer
|
|
166
|
-
required: [ id ]
|
|
167
|
-
handler:
|
|
168
|
-
kind: Sql.Select
|
|
169
|
-
connection: !ref Db
|
|
170
|
-
from: feedback
|
|
171
|
-
columns: [ id, text, source, score, created_at ]
|
|
172
|
-
where:
|
|
173
|
-
- { column: id, op: "=", value: "${{ request.params.id }}" }
|
|
174
|
-
response:
|
|
175
|
-
- status: 200
|
|
176
|
-
when: "size(result.rows) > 0"
|
|
177
|
-
headers:
|
|
178
|
-
Content-Type: application/json
|
|
179
|
-
body: "${{ result.rows[0] }}"
|
|
180
|
-
- status: 404
|
|
181
|
-
headers:
|
|
182
|
-
Content-Type: application/json
|
|
183
|
-
body:
|
|
184
|
-
ok: false
|
|
185
|
-
message: Not found
|
|
186
|
-
```
|
|
47
|
+
See [examples/](./examples/) for a list of working applications.
|
|
187
48
|
|
|
188
49
|
## Status
|
|
189
50
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { SqlConnectionResource, createSqlConnection, type SqlDriver, type PlaceholderStyle, } from "./sql-connection-controller.js";
|
|
2
|
-
export { resolveSqlConnection } from "./sql-connection-ref.js";
|
|
2
|
+
export { isSqlConnection, resolveSqlConnection } from "./sql-connection-ref.js";
|
|
3
3
|
export type { SqliteDb, SqliteStatement } from "./sqlite-driver-interface.js";
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { SqlConnectionResource, createSqlConnection, } from "./sql-connection-controller.js";
|
|
2
|
-
export { resolveSqlConnection } from "./sql-connection-ref.js";
|
|
2
|
+
export { isSqlConnection, resolveSqlConnection } from "./sql-connection-ref.js";
|
|
@@ -10,7 +10,8 @@ class SqlCommandResource {
|
|
|
10
10
|
async invoke(input) {
|
|
11
11
|
const m = this.manifest;
|
|
12
12
|
const ctx = this.ctx;
|
|
13
|
-
const connection = resolveSqlConnection(m.connection, ctx)
|
|
13
|
+
const connection = resolveSqlConnection(m.connection, ctx, () => `Sql.Command "${m.metadata.name}": 'connection'`) ??
|
|
14
|
+
m.transaction?.getConnection();
|
|
14
15
|
if (!connection) {
|
|
15
16
|
throw new Error("Sql: either 'connection' or 'transaction' must be set");
|
|
16
17
|
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import type { ResourceContext } from "@telorun/sdk";
|
|
1
|
+
import type { KindRef, ResourceContext } from "@telorun/sdk";
|
|
2
2
|
import type { SqlConnectionResource } from "./sql-connection-controller.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
/** True when a value already exposes the connection contract (Phase-5 injected). */
|
|
4
|
+
export declare function isSqlConnection(value: unknown): value is SqlConnectionResource;
|
|
5
|
+
/**
|
|
6
|
+
* Resolve a `connection` `!ref` field to a live connection. The slot is optional
|
|
7
|
+
* — an unset one yields `undefined` so the caller can fall back to a `transaction`
|
|
8
|
+
* — but a slot that IS set must resolve. `describe` names the owning resource and
|
|
9
|
+
* slot, so the failure points at a concrete manifest location.
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveSqlConnection(value: SqlConnectionResource | KindRef<SqlConnectionResource> | undefined, ctx: ResourceContext, describe: () => string): SqlConnectionResource | undefined;
|
|
@@ -1,24 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/** True when a value already exposes the connection contract (Phase-5 injected). */
|
|
2
|
+
export function isSqlConnection(value) {
|
|
3
|
+
return typeof value?.execute === "function";
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Resolve a `connection` `!ref` field to a live connection. The slot is optional
|
|
7
|
+
* — an unset one yields `undefined` so the caller can fall back to a `transaction`
|
|
8
|
+
* — but a slot that IS set must resolve. `describe` names the owning resource and
|
|
9
|
+
* slot, so the failure points at a concrete manifest location.
|
|
10
|
+
*/
|
|
11
|
+
export function resolveSqlConnection(value, ctx, describe) {
|
|
12
|
+
if (!value)
|
|
3
13
|
return undefined;
|
|
4
|
-
|
|
5
|
-
if (typeof value.execute === "function") {
|
|
6
|
-
return value;
|
|
7
|
-
}
|
|
8
|
-
const ref = value;
|
|
9
|
-
if (typeof ref.name !== "string") {
|
|
10
|
-
throw new Error("Sql: invalid connection reference");
|
|
11
|
-
}
|
|
12
|
-
// Cross-module reference (`!ref Alias.Connection`): a connection resolved
|
|
13
|
-
// inside a nested library is not Phase-5-injected, so the controller receives
|
|
14
|
-
// the raw `{name, alias}` ref and must route through the import's exported
|
|
15
|
-
// scope rather than a bare local lookup.
|
|
16
|
-
if (ref.alias && ref.alias !== "Self") {
|
|
17
|
-
const instance = ctx.moduleContext.resolveImportedInstance(ref.alias, ref.name);
|
|
18
|
-
if (typeof instance?.execute !== "function") {
|
|
19
|
-
throw new Error(`Sql: connection reference '${ref.alias}.${ref.name}' did not resolve to an exported connection instance.`);
|
|
20
|
-
}
|
|
21
|
-
return instance;
|
|
22
|
-
}
|
|
23
|
-
return ctx.moduleContext.getInstance(ref.name);
|
|
14
|
+
return ctx.resolveRef(value, isSqlConnection, describe, "std/sql#Connection");
|
|
24
15
|
}
|
|
@@ -32,7 +32,7 @@ class SqlMigrationsResource {
|
|
|
32
32
|
this.ctx = ctx;
|
|
33
33
|
}
|
|
34
34
|
async run() {
|
|
35
|
-
const conn = resolveSqlConnection(this.manifest.connection, this.ctx) ?? failMissingConnection();
|
|
35
|
+
const conn = resolveSqlConnection(this.manifest.connection, this.ctx, () => `Sql.Migrations "${this.manifest.metadata.name}": 'connection'`) ?? failMissingConnection();
|
|
36
36
|
const migrations = {};
|
|
37
37
|
// Legacy: standalone `Sql.Migration` resources in the same module scope.
|
|
38
38
|
for (const [, { resource }] of this.ctx.moduleContext.resourceInstances) {
|
|
@@ -10,13 +10,15 @@ class SqlQueryResource {
|
|
|
10
10
|
async invoke(input) {
|
|
11
11
|
const m = this.manifest;
|
|
12
12
|
const ctx = this.ctx;
|
|
13
|
-
const connection = resolveConnection(m.connection, m.transaction, ctx);
|
|
13
|
+
const connection = resolveConnection(m.connection, m.transaction, ctx, () => `Sql.Query "${m.metadata.name}": 'connection'`);
|
|
14
14
|
const result = await runSql(connection, m.transaction, input, ctx);
|
|
15
15
|
return { rows: result.rows, rowCount: result.rows.length };
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
-
function resolveConnection(connection, transaction, ctx) {
|
|
19
|
-
return (resolveSqlConnection(connection, ctx
|
|
18
|
+
function resolveConnection(connection, transaction, ctx, describe) {
|
|
19
|
+
return (resolveSqlConnection(connection, ctx, describe) ??
|
|
20
|
+
transaction?.getConnection() ??
|
|
21
|
+
failMissingConnection());
|
|
20
22
|
}
|
|
21
23
|
function failMissingConnection() {
|
|
22
24
|
throw new Error("Sql: either 'connection' or 'transaction' must be set");
|
|
@@ -19,7 +19,8 @@ class SqlSelectionResource {
|
|
|
19
19
|
const having = ctx.expandValue(m.having ?? [], expandCtx);
|
|
20
20
|
const limit = m.limit != null ? ctx.expandValue(m.limit, expandCtx) : undefined;
|
|
21
21
|
const offset = m.offset != null ? ctx.expandValue(m.offset, expandCtx) : undefined;
|
|
22
|
-
const connection = resolveSqlConnection(m.connection, ctx)
|
|
22
|
+
const connection = resolveSqlConnection(m.connection, ctx, () => `Sql.Selection "${m.metadata.name}": 'connection'`) ??
|
|
23
|
+
m.transaction?.getConnection();
|
|
23
24
|
if (!connection) {
|
|
24
25
|
throw new Error("Sql.Selection: either 'connection' or 'transaction' must be set");
|
|
25
26
|
}
|
|
@@ -8,8 +8,7 @@ export class SqlTransactionResource {
|
|
|
8
8
|
this.ctx = ctx;
|
|
9
9
|
}
|
|
10
10
|
getConnection() {
|
|
11
|
-
return (resolveSqlConnection(this.manifest.connection, this.ctx) ??
|
|
12
|
-
failMissingConnection(this.manifest.metadata.name));
|
|
11
|
+
return (resolveSqlConnection(this.manifest.connection, this.ctx, () => `Sql.Transaction "${this.manifest.metadata.name}": 'connection'`) ?? failMissingConnection(this.manifest.metadata.name));
|
|
13
12
|
}
|
|
14
13
|
assertActive() {
|
|
15
14
|
if (!currentTxId()) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telorun/sql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Telo SQL module - SQL database resource kinds for Telo manifests.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"telo",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^20.0.0",
|
|
72
72
|
"typescript": "^5.0.0",
|
|
73
|
-
"@telorun/sdk": "0.
|
|
73
|
+
"@telorun/sdk": "0.54.0"
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
76
|
"@telorun/sdk": "*"
|
package/src/index.ts
CHANGED
|
@@ -4,5 +4,5 @@ export {
|
|
|
4
4
|
type SqlDriver,
|
|
5
5
|
type PlaceholderStyle,
|
|
6
6
|
} from "./sql-connection-controller.js";
|
|
7
|
-
export { resolveSqlConnection } from "./sql-connection-ref.js";
|
|
7
|
+
export { isSqlConnection, resolveSqlConnection } from "./sql-connection-ref.js";
|
|
8
8
|
export type { SqliteDb, SqliteStatement } from "./sqlite-driver-interface.js";
|
|
@@ -25,7 +25,9 @@ class SqlCommandResource implements ResourceInstance {
|
|
|
25
25
|
const m = this.manifest;
|
|
26
26
|
const ctx = this.ctx;
|
|
27
27
|
|
|
28
|
-
const connection =
|
|
28
|
+
const connection =
|
|
29
|
+
resolveSqlConnection(m.connection, ctx, () => `Sql.Command "${m.metadata.name}": 'connection'`) ??
|
|
30
|
+
m.transaction?.getConnection();
|
|
29
31
|
if (!connection) {
|
|
30
32
|
throw new Error("Sql: either 'connection' or 'transaction' must be set");
|
|
31
33
|
}
|
|
@@ -1,41 +1,22 @@
|
|
|
1
|
-
import type { ResourceContext } from "@telorun/sdk";
|
|
1
|
+
import type { KindRef, ResourceContext } from "@telorun/sdk";
|
|
2
2
|
import type { SqlConnectionResource } from "./sql-connection-controller.js";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
/** True when a value already exposes the connection contract (Phase-5 injected). */
|
|
5
|
+
export function isSqlConnection(value: unknown): value is SqlConnectionResource {
|
|
6
|
+
return typeof (value as SqlConnectionResource | undefined)?.execute === "function";
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Resolve a `connection` `!ref` field to a live connection. The slot is optional
|
|
11
|
+
* — an unset one yields `undefined` so the caller can fall back to a `transaction`
|
|
12
|
+
* — but a slot that IS set must resolve. `describe` names the owning resource and
|
|
13
|
+
* slot, so the failure points at a concrete manifest location.
|
|
14
|
+
*/
|
|
9
15
|
export function resolveSqlConnection(
|
|
10
|
-
value: SqlConnectionResource |
|
|
16
|
+
value: SqlConnectionResource | KindRef<SqlConnectionResource> | undefined,
|
|
11
17
|
ctx: ResourceContext,
|
|
18
|
+
describe: () => string,
|
|
12
19
|
): SqlConnectionResource | undefined {
|
|
13
|
-
if (!value)
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
if (typeof (value as SqlConnectionResource).execute === "function") {
|
|
18
|
-
return value as SqlConnectionResource;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const ref = value as ConnectionRef;
|
|
22
|
-
if (typeof ref.name !== "string") {
|
|
23
|
-
throw new Error("Sql: invalid connection reference");
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Cross-module reference (`!ref Alias.Connection`): a connection resolved
|
|
27
|
-
// inside a nested library is not Phase-5-injected, so the controller receives
|
|
28
|
-
// the raw `{name, alias}` ref and must route through the import's exported
|
|
29
|
-
// scope rather than a bare local lookup.
|
|
30
|
-
if (ref.alias && ref.alias !== "Self") {
|
|
31
|
-
const instance = ctx.moduleContext.resolveImportedInstance(ref.alias, ref.name);
|
|
32
|
-
if (typeof (instance as SqlConnectionResource | undefined)?.execute !== "function") {
|
|
33
|
-
throw new Error(
|
|
34
|
-
`Sql: connection reference '${ref.alias}.${ref.name}' did not resolve to an exported connection instance.`,
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
return instance as unknown as SqlConnectionResource;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return ctx.moduleContext.getInstance(ref.name) as SqlConnectionResource;
|
|
20
|
+
if (!value) return undefined;
|
|
21
|
+
return ctx.resolveRef(value, isSqlConnection, describe, "std/sql#Connection");
|
|
41
22
|
}
|
|
@@ -56,7 +56,11 @@ class SqlMigrationsResource implements ResourceInstance {
|
|
|
56
56
|
|
|
57
57
|
async run(): Promise<void> {
|
|
58
58
|
const conn =
|
|
59
|
-
resolveSqlConnection(
|
|
59
|
+
resolveSqlConnection(
|
|
60
|
+
this.manifest.connection,
|
|
61
|
+
this.ctx,
|
|
62
|
+
() => `Sql.Migrations "${this.manifest.metadata.name}": 'connection'`,
|
|
63
|
+
) ?? failMissingConnection();
|
|
60
64
|
|
|
61
65
|
const migrations: Record<string, string[]> = {};
|
|
62
66
|
// Legacy: standalone `Sql.Migration` resources in the same module scope.
|
|
@@ -28,7 +28,12 @@ class SqlQueryResource implements ResourceInstance {
|
|
|
28
28
|
async invoke(input: unknown): Promise<SqlResult> {
|
|
29
29
|
const m = this.manifest;
|
|
30
30
|
const ctx = this.ctx;
|
|
31
|
-
const connection = resolveConnection(
|
|
31
|
+
const connection = resolveConnection(
|
|
32
|
+
m.connection,
|
|
33
|
+
m.transaction,
|
|
34
|
+
ctx,
|
|
35
|
+
() => `Sql.Query "${m.metadata.name}": 'connection'`,
|
|
36
|
+
);
|
|
32
37
|
const result = await runSql(connection, m.transaction, input, ctx);
|
|
33
38
|
return { rows: result.rows, rowCount: result.rows.length };
|
|
34
39
|
}
|
|
@@ -38,9 +43,12 @@ function resolveConnection(
|
|
|
38
43
|
connection: SqlConnectionResource | undefined,
|
|
39
44
|
transaction: SqlTransactionResource | undefined,
|
|
40
45
|
ctx: ResourceContext,
|
|
46
|
+
describe: () => string,
|
|
41
47
|
): SqlConnectionResource {
|
|
42
48
|
return (
|
|
43
|
-
resolveSqlConnection(connection, ctx
|
|
49
|
+
resolveSqlConnection(connection, ctx, describe) ??
|
|
50
|
+
transaction?.getConnection() ??
|
|
51
|
+
failMissingConnection()
|
|
44
52
|
);
|
|
45
53
|
}
|
|
46
54
|
|
|
@@ -96,7 +96,9 @@ class SqlSelectionResource implements ResourceInstance {
|
|
|
96
96
|
const limit = m.limit != null ? ctx.expandValue(m.limit, expandCtx) : undefined;
|
|
97
97
|
const offset = m.offset != null ? ctx.expandValue(m.offset, expandCtx) : undefined;
|
|
98
98
|
|
|
99
|
-
const connection =
|
|
99
|
+
const connection =
|
|
100
|
+
resolveSqlConnection(m.connection, ctx, () => `Sql.Selection "${m.metadata.name}": 'connection'`) ??
|
|
101
|
+
m.transaction?.getConnection();
|
|
100
102
|
if (!connection) {
|
|
101
103
|
throw new Error("Sql.Selection: either 'connection' or 'transaction' must be set");
|
|
102
104
|
}
|
|
@@ -18,8 +18,11 @@ export class SqlTransactionResource implements ResourceInstance {
|
|
|
18
18
|
|
|
19
19
|
getConnection(): SqlConnectionResource {
|
|
20
20
|
return (
|
|
21
|
-
resolveSqlConnection(
|
|
22
|
-
|
|
21
|
+
resolveSqlConnection(
|
|
22
|
+
this.manifest.connection,
|
|
23
|
+
this.ctx,
|
|
24
|
+
() => `Sql.Transaction "${this.manifest.metadata.name}": 'connection'`,
|
|
25
|
+
) ?? failMissingConnection(this.manifest.metadata.name)
|
|
23
26
|
);
|
|
24
27
|
}
|
|
25
28
|
|