domainkit 0.15.0 → 0.16.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/README.md +9 -2
- package/dist/Provision.mjs +209 -19
- package/dist/Provision.mjs.map +1 -1
- package/dist/Reason.mjs +22 -2
- package/dist/Reason.mjs.map +1 -1
- package/dist/Server.mjs +86 -4
- package/dist/Server.mjs.map +1 -1
- package/dist/Storage.mjs +100 -2
- package/dist/Storage.mjs.map +1 -1
- package/dist/internal/attempts.mjs +14 -4
- package/dist/internal/attempts.mjs.map +1 -1
- package/dist/internal/conformance/storage.mjs +183 -0
- package/dist/internal/conformance/storage.mjs.map +1 -1
- package/dist/internal/error.mjs +2 -0
- package/dist/internal/error.mjs.map +1 -1
- package/dist/internal/storage-memory.mjs +185 -2
- package/dist/internal/storage-memory.mjs.map +1 -1
- package/dist/package.mjs +1 -1
- package/dist/types/Provision.d.ts +124 -3
- package/dist/types/Reason.d.ts +18 -2
- package/dist/types/Server.d.ts +3619 -202
- package/dist/types/Storage.d.ts +176 -0
- package/dist/types/internal/attempts.d.ts +1 -0
- package/dist/types/internal/error.d.ts +1 -1
- package/package.json +1 -1
package/dist/types/Storage.d.ts
CHANGED
|
@@ -130,6 +130,94 @@ declare const Attempt_base: Schema.Class<Attempt, Schema.Struct<{
|
|
|
130
130
|
/** One durable plan -> approval -> receipt lifecycle. */
|
|
131
131
|
export declare class Attempt extends Attempt_base {
|
|
132
132
|
}
|
|
133
|
+
export declare const BatchId: Schema.brand<Schema.String, "@domainkit/BatchId">;
|
|
134
|
+
export type BatchId = typeof BatchId.Type;
|
|
135
|
+
/**
|
|
136
|
+
* Where a batch stands, recomputed from its items' attempts after every transition.
|
|
137
|
+
*
|
|
138
|
+
* `planning` while any item is still without a plan, `planned` once every item carries one,
|
|
139
|
+
* `approved` once the principal bound the batch digest, then the apply states. `partial` and
|
|
140
|
+
* `failed` are resumable; `complete` and `rejected` are terminal and leave the unfinished index.
|
|
141
|
+
*/
|
|
142
|
+
export declare const BatchStatus: Schema.Literals<readonly ["planning", "planned", "approved", "applying", "complete", "partial", "failed", "rejected"]>;
|
|
143
|
+
export type BatchStatus = typeof BatchStatus.Type;
|
|
144
|
+
/** Who bound the batch to its digest, and when. The digest itself is `Batch.digest`. */
|
|
145
|
+
export declare const BatchApproval: Schema.Struct<{
|
|
146
|
+
readonly actorId: Schema.String;
|
|
147
|
+
readonly at: Schema.DateTimeUtcFromString;
|
|
148
|
+
}>;
|
|
149
|
+
export type BatchApproval = typeof BatchApproval.Type;
|
|
150
|
+
declare const Batch_base: Schema.Class<Batch, Schema.Struct<{
|
|
151
|
+
readonly id: Schema.brand<Schema.String, "@domainkit/BatchId">;
|
|
152
|
+
readonly ownerId: Schema.String;
|
|
153
|
+
readonly kind: Schema.Literals<readonly ["provisioning", "cleanup"]>;
|
|
154
|
+
readonly status: Schema.Literals<readonly ["planning", "planned", "approved", "applying", "complete", "partial", "failed", "rejected"]>;
|
|
155
|
+
/** SHA-256 over the items' `attachmentId:planDigest` pairs, bound at approval. */
|
|
156
|
+
readonly digest: Schema.NullOr<Schema.brand<Schema.String, "@domainkit/Digest">>;
|
|
157
|
+
readonly approval: Schema.NullOr<Schema.Struct<{
|
|
158
|
+
readonly actorId: Schema.String;
|
|
159
|
+
readonly at: Schema.DateTimeUtcFromString;
|
|
160
|
+
}>>;
|
|
161
|
+
readonly rejection: Schema.NullOr<Schema.Struct<{
|
|
162
|
+
readonly actorId: Schema.String;
|
|
163
|
+
readonly reason: Schema.NullOr<Schema.String>;
|
|
164
|
+
readonly at: Schema.DateTimeUtcFromString;
|
|
165
|
+
}>>;
|
|
166
|
+
/** Unique per owner: a replayed create returns the batch this key already made. */
|
|
167
|
+
readonly idempotencyKey: Schema.String;
|
|
168
|
+
readonly createdBy: Schema.String;
|
|
169
|
+
readonly createdAt: Schema.DateTimeUtcFromString;
|
|
170
|
+
readonly updatedAt: Schema.DateTimeUtcFromString;
|
|
171
|
+
/** When the batch reached `complete` or `rejected`; null while it still owes a move. */
|
|
172
|
+
readonly completedAt: Schema.NullOr<Schema.DateTimeUtcFromString>;
|
|
173
|
+
}>, {}>;
|
|
174
|
+
/**
|
|
175
|
+
* Many domains planned together, approved once, and applied with bounded concurrency.
|
|
176
|
+
*
|
|
177
|
+
* The batch owns the aggregate's own state only: its digest, who approved or declined it, and the
|
|
178
|
+
* status recomputed from its items. Every plan, approval, receipt, lease, and failure stays on the
|
|
179
|
+
* attempt an item points at, so nothing is stored twice.
|
|
180
|
+
*/
|
|
181
|
+
export declare class Batch extends Batch_base {
|
|
182
|
+
}
|
|
183
|
+
declare const BatchItem_base: Schema.Class<BatchItem, Schema.Struct<{
|
|
184
|
+
readonly batchId: Schema.brand<Schema.String, "@domainkit/BatchId">;
|
|
185
|
+
readonly attachmentId: Schema.String;
|
|
186
|
+
/** The item's place in the batch, as `create` received it. */
|
|
187
|
+
readonly position: Schema.Number;
|
|
188
|
+
readonly attemptId: Schema.NullOr<Schema.brand<Schema.String, "@domainkit/PlanId">>;
|
|
189
|
+
readonly planFailure: Schema.NullOr<Schema.String>;
|
|
190
|
+
}>, {}>;
|
|
191
|
+
/**
|
|
192
|
+
* One domain's place in a batch: a pointer to the attachment and to the attempt carrying its plan.
|
|
193
|
+
*
|
|
194
|
+
* `attemptId` is null until the item is planned, and `planFailure` carries why the last planning
|
|
195
|
+
* pass stopped, cleared the moment a plan lands.
|
|
196
|
+
*/
|
|
197
|
+
export declare class BatchItem extends BatchItem_base {
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* The status a batch holds, given whether it was approved or declined and where each item's
|
|
201
|
+
* attempt stands. `null` names an item that has no plan yet.
|
|
202
|
+
*
|
|
203
|
+
* Both `Storage` implementations recompute the stored status through this after every transition,
|
|
204
|
+
* so the owner-scoped unfinished index never disagrees with the attempts it summarizes.
|
|
205
|
+
*/
|
|
206
|
+
export declare const batchStatusOf: (input: {
|
|
207
|
+
readonly approved: boolean;
|
|
208
|
+
readonly rejected: boolean;
|
|
209
|
+
readonly items: ReadonlyArray<AttemptStatus | null>;
|
|
210
|
+
}) => BatchStatus;
|
|
211
|
+
/** A batch with its items, in `position` order. Every batch read returns one. */
|
|
212
|
+
export interface BatchAggregate {
|
|
213
|
+
readonly batch: Batch;
|
|
214
|
+
readonly items: ReadonlyArray<BatchItem>;
|
|
215
|
+
}
|
|
216
|
+
/** One item's attempt approval, as `batches.approve` writes it beside the batch's own. */
|
|
217
|
+
export interface BatchItemApproval {
|
|
218
|
+
readonly attachmentId: string;
|
|
219
|
+
readonly approval: Approval.Model;
|
|
220
|
+
}
|
|
133
221
|
export declare const RequirementStatus: Schema.Literals<readonly ["satisfied", "missing", "mismatch", "unknown"]>;
|
|
134
222
|
export type RequirementStatus = typeof RequirementStatus.Type;
|
|
135
223
|
export declare const Overall: Schema.Literals<readonly ["ready", "pending", "failed"]>;
|
|
@@ -247,6 +335,72 @@ export interface Interface {
|
|
|
247
335
|
readonly complete: (id: Plan.PlanId, receipt: Receipt.Model) => Fx<Attempt>;
|
|
248
336
|
readonly fail: (id: Plan.PlanId, message: string) => Fx<Attempt>;
|
|
249
337
|
};
|
|
338
|
+
/**
|
|
339
|
+
* The batch aggregate: many attempts planned together and approved once.
|
|
340
|
+
*
|
|
341
|
+
* Every transition runs in one transaction over the locked batch row and leaves `status`
|
|
342
|
+
* recomputed from the items' attempts, so the owner-scoped unfinished index never disagrees
|
|
343
|
+
* with the attempts it summarizes.
|
|
344
|
+
*/
|
|
345
|
+
readonly batches: {
|
|
346
|
+
/**
|
|
347
|
+
* Insert the batch and one item per attachment, or return the batch this owner already
|
|
348
|
+
* created under `idempotencyKey`. A replay ignores `attachmentIds` and returns what is
|
|
349
|
+
* stored, which is what makes a retried create safe.
|
|
350
|
+
*/
|
|
351
|
+
readonly create: (input: {
|
|
352
|
+
readonly kind: Plan.Kind;
|
|
353
|
+
readonly idempotencyKey: string;
|
|
354
|
+
/** One item per attachment, in order; empty or duplicated fails `InvalidInput`. */
|
|
355
|
+
readonly attachmentIds: ReadonlyArray<string>;
|
|
356
|
+
}) => Fx<BatchAggregate>;
|
|
357
|
+
readonly get: (id: BatchId) => Fx<BatchAggregate>;
|
|
358
|
+
/**
|
|
359
|
+
* The batch this owner created under `idempotencyKey`, if any.
|
|
360
|
+
*
|
|
361
|
+
* What a caller reads before it validates a create's payload, so a retried request answers
|
|
362
|
+
* with the batch the first one made whatever the retry now says.
|
|
363
|
+
*/
|
|
364
|
+
readonly byIdempotencyKey: (key: string) => Fx<Option.Option<BatchAggregate>>;
|
|
365
|
+
/** Every batch that has not reached `complete` or `rejected`, most recently touched first. */
|
|
366
|
+
readonly listUnfinished: () => Fx<ReadonlyArray<BatchAggregate>>;
|
|
367
|
+
/**
|
|
368
|
+
* Point an item at the attempt that now carries its plan and clear its plan failure.
|
|
369
|
+
*
|
|
370
|
+
* Fails `Stale` unless the batch is still `planning` or `planned`. That check is the fence a
|
|
371
|
+
* planner still in flight hits when the principal declines the batch underneath it, so plan
|
|
372
|
+
* state never lands on a rejected aggregate.
|
|
373
|
+
*/
|
|
374
|
+
readonly recordItemPlan: (id: BatchId, attachmentId: string, attemptId: Plan.PlanId) => Fx<BatchAggregate>;
|
|
375
|
+
/** Record why an item could not be planned; the batch stays `planning`. Same fence. */
|
|
376
|
+
readonly recordItemPlanFailure: (id: BatchId, attachmentId: string, message: string) => Fx<BatchAggregate>;
|
|
377
|
+
/**
|
|
378
|
+
* Bind the batch to `digest` and approve every item's attempt in the same transaction, so a
|
|
379
|
+
* batch is never approved without the per-attempt approvals `attempts.apply` needs.
|
|
380
|
+
*
|
|
381
|
+
* Fails `Stale` unless the batch is `planned` and every item still points at the attempt its
|
|
382
|
+
* approval names. Approving an approved batch under the same digest returns it unchanged; a
|
|
383
|
+
* different digest fails `Stale`.
|
|
384
|
+
*/
|
|
385
|
+
readonly approve: (id: BatchId, input: {
|
|
386
|
+
readonly digest: Plan.Digest;
|
|
387
|
+
readonly actorId: string;
|
|
388
|
+
/** One per item, covering every item in the batch. */
|
|
389
|
+
readonly approvals: ReadonlyArray<BatchItemApproval>;
|
|
390
|
+
}) => Fx<BatchAggregate>;
|
|
391
|
+
/**
|
|
392
|
+
* Decline the batch and every planned attempt under it, in one transaction. Terminal.
|
|
393
|
+
*
|
|
394
|
+
* Rejecting again returns the batch unchanged; a batch that reached `approved` or later
|
|
395
|
+
* fails `Stale`.
|
|
396
|
+
*/
|
|
397
|
+
readonly reject: (id: BatchId, input: {
|
|
398
|
+
readonly actorId: string;
|
|
399
|
+
readonly reason: string | null;
|
|
400
|
+
}) => Fx<BatchAggregate>;
|
|
401
|
+
/** Recompute the stored status from the items' attempts, after applying moved them. */
|
|
402
|
+
readonly refresh: (id: BatchId) => Fx<BatchAggregate>;
|
|
403
|
+
};
|
|
250
404
|
readonly readiness: {
|
|
251
405
|
/** One row per (owner, domain); `attachmentId`, when set, must exist for the owner. */
|
|
252
406
|
readonly put: (readiness: Readiness) => Fx<void>;
|
|
@@ -334,6 +488,28 @@ export interface AsyncInterface {
|
|
|
334
488
|
readonly complete: (principal: Principal.Interface, id: Plan.PlanId, receipt: Receipt.Model) => Promise<Attempt>;
|
|
335
489
|
readonly fail: (principal: Principal.Interface, id: Plan.PlanId, message: string) => Promise<Attempt>;
|
|
336
490
|
};
|
|
491
|
+
readonly batches: {
|
|
492
|
+
readonly create: (principal: Principal.Interface, input: {
|
|
493
|
+
readonly kind: Plan.Kind;
|
|
494
|
+
readonly idempotencyKey: string;
|
|
495
|
+
readonly attachmentIds: ReadonlyArray<string>;
|
|
496
|
+
}) => Promise<BatchAggregate>;
|
|
497
|
+
readonly get: (principal: Principal.Interface, id: BatchId) => Promise<BatchAggregate>;
|
|
498
|
+
readonly byIdempotencyKey: (principal: Principal.Interface, key: string) => Promise<BatchAggregate | null>;
|
|
499
|
+
readonly listUnfinished: (principal: Principal.Interface) => Promise<ReadonlyArray<BatchAggregate>>;
|
|
500
|
+
readonly recordItemPlan: (principal: Principal.Interface, id: BatchId, attachmentId: string, attemptId: Plan.PlanId) => Promise<BatchAggregate>;
|
|
501
|
+
readonly recordItemPlanFailure: (principal: Principal.Interface, id: BatchId, attachmentId: string, message: string) => Promise<BatchAggregate>;
|
|
502
|
+
readonly approve: (principal: Principal.Interface, id: BatchId, input: {
|
|
503
|
+
readonly digest: Plan.Digest;
|
|
504
|
+
readonly actorId: string;
|
|
505
|
+
readonly approvals: ReadonlyArray<BatchItemApproval>;
|
|
506
|
+
}) => Promise<BatchAggregate>;
|
|
507
|
+
readonly reject: (principal: Principal.Interface, id: BatchId, input: {
|
|
508
|
+
readonly actorId: string;
|
|
509
|
+
readonly reason: string | null;
|
|
510
|
+
}) => Promise<BatchAggregate>;
|
|
511
|
+
readonly refresh: (principal: Principal.Interface, id: BatchId) => Promise<BatchAggregate>;
|
|
512
|
+
};
|
|
337
513
|
readonly readiness: {
|
|
338
514
|
readonly put: (principal: Principal.Interface, readiness: Readiness) => Promise<void>;
|
|
339
515
|
readonly get: (principal: Principal.Interface, domain: string) => Promise<Readiness | null>;
|
|
@@ -29,6 +29,7 @@ export declare const make: (storage: Storage.Interface, connect: Connect.Interfa
|
|
|
29
29
|
readonly sourceReceiptId: Receipt.ReceiptId | null;
|
|
30
30
|
}) => Fx<Plan.Model>;
|
|
31
31
|
approve: (plan: Plan.Model | Plan.PlanId, options?: ApproveOptions) => Fx<Approval.Model>;
|
|
32
|
+
draftApproval: (attempt: Storage.Attempt, options?: ApproveOptions) => Fx<Approval.Model>;
|
|
32
33
|
reject: (plan: Plan.Model | Plan.PlanId, options?: {
|
|
33
34
|
readonly reason?: string;
|
|
34
35
|
}) => Fx<Storage.Attempt>;
|
|
@@ -7,7 +7,7 @@ import { Effect, Schema } from "effect";
|
|
|
7
7
|
import * as Reason from "../Reason.ts";
|
|
8
8
|
export type Category = "request" | "auth" | "plan" | "provider" | "storage" | "internal";
|
|
9
9
|
declare const DomainKitError_base: Schema.Class<DomainKitError, Schema.TaggedStruct<"DomainKitError", {
|
|
10
|
-
readonly reason: Schema.suspend<Schema.Union<readonly [typeof Reason.InvalidInput, typeof Reason.Unauthenticated, typeof Reason.Forbidden, typeof Reason.NotFound, typeof Reason.Conflict, typeof Reason.Stale, typeof Reason.Expired, typeof Reason.Busy, typeof Reason.ProviderRejected, typeof Reason.ProviderUnavailable, typeof Reason.ProviderConflict, typeof Reason.Unsupported, typeof Reason.Reconnect, typeof Reason.StorageFailed, typeof Reason.CryptoFailed, typeof Reason.ResolverFailed]>>;
|
|
10
|
+
readonly reason: Schema.suspend<Schema.Union<readonly [typeof Reason.InvalidInput, typeof Reason.Unauthenticated, typeof Reason.Forbidden, typeof Reason.NotFound, typeof Reason.Conflict, typeof Reason.Stale, typeof Reason.BatchStale, typeof Reason.Expired, typeof Reason.Busy, typeof Reason.ProviderRejected, typeof Reason.ProviderUnavailable, typeof Reason.ProviderConflict, typeof Reason.Unsupported, typeof Reason.Reconnect, typeof Reason.StorageFailed, typeof Reason.CryptoFailed, typeof Reason.ResolverFailed]>>;
|
|
11
11
|
}>, import("effect/Cause").YieldableError>;
|
|
12
12
|
export declare class DomainKitError extends DomainKitError_base {
|
|
13
13
|
get category(): Category;
|