@spinajs/session-provider-db 2.0.488 → 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.
Files changed (47) hide show
  1. package/lib/cjs/index.d.ts +2 -0
  2. package/lib/cjs/index.d.ts.map +1 -1
  3. package/lib/cjs/index.js +33 -1
  4. package/lib/cjs/index.js.map +1 -1
  5. package/lib/cjs/migration-support.d.ts +43 -0
  6. package/lib/cjs/migration-support.d.ts.map +1 -0
  7. package/lib/cjs/migration-support.js +97 -0
  8. package/lib/cjs/migration-support.js.map +1 -0
  9. package/lib/cjs/migrations/UserSessionDBSqlMigration_2022_06_28_01_20_00.d.ts.map +1 -1
  10. package/lib/cjs/migrations/UserSessionDBSqlMigration_2022_06_28_01_20_00.js +8 -0
  11. package/lib/cjs/migrations/UserSessionDBSqlMigration_2022_06_28_01_20_00.js.map +1 -1
  12. package/lib/cjs/migrations/UserSessionDataJson_2026_07_31_00_00_00.d.ts +101 -0
  13. package/lib/cjs/migrations/UserSessionDataJson_2026_07_31_00_00_00.d.ts.map +1 -0
  14. package/lib/cjs/migrations/UserSessionDataJson_2026_07_31_00_00_00.js +144 -0
  15. package/lib/cjs/migrations/UserSessionDataJson_2026_07_31_00_00_00.js.map +1 -0
  16. package/lib/cjs/migrations/UserSessionTimestamps_2026_07_31_00_00_01.d.ts +68 -0
  17. package/lib/cjs/migrations/UserSessionTimestamps_2026_07_31_00_00_01.d.ts.map +1 -0
  18. package/lib/cjs/migrations/UserSessionTimestamps_2026_07_31_00_00_01.js +95 -0
  19. package/lib/cjs/migrations/UserSessionTimestamps_2026_07_31_00_00_01.js.map +1 -0
  20. package/lib/cjs/models/DbSession.d.ts +15 -0
  21. package/lib/cjs/models/DbSession.d.ts.map +1 -1
  22. package/lib/cjs/models/DbSession.js.map +1 -1
  23. package/lib/mjs/index.d.ts +2 -0
  24. package/lib/mjs/index.d.ts.map +1 -1
  25. package/lib/mjs/index.js +33 -1
  26. package/lib/mjs/index.js.map +1 -1
  27. package/lib/mjs/migration-support.d.ts +43 -0
  28. package/lib/mjs/migration-support.d.ts.map +1 -0
  29. package/lib/mjs/migration-support.js +91 -0
  30. package/lib/mjs/migration-support.js.map +1 -0
  31. package/lib/mjs/migrations/UserSessionDBSqlMigration_2022_06_28_01_20_00.d.ts.map +1 -1
  32. package/lib/mjs/migrations/UserSessionDBSqlMigration_2022_06_28_01_20_00.js +8 -0
  33. package/lib/mjs/migrations/UserSessionDBSqlMigration_2022_06_28_01_20_00.js.map +1 -1
  34. package/lib/mjs/migrations/UserSessionDataJson_2026_07_31_00_00_00.d.ts +101 -0
  35. package/lib/mjs/migrations/UserSessionDataJson_2026_07_31_00_00_00.d.ts.map +1 -0
  36. package/lib/mjs/migrations/UserSessionDataJson_2026_07_31_00_00_00.js +141 -0
  37. package/lib/mjs/migrations/UserSessionDataJson_2026_07_31_00_00_00.js.map +1 -0
  38. package/lib/mjs/migrations/UserSessionTimestamps_2026_07_31_00_00_01.d.ts +68 -0
  39. package/lib/mjs/migrations/UserSessionTimestamps_2026_07_31_00_00_01.d.ts.map +1 -0
  40. package/lib/mjs/migrations/UserSessionTimestamps_2026_07_31_00_00_01.js +92 -0
  41. package/lib/mjs/migrations/UserSessionTimestamps_2026_07_31_00_00_01.js.map +1 -0
  42. package/lib/mjs/models/DbSession.d.ts +15 -0
  43. package/lib/mjs/models/DbSession.d.ts.map +1 -1
  44. package/lib/mjs/models/DbSession.js.map +1 -1
  45. package/lib/tsconfig.cjs.tsbuildinfo +1 -1
  46. package/lib/tsconfig.mjs.tsbuildinfo +1 -1
  47. package/package.json +10 -10
@@ -0,0 +1,95 @@
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.UserSessionTimestamps_2026_07_31_00_00_01 = void 0;
10
+ const orm_1 = require("@spinajs/orm");
11
+ const migration_support_js_1 = require("../migration-support.js");
12
+ /**
13
+ * Widens `user_sessions.CreatedAt` / `Expiration` from `date` to `datetime`.
14
+ *
15
+ * !! This is a correctness fix in its own right and has nothing to do with the
16
+ * JSON conversion it used to be bundled with. It is a separate migration
17
+ * precisely so it can be applied, skipped, reviewed and reverted on its own.
18
+ *
19
+ * The bug
20
+ * -------
21
+ * A MySQL `date` column stores only the calendar day - MySQL truncates the time
22
+ * component on write, silently:
23
+ *
24
+ * INSERT INTO probe VALUES ('2026-07-31 23:45:12', '2026-07-31 23:45:12');
25
+ * asDate asDateTime
26
+ * 2026-07-31 2026-07-31 23:45:12
27
+ *
28
+ * So on a deployed database a session's `Expiration` has no time of day: a
29
+ * session created at 23:45 with a 60-minute sliding TTL, due to expire at
30
+ * 00:45:12, is stored as expiring at 00:00:00 and the cleanup sweep kills it
31
+ * ~45 minutes early. Sliding expiration, `SessionProvider.isExpired`,
32
+ * `cleanupExpired` and `touch` all operate on that truncated value. `DbSession`
33
+ * declares both fields as luxon `DateTime` and the create path has declared both
34
+ * as `dateTime` since 2024; `datetime` is the type the code has always assumed
35
+ * it had.
36
+ *
37
+ * Why this one is safe where the JSON one was not
38
+ * -----------------------------------------------
39
+ * `date -> datetime` is a WIDENING cast. Every existing value is representable
40
+ * in the target type (midnight of the stored day), so unlike the `text -> json`
41
+ * conversion it cannot fail on row content and needs no wipe. Nothing is
42
+ * deleted here.
43
+ *
44
+ * Guards mirror the JSON migration: skip when the table is absent, skip unless
45
+ * `CreatedAt` is currently exactly `date`, skip on any non-MySQL dialect
46
+ * (`MODIFY` is MySQL syntax; a failed migration at boot bricks every later
47
+ * migration on the connection through `assertNoFailed()`). `CreatedAt` alone is
48
+ * probed - the two columns were created by the same statement in every database
49
+ * that has the old shape, and a `date`/`datetime` split between them has never
50
+ * existed.
51
+ *
52
+ * ONE statement with two `MODIFY` clauses, via the raw escape hatch, rather than
53
+ * `alterTable()`: the builder compiles one statement per column and
54
+ * `SqlDriver.execute` runs them through `Promise.all`, i.e. two concurrent
55
+ * `ALTER TABLE`s on the same table from two pooled connections.
56
+ *
57
+ * Nullability is restated verbatim from the create path - `MODIFY` replaces the
58
+ * whole column definition, and `CreatedAt` is `notNull()` there while
59
+ * `Expiration` is nullable (a never-expiring session persists a NULL).
60
+ */
61
+ let UserSessionTimestamps_2026_07_31_00_00_01 = class UserSessionTimestamps_2026_07_31_00_00_01 extends orm_1.OrmMigration {
62
+ async up(connection) {
63
+ if (!(await connection.schema().tableExists('user_sessions'))) {
64
+ return;
65
+ }
66
+ const createdAtType = await (0, migration_support_js_1.columnNativeType)(connection, 'user_sessions', 'CreatedAt');
67
+ // Only the one known-bad state is converted. `datetime` is the target,
68
+ // `timestamp` needs no help, `null` means the probe could not read the
69
+ // table, and anything else is a shape this migration was not written for.
70
+ if (createdAtType !== 'date' || !(0, migration_support_js_1.isMySqlDialect)(connection)) {
71
+ return;
72
+ }
73
+ await connection.schema().raw('ALTER TABLE `user_sessions` MODIFY `CreatedAt` DATETIME NOT NULL, MODIFY `Expiration` DATETIME');
74
+ }
75
+ /**
76
+ * Deliberately a no-op, and this is a decision rather than an omission.
77
+ *
78
+ * `up()` widened `date -> datetime`. Reversing it would be a NARROWING cast
79
+ * that silently truncates the time-of-day off every stored row - it destroys
80
+ * data, and it re-introduces the exact expiry bug this migration exists to
81
+ * fix, on a schema whose model and create path have declared `dateTime` for
82
+ * years. A `datetime` column is also perfectly readable by every version of
83
+ * this package, so a rollback has nothing to gain from shrinking it.
84
+ *
85
+ * (The JSON migration's `down()` is implemented, because there the old readers
86
+ * genuinely cannot cope with the new column type. That is not the case here.)
87
+ */
88
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
89
+ async down(_connection) { }
90
+ };
91
+ exports.UserSessionTimestamps_2026_07_31_00_00_01 = UserSessionTimestamps_2026_07_31_00_00_01;
92
+ exports.UserSessionTimestamps_2026_07_31_00_00_01 = UserSessionTimestamps_2026_07_31_00_00_01 = __decorate([
93
+ (0, orm_1.Migration)('session-provider-connection')
94
+ ], UserSessionTimestamps_2026_07_31_00_00_01);
95
+ //# sourceMappingURL=UserSessionTimestamps_2026_07_31_00_00_01.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserSessionTimestamps_2026_07_31_00_00_01.js","sourceRoot":"","sources":["../../../src/migrations/UserSessionTimestamps_2026_07_31_00_00_01.ts"],"names":[],"mappings":";;;;;;;;;AAAA,sCAAkE;AAClE,kEAA2E;AAE3E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAEI,IAAM,yCAAyC,GAA/C,MAAM,yCAA0C,SAAQ,kBAAY;IAClE,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,aAAa,GAAG,MAAM,IAAA,uCAAgB,EAAC,UAAU,EAAE,eAAe,EAAE,WAAW,CAAC,CAAC;QAEvF,uEAAuE;QACvE,uEAAuE;QACvE,0EAA0E;QAC1E,IAAI,aAAa,KAAK,MAAM,IAAI,CAAC,IAAA,qCAAc,EAAC,UAAU,CAAC,EAAE,CAAC;YAC5D,OAAO;QACT,CAAC;QAED,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,gGAAgG,CAAC,CAAC;IAClI,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,gEAAgE;IACzD,KAAK,CAAC,IAAI,CAAC,WAAsB,IAAkB,CAAC;CAC5D,CAAA;AAjCY,8FAAyC;oDAAzC,yCAAyC;IADrD,IAAA,eAAS,EAAC,6BAA6B,CAAC;GAC5B,yCAAyC,CAiCrD"}
@@ -4,6 +4,21 @@ export declare class DbSession extends ModelBase {
4
4
  SessionId: string;
5
5
  CreatedAt: DateTime;
6
6
  Expiration: DateTime;
7
+ /**
8
+ * Serialized session payload - see `encodeSessionData` / `decodeSessionData`.
9
+ *
10
+ * Declared as a string because that is what the WRITE path assigns:
11
+ * `encodeSessionData` produces JSON text and a MySQL `json` column stores it
12
+ * verbatim. What comes BACK depends on the driver - mysql2 parses a json
13
+ * column into an object, sqlite returns the text - so the read path accepts
14
+ * both. The declared type is deliberately not widened to `string | object`:
15
+ * this model is exported, the object shape never escapes the provider, and
16
+ * widening would force every consumer to narrow for a case it cannot observe.
17
+ *
18
+ * No `@Json()` decorator on purpose: the ORM's JsonValueConverter would
19
+ * `JSON.stringify` the already-encoded string on write and double-encode
20
+ * every session.
21
+ */
7
22
  Data: string;
8
23
  UserId: number;
9
24
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DbSession.d.ts","sourceRoot":"","sources":["../../../src/models/DbSession.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAyD,MAAM,cAAc,CAAC;AAChG,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,qBAEa,SAAU,SAAQ,SAAS;IAE/B,SAAS,EAAE,MAAM,CAAC;IAGlB,SAAS,EAAE,QAAQ,CAAC;IAGpB,UAAU,EAAE,QAAQ,CAAC;IAErB,IAAI,EAAE,MAAM,CAAC;IAEb,MAAM,EAAE,MAAM,CAAC;CACvB"}
1
+ {"version":3,"file":"DbSession.d.ts","sourceRoot":"","sources":["../../../src/models/DbSession.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAyD,MAAM,cAAc,CAAC;AAChG,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,qBAEa,SAAU,SAAQ,SAAS;IAE/B,SAAS,EAAE,MAAM,CAAC;IAGlB,SAAS,EAAE,QAAQ,CAAC;IAGpB,UAAU,EAAE,QAAQ,CAAC;IAE5B;;;;;;;;;;;;;;OAcG;IACI,IAAI,EAAE,MAAM,CAAC;IAEb,MAAM,EAAE,MAAM,CAAC;CACvB"}
@@ -1 +1 @@
1
- {"version":3,"file":"DbSession.js","sourceRoot":"","sources":["../../../src/models/DbSession.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,sCAAgG;AAChG,iCAAiC;AAG1B,IAAM,SAAS,GAAf,MAAM,SAAU,SAAQ,eAAS;CAavC,CAAA;AAbY,8BAAS;AAEb;IADN,IAAA,aAAO,GAAE;;4CACe;AAGlB;IADN,IAAA,eAAS,GAAE;8BACM,gBAAQ;4CAAC;AAGpB;IADN,IAAA,cAAE,GAAE;8BACc,gBAAQ;6CAAC;oBARjB,SAAS;IAFrB,IAAA,gBAAU,EAAC,6BAA6B,CAAC;IACzC,IAAA,WAAK,EAAC,eAAe,CAAC;GACV,SAAS,CAarB"}
1
+ {"version":3,"file":"DbSession.js","sourceRoot":"","sources":["../../../src/models/DbSession.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,sCAAgG;AAChG,iCAAiC;AAG1B,IAAM,SAAS,GAAf,MAAM,SAAU,SAAQ,eAAS;CA4BvC,CAAA;AA5BY,8BAAS;AAEb;IADN,IAAA,aAAO,GAAE;;4CACe;AAGlB;IADN,IAAA,eAAS,GAAE;8BACM,gBAAQ;4CAAC;AAGpB;IADN,IAAA,cAAE,GAAE;8BACc,gBAAQ;6CAAC;oBARjB,SAAS;IAFrB,IAAA,gBAAU,EAAC,6BAA6B,CAAC;IACzC,IAAA,WAAK,EAAC,eAAe,CAAC;GACV,SAAS,CA4BrB"}
@@ -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
@@ -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;AAE9E;;;;;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"}
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/mjs/index.js CHANGED
@@ -16,6 +16,8 @@ import { Config } from '@spinajs/configuration';
16
16
  import { DateTime } from 'luxon';
17
17
  export * from './models/DbSession.js';
18
18
  export * from './migrations/UserSessionDBSqlMigration_2022_06_28_01_20_00.js';
19
+ export * from './migrations/UserSessionDataJson_2026_07_31_00_00_00.js';
20
+ export * from './migrations/UserSessionTimestamps_2026_07_31_00_00_01.js';
19
21
  /**
20
22
  * Relational-db backed session store. Conforms to the `@spinajs/rbac`
21
23
  * `SessionProvider` contract: ownership is the numeric `UserId`, expiration is
@@ -121,7 +123,7 @@ let DbSessionStore = class DbSessionStore extends SessionProvider {
121
123
  UserId: row.UserId,
122
124
  Creation: row.CreatedAt,
123
125
  Expiration: row.Expiration ?? undefined,
124
- Data: decodeSessionData(row.Data),
126
+ Data: decodeSessionData(sessionDataFromColumn(row.Data)),
125
127
  });
126
128
  }
127
129
  expirationEquals(a, b) {
@@ -145,4 +147,34 @@ DbSessionStore = __decorate([
145
147
  Injectable(SessionProvider)
146
148
  ], DbSessionStore);
147
149
  export { DbSessionStore };
150
+ /**
151
+ * Presents whatever the driver hands back for `user_sessions.Data` in one of the
152
+ * two shapes {@link decodeSessionData} understands - a JSON string, or an
153
+ * already-parsed object graph.
154
+ *
155
+ * `Data` is a MySQL `json` column, so mysql2 parses it for us and this function
156
+ * passes the OBJECT straight through: no `JSON.stringify` / `JSON.parse`
157
+ * round-trip, the decoder walks the graph natively. Strings pass through just as
158
+ * untouched - sqlite has no json type and returns the stored text, and a
159
+ * connection configured otherwise (`typeCast`, an older column, a text-typed
160
+ * table that has not run the converging migration yet) may still yield one.
161
+ *
162
+ * The only value actually converted is a `Buffer`: a blob-ish column or a driver
163
+ * in binary mode delivers one, and neither the object walk nor `JSON.parse` can
164
+ * read it. Note it is decoded to text rather than handed over as an object -
165
+ * treating a Buffer as an object graph would walk its byte indices and decode to
166
+ * an empty session.
167
+ *
168
+ * The write path is untouched: `encodeSessionData` still stores a string, which
169
+ * a `json` column accepts verbatim (mysql2 does NOT auto-serialize objects into
170
+ * json columns, and `DbSession.Data` deliberately carries no `@Json()`
171
+ * decorator - the ORM's JsonValueConverter would `JSON.stringify` the already
172
+ * encoded string and double-encode every session).
173
+ */
174
+ function sessionDataFromColumn(data) {
175
+ if (Buffer.isBuffer(data)) {
176
+ return data.toString('utf8');
177
+ }
178
+ return data;
179
+ }
148
180
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -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,91 @@
1
+ /**
2
+ * Shared probes for the converging migrations in `./migrations`.
3
+ *
4
+ * Lives OUTSIDE `src/migrations` on purpose: `MigrationSource` globs that
5
+ * directory and imports every `.js` it finds, and a module that is not a
6
+ * migration has no business being pulled into that scan.
7
+ */
8
+ /**
9
+ * Driver names (the `@Injectable(...)` keys the mysql package registers, which
10
+ * is what `db.Connections[n].Driver` carries) whose dialect understands
11
+ * `ALTER TABLE ... MODIFY <col> <type>` and has a native `JSON` column type.
12
+ *
13
+ * This gate is not decoration. `MODIFY` is MySQL/MariaDB syntax - MSSQL spells
14
+ * it `ALTER COLUMN` and has no `JSON` type at all, sqlite has neither - so
15
+ * emitting the statement anywhere else is a syntax error, i.e. a FAILED
16
+ * migration, i.e. `assertNoFailed()` blocking every future migration on that
17
+ * connection. Every other dialect stores the payload as text and the read path
18
+ * (`decodeSessionData`) already handles the string shape, so skipping there is
19
+ * not a compromise: there is genuinely nothing to converge.
20
+ */
21
+ const MYSQL_DRIVERS = ['orm-driver-mysql', 'orm-driver-mysql-ssh'];
22
+ /**
23
+ * The text-family native types a legacy `user_sessions.Data` can carry. A
24
+ * column of any of these is the state the JSON conversion exists to fix;
25
+ * anything else (`json` on a converged or fresh database, or something nobody
26
+ * anticipated) is left alone.
27
+ */
28
+ const TEXT_FAMILY = ['text', 'tinytext', 'mediumtext', 'longtext', 'varchar', 'nvarchar'];
29
+ /**
30
+ * True when the connection speaks MySQL's DDL dialect. See {@link MYSQL_DRIVERS}.
31
+ *
32
+ * @param connection - the migration's connection
33
+ */
34
+ export function isMySqlDialect(connection) {
35
+ return MYSQL_DRIVERS.includes(connection.Options?.Driver);
36
+ }
37
+ /**
38
+ * Reduces a driver-reported `NativeType` to its bare type name, lower-cased.
39
+ *
40
+ * `IColumnDescriptor.NativeType` is documented as the FULL database type - MySQL
41
+ * reports `information_schema.DATA_TYPE` (already bare: `varchar`, `text`,
42
+ * `json`, `date`), but sqlite reports the declared type verbatim, so
43
+ * `VARCHAR(36)` and `INT(10) UNSIGNED` both occur. Comparing raw strings would
44
+ * therefore miss exactly the databases the probe exists to recognise.
45
+ *
46
+ * @param nativeType - whatever the driver reported
47
+ */
48
+ export function normalizeNativeType(nativeType) {
49
+ return String(nativeType).toLowerCase().split('(')[0].trim().split(/\s+/)[0];
50
+ }
51
+ /**
52
+ * True when the (normalized) type is one of the text-family types listed in
53
+ * {@link TEXT_FAMILY}.
54
+ *
55
+ * @param nativeType - a type name, normalized or not
56
+ */
57
+ export function isTextFamily(nativeType) {
58
+ return TEXT_FAMILY.includes(normalizeNativeType(nativeType));
59
+ }
60
+ /**
61
+ * Normalized native type of one column, or `null` when it cannot be established.
62
+ *
63
+ * `null` means "do not touch anything": the table is not described, the column
64
+ * is absent, or the driver's `tableInfo()` refused/failed. A migration that
65
+ * cannot see the current state must SKIP, never guess - a throw here would be
66
+ * recorded as a failed migration, and `assertNoFailed()` then blocks every
67
+ * subsequent migration on that connection until somebody edits the tracking
68
+ * table by hand. On a service that migrates at boot that is an outage, and the
69
+ * thing being probed is a type conversion nobody is waiting for.
70
+ *
71
+ * @param connection - the migration's connection
72
+ * @param table - table name
73
+ * @param column - column name, compared case-insensitively
74
+ */
75
+ export async function columnNativeType(connection, table, column) {
76
+ let columns;
77
+ try {
78
+ columns = await connection.tableInfo(table);
79
+ }
80
+ catch {
81
+ // e.g. the mysql driver throws when the connection carries no Database, and
82
+ // a driver may not implement tableInfo usefully at all.
83
+ return null;
84
+ }
85
+ const found = columns?.find((c) => c.Name?.toLowerCase() === column.toLowerCase());
86
+ if (!found?.NativeType) {
87
+ return null;
88
+ }
89
+ return normalizeNativeType(found.NativeType);
90
+ }
91
+ //# sourceMappingURL=migration-support.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration-support.js","sourceRoot":"","sources":["../../src/migration-support.ts"],"names":[],"mappings":"AAEA;;;;;;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,MAAM,UAAU,cAAc,CAAC,UAAqB;IAClD,OAAO,aAAa,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;AAC5D,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,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,MAAM,UAAU,YAAY,CAAC,UAAkB;IAC7C,OAAO,WAAW,CAAC,QAAQ,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,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;IAsBxC,IAAI,CAAC,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAC/C"}
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"}
@@ -38,6 +38,14 @@ let UserSessionDBSqlMigration_2022_06_28_01_20_00 = class UserSessionDBSqlMigrat
38
38
  table.string('SessionId', 36).primaryKey().notNull();
39
39
  table.dateTime('CreatedAt').notNull();
40
40
  table.dateTime('Expiration');
41
+ // JSON is the intended type, so a fresh install gets it straight away and never needs the
42
+ // converging migration (`UserSessionDataJson_2026_07_31_00_00_00`) to do any work. Deployed
43
+ // databases created before this migration existed carry a text-family column - the guard
44
+ // above means the type declared here has never run against them - and that migration is what
45
+ // brings them here.
46
+ //
47
+ // The read path consumes both shapes on purpose: mysql2 returns a json column as a parsed
48
+ // object, sqlite (which has no json type) returns the stored text. See `decodeSessionData`.
41
49
  table.json('Data').notNull();
42
50
  table.int('UserId').notNull();
43
51
  table.foreignKey('UserId').references('users', 'Id').onDelete(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,OAAO,EAAE,SAAS,EAAE,YAAY,EAAa,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAErF;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEI,IAAM,6CAA6C,GAAnD,MAAM,6CAA8C,SAAQ,YAAY;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,iBAAiB,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;AAxBY,6CAA6C;IADzD,SAAS,CAAC,6BAA6B,CAAC;GAC5B,6CAA6C,CAwBzD"}
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,OAAO,EAAE,SAAS,EAAE,YAAY,EAAa,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAErF;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEI,IAAM,6CAA6C,GAAnD,MAAM,6CAA8C,SAAQ,YAAY;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,iBAAiB,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,6CAA6C;IADzD,SAAS,CAAC,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"}