neoorm 0.9.6 → 0.9.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/neoorm.d.ts.map +1 -1
- package/dist/bin/neoorm.js +83 -141
- package/dist/bin/neoorm.js.map +1 -1
- package/dist/codegen/column-ts.d.ts.map +1 -1
- package/dist/codegen/column-ts.js +5 -2
- package/dist/codegen/column-ts.js.map +1 -1
- package/dist/codegen/emit-query-types.d.ts.map +1 -1
- package/dist/codegen/emit-query-types.js +35 -0
- package/dist/codegen/emit-query-types.js.map +1 -1
- package/dist/codegen/generate.d.ts +1 -0
- package/dist/codegen/generate.d.ts.map +1 -1
- package/dist/codegen/generate.js +8 -1
- package/dist/codegen/generate.js.map +1 -1
- package/dist/codegen/schema-to-manifest.d.ts.map +1 -1
- package/dist/codegen/schema-to-manifest.js +65 -0
- package/dist/codegen/schema-to-manifest.js.map +1 -1
- package/dist/codegen/validation/from-manifest.d.ts.map +1 -1
- package/dist/codegen/validation/from-manifest.js +12 -2
- package/dist/codegen/validation/from-manifest.js.map +1 -1
- package/dist/dialect/sqlite.test.js +9 -0
- package/dist/dialect/sqlite.test.js.map +1 -1
- package/dist/introspect/mysql/to-manifest.d.ts.map +1 -1
- package/dist/introspect/mysql/to-manifest.js +2 -1
- package/dist/introspect/mysql/to-manifest.js.map +1 -1
- package/dist/introspect/pull.d.ts +5 -0
- package/dist/introspect/pull.d.ts.map +1 -1
- package/dist/introspect/pull.js +160 -22
- package/dist/introspect/pull.js.map +1 -1
- package/dist/introspect/sqlite/to-manifest.d.ts.map +1 -1
- package/dist/introspect/sqlite/to-manifest.js +2 -1
- package/dist/introspect/sqlite/to-manifest.js.map +1 -1
- package/dist/introspect/table-map.d.ts +9 -0
- package/dist/introspect/table-map.d.ts.map +1 -0
- package/dist/introspect/table-map.js +21 -0
- package/dist/introspect/table-map.js.map +1 -0
- package/dist/introspect/to-manifest.d.ts.map +1 -1
- package/dist/introspect/to-manifest.js +2 -1
- package/dist/introspect/to-manifest.js.map +1 -1
- package/dist/migrate/dev-workflow.d.ts +36 -0
- package/dist/migrate/dev-workflow.d.ts.map +1 -0
- package/dist/migrate/dev-workflow.js +96 -0
- package/dist/migrate/dev-workflow.js.map +1 -0
- package/dist/migrate/runner.d.ts.map +1 -1
- package/dist/migrate/runner.js +23 -5
- package/dist/migrate/runner.js.map +1 -1
- package/dist/runtime/driver-sqlite.test.js +115 -0
- package/dist/runtime/driver-sqlite.test.js.map +1 -1
- package/dist/runtime/driver.d.ts +8 -0
- package/dist/runtime/driver.d.ts.map +1 -1
- package/dist/runtime/driver.js +51 -97
- package/dist/runtime/driver.js.map +1 -1
- package/dist/runtime/error-codes.d.ts +3 -0
- package/dist/runtime/error-codes.d.ts.map +1 -1
- package/dist/runtime/error-codes.js +3 -0
- package/dist/runtime/error-codes.js.map +1 -1
- package/dist/runtime/mysql-driver.d.ts.map +1 -1
- package/dist/runtime/mysql-driver.js +5 -1
- package/dist/runtime/mysql-driver.js.map +1 -1
- package/dist/runtime/mysql-family-pool.test.js +14 -1
- package/dist/runtime/mysql-family-pool.test.js.map +1 -1
- package/dist/runtime/mysql-placeholders.d.ts.map +1 -1
- package/dist/runtime/mysql-placeholders.js +12 -0
- package/dist/runtime/mysql-placeholders.js.map +1 -1
- package/dist/runtime/mysql-placeholders.test.js +15 -0
- package/dist/runtime/mysql-placeholders.test.js.map +1 -1
- package/dist/runtime/query/compile-where.d.ts.map +1 -1
- package/dist/runtime/query/compile-where.js +26 -1
- package/dist/runtime/query/compile-where.js.map +1 -1
- package/dist/runtime/query/find-or-create.test.js +19 -0
- package/dist/runtime/query/find-or-create.test.js.map +1 -1
- package/dist/runtime/query/update.d.ts.map +1 -1
- package/dist/runtime/query/update.js +9 -1
- package/dist/runtime/query/update.js.map +1 -1
- package/dist/runtime/query/upsert.d.ts.map +1 -1
- package/dist/runtime/query/upsert.js +12 -3
- package/dist/runtime/query/upsert.js.map +1 -1
- package/dist/runtime/regex-guard.d.ts +28 -0
- package/dist/runtime/regex-guard.d.ts.map +1 -0
- package/dist/runtime/regex-guard.js +124 -0
- package/dist/runtime/regex-guard.js.map +1 -0
- package/dist/runtime/regex-guard.test.d.ts +2 -0
- package/dist/runtime/regex-guard.test.d.ts.map +1 -0
- package/dist/runtime/regex-guard.test.js +63 -0
- package/dist/runtime/regex-guard.test.js.map +1 -0
- package/dist/runtime/sqlite-open.d.ts.map +1 -1
- package/dist/runtime/sqlite-open.js +22 -7
- package/dist/runtime/sqlite-open.js.map +1 -1
- package/dist/runtime/sqlite-open.test.d.ts +2 -0
- package/dist/runtime/sqlite-open.test.d.ts.map +1 -0
- package/dist/runtime/sqlite-open.test.js +66 -0
- package/dist/runtime/sqlite-open.test.js.map +1 -0
- package/dist/sql/split-statements.d.ts +15 -0
- package/dist/sql/split-statements.d.ts.map +1 -0
- package/dist/sql/split-statements.js +132 -0
- package/dist/sql/split-statements.js.map +1 -0
- package/docs/migrate-dev-workflow.md +84 -0
- package/docs/migrations.md +3 -0
- package/docs/sqlite.md +2 -0
- package/llms-full.txt +90 -0
- package/package.json +1 -1
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guards SQLite `search`/`REGEXP` against catastrophic-backtracking (ReDoS)
|
|
3
|
+
* patterns.
|
|
4
|
+
*
|
|
5
|
+
* On SQLite the pattern runs as an in-process JavaScript `RegExp` inside the
|
|
6
|
+
* driver's single shared connection, so an exponential-backtracking pattern
|
|
7
|
+
* (e.g. `(a+)+$`) freezes the whole event loop, not just one query.
|
|
8
|
+
* PostgreSQL and MySQL execute REGEXP in the database server, where
|
|
9
|
+
* `statement_timeout` bounds a runaway match, so this guard is SQLite-only.
|
|
10
|
+
*
|
|
11
|
+
* The detector flags the classic nested-unbounded-quantifier shape (star
|
|
12
|
+
* height ≥ 2): an unbounded quantifier (`*`, `+`, `{n,}`) applied to a group
|
|
13
|
+
* whose body itself contains an unbounded quantifier — `(a+)+`, `(a*)*`,
|
|
14
|
+
* `(\d+)+`, `((ab)+)+`. It is a heuristic: overlapping-alternation shapes
|
|
15
|
+
* like `(a|a)*` slip through, and a rare ambiguous-but-practically-linear
|
|
16
|
+
* pattern like `(a+b)+` is rejected. `SqliteClientOptions.unsafeAllowRegex`
|
|
17
|
+
* opts out for the latter case.
|
|
18
|
+
*/
|
|
19
|
+
const OPEN_ENDED_REPEAT = /\{\d+,\}\??/y;
|
|
20
|
+
/** Length of the unbounded quantifier (`*`, `+`, `{n,}`, with optional lazy `?`) at `index`, or 0. */
|
|
21
|
+
function unboundedQuantifierLength(pattern, index) {
|
|
22
|
+
const ch = pattern[index];
|
|
23
|
+
if (ch === "*" || ch === "+") {
|
|
24
|
+
return pattern[index + 1] === "?" ? 2 : 1;
|
|
25
|
+
}
|
|
26
|
+
if (ch === "{") {
|
|
27
|
+
OPEN_ENDED_REPEAT.lastIndex = index;
|
|
28
|
+
return OPEN_ENDED_REPEAT.exec(pattern)?.[0].length ?? 0;
|
|
29
|
+
}
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Returns true when `pattern` nests an unbounded quantifier inside another
|
|
34
|
+
* unbounded quantifier — the shape that backtracks exponentially. Assumes a
|
|
35
|
+
* syntactically valid pattern (callers compile with `new RegExp` anyway, so
|
|
36
|
+
* malformed input surfaces as a SyntaxError after this returns false).
|
|
37
|
+
*/
|
|
38
|
+
export function hasCatastrophicBacktracking(pattern) {
|
|
39
|
+
// One frame per open group; index 0 is the synthetic top level. Each frame
|
|
40
|
+
// records whether its body contains an unbounded quantifier.
|
|
41
|
+
const frames = [false];
|
|
42
|
+
// True when the preceding atom was itself unbounded-quantified, so a
|
|
43
|
+
// following quantifier wraps it: `a+*` is `(a+)*`.
|
|
44
|
+
let prevQuantifiedAtom = false;
|
|
45
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
46
|
+
const ch = pattern[i];
|
|
47
|
+
if (ch === "\\") {
|
|
48
|
+
i++;
|
|
49
|
+
prevQuantifiedAtom = false;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (ch === "[") {
|
|
53
|
+
// Character-class contents are literals; skip to the closing bracket.
|
|
54
|
+
i++;
|
|
55
|
+
if (pattern[i] === "^")
|
|
56
|
+
i++;
|
|
57
|
+
if (pattern[i] === "]")
|
|
58
|
+
i++;
|
|
59
|
+
while (i < pattern.length && pattern[i] !== "]") {
|
|
60
|
+
if (pattern[i] === "\\")
|
|
61
|
+
i++;
|
|
62
|
+
i++;
|
|
63
|
+
}
|
|
64
|
+
prevQuantifiedAtom = false;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (ch === "(") {
|
|
68
|
+
frames.push(false);
|
|
69
|
+
prevQuantifiedAtom = false;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (ch === ")") {
|
|
73
|
+
if (frames.length === 1) {
|
|
74
|
+
// Unbalanced `)`; RegExp will reject the pattern later.
|
|
75
|
+
prevQuantifiedAtom = false;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const bodyHasQuantifier = frames.pop() ?? false;
|
|
79
|
+
const quantifier = unboundedQuantifierLength(pattern, i + 1);
|
|
80
|
+
// The quantifier wraps the whole group body: nested iff the body
|
|
81
|
+
// itself contains an unbounded quantifier.
|
|
82
|
+
if (bodyHasQuantifier && quantifier > 0)
|
|
83
|
+
return true;
|
|
84
|
+
if (quantifier > 0) {
|
|
85
|
+
// The quantifier starts at i+1; skip past its last char so it
|
|
86
|
+
// is not re-processed as a fresh quantifier below.
|
|
87
|
+
i += quantifier;
|
|
88
|
+
prevQuantifiedAtom = true;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
prevQuantifiedAtom = false;
|
|
92
|
+
}
|
|
93
|
+
// The group's contents (and its quantifier, if any) live inside the
|
|
94
|
+
// parent body: `((a+))+` is star height 2 once the outer group is
|
|
95
|
+
// quantified.
|
|
96
|
+
if (bodyHasQuantifier || quantifier > 0) {
|
|
97
|
+
frames[frames.length - 1] = true;
|
|
98
|
+
}
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const quantifier = unboundedQuantifierLength(pattern, i);
|
|
102
|
+
if (quantifier > 0) {
|
|
103
|
+
if (prevQuantifiedAtom)
|
|
104
|
+
return true;
|
|
105
|
+
i += quantifier - 1;
|
|
106
|
+
frames[frames.length - 1] = true;
|
|
107
|
+
prevQuantifiedAtom = true;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
prevQuantifiedAtom = false;
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
const MAX_SHOWN_PATTERN_LENGTH = 100;
|
|
115
|
+
/** Message for a rejected pattern; safe to surface in query errors. */
|
|
116
|
+
export function catastrophicRegexMessage(pattern) {
|
|
117
|
+
const shown = pattern.length > MAX_SHOWN_PATTERN_LENGTH
|
|
118
|
+
? `${pattern.slice(0, MAX_SHOWN_PATTERN_LENGTH)}…`
|
|
119
|
+
: pattern;
|
|
120
|
+
return (`SQLite REGEXP pattern ${JSON.stringify(shown)} nests unbounded quantifiers ` +
|
|
121
|
+
'(e.g. "(a+)+") and can backtrack exponentially, freezing the process. ' +
|
|
122
|
+
"Simplify the pattern, or pass { unsafeAllowRegex: true } to sqliteClient() to allow it.");
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=regex-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"regex-guard.js","sourceRoot":"","sources":["../../src/runtime/regex-guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"regex-guard.test.d.ts","sourceRoot":"","sources":["../../src/runtime/regex-guard.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { catastrophicRegexMessage, hasCatastrophicBacktracking, } from "./regex-guard.js";
|
|
3
|
+
describe("hasCatastrophicBacktracking", () => {
|
|
4
|
+
it.each([
|
|
5
|
+
"(a+)+",
|
|
6
|
+
"(a*)*",
|
|
7
|
+
"(a+)*",
|
|
8
|
+
"(a*)+",
|
|
9
|
+
"(a+)+$",
|
|
10
|
+
"^(a+)+$",
|
|
11
|
+
"(\\d+)+",
|
|
12
|
+
"((ab)+)+",
|
|
13
|
+
"(?:x+){2,}",
|
|
14
|
+
"(\\w+\\s*)+",
|
|
15
|
+
"(a{2,})+",
|
|
16
|
+
"a+*",
|
|
17
|
+
"(a+){3,}",
|
|
18
|
+
])("flags nested unbounded quantifiers: %s", (pattern) => {
|
|
19
|
+
expect(hasCatastrophicBacktracking(pattern)).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
it.each([
|
|
22
|
+
"(a)+",
|
|
23
|
+
"a+",
|
|
24
|
+
"^foo$",
|
|
25
|
+
"[a+]+",
|
|
26
|
+
"([a+])+",
|
|
27
|
+
"https?://",
|
|
28
|
+
"a+b+",
|
|
29
|
+
"foo{2,3}",
|
|
30
|
+
"(?:ab)+",
|
|
31
|
+
"(foo|bar)+",
|
|
32
|
+
"(\\+\\+)+",
|
|
33
|
+
"\\+",
|
|
34
|
+
"(a+)?",
|
|
35
|
+
"(a+){2,3}",
|
|
36
|
+
"[0-9]+\\.[0-9]+",
|
|
37
|
+
])("allows benign patterns: %s", (pattern) => {
|
|
38
|
+
expect(hasCatastrophicBacktracking(pattern)).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
it.each([
|
|
41
|
+
")",
|
|
42
|
+
"(a+",
|
|
43
|
+
"[a+",
|
|
44
|
+
"{2,}",
|
|
45
|
+
"(?",
|
|
46
|
+
"[]",
|
|
47
|
+
])("does not throw on malformed input: %s", (pattern) => {
|
|
48
|
+
expect(() => hasCatastrophicBacktracking(pattern)).not.toThrow();
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe("catastrophicRegexMessage", () => {
|
|
52
|
+
it("names the pattern and the opt-out", () => {
|
|
53
|
+
const message = catastrophicRegexMessage("(a+)+$");
|
|
54
|
+
expect(message).toContain("(a+)+$");
|
|
55
|
+
expect(message).toContain("unsafeAllowRegex");
|
|
56
|
+
});
|
|
57
|
+
it("truncates very long patterns", () => {
|
|
58
|
+
const message = catastrophicRegexMessage("a".repeat(500));
|
|
59
|
+
expect(message).toContain(`${"a".repeat(100)}…`);
|
|
60
|
+
expect(message).not.toContain("a".repeat(101));
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
//# sourceMappingURL=regex-guard.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"regex-guard.test.js","sourceRoot":"","sources":["../../src/runtime/regex-guard.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACN,wBAAwB,EACxB,2BAA2B,GAC3B,MAAM,kBAAkB,CAAC;AAE1B,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;IAC5C,EAAE,CAAC,IAAI,CAAC;QACP,OAAO;QACP,OAAO;QACP,OAAO;QACP,OAAO;QACP,QAAQ;QACR,SAAS;QACT,SAAS;QACT,UAAU;QACV,YAAY;QACZ,aAAa;QACb,UAAU;QACV,KAAK;QACL,UAAU;KACV,CAAC,CAAC,wCAAwC,EAAE,CAAC,OAAO,EAAE,EAAE;QACxD,MAAM,CAAC,2BAA2B,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,IAAI,CAAC;QACP,MAAM;QACN,IAAI;QACJ,OAAO;QACP,OAAO;QACP,SAAS;QACT,WAAW;QACX,MAAM;QACN,UAAU;QACV,SAAS;QACT,YAAY;QACZ,WAAW;QACX,KAAK;QACL,OAAO;QACP,WAAW;QACX,iBAAiB;KACjB,CAAC,CAAC,4BAA4B,EAAE,CAAC,OAAO,EAAE,EAAE;QAC5C,MAAM,CAAC,2BAA2B,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,IAAI,CAAC;QACP,GAAG;QACH,KAAK;QACL,KAAK;QACL,MAAM;QACN,IAAI;QACJ,IAAI;KACJ,CAAC,CAAC,uCAAuC,EAAE,CAAC,OAAO,EAAE,EAAE;QACvD,MAAM,CAAC,GAAG,EAAE,CAAC,2BAA2B,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAClE,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC5C,MAAM,OAAO,GAAG,wBAAwB,CAAC,QAAQ,CAAC,CAAC;QACnD,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACpC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACvC,MAAM,OAAO,GAAG,wBAAwB,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1D,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjD,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-open.d.ts","sourceRoot":"","sources":["../../src/runtime/sqlite-open.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"sqlite-open.d.ts","sourceRoot":"","sources":["../../src/runtime/sqlite-open.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAqBtD,oFAAoF;AACpF,wBAAgB,yBAAyB,CACxC,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,WAAW,EAAE,MAAM,GAAG,SAAS,GAC7B,MAAM,CAOR;AAED,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,kBAAkB,CAsC3E"}
|
|
@@ -2,12 +2,18 @@ import { createRequire } from "node:module";
|
|
|
2
2
|
import { schemaError } from "./error-builders.js";
|
|
3
3
|
import { SchemaErrorCode } from "./error-codes.js";
|
|
4
4
|
const require = createRequire(import.meta.url);
|
|
5
|
-
|
|
5
|
+
// A `scheme://` prefix marks a connection URL. Only `file:`/`sqlite:` are
|
|
6
|
+
// valid SQLite URIs; every other scheme (postgres://, mysql://, mariadb://, …)
|
|
7
|
+
// is another engine's connection URL and must never become a literal file path.
|
|
8
|
+
const URL_SCHEME = /^([a-z][a-z0-9+.-]*):\/\//i;
|
|
9
|
+
const SQLITE_URI_SCHEMES = new Set(["file", "sqlite"]);
|
|
6
10
|
function usableSqlitePath(path) {
|
|
7
11
|
if (!path)
|
|
8
12
|
return undefined;
|
|
9
|
-
|
|
13
|
+
const scheme = URL_SCHEME.exec(path)?.[1]?.toLowerCase();
|
|
14
|
+
if (scheme !== undefined && !SQLITE_URI_SCHEMES.has(scheme)) {
|
|
10
15
|
return undefined;
|
|
16
|
+
}
|
|
11
17
|
return path;
|
|
12
18
|
}
|
|
13
19
|
/** File path or `:memory:` for SQLite. Ignores PostgreSQL `DATABASE_URL` values. */
|
|
@@ -20,20 +26,29 @@ export function resolveSqliteDatabasePath(databasePath, manifestUrl) {
|
|
|
20
26
|
export function openSqliteDatabase(databasePath) {
|
|
21
27
|
const bunRuntime = typeof globalThis.Bun !== "undefined";
|
|
22
28
|
if (bunRuntime) {
|
|
29
|
+
let Database;
|
|
23
30
|
try {
|
|
24
|
-
|
|
25
|
-
return new Database(databasePath);
|
|
31
|
+
Database = require("bun:sqlite").Database;
|
|
26
32
|
}
|
|
27
33
|
catch {
|
|
28
|
-
// fall through to node:sqlite
|
|
34
|
+
// bun:sqlite unavailable — fall through to node:sqlite
|
|
35
|
+
}
|
|
36
|
+
if (Database) {
|
|
37
|
+
// Open outside the require try: a real open failure (bad path,
|
|
38
|
+
// permissions, is-a-directory) must surface as itself, not be
|
|
39
|
+
// conflated with a missing driver.
|
|
40
|
+
return new Database(databasePath);
|
|
29
41
|
}
|
|
30
42
|
}
|
|
43
|
+
let DatabaseSync;
|
|
31
44
|
try {
|
|
32
|
-
|
|
33
|
-
return new DatabaseSync(databasePath);
|
|
45
|
+
DatabaseSync = require("node:sqlite").DatabaseSync;
|
|
34
46
|
}
|
|
35
47
|
catch {
|
|
36
48
|
throw schemaError(SchemaErrorCode.invalid_config, "No SQLite driver available. Provide a `db` instance, or run on Bun or Node.js 22.5+ so `databasePath` can be opened automatically.");
|
|
37
49
|
}
|
|
50
|
+
// Open outside the require try: a real open failure must surface as itself,
|
|
51
|
+
// not be reported as "No SQLite driver available".
|
|
52
|
+
return new DatabaseSync(databasePath);
|
|
38
53
|
}
|
|
39
54
|
//# sourceMappingURL=sqlite-open.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-open.js","sourceRoot":"","sources":["../../src/runtime/sqlite-open.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;AAE/C,MAAM,
|
|
1
|
+
{"version":3,"file":"sqlite-open.js","sourceRoot":"","sources":["../../src/runtime/sqlite-open.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;AAE/C,0EAA0E;AAC1E,+EAA+E;AAC/E,gFAAgF;AAChF,MAAM,UAAU,GAAG,4BAA4B,CAAC;AAChD,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;AAEvD,SAAS,gBAAgB,CAAC,IAAwB;IACjD,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;IACzD,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7D,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,yBAAyB,CACxC,YAAgC,EAChC,WAA+B;IAE/B,OAAO,CACN,gBAAgB,CAAC,YAAY,CAAC;QAC9B,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QAC1C,gBAAgB,CAAC,WAAW,CAAC;QAC7B,UAAU,CACV,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,YAAoB;IACtD,MAAM,UAAU,GACf,OAAQ,UAAgC,CAAC,GAAG,KAAK,WAAW,CAAC;IAC9D,IAAI,UAAU,EAAE,CAAC;QAChB,IAAI,QAAgE,CAAC;QACrE,IAAI,CAAC;YACJ,QAAQ,GACP,OAAO,CAAC,YAAY,CAGpB,CAAC,QAAQ,CAAC;QACZ,CAAC;QAAC,MAAM,CAAC;YACR,uDAAuD;QACxD,CAAC;QACD,IAAI,QAAQ,EAAE,CAAC;YACd,+DAA+D;YAC/D,8DAA8D;YAC9D,mCAAmC;YACnC,OAAO,IAAI,QAAQ,CAAC,YAAY,CAAC,CAAC;QACnC,CAAC;IACF,CAAC;IAED,IAAI,YAAoE,CAAC;IACzE,IAAI,CAAC;QACJ,YAAY,GACX,OAAO,CAAC,aAAa,CAGrB,CAAC,YAAY,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACR,MAAM,WAAW,CAChB,eAAe,CAAC,cAAc,EAC9B,oIAAoI,CACpI,CAAC;IACH,CAAC;IACD,4EAA4E;IAC5E,mDAAmD;IACnD,OAAO,IAAI,YAAY,CAAC,YAAY,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-open.test.d.ts","sourceRoot":"","sources":["../../src/runtime/sqlite-open.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { mkdirSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterAll, afterEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { openSqliteDatabase, resolveSqliteDatabasePath, } from "./sqlite-open.js";
|
|
6
|
+
const workDir = join(tmpdir(), "neoorm-sqlite-open-test");
|
|
7
|
+
afterAll(() => {
|
|
8
|
+
rmSync(workDir, { recursive: true, force: true });
|
|
9
|
+
});
|
|
10
|
+
describe("openSqliteDatabase error reporting", () => {
|
|
11
|
+
it("surfaces a real open failure instead of 'no driver available'", () => {
|
|
12
|
+
// node:sqlite is present here (its require succeeds). Only the open
|
|
13
|
+
// fails because the parent directory does not exist. That real error
|
|
14
|
+
// must not be swallowed and re-reported as a missing driver.
|
|
15
|
+
mkdirSync(workDir, { recursive: true });
|
|
16
|
+
expect(() => openSqliteDatabase(join(workDir, "no", "such", "dir", "db.sqlite"))).toThrowError(/unable to open database file/);
|
|
17
|
+
expect(() => openSqliteDatabase(join(workDir, "no", "such", "dir", "db.sqlite"))).not.toThrowError(/No SQLite driver available/);
|
|
18
|
+
});
|
|
19
|
+
it("surfaces a real open failure when the path is a directory", () => {
|
|
20
|
+
mkdirSync(workDir, { recursive: true });
|
|
21
|
+
expect(() => openSqliteDatabase(workDir)).toThrowError(/unable to open database file/);
|
|
22
|
+
expect(() => openSqliteDatabase(workDir)).not.toThrowError(/No SQLite driver available/);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
describe("resolveSqliteDatabasePath URL handling", () => {
|
|
26
|
+
const envSave = process.env.DATABASE_URL;
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
delete process.env.DATABASE_URL;
|
|
29
|
+
if (envSave !== undefined)
|
|
30
|
+
process.env.DATABASE_URL = envSave;
|
|
31
|
+
});
|
|
32
|
+
it("ignores non-SQLite connection URLs (mysql, mariadb, …), like postgres", () => {
|
|
33
|
+
delete process.env.DATABASE_URL;
|
|
34
|
+
// These are other engines' connection URLs, never literal file paths.
|
|
35
|
+
expect(resolveSqliteDatabasePath(undefined, "mysql://h/db")).toBe(":memory:");
|
|
36
|
+
expect(resolveSqliteDatabasePath(undefined, "mariadb://h/db")).toBe(":memory:");
|
|
37
|
+
expect(resolveSqliteDatabasePath(undefined, "postgres://h/db")).toBe(":memory:");
|
|
38
|
+
expect(resolveSqliteDatabasePath(undefined, "postgresql://h/db")).toBe(":memory:");
|
|
39
|
+
expect(resolveSqliteDatabasePath(undefined, "mongodb://h/db")).toBe(":memory:");
|
|
40
|
+
});
|
|
41
|
+
it("passes SQLite file URIs and plain paths through unchanged", () => {
|
|
42
|
+
delete process.env.DATABASE_URL;
|
|
43
|
+
expect(resolveSqliteDatabasePath(undefined, "file:/tmp/x.db")).toBe("file:/tmp/x.db");
|
|
44
|
+
expect(resolveSqliteDatabasePath(undefined, "file:///tmp/x.db")).toBe("file:///tmp/x.db");
|
|
45
|
+
expect(resolveSqliteDatabasePath(undefined, "sqlite:/tmp/x.db")).toBe("sqlite:/tmp/x.db");
|
|
46
|
+
expect(resolveSqliteDatabasePath(undefined, ":memory:")).toBe(":memory:");
|
|
47
|
+
expect(resolveSqliteDatabasePath("./rel.db", undefined)).toBe("./rel.db");
|
|
48
|
+
// A Windows path has a drive letter but no `//`, so it is not a URL.
|
|
49
|
+
expect(resolveSqliteDatabasePath(undefined, "C:\\data\\x.db")).toBe("C:\\data\\x.db");
|
|
50
|
+
});
|
|
51
|
+
it("falls through a bad explicit URL to the next usable source", () => {
|
|
52
|
+
delete process.env.DATABASE_URL;
|
|
53
|
+
// explicit databasePath is a mysql URL → not usable → manifest path wins
|
|
54
|
+
expect(resolveSqliteDatabasePath("mysql://h/db", "/tmp/ok.db")).toBe("/tmp/ok.db");
|
|
55
|
+
});
|
|
56
|
+
it("prefers an explicit path and falls back to :memory:", () => {
|
|
57
|
+
delete process.env.DATABASE_URL;
|
|
58
|
+
expect(resolveSqliteDatabasePath("/abs/x.db", "mysql://h/db")).toBe("/abs/x.db");
|
|
59
|
+
expect(resolveSqliteDatabasePath(undefined, undefined)).toBe(":memory:");
|
|
60
|
+
});
|
|
61
|
+
it("ignores a non-SQLite DATABASE_URL and uses the manifest url", () => {
|
|
62
|
+
process.env.DATABASE_URL = "postgres://env/db";
|
|
63
|
+
expect(resolveSqliteDatabasePath(undefined, "/tmp/m.db")).toBe("/tmp/m.db");
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
//# sourceMappingURL=sqlite-open.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-open.test.js","sourceRoot":"","sources":["../../src/runtime/sqlite-open.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type SplitStatementsOptions = {
|
|
2
|
+
/**
|
|
3
|
+
* Treat `\` as an escape character inside quoted strings and quoted
|
|
4
|
+
* identifiers (MySQL/MariaDB string syntax). Off by default: SQLite and
|
|
5
|
+
* PostgreSQL treat a backslash as a literal character in strings.
|
|
6
|
+
*/
|
|
7
|
+
backslashEscapes?: boolean;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Split a SQL script into individual statements on `;` boundaries, honoring
|
|
11
|
+
* quoted strings, quoted identifiers (backticks/double quotes), and
|
|
12
|
+
* line/block comments — so `;` inside any of them never splits.
|
|
13
|
+
*/
|
|
14
|
+
export declare function splitStatements(sql: string, options?: SplitStatementsOptions): string[];
|
|
15
|
+
//# sourceMappingURL=split-statements.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"split-statements.d.ts","sourceRoot":"","sources":["../../src/sql/split-statements.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,sBAAsB,GAAG;IACpC;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,eAAe,CAC9B,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,sBAAsB,GAC9B,MAAM,EAAE,CA6HV"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Split a SQL script into individual statements on `;` boundaries, honoring
|
|
3
|
+
* quoted strings, quoted identifiers (backticks/double quotes), and
|
|
4
|
+
* line/block comments — so `;` inside any of them never splits.
|
|
5
|
+
*/
|
|
6
|
+
export function splitStatements(sql, options) {
|
|
7
|
+
const backslash = options?.backslashEscapes === true;
|
|
8
|
+
const statements = [];
|
|
9
|
+
let current = "";
|
|
10
|
+
let inSingle = false;
|
|
11
|
+
let inDouble = false;
|
|
12
|
+
let inBacktick = false;
|
|
13
|
+
let inLineComment = false;
|
|
14
|
+
let inBlockComment = false;
|
|
15
|
+
for (let i = 0; i < sql.length; i++) {
|
|
16
|
+
const ch = sql[i];
|
|
17
|
+
const next = sql[i + 1];
|
|
18
|
+
if (inLineComment) {
|
|
19
|
+
current += ch;
|
|
20
|
+
if (ch === "\n")
|
|
21
|
+
inLineComment = false;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (inBlockComment) {
|
|
25
|
+
current += ch;
|
|
26
|
+
if (ch === "*" && next === "/") {
|
|
27
|
+
current += next;
|
|
28
|
+
i++;
|
|
29
|
+
inBlockComment = false;
|
|
30
|
+
}
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (inSingle) {
|
|
34
|
+
current += ch;
|
|
35
|
+
if (backslash && ch === "\\") {
|
|
36
|
+
if (next !== undefined) {
|
|
37
|
+
current += next;
|
|
38
|
+
i++;
|
|
39
|
+
}
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (ch === "'") {
|
|
43
|
+
if (next === "'") {
|
|
44
|
+
current += next;
|
|
45
|
+
i++;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
inSingle = false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (inDouble) {
|
|
54
|
+
current += ch;
|
|
55
|
+
if (backslash && ch === "\\") {
|
|
56
|
+
if (next !== undefined) {
|
|
57
|
+
current += next;
|
|
58
|
+
i++;
|
|
59
|
+
}
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (ch === '"') {
|
|
63
|
+
if (next === '"') {
|
|
64
|
+
current += next;
|
|
65
|
+
i++;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
inDouble = false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (inBacktick) {
|
|
74
|
+
current += ch;
|
|
75
|
+
if (backslash && ch === "\\") {
|
|
76
|
+
if (next !== undefined) {
|
|
77
|
+
current += next;
|
|
78
|
+
i++;
|
|
79
|
+
}
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (ch === "`") {
|
|
83
|
+
if (next === "`") {
|
|
84
|
+
current += next;
|
|
85
|
+
i++;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
inBacktick = false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (ch === "-" && next === "-") {
|
|
94
|
+
inLineComment = true;
|
|
95
|
+
current += ch;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (ch === "/" && next === "*") {
|
|
99
|
+
inBlockComment = true;
|
|
100
|
+
current += ch;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (ch === "'") {
|
|
104
|
+
inSingle = true;
|
|
105
|
+
current += ch;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (ch === '"') {
|
|
109
|
+
inDouble = true;
|
|
110
|
+
current += ch;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (ch === "`") {
|
|
114
|
+
inBacktick = true;
|
|
115
|
+
current += ch;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (ch === ";") {
|
|
119
|
+
if (current.trim().length > 0) {
|
|
120
|
+
statements.push(current.trim());
|
|
121
|
+
}
|
|
122
|
+
current = "";
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
current += ch;
|
|
126
|
+
}
|
|
127
|
+
if (current.trim().length > 0) {
|
|
128
|
+
statements.push(current.trim());
|
|
129
|
+
}
|
|
130
|
+
return statements.length > 0 ? statements : [sql];
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=split-statements.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"split-statements.js","sourceRoot":"","sources":["../../src/sql/split-statements.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# `migrate dev` Workflow
|
|
2
|
+
|
|
3
|
+
This guide records the workflow contract and the implementation changes behind `neoorm migrate dev`. The refactor preserves the command's migration order and user-visible output timing.
|
|
4
|
+
|
|
5
|
+
## Runtime contract
|
|
6
|
+
|
|
7
|
+
`runMigrateDev` derives `migrationsDir` from `outDir` and runs the complete sequence inside `withMigrateDevLock`:
|
|
8
|
+
|
|
9
|
+
1. Read the snapshot, compile the target schema, reconcile pending `CREATE TABLE` migrations against the live database, then apply remaining pending migrations.
|
|
10
|
+
2. Generate from the current schema. If generation fails with the existing missing-migration-name error, no explicit name was supplied, and the CLI provided an interactive prompt callback, ask once and retry once. Other errors propagate unchanged. If generation needs no migration, it does not prompt.
|
|
11
|
+
3. Emit the generation summary and warnings, then reconcile a newly written migration against existing tables or apply it when reconciliation did not record it.
|
|
12
|
+
4. Return whether destructive changes were blocked; the CLI sets `process.exitCode` after the workflow completes.
|
|
13
|
+
|
|
14
|
+
The lock covers snapshot reading, both reconciliation/deployment phases, generation, and the optional prompt. Progress events keep the CLI output at the same stage as before: pending-migration output precedes a possible prompt, while generation output precedes post-generation reconciliation/application.
|
|
15
|
+
|
|
16
|
+
## File-by-file changes
|
|
17
|
+
|
|
18
|
+
### `src/migrate/dev-workflow.ts`
|
|
19
|
+
|
|
20
|
+
- **Before:** The ordered `migrate dev` sequence had no dedicated workflow module; the CLI path coordinated it.
|
|
21
|
+
- **After:** `runMigrateDev` owns snapshot reading, initial reconciliation/deployment, generation, the optional one-shot prompt/retry, and post-generation reconciliation/deployment. A typed progress-event union reports completed stages, while existing migration and code-generation helpers remain responsible for their work. The migration directory is derived from `outDir`.
|
|
22
|
+
- **Why it is useful:** The lock scope and ordering have one owner, and the workflow can be tested through a small interface without a CLI/TTY harness.
|
|
23
|
+
|
|
24
|
+
### `src/bin/neoorm.ts`
|
|
25
|
+
|
|
26
|
+
- **Before:** The CLI command path contained the `migrate dev` orchestration alongside the separate `migrate deploy` path.
|
|
27
|
+
- **After:** `migrate dev` calls `runMigrateDev`, adapts its progress events to the existing console output, supplies the prompt callback only for interactive runs, and handles the returned destructive-blocked status. `migrate deploy` remains on its separate path.
|
|
28
|
+
- **Why it is useful:** Terminal interaction and presentation stay in the CLI, while moving orchestration does not delay or reorder output around the prompt.
|
|
29
|
+
|
|
30
|
+
### `src/codegen/generate.ts`
|
|
31
|
+
|
|
32
|
+
- **Before:** The missing-migration-name predicate lived in the CLI even though both regular `generate` and `migrate dev` use it.
|
|
33
|
+
- **After:** The same predicate lives beside `missingMigrationNameError` in the code-generation module and is shared by both callers.
|
|
34
|
+
- **Why it is useful:** There is one definition of the narrow prompt-eligibility check; its error conditions are unchanged, not broadened into general error handling.
|
|
35
|
+
|
|
36
|
+
### `test/migrate-dev-workflow.test.ts`
|
|
37
|
+
|
|
38
|
+
- **Before:** Existing tests covered migration, generation, and lock helpers separately, but not their ordering at the workflow boundary.
|
|
39
|
+
- **After:** Real in-memory SQLite tests verify that a pending migration is applied and the lock remains held when the controlled name prompt runs, and that an unchanged schema completes without prompting or creating another migration.
|
|
40
|
+
- **Why it is useful:** The regression coverage checks observable database state and timing rather than mocked helper forwarding or CLI wiring.
|
|
41
|
+
|
|
42
|
+
### `docs/migrations.md`
|
|
43
|
+
|
|
44
|
+
- **Before:** The migration guide described command behavior but did not state the full `migrate dev` lock interval.
|
|
45
|
+
- **After:** It documents that the lock starts before pending reconciliation and remains held through the optional name prompt, generation, and post-generation reconciliation/application; it links to this implementation guide.
|
|
46
|
+
- **Why it is useful:** Readers can reason about concurrency and prompt timing from the documented contract.
|
|
47
|
+
|
|
48
|
+
### `llms-full.txt`
|
|
49
|
+
|
|
50
|
+
- **Before:** The generated documentation aggregate did not include the updated lock-lifetime note or this guide.
|
|
51
|
+
- **After:** Regeneration mirrors the migration-guide update and includes the new workflow page.
|
|
52
|
+
- **Why it is useful:** The aggregate stays aligned with the source documentation instead of presenting stale workflow details.
|
|
53
|
+
|
|
54
|
+
### `examples/blog/seed.ts`
|
|
55
|
+
|
|
56
|
+
- **Before:** The schema was imported as a runtime value even though it was used only for a type.
|
|
57
|
+
- **After:** The import is type-only.
|
|
58
|
+
- **Why it is useful:** TypeScript erases the import from emitted JavaScript, and lint no longer reports the type-only usage.
|
|
59
|
+
|
|
60
|
+
### `docs/migrate-dev-workflow.md`
|
|
61
|
+
|
|
62
|
+
- **Before:** There was no durable, file-by-file record of the workflow ownership, preserved behavior, and verification.
|
|
63
|
+
- **After:** This page records the execution contract, concrete before/after changes, and their rationale.
|
|
64
|
+
- **Why it is useful:** Future maintenance can distinguish an ownership refactor from an intended behavior change.
|
|
65
|
+
|
|
66
|
+
## Preserved behavior and scope
|
|
67
|
+
|
|
68
|
+
- Pending migrations are reconciled and applied before a missing name can be requested.
|
|
69
|
+
- The prompt is limited to the existing missing-name condition and is attempted at most once; unchanged schemas do not prompt.
|
|
70
|
+
- The migrate-dev lock remains held during the prompt and until post-generation reconciliation/application finishes.
|
|
71
|
+
- The existing second schema compilation remains; this change does not optimize it.
|
|
72
|
+
- `migrate deploy` keeps its separate execution path.
|
|
73
|
+
|
|
74
|
+
## Verification
|
|
75
|
+
|
|
76
|
+
The implementation verification below was recorded before this documentation continuation; it was not rerun as part of the documentation-only changes:
|
|
77
|
+
|
|
78
|
+
- `bun run build` — passed.
|
|
79
|
+
- `bun run test` — 128 files passed, 4 skipped; 1,215 tests passed, 42 skipped.
|
|
80
|
+
- `bun run typecheck` and `bun run lint` — passed; lint was clean.
|
|
81
|
+
- Focused migration suites — 4 files and 34 tests passed.
|
|
82
|
+
- CLI fixture smoke — `migrate dev --name initial`, a no-name no-op `migrate dev`, and a no-op `migrate deploy` each exited 0; the SQLite database had the expected table and one applied ledger entry.
|
|
83
|
+
|
|
84
|
+
The generated documentation is refreshed with `bun run docs:llms` after editing this page.
|
package/docs/migrations.md
CHANGED
|
@@ -28,6 +28,9 @@
|
|
|
28
28
|
|
|
29
29
|
Migration names are slugified (`Add Users` → `add_users`, capped at 50 chars) and prefixed with a timestamp so folders sort in apply order. `--name` is only needed when a migration is created: the CLI prompts on a TTY at that point (after pending migrations are applied in `dev`) and errors in non-interactive runs. Runs with no schema changes (client regeneration only) and pending-apply-only `dev` runs succeed without `--name` and never prompt. Older `*_migration` folders still apply in order.
|
|
30
30
|
|
|
31
|
+
`migrate dev` holds its lock from before pending reconciliation through the optional name prompt, generation, and post-generation reconciliation/application.
|
|
32
|
+
For the implementation ordering and regression coverage, see [the workflow guide](migrate-dev-workflow.md).
|
|
33
|
+
|
|
31
34
|
When migration is blocked, the CLI explains why — for example unsupported type casts (`alter_column_type_manual`), enum value changes, or destructive drops. Re-run with `--accept-data-loss` to include destructive DDL:
|
|
32
35
|
|
|
33
36
|
```bash
|
package/docs/sqlite.md
CHANGED
|
@@ -60,6 +60,8 @@ const db = createNeoOrmClient(manifest, {
|
|
|
60
60
|
|
|
61
61
|
`busyTimeout` can also be a millisecond wait (`sqlite: { busyTimeout: 10_000 }`).
|
|
62
62
|
|
|
63
|
+
SQLite runs `search`/`REGEXP` patterns in-process as JavaScript `RegExp`, so a catastrophic-backtracking pattern (e.g. `(a+)+$`) could freeze the whole process. neoorm rejects patterns that nest unbounded quantifiers; pass `sqlite: { unsafeAllowRegex: true }` to allow them anyway.
|
|
64
|
+
|
|
63
65
|
### Custom driver
|
|
64
66
|
|
|
65
67
|
Pass any object implementing `prepare(sql)`, `exec(sql)`, and `close()` — the `node:sqlite` and `bun:sqlite` APIs both match. Useful for sandboxed runtimes or existing database handles:
|