sqlstack 1.0.25 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/dist/cjs/adapters/mysql.d.ts.map +1 -1
  2. package/dist/cjs/adapters/mysql.js +3 -3
  3. package/dist/cjs/adapters/mysql.js.map +1 -1
  4. package/dist/cjs/adapters/pg.d.ts.map +1 -1
  5. package/dist/cjs/adapters/pg.js +3 -3
  6. package/dist/cjs/adapters/pg.js.map +1 -1
  7. package/dist/cjs/codegen/cli.d.ts +3 -0
  8. package/dist/cjs/codegen/cli.d.ts.map +1 -0
  9. package/dist/cjs/codegen/cli.js +50 -0
  10. package/dist/cjs/codegen/cli.js.map +1 -0
  11. package/dist/cjs/codegen/generateManifest.d.ts +72 -0
  12. package/dist/cjs/codegen/generateManifest.d.ts.map +1 -0
  13. package/dist/cjs/codegen/generateManifest.js +191 -0
  14. package/dist/cjs/codegen/generateManifest.js.map +1 -0
  15. package/dist/cjs/core/loadNodeModule.d.ts +17 -0
  16. package/dist/cjs/core/loadNodeModule.d.ts.map +1 -0
  17. package/dist/cjs/core/loadNodeModule.js +36 -0
  18. package/dist/cjs/core/loadNodeModule.js.map +1 -0
  19. package/dist/cjs/core/resolver.d.ts +94 -0
  20. package/dist/cjs/core/resolver.d.ts.map +1 -0
  21. package/dist/cjs/core/resolver.js +145 -0
  22. package/dist/cjs/core/resolver.js.map +1 -0
  23. package/dist/cjs/core/sourceScanResolver.d.ts +25 -0
  24. package/dist/cjs/core/sourceScanResolver.d.ts.map +1 -0
  25. package/dist/cjs/core/sourceScanResolver.js +56 -0
  26. package/dist/cjs/core/sourceScanResolver.js.map +1 -0
  27. package/dist/cjs/decorators/query.d.ts.map +1 -1
  28. package/dist/cjs/decorators/query.js +42 -14
  29. package/dist/cjs/decorators/query.js.map +1 -1
  30. package/dist/cjs/families/sqliteFamily.d.ts +26 -0
  31. package/dist/cjs/families/sqliteFamily.d.ts.map +1 -0
  32. package/dist/cjs/families/sqliteFamily.js +187 -0
  33. package/dist/cjs/families/sqliteFamily.js.map +1 -0
  34. package/dist/cjs/family.d.ts +69 -0
  35. package/dist/cjs/family.d.ts.map +1 -0
  36. package/dist/cjs/family.js +206 -0
  37. package/dist/cjs/family.js.map +1 -0
  38. package/dist/cjs/familyBinding.d.ts +22 -0
  39. package/dist/cjs/familyBinding.d.ts.map +1 -0
  40. package/dist/cjs/familyBinding.js +22 -0
  41. package/dist/cjs/familyBinding.js.map +1 -0
  42. package/dist/cjs/index.d.ts +10 -0
  43. package/dist/cjs/index.d.ts.map +1 -1
  44. package/dist/cjs/index.js +18 -1
  45. package/dist/cjs/index.js.map +1 -1
  46. package/dist/cjs/ioc.d.ts +22 -0
  47. package/dist/cjs/ioc.d.ts.map +1 -0
  48. package/dist/cjs/ioc.js +35 -0
  49. package/dist/cjs/ioc.js.map +1 -0
  50. package/dist/cjs/runtime.d.ts +25 -0
  51. package/dist/cjs/runtime.d.ts.map +1 -0
  52. package/dist/cjs/runtime.js +133 -0
  53. package/dist/cjs/runtime.js.map +1 -0
  54. package/dist/cjs/stack.d.ts +101 -0
  55. package/dist/cjs/stack.d.ts.map +1 -0
  56. package/dist/cjs/stack.js +288 -0
  57. package/dist/cjs/stack.js.map +1 -0
  58. package/dist/cjs/testing/builder.d.ts +29 -0
  59. package/dist/cjs/testing/builder.d.ts.map +1 -0
  60. package/dist/cjs/testing/builder.js +131 -0
  61. package/dist/cjs/testing/builder.js.map +1 -0
  62. package/dist/cjs/testing/index.d.ts +10 -0
  63. package/dist/cjs/testing/index.d.ts.map +1 -0
  64. package/dist/cjs/testing/index.js +18 -0
  65. package/dist/cjs/testing/index.js.map +1 -0
  66. package/dist/cjs/testing/postgres/allocator.d.ts +17 -0
  67. package/dist/cjs/testing/postgres/allocator.d.ts.map +1 -0
  68. package/dist/cjs/testing/postgres/allocator.js +54 -0
  69. package/dist/cjs/testing/postgres/allocator.js.map +1 -0
  70. package/dist/cjs/testing/postgres/database.d.ts +21 -0
  71. package/dist/cjs/testing/postgres/database.d.ts.map +1 -0
  72. package/dist/cjs/testing/postgres/database.js +39 -0
  73. package/dist/cjs/testing/postgres/database.js.map +1 -0
  74. package/dist/cjs/testing/postgres/loadData.d.ts +31 -0
  75. package/dist/cjs/testing/postgres/loadData.d.ts.map +1 -0
  76. package/dist/cjs/testing/postgres/loadData.js +184 -0
  77. package/dist/cjs/testing/postgres/loadData.js.map +1 -0
  78. package/dist/cjs/testing/postgres/server.d.ts +19 -0
  79. package/dist/cjs/testing/postgres/server.d.ts.map +1 -0
  80. package/dist/cjs/testing/postgres/server.js +78 -0
  81. package/dist/cjs/testing/postgres/server.js.map +1 -0
  82. package/dist/cjs/testing/schema/ddl.d.ts +14 -0
  83. package/dist/cjs/testing/schema/ddl.d.ts.map +1 -0
  84. package/dist/cjs/testing/schema/ddl.js +54 -0
  85. package/dist/cjs/testing/schema/ddl.js.map +1 -0
  86. package/dist/cjs/testing/schema/normalize.d.ts +7 -0
  87. package/dist/cjs/testing/schema/normalize.d.ts.map +1 -0
  88. package/dist/cjs/testing/schema/normalize.js +202 -0
  89. package/dist/cjs/testing/schema/normalize.js.map +1 -0
  90. package/dist/cjs/testing/schema/parser.d.ts +9 -0
  91. package/dist/cjs/testing/schema/parser.d.ts.map +1 -0
  92. package/dist/cjs/testing/schema/parser.js +80 -0
  93. package/dist/cjs/testing/schema/parser.js.map +1 -0
  94. package/dist/cjs/testing/schema/types.d.ts +82 -0
  95. package/dist/cjs/testing/schema/types.d.ts.map +1 -0
  96. package/dist/cjs/testing/schema/types.js +10 -0
  97. package/dist/cjs/testing/schema/types.js.map +1 -0
  98. package/dist/cjs/transactions.d.ts +14 -5
  99. package/dist/cjs/transactions.d.ts.map +1 -1
  100. package/dist/cjs/transactions.js +40 -24
  101. package/dist/cjs/transactions.js.map +1 -1
  102. package/dist/esm/adapters/mysql.js +3 -3
  103. package/dist/esm/adapters/mysql.js.map +1 -1
  104. package/dist/esm/adapters/pg.js +3 -3
  105. package/dist/esm/adapters/pg.js.map +1 -1
  106. package/dist/esm/codegen/cli.js +48 -0
  107. package/dist/esm/codegen/cli.js.map +1 -0
  108. package/dist/esm/codegen/generateManifest.js +183 -0
  109. package/dist/esm/codegen/generateManifest.js.map +1 -0
  110. package/dist/esm/core/loadNodeModule.js +33 -0
  111. package/dist/esm/core/loadNodeModule.js.map +1 -0
  112. package/dist/esm/core/resolver.js +132 -0
  113. package/dist/esm/core/resolver.js.map +1 -0
  114. package/dist/esm/core/sourceScanResolver.js +51 -0
  115. package/dist/esm/core/sourceScanResolver.js.map +1 -0
  116. package/dist/esm/decorators/query.js +43 -15
  117. package/dist/esm/decorators/query.js.map +1 -1
  118. package/dist/esm/families/sqliteFamily.js +151 -0
  119. package/dist/esm/families/sqliteFamily.js.map +1 -0
  120. package/dist/esm/family.js +202 -0
  121. package/dist/esm/family.js.map +1 -0
  122. package/dist/esm/familyBinding.js +18 -0
  123. package/dist/esm/familyBinding.js.map +1 -0
  124. package/dist/esm/index.js +8 -0
  125. package/dist/esm/index.js.map +1 -1
  126. package/dist/esm/ioc.js +32 -0
  127. package/dist/esm/ioc.js.map +1 -0
  128. package/dist/esm/runtime.js +94 -0
  129. package/dist/esm/runtime.js.map +1 -0
  130. package/dist/esm/stack.js +251 -0
  131. package/dist/esm/stack.js.map +1 -0
  132. package/dist/esm/testing/builder.js +95 -0
  133. package/dist/esm/testing/builder.js.map +1 -0
  134. package/dist/esm/testing/index.js +7 -0
  135. package/dist/esm/testing/index.js.map +1 -0
  136. package/dist/esm/testing/postgres/allocator.js +49 -0
  137. package/dist/esm/testing/postgres/allocator.js.map +1 -0
  138. package/dist/esm/testing/postgres/database.js +36 -0
  139. package/dist/esm/testing/postgres/database.js.map +1 -0
  140. package/dist/esm/testing/postgres/loadData.js +176 -0
  141. package/dist/esm/testing/postgres/loadData.js.map +1 -0
  142. package/dist/esm/testing/postgres/server.js +69 -0
  143. package/dist/esm/testing/postgres/server.js.map +1 -0
  144. package/dist/esm/testing/schema/ddl.js +49 -0
  145. package/dist/esm/testing/schema/ddl.js.map +1 -0
  146. package/dist/esm/testing/schema/normalize.js +164 -0
  147. package/dist/esm/testing/schema/normalize.js.map +1 -0
  148. package/dist/esm/testing/schema/parser.js +43 -0
  149. package/dist/esm/testing/schema/parser.js.map +1 -0
  150. package/dist/esm/testing/schema/types.js +9 -0
  151. package/dist/esm/testing/schema/types.js.map +1 -0
  152. package/dist/esm/transactions.js +39 -24
  153. package/dist/esm/transactions.js.map +1 -1
  154. package/package.json +29 -4
@@ -0,0 +1,187 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.sqliteFamily = sqliteFamily;
37
+ const fs = __importStar(require("node:fs"));
38
+ const path = __importStar(require("node:path"));
39
+ const errors_1 = require("../core/errors");
40
+ const sqlite_1 = require("../adapters/sqlite");
41
+ const SIDECARS = ['-wal', '-shm', '-journal'];
42
+ /**
43
+ * Strict SQLite file family (spec 17 §15/§20): open() NEVER creates a missing
44
+ * file — a lost database must surface as a failure, not become a fresh empty
45
+ * migrated database. Creation is explicit.
46
+ *
47
+ * Keys are opaque (spec 17 §18): any non-empty string is accepted, including
48
+ * path-shaped legacy keys. Safety is containment — the resolved file must
49
+ * stay inside the family root — and canonical identity: keyId is the
50
+ * resolved root-relative path, so aliasing spellings collapse to one member.
51
+ */
52
+ function sqliteFamily(options) {
53
+ const root = path.resolve(options.directory);
54
+ function resolvePath(key) {
55
+ // Default naming: keys already carrying .db (legacy stored paths) map
56
+ // as-is; bare logical keys get the .db suffix.
57
+ const file = options.filename
58
+ ? options.filename(key)
59
+ : key.endsWith('.db')
60
+ ? key
61
+ : `${key}.db`;
62
+ const resolved = path.resolve(root, file);
63
+ if (resolved === root || !resolved.startsWith(root + path.sep)) {
64
+ throw new errors_1.SqlStackError(`SQLite family key "${key}" resolves outside the family directory (${root}). ` +
65
+ 'Resolved member files must stay inside the configured root.');
66
+ }
67
+ return resolved;
68
+ }
69
+ async function openConnection(file, mustExist) {
70
+ let BetterSqlite3;
71
+ try {
72
+ BetterSqlite3 = (await Promise.resolve(`${'better-sqlite3'}`).then(s => __importStar(require(s)))).default;
73
+ }
74
+ catch {
75
+ throw new errors_1.SqlStackError('sqliteFamily requires the optional peer "better-sqlite3".');
76
+ }
77
+ const conn = new BetterSqlite3(file, { fileMustExist: mustExist });
78
+ for (const pragma of options.pragmas ?? []) {
79
+ conn.pragma(pragma);
80
+ }
81
+ return (0, sqlite_1.createSqliteDb)(conn);
82
+ }
83
+ return {
84
+ normalizeKey(input) {
85
+ if (typeof input !== 'string' || input.length === 0) {
86
+ throw new errors_1.SqlStackError(`Invalid SQLite family key ${JSON.stringify(input)}: keys are non-empty strings.`);
87
+ }
88
+ // Containment is validated eagerly so a bad key fails at normalization
89
+ // time with the key in the diagnostic, before any lifecycle operation.
90
+ resolvePath(input);
91
+ return input;
92
+ },
93
+ // Canonical identity (spec 17 §18): aliasing spellings of the same file
94
+ // ("a.db" vs "sub/../a.db") collapse to one keyId, so cache entries,
95
+ // lease counts, and transaction identity can never split across aliases.
96
+ keyId(key) {
97
+ return path.relative(root, resolvePath(key));
98
+ },
99
+ async exists(key) {
100
+ return fs.existsSync(resolvePath(key));
101
+ },
102
+ async open(key) {
103
+ const file = resolvePath(key);
104
+ try {
105
+ return await openConnection(file, true);
106
+ }
107
+ catch (err) {
108
+ if (!fs.existsSync(file)) {
109
+ throw new errors_1.SqlStackError(`SQLite family database "${key}" does not exist at ${file}. ` +
110
+ 'open() never creates databases — use create(key) or ensure(key) explicitly.');
111
+ }
112
+ throw err;
113
+ }
114
+ },
115
+ async create(key) {
116
+ const file = resolvePath(key);
117
+ if (options.mkdirs !== false) {
118
+ fs.mkdirSync(path.dirname(file), { recursive: true });
119
+ }
120
+ // Exclusive file creation makes "must not already exist" race-free; an
121
+ // empty file is a valid brand-new SQLite database.
122
+ try {
123
+ fs.closeSync(fs.openSync(file, 'wx'));
124
+ }
125
+ catch (err) {
126
+ if (err?.code === 'EEXIST') {
127
+ throw new errors_1.SqlStackError(`SQLite family database "${key}" already exists at ${file}. ` +
128
+ 'create() requires a new database — use ensure(key) for create-if-missing.');
129
+ }
130
+ throw err;
131
+ }
132
+ try {
133
+ const db = await openConnection(file, true);
134
+ if (options.prepare)
135
+ await options.prepare(db, key);
136
+ return db;
137
+ }
138
+ catch (err) {
139
+ // Safe cleanup of the storage this create() call owns.
140
+ try {
141
+ fs.rmSync(file, { force: true });
142
+ }
143
+ catch {
144
+ /* keep original error */
145
+ }
146
+ throw err;
147
+ }
148
+ },
149
+ async ensure(key) {
150
+ const file = resolvePath(key);
151
+ if (fs.existsSync(file))
152
+ return this.open(key);
153
+ return this.create(key);
154
+ },
155
+ async delete(key) {
156
+ const file = resolvePath(key);
157
+ if (!fs.existsSync(file)) {
158
+ throw new errors_1.SqlStackError(`SQLite family database "${key}" does not exist at ${file}; nothing to delete.`);
159
+ }
160
+ fs.rmSync(file);
161
+ for (const suffix of SIDECARS) {
162
+ fs.rmSync(file + suffix, { force: true });
163
+ }
164
+ },
165
+ async inspect(key) {
166
+ const file = resolvePath(key);
167
+ if (!fs.existsSync(file))
168
+ return { state: 'missing', path: file };
169
+ const sizeBytes = fs.statSync(file).size;
170
+ let sidecarBytes = 0;
171
+ for (const suffix of SIDECARS) {
172
+ if (fs.existsSync(file + suffix))
173
+ sidecarBytes += fs.statSync(file + suffix).size;
174
+ }
175
+ return { state: 'closed', path: file, sizeBytes, sidecarBytes };
176
+ },
177
+ async *list() {
178
+ if (!fs.existsSync(root))
179
+ return;
180
+ for (const entry of fs.readdirSync(root)) {
181
+ if (entry.endsWith('.db'))
182
+ yield entry.slice(0, -3);
183
+ }
184
+ },
185
+ };
186
+ }
187
+ //# sourceMappingURL=sqliteFamily.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqliteFamily.js","sourceRoot":"","sources":["../../../src/families/sqliteFamily.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { Database } from '../registry';\nimport { SqlStackError } from '../core/errors';\nimport { createSqliteDb } from '../adapters/sqlite';\nimport type { DatabaseFamilyProvider, DatabaseInspection } from '../family';\n\nexport interface SqliteFamilyOptions {\n /** Root directory that owns every member file. Keys resolve inside it. */\n directory: string;\n /** Filename for a key (default `<key>.db`). Must not escape the directory. */\n filename?: (key: string) => string;\n /** Pragmas applied on every open (e.g. ['journal_mode = WAL']). */\n pragmas?: readonly string[];\n /** Preparation hook (e.g. Proper migrations) run after create/ensure-open. */\n prepare?: (db: Database, key: string) => Promise<void>;\n /** Create parent directories on create/ensure (default true). */\n mkdirs?: boolean;\n}\n\nconst SIDECARS = ['-wal', '-shm', '-journal'];\n\n/**\n * Strict SQLite file family (spec 17 §15/§20): open() NEVER creates a missing\n * file — a lost database must surface as a failure, not become a fresh empty\n * migrated database. Creation is explicit.\n *\n * Keys are opaque (spec 17 §18): any non-empty string is accepted, including\n * path-shaped legacy keys. Safety is containment — the resolved file must\n * stay inside the family root — and canonical identity: keyId is the\n * resolved root-relative path, so aliasing spellings collapse to one member.\n */\nexport function sqliteFamily(options: SqliteFamilyOptions): DatabaseFamilyProvider<string> {\n const root = path.resolve(options.directory);\n\n function resolvePath(key: string): string {\n // Default naming: keys already carrying .db (legacy stored paths) map\n // as-is; bare logical keys get the .db suffix.\n const file = options.filename\n ? options.filename(key)\n : key.endsWith('.db')\n ? key\n : `${key}.db`;\n const resolved = path.resolve(root, file);\n if (resolved === root || !resolved.startsWith(root + path.sep)) {\n throw new SqlStackError(\n `SQLite family key \"${key}\" resolves outside the family directory (${root}). ` +\n 'Resolved member files must stay inside the configured root.'\n );\n }\n return resolved;\n }\n\n async function openConnection(file: string, mustExist: boolean): Promise<Database> {\n let BetterSqlite3: any;\n try {\n BetterSqlite3 = ((await import('better-sqlite3' as string)) as any).default;\n } catch {\n throw new SqlStackError('sqliteFamily requires the optional peer \"better-sqlite3\".');\n }\n const conn = new BetterSqlite3(file, { fileMustExist: mustExist });\n for (const pragma of options.pragmas ?? []) {\n conn.pragma(pragma);\n }\n return createSqliteDb(conn);\n }\n\n return {\n normalizeKey(input: unknown): string {\n if (typeof input !== 'string' || input.length === 0) {\n throw new SqlStackError(\n `Invalid SQLite family key ${JSON.stringify(input)}: keys are non-empty strings.`\n );\n }\n // Containment is validated eagerly so a bad key fails at normalization\n // time with the key in the diagnostic, before any lifecycle operation.\n resolvePath(input);\n return input;\n },\n\n // Canonical identity (spec 17 §18): aliasing spellings of the same file\n // (\"a.db\" vs \"sub/../a.db\") collapse to one keyId, so cache entries,\n // lease counts, and transaction identity can never split across aliases.\n keyId(key: string): string {\n return path.relative(root, resolvePath(key));\n },\n\n async exists(key: string): Promise<boolean> {\n return fs.existsSync(resolvePath(key));\n },\n\n async open(key: string): Promise<Database> {\n const file = resolvePath(key);\n try {\n return await openConnection(file, true);\n } catch (err: any) {\n if (!fs.existsSync(file)) {\n throw new SqlStackError(\n `SQLite family database \"${key}\" does not exist at ${file}. ` +\n 'open() never creates databases — use create(key) or ensure(key) explicitly.'\n );\n }\n throw err;\n }\n },\n\n async create(key: string): Promise<Database> {\n const file = resolvePath(key);\n if (options.mkdirs !== false) {\n fs.mkdirSync(path.dirname(file), { recursive: true });\n }\n // Exclusive file creation makes \"must not already exist\" race-free; an\n // empty file is a valid brand-new SQLite database.\n try {\n fs.closeSync(fs.openSync(file, 'wx'));\n } catch (err: any) {\n if (err?.code === 'EEXIST') {\n throw new SqlStackError(\n `SQLite family database \"${key}\" already exists at ${file}. ` +\n 'create() requires a new database — use ensure(key) for create-if-missing.'\n );\n }\n throw err;\n }\n try {\n const db = await openConnection(file, true);\n if (options.prepare) await options.prepare(db, key);\n return db;\n } catch (err) {\n // Safe cleanup of the storage this create() call owns.\n try {\n fs.rmSync(file, { force: true });\n } catch {\n /* keep original error */\n }\n throw err;\n }\n },\n\n async ensure(key: string): Promise<Database> {\n const file = resolvePath(key);\n if (fs.existsSync(file)) return this.open(key);\n return this.create!(key);\n },\n\n async delete(key: string): Promise<void> {\n const file = resolvePath(key);\n if (!fs.existsSync(file)) {\n throw new SqlStackError(\n `SQLite family database \"${key}\" does not exist at ${file}; nothing to delete.`\n );\n }\n fs.rmSync(file);\n for (const suffix of SIDECARS) {\n fs.rmSync(file + suffix, { force: true });\n }\n },\n\n async inspect(key: string): Promise<DatabaseInspection> {\n const file = resolvePath(key);\n if (!fs.existsSync(file)) return { state: 'missing', path: file };\n const sizeBytes = fs.statSync(file).size;\n let sidecarBytes = 0;\n for (const suffix of SIDECARS) {\n if (fs.existsSync(file + suffix)) sidecarBytes += fs.statSync(file + suffix).size;\n }\n return { state: 'closed', path: file, sizeBytes, sidecarBytes };\n },\n\n async *list(): AsyncIterable<string> {\n if (!fs.existsSync(root)) return;\n for (const entry of fs.readdirSync(root)) {\n if (entry.endsWith('.db')) yield entry.slice(0, -3);\n }\n },\n };\n}\n"]}
@@ -0,0 +1,69 @@
1
+ import type { Database } from './registry';
2
+ import type { SqlStack, DatabaseEntry } from './stack';
3
+ /**
4
+ * Dynamic database families (spec 17): one registration that resolves many
5
+ * concrete databases by key. The provider owns physical mechanics; the
6
+ * family runtime owns identity, leases, bounded caching, and lifecycle.
7
+ */
8
+ export interface DatabaseInspection {
9
+ state: 'closed' | 'open' | 'missing';
10
+ [fact: string]: unknown;
11
+ }
12
+ export interface DatabaseFamilyProvider<K = string> {
13
+ /** Normalize/validate an application key. Must be deterministic and safe. */
14
+ normalizeKey(input: unknown): K;
15
+ /** Serialize a normalized key to the identity string used for routing. */
16
+ keyId?(key: K): string;
17
+ /** Open an EXISTING database. Must fail if it does not exist (never create). */
18
+ open(key: K): Promise<Database>;
19
+ /** Create a NEW database. Must fail if it already exists. */
20
+ create?(key: K): Promise<Database>;
21
+ /** Explicit create-if-missing. */
22
+ ensure?(key: K): Promise<Database>;
23
+ exists?(key: K): Promise<boolean>;
24
+ /** Physically delete the database (handle already closed by the runtime). */
25
+ delete?(key: K): Promise<void>;
26
+ inspect?(key: K): Promise<DatabaseInspection>;
27
+ list?(): AsyncIterable<K>;
28
+ }
29
+ export interface FamilyConfig<K = string> {
30
+ provider: DatabaseFamilyProvider<K>;
31
+ cache?: {
32
+ /** Max concurrently open members (default 16). Leased members never evict. */
33
+ maxOpen?: number;
34
+ };
35
+ }
36
+ /** Concrete member identity: owning stack + family registration + key. */
37
+ export interface FamilyMemberEntry extends DatabaseEntry {
38
+ readonly family: string;
39
+ readonly keyId: string;
40
+ }
41
+ export interface DatabaseLease {
42
+ readonly entry: FamilyMemberEntry;
43
+ release(): Promise<void>;
44
+ }
45
+ export declare class FamilyRuntime<K = string> {
46
+ #private;
47
+ readonly name: string;
48
+ constructor(stack: SqlStack, name: string, config: FamilyConfig<K>);
49
+ normalize(input: unknown): {
50
+ key: K;
51
+ keyId: string;
52
+ };
53
+ /** Stable concrete identity for a key; no I/O. */
54
+ ref(input: unknown): FamilyMemberEntry;
55
+ exists(input: unknown): Promise<boolean>;
56
+ create(input: unknown): Promise<void>;
57
+ ensure(input: unknown): Promise<void>;
58
+ /** Acquire a lease on an opened (existing) member. */
59
+ acquire(input: unknown): Promise<DatabaseLease>;
60
+ with<T>(input: unknown, fn: (entry: FamilyMemberEntry) => Promise<T>): Promise<T>;
61
+ /** Close/evict a member's cached handle; the database itself is preserved. */
62
+ closeMember(input: unknown): Promise<void>;
63
+ /** Physically delete a member. Fails while leased; closes the cached handle first. */
64
+ deleteMember(input: unknown): Promise<void>;
65
+ inspect(input: unknown): Promise<DatabaseInspection>;
66
+ /** Close every cached handle (root disposal). Databases are preserved. */
67
+ closeAll(): Promise<void>;
68
+ }
69
+ //# sourceMappingURL=family.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"family.d.ts","sourceRoot":"","sources":["../../src/family.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,206 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _FamilyRuntime_instances, _FamilyRuntime_stack, _FamilyRuntime_provider, _FamilyRuntime_maxOpen, _FamilyRuntime_members, _FamilyRuntime_useTick, _FamilyRuntime_closed, _FamilyRuntime_member, _FamilyRuntime_open, _FamilyRuntime_closeHandle, _FamilyRuntime_evictIfNeeded, _FamilyRuntime_assertOpen;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.FamilyRuntime = void 0;
16
+ const errors_1 = require("./core/errors");
17
+ function unsupported(family, op) {
18
+ throw new errors_1.SqlStackError(`Database family "${family}" does not support ${op}(): its provider declares no such capability.`);
19
+ }
20
+ class FamilyRuntime {
21
+ constructor(stack, name, config) {
22
+ _FamilyRuntime_instances.add(this);
23
+ _FamilyRuntime_stack.set(this, void 0);
24
+ _FamilyRuntime_provider.set(this, void 0);
25
+ _FamilyRuntime_maxOpen.set(this, void 0);
26
+ _FamilyRuntime_members.set(this, new Map());
27
+ _FamilyRuntime_useTick.set(this, 0);
28
+ _FamilyRuntime_closed.set(this, false);
29
+ __classPrivateFieldSet(this, _FamilyRuntime_stack, stack, "f");
30
+ this.name = name;
31
+ __classPrivateFieldSet(this, _FamilyRuntime_provider, config.provider, "f");
32
+ __classPrivateFieldSet(this, _FamilyRuntime_maxOpen, config.cache?.maxOpen ?? 16, "f");
33
+ }
34
+ normalize(input) {
35
+ const key = __classPrivateFieldGet(this, _FamilyRuntime_provider, "f").normalizeKey(input);
36
+ const keyId = __classPrivateFieldGet(this, _FamilyRuntime_provider, "f").keyId ? __classPrivateFieldGet(this, _FamilyRuntime_provider, "f").keyId(key) : String(key);
37
+ return { key, keyId };
38
+ }
39
+ /** Stable concrete identity for a key; no I/O. */
40
+ ref(input) {
41
+ return __classPrivateFieldGet(this, _FamilyRuntime_instances, "m", _FamilyRuntime_member).call(this, input).entry;
42
+ }
43
+ async exists(input) {
44
+ const { key } = this.normalize(input);
45
+ if (!__classPrivateFieldGet(this, _FamilyRuntime_provider, "f").exists)
46
+ unsupported(this.name, 'exists');
47
+ return __classPrivateFieldGet(this, _FamilyRuntime_provider, "f").exists(key);
48
+ }
49
+ async create(input) {
50
+ __classPrivateFieldGet(this, _FamilyRuntime_instances, "m", _FamilyRuntime_assertOpen).call(this);
51
+ const member = __classPrivateFieldGet(this, _FamilyRuntime_instances, "m", _FamilyRuntime_member).call(this, input);
52
+ if (!__classPrivateFieldGet(this, _FamilyRuntime_provider, "f").create)
53
+ unsupported(this.name, 'create');
54
+ const db = await __classPrivateFieldGet(this, _FamilyRuntime_provider, "f").create(member.key);
55
+ await db.close();
56
+ }
57
+ async ensure(input) {
58
+ __classPrivateFieldGet(this, _FamilyRuntime_instances, "m", _FamilyRuntime_assertOpen).call(this);
59
+ const member = __classPrivateFieldGet(this, _FamilyRuntime_instances, "m", _FamilyRuntime_member).call(this, input);
60
+ if (!__classPrivateFieldGet(this, _FamilyRuntime_provider, "f").ensure)
61
+ unsupported(this.name, 'ensure');
62
+ const db = await __classPrivateFieldGet(this, _FamilyRuntime_provider, "f").ensure(member.key);
63
+ await db.close();
64
+ }
65
+ /** Acquire a lease on an opened (existing) member. */
66
+ async acquire(input) {
67
+ var _a;
68
+ __classPrivateFieldGet(this, _FamilyRuntime_instances, "m", _FamilyRuntime_assertOpen).call(this);
69
+ const member = __classPrivateFieldGet(this, _FamilyRuntime_instances, "m", _FamilyRuntime_member).call(this, input);
70
+ await __classPrivateFieldGet(this, _FamilyRuntime_instances, "m", _FamilyRuntime_open).call(this, member);
71
+ member.leases++;
72
+ member.lastUsed = __classPrivateFieldSet(this, _FamilyRuntime_useTick, (_a = __classPrivateFieldGet(this, _FamilyRuntime_useTick, "f"), ++_a), "f");
73
+ let released = false;
74
+ return {
75
+ entry: member.entry,
76
+ release: async () => {
77
+ if (released)
78
+ return;
79
+ released = true;
80
+ member.leases = Math.max(0, member.leases - 1);
81
+ __classPrivateFieldGet(this, _FamilyRuntime_instances, "m", _FamilyRuntime_evictIfNeeded).call(this);
82
+ },
83
+ };
84
+ }
85
+ async with(input, fn) {
86
+ const lease = await this.acquire(input);
87
+ try {
88
+ return await fn(lease.entry);
89
+ }
90
+ finally {
91
+ await lease.release();
92
+ }
93
+ }
94
+ /** Close/evict a member's cached handle; the database itself is preserved. */
95
+ async closeMember(input) {
96
+ const { keyId } = this.normalize(input);
97
+ const member = __classPrivateFieldGet(this, _FamilyRuntime_members, "f").get(keyId);
98
+ if (!member?.db)
99
+ return;
100
+ if (member.leases > 0) {
101
+ throw new errors_1.SqlStackError(`Cannot close "${this.name}/${keyId}": ${member.leases} lease(s) are active.`);
102
+ }
103
+ await __classPrivateFieldGet(this, _FamilyRuntime_instances, "m", _FamilyRuntime_closeHandle).call(this, member);
104
+ }
105
+ /** Physically delete a member. Fails while leased; closes the cached handle first. */
106
+ async deleteMember(input) {
107
+ __classPrivateFieldGet(this, _FamilyRuntime_instances, "m", _FamilyRuntime_assertOpen).call(this);
108
+ const member = __classPrivateFieldGet(this, _FamilyRuntime_instances, "m", _FamilyRuntime_member).call(this, input);
109
+ if (!__classPrivateFieldGet(this, _FamilyRuntime_provider, "f").delete)
110
+ unsupported(this.name, 'delete');
111
+ if (member.leases > 0) {
112
+ throw new errors_1.SqlStackError(`Cannot delete "${this.name}/${member.entry.keyId}": ${member.leases} lease(s) are active.`);
113
+ }
114
+ if (member.db)
115
+ await __classPrivateFieldGet(this, _FamilyRuntime_instances, "m", _FamilyRuntime_closeHandle).call(this, member);
116
+ await __classPrivateFieldGet(this, _FamilyRuntime_provider, "f").delete(member.key);
117
+ __classPrivateFieldGet(this, _FamilyRuntime_members, "f").delete(member.entry.keyId);
118
+ }
119
+ async inspect(input) {
120
+ const member = __classPrivateFieldGet(this, _FamilyRuntime_instances, "m", _FamilyRuntime_member).call(this, input);
121
+ if (!__classPrivateFieldGet(this, _FamilyRuntime_provider, "f").inspect)
122
+ unsupported(this.name, 'inspect');
123
+ const facts = await __classPrivateFieldGet(this, _FamilyRuntime_provider, "f").inspect(member.key);
124
+ return { ...facts, cached: !!member.db, activeLeases: member.leases };
125
+ }
126
+ /** Close every cached handle (root disposal). Databases are preserved. */
127
+ async closeAll() {
128
+ __classPrivateFieldSet(this, _FamilyRuntime_closed, true, "f");
129
+ const failures = [];
130
+ for (const member of __classPrivateFieldGet(this, _FamilyRuntime_members, "f").values()) {
131
+ if (!member.db)
132
+ continue;
133
+ try {
134
+ await __classPrivateFieldGet(this, _FamilyRuntime_instances, "m", _FamilyRuntime_closeHandle).call(this, member);
135
+ }
136
+ catch (err) {
137
+ failures.push(`"${this.name}/${member.entry.keyId}": ${err?.message ?? String(err)}`);
138
+ }
139
+ }
140
+ __classPrivateFieldGet(this, _FamilyRuntime_members, "f").clear();
141
+ if (failures.length) {
142
+ throw new errors_1.SqlStackError(`Family close failed for ${failures.join(', ')}`);
143
+ }
144
+ }
145
+ }
146
+ exports.FamilyRuntime = FamilyRuntime;
147
+ _FamilyRuntime_stack = new WeakMap(), _FamilyRuntime_provider = new WeakMap(), _FamilyRuntime_maxOpen = new WeakMap(), _FamilyRuntime_members = new WeakMap(), _FamilyRuntime_useTick = new WeakMap(), _FamilyRuntime_closed = new WeakMap(), _FamilyRuntime_instances = new WeakSet(), _FamilyRuntime_member = function _FamilyRuntime_member(input) {
148
+ const { key, keyId } = this.normalize(input);
149
+ let member = __classPrivateFieldGet(this, _FamilyRuntime_members, "f").get(keyId);
150
+ if (!member) {
151
+ const self = this;
152
+ const entry = {
153
+ name: this.name,
154
+ family: this.name,
155
+ keyId,
156
+ owned: true,
157
+ stack: __classPrivateFieldGet(this, _FamilyRuntime_stack, "f"),
158
+ get db() {
159
+ const state = __classPrivateFieldGet(self, _FamilyRuntime_members, "f").get(keyId);
160
+ if (!state?.db) {
161
+ throw new errors_1.SqlStackError(`Database family member "${self.name}/${keyId}" is not open. ` +
162
+ 'Acquire it through withDatabase(...)/family.with(...) before querying.');
163
+ }
164
+ return state.db;
165
+ },
166
+ };
167
+ member = { entry, leases: 0, lastUsed: 0 };
168
+ __classPrivateFieldGet(this, _FamilyRuntime_members, "f").set(keyId, member);
169
+ }
170
+ return Object.assign(member, { key: this.normalize(input).key });
171
+ }, _FamilyRuntime_open = async function _FamilyRuntime_open(member) {
172
+ if (member.db)
173
+ return;
174
+ if (!member.opening) {
175
+ member.opening = __classPrivateFieldGet(this, _FamilyRuntime_provider, "f").open(member.key).finally(() => {
176
+ member.opening = undefined;
177
+ });
178
+ member.opening.catch(() => undefined); // surfaced to awaiting callers
179
+ }
180
+ const db = await member.opening;
181
+ member.db = db;
182
+ __classPrivateFieldGet(this, _FamilyRuntime_instances, "m", _FamilyRuntime_evictIfNeeded).call(this);
183
+ }, _FamilyRuntime_closeHandle = async function _FamilyRuntime_closeHandle(member) {
184
+ const db = member.db;
185
+ member.db = undefined;
186
+ if (db)
187
+ await db.close();
188
+ }, _FamilyRuntime_evictIfNeeded = function _FamilyRuntime_evictIfNeeded() {
189
+ const open = [...__classPrivateFieldGet(this, _FamilyRuntime_members, "f").values()].filter((m) => m.db);
190
+ if (open.length <= __classPrivateFieldGet(this, _FamilyRuntime_maxOpen, "f"))
191
+ return;
192
+ const evictable = open
193
+ .filter((m) => m.leases === 0)
194
+ .sort((a, b) => a.lastUsed - b.lastUsed);
195
+ for (const member of evictable) {
196
+ if ([...__classPrivateFieldGet(this, _FamilyRuntime_members, "f").values()].filter((m) => m.db).length <= __classPrivateFieldGet(this, _FamilyRuntime_maxOpen, "f"))
197
+ break;
198
+ // Fire-and-forget close; eviction failures must not break the acquiring flow.
199
+ void __classPrivateFieldGet(this, _FamilyRuntime_instances, "m", _FamilyRuntime_closeHandle).call(this, member).catch(() => undefined);
200
+ }
201
+ }, _FamilyRuntime_assertOpen = function _FamilyRuntime_assertOpen() {
202
+ if (__classPrivateFieldGet(this, _FamilyRuntime_closed, "f")) {
203
+ throw new errors_1.SqlStackError(`Database family "${this.name}" belongs to a closed SqlStack.`);
204
+ }
205
+ };
206
+ //# sourceMappingURL=family.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"family.js","sourceRoot":"","sources":["../../src/family.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Database } from './registry';\nimport { SqlStackError } from './core/errors';\nimport type { SqlStack, DatabaseEntry } from './stack';\n\n/**\n * Dynamic database families (spec 17): one registration that resolves many\n * concrete databases by key. The provider owns physical mechanics; the\n * family runtime owns identity, leases, bounded caching, and lifecycle.\n */\n\nexport interface DatabaseInspection {\n state: 'closed' | 'open' | 'missing';\n [fact: string]: unknown;\n}\n\nexport interface DatabaseFamilyProvider<K = string> {\n /** Normalize/validate an application key. Must be deterministic and safe. */\n normalizeKey(input: unknown): K;\n /** Serialize a normalized key to the identity string used for routing. */\n keyId?(key: K): string;\n /** Open an EXISTING database. Must fail if it does not exist (never create). */\n open(key: K): Promise<Database>;\n /** Create a NEW database. Must fail if it already exists. */\n create?(key: K): Promise<Database>;\n /** Explicit create-if-missing. */\n ensure?(key: K): Promise<Database>;\n exists?(key: K): Promise<boolean>;\n /** Physically delete the database (handle already closed by the runtime). */\n delete?(key: K): Promise<void>;\n inspect?(key: K): Promise<DatabaseInspection>;\n list?(): AsyncIterable<K>;\n}\n\nexport interface FamilyConfig<K = string> {\n provider: DatabaseFamilyProvider<K>;\n cache?: {\n /** Max concurrently open members (default 16). Leased members never evict. */\n maxOpen?: number;\n };\n}\n\n/** Concrete member identity: owning stack + family registration + key. */\nexport interface FamilyMemberEntry extends DatabaseEntry {\n readonly family: string;\n readonly keyId: string;\n}\n\nexport interface DatabaseLease {\n readonly entry: FamilyMemberEntry;\n release(): Promise<void>;\n}\n\ntype MemberState = {\n entry: FamilyMemberEntry;\n db?: Database;\n opening?: Promise<Database>;\n leases: number;\n lastUsed: number;\n};\n\nfunction unsupported(family: string, op: string): never {\n throw new SqlStackError(\n `Database family \"${family}\" does not support ${op}(): its provider declares no such capability.`\n );\n}\n\nexport class FamilyRuntime<K = string> {\n readonly name: string;\n readonly #stack: SqlStack;\n readonly #provider: DatabaseFamilyProvider<K>;\n readonly #maxOpen: number;\n readonly #members = new Map<string, MemberState>();\n #useTick = 0;\n #closed = false;\n\n constructor(stack: SqlStack, name: string, config: FamilyConfig<K>) {\n this.#stack = stack;\n this.name = name;\n this.#provider = config.provider;\n this.#maxOpen = config.cache?.maxOpen ?? 16;\n }\n\n normalize(input: unknown): { key: K; keyId: string } {\n const key = this.#provider.normalizeKey(input);\n const keyId = this.#provider.keyId ? this.#provider.keyId(key) : String(key);\n return { key, keyId };\n }\n\n /** Stable concrete identity for a key; no I/O. */\n ref(input: unknown): FamilyMemberEntry {\n return this.#member(input).entry;\n }\n\n async exists(input: unknown): Promise<boolean> {\n const { key } = this.normalize(input);\n if (!this.#provider.exists) unsupported(this.name, 'exists');\n return this.#provider.exists(key);\n }\n\n async create(input: unknown): Promise<void> {\n this.#assertOpen();\n const member = this.#member(input);\n if (!this.#provider.create) unsupported(this.name, 'create');\n const db = await this.#provider.create(member.key);\n await db.close();\n }\n\n async ensure(input: unknown): Promise<void> {\n this.#assertOpen();\n const member = this.#member(input);\n if (!this.#provider.ensure) unsupported(this.name, 'ensure');\n const db = await this.#provider.ensure(member.key);\n await db.close();\n }\n\n /** Acquire a lease on an opened (existing) member. */\n async acquire(input: unknown): Promise<DatabaseLease> {\n this.#assertOpen();\n const member = this.#member(input);\n await this.#open(member);\n member.leases++;\n member.lastUsed = ++this.#useTick;\n let released = false;\n return {\n entry: member.entry,\n release: async () => {\n if (released) return;\n released = true;\n member.leases = Math.max(0, member.leases - 1);\n this.#evictIfNeeded();\n },\n };\n }\n\n async with<T>(input: unknown, fn: (entry: FamilyMemberEntry) => Promise<T>): Promise<T> {\n const lease = await this.acquire(input);\n try {\n return await fn(lease.entry);\n } finally {\n await lease.release();\n }\n }\n\n /** Close/evict a member's cached handle; the database itself is preserved. */\n async closeMember(input: unknown): Promise<void> {\n const { keyId } = this.normalize(input);\n const member = this.#members.get(keyId);\n if (!member?.db) return;\n if (member.leases > 0) {\n throw new SqlStackError(\n `Cannot close \"${this.name}/${keyId}\": ${member.leases} lease(s) are active.`\n );\n }\n await this.#closeHandle(member);\n }\n\n /** Physically delete a member. Fails while leased; closes the cached handle first. */\n async deleteMember(input: unknown): Promise<void> {\n this.#assertOpen();\n const member = this.#member(input);\n if (!this.#provider.delete) unsupported(this.name, 'delete');\n if (member.leases > 0) {\n throw new SqlStackError(\n `Cannot delete \"${this.name}/${member.entry.keyId}\": ${member.leases} lease(s) are active.`\n );\n }\n if (member.db) await this.#closeHandle(member);\n await this.#provider.delete(member.key);\n this.#members.delete(member.entry.keyId);\n }\n\n async inspect(input: unknown): Promise<DatabaseInspection> {\n const member = this.#member(input);\n if (!this.#provider.inspect) unsupported(this.name, 'inspect');\n const facts = await this.#provider.inspect(member.key);\n return { ...facts, cached: !!member.db, activeLeases: member.leases };\n }\n\n /** Close every cached handle (root disposal). Databases are preserved. */\n async closeAll(): Promise<void> {\n this.#closed = true;\n const failures: string[] = [];\n for (const member of this.#members.values()) {\n if (!member.db) continue;\n try {\n await this.#closeHandle(member);\n } catch (err: any) {\n failures.push(`\"${this.name}/${member.entry.keyId}\": ${err?.message ?? String(err)}`);\n }\n }\n this.#members.clear();\n if (failures.length) {\n throw new SqlStackError(`Family close failed for ${failures.join(', ')}`);\n }\n }\n\n #member(input: unknown): MemberState & { key: K } {\n const { key, keyId } = this.normalize(input);\n let member = this.#members.get(keyId);\n if (!member) {\n const self = this;\n const entry: FamilyMemberEntry = {\n name: this.name,\n family: this.name,\n keyId,\n owned: true,\n stack: this.#stack,\n get db(): Database {\n const state = self.#members.get(keyId);\n if (!state?.db) {\n throw new SqlStackError(\n `Database family member \"${self.name}/${keyId}\" is not open. ` +\n 'Acquire it through withDatabase(...)/family.with(...) before querying.'\n );\n }\n return state.db;\n },\n };\n member = { entry, leases: 0, lastUsed: 0 };\n this.#members.set(keyId, member);\n }\n return Object.assign(member, { key: this.normalize(input).key });\n }\n\n async #open(member: MemberState & { key: K }): Promise<void> {\n if (member.db) return;\n if (!member.opening) {\n member.opening = this.#provider.open(member.key).finally(() => {\n member.opening = undefined;\n });\n member.opening.catch(() => undefined); // surfaced to awaiting callers\n }\n const db = await member.opening;\n member.db = db;\n this.#evictIfNeeded();\n }\n\n async #closeHandle(member: MemberState): Promise<void> {\n const db = member.db;\n member.db = undefined;\n if (db) await db.close();\n }\n\n #evictIfNeeded(): void {\n const open = [...this.#members.values()].filter((m) => m.db);\n if (open.length <= this.#maxOpen) return;\n const evictable = open\n .filter((m) => m.leases === 0)\n .sort((a, b) => a.lastUsed - b.lastUsed);\n for (const member of evictable) {\n if ([...this.#members.values()].filter((m) => m.db).length <= this.#maxOpen) break;\n // Fire-and-forget close; eviction failures must not break the acquiring flow.\n void this.#closeHandle(member).catch(() => undefined);\n }\n }\n\n #assertOpen(): void {\n if (this.#closed) {\n throw new SqlStackError(`Database family \"${this.name}\" belongs to a closed SqlStack.`);\n }\n }\n}\n"]}
@@ -0,0 +1,22 @@
1
+ import type { SqlStack } from './stack';
2
+ import type { DatabaseLease } from './family';
3
+ /**
4
+ * SQLStack-owned flow-local family binding (spec 17 §10-11).
5
+ *
6
+ * This carries WHICH concrete member of a dynamic family is selected for the
7
+ * current execution flow. It is deliberately separate from the IoC execution
8
+ * context (which owns root identity) and from transaction state. Frames are
9
+ * keyed by SqlStack instance, so two roots binding the same family name can
10
+ * never observe each other's selection.
11
+ */
12
+ export type BindingFrame = {
13
+ readonly stack: SqlStack;
14
+ readonly family: string;
15
+ readonly keyInput: unknown;
16
+ readonly keyId: string;
17
+ /** Acquired lazily on first database resolution inside the frame. */
18
+ lease?: DatabaseLease;
19
+ };
20
+ export declare function currentBinding(stack: SqlStack, family: string): BindingFrame | undefined;
21
+ export declare function runWithBinding<T>(frame: BindingFrame, fn: () => T): T;
22
+ //# sourceMappingURL=familyBinding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"familyBinding.d.ts","sourceRoot":"","sources":["../../src/familyBinding.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB,CAAC;AAIF,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAQxF;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAGrE"}
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.currentBinding = currentBinding;
4
+ exports.runWithBinding = runWithBinding;
5
+ const node_async_hooks_1 = require("node:async_hooks");
6
+ const als = new node_async_hooks_1.AsyncLocalStorage();
7
+ function currentBinding(stack, family) {
8
+ const frames = als.getStore();
9
+ if (!frames)
10
+ return undefined;
11
+ for (let i = frames.length - 1; i >= 0; i--) {
12
+ const frame = frames[i];
13
+ if (frame.stack === stack && frame.family === family)
14
+ return frame;
15
+ }
16
+ return undefined;
17
+ }
18
+ function runWithBinding(frame, fn) {
19
+ const frames = als.getStore() ?? [];
20
+ return als.run([...frames, frame], fn);
21
+ }
22
+ //# sourceMappingURL=familyBinding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"familyBinding.js","sourceRoot":"","sources":["../../src/familyBinding.ts"],"names":[],"mappings":";;AAwBA,wCAQC;AAED,wCAGC;AArCD,uDAAqD;AAsBrD,MAAM,GAAG,GAAG,IAAI,oCAAiB,EAA2B,CAAC;AAE7D,SAAgB,cAAc,CAAC,KAAe,EAAE,MAAc;IAC5D,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;IAC9B,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC9B,KAAK,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,KAAK,CAAC,KAAK,KAAK,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM;YAAE,OAAO,KAAK,CAAC;IACrE,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAgB,cAAc,CAAI,KAAmB,EAAE,EAAW;IAChE,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC;IACpC,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;AACzC,CAAC","sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { SqlStack } from './stack';\nimport type { DatabaseLease } from './family';\n\n/**\n * SQLStack-owned flow-local family binding (spec 17 §10-11).\n *\n * This carries WHICH concrete member of a dynamic family is selected for the\n * current execution flow. It is deliberately separate from the IoC execution\n * context (which owns root identity) and from transaction state. Frames are\n * keyed by SqlStack instance, so two roots binding the same family name can\n * never observe each other's selection.\n */\nexport type BindingFrame = {\n readonly stack: SqlStack;\n readonly family: string;\n readonly keyInput: unknown;\n readonly keyId: string;\n /** Acquired lazily on first database resolution inside the frame. */\n lease?: DatabaseLease;\n};\n\nconst als = new AsyncLocalStorage<readonly BindingFrame[]>();\n\nexport function currentBinding(stack: SqlStack, family: string): BindingFrame | undefined {\n const frames = als.getStore();\n if (!frames) return undefined;\n for (let i = frames.length - 1; i >= 0; i--) {\n const frame = frames[i];\n if (frame.stack === stack && frame.family === family) return frame;\n }\n return undefined;\n}\n\nexport function runWithBinding<T>(frame: BindingFrame, fn: () => T): T {\n const frames = als.getStore() ?? [];\n return als.run([...frames, frame], fn);\n}\n"]}
@@ -13,5 +13,15 @@ export * from './core/validator';
13
13
  export * from './utils/dto';
14
14
  export type { Dialect, WriteResult, PostgresWriteResult, QueryResult, Database } from './registry';
15
15
  export { SqlStackDB, IStackDB } from './registry';
16
+ export { SqlStack } from './stack';
17
+ export type { DatabaseEntry, DatabaseRegistration, SqlStackConfig } from './stack';
18
+ export { activeSqlStack, resolveExecution } from './runtime';
19
+ export { FamilyRuntime } from './family';
20
+ export type { DatabaseFamilyProvider, FamilyConfig, FamilyMemberEntry, DatabaseLease, DatabaseInspection, } from './family';
21
+ export { sqliteFamily } from './families/sqliteFamily';
22
+ export type { SqliteFamilyOptions } from './families/sqliteFamily';
23
+ export type { SqlResolver, SqlManifest, SqlResolutionRequest, ManifestResolverOptions } from './core/resolver';
24
+ export { FsResolver, ManifestResolver, MANIFEST_DIALECT_EXT } from './core/resolver';
25
+ export { SourceScanResolver } from './core/sourceScanResolver';
16
26
  export * from './binders/sqlBinder';
17
27
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAGzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,gBAAgB,CAAC;AAG/B,cAAc,YAAY,CAAC;AAG3B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AAGjC,cAAc,aAAa,CAAC;AAG5B,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACnG,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGlD,cAAc,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAGzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,gBAAgB,CAAC;AAG/B,cAAc,YAAY,CAAC;AAG3B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AAGjC,cAAc,aAAa,CAAC;AAG5B,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACnG,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGlD,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACnF,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAG7D,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,YAAY,EACV,sBAAsB,EACtB,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,kBAAkB,GACnB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,YAAY,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAGnE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC/G,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAG/D,cAAc,qBAAqB,CAAC"}