@powerhousedao/reactor-drive 6.2.3-dev.5 → 6.2.3-dev.51

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