domainkit 0.14.1 → 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/Cloudflare.mjs +16 -7
- package/dist/Cloudflare.mjs.map +1 -1
- package/dist/Connect.mjs +5 -1
- package/dist/Connect.mjs.map +1 -1
- package/dist/Provider.mjs.map +1 -1
- 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 +89 -5
- package/dist/Server.mjs.map +1 -1
- package/dist/Storage.mjs +105 -2
- package/dist/Storage.mjs.map +1 -1
- package/dist/Testing.mjs +2 -0
- package/dist/Testing.mjs.map +1 -1
- package/dist/internal/attempts.mjs +14 -4
- package/dist/internal/attempts.mjs.map +1 -1
- package/dist/internal/conformance/provider.mjs +1 -0
- package/dist/internal/conformance/provider.mjs.map +1 -1
- package/dist/internal/conformance/storage.mjs +185 -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/Connect.d.ts +3 -1
- package/dist/types/Provider.d.ts +6 -0
- package/dist/types/Provision.d.ts +124 -3
- package/dist/types/Reason.d.ts +18 -2
- package/dist/types/Server.d.ts +3623 -202
- package/dist/types/Storage.d.ts +181 -0
- package/dist/types/Testing.d.ts +2 -0
- package/dist/types/internal/attempts.d.ts +1 -0
- package/dist/types/internal/error.d.ts +1 -1
- package/package.json +2 -2
|
@@ -30,6 +30,7 @@ const authorizationRow = (principal, id, now) => new Authorization({
|
|
|
30
30
|
method: "token",
|
|
31
31
|
capabilities: ["dns:read"],
|
|
32
32
|
context: { account: "acc" },
|
|
33
|
+
label: "Acme",
|
|
33
34
|
revocation: "active",
|
|
34
35
|
createdBy: principal.actorId,
|
|
35
36
|
createdAt: now
|
|
@@ -158,6 +159,7 @@ const cases = (layer) => {
|
|
|
158
159
|
yield* storage.authorizations.promoteCapabilities("auth-cred", ["dns:write", "dns:read"]);
|
|
159
160
|
const promoted = yield* storage.authorizations.get("auth-cred");
|
|
160
161
|
yield* expect([...promoted.capabilities].sort().join(",") === "dns:read,dns:write", "promoteCapabilities did not merge capabilities");
|
|
162
|
+
yield* expect(promoted.label === "Acme", "authorization label was not stored");
|
|
161
163
|
}).pipe(Effect.provideService(Service, owner)))
|
|
162
164
|
},
|
|
163
165
|
{
|
|
@@ -418,6 +420,189 @@ const cases = (layer) => {
|
|
|
418
420
|
yield* expect(afterFailure === "free", "lock was not released after a failure");
|
|
419
421
|
}).pipe(Effect.provideService(Service, owner)))
|
|
420
422
|
},
|
|
423
|
+
{
|
|
424
|
+
name: "creates a batch once per idempotency key and keeps it inside its owner",
|
|
425
|
+
run: run(Effect.gen(function* () {
|
|
426
|
+
const first = yield* connect(owner, "auth-batch-key");
|
|
427
|
+
const second = yield* connect(owner, "auth-batch-key-2");
|
|
428
|
+
const storage = first.storage;
|
|
429
|
+
const attachmentIds = [first.attachment.id, second.attachment.id];
|
|
430
|
+
const created = yield* storage.batches.create({
|
|
431
|
+
kind: "provisioning",
|
|
432
|
+
idempotencyKey: "batch-key-create",
|
|
433
|
+
attachmentIds
|
|
434
|
+
});
|
|
435
|
+
yield* expect(created.batch.status === "planning", "a new batch is not planning");
|
|
436
|
+
yield* expect(created.items.map((item) => item.position).join(",") === "0,1", "batch items did not keep the order they were created in");
|
|
437
|
+
yield* expect(created.items.every((item) => item.attemptId === null && item.planFailure === null), "a new batch item already points at an attempt");
|
|
438
|
+
const replay = yield* storage.batches.create({
|
|
439
|
+
kind: "provisioning",
|
|
440
|
+
idempotencyKey: "batch-key-create",
|
|
441
|
+
attachmentIds: [first.attachment.id]
|
|
442
|
+
});
|
|
443
|
+
yield* expect(replay.batch.id === created.batch.id && replay.items.length === 2, "a replayed idempotency key did not return the stored batch");
|
|
444
|
+
yield* expectReason(storage.batches.create({
|
|
445
|
+
kind: "provisioning",
|
|
446
|
+
idempotencyKey: "batch-key-empty",
|
|
447
|
+
attachmentIds: []
|
|
448
|
+
}), "InvalidInput", "batches.create with no attachments");
|
|
449
|
+
yield* expectReason(storage.batches.create({
|
|
450
|
+
kind: "provisioning",
|
|
451
|
+
idempotencyKey: "batch-key-duplicate",
|
|
452
|
+
attachmentIds: [first.attachment.id, first.attachment.id]
|
|
453
|
+
}), "InvalidInput", "batches.create with a repeated attachment");
|
|
454
|
+
const found = yield* storage.batches.byIdempotencyKey("batch-key-create");
|
|
455
|
+
yield* expect(Option.isSome(found) && found.value.batch.id === created.batch.id, "batches.byIdempotencyKey did not find the batch the key made");
|
|
456
|
+
const unknown = yield* storage.batches.byIdempotencyKey("batch-key-never-used");
|
|
457
|
+
yield* expect(Option.isNone(unknown), "batches.byIdempotencyKey invented a batch for an unused key");
|
|
458
|
+
const asOther = (effect) => effect.pipe(Effect.provideService(Service, other));
|
|
459
|
+
yield* expectReason(asOther(storage.batches.get(created.batch.id)), "NotFound", "batches.get");
|
|
460
|
+
const foreignKey = yield* asOther(storage.batches.byIdempotencyKey("batch-key-create"));
|
|
461
|
+
yield* expect(Option.isNone(foreignKey), "batches.byIdempotencyKey leaked across owners");
|
|
462
|
+
const foreign = yield* asOther(storage.batches.listUnfinished());
|
|
463
|
+
yield* expect(!foreign.some(({ batch }) => batch.id === created.batch.id), "batches.listUnfinished leaked across owners");
|
|
464
|
+
const unfinished = yield* storage.batches.listUnfinished();
|
|
465
|
+
yield* expect(unfinished.some(({ batch }) => batch.id === created.batch.id), "a planning batch is missing from the unfinished index");
|
|
466
|
+
}).pipe(Effect.provideService(Service, owner)))
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
name: "recomputes batch status from its items and approves every attempt at once",
|
|
470
|
+
run: run(Effect.gen(function* () {
|
|
471
|
+
const first = yield* connect(owner, "auth-batch-plan");
|
|
472
|
+
const second = yield* connect(owner, "auth-batch-plan-2");
|
|
473
|
+
const storage = first.storage;
|
|
474
|
+
const now = yield* DateTime.now;
|
|
475
|
+
const planA = planRow(first.attachment.id, now, "batch-a");
|
|
476
|
+
const planB = planRow(second.attachment.id, now, "batch-b");
|
|
477
|
+
yield* storage.attempts.create(attemptRow(owner, planA));
|
|
478
|
+
yield* storage.attempts.create(attemptRow(owner, planB));
|
|
479
|
+
const { batch } = yield* storage.batches.create({
|
|
480
|
+
kind: "provisioning",
|
|
481
|
+
idempotencyKey: "batch-key-plan",
|
|
482
|
+
attachmentIds: [first.attachment.id, second.attachment.id]
|
|
483
|
+
});
|
|
484
|
+
const approvalA = approvalRow(planA, "batch-a");
|
|
485
|
+
const approvalB = approvalRow(planB, "batch-b");
|
|
486
|
+
const approvals = [{
|
|
487
|
+
attachmentId: first.attachment.id,
|
|
488
|
+
approval: approvalA
|
|
489
|
+
}, {
|
|
490
|
+
attachmentId: second.attachment.id,
|
|
491
|
+
approval: approvalB
|
|
492
|
+
}];
|
|
493
|
+
const digest = Digest.make("batch-digest-1");
|
|
494
|
+
yield* expectReason(storage.batches.approve(batch.id, {
|
|
495
|
+
digest,
|
|
496
|
+
actorId: owner.actorId,
|
|
497
|
+
approvals
|
|
498
|
+
}), "BatchStale", "batches.approve before every item is planned");
|
|
499
|
+
const failed = yield* storage.batches.recordItemPlanFailure(batch.id, first.attachment.id, "the provider was unavailable");
|
|
500
|
+
yield* expect(failed.batch.status === "planning" && failed.items[0]?.planFailure === "the provider was unavailable", "a plan failure did not leave the batch planning");
|
|
501
|
+
const partiallyPlanned = yield* storage.batches.recordItemPlan(batch.id, first.attachment.id, planA.id);
|
|
502
|
+
yield* expect(partiallyPlanned.batch.status === "planning" && partiallyPlanned.items[0]?.planFailure === null, "a landed plan did not clear the item's failure");
|
|
503
|
+
const planned = yield* storage.batches.recordItemPlan(batch.id, second.attachment.id, planB.id);
|
|
504
|
+
yield* expect(planned.batch.status === "planned", "a batch whose items are all planned is not planned");
|
|
505
|
+
const approved = yield* storage.batches.approve(batch.id, {
|
|
506
|
+
digest,
|
|
507
|
+
actorId: owner.actorId,
|
|
508
|
+
approvals
|
|
509
|
+
});
|
|
510
|
+
yield* expect(approved.batch.status === "approved" && approved.batch.digest === digest, "batches.approve did not bind the digest");
|
|
511
|
+
const attemptA = yield* storage.attempts.get(planA.id);
|
|
512
|
+
const attemptB = yield* storage.attempts.get(planB.id);
|
|
513
|
+
yield* expect(attemptA.approval?.id === approvalA.id && attemptB.approval?.id === approvalB.id, "batches.approve did not approve every item's attempt");
|
|
514
|
+
const again = yield* storage.batches.approve(batch.id, {
|
|
515
|
+
digest,
|
|
516
|
+
actorId: owner.actorId,
|
|
517
|
+
approvals
|
|
518
|
+
});
|
|
519
|
+
yield* expect(again.batch.status === "approved", "approving an approved batch under the same digest is not idempotent");
|
|
520
|
+
yield* expectReason(storage.batches.approve(batch.id, {
|
|
521
|
+
digest: Digest.make("batch-digest-moved"),
|
|
522
|
+
actorId: owner.actorId,
|
|
523
|
+
approvals
|
|
524
|
+
}), "BatchStale", "batches.approve with a digest the batch does not hold");
|
|
525
|
+
yield* expectReason(storage.batches.reject(batch.id, {
|
|
526
|
+
actorId: owner.actorId,
|
|
527
|
+
reason: null
|
|
528
|
+
}), "BatchStale", "batches.reject after approval");
|
|
529
|
+
const lease = DateTime.add(now, { minutes: 2 });
|
|
530
|
+
yield* storage.attempts.claim(planA.id, lease);
|
|
531
|
+
yield* storage.attempts.complete(planA.id, receiptRow(planA, approvalA, "batch-a"));
|
|
532
|
+
const applying = yield* storage.batches.refresh(batch.id);
|
|
533
|
+
yield* expect(applying.batch.status === "applying", "a batch with one item left is not applying");
|
|
534
|
+
yield* storage.attempts.claim(planB.id, lease);
|
|
535
|
+
yield* storage.attempts.complete(planB.id, receiptRow(planB, approvalB, "batch-b"));
|
|
536
|
+
const complete = yield* storage.batches.refresh(batch.id);
|
|
537
|
+
yield* expect(complete.batch.status === "complete" && complete.batch.completedAt !== null, "a batch whose items all completed is not complete");
|
|
538
|
+
const unfinished = yield* storage.batches.listUnfinished();
|
|
539
|
+
yield* expect(!unfinished.some((entry) => entry.batch.id === batch.id), "a complete batch is still in the unfinished index");
|
|
540
|
+
}).pipe(Effect.provideService(Service, owner)))
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
name: "refuses a batch approval for an attempt that was declined on its own",
|
|
544
|
+
run: run(Effect.gen(function* () {
|
|
545
|
+
const { storage, attachment } = yield* connect(owner, "auth-batch-declined");
|
|
546
|
+
const now = yield* DateTime.now;
|
|
547
|
+
const plan = planRow(attachment.id, now, "batch-declined");
|
|
548
|
+
yield* storage.attempts.create(attemptRow(owner, plan));
|
|
549
|
+
const { batch } = yield* storage.batches.create({
|
|
550
|
+
kind: "provisioning",
|
|
551
|
+
idempotencyKey: "batch-key-declined",
|
|
552
|
+
attachmentIds: [attachment.id]
|
|
553
|
+
});
|
|
554
|
+
const planned = yield* storage.batches.recordItemPlan(batch.id, attachment.id, plan.id);
|
|
555
|
+
yield* expect(planned.batch.status === "planned", "the batch is not planned");
|
|
556
|
+
yield* storage.attempts.reject(plan.id, {
|
|
557
|
+
digest: plan.digest,
|
|
558
|
+
actorId: owner.actorId,
|
|
559
|
+
reason: null
|
|
560
|
+
});
|
|
561
|
+
yield* expectReason(storage.batches.approve(batch.id, {
|
|
562
|
+
digest: Digest.make("batch-digest-declined"),
|
|
563
|
+
actorId: owner.actorId,
|
|
564
|
+
approvals: [{
|
|
565
|
+
attachmentId: attachment.id,
|
|
566
|
+
approval: approvalRow(plan, "batch-declined")
|
|
567
|
+
}]
|
|
568
|
+
}), "Stale", "batches.approve over a declined attempt");
|
|
569
|
+
const attempt = yield* storage.attempts.get(plan.id);
|
|
570
|
+
yield* expect(attempt.status === "rejected" && attempt.approval === null, "batches.approve reopened a declined attempt");
|
|
571
|
+
}).pipe(Effect.provideService(Service, owner)))
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
name: "declines a batch with its planned attempts and fences a late planner",
|
|
575
|
+
run: run(Effect.gen(function* () {
|
|
576
|
+
const { storage, attachment } = yield* connect(owner, "auth-batch-reject");
|
|
577
|
+
const now = yield* DateTime.now;
|
|
578
|
+
const plan = planRow(attachment.id, now, "batch-reject");
|
|
579
|
+
const late = planRow(attachment.id, now, "batch-reject-late");
|
|
580
|
+
yield* storage.attempts.create(attemptRow(owner, plan));
|
|
581
|
+
yield* storage.attempts.create(attemptRow(owner, late));
|
|
582
|
+
const { batch } = yield* storage.batches.create({
|
|
583
|
+
kind: "provisioning",
|
|
584
|
+
idempotencyKey: "batch-key-reject",
|
|
585
|
+
attachmentIds: [attachment.id]
|
|
586
|
+
});
|
|
587
|
+
yield* storage.batches.recordItemPlan(batch.id, attachment.id, plan.id);
|
|
588
|
+
const rejected = yield* storage.batches.reject(batch.id, {
|
|
589
|
+
actorId: owner.actorId,
|
|
590
|
+
reason: "the customer changed their mind"
|
|
591
|
+
});
|
|
592
|
+
yield* expect(rejected.batch.status === "rejected" && rejected.batch.completedAt !== null, "batches.reject is not terminal");
|
|
593
|
+
const attempt = yield* storage.attempts.get(plan.id);
|
|
594
|
+
yield* expect(attempt.status === "rejected", "batches.reject left a planned attempt open");
|
|
595
|
+
const twice = yield* storage.batches.reject(batch.id, {
|
|
596
|
+
actorId: owner.actorId,
|
|
597
|
+
reason: null
|
|
598
|
+
});
|
|
599
|
+
yield* expect(twice.batch.rejection?.reason === "the customer changed their mind", "a second rejection overwrote the first");
|
|
600
|
+
yield* expectReason(storage.batches.recordItemPlan(batch.id, attachment.id, late.id), "BatchStale", "batches.recordItemPlan on a rejected batch");
|
|
601
|
+
yield* expectReason(storage.batches.recordItemPlanFailure(batch.id, attachment.id, "too late"), "BatchStale", "batches.recordItemPlanFailure on a rejected batch");
|
|
602
|
+
const unfinished = yield* storage.batches.listUnfinished();
|
|
603
|
+
yield* expect(!unfinished.some((entry) => entry.batch.id === batch.id), "a rejected batch is still in the unfinished index");
|
|
604
|
+
}).pipe(Effect.provideService(Service, owner)))
|
|
605
|
+
},
|
|
421
606
|
{
|
|
422
607
|
name: "stores readiness per domain, with or without an attachment",
|
|
423
608
|
run: run(Effect.gen(function* () {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.mjs","names":["Principal.make","Errors.isDomainKitError","Storage.Authorization","Storage.Credential","Storage.Service","Principal.Service","Plan.Model","Plan.Create","DnsRecord.txt","Storage.Attempt","Approval.Model","Receipt.Model","Receipt.Applied","Storage.Continuation","Storage.Readiness"],"sources":["../../../src/internal/conformance/storage.ts"],"sourcesContent":["import { DateTime, Deferred, Effect, Fiber, Layer, Option } from \"effect\";\n\nimport * as Approval from \"../../Approval.ts\";\nimport * as DnsRecord from \"../../DnsRecord.ts\";\nimport * as Errors from \"../error.ts\";\nimport * as Reason from \"../../Reason.ts\";\nimport * as Plan from \"../../Plan.ts\";\nimport * as Principal from \"../../Principal.ts\";\nimport * as Receipt from \"../../Receipt.ts\";\nimport * as Storage from \"../../Storage.ts\";\n\nexport interface Case {\n readonly name: string;\n readonly run: Effect.Effect<void, unknown>;\n}\n\nexport interface Options {\n /** Register each case with a test runner, e.g. vitest's `it`. */\n readonly it?: (name: string, run: () => Promise<void>) => void;\n}\n\nconst owner = Principal.make({ ownerId: \"conformance-owner-a\", actorId: \"actor-a\" });\nconst other = Principal.make({ ownerId: \"conformance-owner-b\", actorId: \"actor-b\" });\n\nclass Failure extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"StorageConformanceFailure\";\n }\n}\n\nconst expect = (condition: boolean, message: string) =>\n condition ? Effect.void : Effect.fail(new Failure(message));\n\nconst expectReason = <A, R>(\n effect: Effect.Effect<A, unknown, R>,\n tag: Reason.Model[\"_tag\"],\n label: string,\n): Effect.Effect<void, Failure, R> =>\n effect.pipe(\n Effect.flip,\n Effect.mapError(() => new Failure(`${label}: expected failure ${tag}, effect succeeded`)),\n Effect.flatMap((cause) =>\n expect(\n Errors.isDomainKitError(cause) && cause.reason._tag === tag,\n `${label}: expected reason ${tag}, received ${Errors.isDomainKitError(cause) ? cause.reason._tag : String(cause)}`,\n ),\n ),\n );\n\nconst authorizationRow = (principal: Principal.Interface, id: string, now: DateTime.Utc) =>\n new Storage.Authorization({\n id,\n ownerId: principal.ownerId,\n provider: \"fake\",\n method: \"token\",\n capabilities: [\"dns:read\"],\n context: { account: \"acc\" },\n revocation: \"active\",\n createdBy: principal.actorId,\n createdAt: now,\n });\n\nconst credentialRow = (now: DateTime.Utc, ciphertext = \"sealed-1\") =>\n new Storage.Credential({ ciphertext, expiresAt: null, rotatedAt: now });\n\nconst connect = (principal: Principal.Interface, id: string) =>\n Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const now = yield* DateTime.now;\n yield* storage.authorizations.upsert({\n authorization: authorizationRow(principal, id, now),\n credential: credentialRow(now),\n });\n const connection = yield* storage.connections.create(id);\n const attachment = yield* storage.attachments.create({\n connectionId: connection.id,\n domain: `app.${id}.example.com`,\n zone: `${id}.example.com`,\n label: `${id}.example.com (${id})`,\n target: { zoneId: `zone-${id}` },\n });\n return { storage, connection, attachment };\n }).pipe(Effect.provideService(Principal.Service, principal));\n\nconst planRow = (attachmentId: string, now: DateTime.Utc, suffix: string) =>\n new Plan.Model({\n id: Plan.PlanId.make(`plan-${suffix}`),\n version: \"domainkit.plan.v2\",\n kind: \"provisioning\",\n digest: Plan.Digest.make(`digest-${suffix}`),\n domain: \"app.example.com\",\n zone: \"example.com\",\n provider: \"fake\",\n attachmentId,\n operations: [\n new Plan.Create({\n id: Plan.OperationId.make(`op-${suffix}`),\n record: DnsRecord.txt({ name: \"app.example.com\", value: suffix }),\n }),\n ],\n createdAt: now,\n expiresAt: DateTime.add(now, { hours: 1 }),\n });\n\nconst attemptRow = (principal: Principal.Interface, plan: Plan.Model) =>\n new Storage.Attempt({\n id: plan.id,\n ownerId: principal.ownerId,\n attachmentId: plan.attachmentId,\n kind: plan.kind,\n status: \"planned\",\n plan,\n approval: null,\n receipt: null,\n rejection: null,\n sourceReceiptId: null,\n leaseExpiresAt: null,\n failure: null,\n updatedAt: plan.createdAt,\n });\n\nconst approvalRow = (plan: Plan.Model, suffix: string) =>\n new Approval.Model({\n id: Approval.ApprovalId.make(`approval-${suffix}`),\n version: \"domainkit.approval.v2\",\n kind: plan.kind,\n planId: plan.id,\n digest: plan.digest,\n operationIds: plan.operations.map(({ id }) => id),\n approvedBy: \"actor\",\n approvedAt: plan.createdAt,\n expiresAt: plan.expiresAt,\n });\n\nconst receiptRow = (plan: Plan.Model, approval: Approval.Model, suffix: string) =>\n new Receipt.Model({\n id: Receipt.ReceiptId.make(`receipt-${suffix}`),\n version: \"domainkit.receipt.v2\",\n kind: plan.kind,\n planId: plan.id,\n approvalId: approval.id,\n digest: plan.digest,\n provider: plan.provider,\n zone: plan.zone,\n status: \"complete\",\n outcomes: plan.operations.map(\n (operation) => new Receipt.Applied({ operationId: operation.id, providerRecordId: \"r1\" }),\n ),\n appliedAt: plan.createdAt,\n });\n\n/** Every invariant a `Storage` implementation must hold. Each case runs against a fresh layer. */\nexport const cases = (layer: Layer.Layer<Storage.Service, unknown>): ReadonlyArray<Case> => {\n const run = <E>(effect: Effect.Effect<void, E, Storage.Service>): Effect.Effect<void, unknown> =>\n effect.pipe(Effect.provide(layer));\n return [\n {\n name: \"scopes every row by the principal's owner\",\n run: run(\n Effect.gen(function* () {\n const { storage, connection, attachment } = yield* connect(owner, \"auth-a\");\n const asOther = <A, E>(effect: Effect.Effect<A, E, Principal.Service>) =>\n effect.pipe(Effect.provideService(Principal.Service, other));\n yield* expectReason(\n asOther(storage.authorizations.get(\"auth-a\")),\n \"NotFound\",\n \"authorizations.get\",\n );\n yield* expectReason(\n asOther(storage.authorizations.credential(\"auth-a\")),\n \"NotFound\",\n \"authorizations.credential\",\n );\n yield* expectReason(\n asOther(storage.connections.get(connection.id)),\n \"NotFound\",\n \"connections.get\",\n );\n yield* expectReason(\n asOther(storage.attachments.get(attachment.id)),\n \"NotFound\",\n \"attachments.get\",\n );\n yield* expectReason(\n asOther(storage.attachments.list(connection.id)),\n \"NotFound\",\n \"attachments.list\",\n );\n const connections = yield* asOther(storage.connections.list());\n yield* expect(connections.length === 0, \"connections.list leaked across owners\");\n const byDomain = yield* asOther(storage.attachments.byDomain(attachment.domain));\n yield* expect(Option.isNone(byDomain), \"attachments.byDomain leaked across owners\");\n const now = yield* DateTime.now;\n yield* expectReason(\n asOther(\n storage.authorizations.upsert({\n authorization: authorizationRow(owner, \"auth-b\", now),\n credential: credentialRow(now),\n }),\n ),\n \"InvalidInput\",\n \"authorizations.upsert with a foreign ownerId\",\n );\n }),\n ),\n },\n {\n name: \"stores, rotates, and promotes credentials without exposing plaintext\",\n run: run(\n Effect.gen(function* () {\n const { storage } = yield* connect(owner, \"auth-cred\");\n const first = yield* storage.authorizations.credential(\"auth-cred\");\n yield* expect(first.ciphertext === \"sealed-1\", \"credential ciphertext was not stored\");\n const now = yield* DateTime.now;\n yield* storage.authorizations.rotate(\"auth-cred\", credentialRow(now, \"sealed-2\"));\n const rotated = yield* storage.authorizations.credential(\"auth-cred\");\n yield* expect(rotated.ciphertext === \"sealed-2\", \"rotate did not replace the credential\");\n yield* storage.authorizations.promoteCapabilities(\"auth-cred\", [\"dns:write\", \"dns:read\"]);\n const promoted = yield* storage.authorizations.get(\"auth-cred\");\n yield* expect(\n [...promoted.capabilities].sort().join(\",\") === \"dns:read,dns:write\",\n \"promoteCapabilities did not merge capabilities\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"replaces an authorization only through the expected id\",\n run: run(\n Effect.gen(function* () {\n const { storage } = yield* connect(owner, \"auth-cas\");\n const now = yield* DateTime.now;\n const replaced = yield* storage.authorizations.upsert({\n authorization: new Storage.Authorization({\n ...authorizationRow(owner, \"auth-cas\", now),\n method: \"oauth\",\n }),\n credential: credentialRow(now, \"sealed-oauth\"),\n expectedId: \"auth-cas\",\n });\n yield* expect(\n replaced.method === \"oauth\",\n \"upsert with expectedId did not replace the row\",\n );\n const credential = yield* storage.authorizations.credential(\"auth-cas\");\n yield* expect(\n credential.ciphertext === \"sealed-oauth\",\n \"upsert with expectedId did not replace the credential\",\n );\n yield* expectReason(\n storage.authorizations.upsert({\n authorization: authorizationRow(owner, \"auth-missing\", now),\n credential: credentialRow(now),\n expectedId: \"auth-missing\",\n }),\n \"NotFound\",\n \"upsert with an unknown expectedId\",\n );\n yield* expectReason(\n storage.authorizations.upsert({\n authorization: authorizationRow(owner, \"auth-cas\", now),\n credential: credentialRow(now),\n }),\n \"InvalidInput\",\n \"insert of an existing id\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"consumes continuations exactly once and rejects expired ones\",\n run: run(\n Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const now = yield* DateTime.now;\n const continuation = new Storage.Continuation({\n id: \"cont-1\",\n ownerId: owner.ownerId,\n actorId: owner.actorId,\n provider: \"fake\",\n payload: { codeVerifier: \"v\" },\n returnTo: null,\n expiresAt: DateTime.add(now, { minutes: 15 }),\n });\n yield* storage.continuations.put(continuation);\n yield* expectReason(\n storage.continuations\n .consume(\"cont-1\")\n .pipe(Effect.provideService(Principal.Service, other)),\n \"NotFound\",\n \"consume by another owner\",\n );\n const peeked = yield* storage.continuations.get(\"cont-1\");\n yield* expect(peeked.id === \"cont-1\", \"get did not return the continuation\");\n const consumed = yield* storage.continuations.consume(\"cont-1\");\n yield* expect(consumed.id === \"cont-1\", \"consume did not return the continuation\");\n yield* expectReason(storage.continuations.get(\"cont-1\"), \"NotFound\", \"get after consume\");\n yield* expectReason(\n storage.continuations.consume(\"cont-1\"),\n \"NotFound\",\n \"second consume\",\n );\n yield* storage.continuations.put(\n new Storage.Continuation({\n ...continuation,\n id: \"cont-2\",\n expiresAt: DateTime.subtract(now, { minutes: 1 }),\n }),\n );\n yield* expectReason(storage.continuations.get(\"cont-2\"), \"Expired\", \"expired get\");\n yield* expectReason(\n storage.continuations.consume(\"cont-2\"),\n \"Expired\",\n \"expired consume\",\n );\n yield* expectReason(\n storage.continuations.consume(\"cont-2\"),\n \"NotFound\",\n \"expired consume is also spent\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"reads a continuation header under any principal and still refuses an expired one\",\n run: run(\n Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const now = yield* DateTime.now;\n const continuation = new Storage.Continuation({\n id: \"cont-header-1\",\n ownerId: owner.ownerId,\n actorId: owner.actorId,\n provider: \"fake\",\n payload: { codeVerifier: \"v\" },\n returnTo: null,\n expiresAt: DateTime.add(now, { minutes: 15 }),\n });\n yield* storage.continuations.put(continuation);\n // The deliberate exception to the tenant isolation every other case here enforces, and\n // not a bug. A provider callback is a top-level navigation carrying a continuation id\n // and a session cookie, so the route has to learn whose flow it is before it can name a\n // principal to scope by. The read therefore takes no principal at all — that is a type\n // constraint, and providing a foreign one below changes nothing — and it answers with\n // header fields only. `payload` holds the PKCE verifier and stays behind the\n // owner-scoped `get`. Binding the caller to the flow is the reader's job, and\n // `domainkit/server` does it on the callback route.\n const foreign = yield* storage.continuations\n .header(\"cont-header-1\")\n .pipe(Effect.provideService(Principal.Service, other));\n yield* expect(\n foreign.ownerId === owner.ownerId && foreign.actorId === owner.actorId,\n \"header did not report the recorded owner and actor\",\n );\n yield* expect(foreign.provider === \"fake\", \"header did not report the provider\");\n yield* expect(!(\"payload\" in foreign), \"header exposed the continuation payload\");\n // A read, never a claim: the flow is still there to be spent.\n const still = yield* storage.continuations.get(\"cont-header-1\");\n yield* expect(still.id === \"cont-header-1\", \"the header read spent the continuation\");\n yield* expectReason(\n storage.continuations.header(\"cont-header-missing\"),\n \"NotFound\",\n \"header of an unknown continuation\",\n );\n yield* storage.continuations.put(\n new Storage.Continuation({\n ...continuation,\n id: \"cont-header-2\",\n expiresAt: DateTime.subtract(now, { minutes: 1 }),\n }),\n );\n yield* expectReason(\n storage.continuations.header(\"cont-header-2\"),\n \"Expired\",\n \"header of an expired continuation\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"moves attempts through planned, approved, applying, and complete with leases\",\n run: run(\n Effect.gen(function* () {\n const { storage, attachment } = yield* connect(owner, \"auth-attempt\");\n const now = yield* DateTime.now;\n const plan = planRow(attachment.id, now, \"1\");\n yield* storage.attempts.create(attemptRow(owner, plan));\n yield* expectReason(\n storage.attempts.create(attemptRow(owner, plan)),\n \"InvalidInput\",\n \"duplicate attempt\",\n );\n yield* expectReason(\n storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })),\n \"Stale\",\n \"claim before approval\",\n );\n const approval = approvalRow(plan, \"1\");\n const approved = yield* storage.attempts.approve(plan.id, approval);\n yield* expect(approved.status === \"approved\", \"approve did not move the status\");\n const again = yield* storage.attempts.approve(plan.id, approval);\n yield* expect(\n again.approval?.id === approval.id,\n \"re-approving with the same approval must be idempotent\",\n );\n yield* expectReason(\n storage.attempts.approve(plan.id, approvalRow(plan, \"other\")),\n \"Stale\",\n \"second approval\",\n );\n const byApproval = yield* storage.attempts.byApproval(approval.id);\n yield* expect(byApproval.id === plan.id, \"byApproval did not find the attempt\");\n const claimed = yield* storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 }));\n yield* expect(\n claimed.status === \"applying\" && claimed.leaseExpiresAt !== null,\n \"claim did not lease\",\n );\n yield* expectReason(\n storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })),\n \"Busy\",\n \"claim while leased\",\n );\n yield* storage.attempts.fail(plan.id, \"provider down\");\n const failed = yield* storage.attempts.get(plan.id);\n yield* expect(\n failed.status === \"failed\" && failed.failure === \"provider down\",\n \"fail did not record the message\",\n );\n const reclaimed = yield* storage.attempts.claim(\n plan.id,\n DateTime.subtract(now, { minutes: 1 }),\n );\n yield* expect(reclaimed.failure === null, \"claim did not clear the failure\");\n const expiredLease = yield* storage.attempts.claim(\n plan.id,\n DateTime.add(now, { minutes: 2 }),\n );\n yield* expect(expiredLease.status === \"applying\", \"an expired lease must be reclaimable\");\n const receipt = receiptRow(plan, approval, \"1\");\n const completed = yield* storage.attempts.complete(plan.id, receipt);\n yield* expect(\n completed.status === \"complete\" && completed.leaseExpiresAt === null,\n \"complete did not clear the lease\",\n );\n yield* expectReason(\n storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })),\n \"Stale\",\n \"claim after completion\",\n );\n const byReceipt = yield* storage.attempts.byReceipt(receipt.id);\n yield* expect(byReceipt.receipt?.id === receipt.id, \"byReceipt did not find the attempt\");\n const later = planRow(attachment.id, DateTime.add(now, { seconds: 1 }), \"2\");\n yield* storage.attempts.create(attemptRow(owner, later));\n const latest = yield* storage.attempts.latest(attachment.id, \"provisioning\");\n yield* expect(\n Option.isSome(latest) && latest.value.id === later.id,\n \"latest did not return the newest attempt\",\n );\n const cleanup = yield* storage.attempts.latest(attachment.id, \"cleanup\");\n yield* expect(Option.isNone(cleanup), \"latest must filter by kind\");\n yield* expectReason(\n storage.attempts.get(plan.id).pipe(Effect.provideService(Principal.Service, other)),\n \"NotFound\",\n \"attempts.get across owners\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"rejects a planned attempt once and terminally\",\n run: run(\n Effect.gen(function* () {\n const { storage, attachment } = yield* connect(owner, \"auth-reject\");\n const now = yield* DateTime.now;\n const plan = planRow(attachment.id, now, \"r1\");\n yield* storage.attempts.create(attemptRow(owner, plan));\n yield* expectReason(\n storage.attempts.reject(plan.id, {\n digest: Plan.Digest.make(\"other\"),\n actorId: \"a\",\n reason: null,\n }),\n \"Stale\",\n \"reject with a digest mismatch\",\n );\n yield* expectReason(\n storage.attempts\n .reject(plan.id, { digest: plan.digest, actorId: other.actorId, reason: null })\n .pipe(Effect.provideService(Principal.Service, other)),\n \"NotFound\",\n \"reject by another owner\",\n );\n const rejected = yield* storage.attempts.reject(plan.id, {\n digest: plan.digest,\n actorId: owner.actorId,\n reason: \"wrong zone\",\n });\n yield* expect(\n rejected.status === \"rejected\" &&\n rejected.rejection?.actorId === owner.actorId &&\n rejected.rejection.reason === \"wrong zone\" &&\n DateTime.toEpochMillis(rejected.rejection.at) >= DateTime.toEpochMillis(now),\n \"reject did not record the actor, reason, and time\",\n );\n const again = yield* storage.attempts.reject(plan.id, {\n digest: plan.digest,\n actorId: \"someone-else\",\n reason: \"later\",\n });\n yield* expect(\n again.rejection?.reason === \"wrong zone\",\n \"rejecting a rejected attempt must return it unchanged\",\n );\n yield* expectReason(\n storage.attempts.approve(plan.id, approvalRow(plan, \"r1\")),\n \"Stale\",\n \"approve after reject\",\n );\n yield* expectReason(\n storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })),\n \"Stale\",\n \"claim after reject\",\n );\n const approvedPlan = planRow(attachment.id, now, \"r2\");\n yield* storage.attempts.create(attemptRow(owner, approvedPlan));\n yield* storage.attempts.approve(approvedPlan.id, approvalRow(approvedPlan, \"r2\"));\n yield* expectReason(\n storage.attempts.reject(approvedPlan.id, {\n digest: approvedPlan.digest,\n actorId: \"a\",\n reason: null,\n }),\n \"Stale\",\n \"reject after approve\",\n );\n const expiredPlan = planRow(attachment.id, now, \"r3\");\n yield* storage.attempts.create(\n new Storage.Attempt({ ...attemptRow(owner, expiredPlan), status: \"expired\" }),\n );\n yield* expectReason(\n storage.attempts.reject(expiredPlan.id, {\n digest: expiredPlan.digest,\n actorId: \"a\",\n reason: null,\n }),\n \"Expired\",\n \"reject an expired plan\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"keeps a failed revocation pending and finishes it on recovery\",\n run: run(\n Effect.gen(function* () {\n const { storage, connection, attachment } = yield* connect(owner, \"auth-revoke\");\n yield* storage.attachments.remove(attachment.id);\n yield* storage.connections.remove(connection.id);\n const revokeFailure = yield* storage.authorizations\n .revoke(\"auth-revoke\", Effect.fail(new Failure(\"provider unavailable\")))\n .pipe(Effect.flip);\n yield* expect(\n revokeFailure instanceof Failure,\n \"revoke must surface the provider failure\",\n );\n const pending = yield* storage.authorizations.get(\"auth-revoke\");\n yield* expect(\n pending.revocation === \"pending\",\n \"a failed revoke must leave the row pending\",\n );\n const now = yield* DateTime.now;\n yield* expectReason(\n storage.authorizations.upsert({\n authorization: authorizationRow(owner, \"auth-revoke\", now),\n credential: credentialRow(now),\n expectedId: \"auth-revoke\",\n }),\n \"Busy\",\n \"reconnect while revocation is pending\",\n );\n const recoveredByOther = yield* storage.authorizations\n .recoverRevocations(() => Effect.void)\n .pipe(Effect.provideService(Principal.Service, other));\n yield* expect(recoveredByOther === 0, \"recovery must not cross owners\");\n let calls = 0;\n const recovered = yield* storage.authorizations.recoverRevocations(() =>\n Effect.sync(() => void (calls += 1)),\n );\n yield* expect(\n recovered === 1 && calls === 1,\n \"recovery must revoke exactly the pending authorization\",\n );\n yield* expectReason(\n storage.authorizations.get(\"auth-revoke\"),\n \"NotFound\",\n \"authorization after recovery\",\n );\n const again = yield* storage.authorizations.recoverRevocations(() => Effect.void);\n yield* expect(again === 0, \"recovery must be idempotent\");\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"returns an attachment's label from every read\",\n run: run(\n Effect.gen(function* () {\n const { storage, connection, attachment } = yield* connect(owner, \"auth-label\");\n const label = `${attachment.zone} (auth-label)`;\n yield* expect(attachment.label === label, \"create must return the label it was given\");\n const read = yield* storage.attachments.get(attachment.id);\n yield* expect(read.label === label, \"get must return the stored label\");\n const byDomain = yield* storage.attachments.byDomain(attachment.domain);\n yield* expect(\n Option.isSome(byDomain) && byDomain.value.label === label,\n \"byDomain must return the stored label\",\n );\n const listed = yield* storage.attachments.list(connection.id);\n yield* expect(\n listed.every((row) => row.label === label),\n \"list must return the stored label\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"blocks connection removal while attachments exist\",\n run: run(\n Effect.gen(function* () {\n const { storage, connection, attachment } = yield* connect(owner, \"auth-remove\");\n yield* expectReason(\n storage.connections.remove(connection.id),\n \"InvalidInput\",\n \"remove with attachments\",\n );\n yield* expectReason(\n storage.attachments.create({\n connectionId: connection.id,\n domain: attachment.domain,\n zone: attachment.zone,\n label: attachment.label,\n target: {},\n }),\n \"InvalidInput\",\n \"duplicate domain attachment\",\n );\n yield* storage.attachments.remove(attachment.id);\n yield* storage.connections.remove(connection.id);\n yield* expectReason(\n storage.connections.get(connection.id),\n \"NotFound\",\n \"connection after remove\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"fails Busy while a lock is held and releases it afterwards\",\n run: run(\n Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const gate = yield* Deferred.make<void>();\n const holder = yield* Effect.forkChild(\n storage.withLock(\"refresh:auth-1\", Deferred.await(gate)),\n );\n yield* Effect.yieldNow;\n yield* expectReason(\n storage.withLock(\"refresh:auth-1\", Effect.void),\n \"Busy\",\n \"second holder\",\n );\n const otherOwner = yield* storage\n .withLock(\"refresh:auth-1\", Effect.succeed(\"ok\"))\n .pipe(Effect.provideService(Principal.Service, other));\n yield* expect(otherOwner === \"ok\", \"locks must be scoped by owner\");\n yield* Deferred.succeed(gate, undefined);\n yield* Fiber.join(holder);\n const after = yield* storage.withLock(\"refresh:auth-1\", Effect.succeed(\"free\"));\n yield* expect(after === \"free\", \"lock was not released\");\n yield* storage\n .withLock(\"refresh:auth-1\", Effect.fail(new Failure(\"inside\")))\n .pipe(Effect.ignore);\n const afterFailure = yield* storage.withLock(\"refresh:auth-1\", Effect.succeed(\"free\"));\n yield* expect(afterFailure === \"free\", \"lock was not released after a failure\");\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"stores readiness per domain, with or without an attachment\",\n run: run(\n Effect.gen(function* () {\n const { storage, attachment } = yield* connect(owner, \"auth-ready\");\n const now = yield* DateTime.now;\n const readiness = new Storage.Readiness({\n domain: attachment.domain,\n attachmentId: attachment.id,\n ownerId: owner.ownerId,\n overall: \"pending\",\n requirements: [\n {\n operationId: null,\n record: DnsRecord.txt({ name: \"app.example.com\", value: \"v\" }),\n status: \"missing\",\n evidence: [],\n },\n ],\n host: [],\n pendingSince: now,\n checkedAt: now,\n nextCheckAt: DateTime.add(now, { seconds: 15 }),\n });\n yield* storage.readiness.put(readiness);\n const stored = yield* storage.readiness.get(attachment.domain);\n yield* expect(\n Option.isSome(stored) && stored.value.overall === \"pending\",\n \"readiness was not stored\",\n );\n const foreign = yield* storage.readiness\n .get(attachment.domain)\n .pipe(Effect.provideService(Principal.Service, other));\n yield* expect(Option.isNone(foreign), \"readiness leaked across owners\");\n yield* storage.readiness.put(\n new Storage.Readiness({\n ...readiness,\n domain: \"observe-only.example.com\",\n attachmentId: null,\n }),\n );\n const observeOnly = yield* storage.readiness.get(\"observe-only.example.com\");\n yield* expect(\n Option.isSome(observeOnly) && observeOnly.value.attachmentId === null,\n \"readiness without an attachment was not stored\",\n );\n yield* expectReason(\n storage.readiness.put(\n new Storage.Readiness({ ...readiness, attachmentId: \"att-missing\" }),\n ),\n \"NotFound\",\n \"readiness with an unknown attachment\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n ];\n};\n\n/** Registers every case with `options.it` when given; always returns the cases. */\nexport const storage = (\n layer: Layer.Layer<Storage.Service, unknown>,\n options: Options = {},\n): ReadonlyArray<Case> => {\n const all = cases(layer);\n if (options.it !== undefined) {\n for (const item of all) options.it(item.name, () => Effect.runPromise(item.run));\n }\n return all;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"storage.mjs","names":["Principal.make","Errors.isDomainKitError","Storage.Authorization","Storage.Credential","Storage.Service","Principal.Service","Plan.Model","Plan.Create","DnsRecord.txt","Storage.Attempt","Approval.Model","Receipt.Model","Receipt.Applied","Storage.Continuation","Storage.Readiness"],"sources":["../../../src/internal/conformance/storage.ts"],"sourcesContent":["import { DateTime, Deferred, Effect, Fiber, Layer, Option } from \"effect\";\n\nimport * as Approval from \"../../Approval.ts\";\nimport * as DnsRecord from \"../../DnsRecord.ts\";\nimport * as Errors from \"../error.ts\";\nimport * as Reason from \"../../Reason.ts\";\nimport * as Plan from \"../../Plan.ts\";\nimport * as Principal from \"../../Principal.ts\";\nimport * as Receipt from \"../../Receipt.ts\";\nimport * as Storage from \"../../Storage.ts\";\n\nexport interface Case {\n readonly name: string;\n readonly run: Effect.Effect<void, unknown>;\n}\n\nexport interface Options {\n /** Register each case with a test runner, e.g. vitest's `it`. */\n readonly it?: (name: string, run: () => Promise<void>) => void;\n}\n\nconst owner = Principal.make({ ownerId: \"conformance-owner-a\", actorId: \"actor-a\" });\nconst other = Principal.make({ ownerId: \"conformance-owner-b\", actorId: \"actor-b\" });\n\nclass Failure extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"StorageConformanceFailure\";\n }\n}\n\nconst expect = (condition: boolean, message: string) =>\n condition ? Effect.void : Effect.fail(new Failure(message));\n\nconst expectReason = <A, R>(\n effect: Effect.Effect<A, unknown, R>,\n tag: Reason.Model[\"_tag\"],\n label: string,\n): Effect.Effect<void, Failure, R> =>\n effect.pipe(\n Effect.flip,\n Effect.mapError(() => new Failure(`${label}: expected failure ${tag}, effect succeeded`)),\n Effect.flatMap((cause) =>\n expect(\n Errors.isDomainKitError(cause) && cause.reason._tag === tag,\n `${label}: expected reason ${tag}, received ${Errors.isDomainKitError(cause) ? cause.reason._tag : String(cause)}`,\n ),\n ),\n );\n\nconst authorizationRow = (principal: Principal.Interface, id: string, now: DateTime.Utc) =>\n new Storage.Authorization({\n id,\n ownerId: principal.ownerId,\n provider: \"fake\",\n method: \"token\",\n capabilities: [\"dns:read\"],\n context: { account: \"acc\" },\n label: \"Acme\",\n revocation: \"active\",\n createdBy: principal.actorId,\n createdAt: now,\n });\n\nconst credentialRow = (now: DateTime.Utc, ciphertext = \"sealed-1\") =>\n new Storage.Credential({ ciphertext, expiresAt: null, rotatedAt: now });\n\nconst connect = (principal: Principal.Interface, id: string) =>\n Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const now = yield* DateTime.now;\n yield* storage.authorizations.upsert({\n authorization: authorizationRow(principal, id, now),\n credential: credentialRow(now),\n });\n const connection = yield* storage.connections.create(id);\n const attachment = yield* storage.attachments.create({\n connectionId: connection.id,\n domain: `app.${id}.example.com`,\n zone: `${id}.example.com`,\n label: `${id}.example.com (${id})`,\n target: { zoneId: `zone-${id}` },\n });\n return { storage, connection, attachment };\n }).pipe(Effect.provideService(Principal.Service, principal));\n\nconst planRow = (attachmentId: string, now: DateTime.Utc, suffix: string) =>\n new Plan.Model({\n id: Plan.PlanId.make(`plan-${suffix}`),\n version: \"domainkit.plan.v2\",\n kind: \"provisioning\",\n digest: Plan.Digest.make(`digest-${suffix}`),\n domain: \"app.example.com\",\n zone: \"example.com\",\n provider: \"fake\",\n attachmentId,\n operations: [\n new Plan.Create({\n id: Plan.OperationId.make(`op-${suffix}`),\n record: DnsRecord.txt({ name: \"app.example.com\", value: suffix }),\n }),\n ],\n createdAt: now,\n expiresAt: DateTime.add(now, { hours: 1 }),\n });\n\nconst attemptRow = (principal: Principal.Interface, plan: Plan.Model) =>\n new Storage.Attempt({\n id: plan.id,\n ownerId: principal.ownerId,\n attachmentId: plan.attachmentId,\n kind: plan.kind,\n status: \"planned\",\n plan,\n approval: null,\n receipt: null,\n rejection: null,\n sourceReceiptId: null,\n leaseExpiresAt: null,\n failure: null,\n updatedAt: plan.createdAt,\n });\n\nconst approvalRow = (plan: Plan.Model, suffix: string) =>\n new Approval.Model({\n id: Approval.ApprovalId.make(`approval-${suffix}`),\n version: \"domainkit.approval.v2\",\n kind: plan.kind,\n planId: plan.id,\n digest: plan.digest,\n operationIds: plan.operations.map(({ id }) => id),\n approvedBy: \"actor\",\n approvedAt: plan.createdAt,\n expiresAt: plan.expiresAt,\n });\n\nconst receiptRow = (plan: Plan.Model, approval: Approval.Model, suffix: string) =>\n new Receipt.Model({\n id: Receipt.ReceiptId.make(`receipt-${suffix}`),\n version: \"domainkit.receipt.v2\",\n kind: plan.kind,\n planId: plan.id,\n approvalId: approval.id,\n digest: plan.digest,\n provider: plan.provider,\n zone: plan.zone,\n status: \"complete\",\n outcomes: plan.operations.map(\n (operation) => new Receipt.Applied({ operationId: operation.id, providerRecordId: \"r1\" }),\n ),\n appliedAt: plan.createdAt,\n });\n\n/** Every invariant a `Storage` implementation must hold. Each case runs against a fresh layer. */\nexport const cases = (layer: Layer.Layer<Storage.Service, unknown>): ReadonlyArray<Case> => {\n const run = <E>(effect: Effect.Effect<void, E, Storage.Service>): Effect.Effect<void, unknown> =>\n effect.pipe(Effect.provide(layer));\n return [\n {\n name: \"scopes every row by the principal's owner\",\n run: run(\n Effect.gen(function* () {\n const { storage, connection, attachment } = yield* connect(owner, \"auth-a\");\n const asOther = <A, E>(effect: Effect.Effect<A, E, Principal.Service>) =>\n effect.pipe(Effect.provideService(Principal.Service, other));\n yield* expectReason(\n asOther(storage.authorizations.get(\"auth-a\")),\n \"NotFound\",\n \"authorizations.get\",\n );\n yield* expectReason(\n asOther(storage.authorizations.credential(\"auth-a\")),\n \"NotFound\",\n \"authorizations.credential\",\n );\n yield* expectReason(\n asOther(storage.connections.get(connection.id)),\n \"NotFound\",\n \"connections.get\",\n );\n yield* expectReason(\n asOther(storage.attachments.get(attachment.id)),\n \"NotFound\",\n \"attachments.get\",\n );\n yield* expectReason(\n asOther(storage.attachments.list(connection.id)),\n \"NotFound\",\n \"attachments.list\",\n );\n const connections = yield* asOther(storage.connections.list());\n yield* expect(connections.length === 0, \"connections.list leaked across owners\");\n const byDomain = yield* asOther(storage.attachments.byDomain(attachment.domain));\n yield* expect(Option.isNone(byDomain), \"attachments.byDomain leaked across owners\");\n const now = yield* DateTime.now;\n yield* expectReason(\n asOther(\n storage.authorizations.upsert({\n authorization: authorizationRow(owner, \"auth-b\", now),\n credential: credentialRow(now),\n }),\n ),\n \"InvalidInput\",\n \"authorizations.upsert with a foreign ownerId\",\n );\n }),\n ),\n },\n {\n name: \"stores, rotates, and promotes credentials without exposing plaintext\",\n run: run(\n Effect.gen(function* () {\n const { storage } = yield* connect(owner, \"auth-cred\");\n const first = yield* storage.authorizations.credential(\"auth-cred\");\n yield* expect(first.ciphertext === \"sealed-1\", \"credential ciphertext was not stored\");\n const now = yield* DateTime.now;\n yield* storage.authorizations.rotate(\"auth-cred\", credentialRow(now, \"sealed-2\"));\n const rotated = yield* storage.authorizations.credential(\"auth-cred\");\n yield* expect(rotated.ciphertext === \"sealed-2\", \"rotate did not replace the credential\");\n yield* storage.authorizations.promoteCapabilities(\"auth-cred\", [\"dns:write\", \"dns:read\"]);\n const promoted = yield* storage.authorizations.get(\"auth-cred\");\n yield* expect(\n [...promoted.capabilities].sort().join(\",\") === \"dns:read,dns:write\",\n \"promoteCapabilities did not merge capabilities\",\n );\n // The account label names the connection without a provider call, so it has to survive\n // the round trip as written.\n yield* expect(promoted.label === \"Acme\", \"authorization label was not stored\");\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"replaces an authorization only through the expected id\",\n run: run(\n Effect.gen(function* () {\n const { storage } = yield* connect(owner, \"auth-cas\");\n const now = yield* DateTime.now;\n const replaced = yield* storage.authorizations.upsert({\n authorization: new Storage.Authorization({\n ...authorizationRow(owner, \"auth-cas\", now),\n method: \"oauth\",\n }),\n credential: credentialRow(now, \"sealed-oauth\"),\n expectedId: \"auth-cas\",\n });\n yield* expect(\n replaced.method === \"oauth\",\n \"upsert with expectedId did not replace the row\",\n );\n const credential = yield* storage.authorizations.credential(\"auth-cas\");\n yield* expect(\n credential.ciphertext === \"sealed-oauth\",\n \"upsert with expectedId did not replace the credential\",\n );\n yield* expectReason(\n storage.authorizations.upsert({\n authorization: authorizationRow(owner, \"auth-missing\", now),\n credential: credentialRow(now),\n expectedId: \"auth-missing\",\n }),\n \"NotFound\",\n \"upsert with an unknown expectedId\",\n );\n yield* expectReason(\n storage.authorizations.upsert({\n authorization: authorizationRow(owner, \"auth-cas\", now),\n credential: credentialRow(now),\n }),\n \"InvalidInput\",\n \"insert of an existing id\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"consumes continuations exactly once and rejects expired ones\",\n run: run(\n Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const now = yield* DateTime.now;\n const continuation = new Storage.Continuation({\n id: \"cont-1\",\n ownerId: owner.ownerId,\n actorId: owner.actorId,\n provider: \"fake\",\n payload: { codeVerifier: \"v\" },\n returnTo: null,\n expiresAt: DateTime.add(now, { minutes: 15 }),\n });\n yield* storage.continuations.put(continuation);\n yield* expectReason(\n storage.continuations\n .consume(\"cont-1\")\n .pipe(Effect.provideService(Principal.Service, other)),\n \"NotFound\",\n \"consume by another owner\",\n );\n const peeked = yield* storage.continuations.get(\"cont-1\");\n yield* expect(peeked.id === \"cont-1\", \"get did not return the continuation\");\n const consumed = yield* storage.continuations.consume(\"cont-1\");\n yield* expect(consumed.id === \"cont-1\", \"consume did not return the continuation\");\n yield* expectReason(storage.continuations.get(\"cont-1\"), \"NotFound\", \"get after consume\");\n yield* expectReason(\n storage.continuations.consume(\"cont-1\"),\n \"NotFound\",\n \"second consume\",\n );\n yield* storage.continuations.put(\n new Storage.Continuation({\n ...continuation,\n id: \"cont-2\",\n expiresAt: DateTime.subtract(now, { minutes: 1 }),\n }),\n );\n yield* expectReason(storage.continuations.get(\"cont-2\"), \"Expired\", \"expired get\");\n yield* expectReason(\n storage.continuations.consume(\"cont-2\"),\n \"Expired\",\n \"expired consume\",\n );\n yield* expectReason(\n storage.continuations.consume(\"cont-2\"),\n \"NotFound\",\n \"expired consume is also spent\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"reads a continuation header under any principal and still refuses an expired one\",\n run: run(\n Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const now = yield* DateTime.now;\n const continuation = new Storage.Continuation({\n id: \"cont-header-1\",\n ownerId: owner.ownerId,\n actorId: owner.actorId,\n provider: \"fake\",\n payload: { codeVerifier: \"v\" },\n returnTo: null,\n expiresAt: DateTime.add(now, { minutes: 15 }),\n });\n yield* storage.continuations.put(continuation);\n // The deliberate exception to the tenant isolation every other case here enforces, and\n // not a bug. A provider callback is a top-level navigation carrying a continuation id\n // and a session cookie, so the route has to learn whose flow it is before it can name a\n // principal to scope by. The read therefore takes no principal at all — that is a type\n // constraint, and providing a foreign one below changes nothing — and it answers with\n // header fields only. `payload` holds the PKCE verifier and stays behind the\n // owner-scoped `get`. Binding the caller to the flow is the reader's job, and\n // `domainkit/server` does it on the callback route.\n const foreign = yield* storage.continuations\n .header(\"cont-header-1\")\n .pipe(Effect.provideService(Principal.Service, other));\n yield* expect(\n foreign.ownerId === owner.ownerId && foreign.actorId === owner.actorId,\n \"header did not report the recorded owner and actor\",\n );\n yield* expect(foreign.provider === \"fake\", \"header did not report the provider\");\n yield* expect(!(\"payload\" in foreign), \"header exposed the continuation payload\");\n // A read, never a claim: the flow is still there to be spent.\n const still = yield* storage.continuations.get(\"cont-header-1\");\n yield* expect(still.id === \"cont-header-1\", \"the header read spent the continuation\");\n yield* expectReason(\n storage.continuations.header(\"cont-header-missing\"),\n \"NotFound\",\n \"header of an unknown continuation\",\n );\n yield* storage.continuations.put(\n new Storage.Continuation({\n ...continuation,\n id: \"cont-header-2\",\n expiresAt: DateTime.subtract(now, { minutes: 1 }),\n }),\n );\n yield* expectReason(\n storage.continuations.header(\"cont-header-2\"),\n \"Expired\",\n \"header of an expired continuation\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"moves attempts through planned, approved, applying, and complete with leases\",\n run: run(\n Effect.gen(function* () {\n const { storage, attachment } = yield* connect(owner, \"auth-attempt\");\n const now = yield* DateTime.now;\n const plan = planRow(attachment.id, now, \"1\");\n yield* storage.attempts.create(attemptRow(owner, plan));\n yield* expectReason(\n storage.attempts.create(attemptRow(owner, plan)),\n \"InvalidInput\",\n \"duplicate attempt\",\n );\n yield* expectReason(\n storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })),\n \"Stale\",\n \"claim before approval\",\n );\n const approval = approvalRow(plan, \"1\");\n const approved = yield* storage.attempts.approve(plan.id, approval);\n yield* expect(approved.status === \"approved\", \"approve did not move the status\");\n const again = yield* storage.attempts.approve(plan.id, approval);\n yield* expect(\n again.approval?.id === approval.id,\n \"re-approving with the same approval must be idempotent\",\n );\n yield* expectReason(\n storage.attempts.approve(plan.id, approvalRow(plan, \"other\")),\n \"Stale\",\n \"second approval\",\n );\n const byApproval = yield* storage.attempts.byApproval(approval.id);\n yield* expect(byApproval.id === plan.id, \"byApproval did not find the attempt\");\n const claimed = yield* storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 }));\n yield* expect(\n claimed.status === \"applying\" && claimed.leaseExpiresAt !== null,\n \"claim did not lease\",\n );\n yield* expectReason(\n storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })),\n \"Busy\",\n \"claim while leased\",\n );\n yield* storage.attempts.fail(plan.id, \"provider down\");\n const failed = yield* storage.attempts.get(plan.id);\n yield* expect(\n failed.status === \"failed\" && failed.failure === \"provider down\",\n \"fail did not record the message\",\n );\n const reclaimed = yield* storage.attempts.claim(\n plan.id,\n DateTime.subtract(now, { minutes: 1 }),\n );\n yield* expect(reclaimed.failure === null, \"claim did not clear the failure\");\n const expiredLease = yield* storage.attempts.claim(\n plan.id,\n DateTime.add(now, { minutes: 2 }),\n );\n yield* expect(expiredLease.status === \"applying\", \"an expired lease must be reclaimable\");\n const receipt = receiptRow(plan, approval, \"1\");\n const completed = yield* storage.attempts.complete(plan.id, receipt);\n yield* expect(\n completed.status === \"complete\" && completed.leaseExpiresAt === null,\n \"complete did not clear the lease\",\n );\n yield* expectReason(\n storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })),\n \"Stale\",\n \"claim after completion\",\n );\n const byReceipt = yield* storage.attempts.byReceipt(receipt.id);\n yield* expect(byReceipt.receipt?.id === receipt.id, \"byReceipt did not find the attempt\");\n const later = planRow(attachment.id, DateTime.add(now, { seconds: 1 }), \"2\");\n yield* storage.attempts.create(attemptRow(owner, later));\n const latest = yield* storage.attempts.latest(attachment.id, \"provisioning\");\n yield* expect(\n Option.isSome(latest) && latest.value.id === later.id,\n \"latest did not return the newest attempt\",\n );\n const cleanup = yield* storage.attempts.latest(attachment.id, \"cleanup\");\n yield* expect(Option.isNone(cleanup), \"latest must filter by kind\");\n yield* expectReason(\n storage.attempts.get(plan.id).pipe(Effect.provideService(Principal.Service, other)),\n \"NotFound\",\n \"attempts.get across owners\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"rejects a planned attempt once and terminally\",\n run: run(\n Effect.gen(function* () {\n const { storage, attachment } = yield* connect(owner, \"auth-reject\");\n const now = yield* DateTime.now;\n const plan = planRow(attachment.id, now, \"r1\");\n yield* storage.attempts.create(attemptRow(owner, plan));\n yield* expectReason(\n storage.attempts.reject(plan.id, {\n digest: Plan.Digest.make(\"other\"),\n actorId: \"a\",\n reason: null,\n }),\n \"Stale\",\n \"reject with a digest mismatch\",\n );\n yield* expectReason(\n storage.attempts\n .reject(plan.id, { digest: plan.digest, actorId: other.actorId, reason: null })\n .pipe(Effect.provideService(Principal.Service, other)),\n \"NotFound\",\n \"reject by another owner\",\n );\n const rejected = yield* storage.attempts.reject(plan.id, {\n digest: plan.digest,\n actorId: owner.actorId,\n reason: \"wrong zone\",\n });\n yield* expect(\n rejected.status === \"rejected\" &&\n rejected.rejection?.actorId === owner.actorId &&\n rejected.rejection.reason === \"wrong zone\" &&\n DateTime.toEpochMillis(rejected.rejection.at) >= DateTime.toEpochMillis(now),\n \"reject did not record the actor, reason, and time\",\n );\n const again = yield* storage.attempts.reject(plan.id, {\n digest: plan.digest,\n actorId: \"someone-else\",\n reason: \"later\",\n });\n yield* expect(\n again.rejection?.reason === \"wrong zone\",\n \"rejecting a rejected attempt must return it unchanged\",\n );\n yield* expectReason(\n storage.attempts.approve(plan.id, approvalRow(plan, \"r1\")),\n \"Stale\",\n \"approve after reject\",\n );\n yield* expectReason(\n storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })),\n \"Stale\",\n \"claim after reject\",\n );\n const approvedPlan = planRow(attachment.id, now, \"r2\");\n yield* storage.attempts.create(attemptRow(owner, approvedPlan));\n yield* storage.attempts.approve(approvedPlan.id, approvalRow(approvedPlan, \"r2\"));\n yield* expectReason(\n storage.attempts.reject(approvedPlan.id, {\n digest: approvedPlan.digest,\n actorId: \"a\",\n reason: null,\n }),\n \"Stale\",\n \"reject after approve\",\n );\n const expiredPlan = planRow(attachment.id, now, \"r3\");\n yield* storage.attempts.create(\n new Storage.Attempt({ ...attemptRow(owner, expiredPlan), status: \"expired\" }),\n );\n yield* expectReason(\n storage.attempts.reject(expiredPlan.id, {\n digest: expiredPlan.digest,\n actorId: \"a\",\n reason: null,\n }),\n \"Expired\",\n \"reject an expired plan\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"keeps a failed revocation pending and finishes it on recovery\",\n run: run(\n Effect.gen(function* () {\n const { storage, connection, attachment } = yield* connect(owner, \"auth-revoke\");\n yield* storage.attachments.remove(attachment.id);\n yield* storage.connections.remove(connection.id);\n const revokeFailure = yield* storage.authorizations\n .revoke(\"auth-revoke\", Effect.fail(new Failure(\"provider unavailable\")))\n .pipe(Effect.flip);\n yield* expect(\n revokeFailure instanceof Failure,\n \"revoke must surface the provider failure\",\n );\n const pending = yield* storage.authorizations.get(\"auth-revoke\");\n yield* expect(\n pending.revocation === \"pending\",\n \"a failed revoke must leave the row pending\",\n );\n const now = yield* DateTime.now;\n yield* expectReason(\n storage.authorizations.upsert({\n authorization: authorizationRow(owner, \"auth-revoke\", now),\n credential: credentialRow(now),\n expectedId: \"auth-revoke\",\n }),\n \"Busy\",\n \"reconnect while revocation is pending\",\n );\n const recoveredByOther = yield* storage.authorizations\n .recoverRevocations(() => Effect.void)\n .pipe(Effect.provideService(Principal.Service, other));\n yield* expect(recoveredByOther === 0, \"recovery must not cross owners\");\n let calls = 0;\n const recovered = yield* storage.authorizations.recoverRevocations(() =>\n Effect.sync(() => void (calls += 1)),\n );\n yield* expect(\n recovered === 1 && calls === 1,\n \"recovery must revoke exactly the pending authorization\",\n );\n yield* expectReason(\n storage.authorizations.get(\"auth-revoke\"),\n \"NotFound\",\n \"authorization after recovery\",\n );\n const again = yield* storage.authorizations.recoverRevocations(() => Effect.void);\n yield* expect(again === 0, \"recovery must be idempotent\");\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"returns an attachment's label from every read\",\n run: run(\n Effect.gen(function* () {\n const { storage, connection, attachment } = yield* connect(owner, \"auth-label\");\n const label = `${attachment.zone} (auth-label)`;\n yield* expect(attachment.label === label, \"create must return the label it was given\");\n const read = yield* storage.attachments.get(attachment.id);\n yield* expect(read.label === label, \"get must return the stored label\");\n const byDomain = yield* storage.attachments.byDomain(attachment.domain);\n yield* expect(\n Option.isSome(byDomain) && byDomain.value.label === label,\n \"byDomain must return the stored label\",\n );\n const listed = yield* storage.attachments.list(connection.id);\n yield* expect(\n listed.every((row) => row.label === label),\n \"list must return the stored label\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"blocks connection removal while attachments exist\",\n run: run(\n Effect.gen(function* () {\n const { storage, connection, attachment } = yield* connect(owner, \"auth-remove\");\n yield* expectReason(\n storage.connections.remove(connection.id),\n \"InvalidInput\",\n \"remove with attachments\",\n );\n yield* expectReason(\n storage.attachments.create({\n connectionId: connection.id,\n domain: attachment.domain,\n zone: attachment.zone,\n label: attachment.label,\n target: {},\n }),\n \"InvalidInput\",\n \"duplicate domain attachment\",\n );\n yield* storage.attachments.remove(attachment.id);\n yield* storage.connections.remove(connection.id);\n yield* expectReason(\n storage.connections.get(connection.id),\n \"NotFound\",\n \"connection after remove\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"fails Busy while a lock is held and releases it afterwards\",\n run: run(\n Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const gate = yield* Deferred.make<void>();\n const holder = yield* Effect.forkChild(\n storage.withLock(\"refresh:auth-1\", Deferred.await(gate)),\n );\n yield* Effect.yieldNow;\n yield* expectReason(\n storage.withLock(\"refresh:auth-1\", Effect.void),\n \"Busy\",\n \"second holder\",\n );\n const otherOwner = yield* storage\n .withLock(\"refresh:auth-1\", Effect.succeed(\"ok\"))\n .pipe(Effect.provideService(Principal.Service, other));\n yield* expect(otherOwner === \"ok\", \"locks must be scoped by owner\");\n yield* Deferred.succeed(gate, undefined);\n yield* Fiber.join(holder);\n const after = yield* storage.withLock(\"refresh:auth-1\", Effect.succeed(\"free\"));\n yield* expect(after === \"free\", \"lock was not released\");\n yield* storage\n .withLock(\"refresh:auth-1\", Effect.fail(new Failure(\"inside\")))\n .pipe(Effect.ignore);\n const afterFailure = yield* storage.withLock(\"refresh:auth-1\", Effect.succeed(\"free\"));\n yield* expect(afterFailure === \"free\", \"lock was not released after a failure\");\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"creates a batch once per idempotency key and keeps it inside its owner\",\n run: run(\n Effect.gen(function* () {\n const first = yield* connect(owner, \"auth-batch-key\");\n const second = yield* connect(owner, \"auth-batch-key-2\");\n const storage = first.storage;\n const attachmentIds = [first.attachment.id, second.attachment.id];\n const created = yield* storage.batches.create({\n kind: \"provisioning\",\n idempotencyKey: \"batch-key-create\",\n attachmentIds,\n });\n yield* expect(created.batch.status === \"planning\", \"a new batch is not planning\");\n yield* expect(\n created.items.map((item) => item.position).join(\",\") === \"0,1\",\n \"batch items did not keep the order they were created in\",\n );\n yield* expect(\n created.items.every((item) => item.attemptId === null && item.planFailure === null),\n \"a new batch item already points at an attempt\",\n );\n // A retried create is the same batch, whatever the second call asks for.\n const replay = yield* storage.batches.create({\n kind: \"provisioning\",\n idempotencyKey: \"batch-key-create\",\n attachmentIds: [first.attachment.id],\n });\n yield* expect(\n replay.batch.id === created.batch.id && replay.items.length === 2,\n \"a replayed idempotency key did not return the stored batch\",\n );\n yield* expectReason(\n storage.batches.create({\n kind: \"provisioning\",\n idempotencyKey: \"batch-key-empty\",\n attachmentIds: [],\n }),\n \"InvalidInput\",\n \"batches.create with no attachments\",\n );\n yield* expectReason(\n storage.batches.create({\n kind: \"provisioning\",\n idempotencyKey: \"batch-key-duplicate\",\n attachmentIds: [first.attachment.id, first.attachment.id],\n }),\n \"InvalidInput\",\n \"batches.create with a repeated attachment\",\n );\n const found = yield* storage.batches.byIdempotencyKey(\"batch-key-create\");\n yield* expect(\n Option.isSome(found) && found.value.batch.id === created.batch.id,\n \"batches.byIdempotencyKey did not find the batch the key made\",\n );\n const unknown = yield* storage.batches.byIdempotencyKey(\"batch-key-never-used\");\n yield* expect(\n Option.isNone(unknown),\n \"batches.byIdempotencyKey invented a batch for an unused key\",\n );\n const asOther = <A, E>(effect: Effect.Effect<A, E, Principal.Service>) =>\n effect.pipe(Effect.provideService(Principal.Service, other));\n yield* expectReason(\n asOther(storage.batches.get(created.batch.id)),\n \"NotFound\",\n \"batches.get\",\n );\n const foreignKey = yield* asOther(storage.batches.byIdempotencyKey(\"batch-key-create\"));\n yield* expect(Option.isNone(foreignKey), \"batches.byIdempotencyKey leaked across owners\");\n const foreign = yield* asOther(storage.batches.listUnfinished());\n yield* expect(\n !foreign.some(({ batch }) => batch.id === created.batch.id),\n \"batches.listUnfinished leaked across owners\",\n );\n const unfinished = yield* storage.batches.listUnfinished();\n yield* expect(\n unfinished.some(({ batch }) => batch.id === created.batch.id),\n \"a planning batch is missing from the unfinished index\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"recomputes batch status from its items and approves every attempt at once\",\n run: run(\n Effect.gen(function* () {\n const first = yield* connect(owner, \"auth-batch-plan\");\n const second = yield* connect(owner, \"auth-batch-plan-2\");\n const storage = first.storage;\n const now = yield* DateTime.now;\n const planA = planRow(first.attachment.id, now, \"batch-a\");\n const planB = planRow(second.attachment.id, now, \"batch-b\");\n yield* storage.attempts.create(attemptRow(owner, planA));\n yield* storage.attempts.create(attemptRow(owner, planB));\n const { batch } = yield* storage.batches.create({\n kind: \"provisioning\",\n idempotencyKey: \"batch-key-plan\",\n attachmentIds: [first.attachment.id, second.attachment.id],\n });\n const approvalA = approvalRow(planA, \"batch-a\");\n const approvalB = approvalRow(planB, \"batch-b\");\n const approvals = [\n { attachmentId: first.attachment.id, approval: approvalA },\n { attachmentId: second.attachment.id, approval: approvalB },\n ];\n const digest = Plan.Digest.make(\"batch-digest-1\");\n yield* expectReason(\n storage.batches.approve(batch.id, {\n digest,\n actorId: owner.actorId,\n approvals,\n }),\n \"BatchStale\",\n \"batches.approve before every item is planned\",\n );\n const failed = yield* storage.batches.recordItemPlanFailure(\n batch.id,\n first.attachment.id,\n \"the provider was unavailable\",\n );\n yield* expect(\n failed.batch.status === \"planning\" &&\n failed.items[0]?.planFailure === \"the provider was unavailable\",\n \"a plan failure did not leave the batch planning\",\n );\n const partiallyPlanned = yield* storage.batches.recordItemPlan(\n batch.id,\n first.attachment.id,\n planA.id,\n );\n yield* expect(\n partiallyPlanned.batch.status === \"planning\" &&\n partiallyPlanned.items[0]?.planFailure === null,\n \"a landed plan did not clear the item's failure\",\n );\n const planned = yield* storage.batches.recordItemPlan(\n batch.id,\n second.attachment.id,\n planB.id,\n );\n yield* expect(\n planned.batch.status === \"planned\",\n \"a batch whose items are all planned is not planned\",\n );\n const approved = yield* storage.batches.approve(batch.id, {\n digest,\n actorId: owner.actorId,\n approvals,\n });\n yield* expect(\n approved.batch.status === \"approved\" && approved.batch.digest === digest,\n \"batches.approve did not bind the digest\",\n );\n const attemptA = yield* storage.attempts.get(planA.id);\n const attemptB = yield* storage.attempts.get(planB.id);\n yield* expect(\n attemptA.approval?.id === approvalA.id && attemptB.approval?.id === approvalB.id,\n \"batches.approve did not approve every item's attempt\",\n );\n const again = yield* storage.batches.approve(batch.id, {\n digest,\n actorId: owner.actorId,\n approvals,\n });\n yield* expect(\n again.batch.status === \"approved\",\n \"approving an approved batch under the same digest is not idempotent\",\n );\n yield* expectReason(\n storage.batches.approve(batch.id, {\n digest: Plan.Digest.make(\"batch-digest-moved\"),\n actorId: owner.actorId,\n approvals,\n }),\n \"BatchStale\",\n \"batches.approve with a digest the batch does not hold\",\n );\n yield* expectReason(\n storage.batches.reject(batch.id, { actorId: owner.actorId, reason: null }),\n \"BatchStale\",\n \"batches.reject after approval\",\n );\n const lease = DateTime.add(now, { minutes: 2 });\n yield* storage.attempts.claim(planA.id, lease);\n yield* storage.attempts.complete(planA.id, receiptRow(planA, approvalA, \"batch-a\"));\n const applying = yield* storage.batches.refresh(batch.id);\n yield* expect(\n applying.batch.status === \"applying\",\n \"a batch with one item left is not applying\",\n );\n yield* storage.attempts.claim(planB.id, lease);\n yield* storage.attempts.complete(planB.id, receiptRow(planB, approvalB, \"batch-b\"));\n const complete = yield* storage.batches.refresh(batch.id);\n yield* expect(\n complete.batch.status === \"complete\" && complete.batch.completedAt !== null,\n \"a batch whose items all completed is not complete\",\n );\n const unfinished = yield* storage.batches.listUnfinished();\n yield* expect(\n !unfinished.some((entry) => entry.batch.id === batch.id),\n \"a complete batch is still in the unfinished index\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"refuses a batch approval for an attempt that was declined on its own\",\n run: run(\n Effect.gen(function* () {\n const { storage, attachment } = yield* connect(owner, \"auth-batch-declined\");\n const now = yield* DateTime.now;\n const plan = planRow(attachment.id, now, \"batch-declined\");\n yield* storage.attempts.create(attemptRow(owner, plan));\n const { batch } = yield* storage.batches.create({\n kind: \"provisioning\",\n idempotencyKey: \"batch-key-declined\",\n attachmentIds: [attachment.id],\n });\n const planned = yield* storage.batches.recordItemPlan(batch.id, attachment.id, plan.id);\n yield* expect(planned.batch.status === \"planned\", \"the batch is not planned\");\n // The single-domain API declines the attempt underneath the batch.\n yield* storage.attempts.reject(plan.id, {\n digest: plan.digest,\n actorId: owner.actorId,\n reason: null,\n });\n yield* expectReason(\n storage.batches.approve(batch.id, {\n digest: Plan.Digest.make(\"batch-digest-declined\"),\n actorId: owner.actorId,\n approvals: [\n { attachmentId: attachment.id, approval: approvalRow(plan, \"batch-declined\") },\n ],\n }),\n \"Stale\",\n \"batches.approve over a declined attempt\",\n );\n const attempt = yield* storage.attempts.get(plan.id);\n yield* expect(\n attempt.status === \"rejected\" && attempt.approval === null,\n \"batches.approve reopened a declined attempt\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"declines a batch with its planned attempts and fences a late planner\",\n run: run(\n Effect.gen(function* () {\n const { storage, attachment } = yield* connect(owner, \"auth-batch-reject\");\n const now = yield* DateTime.now;\n const plan = planRow(attachment.id, now, \"batch-reject\");\n const late = planRow(attachment.id, now, \"batch-reject-late\");\n yield* storage.attempts.create(attemptRow(owner, plan));\n yield* storage.attempts.create(attemptRow(owner, late));\n const { batch } = yield* storage.batches.create({\n kind: \"provisioning\",\n idempotencyKey: \"batch-key-reject\",\n attachmentIds: [attachment.id],\n });\n yield* storage.batches.recordItemPlan(batch.id, attachment.id, plan.id);\n const rejected = yield* storage.batches.reject(batch.id, {\n actorId: owner.actorId,\n reason: \"the customer changed their mind\",\n });\n yield* expect(\n rejected.batch.status === \"rejected\" && rejected.batch.completedAt !== null,\n \"batches.reject is not terminal\",\n );\n const attempt = yield* storage.attempts.get(plan.id);\n yield* expect(\n attempt.status === \"rejected\",\n \"batches.reject left a planned attempt open\",\n );\n const twice = yield* storage.batches.reject(batch.id, {\n actorId: owner.actorId,\n reason: null,\n });\n yield* expect(\n twice.batch.rejection?.reason === \"the customer changed their mind\",\n \"a second rejection overwrote the first\",\n );\n // The fence: a planning pass still in flight must not land state on a declined batch.\n yield* expectReason(\n storage.batches.recordItemPlan(batch.id, attachment.id, late.id),\n \"BatchStale\",\n \"batches.recordItemPlan on a rejected batch\",\n );\n yield* expectReason(\n storage.batches.recordItemPlanFailure(batch.id, attachment.id, \"too late\"),\n \"BatchStale\",\n \"batches.recordItemPlanFailure on a rejected batch\",\n );\n const unfinished = yield* storage.batches.listUnfinished();\n yield* expect(\n !unfinished.some((entry) => entry.batch.id === batch.id),\n \"a rejected batch is still in the unfinished index\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"stores readiness per domain, with or without an attachment\",\n run: run(\n Effect.gen(function* () {\n const { storage, attachment } = yield* connect(owner, \"auth-ready\");\n const now = yield* DateTime.now;\n const readiness = new Storage.Readiness({\n domain: attachment.domain,\n attachmentId: attachment.id,\n ownerId: owner.ownerId,\n overall: \"pending\",\n requirements: [\n {\n operationId: null,\n record: DnsRecord.txt({ name: \"app.example.com\", value: \"v\" }),\n status: \"missing\",\n evidence: [],\n },\n ],\n host: [],\n pendingSince: now,\n checkedAt: now,\n nextCheckAt: DateTime.add(now, { seconds: 15 }),\n });\n yield* storage.readiness.put(readiness);\n const stored = yield* storage.readiness.get(attachment.domain);\n yield* expect(\n Option.isSome(stored) && stored.value.overall === \"pending\",\n \"readiness was not stored\",\n );\n const foreign = yield* storage.readiness\n .get(attachment.domain)\n .pipe(Effect.provideService(Principal.Service, other));\n yield* expect(Option.isNone(foreign), \"readiness leaked across owners\");\n yield* storage.readiness.put(\n new Storage.Readiness({\n ...readiness,\n domain: \"observe-only.example.com\",\n attachmentId: null,\n }),\n );\n const observeOnly = yield* storage.readiness.get(\"observe-only.example.com\");\n yield* expect(\n Option.isSome(observeOnly) && observeOnly.value.attachmentId === null,\n \"readiness without an attachment was not stored\",\n );\n yield* expectReason(\n storage.readiness.put(\n new Storage.Readiness({ ...readiness, attachmentId: \"att-missing\" }),\n ),\n \"NotFound\",\n \"readiness with an unknown attachment\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n ];\n};\n\n/** Registers every case with `options.it` when given; always returns the cases. */\nexport const storage = (\n layer: Layer.Layer<Storage.Service, unknown>,\n options: Options = {},\n): ReadonlyArray<Case> => {\n const all = cases(layer);\n if (options.it !== undefined) {\n for (const item of all) options.it(item.name, () => Effect.runPromise(item.run));\n }\n return all;\n};\n"],"mappings":"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"}
|
package/dist/internal/error.mjs
CHANGED
|
@@ -16,6 +16,7 @@ var DomainKitError = class extends Schema.TaggedError("@domainkit/DomainKitError
|
|
|
16
16
|
case "Reconnect": return "auth";
|
|
17
17
|
case "Conflict":
|
|
18
18
|
case "Stale":
|
|
19
|
+
case "BatchStale":
|
|
19
20
|
case "Expired":
|
|
20
21
|
case "Busy": return "plan";
|
|
21
22
|
case "ProviderRejected":
|
|
@@ -40,6 +41,7 @@ var DomainKitError = class extends Schema.TaggedError("@domainkit/DomainKitError
|
|
|
40
41
|
case "NotFound": return 404;
|
|
41
42
|
case "Conflict":
|
|
42
43
|
case "Stale":
|
|
44
|
+
case "BatchStale":
|
|
43
45
|
case "Expired":
|
|
44
46
|
case "Busy": return 409;
|
|
45
47
|
case "ProviderRejected": return 502;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.mjs","names":["Reason.Model","Reason.InvalidInput"],"sources":["../../src/internal/error.ts"],"sourcesContent":["/**\n * The one error every DomainKit operation fails with, published as `DomainKit.Error`. Hosts match\n * on `reason`; `category`, `isRetryable`, and `httpStatus` derive from it. Modelled on `SqlError`\n * and `HttpClientError` in Effect.\n */\nimport { Effect, Schema } from \"effect\";\n\nimport * as Reason from \"../Reason.ts\";\n\nexport type Category = \"request\" | \"auth\" | \"plan\" | \"provider\" | \"storage\" | \"internal\";\n\nexport class DomainKitError extends Schema.TaggedError<DomainKitError>(\"@domainkit/DomainKitError\")(\n \"DomainKitError\",\n { reason: Schema.suspend(() => Reason.Model) },\n) {\n get category(): Category {\n switch (this.reason._tag) {\n case \"InvalidInput\":\n case \"NotFound\":\n return \"request\";\n case \"Unauthenticated\":\n case \"Forbidden\":\n case \"Reconnect\":\n return \"auth\";\n case \"Conflict\":\n case \"Stale\":\n case \"Expired\":\n case \"Busy\":\n return \"plan\";\n case \"ProviderRejected\":\n case \"ProviderUnavailable\":\n case \"ProviderConflict\":\n case \"Unsupported\":\n return \"provider\";\n case \"StorageFailed\":\n return \"storage\";\n case \"CryptoFailed\":\n case \"ResolverFailed\":\n return \"internal\";\n }\n }\n\n get isRetryable(): boolean {\n return (\n this.reason._tag === \"Busy\" ||\n this.reason._tag === \"ProviderUnavailable\" ||\n this.reason._tag === \"StorageFailed\"\n );\n }\n\n /** The HTTP status `domainkit/server` answers with; hosts mapping their own routes use the same table. */\n get httpStatus(): number {\n switch (this.reason._tag) {\n case \"InvalidInput\":\n return 400;\n case \"Unauthenticated\":\n return 401;\n case \"Forbidden\":\n case \"Reconnect\":\n return 403;\n case \"NotFound\":\n return 404;\n case \"Conflict\":\n case \"Stale\":\n case \"Expired\":\n case \"Busy\":\n return 409;\n case \"ProviderRejected\":\n return 502;\n case \"ProviderConflict\":\n return 409;\n case \"Unsupported\":\n return 501;\n case \"ProviderUnavailable\":\n return 503;\n case \"StorageFailed\":\n case \"CryptoFailed\":\n case \"ResolverFailed\":\n return 500;\n }\n }\n\n override get message(): string {\n return this.reason.message;\n }\n}\n\nexport const isDomainKitError = (input: unknown): input is DomainKitError =>\n input instanceof DomainKitError;\n\n/** `Effect.fail(new DomainKitError({ reason }))` shorthand used across the package. */\nexport const fail = <R extends Reason.Model>(reason: R): Effect.Effect<never, DomainKitError> =>\n Effect.fail(new DomainKitError({ reason }));\n\n/** Decode with any schema, failing with reason `InvalidInput` and the schema's message. */\nexport const decode = <S extends Schema.ConstraintDecoder<unknown>>(\n schema: S,\n input: unknown,\n field?: string,\n): Effect.Effect<S[\"Type\"], DomainKitError> =>\n Schema.decodeUnknownEffect(schema)(input).pipe(\n Effect.mapError(\n (cause) =>\n new DomainKitError({\n reason: new Reason.InvalidInput({\n message: cause.message,\n ...(field === undefined ? {} : { field }),\n }),\n }),\n ),\n );\n"],"mappings":";;;;;;;;AAWA,IAAa,iBAAb,cAAoC,OAAO,YAA4B,2BAA2B,CAAC,CACjG,kBACA,EAAE,QAAQ,OAAO,cAAcA,KAAY,EAAE,CAC/C,CAAC,CAAC;CACA,IAAI,WAAqB;EACvB,QAAQ,KAAK,OAAO,MAApB;GACE,KAAK;GACL,KAAK,YACH,OAAO;GACT,KAAK;GACL,KAAK;GACL,KAAK,aACH,OAAO;GACT,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK,QACH,OAAO;GACT,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK,eACH,OAAO;GACT,KAAK,iBACH,OAAO;GACT,KAAK;GACL,KAAK,kBACH,OAAO;EACX;CACF;CAEA,IAAI,cAAuB;EACzB,OACE,KAAK,OAAO,SAAS,UACrB,KAAK,OAAO,SAAS,yBACrB,KAAK,OAAO,SAAS;CAEzB;;CAGA,IAAI,aAAqB;EACvB,QAAQ,KAAK,OAAO,MAApB;GACE,KAAK,gBACH,OAAO;GACT,KAAK,mBACH,OAAO;GACT,KAAK;GACL,KAAK,aACH,OAAO;GACT,KAAK,YACH,OAAO;GACT,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK,QACH,OAAO;GACT,KAAK,oBACH,OAAO;GACT,KAAK,oBACH,OAAO;GACT,KAAK,eACH,OAAO;GACT,KAAK,uBACH,OAAO;GACT,KAAK;GACL,KAAK;GACL,KAAK,kBACH,OAAO;EACX;CACF;CAEA,IAAa,UAAkB;EAC7B,OAAO,KAAK,OAAO;CACrB;AACF;AAEA,MAAa,oBAAoB,UAC/B,iBAAiB;;AAGnB,MAAa,QAAgC,WAC3C,OAAO,KAAK,IAAI,eAAe,EAAE,OAAO,CAAC,CAAC;;AAG5C,MAAa,UACX,QACA,OACA,UAEA,OAAO,oBAAoB,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,KACxC,OAAO,UACJ,UACC,IAAI,eAAe,EACjB,QAAQ,IAAIC,aAAoB;CAC9B,SAAS,MAAM;CACf,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;AACzC,CAAC,EACH,CAAC,CACL,CACF"}
|
|
1
|
+
{"version":3,"file":"error.mjs","names":["Reason.Model","Reason.InvalidInput"],"sources":["../../src/internal/error.ts"],"sourcesContent":["/**\n * The one error every DomainKit operation fails with, published as `DomainKit.Error`. Hosts match\n * on `reason`; `category`, `isRetryable`, and `httpStatus` derive from it. Modelled on `SqlError`\n * and `HttpClientError` in Effect.\n */\nimport { Effect, Schema } from \"effect\";\n\nimport * as Reason from \"../Reason.ts\";\n\nexport type Category = \"request\" | \"auth\" | \"plan\" | \"provider\" | \"storage\" | \"internal\";\n\nexport class DomainKitError extends Schema.TaggedError<DomainKitError>(\"@domainkit/DomainKitError\")(\n \"DomainKitError\",\n { reason: Schema.suspend(() => Reason.Model) },\n) {\n get category(): Category {\n switch (this.reason._tag) {\n case \"InvalidInput\":\n case \"NotFound\":\n return \"request\";\n case \"Unauthenticated\":\n case \"Forbidden\":\n case \"Reconnect\":\n return \"auth\";\n case \"Conflict\":\n case \"Stale\":\n case \"BatchStale\":\n case \"Expired\":\n case \"Busy\":\n return \"plan\";\n case \"ProviderRejected\":\n case \"ProviderUnavailable\":\n case \"ProviderConflict\":\n case \"Unsupported\":\n return \"provider\";\n case \"StorageFailed\":\n return \"storage\";\n case \"CryptoFailed\":\n case \"ResolverFailed\":\n return \"internal\";\n }\n }\n\n get isRetryable(): boolean {\n return (\n this.reason._tag === \"Busy\" ||\n this.reason._tag === \"ProviderUnavailable\" ||\n this.reason._tag === \"StorageFailed\"\n );\n }\n\n /** The HTTP status `domainkit/server` answers with; hosts mapping their own routes use the same table. */\n get httpStatus(): number {\n switch (this.reason._tag) {\n case \"InvalidInput\":\n return 400;\n case \"Unauthenticated\":\n return 401;\n case \"Forbidden\":\n case \"Reconnect\":\n return 403;\n case \"NotFound\":\n return 404;\n case \"Conflict\":\n case \"Stale\":\n case \"BatchStale\":\n case \"Expired\":\n case \"Busy\":\n return 409;\n case \"ProviderRejected\":\n return 502;\n case \"ProviderConflict\":\n return 409;\n case \"Unsupported\":\n return 501;\n case \"ProviderUnavailable\":\n return 503;\n case \"StorageFailed\":\n case \"CryptoFailed\":\n case \"ResolverFailed\":\n return 500;\n }\n }\n\n override get message(): string {\n return this.reason.message;\n }\n}\n\nexport const isDomainKitError = (input: unknown): input is DomainKitError =>\n input instanceof DomainKitError;\n\n/** `Effect.fail(new DomainKitError({ reason }))` shorthand used across the package. */\nexport const fail = <R extends Reason.Model>(reason: R): Effect.Effect<never, DomainKitError> =>\n Effect.fail(new DomainKitError({ reason }));\n\n/** Decode with any schema, failing with reason `InvalidInput` and the schema's message. */\nexport const decode = <S extends Schema.ConstraintDecoder<unknown>>(\n schema: S,\n input: unknown,\n field?: string,\n): Effect.Effect<S[\"Type\"], DomainKitError> =>\n Schema.decodeUnknownEffect(schema)(input).pipe(\n Effect.mapError(\n (cause) =>\n new DomainKitError({\n reason: new Reason.InvalidInput({\n message: cause.message,\n ...(field === undefined ? {} : { field }),\n }),\n }),\n ),\n );\n"],"mappings":";;;;;;;;AAWA,IAAa,iBAAb,cAAoC,OAAO,YAA4B,2BAA2B,CAAC,CACjG,kBACA,EAAE,QAAQ,OAAO,cAAcA,KAAY,EAAE,CAC/C,CAAC,CAAC;CACA,IAAI,WAAqB;EACvB,QAAQ,KAAK,OAAO,MAApB;GACE,KAAK;GACL,KAAK,YACH,OAAO;GACT,KAAK;GACL,KAAK;GACL,KAAK,aACH,OAAO;GACT,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK,QACH,OAAO;GACT,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK,eACH,OAAO;GACT,KAAK,iBACH,OAAO;GACT,KAAK;GACL,KAAK,kBACH,OAAO;EACX;CACF;CAEA,IAAI,cAAuB;EACzB,OACE,KAAK,OAAO,SAAS,UACrB,KAAK,OAAO,SAAS,yBACrB,KAAK,OAAO,SAAS;CAEzB;;CAGA,IAAI,aAAqB;EACvB,QAAQ,KAAK,OAAO,MAApB;GACE,KAAK,gBACH,OAAO;GACT,KAAK,mBACH,OAAO;GACT,KAAK;GACL,KAAK,aACH,OAAO;GACT,KAAK,YACH,OAAO;GACT,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK,QACH,OAAO;GACT,KAAK,oBACH,OAAO;GACT,KAAK,oBACH,OAAO;GACT,KAAK,eACH,OAAO;GACT,KAAK,uBACH,OAAO;GACT,KAAK;GACL,KAAK;GACL,KAAK,kBACH,OAAO;EACX;CACF;CAEA,IAAa,UAAkB;EAC7B,OAAO,KAAK,OAAO;CACrB;AACF;AAEA,MAAa,oBAAoB,UAC/B,iBAAiB;;AAGnB,MAAa,QAAgC,WAC3C,OAAO,KAAK,IAAI,eAAe,EAAE,OAAO,CAAC,CAAC;;AAG5C,MAAa,UACX,QACA,OACA,UAEA,OAAO,oBAAoB,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,KACxC,OAAO,UACJ,UACC,IAAI,eAAe,EACjB,QAAQ,IAAIC,aAAoB;CAC9B,SAAS,MAAM;CACf,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;AACzC,CAAC,EACH,CAAC,CACL,CACF"}
|