@enbox/dwn-sdk-js 0.0.2 → 0.0.4
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/README.md +52 -301
- package/dist/bundles/dwn.js +19 -21
- package/dist/esm/generated/precompiled-validators.js +2764 -1773
- package/dist/esm/generated/precompiled-validators.js.map +1 -1
- package/dist/esm/src/core/dwn-error.js +27 -3
- package/dist/esm/src/core/dwn-error.js.map +1 -1
- package/dist/esm/src/core/message.js.map +1 -1
- package/dist/esm/src/core/messages-grant-authorization.js +17 -6
- package/dist/esm/src/core/messages-grant-authorization.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization.js +245 -69
- package/dist/esm/src/core/protocol-authorization.js.map +1 -1
- package/dist/esm/src/core/resumable-task-manager.js +4 -4
- package/dist/esm/src/core/resumable-task-manager.js.map +1 -1
- package/dist/esm/src/dwn.js +10 -8
- package/dist/esm/src/dwn.js.map +1 -1
- package/dist/esm/src/enums/dwn-interface-method.js +4 -2
- package/dist/esm/src/enums/dwn-interface-method.js.map +1 -1
- package/dist/esm/src/event-stream/event-emitter-stream.js.map +1 -0
- package/dist/esm/src/handlers/messages-subscribe.js +1 -1
- package/dist/esm/src/handlers/messages-subscribe.js.map +1 -1
- package/dist/esm/src/handlers/messages-sync.js +116 -0
- package/dist/esm/src/handlers/messages-sync.js.map +1 -0
- package/dist/esm/src/handlers/protocols-configure.js +149 -16
- package/dist/esm/src/handlers/protocols-configure.js.map +1 -1
- package/dist/esm/src/handlers/protocols-query.js +2 -2
- package/dist/esm/src/handlers/protocols-query.js.map +1 -1
- package/dist/esm/src/handlers/records-count.js +143 -0
- package/dist/esm/src/handlers/records-count.js.map +1 -0
- package/dist/esm/src/handlers/records-query.js +4 -0
- package/dist/esm/src/handlers/records-query.js.map +1 -1
- package/dist/esm/src/handlers/records-read.js +4 -6
- package/dist/esm/src/handlers/records-read.js.map +1 -1
- package/dist/esm/src/handlers/records-write.js +17 -18
- package/dist/esm/src/handlers/records-write.js.map +1 -1
- package/dist/esm/src/index.js +9 -5
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/interfaces/messages-read.js +2 -7
- package/dist/esm/src/interfaces/messages-read.js.map +1 -1
- package/dist/esm/src/interfaces/messages-subscribe.js +1 -0
- package/dist/esm/src/interfaces/messages-subscribe.js.map +1 -1
- package/dist/esm/src/interfaces/{messages-query.js → messages-sync.js} +11 -12
- package/dist/esm/src/interfaces/messages-sync.js.map +1 -0
- package/dist/esm/src/interfaces/protocols-configure.js +153 -30
- package/dist/esm/src/interfaces/protocols-configure.js.map +1 -1
- package/dist/esm/src/interfaces/protocols-query.js +1 -0
- package/dist/esm/src/interfaces/protocols-query.js.map +1 -1
- package/dist/esm/src/interfaces/records-count.js +91 -0
- package/dist/esm/src/interfaces/records-count.js.map +1 -0
- package/dist/esm/src/interfaces/records-read.js +15 -1
- package/dist/esm/src/interfaces/records-read.js.map +1 -1
- package/dist/esm/src/interfaces/records-write.js +64 -15
- package/dist/esm/src/interfaces/records-write.js.map +1 -1
- package/dist/esm/src/jose/algorithms/signing/ed25519.js.map +1 -1
- package/dist/esm/src/jose/algorithms/signing/signature-algorithms.js.map +1 -1
- package/dist/esm/src/jose/jws/general/builder.js.map +1 -1
- package/dist/esm/src/jose/jws/general/verifier.js.map +1 -1
- package/dist/esm/src/protocols/permission-grant.js +30 -0
- package/dist/esm/src/protocols/permission-grant.js.map +1 -1
- package/dist/esm/src/protocols/permission-request.js +24 -0
- package/dist/esm/src/protocols/permission-request.js.map +1 -1
- package/dist/esm/src/protocols/permissions.js +1 -1
- package/dist/esm/src/protocols/permissions.js.map +1 -1
- package/dist/esm/src/schema-validator.js +0 -1
- package/dist/esm/src/schema-validator.js.map +1 -1
- package/dist/esm/src/smt/smt-store-level.js +125 -0
- package/dist/esm/src/smt/smt-store-level.js.map +1 -0
- package/dist/esm/src/smt/smt-store-memory.js +67 -0
- package/dist/esm/src/smt/smt-store-memory.js.map +1 -0
- package/dist/esm/src/smt/smt-utils.js +146 -0
- package/dist/esm/src/smt/smt-utils.js.map +1 -0
- package/dist/esm/src/smt/sparse-merkle-tree.js +622 -0
- package/dist/esm/src/smt/sparse-merkle-tree.js.map +1 -0
- package/dist/esm/src/state-index/state-index-level.js +228 -0
- package/dist/esm/src/state-index/state-index-level.js.map +1 -0
- package/dist/esm/src/store/data-store-level.js +6 -6
- package/dist/esm/src/store/data-store-level.js.map +1 -1
- package/dist/esm/src/store/index-level.js +375 -17
- package/dist/esm/src/store/index-level.js.map +1 -1
- package/dist/esm/src/store/message-store-level.js +56 -0
- package/dist/esm/src/store/message-store-level.js.map +1 -1
- package/dist/esm/src/store/storage-controller.js +19 -16
- package/dist/esm/src/store/storage-controller.js.map +1 -1
- package/dist/esm/src/types/encryption-types.js +2 -0
- package/dist/esm/src/types/encryption-types.js.map +1 -0
- package/dist/esm/src/types/message-types.js.map +1 -1
- package/dist/esm/src/types/protocols-types.js +0 -2
- package/dist/esm/src/types/protocols-types.js.map +1 -1
- package/dist/esm/src/types/records-types.js +2 -0
- package/dist/esm/src/types/records-types.js.map +1 -1
- package/dist/esm/src/types/smt-types.js +5 -0
- package/dist/esm/src/types/smt-types.js.map +1 -0
- package/dist/esm/src/types/state-index.js +2 -0
- package/dist/esm/src/types/state-index.js.map +1 -0
- package/dist/esm/src/utils/cid.js +2 -1
- package/dist/esm/src/utils/cid.js.map +1 -1
- package/dist/esm/src/utils/data-stream.js +84 -29
- package/dist/esm/src/utils/data-stream.js.map +1 -1
- package/dist/esm/src/utils/encryption.js +22 -31
- package/dist/esm/src/utils/encryption.js.map +1 -1
- package/dist/esm/src/utils/hd-key.js +3 -3
- package/dist/esm/src/utils/hd-key.js.map +1 -1
- package/dist/esm/src/utils/jws.js +4 -4
- package/dist/esm/src/utils/jws.js.map +1 -1
- package/dist/esm/src/utils/private-key-signer.js +4 -3
- package/dist/esm/src/utils/private-key-signer.js.map +1 -1
- package/dist/esm/src/utils/protocols.js +82 -9
- package/dist/esm/src/utils/protocols.js.map +1 -1
- package/dist/esm/src/utils/records.js +82 -26
- package/dist/esm/src/utils/records.js.map +1 -1
- package/dist/esm/src/utils/secp256k1.js +4 -3
- package/dist/esm/src/utils/secp256k1.js.map +1 -1
- package/dist/esm/src/utils/secp256r1.js +3 -2
- package/dist/esm/src/utils/secp256r1.js.map +1 -1
- package/dist/esm/src/utils/time.js +1 -1
- package/dist/esm/src/utils/url.js +1 -1
- package/dist/esm/src/utils/url.js.map +1 -1
- package/dist/esm/tests/core/auth.spec.js +2 -2
- package/dist/esm/tests/core/auth.spec.js.map +1 -1
- package/dist/esm/tests/core/message-reply.spec.js +3 -3
- package/dist/esm/tests/core/message-reply.spec.js.map +1 -1
- package/dist/esm/tests/core/message.spec.js +13 -13
- package/dist/esm/tests/core/message.spec.js.map +1 -1
- package/dist/esm/tests/core/protocol-authorization.spec.js +3 -3
- package/dist/esm/tests/core/protocol-authorization.spec.js.map +1 -1
- package/dist/esm/tests/dwn.spec.js +27 -37
- package/dist/esm/tests/dwn.spec.js.map +1 -1
- package/dist/esm/tests/{event-log → event-stream}/event-emitter-stream.spec.js +14 -15
- package/dist/esm/tests/event-stream/event-emitter-stream.spec.js.map +1 -0
- package/dist/esm/tests/{event-log → event-stream}/event-stream.spec.js +13 -15
- package/dist/esm/tests/event-stream/event-stream.spec.js.map +1 -0
- package/dist/esm/tests/features/author-delegated-grant.spec.js +281 -135
- package/dist/esm/tests/features/author-delegated-grant.spec.js.map +1 -1
- package/dist/esm/tests/features/owner-delegated-grant.spec.js +57 -59
- package/dist/esm/tests/features/owner-delegated-grant.spec.js.map +1 -1
- package/dist/esm/tests/features/owner-signature.spec.js +32 -34
- package/dist/esm/tests/features/owner-signature.spec.js.map +1 -1
- package/dist/esm/tests/features/permissions.spec.js +73 -95
- package/dist/esm/tests/features/permissions.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-composition.spec.js +1645 -0
- package/dist/esm/tests/features/protocol-composition.spec.js.map +1 -0
- package/dist/esm/tests/features/protocol-create-action.spec.js +25 -27
- package/dist/esm/tests/features/protocol-create-action.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-delete-action.spec.js +42 -44
- package/dist/esm/tests/features/protocol-delete-action.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-update-action.spec.js +53 -55
- package/dist/esm/tests/features/protocol-update-action.spec.js.map +1 -1
- package/dist/esm/tests/features/records-prune.spec.js +126 -100
- package/dist/esm/tests/features/records-prune.spec.js.map +1 -1
- package/dist/esm/tests/features/records-tags.spec.js +272 -272
- package/dist/esm/tests/features/records-tags.spec.js.map +1 -1
- package/dist/esm/tests/features/resumable-tasks.spec.js +35 -37
- package/dist/esm/tests/features/resumable-tasks.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-read.spec.js +112 -112
- package/dist/esm/tests/handlers/messages-read.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-subscribe.spec.js +78 -76
- package/dist/esm/tests/handlers/messages-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-sync.spec.js +528 -0
- package/dist/esm/tests/handlers/messages-sync.spec.js.map +1 -0
- package/dist/esm/tests/handlers/protocols-configure.spec.js +545 -152
- package/dist/esm/tests/handlers/protocols-configure.spec.js.map +1 -1
- package/dist/esm/tests/handlers/protocols-query.spec.js +70 -72
- package/dist/esm/tests/handlers/protocols-query.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-count.spec.js +313 -0
- package/dist/esm/tests/handlers/records-count.spec.js.map +1 -0
- package/dist/esm/tests/handlers/records-delete.spec.js +106 -109
- package/dist/esm/tests/handlers/records-delete.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-query.spec.js +863 -463
- package/dist/esm/tests/handlers/records-query.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-read.spec.js +439 -209
- package/dist/esm/tests/handlers/records-read.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-subscribe.spec.js +292 -97
- package/dist/esm/tests/handlers/records-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-write.spec.js +481 -483
- package/dist/esm/tests/handlers/records-write.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/messages-get.spec.js +31 -11
- package/dist/esm/tests/interfaces/messages-get.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/messages-subscribe.spec.js +5 -5
- package/dist/esm/tests/interfaces/messages-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/protocols-configure.spec.js +64 -134
- package/dist/esm/tests/interfaces/protocols-configure.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/protocols-query.spec.js +4 -6
- package/dist/esm/tests/interfaces/protocols-query.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-delete.spec.js +3 -5
- package/dist/esm/tests/interfaces/records-delete.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-query.spec.js +9 -11
- package/dist/esm/tests/interfaces/records-query.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-read.spec.js +76 -7
- package/dist/esm/tests/interfaces/records-read.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-subscribe.spec.js +7 -9
- package/dist/esm/tests/interfaces/records-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-write.spec.js +244 -48
- package/dist/esm/tests/interfaces/records-write.spec.js.map +1 -1
- package/dist/esm/tests/jose/jws/general.spec.js +15 -18
- package/dist/esm/tests/jose/jws/general.spec.js.map +1 -1
- package/dist/esm/tests/protocols/permission-grant.spec.js +114 -0
- package/dist/esm/tests/protocols/permission-grant.spec.js.map +1 -0
- package/dist/esm/tests/protocols/permission-request.spec.js +43 -7
- package/dist/esm/tests/protocols/permission-request.spec.js.map +1 -1
- package/dist/esm/tests/protocols/permissions.spec.js +9 -11
- package/dist/esm/tests/protocols/permissions.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/aggregator.spec.js +90 -92
- package/dist/esm/tests/scenarios/aggregator.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/deleted-record.spec.js +17 -19
- package/dist/esm/tests/scenarios/deleted-record.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/end-to-end-tests.spec.js +27 -29
- package/dist/esm/tests/scenarios/end-to-end-tests.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/nested-roles.spec.js +37 -39
- package/dist/esm/tests/scenarios/nested-roles.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/subscriptions.spec.js +163 -163
- package/dist/esm/tests/scenarios/subscriptions.spec.js.map +1 -1
- package/dist/esm/tests/smt/smt-store-level.spec.js +143 -0
- package/dist/esm/tests/smt/smt-store-level.spec.js.map +1 -0
- package/dist/esm/tests/smt/sparse-merkle-tree.spec.js +741 -0
- package/dist/esm/tests/smt/sparse-merkle-tree.spec.js.map +1 -0
- package/dist/esm/tests/state-index/state-index-level.spec.js +254 -0
- package/dist/esm/tests/state-index/state-index-level.spec.js.map +1 -0
- package/dist/esm/tests/store/blockstore-level.spec.js +136 -0
- package/dist/esm/tests/store/blockstore-level.spec.js.map +1 -0
- package/dist/esm/tests/store/blockstore-mock.spec.js +29 -28
- package/dist/esm/tests/store/blockstore-mock.spec.js.map +1 -1
- package/dist/esm/tests/store/data-store-level.spec.js +23 -25
- package/dist/esm/tests/store/data-store-level.spec.js.map +1 -1
- package/dist/esm/tests/store/index-level.spec.js +544 -194
- package/dist/esm/tests/store/index-level.spec.js.map +1 -1
- package/dist/esm/tests/store/message-store-level.spec.js +4 -4
- package/dist/esm/tests/store/message-store-level.spec.js.map +1 -1
- package/dist/esm/tests/store/message-store.spec.js +147 -73
- package/dist/esm/tests/store/message-store.spec.js.map +1 -1
- package/dist/esm/tests/store-dependent-tests.spec.js +1 -0
- package/dist/esm/tests/store-dependent-tests.spec.js.map +1 -1
- package/dist/esm/tests/test-stores.js +5 -5
- package/dist/esm/tests/test-stores.js.map +1 -1
- package/dist/esm/tests/test-suite.js +9 -8
- package/dist/esm/tests/test-suite.js.map +1 -1
- package/dist/esm/tests/utils/cid.spec.js +8 -11
- package/dist/esm/tests/utils/cid.spec.js.map +1 -1
- package/dist/esm/tests/utils/data-stream.spec.js +167 -13
- package/dist/esm/tests/utils/data-stream.spec.js.map +1 -1
- package/dist/esm/tests/utils/encryption-callbacks.spec.js +233 -0
- package/dist/esm/tests/utils/encryption-callbacks.spec.js.map +1 -0
- package/dist/esm/tests/utils/encryption.spec.js +34 -85
- package/dist/esm/tests/utils/encryption.spec.js.map +1 -1
- package/dist/esm/tests/utils/filters.spec.js +67 -69
- package/dist/esm/tests/utils/filters.spec.js.map +1 -1
- package/dist/esm/tests/utils/hd-key.spec.js +3 -3
- package/dist/esm/tests/utils/hd-key.spec.js.map +1 -1
- package/dist/esm/tests/utils/jws.spec.js +54 -3
- package/dist/esm/tests/utils/jws.spec.js.map +1 -1
- package/dist/esm/tests/utils/memory-cache.spec.js +6 -9
- package/dist/esm/tests/utils/memory-cache.spec.js.map +1 -1
- package/dist/esm/tests/utils/messages.spec.js +63 -29
- package/dist/esm/tests/utils/messages.spec.js.map +1 -1
- package/dist/esm/tests/utils/object.spec.js +3 -3
- package/dist/esm/tests/utils/object.spec.js.map +1 -1
- package/dist/esm/tests/utils/poller.js +1 -1
- package/dist/esm/tests/utils/poller.js.map +1 -1
- package/dist/esm/tests/utils/private-key-signer.spec.js +6 -6
- package/dist/esm/tests/utils/private-key-signer.spec.js.map +1 -1
- package/dist/esm/tests/utils/records.spec.js +37 -5
- package/dist/esm/tests/utils/records.spec.js.map +1 -1
- package/dist/esm/tests/utils/secp256k1.spec.js +7 -7
- package/dist/esm/tests/utils/secp256k1.spec.js.map +1 -1
- package/dist/esm/tests/utils/secp256r1.spec.js +7 -7
- package/dist/esm/tests/utils/secp256r1.spec.js.map +1 -1
- package/dist/esm/tests/utils/test-data-generator.js +47 -28
- package/dist/esm/tests/utils/test-data-generator.js.map +1 -1
- package/dist/esm/tests/utils/time.spec.js +7 -7
- package/dist/esm/tests/utils/time.spec.js.map +1 -1
- package/dist/esm/tests/utils/url.spec.js +25 -27
- package/dist/esm/tests/utils/url.spec.js.map +1 -1
- package/dist/esm/tests/validation/json-schemas/definitions.spec.js +4 -4
- package/dist/esm/tests/validation/json-schemas/definitions.spec.js.map +1 -1
- package/dist/esm/tests/validation/json-schemas/jwk/general-jwk.spec.js +15 -3
- package/dist/esm/tests/validation/json-schemas/jwk/general-jwk.spec.js.map +1 -1
- package/dist/esm/tests/validation/json-schemas/jwk/public-jwk.spec.js +8 -8
- package/dist/esm/tests/validation/json-schemas/jwk/public-jwk.spec.js.map +1 -1
- package/dist/esm/tests/validation/json-schemas/jwk-verification-method.spec.js +8 -18
- package/dist/esm/tests/validation/json-schemas/jwk-verification-method.spec.js.map +1 -1
- package/dist/esm/tests/validation/json-schemas/protocols/protocols-configure.spec.js +3 -3
- package/dist/esm/tests/validation/json-schemas/protocols/protocols-configure.spec.js.map +1 -1
- package/dist/esm/tests/validation/json-schemas/records/records-query.spec.js +9 -9
- package/dist/esm/tests/validation/json-schemas/records/records-query.spec.js.map +1 -1
- package/dist/esm/tests/validation/json-schemas/records/records-read.spec.js +106 -0
- package/dist/esm/tests/validation/json-schemas/records/records-read.spec.js.map +1 -0
- package/dist/esm/tests/validation/json-schemas/records/records-write.spec.js +18 -18
- package/dist/esm/tests/validation/json-schemas/records/records-write.spec.js.map +1 -1
- package/dist/esm/tests/vectors/protocol-definitions/email.json +1 -1
- package/dist/esm/tests/vectors/protocol-definitions/friend-role.json +2 -4
- package/dist/esm/tests/vectors/protocol-definitions/slack.json +2 -6
- package/dist/esm/tests/vectors/protocol-definitions/thread-role.json +2 -6
- package/dist/types/generated/precompiled-validators.d.ts +82 -64
- package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
- package/dist/types/src/core/dwn-error.d.ts +27 -3
- package/dist/types/src/core/dwn-error.d.ts.map +1 -1
- package/dist/types/src/core/message-reply.d.ts +1 -1
- package/dist/types/src/core/message.d.ts +3 -3
- package/dist/types/src/core/message.d.ts.map +1 -1
- package/dist/types/src/core/messages-grant-authorization.d.ts +4 -4
- package/dist/types/src/core/messages-grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/protocol-authorization.d.ts +43 -2
- package/dist/types/src/core/protocol-authorization.d.ts.map +1 -1
- package/dist/types/src/core/records-grant-authorization.d.ts +2 -2
- package/dist/types/src/core/records-grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/resumable-task-manager.d.ts +1 -0
- package/dist/types/src/core/resumable-task-manager.d.ts.map +1 -1
- package/dist/types/src/dwn.d.ts +8 -8
- package/dist/types/src/dwn.d.ts.map +1 -1
- package/dist/types/src/enums/dwn-interface-method.d.ts +5 -3
- package/dist/types/src/enums/dwn-interface-method.d.ts.map +1 -1
- package/dist/types/src/event-stream/event-emitter-stream.d.ts.map +1 -0
- package/dist/types/src/handlers/messages-sync.d.ts +21 -0
- package/dist/types/src/handlers/messages-sync.d.ts.map +1 -0
- package/dist/types/src/handlers/protocols-configure.d.ts +24 -4
- package/dist/types/src/handlers/protocols-configure.d.ts.map +1 -1
- package/dist/types/src/handlers/protocols-query.d.ts.map +1 -1
- package/dist/types/src/handlers/records-count.d.ts +43 -0
- package/dist/types/src/handlers/records-count.d.ts.map +1 -0
- package/dist/types/src/handlers/records-query.d.ts.map +1 -1
- package/dist/types/src/handlers/records-read.d.ts.map +1 -1
- package/dist/types/src/handlers/records-write.d.ts +5 -5
- package/dist/types/src/handlers/records-write.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +72 -37
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/interfaces/messages-read.d.ts +2 -2
- package/dist/types/src/interfaces/messages-read.d.ts.map +1 -1
- package/dist/types/src/interfaces/messages-subscribe.d.ts +2 -2
- package/dist/types/src/interfaces/messages-subscribe.d.ts.map +1 -1
- package/dist/types/src/interfaces/messages-sync.d.ts +16 -0
- package/dist/types/src/interfaces/messages-sync.d.ts.map +1 -0
- package/dist/types/src/interfaces/protocols-configure.d.ts +22 -2
- package/dist/types/src/interfaces/protocols-configure.d.ts.map +1 -1
- package/dist/types/src/interfaces/protocols-query.d.ts +2 -2
- package/dist/types/src/interfaces/protocols-query.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-count.d.ts +27 -0
- package/dist/types/src/interfaces/records-count.d.ts.map +1 -0
- package/dist/types/src/interfaces/records-delete.d.ts +2 -2
- package/dist/types/src/interfaces/records-delete.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-query.d.ts +2 -2
- package/dist/types/src/interfaces/records-query.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-read.d.ts +4 -2
- package/dist/types/src/interfaces/records-read.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-subscribe.d.ts +2 -2
- package/dist/types/src/interfaces/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-write.d.ts +37 -15
- package/dist/types/src/interfaces/records-write.d.ts.map +1 -1
- package/dist/types/src/jose/algorithms/signing/ed25519.d.ts.map +1 -1
- package/dist/types/src/jose/algorithms/signing/signature-algorithms.d.ts +5 -1
- package/dist/types/src/jose/algorithms/signing/signature-algorithms.d.ts.map +1 -1
- package/dist/types/src/jose/jws/general/builder.d.ts +3 -3
- package/dist/types/src/jose/jws/general/builder.d.ts.map +1 -1
- package/dist/types/src/protocols/permission-grant.d.ts +11 -0
- package/dist/types/src/protocols/permission-grant.d.ts.map +1 -1
- package/dist/types/src/protocols/permission-request.d.ts +11 -0
- package/dist/types/src/protocols/permission-request.d.ts.map +1 -1
- package/dist/types/src/protocols/permissions.d.ts +4 -4
- package/dist/types/src/protocols/permissions.d.ts.map +1 -1
- package/dist/types/src/schema-validator.d.ts +1 -1
- package/dist/types/src/schema-validator.d.ts.map +1 -1
- package/dist/types/src/smt/smt-store-level.d.ts +32 -0
- package/dist/types/src/smt/smt-store-level.d.ts.map +1 -0
- package/dist/types/src/smt/smt-store-memory.d.ts +22 -0
- package/dist/types/src/smt/smt-store-memory.d.ts.map +1 -0
- package/dist/types/src/smt/smt-utils.d.ts +58 -0
- package/dist/types/src/smt/smt-utils.d.ts.map +1 -0
- package/dist/types/src/smt/sparse-merkle-tree.d.ts +124 -0
- package/dist/types/src/smt/sparse-merkle-tree.d.ts.map +1 -0
- package/dist/types/src/state-index/state-index-level.d.ts +83 -0
- package/dist/types/src/state-index/state-index-level.d.ts.map +1 -0
- package/dist/types/src/store/data-store-level.d.ts +1 -2
- package/dist/types/src/store/data-store-level.d.ts.map +1 -1
- package/dist/types/src/store/index-level.d.ts +98 -2
- package/dist/types/src/store/index-level.d.ts.map +1 -1
- package/dist/types/src/store/level-wrapper.d.ts.map +1 -1
- package/dist/types/src/store/message-store-level.d.ts +5 -0
- package/dist/types/src/store/message-store-level.d.ts.map +1 -1
- package/dist/types/src/store/storage-controller.d.ts +7 -7
- package/dist/types/src/store/storage-controller.d.ts.map +1 -1
- package/dist/types/src/types/data-store.d.ts +2 -3
- package/dist/types/src/types/data-store.d.ts.map +1 -1
- package/dist/types/src/types/encryption-types.d.ts +48 -0
- package/dist/types/src/types/encryption-types.d.ts.map +1 -0
- package/dist/types/src/types/jose-types.d.ts +9 -40
- package/dist/types/src/types/jose-types.d.ts.map +1 -1
- package/dist/types/src/types/message-store.d.ts +5 -0
- package/dist/types/src/types/message-store.d.ts.map +1 -1
- package/dist/types/src/types/message-types.d.ts +19 -0
- package/dist/types/src/types/message-types.d.ts.map +1 -1
- package/dist/types/src/types/messages-types.d.ts +16 -11
- package/dist/types/src/types/messages-types.d.ts.map +1 -1
- package/dist/types/src/types/method-handler.d.ts +1 -2
- package/dist/types/src/types/method-handler.d.ts.map +1 -1
- package/dist/types/src/types/permission-types.d.ts +2 -2
- package/dist/types/src/types/permission-types.d.ts.map +1 -1
- package/dist/types/src/types/protocols-types.d.ts +49 -5
- package/dist/types/src/types/protocols-types.d.ts.map +1 -1
- package/dist/types/src/types/records-types.d.ts +23 -7
- package/dist/types/src/types/records-types.d.ts.map +1 -1
- package/dist/types/src/types/signer.d.ts +1 -1
- package/dist/types/src/types/signer.d.ts.map +1 -1
- package/dist/types/src/types/smt-types.d.ts +81 -0
- package/dist/types/src/types/smt-types.d.ts.map +1 -0
- package/dist/types/src/types/state-index.d.ts +90 -0
- package/dist/types/src/types/state-index.d.ts.map +1 -0
- package/dist/types/src/utils/cid.d.ts +1 -2
- package/dist/types/src/utils/cid.d.ts.map +1 -1
- package/dist/types/src/utils/data-stream.d.ts +14 -7
- package/dist/types/src/utils/data-stream.d.ts.map +1 -1
- package/dist/types/src/utils/encryption.d.ts +2 -3
- package/dist/types/src/utils/encryption.d.ts.map +1 -1
- package/dist/types/src/utils/hd-key.d.ts +4 -4
- package/dist/types/src/utils/hd-key.d.ts.map +1 -1
- package/dist/types/src/utils/jws.d.ts +7 -7
- package/dist/types/src/utils/jws.d.ts.map +1 -1
- package/dist/types/src/utils/private-key-signer.d.ts +4 -4
- package/dist/types/src/utils/private-key-signer.d.ts.map +1 -1
- package/dist/types/src/utils/protocols.d.ts +46 -3
- package/dist/types/src/utils/protocols.d.ts.map +1 -1
- package/dist/types/src/utils/records.d.ts +33 -6
- package/dist/types/src/utils/records.d.ts.map +1 -1
- package/dist/types/src/utils/secp256k1.d.ts +11 -11
- package/dist/types/src/utils/secp256k1.d.ts.map +1 -1
- package/dist/types/src/utils/secp256r1.d.ts +8 -8
- package/dist/types/src/utils/secp256r1.d.ts.map +1 -1
- package/dist/types/src/utils/time.d.ts +1 -1
- package/dist/types/tests/dwn.spec.d.ts.map +1 -1
- package/dist/types/tests/event-stream/event-emitter-stream.spec.d.ts.map +1 -0
- package/dist/types/tests/event-stream/event-stream.spec.d.ts.map +1 -0
- package/dist/types/tests/features/author-delegated-grant.spec.d.ts.map +1 -1
- package/dist/types/tests/features/owner-delegated-grant.spec.d.ts.map +1 -1
- package/dist/types/tests/features/owner-signature.spec.d.ts.map +1 -1
- package/dist/types/tests/features/permissions.spec.d.ts.map +1 -1
- package/dist/types/tests/features/protocol-composition.spec.d.ts +5 -0
- package/dist/types/tests/features/protocol-composition.spec.d.ts.map +1 -0
- package/dist/types/tests/features/protocol-create-action.spec.d.ts.map +1 -1
- package/dist/types/tests/features/protocol-delete-action.spec.d.ts.map +1 -1
- package/dist/types/tests/features/protocol-update-action.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-prune.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-tags.spec.d.ts.map +1 -1
- package/dist/types/tests/features/resumable-tasks.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-read.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-subscribe.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-sync.spec.d.ts +2 -0
- package/dist/types/tests/handlers/messages-sync.spec.d.ts.map +1 -0
- package/dist/types/tests/handlers/protocols-configure.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/protocols-query.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-count.spec.d.ts +2 -0
- package/dist/types/tests/handlers/records-count.spec.d.ts.map +1 -0
- package/dist/types/tests/handlers/records-delete.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-query.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-read.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-subscribe.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-write.spec.d.ts.map +1 -1
- package/dist/types/tests/protocols/permission-grant.spec.d.ts +2 -0
- package/dist/types/tests/protocols/permission-grant.spec.d.ts.map +1 -0
- package/dist/types/tests/scenarios/deleted-record.spec.d.ts.map +1 -1
- package/dist/types/tests/scenarios/end-to-end-tests.spec.d.ts.map +1 -1
- package/dist/types/tests/scenarios/nested-roles.spec.d.ts.map +1 -1
- package/dist/types/tests/smt/smt-store-level.spec.d.ts +2 -0
- package/dist/types/tests/smt/smt-store-level.spec.d.ts.map +1 -0
- package/dist/types/tests/smt/sparse-merkle-tree.spec.d.ts +2 -0
- package/dist/types/tests/smt/sparse-merkle-tree.spec.d.ts.map +1 -0
- package/dist/types/tests/state-index/state-index-level.spec.d.ts +2 -0
- package/dist/types/tests/state-index/state-index-level.spec.d.ts.map +1 -0
- package/dist/types/tests/store/blockstore-level.spec.d.ts +2 -0
- package/dist/types/tests/store/blockstore-level.spec.d.ts.map +1 -0
- package/dist/types/tests/store/message-store.spec.d.ts.map +1 -1
- package/dist/types/tests/test-stores.d.ts +4 -4
- package/dist/types/tests/test-stores.d.ts.map +1 -1
- package/dist/types/tests/test-suite.d.ts +2 -2
- package/dist/types/tests/test-suite.d.ts.map +1 -1
- package/dist/types/tests/utils/encryption-callbacks.spec.d.ts +2 -0
- package/dist/types/tests/utils/encryption-callbacks.spec.d.ts.map +1 -0
- package/dist/types/tests/utils/test-data-generator.d.ts +31 -28
- package/dist/types/tests/utils/test-data-generator.d.ts.map +1 -1
- package/dist/types/tests/validation/json-schemas/records/records-read.spec.d.ts +2 -0
- package/dist/types/tests/validation/json-schemas/records/records-read.spec.d.ts.map +1 -0
- package/package.json +27 -46
- package/src/core/dwn-error.ts +27 -3
- package/src/core/message-reply.ts +1 -1
- package/src/core/message.ts +5 -5
- package/src/core/messages-grant-authorization.ts +22 -8
- package/src/core/protocol-authorization.ts +345 -68
- package/src/core/records-grant-authorization.ts +2 -2
- package/src/core/resumable-task-manager.ts +4 -5
- package/src/dwn.ts +25 -20
- package/src/enums/dwn-interface-method.ts +5 -3
- package/src/handlers/messages-subscribe.ts +1 -1
- package/src/handlers/messages-sync.ts +129 -0
- package/src/handlers/protocols-configure.ts +195 -17
- package/src/handlers/protocols-query.ts +7 -5
- package/src/handlers/records-count.ts +184 -0
- package/src/handlers/records-query.ts +4 -0
- package/src/handlers/records-read.ts +4 -8
- package/src/handlers/records-write.ts +20 -21
- package/src/index.ts +74 -37
- package/src/interfaces/messages-read.ts +6 -5
- package/src/interfaces/messages-subscribe.ts +7 -6
- package/src/interfaces/messages-sync.ts +59 -0
- package/src/interfaces/protocols-configure.ts +211 -33
- package/src/interfaces/protocols-query.ts +7 -6
- package/src/interfaces/records-count.ts +106 -0
- package/src/interfaces/records-delete.ts +2 -2
- package/src/interfaces/records-query.ts +2 -2
- package/src/interfaces/records-read.ts +26 -3
- package/src/interfaces/records-subscribe.ts +2 -2
- package/src/interfaces/records-write.ts +115 -46
- package/src/jose/algorithms/signing/ed25519.ts +13 -12
- package/src/jose/algorithms/signing/signature-algorithms.ts +6 -1
- package/src/jose/jws/general/builder.ts +3 -3
- package/src/jose/jws/general/verifier.ts +3 -3
- package/src/protocols/permission-grant.ts +51 -0
- package/src/protocols/permission-request.ts +37 -0
- package/src/protocols/permissions.ts +5 -5
- package/src/schema-validator.ts +11 -3
- package/src/smt/smt-store-level.ts +143 -0
- package/src/smt/smt-store-memory.ts +53 -0
- package/src/smt/smt-utils.ts +149 -0
- package/src/smt/sparse-merkle-tree.ts +698 -0
- package/src/state-index/state-index-level.ts +241 -0
- package/src/store/data-store-level.ts +8 -7
- package/src/store/index-level.ts +415 -19
- package/src/store/level-wrapper.ts +1 -1
- package/src/store/message-store-level.ts +62 -0
- package/src/store/storage-controller.ts +21 -19
- package/src/types/data-store.ts +2 -4
- package/src/types/encryption-types.ts +52 -0
- package/src/types/jose-types.ts +10 -42
- package/src/types/message-store.ts +11 -0
- package/src/types/message-types.ts +21 -0
- package/src/types/messages-types.ts +21 -15
- package/src/types/method-handler.ts +1 -2
- package/src/types/permission-types.ts +2 -2
- package/src/types/protocols-types.ts +55 -6
- package/src/types/records-types.ts +26 -7
- package/src/types/signer.ts +1 -1
- package/src/types/smt-types.ts +95 -0
- package/src/types/state-index.ts +100 -0
- package/src/utils/cid.ts +3 -4
- package/src/utils/data-stream.ts +75 -38
- package/src/utils/encryption.ts +24 -39
- package/src/utils/hd-key.ts +6 -6
- package/src/utils/jws.ts +9 -9
- package/src/utils/private-key-signer.ts +9 -8
- package/src/utils/protocols.ts +132 -6
- package/src/utils/records.ts +118 -29
- package/src/utils/secp256k1.ts +23 -21
- package/src/utils/secp256r1.ts +17 -15
- package/src/utils/time.ts +1 -1
- package/src/utils/url.ts +1 -1
- package/dist/cjs/index.js +0 -36749
- package/dist/cjs/package.json +0 -1
- package/dist/esm/src/event-log/event-emitter-stream.js.map +0 -1
- package/dist/esm/src/event-log/event-log-level.js +0 -63
- package/dist/esm/src/event-log/event-log-level.js.map +0 -1
- package/dist/esm/src/handlers/messages-query.js +0 -71
- package/dist/esm/src/handlers/messages-query.js.map +0 -1
- package/dist/esm/src/interfaces/messages-query.js.map +0 -1
- package/dist/esm/src/types/event-log.js +0 -2
- package/dist/esm/src/types/event-log.js.map +0 -1
- package/dist/esm/tests/event-log/event-emitter-stream.spec.js.map +0 -1
- package/dist/esm/tests/event-log/event-log-level.spec.js +0 -44
- package/dist/esm/tests/event-log/event-log-level.spec.js.map +0 -1
- package/dist/esm/tests/event-log/event-log.spec.js +0 -236
- package/dist/esm/tests/event-log/event-log.spec.js.map +0 -1
- package/dist/esm/tests/event-log/event-stream.spec.js.map +0 -1
- package/dist/esm/tests/handlers/messages-query.spec.js +0 -349
- package/dist/esm/tests/handlers/messages-query.spec.js.map +0 -1
- package/dist/esm/tests/interfaces/messagess-query.spec.js +0 -127
- package/dist/esm/tests/interfaces/messagess-query.spec.js.map +0 -1
- package/dist/esm/tests/scenarios/messages-query.spec.js +0 -395
- package/dist/esm/tests/scenarios/messages-query.spec.js.map +0 -1
- package/dist/types/src/event-log/event-emitter-stream.d.ts.map +0 -1
- package/dist/types/src/event-log/event-log-level.d.ts +0 -35
- package/dist/types/src/event-log/event-log-level.d.ts.map +0 -1
- package/dist/types/src/handlers/messages-query.d.ts +0 -17
- package/dist/types/src/handlers/messages-query.d.ts.map +0 -1
- package/dist/types/src/interfaces/messages-query.d.ts +0 -16
- package/dist/types/src/interfaces/messages-query.d.ts.map +0 -1
- package/dist/types/src/types/event-log.d.ts +0 -52
- package/dist/types/src/types/event-log.d.ts.map +0 -1
- package/dist/types/tests/event-log/event-emitter-stream.spec.d.ts.map +0 -1
- package/dist/types/tests/event-log/event-log-level.spec.d.ts +0 -2
- package/dist/types/tests/event-log/event-log-level.spec.d.ts.map +0 -1
- package/dist/types/tests/event-log/event-log.spec.d.ts +0 -2
- package/dist/types/tests/event-log/event-log.spec.d.ts.map +0 -1
- package/dist/types/tests/event-log/event-stream.spec.d.ts.map +0 -1
- package/dist/types/tests/handlers/messages-query.spec.d.ts +0 -2
- package/dist/types/tests/handlers/messages-query.spec.d.ts.map +0 -1
- package/dist/types/tests/interfaces/messagess-query.spec.d.ts +0 -2
- package/dist/types/tests/interfaces/messagess-query.spec.d.ts.map +0 -1
- package/dist/types/tests/scenarios/messages-query.spec.d.ts +0 -2
- package/dist/types/tests/scenarios/messages-query.spec.d.ts.map +0 -1
- package/src/event-log/event-log-level.ts +0 -72
- package/src/handlers/messages-query.ts +0 -67
- package/src/interfaces/messages-query.ts +0 -60
- package/src/types/event-log.ts +0 -52
- /package/dist/esm/src/{event-log → event-stream}/event-emitter-stream.js +0 -0
- /package/dist/types/src/{event-log → event-stream}/event-emitter-stream.d.ts +0 -0
- /package/dist/types/tests/{event-log → event-stream}/event-emitter-stream.spec.d.ts +0 -0
- /package/dist/types/tests/{event-log → event-stream}/event-stream.spec.d.ts +0 -0
- /package/src/{event-log → event-stream}/event-emitter-stream.ts +0 -0
package/src/store/index-level.ts
CHANGED
|
@@ -10,13 +10,35 @@ import { FilterSelector, FilterUtility } from '../utils/filter.js';
|
|
|
10
10
|
|
|
11
11
|
export type IndexLevelConfig = {
|
|
12
12
|
location: string,
|
|
13
|
-
createLevelDatabase?: typeof createLevelDatabase
|
|
13
|
+
createLevelDatabase?: typeof createLevelDatabase,
|
|
14
|
+
compoundIndexes?: CompoundIndexDefinition[],
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
export type IndexedItem = { messageCid: string, indexes: KeyValues };
|
|
17
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Defines a compound index that covers multiple filter properties and a sort property.
|
|
21
|
+
*
|
|
22
|
+
* When a query's equality filter properties match all `properties` AND its sort property
|
|
23
|
+
* matches `sortProperty`, this compound index can serve the entire query from a single
|
|
24
|
+
* LevelDB range scan — no in-memory filtering or sorting required.
|
|
25
|
+
*
|
|
26
|
+
* Key encoding: `<prop1>\x01<prop2>\x01...\x01<sortValue>\x00<messageCid>`
|
|
27
|
+
*/
|
|
28
|
+
export type CompoundIndexDefinition = {
|
|
29
|
+
/** Unique name for this compound index (used as the sublevel partition name). */
|
|
30
|
+
name: string;
|
|
31
|
+
/** Filter properties that must all be present as equality filters in the query. */
|
|
32
|
+
properties: string[];
|
|
33
|
+
/** The sort property embedded at the end of the compound key. */
|
|
34
|
+
sortProperty: string;
|
|
35
|
+
};
|
|
36
|
+
|
|
18
37
|
const INDEX_SUBLEVEL_NAME = 'index';
|
|
19
38
|
|
|
39
|
+
/** Separator between compound key segments (higher than \x00 so prefix scans work correctly). */
|
|
40
|
+
const COMPOUND_SEGMENT_SEPARATOR = '\x01';
|
|
41
|
+
|
|
20
42
|
export interface IndexLevelOptions {
|
|
21
43
|
signal?: AbortSignal;
|
|
22
44
|
}
|
|
@@ -27,6 +49,7 @@ export interface IndexLevelOptions {
|
|
|
27
49
|
export class IndexLevel {
|
|
28
50
|
db: LevelWrapper<string>;
|
|
29
51
|
config: IndexLevelConfig;
|
|
52
|
+
private _compoundIndexes: CompoundIndexDefinition[];
|
|
30
53
|
|
|
31
54
|
constructor(config: IndexLevelConfig) {
|
|
32
55
|
this.config = {
|
|
@@ -34,6 +57,8 @@ export class IndexLevel {
|
|
|
34
57
|
...config,
|
|
35
58
|
};
|
|
36
59
|
|
|
60
|
+
this._compoundIndexes = config.compoundIndexes ?? [];
|
|
61
|
+
|
|
37
62
|
this.db = new LevelWrapper<string>({
|
|
38
63
|
location : this.config.location,
|
|
39
64
|
createLevelDatabase : this.config.createLevelDatabase,
|
|
@@ -94,6 +119,14 @@ export class IndexLevel {
|
|
|
94
119
|
}
|
|
95
120
|
}
|
|
96
121
|
|
|
122
|
+
// create compound index entries for any registered compound indexes whose properties are all present in the indexes.
|
|
123
|
+
for (const compoundIndex of this._compoundIndexes) {
|
|
124
|
+
const compoundOp = this.createCompoundIndexPutOperation(tenant, item, compoundIndex);
|
|
125
|
+
if (compoundOp !== undefined) {
|
|
126
|
+
opCreationPromises.push(compoundOp);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
97
130
|
// create a reverse lookup for the sortedIndex values. This is used during deletion and cursor starting point lookup.
|
|
98
131
|
const partitionOperationPromise = this.createOperationForIndexesLookupPartition(
|
|
99
132
|
tenant,
|
|
@@ -136,6 +169,14 @@ export class IndexLevel {
|
|
|
136
169
|
}
|
|
137
170
|
}
|
|
138
171
|
|
|
172
|
+
// delete compound index entries
|
|
173
|
+
for (const compoundIndex of this._compoundIndexes) {
|
|
174
|
+
const compoundOp = this.createCompoundIndexDeleteOperation(tenant, messageCid, indexes, compoundIndex);
|
|
175
|
+
if (compoundOp !== undefined) {
|
|
176
|
+
opCreationPromises.push(compoundOp);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
139
180
|
const indexOps = await Promise.all(opCreationPromises);
|
|
140
181
|
const tenantPartition = await this.db.partition(tenant);
|
|
141
182
|
await tenantPartition.batch(indexOps, options);
|
|
@@ -233,6 +274,12 @@ export class IndexLevel {
|
|
|
233
274
|
/**
|
|
234
275
|
* Queries the index for items that match the filters. If no filters are provided, all items are returned.
|
|
235
276
|
*
|
|
277
|
+
* Query strategy selection (in priority order):
|
|
278
|
+
* 1. **Compound index**: If a single filter matches a registered compound index (all equality properties + sort property),
|
|
279
|
+
* the entire query is served from a single LevelDB range scan. Supports cursors natively.
|
|
280
|
+
* 2. **In-memory paging**: For "concise" filters without cursors. Scans the most selective property index, verifies in memory.
|
|
281
|
+
* 3. **Iterator paging**: Default fallback. Scans the sort property index, testing each item against the filter.
|
|
282
|
+
*
|
|
236
283
|
* @param filters Array of filters that are treated as an OR query.
|
|
237
284
|
* @param queryOptions query options for sort and pagination, requires at least `sortProperty`. The default sort direction is ascending.
|
|
238
285
|
* @param options IndexLevelOptions that include an AbortSignal.
|
|
@@ -240,13 +287,45 @@ export class IndexLevel {
|
|
|
240
287
|
*/
|
|
241
288
|
async query(tenant: string, filters: Filter[], queryOptions: QueryOptions, options?: IndexLevelOptions): Promise<IndexedItem[]> {
|
|
242
289
|
|
|
243
|
-
//
|
|
290
|
+
// Strategy 1: try compound index for single-filter queries
|
|
291
|
+
if (filters.length === 1 && !isEmptyObject(filters[0])) {
|
|
292
|
+
const compoundResult = this.selectCompoundIndex(filters[0], queryOptions);
|
|
293
|
+
if (compoundResult !== undefined) {
|
|
294
|
+
return this.queryWithCompoundIndex(tenant, filters[0], queryOptions, compoundResult, options);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Strategy 2: in-memory paging for concise filters
|
|
244
299
|
if (IndexLevel.shouldQueryWithInMemoryPaging(filters, queryOptions)) {
|
|
245
300
|
return this.queryWithInMemoryPaging(tenant, filters, queryOptions, options);
|
|
246
301
|
}
|
|
302
|
+
|
|
303
|
+
// Strategy 3: iterator paging (default)
|
|
247
304
|
return this.queryWithIteratorPaging(tenant, filters, queryOptions, options);
|
|
248
305
|
}
|
|
249
306
|
|
|
307
|
+
/**
|
|
308
|
+
* Counts the number of items that match the given filters without loading full records.
|
|
309
|
+
*
|
|
310
|
+
* When a compound index covers the query, counting is a simple key iteration without value deserialization.
|
|
311
|
+
* Otherwise, falls back to counting via the existing query strategies.
|
|
312
|
+
*/
|
|
313
|
+
async count(tenant: string, filters: Filter[], queryOptions: Omit<QueryOptions, 'limit' | 'cursor'>,
|
|
314
|
+
options?: IndexLevelOptions): Promise<number> {
|
|
315
|
+
|
|
316
|
+
// try compound index for single-filter queries
|
|
317
|
+
if (filters.length === 1 && !isEmptyObject(filters[0])) {
|
|
318
|
+
const compoundResult = this.selectCompoundIndex(filters[0], { ...queryOptions });
|
|
319
|
+
if (compoundResult !== undefined) {
|
|
320
|
+
return this.countWithCompoundIndex(tenant, filters[0], compoundResult, options);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// fallback: run a full query without limit and count the results
|
|
325
|
+
const results = await this.query(tenant, filters, { ...queryOptions }, options);
|
|
326
|
+
return results.length;
|
|
327
|
+
}
|
|
328
|
+
|
|
250
329
|
/**
|
|
251
330
|
* Queries the sort property index for items that match the filters. If no filters are provided, all items are returned.
|
|
252
331
|
* This query is a linear iterator over the sorted index, checking each item for a match.
|
|
@@ -508,6 +587,10 @@ export class IndexLevel {
|
|
|
508
587
|
|
|
509
588
|
/**
|
|
510
589
|
* Returns items that match the range filter.
|
|
590
|
+
*
|
|
591
|
+
* For `lte` bounds, the encoded value is extended with a trailing `\xff` so that composite keys
|
|
592
|
+
* of the form `<encodedValue>\x00<messageCid>` are naturally included in the range scan,
|
|
593
|
+
* eliminating the need for a separate exact-match query.
|
|
511
594
|
*/
|
|
512
595
|
private async filterRangeMatches(
|
|
513
596
|
tenant: string,
|
|
@@ -518,7 +601,15 @@ export class IndexLevel {
|
|
|
518
601
|
const iteratorOptions: LevelWrapperIteratorOptions<string> = {};
|
|
519
602
|
for (const comparator in rangeFilter) {
|
|
520
603
|
const comparatorName = comparator as keyof RangeFilter;
|
|
521
|
-
|
|
604
|
+
const encodedValue = IndexLevel.encodeValue(rangeFilter[comparatorName]!);
|
|
605
|
+
if (comparatorName === 'lte') {
|
|
606
|
+
// Extend the lte bound so that composite keys `<encodedValue>\x00<messageCid>` are included.
|
|
607
|
+
// Since \x00 < \xff, any key starting with encodedValue followed by the \x00 delimiter
|
|
608
|
+
// will be lexicographically less than encodedValue + \xff.
|
|
609
|
+
iteratorOptions[comparatorName] = encodedValue + '\xff';
|
|
610
|
+
} else {
|
|
611
|
+
iteratorOptions[comparatorName] = encodedValue;
|
|
612
|
+
}
|
|
522
613
|
}
|
|
523
614
|
|
|
524
615
|
// if there is no lower bound specified (`gt` or `gte`), we need to iterate from the upper bound,
|
|
@@ -531,24 +622,13 @@ export class IndexLevel {
|
|
|
531
622
|
const filterPartition = await this.getIndexPartition(tenant, propertyName);
|
|
532
623
|
|
|
533
624
|
for await (const [ key, value ] of filterPartition.iterator(iteratorOptions, options)) {
|
|
534
|
-
// if "greater-than" is specified, skip all keys that
|
|
625
|
+
// if "greater-than" is specified, skip all keys that contain the exact value given in the "greater-than" condition
|
|
535
626
|
if ('gt' in rangeFilter && this.extractIndexValueFromKey(key) === IndexLevel.encodeValue(rangeFilter.gt!)) {
|
|
536
627
|
continue;
|
|
537
628
|
}
|
|
538
629
|
matches.push(JSON.parse(value) as IndexedItem);
|
|
539
630
|
}
|
|
540
631
|
|
|
541
|
-
if ('lte' in rangeFilter) {
|
|
542
|
-
// When `lte` is used, we must also query the exact match explicitly because the exact match will not be included in the iterator above.
|
|
543
|
-
// This is due to the extra data appended to the (property + value) key prefix, e.g.
|
|
544
|
-
// the key '"2023-05-25T11:22:33.000000Z"\u0000bayfreigu....'
|
|
545
|
-
// would be considered greater than `lte` value in { lte: '"2023-05-25T11:22:33.000000Z"' } iterator options,
|
|
546
|
-
// thus would not be included in the iterator even though we'd like it to be.
|
|
547
|
-
for (const item of await this.filterExactMatches(tenant, propertyName, rangeFilter.lte as EqualFilter, options)) {
|
|
548
|
-
matches.push(item);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
|
|
552
632
|
return matches;
|
|
553
633
|
}
|
|
554
634
|
|
|
@@ -570,12 +650,14 @@ export class IndexLevel {
|
|
|
570
650
|
}
|
|
571
651
|
|
|
572
652
|
/**
|
|
573
|
-
* Find the starting position for pagination within the IndexedItem array.
|
|
574
|
-
* Returns the index of the first item
|
|
653
|
+
* Find the starting position for pagination within the IndexedItem array using binary search.
|
|
654
|
+
* Returns the index of the first item after the cursor, or -1 if no such item exists.
|
|
655
|
+
*
|
|
656
|
+
* Since the array is already sorted, binary search provides O(log n) performance instead of O(n).
|
|
575
657
|
*/
|
|
576
658
|
private findCursorStartingIndex(items: IndexedItem[], sortDirection: SortDirection, sortProperty: string, cursorStartingKey: string): number {
|
|
577
659
|
|
|
578
|
-
const
|
|
660
|
+
const isAfterCursor = (item: IndexedItem): boolean => {
|
|
579
661
|
const { messageCid, indexes } = item;
|
|
580
662
|
const sortValue = indexes[sortProperty] as string | number;
|
|
581
663
|
const itemCompareValue = IndexLevel.keySegmentJoin(IndexLevel.encodeValue(sortValue), messageCid);
|
|
@@ -585,7 +667,20 @@ export class IndexLevel {
|
|
|
585
667
|
itemCompareValue < cursorStartingKey;
|
|
586
668
|
};
|
|
587
669
|
|
|
588
|
-
|
|
670
|
+
// binary search for the first item after the cursor
|
|
671
|
+
let low = 0;
|
|
672
|
+
let high = items.length;
|
|
673
|
+
|
|
674
|
+
while (low < high) {
|
|
675
|
+
const mid = (low + high) >>> 1;
|
|
676
|
+
if (isAfterCursor(items[mid])) {
|
|
677
|
+
high = mid;
|
|
678
|
+
} else {
|
|
679
|
+
low = mid + 1;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
return low < items.length ? low : -1;
|
|
589
684
|
}
|
|
590
685
|
|
|
591
686
|
/**
|
|
@@ -654,6 +749,307 @@ export class IndexLevel {
|
|
|
654
749
|
}
|
|
655
750
|
}
|
|
656
751
|
|
|
752
|
+
// =========================================================================
|
|
753
|
+
// Compound index methods
|
|
754
|
+
// =========================================================================
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Gets the compound index partition for a given compound index definition.
|
|
758
|
+
* Compound index sublevels use the naming convention `__compound:<name>__`.
|
|
759
|
+
*/
|
|
760
|
+
private async getCompoundIndexPartition(tenant: string, compoundIndex: CompoundIndexDefinition): Promise<LevelWrapper<string>> {
|
|
761
|
+
const partitionName = `__compound:${compoundIndex.name}__`;
|
|
762
|
+
return (await this.db.partition(tenant)).partition(partitionName);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/**
|
|
766
|
+
* Builds a compound index key from the given indexes and compound index definition.
|
|
767
|
+
*
|
|
768
|
+
* Key format: `<prop1>\x01<prop2>\x01...\x01<sortValue>\x00<messageCid>`
|
|
769
|
+
*
|
|
770
|
+
* @returns the compound key, or undefined if the indexes don't contain all required properties.
|
|
771
|
+
*/
|
|
772
|
+
private static buildCompoundKey(messageCid: string, indexes: KeyValues, compoundIndex: CompoundIndexDefinition): string | undefined {
|
|
773
|
+
const segments: string[] = [];
|
|
774
|
+
|
|
775
|
+
for (const property of compoundIndex.properties) {
|
|
776
|
+
const value = indexes[property];
|
|
777
|
+
if (value === undefined || Array.isArray(value)) {
|
|
778
|
+
return undefined; // compound indexes don't support array values or missing properties
|
|
779
|
+
}
|
|
780
|
+
segments.push(IndexLevel.encodeValue(value));
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
const sortValue = indexes[compoundIndex.sortProperty];
|
|
784
|
+
if (sortValue === undefined || Array.isArray(sortValue)) {
|
|
785
|
+
return undefined;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// join prefix segments with \x01, then append sort value and messageCid with the standard delimiters
|
|
789
|
+
const prefixPart = segments.join(COMPOUND_SEGMENT_SEPARATOR);
|
|
790
|
+
const sortPart = IndexLevel.encodeValue(sortValue);
|
|
791
|
+
return prefixPart + COMPOUND_SEGMENT_SEPARATOR + sortPart + IndexLevel.delimiter + messageCid;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/**
|
|
795
|
+
* Builds the prefix portion of a compound key from filter values (without the sort/messageCid suffix).
|
|
796
|
+
* Used for range scans: all entries with this prefix match the filter.
|
|
797
|
+
*/
|
|
798
|
+
private static buildCompoundPrefix(filter: Filter, compoundIndex: CompoundIndexDefinition): string | undefined {
|
|
799
|
+
const segments: string[] = [];
|
|
800
|
+
|
|
801
|
+
for (const property of compoundIndex.properties) {
|
|
802
|
+
const filterValue = filter[property];
|
|
803
|
+
if (filterValue === undefined || typeof filterValue === 'object') {
|
|
804
|
+
return undefined; // compound prefix only works with equality filters
|
|
805
|
+
}
|
|
806
|
+
segments.push(IndexLevel.encodeValue(filterValue));
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
return segments.join(COMPOUND_SEGMENT_SEPARATOR) + COMPOUND_SEGMENT_SEPARATOR;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* Creates a put operation for a compound index entry.
|
|
814
|
+
* Returns undefined if the indexes don't contain all required compound index properties.
|
|
815
|
+
*/
|
|
816
|
+
private createCompoundIndexPutOperation(
|
|
817
|
+
tenant: string,
|
|
818
|
+
item: IndexedItem,
|
|
819
|
+
compoundIndex: CompoundIndexDefinition
|
|
820
|
+
): Promise<LevelWrapperBatchOperation<string>> | undefined {
|
|
821
|
+
const key = IndexLevel.buildCompoundKey(item.messageCid, item.indexes, compoundIndex);
|
|
822
|
+
if (key === undefined) {
|
|
823
|
+
return undefined;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
return this.createOperationForPartition(tenant, `__compound:${compoundIndex.name}__`, {
|
|
827
|
+
type : 'put',
|
|
828
|
+
key,
|
|
829
|
+
value : JSON.stringify(item),
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* Creates a delete operation for a compound index entry.
|
|
835
|
+
* Returns undefined if the indexes don't contain all required compound index properties.
|
|
836
|
+
*/
|
|
837
|
+
private createCompoundIndexDeleteOperation(
|
|
838
|
+
tenant: string,
|
|
839
|
+
messageCid: string,
|
|
840
|
+
indexes: KeyValues,
|
|
841
|
+
compoundIndex: CompoundIndexDefinition
|
|
842
|
+
): Promise<LevelWrapperBatchOperation<string>> | undefined {
|
|
843
|
+
const key = IndexLevel.buildCompoundKey(messageCid, indexes, compoundIndex);
|
|
844
|
+
if (key === undefined) {
|
|
845
|
+
return undefined;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
return this.createOperationForPartition(tenant, `__compound:${compoundIndex.name}__`, {
|
|
849
|
+
type: 'del',
|
|
850
|
+
key,
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* Generic helper to create a batch operation for any named partition under a tenant.
|
|
856
|
+
*/
|
|
857
|
+
private async createOperationForPartition(
|
|
858
|
+
tenant: string,
|
|
859
|
+
partitionName: string,
|
|
860
|
+
operation: LevelWrapperBatchOperation<string>
|
|
861
|
+
): Promise<LevelWrapperBatchOperation<string>> {
|
|
862
|
+
const tenantPartition = await this.db.partition(tenant);
|
|
863
|
+
return tenantPartition.createPartitionOperation(partitionName, operation);
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
/**
|
|
867
|
+
* Selects the best compound index that covers the given filter and sort requirements.
|
|
868
|
+
*
|
|
869
|
+
* A compound index "covers" a query when:
|
|
870
|
+
* 1. Every property in the compound index definition is present in the filter as an equality filter.
|
|
871
|
+
* 2. The compound index's sort property matches the query's sort property.
|
|
872
|
+
*
|
|
873
|
+
* Among multiple matching compound indexes, the one with the most properties is preferred
|
|
874
|
+
* (more specific = fewer false positives in the prefix scan).
|
|
875
|
+
*/
|
|
876
|
+
private selectCompoundIndex(filter: Filter, queryOptions: QueryOptions): CompoundIndexDefinition | undefined {
|
|
877
|
+
let bestMatch: CompoundIndexDefinition | undefined;
|
|
878
|
+
let bestPropertyCount = 0;
|
|
879
|
+
|
|
880
|
+
for (const compoundIndex of this._compoundIndexes) {
|
|
881
|
+
// check that the sort property matches
|
|
882
|
+
if (compoundIndex.sortProperty !== queryOptions.sortProperty) {
|
|
883
|
+
continue;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// check that all compound properties are present in the filter as equality filters
|
|
887
|
+
let allPropertiesMatch = true;
|
|
888
|
+
for (const property of compoundIndex.properties) {
|
|
889
|
+
const filterValue = filter[property];
|
|
890
|
+
if (filterValue === undefined || typeof filterValue === 'object') {
|
|
891
|
+
allPropertiesMatch = false;
|
|
892
|
+
break;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
if (allPropertiesMatch && compoundIndex.properties.length > bestPropertyCount) {
|
|
897
|
+
bestMatch = compoundIndex;
|
|
898
|
+
bestPropertyCount = compoundIndex.properties.length;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
return bestMatch;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
/**
|
|
906
|
+
* Queries using a compound index. This is the most efficient query strategy: a single LevelDB
|
|
907
|
+
* range scan that filters, sorts, and paginates all at once.
|
|
908
|
+
*
|
|
909
|
+
* The compound key encodes the filter properties as a prefix and the sort property as a suffix,
|
|
910
|
+
* so iterating over keys with the filter prefix yields results in sort order.
|
|
911
|
+
*
|
|
912
|
+
* Any remaining filter properties not covered by the compound index are verified in memory.
|
|
913
|
+
*/
|
|
914
|
+
private async queryWithCompoundIndex(
|
|
915
|
+
tenant: string,
|
|
916
|
+
filter: Filter,
|
|
917
|
+
queryOptions: QueryOptions,
|
|
918
|
+
compoundIndex: CompoundIndexDefinition,
|
|
919
|
+
options?: IndexLevelOptions
|
|
920
|
+
): Promise<IndexedItem[]> {
|
|
921
|
+
const { sortDirection = SortDirection.Ascending, cursor, limit } = queryOptions;
|
|
922
|
+
|
|
923
|
+
const prefix = IndexLevel.buildCompoundPrefix(filter, compoundIndex);
|
|
924
|
+
if (prefix === undefined) {
|
|
925
|
+
// should not happen since selectCompoundIndex already validated, but guard against it
|
|
926
|
+
return this.queryWithIteratorPaging(tenant, [filter], queryOptions, options);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
const partition = await this.getCompoundIndexPartition(tenant, compoundIndex);
|
|
930
|
+
|
|
931
|
+
// determine the iterator bounds from the prefix
|
|
932
|
+
const iteratorOptions: LevelWrapperIteratorOptions<string> = {};
|
|
933
|
+
|
|
934
|
+
if (cursor !== undefined) {
|
|
935
|
+
// build the full compound key for the cursor position
|
|
936
|
+
const cursorSortEncoded = IndexLevel.encodeValue(cursor.value);
|
|
937
|
+
const cursorKey = prefix + cursorSortEncoded + IndexLevel.delimiter + cursor.messageCid;
|
|
938
|
+
|
|
939
|
+
if (sortDirection === SortDirection.Ascending) {
|
|
940
|
+
iteratorOptions.gt = cursorKey;
|
|
941
|
+
// upper bound: everything with this prefix (prefix + \xff is past all valid compound keys with this prefix)
|
|
942
|
+
iteratorOptions.lt = prefix + '\xff';
|
|
943
|
+
} else {
|
|
944
|
+
iteratorOptions.lt = cursorKey;
|
|
945
|
+
iteratorOptions.gt = prefix;
|
|
946
|
+
iteratorOptions.reverse = true;
|
|
947
|
+
}
|
|
948
|
+
} else {
|
|
949
|
+
if (sortDirection === SortDirection.Ascending) {
|
|
950
|
+
iteratorOptions.gt = prefix;
|
|
951
|
+
iteratorOptions.lt = prefix + '\xff';
|
|
952
|
+
} else {
|
|
953
|
+
// for descending without cursor, start from the end of the prefix range
|
|
954
|
+
iteratorOptions.gt = prefix;
|
|
955
|
+
iteratorOptions.lt = prefix + '\xff';
|
|
956
|
+
iteratorOptions.reverse = true;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
// determine which filter properties are NOT covered by the compound index
|
|
961
|
+
// (need in-memory verification for these)
|
|
962
|
+
// NOTE: the compound index equality properties are fully covered by the prefix scan,
|
|
963
|
+
// but the sort property is only covered for ordering — any range filter on the sort
|
|
964
|
+
// property must still be applied as a residual filter.
|
|
965
|
+
const coveredEqualityProperties = new Set(compoundIndex.properties);
|
|
966
|
+
const residualFilter: Filter = {};
|
|
967
|
+
let hasResidualFilter = false;
|
|
968
|
+
for (const property in filter) {
|
|
969
|
+
if (!coveredEqualityProperties.has(property)) {
|
|
970
|
+
residualFilter[property] = filter[property];
|
|
971
|
+
hasResidualFilter = true;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
const matches: IndexedItem[] = [];
|
|
976
|
+
for await (const [_key, value] of partition.iterator(iteratorOptions, options)) {
|
|
977
|
+
if (limit !== undefined && matches.length === limit) {
|
|
978
|
+
break;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
const item = JSON.parse(value) as IndexedItem;
|
|
982
|
+
|
|
983
|
+
// verify any residual filter properties in memory
|
|
984
|
+
if (hasResidualFilter && !FilterUtility.matchFilter(item.indexes, residualFilter)) {
|
|
985
|
+
continue;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
matches.push(item);
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
return matches;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
/**
|
|
995
|
+
* Counts items matching a compound index prefix without loading full records.
|
|
996
|
+
* Iterates only keys (not values) for maximum efficiency.
|
|
997
|
+
*/
|
|
998
|
+
private async countWithCompoundIndex(
|
|
999
|
+
tenant: string,
|
|
1000
|
+
filter: Filter,
|
|
1001
|
+
compoundIndex: CompoundIndexDefinition,
|
|
1002
|
+
options?: IndexLevelOptions
|
|
1003
|
+
): Promise<number> {
|
|
1004
|
+
const prefix = IndexLevel.buildCompoundPrefix(filter, compoundIndex);
|
|
1005
|
+
if (prefix === undefined) {
|
|
1006
|
+
// fallback
|
|
1007
|
+
const results = await this.query(tenant, [filter], { sortProperty: compoundIndex.sortProperty }, options);
|
|
1008
|
+
return results.length;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
const partition = await this.getCompoundIndexPartition(tenant, compoundIndex);
|
|
1012
|
+
|
|
1013
|
+
// determine which filter properties are NOT covered by the compound index
|
|
1014
|
+
// (same logic as queryWithCompoundIndex: sort property range filters are residual)
|
|
1015
|
+
const coveredEqualityProperties = new Set(compoundIndex.properties);
|
|
1016
|
+
let hasResidualFilter = false;
|
|
1017
|
+
const residualFilter: Filter = {};
|
|
1018
|
+
for (const property in filter) {
|
|
1019
|
+
if (!coveredEqualityProperties.has(property)) {
|
|
1020
|
+
residualFilter[property] = filter[property];
|
|
1021
|
+
hasResidualFilter = true;
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
const iteratorOptions: LevelWrapperIteratorOptions<string> = {
|
|
1026
|
+
gt : prefix,
|
|
1027
|
+
lt : prefix + '\xff',
|
|
1028
|
+
};
|
|
1029
|
+
|
|
1030
|
+
let count = 0;
|
|
1031
|
+
if (hasResidualFilter) {
|
|
1032
|
+
// must read values to check residual filter
|
|
1033
|
+
for await (const [_key, value] of partition.iterator(iteratorOptions, options)) {
|
|
1034
|
+
const item = JSON.parse(value) as IndexedItem;
|
|
1035
|
+
if (FilterUtility.matchFilter(item.indexes, residualFilter)) {
|
|
1036
|
+
count++;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
} else {
|
|
1040
|
+
// no residual filter — iterate keys via iterator without parsing values
|
|
1041
|
+
for await (const [_key, _value] of partition.iterator(iteratorOptions, options)) {
|
|
1042
|
+
count++;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
return count;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
// =========================================================================
|
|
1050
|
+
// Query strategy selection
|
|
1051
|
+
// =========================================================================
|
|
1052
|
+
|
|
657
1053
|
private static shouldQueryWithInMemoryPaging(filters: Filter[], queryOptions: QueryOptions): boolean {
|
|
658
1054
|
for (const filter of filters) {
|
|
659
1055
|
if (!IndexLevel.isFilterConcise(filter, queryOptions)) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AbstractBatchOperation, AbstractDatabaseOptions, AbstractIteratorOptions
|
|
1
|
+
import type { AbstractBatchOperation, AbstractDatabaseOptions, AbstractIteratorOptions } from 'abstract-level';
|
|
2
2
|
|
|
3
3
|
import { executeUnlessAborted } from '../utils/abort.js';
|
|
4
4
|
import { Level } from 'level';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
|
|
2
|
+
import type { CompoundIndexDefinition } from './index-level.js';
|
|
2
3
|
import type { Filter, KeyValues, PaginationCursor, QueryOptions } from '../types/query-types.js';
|
|
3
4
|
import type { GenericMessage, MessageSort, Pagination } from '../types/message-types.js';
|
|
4
5
|
import type { MessageStore, MessageStoreOptions } from '../types/message-store.js';
|
|
@@ -17,6 +18,51 @@ import { sha256 } from 'multiformats/hashes/sha2';
|
|
|
17
18
|
import { SortDirection } from '../types/query-types.js';
|
|
18
19
|
|
|
19
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Default compound indexes that cover the most common DWN query patterns.
|
|
23
|
+
* These are automatically registered unless overridden via config.
|
|
24
|
+
*
|
|
25
|
+
* Each compound index enables a single-scan query when the filter matches
|
|
26
|
+
* all equality properties and the sort matches the sort property.
|
|
27
|
+
*/
|
|
28
|
+
const DEFAULT_COMPOUND_INDEXES: CompoundIndexDefinition[] = [
|
|
29
|
+
{
|
|
30
|
+
// Covers: RecordsQuery with protocol + protocolPath, sorted by messageTimestamp
|
|
31
|
+
// Example: "all chat messages in protocol X, path 'chat/message', sorted by time"
|
|
32
|
+
name : 'protocol-protocolPath-messageTimestamp',
|
|
33
|
+
properties : ['protocol', 'protocolPath'],
|
|
34
|
+
sortProperty : 'messageTimestamp',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
// Covers: RecordsQuery with protocol + protocolPath, sorted by dateCreated
|
|
38
|
+
name : 'protocol-protocolPath-dateCreated',
|
|
39
|
+
properties : ['protocol', 'protocolPath'],
|
|
40
|
+
sortProperty : 'dateCreated',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
// Covers: RecordsQuery with schema, sorted by messageTimestamp
|
|
44
|
+
// Example: "all records with schema X, sorted by time"
|
|
45
|
+
name : 'schema-messageTimestamp',
|
|
46
|
+
properties : ['schema'],
|
|
47
|
+
sortProperty : 'messageTimestamp',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
// Covers: RecordsQuery with schema, sorted by dateCreated
|
|
51
|
+
name : 'schema-dateCreated',
|
|
52
|
+
properties : ['schema'],
|
|
53
|
+
sortProperty : 'dateCreated',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
// Covers: RecordsQuery with protocol + contextId, sorted by messageTimestamp
|
|
57
|
+
// Example: "all messages in a specific protocol context (conversation), sorted by time"
|
|
58
|
+
// NOTE: contextId is often a prefix/range filter. Compound indexes only support equality.
|
|
59
|
+
// This index is useful when contextId is an exact match (e.g., root-level context queries).
|
|
60
|
+
name : 'protocol-contextId-messageTimestamp',
|
|
61
|
+
properties : ['protocol', 'contextId'],
|
|
62
|
+
sortProperty : 'messageTimestamp',
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
|
|
20
66
|
/**
|
|
21
67
|
* A simple implementation of {@link MessageStore} that works in both the browser and server-side.
|
|
22
68
|
* Leverages LevelDB under the hood.
|
|
@@ -33,6 +79,8 @@ export class MessageStoreLevel implements MessageStore {
|
|
|
33
79
|
* LevelDB will store its files, or in browsers, the name of the
|
|
34
80
|
* {@link https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase IDBDatabase} to be opened.
|
|
35
81
|
* @param {string} config.indexLocation - same as config.blockstoreLocation
|
|
82
|
+
* @param {CompoundIndexDefinition[]} config.compoundIndexes - compound indexes to register.
|
|
83
|
+
* Defaults to DEFAULT_COMPOUND_INDEXES which cover the most common DWN query patterns.
|
|
36
84
|
*/
|
|
37
85
|
constructor(config: MessageStoreLevelConfig = {}) {
|
|
38
86
|
this.config = {
|
|
@@ -50,6 +98,7 @@ export class MessageStoreLevel implements MessageStore {
|
|
|
50
98
|
this.index = new IndexLevel({
|
|
51
99
|
location : this.config.indexLocation!,
|
|
52
100
|
createLevelDatabase : this.config.createLevelDatabase,
|
|
101
|
+
compoundIndexes : this.config.compoundIndexes ?? DEFAULT_COMPOUND_INDEXES,
|
|
53
102
|
});
|
|
54
103
|
}
|
|
55
104
|
|
|
@@ -115,6 +164,18 @@ export class MessageStoreLevel implements MessageStore {
|
|
|
115
164
|
return { messages, cursor };
|
|
116
165
|
}
|
|
117
166
|
|
|
167
|
+
async count(
|
|
168
|
+
tenant: string,
|
|
169
|
+
filters: Filter[],
|
|
170
|
+
messageSort?: MessageSort,
|
|
171
|
+
options?: MessageStoreOptions
|
|
172
|
+
): Promise<number> {
|
|
173
|
+
options?.signal?.throwIfAborted();
|
|
174
|
+
|
|
175
|
+
const queryOptions = MessageStoreLevel.buildQueryOptions(messageSort);
|
|
176
|
+
return this.index.count(tenant, filters, queryOptions, options);
|
|
177
|
+
}
|
|
178
|
+
|
|
118
179
|
/**
|
|
119
180
|
* Builds the IndexLevel QueryOptions object given MessageStore sort and pagination parameters.
|
|
120
181
|
*/
|
|
@@ -192,4 +253,5 @@ export type MessageStoreLevelConfig = {
|
|
|
192
253
|
blockstoreLocation?: string,
|
|
193
254
|
indexLocation?: string,
|
|
194
255
|
createLevelDatabase?: typeof createLevelDatabase,
|
|
256
|
+
compoundIndexes?: CompoundIndexDefinition[],
|
|
195
257
|
};
|