was-teaching-server 0.9.1
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/CHANGELOG.md +1432 -0
- package/LICENSE +661 -0
- package/README.md +305 -0
- package/common/css/materialize.1.0.0.min.css +13 -0
- package/dist/auth-header-hooks.d.ts +84 -0
- package/dist/auth-header-hooks.d.ts.map +1 -0
- package/dist/auth-header-hooks.js +136 -0
- package/dist/auth-header-hooks.js.map +1 -0
- package/dist/authorize.d.ts +45 -0
- package/dist/authorize.d.ts.map +1 -0
- package/dist/authorize.js +76 -0
- package/dist/authorize.js.map +1 -0
- package/dist/backends/filesystem.d.ts +1179 -0
- package/dist/backends/filesystem.d.ts.map +1 -0
- package/dist/backends/filesystem.js +2727 -0
- package/dist/backends/filesystem.js.map +1 -0
- package/dist/backends/postgres.d.ts +833 -0
- package/dist/backends/postgres.d.ts.map +1 -0
- package/dist/backends/postgres.js +2164 -0
- package/dist/backends/postgres.js.map +1 -0
- package/dist/backends/postgresSchema.d.ts +36 -0
- package/dist/backends/postgresSchema.d.ts.map +1 -0
- package/dist/backends/postgresSchema.js +203 -0
- package/dist/backends/postgresSchema.js.map +1 -0
- package/dist/config.default.d.ts +360 -0
- package/dist/config.default.d.ts.map +1 -0
- package/dist/config.default.js +493 -0
- package/dist/config.default.js.map +1 -0
- package/dist/corsProxy.d.ts +45 -0
- package/dist/corsProxy.d.ts.map +1 -0
- package/dist/corsProxy.js +278 -0
- package/dist/corsProxy.js.map +1 -0
- package/dist/digest.d.ts +45 -0
- package/dist/digest.d.ts.map +1 -0
- package/dist/digest.js +174 -0
- package/dist/digest.js.map +1 -0
- package/dist/errors.d.ts +654 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +960 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/atomicFile.d.ts +61 -0
- package/dist/lib/atomicFile.d.ts.map +1 -0
- package/dist/lib/atomicFile.js +150 -0
- package/dist/lib/atomicFile.js.map +1 -0
- package/dist/lib/backendRegistry.d.ts +59 -0
- package/dist/lib/backendRegistry.d.ts.map +1 -0
- package/dist/lib/backendRegistry.js +92 -0
- package/dist/lib/backendRegistry.js.map +1 -0
- package/dist/lib/backendUsage.d.ts +28 -0
- package/dist/lib/backendUsage.d.ts.map +1 -0
- package/dist/lib/backendUsage.js +55 -0
- package/dist/lib/backendUsage.js.map +1 -0
- package/dist/lib/backends.d.ts +142 -0
- package/dist/lib/backends.d.ts.map +1 -0
- package/dist/lib/backends.js +272 -0
- package/dist/lib/backends.js.map +1 -0
- package/dist/lib/blindedIndex.d.ts +156 -0
- package/dist/lib/blindedIndex.d.ts.map +1 -0
- package/dist/lib/blindedIndex.js +338 -0
- package/dist/lib/blindedIndex.js.map +1 -0
- package/dist/lib/collectionDescription.d.ts +33 -0
- package/dist/lib/collectionDescription.d.ts.map +1 -0
- package/dist/lib/collectionDescription.js +21 -0
- package/dist/lib/collectionDescription.js.map +1 -0
- package/dist/lib/cursor.d.ts +18 -0
- package/dist/lib/cursor.d.ts.map +1 -0
- package/dist/lib/cursor.js +50 -0
- package/dist/lib/cursor.js.map +1 -0
- package/dist/lib/edvEnvelope.d.ts +50 -0
- package/dist/lib/edvEnvelope.d.ts.map +1 -0
- package/dist/lib/edvEnvelope.js +106 -0
- package/dist/lib/edvEnvelope.js.map +1 -0
- package/dist/lib/encryption.d.ts +192 -0
- package/dist/lib/encryption.d.ts.map +1 -0
- package/dist/lib/encryption.js +383 -0
- package/dist/lib/encryption.js.map +1 -0
- package/dist/lib/etag.d.ts +33 -0
- package/dist/lib/etag.d.ts.map +1 -0
- package/dist/lib/etag.js +36 -0
- package/dist/lib/etag.js.map +1 -0
- package/dist/lib/exportManifest.d.ts +29 -0
- package/dist/lib/exportManifest.d.ts.map +1 -0
- package/dist/lib/exportManifest.js +77 -0
- package/dist/lib/exportManifest.js.map +1 -0
- package/dist/lib/importTar.d.ts +81 -0
- package/dist/lib/importTar.d.ts.map +1 -0
- package/dist/lib/importTar.js +301 -0
- package/dist/lib/importTar.js.map +1 -0
- package/dist/lib/isJson.d.ts +16 -0
- package/dist/lib/isJson.d.ts.map +1 -0
- package/dist/lib/isJson.js +17 -0
- package/dist/lib/isJson.js.map +1 -0
- package/dist/lib/keyEpoch.d.ts +38 -0
- package/dist/lib/keyEpoch.d.ts.map +1 -0
- package/dist/lib/keyEpoch.js +75 -0
- package/dist/lib/keyEpoch.js.map +1 -0
- package/dist/lib/keyedMutex.d.ts +28 -0
- package/dist/lib/keyedMutex.d.ts.map +1 -0
- package/dist/lib/keyedMutex.js +45 -0
- package/dist/lib/keyedMutex.js.map +1 -0
- package/dist/lib/kmsModule.d.ts +62 -0
- package/dist/lib/kmsModule.d.ts.map +1 -0
- package/dist/lib/kmsModule.js +327 -0
- package/dist/lib/kmsModule.js.map +1 -0
- package/dist/lib/kmsRecordCipher.d.ts +82 -0
- package/dist/lib/kmsRecordCipher.d.ts.map +1 -0
- package/dist/lib/kmsRecordCipher.js +226 -0
- package/dist/lib/kmsRecordCipher.js.map +1 -0
- package/dist/lib/pagination.d.ts +19 -0
- package/dist/lib/pagination.d.ts.map +1 -0
- package/dist/lib/pagination.js +21 -0
- package/dist/lib/pagination.js.map +1 -0
- package/dist/lib/paths.d.ts +273 -0
- package/dist/lib/paths.d.ts.map +1 -0
- package/dist/lib/paths.js +270 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/preconditions.d.ts +65 -0
- package/dist/lib/preconditions.d.ts.map +1 -0
- package/dist/lib/preconditions.js +112 -0
- package/dist/lib/preconditions.js.map +1 -0
- package/dist/lib/resourceFileName.d.ts +41 -0
- package/dist/lib/resourceFileName.d.ts.map +1 -0
- package/dist/lib/resourceFileName.js +58 -0
- package/dist/lib/resourceFileName.js.map +1 -0
- package/dist/lib/revocations.d.ts +49 -0
- package/dist/lib/revocations.d.ts.map +1 -0
- package/dist/lib/revocations.js +86 -0
- package/dist/lib/revocations.js.map +1 -0
- package/dist/lib/validateDid.d.ts +19 -0
- package/dist/lib/validateDid.d.ts.map +1 -0
- package/dist/lib/validateDid.js +35 -0
- package/dist/lib/validateDid.js.map +1 -0
- package/dist/lib/validateId.d.ts +66 -0
- package/dist/lib/validateId.d.ts.map +1 -0
- package/dist/lib/validateId.js +126 -0
- package/dist/lib/validateId.js.map +1 -0
- package/dist/plugin.d.ts +115 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +134 -0
- package/dist/plugin.js.map +1 -0
- package/dist/policy.d.ts +78 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +115 -0
- package/dist/policy.js.map +1 -0
- package/dist/provisioning.d.ts +38 -0
- package/dist/provisioning.d.ts.map +1 -0
- package/dist/provisioning.js +72 -0
- package/dist/provisioning.js.map +1 -0
- package/dist/requests/BackendRequest.d.ts +61 -0
- package/dist/requests/BackendRequest.d.ts.map +1 -0
- package/dist/requests/BackendRequest.js +152 -0
- package/dist/requests/BackendRequest.js.map +1 -0
- package/dist/requests/CollectionRequest.d.ts +234 -0
- package/dist/requests/CollectionRequest.d.ts.map +1 -0
- package/dist/requests/CollectionRequest.js +705 -0
- package/dist/requests/CollectionRequest.js.map +1 -0
- package/dist/requests/KeyRequest.d.ts +115 -0
- package/dist/requests/KeyRequest.d.ts.map +1 -0
- package/dist/requests/KeyRequest.js +468 -0
- package/dist/requests/KeyRequest.js.map +1 -0
- package/dist/requests/KeystoreRequest.d.ts +99 -0
- package/dist/requests/KeystoreRequest.d.ts.map +1 -0
- package/dist/requests/KeystoreRequest.js +247 -0
- package/dist/requests/KeystoreRequest.js.map +1 -0
- package/dist/requests/PolicyRequest.d.ts +53 -0
- package/dist/requests/PolicyRequest.d.ts.map +1 -0
- package/dist/requests/PolicyRequest.js +124 -0
- package/dist/requests/PolicyRequest.js.map +1 -0
- package/dist/requests/ResourceRequest.d.ts +134 -0
- package/dist/requests/ResourceRequest.d.ts.map +1 -0
- package/dist/requests/ResourceRequest.js +548 -0
- package/dist/requests/ResourceRequest.js.map +1 -0
- package/dist/requests/RevocationRequest.d.ts +67 -0
- package/dist/requests/RevocationRequest.d.ts.map +1 -0
- package/dist/requests/RevocationRequest.js +187 -0
- package/dist/requests/RevocationRequest.js.map +1 -0
- package/dist/requests/SpaceRequest.d.ts +204 -0
- package/dist/requests/SpaceRequest.d.ts.map +1 -0
- package/dist/requests/SpaceRequest.js +520 -0
- package/dist/requests/SpaceRequest.js.map +1 -0
- package/dist/requests/SpacesRepositoryRequest.d.ts +50 -0
- package/dist/requests/SpacesRepositoryRequest.d.ts.map +1 -0
- package/dist/requests/SpacesRepositoryRequest.js +159 -0
- package/dist/requests/SpacesRepositoryRequest.js.map +1 -0
- package/dist/requests/collectionContext.d.ts +21 -0
- package/dist/requests/collectionContext.d.ts.map +1 -0
- package/dist/requests/collectionContext.js +30 -0
- package/dist/requests/collectionContext.js.map +1 -0
- package/dist/requests/controllerConsent.d.ts +58 -0
- package/dist/requests/controllerConsent.d.ts.map +1 -0
- package/dist/requests/controllerConsent.js +73 -0
- package/dist/requests/controllerConsent.js.map +1 -0
- package/dist/requests/keystoreContext.d.ts +68 -0
- package/dist/requests/keystoreContext.d.ts.map +1 -0
- package/dist/requests/keystoreContext.js +87 -0
- package/dist/requests/keystoreContext.js.map +1 -0
- package/dist/requests/resourceInput.d.ts +19 -0
- package/dist/requests/resourceInput.d.ts.map +1 -0
- package/dist/requests/resourceInput.js +102 -0
- package/dist/requests/resourceInput.js.map +1 -0
- package/dist/requests/spaceContext.d.ts +127 -0
- package/dist/requests/spaceContext.d.ts.map +1 -0
- package/dist/requests/spaceContext.js +201 -0
- package/dist/requests/spaceContext.js.map +1 -0
- package/dist/routes.d.ts +55 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +306 -0
- package/dist/routes.js.map +1 -0
- package/dist/server.d.ts +20 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +60 -0
- package/dist/server.js.map +1 -0
- package/dist/start.d.ts +7 -0
- package/dist/start.d.ts.map +1 -0
- package/dist/start.js +61 -0
- package/dist/start.js.map +1 -0
- package/dist/storage.d.ts +29 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +47 -0
- package/dist/storage.js.map +1 -0
- package/dist/types.d.ts +915 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/views/home.hbs +9 -0
- package/dist/views/templates/main.hbs +12 -0
- package/dist/zcap.d.ts +267 -0
- package/dist/zcap.d.ts.map +1 -0
- package/dist/zcap.js +478 -0
- package/dist/zcap.js.map +1 -0
- package/package.json +109 -0
- package/src/auth-header-hooks.ts +163 -0
- package/src/authorize.ts +117 -0
- package/src/backends/filesystem.ts +3626 -0
- package/src/backends/postgres.ts +3073 -0
- package/src/backends/postgresSchema.ts +231 -0
- package/src/config.default.ts +657 -0
- package/src/corsProxy.ts +326 -0
- package/src/declarations.d.ts +28 -0
- package/src/digest.ts +223 -0
- package/src/errors.ts +1122 -0
- package/src/index.ts +21 -0
- package/src/lib/atomicFile.ts +176 -0
- package/src/lib/backendRegistry.ts +139 -0
- package/src/lib/backendUsage.ts +79 -0
- package/src/lib/backends.ts +351 -0
- package/src/lib/blindedIndex.ts +466 -0
- package/src/lib/collectionDescription.ts +43 -0
- package/src/lib/cursor.ts +52 -0
- package/src/lib/edvEnvelope.ts +120 -0
- package/src/lib/encryption.ts +472 -0
- package/src/lib/etag.ts +40 -0
- package/src/lib/exportManifest.ts +115 -0
- package/src/lib/importTar.ts +381 -0
- package/src/lib/isJson.ts +18 -0
- package/src/lib/keyEpoch.ts +89 -0
- package/src/lib/keyedMutex.ts +45 -0
- package/src/lib/kmsModule.ts +451 -0
- package/src/lib/kmsRecordCipher.ts +292 -0
- package/src/lib/pagination.ts +22 -0
- package/src/lib/paths.ts +382 -0
- package/src/lib/preconditions.ts +145 -0
- package/src/lib/resourceFileName.ts +69 -0
- package/src/lib/revocations.ts +116 -0
- package/src/lib/validateDid.ts +41 -0
- package/src/lib/validateId.ts +148 -0
- package/src/plugin.ts +288 -0
- package/src/policy.ts +176 -0
- package/src/provisioning.ts +98 -0
- package/src/requests/BackendRequest.ts +219 -0
- package/src/requests/CollectionRequest.ts +899 -0
- package/src/requests/KeyRequest.ts +617 -0
- package/src/requests/KeystoreRequest.ts +325 -0
- package/src/requests/PolicyRequest.ts +169 -0
- package/src/requests/ResourceRequest.ts +649 -0
- package/src/requests/RevocationRequest.ts +267 -0
- package/src/requests/SpaceRequest.ts +686 -0
- package/src/requests/SpacesRepositoryRequest.ts +194 -0
- package/src/requests/collectionContext.ts +41 -0
- package/src/requests/controllerConsent.ts +108 -0
- package/src/requests/keystoreContext.ts +124 -0
- package/src/requests/resourceInput.ts +118 -0
- package/src/requests/spaceContext.ts +311 -0
- package/src/routes.ts +420 -0
- package/src/server.ts +69 -0
- package/src/start.ts +71 -0
- package/src/storage.ts +60 -0
- package/src/types.ts +1016 -0
- package/src/views/home.hbs +9 -0
- package/src/views/templates/main.hbs +12 -0
- package/src/zcap.ts +657 -0
|
@@ -0,0 +1,833 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL persistence backend: stores Spaces, Collections, and Resources as
|
|
3
|
+
* rows (schema in postgresSchema.ts), and WebKMS keystores in a sibling table
|
|
4
|
+
* tree, implementing the same StorageBackend contract as the filesystem
|
|
5
|
+
* backend (types.ts). Selected by configuration (`DATABASE_URL`) and injected
|
|
6
|
+
* the same way (`createApp({ backend })`).
|
|
7
|
+
*
|
|
8
|
+
* Design departures from the filesystem backend (deliberate):
|
|
9
|
+
* - Quota accounting is transactional (`spaces.usage_bytes`, maintained in the
|
|
10
|
+
* same transaction as every write/delete), making the per-Space capacity a
|
|
11
|
+
* HARD limit under concurrency. "Usage" is exactly the stored content bytes;
|
|
12
|
+
* descriptions, policies, and metadata are not counted (a divergence from
|
|
13
|
+
* the filesystem's `du`, which counts every file).
|
|
14
|
+
* - Conditional writes use row locks (`SELECT ... FOR UPDATE`) and
|
|
15
|
+
* transactions instead of the single-process `KeyedMutex`, so two server
|
|
16
|
+
* processes sharing one database get correct conditional writes.
|
|
17
|
+
* - Blobs are buffered single-`bytea` writes bounded by `maxUploadBytes`; an
|
|
18
|
+
* unset cap defaults to `DEFAULT_MAX_UPLOAD_BYTES` rather than "unbounded"
|
|
19
|
+
* (unbounded buffering into a `bytea` is a footgun). Chunked-row streaming
|
|
20
|
+
* is a planned follow-up increment.
|
|
21
|
+
* - `exportSpace` / `importSpace` speak the same tar dialect as the
|
|
22
|
+
* filesystem backend (same file-name codecs, same manifest), so archives
|
|
23
|
+
* migrate between the two backends in either direction; the Postgres import
|
|
24
|
+
* apply loop additionally runs in a single transaction (atomic rollback).
|
|
25
|
+
*/
|
|
26
|
+
import { Readable } from 'node:stream';
|
|
27
|
+
import type { FastifyBaseLogger } from 'fastify';
|
|
28
|
+
import type { BlindedIndexQuery, BlindedIndexQueryPage } from '../lib/blindedIndex.js';
|
|
29
|
+
import type { SpaceDescription, CollectionDescription, CollectionSummary, CollectionResourcesList, ResourceResult, ResourceMetadata, ResourceMetadataCustom, ResourceInput, ImportStats, PolicyDocument, BackendDescriptor, BackendUsage, StorageBackend, StoredBackendRecord, KeystoreConfig, KmsKeyRecord, RevocationRecord, RevocationScope, CapabilitySummary, IDID } from '../types.js';
|
|
30
|
+
export declare class PostgresBackend implements StorageBackend {
|
|
31
|
+
logger: FastifyBaseLogger;
|
|
32
|
+
/**
|
|
33
|
+
* Per-Space storage capacity, in bytes (spec "Quotas"). `undefined` means no
|
|
34
|
+
* configured limit. Unlike the filesystem backend's `du`-sampled soft limit,
|
|
35
|
+
* this is enforced transactionally on every content write -- a HARD limit
|
|
36
|
+
* under concurrency.
|
|
37
|
+
*/
|
|
38
|
+
capacityBytes?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Largest single upload accepted, in bytes (spec "Quotas",
|
|
41
|
+
* `maxUploadBytes`). Always set on this backend: an unconfigured cap
|
|
42
|
+
* defaults to `DEFAULT_MAX_UPLOAD_BYTES`, because every blob write buffers
|
|
43
|
+
* through memory on the single-`bytea` path.
|
|
44
|
+
*/
|
|
45
|
+
maxUploadBytes: number;
|
|
46
|
+
/**
|
|
47
|
+
* Max Spaces a single controller may create (spec "Quotas", a default-on
|
|
48
|
+
* count quota). `undefined` means no cap. Enforced transactionally on the
|
|
49
|
+
* Space create path (`writeSpace`), serialized per controller by an advisory
|
|
50
|
+
* lock -- a HARD limit under concurrency, like the byte quota. The
|
|
51
|
+
* constructor normalizes an unset option to
|
|
52
|
+
* {@link DEFAULT_MAX_SPACES_PER_CONTROLLER} and a non-finite option
|
|
53
|
+
* (`Infinity`) to `undefined`.
|
|
54
|
+
*/
|
|
55
|
+
maxSpacesPerController?: number;
|
|
56
|
+
/**
|
|
57
|
+
* Max Collections a single Space may hold (spec "Quotas", a default-on count
|
|
58
|
+
* quota). `undefined` means no cap. Enforced on the Collection create path,
|
|
59
|
+
* serialized per Space by the space row lock. Normalized like
|
|
60
|
+
* {@link maxSpacesPerController}.
|
|
61
|
+
*/
|
|
62
|
+
maxCollectionsPerSpace?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Max live Resources a single Space may hold across all its Collections (spec
|
|
65
|
+
* "Quotas", a default-on count quota). `undefined` means no cap. Enforced on
|
|
66
|
+
* the Resource create path (a tombstone does not count). Normalized like
|
|
67
|
+
* {@link maxSpacesPerController}.
|
|
68
|
+
*/
|
|
69
|
+
maxResourcesPerSpace?: number;
|
|
70
|
+
private _pool;
|
|
71
|
+
private _schema?;
|
|
72
|
+
/**
|
|
73
|
+
* @param options {object}
|
|
74
|
+
* @param options.connectionString {string} a `postgres://` URL
|
|
75
|
+
* @param [options.schema] {string} Postgres schema to operate in (set as
|
|
76
|
+
* the connection `search_path`; created by `init()` if absent). Used for
|
|
77
|
+
* test isolation; production uses the default `public`.
|
|
78
|
+
* @param [options.logger] {FastifyBaseLogger}
|
|
79
|
+
* @param [options.capacityBytes] {number} per-Space quota in bytes; a
|
|
80
|
+
* finite value is enforced, `undefined` or a non-finite value means no
|
|
81
|
+
* configured limit
|
|
82
|
+
* @param [options.maxUploadBytes] {number} per-upload cap in bytes;
|
|
83
|
+
* `undefined` applies the `DEFAULT_MAX_UPLOAD_BYTES` default. A non-finite
|
|
84
|
+
* value (`Infinity`, from `MAX_UPLOAD_BYTES=unlimited`) throws: this backend
|
|
85
|
+
* buffers each upload in memory as a single `bytea`, so an unbounded cap is
|
|
86
|
+
* not supported.
|
|
87
|
+
* @param [options.maxSpacesPerController] {number} max Spaces per
|
|
88
|
+
* controller (spec "Quotas"); `undefined` applies the default-on limit,
|
|
89
|
+
* `Infinity` means no cap
|
|
90
|
+
* @param [options.maxCollectionsPerSpace] {number} max Collections per
|
|
91
|
+
* Space; `undefined` applies the default-on limit, `Infinity` means no cap
|
|
92
|
+
* @param [options.maxResourcesPerSpace] {number} max live Resources per
|
|
93
|
+
* Space; `undefined` applies the default-on limit, `Infinity` means no cap
|
|
94
|
+
*/
|
|
95
|
+
constructor({ connectionString, schema, logger, capacityBytes, maxUploadBytes, maxSpacesPerController, maxCollectionsPerSpace, maxResourcesPerSpace }: {
|
|
96
|
+
connectionString: string;
|
|
97
|
+
schema?: string;
|
|
98
|
+
logger?: FastifyBaseLogger;
|
|
99
|
+
capacityBytes?: number;
|
|
100
|
+
maxUploadBytes?: number;
|
|
101
|
+
maxSpacesPerController?: number;
|
|
102
|
+
maxCollectionsPerSpace?: number;
|
|
103
|
+
maxResourcesPerSpace?: number;
|
|
104
|
+
});
|
|
105
|
+
/**
|
|
106
|
+
* Connects and applies schema migrations (idempotent, advisory-locked; see
|
|
107
|
+
* postgresSchema.ts). Called once by the `createApp` composition before the
|
|
108
|
+
* server starts listening.
|
|
109
|
+
* @returns {Promise<void>}
|
|
110
|
+
*/
|
|
111
|
+
init(): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* Drains the connection pool. Wired to the Fastify `onClose` hook by the
|
|
114
|
+
* plugin composition.
|
|
115
|
+
* @returns {Promise<void>}
|
|
116
|
+
*/
|
|
117
|
+
close(): Promise<void>;
|
|
118
|
+
/**
|
|
119
|
+
* Runs `fn` inside one transaction on a dedicated client, committing on
|
|
120
|
+
* success and rolling back on any throw.
|
|
121
|
+
* @param fn {(client: pg.PoolClient) => Promise<T>}
|
|
122
|
+
* @returns {Promise<T>}
|
|
123
|
+
*/
|
|
124
|
+
private _withTransaction;
|
|
125
|
+
/**
|
|
126
|
+
* Self-description advertised at `GET /space/:spaceId/backends`. Same
|
|
127
|
+
* affordances as the filesystem backend: conditional writes (ETag
|
|
128
|
+
* validators, row-locked preconditions) and the `changes` and
|
|
129
|
+
* `blinded-index` query profiles.
|
|
130
|
+
* @returns {Required<Omit<BackendDescriptor, 'provider' | 'connection'>>}
|
|
131
|
+
*/
|
|
132
|
+
describe(): Required<Omit<BackendDescriptor, 'provider' | 'connection'>>;
|
|
133
|
+
/**
|
|
134
|
+
* Ensures the `spaces` row for `spaceId` exists (a placeholder with a NULL
|
|
135
|
+
* description when the Space was never described -- the analogue of the
|
|
136
|
+
* filesystem creating a Space directory on a sub-Space write).
|
|
137
|
+
* @param options {object}
|
|
138
|
+
* @param options.client {pg.PoolClient}
|
|
139
|
+
* @param options.spaceId {string}
|
|
140
|
+
* @returns {Promise<void>}
|
|
141
|
+
*/
|
|
142
|
+
private _ensureSpaceRow;
|
|
143
|
+
/**
|
|
144
|
+
* Ensures the `collections` row (and its parent `spaces` row) exists,
|
|
145
|
+
* placeholder-description like `_ensureSpaceRow`.
|
|
146
|
+
* @param options {object}
|
|
147
|
+
* @param options.client {pg.PoolClient}
|
|
148
|
+
* @param options.spaceId {string}
|
|
149
|
+
* @param options.collectionId {string}
|
|
150
|
+
* @returns {Promise<void>}
|
|
151
|
+
*/
|
|
152
|
+
private _ensureCollectionRow;
|
|
153
|
+
/**
|
|
154
|
+
* Applies a usage delta to the Space's transactional quota counter,
|
|
155
|
+
* enforcing the configured capacity in the same statement (the hard-limit
|
|
156
|
+
* departure from the filesystem's `du`-sampled soft check). Zero rows
|
|
157
|
+
* updated with the row present means the write would not fit:
|
|
158
|
+
* `QuotaExceededError` (507), rolling back the enclosing transaction.
|
|
159
|
+
* MUST run inside the same transaction as the content mutation.
|
|
160
|
+
* @param options {object}
|
|
161
|
+
* @param options.client {pg.PoolClient}
|
|
162
|
+
* @param options.spaceId {string}
|
|
163
|
+
* @param options.delta {number} signed byte delta (new minus old size)
|
|
164
|
+
* @returns {Promise<void>}
|
|
165
|
+
*/
|
|
166
|
+
private _applyUsageDelta;
|
|
167
|
+
/**
|
|
168
|
+
* Reports the Space's usage from the transactional counter (no measurement
|
|
169
|
+
* pass, no cache). The per-Collection breakdown is computed on demand with
|
|
170
|
+
* one aggregate query.
|
|
171
|
+
* @param options {object}
|
|
172
|
+
* @param options.spaceId {string}
|
|
173
|
+
* @param [options.includeCollections] {boolean}
|
|
174
|
+
* @returns {Promise<BackendUsage>}
|
|
175
|
+
*/
|
|
176
|
+
reportUsage({ spaceId, includeCollections }: {
|
|
177
|
+
spaceId: string;
|
|
178
|
+
includeCollections?: boolean;
|
|
179
|
+
}): Promise<BackendUsage>;
|
|
180
|
+
/**
|
|
181
|
+
* Reports a single Collection's usage (its `SUM(size_bytes)` slice), with
|
|
182
|
+
* `state` / `limit` derived from the Space total (the quota is a per-Space
|
|
183
|
+
* limit).
|
|
184
|
+
* @param options {object}
|
|
185
|
+
* @param options.spaceId {string}
|
|
186
|
+
* @param options.collectionId {string}
|
|
187
|
+
* @returns {Promise<BackendUsage>}
|
|
188
|
+
*/
|
|
189
|
+
reportCollectionUsage({ spaceId, collectionId }: {
|
|
190
|
+
spaceId: string;
|
|
191
|
+
collectionId: string;
|
|
192
|
+
}): Promise<BackendUsage>;
|
|
193
|
+
/**
|
|
194
|
+
* Builds the shared quota-report condition fields (see
|
|
195
|
+
* `lib/backendUsage.ts`).
|
|
196
|
+
* @param options {object}
|
|
197
|
+
* @param options.usageBytes {number}
|
|
198
|
+
* @param options.spaceTotalBytes {number}
|
|
199
|
+
* @returns {Omit<BackendUsage, 'measuredAt' | 'usageByCollection'>}
|
|
200
|
+
*/
|
|
201
|
+
private _backendUsageFields;
|
|
202
|
+
/**
|
|
203
|
+
* @param options {object}
|
|
204
|
+
* @param options.spaceId {string}
|
|
205
|
+
* @param options.spaceDescription {SpaceDescription}
|
|
206
|
+
* @param [options.createdBy] {string} DID of the invoker, recorded as the
|
|
207
|
+
* Space's `createdBy` on first write only
|
|
208
|
+
* @returns {Promise<void>}
|
|
209
|
+
*/
|
|
210
|
+
writeSpace({ spaceId, spaceDescription, createdBy }: {
|
|
211
|
+
spaceId: string;
|
|
212
|
+
spaceDescription: SpaceDescription;
|
|
213
|
+
createdBy?: IDID;
|
|
214
|
+
}): Promise<void>;
|
|
215
|
+
/**
|
|
216
|
+
* @param options {object}
|
|
217
|
+
* @param options.spaceId {string}
|
|
218
|
+
* @returns {Promise<SpaceDescription|undefined>} falsy when the Space does
|
|
219
|
+
* not exist or is a placeholder row without a description
|
|
220
|
+
*/
|
|
221
|
+
getSpaceDescription({ spaceId }: {
|
|
222
|
+
spaceId: string;
|
|
223
|
+
}): Promise<SpaceDescription | undefined>;
|
|
224
|
+
/**
|
|
225
|
+
* Deletes the Space row; collections, resources, policies, and backend
|
|
226
|
+
* records go with it via `ON DELETE CASCADE` (keystores are a sibling tree
|
|
227
|
+
* and are deliberately untouched). Idempotent.
|
|
228
|
+
* @param options {object}
|
|
229
|
+
* @param options.spaceId {string}
|
|
230
|
+
* @returns {Promise<void>}
|
|
231
|
+
*/
|
|
232
|
+
deleteSpace({ spaceId }: {
|
|
233
|
+
spaceId: string;
|
|
234
|
+
}): Promise<void>;
|
|
235
|
+
/**
|
|
236
|
+
* Every described Space, sorted by id (byte order via `COLLATE "C"`).
|
|
237
|
+
* Placeholder rows without a description are skipped, like a Space dir
|
|
238
|
+
* without a readable description file.
|
|
239
|
+
* @returns {Promise<SpaceDescription[]>}
|
|
240
|
+
*/
|
|
241
|
+
listSpaces(): Promise<SpaceDescription[]>;
|
|
242
|
+
/**
|
|
243
|
+
* @param options {object}
|
|
244
|
+
* @param options.spaceId {string}
|
|
245
|
+
* @param options.collectionId {string}
|
|
246
|
+
* @param options.collectionDescription {CollectionDescription}
|
|
247
|
+
* @param [options.createdBy] {string} DID of the invoker, recorded as the
|
|
248
|
+
* Collection's `createdBy` on first write only
|
|
249
|
+
* @param [options.ifMatch] {string} an `If-Match` compare-and-swap on the
|
|
250
|
+
* current description version (the `key-epochs` feature); a stale validator
|
|
251
|
+
* throws `PreconditionFailedError` (412)
|
|
252
|
+
* @returns {Promise<{ version: number }>} the Collection's new description
|
|
253
|
+
* version (the `ETag` validator)
|
|
254
|
+
*/
|
|
255
|
+
writeCollection({ spaceId, collectionId, collectionDescription, createdBy, ifMatch, assertTransition }: {
|
|
256
|
+
spaceId: string;
|
|
257
|
+
collectionId: string;
|
|
258
|
+
collectionDescription: CollectionDescription;
|
|
259
|
+
createdBy?: IDID;
|
|
260
|
+
ifMatch?: string;
|
|
261
|
+
assertTransition?: (prior?: CollectionDescription & {
|
|
262
|
+
descriptionVersion?: number;
|
|
263
|
+
}) => void;
|
|
264
|
+
}): Promise<{
|
|
265
|
+
version: number;
|
|
266
|
+
}>;
|
|
267
|
+
/**
|
|
268
|
+
* The one Collection-description upsert statement, shared by
|
|
269
|
+
* `writeCollection` and the import apply loop. `createdBy` is resolved
|
|
270
|
+
* within this same statement, in one round trip (no separate read-then-write
|
|
271
|
+
* race with a concurrent write for the same id):
|
|
272
|
+
* - `collections.description IS NULL` means there is no prior description
|
|
273
|
+
* row (a placeholder row created by a sub-Resource write before any
|
|
274
|
+
* Collection Description was written) -- this write IS the create, so it
|
|
275
|
+
* behaves like the insert branch: attach the `createdBy` parameter when
|
|
276
|
+
* present, otherwise omit the key entirely (never store it as JSON
|
|
277
|
+
* `null`).
|
|
278
|
+
* - Otherwise a prior description exists, and its `createdBy` -- present or
|
|
279
|
+
* absent -- is preserved verbatim via the jsonb `?` key-existence
|
|
280
|
+
* operator; the `createdBy` parameter is ignored entirely (never
|
|
281
|
+
* backfilled).
|
|
282
|
+
* Any `createdBy` embedded in `collectionDescription` itself is discarded
|
|
283
|
+
* here: `writeCollection` passes the invoker DID via the `createdBy`
|
|
284
|
+
* parameter instead, and the import apply loop passes the imported
|
|
285
|
+
* document's own `createdBy` so a restored Collection keeps its original
|
|
286
|
+
* creator (that import call is always a create -- the caller skips existing
|
|
287
|
+
* Collections -- so there is no prior row to preserve instead).
|
|
288
|
+
* @param options {object}
|
|
289
|
+
* @param options.queryable {Queryable}
|
|
290
|
+
* @param options.spaceId {string}
|
|
291
|
+
* @param options.collectionId {string}
|
|
292
|
+
* @param options.collectionDescription {CollectionDescription}
|
|
293
|
+
* @param [options.createdBy] {string} resolved creator to fall back to
|
|
294
|
+
* when there is no prior row
|
|
295
|
+
* @param [options.descriptionVersion] {number} the monotonic description
|
|
296
|
+
* version to stamp (the `ETag` validator; the `key-epochs` feature), kept
|
|
297
|
+
* OUT of the `description` jsonb -- it lives in the `description_version`
|
|
298
|
+
* column and travels only as the `ETag` header. When omitted (the import
|
|
299
|
+
* path), a `_version` on the incoming archived description is used, else 1.
|
|
300
|
+
* @returns {Promise<void>}
|
|
301
|
+
*/
|
|
302
|
+
private _upsertCollection;
|
|
303
|
+
/**
|
|
304
|
+
* @param options {object}
|
|
305
|
+
* @param options.spaceId {string}
|
|
306
|
+
* @param options.collectionId {string}
|
|
307
|
+
* @returns {Promise<(CollectionDescription & { descriptionVersion?: number })
|
|
308
|
+
* | undefined>} `descriptionVersion` is the out-of-band `ETag` validator.
|
|
309
|
+
*/
|
|
310
|
+
getCollectionDescription({ spaceId, collectionId }: {
|
|
311
|
+
spaceId: string;
|
|
312
|
+
collectionId: string;
|
|
313
|
+
}): Promise<(CollectionDescription & {
|
|
314
|
+
descriptionVersion?: number;
|
|
315
|
+
}) | undefined>;
|
|
316
|
+
/**
|
|
317
|
+
* Deletes the Collection row (resources cascade), its policies, and
|
|
318
|
+
* subtracts the Collection's stored bytes from the Space usage counter --
|
|
319
|
+
* all in one transaction. Idempotent.
|
|
320
|
+
* @param options {object}
|
|
321
|
+
* @param options.spaceId {string}
|
|
322
|
+
* @param options.collectionId {string}
|
|
323
|
+
* @returns {Promise<void>}
|
|
324
|
+
*/
|
|
325
|
+
deleteCollection({ spaceId, collectionId }: {
|
|
326
|
+
spaceId: string;
|
|
327
|
+
collectionId: string;
|
|
328
|
+
}): Promise<void>;
|
|
329
|
+
/**
|
|
330
|
+
* Every Collection row in the Space, sorted by id. A row without a
|
|
331
|
+
* description (created by a sub-Collection write) falls back to the id for
|
|
332
|
+
* its `name`, like a description-less directory on the filesystem.
|
|
333
|
+
* @param options {object}
|
|
334
|
+
* @param options.spaceId {string}
|
|
335
|
+
* @returns {Promise<CollectionSummary[]>}
|
|
336
|
+
*/
|
|
337
|
+
listCollections({ spaceId }: {
|
|
338
|
+
spaceId: string;
|
|
339
|
+
}): Promise<CollectionSummary[]>;
|
|
340
|
+
/**
|
|
341
|
+
* Lists a Collection's Resources, cursor-paginated with the same keyset
|
|
342
|
+
* (ascending `resourceId`, byte order), cursor codec, clamps, and `next`
|
|
343
|
+
* construction as the filesystem backend. Tombstones are invisible.
|
|
344
|
+
* @param options {object}
|
|
345
|
+
* @param options.spaceId {string}
|
|
346
|
+
* @param options.collectionId {string}
|
|
347
|
+
* @param [options.limit] {number}
|
|
348
|
+
* @param [options.cursor] {string}
|
|
349
|
+
* @param [options.collectionDescription] {CollectionDescription}
|
|
350
|
+
* @returns {Promise<CollectionResourcesList>}
|
|
351
|
+
*/
|
|
352
|
+
listCollectionItems({ spaceId, collectionId, limit, cursor, collectionDescription: providedDescription }: {
|
|
353
|
+
spaceId: string;
|
|
354
|
+
collectionId: string;
|
|
355
|
+
limit?: number;
|
|
356
|
+
cursor?: string;
|
|
357
|
+
collectionDescription?: CollectionDescription;
|
|
358
|
+
}): Promise<CollectionResourcesList>;
|
|
359
|
+
/**
|
|
360
|
+
* Writes a Resource representation as one transaction: row lock, shared
|
|
361
|
+
* precondition evaluation (exact filesystem semantics -- a tombstone counts
|
|
362
|
+
* as "not exists", `ifNoneMatch` precedence per RFC9110), monotonic
|
|
363
|
+
* `version` bump continuing through delete/recreate, and the transactional
|
|
364
|
+
* quota delta. JSON is stored as its serialized UTF-8 bytes; blobs buffer
|
|
365
|
+
* through the capped accumulator.
|
|
366
|
+
* @param options {object}
|
|
367
|
+
* @param options.spaceId {string}
|
|
368
|
+
* @param options.collectionId {string}
|
|
369
|
+
* @param options.resourceId {string}
|
|
370
|
+
* @param options.input {ResourceInput}
|
|
371
|
+
* @param [options.createdBy] {string} DID of the invoker, recorded as the
|
|
372
|
+
* Resource's `createdBy` on first write only
|
|
373
|
+
* @param [options.ifMatch] {string}
|
|
374
|
+
* @param [options.ifNoneMatch] {boolean}
|
|
375
|
+
* @returns {Promise<{ version: number }>}
|
|
376
|
+
*/
|
|
377
|
+
writeResource({ spaceId, collectionId, resourceId, input, createdBy, epoch, ifMatch, ifNoneMatch }: {
|
|
378
|
+
spaceId: string;
|
|
379
|
+
collectionId: string;
|
|
380
|
+
resourceId: string;
|
|
381
|
+
input: ResourceInput;
|
|
382
|
+
createdBy?: IDID;
|
|
383
|
+
epoch?: string;
|
|
384
|
+
ifMatch?: string;
|
|
385
|
+
ifNoneMatch?: boolean;
|
|
386
|
+
}): Promise<{
|
|
387
|
+
version: number;
|
|
388
|
+
}>;
|
|
389
|
+
/**
|
|
390
|
+
* @param options {object}
|
|
391
|
+
* @param options.spaceId {string}
|
|
392
|
+
* @param options.collectionId {string}
|
|
393
|
+
* @param options.resourceId {string}
|
|
394
|
+
* @param [options.contentType] {string} advisory; ignored for lookup
|
|
395
|
+
* @returns {Promise<ResourceResult>}
|
|
396
|
+
*/
|
|
397
|
+
getResource({ spaceId, collectionId, resourceId }: {
|
|
398
|
+
spaceId: string;
|
|
399
|
+
collectionId: string;
|
|
400
|
+
resourceId: string;
|
|
401
|
+
contentType?: string;
|
|
402
|
+
}): Promise<ResourceResult>;
|
|
403
|
+
/**
|
|
404
|
+
* Soft-deletes a Resource into a tombstone row: content dropped, `deleted`
|
|
405
|
+
* set, `version` bumped (so the change feed surfaces it), last-known
|
|
406
|
+
* `content_type` retained, `custom` / `meta_version` dropped, and the freed
|
|
407
|
+
* bytes subtracted from the quota counter -- one transaction. Idempotent on
|
|
408
|
+
* an absent Resource or an existing tombstone.
|
|
409
|
+
* @param options {object}
|
|
410
|
+
* @param options.spaceId {string}
|
|
411
|
+
* @param options.collectionId {string}
|
|
412
|
+
* @param options.resourceId {string}
|
|
413
|
+
* @param [options.ifMatch] {string}
|
|
414
|
+
* @returns {Promise<void>}
|
|
415
|
+
*/
|
|
416
|
+
deleteResource({ spaceId, collectionId, resourceId, ifMatch }: {
|
|
417
|
+
spaceId: string;
|
|
418
|
+
collectionId: string;
|
|
419
|
+
resourceId: string;
|
|
420
|
+
ifMatch?: string;
|
|
421
|
+
}): Promise<void>;
|
|
422
|
+
/**
|
|
423
|
+
* Reads the metadata of a Resource's current representation. Tombstones and
|
|
424
|
+
* absent Resources resolve `undefined`. `custom` is included only when
|
|
425
|
+
* non-empty, verbatim (`{ name, tags }` or the opaque envelope).
|
|
426
|
+
* @param options {object}
|
|
427
|
+
* @param options.spaceId {string}
|
|
428
|
+
* @param options.collectionId {string}
|
|
429
|
+
* @param options.resourceId {string}
|
|
430
|
+
* @returns {Promise<(ResourceMetadata & { createdBy?: IDID; version?:
|
|
431
|
+
* number; metaVersion?: number }) | undefined>}
|
|
432
|
+
*/
|
|
433
|
+
getResourceMetadata({ spaceId, collectionId, resourceId }: {
|
|
434
|
+
spaceId: string;
|
|
435
|
+
collectionId: string;
|
|
436
|
+
resourceId: string;
|
|
437
|
+
}): Promise<(ResourceMetadata & {
|
|
438
|
+
createdBy?: IDID;
|
|
439
|
+
version?: number;
|
|
440
|
+
metaVersion?: number;
|
|
441
|
+
}) | undefined>;
|
|
442
|
+
/**
|
|
443
|
+
* Replaces the user-writable `custom` object (full replacement; `{}`
|
|
444
|
+
* clears), bumping `updatedAt` and the independent `metaVersion` -- one
|
|
445
|
+
* row-locked transaction, preconditions evaluated on the current
|
|
446
|
+
* `metaVersion` via the shared helper. Resolves `undefined` (no create) for
|
|
447
|
+
* an absent or tombstoned Resource.
|
|
448
|
+
* @param options {object}
|
|
449
|
+
* @param options.spaceId {string}
|
|
450
|
+
* @param options.collectionId {string}
|
|
451
|
+
* @param options.resourceId {string}
|
|
452
|
+
* @param options.custom {ResourceMetadataCustom | Record<string, unknown>}
|
|
453
|
+
* @param [options.ifMatch] {string}
|
|
454
|
+
* @param [options.ifNoneMatch] {boolean}
|
|
455
|
+
* @returns {Promise<{ metaVersion: number } | undefined>}
|
|
456
|
+
*/
|
|
457
|
+
writeResourceMetadata({ spaceId, collectionId, resourceId, custom, epoch, ifMatch, ifNoneMatch }: {
|
|
458
|
+
spaceId: string;
|
|
459
|
+
collectionId: string;
|
|
460
|
+
resourceId: string;
|
|
461
|
+
custom: ResourceMetadataCustom | Record<string, unknown>;
|
|
462
|
+
epoch?: string;
|
|
463
|
+
ifMatch?: string;
|
|
464
|
+
ifNoneMatch?: boolean;
|
|
465
|
+
}): Promise<{
|
|
466
|
+
metaVersion: number;
|
|
467
|
+
} | undefined>;
|
|
468
|
+
/**
|
|
469
|
+
* Replication change feed (the `changes` query profile): one indexed keyset
|
|
470
|
+
* query over `(updatedAt, resourceId)`, tombstones included, JSON documents
|
|
471
|
+
* only, bodies parsed for the returned page.
|
|
472
|
+
* @param options {object}
|
|
473
|
+
* @param options.spaceId {string}
|
|
474
|
+
* @param options.collectionId {string}
|
|
475
|
+
* @param [options.checkpoint] {{ id: string, updatedAt: string }}
|
|
476
|
+
* @param options.limit {number}
|
|
477
|
+
* @returns {Promise<{ documents: Array<object>, checkpoint: object | null }>}
|
|
478
|
+
*/
|
|
479
|
+
changesSince({ spaceId, collectionId, checkpoint, limit }: {
|
|
480
|
+
spaceId: string;
|
|
481
|
+
collectionId: string;
|
|
482
|
+
checkpoint?: {
|
|
483
|
+
id: string;
|
|
484
|
+
updatedAt: string;
|
|
485
|
+
};
|
|
486
|
+
limit: number;
|
|
487
|
+
}): Promise<{
|
|
488
|
+
documents: Array<{
|
|
489
|
+
resourceId: string;
|
|
490
|
+
version: number;
|
|
491
|
+
metaVersion?: number;
|
|
492
|
+
createdBy?: IDID;
|
|
493
|
+
updatedAt: string;
|
|
494
|
+
deleted: boolean;
|
|
495
|
+
data?: unknown;
|
|
496
|
+
custom?: ResourceMetadataCustom | Record<string, unknown>;
|
|
497
|
+
epoch?: string;
|
|
498
|
+
}>;
|
|
499
|
+
checkpoint: {
|
|
500
|
+
id: string;
|
|
501
|
+
updatedAt: string;
|
|
502
|
+
} | null;
|
|
503
|
+
}>;
|
|
504
|
+
/**
|
|
505
|
+
* Blinded-index query (the `blinded-index` query profile; see the
|
|
506
|
+
* `StorageBackend.queryByBlindedIndex` contract). Selects the Collection's
|
|
507
|
+
* live JSON rows, parses each body, and hands the candidates to the shared
|
|
508
|
+
* evaluator (`lib/blindedIndex.ts`) for matching, ordering, and cursor
|
|
509
|
+
* pagination -- identical semantics to the filesystem backend. A full scan
|
|
510
|
+
* of the Collection per call, deliberate for this teaching backend; an
|
|
511
|
+
* indexed variant would flatten the blinded attributes into an indexed
|
|
512
|
+
* token side-table (the bedrock-edv-storage strategy). Unparsable JSON is
|
|
513
|
+
* skipped.
|
|
514
|
+
* @param options {object}
|
|
515
|
+
* @param options.spaceId {string}
|
|
516
|
+
* @param options.collectionId {string}
|
|
517
|
+
* @param options.query {BlindedIndexQuery}
|
|
518
|
+
* @param [options.count] {boolean} return only the match count
|
|
519
|
+
* @param [options.limit] {number} requested page size
|
|
520
|
+
* @param [options.cursor] {string} opaque cursor from a prior page
|
|
521
|
+
* @returns {Promise<{ count: number } | BlindedIndexQueryPage>}
|
|
522
|
+
*/
|
|
523
|
+
queryByBlindedIndex({ spaceId, collectionId, query, count, limit, cursor }: {
|
|
524
|
+
spaceId: string;
|
|
525
|
+
collectionId: string;
|
|
526
|
+
query: BlindedIndexQuery;
|
|
527
|
+
count?: boolean;
|
|
528
|
+
limit?: number;
|
|
529
|
+
cursor?: string;
|
|
530
|
+
}): Promise<{
|
|
531
|
+
count: number;
|
|
532
|
+
} | BlindedIndexQueryPage>;
|
|
533
|
+
/**
|
|
534
|
+
* Maps the optional-id policy addressing onto the sentinel-column primary
|
|
535
|
+
* key: Space policy `('', '')`, Collection policy `(cid, '')`, Resource
|
|
536
|
+
* policy `(cid, rid)`.
|
|
537
|
+
* @param options {object}
|
|
538
|
+
* @param [options.collectionId] {string}
|
|
539
|
+
* @param [options.resourceId] {string}
|
|
540
|
+
* @returns {{ collectionKey: string, resourceKey: string }}
|
|
541
|
+
*/
|
|
542
|
+
private _policyKey;
|
|
543
|
+
/**
|
|
544
|
+
* @param options {object}
|
|
545
|
+
* @param options.spaceId {string}
|
|
546
|
+
* @param [options.collectionId] {string}
|
|
547
|
+
* @param [options.resourceId] {string}
|
|
548
|
+
* @returns {Promise<PolicyDocument|undefined>}
|
|
549
|
+
*/
|
|
550
|
+
getPolicy({ spaceId, collectionId, resourceId }: {
|
|
551
|
+
spaceId: string;
|
|
552
|
+
collectionId?: string;
|
|
553
|
+
resourceId?: string;
|
|
554
|
+
}): Promise<PolicyDocument | undefined>;
|
|
555
|
+
/**
|
|
556
|
+
* @param options {object}
|
|
557
|
+
* @param options.spaceId {string}
|
|
558
|
+
* @param [options.collectionId] {string}
|
|
559
|
+
* @param [options.resourceId] {string}
|
|
560
|
+
* @param options.policy {PolicyDocument}
|
|
561
|
+
* @returns {Promise<void>}
|
|
562
|
+
*/
|
|
563
|
+
writePolicy({ spaceId, collectionId, resourceId, policy }: {
|
|
564
|
+
spaceId: string;
|
|
565
|
+
collectionId?: string;
|
|
566
|
+
resourceId?: string;
|
|
567
|
+
policy: PolicyDocument;
|
|
568
|
+
}): Promise<void>;
|
|
569
|
+
/**
|
|
570
|
+
* The one policy upsert statement, shared by `writePolicy` and the import
|
|
571
|
+
* apply loop; keys through `_policyKey` so the sentinel convention lives in
|
|
572
|
+
* one place.
|
|
573
|
+
* @param options {object}
|
|
574
|
+
* @param options.queryable {Queryable}
|
|
575
|
+
* @param options.spaceId {string}
|
|
576
|
+
* @param [options.collectionId] {string}
|
|
577
|
+
* @param [options.resourceId] {string}
|
|
578
|
+
* @param options.policy {PolicyDocument}
|
|
579
|
+
* @returns {Promise<void>}
|
|
580
|
+
*/
|
|
581
|
+
private _upsertPolicy;
|
|
582
|
+
/**
|
|
583
|
+
* @param options {object}
|
|
584
|
+
* @param options.spaceId {string}
|
|
585
|
+
* @param [options.collectionId] {string}
|
|
586
|
+
* @param [options.resourceId] {string}
|
|
587
|
+
* @returns {Promise<void>} idempotent
|
|
588
|
+
*/
|
|
589
|
+
deletePolicy({ spaceId, collectionId, resourceId }: {
|
|
590
|
+
spaceId: string;
|
|
591
|
+
collectionId?: string;
|
|
592
|
+
resourceId?: string;
|
|
593
|
+
}): Promise<void>;
|
|
594
|
+
/**
|
|
595
|
+
* Persists a full (secret-bearing) backend-registration record. Upsert.
|
|
596
|
+
* @param options {object}
|
|
597
|
+
* @param options.spaceId {string}
|
|
598
|
+
* @param options.backendId {string}
|
|
599
|
+
* @param options.record {StoredBackendRecord}
|
|
600
|
+
* @returns {Promise<void>}
|
|
601
|
+
*/
|
|
602
|
+
writeBackend({ spaceId, backendId, record }: {
|
|
603
|
+
spaceId: string;
|
|
604
|
+
backendId: string;
|
|
605
|
+
record: StoredBackendRecord;
|
|
606
|
+
}): Promise<void>;
|
|
607
|
+
/**
|
|
608
|
+
* The full (secret-bearing) record, or `undefined`. The only method that
|
|
609
|
+
* exposes secret connection material -- internal use.
|
|
610
|
+
* @param options {object}
|
|
611
|
+
* @param options.spaceId {string}
|
|
612
|
+
* @param options.backendId {string}
|
|
613
|
+
* @returns {Promise<StoredBackendRecord|undefined>}
|
|
614
|
+
*/
|
|
615
|
+
getBackend({ spaceId, backendId }: {
|
|
616
|
+
spaceId: string;
|
|
617
|
+
backendId: string;
|
|
618
|
+
}): Promise<StoredBackendRecord | undefined>;
|
|
619
|
+
/**
|
|
620
|
+
* The Space's registered backends, **sanitized** (mapped through
|
|
621
|
+
* `sanitizeBackendRecord` -- the secret boundary is unchanged), sorted by id.
|
|
622
|
+
* @param options {object}
|
|
623
|
+
* @param options.spaceId {string}
|
|
624
|
+
* @returns {Promise<BackendDescriptor[]>}
|
|
625
|
+
*/
|
|
626
|
+
listBackends({ spaceId }: {
|
|
627
|
+
spaceId: string;
|
|
628
|
+
}): Promise<BackendDescriptor[]>;
|
|
629
|
+
/**
|
|
630
|
+
* @param options {object}
|
|
631
|
+
* @param options.spaceId {string}
|
|
632
|
+
* @param options.backendId {string}
|
|
633
|
+
* @returns {Promise<void>} idempotent
|
|
634
|
+
*/
|
|
635
|
+
deleteBackend({ spaceId, backendId }: {
|
|
636
|
+
spaceId: string;
|
|
637
|
+
backendId: string;
|
|
638
|
+
}): Promise<void>;
|
|
639
|
+
/**
|
|
640
|
+
* Persists a keystore config unconditionally (the create path; local ids
|
|
641
|
+
* are server-generated random values). The queryable/gated fields
|
|
642
|
+
* (`controller`, `sequence`, `kmsModule`) are denormalized alongside the
|
|
643
|
+
* verbatim config.
|
|
644
|
+
* @param options {object}
|
|
645
|
+
* @param options.keystoreId {string}
|
|
646
|
+
* @param options.config {KeystoreConfig}
|
|
647
|
+
* @returns {Promise<void>}
|
|
648
|
+
*/
|
|
649
|
+
writeKeystore({ keystoreId, config }: {
|
|
650
|
+
keystoreId: string;
|
|
651
|
+
config: KeystoreConfig;
|
|
652
|
+
}): Promise<void>;
|
|
653
|
+
/**
|
|
654
|
+
* @param options {object}
|
|
655
|
+
* @param options.keystoreId {string}
|
|
656
|
+
* @returns {Promise<KeystoreConfig|undefined>}
|
|
657
|
+
*/
|
|
658
|
+
getKeystore({ keystoreId }: {
|
|
659
|
+
keystoreId: string;
|
|
660
|
+
}): Promise<KeystoreConfig | undefined>;
|
|
661
|
+
/**
|
|
662
|
+
* Replaces a keystore config, gated atomically in one conditional `UPDATE`:
|
|
663
|
+
* the row must exist with `sequence` exactly one less than the incoming
|
|
664
|
+
* config's and an unchanged `kmsModule`. Zero rows updated -- missing
|
|
665
|
+
* keystore, stale sequence, or module change alike -- rejects with the
|
|
666
|
+
* protocol's single merged 409 (`KeystoreStateConflictError`).
|
|
667
|
+
* @param options {object}
|
|
668
|
+
* @param options.keystoreId {string}
|
|
669
|
+
* @param options.config {KeystoreConfig}
|
|
670
|
+
* @returns {Promise<void>}
|
|
671
|
+
*/
|
|
672
|
+
updateKeystore({ keystoreId, config }: {
|
|
673
|
+
keystoreId: string;
|
|
674
|
+
config: KeystoreConfig;
|
|
675
|
+
}): Promise<void>;
|
|
676
|
+
/**
|
|
677
|
+
* Every stored keystore config whose `controller` matches, sorted by local
|
|
678
|
+
* id (the request layer caps the wire result).
|
|
679
|
+
* @param options {object}
|
|
680
|
+
* @param options.controller {IDID}
|
|
681
|
+
* @returns {Promise<KeystoreConfig[]>}
|
|
682
|
+
*/
|
|
683
|
+
listKeystoresByController({ controller }: {
|
|
684
|
+
controller: IDID;
|
|
685
|
+
}): Promise<KeystoreConfig[]>;
|
|
686
|
+
/**
|
|
687
|
+
* Inserts a key record, create-only: the primary key enforces the
|
|
688
|
+
* `(keystoreId, localId)` uniqueness atomically; a duplicate rejects with
|
|
689
|
+
* the protocol's 409 (`KeyIdConflictError`). The record is stored verbatim
|
|
690
|
+
* (opaque to storage -- the at-rest cipher applies above the backend).
|
|
691
|
+
* @param options {object}
|
|
692
|
+
* @param options.keystoreId {string}
|
|
693
|
+
* @param options.localId {string}
|
|
694
|
+
* @param options.record {KmsKeyRecord}
|
|
695
|
+
* @returns {Promise<void>}
|
|
696
|
+
*/
|
|
697
|
+
insertKey({ keystoreId, localId, record }: {
|
|
698
|
+
keystoreId: string;
|
|
699
|
+
localId: string;
|
|
700
|
+
record: KmsKeyRecord;
|
|
701
|
+
}): Promise<void>;
|
|
702
|
+
/**
|
|
703
|
+
* @param options {object}
|
|
704
|
+
* @param options.keystoreId {string}
|
|
705
|
+
* @param options.localId {string}
|
|
706
|
+
* @returns {Promise<KmsKeyRecord|undefined>}
|
|
707
|
+
*/
|
|
708
|
+
getKey({ keystoreId, localId }: {
|
|
709
|
+
keystoreId: string;
|
|
710
|
+
localId: string;
|
|
711
|
+
}): Promise<KmsKeyRecord | undefined>;
|
|
712
|
+
/**
|
|
713
|
+
* Every stored key record under the keystore, sorted by local id (the request
|
|
714
|
+
* layer caps and paginates the wire result). An empty keystore resolves an
|
|
715
|
+
* empty list. The record is returned verbatim -- the at-rest cipher applies
|
|
716
|
+
* above the backend.
|
|
717
|
+
* @param options {object}
|
|
718
|
+
* @param options.keystoreId {string}
|
|
719
|
+
* @returns {Promise<Array<{ localId: string, record: KmsKeyRecord }>>}
|
|
720
|
+
*/
|
|
721
|
+
listKeys({ keystoreId }: {
|
|
722
|
+
keystoreId: string;
|
|
723
|
+
}): Promise<Array<{
|
|
724
|
+
localId: string;
|
|
725
|
+
record: KmsKeyRecord;
|
|
726
|
+
}>>;
|
|
727
|
+
/**
|
|
728
|
+
* Resolves a revocation scope to the table it lives in, the scope column
|
|
729
|
+
* within that table, and the scope id value. The returned `table` and
|
|
730
|
+
* `column` are server-chosen constants (never user input), so callers may
|
|
731
|
+
* safely interpolate them into a SQL template; `id` remains a bound value.
|
|
732
|
+
* @param scope {RevocationScope}
|
|
733
|
+
* @returns {{ table: string, column: string, id: string }}
|
|
734
|
+
*/
|
|
735
|
+
private _revocationTable;
|
|
736
|
+
/**
|
|
737
|
+
* Inserts a revocation record, create-only on
|
|
738
|
+
* `(scope id, delegator, capability.id)`; a duplicate rejects with the
|
|
739
|
+
* protocol's 409 (`DuplicateRevocationError`).
|
|
740
|
+
* @param options {object}
|
|
741
|
+
* @param options.scope {RevocationScope}
|
|
742
|
+
* @param options.record {RevocationRecord}
|
|
743
|
+
* @returns {Promise<void>}
|
|
744
|
+
*/
|
|
745
|
+
insertRevocation({ scope, record }: {
|
|
746
|
+
scope: RevocationScope;
|
|
747
|
+
record: RevocationRecord;
|
|
748
|
+
}): Promise<void>;
|
|
749
|
+
/**
|
|
750
|
+
* True when any of the given capabilities has a stored, unexpired
|
|
751
|
+
* revocation under the scope. A single read-only SELECT: rows past their
|
|
752
|
+
* `meta.expires` GC horizon are filtered out in the predicate rather than
|
|
753
|
+
* pruned here -- this runs on every delegated-chain verification, so it
|
|
754
|
+
* must not write; `insertRevocation` prunes on the (rare) write path
|
|
755
|
+
* instead. ISO-8601 strings compare correctly under the column's byte-order
|
|
756
|
+
* collation.
|
|
757
|
+
* @param options {object}
|
|
758
|
+
* @param options.scope {RevocationScope}
|
|
759
|
+
* @param options.capabilities {CapabilitySummary[]}
|
|
760
|
+
* @returns {Promise<boolean>}
|
|
761
|
+
*/
|
|
762
|
+
isRevoked({ scope, capabilities }: {
|
|
763
|
+
scope: RevocationScope;
|
|
764
|
+
capabilities: CapabilitySummary[];
|
|
765
|
+
}): Promise<boolean>;
|
|
766
|
+
/**
|
|
767
|
+
* Builds the filesystem-dialect sidecar object for a resource row (the
|
|
768
|
+
* `.meta.<id>.json` shape), field order matching the filesystem writer so
|
|
769
|
+
* archives stay as close to byte-compatible as jsonb round-tripping allows.
|
|
770
|
+
* @param row {Omit<ResourceRow, 'content'>}
|
|
771
|
+
* @returns {SidecarShape}
|
|
772
|
+
*/
|
|
773
|
+
private _sidecarFor;
|
|
774
|
+
/**
|
|
775
|
+
* Exports the Space as a tar stream in the exact filesystem on-disk layout
|
|
776
|
+
* (same file-name codecs, same manifest), so the archive imports into
|
|
777
|
+
* either backend. Backend registration records are excluded (secret
|
|
778
|
+
* material), exactly as on the filesystem.
|
|
779
|
+
* @param options {object}
|
|
780
|
+
* @param options.spaceId {string}
|
|
781
|
+
* @returns {Promise<Readable>} tar-stream pack
|
|
782
|
+
*/
|
|
783
|
+
exportSpace({ spaceId }: {
|
|
784
|
+
spaceId: string;
|
|
785
|
+
}): Promise<Readable>;
|
|
786
|
+
/**
|
|
787
|
+
* Fetches one resource's content bytes for the export pack loop. A row
|
|
788
|
+
* deleted or tombstoned between the metadata pass and this read (the export
|
|
789
|
+
* is not one transaction, same as the filesystem's racy directory walk)
|
|
790
|
+
* yields an empty body rather than failing the whole archive.
|
|
791
|
+
* @param options {object}
|
|
792
|
+
* @param options.spaceId {string}
|
|
793
|
+
* @param options.collectionId {string}
|
|
794
|
+
* @param options.resourceId {string}
|
|
795
|
+
* @returns {Promise<Buffer>}
|
|
796
|
+
*/
|
|
797
|
+
private _resourceContent;
|
|
798
|
+
/**
|
|
799
|
+
* Merges a WAS space-export tarball into an existing Space with the same
|
|
800
|
+
* three-invariant pre-flight (per-entry 413, fail-closed 422 encryption
|
|
801
|
+
* conformance, cumulative 507) and skip-not-overwrite merge semantics as
|
|
802
|
+
* the filesystem backend -- including tombstone carry-over and "a tombstone
|
|
803
|
+
* blocks resurrection". One strict improvement: the entire apply loop runs
|
|
804
|
+
* in a single transaction, so a mid-import failure leaves the Space
|
|
805
|
+
* untouched atomically.
|
|
806
|
+
* @param options {object}
|
|
807
|
+
* @param options.spaceId {string}
|
|
808
|
+
* @param options.tarStream {Readable}
|
|
809
|
+
* @returns {Promise<ImportStats>}
|
|
810
|
+
*/
|
|
811
|
+
importSpace({ spaceId, tarStream }: {
|
|
812
|
+
spaceId: string;
|
|
813
|
+
tarStream: Readable;
|
|
814
|
+
}): Promise<ImportStats>;
|
|
815
|
+
/**
|
|
816
|
+
* Inserts one archived resource (or orphan tombstone) row for the import
|
|
817
|
+
* apply loop. Timestamps, versions, `createdBy`, and `custom` come from the
|
|
818
|
+
* archive's sidecar when present; an archive resource without a sidecar is
|
|
819
|
+
* treated as a fresh first write on this backend (version 1, no
|
|
820
|
+
* `createdBy`).
|
|
821
|
+
* @param options {object}
|
|
822
|
+
* @param options.client {pg.PoolClient}
|
|
823
|
+
* @param options.spaceId {string}
|
|
824
|
+
* @param options.collectionId {string}
|
|
825
|
+
* @param options.resourceId {string}
|
|
826
|
+
* @param options.contentType {string}
|
|
827
|
+
* @param options.body {Buffer|null} `null` re-creates a tombstone
|
|
828
|
+
* @param [options.sidecar] {SidecarShape}
|
|
829
|
+
* @returns {Promise<void>}
|
|
830
|
+
*/
|
|
831
|
+
private _insertImportedResource;
|
|
832
|
+
}
|
|
833
|
+
//# sourceMappingURL=postgres.d.ts.map
|