@powerhousedao/reactor 6.0.0-dev.209 → 6.0.0-dev.210

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.js CHANGED
@@ -1,5 +1,5 @@
1
- import { actions, actions as documentActions, createPresignedHeader, defaultBaseState, deriveOperationId, generateId, hashDocumentStateForScope, isUndoRedo } from "@powerhousedao/shared/document-model";
2
- import { addFile, deleteNode } from "@powerhousedao/shared/document-drive";
1
+ import { actions, actions as documentActions, createPresignedHeader, defaultBaseState, deriveOperationId, generateId, hashDocumentStateForScope, isUndoRedo, replayDocument } from "@powerhousedao/shared/document-model";
2
+ import { addFile, addFolder, copyNode, deleteNode, driveCreateDocument, generateNodesCopy, getDescendants, handleTargetNameCollisions, isFileNode, isFolderNode, moveNode, updateNode } from "@powerhousedao/shared/document-drive";
3
3
  import { v4 } from "uuid";
4
4
  import { ConsoleLogger } from "document-model";
5
5
  import { Migrator, sql } from "kysely";
@@ -257,33 +257,6 @@ function buildSingleJobMeta(jobId, callerMeta) {
257
257
  };
258
258
  }
259
259
  //#endregion
260
- //#region src/events/types.ts
261
- /**
262
- * Custom error class that aggregates multiple errors from event subscribers.
263
- */
264
- var EventBusAggregateError = class extends Error {
265
- errors;
266
- constructor(errors) {
267
- const message = `EventBus emit failed with ${errors.length} error(s): ${errors.map((e) => {
268
- if (e && typeof e === "object" && "message" in e) return e.message;
269
- return String(e);
270
- }).join("; ")}`;
271
- super(message);
272
- this.name = "EventBusAggregateError";
273
- this.errors = errors;
274
- }
275
- };
276
- /**
277
- * Event types for reactor lifecycle events.
278
- */
279
- const ReactorEventTypes = {
280
- JOB_PENDING: 10001,
281
- JOB_RUNNING: 10002,
282
- JOB_WRITE_READY: 10003,
283
- JOB_READ_READY: 10004,
284
- JOB_FAILED: 10005
285
- };
286
- //#endregion
287
260
  //#region src/shared/types.ts
288
261
  /**
289
262
  * Enum that determines deletion propagation.
@@ -318,6 +291,230 @@ let JobStatus = /* @__PURE__ */ function(JobStatus) {
318
291
  return JobStatus;
319
292
  }({});
320
293
  //#endregion
294
+ //#region src/client/drive-client.ts
295
+ /**
296
+ * Implementation of {@link IDriveClient}.
297
+ *
298
+ * Holds a back-reference to its parent {@link IReactorClient} for read and
299
+ * single-document write primitives, plus direct access to {@link IReactor}
300
+ * for batch execution. The back-reference is captured but never invoked
301
+ * during construction, so the partial-`this` hazard does not apply.
302
+ */
303
+ var DriveClient = class {
304
+ constructor(client, logger, reactor, signer) {
305
+ this.client = client;
306
+ this.logger = logger;
307
+ this.reactor = reactor;
308
+ this.signer = signer;
309
+ }
310
+ async create(input, signal) {
311
+ this.logger.verbose("drives.create(@input)", input);
312
+ const driveDoc = driveCreateDocument({ global: {
313
+ name: input.global.name || "",
314
+ icon: input.global.icon ?? null,
315
+ nodes: []
316
+ } });
317
+ if (input.preferredEditor) driveDoc.header.meta = {
318
+ ...driveDoc.header.meta,
319
+ preferredEditor: input.preferredEditor
320
+ };
321
+ return this.client.create(driveDoc, void 0, signal);
322
+ }
323
+ async addFile(driveIdentifier, document, parentFolder, signal) {
324
+ this.logger.verbose("drives.addFile(@driveIdentifier, @document, @parentFolder)", driveIdentifier, document.header.id, parentFolder);
325
+ const documentId = document.header.id;
326
+ const documentActions = await signActions([
327
+ createDocumentAction({
328
+ model: document.header.documentType,
329
+ version: 0,
330
+ documentId: document.header.id,
331
+ signing: {
332
+ signature: document.header.id,
333
+ publicKey: document.header.sig.publicKey,
334
+ nonce: document.header.sig.nonce,
335
+ createdAtUtcIso: document.header.createdAtUtcIso,
336
+ documentType: document.header.documentType
337
+ },
338
+ slug: document.header.slug,
339
+ name: document.header.name,
340
+ branch: document.header.branch,
341
+ meta: document.header.meta,
342
+ protocolVersions: document.header.protocolVersions ?? { "base-reducer": 2 }
343
+ }),
344
+ upgradeDocumentAction({
345
+ documentId: document.header.id,
346
+ model: document.header.documentType,
347
+ fromVersion: 0,
348
+ toVersion: 1,
349
+ initialState: document.state
350
+ }),
351
+ addRelationshipAction(driveIdentifier, documentId, "child")
352
+ ], this.signer, signal);
353
+ const driveActions = await signActions([addFile({
354
+ id: documentId,
355
+ name: document.header.name || documentId,
356
+ documentType: document.header.documentType,
357
+ parentFolder
358
+ })], this.signer, signal);
359
+ const batchResult = await this.reactor.executeBatch({ jobs: [{
360
+ key: "document",
361
+ documentId,
362
+ scope: getSharedActionScope(documentActions),
363
+ branch: "main",
364
+ actions: documentActions,
365
+ dependsOn: []
366
+ }, {
367
+ key: "drive",
368
+ documentId: driveIdentifier,
369
+ scope: getSharedActionScope(driveActions),
370
+ branch: "main",
371
+ actions: driveActions,
372
+ dependsOn: ["document"]
373
+ }] }, signal);
374
+ const completedJobs = await Promise.all(Object.values(batchResult.jobs).map((job) => this.client.waitForJob(job, signal)));
375
+ for (const job of completedJobs) if (job.status === JobStatus.FAILED) throw new Error(job.error?.message);
376
+ return this.reactor.get(documentId);
377
+ }
378
+ async addFolder(driveIdentifier, name, parentFolder, signal) {
379
+ this.logger.verbose("drives.addFolder(@driveIdentifier, @name, @parentFolder)", driveIdentifier, name, parentFolder);
380
+ const folderId = generateId();
381
+ const node = (await this.client.execute(driveIdentifier, "main", [addFolder({
382
+ id: folderId,
383
+ name,
384
+ parentFolder
385
+ })], signal)).state.global.nodes.find((n) => n.id === folderId);
386
+ if (!node || !isFolderNode(node)) throw new Error("Folder creation failed");
387
+ return node;
388
+ }
389
+ async removeNode(driveIdentifier, nodeId, signal) {
390
+ this.logger.verbose("drives.removeNode(@driveIdentifier, @nodeId)", driveIdentifier, nodeId);
391
+ const drive = await this.client.get(driveIdentifier, void 0, signal);
392
+ const node = drive.state.global.nodes.find((n) => n.id === nodeId);
393
+ if (!node) throw new Error(`Node ${nodeId} not found in drive ${driveIdentifier}`);
394
+ if (isFolderNode(node)) {
395
+ const fileDescendants = getDescendants(node, drive.state.global.nodes).filter(isFileNode);
396
+ for (const file of fileDescendants) await this.removeFileNode(driveIdentifier, file.id, signal);
397
+ await this.client.execute(driveIdentifier, "main", [deleteNode({ id: nodeId })], signal);
398
+ return;
399
+ }
400
+ await this.removeFileNode(driveIdentifier, nodeId, signal);
401
+ }
402
+ async renameNode(driveIdentifier, nodeId, name, signal) {
403
+ this.logger.verbose("drives.renameNode(@driveIdentifier, @nodeId, @name)", driveIdentifier, nodeId, name);
404
+ if ((await this.client.execute(nodeId, "main", [actions.setName({ name })], signal)).header.name !== name) throw new Error("Document rename did not apply");
405
+ const node = (await this.client.execute(driveIdentifier, "main", [updateNode({
406
+ id: nodeId,
407
+ name
408
+ })], signal)).state.global.nodes.find((n) => n.id === nodeId);
409
+ if (!node) throw new Error("Node missing from drive after rename");
410
+ return node;
411
+ }
412
+ async moveNode(driveIdentifier, srcNodeId, targetParentFolderId, signal) {
413
+ this.logger.verbose("drives.moveNode(@driveIdentifier, @srcNodeId, @targetParentFolderId)", driveIdentifier, srcNodeId, targetParentFolderId);
414
+ return this.client.execute(driveIdentifier, "main", [moveNode({
415
+ srcFolder: srcNodeId,
416
+ targetParentFolder: targetParentFolderId
417
+ })], signal);
418
+ }
419
+ async copyNode(driveIdentifier, srcNodeId, targetParentFolderId, signal) {
420
+ this.logger.verbose("drives.copyNode(@driveIdentifier, @srcNodeId, @targetParentFolderId)", driveIdentifier, srcNodeId, targetParentFolderId);
421
+ const drive = await this.client.get(driveIdentifier, void 0, signal);
422
+ const srcNode = drive.state.global.nodes.find((n) => n.id === srcNodeId);
423
+ if (!srcNode) throw new Error(`Node ${srcNodeId} not found in drive ${driveIdentifier}`);
424
+ const copyPlan = generateNodesCopy({
425
+ srcId: srcNodeId,
426
+ targetParentFolder: targetParentFolderId,
427
+ targetName: srcNode.name
428
+ }, () => generateId(), drive.state.global.nodes);
429
+ const resolvedNamesByTargetId = /* @__PURE__ */ new Map();
430
+ for (const entry of copyPlan) {
431
+ const node = drive.state.global.nodes.find((n) => n.id === entry.srcId);
432
+ if (!node) continue;
433
+ const resolved = handleTargetNameCollisions({
434
+ nodes: drive.state.global.nodes,
435
+ srcName: entry.targetName || node.name,
436
+ srcKind: isFileNode(node) ? "file" : "folder",
437
+ targetParentFolder: entry.targetParentFolder ?? null
438
+ });
439
+ resolvedNamesByTargetId.set(entry.targetId, resolved);
440
+ }
441
+ for (const entry of copyPlan) {
442
+ const node = drive.state.global.nodes.find((n) => n.id === entry.srcId);
443
+ if (!node || !isFileNode(node)) continue;
444
+ const srcDoc = await this.client.get(entry.srcId, void 0, signal);
445
+ const module = await this.client.getDocumentModelModule(srcDoc.header.documentType);
446
+ const duplicated = replayDocument(srcDoc.initialState, srcDoc.operations, module.reducer, createPresignedHeader(entry.targetId, srcDoc.header.documentType));
447
+ const resolvedName = resolvedNamesByTargetId.get(entry.targetId);
448
+ if (resolvedName) duplicated.header.name = resolvedName;
449
+ await this.addFile(driveIdentifier, duplicated, entry.targetParentFolder ?? void 0, signal);
450
+ }
451
+ return this.client.execute(driveIdentifier, "main", copyPlan.map((entry) => copyNode(entry)), signal);
452
+ }
453
+ async getNode(driveIdentifier, nodeId, signal) {
454
+ this.logger.verbose("drives.getNode(@driveIdentifier, @nodeId)", driveIdentifier, nodeId);
455
+ const node = (await this.client.get(driveIdentifier, void 0, signal)).state.global.nodes.find((n) => n.id === nodeId);
456
+ if (!node) throw new Error(`Node ${nodeId} not found in drive ${driveIdentifier}`);
457
+ return node;
458
+ }
459
+ async listNodes(driveIdentifier, parentFolder, signal) {
460
+ this.logger.verbose("drives.listNodes(@driveIdentifier, @parentFolder)", driveIdentifier, parentFolder);
461
+ const nodes = (await this.client.get(driveIdentifier, void 0, signal)).state.global.nodes;
462
+ if (parentFolder === void 0) return [...nodes];
463
+ return nodes.filter((n) => (n.parentFolder ?? null) === parentFolder);
464
+ }
465
+ async removeFileNode(driveId, fileId, signal) {
466
+ const relationshipActions = await signActions([removeRelationshipAction(driveId, fileId, "child")], this.signer, signal);
467
+ const driveActions = await signActions([deleteNode({ id: fileId })], this.signer, signal);
468
+ const batchResult = await this.reactor.executeBatch({ jobs: [{
469
+ key: "relationship",
470
+ documentId: driveId,
471
+ scope: getSharedActionScope(relationshipActions),
472
+ branch: "main",
473
+ actions: relationshipActions,
474
+ dependsOn: []
475
+ }, {
476
+ key: "drive",
477
+ documentId: driveId,
478
+ scope: getSharedActionScope(driveActions),
479
+ branch: "main",
480
+ actions: driveActions,
481
+ dependsOn: ["relationship"]
482
+ }] }, signal);
483
+ const completedJobs = await Promise.all(Object.values(batchResult.jobs).map((job) => this.client.waitForJob(job, signal)));
484
+ for (const job of completedJobs) if (job.status === JobStatus.FAILED) throw new Error(job.error?.message);
485
+ const deleteJob = await this.reactor.deleteDocument(fileId, this.signer, signal);
486
+ const deleteCompleted = await this.client.waitForJob(deleteJob, signal);
487
+ if (deleteCompleted.status === JobStatus.FAILED) throw new Error(deleteCompleted.error?.message);
488
+ }
489
+ };
490
+ //#endregion
491
+ //#region src/events/types.ts
492
+ /**
493
+ * Custom error class that aggregates multiple errors from event subscribers.
494
+ */
495
+ var EventBusAggregateError = class extends Error {
496
+ errors;
497
+ constructor(errors) {
498
+ const message = `EventBus emit failed with ${errors.length} error(s): ${errors.map((e) => {
499
+ if (e && typeof e === "object" && "message" in e) return e.message;
500
+ return String(e);
501
+ }).join("; ")}`;
502
+ super(message);
503
+ this.name = "EventBusAggregateError";
504
+ this.errors = errors;
505
+ }
506
+ };
507
+ /**
508
+ * Event types for reactor lifecycle events.
509
+ */
510
+ const ReactorEventTypes = {
511
+ JOB_PENDING: 10001,
512
+ JOB_RUNNING: 10002,
513
+ JOB_WRITE_READY: 10003,
514
+ JOB_READ_READY: 10004,
515
+ JOB_FAILED: 10005
516
+ };
517
+ //#endregion
321
518
  //#region src/shared/awaiter.ts
322
519
  /**
323
520
  * Checks if a job status is terminal (job has finished).
@@ -484,6 +681,7 @@ var ReactorClient = class {
484
681
  jobAwaiter;
485
682
  documentIndexer;
486
683
  documentView;
684
+ drives;
487
685
  constructor(logger, reactor, signer, subscriptionManager, jobAwaiter, documentIndexer, documentView) {
488
686
  this.logger = logger;
489
687
  this.reactor = reactor;
@@ -492,6 +690,7 @@ var ReactorClient = class {
492
690
  this.jobAwaiter = jobAwaiter;
493
691
  this.documentIndexer = documentIndexer;
494
692
  this.documentView = documentView;
693
+ this.drives = new DriveClient(this, logger, reactor, signer);
495
694
  this.logger.verbose("ReactorClient initialized");
496
695
  }
497
696
  /**
@@ -579,36 +778,36 @@ var ReactorClient = class {
579
778
  };
580
779
  }
581
780
  /**
582
- * Retrieves children of a document
781
+ * Retrieves outgoing relationships of a given type from a source document.
583
782
  */
584
- async getChildren(parentIdentifier, view, paging, signal) {
585
- this.logger.verbose("getChildren(@parentIdentifier, @view, @paging)", parentIdentifier, view, paging);
586
- const parentId = await this.documentView.resolveIdOrSlug(parentIdentifier, view, void 0, signal);
587
- const childIds = (await this.documentIndexer.getOutgoing(parentId, void 0, void 0, void 0, signal)).results.map((rel) => rel.targetId);
588
- if (childIds.length === 0) return {
783
+ async getOutgoingRelationships(sourceIdentifier, relationshipType, view, paging, signal) {
784
+ this.logger.verbose("getOutgoingRelationships(@sourceIdentifier, @relationshipType, @view, @paging)", sourceIdentifier, relationshipType, view, paging);
785
+ const sourceId = await this.documentView.resolveIdOrSlug(sourceIdentifier, view, void 0, signal);
786
+ const targetIds = (await this.documentIndexer.getOutgoing(sourceId, [relationshipType], void 0, void 0, signal)).results.map((rel) => rel.targetId);
787
+ if (targetIds.length === 0) return {
589
788
  results: [],
590
789
  options: paging || {
591
790
  cursor: "0",
592
791
  limit: 0
593
792
  }
594
793
  };
595
- return this.reactor.find({ ids: childIds }, view, paging, void 0, signal);
794
+ return this.reactor.find({ ids: targetIds }, view, paging, void 0, signal);
596
795
  }
597
796
  /**
598
- * Retrieves parents of a document
797
+ * Retrieves incoming relationships of a given type to a target document.
599
798
  */
600
- async getParents(childIdentifier, view, paging, signal) {
601
- this.logger.verbose("getParents(@childIdentifier, @view, @paging)", childIdentifier, view, paging);
602
- const childId = await this.documentView.resolveIdOrSlug(childIdentifier, view, void 0, signal);
603
- const parentIds = (await this.documentIndexer.getIncoming(childId, void 0, void 0, void 0, signal)).results.map((rel) => rel.sourceId);
604
- if (parentIds.length === 0) return {
799
+ async getIncomingRelationships(targetIdentifier, relationshipType, view, paging, signal) {
800
+ this.logger.verbose("getIncomingRelationships(@targetIdentifier, @relationshipType, @view, @paging)", targetIdentifier, relationshipType, view, paging);
801
+ const targetId = await this.documentView.resolveIdOrSlug(targetIdentifier, view, void 0, signal);
802
+ const sourceIds = (await this.documentIndexer.getIncoming(targetId, [relationshipType], void 0, void 0, signal)).results.map((rel) => rel.sourceId);
803
+ if (sourceIds.length === 0) return {
605
804
  results: [],
606
805
  options: paging || {
607
806
  cursor: "0",
608
807
  limit: 0
609
808
  }
610
809
  };
611
- return this.reactor.find({ ids: parentIds }, view, paging, void 0, signal);
810
+ return this.reactor.find({ ids: sourceIds }, view, paging, void 0, signal);
612
811
  }
613
812
  /**
614
813
  * Filters documents by criteria and returns a list of them
@@ -693,61 +892,13 @@ var ReactorClient = class {
693
892
  }
694
893
  /**
695
894
  * Creates an empty document in a drive as a single batched operation.
895
+ * Delegates to {@link IDriveClient.addFile}.
896
+ *
897
+ * @deprecated Use `client.drives.addFile` instead. This method will be
898
+ * removed in a future release.
696
899
  */
697
900
  async createDocumentInDrive(driveId, document, parentFolder, signal) {
698
- this.logger.verbose("createDocumentInDrive(@driveId, @document, @parentFolder)", driveId, document, parentFolder);
699
- const documentId = document.header.id;
700
- const documentActions = await signActions([
701
- createDocumentAction({
702
- model: document.header.documentType,
703
- version: 0,
704
- documentId: document.header.id,
705
- signing: {
706
- signature: document.header.id,
707
- publicKey: document.header.sig.publicKey,
708
- nonce: document.header.sig.nonce,
709
- createdAtUtcIso: document.header.createdAtUtcIso,
710
- documentType: document.header.documentType
711
- },
712
- slug: document.header.slug,
713
- name: document.header.name,
714
- branch: document.header.branch,
715
- meta: document.header.meta,
716
- protocolVersions: document.header.protocolVersions ?? { "base-reducer": 2 }
717
- }),
718
- upgradeDocumentAction({
719
- documentId: document.header.id,
720
- model: document.header.documentType,
721
- fromVersion: 0,
722
- toVersion: 1,
723
- initialState: document.state
724
- }),
725
- addRelationshipAction(driveId, documentId, "child")
726
- ], this.signer, signal);
727
- const driveActions = await signActions([addFile({
728
- id: documentId,
729
- name: document.header.name || documentId,
730
- documentType: document.header.documentType,
731
- parentFolder
732
- })], this.signer, signal);
733
- const batchResult = await this.reactor.executeBatch({ jobs: [{
734
- key: "document",
735
- documentId,
736
- scope: getSharedActionScope(documentActions),
737
- branch: "main",
738
- actions: documentActions,
739
- dependsOn: []
740
- }, {
741
- key: "drive",
742
- documentId: driveId,
743
- scope: getSharedActionScope(driveActions),
744
- branch: "main",
745
- actions: driveActions,
746
- dependsOn: ["document"]
747
- }] }, signal);
748
- const completedJobs = await Promise.all(Object.values(batchResult.jobs).map((job) => this.waitForJob(job, signal)));
749
- for (const job of completedJobs) if (job.status === JobStatus.FAILED) throw new Error(job.error?.message);
750
- return this.reactor.get(documentId);
901
+ return this.drives.addFile(driveId, document, parentFolder, signal);
751
902
  }
752
903
  /**
753
904
  * Applies a list of actions to a document and waits for completion
@@ -779,32 +930,32 @@ var ReactorClient = class {
779
930
  /**
780
931
  * Adds multiple documents as children to another and waits for completion
781
932
  */
782
- async addChildren(parentIdentifier, documentIdentifiers, branch = "main", signal) {
783
- this.logger.verbose("addChildren(@parentIdentifier, @count children, @branch)", parentIdentifier, documentIdentifiers.length, branch);
784
- const jobInfo = await this.reactor.addChildren(parentIdentifier, documentIdentifiers, branch, this.signer, signal);
933
+ async addRelationship(sourceIdentifier, targetIdentifier, relationshipType, branch = "main", signal) {
934
+ this.logger.verbose("addRelationship(@sourceIdentifier, @targetIdentifier, @relationshipType, @branch)", sourceIdentifier, targetIdentifier, relationshipType, branch);
935
+ const jobInfo = await this.reactor.addRelationship(sourceIdentifier, targetIdentifier, relationshipType, branch, this.signer, signal);
785
936
  const completedJob = await this.waitForJob(jobInfo, signal);
786
937
  if (completedJob.status === JobStatus.FAILED) throw new Error(completedJob.error?.message);
787
- return await this.reactor.getByIdOrSlug(parentIdentifier, { branch }, completedJob.consistencyToken, signal);
938
+ return await this.reactor.getByIdOrSlug(sourceIdentifier, { branch }, completedJob.consistencyToken, signal);
788
939
  }
789
940
  /**
790
- * Removes multiple documents as children from another and waits for completion
941
+ * Removes a relationship between two documents and waits for completion.
791
942
  */
792
- async removeChildren(parentIdentifier, documentIdentifiers, branch = "main", signal) {
793
- this.logger.verbose("removeChildren(@parentIdentifier, @count children, @branch)", parentIdentifier, documentIdentifiers.length, branch);
794
- const jobInfo = await this.reactor.removeChildren(parentIdentifier, documentIdentifiers, branch, this.signer, signal);
943
+ async removeRelationship(sourceIdentifier, targetIdentifier, relationshipType, branch = "main", signal) {
944
+ this.logger.verbose("removeRelationship(@sourceIdentifier, @targetIdentifier, @relationshipType, @branch)", sourceIdentifier, targetIdentifier, relationshipType, branch);
945
+ const jobInfo = await this.reactor.removeRelationship(sourceIdentifier, targetIdentifier, relationshipType, branch, this.signer, signal);
795
946
  const completedJob = await this.waitForJob(jobInfo, signal);
796
947
  if (completedJob.status === JobStatus.FAILED) throw new Error(completedJob.error?.message);
797
- return await this.reactor.getByIdOrSlug(parentIdentifier, { branch }, completedJob.consistencyToken, signal);
948
+ return await this.reactor.getByIdOrSlug(sourceIdentifier, { branch }, completedJob.consistencyToken, signal);
798
949
  }
799
950
  /**
800
- * Moves multiple documents from one parent to another and waits for completion
951
+ * Moves a relationship from one source document to another and waits for completion.
801
952
  */
802
- async moveChildren(sourceParentIdentifier, targetParentIdentifier, documentIdentifiers, branch = "main", signal) {
803
- this.logger.verbose("moveChildren(@sourceParentIdentifier, @targetParentIdentifier, @count children, @branch)", sourceParentIdentifier, targetParentIdentifier, documentIdentifiers.length, branch);
804
- const removeJobInfo = await this.reactor.removeChildren(sourceParentIdentifier, documentIdentifiers, branch, this.signer, signal);
953
+ async moveRelationship(sourceParentIdentifier, targetParentIdentifier, targetIdentifier, relationshipType, branch = "main", signal) {
954
+ this.logger.verbose("moveRelationship(@sourceParentIdentifier, @targetParentIdentifier, @targetIdentifier, @relationshipType, @branch)", sourceParentIdentifier, targetParentIdentifier, targetIdentifier, relationshipType, branch);
955
+ const removeJobInfo = await this.reactor.removeRelationship(sourceParentIdentifier, targetIdentifier, relationshipType, branch, this.signer, signal);
805
956
  const removeCompletedJob = await this.waitForJob(removeJobInfo, signal);
806
957
  if (removeCompletedJob.status === JobStatus.FAILED) throw new Error(removeCompletedJob.error?.message);
807
- const addJobInfo = await this.reactor.addChildren(targetParentIdentifier, documentIdentifiers, branch, this.signer, signal);
958
+ const addJobInfo = await this.reactor.addRelationship(targetParentIdentifier, targetIdentifier, relationshipType, branch, this.signer, signal);
808
959
  const addCompletedJob = await this.waitForJob(addJobInfo, signal);
809
960
  if (addCompletedJob.status === JobStatus.FAILED) throw new Error(addCompletedJob.error?.message);
810
961
  return {
@@ -841,13 +992,13 @@ var ReactorClient = class {
841
992
  }
842
993
  for (const descendantId of toDelete) {
843
994
  if (descendantId === identifier) continue;
844
- const removalJobs = await this.removeFromAllParents(descendantId, signal);
995
+ const removalJobs = await this.removeAllIncomingRelationships(descendantId, signal);
845
996
  jobs.push(...removalJobs);
846
997
  const jobInfo = await this.reactor.deleteDocument(descendantId, this.signer, signal);
847
998
  jobs.push(jobInfo);
848
999
  }
849
1000
  }
850
- const removalJobs = await this.removeFromAllParents(identifier, signal);
1001
+ const removalJobs = await this.removeAllIncomingRelationships(identifier, signal);
851
1002
  jobs.push(...removalJobs);
852
1003
  const jobInfo = await this.reactor.deleteDocument(identifier, this.signer, signal);
853
1004
  jobs.push(jobInfo);
@@ -923,46 +1074,15 @@ var ReactorClient = class {
923
1074
  unsubscribeRelationship();
924
1075
  };
925
1076
  }
926
- async removeFromAllParents(documentId, signal) {
1077
+ async removeAllIncomingRelationships(documentId, signal) {
927
1078
  const incoming = await this.documentIndexer.getIncoming(documentId, void 0, void 0, void 0, signal);
928
1079
  const jobs = [];
929
1080
  for (const rel of incoming.results) {
930
- const relJobs = await this.removeFromParent(documentId, rel, signal);
931
- jobs.push(...relJobs);
1081
+ const jobInfo = await this.reactor.removeRelationship(rel.sourceId, documentId, rel.relationshipType, "main", this.signer, signal);
1082
+ jobs.push(jobInfo);
932
1083
  }
933
1084
  return jobs;
934
1085
  }
935
- async removeFromParent(documentId, rel, signal) {
936
- const parentId = rel.sourceId;
937
- let parentDoc;
938
- try {
939
- parentDoc = await this.reactor.get(parentId, void 0, void 0, signal);
940
- } catch {
941
- return [];
942
- }
943
- const isDrive = parentDoc.header.documentType === "powerhouse/document-drive";
944
- const relationshipActions = await signActions([removeRelationshipAction(parentId, documentId, rel.relationshipType)], this.signer, signal);
945
- if (isDrive) {
946
- const driveActions = await signActions([deleteNode({ id: documentId })], this.signer, signal);
947
- const batchResult = await this.reactor.executeBatch({ jobs: [{
948
- key: "relationship",
949
- documentId: parentId,
950
- scope: getSharedActionScope(relationshipActions),
951
- branch: "main",
952
- actions: relationshipActions,
953
- dependsOn: []
954
- }, {
955
- key: "drive",
956
- documentId: parentId,
957
- scope: getSharedActionScope(driveActions),
958
- branch: "main",
959
- actions: driveActions,
960
- dependsOn: ["relationship"]
961
- }] }, signal);
962
- return [...Object.values(batchResult.jobs)];
963
- }
964
- return [await this.reactor.removeChildren(parentId, [documentId], "main", this.signer, signal)];
965
- }
966
1086
  };
967
1087
  //#endregion
968
1088
  //#region src/cache/collection-membership-cache.ts
@@ -8816,13 +8936,13 @@ var Reactor = class {
8816
8936
  this.logger.verbose("getByIdOrSlug(@identifier, @view)", identifier, view);
8817
8937
  return await this.documentView.getByIdOrSlug(identifier, view, consistencyToken, signal);
8818
8938
  }
8819
- async getChildren(documentId, consistencyToken, signal) {
8820
- const relationships = await this.documentIndexer.getOutgoing(documentId, ["child"], void 0, consistencyToken, signal);
8939
+ async getOutgoingRelationships(sourceId, relationshipType, consistencyToken, signal) {
8940
+ const relationships = await this.documentIndexer.getOutgoing(sourceId, [relationshipType], void 0, consistencyToken, signal);
8821
8941
  if (signal?.aborted) throw new AbortError();
8822
8942
  return relationships.results.map((rel) => rel.targetId);
8823
8943
  }
8824
- async getParents(childId, consistencyToken, signal) {
8825
- const relationships = await this.documentIndexer.getIncoming(childId, ["parent"], void 0, consistencyToken, signal);
8944
+ async getIncomingRelationships(targetId, relationshipType, consistencyToken, signal) {
8945
+ const relationships = await this.documentIndexer.getIncoming(targetId, [relationshipType], void 0, consistencyToken, signal);
8826
8946
  if (signal?.aborted) throw new AbortError();
8827
8947
  return relationships.results.map((rel) => rel.sourceId);
8828
8948
  }
@@ -9182,19 +9302,19 @@ var Reactor = class {
9182
9302
  }
9183
9303
  return { jobs: Object.fromEntries(jobInfos) };
9184
9304
  }
9185
- async addChildren(parentId, documentIds, branch = "main", signer, signal) {
9186
- this.logger.verbose("addChildren(@parentId, @count children, @branch)", parentId, documentIds.length, branch);
9305
+ async addRelationship(sourceId, targetId, relationshipType, branch = "main", signer, signal) {
9306
+ this.logger.verbose("addRelationship(@sourceId, @targetId, @relationshipType, @branch)", sourceId, targetId, relationshipType, branch);
9187
9307
  if (signal?.aborted) throw new AbortError();
9188
- let actions = documentIds.map((childId) => addRelationshipAction(parentId, childId, "child"));
9308
+ let actions = [addRelationshipAction(sourceId, targetId, relationshipType)];
9189
9309
  if (signer) actions = await signActions(actions, signer, signal);
9190
- return await this.execute(parentId, branch, actions, signal);
9310
+ return await this.execute(sourceId, branch, actions, signal);
9191
9311
  }
9192
- async removeChildren(parentId, documentIds, branch = "main", signer, signal) {
9193
- this.logger.verbose("removeChildren(@parentId, @count children, @branch)", parentId, documentIds.length, branch);
9312
+ async removeRelationship(sourceId, targetId, relationshipType, branch = "main", signer, signal) {
9313
+ this.logger.verbose("removeRelationship(@sourceId, @targetId, @relationshipType, @branch)", sourceId, targetId, relationshipType, branch);
9194
9314
  if (signal?.aborted) throw new AbortError();
9195
- let actions = documentIds.map((childId) => removeRelationshipAction(parentId, childId, "child"));
9315
+ let actions = [removeRelationshipAction(sourceId, targetId, relationshipType)];
9196
9316
  if (signer) actions = await signActions(actions, signer, signal);
9197
- return await this.execute(parentId, branch, actions, signal);
9317
+ return await this.execute(sourceId, branch, actions, signal);
9198
9318
  }
9199
9319
  getJobStatus(jobId, signal) {
9200
9320
  this.logger.verbose("getJobStatus(@jobId)", jobId);
@@ -9772,6 +9892,6 @@ var DocumentIntegrityService = class {
9772
9892
  }
9773
9893
  };
9774
9894
  //#endregion
9775
- export { BaseReadModel, ChannelError, ChannelErrorSource, ChannelScheme, ConsistencyTracker, DefaultSubscriptionErrorHandler, DocumentChangeType, DocumentIntegrityService, DocumentModelRegistry, DuplicateManifestError, DuplicateModuleError, DuplicateOperationError, EventBus, EventBusAggregateError, GqlRequestChannel, GqlRequestChannelFactory, GqlResponseChannel, GqlResponseChannelFactory, SimpleJobExecutor as InMemoryJobExecutor, SimpleJobExecutor, InMemoryJobTracker, InMemoryQueue, IntervalPollTimer, InvalidModuleError, JobAwaiter, JobExecutorEventTypes, JobStatus, KyselyDocumentIndexer, KyselyDocumentView, KyselyKeyframeStore, KyselyOperationStore, KyselySyncCursorStorage, KyselySyncRemoteStorage, KyselyWriteCache, Mailbox, ModuleNotFoundError, NullDocumentModelResolver, OptimisticLockError, PollingChannelError, ProcessorManager, PropagationMode, QueueEventTypes, REACTOR_SCHEMA, Reactor, ReactorBuilder, ReactorClient, ReactorClientBuilder, ReactorEventTypes, ReactorSubscriptionManager, ReadModelCoordinator, RelationalDbProcessor, RelationshipChangeType, RevisionMismatchError, SimpleJobExecutorManager, SyncBuilder, SyncEventTypes, SyncOperation, SyncOperationAggregateError, SyncOperationStatus, SyncStatus, SyncStatusTracker, addRelationshipAction, batchOperationsByDocument, consolidateSyncOperations, createDocumentAction, createMutableShutdownStatus, createRelationalDb, deleteDocumentAction, documentActions, driveCollectionId, driveIdFromUrl, envelopesToSyncOperations, getMigrationStatus, makeConsistencyKey, parseDriveUrl, removeRelationshipAction, runMigrations, sortEnvelopesByFirstOperationTimestamp, trimMailboxFromAckOrdinal, upgradeDocumentAction };
9895
+ export { BaseReadModel, ChannelError, ChannelErrorSource, ChannelScheme, ConsistencyTracker, DefaultSubscriptionErrorHandler, DocumentChangeType, DocumentIntegrityService, DocumentModelRegistry, DriveClient, DuplicateManifestError, DuplicateModuleError, DuplicateOperationError, EventBus, EventBusAggregateError, GqlRequestChannel, GqlRequestChannelFactory, GqlResponseChannel, GqlResponseChannelFactory, SimpleJobExecutor as InMemoryJobExecutor, SimpleJobExecutor, InMemoryJobTracker, InMemoryQueue, IntervalPollTimer, InvalidModuleError, JobAwaiter, JobExecutorEventTypes, JobStatus, KyselyDocumentIndexer, KyselyDocumentView, KyselyKeyframeStore, KyselyOperationStore, KyselySyncCursorStorage, KyselySyncRemoteStorage, KyselyWriteCache, Mailbox, ModuleNotFoundError, NullDocumentModelResolver, OptimisticLockError, PollingChannelError, ProcessorManager, PropagationMode, QueueEventTypes, REACTOR_SCHEMA, Reactor, ReactorBuilder, ReactorClient, ReactorClientBuilder, ReactorEventTypes, ReactorSubscriptionManager, ReadModelCoordinator, RelationalDbProcessor, RelationshipChangeType, RevisionMismatchError, SimpleJobExecutorManager, SyncBuilder, SyncEventTypes, SyncOperation, SyncOperationAggregateError, SyncOperationStatus, SyncStatus, SyncStatusTracker, addRelationshipAction, batchOperationsByDocument, consolidateSyncOperations, createDocumentAction, createMutableShutdownStatus, createRelationalDb, deleteDocumentAction, documentActions, driveCollectionId, driveIdFromUrl, envelopesToSyncOperations, getMigrationStatus, makeConsistencyKey, parseDriveUrl, removeRelationshipAction, runMigrations, sortEnvelopesByFirstOperationTimestamp, trimMailboxFromAckOrdinal, upgradeDocumentAction };
9776
9896
 
9777
9897
  //# sourceMappingURL=index.js.map