@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 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: _powerhousedao_shared_document_model0.Reducer<ReactorDrivePHState>;
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
- declare class NodeProcessor extends BaseReadModel {
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: graphql_language_ast_js0.DocumentNode;
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. The resolvers are
267
- * pure — every external dependency is read off the GraphQL context. Wiring
268
- * the resolvers into a subgraph (e.g. `reactor-api`'s `ISubgraph`) is the
269
- * caller's responsibility.
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
- }, ctx: ReactorDriveResolverContext): Promise<{
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
- }, ctx: ReactorDriveResolverContext): Promise<ReactorDriveNode | undefined>;
296
+ }, context: ReactorDriveResolverContext): Promise<ReactorDriveNode | undefined>;
286
297
  reactorDriveDescendants(_root: unknown, args: {
287
298
  driveId: string;
288
299
  root: string;
289
- }, ctx: ReactorDriveResolverContext): Promise<ReactorDriveNode[]>;
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
- }, ctx: ReactorDriveResolverContext): Promise<{
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
- }, ctx: ReactorDriveResolverContext): Promise<{
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, DRIVE_CHILD_RELATIONSHIP_TYPE, type DocumentNameTable, type DriveChildFileMetadata, type DriveNodeTable, DriveNodeView, type IDriveReadModel, type MigrateLegacyDriveStateArgs, type MigrateLegacyDriveStateResult, NodeProcessor, REACTOR_DRIVE_DOCUMENT_TYPE, REACTOR_DRIVE_FILE_EXTENSION, ReactorDriveClient, 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, addFolderAction, createReactorDriveResolvers, getReactorDriveMigrationStatus, migrateLegacyDriveState, reactorDriveActions, reactorDriveCreateDocument, reactorDriveCreateState, reactorDriveDocumentModelModule, reactorDriveDocumentReducer, reactorDriveStateReducer, typeDefs as reactorDriveSubgraphTypeDefs, removeFolderAction, resolveCollision, runReactorDriveMigrations, setAvailableOfflineAction, setDriveIconAction, setDriveNameAction, setSharingTypeAction, updateFolderAction };
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
@@ -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":"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"}
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, createPresignedHeader, createReducer, createState, defaultBaseState, generateId, isDocumentAction, replayDocumentVersioned } from "@powerhousedao/shared/document-model";
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
- const newId = idMap.get(node.id);
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 duplicated = replayDocumentVersioned(srcDoc.initialState, srcDoc.operations, config, createPresignedHeader(newId, documentType));
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 depth = compute(folder.parentFolder ?? rootId) + 1;
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
- sharingType: String!
1069
- availableOffline: Boolean!
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
- totalCount: page.totalCount
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. The resolvers are
1110
- * pure — every external dependency is read off the GraphQL context. Wiring
1111
- * the resolvers into a subgraph (e.g. `reactor-api`'s `ISubgraph`) is the
1112
- * caller's responsibility.
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, ctx) {
1118
- const document = await ctx.reactorClient.get(args.id);
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: document.state.global.name,
1123
- icon: document.state.global.icon,
1124
- sharingType: document.state.local.sharingType,
1125
- availableOffline: document.state.local.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, ctx) {
1129
- return ctx.readModel.getNode(args.driveId, args.id);
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, ctx) {
1132
- return ctx.readModel.getDescendants(args.driveId, args.root);
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, ctx) {
1136
- return shapePage(filterByKind(await ctx.readModel.listChildren(parent.id, null, toPaging(args.paging)), args.kind));
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, ctx) {
1139
- return shapePage(filterByKind(await ctx.readModel.listChildren(parent.driveId, parent.id, toPaging(args.paging)), args.kind));
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-dev.8",
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-dev.8",
27
- "@powerhousedao/shared": "6.2.3-dev.8",
28
- "document-model": "6.2.3-dev.8",
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.21.1",
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": "eslint",
53
+ "lint": "cd ../.. && oxlint packages/reactor-drive",
54
54
  "build": "tsdown",
55
55
  "test": "vitest --run",
56
56
  "bench": "vitest bench --run"