@powerhousedao/reactor-drive 6.2.3-dev.4 → 6.2.3-dev.41
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/dist/index.d.ts +68 -47
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +163 -53
- package/dist/index.js.map +1 -1
- package/package.json +8 -6
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
import * as _powerhousedao_shared_document_model0 from "@powerhousedao/shared/document-model";
|
|
2
1
|
import { Action, CreateDocument, CreateState, DocumentModelModule, OperationWithContext, PHBaseState, PHDocument, StateReducer } from "@powerhousedao/shared/document-model";
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import { BaseReadModel, IConsistencyTracker, IDriveClient, IOperationIndex, IReactorClient, IWriteCache, PagedResults, PagingOptions } from "@powerhousedao/reactor";
|
|
2
|
+
import { Generated, Kysely, Transaction } from "kysely";
|
|
3
|
+
import { BaseReadModel, DocumentViewDatabase, IConsistencyTracker, IDriveClient, IOperationIndex, IReactorClient, IWriteCache, PagedResults, PagingOptions } from "@powerhousedao/reactor";
|
|
6
4
|
import { DocumentDriveDocument, DriveInput, FolderNode, Node } from "@powerhousedao/shared/document-drive";
|
|
7
|
-
import * as graphql_language_ast_js0 from "graphql/language/ast.js";
|
|
8
|
-
|
|
9
5
|
//#region src/constants.d.ts
|
|
10
|
-
declare const REACTOR_DRIVE_DOCUMENT_TYPE = "powerhouse/reactor-drive";
|
|
11
|
-
declare const DRIVE_CHILD_RELATIONSHIP_TYPE = "drive/child";
|
|
12
|
-
declare const REACTOR_DRIVE_FILE_EXTENSION = "phrd";
|
|
6
|
+
export declare const REACTOR_DRIVE_DOCUMENT_TYPE = "powerhouse/reactor-drive";
|
|
7
|
+
export declare const DRIVE_CHILD_RELATIONSHIP_TYPE = "drive/child";
|
|
8
|
+
export declare const REACTOR_DRIVE_FILE_EXTENSION = "phrd";
|
|
13
9
|
//#endregion
|
|
14
10
|
//#region src/types.d.ts
|
|
15
11
|
type ReactorDriveGlobalState = {
|
|
@@ -87,14 +83,14 @@ type SetSharingTypeInput = {
|
|
|
87
83
|
type SetAvailableOfflineInput = {
|
|
88
84
|
availableOffline: boolean;
|
|
89
85
|
};
|
|
90
|
-
declare function setDriveNameAction(input: SetDriveNameInput): Action;
|
|
91
|
-
declare function setDriveIconAction(input: SetDriveIconInput): Action;
|
|
92
|
-
declare function setSharingTypeAction(input: SetSharingTypeInput): Action;
|
|
93
|
-
declare function setAvailableOfflineAction(input: SetAvailableOfflineInput): Action;
|
|
94
|
-
declare function addFolderAction(input: AddFolderActionInput): Action;
|
|
95
|
-
declare function updateFolderAction(input: UpdateFolderActionInput): Action;
|
|
96
|
-
declare function removeFolderAction(input: RemoveFolderActionInput): Action;
|
|
97
|
-
declare const reactorDriveActions: {
|
|
86
|
+
export declare function setDriveNameAction(input: SetDriveNameInput): Action;
|
|
87
|
+
export declare function setDriveIconAction(input: SetDriveIconInput): Action;
|
|
88
|
+
export declare function setSharingTypeAction(input: SetSharingTypeInput): Action;
|
|
89
|
+
export declare function setAvailableOfflineAction(input: SetAvailableOfflineInput): Action;
|
|
90
|
+
export declare function addFolderAction(input: AddFolderActionInput): Action;
|
|
91
|
+
export declare function updateFolderAction(input: UpdateFolderActionInput): Action;
|
|
92
|
+
export declare function removeFolderAction(input: RemoveFolderActionInput): Action;
|
|
93
|
+
export declare const reactorDriveActions: {
|
|
98
94
|
setDriveName: typeof setDriveNameAction;
|
|
99
95
|
setDriveIcon: typeof setDriveIconAction;
|
|
100
96
|
setSharingType: typeof setSharingTypeAction;
|
|
@@ -105,13 +101,13 @@ declare const reactorDriveActions: {
|
|
|
105
101
|
};
|
|
106
102
|
//#endregion
|
|
107
103
|
//#region src/module.d.ts
|
|
108
|
-
declare const reactorDriveCreateState: CreateState<ReactorDrivePHState>;
|
|
109
|
-
declare const reactorDriveCreateDocument: CreateDocument<ReactorDrivePHState>;
|
|
110
|
-
declare const reactorDriveDocumentReducer:
|
|
111
|
-
declare const reactorDriveDocumentModelModule: ReactorDriveDocumentModelModule;
|
|
104
|
+
export declare const reactorDriveCreateState: CreateState<ReactorDrivePHState>;
|
|
105
|
+
export declare const reactorDriveCreateDocument: CreateDocument<ReactorDrivePHState>;
|
|
106
|
+
export declare const reactorDriveDocumentReducer: import("@powerhousedao/shared/document-model").Reducer<ReactorDrivePHState>;
|
|
107
|
+
export declare const reactorDriveDocumentModelModule: ReactorDriveDocumentModelModule;
|
|
112
108
|
//#endregion
|
|
113
109
|
//#region src/reducer/drive.d.ts
|
|
114
|
-
declare const reactorDriveStateReducer: StateReducer<ReactorDrivePHState>;
|
|
110
|
+
export declare const reactorDriveStateReducer: StateReducer<ReactorDrivePHState>;
|
|
115
111
|
//#endregion
|
|
116
112
|
//#region src/schema/tables.d.ts
|
|
117
113
|
interface DriveNodeTable {
|
|
@@ -141,8 +137,8 @@ interface ReactorDriveMigrationResult {
|
|
|
141
137
|
migrationsExecuted: string[];
|
|
142
138
|
error?: Error;
|
|
143
139
|
}
|
|
144
|
-
declare function runReactorDriveMigrations(db: Kysely<unknown>, schema: string): Promise<ReactorDriveMigrationResult>;
|
|
145
|
-
declare function getReactorDriveMigrationStatus(db: Kysely<unknown>, schema: string): Promise<readonly kysely.MigrationInfo[]>;
|
|
140
|
+
export declare function runReactorDriveMigrations(db: Kysely<unknown>, schema: string): Promise<ReactorDriveMigrationResult>;
|
|
141
|
+
export declare function getReactorDriveMigrationStatus(db: Kysely<unknown>, schema: string): Promise<readonly import("kysely").MigrationInfo[]>;
|
|
146
142
|
//#endregion
|
|
147
143
|
//#region src/processors/utils/collisions.d.ts
|
|
148
144
|
/**
|
|
@@ -153,19 +149,38 @@ declare function getReactorDriveMigrationStatus(db: Kysely<unknown>, schema: str
|
|
|
153
149
|
* The rule matches the legacy document-drive module so that migration from
|
|
154
150
|
* the legacy state produces stable suffixes.
|
|
155
151
|
*/
|
|
156
|
-
declare function resolveCollision(requested: string, takenNames: Iterable<string>): string;
|
|
152
|
+
export declare function resolveCollision(requested: string, takenNames: Iterable<string>): string;
|
|
157
153
|
//#endregion
|
|
158
154
|
//#region src/processors/node-processor.d.ts
|
|
159
|
-
|
|
155
|
+
/**
|
|
156
|
+
* Projects drive structure operations into the DriveNode tree.
|
|
157
|
+
*
|
|
158
|
+
* Indexes a batch unchunked: a single job may carry one action per descendant
|
|
159
|
+
* of a copied subtree, and a partially committed copy is a half-built tree that
|
|
160
|
+
* readers can see. Chunked commits would only be repaired at the next init(),
|
|
161
|
+
* so the whole batch commits or none of it does.
|
|
162
|
+
*/
|
|
163
|
+
export declare class NodeProcessor extends BaseReadModel {
|
|
164
|
+
static readonly commitsInFenceTransaction = true;
|
|
160
165
|
private readonly driveDb;
|
|
161
166
|
private readonly baseDb;
|
|
162
167
|
private readonly schema;
|
|
163
|
-
|
|
168
|
+
private readonly reactorSchema;
|
|
169
|
+
constructor(baseDb: Kysely<unknown>, schema: string, operationIndex: IOperationIndex, writeCache: IWriteCache, consistencyTracker: IConsistencyTracker, reactorSchema?: string);
|
|
170
|
+
protected purgeLookup(db: Kysely<any>): Kysely<any>;
|
|
164
171
|
init(): Promise<void>;
|
|
165
|
-
protected commitOperations(items: OperationWithContext[]): Promise<void>;
|
|
172
|
+
protected commitOperations(items: OperationWithContext[], fenceTrx?: Transaction<DocumentViewDatabase>): Promise<void>;
|
|
173
|
+
protected writesRows(items: OperationWithContext[]): boolean;
|
|
174
|
+
private applyAll;
|
|
175
|
+
/** Tombstoned targets of this batch's drive-child ADD_RELATIONSHIPs. */
|
|
176
|
+
private findPurgedTargets;
|
|
177
|
+
private namesPurgedTarget;
|
|
178
|
+
private addedChildOf;
|
|
166
179
|
private applyNameOperation;
|
|
167
180
|
private applyStructureOperation;
|
|
168
181
|
private applyDeleteDocument;
|
|
182
|
+
/** The id's nodes in every drive, a drive's own tree, and its name. */
|
|
183
|
+
private eraseDocument;
|
|
169
184
|
private handleAddFileRelationship;
|
|
170
185
|
private handleAddFolder;
|
|
171
186
|
private handleUpdateFolder;
|
|
@@ -209,7 +224,7 @@ interface IDriveReadModel {
|
|
|
209
224
|
}
|
|
210
225
|
//#endregion
|
|
211
226
|
//#region src/read-model/drive-node-view.d.ts
|
|
212
|
-
declare class DriveNodeView implements IDriveReadModel {
|
|
227
|
+
export declare class DriveNodeView implements IDriveReadModel {
|
|
213
228
|
private readonly db;
|
|
214
229
|
constructor(db: Kysely<ReactorDriveDatabase>);
|
|
215
230
|
getNode(driveId: string, nodeId: string): Promise<ReactorDriveNode | undefined>;
|
|
@@ -230,7 +245,7 @@ interface ReactorDriveClientArgs {
|
|
|
230
245
|
* for files) and is materialised by `NodeProcessor` into the `DriveNode`
|
|
231
246
|
* table consumed via {@link IDriveReadModel}.
|
|
232
247
|
*/
|
|
233
|
-
declare class ReactorDriveClient implements IDriveClient {
|
|
248
|
+
export declare class ReactorDriveClient implements IDriveClient {
|
|
234
249
|
private readonly reactor;
|
|
235
250
|
private readonly readModel;
|
|
236
251
|
constructor(args: ReactorDriveClientArgs);
|
|
@@ -250,12 +265,19 @@ declare class ReactorDriveClient implements IDriveClient {
|
|
|
250
265
|
}
|
|
251
266
|
//#endregion
|
|
252
267
|
//#region src/subgraph/schema.d.ts
|
|
253
|
-
declare const typeDefs:
|
|
268
|
+
declare const typeDefs: import("graphql/language/ast.js").DocumentNode;
|
|
254
269
|
//#endregion
|
|
255
270
|
//#region src/subgraph/resolvers.d.ts
|
|
256
271
|
interface ReactorDriveResolverContext {
|
|
257
272
|
reactorClient: IReactorClient;
|
|
258
273
|
readModel: IDriveReadModel;
|
|
274
|
+
/** The authenticated caller; absent reads as anonymous, never the host. */
|
|
275
|
+
user?: {
|
|
276
|
+
address?: string;
|
|
277
|
+
appKey?: string;
|
|
278
|
+
};
|
|
279
|
+
/** The host's own read check, applied on top of the reactor's read gate. */
|
|
280
|
+
hostCanRead?: (documentId: string, address?: string) => Promise<boolean>;
|
|
259
281
|
}
|
|
260
282
|
type PagingInput = {
|
|
261
283
|
cursor?: string;
|
|
@@ -263,30 +285,30 @@ type PagingInput = {
|
|
|
263
285
|
};
|
|
264
286
|
type NodeKindFilter = "FILE" | "FOLDER" | undefined;
|
|
265
287
|
/**
|
|
266
|
-
* Builds GraphQL resolvers backed by the drive read model.
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
288
|
+
* Builds GraphQL resolvers backed by the drive read model. Dependencies are
|
|
289
|
+
* read off the GraphQL context unless bound here. Every read is decided as the
|
|
290
|
+
* caller: a drive's nodes are its content, so they are served only with the
|
|
291
|
+
* drive, and a file node only with its own document too.
|
|
270
292
|
*/
|
|
271
|
-
declare function createReactorDriveResolvers(): {
|
|
293
|
+
export declare function createReactorDriveResolvers(bound?: Omit<Partial<ReactorDriveResolverContext>, "user">): {
|
|
272
294
|
Query: {
|
|
273
295
|
reactorDrive(_root: unknown, args: {
|
|
274
296
|
id: string;
|
|
275
|
-
},
|
|
297
|
+
}, context: ReactorDriveResolverContext): Promise<{
|
|
276
298
|
id: string;
|
|
277
299
|
name: string;
|
|
278
300
|
icon: string | null;
|
|
279
|
-
sharingType: string;
|
|
280
|
-
availableOffline: boolean;
|
|
301
|
+
sharingType: string | null;
|
|
302
|
+
availableOffline: boolean | null;
|
|
281
303
|
} | null>;
|
|
282
304
|
reactorDriveNode(_root: unknown, args: {
|
|
283
305
|
driveId: string;
|
|
284
306
|
id: string;
|
|
285
|
-
},
|
|
307
|
+
}, context: ReactorDriveResolverContext): Promise<ReactorDriveNode | undefined>;
|
|
286
308
|
reactorDriveDescendants(_root: unknown, args: {
|
|
287
309
|
driveId: string;
|
|
288
310
|
root: string;
|
|
289
|
-
},
|
|
311
|
+
}, context: ReactorDriveResolverContext): Promise<ReactorDriveNode[]>;
|
|
290
312
|
};
|
|
291
313
|
ReactorDrive: {
|
|
292
314
|
rootNodes(parent: {
|
|
@@ -294,11 +316,10 @@ declare function createReactorDriveResolvers(): {
|
|
|
294
316
|
}, args: {
|
|
295
317
|
paging?: PagingInput;
|
|
296
318
|
kind?: NodeKindFilter;
|
|
297
|
-
},
|
|
319
|
+
}, context: ReactorDriveResolverContext): Promise<{
|
|
298
320
|
results: ReactorDriveNode[];
|
|
299
321
|
nextCursor: string | undefined;
|
|
300
322
|
hasMore: boolean;
|
|
301
|
-
totalCount: number | undefined;
|
|
302
323
|
}>;
|
|
303
324
|
};
|
|
304
325
|
ReactorDriveFolderNode: {
|
|
@@ -308,11 +329,10 @@ declare function createReactorDriveResolvers(): {
|
|
|
308
329
|
}, args: {
|
|
309
330
|
paging?: PagingInput;
|
|
310
331
|
kind?: NodeKindFilter;
|
|
311
|
-
},
|
|
332
|
+
}, context: ReactorDriveResolverContext): Promise<{
|
|
312
333
|
results: ReactorDriveNode[];
|
|
313
334
|
nextCursor: string | undefined;
|
|
314
335
|
hasMore: boolean;
|
|
315
|
-
totalCount: number | undefined;
|
|
316
336
|
}>;
|
|
317
337
|
};
|
|
318
338
|
ReactorDriveNode: {
|
|
@@ -347,7 +367,8 @@ interface MigrateLegacyDriveStateResult {
|
|
|
347
367
|
* Re-running the migration is safe: existing `DriveNode` rows are skipped
|
|
348
368
|
* so already-migrated nodes are left untouched.
|
|
349
369
|
*/
|
|
350
|
-
declare function migrateLegacyDriveState(args: MigrateLegacyDriveStateArgs): Promise<MigrateLegacyDriveStateResult>;
|
|
370
|
+
export declare function migrateLegacyDriveState(args: MigrateLegacyDriveStateArgs): Promise<MigrateLegacyDriveStateResult>;
|
|
351
371
|
//#endregion
|
|
352
|
-
export { type AddFolderActionInput,
|
|
372
|
+
export { type AddFolderActionInput, type DocumentNameTable, type DriveChildFileMetadata, type DriveNodeTable, type IDriveReadModel, type MigrateLegacyDriveStateArgs, type MigrateLegacyDriveStateResult, type ReactorDriveClientArgs, type ReactorDriveDatabase, type ReactorDriveDocumentModelModule, type ReactorDriveFileNode, type ReactorDriveFolderNode, type ReactorDriveGlobalState, type ReactorDriveInput, type ReactorDriveLocalState, type ReactorDriveMigrationResult, type ReactorDriveNode, type ReactorDriveNodeKind, type ReactorDrivePHState, type ReactorDriveResolverContext, type RemoveFolderActionInput, type SetAvailableOfflineInput, type SetDriveIconInput, type SetDriveNameInput, type SetSharingTypeInput, type UpdateFolderActionInput, typeDefs as reactorDriveSubgraphTypeDefs };
|
|
373
|
+
export {};
|
|
353
374
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/constants.ts","../src/types.ts","../src/actions.ts","../src/module.ts","../src/reducer/drive.ts","../src/schema/tables.ts","../src/schema/migrations/migrator.ts","../src/processors/utils/collisions.ts","../src/processors/node-processor.ts","../src/read-model/interfaces.ts","../src/read-model/drive-node-view.ts","../src/client/reactor-drive-client.ts","../src/subgraph/schema.ts","../src/subgraph/resolvers.ts","../src/migration/migrate-legacy-state.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/constants.ts","../src/types.ts","../src/actions.ts","../src/module.ts","../src/reducer/drive.ts","../src/schema/tables.ts","../src/schema/migrations/migrator.ts","../src/processors/utils/collisions.ts","../src/processors/node-processor.ts","../src/read-model/interfaces.ts","../src/read-model/drive-node-view.ts","../src/client/reactor-drive-client.ts","../src/subgraph/schema.ts","../src/subgraph/resolvers.ts","../src/migration/migrate-legacy-state.ts"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { assertAuthPreservedOnDuplicate, baseCreateDocument, baseLoadFromInput, baseSaveToFileHandle,
|
|
1
|
+
import { assertAuthPreservedOnDuplicate, baseCreateDocument, baseLoadFromInput, baseSaveToFileHandle, createCopyHeader, createReducer, createState, defaultBaseState, generateId, isDocumentAction, replayDocumentVersioned, requestedSignaturePolicy, withSignaturePolicy } from "@powerhousedao/shared/document-model";
|
|
2
2
|
import { Migrator, sql } from "kysely";
|
|
3
|
-
import { BaseReadModel, addRelationshipAction, createDocumentAction, removeRelationshipAction, upgradeDocumentAction } from "@powerhousedao/reactor";
|
|
3
|
+
import { BaseReadModel, REACTOR_SCHEMA, addRelationshipAction, createDocumentAction, findPurged, isPurgeMarker, removeRelationshipAction, unchunkedReadModelIndexingConfig, upgradeDocumentAction } from "@powerhousedao/reactor";
|
|
4
4
|
import { gql } from "graphql-tag";
|
|
5
5
|
//#region \0rolldown/runtime.js
|
|
6
6
|
var __defProp = Object.defineProperty;
|
|
@@ -410,53 +410,109 @@ function resolveCollision(requested, takenNames) {
|
|
|
410
410
|
}
|
|
411
411
|
//#endregion
|
|
412
412
|
//#region src/processors/node-processor.ts
|
|
413
|
-
const NAME_ACTION_TYPES = new Set([
|
|
413
|
+
const NAME_ACTION_TYPES = /* @__PURE__ */ new Set([
|
|
414
414
|
"CREATE_DOCUMENT",
|
|
415
415
|
"UPGRADE_DOCUMENT",
|
|
416
416
|
"SET_NAME"
|
|
417
417
|
]);
|
|
418
|
-
const STRUCTURE_ACTION_TYPES = new Set([
|
|
418
|
+
const STRUCTURE_ACTION_TYPES = /* @__PURE__ */ new Set([
|
|
419
419
|
"ADD_RELATIONSHIP",
|
|
420
420
|
"REMOVE_RELATIONSHIP",
|
|
421
421
|
"ADD_FOLDER",
|
|
422
422
|
"UPDATE_FOLDER",
|
|
423
423
|
"REMOVE_FOLDER"
|
|
424
424
|
]);
|
|
425
|
+
/**
|
|
426
|
+
* Projects drive structure operations into the DriveNode tree.
|
|
427
|
+
*
|
|
428
|
+
* Indexes a batch unchunked: a single job may carry one action per descendant
|
|
429
|
+
* of a copied subtree, and a partially committed copy is a half-built tree that
|
|
430
|
+
* readers can see. Chunked commits would only be repaired at the next init(),
|
|
431
|
+
* so the whole batch commits or none of it does.
|
|
432
|
+
*/
|
|
425
433
|
var NodeProcessor = class extends BaseReadModel {
|
|
434
|
+
static commitsInFenceTransaction = true;
|
|
426
435
|
driveDb;
|
|
427
436
|
baseDb;
|
|
428
437
|
schema;
|
|
429
|
-
|
|
438
|
+
reactorSchema;
|
|
439
|
+
constructor(baseDb, schema, operationIndex, writeCache, consistencyTracker, reactorSchema = REACTOR_SCHEMA) {
|
|
430
440
|
const scopedDb = baseDb.withSchema(schema);
|
|
431
441
|
super(scopedDb, operationIndex, writeCache, consistencyTracker, {
|
|
432
442
|
readModelId: "reactor-drive-node-processor",
|
|
433
|
-
rebuildStateOnInit: false
|
|
443
|
+
rebuildStateOnInit: false,
|
|
444
|
+
indexing: unchunkedReadModelIndexingConfig,
|
|
445
|
+
purgeFence: "locked"
|
|
434
446
|
});
|
|
435
447
|
this.driveDb = scopedDb;
|
|
436
448
|
this.baseDb = baseDb;
|
|
437
449
|
this.schema = schema;
|
|
450
|
+
this.reactorSchema = reactorSchema;
|
|
451
|
+
}
|
|
452
|
+
purgeLookup(db) {
|
|
453
|
+
return db.withSchema(this.reactorSchema);
|
|
438
454
|
}
|
|
439
455
|
async init() {
|
|
440
456
|
const result = await runReactorDriveMigrations(this.baseDb, this.schema);
|
|
441
457
|
if (!result.success && result.error) throw new Error(`Reactor drive migrations failed: ${result.error.message}`);
|
|
442
458
|
await super.init();
|
|
443
459
|
}
|
|
444
|
-
async commitOperations(items) {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
if (actionType === "DELETE_DOCUMENT") await this.applyDeleteDocument(trx, item);
|
|
457
|
-
}
|
|
460
|
+
async commitOperations(items, fenceTrx) {
|
|
461
|
+
if (fenceTrx) {
|
|
462
|
+
await this.applyAll(items, fenceTrx);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
if (!this.writesRows(items)) return;
|
|
466
|
+
await this.driveDb.transaction().execute((trx) => this.applyAll(items, trx));
|
|
467
|
+
}
|
|
468
|
+
writesRows(items) {
|
|
469
|
+
return items.some((item) => {
|
|
470
|
+
const actionType = item.operation.action.type;
|
|
471
|
+
return NAME_ACTION_TYPES.has(actionType) || STRUCTURE_ACTION_TYPES.has(actionType) || actionType === "DELETE_DOCUMENT" || isPurgeMarker(item.operation);
|
|
458
472
|
});
|
|
459
473
|
}
|
|
474
|
+
async applyAll(items, trx) {
|
|
475
|
+
const purgedTargets = await this.findPurgedTargets(trx, items);
|
|
476
|
+
for (const item of items) {
|
|
477
|
+
const actionType = item.operation.action.type;
|
|
478
|
+
if (isPurgeMarker(item.operation)) {
|
|
479
|
+
const input = item.operation.action.input;
|
|
480
|
+
await this.eraseDocument(trx, input.documentId || item.context.documentId);
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
if (NAME_ACTION_TYPES.has(actionType)) {
|
|
484
|
+
await this.applyNameOperation(trx, item);
|
|
485
|
+
continue;
|
|
486
|
+
}
|
|
487
|
+
if (STRUCTURE_ACTION_TYPES.has(actionType)) {
|
|
488
|
+
if (this.namesPurgedTarget(item, purgedTargets)) continue;
|
|
489
|
+
await this.applyStructureOperation(trx, item);
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
if (actionType === "DELETE_DOCUMENT") await this.applyDeleteDocument(trx, item);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
/** Tombstoned targets of this batch's drive-child ADD_RELATIONSHIPs. */
|
|
496
|
+
async findPurgedTargets(trx, items) {
|
|
497
|
+
const targets = [];
|
|
498
|
+
for (const item of items) {
|
|
499
|
+
const target = this.addedChildOf(item);
|
|
500
|
+
if (target !== void 0) targets.push(target);
|
|
501
|
+
}
|
|
502
|
+
return findPurged(this.purgeLookup(trx), targets);
|
|
503
|
+
}
|
|
504
|
+
namesPurgedTarget(item, purgedTargets) {
|
|
505
|
+
const target = this.addedChildOf(item);
|
|
506
|
+
return target !== void 0 && purgedTargets.has(target);
|
|
507
|
+
}
|
|
508
|
+
addedChildOf(item) {
|
|
509
|
+
const action = item.operation.action;
|
|
510
|
+
if (action.type !== "ADD_RELATIONSHIP") return void 0;
|
|
511
|
+
if (item.context.documentType !== "powerhouse/reactor-drive") return;
|
|
512
|
+
const input = action.input;
|
|
513
|
+
if (input.relationshipType !== "drive/child") return;
|
|
514
|
+
return input.targetId;
|
|
515
|
+
}
|
|
460
516
|
async applyNameOperation(trx, item) {
|
|
461
517
|
const action = item.operation.action;
|
|
462
518
|
const docId = item.context.documentId;
|
|
@@ -506,8 +562,12 @@ var NodeProcessor = class extends BaseReadModel {
|
|
|
506
562
|
}
|
|
507
563
|
}
|
|
508
564
|
async applyDeleteDocument(trx, item) {
|
|
509
|
-
const
|
|
510
|
-
await
|
|
565
|
+
const input = item.operation.action.input;
|
|
566
|
+
await this.eraseDocument(trx, input.documentId || item.context.documentId);
|
|
567
|
+
}
|
|
568
|
+
/** The id's nodes in every drive, a drive's own tree, and its name. */
|
|
569
|
+
async eraseDocument(trx, docId) {
|
|
570
|
+
await trx.deleteFrom("DriveNode").where((eb) => eb.or([eb("id", "=", docId), eb("driveId", "=", docId)])).execute();
|
|
511
571
|
await trx.deleteFrom("DocumentName").where("docId", "=", docId).execute();
|
|
512
572
|
}
|
|
513
573
|
async handleAddFileRelationship(trx, driveId, input) {
|
|
@@ -640,6 +700,7 @@ function decodeKeysetCursor(cursor) {
|
|
|
640
700
|
};
|
|
641
701
|
}
|
|
642
702
|
var DriveNodeView = class {
|
|
703
|
+
db;
|
|
643
704
|
constructor(db) {
|
|
644
705
|
this.db = db;
|
|
645
706
|
}
|
|
@@ -737,10 +798,10 @@ var ReactorDriveClient = class {
|
|
|
737
798
|
this.readModel = args.readModel;
|
|
738
799
|
}
|
|
739
800
|
async create(input, signal) {
|
|
740
|
-
const driveDoc = reactorDriveCreateDocument({ global: {
|
|
801
|
+
const driveDoc = withSignaturePolicy(reactorDriveCreateDocument({ global: {
|
|
741
802
|
name: input.global.name,
|
|
742
803
|
icon: input.global.icon ?? null
|
|
743
|
-
} });
|
|
804
|
+
} }), requestedSignaturePolicy(input, await this.reactor.getCreateSignaturePolicy()), { protocolVersions: input.protocolVersions });
|
|
744
805
|
if (input.local) {
|
|
745
806
|
if (typeof input.local.sharingType === "string") driveDoc.state.local.sharingType = input.local.sharingType;
|
|
746
807
|
if (typeof input.local.availableOffline === "boolean") driveDoc.state.local.availableOffline = input.local.availableOffline;
|
|
@@ -873,11 +934,12 @@ var ReactorDriveClient = class {
|
|
|
873
934
|
if (targetParentFolderId !== void 0 && subtree.some((n) => n.id === targetParentFolderId)) throw new Error(`Cannot copy node ${srcNodeId} into itself or one of its descendants (target: ${targetParentFolderId})`);
|
|
874
935
|
const idMap = /* @__PURE__ */ new Map();
|
|
875
936
|
for (const node of subtree) idMap.set(node.id, generateId());
|
|
937
|
+
const policy = await this.reactor.getCreateSignaturePolicy();
|
|
876
938
|
const jobs = [];
|
|
877
939
|
const driveActions = [];
|
|
878
940
|
const fileCreateKeys = [];
|
|
879
941
|
for (const node of subtree) {
|
|
880
|
-
|
|
942
|
+
let newId = idMap.get(node.id);
|
|
881
943
|
let newParent;
|
|
882
944
|
if (node.id === srcNodeId) newParent = targetParentFolderId ?? null;
|
|
883
945
|
else {
|
|
@@ -902,7 +964,9 @@ var ReactorDriveClient = class {
|
|
|
902
964
|
const reducers = {};
|
|
903
965
|
for (const m of modulesForType) reducers[m.version ?? 1] = m.reducer;
|
|
904
966
|
const config = { reducers };
|
|
905
|
-
const
|
|
967
|
+
const replayHeader = createCopyHeader(srcDoc.header, newId, policy);
|
|
968
|
+
newId = replayHeader.id;
|
|
969
|
+
const duplicated = replayDocumentVersioned(srcDoc.initialState, srcDoc.operations, config, replayHeader);
|
|
906
970
|
assertAuthPreservedOnDuplicate(node.id, srcDoc.state.auth, duplicated.state.auth);
|
|
907
971
|
duplicated.header.name = node.name;
|
|
908
972
|
const createInput = {
|
|
@@ -970,8 +1034,7 @@ var ReactorDriveClient = class {
|
|
|
970
1034
|
return {
|
|
971
1035
|
results: page.results.map((node) => this.toLegacyNode(node)),
|
|
972
1036
|
options: page.options,
|
|
973
|
-
...page.nextCursor !== void 0 ? { nextCursor: page.nextCursor } : {}
|
|
974
|
-
...page.totalCount !== void 0 ? { totalCount: page.totalCount } : {}
|
|
1037
|
+
...page.nextCursor !== void 0 ? { nextCursor: page.nextCursor } : {}
|
|
975
1038
|
};
|
|
976
1039
|
}
|
|
977
1040
|
toLegacyNode(node) {
|
|
@@ -1013,7 +1076,8 @@ var ReactorDriveClient = class {
|
|
|
1013
1076
|
if (cached !== void 0) return cached;
|
|
1014
1077
|
const folder = folders.find((f) => f.id === id);
|
|
1015
1078
|
if (!folder) return 0;
|
|
1016
|
-
const
|
|
1079
|
+
const parent = folder.parentFolder ?? rootId;
|
|
1080
|
+
const depth = compute(parent) + 1;
|
|
1017
1081
|
depthById.set(id, depth);
|
|
1018
1082
|
return depth;
|
|
1019
1083
|
};
|
|
@@ -1058,15 +1122,16 @@ const typeDefs = gql`
|
|
|
1058
1122
|
results: [ReactorDriveNode!]!
|
|
1059
1123
|
nextCursor: String
|
|
1060
1124
|
hasMore: Boolean!
|
|
1061
|
-
totalCount: Int
|
|
1062
1125
|
}
|
|
1063
1126
|
|
|
1064
1127
|
type ReactorDrive {
|
|
1065
1128
|
id: ID!
|
|
1066
1129
|
name: String!
|
|
1067
1130
|
icon: String
|
|
1068
|
-
|
|
1069
|
-
|
|
1131
|
+
"Null when the caller may not read the drive's local scope."
|
|
1132
|
+
sharingType: String
|
|
1133
|
+
"Null when the caller may not read the drive's local scope."
|
|
1134
|
+
availableOffline: Boolean
|
|
1070
1135
|
rootNodes(
|
|
1071
1136
|
paging: ReactorDrivePagingInput
|
|
1072
1137
|
kind: ReactorDriveNodeKind
|
|
@@ -1101,42 +1166,87 @@ function shapePage(page) {
|
|
|
1101
1166
|
return {
|
|
1102
1167
|
results: page.results,
|
|
1103
1168
|
nextCursor: page.nextCursor,
|
|
1104
|
-
hasMore: page.nextCursor !== void 0
|
|
1105
|
-
|
|
1169
|
+
hasMore: page.nextCursor !== void 0
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
function subjectOf(ctx) {
|
|
1173
|
+
return {
|
|
1174
|
+
address: ctx.user?.address,
|
|
1175
|
+
key: ctx.user?.appKey
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
/** Whether a listing would serve the document to the caller; fails closed. */
|
|
1179
|
+
async function serves(ctx, documentId) {
|
|
1180
|
+
try {
|
|
1181
|
+
if (ctx.hostCanRead && !await ctx.hostCanRead(documentId, ctx.user?.address)) return false;
|
|
1182
|
+
return await ctx.reactorClient.isServed(documentId, { subject: subjectOf(ctx) });
|
|
1183
|
+
} catch {
|
|
1184
|
+
return false;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
/** A file node names its document, so it is served only with that document. */
|
|
1188
|
+
async function servesNode(ctx, node) {
|
|
1189
|
+
return node.kind === "folder" || serves(ctx, node.id);
|
|
1190
|
+
}
|
|
1191
|
+
async function servedNodes(ctx, nodes) {
|
|
1192
|
+
const served = await Promise.all(nodes.map((node) => servesNode(ctx, node)));
|
|
1193
|
+
return nodes.filter((_, i) => served[i]);
|
|
1194
|
+
}
|
|
1195
|
+
async function servedPage(ctx, page) {
|
|
1196
|
+
return {
|
|
1197
|
+
...page,
|
|
1198
|
+
results: await servedNodes(ctx, page.results)
|
|
1106
1199
|
};
|
|
1107
1200
|
}
|
|
1108
1201
|
/**
|
|
1109
|
-
* Builds GraphQL resolvers backed by the drive read model.
|
|
1110
|
-
*
|
|
1111
|
-
*
|
|
1112
|
-
*
|
|
1202
|
+
* Builds GraphQL resolvers backed by the drive read model. Dependencies are
|
|
1203
|
+
* read off the GraphQL context unless bound here. Every read is decided as the
|
|
1204
|
+
* caller: a drive's nodes are its content, so they are served only with the
|
|
1205
|
+
* drive, and a file node only with its own document too.
|
|
1113
1206
|
*/
|
|
1114
|
-
function createReactorDriveResolvers() {
|
|
1207
|
+
function createReactorDriveResolvers(bound = {}) {
|
|
1208
|
+
const withBound = (ctx) => ({
|
|
1209
|
+
...ctx,
|
|
1210
|
+
...bound,
|
|
1211
|
+
user: ctx.user
|
|
1212
|
+
});
|
|
1115
1213
|
return {
|
|
1116
1214
|
Query: {
|
|
1117
|
-
async reactorDrive(_root, args,
|
|
1118
|
-
const
|
|
1215
|
+
async reactorDrive(_root, args, context) {
|
|
1216
|
+
const ctx = withBound(context);
|
|
1217
|
+
const document = await ctx.reactorClient.get(args.id, { subject: subjectOf(ctx) });
|
|
1119
1218
|
if (document.header.documentType !== "powerhouse/reactor-drive") return null;
|
|
1219
|
+
if (!await serves(ctx, document.header.id)) return null;
|
|
1220
|
+
const { global, local } = document.state;
|
|
1221
|
+
if (!global) return null;
|
|
1120
1222
|
return {
|
|
1121
1223
|
id: document.header.id,
|
|
1122
|
-
name:
|
|
1123
|
-
icon:
|
|
1124
|
-
sharingType:
|
|
1125
|
-
availableOffline:
|
|
1224
|
+
name: global.name,
|
|
1225
|
+
icon: global.icon,
|
|
1226
|
+
sharingType: local?.sharingType ?? null,
|
|
1227
|
+
availableOffline: local?.availableOffline ?? null
|
|
1126
1228
|
};
|
|
1127
1229
|
},
|
|
1128
|
-
async reactorDriveNode(_root, args,
|
|
1129
|
-
|
|
1230
|
+
async reactorDriveNode(_root, args, context) {
|
|
1231
|
+
const ctx = withBound(context);
|
|
1232
|
+
if (!await serves(ctx, args.driveId)) return;
|
|
1233
|
+
const node = await ctx.readModel.getNode(args.driveId, args.id);
|
|
1234
|
+
if (!node || !await servesNode(ctx, node)) return;
|
|
1235
|
+
return node;
|
|
1130
1236
|
},
|
|
1131
|
-
async reactorDriveDescendants(_root, args,
|
|
1132
|
-
|
|
1237
|
+
async reactorDriveDescendants(_root, args, context) {
|
|
1238
|
+
const ctx = withBound(context);
|
|
1239
|
+
if (!await serves(ctx, args.driveId)) return [];
|
|
1240
|
+
return servedNodes(ctx, await ctx.readModel.getDescendants(args.driveId, args.root));
|
|
1133
1241
|
}
|
|
1134
1242
|
},
|
|
1135
|
-
ReactorDrive: { async rootNodes(parent, args,
|
|
1136
|
-
|
|
1243
|
+
ReactorDrive: { async rootNodes(parent, args, context) {
|
|
1244
|
+
const ctx = withBound(context);
|
|
1245
|
+
return shapePage(await servedPage(ctx, filterByKind(await ctx.readModel.listChildren(parent.id, null, toPaging(args.paging)), args.kind)));
|
|
1137
1246
|
} },
|
|
1138
|
-
ReactorDriveFolderNode: { async children(parent, args,
|
|
1139
|
-
|
|
1247
|
+
ReactorDriveFolderNode: { async children(parent, args, context) {
|
|
1248
|
+
const ctx = withBound(context);
|
|
1249
|
+
return shapePage(await servedPage(ctx, filterByKind(await ctx.readModel.listChildren(parent.driveId, parent.id, toPaging(args.paging)), args.kind)));
|
|
1140
1250
|
} },
|
|
1141
1251
|
ReactorDriveNode: { __resolveType(node) {
|
|
1142
1252
|
return node.kind === "file" ? "ReactorDriveFileNode" : "ReactorDriveFolderNode";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["up","down","migration0001","migration0002","DEFAULT_LIMIT"],"sources":["../src/constants.ts","../src/actions.ts","../src/reducer/drive.ts","../src/module.ts","../src/schema/migrations/0001_drive_node.ts","../src/schema/migrations/0002_document_name.ts","../src/schema/migrations/migrator.ts","../src/processors/utils/collisions.ts","../src/processors/node-processor.ts","../src/read-model/drive-node-view.ts","../src/client/reactor-drive-client.ts","../src/subgraph/schema.ts","../src/subgraph/resolvers.ts","../src/migration/migrate-legacy-state.ts"],"sourcesContent":["export const REACTOR_DRIVE_DOCUMENT_TYPE = \"powerhouse/reactor-drive\";\n\nexport const DRIVE_CHILD_RELATIONSHIP_TYPE = \"drive/child\";\n\nexport const REACTOR_DRIVE_FILE_EXTENSION = \"phrd\";\n","import type { Action } from \"@powerhousedao/shared/document-model\";\nimport { generateId } from \"@powerhousedao/shared/document-model\";\nimport type {\n AddFolderActionInput,\n RemoveFolderActionInput,\n UpdateFolderActionInput,\n} from \"./types.js\";\n\nexport type SetDriveNameInput = { name: string };\nexport type SetDriveIconInput = { icon: string | null };\nexport type SetSharingTypeInput = { sharingType: string };\nexport type SetAvailableOfflineInput = { availableOffline: boolean };\n\nexport function setDriveNameAction(input: SetDriveNameInput): Action {\n return {\n id: generateId(),\n type: \"SET_DRIVE_NAME\",\n scope: \"global\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\nexport function setDriveIconAction(input: SetDriveIconInput): Action {\n return {\n id: generateId(),\n type: \"SET_DRIVE_ICON\",\n scope: \"global\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\nexport function setSharingTypeAction(input: SetSharingTypeInput): Action {\n return {\n id: generateId(),\n type: \"SET_SHARING_TYPE\",\n scope: \"local\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\nexport function setAvailableOfflineAction(\n input: SetAvailableOfflineInput,\n): Action {\n return {\n id: generateId(),\n type: \"SET_AVAILABLE_OFFLINE\",\n scope: \"local\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\nexport function addFolderAction(input: AddFolderActionInput): Action {\n return {\n id: generateId(),\n type: \"ADD_FOLDER\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\nexport function updateFolderAction(input: UpdateFolderActionInput): Action {\n return {\n id: generateId(),\n type: \"UPDATE_FOLDER\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\nexport function removeFolderAction(input: RemoveFolderActionInput): Action {\n return {\n id: generateId(),\n type: \"REMOVE_FOLDER\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\nexport const reactorDriveActions = {\n setDriveName: setDriveNameAction,\n setDriveIcon: setDriveIconAction,\n setSharingType: setSharingTypeAction,\n setAvailableOffline: setAvailableOfflineAction,\n addFolder: addFolderAction,\n updateFolder: updateFolderAction,\n removeFolder: removeFolderAction,\n};\n","import type {\n Action,\n StateReducer,\n} from \"@powerhousedao/shared/document-model\";\nimport { isDocumentAction } from \"@powerhousedao/shared/document-model\";\nimport type {\n SetAvailableOfflineInput,\n SetDriveIconInput,\n SetDriveNameInput,\n SetSharingTypeInput,\n} from \"../actions.js\";\nimport type { ReactorDrivePHState } from \"../types.js\";\n\ntype TypedAction = Action & { type: string };\n\nexport const reactorDriveStateReducer: StateReducer<ReactorDrivePHState> = (\n state,\n action,\n) => {\n if (isDocumentAction(action)) {\n return state;\n }\n\n const typedAction = action as TypedAction;\n\n switch (typedAction.type) {\n case \"SET_DRIVE_NAME\": {\n const input = typedAction.input as SetDriveNameInput;\n state.global.name = input.name;\n return state;\n }\n case \"SET_DRIVE_ICON\": {\n const input = typedAction.input as SetDriveIconInput;\n state.global.icon = input.icon;\n return state;\n }\n case \"SET_SHARING_TYPE\": {\n const input = typedAction.input as SetSharingTypeInput;\n state.local.sharingType = input.sharingType;\n return state;\n }\n case \"SET_AVAILABLE_OFFLINE\": {\n const input = typedAction.input as SetAvailableOfflineInput;\n state.local.availableOffline = input.availableOffline;\n return state;\n }\n case \"ADD_FOLDER\":\n case \"UPDATE_FOLDER\":\n case \"REMOVE_FOLDER\":\n // Identity: folder ops are recorded for sync and materialized by NodeProcessor.\n return state;\n default:\n return state;\n }\n};\n","import type {\n AssertIsDocumentOfType,\n AssertIsStateOfType,\n CreateDocument,\n CreateState,\n DocumentModelGlobalState,\n IsDocumentOfType,\n IsStateOfType,\n LoadFromInput,\n PHDocument,\n SaveToFileHandle,\n} from \"@powerhousedao/shared/document-model\";\nimport {\n baseCreateDocument,\n baseLoadFromInput,\n baseSaveToFileHandle,\n createReducer,\n createState,\n defaultBaseState,\n} from \"@powerhousedao/shared/document-model\";\nimport { reactorDriveActions } from \"./actions.js\";\nimport {\n REACTOR_DRIVE_DOCUMENT_TYPE,\n REACTOR_DRIVE_FILE_EXTENSION,\n} from \"./constants.js\";\nimport { reactorDriveStateReducer } from \"./reducer/drive.js\";\nimport type {\n ReactorDriveDocumentModelModule,\n ReactorDriveGlobalState,\n ReactorDriveLocalState,\n ReactorDrivePHState,\n} from \"./types.js\";\n\nconst initialGlobalState: ReactorDriveGlobalState = {\n name: \"\",\n icon: null,\n};\n\nconst initialLocalState: ReactorDriveLocalState = {\n sharingType: \"private\",\n availableOffline: false,\n};\n\nexport const reactorDriveCreateState: CreateState<ReactorDrivePHState> = (\n state,\n) => {\n return {\n ...defaultBaseState(),\n global: { ...initialGlobalState, ...state?.global },\n local: { ...initialLocalState, ...state?.local },\n };\n};\n\nexport const reactorDriveCreateDocument: CreateDocument<ReactorDrivePHState> = (\n state,\n) => {\n return baseCreateDocument(\n reactorDriveCreateState,\n state,\n REACTOR_DRIVE_DOCUMENT_TYPE,\n );\n};\n\nconst reactorDriveSaveToFileHandle: SaveToFileHandle = (document, input) => {\n return baseSaveToFileHandle(document, input);\n};\n\nconst reactorDriveLoadFromInput: LoadFromInput<ReactorDrivePHState> = (\n input,\n) => {\n return baseLoadFromInput(input, reactorDriveDocumentReducer);\n};\n\nconst isReactorDriveState: IsStateOfType<ReactorDrivePHState> = (\n state,\n): state is ReactorDrivePHState => {\n return (\n typeof state === \"object\" &&\n state !== null &&\n \"global\" in state &&\n \"local\" in state\n );\n};\n\nconst assertIsReactorDriveState: AssertIsStateOfType<ReactorDrivePHState> = (\n state,\n) => {\n if (!isReactorDriveState(state)) {\n throw new Error(\"Not a reactor-drive state\");\n }\n};\n\nconst isReactorDriveDocument: IsDocumentOfType<ReactorDrivePHState> = (\n document,\n): document is PHDocument<ReactorDrivePHState> => {\n return (\n typeof document === \"object\" &&\n document !== null &&\n \"header\" in document &&\n (document as { header: { documentType: string } }).header.documentType ===\n REACTOR_DRIVE_DOCUMENT_TYPE\n );\n};\n\nconst assertIsReactorDriveDocument: AssertIsDocumentOfType<\n ReactorDrivePHState\n> = (document) => {\n if (!isReactorDriveDocument(document)) {\n throw new Error(\"Not a reactor-drive document\");\n }\n};\n\nexport const reactorDriveDocumentReducer = createReducer<ReactorDrivePHState>(\n reactorDriveStateReducer,\n);\n\nconst reactorDriveDocumentGlobalState: DocumentModelGlobalState = {\n id: REACTOR_DRIVE_DOCUMENT_TYPE,\n name: \"ReactorDrive\",\n extension: REACTOR_DRIVE_FILE_EXTENSION,\n description: \"\",\n author: {\n name: \"Powerhouse Inc\",\n website: \"https://www.powerhouse.inc/\",\n },\n specifications: [\n {\n version: 1,\n changeLog: [],\n state: {\n global: {\n schema:\n \"type ReactorDriveState {\\n name: String!\\n icon: String\\n}\",\n // Double-encoded by convention: `initialValue` holds a JSON-encoded string that consumers parse with JSON.parse.\n initialValue: JSON.stringify(JSON.stringify(initialGlobalState)),\n examples: [],\n },\n local: {\n schema:\n \"type ReactorDriveLocalState {\\n sharingType: String!\\n availableOffline: Boolean!\\n}\",\n // Double-encoded by convention: `initialValue` holds a JSON-encoded string that consumers parse with JSON.parse.\n initialValue: JSON.stringify(JSON.stringify(initialLocalState)),\n examples: [],\n },\n },\n modules: [\n {\n id: \"reactor-drive/base-operations\",\n name: \"base_operations\",\n description: \"\",\n operations: [\n {\n id: \"SET_DRIVE_NAME\",\n name: \"SET_DRIVE_NAME\",\n description: \"\",\n schema: \"input SetDriveNameInput { name: String! }\",\n template: \"\",\n reducer: \"\",\n errors: [],\n examples: [],\n scope: \"global\",\n },\n {\n id: \"SET_DRIVE_ICON\",\n name: \"SET_DRIVE_ICON\",\n description: \"\",\n schema: \"input SetDriveIconInput { icon: String }\",\n template: \"\",\n reducer: \"\",\n errors: [],\n examples: [],\n scope: \"global\",\n },\n {\n id: \"SET_SHARING_TYPE\",\n name: \"SET_SHARING_TYPE\",\n description: \"\",\n schema: \"input SetSharingTypeInput { sharingType: String! }\",\n template: \"\",\n reducer: \"\",\n errors: [],\n examples: [],\n scope: \"local\",\n },\n {\n id: \"SET_AVAILABLE_OFFLINE\",\n name: \"SET_AVAILABLE_OFFLINE\",\n description: \"\",\n schema:\n \"input SetAvailableOfflineInput { availableOffline: Boolean! }\",\n template: \"\",\n reducer: \"\",\n errors: [],\n examples: [],\n scope: \"local\",\n },\n {\n id: \"ADD_FOLDER\",\n name: \"ADD_FOLDER\",\n description: \"\",\n schema:\n \"input AddFolderInput { folderId: String! parentFolderId: String name: String! }\",\n template: \"\",\n reducer: \"\",\n errors: [],\n examples: [],\n scope: \"document\",\n },\n {\n id: \"UPDATE_FOLDER\",\n name: \"UPDATE_FOLDER\",\n description: \"\",\n schema:\n \"input UpdateFolderInput { folderId: String! name: String parentFolderId: String }\",\n template: \"\",\n reducer: \"\",\n errors: [],\n examples: [],\n scope: \"document\",\n },\n {\n id: \"REMOVE_FOLDER\",\n name: \"REMOVE_FOLDER\",\n description: \"\",\n schema: \"input RemoveFolderInput { folderId: String! }\",\n template: \"\",\n reducer: \"\",\n errors: [],\n examples: [],\n scope: \"document\",\n },\n ],\n },\n ],\n },\n ],\n};\n\nexport const reactorDriveDocumentModelModule: ReactorDriveDocumentModelModule =\n {\n actions: reactorDriveActions,\n reducer: reactorDriveDocumentReducer,\n documentModel: createState(\n defaultBaseState(),\n reactorDriveDocumentGlobalState,\n ),\n utils: {\n fileExtension: REACTOR_DRIVE_FILE_EXTENSION,\n createState: reactorDriveCreateState,\n createDocument: reactorDriveCreateDocument,\n loadFromInput: reactorDriveLoadFromInput,\n saveToFileHandle: reactorDriveSaveToFileHandle,\n isStateOfType: isReactorDriveState,\n assertIsStateOfType: assertIsReactorDriveState,\n isDocumentOfType: isReactorDriveDocument,\n assertIsDocumentOfType: assertIsReactorDriveDocument,\n },\n };\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n await db.schema\n .createTable(\"DriveNode\")\n .ifNotExists()\n .addColumn(\"driveId\", \"text\", (col) => col.notNull())\n .addColumn(\"id\", \"text\", (col) => col.notNull())\n .addColumn(\"kind\", \"text\", (col) => col.notNull())\n .addColumn(\"name\", \"text\", (col) => col.notNull())\n .addColumn(\"requestedName\", \"text\", (col) => col.notNull())\n .addColumn(\"parentFolder\", \"text\")\n .addColumn(\"documentType\", \"text\")\n .addColumn(\"createdAt\", \"timestamptz\", (col) =>\n col.notNull().defaultTo(sql`NOW()`),\n )\n .addColumn(\"updatedAt\", \"timestamptz\", (col) =>\n col.notNull().defaultTo(sql`NOW()`),\n )\n .addPrimaryKeyConstraint(\"pk_drive_node\", [\"driveId\", \"id\"])\n .addCheckConstraint(\n \"chk_drive_node_document_type\",\n sql`(kind = 'file' AND \"documentType\" IS NOT NULL) OR (kind = 'folder' AND \"documentType\" IS NULL)`,\n )\n .execute();\n\n await db.schema\n .createIndex(\"idx_drive_node_parent_name\")\n .ifNotExists()\n .on(\"DriveNode\")\n .columns([\"driveId\", \"parentFolder\", \"name\"])\n .execute();\n\n await db.schema\n .createIndex(\"idx_drive_node_parent_kind_id\")\n .ifNotExists()\n .on(\"DriveNode\")\n .columns([\"driveId\", \"parentFolder\", \"kind\", \"id\"])\n .execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n await db.schema.dropTable(\"DriveNode\").execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n await db.schema\n .createTable(\"DocumentName\")\n .ifNotExists()\n .addColumn(\"docId\", \"text\", (col) => col.primaryKey())\n .addColumn(\"name\", \"text\", (col) => col.notNull())\n .addColumn(\"updatedAt\", \"timestamptz\", (col) =>\n col.notNull().defaultTo(sql`NOW()`),\n )\n .execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n await db.schema.dropTable(\"DocumentName\").execute();\n}\n","import type { Kysely, MigrationProvider } from \"kysely\";\nimport { Migrator, sql } from \"kysely\";\nimport * as migration0001 from \"./0001_drive_node.js\";\nimport * as migration0002 from \"./0002_document_name.js\";\n\nconst migrations = {\n \"0001_drive_node\": migration0001,\n \"0002_document_name\": migration0002,\n};\n\nconst REACTOR_DRIVE_MIGRATION_TABLE = \"kysely_migration_reactor_drive\";\nconst REACTOR_DRIVE_MIGRATION_LOCK_TABLE =\n \"kysely_migration_reactor_drive_lock\";\n\nexport interface ReactorDriveMigrationResult {\n success: boolean;\n migrationsExecuted: string[];\n error?: Error;\n}\n\nclass ProgrammaticMigrationProvider implements MigrationProvider {\n getMigrations() {\n return Promise.resolve(migrations);\n }\n}\n\nexport async function runReactorDriveMigrations(\n db: Kysely<unknown>,\n schema: string,\n): Promise<ReactorDriveMigrationResult> {\n try {\n await sql`CREATE SCHEMA IF NOT EXISTS ${sql.id(schema)}`.execute(db);\n } catch (error) {\n return {\n success: false,\n migrationsExecuted: [],\n error:\n error instanceof Error ? error : new Error(\"Failed to create schema\"),\n };\n }\n\n const migrator = new Migrator({\n db: db.withSchema(schema),\n provider: new ProgrammaticMigrationProvider(),\n migrationTableSchema: schema,\n migrationTableName: REACTOR_DRIVE_MIGRATION_TABLE,\n migrationLockTableName: REACTOR_DRIVE_MIGRATION_LOCK_TABLE,\n });\n\n let error: unknown;\n let results: Awaited<ReturnType<typeof migrator.migrateToLatest>>[\"results\"];\n try {\n const result = await migrator.migrateToLatest();\n error = result.error;\n results = result.results;\n } catch (e) {\n error = e;\n results = [];\n }\n\n const migrationsExecuted =\n results?.map((result) => result.migrationName) ?? [];\n\n if (error) {\n return {\n success: false,\n migrationsExecuted,\n error:\n error instanceof Error ? error : new Error(\"Unknown migration error\"),\n };\n }\n\n return {\n success: true,\n migrationsExecuted,\n };\n}\n\nexport async function getReactorDriveMigrationStatus(\n db: Kysely<unknown>,\n schema: string,\n) {\n const migrator = new Migrator({\n db: db.withSchema(schema),\n provider: new ProgrammaticMigrationProvider(),\n migrationTableSchema: schema,\n migrationTableName: REACTOR_DRIVE_MIGRATION_TABLE,\n migrationLockTableName: REACTOR_DRIVE_MIGRATION_LOCK_TABLE,\n });\n\n return await migrator.getMigrations();\n}\n","/**\n * Deterministic per-folder name collision rule. Given a desired name and the\n * set of names already taken by siblings, returns the next available name as\n * `requested`, `requested (2)`, `requested (3)`, etc.\n *\n * The rule matches the legacy document-drive module so that migration from\n * the legacy state produces stable suffixes.\n */\nexport function resolveCollision(\n requested: string,\n takenNames: Iterable<string>,\n): string {\n const taken = new Set<string>();\n for (const name of takenNames) {\n taken.add(name);\n }\n\n if (!taken.has(requested)) {\n return requested;\n }\n\n let suffix = 2;\n while (taken.has(`${requested} (${suffix})`)) {\n suffix += 1;\n }\n return `${requested} (${suffix})`;\n}\n","import type { IOperationIndex, IWriteCache } from \"@powerhousedao/reactor\";\nimport {\n BaseReadModel,\n type DocumentViewDatabase,\n type IConsistencyTracker,\n} from \"@powerhousedao/reactor\";\nimport type {\n AddRelationshipActionInput,\n CreateDocumentActionInput,\n DeleteDocumentActionInput,\n OperationWithContext,\n RemoveRelationshipActionInput,\n SetNameActionInput,\n UpgradeDocumentActionInput,\n} from \"@powerhousedao/shared/document-model\";\nimport type { Kysely, Transaction } from \"kysely\";\nimport {\n DRIVE_CHILD_RELATIONSHIP_TYPE,\n REACTOR_DRIVE_DOCUMENT_TYPE,\n} from \"../constants.js\";\nimport { runReactorDriveMigrations } from \"../schema/migrations/migrator.js\";\nimport type { ReactorDriveDatabase } from \"../schema/tables.js\";\nimport type {\n AddFolderActionInput,\n DriveChildFileMetadata,\n RemoveFolderActionInput,\n UpdateFolderActionInput,\n} from \"../types.js\";\nimport { resolveCollision } from \"./utils/collisions.js\";\n\nexport type NodeProcessorDatabase = ReactorDriveDatabase & DocumentViewDatabase;\n\nconst NAME_ACTION_TYPES = new Set([\n \"CREATE_DOCUMENT\",\n \"UPGRADE_DOCUMENT\",\n \"SET_NAME\",\n]);\n\nconst STRUCTURE_ACTION_TYPES = new Set([\n \"ADD_RELATIONSHIP\",\n \"REMOVE_RELATIONSHIP\",\n \"ADD_FOLDER\",\n \"UPDATE_FOLDER\",\n \"REMOVE_FOLDER\",\n]);\n\nexport class NodeProcessor extends BaseReadModel {\n private readonly driveDb: Kysely<NodeProcessorDatabase>;\n private readonly baseDb: Kysely<unknown>;\n private readonly schema: string;\n\n constructor(\n baseDb: Kysely<unknown>,\n schema: string,\n operationIndex: IOperationIndex,\n writeCache: IWriteCache,\n consistencyTracker: IConsistencyTracker,\n ) {\n const scopedDb = baseDb.withSchema(\n schema,\n ) as unknown as Kysely<NodeProcessorDatabase>;\n super(\n scopedDb as unknown as Kysely<DocumentViewDatabase>,\n operationIndex,\n writeCache,\n consistencyTracker,\n {\n readModelId: \"reactor-drive-node-processor\",\n rebuildStateOnInit: false,\n },\n );\n this.driveDb = scopedDb;\n this.baseDb = baseDb;\n this.schema = schema;\n }\n\n override async init(): Promise<void> {\n const result = await runReactorDriveMigrations(this.baseDb, this.schema);\n if (!result.success && result.error) {\n throw new Error(\n `Reactor drive migrations failed: ${result.error.message}`,\n );\n }\n await super.init();\n }\n\n protected override async commitOperations(\n items: OperationWithContext[],\n ): Promise<void> {\n await this.driveDb.transaction().execute(async (trx) => {\n for (const item of items) {\n const actionType = item.operation.action.type;\n if (NAME_ACTION_TYPES.has(actionType)) {\n await this.applyNameOperation(trx, item);\n continue;\n }\n if (STRUCTURE_ACTION_TYPES.has(actionType)) {\n await this.applyStructureOperation(trx, item);\n continue;\n }\n if (actionType === \"DELETE_DOCUMENT\") {\n await this.applyDeleteDocument(trx, item);\n }\n }\n });\n }\n\n private async applyNameOperation(\n trx: Transaction<NodeProcessorDatabase>,\n item: OperationWithContext,\n ): Promise<void> {\n const action = item.operation.action;\n const docId = item.context.documentId;\n let name: string | undefined;\n\n if (action.type === \"CREATE_DOCUMENT\") {\n const input = action.input as CreateDocumentActionInput;\n name = input.name;\n } else if (action.type === \"UPGRADE_DOCUMENT\") {\n const input = action.input as UpgradeDocumentActionInput;\n const initial = (input.initialState ?? {}) as {\n header?: { name?: string };\n };\n name = initial.header?.name;\n } else if (action.type === \"SET_NAME\") {\n const input = action.input as SetNameActionInput;\n name = input.name;\n }\n\n if (name === undefined) {\n return;\n }\n\n await this.upsertDocumentName(trx, docId, name);\n\n const linkedRows = await trx\n .selectFrom(\"DriveNode\")\n .selectAll()\n .where(\"id\", \"=\", docId)\n .where(\"kind\", \"=\", \"file\")\n .execute();\n\n for (const row of linkedRows) {\n const resolved = await this.resolveSiblingName(\n trx,\n row.driveId,\n row.parentFolder,\n name,\n docId,\n );\n await trx\n .updateTable(\"DriveNode\")\n .set({\n name: resolved,\n requestedName: name,\n updatedAt: new Date(),\n })\n .where(\"driveId\", \"=\", row.driveId)\n .where(\"id\", \"=\", docId)\n .execute();\n }\n }\n\n private async applyStructureOperation(\n trx: Transaction<NodeProcessorDatabase>,\n item: OperationWithContext,\n ): Promise<void> {\n if (item.context.documentType !== REACTOR_DRIVE_DOCUMENT_TYPE) {\n return;\n }\n\n const action = item.operation.action;\n const driveId = item.context.documentId;\n\n if (action.type === \"ADD_RELATIONSHIP\") {\n const input = action.input as AddRelationshipActionInput;\n if (input.relationshipType !== DRIVE_CHILD_RELATIONSHIP_TYPE) return;\n await this.handleAddFileRelationship(trx, driveId, input);\n return;\n }\n\n if (action.type === \"REMOVE_RELATIONSHIP\") {\n const input = action.input as RemoveRelationshipActionInput;\n if (input.relationshipType !== DRIVE_CHILD_RELATIONSHIP_TYPE) return;\n await trx\n .deleteFrom(\"DriveNode\")\n .where(\"driveId\", \"=\", driveId)\n .where(\"id\", \"=\", input.targetId)\n .execute();\n return;\n }\n\n if (action.type === \"ADD_FOLDER\") {\n await this.handleAddFolder(\n trx,\n driveId,\n action.input as AddFolderActionInput,\n );\n return;\n }\n\n if (action.type === \"UPDATE_FOLDER\") {\n await this.handleUpdateFolder(\n trx,\n driveId,\n action.input as UpdateFolderActionInput,\n );\n return;\n }\n\n if (action.type === \"REMOVE_FOLDER\") {\n const input = action.input as RemoveFolderActionInput;\n await trx\n .deleteFrom(\"DriveNode\")\n .where(\"driveId\", \"=\", driveId)\n .where(\"id\", \"=\", input.folderId)\n .execute();\n }\n }\n\n private async applyDeleteDocument(\n trx: Transaction<NodeProcessorDatabase>,\n item: OperationWithContext,\n ): Promise<void> {\n const input = item.operation.action.input as DeleteDocumentActionInput;\n const docId = input.documentId || item.context.documentId;\n\n await trx.deleteFrom(\"DriveNode\").where(\"id\", \"=\", docId).execute();\n await trx.deleteFrom(\"DocumentName\").where(\"docId\", \"=\", docId).execute();\n }\n\n private async handleAddFileRelationship(\n trx: Transaction<NodeProcessorDatabase>,\n driveId: string,\n input: AddRelationshipActionInput,\n ): Promise<void> {\n const metadata = this.parseFileMetadata(input);\n const parentFolder = metadata.parentFolderId ?? null;\n const documentType = metadata.documentType;\n\n const requestedName =\n (await this.lookupDocumentName(trx, input.targetId)) ?? \"\";\n\n const resolved = await this.resolveSiblingName(\n trx,\n driveId,\n parentFolder,\n requestedName,\n input.targetId,\n );\n\n await trx\n .insertInto(\"DriveNode\")\n .values({\n driveId,\n id: input.targetId,\n kind: \"file\",\n name: resolved,\n requestedName,\n parentFolder,\n documentType,\n })\n .onConflict((oc) =>\n oc.columns([\"driveId\", \"id\"]).doUpdateSet({\n parentFolder,\n name: resolved,\n requestedName,\n kind: \"file\",\n documentType,\n updatedAt: new Date(),\n }),\n )\n .execute();\n }\n\n private async handleAddFolder(\n trx: Transaction<NodeProcessorDatabase>,\n driveId: string,\n input: AddFolderActionInput,\n ): Promise<void> {\n const parentFolder = input.parentFolderId ?? null;\n const resolved = await this.resolveSiblingName(\n trx,\n driveId,\n parentFolder,\n input.name,\n input.folderId,\n );\n\n await trx\n .insertInto(\"DriveNode\")\n .values({\n driveId,\n id: input.folderId,\n kind: \"folder\",\n name: resolved,\n requestedName: input.name,\n parentFolder,\n documentType: null,\n })\n .onConflict((oc) =>\n oc.columns([\"driveId\", \"id\"]).doUpdateSet({\n parentFolder,\n name: resolved,\n requestedName: input.name,\n kind: \"folder\",\n documentType: null,\n updatedAt: new Date(),\n }),\n )\n .execute();\n }\n\n private async handleUpdateFolder(\n trx: Transaction<NodeProcessorDatabase>,\n driveId: string,\n input: UpdateFolderActionInput,\n ): Promise<void> {\n const row = await trx\n .selectFrom(\"DriveNode\")\n .selectAll()\n .where(\"driveId\", \"=\", driveId)\n .where(\"id\", \"=\", input.folderId)\n .executeTakeFirst();\n if (!row) return;\n\n const nextParentFolder =\n input.parentFolderId === undefined\n ? row.parentFolder\n : (input.parentFolderId ?? null);\n const nextRequestedName =\n typeof input.name === \"string\" ? input.name : row.requestedName;\n\n const resolved = await this.resolveSiblingName(\n trx,\n driveId,\n nextParentFolder,\n nextRequestedName,\n input.folderId,\n );\n\n await trx\n .updateTable(\"DriveNode\")\n .set({\n parentFolder: nextParentFolder,\n name: resolved,\n requestedName: nextRequestedName,\n updatedAt: new Date(),\n })\n .where(\"driveId\", \"=\", driveId)\n .where(\"id\", \"=\", input.folderId)\n .execute();\n }\n\n private async resolveSiblingName(\n trx: Transaction<NodeProcessorDatabase>,\n driveId: string,\n parentFolder: string | null,\n requested: string,\n excludeId: string,\n ): Promise<string> {\n let query = trx\n .selectFrom(\"DriveNode\")\n .select(\"name\")\n .where(\"driveId\", \"=\", driveId)\n .where(\"id\", \"!=\", excludeId);\n query =\n parentFolder === null\n ? query.where(\"parentFolder\", \"is\", null)\n : query.where(\"parentFolder\", \"=\", parentFolder);\n\n const rows = await query.execute();\n return resolveCollision(\n requested,\n rows.map((r) => r.name),\n );\n }\n\n private async lookupDocumentName(\n trx: Transaction<NodeProcessorDatabase>,\n docId: string,\n ): Promise<string | undefined> {\n const row = await trx\n .selectFrom(\"DocumentName\")\n .select(\"name\")\n .where(\"docId\", \"=\", docId)\n .executeTakeFirst();\n return row?.name;\n }\n\n private parseFileMetadata(\n input: AddRelationshipActionInput,\n ): DriveChildFileMetadata {\n const metadata = input.metadata;\n if (\n !metadata ||\n typeof metadata !== \"object\" ||\n (metadata as { kind?: unknown }).kind !== \"file\" ||\n typeof (metadata as { documentType?: unknown }).documentType !== \"string\"\n ) {\n throw new Error(\n `ADD_RELATIONSHIP for target ${input.targetId}: missing or invalid drive/child file metadata (expected { kind: \"file\", parentFolderId, documentType })`,\n );\n }\n const typed = metadata as DriveChildFileMetadata;\n return {\n kind: \"file\",\n parentFolderId: typed.parentFolderId ?? null,\n documentType: typed.documentType,\n };\n }\n\n private async upsertDocumentName(\n trx: Transaction<NodeProcessorDatabase>,\n docId: string,\n name: string,\n ): Promise<void> {\n await trx\n .insertInto(\"DocumentName\")\n .values({ docId, name })\n .onConflict((oc) =>\n oc.column(\"docId\").doUpdateSet({ name, updatedAt: new Date() }),\n )\n .execute();\n }\n}\n","import { type PagedResults, type PagingOptions } from \"@powerhousedao/reactor\";\nimport type { Kysely } from \"kysely\";\nimport type { ReactorDriveDatabase } from \"../schema/tables.js\";\nimport type {\n ReactorDriveFileNode,\n ReactorDriveFolderNode,\n ReactorDriveNode,\n} from \"../types.js\";\nimport type { IDriveReadModel } from \"./interfaces.js\";\n\nconst DEFAULT_LIMIT = 100;\n\ntype KeysetCursor = {\n createdAt: Date;\n id: string;\n};\n\nfunction parseListChildrenPaging(paging: PagingOptions | undefined): {\n limit: number;\n cursor: KeysetCursor | null;\n} {\n if (paging === undefined) {\n return { limit: DEFAULT_LIMIT, cursor: null };\n }\n if (!Number.isInteger(paging.limit) || paging.limit < 1) {\n throw new Error(\n `Invalid paging limit: ${String(paging.limit)} (must be an integer >= 1)`,\n );\n }\n if (paging.cursor === \"\") {\n return { limit: paging.limit, cursor: null };\n }\n return { limit: paging.limit, cursor: decodeKeysetCursor(paging.cursor) };\n}\n\nfunction encodeKeysetCursor(createdAt: Date, id: string): string {\n return globalThis.btoa(\n JSON.stringify({ createdAt: createdAt.toISOString(), id }),\n );\n}\n\nfunction decodeKeysetCursor(cursor: string): KeysetCursor {\n let decoded: string;\n try {\n decoded = globalThis.atob(cursor);\n } catch {\n throw new Error(\n `Invalid paging cursor: ${JSON.stringify(cursor)} (must be a keyset cursor returned by a prior page)`,\n );\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(decoded);\n } catch {\n throw new Error(\n `Invalid paging cursor: ${JSON.stringify(cursor)} (must be a keyset cursor returned by a prior page)`,\n );\n }\n if (\n parsed === null ||\n typeof parsed !== \"object\" ||\n typeof (parsed as { createdAt?: unknown }).createdAt !== \"string\" ||\n typeof (parsed as { id?: unknown }).id !== \"string\"\n ) {\n throw new Error(\n `Invalid paging cursor: ${JSON.stringify(cursor)} (must be a keyset cursor returned by a prior page)`,\n );\n }\n const createdAt = new Date((parsed as { createdAt: string }).createdAt);\n if (Number.isNaN(createdAt.getTime())) {\n throw new Error(\n `Invalid paging cursor: ${JSON.stringify(cursor)} (createdAt is not a valid timestamp)`,\n );\n }\n return { createdAt, id: (parsed as { id: string }).id };\n}\n\nexport class DriveNodeView implements IDriveReadModel {\n constructor(private readonly db: Kysely<ReactorDriveDatabase>) {}\n\n async getNode(\n driveId: string,\n nodeId: string,\n ): Promise<ReactorDriveNode | undefined> {\n const row = await this.db\n .selectFrom(\"DriveNode\")\n .selectAll()\n .where(\"driveId\", \"=\", driveId)\n .where(\"id\", \"=\", nodeId)\n .executeTakeFirst();\n if (!row) return undefined;\n return rowToNode(row);\n }\n\n async listChildren(\n driveId: string,\n parentFolder: string | null | undefined,\n paging?: PagingOptions,\n ): Promise<PagedResults<ReactorDriveNode>> {\n const { limit, cursor } = parseListChildrenPaging(paging);\n\n let query = this.db\n .selectFrom(\"DriveNode\")\n .selectAll()\n .where(\"driveId\", \"=\", driveId);\n\n if (parentFolder === null) {\n query = query.where(\"parentFolder\", \"is\", null);\n } else if (parentFolder !== undefined) {\n query = query.where(\"parentFolder\", \"=\", parentFolder);\n }\n\n if (cursor !== null) {\n query = query.where((eb) =>\n eb(\n eb.refTuple(\"createdAt\", \"id\"),\n \">\",\n eb.tuple(cursor.createdAt, cursor.id),\n ),\n );\n }\n\n const rows = await query\n .orderBy(\"createdAt\", \"asc\")\n .orderBy(\"id\", \"asc\")\n .limit(limit + 1)\n .execute();\n\n const hasMore = rows.length > limit;\n const sliced = hasMore ? rows.slice(0, limit) : rows;\n const last = sliced[sliced.length - 1];\n\n return {\n results: sliced.map(rowToNode),\n options: { cursor: paging?.cursor ?? \"\", limit },\n nextCursor: hasMore\n ? encodeKeysetCursor(last.createdAt, last.id)\n : undefined,\n };\n }\n\n async listAll(driveId: string): Promise<ReactorDriveNode[]> {\n const rows = await this.db\n .selectFrom(\"DriveNode\")\n .selectAll()\n .where(\"driveId\", \"=\", driveId)\n .orderBy(\"createdAt\", \"asc\")\n .orderBy(\"id\", \"asc\")\n .execute();\n return rows.map(rowToNode);\n }\n\n async getDescendants(\n driveId: string,\n root: string,\n ): Promise<ReactorDriveNode[]> {\n const rows = await this.db\n .withRecursive(\"descendants\", (qb) =>\n qb\n .selectFrom(\"DriveNode\")\n .select([\n \"driveId\",\n \"id\",\n \"kind\",\n \"name\",\n \"requestedName\",\n \"parentFolder\",\n \"documentType\",\n \"createdAt\",\n ])\n .where(\"driveId\", \"=\", driveId)\n .where(\"id\", \"=\", root)\n .unionAll(\n qb\n .selectFrom(\"DriveNode\")\n .innerJoin(\n \"descendants\",\n \"DriveNode.parentFolder\",\n \"descendants.id\",\n )\n .where(\"DriveNode.driveId\", \"=\", driveId)\n .select([\n \"DriveNode.driveId\",\n \"DriveNode.id\",\n \"DriveNode.kind\",\n \"DriveNode.name\",\n \"DriveNode.requestedName\",\n \"DriveNode.parentFolder\",\n \"DriveNode.documentType\",\n \"DriveNode.createdAt\",\n ]),\n ),\n )\n .selectFrom(\"descendants\")\n .select([\n \"driveId\",\n \"id\",\n \"kind\",\n \"name\",\n \"requestedName\",\n \"parentFolder\",\n \"documentType\",\n ])\n .orderBy(\"createdAt\", \"asc\")\n .orderBy(\"id\", \"asc\")\n .execute();\n return rows.map(rowToNode);\n }\n}\n\ntype DriveNodeRow = {\n driveId: string;\n id: string;\n kind: \"file\" | \"folder\";\n name: string;\n requestedName: string;\n parentFolder: string | null;\n documentType: string | null;\n};\n\nfunction rowToNode(row: DriveNodeRow): ReactorDriveNode {\n if (row.kind === \"file\") {\n if (row.documentType === null) {\n throw new Error(\n `DriveNode ${row.driveId}/${row.id}: file row has null documentType, which violates the schema CHECK constraint`,\n );\n }\n const node: ReactorDriveFileNode = {\n kind: \"file\",\n id: row.id,\n driveId: row.driveId,\n parentFolder: row.parentFolder,\n name: row.name,\n documentType: row.documentType,\n };\n return node;\n }\n const node: ReactorDriveFolderNode = {\n kind: \"folder\",\n id: row.id,\n driveId: row.driveId,\n parentFolder: row.parentFolder,\n name: row.name,\n };\n return node;\n}\n","import {\n addRelationshipAction,\n createDocumentAction,\n removeRelationshipAction,\n upgradeDocumentAction,\n type BatchExecutionRequest,\n type ExecutionJobPlan,\n type IDriveClient,\n type IReactorClient,\n type PagedResults,\n type PagingOptions,\n type PropagationMode,\n} from \"@powerhousedao/reactor\";\nimport type {\n DocumentDriveDocument,\n DriveInput,\n FileNode,\n FolderNode,\n Node,\n} from \"@powerhousedao/shared/document-drive\";\nimport {\n assertAuthPreservedOnDuplicate,\n createPresignedHeader,\n generateId,\n replayDocumentVersioned,\n type Action,\n type CreateDocumentActionInput,\n type PHBaseState,\n type PHDocument,\n type Reducer,\n type VersionedReplayConfig,\n} from \"@powerhousedao/shared/document-model\";\nimport {\n DRIVE_CHILD_RELATIONSHIP_TYPE,\n REACTOR_DRIVE_DOCUMENT_TYPE,\n} from \"../constants.js\";\nimport {\n addFolderAction,\n removeFolderAction,\n setAvailableOfflineAction,\n setDriveIconAction,\n setDriveNameAction,\n setSharingTypeAction,\n updateFolderAction,\n} from \"../actions.js\";\nimport { reactorDriveCreateDocument } from \"../module.js\";\nimport type { IDriveReadModel } from \"../read-model/interfaces.js\";\nimport type {\n DriveChildFileMetadata,\n ReactorDriveFileNode,\n ReactorDriveFolderNode,\n ReactorDriveNode,\n} from \"../types.js\";\n\nexport interface ReactorDriveClientArgs {\n reactor: IReactorClient;\n readModel: IDriveReadModel;\n}\n\n/**\n * Implementation of {@link IDriveClient} backed by the reactor's relationship\n * primitives and the drive-scoped folder actions. Folder structure lives in\n * the operation log (ADD_FOLDER/UPDATE_FOLDER/REMOVE_FOLDER + ADD_RELATIONSHIP\n * for files) and is materialised by `NodeProcessor` into the `DriveNode`\n * table consumed via {@link IDriveReadModel}.\n */\nexport class ReactorDriveClient implements IDriveClient {\n private readonly reactor: IReactorClient;\n private readonly readModel: IDriveReadModel;\n\n constructor(args: ReactorDriveClientArgs) {\n this.reactor = args.reactor;\n this.readModel = args.readModel;\n }\n\n async create(\n input: DriveInput,\n signal?: AbortSignal,\n ): Promise<DocumentDriveDocument> {\n const driveDoc = reactorDriveCreateDocument({\n global: {\n name: input.global.name,\n icon: input.global.icon ?? null,\n },\n });\n if (input.local) {\n if (typeof input.local.sharingType === \"string\") {\n driveDoc.state.local.sharingType = input.local.sharingType;\n }\n if (typeof input.local.availableOffline === \"boolean\") {\n driveDoc.state.local.availableOffline = input.local.availableOffline;\n }\n }\n if (input.preferredEditor) {\n driveDoc.header.meta = {\n ...driveDoc.header.meta,\n preferredEditor: input.preferredEditor,\n };\n }\n const created = await this.reactor.create(driveDoc, undefined, signal);\n return this.toLegacyDriveDocument(created, created.header.id);\n }\n\n async addFile<TDocument extends PHDocument = PHDocument>(\n driveIdentifier: string,\n document: PHDocument,\n parentFolder?: string,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n const documentId = document.header.id;\n\n const createInput: CreateDocumentActionInput = {\n model: document.header.documentType,\n version: 0,\n documentId,\n signing: {\n signature: documentId,\n publicKey: document.header.sig.publicKey,\n nonce: document.header.sig.nonce,\n createdAtUtcIso: document.header.createdAtUtcIso,\n documentType: document.header.documentType,\n },\n slug: document.header.slug,\n name: document.header.name,\n branch: document.header.branch,\n meta: document.header.meta,\n protocolVersions: document.header.protocolVersions ?? {\n \"base-reducer\": 2,\n },\n };\n\n const metadata: DriveChildFileMetadata = {\n kind: \"file\",\n parentFolderId: parentFolder ?? null,\n documentType: document.header.documentType,\n };\n\n const request: BatchExecutionRequest = {\n jobs: [\n {\n key: \"create\",\n documentId,\n scope: \"document\",\n branch: \"main\",\n actions: [\n createDocumentAction(createInput),\n upgradeDocumentAction({\n documentId,\n model: document.header.documentType,\n fromVersion: 0,\n toVersion: document.state.document.version,\n initialState: document.state,\n }),\n ],\n dependsOn: [],\n },\n {\n key: \"link\",\n documentId: driveIdentifier,\n scope: \"document\",\n branch: \"main\",\n actions: [\n addRelationshipAction(\n driveIdentifier,\n documentId,\n DRIVE_CHILD_RELATIONSHIP_TYPE,\n metadata,\n ),\n ],\n dependsOn: [\"create\"],\n },\n ],\n };\n\n await this.reactor.executeBatch(request, signal);\n return this.reactor.get<TDocument>(documentId, undefined, signal);\n }\n\n async addFolder(\n driveIdentifier: string,\n name: string,\n parentFolder?: string,\n signal?: AbortSignal,\n ): Promise<FolderNode> {\n const folderId = generateId();\n await this.reactor.execute(\n driveIdentifier,\n \"main\",\n [\n addFolderAction({\n folderId,\n parentFolderId: parentFolder ?? null,\n name,\n }),\n ],\n signal,\n );\n return {\n id: folderId,\n kind: \"folder\",\n name,\n parentFolder: parentFolder ?? null,\n };\n }\n\n async removeNode(\n driveIdentifier: string,\n nodeId: string,\n signal?: AbortSignal,\n ): Promise<void> {\n const node = await this.readModel.getNode(driveIdentifier, nodeId, signal);\n if (!node) {\n throw new Error(`Node ${nodeId} not found in drive ${driveIdentifier}`);\n }\n\n if (node.kind === \"folder\") {\n const subtree = await this.readModel.getDescendants(\n driveIdentifier,\n nodeId,\n signal,\n );\n const fileDescendants = subtree.filter(\n (n): n is ReactorDriveFileNode => n.kind === \"file\",\n );\n const subFolders = subtree\n .filter((n): n is ReactorDriveFolderNode => n.kind === \"folder\")\n .filter((f) => f.id !== nodeId);\n const deepestFirstFolders = this.orderDeepestFirst(subFolders, nodeId);\n\n const batch: Action[] = [\n ...fileDescendants.map((f) =>\n removeRelationshipAction(\n driveIdentifier,\n f.id,\n DRIVE_CHILD_RELATIONSHIP_TYPE,\n ),\n ),\n ...deepestFirstFolders.map((f) =>\n removeFolderAction({ folderId: f.id }),\n ),\n removeFolderAction({ folderId: nodeId }),\n ];\n\n await this.reactor.execute(driveIdentifier, \"main\", batch, signal);\n\n for (const file of fileDescendants) {\n await this.reactor.deleteDocument(\n file.id,\n \"cascade\" as PropagationMode,\n signal,\n );\n }\n return;\n }\n\n await this.reactor.execute(\n driveIdentifier,\n \"main\",\n [\n removeRelationshipAction(\n driveIdentifier,\n nodeId,\n DRIVE_CHILD_RELATIONSHIP_TYPE,\n ),\n ],\n signal,\n );\n await this.reactor.deleteDocument(nodeId, undefined, signal);\n }\n\n async renameNode(\n driveIdentifier: string,\n nodeId: string,\n name: string,\n signal?: AbortSignal,\n ): Promise<Node> {\n const node = await this.readModel.getNode(driveIdentifier, nodeId, signal);\n if (!node) {\n throw new Error(`Node ${nodeId} not found in drive ${driveIdentifier}`);\n }\n\n if (node.kind === \"file\") {\n await this.reactor.rename(nodeId, name, \"main\", signal);\n } else {\n await this.reactor.execute(\n driveIdentifier,\n \"main\",\n [updateFolderAction({ folderId: nodeId, name })],\n signal,\n );\n }\n\n const updated = await this.readModel.getNode(\n driveIdentifier,\n nodeId,\n signal,\n );\n if (!updated) {\n throw new Error(\"Node missing from drive after rename\");\n }\n return this.toLegacyNode(updated);\n }\n\n async setPreferredEditorOnNode(\n nodeId: string,\n preferredEditor: string | null,\n signal?: AbortSignal,\n ): Promise<PHDocument> {\n return this.reactor.setPreferredEditor(\n nodeId,\n preferredEditor,\n \"main\",\n signal,\n );\n }\n\n async moveNode(\n driveIdentifier: string,\n srcNodeId: string,\n targetParentFolderId: string | undefined,\n signal?: AbortSignal,\n ): Promise<DocumentDriveDocument> {\n const node = await this.readModel.getNode(\n driveIdentifier,\n srcNodeId,\n signal,\n );\n if (!node) {\n throw new Error(\n `Node ${srcNodeId} not found in drive ${driveIdentifier}`,\n );\n }\n\n if (node.kind === \"folder\") {\n await this.reactor.execute(\n driveIdentifier,\n \"main\",\n [\n updateFolderAction({\n folderId: srcNodeId,\n parentFolderId: targetParentFolderId ?? null,\n }),\n ],\n signal,\n );\n } else {\n const metadata: DriveChildFileMetadata = {\n kind: \"file\",\n parentFolderId: targetParentFolderId ?? null,\n documentType: node.documentType,\n };\n await this.reactor.execute(\n driveIdentifier,\n \"main\",\n [\n removeRelationshipAction(\n driveIdentifier,\n srcNodeId,\n DRIVE_CHILD_RELATIONSHIP_TYPE,\n ),\n addRelationshipAction(\n driveIdentifier,\n srcNodeId,\n DRIVE_CHILD_RELATIONSHIP_TYPE,\n metadata,\n ),\n ],\n signal,\n );\n }\n\n const drive = await this.reactor.get<PHDocument>(\n driveIdentifier,\n undefined,\n signal,\n );\n return this.toLegacyDriveDocument(drive, driveIdentifier);\n }\n\n async copyNode(\n driveIdentifier: string,\n srcNodeId: string,\n targetParentFolderId: string | undefined,\n signal?: AbortSignal,\n ): Promise<DocumentDriveDocument> {\n const srcNode = await this.readModel.getNode(\n driveIdentifier,\n srcNodeId,\n signal,\n );\n if (!srcNode) {\n throw new Error(\n `Node ${srcNodeId} not found in drive ${driveIdentifier}`,\n );\n }\n\n const subtree = await this.readModel.getDescendants(\n driveIdentifier,\n srcNodeId,\n signal,\n );\n\n if (\n targetParentFolderId !== undefined &&\n subtree.some((n) => n.id === targetParentFolderId)\n ) {\n throw new Error(\n `Cannot copy node ${srcNodeId} into itself or one of its descendants (target: ${targetParentFolderId})`,\n );\n }\n\n const idMap = new Map<string, string>();\n for (const node of subtree) {\n idMap.set(node.id, generateId());\n }\n\n const jobs: ExecutionJobPlan[] = [];\n const driveActions: Action[] = [];\n const fileCreateKeys: string[] = [];\n\n for (const node of subtree) {\n const newId = idMap.get(node.id)!;\n let newParent: string | null;\n if (node.id === srcNodeId) {\n newParent = targetParentFolderId ?? null;\n } else {\n if (node.parentFolder == null) {\n throw new Error(\n `copyNode: descendant ${node.id} has no parentFolder`,\n );\n }\n const mapped = idMap.get(node.parentFolder);\n if (mapped === undefined) {\n throw new Error(\n `copyNode: descendant ${node.id} parent ${node.parentFolder} missing from idMap`,\n );\n }\n newParent = mapped;\n }\n\n if (node.kind === \"folder\") {\n driveActions.push(\n addFolderAction({\n folderId: newId,\n parentFolderId: newParent,\n name: node.name,\n }),\n );\n continue;\n }\n\n const srcDoc = await this.reactor.get<PHDocument>(\n node.id,\n undefined,\n signal,\n );\n const documentType = srcDoc.header.documentType;\n const { results: allModules } =\n await this.reactor.getDocumentModelModules();\n const modulesForType = allModules.filter(\n (m) => m.documentModel.global.id === documentType,\n );\n if (modulesForType.length === 0) {\n throw new Error(\n `Document model module not found for type: ${documentType}`,\n );\n }\n const reducers: VersionedReplayConfig[\"reducers\"] = {};\n for (const m of modulesForType) {\n reducers[m.version ?? 1] = m.reducer as Reducer<PHBaseState>;\n }\n const config: VersionedReplayConfig = { reducers };\n const duplicated: PHDocument = replayDocumentVersioned(\n srcDoc.initialState,\n srcDoc.operations,\n config,\n createPresignedHeader(newId, documentType),\n );\n assertAuthPreservedOnDuplicate(\n node.id,\n srcDoc.state.auth,\n duplicated.state.auth,\n );\n duplicated.header.name = node.name;\n\n const createInput: CreateDocumentActionInput = {\n model: duplicated.header.documentType,\n version: 0,\n documentId: newId,\n signing: {\n signature: newId,\n publicKey: duplicated.header.sig.publicKey,\n nonce: duplicated.header.sig.nonce,\n createdAtUtcIso: duplicated.header.createdAtUtcIso,\n documentType: duplicated.header.documentType,\n },\n slug: duplicated.header.slug,\n name: duplicated.header.name,\n branch: duplicated.header.branch,\n meta: duplicated.header.meta,\n protocolVersions: duplicated.header.protocolVersions ?? {\n \"base-reducer\": 2,\n },\n };\n\n const createKey = `create:${newId}`;\n jobs.push({\n key: createKey,\n documentId: newId,\n scope: \"document\",\n branch: \"main\",\n actions: [\n createDocumentAction(createInput),\n upgradeDocumentAction({\n documentId: newId,\n model: duplicated.header.documentType,\n fromVersion: 0,\n toVersion: duplicated.state.document.version,\n initialState: duplicated.state,\n }),\n ],\n dependsOn: [],\n });\n fileCreateKeys.push(createKey);\n\n const metadata: DriveChildFileMetadata = {\n kind: \"file\",\n parentFolderId: newParent,\n documentType: duplicated.header.documentType,\n };\n driveActions.push(\n addRelationshipAction(\n driveIdentifier,\n newId,\n DRIVE_CHILD_RELATIONSHIP_TYPE,\n metadata,\n ),\n );\n }\n\n if (driveActions.length > 0) {\n jobs.push({\n key: \"drive\",\n documentId: driveIdentifier,\n scope: \"document\",\n branch: \"main\",\n actions: driveActions,\n dependsOn: fileCreateKeys,\n });\n }\n\n if (jobs.length > 0) {\n const request: BatchExecutionRequest = { jobs };\n await this.reactor.executeBatch(request, signal);\n }\n\n const drive = await this.reactor.get<PHDocument>(\n driveIdentifier,\n undefined,\n signal,\n );\n return this.toLegacyDriveDocument(drive, driveIdentifier);\n }\n\n async getNode(\n driveIdentifier: string,\n nodeId: string,\n signal?: AbortSignal,\n ): Promise<Node> {\n const node = await this.readModel.getNode(driveIdentifier, nodeId, signal);\n if (!node) {\n throw new Error(`Node ${nodeId} not found in drive ${driveIdentifier}`);\n }\n return this.toLegacyNode(node);\n }\n\n async listNodes(\n driveIdentifier: string,\n parentFolder?: string | null,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<Node>> {\n const page = await this.readModel.listChildren(\n driveIdentifier,\n parentFolder,\n paging,\n signal,\n );\n return {\n results: page.results.map((node) => this.toLegacyNode(node)),\n options: page.options,\n ...(page.nextCursor !== undefined ? { nextCursor: page.nextCursor } : {}),\n ...(page.totalCount !== undefined ? { totalCount: page.totalCount } : {}),\n };\n }\n\n private toLegacyNode(node: ReactorDriveNode): Node {\n if (node.kind === \"file\") {\n const file: ReactorDriveFileNode = node;\n const legacy: FileNode = {\n id: file.id,\n kind: \"file\",\n name: file.name,\n parentFolder: file.parentFolder,\n documentType: file.documentType,\n };\n return legacy;\n }\n const folder: ReactorDriveFolderNode = node;\n const legacy: FolderNode = {\n id: folder.id,\n kind: \"folder\",\n name: folder.name,\n parentFolder: folder.parentFolder,\n };\n return legacy;\n }\n\n private async toLegacyDriveDocument(\n doc: PHDocument,\n driveId: string,\n ): Promise<DocumentDriveDocument> {\n if (doc.header.documentType !== REACTOR_DRIVE_DOCUMENT_TYPE) {\n throw new Error(\n `Document ${doc.header.id} is not a reactor-drive document`,\n );\n }\n const allNodes = await this.readModel.listAll(driveId);\n const legacy = structuredClone(doc) as unknown as DocumentDriveDocument;\n const existingGlobal = legacy.state.global as Partial<\n DocumentDriveDocument[\"state\"][\"global\"]\n >;\n legacy.state.global = {\n name: existingGlobal.name ?? \"\",\n icon: existingGlobal.icon ?? null,\n nodes: allNodes.map((n) => this.toLegacyNode(n)),\n };\n return legacy;\n }\n\n private orderDeepestFirst(\n folders: ReactorDriveFolderNode[],\n rootId: string,\n ): ReactorDriveFolderNode[] {\n const depthById = new Map<string, number>();\n const compute = (id: string): number => {\n if (id === rootId) return 0;\n const cached = depthById.get(id);\n if (cached !== undefined) return cached;\n const folder = folders.find((f) => f.id === id);\n if (!folder) return 0;\n const parent = folder.parentFolder ?? rootId;\n const depth = compute(parent) + 1;\n depthById.set(id, depth);\n return depth;\n };\n return folders.slice().sort((a, b) => compute(b.id) - compute(a.id));\n }\n}\n\nexport {\n setAvailableOfflineAction,\n setDriveIconAction,\n setDriveNameAction,\n setSharingTypeAction,\n};\n","import { gql } from \"graphql-tag\";\n\nexport const typeDefs = gql`\n enum ReactorDriveNodeKind {\n FILE\n FOLDER\n }\n\n input ReactorDrivePagingInput {\n cursor: String\n limit: Int\n }\n\n type ReactorDriveFileNode {\n id: ID!\n driveId: ID!\n name: String!\n parentFolder: ID\n documentType: String!\n }\n\n type ReactorDriveFolderNode {\n id: ID!\n driveId: ID!\n name: String!\n parentFolder: ID\n children(\n paging: ReactorDrivePagingInput\n kind: ReactorDriveNodeKind\n ): ReactorDriveNodePage!\n }\n\n union ReactorDriveNode = ReactorDriveFileNode | ReactorDriveFolderNode\n\n type ReactorDriveNodePage {\n results: [ReactorDriveNode!]!\n nextCursor: String\n hasMore: Boolean!\n totalCount: Int\n }\n\n type ReactorDrive {\n id: ID!\n name: String!\n icon: String\n sharingType: String!\n availableOffline: Boolean!\n rootNodes(\n paging: ReactorDrivePagingInput\n kind: ReactorDriveNodeKind\n ): ReactorDriveNodePage!\n }\n\n type Query {\n reactorDrive(id: ID!): ReactorDrive\n reactorDriveNode(driveId: ID!, id: ID!): ReactorDriveNode\n reactorDriveDescendants(driveId: ID!, root: ID!): [ReactorDriveNode!]!\n }\n`;\n","import type {\n IReactorClient,\n PagedResults,\n PagingOptions,\n} from \"@powerhousedao/reactor\";\nimport type { PHDocument } from \"@powerhousedao/shared/document-model\";\nimport { REACTOR_DRIVE_DOCUMENT_TYPE } from \"../constants.js\";\nimport type { IDriveReadModel } from \"../read-model/interfaces.js\";\nimport type { ReactorDrivePHState, ReactorDriveNode } from \"../types.js\";\n\nexport interface ReactorDriveResolverContext {\n reactorClient: IReactorClient;\n readModel: IDriveReadModel;\n}\n\ntype PagingInput = { cursor?: string; limit?: number };\ntype NodeKindFilter = \"FILE\" | \"FOLDER\" | undefined;\n\nconst DEFAULT_LIMIT = 100;\n\nfunction toPaging(input: PagingInput | undefined): PagingOptions | undefined {\n if (!input) return undefined;\n return {\n cursor: input.cursor ?? \"\",\n limit: input.limit ?? DEFAULT_LIMIT,\n };\n}\n\nfunction filterByKind(\n page: PagedResults<ReactorDriveNode>,\n kind: NodeKindFilter,\n): PagedResults<ReactorDriveNode> {\n if (!kind) return page;\n const wanted = kind === \"FILE\" ? \"file\" : \"folder\";\n return {\n ...page,\n results: page.results.filter((node) => node.kind === wanted),\n };\n}\n\nfunction shapePage(page: PagedResults<ReactorDriveNode>) {\n return {\n results: page.results,\n nextCursor: page.nextCursor,\n hasMore: page.nextCursor !== undefined,\n totalCount: page.totalCount,\n };\n}\n\n/**\n * Builds GraphQL resolvers backed by the drive read model. The resolvers are\n * pure — every external dependency is read off the GraphQL context. Wiring\n * the resolvers into a subgraph (e.g. `reactor-api`'s `ISubgraph`) is the\n * caller's responsibility.\n */\nexport function createReactorDriveResolvers() {\n return {\n Query: {\n async reactorDrive(\n _root: unknown,\n args: { id: string },\n ctx: ReactorDriveResolverContext,\n ) {\n const document = await ctx.reactorClient.get<\n PHDocument<ReactorDrivePHState>\n >(args.id);\n if (document.header.documentType !== REACTOR_DRIVE_DOCUMENT_TYPE) {\n return null;\n }\n return {\n id: document.header.id,\n name: document.state.global.name,\n icon: document.state.global.icon,\n sharingType: document.state.local.sharingType,\n availableOffline: document.state.local.availableOffline,\n };\n },\n async reactorDriveNode(\n _root: unknown,\n args: { driveId: string; id: string },\n ctx: ReactorDriveResolverContext,\n ) {\n return ctx.readModel.getNode(args.driveId, args.id);\n },\n async reactorDriveDescendants(\n _root: unknown,\n args: { driveId: string; root: string },\n ctx: ReactorDriveResolverContext,\n ) {\n return ctx.readModel.getDescendants(args.driveId, args.root);\n },\n },\n ReactorDrive: {\n async rootNodes(\n parent: { id: string },\n args: { paging?: PagingInput; kind?: NodeKindFilter },\n ctx: ReactorDriveResolverContext,\n ) {\n const page = await ctx.readModel.listChildren(\n parent.id,\n null,\n toPaging(args.paging),\n );\n return shapePage(filterByKind(page, args.kind));\n },\n },\n ReactorDriveFolderNode: {\n async children(\n parent: { id: string; driveId: string },\n args: { paging?: PagingInput; kind?: NodeKindFilter },\n ctx: ReactorDriveResolverContext,\n ) {\n const page = await ctx.readModel.listChildren(\n parent.driveId,\n parent.id,\n toPaging(args.paging),\n );\n return shapePage(filterByKind(page, args.kind));\n },\n },\n ReactorDriveNode: {\n __resolveType(node: ReactorDriveNode) {\n return node.kind === \"file\"\n ? \"ReactorDriveFileNode\"\n : \"ReactorDriveFolderNode\";\n },\n },\n };\n}\n","import {\n addRelationshipAction,\n type IReactorClient,\n} from \"@powerhousedao/reactor\";\nimport type { FileNode, Node } from \"@powerhousedao/shared/document-drive\";\nimport type { Action } from \"@powerhousedao/shared/document-model\";\nimport { addFolderAction } from \"../actions.js\";\nimport { DRIVE_CHILD_RELATIONSHIP_TYPE } from \"../constants.js\";\nimport type { IDriveReadModel } from \"../read-model/interfaces.js\";\nimport type { DriveChildFileMetadata } from \"../types.js\";\n\nexport interface MigrateLegacyDriveStateArgs {\n reactor: IReactorClient;\n readModel: IDriveReadModel;\n driveId: string;\n nodes: Node[];\n branch?: string;\n signal?: AbortSignal;\n}\n\nexport interface MigrateLegacyDriveStateResult {\n emittedActions: number;\n skippedExisting: number;\n}\n\n/**\n * Translates a legacy `document-drive` `state.global.nodes` array into the\n * action vocabulary used by the new reactor-drive module.\n *\n * Folder nodes are emitted as `ADD_FOLDER` actions targeting the drive\n * document. File nodes are emitted as `ADD_RELATIONSHIP` actions on the drive\n * with `drive/child` metadata carrying the parent folder id. File nodes\n * assume the underlying PHDocument still exists under the same id — the\n * migration only re-links it into the new drive, it does not recreate\n * documents.\n *\n * Re-running the migration is safe: existing `DriveNode` rows are skipped\n * so already-migrated nodes are left untouched.\n */\nexport async function migrateLegacyDriveState(\n args: MigrateLegacyDriveStateArgs,\n): Promise<MigrateLegacyDriveStateResult> {\n const { reactor, readModel, driveId, nodes, signal } = args;\n const branch = args.branch ?? \"main\";\n\n const ordered = orderLegacyNodes(nodes);\n const actions: Action[] = [];\n let skippedExisting = 0;\n\n for (const node of ordered) {\n const existing = await readModel.getNode(driveId, node.id, signal);\n if (existing) {\n skippedExisting += 1;\n continue;\n }\n actions.push(toAction(driveId, node));\n }\n\n if (actions.length === 0) {\n return { emittedActions: 0, skippedExisting };\n }\n\n await reactor.execute(driveId, branch, actions, signal);\n return { emittedActions: actions.length, skippedExisting };\n}\n\nfunction toAction(driveId: string, node: Node): Action {\n if (node.kind === \"folder\") {\n return addFolderAction({\n folderId: node.id,\n parentFolderId: node.parentFolder ?? null,\n name: node.name,\n });\n }\n const fileNode = node as FileNode;\n const metadata: DriveChildFileMetadata = {\n kind: \"file\",\n parentFolderId: fileNode.parentFolder ?? null,\n documentType: fileNode.documentType,\n };\n return addRelationshipAction(\n driveId,\n fileNode.id,\n DRIVE_CHILD_RELATIONSHIP_TYPE,\n metadata,\n );\n}\n\n/**\n * Sorts legacy nodes so that any folder appears before its children. The\n * legacy state stores nodes in insertion order, which usually already\n * satisfies that property, but defensive sorting keeps replay deterministic\n * if the input was constructed out of order.\n */\nfunction orderLegacyNodes(nodes: Node[]): Node[] {\n const byId = new Map<string, Node>();\n for (const node of nodes) {\n byId.set(node.id, node);\n }\n const visited = new Set<string>();\n const ordered: Node[] = [];\n\n for (const start of nodes) {\n if (visited.has(start.id)) continue;\n const chain: Node[] = [];\n const seenInWalk = new Set<string>();\n let current: Node | undefined = start;\n while (current && !visited.has(current.id) && !seenInWalk.has(current.id)) {\n seenInWalk.add(current.id);\n chain.push(current);\n const parentId: string | null | undefined = current.parentFolder;\n current = parentId ? byId.get(parentId) : undefined;\n }\n while (chain.length > 0) {\n const node = chain.pop()!;\n if (visited.has(node.id)) continue;\n visited.add(node.id);\n ordered.push(node);\n }\n }\n return ordered;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["up","down","migration0001","migration0002","DEFAULT_LIMIT"],"sources":["../src/constants.ts","../src/actions.ts","../src/reducer/drive.ts","../src/module.ts","../src/schema/migrations/0001_drive_node.ts","../src/schema/migrations/0002_document_name.ts","../src/schema/migrations/migrator.ts","../src/processors/utils/collisions.ts","../src/processors/node-processor.ts","../src/read-model/drive-node-view.ts","../src/client/reactor-drive-client.ts","../src/subgraph/schema.ts","../src/subgraph/resolvers.ts","../src/migration/migrate-legacy-state.ts"],"sourcesContent":["export const REACTOR_DRIVE_DOCUMENT_TYPE = \"powerhouse/reactor-drive\";\n\nexport const DRIVE_CHILD_RELATIONSHIP_TYPE = \"drive/child\";\n\nexport const REACTOR_DRIVE_FILE_EXTENSION = \"phrd\";\n","import type { Action } from \"@powerhousedao/shared/document-model\";\nimport { generateId } from \"@powerhousedao/shared/document-model\";\nimport type {\n AddFolderActionInput,\n RemoveFolderActionInput,\n UpdateFolderActionInput,\n} from \"./types.js\";\n\nexport type SetDriveNameInput = { name: string };\nexport type SetDriveIconInput = { icon: string | null };\nexport type SetSharingTypeInput = { sharingType: string };\nexport type SetAvailableOfflineInput = { availableOffline: boolean };\n\nexport function setDriveNameAction(input: SetDriveNameInput): Action {\n return {\n id: generateId(),\n type: \"SET_DRIVE_NAME\",\n scope: \"global\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\nexport function setDriveIconAction(input: SetDriveIconInput): Action {\n return {\n id: generateId(),\n type: \"SET_DRIVE_ICON\",\n scope: \"global\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\nexport function setSharingTypeAction(input: SetSharingTypeInput): Action {\n return {\n id: generateId(),\n type: \"SET_SHARING_TYPE\",\n scope: \"local\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\nexport function setAvailableOfflineAction(\n input: SetAvailableOfflineInput,\n): Action {\n return {\n id: generateId(),\n type: \"SET_AVAILABLE_OFFLINE\",\n scope: \"local\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\nexport function addFolderAction(input: AddFolderActionInput): Action {\n return {\n id: generateId(),\n type: \"ADD_FOLDER\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\nexport function updateFolderAction(input: UpdateFolderActionInput): Action {\n return {\n id: generateId(),\n type: \"UPDATE_FOLDER\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\nexport function removeFolderAction(input: RemoveFolderActionInput): Action {\n return {\n id: generateId(),\n type: \"REMOVE_FOLDER\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\nexport const reactorDriveActions = {\n setDriveName: setDriveNameAction,\n setDriveIcon: setDriveIconAction,\n setSharingType: setSharingTypeAction,\n setAvailableOffline: setAvailableOfflineAction,\n addFolder: addFolderAction,\n updateFolder: updateFolderAction,\n removeFolder: removeFolderAction,\n};\n","import type {\n Action,\n StateReducer,\n} from \"@powerhousedao/shared/document-model\";\nimport { isDocumentAction } from \"@powerhousedao/shared/document-model\";\nimport type {\n SetAvailableOfflineInput,\n SetDriveIconInput,\n SetDriveNameInput,\n SetSharingTypeInput,\n} from \"../actions.js\";\nimport type { ReactorDrivePHState } from \"../types.js\";\n\ntype TypedAction = Action & { type: string };\n\nexport const reactorDriveStateReducer: StateReducer<ReactorDrivePHState> = (\n state,\n action,\n) => {\n if (isDocumentAction(action)) {\n return state;\n }\n\n const typedAction = action as TypedAction;\n\n switch (typedAction.type) {\n case \"SET_DRIVE_NAME\": {\n const input = typedAction.input as SetDriveNameInput;\n state.global.name = input.name;\n return state;\n }\n case \"SET_DRIVE_ICON\": {\n const input = typedAction.input as SetDriveIconInput;\n state.global.icon = input.icon;\n return state;\n }\n case \"SET_SHARING_TYPE\": {\n const input = typedAction.input as SetSharingTypeInput;\n state.local.sharingType = input.sharingType;\n return state;\n }\n case \"SET_AVAILABLE_OFFLINE\": {\n const input = typedAction.input as SetAvailableOfflineInput;\n state.local.availableOffline = input.availableOffline;\n return state;\n }\n case \"ADD_FOLDER\":\n case \"UPDATE_FOLDER\":\n case \"REMOVE_FOLDER\":\n // Identity: folder ops are recorded for sync and materialized by NodeProcessor.\n return state;\n default:\n return state;\n }\n};\n","import type {\n AssertIsDocumentOfType,\n AssertIsStateOfType,\n CreateDocument,\n CreateState,\n DocumentModelGlobalState,\n IsDocumentOfType,\n IsStateOfType,\n LoadFromInput,\n PHDocument,\n SaveToFileHandle,\n} from \"@powerhousedao/shared/document-model\";\nimport {\n baseCreateDocument,\n baseLoadFromInput,\n baseSaveToFileHandle,\n createReducer,\n createState,\n defaultBaseState,\n} from \"@powerhousedao/shared/document-model\";\nimport { reactorDriveActions } from \"./actions.js\";\nimport {\n REACTOR_DRIVE_DOCUMENT_TYPE,\n REACTOR_DRIVE_FILE_EXTENSION,\n} from \"./constants.js\";\nimport { reactorDriveStateReducer } from \"./reducer/drive.js\";\nimport type {\n ReactorDriveDocumentModelModule,\n ReactorDriveGlobalState,\n ReactorDriveLocalState,\n ReactorDrivePHState,\n} from \"./types.js\";\n\nconst initialGlobalState: ReactorDriveGlobalState = {\n name: \"\",\n icon: null,\n};\n\nconst initialLocalState: ReactorDriveLocalState = {\n sharingType: \"private\",\n availableOffline: false,\n};\n\nexport const reactorDriveCreateState: CreateState<ReactorDrivePHState> = (\n state,\n) => {\n return {\n ...defaultBaseState(),\n global: { ...initialGlobalState, ...state?.global },\n local: { ...initialLocalState, ...state?.local },\n };\n};\n\nexport const reactorDriveCreateDocument: CreateDocument<ReactorDrivePHState> = (\n state,\n) => {\n return baseCreateDocument(\n reactorDriveCreateState,\n state,\n REACTOR_DRIVE_DOCUMENT_TYPE,\n );\n};\n\nconst reactorDriveSaveToFileHandle: SaveToFileHandle = (document, input) => {\n return baseSaveToFileHandle(document, input);\n};\n\nconst reactorDriveLoadFromInput: LoadFromInput<ReactorDrivePHState> = (\n input,\n) => {\n return baseLoadFromInput(input, reactorDriveDocumentReducer);\n};\n\nconst isReactorDriveState: IsStateOfType<ReactorDrivePHState> = (\n state,\n): state is ReactorDrivePHState => {\n return (\n typeof state === \"object\" &&\n state !== null &&\n \"global\" in state &&\n \"local\" in state\n );\n};\n\nconst assertIsReactorDriveState: AssertIsStateOfType<ReactorDrivePHState> = (\n state,\n) => {\n if (!isReactorDriveState(state)) {\n throw new Error(\"Not a reactor-drive state\");\n }\n};\n\nconst isReactorDriveDocument: IsDocumentOfType<ReactorDrivePHState> = (\n document,\n): document is PHDocument<ReactorDrivePHState> => {\n return (\n typeof document === \"object\" &&\n document !== null &&\n \"header\" in document &&\n (document as { header: { documentType: string } }).header.documentType ===\n REACTOR_DRIVE_DOCUMENT_TYPE\n );\n};\n\nconst assertIsReactorDriveDocument: AssertIsDocumentOfType<\n ReactorDrivePHState\n> = (document) => {\n if (!isReactorDriveDocument(document)) {\n throw new Error(\"Not a reactor-drive document\");\n }\n};\n\nexport const reactorDriveDocumentReducer = createReducer<ReactorDrivePHState>(\n reactorDriveStateReducer,\n);\n\nconst reactorDriveDocumentGlobalState: DocumentModelGlobalState = {\n id: REACTOR_DRIVE_DOCUMENT_TYPE,\n name: \"ReactorDrive\",\n extension: REACTOR_DRIVE_FILE_EXTENSION,\n description: \"\",\n author: {\n name: \"Powerhouse Inc\",\n website: \"https://www.powerhouse.inc/\",\n },\n specifications: [\n {\n version: 1,\n changeLog: [],\n state: {\n global: {\n schema:\n \"type ReactorDriveState {\\n name: String!\\n icon: String\\n}\",\n // Double-encoded by convention: `initialValue` holds a JSON-encoded string that consumers parse with JSON.parse.\n initialValue: JSON.stringify(JSON.stringify(initialGlobalState)),\n examples: [],\n },\n local: {\n schema:\n \"type ReactorDriveLocalState {\\n sharingType: String!\\n availableOffline: Boolean!\\n}\",\n // Double-encoded by convention: `initialValue` holds a JSON-encoded string that consumers parse with JSON.parse.\n initialValue: JSON.stringify(JSON.stringify(initialLocalState)),\n examples: [],\n },\n },\n modules: [\n {\n id: \"reactor-drive/base-operations\",\n name: \"base_operations\",\n description: \"\",\n operations: [\n {\n id: \"SET_DRIVE_NAME\",\n name: \"SET_DRIVE_NAME\",\n description: \"\",\n schema: \"input SetDriveNameInput { name: String! }\",\n template: \"\",\n reducer: \"\",\n errors: [],\n examples: [],\n scope: \"global\",\n },\n {\n id: \"SET_DRIVE_ICON\",\n name: \"SET_DRIVE_ICON\",\n description: \"\",\n schema: \"input SetDriveIconInput { icon: String }\",\n template: \"\",\n reducer: \"\",\n errors: [],\n examples: [],\n scope: \"global\",\n },\n {\n id: \"SET_SHARING_TYPE\",\n name: \"SET_SHARING_TYPE\",\n description: \"\",\n schema: \"input SetSharingTypeInput { sharingType: String! }\",\n template: \"\",\n reducer: \"\",\n errors: [],\n examples: [],\n scope: \"local\",\n },\n {\n id: \"SET_AVAILABLE_OFFLINE\",\n name: \"SET_AVAILABLE_OFFLINE\",\n description: \"\",\n schema:\n \"input SetAvailableOfflineInput { availableOffline: Boolean! }\",\n template: \"\",\n reducer: \"\",\n errors: [],\n examples: [],\n scope: \"local\",\n },\n {\n id: \"ADD_FOLDER\",\n name: \"ADD_FOLDER\",\n description: \"\",\n schema:\n \"input AddFolderInput { folderId: String! parentFolderId: String name: String! }\",\n template: \"\",\n reducer: \"\",\n errors: [],\n examples: [],\n scope: \"document\",\n },\n {\n id: \"UPDATE_FOLDER\",\n name: \"UPDATE_FOLDER\",\n description: \"\",\n schema:\n \"input UpdateFolderInput { folderId: String! name: String parentFolderId: String }\",\n template: \"\",\n reducer: \"\",\n errors: [],\n examples: [],\n scope: \"document\",\n },\n {\n id: \"REMOVE_FOLDER\",\n name: \"REMOVE_FOLDER\",\n description: \"\",\n schema: \"input RemoveFolderInput { folderId: String! }\",\n template: \"\",\n reducer: \"\",\n errors: [],\n examples: [],\n scope: \"document\",\n },\n ],\n },\n ],\n },\n ],\n};\n\nexport const reactorDriveDocumentModelModule: ReactorDriveDocumentModelModule =\n {\n actions: reactorDriveActions,\n reducer: reactorDriveDocumentReducer,\n documentModel: createState(\n defaultBaseState(),\n reactorDriveDocumentGlobalState,\n ),\n utils: {\n fileExtension: REACTOR_DRIVE_FILE_EXTENSION,\n createState: reactorDriveCreateState,\n createDocument: reactorDriveCreateDocument,\n loadFromInput: reactorDriveLoadFromInput,\n saveToFileHandle: reactorDriveSaveToFileHandle,\n isStateOfType: isReactorDriveState,\n assertIsStateOfType: assertIsReactorDriveState,\n isDocumentOfType: isReactorDriveDocument,\n assertIsDocumentOfType: assertIsReactorDriveDocument,\n },\n };\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n await db.schema\n .createTable(\"DriveNode\")\n .ifNotExists()\n .addColumn(\"driveId\", \"text\", (col) => col.notNull())\n .addColumn(\"id\", \"text\", (col) => col.notNull())\n .addColumn(\"kind\", \"text\", (col) => col.notNull())\n .addColumn(\"name\", \"text\", (col) => col.notNull())\n .addColumn(\"requestedName\", \"text\", (col) => col.notNull())\n .addColumn(\"parentFolder\", \"text\")\n .addColumn(\"documentType\", \"text\")\n .addColumn(\"createdAt\", \"timestamptz\", (col) =>\n col.notNull().defaultTo(sql`NOW()`),\n )\n .addColumn(\"updatedAt\", \"timestamptz\", (col) =>\n col.notNull().defaultTo(sql`NOW()`),\n )\n .addPrimaryKeyConstraint(\"pk_drive_node\", [\"driveId\", \"id\"])\n .addCheckConstraint(\n \"chk_drive_node_document_type\",\n sql`(kind = 'file' AND \"documentType\" IS NOT NULL) OR (kind = 'folder' AND \"documentType\" IS NULL)`,\n )\n .execute();\n\n await db.schema\n .createIndex(\"idx_drive_node_parent_name\")\n .ifNotExists()\n .on(\"DriveNode\")\n .columns([\"driveId\", \"parentFolder\", \"name\"])\n .execute();\n\n await db.schema\n .createIndex(\"idx_drive_node_parent_kind_id\")\n .ifNotExists()\n .on(\"DriveNode\")\n .columns([\"driveId\", \"parentFolder\", \"kind\", \"id\"])\n .execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n await db.schema.dropTable(\"DriveNode\").execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n await db.schema\n .createTable(\"DocumentName\")\n .ifNotExists()\n .addColumn(\"docId\", \"text\", (col) => col.primaryKey())\n .addColumn(\"name\", \"text\", (col) => col.notNull())\n .addColumn(\"updatedAt\", \"timestamptz\", (col) =>\n col.notNull().defaultTo(sql`NOW()`),\n )\n .execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n await db.schema.dropTable(\"DocumentName\").execute();\n}\n","import type { Kysely, MigrationProvider } from \"kysely\";\nimport { Migrator, sql } from \"kysely\";\nimport * as migration0001 from \"./0001_drive_node.js\";\nimport * as migration0002 from \"./0002_document_name.js\";\n\nconst migrations = {\n \"0001_drive_node\": migration0001,\n \"0002_document_name\": migration0002,\n};\n\nconst REACTOR_DRIVE_MIGRATION_TABLE = \"kysely_migration_reactor_drive\";\nconst REACTOR_DRIVE_MIGRATION_LOCK_TABLE =\n \"kysely_migration_reactor_drive_lock\";\n\nexport interface ReactorDriveMigrationResult {\n success: boolean;\n migrationsExecuted: string[];\n error?: Error;\n}\n\nclass ProgrammaticMigrationProvider implements MigrationProvider {\n getMigrations() {\n return Promise.resolve(migrations);\n }\n}\n\nexport async function runReactorDriveMigrations(\n db: Kysely<unknown>,\n schema: string,\n): Promise<ReactorDriveMigrationResult> {\n try {\n await sql`CREATE SCHEMA IF NOT EXISTS ${sql.id(schema)}`.execute(db);\n } catch (error) {\n return {\n success: false,\n migrationsExecuted: [],\n error:\n error instanceof Error ? error : new Error(\"Failed to create schema\"),\n };\n }\n\n const migrator = new Migrator({\n db: db.withSchema(schema),\n provider: new ProgrammaticMigrationProvider(),\n migrationTableSchema: schema,\n migrationTableName: REACTOR_DRIVE_MIGRATION_TABLE,\n migrationLockTableName: REACTOR_DRIVE_MIGRATION_LOCK_TABLE,\n });\n\n let error: unknown;\n let results: Awaited<ReturnType<typeof migrator.migrateToLatest>>[\"results\"];\n try {\n const result = await migrator.migrateToLatest();\n error = result.error;\n results = result.results;\n } catch (e) {\n error = e;\n results = [];\n }\n\n const migrationsExecuted =\n results?.map((result) => result.migrationName) ?? [];\n\n if (error) {\n return {\n success: false,\n migrationsExecuted,\n error:\n error instanceof Error ? error : new Error(\"Unknown migration error\"),\n };\n }\n\n return {\n success: true,\n migrationsExecuted,\n };\n}\n\nexport async function getReactorDriveMigrationStatus(\n db: Kysely<unknown>,\n schema: string,\n) {\n const migrator = new Migrator({\n db: db.withSchema(schema),\n provider: new ProgrammaticMigrationProvider(),\n migrationTableSchema: schema,\n migrationTableName: REACTOR_DRIVE_MIGRATION_TABLE,\n migrationLockTableName: REACTOR_DRIVE_MIGRATION_LOCK_TABLE,\n });\n\n return await migrator.getMigrations();\n}\n","/**\n * Deterministic per-folder name collision rule. Given a desired name and the\n * set of names already taken by siblings, returns the next available name as\n * `requested`, `requested (2)`, `requested (3)`, etc.\n *\n * The rule matches the legacy document-drive module so that migration from\n * the legacy state produces stable suffixes.\n */\nexport function resolveCollision(\n requested: string,\n takenNames: Iterable<string>,\n): string {\n const taken = new Set<string>();\n for (const name of takenNames) {\n taken.add(name);\n }\n\n if (!taken.has(requested)) {\n return requested;\n }\n\n let suffix = 2;\n while (taken.has(`${requested} (${suffix})`)) {\n suffix += 1;\n }\n return `${requested} (${suffix})`;\n}\n","import type { IOperationIndex, IWriteCache } from \"@powerhousedao/reactor\";\nimport {\n BaseReadModel,\n findPurged,\n isPurgeMarker,\n REACTOR_SCHEMA,\n unchunkedReadModelIndexingConfig,\n type DocumentViewDatabase,\n type IConsistencyTracker,\n} from \"@powerhousedao/reactor\";\nimport type {\n AddRelationshipActionInput,\n CreateDocumentActionInput,\n DeleteDocumentActionInput,\n OperationWithContext,\n PurgeDocumentActionInput,\n RemoveRelationshipActionInput,\n SetNameActionInput,\n UpgradeDocumentActionInput,\n} from \"@powerhousedao/shared/document-model\";\nimport type { Kysely, Transaction } from \"kysely\";\nimport {\n DRIVE_CHILD_RELATIONSHIP_TYPE,\n REACTOR_DRIVE_DOCUMENT_TYPE,\n} from \"../constants.js\";\nimport { runReactorDriveMigrations } from \"../schema/migrations/migrator.js\";\nimport type { ReactorDriveDatabase } from \"../schema/tables.js\";\nimport type {\n AddFolderActionInput,\n DriveChildFileMetadata,\n RemoveFolderActionInput,\n UpdateFolderActionInput,\n} from \"../types.js\";\nimport { resolveCollision } from \"./utils/collisions.js\";\n\nexport type NodeProcessorDatabase = ReactorDriveDatabase & DocumentViewDatabase;\n\nconst NAME_ACTION_TYPES = new Set([\n \"CREATE_DOCUMENT\",\n \"UPGRADE_DOCUMENT\",\n \"SET_NAME\",\n]);\n\nconst STRUCTURE_ACTION_TYPES = new Set([\n \"ADD_RELATIONSHIP\",\n \"REMOVE_RELATIONSHIP\",\n \"ADD_FOLDER\",\n \"UPDATE_FOLDER\",\n \"REMOVE_FOLDER\",\n]);\n\n/**\n * Projects drive structure operations into the DriveNode tree.\n *\n * Indexes a batch unchunked: a single job may carry one action per descendant\n * of a copied subtree, and a partially committed copy is a half-built tree that\n * readers can see. Chunked commits would only be repaired at the next init(),\n * so the whole batch commits or none of it does.\n */\nexport class NodeProcessor extends BaseReadModel {\n static override readonly commitsInFenceTransaction = true;\n\n private readonly driveDb: Kysely<NodeProcessorDatabase>;\n private readonly baseDb: Kysely<unknown>;\n private readonly schema: string;\n private readonly reactorSchema: string;\n\n constructor(\n baseDb: Kysely<unknown>,\n schema: string,\n operationIndex: IOperationIndex,\n writeCache: IWriteCache,\n consistencyTracker: IConsistencyTracker,\n reactorSchema: string = REACTOR_SCHEMA,\n ) {\n const scopedDb = baseDb.withSchema(\n schema,\n ) as unknown as Kysely<NodeProcessorDatabase>;\n super(\n scopedDb as unknown as Kysely<DocumentViewDatabase>,\n operationIndex,\n writeCache,\n consistencyTracker,\n {\n readModelId: \"reactor-drive-node-processor\",\n rebuildStateOnInit: false,\n indexing: unchunkedReadModelIndexingConfig,\n purgeFence: \"locked\",\n },\n );\n this.driveDb = scopedDb;\n this.baseDb = baseDb;\n this.schema = schema;\n this.reactorSchema = reactorSchema;\n }\n\n protected override purgeLookup(db: Kysely<any>): Kysely<any> {\n return db.withSchema(this.reactorSchema);\n }\n\n override async init(): Promise<void> {\n const result = await runReactorDriveMigrations(this.baseDb, this.schema);\n if (!result.success && result.error) {\n throw new Error(\n `Reactor drive migrations failed: ${result.error.message}`,\n );\n }\n await super.init();\n }\n\n protected override async commitOperations(\n items: OperationWithContext[],\n fenceTrx?: Transaction<DocumentViewDatabase>,\n ): Promise<void> {\n if (fenceTrx) {\n await this.applyAll(\n items,\n fenceTrx as unknown as Transaction<NodeProcessorDatabase>,\n );\n return;\n }\n if (!this.writesRows(items)) return;\n await this.driveDb\n .transaction()\n .execute((trx) => this.applyAll(items, trx));\n }\n\n protected override writesRows(items: OperationWithContext[]): boolean {\n return items.some((item) => {\n const actionType = item.operation.action.type;\n return (\n NAME_ACTION_TYPES.has(actionType) ||\n STRUCTURE_ACTION_TYPES.has(actionType) ||\n actionType === \"DELETE_DOCUMENT\" ||\n isPurgeMarker(item.operation)\n );\n });\n }\n\n private async applyAll(\n items: OperationWithContext[],\n trx: Transaction<NodeProcessorDatabase>,\n ): Promise<void> {\n const purgedTargets = await this.findPurgedTargets(trx, items);\n for (const item of items) {\n const actionType = item.operation.action.type;\n if (isPurgeMarker(item.operation)) {\n const input = item.operation.action.input as PurgeDocumentActionInput;\n await this.eraseDocument(\n trx,\n input.documentId || item.context.documentId,\n );\n continue;\n }\n if (NAME_ACTION_TYPES.has(actionType)) {\n await this.applyNameOperation(trx, item);\n continue;\n }\n if (STRUCTURE_ACTION_TYPES.has(actionType)) {\n if (this.namesPurgedTarget(item, purgedTargets)) continue;\n await this.applyStructureOperation(trx, item);\n continue;\n }\n if (actionType === \"DELETE_DOCUMENT\") {\n await this.applyDeleteDocument(trx, item);\n }\n }\n }\n\n /** Tombstoned targets of this batch's drive-child ADD_RELATIONSHIPs. */\n private async findPurgedTargets(\n trx: Transaction<NodeProcessorDatabase>,\n items: OperationWithContext[],\n ): Promise<Set<string>> {\n const targets: string[] = [];\n for (const item of items) {\n const target = this.addedChildOf(item);\n if (target !== undefined) targets.push(target);\n }\n return findPurged(this.purgeLookup(trx), targets);\n }\n\n private namesPurgedTarget(\n item: OperationWithContext,\n purgedTargets: Set<string>,\n ): boolean {\n const target = this.addedChildOf(item);\n return target !== undefined && purgedTargets.has(target);\n }\n\n private addedChildOf(item: OperationWithContext): string | undefined {\n const action = item.operation.action;\n if (action.type !== \"ADD_RELATIONSHIP\") return undefined;\n if (item.context.documentType !== REACTOR_DRIVE_DOCUMENT_TYPE) {\n return undefined;\n }\n const input = action.input as AddRelationshipActionInput;\n if (input.relationshipType !== DRIVE_CHILD_RELATIONSHIP_TYPE) {\n return undefined;\n }\n return input.targetId;\n }\n\n private async applyNameOperation(\n trx: Transaction<NodeProcessorDatabase>,\n item: OperationWithContext,\n ): Promise<void> {\n const action = item.operation.action;\n const docId = item.context.documentId;\n let name: string | undefined;\n\n if (action.type === \"CREATE_DOCUMENT\") {\n const input = action.input as CreateDocumentActionInput;\n name = input.name;\n } else if (action.type === \"UPGRADE_DOCUMENT\") {\n const input = action.input as UpgradeDocumentActionInput;\n const initial = (input.initialState ?? {}) as {\n header?: { name?: string };\n };\n name = initial.header?.name;\n } else if (action.type === \"SET_NAME\") {\n const input = action.input as SetNameActionInput;\n name = input.name;\n }\n\n if (name === undefined) {\n return;\n }\n\n await this.upsertDocumentName(trx, docId, name);\n\n const linkedRows = await trx\n .selectFrom(\"DriveNode\")\n .selectAll()\n .where(\"id\", \"=\", docId)\n .where(\"kind\", \"=\", \"file\")\n .execute();\n\n for (const row of linkedRows) {\n const resolved = await this.resolveSiblingName(\n trx,\n row.driveId,\n row.parentFolder,\n name,\n docId,\n );\n await trx\n .updateTable(\"DriveNode\")\n .set({\n name: resolved,\n requestedName: name,\n updatedAt: new Date(),\n })\n .where(\"driveId\", \"=\", row.driveId)\n .where(\"id\", \"=\", docId)\n .execute();\n }\n }\n\n private async applyStructureOperation(\n trx: Transaction<NodeProcessorDatabase>,\n item: OperationWithContext,\n ): Promise<void> {\n if (item.context.documentType !== REACTOR_DRIVE_DOCUMENT_TYPE) {\n return;\n }\n\n const action = item.operation.action;\n const driveId = item.context.documentId;\n\n if (action.type === \"ADD_RELATIONSHIP\") {\n const input = action.input as AddRelationshipActionInput;\n if (input.relationshipType !== DRIVE_CHILD_RELATIONSHIP_TYPE) return;\n await this.handleAddFileRelationship(trx, driveId, input);\n return;\n }\n\n if (action.type === \"REMOVE_RELATIONSHIP\") {\n const input = action.input as RemoveRelationshipActionInput;\n if (input.relationshipType !== DRIVE_CHILD_RELATIONSHIP_TYPE) return;\n await trx\n .deleteFrom(\"DriveNode\")\n .where(\"driveId\", \"=\", driveId)\n .where(\"id\", \"=\", input.targetId)\n .execute();\n return;\n }\n\n if (action.type === \"ADD_FOLDER\") {\n await this.handleAddFolder(\n trx,\n driveId,\n action.input as AddFolderActionInput,\n );\n return;\n }\n\n if (action.type === \"UPDATE_FOLDER\") {\n await this.handleUpdateFolder(\n trx,\n driveId,\n action.input as UpdateFolderActionInput,\n );\n return;\n }\n\n if (action.type === \"REMOVE_FOLDER\") {\n const input = action.input as RemoveFolderActionInput;\n await trx\n .deleteFrom(\"DriveNode\")\n .where(\"driveId\", \"=\", driveId)\n .where(\"id\", \"=\", input.folderId)\n .execute();\n }\n }\n\n private async applyDeleteDocument(\n trx: Transaction<NodeProcessorDatabase>,\n item: OperationWithContext,\n ): Promise<void> {\n const input = item.operation.action.input as DeleteDocumentActionInput;\n await this.eraseDocument(trx, input.documentId || item.context.documentId);\n }\n\n /** The id's nodes in every drive, a drive's own tree, and its name. */\n private async eraseDocument(\n trx: Transaction<NodeProcessorDatabase>,\n docId: string,\n ): Promise<void> {\n await trx\n .deleteFrom(\"DriveNode\")\n .where((eb) => eb.or([eb(\"id\", \"=\", docId), eb(\"driveId\", \"=\", docId)]))\n .execute();\n await trx.deleteFrom(\"DocumentName\").where(\"docId\", \"=\", docId).execute();\n }\n\n private async handleAddFileRelationship(\n trx: Transaction<NodeProcessorDatabase>,\n driveId: string,\n input: AddRelationshipActionInput,\n ): Promise<void> {\n const metadata = this.parseFileMetadata(input);\n const parentFolder = metadata.parentFolderId ?? null;\n const documentType = metadata.documentType;\n\n const requestedName =\n (await this.lookupDocumentName(trx, input.targetId)) ?? \"\";\n\n const resolved = await this.resolveSiblingName(\n trx,\n driveId,\n parentFolder,\n requestedName,\n input.targetId,\n );\n\n await trx\n .insertInto(\"DriveNode\")\n .values({\n driveId,\n id: input.targetId,\n kind: \"file\",\n name: resolved,\n requestedName,\n parentFolder,\n documentType,\n })\n .onConflict((oc) =>\n oc.columns([\"driveId\", \"id\"]).doUpdateSet({\n parentFolder,\n name: resolved,\n requestedName,\n kind: \"file\",\n documentType,\n updatedAt: new Date(),\n }),\n )\n .execute();\n }\n\n private async handleAddFolder(\n trx: Transaction<NodeProcessorDatabase>,\n driveId: string,\n input: AddFolderActionInput,\n ): Promise<void> {\n const parentFolder = input.parentFolderId ?? null;\n const resolved = await this.resolveSiblingName(\n trx,\n driveId,\n parentFolder,\n input.name,\n input.folderId,\n );\n\n await trx\n .insertInto(\"DriveNode\")\n .values({\n driveId,\n id: input.folderId,\n kind: \"folder\",\n name: resolved,\n requestedName: input.name,\n parentFolder,\n documentType: null,\n })\n .onConflict((oc) =>\n oc.columns([\"driveId\", \"id\"]).doUpdateSet({\n parentFolder,\n name: resolved,\n requestedName: input.name,\n kind: \"folder\",\n documentType: null,\n updatedAt: new Date(),\n }),\n )\n .execute();\n }\n\n private async handleUpdateFolder(\n trx: Transaction<NodeProcessorDatabase>,\n driveId: string,\n input: UpdateFolderActionInput,\n ): Promise<void> {\n const row = await trx\n .selectFrom(\"DriveNode\")\n .selectAll()\n .where(\"driveId\", \"=\", driveId)\n .where(\"id\", \"=\", input.folderId)\n .executeTakeFirst();\n if (!row) return;\n\n const nextParentFolder =\n input.parentFolderId === undefined\n ? row.parentFolder\n : (input.parentFolderId ?? null);\n const nextRequestedName =\n typeof input.name === \"string\" ? input.name : row.requestedName;\n\n const resolved = await this.resolveSiblingName(\n trx,\n driveId,\n nextParentFolder,\n nextRequestedName,\n input.folderId,\n );\n\n await trx\n .updateTable(\"DriveNode\")\n .set({\n parentFolder: nextParentFolder,\n name: resolved,\n requestedName: nextRequestedName,\n updatedAt: new Date(),\n })\n .where(\"driveId\", \"=\", driveId)\n .where(\"id\", \"=\", input.folderId)\n .execute();\n }\n\n private async resolveSiblingName(\n trx: Transaction<NodeProcessorDatabase>,\n driveId: string,\n parentFolder: string | null,\n requested: string,\n excludeId: string,\n ): Promise<string> {\n let query = trx\n .selectFrom(\"DriveNode\")\n .select(\"name\")\n .where(\"driveId\", \"=\", driveId)\n .where(\"id\", \"!=\", excludeId);\n query =\n parentFolder === null\n ? query.where(\"parentFolder\", \"is\", null)\n : query.where(\"parentFolder\", \"=\", parentFolder);\n\n const rows = await query.execute();\n return resolveCollision(\n requested,\n rows.map((r) => r.name),\n );\n }\n\n private async lookupDocumentName(\n trx: Transaction<NodeProcessorDatabase>,\n docId: string,\n ): Promise<string | undefined> {\n const row = await trx\n .selectFrom(\"DocumentName\")\n .select(\"name\")\n .where(\"docId\", \"=\", docId)\n .executeTakeFirst();\n return row?.name;\n }\n\n private parseFileMetadata(\n input: AddRelationshipActionInput,\n ): DriveChildFileMetadata {\n const metadata = input.metadata;\n if (\n !metadata ||\n typeof metadata !== \"object\" ||\n (metadata as { kind?: unknown }).kind !== \"file\" ||\n typeof (metadata as { documentType?: unknown }).documentType !== \"string\"\n ) {\n throw new Error(\n `ADD_RELATIONSHIP for target ${input.targetId}: missing or invalid drive/child file metadata (expected { kind: \"file\", parentFolderId, documentType })`,\n );\n }\n const typed = metadata as DriveChildFileMetadata;\n return {\n kind: \"file\",\n parentFolderId: typed.parentFolderId ?? null,\n documentType: typed.documentType,\n };\n }\n\n private async upsertDocumentName(\n trx: Transaction<NodeProcessorDatabase>,\n docId: string,\n name: string,\n ): Promise<void> {\n await trx\n .insertInto(\"DocumentName\")\n .values({ docId, name })\n .onConflict((oc) =>\n oc.column(\"docId\").doUpdateSet({ name, updatedAt: new Date() }),\n )\n .execute();\n }\n}\n","import { type PagedResults, type PagingOptions } from \"@powerhousedao/reactor\";\nimport type { Kysely } from \"kysely\";\nimport type { ReactorDriveDatabase } from \"../schema/tables.js\";\nimport type {\n ReactorDriveFileNode,\n ReactorDriveFolderNode,\n ReactorDriveNode,\n} from \"../types.js\";\nimport type { IDriveReadModel } from \"./interfaces.js\";\n\nconst DEFAULT_LIMIT = 100;\n\ntype KeysetCursor = {\n createdAt: Date;\n id: string;\n};\n\nfunction parseListChildrenPaging(paging: PagingOptions | undefined): {\n limit: number;\n cursor: KeysetCursor | null;\n} {\n if (paging === undefined) {\n return { limit: DEFAULT_LIMIT, cursor: null };\n }\n if (!Number.isInteger(paging.limit) || paging.limit < 1) {\n throw new Error(\n `Invalid paging limit: ${String(paging.limit)} (must be an integer >= 1)`,\n );\n }\n if (paging.cursor === \"\") {\n return { limit: paging.limit, cursor: null };\n }\n return { limit: paging.limit, cursor: decodeKeysetCursor(paging.cursor) };\n}\n\nfunction encodeKeysetCursor(createdAt: Date, id: string): string {\n return globalThis.btoa(\n JSON.stringify({ createdAt: createdAt.toISOString(), id }),\n );\n}\n\nfunction decodeKeysetCursor(cursor: string): KeysetCursor {\n let decoded: string;\n try {\n decoded = globalThis.atob(cursor);\n } catch {\n throw new Error(\n `Invalid paging cursor: ${JSON.stringify(cursor)} (must be a keyset cursor returned by a prior page)`,\n );\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(decoded);\n } catch {\n throw new Error(\n `Invalid paging cursor: ${JSON.stringify(cursor)} (must be a keyset cursor returned by a prior page)`,\n );\n }\n if (\n parsed === null ||\n typeof parsed !== \"object\" ||\n typeof (parsed as { createdAt?: unknown }).createdAt !== \"string\" ||\n typeof (parsed as { id?: unknown }).id !== \"string\"\n ) {\n throw new Error(\n `Invalid paging cursor: ${JSON.stringify(cursor)} (must be a keyset cursor returned by a prior page)`,\n );\n }\n const createdAt = new Date((parsed as { createdAt: string }).createdAt);\n if (Number.isNaN(createdAt.getTime())) {\n throw new Error(\n `Invalid paging cursor: ${JSON.stringify(cursor)} (createdAt is not a valid timestamp)`,\n );\n }\n return { createdAt, id: (parsed as { id: string }).id };\n}\n\nexport class DriveNodeView implements IDriveReadModel {\n constructor(private readonly db: Kysely<ReactorDriveDatabase>) {}\n\n async getNode(\n driveId: string,\n nodeId: string,\n ): Promise<ReactorDriveNode | undefined> {\n const row = await this.db\n .selectFrom(\"DriveNode\")\n .selectAll()\n .where(\"driveId\", \"=\", driveId)\n .where(\"id\", \"=\", nodeId)\n .executeTakeFirst();\n if (!row) return undefined;\n return rowToNode(row);\n }\n\n async listChildren(\n driveId: string,\n parentFolder: string | null | undefined,\n paging?: PagingOptions,\n ): Promise<PagedResults<ReactorDriveNode>> {\n const { limit, cursor } = parseListChildrenPaging(paging);\n\n let query = this.db\n .selectFrom(\"DriveNode\")\n .selectAll()\n .where(\"driveId\", \"=\", driveId);\n\n if (parentFolder === null) {\n query = query.where(\"parentFolder\", \"is\", null);\n } else if (parentFolder !== undefined) {\n query = query.where(\"parentFolder\", \"=\", parentFolder);\n }\n\n if (cursor !== null) {\n query = query.where((eb) =>\n eb(\n eb.refTuple(\"createdAt\", \"id\"),\n \">\",\n eb.tuple(cursor.createdAt, cursor.id),\n ),\n );\n }\n\n const rows = await query\n .orderBy(\"createdAt\", \"asc\")\n .orderBy(\"id\", \"asc\")\n .limit(limit + 1)\n .execute();\n\n const hasMore = rows.length > limit;\n const sliced = hasMore ? rows.slice(0, limit) : rows;\n const last = sliced[sliced.length - 1];\n\n return {\n results: sliced.map(rowToNode),\n options: { cursor: paging?.cursor ?? \"\", limit },\n nextCursor: hasMore\n ? encodeKeysetCursor(last.createdAt, last.id)\n : undefined,\n };\n }\n\n async listAll(driveId: string): Promise<ReactorDriveNode[]> {\n const rows = await this.db\n .selectFrom(\"DriveNode\")\n .selectAll()\n .where(\"driveId\", \"=\", driveId)\n .orderBy(\"createdAt\", \"asc\")\n .orderBy(\"id\", \"asc\")\n .execute();\n return rows.map(rowToNode);\n }\n\n async getDescendants(\n driveId: string,\n root: string,\n ): Promise<ReactorDriveNode[]> {\n const rows = await this.db\n .withRecursive(\"descendants\", (qb) =>\n qb\n .selectFrom(\"DriveNode\")\n .select([\n \"driveId\",\n \"id\",\n \"kind\",\n \"name\",\n \"requestedName\",\n \"parentFolder\",\n \"documentType\",\n \"createdAt\",\n ])\n .where(\"driveId\", \"=\", driveId)\n .where(\"id\", \"=\", root)\n .unionAll(\n qb\n .selectFrom(\"DriveNode\")\n .innerJoin(\n \"descendants\",\n \"DriveNode.parentFolder\",\n \"descendants.id\",\n )\n .where(\"DriveNode.driveId\", \"=\", driveId)\n .select([\n \"DriveNode.driveId\",\n \"DriveNode.id\",\n \"DriveNode.kind\",\n \"DriveNode.name\",\n \"DriveNode.requestedName\",\n \"DriveNode.parentFolder\",\n \"DriveNode.documentType\",\n \"DriveNode.createdAt\",\n ]),\n ),\n )\n .selectFrom(\"descendants\")\n .select([\n \"driveId\",\n \"id\",\n \"kind\",\n \"name\",\n \"requestedName\",\n \"parentFolder\",\n \"documentType\",\n ])\n .orderBy(\"createdAt\", \"asc\")\n .orderBy(\"id\", \"asc\")\n .execute();\n return rows.map(rowToNode);\n }\n}\n\ntype DriveNodeRow = {\n driveId: string;\n id: string;\n kind: \"file\" | \"folder\";\n name: string;\n requestedName: string;\n parentFolder: string | null;\n documentType: string | null;\n};\n\nfunction rowToNode(row: DriveNodeRow): ReactorDriveNode {\n if (row.kind === \"file\") {\n if (row.documentType === null) {\n throw new Error(\n `DriveNode ${row.driveId}/${row.id}: file row has null documentType, which violates the schema CHECK constraint`,\n );\n }\n const node: ReactorDriveFileNode = {\n kind: \"file\",\n id: row.id,\n driveId: row.driveId,\n parentFolder: row.parentFolder,\n name: row.name,\n documentType: row.documentType,\n };\n return node;\n }\n const node: ReactorDriveFolderNode = {\n kind: \"folder\",\n id: row.id,\n driveId: row.driveId,\n parentFolder: row.parentFolder,\n name: row.name,\n };\n return node;\n}\n","import {\n addRelationshipAction,\n createDocumentAction,\n removeRelationshipAction,\n upgradeDocumentAction,\n type BatchExecutionRequest,\n type ExecutionJobPlan,\n type IDriveClient,\n type IReactorClient,\n type PagedResults,\n type PagingOptions,\n type PropagationMode,\n} from \"@powerhousedao/reactor\";\nimport type {\n DocumentDriveDocument,\n DriveInput,\n FileNode,\n FolderNode,\n Node,\n} from \"@powerhousedao/shared/document-drive\";\nimport {\n assertAuthPreservedOnDuplicate,\n createCopyHeader,\n generateId,\n replayDocumentVersioned,\n requestedSignaturePolicy,\n withSignaturePolicy,\n type Action,\n type CreateDocumentActionInput,\n type PHBaseState,\n type PHDocument,\n type Reducer,\n type VersionedReplayConfig,\n} from \"@powerhousedao/shared/document-model\";\nimport {\n DRIVE_CHILD_RELATIONSHIP_TYPE,\n REACTOR_DRIVE_DOCUMENT_TYPE,\n} from \"../constants.js\";\nimport {\n addFolderAction,\n removeFolderAction,\n setAvailableOfflineAction,\n setDriveIconAction,\n setDriveNameAction,\n setSharingTypeAction,\n updateFolderAction,\n} from \"../actions.js\";\nimport { reactorDriveCreateDocument } from \"../module.js\";\nimport type { IDriveReadModel } from \"../read-model/interfaces.js\";\nimport type {\n DriveChildFileMetadata,\n ReactorDriveFileNode,\n ReactorDriveFolderNode,\n ReactorDriveNode,\n} from \"../types.js\";\n\nexport interface ReactorDriveClientArgs {\n reactor: IReactorClient;\n readModel: IDriveReadModel;\n}\n\n/**\n * Implementation of {@link IDriveClient} backed by the reactor's relationship\n * primitives and the drive-scoped folder actions. Folder structure lives in\n * the operation log (ADD_FOLDER/UPDATE_FOLDER/REMOVE_FOLDER + ADD_RELATIONSHIP\n * for files) and is materialised by `NodeProcessor` into the `DriveNode`\n * table consumed via {@link IDriveReadModel}.\n */\nexport class ReactorDriveClient implements IDriveClient {\n private readonly reactor: IReactorClient;\n private readonly readModel: IDriveReadModel;\n\n constructor(args: ReactorDriveClientArgs) {\n this.reactor = args.reactor;\n this.readModel = args.readModel;\n }\n\n async create(\n input: DriveInput,\n signal?: AbortSignal,\n ): Promise<DocumentDriveDocument> {\n const driveDoc = withSignaturePolicy(\n reactorDriveCreateDocument({\n global: {\n name: input.global.name,\n icon: input.global.icon ?? null,\n },\n }),\n requestedSignaturePolicy(\n input,\n await this.reactor.getCreateSignaturePolicy(),\n ),\n { protocolVersions: input.protocolVersions },\n );\n if (input.local) {\n if (typeof input.local.sharingType === \"string\") {\n driveDoc.state.local.sharingType = input.local.sharingType;\n }\n if (typeof input.local.availableOffline === \"boolean\") {\n driveDoc.state.local.availableOffline = input.local.availableOffline;\n }\n }\n if (input.preferredEditor) {\n driveDoc.header.meta = {\n ...driveDoc.header.meta,\n preferredEditor: input.preferredEditor,\n };\n }\n const created = await this.reactor.create(driveDoc, undefined, signal);\n return this.toLegacyDriveDocument(created, created.header.id);\n }\n\n async addFile<TDocument extends PHDocument = PHDocument>(\n driveIdentifier: string,\n document: PHDocument,\n parentFolder?: string,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n const documentId = document.header.id;\n\n const createInput: CreateDocumentActionInput = {\n model: document.header.documentType,\n version: 0,\n documentId,\n signing: {\n signature: documentId,\n publicKey: document.header.sig.publicKey,\n nonce: document.header.sig.nonce,\n createdAtUtcIso: document.header.createdAtUtcIso,\n documentType: document.header.documentType,\n },\n slug: document.header.slug,\n name: document.header.name,\n branch: document.header.branch,\n meta: document.header.meta,\n protocolVersions: document.header.protocolVersions ?? {\n \"base-reducer\": 2,\n },\n };\n\n const metadata: DriveChildFileMetadata = {\n kind: \"file\",\n parentFolderId: parentFolder ?? null,\n documentType: document.header.documentType,\n };\n\n const request: BatchExecutionRequest = {\n jobs: [\n {\n key: \"create\",\n documentId,\n scope: \"document\",\n branch: \"main\",\n actions: [\n createDocumentAction(createInput),\n upgradeDocumentAction({\n documentId,\n model: document.header.documentType,\n fromVersion: 0,\n toVersion: document.state.document.version,\n initialState: document.state,\n }),\n ],\n dependsOn: [],\n },\n {\n key: \"link\",\n documentId: driveIdentifier,\n scope: \"document\",\n branch: \"main\",\n actions: [\n addRelationshipAction(\n driveIdentifier,\n documentId,\n DRIVE_CHILD_RELATIONSHIP_TYPE,\n metadata,\n ),\n ],\n dependsOn: [\"create\"],\n },\n ],\n };\n\n await this.reactor.executeBatch(request, signal);\n return this.reactor.get<TDocument>(documentId, undefined, signal);\n }\n\n async addFolder(\n driveIdentifier: string,\n name: string,\n parentFolder?: string,\n signal?: AbortSignal,\n ): Promise<FolderNode> {\n const folderId = generateId();\n await this.reactor.execute(\n driveIdentifier,\n \"main\",\n [\n addFolderAction({\n folderId,\n parentFolderId: parentFolder ?? null,\n name,\n }),\n ],\n signal,\n );\n return {\n id: folderId,\n kind: \"folder\",\n name,\n parentFolder: parentFolder ?? null,\n };\n }\n\n async removeNode(\n driveIdentifier: string,\n nodeId: string,\n signal?: AbortSignal,\n ): Promise<void> {\n const node = await this.readModel.getNode(driveIdentifier, nodeId, signal);\n if (!node) {\n throw new Error(`Node ${nodeId} not found in drive ${driveIdentifier}`);\n }\n\n if (node.kind === \"folder\") {\n const subtree = await this.readModel.getDescendants(\n driveIdentifier,\n nodeId,\n signal,\n );\n const fileDescendants = subtree.filter(\n (n): n is ReactorDriveFileNode => n.kind === \"file\",\n );\n const subFolders = subtree\n .filter((n): n is ReactorDriveFolderNode => n.kind === \"folder\")\n .filter((f) => f.id !== nodeId);\n const deepestFirstFolders = this.orderDeepestFirst(subFolders, nodeId);\n\n const batch: Action[] = [\n ...fileDescendants.map((f) =>\n removeRelationshipAction(\n driveIdentifier,\n f.id,\n DRIVE_CHILD_RELATIONSHIP_TYPE,\n ),\n ),\n ...deepestFirstFolders.map((f) =>\n removeFolderAction({ folderId: f.id }),\n ),\n removeFolderAction({ folderId: nodeId }),\n ];\n\n await this.reactor.execute(driveIdentifier, \"main\", batch, signal);\n\n for (const file of fileDescendants) {\n await this.reactor.deleteDocument(\n file.id,\n \"cascade\" as PropagationMode,\n signal,\n );\n }\n return;\n }\n\n await this.reactor.execute(\n driveIdentifier,\n \"main\",\n [\n removeRelationshipAction(\n driveIdentifier,\n nodeId,\n DRIVE_CHILD_RELATIONSHIP_TYPE,\n ),\n ],\n signal,\n );\n await this.reactor.deleteDocument(nodeId, undefined, signal);\n }\n\n async renameNode(\n driveIdentifier: string,\n nodeId: string,\n name: string,\n signal?: AbortSignal,\n ): Promise<Node> {\n const node = await this.readModel.getNode(driveIdentifier, nodeId, signal);\n if (!node) {\n throw new Error(`Node ${nodeId} not found in drive ${driveIdentifier}`);\n }\n\n if (node.kind === \"file\") {\n await this.reactor.rename(nodeId, name, \"main\", signal);\n } else {\n await this.reactor.execute(\n driveIdentifier,\n \"main\",\n [updateFolderAction({ folderId: nodeId, name })],\n signal,\n );\n }\n\n const updated = await this.readModel.getNode(\n driveIdentifier,\n nodeId,\n signal,\n );\n if (!updated) {\n throw new Error(\"Node missing from drive after rename\");\n }\n return this.toLegacyNode(updated);\n }\n\n async setPreferredEditorOnNode(\n nodeId: string,\n preferredEditor: string | null,\n signal?: AbortSignal,\n ): Promise<PHDocument> {\n return this.reactor.setPreferredEditor(\n nodeId,\n preferredEditor,\n \"main\",\n signal,\n );\n }\n\n async moveNode(\n driveIdentifier: string,\n srcNodeId: string,\n targetParentFolderId: string | undefined,\n signal?: AbortSignal,\n ): Promise<DocumentDriveDocument> {\n const node = await this.readModel.getNode(\n driveIdentifier,\n srcNodeId,\n signal,\n );\n if (!node) {\n throw new Error(\n `Node ${srcNodeId} not found in drive ${driveIdentifier}`,\n );\n }\n\n if (node.kind === \"folder\") {\n await this.reactor.execute(\n driveIdentifier,\n \"main\",\n [\n updateFolderAction({\n folderId: srcNodeId,\n parentFolderId: targetParentFolderId ?? null,\n }),\n ],\n signal,\n );\n } else {\n const metadata: DriveChildFileMetadata = {\n kind: \"file\",\n parentFolderId: targetParentFolderId ?? null,\n documentType: node.documentType,\n };\n await this.reactor.execute(\n driveIdentifier,\n \"main\",\n [\n removeRelationshipAction(\n driveIdentifier,\n srcNodeId,\n DRIVE_CHILD_RELATIONSHIP_TYPE,\n ),\n addRelationshipAction(\n driveIdentifier,\n srcNodeId,\n DRIVE_CHILD_RELATIONSHIP_TYPE,\n metadata,\n ),\n ],\n signal,\n );\n }\n\n const drive = await this.reactor.get<PHDocument>(\n driveIdentifier,\n undefined,\n signal,\n );\n return this.toLegacyDriveDocument(drive, driveIdentifier);\n }\n\n async copyNode(\n driveIdentifier: string,\n srcNodeId: string,\n targetParentFolderId: string | undefined,\n signal?: AbortSignal,\n ): Promise<DocumentDriveDocument> {\n const srcNode = await this.readModel.getNode(\n driveIdentifier,\n srcNodeId,\n signal,\n );\n if (!srcNode) {\n throw new Error(\n `Node ${srcNodeId} not found in drive ${driveIdentifier}`,\n );\n }\n\n const subtree = await this.readModel.getDescendants(\n driveIdentifier,\n srcNodeId,\n signal,\n );\n\n if (\n targetParentFolderId !== undefined &&\n subtree.some((n) => n.id === targetParentFolderId)\n ) {\n throw new Error(\n `Cannot copy node ${srcNodeId} into itself or one of its descendants (target: ${targetParentFolderId})`,\n );\n }\n\n const idMap = new Map<string, string>();\n for (const node of subtree) {\n idMap.set(node.id, generateId());\n }\n const policy = await this.reactor.getCreateSignaturePolicy();\n\n const jobs: ExecutionJobPlan[] = [];\n const driveActions: Action[] = [];\n const fileCreateKeys: string[] = [];\n\n for (const node of subtree) {\n let newId = idMap.get(node.id)!;\n let newParent: string | null;\n if (node.id === srcNodeId) {\n newParent = targetParentFolderId ?? null;\n } else {\n if (node.parentFolder == null) {\n throw new Error(\n `copyNode: descendant ${node.id} has no parentFolder`,\n );\n }\n const mapped = idMap.get(node.parentFolder);\n if (mapped === undefined) {\n throw new Error(\n `copyNode: descendant ${node.id} parent ${node.parentFolder} missing from idMap`,\n );\n }\n newParent = mapped;\n }\n\n if (node.kind === \"folder\") {\n driveActions.push(\n addFolderAction({\n folderId: newId,\n parentFolderId: newParent,\n name: node.name,\n }),\n );\n continue;\n }\n\n const srcDoc = await this.reactor.get<PHDocument>(\n node.id,\n undefined,\n signal,\n );\n const documentType = srcDoc.header.documentType;\n const { results: allModules } =\n await this.reactor.getDocumentModelModules();\n const modulesForType = allModules.filter(\n (m) => m.documentModel.global.id === documentType,\n );\n if (modulesForType.length === 0) {\n throw new Error(\n `Document model module not found for type: ${documentType}`,\n );\n }\n const reducers: VersionedReplayConfig[\"reducers\"] = {};\n for (const m of modulesForType) {\n reducers[m.version ?? 1] = m.reducer as Reducer<PHBaseState>;\n }\n const config: VersionedReplayConfig = { reducers };\n const replayHeader = createCopyHeader(srcDoc.header, newId, policy);\n // A v2-required copy takes a derived id; files head no subtree.\n newId = replayHeader.id;\n const duplicated: PHDocument = replayDocumentVersioned(\n srcDoc.initialState,\n srcDoc.operations,\n config,\n replayHeader,\n );\n assertAuthPreservedOnDuplicate(\n node.id,\n srcDoc.state.auth,\n duplicated.state.auth,\n );\n duplicated.header.name = node.name;\n\n const createInput: CreateDocumentActionInput = {\n model: duplicated.header.documentType,\n version: 0,\n documentId: newId,\n signing: {\n signature: newId,\n publicKey: duplicated.header.sig.publicKey,\n nonce: duplicated.header.sig.nonce,\n createdAtUtcIso: duplicated.header.createdAtUtcIso,\n documentType: duplicated.header.documentType,\n },\n slug: duplicated.header.slug,\n name: duplicated.header.name,\n branch: duplicated.header.branch,\n meta: duplicated.header.meta,\n protocolVersions: duplicated.header.protocolVersions ?? {\n \"base-reducer\": 2,\n },\n };\n\n const createKey = `create:${newId}`;\n jobs.push({\n key: createKey,\n documentId: newId,\n scope: \"document\",\n branch: \"main\",\n actions: [\n createDocumentAction(createInput),\n upgradeDocumentAction({\n documentId: newId,\n model: duplicated.header.documentType,\n fromVersion: 0,\n toVersion: duplicated.state.document.version,\n initialState: duplicated.state,\n }),\n ],\n dependsOn: [],\n });\n fileCreateKeys.push(createKey);\n\n const metadata: DriveChildFileMetadata = {\n kind: \"file\",\n parentFolderId: newParent,\n documentType: duplicated.header.documentType,\n };\n driveActions.push(\n addRelationshipAction(\n driveIdentifier,\n newId,\n DRIVE_CHILD_RELATIONSHIP_TYPE,\n metadata,\n ),\n );\n }\n\n if (driveActions.length > 0) {\n jobs.push({\n key: \"drive\",\n documentId: driveIdentifier,\n scope: \"document\",\n branch: \"main\",\n actions: driveActions,\n dependsOn: fileCreateKeys,\n });\n }\n\n if (jobs.length > 0) {\n const request: BatchExecutionRequest = { jobs };\n await this.reactor.executeBatch(request, signal);\n }\n\n const drive = await this.reactor.get<PHDocument>(\n driveIdentifier,\n undefined,\n signal,\n );\n return this.toLegacyDriveDocument(drive, driveIdentifier);\n }\n\n async getNode(\n driveIdentifier: string,\n nodeId: string,\n signal?: AbortSignal,\n ): Promise<Node> {\n const node = await this.readModel.getNode(driveIdentifier, nodeId, signal);\n if (!node) {\n throw new Error(`Node ${nodeId} not found in drive ${driveIdentifier}`);\n }\n return this.toLegacyNode(node);\n }\n\n async listNodes(\n driveIdentifier: string,\n parentFolder?: string | null,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<Node>> {\n const page = await this.readModel.listChildren(\n driveIdentifier,\n parentFolder,\n paging,\n signal,\n );\n return {\n results: page.results.map((node) => this.toLegacyNode(node)),\n options: page.options,\n ...(page.nextCursor !== undefined ? { nextCursor: page.nextCursor } : {}),\n };\n }\n\n private toLegacyNode(node: ReactorDriveNode): Node {\n if (node.kind === \"file\") {\n const file: ReactorDriveFileNode = node;\n const legacy: FileNode = {\n id: file.id,\n kind: \"file\",\n name: file.name,\n parentFolder: file.parentFolder,\n documentType: file.documentType,\n };\n return legacy;\n }\n const folder: ReactorDriveFolderNode = node;\n const legacy: FolderNode = {\n id: folder.id,\n kind: \"folder\",\n name: folder.name,\n parentFolder: folder.parentFolder,\n };\n return legacy;\n }\n\n private async toLegacyDriveDocument(\n doc: PHDocument,\n driveId: string,\n ): Promise<DocumentDriveDocument> {\n if (doc.header.documentType !== REACTOR_DRIVE_DOCUMENT_TYPE) {\n throw new Error(\n `Document ${doc.header.id} is not a reactor-drive document`,\n );\n }\n const allNodes = await this.readModel.listAll(driveId);\n const legacy = structuredClone(doc) as unknown as DocumentDriveDocument;\n const existingGlobal = legacy.state.global as Partial<\n DocumentDriveDocument[\"state\"][\"global\"]\n >;\n legacy.state.global = {\n name: existingGlobal.name ?? \"\",\n icon: existingGlobal.icon ?? null,\n nodes: allNodes.map((n) => this.toLegacyNode(n)),\n };\n return legacy;\n }\n\n private orderDeepestFirst(\n folders: ReactorDriveFolderNode[],\n rootId: string,\n ): ReactorDriveFolderNode[] {\n const depthById = new Map<string, number>();\n const compute = (id: string): number => {\n if (id === rootId) return 0;\n const cached = depthById.get(id);\n if (cached !== undefined) return cached;\n const folder = folders.find((f) => f.id === id);\n if (!folder) return 0;\n const parent = folder.parentFolder ?? rootId;\n const depth = compute(parent) + 1;\n depthById.set(id, depth);\n return depth;\n };\n return folders.slice().sort((a, b) => compute(b.id) - compute(a.id));\n }\n}\n\nexport {\n setAvailableOfflineAction,\n setDriveIconAction,\n setDriveNameAction,\n setSharingTypeAction,\n};\n","import { gql } from \"graphql-tag\";\n\nexport const typeDefs = gql`\n enum ReactorDriveNodeKind {\n FILE\n FOLDER\n }\n\n input ReactorDrivePagingInput {\n cursor: String\n limit: Int\n }\n\n type ReactorDriveFileNode {\n id: ID!\n driveId: ID!\n name: String!\n parentFolder: ID\n documentType: String!\n }\n\n type ReactorDriveFolderNode {\n id: ID!\n driveId: ID!\n name: String!\n parentFolder: ID\n children(\n paging: ReactorDrivePagingInput\n kind: ReactorDriveNodeKind\n ): ReactorDriveNodePage!\n }\n\n union ReactorDriveNode = ReactorDriveFileNode | ReactorDriveFolderNode\n\n type ReactorDriveNodePage {\n results: [ReactorDriveNode!]!\n nextCursor: String\n hasMore: Boolean!\n }\n\n type ReactorDrive {\n id: ID!\n name: String!\n icon: String\n \"Null when the caller may not read the drive's local scope.\"\n sharingType: String\n \"Null when the caller may not read the drive's local scope.\"\n availableOffline: Boolean\n rootNodes(\n paging: ReactorDrivePagingInput\n kind: ReactorDriveNodeKind\n ): ReactorDriveNodePage!\n }\n\n type Query {\n reactorDrive(id: ID!): ReactorDrive\n reactorDriveNode(driveId: ID!, id: ID!): ReactorDriveNode\n reactorDriveDescendants(driveId: ID!, root: ID!): [ReactorDriveNode!]!\n }\n`;\n","import type {\n IReactorClient,\n PagedResults,\n PagingOptions,\n} from \"@powerhousedao/reactor\";\nimport type {\n AuthSubject,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport { REACTOR_DRIVE_DOCUMENT_TYPE } from \"../constants.js\";\nimport type { IDriveReadModel } from \"../read-model/interfaces.js\";\nimport type { ReactorDrivePHState, ReactorDriveNode } from \"../types.js\";\n\nexport interface ReactorDriveResolverContext {\n reactorClient: IReactorClient;\n readModel: IDriveReadModel;\n /** The authenticated caller; absent reads as anonymous, never the host. */\n user?: { address?: string; appKey?: string };\n /** The host's own read check, applied on top of the reactor's read gate. */\n hostCanRead?: (documentId: string, address?: string) => Promise<boolean>;\n}\n\ntype PagingInput = { cursor?: string; limit?: number };\ntype NodeKindFilter = \"FILE\" | \"FOLDER\" | undefined;\n\nconst DEFAULT_LIMIT = 100;\n\nfunction toPaging(input: PagingInput | undefined): PagingOptions | undefined {\n if (!input) return undefined;\n return {\n cursor: input.cursor ?? \"\",\n limit: input.limit ?? DEFAULT_LIMIT,\n };\n}\n\nfunction filterByKind(\n page: PagedResults<ReactorDriveNode>,\n kind: NodeKindFilter,\n): PagedResults<ReactorDriveNode> {\n if (!kind) return page;\n const wanted = kind === \"FILE\" ? \"file\" : \"folder\";\n return {\n ...page,\n results: page.results.filter((node) => node.kind === wanted),\n };\n}\n\nfunction shapePage(page: PagedResults<ReactorDriveNode>) {\n return {\n results: page.results,\n nextCursor: page.nextCursor,\n hasMore: page.nextCursor !== undefined,\n };\n}\n\nfunction subjectOf(ctx: ReactorDriveResolverContext): AuthSubject {\n return { address: ctx.user?.address, key: ctx.user?.appKey };\n}\n\n/** Whether a listing would serve the document to the caller; fails closed. */\nasync function serves(\n ctx: ReactorDriveResolverContext,\n documentId: string,\n): Promise<boolean> {\n try {\n if (\n ctx.hostCanRead &&\n !(await ctx.hostCanRead(documentId, ctx.user?.address))\n ) {\n return false;\n }\n return await ctx.reactorClient.isServed(documentId, {\n subject: subjectOf(ctx),\n });\n } catch {\n return false;\n }\n}\n\n/** A file node names its document, so it is served only with that document. */\nasync function servesNode(\n ctx: ReactorDriveResolverContext,\n node: ReactorDriveNode,\n): Promise<boolean> {\n return node.kind === \"folder\" || serves(ctx, node.id);\n}\n\nasync function servedNodes(\n ctx: ReactorDriveResolverContext,\n nodes: ReactorDriveNode[],\n): Promise<ReactorDriveNode[]> {\n const served = await Promise.all(nodes.map((node) => servesNode(ctx, node)));\n return nodes.filter((_, i) => served[i]);\n}\n\nasync function servedPage(\n ctx: ReactorDriveResolverContext,\n page: PagedResults<ReactorDriveNode>,\n): Promise<PagedResults<ReactorDriveNode>> {\n return { ...page, results: await servedNodes(ctx, page.results) };\n}\n\n/**\n * Builds GraphQL resolvers backed by the drive read model. Dependencies are\n * read off the GraphQL context unless bound here. Every read is decided as the\n * caller: a drive's nodes are its content, so they are served only with the\n * drive, and a file node only with its own document too.\n */\nexport function createReactorDriveResolvers(\n bound: Omit<Partial<ReactorDriveResolverContext>, \"user\"> = {},\n) {\n const withBound = (\n ctx: ReactorDriveResolverContext,\n ): ReactorDriveResolverContext => ({ ...ctx, ...bound, user: ctx.user });\n\n return {\n Query: {\n async reactorDrive(\n _root: unknown,\n args: { id: string },\n context: ReactorDriveResolverContext,\n ) {\n const ctx = withBound(context);\n const document = await ctx.reactorClient.get<\n PHDocument<ReactorDrivePHState>\n >(args.id, { subject: subjectOf(ctx) });\n if (document.header.documentType !== REACTOR_DRIVE_DOCUMENT_TYPE) {\n return null;\n }\n if (!(await serves(ctx, document.header.id))) {\n return null;\n }\n const { global, local } = document.state as Partial<\n PHDocument<ReactorDrivePHState>[\"state\"]\n >;\n if (!global) {\n return null;\n }\n return {\n id: document.header.id,\n name: global.name,\n icon: global.icon,\n sharingType: local?.sharingType ?? null,\n availableOffline: local?.availableOffline ?? null,\n };\n },\n async reactorDriveNode(\n _root: unknown,\n args: { driveId: string; id: string },\n context: ReactorDriveResolverContext,\n ) {\n const ctx = withBound(context);\n if (!(await serves(ctx, args.driveId))) {\n return undefined;\n }\n const node = await ctx.readModel.getNode(args.driveId, args.id);\n if (!node || !(await servesNode(ctx, node))) {\n return undefined;\n }\n return node;\n },\n async reactorDriveDescendants(\n _root: unknown,\n args: { driveId: string; root: string },\n context: ReactorDriveResolverContext,\n ) {\n const ctx = withBound(context);\n if (!(await serves(ctx, args.driveId))) {\n return [];\n }\n return servedNodes(\n ctx,\n await ctx.readModel.getDescendants(args.driveId, args.root),\n );\n },\n },\n ReactorDrive: {\n async rootNodes(\n parent: { id: string },\n args: { paging?: PagingInput; kind?: NodeKindFilter },\n context: ReactorDriveResolverContext,\n ) {\n const ctx = withBound(context);\n const page = await ctx.readModel.listChildren(\n parent.id,\n null,\n toPaging(args.paging),\n );\n return shapePage(await servedPage(ctx, filterByKind(page, args.kind)));\n },\n },\n ReactorDriveFolderNode: {\n async children(\n parent: { id: string; driveId: string },\n args: { paging?: PagingInput; kind?: NodeKindFilter },\n context: ReactorDriveResolverContext,\n ) {\n const ctx = withBound(context);\n const page = await ctx.readModel.listChildren(\n parent.driveId,\n parent.id,\n toPaging(args.paging),\n );\n return shapePage(await servedPage(ctx, filterByKind(page, args.kind)));\n },\n },\n ReactorDriveNode: {\n __resolveType(node: ReactorDriveNode) {\n return node.kind === \"file\"\n ? \"ReactorDriveFileNode\"\n : \"ReactorDriveFolderNode\";\n },\n },\n };\n}\n","import {\n addRelationshipAction,\n type IReactorClient,\n} from \"@powerhousedao/reactor\";\nimport type { FileNode, Node } from \"@powerhousedao/shared/document-drive\";\nimport type { Action } from \"@powerhousedao/shared/document-model\";\nimport { addFolderAction } from \"../actions.js\";\nimport { DRIVE_CHILD_RELATIONSHIP_TYPE } from \"../constants.js\";\nimport type { IDriveReadModel } from \"../read-model/interfaces.js\";\nimport type { DriveChildFileMetadata } from \"../types.js\";\n\nexport interface MigrateLegacyDriveStateArgs {\n reactor: IReactorClient;\n readModel: IDriveReadModel;\n driveId: string;\n nodes: Node[];\n branch?: string;\n signal?: AbortSignal;\n}\n\nexport interface MigrateLegacyDriveStateResult {\n emittedActions: number;\n skippedExisting: number;\n}\n\n/**\n * Translates a legacy `document-drive` `state.global.nodes` array into the\n * action vocabulary used by the new reactor-drive module.\n *\n * Folder nodes are emitted as `ADD_FOLDER` actions targeting the drive\n * document. File nodes are emitted as `ADD_RELATIONSHIP` actions on the drive\n * with `drive/child` metadata carrying the parent folder id. File nodes\n * assume the underlying PHDocument still exists under the same id — the\n * migration only re-links it into the new drive, it does not recreate\n * documents.\n *\n * Re-running the migration is safe: existing `DriveNode` rows are skipped\n * so already-migrated nodes are left untouched.\n */\nexport async function migrateLegacyDriveState(\n args: MigrateLegacyDriveStateArgs,\n): Promise<MigrateLegacyDriveStateResult> {\n const { reactor, readModel, driveId, nodes, signal } = args;\n const branch = args.branch ?? \"main\";\n\n const ordered = orderLegacyNodes(nodes);\n const actions: Action[] = [];\n let skippedExisting = 0;\n\n for (const node of ordered) {\n const existing = await readModel.getNode(driveId, node.id, signal);\n if (existing) {\n skippedExisting += 1;\n continue;\n }\n actions.push(toAction(driveId, node));\n }\n\n if (actions.length === 0) {\n return { emittedActions: 0, skippedExisting };\n }\n\n await reactor.execute(driveId, branch, actions, signal);\n return { emittedActions: actions.length, skippedExisting };\n}\n\nfunction toAction(driveId: string, node: Node): Action {\n if (node.kind === \"folder\") {\n return addFolderAction({\n folderId: node.id,\n parentFolderId: node.parentFolder ?? null,\n name: node.name,\n });\n }\n const fileNode = node as FileNode;\n const metadata: DriveChildFileMetadata = {\n kind: \"file\",\n parentFolderId: fileNode.parentFolder ?? null,\n documentType: fileNode.documentType,\n };\n return addRelationshipAction(\n driveId,\n fileNode.id,\n DRIVE_CHILD_RELATIONSHIP_TYPE,\n metadata,\n );\n}\n\n/**\n * Sorts legacy nodes so that any folder appears before its children. The\n * legacy state stores nodes in insertion order, which usually already\n * satisfies that property, but defensive sorting keeps replay deterministic\n * if the input was constructed out of order.\n */\nfunction orderLegacyNodes(nodes: Node[]): Node[] {\n const byId = new Map<string, Node>();\n for (const node of nodes) {\n byId.set(node.id, node);\n }\n const visited = new Set<string>();\n const ordered: Node[] = [];\n\n for (const start of nodes) {\n if (visited.has(start.id)) continue;\n const chain: Node[] = [];\n const seenInWalk = new Set<string>();\n let current: Node | undefined = start;\n while (current && !visited.has(current.id) && !seenInWalk.has(current.id)) {\n seenInWalk.add(current.id);\n chain.push(current);\n const parentId: string | null | undefined = current.parentFolder;\n current = parentId ? byId.get(parentId) : undefined;\n }\n while (chain.length > 0) {\n const node = chain.pop()!;\n if (visited.has(node.id)) continue;\n visited.add(node.id);\n ordered.push(node);\n }\n }\n return ordered;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@powerhousedao/reactor-drive",
|
|
3
|
-
"version": "6.2.3-dev.
|
|
3
|
+
"version": "6.2.3-dev.41",
|
|
4
4
|
"license": "AGPL-3.0-only",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -23,17 +23,19 @@
|
|
|
23
23
|
},
|
|
24
24
|
"sideEffects": false,
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@powerhousedao/reactor": "6.2.3-dev.
|
|
27
|
-
"@powerhousedao/shared": "6.2.3-dev.
|
|
28
|
-
"document-model": "6.2.3-dev.
|
|
26
|
+
"@powerhousedao/reactor": "6.2.3-dev.41",
|
|
27
|
+
"@powerhousedao/shared": "6.2.3-dev.41",
|
|
28
|
+
"document-model": "6.2.3-dev.41",
|
|
29
29
|
"graphql": "^16",
|
|
30
30
|
"graphql-tag": "^2",
|
|
31
31
|
"kysely": "0.28.16"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@electric-sql/pglite": "0.3.15",
|
|
35
|
+
"@types/pg": "8.16.0",
|
|
35
36
|
"kysely-pglite-dialect": "1.2.0",
|
|
36
|
-
"
|
|
37
|
+
"pg": "8.18.0",
|
|
38
|
+
"tsdown": "0.23.0",
|
|
37
39
|
"vitest": "4.1.1"
|
|
38
40
|
},
|
|
39
41
|
"description": "Drive management layer for the Powerhouse reactor — handles document drive operations, sync, and storage backends.",
|
|
@@ -50,7 +52,7 @@
|
|
|
50
52
|
"homepage": "https://academy.vetra.io/academy/Reference/Reactor/WorkingWithTheReactor",
|
|
51
53
|
"scripts": {
|
|
52
54
|
"tsc": "tsc",
|
|
53
|
-
"lint": "
|
|
55
|
+
"lint": "cd ../.. && oxlint packages/reactor-drive",
|
|
54
56
|
"build": "tsdown",
|
|
55
57
|
"test": "vitest --run",
|
|
56
58
|
"bench": "vitest bench --run"
|