sqlstack 2.0.0 → 3.1.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.
Files changed (156) hide show
  1. package/dist/cjs/codegen/generateManifest.d.ts +12 -0
  2. package/dist/cjs/codegen/generateManifest.d.ts.map +1 -1
  3. package/dist/cjs/codegen/generateManifest.js +21 -11
  4. package/dist/cjs/codegen/generateManifest.js.map +1 -1
  5. package/dist/cjs/core/resolver.d.ts +25 -12
  6. package/dist/cjs/core/resolver.d.ts.map +1 -1
  7. package/dist/cjs/core/resolver.js +66 -20
  8. package/dist/cjs/core/resolver.js.map +1 -1
  9. package/dist/cjs/core/sourceScanResolver.d.ts +25 -0
  10. package/dist/cjs/core/sourceScanResolver.d.ts.map +1 -0
  11. package/dist/cjs/core/sourceScanResolver.js +56 -0
  12. package/dist/cjs/core/sourceScanResolver.js.map +1 -0
  13. package/dist/cjs/decorators/query.d.ts.map +1 -1
  14. package/dist/cjs/decorators/query.js +276 -245
  15. package/dist/cjs/decorators/query.js.map +1 -1
  16. package/dist/cjs/families/sqliteFamily.d.ts +26 -0
  17. package/dist/cjs/families/sqliteFamily.d.ts.map +1 -0
  18. package/dist/cjs/families/sqliteFamily.js +187 -0
  19. package/dist/cjs/families/sqliteFamily.js.map +1 -0
  20. package/dist/cjs/family.d.ts +69 -0
  21. package/dist/cjs/family.d.ts.map +1 -0
  22. package/dist/cjs/family.js +206 -0
  23. package/dist/cjs/family.js.map +1 -0
  24. package/dist/cjs/familyBinding.d.ts +22 -0
  25. package/dist/cjs/familyBinding.d.ts.map +1 -0
  26. package/dist/cjs/familyBinding.js +22 -0
  27. package/dist/cjs/familyBinding.js.map +1 -0
  28. package/dist/cjs/index.d.ts +10 -1
  29. package/dist/cjs/index.d.ts.map +1 -1
  30. package/dist/cjs/index.js +17 -2
  31. package/dist/cjs/index.js.map +1 -1
  32. package/dist/cjs/ioc.d.ts +22 -0
  33. package/dist/cjs/ioc.d.ts.map +1 -0
  34. package/dist/cjs/ioc.js +35 -0
  35. package/dist/cjs/ioc.js.map +1 -0
  36. package/dist/cjs/run.d.ts +29 -0
  37. package/dist/cjs/run.d.ts.map +1 -0
  38. package/dist/cjs/run.js +38 -0
  39. package/dist/cjs/run.js.map +1 -0
  40. package/dist/cjs/runtime.d.ts +25 -0
  41. package/dist/cjs/runtime.d.ts.map +1 -0
  42. package/dist/cjs/runtime.js +133 -0
  43. package/dist/cjs/runtime.js.map +1 -0
  44. package/dist/cjs/stack.d.ts +101 -0
  45. package/dist/cjs/stack.d.ts.map +1 -0
  46. package/dist/cjs/stack.js +288 -0
  47. package/dist/cjs/stack.js.map +1 -0
  48. package/dist/cjs/testing/builder.d.ts +29 -0
  49. package/dist/cjs/testing/builder.d.ts.map +1 -0
  50. package/dist/cjs/testing/builder.js +131 -0
  51. package/dist/cjs/testing/builder.js.map +1 -0
  52. package/dist/cjs/testing/index.d.ts +14 -0
  53. package/dist/cjs/testing/index.d.ts.map +1 -0
  54. package/dist/cjs/testing/index.js +22 -0
  55. package/dist/cjs/testing/index.js.map +1 -0
  56. package/dist/cjs/testing/postgres/allocator.d.ts +17 -0
  57. package/dist/cjs/testing/postgres/allocator.d.ts.map +1 -0
  58. package/dist/cjs/testing/postgres/allocator.js +54 -0
  59. package/dist/cjs/testing/postgres/allocator.js.map +1 -0
  60. package/dist/cjs/testing/postgres/database.d.ts +21 -0
  61. package/dist/cjs/testing/postgres/database.d.ts.map +1 -0
  62. package/dist/cjs/testing/postgres/database.js +39 -0
  63. package/dist/cjs/testing/postgres/database.js.map +1 -0
  64. package/dist/cjs/testing/postgres/loadData.d.ts +31 -0
  65. package/dist/cjs/testing/postgres/loadData.d.ts.map +1 -0
  66. package/dist/cjs/testing/postgres/loadData.js +184 -0
  67. package/dist/cjs/testing/postgres/loadData.js.map +1 -0
  68. package/dist/cjs/testing/postgres/server.d.ts +19 -0
  69. package/dist/cjs/testing/postgres/server.d.ts.map +1 -0
  70. package/dist/cjs/testing/postgres/server.js +78 -0
  71. package/dist/cjs/testing/postgres/server.js.map +1 -0
  72. package/dist/cjs/testing/schema/ddl.d.ts +5 -0
  73. package/dist/cjs/testing/schema/ddl.d.ts.map +1 -0
  74. package/dist/cjs/testing/schema/ddl.js +70 -0
  75. package/dist/cjs/testing/schema/ddl.js.map +1 -0
  76. package/dist/cjs/testing/schema/normalize.d.ts +7 -0
  77. package/dist/cjs/testing/schema/normalize.d.ts.map +1 -0
  78. package/dist/cjs/testing/schema/normalize.js +202 -0
  79. package/dist/cjs/testing/schema/normalize.js.map +1 -0
  80. package/dist/cjs/testing/schema/parser.d.ts +9 -0
  81. package/dist/cjs/testing/schema/parser.d.ts.map +1 -0
  82. package/dist/cjs/testing/schema/parser.js +80 -0
  83. package/dist/cjs/testing/schema/parser.js.map +1 -0
  84. package/dist/cjs/testing/schema/types.d.ts +63 -0
  85. package/dist/cjs/testing/schema/types.d.ts.map +1 -0
  86. package/dist/cjs/testing/schema/types.js +6 -0
  87. package/dist/cjs/testing/schema/types.js.map +1 -0
  88. package/dist/cjs/testing/sqlite/builder.d.ts +24 -0
  89. package/dist/cjs/testing/sqlite/builder.d.ts.map +1 -0
  90. package/dist/cjs/testing/sqlite/builder.js +134 -0
  91. package/dist/cjs/testing/sqlite/builder.js.map +1 -0
  92. package/dist/cjs/testing/sqlite/database.d.ts +18 -0
  93. package/dist/cjs/testing/sqlite/database.d.ts.map +1 -0
  94. package/dist/cjs/testing/sqlite/database.js +123 -0
  95. package/dist/cjs/testing/sqlite/database.js.map +1 -0
  96. package/dist/cjs/testing/sqlite/loadData.d.ts +7 -0
  97. package/dist/cjs/testing/sqlite/loadData.d.ts.map +1 -0
  98. package/dist/cjs/testing/sqlite/loadData.js +55 -0
  99. package/dist/cjs/testing/sqlite/loadData.js.map +1 -0
  100. package/dist/cjs/transactions.d.ts +14 -5
  101. package/dist/cjs/transactions.d.ts.map +1 -1
  102. package/dist/cjs/transactions.js +40 -24
  103. package/dist/cjs/transactions.js.map +1 -1
  104. package/dist/esm/codegen/generateManifest.js +20 -11
  105. package/dist/esm/codegen/generateManifest.js.map +1 -1
  106. package/dist/esm/core/resolver.js +59 -19
  107. package/dist/esm/core/resolver.js.map +1 -1
  108. package/dist/esm/core/sourceScanResolver.js +51 -0
  109. package/dist/esm/core/sourceScanResolver.js.map +1 -0
  110. package/dist/esm/decorators/query.js +278 -247
  111. package/dist/esm/decorators/query.js.map +1 -1
  112. package/dist/esm/families/sqliteFamily.js +151 -0
  113. package/dist/esm/families/sqliteFamily.js.map +1 -0
  114. package/dist/esm/family.js +202 -0
  115. package/dist/esm/family.js.map +1 -0
  116. package/dist/esm/familyBinding.js +18 -0
  117. package/dist/esm/familyBinding.js.map +1 -0
  118. package/dist/esm/index.js +10 -1
  119. package/dist/esm/index.js.map +1 -1
  120. package/dist/esm/ioc.js +32 -0
  121. package/dist/esm/ioc.js.map +1 -0
  122. package/dist/esm/run.js +34 -0
  123. package/dist/esm/run.js.map +1 -0
  124. package/dist/esm/runtime.js +94 -0
  125. package/dist/esm/runtime.js.map +1 -0
  126. package/dist/esm/stack.js +251 -0
  127. package/dist/esm/stack.js.map +1 -0
  128. package/dist/esm/testing/builder.js +95 -0
  129. package/dist/esm/testing/builder.js.map +1 -0
  130. package/dist/esm/testing/index.js +9 -0
  131. package/dist/esm/testing/index.js.map +1 -0
  132. package/dist/esm/testing/postgres/allocator.js +49 -0
  133. package/dist/esm/testing/postgres/allocator.js.map +1 -0
  134. package/dist/esm/testing/postgres/database.js +36 -0
  135. package/dist/esm/testing/postgres/database.js.map +1 -0
  136. package/dist/esm/testing/postgres/loadData.js +176 -0
  137. package/dist/esm/testing/postgres/loadData.js.map +1 -0
  138. package/dist/esm/testing/postgres/server.js +69 -0
  139. package/dist/esm/testing/postgres/server.js.map +1 -0
  140. package/dist/esm/testing/schema/ddl.js +65 -0
  141. package/dist/esm/testing/schema/ddl.js.map +1 -0
  142. package/dist/esm/testing/schema/normalize.js +164 -0
  143. package/dist/esm/testing/schema/normalize.js.map +1 -0
  144. package/dist/esm/testing/schema/parser.js +43 -0
  145. package/dist/esm/testing/schema/parser.js.map +1 -0
  146. package/dist/esm/testing/schema/types.js +5 -0
  147. package/dist/esm/testing/schema/types.js.map +1 -0
  148. package/dist/esm/testing/sqlite/builder.js +98 -0
  149. package/dist/esm/testing/sqlite/builder.js.map +1 -0
  150. package/dist/esm/testing/sqlite/database.js +85 -0
  151. package/dist/esm/testing/sqlite/database.js.map +1 -0
  152. package/dist/esm/testing/sqlite/loadData.js +48 -0
  153. package/dist/esm/testing/sqlite/loadData.js.map +1 -0
  154. package/dist/esm/transactions.js +39 -24
  155. package/dist/esm/transactions.js.map +1 -1
  156. package/package.json +19 -2
@@ -4,12 +4,14 @@ exports.Query = Query;
4
4
  const metadata_1 = require("../core/metadata");
5
5
  const errors_1 = require("../core/errors");
6
6
  const validator_1 = require("../core/validator");
7
- const registry_1 = require("../registry");
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 sourceScanResolver_1 = require("../core/sourceScanResolver");
11
12
  const transactions_1 = require("../transactions");
12
13
  const sql_1 = require("../sql");
14
+ const run_1 = require("../run");
13
15
  function Query(arg) {
14
16
  return function (_target, prop, desc) {
15
17
  const options = typeof arg === 'string' ? undefined : arg;
@@ -23,292 +25,321 @@ function Query(arg) {
23
25
  (0, metadata_1.setMethodLabels)(_target, prop, { source: { kind: 'inline', sql: options.sql } });
24
26
  }
25
27
  else {
26
- // FS-based resolution
28
+ // FS-based resolution: only an EXPLICIT path argument pins a directory.
29
+ // Bare @Query() resolves through the active resolver (or the default
30
+ // SourceScanResolver) — no stack-based directory guessing, which breaks
31
+ // under bundlers and test transforms.
27
32
  const { rootDir } = (0, fileResolution_1.coerceRootPath)(rootArg);
28
- const dir = rootDir ?? (0, fileResolution_1.guessDecoratorDir)();
29
- if (dir) {
30
- (0, metadata_1.setMethodLabels)(_target, prop, { source: { kind: 'fs', rootDir: dir } });
33
+ if (rootDir) {
34
+ (0, metadata_1.setMethodLabels)(_target, prop, { source: { kind: 'fs', rootDir } });
31
35
  }
32
36
  }
33
- // Record for resolver coverage checks (ManifestResolver startup assertion).
34
- (0, resolver_1.registerDecoratedMethod)({
37
+ // Record for resolver coverage checks: immutable class metadata (the
38
+ // canonical, environment-local source) plus the deprecated global list
39
+ // consulted by the legacy global-resolver path.
40
+ const decoratedEntry = {
35
41
  className: _target?.constructor?.name ?? '<anonymous>',
36
42
  methodName: String(prop),
37
43
  inline: !!options?.sql,
38
- });
44
+ };
45
+ (0, resolver_1.recordQueryMethodOnClass)(_target, decoratedEntry);
46
+ (0, resolver_1.registerDecoratedMethod)(decoratedEntry);
39
47
  const original = desc.value;
40
48
  desc.value = async function (...callArgs) {
41
49
  const labels = (0, metadata_1.getMethodLabels)(this, prop);
42
50
  const classLabels = (0, metadata_1.getClassLabels)(this);
43
- // 0) Check if the method body returns a SqlFragment (template literal path)
44
- let fragmentResult;
45
- try {
46
- fragmentResult = await original.apply(this, callArgs);
47
- }
48
- catch {
49
- // Method body threw (e.g. placeholder `throw new Error('replaced')`) — fall through to file/inline SQL
50
- fragmentResult = undefined;
51
- }
52
- if (fragmentResult instanceof sql_1.SqlFragment) {
53
- const { sql: boundSql, params } = fragmentResult.toSQL();
51
+ // The full file/inline pipeline (resolve source, bind, execute,
52
+ // shape/transform/validate). Invoked either by run() from inside the
53
+ // method body, or directly as the legacy fallback when the body throws.
54
+ const pipeline = async () => {
55
+ // 1) prepare args with defaults
56
+ const rawInput = callArgs.length === 1 ? callArgs[0] : callArgs; // object or positional array
57
+ // 2) choose database and dialect via the active environment
54
58
  const dbName = labels.db ?? options?.db ?? classLabels?.db;
55
- const { name: resolvedDbName, db } = registry_1.SqlStackDB.getEntry(dbName);
56
- let out;
57
- try {
58
- const tx = (0, transactions_1.getActiveTransaction)(resolvedDbName);
59
- out = tx ? await tx.query(boundSql, params) : await db.query(boundSql, params);
60
- }
61
- catch (dbError) {
62
- throw new errors_1.DatabaseError(dbError?.message || String(dbError), boundSql, dbError instanceof Error ? dbError : undefined);
63
- }
64
- if (Array.isArray(out)) {
65
- const shaped = applyShape(out, labels.shape);
66
- const transformed = labels.transform ? labels.transform(shaped) : shaped;
67
- if (labels.validateSchema && !(0, validator_1.isValidationDisabled)()) {
68
- if (Array.isArray(transformed)) {
69
- for (const row of transformed)
70
- (0, validator_1.validateOutput)(labels.validateSchema, row);
71
- }
72
- else if (transformed !== null && transformed !== undefined) {
73
- (0, validator_1.validateOutput)(labels.validateSchema, transformed);
74
- }
75
- }
76
- return transformed;
59
+ const { entry, resolver: activeResolver } = await (0, runtime_1.resolveExecution)(dbName);
60
+ const db = entry.db;
61
+ const dialect = options?.dialect ?? db.dialect ?? 'sqlite';
62
+ // 3) load SQL source (inline, environment resolver, v1 fs fallback,
63
+ // or the zero-config source scan — bare @Query() with nothing installed)
64
+ const sql = labels?.source?.kind === 'inline'
65
+ ? labels.source.sql
66
+ : activeResolver
67
+ ? activeResolver.resolve({
68
+ className: this?.constructor?.name ?? '<anonymous>',
69
+ methodName: String(prop),
70
+ dialect,
71
+ rootDir: labels?.source?.kind === 'fs' ? labels.source.rootDir : undefined,
72
+ })
73
+ : labels?.source?.kind === 'fs'
74
+ ? (0, fileResolution_1.resolveSqlFromFs)(labels.source.rootDir, String(prop), dialect).sql
75
+ : (0, sourceScanResolver_1.defaultSourceScanResolver)()?.resolve({
76
+ className: this?.constructor?.name ?? '<anonymous>',
77
+ methodName: String(prop),
78
+ dialect,
79
+ });
80
+ if (!sql) {
81
+ throw new errors_1.SqlStackError(`No SQL source provided for method ${String(prop)}. Use @Query() with a file/folder path or inline sql.`);
77
82
  }
78
- return out;
79
- }
80
- // 1) prepare args with defaults
81
- const rawInput = callArgs.length === 1 ? callArgs[0] : callArgs; // object or positional array
82
- // 2) choose database and dialect
83
- const dbName = labels.db ?? options?.db ?? classLabels?.db;
84
- const { name: resolvedDbName, db } = registry_1.SqlStackDB.getEntry(dbName);
85
- const dialect = options?.dialect ?? db.dialect ?? 'sqlite';
86
- // 3) load SQL source (inline, active resolver, or v1 fs fallback)
87
- const activeResolver = (0, resolver_1.getActiveResolver)();
88
- const sql = labels?.source?.kind === 'inline'
89
- ? labels.source.sql
90
- : activeResolver
91
- ? activeResolver.resolve({
92
- className: this?.constructor?.name ?? '<anonymous>',
93
- methodName: String(prop),
94
- dialect,
95
- rootDir: labels?.source?.kind === 'fs' ? labels.source.rootDir : undefined,
96
- })
97
- : labels?.source?.kind === 'fs'
98
- ? (0, fileResolution_1.resolveSqlFromFs)(labels.source.rootDir, String(prop), dialect).sql
99
- : undefined;
100
- if (!sql) {
101
- throw new errors_1.SqlStackError(`No SQL source provided for method ${String(prop)}. Use @Query() with a file/folder path or inline sql.`);
102
- }
103
- // 4) bind placeholders
104
- // Decide argument mode: positional if SQL contains :argN
105
- let workingSql = sql;
106
- const hasPositional = /:arg[1-9][0-9]*/.test(workingSql);
107
- // Prepare args for binder
108
- let args;
109
- if (hasPositional) {
110
- args = callArgs;
111
- }
112
- else {
113
- const namedBase = callArgs.length > 0 ? callArgs[0] : undefined;
114
- args = applyDefaults(labels.defaults, namedBase ?? {});
115
- }
116
- // Compute paging and inject :offset/:limit (named mode) or rewrite to positional
117
- const pageCfg = labels.page;
118
- if (pageCfg) {
119
- const { size, zeroBase } = pageCfg;
120
- const base = zeroBase ? 0 : 1;
121
- // Extract page number from input (object) or a trailing options object in positional mode
122
- const pageFromObject = (val) => {
123
- if (val && typeof val === 'object' && !Array.isArray(val) && typeof val.page !== 'undefined') {
124
- const p = val.page;
125
- if (typeof p === 'number' && isFinite(p))
126
- return p;
127
- }
128
- return undefined;
129
- };
130
- let pageNum;
83
+ // 4) bind placeholders
84
+ // Decide argument mode: positional if SQL contains :argN
85
+ let workingSql = sql;
86
+ const hasPositional = /:arg[1-9][0-9]*/.test(workingSql);
87
+ // Prepare args for binder
88
+ let args;
131
89
  if (hasPositional) {
132
- // Variadic-style: only the LAST argument may carry `{ page }`
133
- if (callArgs.length > 0) {
134
- const last = callArgs[callArgs.length - 1];
135
- pageNum = pageFromObject(last);
136
- }
90
+ args = callArgs;
137
91
  }
138
92
  else {
139
- // Prefer last-arg options if provided; otherwise read from named object
140
- if (callArgs.length > 1) {
141
- const last = callArgs[callArgs.length - 1];
142
- pageNum = pageFromObject(last);
93
+ const namedBase = callArgs.length > 0 ? callArgs[0] : undefined;
94
+ args = applyDefaults(labels.defaults, namedBase ?? {});
95
+ }
96
+ // Compute paging and inject :offset/:limit (named mode) or rewrite to positional
97
+ const pageCfg = labels.page;
98
+ if (pageCfg) {
99
+ const { size, zeroBase } = pageCfg;
100
+ const base = zeroBase ? 0 : 1;
101
+ // Extract page number from input (object) or a trailing options object in positional mode
102
+ const pageFromObject = (val) => {
103
+ if (val && typeof val === 'object' && !Array.isArray(val) && typeof val.page !== 'undefined') {
104
+ const p = val.page;
105
+ if (typeof p === 'number' && isFinite(p))
106
+ return p;
107
+ }
108
+ return undefined;
109
+ };
110
+ let pageNum;
111
+ if (hasPositional) {
112
+ // Variadic-style: only the LAST argument may carry `{ page }`
113
+ if (callArgs.length > 0) {
114
+ const last = callArgs[callArgs.length - 1];
115
+ pageNum = pageFromObject(last);
116
+ }
143
117
  }
144
- if (pageNum === undefined) {
145
- pageNum = pageFromObject(args);
118
+ else {
119
+ // Prefer last-arg options if provided; otherwise read from named object
120
+ if (callArgs.length > 1) {
121
+ const last = callArgs[callArgs.length - 1];
122
+ pageNum = pageFromObject(last);
123
+ }
124
+ if (pageNum === undefined) {
125
+ pageNum = pageFromObject(args);
126
+ }
146
127
  }
147
- }
148
- const effectivePage = typeof pageNum === 'number' ? pageNum : base;
149
- const offset = (effectivePage - base) * size;
150
- const limit = size;
151
- if (hasPositional) {
152
- // Replace occurrences of :offset and :limit with new :argN tokens appended to args
153
- const baseIndex = callArgs.length; // start numbering after user args
154
- let nextIndex = baseIndex + 1;
155
- const additions = [];
156
- const rewrite = (sqlText) => {
157
- const out = [];
158
- let i = 0;
159
- let inSQ = false, inDQ = false, inLC = false, inBC = false;
160
- const len = sqlText.length;
161
- while (i < len) {
162
- const ch = sqlText[i];
163
- const next = i + 1 < len ? sqlText[i + 1] : '';
164
- if (inLC) {
165
- out.push(ch);
166
- if (ch === '\n')
167
- inLC = false;
168
- i++;
169
- continue;
170
- }
171
- if (inBC) {
172
- out.push(ch);
173
- if (ch === '*' && next === '/') {
174
- out.push(next);
128
+ const effectivePage = typeof pageNum === 'number' ? pageNum : base;
129
+ const offset = (effectivePage - base) * size;
130
+ const limit = size;
131
+ if (hasPositional) {
132
+ // Replace occurrences of :offset and :limit with new :argN tokens appended to args
133
+ const baseIndex = callArgs.length; // start numbering after user args
134
+ let nextIndex = baseIndex + 1;
135
+ const additions = [];
136
+ const rewrite = (sqlText) => {
137
+ const out = [];
138
+ let i = 0;
139
+ let inSQ = false, inDQ = false, inLC = false, inBC = false;
140
+ const len = sqlText.length;
141
+ while (i < len) {
142
+ const ch = sqlText[i];
143
+ const next = i + 1 < len ? sqlText[i + 1] : '';
144
+ if (inLC) {
145
+ out.push(ch);
146
+ if (ch === '\n')
147
+ inLC = false;
175
148
  i++;
176
- inBC = false;
149
+ continue;
177
150
  }
178
- i++;
179
- continue;
180
- }
181
- if (inSQ) {
182
- out.push(ch);
183
- if (ch === "'" && next === "'") {
184
- out.push(next);
151
+ if (inBC) {
152
+ out.push(ch);
153
+ if (ch === '*' && next === '/') {
154
+ out.push(next);
155
+ i++;
156
+ inBC = false;
157
+ }
158
+ i++;
159
+ continue;
160
+ }
161
+ if (inSQ) {
162
+ out.push(ch);
163
+ if (ch === "'" && next === "'") {
164
+ out.push(next);
165
+ i += 2;
166
+ continue;
167
+ }
168
+ if (ch === "'")
169
+ inSQ = false;
170
+ i++;
171
+ continue;
172
+ }
173
+ if (inDQ) {
174
+ out.push(ch);
175
+ if (ch === '"' && next === '"') {
176
+ out.push(next);
177
+ i += 2;
178
+ continue;
179
+ }
180
+ if (ch === '"')
181
+ inDQ = false;
182
+ i++;
183
+ continue;
184
+ }
185
+ if (ch === '-' && next === '-') {
186
+ out.push(ch, next);
185
187
  i += 2;
188
+ inLC = true;
186
189
  continue;
187
190
  }
188
- if (ch === "'")
189
- inSQ = false;
190
- i++;
191
- continue;
192
- }
193
- if (inDQ) {
194
- out.push(ch);
195
- if (ch === '"' && next === '"') {
196
- out.push(next);
191
+ if (ch === '/' && next === '*') {
192
+ out.push(ch, next);
197
193
  i += 2;
194
+ inBC = true;
198
195
  continue;
199
196
  }
200
- if (ch === '"')
201
- inDQ = false;
202
- i++;
203
- continue;
204
- }
205
- if (ch === '-' && next === '-') {
206
- out.push(ch, next);
207
- i += 2;
208
- inLC = true;
209
- continue;
210
- }
211
- if (ch === '/' && next === '*') {
212
- out.push(ch, next);
213
- i += 2;
214
- inBC = true;
215
- continue;
216
- }
217
- if (ch === '"') {
218
- out.push(ch);
219
- inDQ = true;
220
- i++;
221
- continue;
222
- }
223
- if (ch === "'") {
224
- out.push(ch);
225
- inSQ = true;
226
- i++;
227
- continue;
228
- }
229
- if (ch === ':' && next === ':') {
230
- out.push('::');
231
- i += 2;
232
- continue;
233
- }
234
- if (ch === ':') {
235
- // parse token
236
- let j = i + 1;
237
- const start = j;
238
- while (j < len) {
239
- const c = sqlText[j];
240
- if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c === '_')
241
- j++;
242
- else
243
- break;
197
+ if (ch === '"') {
198
+ out.push(ch);
199
+ inDQ = true;
200
+ i++;
201
+ continue;
244
202
  }
245
- const name = sqlText.slice(start, j);
246
- if (name === 'offset') {
247
- const token = `:arg${nextIndex++}`;
248
- out.push(token);
249
- additions.push(offset);
250
- i = j;
203
+ if (ch === "'") {
204
+ out.push(ch);
205
+ inSQ = true;
206
+ i++;
207
+ continue;
208
+ }
209
+ if (ch === ':' && next === ':') {
210
+ out.push('::');
211
+ i += 2;
251
212
  continue;
252
213
  }
253
- if (name === 'limit') {
254
- const token = `:arg${nextIndex++}`;
255
- out.push(token);
256
- additions.push(limit);
257
- i = j;
214
+ if (ch === ':') {
215
+ // parse token
216
+ let j = i + 1;
217
+ const start = j;
218
+ while (j < len) {
219
+ const c = sqlText[j];
220
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c === '_')
221
+ j++;
222
+ else
223
+ break;
224
+ }
225
+ const name = sqlText.slice(start, j);
226
+ if (name === 'offset') {
227
+ const token = `:arg${nextIndex++}`;
228
+ out.push(token);
229
+ additions.push(offset);
230
+ i = j;
231
+ continue;
232
+ }
233
+ if (name === 'limit') {
234
+ const token = `:arg${nextIndex++}`;
235
+ out.push(token);
236
+ additions.push(limit);
237
+ i = j;
238
+ continue;
239
+ }
240
+ // otherwise leave untouched
241
+ out.push(':');
242
+ i++;
258
243
  continue;
259
244
  }
260
- // otherwise leave untouched
261
- out.push(':');
245
+ out.push(ch);
262
246
  i++;
263
- continue;
264
247
  }
265
- out.push(ch);
266
- i++;
248
+ return out.join('');
249
+ };
250
+ const rewritten = rewrite(workingSql);
251
+ if (rewritten !== workingSql) {
252
+ workingSql = rewritten;
253
+ // append values so :argK map correctly
254
+ args.push(...additions);
267
255
  }
268
- return out.join('');
269
- };
270
- const rewritten = rewrite(workingSql);
271
- if (rewritten !== workingSql) {
272
- workingSql = rewritten;
273
- // append values so :argK map correctly
274
- args.push(...additions);
256
+ }
257
+ else {
258
+ // Named-mode: attach computed values (override any provided)
259
+ args = { ...args, offset, limit };
275
260
  }
276
261
  }
277
- else {
278
- // Named-mode: attach computed values (override any provided)
279
- args = { ...args, offset, limit };
262
+ const binder = dialect === 'postgres' ? (0, sqlBinder_1.createPostgresBinder)() : dialect === 'mysql' ? (0, sqlBinder_1.createMysqlBinder)() : (0, sqlBinder_1.createSqliteBinder)();
263
+ const { sql: boundSql, params } = binder.bind(workingSql, args);
264
+ // 5) execute query via registry DB
265
+ let out;
266
+ try {
267
+ const tx = (0, transactions_1.getActiveTransaction)(entry);
268
+ out = tx ? await tx.query(boundSql, params) : await db.query(boundSql, params);
280
269
  }
281
- }
282
- const binder = dialect === 'postgres' ? (0, sqlBinder_1.createPostgresBinder)() : dialect === 'mysql' ? (0, sqlBinder_1.createMysqlBinder)() : (0, sqlBinder_1.createSqliteBinder)();
283
- const { sql: boundSql, params } = binder.bind(workingSql, args);
284
- // 5) execute query via registry DB
285
- let out;
270
+ catch (dbError) {
271
+ // Attach SQL query (with placeholders) to error for debugging
272
+ throw new errors_1.DatabaseError(dbError?.message || String(dbError), boundSql, dbError instanceof Error ? dbError : undefined);
273
+ }
274
+ // 6) If rows were returned (SELECT/CTE), shape + transform + validate
275
+ if (Array.isArray(out)) {
276
+ const shaped = applyShape(out, labels.shape);
277
+ const transformed = labels.transform ? labels.transform(shaped) : shaped;
278
+ // 7) validate result via JSON Schema, if provided
279
+ if (labels.validateSchema && !(0, validator_1.isValidationDisabled)()) {
280
+ if (Array.isArray(transformed)) {
281
+ for (const row of transformed)
282
+ (0, validator_1.validateOutput)(labels.validateSchema, row);
283
+ }
284
+ else if (transformed !== null && transformed !== undefined) {
285
+ (0, validator_1.validateOutput)(labels.validateSchema, transformed);
286
+ }
287
+ }
288
+ return transformed;
289
+ }
290
+ // Non-SELECT: return the write result as-is
291
+ return out;
292
+ };
293
+ // 0) Invoke the method body inside a run() context. Three outcomes:
294
+ // - body calls run(): the pipeline executed inside the body; return
295
+ // the body's result verbatim (it may have caught/transformed it).
296
+ // - body returns a SqlFragment (template literal path): execute it.
297
+ // - body throws without calling run() (legacy placeholder throw):
298
+ // fall through to the pipeline.
299
+ const runCtx = { ran: false, execute: pipeline };
300
+ let fragmentResult;
286
301
  try {
287
- const tx = (0, transactions_1.getActiveTransaction)(resolvedDbName);
288
- out = tx ? await tx.query(boundSql, params) : await db.query(boundSql, params);
302
+ fragmentResult = await run_1.runStorage.run(runCtx, () => original.apply(this, callArgs));
289
303
  }
290
- catch (dbError) {
291
- // Attach SQL query (with placeholders) to error for debugging
292
- throw new errors_1.DatabaseError(dbError?.message || String(dbError), boundSql, dbError instanceof Error ? dbError : undefined);
304
+ catch (bodyError) {
305
+ // A body that invoked run() is real logic — its errors propagate.
306
+ if (runCtx.ran)
307
+ throw bodyError;
308
+ // Method body threw (e.g. placeholder `throw new Error('replaced')`) — fall through to file/inline SQL
309
+ fragmentResult = undefined;
293
310
  }
294
- // 6) If rows were returned (SELECT/CTE), shape + transform + validate
295
- if (Array.isArray(out)) {
296
- const shaped = applyShape(out, labels.shape);
297
- const transformed = labels.transform ? labels.transform(shaped) : shaped;
298
- // 7) validate result via JSON Schema, if provided
299
- if (labels.validateSchema && !(0, validator_1.isValidationDisabled)()) {
300
- if (Array.isArray(transformed)) {
301
- for (const row of transformed)
302
- (0, validator_1.validateOutput)(labels.validateSchema, row);
303
- }
304
- else if (transformed !== null && transformed !== undefined) {
305
- (0, validator_1.validateOutput)(labels.validateSchema, transformed);
311
+ if (runCtx.ran)
312
+ return fragmentResult;
313
+ if (fragmentResult instanceof sql_1.SqlFragment) {
314
+ const { sql: boundSql, params } = fragmentResult.toSQL();
315
+ const dbName = labels.db ?? options?.db ?? classLabels?.db;
316
+ const { entry } = await (0, runtime_1.resolveExecution)(dbName);
317
+ const db = entry.db;
318
+ let out;
319
+ try {
320
+ const tx = (0, transactions_1.getActiveTransaction)(entry);
321
+ out = tx ? await tx.query(boundSql, params) : await db.query(boundSql, params);
322
+ }
323
+ catch (dbError) {
324
+ throw new errors_1.DatabaseError(dbError?.message || String(dbError), boundSql, dbError instanceof Error ? dbError : undefined);
325
+ }
326
+ if (Array.isArray(out)) {
327
+ const shaped = applyShape(out, labels.shape);
328
+ const transformed = labels.transform ? labels.transform(shaped) : shaped;
329
+ if (labels.validateSchema && !(0, validator_1.isValidationDisabled)()) {
330
+ if (Array.isArray(transformed)) {
331
+ for (const row of transformed)
332
+ (0, validator_1.validateOutput)(labels.validateSchema, row);
333
+ }
334
+ else if (transformed !== null && transformed !== undefined) {
335
+ (0, validator_1.validateOutput)(labels.validateSchema, transformed);
336
+ }
306
337
  }
338
+ return transformed;
307
339
  }
308
- return transformed;
340
+ return out;
309
341
  }
310
- // Non-SELECT: return the write result as-is
311
- return out;
342
+ return pipeline();
312
343
  };
313
344
  return desc;
314
345
  };