@cosmicdrift/kumiko-types 0.166.0 → 0.167.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 CHANGED
@@ -7,9 +7,10 @@ Framework type definitions for Kumiko — `FeatureDefinition`, boot-check types,
7
7
  and the pure engine types. Lets downstream consumers build against the type
8
8
  contracts without importing the whole framework package.
9
9
 
10
- Also contains the identity-sensitive error classes (`event-store-errors.ts`,
11
- `kms-adapter-types.ts`) as runtime code `kumiko-framework`/
12
- `kumiko-bundled-features` declare this package as a `peerDependency`
13
- (single-copy constraint), not a plain dependency.
10
+ Carries no identity-sensitive runtime values: no classes, and brand symbols use
11
+ `Symbol.for`. Resolving two copies of this package is therefore harmless, so
12
+ `kumiko-framework`/`kumiko-bundled-features` declare it as a plain dependency.
13
+ The error classes callers branch on with `instanceof` live in `kumiko-framework`
14
+ (`/event-store`, `/crypto`), which stays a single copy.
14
15
 
15
16
  See the [monorepo root README](../../README.md) for the broader pitch.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-types",
3
- "version": "0.166.0",
4
- "description": "Framework-Type-Definitions für Kumiko — FeatureDefinition, BootCheck-Types und die reinen Engine-Types. Erlaubt Downstream-Konsumenten, gegen die Type-Contracts zu bauen, ohne das ganze Framework-Package zu importieren. Enthaelt auch die identitaets-sensitiven Error-Klassen (event-store-errors.ts, kms-adapter-types.ts) als Runtime-Code kumiko-framework/kumiko-bundled-features deklarieren dieses Package deshalb als peerDependency (Single-Copy-Zwang), nicht als plain dependency.",
3
+ "version": "0.167.0",
4
+ "description": "Framework-Type-Definitions für Kumiko — FeatureDefinition, BootCheck-Types und die reinen Engine-Types. Erlaubt Downstream-Konsumenten, gegen die Type-Contracts zu bauen, ohne das ganze Framework-Package zu importieren. Enthaelt keine identitaets-sensitiven Runtime-Werte mehr (Error-Klassen leben seit #1629 in kumiko-framework, Brand-Symbole nutzen Symbol.for) und ist deshalb eine plain dependency, keine peerDependency.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
7
7
  "repository": {
@@ -206,10 +206,6 @@
206
206
  "types": "./src/secrets-types.ts",
207
207
  "default": "./src/secrets-types.ts"
208
208
  },
209
- "./event-store-errors": {
210
- "types": "./src/event-store-errors.ts",
211
- "default": "./src/event-store-errors.ts"
212
- },
213
209
  "./kms-adapter-types": {
214
210
  "types": "./src/kms-adapter-types.ts",
215
211
  "default": "./src/kms-adapter-types.ts"
@@ -228,12 +224,12 @@
228
224
  }
229
225
  },
230
226
  "devDependencies": {
231
- "hono": "^4.12.18",
227
+ "hono": "^4.12.27",
232
228
  "postgres": "^3.4.9",
233
229
  "temporal-polyfill": "^0.3.2"
234
230
  },
235
231
  "peerDependencies": {
236
- "hono": "^4.12.18",
232
+ "hono": "^4.12.27",
237
233
  "postgres": "^3.4.9",
238
234
  "temporal-polyfill": "^0.3.2"
239
235
  },
@@ -1,5 +1,13 @@
1
1
  import type { TenantId } from "./identifiers";
2
2
 
3
+ // Structural shape of kumiko-framework's VersionConflictError. The class
4
+ // itself cannot live here (#1629) — this package must stay free of
5
+ // identity-sensitive runtime values so it can be a plain dependency.
6
+ export interface VersionConflict extends Error {
7
+ readonly aggregateId: string;
8
+ readonly expectedVersion: number;
9
+ }
10
+
3
11
  export type EventMetadata = {
4
12
  readonly userId: string;
5
13
  readonly requestId?: string;
package/src/handlers.ts CHANGED
@@ -689,7 +689,7 @@ export type UnsafeAppendEventFn = (args: AppendEventArgs) => Promise<void>;
689
689
  // entire enclosing transaction.
690
690
  export type TryAppendEventResult =
691
691
  | { readonly ok: true; readonly event: import("./event-store-types").StoredEvent }
692
- | { readonly ok: false; readonly conflict: import("./event-store-errors").VersionConflictError };
692
+ | { readonly ok: false; readonly conflict: import("./event-store-types").VersionConflict };
693
693
 
694
694
  export type TryAppendEventFn = (args: AppendEventArgs) => Promise<TryAppendEventResult>;
695
695
 
@@ -95,24 +95,3 @@ export type KmsAdapter = LocalKeyKmsAdapter | RemoteCryptoKmsAdapter;
95
95
  export function isLocalKeyKmsAdapter(adapter: KmsAdapter): adapter is LocalKeyKmsAdapter {
96
96
  return adapter.capabilities.mode === "local-key";
97
97
  }
98
-
99
- export class KeyErasedError extends Error {
100
- constructor(public readonly subject: SubjectId) {
101
- super(`Subject key erased: ${subjectIdToKey(subject)}`);
102
- this.name = "KeyErasedError";
103
- }
104
- }
105
-
106
- export class KeyNotFoundError extends Error {
107
- constructor(public readonly subject: SubjectId) {
108
- super(`Subject key not found: ${subjectIdToKey(subject)}`);
109
- this.name = "KeyNotFoundError";
110
- }
111
- }
112
-
113
- export class KeyAlreadyExistsError extends Error {
114
- constructor(public readonly subject: SubjectId) {
115
- super(`Subject key already exists: ${subjectIdToKey(subject)}`);
116
- this.name = "KeyAlreadyExistsError";
117
- }
118
- }
@@ -10,9 +10,14 @@ import type { TenantId } from "./identifiers";
10
10
  // intentional. Framework code that sees `Secret<string>` knows the caller
11
11
  // has already gone through the audited ctx.secrets.get path.
12
12
  //
13
- // The brand is a real (non-registered) Symbol so it exists at runtime for
14
- // isSecret() without clashing with user-land symbols of the same name.
15
- const SecretBrand: unique symbol = Symbol("kumiko.secret");
13
+ // Registered symbol, matching KUMIKO_*_SYMBOL in schema-table-types.ts. A
14
+ // plain Symbol() is per-copy: two resolved copies of this package brand with
15
+ // two different symbols, so createSecret() from one and isSecret() from the
16
+ // other disagree — and isSecret() is the ONLY thing assertNoSecretLeak has,
17
+ // so a false there serializes the plaintext into the response. The namespaced
18
+ // key makes accidental user-land collision implausible; a deliberate forge
19
+ // would only over-trigger the guard, which fails safe.
20
+ const SecretBrand: unique symbol = Symbol.for("kumiko.secret");
16
21
 
17
22
  export type Secret<T = string> = {
18
23
  readonly [SecretBrand]: true;
@@ -1,54 +0,0 @@
1
- // Failure modes of the event-store's append() path. Surfaced as typed
2
- // errors so the executor layer can map them to the framework's
3
- // WriteResult error contract (version_conflict).
4
-
5
- export class VersionConflictError extends Error {
6
- public readonly aggregateId: string;
7
- public readonly expectedVersion: number;
8
- constructor(aggregateId: string, expectedVersion: number) {
9
- super(
10
- `Version conflict on aggregate ${aggregateId}: expected predecessor version ${expectedVersion}`,
11
- );
12
- this.name = "VersionConflictError";
13
- this.aggregateId = aggregateId;
14
- this.expectedVersion = expectedVersion;
15
- }
16
- }
17
-
18
- // Thrown when append() collides on the partial unique index over
19
- // metadata.idempotencyKey (tenant-scoped). Distinct from VersionConflictError:
20
- // a version conflict means two writers raced the same predecessor; this
21
- // means the same idempotency key was used twice, which is a caller-side
22
- // retry that must have already appended once. Callers that set
23
- // idempotencyKey should treat this as "already applied" rather than retry.
24
- export class IdempotentAppendConflictError extends Error {
25
- public readonly tenantId: string;
26
- public readonly idempotencyKey: string;
27
- constructor(tenantId: string, idempotencyKey: string) {
28
- super(
29
- `Idempotency conflict on tenant ${tenantId}: an event with idempotencyKey "${idempotencyKey}" was already appended.`,
30
- );
31
- this.name = "IdempotentAppendConflictError";
32
- this.tenantId = tenantId;
33
- this.idempotencyKey = idempotencyKey;
34
- }
35
- }
36
-
37
- // Thrown when ctx.appendEvent targets an archived stream. Archived aggregates
38
- // are read-only — restoreStream() makes them writable again. The archive
39
- // state is not carried on the events themselves; it lives on the sparse
40
- // kumiko_archived_streams table. Handlers that need to branch on archive
41
- // state should call ctx.isStreamArchived(id) first.
42
- export class ArchivedStreamError extends Error {
43
- public readonly tenantId: string;
44
- public readonly aggregateId: string;
45
- constructor(tenantId: string, aggregateId: string) {
46
- super(
47
- `Aggregate ${aggregateId} on tenant ${tenantId} is archived — appendEvent is blocked. ` +
48
- `Call restoreStream() to re-open the stream before writing.`,
49
- );
50
- this.name = "ArchivedStreamError";
51
- this.tenantId = tenantId;
52
- this.aggregateId = aggregateId;
53
- }
54
- }