@framers/sql-storage-adapter 0.7.0 → 0.7.2

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 (33) hide show
  1. package/README.md +59 -17
  2. package/dist/adapters/betterSqliteAdapter.d.ts.map +1 -1
  3. package/dist/adapters/betterSqliteAdapter.js +5 -2
  4. package/dist/adapters/betterSqliteAdapter.js.map +1 -1
  5. package/dist/core/contracts/dialect.d.ts +103 -4
  6. package/dist/core/contracts/dialect.d.ts.map +1 -1
  7. package/dist/core/contracts/index.d.ts +1 -1
  8. package/dist/core/contracts/index.d.ts.map +1 -1
  9. package/dist/dialects/PostgresDialect.d.ts +17 -1
  10. package/dist/dialects/PostgresDialect.d.ts.map +1 -1
  11. package/dist/dialects/PostgresDialect.js +91 -6
  12. package/dist/dialects/PostgresDialect.js.map +1 -1
  13. package/dist/dialects/SqliteDialect.d.ts +21 -5
  14. package/dist/dialects/SqliteDialect.d.ts.map +1 -1
  15. package/dist/dialects/SqliteDialect.js +109 -5
  16. package/dist/dialects/SqliteDialect.js.map +1 -1
  17. package/dist/dialects/shared.d.ts +17 -0
  18. package/dist/dialects/shared.d.ts.map +1 -0
  19. package/dist/dialects/shared.js +66 -0
  20. package/dist/dialects/shared.js.map +1 -0
  21. package/dist/features/migrations/schemaMigrations.d.ts +93 -0
  22. package/dist/features/migrations/schemaMigrations.d.ts.map +1 -0
  23. package/dist/features/migrations/schemaMigrations.js +216 -0
  24. package/dist/features/migrations/schemaMigrations.js.map +1 -0
  25. package/dist/index.d.ts +1 -0
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +1 -0
  28. package/dist/index.js.map +1 -1
  29. package/dist/shared/sqlPlaceholders.d.ts +8 -0
  30. package/dist/shared/sqlPlaceholders.d.ts.map +1 -1
  31. package/dist/shared/sqlPlaceholders.js +22 -0
  32. package/dist/shared/sqlPlaceholders.js.map +1 -1
  33. package/package.json +1 -1
@@ -1,9 +1,30 @@
1
+ import { checkChoice, checkDateBucketUnit, intervalSeconds, parseJsonPath, upsertUpdateColumns } from './shared.js';
2
+ /** The time format a bound Date takes, which SQLite's date functions read and write. */
3
+ const ISO_FORMAT = '%Y-%m-%dT%H:%M:%fZ';
4
+ const BUCKET_FORMATS = {
5
+ minute: '%Y-%m-%dT%H:%M:00.000Z',
6
+ hour: '%Y-%m-%dT%H:00:00.000Z',
7
+ day: '%Y-%m-%dT00:00:00.000Z',
8
+ month: '%Y-%m-01T00:00:00.000Z',
9
+ year: '%Y-01-01T00:00:00.000Z',
10
+ };
11
+ const COLUMN_TYPES = {
12
+ text: 'TEXT',
13
+ integer: 'INTEGER',
14
+ bigint: 'INTEGER',
15
+ real: 'REAL',
16
+ boolean: 'INTEGER',
17
+ timestamp: 'TEXT',
18
+ json: 'TEXT',
19
+ uuid: 'TEXT',
20
+ blob: 'BLOB',
21
+ };
1
22
  /**
2
23
  * SQLite dialect implementation.
3
24
  *
4
- * Generates standard SQLite SQL syntax including `INSERT OR IGNORE`,
5
- * `INSERT OR REPLACE`, `json_extract()`, `ifnull()`, `PRAGMA`, and
6
- * positional `?` placeholders.
25
+ * Generates SQLite SQL: `INSERT OR IGNORE`, `ON CONFLICT ... DO UPDATE`
26
+ * upserts, `json_extract()`, `ifnull()`, `strftime()` times in ISO 8601 UTC
27
+ * text, `PRAGMA`, and positional `?` placeholders.
7
28
  */
8
29
  export class SqliteDialect {
9
30
  constructor() {
@@ -12,23 +33,106 @@ export class SqliteDialect {
12
33
  insertOrIgnore(table, columns, placeholders) {
13
34
  return `INSERT OR IGNORE INTO ${table} (${columns.join(', ')}) VALUES (${placeholders.join(', ')})`;
14
35
  }
15
- insertOrReplace(table, columns, placeholders, _primaryKey) {
16
- return `INSERT OR REPLACE INTO ${table} (${columns.join(', ')}) VALUES (${placeholders.join(', ')})`;
36
+ insertOrReplace(table, columns, placeholders, primaryKey) {
37
+ // ON CONFLICT DO UPDATE, not INSERT OR REPLACE: REPLACE deletes the
38
+ // conflicting row first, which fires ON DELETE actions on its dependents
39
+ // and resets the columns the statement does not name.
40
+ return this.upsert(table, columns, placeholders, [primaryKey ?? columns[0]]);
41
+ }
42
+ upsert(table, columns, placeholders, conflictColumns, updateColumns) {
43
+ const updates = upsertUpdateColumns(columns, conflictColumns, updateColumns);
44
+ const action = updates.length
45
+ ? `DO UPDATE SET ${updates.map((column) => `${column} = excluded.${column}`).join(', ')}`
46
+ : 'DO NOTHING';
47
+ return `INSERT INTO ${table} (${columns.join(', ')}) VALUES (${placeholders.join(', ')}) ON CONFLICT (${conflictColumns.join(', ')}) ${action}`;
17
48
  }
18
49
  jsonExtract(column, jsonPath) {
19
50
  return `json_extract(${column}, '${jsonPath}')`;
20
51
  }
52
+ jsonExtractAs(column, jsonPath, type) {
53
+ checkChoice('jsonExtractAs type', type, ['text', 'integer', 'real', 'json']);
54
+ const path = `$${parseJsonPath(jsonPath)
55
+ .map((step) => (typeof step === 'number' ? `[${step}]` : `.${step}`))
56
+ .join('')}`;
57
+ const value = `json_extract(${column}, '${path}')`;
58
+ if (type === 'json')
59
+ return value;
60
+ return `CAST(${value} AS ${type === 'text' ? 'TEXT' : type === 'integer' ? 'INTEGER' : 'REAL'})`;
61
+ }
62
+ jsonArrayContains(jsonArray, valueExpr) {
63
+ return `EXISTS (SELECT 1 FROM json_each(${jsonArray}) WHERE CAST(json_each.value AS TEXT) = CAST(${valueExpr} AS TEXT))`;
64
+ }
21
65
  ifnull(expr, fallback) {
22
66
  return `ifnull(${expr}, ${fallback})`;
23
67
  }
24
68
  autoIncrementPrimaryKey() {
25
69
  return 'INTEGER PRIMARY KEY AUTOINCREMENT';
26
70
  }
71
+ columnType(kind) {
72
+ checkChoice('columnType kind', kind, Object.keys(COLUMN_TYPES));
73
+ return COLUMN_TYPES[kind];
74
+ }
27
75
  pragma(key, value) {
28
76
  return `PRAGMA ${key} = ${value}`;
29
77
  }
30
78
  placeholder(_index) {
31
79
  return '?';
32
80
  }
81
+ now() {
82
+ return `strftime('${ISO_FORMAT}', 'now')`;
83
+ }
84
+ cast(expr, type) {
85
+ checkChoice('cast type', type, ['text', 'integer', 'real', 'timestamp']);
86
+ if (type === 'timestamp')
87
+ return `strftime('${ISO_FORMAT}', ${expr})`;
88
+ return `CAST(${expr} AS ${type === 'text' ? 'TEXT' : type === 'integer' ? 'INTEGER' : 'REAL'})`;
89
+ }
90
+ addInterval(expr, amount, unit) {
91
+ const seconds = intervalSeconds(amount, unit);
92
+ return `strftime('${ISO_FORMAT}', ${expr}, '${seconds >= 0 ? '+' : ''}${seconds} seconds')`;
93
+ }
94
+ dateBucket(unit, expr) {
95
+ return `strftime('${BUCKET_FORMATS[checkDateBucketUnit(unit)]}', ${expr})`;
96
+ }
97
+ ilike(expr, pattern) {
98
+ return `${expr} LIKE ${pattern}`;
99
+ }
100
+ inList(expr, items) {
101
+ return items.length ? `${expr} IN (${items.join(', ')})` : '(1 = 0)';
102
+ }
103
+ inArray(expr, arrayParam) {
104
+ return `${expr} IN (SELECT value FROM json_each(${arrayParam}))`;
105
+ }
106
+ rowLock(mode, wait = 'wait') {
107
+ checkChoice('rowLock mode', mode, ['update', 'share']);
108
+ checkChoice('rowLock wait', wait, ['wait', 'skipLocked', 'noWait']);
109
+ return '';
110
+ }
111
+ advisoryTransactionLock(_keyExpr) {
112
+ return null;
113
+ }
114
+ greatest(...exprs) {
115
+ return nullSkippingExtreme('max', exprs);
116
+ }
117
+ least(...exprs) {
118
+ return nullSkippingExtreme('min', exprs);
119
+ }
120
+ uuid() {
121
+ return ("lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)), 2) || '-' || " +
122
+ "substr('89ab', 1 + (random() & 3), 1) || substr(hex(randomblob(2)), 2) || '-' || hex(randomblob(6)))");
123
+ }
33
124
  }
125
+ /**
126
+ * SQLite's multi-argument max() and min() return NULL when any argument is
127
+ * NULL; PostgreSQL's GREATEST and LEAST skip NULLs. Coalescing each argument
128
+ * with the others first gives the PostgreSQL result: NULL only when all are.
129
+ */
130
+ const nullSkippingExtreme = (fn, exprs) => {
131
+ if (exprs.length === 0)
132
+ throw new Error(`${fn === 'max' ? 'greatest' : 'least'} needs at least one expression.`);
133
+ if (exprs.length === 1)
134
+ return `(${exprs[0]})`;
135
+ const coalesced = exprs.map((expr, index) => `coalesce(${[expr, ...exprs.filter((_, other) => other !== index)].join(', ')})`);
136
+ return `${fn}(${coalesced.join(', ')})`;
137
+ };
34
138
  //# sourceMappingURL=SqliteDialect.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SqliteDialect.js","sourceRoot":"","sources":["../../src/dialects/SqliteDialect.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,OAAO,aAAa;IAA1B;QACW,SAAI,GAAG,QAAiB,CAAC;IA6BpC,CAAC;IA3BC,cAAc,CAAC,KAAa,EAAE,OAAiB,EAAE,YAAsB;QACrE,OAAO,yBAAyB,KAAK,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IACtG,CAAC;IAED,eAAe,CAAC,KAAa,EAAE,OAAiB,EAAE,YAAsB,EAAE,WAAoB;QAC5F,OAAO,0BAA0B,KAAK,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IACvG,CAAC;IAED,WAAW,CAAC,MAAc,EAAE,QAAgB;QAC1C,OAAO,gBAAgB,MAAM,MAAM,QAAQ,IAAI,CAAC;IAClD,CAAC;IAED,MAAM,CAAC,IAAY,EAAE,QAAgB;QACnC,OAAO,UAAU,IAAI,KAAK,QAAQ,GAAG,CAAC;IACxC,CAAC;IAED,uBAAuB;QACrB,OAAO,mCAAmC,CAAC;IAC7C,CAAC;IAED,MAAM,CAAC,GAAW,EAAE,KAAa;QAC/B,OAAO,UAAU,GAAG,MAAM,KAAK,EAAE,CAAC;IACpC,CAAC;IAED,WAAW,CAAC,MAAc;QACxB,OAAO,GAAG,CAAC;IACb,CAAC;CACF"}
1
+ {"version":3,"file":"SqliteDialect.js","sourceRoot":"","sources":["../../src/dialects/SqliteDialect.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,eAAe,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEpH,wFAAwF;AACxF,MAAM,UAAU,GAAG,oBAAoB,CAAC;AAExC,MAAM,cAAc,GAAmC;IACrD,MAAM,EAAE,wBAAwB;IAChC,IAAI,EAAE,wBAAwB;IAC9B,GAAG,EAAE,wBAAwB;IAC7B,KAAK,EAAE,wBAAwB;IAC/B,IAAI,EAAE,wBAAwB;CAC/B,CAAC;AAEF,MAAM,YAAY,GAA+B;IAC/C,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,SAAS;IAClB,MAAM,EAAE,SAAS;IACjB,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,SAAS;IAClB,SAAS,EAAE,MAAM;IACjB,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,MAAM;CACb,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,OAAO,aAAa;IAA1B;QACW,SAAI,GAAG,QAAiB,CAAC;IAyHpC,CAAC;IAvHC,cAAc,CAAC,KAAa,EAAE,OAAiB,EAAE,YAAsB;QACrE,OAAO,yBAAyB,KAAK,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IACtG,CAAC;IAED,eAAe,CAAC,KAAa,EAAE,OAAiB,EAAE,YAAsB,EAAE,UAAmB;QAC3F,oEAAoE;QACpE,yEAAyE;QACzE,sDAAsD;QACtD,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,UAAU,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/E,CAAC;IAED,MAAM,CACJ,KAAa,EACb,OAAiB,EACjB,YAAsB,EACtB,eAAyB,EACzB,aAAwB;QAExB,MAAM,OAAO,GAAG,mBAAmB,CAAC,OAAO,EAAE,eAAe,EAAE,aAAa,CAAC,CAAC;QAC7E,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM;YAC3B,CAAC,CAAC,iBAAiB,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,MAAM,eAAe,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;YACzF,CAAC,CAAC,YAAY,CAAC;QACjB,OAAO,eAAe,KAAK,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,MAAM,EAAE,CAAC;IAClJ,CAAC;IAED,WAAW,CAAC,MAAc,EAAE,QAAgB;QAC1C,OAAO,gBAAgB,MAAM,MAAM,QAAQ,IAAI,CAAC;IAClD,CAAC;IAED,aAAa,CAAC,MAAc,EAAE,QAAgB,EAAE,IAAmB;QACjE,WAAW,CAAC,oBAAoB,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QAC7E,MAAM,IAAI,GAAG,IAAI,aAAa,CAAC,QAAQ,CAAC;aACrC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;aACpE,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;QACd,MAAM,KAAK,GAAG,gBAAgB,MAAM,MAAM,IAAI,IAAI,CAAC;QACnD,IAAI,IAAI,KAAK,MAAM;YAAE,OAAO,KAAK,CAAC;QAClC,OAAO,QAAQ,KAAK,OAAO,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC;IACnG,CAAC;IAED,iBAAiB,CAAC,SAAiB,EAAE,SAAiB;QACpD,OAAO,mCAAmC,SAAS,gDAAgD,SAAS,YAAY,CAAC;IAC3H,CAAC;IAED,MAAM,CAAC,IAAY,EAAE,QAAgB;QACnC,OAAO,UAAU,IAAI,KAAK,QAAQ,GAAG,CAAC;IACxC,CAAC;IAED,uBAAuB;QACrB,OAAO,mCAAmC,CAAC;IAC7C,CAAC;IAED,UAAU,CAAC,IAAgB;QACzB,WAAW,CAAC,iBAAiB,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,YAAY,CAAiB,CAAC,CAAC;QAChF,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAED,MAAM,CAAC,GAAW,EAAE,KAAa;QAC/B,OAAO,UAAU,GAAG,MAAM,KAAK,EAAE,CAAC;IACpC,CAAC;IAED,WAAW,CAAC,MAAc;QACxB,OAAO,GAAG,CAAC;IACb,CAAC;IAED,GAAG;QACD,OAAO,aAAa,UAAU,WAAW,CAAC;IAC5C,CAAC;IAED,IAAI,CAAC,IAAY,EAAE,IAAc;QAC/B,WAAW,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;QACzE,IAAI,IAAI,KAAK,WAAW;YAAE,OAAO,aAAa,UAAU,MAAM,IAAI,GAAG,CAAC;QACtE,OAAO,QAAQ,IAAI,OAAO,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC;IAClG,CAAC;IAED,WAAW,CAAC,IAAY,EAAE,MAAc,EAAE,IAAkB;QAC1D,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC9C,OAAO,aAAa,UAAU,MAAM,IAAI,MAAM,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,YAAY,CAAC;IAC9F,CAAC;IAED,UAAU,CAAC,IAAoB,EAAE,IAAY;QAC3C,OAAO,aAAa,cAAc,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC;IAC7E,CAAC;IAED,KAAK,CAAC,IAAY,EAAE,OAAe;QACjC,OAAO,GAAG,IAAI,SAAS,OAAO,EAAE,CAAC;IACnC,CAAC;IAED,MAAM,CAAC,IAAY,EAAE,KAAe;QAClC,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,QAAQ,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IACvE,CAAC;IAED,OAAO,CAAC,IAAY,EAAE,UAAkB;QACtC,OAAO,GAAG,IAAI,oCAAoC,UAAU,IAAI,CAAC;IACnE,CAAC;IAED,OAAO,CAAC,IAAwB,EAAE,OAAyC,MAAM;QAC/E,WAAW,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;QACvD,WAAW,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAC;QACpE,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,uBAAuB,CAAC,QAAgB;QACtC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,QAAQ,CAAC,GAAG,KAAe;QACzB,OAAO,mBAAmB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,GAAG,KAAe;QACtB,OAAO,mBAAmB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC3C,CAAC;IAED,IAAI;QACF,OAAO,CACL,2GAA2G;YAC3G,sGAAsG,CACvG,CAAC;IACJ,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,mBAAmB,GAAG,CAAC,EAAiB,EAAE,KAAe,EAAU,EAAE;IACzE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,iCAAiC,CAAC,CAAC;IACjH,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC;IAC/C,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/H,OAAO,GAAG,EAAE,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AAC1C,CAAC,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { DateBucketUnit, IntervalUnit } from '../core/contracts/dialect.js';
2
+ /** `amount` units as a number of seconds, after checking both. */
3
+ export declare const intervalSeconds: (amount: number, unit: IntervalUnit) => number;
4
+ /** Checks a dateBucket unit. */
5
+ export declare const checkDateBucketUnit: (unit: DateBucketUnit) => DateBucketUnit;
6
+ /** One step of a JSON path: an object key or an array index. */
7
+ export type JsonPathStep = string | number;
8
+ /**
9
+ * Splits a JSON path of the form `$.key`, `$.key.key` or `$.key[0]` into its
10
+ * steps. Anything else is refused, so a path never carries SQL.
11
+ */
12
+ export declare const parseJsonPath: (jsonPath: string) => JsonPathStep[];
13
+ /** Checks one of a fixed set of option values. */
14
+ export declare const checkChoice: <T extends string>(what: string, value: T, allowed: readonly T[]) => T;
15
+ /** The columns an upsert updates: those given, or every column outside the conflict target. */
16
+ export declare const upsertUpdateColumns: (columns: string[], conflictColumns: string[], updateColumns?: string[]) => string[];
17
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/dialects/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAUjF,kEAAkE;AAClE,eAAO,MAAM,eAAe,WAAY,MAAM,QAAQ,YAAY,KAAG,MASpE,CAAC;AAIF,gCAAgC;AAChC,eAAO,MAAM,mBAAmB,SAAU,cAAc,KAAG,cAK1D,CAAC;AAEF,gEAAgE;AAChE,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,CAAC;AAI3C;;;GAGG;AACH,eAAO,MAAM,aAAa,aAAc,MAAM,KAAG,YAAY,EAgB5D,CAAC;AAEF,kDAAkD;AAClD,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,MAAM,QAAQ,MAAM,SAAS,CAAC,WAAW,SAAS,CAAC,EAAE,KAAG,CAK7F,CAAC;AAEF,+FAA+F;AAC/F,eAAO,MAAM,mBAAmB,YAAa,MAAM,EAAE,mBAAmB,MAAM,EAAE,kBAAkB,MAAM,EAAE,KAAG,MAAM,EAKlH,CAAC"}
@@ -0,0 +1,66 @@
1
+ /** Seconds per addInterval unit. */
2
+ const SECONDS_PER_UNIT = new Map([
3
+ ['seconds', 1],
4
+ ['minutes', 60],
5
+ ['hours', 3600],
6
+ ['days', 86400],
7
+ ]);
8
+ /** `amount` units as a number of seconds, after checking both. */
9
+ export const intervalSeconds = (amount, unit) => {
10
+ if (typeof amount !== 'number' || !Number.isFinite(amount)) {
11
+ throw new Error(`addInterval amount must be a finite number; got ${String(amount)}.`);
12
+ }
13
+ const perUnit = SECONDS_PER_UNIT.get(unit);
14
+ if (perUnit === undefined) {
15
+ throw new Error(`addInterval unit must be 'seconds', 'minutes', 'hours' or 'days'; got ${JSON.stringify(unit)}.`);
16
+ }
17
+ return amount * perUnit;
18
+ };
19
+ const DATE_BUCKET_UNITS = new Set(['minute', 'hour', 'day', 'month', 'year']);
20
+ /** Checks a dateBucket unit. */
21
+ export const checkDateBucketUnit = (unit) => {
22
+ if (!DATE_BUCKET_UNITS.has(unit)) {
23
+ throw new Error(`dateBucket unit must be 'minute', 'hour', 'day', 'month' or 'year'; got ${JSON.stringify(unit)}.`);
24
+ }
25
+ return unit;
26
+ };
27
+ const PATH_STEP = /\.([A-Za-z_][A-Za-z0-9_]*)|\[(\d+)\]/y;
28
+ /**
29
+ * Splits a JSON path of the form `$.key`, `$.key.key` or `$.key[0]` into its
30
+ * steps. Anything else is refused, so a path never carries SQL.
31
+ */
32
+ export const parseJsonPath = (jsonPath) => {
33
+ const refuse = () => {
34
+ throw new Error(`Unsupported JSON path ${JSON.stringify(jsonPath)}: write $.key, $.key.key or $.key[0].`);
35
+ };
36
+ if (typeof jsonPath !== 'string' || !jsonPath.startsWith('$'))
37
+ refuse();
38
+ const steps = [];
39
+ let index = 1;
40
+ while (index < jsonPath.length) {
41
+ PATH_STEP.lastIndex = index;
42
+ const match = PATH_STEP.exec(jsonPath);
43
+ if (!match)
44
+ refuse();
45
+ steps.push(match[1] !== undefined ? match[1] : Number(match[2]));
46
+ index = PATH_STEP.lastIndex;
47
+ }
48
+ if (steps.length === 0)
49
+ refuse();
50
+ return steps;
51
+ };
52
+ /** Checks one of a fixed set of option values. */
53
+ export const checkChoice = (what, value, allowed) => {
54
+ if (!allowed.includes(value)) {
55
+ throw new Error(`${what} must be ${allowed.map((option) => `'${option}'`).join(', ')}; got ${JSON.stringify(value)}.`);
56
+ }
57
+ return value;
58
+ };
59
+ /** The columns an upsert updates: those given, or every column outside the conflict target. */
60
+ export const upsertUpdateColumns = (columns, conflictColumns, updateColumns) => {
61
+ if (conflictColumns.length === 0) {
62
+ throw new Error('upsert needs at least one conflict column.');
63
+ }
64
+ return updateColumns ?? columns.filter((column) => !conflictColumns.includes(column));
65
+ };
66
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/dialects/shared.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * A versioned schema-migration runner over StorageAdapter, for PostgreSQL and
3
+ * SQLite alike. It applies hand-written SQL files in number order, each in its
4
+ * own transaction, records each in a ledger table with the SHA-256 of its text,
5
+ * refuses an applied file whose text changed, and stops at the first error.
6
+ *
7
+ * Locking: on PostgreSQL each file's transaction first takes a
8
+ * transaction-scoped advisory lock, and checks the ledger again under it, so
9
+ * two runners at once apply each file once. A file marked to run outside a
10
+ * transaction (CREATE INDEX CONCURRENTLY) runs under the session-level lock on
11
+ * one held connection. On SQLite the file's write transaction is the lock.
12
+ *
13
+ * @module
14
+ */
15
+ import type { StorageAdapter } from '../../core/contracts/index.js';
16
+ /** One migration: its file name, which orders it, and its SQL. */
17
+ export interface SchemaMigration {
18
+ name: string;
19
+ sql: string;
20
+ }
21
+ /** How the runner names its ledger and lock, and what it does with surprises. */
22
+ export interface SchemaMigrationOptions {
23
+ /** The ledger table, schema-qualified if need be. Default: `schema_migrations`. */
24
+ table?: string;
25
+ /** The ledger column that holds each file's SHA-256 in hex. Default: `checksum`. */
26
+ checksumColumn?: string;
27
+ /**
28
+ * The PostgreSQL advisory lock key: an integer, or text hashed with
29
+ * `hashtext()`. Default: 724313571. Runners that share a database share a key.
30
+ */
31
+ lockKey?: number | string;
32
+ /**
33
+ * A ledger row whose file the list lacks (the database is ahead of the
34
+ * code): `'refuse'` (the default) stops before applying anything; `'ignore'`
35
+ * goes on.
36
+ */
37
+ unknown?: 'refuse' | 'ignore';
38
+ /**
39
+ * A file whose first line, trimmed, is this runs outside a transaction.
40
+ * Default: `-- outside transaction`.
41
+ */
42
+ outsideTransactionMarker?: string;
43
+ }
44
+ /** What a run or a status check found. */
45
+ export interface SchemaMigrationReport {
46
+ /** Applied by this run, in order (a status check applies none, and lists the files the ledger holds unchanged). */
47
+ applied: string[];
48
+ /** Already in the ledger with the same checksum: a run skipped them. */
49
+ skipped: string[];
50
+ /** In the list and not in the ledger: a status check's pending files. */
51
+ pending: string[];
52
+ /** In the ledger with a different checksum. */
53
+ changed: string[];
54
+ /** In the ledger and not in the list. */
55
+ unknown: string[];
56
+ }
57
+ /** Options for reading a migrations folder. */
58
+ export interface ReadSchemaMigrationsOptions {
59
+ /** Reads `<dir>/<dialect>` when that folder exists, else `<dir>`. */
60
+ dialect?: 'postgres' | 'sqlite';
61
+ /**
62
+ * The file-name form, whose first capture group is the number that orders
63
+ * the file. Default: digits, an underscore, then lower-case letters, digits
64
+ * and underscores, `.sql` (`001_init.sql`, `0001_accounts.sql`).
65
+ */
66
+ namePattern?: RegExp;
67
+ }
68
+ /**
69
+ * Reads the migrations of a folder in number order: `<dir>/<dialect>` when
70
+ * that folder exists, else `<dir>`. Files not ending in `.sql` are left out; a
71
+ * `.sql` name out of form, or a number used twice, is refused. Node only.
72
+ */
73
+ export declare function readSchemaMigrations(dir: string, options?: ReadSchemaMigrationsOptions): Promise<SchemaMigration[]>;
74
+ /**
75
+ * Compares the list with the ledger and applies nothing: `applied` lists the
76
+ * files the ledger holds unchanged, `pending` the files it lacks, `changed`
77
+ * the files whose text differs from what was applied, `unknown` the ledger
78
+ * rows the list lacks. Creates the ledger when it is missing.
79
+ */
80
+ export declare function schemaMigrationStatus(adapter: StorageAdapter, migrations: SchemaMigration[], options?: SchemaMigrationOptions): Promise<SchemaMigrationReport>;
81
+ /**
82
+ * Applies every migration the ledger lacks, in list order, and reports what
83
+ * it did: `applied` (this run) and `skipped` (already applied). Refuses to
84
+ * start when an applied file's text changed, or (unless `unknown: 'ignore'`)
85
+ * when the ledger holds a file the list lacks. A file runs in its own
86
+ * transaction and holds no BEGIN or COMMIT; a file whose first line is the
87
+ * outside-transaction marker runs outside one (on PostgreSQL that needs
88
+ * `withConnection`, which PostgresAdapter has). Stops at the first error,
89
+ * after rolling that file back, with an error that names it and carries the
90
+ * database's error as `cause`.
91
+ */
92
+ export declare function runSchemaMigrations(adapter: StorageAdapter, migrations: SchemaMigration[], options?: SchemaMigrationOptions): Promise<SchemaMigrationReport>;
93
+ //# sourceMappingURL=schemaMigrations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemaMigrations.d.ts","sourceRoot":"","sources":["../../../src/features/migrations/schemaMigrations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAI3D,kEAAkE;AAClE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,iFAAiF;AACjF,MAAM,WAAW,sBAAsB;IACrC,mFAAmF;IACnF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oFAAoF;IACpF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B;;;;OAIG;IACH,OAAO,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC9B;;;OAGG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED,0CAA0C;AAC1C,MAAM,WAAW,qBAAqB;IACpC,mHAAmH;IACnH,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,wEAAwE;IACxE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,yEAAyE;IACzE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,+CAA+C;IAC/C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,yCAAyC;IACzC,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,+CAA+C;AAC/C,MAAM,WAAW,2BAA2B;IAC1C,qEAAqE;IACrE,OAAO,CAAC,EAAE,UAAU,GAAG,QAAQ,CAAC;IAChC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AA2BD;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,2BAAgC,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CA4B7H;AA8ED;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,eAAe,EAAE,EAC7B,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,qBAAqB,CAAC,CAIhC;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,eAAe,EAAE,EAC7B,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,qBAAqB,CAAC,CA6BhC"}
@@ -0,0 +1,216 @@
1
+ import { createStorageFeatures } from '../../core/contracts/features.js';
2
+ import { getStorageLogger } from '../../shared/logger.js';
3
+ const DEFAULT_TABLE = 'schema_migrations';
4
+ const DEFAULT_CHECKSUM_COLUMN = 'checksum';
5
+ const DEFAULT_LOCK_KEY = 724313571;
6
+ const DEFAULT_MARKER = '-- outside transaction';
7
+ const DEFAULT_NAME = /^(\d+)_[a-z0-9_]+\.sql$/;
8
+ const IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)?$/;
9
+ /** The SHA-256 of `text` (its UTF-8 bytes) in hex, through Web Crypto, else node:crypto. */
10
+ const sha256Hex = async (text) => {
11
+ const subtle = globalThis.crypto?.subtle;
12
+ if (subtle) {
13
+ const digest = await subtle.digest('SHA-256', new TextEncoder().encode(text));
14
+ return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, '0')).join('');
15
+ }
16
+ const { createHash } = await import('crypto');
17
+ return createHash('sha256').update(text).digest('hex');
18
+ };
19
+ const checkIdentifier = (what, value) => {
20
+ if (typeof value !== 'string' || !IDENTIFIER.test(value)) {
21
+ throw new Error(`${what} must be an SQL identifier (letters, digits and underscores, optionally schema-qualified); got ${JSON.stringify(value)}.`);
22
+ }
23
+ return value;
24
+ };
25
+ /**
26
+ * Reads the migrations of a folder in number order: `<dir>/<dialect>` when
27
+ * that folder exists, else `<dir>`. Files not ending in `.sql` are left out; a
28
+ * `.sql` name out of form, or a number used twice, is refused. Node only.
29
+ */
30
+ export async function readSchemaMigrations(dir, options = {}) {
31
+ const { readdir, readFile, stat } = await import('fs/promises');
32
+ const path = await import('path');
33
+ const pattern = options.namePattern ?? DEFAULT_NAME;
34
+ let folder = dir;
35
+ if (options.dialect) {
36
+ const own = path.join(dir, options.dialect);
37
+ const isFolder = await stat(own).then((entry) => entry.isDirectory(), () => false);
38
+ if (isFolder)
39
+ folder = own;
40
+ }
41
+ const numbers = new Map();
42
+ const entries = [];
43
+ for (const name of await readdir(folder)) {
44
+ if (!name.endsWith('.sql'))
45
+ continue;
46
+ const number = pattern.exec(name)?.[1];
47
+ if (number === undefined) {
48
+ throw new Error(`${name}: a migration's name is digits, an underscore and a lower-case name, ending in .sql.`);
49
+ }
50
+ const key = String(Number(number));
51
+ if (numbers.has(key))
52
+ throw new Error(`${name}: the number ${number} is used twice (also ${numbers.get(key)}).`);
53
+ numbers.set(key, name);
54
+ entries.push({ name, number: Number(number) });
55
+ }
56
+ entries.sort((a, b) => a.number - b.number || a.name.localeCompare(b.name));
57
+ return Promise.all(entries.map(async ({ name }) => ({ name, sql: await readFile(path.join(folder, name), 'utf8') })));
58
+ }
59
+ const resolve = (adapter, options) => {
60
+ const table = checkIdentifier('table', options.table ?? DEFAULT_TABLE);
61
+ const checksumColumn = checkIdentifier('checksumColumn', options.checksumColumn ?? DEFAULT_CHECKSUM_COLUMN);
62
+ const key = options.lockKey ?? DEFAULT_LOCK_KEY;
63
+ if (typeof key === 'number' && !Number.isSafeInteger(key))
64
+ throw new Error(`lockKey must be a safe integer or text; got ${key}.`);
65
+ const dialect = createStorageFeatures(adapter).dialect.name;
66
+ const keyExpr = typeof key === 'number' ? '?' : 'hashtext(?)';
67
+ return {
68
+ table,
69
+ checksumColumn,
70
+ lock: dialect === 'postgres'
71
+ ? {
72
+ sql: `SELECT pg_advisory_xact_lock(${keyExpr})`,
73
+ session: `SELECT pg_advisory_lock(${keyExpr})`,
74
+ unlock: `SELECT pg_advisory_unlock(${keyExpr})`,
75
+ params: [key],
76
+ }
77
+ : null,
78
+ unknown: options.unknown ?? 'refuse',
79
+ marker: options.outsideTransactionMarker ?? DEFAULT_MARKER,
80
+ dialect,
81
+ };
82
+ };
83
+ const ledgerDdl = (r) => r.dialect === 'postgres'
84
+ ? `CREATE TABLE IF NOT EXISTS ${r.table} (name TEXT PRIMARY KEY, ${r.checksumColumn} TEXT NOT NULL, applied_at TIMESTAMPTZ NOT NULL DEFAULT now())`
85
+ : `CREATE TABLE IF NOT EXISTS ${r.table} (name TEXT PRIMARY KEY, ${r.checksumColumn} TEXT NOT NULL, applied_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')))`;
86
+ /** Runs `work` in a transaction that first takes the PostgreSQL lock. */
87
+ const inLockedTransaction = (adapter, r, work) => adapter.transaction(async (trx) => {
88
+ if (r.lock)
89
+ await trx.get(r.lock.sql, r.lock.params);
90
+ return work(trx);
91
+ });
92
+ const ledgerRows = async (db, r) => {
93
+ const rows = await db.all(`SELECT name, ${r.checksumColumn} AS checksum FROM ${r.table}`);
94
+ return new Map(rows.map((row) => [row.name, row.checksum]));
95
+ };
96
+ const compare = (list, ledger) => {
97
+ const report = { applied: [], pending: [], changed: [], unknown: [] };
98
+ const carried = new Set(list.map((migration) => migration.name));
99
+ for (const migration of list) {
100
+ const recorded = ledger.get(migration.name);
101
+ if (recorded === undefined)
102
+ report.pending.push(migration.name);
103
+ else if (recorded !== migration.checksum)
104
+ report.changed.push(migration.name);
105
+ else
106
+ report.applied.push(migration.name);
107
+ }
108
+ report.unknown = [...ledger.keys()].filter((name) => !carried.has(name)).sort();
109
+ return report;
110
+ };
111
+ const withChecksums = (migrations) => Promise.all(migrations.map(async (migration) => ({ ...migration, checksum: await sha256Hex(migration.sql) })));
112
+ /** Creates the ledger if it is missing, under the lock. */
113
+ const ensureLedger = (adapter, r) => inLockedTransaction(adapter, r, async (trx) => {
114
+ await trx.exec(ledgerDdl(r));
115
+ });
116
+ /**
117
+ * Compares the list with the ledger and applies nothing: `applied` lists the
118
+ * files the ledger holds unchanged, `pending` the files it lacks, `changed`
119
+ * the files whose text differs from what was applied, `unknown` the ledger
120
+ * rows the list lacks. Creates the ledger when it is missing.
121
+ */
122
+ export async function schemaMigrationStatus(adapter, migrations, options = {}) {
123
+ const r = resolve(adapter, options);
124
+ await ensureLedger(adapter, r);
125
+ return { ...compare(await withChecksums(migrations), await ledgerRows(adapter, r)), skipped: [] };
126
+ }
127
+ /**
128
+ * Applies every migration the ledger lacks, in list order, and reports what
129
+ * it did: `applied` (this run) and `skipped` (already applied). Refuses to
130
+ * start when an applied file's text changed, or (unless `unknown: 'ignore'`)
131
+ * when the ledger holds a file the list lacks. A file runs in its own
132
+ * transaction and holds no BEGIN or COMMIT; a file whose first line is the
133
+ * outside-transaction marker runs outside one (on PostgreSQL that needs
134
+ * `withConnection`, which PostgresAdapter has). Stops at the first error,
135
+ * after rolling that file back, with an error that names it and carries the
136
+ * database's error as `cause`.
137
+ */
138
+ export async function runSchemaMigrations(adapter, migrations, options = {}) {
139
+ const r = resolve(adapter, options);
140
+ const list = await withChecksums(migrations);
141
+ await ensureLedger(adapter, r);
142
+ const before = compare(list, await ledgerRows(adapter, r));
143
+ if (before.changed.length) {
144
+ throw new Error(`Applied migrations differ from the files given: ${before.changed.join(', ')}. An applied migration never changes.`);
145
+ }
146
+ if (before.unknown.length && r.unknown === 'refuse') {
147
+ throw new Error(`The database has applied migrations the list lacks: ${before.unknown.join(', ')}. Run the newer code, or pass unknown: 'ignore'.`);
148
+ }
149
+ const report = { applied: [], skipped: [...before.applied], pending: [], changed: [], unknown: before.unknown };
150
+ for (const migration of list) {
151
+ if (!before.pending.includes(migration.name))
152
+ continue;
153
+ let applied;
154
+ try {
155
+ applied = outsideTransaction(migration, r) ? await applyOutside(adapter, r, migration) : await applyInside(adapter, r, migration);
156
+ }
157
+ catch (error) {
158
+ const message = error instanceof Error ? error.message : String(error);
159
+ const failure = new Error(`Migration ${migration.name} failed: ${message}`);
160
+ failure.cause = error;
161
+ throw failure;
162
+ }
163
+ (applied ? report.applied : report.skipped).push(migration.name);
164
+ if (applied)
165
+ getStorageLogger().info({ migration: migration.name, table: r.table }, `Applied migration ${migration.name}.`);
166
+ }
167
+ return report;
168
+ }
169
+ const outsideTransaction = (migration, r) => migration.sql.split('\n', 1)[0]?.trim() === r.marker;
170
+ /** The checksum the ledger holds for `name` now, read through `db`. */
171
+ const recorded = async (db, r, name) => (await db.get(`SELECT ${r.checksumColumn} AS checksum FROM ${r.table} WHERE name = ?`, [name]))?.checksum;
172
+ /** Throws when another runner applied a different text under this name. True when the file is applied. */
173
+ const appliedAlready = (migration, checksum) => {
174
+ if (checksum === undefined)
175
+ return false;
176
+ if (checksum !== migration.checksum) {
177
+ throw new Error(`it was applied with checksum ${checksum} and now reads ${migration.checksum}`);
178
+ }
179
+ return true;
180
+ };
181
+ const record = (db, r, migration) => db.run(`INSERT INTO ${r.table} (name, ${r.checksumColumn}) VALUES (?, ?)`, [migration.name, migration.checksum]);
182
+ /** Applies one file in its own locked transaction, checking the ledger again under the lock. */
183
+ const applyInside = (adapter, r, migration) => inLockedTransaction(adapter, r, async (trx) => {
184
+ if (appliedAlready(migration, await recorded(trx, r, migration.name)))
185
+ return false;
186
+ await trx.exec(migration.sql);
187
+ await record(trx, r, migration);
188
+ return true;
189
+ });
190
+ /** Applies one marked file outside a transaction: on PostgreSQL under the session-level lock on one held connection. */
191
+ const applyOutside = async (adapter, r, migration) => {
192
+ const work = async (db) => {
193
+ if (appliedAlready(migration, await recorded(db, r, migration.name)))
194
+ return false;
195
+ await db.exec(migration.sql);
196
+ await record(db, r, migration);
197
+ return true;
198
+ };
199
+ if (!r.lock)
200
+ return work(adapter);
201
+ const held = adapter;
202
+ if (typeof held.withConnection !== 'function') {
203
+ throw new Error('a file outside a transaction needs an adapter with withConnection() on PostgreSQL, to hold the lock on one session');
204
+ }
205
+ const { params, session, unlock } = r.lock;
206
+ return held.withConnection(async (connection) => {
207
+ await connection.get(session, params);
208
+ try {
209
+ return await work(connection);
210
+ }
211
+ finally {
212
+ await connection.get(unlock, params);
213
+ }
214
+ });
215
+ };
216
+ //# sourceMappingURL=schemaMigrations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemaMigrations.js","sourceRoot":"","sources":["../../../src/features/migrations/schemaMigrations.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -12,6 +12,7 @@ export * from './features/backup/cloudBackup.js';
12
12
  export * from './features/migrations/dataExport.js';
13
13
  export * from './features/migrations/dataImport.js';
14
14
  export * from './features/migrations/migration.js';
15
+ export * from './features/migrations/schemaMigrations.js';
15
16
  export * from './features/sync/syncManager.js';
16
17
  export * from './shared/parameterUtils.js';
17
18
  export { segmentFilePath, segmentName, validateSegment } from './shared/segment.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;AAGxB,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mCAAmC,CAAC;AAClD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,+BAA+B,CAAC;AAQ9C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,6BAA6B,CAAC;AAM5C,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAMjF,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;AAGxB,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mCAAmC,CAAC;AAClD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,+BAA+B,CAAC;AAQ9C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,wCAAwC,CAAC;AACvD,cAAc,6BAA6B,CAAC;AAM5C,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAMjF,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -20,6 +20,7 @@ export * from './features/backup/cloudBackup.js';
20
20
  export * from './features/migrations/dataExport.js';
21
21
  export * from './features/migrations/dataImport.js';
22
22
  export * from './features/migrations/migration.js';
23
+ export * from './features/migrations/schemaMigrations.js';
23
24
  export * from './features/sync/syncManager.js';
24
25
  // Note: Cross-platform sync is available via separate entry point for cleaner imports.
25
26
  // Usage: import { createCrossPlatformSync } from '@framers/sql-storage-adapter/sync';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,cAAc,SAAS,CAAC;AAExB,8EAA8E;AAC9E,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAEhC,+EAA+E;AAC/E,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mCAAmC,CAAC;AAClD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,+BAA+B,CAAC;AAE9C,wEAAwE;AACxE,yDAAyD;AACzD,2HAA2H;AAC3H,mEAAmE;AAEnE,+EAA+E;AAC/E,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,6BAA6B,CAAC;AAE5C,uFAAuF;AACvF,sFAAsF;AAEtF,+EAA+E;AAC/E,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEjF,6FAA6F;AAC7F,uGAAuG;AAEvG,iFAAiF;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAsB,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,cAAc,SAAS,CAAC;AAExB,8EAA8E;AAC9E,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAEhC,+EAA+E;AAC/E,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mCAAmC,CAAC;AAClD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,+BAA+B,CAAC;AAE9C,wEAAwE;AACxE,yDAAyD;AACzD,2HAA2H;AAC3H,mEAAmE;AAEnE,+EAA+E;AAC/E,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,wCAAwC,CAAC;AACvD,cAAc,6BAA6B,CAAC;AAE5C,uFAAuF;AACvF,sFAAsF;AAEtF,+EAA+E;AAC/E,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEjF,6FAA6F;AAC7F,uGAAuG;AAEvG,iFAAiF;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAsB,MAAM,iBAAiB,CAAC"}
@@ -37,4 +37,12 @@ export declare const toSqliteValue: (value: unknown) => unknown;
37
37
  * toSqliteValue, positional and named alike.
38
38
  */
39
39
  export declare const toSqliteStatement: (statement: string, parameters?: StorageParameters) => SqliteStatement;
40
+ /**
41
+ * better-sqlite3 binds every JavaScript number as a REAL (sqlite3_bind_double),
42
+ * so 7 reaches SQL as 7.0: `CAST(? AS TEXT)` gives '7.0', a TEXT column
43
+ * stores '7.0', and a comparison with a JSON element misses. It binds a
44
+ * BigInt as an INTEGER, so this hands it each safe integer as one. sql.js
45
+ * binds integers in the 32-bit range as INTEGER already.
46
+ */
47
+ export declare const integersAsBigInt: (parameters?: StorageParameters) => StorageParameters;
40
48
  //# sourceMappingURL=sqlPlaceholders.d.ts.map