@powerhousedao/reactor 6.0.0-dev.13 → 6.0.0-dev.130
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 +4320 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9442 -0
- package/dist/index.js.map +1 -0
- package/package.json +21 -20
- package/dist/src/actions/index.d.ts +0 -24
- package/dist/src/actions/index.d.ts.map +0 -1
- package/dist/src/actions/index.js +0 -76
- package/dist/src/actions/index.js.map +0 -1
- package/dist/src/cache/buffer/ring-buffer.d.ts +0 -37
- package/dist/src/cache/buffer/ring-buffer.d.ts.map +0 -1
- package/dist/src/cache/buffer/ring-buffer.js +0 -69
- package/dist/src/cache/buffer/ring-buffer.js.map +0 -1
- package/dist/src/cache/document-meta-cache-types.d.ts +0 -114
- package/dist/src/cache/document-meta-cache-types.d.ts.map +0 -1
- package/dist/src/cache/document-meta-cache-types.js +0 -2
- package/dist/src/cache/document-meta-cache-types.js.map +0 -1
- package/dist/src/cache/document-meta-cache.d.ts +0 -30
- package/dist/src/cache/document-meta-cache.d.ts.map +0 -1
- package/dist/src/cache/document-meta-cache.js +0 -128
- package/dist/src/cache/document-meta-cache.js.map +0 -1
- package/dist/src/cache/index.d.ts +0 -3
- package/dist/src/cache/index.d.ts.map +0 -1
- package/dist/src/cache/index.js +0 -2
- package/dist/src/cache/index.js.map +0 -1
- package/dist/src/cache/kysely-operation-index.d.ts +0 -15
- package/dist/src/cache/kysely-operation-index.d.ts.map +0 -1
- package/dist/src/cache/kysely-operation-index.js +0 -250
- package/dist/src/cache/kysely-operation-index.js.map +0 -1
- package/dist/src/cache/kysely-write-cache.d.ts +0 -134
- package/dist/src/cache/kysely-write-cache.d.ts.map +0 -1
- package/dist/src/cache/kysely-write-cache.js +0 -388
- package/dist/src/cache/kysely-write-cache.js.map +0 -1
- package/dist/src/cache/lru/lru-tracker.d.ts +0 -15
- package/dist/src/cache/lru/lru-tracker.d.ts.map +0 -1
- package/dist/src/cache/lru/lru-tracker.js +0 -96
- package/dist/src/cache/lru/lru-tracker.js.map +0 -1
- package/dist/src/cache/operation-index-types.d.ts +0 -50
- package/dist/src/cache/operation-index-types.d.ts.map +0 -1
- package/dist/src/cache/operation-index-types.js +0 -4
- package/dist/src/cache/operation-index-types.js.map +0 -1
- package/dist/src/cache/write/interfaces.d.ts +0 -83
- package/dist/src/cache/write/interfaces.d.ts.map +0 -1
- package/dist/src/cache/write/interfaces.js +0 -2
- package/dist/src/cache/write/interfaces.js.map +0 -1
- package/dist/src/cache/write-cache-types.d.ts +0 -42
- package/dist/src/cache/write-cache-types.d.ts.map +0 -1
- package/dist/src/cache/write-cache-types.js +0 -2
- package/dist/src/cache/write-cache-types.js.map +0 -1
- package/dist/src/client/reactor-client.d.ts +0 -117
- package/dist/src/client/reactor-client.d.ts.map +0 -1
- package/dist/src/client/reactor-client.js +0 -406
- package/dist/src/client/reactor-client.js.map +0 -1
- package/dist/src/client/types.d.ts +0 -241
- package/dist/src/client/types.d.ts.map +0 -1
- package/dist/src/client/types.js +0 -14
- package/dist/src/client/types.js.map +0 -1
- package/dist/src/core/reactor-builder.d.ts +0 -47
- package/dist/src/core/reactor-builder.d.ts.map +0 -1
- package/dist/src/core/reactor-builder.js +0 -231
- package/dist/src/core/reactor-builder.js.map +0 -1
- package/dist/src/core/reactor-client-builder.d.ts +0 -49
- package/dist/src/core/reactor-client-builder.d.ts.map +0 -1
- package/dist/src/core/reactor-client-builder.js +0 -123
- package/dist/src/core/reactor-client-builder.js.map +0 -1
- package/dist/src/core/reactor.d.ts +0 -116
- package/dist/src/core/reactor.d.ts.map +0 -1
- package/dist/src/core/reactor.js +0 -981
- package/dist/src/core/reactor.js.map +0 -1
- package/dist/src/core/types.d.ts +0 -276
- package/dist/src/core/types.d.ts.map +0 -1
- package/dist/src/core/types.js +0 -2
- package/dist/src/core/types.js.map +0 -1
- package/dist/src/core/utils.d.ts +0 -59
- package/dist/src/core/utils.d.ts.map +0 -1
- package/dist/src/core/utils.js +0 -171
- package/dist/src/core/utils.js.map +0 -1
- package/dist/src/events/event-bus.d.ts +0 -8
- package/dist/src/events/event-bus.d.ts.map +0 -1
- package/dist/src/events/event-bus.js +0 -53
- package/dist/src/events/event-bus.js.map +0 -1
- package/dist/src/events/interfaces.d.ts +0 -27
- package/dist/src/events/interfaces.d.ts.map +0 -1
- package/dist/src/events/interfaces.js +0 -2
- package/dist/src/events/interfaces.js.map +0 -1
- package/dist/src/events/types.d.ts +0 -67
- package/dist/src/events/types.d.ts.map +0 -1
- package/dist/src/events/types.js +0 -28
- package/dist/src/events/types.js.map +0 -1
- package/dist/src/executor/interfaces.d.ts +0 -49
- package/dist/src/executor/interfaces.d.ts.map +0 -1
- package/dist/src/executor/interfaces.js +0 -2
- package/dist/src/executor/interfaces.js.map +0 -1
- package/dist/src/executor/simple-job-executor-manager.d.ts +0 -34
- package/dist/src/executor/simple-job-executor-manager.d.ts.map +0 -1
- package/dist/src/executor/simple-job-executor-manager.js +0 -233
- package/dist/src/executor/simple-job-executor-manager.js.map +0 -1
- package/dist/src/executor/simple-job-executor.d.ts +0 -80
- package/dist/src/executor/simple-job-executor.d.ts.map +0 -1
- package/dist/src/executor/simple-job-executor.js +0 -898
- package/dist/src/executor/simple-job-executor.js.map +0 -1
- package/dist/src/executor/types.d.ts +0 -93
- package/dist/src/executor/types.d.ts.map +0 -1
- package/dist/src/executor/types.js +0 -11
- package/dist/src/executor/types.js.map +0 -1
- package/dist/src/executor/util.d.ts +0 -74
- package/dist/src/executor/util.d.ts.map +0 -1
- package/dist/src/executor/util.js +0 -184
- package/dist/src/executor/util.js.map +0 -1
- package/dist/src/index.d.ts +0 -53
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/index.js +0 -62
- package/dist/src/index.js.map +0 -1
- package/dist/src/job-tracker/in-memory-job-tracker.d.ts +0 -25
- package/dist/src/job-tracker/in-memory-job-tracker.d.ts.map +0 -1
- package/dist/src/job-tracker/in-memory-job-tracker.js +0 -112
- package/dist/src/job-tracker/in-memory-job-tracker.js.map +0 -1
- package/dist/src/job-tracker/index.d.ts +0 -3
- package/dist/src/job-tracker/index.d.ts.map +0 -1
- package/dist/src/job-tracker/index.js +0 -2
- package/dist/src/job-tracker/index.js.map +0 -1
- package/dist/src/job-tracker/interfaces.d.ts +0 -39
- package/dist/src/job-tracker/interfaces.d.ts.map +0 -1
- package/dist/src/job-tracker/interfaces.js +0 -2
- package/dist/src/job-tracker/interfaces.js.map +0 -1
- package/dist/src/logging/console.d.ts +0 -23
- package/dist/src/logging/console.d.ts.map +0 -1
- package/dist/src/logging/console.js +0 -108
- package/dist/src/logging/console.js.map +0 -1
- package/dist/src/logging/types.d.ts +0 -12
- package/dist/src/logging/types.d.ts.map +0 -1
- package/dist/src/logging/types.js +0 -2
- package/dist/src/logging/types.js.map +0 -1
- package/dist/src/processors/index.d.ts +0 -3
- package/dist/src/processors/index.d.ts.map +0 -1
- package/dist/src/processors/index.js +0 -2
- package/dist/src/processors/index.js.map +0 -1
- package/dist/src/processors/processor-manager.d.ts +0 -38
- package/dist/src/processors/processor-manager.d.ts.map +0 -1
- package/dist/src/processors/processor-manager.js +0 -165
- package/dist/src/processors/processor-manager.js.map +0 -1
- package/dist/src/processors/types.d.ts +0 -63
- package/dist/src/processors/types.d.ts.map +0 -1
- package/dist/src/processors/types.js +0 -2
- package/dist/src/processors/types.js.map +0 -1
- package/dist/src/processors/utils.d.ts +0 -10
- package/dist/src/processors/utils.d.ts.map +0 -1
- package/dist/src/processors/utils.js +0 -59
- package/dist/src/processors/utils.js.map +0 -1
- package/dist/src/queue/interfaces.d.ts +0 -103
- package/dist/src/queue/interfaces.d.ts.map +0 -1
- package/dist/src/queue/interfaces.js +0 -2
- package/dist/src/queue/interfaces.js.map +0 -1
- package/dist/src/queue/job-execution-handle.d.ts +0 -25
- package/dist/src/queue/job-execution-handle.d.ts.map +0 -1
- package/dist/src/queue/job-execution-handle.js +0 -62
- package/dist/src/queue/job-execution-handle.js.map +0 -1
- package/dist/src/queue/queue.d.ts +0 -81
- package/dist/src/queue/queue.d.ts.map +0 -1
- package/dist/src/queue/queue.js +0 -384
- package/dist/src/queue/queue.js.map +0 -1
- package/dist/src/queue/types.d.ts +0 -73
- package/dist/src/queue/types.d.ts.map +0 -1
- package/dist/src/queue/types.js +0 -19
- package/dist/src/queue/types.js.map +0 -1
- package/dist/src/read-models/base-read-model.d.ts +0 -60
- package/dist/src/read-models/base-read-model.d.ts.map +0 -1
- package/dist/src/read-models/base-read-model.js +0 -143
- package/dist/src/read-models/base-read-model.js.map +0 -1
- package/dist/src/read-models/coordinator.d.ts +0 -39
- package/dist/src/read-models/coordinator.d.ts.map +0 -1
- package/dist/src/read-models/coordinator.js +0 -72
- package/dist/src/read-models/coordinator.js.map +0 -1
- package/dist/src/read-models/document-view.d.ts +0 -24
- package/dist/src/read-models/document-view.d.ts.map +0 -1
- package/dist/src/read-models/document-view.js +0 -375
- package/dist/src/read-models/document-view.js.map +0 -1
- package/dist/src/read-models/interfaces.d.ts +0 -29
- package/dist/src/read-models/interfaces.d.ts.map +0 -1
- package/dist/src/read-models/interfaces.js +0 -2
- package/dist/src/read-models/interfaces.js.map +0 -1
- package/dist/src/read-models/types.d.ts +0 -47
- package/dist/src/read-models/types.d.ts.map +0 -1
- package/dist/src/read-models/types.js +0 -2
- package/dist/src/read-models/types.js.map +0 -1
- package/dist/src/registry/implementation.d.ts +0 -70
- package/dist/src/registry/implementation.d.ts.map +0 -1
- package/dist/src/registry/implementation.js +0 -216
- package/dist/src/registry/implementation.js.map +0 -1
- package/dist/src/registry/index.d.ts +0 -3
- package/dist/src/registry/index.d.ts.map +0 -1
- package/dist/src/registry/index.js +0 -2
- package/dist/src/registry/index.js.map +0 -1
- package/dist/src/registry/interfaces.d.ts +0 -100
- package/dist/src/registry/interfaces.d.ts.map +0 -1
- package/dist/src/registry/interfaces.js +0 -2
- package/dist/src/registry/interfaces.js.map +0 -1
- package/dist/src/shared/awaiter.d.ts +0 -35
- package/dist/src/shared/awaiter.d.ts.map +0 -1
- package/dist/src/shared/awaiter.js +0 -123
- package/dist/src/shared/awaiter.js.map +0 -1
- package/dist/src/shared/consistency-tracker.d.ts +0 -48
- package/dist/src/shared/consistency-tracker.d.ts.map +0 -1
- package/dist/src/shared/consistency-tracker.js +0 -123
- package/dist/src/shared/consistency-tracker.js.map +0 -1
- package/dist/src/shared/drive-url.d.ts +0 -15
- package/dist/src/shared/drive-url.d.ts.map +0 -1
- package/dist/src/shared/drive-url.js +0 -17
- package/dist/src/shared/drive-url.js.map +0 -1
- package/dist/src/shared/errors.d.ts +0 -41
- package/dist/src/shared/errors.d.ts.map +0 -1
- package/dist/src/shared/errors.js +0 -75
- package/dist/src/shared/errors.js.map +0 -1
- package/dist/src/shared/factories.d.ts +0 -16
- package/dist/src/shared/factories.d.ts.map +0 -1
- package/dist/src/shared/factories.js +0 -33
- package/dist/src/shared/factories.js.map +0 -1
- package/dist/src/shared/types.d.ts +0 -135
- package/dist/src/shared/types.d.ts.map +0 -1
- package/dist/src/shared/types.js +0 -38
- package/dist/src/shared/types.js.map +0 -1
- package/dist/src/shared/utils.d.ts +0 -3
- package/dist/src/shared/utils.d.ts.map +0 -1
- package/dist/src/shared/utils.js +0 -8
- package/dist/src/shared/utils.js.map +0 -1
- package/dist/src/signer/passthrough-signer.d.ts +0 -12
- package/dist/src/signer/passthrough-signer.d.ts.map +0 -1
- package/dist/src/signer/passthrough-signer.js +0 -19
- package/dist/src/signer/passthrough-signer.js.map +0 -1
- package/dist/src/signer/types.d.ts +0 -17
- package/dist/src/signer/types.d.ts.map +0 -1
- package/dist/src/signer/types.js +0 -2
- package/dist/src/signer/types.js.map +0 -1
- package/dist/src/storage/consistency-aware-legacy-storage.d.ts +0 -33
- package/dist/src/storage/consistency-aware-legacy-storage.d.ts.map +0 -1
- package/dist/src/storage/consistency-aware-legacy-storage.js +0 -65
- package/dist/src/storage/consistency-aware-legacy-storage.js.map +0 -1
- package/dist/src/storage/index.d.ts +0 -4
- package/dist/src/storage/index.d.ts.map +0 -1
- package/dist/src/storage/index.js +0 -3
- package/dist/src/storage/index.js.map +0 -1
- package/dist/src/storage/interfaces.d.ts +0 -439
- package/dist/src/storage/interfaces.d.ts.map +0 -1
- package/dist/src/storage/interfaces.js +0 -19
- package/dist/src/storage/interfaces.js.map +0 -1
- package/dist/src/storage/kysely/document-indexer.d.ts +0 -28
- package/dist/src/storage/kysely/document-indexer.d.ts.map +0 -1
- package/dist/src/storage/kysely/document-indexer.js +0 -350
- package/dist/src/storage/kysely/document-indexer.js.map +0 -1
- package/dist/src/storage/kysely/keyframe-store.d.ts +0 -15
- package/dist/src/storage/kysely/keyframe-store.d.ts.map +0 -1
- package/dist/src/storage/kysely/keyframe-store.js +0 -64
- package/dist/src/storage/kysely/keyframe-store.js.map +0 -1
- package/dist/src/storage/kysely/store.d.ts +0 -16
- package/dist/src/storage/kysely/store.d.ts.map +0 -1
- package/dist/src/storage/kysely/store.js +0 -233
- package/dist/src/storage/kysely/store.js.map +0 -1
- package/dist/src/storage/kysely/sync-cursor-storage.d.ts +0 -13
- package/dist/src/storage/kysely/sync-cursor-storage.d.ts.map +0 -1
- package/dist/src/storage/kysely/sync-cursor-storage.js +0 -93
- package/dist/src/storage/kysely/sync-cursor-storage.js.map +0 -1
- package/dist/src/storage/kysely/sync-remote-storage.d.ts +0 -13
- package/dist/src/storage/kysely/sync-remote-storage.d.ts.map +0 -1
- package/dist/src/storage/kysely/sync-remote-storage.js +0 -133
- package/dist/src/storage/kysely/sync-remote-storage.js.map +0 -1
- package/dist/src/storage/kysely/types.d.ts +0 -135
- package/dist/src/storage/kysely/types.d.ts.map +0 -1
- package/dist/src/storage/kysely/types.js +0 -2
- package/dist/src/storage/kysely/types.js.map +0 -1
- package/dist/src/storage/migrations/001_create_operation_table.d.ts +0 -3
- package/dist/src/storage/migrations/001_create_operation_table.d.ts.map +0 -1
- package/dist/src/storage/migrations/001_create_operation_table.js +0 -41
- package/dist/src/storage/migrations/001_create_operation_table.js.map +0 -1
- package/dist/src/storage/migrations/002_create_keyframe_table.d.ts +0 -3
- package/dist/src/storage/migrations/002_create_keyframe_table.d.ts.map +0 -1
- package/dist/src/storage/migrations/002_create_keyframe_table.js +0 -27
- package/dist/src/storage/migrations/002_create_keyframe_table.js.map +0 -1
- package/dist/src/storage/migrations/003_create_document_table.d.ts +0 -3
- package/dist/src/storage/migrations/003_create_document_table.d.ts.map +0 -1
- package/dist/src/storage/migrations/003_create_document_table.js +0 -10
- package/dist/src/storage/migrations/003_create_document_table.js.map +0 -1
- package/dist/src/storage/migrations/004_create_document_relationship_table.d.ts +0 -3
- package/dist/src/storage/migrations/004_create_document_relationship_table.d.ts.map +0 -1
- package/dist/src/storage/migrations/004_create_document_relationship_table.js +0 -35
- package/dist/src/storage/migrations/004_create_document_relationship_table.js.map +0 -1
- package/dist/src/storage/migrations/005_create_indexer_state_table.d.ts +0 -3
- package/dist/src/storage/migrations/005_create_indexer_state_table.d.ts.map +0 -1
- package/dist/src/storage/migrations/005_create_indexer_state_table.js +0 -10
- package/dist/src/storage/migrations/005_create_indexer_state_table.js.map +0 -1
- package/dist/src/storage/migrations/006_create_document_snapshot_table.d.ts +0 -3
- package/dist/src/storage/migrations/006_create_document_snapshot_table.d.ts.map +0 -1
- package/dist/src/storage/migrations/006_create_document_snapshot_table.js +0 -49
- package/dist/src/storage/migrations/006_create_document_snapshot_table.js.map +0 -1
- package/dist/src/storage/migrations/007_create_slug_mapping_table.d.ts +0 -3
- package/dist/src/storage/migrations/007_create_slug_mapping_table.d.ts.map +0 -1
- package/dist/src/storage/migrations/007_create_slug_mapping_table.js +0 -24
- package/dist/src/storage/migrations/007_create_slug_mapping_table.js.map +0 -1
- package/dist/src/storage/migrations/008_create_view_state_table.d.ts +0 -3
- package/dist/src/storage/migrations/008_create_view_state_table.d.ts.map +0 -1
- package/dist/src/storage/migrations/008_create_view_state_table.js +0 -10
- package/dist/src/storage/migrations/008_create_view_state_table.js.map +0 -1
- package/dist/src/storage/migrations/009_create_operation_index_tables.d.ts +0 -3
- package/dist/src/storage/migrations/009_create_operation_index_tables.d.ts.map +0 -1
- package/dist/src/storage/migrations/009_create_operation_index_tables.js +0 -50
- package/dist/src/storage/migrations/009_create_operation_index_tables.js.map +0 -1
- package/dist/src/storage/migrations/010_create_sync_tables.d.ts +0 -3
- package/dist/src/storage/migrations/010_create_sync_tables.d.ts.map +0 -1
- package/dist/src/storage/migrations/010_create_sync_tables.js +0 -43
- package/dist/src/storage/migrations/010_create_sync_tables.js.map +0 -1
- package/dist/src/storage/migrations/index.d.ts +0 -3
- package/dist/src/storage/migrations/index.d.ts.map +0 -1
- package/dist/src/storage/migrations/index.js +0 -3
- package/dist/src/storage/migrations/index.js.map +0 -1
- package/dist/src/storage/migrations/migrator.d.ts +0 -6
- package/dist/src/storage/migrations/migrator.d.ts.map +0 -1
- package/dist/src/storage/migrations/migrator.js +0 -78
- package/dist/src/storage/migrations/migrator.js.map +0 -1
- package/dist/src/storage/migrations/run-migrations.d.ts +0 -2
- package/dist/src/storage/migrations/run-migrations.d.ts.map +0 -1
- package/dist/src/storage/migrations/run-migrations.js +0 -58
- package/dist/src/storage/migrations/run-migrations.js.map +0 -1
- package/dist/src/storage/migrations/types.d.ts +0 -9
- package/dist/src/storage/migrations/types.d.ts.map +0 -1
- package/dist/src/storage/migrations/types.js +0 -2
- package/dist/src/storage/migrations/types.js.map +0 -1
- package/dist/src/storage/txn.d.ts +0 -15
- package/dist/src/storage/txn.d.ts.map +0 -1
- package/dist/src/storage/txn.js +0 -42
- package/dist/src/storage/txn.js.map +0 -1
- package/dist/src/subs/default-error-handler.d.ts +0 -13
- package/dist/src/subs/default-error-handler.d.ts.map +0 -1
- package/dist/src/subs/default-error-handler.js +0 -27
- package/dist/src/subs/default-error-handler.js.map +0 -1
- package/dist/src/subs/react-subscription-manager.d.ts +0 -45
- package/dist/src/subs/react-subscription-manager.d.ts.map +0 -1
- package/dist/src/subs/react-subscription-manager.js +0 -185
- package/dist/src/subs/react-subscription-manager.js.map +0 -1
- package/dist/src/subs/subscription-notification-read-model.d.ts +0 -17
- package/dist/src/subs/subscription-notification-read-model.d.ts.map +0 -1
- package/dist/src/subs/subscription-notification-read-model.js +0 -62
- package/dist/src/subs/subscription-notification-read-model.js.map +0 -1
- package/dist/src/subs/types.d.ts +0 -64
- package/dist/src/subs/types.d.ts.map +0 -1
- package/dist/src/subs/types.js +0 -2
- package/dist/src/subs/types.js.map +0 -1
- package/dist/src/sync/channels/composite-channel-factory.d.ts +0 -30
- package/dist/src/sync/channels/composite-channel-factory.d.ts.map +0 -1
- package/dist/src/sync/channels/composite-channel-factory.js +0 -87
- package/dist/src/sync/channels/composite-channel-factory.js.map +0 -1
- package/dist/src/sync/channels/gql-channel-factory.d.ts +0 -25
- package/dist/src/sync/channels/gql-channel-factory.d.ts.map +0 -1
- package/dist/src/sync/channels/gql-channel-factory.js +0 -76
- package/dist/src/sync/channels/gql-channel-factory.js.map +0 -1
- package/dist/src/sync/channels/gql-channel.d.ts +0 -118
- package/dist/src/sync/channels/gql-channel.d.ts.map +0 -1
- package/dist/src/sync/channels/gql-channel.js +0 -423
- package/dist/src/sync/channels/gql-channel.js.map +0 -1
- package/dist/src/sync/channels/index.d.ts +0 -6
- package/dist/src/sync/channels/index.d.ts.map +0 -1
- package/dist/src/sync/channels/index.js +0 -6
- package/dist/src/sync/channels/index.js.map +0 -1
- package/dist/src/sync/channels/polling-channel.d.ts +0 -39
- package/dist/src/sync/channels/polling-channel.d.ts.map +0 -1
- package/dist/src/sync/channels/polling-channel.js +0 -72
- package/dist/src/sync/channels/polling-channel.js.map +0 -1
- package/dist/src/sync/channels/utils.d.ts +0 -30
- package/dist/src/sync/channels/utils.d.ts.map +0 -1
- package/dist/src/sync/channels/utils.js +0 -96
- package/dist/src/sync/channels/utils.js.map +0 -1
- package/dist/src/sync/errors.d.ts +0 -10
- package/dist/src/sync/errors.d.ts.map +0 -1
- package/dist/src/sync/errors.js +0 -17
- package/dist/src/sync/errors.js.map +0 -1
- package/dist/src/sync/index.d.ts +0 -12
- package/dist/src/sync/index.d.ts.map +0 -1
- package/dist/src/sync/index.js +0 -9
- package/dist/src/sync/index.js.map +0 -1
- package/dist/src/sync/interfaces.d.ts +0 -180
- package/dist/src/sync/interfaces.d.ts.map +0 -1
- package/dist/src/sync/interfaces.js +0 -2
- package/dist/src/sync/interfaces.js.map +0 -1
- package/dist/src/sync/mailbox.d.ts +0 -21
- package/dist/src/sync/mailbox.d.ts.map +0 -1
- package/dist/src/sync/mailbox.js +0 -59
- package/dist/src/sync/mailbox.js.map +0 -1
- package/dist/src/sync/sync-builder.d.ts +0 -19
- package/dist/src/sync/sync-builder.d.ts.map +0 -1
- package/dist/src/sync/sync-builder.js +0 -39
- package/dist/src/sync/sync-builder.js.map +0 -1
- package/dist/src/sync/sync-manager.d.ts +0 -37
- package/dist/src/sync/sync-manager.d.ts.map +0 -1
- package/dist/src/sync/sync-manager.js +0 -266
- package/dist/src/sync/sync-manager.js.map +0 -1
- package/dist/src/sync/sync-operation.d.ts +0 -28
- package/dist/src/sync/sync-operation.d.ts.map +0 -1
- package/dist/src/sync/sync-operation.js +0 -63
- package/dist/src/sync/sync-operation.js.map +0 -1
- package/dist/src/sync/types.d.ts +0 -60
- package/dist/src/sync/types.d.ts.map +0 -1
- package/dist/src/sync/types.js +0 -16
- package/dist/src/sync/types.js.map +0 -1
- package/dist/src/sync/utils.d.ts +0 -36
- package/dist/src/sync/utils.d.ts.map +0 -1
- package/dist/src/sync/utils.js +0 -78
- package/dist/src/sync/utils.js.map +0 -1
- package/dist/src/utils/reshuffle.d.ts +0 -30
- package/dist/src/utils/reshuffle.d.ts.map +0 -1
- package/dist/src/utils/reshuffle.js +0 -47
- package/dist/src/utils/reshuffle.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["uuidv4","DowngradeNotSupportedError","DowngradeNotSupportedError","uuidv4","uuidv4","uuidv4","up","up","up","up","up","up","up","up","up","up","up","up","up","migration001","migration002","migration003","migration004","migration005","migration006","migration007","migration008","migration009","migration010","migration011","migration012","migration013","migration014","uuidv4"],"sources":["../src/actions/index.ts","../src/core/utils.ts","../src/events/types.ts","../src/shared/types.ts","../src/shared/awaiter.ts","../src/client/types.ts","../src/client/reactor-client.ts","../src/cache/collection-membership-cache.ts","../src/shared/errors.ts","../src/executor/util.ts","../src/cache/lru/lru-tracker.ts","../src/cache/document-meta-cache.ts","../src/cache/kysely-operation-index.ts","../src/cache/buffer/ring-buffer.ts","../src/cache/kysely-write-cache.ts","../src/events/event-bus.ts","../src/executor/execution-scope.ts","../src/queue/types.ts","../src/registry/implementation.ts","../src/executor/simple-job-executor-manager.ts","../src/utils/reshuffle.ts","../src/cache/operation-index-types.ts","../src/executor/document-action-handler.ts","../src/executor/signature-verifier.ts","../src/executor/simple-job-executor.ts","../src/job-tracker/in-memory-job-tracker.ts","../src/read-models/base-read-model.ts","../src/processors/utils.ts","../src/processors/processor-manager.ts","../src/queue/job-execution-handle.ts","../src/queue/queue.ts","../src/read-models/coordinator.ts","../src/read-models/document-view.ts","../src/registry/document-model-resolver.ts","../src/shared/consistency-tracker.ts","../src/shared/collect-all-pages.ts","../src/storage/kysely/document-indexer.ts","../src/storage/kysely/keyframe-store.ts","../src/storage/interfaces.ts","../src/storage/txn.ts","../src/storage/kysely/store.ts","../src/storage/migrations/001_create_operation_table.ts","../src/storage/migrations/002_create_keyframe_table.ts","../src/storage/migrations/003_create_document_table.ts","../src/storage/migrations/004_create_document_relationship_table.ts","../src/storage/migrations/005_create_indexer_state_table.ts","../src/storage/migrations/006_create_document_snapshot_table.ts","../src/storage/migrations/007_create_slug_mapping_table.ts","../src/storage/migrations/008_create_view_state_table.ts","../src/storage/migrations/009_create_operation_index_tables.ts","../src/storage/migrations/010_create_sync_tables.ts","../src/storage/migrations/011_add_cursor_type_column.ts","../src/storage/migrations/012_add_source_remote_column.ts","../src/storage/migrations/013_create_sync_dead_letters_table.ts","../src/storage/migrations/014_create_processor_cursor_table.ts","../src/storage/migrations/migrator.ts","../src/subs/default-error-handler.ts","../src/subs/react-subscription-manager.ts","../src/subs/subscription-notification-read-model.ts","../src/sync/types.ts","../src/sync/mailbox.ts","../src/sync/buffered-mailbox.ts","../src/sync/errors.ts","../src/sync/sync-operation.ts","../src/sync/utils.ts","../src/sync/channels/interval-poll-timer.ts","../src/sync/channels/utils.ts","../src/sync/channels/gql-req-channel.ts","../src/sync/channels/gql-request-channel-factory.ts","../src/sync/channels/gql-res-channel.ts","../src/sync/channels/gql-response-channel-factory.ts","../src/storage/kysely/sync-cursor-storage.ts","../src/storage/kysely/sync-dead-letter-storage.ts","../src/storage/kysely/sync-remote-storage.ts","../src/sync/batch-aggregator.ts","../src/sync/sync-awaiter.ts","../src/sync/sync-status-tracker.ts","../src/sync/sync-manager.ts","../src/sync/sync-builder.ts","../src/shared/factories.ts","../src/shared/utils.ts","../src/core/types.ts","../src/core/reactor.ts","../src/core/reactor-builder.ts","../src/signer/passthrough-signer.ts","../src/core/reactor-client-builder.ts","../src/shared/drive-url.ts","../src/executor/types.ts","../src/admin/document-integrity-service.ts"],"sourcesContent":["import type {\n Action,\n AddRelationshipActionInput,\n CreateDocumentActionInput,\n DeleteDocumentActionInput,\n RemoveRelationshipActionInput,\n UpgradeDocumentActionInput,\n} from \"@powerhousedao/shared/document-model\";\nimport {\n actions as documentActions,\n generateId,\n} from \"@powerhousedao/shared/document-model\";\n\nexport { documentActions };\n\n/**\n * Creates a CREATE_DOCUMENT action for document creation.\n */\nexport function createDocumentAction(input: CreateDocumentActionInput): Action {\n return {\n id: generateId(),\n type: \"CREATE_DOCUMENT\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\n/**\n * Creates an UPGRADE_DOCUMENT action to set initial document state.\n */\nexport function upgradeDocumentAction(\n input: UpgradeDocumentActionInput,\n): Action {\n return {\n id: generateId(),\n type: \"UPGRADE_DOCUMENT\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\n/**\n * Creates a DELETE_DOCUMENT action for document deletion.\n */\nexport function deleteDocumentAction(documentId: string): Action {\n const input: DeleteDocumentActionInput = {\n documentId,\n };\n\n return {\n id: generateId(),\n type: \"DELETE_DOCUMENT\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\n/**\n * Creates an ADD_RELATIONSHIP action to establish a parent-child relationship.\n */\nexport function addRelationshipAction(\n sourceId: string,\n targetId: string,\n relationshipType: string = \"child\",\n): Action {\n const input: AddRelationshipActionInput = {\n sourceId,\n targetId,\n relationshipType,\n };\n\n return {\n id: generateId(),\n type: \"ADD_RELATIONSHIP\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\n/**\n * Creates a REMOVE_RELATIONSHIP action to remove a parent-child relationship.\n */\nexport function removeRelationshipAction(\n sourceId: string,\n targetId: string,\n relationshipType: string = \"child\",\n): Action {\n const input: RemoveRelationshipActionInput = {\n sourceId,\n targetId,\n relationshipType,\n };\n\n return {\n id: generateId(),\n type: \"REMOVE_RELATIONSHIP\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n","import type {\n Action,\n ISigner,\n Operation,\n PHDocument,\n Signature,\n} from \"@powerhousedao/shared/document-model\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport type { ErrorInfo, JobMeta, PagedResults } from \"../shared/types.js\";\n\n/**\n * Represents a minimal job plan for validation purposes\n */\nexport type JobPlanForValidation = {\n key: string;\n actions: Action[];\n dependsOn: string[];\n};\n\n/**\n * Represents a minimal load job plan for validation purposes\n */\nexport type LoadJobPlanForValidation = {\n key: string;\n operations: Operation[];\n dependsOn: string[];\n};\n\n/**\n * Represents a job plan with scope information for action validation\n */\nexport type JobPlanWithScope = {\n key: string;\n scope: string;\n actions: Action[];\n};\n\n/**\n * Represents a load job plan with scope information for operation validation\n */\nexport type LoadJobPlanWithScope = {\n key: string;\n scope: string;\n operations: Operation[];\n};\n\n/**\n * Represents a job plan with dependencies for topological sorting\n */\nexport type JobPlanForSorting = {\n key: string;\n dependsOn: string[];\n};\n\n/**\n * Validates structural properties shared by all batch requests:\n * duplicate keys, missing dependencies, and dependency cycles.\n */\nexport function validateBatchStructure(jobs: JobPlanForSorting[]): void {\n const keys = new Set<string>();\n for (const job of jobs) {\n if (keys.has(job.key)) {\n throw new Error(`Duplicate plan key: ${job.key}`);\n }\n keys.add(job.key);\n }\n for (const job of jobs) {\n for (const depKey of job.dependsOn) {\n if (!keys.has(depKey)) {\n throw new Error(\n `Job '${job.key}' depends on non-existent key: ${depKey}`,\n );\n }\n }\n }\n const visited = new Set<string>();\n const recStack = new Set<string>();\n const detectCycle = (key: string): boolean => {\n visited.add(key);\n recStack.add(key);\n const job = jobs.find((j) => j.key === key);\n if (job) {\n for (const depKey of job.dependsOn) {\n if (!visited.has(depKey)) {\n if (detectCycle(depKey)) {\n return true;\n }\n } else if (recStack.has(depKey)) {\n return true;\n }\n }\n }\n recStack.delete(key);\n return false;\n };\n for (const job of jobs) {\n if (!visited.has(job.key)) {\n if (detectCycle(job.key)) {\n throw new Error(`Dependency cycle detected involving key: ${job.key}`);\n }\n }\n }\n}\n\n/**\n * Validates a batch mutation request for common errors\n */\nexport function validateBatchRequest(jobs: JobPlanForValidation[]): void {\n validateBatchStructure(jobs);\n for (const job of jobs) {\n if (job.actions.length === 0) {\n throw new Error(`Job '${job.key}' has empty actions array`);\n }\n }\n}\n\n/**\n * Validates a batch load request for common errors\n */\nexport function validateBatchLoadRequest(\n jobs: LoadJobPlanForValidation[],\n): void {\n validateBatchStructure(jobs);\n for (const job of jobs) {\n if (job.operations.length === 0) {\n throw new Error(`Job '${job.key}' has empty operations array`);\n }\n }\n}\n\n/**\n * Validates that all actions in a job match the declared scope\n */\nexport function validateActionScopes(job: JobPlanWithScope): void {\n for (const action of job.actions) {\n const actionScope = action.scope || \"global\";\n if (actionScope !== job.scope) {\n throw new Error(\n `Job '${job.key}' declares scope '${job.scope}' but action has scope '${actionScope}'`,\n );\n }\n }\n}\n\n/**\n * Validates that all operations in a job match the declared scope\n */\nexport function validateOperationScopes(job: LoadJobPlanWithScope): void {\n for (const operation of job.operations) {\n const operationScope = operation.action.scope || \"global\";\n if (operationScope !== job.scope) {\n throw new Error(\n `Job '${job.key}' declares scope '${job.scope}' but operation has scope '${operationScope}'`,\n );\n }\n }\n}\n\n/**\n * Performs topological sort on jobs based on dependencies\n */\nexport function topologicalSort(jobs: JobPlanForSorting[]): string[] {\n const result: string[] = [];\n const visited = new Set<string>();\n const visit = (key: string): void => {\n if (visited.has(key)) {\n return;\n }\n visited.add(key);\n const job = jobs.find((j) => j.key === key);\n if (job) {\n for (const depKey of job.dependsOn) {\n visit(depKey);\n }\n }\n result.push(key);\n };\n for (const job of jobs) {\n visit(job.key);\n }\n return result;\n}\n\n/**\n * Converts an Error or string to ErrorInfo\n */\nexport function toErrorInfo(error: Error | string): ErrorInfo {\n if (error instanceof Error) {\n return {\n message: error.message,\n stack: error.stack || new Error().stack || \"\",\n };\n }\n return {\n message: error,\n stack: new Error().stack || \"\",\n };\n}\n\n/**\n * Filters paged results by document type\n */\nexport function filterByType(\n results: PagedResults<PHDocument>,\n type: string,\n): PagedResults<PHDocument> {\n // Filter documents by their document type from the header\n const filteredDocuments = results.results.filter(\n (document) => document.header.documentType === type,\n );\n\n // Create new paged results with filtered documents\n // Note: This maintains the same paging structure but with filtered results\n return {\n results: filteredDocuments,\n options: results.options,\n nextCursor: results.nextCursor,\n next: results.next\n ? async () => {\n // If there's a next function, apply the same filter to the next page\n const nextResults = await results.next!();\n return filterByType(nextResults, type);\n }\n : undefined,\n };\n}\n\n/**\n * Validates that all operations share the same scope.\n * Throws an error if any operation has a different scope.\n */\nexport function getSharedOperationScope(operations: Operation[]): string {\n if (operations.length === 0) {\n throw new Error(\"No operations provided\");\n }\n\n const baseScope = operations[0].action.scope;\n for (const [index, operation] of operations.entries()) {\n const scope = operation.action.scope;\n if (scope !== baseScope) {\n throw new Error(\n `All operations in load must share the same scope. Expected '${baseScope}', received '${scope}' at position ${index}`,\n );\n }\n }\n\n return baseScope;\n}\n\n/**\n * Validates that all actions share the same scope.\n * Throws an error if any action has a different scope.\n */\nexport function getSharedActionScope(actions: Action[]): string {\n if (actions.length === 0) {\n throw new Error(\"No actions provided\");\n }\n\n const baseScope = actions[0].scope;\n for (const action of actions) {\n if (action.scope !== baseScope) {\n throw new Error(\n `All actions must share the same scope. Expected '${baseScope}', received '${action.scope}'`,\n );\n }\n }\n\n return baseScope;\n}\n\n/**\n * Signs an action with the provided signer.\n * If the action already has valid signatures, it is returned unchanged.\n */\nexport const signAction = async (\n action: Action,\n signer: ISigner,\n signal?: AbortSignal,\n): Promise<Action> => {\n const existingSignatures = action.context?.signer?.signatures;\n if (existingSignatures && existingSignatures.length > 0) {\n return action;\n }\n\n const signature: Signature = await signer.signAction(action, signal);\n\n return {\n ...action,\n context: {\n ...action.context,\n signer: {\n user: {\n address: signer.user?.address || \"\",\n networkId: signer.user?.networkId || \"\",\n chainId: signer.user?.chainId || 0,\n },\n app: {\n name: signer.app?.name || \"\",\n key: signer.app?.key || \"\",\n },\n signatures: [signature],\n },\n },\n };\n};\n\n/**\n * Signs multiple actions with the provided signer\n */\nexport const signActions = async (\n actions: Action[],\n signer: ISigner,\n signal?: AbortSignal,\n): Promise<Action[]> => {\n return Promise.all(\n actions.map((action) => signAction(action, signer, signal)),\n );\n};\n\nexport function buildSingleJobMeta(\n jobId: string,\n callerMeta?: Record<string, unknown>,\n): JobMeta {\n return { ...callerMeta, batchId: uuidv4(), batchJobIds: [jobId] };\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { Job } from \"../queue/types.js\";\nimport type { JobMeta } from \"../shared/types.js\";\n\n/**\n * Describes a function to unsubscribe from an event.\n */\nexport type Unsubscribe = () => void;\n\n/**\n * A subscriber is a function that is called when an event is emitted.\n *\n * It is passed the event type and the data.\n * It can return a promise or a value.\n * If it returns a promise, the event bus will wait for the promise to resolve before calling the next subscriber.\n * If it throws an error, the event bus will reject with an aggregate error of all errors.\n *\n * @param type - The type of event to emit.\n * @param data - The data to pass to the subscriber.\n */\nexport type Subscriber = (type: number, data: any) => void | Promise<void>;\n\n/**\n * Custom error class that aggregates multiple errors from event subscribers.\n */\nexport class EventBusAggregateError extends Error {\n public readonly errors: any[];\n\n constructor(errors: unknown[]) {\n const message = `EventBus emit failed with ${errors.length} error(s): ${errors\n .map((e) => {\n if (e && typeof e === \"object\" && \"message\" in e) {\n return (e as Error).message;\n }\n return String(e);\n })\n .join(\"; \")}`;\n super(message);\n\n this.name = \"EventBusAggregateError\";\n this.errors = errors;\n }\n}\n\n/**\n * Event types for reactor lifecycle events.\n */\nexport const ReactorEventTypes = {\n JOB_PENDING: 10001,\n JOB_RUNNING: 10002,\n JOB_WRITE_READY: 10003,\n JOB_READ_READY: 10004,\n JOB_FAILED: 10005,\n} as const;\n\n/**\n * Event emitted when a job is registered and waiting to be executed.\n */\nexport type JobPendingEvent = {\n jobId: string;\n jobMeta: JobMeta;\n};\n\n/**\n * Event emitted when a job starts executing.\n */\nexport type JobRunningEvent = {\n jobId: string;\n jobMeta: JobMeta;\n};\n\n/**\n * Event emitted when operations are written to IOperationStore.\n * Contains the operations directly to avoid round-trip queries.\n */\nexport type JobWriteReadyEvent = {\n jobId: string;\n operations: OperationWithContext[];\n jobMeta: JobMeta;\n /**\n * Maps documentId to the collection IDs it belongs to.\n * Used by SyncManager to route operations only to remotes\n * whose collection contains the document.\n */\n collectionMemberships?: Record<string, string[]>;\n};\n\n/**\n * Event emitted after all read models have finished processing operations.\n * This event fires after JOB_WRITE_READY and guarantees that:\n * - All read models (DocumentView, DocumentIndexer, etc.) have indexed the operations\n * - All consistency trackers have been updated with the new operation indices\n * - Queries without consistency tokens will now see the updated data\n *\n * This event is useful for:\n * - Test synchronization (knowing when read models are ready)\n * - Observability (measuring read model latency)\n * - Event-driven workflows (triggering downstream processes)\n */\nexport type JobReadReadyEvent = {\n jobId: string;\n operations: OperationWithContext[];\n};\n\n/**\n * Event emitted when a job fails with an unrecoverable error.\n * This event allows the JobAwaiter and other subscribers to react to job failures\n * without polling.\n */\nexport type JobFailedEvent = {\n jobId: string;\n error: Error;\n job?: Job;\n};\n","/**\n * The document ID used for system operations (CREATE_DOCUMENT, DELETE_DOCUMENT, etc.)\n * System operations use this special ID along with the \"system\" scope.\n */\nexport const SYSTEM_DOCUMENT_ID = \"00000000-0000-0000-0000-000000000000\";\n\n/**\n * Information about an error including message and stack trace.\n */\nexport type ErrorInfo = {\n message: string;\n stack: string;\n};\n\n/**\n * Describes the status of a shutdown operation.\n */\nexport type ShutdownStatus = {\n /**\n * True if and only if the system has been shutdown.\n *\n * This value is meant to be polled to determine if the system has been shutdown.\n *\n * In the case of a browser process, the `kill` method should be able to synchronously set this to true.\n *\n * In the case of a server process, a graceful shutdown period should be allowed for the system to finish its work.\n */\n get isShutdown(): boolean;\n\n /**\n * A promise that resolves when the shutdown process is complete.\n *\n * For server environments, await this promise to ensure all active jobs finish\n * before exiting the process.\n */\n completed: Promise<void>;\n};\n\n/**\n * Enum that determines deletion propagation.\n */\nexport enum PropagationMode {\n None = \"none\",\n Cascade = \"cascade\",\n}\n\n/**\n * Enum that describes the type of relationship change.\n */\nexport enum RelationshipChangeType {\n Added = \"added\",\n Removed = \"removed\",\n}\n\n/**\n * Batch-specific metadata always present on every job.\n * Single jobs get a unique batchId and batchJobIds of [jobId].\n */\nexport type BatchMeta = {\n batchId: string;\n batchJobIds: string[];\n};\n\n/**\n * Metadata that flows through the job lifecycle.\n * Always includes batch fields; callers may add additional properties.\n */\nexport type JobMeta = BatchMeta & Record<string, unknown>;\n\nimport type { Job } from \"../queue/types.js\";\n\n/**\n * Describes the current state of a job.\n */\nexport type JobInfo = {\n id: string;\n status: JobStatus;\n createdAtUtcIso: string;\n completedAtUtcIso?: string;\n error?: ErrorInfo;\n errorHistory?: ErrorInfo[];\n result?: any;\n\n /**\n * A token for coordinating reads, only valid once a job reaches COMPLETED.\n */\n consistencyToken: ConsistencyToken;\n\n /**\n * Metadata that flows through the job lifecycle.\n */\n meta: JobMeta;\n\n /**\n * The full job object, populated on failure for debugging purposes.\n */\n job?: Job;\n};\n\n/**\n * Job execution statuses\n */\nexport enum JobStatus {\n /** Job is queued but not yet started */\n PENDING = \"PENDING\",\n /** Job is currently being executed */\n RUNNING = \"RUNNING\",\n /** Operations have been written to the operation store (JOB_WRITE_READY event) */\n WRITE_READY = \"WRITE_READY\",\n /** Read models have finished indexing operations (JOB_READ_READY event) */\n READ_READY = \"READ_READY\",\n /** Job failed (may be retried) */\n FAILED = \"FAILED\",\n}\n\n/**\n * Describe the view of a set of documents. That is, what pieces of the\n * documents are populated.\n */\nexport type ViewFilter = {\n branch?: string;\n scopes?: string[];\n revision?: number;\n};\n\n/**\n * Describes filter options for searching documents.\n *\n * Each parameter is treated as an AND condition.\n */\nexport type SearchFilter = {\n type?: string;\n parentId?: string;\n ids?: string[];\n slugs?: string[];\n};\n\n/**\n * Describes the options for paging.\n */\nexport type PagingOptions = {\n cursor: string;\n limit: number;\n};\n\n/**\n * The paged result.\n */\nexport type PagedResults<T> = {\n results: T[];\n options: PagingOptions;\n\n next?: () => Promise<PagedResults<T>>;\n nextCursor?: string;\n totalCount?: number;\n};\n\n/**\n * A string key in the format `documentId:scope:branch` used to identify a consistency checkpoint.\n */\nexport type ConsistencyKey = `${string}:${string}:${string}`;\n\n/**\n * Describes a specific point in a document's operation history.\n */\nexport type ConsistencyCoordinate = {\n documentId: string;\n scope: string;\n branch: string;\n operationIndex: number;\n};\n\n/**\n * A token that captures the state of write operations at a point in time.\n * Can be used to ensure read-after-write consistency.\n */\nexport type ConsistencyToken = {\n version: 1;\n createdAtUtcIso: string;\n coordinates: ConsistencyCoordinate[];\n};\n","import type { IEventBus } from \"../events/interfaces.js\";\nimport {\n ReactorEventTypes,\n type JobFailedEvent,\n type JobReadReadyEvent,\n type JobWriteReadyEvent,\n type Unsubscribe,\n} from \"../events/types.js\";\nimport { JobStatus, type JobInfo } from \"./types.js\";\n\nexport interface IJobAwaiter {\n /**\n * Waits for a job to complete: turns a job into a promise.\n *\n * @param jobId - The job id or job object\n * @param signal - Optional abort signal to cancel the request\n * @returns The result of the job\n */\n waitForJob(jobId: string, signal?: AbortSignal): Promise<JobInfo>;\n\n /**\n * Shuts down the job awaiter. This will synchronously reject all pending jobs.\n */\n shutdown(): void;\n}\n\ntype JobWaiter = {\n resolve: (value: JobInfo) => void;\n reject: (reason: Error) => void;\n signal?: AbortSignal;\n};\n\n/**\n * Checks if a job status is terminal (job has finished).\n * WRITE_READY is not terminal - it's an intermediate state.\n * Only READ_READY and FAILED are truly terminal.\n */\nfunction isTerminalStatus(status: JobStatus): boolean {\n return status === JobStatus.READ_READY || status === JobStatus.FAILED;\n}\n\n/**\n * Event-driven implementation of IJobAwaiter.\n * Subscribes to operation events to detect job completion without polling.\n */\nexport class JobAwaiter implements IJobAwaiter {\n private pendingJobs = new Map<string, JobWaiter[]>();\n private unsubscribers: Unsubscribe[] = [];\n\n constructor(\n private eventBus: IEventBus,\n private getJobStatus: (\n jobId: string,\n signal?: AbortSignal,\n ) => Promise<JobInfo>,\n ) {\n this.subscribeToEvents();\n }\n\n private subscribeToEvents(): void {\n this.unsubscribers.push(\n this.eventBus.subscribe(\n ReactorEventTypes.JOB_WRITE_READY,\n async (_type, event: JobWriteReadyEvent) => {\n await this.handleWriteReady(event);\n },\n ),\n );\n\n this.unsubscribers.push(\n this.eventBus.subscribe(\n ReactorEventTypes.JOB_READ_READY,\n async (_type, event: JobReadReadyEvent) => {\n await this.handleReadReady(event);\n },\n ),\n );\n\n this.unsubscribers.push(\n this.eventBus.subscribe(\n ReactorEventTypes.JOB_FAILED,\n async (_type, event: JobFailedEvent) => {\n await this.handleJobFailed(event);\n },\n ),\n );\n }\n\n shutdown(): void {\n for (const unsubscribe of this.unsubscribers) {\n unsubscribe();\n }\n this.unsubscribers = [];\n\n for (const [, waiters] of this.pendingJobs) {\n for (const waiter of waiters) {\n waiter.reject(new Error(\"JobAwaiter destroyed\"));\n }\n }\n this.pendingJobs.clear();\n }\n\n async waitForJob(jobId: string, signal?: AbortSignal): Promise<JobInfo> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const currentStatus = await this.getJobStatus(jobId, signal);\n if (isTerminalStatus(currentStatus.status)) {\n return currentStatus;\n }\n\n const promise = new Promise<JobInfo>((resolve, reject) => {\n const waiter: JobWaiter = { resolve, reject, signal };\n\n const existingWaiters = this.pendingJobs.get(jobId) || [];\n existingWaiters.push(waiter);\n this.pendingJobs.set(jobId, existingWaiters);\n\n if (signal) {\n const abortHandler = () => {\n const waiters = this.pendingJobs.get(jobId);\n if (waiters) {\n const index = waiters.indexOf(waiter);\n if (index !== -1) {\n waiters.splice(index, 1);\n if (waiters.length === 0) {\n this.pendingJobs.delete(jobId);\n }\n waiter.reject(new Error(\"Operation aborted\"));\n }\n }\n };\n\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n });\n\n return promise;\n }\n\n private async handleWriteReady(event: JobWriteReadyEvent): Promise<void> {\n const jobId = event.jobId;\n await this.checkAndResolveWaiters(jobId);\n }\n\n private async handleReadReady(event: JobReadReadyEvent): Promise<void> {\n const jobId = event.jobId;\n await this.checkAndResolveWaiters(jobId);\n }\n\n private async handleJobFailed(event: JobFailedEvent): Promise<void> {\n await this.checkAndResolveWaiters(event.jobId);\n }\n\n private async checkAndResolveWaiters(jobId: string): Promise<void> {\n const waiters = this.pendingJobs.get(jobId);\n if (!waiters || waiters.length === 0) {\n return;\n }\n\n try {\n const activeWaiters = waiters.filter((w) => !w.signal?.aborted);\n\n if (activeWaiters.length === 0) {\n this.pendingJobs.delete(jobId);\n return;\n }\n\n const jobInfo = await this.getJobStatus(jobId, activeWaiters[0].signal);\n\n if (isTerminalStatus(jobInfo.status)) {\n this.pendingJobs.delete(jobId);\n\n for (const waiter of activeWaiters) {\n waiter.resolve(jobInfo);\n }\n\n for (const waiter of waiters) {\n if (waiter.signal?.aborted) {\n waiter.reject(new Error(\"Operation aborted\"));\n }\n }\n }\n } catch (error) {\n this.pendingJobs.delete(jobId);\n\n for (const waiter of waiters) {\n waiter.reject(\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n }\n }\n}\n","import type {\n Action,\n DocumentModelModule,\n Operation,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\n\nimport type { BatchLoadRequest, BatchLoadResult } from \"../core/types.js\";\nimport type {\n JobInfo,\n PagedResults,\n PagingOptions,\n PropagationMode,\n SearchFilter,\n ViewFilter,\n} from \"../shared/types.js\";\nimport type { OperationFilter } from \"../storage/interfaces.js\";\n\n/**\n * Describes the types of document changes that can occur.\n */\nexport enum DocumentChangeType {\n Created = \"created\",\n Deleted = \"deleted\",\n Updated = \"updated\",\n ParentAdded = \"parent_added\",\n ParentRemoved = \"parent_removed\",\n ChildAdded = \"child_added\",\n ChildRemoved = \"child_removed\",\n}\n\n/**\n * Represents a change event for documents.\n */\nexport type DocumentChangeEvent = {\n type: DocumentChangeType;\n documents: PHDocument[];\n context?: {\n parentId?: string;\n childId?: string;\n };\n};\n\n/**\n * Options for creating an empty document.\n */\nexport type CreateDocumentOptions = {\n /** Optional \"id\" or \"slug\" of parent document */\n parentIdentifier?: string;\n /** Optional version of the document model to use (defaults to latest) */\n documentModelVersion?: number;\n};\n\n/**\n * The ReactorClient interface that wraps lower-level APIs to provide\n * a simpler interface for document operations.\n *\n * Features:\n * - Wraps Jobs with Promises for easier async handling\n * - Manages signing of submitted Action objects\n * - Provides quality-of-life functions for common tasks\n * - Wraps subscription interface with ViewFilters\n */\nexport interface IReactorClient {\n /**\n * Retrieves a list of document model modules.\n *\n * @param namespace - Optional namespace like \"powerhouse\" or \"sky\", defaults to \"\"\n * @param paging - Optional pagination options\n * @param signal - Optional abort signal to cancel the request\n * @returns List of document model modules\n */\n getDocumentModelModules(\n namespace?: string,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentModelModule>>;\n\n /**\n * Retrieves a specific document model module by document type.\n *\n * @param documentType - The document type identifier\n * @returns The document model module\n */\n getDocumentModelModule(\n documentType: string,\n ): Promise<DocumentModelModule<any>>;\n\n /**\n * Retrieves a specific document by identifier (either id or slug).\n *\n * @param identifier - Required, this is the document id or slug\n * @param view - Optional filter containing branch and scopes information\n * @param signal - Optional abort signal to cancel the request\n * @returns The up-to-date PHDocument with scopes and list of child document ids\n */\n get<TDocument extends PHDocument>(\n identifier: string,\n view?: ViewFilter,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Retrieves operations for a document.\n *\n * @param documentIdentifier - Required, this is either a document \"id\" field or a \"slug\"\n * @param view - Optional filter containing branch and scopes information\n * @param filter - Optional filter for actionTypes, timestamps, and revision\n * @param paging - Optional pagination options\n * @param signal - Optional abort signal to cancel the request\n * @returns Paginated list of operations\n */\n getOperations(\n documentIdentifier: string,\n view?: ViewFilter,\n filter?: OperationFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<Operation>>;\n\n /**\n * Retrieves children of a document.\n *\n * @param parentIdentifier - Required, this is either a document \"id\" field or a \"slug\"\n * @param view - Optional filter containing branch and scopes information\n * @param paging - Optional pagination options\n * @param signal - Optional abort signal to cancel the request\n * @returns The up-to-date PHDocument and paging cursor\n */\n getChildren(\n parentIdentifier: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>>;\n\n /**\n * Retrieves parents of a document.\n *\n * @param childIdentifier - Required, this is either a document \"id\" field or a \"slug\"\n * @param view - Optional filter containing branch and scopes information\n * @param paging - Optional pagination options\n * @param signal - Optional abort signal to cancel the request\n * @returns The up-to-date PHDocument and paging cursor\n */\n getParents(\n childIdentifier: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>>;\n\n /**\n * Filters documents by criteria and returns a list of them\n *\n * @param search - Search filter options (type, parentId, identifiers)\n * @param view - Optional filter containing branch and scopes information\n * @param paging - Optional pagination options\n * @param signal - Optional abort signal to cancel the request\n * @returns List of documents matching criteria and pagination cursor\n */\n find(\n search: SearchFilter,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>>;\n\n /**\n * Creates a document and waits for completion\n *\n * @param document - Document with optional id, slug, parent, model type, and initial state\n * @param parentIdentifier - Optional \"id\" or \"slug\" of parent document\n * @param signal - Optional abort signal to cancel the request\n * @returns The created document\n */\n create<TDocument extends PHDocument = PHDocument>(\n document: PHDocument,\n parentIdentifier?: string,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Creates an empty document and waits for completion\n *\n * @param documentModelType - Type of document to create\n * @param options - Optional creation options (parentIdentifier, documentModelVersion)\n * @param signal - Optional abort signal to cancel the request\n */\n createEmpty<TDocument extends PHDocument>(\n documentModelType: string,\n options?: CreateDocumentOptions,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Creates an empty document in a drive as a single batched operation.\n * This is more efficient than createEmpty + addFile as it batches all\n * actions into dependent jobs and waits for them to complete together.\n *\n * @param driveId - The drive document id or slug\n * @param document - The document to create\n * @param parentFolder - Optional folder id within the drive\n * @param signal - Optional abort signal to cancel the request\n * @returns The created document\n */\n createDocumentInDrive<TDocument extends PHDocument>(\n driveId: string,\n document: PHDocument,\n parentFolder?: string,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Applies a list of actions to a document and waits for completion\n *\n * @param documentIdentifier - Target document id or slug\n * @param branch - Branch to apply actions to\n * @param actions - List of actions to apply\n * @param signal - Optional abort signal to cancel the request\n * @returns The updated document\n */\n execute<TDocument extends PHDocument>(\n documentIdentifier: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Submits a list of actions to a document\n *\n * @param documentIdentifier - Target document id or slug\n * @param branch - Branch to apply actions to\n * @param actions - List of actions to apply\n * @param signal - Optional abort signal to cancel the request\n * @returns The job\n */\n executeAsync(\n documentIdentifier: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\n ): Promise<JobInfo>;\n\n /**\n * Renames a document and waits for completion\n *\n * @param documentIdentifier - Target document id or slug\n * @param name - The new name of the document\n * @param branch - Optional branch to rename the document, defaults to \"main\"\n * @param signal - Optional abort signal to cancel the request\n * @returns The updated document.\n */\n rename(\n documentIdentifier: string,\n name: string,\n branch?: string,\n signal?: AbortSignal,\n ): Promise<PHDocument>;\n\n /**\n * Adds multiple documents as children to another and waits for completion\n *\n * @param parentIdentifier - Parent document id or slug\n * @param documentIdentifiers - List of document identifiers to add as children\n * @param branch - Optional branch to add children to, defaults to \"main\"\n * @param signal - Optional abort signal to cancel the request\n * @returns The updated parent document\n */\n addChildren(\n parentIdentifier: string,\n documentIdentifiers: string[],\n branch?: string,\n signal?: AbortSignal,\n ): Promise<PHDocument>;\n\n /**\n * Removes multiple documents as children from another and waits for completion\n *\n * @param parentIdentifier - Parent document identifiers\n * @param documentIdentifiers - List of document ids to remove as children\n * @param branch - Optional branch to remove children from, defaults to \"main\"\n * @param signal - Optional abort signal to cancel the request\n * @returns The updated parent document\n */\n removeChildren(\n parentIdentifier: string,\n documentIdentifiers: string[],\n branch?: string,\n signal?: AbortSignal,\n ): Promise<PHDocument>;\n\n /**\n * Moves multiple documents from one parent to another and waits for completion\n *\n * @param sourceParentIdentifier - Source parent document id or slug\n * @param targetParentIdentifier - Target parent document id or slug\n * @param documentIdentifiers - List of document identifiers to move\n * @param branch - Optional branch to move children to, defaults to \"main\"\n * @param signal - Optional abort signal to cancel the request\n * @returns The updated source and target documents\n */\n moveChildren(\n sourceParentIdentifier: string,\n targetParentIdentifier: string,\n documentIdentifiers: string[],\n branch?: string,\n signal?: AbortSignal,\n ): Promise<{\n source: PHDocument;\n target: PHDocument;\n }>;\n\n /**\n * Deletes a document and waits for completion\n *\n * @param identifier - Document identifier (id or slug)\n * @param propagate - Optional mode for handling children, CASCADE deletes child documents\n * @param signal - Optional abort signal to cancel the request\n * @returns a promise, resolving on deletion confirmation\n */\n deleteDocument(\n identifier: string,\n propagate?: PropagationMode,\n signal?: AbortSignal,\n ): Promise<void>;\n\n /**\n * Deletes documents and waits for completion\n *\n * @param identifiers - Document identifiers (ids or slugs)\n * @param propagate - Optional mode for handling children, CASCADE deletes child documents\n * @param signal - Optional abort signal to cancel the request\n * @returns a promise, resolving on deletion confirmation\n */\n deleteDocuments(\n identifiers: string[],\n propagate?: PropagationMode,\n signal?: AbortSignal,\n ): Promise<void>;\n\n /**\n * Loads multiple batches of pre-existing operations across documents with dependency management.\n * Waits for all jobs to complete.\n *\n * @param request - Batch load request containing jobs with dependencies\n * @param signal - Optional abort signal to cancel the request\n * @returns Map of job keys to completed job information\n */\n loadBatch(\n request: BatchLoadRequest,\n signal?: AbortSignal,\n ): Promise<BatchLoadResult>;\n\n /**\n * Retrieves the status of a job\n *\n * @param jobId - The job id\n * @param signal - Optional abort signal to cancel the request\n * @returns The job status\n */\n getJobStatus(jobId: string, signal?: AbortSignal): Promise<JobInfo>;\n\n /**\n * Waits for a job to complete\n *\n * @param jobId - The job id or job object\n * @param signal - Optional abort signal to cancel the request\n * @returns The result of the job\n */\n waitForJob(jobId: string | JobInfo, signal?: AbortSignal): Promise<JobInfo>;\n\n /**\n * Subscribes to changes for documents matching specified filters\n *\n * @param search - Search filter options (type, parentId, identifiers)\n * @param callback - Function called when documents change with the change event details\n * @param view - Optional filter containing branch and scopes information\n * @returns A function that unsubscribes from the changes\n */\n subscribe(\n search: SearchFilter,\n callback: (event: DocumentChangeEvent) => void,\n view?: ViewFilter,\n ): () => void;\n}\n","import { addFile, deleteNode } from \"@powerhousedao/shared/document-drive\";\nimport type {\n Action,\n CreateDocumentActionInput,\n DocumentModelModule,\n ISigner,\n Operation,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport { actions } from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport {\n addRelationshipAction,\n createDocumentAction,\n removeRelationshipAction,\n upgradeDocumentAction,\n} from \"../actions/index.js\";\nimport type {\n BatchLoadRequest,\n BatchLoadResult,\n ExecutionJobPlan,\n IReactor,\n} from \"../core/types.js\";\nimport { getSharedActionScope, signActions } from \"../core/utils.js\";\nimport { type IJobAwaiter } from \"../shared/awaiter.js\";\nimport {\n JobStatus,\n PropagationMode,\n RelationshipChangeType,\n type JobInfo,\n type PagedResults,\n type PagingOptions,\n type SearchFilter,\n type ViewFilter,\n} from \"../shared/types.js\";\nimport type {\n IDocumentIndexer,\n IDocumentView,\n OperationFilter,\n} from \"../storage/interfaces.js\";\nimport type { IReactorSubscriptionManager } from \"../subs/types.js\";\nimport {\n DocumentChangeType,\n type CreateDocumentOptions,\n type DocumentChangeEvent,\n type IReactorClient,\n} from \"./types.js\";\n\n/**\n * ReactorClient implementation that wraps lower-level APIs to provide\n * a simpler interface for document operations.\n *\n * Features:\n * - Wraps Jobs with Promises for easier async handling\n * - Manages signing of submitted Action objects\n * - Provides quality-of-life functions for common tasks\n * - Wraps subscription interface with ViewFilters\n */\nexport class ReactorClient implements IReactorClient {\n private logger: ILogger;\n private reactor: IReactor;\n private signer: ISigner;\n private subscriptionManager: IReactorSubscriptionManager;\n private jobAwaiter: IJobAwaiter;\n private documentIndexer: IDocumentIndexer;\n private documentView: IDocumentView;\n\n constructor(\n logger: ILogger,\n reactor: IReactor,\n signer: ISigner,\n subscriptionManager: IReactorSubscriptionManager,\n jobAwaiter: IJobAwaiter,\n documentIndexer: IDocumentIndexer,\n documentView: IDocumentView,\n ) {\n this.logger = logger;\n this.reactor = reactor;\n this.signer = signer;\n this.subscriptionManager = subscriptionManager;\n this.jobAwaiter = jobAwaiter;\n this.documentIndexer = documentIndexer;\n this.documentView = documentView;\n this.logger.verbose(\"ReactorClient initialized\");\n }\n\n /**\n * Retrieves a list of document model modules.\n */\n async getDocumentModelModules(\n namespace?: string,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentModelModule>> {\n this.logger.verbose(\n \"getDocumentModels(@namespace, @paging)\",\n namespace,\n paging,\n );\n return this.reactor.getDocumentModels(namespace, paging, signal);\n }\n\n /**\n * Retrieves a specific document model module by document type.\n *\n * @param documentType - The document type identifier\n * @returns The document model module\n */\n async getDocumentModelModule(\n documentType: string,\n ): Promise<DocumentModelModule<any>> {\n const modules = await this.reactor.getDocumentModels();\n const module = modules.results.find(\n (m) => m.documentModel.global.id === documentType,\n );\n\n if (!module) {\n throw new Error(\n `Document model module not found for type: ${documentType}`,\n );\n }\n\n return module as DocumentModelModule<any>;\n }\n\n /**\n * Retrieves a specific PHDocument\n */\n async get<TDocument extends PHDocument>(\n identifier: string,\n view?: ViewFilter,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\"get(@identifier, @view)\", identifier, view);\n return await this.reactor.getByIdOrSlug<TDocument>(\n identifier,\n view,\n undefined,\n signal,\n );\n }\n\n /**\n * Retrieves operations for a document\n */\n async getOperations(\n documentIdentifier: string,\n view?: ViewFilter,\n filter?: OperationFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<Operation>> {\n this.logger.verbose(\n \"getOperations(@documentIdentifier, @view, @filter, @paging)\",\n documentIdentifier,\n view,\n filter,\n paging,\n );\n\n const documentId = await this.documentView.resolveIdOrSlug(\n documentIdentifier,\n view,\n undefined,\n signal,\n );\n\n const operationsByScope = await this.reactor.getOperations(\n documentId,\n view,\n filter,\n paging,\n undefined,\n signal,\n );\n\n const scopeEntries = Object.values(operationsByScope);\n\n const allOperations: Operation[] = [];\n for (const scopeResults of scopeEntries) {\n allOperations.push(...scopeResults.results);\n }\n\n allOperations.sort((a, b) => a.index - b.index);\n\n const effectivePaging = paging || { cursor: \"0\", limit: 100 };\n\n // Cursor is only valid for single-scope results\n const nextCursor =\n scopeEntries.length === 1 ? scopeEntries[0].nextCursor : undefined;\n\n return {\n results: allOperations,\n options: effectivePaging,\n nextCursor,\n };\n }\n\n /**\n * Retrieves children of a document\n */\n async getChildren(\n parentIdentifier: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\n \"getChildren(@parentIdentifier, @view, @paging)\",\n parentIdentifier,\n view,\n paging,\n );\n\n const parentId = await this.documentView.resolveIdOrSlug(\n parentIdentifier,\n view,\n undefined,\n signal,\n );\n\n const relationships = await this.documentIndexer.getOutgoing(\n parentId,\n undefined,\n undefined,\n undefined,\n signal,\n );\n\n const childIds = relationships.results.map((rel) => rel.targetId);\n\n if (childIds.length === 0) {\n return {\n results: [],\n options: paging || { cursor: \"0\", limit: 0 },\n };\n }\n\n return this.reactor.find(\n { ids: childIds },\n view,\n paging,\n undefined,\n signal,\n );\n }\n\n /**\n * Retrieves parents of a document\n */\n async getParents(\n childIdentifier: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\n \"getParents(@childIdentifier, @view, @paging)\",\n childIdentifier,\n view,\n paging,\n );\n\n const childId = await this.documentView.resolveIdOrSlug(\n childIdentifier,\n view,\n undefined,\n signal,\n );\n\n const relationships = await this.documentIndexer.getIncoming(\n childId,\n undefined,\n undefined,\n undefined,\n signal,\n );\n\n const parentIds = relationships.results.map((rel) => rel.sourceId);\n\n if (parentIds.length === 0) {\n return {\n results: [],\n options: paging || { cursor: \"0\", limit: 0 },\n };\n }\n\n return this.reactor.find(\n { ids: parentIds },\n view,\n paging,\n undefined,\n signal,\n );\n }\n\n /**\n * Filters documents by criteria and returns a list of them\n */\n async find(\n search: SearchFilter,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\"find(@search, @view, @paging)\", search, view, paging);\n return this.reactor.find(search, view, paging, undefined, signal);\n }\n\n /**\n * Creates a document and waits for completion\n */\n async create<TDocument extends PHDocument = PHDocument>(\n document: PHDocument,\n parentIdentifier?: string,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\n \"create(@id, @parentIdentifier)\",\n document.header.id,\n parentIdentifier,\n );\n\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 createActions: Action[] = await signActions(\n [\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 this.signer,\n signal,\n );\n\n const jobs: ExecutionJobPlan[] = [\n {\n key: \"create\",\n documentId,\n scope: getSharedActionScope(createActions),\n branch: \"main\",\n actions: createActions,\n dependsOn: [],\n },\n ];\n\n if (parentIdentifier) {\n const parentActions: Action[] = await signActions(\n [addRelationshipAction(parentIdentifier, documentId, \"child\")],\n this.signer,\n signal,\n );\n\n jobs.push({\n key: \"parent\",\n documentId: parentIdentifier,\n scope: getSharedActionScope(parentActions),\n branch: \"main\",\n actions: parentActions,\n dependsOn: [\"create\"],\n });\n }\n\n const batchResult = await this.reactor.executeBatch({ jobs }, signal);\n\n const completedJobs = await Promise.all(\n Object.values(batchResult.jobs).map((job) =>\n this.waitForJob(job, signal),\n ),\n );\n\n for (const job of completedJobs) {\n if (job.status === JobStatus.FAILED) {\n throw new Error(job.error?.message);\n }\n }\n\n return await this.reactor.get<TDocument>(documentId);\n }\n\n /**\n * Creates an empty document and waits for completion\n */\n async createEmpty<TDocument extends PHDocument>(\n documentModelType: string,\n options?: CreateDocumentOptions,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\n \"createEmpty(@documentModelType, @options)\",\n documentModelType,\n options,\n );\n const modulesResult = await this.reactor.getDocumentModels(\n undefined,\n undefined,\n signal,\n );\n\n const matchingModules = modulesResult.results.filter(\n (m) => m.documentModel.global.id === documentModelType,\n );\n\n let module: DocumentModelModule | undefined;\n if (options?.documentModelVersion !== undefined) {\n module = matchingModules.find(\n (m) => m.version === options.documentModelVersion,\n );\n if (!module) {\n throw new Error(\n `Document model not found for type: ${documentModelType} with version: ${options.documentModelVersion}`,\n );\n }\n } else {\n module = matchingModules.reduce<DocumentModelModule | undefined>(\n (latest, current) => {\n if (latest === undefined) return current;\n const currentVersion = current.version ?? 0;\n const latestVersion = latest.version ?? 0;\n return currentVersion > latestVersion ? current : latest;\n },\n undefined,\n );\n if (!module) {\n throw new Error(\n `Document model not found for type: ${documentModelType}`,\n );\n }\n }\n\n const document = module.utils.createDocument();\n document.state.document.version = module.version ?? 1;\n\n return this.create<TDocument>(document, options?.parentIdentifier, signal);\n }\n\n /**\n * Creates an empty document in a drive as a single batched operation.\n */\n async createDocumentInDrive<TDocument extends PHDocument>(\n driveId: string,\n document: PHDocument,\n parentFolder?: string,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\n \"createDocumentInDrive(@driveId, @document, @parentFolder)\",\n driveId,\n document,\n parentFolder,\n );\n\n const documentId = document.header.id;\n\n const createInput: CreateDocumentActionInput = {\n model: document.header.documentType,\n version: 0,\n documentId: document.header.id,\n signing: {\n signature: document.header.id,\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 documentActions: Action[] = await signActions(\n [\n createDocumentAction(createInput),\n upgradeDocumentAction({\n documentId: document.header.id,\n model: document.header.documentType,\n fromVersion: 0,\n toVersion: 1,\n initialState: document.state,\n }),\n addRelationshipAction(driveId, documentId, \"child\"),\n ],\n this.signer,\n signal,\n );\n\n const driveActions: Action[] = await signActions(\n [\n addFile({\n id: documentId,\n name: document.header.name || documentId,\n documentType: document.header.documentType,\n parentFolder,\n }),\n ],\n this.signer,\n signal,\n );\n\n const batchResult = await this.reactor.executeBatch(\n {\n jobs: [\n {\n key: \"document\",\n documentId,\n scope: getSharedActionScope(documentActions),\n branch: \"main\",\n actions: documentActions,\n dependsOn: [],\n },\n {\n key: \"drive\",\n documentId: driveId,\n scope: getSharedActionScope(driveActions),\n branch: \"main\",\n actions: driveActions,\n dependsOn: [\"document\"],\n },\n ],\n },\n signal,\n );\n\n const completedJobs = await Promise.all(\n Object.values(batchResult.jobs).map((job) =>\n this.waitForJob(job, signal),\n ),\n );\n\n for (const job of completedJobs) {\n if (job.status === JobStatus.FAILED) {\n throw new Error(job.error?.message);\n }\n }\n\n // since we waited for the job to complete we don't need the consistency token\n return this.reactor.get<TDocument>(documentId);\n }\n\n /**\n * Applies a list of actions to a document and waits for completion\n */\n async execute<TDocument extends PHDocument>(\n documentIdentifier: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\n \"execute(@documentIdentifier, @branch, @count actions)\",\n documentIdentifier,\n branch,\n actions.length,\n );\n const signedActions = await signActions(actions, this.signer, signal);\n\n const jobInfo = await this.reactor.execute(\n documentIdentifier,\n branch,\n signedActions,\n signal,\n );\n\n const completedJob = await this.waitForJob(jobInfo, signal);\n\n if (completedJob.status === JobStatus.FAILED) {\n throw new Error(completedJob.error?.message);\n }\n\n const view: ViewFilter = { branch };\n const result = await this.reactor.getByIdOrSlug<TDocument>(\n documentIdentifier,\n view,\n completedJob.consistencyToken,\n signal,\n );\n return result;\n }\n\n /**\n * Submits a list of actions to a document\n */\n async executeAsync(\n documentIdentifier: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\n ): Promise<JobInfo> {\n this.logger.verbose(\n \"executeAsync(@documentIdentifier, @branch, @count actions)\",\n documentIdentifier,\n branch,\n actions.length,\n );\n const signedActions = await signActions(actions, this.signer, signal);\n\n return this.reactor.execute(\n documentIdentifier,\n branch,\n signedActions,\n signal,\n );\n }\n\n /**\n * Renames a document and waits for completion\n */\n async rename(\n documentIdentifier: string,\n name: string,\n branch: string = \"main\",\n signal?: AbortSignal,\n ): Promise<PHDocument> {\n this.logger.verbose(\n \"rename(@documentIdentifier, @name, @branch)\",\n documentIdentifier,\n name,\n branch,\n );\n return this.execute(\n documentIdentifier,\n branch,\n [actions.setName(name)],\n signal,\n );\n }\n\n /**\n * Adds multiple documents as children to another and waits for completion\n */\n async addChildren(\n parentIdentifier: string,\n documentIdentifiers: string[],\n branch: string = \"main\",\n signal?: AbortSignal,\n ): Promise<PHDocument> {\n this.logger.verbose(\n \"addChildren(@parentIdentifier, @count children, @branch)\",\n parentIdentifier,\n documentIdentifiers.length,\n branch,\n );\n const jobInfo = await this.reactor.addChildren(\n parentIdentifier,\n documentIdentifiers,\n branch,\n this.signer,\n signal,\n );\n\n const completedJob = await this.waitForJob(jobInfo, signal);\n\n if (completedJob.status === JobStatus.FAILED) {\n throw new Error(completedJob.error?.message);\n }\n\n const result = await this.reactor.getByIdOrSlug<PHDocument>(\n parentIdentifier,\n { branch },\n completedJob.consistencyToken,\n signal,\n );\n return result;\n }\n\n /**\n * Removes multiple documents as children from another and waits for completion\n */\n async removeChildren(\n parentIdentifier: string,\n documentIdentifiers: string[],\n branch: string = \"main\",\n signal?: AbortSignal,\n ): Promise<PHDocument> {\n this.logger.verbose(\n \"removeChildren(@parentIdentifier, @count children, @branch)\",\n parentIdentifier,\n documentIdentifiers.length,\n branch,\n );\n const jobInfo = await this.reactor.removeChildren(\n parentIdentifier,\n documentIdentifiers,\n branch,\n this.signer,\n signal,\n );\n\n const completedJob = await this.waitForJob(jobInfo, signal);\n\n if (completedJob.status === JobStatus.FAILED) {\n throw new Error(completedJob.error?.message);\n }\n\n const result = await this.reactor.getByIdOrSlug<PHDocument>(\n parentIdentifier,\n { branch },\n completedJob.consistencyToken,\n signal,\n );\n return result;\n }\n\n /**\n * Moves multiple documents from one parent to another and waits for completion\n */\n async moveChildren(\n sourceParentIdentifier: string,\n targetParentIdentifier: string,\n documentIdentifiers: string[],\n branch: string = \"main\",\n signal?: AbortSignal,\n ): Promise<{\n source: PHDocument;\n target: PHDocument;\n }> {\n this.logger.verbose(\n \"moveChildren(@sourceParentIdentifier, @targetParentIdentifier, @count children, @branch)\",\n sourceParentIdentifier,\n targetParentIdentifier,\n documentIdentifiers.length,\n branch,\n );\n const removeJobInfo = await this.reactor.removeChildren(\n sourceParentIdentifier,\n documentIdentifiers,\n branch,\n this.signer,\n signal,\n );\n\n const removeCompletedJob = await this.waitForJob(removeJobInfo, signal);\n\n if (removeCompletedJob.status === JobStatus.FAILED) {\n throw new Error(removeCompletedJob.error?.message);\n }\n\n const addJobInfo = await this.reactor.addChildren(\n targetParentIdentifier,\n documentIdentifiers,\n branch,\n this.signer,\n signal,\n );\n\n const addCompletedJob = await this.waitForJob(addJobInfo, signal);\n\n if (addCompletedJob.status === JobStatus.FAILED) {\n throw new Error(addCompletedJob.error?.message);\n }\n\n const sourceResult = await this.reactor.getByIdOrSlug<PHDocument>(\n sourceParentIdentifier,\n { branch },\n removeCompletedJob.consistencyToken,\n signal,\n );\n\n const targetResult = await this.reactor.getByIdOrSlug<PHDocument>(\n targetParentIdentifier,\n { branch },\n addCompletedJob.consistencyToken,\n signal,\n );\n\n return {\n source: sourceResult,\n target: targetResult,\n };\n }\n\n async loadBatch(\n request: BatchLoadRequest,\n signal?: AbortSignal,\n ): Promise<BatchLoadResult> {\n this.logger.verbose(\"loadBatch(@count jobs)\", request.jobs.length);\n const result = await this.reactor.loadBatch(request, signal);\n\n const completedJobs = await Promise.all(\n Object.entries(result.jobs).map(async ([key, jobInfo]) => {\n const completed = await this.waitForJob(jobInfo, signal);\n return [key, completed] as const;\n }),\n );\n\n for (const [, completedJob] of completedJobs) {\n if (completedJob.status === JobStatus.FAILED) {\n throw new Error(completedJob.error?.message);\n }\n }\n\n return { jobs: Object.fromEntries(completedJobs) };\n }\n\n /**\n * Deletes a document and waits for completion\n */\n async deleteDocument(\n identifier: string,\n propagate?: PropagationMode,\n signal?: AbortSignal,\n ): Promise<void> {\n this.logger.verbose(\n \"deleteDocument(@identifier, @propagate)\",\n identifier,\n propagate,\n );\n const jobs: JobInfo[] = [];\n\n if (propagate === PropagationMode.Cascade) {\n const descendants: string[] = [];\n const queue: string[] = [identifier];\n const visited = new Set<string>();\n\n while (queue.length > 0) {\n const currentId = queue.shift()!;\n\n if (visited.has(currentId)) {\n continue;\n }\n\n visited.add(currentId);\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const relationships = await this.documentIndexer.getOutgoing(\n currentId,\n [\"child\"],\n undefined,\n undefined,\n signal,\n );\n\n for (const rel of relationships.results) {\n if (!visited.has(rel.targetId)) {\n descendants.push(rel.targetId);\n queue.push(rel.targetId);\n }\n }\n }\n\n for (const descendantId of descendants) {\n const removalJobs = await this.removeFromAllParents(\n descendantId,\n signal,\n );\n jobs.push(...removalJobs);\n\n const jobInfo = await this.reactor.deleteDocument(\n descendantId,\n this.signer,\n signal,\n );\n jobs.push(jobInfo);\n }\n }\n\n const removalJobs = await this.removeFromAllParents(identifier, signal);\n jobs.push(...removalJobs);\n\n const jobInfo = await this.reactor.deleteDocument(\n identifier,\n this.signer,\n signal,\n );\n jobs.push(jobInfo);\n\n const completedJobs = await Promise.all(\n jobs.map((job) => this.waitForJob(job, signal)),\n );\n\n for (const completedJob of completedJobs) {\n if (completedJob.status === JobStatus.FAILED) {\n throw new Error(completedJob.error?.message);\n }\n }\n }\n\n /**\n * Deletes documents and waits for completion\n */\n async deleteDocuments(\n identifiers: string[],\n propagate?: PropagationMode,\n signal?: AbortSignal,\n ): Promise<void> {\n this.logger.verbose(\n \"deleteDocuments(@count identifiers, @propagate)\",\n identifiers.length,\n propagate,\n );\n const deletePromises = identifiers.map((identifier) =>\n this.deleteDocument(identifier, propagate, signal),\n );\n\n await Promise.all(deletePromises);\n }\n\n /**\n * Retrieves the status of a job\n */\n async getJobStatus(jobId: string, signal?: AbortSignal): Promise<JobInfo> {\n this.logger.verbose(\"getJobStatus(@jobId)\", jobId);\n return this.reactor.getJobStatus(jobId, signal);\n }\n\n /**\n * Waits for a job to complete\n */\n async waitForJob(\n jobId: string | JobInfo,\n signal?: AbortSignal,\n ): Promise<JobInfo> {\n const id = typeof jobId === \"string\" ? jobId : jobId.id;\n this.logger.verbose(\"waitForJob(@id)\", id);\n return this.jobAwaiter.waitForJob(id, signal);\n }\n\n /**\n * Subscribes to changes for documents matching specified filters\n */\n subscribe(\n search: SearchFilter,\n callback: (event: DocumentChangeEvent) => void,\n view?: ViewFilter,\n ): () => void {\n this.logger.verbose(\"subscribe(@search, @view)\", search, view);\n const unsubscribeCreated = this.subscriptionManager.onDocumentCreated(\n (result) => {\n void (async () => {\n try {\n const documents = await Promise.all(\n result.results.map((id) =>\n this.reactor.get(id, view, undefined, undefined),\n ),\n );\n\n callback({\n type: DocumentChangeType.Created,\n documents,\n });\n } catch {\n // Silently ignore errors when fetching created documents\n }\n })();\n },\n search,\n );\n\n const unsubscribeDeleted = this.subscriptionManager.onDocumentDeleted(\n (documentIds) => {\n callback({\n type: DocumentChangeType.Deleted,\n documents: [],\n context: { childId: documentIds[0] },\n });\n },\n search,\n );\n\n const unsubscribeUpdated = this.subscriptionManager.onDocumentStateUpdated(\n (result) => {\n callback({\n type: DocumentChangeType.Updated,\n documents: result.results,\n });\n },\n search,\n view,\n );\n\n const unsubscribeRelationship =\n this.subscriptionManager.onRelationshipChanged(\n (parentId, childId, changeType) => {\n callback({\n type:\n changeType === RelationshipChangeType.Added\n ? DocumentChangeType.ChildAdded\n : DocumentChangeType.ChildRemoved,\n documents: [],\n context: {\n parentId,\n childId,\n },\n });\n },\n search,\n );\n\n return () => {\n unsubscribeCreated();\n unsubscribeDeleted();\n unsubscribeUpdated();\n unsubscribeRelationship();\n };\n }\n\n private async removeFromAllParents(\n documentId: string,\n signal?: AbortSignal,\n ): Promise<JobInfo[]> {\n const incoming = await this.documentIndexer.getIncoming(\n documentId,\n undefined,\n undefined,\n undefined,\n signal,\n );\n\n const jobs: JobInfo[] = [];\n\n for (const rel of incoming.results) {\n const parentId = rel.sourceId;\n\n let parentDoc: PHDocument;\n try {\n parentDoc = await this.reactor.get(\n parentId,\n undefined,\n undefined,\n signal,\n );\n } catch {\n continue;\n }\n\n const isDrive =\n parentDoc.header.documentType === \"powerhouse/document-drive\";\n\n const relationshipActions: Action[] = await signActions(\n [removeRelationshipAction(parentId, documentId, rel.relationshipType)],\n this.signer,\n signal,\n );\n\n if (isDrive) {\n const driveActions: Action[] = await signActions(\n [deleteNode({ id: documentId })],\n this.signer,\n signal,\n );\n\n const batchResult = await this.reactor.executeBatch(\n {\n jobs: [\n {\n key: \"relationship\",\n documentId: parentId,\n scope: getSharedActionScope(relationshipActions),\n branch: \"main\",\n actions: relationshipActions,\n dependsOn: [],\n },\n {\n key: \"drive\",\n documentId: parentId,\n scope: getSharedActionScope(driveActions),\n branch: \"main\",\n actions: driveActions,\n dependsOn: [\"relationship\"],\n },\n ],\n },\n signal,\n );\n\n jobs.push(...Object.values(batchResult.jobs));\n } else {\n const jobInfo = await this.reactor.removeChildren(\n parentId,\n [documentId],\n \"main\",\n this.signer,\n signal,\n );\n jobs.push(jobInfo);\n }\n }\n\n return jobs;\n }\n}\n","import type { IOperationIndex } from \"./operation-index-types.js\";\n\nexport interface ICollectionMembershipCache {\n // Get collections for documents (lazy load from index if not cached)\n getCollectionsForDocuments(\n documentIds: string[],\n ): Promise<Record<string, string[]>>;\n\n // Invalidate a document's cache entry (when membership changes)\n invalidate(documentId: string): void;\n}\n\nexport class CollectionMembershipCache implements ICollectionMembershipCache {\n private cache: Map<string, string[]> = new Map();\n\n constructor(private operationIndex: IOperationIndex) {}\n\n withScopedIndex(operationIndex: IOperationIndex): CollectionMembershipCache {\n const scoped = new CollectionMembershipCache(operationIndex);\n scoped.cache = this.cache;\n return scoped;\n }\n\n async getCollectionsForDocuments(\n documentIds: string[],\n ): Promise<Record<string, string[]>> {\n const result: Record<string, string[]> = {};\n const missing: string[] = [];\n\n for (const docId of documentIds) {\n const cached = this.cache.get(docId);\n if (cached !== undefined) {\n result[docId] = cached;\n } else {\n missing.push(docId);\n }\n }\n\n if (missing.length > 0) {\n const fromDb =\n await this.operationIndex.getCollectionsForDocuments(missing);\n for (const docId of missing) {\n const collections = fromDb[docId] ?? [];\n result[docId] = collections;\n this.cache.set(docId, collections);\n }\n }\n\n return result;\n }\n\n invalidate(documentId: string): void {\n this.cache.delete(documentId);\n }\n}\n","/**\n * Error thrown when attempting to access a deleted document.\n */\nexport class DocumentDeletedError extends Error {\n public readonly documentId: string;\n public readonly deletedAtUtcIso: string | null;\n\n constructor(documentId: string, deletedAtUtcIso: string | null = null) {\n const message = deletedAtUtcIso\n ? `Document ${documentId} was deleted at ${deletedAtUtcIso}`\n : `Document ${documentId} has been deleted`;\n\n super(message);\n this.name = \"DocumentDeletedError\";\n this.documentId = documentId;\n this.deletedAtUtcIso = deletedAtUtcIso;\n\n Error.captureStackTrace(this, DocumentDeletedError);\n }\n\n static isError(error: unknown): error is DocumentDeletedError {\n return Error.isError(error) && error.name === \"DocumentDeletedError\";\n }\n}\n\n/**\n * Error thrown when attempting to add operations before CREATE_DOCUMENT.\n */\nexport class CreateDocumentRequiredError extends Error {\n public readonly documentId: string;\n public readonly scope: string;\n\n constructor(documentId: string, scope: string) {\n const message = `Document ${documentId} requires a CREATE_DOCUMENT operation at revision 0 in the \"document\" scope before operations can be added to scope \"${scope}\"`;\n\n super(message);\n this.name = \"CreateDocumentRequiredError\";\n this.documentId = documentId;\n this.scope = scope;\n\n Error.captureStackTrace(this, CreateDocumentRequiredError);\n }\n}\n\n/**\n * Error thrown when an operation has an invalid signature.\n */\nexport class InvalidSignatureError extends Error {\n public readonly documentId: string;\n public readonly reason: string;\n\n constructor(documentId: string, reason: string) {\n super(`Invalid signature in document ${documentId}: ${reason}`);\n this.name = \"InvalidSignatureError\";\n this.documentId = documentId;\n this.reason = reason;\n\n Error.captureStackTrace(this, InvalidSignatureError);\n }\n}\n\n/**\n * Error thrown when attempting to downgrade a document version.\n */\nexport class DowngradeNotSupportedError extends Error {\n public readonly documentType: string;\n public readonly fromVersion: number;\n public readonly toVersion: number;\n\n constructor(documentType: string, fromVersion: number, toVersion: number) {\n super(\n `Downgrade not supported for ${documentType}: cannot upgrade from version ${fromVersion} to ${toVersion}`,\n );\n this.name = \"DowngradeNotSupportedError\";\n this.documentType = documentType;\n this.fromVersion = fromVersion;\n this.toVersion = toVersion;\n\n Error.captureStackTrace(this, DowngradeNotSupportedError);\n }\n}\n\n/**\n * Error thrown when an upgrade manifest is required but not registered.\n */\nexport class UpgradeManifestNotFoundError extends Error {\n public readonly documentType: string;\n\n constructor(documentType: string) {\n super(`No upgrade manifest registered for document type: ${documentType}`);\n this.name = \"UpgradeManifestNotFoundError\";\n this.documentType = documentType;\n\n Error.captureStackTrace(this, UpgradeManifestNotFoundError);\n }\n}\n\n/**\n * Error thrown when a document is not found (no operations exist for the document ID).\n */\nexport class DocumentNotFoundError extends Error {\n public readonly documentId: string;\n\n constructor(documentId: string) {\n super(`Document ${documentId} not found`);\n this.name = \"DocumentNotFoundError\";\n this.documentId = documentId;\n\n Error.captureStackTrace(this, DocumentNotFoundError);\n }\n\n static isError(error: unknown): error is DocumentNotFoundError {\n return Error.isError(error) && error.name === \"DocumentNotFoundError\";\n }\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type {\n Action,\n CreateDocumentAction,\n CreateDocumentActionInput,\n DeleteDocumentAction,\n Operation,\n PHDocument,\n UpgradeDocumentAction,\n UpgradeTransition,\n} from \"@powerhousedao/shared/document-model\";\nimport {\n createPresignedHeader,\n defaultBaseState,\n deriveOperationId,\n} from \"@powerhousedao/shared/document-model\";\nimport type { Job } from \"../queue/types.js\";\nimport { DowngradeNotSupportedError } from \"../shared/errors.js\";\nimport type {\n ConsistencyCoordinate,\n ConsistencyToken,\n} from \"../shared/types.js\";\nimport type { JobResult } from \"./types.js\";\n\n/**\n * Creates a PHDocument from a CREATE_DOCUMENT action input.\n * Reconstructs the document header and initializes the base state.\n *\n * @param action - The CREATE_DOCUMENT action containing the document parameters\n * @returns A newly constructed PHDocument with initialized header and base state\n */\nexport function createDocumentFromAction(\n action: CreateDocumentAction,\n): PHDocument {\n const input = action.input as CreateDocumentActionInput;\n\n // Reconstruct the document from CreateDocumentActionInput\n const header = createPresignedHeader();\n header.id = input.documentId;\n header.documentType = input.model;\n\n // If signing info is present, populate the header signature fields\n if (input.signing) {\n header.createdAtUtcIso = input.signing.createdAtUtcIso;\n header.lastModifiedAtUtcIso = input.signing.createdAtUtcIso;\n header.sig = {\n publicKey: input.signing.publicKey,\n nonce: input.signing.nonce,\n };\n }\n\n // Populate optional mutable header fields\n if (input.slug !== undefined) {\n header.slug = input.slug;\n }\n // Default slug to document ID if empty (matching legacy behavior)\n if (!header.slug) {\n header.slug = input.documentId;\n }\n if (input.name !== undefined) {\n header.name = input.name;\n }\n if (input.branch !== undefined) {\n header.branch = input.branch;\n }\n if (input.meta !== undefined) {\n header.meta = input.meta;\n }\n if (input.protocolVersions !== undefined) {\n header.protocolVersions = input.protocolVersions;\n }\n\n // Construct the document with default base state (UPGRADE_DOCUMENT will set the full state)\n const baseState = defaultBaseState();\n const document: PHDocument = {\n header,\n operations: {},\n state: baseState,\n initialState: baseState,\n clipboard: [],\n };\n\n return document;\n}\n\n/**\n * Applies an UPGRADE_DOCUMENT action to a document.\n * Handles all upgrade scenarios including initial upgrades, no-ops, and multi-step upgrades.\n *\n * Behavior based on fromVersion/toVersion:\n * - fromVersion === toVersion (and fromVersion > 0): No-op - return unchanged document\n * - fromVersion > toVersion: Throw DowngradeNotSupportedError\n * - All other cases: Apply upgradePath transitions (if provided), then apply initialState, set version\n *\n * The initialState from the action is always applied (if provided) to maintain backward\n * compatibility with the original implementation.\n *\n * @param document - The document to upgrade\n * @param action - The UPGRADE_DOCUMENT action\n * @param upgradePath - Optional pre-computed upgrade path for multi-step upgrades\n * @returns The upgraded document (unchanged if no-op)\n * @throws DowngradeNotSupportedError if attempting to downgrade\n */\nexport function applyUpgradeDocumentAction(\n document: PHDocument,\n action: UpgradeDocumentAction,\n upgradePath?: UpgradeTransition[],\n): PHDocument {\n const fromVersion = action.input.fromVersion;\n const toVersion = action.input.toVersion;\n\n if (fromVersion === toVersion && fromVersion > 0) {\n return document;\n }\n\n if (fromVersion > toVersion) {\n throw new DowngradeNotSupportedError(\n document.header.documentType,\n fromVersion,\n toVersion,\n );\n }\n\n if (upgradePath) {\n for (const transition of upgradePath) {\n document = transition.upgradeReducer(document, action);\n }\n }\n\n applyInitialState(document, action);\n\n document.state.document = {\n ...document.state.document,\n version: toVersion,\n };\n return document;\n}\n\nfunction applyInitialState(\n document: PHDocument,\n action: UpgradeDocumentAction,\n): void {\n const input = action.input as {\n initialState?: PHDocument[\"state\"];\n state?: PHDocument[\"state\"];\n };\n\n const newState = input.initialState || input.state;\n if (newState) {\n document.state = { ...document.state, ...newState };\n document.initialState = document.state;\n }\n}\n\n/**\n * Applies a DELETE_DOCUMENT action to a document.\n * Marks the document as deleted in the document scope state.\n *\n * @param document - The document to mark as deleted\n * @param action - The DELETE_DOCUMENT action\n * @returns The updated document (mutates in place and returns for convenience)\n */\nexport function applyDeleteDocumentAction(\n document: PHDocument,\n action: DeleteDocumentAction,\n): PHDocument {\n const deletedAt = action.timestampUtcMs || new Date().toISOString();\n\n document.state = {\n ...document.state,\n document: {\n ...document.state.document,\n isDeleted: true,\n deletedAtUtcIso: deletedAt,\n },\n };\n\n return document;\n}\n\n/**\n * Calculate the next operation index for a specific scope.\n * Each scope maintains its own independent index sequence.\n *\n * Per-scope indexing means:\n * - Each scope (document, global, local, etc.) has independent indexes\n * - Indexes start at 0 for each scope\n * - Different scopes can have operations with the same index value\n *\n * This function uses header.revision which is populated by the cache/storage layer\n * and contains the next available index for each scope. This design avoids requiring\n * the full operation history to be loaded, which is crucial for snapshot-based caching.\n *\n * @param document - The document whose header.revision to inspect\n * @param scope - The scope to calculate the next index for\n * @returns The next available index in the specified scope\n */\nexport const getNextIndexForScope = (\n document: PHDocument,\n scope: string,\n): number => {\n return document.header.revision[scope] || 0;\n};\n\n/**\n * Creates an empty consistency token with no coordinates.\n * Used when a job is registered or fails without writing operations.\n *\n * @returns A consistency token with an empty coordinates array\n */\nexport function createEmptyConsistencyToken(): ConsistencyToken {\n return {\n version: 1,\n createdAtUtcIso: new Date().toISOString(),\n coordinates: [],\n };\n}\n\n/**\n * Creates a consistency token from operations written during job execution.\n * Maps each operation to a consistency coordinate tracking (documentId, scope, branch, operationIndex).\n * If no operations are provided, returns an empty token.\n *\n * @param operationsWithContext - Array of operations with their execution context\n * @returns A consistency token representing all operations written\n */\nexport function createConsistencyToken(\n operationsWithContext: OperationWithContext[],\n): ConsistencyToken {\n if (operationsWithContext.length === 0) {\n return createEmptyConsistencyToken();\n }\n\n const coordinates: ConsistencyCoordinate[] = [];\n for (let i = 0; i < operationsWithContext.length; i++) {\n const opWithContext = operationsWithContext[i]!;\n coordinates.push({\n documentId: opWithContext.context.documentId,\n scope: opWithContext.context.scope,\n branch: opWithContext.context.branch,\n operationIndex: opWithContext.operation.index,\n });\n }\n\n return {\n version: 1,\n createdAtUtcIso: new Date().toISOString(),\n coordinates,\n };\n}\n\nexport function createOperation(\n action: Action,\n index: number,\n skip: number,\n context: { documentId: string; scope: string; branch: string },\n): Operation {\n const id = deriveOperationId(\n context.documentId,\n context.scope,\n context.branch,\n action.id,\n );\n\n return {\n id,\n index: index,\n timestampUtcMs: action.timestampUtcMs || new Date().toISOString(),\n hash: \"\",\n skip: skip,\n action: action,\n };\n}\n\nexport function updateDocumentRevision(\n document: PHDocument,\n scope: string,\n operationIndex: number,\n): void {\n document.header.revision = {\n ...document.header.revision,\n [scope]: operationIndex + 1,\n };\n}\n\nexport function buildSuccessResult(\n job: Job,\n operation: Operation,\n documentId: string,\n documentType: string,\n resultingState: string,\n startTime: number,\n): JobResult {\n return {\n job,\n success: true,\n operations: [operation],\n operationsWithContext: [\n {\n operation,\n context: {\n documentId: documentId,\n scope: job.scope,\n branch: job.branch,\n documentType: documentType,\n resultingState,\n ordinal: 0,\n },\n },\n ],\n duration: Date.now() - startTime,\n };\n}\n\nexport function buildErrorResult(\n job: Job,\n error: Error,\n startTime: number,\n): JobResult {\n return {\n job,\n success: false,\n error: error,\n duration: Date.now() - startTime,\n };\n}\n","class LRUNode<K> {\n key: K;\n prev: LRUNode<K> | undefined;\n next: LRUNode<K> | undefined;\n\n constructor(key: K) {\n this.key = key;\n this.prev = undefined;\n this.next = undefined;\n }\n}\n\nexport class LRUTracker<K> {\n private map: Map<K, LRUNode<K>>;\n private head: LRUNode<K> | undefined;\n private tail: LRUNode<K> | undefined;\n\n constructor() {\n this.map = new Map();\n this.head = undefined;\n this.tail = undefined;\n }\n\n get size(): number {\n return this.map.size;\n }\n\n touch(key: K): void {\n const node = this.map.get(key);\n\n if (node) {\n this.moveToFront(node);\n } else {\n this.addToFront(key);\n }\n }\n\n evict(): K | undefined {\n if (!this.tail) {\n return undefined;\n }\n\n const key = this.tail.key;\n this.remove(key);\n return key;\n }\n\n remove(key: K): void {\n const node = this.map.get(key);\n if (!node) {\n return;\n }\n\n this.removeNode(node);\n this.map.delete(key);\n }\n\n clear(): void {\n this.map.clear();\n this.head = undefined;\n this.tail = undefined;\n }\n\n private addToFront(key: K): void {\n const node = new LRUNode(key);\n this.map.set(key, node);\n\n if (!this.head) {\n this.head = node;\n this.tail = node;\n } else {\n node.next = this.head;\n this.head.prev = node;\n this.head = node;\n }\n }\n\n private moveToFront(node: LRUNode<K>): void {\n if (node === this.head) {\n return;\n }\n\n this.removeNode(node);\n node.prev = undefined;\n node.next = this.head;\n\n if (this.head) {\n this.head.prev = node;\n }\n\n this.head = node;\n\n if (!this.tail) {\n this.tail = node;\n }\n }\n\n private removeNode(node: LRUNode<K>): void {\n if (node.prev) {\n node.prev.next = node.next;\n } else {\n this.head = node.next;\n }\n\n if (node.next) {\n node.next.prev = node.prev;\n } else {\n this.tail = node.prev;\n }\n }\n}\n","import type {\n CreateDocumentAction,\n DeleteDocumentAction,\n UpgradeDocumentAction,\n} from \"@powerhousedao/shared/document-model\";\nimport {\n applyDeleteDocumentAction,\n applyUpgradeDocumentAction,\n createDocumentFromAction,\n} from \"../executor/util.js\";\nimport { DocumentNotFoundError } from \"../shared/errors.js\";\nimport type { IOperationStore } from \"../storage/interfaces.js\";\nimport type {\n CachedDocumentMeta,\n DocumentMetaCacheConfig,\n IDocumentMetaCache,\n} from \"./document-meta-cache-types.js\";\nimport { LRUTracker } from \"./lru/lru-tracker.js\";\n\n/**\n * In-memory document metadata cache with LRU eviction.\n *\n * Caches PHDocumentState per (documentId, branch) key. On cache miss,\n * rebuilds from document scope operations. Provides an explicit cross-scope\n * contract for accessing document scope metadata.\n *\n * **Thread Safety:**\n * Not thread-safe. Designed for single-threaded job executor environment.\n */\nexport class DocumentMetaCache implements IDocumentMetaCache {\n private cache: Map<string, CachedDocumentMeta>;\n private lruTracker: LRUTracker<string>;\n private operationStore: IOperationStore;\n private config: DocumentMetaCacheConfig;\n\n constructor(\n operationStore: IOperationStore,\n config: DocumentMetaCacheConfig,\n ) {\n this.operationStore = operationStore;\n this.config = {\n maxDocuments: config.maxDocuments,\n };\n this.cache = new Map();\n this.lruTracker = new LRUTracker<string>();\n }\n\n withScopedStore(operationStore: IOperationStore): DocumentMetaCache {\n const scoped = new DocumentMetaCache(operationStore, this.config);\n scoped.cache = this.cache;\n scoped.lruTracker = this.lruTracker;\n return scoped;\n }\n\n async startup(): Promise<void> {\n return Promise.resolve();\n }\n\n async shutdown(): Promise<void> {\n return Promise.resolve();\n }\n\n async getDocumentMeta(\n documentId: string,\n branch: string,\n signal?: AbortSignal,\n ): Promise<CachedDocumentMeta> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const key = this.makeKey(documentId, branch);\n const cached = this.cache.get(key);\n\n if (cached) {\n this.lruTracker.touch(key);\n return cached;\n }\n\n const meta = await this.rebuildLatest(documentId, branch, signal);\n this.putDocumentMeta(documentId, branch, meta);\n return meta;\n }\n\n async rebuildAtRevision(\n documentId: string,\n branch: string,\n targetRevision: number,\n signal?: AbortSignal,\n ): Promise<CachedDocumentMeta> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n return this.rebuildFromOperations(\n documentId,\n branch,\n targetRevision,\n signal,\n );\n }\n\n putDocumentMeta(\n documentId: string,\n branch: string,\n meta: CachedDocumentMeta,\n ): void {\n const key = this.makeKey(documentId, branch);\n\n if (!this.cache.has(key) && this.cache.size >= this.config.maxDocuments) {\n const evictKey = this.lruTracker.evict();\n if (evictKey) {\n this.cache.delete(evictKey);\n }\n }\n\n this.cache.set(key, structuredClone(meta));\n this.lruTracker.touch(key);\n }\n\n invalidate(documentId: string, branch?: string): number {\n let evicted = 0;\n\n if (branch === undefined) {\n for (const key of this.cache.keys()) {\n if (key.startsWith(`${documentId}:`)) {\n this.cache.delete(key);\n this.lruTracker.remove(key);\n evicted++;\n }\n }\n } else {\n const key = this.makeKey(documentId, branch);\n if (this.cache.has(key)) {\n this.cache.delete(key);\n this.lruTracker.remove(key);\n evicted = 1;\n }\n }\n\n return evicted;\n }\n\n clear(): void {\n this.cache.clear();\n this.lruTracker.clear();\n }\n\n private makeKey(documentId: string, branch: string): string {\n return `${documentId}:${branch}`;\n }\n\n private async rebuildLatest(\n documentId: string,\n branch: string,\n signal?: AbortSignal,\n ): Promise<CachedDocumentMeta> {\n return this.rebuildFromOperations(documentId, branch, undefined, signal);\n }\n\n private async rebuildFromOperations(\n documentId: string,\n branch: string,\n targetRevision: number | undefined,\n signal?: AbortSignal,\n ): Promise<CachedDocumentMeta> {\n const docScopeOps = await this.operationStore.getSince(\n documentId,\n \"document\",\n branch,\n -1,\n undefined,\n undefined,\n signal,\n );\n\n if (docScopeOps.results.length === 0) {\n throw new DocumentNotFoundError(documentId);\n }\n\n const createOp = docScopeOps.results[0];\n if (createOp.action.type !== \"CREATE_DOCUMENT\") {\n throw new Error(\n `Invalid document: first operation must be CREATE_DOCUMENT, found ${createOp.action.type}`,\n );\n }\n\n const createAction = createOp.action as CreateDocumentAction;\n const documentType = createAction.input.model;\n\n let document = createDocumentFromAction(createAction);\n let documentScopeRevision = 0;\n\n for (const op of docScopeOps.results) {\n if (targetRevision !== undefined && op.index > targetRevision) {\n break;\n }\n\n documentScopeRevision = op.index;\n\n if (op.action.type === \"UPGRADE_DOCUMENT\") {\n const upgradeAction = op.action as UpgradeDocumentAction;\n document = applyUpgradeDocumentAction(document, upgradeAction);\n } else if (op.action.type === \"DELETE_DOCUMENT\") {\n document = applyDeleteDocumentAction(\n document,\n op.action as DeleteDocumentAction,\n );\n }\n\n // for now, we are skipping relationship operations\n }\n\n return {\n state: document.state.document,\n documentType,\n documentScopeRevision: documentScopeRevision + 1,\n };\n }\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { Kysely, Transaction } from \"kysely\";\nimport { sql } from \"kysely\";\nimport type { PagedResults, PagingOptions } from \"../shared/types.js\";\nimport type { ViewFilter } from \"../storage/interfaces.js\";\nimport type { Database } from \"../storage/kysely/types.js\";\nimport type {\n InsertableDocumentCollection,\n InsertableOperationIndexOperation,\n IOperationIndex,\n IOperationIndexTxn,\n OperationIndexEntry,\n OperationIndexOperationRow,\n} from \"./operation-index-types.js\";\n\ntype CollectionMembershipRecord = {\n collectionId: string;\n documentId: string;\n\n // this is NOT operation.index -- it is the index of the operation in the\n // operations array\n operationIndex: number;\n};\n\nclass KyselyOperationIndexTxn implements IOperationIndexTxn {\n private collections: string[] = [];\n private collectionMemberships: CollectionMembershipRecord[] = [];\n private collectionRemovals: CollectionMembershipRecord[] = [];\n private operations: OperationIndexEntry[] = [];\n\n createCollection(collectionId: string): void {\n this.collections.push(collectionId);\n }\n\n addToCollection(collectionId: string, documentId: string): void {\n const lastOpIndex = this.operations.length - 1;\n if (lastOpIndex < 0) {\n throw new Error(\n \"addToCollection must be called after write() - no operations in transaction\",\n );\n }\n this.collectionMemberships.push({\n collectionId,\n documentId,\n operationIndex: lastOpIndex,\n });\n }\n\n removeFromCollection(collectionId: string, documentId: string): void {\n const lastOpIndex = this.operations.length - 1;\n if (lastOpIndex < 0) {\n throw new Error(\n \"removeFromCollection must be called after write() - no operations in transaction\",\n );\n }\n this.collectionRemovals.push({\n collectionId,\n documentId,\n operationIndex: lastOpIndex,\n });\n }\n\n write(operations: OperationIndexEntry[]): void {\n this.operations.push(...operations);\n }\n\n getCollections(): string[] {\n return this.collections;\n }\n\n getCollectionMembershipRecords(): CollectionMembershipRecord[] {\n return this.collectionMemberships;\n }\n\n getCollectionRemovals(): CollectionMembershipRecord[] {\n return this.collectionRemovals;\n }\n\n getOperations(): OperationIndexEntry[] {\n return this.operations;\n }\n}\n\nexport class KyselyOperationIndex implements IOperationIndex {\n private trx?: Transaction<Database>;\n\n constructor(private db: Kysely<Database>) {}\n\n private get queryExecutor(): Kysely<Database> | Transaction<Database> {\n return this.trx ?? this.db;\n }\n\n withTransaction(trx: Transaction<Database>): KyselyOperationIndex {\n const instance = new KyselyOperationIndex(this.db);\n instance.trx = trx;\n return instance;\n }\n\n start(): IOperationIndexTxn {\n return new KyselyOperationIndexTxn();\n }\n\n async commit(\n txn: IOperationIndexTxn,\n signal?: AbortSignal,\n ): Promise<number[]> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const kyselyTxn = txn as KyselyOperationIndexTxn;\n\n if (this.trx) {\n return this.executeCommit(this.trx, kyselyTxn);\n }\n\n let resultOrdinals: number[] = [];\n await this.db.transaction().execute(async (trx) => {\n resultOrdinals = await this.executeCommit(trx, kyselyTxn);\n });\n return resultOrdinals;\n }\n\n private async executeCommit(\n trx: Transaction<Database>,\n kyselyTxn: KyselyOperationIndexTxn,\n ): Promise<number[]> {\n const collections = kyselyTxn.getCollections();\n const memberships = kyselyTxn.getCollectionMembershipRecords();\n const removals = kyselyTxn.getCollectionRemovals();\n const operations = kyselyTxn.getOperations();\n\n if (collections.length > 0) {\n const collectionRows: InsertableDocumentCollection[] = collections.map(\n (collectionId) => ({\n documentId: collectionId,\n collectionId,\n joinedOrdinal: BigInt(0),\n leftOrdinal: null,\n }),\n );\n\n await trx\n .insertInto(\"document_collections\")\n .values(collectionRows)\n .onConflict((oc) => oc.doNothing())\n .execute();\n }\n\n let operationOrdinals: number[] = [];\n if (operations.length > 0) {\n const operationRows: InsertableOperationIndexOperation[] = operations.map(\n (op) => ({\n opId: op.id || \"\",\n documentId: op.documentId,\n documentType: op.documentType,\n scope: op.scope,\n branch: op.branch,\n timestampUtcMs: op.timestampUtcMs,\n index: op.index,\n skip: op.skip,\n hash: op.hash,\n action: op.action as unknown,\n sourceRemote: op.sourceRemote,\n }),\n );\n\n const insertedOps = await trx\n .insertInto(\"operation_index_operations\")\n .values(operationRows)\n .returning(\"ordinal\")\n .execute();\n\n operationOrdinals = insertedOps.map((row) => row.ordinal);\n }\n\n if (memberships.length > 0) {\n for (const m of memberships) {\n const ordinal = operationOrdinals[m.operationIndex];\n\n await trx\n .insertInto(\"document_collections\")\n .values({\n documentId: m.documentId,\n collectionId: m.collectionId,\n joinedOrdinal: BigInt(ordinal),\n leftOrdinal: null,\n })\n .onConflict((oc) =>\n oc.columns([\"documentId\", \"collectionId\"]).doUpdateSet({\n joinedOrdinal: BigInt(ordinal),\n leftOrdinal: null,\n }),\n )\n .execute();\n }\n }\n\n if (removals.length > 0) {\n for (const r of removals) {\n const ordinal = operationOrdinals[r.operationIndex];\n\n await trx\n .updateTable(\"document_collections\")\n .set({\n leftOrdinal: BigInt(ordinal),\n })\n .where(\"collectionId\", \"=\", r.collectionId)\n .where(\"documentId\", \"=\", r.documentId)\n .where(\"leftOrdinal\", \"is\", null)\n .execute();\n }\n }\n\n return operationOrdinals;\n }\n\n async find(\n collectionId: string,\n cursor?: number,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<OperationIndexEntry>> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n let query = this.queryExecutor\n .selectFrom(\"operation_index_operations as oi\")\n .innerJoin(\"document_collections as dc\", \"oi.documentId\", \"dc.documentId\")\n .selectAll(\"oi\")\n .select([\"dc.documentId\", \"dc.collectionId\"])\n .where(\"dc.collectionId\", \"=\", collectionId)\n .where(\n sql<boolean>`(dc.\"leftOrdinal\" IS NULL OR oi.ordinal < dc.\"leftOrdinal\")`,\n )\n .orderBy(\"oi.ordinal\", \"asc\");\n\n if (cursor !== undefined) {\n query = query.where(\"oi.ordinal\", \">\", cursor);\n }\n\n if (view?.branch) {\n query = query.where(\"oi.branch\", \"=\", view.branch);\n }\n\n if (view?.scopes && view.scopes.length > 0) {\n query = query.where(\"oi.scope\", \"in\", view.scopes);\n }\n\n if (view?.excludeSourceRemote) {\n query = query.where(\"oi.sourceRemote\", \"!=\", view.excludeSourceRemote);\n }\n\n if (paging?.cursor) {\n const cursorOrdinal = Number.parseInt(paging.cursor, 10);\n query = query.where(\"oi.ordinal\", \">\", cursorOrdinal);\n }\n\n if (paging?.limit) {\n query = query.limit(paging.limit + 1);\n }\n\n const rows = await query.execute();\n\n let hasMore = false;\n let items = rows;\n\n if (paging?.limit && rows.length > paging.limit) {\n hasMore = true;\n items = rows.slice(0, paging.limit);\n }\n\n const nextCursor =\n hasMore && items.length > 0\n ? items[items.length - 1].ordinal.toString()\n : undefined;\n\n const cursorValue = paging?.cursor || \"0\";\n const limit = paging?.limit || 100;\n const entries = items.map((row) => this.rowToOperationIndexEntry(row));\n\n return {\n results: entries,\n options: { cursor: cursorValue, limit },\n nextCursor,\n next: hasMore\n ? () =>\n this.find(\n collectionId,\n cursor,\n view,\n { cursor: nextCursor!, limit },\n signal,\n )\n : undefined,\n };\n }\n\n async get(\n documentId: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<OperationIndexEntry>> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n let query = this.queryExecutor\n .selectFrom(\"operation_index_operations\")\n .selectAll()\n .where(\"documentId\", \"=\", documentId)\n .orderBy(\"ordinal\", \"asc\");\n\n if (view?.branch) {\n query = query.where(\"branch\", \"=\", view.branch);\n }\n\n if (view?.scopes && view.scopes.length > 0) {\n query = query.where(\"scope\", \"in\", view.scopes);\n }\n\n if (paging?.cursor) {\n const cursorOrdinal = Number.parseInt(paging.cursor, 10);\n query = query.where(\"ordinal\", \">\", cursorOrdinal);\n }\n\n if (paging?.limit) {\n query = query.limit(paging.limit + 1);\n }\n\n const rows = await query.execute();\n\n let hasMore = false;\n let items = rows;\n\n if (paging?.limit && rows.length > paging.limit) {\n hasMore = true;\n items = rows.slice(0, paging.limit);\n }\n\n const nextCursor =\n hasMore && items.length > 0\n ? items[items.length - 1].ordinal.toString()\n : undefined;\n\n const cursorValue = paging?.cursor || \"0\";\n const limit = paging?.limit || 100;\n const entries = items.map((row) => this.rowToOperationIndexEntry(row));\n\n return {\n results: entries,\n options: { cursor: cursorValue, limit },\n nextCursor,\n next: hasMore\n ? () =>\n this.get(documentId, view, { cursor: nextCursor!, limit }, signal)\n : undefined,\n };\n }\n\n async getSinceOrdinal(\n ordinal: number,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<OperationWithContext>> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n let query = this.queryExecutor\n .selectFrom(\"operation_index_operations\")\n .selectAll()\n .where(\"ordinal\", \">\", ordinal)\n .orderBy(\"ordinal\", \"asc\");\n\n if (paging?.cursor) {\n const cursorOrdinal = Number.parseInt(paging.cursor, 10);\n query = query.where(\"ordinal\", \">\", cursorOrdinal);\n }\n\n if (paging?.limit) {\n query = query.limit(paging.limit + 1);\n }\n\n const rows = await query.execute();\n\n let hasMore = false;\n let items = rows;\n\n if (paging?.limit && rows.length > paging.limit) {\n hasMore = true;\n items = rows.slice(0, paging.limit);\n }\n\n const nextCursor =\n hasMore && items.length > 0\n ? items[items.length - 1].ordinal.toString()\n : undefined;\n\n const cursorValue = paging?.cursor || \"0\";\n const limit = paging?.limit || 100;\n const operations = items.map((row) => this.rowToOperationWithContext(row));\n\n return {\n results: operations,\n options: { cursor: cursorValue, limit },\n nextCursor,\n next: hasMore\n ? () =>\n this.getSinceOrdinal(\n ordinal,\n { cursor: nextCursor!, limit },\n signal,\n )\n : undefined,\n };\n }\n\n private rowToOperationWithContext(\n row: OperationIndexOperationRow,\n ): OperationWithContext {\n return {\n operation: {\n index: row.index,\n timestampUtcMs: row.timestampUtcMs,\n hash: row.hash,\n skip: row.skip,\n action: row.action as OperationWithContext[\"operation\"][\"action\"],\n id: row.opId,\n },\n context: {\n documentId: row.documentId,\n documentType: row.documentType,\n scope: row.scope,\n branch: row.branch,\n ordinal: row.ordinal,\n },\n };\n }\n\n private rowToOperationIndexEntry(\n row: OperationIndexOperationRow,\n ): OperationIndexEntry {\n return {\n ordinal: row.ordinal,\n documentId: row.documentId,\n documentType: row.documentType,\n branch: row.branch,\n scope: row.scope,\n index: row.index,\n timestampUtcMs: row.timestampUtcMs,\n hash: row.hash,\n skip: row.skip,\n action: row.action as OperationIndexEntry[\"action\"],\n id: row.opId,\n sourceRemote: row.sourceRemote,\n };\n }\n\n async getLatestTimestampForCollection(\n collectionId: string,\n signal?: AbortSignal,\n ): Promise<string | null> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const result = await this.queryExecutor\n .selectFrom(\"operation_index_operations as oi\")\n .innerJoin(\"document_collections as dc\", \"oi.documentId\", \"dc.documentId\")\n .select(\"oi.timestampUtcMs\")\n .where(\"dc.collectionId\", \"=\", collectionId)\n .where(\n sql<boolean>`(dc.\"leftOrdinal\" IS NULL OR oi.ordinal < dc.\"leftOrdinal\")`,\n )\n .orderBy(\"oi.ordinal\", \"desc\")\n .limit(1)\n .executeTakeFirst();\n\n return result?.timestampUtcMs ?? null;\n }\n\n async getCollectionsForDocuments(\n documentIds: string[],\n ): Promise<Record<string, string[]>> {\n if (documentIds.length === 0) {\n return {};\n }\n\n const rows = await this.queryExecutor\n .selectFrom(\"document_collections\")\n .select([\"documentId\", \"collectionId\"])\n .where(\"documentId\", \"in\", documentIds)\n .where(\"leftOrdinal\", \"is\", null)\n .execute();\n\n const result: Record<string, string[]> = {};\n for (const row of rows) {\n if (!(row.documentId in result)) {\n result[row.documentId] = [];\n }\n result[row.documentId].push(row.collectionId);\n }\n return result;\n }\n}\n","/**\n * RingBuffer is a generic circular buffer implementation that stores a fixed number\n * of items. When the buffer is full, new items overwrite the oldest items.\n *\n * This implementation maintains O(1) time complexity for push operations and provides\n * items in chronological order (oldest to newest) via getAll().\n *\n * @template T - The type of items stored in the buffer\n */\nexport class RingBuffer<T> {\n private buffer: T[];\n private head: number = 0;\n private size: number = 0;\n private capacity: number;\n\n constructor(capacity: number) {\n if (capacity <= 0) {\n throw new Error(\"Ring buffer capacity must be greater than 0\");\n }\n this.capacity = capacity;\n this.buffer = new Array<T>(capacity);\n }\n\n /**\n * Adds an item to the buffer. If the buffer is full, overwrites the oldest item.\n *\n * @param item - The item to add\n */\n push(item: T): void {\n const index = (this.head + this.size) % this.capacity;\n\n if (this.size < this.capacity) {\n this.buffer[index] = item;\n this.size++;\n } else {\n this.buffer[this.head] = item;\n this.head = (this.head + 1) % this.capacity;\n }\n }\n\n /**\n * Returns all items in the buffer in chronological order (oldest to newest).\n *\n * @returns Array of items in insertion order\n */\n getAll(): T[] {\n if (this.size === 0) {\n return [];\n }\n\n const result: T[] = [];\n for (let i = 0; i < this.size; i++) {\n const index = (this.head + i) % this.capacity;\n result.push(this.buffer[index]);\n }\n return result;\n }\n\n /**\n * Clears all items from the buffer.\n */\n clear(): void {\n this.buffer = new Array<T>(this.capacity);\n this.head = 0;\n this.size = 0;\n }\n\n /**\n * Gets the current number of items in the buffer.\n */\n get length(): number {\n return this.size;\n }\n}\n","import type {\n CreateDocumentAction,\n PHDocument,\n UpgradeDocumentAction,\n} from \"@powerhousedao/shared/document-model\";\nimport {\n applyDeleteDocumentAction,\n applyUpgradeDocumentAction,\n createDocumentFromAction,\n} from \"../executor/util.js\";\nimport type { IDocumentModelRegistry } from \"../registry/interfaces.js\";\nimport type { IKeyframeStore, IOperationStore } from \"../storage/interfaces.js\";\nimport { RingBuffer } from \"./buffer/ring-buffer.js\";\nimport { LRUTracker } from \"./lru/lru-tracker.js\";\nimport type { CachedSnapshot, WriteCacheConfig } from \"./write-cache-types.js\";\nimport type { IWriteCache } from \"./write/interfaces.js\";\n\ntype DocumentStream = {\n key: string;\n ringBuffer: RingBuffer<CachedSnapshot>;\n};\n\nfunction extractModuleVersion(doc: PHDocument): number | undefined {\n const v = (doc.state as Record<string, Record<string, unknown>>).document\n .version as number | undefined;\n return v === 0 ? undefined : v;\n}\n\n/**\n * In-memory write cache with keyframe persistence for PHDocuments.\n *\n * Caches document snapshots in ring buffers with LRU eviction. On cache miss,\n * rebuilds documents from nearest keyframe or full operation history.\n *\n * **Performance Characteristics:**\n * - Cache hit: O(1) lookup in ring buffer\n * - Cold miss: O(n) where n is total operation count, or O(k) where k is operations since keyframe\n * - Warm miss: O(m) where m is operations since cached revision\n * - Eviction: O(1) for LRU tracking and removal\n *\n * **Thread Safety:**\n * Not thread-safe. Designed for single-threaded job executor environment.\n * External synchronization required for concurrent access across multiple executors.\n *\n * **Example:**\n * ```typescript\n * const cache = new KyselyWriteCache(\n * keyframeStore,\n * operationStore,\n * registry,\n * { maxDocuments: 1000, ringBufferSize: 10, keyframeInterval: 10 }\n * );\n *\n * await cache.startup();\n *\n * // Retrieve or rebuild document\n * const doc = await cache.getState(docId, docType, scope, branch, revision);\n *\n * // Cache result after job execution\n * cache.putState(docId, docType, scope, branch, newRevision, updatedDoc);\n *\n * await cache.shutdown();\n * ```\n */\nexport class KyselyWriteCache implements IWriteCache {\n private streams: Map<string, DocumentStream>;\n private lruTracker: LRUTracker<string>;\n private keyframeStore: IKeyframeStore;\n private operationStore: IOperationStore;\n private registry: IDocumentModelRegistry;\n private config: Required<WriteCacheConfig>;\n\n constructor(\n keyframeStore: IKeyframeStore,\n operationStore: IOperationStore,\n registry: IDocumentModelRegistry,\n config: WriteCacheConfig,\n ) {\n this.keyframeStore = keyframeStore;\n this.operationStore = operationStore;\n this.registry = registry;\n this.config = {\n maxDocuments: config.maxDocuments,\n ringBufferSize: config.ringBufferSize,\n keyframeInterval: config.keyframeInterval,\n };\n this.streams = new Map();\n this.lruTracker = new LRUTracker<string>();\n }\n\n withScopedStores(\n operationStore: IOperationStore,\n keyframeStore: IKeyframeStore,\n ): KyselyWriteCache {\n const scoped = new KyselyWriteCache(\n keyframeStore,\n operationStore,\n this.registry,\n this.config,\n );\n scoped.streams = this.streams;\n scoped.lruTracker = this.lruTracker;\n return scoped;\n }\n\n /**\n * Initializes the write cache.\n * Currently a no-op as keyframe store lifecycle is managed externally.\n */\n async startup(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Shuts down the write cache.\n * Currently a no-op as keyframe store lifecycle is managed externally.\n */\n async shutdown(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Retrieves document state at a specific revision from cache or rebuilds it.\n *\n * Cache hit path: Returns cached snapshot if available (O(1))\n * Warm miss path: Rebuilds from cached base revision + incremental ops\n * Cold miss path: Rebuilds from keyframe or from scratch using all operations\n *\n * @param documentId - The document identifier\n * @param scope - The operation scope\n * @param branch - The operation branch\n * @param targetRevision - The target revision, or undefined for newest\n * @param signal - Optional abort signal to cancel the operation\n * @returns The document at the target revision\n * @throws {Error} \"Operation aborted\" if signal is aborted\n * @throws {ModuleNotFoundError} If document type not registered in registry\n * @throws {Error} \"Failed to rebuild document\" if operation store fails\n * @throws {Error} If reducer throws during operation application\n * @throws {Error} If document serialization fails\n */\n async getState(\n documentId: string,\n scope: string,\n branch: string,\n targetRevision?: number,\n signal?: AbortSignal,\n ): Promise<PHDocument> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const streamKey = this.makeStreamKey(documentId, scope, branch);\n const stream = this.streams.get(streamKey);\n\n if (stream) {\n const snapshots = stream.ringBuffer.getAll();\n\n if (targetRevision === undefined) {\n if (snapshots.length > 0) {\n const newest = snapshots[snapshots.length - 1];\n this.lruTracker.touch(streamKey);\n return newest.document;\n }\n } else {\n const exactMatch = snapshots.find((s) => s.revision === targetRevision);\n if (exactMatch) {\n this.lruTracker.touch(streamKey);\n return exactMatch.document;\n }\n\n const newestOlder = this.findNearestOlderSnapshot(\n snapshots,\n targetRevision,\n );\n if (newestOlder) {\n const document = await this.warmMissRebuild(\n newestOlder.document,\n newestOlder.revision,\n documentId,\n scope,\n branch,\n targetRevision,\n signal,\n );\n\n this.putState(documentId, scope, branch, targetRevision, document);\n this.lruTracker.touch(streamKey);\n\n return document;\n }\n }\n }\n\n const document = await this.coldMissRebuild(\n documentId,\n scope,\n branch,\n targetRevision,\n signal,\n );\n\n let revision = targetRevision;\n if (revision === undefined) {\n revision = document.header.revision[scope] || 0;\n }\n\n this.putState(documentId, scope, branch, revision, document);\n\n return document;\n }\n\n /**\n * Stores a document snapshot in the cache at a specific revision.\n *\n * The cached document is a shallow copy of the input with its operation history\n * truncated to the last operation per scope and its clipboard cleared. This keeps\n * memory use and copy costs constant regardless of operation count. Consumers of\n * getState() must not rely on the full operation history being present; the only\n * guaranteed invariant is that operations[scope].at(-1) reflects the latest\n * operation index for each scope.\n *\n * Updates LRU tracker and may evict least recently used stream if at capacity.\n * Asynchronously persists keyframes at configured intervals (fire-and-forget).\n *\n * @param documentId - The document identifier\n * @param scope - The operation scope\n * @param branch - The operation branch\n * @param revision - The revision number\n * @param document - The document to cache\n * @throws {Error} If document serialization fails\n */\n putState(\n documentId: string,\n scope: string,\n branch: string,\n revision: number,\n document: PHDocument,\n ): void {\n const streamKey = this.makeStreamKey(documentId, scope, branch);\n const stream = this.getOrCreateStream(streamKey);\n\n // Keep only the last operation per scope in the ring buffer. The reducer\n // only needs at(-1).index to determine the next index, so carrying the\n // full history causes O(n²) array copies across n operations. UNDO, REDO,\n // and PRUNE bypass this by forcing a cold-miss rebuild in the job executor.\n const slicedDocument: PHDocument = {\n ...document,\n operations: Object.fromEntries(\n Object.entries(document.operations).map(([k, ops]) => [\n k,\n ops.length ? [ops.at(-1)!] : [],\n ]),\n ),\n clipboard: [],\n };\n\n const snapshot: CachedSnapshot = {\n revision,\n document: slicedDocument,\n };\n\n stream.ringBuffer.push(snapshot);\n\n if (this.isKeyframeRevision(revision)) {\n this.keyframeStore\n .putKeyframe(documentId, scope, branch, revision, {\n ...document,\n operations: {},\n clipboard: [],\n })\n .catch((err) => {\n console.error(\n `Failed to persist keyframe ${documentId}@${revision}:`,\n err,\n );\n });\n }\n }\n\n /**\n * Invalidates cached document streams.\n *\n * Supports three invalidation scopes:\n * - Document-level: invalidate(documentId) - removes all streams for document\n * - Scope-level: invalidate(documentId, scope) - removes all branches for scope\n * - Stream-level: invalidate(documentId, scope, branch) - removes specific stream\n *\n * @param documentId - The document identifier\n * @param scope - Optional scope to narrow invalidation\n * @param branch - Optional branch to narrow invalidation (requires scope)\n * @returns The number of streams evicted\n */\n invalidate(documentId: string, scope?: string, branch?: string): number {\n let evicted = 0;\n\n if (scope === undefined && branch === undefined) {\n for (const [key] of this.streams.entries()) {\n if (key.startsWith(`${documentId}:`)) {\n this.streams.delete(key);\n this.lruTracker.remove(key);\n evicted++;\n }\n }\n } else if (scope !== undefined && branch === undefined) {\n for (const [key] of this.streams.entries()) {\n if (key.startsWith(`${documentId}:${scope}:`)) {\n this.streams.delete(key);\n this.lruTracker.remove(key);\n evicted++;\n }\n }\n } else if (scope !== undefined && branch !== undefined) {\n const key = this.makeStreamKey(documentId, scope, branch);\n if (this.streams.has(key)) {\n this.streams.delete(key);\n this.lruTracker.remove(key);\n evicted = 1;\n }\n }\n\n return evicted;\n }\n\n /**\n * Clears the entire cache, removing all cached document streams.\n * Resets LRU tracking state. This operation always succeeds.\n */\n clear(): void {\n this.streams.clear();\n this.lruTracker.clear();\n }\n\n /**\n * Retrieves a specific stream for a document. Exposed on the implementation\n * for testing, but not on the interface.\n *\n * @internal\n */\n getStream(\n documentId: string,\n scope: string,\n branch: string,\n ): DocumentStream | undefined {\n const key = this.makeStreamKey(documentId, scope, branch);\n return this.streams.get(key);\n }\n\n private async findNearestKeyframe(\n documentId: string,\n scope: string,\n branch: string,\n targetRevision: number,\n signal?: AbortSignal,\n ): Promise<{ revision: number; document: PHDocument } | undefined> {\n if (targetRevision === Number.MAX_SAFE_INTEGER || targetRevision <= 0) {\n return undefined;\n }\n\n return this.keyframeStore.findNearestKeyframe(\n documentId,\n scope,\n branch,\n targetRevision,\n signal,\n );\n }\n\n private async coldMissRebuild(\n documentId: string,\n scope: string,\n branch: string,\n targetRevision: number | undefined,\n signal?: AbortSignal,\n ): Promise<PHDocument> {\n const effectiveTargetRevision = targetRevision || Number.MAX_SAFE_INTEGER;\n\n const keyframe = await this.findNearestKeyframe(\n documentId,\n scope,\n branch,\n effectiveTargetRevision,\n signal,\n );\n\n let document: PHDocument | undefined;\n let startRevision: number;\n let documentType: string;\n\n if (keyframe) {\n document = keyframe.document;\n startRevision = keyframe.revision;\n documentType = keyframe.document.header.documentType;\n } else {\n document = undefined;\n startRevision = -1;\n const createOpResult = await this.operationStore.getSince(\n documentId,\n \"document\",\n branch,\n -1,\n undefined,\n { cursor: \"0\", limit: 1 },\n signal,\n );\n\n if (createOpResult.results.length === 0) {\n throw new Error(\n `Failed to rebuild document ${documentId}: no CREATE_DOCUMENT operation found in document scope`,\n );\n }\n\n const createOp = createOpResult.results[0];\n if (createOp.action.type !== \"CREATE_DOCUMENT\") {\n throw new Error(\n `Failed to rebuild document ${documentId}: first operation in document scope must be CREATE_DOCUMENT, found ${createOp.action.type}`,\n );\n }\n\n const documentCreateAction = createOp.action as CreateDocumentAction;\n documentType = documentCreateAction.input.model;\n if (!documentType) {\n throw new Error(\n `Failed to rebuild document ${documentId}: CREATE_DOCUMENT action missing model in input`,\n );\n }\n\n document = createDocumentFromAction(documentCreateAction);\n\n let docModule = this.registry.getModule(\n documentType,\n extractModuleVersion(document),\n );\n const docScopeOps = await this.operationStore.getSince(\n documentId,\n \"document\",\n branch,\n 0,\n undefined,\n undefined,\n signal,\n );\n\n for (const operation of docScopeOps.results) {\n if (operation.index === 0) {\n continue;\n }\n\n if (operation.action.type === \"UPGRADE_DOCUMENT\") {\n const upgradeAction = operation.action as UpgradeDocumentAction;\n document = applyUpgradeDocumentAction(document, upgradeAction);\n docModule = this.registry.getModule(\n documentType,\n extractModuleVersion(document),\n );\n } else if (operation.action.type === \"DELETE_DOCUMENT\") {\n applyDeleteDocumentAction(document, operation.action as never);\n } else {\n const protocolVersion =\n document.header.protocolVersions?.[\"base-reducer\"] ?? 1;\n document = docModule.reducer(document, operation.action, undefined, {\n skip: operation.skip,\n protocolVersion,\n });\n }\n }\n }\n\n const module = this.registry.getModule(\n documentType,\n extractModuleVersion(document),\n );\n let cursor: string | undefined = undefined;\n const pageSize = 100;\n let hasMorePages: boolean;\n\n do {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const paging = { cursor: cursor || \"0\", limit: pageSize };\n\n try {\n const result = await this.operationStore.getSince(\n documentId,\n scope,\n branch,\n startRevision,\n undefined,\n paging,\n signal,\n );\n\n for (const operation of result.results) {\n if (\n targetRevision !== undefined &&\n operation.index > targetRevision\n ) {\n break;\n }\n\n // Fail-fast: if reducer throws, error propagates immediately without caching partial state\n const protocolVersion =\n document.header.protocolVersions?.[\"base-reducer\"] ?? 1;\n document = module.reducer(document, operation.action, undefined, {\n skip: operation.skip,\n protocolVersion,\n });\n }\n\n const reachedTarget =\n targetRevision !== undefined &&\n result.results.some((op) => op.index >= targetRevision);\n hasMorePages = Boolean(result.nextCursor) && !reachedTarget;\n\n if (hasMorePages) {\n cursor = result.nextCursor;\n }\n } catch (err) {\n // Wrap errors with context to include document ID for debugging\n throw new Error(\n `Failed to rebuild document ${documentId}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n } while (hasMorePages);\n\n // we let these errors bubble up to jobs\n const revisions = await this.operationStore.getRevisions(\n documentId,\n branch,\n signal,\n );\n document.header.revision = revisions.revision;\n document.header.lastModifiedAtUtcIso = revisions.latestTimestamp;\n\n return document;\n }\n\n private async warmMissRebuild(\n baseDocument: PHDocument,\n baseRevision: number,\n documentId: string,\n scope: string,\n branch: string,\n targetRevision: number | undefined,\n signal?: AbortSignal,\n ): Promise<PHDocument> {\n const documentType = baseDocument.header.documentType;\n const module = this.registry.getModule(documentType);\n let document = baseDocument;\n\n try {\n const pagedResults = await this.operationStore.getSince(\n documentId,\n scope,\n branch,\n baseRevision,\n undefined,\n undefined,\n signal,\n );\n\n for (const operation of pagedResults.results) {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n if (targetRevision !== undefined && operation.index > targetRevision) {\n break;\n }\n\n // Fail-fast: if reducer throws, error propagates immediately without caching partial state\n const protocolVersion =\n document.header.protocolVersions?.[\"base-reducer\"] ?? 1;\n document = module.reducer(document, operation.action, undefined, {\n skip: operation.skip,\n protocolVersion,\n });\n\n if (\n targetRevision !== undefined &&\n operation.index === targetRevision\n ) {\n break;\n }\n }\n } catch (err) {\n // Wrap errors with context to include document ID for debugging\n throw new Error(\n `Failed to rebuild document ${documentId}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n // we let these errors bubble up to jobs\n const revisions = await this.operationStore.getRevisions(\n documentId,\n branch,\n signal,\n );\n document.header.revision = revisions.revision;\n document.header.lastModifiedAtUtcIso = revisions.latestTimestamp;\n\n return document;\n }\n\n private findNearestOlderSnapshot(\n snapshots: CachedSnapshot[],\n targetRevision: number,\n ): CachedSnapshot | undefined {\n let nearest: CachedSnapshot | undefined = undefined;\n\n for (const snapshot of snapshots) {\n if (snapshot.revision < targetRevision) {\n if (!nearest || snapshot.revision > nearest.revision) {\n nearest = snapshot;\n }\n }\n }\n\n return nearest;\n }\n\n private makeStreamKey(\n documentId: string,\n scope: string,\n branch: string,\n ): string {\n return `${documentId}:${scope}:${branch}`;\n }\n\n private getOrCreateStream(key: string): DocumentStream {\n let stream = this.streams.get(key);\n\n if (!stream) {\n if (this.streams.size >= this.config.maxDocuments) {\n const evictKey = this.lruTracker.evict();\n if (evictKey) {\n this.streams.delete(evictKey);\n }\n }\n\n stream = {\n key,\n ringBuffer: new RingBuffer<CachedSnapshot>(this.config.ringBufferSize),\n };\n this.streams.set(key, stream);\n }\n\n this.lruTracker.touch(key);\n return stream;\n }\n\n private isKeyframeRevision(revision: number): boolean {\n return revision > 0 && revision % this.config.keyframeInterval === 0;\n }\n}\n","import type { IEventBus } from \"./interfaces.js\";\nimport type { Subscriber, Unsubscribe } from \"./types.js\";\nimport { EventBusAggregateError } from \"./types.js\";\n\nexport class EventBus implements IEventBus {\n public readonly eventTypeToSubscribers = new Map<number, Subscriber[]>();\n\n subscribe<K>(\n type: number,\n subscriber: (type: number, event: K) => void | Promise<void>,\n ): Unsubscribe {\n let list = this.eventTypeToSubscribers.get(type);\n if (!list) {\n list = [];\n this.eventTypeToSubscribers.set(type, list);\n }\n list.push(subscriber as Subscriber);\n\n let done = false;\n return () => {\n if (done) {\n return;\n }\n done = true;\n\n const arr = this.eventTypeToSubscribers.get(type);\n if (!arr) {\n return;\n }\n\n const idx = arr.indexOf(subscriber as Subscriber);\n if (idx !== -1) {\n arr.splice(idx, 1);\n }\n if (arr.length === 0) {\n this.eventTypeToSubscribers.delete(type);\n }\n };\n }\n\n async emit(type: number, data: any): Promise<void> {\n const list = this.eventTypeToSubscribers.get(type);\n if (!list || list.length === 0) {\n return;\n }\n\n // Snapshot ensures subscribers added/removed during emit don't affect this cycle.\n const snapshot = list.slice();\n\n // Call each subscriber sequentially and collect any errors\n const errors: any[] = [];\n for (const fn of snapshot) {\n try {\n await Promise.resolve(fn(type, data));\n } catch (err) {\n errors.push(err);\n }\n }\n\n // If any errors occurred, throw an aggregate error containing all of them\n if (errors.length > 0) {\n throw new EventBusAggregateError(errors);\n }\n }\n}\n","import type { Kysely, Transaction } from \"kysely\";\nimport type { CollectionMembershipCache } from \"../cache/collection-membership-cache.js\";\nimport type { DocumentMetaCache } from \"../cache/document-meta-cache.js\";\nimport type { KyselyOperationIndex } from \"../cache/kysely-operation-index.js\";\nimport type { KyselyWriteCache } from \"../cache/kysely-write-cache.js\";\nimport type { IOperationIndex } from \"../cache/operation-index-types.js\";\nimport type { IWriteCache } from \"../cache/write/interfaces.js\";\nimport type { IDocumentMetaCache } from \"../cache/document-meta-cache-types.js\";\nimport type { ICollectionMembershipCache } from \"../cache/collection-membership-cache.js\";\nimport type { IOperationStore } from \"../storage/interfaces.js\";\nimport type { KyselyOperationStore } from \"../storage/kysely/store.js\";\nimport type { KyselyKeyframeStore } from \"../storage/kysely/keyframe-store.js\";\nimport type { Database } from \"../storage/kysely/types.js\";\n\nexport interface ExecutionStores {\n operationStore: IOperationStore;\n operationIndex: IOperationIndex;\n writeCache: IWriteCache;\n documentMetaCache: IDocumentMetaCache;\n collectionMembershipCache: ICollectionMembershipCache;\n}\n\nexport interface IExecutionScope {\n run<T>(\n fn: (stores: ExecutionStores) => Promise<T>,\n signal?: AbortSignal,\n ): Promise<T>;\n}\n\nexport class DefaultExecutionScope implements IExecutionScope {\n constructor(\n private operationStore: IOperationStore,\n private operationIndex: IOperationIndex,\n private writeCache: IWriteCache,\n private documentMetaCache: IDocumentMetaCache,\n private collectionMembershipCache: ICollectionMembershipCache,\n ) {}\n\n async run<T>(\n fn: (stores: ExecutionStores) => Promise<T>,\n signal?: AbortSignal,\n ): Promise<T> {\n signal?.throwIfAborted();\n return fn({\n operationStore: this.operationStore,\n operationIndex: this.operationIndex,\n writeCache: this.writeCache,\n documentMetaCache: this.documentMetaCache,\n collectionMembershipCache: this.collectionMembershipCache,\n });\n }\n}\n\nexport class KyselyExecutionScope implements IExecutionScope {\n constructor(\n private db: Kysely<Database>,\n private operationStore: KyselyOperationStore,\n private operationIndex: KyselyOperationIndex,\n private keyframeStore: KyselyKeyframeStore,\n private writeCache: KyselyWriteCache,\n private documentMetaCache: DocumentMetaCache,\n private collectionMembershipCache: CollectionMembershipCache,\n ) {}\n\n async run<T>(\n fn: (stores: ExecutionStores) => Promise<T>,\n signal?: AbortSignal,\n ): Promise<T> {\n signal?.throwIfAborted();\n return this.db.transaction().execute(async (trx: Transaction<Database>) => {\n const scopedOperationStore = this.operationStore.withTransaction(trx);\n const scopedOperationIndex = this.operationIndex.withTransaction(trx);\n const scopedKeyframeStore = this.keyframeStore.withTransaction(trx);\n return fn({\n operationStore: scopedOperationStore,\n operationIndex: scopedOperationIndex,\n writeCache: this.writeCache.withScopedStores(\n scopedOperationStore,\n scopedKeyframeStore,\n ),\n documentMetaCache:\n this.documentMetaCache.withScopedStore(scopedOperationStore),\n collectionMembershipCache:\n this.collectionMembershipCache.withScopedIndex(scopedOperationIndex),\n });\n });\n }\n}\n","import type { Action, Operation } from \"@powerhousedao/shared/document-model\";\nimport type { ErrorInfo, JobMeta } from \"../shared/types.js\";\n\nexport type JobKind = \"mutation\" | \"load\";\n\n/**\n * State of a job in the queue\n */\nexport enum JobQueueState {\n UNKNOWN = -1,\n PREPROCESSING = 0,\n PENDING = 1,\n READY = 2,\n RUNNING = 3,\n RESOLVED = 4,\n}\n\n/**\n * Interface for a job execution handle\n */\nexport interface IJobExecutionHandle {\n readonly job: Job;\n readonly state: JobQueueState;\n\n start(): void;\n complete(): void;\n fail(error: ErrorInfo): void;\n defer(): void;\n}\n\n/**\n * Represents a job to be executed by the job executor\n */\nexport type Job = {\n /** Unique identifier for the job */\n id: string;\n\n /** Classification of the job so executors can switch behavior */\n kind: JobKind;\n\n /** The document ID this job operates on */\n documentId: string;\n\n /** The scope of the operations */\n scope: string;\n\n /** The branch of the operations */\n branch: string;\n\n /** The actions to be executed (processed sequentially) */\n actions: Action[];\n\n /** Pre-existing operations to import (used for load jobs) */\n operations: Operation[];\n\n /** Timestamp when the job was created */\n createdAt: string;\n\n /** The hint for the queue to use for ordering the job */\n queueHint: string[];\n\n /** Number of retry attempts */\n retryCount?: number;\n\n /** Maximum number of retries allowed */\n maxRetries?: number;\n\n /** Last error if job failed */\n lastError?: ErrorInfo;\n\n /** History of all errors from each attempt (ordered) */\n errorHistory: ErrorInfo[];\n\n /** Metadata that flows through the job lifecycle */\n meta: JobMeta;\n};\n\n/**\n * Event types for the queue system\n */\nexport const QueueEventTypes = {\n JOB_AVAILABLE: 10000,\n} as const;\n\n/**\n * Event data for job available events\n */\nexport type JobAvailableEvent = {\n documentId: string;\n scope: string;\n branch: string;\n jobId: string;\n};\n","import type {\n DocumentModelModule,\n UpgradeManifest,\n UpgradeReducer,\n UpgradeTransition,\n} from \"@powerhousedao/shared/document-model\";\nimport type { IDocumentModelRegistry } from \"./interfaces.js\";\n\n/**\n * Error thrown when a document model module is not found in the registry.\n */\nexport class ModuleNotFoundError extends Error {\n readonly documentType: string;\n readonly requestedVersion: number | undefined;\n\n constructor(documentType: string, version?: number) {\n const versionSuffix = version !== undefined ? ` version ${version}` : \"\";\n super(\n `Document model module not found for type: ${documentType}${versionSuffix}`,\n );\n this.name = \"ModuleNotFoundError\";\n this.documentType = documentType;\n this.requestedVersion = version;\n }\n\n static isError(error: unknown): error is ModuleNotFoundError {\n return Error.isError(error) && error.name === \"ModuleNotFoundError\";\n }\n}\n\n/**\n * Error thrown when attempting to register a module that already exists.\n */\nexport class DuplicateModuleError extends Error {\n constructor(documentType: string, version?: number) {\n const versionSuffix = version !== undefined ? ` (version ${version})` : \"\";\n super(\n `Document model module already registered for type: ${documentType}${versionSuffix}`,\n );\n this.name = \"DuplicateModuleError\";\n }\n\n static isError(error: unknown): error is DuplicateModuleError {\n return Error.isError(error) && error.name === \"DuplicateModuleError\";\n }\n}\n\n/**\n * Error thrown when a module is invalid or malformed.\n */\nexport class InvalidModuleError extends Error {\n constructor(message: string) {\n super(`Invalid document model module: ${message}`);\n this.name = \"InvalidModuleError\";\n }\n}\n\n/**\n * Error thrown when attempting to register an upgrade manifest that already exists.\n */\nexport class DuplicateManifestError extends Error {\n constructor(documentType: string) {\n super(`Upgrade manifest already registered for type: ${documentType}`);\n this.name = \"DuplicateManifestError\";\n }\n}\n\n/**\n * Error thrown when an upgrade manifest is not found.\n */\nexport class ManifestNotFoundError extends Error {\n constructor(documentType: string) {\n super(`Upgrade manifest not found for type: ${documentType}`);\n this.name = \"ManifestNotFoundError\";\n }\n}\n\n/**\n * Error thrown when attempting a downgrade operation.\n */\nexport class DowngradeNotSupportedError extends Error {\n constructor(documentType: string, fromVersion: number, toVersion: number) {\n super(\n `Downgrade not supported for ${documentType}: cannot go from version ${fromVersion} to ${toVersion}`,\n );\n this.name = \"DowngradeNotSupportedError\";\n }\n}\n\n/**\n * Error thrown when a required upgrade transition is missing from the manifest.\n */\nexport class MissingUpgradeTransitionError extends Error {\n constructor(documentType: string, fromVersion: number, toVersion: number) {\n super(\n `Missing upgrade transition for ${documentType}: v${fromVersion} to v${toVersion}`,\n );\n this.name = \"MissingUpgradeTransitionError\";\n }\n}\n\n/**\n * Error thrown when getUpgradeReducer is called with a non-single-step version increment.\n */\nexport class InvalidUpgradeStepError extends Error {\n constructor(documentType: string, fromVersion: number, toVersion: number) {\n super(\n `Invalid upgrade step for ${documentType}: must be single version increment, got v${fromVersion} to v${toVersion}`,\n );\n this.name = \"InvalidUpgradeStepError\";\n }\n}\n\n/**\n * In-memory implementation of the IDocumentModelRegistry interface.\n * Manages document model modules with version-aware storage and upgrade manifest support.\n */\nexport class DocumentModelRegistry implements IDocumentModelRegistry {\n private modules: DocumentModelModule<any>[] = [];\n private manifests: UpgradeManifest<readonly number[]>[] = [];\n\n registerModules(...modules: DocumentModelModule<any>[]): void {\n for (const module of modules) {\n const documentType = module.documentModel.global.id;\n const version = module.version ?? 1;\n\n for (let i = 0; i < this.modules.length; i++) {\n const existing = this.modules[i];\n const existingType = existing.documentModel.global.id;\n const existingVersion = existing.version ?? 1;\n\n if (existingType === documentType && existingVersion === version) {\n throw new DuplicateModuleError(documentType, version);\n }\n }\n\n this.modules.push(module);\n }\n }\n\n unregisterModules(...documentTypes: string[]): boolean {\n let allFound = true;\n\n for (const documentType of documentTypes) {\n const hasModule = this.modules.some(\n (m) => m.documentModel.global.id === documentType,\n );\n\n if (!hasModule) {\n allFound = false;\n }\n\n this.modules = this.modules.filter(\n (m) => m.documentModel.global.id !== documentType,\n );\n }\n\n return allFound;\n }\n\n getModule(documentType: string, version?: number): DocumentModelModule<any> {\n let latestModule: DocumentModelModule<any> | undefined;\n let latestVersion = -1;\n\n for (let i = 0; i < this.modules.length; i++) {\n const module = this.modules[i];\n const moduleType = module.documentModel.global.id;\n const moduleVersion = module.version ?? 1;\n\n if (moduleType === documentType) {\n if (version !== undefined && moduleVersion === version) {\n return module;\n }\n\n if (moduleVersion > latestVersion) {\n latestModule = module;\n latestVersion = moduleVersion;\n }\n }\n }\n\n if (version === undefined && latestModule !== undefined) {\n return latestModule;\n }\n\n throw new ModuleNotFoundError(documentType, version);\n }\n\n getAllModules(): DocumentModelModule<any>[] {\n return [...this.modules];\n }\n\n clear(): void {\n this.modules = [];\n this.manifests = [];\n }\n\n getSupportedVersions(documentType: string): number[] {\n const versions: number[] = [];\n\n for (const module of this.modules) {\n if (module.documentModel.global.id === documentType) {\n versions.push(module.version ?? 1);\n }\n }\n\n if (versions.length === 0) {\n throw new ModuleNotFoundError(documentType);\n }\n\n return versions.sort((a, b) => a - b);\n }\n\n getLatestVersion(documentType: string): number {\n let latest = -1;\n let found = false;\n\n for (const module of this.modules) {\n if (module.documentModel.global.id === documentType) {\n found = true;\n const version = module.version ?? 1;\n if (version > latest) {\n latest = version;\n }\n }\n }\n\n if (!found) {\n throw new ModuleNotFoundError(documentType);\n }\n\n return latest;\n }\n\n registerUpgradeManifests(\n ...manifestsToRegister: UpgradeManifest<readonly number[]>[]\n ): void {\n for (const manifestToRegister of manifestsToRegister) {\n for (const registeredManifest of this.manifests) {\n const registeredManifestDocumentType = registeredManifest.documentType;\n const manifestToRegisterDocumentType = manifestToRegister.documentType;\n\n if (!registeredManifestDocumentType || !manifestToRegisterDocumentType)\n continue;\n\n if (registeredManifestDocumentType === manifestToRegisterDocumentType) {\n throw new DuplicateManifestError(manifestToRegisterDocumentType);\n }\n }\n this.manifests.push(manifestToRegister);\n }\n }\n\n getUpgradeManifest(documentType: string): UpgradeManifest<readonly number[]> {\n for (let i = 0; i < this.manifests.length; i++) {\n if (this.manifests[i].documentType === documentType) {\n return this.manifests[i];\n }\n }\n throw new ManifestNotFoundError(documentType);\n }\n\n computeUpgradePath(\n documentType: string,\n fromVersion: number,\n toVersion: number,\n ): UpgradeTransition[] {\n if (fromVersion === toVersion) {\n return [];\n }\n\n if (toVersion < fromVersion) {\n throw new DowngradeNotSupportedError(\n documentType,\n fromVersion,\n toVersion,\n );\n }\n\n const manifest = this.getUpgradeManifest(documentType);\n\n const path: UpgradeTransition[] = [];\n for (let v = fromVersion + 1; v <= toVersion; v++) {\n const key = `v${v}`;\n\n if (!(key in manifest.upgrades)) {\n throw new MissingUpgradeTransitionError(documentType, v - 1, v);\n }\n\n const transition =\n manifest.upgrades[key as keyof typeof manifest.upgrades];\n path.push(transition);\n }\n\n return path;\n }\n\n getUpgradeReducer(\n documentType: string,\n fromVersion: number,\n toVersion: number,\n ): UpgradeReducer<any, any> {\n if (toVersion !== fromVersion + 1) {\n throw new InvalidUpgradeStepError(documentType, fromVersion, toVersion);\n }\n\n const manifest = this.getUpgradeManifest(documentType);\n\n const key = `v${toVersion}`;\n\n if (!(key in manifest.upgrades)) {\n throw new MissingUpgradeTransitionError(\n documentType,\n fromVersion,\n toVersion,\n );\n }\n\n const transition = manifest.upgrades[key as keyof typeof manifest.upgrades];\n return transition.upgradeReducer;\n }\n}\n","import type { ILogger } from \"document-model\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport { ReactorEventTypes, type JobRunningEvent } from \"../events/types.js\";\nimport type { IJobTracker } from \"../job-tracker/interfaces.js\";\nimport type { IQueue } from \"../queue/interfaces.js\";\nimport type { IJobExecutionHandle, Job } from \"../queue/types.js\";\nimport { QueueEventTypes } from \"../queue/types.js\";\nimport type { IDocumentModelResolver } from \"../registry/document-model-resolver.js\";\nimport { ModuleNotFoundError } from \"../registry/implementation.js\";\nimport {\n DocumentDeletedError,\n DocumentNotFoundError,\n} from \"../shared/errors.js\";\nimport type { ErrorInfo } from \"../shared/types.js\";\nimport type { IJobExecutor, IJobExecutorManager } from \"./interfaces.js\";\nimport type { ExecutorManagerStatus, JobResult } from \"./types.js\";\n\nexport type JobExecutorFactory = () => IJobExecutor;\n\n/**\n * Manages multiple job executors and coordinates job distribution.\n * Listens for job available events and dispatches jobs to executors.\n */\nexport class SimpleJobExecutorManager implements IJobExecutorManager {\n private executors: IJobExecutor[] = [];\n private isRunning = false;\n private activeJobs = 0;\n private totalJobsProcessed = 0;\n private unsubscribe?: () => void;\n private deferredJobs = new Map<string, Job[]>();\n\n private jobTimeoutMs: number;\n\n constructor(\n private executorFactory: JobExecutorFactory,\n private eventBus: IEventBus,\n private queue: IQueue,\n private jobTracker: IJobTracker,\n private logger: ILogger,\n private resolver: IDocumentModelResolver,\n jobTimeoutMs: number = 30_000,\n ) {\n this.jobTimeoutMs = jobTimeoutMs;\n }\n\n async start(numExecutors: number): Promise<void> {\n if (this.isRunning) {\n throw new Error(\"JobExecutorManager is already running\");\n }\n\n if (numExecutors < 1) {\n throw new Error(\"Number of executors must be at least 1\");\n }\n\n // Create executors\n this.executors = [];\n for (let i = 0; i < numExecutors; i++) {\n this.executors.push(this.executorFactory());\n }\n\n // Start listening for job available events\n this.unsubscribe = this.eventBus.subscribe(\n QueueEventTypes.JOB_AVAILABLE,\n async () => {\n // Only process if we have capacity (simple round-robin for now)\n if (this.activeJobs < this.executors.length) {\n await this.processNextJob();\n }\n },\n );\n\n this.isRunning = true;\n\n // Process any existing jobs in the queue\n await this.processExistingJobs();\n }\n\n async stop(graceful = true): Promise<void> {\n if (!this.isRunning) {\n return;\n }\n\n // Stop listening for new jobs\n if (this.unsubscribe) {\n this.unsubscribe();\n this.unsubscribe = undefined;\n }\n\n if (graceful) {\n // Wait for active jobs to complete\n while (this.activeJobs > 0) {\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n }\n\n // Fail any deferred jobs that were never flushed\n for (const [, jobs] of this.deferredJobs) {\n for (const job of jobs) {\n const errorInfo = this.toErrorInfo(\n new DocumentNotFoundError(job.documentId),\n );\n this.jobTracker.markFailed(job.id, errorInfo, job);\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId: job.id,\n error: new DocumentNotFoundError(job.documentId),\n job,\n })\n .catch(() => {});\n }\n }\n this.deferredJobs.clear();\n\n this.executors = [];\n this.isRunning = false;\n }\n\n getExecutors(): IJobExecutor[] {\n return [...this.executors];\n }\n\n getStatus(): ExecutorManagerStatus {\n return {\n isRunning: this.isRunning,\n numExecutors: this.executors.length,\n activeJobs: this.activeJobs,\n totalJobsProcessed: this.totalJobsProcessed,\n };\n }\n\n private async processNextJob(): Promise<void> {\n // dequeue next available job\n let handle: IJobExecutionHandle | null;\n try {\n handle = await this.queue.dequeueNext();\n } catch (error) {\n this.logger.error(\"Error dequeueing next job: @Error\", error);\n return;\n }\n\n if (!handle) {\n return;\n }\n\n // start the job execution\n handle.start();\n this.activeJobs++;\n this.jobTracker.markRunning(handle.job.id);\n\n // Emit JOB_RUNNING event\n const runningEvent: JobRunningEvent = {\n jobId: handle.job.id,\n jobMeta: handle.job.meta,\n };\n this.eventBus\n .emit(ReactorEventTypes.JOB_RUNNING, runningEvent)\n .catch(() => {\n // Ignore event emission errors\n });\n\n // Find an available executor (simple round-robin)\n const executorIndex = this.totalJobsProcessed % this.executors.length;\n const executor = this.executors[executorIndex];\n\n // execute the job with a timeout signal; race ensures the timeout fires\n // even if the executor hangs on a call that does not check the signal\n const signal = AbortSignal.timeout(this.jobTimeoutMs);\n const toError = (reason: unknown): Error =>\n reason instanceof Error ? reason : new Error(String(reason));\n const abortPromise = new Promise<never>((_, reject) => {\n if (signal.aborted) {\n reject(toError(signal.reason));\n return;\n }\n signal.addEventListener(\"abort\", () => reject(toError(signal.reason)), {\n once: true,\n });\n });\n let result: JobResult;\n try {\n result = await Promise.race([\n executor.executeJob(handle.job, signal),\n abortPromise,\n ]);\n } catch (error) {\n const errorInfo = this.toErrorInfo(\n error instanceof Error ? error : String(error),\n );\n\n handle.fail(errorInfo);\n this.activeJobs--;\n this.jobTracker.markFailed(handle.job.id, errorInfo, handle.job);\n\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId: handle.job.id,\n error: new Error(errorInfo.message),\n job: handle.job,\n })\n .catch(() => {});\n\n await this.checkForMoreJobs();\n return;\n }\n\n // handle the result\n if (result.success) {\n handle.complete();\n this.totalJobsProcessed++;\n\n if (this.hasCreateDocumentAction(handle.job)) {\n await this.flushDeferredJobs(handle.job.documentId);\n }\n } else {\n // Attempt model recovery before exhausting retries\n if (result.error && ModuleNotFoundError.isError(result.error)) {\n let modelLoaded = false;\n try {\n await this.resolver.ensureModelLoaded(result.error.documentType);\n modelLoaded = true;\n } catch {\n // Model could not be loaded, fall through to normal failure path\n }\n\n if (modelLoaded) {\n const errorInfo = this.toErrorInfo(result.error);\n try {\n await this.queue.retryJob(handle.job.id, errorInfo);\n this.activeJobs--;\n await this.checkForMoreJobs();\n return;\n } catch {\n // Fall through to normal failure path\n }\n }\n }\n\n // DocumentNotFoundError: defer the job instead of failing immediately.\n // A CREATE_DOCUMENT job may arrive later and unblock it.\n if (result.error && DocumentNotFoundError.isError(result.error)) {\n const job = handle.job;\n\n handle.defer();\n this.activeJobs--;\n\n const docId = job.documentId;\n const existing = this.deferredJobs.get(docId) ?? [];\n existing.push(job);\n this.deferredJobs.set(docId, existing);\n\n await this.checkForMoreJobs();\n return;\n }\n\n if (result.error && DocumentDeletedError.isError(result.error)) {\n const errorInfo = this.toErrorInfo(result.error);\n this.jobTracker.markFailed(handle.job.id, errorInfo, handle.job);\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId: handle.job.id,\n error: result.error,\n job: handle.job,\n })\n .catch(() => {});\n handle.fail(errorInfo);\n this.activeJobs--;\n await this.checkForMoreJobs();\n return;\n }\n\n // Handle retry logic\n const retryCount = handle.job.retryCount || 0;\n const maxRetries = handle.job.maxRetries || 0;\n\n if (retryCount < maxRetries) {\n const currentErrorInfo = result.error\n ? this.toErrorInfo(result.error)\n : this.toErrorInfo(\"Unknown error\");\n\n try {\n await this.queue.retryJob(handle.job.id, currentErrorInfo);\n } catch (error) {\n const retryErrorInfo = this.toErrorInfo(\n error instanceof Error ? error : \"Failed to retry job\",\n );\n\n this.jobTracker.markFailed(handle.job.id, retryErrorInfo, handle.job);\n\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId: handle.job.id,\n error: result.error ?? new Error(retryErrorInfo.message),\n job: handle.job,\n })\n .catch(() => {});\n\n handle.fail(retryErrorInfo);\n }\n } else {\n const currentErrorInfo = result.error\n ? this.toErrorInfo(result.error)\n : this.toErrorInfo(\"Unknown error\");\n\n const fullErrorInfo = this.formatErrorHistory(\n handle.job.errorHistory,\n currentErrorInfo,\n retryCount + 1,\n );\n\n this.jobTracker.markFailed(handle.job.id, fullErrorInfo, handle.job);\n\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId: handle.job.id,\n error: result.error ?? new Error(fullErrorInfo.message),\n job: handle.job,\n })\n .catch(() => {});\n\n handle.fail(fullErrorInfo);\n }\n }\n\n this.activeJobs--;\n await this.checkForMoreJobs();\n }\n\n private async checkForMoreJobs(): Promise<void> {\n if (!this.isRunning) {\n return;\n }\n\n let hasMore: boolean;\n try {\n hasMore = await this.queue.hasJobs();\n } catch (error) {\n this.logger.error(\"Error checking for more jobs: @Error\", error);\n return;\n }\n\n if (hasMore) {\n await this.processNextJob();\n }\n }\n\n private async processExistingJobs(): Promise<void> {\n let hasJobs: boolean;\n try {\n hasJobs = await this.queue.hasJobs();\n } catch (error) {\n this.logger.error(\"Error checking for existing jobs: @Error\", error);\n return;\n }\n\n if (hasJobs) {\n // Start processing up to the number of executors\n const promises: Promise<void>[] = [];\n for (let i = 0; i < Math.min(this.executors.length, 5); i++) {\n promises.push(this.processNextJob());\n }\n\n try {\n await Promise.all(promises);\n } catch (error) {\n this.logger.error(\"Error processing existing jobs: @Error\", error);\n }\n }\n }\n\n private toErrorInfo(error: Error | string): ErrorInfo {\n if (error instanceof Error) {\n return {\n message: error.message,\n stack: error.stack || new Error().stack || \"\",\n };\n }\n return {\n message: error,\n stack: new Error().stack || \"\",\n };\n }\n\n private hasCreateDocumentAction(job: Job): boolean {\n for (const action of job.actions) {\n if (action.type === \"CREATE_DOCUMENT\") {\n return true;\n }\n }\n for (const operation of job.operations) {\n if (operation.action.type === \"CREATE_DOCUMENT\") {\n return true;\n }\n }\n return false;\n }\n\n private async flushDeferredJobs(documentId: string): Promise<void> {\n const jobs = this.deferredJobs.get(documentId);\n if (!jobs || jobs.length === 0) {\n return;\n }\n this.deferredJobs.delete(documentId);\n\n for (const job of jobs) {\n try {\n await this.queue.enqueue(job);\n } catch (error) {\n this.logger.error(\"Error re-enqueuing deferred job: @Error\", error);\n }\n }\n }\n\n private formatErrorHistory(\n errorHistory: ErrorInfo[],\n currentError: ErrorInfo,\n totalAttempts: number,\n ): ErrorInfo {\n const allErrors = [...errorHistory, currentError];\n\n if (allErrors.length === 1) {\n return currentError;\n }\n\n const messageLines = [`Job failed after ${totalAttempts} attempts:`];\n const stackLines: string[] = [];\n\n allErrors.forEach((error, index) => {\n messageLines.push(`[Attempt ${index + 1}] ${error.message}`);\n stackLines.push(`[Attempt ${index + 1}] Stack trace:\\n${error.stack}`);\n });\n\n return {\n message: messageLines.join(\"\\n\"),\n stack: stackLines.join(\"\\n\\n\"),\n };\n }\n}\n","type OperationIndex = {\n index: number;\n skip: number;\n id: string;\n timestampUtcMs: string;\n action?: {\n id?: string;\n type?: string;\n };\n};\n\nconst STRICT_ORDER_ACTION_TYPES = new Set([\n \"CREATE_DOCUMENT\",\n \"DELETE_DOCUMENT\",\n \"UPGRADE_DOCUMENT\",\n \"ADD_RELATIONSHIP\",\n \"REMOVE_RELATIONSHIP\",\n]);\n\n/**\n * Sorts operations by index and skip number.\n * [0:0 2:0 1:0 3:3 3:1] => [0:0 1:0 2:0 3:1 3:3]\n */\nexport function sortOperations<TOpIndex extends OperationIndex>(\n operations: TOpIndex[],\n): TOpIndex[] {\n return operations\n .slice()\n .sort((a, b) => a.skip - b.skip)\n .sort((a, b) => a.index - b.index);\n}\n\n/**\n * Reshuffles operations by timestamp, then applies deterministic tie-breaking.\n * Used for merging concurrent operations from different branches.\n *\n * For strict document-structure actions (e.g., CREATE_DOCUMENT/UPGRADE_DOCUMENT),\n * logical index (index - skip) is prioritized to preserve causal replay order.\n *\n * For other actions, action ID is prioritized to ensure a canonical cross-reactor order\n * for concurrent operations that may have diverged local indices due to prior reshuffles.\n * Logical index and operation ID are then used as deterministic tie-breakers.\n *\n * Example:\n * [0:0, 1:0, 2:0, A3:0, A4:0, A5:0] + [0:0, 1:0, 2:0, B3:0, B4:2, B5:0]\n * GC => [0:0, 1:0, 2:0, A3:0, A4:0, A5:0] + [0:0, 1:0, B4:2, B5:0]\n * Split => [0:0, 1:0] + [2:0, A3:0, A4:0, A5:0] + [B4:2, B5:0]\n * Reshuffle(6:4) => [6:4, 7:0, 8:0, 9:0, 10:0, 11:0]\n * merge => [0:0, 1:0, 6:4, 7:0, 8:0, 9:0, 10:0, 11:0]\n */\nexport function reshuffleByTimestamp<TOp extends OperationIndex>(\n startIndex: { index: number; skip: number },\n opsA: TOp[],\n opsB: TOp[],\n): TOp[] {\n return [...opsA, ...opsB]\n .sort((a, b) => {\n const timestampDiff =\n new Date(a.timestampUtcMs).getTime() -\n new Date(b.timestampUtcMs).getTime();\n if (timestampDiff !== 0) {\n return timestampDiff;\n }\n\n const shouldPrioritizeLogicalIndex =\n STRICT_ORDER_ACTION_TYPES.has(a.action?.type ?? \"\") ||\n STRICT_ORDER_ACTION_TYPES.has(b.action?.type ?? \"\");\n const logicalIndexDiff = a.index - a.skip - (b.index - b.skip);\n\n if (shouldPrioritizeLogicalIndex) {\n if (logicalIndexDiff !== 0) {\n return logicalIndexDiff;\n }\n }\n\n const actionIdDiff = (a.action?.id ?? \"\").localeCompare(\n b.action?.id ?? \"\",\n );\n if (actionIdDiff !== 0) {\n return actionIdDiff;\n }\n\n if (!shouldPrioritizeLogicalIndex && logicalIndexDiff !== 0) {\n return logicalIndexDiff;\n }\n\n return a.id.localeCompare(b.id);\n })\n .map((op, i) => ({\n ...op,\n index: startIndex.index + i,\n skip: i === 0 ? startIndex.skip : 0,\n }));\n}\n\n/**\n * Reshuffles operations by timestamp first, then by original index value.\n * Used for merging concurrent operations while preserving index ordering for operations with same timestamp.\n */\nexport function reshuffleByTimestampAndIndex<TOp extends OperationIndex>(\n startIndex: { index: number; skip: number },\n opsA: TOp[],\n opsB: TOp[],\n): TOp[] {\n return [...opsA, ...opsB]\n .sort((a, b) => {\n const indexDiff = a.index - b.index;\n if (indexDiff !== 0) {\n return indexDiff;\n }\n const timestampDiff =\n new Date(a.timestampUtcMs).getTime() -\n new Date(b.timestampUtcMs).getTime();\n if (timestampDiff !== 0) {\n return timestampDiff;\n }\n return a.id.localeCompare(b.id);\n })\n .map((op, i) => ({\n ...op,\n index: startIndex.index + i,\n skip: i === 0 ? startIndex.skip : 0,\n }));\n}\n","import type {\n Operation,\n OperationWithContext,\n} from \"@powerhousedao/shared/document-model\";\nimport type { Generated, Insertable, Selectable, Updateable } from \"kysely\";\nimport type { PagedResults, PagingOptions } from \"../shared/types.js\";\nimport type { ViewFilter } from \"../storage/interfaces.js\";\n\nexport type OperationIndexEntry = Operation & {\n ordinal?: number;\n documentId: string;\n documentType: string;\n branch: string;\n scope: string;\n sourceRemote: string;\n};\n\nexport interface IOperationIndexTxn {\n createCollection(collectionId: string): void;\n addToCollection(collectionId: string, documentId: string): void;\n removeFromCollection(collectionId: string, documentId: string): void;\n write(operations: OperationIndexEntry[]): void;\n}\n\nexport interface IOperationIndex {\n start(): IOperationIndexTxn;\n commit(txn: IOperationIndexTxn, signal?: AbortSignal): Promise<number[]>;\n find(\n collectionId: string,\n cursor?: number,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<OperationIndexEntry>>;\n /**\n * Get all operations for a specific document, ordered by ordinal.\n * Used for retroactive sync when a document is added to a collection.\n */\n get(\n documentId: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<OperationIndexEntry>>;\n getSinceOrdinal(\n ordinal: number,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<OperationWithContext>>;\n getLatestTimestampForCollection(\n collectionId: string,\n signal?: AbortSignal,\n ): Promise<string | null>;\n /**\n * Get all collection memberships for the given document IDs.\n * Returns a map of documentId to array of collection IDs.\n */\n getCollectionsForDocuments(\n documentIds: string[],\n ): Promise<Record<string, string[]>>;\n}\n\nexport interface DocumentCollectionTable {\n documentId: string;\n collectionId: string;\n joinedOrdinal: bigint;\n leftOrdinal: bigint | null;\n}\n\nexport interface OperationIndexOperationTable {\n ordinal: Generated<number>;\n opId: string;\n documentId: string;\n documentType: string;\n scope: string;\n branch: string;\n timestampUtcMs: string;\n writeTimestampUtcMs: Generated<Date>;\n index: number;\n skip: number;\n hash: string;\n action: unknown;\n sourceRemote: Generated<string>;\n}\n\nexport type DocumentCollectionRow = Selectable<DocumentCollectionTable>;\nexport type InsertableDocumentCollection = Insertable<DocumentCollectionTable>;\nexport type UpdateableDocumentCollection = Updateable<DocumentCollectionTable>;\n\nexport type OperationIndexOperationRow =\n Selectable<OperationIndexOperationTable>;\nexport type InsertableOperationIndexOperation =\n Insertable<OperationIndexOperationTable>;\nexport type UpdateableOperationIndexOperation =\n Updateable<OperationIndexOperationTable>;\n\nexport function driveCollectionId(branch: string, driveId: string): string {\n return `drive.${branch}.${driveId}`;\n}\n","import type {\n Action,\n AddRelationshipActionInput,\n CreateDocumentAction,\n DeleteDocumentActionInput,\n Operation,\n PHDocument,\n RemoveRelationshipActionInput,\n UpgradeDocumentAction,\n UpgradeDocumentActionInput,\n UpgradeTransition,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport type { IOperationIndexTxn } from \"../cache/operation-index-types.js\";\nimport { driveCollectionId } from \"../cache/operation-index-types.js\";\nimport type { Job } from \"../queue/types.js\";\nimport type { IDocumentModelRegistry } from \"../registry/interfaces.js\";\nimport { DocumentDeletedError } from \"../shared/errors.js\";\nimport type { ExecutionStores } from \"./execution-scope.js\";\nimport type { JobResult } from \"./types.js\";\nimport {\n applyDeleteDocumentAction,\n applyUpgradeDocumentAction,\n buildErrorResult,\n buildSuccessResult,\n createDocumentFromAction,\n createOperation,\n getNextIndexForScope,\n updateDocumentRevision,\n} from \"./util.js\";\n\nexport class DocumentActionHandler {\n constructor(\n private registry: IDocumentModelRegistry,\n private logger: ILogger,\n ) {}\n\n async execute(\n job: Job,\n action: Action,\n startTime: number,\n indexTxn: IOperationIndexTxn,\n stores: ExecutionStores,\n skip: number = 0,\n sourceRemote: string = \"\",\n signal?: AbortSignal,\n ): Promise<\n JobResult & {\n operationsWithContext?: Array<{\n operation: Operation;\n context: {\n documentId: string;\n scope: string;\n branch: string;\n documentType: string;\n };\n }>;\n }\n > {\n switch (action.type) {\n case \"CREATE_DOCUMENT\":\n return this.executeCreate(\n job,\n action,\n startTime,\n indexTxn,\n stores,\n skip,\n sourceRemote,\n signal,\n );\n case \"DELETE_DOCUMENT\":\n return this.executeDelete(\n job,\n action,\n startTime,\n indexTxn,\n stores,\n sourceRemote,\n signal,\n );\n case \"UPGRADE_DOCUMENT\":\n return this.executeUpgrade(\n job,\n action,\n startTime,\n indexTxn,\n stores,\n skip,\n sourceRemote,\n signal,\n );\n case \"ADD_RELATIONSHIP\":\n return this.executeAddRelationship(\n job,\n action,\n startTime,\n indexTxn,\n stores,\n sourceRemote,\n signal,\n );\n case \"REMOVE_RELATIONSHIP\":\n return this.executeRemoveRelationship(\n job,\n action,\n startTime,\n indexTxn,\n stores,\n sourceRemote,\n signal,\n );\n default:\n return buildErrorResult(\n job,\n new Error(`Unknown document action type: ${action.type}`),\n startTime,\n );\n }\n }\n\n private async executeCreate(\n job: Job,\n action: Action,\n startTime: number,\n indexTxn: IOperationIndexTxn,\n stores: ExecutionStores,\n skip: number = 0,\n sourceRemote: string = \"\",\n signal?: AbortSignal,\n ): Promise<\n JobResult & {\n operationsWithContext?: Array<{\n operation: Operation;\n context: {\n documentId: string;\n scope: string;\n branch: string;\n documentType: string;\n };\n }>;\n }\n > {\n if (job.scope !== \"document\") {\n return {\n job,\n success: false,\n error: new Error(\n `CREATE_DOCUMENT must be in \"document\" scope, got \"${job.scope}\"`,\n ),\n duration: Date.now() - startTime,\n };\n }\n\n const document = createDocumentFromAction(action as CreateDocumentAction);\n\n const operation = createOperation(action, 0, skip, {\n documentId: document.header.id,\n scope: job.scope,\n branch: job.branch,\n });\n\n const resultingStateObj: Record<string, unknown> = {\n header: document.header,\n ...document.state,\n };\n const resultingState = JSON.stringify(resultingStateObj);\n\n const writeError = await this.writeOperationToStore(\n document.header.id,\n document.header.documentType,\n job.scope,\n job.branch,\n operation,\n job,\n startTime,\n stores,\n signal,\n );\n if (writeError !== null) {\n return writeError;\n }\n\n updateDocumentRevision(document, job.scope, operation.index);\n\n stores.writeCache.putState(\n document.header.id,\n job.scope,\n job.branch,\n operation.index,\n document,\n );\n\n indexTxn.write([\n {\n ...operation,\n documentId: document.header.id,\n documentType: document.header.documentType,\n branch: job.branch,\n scope: job.scope,\n sourceRemote,\n },\n ]);\n\n if (document.header.documentType === \"powerhouse/document-drive\") {\n const collectionId = driveCollectionId(job.branch, document.header.id);\n indexTxn.createCollection(collectionId);\n indexTxn.addToCollection(collectionId, document.header.id);\n }\n\n stores.documentMetaCache.putDocumentMeta(document.header.id, job.branch, {\n state: document.state.document,\n documentType: document.header.documentType,\n documentScopeRevision: 1,\n });\n\n return buildSuccessResult(\n job,\n operation,\n document.header.id,\n document.header.documentType,\n resultingState,\n startTime,\n );\n }\n\n private async executeDelete(\n job: Job,\n action: Action,\n startTime: number,\n indexTxn: IOperationIndexTxn,\n stores: ExecutionStores,\n sourceRemote: string = \"\",\n signal?: AbortSignal,\n ): Promise<\n JobResult & {\n operationsWithContext?: Array<{\n operation: Operation;\n context: {\n documentId: string;\n scope: string;\n branch: string;\n documentType: string;\n };\n }>;\n }\n > {\n const input = action.input as DeleteDocumentActionInput;\n\n if (!input.documentId) {\n return buildErrorResult(\n job,\n new Error(\"DELETE_DOCUMENT action requires a documentId in input\"),\n startTime,\n );\n }\n\n const documentId = input.documentId;\n\n let document: PHDocument;\n try {\n document = await stores.writeCache.getState(\n documentId,\n job.scope,\n job.branch,\n undefined,\n signal,\n );\n } catch (error) {\n return buildErrorResult(\n job,\n new Error(\n `Failed to fetch document before deletion: ${error instanceof Error ? error.message : String(error)}`,\n ),\n startTime,\n );\n }\n\n const documentState = document.state.document;\n if (documentState.isDeleted) {\n return buildErrorResult(\n job,\n new DocumentDeletedError(documentId, documentState.deletedAtUtcIso),\n startTime,\n );\n }\n\n const nextIndex = getNextIndexForScope(document, job.scope);\n\n const operation = createOperation(action, nextIndex, 0, {\n documentId,\n scope: job.scope,\n branch: job.branch,\n });\n\n applyDeleteDocumentAction(document, action as never);\n\n const resultingStateObj: Record<string, unknown> = {\n header: document.header,\n document: document.state.document,\n };\n const resultingState = JSON.stringify(resultingStateObj);\n\n const writeError = await this.writeOperationToStore(\n documentId,\n document.header.documentType,\n job.scope,\n job.branch,\n operation,\n job,\n startTime,\n stores,\n signal,\n );\n if (writeError !== null) {\n return writeError;\n }\n\n updateDocumentRevision(document, job.scope, operation.index);\n\n stores.writeCache.putState(\n documentId,\n job.scope,\n job.branch,\n operation.index,\n document,\n );\n\n indexTxn.write([\n {\n ...operation,\n documentId: documentId,\n documentType: document.header.documentType,\n branch: job.branch,\n scope: job.scope,\n sourceRemote,\n },\n ]);\n\n stores.documentMetaCache.putDocumentMeta(documentId, job.branch, {\n state: document.state.document,\n documentType: document.header.documentType,\n documentScopeRevision: operation.index + 1,\n });\n\n return buildSuccessResult(\n job,\n operation,\n documentId,\n document.header.documentType,\n resultingState,\n startTime,\n );\n }\n\n private async executeUpgrade(\n job: Job,\n action: Action,\n startTime: number,\n indexTxn: IOperationIndexTxn,\n stores: ExecutionStores,\n skip: number = 0,\n sourceRemote: string = \"\",\n signal?: AbortSignal,\n ): Promise<\n JobResult & {\n operationsWithContext?: Array<{\n operation: Operation;\n context: {\n documentId: string;\n scope: string;\n branch: string;\n documentType: string;\n };\n }>;\n }\n > {\n const input = action.input as UpgradeDocumentActionInput;\n\n if (!input.documentId) {\n return buildErrorResult(\n job,\n new Error(\"UPGRADE_DOCUMENT action requires a documentId in input\"),\n startTime,\n );\n }\n\n const documentId = input.documentId;\n\n const fromVersion = input.fromVersion;\n const toVersion = input.toVersion;\n\n let document: PHDocument;\n try {\n document = await stores.writeCache.getState(\n documentId,\n job.scope,\n job.branch,\n undefined,\n signal,\n );\n } catch (error) {\n return buildErrorResult(\n job,\n new Error(\n `Failed to fetch document for upgrade: ${error instanceof Error ? error.message : String(error)}`,\n ),\n startTime,\n );\n }\n\n const documentState = document.state.document;\n if (documentState.isDeleted) {\n return buildErrorResult(\n job,\n new DocumentDeletedError(documentId, documentState.deletedAtUtcIso),\n startTime,\n );\n }\n\n const nextIndex = getNextIndexForScope(document, job.scope);\n\n let upgradePath: UpgradeTransition[] | undefined;\n if (fromVersion > 0 && fromVersion < toVersion) {\n try {\n upgradePath = this.registry.computeUpgradePath(\n document.header.documentType,\n fromVersion,\n toVersion,\n );\n } catch (error) {\n return buildErrorResult(\n job,\n error instanceof Error ? error : new Error(String(error)),\n startTime,\n );\n }\n }\n\n if (fromVersion === toVersion && fromVersion > 0) {\n return {\n job,\n success: true,\n operations: [],\n operationsWithContext: [],\n duration: Date.now() - startTime,\n };\n }\n\n try {\n document = applyUpgradeDocumentAction(\n document,\n action as UpgradeDocumentAction,\n upgradePath,\n );\n } catch (error) {\n return buildErrorResult(\n job,\n error instanceof Error ? error : new Error(String(error)),\n startTime,\n );\n }\n\n const operation = createOperation(action, nextIndex, skip, {\n documentId,\n scope: job.scope,\n branch: job.branch,\n });\n\n const resultingStateObj: Record<string, unknown> = {\n header: document.header,\n ...document.state,\n };\n const resultingState = JSON.stringify(resultingStateObj);\n\n const writeError = await this.writeOperationToStore(\n documentId,\n document.header.documentType,\n job.scope,\n job.branch,\n operation,\n job,\n startTime,\n stores,\n signal,\n );\n if (writeError !== null) {\n return writeError;\n }\n\n updateDocumentRevision(document, job.scope, operation.index);\n\n stores.writeCache.putState(\n documentId,\n job.scope,\n job.branch,\n operation.index,\n document,\n );\n\n indexTxn.write([\n {\n ...operation,\n documentId: documentId,\n documentType: document.header.documentType,\n branch: job.branch,\n scope: job.scope,\n sourceRemote,\n },\n ]);\n\n stores.documentMetaCache.putDocumentMeta(documentId, job.branch, {\n state: document.state.document,\n documentType: document.header.documentType,\n documentScopeRevision: operation.index + 1,\n });\n\n return buildSuccessResult(\n job,\n operation,\n documentId,\n document.header.documentType,\n resultingState,\n startTime,\n );\n }\n\n private async executeAddRelationship(\n job: Job,\n action: Action,\n startTime: number,\n indexTxn: IOperationIndexTxn,\n stores: ExecutionStores,\n sourceRemote: string = \"\",\n signal?: AbortSignal,\n ): Promise<\n JobResult & {\n operationsWithContext?: Array<{\n operation: Operation;\n context: {\n documentId: string;\n scope: string;\n branch: string;\n documentType: string;\n };\n }>;\n }\n > {\n if (job.scope !== \"document\") {\n return buildErrorResult(\n job,\n new Error(\n `ADD_RELATIONSHIP must be in \"document\" scope, got \"${job.scope}\"`,\n ),\n startTime,\n );\n }\n\n const input = action.input as AddRelationshipActionInput;\n\n if (!input.sourceId || !input.targetId || !input.relationshipType) {\n return buildErrorResult(\n job,\n new Error(\n \"ADD_RELATIONSHIP action requires sourceId, targetId, and relationshipType in input\",\n ),\n startTime,\n );\n }\n\n if (input.sourceId === input.targetId) {\n return buildErrorResult(\n job,\n new Error(\n \"ADD_RELATIONSHIP: sourceId and targetId cannot be the same (self-relationships not allowed)\",\n ),\n startTime,\n );\n }\n\n let sourceDoc: PHDocument;\n try {\n sourceDoc = await stores.writeCache.getState(\n input.sourceId,\n \"document\",\n job.branch,\n undefined,\n signal,\n );\n } catch (error) {\n return buildErrorResult(\n job,\n new Error(\n `ADD_RELATIONSHIP: source document ${input.sourceId} not found: ${error instanceof Error ? error.message : String(error)}`,\n ),\n startTime,\n );\n }\n\n const nextIndex = getNextIndexForScope(sourceDoc, job.scope);\n\n const operation = createOperation(action, nextIndex, 0, {\n documentId: input.sourceId,\n scope: job.scope,\n branch: job.branch,\n });\n\n const writeError = await this.writeOperationToStore(\n input.sourceId,\n sourceDoc.header.documentType,\n job.scope,\n job.branch,\n operation,\n job,\n startTime,\n stores,\n signal,\n );\n if (writeError !== null) {\n return writeError;\n }\n\n sourceDoc.header.lastModifiedAtUtcIso =\n operation.timestampUtcMs || new Date().toISOString();\n\n updateDocumentRevision(sourceDoc, job.scope, operation.index);\n\n sourceDoc.operations = {\n ...sourceDoc.operations,\n [job.scope]: [...(sourceDoc.operations[job.scope] ?? []), operation],\n };\n\n const scopeState = (sourceDoc.state as Record<string, unknown>)[job.scope];\n const resultingStateObj: Record<string, unknown> = {\n header: structuredClone(sourceDoc.header),\n [job.scope]: scopeState === undefined ? {} : structuredClone(scopeState),\n };\n const resultingState = JSON.stringify(resultingStateObj);\n\n stores.writeCache.putState(\n input.sourceId,\n job.scope,\n job.branch,\n operation.index,\n sourceDoc,\n );\n\n indexTxn.write([\n {\n ...operation,\n documentId: input.sourceId,\n documentType: sourceDoc.header.documentType,\n branch: job.branch,\n scope: job.scope,\n sourceRemote,\n },\n ]);\n\n if (sourceDoc.header.documentType === \"powerhouse/document-drive\") {\n const collectionId = driveCollectionId(job.branch, input.sourceId);\n indexTxn.addToCollection(collectionId, input.targetId);\n stores.collectionMembershipCache.invalidate(input.targetId);\n }\n\n stores.documentMetaCache.putDocumentMeta(input.sourceId, job.branch, {\n state: sourceDoc.state.document,\n documentType: sourceDoc.header.documentType,\n documentScopeRevision: operation.index + 1,\n });\n\n return buildSuccessResult(\n job,\n operation,\n input.sourceId,\n sourceDoc.header.documentType,\n resultingState,\n startTime,\n );\n }\n\n private async executeRemoveRelationship(\n job: Job,\n action: Action,\n startTime: number,\n indexTxn: IOperationIndexTxn,\n stores: ExecutionStores,\n sourceRemote: string = \"\",\n signal?: AbortSignal,\n ): Promise<\n JobResult & {\n operationsWithContext?: Array<{\n operation: Operation;\n context: {\n documentId: string;\n scope: string;\n branch: string;\n documentType: string;\n };\n }>;\n }\n > {\n if (job.scope !== \"document\") {\n return buildErrorResult(\n job,\n new Error(\n `REMOVE_RELATIONSHIP must be in \"document\" scope, got \"${job.scope}\"`,\n ),\n startTime,\n );\n }\n\n const input = action.input as RemoveRelationshipActionInput;\n\n if (!input.sourceId || !input.targetId || !input.relationshipType) {\n return buildErrorResult(\n job,\n new Error(\n \"REMOVE_RELATIONSHIP action requires sourceId, targetId, and relationshipType in input\",\n ),\n startTime,\n );\n }\n\n let sourceDoc: PHDocument;\n try {\n sourceDoc = await stores.writeCache.getState(\n input.sourceId,\n \"document\",\n job.branch,\n undefined,\n signal,\n );\n } catch (error) {\n return buildErrorResult(\n job,\n new Error(\n `REMOVE_RELATIONSHIP: source document ${input.sourceId} not found: ${error instanceof Error ? error.message : String(error)}`,\n ),\n startTime,\n );\n }\n\n const nextIndex = getNextIndexForScope(sourceDoc, job.scope);\n\n const operation = createOperation(action, nextIndex, 0, {\n documentId: input.sourceId,\n scope: job.scope,\n branch: job.branch,\n });\n\n const writeError = await this.writeOperationToStore(\n input.sourceId,\n sourceDoc.header.documentType,\n job.scope,\n job.branch,\n operation,\n job,\n startTime,\n stores,\n signal,\n );\n if (writeError !== null) {\n return writeError;\n }\n\n sourceDoc.header.lastModifiedAtUtcIso =\n operation.timestampUtcMs || new Date().toISOString();\n\n updateDocumentRevision(sourceDoc, job.scope, operation.index);\n\n sourceDoc.operations = {\n ...sourceDoc.operations,\n [job.scope]: [...(sourceDoc.operations[job.scope] ?? []), operation],\n };\n\n const scopeState = (sourceDoc.state as Record<string, unknown>)[job.scope];\n const resultingStateObj: Record<string, unknown> = {\n header: structuredClone(sourceDoc.header),\n [job.scope]: scopeState === undefined ? {} : structuredClone(scopeState),\n };\n const resultingState = JSON.stringify(resultingStateObj);\n\n stores.writeCache.putState(\n input.sourceId,\n job.scope,\n job.branch,\n operation.index,\n sourceDoc,\n );\n\n indexTxn.write([\n {\n ...operation,\n documentId: input.sourceId,\n documentType: sourceDoc.header.documentType,\n branch: job.branch,\n scope: job.scope,\n sourceRemote,\n },\n ]);\n\n if (sourceDoc.header.documentType === \"powerhouse/document-drive\") {\n const collectionId = driveCollectionId(job.branch, input.sourceId);\n indexTxn.removeFromCollection(collectionId, input.targetId);\n stores.collectionMembershipCache.invalidate(input.targetId);\n }\n\n stores.documentMetaCache.putDocumentMeta(input.sourceId, job.branch, {\n state: sourceDoc.state.document,\n documentType: sourceDoc.header.documentType,\n documentScopeRevision: operation.index + 1,\n });\n\n return buildSuccessResult(\n job,\n operation,\n input.sourceId,\n sourceDoc.header.documentType,\n resultingState,\n startTime,\n );\n }\n\n private async writeOperationToStore(\n documentId: string,\n documentType: string,\n scope: string,\n branch: string,\n operation: Operation,\n job: Job,\n startTime: number,\n stores: ExecutionStores,\n signal?: AbortSignal,\n ): Promise<JobResult | null> {\n try {\n await stores.operationStore.apply(\n documentId,\n documentType,\n scope,\n branch,\n operation.index,\n (txn) => {\n txn.addOperations(operation);\n },\n signal,\n );\n return null;\n } catch (error) {\n this.logger.error(\n \"Error writing @Operation to IOperationStore: @Error\",\n operation,\n error,\n );\n\n stores.writeCache.invalidate(documentId, scope, branch);\n\n return {\n job,\n success: false,\n error: new Error(\n `Failed to write operation to IOperationStore: ${error instanceof Error ? error.message : String(error)}`,\n ),\n duration: Date.now() - startTime,\n };\n }\n }\n}\n","import type { Action, Operation } from \"@powerhousedao/shared/document-model\";\nimport { deriveOperationId } from \"@powerhousedao/shared/document-model\";\nimport { InvalidSignatureError } from \"../shared/errors.js\";\nimport type { SignatureVerificationHandler } from \"../signer/types.js\";\n\nexport class SignatureVerifier {\n constructor(private verifier?: SignatureVerificationHandler) {}\n\n async verifyActions(\n documentId: string,\n branch: string,\n actions: Action[],\n ): Promise<void> {\n if (!this.verifier) {\n return;\n }\n\n for (const action of actions) {\n const signer = action.context?.signer;\n\n if (!signer) {\n continue;\n }\n\n if (signer.signatures.length === 0) {\n throw new InvalidSignatureError(\n documentId,\n `Action ${action.id} has signer but no signatures`,\n );\n }\n\n const publicKey = signer.app.key;\n let isValid = false;\n\n try {\n const tempOperation: Operation = {\n id: deriveOperationId(documentId, action.scope, branch, action.id),\n index: 0,\n timestampUtcMs: action.timestampUtcMs || new Date().toISOString(),\n hash: \"\",\n skip: 0,\n action: action,\n };\n\n isValid = await this.verifier(tempOperation, publicKey);\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n throw new InvalidSignatureError(\n documentId,\n `Action ${action.id} verification failed: ${errorMessage}`,\n );\n }\n\n if (!isValid) {\n throw new InvalidSignatureError(\n documentId,\n `Action ${action.id} signature verification returned false`,\n );\n }\n }\n }\n\n async verifyOperations(\n documentId: string,\n operations: Operation[],\n ): Promise<void> {\n if (!this.verifier) {\n return;\n }\n\n for (let i = 0; i < operations.length; i++) {\n const operation = operations[i];\n const signer = operation.action.context?.signer;\n\n if (!signer) {\n continue;\n }\n\n if (signer.signatures.length === 0) {\n throw new InvalidSignatureError(\n documentId,\n `Operation ${operation.id} at index ${operation.index} has signer but no signatures`,\n );\n }\n\n const publicKey = signer.app.key;\n let isValid = false;\n\n try {\n isValid = await this.verifier(operation, publicKey);\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n throw new InvalidSignatureError(\n documentId,\n `Operation ${operation.id} at index ${operation.index} verification failed: ${errorMessage}`,\n );\n }\n\n if (!isValid) {\n throw new InvalidSignatureError(\n documentId,\n `Operation ${operation.id} at index ${operation.index} signature verification returned false`,\n );\n }\n }\n }\n}\n","import type {\n Action,\n DocumentModelModule,\n Operation,\n OperationWithContext,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport { isUndoRedo } from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport type { ICollectionMembershipCache } from \"../cache/collection-membership-cache.js\";\nimport type { IDocumentMetaCache } from \"../cache/document-meta-cache-types.js\";\nimport type {\n IOperationIndex,\n IOperationIndexTxn,\n} from \"../cache/operation-index-types.js\";\nimport type { IWriteCache } from \"../cache/write/interfaces.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport { ReactorEventTypes, type JobWriteReadyEvent } from \"../events/types.js\";\nimport type { Job } from \"../queue/types.js\";\nimport type { IDocumentModelRegistry } from \"../registry/interfaces.js\";\nimport { DocumentDeletedError } from \"../shared/errors.js\";\nimport type { SignatureVerificationHandler } from \"../signer/types.js\";\nimport type { IOperationStore } from \"../storage/interfaces.js\";\nimport { reshuffleByTimestamp } from \"../utils/reshuffle.js\";\nimport { DocumentActionHandler } from \"./document-action-handler.js\";\nimport type { ExecutionStores, IExecutionScope } from \"./execution-scope.js\";\nimport { DefaultExecutionScope } from \"./execution-scope.js\";\nimport type { IJobExecutor } from \"./interfaces.js\";\nimport { SignatureVerifier } from \"./signature-verifier.js\";\nimport type { JobExecutorConfig, JobResult } from \"./types.js\";\nimport { buildErrorResult } from \"./util.js\";\n\nconst MAX_SKIP_THRESHOLD = 1000;\n\ntype ProcessActionsResult = {\n success: boolean;\n generatedOperations: Operation[];\n operationsWithContext: OperationWithContext[];\n error?: Error;\n};\n\nconst documentScopeActions = [\n \"CREATE_DOCUMENT\",\n \"DELETE_DOCUMENT\",\n \"UPGRADE_DOCUMENT\",\n \"ADD_RELATIONSHIP\",\n \"REMOVE_RELATIONSHIP\",\n];\n\n/**\n * Simple job executor that processes a job by applying actions through document model reducers.\n */\nexport class SimpleJobExecutor implements IJobExecutor {\n private config: Required<JobExecutorConfig>;\n private signatureVerifierModule: SignatureVerifier;\n private documentActionHandler: DocumentActionHandler;\n private executionScope: IExecutionScope;\n\n constructor(\n private logger: ILogger,\n private registry: IDocumentModelRegistry,\n private operationStore: IOperationStore,\n private eventBus: IEventBus,\n private writeCache: IWriteCache,\n private operationIndex: IOperationIndex,\n private documentMetaCache: IDocumentMetaCache,\n private collectionMembershipCache: ICollectionMembershipCache,\n config: JobExecutorConfig,\n signatureVerifier?: SignatureVerificationHandler,\n executionScope?: IExecutionScope,\n ) {\n this.config = {\n maxSkipThreshold: config.maxSkipThreshold ?? MAX_SKIP_THRESHOLD,\n maxConcurrency: config.maxConcurrency ?? 1,\n jobTimeoutMs: config.jobTimeoutMs ?? 30000,\n retryBaseDelayMs: config.retryBaseDelayMs ?? 100,\n retryMaxDelayMs: config.retryMaxDelayMs ?? 5000,\n };\n this.signatureVerifierModule = new SignatureVerifier(signatureVerifier);\n this.documentActionHandler = new DocumentActionHandler(registry, logger);\n this.executionScope =\n executionScope ??\n new DefaultExecutionScope(\n operationStore,\n operationIndex,\n writeCache,\n documentMetaCache,\n collectionMembershipCache,\n );\n }\n\n /**\n * Execute a single job by applying all its actions through the appropriate reducers.\n * Actions are processed sequentially in order.\n */\n async executeJob(job: Job, signal?: AbortSignal): Promise<JobResult> {\n const startTime = Date.now();\n\n // Track document IDs touched during execution for cache invalidation on rollback\n const touchedCacheEntries: Array<{\n documentId: string;\n scope: string;\n branch: string;\n }> = [];\n\n let pendingEvent: JobWriteReadyEvent | undefined;\n let result: JobResult;\n try {\n result = await this.executionScope.run(async (stores) => {\n const indexTxn = stores.operationIndex.start();\n\n if (job.kind === \"load\") {\n const loadResult = await this.executeLoadJob(\n job,\n startTime,\n indexTxn,\n stores,\n signal,\n );\n if (loadResult.success && loadResult.operationsWithContext) {\n for (const owc of loadResult.operationsWithContext) {\n touchedCacheEntries.push({\n documentId: owc.context.documentId,\n scope: owc.context.scope,\n branch: owc.context.branch,\n });\n }\n\n const ordinals = await stores.operationIndex.commit(\n indexTxn,\n signal,\n );\n\n for (let i = 0; i < loadResult.operationsWithContext.length; i++) {\n loadResult.operationsWithContext[i].context.ordinal = ordinals[i];\n }\n const collectionMemberships =\n loadResult.operationsWithContext.length > 0\n ? await this.getCollectionMembershipsForOperations(\n loadResult.operationsWithContext,\n stores,\n )\n : {};\n pendingEvent = {\n jobId: job.id,\n operations: loadResult.operationsWithContext,\n jobMeta: job.meta,\n collectionMemberships,\n };\n }\n return loadResult;\n }\n\n const actionResult = await this.processActions(\n job,\n job.actions,\n startTime,\n indexTxn,\n stores,\n undefined,\n undefined,\n \"\",\n signal,\n );\n\n if (!actionResult.success) {\n return {\n job,\n success: false as const,\n error: actionResult.error,\n duration: Date.now() - startTime,\n };\n }\n\n if (actionResult.operationsWithContext.length > 0) {\n for (const owc of actionResult.operationsWithContext) {\n touchedCacheEntries.push({\n documentId: owc.context.documentId,\n scope: owc.context.scope,\n branch: owc.context.branch,\n });\n }\n }\n\n const ordinals = await stores.operationIndex.commit(indexTxn, signal);\n\n if (actionResult.operationsWithContext.length > 0) {\n for (let i = 0; i < actionResult.operationsWithContext.length; i++) {\n actionResult.operationsWithContext[i].context.ordinal = ordinals[i];\n }\n const collectionMemberships =\n await this.getCollectionMembershipsForOperations(\n actionResult.operationsWithContext,\n stores,\n );\n pendingEvent = {\n jobId: job.id,\n operations: actionResult.operationsWithContext,\n jobMeta: job.meta,\n collectionMemberships,\n };\n }\n\n return {\n job,\n success: true as const,\n operations: actionResult.generatedOperations,\n operationsWithContext: actionResult.operationsWithContext,\n duration: Date.now() - startTime,\n };\n }, signal);\n } catch (error) {\n for (const entry of touchedCacheEntries) {\n this.writeCache.invalidate(entry.documentId, entry.scope, entry.branch);\n this.documentMetaCache.invalidate(entry.documentId, entry.branch);\n }\n throw error;\n }\n\n if (pendingEvent) {\n this.eventBus\n .emit(ReactorEventTypes.JOB_WRITE_READY, pendingEvent)\n .catch((error) => {\n this.logger.error(\n \"Failed to emit JOB_WRITE_READY event: @Event : @Error\",\n pendingEvent,\n error,\n );\n });\n }\n\n return result;\n }\n\n private async getCollectionMembershipsForOperations(\n operations: OperationWithContext[],\n stores: ExecutionStores,\n ): Promise<Record<string, string[]>> {\n const documentIds = [\n ...new Set(operations.map((op) => op.context.documentId)),\n ];\n return stores.collectionMembershipCache.getCollectionsForDocuments(\n documentIds,\n );\n }\n\n private async processActions(\n job: Job,\n actions: Action[],\n startTime: number,\n indexTxn: IOperationIndexTxn,\n stores: ExecutionStores,\n skipValues?: number[],\n sourceOperations?: (Operation | undefined)[],\n sourceRemote: string = \"\",\n signal?: AbortSignal,\n ): Promise<ProcessActionsResult> {\n const generatedOperations: Operation[] = [];\n const operationsWithContext: OperationWithContext[] = [];\n\n try {\n await this.signatureVerifierModule.verifyActions(\n job.documentId,\n job.branch,\n actions,\n );\n } catch (error) {\n return {\n success: false,\n generatedOperations,\n operationsWithContext,\n error: error instanceof Error ? error : new Error(String(error)),\n };\n }\n\n for (let actionIndex = 0; actionIndex < actions.length; actionIndex++) {\n const action = actions[actionIndex];\n const skip = skipValues?.[actionIndex] ?? 0;\n const sourceOperation = sourceOperations?.[actionIndex];\n\n const isDocumentAction = documentScopeActions.includes(action.type);\n const result = isDocumentAction\n ? await this.documentActionHandler.execute(\n job,\n action,\n startTime,\n indexTxn,\n stores,\n skip,\n sourceRemote,\n signal,\n )\n : await this.executeRegularAction(\n job,\n action,\n startTime,\n indexTxn,\n stores,\n skip,\n sourceOperation,\n sourceRemote,\n signal,\n );\n\n const error = this.accumulateResultOrReturnError(\n result,\n generatedOperations,\n operationsWithContext,\n );\n if (error !== null) {\n return {\n success: false,\n generatedOperations,\n operationsWithContext,\n error: error.error,\n };\n }\n }\n\n return {\n success: true,\n generatedOperations,\n operationsWithContext,\n };\n }\n\n private async executeRegularAction(\n job: Job,\n action: Action,\n startTime: number,\n indexTxn: IOperationIndexTxn,\n stores: ExecutionStores,\n skip: number = 0,\n sourceOperation?: Operation,\n sourceRemote: string = \"\",\n signal?: AbortSignal,\n ): Promise<\n JobResult & {\n operationsWithContext?: Array<{\n operation: Operation;\n context: {\n documentId: string;\n scope: string;\n branch: string;\n documentType: string;\n };\n }>;\n }\n > {\n let docMeta;\n try {\n docMeta = await stores.documentMetaCache.getDocumentMeta(\n job.documentId,\n job.branch,\n signal,\n );\n } catch (error) {\n return buildErrorResult(\n job,\n error instanceof Error ? error : new Error(String(error)),\n startTime,\n );\n }\n\n if (docMeta.state.isDeleted) {\n return buildErrorResult(\n job,\n new DocumentDeletedError(job.documentId, docMeta.state.deletedAtUtcIso),\n startTime,\n );\n }\n\n // UNDO, REDO, PRUNE, and NOOP+skip need the full operation history to\n // replay state correctly. The write cache stores sliced documents (last\n // op per scope only), so invalidate before loading to force a cold-miss\n // rebuild. NOOP+skip arises in executeLoadJob when sync reshuffling\n // converts conflicting local ops to NOOPs.\n if (\n isUndoRedo(action) ||\n action.type === \"PRUNE\" ||\n (action.type === \"NOOP\" && skip > 0)\n ) {\n stores.writeCache.invalidate(job.documentId, job.scope, job.branch);\n }\n\n let document: PHDocument;\n try {\n document = await stores.writeCache.getState(\n job.documentId,\n job.scope,\n job.branch,\n undefined,\n signal,\n );\n } catch (error) {\n return buildErrorResult(\n job,\n error instanceof Error ? error : new Error(String(error)),\n startTime,\n );\n }\n\n let module: DocumentModelModule;\n try {\n const moduleVersion =\n docMeta.state.version === 0 ? undefined : docMeta.state.version;\n module = this.registry.getModule(\n document.header.documentType,\n moduleVersion,\n );\n } catch (error) {\n return buildErrorResult(\n job,\n error instanceof Error ? error : new Error(String(error)),\n startTime,\n );\n }\n\n let updatedDocument: PHDocument;\n try {\n const protocolVersion =\n document.header.protocolVersions?.[\"base-reducer\"] ?? 1;\n const reducerOptions = sourceOperation\n ? {\n skip,\n branch: job.branch,\n replayOptions: { operation: sourceOperation },\n protocolVersion,\n }\n : { skip, branch: job.branch, protocolVersion };\n updatedDocument = module.reducer(\n document as PHDocument,\n action,\n undefined,\n reducerOptions,\n );\n } catch (error) {\n const contextMessage = `Failed to apply action to document:\\n Action type: ${action.type}\\n Document ID: ${job.documentId}\\n Document type: ${document.header.documentType}\\n Scope: ${job.scope}\\n Original error: ${error instanceof Error ? error.message : String(error)}`;\n const enhancedError = new Error(contextMessage);\n if (error instanceof Error && error.stack) {\n enhancedError.stack = `${contextMessage}\\n\\nOriginal stack trace:\\n${error.stack}`;\n }\n return buildErrorResult(job, enhancedError, startTime);\n }\n\n const scope = job.scope;\n const operations = updatedDocument.operations[scope];\n\n if (operations.length === 0) {\n return buildErrorResult(\n job,\n new Error(\"No operation generated from action\"),\n startTime,\n );\n }\n\n const newOperation = operations[operations.length - 1];\n\n if (!isUndoRedo(action)) {\n newOperation.skip = skip;\n }\n\n const resultingState = JSON.stringify({\n ...updatedDocument.state,\n header: updatedDocument.header,\n });\n\n try {\n await stores.operationStore.apply(\n job.documentId,\n document.header.documentType,\n scope,\n job.branch,\n newOperation.index,\n (txn) => {\n txn.addOperations(newOperation);\n },\n signal,\n );\n } catch (error) {\n this.logger.error(\n \"Error writing @Operation to IOperationStore: @Error\",\n newOperation,\n error,\n );\n\n stores.writeCache.invalidate(job.documentId, scope, job.branch);\n\n return {\n job,\n success: false,\n error: new Error(\n `Failed to write operation to IOperationStore: ${error instanceof Error ? error.message : String(error)}`,\n ),\n duration: Date.now() - startTime,\n };\n }\n\n updatedDocument.header.revision = {\n ...updatedDocument.header.revision,\n [scope]: newOperation.index + 1,\n };\n\n stores.writeCache.putState(\n job.documentId,\n scope,\n job.branch,\n newOperation.index,\n updatedDocument,\n );\n\n indexTxn.write([\n {\n ...newOperation,\n documentId: job.documentId,\n documentType: document.header.documentType,\n branch: job.branch,\n scope,\n sourceRemote,\n },\n ]);\n\n return {\n job,\n success: true,\n operations: [newOperation],\n operationsWithContext: [\n {\n operation: newOperation,\n context: {\n documentId: job.documentId,\n scope,\n branch: job.branch,\n documentType: document.header.documentType,\n resultingState,\n ordinal: 0,\n },\n },\n ],\n duration: Date.now() - startTime,\n };\n }\n\n private async executeLoadJob(\n job: Job,\n startTime: number,\n indexTxn: IOperationIndexTxn,\n stores: ExecutionStores,\n signal?: AbortSignal,\n ): Promise<JobResult> {\n if (job.operations.length === 0) {\n return buildErrorResult(\n job,\n new Error(\"Load job must include at least one operation\"),\n startTime,\n );\n }\n\n let docMeta;\n try {\n docMeta = await stores.documentMetaCache.getDocumentMeta(\n job.documentId,\n job.branch,\n signal,\n );\n } catch {\n // Document meta not found -- continue with load (may be a new document)\n }\n\n if (docMeta?.state.isDeleted) {\n return buildErrorResult(\n job,\n new DocumentDeletedError(job.documentId, docMeta.state.deletedAtUtcIso),\n startTime,\n );\n }\n\n const scope = job.scope;\n\n let latestRevision = 0;\n try {\n const revisions = await stores.operationStore.getRevisions(\n job.documentId,\n job.branch,\n signal,\n );\n latestRevision = revisions.revision[scope] ?? 0;\n } catch {\n latestRevision = 0;\n }\n\n let minIncomingIndex = Number.POSITIVE_INFINITY;\n let minIncomingTimestamp = job.operations[0]?.timestampUtcMs || \"\";\n for (const operation of job.operations) {\n minIncomingIndex = Math.min(minIncomingIndex, operation.index);\n const ts = operation.timestampUtcMs || \"\";\n if (ts < minIncomingTimestamp) {\n minIncomingTimestamp = ts;\n }\n }\n\n let conflictingOps: Operation[] = [];\n try {\n const conflictingResult = await stores.operationStore.getConflicting(\n job.documentId,\n scope,\n job.branch,\n minIncomingTimestamp,\n undefined,\n signal,\n );\n\n conflictingOps = conflictingResult.results;\n } catch {\n conflictingOps = [];\n }\n\n let allOpsFromMinConflictingIndex: Operation[] = conflictingOps;\n if (conflictingOps.length > 0) {\n const minConflictingIndex = Math.min(\n ...conflictingOps.map((op) => op.index),\n );\n try {\n const allOpsResult = await stores.operationStore.getSince(\n job.documentId,\n scope,\n job.branch,\n minConflictingIndex - 1,\n undefined,\n undefined,\n signal,\n );\n allOpsFromMinConflictingIndex = allOpsResult.results;\n } catch {\n allOpsFromMinConflictingIndex = conflictingOps;\n }\n }\n\n const nonSupersededOps = conflictingOps.filter((op) => {\n for (const laterOp of allOpsFromMinConflictingIndex) {\n if (laterOp.index > op.index && laterOp.skip > 0) {\n const logicalIndex = laterOp.index - laterOp.skip;\n if (logicalIndex <= op.index) {\n return false;\n }\n }\n }\n return true;\n });\n\n const existingOpsToReshuffle = nonSupersededOps;\n\n const skipCount = existingOpsToReshuffle.length;\n\n if (skipCount > this.config.maxSkipThreshold) {\n return {\n job,\n success: false,\n error: new Error(\n `Excessive reshuffle detected: skip count of ${skipCount} exceeds threshold of ${this.config.maxSkipThreshold}. ` +\n `This indicates a significant divergence between local and incoming operations.`,\n ),\n duration: Date.now() - startTime,\n };\n }\n\n const existingActionIds = new Set(\n nonSupersededOps.map((op) => op.action.id),\n );\n const seenIncomingActionIds = new Set<string>();\n const incomingOpsToApply = job.operations.filter((op) => {\n if (existingActionIds.has(op.action.id)) return false;\n if (seenIncomingActionIds.has(op.action.id)) return false;\n seenIncomingActionIds.add(op.action.id);\n return true;\n });\n\n if (incomingOpsToApply.length === 0) {\n return {\n job,\n success: true,\n operations: [],\n operationsWithContext: [],\n duration: Date.now() - startTime,\n };\n }\n\n const reshuffledOperations = reshuffleByTimestamp(\n {\n index: latestRevision,\n skip: skipCount,\n },\n existingOpsToReshuffle,\n incomingOpsToApply.map((operation) => ({\n ...operation,\n id: operation.id,\n })),\n );\n\n for (const operation of reshuffledOperations) {\n if (operation.action.type === \"NOOP\") {\n operation.skip = 1;\n }\n }\n\n const actions = reshuffledOperations.map((operation) => operation.action);\n const skipValues = reshuffledOperations.map((operation) => operation.skip);\n\n const effectiveSourceRemote =\n skipCount > 0\n ? \"\" // reshuffle: send to all remotes including source\n : (job.meta.sourceRemote as string) || \"\"; // trivial append: suppress echo to source\n\n const result = await this.processActions(\n job,\n actions,\n startTime,\n indexTxn,\n stores,\n skipValues,\n reshuffledOperations,\n effectiveSourceRemote,\n signal,\n );\n\n if (!result.success) {\n return {\n job,\n success: false,\n error: result.error,\n duration: Date.now() - startTime,\n };\n }\n\n stores.writeCache.invalidate(job.documentId, scope, job.branch);\n\n if (scope === \"document\") {\n stores.documentMetaCache.invalidate(job.documentId, job.branch);\n }\n\n return {\n job,\n success: true,\n operations: result.generatedOperations,\n operationsWithContext: result.operationsWithContext,\n duration: Date.now() - startTime,\n };\n }\n\n private accumulateResultOrReturnError(\n result: JobResult,\n generatedOperations: Operation[],\n operationsWithContext: OperationWithContext[],\n ): JobResult | null {\n if (!result.success) {\n return result;\n }\n if (result.operations && result.operations.length > 0) {\n generatedOperations.push(...result.operations);\n }\n if (result.operationsWithContext) {\n operationsWithContext.push(...result.operationsWithContext);\n }\n return null;\n }\n}\n","import type { IEventBus } from \"../events/interfaces.js\";\nimport {\n ReactorEventTypes,\n type JobFailedEvent,\n type JobReadReadyEvent,\n type JobWriteReadyEvent,\n type Unsubscribe,\n} from \"../events/types.js\";\nimport {\n createConsistencyToken,\n createEmptyConsistencyToken,\n} from \"../executor/util.js\";\nimport type { Job } from \"../queue/types.js\";\nimport type { ErrorInfo } from \"../shared/types.js\";\nimport { JobStatus, type JobInfo } from \"../shared/types.js\";\nimport type { IJobTracker } from \"./interfaces.js\";\n\n/**\n * In-memory implementation of IJobTracker.\n * Maintains job status in a Map for synchronous access.\n * Subscribes to operation events to update job states.\n */\nexport class InMemoryJobTracker implements IJobTracker {\n private jobs = new Map<string, JobInfo>();\n private unsubscribers: Unsubscribe[] = [];\n\n constructor(private eventBus: IEventBus) {\n this.subscribeToEvents();\n }\n\n private subscribeToEvents(): void {\n this.unsubscribers.push(\n this.eventBus.subscribe(\n ReactorEventTypes.JOB_WRITE_READY,\n (_type, event: JobWriteReadyEvent) => {\n this.handleWriteReady(event);\n },\n ),\n );\n\n this.unsubscribers.push(\n this.eventBus.subscribe(\n ReactorEventTypes.JOB_READ_READY,\n (_type, event: JobReadReadyEvent) => {\n this.handleReadReady(event);\n },\n ),\n );\n\n this.unsubscribers.push(\n this.eventBus.subscribe(\n ReactorEventTypes.JOB_FAILED,\n (_type, event: JobFailedEvent) => {\n this.handleJobFailed(event);\n },\n ),\n );\n }\n\n private handleWriteReady(event: JobWriteReadyEvent): void {\n const jobId = event.jobId;\n const job = this.jobs.get(jobId);\n if (job && job.status === JobStatus.RUNNING) {\n const consistencyToken = createConsistencyToken(event.operations);\n this.jobs.set(jobId, {\n ...job,\n status: JobStatus.WRITE_READY,\n consistencyToken,\n });\n }\n }\n\n private handleReadReady(event: JobReadReadyEvent): void {\n const jobId = event.jobId;\n const job = this.jobs.get(jobId);\n if (job && job.status === JobStatus.WRITE_READY) {\n this.jobs.set(jobId, {\n ...job,\n status: JobStatus.READ_READY,\n });\n }\n }\n\n private handleJobFailed(event: JobFailedEvent): void {\n this.markFailed(\n event.jobId,\n {\n message: event.error.message,\n stack: event.error.stack || \"\",\n },\n event.job,\n );\n }\n\n shutdown(): void {\n for (const unsubscribe of this.unsubscribers) {\n unsubscribe();\n }\n this.unsubscribers = [];\n }\n\n registerJob(jobInfo: JobInfo): void {\n this.jobs.set(jobInfo.id, { ...jobInfo });\n }\n\n markRunning(jobId: string): void {\n const job = this.jobs.get(jobId);\n if (!job) {\n // Job not found - might have been registered elsewhere\n // Create minimal job entry\n this.jobs.set(jobId, {\n id: jobId,\n status: JobStatus.RUNNING,\n createdAtUtcIso: new Date().toISOString(),\n consistencyToken: createEmptyConsistencyToken(),\n meta: { batchId: jobId, batchJobIds: [jobId] },\n });\n return;\n }\n\n // Update existing job\n this.jobs.set(jobId, {\n ...job,\n status: JobStatus.RUNNING,\n });\n }\n\n markFailed(jobId: string, error: ErrorInfo, job?: Job): void {\n const existing = this.jobs.get(jobId);\n if (!existing) {\n this.jobs.set(jobId, {\n id: jobId,\n status: JobStatus.FAILED,\n createdAtUtcIso: new Date().toISOString(),\n completedAtUtcIso: new Date().toISOString(),\n error,\n job,\n consistencyToken: createEmptyConsistencyToken(),\n meta: { batchId: jobId, batchJobIds: [jobId] },\n });\n return;\n }\n\n this.jobs.set(jobId, {\n ...existing,\n status: JobStatus.FAILED,\n completedAtUtcIso: new Date().toISOString(),\n error,\n job,\n consistencyToken: createEmptyConsistencyToken(),\n });\n }\n\n getJobStatus(jobId: string): JobInfo | null {\n const job = this.jobs.get(jobId);\n return job ? { ...job } : null;\n }\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { Kysely, Transaction } from \"kysely\";\nimport type { IOperationIndex } from \"../cache/operation-index-types.js\";\nimport type { IWriteCache } from \"../cache/write/interfaces.js\";\nimport type { IConsistencyTracker } from \"../shared/consistency-tracker.js\";\nimport type {\n ConsistencyCoordinate,\n ConsistencyToken,\n} from \"../shared/types.js\";\nimport type { IReadModel } from \"./interfaces.js\";\nimport type { DocumentViewDatabase } from \"./types.js\";\n\nexport type BaseReadModelConfig = {\n readModelId: string;\n rebuildStateOnInit: boolean;\n};\n\n/**\n * Base class for read models that provides catch-up/rewind functionality.\n * Handles initialization, state tracking via ViewState table, and consistency tracking.\n * Subclasses override commitOperations() with their specific domain logic.\n */\nexport class BaseReadModel implements IReadModel {\n protected lastOrdinal: number = 0;\n\n constructor(\n protected db: Kysely<DocumentViewDatabase>,\n protected operationIndex: IOperationIndex,\n protected writeCache: IWriteCache,\n protected consistencyTracker: IConsistencyTracker,\n protected config: BaseReadModelConfig,\n ) {}\n\n /**\n * Initializes the read model by loading state and catching up on missed operations.\n */\n async init(): Promise<void> {\n const viewState = await this.loadState();\n\n if (viewState !== undefined) {\n this.lastOrdinal = viewState;\n const missedOperations = await this.operationIndex.getSinceOrdinal(\n this.lastOrdinal,\n );\n\n if (missedOperations.results.length > 0) {\n const ops = this.config.rebuildStateOnInit\n ? await this.rebuildStateForOperations(missedOperations.results)\n : missedOperations.results;\n await this.indexOperations(ops);\n }\n } else {\n await this.initializeState();\n const allOperations = await this.operationIndex.getSinceOrdinal(0);\n\n if (allOperations.results.length > 0) {\n const ops = this.config.rebuildStateOnInit\n ? await this.rebuildStateForOperations(allOperations.results)\n : allOperations.results;\n await this.indexOperations(ops);\n }\n }\n }\n\n /**\n * Template method: runs domain-specific commitOperations, then persists\n * state and updates consistency tracking.\n */\n async indexOperations(items: OperationWithContext[]): Promise<void> {\n if (items.length === 0) return;\n\n await this.commitOperations(items);\n\n await this.db.transaction().execute(async (trx) => {\n await this.saveState(trx, items);\n });\n\n this.updateConsistencyTracker(items);\n }\n\n /**\n * Waits for the read model to reach the specified consistency level.\n */\n async waitForConsistency(\n token: ConsistencyToken,\n timeoutMs?: number,\n signal?: AbortSignal,\n ): Promise<void> {\n if (token.coordinates.length === 0) {\n return;\n }\n await this.consistencyTracker.waitFor(token.coordinates, timeoutMs, signal);\n }\n\n // Subclass does domain-specific work here (snapshots, relationships, processor routing, etc.).\n protected async commitOperations(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n items: OperationWithContext[],\n ): Promise<void> {}\n\n /**\n * Rebuilds document state for each operation using the write cache.\n */\n protected async rebuildStateForOperations(\n operations: OperationWithContext[],\n ): Promise<OperationWithContext[]> {\n const result: OperationWithContext[] = [];\n\n for (const op of operations) {\n const { documentId, scope, branch } = op.context;\n const targetRevision = op.operation.index;\n\n const document = await this.writeCache.getState(\n documentId,\n scope,\n branch,\n targetRevision,\n );\n\n result.push({\n operation: op.operation,\n context: {\n ...op.context,\n resultingState: JSON.stringify(document),\n },\n });\n }\n\n return result;\n }\n\n /**\n * Loads the last processed ordinal from the ViewState table.\n * Returns undefined if no state exists for this read model.\n */\n protected async loadState(): Promise<number | undefined> {\n const viewStateDb = this.db as unknown as Kysely<DocumentViewDatabase>;\n const row = await viewStateDb\n .selectFrom(\"ViewState\")\n .select(\"lastOrdinal\")\n .where(\"readModelId\", \"=\", this.config.readModelId)\n .executeTakeFirst();\n\n return row?.lastOrdinal;\n }\n\n /**\n * Initializes the ViewState row for this read model.\n */\n protected async initializeState(): Promise<void> {\n const viewStateDb = this.db as unknown as Kysely<DocumentViewDatabase>;\n await viewStateDb\n .insertInto(\"ViewState\")\n .values({\n readModelId: this.config.readModelId,\n lastOrdinal: 0,\n })\n .execute();\n }\n\n /**\n * Saves the last processed ordinal to the ViewState table.\n */\n protected async saveState(\n trx: Transaction<DocumentViewDatabase>,\n items: OperationWithContext[],\n ): Promise<void> {\n const maxOrdinal = Math.max(...items.map((item) => item.context.ordinal));\n this.lastOrdinal = maxOrdinal;\n\n await trx\n .updateTable(\"ViewState\")\n .set({\n lastOrdinal: maxOrdinal,\n lastOperationTimestamp: new Date(),\n })\n .where(\"readModelId\", \"=\", this.config.readModelId)\n .execute();\n }\n\n /**\n * Updates the consistency tracker with the processed operations.\n */\n protected updateConsistencyTracker(items: OperationWithContext[]): void {\n const coordinates: ConsistencyCoordinate[] = [];\n\n for (let i = 0; i < items.length; i++) {\n const item = items[i]!;\n coordinates.push({\n documentId: item.context.documentId,\n scope: item.context.scope,\n branch: item.context.branch,\n operationIndex: item.operation.index,\n });\n }\n\n this.consistencyTracker.update(coordinates);\n }\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { ProcessorFilter } from \"@powerhousedao/shared/processors\";\nimport type { PHDocumentHeader } from \"@powerhousedao/shared/document-model\";\n\nexport const DRIVE_DOCUMENT_TYPE = \"powerhouse/document-drive\";\n\nexport function isDriveCreation(op: OperationWithContext): boolean {\n return (\n op.operation.action.type === \"CREATE_DOCUMENT\" &&\n op.context.documentType === DRIVE_DOCUMENT_TYPE\n );\n}\n\nexport function isDriveDeletion(op: OperationWithContext): boolean {\n return op.operation.action.type === \"DELETE_DOCUMENT\";\n}\n\nexport function extractDriveHeader(\n op: OperationWithContext,\n): PHDocumentHeader | undefined {\n if (!op.context.resultingState) return undefined;\n\n const state = JSON.parse(op.context.resultingState) as Record<\n string,\n unknown\n >;\n return state.header as PHDocumentHeader | undefined;\n}\n\nexport function extractDeletedDocumentId(\n op: OperationWithContext,\n): string | undefined {\n const input = op.operation.action.input as { documentId?: string };\n return input.documentId ?? op.context.documentId;\n}\n\nexport function createMinimalDriveHeader(driveId: string): PHDocumentHeader {\n return {\n id: driveId,\n documentType: DRIVE_DOCUMENT_TYPE,\n sig: {\n publicKey: {},\n nonce: \"\",\n },\n slug: \"\",\n name: \"\",\n branch: \"main\",\n revision: {},\n createdAtUtcIso: new Date().toISOString(),\n lastModifiedAtUtcIso: new Date().toISOString(),\n };\n}\n\nexport function matchesFilter(\n op: OperationWithContext,\n filter: ProcessorFilter,\n): boolean {\n if (filter.documentType && filter.documentType.length > 0) {\n if (!filter.documentType.includes(op.context.documentType)) {\n return false;\n }\n }\n\n if (filter.scope && filter.scope.length > 0) {\n if (!filter.scope.includes(op.context.scope)) {\n return false;\n }\n }\n\n if (filter.branch && filter.branch.length > 0) {\n if (!filter.branch.includes(op.context.branch)) {\n return false;\n }\n }\n\n if (filter.documentId && filter.documentId.length > 0) {\n const hasWildcard = filter.documentId.includes(\"*\");\n if (!hasWildcard && !filter.documentId.includes(op.context.documentId)) {\n return false;\n }\n }\n\n return true;\n}\n","import type {\n OperationWithContext,\n PHDocumentHeader,\n} from \"@powerhousedao/shared/document-model\";\nimport type {\n IProcessor,\n IProcessorManager,\n ProcessorFactory,\n ProcessorRecord,\n TrackedProcessor,\n} from \"@powerhousedao/shared/processors\";\nimport type { ILogger } from \"document-model\";\nimport type { Kysely } from \"kysely\";\nimport type { IOperationIndex } from \"../cache/operation-index-types.js\";\nimport type { IWriteCache } from \"../cache/write/interfaces.js\";\nimport { BaseReadModel } from \"../read-models/base-read-model.js\";\nimport type {\n DocumentViewDatabase,\n ProcessorCursorRow,\n} from \"../read-models/types.js\";\nimport type { IConsistencyTracker } from \"../shared/consistency-tracker.js\";\nimport {\n DRIVE_DOCUMENT_TYPE,\n createMinimalDriveHeader,\n extractDeletedDocumentId,\n extractDriveHeader,\n isDriveCreation,\n isDriveDeletion,\n matchesFilter,\n} from \"./utils.js\";\n\n/**\n * Manages processor lifecycle based on operations.\n * Extends BaseReadModel to receive operations from ReadModelCoordinator.\n *\n * Responsibilities:\n * 1. Detect drive creation from CREATE_DOCUMENT operations\n * 2. Create processors for each drive using registered factories\n * 3. Route operations to matching processors based on filters\n * 4. Clean up processors when drives are deleted or factories are unregistered\n * 5. Track per-processor cursors for failure recovery and backfill\n */\nexport class ProcessorManager\n extends BaseReadModel\n implements IProcessorManager\n{\n private factoryRegistry: Map<string, ProcessorFactory> = new Map();\n private processorsByDrive: Map<string, TrackedProcessor[]> = new Map();\n private factoryToProcessors: Map<string, Map<string, TrackedProcessor[]>> =\n new Map();\n private knownDriveIds: Set<string> = new Set();\n private cursorCache: Map<string, ProcessorCursorRow> = new Map();\n private logger: ILogger;\n\n constructor(\n db: Kysely<DocumentViewDatabase>,\n operationIndex: IOperationIndex,\n writeCache: IWriteCache,\n consistencyTracker: IConsistencyTracker,\n logger: ILogger,\n ) {\n super(db, operationIndex, writeCache, consistencyTracker, {\n readModelId: \"processor-manager\",\n rebuildStateOnInit: true,\n });\n this.logger = logger;\n }\n\n override async init(): Promise<void> {\n await super.init();\n await this.loadAllCursors();\n await this.discoverExistingDrives();\n }\n\n protected override async commitOperations(\n items: OperationWithContext[],\n ): Promise<void> {\n await this.detectAndRegisterNewDrives(items);\n await this.detectAndCleanupDeletedDrives(items);\n await this.routeOperationsToProcessors(items);\n }\n\n async registerFactory(\n identifier: string,\n factory: ProcessorFactory,\n ): Promise<void> {\n if (this.factoryRegistry.has(identifier)) {\n await this.unregisterFactory(identifier);\n }\n\n this.factoryRegistry.set(identifier, factory);\n this.factoryToProcessors.set(identifier, new Map());\n\n for (const driveId of this.knownDriveIds) {\n const driveHeader = createMinimalDriveHeader(driveId);\n await this.createProcessorsForDrive(\n driveId,\n identifier,\n factory,\n driveHeader,\n );\n }\n }\n\n async unregisterFactory(identifier: string): Promise<void> {\n const factoryProcessors = this.factoryToProcessors.get(identifier);\n if (!factoryProcessors) return;\n\n for (const [driveId, tracked] of factoryProcessors) {\n for (const t of tracked) {\n await this.safeDisconnect(t.record.processor);\n }\n\n const driveProcessors = this.processorsByDrive.get(driveId);\n if (driveProcessors) {\n const remaining = driveProcessors.filter((p) => !tracked.includes(p));\n if (remaining.length > 0) {\n this.processorsByDrive.set(driveId, remaining);\n } else {\n this.processorsByDrive.delete(driveId);\n }\n }\n }\n\n await this.deleteProcessorCursors({ factoryId: identifier });\n this.factoryToProcessors.delete(identifier);\n this.factoryRegistry.delete(identifier);\n }\n\n get(processorId: string): TrackedProcessor | undefined {\n for (const tracked of this.allTrackedProcessors()) {\n if (tracked.processorId === processorId) return tracked;\n }\n return undefined;\n }\n\n getAll(): TrackedProcessor[] {\n return Array.from(this.allTrackedProcessors());\n }\n\n private *allTrackedProcessors(): Iterable<TrackedProcessor> {\n for (const tracked of this.processorsByDrive.values()) {\n yield* tracked;\n }\n }\n\n private async detectAndRegisterNewDrives(\n operations: OperationWithContext[],\n ): Promise<void> {\n for (const op of operations) {\n if (!isDriveCreation(op)) continue;\n\n const driveId = op.context.documentId;\n if (this.knownDriveIds.has(driveId)) continue;\n\n this.knownDriveIds.add(driveId);\n\n const driveHeader = extractDriveHeader(op);\n if (!driveHeader) continue;\n\n for (const [identifier, factory] of this.factoryRegistry) {\n await this.createProcessorsForDrive(\n driveId,\n identifier,\n factory,\n driveHeader,\n );\n }\n }\n }\n\n private async detectAndCleanupDeletedDrives(\n operations: OperationWithContext[],\n ): Promise<void> {\n for (const op of operations) {\n if (!isDriveDeletion(op)) continue;\n\n const driveId = extractDeletedDocumentId(op);\n if (!driveId || !this.knownDriveIds.has(driveId)) continue;\n\n if (!this.isDeletedDocumentADrive(driveId)) continue;\n\n await this.cleanupDriveProcessors(driveId);\n this.knownDriveIds.delete(driveId);\n }\n }\n\n private async discoverExistingDrives(): Promise<void> {\n const drives = await this.db\n .selectFrom(\"DocumentSnapshot\")\n .select(\"documentId\")\n .where(\"documentType\", \"=\", DRIVE_DOCUMENT_TYPE)\n .where(\"isDeleted\", \"=\", false)\n .execute();\n\n for (const drive of drives) {\n this.knownDriveIds.add(drive.documentId);\n }\n }\n\n private isDeletedDocumentADrive(documentId: string): boolean {\n return this.knownDriveIds.has(documentId);\n }\n\n private async createProcessorsForDrive(\n driveId: string,\n identifier: string,\n factory: ProcessorFactory,\n driveHeader: PHDocumentHeader,\n ): Promise<void> {\n let records: ProcessorRecord[] = [];\n\n try {\n records = await factory(driveHeader);\n } catch (error) {\n this.logger.error(\n \"Factory '@FactoryId' failed for drive '@DriveId': @Error\",\n identifier,\n driveId,\n error,\n );\n return;\n }\n\n if (records.length === 0) return;\n\n const trackedList: TrackedProcessor[] = [];\n\n for (let i = 0; i < records.length; i++) {\n const record = records[i]!;\n const processorId = `${identifier}:${driveId}:${i}`;\n\n const cached = this.cursorCache.get(processorId);\n let lastOrdinal: number;\n let status: \"active\" | \"errored\";\n let lastError: string | undefined;\n let lastErrorTimestamp: Date | undefined;\n\n if (cached) {\n lastOrdinal = cached.lastOrdinal;\n status = cached.status as \"active\" | \"errored\";\n lastError = cached.lastError ?? undefined;\n lastErrorTimestamp = cached.lastErrorTimestamp ?? undefined;\n } else {\n const startFrom = record.startFrom ?? \"beginning\";\n lastOrdinal = startFrom === \"current\" ? this.lastOrdinal : 0;\n status = \"active\";\n lastError = undefined;\n lastErrorTimestamp = undefined;\n }\n\n const tracked: TrackedProcessor = {\n processorId,\n factoryId: identifier,\n driveId,\n processorIndex: i,\n record,\n lastOrdinal,\n status,\n lastError,\n lastErrorTimestamp,\n retry: () => this.retryProcessor(tracked),\n };\n\n trackedList.push(tracked);\n\n await this.saveProcessorCursor(tracked);\n }\n\n // Clean up orphaned cursor rows from previous runs with more processors\n await this.db\n .deleteFrom(\"ProcessorCursor\")\n .where(\"factoryId\", \"=\", identifier)\n .where(\"driveId\", \"=\", driveId)\n .where(\"processorIndex\", \">=\", records.length)\n .execute();\n\n for (const [id, row] of this.cursorCache) {\n if (\n row.factoryId === identifier &&\n row.driveId === driveId &&\n row.processorIndex >= records.length\n ) {\n this.cursorCache.delete(id);\n }\n }\n\n const factoryProcessors = this.factoryToProcessors.get(identifier);\n if (factoryProcessors) {\n factoryProcessors.set(driveId, trackedList);\n }\n\n const existingDriveProcessors = this.processorsByDrive.get(driveId) ?? [];\n this.processorsByDrive.set(driveId, [\n ...existingDriveProcessors,\n ...trackedList,\n ]);\n\n for (const tracked of trackedList) {\n if (\n tracked.status === \"active\" &&\n tracked.lastOrdinal < this.lastOrdinal\n ) {\n await this.backfillProcessor(tracked);\n }\n }\n }\n\n private async backfillProcessor(tracked: TrackedProcessor): Promise<void> {\n let page = await this.operationIndex.getSinceOrdinal(tracked.lastOrdinal);\n\n while (page.results.length > 0) {\n const matching = page.results.filter((op) =>\n matchesFilter(op, tracked.record.filter),\n );\n\n if (matching.length > 0) {\n try {\n await tracked.record.processor.onOperations(matching);\n } catch (error) {\n tracked.status = \"errored\";\n tracked.lastError =\n error instanceof Error ? error.message : String(error);\n tracked.lastErrorTimestamp = new Date();\n await this.safeSaveProcessorCursor(tracked);\n this.logger.error(\n \"Processor '@ProcessorId' failed during backfill at ordinal @Ordinal: @Error\",\n tracked.processorId,\n tracked.lastOrdinal,\n error,\n );\n return;\n }\n }\n\n const maxOrdinal = Math.max(\n ...page.results.map((op) => op.context.ordinal),\n );\n tracked.lastOrdinal = maxOrdinal;\n await this.safeSaveProcessorCursor(tracked);\n\n if (!page.next) break;\n page = await page.next();\n }\n }\n\n private async retryProcessor(tracked: TrackedProcessor): Promise<void> {\n if (tracked.status !== \"errored\") return;\n tracked.status = \"active\";\n tracked.lastError = undefined;\n tracked.lastErrorTimestamp = undefined;\n await this.saveProcessorCursor(tracked);\n await this.backfillProcessor(tracked);\n }\n\n private async cleanupDriveProcessors(driveId: string): Promise<void> {\n const processors = this.processorsByDrive.get(driveId);\n if (!processors) return;\n\n for (const tracked of processors) {\n await this.safeDisconnect(tracked.record.processor);\n }\n\n this.processorsByDrive.delete(driveId);\n\n for (const factoryProcessors of this.factoryToProcessors.values()) {\n factoryProcessors.delete(driveId);\n }\n\n await this.deleteProcessorCursors({ driveId });\n }\n\n private async safeDisconnect(processor: IProcessor): Promise<void> {\n try {\n await processor.onDisconnect();\n } catch (error) {\n this.logger.error(\"Error disconnecting processor: @Error\", error);\n }\n }\n\n private async routeOperationsToProcessors(\n operations: OperationWithContext[],\n ): Promise<void> {\n const maxOrdinal = Math.max(...operations.map((op) => op.context.ordinal));\n const allTracked = Array.from(this.allTrackedProcessors());\n\n await Promise.all(\n allTracked.map(async (tracked) => {\n if (tracked.status !== \"active\") return;\n\n const unseen = operations.filter(\n (op) => op.context.ordinal > tracked.lastOrdinal,\n );\n const matching = unseen.filter((op) =>\n matchesFilter(op, tracked.record.filter),\n );\n\n if (matching.length > 0) {\n try {\n await tracked.record.processor.onOperations(matching);\n } catch (error) {\n tracked.status = \"errored\";\n tracked.lastError =\n error instanceof Error ? error.message : String(error);\n tracked.lastErrorTimestamp = new Date();\n await this.safeSaveProcessorCursor(tracked);\n this.logger.error(\n \"Processor '@ProcessorId' failed at ordinal @Ordinal: @Error\",\n tracked.processorId,\n tracked.lastOrdinal,\n error,\n );\n return;\n }\n }\n\n tracked.lastOrdinal = maxOrdinal;\n await this.safeSaveProcessorCursor(tracked);\n }),\n );\n }\n\n private async loadAllCursors(): Promise<void> {\n const rows = await this.db\n .selectFrom(\"ProcessorCursor\")\n .selectAll()\n .execute();\n\n for (const row of rows) {\n this.cursorCache.set(row.processorId, row);\n }\n }\n\n private async safeSaveProcessorCursor(\n tracked: TrackedProcessor,\n ): Promise<void> {\n try {\n await this.saveProcessorCursor(tracked);\n } catch (error) {\n this.logger.error(\n \"Failed to persist cursor for '@ProcessorId': @Error\",\n tracked.processorId,\n error,\n );\n }\n }\n\n private async saveProcessorCursor(tracked: TrackedProcessor): Promise<void> {\n await this.db\n .insertInto(\"ProcessorCursor\")\n .values({\n processorId: tracked.processorId,\n factoryId: tracked.factoryId,\n driveId: tracked.driveId,\n processorIndex: tracked.processorIndex,\n lastOrdinal: tracked.lastOrdinal,\n status: tracked.status,\n lastError: tracked.lastError ?? null,\n lastErrorTimestamp: tracked.lastErrorTimestamp ?? null,\n updatedAt: new Date(),\n })\n .onConflict((oc) =>\n oc.column(\"processorId\").doUpdateSet({\n lastOrdinal: tracked.lastOrdinal,\n status: tracked.status,\n lastError: tracked.lastError ?? null,\n lastErrorTimestamp: tracked.lastErrorTimestamp ?? null,\n updatedAt: new Date(),\n }),\n )\n .execute();\n\n this.cursorCache.set(tracked.processorId, {\n processorId: tracked.processorId,\n factoryId: tracked.factoryId,\n driveId: tracked.driveId,\n processorIndex: tracked.processorIndex,\n lastOrdinal: tracked.lastOrdinal,\n status: tracked.status,\n lastError: tracked.lastError ?? null,\n lastErrorTimestamp: tracked.lastErrorTimestamp ?? null,\n createdAt: new Date(),\n updatedAt: new Date(),\n });\n }\n\n private async deleteProcessorCursors(\n filter: { factoryId: string } | { driveId: string },\n ): Promise<void> {\n if (\"factoryId\" in filter) {\n await this.db\n .deleteFrom(\"ProcessorCursor\")\n .where(\"factoryId\", \"=\", filter.factoryId)\n .execute();\n for (const [id, row] of this.cursorCache) {\n if (row.factoryId === filter.factoryId) this.cursorCache.delete(id);\n }\n } else {\n await this.db\n .deleteFrom(\"ProcessorCursor\")\n .where(\"driveId\", \"=\", filter.driveId)\n .execute();\n for (const [id, row] of this.cursorCache) {\n if (row.driveId === filter.driveId) this.cursorCache.delete(id);\n }\n }\n }\n}\n","import type { ErrorInfo } from \"../shared/types.js\";\nimport type { IJobExecutionHandle, Job } from \"./types.js\";\nimport { JobQueueState } from \"./types.js\";\n\n/**\n * Implementation of the IJobExecutionHandle interface\n */\nexport class JobExecutionHandle implements IJobExecutionHandle {\n private _state: JobQueueState;\n private _job: Job;\n private onStart?: () => void;\n private onComplete?: () => void;\n private onFail?: (error: ErrorInfo) => void;\n private onDefer?: () => void;\n\n private getStateName(state: JobQueueState): string {\n switch (state) {\n case JobQueueState.PREPROCESSING:\n return \"PREPROCESSING\";\n case JobQueueState.PENDING:\n return \"PENDING\";\n case JobQueueState.READY:\n return \"READY\";\n case JobQueueState.RUNNING:\n return \"RUNNING\";\n case JobQueueState.RESOLVED:\n return \"RESOLVED\";\n default:\n return `UNKNOWN`;\n }\n }\n\n constructor(\n job: Job,\n initialState: JobQueueState,\n callbacks?: {\n onStart?: () => void;\n onComplete?: () => void;\n onFail?: (error: ErrorInfo) => void;\n onDefer?: () => void;\n },\n ) {\n this._job = job;\n this._state = initialState;\n this.onStart = callbacks?.onStart;\n this.onComplete = callbacks?.onComplete;\n this.onFail = callbacks?.onFail;\n this.onDefer = callbacks?.onDefer;\n }\n\n get job(): Job {\n return this._job;\n }\n\n get state(): JobQueueState {\n return this._state;\n }\n\n start(): void {\n if (this._state !== JobQueueState.READY) {\n throw new Error(\n `Cannot start job in state ${this.getStateName(this._state)}`,\n );\n }\n this._state = JobQueueState.RUNNING;\n this.onStart?.();\n }\n\n complete(): void {\n if (this._state !== JobQueueState.RUNNING) {\n throw new Error(\n `Cannot complete job in state ${this.getStateName(this._state)}`,\n );\n }\n this._state = JobQueueState.RESOLVED;\n this.onComplete?.();\n }\n\n fail(error: ErrorInfo): void {\n if (this._state !== JobQueueState.RUNNING) {\n throw new Error(\n `Cannot fail job in state ${this.getStateName(this._state)}`,\n );\n }\n this._state = JobQueueState.RESOLVED;\n this.onFail?.(error);\n }\n\n defer(): void {\n if (this._state !== JobQueueState.RUNNING) {\n throw new Error(\n `Cannot defer job in state ${this.getStateName(this._state)}`,\n );\n }\n this._state = JobQueueState.RESOLVED;\n this.onDefer?.();\n }\n}\n","import type { CreateDocumentActionInput } from \"@powerhousedao/shared/document-model\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport { ReactorEventTypes } from \"../events/types.js\";\nimport type { IDocumentModelResolver } from \"../registry/document-model-resolver.js\";\nimport type { ErrorInfo } from \"../shared/types.js\";\nimport type { IQueue } from \"./interfaces.js\";\nimport { JobExecutionHandle } from \"./job-execution-handle.js\";\nimport type { IJobExecutionHandle, Job, JobAvailableEvent } from \"./types.js\";\nimport { JobQueueState, QueueEventTypes } from \"./types.js\";\n\n/**\n * In-memory implementation of the IQueue interface.\n * Organizes jobs by documentId, scope, and branch to ensure proper ordering.\n * Ensures serial execution per document by tracking executing jobs.\n * Implements dependency management through queue hints.\n */\nexport class InMemoryQueue implements IQueue {\n private queues = new Map<string, Job[]>();\n private jobIdToQueueKey = new Map<string, string>();\n private docIdToJobId = new Map<string, Set<string>>();\n private jobIdToDocId = new Map<string, string>();\n private completedJobs = new Set<string>();\n private jobIndex = new Map<string, Job>();\n private isBlocked = false;\n private onDrainedCallback?: () => void;\n private isPausedFlag = false;\n\n constructor(\n private eventBus: IEventBus,\n private resolver: IDocumentModelResolver,\n ) {}\n\n private toErrorInfo(error: Error | string): ErrorInfo {\n if (error instanceof Error) {\n return {\n message: error.message,\n stack: error.stack || new Error().stack || \"\",\n };\n }\n return {\n message: error,\n stack: new Error().stack || \"\",\n };\n }\n\n /**\n * Creates a unique key for a document/scope/branch combination\n */\n private createQueueKey(\n documentId: string,\n scope: string,\n branch: string,\n ): string {\n return `${documentId}:${scope}:${branch}`;\n }\n\n /**\n * Gets or creates a queue for the given key\n */\n private getQueue(queueKey: string): Job[] {\n let queue = this.queues.get(queueKey);\n if (!queue) {\n queue = [];\n this.queues.set(queueKey, queue);\n }\n return queue;\n }\n\n /**\n * Check if a document has any jobs currently executing\n */\n private isDocumentExecuting(documentId: string): boolean {\n const executingSet = this.docIdToJobId.get(documentId);\n return executingSet ? executingSet.size > 0 : false;\n }\n\n /**\n * Mark a job as executing for its document\n */\n private markJobExecuting(job: Job): void {\n let executingSet = this.docIdToJobId.get(job.documentId);\n if (!executingSet) {\n executingSet = new Set();\n this.docIdToJobId.set(job.documentId, executingSet);\n }\n executingSet.add(job.id);\n this.jobIdToDocId.set(job.id, job.documentId);\n }\n\n /**\n * Mark a job as no longer executing for its document\n */\n private markJobComplete(jobId: string, documentId: string): void {\n const executingSet = this.docIdToJobId.get(documentId);\n if (executingSet) {\n executingSet.delete(jobId);\n if (executingSet.size === 0) {\n this.docIdToJobId.delete(documentId);\n }\n }\n this.jobIdToDocId.delete(jobId);\n }\n\n /**\n * Check if all dependencies for a job have been completed\n */\n private areDependenciesMet(job: Job): boolean {\n if (job.queueHint.length === 0) {\n return true;\n }\n return job.queueHint.every((depId) => this.completedJobs.has(depId));\n }\n\n /**\n * Get the next job that has all its dependencies met\n */\n private getNextJobWithMetDependencies(queue: Job[]): Job | null {\n for (const job of queue) {\n if (this.areDependenciesMet(job)) {\n return job;\n }\n }\n return null;\n }\n\n private getCreateDocumentType(job: Job): string | undefined {\n for (const action of job.actions) {\n if (action.type === \"CREATE_DOCUMENT\") {\n return (action.input as CreateDocumentActionInput).model;\n }\n }\n for (const operation of job.operations) {\n if (operation.action.type === \"CREATE_DOCUMENT\") {\n return (operation.action.input as CreateDocumentActionInput).model;\n }\n }\n return undefined;\n }\n\n async enqueue(job: Job): Promise<void> {\n // Throw error if queue is blocked\n if (this.isBlocked) {\n throw new Error(\"Queue is blocked\");\n }\n\n const queueKey = this.createQueueKey(job.documentId, job.scope, job.branch);\n const queue = this.getQueue(queueKey);\n\n // Add job to the end of the queue (FIFO)\n queue.push(job);\n\n // Track job location for removal and dependency resolution\n this.jobIdToQueueKey.set(job.id, queueKey);\n this.jobIndex.set(job.id, job);\n\n // Gate CREATE_DOCUMENT jobs on model availability\n const documentType = this.getCreateDocumentType(job);\n if (documentType) {\n try {\n await this.resolver.ensureModelLoaded(documentType);\n } catch {\n await this.failJob(job.id, {\n message: `Failed to load document model for type: ${documentType}`,\n stack: new Error().stack || \"\",\n });\n return;\n }\n }\n\n // Emit job available event\n const eventData: JobAvailableEvent = {\n documentId: job.documentId,\n scope: job.scope,\n branch: job.branch,\n jobId: job.id,\n };\n\n await this.eventBus.emit(QueueEventTypes.JOB_AVAILABLE, eventData);\n }\n\n dequeue(\n documentId: string,\n scope: string,\n branch: string,\n signal?: AbortSignal,\n ): Promise<IJobExecutionHandle | null> {\n const queueKey = this.createQueueKey(documentId, scope, branch);\n const queue = this.queues.get(queueKey);\n\n if (signal?.aborted) {\n return Promise.reject(new Error(\"Operation aborted\"));\n }\n\n if (!queue || queue.length === 0) {\n return Promise.resolve(null);\n }\n\n // Find the first job with met dependencies\n const job = this.getNextJobWithMetDependencies(queue);\n if (!job) {\n return Promise.resolve(null);\n }\n\n // Remove job from queue\n const jobIndex = queue.indexOf(job);\n queue.splice(jobIndex, 1);\n\n // Remove from queue tracking but keep in job index for retry\n this.jobIdToQueueKey.delete(job.id);\n\n // Mark this job as executing for its document\n this.markJobExecuting(job);\n\n // Clean up empty queue\n if (queue.length === 0) {\n this.queues.delete(queueKey);\n }\n\n // Create and return the execution handle\n const handle = new JobExecutionHandle(job, JobQueueState.READY, {\n onStart: () => {\n // Job is now running\n },\n onComplete: () => {\n void this.completeJob(job.id);\n },\n onFail: (error: ErrorInfo) => {\n void this.failJob(job.id, error);\n },\n onDefer: () => {\n void this.deferJob(job.id);\n },\n });\n\n return Promise.resolve(handle);\n }\n\n dequeueNext(signal?: AbortSignal): Promise<IJobExecutionHandle | null> {\n if (signal?.aborted) {\n return Promise.reject(new Error(\"Operation aborted\"));\n }\n\n if (this.isPausedFlag) {\n return Promise.resolve(null);\n }\n\n // Find the first non-empty queue for a document that's not currently executing\n for (const [queueKey, queue] of this.queues.entries()) {\n if (queue.length > 0) {\n // Find the first job with met dependencies\n const job = this.getNextJobWithMetDependencies(queue);\n if (!job) {\n continue; // No job with met dependencies in this queue\n }\n\n // Only dequeue if the document is not currently executing jobs\n if (!this.isDocumentExecuting(job.documentId)) {\n // Remove job from queue\n const jobIdx = queue.indexOf(job);\n queue.splice(jobIdx, 1);\n\n // Remove from queue tracking but keep in job index for retry\n this.jobIdToQueueKey.delete(job.id);\n // Keep job in jobIndex so we can retry it if needed\n\n // Mark this job as executing for its document\n this.markJobExecuting(job);\n\n // Clean up empty queue\n if (queue.length === 0) {\n this.queues.delete(queueKey);\n }\n\n // Create and return the execution handle\n const handle = new JobExecutionHandle(job, JobQueueState.READY, {\n onStart: () => {\n // Job is now running\n },\n onComplete: () => {\n void this.completeJob(job.id);\n },\n onFail: (error: ErrorInfo) => {\n void this.failJob(job.id, error);\n },\n onDefer: () => {\n void this.deferJob(job.id);\n },\n });\n\n return Promise.resolve(handle);\n }\n }\n }\n\n return Promise.resolve(null);\n }\n\n size(documentId: string, scope: string, branch: string): Promise<number> {\n const queueKey = this.createQueueKey(documentId, scope, branch);\n const queue = this.queues.get(queueKey);\n return Promise.resolve(queue ? queue.length : 0);\n }\n\n totalSize(): Promise<number> {\n let total = 0;\n for (const queue of this.queues.values()) {\n total += queue.length;\n }\n return Promise.resolve(total);\n }\n\n remove(jobId: string): Promise<boolean> {\n const queueKey = this.jobIdToQueueKey.get(jobId);\n if (!queueKey) {\n return Promise.resolve(false);\n }\n\n const queue = this.queues.get(queueKey);\n if (!queue) {\n // Clean up orphaned index entry\n this.jobIdToQueueKey.delete(jobId);\n this.jobIndex.delete(jobId);\n return Promise.resolve(false);\n }\n\n const jobIdx = queue.findIndex((job) => job.id === jobId);\n if (jobIdx === -1) {\n // Clean up orphaned index entry\n this.jobIdToQueueKey.delete(jobId);\n this.jobIndex.delete(jobId);\n return Promise.resolve(false);\n }\n\n // Remove job from queue\n queue.splice(jobIdx, 1);\n\n // Remove from job index\n this.jobIdToQueueKey.delete(jobId);\n this.jobIndex.delete(jobId);\n\n // Clean up empty queue\n if (queue.length === 0) {\n this.queues.delete(queueKey);\n }\n\n return Promise.resolve(true);\n }\n\n clear(documentId: string, scope: string, branch: string): Promise<void> {\n const queueKey = this.createQueueKey(documentId, scope, branch);\n const queue = this.queues.get(queueKey);\n\n if (queue) {\n // Remove all jobs from the job index\n for (const job of queue) {\n this.jobIdToQueueKey.delete(job.id);\n this.jobIndex.delete(job.id);\n }\n\n // Remove the queue\n this.queues.delete(queueKey);\n }\n\n return Promise.resolve();\n }\n\n clearAll(): Promise<void> {\n // Clear all job indices\n this.jobIdToQueueKey.clear();\n this.jobIndex.clear();\n this.completedJobs.clear();\n\n // Clear all queues\n this.queues.clear();\n\n return Promise.resolve();\n }\n\n hasJobs(): Promise<boolean> {\n return Promise.resolve(\n this.queues.size > 0 &&\n Array.from(this.queues.values()).some((q) => q.length > 0),\n );\n }\n\n async completeJob(jobId: string): Promise<void> {\n // Get the documentId for the executing job\n const documentId = this.jobIdToDocId.get(jobId);\n if (documentId) {\n // Mark the job as no longer executing\n this.markJobComplete(jobId, documentId);\n }\n\n // Track the job as completed for dependency resolution\n this.completedJobs.add(jobId);\n\n // Remove from job index\n this.jobIndex.delete(jobId);\n\n // For in-memory queue, completing just removes the job\n // In a persistent queue, this would update the job status\n await this.remove(jobId);\n\n // Check if queue is now drained\n this.checkDrained();\n }\n\n async failJob(jobId: string, error?: ErrorInfo): Promise<void> {\n // Get the documentId for the executing job\n const documentId = this.jobIdToDocId.get(jobId);\n if (documentId) {\n // Mark the job as no longer executing\n this.markJobComplete(jobId, documentId);\n }\n\n // update the job lastError and errorHistory\n const job = this.jobIndex.get(jobId);\n if (job) {\n job.lastError = error;\n if (error) {\n job.errorHistory.push(error);\n }\n }\n\n // Remove from job index\n this.jobIndex.delete(jobId);\n\n // Track as completed so dependent jobs are unblocked\n this.completedJobs.add(jobId);\n\n // For in-memory queue, failing just removes the job\n // In a persistent queue, this would update the job status and store the error\n await this.remove(jobId);\n\n // Emit JOB_FAILED so subscribers (sync manager, job tracker, etc.) can react\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId,\n error: new Error(error?.message ?? \"Job failed\"),\n job,\n })\n .catch(() => {});\n\n // Check if queue is now drained\n this.checkDrained();\n }\n\n deferJob(jobId: string): void {\n const documentId = this.jobIdToDocId.get(jobId);\n if (documentId) {\n this.markJobComplete(jobId, documentId);\n }\n this.jobIndex.delete(jobId);\n }\n\n async retryJob(jobId: string, error?: ErrorInfo): Promise<void> {\n // Get the job from the index (it might be executing, not in queue)\n const job = this.jobIndex.get(jobId);\n if (!job) {\n return;\n }\n\n // update the job lastError\n job.lastError = error;\n\n // Mark it as no longer executing if it was\n const documentId = this.jobIdToDocId.get(jobId);\n if (documentId) {\n this.markJobComplete(jobId, documentId);\n }\n\n // Remove from indices\n this.jobIndex.delete(jobId);\n this.jobIdToQueueKey.delete(jobId);\n\n // Add error to history\n if (error) {\n job.errorHistory.push(error);\n }\n\n // Update retry count\n const updatedJob: Job = {\n ...job,\n retryCount: (job.retryCount || 0) + 1,\n lastError: error,\n };\n\n // Re-enqueue with updated retry count\n await this.enqueue(updatedJob);\n }\n\n /**\n * Check if the queue is drained and call the callback if it is\n */\n private checkDrained(): void {\n if (this.isDrained && this.onDrainedCallback) {\n const callback = this.onDrainedCallback;\n this.onDrainedCallback = undefined;\n callback();\n }\n }\n\n /**\n * Returns true if and only if all jobs have been resolved.\n */\n get isDrained(): boolean {\n // Queue is drained if there are no pending jobs and no executing jobs\n const hasPendingJobs =\n this.queues.size > 0 &&\n Array.from(this.queues.values()).some((q) => q.length > 0);\n const hasExecutingJobs =\n this.docIdToJobId.size > 0 &&\n Array.from(this.docIdToJobId.values()).some((set) => set.size > 0);\n\n return !hasPendingJobs && !hasExecutingJobs;\n }\n\n /**\n * Blocks the queue from accepting new jobs.\n * @param onDrained - Optional callback to call when the queue is drained\n */\n block(onDrained?: () => void): void {\n this.isBlocked = true;\n this.onDrainedCallback = onDrained;\n\n // Check if already drained\n this.checkDrained();\n }\n\n /**\n * Unblocks the queue from accepting new jobs.\n */\n unblock(): void {\n this.isBlocked = false;\n this.onDrainedCallback = undefined;\n }\n\n /**\n * Pauses job dequeuing. Jobs can still be enqueued but dequeueNext() will return null.\n */\n pause(): void {\n this.isPausedFlag = true;\n }\n\n /**\n * Resumes job dequeuing and emits JOB_AVAILABLE events for pending jobs to wake up executors.\n */\n async resume(): Promise<void> {\n this.isPausedFlag = false;\n // Emit JOB_AVAILABLE for each queue with pending jobs to wake up the executor manager\n for (const [, queue] of this.queues.entries()) {\n if (queue.length > 0) {\n const job = queue[0];\n await this.eventBus.emit(QueueEventTypes.JOB_AVAILABLE, {\n documentId: job.documentId,\n scope: job.scope,\n branch: job.branch,\n jobId: job.id,\n });\n }\n }\n }\n\n /**\n * Returns whether job dequeuing is paused.\n */\n get paused(): boolean {\n return this.isPausedFlag;\n }\n\n /**\n * Returns all pending jobs across all queues.\n */\n getPendingJobs(): Job[] {\n const jobs: Job[] = [];\n for (const queue of this.queues.values()) {\n jobs.push(...queue);\n }\n return jobs;\n }\n\n /**\n * Returns a map of document IDs to sets of executing job IDs.\n */\n getExecutingJobIds(): Map<string, Set<string>> {\n return new Map(\n Array.from(this.docIdToJobId.entries()).map(([k, v]) => [k, new Set(v)]),\n );\n }\n\n /**\n * Returns a job by ID from the job index.\n */\n getJob(jobId: string): Job | undefined {\n return this.jobIndex.get(jobId);\n }\n}\n","import type { IEventBus } from \"../events/interfaces.js\";\nimport {\n ReactorEventTypes,\n type JobReadReadyEvent,\n type JobWriteReadyEvent,\n type Unsubscribe,\n} from \"../events/types.js\";\nimport type { IReadModel, IReadModelCoordinator } from \"./interfaces.js\";\n\n/**\n * Coordinates read model synchronization by listening to operation write events\n * and updating all registered read models in parallel.\n *\n * This coordinator is responsible for:\n * - Subscribing to OPERATION_WRITTEN events from the event bus\n * - Distributing operation updates to all registered read models\n * - Managing the lifecycle of read model subscriptions\n *\n * Read models are updated asynchronously and in parallel to avoid blocking\n * the write path. Errors in read model updates are propagated through the\n * event bus but do not affect the write operation success.\n */\nexport class ReadModelCoordinator implements IReadModelCoordinator {\n private unsubscribe?: Unsubscribe;\n private isRunning = false;\n\n constructor(\n private eventBus: IEventBus,\n public readonly preReady: IReadModel[],\n public readonly postReady: IReadModel[],\n ) {\n //\n }\n\n /**\n * Start listening for operation events and updating read models.\n * Can be called multiple times safely (subsequent calls are no-ops).\n */\n start(): void {\n if (this.isRunning) {\n return;\n }\n\n // Subscribe to WRITE_READY events\n this.unsubscribe = this.eventBus.subscribe(\n ReactorEventTypes.JOB_WRITE_READY,\n async (type, event: JobWriteReadyEvent) => {\n await this.handleWriteReady(event);\n },\n );\n\n this.isRunning = true;\n }\n\n /**\n * Stop listening and clean up subscriptions.\n * Can be called multiple times safely (subsequent calls are no-ops).\n */\n stop(): void {\n if (!this.isRunning) {\n return;\n }\n\n if (this.unsubscribe) {\n this.unsubscribe();\n this.unsubscribe = undefined;\n }\n\n this.isRunning = false;\n }\n\n /**\n * Handle write ready events by updating all read models in parallel.\n * Errors from individual read models are collected and re-thrown as an aggregate.\n */\n private async handleWriteReady(event: JobWriteReadyEvent): Promise<void> {\n // Index into pre-ready read models in parallel\n await Promise.all(\n this.preReady.map((readModel) =>\n readModel.indexOperations(event.operations),\n ),\n );\n\n // Emit READ_READY event after all pre-ready read models have completed\n const readyEvent: JobReadReadyEvent = {\n jobId: event.jobId,\n operations: event.operations,\n };\n await this.eventBus.emit(ReactorEventTypes.JOB_READ_READY, readyEvent);\n\n // Process post-ready read models (e.g., subscription notifications)\n await Promise.all(\n this.postReady.map((readModel) =>\n readModel.indexOperations(event.operations),\n ),\n );\n }\n}\n","import type {\n OperationWithContext,\n PHDocument,\n PHDocumentHeader,\n} from \"@powerhousedao/shared/document-model\";\nimport type { Kysely } from \"kysely\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport type { IOperationIndex } from \"../cache/operation-index-types.js\";\nimport type { IWriteCache } from \"../cache/write/interfaces.js\";\nimport type { IConsistencyTracker } from \"../shared/consistency-tracker.js\";\nimport type {\n ConsistencyToken,\n PagedResults,\n PagingOptions,\n} from \"../shared/types.js\";\nimport type {\n DocumentRevisions,\n IDocumentView,\n IOperationStore,\n ViewFilter,\n} from \"../storage/interfaces.js\";\nimport type { Database as StorageDatabase } from \"../storage/kysely/types.js\";\nimport { BaseReadModel } from \"./base-read-model.js\";\nimport type {\n DocumentViewDatabase,\n InsertableDocumentSnapshot,\n} from \"./types.js\";\n\ntype Database = StorageDatabase & DocumentViewDatabase;\n\nexport class KyselyDocumentView extends BaseReadModel implements IDocumentView {\n private _db: Kysely<Database>;\n\n constructor(\n db: Kysely<Database>,\n private operationStore: IOperationStore,\n operationIndex: IOperationIndex,\n writeCache: IWriteCache,\n consistencyTracker: IConsistencyTracker,\n ) {\n super(\n db as unknown as Kysely<DocumentViewDatabase>,\n operationIndex,\n writeCache,\n consistencyTracker,\n { readModelId: \"document-view\", rebuildStateOnInit: true },\n );\n this._db = db;\n }\n\n protected override async commitOperations(\n items: OperationWithContext[],\n ): Promise<void> {\n await this._db.transaction().execute(async (trx) => {\n for (const item of items) {\n const { operation, context } = item;\n const { documentId, scope, branch, documentType, resultingState } =\n context;\n const { index, hash } = operation;\n\n if (!resultingState) {\n throw new Error(\n `Missing resultingState in context for operation ${operation.id || \"unknown\"}. ` +\n `IDocumentView requires resultingState from upstream - it does not rebuild documents.`,\n );\n }\n\n let fullState: Record<string, unknown> = {};\n try {\n fullState = JSON.parse(resultingState) as Record<string, unknown>;\n } catch (error) {\n throw new Error(\n `Failed to parse resultingState for operation ${operation.id || \"unknown\"}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n const operationType = operation.action.type;\n\n if (operationType === \"DELETE_DOCUMENT\") {\n const now = new Date();\n await trx\n .updateTable(\"DocumentSnapshot\")\n .set({\n isDeleted: true,\n deletedAt: now,\n lastOperationIndex: index,\n lastOperationHash: hash,\n lastUpdatedAt: now,\n })\n .where(\"documentId\", \"=\", documentId)\n .where(\"branch\", \"=\", branch)\n .execute();\n\n await trx\n .deleteFrom(\"SlugMapping\")\n .where(\"documentId\", \"=\", documentId)\n .where(\"branch\", \"=\", branch)\n .execute();\n\n continue;\n }\n\n let scopesToIndex: Array<[string, unknown]>;\n\n if (operationType === \"CREATE_DOCUMENT\") {\n scopesToIndex = Object.entries(fullState).filter(\n ([key]) => key === \"header\" || key === \"document\" || key === \"auth\",\n );\n } else if (operationType === \"UPGRADE_DOCUMENT\") {\n const scopeStatesToIndex: Array<[string, unknown]> = [];\n\n for (const [scopeName, scopeState] of Object.entries(fullState)) {\n if (scopeName === \"header\") {\n scopeStatesToIndex.push([scopeName, scopeState]);\n continue;\n }\n\n if (scopeName === scope) {\n scopeStatesToIndex.push([scopeName, scopeState]);\n continue;\n }\n\n const existingSnapshot = await trx\n .selectFrom(\"DocumentSnapshot\")\n .select(\"scope\")\n .where(\"documentId\", \"=\", documentId)\n .where(\"scope\", \"=\", scopeName)\n .where(\"branch\", \"=\", branch)\n .executeTakeFirst();\n\n if (!existingSnapshot) {\n scopeStatesToIndex.push([scopeName, scopeState]);\n }\n }\n\n scopesToIndex = scopeStatesToIndex;\n } else {\n scopesToIndex = [];\n\n if (fullState.header !== undefined) {\n scopesToIndex.push([\"header\", fullState.header]);\n }\n\n if (fullState[scope] !== undefined) {\n scopesToIndex.push([scope, fullState[scope]]);\n } else {\n scopesToIndex.push([scope, {}]);\n }\n }\n\n for (const [scopeName, scopeState] of scopesToIndex) {\n const existingSnapshot = await trx\n .selectFrom(\"DocumentSnapshot\")\n .selectAll()\n .where(\"documentId\", \"=\", documentId)\n .where(\"scope\", \"=\", scopeName)\n .where(\"branch\", \"=\", branch)\n .executeTakeFirst();\n\n const newState =\n typeof scopeState === \"object\" && scopeState !== null\n ? (scopeState as Record<string, unknown>)\n : {};\n\n let slug: string | null = existingSnapshot?.slug ?? null;\n let name: string | null = existingSnapshot?.name ?? null;\n\n if (scopeName === \"header\") {\n const headerSlug = newState.slug;\n const headerName = newState.name;\n\n if (typeof headerSlug === \"string\") {\n slug = headerSlug;\n }\n if (typeof headerName === \"string\") {\n name = headerName;\n }\n\n if (slug && slug !== documentId) {\n await trx\n .insertInto(\"SlugMapping\")\n .values({\n slug,\n documentId,\n scope: scopeName,\n branch,\n })\n .onConflict((oc) =>\n oc.column(\"slug\").doUpdateSet({\n documentId,\n scope: scopeName,\n branch,\n }),\n )\n .execute();\n }\n }\n\n if (existingSnapshot) {\n await trx\n .updateTable(\"DocumentSnapshot\")\n .set({\n lastOperationIndex: index,\n lastOperationHash: hash,\n lastUpdatedAt: new Date(),\n snapshotVersion: existingSnapshot.snapshotVersion + 1,\n content: newState,\n slug,\n name,\n })\n .where(\"documentId\", \"=\", documentId)\n .where(\"scope\", \"=\", scopeName)\n .where(\"branch\", \"=\", branch)\n .execute();\n } else {\n const snapshot: InsertableDocumentSnapshot = {\n id: uuidv4(),\n documentId,\n slug,\n name,\n scope: scopeName,\n branch,\n content: newState,\n documentType,\n lastOperationIndex: index,\n lastOperationHash: hash,\n identifiers: null,\n metadata: null,\n deletedAt: null,\n };\n\n await trx.insertInto(\"DocumentSnapshot\").values(snapshot).execute();\n }\n }\n }\n });\n }\n\n async exists(\n documentIds: string[],\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<boolean[]> {\n if (consistencyToken) {\n await this.waitForConsistency(consistencyToken, undefined, signal);\n }\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n if (documentIds.length === 0) {\n return [];\n }\n\n const snapshots = await this._db\n .selectFrom(\"DocumentSnapshot\")\n .select([\"documentId\"])\n .where(\"documentId\", \"in\", documentIds)\n .where(\"isDeleted\", \"=\", false)\n .distinct()\n .execute();\n\n const existingIds = new Set(snapshots.map((s) => s.documentId));\n\n return documentIds.map((id) => existingIds.has(id));\n }\n\n async get<TDocument extends PHDocument>(\n documentId: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n if (consistencyToken) {\n await this.waitForConsistency(consistencyToken, undefined, signal);\n }\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const branch = view?.branch || \"main\";\n\n let scopesToQuery: string[];\n if (view?.scopes && view.scopes.length > 0) {\n scopesToQuery = [...new Set([\"header\", \"document\", ...view.scopes])];\n } else {\n scopesToQuery = [];\n }\n\n let query = this._db\n .selectFrom(\"DocumentSnapshot\")\n .selectAll()\n .where(\"documentId\", \"=\", documentId)\n .where(\"branch\", \"=\", branch)\n .where(\"isDeleted\", \"=\", false);\n\n if (scopesToQuery.length > 0) {\n query = query.where(\"scope\", \"in\", scopesToQuery);\n }\n\n const snapshots = await query.execute();\n\n if (snapshots.length === 0) {\n throw new Error(`Document not found: ${documentId}`);\n }\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const headerSnapshot = snapshots.find((s) => s.scope === \"header\");\n if (!headerSnapshot) {\n throw new Error(`Document header not found: ${documentId}`);\n }\n\n const header = headerSnapshot.content as PHDocumentHeader;\n\n const revisions = await this.operationStore.getRevisions(\n documentId,\n branch,\n signal,\n );\n header.revision = revisions.revision;\n header.lastModifiedAtUtcIso = revisions.latestTimestamp;\n\n const state: Record<string, unknown> = {};\n for (const snapshot of snapshots) {\n if (snapshot.scope === \"header\") {\n continue;\n }\n\n state[snapshot.scope] = snapshot.content;\n }\n\n const document: PHDocument = {\n header,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n state: state as any,\n operations: {},\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n initialState: state as any,\n clipboard: [],\n };\n\n return document as TDocument;\n }\n\n async getMany<TDocument extends PHDocument>(\n documentIds: string[],\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument[]> {\n if (documentIds.length === 0) {\n return [];\n }\n\n if (consistencyToken) {\n await this.waitForConsistency(consistencyToken, undefined, signal);\n }\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const branch = view?.branch || \"main\";\n\n let scopesToQuery: string[];\n if (view?.scopes && view.scopes.length > 0) {\n scopesToQuery = [...new Set([\"header\", \"document\", ...view.scopes])];\n } else {\n scopesToQuery = [];\n }\n\n let query = this._db\n .selectFrom(\"DocumentSnapshot\")\n .selectAll()\n .where(\"documentId\", \"in\", documentIds)\n .where(\"branch\", \"=\", branch)\n .where(\"isDeleted\", \"=\", false);\n\n if (scopesToQuery.length > 0) {\n query = query.where(\"scope\", \"in\", scopesToQuery);\n }\n\n const allSnapshots = await query.execute();\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const snapshotsByDocId = new Map<string, typeof allSnapshots>();\n for (const snapshot of allSnapshots) {\n const existing = snapshotsByDocId.get(snapshot.documentId) || [];\n existing.push(snapshot);\n snapshotsByDocId.set(snapshot.documentId, existing);\n }\n\n const foundDocumentIds = [...snapshotsByDocId.keys()];\n const revisionResults = await Promise.all(\n foundDocumentIds.map((docId) =>\n this.operationStore.getRevisions(docId, branch, signal),\n ),\n );\n\n const revisionsByDocId = new Map<string, DocumentRevisions>();\n for (let i = 0; i < foundDocumentIds.length; i++) {\n revisionsByDocId.set(foundDocumentIds[i], revisionResults[i]);\n }\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const documents: TDocument[] = [];\n for (const documentId of documentIds) {\n const snapshots = snapshotsByDocId.get(documentId);\n if (!snapshots || snapshots.length === 0) {\n continue;\n }\n\n const headerSnapshot = snapshots.find((s) => s.scope === \"header\");\n if (!headerSnapshot) {\n continue;\n }\n\n const header = headerSnapshot.content as PHDocumentHeader;\n const revisions = revisionsByDocId.get(documentId);\n if (revisions) {\n header.revision = revisions.revision;\n header.lastModifiedAtUtcIso = revisions.latestTimestamp;\n }\n\n const state: Record<string, unknown> = {};\n for (const snapshot of snapshots) {\n if (snapshot.scope === \"header\") {\n continue;\n }\n state[snapshot.scope] = snapshot.content;\n }\n\n const document: PHDocument = {\n header,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n state: state as any,\n operations: {},\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n initialState: state as any,\n clipboard: [],\n };\n\n documents.push(document as TDocument);\n }\n\n return documents;\n }\n\n async getByIdOrSlug<TDocument extends PHDocument>(\n identifier: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n const documentId = await this.resolveIdOrSlug(\n identifier,\n view,\n consistencyToken,\n signal,\n );\n return this.get<TDocument>(documentId, view, undefined, signal);\n }\n\n async findByType(\n type: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n if (consistencyToken) {\n await this.waitForConsistency(consistencyToken, undefined, signal);\n }\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const branch = view?.branch || \"main\";\n\n const startIndex = paging?.cursor ? parseInt(paging.cursor) : 0;\n const limit = paging?.limit || 100;\n\n const documents: PHDocument[] = [];\n const processedDocumentIds = new Set<string>();\n const allDocumentIds: string[] = [];\n\n const snapshots = await this._db\n .selectFrom(\"DocumentSnapshot\")\n .selectAll()\n .where(\"documentType\", \"=\", type)\n .where(\"branch\", \"=\", branch)\n .where(\"isDeleted\", \"=\", false)\n .orderBy(\"lastUpdatedAt\", \"desc\")\n .execute();\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n for (const snapshot of snapshots) {\n if (processedDocumentIds.has(snapshot.documentId)) {\n continue;\n }\n\n processedDocumentIds.add(snapshot.documentId);\n allDocumentIds.push(snapshot.documentId);\n }\n\n const docsToFetch = allDocumentIds.slice(startIndex, startIndex + limit);\n\n for (const documentId of docsToFetch) {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n try {\n const document = await this.get<PHDocument>(\n documentId,\n view,\n undefined,\n signal,\n );\n documents.push(document);\n } catch {\n continue;\n }\n }\n\n const hasMore = allDocumentIds.length > startIndex + limit;\n const nextCursor = hasMore ? String(startIndex + limit) : undefined;\n\n return {\n results: documents,\n options: paging || { cursor: \"0\", limit: 100 },\n nextCursor,\n totalCount: allDocumentIds.length,\n next: hasMore\n ? () =>\n this.findByType(\n type,\n view,\n { cursor: nextCursor!, limit },\n consistencyToken,\n signal,\n )\n : undefined,\n };\n }\n\n async resolveSlug(\n slug: string,\n // TODO: this should only be branch\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<string | undefined> {\n if (consistencyToken) {\n await this.waitForConsistency(consistencyToken, undefined, signal);\n }\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const branch = view?.branch || \"main\";\n\n const mapping = await this._db\n .selectFrom(\"SlugMapping\")\n .select(\"documentId\")\n .where(\"slug\", \"=\", slug)\n .where(\"branch\", \"=\", branch)\n .executeTakeFirst();\n\n if (!mapping) {\n return undefined;\n }\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n if (view?.scopes && view.scopes.length > 0) {\n const scopeCheck = await this._db\n .selectFrom(\"DocumentSnapshot\")\n .select(\"scope\")\n .where(\"documentId\", \"=\", mapping.documentId)\n .where(\"branch\", \"=\", branch)\n .where(\"scope\", \"in\", view.scopes)\n .where(\"isDeleted\", \"=\", false)\n .executeTakeFirst();\n\n if (!scopeCheck) {\n return undefined;\n }\n }\n\n return mapping.documentId;\n }\n\n // TODO: fix\n async resolveSlugs(\n slugs: string[],\n // TODO: this should only be branch\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<string[]> {\n const ids = await Promise.all(\n slugs.map((slug) =>\n this.resolveSlug(slug, view, consistencyToken, signal),\n ),\n );\n return ids.filter((id) => id !== undefined) as string[];\n }\n\n async resolveIdOrSlug(\n identifier: string,\n // TODO: this should only be branch\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<string> {\n if (consistencyToken) {\n await this.waitForConsistency(consistencyToken, undefined, signal);\n }\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const branch = view?.branch || \"main\";\n\n const idCheckPromise = this._db\n .selectFrom(\"DocumentSnapshot\")\n .select(\"documentId\")\n .where(\"documentId\", \"=\", identifier)\n .where(\"branch\", \"=\", branch)\n .where(\"isDeleted\", \"=\", false)\n .executeTakeFirst();\n\n const slugCheckPromise = this._db\n .selectFrom(\"SlugMapping\")\n .select(\"documentId\")\n .where(\"slug\", \"=\", identifier)\n .where(\"branch\", \"=\", branch)\n .executeTakeFirst();\n\n const [idMatch, slugMatch] = await Promise.all([\n idCheckPromise,\n slugCheckPromise,\n ]);\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const idMatchDocId = idMatch?.documentId;\n const slugMatchDocId = slugMatch?.documentId;\n\n if (idMatchDocId && slugMatchDocId && idMatchDocId !== slugMatchDocId) {\n throw new Error(\n `Ambiguous identifier \"${identifier}\": matches both document ID \"${idMatchDocId}\" and slug for document ID \"${slugMatchDocId}\". ` +\n `Please use get() for ID or resolveSlug() + get() for slug to be explicit.`,\n );\n }\n\n const resolvedDocumentId = idMatchDocId || slugMatchDocId;\n\n if (!resolvedDocumentId) {\n throw new Error(`Document not found: ${identifier}`);\n }\n\n return resolvedDocumentId;\n }\n}\n","import { DuplicateModuleError, ModuleNotFoundError } from \"./implementation.js\";\nimport type {\n IDocumentModelLoader,\n IDocumentModelRegistry,\n} from \"./interfaces.js\";\n\nexport interface IDocumentModelResolver {\n ensureModelLoaded(documentType: string): Promise<void>;\n}\n\n/**\n * Encapsulates the logic for resolving document model modules on demand.\n * Shared between the queue (CREATE_DOCUMENT gate) and the executor manager\n * (post-failure recovery) so that both paths use the same deduplication\n * and failure-caching state.\n */\nexport class DocumentModelResolver implements IDocumentModelResolver {\n private loadingModels = new Map<string, Promise<void>>();\n private failedModelTypes = new Set<string>();\n\n constructor(\n private registry: IDocumentModelRegistry,\n private loader: IDocumentModelLoader,\n ) {}\n\n async ensureModelLoaded(documentType: string): Promise<void> {\n try {\n this.registry.getModule(documentType);\n return;\n } catch (error) {\n if (!ModuleNotFoundError.isError(error)) {\n throw error;\n }\n }\n\n if (this.failedModelTypes.has(documentType)) {\n throw new Error(\n `Document model type previously failed to load: ${documentType}`,\n );\n }\n\n const existing = this.loadingModels.get(documentType);\n if (existing) {\n return existing;\n }\n\n const loadPromise = (async () => {\n try {\n const module = await this.loader.load(documentType);\n try {\n this.registry.registerModules(module);\n } catch (registerError) {\n if (!DuplicateModuleError.isError(registerError)) {\n throw registerError;\n }\n }\n } catch (error) {\n this.failedModelTypes.add(documentType);\n throw error;\n } finally {\n this.loadingModels.delete(documentType);\n }\n })();\n\n this.loadingModels.set(documentType, loadPromise);\n return loadPromise;\n }\n}\n\n/**\n * No-op resolver used when no document model loader is configured.\n * Checks the registry for the model and returns if found; throws if not.\n * Since there is no loader, missing models cannot be recovered.\n */\nexport class NullDocumentModelResolver implements IDocumentModelResolver {\n constructor(private registry?: IDocumentModelRegistry) {}\n\n ensureModelLoaded(documentType: string): Promise<void> {\n if (this.registry) {\n try {\n this.registry.getModule(documentType);\n return Promise.resolve();\n } catch {\n // fall through to throw below\n }\n }\n\n return Promise.reject(new ModuleNotFoundError(documentType));\n }\n}\n","import type { ConsistencyCoordinate, ConsistencyKey } from \"./types.js\";\n\nexport interface IConsistencyTracker {\n /**\n * Updates the tracker with new operation indexes.\n * When multiple coordinates have the same key, keeps the highest operationIndex.\n * Resolves any pending waiters whose coordinates are now satisfied.\n */\n update(coordinates: ConsistencyCoordinate[]): void;\n\n /**\n * Returns the latest operation index for a given key, or undefined if not tracked.\n */\n getLatest(key: ConsistencyKey): number | undefined;\n\n /**\n * Returns a promise that resolves when all coordinates are satisfied.\n * Rejects if the timeout is reached or the signal is aborted.\n */\n waitFor(\n coordinates: ConsistencyCoordinate[],\n timeoutMs?: number,\n signal?: AbortSignal,\n ): Promise<void>;\n\n /**\n * Returns a serializable snapshot of the current state.\n */\n serialize(): Array<[ConsistencyKey, number]>;\n\n /**\n * Restores state from a serialized snapshot.\n */\n hydrate(entries: Array<[ConsistencyKey, number]>): void;\n}\n\ntype Waiter = {\n coordinates: ConsistencyCoordinate[];\n resolve: () => void;\n reject: (reason: Error) => void;\n signal?: AbortSignal;\n timeoutId?: NodeJS.Timeout;\n};\n\n/**\n * Creates a consistency key from documentId, scope, and branch.\n */\nexport function makeConsistencyKey(\n documentId: string,\n scope: string,\n branch: string,\n): ConsistencyKey {\n return `${documentId}:${scope}:${branch}`;\n}\n\n/**\n * Tracks operation indexes for documents and provides read-after-write consistency guarantees.\n * Maintains an in-memory map of the latest operation index for each (documentId, scope, branch) tuple.\n */\nexport class ConsistencyTracker implements IConsistencyTracker {\n private state = new Map<ConsistencyKey, number>();\n private waiters: Waiter[] = [];\n\n update(coordinates: ConsistencyCoordinate[]): void {\n const deduplicated = this.deduplicateCoordinates(coordinates);\n\n for (let i = 0; i < deduplicated.length; i++) {\n const coord = deduplicated[i]!;\n const key = makeConsistencyKey(\n coord.documentId,\n coord.scope,\n coord.branch,\n );\n const current = this.state.get(key);\n if (current === undefined || coord.operationIndex > current) {\n this.state.set(key, coord.operationIndex);\n }\n }\n\n this.checkWaiters();\n }\n\n getLatest(key: ConsistencyKey): number | undefined {\n return this.state.get(key);\n }\n\n waitFor(\n coordinates: ConsistencyCoordinate[],\n timeoutMs?: number,\n signal?: AbortSignal,\n ): Promise<void> {\n if (signal?.aborted) {\n return Promise.reject(new Error(\"Operation aborted\"));\n }\n\n if (this.areCoordinatesSatisfied(coordinates)) {\n return Promise.resolve();\n }\n\n return new Promise<void>((resolve, reject) => {\n const waiter: Waiter = {\n coordinates,\n resolve,\n reject,\n signal,\n };\n\n if (timeoutMs !== undefined) {\n waiter.timeoutId = setTimeout(() => {\n this.removeWaiter(waiter);\n reject(new Error(`Consistency wait timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n }\n\n if (signal) {\n const abortHandler = () => {\n this.removeWaiter(waiter);\n reject(new Error(\"Operation aborted\"));\n };\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n\n this.waiters.push(waiter);\n });\n }\n\n serialize(): Array<[ConsistencyKey, number]> {\n return Array.from(this.state.entries());\n }\n\n hydrate(entries: Array<[ConsistencyKey, number]>): void {\n this.state.clear();\n for (const [key, index] of entries) {\n this.state.set(key, index);\n }\n }\n\n private deduplicateCoordinates(\n coordinates: ConsistencyCoordinate[],\n ): ConsistencyCoordinate[] {\n const map = new Map<ConsistencyKey, ConsistencyCoordinate>();\n\n for (let i = 0; i < coordinates.length; i++) {\n const coord = coordinates[i]!;\n const key = makeConsistencyKey(\n coord.documentId,\n coord.scope,\n coord.branch,\n );\n const existing = map.get(key);\n\n if (!existing || coord.operationIndex > existing.operationIndex) {\n map.set(key, coord);\n }\n }\n\n return Array.from(map.values());\n }\n\n private areCoordinatesSatisfied(\n coordinates: ConsistencyCoordinate[],\n ): boolean {\n for (let i = 0; i < coordinates.length; i++) {\n const coord = coordinates[i]!;\n const key = makeConsistencyKey(\n coord.documentId,\n coord.scope,\n coord.branch,\n );\n const latest = this.state.get(key);\n if (latest === undefined || latest < coord.operationIndex) {\n return false;\n }\n }\n return true;\n }\n\n private checkWaiters(): void {\n const satisfiedWaiters: Waiter[] = [];\n const unsatisfiedWaiters: Waiter[] = [];\n\n for (const waiter of this.waiters) {\n if (waiter.signal?.aborted) {\n continue;\n }\n\n if (this.areCoordinatesSatisfied(waiter.coordinates)) {\n satisfiedWaiters.push(waiter);\n } else {\n unsatisfiedWaiters.push(waiter);\n }\n }\n\n this.waiters = unsatisfiedWaiters;\n\n for (const waiter of satisfiedWaiters) {\n if (waiter.timeoutId !== undefined) {\n clearTimeout(waiter.timeoutId);\n }\n waiter.resolve();\n }\n }\n\n private removeWaiter(waiter: Waiter): void {\n const index = this.waiters.indexOf(waiter);\n if (index !== -1) {\n this.waiters.splice(index, 1);\n }\n if (waiter.timeoutId !== undefined) {\n clearTimeout(waiter.timeoutId);\n }\n }\n}\n","import type { PagedResults } from \"./types.js\";\n\n/**\n * Collects all results from a paged result set by following the next() function\n * until all pages have been fetched.\n */\nexport async function collectAllPages<T>(\n firstPage: PagedResults<T>,\n signal?: AbortSignal,\n): Promise<T[]> {\n const allResults: T[] = [...firstPage.results];\n let currentPage = firstPage;\n\n while (currentPage.next) {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n currentPage = await currentPage.next();\n allResults.push(...currentPage.results);\n }\n\n return allResults;\n}\n","import type {\n Operation,\n OperationWithContext,\n} from \"@powerhousedao/shared/document-model\";\nimport type { Kysely } from \"kysely\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport type { IOperationIndex } from \"../../cache/operation-index-types.js\";\nimport type { IWriteCache } from \"../../cache/write/interfaces.js\";\nimport { BaseReadModel } from \"../../read-models/base-read-model.js\";\nimport type { DocumentViewDatabase } from \"../../read-models/types.js\";\nimport { collectAllPages } from \"../../shared/collect-all-pages.js\";\nimport type { IConsistencyTracker } from \"../../shared/consistency-tracker.js\";\nimport type {\n ConsistencyToken,\n PagedResults,\n PagingOptions,\n} from \"../../shared/types.js\";\nimport type {\n DocumentGraphEdge,\n DocumentRelationship,\n IDocumentGraph,\n IDocumentIndexer,\n} from \"../interfaces.js\";\nimport type {\n DocumentIndexerDatabase,\n InsertableDocumentRelationship,\n Database as StorageDatabase,\n} from \"./types.js\";\n\nexport type IndexerDatabase = StorageDatabase &\n DocumentIndexerDatabase &\n DocumentViewDatabase;\n\nexport class KyselyDocumentIndexer\n extends BaseReadModel\n implements IDocumentIndexer\n{\n private _db: Kysely<IndexerDatabase>;\n\n constructor(\n db: Kysely<IndexerDatabase>,\n operationIndex: IOperationIndex,\n writeCache: IWriteCache,\n consistencyTracker: IConsistencyTracker,\n ) {\n super(\n db as unknown as Kysely<DocumentViewDatabase>,\n operationIndex,\n writeCache,\n consistencyTracker,\n { readModelId: \"document-indexer\", rebuildStateOnInit: false },\n );\n this._db = db;\n }\n\n protected override async commitOperations(\n items: OperationWithContext[],\n ): Promise<void> {\n await this._db.transaction().execute(async (trx) => {\n for (const item of items) {\n const { operation } = item;\n const actionType = operation.action.type;\n\n if (actionType === \"ADD_RELATIONSHIP\") {\n await this.handleAddRelationship(trx, operation);\n } else if (actionType === \"REMOVE_RELATIONSHIP\") {\n await this.handleRemoveRelationship(trx, operation);\n }\n }\n });\n }\n\n async getOutgoing(\n documentId: string,\n types?: string[],\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentRelationship>> {\n if (consistencyToken) {\n await this.waitForConsistency(consistencyToken, undefined, signal);\n }\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const startIndex = paging?.cursor ? parseInt(paging.cursor) : 0;\n const limit = paging?.limit || 100;\n\n let query = this._db\n .selectFrom(\"DocumentRelationship\")\n .selectAll()\n .where(\"sourceId\", \"=\", documentId);\n\n if (types && types.length > 0) {\n query = query.where(\"relationshipType\", \"in\", types);\n }\n\n const rows = await query\n .orderBy(\"createdAt\", \"asc\")\n .orderBy(\"id\", \"asc\")\n .offset(startIndex)\n .limit(limit + 1)\n .execute();\n\n const hasMore = rows.length > limit;\n const results = hasMore ? rows.slice(0, limit) : rows;\n const nextCursor = hasMore ? String(startIndex + limit) : undefined;\n\n return {\n results: results.map((row) => ({\n sourceId: row.sourceId,\n targetId: row.targetId,\n relationshipType: row.relationshipType,\n metadata: row.metadata\n ? (row.metadata as Record<string, unknown>)\n : undefined,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n })),\n options: paging || { cursor: \"0\", limit: 100 },\n nextCursor,\n next: hasMore\n ? () =>\n this.getOutgoing(\n documentId,\n types,\n { cursor: nextCursor!, limit },\n consistencyToken,\n signal,\n )\n : undefined,\n };\n }\n\n async getIncoming(\n documentId: string,\n types?: string[],\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentRelationship>> {\n if (consistencyToken) {\n await this.waitForConsistency(consistencyToken, undefined, signal);\n }\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const startIndex = paging?.cursor ? parseInt(paging.cursor) : 0;\n const limit = paging?.limit || 100;\n\n let query = this._db\n .selectFrom(\"DocumentRelationship\")\n .selectAll()\n .where(\"targetId\", \"=\", documentId);\n\n if (types && types.length > 0) {\n query = query.where(\"relationshipType\", \"in\", types);\n }\n\n const rows = await query\n .orderBy(\"createdAt\", \"asc\")\n .orderBy(\"id\", \"asc\")\n .offset(startIndex)\n .limit(limit + 1)\n .execute();\n\n const hasMore = rows.length > limit;\n const results = hasMore ? rows.slice(0, limit) : rows;\n const nextCursor = hasMore ? String(startIndex + limit) : undefined;\n\n return {\n results: results.map((row) => ({\n sourceId: row.sourceId,\n targetId: row.targetId,\n relationshipType: row.relationshipType,\n metadata: row.metadata\n ? (row.metadata as Record<string, unknown>)\n : undefined,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n })),\n options: paging || { cursor: \"0\", limit: 100 },\n nextCursor,\n next: hasMore\n ? () =>\n this.getIncoming(\n documentId,\n types,\n { cursor: nextCursor!, limit },\n consistencyToken,\n signal,\n )\n : undefined,\n };\n }\n\n async hasRelationship(\n sourceId: string,\n targetId: string,\n types?: string[],\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<boolean> {\n if (consistencyToken) {\n await this.waitForConsistency(consistencyToken, undefined, signal);\n }\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n let query = this._db\n .selectFrom(\"DocumentRelationship\")\n .select(\"id\")\n .where(\"sourceId\", \"=\", sourceId)\n .where(\"targetId\", \"=\", targetId);\n\n if (types && types.length > 0) {\n query = query.where(\"relationshipType\", \"in\", types);\n }\n\n const result = await query.executeTakeFirst();\n\n return result !== undefined;\n }\n\n async getUndirectedRelationships(\n a: string,\n b: string,\n types?: string[],\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentRelationship>> {\n if (consistencyToken) {\n await this.waitForConsistency(consistencyToken, undefined, signal);\n }\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const startIndex = paging?.cursor ? parseInt(paging.cursor) : 0;\n const limit = paging?.limit || 100;\n\n let query = this._db\n .selectFrom(\"DocumentRelationship\")\n .selectAll()\n .where((eb) =>\n eb.or([\n eb.and([eb(\"sourceId\", \"=\", a), eb(\"targetId\", \"=\", b)]),\n eb.and([eb(\"sourceId\", \"=\", b), eb(\"targetId\", \"=\", a)]),\n ]),\n );\n\n if (types && types.length > 0) {\n query = query.where(\"relationshipType\", \"in\", types);\n }\n\n const rows = await query\n .orderBy(\"createdAt\", \"asc\")\n .orderBy(\"id\", \"asc\")\n .offset(startIndex)\n .limit(limit + 1)\n .execute();\n\n const hasMore = rows.length > limit;\n const results = hasMore ? rows.slice(0, limit) : rows;\n const nextCursor = hasMore ? String(startIndex + limit) : undefined;\n\n return {\n results: results.map((row) => ({\n sourceId: row.sourceId,\n targetId: row.targetId,\n relationshipType: row.relationshipType,\n metadata: row.metadata\n ? (row.metadata as Record<string, unknown>)\n : undefined,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n })),\n options: paging || { cursor: \"0\", limit: 100 },\n nextCursor,\n next: hasMore\n ? () =>\n this.getUndirectedRelationships(\n a,\n b,\n types,\n { cursor: nextCursor!, limit },\n consistencyToken,\n signal,\n )\n : undefined,\n };\n }\n\n async getDirectedRelationships(\n sourceId: string,\n targetId: string,\n types?: string[],\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentRelationship>> {\n if (consistencyToken) {\n await this.waitForConsistency(consistencyToken, undefined, signal);\n }\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const startIndex = paging?.cursor ? parseInt(paging.cursor) : 0;\n const limit = paging?.limit || 100;\n\n let query = this._db\n .selectFrom(\"DocumentRelationship\")\n .selectAll()\n .where(\"sourceId\", \"=\", sourceId)\n .where(\"targetId\", \"=\", targetId);\n\n if (types && types.length > 0) {\n query = query.where(\"relationshipType\", \"in\", types);\n }\n\n const rows = await query\n .orderBy(\"createdAt\", \"asc\")\n .orderBy(\"id\", \"asc\")\n .offset(startIndex)\n .limit(limit + 1)\n .execute();\n\n const hasMore = rows.length > limit;\n const results = hasMore ? rows.slice(0, limit) : rows;\n const nextCursor = hasMore ? String(startIndex + limit) : undefined;\n\n return {\n results: results.map((row) => ({\n sourceId: row.sourceId,\n targetId: row.targetId,\n relationshipType: row.relationshipType,\n metadata: row.metadata\n ? (row.metadata as Record<string, unknown>)\n : undefined,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n })),\n options: paging || { cursor: \"0\", limit: 100 },\n nextCursor,\n next: hasMore\n ? () =>\n this.getDirectedRelationships(\n sourceId,\n targetId,\n types,\n { cursor: nextCursor!, limit },\n consistencyToken,\n signal,\n )\n : undefined,\n };\n }\n\n async findPath(\n sourceId: string,\n targetId: string,\n types?: string[],\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<string[] | null> {\n if (consistencyToken) {\n await this.waitForConsistency(consistencyToken, undefined, signal);\n }\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n if (sourceId === targetId) {\n return [sourceId];\n }\n\n const visited = new Set<string>();\n const queue: Array<{ id: string; path: string[] }> = [\n { id: sourceId, path: [sourceId] },\n ];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n\n if (current.id === targetId) {\n return current.path;\n }\n\n if (visited.has(current.id)) {\n continue;\n }\n\n visited.add(current.id);\n\n const outgoingPage = await this.getOutgoing(\n current.id,\n types,\n undefined,\n consistencyToken,\n signal,\n );\n const outgoingRelationships = await collectAllPages(outgoingPage, signal);\n\n for (const rel of outgoingRelationships) {\n if (!visited.has(rel.targetId)) {\n queue.push({\n id: rel.targetId,\n path: [...current.path, rel.targetId],\n });\n }\n }\n }\n\n return null;\n }\n\n async findAncestors(\n documentId: string,\n types?: string[],\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<IDocumentGraph> {\n if (consistencyToken) {\n await this.waitForConsistency(consistencyToken, undefined, signal);\n }\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const nodes = new Set<string>([documentId]);\n const edges: DocumentGraphEdge[] = [];\n const queue: string[] = [documentId];\n const visited = new Set<string>();\n\n while (queue.length > 0) {\n const currentId = queue.shift()!;\n\n if (visited.has(currentId)) {\n continue;\n }\n\n visited.add(currentId);\n\n const incomingPage = await this.getIncoming(\n currentId,\n types,\n undefined,\n consistencyToken,\n signal,\n );\n const incomingRelationships = await collectAllPages(incomingPage, signal);\n\n for (const rel of incomingRelationships) {\n nodes.add(rel.sourceId);\n edges.push({\n from: rel.sourceId,\n to: rel.targetId,\n type: rel.relationshipType,\n });\n\n if (!visited.has(rel.sourceId)) {\n queue.push(rel.sourceId);\n }\n }\n }\n\n return {\n nodes: Array.from(nodes),\n edges,\n };\n }\n\n async getRelationshipTypes(\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<string[]> {\n if (consistencyToken) {\n await this.waitForConsistency(consistencyToken, undefined, signal);\n }\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const rows = await this._db\n .selectFrom(\"DocumentRelationship\")\n .select(\"relationshipType\")\n .distinct()\n .execute();\n\n return rows.map((row) => row.relationshipType);\n }\n\n private async handleAddRelationship(\n trx: Kysely<IndexerDatabase>,\n operation: Operation,\n ): Promise<void> {\n const input = operation.action.input as {\n sourceId: string;\n targetId: string;\n relationshipType: string;\n metadata?: Record<string, unknown>;\n };\n\n const existingDoc = await trx\n .selectFrom(\"Document\")\n .select(\"id\")\n .where(\"id\", \"=\", input.sourceId)\n .executeTakeFirst();\n\n if (!existingDoc) {\n await trx\n .insertInto(\"Document\")\n .values({\n id: input.sourceId,\n })\n .execute();\n }\n\n const existingTargetDoc = await trx\n .selectFrom(\"Document\")\n .select(\"id\")\n .where(\"id\", \"=\", input.targetId)\n .executeTakeFirst();\n\n if (!existingTargetDoc) {\n await trx\n .insertInto(\"Document\")\n .values({\n id: input.targetId,\n })\n .execute();\n }\n\n const existingRel = await trx\n .selectFrom(\"DocumentRelationship\")\n .select(\"id\")\n .where(\"sourceId\", \"=\", input.sourceId)\n .where(\"targetId\", \"=\", input.targetId)\n .where(\"relationshipType\", \"=\", input.relationshipType)\n .executeTakeFirst();\n\n if (!existingRel) {\n const relationship: InsertableDocumentRelationship = {\n id: uuidv4(),\n sourceId: input.sourceId,\n targetId: input.targetId,\n relationshipType: input.relationshipType,\n metadata: input.metadata || null,\n };\n\n await trx\n .insertInto(\"DocumentRelationship\")\n .values(relationship)\n .execute();\n }\n }\n\n private async handleRemoveRelationship(\n trx: Kysely<IndexerDatabase>,\n operation: Operation,\n ): Promise<void> {\n const input = operation.action.input as {\n sourceId: string;\n targetId: string;\n relationshipType: string;\n };\n\n await trx\n .deleteFrom(\"DocumentRelationship\")\n .where(\"sourceId\", \"=\", input.sourceId)\n .where(\"targetId\", \"=\", input.targetId)\n .where(\"relationshipType\", \"=\", input.relationshipType)\n .execute();\n }\n}\n","import type { PHDocument } from \"@powerhousedao/shared/document-model\";\nimport type { Kysely, Transaction } from \"kysely\";\nimport type { IKeyframeStore } from \"../interfaces.js\";\nimport type { Database } from \"./types.js\";\n\nexport class KyselyKeyframeStore implements IKeyframeStore {\n private trx?: Transaction<Database>;\n\n constructor(private db: Kysely<Database>) {}\n\n private get queryExecutor(): Kysely<Database> | Transaction<Database> {\n return this.trx ?? this.db;\n }\n\n withTransaction(trx: Transaction<Database>): KyselyKeyframeStore {\n const instance = new KyselyKeyframeStore(this.db);\n instance.trx = trx;\n return instance;\n }\n\n async putKeyframe(\n documentId: string,\n scope: string,\n branch: string,\n revision: number,\n document: PHDocument,\n signal?: AbortSignal,\n ): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n await this.queryExecutor\n .insertInto(\"Keyframe\")\n .values({\n documentId,\n documentType: document.header.documentType,\n scope,\n branch,\n revision,\n document,\n })\n .onConflict((oc) =>\n oc\n .columns([\"documentId\", \"scope\", \"branch\", \"revision\"])\n .doUpdateSet({ document }),\n )\n .execute();\n }\n\n async findNearestKeyframe(\n documentId: string,\n scope: string,\n branch: string,\n targetRevision: number,\n signal?: AbortSignal,\n ): Promise<{ revision: number; document: PHDocument } | undefined> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const row = await this.queryExecutor\n .selectFrom(\"Keyframe\")\n .selectAll()\n .where(\"documentId\", \"=\", documentId)\n .where(\"scope\", \"=\", scope)\n .where(\"branch\", \"=\", branch)\n .where(\"revision\", \"<=\", targetRevision)\n .orderBy(\"revision\", \"desc\")\n .limit(1)\n .executeTakeFirst();\n\n if (!row) {\n return undefined;\n }\n\n return {\n revision: row.revision,\n document: row.document as PHDocument,\n };\n }\n\n async listKeyframes(\n documentId: string,\n scope?: string,\n branch?: string,\n signal?: AbortSignal,\n ): Promise<\n Array<{\n scope: string;\n branch: string;\n revision: number;\n document: PHDocument;\n }>\n > {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n let query = this.queryExecutor\n .selectFrom(\"Keyframe\")\n .selectAll()\n .where(\"documentId\", \"=\", documentId)\n .orderBy(\"revision\", \"asc\");\n\n if (scope !== undefined) {\n query = query.where(\"scope\", \"=\", scope);\n }\n if (branch !== undefined) {\n query = query.where(\"branch\", \"=\", branch);\n }\n\n const rows = await query.execute();\n\n return rows.map((row) => ({\n scope: row.scope,\n branch: row.branch,\n revision: row.revision,\n document: row.document as PHDocument,\n }));\n }\n\n async deleteKeyframes(\n documentId: string,\n scope?: string,\n branch?: string,\n signal?: AbortSignal,\n ): Promise<number> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n let query = this.queryExecutor\n .deleteFrom(\"Keyframe\")\n .where(\"documentId\", \"=\", documentId);\n\n if (scope !== undefined && branch !== undefined) {\n query = query.where(\"scope\", \"=\", scope).where(\"branch\", \"=\", branch);\n } else if (scope !== undefined) {\n query = query.where(\"scope\", \"=\", scope);\n }\n\n const result = await query.executeTakeFirst();\n\n return Number(result.numDeletedRows || 0n);\n }\n}\n","import type {\n Operation,\n OperationWithContext,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport type {\n ConsistencyToken,\n PagedResults,\n PagingOptions,\n} from \"../shared/types.js\";\nimport type { ChannelErrorSource } from \"../sync/types.js\";\nimport type { RemoteCursor, RemoteRecord } from \"../sync/types.js\";\n\nexport type { PagedResults, PagingOptions } from \"../shared/types.js\";\n\n/**\n * Thrown when an operation with the same identity already exists in the store.\n */\nexport class DuplicateOperationError extends Error {\n constructor(description: string) {\n super(`Duplicate operation: ${description}`);\n this.name = \"DuplicateOperationError\";\n }\n}\n\n/**\n * Thrown when a concurrent write conflict is detected during an atomic apply.\n */\nexport class OptimisticLockError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"OptimisticLockError\";\n }\n}\n\n/**\n * Thrown when the caller-provided revision does not match the current\n * stored revision, indicating a stale read.\n */\nexport class RevisionMismatchError extends Error {\n constructor(expected: number, actual: number) {\n super(`Revision mismatch: expected ${expected}, got ${actual}`);\n this.name = \"RevisionMismatchError\";\n }\n}\n\n/**\n * A write transaction passed to {@link IOperationStore.apply}. Accumulates\n * operations that are committed atomically when the callback returns.\n */\nexport interface AtomicTxn {\n /** Stages one or more operations to be written as part of this transaction. */\n addOperations(...operations: Operation[]): void;\n}\n\n/**\n * Per-scope revision map for a document, used to reconstruct the header\n * revision field and lastModified timestamp.\n */\nexport type DocumentRevisions = {\n /** Map of scope to operation index for that scope */\n revision: Record<string, number>;\n\n /** Latest timestamp across revisions */\n latestTimestamp: string;\n};\n\n/**\n * Append-only store for document operations. Operations are partitioned by\n * (documentId, scope, branch) and ordered by a monotonic revision index.\n */\nexport interface IOperationStore {\n /**\n * Atomically appends operations for a single document/scope/branch.\n * The provided revision must match the current head; otherwise a\n * {@link RevisionMismatchError} is thrown.\n *\n * @param documentId - The document id\n * @param documentType - The document type identifier\n * @param scope - The operation scope (e.g. \"global\", \"local\")\n * @param branch - The branch name\n * @param revision - Expected current revision (optimistic lock)\n * @param fn - Callback that stages operations via {@link AtomicTxn}\n * @param signal - Optional abort signal to cancel the request\n */\n apply(\n documentId: string,\n documentType: string,\n scope: string,\n branch: string,\n revision: number,\n fn: (txn: AtomicTxn) => void | Promise<void>,\n signal?: AbortSignal,\n ): Promise<void>;\n\n /**\n * Returns operations for a document/scope/branch whose index is greater\n * than the given revision.\n *\n * @param documentId - The document id\n * @param scope - The operation scope\n * @param branch - The branch name\n * @param revision - Return operations after this revision index\n * @param filter - Optional filters (action types, timestamp range)\n * @param paging - Optional paging options for cursor-based pagination\n * @param signal - Optional abort signal to cancel the request\n */\n getSince(\n documentId: string,\n scope: string,\n branch: string,\n revision: number,\n filter?: OperationFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<Operation>>;\n\n /**\n * Returns operations across all documents whose auto-increment store id\n * is greater than the given id. Used by read models and sync to catch up\n * on operations they may have missed.\n *\n * @param id - Return operations with store id greater than this value\n * @param paging - Optional paging options for cursor-based pagination\n * @param signal - Optional abort signal to cancel the request\n */\n getSinceId(\n id: number,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<OperationWithContext>>;\n\n /**\n * Gets operations that may conflict with incoming operations during a load.\n *\n * @param documentId - The document id\n * @param scope - The scope to query\n * @param branch - The branch name\n * @param minTimestamp - Minimum timestamp (inclusive) as ISO string\n * @param paging - Optional paging options for cursor-based pagination\n * @param signal - Optional abort signal to cancel the request\n * @returns Paged results of operations that may conflict\n */\n getConflicting(\n documentId: string,\n scope: string,\n branch: string,\n minTimestamp: string,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<Operation>>;\n\n /**\n * Gets the latest operation index for each scope of a document, along with\n * the latest timestamp across all scopes. This is used to efficiently reconstruct\n * the revision map and lastModified timestamp for document headers.\n *\n * @param documentId - The document id\n * @param branch - The branch name\n * @param signal - Optional abort signal to cancel the request\n * @returns Object containing revision map and latest timestamp\n */\n getRevisions(\n documentId: string,\n branch: string,\n signal?: AbortSignal,\n ): Promise<DocumentRevisions>;\n}\n\n/**\n * Stores periodic document snapshots (keyframes) so that document state\n * can be reconstructed without replaying the full operation history.\n */\nexport interface IKeyframeStore {\n /**\n * Stores a document snapshot at a specific revision.\n *\n * @param documentId - The document id\n * @param scope - The operation scope\n * @param branch - The branch name\n * @param revision - The operation index this snapshot corresponds to\n * @param document - The full document state to persist\n * @param signal - Optional abort signal to cancel the request\n */\n putKeyframe(\n documentId: string,\n scope: string,\n branch: string,\n revision: number,\n document: PHDocument,\n signal?: AbortSignal,\n ): Promise<void>;\n\n /**\n * Finds the keyframe closest to (but not exceeding) the target revision.\n * Returns undefined if no keyframe exists for this document/scope/branch.\n *\n * @param documentId - The document id\n * @param scope - The operation scope\n * @param branch - The branch name\n * @param targetRevision - The desired revision upper bound\n * @param signal - Optional abort signal to cancel the request\n */\n findNearestKeyframe(\n documentId: string,\n scope: string,\n branch: string,\n targetRevision: number,\n signal?: AbortSignal,\n ): Promise<{ revision: number; document: PHDocument } | undefined>;\n\n /**\n * Lists all keyframes for a document, optionally filtered by scope and branch.\n *\n * @param documentId - The document id\n * @param scope - Optional scope filter\n * @param branch - Optional branch filter\n * @param signal - Optional abort signal to cancel the request\n */\n listKeyframes(\n documentId: string,\n scope?: string,\n branch?: string,\n signal?: AbortSignal,\n ): Promise<\n Array<{\n scope: string;\n branch: string;\n revision: number;\n document: PHDocument;\n }>\n >;\n\n /**\n * Deletes keyframes for a document. Optionally scoped to a specific\n * scope and/or branch.\n *\n * @param documentId - The document id\n * @param scope - Optional scope filter; omit to delete across all scopes\n * @param branch - Optional branch filter; omit to delete across all branches\n * @param signal - Optional abort signal to cancel the request\n * @returns The number of keyframes deleted\n */\n deleteKeyframes(\n documentId: string,\n scope?: string,\n branch?: string,\n signal?: AbortSignal,\n ): Promise<number>;\n}\n\n/**\n * Filters applied when reading document state from {@link IDocumentView}.\n */\nexport interface ViewFilter {\n /** Branch to read from. Defaults to the main branch when omitted. */\n branch?: string;\n /** Scopes to include. When omitted, all scopes are included. */\n scopes?: string[];\n /** Exclude operations originating from this remote name. */\n excludeSourceRemote?: string;\n}\n\n/**\n * Criteria for searching documents in storage-backed read models.\n * All provided fields are combined with AND logic.\n */\nexport interface SearchFilter {\n /** Filter by document type identifier. */\n documentType?: string;\n /** Filter by parent document id. */\n parentId?: string;\n /** Filter by arbitrary key-value identifiers stored on the document. */\n identifiers?: Record<string, any>;\n /** When true, include soft-deleted documents in results. */\n includeDeleted?: boolean;\n}\n\n/**\n * Filter options for querying operations. When multiple filters are provided,\n * they are combined with AND logic.\n */\nexport interface OperationFilter {\n /** Filter by action types (OR logic within array) */\n actionTypes?: string[];\n /** Filter operations with timestamp >= this value (ISO string) */\n timestampFrom?: string;\n /** Filter operations with timestamp <= this value (ISO string) */\n timestampTo?: string;\n /** Filter operations with index >= this value */\n sinceRevision?: number;\n}\n\n/**\n * Materialised read model that maintains document snapshots. Snapshots are\n * updated by indexing operations (which must include `resultingState`) and\n * queried with optional consistency tokens for read-after-write guarantees.\n */\nexport interface IDocumentView {\n /**\n * Initializes the view.\n */\n init(): Promise<void>;\n\n /**\n * Indexes a list of operations.\n *\n * @param items - Operations with context. Context MUST include ephemeral\n * `resultingState` for optimization. IDocumentView never rebuilds\n * documents from operations - it always requires resultingState.\n */\n indexOperations(items: OperationWithContext[]): Promise<void>;\n\n /**\n * Blocks until the view has processed the coordinates referenced by the\n * provided consistency token.\n *\n * @param token - Consistency token derived from the originating job\n * @param timeoutMs - Optional timeout window in milliseconds\n * @param signal - Optional abort signal to cancel the wait\n */\n waitForConsistency(\n token: ConsistencyToken,\n timeoutMs?: number,\n signal?: AbortSignal,\n ): Promise<void>;\n\n /**\n * Returns true if and only if the documents exist.\n *\n * @param documentIds - The list of document ids to check.\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n */\n exists(\n documentIds: string[],\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<boolean[]>;\n\n /**\n * Returns the document with the given id.\n *\n * @param documentId - The id of the document to get.\n * @param view - Optional filter containing branch and scopes information\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n */\n get<TDocument extends PHDocument>(\n documentId: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Returns the documents with the given ids.\n *\n * @param documentIds - The list of document ids to get.\n * @param view - Optional filter containing branch and scopes information\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n */\n getMany<TDocument extends PHDocument>(\n documentIds: string[],\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument[]>;\n\n /**\n * Returns the document with the given identifier (either id or slug).\n * Throws an error if the identifier matches both an id and a slug that refer to different documents.\n *\n * @param identifier - The id or slug of the document to get.\n * @param view - Optional filter containing branch and scopes information\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @throws {Error} If identifier matches both an ID and slug referring to different documents\n */\n getByIdOrSlug<TDocument extends PHDocument>(\n identifier: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Finds documents by their document type.\n *\n * @param type - The document type to search for\n * @param view - Optional filter containing branch and scopes information\n * @param paging - Optional paging options for cursor-based pagination\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n */\n findByType(\n type: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>>;\n\n /**\n * Resolves a slug to a document ID.\n *\n * @param slug - The slug to resolve\n * @param view - Optional filter containing branch and scopes information\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The document ID or undefined if the slug doesn't exist\n */\n resolveSlug(\n slug: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<string | undefined>;\n\n /**\n * Resolves a list of slugs to document IDs.\n *\n * @param slugs - The list of slugs to resolve.\n * @param view - Optional filter containing branch and scopes information\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The list of document IDs\n */\n resolveSlugs(\n slugs: string[],\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<string[]>;\n\n /**\n * Resolves an identifier (either id or slug) to a document ID.\n * This is a lightweight alternative to getByIdOrSlug that returns just the ID\n * without fetching the full document.\n *\n * @param identifier - The id or slug to resolve\n * @param view - Optional filter containing branch and scopes information\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The document ID\n * @throws {Error} If document not found or identifier matches both an ID and slug referring to different documents\n */\n resolveIdOrSlug(\n identifier: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<string>;\n}\n\n/**\n * A directed relationship between two documents in the document graph.\n */\nexport type DocumentRelationship = {\n sourceId: string;\n targetId: string;\n relationshipType: string;\n metadata?: Record<string, unknown>;\n createdAt: Date;\n updatedAt: Date;\n};\n\n/**\n * A lightweight directed edge in a {@link IDocumentGraph}.\n */\nexport type DocumentGraphEdge = {\n from: string;\n to: string;\n type: string;\n};\n\n/**\n * A subgraph of the document relationship graph, returned by traversal\n * queries such as {@link IDocumentIndexer.findAncestors}.\n */\nexport interface IDocumentGraph {\n nodes: string[];\n edges: DocumentGraphEdge[];\n}\n\n/**\n * Read model that maintains a directed graph of document relationships.\n * Relationships are created and removed by indexing operations containing\n * ADD_RELATIONSHIP and REMOVE_RELATIONSHIP actions.\n */\nexport interface IDocumentIndexer {\n /**\n * Initializes the indexer and catches up on any missed operations.\n */\n init(): Promise<void>;\n\n /**\n * Indexes a list of operations to update the relationship graph.\n *\n * @param operations - Operations to index. Will process ADD_RELATIONSHIP and\n * REMOVE_RELATIONSHIP operations.\n */\n indexOperations(operations: OperationWithContext[]): Promise<void>;\n\n /**\n * Blocks until the indexer has processed the coordinates referenced by the\n * provided consistency token.\n *\n * @param token - Consistency token derived from the originating job\n * @param timeoutMs - Optional timeout window in milliseconds\n * @param signal - Optional abort signal to cancel the wait\n */\n waitForConsistency(\n token: ConsistencyToken,\n timeoutMs?: number,\n signal?: AbortSignal,\n ): Promise<void>;\n\n /**\n * Returns outgoing relationships from a document.\n *\n * @param documentId - The source document id\n * @param types - Optional filter by relationship types\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n */\n getOutgoing(\n documentId: string,\n types?: string[],\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentRelationship>>;\n\n /**\n * Returns incoming relationships to a document.\n *\n * @param documentId - The target document id\n * @param types - Optional filter by relationship types\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n */\n getIncoming(\n documentId: string,\n types?: string[],\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentRelationship>>;\n\n /**\n * Checks if a relationship exists between two documents.\n *\n * @param sourceId - The source document id\n * @param targetId - The target document id\n * @param types - Optional filter by relationship types\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n */\n hasRelationship(\n sourceId: string,\n targetId: string,\n types?: string[],\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<boolean>;\n\n /**\n * Returns all undirected relationships between two documents.\n *\n * @param a - The ID of the first document\n * @param b - The ID of the second document\n * @param types - Optional filter by relationship types\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n */\n getUndirectedRelationships(\n a: string,\n b: string,\n types?: string[],\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentRelationship>>;\n\n /**\n * Returns all directed relationships between two documents.\n *\n * @param sourceId - The source document id\n * @param targetId - The target document id\n * @param types - Optional filter by relationship types\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n */\n getDirectedRelationships(\n sourceId: string,\n targetId: string,\n types?: string[],\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentRelationship>>;\n\n /**\n * Finds a path from source to target following directed edges.\n *\n * @param sourceId - The source document id\n * @param targetId - The target document id\n * @param types - Optional filter by relationship types\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns Array of document ids representing the path, or null if no path exists\n */\n findPath(\n sourceId: string,\n targetId: string,\n types?: string[],\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<string[] | null>;\n\n /**\n * Returns all ancestors of a document in the relationship graph.\n *\n * @param documentId - The document id\n * @param types - Optional filter by relationship types\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n */\n findAncestors(\n documentId: string,\n types?: string[],\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<IDocumentGraph>;\n\n /**\n * Returns all relationship types currently in the system.\n *\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n */\n getRelationshipTypes(\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<string[]>;\n}\n\n/**\n * Persistent storage for sync remote configurations. Each remote represents\n * a connection to an external system that operations can be synced with.\n */\nexport interface ISyncRemoteStorage {\n /**\n * Lists all remotes.\n *\n * @param signal - Optional abort signal to cancel the request\n * @returns The remotes\n */\n list(signal?: AbortSignal): Promise<RemoteRecord[]>;\n\n /**\n * Gets a remote by name.\n *\n * @param name - The name of the remote\n * @param signal - Optional abort signal to cancel the request\n * @returns The remote\n */\n get(name: string, signal?: AbortSignal): Promise<RemoteRecord>;\n\n /**\n * Upserts a remote.\n *\n * @param remote - The remote to upsert\n * @param signal - Optional abort signal to cancel the request\n * @returns The remote\n */\n upsert(remote: RemoteRecord, signal?: AbortSignal): Promise<void>;\n\n /**\n * Removes a remote by name.\n *\n * @param name - The name of the remote\n * @param signal - Optional abort signal to cancel the request\n * @returns The remote\n */\n remove(name: string, signal?: AbortSignal): Promise<void>;\n}\n\n/**\n * Persistent storage for sync cursors that track inbox/outbox progress\n * per remote. Cursors allow sync to resume from where it left off.\n */\nexport interface ISyncCursorStorage {\n /**\n * Lists all cursors for a remote.\n *\n * @param remoteName - The name of the remote\n * @param signal - Optional abort signal to cancel the request\n * @returns The cursors\n */\n list(remoteName: string, signal?: AbortSignal): Promise<RemoteCursor[]>;\n\n /**\n * Gets a cursor for a remote.\n *\n * @param remoteName - The name of the remote\n * @param cursorType - The type of cursor (\"inbox\" or \"outbox\")\n * @param signal - Optional abort signal to cancel the request\n * @returns The cursor\n */\n get(\n remoteName: string,\n cursorType: \"inbox\" | \"outbox\",\n signal?: AbortSignal,\n ): Promise<RemoteCursor>;\n\n /**\n * Upserts a cursor.\n *\n * @param cursor - The cursor to upsert\n * @param signal - Optional abort signal to cancel the request\n * @returns The cursor\n */\n upsert(cursor: RemoteCursor, signal?: AbortSignal): Promise<void>;\n\n /**\n * Removes a cursor for a remote.\n *\n * @param remoteName - The name of the remote\n * @param signal - Optional abort signal to cancel the request\n * @returns The cursor\n */\n remove(remoteName: string, signal?: AbortSignal): Promise<void>;\n}\n\n/**\n * Serializable snapshot of a permanently failed SyncOperation.\n */\nexport type DeadLetterRecord = {\n id: string;\n jobId: string;\n jobDependencies: string[];\n remoteName: string;\n documentId: string;\n scopes: string[];\n branch: string;\n operations: OperationWithContext[];\n errorSource: ChannelErrorSource;\n errorMessage: string;\n};\n\n/**\n * Persists dead-lettered sync operations so they survive reactor restarts.\n */\nexport interface ISyncDeadLetterStorage {\n /**\n * Lists dead letters for a remote, ordered by ordinal DESC (newest first).\n *\n * @param remoteName - The name of the remote\n * @param paging - Optional paging options (cursor + limit)\n * @param signal - Optional abort signal to cancel the request\n * @returns Paged dead letter records\n */\n list(\n remoteName: string,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<DeadLetterRecord>>;\n\n /**\n * Adds a dead letter. Duplicate ids are silently ignored.\n *\n * @param deadLetter - The dead letter record to persist\n * @param signal - Optional abort signal to cancel the request\n */\n add(deadLetter: DeadLetterRecord, signal?: AbortSignal): Promise<void>;\n\n /**\n * Removes a single dead letter by id.\n *\n * @param id - The dead letter id\n * @param signal - Optional abort signal to cancel the request\n */\n remove(id: string, signal?: AbortSignal): Promise<void>;\n\n /**\n * Removes all dead letters for a remote.\n *\n * @param remoteName - The name of the remote\n * @param signal - Optional abort signal to cancel the request\n */\n removeByRemote(remoteName: string, signal?: AbortSignal): Promise<void>;\n\n /**\n * Returns distinct document IDs that have any dead letter record across all remotes.\n * Used to populate the quarantine set on startup.\n *\n * @param signal - Optional abort signal to cancel the request\n */\n listQuarantinedDocumentIds(signal?: AbortSignal): Promise<string[]>;\n}\n","import type { Operation } from \"@powerhousedao/shared/document-model\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport { type AtomicTxn as IAtomicTxn } from \"./interfaces.js\";\nimport type { InsertableOperation } from \"./kysely/types.js\";\n\nexport class AtomicTransaction implements IAtomicTxn {\n private operations: InsertableOperation[] = [];\n\n constructor(\n private documentId: string,\n private documentType: string,\n private scope: string,\n private branch: string,\n private baseRevision: number,\n ) {\n //\n }\n\n addOperations(...operations: Operation[]): void {\n for (const op of operations) {\n this.operations.push({\n // WRONG -- we should be using the jobId\n jobId: uuidv4(),\n opId: op.id,\n prevOpId: \"\", // Will be set during apply\n documentId: this.documentId,\n documentType: this.documentType,\n scope: this.scope,\n branch: this.branch,\n timestampUtcMs: new Date(op.timestampUtcMs),\n index: op.index,\n action: JSON.stringify(op.action),\n skip: op.skip,\n error: op.error || null,\n hash: op.hash,\n });\n }\n }\n\n getOperations(): InsertableOperation[] {\n return this.operations;\n }\n}\n","import {\n type Operation,\n type OperationWithContext,\n} from \"@powerhousedao/shared/document-model\";\nimport { sql, type Kysely, type Transaction } from \"kysely\";\nimport type { PagedResults, PagingOptions } from \"../../shared/types.js\";\nimport {\n DuplicateOperationError,\n RevisionMismatchError,\n type AtomicTxn,\n type DocumentRevisions,\n type IOperationStore,\n type OperationFilter,\n} from \"../interfaces.js\";\nimport { AtomicTransaction } from \"../txn.js\";\nimport type { Database, OperationRow } from \"./types.js\";\n\nexport class KyselyOperationStore implements IOperationStore {\n private trx?: Transaction<Database>;\n\n constructor(private db: Kysely<Database>) {}\n\n private get queryExecutor(): Kysely<Database> | Transaction<Database> {\n return this.trx ?? this.db;\n }\n\n withTransaction(trx: Transaction<Database>): KyselyOperationStore {\n const instance = new KyselyOperationStore(this.db);\n instance.trx = trx;\n return instance;\n }\n\n async apply(\n documentId: string,\n documentType: string,\n scope: string,\n branch: string,\n revision: number,\n fn: (txn: AtomicTxn) => void | Promise<void>,\n signal?: AbortSignal,\n ): Promise<void> {\n if (this.trx) {\n await this.executeApply(\n this.trx,\n documentId,\n documentType,\n scope,\n branch,\n revision,\n fn,\n signal,\n );\n } else {\n await this.db.transaction().execute(async (trx) => {\n await this.executeApply(\n trx,\n documentId,\n documentType,\n scope,\n branch,\n revision,\n fn,\n signal,\n );\n });\n }\n }\n\n private async executeApply(\n trx: Transaction<Database>,\n documentId: string,\n documentType: string,\n scope: string,\n branch: string,\n revision: number,\n fn: (txn: AtomicTxn) => void | Promise<void>,\n signal?: AbortSignal,\n ): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const latestOp = await trx\n .selectFrom(\"Operation\")\n .selectAll()\n .where(\"documentId\", \"=\", documentId)\n .where(\"scope\", \"=\", scope)\n .where(\"branch\", \"=\", branch)\n .orderBy(\"index\", \"desc\")\n .limit(1)\n .executeTakeFirst();\n\n const currentRevision = latestOp ? latestOp.index : -1;\n if (currentRevision !== revision - 1) {\n throw new RevisionMismatchError(currentRevision + 1, revision);\n }\n\n const atomicTxn = new AtomicTransaction(\n documentId,\n documentType,\n scope,\n branch,\n revision,\n );\n await fn(atomicTxn);\n\n const operations = atomicTxn.getOperations();\n\n if (operations.length > 0) {\n let prevOpId = latestOp?.opId || \"\";\n for (const op of operations) {\n op.prevOpId = prevOpId;\n prevOpId = op.opId;\n }\n\n try {\n await trx.insertInto(\"Operation\").values(operations).execute();\n } catch (error: unknown) {\n if (error instanceof Error) {\n if (error.message.includes(\"unique constraint\")) {\n const op = operations[0];\n throw new DuplicateOperationError(\n `${op.opId} at index ${op.index} with skip ${op.skip}`,\n );\n }\n\n throw error;\n }\n\n throw error;\n }\n }\n }\n\n async getSince(\n documentId: string,\n scope: string,\n branch: string,\n revision: number,\n filter?: OperationFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<Operation>> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n let query = this.queryExecutor\n .selectFrom(\"Operation\")\n .selectAll()\n .where(\"documentId\", \"=\", documentId)\n .where(\"scope\", \"=\", scope)\n .where(\"branch\", \"=\", branch)\n .where(\"index\", \">\", revision)\n .orderBy(\"index\", \"asc\");\n\n if (filter) {\n if (filter.actionTypes && filter.actionTypes.length > 0) {\n const actionTypesArray = filter.actionTypes\n .map((t) => `'${t.replace(/'/g, \"''\")}'`)\n .join(\",\");\n query = query.where(\n sql<boolean>`action->>'type' = ANY(ARRAY[${sql.raw(actionTypesArray)}]::text[])`,\n );\n }\n if (filter.timestampFrom) {\n query = query.where(\n \"timestampUtcMs\",\n \">=\",\n new Date(filter.timestampFrom),\n );\n }\n if (filter.timestampTo) {\n query = query.where(\n \"timestampUtcMs\",\n \"<=\",\n new Date(filter.timestampTo),\n );\n }\n if (filter.sinceRevision !== undefined) {\n query = query.where(\"index\", \">=\", filter.sinceRevision);\n }\n }\n\n if (paging) {\n const cursorValue = Number.parseInt(paging.cursor, 10);\n if (cursorValue > 0) {\n query = query.where(\"index\", \">\", cursorValue);\n }\n\n if (paging.limit) {\n query = query.limit(paging.limit + 1);\n }\n }\n\n const rows = await query.execute();\n\n let hasMore = false;\n let items = rows;\n\n if (paging?.limit && rows.length > paging.limit) {\n hasMore = true;\n items = rows.slice(0, paging.limit);\n }\n\n const nextCursor =\n hasMore && items.length > 0\n ? items[items.length - 1].index.toString()\n : undefined;\n\n const cursor = paging?.cursor || \"0\";\n const limit = paging?.limit || 100;\n const operations = items.map((row) => this.rowToOperation(row));\n\n return {\n results: operations,\n options: { cursor, limit },\n nextCursor,\n next: hasMore\n ? () =>\n this.getSince(\n documentId,\n scope,\n branch,\n revision,\n filter,\n { cursor: nextCursor!, limit },\n signal,\n )\n : undefined,\n };\n }\n\n async getSinceId(\n id: number,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<OperationWithContext>> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n let query = this.queryExecutor\n .selectFrom(\"Operation\")\n .selectAll()\n .where(\"id\", \">\", id)\n .orderBy(\"id\", \"asc\");\n\n // Handle cursor-based pagination\n if (paging) {\n // Cursor encodes the last seen id\n const cursorValue = Number.parseInt(paging.cursor, 10);\n if (cursorValue > 0) {\n query = query.where(\"id\", \">\", cursorValue);\n }\n\n // Apply limit if specified (fetch one extra to determine hasMore)\n if (paging.limit) {\n query = query.limit(paging.limit + 1);\n }\n }\n\n const rows = await query.execute();\n\n // Determine if there are more results\n let hasMore = false;\n let items = rows;\n\n if (paging?.limit && rows.length > paging.limit) {\n hasMore = true;\n items = rows.slice(0, paging.limit);\n }\n\n // Generate next cursor from last item's id\n const nextCursor =\n hasMore && items.length > 0\n ? items[items.length - 1].id.toString()\n : undefined;\n\n const cursor = paging?.cursor || \"0\";\n const limit = paging?.limit || 100;\n const operations = items.map((row) => this.rowToOperationWithContext(row));\n\n return {\n results: operations,\n options: { cursor, limit },\n nextCursor,\n next: hasMore\n ? () => this.getSinceId(id, { cursor: nextCursor!, limit }, signal)\n : undefined,\n };\n }\n\n async getConflicting(\n documentId: string,\n scope: string,\n branch: string,\n minTimestamp: string,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<Operation>> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n let query = this.queryExecutor\n .selectFrom(\"Operation\")\n .selectAll()\n .where(\"documentId\", \"=\", documentId)\n .where(\"scope\", \"=\", scope)\n .where(\"branch\", \"=\", branch)\n .where(\"timestampUtcMs\", \">=\", new Date(minTimestamp))\n .orderBy(\"index\", \"asc\");\n\n if (paging) {\n const cursorValue = Number.parseInt(paging.cursor, 10);\n if (cursorValue > 0) {\n query = query.where(\"index\", \">\", cursorValue);\n }\n\n if (paging.limit) {\n query = query.limit(paging.limit + 1);\n }\n }\n\n const rows = await query.execute();\n\n let hasMore = false;\n let items = rows;\n\n if (paging?.limit && rows.length > paging.limit) {\n hasMore = true;\n items = rows.slice(0, paging.limit);\n }\n\n const nextCursor =\n hasMore && items.length > 0\n ? items[items.length - 1].index.toString()\n : undefined;\n\n const cursor = paging?.cursor || \"0\";\n const limit = paging?.limit || 100;\n const operations = items.map((row) => this.rowToOperation(row));\n\n return {\n results: operations,\n options: { cursor, limit },\n nextCursor,\n next: hasMore\n ? () =>\n this.getConflicting(\n documentId,\n scope,\n branch,\n minTimestamp,\n { cursor: nextCursor!, limit },\n signal,\n )\n : undefined,\n };\n }\n\n async getRevisions(\n documentId: string,\n branch: string,\n signal?: AbortSignal,\n ): Promise<DocumentRevisions> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n // Get the latest operation for each scope in a single query\n // Uses a subquery to find operations where the index equals the max index for that scope\n const scopeRevisions = await this.queryExecutor\n .selectFrom(\"Operation as o1\")\n .select([\"o1.scope\", \"o1.index\", \"o1.timestampUtcMs\"])\n .where(\"o1.documentId\", \"=\", documentId)\n .where(\"o1.branch\", \"=\", branch)\n .where((eb) =>\n eb(\n \"o1.index\",\n \"=\",\n eb\n .selectFrom(\"Operation as o2\")\n .select((eb2) => eb2.fn.max(\"o2.index\").as(\"maxIndex\"))\n .where(\"o2.documentId\", \"=\", eb.ref(\"o1.documentId\"))\n .where(\"o2.branch\", \"=\", eb.ref(\"o1.branch\"))\n .where(\"o2.scope\", \"=\", eb.ref(\"o1.scope\")),\n ),\n )\n .execute();\n\n const revision: Record<string, number> = {};\n let latestTimestamp = new Date(0).toISOString();\n\n for (const row of scopeRevisions) {\n revision[row.scope] = row.index + 1;\n const timestamp = row.timestampUtcMs.toISOString();\n if (timestamp > latestTimestamp) {\n latestTimestamp = timestamp;\n }\n }\n\n return {\n revision,\n latestTimestamp,\n };\n }\n\n private rowToOperation(row: OperationRow): Operation {\n return {\n index: row.index,\n timestampUtcMs: row.timestampUtcMs.toISOString(),\n hash: row.hash,\n skip: row.skip,\n error: row.error || undefined,\n id: row.opId,\n action: row.action as Operation[\"action\"],\n };\n }\n\n private rowToOperationWithContext(row: OperationRow): OperationWithContext {\n return {\n operation: this.rowToOperation(row),\n context: {\n documentId: row.documentId,\n documentType: row.documentType,\n scope: row.scope,\n branch: row.branch,\n ordinal: row.id,\n },\n };\n }\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nexport async function up(db: Kysely<any>): Promise<void> {\n await db.schema\n .createTable(\"Operation\")\n .addColumn(\"id\", \"serial\", (col) => col.primaryKey())\n .addColumn(\"jobId\", \"text\", (col) => col.notNull())\n .addColumn(\"opId\", \"text\", (col) => col.notNull())\n .addColumn(\"prevOpId\", \"text\", (col) => col.notNull())\n .addColumn(\"writeTimestampUtcMs\", \"timestamptz\", (col) =>\n col.notNull().defaultTo(sql`NOW()`),\n )\n .addColumn(\"documentId\", \"text\", (col) => col.notNull())\n .addColumn(\"documentType\", \"text\", (col) => col.notNull())\n .addColumn(\"scope\", \"text\", (col) => col.notNull())\n .addColumn(\"branch\", \"text\", (col) => col.notNull())\n .addColumn(\"timestampUtcMs\", \"timestamptz\", (col) => col.notNull())\n .addColumn(\"index\", \"integer\", (col) => col.notNull())\n .addColumn(\"action\", \"jsonb\", (col) => col.notNull())\n .addColumn(\"skip\", \"integer\", (col) => col.notNull())\n .addColumn(\"error\", \"text\")\n .addColumn(\"hash\", \"text\", (col) => col.notNull())\n .addUniqueConstraint(\"unique_revision\", [\n \"documentId\",\n \"scope\",\n \"branch\",\n \"index\",\n ])\n .addUniqueConstraint(\"unique_operation_instance\", [\"opId\", \"index\", \"skip\"])\n .execute();\n\n // Create index for streaming operations\n await db.schema\n .createIndex(\"streamOperations\")\n .on(\"Operation\")\n .columns([\"documentId\", \"scope\", \"branch\", \"id\"])\n .execute();\n\n // Create index for branchless streaming operations\n await db.schema\n .createIndex(\"branchlessStreamOperations\")\n .on(\"Operation\")\n .columns([\"documentId\", \"scope\", \"id\"])\n .execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nexport async function up(db: Kysely<any>): Promise<void> {\n await db.schema\n .createTable(\"Keyframe\")\n .addColumn(\"id\", \"serial\", (col) => col.primaryKey())\n .addColumn(\"documentId\", \"text\", (col) => col.notNull())\n .addColumn(\"documentType\", \"text\", (col) => col.notNull())\n .addColumn(\"scope\", \"text\", (col) => col.notNull())\n .addColumn(\"branch\", \"text\", (col) => col.notNull())\n .addColumn(\"revision\", \"integer\", (col) => col.notNull())\n .addColumn(\"document\", \"jsonb\", (col) => col.notNull())\n .addColumn(\"createdAt\", \"timestamptz\", (col) =>\n col.notNull().defaultTo(sql`NOW()`),\n )\n .addUniqueConstraint(\"unique_keyframe\", [\n \"documentId\",\n \"scope\",\n \"branch\",\n \"revision\",\n ])\n .execute();\n\n // Create index for keyframe lookup\n await db.schema\n .createIndex(\"keyframe_lookup\")\n .on(\"Keyframe\")\n .columns([\"documentId\", \"scope\", \"branch\", \"revision\"])\n .execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nexport async function up(db: Kysely<any>): Promise<void> {\n await db.schema\n .createTable(\"Document\")\n .addColumn(\"id\", \"text\", (col) => col.primaryKey())\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 .execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nexport async function up(db: Kysely<any>): Promise<void> {\n await db.schema\n .createTable(\"DocumentRelationship\")\n .addColumn(\"id\", \"text\", (col) => col.primaryKey())\n .addColumn(\"sourceId\", \"text\", (col) =>\n col.notNull().references(\"Document.id\").onDelete(\"cascade\"),\n )\n .addColumn(\"targetId\", \"text\", (col) =>\n col.notNull().references(\"Document.id\").onDelete(\"cascade\"),\n )\n .addColumn(\"relationshipType\", \"text\", (col) => col.notNull())\n .addColumn(\"metadata\", \"jsonb\")\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 .addUniqueConstraint(\"unique_source_target_type\", [\n \"sourceId\",\n \"targetId\",\n \"relationshipType\",\n ])\n .execute();\n\n // Create indexes for efficient graph traversal\n await db.schema\n .createIndex(\"idx_relationship_source\")\n .on(\"DocumentRelationship\")\n .column(\"sourceId\")\n .execute();\n\n await db.schema\n .createIndex(\"idx_relationship_target\")\n .on(\"DocumentRelationship\")\n .column(\"targetId\")\n .execute();\n\n await db.schema\n .createIndex(\"idx_relationship_type\")\n .on(\"DocumentRelationship\")\n .column(\"relationshipType\")\n .execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nexport async function up(db: Kysely<any>): Promise<void> {\n await db.schema\n .createTable(\"IndexerState\")\n .addColumn(\"id\", \"integer\", (col) =>\n col.primaryKey().generatedAlwaysAsIdentity(),\n )\n .addColumn(\"lastOperationId\", \"integer\", (col) => col.notNull())\n .addColumn(\"lastOperationTimestamp\", \"timestamptz\", (col) =>\n col.notNull().defaultTo(sql`NOW()`),\n )\n .execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nexport async function up(db: Kysely<any>): Promise<void> {\n await db.schema\n .createTable(\"DocumentSnapshot\")\n .addColumn(\"id\", \"text\", (col) => col.primaryKey())\n .addColumn(\"documentId\", \"text\", (col) => col.notNull())\n .addColumn(\"slug\", \"text\")\n .addColumn(\"name\", \"text\")\n .addColumn(\"scope\", \"text\", (col) => col.notNull())\n .addColumn(\"branch\", \"text\", (col) => col.notNull())\n .addColumn(\"content\", \"jsonb\", (col) => col.notNull())\n .addColumn(\"documentType\", \"text\", (col) => col.notNull())\n .addColumn(\"lastOperationIndex\", \"integer\", (col) => col.notNull())\n .addColumn(\"lastOperationHash\", \"text\", (col) => col.notNull())\n .addColumn(\"lastUpdatedAt\", \"timestamptz\", (col) =>\n col.notNull().defaultTo(sql`NOW()`),\n )\n .addColumn(\"snapshotVersion\", \"integer\", (col) =>\n col.notNull().defaultTo(1),\n )\n .addColumn(\"identifiers\", \"jsonb\")\n .addColumn(\"metadata\", \"jsonb\")\n .addColumn(\"isDeleted\", \"boolean\", (col) => col.notNull().defaultTo(false))\n .addColumn(\"deletedAt\", \"timestamptz\")\n .addUniqueConstraint(\"unique_doc_scope_branch\", [\n \"documentId\",\n \"scope\",\n \"branch\",\n ])\n .execute();\n\n // Create indexes for query optimization\n await db.schema\n .createIndex(\"idx_slug_scope_branch\")\n .on(\"DocumentSnapshot\")\n .columns([\"slug\", \"scope\", \"branch\"])\n .execute();\n\n await db.schema\n .createIndex(\"idx_doctype_scope_branch\")\n .on(\"DocumentSnapshot\")\n .columns([\"documentType\", \"scope\", \"branch\"])\n .execute();\n\n await db.schema\n .createIndex(\"idx_last_updated\")\n .on(\"DocumentSnapshot\")\n .column(\"lastUpdatedAt\")\n .execute();\n\n await db.schema\n .createIndex(\"idx_is_deleted\")\n .on(\"DocumentSnapshot\")\n .column(\"isDeleted\")\n .execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nexport async function up(db: Kysely<any>): Promise<void> {\n await db.schema\n .createTable(\"SlugMapping\")\n .addColumn(\"slug\", \"text\", (col) => col.primaryKey())\n .addColumn(\"documentId\", \"text\", (col) => col.notNull())\n .addColumn(\"scope\", \"text\", (col) => col.notNull())\n .addColumn(\"branch\", \"text\", (col) => col.notNull())\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 .addUniqueConstraint(\"unique_docid_scope_branch\", [\n \"documentId\",\n \"scope\",\n \"branch\",\n ])\n .execute();\n\n // Create index for reverse lookup (documentId -> slug)\n await db.schema\n .createIndex(\"idx_slug_documentid\")\n .on(\"SlugMapping\")\n .column(\"documentId\")\n .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(\"ViewState\")\n .addColumn(\"readModelId\", \"text\", (col) => col.primaryKey())\n .addColumn(\"lastOrdinal\", \"integer\", (col) => col.notNull().defaultTo(0))\n .addColumn(\"lastOperationTimestamp\", \"timestamptz\", (col) =>\n col.notNull().defaultTo(sql`NOW()`),\n )\n .execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nexport async function up(db: Kysely<any>): Promise<void> {\n await db.schema\n .createTable(\"document_collections\")\n .addColumn(\"documentId\", \"text\", (col) => col.notNull())\n .addColumn(\"collectionId\", \"text\", (col) => col.notNull())\n .addColumn(\"joinedOrdinal\", \"bigint\", (col) => col.notNull().defaultTo(0))\n .addColumn(\"leftOrdinal\", \"bigint\")\n .addPrimaryKeyConstraint(\"document_collections_pkey\", [\n \"documentId\",\n \"collectionId\",\n ])\n .execute();\n\n await db.schema\n .createIndex(\"idx_document_collections_collectionId\")\n .on(\"document_collections\")\n .column(\"collectionId\")\n .execute();\n\n await db.schema\n .createIndex(\"idx_doc_collections_collection_range\")\n .on(\"document_collections\")\n .columns([\"collectionId\", \"joinedOrdinal\"])\n .execute();\n\n await db.schema\n .createTable(\"operation_index_operations\")\n .addColumn(\"ordinal\", \"serial\", (col) => col.primaryKey())\n .addColumn(\"opId\", \"text\", (col) => col.notNull())\n .addColumn(\"documentId\", \"text\", (col) => col.notNull())\n .addColumn(\"documentType\", \"text\", (col) => col.notNull())\n .addColumn(\"scope\", \"text\", (col) => col.notNull())\n .addColumn(\"branch\", \"text\", (col) => col.notNull())\n .addColumn(\"timestampUtcMs\", \"text\", (col) => col.notNull())\n .addColumn(\"writeTimestampUtcMs\", \"timestamptz\", (col) =>\n col.notNull().defaultTo(sql`NOW()`),\n )\n .addColumn(\"index\", \"integer\", (col) => col.notNull())\n .addColumn(\"skip\", \"integer\", (col) => col.notNull())\n .addColumn(\"hash\", \"text\", (col) => col.notNull())\n .addColumn(\"action\", \"jsonb\", (col) => col.notNull())\n .execute();\n\n await db.schema\n .createIndex(\"idx_operation_index_operations_document\")\n .on(\"operation_index_operations\")\n .columns([\"documentId\", \"branch\", \"scope\"])\n .execute();\n\n await db.schema\n .createIndex(\"idx_operation_index_operations_ordinal\")\n .on(\"operation_index_operations\")\n .column(\"ordinal\")\n .execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nexport async function up(db: Kysely<any>): Promise<void> {\n await db.schema\n .createTable(\"sync_remotes\")\n .addColumn(\"name\", \"text\", (col) => col.primaryKey())\n .addColumn(\"collection_id\", \"text\", (col) => col.notNull())\n .addColumn(\"channel_type\", \"text\", (col) => col.notNull())\n .addColumn(\"channel_id\", \"text\", (col) => col.notNull().defaultTo(\"\"))\n .addColumn(\"remote_name\", \"text\", (col) => col.notNull().defaultTo(\"\"))\n .addColumn(\"channel_parameters\", \"jsonb\", (col) =>\n col.notNull().defaultTo(sql`'{}'::jsonb`),\n )\n .addColumn(\"filter_document_ids\", \"jsonb\")\n .addColumn(\"filter_scopes\", \"jsonb\")\n .addColumn(\"filter_branch\", \"text\", (col) =>\n col.notNull().defaultTo(\"main\"),\n )\n .addColumn(\"push_state\", \"text\", (col) => col.notNull().defaultTo(\"idle\"))\n .addColumn(\"push_last_success_utc_ms\", \"text\")\n .addColumn(\"push_last_failure_utc_ms\", \"text\")\n .addColumn(\"push_failure_count\", \"integer\", (col) =>\n col.notNull().defaultTo(0),\n )\n .addColumn(\"pull_state\", \"text\", (col) => col.notNull().defaultTo(\"idle\"))\n .addColumn(\"pull_last_success_utc_ms\", \"text\")\n .addColumn(\"pull_last_failure_utc_ms\", \"text\")\n .addColumn(\"pull_failure_count\", \"integer\", (col) =>\n col.notNull().defaultTo(0),\n )\n .addColumn(\"created_at\", \"timestamptz\", (col) =>\n col.notNull().defaultTo(sql`NOW()`),\n )\n .addColumn(\"updated_at\", \"timestamptz\", (col) =>\n col.notNull().defaultTo(sql`NOW()`),\n )\n .execute();\n\n await db.schema\n .createIndex(\"idx_sync_remotes_collection\")\n .on(\"sync_remotes\")\n .column(\"collection_id\")\n .execute();\n\n await db.schema\n .createTable(\"sync_cursors\")\n .addColumn(\"remote_name\", \"text\", (col) =>\n col.primaryKey().references(\"sync_remotes.name\").onDelete(\"cascade\"),\n )\n .addColumn(\"cursor_ordinal\", \"bigint\", (col) => col.notNull().defaultTo(0))\n .addColumn(\"last_synced_at_utc_ms\", \"text\")\n .addColumn(\"updated_at\", \"timestamptz\", (col) =>\n col.notNull().defaultTo(sql`NOW()`),\n )\n .execute();\n\n await db.schema\n .createIndex(\"idx_sync_cursors_ordinal\")\n .on(\"sync_cursors\")\n .column(\"cursor_ordinal\")\n .execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nexport async function up(db: Kysely<any>): Promise<void> {\n // Delete any leftover fake \"outbox::\" cursor rows and remote records\n await db\n .deleteFrom(\"sync_cursors\")\n .where(\"remote_name\", \"like\", \"outbox::%\")\n .execute();\n await db\n .deleteFrom(\"sync_remotes\")\n .where(\"name\", \"like\", \"outbox::%\")\n .execute();\n\n // Recreate sync_cursors with cursor_type column and composite PK (no FK)\n await db.schema.dropTable(\"sync_cursors\").execute();\n\n await db.schema\n .createTable(\"sync_cursors\")\n .addColumn(\"remote_name\", \"text\", (col) => col.notNull())\n .addColumn(\"cursor_type\", \"text\", (col) => col.notNull().defaultTo(\"inbox\"))\n .addColumn(\"cursor_ordinal\", \"bigint\", (col) => col.notNull().defaultTo(0))\n .addColumn(\"last_synced_at_utc_ms\", \"text\")\n .addColumn(\"updated_at\", \"timestamptz\", (col) =>\n col.notNull().defaultTo(sql`NOW()`),\n )\n .addPrimaryKeyConstraint(\"sync_cursors_pk\", [\"remote_name\", \"cursor_type\"])\n .execute();\n\n await db.schema\n .createIndex(\"idx_sync_cursors_ordinal\")\n .on(\"sync_cursors\")\n .column(\"cursor_ordinal\")\n .execute();\n}\n","import type { Kysely } from \"kysely\";\n\nexport async function up(db: Kysely<any>): Promise<void> {\n await db.schema\n .alterTable(\"operation_index_operations\")\n .addColumn(\"sourceRemote\", \"text\", (col) => col.notNull().defaultTo(\"\"))\n .execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nexport async function up(db: Kysely<any>): Promise<void> {\n await db.schema\n .createTable(\"sync_dead_letters\")\n .addColumn(\"ordinal\", \"serial\", (col) => col.primaryKey())\n .addColumn(\"id\", \"text\", (col) => col.unique().notNull())\n .addColumn(\"job_id\", \"text\", (col) => col.notNull())\n .addColumn(\"job_dependencies\", \"jsonb\", (col) =>\n col.notNull().defaultTo(sql`'[]'::jsonb`),\n )\n .addColumn(\"remote_name\", \"text\", (col) =>\n col.notNull().references(\"sync_remotes.name\").onDelete(\"cascade\"),\n )\n .addColumn(\"document_id\", \"text\", (col) => col.notNull())\n .addColumn(\"scopes\", \"jsonb\", (col) =>\n col.notNull().defaultTo(sql`'[]'::jsonb`),\n )\n .addColumn(\"branch\", \"text\", (col) => col.notNull())\n .addColumn(\"operations\", \"jsonb\", (col) =>\n col.notNull().defaultTo(sql`'[]'::jsonb`),\n )\n .addColumn(\"error_source\", \"text\", (col) => col.notNull())\n .addColumn(\"error_message\", \"text\", (col) => col.notNull())\n .addColumn(\"created_at\", \"timestamptz\", (col) =>\n col.notNull().defaultTo(sql`NOW()`),\n )\n .execute();\n\n await db.schema\n .createIndex(\"idx_sync_dead_letters_remote\")\n .on(\"sync_dead_letters\")\n .column(\"remote_name\")\n .execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nexport async function up(db: Kysely<any>): Promise<void> {\n await db.schema\n .createTable(\"ProcessorCursor\")\n .addColumn(\"processorId\", \"text\", (col) => col.primaryKey())\n .addColumn(\"factoryId\", \"text\", (col) => col.notNull())\n .addColumn(\"driveId\", \"text\", (col) => col.notNull())\n .addColumn(\"processorIndex\", \"integer\", (col) => col.notNull())\n .addColumn(\"lastOrdinal\", \"integer\", (col) =>\n col.notNull().defaultTo(sql`0`),\n )\n .addColumn(\"status\", \"text\", (col) =>\n col.notNull().defaultTo(sql`'active'`),\n )\n .addColumn(\"lastError\", \"text\")\n .addColumn(\"lastErrorTimestamp\", \"timestamptz\")\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 .execute();\n}\n","import { Migrator, sql } from \"kysely\";\nimport type { MigrationProvider, Kysely } from \"kysely\";\nimport type { MigrationResult } from \"./types.js\";\n\nexport const REACTOR_SCHEMA = \"reactor\";\nimport * as migration001 from \"./001_create_operation_table.js\";\nimport * as migration002 from \"./002_create_keyframe_table.js\";\nimport * as migration003 from \"./003_create_document_table.js\";\nimport * as migration004 from \"./004_create_document_relationship_table.js\";\nimport * as migration005 from \"./005_create_indexer_state_table.js\";\nimport * as migration006 from \"./006_create_document_snapshot_table.js\";\nimport * as migration007 from \"./007_create_slug_mapping_table.js\";\nimport * as migration008 from \"./008_create_view_state_table.js\";\nimport * as migration009 from \"./009_create_operation_index_tables.js\";\nimport * as migration010 from \"./010_create_sync_tables.js\";\nimport * as migration011 from \"./011_add_cursor_type_column.js\";\nimport * as migration012 from \"./012_add_source_remote_column.js\";\nimport * as migration013 from \"./013_create_sync_dead_letters_table.js\";\nimport * as migration014 from \"./014_create_processor_cursor_table.js\";\n\nconst migrations = {\n \"001_create_operation_table\": migration001,\n \"002_create_keyframe_table\": migration002,\n \"003_create_document_table\": migration003,\n \"004_create_document_relationship_table\": migration004,\n \"005_create_indexer_state_table\": migration005,\n \"006_create_document_snapshot_table\": migration006,\n \"007_create_slug_mapping_table\": migration007,\n \"008_create_view_state_table\": migration008,\n \"009_create_operation_index_tables\": migration009,\n \"010_create_sync_tables\": migration010,\n \"011_add_cursor_type_column\": migration011,\n \"012_add_source_remote_column\": migration012,\n \"013_create_sync_dead_letters_table\": migration013,\n \"014_create_processor_cursor_table\": migration014,\n};\n\nclass ProgrammaticMigrationProvider implements MigrationProvider {\n getMigrations() {\n return Promise.resolve(migrations);\n }\n}\n\nexport async function runMigrations(\n db: Kysely<any>,\n schema: string = REACTOR_SCHEMA,\n): Promise<MigrationResult> {\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 });\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 getMigrationStatus(\n db: Kysely<any>,\n schema: string = REACTOR_SCHEMA,\n) {\n const migrator = new Migrator({\n db: db.withSchema(schema),\n provider: new ProgrammaticMigrationProvider(),\n migrationTableSchema: schema,\n });\n\n return await migrator.getMigrations();\n}\n","import type {\n ISubscriptionErrorHandler,\n SubscriptionErrorContext,\n} from \"./types.js\";\n\n/**\n * Default error handler that re-throws subscription errors.\n * This ensures that errors are not silently swallowed.\n */\nexport class DefaultSubscriptionErrorHandler implements ISubscriptionErrorHandler {\n handleError(error: unknown, context: SubscriptionErrorContext): void {\n const errorMessage = `Subscription error in ${context.eventType} (${context.subscriptionId})`;\n\n if (error instanceof Error) {\n // Preserve the original error with additional context\n const enhancedError = new Error(`${errorMessage}: ${error.message}`);\n enhancedError.cause = error;\n enhancedError.stack = error.stack;\n throw enhancedError;\n } else {\n // Handle non-Error objects\n throw new Error(`${errorMessage}: ${String(error)}`);\n }\n }\n}\n\n/**\n * Creates a default subscription error handler instance\n */\nexport function createDefaultSubscriptionErrorHandler(): ISubscriptionErrorHandler {\n return new DefaultSubscriptionErrorHandler();\n}\n","import type { PHDocument } from \"@powerhousedao/shared/document-model\";\nimport type {\n PagedResults,\n RelationshipChangeType,\n SearchFilter,\n ViewFilter,\n} from \"../shared/types.js\";\nimport type {\n IReactorSubscriptionManager,\n ISubscriptionErrorHandler,\n} from \"./types.js\";\n\ntype DocumentCreatedCallback = (result: PagedResults<string>) => void;\ntype DocumentDeletedCallback = (documentIds: string[]) => void;\ntype DocumentStateUpdatedCallback = (result: PagedResults<PHDocument>) => void;\ntype RelationshipChangedCallback = (\n parentId: string,\n childId: string,\n changeType: RelationshipChangeType,\n) => void;\n\ntype Subscription<T> = {\n id: string;\n callback: T;\n search?: SearchFilter;\n view?: ViewFilter;\n};\n\nexport class ReactorSubscriptionManager implements IReactorSubscriptionManager {\n private createdSubscriptions = new Map<\n string,\n Subscription<DocumentCreatedCallback>\n >();\n private deletedSubscriptions = new Map<\n string,\n Subscription<DocumentDeletedCallback>\n >();\n private updatedSubscriptions = new Map<\n string,\n Subscription<DocumentStateUpdatedCallback>\n >();\n private relationshipSubscriptions = new Map<\n string,\n Subscription<RelationshipChangedCallback>\n >();\n\n private subscriptionCounter = 0;\n private errorHandler: ISubscriptionErrorHandler;\n\n constructor(errorHandler: ISubscriptionErrorHandler) {\n this.errorHandler = errorHandler;\n }\n\n onDocumentCreated(\n callback: DocumentCreatedCallback,\n search?: SearchFilter,\n ): () => void {\n const id = `created-${++this.subscriptionCounter}`;\n this.createdSubscriptions.set(id, { id, callback, search });\n\n return () => {\n this.createdSubscriptions.delete(id);\n };\n }\n\n onDocumentDeleted(\n callback: DocumentDeletedCallback,\n search?: SearchFilter,\n ): () => void {\n const id = `deleted-${++this.subscriptionCounter}`;\n this.deletedSubscriptions.set(id, { id, callback, search });\n\n return () => {\n this.deletedSubscriptions.delete(id);\n };\n }\n\n onDocumentStateUpdated(\n callback: DocumentStateUpdatedCallback,\n search?: SearchFilter,\n view?: ViewFilter,\n ): () => void {\n const id = `updated-${++this.subscriptionCounter}`;\n this.updatedSubscriptions.set(id, { id, callback, search, view });\n\n return () => {\n this.updatedSubscriptions.delete(id);\n };\n }\n\n onRelationshipChanged(\n callback: RelationshipChangedCallback,\n search?: SearchFilter,\n ): () => void {\n const id = `relationship-${++this.subscriptionCounter}`;\n this.relationshipSubscriptions.set(id, { id, callback, search });\n\n return () => {\n this.relationshipSubscriptions.delete(id);\n };\n }\n\n /**\n * Notify subscribers about created documents\n */\n notifyDocumentsCreated(\n documentIds: string[],\n documentTypes?: Map<string, string>,\n parentIds?: Map<string, string | null>,\n ): void {\n const result: PagedResults<string> = {\n results: documentIds,\n options: { cursor: \"\", limit: documentIds.length },\n };\n\n for (const subscription of this.createdSubscriptions.values()) {\n const filteredIds = this.filterDocumentIds(\n documentIds,\n subscription.search,\n documentTypes,\n parentIds,\n );\n\n if (filteredIds.length > 0) {\n try {\n subscription.callback({\n ...result,\n results: filteredIds,\n });\n } catch (error) {\n this.errorHandler.handleError(error, {\n eventType: \"created\",\n subscriptionId: subscription.id,\n eventData: filteredIds,\n });\n }\n }\n }\n }\n\n /**\n * Notify subscribers about deleted documents\n */\n notifyDocumentsDeleted(\n documentIds: string[],\n documentTypes?: Map<string, string>,\n parentIds?: Map<string, string | null>,\n ): void {\n for (const subscription of this.deletedSubscriptions.values()) {\n const filteredIds = this.filterDocumentIds(\n documentIds,\n subscription.search,\n documentTypes,\n parentIds,\n );\n\n if (filteredIds.length > 0) {\n try {\n subscription.callback(filteredIds);\n } catch (error) {\n this.errorHandler.handleError(error, {\n eventType: \"deleted\",\n subscriptionId: subscription.id,\n eventData: filteredIds,\n });\n }\n }\n }\n }\n\n /**\n * Notify subscribers about updated documents\n */\n notifyDocumentsUpdated(documents: PHDocument[]): void {\n const result: PagedResults<PHDocument> = {\n results: documents,\n options: { cursor: \"\", limit: documents.length },\n };\n\n for (const subscription of this.updatedSubscriptions.values()) {\n const filteredDocs = this.filterDocuments(documents, subscription.search);\n\n if (filteredDocs.length > 0) {\n try {\n subscription.callback({\n ...result,\n results: filteredDocs,\n });\n } catch (error) {\n this.errorHandler.handleError(error, {\n eventType: \"updated\",\n subscriptionId: subscription.id,\n eventData: filteredDocs,\n });\n }\n }\n }\n }\n\n /**\n * Notify subscribers about relationship changes\n */\n notifyRelationshipChanged(\n parentId: string,\n childId: string,\n changeType: RelationshipChangeType,\n childType?: string,\n ): void {\n for (const subscription of this.relationshipSubscriptions.values()) {\n if (\n this.matchesRelationshipFilter(\n parentId,\n childId,\n childType,\n subscription.search,\n )\n ) {\n try {\n subscription.callback(parentId, childId, changeType);\n } catch (error) {\n this.errorHandler.handleError(error, {\n eventType: \"relationshipChanged\",\n subscriptionId: subscription.id,\n eventData: { parentId, childId, changeType },\n });\n }\n }\n }\n }\n\n /**\n * Clear all subscriptions\n */\n clearAll(): void {\n this.createdSubscriptions.clear();\n this.deletedSubscriptions.clear();\n this.updatedSubscriptions.clear();\n this.relationshipSubscriptions.clear();\n }\n\n private filterDocumentIds(\n documentIds: string[],\n search?: SearchFilter,\n documentTypes?: Map<string, string>,\n parentIds?: Map<string, string | null>,\n ): string[] {\n if (!search) return documentIds;\n\n return documentIds.filter((id) => {\n if (search.ids && !search.ids.includes(id)) return false;\n\n if (search.type && documentTypes) {\n const docType = documentTypes.get(id);\n if (docType !== search.type) return false;\n }\n\n if (search.parentId && parentIds) {\n const parentId = parentIds.get(id);\n if (parentId !== search.parentId) return false;\n }\n\n return true;\n });\n }\n\n private filterDocuments(\n documents: PHDocument[],\n search?: SearchFilter,\n ): PHDocument[] {\n if (!search) return documents;\n\n return documents.filter((doc) => {\n if (search.ids && !search.ids.includes(doc.header.id)) return false;\n if (search.type && doc.header.documentType !== search.type) return false;\n if (search.slugs && !search.slugs.includes(doc.header.slug)) return false;\n\n return true;\n });\n }\n\n private matchesRelationshipFilter(\n parentId: string,\n childId: string,\n childType?: string,\n search?: SearchFilter,\n ): boolean {\n if (!search) return true;\n\n if (search.parentId && parentId !== search.parentId) return false;\n if (search.ids && !search.ids.includes(childId)) return false;\n if (search.type && childType && childType !== search.type) return false;\n\n return true;\n }\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { IReadModel } from \"../read-models/interfaces.js\";\nimport { RelationshipChangeType } from \"../shared/types.js\";\nimport type { IDocumentView } from \"../storage/interfaces.js\";\nimport type { ReactorSubscriptionManager } from \"./react-subscription-manager.js\";\n\n/**\n * A read model that notifies the subscription manager when operations are processed.\n * This bridges the gap between operation processing and subscription callbacks.\n *\n * Must be processed AFTER other read models have completed and AFTER READ_READY\n * is emitted, so that reactor.get() returns fresh data when callbacks fire.\n */\nexport class SubscriptionNotificationReadModel implements IReadModel {\n constructor(\n private subscriptionManager: ReactorSubscriptionManager,\n private documentView?: IDocumentView,\n ) {}\n\n async indexOperations(operations: OperationWithContext[]): Promise<void> {\n if (operations.length === 0) return;\n\n const created: string[] = [];\n const deleted: string[] = [];\n const updatedIds = new Set<string>();\n const documentTypes = new Map<string, string>();\n const parentIds = new Map<string, string | null>();\n\n for (const item of operations) {\n const { operation, context } = item;\n const actionType = operation.action.type;\n\n documentTypes.set(context.documentId, context.documentType);\n\n if (actionType === \"CREATE_DOCUMENT\") {\n created.push(context.documentId);\n } else if (actionType === \"DELETE_DOCUMENT\") {\n const input = operation.action.input as { documentId?: string };\n const deletedId = input.documentId ?? context.documentId;\n deleted.push(deletedId);\n } else if (actionType === \"ADD_RELATIONSHIP\") {\n const input = operation.action.input as {\n sourceId: string;\n targetId: string;\n childType?: string;\n };\n this.subscriptionManager.notifyRelationshipChanged(\n input.sourceId,\n input.targetId,\n RelationshipChangeType.Added,\n input.childType,\n );\n } else if (actionType === \"REMOVE_RELATIONSHIP\") {\n const input = operation.action.input as {\n sourceId: string;\n targetId: string;\n childType?: string;\n };\n this.subscriptionManager.notifyRelationshipChanged(\n input.sourceId,\n input.targetId,\n RelationshipChangeType.Removed,\n input.childType,\n );\n } else {\n if (!created.includes(context.documentId)) {\n updatedIds.add(context.documentId);\n }\n }\n }\n\n if (created.length > 0) {\n this.subscriptionManager.notifyDocumentsCreated(\n created,\n documentTypes,\n parentIds,\n );\n }\n\n if (deleted.length > 0) {\n this.subscriptionManager.notifyDocumentsDeleted(\n deleted,\n documentTypes,\n parentIds,\n );\n }\n\n if (updatedIds.size > 0 && this.documentView) {\n const documents = await Promise.all(\n Array.from(updatedIds).map((id) => this.documentView!.get(id)),\n );\n this.subscriptionManager.notifyDocumentsUpdated(documents);\n }\n }\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\n\nexport enum ChannelScheme {\n CONNECT = \"connect\",\n SWITCHBOARD = \"switchboard\",\n}\n\n/**\n * Dynamic JWT token handler for generating authentication tokens per-request.\n * Called with the target URL to enable audience-specific tokens (aud claim).\n * Returns undefined if no authentication is available (e.g., user not logged in).\n */\nexport type JwtHandler = (url: string) => Promise<string | undefined>;\n\nexport type RemoteOptions = {\n sinceTimestampUtcMs: string;\n};\n\nexport type RemoteFilter = {\n documentId: string[];\n scope: string[];\n branch: string;\n};\n\nexport type RemoteCursor = {\n remoteName: string;\n cursorType: string;\n cursorOrdinal: number;\n lastSyncedAtUtcMs?: number;\n};\n\nexport type ChannelMeta = {\n id: string;\n};\n\nexport type SyncEnvelopeType = \"operations\" | \"ack\";\n\nexport type SyncEnvelope = {\n type: SyncEnvelopeType;\n channelMeta: ChannelMeta;\n operations?: OperationWithContext[];\n cursor?: RemoteCursor;\n key?: string;\n dependsOn?: string[];\n};\n\nexport enum SyncOperationStatus {\n Unknown = -1,\n TransportPending = 0,\n ExecutionPending = 1,\n Applied = 2,\n Error = 3,\n}\n\nexport enum ChannelErrorSource {\n None = \"none\",\n Channel = \"channel\",\n Inbox = \"inbox\",\n Outbox = \"outbox\",\n}\n\nexport type SyncOperationErrorType =\n | \"SIGNATURE_INVALID\"\n | \"HASH_MISMATCH\"\n | \"LIBRARY_ERROR\"\n | \"MISSING_OPERATIONS\"\n | \"EXCESSIVE_SHUFFLE\"\n | \"GRACEFUL_ABORT\";\n\nexport type ChannelHealth = {\n state: \"idle\" | \"running\" | \"error\";\n lastSuccessUtcMs?: number;\n lastFailureUtcMs?: number;\n failureCount: number;\n};\n\nexport type ConnectionState =\n | \"connecting\"\n | \"connected\"\n | \"disconnected\"\n | \"reconnecting\"\n | \"error\";\n\nexport type ConnectionStateSnapshot = {\n state: ConnectionState;\n failureCount: number;\n lastSuccessUtcMs: number;\n lastFailureUtcMs: number;\n pushBlocked: boolean;\n pushFailureCount: number;\n};\n\nexport type ConnectionStateChangedEvent = {\n remoteName: string;\n remoteId: string;\n previous: ConnectionState;\n current: ConnectionState;\n snapshot: ConnectionStateSnapshot;\n};\n\nexport type RemoteStatus = {\n push: ChannelHealth;\n pull: ChannelHealth;\n};\n\nexport type ChannelConfig = {\n type: string;\n parameters: Record<string, unknown>;\n};\n\nexport type RemoteRecord = {\n id: string;\n name: string;\n collectionId: string;\n channelConfig: ChannelConfig;\n filter: RemoteFilter;\n options: RemoteOptions;\n status: RemoteStatus;\n};\n\n/**\n * Event types for sync lifecycle events.\n * These events track the sync progress of a job's operations to remotes.\n * Uses a separate namespace (20000 range) from ReactorEventTypes (10000 range).\n */\nexport const SyncEventTypes = {\n SYNC_PENDING: 20001,\n SYNC_SUCCEEDED: 20002,\n SYNC_FAILED: 20003,\n DEAD_LETTER_ADDED: 20004,\n CONNECTION_STATE_CHANGED: 20005,\n} as const;\n\n/**\n * Event emitted when all SyncOperations for a job are queued in outboxes.\n */\nexport type SyncPendingEvent = {\n jobId: string;\n syncOperationCount: number;\n remoteNames: string[];\n};\n\n/**\n * Event emitted when all sync operations for a job succeed.\n */\nexport type SyncSucceededEvent = {\n jobId: string;\n syncOperationCount: number;\n};\n\n/**\n * Event emitted when at least one sync operation for a job fails.\n */\nexport type SyncFailedEvent = {\n jobId: string;\n successCount: number;\n failureCount: number;\n errors: Array<{\n remoteName: string;\n documentId: string;\n error: string;\n }>;\n};\n\n/**\n * Event emitted when a sync operation is moved to dead letter storage.\n */\nexport type DeadLetterAddedEvent = {\n id: string;\n jobId: string;\n remoteName: string;\n documentId: string;\n errorSource: ChannelErrorSource;\n};\n\n/**\n * Status of a sync operation result.\n */\nexport type SyncResultStatus = \"succeeded\" | \"failed\";\n\n/**\n * Error information for a failed sync operation to a specific remote.\n */\nexport type SyncResultError = {\n remoteName: string;\n documentId: string;\n error: string;\n};\n\n/**\n * Result of waiting for sync operations to complete for a job.\n * Returned by ISyncManager.waitForSync().\n */\nexport type SyncResult = {\n jobId: string;\n status: SyncResultStatus;\n syncOperationCount: number;\n successCount: number;\n failureCount: number;\n errors: SyncResultError[];\n};\n","import type { SyncOperation } from \"./sync-operation.js\";\nimport { SyncOperationStatus } from \"./types.js\";\n\nexport type MailboxCallback = (items: SyncOperation[]) => void;\n\n/**\n * The Mailbox interface is not intended to use any persistence. Instead, the\n * IChannel implementation is responsible for persisting cursors or other data.\n *\n * This means that ackOrdinal and latestOrdinal are in memory only.\n */\nexport interface IMailbox {\n get items(): ReadonlyArray<SyncOperation>;\n\n /**\n * The latest ordinal that has been acknowledged. Because acknowledged items\n * are removed from the mailbox, this is the last ordinal that has been removed.\n */\n get ackOrdinal(): number;\n\n /**\n * The latest ordinal of the items that are or have been added to the mailbox.\n * This may be greater than the ack ordinal if items have been added but not\n * yet acknowledged.\n */\n get latestOrdinal(): number;\n\n // sync op management\n init(ackOrdinal: number): void;\n advanceOrdinal(ordinal: number): void;\n get(id: string): SyncOperation | undefined;\n add(...items: SyncOperation[]): void;\n remove(...items: SyncOperation[]): void;\n\n // listeners\n onAdded(callback: MailboxCallback): void;\n onRemoved(callback: MailboxCallback): void;\n\n // these are mostly for debug use\n pause(): void;\n resume(): void;\n flush(): void;\n isPaused(): boolean;\n}\n\nexport class MailboxAggregateError extends Error {\n errors: Error[];\n\n constructor(errors: Error[]) {\n const messages = errors.map((e) => e.message).join(\"; \");\n super(\n `Mailbox callback failed with ${errors.length} error(s): ${messages}`,\n );\n this.name = \"MailboxAggregateError\";\n this.errors = errors;\n }\n}\n\nexport class Mailbox implements IMailbox {\n private itemsMap: Map<string, SyncOperation> = new Map();\n private addedCallbacks: MailboxCallback[] = [];\n private removedCallbacks: MailboxCallback[] = [];\n private paused: boolean = false;\n private addedBuffer: SyncOperation[] = [];\n private removedBuffer: SyncOperation[] = [];\n\n private _ack: number = 0;\n private _latestOrdinal: number = 0;\n\n init(ackOrdinal: number) {\n this._ack = this._latestOrdinal = ackOrdinal;\n }\n\n advanceOrdinal(ordinal: number): void {\n this._latestOrdinal = Math.max(this._latestOrdinal, ordinal);\n }\n\n get items(): ReadonlyArray<SyncOperation> {\n return Array.from(this.itemsMap.values());\n }\n\n get ackOrdinal(): number {\n return this._ack;\n }\n\n get latestOrdinal(): number {\n return this._latestOrdinal;\n }\n\n get(id: string): SyncOperation | undefined {\n return this.itemsMap.get(id);\n }\n\n add(...items: SyncOperation[]): void {\n for (const item of items) {\n this.itemsMap.set(item.id, item);\n\n // update latest ordinal\n for (const op of item.operations) {\n this._latestOrdinal = Math.max(this._latestOrdinal, op.context.ordinal);\n }\n\n // listen for updates to the syncop status\n item.on((syncOp, _, next) => {\n if (next === SyncOperationStatus.Applied) {\n for (const op of syncOp.operations) {\n this._ack = Math.max(this._ack, op.context.ordinal);\n }\n }\n });\n }\n\n if (this.paused) {\n this.addedBuffer.push(...items);\n return;\n }\n\n const callbacks = [...this.addedCallbacks];\n const errors: Error[] = [];\n for (const callback of callbacks) {\n try {\n callback(items);\n } catch (error) {\n errors.push(error instanceof Error ? error : new Error(String(error)));\n }\n }\n if (errors.length > 0) {\n throw new MailboxAggregateError(errors);\n }\n }\n\n remove(...items: SyncOperation[]): void {\n for (const item of items) {\n this.itemsMap.delete(item.id);\n }\n\n if (this.paused) {\n this.removedBuffer.push(...items);\n return;\n }\n\n const callbacks = [...this.removedCallbacks];\n const errors: Error[] = [];\n for (const callback of callbacks) {\n try {\n callback(items);\n } catch (error) {\n errors.push(error instanceof Error ? error : new Error(String(error)));\n }\n }\n if (errors.length > 0) {\n throw new MailboxAggregateError(errors);\n }\n }\n\n onAdded(callback: MailboxCallback): void {\n this.addedCallbacks.push(callback);\n }\n\n onRemoved(callback: MailboxCallback): void {\n this.removedCallbacks.push(callback);\n }\n\n pause(): void {\n this.paused = true;\n }\n\n resume(): void {\n this.paused = false;\n this.flush();\n }\n\n flush(): void {\n if (this.addedBuffer.length > 0) {\n const items = this.addedBuffer.splice(0);\n const callbacks = [...this.addedCallbacks];\n const errors: Error[] = [];\n for (const callback of callbacks) {\n try {\n callback(items);\n } catch (error) {\n errors.push(\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n }\n if (errors.length > 0) {\n throw new MailboxAggregateError(errors);\n }\n }\n\n if (this.removedBuffer.length > 0) {\n const items = this.removedBuffer.splice(0);\n const callbacks = [...this.removedCallbacks];\n const errors: Error[] = [];\n for (const callback of callbacks) {\n try {\n callback(items);\n } catch (error) {\n errors.push(\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n }\n if (errors.length > 0) {\n throw new MailboxAggregateError(errors);\n }\n }\n }\n\n isPaused(): boolean {\n return this.paused;\n }\n}\n","import {\n type IMailbox,\n MailboxAggregateError,\n type MailboxCallback,\n} from \"./mailbox.js\";\nimport type { SyncOperation } from \"./sync-operation.js\";\nimport { SyncOperationStatus } from \"./types.js\";\n\nexport class BufferedMailbox implements IMailbox {\n private itemsMap: Map<string, SyncOperation> = new Map();\n private addedCallbacks: MailboxCallback[] = [];\n private removedCallbacks: MailboxCallback[] = [];\n private addedBuffer: SyncOperation[] = [];\n private removedBuffer: SyncOperation[] = [];\n private addedTimer: ReturnType<typeof setTimeout> | null = null;\n private removedTimer: ReturnType<typeof setTimeout> | null = null;\n private readonly milliseconds: number;\n private readonly maxQueued: number;\n private paused: boolean = false;\n\n private _ack: number = 0;\n private _latestOrdinal: number = 0;\n\n constructor(milliseconds: number, maxQueued: number) {\n this.milliseconds = milliseconds;\n this.maxQueued = maxQueued;\n }\n\n init(ackOrdinal: number) {\n this._ack = this._latestOrdinal = ackOrdinal;\n }\n\n advanceOrdinal(ordinal: number): void {\n this._latestOrdinal = Math.max(this._latestOrdinal, ordinal);\n }\n\n get items(): ReadonlyArray<SyncOperation> {\n return Array.from(this.itemsMap.values());\n }\n\n get ackOrdinal(): number {\n return this._ack;\n }\n\n get latestOrdinal(): number {\n return this._latestOrdinal;\n }\n\n get(id: string): SyncOperation | undefined {\n return this.itemsMap.get(id);\n }\n\n add(...items: SyncOperation[]): void {\n for (const item of items) {\n this.itemsMap.set(item.id, item);\n\n // update latest ordinal\n for (const op of item.operations) {\n this._latestOrdinal = Math.max(this._latestOrdinal, op.context.ordinal);\n }\n\n // listen for updates to the syncop status\n item.on((syncOp, _, next) => {\n if (next === SyncOperationStatus.Applied) {\n for (const op of syncOp.operations) {\n this._ack = Math.max(this._ack, op.context.ordinal);\n }\n }\n });\n }\n this.addedBuffer.push(...items);\n\n if (this.paused) {\n return;\n }\n\n if (this.addedBuffer.length >= this.maxQueued) {\n this.flushAdded();\n } else {\n this.scheduleAddedFlush();\n }\n }\n\n remove(...items: SyncOperation[]): void {\n for (const item of items) {\n this.itemsMap.delete(item.id);\n }\n this.removedBuffer.push(...items);\n\n if (this.paused) {\n return;\n }\n\n if (this.removedBuffer.length >= this.maxQueued) {\n this.flushRemoved();\n } else {\n this.scheduleRemovedFlush();\n }\n }\n\n onAdded(callback: MailboxCallback): void {\n this.addedCallbacks.push(callback);\n }\n\n onRemoved(callback: MailboxCallback): void {\n this.removedCallbacks.push(callback);\n }\n\n pause(): void {\n this.paused = true;\n if (this.addedTimer !== null) {\n clearTimeout(this.addedTimer);\n this.addedTimer = null;\n }\n if (this.removedTimer !== null) {\n clearTimeout(this.removedTimer);\n this.removedTimer = null;\n }\n }\n\n resume(): void {\n this.paused = false;\n if (this.addedBuffer.length > 0) {\n this.scheduleAddedFlush();\n }\n if (this.removedBuffer.length > 0) {\n this.scheduleRemovedFlush();\n }\n }\n\n isPaused(): boolean {\n return this.paused;\n }\n\n flush(): void {\n this.flushAdded();\n this.flushRemoved();\n }\n\n private scheduleAddedFlush(): void {\n if (this.addedTimer !== null) {\n clearTimeout(this.addedTimer);\n }\n this.addedTimer = setTimeout(() => {\n this.flushAdded();\n }, this.milliseconds);\n }\n\n private scheduleRemovedFlush(): void {\n if (this.removedTimer !== null) {\n clearTimeout(this.removedTimer);\n }\n this.removedTimer = setTimeout(() => {\n this.flushRemoved();\n }, this.milliseconds);\n }\n\n private flushAdded(): void {\n if (this.addedTimer !== null) {\n clearTimeout(this.addedTimer);\n this.addedTimer = null;\n }\n\n const items = this.addedBuffer;\n this.addedBuffer = [];\n\n if (items.length > 0) {\n this.invokeCallbacks(this.addedCallbacks, items);\n }\n }\n\n private flushRemoved(): void {\n if (this.removedTimer !== null) {\n clearTimeout(this.removedTimer);\n this.removedTimer = null;\n }\n\n const items = this.removedBuffer;\n this.removedBuffer = [];\n\n if (items.length > 0) {\n this.invokeCallbacks(this.removedCallbacks, items);\n }\n }\n\n private invokeCallbacks(\n callbacks: MailboxCallback[],\n items: SyncOperation[],\n ): void {\n const callbacksCopy = [...callbacks];\n const errors: Error[] = [];\n\n for (const callback of callbacksCopy) {\n try {\n callback(items);\n } catch (error) {\n errors.push(error instanceof Error ? error : new Error(String(error)));\n }\n }\n\n if (errors.length > 0) {\n throw new MailboxAggregateError(errors);\n }\n }\n}\n","import type { ChannelErrorSource } from \"./types.js\";\n\nexport type GraphQLRequestErrorCategory =\n | \"network\"\n | \"http\"\n | \"parse\"\n | \"graphql\"\n | \"missing-data\";\n\nexport class GraphQLRequestError extends Error {\n readonly statusCode: number | undefined;\n readonly category: GraphQLRequestErrorCategory;\n\n constructor(\n message: string,\n category: GraphQLRequestErrorCategory,\n statusCode?: number,\n ) {\n super(message);\n this.name = \"GraphQLRequestError\";\n this.category = category;\n this.statusCode = statusCode;\n }\n}\n\nexport class PollingChannelError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"PollingChannelError\";\n }\n}\n\nexport class ChannelError extends Error {\n source: ChannelErrorSource;\n error: Error;\n\n constructor(source: ChannelErrorSource, error: Error) {\n super(`ChannelError[${source}]: ${error.message}`);\n this.name = \"ChannelError\";\n this.source = source;\n this.error = error;\n }\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { ChannelError } from \"./errors.js\";\nimport { SyncOperationStatus } from \"./types.js\";\n\ntype SyncOperationStatusCallback = (\n syncOp: SyncOperation,\n prev: SyncOperationStatus,\n next: SyncOperationStatus,\n) => void;\n\nexport class SyncOperationAggregateError extends Error {\n errors: Error[];\n\n constructor(errors: Error[]) {\n const messages = errors.map((e) => e.message).join(\"; \");\n super(\n `SyncOperation callback failed with ${errors.length} error(s): ${messages}`,\n );\n this.name = \"SyncOperationAggregateError\";\n this.errors = errors;\n }\n}\n\nexport class SyncOperation {\n readonly id: string;\n readonly jobId: string;\n jobDependencies: string[];\n readonly remoteName: string;\n readonly documentId: string;\n readonly scopes: string[];\n readonly branch: string;\n readonly operations: OperationWithContext[];\n status: SyncOperationStatus;\n error?: ChannelError;\n\n private callbacks: SyncOperationStatusCallback[] = [];\n\n constructor(\n id: string,\n jobId: string,\n jobDependencies: string[],\n remoteName: string,\n documentId: string,\n scopes: string[],\n branch: string,\n operations: OperationWithContext[],\n ) {\n this.id = id;\n this.jobId = jobId;\n this.jobDependencies = jobDependencies;\n this.remoteName = remoteName;\n this.documentId = documentId;\n this.scopes = scopes;\n this.branch = branch;\n this.operations = operations;\n this.status = SyncOperationStatus.Unknown;\n }\n\n on(callback: SyncOperationStatusCallback): void {\n this.callbacks.push(callback);\n }\n\n started(): void {\n this.transition(SyncOperationStatus.TransportPending);\n }\n\n transported(): void {\n this.transition(SyncOperationStatus.ExecutionPending);\n }\n\n executed(): void {\n this.transition(SyncOperationStatus.Applied);\n }\n\n failed(error: ChannelError): void {\n this.error = error;\n this.transition(SyncOperationStatus.Error);\n }\n\n private transition(next: SyncOperationStatus): void {\n const prev = this.status;\n if (next <= prev) {\n return;\n }\n this.status = next;\n const errors: Error[] = [];\n for (const callback of this.callbacks) {\n try {\n callback(this, prev, next);\n } catch (error) {\n errors.push(error instanceof Error ? error : new Error(String(error)));\n }\n }\n if (errors.length > 0) {\n throw new SyncOperationAggregateError(errors);\n }\n }\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { Operation } from \"@powerhousedao/shared/document-model\";\nimport {\n driveCollectionId,\n type OperationIndexEntry,\n} from \"../cache/operation-index-types.js\";\nimport type { JobWriteReadyEvent } from \"../events/types.js\";\nimport type { PreparedBatch } from \"./batch-aggregator.js\";\nimport type { IMailbox } from \"./mailbox.js\";\nimport { SyncOperation } from \"./sync-operation.js\";\nimport {\n SyncOperationStatus,\n type ChannelHealth,\n type RemoteFilter,\n} from \"./types.js\";\n\nexport type OperationBatch = {\n documentId: string;\n branch: string;\n scope: string;\n operations: OperationWithContext[];\n};\n\n/**\n * Trims a mailbox using the jobIds from a batch.\n */\nexport function trimMailboxFromBatch(\n mailbox: IMailbox,\n batch: PreparedBatch,\n): void {\n const toRemove: SyncOperation[] = [];\n\n // we want to guarantee:\n //\n // 1. sync ops are still in the inbox when marked as executed\n // 2. we remove syncops as a batch after they have been executed\n for (const syncOp of batch.entries) {\n for (const item of mailbox.items) {\n if (syncOp.event.jobId === item.jobId) {\n toRemove.push(item);\n break;\n }\n }\n }\n\n if (toRemove.length > 0) {\n for (const syncOp of toRemove) {\n syncOp.executed();\n }\n\n mailbox.remove(...toRemove);\n }\n}\n\n/**\n * Trims a mailbox using the ack ordinal.\n */\nexport function trimMailboxFromAckOrdinal(\n mailbox: IMailbox,\n ackOrdinal: number,\n) {\n const toRemove: SyncOperation[] = [];\n\n // we want to guarantee:\n //\n // 1. sync ops are still in the mailbox when marked as applied\n // 2. we remove syncops as a single batch\n for (const syncOp of mailbox.items) {\n let maxOrdinal = 0;\n for (const op of syncOp.operations) {\n maxOrdinal = Math.max(maxOrdinal, op.context.ordinal);\n }\n\n if (maxOrdinal <= ackOrdinal) {\n toRemove.push(syncOp);\n }\n }\n\n if (toRemove.length > 0) {\n for (const syncOp of toRemove) {\n syncOp.executed();\n }\n\n mailbox.remove(...toRemove);\n }\n}\n\n/**\n * Filters operations based on a remote's filter criteria.\n *\n * @param operations - The operations to filter\n * @param filter - The filter criteria to apply\n * @returns The filtered operations that match the criteria\n */\nexport function filterOperations(\n operations: OperationWithContext[],\n filter: RemoteFilter,\n): OperationWithContext[] {\n return operations.filter((op) => {\n if (filter.branch && op.context.branch !== filter.branch) {\n return false;\n }\n\n if (\n filter.documentId.length > 0 &&\n !filter.documentId.includes(op.context.documentId)\n ) {\n return false;\n }\n\n if (filter.scope.length > 0 && !filter.scope.includes(op.context.scope)) {\n return false;\n }\n\n return true;\n });\n}\n\n/**\n * Creates an idle channel health status.\n *\n * @returns A new idle channel health object\n */\nexport function createIdleHealth(): ChannelHealth {\n return {\n state: \"idle\",\n failureCount: 0,\n };\n}\n\n/**\n * Batches consecutive operations by documentId and scope, preserving ordering.\n *\n * For operations [a1_doc, a1_global, a2_doc, b1_global], this returns:\n * - Batch 1: [a1_doc] for doc-a, document scope\n * - Batch 2: [a1_global] for doc-a, global scope\n * - Batch 3: [a2_doc] for doc-a, document scope\n * - Batch 4: [b1_global] for doc-b, global scope\n *\n * This ensures operations are grouped for efficient processing while maintaining\n * causality across documents and scopes.\n */\n/**\n * Sorts envelopes by the timestamp of their first operation.\n * Envelopes without operations are placed at the end.\n */\nexport function sortEnvelopesByFirstOperationTimestamp<\n T extends {\n operations?:\n | ReadonlyArray<{ operation: { timestampUtcMs: string } }>\n | null\n | undefined;\n },\n>(envelopes: T[]): T[] {\n return envelopes.slice().sort((a, b) => {\n const aTimestamp = a.operations?.[0]?.operation.timestampUtcMs;\n const bTimestamp = b.operations?.[0]?.operation.timestampUtcMs;\n\n if (!aTimestamp && !bTimestamp) return 0;\n if (!aTimestamp) return 1;\n if (!bTimestamp) return -1;\n\n return new Date(aTimestamp).getTime() - new Date(bTimestamp).getTime();\n });\n}\n\nexport function batchOperationsByDocument(\n operations: OperationWithContext[],\n): OperationBatch[] {\n const batches: OperationBatch[] = [];\n\n let currentDocId: string | null = null;\n let currentScope: string | null = null;\n let currentBatch: OperationWithContext[] = [];\n\n const flushBatch = () => {\n if (\n currentBatch.length === 0 ||\n currentDocId === null ||\n currentScope === null\n ) {\n return;\n }\n\n batches.push({\n documentId: currentDocId,\n branch: currentBatch[0].context.branch,\n scope: currentScope,\n operations: currentBatch,\n });\n currentBatch = [];\n };\n\n for (const op of operations) {\n const docId = op.context.documentId;\n const scope = op.context.scope;\n if (docId !== currentDocId || scope !== currentScope) {\n flushBatch();\n currentDocId = docId;\n currentScope = scope;\n }\n currentBatch.push(op);\n }\n\n flushBatch();\n return batches;\n}\n\nexport function getMaxOrdinal(operations: OperationWithContext[]): number {\n return operations.reduce(\n (maxOrdinal, operation) => Math.max(maxOrdinal, operation.context.ordinal),\n 0,\n );\n}\n\nexport function filterByCollectionMembership(\n operations: OperationWithContext[],\n collectionId: string,\n collectionMemberships?: Record<string, string[]>,\n): OperationWithContext[] {\n if (!collectionMemberships) {\n return [];\n }\n\n return operations.filter((op) => {\n const documentId = op.context.documentId;\n if (!(documentId in collectionMemberships)) {\n return false;\n }\n return collectionMemberships[documentId].includes(collectionId);\n });\n}\n\nexport function toOperationWithContext(\n entry: OperationIndexEntry,\n): OperationWithContext {\n return {\n operation: {\n id: entry.id,\n index: entry.index,\n skip: entry.skip,\n hash: entry.hash,\n timestampUtcMs: entry.timestampUtcMs,\n action: entry.action,\n } as Operation,\n context: {\n documentId: entry.documentId,\n documentType: entry.documentType,\n scope: entry.scope,\n branch: entry.branch,\n ordinal: entry.ordinal ?? 0,\n },\n };\n}\n\n/**\n * Merges SyncOperations that share the same (documentId, scope, branch) into\n * a single SyncOperation per group. Within each group, operations are sorted\n * by context.ordinal. The merged SyncOperation keeps the first group member's\n * jobId; all other jobIds are remapped so external dependencies still resolve.\n */\nexport function consolidateSyncOperations(\n syncOps: SyncOperation[],\n): SyncOperation[] {\n if (syncOps.length <= 1) {\n return syncOps;\n }\n\n type GroupKey = string;\n const groups = new Map<\n GroupKey,\n { ops: SyncOperation[]; canonicalJobId: string }\n >();\n const jobIdRemap = new Map<string, string>();\n const insertionOrder: GroupKey[] = [];\n\n for (const syncOp of syncOps) {\n const key: GroupKey = `${syncOp.documentId}|${syncOp.scopes.slice().sort().join(\",\")}|${syncOp.branch}`;\n\n const existing = groups.get(key);\n if (existing) {\n existing.ops.push(syncOp);\n if (syncOp.jobId && syncOp.jobId !== existing.canonicalJobId) {\n jobIdRemap.set(syncOp.jobId, existing.canonicalJobId);\n }\n } else {\n groups.set(key, { ops: [syncOp], canonicalJobId: syncOp.jobId });\n insertionOrder.push(key);\n }\n }\n\n const result: SyncOperation[] = [];\n\n for (const key of insertionOrder) {\n const group = groups.get(key)!;\n const allOperations = group.ops\n .flatMap((op) => op.operations)\n .sort((a, b) => a.context.ordinal - b.context.ordinal);\n\n const allDeps = new Set<string>();\n for (const op of group.ops) {\n for (const dep of op.jobDependencies) {\n allDeps.add(dep);\n }\n }\n allDeps.delete(group.canonicalJobId);\n for (const op of group.ops) {\n allDeps.delete(op.jobId);\n }\n\n const remappedDeps: string[] = [];\n for (const dep of allDeps) {\n const mapped = jobIdRemap.get(dep) ?? dep;\n if (!remappedDeps.includes(mapped) && mapped !== group.canonicalJobId) {\n remappedDeps.push(mapped);\n }\n }\n\n const first = group.ops[0];\n const merged = new SyncOperation(\n first.id,\n first.jobId,\n remappedDeps,\n first.remoteName,\n first.documentId,\n first.scopes,\n first.branch,\n allOperations,\n );\n\n if (first.status > SyncOperationStatus.TransportPending) {\n if (first.status >= SyncOperationStatus.Error) {\n merged.executed();\n } else if (first.status >= SyncOperationStatus.Applied) {\n merged.transported();\n } else {\n merged.started();\n }\n }\n\n result.push(merged);\n }\n\n return result;\n}\n\nexport function mergeCollectionMemberships(\n events: JobWriteReadyEvent[],\n): Record<string, string[]> {\n const mergedMemberships: Record<string, string[]> = {};\n\n for (const event of events) {\n if (event.collectionMemberships) {\n for (const [docId, collections] of Object.entries(\n event.collectionMemberships,\n )) {\n if (!(docId in mergedMemberships)) {\n mergedMemberships[docId] = [];\n }\n for (const c of collections) {\n if (!mergedMemberships[docId].includes(c)) {\n mergedMemberships[docId].push(c);\n }\n }\n }\n }\n\n for (const op of event.operations) {\n const action = op.operation.action as {\n type: string;\n input?: { sourceId?: string; targetId?: string };\n };\n if (action.type !== \"ADD_RELATIONSHIP\") {\n continue;\n }\n const input = action.input;\n if (!input?.sourceId || !input.targetId) {\n continue;\n }\n\n const collectionId = driveCollectionId(op.context.branch, input.sourceId);\n if (!(input.targetId in mergedMemberships)) {\n mergedMemberships[input.targetId] = [];\n }\n if (!mergedMemberships[input.targetId].includes(collectionId)) {\n mergedMemberships[input.targetId].push(collectionId);\n }\n }\n }\n\n return mergedMemberships;\n}\n","import type { IQueue } from \"../../queue/interfaces.js\";\nimport type { IPollTimer } from \"./poll-timer.js\";\n\nexport type PollTimerConfig = {\n intervalMs: number;\n maxQueueDepth: number;\n backpressureCheckIntervalMs: number;\n retryBaseDelayMs: number;\n retryMaxDelayMs: number;\n};\n\nconst DEFAULT_CONFIG: PollTimerConfig = {\n intervalMs: 2000,\n maxQueueDepth: 100,\n backpressureCheckIntervalMs: 500,\n retryBaseDelayMs: 1000,\n retryMaxDelayMs: 300000,\n};\n\nexport function calculateBackoffDelay(\n consecutiveFailures: number,\n retryBaseDelayMs: number,\n retryMaxDelayMs: number,\n random: number,\n): number {\n const backoff = Math.min(\n retryMaxDelayMs,\n retryBaseDelayMs * Math.pow(2, consecutiveFailures - 1),\n );\n return backoff / 2 + random * (backoff / 2);\n}\n\n/**\n * Default poll timer using setTimeout.\n * Waits for delegate completion before scheduling next tick.\n * Checks queue depth and defers polling when backpressure is detected.\n */\nexport class IntervalPollTimer implements IPollTimer {\n private delegate: (() => Promise<void>) | undefined;\n private timer: NodeJS.Timeout | undefined;\n private running: boolean;\n private paused: boolean;\n private consecutiveFailures: number;\n private readonly queue: IQueue;\n private readonly config: PollTimerConfig;\n\n constructor(queue: IQueue, config: Partial<PollTimerConfig> = {}) {\n this.queue = queue;\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.running = false;\n this.paused = false;\n this.consecutiveFailures = 0;\n }\n\n setDelegate(delegate: () => Promise<void>): void {\n this.delegate = delegate;\n }\n\n start(): void {\n this.running = true;\n this.consecutiveFailures = 0;\n this.tick();\n }\n\n stop(): void {\n this.running = false;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n }\n\n private tick(): void {\n if (!this.delegate || !this.running) return;\n\n const delegate = this.delegate;\n\n void this.queue\n .totalSize()\n .then((size) => {\n if (!this.running) return;\n if (size > this.config.maxQueueDepth) {\n this.scheduleBackpressureRecheck();\n } else {\n void delegate()\n .then(() => {\n this.consecutiveFailures = 0;\n this.scheduleNext();\n })\n .catch(() => {\n this.consecutiveFailures++;\n this.scheduleRetry();\n });\n }\n })\n .catch(() => {\n // Fail-open: schedule next at normal interval when totalSize() throws\n this.scheduleNext();\n });\n }\n\n private scheduleNext(): void {\n if (!this.running || this.paused) return;\n this.timer = setTimeout(() => this.tick(), this.config.intervalMs);\n }\n\n private scheduleRetry(): void {\n if (!this.running || this.paused) return;\n const delay = calculateBackoffDelay(\n this.consecutiveFailures,\n this.config.retryBaseDelayMs,\n this.config.retryMaxDelayMs,\n Math.random(),\n );\n this.timer = setTimeout(() => this.tick(), delay);\n }\n\n private scheduleBackpressureRecheck(): void {\n if (!this.running || this.paused) return;\n this.timer = setTimeout(\n () => this.tick(),\n this.config.backpressureCheckIntervalMs,\n );\n }\n\n pause(): void {\n this.paused = true;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n }\n\n resume(): void {\n this.paused = false;\n if (this.running) {\n this.scheduleNext();\n }\n }\n\n triggerNow(): void {\n if (this.running && this.delegate) {\n this.tick();\n }\n }\n\n isPaused(): boolean {\n return this.paused;\n }\n\n isRunning(): boolean {\n return this.running;\n }\n\n getIntervalMs(): number {\n return this.config.intervalMs;\n }\n\n setIntervalMs(ms: number): void {\n this.config.intervalMs = ms;\n }\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type {\n Action,\n Operation,\n Signature,\n} from \"@powerhousedao/shared/document-model\";\nimport { SyncOperation } from \"../sync-operation.js\";\nimport { SyncOperationStatus, type SyncEnvelope } from \"../types.js\";\nimport { batchOperationsByDocument } from \"../utils.js\";\n\nlet syncOpCounter = 0;\n\n/**\n * Serializes an action for GraphQL transport, converting signature tuples to strings.\n */\nexport function serializeAction(action: Action): unknown {\n const signer = action.context?.signer;\n if (!signer?.signatures) {\n return action;\n }\n\n return {\n ...action,\n context: {\n ...action.context,\n signer: {\n ...signer,\n signatures: signer.signatures.map((sig: Signature | string) =>\n Array.isArray(sig) ? sig.join(\", \") : sig,\n ),\n },\n },\n };\n}\n\n/**\n * Serializes a SyncEnvelope for GraphQL transport.\n *\n * Signatures are serialized as comma-separated strings since GraphQL schema\n * defines them as [String!]!. The resultingState context field is stripped\n * since it is not defined in OperationContextInput.\n */\nexport function serializeEnvelope(envelope: SyncEnvelope): unknown {\n return {\n type: envelope.type.toUpperCase(),\n channelMeta: envelope.channelMeta,\n operations: envelope.operations?.map((opWithContext) => ({\n operation: {\n index: opWithContext.operation.index,\n timestampUtcMs: opWithContext.operation.timestampUtcMs,\n hash: opWithContext.operation.hash,\n skip: opWithContext.operation.skip,\n error: opWithContext.operation.error,\n id: opWithContext.operation.id,\n action: serializeAction(opWithContext.operation.action),\n },\n context: {\n documentId: opWithContext.context.documentId,\n documentType: opWithContext.context.documentType,\n scope: opWithContext.context.scope,\n branch: opWithContext.context.branch,\n ordinal: opWithContext.context.ordinal,\n },\n })),\n cursor: envelope.cursor,\n key: envelope.key,\n dependsOn: envelope.dependsOn,\n };\n}\n\n/**\n * Deserializes a signature from a comma-separated string back to a tuple.\n *\n * GraphQL serializes Signature tuples as comma-separated strings for transport.\n * This function converts them back to the expected [string, string, string, string, string] format.\n */\nfunction deserializeSignature(sig: Signature | string): Signature {\n if (Array.isArray(sig)) {\n return sig;\n }\n return sig.split(\", \") as Signature;\n}\n\n/**\n * Deserializes signatures in an operation's signer context from strings back to tuples.\n *\n * When operations are transported via GraphQL, signatures are serialized as comma-separated\n * strings. This function restores them to the Signature tuple format required for verification.\n */\nfunction deserializeOperationSignatures(\n opWithContext: OperationWithContext,\n): OperationWithContext {\n const signer = opWithContext.operation.action.context?.signer;\n if (!signer?.signatures || signer.signatures.length === 0) {\n return opWithContext;\n }\n\n const deserializedSignatures = signer.signatures.map(deserializeSignature);\n\n const deserializedOperation: Operation = {\n ...opWithContext.operation,\n action: {\n ...opWithContext.operation.action,\n context: {\n ...opWithContext.operation.action.context,\n signer: {\n ...signer,\n signatures: deserializedSignatures,\n },\n },\n },\n };\n\n return {\n ...opWithContext,\n operation: deserializedOperation,\n };\n}\n\n/**\n * Converts a SyncEnvelope containing operations into a SyncOperation.\n *\n * Extracts the necessary metadata from the envelope's operations to create\n * a sync operation that can be processed by the receiving channel. Also\n * deserializes any signatures from comma-separated strings back to tuples,\n * as GraphQL transport serializes Signature tuples for compatibility.\n *\n * @param envelope - The sync envelope containing operations\n * @param remoteName - The name of the remote this sync operation is associated with\n * @returns A new SyncOperation containing the envelope's operations with deserialized signatures\n * @throws Error if envelope has no operations or operations array is empty\n */\nexport function envelopeToSyncOperation(\n envelope: SyncEnvelope,\n remoteName: string,\n): SyncOperation {\n if (!envelope.operations || envelope.operations.length === 0) {\n throw new Error(\n \"Cannot create SyncOperation from envelope without operations\",\n );\n }\n\n const deserializedOperations = envelope.operations.map(\n deserializeOperationSignatures,\n );\n const firstOp = deserializedOperations[0];\n const documentId = firstOp.context.documentId;\n const branch = firstOp.context.branch;\n const scopes = [\n ...new Set(deserializedOperations.map((op) => op.context.scope)),\n ];\n\n const syncOpId = `syncop-${envelope.channelMeta.id}-${Date.now()}-${syncOpCounter++}`;\n\n return new SyncOperation(\n syncOpId,\n envelope.key ?? \"\",\n (envelope.dependsOn ?? []).filter(Boolean),\n remoteName,\n documentId,\n scopes,\n branch,\n deserializedOperations,\n );\n}\n\n/**\n * Converts a SyncEnvelope containing operations into multiple SyncOperations.\n *\n * This function batches operations by documentId, preserving cross-document ordering.\n * For operations [a1, a2, a3, b1, b2, a4], it returns:\n * - SyncOperation 1: [a1, a2, a3] for doc-a\n * - SyncOperation 2: [b1, b2] for doc-b\n * - SyncOperation 3: [a4] for doc-a\n *\n * This ensures operations are grouped for efficient processing while maintaining\n * causality across documents.\n */\nexport function envelopesToSyncOperations(\n envelope: SyncEnvelope,\n remoteName: string,\n): SyncOperation[] {\n if (!envelope.operations || envelope.operations.length === 0) {\n return [];\n }\n\n const deserializedOps = envelope.operations.map(\n deserializeOperationSignatures,\n );\n const batches = batchOperationsByDocument(deserializedOps);\n\n return batches.map((batch) => {\n const syncOpId = `syncop-${envelope.channelMeta.id}-${Date.now()}-${syncOpCounter++}`;\n return new SyncOperation(\n syncOpId,\n envelope.key ?? \"\",\n (envelope.dependsOn ?? []).filter(Boolean),\n remoteName,\n batch.documentId,\n [batch.scope],\n batch.branch,\n batch.operations,\n );\n });\n}\n\nexport const getLatestAppliedOrdinal = (syncOps: SyncOperation[]): number => {\n let maxOrdinal = 0;\n for (const syncOp of syncOps) {\n if (syncOp.status === SyncOperationStatus.Applied) {\n for (const op of syncOp.operations) {\n maxOrdinal = Math.max(maxOrdinal, op.context.ordinal);\n }\n }\n }\n return maxOrdinal;\n};\n","import type { ILogger } from \"document-model\";\nimport type { IOperationIndex } from \"../../cache/operation-index-types.js\";\nimport type { ISyncCursorStorage } from \"../../storage/interfaces.js\";\nimport { BufferedMailbox } from \"../buffered-mailbox.js\";\nimport { ChannelError, GraphQLRequestError } from \"../errors.js\";\nimport type { ConnectionStateChangeCallback, IChannel } from \"../interfaces.js\";\nimport { type IMailbox, Mailbox } from \"../mailbox.js\";\nimport { SyncOperation } from \"../sync-operation.js\";\nimport type {\n ConnectionState,\n ConnectionStateSnapshot,\n JwtHandler,\n RemoteFilter,\n SyncEnvelope,\n} from \"../types.js\";\nimport { ChannelErrorSource } from \"../types.js\";\nimport {\n consolidateSyncOperations,\n sortEnvelopesByFirstOperationTimestamp,\n trimMailboxFromAckOrdinal,\n} from \"../utils.js\";\nimport { calculateBackoffDelay } from \"./interval-poll-timer.js\";\nimport type { IPollTimer } from \"./poll-timer.js\";\nimport {\n envelopesToSyncOperations,\n getLatestAppliedOrdinal,\n serializeEnvelope,\n} from \"./utils.js\";\n\n/**\n * Configuration parameters for GqlChannel\n */\nexport type GqlChannelConfig = {\n /** The GraphQL endpoint URL */\n url: string;\n /** Dynamic JWT token handler for generating fresh tokens per-request */\n jwtHandler?: JwtHandler;\n /** Custom fetch function for testing (default: global fetch) */\n fetchFn?: typeof fetch;\n /** Collection ID to synchronize */\n collectionId: string;\n /** Filter to apply to operations */\n filter: RemoteFilter;\n /** Base delay in ms for exponential backoff on push retries */\n retryBaseDelayMs: number;\n /** Maximum delay in ms for exponential backoff on push retries */\n retryMaxDelayMs: number;\n};\n\n/**\n * GraphQL-based synchronization channel for network communication between reactors.\n */\nexport class GqlRequestChannel implements IChannel {\n readonly inbox: IMailbox;\n readonly outbox: IMailbox;\n readonly deadLetter: IMailbox;\n readonly config: GqlChannelConfig;\n private readonly bufferedOutbox: BufferedMailbox;\n\n private readonly channelId: string;\n private readonly remoteName: string;\n private readonly cursorStorage: ISyncCursorStorage;\n private readonly operationIndex: IOperationIndex;\n private readonly pollTimer: IPollTimer;\n private readonly abortController = new AbortController();\n private isShutdown: boolean;\n private failureCount: number;\n private lastSuccessUtcMs?: number;\n private lastFailureUtcMs?: number;\n private lastPersistedInboxOrdinal: number = 0;\n private lastPersistedOutboxOrdinal: number = 0;\n private pushFailureCount: number = 0;\n private pushRetryTimer: ReturnType<typeof setTimeout> | null = null;\n private pushBlocked: boolean = false;\n private isPushing: boolean = false;\n private pendingDrain: boolean = false;\n private connectionState: ConnectionState = \"connecting\";\n private readonly connectionStateCallbacks: Set<ConnectionStateChangeCallback> =\n new Set();\n\n constructor(\n private readonly logger: ILogger,\n channelId: string,\n remoteName: string,\n cursorStorage: ISyncCursorStorage,\n config: GqlChannelConfig,\n operationIndex: IOperationIndex,\n pollTimer: IPollTimer,\n ) {\n this.channelId = channelId;\n this.remoteName = remoteName;\n this.cursorStorage = cursorStorage;\n this.operationIndex = operationIndex;\n this.pollTimer = pollTimer;\n this.config = {\n url: config.url,\n jwtHandler: config.jwtHandler,\n fetchFn: config.fetchFn,\n collectionId: config.collectionId,\n filter: config.filter,\n retryBaseDelayMs: config.retryBaseDelayMs,\n retryMaxDelayMs: config.retryMaxDelayMs,\n };\n this.isShutdown = false;\n this.failureCount = 0;\n\n this.inbox = new Mailbox();\n this.bufferedOutbox = new BufferedMailbox(500, 25);\n this.outbox = this.bufferedOutbox;\n this.deadLetter = new Mailbox();\n\n this.deadLetter.onAdded((syncOps) => {\n for (const syncOp of syncOps) {\n this.logger.warn(\n \"Dead letter added for document @DocumentId on channel @ChannelId\",\n syncOp.documentId,\n this.channelId,\n );\n }\n });\n\n // when sync ops are added to the outbox, push them to the remote\n this.outbox.onAdded((syncOps) => {\n if (this.isShutdown) return;\n if (this.isPushing) {\n this.pendingDrain = true;\n return;\n }\n if (this.pushBlocked) return; // ops stay in outbox, included in next retry\n this.attemptPush(syncOps);\n });\n\n // Instead of listening to syncops directly for cursor updates, we listen\n // to the mailbox. This is for efficiency: many syncops may fire on a trim,\n // but only one onRemoved callback will be fired for the batch.\n this.outbox.onRemoved((syncOps) => {\n const maxOrdinal = getLatestAppliedOrdinal(syncOps);\n if (maxOrdinal > this.lastPersistedOutboxOrdinal) {\n this.lastPersistedOutboxOrdinal = maxOrdinal;\n this.cursorStorage\n .upsert({\n remoteName: this.remoteName,\n cursorType: \"outbox\",\n cursorOrdinal: maxOrdinal,\n lastSyncedAtUtcMs: Date.now(),\n })\n .catch((error) => {\n this.logger.error(\n \"Failed to update outbox cursor for @ChannelId! This means that future application runs may resend duplicate operations. This is recoverable (with deduplication protection), but not-optimal: @Error\",\n this.channelId,\n error,\n );\n });\n }\n });\n\n this.inbox.onRemoved((syncOps) => {\n const maxOrdinal = getLatestAppliedOrdinal(syncOps);\n if (maxOrdinal > this.lastPersistedInboxOrdinal) {\n this.lastPersistedInboxOrdinal = maxOrdinal;\n this.cursorStorage\n .upsert({\n remoteName: this.remoteName,\n cursorType: \"inbox\",\n cursorOrdinal: maxOrdinal,\n lastSyncedAtUtcMs: Date.now(),\n })\n .catch((error) => {\n this.logger.error(\n \"Failed to update inbox cursor for @ChannelId! This is unlikely to cause a problem, but not-optimal: @Error\",\n this.channelId,\n error,\n );\n });\n }\n });\n }\n\n /**\n * Shuts down the channel and prevents further operations.\n */\n shutdown(): Promise<void> {\n this.abortController.abort();\n this.bufferedOutbox.flush();\n this.isShutdown = true;\n this.pollTimer.stop();\n\n if (this.pushRetryTimer) {\n clearTimeout(this.pushRetryTimer);\n this.pushRetryTimer = null;\n }\n\n this.transitionConnectionState(\"disconnected\");\n\n return Promise.resolve();\n }\n\n getConnectionState(): ConnectionStateSnapshot {\n return {\n state: this.connectionState,\n failureCount: this.failureCount,\n lastSuccessUtcMs: this.lastSuccessUtcMs ?? 0,\n lastFailureUtcMs: this.lastFailureUtcMs ?? 0,\n pushBlocked: this.pushBlocked,\n pushFailureCount: this.pushFailureCount,\n };\n }\n\n onConnectionStateChange(callback: ConnectionStateChangeCallback): () => void {\n this.connectionStateCallbacks.add(callback);\n return () => {\n this.connectionStateCallbacks.delete(callback);\n };\n }\n\n /**\n * Initializes the channel by registering it on the remote server and starting polling.\n */\n async init(): Promise<void> {\n const { ackOrdinal } = await this.touchRemoteChannel();\n\n // get cursors -- these are the last acknowledged ordinals for the inbox and outbox\n const cursors = await this.cursorStorage.list(this.remoteName);\n const inboxOrdinal =\n cursors.find((c) => c.cursorType === \"inbox\")?.cursorOrdinal ?? 0;\n const outboxOrdinal =\n cursors.find((c) => c.cursorType === \"outbox\")?.cursorOrdinal ?? 0;\n this.inbox.init(inboxOrdinal);\n this.outbox.init(outboxOrdinal);\n this.lastPersistedInboxOrdinal = inboxOrdinal;\n this.lastPersistedOutboxOrdinal = outboxOrdinal;\n\n if (ackOrdinal > 0) {\n trimMailboxFromAckOrdinal(this.outbox, ackOrdinal);\n }\n\n this.pollTimer.setDelegate(() => this.poll());\n this.pollTimer.start();\n this.transitionConnectionState(\"connected\");\n }\n\n private transitionConnectionState(next: ConnectionState): void {\n if (this.connectionState === next) return;\n this.connectionState = next;\n const snapshot = this.getConnectionState();\n for (const callback of this.connectionStateCallbacks) {\n try {\n callback(snapshot);\n } catch (error) {\n this.logger.error(\n \"Connection state change callback error: @Error\",\n error,\n );\n }\n }\n }\n\n /**\n * Polls the remote for new sync envelopes.\n */\n private async poll(): Promise<void> {\n if (this.isShutdown) {\n return;\n }\n\n let response;\n try {\n response = await this.pollSyncEnvelopes(\n this.inbox.ackOrdinal,\n this.inbox.latestOrdinal,\n );\n } catch (error) {\n if (!this.handlePollError(error)) {\n throw error;\n }\n return;\n }\n\n const { envelopes, ackOrdinal, deadLetters } = response;\n\n // first: trim outbox\n if (ackOrdinal > 0) {\n trimMailboxFromAckOrdinal(this.outbox, ackOrdinal);\n }\n\n // todo: Is this necessary? Outbox items should have been sorted when returned.\n const sortedEnvelopes = sortEnvelopesByFirstOperationTimestamp(envelopes);\n\n // convert the envelopes to sync operations\n const allSyncOps: SyncOperation[] = [];\n for (const envelope of sortedEnvelopes) {\n if (envelope.type.toLowerCase() === \"operations\" && envelope.operations) {\n const syncOps = envelopesToSyncOperations(envelope, this.remoteName);\n for (const syncOp of syncOps) {\n syncOp.transported();\n }\n allSyncOps.push(...syncOps);\n }\n }\n\n // merge SyncOps sharing the same (documentId, scope, branch) so\n // multiple polled envelopes for one document become a single load job\n const consolidated =\n allSyncOps.length > 1\n ? consolidateSyncOperations(allSyncOps)\n : allSyncOps;\n\n if (consolidated.length > 0) {\n this.inbox.add(...consolidated);\n }\n\n // handle dead letters from the remote\n if (deadLetters.length > 0) {\n this.handleRemoteDeadLetters(deadLetters);\n }\n\n this.lastSuccessUtcMs = Date.now();\n this.failureCount = 0;\n this.transitionConnectionState(\"connected\");\n }\n\n /**\n * Handles dead letters reported by the remote server.\n * Creates local dead letter SyncOperations so the channel quiesces.\n */\n private handleRemoteDeadLetters(\n deadLetters: Array<{\n documentId: string;\n error: string;\n jobId: string;\n branch: string;\n scopes: string[];\n operationCount: number;\n }>,\n ): void {\n for (const dl of deadLetters) {\n this.logger.error(\n \"Remote dead letter on @ChannelId: document @DocumentId failed with: @Error\",\n this.channelId,\n dl.documentId,\n dl.error,\n );\n }\n\n const syncOps: SyncOperation[] = [];\n for (const dl of deadLetters) {\n const syncOp = new SyncOperation(\n crypto.randomUUID(),\n dl.jobId,\n [],\n this.remoteName,\n dl.documentId,\n dl.scopes,\n dl.branch,\n [],\n );\n syncOp.failed(\n new ChannelError(ChannelErrorSource.Outbox, new Error(dl.error)),\n );\n syncOps.push(syncOp);\n }\n this.deadLetter.add(...syncOps);\n }\n\n /**\n * Handles polling errors with error classification.\n * Returns true if the error was handled (caller should not rethrow).\n */\n private handlePollError(error: unknown): boolean {\n if (this.isShutdown) return true;\n\n const err = error instanceof Error ? error : new Error(String(error));\n\n if (err.message.includes(\"Channel not found\")) {\n this.transitionConnectionState(\"reconnecting\");\n this.recoverFromChannelNotFound();\n return true;\n }\n\n const classification = this.classifyError(err);\n\n this.failureCount++;\n this.lastFailureUtcMs = Date.now();\n\n const channelError = new ChannelError(ChannelErrorSource.Inbox, err);\n\n this.logger.error(\n \"GqlChannel poll error (@FailureCount, @Classification): @Error\",\n this.failureCount,\n classification,\n channelError,\n );\n\n if (classification === \"unrecoverable\") {\n this.pollTimer.stop();\n this.transitionConnectionState(\"error\");\n return true;\n }\n\n this.transitionConnectionState(\"error\");\n return false;\n }\n\n /**\n * Recovers from a \"Channel not found\" error by re-registering and restarting polling.\n * Self-retries with backoff instead of restarting the poll timer on failure.\n */\n private recoverFromChannelNotFound(): void {\n this.logger.info(\n \"GqlChannel @ChannelId not found on remote, re-registering...\",\n this.channelId,\n );\n\n this.pollTimer.stop();\n\n const attemptRecovery = (attempt: number): void => {\n if (this.isShutdown) return;\n\n void this.touchRemoteChannel()\n .then(({ ackOrdinal }) => {\n this.logger.info(\n \"GqlChannel @ChannelId re-registered successfully\",\n this.channelId,\n );\n this.failureCount = 0;\n if (ackOrdinal > 0) {\n trimMailboxFromAckOrdinal(this.outbox, ackOrdinal);\n }\n this.pollTimer.start();\n this.transitionConnectionState(\"connected\");\n })\n .catch((recoveryError: unknown) => {\n const err =\n recoveryError instanceof Error\n ? recoveryError\n : new Error(String(recoveryError));\n const classification = this.classifyError(err);\n\n this.logger.error(\n \"GqlChannel @ChannelId recovery attempt @Attempt failed (@Classification): @Error\",\n this.channelId,\n attempt,\n classification,\n recoveryError,\n );\n\n this.failureCount++;\n this.lastFailureUtcMs = Date.now();\n\n if (classification === \"unrecoverable\") {\n this.transitionConnectionState(\"error\");\n return;\n }\n\n this.transitionConnectionState(\"reconnecting\");\n const delay = calculateBackoffDelay(\n attempt,\n this.config.retryBaseDelayMs,\n this.config.retryMaxDelayMs,\n Math.random(),\n );\n setTimeout(() => attemptRecovery(attempt + 1), delay);\n });\n };\n\n attemptRecovery(1);\n }\n\n /**\n * Queries the remote GraphQL endpoint for sync envelopes.\n */\n private async pollSyncEnvelopes(\n ackOrdinal: number,\n latestOrdinal: number,\n ): Promise<{\n envelopes: SyncEnvelope[];\n ackOrdinal: number;\n deadLetters: Array<{\n documentId: string;\n error: string;\n jobId: string;\n branch: string;\n scopes: string[];\n operationCount: number;\n }>;\n }> {\n const query = `\n query PollSyncEnvelopes($channelId: String!, $outboxAck: Int!, $outboxLatest: Int!) {\n pollSyncEnvelopes(channelId: $channelId, outboxAck: $outboxAck, outboxLatest: $outboxLatest) {\n envelopes {\n type\n channelMeta {\n id\n }\n operations {\n operation {\n index\n timestampUtcMs\n hash\n skip\n error\n id\n action {\n id\n type\n timestampUtcMs\n input\n scope\n attachments {\n data\n mimeType\n hash\n extension\n fileName\n }\n context {\n signer {\n user {\n address\n networkId\n chainId\n }\n app {\n name\n key\n }\n signatures\n }\n }\n }\n }\n context {\n documentId\n documentType\n scope\n branch\n ordinal\n }\n }\n cursor {\n remoteName\n cursorOrdinal\n lastSyncedAtUtcMs\n }\n key\n dependsOn\n }\n ackOrdinal\n deadLetters {\n documentId\n error\n jobId\n branch\n scopes\n operationCount\n }\n }\n }\n `;\n\n const variables = {\n channelId: this.channelId,\n outboxAck: ackOrdinal,\n outboxLatest: latestOrdinal,\n };\n\n const response = await this.executeGraphQL<{\n pollSyncEnvelopes: {\n envelopes: SyncEnvelope[];\n ackOrdinal: number;\n deadLetters?: Array<{\n documentId: string;\n error: string;\n jobId: string;\n branch: string;\n scopes: string[];\n operationCount: number;\n }>;\n };\n }>(query, variables);\n\n return {\n envelopes: response.pollSyncEnvelopes.envelopes,\n ackOrdinal: response.pollSyncEnvelopes.ackOrdinal,\n deadLetters: response.pollSyncEnvelopes.deadLetters ?? [],\n };\n }\n\n /**\n * Registers or updates this channel on the remote server via GraphQL mutation.\n * Returns the remote's ack ordinal so the client can trim its outbox.\n */\n private async touchRemoteChannel(): Promise<{ ackOrdinal: number }> {\n let sinceTimestampUtcMs = \"0\";\n try {\n const result = await this.operationIndex.getLatestTimestampForCollection(\n this.config.collectionId,\n );\n if (result) {\n sinceTimestampUtcMs = result;\n }\n } catch {\n // If query fails, use default \"0\" (sends all operations)\n }\n\n const mutation = `\n mutation TouchChannel($input: TouchChannelInput!) {\n touchChannel(input: $input) {\n success\n ackOrdinal\n }\n }\n `;\n\n const variables = {\n input: {\n id: this.channelId,\n name: this.channelId,\n collectionId: this.config.collectionId,\n filter: {\n documentId: this.config.filter.documentId,\n scope: this.config.filter.scope,\n branch: this.config.filter.branch,\n },\n sinceTimestampUtcMs,\n },\n };\n\n const data = await this.executeGraphQL<{\n touchChannel: { success: boolean; ackOrdinal: number };\n }>(mutation, variables);\n\n if (!data.touchChannel.success) {\n throw new GraphQLRequestError(\n \"touchChannel returned success=false\",\n \"graphql\",\n );\n }\n\n return { ackOrdinal: data.touchChannel.ackOrdinal };\n }\n\n /**\n * Fire-and-forget push with retry on recoverable errors.\n * On success, clears push blocked state. On recoverable error, blocks\n * further pushes and schedules a retry. On unrecoverable error, moves\n * ops to deadLetter.\n */\n private attemptPush(syncOps: SyncOperation[]): void {\n this.isPushing = true;\n this.pushSyncOperations(syncOps)\n .then(() => {\n this.isPushing = false;\n this.pushBlocked = false;\n this.pushFailureCount = 0;\n if (\n this.connectionState === \"reconnecting\" ||\n this.connectionState === \"error\"\n ) {\n this.transitionConnectionState(\"connected\");\n }\n this.drainOutbox();\n })\n .catch((error) => {\n this.isPushing = false;\n this.pendingDrain = false;\n if (this.isShutdown) return;\n\n const err = error instanceof Error ? error : new Error(String(error));\n const classification = this.classifyError(err);\n\n if (classification === \"recoverable\") {\n this.pushFailureCount++;\n this.pushBlocked = true;\n this.logger.error(\n \"GqlChannel push failed (attempt @FailureCount), will retry: @Error\",\n this.pushFailureCount,\n err,\n );\n this.transitionConnectionState(\"reconnecting\");\n this.schedulePushRetry();\n } else {\n const channelError = new ChannelError(ChannelErrorSource.Outbox, err);\n for (const syncOp of syncOps) {\n syncOp.failed(channelError);\n }\n this.deadLetter.add(...syncOps);\n this.outbox.remove(...syncOps);\n this.transitionConnectionState(\"error\");\n }\n });\n }\n\n /**\n * Schedules a retry of all current outbox items using exponential backoff.\n */\n private schedulePushRetry(): void {\n if (this.pushRetryTimer) return;\n\n const delay = calculateBackoffDelay(\n this.pushFailureCount,\n this.config.retryBaseDelayMs,\n this.config.retryMaxDelayMs,\n Math.random(),\n );\n\n this.pushRetryTimer = setTimeout(() => {\n this.pushRetryTimer = null;\n\n if (this.isShutdown) return;\n\n const allItems = this.outbox.items;\n if (allItems.length === 0) {\n this.pushBlocked = false;\n this.pushFailureCount = 0;\n return;\n }\n\n this.attemptPush([...allItems]);\n }, delay);\n }\n\n /**\n * Drains pending outbox items that arrived while a push was in-flight.\n * Server-side action.id dedup handles any overlap with the previous push.\n */\n private drainOutbox(): void {\n if (!this.pendingDrain) return;\n this.pendingDrain = false;\n if (this.isShutdown) return;\n const items = this.outbox.items;\n if (items.length === 0) return;\n this.attemptPush([...items]);\n }\n\n /**\n * Classifies an error as recoverable or unrecoverable based on its type.\n * Recoverable errors are transient and worth retrying (network, 5xx, parse).\n * Unrecoverable errors will not self-heal (auth, client errors, GraphQL rejections).\n */\n private classifyError(error: Error): \"recoverable\" | \"unrecoverable\" {\n if (!(error instanceof GraphQLRequestError)) {\n return \"recoverable\";\n }\n\n switch (error.category) {\n case \"network\":\n return \"recoverable\";\n case \"http\": {\n if (error.statusCode !== undefined && error.statusCode >= 500) {\n return \"recoverable\";\n }\n return \"unrecoverable\";\n }\n case \"parse\":\n return \"recoverable\";\n case \"graphql\":\n return \"unrecoverable\";\n case \"missing-data\":\n return \"unrecoverable\";\n }\n }\n\n /**\n * Pushes multiple sync operations to the remote via a single GraphQL mutation.\n * Creates one SyncEnvelope per SyncOperation with key/dependsOn for batch ordering.\n */\n private async pushSyncOperations(syncOps: SyncOperation[]): Promise<void> {\n for (const syncOp of syncOps) {\n syncOp.started();\n }\n\n const jobIdToKeys = new Map<string, string[]>();\n const envelopes: SyncEnvelope[] = [];\n\n for (let i = 0; i < syncOps.length; i++) {\n const syncOp = syncOps[i];\n const key = String(i);\n\n if (syncOp.jobId) {\n if (!jobIdToKeys.has(syncOp.jobId)) {\n jobIdToKeys.set(syncOp.jobId, []);\n }\n jobIdToKeys.get(syncOp.jobId)!.push(key);\n }\n\n const dependsOn: string[] = [];\n for (const dep of syncOp.jobDependencies) {\n const depKeys = jobIdToKeys.get(dep);\n if (depKeys) {\n dependsOn.push(...depKeys);\n }\n }\n\n this.logger.debug(\n \"[PUSH]: @Operations\",\n syncOp.operations.map(\n (op) =>\n `(${op.context.documentId}, ${op.context.branch}, ${op.context.scope}, ${op.operation.index})`,\n ),\n );\n\n envelopes.push({\n type: \"operations\",\n channelMeta: { id: this.channelId },\n operations: syncOp.operations,\n key,\n dependsOn,\n });\n }\n\n const mutation = `\n mutation PushSyncEnvelopes($envelopes: [SyncEnvelopeInput!]!) {\n pushSyncEnvelopes(envelopes: $envelopes)\n }\n `;\n\n const variables = {\n envelopes: envelopes.map((e) => serializeEnvelope(e)),\n };\n\n await this.executeGraphQL<{ pushSyncEnvelopes: boolean }>(\n mutation,\n variables,\n );\n }\n\n /**\n * Gets the authorization header value using jwtHandler.\n */\n private async getAuthorizationHeader(): Promise<string | undefined> {\n if (!this.config.jwtHandler) {\n return undefined;\n }\n\n try {\n const token = await this.config.jwtHandler(this.config.url);\n if (token) {\n return `Bearer ${token}`;\n }\n } catch (error) {\n this.logger.error(\"JWT handler failed: @Error\", error);\n }\n return undefined;\n }\n\n /**\n * Executes a GraphQL query or mutation against the remote endpoint.\n */\n private async executeGraphQL<T>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<T> {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n const authHeader = await this.getAuthorizationHeader();\n if (authHeader) {\n headers[\"Authorization\"] = authHeader;\n }\n\n const operationMatch = query.match(/(?:query|mutation)\\s+(\\w+)/);\n const operationName = operationMatch?.[1] ?? \"unknown\";\n\n this.logger.verbose(\n \"GQL request @channelId @operation @url vars=@variables\",\n this.channelId,\n operationName,\n this.config.url,\n JSON.stringify(variables),\n );\n\n const fetchFn = this.config.fetchFn ?? fetch;\n let response;\n try {\n response = await fetchFn(this.config.url, {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n query,\n variables,\n }),\n signal: this.abortController.signal,\n });\n } catch (error) {\n throw new GraphQLRequestError(\n `GraphQL request failed: ${error instanceof Error ? error.message : String(error)}`,\n \"network\",\n );\n }\n\n if (!response.ok) {\n throw new GraphQLRequestError(\n `GraphQL request failed: ${response.status} ${response.statusText}`,\n \"http\",\n response.status,\n );\n }\n\n let result;\n try {\n result = (await response.json()) as {\n data?: T;\n errors?: Array<{ message: string }>;\n };\n } catch (error) {\n throw new GraphQLRequestError(\n `Failed to parse GraphQL response: ${error instanceof Error ? error.message : String(error)}`,\n \"parse\",\n );\n }\n\n this.logger.verbose(\n \"GQL response @channelId @operation status=@status data=@data errors=@errors\",\n this.channelId,\n operationName,\n response.status,\n JSON.stringify(result.data),\n result.errors ? JSON.stringify(result.errors) : \"none\",\n );\n\n if (result.errors) {\n throw new GraphQLRequestError(\n `GraphQL errors: ${JSON.stringify(result.errors, null, 2)}`,\n \"graphql\",\n );\n }\n\n if (!result.data) {\n throw new GraphQLRequestError(\n \"GraphQL response missing data field\",\n \"missing-data\",\n );\n }\n\n return result.data;\n }\n\n get poller(): IPollTimer {\n return this.pollTimer;\n }\n}\n","import type { ILogger } from \"document-model\";\nimport type { IOperationIndex } from \"../../cache/operation-index-types.js\";\nimport type { IQueue } from \"../../queue/interfaces.js\";\nimport type { ISyncCursorStorage } from \"../../storage/interfaces.js\";\nimport type { IChannel, IChannelFactory } from \"../interfaces.js\";\nimport type { ChannelConfig, JwtHandler, RemoteFilter } from \"../types.js\";\nimport { GqlRequestChannel, type GqlChannelConfig } from \"./gql-req-channel.js\";\nimport { IntervalPollTimer } from \"./interval-poll-timer.js\";\n\n/**\n * Factory for creating GqlRequestChannel instances.\n *\n * Extracts GraphQL-specific configuration from ChannelConfig.parameters and\n * instantiates GqlRequestChannel instances for network-based synchronization.\n *\n * The optional jwtHandler enables dynamic JWT token generation per-request,\n * which is useful for short-lived tokens with audience-specific claims.\n */\nexport class GqlRequestChannelFactory implements IChannelFactory {\n private readonly logger: ILogger;\n private readonly jwtHandler?: JwtHandler;\n private readonly queue: IQueue;\n\n constructor(\n logger: ILogger,\n jwtHandler: JwtHandler | undefined,\n queue: IQueue,\n ) {\n this.logger = logger;\n this.jwtHandler = jwtHandler;\n this.queue = queue;\n }\n\n /**\n * Creates a new GqlRequestChannel instance with the given configuration.\n * See GqlChannelConfig for the expected parameters.\n *\n * @param config - Channel configuration including type and parameters\n * @param cursorStorage - Storage for persisting synchronization cursors\n * @param operationIndex - Operation index for querying timestamps\n * @returns A new GqlRequestChannel instance\n */\n instance(\n remoteId: string,\n remoteName: string,\n config: ChannelConfig,\n cursorStorage: ISyncCursorStorage,\n collectionId: string,\n filter: RemoteFilter,\n operationIndex: IOperationIndex,\n ): IChannel {\n // Extract and validate required parameters\n const url = config.parameters.url;\n if (typeof url !== \"string\" || !url) {\n throw new Error(\n 'GqlRequestChannelFactory requires \"url\" parameter in config.parameters',\n );\n }\n\n // Extract optional parameters with validation\n const gqlConfig: GqlChannelConfig = {\n url,\n collectionId,\n filter,\n jwtHandler: this.jwtHandler,\n retryBaseDelayMs: 1000,\n retryMaxDelayMs: 300000,\n };\n\n let pollIntervalMs = 2000;\n if (config.parameters.pollIntervalMs !== undefined) {\n if (typeof config.parameters.pollIntervalMs !== \"number\") {\n throw new Error('\"pollIntervalMs\" parameter must be a number');\n }\n pollIntervalMs = config.parameters.pollIntervalMs;\n }\n\n let retryBaseDelayMs: number | undefined;\n if (config.parameters.retryBaseDelayMs !== undefined) {\n if (typeof config.parameters.retryBaseDelayMs !== \"number\") {\n throw new Error('\"retryBaseDelayMs\" parameter must be a number');\n }\n retryBaseDelayMs = config.parameters.retryBaseDelayMs;\n }\n\n let retryMaxDelayMs: number | undefined;\n if (config.parameters.retryMaxDelayMs !== undefined) {\n if (typeof config.parameters.retryMaxDelayMs !== \"number\") {\n throw new Error('\"retryMaxDelayMs\" parameter must be a number');\n }\n retryMaxDelayMs = config.parameters.retryMaxDelayMs;\n }\n\n if (config.parameters.fetchFn !== undefined) {\n if (typeof config.parameters.fetchFn !== \"function\") {\n throw new Error('\"fetchFn\" parameter must be a function');\n }\n gqlConfig.fetchFn = config.parameters.fetchFn as typeof fetch;\n }\n\n if (retryBaseDelayMs !== undefined) {\n gqlConfig.retryBaseDelayMs = retryBaseDelayMs;\n }\n if (retryMaxDelayMs !== undefined) {\n gqlConfig.retryMaxDelayMs = retryMaxDelayMs;\n }\n\n let maxQueueDepth: number | undefined;\n if (config.parameters.maxQueueDepth !== undefined) {\n if (typeof config.parameters.maxQueueDepth !== \"number\") {\n throw new Error('\"maxQueueDepth\" parameter must be a number');\n }\n maxQueueDepth = config.parameters.maxQueueDepth;\n }\n\n let backpressureCheckIntervalMs: number | undefined;\n if (config.parameters.backpressureCheckIntervalMs !== undefined) {\n if (typeof config.parameters.backpressureCheckIntervalMs !== \"number\") {\n throw new Error(\n '\"backpressureCheckIntervalMs\" parameter must be a number',\n );\n }\n backpressureCheckIntervalMs =\n config.parameters.backpressureCheckIntervalMs;\n }\n\n const pollTimer = new IntervalPollTimer(this.queue, {\n intervalMs: pollIntervalMs,\n ...(retryBaseDelayMs !== undefined && { retryBaseDelayMs }),\n ...(retryMaxDelayMs !== undefined && { retryMaxDelayMs }),\n ...(maxQueueDepth !== undefined && { maxQueueDepth }),\n ...(backpressureCheckIntervalMs !== undefined && {\n backpressureCheckIntervalMs,\n }),\n });\n\n return new GqlRequestChannel(\n this.logger,\n remoteId,\n remoteName,\n cursorStorage,\n gqlConfig,\n operationIndex,\n pollTimer,\n );\n }\n}\n","import type { ILogger } from \"document-model\";\nimport type { ISyncCursorStorage } from \"../../storage/interfaces.js\";\nimport type { ConnectionStateChangeCallback, IChannel } from \"../interfaces.js\";\nimport { Mailbox } from \"../mailbox.js\";\nimport type { ConnectionState, ConnectionStateSnapshot } from \"../types.js\";\nimport { getLatestAppliedOrdinal } from \"./utils.js\";\n\n/**\n * This class is used server-side to accumulate inbox + outbox operations.\n *\n * In general, the resolvers are responsible for updating mailboxes.\n */\nexport class GqlResponseChannel implements IChannel {\n readonly inbox: Mailbox;\n readonly outbox: Mailbox;\n readonly deadLetter: Mailbox;\n\n private readonly channelId: string;\n private readonly remoteName: string;\n private readonly cursorStorage: ISyncCursorStorage;\n private isShutdown: boolean;\n private lastPersistedInboxOrdinal: number = 0;\n private lastPersistedOutboxOrdinal: number = 0;\n private connectionState: ConnectionState = \"connecting\";\n private readonly connectionStateCallbacks: Set<ConnectionStateChangeCallback> =\n new Set();\n\n constructor(\n private readonly logger: ILogger,\n channelId: string,\n remoteName: string,\n cursorStorage: ISyncCursorStorage,\n ) {\n this.channelId = channelId;\n this.remoteName = remoteName;\n this.cursorStorage = cursorStorage;\n this.isShutdown = false;\n\n this.inbox = new Mailbox();\n this.outbox = new Mailbox();\n this.deadLetter = new Mailbox();\n\n // Instead of listening to syncops directly for cursor updates, we listen\n // to the mailbox. This is for efficiency: many syncops may fire on a trim,\n // but only one onRemoved callback will be fired for the batch.\n this.outbox.onRemoved((syncOps) => {\n const maxOrdinal = getLatestAppliedOrdinal(syncOps);\n if (maxOrdinal > this.lastPersistedOutboxOrdinal) {\n this.lastPersistedOutboxOrdinal = maxOrdinal;\n this.cursorStorage\n .upsert({\n remoteName: this.remoteName,\n cursorType: \"outbox\",\n cursorOrdinal: maxOrdinal,\n lastSyncedAtUtcMs: Date.now(),\n })\n .catch((error) => {\n this.logger.error(\n \"Failed to update outbox cursor for @ChannelId! This means that future application runs may resend duplicate operations. This is recoverable (with deduplication protection), but not-optimal: @Error\",\n this.channelId,\n error,\n );\n });\n }\n });\n\n this.inbox.onRemoved((syncOps) => {\n const maxOrdinal = getLatestAppliedOrdinal(syncOps);\n if (maxOrdinal > this.lastPersistedInboxOrdinal) {\n this.lastPersistedInboxOrdinal = maxOrdinal;\n this.cursorStorage\n .upsert({\n remoteName: this.remoteName,\n cursorType: \"inbox\",\n cursorOrdinal: maxOrdinal,\n lastSyncedAtUtcMs: Date.now(),\n })\n .catch((error) => {\n this.logger.error(\n \"Failed to update inbox cursor for @ChannelId! This is unlikely to cause a problem, but not-optimal: @Error\",\n this.channelId,\n error,\n );\n });\n }\n });\n }\n\n shutdown(): Promise<void> {\n this.isShutdown = true;\n this.transitionConnectionState(\"disconnected\");\n return Promise.resolve();\n }\n\n getConnectionState(): ConnectionStateSnapshot {\n return {\n state: this.connectionState,\n failureCount: 0,\n lastSuccessUtcMs: 0,\n lastFailureUtcMs: 0,\n pushBlocked: false,\n pushFailureCount: 0,\n };\n }\n\n onConnectionStateChange(callback: ConnectionStateChangeCallback): () => void {\n this.connectionStateCallbacks.add(callback);\n return () => {\n this.connectionStateCallbacks.delete(callback);\n };\n }\n\n private transitionConnectionState(next: ConnectionState): void {\n if (this.connectionState === next) return;\n this.connectionState = next;\n const snapshot = this.getConnectionState();\n for (const callback of this.connectionStateCallbacks) {\n try {\n callback(snapshot);\n } catch (error) {\n this.logger.error(\n \"Connection state change callback error: @Error\",\n error,\n );\n }\n }\n }\n\n async init(): Promise<void> {\n // get cursors -- these are the last acknowledged ordinals for the inbox and outbox\n const cursors = await this.cursorStorage.list(this.remoteName);\n const inboxOrdinal =\n cursors.find((c) => c.cursorType === \"inbox\")?.cursorOrdinal ?? 0;\n const outboxOrdinal =\n cursors.find((c) => c.cursorType === \"outbox\")?.cursorOrdinal ?? 0;\n this.inbox.init(inboxOrdinal);\n this.outbox.init(outboxOrdinal);\n this.lastPersistedInboxOrdinal = inboxOrdinal;\n this.lastPersistedOutboxOrdinal = outboxOrdinal;\n this.transitionConnectionState(\"connected\");\n }\n}\n","import type { ILogger } from \"document-model\";\nimport type { ISyncCursorStorage } from \"../../storage/interfaces.js\";\nimport type { IChannel, IChannelFactory } from \"../interfaces.js\";\nimport type { ChannelConfig } from \"../types.js\";\nimport { GqlResponseChannel } from \"./gql-res-channel.js\";\n\n/**\n * Factory for creating GqlResponseChannel instances.\n */\nexport class GqlResponseChannelFactory implements IChannelFactory {\n private readonly logger: ILogger;\n\n constructor(logger: ILogger) {\n this.logger = logger;\n }\n\n instance(\n remoteId: string,\n remoteName: string,\n config: ChannelConfig,\n cursorStorage: ISyncCursorStorage,\n ): IChannel {\n return new GqlResponseChannel(\n this.logger,\n remoteId,\n remoteName,\n cursorStorage,\n );\n }\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\nimport type { RemoteCursor } from \"../../sync/types.js\";\nimport type { ISyncCursorStorage } from \"../interfaces.js\";\nimport type { Database, InsertableSyncCursor, SyncCursorRow } from \"./types.js\";\n\nfunction rowToRemoteCursor(row: SyncCursorRow): RemoteCursor {\n return {\n remoteName: row.remote_name,\n cursorType: row.cursor_type as \"inbox\" | \"outbox\",\n cursorOrdinal: Number(row.cursor_ordinal),\n lastSyncedAtUtcMs: row.last_synced_at_utc_ms\n ? new Date(row.last_synced_at_utc_ms).getTime()\n : undefined,\n };\n}\n\nfunction remoteCursorToRow(cursor: RemoteCursor): InsertableSyncCursor {\n return {\n remote_name: cursor.remoteName,\n cursor_type: cursor.cursorType,\n cursor_ordinal: BigInt(cursor.cursorOrdinal),\n last_synced_at_utc_ms: cursor.lastSyncedAtUtcMs\n ? new Date(cursor.lastSyncedAtUtcMs).toISOString()\n : null,\n };\n}\n\nexport class KyselySyncCursorStorage implements ISyncCursorStorage {\n constructor(private readonly db: Kysely<Database>) {}\n\n async list(\n remoteName: string,\n signal?: AbortSignal,\n ): Promise<RemoteCursor[]> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const rows = await this.db\n .selectFrom(\"sync_cursors\")\n .selectAll()\n .where(\"remote_name\", \"=\", remoteName)\n .execute();\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n return rows.map(rowToRemoteCursor);\n }\n\n async get(\n remoteName: string,\n cursorType: \"inbox\" | \"outbox\",\n signal?: AbortSignal,\n ): Promise<RemoteCursor> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const row = await this.db\n .selectFrom(\"sync_cursors\")\n .selectAll()\n .where(\"remote_name\", \"=\", remoteName)\n .where(\"cursor_type\", \"=\", cursorType)\n .executeTakeFirst();\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n if (!row) {\n return {\n remoteName,\n cursorType,\n cursorOrdinal: 0,\n };\n }\n\n return rowToRemoteCursor(row);\n }\n\n async upsert(cursor: RemoteCursor, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n await this.db.transaction().execute(async (trx) => {\n const insertable = remoteCursorToRow(cursor);\n\n await trx\n .insertInto(\"sync_cursors\")\n .values(insertable)\n .onConflict((oc) =>\n oc.columns([\"remote_name\", \"cursor_type\"]).doUpdateSet({\n ...insertable,\n updated_at: sql`NOW()`,\n }),\n )\n .execute();\n });\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n }\n\n async remove(remoteName: string, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n await this.db.transaction().execute(async (trx) => {\n await trx\n .deleteFrom(\"sync_cursors\")\n .where(\"remote_name\", \"=\", remoteName)\n .execute();\n });\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n }\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { Kysely } from \"kysely\";\nimport type { PagingOptions, PagedResults } from \"../../shared/types.js\";\nimport type { ChannelErrorSource } from \"../../sync/types.js\";\nimport type { DeadLetterRecord } from \"../interfaces.js\";\nimport type { ISyncDeadLetterStorage } from \"../interfaces.js\";\nimport type {\n Database,\n InsertableSyncDeadLetter,\n SyncDeadLetterRow,\n} from \"./types.js\";\n\nfunction rowToDeadLetterRecord(row: SyncDeadLetterRow): DeadLetterRecord {\n return {\n id: row.id,\n jobId: row.job_id,\n jobDependencies: row.job_dependencies as string[],\n remoteName: row.remote_name,\n documentId: row.document_id,\n scopes: row.scopes as string[],\n branch: row.branch,\n operations: row.operations as OperationWithContext[],\n errorSource: row.error_source as ChannelErrorSource,\n errorMessage: row.error_message,\n };\n}\n\nfunction deadLetterRecordToRow(\n record: DeadLetterRecord,\n): InsertableSyncDeadLetter {\n return {\n id: record.id,\n job_id: record.jobId,\n job_dependencies: JSON.stringify(record.jobDependencies),\n remote_name: record.remoteName,\n document_id: record.documentId,\n scopes: JSON.stringify(record.scopes),\n branch: record.branch,\n operations: JSON.stringify(record.operations),\n error_source: record.errorSource,\n error_message: record.errorMessage,\n };\n}\n\n/**\n * PGlite/Kysely-backed implementation of {@link ISyncDeadLetterStorage}.\n */\nexport class KyselySyncDeadLetterStorage implements ISyncDeadLetterStorage {\n constructor(private readonly db: Kysely<Database>) {}\n\n async list(\n remoteName: string,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<DeadLetterRecord>> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const startIndex = paging?.cursor ? parseInt(paging.cursor) : 0;\n const limit = paging?.limit || 100;\n\n const rows = await this.db\n .selectFrom(\"sync_dead_letters\")\n .selectAll()\n .where(\"remote_name\", \"=\", remoteName)\n .orderBy(\"ordinal\", \"desc\")\n .offset(startIndex)\n .limit(limit + 1)\n .execute();\n\n let hasMore = false;\n let items = rows;\n if (paging?.limit && rows.length > limit) {\n hasMore = true;\n items = rows.slice(0, limit);\n }\n\n const nextCursor = hasMore ? String(startIndex + limit) : undefined;\n const cursor = paging?.cursor || \"0\";\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n return {\n results: items.map(rowToDeadLetterRecord),\n options: { cursor, limit },\n nextCursor,\n };\n }\n\n async add(deadLetter: DeadLetterRecord, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n await this.db.transaction().execute(async (trx) => {\n const insertable = deadLetterRecordToRow(deadLetter);\n\n await trx\n .insertInto(\"sync_dead_letters\")\n .values(insertable)\n .onConflict((oc) => oc.column(\"id\").doNothing())\n .execute();\n });\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n }\n\n async remove(id: string, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n await this.db.transaction().execute(async (trx) => {\n await trx.deleteFrom(\"sync_dead_letters\").where(\"id\", \"=\", id).execute();\n });\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n }\n\n async removeByRemote(\n remoteName: string,\n signal?: AbortSignal,\n ): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n await this.db.transaction().execute(async (trx) => {\n await trx\n .deleteFrom(\"sync_dead_letters\")\n .where(\"remote_name\", \"=\", remoteName)\n .execute();\n });\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n }\n\n async listQuarantinedDocumentIds(signal?: AbortSignal): Promise<string[]> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const rows = await this.db\n .selectFrom(\"sync_dead_letters\")\n .select(\"document_id\")\n .distinct()\n .execute();\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n return rows.map((row) => row.document_id);\n }\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\nimport type { RemoteRecord } from \"../../sync/types.js\";\nimport type { ISyncRemoteStorage } from \"../interfaces.js\";\nimport type { Database, InsertableSyncRemote, SyncRemoteRow } from \"./types.js\";\n\nfunction rowToRemoteRecord(row: SyncRemoteRow): RemoteRecord {\n return {\n id: row.channel_id,\n name: row.name,\n collectionId: row.collection_id,\n channelConfig: {\n type: row.channel_type,\n parameters: (row.channel_parameters ?? {}) as Record<string, unknown>,\n },\n filter: {\n documentId: (row.filter_document_ids ?? []) as string[],\n scope: (row.filter_scopes ?? []) as string[],\n branch: row.filter_branch,\n },\n options: { sinceTimestampUtcMs: \"0\" },\n status: {\n push: {\n state: row.push_state as \"idle\" | \"running\" | \"error\",\n lastSuccessUtcMs: row.push_last_success_utc_ms\n ? new Date(row.push_last_success_utc_ms).getTime()\n : undefined,\n lastFailureUtcMs: row.push_last_failure_utc_ms\n ? new Date(row.push_last_failure_utc_ms).getTime()\n : undefined,\n failureCount: row.push_failure_count,\n },\n pull: {\n state: row.pull_state as \"idle\" | \"running\" | \"error\",\n lastSuccessUtcMs: row.pull_last_success_utc_ms\n ? new Date(row.pull_last_success_utc_ms).getTime()\n : undefined,\n lastFailureUtcMs: row.pull_last_failure_utc_ms\n ? new Date(row.pull_last_failure_utc_ms).getTime()\n : undefined,\n failureCount: row.pull_failure_count,\n },\n },\n };\n}\n\nfunction remoteRecordToRow(remote: RemoteRecord): InsertableSyncRemote {\n return {\n name: remote.name,\n collection_id: remote.collectionId,\n channel_type: remote.channelConfig.type,\n channel_id: remote.id,\n remote_name: remote.name,\n channel_parameters: remote.channelConfig.parameters,\n filter_document_ids:\n remote.filter.documentId.length > 0 ? remote.filter.documentId : null,\n filter_scopes: remote.filter.scope.length > 0 ? remote.filter.scope : null,\n filter_branch: remote.filter.branch,\n push_state: remote.status.push.state,\n push_last_success_utc_ms: remote.status.push.lastSuccessUtcMs\n ? new Date(remote.status.push.lastSuccessUtcMs).toISOString()\n : null,\n push_last_failure_utc_ms: remote.status.push.lastFailureUtcMs\n ? new Date(remote.status.push.lastFailureUtcMs).toISOString()\n : null,\n push_failure_count: remote.status.push.failureCount,\n pull_state: remote.status.pull.state,\n pull_last_success_utc_ms: remote.status.pull.lastSuccessUtcMs\n ? new Date(remote.status.pull.lastSuccessUtcMs).toISOString()\n : null,\n pull_last_failure_utc_ms: remote.status.pull.lastFailureUtcMs\n ? new Date(remote.status.pull.lastFailureUtcMs).toISOString()\n : null,\n pull_failure_count: remote.status.pull.failureCount,\n };\n}\n\nexport class KyselySyncRemoteStorage implements ISyncRemoteStorage {\n constructor(private readonly db: Kysely<Database>) {}\n\n async list(signal?: AbortSignal): Promise<RemoteRecord[]> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const rows = await this.db.selectFrom(\"sync_remotes\").selectAll().execute();\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n return rows.map(rowToRemoteRecord);\n }\n\n async get(name: string, signal?: AbortSignal): Promise<RemoteRecord> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const row = await this.db\n .selectFrom(\"sync_remotes\")\n .selectAll()\n .where(\"name\", \"=\", name)\n .executeTakeFirst();\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n if (!row) {\n throw new Error(`Remote not found: ${name}`);\n }\n\n return rowToRemoteRecord(row);\n }\n\n async upsert(remote: RemoteRecord, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n await this.db.transaction().execute(async (trx) => {\n const insertable = remoteRecordToRow(remote);\n\n await trx\n .insertInto(\"sync_remotes\")\n .values(insertable)\n .onConflict((oc) =>\n oc.column(\"name\").doUpdateSet({\n ...insertable,\n updated_at: sql`NOW()`,\n }),\n )\n .execute();\n });\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n }\n\n async remove(name: string, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n await this.db.transaction().execute(async (trx) => {\n await trx.deleteFrom(\"sync_remotes\").where(\"name\", \"=\", name).execute();\n });\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n }\n}\n","import type { ILogger } from \"document-model\";\nimport type { JobFailedEvent, JobWriteReadyEvent } from \"../events/types.js\";\nimport { mergeCollectionMemberships } from \"./utils.js\";\n\nexport type PreparedBatch = {\n /** Document ID -> Collection IDs that they are a part of */\n collectionMemberships: Record<string, string[]>;\n entries: Array<{\n event: JobWriteReadyEvent;\n jobDependencies: string[];\n }>;\n};\n\ntype PendingBatch = {\n expectedJobIds: Set<string>;\n arrivedJobIds: Set<string>;\n events: JobWriteReadyEvent[];\n};\n\nexport class BatchAggregator {\n private readonly logger: ILogger;\n private readonly onBatchReady: (batch: PreparedBatch) => Promise<void>;\n private queue: JobWriteReadyEvent[] = [];\n private processing: boolean = false;\n private readonly pendingBatches: Map<string, PendingBatch> = new Map();\n\n constructor(\n logger: ILogger,\n onBatchReady: (batch: PreparedBatch) => Promise<void>,\n ) {\n this.logger = logger;\n this.onBatchReady = onBatchReady;\n }\n\n async enqueueWriteReady(event: JobWriteReadyEvent): Promise<void> {\n this.queue.push(event);\n await this.processQueue();\n }\n\n async handleJobFailed(event: JobFailedEvent): Promise<void> {\n const batchId = event.job?.meta.batchId;\n if (!batchId) {\n return;\n }\n\n const pending = this.pendingBatches.get(batchId);\n if (!pending) {\n return;\n }\n\n this.pendingBatches.delete(batchId);\n if (pending.events.length > 0) {\n await this.onBatchReady(this.prepareBatch(pending.events));\n }\n }\n\n clear(): void {\n this.queue = [];\n this.pendingBatches.clear();\n }\n\n private async processQueue(): Promise<void> {\n if (this.processing) {\n return;\n }\n this.processing = true;\n\n try {\n while (this.queue.length > 0) {\n const event = this.queue.shift()!;\n try {\n await this.handleWriteReady(event);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n this.logger.error(\n \"Failed to process write-ready event (@jobId, @error)\",\n event.jobId,\n err.message,\n );\n }\n }\n } finally {\n this.processing = false;\n }\n }\n\n private async handleWriteReady(event: JobWriteReadyEvent): Promise<void> {\n const { batchId, batchJobIds } = event.jobMeta;\n\n if (batchJobIds.length <= 1) {\n await this.onBatchReady(this.prepareBatch([event]));\n return;\n }\n\n let pending = this.pendingBatches.get(batchId);\n if (!pending) {\n pending = {\n expectedJobIds: new Set(batchJobIds),\n arrivedJobIds: new Set(),\n events: [],\n };\n this.pendingBatches.set(batchId, pending);\n }\n\n pending.arrivedJobIds.add(event.jobId);\n pending.events.push(event);\n\n if (pending.arrivedJobIds.size >= pending.expectedJobIds.size) {\n this.pendingBatches.delete(batchId);\n await this.onBatchReady(this.prepareBatch(pending.events));\n }\n }\n\n private prepareBatch(events: JobWriteReadyEvent[]): PreparedBatch {\n const collectionMemberships = mergeCollectionMemberships(events);\n const isBatch = events.length > 1;\n const priorJobIds: string[] = [];\n const entries: PreparedBatch[\"entries\"] = [];\n\n for (const event of events) {\n entries.push({\n event,\n jobDependencies: isBatch ? [...priorJobIds] : [],\n });\n\n if (isBatch && event.jobId) {\n priorJobIds.push(event.jobId);\n }\n }\n\n return { collectionMemberships, entries };\n }\n}\n","import type { IEventBus } from \"../events/interfaces.js\";\nimport type { Unsubscribe } from \"../events/types.js\";\nimport {\n SyncEventTypes,\n type SyncFailedEvent,\n type SyncResult,\n type SyncSucceededEvent,\n} from \"./types.js\";\n\ntype SyncWaiter = {\n resolve: (value: SyncResult) => void;\n reject: (reason: Error) => void;\n signal?: AbortSignal;\n};\n\n/**\n * Provides a promise-based interface for waiting on sync completion.\n * Subscribes to sync events at construction and tracks completed sync results\n * to provide a fast path for jobs that have already synced.\n */\nexport class SyncAwaiter {\n private readonly completedResults = new Map<string, SyncResult>();\n private readonly pendingWaiters = new Map<string, SyncWaiter[]>();\n private readonly unsubscribers: Unsubscribe[] = [];\n private isShutdown = false;\n\n constructor(private readonly eventBus: IEventBus) {\n this.subscribeToEvents();\n }\n\n /**\n * Waits for sync operations for a job to complete.\n * Resolves when SYNC_SUCCEEDED is emitted.\n * Rejects when SYNC_FAILED is emitted.\n *\n * @param jobId - The job id to wait for\n * @param signal - Optional abort signal\n * @returns The sync result\n */\n waitForSync(jobId: string, signal?: AbortSignal): Promise<SyncResult> {\n if (signal?.aborted) {\n return Promise.reject(new Error(\"Operation aborted\"));\n }\n\n if (this.isShutdown) {\n return Promise.reject(new Error(\"SyncAwaiter is shutdown\"));\n }\n\n const completedResult = this.completedResults.get(jobId);\n if (completedResult) {\n return Promise.resolve(completedResult);\n }\n\n return new Promise<SyncResult>((resolve, reject) => {\n const waiter: SyncWaiter = { resolve, reject, signal };\n\n const existingWaiters = this.pendingWaiters.get(jobId) || [];\n existingWaiters.push(waiter);\n this.pendingWaiters.set(jobId, existingWaiters);\n\n if (signal) {\n const abortHandler = () => {\n const waiters = this.pendingWaiters.get(jobId);\n if (waiters) {\n const index = waiters.indexOf(waiter);\n if (index !== -1) {\n waiters.splice(index, 1);\n if (waiters.length === 0) {\n this.pendingWaiters.delete(jobId);\n }\n waiter.reject(new Error(\"Operation aborted\"));\n }\n }\n };\n\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n });\n }\n\n /**\n * Shuts down the sync awaiter. This will synchronously reject all pending waiters.\n */\n shutdown(): void {\n this.isShutdown = true;\n\n for (const unsubscribe of this.unsubscribers) {\n unsubscribe();\n }\n this.unsubscribers.length = 0;\n\n for (const [, waiters] of this.pendingWaiters) {\n for (const waiter of waiters) {\n waiter.reject(new Error(\"SyncAwaiter shutdown\"));\n }\n }\n this.pendingWaiters.clear();\n }\n\n private subscribeToEvents(): void {\n this.unsubscribers.push(\n this.eventBus.subscribe<SyncSucceededEvent>(\n SyncEventTypes.SYNC_SUCCEEDED,\n (_type, event) => {\n this.handleSyncSucceeded(event);\n },\n ),\n );\n\n this.unsubscribers.push(\n this.eventBus.subscribe<SyncFailedEvent>(\n SyncEventTypes.SYNC_FAILED,\n (_type, event) => {\n this.handleSyncFailed(event);\n },\n ),\n );\n }\n\n private handleSyncSucceeded(event: SyncSucceededEvent): void {\n const result: SyncResult = {\n jobId: event.jobId,\n status: \"succeeded\",\n syncOperationCount: event.syncOperationCount,\n successCount: event.syncOperationCount,\n failureCount: 0,\n errors: [],\n };\n\n this.completedResults.set(event.jobId, result);\n this.resolveWaiters(event.jobId, result);\n }\n\n private handleSyncFailed(event: SyncFailedEvent): void {\n const result: SyncResult = {\n jobId: event.jobId,\n status: \"failed\",\n syncOperationCount: event.successCount + event.failureCount,\n successCount: event.successCount,\n failureCount: event.failureCount,\n errors: event.errors,\n };\n\n this.completedResults.set(event.jobId, result);\n this.resolveWaiters(event.jobId, result);\n }\n\n private resolveWaiters(jobId: string, result: SyncResult): void {\n const waiters = this.pendingWaiters.get(jobId);\n if (!waiters || waiters.length === 0) {\n return;\n }\n\n this.pendingWaiters.delete(jobId);\n\n for (const waiter of waiters) {\n if (waiter.signal?.aborted) {\n waiter.reject(new Error(\"Operation aborted\"));\n } else {\n waiter.resolve(result);\n }\n }\n }\n}\n","import type { IChannel } from \"./interfaces.js\";\nimport type { SyncOperation } from \"./sync-operation.js\";\n\nexport enum SyncStatus {\n Synced = \"SYNCED\",\n Outgoing = \"OUTGOING\",\n Incoming = \"INCOMING\",\n OutgoingAndIncoming = \"OUTGOING_AND_INCOMING\",\n Error = \"ERROR\",\n}\n\nexport type SyncStatusChangeCallback = (\n documentId: string,\n status: SyncStatus,\n) => void;\n\nexport interface ISyncStatusTracker {\n getStatus(documentId: string): SyncStatus | undefined;\n onChange(callback: SyncStatusChangeCallback): () => void;\n trackRemote(remoteName: string, channel: IChannel): void;\n untrackRemote(remoteName: string): void;\n clear(): void;\n}\n\ntype DocumentCounts = {\n inboxCount: number;\n outboxCount: number;\n errorCount: number;\n};\n\ntype MailboxType = \"inbox\" | \"outbox\" | \"deadLetter\";\n\nexport class SyncStatusTracker implements ISyncStatusTracker {\n private readonly remotes: Map<string, Map<string, DocumentCounts>> =\n new Map();\n private readonly seen: Set<string> = new Set();\n private readonly callbacks: Set<SyncStatusChangeCallback> = new Set();\n\n getStatus(documentId: string): SyncStatus | undefined {\n if (!this.seen.has(documentId)) {\n return undefined;\n }\n\n let totalInbox = 0;\n let totalOutbox = 0;\n let totalErrors = 0;\n\n for (const documents of this.remotes.values()) {\n const counts = documents.get(documentId);\n if (counts) {\n totalInbox += counts.inboxCount;\n totalOutbox += counts.outboxCount;\n totalErrors += counts.errorCount;\n }\n }\n\n return deriveStatus(totalInbox, totalOutbox, totalErrors);\n }\n\n onChange(callback: SyncStatusChangeCallback): () => void {\n this.callbacks.add(callback);\n return () => {\n this.callbacks.delete(callback);\n };\n }\n\n trackRemote(remoteName: string, channel: IChannel): void {\n this.remotes.set(remoteName, new Map());\n\n channel.inbox.onAdded((syncOps) =>\n this.handleAdded(remoteName, \"inbox\", syncOps),\n );\n channel.inbox.onRemoved((syncOps) =>\n this.handleRemoved(remoteName, \"inbox\", syncOps),\n );\n channel.outbox.onAdded((syncOps) =>\n this.handleAdded(remoteName, \"outbox\", syncOps),\n );\n channel.outbox.onRemoved((syncOps) =>\n this.handleRemoved(remoteName, \"outbox\", syncOps),\n );\n channel.deadLetter.onAdded((syncOps) =>\n this.handleAdded(remoteName, \"deadLetter\", syncOps),\n );\n }\n\n untrackRemote(remoteName: string): void {\n const documents = this.remotes.get(remoteName);\n if (!documents) {\n return;\n }\n\n const affectedDocumentIds = [...documents.keys()];\n this.remotes.delete(remoteName);\n\n for (const documentId of affectedDocumentIds) {\n this.notifyChange(documentId);\n }\n }\n\n clear(): void {\n this.remotes.clear();\n this.seen.clear();\n this.callbacks.clear();\n }\n\n private handleAdded(\n remoteName: string,\n mailboxType: MailboxType,\n syncOps: SyncOperation[],\n ): void {\n const changedDocuments = new Set<string>();\n\n for (const syncOp of syncOps) {\n if (mailboxType === \"inbox\" && !syncOp.remoteName) {\n continue;\n }\n\n const counts = this.getOrCreateCounts(remoteName, syncOp.documentId);\n this.seen.add(syncOp.documentId);\n\n if (mailboxType === \"inbox\") {\n counts.inboxCount++;\n } else if (mailboxType === \"outbox\") {\n counts.outboxCount++;\n } else {\n counts.errorCount++;\n }\n\n changedDocuments.add(syncOp.documentId);\n }\n\n for (const documentId of changedDocuments) {\n this.notifyChange(documentId);\n }\n }\n\n private handleRemoved(\n remoteName: string,\n mailboxType: MailboxType,\n syncOps: SyncOperation[],\n ): void {\n const changedDocuments = new Set<string>();\n\n for (const syncOp of syncOps) {\n const counts = this.getOrCreateCounts(remoteName, syncOp.documentId);\n\n if (mailboxType === \"inbox\") {\n counts.inboxCount = Math.max(0, counts.inboxCount - 1);\n } else if (mailboxType === \"outbox\") {\n counts.outboxCount = Math.max(0, counts.outboxCount - 1);\n }\n\n changedDocuments.add(syncOp.documentId);\n }\n\n for (const documentId of changedDocuments) {\n this.notifyChange(documentId);\n }\n }\n\n private getOrCreateCounts(\n remoteName: string,\n documentId: string,\n ): DocumentCounts {\n let documents = this.remotes.get(remoteName);\n if (!documents) {\n documents = new Map();\n this.remotes.set(remoteName, documents);\n }\n\n let counts = documents.get(documentId);\n if (!counts) {\n counts = { inboxCount: 0, outboxCount: 0, errorCount: 0 };\n documents.set(documentId, counts);\n }\n\n return counts;\n }\n\n private notifyChange(documentId: string): void {\n const status = this.getStatus(documentId);\n if (status === undefined) {\n return;\n }\n\n for (const callback of [...this.callbacks]) {\n callback(documentId, status);\n }\n }\n}\n\nfunction deriveStatus(\n inboxCount: number,\n outboxCount: number,\n errorCount: number,\n): SyncStatus {\n if (errorCount > 0) {\n return SyncStatus.Error;\n }\n if (inboxCount > 0 && outboxCount > 0) {\n return SyncStatus.OutgoingAndIncoming;\n }\n if (inboxCount > 0) {\n return SyncStatus.Incoming;\n }\n if (outboxCount > 0) {\n return SyncStatus.Outgoing;\n }\n return SyncStatus.Synced;\n}\n","import type {\n Operation,\n OperationWithContext,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport type { IOperationIndex } from \"../cache/operation-index-types.js\";\nimport type {\n BatchLoadRequest,\n BatchLoadResult,\n IReactor,\n} from \"../core/types.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport {\n ReactorEventTypes,\n type JobFailedEvent,\n type JobWriteReadyEvent,\n} from \"../events/types.js\";\nimport { JobAwaiter } from \"../shared/awaiter.js\";\nimport { JobStatus, type ShutdownStatus } from \"../shared/types.js\";\nimport type {\n DeadLetterRecord,\n ISyncCursorStorage,\n ISyncDeadLetterStorage,\n ISyncRemoteStorage,\n} from \"../storage/interfaces.js\";\nimport { BatchAggregator, type PreparedBatch } from \"./batch-aggregator.js\";\nimport { ChannelError } from \"./errors.js\";\nimport type { IChannelFactory, ISyncManager, Remote } from \"./interfaces.js\";\nimport { SyncAwaiter } from \"./sync-awaiter.js\";\nimport { SyncOperation } from \"./sync-operation.js\";\nimport {\n SyncStatusTracker,\n type SyncStatus,\n type SyncStatusChangeCallback,\n} from \"./sync-status-tracker.js\";\nimport type {\n ChannelConfig,\n ConnectionStateChangedEvent,\n DeadLetterAddedEvent,\n RemoteFilter,\n RemoteOptions,\n RemoteRecord,\n RemoteStatus,\n SyncResult,\n} from \"./types.js\";\nimport { ChannelErrorSource, SyncEventTypes } from \"./types.js\";\nimport {\n batchOperationsByDocument,\n createIdleHealth,\n filterOperations,\n toOperationWithContext,\n trimMailboxFromBatch,\n} from \"./utils.js\";\n\nexport class SyncManager implements ISyncManager {\n private readonly logger: ILogger;\n private readonly remoteStorage: ISyncRemoteStorage;\n private readonly cursorStorage: ISyncCursorStorage;\n private readonly deadLetterStorage: ISyncDeadLetterStorage;\n private readonly channelFactory: IChannelFactory;\n private readonly operationIndex: IOperationIndex;\n private readonly reactor: IReactor;\n private readonly eventBus: IEventBus;\n private readonly remotes: Map<string, Remote>;\n private readonly awaiter: JobAwaiter;\n private readonly syncAwaiter: SyncAwaiter;\n private readonly abortController = new AbortController();\n private isShutdown: boolean;\n private eventUnsubscribe?: () => void;\n private failedEventUnsubscribe?: () => void;\n private readonly batchAggregator: BatchAggregator;\n private readonly syncStatusTracker: SyncStatusTracker;\n private readonly maxDeadLettersPerRemote: number;\n private readonly connectionStateUnsubscribes: Map<string, () => void> =\n new Map();\n private readonly quarantinedDocumentIds = new Set<string>();\n\n constructor(\n logger: ILogger,\n remoteStorage: ISyncRemoteStorage,\n cursorStorage: ISyncCursorStorage,\n deadLetterStorage: ISyncDeadLetterStorage,\n channelFactory: IChannelFactory,\n operationIndex: IOperationIndex,\n reactor: IReactor,\n eventBus: IEventBus,\n maxDeadLettersPerRemote: number = 100,\n ) {\n this.logger = logger;\n this.remoteStorage = remoteStorage;\n this.cursorStorage = cursorStorage;\n this.deadLetterStorage = deadLetterStorage;\n this.channelFactory = channelFactory;\n this.operationIndex = operationIndex;\n this.reactor = reactor;\n this.eventBus = eventBus;\n this.maxDeadLettersPerRemote = maxDeadLettersPerRemote;\n this.remotes = new Map();\n this.awaiter = new JobAwaiter(eventBus, (jobId, signal) =>\n reactor.getJobStatus(jobId, signal),\n );\n this.syncAwaiter = new SyncAwaiter(eventBus);\n this.isShutdown = false;\n this.batchAggregator = new BatchAggregator(logger, (batch) =>\n this.processCompleteBatch(batch),\n );\n this.syncStatusTracker = new SyncStatusTracker();\n }\n\n async startup(): Promise<void> {\n if (this.isShutdown) {\n throw new Error(\"SyncManager is already shutdown and cannot be started\");\n }\n\n try {\n const quarantinedIds =\n await this.deadLetterStorage.listQuarantinedDocumentIds();\n for (const id of quarantinedIds) {\n this.quarantinedDocumentIds.add(id);\n }\n } catch (error) {\n this.logger.error(\n \"Failed to load quarantined document IDs (@error)\",\n error instanceof Error ? error.message : String(error),\n );\n }\n\n const remoteRecords = await this.remoteStorage.list();\n\n for (const record of remoteRecords) {\n const channel = this.channelFactory.instance(\n record.id,\n record.name,\n record.channelConfig,\n this.cursorStorage,\n record.collectionId,\n record.filter,\n this.operationIndex,\n );\n\n const remote: Remote = {\n id: record.id,\n name: record.name,\n collectionId: record.collectionId,\n filter: record.filter,\n options: record.options,\n channel,\n };\n\n this.remotes.set(record.name, remote);\n await this.loadDeadLetters(remote);\n this.wireChannelCallbacks(remote);\n\n try {\n await channel.init();\n } catch (error) {\n this.logger.error(\n \"Error initializing channel for remote (@name, @error)\",\n record.name,\n error instanceof Error ? error.message : String(error),\n );\n this.remotes.delete(record.name);\n continue;\n }\n\n // backfill channels\n const outboxAckOrdinal = remote.channel.outbox.ackOrdinal;\n if (outboxAckOrdinal > 0) {\n await this.updateOutbox(remote, outboxAckOrdinal);\n }\n }\n\n this.eventUnsubscribe = this.eventBus.subscribe<JobWriteReadyEvent>(\n ReactorEventTypes.JOB_WRITE_READY,\n async (_type, event) => this.batchAggregator.enqueueWriteReady(event),\n );\n\n this.failedEventUnsubscribe = this.eventBus.subscribe<JobFailedEvent>(\n ReactorEventTypes.JOB_FAILED,\n async (_type, event) => this.batchAggregator.handleJobFailed(event),\n );\n }\n\n shutdown(): ShutdownStatus {\n this.isShutdown = true;\n this.abortController.abort();\n this.batchAggregator.clear();\n\n if (this.eventUnsubscribe) {\n this.eventUnsubscribe();\n this.eventUnsubscribe = undefined;\n }\n\n if (this.failedEventUnsubscribe) {\n this.failedEventUnsubscribe();\n this.failedEventUnsubscribe = undefined;\n }\n\n this.awaiter.shutdown();\n this.syncAwaiter.shutdown();\n this.syncStatusTracker.clear();\n\n for (const unsub of this.connectionStateUnsubscribes.values()) {\n unsub();\n }\n this.connectionStateUnsubscribes.clear();\n\n const promises: Promise<void>[] = [];\n for (const remote of this.remotes.values()) {\n promises.push(remote.channel.shutdown());\n }\n\n this.remotes.clear();\n\n return {\n isShutdown: true,\n completed: Promise.all(promises).then(() => undefined),\n };\n }\n\n getByName(name: string): Remote {\n const remote = this.remotes.get(name);\n if (!remote) {\n throw new Error(`Remote with name '${name}' does not exist`);\n }\n return remote;\n }\n\n getById(id: string): Remote {\n for (const remote of this.remotes.values()) {\n if (remote.id === id) {\n return remote;\n }\n }\n throw new Error(`Remote with id '${id}' does not exist`);\n }\n\n async add(\n name: string,\n collectionId: string,\n channelConfig: ChannelConfig,\n filter: RemoteFilter = { documentId: [], scope: [], branch: \"\" },\n options: RemoteOptions = { sinceTimestampUtcMs: \"0\" },\n id?: string,\n ): Promise<Remote> {\n if (this.isShutdown) {\n throw new Error(\"SyncManager is shutdown and cannot add remotes\");\n }\n\n if (this.remotes.has(name)) {\n throw new Error(`Remote with name '${name}' already exists`);\n }\n\n this.logger.debug(\n \"Adding remote (@name, @collectionId, @channelConfig, @filter, @options, @id)\",\n name,\n collectionId,\n channelConfig,\n filter,\n options,\n id,\n );\n\n const remoteId = id ?? crypto.randomUUID();\n\n const status: RemoteStatus = {\n push: createIdleHealth(),\n pull: createIdleHealth(),\n };\n\n const remoteRecord: RemoteRecord = {\n id: remoteId,\n name,\n collectionId,\n channelConfig,\n filter,\n options,\n status,\n };\n\n await this.remoteStorage.upsert(remoteRecord);\n\n const channel = this.channelFactory.instance(\n remoteId,\n name,\n channelConfig,\n this.cursorStorage,\n collectionId,\n filter,\n this.operationIndex,\n );\n\n const remote: Remote = {\n id: remoteId,\n name,\n collectionId,\n filter,\n options,\n channel,\n };\n\n this.remotes.set(name, remote);\n await this.loadDeadLetters(remote);\n this.wireChannelCallbacks(remote);\n\n try {\n await channel.init();\n } catch (error) {\n this.remotes.delete(name);\n await this.remoteStorage.remove(name);\n throw error;\n }\n\n // backfill\n await this.updateOutbox(remote, 0);\n\n return remote;\n }\n\n async remove(name: string): Promise<void> {\n const remote = this.remotes.get(name);\n if (!remote) {\n throw new Error(`Remote with name '${name}' does not exist`);\n }\n\n // shutdown the channel\n await remote.channel.shutdown();\n\n // delete the remote's data\n await this.remoteStorage.remove(name);\n await this.cursorStorage.remove(name);\n\n this.syncStatusTracker.untrackRemote(name);\n const unsub = this.connectionStateUnsubscribes.get(name);\n if (unsub) {\n unsub();\n this.connectionStateUnsubscribes.delete(name);\n }\n this.remotes.delete(name);\n }\n\n list(): Remote[] {\n return Array.from(this.remotes.values());\n }\n\n waitForSync(jobId: string, signal?: AbortSignal): Promise<SyncResult> {\n return this.syncAwaiter.waitForSync(jobId, signal);\n }\n\n getSyncStatus(documentId: string): SyncStatus | undefined {\n return this.syncStatusTracker.getStatus(documentId);\n }\n\n onSyncStatusChange(callback: SyncStatusChangeCallback): () => void {\n return this.syncStatusTracker.onChange(callback);\n }\n\n private wireChannelCallbacks(remote: Remote): void {\n remote.channel.inbox.onAdded((syncOps) =>\n this.handleInboxAdded(remote, syncOps),\n );\n\n this.syncStatusTracker.trackRemote(remote.name, remote.channel);\n\n const unsubscribe = remote.channel.onConnectionStateChange((snapshot) => {\n void this.eventBus\n .emit(SyncEventTypes.CONNECTION_STATE_CHANGED, {\n remoteName: remote.name,\n remoteId: remote.id,\n previous: snapshot.state,\n current: snapshot.state,\n snapshot,\n } satisfies ConnectionStateChangedEvent)\n .catch(() => {});\n });\n this.connectionStateUnsubscribes.set(remote.name, unsubscribe);\n\n remote.channel.deadLetter.onAdded((syncOps) => {\n for (const syncOp of syncOps) {\n this.logger.error(\n \"Dead letter (@remote, @documentId, @jobId, @error, @dependencies)\",\n remote.name,\n syncOp.documentId,\n syncOp.jobId,\n syncOp.error?.message ?? \"unknown\",\n syncOp.jobDependencies,\n );\n\n this.quarantinedDocumentIds.add(syncOp.documentId);\n\n const record: DeadLetterRecord = {\n id: syncOp.id,\n jobId: syncOp.jobId,\n jobDependencies: syncOp.jobDependencies,\n remoteName: syncOp.remoteName,\n documentId: syncOp.documentId,\n scopes: syncOp.scopes,\n branch: syncOp.branch,\n operations: syncOp.operations,\n errorSource: syncOp.error?.source ?? ChannelErrorSource.None,\n errorMessage: syncOp.error?.error.message ?? \"unknown\",\n };\n\n void this.deadLetterStorage.add(record).catch((err) => {\n this.logger.error(\n \"Failed to persist dead letter (@id, @error)\",\n record.id,\n err instanceof Error ? err.message : String(err),\n );\n });\n\n void this.eventBus\n .emit(SyncEventTypes.DEAD_LETTER_ADDED, {\n id: record.id,\n jobId: record.jobId,\n remoteName: record.remoteName,\n documentId: record.documentId,\n errorSource: record.errorSource,\n } satisfies DeadLetterAddedEvent)\n .catch(() => {});\n }\n\n // Evict oldest dead letters from mailbox if over capacity\n const items = remote.channel.deadLetter.items;\n if (items.length > this.maxDeadLettersPerRemote) {\n const excessCount = items.length - this.maxDeadLettersPerRemote;\n const toEvict = items.slice(0, excessCount);\n remote.channel.deadLetter.remove(...toEvict);\n }\n });\n }\n\n private async loadDeadLetters(remote: Remote): Promise<void> {\n let records: DeadLetterRecord[];\n try {\n const page = await this.deadLetterStorage.list(remote.name, {\n cursor: \"0\",\n limit: this.maxDeadLettersPerRemote,\n });\n records = page.results;\n } catch (error) {\n this.logger.error(\n \"Failed to load dead letters for remote (@name, @error)\",\n remote.name,\n error instanceof Error ? error.message : String(error),\n );\n return;\n }\n\n if (records.length === 0) {\n return;\n }\n\n // Records come in ordinal DESC order (newest first).\n // Reverse so the Map maintains chronological insertion order (oldest first),\n // which makes eviction (slice from the front) straightforward.\n records.reverse();\n\n const syncOps: SyncOperation[] = [];\n for (const record of records) {\n const syncOp = new SyncOperation(\n record.id,\n record.jobId,\n record.jobDependencies,\n record.remoteName,\n record.documentId,\n record.scopes,\n record.branch,\n record.operations,\n );\n syncOp.failed(\n new ChannelError(record.errorSource, new Error(record.errorMessage)),\n );\n syncOps.push(syncOp);\n }\n\n remote.channel.deadLetter.add(...syncOps);\n\n this.logger.debug(\n \"Loaded @count persisted dead letters for remote @name\",\n records.length,\n remote.name,\n );\n }\n\n private getRemotesForCollection(collectionId: string): Remote[] {\n return Array.from(this.remotes.values()).filter(\n (remote) => remote.collectionId === collectionId,\n );\n }\n\n private async processCompleteBatch(batch: PreparedBatch): Promise<void> {\n if (this.isShutdown) return;\n\n // get the unique set of collection ids\n const collectionIds = [\n ...new Set(\n Object.values(batch.collectionMemberships).flatMap(\n (collections) => collections,\n ),\n ),\n ];\n\n // get the unique set of affected remotes\n const affectedRemotes: Remote[] = [];\n for (const collectionId of collectionIds) {\n const remotes = this.getRemotesForCollection(collectionId);\n for (const remote of remotes) {\n if (!affectedRemotes.includes(remote)) {\n affectedRemotes.push(remote);\n }\n }\n }\n\n // ack matching inbox items\n for (const remote of affectedRemotes) {\n trimMailboxFromBatch(remote.channel.inbox, batch);\n }\n\n // finally, work through the affected remotes and backfill based on the last operation in the outbox\n for (const remote of affectedRemotes) {\n await this.updateOutbox(remote, remote.channel.outbox.latestOrdinal);\n }\n }\n\n private handleInboxAdded(remote: Remote, syncOps: SyncOperation[]): void {\n if (this.isShutdown) {\n return;\n }\n\n const eligible = syncOps.filter(\n (op) => !this.quarantinedDocumentIds.has(op.documentId),\n );\n if (eligible.length === 0) return;\n\n const keyed: SyncOperation[] = [];\n const nonKeyed: SyncOperation[] = [];\n\n for (const syncOp of eligible) {\n if (syncOp.jobId) {\n keyed.push(syncOp);\n } else {\n nonKeyed.push(syncOp);\n }\n }\n\n for (const syncOp of nonKeyed) {\n void this.applyInboxJob(remote, syncOp);\n }\n\n if (keyed.length > 0) {\n void this.applyInboxBatch(keyed.map((syncOp) => ({ remote, syncOp })));\n }\n }\n\n private async applyInboxJob(\n remote: Remote,\n syncOp: SyncOperation,\n ): Promise<void> {\n const operations: Operation[] = syncOp.operations.map((op) => op.operation);\n\n let jobInfo;\n try {\n jobInfo = await this.reactor.load(\n syncOp.documentId,\n syncOp.branch,\n operations,\n this.abortController.signal,\n { sourceRemote: remote.name },\n );\n } catch (error) {\n if (this.isShutdown) return;\n const err = error instanceof Error ? error : new Error(String(error));\n this.logger.error(\n \"Failed to load operations from inbox (@remote, @documentId, @error)\",\n remote.name,\n syncOp.documentId,\n err.message,\n );\n const channelError = new ChannelError(ChannelErrorSource.Inbox, err);\n syncOp.failed(channelError);\n remote.channel.deadLetter.add(syncOp);\n remote.channel.inbox.remove(syncOp);\n return;\n }\n\n let completedJobInfo;\n try {\n completedJobInfo = await this.awaiter.waitForJob(\n jobInfo.id,\n this.abortController.signal,\n );\n } catch (error) {\n if (this.isShutdown) return;\n const err = error instanceof Error ? error : new Error(String(error));\n this.logger.error(\n \"Failed to wait for job completion (@remote, @documentId, @jobId, @error)\",\n remote.name,\n syncOp.documentId,\n jobInfo.id,\n err.message,\n );\n const channelError = new ChannelError(ChannelErrorSource.Inbox, err);\n syncOp.failed(channelError);\n remote.channel.deadLetter.add(syncOp);\n remote.channel.inbox.remove(syncOp);\n return;\n }\n\n if (this.isShutdown) return;\n\n if (completedJobInfo.status === JobStatus.FAILED) {\n const errorMessage = completedJobInfo.error?.message || \"Unknown error\";\n this.logger.error(\n \"Failed to apply operations from inbox (@remote, @documentId, @jobId, @error)\",\n remote.name,\n syncOp.documentId,\n completedJobInfo.id,\n errorMessage,\n );\n const error = new ChannelError(\n ChannelErrorSource.Inbox,\n new Error(`Failed to apply operations: ${errorMessage}`),\n );\n syncOp.failed(error);\n remote.channel.deadLetter.add(syncOp);\n } else {\n syncOp.executed();\n }\n\n remote.channel.inbox.remove(syncOp);\n }\n\n private async applyInboxBatch(\n items: Array<{ remote: Remote; syncOp: SyncOperation }>,\n ): Promise<void> {\n const sourceRemote = items[0].remote.name;\n\n const jobs = items.map(({ syncOp }) => ({\n key: syncOp.jobId,\n documentId: syncOp.documentId,\n scope: syncOp.scopes[0],\n branch: syncOp.branch,\n operations: syncOp.operations.map((op) => op.operation),\n dependsOn: syncOp.jobDependencies.filter(Boolean),\n }));\n\n const request: BatchLoadRequest = { jobs };\n\n let result: BatchLoadResult;\n try {\n result = await this.reactor.loadBatch(\n request,\n this.abortController.signal,\n { sourceRemote },\n );\n } catch (error) {\n if (this.isShutdown) return;\n for (const { remote, syncOp } of items) {\n const err = error instanceof Error ? error : new Error(String(error));\n syncOp.failed(new ChannelError(ChannelErrorSource.Inbox, err));\n remote.channel.deadLetter.add(syncOp);\n remote.channel.inbox.remove(syncOp);\n }\n return;\n }\n\n if (this.isShutdown) return;\n\n for (const { remote, syncOp } of items) {\n if (!(syncOp.jobId in result.jobs)) {\n this.logger.error(\n \"Job key missing from batch load result (@remote, @documentId, @jobId)\",\n remote.name,\n syncOp.documentId,\n syncOp.jobId,\n );\n const error = new ChannelError(\n ChannelErrorSource.Inbox,\n new Error(`Job key '${syncOp.jobId}' missing from batch load result`),\n );\n syncOp.failed(error);\n remote.channel.deadLetter.add(syncOp);\n remote.channel.inbox.remove(syncOp);\n continue;\n }\n const jobInfo = result.jobs[syncOp.jobId];\n\n let completedJobInfo;\n try {\n completedJobInfo = await this.awaiter.waitForJob(\n jobInfo.id,\n this.abortController.signal,\n );\n } catch (error) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- isShutdown may change during await\n if (this.isShutdown) continue;\n const err = error instanceof Error ? error : new Error(String(error));\n syncOp.failed(new ChannelError(ChannelErrorSource.Inbox, err));\n remote.channel.deadLetter.add(syncOp);\n remote.channel.inbox.remove(syncOp);\n continue;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- isShutdown may change during await\n if (this.isShutdown) return;\n\n if (completedJobInfo.status === JobStatus.FAILED) {\n const errorMessage = completedJobInfo.error?.message || \"Unknown error\";\n const channelError = new ChannelError(\n ChannelErrorSource.Inbox,\n new Error(`Failed to apply operations: ${errorMessage}`),\n );\n syncOp.failed(channelError);\n remote.channel.deadLetter.add(syncOp);\n } else {\n syncOp.executed();\n }\n\n remote.channel.inbox.remove(syncOp);\n }\n }\n\n private async updateOutbox(\n remote: Remote,\n ackOrdinal: number,\n ): Promise<void> {\n let maxOrdinal = ackOrdinal;\n const lastJobByDoc = new Map<string, string>();\n const sinceTimestamp = remote.options.sinceTimestampUtcMs;\n\n let page = await this.operationIndex.find(\n remote.collectionId,\n ackOrdinal,\n { excludeSourceRemote: remote.name },\n undefined,\n this.abortController.signal,\n );\n\n let hasMore: boolean;\n do {\n for (const entry of page.results) {\n maxOrdinal = Math.max(maxOrdinal, entry.ordinal ?? 0);\n }\n\n let operations = page.results.map((entry) =>\n toOperationWithContext(entry),\n );\n\n if (sinceTimestamp && sinceTimestamp !== \"0\") {\n operations = operations.filter(\n (op) => op.operation.timestampUtcMs >= sinceTimestamp,\n );\n }\n operations = filterOperations(operations, remote.filter);\n operations = operations.filter(\n (op) => !this.quarantinedDocumentIds.has(op.context.documentId),\n );\n\n if (operations.length > 0) {\n operations.sort((a, b) => {\n if (a.context.documentId !== b.context.documentId) {\n return a.context.documentId < b.context.documentId ? -1 : 1;\n }\n if (a.context.scope !== b.context.scope) {\n return a.context.scope < b.context.scope ? -1 : 1;\n }\n return a.context.ordinal - b.context.ordinal;\n });\n\n const batches = batchOperationsByDocument(operations);\n\n const syncOps: SyncOperation[] = [];\n for (const batch of batches) {\n const jobId = crypto.randomUUID();\n const prevJobId = lastJobByDoc.get(batch.documentId);\n const syncOp = new SyncOperation(\n crypto.randomUUID(),\n jobId,\n prevJobId ? [prevJobId] : [],\n remote.name,\n batch.documentId,\n [batch.scope],\n batch.branch,\n batch.operations,\n );\n\n syncOps.push(syncOp);\n lastJobByDoc.set(batch.documentId, jobId);\n }\n\n remote.channel.outbox.add(...syncOps);\n }\n\n hasMore = !!page.next;\n if (hasMore) {\n page = await page.next!();\n }\n } while (hasMore);\n\n remote.channel.outbox.advanceOrdinal(maxOrdinal);\n }\n}\n","import type { ILogger } from \"document-model\";\nimport type { Kysely } from \"kysely\";\nimport type { IOperationIndex } from \"../cache/operation-index-types.js\";\nimport type { IReactor, SyncModule } from \"../core/types.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport type {\n ISyncCursorStorage,\n ISyncDeadLetterStorage,\n ISyncRemoteStorage,\n} from \"../storage/interfaces.js\";\nimport { KyselySyncCursorStorage } from \"../storage/kysely/sync-cursor-storage.js\";\nimport { KyselySyncDeadLetterStorage } from \"../storage/kysely/sync-dead-letter-storage.js\";\nimport { KyselySyncRemoteStorage } from \"../storage/kysely/sync-remote-storage.js\";\nimport type { Database } from \"../storage/kysely/types.js\";\nimport type { IChannelFactory, ISyncManager } from \"./interfaces.js\";\nimport { SyncManager } from \"./sync-manager.js\";\n\nexport class SyncBuilder {\n private channelFactory?: IChannelFactory;\n private remoteStorage?: ISyncRemoteStorage;\n private cursorStorage?: ISyncCursorStorage;\n private deadLetterStorage?: ISyncDeadLetterStorage;\n private maxDeadLettersPerRemote: number = 100;\n\n withChannelFactory(factory: IChannelFactory): this {\n this.channelFactory = factory;\n return this;\n }\n\n withRemoteStorage(storage: ISyncRemoteStorage): this {\n this.remoteStorage = storage;\n return this;\n }\n\n withCursorStorage(storage: ISyncCursorStorage): this {\n this.cursorStorage = storage;\n return this;\n }\n\n withDeadLetterStorage(storage: ISyncDeadLetterStorage): this {\n this.deadLetterStorage = storage;\n return this;\n }\n\n withMaxDeadLettersPerRemote(limit: number): this {\n this.maxDeadLettersPerRemote = limit;\n return this;\n }\n\n build(\n reactor: IReactor,\n logger: ILogger,\n operationIndex: IOperationIndex,\n eventBus: IEventBus,\n db: Kysely<Database>,\n ): ISyncManager {\n const module = this.buildModule(\n reactor,\n logger,\n operationIndex,\n eventBus,\n db,\n );\n return module.syncManager;\n }\n\n buildModule(\n reactor: IReactor,\n logger: ILogger,\n operationIndex: IOperationIndex,\n eventBus: IEventBus,\n db: Kysely<Database>,\n ): SyncModule {\n if (!this.channelFactory) {\n throw new Error(\"Channel factory is required\");\n }\n\n const remoteStorage = this.remoteStorage ?? new KyselySyncRemoteStorage(db);\n const cursorStorage = this.cursorStorage ?? new KyselySyncCursorStorage(db);\n const deadLetterStorage =\n this.deadLetterStorage ?? new KyselySyncDeadLetterStorage(db);\n\n const syncManager = new SyncManager(\n logger,\n remoteStorage,\n cursorStorage,\n deadLetterStorage,\n this.channelFactory,\n operationIndex,\n reactor,\n eventBus,\n this.maxDeadLettersPerRemote,\n );\n\n return {\n remoteStorage,\n cursorStorage,\n deadLetterStorage,\n channelFactory: this.channelFactory,\n syncManager,\n };\n }\n}\n","import type { ShutdownStatus } from \"./types.js\";\n\n/**\n * Factory method to create a ShutdownStatus object\n *\n * @param isShutdown - Initial shutdown state\n * @returns A ShutdownStatus object with a getter for the shutdown state\n */\nexport function createShutdownStatus(isShutdown: boolean): ShutdownStatus {\n const shutdownState = isShutdown;\n\n return {\n get isShutdown() {\n return shutdownState;\n },\n completed: Promise.resolve(),\n };\n}\n\n/**\n * Factory method to create a ShutdownStatus that can be updated\n *\n * @param initialState - Initial shutdown state (default: false)\n * @returns A tuple of [ShutdownStatus, setShutdown function, setCompleted function]\n */\nexport function createMutableShutdownStatus(\n initialState = false,\n): [\n ShutdownStatus,\n (value: boolean) => void,\n (completed: Promise<void>) => void,\n] {\n let shutdownState = initialState;\n let completedPromise: Promise<void> = Promise.resolve();\n\n const status: ShutdownStatus = {\n get isShutdown() {\n return shutdownState;\n },\n get completed() {\n return completedPromise;\n },\n };\n\n const setShutdown = (value: boolean) => {\n shutdownState = value;\n };\n\n const setCompleted = (promise: Promise<void>) => {\n completedPromise = promise;\n };\n\n return [status, setShutdown, setCompleted];\n}\n","import type { ViewFilter } from \"./types.js\";\n\nexport function matchesScope(view: ViewFilter = {}, scope: string): boolean {\n if (view.scopes) {\n return view.scopes.includes(scope);\n }\n\n // if there are no scopes specified, we match all scopes\n return true;\n}\n","import type {\n Action,\n DocumentModelModule,\n ISigner,\n Operation,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport type { Kysely } from \"kysely\";\n\nimport type { IProcessorManager } from \"@powerhousedao/shared/processors\";\nimport type { IOperationIndex } from \"../cache/operation-index-types.js\";\nimport type { IWriteCache } from \"../cache/write/interfaces.js\";\nimport type { ReactorClient } from \"../client/reactor-client.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport type { IJobExecutorManager } from \"../executor/interfaces.js\";\nimport type { IJobTracker } from \"../job-tracker/interfaces.js\";\nimport type { IQueue } from \"../queue/interfaces.js\";\nimport type { IReadModelCoordinator } from \"../read-models/interfaces.js\";\nimport type { DocumentViewDatabase } from \"../read-models/types.js\";\nimport type { IDocumentModelRegistry } from \"../registry/interfaces.js\";\nimport type { IJobAwaiter } from \"../shared/awaiter.js\";\nimport type { IConsistencyTracker } from \"../shared/consistency-tracker.js\";\nimport type {\n ConsistencyToken,\n JobInfo,\n PagedResults,\n PagingOptions,\n SearchFilter,\n ShutdownStatus,\n ViewFilter,\n} from \"../shared/types.js\";\nimport type {\n IDocumentIndexer,\n IDocumentView,\n IKeyframeStore,\n IOperationStore,\n ISyncCursorStorage,\n ISyncDeadLetterStorage,\n ISyncRemoteStorage,\n OperationFilter,\n} from \"../storage/interfaces.js\";\nimport type {\n DocumentIndexerDatabase,\n Database as StorageDatabase,\n} from \"../storage/kysely/types.js\";\nimport type { IReactorSubscriptionManager } from \"../subs/types.js\";\nimport type { IChannelFactory, ISyncManager } from \"../sync/interfaces.js\";\n\nexport class AbortError extends Error {\n constructor(message?: string) {\n super(message || \"Aborted\");\n\n this.name = \"AbortError\";\n }\n}\n\nexport const isAbortError = (error: unknown): boolean => {\n return error instanceof AbortError;\n};\n\n/**\n * A single mutation job within a batch request.\n */\nexport type ExecutionJobPlan = {\n key: string;\n documentId: string;\n scope: string;\n branch: string;\n actions: Action[];\n dependsOn: string[];\n};\n\n/**\n * Request for batch mutation operation.\n */\nexport type BatchExecutionRequest = {\n jobs: ExecutionJobPlan[];\n};\n\n/**\n * Result from batch mutation operation.\n */\nexport type BatchExecutionResult = {\n jobs: Record<string, JobInfo>;\n};\n\n/**\n * A single load job within a batch request.\n */\nexport type LoadJobPlan = {\n key: string;\n documentId: string;\n scope: string;\n branch: string;\n operations: Operation[];\n dependsOn: string[];\n};\n\n/**\n * Request for batch load operation.\n */\nexport type BatchLoadRequest = {\n jobs: LoadJobPlan[];\n};\n\n/**\n * Result from batch load operation.\n */\nexport type BatchLoadResult = {\n jobs: Record<string, JobInfo>;\n};\n\n/**\n * The main Reactor interface that serves as a facade for document operations.\n * This interface provides a unified API for document management, including\n * creation, retrieval, mutation, and deletion operations.\n */\nexport interface IReactor {\n /**\n * Signals that the reactor should shutdown.\n */\n kill(): ShutdownStatus;\n\n /**\n * Retrieves a list of document model modules.\n *\n * @param namespace - Optional namespace like \"powerhouse\" or \"sky\", defaults to \"\"\n * @param paging - Optional options for paging data in large queries.\n * @param signal - Optional abort signal to cancel the request\n * @returns List of document model modules\n */\n getDocumentModels(\n namespace?: string,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentModelModule>>;\n\n /**\n * Retrieves a specific PHDocument by id\n *\n * @param id - Required, this is the document id\n * @param view - Optional filter containing branch and scopes information\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The up-to-date PHDocument\n */\n get<TDocument extends PHDocument>(\n id: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Retrieves a specific PHDocument by slug\n *\n * @param slug - Required, this is the document slug\n * @param view - Optional filter containing branch and scopes information\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The up-to-date PHDocument with scopes and list of child document ids\n */\n getBySlug<TDocument extends PHDocument>(\n slug: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Retrieves a specific PHDocument by identifier (either id or slug).\n * Throws an error if the identifier matches both an id and a slug that refer to different documents.\n *\n * @param identifier - Required, this is the document id or slug\n * @param view - Optional filter containing branch and scopes information\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The up-to-date PHDocument with scopes and list of child document ids\n * @throws {Error} If identifier matches both an ID and slug referring to different documents\n */\n getByIdOrSlug<TDocument extends PHDocument>(\n identifier: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Retrieves the children of a document\n *\n * @param parentId - The parent document id\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The list of child document ids\n */\n getChildren(\n parentId: string,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<string[]>;\n\n /**\n * Retrieves the parents of a document\n *\n * @param childId - The child document id\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The list of parent document ids\n */\n getParents(\n childId: string,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<string[]>;\n\n /**\n * Retrieves the operations for a document\n *\n * @param documentId - The document id\n * @param view - Optional filter containing branch and scopes information\n * @param filter - Optional filter for actionTypes, timestamps, and revision\n * @param paging - Optional pagination options\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The list of operations\n */\n getOperations(\n documentId: string,\n view?: ViewFilter,\n filter?: OperationFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<Record<string, PagedResults<Operation>>>;\n\n /**\n * Filters documents by criteria and returns a list of them\n *\n * @param search - Search filter options (type, parentId, identifiers)\n * @param view - Optional filter containing branch and scopes information\n * @param paging - Optional pagination options\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns List of documents matching criteria and pagination cursor\n */\n find(\n search: SearchFilter,\n view?: ViewFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>>;\n\n /**\n * Creates a document\n *\n * @param document - Document with optional id, slug, parent, model type, and initial state\n * @param signer - Optional signer to sign the actions\n * @param signal - Optional abort signal to cancel the request\n * @param meta - Optional metadata that flows through the job lifecycle\n * @returns The job status\n */\n create(\n document: PHDocument,\n signer?: ISigner,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo>;\n\n /**\n * Deletes a document\n *\n * @param id - Document id\n * @param signer - Optional signer to sign the actions\n * @param signal - Optional abort signal to cancel the request\n * @param meta - Optional metadata that flows through the job lifecycle\n * @returns The job id and status\n */\n deleteDocument(\n id: string,\n signer?: ISigner,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo>;\n\n /**\n * Applies a list of actions to a document.\n *\n * @param docId - Document id\n * @param branch - Branch to apply actions to\n * @param actions - List of actions to apply\n * @param signal - Optional abort signal to cancel the request\n * @param meta - Optional metadata that flows through the job lifecycle\n * @returns The job id and status\n */\n execute(\n docId: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo>;\n\n /**\n * Loads existing operations generated elsewhere into this reactor.\n *\n * @param docId - Document id\n * @param branch - Branch to load operations to\n * @param operations - List of operations to load\n * @param signal - Optional abort signal to cancel the request\n * @param meta - Optional metadata that flows through the job lifecycle\n * @returns The job id and status\n */\n load(\n docId: string,\n branch: string,\n operations: Operation[],\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo>;\n\n /**\n * Applies multiple mutations across documents with dependency management.\n *\n * @param request - Batch mutation request containing jobs with dependencies\n * @param signal - Optional abort signal to cancel the request\n * @param meta - Optional metadata that flows through the job lifecycle\n * @returns Map of job keys to job information\n */\n executeBatch(\n request: BatchExecutionRequest,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<BatchExecutionResult>;\n\n /**\n * Loads multiple batches of pre-existing operations across documents with dependency management.\n *\n * @param request - Batch load request containing jobs with dependencies\n * @param signal - Optional abort signal to cancel the request\n * @param meta - Optional metadata that flows through the job lifecycle\n * @returns Map of job keys to job information\n */\n loadBatch(\n request: BatchLoadRequest,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<BatchLoadResult>;\n\n /**\n * Adds multiple documents as children to another\n *\n * @param parentId - Parent document id\n * @param documentIds - List of document ids to add as children\n * @param branch - Branch to add children to, defaults to \"main\"\n * @param signer - Optional signer to sign the actions\n * @param signal - Optional abort signal to cancel the request\n * @returns The job id and status\n */\n addChildren(\n parentId: string,\n documentIds: string[],\n branch?: string,\n signer?: ISigner,\n signal?: AbortSignal,\n ): Promise<JobInfo>;\n\n /**\n * Removes multiple documents as children from another\n *\n * @param parentId - Parent document id\n * @param documentIds - List of document ids to remove as children\n * @param branch - Branch to remove children from, defaults to \"main\"\n * @param signer - Optional signer to sign the actions\n * @param signal - Optional abort signal to cancel the request\n * @returns The job id and status\n */\n removeChildren(\n parentId: string,\n documentIds: string[],\n branch?: string,\n signer?: ISigner,\n signal?: AbortSignal,\n ): Promise<JobInfo>;\n\n /**\n * Retrieves the status of a job\n *\n * @param jobId - The job id\n * @returns The job status\n */\n getJobStatus(jobId: string, signal?: AbortSignal): Promise<JobInfo>;\n}\n\n/**\n * Feature flags for reactor configuration\n */\nexport type ReactorFeatures = { [key: string]: boolean };\n\n/**\n * Combined database type that includes all schemas\n */\nexport type Database = StorageDatabase &\n DocumentViewDatabase &\n DocumentIndexerDatabase;\n\n/**\n * Container for all sync manager dependencies created during the build process.\n */\nexport interface SyncModule {\n remoteStorage: ISyncRemoteStorage;\n cursorStorage: ISyncCursorStorage;\n deadLetterStorage: ISyncDeadLetterStorage;\n channelFactory: IChannelFactory;\n syncManager: ISyncManager;\n}\n\n/**\n * Container for all reactor dependencies created during the build process.\n * Provides direct access to internal components for advanced use cases,\n * testing, or integration scenarios.\n */\nexport interface ReactorModule {\n eventBus: IEventBus;\n documentModelRegistry: IDocumentModelRegistry;\n queue: IQueue;\n jobTracker: IJobTracker;\n executorManager: IJobExecutorManager;\n database: Kysely<Database>;\n operationStore: IOperationStore;\n keyframeStore: IKeyframeStore;\n writeCache: IWriteCache;\n operationIndex: IOperationIndex;\n documentView: IDocumentView;\n documentViewConsistencyTracker: IConsistencyTracker;\n documentIndexer: IDocumentIndexer;\n documentIndexerConsistencyTracker: IConsistencyTracker;\n readModelCoordinator: IReadModelCoordinator;\n subscriptionManager: IReactorSubscriptionManager;\n processorManager: IProcessorManager;\n processorManagerConsistencyTracker: IConsistencyTracker;\n syncModule: SyncModule | undefined;\n reactor: IReactor;\n}\n\n/**\n * Container for all reactor client dependencies created during the build process.\n * Provides direct access to internal components for advanced use cases,\n * testing, or integration scenarios.\n */\nexport interface ReactorClientModule {\n client: ReactorClient;\n reactor: IReactor;\n eventBus: IEventBus;\n documentIndexer: IDocumentIndexer;\n documentView: IDocumentView;\n signer: ISigner;\n subscriptionManager: IReactorSubscriptionManager;\n jobAwaiter: IJobAwaiter;\n reactorModule: ReactorModule | undefined;\n}\n","import type {\n Action,\n CreateDocumentActionInput,\n DocumentModelModule,\n ISigner,\n Operation,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport {\n addRelationshipAction,\n createDocumentAction,\n deleteDocumentAction,\n removeRelationshipAction,\n upgradeDocumentAction,\n} from \"../actions/index.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport {\n ReactorEventTypes,\n type JobFailedEvent,\n type JobPendingEvent,\n} from \"../events/types.js\";\nimport type { IJobExecutorManager } from \"../executor/interfaces.js\";\nimport type { IJobTracker } from \"../job-tracker/interfaces.js\";\nimport type { IQueue } from \"../queue/interfaces.js\";\nimport type { Job } from \"../queue/types.js\";\nimport type { IReadModelCoordinator } from \"../read-models/interfaces.js\";\nimport type { IDocumentModelRegistry } from \"../registry/interfaces.js\";\nimport { createMutableShutdownStatus } from \"../shared/factories.js\";\nimport type {\n ConsistencyToken,\n JobInfo,\n JobMeta,\n PagedResults,\n PagingOptions,\n SearchFilter,\n ShutdownStatus,\n ViewFilter,\n} from \"../shared/types.js\";\nimport { JobStatus } from \"../shared/types.js\";\nimport { matchesScope } from \"../shared/utils.js\";\nimport type {\n IDocumentIndexer,\n IDocumentView,\n IOperationStore,\n OperationFilter,\n} from \"../storage/interfaces.js\";\nimport {\n AbortError,\n type BatchExecutionRequest,\n type BatchExecutionResult,\n type BatchLoadRequest,\n type BatchLoadResult,\n type IReactor,\n type ReactorFeatures,\n} from \"./types.js\";\nimport {\n buildSingleJobMeta,\n filterByType,\n getSharedActionScope,\n getSharedOperationScope,\n signAction,\n signActions,\n toErrorInfo,\n topologicalSort,\n validateActionScopes,\n validateBatchLoadRequest,\n validateBatchRequest,\n validateOperationScopes,\n} from \"./utils.js\";\n\n/**\n * This class implements the IReactor interface and serves as the main entry point\n * for the new Reactor architecture.\n */\nexport class Reactor implements IReactor {\n private logger: ILogger;\n private documentModelRegistry: IDocumentModelRegistry;\n private shutdownStatus: ShutdownStatus;\n private setShutdown: (value: boolean) => void;\n private setCompleted: (completed: Promise<void>) => void;\n private queue: IQueue;\n private jobTracker: IJobTracker;\n private readModelCoordinator: IReadModelCoordinator;\n private features: ReactorFeatures;\n private documentView: IDocumentView;\n private documentIndexer: IDocumentIndexer;\n private operationStore: IOperationStore;\n private eventBus: IEventBus;\n private executorManager: IJobExecutorManager;\n\n constructor(\n logger: ILogger,\n documentModelRegistry: IDocumentModelRegistry,\n queue: IQueue,\n jobTracker: IJobTracker,\n readModelCoordinator: IReadModelCoordinator,\n features: ReactorFeatures,\n documentView: IDocumentView,\n documentIndexer: IDocumentIndexer,\n operationStore: IOperationStore,\n eventBus: IEventBus,\n executorManager: IJobExecutorManager,\n ) {\n this.logger = logger;\n this.documentModelRegistry = documentModelRegistry;\n this.queue = queue;\n this.jobTracker = jobTracker;\n this.readModelCoordinator = readModelCoordinator;\n this.features = features;\n this.documentView = documentView;\n this.documentIndexer = documentIndexer;\n this.operationStore = operationStore;\n this.eventBus = eventBus;\n this.executorManager = executorManager;\n\n const [status, setShutdown, setCompleted] =\n createMutableShutdownStatus(false);\n this.shutdownStatus = status;\n this.setShutdown = setShutdown;\n this.setCompleted = setCompleted;\n\n this.eventBus.subscribe(\n ReactorEventTypes.JOB_FAILED,\n (_type, event: JobFailedEvent) => {\n this.logger.error(\n \"Job @JobId failed with @Message: @Job\",\n event.jobId,\n event.error.message,\n event.job,\n );\n },\n );\n\n this.readModelCoordinator.start();\n }\n\n kill(): ShutdownStatus {\n this.logger.verbose(\"kill()\");\n\n if (this.shutdownStatus.isShutdown) {\n return this.shutdownStatus;\n }\n\n this.setShutdown(true);\n\n const shutdownAsync = async () => {\n await this.executorManager.stop(true);\n\n this.readModelCoordinator.stop();\n this.jobTracker.shutdown();\n };\n\n this.setCompleted(shutdownAsync());\n\n return this.shutdownStatus;\n }\n\n getDocumentModels(\n namespace?: string,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentModelModule>> {\n this.logger.verbose(\n \"getDocumentModels(@namespace, @paging)\",\n namespace,\n paging,\n );\n\n if (signal?.aborted) {\n throw new AbortError();\n }\n\n const modules = this.documentModelRegistry.getAllModules();\n const filteredModels = modules.filter(\n (module: DocumentModelModule) =>\n !namespace || module.documentModel.global.id.startsWith(namespace),\n );\n\n const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;\n const limit = paging?.limit || filteredModels.length;\n const pagedModels = filteredModels.slice(startIndex, startIndex + limit);\n\n const hasMore = startIndex + limit < filteredModels.length;\n const nextCursor = hasMore ? String(startIndex + limit) : undefined;\n\n return Promise.resolve({\n results: pagedModels,\n options: paging || { cursor: \"0\", limit: filteredModels.length },\n nextCursor,\n next: hasMore\n ? () =>\n this.getDocumentModels(\n namespace,\n { cursor: nextCursor!, limit },\n signal,\n )\n : undefined,\n });\n }\n\n async get<TDocument extends PHDocument>(\n id: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\"get(@id, @view)\", id, view);\n\n return await this.documentView.get<TDocument>(\n id,\n view,\n consistencyToken,\n signal,\n );\n }\n\n async getBySlug<TDocument extends PHDocument>(\n slug: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\"getBySlug(@slug, @view)\", slug, view);\n\n const documentId = await this.documentView.resolveSlug(\n slug,\n view,\n consistencyToken,\n signal,\n );\n\n if (!documentId) {\n throw new Error(`Document not found with slug: ${slug}`);\n }\n\n return await this.get<TDocument>(\n documentId,\n view,\n consistencyToken,\n signal,\n );\n }\n\n async getByIdOrSlug<TDocument extends PHDocument>(\n identifier: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\"getByIdOrSlug(@identifier, @view)\", identifier, view);\n\n return await this.documentView.getByIdOrSlug<TDocument>(\n identifier,\n view,\n consistencyToken,\n signal,\n );\n }\n\n async getChildren(\n documentId: string,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<string[]> {\n const relationships = await this.documentIndexer.getOutgoing(\n documentId,\n [\"child\"],\n undefined,\n consistencyToken,\n signal,\n );\n\n if (signal?.aborted) {\n throw new AbortError();\n }\n\n return relationships.results.map((rel) => rel.targetId);\n }\n\n async getParents(\n childId: string,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<string[]> {\n const relationships = await this.documentIndexer.getIncoming(\n childId,\n [\"parent\"],\n undefined,\n consistencyToken,\n signal,\n );\n\n if (signal?.aborted) {\n throw new AbortError();\n }\n\n return relationships.results.map((rel) => rel.sourceId);\n }\n\n async getOperations(\n documentId: string,\n view?: ViewFilter,\n filter?: OperationFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<Record<string, PagedResults<Operation>>> {\n this.logger.verbose(\n \"getOperations(@documentId, @view, @filter, @paging)\",\n documentId,\n view,\n filter,\n paging,\n );\n\n const branch = view?.branch || \"main\";\n\n const revisions = await this.operationStore.getRevisions(\n documentId,\n branch,\n signal,\n );\n\n if (signal?.aborted) {\n throw new AbortError();\n }\n\n const allScopes = Object.keys(revisions.revision);\n const result: Record<string, PagedResults<Operation>> = {};\n\n for (const scope of allScopes) {\n if (!matchesScope(view, scope)) {\n continue;\n }\n\n if (signal?.aborted) {\n throw new AbortError();\n }\n\n const scopeResult = await this.operationStore.getSince(\n documentId,\n scope,\n branch,\n -1,\n filter,\n paging,\n signal,\n );\n\n result[scope] = {\n results: scopeResult.results,\n options: scopeResult.options,\n nextCursor: scopeResult.nextCursor,\n next: scopeResult.next\n ? async () => {\n const nextPage = await this.getOperations(\n documentId,\n view,\n filter,\n {\n cursor: scopeResult.nextCursor!,\n limit: scopeResult.options.limit,\n },\n consistencyToken,\n signal,\n );\n return nextPage[scope];\n }\n : undefined,\n };\n }\n\n return result;\n }\n\n async find(\n search: SearchFilter,\n view?: ViewFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\"find(@search, @view, @paging)\", search, view, paging);\n\n let results: PagedResults<PHDocument>;\n if (search.ids) {\n if (search.slugs && search.slugs.length > 0) {\n throw new Error(\"Cannot use both ids and slugs in the same search\");\n }\n\n results = await this.findByIds(\n search.ids,\n view,\n paging,\n consistencyToken,\n signal,\n );\n\n if (search.type) {\n results = filterByType(results, search.type);\n }\n } else if (search.slugs) {\n results = await this.findBySlugs(\n search.slugs,\n view,\n paging,\n consistencyToken,\n signal,\n );\n\n if (search.type) {\n results = filterByType(results, search.type);\n }\n } else if (search.parentId) {\n results = await this.findByParentId(\n search.parentId,\n view,\n paging,\n consistencyToken,\n signal,\n );\n\n if (search.type) {\n results = filterByType(results, search.type);\n }\n } else if (search.type) {\n results = await this.findByType(\n search.type,\n view,\n paging,\n consistencyToken,\n signal,\n );\n } else {\n throw new Error(\"No search criteria provided\");\n }\n\n if (signal?.aborted) {\n throw new AbortError();\n }\n\n return results;\n }\n\n async create(\n document: PHDocument,\n signer?: ISigner,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo> {\n this.logger.verbose(\n \"create(@id, @type, @slug)\",\n document.header.id,\n document.header.documentType,\n document.header.slug,\n );\n const createdAtUtcIso = new Date().toISOString();\n\n if (signal?.aborted) {\n throw new AbortError();\n }\n\n const createInput: CreateDocumentActionInput = {\n model: document.header.documentType,\n version: 0,\n documentId: document.header.id,\n signing: {\n signature: document.header.id,\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 createAction = createDocumentAction(createInput);\n const upgradeAction = upgradeDocumentAction({\n documentId: document.header.id,\n model: document.header.documentType,\n fromVersion: 0,\n toVersion: document.state.document.version,\n initialState: document.state,\n });\n\n let actions: Action[] = [createAction, upgradeAction];\n if (signer) {\n actions = await signActions(actions, signer, signal);\n }\n\n const jobId = uuidv4();\n const jobMeta = buildSingleJobMeta(jobId, meta);\n\n const job: Job = {\n id: jobId,\n kind: \"mutation\",\n documentId: document.header.id,\n scope: \"document\",\n branch: \"main\",\n actions,\n operations: [],\n createdAt: new Date().toISOString(),\n queueHint: [],\n maxRetries: 3,\n errorHistory: [],\n meta: jobMeta,\n };\n\n const jobInfo: JobInfo = {\n id: jobId,\n status: JobStatus.PENDING,\n createdAtUtcIso,\n consistencyToken: {\n version: 1,\n createdAtUtcIso,\n coordinates: [],\n },\n meta: jobMeta,\n };\n this.jobTracker.registerJob(jobInfo);\n this.emitJobPending(jobInfo.id, jobMeta);\n\n await this.queue.enqueue(job);\n\n return jobInfo;\n }\n\n async deleteDocument(\n id: string,\n signer?: ISigner,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo> {\n this.logger.verbose(\"deleteDocument(@id)\", id);\n const createdAtUtcIso = new Date().toISOString();\n\n if (signal?.aborted) {\n throw new AbortError();\n }\n\n let action = deleteDocumentAction(id);\n\n if (signer) {\n action = await signAction(action, signer, signal);\n }\n\n const jobId = uuidv4();\n const jobMeta = buildSingleJobMeta(jobId, meta);\n\n const job: Job = {\n id: jobId,\n kind: \"mutation\",\n documentId: id,\n scope: \"document\",\n branch: \"main\",\n actions: [action],\n operations: [],\n createdAt: new Date().toISOString(),\n queueHint: [],\n maxRetries: 3,\n errorHistory: [],\n meta: jobMeta,\n };\n\n const jobInfo: JobInfo = {\n id: jobId,\n status: JobStatus.PENDING,\n createdAtUtcIso,\n consistencyToken: {\n version: 1,\n createdAtUtcIso,\n coordinates: [],\n },\n meta: jobMeta,\n };\n this.jobTracker.registerJob(jobInfo);\n this.emitJobPending(jobInfo.id, jobMeta);\n\n await this.queue.enqueue(job);\n\n return jobInfo;\n }\n\n async execute(\n docId: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo> {\n this.logger.verbose(\n \"execute(@docId, @branch, @actions)\",\n docId,\n branch,\n actions,\n );\n\n if (signal?.aborted) {\n throw new AbortError();\n }\n\n const createdAtUtcIso = new Date().toISOString();\n const scope = getSharedActionScope(actions);\n const jobId = uuidv4();\n const jobMeta = buildSingleJobMeta(jobId, meta);\n\n const job: Job = {\n id: jobId,\n kind: \"mutation\",\n documentId: docId,\n scope: scope,\n branch: branch,\n actions: actions,\n operations: [],\n createdAt: new Date().toISOString(),\n queueHint: [],\n maxRetries: 3,\n errorHistory: [],\n meta: jobMeta,\n };\n\n const jobInfo: JobInfo = {\n id: jobId,\n status: JobStatus.PENDING,\n createdAtUtcIso,\n consistencyToken: {\n version: 1,\n createdAtUtcIso,\n coordinates: [],\n },\n meta: jobMeta,\n };\n this.jobTracker.registerJob(jobInfo);\n this.emitJobPending(jobInfo.id, jobMeta);\n\n await this.queue.enqueue(job);\n\n if (signal?.aborted) {\n throw new AbortError();\n }\n\n return jobInfo;\n }\n\n async load(\n docId: string,\n branch: string,\n operations: Operation[],\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo> {\n this.logger.verbose(\n \"load(@docId, @branch, @count, @operations)\",\n docId,\n branch,\n operations.length,\n operations,\n );\n\n if (signal?.aborted) {\n throw new AbortError();\n }\n\n if (operations.length === 0) {\n throw new Error(\"load requires at least one operation\");\n }\n\n const scope = getSharedOperationScope(operations);\n const createdAtUtcIso = new Date().toISOString();\n const jobId = uuidv4();\n const jobMeta = buildSingleJobMeta(jobId, meta);\n\n const job: Job = {\n id: jobId,\n kind: \"load\",\n documentId: docId,\n scope,\n branch,\n actions: [],\n operations,\n createdAt: createdAtUtcIso,\n queueHint: [],\n maxRetries: 3,\n errorHistory: [],\n meta: jobMeta,\n };\n\n const jobInfo: JobInfo = {\n id: jobId,\n status: JobStatus.PENDING,\n createdAtUtcIso,\n consistencyToken: {\n version: 1,\n createdAtUtcIso,\n coordinates: [],\n },\n meta: jobMeta,\n };\n this.jobTracker.registerJob(jobInfo);\n this.emitJobPending(jobInfo.id, jobMeta);\n\n await this.queue.enqueue(job);\n\n if (signal?.aborted) {\n throw new AbortError();\n }\n\n return jobInfo;\n }\n\n async executeBatch(\n request: BatchExecutionRequest,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<BatchExecutionResult> {\n this.logger.verbose(\"executeBatch(@count jobs)\", request.jobs.length);\n\n if (signal?.aborted) {\n throw new AbortError();\n }\n validateBatchRequest(request.jobs);\n for (const jobPlan of request.jobs) {\n validateActionScopes(jobPlan);\n }\n const createdAtUtcIso = new Date().toISOString();\n const planKeyToJobId = new Map<string, string>();\n for (const jobPlan of request.jobs) {\n planKeyToJobId.set(jobPlan.key, uuidv4());\n }\n const batchId = uuidv4();\n const batchJobIds = [...planKeyToJobId.values()];\n const batchMeta: JobMeta = {\n ...meta,\n batchId,\n batchJobIds,\n };\n const jobInfos = new Map<string, JobInfo>();\n for (const jobPlan of request.jobs) {\n const jobId = planKeyToJobId.get(jobPlan.key)!;\n const jobInfo: JobInfo = {\n id: jobId,\n status: JobStatus.PENDING,\n createdAtUtcIso,\n consistencyToken: {\n version: 1,\n createdAtUtcIso,\n coordinates: [],\n },\n meta: batchMeta,\n };\n this.jobTracker.registerJob(jobInfo);\n this.emitJobPending(jobInfo.id, batchMeta);\n jobInfos.set(jobPlan.key, jobInfo);\n }\n const sortedKeys = topologicalSort(request.jobs);\n const enqueuedKeys: string[] = [];\n try {\n for (const key of sortedKeys) {\n if (signal?.aborted) {\n throw new AbortError();\n }\n const jobPlan = request.jobs.find((j) => j.key === key)!;\n const jobId = planKeyToJobId.get(key)!;\n const queueHint = jobPlan.dependsOn.map(\n (depKey) => planKeyToJobId.get(depKey)!,\n );\n const job: Job = {\n id: jobId,\n kind: \"mutation\",\n documentId: jobPlan.documentId,\n scope: jobPlan.scope,\n branch: jobPlan.branch,\n actions: jobPlan.actions,\n operations: [],\n createdAt: createdAtUtcIso,\n queueHint,\n maxRetries: 3,\n errorHistory: [],\n meta: batchMeta,\n };\n await this.queue.enqueue(job);\n enqueuedKeys.push(key);\n }\n } catch (error) {\n for (const key of enqueuedKeys) {\n const jobId = planKeyToJobId.get(key)!;\n try {\n await this.queue.remove(jobId);\n } catch {\n // Ignore removal errors during cleanup\n }\n }\n for (const jobInfo of jobInfos.values()) {\n this.jobTracker.markFailed(\n jobInfo.id,\n toErrorInfo(\"Batch enqueue failed\"),\n );\n }\n throw error;\n }\n const result: BatchExecutionResult = {\n jobs: Object.fromEntries(jobInfos),\n };\n return result;\n }\n\n async loadBatch(\n request: BatchLoadRequest,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<BatchLoadResult> {\n this.logger.verbose(\"loadBatch(@count jobs)\", request.jobs.length);\n\n if (signal?.aborted) {\n throw new AbortError();\n }\n validateBatchLoadRequest(request.jobs);\n for (const jobPlan of request.jobs) {\n validateOperationScopes(jobPlan);\n }\n const createdAtUtcIso = new Date().toISOString();\n const planKeyToJobId = new Map<string, string>();\n for (const jobPlan of request.jobs) {\n planKeyToJobId.set(jobPlan.key, uuidv4());\n }\n const batchId = uuidv4();\n const batchJobIds = [...planKeyToJobId.values()];\n const batchMeta: JobMeta = {\n ...meta,\n batchId,\n batchJobIds,\n };\n const jobInfos = new Map<string, JobInfo>();\n for (const jobPlan of request.jobs) {\n const jobId = planKeyToJobId.get(jobPlan.key)!;\n const jobInfo: JobInfo = {\n id: jobId,\n status: JobStatus.PENDING,\n createdAtUtcIso,\n consistencyToken: {\n version: 1,\n createdAtUtcIso,\n coordinates: [],\n },\n meta: batchMeta,\n };\n this.jobTracker.registerJob(jobInfo);\n this.emitJobPending(jobInfo.id, batchMeta);\n jobInfos.set(jobPlan.key, jobInfo);\n }\n const sortedKeys = topologicalSort(request.jobs);\n const enqueuedKeys: string[] = [];\n try {\n for (const key of sortedKeys) {\n if (signal?.aborted) {\n throw new AbortError();\n }\n const jobPlan = request.jobs.find((j) => j.key === key)!;\n const jobId = planKeyToJobId.get(key)!;\n const queueHint = jobPlan.dependsOn.map(\n (depKey) => planKeyToJobId.get(depKey)!,\n );\n const job: Job = {\n id: jobId,\n kind: \"load\",\n documentId: jobPlan.documentId,\n scope: jobPlan.scope,\n branch: jobPlan.branch,\n actions: [],\n operations: jobPlan.operations,\n createdAt: createdAtUtcIso,\n queueHint,\n maxRetries: 3,\n errorHistory: [],\n meta: batchMeta,\n };\n await this.queue.enqueue(job);\n enqueuedKeys.push(key);\n }\n } catch (error) {\n for (const key of enqueuedKeys) {\n const jobId = planKeyToJobId.get(key)!;\n try {\n await this.queue.remove(jobId);\n } catch {\n // Ignore removal errors during cleanup\n }\n }\n for (const jobInfo of jobInfos.values()) {\n this.jobTracker.markFailed(\n jobInfo.id,\n toErrorInfo(\"Batch enqueue failed\"),\n );\n }\n throw error;\n }\n const result: BatchLoadResult = {\n jobs: Object.fromEntries(jobInfos),\n };\n return result;\n }\n\n async addChildren(\n parentId: string,\n documentIds: string[],\n branch: string = \"main\",\n signer?: ISigner,\n signal?: AbortSignal,\n ): Promise<JobInfo> {\n this.logger.verbose(\n \"addChildren(@parentId, @count children, @branch)\",\n parentId,\n documentIds.length,\n branch,\n );\n\n if (signal?.aborted) {\n throw new AbortError();\n }\n\n let actions: Action[] = documentIds.map((childId) =>\n addRelationshipAction(parentId, childId, \"child\"),\n );\n\n if (signer) {\n actions = await signActions(actions, signer, signal);\n }\n\n return await this.execute(parentId, branch, actions, signal);\n }\n\n async removeChildren(\n parentId: string,\n documentIds: string[],\n branch: string = \"main\",\n signer?: ISigner,\n signal?: AbortSignal,\n ): Promise<JobInfo> {\n this.logger.verbose(\n \"removeChildren(@parentId, @count children, @branch)\",\n parentId,\n documentIds.length,\n branch,\n );\n\n if (signal?.aborted) {\n throw new AbortError();\n }\n\n let actions: Action[] = documentIds.map((childId) =>\n removeRelationshipAction(parentId, childId, \"child\"),\n );\n\n if (signer) {\n actions = await signActions(actions, signer, signal);\n }\n\n return await this.execute(parentId, branch, actions, signal);\n }\n\n getJobStatus(jobId: string, signal?: AbortSignal): Promise<JobInfo> {\n this.logger.verbose(\"getJobStatus(@jobId)\", jobId);\n\n if (signal?.aborted) {\n throw new AbortError();\n }\n\n const jobInfo = this.jobTracker.getJobStatus(jobId);\n\n if (!jobInfo) {\n const now = new Date().toISOString();\n return Promise.resolve({\n id: jobId,\n status: JobStatus.FAILED,\n createdAtUtcIso: now,\n completedAtUtcIso: now,\n error: toErrorInfo(\"Job not found\"),\n consistencyToken: {\n version: 1,\n createdAtUtcIso: now,\n coordinates: [],\n },\n meta: { batchId: jobId, batchJobIds: [jobId] },\n });\n }\n\n return Promise.resolve(jobInfo);\n }\n\n private async findByIds(\n ids: string[],\n view?: ViewFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\"findByIds(@count ids)\", ids.length);\n\n const startIndex = paging?.cursor ? parseInt(paging.cursor) || 0 : 0;\n const limit = paging?.limit || ids.length;\n const pagedIds = ids.slice(startIndex, startIndex + limit);\n\n const results = await this.documentView.getMany<PHDocument>(\n pagedIds,\n view,\n consistencyToken,\n signal,\n );\n\n const hasMore = startIndex + limit < ids.length;\n const nextCursor = hasMore ? String(startIndex + limit) : undefined;\n\n return {\n results,\n options: paging || { cursor: \"0\", limit: ids.length },\n nextCursor,\n };\n }\n\n private async findBySlugs(\n slugs: string[],\n view?: ViewFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\"findBySlugs(@count slugs)\", slugs.length);\n\n const ids = await this.documentView.resolveSlugs(\n slugs,\n view,\n consistencyToken,\n signal,\n );\n\n return await this.findByIds(ids, view, paging, consistencyToken, signal);\n }\n\n private async findByParentId(\n parentId: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\"findByParentId(@parentId)\", parentId);\n\n const relationships = await this.documentIndexer.getOutgoing(\n parentId,\n [\"child\"],\n paging,\n consistencyToken,\n signal,\n );\n\n const ids = relationships.results.map((rel) => rel.targetId);\n return await this.findByIds(ids, view, paging, undefined, signal);\n }\n\n private async findByType(\n type: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\"findByType(@type)\", type);\n\n return await this.documentView.findByType(\n type,\n view,\n paging,\n consistencyToken,\n signal,\n );\n }\n\n private emitJobPending(jobId: string, meta: JobMeta): void {\n const event: JobPendingEvent = {\n jobId,\n jobMeta: meta,\n };\n this.eventBus.emit(ReactorEventTypes.JOB_PENDING, event).catch(() => {\n // Ignore event emission errors\n });\n }\n}\n","import { PGlite } from \"@electric-sql/pglite\";\nimport type {\n DocumentModelModule,\n UpgradeManifest,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport { ConsoleLogger } from \"document-model\";\nimport { Kysely } from \"kysely\";\nimport { PGliteDialect } from \"kysely-pglite-dialect\";\nimport { CollectionMembershipCache } from \"../cache/collection-membership-cache.js\";\nimport { DocumentMetaCache } from \"../cache/document-meta-cache.js\";\nimport { KyselyOperationIndex } from \"../cache/kysely-operation-index.js\";\nimport { KyselyWriteCache } from \"../cache/kysely-write-cache.js\";\nimport type { WriteCacheConfig } from \"../cache/write-cache-types.js\";\nimport { EventBus } from \"../events/event-bus.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport {\n KyselyExecutionScope,\n type IExecutionScope,\n} from \"../executor/execution-scope.js\";\nimport type { IJobExecutorManager } from \"../executor/interfaces.js\";\nimport { SimpleJobExecutorManager } from \"../executor/simple-job-executor-manager.js\";\nimport { SimpleJobExecutor } from \"../executor/simple-job-executor.js\";\nimport type { JobExecutorConfig } from \"../executor/types.js\";\nimport { InMemoryJobTracker } from \"../job-tracker/in-memory-job-tracker.js\";\nimport { ProcessorManager } from \"../processors/processor-manager.js\";\nimport type { IQueue } from \"../queue/interfaces.js\";\nimport { InMemoryQueue } from \"../queue/queue.js\";\nimport { ReadModelCoordinator } from \"../read-models/coordinator.js\";\nimport { KyselyDocumentView } from \"../read-models/document-view.js\";\nimport type {\n IReadModel,\n IReadModelCoordinator,\n} from \"../read-models/interfaces.js\";\nimport {\n DocumentModelResolver,\n NullDocumentModelResolver,\n} from \"../registry/document-model-resolver.js\";\nimport { DocumentModelRegistry } from \"../registry/implementation.js\";\nimport type { IDocumentModelLoader } from \"../registry/interfaces.js\";\nimport { ConsistencyTracker } from \"../shared/consistency-tracker.js\";\nimport type { SignatureVerificationHandler } from \"../signer/types.js\";\nimport {\n KyselyDocumentIndexer,\n type IndexerDatabase,\n} from \"../storage/kysely/document-indexer.js\";\nimport { KyselyKeyframeStore } from \"../storage/kysely/keyframe-store.js\";\nimport { KyselyOperationStore } from \"../storage/kysely/store.js\";\nimport type { Database as StorageDatabase } from \"../storage/kysely/types.js\";\nimport {\n REACTOR_SCHEMA,\n runMigrations,\n} from \"../storage/migrations/migrator.js\";\nimport type { MigrationStrategy } from \"../storage/migrations/types.js\";\nimport { DefaultSubscriptionErrorHandler } from \"../subs/default-error-handler.js\";\nimport { ReactorSubscriptionManager } from \"../subs/react-subscription-manager.js\";\nimport { SubscriptionNotificationReadModel } from \"../subs/subscription-notification-read-model.js\";\nimport { GqlRequestChannelFactory } from \"../sync/channels/gql-request-channel-factory.js\";\nimport { GqlResponseChannelFactory } from \"../sync/channels/gql-response-channel-factory.js\";\nimport { SyncBuilder } from \"../sync/sync-builder.js\";\nimport type { JwtHandler } from \"../sync/types.js\";\nimport { ChannelScheme } from \"../sync/types.js\";\nimport { Reactor } from \"./reactor.js\";\nimport type {\n Database,\n IReactor,\n ReactorFeatures,\n ReactorModule,\n SyncModule,\n} from \"./types.js\";\n\nexport class ReactorBuilder {\n private logger?: ILogger;\n private documentModels: DocumentModelModule<any>[] = [];\n private upgradeManifests: UpgradeManifest<readonly number[]>[] = [];\n private features: ReactorFeatures = { legacyStorageEnabled: false };\n private readModels: IReadModel[] = [];\n private executorManager: IJobExecutorManager | undefined;\n private executorConfig: JobExecutorConfig = {};\n private writeCacheConfig?: Partial<WriteCacheConfig>;\n private migrationStrategy: MigrationStrategy = \"auto\";\n private syncBuilder?: SyncBuilder;\n private eventBus?: IEventBus;\n private readModelCoordinator?: IReadModelCoordinator;\n private signatureVerifier?: SignatureVerificationHandler;\n private kyselyInstance?: Kysely<Database>;\n private signalHandlersEnabled = false;\n private queueInstance?: IQueue;\n private channelScheme?: ChannelScheme;\n private jwtHandler?: JwtHandler;\n private documentModelLoader?: IDocumentModelLoader;\n\n withLogger(logger: ILogger): this {\n this.logger = logger;\n return this;\n }\n\n withDocumentModels(models: DocumentModelModule<any>[]): this {\n this.documentModels = models;\n return this;\n }\n\n withUpgradeManifests(manifests: UpgradeManifest<readonly number[]>[]): this {\n this.upgradeManifests = manifests;\n return this;\n }\n\n withFeatures(features: ReactorFeatures): this {\n this.features = { ...this.features, ...features };\n return this;\n }\n\n withReadModel(readModel: IReadModel): this {\n this.readModels.push(readModel);\n return this;\n }\n\n withReadModelCoordinator(readModelCoordinator: IReadModelCoordinator): this {\n this.readModelCoordinator = readModelCoordinator;\n return this;\n }\n\n withExecutor(executor: IJobExecutorManager): this {\n this.executorManager = executor;\n return this;\n }\n\n withExecutorConfig(config: Partial<JobExecutorConfig>): this {\n this.executorConfig = { ...this.executorConfig, ...config };\n return this;\n }\n\n withWriteCacheConfig(config: Partial<WriteCacheConfig>): this {\n this.writeCacheConfig = config;\n return this;\n }\n\n withMigrationStrategy(strategy: MigrationStrategy): this {\n this.migrationStrategy = strategy;\n return this;\n }\n\n withSync(syncBuilder: SyncBuilder): this {\n this.syncBuilder = syncBuilder;\n return this;\n }\n\n withEventBus(eventBus: IEventBus): this {\n this.eventBus = eventBus;\n return this;\n }\n\n withSignatureVerifier(verifier: SignatureVerificationHandler): this {\n this.signatureVerifier = verifier;\n return this;\n }\n\n withKysely(kysely: Kysely<Database>): this {\n this.kyselyInstance = kysely;\n return this;\n }\n\n withQueue(queue: IQueue): this {\n this.queueInstance = queue;\n return this;\n }\n\n withChannelScheme(scheme: ChannelScheme): this {\n this.channelScheme = scheme;\n return this;\n }\n\n withJwtHandler(handler: JwtHandler): this {\n this.jwtHandler = handler;\n return this;\n }\n\n withDocumentModelLoader(loader: IDocumentModelLoader): this {\n this.documentModelLoader = loader;\n return this;\n }\n\n withSignalHandlers(): this {\n this.signalHandlersEnabled = true;\n return this;\n }\n\n async build(): Promise<IReactor> {\n const module = await this.buildModule();\n return module.reactor;\n }\n\n async buildModule(): Promise<ReactorModule> {\n if (!this.logger) {\n this.logger = new ConsoleLogger([\"reactor\"]);\n }\n\n const documentModelRegistry = new DocumentModelRegistry();\n if (this.upgradeManifests.length > 0) {\n documentModelRegistry.registerUpgradeManifests(...this.upgradeManifests);\n }\n if (this.documentModels.length > 0) {\n documentModelRegistry.registerModules(...this.documentModels);\n }\n\n const baseDatabase =\n this.kyselyInstance ??\n new Kysely<Database>({\n dialect: new PGliteDialect(new PGlite()),\n });\n\n if (this.migrationStrategy === \"auto\") {\n const result = await runMigrations(baseDatabase, REACTOR_SCHEMA);\n if (!result.success && result.error) {\n throw new Error(`Database migration failed: ${result.error.message}`);\n }\n }\n\n const database = baseDatabase.withSchema(REACTOR_SCHEMA);\n\n const operationStore = new KyselyOperationStore(\n database as unknown as Kysely<StorageDatabase>,\n );\n const keyframeStore = new KyselyKeyframeStore(\n database as unknown as Kysely<StorageDatabase>,\n );\n\n const eventBus = this.eventBus || new EventBus();\n const resolver = this.documentModelLoader\n ? new DocumentModelResolver(\n documentModelRegistry,\n this.documentModelLoader,\n )\n : new NullDocumentModelResolver(documentModelRegistry);\n const queue = this.queueInstance ?? new InMemoryQueue(eventBus, resolver);\n const jobTracker = new InMemoryJobTracker(eventBus);\n\n const cacheConfig: WriteCacheConfig = {\n maxDocuments: this.writeCacheConfig?.maxDocuments ?? 100,\n ringBufferSize: this.writeCacheConfig?.ringBufferSize ?? 10,\n keyframeInterval: this.writeCacheConfig?.keyframeInterval ?? 10,\n };\n\n const writeCache = new KyselyWriteCache(\n keyframeStore,\n operationStore,\n documentModelRegistry,\n cacheConfig,\n );\n await writeCache.startup();\n\n const operationIndex = new KyselyOperationIndex(\n database as unknown as Kysely<StorageDatabase>,\n );\n\n const documentMetaCache = new DocumentMetaCache(operationStore, {\n maxDocuments: 1000,\n });\n await documentMetaCache.startup();\n\n const collectionMembershipCache = new CollectionMembershipCache(\n operationIndex,\n );\n\n const executionScope: IExecutionScope = new KyselyExecutionScope(\n database as unknown as Kysely<StorageDatabase>,\n operationStore,\n operationIndex,\n keyframeStore,\n writeCache,\n documentMetaCache,\n collectionMembershipCache,\n );\n\n let executorManager = this.executorManager;\n if (!executorManager) {\n executorManager = new SimpleJobExecutorManager(\n () =>\n new SimpleJobExecutor(\n this.logger!,\n documentModelRegistry,\n operationStore,\n eventBus,\n writeCache,\n operationIndex,\n documentMetaCache,\n collectionMembershipCache,\n this.executorConfig,\n this.signatureVerifier,\n executionScope,\n ),\n eventBus,\n queue,\n jobTracker,\n this.logger,\n resolver,\n this.executorConfig.jobTimeoutMs,\n );\n }\n\n await executorManager.start(this.executorConfig.maxConcurrency ?? 1);\n\n const readModelInstances: IReadModel[] = Array.from(\n new Set([...this.readModels]),\n );\n\n const documentViewConsistencyTracker = new ConsistencyTracker();\n const documentView = new KyselyDocumentView(\n // @ts-expect-error - Database type is a superset that includes all required tables\n database,\n operationStore,\n operationIndex,\n writeCache,\n documentViewConsistencyTracker,\n );\n\n try {\n await documentView.init();\n } catch (error) {\n console.error(\"Error initializing document view\", error);\n }\n\n readModelInstances.push(documentView);\n\n const documentIndexerConsistencyTracker = new ConsistencyTracker();\n const documentIndexer = new KyselyDocumentIndexer(\n database as unknown as Kysely<IndexerDatabase>,\n operationIndex,\n writeCache,\n documentIndexerConsistencyTracker,\n );\n\n try {\n await documentIndexer.init();\n } catch (error) {\n console.error(\"Error initializing document indexer\", error);\n }\n\n readModelInstances.push(documentIndexer);\n\n const subscriptionManager = new ReactorSubscriptionManager(\n new DefaultSubscriptionErrorHandler(),\n );\n\n const subscriptionNotificationReadModel =\n new SubscriptionNotificationReadModel(subscriptionManager, documentView);\n\n const processorManagerConsistencyTracker = new ConsistencyTracker();\n const processorManager = new ProcessorManager(\n // @ts-expect-error - Database type is a superset that includes all required tables\n database,\n operationIndex,\n writeCache,\n processorManagerConsistencyTracker,\n this.logger!,\n );\n\n try {\n await processorManager.init();\n } catch (error) {\n console.error(\"Error initializing processor manager\", error);\n }\n\n const readModelCoordinator = this.readModelCoordinator\n ? this.readModelCoordinator\n : new ReadModelCoordinator(eventBus, readModelInstances, [\n subscriptionNotificationReadModel,\n processorManager,\n ]);\n\n const reactor = new Reactor(\n this.logger,\n documentModelRegistry,\n queue,\n jobTracker,\n readModelCoordinator,\n this.features,\n documentView,\n documentIndexer,\n operationStore,\n eventBus,\n executorManager,\n );\n\n let syncModule: SyncModule | undefined = undefined;\n if (this.channelScheme) {\n const factory =\n this.channelScheme === ChannelScheme.CONNECT\n ? new GqlRequestChannelFactory(this.logger, this.jwtHandler, queue)\n : new GqlResponseChannelFactory(this.logger);\n\n const syncBuilder = new SyncBuilder().withChannelFactory(factory);\n syncModule = syncBuilder.buildModule(\n reactor,\n this.logger,\n operationIndex,\n eventBus,\n database as unknown as Kysely<StorageDatabase>,\n );\n await syncModule.syncManager.startup();\n } else if (this.syncBuilder) {\n syncModule = this.syncBuilder.buildModule(\n reactor,\n this.logger,\n operationIndex,\n eventBus,\n database as unknown as Kysely<StorageDatabase>,\n );\n await syncModule.syncManager.startup();\n }\n\n const module: ReactorModule = {\n eventBus,\n documentModelRegistry,\n queue,\n jobTracker,\n executorManager,\n database,\n operationStore,\n keyframeStore,\n writeCache,\n operationIndex,\n documentView,\n documentViewConsistencyTracker,\n documentIndexer,\n documentIndexerConsistencyTracker,\n readModelCoordinator,\n subscriptionManager,\n processorManager,\n processorManagerConsistencyTracker,\n syncModule,\n reactor,\n };\n\n if (this.signalHandlersEnabled) {\n this.attachSignalHandlers(module);\n }\n\n return module;\n }\n\n private attachSignalHandlers(module: ReactorModule): void {\n if (\n typeof globalThis === \"undefined\" ||\n !(\"process\" in globalThis) ||\n typeof globalThis.process.on !== \"function\"\n ) {\n return;\n }\n\n const nodeProcess = globalThis.process;\n let shutdownInProgress = false;\n\n const handler = async (signal: string) => {\n if (shutdownInProgress) {\n this.logger!.warn(`Received ${signal} again, forcing exit`);\n nodeProcess.exit(1);\n }\n\n shutdownInProgress = true;\n this.logger!.info(`Received ${signal}, starting graceful shutdown...`);\n\n const status = module.reactor.kill();\n\n try {\n await status.completed;\n } catch (error) {\n this.logger!.error(\"Shutdown failed waiting for reactor:\", error);\n nodeProcess.exit(1);\n return;\n }\n\n try {\n await module.database.destroy();\n } catch (error) {\n this.logger!.error(\"Shutdown failed destroying database:\", error);\n nodeProcess.exit(1);\n return;\n }\n\n this.logger!.info(\"Shutdown complete\");\n nodeProcess.exit(0);\n };\n\n nodeProcess.on(\"SIGINT\", () => void handler(\"SIGINT\"));\n nodeProcess.on(\"SIGTERM\", () => void handler(\"SIGTERM\"));\n }\n}\n","import type { ISigner, Signature } from \"@powerhousedao/shared/document-model\";\n\n/**\n * A no-op signer that returns empty values for all methods.\n * Used when signing is not required.\n */\nexport class PassthroughSigner implements ISigner {\n publicKey = {} as unknown as CryptoKey;\n\n sign(): Promise<Uint8Array> {\n return Promise.resolve(new Uint8Array(0));\n }\n\n verify(): Promise<void> {\n return Promise.resolve();\n }\n\n signAction(): Promise<Signature> {\n return Promise.resolve([\"\", \"\", \"\", \"\", \"\"]);\n }\n}\n","import type { ISigner } from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport { ConsoleLogger } from \"document-model\";\nimport { ReactorClient } from \"../client/reactor-client.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport type { IDocumentModelLoader } from \"../registry/interfaces.js\";\nimport { JobAwaiter, type IJobAwaiter } from \"../shared/awaiter.js\";\nimport { PassthroughSigner } from \"../signer/passthrough-signer.js\";\nimport type {\n SignatureVerificationHandler,\n SignerConfig,\n} from \"../signer/types.js\";\nimport type { IDocumentIndexer, IDocumentView } from \"../storage/interfaces.js\";\nimport { DefaultSubscriptionErrorHandler } from \"../subs/default-error-handler.js\";\nimport { ReactorSubscriptionManager } from \"../subs/react-subscription-manager.js\";\nimport type { IReactorSubscriptionManager } from \"../subs/types.js\";\nimport type { ReactorBuilder } from \"./reactor-builder.js\";\nimport type { IReactor, ReactorClientModule, ReactorModule } from \"./types.js\";\n\n/**\n * Builder class for constructing ReactorClient instances with proper configuration\n */\nexport class ReactorClientBuilder {\n private logger?: ILogger;\n private reactorBuilder?: ReactorBuilder;\n private reactor?: IReactor;\n private eventBus?: IEventBus;\n private documentIndexer?: IDocumentIndexer;\n private documentView?: IDocumentView;\n private signer?: ISigner;\n private signatureVerifier?: SignatureVerificationHandler;\n private subscriptionManager?: IReactorSubscriptionManager;\n private jobAwaiter?: IJobAwaiter;\n private documentModelLoader?: IDocumentModelLoader;\n\n /**\n * Sets the logger for the ReactorClient.\n * @param logger - The logger to use.\n * @returns The ReactorClientBuilder instance.\n */\n public withLogger(logger: ILogger): this {\n this.logger = logger;\n return this;\n }\n\n /**\n * Either this or withReactor must be set.\n */\n public withReactorBuilder(reactorBuilder: ReactorBuilder): this {\n if (this.reactor) {\n throw new Error(\"Reactor is already set\");\n }\n\n this.reactorBuilder = reactorBuilder;\n return this;\n }\n\n /**\n * Either this or withReactorBuilder must be set.\n */\n public withReactor(\n reactor: IReactor,\n eventBus: IEventBus,\n documentIndexer: IDocumentIndexer,\n documentView: IDocumentView,\n ): this {\n if (this.reactorBuilder) {\n throw new Error(\"ReactorBuilder is already set\");\n }\n\n this.reactor = reactor;\n this.eventBus = eventBus;\n this.documentIndexer = documentIndexer;\n this.documentView = documentView;\n return this;\n }\n\n /**\n * Sets the signer configuration for signing and verifying actions.\n *\n * @param config - Either an ISigner for signing only, or a SignerConfig for both signing and verification\n */\n public withSigner(config: ISigner | SignerConfig): this {\n if (\"signer\" in config) {\n this.signer = config.signer;\n this.signatureVerifier = config.verifier;\n } else {\n this.signer = config;\n }\n return this;\n }\n\n public withSubscriptionManager(\n subscriptionManager: IReactorSubscriptionManager,\n ): this {\n this.subscriptionManager = subscriptionManager;\n return this;\n }\n\n public withJobAwaiter(jobAwaiter: IJobAwaiter): this {\n this.jobAwaiter = jobAwaiter;\n return this;\n }\n\n public withDocumentModelLoader(loader: IDocumentModelLoader): this {\n this.documentModelLoader = loader;\n return this;\n }\n\n public async build(): Promise<ReactorClient> {\n const module = await this.buildModule();\n return module.client;\n }\n\n public async buildModule(): Promise<ReactorClientModule> {\n if (!this.logger) {\n this.logger = new ConsoleLogger([\"reactor-client\"]);\n }\n\n let reactor: IReactor;\n let eventBus: IEventBus;\n let documentIndexer: IDocumentIndexer;\n let documentView: IDocumentView;\n let reactorModule: ReactorModule | undefined;\n\n if (this.reactorBuilder) {\n if (this.signatureVerifier) {\n this.reactorBuilder.withSignatureVerifier(this.signatureVerifier);\n }\n if (this.documentModelLoader) {\n this.reactorBuilder.withDocumentModelLoader(this.documentModelLoader);\n }\n reactorModule = await this.reactorBuilder.buildModule();\n reactor = reactorModule.reactor;\n eventBus = reactorModule.eventBus;\n documentIndexer = reactorModule.documentIndexer;\n documentView = reactorModule.documentView;\n } else if (\n this.reactor &&\n this.eventBus &&\n this.documentIndexer &&\n this.documentView\n ) {\n reactor = this.reactor;\n eventBus = this.eventBus;\n documentIndexer = this.documentIndexer;\n documentView = this.documentView;\n reactorModule = undefined;\n } else {\n throw new Error(\n \"Either ReactorBuilder or (Reactor + EventBus + DocumentIndexer + DocumentView) is required\",\n );\n }\n\n const signer = this.signer ?? new PassthroughSigner();\n\n const subscriptionManager =\n this.subscriptionManager ??\n reactorModule?.subscriptionManager ??\n new ReactorSubscriptionManager(new DefaultSubscriptionErrorHandler());\n\n const jobAwaiter =\n this.jobAwaiter ??\n new JobAwaiter(eventBus, (jobId, signal) =>\n reactor.getJobStatus(jobId, signal),\n );\n\n const client = new ReactorClient(\n this.logger,\n reactor,\n signer,\n subscriptionManager,\n jobAwaiter,\n documentIndexer,\n documentView,\n );\n\n return {\n client,\n reactor,\n eventBus,\n documentIndexer,\n documentView,\n signer,\n subscriptionManager,\n jobAwaiter,\n reactorModule,\n };\n }\n}\n","export interface ParsedDriveUrl {\n url: string;\n driveId: string;\n graphqlEndpoint: string;\n}\n\n/**\n * Parse a drive URL to extract drive ID and construct GraphQL endpoint.\n * e.g., \"http://localhost:4001/d/abc123\" -> { driveId: \"abc123\", graphqlEndpoint: \"http://localhost:4001/graphql/r\" }\n */\nexport function parseDriveUrl(url: string): ParsedDriveUrl {\n const parsedUrl = new URL(url);\n const driveId = url.split(\"/\").pop() ?? \"\";\n const graphqlEndpoint = `${parsedUrl.protocol}//${parsedUrl.host}/graphql/r`;\n return { url, driveId, graphqlEndpoint };\n}\n\n/**\n * Extract drive ID from a drive URL.\n */\nexport function driveIdFromUrl(url: string): string {\n return url.split(\"/\").pop() ?? \"\";\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { Operation } from \"@powerhousedao/shared/document-model\";\nimport type { Job } from \"../queue/types.js\";\n\n/**\n * Represents the result of a job execution\n */\nexport type JobResult = {\n /** The job that was executed */\n job: Job;\n\n /** Whether the job executed successfully */\n success: boolean;\n\n /** Error if the job failed */\n error?: Error;\n\n /** The operations generated from the actions (if successful) */\n operations?: Operation[];\n\n /**\n * Operations with context (includes ephemeral resultingState).\n * Used for emitting to IDocumentView via event bus.\n */\n operationsWithContext?: OperationWithContext[];\n\n /** Timestamp when the job execution completed */\n completedAt?: string;\n\n /** Duration of job execution in milliseconds */\n duration?: number;\n\n /** Any additional metadata from the execution */\n metadata?: Record<string, any>;\n};\n\n/**\n * Configuration options for the job executor\n */\nexport type JobExecutorConfig = {\n /** Maximum number of conflicting operations to skip when reshuffling. */\n maxSkipThreshold?: number;\n\n /** Maximum number of concurrent jobs to execute */\n maxConcurrency?: number;\n\n /** Maximum time in milliseconds a job can run before being considered timed out */\n jobTimeoutMs?: number;\n\n /** Base delay in milliseconds for exponential backoff retries */\n retryBaseDelayMs?: number;\n\n /** Maximum delay in milliseconds for exponential backoff retries */\n retryMaxDelayMs?: number;\n};\n\n/**\n * Event types for the job executor\n */\nexport const JobExecutorEventTypes = {\n JOB_STARTED: 20000,\n JOB_COMPLETED: 20001,\n JOB_FAILED: 20002,\n EXECUTOR_STARTED: 20003,\n EXECUTOR_STOPPED: 20004,\n} as const;\n\n/**\n * Event data for job execution events\n */\nexport type JobStartedEvent = {\n job: Job;\n startedAt: string;\n};\n\nexport type JobCompletedEvent = {\n job: Job;\n result: JobResult;\n};\n\nexport type JobFailedEvent = {\n job: Job;\n error: string;\n willRetry: boolean;\n retryCount: number;\n};\n\nexport type ExecutorStartedEvent = {\n config: JobExecutorConfig;\n startedAt: string;\n};\n\nexport type ExecutorStoppedEvent = {\n stoppedAt: string;\n graceful: boolean;\n};\n\n/**\n * Status information for the job executor manager\n */\nexport type ExecutorManagerStatus = {\n /** Whether the manager is currently running */\n isRunning: boolean;\n\n /** Number of executor instances managed */\n numExecutors: number;\n\n /** Number of jobs currently being processed */\n activeJobs: number;\n\n /** Total number of jobs processed since start */\n totalJobsProcessed: number;\n};\n","import type { PHDocument } from \"@powerhousedao/shared/document-model\";\nimport { hashDocumentStateForScope } from \"@powerhousedao/shared/document-model\";\nimport { KyselyWriteCache } from \"../cache/kysely-write-cache.js\";\nimport type { IWriteCache } from \"../cache/write/interfaces.js\";\nimport type { IDocumentModelRegistry } from \"../registry/interfaces.js\";\nimport type {\n IDocumentView,\n IKeyframeStore,\n IOperationStore,\n} from \"../storage/interfaces.js\";\nimport type {\n IDocumentIntegrityService,\n KeyframeValidationIssue,\n RebuildResult,\n SnapshotValidationIssue,\n ValidationResult,\n} from \"./types.js\";\n\nconst nullKeyframeStore: IKeyframeStore = {\n putKeyframe: () => Promise.resolve(),\n findNearestKeyframe: () => Promise.resolve(undefined),\n listKeyframes: () => Promise.resolve([]),\n deleteKeyframes: () => Promise.resolve(0),\n};\n\nexport class DocumentIntegrityService implements IDocumentIntegrityService {\n private readonly keyframeStore: IKeyframeStore;\n private readonly operationStore: IOperationStore;\n private readonly writeCache: IWriteCache;\n private readonly documentView: IDocumentView;\n private readonly documentModelRegistry: IDocumentModelRegistry;\n\n constructor(\n keyframeStore: IKeyframeStore,\n operationStore: IOperationStore,\n writeCache: IWriteCache,\n documentView: IDocumentView,\n documentModelRegistry: IDocumentModelRegistry,\n ) {\n this.keyframeStore = keyframeStore;\n this.operationStore = operationStore;\n this.writeCache = writeCache;\n this.documentView = documentView;\n this.documentModelRegistry = documentModelRegistry;\n }\n\n async validateDocument(\n documentId: string,\n branch = \"main\",\n signal?: AbortSignal,\n ): Promise<ValidationResult> {\n const keyframeIssues: KeyframeValidationIssue[] = [];\n const snapshotIssues: SnapshotValidationIssue[] = [];\n\n const replayCache = new KyselyWriteCache(\n nullKeyframeStore,\n this.operationStore,\n this.documentModelRegistry,\n {\n maxDocuments: 1,\n ringBufferSize: 1,\n keyframeInterval: Number.MAX_SAFE_INTEGER,\n },\n );\n\n const keyframes = await this.keyframeStore.listKeyframes(\n documentId,\n undefined,\n branch,\n signal,\n );\n\n for (const keyframe of keyframes) {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n replayCache.invalidate(documentId, keyframe.scope, branch);\n const replayedDoc = await replayCache.getState(\n documentId,\n keyframe.scope,\n branch,\n keyframe.revision,\n signal,\n );\n\n const kfHash = hashDocumentStateForScope(\n keyframe.document,\n keyframe.scope,\n );\n const replayHash = hashDocumentStateForScope(replayedDoc, keyframe.scope);\n\n if (kfHash !== replayHash) {\n keyframeIssues.push({\n scope: keyframe.scope,\n branch,\n revision: keyframe.revision,\n keyframeHash: kfHash,\n replayedHash: replayHash,\n });\n }\n }\n\n let currentDoc: PHDocument;\n try {\n currentDoc = await this.documentView.get(documentId);\n } catch {\n return {\n documentId,\n isConsistent: keyframeIssues.length === 0,\n keyframeIssues,\n snapshotIssues,\n };\n }\n\n const revisions = await this.operationStore.getRevisions(\n documentId,\n branch,\n signal,\n );\n const allScopes = Object.keys(revisions.revision);\n\n for (const scope of allScopes) {\n if (scope === \"document\") continue;\n\n replayCache.invalidate(documentId, scope, branch);\n\n let replayedDoc: PHDocument;\n try {\n replayedDoc = await replayCache.getState(\n documentId,\n scope,\n branch,\n undefined,\n signal,\n );\n } catch {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n continue;\n }\n\n const snapshotHash = hashDocumentStateForScope(currentDoc, scope);\n const replayHash = hashDocumentStateForScope(replayedDoc, scope);\n if (snapshotHash !== replayHash) {\n snapshotIssues.push({\n scope,\n branch,\n snapshotHash,\n replayedHash: replayHash,\n });\n }\n }\n\n return {\n documentId,\n isConsistent: keyframeIssues.length === 0 && snapshotIssues.length === 0,\n keyframeIssues,\n snapshotIssues,\n };\n }\n\n async rebuildKeyframes(\n documentId: string,\n branch = \"main\",\n signal?: AbortSignal,\n ): Promise<RebuildResult> {\n const deleted = await this.keyframeStore.deleteKeyframes(\n documentId,\n undefined,\n branch,\n signal,\n );\n\n return {\n documentId,\n keyframesDeleted: deleted,\n scopesInvalidated: 0,\n };\n }\n\n async rebuildSnapshots(\n documentId: string,\n branch = \"main\",\n signal?: AbortSignal,\n ): Promise<RebuildResult> {\n const scopes = await this.discoverScopes(documentId, branch, signal);\n\n for (const scope of scopes) {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n this.writeCache.invalidate(documentId, scope, branch);\n }\n\n return {\n documentId,\n keyframesDeleted: 0,\n scopesInvalidated: scopes.length,\n };\n }\n\n private async discoverScopes(\n documentId: string,\n branch: string,\n signal?: AbortSignal,\n ): Promise<string[]> {\n const revisions = await this.operationStore.getRevisions(\n documentId,\n branch,\n signal,\n );\n return Object.keys(revisions.revision);\n }\n}\n"],"mappings":"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"}
|