@morlay/session-rdb 0.0.10 → 0.0.12
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 +0 -16
- package/dist/index.d.mts +203 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2030 -0
- package/dist/index.mjs.map +1 -0
- package/dist/invariant.d.mts +8 -0
- package/dist/invariant.d.mts.map +1 -0
- package/dist/invariant.mjs +10 -0
- package/dist/invariant.mjs.map +1 -0
- package/package.json +22 -21
- package/src/adapters/ddl.ts +77 -0
- package/src/adapters/index.ts +4 -0
- package/src/adapters/to-postgres.ts +91 -0
- package/src/adapters/to-sqlite.ts +79 -0
- package/src/backend.ts +112 -0
- package/src/branch.ts +508 -0
- package/src/entities/events.ts +27 -0
- package/src/entities/index.ts +30 -0
- package/src/entities/persistence-state.ts +10 -0
- package/src/entities/schema-meta.ts +9 -0
- package/src/entities/session-events.ts +29 -0
- package/src/entities/sessions.ts +20 -0
- package/src/entities/types.ts +48 -0
- package/src/import.ts +270 -0
- package/src/index.ts +543 -0
- package/src/invariant.ts +15 -0
- package/src/log.ts +370 -0
- package/src/migrate.ts +137 -0
- package/src/postgres.ts +369 -0
- package/src/schema.ts +234 -0
- package/src/sqlite.ts +412 -0
- package/src/write-guard.ts +27 -0
- package/lib/index.d.mts +0 -558
- package/lib/index.d.mts.map +0 -1
- package/lib/index.mjs +0 -2175
- package/lib/index.mjs.map +0 -1
- package/lib/invariant.d.mts +0 -15
- package/lib/invariant.d.mts.map +0 -1
- package/lib/invariant.mjs +0 -21
- package/lib/invariant.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -5,22 +5,6 @@ RDB(SQLite / PostgreSQL)持久会话后端(`ctx.sessionPersistence`):
|
|
|
5
5
|
支持配置选择 SQLite 或 PostgreSQL 后端。设计细节(表结构、delta 过滤、并发写、
|
|
6
6
|
方言差异、仓库结构)见 [docs/design.md](docs/design.md)。
|
|
7
7
|
|
|
8
|
-
## 安装
|
|
9
|
-
|
|
10
|
-
包发布在 GitHub Packages,按包名安装到 dsh profile(示例:web;`dsh plugin
|
|
11
|
-
add` 把参数原样转发给 pnpm,版本号按发布版本调整)。registry 必须限定在
|
|
12
|
-
`@morlay` scope(`--config.@morlay:registry=…`),不要用全局 `--registry`,
|
|
13
|
-
否则依赖(`@deepseek-ai/*` 等)也会被指到 GitHub Packages 而解析失败:
|
|
14
|
-
|
|
15
|
-
```sh
|
|
16
|
-
dsh plugin --profile=web add "@morlay/session-rdb"
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
GitHub Packages 的 registry 要求认证(公开包也一样):profile 的 `.npmrc`
|
|
20
|
-
需为 `npm.pkg.github.com` 配置访问 token(如
|
|
21
|
-
`//npm.pkg.github.com/:_authToken=<token>`),详见
|
|
22
|
-
[GitHub 文档](https://docs.github.com/packages/working-with-a-github-packages-registry/working-with-the-npm-registry)。
|
|
23
|
-
|
|
24
8
|
## 配置
|
|
25
9
|
|
|
26
10
|
配置写在 `${DSH_HOME}/settings.yaml`,settings namespace 为插件短名
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import z from "@deepseek-ai/schemastery";
|
|
2
|
+
import { PersistenceBackend, SessionLocation, SessionPersistence, SessionPersistenceRevision, SessionPersistenceSnapshot, SessionStorageMetadata, StoredPrefix, StoredSuffix } from "@deepseek-ai/dsh-session-persistence";
|
|
3
|
+
import { Session, SessionEvent, SessionHeader, SessionId } from "@deepseek-ai/dsh-session";
|
|
4
|
+
import { BranchAnchorMode, BranchBoundary, ForkFromOptions, SessionBranch, SessionBranchProvider } from "@morlay/session-branch";
|
|
5
|
+
import { Context } from "@deepseek-ai/cordis";
|
|
6
|
+
//#region src/backend.d.ts
|
|
7
|
+
interface SessionRow {
|
|
8
|
+
fSessionId: string;
|
|
9
|
+
fHeadEventId: string;
|
|
10
|
+
fHeadSequence: number;
|
|
11
|
+
fVersion: number;
|
|
12
|
+
fCreatedAt: number;
|
|
13
|
+
fCwd: string | null;
|
|
14
|
+
fParentSession: string | null;
|
|
15
|
+
fSeedLength: number | null;
|
|
16
|
+
fOrigin: string | null;
|
|
17
|
+
fDelegationDepth: number | null;
|
|
18
|
+
fIncarnation: string;
|
|
19
|
+
fRevision: number;
|
|
20
|
+
}
|
|
21
|
+
interface EventInsert {
|
|
22
|
+
fEventId: string;
|
|
23
|
+
fParentId: string;
|
|
24
|
+
fType: string;
|
|
25
|
+
fKind: string;
|
|
26
|
+
fRole: string;
|
|
27
|
+
fName: string;
|
|
28
|
+
fActionId: string;
|
|
29
|
+
fEncoding: string;
|
|
30
|
+
fData: string;
|
|
31
|
+
fCreatedAt: number;
|
|
32
|
+
}
|
|
33
|
+
interface EventRow {
|
|
34
|
+
fEventId: string;
|
|
35
|
+
fSequence: number;
|
|
36
|
+
fOriginalSeq: number;
|
|
37
|
+
fType: string;
|
|
38
|
+
fKind: string;
|
|
39
|
+
fRole: string;
|
|
40
|
+
fName: string;
|
|
41
|
+
fActionId: string;
|
|
42
|
+
fCreatedAt: number;
|
|
43
|
+
fData: string;
|
|
44
|
+
fSurfaceOp: string | null;
|
|
45
|
+
}
|
|
46
|
+
interface BackendTx {
|
|
47
|
+
upsertSession(storage: SessionStorageMetadata, incarnation: string): Promise<void>;
|
|
48
|
+
getHead(id: SessionId): Promise<Pick<SessionRow, "fHeadEventId" | "fHeadSequence">>;
|
|
49
|
+
getSeedLength(id: SessionId): Promise<number | null>;
|
|
50
|
+
updateSeedLength(id: SessionId, seedLength: number): Promise<void>;
|
|
51
|
+
insertEvents(events: EventInsert[]): Promise<void>;
|
|
52
|
+
insertBridges(rows: Array<{
|
|
53
|
+
fSessionId: SessionId;
|
|
54
|
+
fEventId: string;
|
|
55
|
+
fSequence: number;
|
|
56
|
+
fOriginalSeq: number;
|
|
57
|
+
fSurfaceOp: string | null;
|
|
58
|
+
}>): Promise<void>;
|
|
59
|
+
updateHead(id: SessionId, headEventId: string, headSequence: number): Promise<void>;
|
|
60
|
+
bumpRevision(id: SessionId): Promise<void>;
|
|
61
|
+
deleteBridgeTail(id: SessionId, fromSequence: number): Promise<void>;
|
|
62
|
+
getPrevBridge(id: SessionId, sequence: number): Promise<{
|
|
63
|
+
fEventId: string;
|
|
64
|
+
fSequence: number;
|
|
65
|
+
} | undefined>;
|
|
66
|
+
getLastBridge(id: SessionId): Promise<{
|
|
67
|
+
fEventId: string;
|
|
68
|
+
fSequence: number;
|
|
69
|
+
} | undefined>;
|
|
70
|
+
}
|
|
71
|
+
interface Backend {
|
|
72
|
+
readonly kind: "sqlite" | "postgres";
|
|
73
|
+
readonly storeIdentity: string;
|
|
74
|
+
open(): Promise<void>;
|
|
75
|
+
getSession(id: SessionId): Promise<SessionRow | undefined>;
|
|
76
|
+
getSeqMapRows(id: SessionId): Promise<Array<{
|
|
77
|
+
fSequence: number;
|
|
78
|
+
fOriginalSeq: number;
|
|
79
|
+
}>>;
|
|
80
|
+
getEventRows(id: SessionId, fromSequence?: number): Promise<EventRow[]>;
|
|
81
|
+
listSessions(): Promise<SessionRow[]>;
|
|
82
|
+
transaction<T>(fn: (tx: BackendTx) => Promise<T>): Promise<T>;
|
|
83
|
+
close(): Promise<void>;
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/write-guard.d.ts
|
|
87
|
+
declare class WriteGuard {
|
|
88
|
+
private readonly headSeqs;
|
|
89
|
+
confirmHead(id: SessionId, head: number): void;
|
|
90
|
+
assertNoConcurrentWriter(id: SessionId, storedHead: number): void;
|
|
91
|
+
}
|
|
92
|
+
//#endregion
|
|
93
|
+
//#region src/schema.d.ts
|
|
94
|
+
declare const SCHEMA_VERSION = 2;
|
|
95
|
+
declare const EPHEMERAL_EVENT_TYPES: readonly ["assistant/chunk"];
|
|
96
|
+
type JournalMode = "wal" | "delete" | "truncate" | "persist";
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region src/branch.d.ts
|
|
99
|
+
declare function locateTurnEnd(events: readonly SessionEvent[], atSeq?: number, mode?: BranchAnchorMode): number;
|
|
100
|
+
interface LiveSessionHooks {
|
|
101
|
+
getSession(id: SessionId): Session | undefined;
|
|
102
|
+
getAgent(id: SessionId): LiveAgentLike | undefined;
|
|
103
|
+
flush(session: Session): Promise<boolean>;
|
|
104
|
+
setCoordinatorCursor(id: SessionId, cursor: number): void;
|
|
105
|
+
setCoordinatorState(id: SessionId, cursor: number, meta: SessionHeader): void;
|
|
106
|
+
setCoordinatorSeedLength(id: SessionId, seedLength: number): void;
|
|
107
|
+
}
|
|
108
|
+
interface LiveAgentLike {
|
|
109
|
+
session: Session;
|
|
110
|
+
requestHeaderLogged?: boolean;
|
|
111
|
+
}
|
|
112
|
+
declare class SessionBranchRdbProvider implements SessionBranchProvider {
|
|
113
|
+
private readonly persistence;
|
|
114
|
+
private readonly live;
|
|
115
|
+
readonly name = "session-rdb";
|
|
116
|
+
constructor(persistence: SessionPersistenceRdb, live?: LiveSessionHooks);
|
|
117
|
+
readBranchPrefix(id: SessionId, atSeq?: number, mode?: BranchAnchorMode, signal?: AbortSignal): Promise<BranchBoundary>;
|
|
118
|
+
readRawEvents(id: SessionId, signal?: AbortSignal): Promise<{
|
|
119
|
+
meta: SessionHeader;
|
|
120
|
+
events: readonly SessionEvent[];
|
|
121
|
+
}>;
|
|
122
|
+
forkFrom(sourceId: SessionId, options?: ForkFromOptions, signal?: AbortSignal): Promise<SessionId>;
|
|
123
|
+
rewind(id: SessionId, toBoundary: number, signal?: AbortSignal): Promise<SessionPersistenceSnapshot>;
|
|
124
|
+
}
|
|
125
|
+
declare class SessionBranchRdb extends SessionBranch {
|
|
126
|
+
static inject: string[];
|
|
127
|
+
constructor(ctx: import("@deepseek-ai/cordis").Context);
|
|
128
|
+
private readonly provider;
|
|
129
|
+
readBranchPrefix(id: SessionId, atSeq?: number, mode?: BranchAnchorMode, signal?: AbortSignal): Promise<BranchBoundary>;
|
|
130
|
+
readRawEvents(id: SessionId, signal?: AbortSignal): Promise<{
|
|
131
|
+
meta: SessionHeader;
|
|
132
|
+
events: readonly SessionEvent[];
|
|
133
|
+
}>;
|
|
134
|
+
forkFrom(sourceId: SessionId, options?: ForkFromOptions, signal?: AbortSignal): Promise<SessionId>;
|
|
135
|
+
rewind(id: SessionId, toBoundary: number, signal?: AbortSignal): Promise<SessionPersistenceSnapshot>;
|
|
136
|
+
syncLiveCursor(sessionId: SessionId): void;
|
|
137
|
+
timeline(sessionId: SessionId, signal?: AbortSignal): Promise<import("@morlay/session-branch").BranchTimeline>;
|
|
138
|
+
}
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/index.d.ts
|
|
141
|
+
interface SessionPersistenceRdbInternals {
|
|
142
|
+
readonly backend: Backend;
|
|
143
|
+
readonly writeGuard: WriteGuard;
|
|
144
|
+
create(meta: SessionHeader, inheritedEventCount?: number): Promise<void>;
|
|
145
|
+
append(id: SessionId, events: readonly SessionEvent[]): Promise<void>;
|
|
146
|
+
load(id: SessionId): Promise<import("@deepseek-ai/dsh-session-persistence").SessionInspection>;
|
|
147
|
+
inspect(id: SessionId, signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-session-persistence").SessionInspection>;
|
|
148
|
+
readFrom(id: SessionId, fromSeq: number, signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-session-persistence").SessionEventSuffix>;
|
|
149
|
+
listSnapshots(signal?: AbortSignal): Promise<SessionPersistenceSnapshot[]>;
|
|
150
|
+
readStoredRevision(id: SessionId, signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-session-persistence").SessionPersistenceRevision | undefined>;
|
|
151
|
+
registerReuseEventIds(childId: SessionId, map: ReadonlyMap<number, string>): void;
|
|
152
|
+
}
|
|
153
|
+
type Config = {
|
|
154
|
+
type: "sqlite";
|
|
155
|
+
path: string;
|
|
156
|
+
journalMode?: JournalMode;
|
|
157
|
+
busyTimeout?: number;
|
|
158
|
+
} | {
|
|
159
|
+
type: "postgres";
|
|
160
|
+
connectionString: string;
|
|
161
|
+
schema?: string;
|
|
162
|
+
};
|
|
163
|
+
declare class SessionPersistenceRdb extends SessionPersistence implements PersistenceBackend<number> {
|
|
164
|
+
config: Config;
|
|
165
|
+
static inject: string[];
|
|
166
|
+
static Config: z<Config>;
|
|
167
|
+
static readonly settingsNs = "session-rdb";
|
|
168
|
+
readonly name = "session-rdb";
|
|
169
|
+
readonly supportsRawArtifacts = true;
|
|
170
|
+
readRaw(id: SessionId, signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-session-persistence").SessionRawArtifact | undefined>;
|
|
171
|
+
private readonly backend;
|
|
172
|
+
private storeIdentity;
|
|
173
|
+
private readonly ready;
|
|
174
|
+
private readonly coordinator;
|
|
175
|
+
private readonly writeGuard;
|
|
176
|
+
private readonly reuseEventIds;
|
|
177
|
+
constructor(ctx: Context, config: Config, injectedBackend?: Backend);
|
|
178
|
+
private init;
|
|
179
|
+
locate(_meta: SessionHeader): SessionLocation | undefined;
|
|
180
|
+
create(meta: SessionHeader, inheritedEventCount?: number): Promise<void>;
|
|
181
|
+
append(id: SessionId, events: readonly SessionEvent[]): Promise<void>;
|
|
182
|
+
load(id: SessionId): Promise<import("@deepseek-ai/dsh-session-persistence").SessionInspection>;
|
|
183
|
+
inspect(id: SessionId, signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-session-persistence").SessionInspection>;
|
|
184
|
+
readFrom(id: SessionId, fromSeq: number, signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-session-persistence").SessionEventSuffix>;
|
|
185
|
+
borrowSession(id: SessionId, signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-session-persistence").BorrowedSessionSource>;
|
|
186
|
+
loadStored(id: SessionId, signal?: AbortSignal): Promise<StoredPrefix<number> | undefined>;
|
|
187
|
+
loadStoredFrom(id: SessionId, fromSeq: number, signal?: AbortSignal): Promise<StoredSuffix | undefined>;
|
|
188
|
+
private readPrefix;
|
|
189
|
+
readStoredRevision(id: SessionId, signal?: AbortSignal): Promise<SessionPersistenceRevision | undefined>;
|
|
190
|
+
private readLog;
|
|
191
|
+
appendBatch(storage: SessionStorageMetadata, events: readonly SessionEvent[], _isMaterialized: boolean): Promise<void>;
|
|
192
|
+
commitRepair(storage: SessionStorageMetadata, tornMarker: number | undefined, closers: readonly SessionEvent[]): Promise<void>;
|
|
193
|
+
list(signal?: AbortSignal): Promise<SessionHeader[]>;
|
|
194
|
+
listSnapshots(signal?: AbortSignal): Promise<Array<SessionPersistenceSnapshot & {
|
|
195
|
+
inheritedEventCount: number;
|
|
196
|
+
}>>;
|
|
197
|
+
close(): Promise<void>;
|
|
198
|
+
registerReuseEventIds(childId: SessionId, map: ReadonlyMap<number, string>): void;
|
|
199
|
+
internals(): SessionPersistenceRdbInternals;
|
|
200
|
+
}
|
|
201
|
+
//#endregion
|
|
202
|
+
export { Config, EPHEMERAL_EVENT_TYPES, SCHEMA_VERSION, SessionBranchRdb, SessionBranchRdbProvider, SessionPersistenceRdb, SessionPersistenceRdb as default, SessionPersistenceRdbInternals, locateTurnEnd };
|
|
203
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/backend.ts","../src/write-guard.ts","../src/schema.ts","../src/branch.ts","../src/index.ts"],"mappings":"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"}
|