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