@powerhousedao/reactor-drive 6.2.3-dev.8 → 6.2.3-staging.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +53 -43
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +95 -36
- package/dist/index.js.map +1 -1
- package/package.json +6 -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 * as kysely from "kysely";
|
|
4
2
|
import { Generated, Kysely } from "kysely";
|
|
5
3
|
import { BaseReadModel, 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,10 +149,18 @@ 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 {
|
|
160
164
|
private readonly driveDb;
|
|
161
165
|
private readonly baseDb;
|
|
162
166
|
private readonly schema;
|
|
@@ -209,7 +213,7 @@ interface IDriveReadModel {
|
|
|
209
213
|
}
|
|
210
214
|
//#endregion
|
|
211
215
|
//#region src/read-model/drive-node-view.d.ts
|
|
212
|
-
declare class DriveNodeView implements IDriveReadModel {
|
|
216
|
+
export declare class DriveNodeView implements IDriveReadModel {
|
|
213
217
|
private readonly db;
|
|
214
218
|
constructor(db: Kysely<ReactorDriveDatabase>);
|
|
215
219
|
getNode(driveId: string, nodeId: string): Promise<ReactorDriveNode | undefined>;
|
|
@@ -230,7 +234,7 @@ interface ReactorDriveClientArgs {
|
|
|
230
234
|
* for files) and is materialised by `NodeProcessor` into the `DriveNode`
|
|
231
235
|
* table consumed via {@link IDriveReadModel}.
|
|
232
236
|
*/
|
|
233
|
-
declare class ReactorDriveClient implements IDriveClient {
|
|
237
|
+
export declare class ReactorDriveClient implements IDriveClient {
|
|
234
238
|
private readonly reactor;
|
|
235
239
|
private readonly readModel;
|
|
236
240
|
constructor(args: ReactorDriveClientArgs);
|
|
@@ -250,12 +254,19 @@ declare class ReactorDriveClient implements IDriveClient {
|
|
|
250
254
|
}
|
|
251
255
|
//#endregion
|
|
252
256
|
//#region src/subgraph/schema.d.ts
|
|
253
|
-
declare const typeDefs:
|
|
257
|
+
declare const typeDefs: import("graphql/language/ast.js").DocumentNode;
|
|
254
258
|
//#endregion
|
|
255
259
|
//#region src/subgraph/resolvers.d.ts
|
|
256
260
|
interface ReactorDriveResolverContext {
|
|
257
261
|
reactorClient: IReactorClient;
|
|
258
262
|
readModel: IDriveReadModel;
|
|
263
|
+
/** The authenticated caller; absent reads as anonymous, never the host. */
|
|
264
|
+
user?: {
|
|
265
|
+
address?: string;
|
|
266
|
+
appKey?: string;
|
|
267
|
+
};
|
|
268
|
+
/** The host's own read check, applied on top of the reactor's read gate. */
|
|
269
|
+
hostCanRead?: (documentId: string, address?: string) => Promise<boolean>;
|
|
259
270
|
}
|
|
260
271
|
type PagingInput = {
|
|
261
272
|
cursor?: string;
|
|
@@ -263,30 +274,30 @@ type PagingInput = {
|
|
|
263
274
|
};
|
|
264
275
|
type NodeKindFilter = "FILE" | "FOLDER" | undefined;
|
|
265
276
|
/**
|
|
266
|
-
* Builds GraphQL resolvers backed by the drive read model.
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
277
|
+
* Builds GraphQL resolvers backed by the drive read model. Dependencies are
|
|
278
|
+
* read off the GraphQL context unless bound here. Every read is decided as the
|
|
279
|
+
* caller: a drive's nodes are its content, so they are served only with the
|
|
280
|
+
* drive, and a file node only with its own document too.
|
|
270
281
|
*/
|
|
271
|
-
declare function createReactorDriveResolvers(): {
|
|
282
|
+
export declare function createReactorDriveResolvers(bound?: Omit<Partial<ReactorDriveResolverContext>, "user">): {
|
|
272
283
|
Query: {
|
|
273
284
|
reactorDrive(_root: unknown, args: {
|
|
274
285
|
id: string;
|
|
275
|
-
},
|
|
286
|
+
}, context: ReactorDriveResolverContext): Promise<{
|
|
276
287
|
id: string;
|
|
277
288
|
name: string;
|
|
278
289
|
icon: string | null;
|
|
279
|
-
sharingType: string;
|
|
280
|
-
availableOffline: boolean;
|
|
290
|
+
sharingType: string | null;
|
|
291
|
+
availableOffline: boolean | null;
|
|
281
292
|
} | null>;
|
|
282
293
|
reactorDriveNode(_root: unknown, args: {
|
|
283
294
|
driveId: string;
|
|
284
295
|
id: string;
|
|
285
|
-
},
|
|
296
|
+
}, context: ReactorDriveResolverContext): Promise<ReactorDriveNode | undefined>;
|
|
286
297
|
reactorDriveDescendants(_root: unknown, args: {
|
|
287
298
|
driveId: string;
|
|
288
299
|
root: string;
|
|
289
|
-
},
|
|
300
|
+
}, context: ReactorDriveResolverContext): Promise<ReactorDriveNode[]>;
|
|
290
301
|
};
|
|
291
302
|
ReactorDrive: {
|
|
292
303
|
rootNodes(parent: {
|
|
@@ -294,11 +305,10 @@ declare function createReactorDriveResolvers(): {
|
|
|
294
305
|
}, args: {
|
|
295
306
|
paging?: PagingInput;
|
|
296
307
|
kind?: NodeKindFilter;
|
|
297
|
-
},
|
|
308
|
+
}, context: ReactorDriveResolverContext): Promise<{
|
|
298
309
|
results: ReactorDriveNode[];
|
|
299
310
|
nextCursor: string | undefined;
|
|
300
311
|
hasMore: boolean;
|
|
301
|
-
totalCount: number | undefined;
|
|
302
312
|
}>;
|
|
303
313
|
};
|
|
304
314
|
ReactorDriveFolderNode: {
|
|
@@ -308,11 +318,10 @@ declare function createReactorDriveResolvers(): {
|
|
|
308
318
|
}, args: {
|
|
309
319
|
paging?: PagingInput;
|
|
310
320
|
kind?: NodeKindFilter;
|
|
311
|
-
},
|
|
321
|
+
}, context: ReactorDriveResolverContext): Promise<{
|
|
312
322
|
results: ReactorDriveNode[];
|
|
313
323
|
nextCursor: string | undefined;
|
|
314
324
|
hasMore: boolean;
|
|
315
|
-
totalCount: number | undefined;
|
|
316
325
|
}>;
|
|
317
326
|
};
|
|
318
327
|
ReactorDriveNode: {
|
|
@@ -347,7 +356,8 @@ interface MigrateLegacyDriveStateResult {
|
|
|
347
356
|
* Re-running the migration is safe: existing `DriveNode` rows are skipped
|
|
348
357
|
* so already-migrated nodes are left untouched.
|
|
349
358
|
*/
|
|
350
|
-
declare function migrateLegacyDriveState(args: MigrateLegacyDriveStateArgs): Promise<MigrateLegacyDriveStateResult>;
|
|
359
|
+
export declare function migrateLegacyDriveState(args: MigrateLegacyDriveStateArgs): Promise<MigrateLegacyDriveStateResult>;
|
|
351
360
|
//#endregion
|
|
352
|
-
export { type AddFolderActionInput,
|
|
361
|
+
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 };
|
|
362
|
+
export {};
|
|
353
363
|
//# 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, addRelationshipAction, createDocumentAction, 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,18 +410,26 @@ 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 {
|
|
426
434
|
driveDb;
|
|
427
435
|
baseDb;
|
|
@@ -430,7 +438,8 @@ var NodeProcessor = class extends BaseReadModel {
|
|
|
430
438
|
const scopedDb = baseDb.withSchema(schema);
|
|
431
439
|
super(scopedDb, operationIndex, writeCache, consistencyTracker, {
|
|
432
440
|
readModelId: "reactor-drive-node-processor",
|
|
433
|
-
rebuildStateOnInit: false
|
|
441
|
+
rebuildStateOnInit: false,
|
|
442
|
+
indexing: unchunkedReadModelIndexingConfig
|
|
434
443
|
});
|
|
435
444
|
this.driveDb = scopedDb;
|
|
436
445
|
this.baseDb = baseDb;
|
|
@@ -640,6 +649,7 @@ function decodeKeysetCursor(cursor) {
|
|
|
640
649
|
};
|
|
641
650
|
}
|
|
642
651
|
var DriveNodeView = class {
|
|
652
|
+
db;
|
|
643
653
|
constructor(db) {
|
|
644
654
|
this.db = db;
|
|
645
655
|
}
|
|
@@ -737,10 +747,10 @@ var ReactorDriveClient = class {
|
|
|
737
747
|
this.readModel = args.readModel;
|
|
738
748
|
}
|
|
739
749
|
async create(input, signal) {
|
|
740
|
-
const driveDoc = reactorDriveCreateDocument({ global: {
|
|
750
|
+
const driveDoc = withSignaturePolicy(reactorDriveCreateDocument({ global: {
|
|
741
751
|
name: input.global.name,
|
|
742
752
|
icon: input.global.icon ?? null
|
|
743
|
-
} });
|
|
753
|
+
} }), requestedSignaturePolicy(input, await this.reactor.getCreateSignaturePolicy()), { protocolVersions: input.protocolVersions });
|
|
744
754
|
if (input.local) {
|
|
745
755
|
if (typeof input.local.sharingType === "string") driveDoc.state.local.sharingType = input.local.sharingType;
|
|
746
756
|
if (typeof input.local.availableOffline === "boolean") driveDoc.state.local.availableOffline = input.local.availableOffline;
|
|
@@ -873,11 +883,12 @@ var ReactorDriveClient = class {
|
|
|
873
883
|
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
884
|
const idMap = /* @__PURE__ */ new Map();
|
|
875
885
|
for (const node of subtree) idMap.set(node.id, generateId());
|
|
886
|
+
const policy = await this.reactor.getCreateSignaturePolicy();
|
|
876
887
|
const jobs = [];
|
|
877
888
|
const driveActions = [];
|
|
878
889
|
const fileCreateKeys = [];
|
|
879
890
|
for (const node of subtree) {
|
|
880
|
-
|
|
891
|
+
let newId = idMap.get(node.id);
|
|
881
892
|
let newParent;
|
|
882
893
|
if (node.id === srcNodeId) newParent = targetParentFolderId ?? null;
|
|
883
894
|
else {
|
|
@@ -902,7 +913,9 @@ var ReactorDriveClient = class {
|
|
|
902
913
|
const reducers = {};
|
|
903
914
|
for (const m of modulesForType) reducers[m.version ?? 1] = m.reducer;
|
|
904
915
|
const config = { reducers };
|
|
905
|
-
const
|
|
916
|
+
const replayHeader = createCopyHeader(srcDoc.header, newId, policy);
|
|
917
|
+
newId = replayHeader.id;
|
|
918
|
+
const duplicated = replayDocumentVersioned(srcDoc.initialState, srcDoc.operations, config, replayHeader);
|
|
906
919
|
assertAuthPreservedOnDuplicate(node.id, srcDoc.state.auth, duplicated.state.auth);
|
|
907
920
|
duplicated.header.name = node.name;
|
|
908
921
|
const createInput = {
|
|
@@ -970,8 +983,7 @@ var ReactorDriveClient = class {
|
|
|
970
983
|
return {
|
|
971
984
|
results: page.results.map((node) => this.toLegacyNode(node)),
|
|
972
985
|
options: page.options,
|
|
973
|
-
...page.nextCursor !== void 0 ? { nextCursor: page.nextCursor } : {}
|
|
974
|
-
...page.totalCount !== void 0 ? { totalCount: page.totalCount } : {}
|
|
986
|
+
...page.nextCursor !== void 0 ? { nextCursor: page.nextCursor } : {}
|
|
975
987
|
};
|
|
976
988
|
}
|
|
977
989
|
toLegacyNode(node) {
|
|
@@ -1013,7 +1025,8 @@ var ReactorDriveClient = class {
|
|
|
1013
1025
|
if (cached !== void 0) return cached;
|
|
1014
1026
|
const folder = folders.find((f) => f.id === id);
|
|
1015
1027
|
if (!folder) return 0;
|
|
1016
|
-
const
|
|
1028
|
+
const parent = folder.parentFolder ?? rootId;
|
|
1029
|
+
const depth = compute(parent) + 1;
|
|
1017
1030
|
depthById.set(id, depth);
|
|
1018
1031
|
return depth;
|
|
1019
1032
|
};
|
|
@@ -1058,15 +1071,16 @@ const typeDefs = gql`
|
|
|
1058
1071
|
results: [ReactorDriveNode!]!
|
|
1059
1072
|
nextCursor: String
|
|
1060
1073
|
hasMore: Boolean!
|
|
1061
|
-
totalCount: Int
|
|
1062
1074
|
}
|
|
1063
1075
|
|
|
1064
1076
|
type ReactorDrive {
|
|
1065
1077
|
id: ID!
|
|
1066
1078
|
name: String!
|
|
1067
1079
|
icon: String
|
|
1068
|
-
|
|
1069
|
-
|
|
1080
|
+
"Null when the caller may not read the drive's local scope."
|
|
1081
|
+
sharingType: String
|
|
1082
|
+
"Null when the caller may not read the drive's local scope."
|
|
1083
|
+
availableOffline: Boolean
|
|
1070
1084
|
rootNodes(
|
|
1071
1085
|
paging: ReactorDrivePagingInput
|
|
1072
1086
|
kind: ReactorDriveNodeKind
|
|
@@ -1101,42 +1115,87 @@ function shapePage(page) {
|
|
|
1101
1115
|
return {
|
|
1102
1116
|
results: page.results,
|
|
1103
1117
|
nextCursor: page.nextCursor,
|
|
1104
|
-
hasMore: page.nextCursor !== void 0
|
|
1105
|
-
|
|
1118
|
+
hasMore: page.nextCursor !== void 0
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
function subjectOf(ctx) {
|
|
1122
|
+
return {
|
|
1123
|
+
address: ctx.user?.address,
|
|
1124
|
+
key: ctx.user?.appKey
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
/** Whether a listing would serve the document to the caller; fails closed. */
|
|
1128
|
+
async function serves(ctx, documentId) {
|
|
1129
|
+
try {
|
|
1130
|
+
if (ctx.hostCanRead && !await ctx.hostCanRead(documentId, ctx.user?.address)) return false;
|
|
1131
|
+
return await ctx.reactorClient.isServed(documentId, { subject: subjectOf(ctx) });
|
|
1132
|
+
} catch {
|
|
1133
|
+
return false;
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
/** A file node names its document, so it is served only with that document. */
|
|
1137
|
+
async function servesNode(ctx, node) {
|
|
1138
|
+
return node.kind === "folder" || serves(ctx, node.id);
|
|
1139
|
+
}
|
|
1140
|
+
async function servedNodes(ctx, nodes) {
|
|
1141
|
+
const served = await Promise.all(nodes.map((node) => servesNode(ctx, node)));
|
|
1142
|
+
return nodes.filter((_, i) => served[i]);
|
|
1143
|
+
}
|
|
1144
|
+
async function servedPage(ctx, page) {
|
|
1145
|
+
return {
|
|
1146
|
+
...page,
|
|
1147
|
+
results: await servedNodes(ctx, page.results)
|
|
1106
1148
|
};
|
|
1107
1149
|
}
|
|
1108
1150
|
/**
|
|
1109
|
-
* Builds GraphQL resolvers backed by the drive read model.
|
|
1110
|
-
*
|
|
1111
|
-
*
|
|
1112
|
-
*
|
|
1151
|
+
* Builds GraphQL resolvers backed by the drive read model. Dependencies are
|
|
1152
|
+
* read off the GraphQL context unless bound here. Every read is decided as the
|
|
1153
|
+
* caller: a drive's nodes are its content, so they are served only with the
|
|
1154
|
+
* drive, and a file node only with its own document too.
|
|
1113
1155
|
*/
|
|
1114
|
-
function createReactorDriveResolvers() {
|
|
1156
|
+
function createReactorDriveResolvers(bound = {}) {
|
|
1157
|
+
const withBound = (ctx) => ({
|
|
1158
|
+
...ctx,
|
|
1159
|
+
...bound,
|
|
1160
|
+
user: ctx.user
|
|
1161
|
+
});
|
|
1115
1162
|
return {
|
|
1116
1163
|
Query: {
|
|
1117
|
-
async reactorDrive(_root, args,
|
|
1118
|
-
const
|
|
1164
|
+
async reactorDrive(_root, args, context) {
|
|
1165
|
+
const ctx = withBound(context);
|
|
1166
|
+
const document = await ctx.reactorClient.get(args.id, { subject: subjectOf(ctx) });
|
|
1119
1167
|
if (document.header.documentType !== "powerhouse/reactor-drive") return null;
|
|
1168
|
+
if (!await serves(ctx, document.header.id)) return null;
|
|
1169
|
+
const { global, local } = document.state;
|
|
1170
|
+
if (!global) return null;
|
|
1120
1171
|
return {
|
|
1121
1172
|
id: document.header.id,
|
|
1122
|
-
name:
|
|
1123
|
-
icon:
|
|
1124
|
-
sharingType:
|
|
1125
|
-
availableOffline:
|
|
1173
|
+
name: global.name,
|
|
1174
|
+
icon: global.icon,
|
|
1175
|
+
sharingType: local?.sharingType ?? null,
|
|
1176
|
+
availableOffline: local?.availableOffline ?? null
|
|
1126
1177
|
};
|
|
1127
1178
|
},
|
|
1128
|
-
async reactorDriveNode(_root, args,
|
|
1129
|
-
|
|
1179
|
+
async reactorDriveNode(_root, args, context) {
|
|
1180
|
+
const ctx = withBound(context);
|
|
1181
|
+
if (!await serves(ctx, args.driveId)) return;
|
|
1182
|
+
const node = await ctx.readModel.getNode(args.driveId, args.id);
|
|
1183
|
+
if (!node || !await servesNode(ctx, node)) return;
|
|
1184
|
+
return node;
|
|
1130
1185
|
},
|
|
1131
|
-
async reactorDriveDescendants(_root, args,
|
|
1132
|
-
|
|
1186
|
+
async reactorDriveDescendants(_root, args, context) {
|
|
1187
|
+
const ctx = withBound(context);
|
|
1188
|
+
if (!await serves(ctx, args.driveId)) return [];
|
|
1189
|
+
return servedNodes(ctx, await ctx.readModel.getDescendants(args.driveId, args.root));
|
|
1133
1190
|
}
|
|
1134
1191
|
},
|
|
1135
|
-
ReactorDrive: { async rootNodes(parent, args,
|
|
1136
|
-
|
|
1192
|
+
ReactorDrive: { async rootNodes(parent, args, context) {
|
|
1193
|
+
const ctx = withBound(context);
|
|
1194
|
+
return shapePage(await servedPage(ctx, filterByKind(await ctx.readModel.listChildren(parent.id, null, toPaging(args.paging)), args.kind)));
|
|
1137
1195
|
} },
|
|
1138
|
-
ReactorDriveFolderNode: { async children(parent, args,
|
|
1139
|
-
|
|
1196
|
+
ReactorDriveFolderNode: { async children(parent, args, context) {
|
|
1197
|
+
const ctx = withBound(context);
|
|
1198
|
+
return shapePage(await servedPage(ctx, filterByKind(await ctx.readModel.listChildren(parent.driveId, parent.id, toPaging(args.paging)), args.kind)));
|
|
1140
1199
|
} },
|
|
1141
1200
|
ReactorDriveNode: { __resolveType(node) {
|
|
1142
1201
|
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 unchunkedReadModelIndexingConfig,\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\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 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 indexing: unchunkedReadModelIndexingConfig,\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 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-
|
|
3
|
+
"version": "6.2.3-staging.0",
|
|
4
4
|
"license": "AGPL-3.0-only",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
},
|
|
24
24
|
"sideEffects": false,
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@powerhousedao/reactor": "6.2.3-
|
|
27
|
-
"@powerhousedao/shared": "6.2.3-
|
|
28
|
-
"document-model": "6.2.3-
|
|
26
|
+
"@powerhousedao/reactor": "6.2.3-staging.0",
|
|
27
|
+
"@powerhousedao/shared": "6.2.3-staging.0",
|
|
28
|
+
"document-model": "6.2.3-staging.0",
|
|
29
29
|
"graphql": "^16",
|
|
30
30
|
"graphql-tag": "^2",
|
|
31
31
|
"kysely": "0.28.16"
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@electric-sql/pglite": "0.3.15",
|
|
35
35
|
"kysely-pglite-dialect": "1.2.0",
|
|
36
|
-
"tsdown": "0.
|
|
36
|
+
"tsdown": "0.23.0",
|
|
37
37
|
"vitest": "4.1.1"
|
|
38
38
|
},
|
|
39
39
|
"description": "Drive management layer for the Powerhouse reactor — handles document drive operations, sync, and storage backends.",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"homepage": "https://academy.vetra.io/academy/Reference/Reactor/WorkingWithTheReactor",
|
|
51
51
|
"scripts": {
|
|
52
52
|
"tsc": "tsc",
|
|
53
|
-
"lint": "
|
|
53
|
+
"lint": "cd ../.. && oxlint packages/reactor-drive",
|
|
54
54
|
"build": "tsdown",
|
|
55
55
|
"test": "vitest --run",
|
|
56
56
|
"bench": "vitest bench --run"
|