@morlay/session-rdb 0.0.15 → 0.0.16-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -13
- package/dist/artifact.d.mts +6 -16
- package/dist/artifact.mjs +3 -3
- package/dist/{import-DFed8DBt.mjs → import-CKrzjXVB.mjs} +73 -83
- package/dist/import.d.mts +2 -3
- package/dist/import.mjs +2 -2
- package/dist/index-CgGDHQSK.d.mts +225 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +694 -169
- package/dist/log-DTJsxMud.mjs +314 -0
- package/dist/{schema-CFXZURX7.d.mts → schema-COK7-wRV.d.mts} +3 -15
- package/dist/sqlite-DCy4VTD8.mjs +698 -0
- package/dist/storage.d.mts +2 -7
- package/dist/storage.mjs +2 -3
- package/dist/testing.d.mts +30 -1
- package/dist/testing.mjs +676 -1991
- package/drizzle/postgres/20260908072805_v2_initial/migration.sql +58 -0
- package/drizzle/postgres/20260908072805_v2_initial/snapshot.json +686 -0
- package/drizzle/postgres/20260908072806_v3_drop_original_seq/migration.sql +1 -0
- package/drizzle/postgres/20260908072806_v3_drop_original_seq/snapshot.json +673 -0
- package/drizzle/sqlite/20260908072751_v2_initial/migration.sql +53 -0
- package/drizzle/sqlite/20260908072751_v2_initial/snapshot.json +492 -0
- package/drizzle/sqlite/20260908072752_v3_drop_original_seq/migration.sql +6 -0
- package/drizzle/sqlite/20260908072752_v3_drop_original_seq/snapshot.json +513 -0
- package/package.json +17 -12
- package/src/adapters/index.ts +10 -2
- package/src/adapters/to-postgres.ts +19 -15
- package/src/adapters/to-sqlite.ts +20 -14
- package/src/{entities → adapters}/types.ts +7 -8
- package/src/backend.ts +0 -7
- package/src/branch.ts +29 -110
- package/src/drizzle/postgres-v2.ts +11 -0
- package/src/drizzle/postgres-v3.ts +11 -0
- package/src/drizzle/sqlite-v2.ts +10 -0
- package/src/drizzle/sqlite-v3.ts +11 -0
- package/src/entities/index.ts +2 -30
- package/src/entities/v2/index.ts +20 -0
- package/src/entities/v2/session-events.ts +11 -0
- package/src/entities/v3/events.ts +27 -0
- package/src/entities/v3/index.ts +27 -0
- package/src/entities/v3/persistence-state.ts +10 -0
- package/src/entities/v3/schema-meta.ts +9 -0
- package/src/entities/v3/session-events.ts +26 -0
- package/src/entities/v3/sessions.ts +20 -0
- package/src/import.ts +65 -57
- package/src/index.ts +868 -223
- package/src/invariant.ts +0 -2
- package/src/legacy.ts +67 -0
- package/src/log.ts +41 -87
- package/src/postgres.ts +75 -93
- package/src/schema.ts +3 -14
- package/src/sqlite.ts +59 -46
- package/src/testing/contract.ts +460 -375
- package/src/testing/coordinator-contract.ts +108 -1374
- package/src/testing.ts +1 -6
- package/dist/index-uUvn6gYp.d.mts +0 -111
- package/dist/schema-vwzwEXNE.mjs +0 -878
- package/dist/sqlite-CG_Qcx5C.mjs +0 -356
- package/src/adapters/ddl.ts +0 -77
- package/src/entities/events.ts +0 -27
- package/src/entities/persistence-state.ts +0 -10
- package/src/entities/schema-meta.ts +0 -9
- package/src/entities/session-events.ts +0 -29
- package/src/entities/sessions.ts +0 -20
- package/src/migrate.ts +0 -137
|
@@ -10,24 +10,26 @@ import {
|
|
|
10
10
|
type AnySQLiteTable,
|
|
11
11
|
type SQLiteColumnBuilder,
|
|
12
12
|
} from "drizzle-orm/sqlite-core";
|
|
13
|
-
import { toProperty, type ColumnDef, type TableDef } from "
|
|
13
|
+
import { toProperty, type ColumnDef, type TableDef } from "./types.ts";
|
|
14
14
|
|
|
15
15
|
type TableRegistry = Record<string, AnySQLiteTable>;
|
|
16
16
|
|
|
17
|
-
function buildColumn(c: ColumnDef, tables: TableRegistry): SQLiteColumnBuilder {
|
|
17
|
+
function buildColumn(name: string, c: ColumnDef, tables: TableRegistry): SQLiteColumnBuilder {
|
|
18
18
|
// 具体 builder 类型与基类在方法层面不兼容(泛型逆变),构建阶段用宽松
|
|
19
19
|
// 类型合并,返回时收窄为基类;查询类型安全由手写行接口兜底。
|
|
20
20
|
let col: any;
|
|
21
21
|
switch (c.type) {
|
|
22
|
-
case "text":
|
|
23
|
-
|
|
22
|
+
case "text": {
|
|
23
|
+
const built = text(name);
|
|
24
|
+
col = c.primaryKey ? built.primaryKey() : built;
|
|
24
25
|
break;
|
|
26
|
+
}
|
|
25
27
|
case "serial":
|
|
26
|
-
col = integer(
|
|
28
|
+
col = integer(name).primaryKey({ autoIncrement: true });
|
|
27
29
|
break;
|
|
28
30
|
case "integer":
|
|
29
31
|
case "bigint": {
|
|
30
|
-
const built = integer(
|
|
32
|
+
const built = integer(name);
|
|
31
33
|
col = c.primaryKey ? built.primaryKey() : built;
|
|
32
34
|
break;
|
|
33
35
|
}
|
|
@@ -49,20 +51,24 @@ export function toSqliteSchema(defs: readonly TableDef[]): Record<string, AnySQL
|
|
|
49
51
|
const tables: TableRegistry = {};
|
|
50
52
|
for (const def of defs) {
|
|
51
53
|
const columns: Record<string, SQLiteColumnBuilder> = {};
|
|
52
|
-
for (const c of def.columns)
|
|
54
|
+
for (const [name, c] of Object.entries(def.columns)) {
|
|
55
|
+
columns[toProperty(name)] = buildColumn(name, c, tables);
|
|
56
|
+
}
|
|
53
57
|
const extra = (self: Record<string, unknown>) => [
|
|
54
|
-
...(def.checks ??
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
+
...Object.entries(def.checks ?? {}).map(([name, c]) =>
|
|
59
|
+
sqliteCheck(name, sql.raw(c.expression)),
|
|
60
|
+
),
|
|
61
|
+
...Object.entries(def.uniques ?? {}).map(([name, u]) =>
|
|
62
|
+
unique(name).on(
|
|
63
|
+
...(u.columns.map((n) => self[toProperty(n)] as AnySQLiteColumn) as [
|
|
58
64
|
AnySQLiteColumn,
|
|
59
65
|
...AnySQLiteColumn[],
|
|
60
66
|
]),
|
|
61
67
|
),
|
|
62
68
|
),
|
|
63
|
-
...(def.indexes ??
|
|
64
|
-
index(
|
|
65
|
-
...(i.columns.map((
|
|
69
|
+
...Object.entries(def.indexes ?? {}).map(([name, i]) =>
|
|
70
|
+
index(name).on(
|
|
71
|
+
...(i.columns.map((n) => self[toProperty(n)] as AnySQLiteColumn) as [
|
|
66
72
|
AnySQLiteColumn,
|
|
67
73
|
...AnySQLiteColumn[],
|
|
68
74
|
]),
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
/** drizzle util 层:方言无关的表结构描述(entities 与 adapters 共用)。 */
|
|
2
|
+
|
|
1
3
|
export type ColumnTypeName = "serial" | "integer" | "bigint" | "text";
|
|
2
4
|
|
|
3
5
|
export type DeleteAction = "cascade" | "set null" | "restrict" | "no action";
|
|
4
6
|
|
|
5
7
|
export interface ColumnDef {
|
|
6
|
-
name: string;
|
|
7
8
|
type: ColumnTypeName;
|
|
8
9
|
notNull?: boolean;
|
|
9
10
|
|
|
@@ -21,26 +22,24 @@ export interface ColumnDef {
|
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export interface CheckDef {
|
|
24
|
-
name: string;
|
|
25
25
|
expression: string;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export interface UniqueDef {
|
|
29
|
-
name?: string;
|
|
30
29
|
columns: string[];
|
|
31
30
|
}
|
|
32
31
|
|
|
33
32
|
export interface IndexDef {
|
|
34
|
-
name: string;
|
|
35
33
|
columns: string[];
|
|
36
34
|
}
|
|
37
35
|
|
|
36
|
+
/** 表结构描述:columns / checks / uniques / indexes 以 Record 键为名,天然唯一。 */
|
|
38
37
|
export interface TableDef {
|
|
39
38
|
name: string;
|
|
40
|
-
columns: ColumnDef
|
|
41
|
-
checks?: CheckDef
|
|
42
|
-
uniques?: UniqueDef
|
|
43
|
-
indexes?: IndexDef
|
|
39
|
+
columns: Record<string, ColumnDef>;
|
|
40
|
+
checks?: Record<string, CheckDef>;
|
|
41
|
+
uniques?: Record<string, UniqueDef>;
|
|
42
|
+
indexes?: Record<string, IndexDef>;
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
export function toProperty(name: string): string {
|
package/src/backend.ts
CHANGED
|
@@ -37,8 +37,6 @@ export interface EventRow {
|
|
|
37
37
|
|
|
38
38
|
fSequence: number;
|
|
39
39
|
|
|
40
|
-
fOriginalSeq: number;
|
|
41
|
-
|
|
42
40
|
fType: string;
|
|
43
41
|
|
|
44
42
|
fKind: string;
|
|
@@ -72,7 +70,6 @@ export interface BackendTx {
|
|
|
72
70
|
fSessionId: SessionId;
|
|
73
71
|
fEventId: string;
|
|
74
72
|
fSequence: number;
|
|
75
|
-
fOriginalSeq: number;
|
|
76
73
|
fSurfaceOp: string | null;
|
|
77
74
|
}>,
|
|
78
75
|
): Promise<void>;
|
|
@@ -87,8 +84,6 @@ export interface BackendTx {
|
|
|
87
84
|
id: SessionId,
|
|
88
85
|
sequence: number,
|
|
89
86
|
): Promise<{ fEventId: string; fSequence: number } | undefined>;
|
|
90
|
-
|
|
91
|
-
getLastBridge(id: SessionId): Promise<{ fEventId: string; fSequence: number } | undefined>;
|
|
92
87
|
}
|
|
93
88
|
|
|
94
89
|
export interface Backend {
|
|
@@ -100,8 +95,6 @@ export interface Backend {
|
|
|
100
95
|
|
|
101
96
|
getSession(id: SessionId): Promise<SessionRow | undefined>;
|
|
102
97
|
|
|
103
|
-
getSeqMapRows(id: SessionId): Promise<Array<{ fSequence: number; fOriginalSeq: number }>>;
|
|
104
|
-
|
|
105
98
|
getEventRows(id: SessionId, fromSequence?: number): Promise<EventRow[]>;
|
|
106
99
|
|
|
107
100
|
listSessions(): Promise<SessionRow[]>;
|
package/src/branch.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SESSION_FORMAT_VERSION,
|
|
3
|
+
SessionLogOffset,
|
|
3
4
|
type Session,
|
|
4
5
|
type SessionEvent,
|
|
5
6
|
type SessionHeader,
|
|
@@ -19,7 +20,6 @@ import {
|
|
|
19
20
|
import { randomUUID } from "node:crypto";
|
|
20
21
|
import type { SessionPersistenceRdb } from "./index.ts";
|
|
21
22
|
import { rowToMeta } from "./log.ts";
|
|
22
|
-
import { isPersistedEvent } from "./schema.ts";
|
|
23
23
|
|
|
24
24
|
export function locateTurnEnd(
|
|
25
25
|
events: readonly SessionEvent[],
|
|
@@ -66,12 +66,6 @@ export interface LiveSessionHooks {
|
|
|
66
66
|
getAgent(id: SessionId): LiveAgentLike | undefined;
|
|
67
67
|
|
|
68
68
|
flush(session: Session): Promise<boolean>;
|
|
69
|
-
|
|
70
|
-
setCoordinatorCursor(id: SessionId, cursor: number): void;
|
|
71
|
-
|
|
72
|
-
setCoordinatorState(id: SessionId, cursor: number, meta: SessionHeader): void;
|
|
73
|
-
|
|
74
|
-
setCoordinatorSeedLength(id: SessionId, seedLength: number): void;
|
|
75
69
|
}
|
|
76
70
|
|
|
77
71
|
export interface LiveAgentLike {
|
|
@@ -155,9 +149,6 @@ export class SessionBranchRdbProvider implements SessionBranchProvider {
|
|
|
155
149
|
getSession: () => undefined,
|
|
156
150
|
getAgent: () => undefined,
|
|
157
151
|
flush: async () => true,
|
|
158
|
-
setCoordinatorCursor: () => {},
|
|
159
|
-
setCoordinatorState: () => {},
|
|
160
|
-
setCoordinatorSeedLength: () => {},
|
|
161
152
|
},
|
|
162
153
|
) {}
|
|
163
154
|
|
|
@@ -176,7 +167,7 @@ export class SessionBranchRdbProvider implements SessionBranchProvider {
|
|
|
176
167
|
id: SessionId,
|
|
177
168
|
signal?: AbortSignal,
|
|
178
169
|
): Promise<{ meta: SessionHeader; events: readonly SessionEvent[] }> {
|
|
179
|
-
const stored = await this.persistence.
|
|
170
|
+
const stored = await this.persistence.readLog(id, {}, signal);
|
|
180
171
|
if (stored === undefined)
|
|
181
172
|
throw new SessionBranchError(`session "${id}" not found`, "SESSION_NOT_FOUND");
|
|
182
173
|
return { meta: stored.meta, events: stored.events };
|
|
@@ -189,7 +180,9 @@ export class SessionBranchRdbProvider implements SessionBranchProvider {
|
|
|
189
180
|
): Promise<SessionId> {
|
|
190
181
|
signal?.throwIfAborted();
|
|
191
182
|
const { atSeq, anchorMode = "after", seedSuffix = [], childSessionId, meta = {} } = options;
|
|
192
|
-
const source = await this.persistence.
|
|
183
|
+
const source = await this.persistence.readLog(sourceId, {}, signal);
|
|
184
|
+
if (source === undefined)
|
|
185
|
+
throw new SessionBranchError(`session "${sourceId}" not found`, "SESSION_NOT_FOUND");
|
|
193
186
|
const boundary = locateTurnEnd(source.events, atSeq, anchorMode);
|
|
194
187
|
const prefix = source.events.slice(0, boundary + 1);
|
|
195
188
|
const childId = childSessionId ?? mintSessionId();
|
|
@@ -213,20 +206,26 @@ export class SessionBranchRdbProvider implements SessionBranchProvider {
|
|
|
213
206
|
...(meta.delegationDepth !== undefined ? { delegationDepth: meta.delegationDepth } : {}),
|
|
214
207
|
};
|
|
215
208
|
const seed = [...renumber(prefix, 0), ...renumber(seedSuffix, prefix.length)];
|
|
216
|
-
//
|
|
209
|
+
// 事件行复用:前缀事件(稠密 seq → 源会话已存在事件行 id)注册到写路径,
|
|
217
210
|
// appendBatch 消费时复用事件行、不复制。seedSuffix 的 manualTurn 事件是
|
|
218
211
|
// 新事件(无源行),不注册。
|
|
219
212
|
const internals = this.persistence.internals();
|
|
220
213
|
const sourceRows = await internals.backend.getEventRows(sourceId);
|
|
221
|
-
const sourceEventIds = new Map(sourceRows.map((row) => [row.
|
|
214
|
+
const sourceEventIds = new Map(sourceRows.map((row) => [row.fSequence, row.fEventId]));
|
|
222
215
|
const reuse = new Map<number, string>();
|
|
223
216
|
for (const event of prefix) {
|
|
224
217
|
const eventId = sourceEventIds.get(event.seq);
|
|
225
218
|
if (eventId !== undefined) reuse.set(event.seq, eventId);
|
|
226
219
|
}
|
|
227
220
|
internals.registerReuseEventIds(childId, reuse);
|
|
228
|
-
await this.persistence.create(childMeta,
|
|
229
|
-
|
|
221
|
+
const handle = await this.persistence.create(childMeta, {
|
|
222
|
+
inheritedEventCount: SessionLogOffset(prefix.length),
|
|
223
|
+
});
|
|
224
|
+
try {
|
|
225
|
+
if (seed.length > 0) await handle.append(seed);
|
|
226
|
+
} finally {
|
|
227
|
+
await handle.close();
|
|
228
|
+
}
|
|
230
229
|
return childId;
|
|
231
230
|
}
|
|
232
231
|
|
|
@@ -245,11 +244,12 @@ export class SessionBranchRdbProvider implements SessionBranchProvider {
|
|
|
245
244
|
const live = this.live.getSession(id);
|
|
246
245
|
// live 会话先落盘 write-behind 缓冲,保证后续读取与后端事务同视图。
|
|
247
246
|
if (live !== undefined) await this.live.flush(live);
|
|
248
|
-
// 边界校验用原始事件(
|
|
247
|
+
// 边界校验用原始事件(readLog,不补 closers)——inspect 会给未闭合
|
|
249
248
|
// log 补合成 closers,把 user/message 边界掩盖成 turn/end,丢失 exclusive
|
|
250
249
|
// 语义。
|
|
251
250
|
const raw = live === undefined ? await this.readRawEvents(id, signal) : undefined;
|
|
252
|
-
const inspection =
|
|
251
|
+
const inspection =
|
|
252
|
+
live === undefined ? undefined : await this.persistence.internals().inspect(id, signal);
|
|
253
253
|
const events = live === undefined ? raw!.events : inspection!.events;
|
|
254
254
|
const meta = live === undefined ? raw!.meta : inspection!.meta;
|
|
255
255
|
const boundaryEvent = events[toBoundary];
|
|
@@ -273,12 +273,9 @@ export class SessionBranchRdbProvider implements SessionBranchProvider {
|
|
|
273
273
|
const keepLength = balanceRewindPrefix(events.slice(0, rawKeepLength)).length;
|
|
274
274
|
|
|
275
275
|
const internals = this.persistence.internals();
|
|
276
|
-
// live
|
|
277
|
-
//
|
|
278
|
-
const denseBoundary =
|
|
279
|
-
live === undefined
|
|
280
|
-
? keepLength - 1
|
|
281
|
-
: events.slice(0, keepLength).filter(isPersistedEvent).length - 1;
|
|
276
|
+
// 原样存储(无过滤):live 视图与 RDB head 同空间(上游 seq),
|
|
277
|
+
// 边界即保留前缀长度 - 1。
|
|
278
|
+
const denseBoundary = keepLength - 1;
|
|
282
279
|
const newSeedLength = await internals.backend.transaction(async (tx) => {
|
|
283
280
|
signal?.throwIfAborted();
|
|
284
281
|
const head = await tx.getHead(id);
|
|
@@ -308,17 +305,12 @@ export class SessionBranchRdbProvider implements SessionBranchProvider {
|
|
|
308
305
|
await tx.bumpRevision(id);
|
|
309
306
|
return shrunk;
|
|
310
307
|
});
|
|
311
|
-
// 同步 coordinator 的 storage.inheritedEventCount——DB 已收缩而内存不
|
|
312
|
-
// 收缩的话,下一次 append 的 upsert 会把旧值覆盖回去。
|
|
313
|
-
if (newSeedLength !== null) {
|
|
314
|
-
this.live.setCoordinatorSeedLength(id, newSeedLength);
|
|
315
|
-
}
|
|
316
308
|
|
|
317
309
|
// 更新确认 head(下一次 append 的并发校验基准),与 appendBatch 同语义。
|
|
318
310
|
internals.writeGuard.confirmHead(id, denseBoundary);
|
|
319
311
|
|
|
320
312
|
if (live !== undefined) {
|
|
321
|
-
// live 分支:截断内存 log 并重置派生缓存;同步
|
|
313
|
+
// live 分支:截断内存 log 并重置派生缓存;同步 handle cursor 与
|
|
322
314
|
// agent 轮次游标。不调用 load——live 时 load 会先 flush 把旧内存写回,
|
|
323
315
|
// 撤销本次截断。
|
|
324
316
|
truncateLiveSession(live, keepLength);
|
|
@@ -331,14 +323,12 @@ export class SessionBranchRdbProvider implements SessionBranchProvider {
|
|
|
331
323
|
const phase = (agent as unknown as { phase?: { lastTurn?: number } }).phase;
|
|
332
324
|
if (phase !== undefined) phase.lastTurn = lastTurn;
|
|
333
325
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
//
|
|
337
|
-
|
|
338
|
-
if (
|
|
339
|
-
|
|
340
|
-
} else {
|
|
341
|
-
await this.persistence.load(id);
|
|
326
|
+
// DB 已截断:同步 live write handle 的 cursor 与继承前缀,使下一次
|
|
327
|
+
// append 从截断后的位置续接。handle cursor 是**上游空间**(live 内存
|
|
328
|
+
// log 截断后的长度,与 drainBuffered 的过滤/contiguity 校验同空间)。
|
|
329
|
+
const handle = this.persistence.tracker.writerOf(id);
|
|
330
|
+
if (handle !== undefined) {
|
|
331
|
+
handle.resetAfterRewind(keepLength, newSeedLength === null ? undefined : newSeedLength);
|
|
342
332
|
}
|
|
343
333
|
}
|
|
344
334
|
|
|
@@ -385,57 +375,6 @@ export class SessionBranchRdb extends SessionBranch {
|
|
|
385
375
|
return agents?.get(id);
|
|
386
376
|
},
|
|
387
377
|
flush: (session) => this.ctx.sessions.flush(session),
|
|
388
|
-
setCoordinatorCursor: (id, cursor) => {
|
|
389
|
-
// states 是私有 Map;保留条目(owner 不能丢),仅对齐 cursor 到新尾部。
|
|
390
|
-
const persistence = this.ctx.sessionPersistence as unknown as {
|
|
391
|
-
coordinator?: {
|
|
392
|
-
states?: Map<SessionId, { cursor: number } | undefined>;
|
|
393
|
-
};
|
|
394
|
-
};
|
|
395
|
-
const state = persistence.coordinator?.states?.get(id);
|
|
396
|
-
if (state !== undefined) state.cursor = cursor;
|
|
397
|
-
},
|
|
398
|
-
setCoordinatorState: (id, cursor, meta) => {
|
|
399
|
-
// states 条目可能不存在(会话从未被 adopt);存在则仅对齐 cursor,
|
|
400
|
-
// 不存在则创建 ownerless 条目,使下一次 append 走标准路径而非 adopt
|
|
401
|
-
// (adopt 会经 prepareCore 补 closers 撤销截断)。
|
|
402
|
-
const persistence = this.ctx.sessionPersistence as unknown as {
|
|
403
|
-
coordinator?: {
|
|
404
|
-
states?: Map<
|
|
405
|
-
SessionId,
|
|
406
|
-
{ cursor: number; meta: SessionHeader; materialized: boolean } | undefined
|
|
407
|
-
>;
|
|
408
|
-
};
|
|
409
|
-
};
|
|
410
|
-
const states = persistence.coordinator?.states;
|
|
411
|
-
if (states === undefined) return;
|
|
412
|
-
const state = states.get(id);
|
|
413
|
-
if (state !== undefined) {
|
|
414
|
-
state.cursor = cursor;
|
|
415
|
-
} else {
|
|
416
|
-
states.set(id, { meta, cursor, materialized: true });
|
|
417
|
-
}
|
|
418
|
-
},
|
|
419
|
-
setCoordinatorSeedLength: (id, seedLength) => {
|
|
420
|
-
// 收缩 storage.inheritedEventCount,与 DB 事务内的收缩保持一致——
|
|
421
|
-
// 不同步的话下一次 append 的 upsert 会把旧值覆盖回去。storage 对象
|
|
422
|
-
// 可能被冻结,整体替换 state.storage 而非改字段。
|
|
423
|
-
const persistence = this.ctx.sessionPersistence as unknown as {
|
|
424
|
-
coordinator?: {
|
|
425
|
-
states?: Map<
|
|
426
|
-
SessionId,
|
|
427
|
-
| {
|
|
428
|
-
storage?: { meta: SessionHeader; inheritedEventCount: number };
|
|
429
|
-
}
|
|
430
|
-
| undefined
|
|
431
|
-
>;
|
|
432
|
-
};
|
|
433
|
-
};
|
|
434
|
-
const state = persistence.coordinator?.states?.get(id);
|
|
435
|
-
if (state?.storage !== undefined && state.storage.inheritedEventCount > seedLength) {
|
|
436
|
-
state.storage = { ...state.storage, inheritedEventCount: seedLength };
|
|
437
|
-
}
|
|
438
|
-
},
|
|
439
378
|
},
|
|
440
379
|
);
|
|
441
380
|
|
|
@@ -471,26 +410,6 @@ export class SessionBranchRdb extends SessionBranch {
|
|
|
471
410
|
return this.provider.rewind(id, toBoundary, signal);
|
|
472
411
|
}
|
|
473
412
|
|
|
474
|
-
syncLiveCursor(sessionId: SessionId): void {
|
|
475
|
-
const live = this.ctx.sessions.get(sessionId);
|
|
476
|
-
if (live === undefined) return;
|
|
477
|
-
const persistence = this.ctx.sessionPersistence as unknown as {
|
|
478
|
-
coordinator?: {
|
|
479
|
-
states?: Map<SessionId, { cursor: number } | undefined>;
|
|
480
|
-
};
|
|
481
|
-
};
|
|
482
|
-
const state = persistence.coordinator?.states?.get(sessionId);
|
|
483
|
-
if (state === undefined) return;
|
|
484
|
-
let cursor = state.cursor;
|
|
485
|
-
// ignorable 是下游信封扩展(上游 SessionEvent 无此字段),结构化读取。
|
|
486
|
-
while (
|
|
487
|
-
(live.snapshotEvents()[cursor] as (SessionEvent & { ignorable?: unknown }) | undefined)
|
|
488
|
-
?.ignorable === true
|
|
489
|
-
)
|
|
490
|
-
cursor += 1;
|
|
491
|
-
state.cursor = cursor;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
413
|
async timeline(sessionId: SessionId, signal?: AbortSignal) {
|
|
495
414
|
const persistence = this.ctx.sessionPersistence as SessionPersistenceRdb;
|
|
496
415
|
const snapshots = await persistence.listSnapshots(signal);
|
|
@@ -499,7 +418,7 @@ export class SessionBranchRdb extends SessionBranch {
|
|
|
499
418
|
const readOwnEvents = async (id: SessionId, fromSeq: number, s?: AbortSignal) => {
|
|
500
419
|
const live = this.ctx.sessions.get(id);
|
|
501
420
|
if (live !== undefined) return live.snapshotEvents().slice(fromSeq);
|
|
502
|
-
return (await persistence.readFrom(id, fromSeq, s)).events;
|
|
421
|
+
return (await persistence.internals().readFrom(id, fromSeq, s)).events;
|
|
503
422
|
};
|
|
504
423
|
return buildTimeline(snapshots, readOwnEvents, sessionId, signal);
|
|
505
424
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// drizzle-kit 入口:v2 快照(PG,具名导出表对象)。
|
|
2
|
+
import { toPostgresSchema } from "../adapters/to-postgres.ts";
|
|
3
|
+
import { postgresTableDefsV2 } from "../entities/v2/index.ts";
|
|
4
|
+
|
|
5
|
+
const tables = toPostgresSchema(postgresTableDefsV2);
|
|
6
|
+
|
|
7
|
+
export const tPersistenceState = tables["t_persistence_state"]!;
|
|
8
|
+
export const tSchemaMeta = tables["t_schema_meta"]!;
|
|
9
|
+
export const tSessions = tables["t_sessions"]!;
|
|
10
|
+
export const tEvents = tables["t_events"]!;
|
|
11
|
+
export const tSessionEvents = tables["t_session_events"]!;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// drizzle-kit 入口:v3 快照(PG,具名导出表对象)。
|
|
2
|
+
import { toPostgresSchema } from "../adapters/to-postgres.ts";
|
|
3
|
+
import { postgresTableDefs } from "../entities/v3/index.ts";
|
|
4
|
+
|
|
5
|
+
const tables = toPostgresSchema(postgresTableDefs);
|
|
6
|
+
|
|
7
|
+
export const tPersistenceState = tables["t_persistence_state"]!;
|
|
8
|
+
export const tSchemaMeta = tables["t_schema_meta"]!;
|
|
9
|
+
export const tSessions = tables["t_sessions"]!;
|
|
10
|
+
export const tEvents = tables["t_events"]!;
|
|
11
|
+
export const tSessionEvents = tables["t_session_events"]!;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// drizzle-kit 入口:v2 快照(具名导出表对象,供 generate 生成 v2 baseline)。
|
|
2
|
+
import { toSqliteSchema } from "../adapters/to-sqlite.ts";
|
|
3
|
+
import { sqliteTableDefsV2 } from "../entities/v2/index.ts";
|
|
4
|
+
|
|
5
|
+
const tables = toSqliteSchema(sqliteTableDefsV2);
|
|
6
|
+
|
|
7
|
+
export const tPersistenceState = tables["t_persistence_state"]!;
|
|
8
|
+
export const tSessions = tables["t_sessions"]!;
|
|
9
|
+
export const tEvents = tables["t_events"]!;
|
|
10
|
+
export const tSessionEvents = tables["t_session_events"]!;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// drizzle-kit 入口:v3 快照(具名导出表对象,供 generate 生成 v3 diff)。
|
|
2
|
+
import { toSqliteSchema } from "../adapters/to-sqlite.ts";
|
|
3
|
+
import { sqliteTableDefs } from "../entities/v3/index.ts";
|
|
4
|
+
|
|
5
|
+
const tables = toSqliteSchema(sqliteTableDefs);
|
|
6
|
+
|
|
7
|
+
export const tPersistenceState = tables["t_persistence_state"]!;
|
|
8
|
+
export const tSchemaMeta = tables["t_schema_meta"]!;
|
|
9
|
+
export const tSessions = tables["t_sessions"]!;
|
|
10
|
+
export const tEvents = tables["t_events"]!;
|
|
11
|
+
export const tSessionEvents = tables["t_session_events"]!;
|
package/src/entities/index.ts
CHANGED
|
@@ -1,30 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { sessions } from "./sessions.ts";
|
|
4
|
-
import { events } from "./events.ts";
|
|
5
|
-
import { sessionEvents } from "./session-events.ts";
|
|
6
|
-
|
|
7
|
-
export { persistenceState };
|
|
8
|
-
export { schemaMeta };
|
|
9
|
-
export { sessions };
|
|
10
|
-
export { events };
|
|
11
|
-
export { sessionEvents };
|
|
12
|
-
export type {
|
|
13
|
-
ColumnDef,
|
|
14
|
-
TableDef,
|
|
15
|
-
CheckDef,
|
|
16
|
-
UniqueDef,
|
|
17
|
-
IndexDef,
|
|
18
|
-
ColumnTypeName,
|
|
19
|
-
DeleteAction,
|
|
20
|
-
} from "./types.ts";
|
|
21
|
-
|
|
22
|
-
export const sqliteTableDefs = [persistenceState, sessions, events, sessionEvents] as const;
|
|
23
|
-
|
|
24
|
-
export const postgresTableDefs = [
|
|
25
|
-
persistenceState,
|
|
26
|
-
schemaMeta,
|
|
27
|
-
sessions,
|
|
28
|
-
events,
|
|
29
|
-
sessionEvents,
|
|
30
|
-
] as const;
|
|
1
|
+
export { sqliteTableDefs, postgresTableDefs } from "./v3/index.ts";
|
|
2
|
+
export { sqliteTableDefsV2, postgresTableDefsV2 } from "./v2/index.ts";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { persistenceState } from "../v3/persistence-state.ts";
|
|
2
|
+
import { schemaMeta } from "../v3/schema-meta.ts";
|
|
3
|
+
import { sessions } from "../v3/sessions.ts";
|
|
4
|
+
import { events } from "../v3/events.ts";
|
|
5
|
+
import { sessionEvents } from "./session-events.ts";
|
|
6
|
+
|
|
7
|
+
export { persistenceState };
|
|
8
|
+
export { schemaMeta };
|
|
9
|
+
export { sessions };
|
|
10
|
+
export { events };
|
|
11
|
+
export { sessionEvents };
|
|
12
|
+
|
|
13
|
+
export const sqliteTableDefsV2 = [persistenceState, sessions, events, sessionEvents] as const;
|
|
14
|
+
export const postgresTableDefsV2 = [
|
|
15
|
+
persistenceState,
|
|
16
|
+
schemaMeta,
|
|
17
|
+
sessions,
|
|
18
|
+
events,
|
|
19
|
+
sessionEvents,
|
|
20
|
+
] as const;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TableDef } from "../../adapters/types.ts";
|
|
2
|
+
import { sessionEvents as v3SessionEvents } from "../v3/session-events.ts";
|
|
3
|
+
|
|
4
|
+
/** v2 派生:v3 规格 + f_original_seq 列(原样存储前记录上游 seq 的遗留列)。 */
|
|
5
|
+
export const sessionEvents: TableDef = {
|
|
6
|
+
...v3SessionEvents,
|
|
7
|
+
columns: {
|
|
8
|
+
...v3SessionEvents.columns,
|
|
9
|
+
f_original_seq: { type: "integer", notNull: true },
|
|
10
|
+
},
|
|
11
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { TableDef } from "../../adapters/types.ts";
|
|
2
|
+
|
|
3
|
+
export const events: TableDef = {
|
|
4
|
+
name: "t_events",
|
|
5
|
+
columns: {
|
|
6
|
+
f_id: { type: "serial", primaryKey: true },
|
|
7
|
+
f_event_id: { type: "text", notNull: true, unique: true },
|
|
8
|
+
f_parent_id: { type: "text", notNull: true, default: "" },
|
|
9
|
+
f_type: { type: "text", notNull: true, default: "" },
|
|
10
|
+
f_kind: { type: "text", notNull: true, default: "" },
|
|
11
|
+
f_role: { type: "text", notNull: true, default: "" },
|
|
12
|
+
f_name: { type: "text", notNull: true, default: "" },
|
|
13
|
+
f_action_id: { type: "text", notNull: true, default: "" },
|
|
14
|
+
f_encoding: { type: "text", notNull: true, default: "" },
|
|
15
|
+
f_data: { type: "text", notNull: true },
|
|
16
|
+
f_created_at: { type: "bigint", notNull: true, default: 0 },
|
|
17
|
+
},
|
|
18
|
+
// 查询经 f_event_id(列级 UNIQUE 唯一索引)与 t_session_events 复合索引
|
|
19
|
+
// (按 session 过滤后回表);f_parent_id 仅写路径构造;维度列索引为
|
|
20
|
+
// 审计/UI 过滤预留。
|
|
21
|
+
indexes: {
|
|
22
|
+
idx_events_kind: { columns: ["f_kind"] },
|
|
23
|
+
idx_events_role: { columns: ["f_role"] },
|
|
24
|
+
idx_events_name: { columns: ["f_name"] },
|
|
25
|
+
idx_events_action_id: { columns: ["f_action_id"] },
|
|
26
|
+
},
|
|
27
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { persistenceState } from "./persistence-state.ts";
|
|
2
|
+
import { schemaMeta } from "./schema-meta.ts";
|
|
3
|
+
import { sessions } from "./sessions.ts";
|
|
4
|
+
import { events } from "./events.ts";
|
|
5
|
+
import { sessionEvents } from "./session-events.ts";
|
|
6
|
+
|
|
7
|
+
export { persistenceState };
|
|
8
|
+
export { schemaMeta };
|
|
9
|
+
export { sessions };
|
|
10
|
+
export { events };
|
|
11
|
+
export { sessionEvents };
|
|
12
|
+
|
|
13
|
+
export const sqliteTableDefs = [
|
|
14
|
+
persistenceState,
|
|
15
|
+
schemaMeta,
|
|
16
|
+
sessions,
|
|
17
|
+
events,
|
|
18
|
+
sessionEvents,
|
|
19
|
+
] as const;
|
|
20
|
+
|
|
21
|
+
export const postgresTableDefs = [
|
|
22
|
+
persistenceState,
|
|
23
|
+
schemaMeta,
|
|
24
|
+
sessions,
|
|
25
|
+
events,
|
|
26
|
+
sessionEvents,
|
|
27
|
+
] as const;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { TableDef } from "../../adapters/types.ts";
|
|
2
|
+
|
|
3
|
+
export const persistenceState: TableDef = {
|
|
4
|
+
name: "t_persistence_state",
|
|
5
|
+
columns: {
|
|
6
|
+
f_singleton: { type: "integer", primaryKey: true },
|
|
7
|
+
f_store_id: { type: "text", notNull: true },
|
|
8
|
+
},
|
|
9
|
+
checks: { ck_persistence_state_singleton: { expression: "f_singleton = 1" } },
|
|
10
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { TableDef } from "../../adapters/types.ts";
|
|
2
|
+
|
|
3
|
+
export const sessionEvents: TableDef = {
|
|
4
|
+
name: "t_session_events",
|
|
5
|
+
columns: {
|
|
6
|
+
f_id: { type: "serial", primaryKey: true },
|
|
7
|
+
f_session_id: {
|
|
8
|
+
type: "text",
|
|
9
|
+
notNull: true,
|
|
10
|
+
references: { table: "t_sessions", column: "f_session_id", onDelete: "cascade" },
|
|
11
|
+
},
|
|
12
|
+
f_event_id: {
|
|
13
|
+
type: "text",
|
|
14
|
+
notNull: true,
|
|
15
|
+
references: { table: "t_events", column: "f_event_id", onDelete: "cascade" },
|
|
16
|
+
},
|
|
17
|
+
f_sequence: { type: "integer", notNull: true },
|
|
18
|
+
f_surface_op: { type: "text" },
|
|
19
|
+
},
|
|
20
|
+
uniques: {
|
|
21
|
+
uq_session_events_session_sequence: { columns: ["f_session_id", "f_sequence"] },
|
|
22
|
+
},
|
|
23
|
+
// UNIQUE(f_session_id, f_sequence) 自动建唯一索引(按 session 过滤 + seq
|
|
24
|
+
// 范围/排序/取尾);f_event_id 索引覆盖反向查找(孤儿事件行清理)。
|
|
25
|
+
indexes: { idx_session_events_event_id: { columns: ["f_event_id"] } },
|
|
26
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { TableDef } from "../../adapters/types.ts";
|
|
2
|
+
|
|
3
|
+
export const sessions: TableDef = {
|
|
4
|
+
name: "t_sessions",
|
|
5
|
+
columns: {
|
|
6
|
+
f_id: { type: "serial", primaryKey: true },
|
|
7
|
+
f_session_id: { type: "text", notNull: true, unique: true },
|
|
8
|
+
f_head_event_id: { type: "text", notNull: true, default: "" },
|
|
9
|
+
f_head_sequence: { type: "integer", notNull: true, default: -1 },
|
|
10
|
+
f_version: { type: "integer", notNull: true },
|
|
11
|
+
f_created_at: { type: "bigint", notNull: true },
|
|
12
|
+
f_cwd: { type: "text" },
|
|
13
|
+
f_parent_session: { type: "text" },
|
|
14
|
+
f_seed_length: { type: "integer" },
|
|
15
|
+
f_origin: { type: "text" },
|
|
16
|
+
f_delegation_depth: { type: "integer" },
|
|
17
|
+
f_incarnation: { type: "text", notNull: true },
|
|
18
|
+
f_revision: { type: "integer", notNull: true },
|
|
19
|
+
},
|
|
20
|
+
};
|