@byok-sdk/cloud-dataplane 0.4.0
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/LICENSE +21 -0
- package/README.md +224 -0
- package/dist/cleanup.d.ts +106 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +4284 -0
- package/dist/index.js.map +1 -0
- package/dist/migrate.d.ts +54 -0
- package/dist/migrations-dir.d.ts +7 -0
- package/dist/pool.d.ts +39 -0
- package/dist/sql/0001_cloud_local.sql +184 -0
- package/dist/sql/0002_core_domain.sql +416 -0
- package/dist/sql/0003_cloud_cleanup.sql +97 -0
- package/dist/sql/0004_device_proof_truth.sql +39 -0
- package/dist/sql/0005_skill_packs.sql +78 -0
- package/dist/sql/0006_device_presence_toolsets.sql +18 -0
- package/dist/stores/core/board.d.ts +38 -0
- package/dist/stores/core/index.d.ts +37 -0
- package/dist/stores/core/mailbox-sequence.d.ts +10 -0
- package/dist/stores/core/mailbox.d.ts +32 -0
- package/dist/stores/core/objects.d.ts +49 -0
- package/dist/stores/core/presence.d.ts +34 -0
- package/dist/stores/core/quota.d.ts +56 -0
- package/dist/stores/core/skill-pack.d.ts +41 -0
- package/dist/stores/core/truth.d.ts +32 -0
- package/dist/stores/dedup.d.ts +22 -0
- package/dist/stores/devices.d.ts +23 -0
- package/dist/stores/index.d.ts +55 -0
- package/dist/stores/nonces.d.ts +26 -0
- package/dist/stores/pairing-codes.d.ts +26 -0
- package/dist/stores/proof-receipts.d.ts +12 -0
- package/dist/stores/r2-blobs.d.ts +249 -0
- package/dist/stores/receipts.d.ts +25 -0
- package/dist/stores/task-attempts.d.ts +36 -0
- package/dist/truth-committer.d.ts +15 -0
- package/package.json +59 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The R2/S3 {@link CloudBlobStore} — grants, and only grants (§12.7.4, §12.7.7).
|
|
3
|
+
*
|
|
4
|
+
* Zero bytes cross this file either, but for the opposite reason to
|
|
5
|
+
* `stores/core/objects.ts`: the manifest store holds metadata because bytes are
|
|
6
|
+
* not its job, and this store holds no bytes because the DEVICE talks to the
|
|
7
|
+
* object store directly. What it mints is a presigned URL; what the device does
|
|
8
|
+
* with it is between the device and R2. That is why this composition supplies
|
|
9
|
+
* no `BlobContentProxy` — there is no byte path here to proxy, and the absence
|
|
10
|
+
* is the honest declaration (`@byok-sdk/cloud`'s `ports.ts`, design §6).
|
|
11
|
+
*
|
|
12
|
+
* Manifest and bytes are one transaction authority split across two systems
|
|
13
|
+
* with no shared transaction, so the protocol is reserve/verify rather than
|
|
14
|
+
* write/trust:
|
|
15
|
+
*
|
|
16
|
+
* 1. `createUpload` writes the `pending` manifest row FIRST, then signs a PUT.
|
|
17
|
+
* Row-before-bytes is what makes an abandoned upload a reclaimable
|
|
18
|
+
* tombstone instead of an object nobody has a record of.
|
|
19
|
+
* 2. The device PUTs straight to the object store. `Content-Length` and
|
|
20
|
+
* `Content-Type` are in the signed headers, so a body of the wrong size or
|
|
21
|
+
* the wrong type is refused by the object store itself — before a byte is
|
|
22
|
+
* stored, and without this process being in the path.
|
|
23
|
+
* 3. The explicit finalize route calls `observeUpload`, which performs an
|
|
24
|
+
* unconditional `HEAD`. Unconditional because §12.7.7 step 4 is about what
|
|
25
|
+
* the store OBSERVES versus what the client DECLARED, and signing the length
|
|
26
|
+
* proves what one client sent, not what is at the key now. The quota
|
|
27
|
+
* authority then commits manifest/reservation/usage in one Postgres
|
|
28
|
+
* statement; download never performs that transition.
|
|
29
|
+
* 4. `committed` is terminal for writes. Step 1 is re-runnable while a row is
|
|
30
|
+
* `pending` — that is what makes a retried upload idempotent — and refused
|
|
31
|
+
* once it is `committed`, because §12.7.4 lets truth reference a committed
|
|
32
|
+
* manifest and the reference means nothing if the bytes can still be
|
|
33
|
+
* replaced by a same-shaped body under a freshly signed PUT.
|
|
34
|
+
*
|
|
35
|
+
* **No checksum header.** `x-amz-checksum-sha256` was probed rather than
|
|
36
|
+
* assumed (design §3 marked it `[unverified]`): MinIO honors it in a presigned
|
|
37
|
+
* PUT and rejects mismatched bytes with `XAmzContentChecksumMismatch`, but R2's
|
|
38
|
+
* S3 compatibility table lists SHA-256 as `COMPOSITE` only — `FULL_OBJECT`, the
|
|
39
|
+
* type a single-shot PutObject uses, is ❌, and R2's PutObject feature row names
|
|
40
|
+
* no `x-amz-checksum-*` header at all. Signing one would mint URLs that work
|
|
41
|
+
* against the test substrate and fail against production, which is worse than
|
|
42
|
+
* not signing it. The `HEAD` above was never conditional on it.
|
|
43
|
+
*
|
|
44
|
+
* The `blobId` this store mints IS the content hash. That is what makes "no
|
|
45
|
+
* naked object index" constructive rather than disciplinary: there is no
|
|
46
|
+
* surrogate id to look up, every read is `(tenant, hash)` against the manifest
|
|
47
|
+
* primary key, and the object key is derived — once, in {@link #objectUrl} —
|
|
48
|
+
* from a `ContentHash` that core already validated. A non-hex id cannot reach
|
|
49
|
+
* key construction because it cannot become a `ContentHash`.
|
|
50
|
+
*/
|
|
51
|
+
import { type Clock, type ContentHash, type ObjectStore, type StorageReservation, type TenantId } from '@byok-sdk/core';
|
|
52
|
+
import type { BlobObservation, CloudBlobStore } from '@byok-sdk/cloud';
|
|
53
|
+
/** 15 minutes, matching the in-memory reference's `BLOB_URL_TTL_MS`. */
|
|
54
|
+
export declare const DEFAULT_PRESIGN_TTL_SECONDS: number;
|
|
55
|
+
/** `X-Amz-Expires` floor. Below a second there is no grant, only a signature. */
|
|
56
|
+
export declare const MIN_PRESIGN_TTL_SECONDS = 1;
|
|
57
|
+
/** `X-Amz-Expires` ceiling: seven days, the longest lifetime R2 will honor. */
|
|
58
|
+
export declare const MAX_PRESIGN_TTL_SECONDS = 604800;
|
|
59
|
+
/** Three total attempts: the first plus two retries. */
|
|
60
|
+
export declare const DEFAULT_MAX_ATTEMPTS = 3;
|
|
61
|
+
/** Doubles per retry. Deterministic — no jitter, so a test can assert the sequence. */
|
|
62
|
+
export declare const DEFAULT_RETRY_DELAY_MS = 100;
|
|
63
|
+
/** The subset of `fetch` this store uses. The seam a fault injector replaces. */
|
|
64
|
+
export type ObjectStoreFetch = (request: Request) => Promise<Response>;
|
|
65
|
+
/**
|
|
66
|
+
* Raised when the object store answered something this adapter cannot act on —
|
|
67
|
+
* a 4xx that is not "absent", or a transient failure that outlived its retries.
|
|
68
|
+
*
|
|
69
|
+
* A local class rather than a core code: core's taxonomy describes the manifest
|
|
70
|
+
* contract, and "R2 returned 503 three times" is an adapter fault, not a
|
|
71
|
+
* statement about the object.
|
|
72
|
+
*/
|
|
73
|
+
export declare class ObjectStoreRequestError extends Error {
|
|
74
|
+
readonly status: number | undefined;
|
|
75
|
+
readonly attempts: number;
|
|
76
|
+
constructor(message: string, attempts: number, status?: number, options?: ErrorOptions);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* What this adapter refuses on its own account, before anything is signed.
|
|
80
|
+
*
|
|
81
|
+
* A second local class rather than more core codes, for the same reason
|
|
82
|
+
* {@link ObjectStoreRequestError} is one: core's taxonomy describes the
|
|
83
|
+
* manifest contract, and "this deployment's tenant ids cannot be key segments"
|
|
84
|
+
* or "this deployment configured a lifetime R2 will not honor" are facts about
|
|
85
|
+
* an S3 adapter's configuration and inputs, not about an object. Core's code
|
|
86
|
+
* union is closed and deliberately so; widening it from an adapter would put
|
|
87
|
+
* an adapter's vocabulary on a wire contract every composition shares.
|
|
88
|
+
*
|
|
89
|
+
* Code-based branching, matching the idiom of every other error type here.
|
|
90
|
+
*/
|
|
91
|
+
export declare const R2_BLOB_ERROR_CODES: {
|
|
92
|
+
/**
|
|
93
|
+
* A tenant id that cannot be one safe path segment. Wire-relevant: it is the
|
|
94
|
+
* only signal a control plane gets that the id it issued cannot address
|
|
95
|
+
* object storage, and it is raised BEFORE any key is built.
|
|
96
|
+
*/
|
|
97
|
+
readonly storage_tenant_key_unsafe: 'storage_tenant_key_unsafe';
|
|
98
|
+
/** A presign lifetime outside `[MIN_PRESIGN_TTL_SECONDS, MAX_PRESIGN_TTL_SECONDS]`. Construction-time only. */
|
|
99
|
+
readonly storage_presign_ttl_invalid: 'storage_presign_ttl_invalid';
|
|
100
|
+
/** ListObjectsV2 accepts 1..1000 keys per page. Maintenance input only. */
|
|
101
|
+
readonly storage_list_limit_invalid: 'storage_list_limit_invalid';
|
|
102
|
+
/** Continuation tokens are opaque but non-empty. Maintenance input only. */
|
|
103
|
+
readonly storage_list_cursor_invalid: 'storage_list_cursor_invalid';
|
|
104
|
+
};
|
|
105
|
+
export type R2BlobErrorCode = (typeof R2_BLOB_ERROR_CODES)[keyof typeof R2_BLOB_ERROR_CODES];
|
|
106
|
+
export declare class R2BlobStoreError extends Error {
|
|
107
|
+
readonly code: R2BlobErrorCode;
|
|
108
|
+
constructor(code: R2BlobErrorCode, message: string, options?: ErrorOptions);
|
|
109
|
+
}
|
|
110
|
+
export interface R2BlobStoreOptions {
|
|
111
|
+
/**
|
|
112
|
+
* The manifest authority. Same `ObjectStore` the core composition supplies —
|
|
113
|
+
* one row per (tenant, hash), and this store never opens a second one.
|
|
114
|
+
*/
|
|
115
|
+
readonly objects: ObjectStore;
|
|
116
|
+
/**
|
|
117
|
+
* The clock SigV4's `X-Amz-Date` is read from — WALL time, and deliberately
|
|
118
|
+
* NOT the composition's logical clock.
|
|
119
|
+
*
|
|
120
|
+
* Every other instant in this program comes from an injected clock so TTLs
|
|
121
|
+
* are assertable without sleeping, and a store reaching for wall time is a
|
|
122
|
+
* bug. A request signature is the exception, and not a soft one: its validity
|
|
123
|
+
* window is adjudicated by the object store against the object store's own
|
|
124
|
+
* clock. Signing with a logical instant produces a credential the remote side
|
|
125
|
+
* rejects as skewed the moment the two disagree — a 403 at upload time, from
|
|
126
|
+
* a decision made at composition time.
|
|
127
|
+
*
|
|
128
|
+
* Separate and required rather than defaulted, so the distinction is a choice
|
|
129
|
+
* someone makes rather than one they inherit. A test that needs an EXPIRED
|
|
130
|
+
* grant backdates this clock, which is also the only way to assert expiry
|
|
131
|
+
* without sleeping through it.
|
|
132
|
+
*/
|
|
133
|
+
readonly signingClock: Clock;
|
|
134
|
+
/** Origin only, e.g. `https://<account>.r2.cloudflarestorage.com`. */
|
|
135
|
+
readonly endpoint: string;
|
|
136
|
+
readonly bucket: string;
|
|
137
|
+
readonly accessKeyId: string;
|
|
138
|
+
readonly secretAccessKey: string;
|
|
139
|
+
/**
|
|
140
|
+
* `auto` for R2. Required rather than defaulted: a signature scoped to the
|
|
141
|
+
* wrong region fails as a 403 at upload time, which is a terrible place to
|
|
142
|
+
* discover a config default nobody chose.
|
|
143
|
+
*/
|
|
144
|
+
readonly region: string;
|
|
145
|
+
/**
|
|
146
|
+
* A key namespace for this deployment, e.g. `acme/prod` →
|
|
147
|
+
* `acme/prod/tenants/<tenant>/objects/sha256/<hex>`. Omit it and the key is
|
|
148
|
+
* the unprefixed `tenants/...` layout, byte for byte.
|
|
149
|
+
*
|
|
150
|
+
* What it is for: one bucket, several deployments. Without it every
|
|
151
|
+
* deployment owns the bucket root, so a host running two products against
|
|
152
|
+
* one R2 account has to open a bucket per product.
|
|
153
|
+
*
|
|
154
|
+
* **Immutable per deployment, and only for a NEW one.** This value is
|
|
155
|
+
* spliced into the key at write time and at read time from the same field;
|
|
156
|
+
* there is no second layout anything falls back to, by design. Change it on
|
|
157
|
+
* a deployment that has already stored objects and those objects are
|
|
158
|
+
* stranded — still in the bucket, no longer addressable, and invisible to
|
|
159
|
+
* this SDK's own maintenance surface. A dual-read across the old and new
|
|
160
|
+
* prefix is NOT a supported way to switch and will not be added: it would
|
|
161
|
+
* make two key layouts simultaneously authoritative for the same object.
|
|
162
|
+
* Moving an existing deployment onto a prefix is a separate, one-shot,
|
|
163
|
+
* operator-invoked copy of the objects themselves, out of this SDK's scope.
|
|
164
|
+
*
|
|
165
|
+
* Validated at construction ({@link ObjectKeyPrefix}): slash-joined segments
|
|
166
|
+
* of lowercase alphanumerics, `.`, `_`, `-`, each starting with an
|
|
167
|
+
* alphanumeric, no leading/trailing slash, no empty segment. `''` is a
|
|
168
|
+
* refusal, not a synonym for "no prefix" — it is what an unset environment
|
|
169
|
+
* variable looks like, and silently treating it as the default would put a
|
|
170
|
+
* deployment's objects somewhere nobody chose.
|
|
171
|
+
*/
|
|
172
|
+
readonly keyPrefix?: string;
|
|
173
|
+
readonly presignTtlSeconds?: number;
|
|
174
|
+
/** Injected so a fault injector can sit in front of the real one. */
|
|
175
|
+
readonly fetch?: ObjectStoreFetch;
|
|
176
|
+
readonly maxAttempts?: number;
|
|
177
|
+
readonly retryDelayMs?: number;
|
|
178
|
+
}
|
|
179
|
+
/** One tenant-prefixed R2 key returned by ListObjectsV2. */
|
|
180
|
+
export interface R2ListedObject {
|
|
181
|
+
readonly key: string;
|
|
182
|
+
/** Present only when the key is exactly `<tenant>/sha256/<64 lowercase hex>`. */
|
|
183
|
+
readonly hash?: ContentHash;
|
|
184
|
+
readonly byteSize: bigint;
|
|
185
|
+
}
|
|
186
|
+
export interface R2ObjectPage {
|
|
187
|
+
readonly objects: readonly R2ListedObject[];
|
|
188
|
+
readonly nextContinuationToken?: string;
|
|
189
|
+
}
|
|
190
|
+
export type R2DeleteResult = 'deleted' | 'absent';
|
|
191
|
+
/** Operations used only by the host-owned S4B-c maintenance worker. */
|
|
192
|
+
export interface R2ObjectMaintenance {
|
|
193
|
+
inspectObject(tenant: TenantId, hash: ContentHash): Promise<BlobObservation | undefined>;
|
|
194
|
+
deleteObject(tenant: TenantId, hash: ContentHash): Promise<R2DeleteResult>;
|
|
195
|
+
listTenantObjects(tenant: TenantId, continuationToken?: string, limit?: number): Promise<R2ObjectPage>;
|
|
196
|
+
}
|
|
197
|
+
export declare class R2CloudBlobStore implements CloudBlobStore {
|
|
198
|
+
#private;
|
|
199
|
+
constructor(options: R2BlobStoreOptions);
|
|
200
|
+
/**
|
|
201
|
+
* Reserve the manifest row, then hand back a PUT bound to this tenant, this
|
|
202
|
+
* key, this length, this type, and this expiry.
|
|
203
|
+
*
|
|
204
|
+
* `putManifest` is idempotent per (tenant, hash), so a device that declares
|
|
205
|
+
* the same content twice while it is still `pending` gets the same row and
|
|
206
|
+
* the same key — an interrupted upload is retried, not duplicated. It is
|
|
207
|
+
* idempotent per TENANT, which is the same reason the key embeds the tenant:
|
|
208
|
+
* two tenants holding identical bytes hold two independent objects, and
|
|
209
|
+
* neither can learn of the other's.
|
|
210
|
+
*
|
|
211
|
+
* Idempotence stops at `committed`, and that boundary is the point: a
|
|
212
|
+
* committed object is what a truth record is allowed to reference, so it has
|
|
213
|
+
* to be immutable, and re-issuing a write grant for one is the only way this
|
|
214
|
+
* adapter could make it otherwise.
|
|
215
|
+
*/
|
|
216
|
+
createUpload(tenant: TenantId, reservation: StorageReservation): Promise<{
|
|
217
|
+
readonly blobId: string;
|
|
218
|
+
readonly uploadUrl: string;
|
|
219
|
+
}>;
|
|
220
|
+
observeUpload(tenant: TenantId, blobId: string, reservation: StorageReservation): Promise<BlobObservation | undefined>;
|
|
221
|
+
/**
|
|
222
|
+
* A GET for a committed object this tenant owns; `undefined` otherwise.
|
|
223
|
+
*
|
|
224
|
+
* Every miss answers identically — unknown hash, another tenant's object, a
|
|
225
|
+
* malformed id, bytes that never landed, a tombstoned row. A caller cannot
|
|
226
|
+
* tell them apart, which is what keeps `getDownloadUrl` from being an
|
|
227
|
+
* existence oracle across tenants.
|
|
228
|
+
*
|
|
229
|
+
* This is a pure committed-manifest gate. Observation and commit belong to
|
|
230
|
+
* the explicit finalize route; a download must never decide accounting.
|
|
231
|
+
*/
|
|
232
|
+
getDownloadUrl(tenant: TenantId, blobId: string): Promise<string | undefined>;
|
|
233
|
+
}
|
|
234
|
+
export type R2ObjectMaintenanceOptions = Omit<R2BlobStoreOptions, 'objects' | 'presignTtlSeconds'>;
|
|
235
|
+
/**
|
|
236
|
+
* R2 maintenance adapter kept deliberately outside `CloudBlobStore`.
|
|
237
|
+
*
|
|
238
|
+
* Cloud conformance certifies the device-facing blob port with an exact method
|
|
239
|
+
* inventory. LIST/HEAD/DELETE are host operations, so putting them on
|
|
240
|
+
* `R2CloudBlobStore` would over-declare a capability no other cloud composition
|
|
241
|
+
* implements.
|
|
242
|
+
*/
|
|
243
|
+
export declare class R2ObjectMaintenanceStore implements R2ObjectMaintenance {
|
|
244
|
+
#private;
|
|
245
|
+
constructor(options: R2ObjectMaintenanceOptions);
|
|
246
|
+
inspectObject(tenant: TenantId, hash: ContentHash): Promise<BlobObservation | undefined>;
|
|
247
|
+
deleteObject(tenant: TenantId, hash: ContentHash): Promise<R2DeleteResult>;
|
|
248
|
+
listTenantObjects(tenant: TenantId, continuationToken?: string, limit?: number): Promise<R2ObjectPage>;
|
|
249
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Postgres {@link RequestReceiptStore}: the first write is the fact.
|
|
3
|
+
*
|
|
4
|
+
* `INSERT ... ON CONFLICT DO NOTHING` and nothing else. The terminal a device
|
|
5
|
+
* reports is a fact, and the retry the at-least-once wire guarantees must not
|
|
6
|
+
* overwrite it (§12.6.4: 不覆写第一份事实). `created: false` is how the caller
|
|
7
|
+
* learns it was a replay, which is why an upsert that UPDATED would be wrong in
|
|
8
|
+
* a way no naive "record it twice" test would catch — it would pass, while
|
|
9
|
+
* silently rewriting history and restamping `recorded_at`.
|
|
10
|
+
*/
|
|
11
|
+
import type { RequestReceipt, RequestReceiptStore } from '@byok-sdk/cloud';
|
|
12
|
+
import type { Clock, TenantId } from '@byok-sdk/core';
|
|
13
|
+
import type { Pool } from 'pg';
|
|
14
|
+
export declare class PostgresRequestReceiptStore implements RequestReceiptStore {
|
|
15
|
+
#private;
|
|
16
|
+
constructor(pool: Pool, clock: Clock);
|
|
17
|
+
record(tenant: TenantId, input: {
|
|
18
|
+
readonly key: string;
|
|
19
|
+
readonly body: string;
|
|
20
|
+
}): Promise<{
|
|
21
|
+
readonly receipt: RequestReceipt;
|
|
22
|
+
readonly created: boolean;
|
|
23
|
+
}>;
|
|
24
|
+
get(tenant: TenantId, key: string): Promise<RequestReceipt | undefined>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Postgres {@link TaskAttemptStore} — the ownership authority the inbound gate
|
|
3
|
+
* reads (N2).
|
|
4
|
+
*
|
|
5
|
+
* `claim` is a single guarded statement: `UPDATE ... WHERE owner_device_id IS
|
|
6
|
+
* NULL RETURNING ...`. Two devices racing the same offer therefore produce one
|
|
7
|
+
* owner, not a last writer. When the guard rejects, the row is re-read and
|
|
8
|
+
* returned as-is, which makes a losing claim (and the winner's own re-claim)
|
|
9
|
+
* idempotent rather than an error — the caller learns who owns the task either
|
|
10
|
+
* way. Ownership never transfers: reassigning an owner is the one operation
|
|
11
|
+
* that would make the gate's cross-device assertion unfalsifiable.
|
|
12
|
+
*
|
|
13
|
+
* Two deliberate no-ops, both about not letting a guessed id leave a trace:
|
|
14
|
+
* `claim` and `recordStatus` on a task this tenant never offered write nothing
|
|
15
|
+
* and return `undefined`.
|
|
16
|
+
*/
|
|
17
|
+
import type { TaskAttempt, TaskAttemptStatus, TaskAttemptStore } from '@byok-sdk/cloud';
|
|
18
|
+
import type { Clock, TenantId } from '@byok-sdk/core';
|
|
19
|
+
import type { Pool } from 'pg';
|
|
20
|
+
export declare class PostgresTaskAttemptStore implements TaskAttemptStore {
|
|
21
|
+
#private;
|
|
22
|
+
constructor(pool: Pool, clock: Clock);
|
|
23
|
+
open(tenant: TenantId, input: {
|
|
24
|
+
readonly taskId: string;
|
|
25
|
+
readonly deviceId: string;
|
|
26
|
+
}): Promise<TaskAttempt>;
|
|
27
|
+
get(tenant: TenantId, taskId: string): Promise<TaskAttempt | undefined>;
|
|
28
|
+
claim(tenant: TenantId, input: {
|
|
29
|
+
readonly taskId: string;
|
|
30
|
+
readonly deviceId: string;
|
|
31
|
+
}): Promise<TaskAttempt | undefined>;
|
|
32
|
+
recordStatus(tenant: TenantId, input: {
|
|
33
|
+
readonly taskId: string;
|
|
34
|
+
readonly status: TaskAttemptStatus;
|
|
35
|
+
}): Promise<TaskAttempt | undefined>;
|
|
36
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type CloudCrypto, type TruthCommitInput, type TruthCommitResult, type TruthCommitter } from '@byok-sdk/cloud';
|
|
2
|
+
import { type Clock, type TenantId, type TruthRecord } from '@byok-sdk/core';
|
|
3
|
+
import type { Pool } from 'pg';
|
|
4
|
+
export interface PostgresTruthCommitterOptions {
|
|
5
|
+
readonly pool: Pool;
|
|
6
|
+
readonly clock: Clock;
|
|
7
|
+
readonly crypto: Pick<CloudCrypto, 'sha256'>;
|
|
8
|
+
}
|
|
9
|
+
export declare class PostgresTruthCommitter implements TruthCommitter {
|
|
10
|
+
#private;
|
|
11
|
+
constructor(options: PostgresTruthCommitterOptions);
|
|
12
|
+
getRecord(tenant: TenantId, selector: Parameters<TruthCommitter['getRecord']>[1]): Promise<TruthRecord | undefined>;
|
|
13
|
+
listManifest(tenant: TenantId, query: Parameters<TruthCommitter['listManifest']>[1]): Promise<readonly import("@byok-sdk/core").TruthManifestEntry[]>;
|
|
14
|
+
commit(tenant: TenantId, input: TruthCommitInput): Promise<TruthCommitResult>;
|
|
15
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@byok-sdk/cloud-dataplane",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "BYOK SDK hosted data plane: canonical Postgres + R2 stores, migrations, truth transactions, and maintenance",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/Ancienttwo/byok-sdk.git",
|
|
10
|
+
"directory": "packages/cloud-dataplane"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/Ancienttwo/byok-sdk/issues"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/Ancienttwo/byok-sdk#readme",
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=22.19.0"
|
|
18
|
+
},
|
|
19
|
+
"sideEffects": false,
|
|
20
|
+
"main": "./dist/index.js",
|
|
21
|
+
"module": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"import": "./dist/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./package.json": "./package.json"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md",
|
|
33
|
+
"LICENSE"
|
|
34
|
+
],
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"aws4fetch": "1.0.20",
|
|
40
|
+
"fast-xml-parser": "^5.10.1",
|
|
41
|
+
"pg": "^8.22.0",
|
|
42
|
+
"@byok-sdk/cloud": "0.4.0",
|
|
43
|
+
"@byok-sdk/protocol": "0.4.0",
|
|
44
|
+
"@byok-sdk/core": "0.4.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/pg": "^8.20.4",
|
|
48
|
+
"undici": "8.9.0",
|
|
49
|
+
"@byok-sdk/conformance": "0.0.0"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsup && tsc -p tsconfig.build.json && node scripts/copy-migrations.mjs",
|
|
53
|
+
"dev": "tsup --watch",
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"test:watch": "vitest",
|
|
56
|
+
"typecheck": "tsc --noEmit",
|
|
57
|
+
"clean": "rm -rf dist"
|
|
58
|
+
}
|
|
59
|
+
}
|