@powerhousedao/reactor 6.2.2-dev.49 → 6.2.2-dev.50
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/{build-worker-executor-CHkkh55C.js → build-worker-executor-CkuJGUlJ.js} +2 -2
- package/dist/{build-worker-executor-CHkkh55C.js.map → build-worker-executor-CkuJGUlJ.js.map} +1 -1
- package/dist/{document-indexer-BfdCgjG-.js → document-indexer-DlpJB8AK.js} +5 -4
- package/dist/document-indexer-DlpJB8AK.js.map +1 -0
- package/dist/{drive-container-types-h3M1AK3K.js → drive-container-types-BoY5t12r.js} +570 -561
- package/dist/drive-container-types-BoY5t12r.js.map +1 -0
- package/dist/entry.js +2 -2
- package/dist/index.d.ts +566 -385
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +381 -57
- package/dist/index.js.map +1 -1
- package/dist/projection-entry.js +3 -3
- package/dist/{worker-CbtYMIju.js → worker-BCMw-hBw.js} +2 -2
- package/dist/{worker-CbtYMIju.js.map → worker-BCMw-hBw.js.map} +1 -1
- package/package.json +4 -4
- package/dist/document-indexer-BfdCgjG-.js.map +0 -1
- package/dist/drive-container-types-h3M1AK3K.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as decideAtHead, B as InvalidOperationTimestampError, C as DuplicateOperationError, D as DuplicateModuleError, E as DuplicateManifestError, F as AuthTimestampNotMonotonicError, H as matchesScope, I as AuthorizationDeniedError, L as DocumentDeletedError, M as documentDecisionModel, N as authDecisionModel, O as InvalidModuleError, P as buildDecisionModel, R as DocumentNotFoundError, S as AppendConditionFailedError, T as RevisionMismatchError, U as parsePagingOptions, V as UpgradePreconditionFailedError, W as throwIfAborted, _ as DocumentMetaCache, a as createForwardingPoolInstrumentation, b as CollectionMembershipCache, c as KyselyKeyframeStore, d as DriveCollectionId, f as KyselyExecutionScope, g as KyselyOperationIndex, h as KyselyWriteCache, i as runMigrations, j as selectDecisionModel, k as ModuleNotFoundError, l as DocumentModelRegistry, m as EventBus, n as REACTOR_SCHEMA, o as instrumentPgPool, p as resolveFeatureFlags, r as getMigrationStatus, s as KyselyOperationStore, t as DEFAULT_DRIVE_CONTAINER_TYPES, u as SimpleJobExecutor, v as createConsistencyToken, w as OptimisticLockError, x as APPEND_CONDITION_FAILED_PREFIX, y as createEmptyConsistencyToken, z as ExcessiveReshuffleError } from "./drive-container-types-BoY5t12r.js";
|
|
2
2
|
import { n as ReactorEventTypes, t as EventBusAggregateError } from "./types-DMKLa0Ok.js";
|
|
3
3
|
import { i as WorkerInitFailedError, r as WorkerExitedError, t as WorkerAbortTimeoutError } from "./errors-D3S6Eysd.js";
|
|
4
|
-
import { a as ReadModelCoordinator, i as KyselyDocumentView, n as ConsistencyTracker, o as BaseReadModel, r as makeConsistencyKey, t as KyselyDocumentIndexer } from "./document-indexer-
|
|
4
|
+
import { a as ReadModelCoordinator, i as KyselyDocumentView, n as ConsistencyTracker, o as BaseReadModel, r as makeConsistencyKey, t as KyselyDocumentIndexer } from "./document-indexer-DlpJB8AK.js";
|
|
5
5
|
import { n as errorToInfo, r as sanitizeArg, t as createForwardingLogger } from "./forwarding-logger-BBkMSxuJ.js";
|
|
6
|
-
import { t as workerEntryPath } from "./worker-
|
|
7
|
-
import { DowngradeNotSupportedError, UnsupportedDocumentModelVersionError, actions, actions as documentActions, createPresignedHeader, decide, garbageCollect, generateId, groupDocumentType, groupMembershipActionTypes, hashDocumentStateForScope, normalizeDocumentModelVersion, sortOperations } from "@powerhousedao/shared/document-model";
|
|
6
|
+
import { t as workerEntryPath } from "./worker-BCMw-hBw.js";
|
|
7
|
+
import { DowngradeNotSupportedError, MAX_AUTH_GRANTS, UnsupportedDocumentModelVersionError, actions, actions as documentActions, createPresignedHeader, decide, garbageCollect, generateId, groupDocumentType, groupMembershipActionTypes, hashDocumentStateForScope, normalizeDocumentModelVersion, sortOperations } from "@powerhousedao/shared/document-model";
|
|
8
8
|
import { addFile, addFolder, copyNode, deleteNode, driveCreateDocument, generateNodesCopy, getDescendants, handleTargetNameCollisions, isFileNode, isFolderNode, moveNode, updateNode } from "@powerhousedao/shared/document-drive";
|
|
9
9
|
import { v4 } from "uuid";
|
|
10
10
|
import { ConsoleLogger, childLogger } from "document-model";
|
|
@@ -393,7 +393,7 @@ var DriveClient = class {
|
|
|
393
393
|
}] }, signal);
|
|
394
394
|
const completedJobs = await Promise.all(Object.values(batchResult.jobs).map((job) => this.client.waitForJob(job, signal)));
|
|
395
395
|
for (const job of completedJobs) if (job.status === JobStatus.FAILED) throw new Error(job.error?.message);
|
|
396
|
-
return this.
|
|
396
|
+
return this.client.get(documentId, void 0, signal);
|
|
397
397
|
}
|
|
398
398
|
async addFolder(driveIdentifier, name, parentFolder, signal) {
|
|
399
399
|
this.logger.verbose("drives.addFolder(@driveIdentifier, @name, @parentFolder)", driveIdentifier, name, parentFolder);
|
|
@@ -402,7 +402,7 @@ var DriveClient = class {
|
|
|
402
402
|
id: folderId,
|
|
403
403
|
name,
|
|
404
404
|
parentFolder
|
|
405
|
-
})], signal)).state.global
|
|
405
|
+
})], signal)).state.global?.nodes.find((n) => n.id === folderId);
|
|
406
406
|
if (!node || !isFolderNode(node)) throw new Error("Folder creation failed");
|
|
407
407
|
return node;
|
|
408
408
|
}
|
|
@@ -429,7 +429,7 @@ var DriveClient = class {
|
|
|
429
429
|
const node = (await this.client.execute(driveIdentifier, "main", [updateNode({
|
|
430
430
|
id: nodeId,
|
|
431
431
|
name
|
|
432
|
-
})], signal)).state.global
|
|
432
|
+
})], signal)).state.global?.nodes.find((n) => n.id === nodeId);
|
|
433
433
|
if (!node) throw new Error("Node missing from drive after rename");
|
|
434
434
|
return node;
|
|
435
435
|
}
|
|
@@ -686,20 +686,267 @@ let DocumentChangeType = /* @__PURE__ */ function(DocumentChangeType) {
|
|
|
686
686
|
return DocumentChangeType;
|
|
687
687
|
}({});
|
|
688
688
|
//#endregion
|
|
689
|
-
//#region src/
|
|
689
|
+
//#region src/decision/read-gate.ts
|
|
690
|
+
/**
|
|
691
|
+
* Scopes every holder of a document may read, whatever the grants say. Denying
|
|
692
|
+
* the policy itself would let a replica sync a document without it, read the
|
|
693
|
+
* auth scope as uninitialized, and allow every operation it holds, so replicas
|
|
694
|
+
* would diverge permanently. The document scope carries the metadata the same
|
|
695
|
+
* argument covers. Grants gate domain-scope reads only.
|
|
696
|
+
*/
|
|
690
697
|
const ALWAYS_READABLE_SCOPES = new Set(["auth", "document"]);
|
|
698
|
+
/**
|
|
699
|
+
* The branch a group is read on. A group's member list lives on its main
|
|
700
|
+
* branch whatever branch the document naming it is on, and the reference
|
|
701
|
+
* relation records no branch of its own.
|
|
702
|
+
*/
|
|
703
|
+
const GROUP_BRANCH = "main";
|
|
704
|
+
/**
|
|
705
|
+
* The scope of a group document that holds its member list. It is the only
|
|
706
|
+
* scope group serving reaches, because it is the only one the audience must
|
|
707
|
+
* fold in order to evaluate auth with the group.
|
|
708
|
+
*/
|
|
709
|
+
const GROUP_MEMBERSHIP_SCOPE = "global";
|
|
710
|
+
/**
|
|
711
|
+
* How many of a group's referencing documents are examined before a read of
|
|
712
|
+
* that group gives up and withholds. Each one costs a model build, and the
|
|
713
|
+
* relation is append-only, so an old and briefly-held reference still counts.
|
|
714
|
+
* Truncating denies, which is the direction that withholds. It is the cap a
|
|
715
|
+
* version-1 policy carries on its own grant list, taken from there rather than
|
|
716
|
+
* restated: raising that cap raises the read fan-out here.
|
|
717
|
+
*/
|
|
718
|
+
const MAX_EXAMINED_REFERENCERS = MAX_AUTH_GRANTS;
|
|
719
|
+
/**
|
|
720
|
+
* How many referencers are probed at once. The reactor's Postgres pool size is
|
|
721
|
+
* optional and defaults to ten, and a listing resolves one gate per result, so
|
|
722
|
+
* per-gate fan-out multiplies: a handful keeps one group read from claiming the
|
|
723
|
+
* whole pool while still cutting the walk several times over. On PGlite the
|
|
724
|
+
* queries serialize regardless, so a larger number buys nothing in a browser.
|
|
725
|
+
*/
|
|
726
|
+
const REFERENCER_PROBE_CONCURRENCY = 4;
|
|
727
|
+
/**
|
|
728
|
+
* The policy carried on a document, if it carries one. A document handed to the
|
|
729
|
+
* gate with no state at all is not policied, so it is not gated.
|
|
730
|
+
*/
|
|
731
|
+
function authOf(document) {
|
|
732
|
+
return document.state?.auth;
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* The model reads enforce. Below `authEnforcement` there is no model to
|
|
736
|
+
* enforce: the document-only model ignores the auth scope entirely, so reading
|
|
737
|
+
* through it would serve every domain scope of a policied document to anyone.
|
|
738
|
+
* Undefined therefore means "evaluate the policy alone", which is what the read
|
|
739
|
+
* surface did before the model existed.
|
|
740
|
+
*/
|
|
741
|
+
function readDecisionModel(flags, registry) {
|
|
742
|
+
return flags.authEnforcement ? selectDecisionModel(flags, registry) : void 0;
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Evaluates the policy on its own, with no groups map and no condition context.
|
|
746
|
+
* A `{ group }` or conditional grant therefore never applies: an allow that
|
|
747
|
+
* does not apply withholds access, so this cannot widen a policy, but a policy
|
|
748
|
+
* relying on a conditional deny is weaker here than it is written.
|
|
749
|
+
*/
|
|
750
|
+
var BareReadGate = class {
|
|
751
|
+
scopePredicate(document, subject) {
|
|
752
|
+
const auth = authOf(document);
|
|
753
|
+
return Promise.resolve((scope) => ALWAYS_READABLE_SCOPES.has(scope) || decide(auth, subject, {
|
|
754
|
+
verb: "read",
|
|
755
|
+
scope
|
|
756
|
+
}) === "allow");
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
/**
|
|
760
|
+
* Answers a stream read from the document already fetched, and anything else
|
|
761
|
+
* through the read side.
|
|
762
|
+
*
|
|
763
|
+
* The seed is why routing reads through a decision model costs no extra I/O for
|
|
764
|
+
* the document being read: its `document` and `auth` scopes are the two static
|
|
765
|
+
* projections, and the caller has both in hand. Only a group stream the grant
|
|
766
|
+
* list names is fetched.
|
|
767
|
+
*/
|
|
768
|
+
var SeededStateReader = class {
|
|
769
|
+
constructor(documentView, seed, branch) {
|
|
770
|
+
this.documentView = documentView;
|
|
771
|
+
this.seed = seed;
|
|
772
|
+
this.branch = branch;
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* A stream this replica does not hold has to reach buildDecisionModel as the
|
|
776
|
+
* absence it recognises, or the whole read fails instead of leaving the group
|
|
777
|
+
* out of the model, where its principal does not match and the policy fails
|
|
778
|
+
* closed. The read side reports absence as a plain Error, so the absence is
|
|
779
|
+
* confirmed rather than inferred from the message: a transient failure must
|
|
780
|
+
* surface, not silently deny.
|
|
781
|
+
*/
|
|
782
|
+
async getState(documentId, scope, branch, targetRevision, signal) {
|
|
783
|
+
if (targetRevision === void 0 && documentId === this.seed.header.id && branch === this.branch && scope in this.seed.state) return this.seed;
|
|
784
|
+
try {
|
|
785
|
+
return await this.documentView.get(documentId, {
|
|
786
|
+
branch,
|
|
787
|
+
scopes: [scope]
|
|
788
|
+
}, void 0, signal);
|
|
789
|
+
} catch (error) {
|
|
790
|
+
await this.assertAbsent(documentId, error, signal);
|
|
791
|
+
throw new DocumentNotFoundError(documentId);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
async assertAbsent(documentId, error, signal) {
|
|
795
|
+
if (DocumentNotFoundError.isError(error)) return;
|
|
796
|
+
let exists;
|
|
797
|
+
try {
|
|
798
|
+
exists = await this.documentView.exists([documentId], void 0, signal);
|
|
799
|
+
} catch {
|
|
800
|
+
throw error;
|
|
801
|
+
}
|
|
802
|
+
if (exists[0]) throw error;
|
|
803
|
+
}
|
|
804
|
+
};
|
|
805
|
+
/**
|
|
806
|
+
* Evaluates a read against the registered decision model, built at the stream
|
|
807
|
+
* heads. This is what makes `{ group }` principals and conditional grants apply
|
|
808
|
+
* to a read: the model supplies the groups map and the scope's own state, the
|
|
809
|
+
* same two things admission supplies.
|
|
810
|
+
*
|
|
811
|
+
* A read has no action, so a condition on `action.input.*` never holds for one.
|
|
812
|
+
*
|
|
813
|
+
* State is read through the read side rather than the write cache. The write
|
|
814
|
+
* cache is invalidated by whichever process runs the executor, so a reactor
|
|
815
|
+
* running its executors in worker processes would answer reads in the parent
|
|
816
|
+
* from state no commit ever invalidates.
|
|
817
|
+
*/
|
|
818
|
+
var ModelReadGate = class {
|
|
819
|
+
constructor(model, documentView, servesGroups, operationIndex, logger) {
|
|
820
|
+
this.model = model;
|
|
821
|
+
this.documentView = documentView;
|
|
822
|
+
this.servesGroups = servesGroups;
|
|
823
|
+
this.operationIndex = operationIndex;
|
|
824
|
+
this.logger = logger;
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* A served group yields its member list and nothing else. What the audience
|
|
828
|
+
* is owed is the state it must fold to evaluate auth with the group; a
|
|
829
|
+
* group's other scopes are its own business and stay behind its own grants.
|
|
830
|
+
*/
|
|
831
|
+
async scopePredicate(document, subject, branch, signal) {
|
|
832
|
+
const own = await this.ownPolicyPredicate(document, subject, branch, signal);
|
|
833
|
+
if (!this.servesGroup(document) || own(GROUP_MEMBERSHIP_SCOPE)) return own;
|
|
834
|
+
const audience = await this.servesGroupTo(document.header.id, subject, signal);
|
|
835
|
+
return (scope) => own(scope) || audience && scope === GROUP_MEMBERSHIP_SCOPE;
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Whether the subject is served this group because a policy names it.
|
|
839
|
+
*
|
|
840
|
+
* A replica must fold a group's membership to evaluate auth with it, so a
|
|
841
|
+
* group a grant names is served to the audience of the document that names
|
|
842
|
+
* it, whatever the group's own read grants say. Naming a group in a policy
|
|
843
|
+
* publishes its roster to that policy's audience; a group whose membership
|
|
844
|
+
* must stay confidential does not belong in a grant.
|
|
845
|
+
*
|
|
846
|
+
* The referencing document's own domain scopes are the test. Its `auth` and
|
|
847
|
+
* `document` scopes are readable by every holder, so testing those would
|
|
848
|
+
* serve every referenced group to everybody.
|
|
849
|
+
*
|
|
850
|
+
* One level only. A referencer that is itself a group is skipped, and a
|
|
851
|
+
* referencer's own readability is decided from its policy alone, so a
|
|
852
|
+
* reference cycle terminates. Cycles are reachable: the reference relation
|
|
853
|
+
* is recorded from an operation's input, including one later stored denied,
|
|
854
|
+
* so a refused grant naming a group from inside another group leaves a row
|
|
855
|
+
* behind that validation never saw.
|
|
856
|
+
*
|
|
857
|
+
* The referencers are probed a few at a time and the walk stops at the first
|
|
858
|
+
* that serves, because a subject outside the audience is the case that runs to
|
|
859
|
+
* the bound, and it is the common one. A probe that failed decides only when
|
|
860
|
+
* nothing served: serving rests on a real allow, so this cannot widen, and it
|
|
861
|
+
* stops one unreachable referencer from turning an allow already in hand into
|
|
862
|
+
* a denial. A read records no operation, so replicas differing over a
|
|
863
|
+
* transient failure has no consensus consequence.
|
|
864
|
+
*
|
|
865
|
+
* The probes are awaited together rather than raced, so none is ever left
|
|
866
|
+
* running with nobody awaiting it, which is where unhandled rejections come
|
|
867
|
+
* from.
|
|
868
|
+
*/
|
|
869
|
+
async servesGroupTo(groupId, subject, signal) {
|
|
870
|
+
if (!this.operationIndex) return false;
|
|
871
|
+
const referencers = await this.operationIndex.getGroupReferencers(groupId, signal);
|
|
872
|
+
const examined = referencers.slice(0, MAX_EXAMINED_REFERENCERS);
|
|
873
|
+
if (examined.length < referencers.length) this.logger?.warn(`Group ${groupId} is referenced by ${referencers.length} documents; only the first ${MAX_EXAMINED_REFERENCERS} decide whether it is served`);
|
|
874
|
+
const walk = {
|
|
875
|
+
next: 0,
|
|
876
|
+
served: false,
|
|
877
|
+
failure: void 0
|
|
878
|
+
};
|
|
879
|
+
const probe = async () => {
|
|
880
|
+
while (!walk.served && walk.next < examined.length) {
|
|
881
|
+
const referencerId = examined[walk.next++];
|
|
882
|
+
let serves = false;
|
|
883
|
+
try {
|
|
884
|
+
serves = await this.servesThrough(referencerId, subject, signal);
|
|
885
|
+
} catch (error) {
|
|
886
|
+
walk.failure ??= Error.isError(error) ? error : /* @__PURE__ */ new Error(`Probing referencer ${referencerId} failed`);
|
|
887
|
+
}
|
|
888
|
+
if (serves) walk.served = true;
|
|
889
|
+
}
|
|
890
|
+
};
|
|
891
|
+
await Promise.all(Array.from({ length: Math.min(REFERENCER_PROBE_CONCURRENCY, examined.length) }, probe));
|
|
892
|
+
if (!walk.served && walk.failure) throw walk.failure;
|
|
893
|
+
return walk.served;
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Whether one referencing document serves the subject any domain scope. A
|
|
897
|
+
* referencer this replica does not hold serves nothing, which fails closed
|
|
898
|
+
* the same way a group it does not hold does.
|
|
899
|
+
*/
|
|
900
|
+
async servesThrough(referencerId, subject, signal) {
|
|
901
|
+
let referencer;
|
|
902
|
+
try {
|
|
903
|
+
referencer = await this.documentView.get(referencerId, { branch: GROUP_BRANCH }, void 0, signal);
|
|
904
|
+
} catch {
|
|
905
|
+
return false;
|
|
906
|
+
}
|
|
907
|
+
if (this.servesGroup(referencer)) return false;
|
|
908
|
+
const readable = await this.ownPolicyPredicate(referencer, subject, GROUP_BRANCH, signal);
|
|
909
|
+
return Object.keys(referencer.state ?? {}).filter((scope) => !ALWAYS_READABLE_SCOPES.has(scope)).some((scope) => readable(scope));
|
|
910
|
+
}
|
|
911
|
+
servesGroup(document) {
|
|
912
|
+
return this.servesGroups && this.operationIndex !== void 0 && document.header.documentType === groupDocumentType;
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* What this document's own policy says, with no group serving applied.
|
|
916
|
+
*
|
|
917
|
+
* An unpoliced document is readable in full, which is the common case and the
|
|
918
|
+
* one worth answering without building anything. The test is the one
|
|
919
|
+
* `evaluate` makes: a legacy `{}` auth scope and version 0 both mean
|
|
920
|
+
* uninitialized, and "no grants" does not, because a policy with a version and
|
|
921
|
+
* an empty grant list denies everything.
|
|
922
|
+
*/
|
|
923
|
+
async ownPolicyPredicate(document, subject, branch, signal) {
|
|
924
|
+
const auth = authOf(document);
|
|
925
|
+
if (!auth || !auth.version) return () => true;
|
|
926
|
+
const target = {
|
|
927
|
+
documentId: document.header.id,
|
|
928
|
+
branch
|
|
929
|
+
};
|
|
930
|
+
const built = await buildDecisionModel(new SeededStateReader(this.documentView, document, branch), this.model, target, signal);
|
|
931
|
+
const definition = this.model(target);
|
|
932
|
+
const scopeStates = document.state ?? {};
|
|
933
|
+
return (scope) => ALWAYS_READABLE_SCOPES.has(scope) || definition.decide(built.model, subject, {
|
|
934
|
+
verb: "read",
|
|
935
|
+
scope
|
|
936
|
+
}, {
|
|
937
|
+
scopeState: scopeStates[scope],
|
|
938
|
+
actionInput: void 0
|
|
939
|
+
}).decision === "allow";
|
|
940
|
+
}
|
|
941
|
+
};
|
|
942
|
+
//#endregion
|
|
943
|
+
//#region src/client/util.ts
|
|
691
944
|
function authSubjectFromSigner(signer) {
|
|
692
945
|
return {
|
|
693
946
|
address: signer.user?.address,
|
|
694
947
|
key: signer.app?.key
|
|
695
948
|
};
|
|
696
949
|
}
|
|
697
|
-
function canReadScope(auth, subject, scope) {
|
|
698
|
-
return ALWAYS_READABLE_SCOPES.has(scope) || decide(auth, subject, {
|
|
699
|
-
verb: "read",
|
|
700
|
-
scope
|
|
701
|
-
}) === "allow";
|
|
702
|
-
}
|
|
703
950
|
function withAuthScope(view) {
|
|
704
951
|
if (view?.scopes && view.scopes.length > 0) return {
|
|
705
952
|
...view,
|
|
@@ -707,11 +954,14 @@ function withAuthScope(view) {
|
|
|
707
954
|
};
|
|
708
955
|
return view;
|
|
709
956
|
}
|
|
710
|
-
|
|
957
|
+
/**
|
|
958
|
+
* Drops the scopes the predicate refuses. The predicate is resolved by the read
|
|
959
|
+
* gate, which decides against the whole policy once per document; this only
|
|
960
|
+
* applies the answer, so it stays synchronous.
|
|
961
|
+
*/
|
|
962
|
+
function filterReadableScopes(document, readable) {
|
|
711
963
|
const state = document.state;
|
|
712
964
|
if (!state) return document;
|
|
713
|
-
const auth = document.state.auth;
|
|
714
|
-
const readable = (scope) => canReadScope(auth, subject, scope);
|
|
715
965
|
return {
|
|
716
966
|
...document,
|
|
717
967
|
state: keepReadableScopes(state, readable),
|
|
@@ -744,8 +994,9 @@ var ReactorClient = class {
|
|
|
744
994
|
jobAwaiter;
|
|
745
995
|
documentIndexer;
|
|
746
996
|
documentView;
|
|
997
|
+
readGate;
|
|
747
998
|
drives;
|
|
748
|
-
constructor(logger, reactor, signer, subscriptionManager, jobAwaiter, documentIndexer, documentView) {
|
|
999
|
+
constructor(logger, reactor, signer, subscriptionManager, jobAwaiter, documentIndexer, documentView, readGate = new BareReadGate()) {
|
|
749
1000
|
this.logger = logger;
|
|
750
1001
|
this.reactor = reactor;
|
|
751
1002
|
this.signer = signer;
|
|
@@ -753,6 +1004,7 @@ var ReactorClient = class {
|
|
|
753
1004
|
this.jobAwaiter = jobAwaiter;
|
|
754
1005
|
this.documentIndexer = documentIndexer;
|
|
755
1006
|
this.documentView = documentView;
|
|
1007
|
+
this.readGate = readGate;
|
|
756
1008
|
this.drives = new DriveClient(this, logger, reactor, signer);
|
|
757
1009
|
this.logger.verbose("ReactorClient initialized");
|
|
758
1010
|
}
|
|
@@ -760,6 +1012,25 @@ var ReactorClient = class {
|
|
|
760
1012
|
return subject ?? authSubjectFromSigner(this.signer);
|
|
761
1013
|
}
|
|
762
1014
|
/**
|
|
1015
|
+
* Which scopes of one document the subject may read. Resolved once per
|
|
1016
|
+
* document, so the gate builds its model once however many scopes are then
|
|
1017
|
+
* tested, and the filtering itself stays synchronous.
|
|
1018
|
+
*/
|
|
1019
|
+
readableScopes(document, view, signal) {
|
|
1020
|
+
return this.readGate.scopePredicate(document, this.readSubject(view?.subject), view?.branch ?? "main", signal);
|
|
1021
|
+
}
|
|
1022
|
+
/**
|
|
1023
|
+
* One document, filtered to the scopes the subject may read. Every method
|
|
1024
|
+
* that hands a document back goes through here, including the ones that
|
|
1025
|
+
* follow a write: a document returned from a mutation is a read like any
|
|
1026
|
+
* other, and returning it whole served scopes the same subject would be
|
|
1027
|
+
* refused by `get`. Its author still sees what it wrote, because an allow on
|
|
1028
|
+
* execute confers read of that scope.
|
|
1029
|
+
*/
|
|
1030
|
+
async gateDocument(document, view, signal) {
|
|
1031
|
+
return filterReadableScopes(document, await this.readableScopes(document, view, signal));
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
763
1034
|
* Retrieves a list of document model modules.
|
|
764
1035
|
*/
|
|
765
1036
|
async getDocumentModelModules(namespace, paging, signal) {
|
|
@@ -810,7 +1081,8 @@ var ReactorClient = class {
|
|
|
810
1081
|
*/
|
|
811
1082
|
async get(identifier, view, signal) {
|
|
812
1083
|
this.logger.verbose("get(@identifier, @view)", identifier, view);
|
|
813
|
-
|
|
1084
|
+
const document = await this.reactor.getByIdOrSlug(identifier, withAuthScope(view), void 0, signal);
|
|
1085
|
+
return this.gateDocument(document, view, signal);
|
|
814
1086
|
}
|
|
815
1087
|
/**
|
|
816
1088
|
* Resolves an identifier (id or slug) to the canonical document id, using the
|
|
@@ -826,12 +1098,8 @@ var ReactorClient = class {
|
|
|
826
1098
|
async getOperations(documentIdentifier, view, filter, paging, signal) {
|
|
827
1099
|
this.logger.verbose("getOperations(@documentIdentifier, @view, @filter, @paging)", documentIdentifier, view, filter, paging);
|
|
828
1100
|
const documentId = await this.documentView.resolveIdOrSlug(documentIdentifier, view, void 0, signal);
|
|
829
|
-
const
|
|
830
|
-
|
|
831
|
-
branch: view?.branch
|
|
832
|
-
}, void 0, signal);
|
|
833
|
-
const subject = this.readSubject(view?.subject);
|
|
834
|
-
const canRead = (scope) => canReadScope(authDoc?.state.auth, subject, scope);
|
|
1101
|
+
const gated = await this.reactor.getByIdOrSlug(documentId, withAuthScope(view), void 0, signal);
|
|
1102
|
+
const canRead = await this.readableScopes(gated, view, signal);
|
|
835
1103
|
if (paging?.cursor && isCompositeCursor(paging.cursor)) return this.getOperationsWithCompositeCursor(documentId, view, filter, paging, signal, canRead);
|
|
836
1104
|
const operationsByScope = await this.reactor.getOperations(documentId, view, filter, paging, void 0, signal);
|
|
837
1105
|
for (const scope of Object.keys(operationsByScope)) if (!canRead(scope)) delete operationsByScope[scope];
|
|
@@ -925,10 +1193,9 @@ var ReactorClient = class {
|
|
|
925
1193
|
async find(search, view, paging, signal) {
|
|
926
1194
|
this.logger.verbose("find(@search, @view, @paging)", search, view, paging);
|
|
927
1195
|
const results = await this.reactor.find(search, withAuthScope(view), paging, void 0, signal);
|
|
928
|
-
const readSubject = this.readSubject(view?.subject);
|
|
929
1196
|
return {
|
|
930
1197
|
...results,
|
|
931
|
-
results: results.results.map((doc) => filterReadableScopes(doc,
|
|
1198
|
+
results: await Promise.all(results.results.map(async (doc) => filterReadableScopes(doc, await this.readableScopes(doc, view, signal))))
|
|
932
1199
|
};
|
|
933
1200
|
}
|
|
934
1201
|
/**
|
|
@@ -982,7 +1249,8 @@ var ReactorClient = class {
|
|
|
982
1249
|
const batchResult = await this.reactor.executeBatch({ jobs }, signal);
|
|
983
1250
|
const completedJobs = await Promise.all(Object.values(batchResult.jobs).map((job) => this.waitForJob(job, signal)));
|
|
984
1251
|
for (const job of completedJobs) if (job.status === JobStatus.FAILED) throw new Error(job.error?.message);
|
|
985
|
-
|
|
1252
|
+
const created = await this.reactor.get(documentId);
|
|
1253
|
+
return this.gateDocument(created, void 0, signal);
|
|
986
1254
|
}
|
|
987
1255
|
/**
|
|
988
1256
|
* Creates an empty document and waits for completion
|
|
@@ -1029,7 +1297,7 @@ var ReactorClient = class {
|
|
|
1029
1297
|
const fromVersion = normalizeDocumentModelVersion(document.state.document?.version);
|
|
1030
1298
|
let targetVersion = toVersion;
|
|
1031
1299
|
if (targetVersion === void 0) targetVersion = normalizeDocumentModelVersion((await this.getDocumentModelModule(documentType)).version);
|
|
1032
|
-
if (targetVersion === fromVersion) return document;
|
|
1300
|
+
if (targetVersion === fromVersion) return this.gateDocument(document, { branch }, signal);
|
|
1033
1301
|
if (targetVersion < fromVersion) throw new DowngradeNotSupportedError(documentType, fromVersion, targetVersion);
|
|
1034
1302
|
const signedActions = await signActions([upgradeDocumentAction({
|
|
1035
1303
|
documentId,
|
|
@@ -1040,7 +1308,10 @@ var ReactorClient = class {
|
|
|
1040
1308
|
})], this.signer, signal);
|
|
1041
1309
|
const jobInfo = await this.reactor.execute(documentId, branch, signedActions, signal);
|
|
1042
1310
|
const completedJob = await this.waitForJob(jobInfo, signal);
|
|
1043
|
-
if (completedJob.status !== JobStatus.FAILED)
|
|
1311
|
+
if (completedJob.status !== JobStatus.FAILED) {
|
|
1312
|
+
const upgraded = await this.reactor.getByIdOrSlug(documentId, { branch }, completedJob.consistencyToken, signal);
|
|
1313
|
+
return this.gateDocument(upgraded, { branch }, signal);
|
|
1314
|
+
}
|
|
1044
1315
|
if (completedJob.error?.name !== "UpgradePreconditionFailedError") throw new Error(completedJob.error?.message);
|
|
1045
1316
|
lastConflictMessage = completedJob.error.message;
|
|
1046
1317
|
}
|
|
@@ -1066,7 +1337,8 @@ var ReactorClient = class {
|
|
|
1066
1337
|
const completedJob = await this.waitForJob(jobInfo, signal);
|
|
1067
1338
|
if (completedJob.status === JobStatus.FAILED) throw new Error(completedJob.error?.message);
|
|
1068
1339
|
const view = { branch };
|
|
1069
|
-
|
|
1340
|
+
const result = await this.reactor.getByIdOrSlug(documentIdentifier, view, completedJob.consistencyToken, signal);
|
|
1341
|
+
return this.gateDocument(result, view, signal);
|
|
1070
1342
|
}
|
|
1071
1343
|
/**
|
|
1072
1344
|
* Submits a list of actions to a document
|
|
@@ -1110,7 +1382,8 @@ var ReactorClient = class {
|
|
|
1110
1382
|
const jobInfo = await this.reactor.addRelationship(sourceIdentifier, targetIdentifier, relationshipType, branch, this.signer, signal);
|
|
1111
1383
|
const completedJob = await this.waitForJob(jobInfo, signal);
|
|
1112
1384
|
if (completedJob.status === JobStatus.FAILED) throw new Error(completedJob.error?.message);
|
|
1113
|
-
|
|
1385
|
+
const result = await this.reactor.getByIdOrSlug(sourceIdentifier, { branch }, completedJob.consistencyToken, signal);
|
|
1386
|
+
return this.gateDocument(result, { branch }, signal);
|
|
1114
1387
|
}
|
|
1115
1388
|
/**
|
|
1116
1389
|
* Removes a relationship between two documents and waits for completion.
|
|
@@ -1120,7 +1393,8 @@ var ReactorClient = class {
|
|
|
1120
1393
|
const jobInfo = await this.reactor.removeRelationship(sourceIdentifier, targetIdentifier, relationshipType, branch, this.signer, signal);
|
|
1121
1394
|
const completedJob = await this.waitForJob(jobInfo, signal);
|
|
1122
1395
|
if (completedJob.status === JobStatus.FAILED) throw new Error(completedJob.error?.message);
|
|
1123
|
-
|
|
1396
|
+
const result = await this.reactor.getByIdOrSlug(sourceIdentifier, { branch }, completedJob.consistencyToken, signal);
|
|
1397
|
+
return this.gateDocument(result, { branch }, signal);
|
|
1124
1398
|
}
|
|
1125
1399
|
/**
|
|
1126
1400
|
* Moves a relationship from one source document to another and waits for completion.
|
|
@@ -1133,9 +1407,11 @@ var ReactorClient = class {
|
|
|
1133
1407
|
const addJobInfo = await this.reactor.addRelationship(targetParentIdentifier, targetIdentifier, relationshipType, branch, this.signer, signal);
|
|
1134
1408
|
const addCompletedJob = await this.waitForJob(addJobInfo, signal);
|
|
1135
1409
|
if (addCompletedJob.status === JobStatus.FAILED) throw new Error(addCompletedJob.error?.message);
|
|
1410
|
+
const sourceResult = await this.reactor.getByIdOrSlug(sourceParentIdentifier, { branch }, removeCompletedJob.consistencyToken, signal);
|
|
1411
|
+
const targetResult = await this.reactor.getByIdOrSlug(targetParentIdentifier, { branch }, addCompletedJob.consistencyToken, signal);
|
|
1136
1412
|
return {
|
|
1137
|
-
source: await this.
|
|
1138
|
-
target: await this.
|
|
1413
|
+
source: await this.gateDocument(sourceResult, { branch }, signal),
|
|
1414
|
+
target: await this.gateDocument(targetResult, { branch }, signal)
|
|
1139
1415
|
};
|
|
1140
1416
|
}
|
|
1141
1417
|
async loadBatch(request, signal) {
|
|
@@ -1208,34 +1484,56 @@ var ReactorClient = class {
|
|
|
1208
1484
|
*/
|
|
1209
1485
|
subscribe(search, callback, view) {
|
|
1210
1486
|
this.logger.verbose("subscribe(@search, @view)", search, view);
|
|
1211
|
-
const
|
|
1212
|
-
|
|
1487
|
+
const readable = async (document) => filterReadableScopes(document, await this.readableScopes(document, view));
|
|
1488
|
+
let disposed = false;
|
|
1489
|
+
let delivering = Promise.resolve();
|
|
1490
|
+
/**
|
|
1491
|
+
* Queues one event behind those already queued. Gating an event resolves
|
|
1492
|
+
* asynchronously, so an event needing a group fetch would otherwise be
|
|
1493
|
+
* overtaken by the one behind it, and one still in flight would reach a
|
|
1494
|
+
* subscriber that has already unsubscribed. Only delivery is ordered, not
|
|
1495
|
+
* the gating: a single subscription can cover every document in the
|
|
1496
|
+
* reactor, so serializing that work would make its delivery rate the sum
|
|
1497
|
+
* of every gate build rather than the slowest. An event that cannot be
|
|
1498
|
+
* gated is withheld, because serving it unfiltered would leak the scopes
|
|
1499
|
+
* the gate did not clear -- but it is logged rather than swallowed, since
|
|
1500
|
+
* the gate rethrows a transient failure precisely so it is not read as a
|
|
1501
|
+
* denial.
|
|
1502
|
+
*/
|
|
1503
|
+
const deliver = (event) => {
|
|
1504
|
+
Promise.resolve(event).catch(() => void 0);
|
|
1505
|
+
delivering = delivering.then(async () => {
|
|
1506
|
+
const built = await event;
|
|
1507
|
+
if (disposed) return;
|
|
1508
|
+
callback(built);
|
|
1509
|
+
}).catch((error) => {
|
|
1510
|
+
this.logger.error("Subscription delivery failed for @search: @Error", { search }, error);
|
|
1511
|
+
});
|
|
1512
|
+
};
|
|
1213
1513
|
const unsubscribeCreated = this.subscriptionManager.onDocumentCreated((result) => {
|
|
1214
|
-
(async () => {
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
} catch {}
|
|
1222
|
-
})();
|
|
1514
|
+
deliver((async () => {
|
|
1515
|
+
const documents = await Promise.all(result.results.map((id) => this.reactor.get(id, withAuthScope(view), void 0, void 0)));
|
|
1516
|
+
return {
|
|
1517
|
+
type: DocumentChangeType.Created,
|
|
1518
|
+
documents: await Promise.all(documents.map(readable))
|
|
1519
|
+
};
|
|
1520
|
+
})());
|
|
1223
1521
|
}, search);
|
|
1224
1522
|
const unsubscribeDeleted = this.subscriptionManager.onDocumentDeleted((documentIds) => {
|
|
1225
|
-
|
|
1523
|
+
deliver({
|
|
1226
1524
|
type: DocumentChangeType.Deleted,
|
|
1227
1525
|
documents: [],
|
|
1228
1526
|
context: { childId: documentIds[0] }
|
|
1229
1527
|
});
|
|
1230
1528
|
}, search);
|
|
1231
1529
|
const unsubscribeUpdated = this.subscriptionManager.onDocumentStateUpdated((result) => {
|
|
1232
|
-
|
|
1530
|
+
deliver((async () => ({
|
|
1233
1531
|
type: DocumentChangeType.Updated,
|
|
1234
|
-
documents: result.results.map(readable)
|
|
1235
|
-
});
|
|
1532
|
+
documents: await Promise.all(result.results.map(readable))
|
|
1533
|
+
}))());
|
|
1236
1534
|
}, search, view);
|
|
1237
1535
|
const unsubscribeRelationship = this.subscriptionManager.onRelationshipChanged((parentId, childId, changeType) => {
|
|
1238
|
-
|
|
1536
|
+
deliver({
|
|
1239
1537
|
type: changeType === RelationshipChangeType.Added ? DocumentChangeType.ChildAdded : DocumentChangeType.ChildRemoved,
|
|
1240
1538
|
documents: [],
|
|
1241
1539
|
context: {
|
|
@@ -1245,6 +1543,7 @@ var ReactorClient = class {
|
|
|
1245
1543
|
});
|
|
1246
1544
|
}, search);
|
|
1247
1545
|
return () => {
|
|
1546
|
+
disposed = true;
|
|
1248
1547
|
unsubscribeCreated();
|
|
1249
1548
|
unsubscribeDeleted();
|
|
1250
1549
|
unsubscribeUpdated();
|
|
@@ -7116,7 +7415,7 @@ var ReactorBuilder = class {
|
|
|
7116
7415
|
}
|
|
7117
7416
|
async buildModule() {
|
|
7118
7417
|
if (!this.logger) this.logger = new ConsoleLogger(["reactor"]);
|
|
7119
|
-
|
|
7418
|
+
const featureFlags = resolveFeatureFlags(this.executorConfig.featureFlags);
|
|
7120
7419
|
if (this.projectionShardConfig !== void 0 && this.readModelFactories.length > 0) throw new Error("withProjectionShards does not support read models registered through withReadModelFactory; projection workers cannot receive host-only factory dependencies");
|
|
7121
7420
|
const resolvedSources = await resolveModelSources(this.documentModelSources);
|
|
7122
7421
|
if (this.workerPool) {
|
|
@@ -7175,7 +7474,7 @@ var ReactorBuilder = class {
|
|
|
7175
7474
|
await executorManager.start(executorStartCount);
|
|
7176
7475
|
const readModelInstances = Array.from(new Set([...this.readModels]));
|
|
7177
7476
|
const documentViewConsistencyTracker = new ConsistencyTracker();
|
|
7178
|
-
const documentView = new KyselyDocumentView(database, operationStore, operationIndex, writeCache, documentViewConsistencyTracker,
|
|
7477
|
+
const documentView = new KyselyDocumentView(database, operationStore, operationIndex, writeCache, documentViewConsistencyTracker, featureFlags.documentDecisions);
|
|
7179
7478
|
try {
|
|
7180
7479
|
await documentView.init();
|
|
7181
7480
|
} catch (error) {
|
|
@@ -7220,13 +7519,14 @@ var ReactorBuilder = class {
|
|
|
7220
7519
|
await syncModule.syncManager.startup();
|
|
7221
7520
|
}
|
|
7222
7521
|
let groupReevaluationTrigger;
|
|
7223
|
-
if (
|
|
7522
|
+
if (featureFlags.authGroups) {
|
|
7224
7523
|
groupReevaluationTrigger = new GroupReevaluationTrigger(this.logger, eventBus, queue, operationIndex);
|
|
7225
7524
|
groupReevaluationTrigger.startup();
|
|
7226
7525
|
}
|
|
7227
7526
|
const module = {
|
|
7228
7527
|
eventBus,
|
|
7229
7528
|
documentModelRegistry,
|
|
7529
|
+
featureFlags,
|
|
7230
7530
|
queue,
|
|
7231
7531
|
jobTracker,
|
|
7232
7532
|
executorManager,
|
|
@@ -7320,7 +7620,7 @@ var ReactorBuilder = class {
|
|
|
7320
7620
|
const [{ WorkerHandle }, { createThreadTransport }, { workerEntryPath }] = await Promise.all([
|
|
7321
7621
|
import("./worker-handle-CrERzl8s.js"),
|
|
7322
7622
|
import("./transport-ByGviWdZ.js"),
|
|
7323
|
-
import("./worker-
|
|
7623
|
+
import("./worker-BCMw-hBw.js").then((n) => n.n)
|
|
7324
7624
|
]);
|
|
7325
7625
|
const poolConfig = {
|
|
7326
7626
|
enabled: true,
|
|
@@ -7463,6 +7763,7 @@ var ReactorClientBuilder = class {
|
|
|
7463
7763
|
subscriptionManager;
|
|
7464
7764
|
jobAwaiter;
|
|
7465
7765
|
documentModelLoader;
|
|
7766
|
+
readGate;
|
|
7466
7767
|
/**
|
|
7467
7768
|
* Sets the logger for the ReactorClient.
|
|
7468
7769
|
* @param logger - The logger to use.
|
|
@@ -7515,6 +7816,29 @@ var ReactorClientBuilder = class {
|
|
|
7515
7816
|
this.documentModelLoader = loader;
|
|
7516
7817
|
return this;
|
|
7517
7818
|
}
|
|
7819
|
+
/**
|
|
7820
|
+
* Overrides how reads are gated. A client built from a ReactorBuilder derives
|
|
7821
|
+
* this from that reactor's flags; one built from `withReactor` cannot, because
|
|
7822
|
+
* it is handed no flags and no registry, so it gates on the policy alone
|
|
7823
|
+
* unless a gate is supplied here.
|
|
7824
|
+
*/
|
|
7825
|
+
withReadGate(readGate) {
|
|
7826
|
+
this.readGate = readGate;
|
|
7827
|
+
return this;
|
|
7828
|
+
}
|
|
7829
|
+
/**
|
|
7830
|
+
* The gate this reactor's flags call for. Below authEnforcement there is no
|
|
7831
|
+
* model to enforce -- the registered one ignores the auth scope -- so the
|
|
7832
|
+
* policy is evaluated on its own, which is what reads did before the model
|
|
7833
|
+
* existed. Group serving turns on with authGroups, because below it a
|
|
7834
|
+
* `{ group }` grant does not match, so a served roster is one no grant can
|
|
7835
|
+
* use.
|
|
7836
|
+
*/
|
|
7837
|
+
resolveReadGate(reactorModule, documentView) {
|
|
7838
|
+
if (!reactorModule) return new BareReadGate();
|
|
7839
|
+
const model = readDecisionModel(reactorModule.featureFlags, reactorModule.documentModelRegistry);
|
|
7840
|
+
return model === void 0 ? new BareReadGate() : new ModelReadGate(model, documentView, reactorModule.featureFlags.authGroups, reactorModule.operationIndex, this.logger);
|
|
7841
|
+
}
|
|
7518
7842
|
async build() {
|
|
7519
7843
|
return (await this.buildModule()).client;
|
|
7520
7844
|
}
|
|
@@ -7544,7 +7868,7 @@ var ReactorClientBuilder = class {
|
|
|
7544
7868
|
const subscriptionManager = this.subscriptionManager ?? reactorModule?.subscriptionManager ?? new ReactorSubscriptionManager(new DefaultSubscriptionErrorHandler());
|
|
7545
7869
|
const jobAwaiter = this.jobAwaiter ?? new JobAwaiter(eventBus, (jobId, signal) => reactor.getJobStatus(jobId, signal));
|
|
7546
7870
|
return {
|
|
7547
|
-
client: new ReactorClient(this.logger, reactor, signer, subscriptionManager, jobAwaiter, documentIndexer, documentView),
|
|
7871
|
+
client: new ReactorClient(this.logger, reactor, signer, subscriptionManager, jobAwaiter, documentIndexer, documentView, this.readGate ?? this.resolveReadGate(reactorModule, documentView)),
|
|
7548
7872
|
reactor,
|
|
7549
7873
|
eventBus,
|
|
7550
7874
|
documentIndexer,
|
|
@@ -7745,6 +8069,6 @@ var DocumentIntegrityService = class {
|
|
|
7745
8069
|
}
|
|
7746
8070
|
};
|
|
7747
8071
|
//#endregion
|
|
7748
|
-
export { APPEND_CONDITION_FAILED_PREFIX, AppendConditionFailedError, BaseReadModel, ChannelError, ChannelErrorSource, ChannelScheme, ConsistencyTracker, DEFAULT_DRIVE_CONTAINER_TYPES, DRIVE_AUTH_ERROR_MESSAGES, DefaultSubscriptionErrorHandler, DocumentChangeType, DocumentIntegrityService, DocumentModelRegistry, DocumentModelResolver, DriveClient, DriveCollectionId, DuplicateManifestError, DuplicateModuleError, DuplicateOperationError, EventBus, EventBusAggregateError, GqlRequestChannel, GqlRequestChannelFactory, GqlResponseChannel, GqlResponseChannelFactory, SimpleJobExecutor as InMemoryJobExecutor, SimpleJobExecutor, InMemoryJobTracker, InMemoryQueue, IntervalPollTimer, InvalidModuleError, JobAwaiter, JobExecutorEventTypes, JobStatus, KyselyDocumentIndexer, KyselyDocumentView, KyselyKeyframeStore, KyselyOperationStore, KyselySyncCursorStorage, KyselySyncRemoteStorage, KyselyWriteCache, Mailbox, ModuleNotFoundError, NullDocumentModelResolver, OptimisticLockError, PollBehavior, PollingChannelError, ProcessorManager, PropagationMode, QueueEventTypes, REACTOR_SCHEMA, Reactor, ReactorBuilder, ReactorClient, ReactorClientBuilder, ReactorEventTypes, ReactorSubscriptionManager, ReadModelCoordinator, RelationalDbProcessor, RelationshipChangeType, RetryAccounting, RevisionMismatchError, SimpleJobExecutorManager, SyncBuilder, SyncEventTypes, SyncOperation, SyncOperationAggregateError, SyncOperationStatus, SyncStatus, SyncStatusTracker, addRelationshipAction, authDecisionModel, batchOperationsByDocument, buildDecisionModel, classifyJobFailure, consolidateSyncOperations, createDocumentAction, createForwardingLogger, createMutableShutdownStatus, createRelationalDb, decideAtHead, deleteDocumentAction, documentActions, documentDecisionModel, driveIdFromUrl, envelopesToSyncOperations, errorToInfo, firstOutOfOrderPair, getMigrationStatus, instrumentPgPool, isDriveAuthError, makeConsistencyKey, parseDriveUrl, parsePagingOptions, quarantinesDocument, removeRelationshipAction, runMigrations, sanitizeArg, selectDecisionModel, supportsLiveReadModelRegistration, syncOperationErrorType, trimMailboxFromAckOrdinal, updateRelationshipAction, upgradeDocumentAction, workerEntryPath };
|
|
8072
|
+
export { ALWAYS_READABLE_SCOPES, APPEND_CONDITION_FAILED_PREFIX, AppendConditionFailedError, BareReadGate, BaseReadModel, ChannelError, ChannelErrorSource, ChannelScheme, ConsistencyTracker, DEFAULT_DRIVE_CONTAINER_TYPES, DRIVE_AUTH_ERROR_MESSAGES, DefaultSubscriptionErrorHandler, DocumentChangeType, DocumentIntegrityService, DocumentModelRegistry, DocumentModelResolver, DriveClient, DriveCollectionId, DuplicateManifestError, DuplicateModuleError, DuplicateOperationError, EventBus, EventBusAggregateError, GqlRequestChannel, GqlRequestChannelFactory, GqlResponseChannel, GqlResponseChannelFactory, SimpleJobExecutor as InMemoryJobExecutor, SimpleJobExecutor, InMemoryJobTracker, InMemoryQueue, IntervalPollTimer, InvalidModuleError, JobAwaiter, JobExecutorEventTypes, JobStatus, KyselyDocumentIndexer, KyselyDocumentView, KyselyKeyframeStore, KyselyOperationStore, KyselySyncCursorStorage, KyselySyncRemoteStorage, KyselyWriteCache, Mailbox, ModelReadGate, ModuleNotFoundError, NullDocumentModelResolver, OptimisticLockError, PollBehavior, PollingChannelError, ProcessorManager, PropagationMode, QueueEventTypes, REACTOR_SCHEMA, Reactor, ReactorBuilder, ReactorClient, ReactorClientBuilder, ReactorEventTypes, ReactorSubscriptionManager, ReadModelCoordinator, RelationalDbProcessor, RelationshipChangeType, RetryAccounting, RevisionMismatchError, SimpleJobExecutorManager, SyncBuilder, SyncEventTypes, SyncOperation, SyncOperationAggregateError, SyncOperationStatus, SyncStatus, SyncStatusTracker, addRelationshipAction, authDecisionModel, batchOperationsByDocument, buildDecisionModel, classifyJobFailure, consolidateSyncOperations, createDocumentAction, createForwardingLogger, createMutableShutdownStatus, createRelationalDb, decideAtHead, deleteDocumentAction, documentActions, documentDecisionModel, driveIdFromUrl, envelopesToSyncOperations, errorToInfo, firstOutOfOrderPair, getMigrationStatus, instrumentPgPool, isDriveAuthError, makeConsistencyKey, parseDriveUrl, parsePagingOptions, quarantinesDocument, readDecisionModel, removeRelationshipAction, runMigrations, sanitizeArg, selectDecisionModel, supportsLiveReadModelRegistration, syncOperationErrorType, trimMailboxFromAckOrdinal, updateRelationshipAction, upgradeDocumentAction, workerEntryPath };
|
|
7749
8073
|
|
|
7750
8074
|
//# sourceMappingURL=index.js.map
|