sqlstack 3.5.0 → 3.6.0-dev.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/dist/cjs/codegen/generateManifest.d.ts +3 -1
- package/dist/cjs/codegen/generateManifest.d.ts.map +1 -1
- package/dist/cjs/codegen/generateManifest.js +195 -3
- package/dist/cjs/codegen/generateManifest.js.map +1 -1
- package/dist/cjs/core/fileResolution.d.ts +7 -0
- package/dist/cjs/core/fileResolution.d.ts.map +1 -1
- package/dist/cjs/core/fileResolution.js +53 -14
- package/dist/cjs/core/fileResolution.js.map +1 -1
- package/dist/cjs/core/metadata.d.ts +5 -0
- package/dist/cjs/core/metadata.d.ts.map +1 -1
- package/dist/cjs/core/metadata.js.map +1 -1
- package/dist/cjs/core/resources.d.ts +36 -0
- package/dist/cjs/core/resources.d.ts.map +1 -0
- package/dist/cjs/core/resources.js +98 -0
- package/dist/cjs/core/resources.js.map +1 -0
- package/dist/cjs/core/sourceScanResolver.d.ts +8 -14
- package/dist/cjs/core/sourceScanResolver.d.ts.map +1 -1
- package/dist/cjs/core/sourceScanResolver.js +8 -20
- package/dist/cjs/core/sourceScanResolver.js.map +1 -1
- package/dist/cjs/decorators/query.d.ts.map +1 -1
- package/dist/cjs/decorators/query.js +96 -35
- package/dist/cjs/decorators/query.js.map +1 -1
- package/dist/cjs/family.d.ts.map +1 -1
- package/dist/cjs/family.js +3 -1
- package/dist/cjs/family.js.map +1 -1
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +4 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/run.d.ts +44 -4
- package/dist/cjs/run.d.ts.map +1 -1
- package/dist/cjs/run.js +38 -7
- package/dist/cjs/run.js.map +1 -1
- package/dist/cjs/runtime.d.ts +6 -0
- package/dist/cjs/runtime.d.ts.map +1 -1
- package/dist/cjs/runtime.js +18 -0
- package/dist/cjs/runtime.js.map +1 -1
- package/dist/cjs/stack.d.ts +14 -0
- package/dist/cjs/stack.d.ts.map +1 -1
- package/dist/cjs/stack.js +20 -0
- package/dist/cjs/stack.js.map +1 -1
- package/dist/cjs/transactions.d.ts +52 -0
- package/dist/cjs/transactions.d.ts.map +1 -1
- package/dist/cjs/transactions.js +557 -3
- package/dist/cjs/transactions.js.map +1 -1
- package/dist/esm/codegen/generateManifest.js +195 -3
- package/dist/esm/codegen/generateManifest.js.map +1 -1
- package/dist/esm/core/fileResolution.js +52 -14
- package/dist/esm/core/fileResolution.js.map +1 -1
- package/dist/esm/core/metadata.js.map +1 -1
- package/dist/esm/core/resources.js +91 -0
- package/dist/esm/core/resources.js.map +1 -0
- package/dist/esm/core/sourceScanResolver.js +8 -19
- package/dist/esm/core/sourceScanResolver.js.map +1 -1
- package/dist/esm/decorators/query.js +101 -40
- package/dist/esm/decorators/query.js.map +1 -1
- package/dist/esm/family.js +3 -1
- package/dist/esm/family.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/run.js +38 -8
- package/dist/esm/run.js.map +1 -1
- package/dist/esm/runtime.js +17 -0
- package/dist/esm/runtime.js.map +1 -1
- package/dist/esm/stack.js +21 -1
- package/dist/esm/stack.js.map +1 -1
- package/dist/esm/transactions.js +554 -3
- package/dist/esm/transactions.js.map +1 -1
- package/package.json +1 -1
- package/readme.md +74 -133
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Dialect } from '../registry';
|
|
2
|
+
/**
|
|
3
|
+
* Packaged SQL resources: a path-keyed map `{ '<path>/<method>.sql': '<sql>' }`
|
|
4
|
+
* installed by a bundled/packaged runtime (Electron asar, Cloudflare Worker,
|
|
5
|
+
* @noego/app node runner) via `SqlStack.useResources(map)`.
|
|
6
|
+
*
|
|
7
|
+
* Keys are file paths — absolute, or relative to the project root (the shape
|
|
8
|
+
* `import.meta.glob('/server/**\/*.sql', { eager: true, query: '?raw' })`
|
|
9
|
+
* produces). Lookups match by path suffix: the decorator's guessed directory
|
|
10
|
+
* plus `<method>.sql` must END WITH a key. That makes the same map valid
|
|
11
|
+
* whether modules load from `/repo/server/...`, `app.asar/out/server/...`
|
|
12
|
+
* or a Worker's `modules/<project>/server/...` tree, as long as the module
|
|
13
|
+
* tree is not flattened.
|
|
14
|
+
*/
|
|
15
|
+
export type SqlResources = Record<string, string>;
|
|
16
|
+
/** Forward slashes, no `file://`, no leading `./`, exactly one leading `/`. */
|
|
17
|
+
export declare function normalizeResourcePath(p: string): string;
|
|
18
|
+
/** Install (or clear with undefined) the process-wide packaged SQL resources. */
|
|
19
|
+
export declare function setResources(map: SqlResources | undefined): void;
|
|
20
|
+
export declare function getResources(): SqlResources | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Resolve one absolute (or module-tree) candidate path against the installed
|
|
23
|
+
* resources. Order: exact key; the longest key that is a path suffix of the
|
|
24
|
+
* candidate; the unique key sharing the candidate's `<folder>/<file>` tail.
|
|
25
|
+
*/
|
|
26
|
+
export declare function lookupResource(candidatePath: string): string | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Resolve a decorated method's SQL from the installed resources: the
|
|
29
|
+
* dialect-specific `<dir>/<method>.<ext>.sql` first, then `<dir>/<method>.sql`.
|
|
30
|
+
* Returns the tried candidate paths so a miss can be reported precisely.
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveSqlFromResources(dir: string, methodName: string, dialect: Dialect): {
|
|
33
|
+
sql: string | undefined;
|
|
34
|
+
tried: string[];
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=resources.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../../src/core/resources.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAwBlD,+EAA+E;AAC/E,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAMvD;AAOD,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,GAAG,EAAE,YAAY,GAAG,SAAS,GAAG,IAAI,CAchE;AAED,wBAAgB,YAAY,IAAI,YAAY,GAAG,SAAS,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAoBxE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,GACf;IAAE,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAQ9C"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeResourcePath = normalizeResourcePath;
|
|
4
|
+
exports.setResources = setResources;
|
|
5
|
+
exports.getResources = getResources;
|
|
6
|
+
exports.lookupResource = lookupResource;
|
|
7
|
+
exports.resolveSqlFromResources = resolveSqlFromResources;
|
|
8
|
+
const g = globalThis;
|
|
9
|
+
if (!g.__SQLSTACK_RESOURCES_STATE__) {
|
|
10
|
+
g.__SQLSTACK_RESOURCES_STATE__ = {
|
|
11
|
+
resources: undefined,
|
|
12
|
+
byKey: new Map(),
|
|
13
|
+
byTail: new Map(),
|
|
14
|
+
lookups: new Map(),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
const state = g.__SQLSTACK_RESOURCES_STATE__;
|
|
18
|
+
const DIALECT_EXT = { postgres: 'pg', mysql: 'mysql', sqlite: 'sqlite' };
|
|
19
|
+
/** Forward slashes, no `file://`, no leading `./`, exactly one leading `/`. */
|
|
20
|
+
function normalizeResourcePath(p) {
|
|
21
|
+
let s = p.replace(/\\/g, '/');
|
|
22
|
+
if (s.startsWith('file://'))
|
|
23
|
+
s = s.slice('file://'.length);
|
|
24
|
+
s = s.replace(/^(\.\/)+/, '');
|
|
25
|
+
s = s.replace(/^\/+/, '');
|
|
26
|
+
return '/' + s;
|
|
27
|
+
}
|
|
28
|
+
function tailOf(normalized) {
|
|
29
|
+
const parts = normalized.split('/');
|
|
30
|
+
return '/' + parts.slice(-2).join('/');
|
|
31
|
+
}
|
|
32
|
+
/** Install (or clear with undefined) the process-wide packaged SQL resources. */
|
|
33
|
+
function setResources(map) {
|
|
34
|
+
state.byKey = new Map();
|
|
35
|
+
state.byTail = new Map();
|
|
36
|
+
state.lookups = new Map();
|
|
37
|
+
state.resources = map;
|
|
38
|
+
if (!map)
|
|
39
|
+
return;
|
|
40
|
+
for (const [rawKey, sql] of Object.entries(map)) {
|
|
41
|
+
const key = normalizeResourcePath(rawKey);
|
|
42
|
+
state.byKey.set(key, sql);
|
|
43
|
+
const tail = tailOf(key);
|
|
44
|
+
const list = state.byTail.get(tail);
|
|
45
|
+
if (list)
|
|
46
|
+
list.push(key);
|
|
47
|
+
else
|
|
48
|
+
state.byTail.set(tail, [key]);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function getResources() {
|
|
52
|
+
return state.resources;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Resolve one absolute (or module-tree) candidate path against the installed
|
|
56
|
+
* resources. Order: exact key; the longest key that is a path suffix of the
|
|
57
|
+
* candidate; the unique key sharing the candidate's `<folder>/<file>` tail.
|
|
58
|
+
*/
|
|
59
|
+
function lookupResource(candidatePath) {
|
|
60
|
+
if (!state.resources)
|
|
61
|
+
return undefined;
|
|
62
|
+
const candidate = normalizeResourcePath(candidatePath);
|
|
63
|
+
if (state.lookups.has(candidate))
|
|
64
|
+
return state.lookups.get(candidate);
|
|
65
|
+
let found = state.byKey.get(candidate);
|
|
66
|
+
if (found === undefined) {
|
|
67
|
+
let bestLen = -1;
|
|
68
|
+
for (const [key, sql] of state.byKey) {
|
|
69
|
+
if (key.length > bestLen && candidate.endsWith(key)) {
|
|
70
|
+
found = sql;
|
|
71
|
+
bestLen = key.length;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (found === undefined) {
|
|
76
|
+
const hits = state.byTail.get(tailOf(candidate));
|
|
77
|
+
if (hits && hits.length === 1)
|
|
78
|
+
found = state.byKey.get(hits[0]);
|
|
79
|
+
}
|
|
80
|
+
state.lookups.set(candidate, found);
|
|
81
|
+
return found;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Resolve a decorated method's SQL from the installed resources: the
|
|
85
|
+
* dialect-specific `<dir>/<method>.<ext>.sql` first, then `<dir>/<method>.sql`.
|
|
86
|
+
* Returns the tried candidate paths so a miss can be reported precisely.
|
|
87
|
+
*/
|
|
88
|
+
function resolveSqlFromResources(dir, methodName, dialect) {
|
|
89
|
+
const base = normalizeResourcePath(dir).replace(/\/+$/, '');
|
|
90
|
+
const tried = [`${base}/${methodName}.${DIALECT_EXT[dialect]}.sql`, `${base}/${methodName}.sql`];
|
|
91
|
+
for (const candidate of tried) {
|
|
92
|
+
const sql = lookupResource(candidate);
|
|
93
|
+
if (sql !== undefined)
|
|
94
|
+
return { sql, tried };
|
|
95
|
+
}
|
|
96
|
+
return { sql: undefined, tried };
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=resources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resources.js","sourceRoot":"","sources":["../../../src/core/resources.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Dialect } from '../registry';\n\n/**\n * Packaged SQL resources: a path-keyed map `{ '<path>/<method>.sql': '<sql>' }`\n * installed by a bundled/packaged runtime (Electron asar, Cloudflare Worker,\n * @noego/app node runner) via `SqlStack.useResources(map)`.\n *\n * Keys are file paths — absolute, or relative to the project root (the shape\n * `import.meta.glob('/server/**\\/*.sql', { eager: true, query: '?raw' })`\n * produces). Lookups match by path suffix: the decorator's guessed directory\n * plus `<method>.sql` must END WITH a key. That makes the same map valid\n * whether modules load from `/repo/server/...`, `app.asar/out/server/...`\n * or a Worker's `modules/<project>/server/...` tree, as long as the module\n * tree is not flattened.\n */\nexport type SqlResources = Record<string, string>;\n\ntype ResourcesState = {\n resources: SqlResources | undefined;\n /** normalized key → sql */\n byKey: Map<string, string>;\n /** `<folder>/<file>` tail → normalized keys sharing that tail */\n byTail: Map<string, string[]>;\n lookups: Map<string, string | undefined>;\n};\n\nconst g = globalThis as any;\nif (!g.__SQLSTACK_RESOURCES_STATE__) {\n g.__SQLSTACK_RESOURCES_STATE__ = {\n resources: undefined,\n byKey: new Map(),\n byTail: new Map(),\n lookups: new Map(),\n } as ResourcesState;\n}\nconst state: ResourcesState = g.__SQLSTACK_RESOURCES_STATE__;\n\nconst DIALECT_EXT: Record<Dialect, string> = { postgres: 'pg', mysql: 'mysql', sqlite: 'sqlite' };\n\n/** Forward slashes, no `file://`, no leading `./`, exactly one leading `/`. */\nexport function normalizeResourcePath(p: string): string {\n let s = p.replace(/\\\\/g, '/');\n if (s.startsWith('file://')) s = s.slice('file://'.length);\n s = s.replace(/^(\\.\\/)+/, '');\n s = s.replace(/^\\/+/, '');\n return '/' + s;\n}\n\nfunction tailOf(normalized: string): string {\n const parts = normalized.split('/');\n return '/' + parts.slice(-2).join('/');\n}\n\n/** Install (or clear with undefined) the process-wide packaged SQL resources. */\nexport function setResources(map: SqlResources | undefined): void {\n state.byKey = new Map();\n state.byTail = new Map();\n state.lookups = new Map();\n state.resources = map;\n if (!map) return;\n for (const [rawKey, sql] of Object.entries(map)) {\n const key = normalizeResourcePath(rawKey);\n state.byKey.set(key, sql);\n const tail = tailOf(key);\n const list = state.byTail.get(tail);\n if (list) list.push(key);\n else state.byTail.set(tail, [key]);\n }\n}\n\nexport function getResources(): SqlResources | undefined {\n return state.resources;\n}\n\n/**\n * Resolve one absolute (or module-tree) candidate path against the installed\n * resources. Order: exact key; the longest key that is a path suffix of the\n * candidate; the unique key sharing the candidate's `<folder>/<file>` tail.\n */\nexport function lookupResource(candidatePath: string): string | undefined {\n if (!state.resources) return undefined;\n const candidate = normalizeResourcePath(candidatePath);\n if (state.lookups.has(candidate)) return state.lookups.get(candidate);\n let found: string | undefined = state.byKey.get(candidate);\n if (found === undefined) {\n let bestLen = -1;\n for (const [key, sql] of state.byKey) {\n if (key.length > bestLen && candidate.endsWith(key)) {\n found = sql;\n bestLen = key.length;\n }\n }\n }\n if (found === undefined) {\n const hits = state.byTail.get(tailOf(candidate));\n if (hits && hits.length === 1) found = state.byKey.get(hits[0]);\n }\n state.lookups.set(candidate, found);\n return found;\n}\n\n/**\n * Resolve a decorated method's SQL from the installed resources: the\n * dialect-specific `<dir>/<method>.<ext>.sql` first, then `<dir>/<method>.sql`.\n * Returns the tried candidate paths so a miss can be reported precisely.\n */\nexport function resolveSqlFromResources(\n dir: string,\n methodName: string,\n dialect: Dialect\n): { sql: string | undefined; tried: string[] } {\n const base = normalizeResourcePath(dir).replace(/\\/+$/, '');\n const tried = [`${base}/${methodName}.${DIALECT_EXT[dialect]}.sql`, `${base}/${methodName}.sql`];\n for (const candidate of tried) {\n const sql = lookupResource(candidate);\n if (sql !== undefined) return { sql, tried };\n }\n return { sql: undefined, tried };\n}\n"]}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import type { SqlResolver, SqlResolutionRequest } from './resolver';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* Opt-in Node resolution that scans a project tree (process.cwd(), or an
|
|
4
|
+
* explicit rootDir) for @Query classes and their sibling .sql files, then
|
|
5
|
+
* answers Contract A lookups from the in-memory manifest. The scan runs once
|
|
6
|
+
* per instance, on first resolve.
|
|
7
7
|
*
|
|
8
|
-
* This is
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* This is NOT a default. Bare @Query() resolves its own module directory
|
|
9
|
+
* (stack guess at decoration time) against `SqlStack.useResources(...)` or
|
|
10
|
+
* the filesystem; nothing walks the tree unless this resolver is installed
|
|
11
|
+
* explicitly via `SqlStack.useResolver` / `SqlStackConfig.resolver`.
|
|
12
12
|
*/
|
|
13
13
|
export declare class SourceScanResolver implements SqlResolver {
|
|
14
14
|
#private;
|
|
@@ -16,10 +16,4 @@ export declare class SourceScanResolver implements SqlResolver {
|
|
|
16
16
|
constructor(rootDir?: string | undefined);
|
|
17
17
|
resolve(request: SqlResolutionRequest): string | undefined;
|
|
18
18
|
}
|
|
19
|
-
/**
|
|
20
|
-
* @internal Process-wide default used by the @Query call path when no
|
|
21
|
-
* resolver is installed and the decorator captured no filesystem root.
|
|
22
|
-
* Node-only: returns undefined where the filesystem is unavailable.
|
|
23
|
-
*/
|
|
24
|
-
export declare function defaultSourceScanResolver(): SourceScanResolver | undefined;
|
|
25
19
|
//# sourceMappingURL=sourceScanResolver.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sourceScanResolver.d.ts","sourceRoot":"","sources":["../../../src/core/sourceScanResolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAIpE;;;;;;;;;;GAUG;AACH,qBAAa,kBAAmB,YAAW,WAAW;;IAGxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAR,OAAO,CAAC,EAAE,MAAM,YAAA;IAE7C,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,GAAG,SAAS;CAO3D
|
|
1
|
+
{"version":3,"file":"sourceScanResolver.d.ts","sourceRoot":"","sources":["../../../src/core/sourceScanResolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAIpE;;;;;;;;;;GAUG;AACH,qBAAa,kBAAmB,YAAW,WAAW;;IAGxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAR,OAAO,CAAC,EAAE,MAAM,YAAA;IAE7C,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,GAAG,SAAS;CAO3D"}
|
|
@@ -13,19 +13,18 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
|
|
|
13
13
|
var _SourceScanResolver_inner;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.SourceScanResolver = void 0;
|
|
16
|
-
exports.defaultSourceScanResolver = defaultSourceScanResolver;
|
|
17
16
|
const resolver_1 = require("./resolver");
|
|
18
17
|
const generateManifest_1 = require("../codegen/generateManifest");
|
|
19
18
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
19
|
+
* Opt-in Node resolution that scans a project tree (process.cwd(), or an
|
|
20
|
+
* explicit rootDir) for @Query classes and their sibling .sql files, then
|
|
21
|
+
* answers Contract A lookups from the in-memory manifest. The scan runs once
|
|
22
|
+
* per instance, on first resolve.
|
|
24
23
|
*
|
|
25
|
-
* This is
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
24
|
+
* This is NOT a default. Bare @Query() resolves its own module directory
|
|
25
|
+
* (stack guess at decoration time) against `SqlStack.useResources(...)` or
|
|
26
|
+
* the filesystem; nothing walks the tree unless this resolver is installed
|
|
27
|
+
* explicitly via `SqlStack.useResolver` / `SqlStackConfig.resolver`.
|
|
29
28
|
*/
|
|
30
29
|
class SourceScanResolver {
|
|
31
30
|
constructor(rootDir) {
|
|
@@ -42,15 +41,4 @@ class SourceScanResolver {
|
|
|
42
41
|
}
|
|
43
42
|
exports.SourceScanResolver = SourceScanResolver;
|
|
44
43
|
_SourceScanResolver_inner = new WeakMap();
|
|
45
|
-
let defaultInstance;
|
|
46
|
-
/**
|
|
47
|
-
* @internal Process-wide default used by the @Query call path when no
|
|
48
|
-
* resolver is installed and the decorator captured no filesystem root.
|
|
49
|
-
* Node-only: returns undefined where the filesystem is unavailable.
|
|
50
|
-
*/
|
|
51
|
-
function defaultSourceScanResolver() {
|
|
52
|
-
if (typeof process === 'undefined' || typeof process.cwd !== 'function')
|
|
53
|
-
return undefined;
|
|
54
|
-
return (defaultInstance ?? (defaultInstance = new SourceScanResolver()));
|
|
55
|
-
}
|
|
56
44
|
//# sourceMappingURL=sourceScanResolver.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sourceScanResolver.js","sourceRoot":"","sources":["../../../src/core/sourceScanResolver.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"sourceScanResolver.js","sourceRoot":"","sources":["../../../src/core/sourceScanResolver.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AACA,yCAA8C;AAC9C,kEAAkE;AAElE;;;;;;;;;;GAUG;AACH,MAAa,kBAAkB;IAG7B,YAA6B,OAAgB;QAAhB,YAAO,GAAP,OAAO,CAAS;QAF7C,4CAAqC;IAEW,CAAC;IAEjD,OAAO,CAAC,OAA6B;QACnC,IAAI,CAAC,uBAAA,IAAI,iCAAO,EAAE,CAAC;YACjB,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAA,sCAAmB,EAAC,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;YACxE,uBAAA,IAAI,6BAAU,IAAI,2BAAgB,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,MAAA,CAAC;QAClE,CAAC;QACD,OAAO,uBAAA,IAAI,iCAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,CAAC;CACF;AAZD,gDAYC","sourcesContent":["import type { SqlResolver, SqlResolutionRequest } from './resolver';\nimport { ManifestResolver } from './resolver';\nimport { scanSqlManifestSync } from '../codegen/generateManifest';\n\n/**\n * Opt-in Node resolution that scans a project tree (process.cwd(), or an\n * explicit rootDir) for @Query classes and their sibling .sql files, then\n * answers Contract A lookups from the in-memory manifest. The scan runs once\n * per instance, on first resolve.\n *\n * This is NOT a default. Bare @Query() resolves its own module directory\n * (stack guess at decoration time) against `SqlStack.useResources(...)` or\n * the filesystem; nothing walks the tree unless this resolver is installed\n * explicitly via `SqlStack.useResolver` / `SqlStackConfig.resolver`.\n */\nexport class SourceScanResolver implements SqlResolver {\n #inner: ManifestResolver | undefined;\n\n constructor(private readonly rootDir?: string) {}\n\n resolve(request: SqlResolutionRequest): string | undefined {\n if (!this.#inner) {\n const { manifest } = scanSqlManifestSync(this.rootDir ?? process.cwd());\n this.#inner = new ManifestResolver(manifest, { assert: false });\n }\n return this.#inner.resolve(request);\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../src/decorators/query.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../src/decorators/query.ts"],"names":[],"mappings":"AAyBA,KAAK,YAAY,GAAG;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;CAC3C,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,YAAY,IAC9B,SAAS,GAAG,EAAE,MAAM,MAAM,GAAG,MAAM,EAAE,MAAM,kBAAkB,wBA6Y/E"}
|
|
@@ -8,7 +8,7 @@ const runtime_1 = require("../runtime");
|
|
|
8
8
|
const sqlBinder_1 = require("../binders/sqlBinder");
|
|
9
9
|
const fileResolution_1 = require("../core/fileResolution");
|
|
10
10
|
const resolver_1 = require("../core/resolver");
|
|
11
|
-
const
|
|
11
|
+
const resources_1 = require("../core/resources");
|
|
12
12
|
const transactions_1 = require("../transactions");
|
|
13
13
|
const sql_1 = require("../sql");
|
|
14
14
|
const run_1 = require("../run");
|
|
@@ -39,14 +39,20 @@ function Query(arg) {
|
|
|
39
39
|
(0, metadata_1.setMethodLabels)(_target, prop, { source: { kind: 'inline', sql: options.sql } });
|
|
40
40
|
}
|
|
41
41
|
else {
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
42
|
+
// An EXPLICIT path argument pins a filesystem directory. Bare @Query()
|
|
43
|
+
// records the directory of the decorating module (first non-sqlstack
|
|
44
|
+
// stack frame at decoration time); at call time that directory is
|
|
45
|
+
// resolved against the packaged resources map when one is installed
|
|
46
|
+
// (SqlStack.useResources), else read from the filesystem. No tree scan.
|
|
46
47
|
const { rootDir } = (0, fileResolution_1.coerceRootPath)(rootArg);
|
|
47
48
|
if (rootDir) {
|
|
48
49
|
(0, metadata_1.setMethodLabels)(_target, prop, { source: { kind: 'fs', rootDir } });
|
|
49
50
|
}
|
|
51
|
+
else {
|
|
52
|
+
const dir = (0, fileResolution_1.guessDecoratorDir)();
|
|
53
|
+
if (dir)
|
|
54
|
+
(0, metadata_1.setMethodLabels)(_target, prop, { source: { kind: 'guess', dir } });
|
|
55
|
+
}
|
|
50
56
|
}
|
|
51
57
|
// Record for resolver coverage checks: immutable class metadata (the
|
|
52
58
|
// canonical, environment-local source) plus the deprecated global list
|
|
@@ -83,26 +89,25 @@ function Query(arg) {
|
|
|
83
89
|
}
|
|
84
90
|
const db = target.db;
|
|
85
91
|
const dialect = options?.dialect ?? db.dialect ?? 'sqlite';
|
|
86
|
-
// 3) load SQL source
|
|
87
|
-
//
|
|
92
|
+
// 3) load SQL source: inline; else an installed resolver (the
|
|
93
|
+
// instance's, else the process-global one — a build-time manifest
|
|
94
|
+
// registered by a host runner applies even when the SqlStack itself
|
|
95
|
+
// came from an IoC root); else the decorator's directory (explicit
|
|
96
|
+
// path or guessed module dir) against the packaged resources /
|
|
97
|
+
// filesystem.
|
|
98
|
+
const className = this?.constructor?.name ?? '<anonymous>';
|
|
99
|
+
const resolver = activeResolver ?? (0, resolver_1.getActiveResolver)();
|
|
88
100
|
const sql = labels?.source?.kind === 'inline'
|
|
89
101
|
? labels.source.sql
|
|
90
|
-
:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
})
|
|
97
|
-
: labels?.source?.kind === 'fs'
|
|
98
|
-
? (0, fileResolution_1.resolveSqlFromFs)(labels.source.rootDir, String(prop), dialect).sql
|
|
99
|
-
: (0, sourceScanResolver_1.defaultSourceScanResolver)()?.resolve({
|
|
100
|
-
className: this?.constructor?.name ?? '<anonymous>',
|
|
101
|
-
methodName: String(prop),
|
|
102
|
-
dialect,
|
|
103
|
-
});
|
|
102
|
+
: (resolver?.resolve({
|
|
103
|
+
className,
|
|
104
|
+
methodName: String(prop),
|
|
105
|
+
dialect,
|
|
106
|
+
rootDir: labels?.source?.kind === 'fs' ? labels.source.rootDir : undefined,
|
|
107
|
+
}) ?? resolveSourceSql(labels?.source, className, String(prop), dialect));
|
|
104
108
|
if (!sql) {
|
|
105
|
-
throw new errors_1.SqlStackError(`No SQL source
|
|
109
|
+
throw new errors_1.SqlStackError(`No SQL source for ${className}.${String(prop)}: the decorating module's directory could not be ` +
|
|
110
|
+
'determined from the stack. Use @Query() with a file/folder path or inline sql.');
|
|
106
111
|
}
|
|
107
112
|
onSql(sql);
|
|
108
113
|
// 4) bind placeholders
|
|
@@ -314,11 +319,43 @@ function Query(arg) {
|
|
|
314
319
|
const resolved = await (0, runtime_1.resolveExecution)(dbName);
|
|
315
320
|
return {
|
|
316
321
|
db: resolved.entry.db,
|
|
317
|
-
tx: (0, transactions_1.
|
|
322
|
+
tx: (0, transactions_1.getTransactionTarget)(resolved.entry),
|
|
318
323
|
resolver: resolved.resolver,
|
|
319
324
|
byName: true,
|
|
320
325
|
};
|
|
321
326
|
};
|
|
327
|
+
// The method's own leading database argument: a handle (object with
|
|
328
|
+
// query()) or a registered database name. It selects run()'s target
|
|
329
|
+
// and is never bound as a value.
|
|
330
|
+
const leadingDb = async () => {
|
|
331
|
+
if (callArgs.length === 0)
|
|
332
|
+
return undefined;
|
|
333
|
+
const first = callArgs[0];
|
|
334
|
+
if ((0, run_1.isRunDatabaseHandle)(first))
|
|
335
|
+
return first;
|
|
336
|
+
if (typeof first === 'string' && first.length > 0 && (await (0, runtime_1.isRegisteredDatabaseName)(first)))
|
|
337
|
+
return first;
|
|
338
|
+
return undefined;
|
|
339
|
+
};
|
|
340
|
+
// A database chosen by run's argument or the method's leading argument.
|
|
341
|
+
const explicitTarget = async (db) => {
|
|
342
|
+
const target = await (0, run_1.resolveRunTarget)(db);
|
|
343
|
+
// Inline SQL needs no resolver; only look one up for file-based SQL.
|
|
344
|
+
const resolver = target.byName
|
|
345
|
+
? target.resolver
|
|
346
|
+
: labels?.source?.kind === 'inline'
|
|
347
|
+
? undefined
|
|
348
|
+
: await (0, runtime_1.resolveSqlResolver)();
|
|
349
|
+
return { target, resolver };
|
|
350
|
+
};
|
|
351
|
+
// run() (and the legacy fallback): the method's arguments minus a
|
|
352
|
+
// leading database, on that database or the decorator context.
|
|
353
|
+
const executeDefault = async (onSql) => {
|
|
354
|
+
const lead = await leadingDb();
|
|
355
|
+
if (lead === undefined)
|
|
356
|
+
return executeMethodSql(callArgs, undefined, onSql);
|
|
357
|
+
return executeMethodSql(callArgs.slice(1), await explicitTarget(lead), onSql);
|
|
358
|
+
};
|
|
322
359
|
// run's argument-taking forms: return the adapter's raw result.
|
|
323
360
|
const executeInvocation = async (invocation, onSql) => {
|
|
324
361
|
if (invocation.kind === 'fragment') {
|
|
@@ -330,17 +367,19 @@ function Query(arg) {
|
|
|
330
367
|
onSql(boundSql);
|
|
331
368
|
return (0, run_1.executeOnTarget)(target, boundSql, params);
|
|
332
369
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
370
|
+
if (invocation.kind === 'db') {
|
|
371
|
+
// run(db): the method's own SQL against db, bound from the method's
|
|
372
|
+
// arguments minus a leading database argument.
|
|
373
|
+
const lead = await leadingDb();
|
|
374
|
+
const bindArgs = lead === undefined ? callArgs : callArgs.slice(1);
|
|
375
|
+
return executeMethodSql(bindArgs, await explicitTarget(invocation.db), onSql);
|
|
376
|
+
}
|
|
377
|
+
// run(db, params) / run(params): the method's own SQL bound from params
|
|
378
|
+
// only (an object for :named, an array for :argN), against run's db,
|
|
379
|
+
// else the method's leading database, else the decorator context.
|
|
342
380
|
const bindArgs = Array.isArray(invocation.params) ? [...invocation.params] : [invocation.params];
|
|
343
|
-
|
|
381
|
+
const db = invocation.db ?? (await leadingDb());
|
|
382
|
+
return executeMethodSql(bindArgs, db !== undefined ? await explicitTarget(db) : undefined, onSql);
|
|
344
383
|
};
|
|
345
384
|
// The full pipeline: execute, then — if rows were returned
|
|
346
385
|
// (SELECT/CTE/RETURNING) — shape + transform + validate; a write result
|
|
@@ -352,7 +391,7 @@ function Query(arg) {
|
|
|
352
391
|
// DatabaseError with the original error in `.cause`.
|
|
353
392
|
const pipeline = async (invocation) => {
|
|
354
393
|
if (invocation === undefined || invocation.kind === 'default') {
|
|
355
|
-
const out = await
|
|
394
|
+
const out = await executeDefault(() => { });
|
|
356
395
|
return finishResult(out, labels, this, prop);
|
|
357
396
|
}
|
|
358
397
|
let sqlText = invocation.kind === 'fragment' ? (0, run_1.fragmentText)(invocation.fragment) : '';
|
|
@@ -396,7 +435,7 @@ function Query(arg) {
|
|
|
396
435
|
const db = entry.db;
|
|
397
436
|
let out;
|
|
398
437
|
try {
|
|
399
|
-
const tx = (0, transactions_1.
|
|
438
|
+
const tx = (0, transactions_1.getTransactionTarget)(entry);
|
|
400
439
|
out = tx ? await tx.query(boundSql, params) : await db.query(boundSql, params);
|
|
401
440
|
}
|
|
402
441
|
catch (dbError) {
|
|
@@ -409,6 +448,28 @@ function Query(arg) {
|
|
|
409
448
|
return desc;
|
|
410
449
|
};
|
|
411
450
|
}
|
|
451
|
+
// Directory-based SQL for a decorated method. An explicit @Query('<path>')
|
|
452
|
+
// is always the filesystem. A bare @Query() uses the packaged resources map
|
|
453
|
+
// when one is installed (bundled/packaged runtimes), else the filesystem next
|
|
454
|
+
// to the decorating module (tests, plain Node). Misses name every key tried.
|
|
455
|
+
function resolveSourceSql(source, className, methodName, dialect) {
|
|
456
|
+
if (!source || source.kind === 'inline')
|
|
457
|
+
return undefined;
|
|
458
|
+
if (source.kind === 'fs')
|
|
459
|
+
return (0, fileResolution_1.resolveSqlFromFs)(source.rootDir, methodName, dialect).sql;
|
|
460
|
+
const packaged = (0, resources_1.getResources)();
|
|
461
|
+
const fromResources = packaged ? (0, resources_1.resolveSqlFromResources)(source.dir, methodName, dialect) : undefined;
|
|
462
|
+
if (fromResources?.sql !== undefined)
|
|
463
|
+
return fromResources.sql;
|
|
464
|
+
try {
|
|
465
|
+
return (0, fileResolution_1.resolveSqlFromFs)(source.dir, methodName, dialect).sql;
|
|
466
|
+
}
|
|
467
|
+
catch (fsError) {
|
|
468
|
+
const triedResources = fromResources ? `\npackaged resources tried:\n${fromResources.tried.join('\n')}` : '';
|
|
469
|
+
throw new errors_1.SqlStackError(`sqlstack: no SQL for ${className}.${methodName}.${triedResources}\n` +
|
|
470
|
+
`${fsError?.message ?? String(fsError)}`);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
412
473
|
// Deprecated (removal in the next major version): a @Query body that RETURNS
|
|
413
474
|
// an sql`` fragment. It only type-checks when the method's declared return
|
|
414
475
|
// type is bent to admit SqlFragment (an `as any`-style hole). One warning per
|