@spinajs/session-provider-db 2.0.489 → 2.0.490
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/lib/cjs/index.d.ts +2 -0
- package/lib/cjs/index.d.ts.map +1 -1
- package/lib/cjs/index.js +33 -1
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/migration-support.d.ts +43 -0
- package/lib/cjs/migration-support.d.ts.map +1 -0
- package/lib/cjs/migration-support.js +97 -0
- package/lib/cjs/migration-support.js.map +1 -0
- package/lib/cjs/migrations/UserSessionDBSqlMigration_2022_06_28_01_20_00.d.ts.map +1 -1
- package/lib/cjs/migrations/UserSessionDBSqlMigration_2022_06_28_01_20_00.js +8 -0
- package/lib/cjs/migrations/UserSessionDBSqlMigration_2022_06_28_01_20_00.js.map +1 -1
- package/lib/cjs/migrations/UserSessionDataJson_2026_07_31_00_00_00.d.ts +101 -0
- package/lib/cjs/migrations/UserSessionDataJson_2026_07_31_00_00_00.d.ts.map +1 -0
- package/lib/cjs/migrations/UserSessionDataJson_2026_07_31_00_00_00.js +144 -0
- package/lib/cjs/migrations/UserSessionDataJson_2026_07_31_00_00_00.js.map +1 -0
- package/lib/cjs/migrations/UserSessionTimestamps_2026_07_31_00_00_01.d.ts +68 -0
- package/lib/cjs/migrations/UserSessionTimestamps_2026_07_31_00_00_01.d.ts.map +1 -0
- package/lib/cjs/migrations/UserSessionTimestamps_2026_07_31_00_00_01.js +95 -0
- package/lib/cjs/migrations/UserSessionTimestamps_2026_07_31_00_00_01.js.map +1 -0
- package/lib/cjs/models/DbSession.d.ts +15 -0
- package/lib/cjs/models/DbSession.d.ts.map +1 -1
- package/lib/cjs/models/DbSession.js.map +1 -1
- package/lib/mjs/index.d.ts +2 -0
- package/lib/mjs/index.d.ts.map +1 -1
- package/lib/mjs/index.js +33 -1
- package/lib/mjs/index.js.map +1 -1
- package/lib/mjs/migration-support.d.ts +43 -0
- package/lib/mjs/migration-support.d.ts.map +1 -0
- package/lib/mjs/migration-support.js +91 -0
- package/lib/mjs/migration-support.js.map +1 -0
- package/lib/mjs/migrations/UserSessionDBSqlMigration_2022_06_28_01_20_00.d.ts.map +1 -1
- package/lib/mjs/migrations/UserSessionDBSqlMigration_2022_06_28_01_20_00.js +8 -0
- package/lib/mjs/migrations/UserSessionDBSqlMigration_2022_06_28_01_20_00.js.map +1 -1
- package/lib/mjs/migrations/UserSessionDataJson_2026_07_31_00_00_00.d.ts +101 -0
- package/lib/mjs/migrations/UserSessionDataJson_2026_07_31_00_00_00.d.ts.map +1 -0
- package/lib/mjs/migrations/UserSessionDataJson_2026_07_31_00_00_00.js +141 -0
- package/lib/mjs/migrations/UserSessionDataJson_2026_07_31_00_00_00.js.map +1 -0
- package/lib/mjs/migrations/UserSessionTimestamps_2026_07_31_00_00_01.d.ts +68 -0
- package/lib/mjs/migrations/UserSessionTimestamps_2026_07_31_00_00_01.d.ts.map +1 -0
- package/lib/mjs/migrations/UserSessionTimestamps_2026_07_31_00_00_01.js +92 -0
- package/lib/mjs/migrations/UserSessionTimestamps_2026_07_31_00_00_01.js.map +1 -0
- package/lib/mjs/models/DbSession.d.ts +15 -0
- package/lib/mjs/models/DbSession.d.ts.map +1 -1
- package/lib/mjs/models/DbSession.js.map +1 -1
- package/lib/tsconfig.cjs.tsbuildinfo +1 -1
- package/lib/tsconfig.mjs.tsbuildinfo +1 -1
- package/package.json +10 -10
package/lib/cjs/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { SessionProvider, ISession } from '@spinajs/rbac';
|
|
|
2
2
|
import { Log } from '@spinajs/log';
|
|
3
3
|
export * from './models/DbSession.js';
|
|
4
4
|
export * from './migrations/UserSessionDBSqlMigration_2022_06_28_01_20_00.js';
|
|
5
|
+
export * from './migrations/UserSessionDataJson_2026_07_31_00_00_00.js';
|
|
6
|
+
export * from './migrations/UserSessionTimestamps_2026_07_31_00_00_01.js';
|
|
5
7
|
/**
|
|
6
8
|
* Relational-db backed session store. Conforms to the `@spinajs/rbac`
|
|
7
9
|
* `SessionProvider` contract: ownership is the numeric `UserId`, expiration is
|
package/lib/cjs/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAqD,MAAM,eAAe,CAAC;AAE7G,OAAO,EAAU,GAAG,EAAE,MAAM,cAAc,CAAC;AAM3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,+DAA+D,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAqD,MAAM,eAAe,CAAC;AAE7G,OAAO,EAAU,GAAG,EAAE,MAAM,cAAc,CAAC;AAM3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,+DAA+D,CAAC;AAC9E,cAAc,yDAAyD,CAAC;AACxE,cAAc,2DAA2D,CAAC;AAE1E;;;;;GAKG;AACH,qBACa,cAAe,SAAQ,eAAe;IAEjD,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;IAEnB;;;;OAIG;IAIH,SAAS,CAAC,eAAe,EAAE,MAAM,CAAC;IAErB,OAAO;IAWpB;;;;;;OAMG;IACU,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAgBjC,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAmBpD,IAAI,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBtC,KAAK,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IAiB1C,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxC,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAc3C,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAM/C,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAItC,OAAO,CAAC,SAAS;IAUjB,OAAO,CAAC,gBAAgB;CAMzB"}
|
package/lib/cjs/index.js
CHANGED
|
@@ -33,6 +33,8 @@ const configuration_1 = require("@spinajs/configuration");
|
|
|
33
33
|
const luxon_1 = require("luxon");
|
|
34
34
|
__exportStar(require("./models/DbSession.js"), exports);
|
|
35
35
|
__exportStar(require("./migrations/UserSessionDBSqlMigration_2022_06_28_01_20_00.js"), exports);
|
|
36
|
+
__exportStar(require("./migrations/UserSessionDataJson_2026_07_31_00_00_00.js"), exports);
|
|
37
|
+
__exportStar(require("./migrations/UserSessionTimestamps_2026_07_31_00_00_01.js"), exports);
|
|
36
38
|
/**
|
|
37
39
|
* Relational-db backed session store. Conforms to the `@spinajs/rbac`
|
|
38
40
|
* `SessionProvider` contract: ownership is the numeric `UserId`, expiration is
|
|
@@ -138,7 +140,7 @@ let DbSessionStore = class DbSessionStore extends rbac_1.SessionProvider {
|
|
|
138
140
|
UserId: row.UserId,
|
|
139
141
|
Creation: row.CreatedAt,
|
|
140
142
|
Expiration: row.Expiration ?? undefined,
|
|
141
|
-
Data: (0, rbac_1.decodeSessionData)(row.Data),
|
|
143
|
+
Data: (0, rbac_1.decodeSessionData)(sessionDataFromColumn(row.Data)),
|
|
142
144
|
});
|
|
143
145
|
}
|
|
144
146
|
expirationEquals(a, b) {
|
|
@@ -162,4 +164,34 @@ __decorate([
|
|
|
162
164
|
exports.DbSessionStore = DbSessionStore = __decorate([
|
|
163
165
|
(0, di_1.Injectable)(rbac_1.SessionProvider)
|
|
164
166
|
], DbSessionStore);
|
|
167
|
+
/**
|
|
168
|
+
* Presents whatever the driver hands back for `user_sessions.Data` in one of the
|
|
169
|
+
* two shapes {@link decodeSessionData} understands - a JSON string, or an
|
|
170
|
+
* already-parsed object graph.
|
|
171
|
+
*
|
|
172
|
+
* `Data` is a MySQL `json` column, so mysql2 parses it for us and this function
|
|
173
|
+
* passes the OBJECT straight through: no `JSON.stringify` / `JSON.parse`
|
|
174
|
+
* round-trip, the decoder walks the graph natively. Strings pass through just as
|
|
175
|
+
* untouched - sqlite has no json type and returns the stored text, and a
|
|
176
|
+
* connection configured otherwise (`typeCast`, an older column, a text-typed
|
|
177
|
+
* table that has not run the converging migration yet) may still yield one.
|
|
178
|
+
*
|
|
179
|
+
* The only value actually converted is a `Buffer`: a blob-ish column or a driver
|
|
180
|
+
* in binary mode delivers one, and neither the object walk nor `JSON.parse` can
|
|
181
|
+
* read it. Note it is decoded to text rather than handed over as an object -
|
|
182
|
+
* treating a Buffer as an object graph would walk its byte indices and decode to
|
|
183
|
+
* an empty session.
|
|
184
|
+
*
|
|
185
|
+
* The write path is untouched: `encodeSessionData` still stores a string, which
|
|
186
|
+
* a `json` column accepts verbatim (mysql2 does NOT auto-serialize objects into
|
|
187
|
+
* json columns, and `DbSession.Data` deliberately carries no `@Json()`
|
|
188
|
+
* decorator - the ORM's JsonValueConverter would `JSON.stringify` the already
|
|
189
|
+
* encoded string and double-encode every session).
|
|
190
|
+
*/
|
|
191
|
+
function sessionDataFromColumn(data) {
|
|
192
|
+
if (Buffer.isBuffer(data)) {
|
|
193
|
+
return data.toString('utf8');
|
|
194
|
+
}
|
|
195
|
+
return data;
|
|
196
|
+
}
|
|
165
197
|
//# sourceMappingURL=index.js.map
|
package/lib/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wCAA6G;AAC7G,oCAAyC;AACzC,sCAA2C;AAC3C,wDAAkD;AAClD,sCAA+C;AAC/C,0DAAgD;AAChD,iCAAiC;AAEjC,wDAAsC;AACtC,gGAA8E;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wCAA6G;AAC7G,oCAAyC;AACzC,sCAA2C;AAC3C,wDAAkD;AAClD,sCAA+C;AAC/C,0DAAgD;AAChD,iCAAiC;AAEjC,wDAAsC;AACtC,gGAA8E;AAC9E,0FAAwE;AACxE,4FAA0E;AAE1E;;;;;GAKG;AAEI,IAAM,cAAc,GAApB,MAAM,cAAe,SAAQ,sBAAe;IAc1C,KAAK,CAAC,OAAO;QAClB,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;YACnC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;YAE1C,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,sCAAsC,KAAK,EAAE,CAAC,CAAC;QAC/D,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;QAEzB,6DAA6D;QAC7D,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;IAClB,CAAC;IAED;;;;;;OAMG;IACI,KAAK,CAAC,cAAc;QACzB,wEAAwE;QACxE,oEAAoE;QACpE,qEAAqE;QACrE,cAAc;QACd,MAAM,OAAO,GAAG,MAAM,wBAAS,CAAC,KAAK,CAAC,YAAY,EAAE,IAAI,EAAE,gBAAQ,CAAC,GAAG,EAAE,CAAC,CAAC;QAE1E,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,CAAC,CAAC;QACX,CAAC;QAED,MAAM,wBAAS,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;QAEzD,OAAO,OAAO,CAAC,MAAM,CAAC;IACxB,CAAC;IAEM,KAAK,CAAC,OAAO,CAAC,SAAiB;QACpC,MAAM,GAAG,GAAG,MAAM,wBAAS,CAAC,KAAK,CAAC;YAChC,SAAS,EAAE,SAAS;SACrB,CAAC,CAAC,KAAK,EAAE,CAAC;QAEX,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAEpC,sCAAsC;QACtC,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,OAAiB;QACjC,6EAA6E;QAC7E,qEAAqE;QACrE,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YACrC,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACvC,CAAC;QAED,MAAM,CAAC,GAAG,MAAM,wBAAS,CAAC,QAAQ,CAAC;YACjC,SAAS,EAAE,OAAO,CAAC,SAAS;SAC7B,CAAC,CAAC;QAEH,CAAC,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QAChC,CAAC,CAAC,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC;QAC/B,0EAA0E;QAC1E,CAAC,CAAC,UAAU,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,IAAI,CAAa,CAAC;QACxD,CAAC,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC1B,CAAC,CAAC,IAAI,GAAG,IAAA,wBAAiB,EAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAEzC,MAAM,CAAC,CAAC,MAAM,CAAC,qBAAe,CAAC,cAAc,CAAC,CAAC;IACjD,CAAC;IAEM,KAAK,CAAC,KAAK,CAAC,OAAiB;QAClC,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAE/C,qEAAqE;QACrE,IAAI,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC;YAC5C,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,CAAC,UAAU,GAAG,OAAO,CAAC;QAC7B,MAAM,wBAAS,CAAC,MAAM,CAAC;YACrB,UAAU,EAAE,OAAO;SACpB,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QAEzC,OAAO,IAAI,CAAC;IACd,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,SAAiB;QACnC,MAAM,wBAAS,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACrC,CAAC;IAEM,KAAK,CAAC,YAAY,CAAC,MAAc;QACtC,2EAA2E;QAC3E,4EAA4E;QAC5E,wEAAwE;QACxE,kDAAkD;QAClD,MAAM,QAAQ,GAAG,MAAM,wBAAS,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAEzD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO;QACT,CAAC;QAED,MAAM,wBAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC5D,CAAC;IAEM,KAAK,CAAC,UAAU,CAAC,MAAc;QACpC,MAAM,IAAI,GAAG,MAAM,wBAAS,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAErD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,CAAC;IAEM,KAAK,CAAC,QAAQ;QACnB,MAAM,wBAAS,CAAC,QAAQ,EAAE,CAAC;IAC7B,CAAC;IAEO,SAAS,CAAC,GAAc;QAC9B,OAAO,IAAI,kBAAW,CAAC;YACrB,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,QAAQ,EAAE,GAAG,CAAC,SAAS;YACvB,UAAU,EAAE,GAAG,CAAC,UAAU,IAAI,SAAS;YACvC,IAAI,EAAE,IAAA,wBAAiB,EAAC,qBAAqB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;SACzD,CAAC,CAAC;IACL,CAAC;IAEO,gBAAgB,CAAC,CAAuB,EAAE,CAAuB;QACvE,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;YACvC,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC;QACD,OAAO,CAAC,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC;IACvC,CAAC;CACF,CAAA;AArJY,wCAAc;AAEf;IADT,IAAA,YAAM,EAAC,kBAAkB,CAAC;8BACZ,SAAG;2CAAC;AAUT;IAHT,IAAA,sBAAM,EAAC,iCAAiC,EAAE;QACzC,YAAY,EAAE,MAAM;KACrB,CAAC;;uDACgC;yBAZvB,cAAc;IAD1B,IAAA,eAAU,EAAC,sBAAe,CAAC;GACf,cAAc,CAqJ1B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,SAAS,qBAAqB,CAAC,IAAa;IAC1C,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { OrmDriver } from '@spinajs/orm';
|
|
2
|
+
/**
|
|
3
|
+
* True when the connection speaks MySQL's DDL dialect. See {@link MYSQL_DRIVERS}.
|
|
4
|
+
*
|
|
5
|
+
* @param connection - the migration's connection
|
|
6
|
+
*/
|
|
7
|
+
export declare function isMySqlDialect(connection: OrmDriver): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Reduces a driver-reported `NativeType` to its bare type name, lower-cased.
|
|
10
|
+
*
|
|
11
|
+
* `IColumnDescriptor.NativeType` is documented as the FULL database type - MySQL
|
|
12
|
+
* reports `information_schema.DATA_TYPE` (already bare: `varchar`, `text`,
|
|
13
|
+
* `json`, `date`), but sqlite reports the declared type verbatim, so
|
|
14
|
+
* `VARCHAR(36)` and `INT(10) UNSIGNED` both occur. Comparing raw strings would
|
|
15
|
+
* therefore miss exactly the databases the probe exists to recognise.
|
|
16
|
+
*
|
|
17
|
+
* @param nativeType - whatever the driver reported
|
|
18
|
+
*/
|
|
19
|
+
export declare function normalizeNativeType(nativeType: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* True when the (normalized) type is one of the text-family types listed in
|
|
22
|
+
* {@link TEXT_FAMILY}.
|
|
23
|
+
*
|
|
24
|
+
* @param nativeType - a type name, normalized or not
|
|
25
|
+
*/
|
|
26
|
+
export declare function isTextFamily(nativeType: string): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Normalized native type of one column, or `null` when it cannot be established.
|
|
29
|
+
*
|
|
30
|
+
* `null` means "do not touch anything": the table is not described, the column
|
|
31
|
+
* is absent, or the driver's `tableInfo()` refused/failed. A migration that
|
|
32
|
+
* cannot see the current state must SKIP, never guess - a throw here would be
|
|
33
|
+
* recorded as a failed migration, and `assertNoFailed()` then blocks every
|
|
34
|
+
* subsequent migration on that connection until somebody edits the tracking
|
|
35
|
+
* table by hand. On a service that migrates at boot that is an outage, and the
|
|
36
|
+
* thing being probed is a type conversion nobody is waiting for.
|
|
37
|
+
*
|
|
38
|
+
* @param connection - the migration's connection
|
|
39
|
+
* @param table - table name
|
|
40
|
+
* @param column - column name, compared case-insensitively
|
|
41
|
+
*/
|
|
42
|
+
export declare function columnNativeType(connection: OrmDriver, table: string, column: string): Promise<string | null>;
|
|
43
|
+
//# sourceMappingURL=migration-support.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-support.d.ts","sourceRoot":"","sources":["../../src/migration-support.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAiCzC;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,SAAS,GAAG,OAAO,CAE7D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAkBnH"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isMySqlDialect = isMySqlDialect;
|
|
4
|
+
exports.normalizeNativeType = normalizeNativeType;
|
|
5
|
+
exports.isTextFamily = isTextFamily;
|
|
6
|
+
exports.columnNativeType = columnNativeType;
|
|
7
|
+
/**
|
|
8
|
+
* Shared probes for the converging migrations in `./migrations`.
|
|
9
|
+
*
|
|
10
|
+
* Lives OUTSIDE `src/migrations` on purpose: `MigrationSource` globs that
|
|
11
|
+
* directory and imports every `.js` it finds, and a module that is not a
|
|
12
|
+
* migration has no business being pulled into that scan.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Driver names (the `@Injectable(...)` keys the mysql package registers, which
|
|
16
|
+
* is what `db.Connections[n].Driver` carries) whose dialect understands
|
|
17
|
+
* `ALTER TABLE ... MODIFY <col> <type>` and has a native `JSON` column type.
|
|
18
|
+
*
|
|
19
|
+
* This gate is not decoration. `MODIFY` is MySQL/MariaDB syntax - MSSQL spells
|
|
20
|
+
* it `ALTER COLUMN` and has no `JSON` type at all, sqlite has neither - so
|
|
21
|
+
* emitting the statement anywhere else is a syntax error, i.e. a FAILED
|
|
22
|
+
* migration, i.e. `assertNoFailed()` blocking every future migration on that
|
|
23
|
+
* connection. Every other dialect stores the payload as text and the read path
|
|
24
|
+
* (`decodeSessionData`) already handles the string shape, so skipping there is
|
|
25
|
+
* not a compromise: there is genuinely nothing to converge.
|
|
26
|
+
*/
|
|
27
|
+
const MYSQL_DRIVERS = ['orm-driver-mysql', 'orm-driver-mysql-ssh'];
|
|
28
|
+
/**
|
|
29
|
+
* The text-family native types a legacy `user_sessions.Data` can carry. A
|
|
30
|
+
* column of any of these is the state the JSON conversion exists to fix;
|
|
31
|
+
* anything else (`json` on a converged or fresh database, or something nobody
|
|
32
|
+
* anticipated) is left alone.
|
|
33
|
+
*/
|
|
34
|
+
const TEXT_FAMILY = ['text', 'tinytext', 'mediumtext', 'longtext', 'varchar', 'nvarchar'];
|
|
35
|
+
/**
|
|
36
|
+
* True when the connection speaks MySQL's DDL dialect. See {@link MYSQL_DRIVERS}.
|
|
37
|
+
*
|
|
38
|
+
* @param connection - the migration's connection
|
|
39
|
+
*/
|
|
40
|
+
function isMySqlDialect(connection) {
|
|
41
|
+
return MYSQL_DRIVERS.includes(connection.Options?.Driver);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Reduces a driver-reported `NativeType` to its bare type name, lower-cased.
|
|
45
|
+
*
|
|
46
|
+
* `IColumnDescriptor.NativeType` is documented as the FULL database type - MySQL
|
|
47
|
+
* reports `information_schema.DATA_TYPE` (already bare: `varchar`, `text`,
|
|
48
|
+
* `json`, `date`), but sqlite reports the declared type verbatim, so
|
|
49
|
+
* `VARCHAR(36)` and `INT(10) UNSIGNED` both occur. Comparing raw strings would
|
|
50
|
+
* therefore miss exactly the databases the probe exists to recognise.
|
|
51
|
+
*
|
|
52
|
+
* @param nativeType - whatever the driver reported
|
|
53
|
+
*/
|
|
54
|
+
function normalizeNativeType(nativeType) {
|
|
55
|
+
return String(nativeType).toLowerCase().split('(')[0].trim().split(/\s+/)[0];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* True when the (normalized) type is one of the text-family types listed in
|
|
59
|
+
* {@link TEXT_FAMILY}.
|
|
60
|
+
*
|
|
61
|
+
* @param nativeType - a type name, normalized or not
|
|
62
|
+
*/
|
|
63
|
+
function isTextFamily(nativeType) {
|
|
64
|
+
return TEXT_FAMILY.includes(normalizeNativeType(nativeType));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Normalized native type of one column, or `null` when it cannot be established.
|
|
68
|
+
*
|
|
69
|
+
* `null` means "do not touch anything": the table is not described, the column
|
|
70
|
+
* is absent, or the driver's `tableInfo()` refused/failed. A migration that
|
|
71
|
+
* cannot see the current state must SKIP, never guess - a throw here would be
|
|
72
|
+
* recorded as a failed migration, and `assertNoFailed()` then blocks every
|
|
73
|
+
* subsequent migration on that connection until somebody edits the tracking
|
|
74
|
+
* table by hand. On a service that migrates at boot that is an outage, and the
|
|
75
|
+
* thing being probed is a type conversion nobody is waiting for.
|
|
76
|
+
*
|
|
77
|
+
* @param connection - the migration's connection
|
|
78
|
+
* @param table - table name
|
|
79
|
+
* @param column - column name, compared case-insensitively
|
|
80
|
+
*/
|
|
81
|
+
async function columnNativeType(connection, table, column) {
|
|
82
|
+
let columns;
|
|
83
|
+
try {
|
|
84
|
+
columns = await connection.tableInfo(table);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// e.g. the mysql driver throws when the connection carries no Database, and
|
|
88
|
+
// a driver may not implement tableInfo usefully at all.
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
const found = columns?.find((c) => c.Name?.toLowerCase() === column.toLowerCase());
|
|
92
|
+
if (!found?.NativeType) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
return normalizeNativeType(found.NativeType);
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=migration-support.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-support.js","sourceRoot":"","sources":["../../src/migration-support.ts"],"names":[],"mappings":";;AAsCA,wCAEC;AAaD,kDAEC;AAQD,oCAEC;AAiBD,4CAkBC;AAlGD;;;;;;GAMG;AAEH;;;;;;;;;;;;GAYG;AACH,MAAM,aAAa,GAAG,CAAC,kBAAkB,EAAE,sBAAsB,CAAC,CAAC;AAEnE;;;;;GAKG;AACH,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;AAE1F;;;;GAIG;AACH,SAAgB,cAAc,CAAC,UAAqB;IAClD,OAAO,aAAa,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;AAC5D,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,mBAAmB,CAAC,UAAkB;IACpD,OAAO,MAAM,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/E,CAAC;AAED;;;;;GAKG;AACH,SAAgB,YAAY,CAAC,UAAkB;IAC7C,OAAO,WAAW,CAAC,QAAQ,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACI,KAAK,UAAU,gBAAgB,CAAC,UAAqB,EAAE,KAAa,EAAE,MAAc;IACzF,IAAI,OAAO,CAAC;IAEZ,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,4EAA4E;QAC5E,wDAAwD;QACxD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IAEnF,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,mBAAmB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;AAC/C,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserSessionDBSqlMigration_2022_06_28_01_20_00.d.ts","sourceRoot":"","sources":["../../../src/migrations/UserSessionDBSqlMigration_2022_06_28_01_20_00.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAAE,SAAS,EAAqB,MAAM,cAAc,CAAC;AAErF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBACa,6CAA8C,SAAQ,YAAY;IAChE,EAAE,CAAC,UAAU,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"UserSessionDBSqlMigration_2022_06_28_01_20_00.d.ts","sourceRoot":"","sources":["../../../src/migrations/UserSessionDBSqlMigration_2022_06_28_01_20_00.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAAE,SAAS,EAAqB,MAAM,cAAc,CAAC;AAErF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBACa,6CAA8C,SAAQ,YAAY;IAChE,EAAE,CAAC,UAAU,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BxC,IAAI,CAAC,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAC/C"}
|
|
@@ -41,6 +41,14 @@ let UserSessionDBSqlMigration_2022_06_28_01_20_00 = class UserSessionDBSqlMigrat
|
|
|
41
41
|
table.string('SessionId', 36).primaryKey().notNull();
|
|
42
42
|
table.dateTime('CreatedAt').notNull();
|
|
43
43
|
table.dateTime('Expiration');
|
|
44
|
+
// JSON is the intended type, so a fresh install gets it straight away and never needs the
|
|
45
|
+
// converging migration (`UserSessionDataJson_2026_07_31_00_00_00`) to do any work. Deployed
|
|
46
|
+
// databases created before this migration existed carry a text-family column - the guard
|
|
47
|
+
// above means the type declared here has never run against them - and that migration is what
|
|
48
|
+
// brings them here.
|
|
49
|
+
//
|
|
50
|
+
// The read path consumes both shapes on purpose: mysql2 returns a json column as a parsed
|
|
51
|
+
// object, sqlite (which has no json type) returns the stored text. See `decodeSessionData`.
|
|
44
52
|
table.json('Data').notNull();
|
|
45
53
|
table.int('UserId').notNull();
|
|
46
54
|
table.foreignKey('UserId').references('users', 'Id').onDelete(orm_1.ReferentialAction.Cascade);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserSessionDBSqlMigration_2022_06_28_01_20_00.js","sourceRoot":"","sources":["../../../src/migrations/UserSessionDBSqlMigration_2022_06_28_01_20_00.ts"],"names":[],"mappings":";;;;;;;;;AAAA,sCAAqF;AAErF;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEI,IAAM,6CAA6C,GAAnD,MAAM,6CAA8C,SAAQ,kBAAY;IACtE,KAAK,CAAC,EAAE,CAAC,UAAqB;QACnC,6FAA6F;QAC7F,uEAAuE;QACvE,IAAI,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,eAAe,CAAC,EAAE,CAAC;YAC3D,OAAO;QACT,CAAC;QAED,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,EAAE;YAC/D,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE,CAAC;YACrD,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC;YACtC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;YAC7B,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC;YAC7B,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;YAE9B,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,uBAAiB,CAAC,OAAO,CAAC,CAAC;QAC3F,CAAC,CAAC,CAAC;QAEH,gGAAgG;QAChG,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACtH,CAAC;IAED,qCAAqC;IAC9B,KAAK,CAAC,IAAI,CAAC,CAAY,IAAkB,CAAC;CAClD,CAAA;
|
|
1
|
+
{"version":3,"file":"UserSessionDBSqlMigration_2022_06_28_01_20_00.js","sourceRoot":"","sources":["../../../src/migrations/UserSessionDBSqlMigration_2022_06_28_01_20_00.ts"],"names":[],"mappings":";;;;;;;;;AAAA,sCAAqF;AAErF;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEI,IAAM,6CAA6C,GAAnD,MAAM,6CAA8C,SAAQ,kBAAY;IACtE,KAAK,CAAC,EAAE,CAAC,UAAqB;QACnC,6FAA6F;QAC7F,uEAAuE;QACvE,IAAI,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,eAAe,CAAC,EAAE,CAAC;YAC3D,OAAO;QACT,CAAC;QAED,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,EAAE;YAC/D,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE,CAAC;YACrD,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC;YACtC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;YAC7B,0FAA0F;YAC1F,4FAA4F;YAC5F,yFAAyF;YACzF,6FAA6F;YAC7F,oBAAoB;YACpB,EAAE;YACF,0FAA0F;YAC1F,4FAA4F;YAC5F,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC;YAC7B,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;YAE9B,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,uBAAiB,CAAC,OAAO,CAAC,CAAC;QAC3F,CAAC,CAAC,CAAC;QAEH,gGAAgG;QAChG,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACtH,CAAC;IAED,qCAAqC;IAC9B,KAAK,CAAC,IAAI,CAAC,CAAY,IAAkB,CAAC;CAClD,CAAA;AAhCY,sGAA6C;wDAA7C,6CAA6C;IADzD,IAAA,eAAS,EAAC,6BAA6B,CAAC;GAC5B,6CAA6C,CAgCzD"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { OrmMigration, OrmDriver } from '@spinajs/orm';
|
|
2
|
+
/**
|
|
3
|
+
* Converts an EXISTING text-typed `user_sessions.Data` to a native MySQL `json`
|
|
4
|
+
* column, so the read path receives a parsed object graph instead of text.
|
|
5
|
+
*
|
|
6
|
+
* Databases created before the create-path migration
|
|
7
|
+
* (`UserSessionDBSqlMigration_2022_06_28_01_20_00`) declared `json` - i.e. every
|
|
8
|
+
* deployment - carry `Data text NOT NULL`. The create migration returns early
|
|
9
|
+
* when the table is already there, so the type it declares has never reached
|
|
10
|
+
* them. Fresh installs already get `json`. Two shapes of the same table exist in
|
|
11
|
+
* the wild and nothing detected which one you had; this migration ends that.
|
|
12
|
+
*
|
|
13
|
+
* mysql2 parses a json column on the way out, so the read path receives the
|
|
14
|
+
* payload as a live object graph and rebuilds the session `Map` from it directly
|
|
15
|
+
* (`decodeSessionData`). The write path is unchanged - `encodeSessionData` still
|
|
16
|
+
* produces JSON text, which a json column stores verbatim.
|
|
17
|
+
*
|
|
18
|
+
* Everything below is a guard. Read them in order.
|
|
19
|
+
*
|
|
20
|
+
* 1. Table missing: nothing to converge
|
|
21
|
+
* ---------------------------------------
|
|
22
|
+
* It normally IS there - the create migration carries an earlier timestamp and
|
|
23
|
+
* runs first in the same batch - but a connection that hosts only part of the
|
|
24
|
+
* schema must not blow up here.
|
|
25
|
+
*
|
|
26
|
+
* 2. Column state probe: skip unless it is text-family
|
|
27
|
+
* ------------------------------------------------------
|
|
28
|
+
* The previous version of this migration was unconditional, on the argument that
|
|
29
|
+
* `MODIFY` to a type a column already has is a harmless rewrite. True on MySQL,
|
|
30
|
+
* and irrelevant everywhere else: it reached the schema builder's `alterTable()`,
|
|
31
|
+
* which resolves `AlterTableQueryCompiler` - and `orm-mssql` registers no
|
|
32
|
+
* implementation for it, so DI hands back the ABSTRACT class, whose `compile()`
|
|
33
|
+
* does not exist. That throw is recorded as a FAILED migration, and
|
|
34
|
+
* `assertNoFailed()` then refuses to run ANY migration on that connection ever
|
|
35
|
+
* again: the application never boots without manual surgery on the tracking
|
|
36
|
+
* table. Probing the target state first and returning early is the same shape
|
|
37
|
+
* `@spinajs/rbac`'s `RBACRegisteredAt_2026_07_28_00_00_00` already uses, and it
|
|
38
|
+
* makes the no-op case genuinely free rather than merely survivable.
|
|
39
|
+
*
|
|
40
|
+
* An unreadable probe skips too - see `columnNativeType`.
|
|
41
|
+
*
|
|
42
|
+
* 3. Dialect gate: MySQL only
|
|
43
|
+
* ------------------------------
|
|
44
|
+
* `MODIFY` and the `JSON` type are MySQL's. A text-family `Data` on MSSQL
|
|
45
|
+
* (`nvarchar`) or sqlite (`TEXT`) passes guard 2 and would then be handed
|
|
46
|
+
* syntax its server rejects - the same failed-migration outage guard 2 exists to
|
|
47
|
+
* prevent. Those dialects keep a text column and the read path keeps decoding a
|
|
48
|
+
* string, which is exactly what `decodeSessionData` does with it.
|
|
49
|
+
*
|
|
50
|
+
* 4. The wipe
|
|
51
|
+
* -----------
|
|
52
|
+
* Reached only when an actual `text -> json` conversion is about to happen.
|
|
53
|
+
* MySQL validates EVERY existing row during that conversion, and a single
|
|
54
|
+
* malformed / truncated / empty payload (a row from a crashed write, a row
|
|
55
|
+
* written by an older codec, a row truncated by TEXT's 64 KiB limit) fails the
|
|
56
|
+
* statement:
|
|
57
|
+
*
|
|
58
|
+
* ERROR 3140 (22032): Invalid JSON text: "Invalid value." at position 1
|
|
59
|
+
*
|
|
60
|
+
* Emptying the table first makes the conversion unconditionally safe. Sessions
|
|
61
|
+
* are disposable state: the worst a user sees is one forced re-login, and that
|
|
62
|
+
* is now paid ONLY by the deployments actually being converted - a fresh install
|
|
63
|
+
* skips at guard 2 and never deletes anything.
|
|
64
|
+
*
|
|
65
|
+
* `DELETE`, not `TRUNCATE`: truncate is not transactional, is refused outright
|
|
66
|
+
* by some engines for a table involved in foreign keys, and there is no volume
|
|
67
|
+
* here to make the difference worth having.
|
|
68
|
+
*
|
|
69
|
+
* 5. ONE statement, not three
|
|
70
|
+
* ----------------------------
|
|
71
|
+
* `alterTable()` compiles one `ICompilerOutput` per modified column and
|
|
72
|
+
* `SqlDriver.execute` wraps the array in `Promise.all` - three concurrent
|
|
73
|
+
* `ALTER TABLE`s on three pooled connections against one table. A partial
|
|
74
|
+
* failure leaves the table half-converted AND the migration marked failed. The
|
|
75
|
+
* raw escape hatch (`schema().raw`, compiled by `SqlRawSchemaQueryCompiler`,
|
|
76
|
+
* registered by every SQL driver via `orm-sql`) sends exactly one statement.
|
|
77
|
+
* The `CreatedAt`/`Expiration` widening that used to ride along here now lives
|
|
78
|
+
* in its own migration (`UserSessionTimestamps_2026_07_31_00_00_01`), because it
|
|
79
|
+
* is a cast that cannot fail and must stay independently applicable.
|
|
80
|
+
*/
|
|
81
|
+
export declare class UserSessionDataJson_2026_07_31_00_00_00 extends OrmMigration {
|
|
82
|
+
up(connection: OrmDriver): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Restores the text column. The sessions `up()` deleted are gone for good -
|
|
85
|
+
* that part is not reversible and nothing pretends otherwise - but the SHAPE
|
|
86
|
+
* is, and a package rollback that leaves a `json` column behind for a
|
|
87
|
+
* `2.0.488` reader is its own outage.
|
|
88
|
+
*
|
|
89
|
+
* Guarded by the inverse probe: only a column that is currently `json` is
|
|
90
|
+
* touched, so running `down()` twice, or against a database this migration
|
|
91
|
+
* never converted, does nothing.
|
|
92
|
+
*
|
|
93
|
+
* `TEXT` because `TEXT` is what the deployed databases had. Note this is a
|
|
94
|
+
* NARROWING cast - a stored payload above TEXT's 64 KiB limit would fail the
|
|
95
|
+
* statement in strict mode. That is acceptable here in a way it would not be
|
|
96
|
+
* in `up()`: `down()` runs only on an explicit, interactive rollback, never at
|
|
97
|
+
* boot, so a failure is loud and immediate rather than an unbootable service.
|
|
98
|
+
*/
|
|
99
|
+
down(connection: OrmDriver): Promise<void>;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=UserSessionDataJson_2026_07_31_00_00_00.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserSessionDataJson_2026_07_31_00_00_00.d.ts","sourceRoot":"","sources":["../../../src/migrations/UserSessionDataJson_2026_07_31_00_00_00.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGlE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8EG;AACH,qBACa,uCAAwC,SAAQ,YAAY;IAC1D,EAAE,CAAC,UAAU,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BrD;;;;;;;;;;;;;;;OAeG;IACU,IAAI,CAAC,UAAU,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAaxD"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.UserSessionDataJson_2026_07_31_00_00_00 = void 0;
|
|
10
|
+
const orm_1 = require("@spinajs/orm");
|
|
11
|
+
const migration_support_js_1 = require("../migration-support.js");
|
|
12
|
+
/**
|
|
13
|
+
* Converts an EXISTING text-typed `user_sessions.Data` to a native MySQL `json`
|
|
14
|
+
* column, so the read path receives a parsed object graph instead of text.
|
|
15
|
+
*
|
|
16
|
+
* Databases created before the create-path migration
|
|
17
|
+
* (`UserSessionDBSqlMigration_2022_06_28_01_20_00`) declared `json` - i.e. every
|
|
18
|
+
* deployment - carry `Data text NOT NULL`. The create migration returns early
|
|
19
|
+
* when the table is already there, so the type it declares has never reached
|
|
20
|
+
* them. Fresh installs already get `json`. Two shapes of the same table exist in
|
|
21
|
+
* the wild and nothing detected which one you had; this migration ends that.
|
|
22
|
+
*
|
|
23
|
+
* mysql2 parses a json column on the way out, so the read path receives the
|
|
24
|
+
* payload as a live object graph and rebuilds the session `Map` from it directly
|
|
25
|
+
* (`decodeSessionData`). The write path is unchanged - `encodeSessionData` still
|
|
26
|
+
* produces JSON text, which a json column stores verbatim.
|
|
27
|
+
*
|
|
28
|
+
* Everything below is a guard. Read them in order.
|
|
29
|
+
*
|
|
30
|
+
* 1. Table missing: nothing to converge
|
|
31
|
+
* ---------------------------------------
|
|
32
|
+
* It normally IS there - the create migration carries an earlier timestamp and
|
|
33
|
+
* runs first in the same batch - but a connection that hosts only part of the
|
|
34
|
+
* schema must not blow up here.
|
|
35
|
+
*
|
|
36
|
+
* 2. Column state probe: skip unless it is text-family
|
|
37
|
+
* ------------------------------------------------------
|
|
38
|
+
* The previous version of this migration was unconditional, on the argument that
|
|
39
|
+
* `MODIFY` to a type a column already has is a harmless rewrite. True on MySQL,
|
|
40
|
+
* and irrelevant everywhere else: it reached the schema builder's `alterTable()`,
|
|
41
|
+
* which resolves `AlterTableQueryCompiler` - and `orm-mssql` registers no
|
|
42
|
+
* implementation for it, so DI hands back the ABSTRACT class, whose `compile()`
|
|
43
|
+
* does not exist. That throw is recorded as a FAILED migration, and
|
|
44
|
+
* `assertNoFailed()` then refuses to run ANY migration on that connection ever
|
|
45
|
+
* again: the application never boots without manual surgery on the tracking
|
|
46
|
+
* table. Probing the target state first and returning early is the same shape
|
|
47
|
+
* `@spinajs/rbac`'s `RBACRegisteredAt_2026_07_28_00_00_00` already uses, and it
|
|
48
|
+
* makes the no-op case genuinely free rather than merely survivable.
|
|
49
|
+
*
|
|
50
|
+
* An unreadable probe skips too - see `columnNativeType`.
|
|
51
|
+
*
|
|
52
|
+
* 3. Dialect gate: MySQL only
|
|
53
|
+
* ------------------------------
|
|
54
|
+
* `MODIFY` and the `JSON` type are MySQL's. A text-family `Data` on MSSQL
|
|
55
|
+
* (`nvarchar`) or sqlite (`TEXT`) passes guard 2 and would then be handed
|
|
56
|
+
* syntax its server rejects - the same failed-migration outage guard 2 exists to
|
|
57
|
+
* prevent. Those dialects keep a text column and the read path keeps decoding a
|
|
58
|
+
* string, which is exactly what `decodeSessionData` does with it.
|
|
59
|
+
*
|
|
60
|
+
* 4. The wipe
|
|
61
|
+
* -----------
|
|
62
|
+
* Reached only when an actual `text -> json` conversion is about to happen.
|
|
63
|
+
* MySQL validates EVERY existing row during that conversion, and a single
|
|
64
|
+
* malformed / truncated / empty payload (a row from a crashed write, a row
|
|
65
|
+
* written by an older codec, a row truncated by TEXT's 64 KiB limit) fails the
|
|
66
|
+
* statement:
|
|
67
|
+
*
|
|
68
|
+
* ERROR 3140 (22032): Invalid JSON text: "Invalid value." at position 1
|
|
69
|
+
*
|
|
70
|
+
* Emptying the table first makes the conversion unconditionally safe. Sessions
|
|
71
|
+
* are disposable state: the worst a user sees is one forced re-login, and that
|
|
72
|
+
* is now paid ONLY by the deployments actually being converted - a fresh install
|
|
73
|
+
* skips at guard 2 and never deletes anything.
|
|
74
|
+
*
|
|
75
|
+
* `DELETE`, not `TRUNCATE`: truncate is not transactional, is refused outright
|
|
76
|
+
* by some engines for a table involved in foreign keys, and there is no volume
|
|
77
|
+
* here to make the difference worth having.
|
|
78
|
+
*
|
|
79
|
+
* 5. ONE statement, not three
|
|
80
|
+
* ----------------------------
|
|
81
|
+
* `alterTable()` compiles one `ICompilerOutput` per modified column and
|
|
82
|
+
* `SqlDriver.execute` wraps the array in `Promise.all` - three concurrent
|
|
83
|
+
* `ALTER TABLE`s on three pooled connections against one table. A partial
|
|
84
|
+
* failure leaves the table half-converted AND the migration marked failed. The
|
|
85
|
+
* raw escape hatch (`schema().raw`, compiled by `SqlRawSchemaQueryCompiler`,
|
|
86
|
+
* registered by every SQL driver via `orm-sql`) sends exactly one statement.
|
|
87
|
+
* The `CreatedAt`/`Expiration` widening that used to ride along here now lives
|
|
88
|
+
* in its own migration (`UserSessionTimestamps_2026_07_31_00_00_01`), because it
|
|
89
|
+
* is a cast that cannot fail and must stay independently applicable.
|
|
90
|
+
*/
|
|
91
|
+
let UserSessionDataJson_2026_07_31_00_00_00 = class UserSessionDataJson_2026_07_31_00_00_00 extends orm_1.OrmMigration {
|
|
92
|
+
async up(connection) {
|
|
93
|
+
if (!(await connection.schema().tableExists('user_sessions'))) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const dataType = await (0, migration_support_js_1.columnNativeType)(connection, 'user_sessions', 'Data');
|
|
97
|
+
// already `json` (fresh install / already converged), unreadable, or a type
|
|
98
|
+
// nobody anticipated - all three mean "leave it alone"
|
|
99
|
+
if (dataType === null || !(0, migration_support_js_1.isTextFamily)(dataType)) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (!(0, migration_support_js_1.isMySqlDialect)(connection)) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
await connection.schema().raw('DELETE FROM user_sessions');
|
|
106
|
+
// Identifiers are backtick-quoted: this statement is MySQL-only by
|
|
107
|
+
// construction (guard 3), so portability of the quoting style is not a
|
|
108
|
+
// constraint, and quoting removes any question about `Data` being a MySQL
|
|
109
|
+
// keyword. `NOT NULL` is restated because MODIFY replaces the WHOLE column
|
|
110
|
+
// definition - omitting it would silently make the column nullable.
|
|
111
|
+
await connection.schema().raw('ALTER TABLE `user_sessions` MODIFY `Data` JSON NOT NULL');
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Restores the text column. The sessions `up()` deleted are gone for good -
|
|
115
|
+
* that part is not reversible and nothing pretends otherwise - but the SHAPE
|
|
116
|
+
* is, and a package rollback that leaves a `json` column behind for a
|
|
117
|
+
* `2.0.488` reader is its own outage.
|
|
118
|
+
*
|
|
119
|
+
* Guarded by the inverse probe: only a column that is currently `json` is
|
|
120
|
+
* touched, so running `down()` twice, or against a database this migration
|
|
121
|
+
* never converted, does nothing.
|
|
122
|
+
*
|
|
123
|
+
* `TEXT` because `TEXT` is what the deployed databases had. Note this is a
|
|
124
|
+
* NARROWING cast - a stored payload above TEXT's 64 KiB limit would fail the
|
|
125
|
+
* statement in strict mode. That is acceptable here in a way it would not be
|
|
126
|
+
* in `up()`: `down()` runs only on an explicit, interactive rollback, never at
|
|
127
|
+
* boot, so a failure is loud and immediate rather than an unbootable service.
|
|
128
|
+
*/
|
|
129
|
+
async down(connection) {
|
|
130
|
+
if (!(await connection.schema().tableExists('user_sessions'))) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const dataType = await (0, migration_support_js_1.columnNativeType)(connection, 'user_sessions', 'Data');
|
|
134
|
+
if (dataType !== 'json' || !(0, migration_support_js_1.isMySqlDialect)(connection)) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
await connection.schema().raw('ALTER TABLE `user_sessions` MODIFY `Data` TEXT NOT NULL');
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
exports.UserSessionDataJson_2026_07_31_00_00_00 = UserSessionDataJson_2026_07_31_00_00_00;
|
|
141
|
+
exports.UserSessionDataJson_2026_07_31_00_00_00 = UserSessionDataJson_2026_07_31_00_00_00 = __decorate([
|
|
142
|
+
(0, orm_1.Migration)('session-provider-connection')
|
|
143
|
+
], UserSessionDataJson_2026_07_31_00_00_00);
|
|
144
|
+
//# sourceMappingURL=UserSessionDataJson_2026_07_31_00_00_00.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserSessionDataJson_2026_07_31_00_00_00.js","sourceRoot":"","sources":["../../../src/migrations/UserSessionDataJson_2026_07_31_00_00_00.ts"],"names":[],"mappings":";;;;;;;;;AAAA,sCAAkE;AAClE,kEAAyF;AAEzF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8EG;AAEI,IAAM,uCAAuC,GAA7C,MAAM,uCAAwC,SAAQ,kBAAY;IAChE,KAAK,CAAC,EAAE,CAAC,UAAqB;QACnC,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,EAAE,CAAC;YAC9D,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,IAAA,uCAAgB,EAAC,UAAU,EAAE,eAAe,EAAE,MAAM,CAAC,CAAC;QAE7E,4EAA4E;QAC5E,uDAAuD;QACvD,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC,IAAA,mCAAY,EAAC,QAAQ,CAAC,EAAE,CAAC;YACjD,OAAO;QACT,CAAC;QAED,IAAI,CAAC,IAAA,qCAAc,EAAC,UAAU,CAAC,EAAE,CAAC;YAChC,OAAO;QACT,CAAC;QAED,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;QAE3D,mEAAmE;QACnE,uEAAuE;QACvE,0EAA0E;QAC1E,2EAA2E;QAC3E,oEAAoE;QACpE,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,yDAAyD,CAAC,CAAC;IAC3F,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACI,KAAK,CAAC,IAAI,CAAC,UAAqB;QACrC,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,EAAE,CAAC;YAC9D,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,IAAA,uCAAgB,EAAC,UAAU,EAAE,eAAe,EAAE,MAAM,CAAC,CAAC;QAE7E,IAAI,QAAQ,KAAK,MAAM,IAAI,CAAC,IAAA,qCAAc,EAAC,UAAU,CAAC,EAAE,CAAC;YACvD,OAAO;QACT,CAAC;QAED,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,yDAAyD,CAAC,CAAC;IAC3F,CAAC;CACF,CAAA;AAzDY,0FAAuC;kDAAvC,uCAAuC;IADnD,IAAA,eAAS,EAAC,6BAA6B,CAAC;GAC5B,uCAAuC,CAyDnD"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { OrmMigration, OrmDriver } from '@spinajs/orm';
|
|
2
|
+
/**
|
|
3
|
+
* Widens `user_sessions.CreatedAt` / `Expiration` from `date` to `datetime`.
|
|
4
|
+
*
|
|
5
|
+
* !! This is a correctness fix in its own right and has nothing to do with the
|
|
6
|
+
* JSON conversion it used to be bundled with. It is a separate migration
|
|
7
|
+
* precisely so it can be applied, skipped, reviewed and reverted on its own.
|
|
8
|
+
*
|
|
9
|
+
* The bug
|
|
10
|
+
* -------
|
|
11
|
+
* A MySQL `date` column stores only the calendar day - MySQL truncates the time
|
|
12
|
+
* component on write, silently:
|
|
13
|
+
*
|
|
14
|
+
* INSERT INTO probe VALUES ('2026-07-31 23:45:12', '2026-07-31 23:45:12');
|
|
15
|
+
* asDate asDateTime
|
|
16
|
+
* 2026-07-31 2026-07-31 23:45:12
|
|
17
|
+
*
|
|
18
|
+
* So on a deployed database a session's `Expiration` has no time of day: a
|
|
19
|
+
* session created at 23:45 with a 60-minute sliding TTL, due to expire at
|
|
20
|
+
* 00:45:12, is stored as expiring at 00:00:00 and the cleanup sweep kills it
|
|
21
|
+
* ~45 minutes early. Sliding expiration, `SessionProvider.isExpired`,
|
|
22
|
+
* `cleanupExpired` and `touch` all operate on that truncated value. `DbSession`
|
|
23
|
+
* declares both fields as luxon `DateTime` and the create path has declared both
|
|
24
|
+
* as `dateTime` since 2024; `datetime` is the type the code has always assumed
|
|
25
|
+
* it had.
|
|
26
|
+
*
|
|
27
|
+
* Why this one is safe where the JSON one was not
|
|
28
|
+
* -----------------------------------------------
|
|
29
|
+
* `date -> datetime` is a WIDENING cast. Every existing value is representable
|
|
30
|
+
* in the target type (midnight of the stored day), so unlike the `text -> json`
|
|
31
|
+
* conversion it cannot fail on row content and needs no wipe. Nothing is
|
|
32
|
+
* deleted here.
|
|
33
|
+
*
|
|
34
|
+
* Guards mirror the JSON migration: skip when the table is absent, skip unless
|
|
35
|
+
* `CreatedAt` is currently exactly `date`, skip on any non-MySQL dialect
|
|
36
|
+
* (`MODIFY` is MySQL syntax; a failed migration at boot bricks every later
|
|
37
|
+
* migration on the connection through `assertNoFailed()`). `CreatedAt` alone is
|
|
38
|
+
* probed - the two columns were created by the same statement in every database
|
|
39
|
+
* that has the old shape, and a `date`/`datetime` split between them has never
|
|
40
|
+
* existed.
|
|
41
|
+
*
|
|
42
|
+
* ONE statement with two `MODIFY` clauses, via the raw escape hatch, rather than
|
|
43
|
+
* `alterTable()`: the builder compiles one statement per column and
|
|
44
|
+
* `SqlDriver.execute` runs them through `Promise.all`, i.e. two concurrent
|
|
45
|
+
* `ALTER TABLE`s on the same table from two pooled connections.
|
|
46
|
+
*
|
|
47
|
+
* Nullability is restated verbatim from the create path - `MODIFY` replaces the
|
|
48
|
+
* whole column definition, and `CreatedAt` is `notNull()` there while
|
|
49
|
+
* `Expiration` is nullable (a never-expiring session persists a NULL).
|
|
50
|
+
*/
|
|
51
|
+
export declare class UserSessionTimestamps_2026_07_31_00_00_01 extends OrmMigration {
|
|
52
|
+
up(connection: OrmDriver): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Deliberately a no-op, and this is a decision rather than an omission.
|
|
55
|
+
*
|
|
56
|
+
* `up()` widened `date -> datetime`. Reversing it would be a NARROWING cast
|
|
57
|
+
* that silently truncates the time-of-day off every stored row - it destroys
|
|
58
|
+
* data, and it re-introduces the exact expiry bug this migration exists to
|
|
59
|
+
* fix, on a schema whose model and create path have declared `dateTime` for
|
|
60
|
+
* years. A `datetime` column is also perfectly readable by every version of
|
|
61
|
+
* this package, so a rollback has nothing to gain from shrinking it.
|
|
62
|
+
*
|
|
63
|
+
* (The JSON migration's `down()` is implemented, because there the old readers
|
|
64
|
+
* genuinely cannot cope with the new column type. That is not the case here.)
|
|
65
|
+
*/
|
|
66
|
+
down(_connection: OrmDriver): Promise<void>;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=UserSessionTimestamps_2026_07_31_00_00_01.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserSessionTimestamps_2026_07_31_00_00_01.d.ts","sourceRoot":"","sources":["../../../src/migrations/UserSessionTimestamps_2026_07_31_00_00_01.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGlE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,qBACa,yCAA0C,SAAQ,YAAY;IAC5D,EAAE,CAAC,UAAU,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBrD;;;;;;;;;;;;OAYG;IAEU,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CACzD"}
|