@cosmicdrift/kumiko-types 0.388.0 → 0.390.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/dist/config.d.ts CHANGED
@@ -4,7 +4,7 @@ import type { ConfigScope } from "./config-scope.ts";
4
4
  import type { DbConnection } from "./db-connection.ts";
5
5
  import type { EntityFieldDefinition, OptionsQueryPayload } from "./fields.ts";
6
6
  import type { EscapeHatchDeclaration, JobContext, JobDeferral } from "./handlers.ts";
7
- import type { PostDeleteHookFn, PostSaveHookFn, PreDeleteHookFn, PreQueryHookFn, PreSaveHookFn } from "./hooks.ts";
7
+ import type { PostDeleteHookFn, PostSaveHookFn, PreDeleteHookFn, PreSaveHookFn } from "./hooks.ts";
8
8
  import type { TenantId } from "./identifiers.ts";
9
9
  import type { NavIconKey } from "./nav-icon.ts";
10
10
  import type { DashboardCustomPanel, DashboardScreenPanel } from "./screen.ts";
@@ -31,6 +31,7 @@ export type ConfigValidateContext = {
31
31
  readonly tenantId: TenantId;
32
32
  readonly userId: string | null;
33
33
  readonly db: TenantDb;
34
+ readonly readCascade: (qualifiedKey: string) => Promise<ConfigCascade>;
34
35
  };
35
36
  export type ConfigValidateFn<T extends ConfigKeyType = ConfigKeyType> = (value: ConfigValue<T>, ctx: ConfigValidateContext) => Promise<void>;
36
37
  export type ConfigBacking = "config" | "secrets";
@@ -282,7 +283,6 @@ export type RegistrarExtensionHooks = {
282
283
  readonly postSave?: PostSaveHookFn;
283
284
  readonly preDelete?: PreDeleteHookFn;
284
285
  readonly postDelete?: PostDeleteHookFn;
285
- readonly preQuery?: PreQueryHookFn;
286
286
  };
287
287
  export type UiExtensionDef = {
288
288
  readonly editSection?: string;
@@ -1,4 +1,4 @@
1
- import type postgres from "postgres";
1
+ import type { SQL, TransactionSQL } from "bun";
2
2
  export type RawDbClient = {
3
3
  unsafe: (sql: string, params?: readonly unknown[]) => PromiseLike<readonly unknown[]>;
4
4
  begin: <T>(fn: (tx: DbTx) => Promise<T>) => Promise<T>;
@@ -6,11 +6,11 @@ export type RawDbClient = {
6
6
  timeout?: number;
7
7
  }) => Promise<void>;
8
8
  };
9
- export type DbConnection = ReturnType<typeof postgres> | RawDbClient;
10
- export type DbTx = postgres.TransactionSql<Record<string, unknown>> | RawDbClient;
9
+ export type DbConnection = SQL | RawDbClient;
10
+ export type DbTx = TransactionSQL | RawDbClient;
11
11
  export type DbRunner = DbConnection | DbTx;
12
12
  export type DbRow = Record<string, unknown>;
13
- export type PgClient = ReturnType<typeof postgres>;
13
+ export type PgClient = SQL;
14
14
  export type PgListenSubscription = {
15
15
  unlisten(): Promise<void>;
16
16
  };
@@ -100,7 +100,7 @@ export type EventStoreExecutor<Row extends Record<string, unknown> = Record<stri
100
100
  /** Per-call SearchAdapter override. When the
101
101
  * executor didn't get a SearchAdapter via Options at build time
102
102
  * (defaultEntityQueryHandler path), the caller (handler) can pass
103
- * one from ctx.searchAdapter here at runtime.
103
+ * one from ctx.services.searchAdapter here at runtime.
104
104
  * options.searchAdapter (build-time) wins — the runtime override
105
105
  * is the fallback for the default wrapper. */
106
106
  runtimeOptions?: ListRuntimeOptions) => Promise<CursorResult<Row>>;
@@ -1,6 +1,6 @@
1
1
  // Cross-Feature Compile-Time Type Map.
2
2
  //
3
- // Purpose: check ctx.appendEvent / ctx.queryProjection / dispatcher.write against a
3
+ // Purpose: check ctx.events.append / ctx.dispatch.queryProjection / dispatcher.write against a
4
4
  // statically known schema image instead of failing only at boot or runtime
5
5
  // (zod-validate). The Designer/AI layer benefits immediately: autocomplete knows all
6
6
  // event types from all loaded features, and payload-shape mismatches appear in the
@@ -0,0 +1,321 @@
1
+ import type * as z from "zod";
2
+ import type { ZodType } from "zod";
3
+ import type { ConfigKeyDefinition, ConfigKeyHandle, ConfigKeyType, ConfigSeedDef, ExtensionSelectorPanel, JobDefinition, JobHandlerFn, NotificationDataFn, NotificationLocalizedTemplate, NotificationRecipientFn, NotificationRecordMapping, NotificationTemplateBody, NotificationTemplateFn, NotificationTemplateMeta, RegistrarExtensionDef, TemplateVariables, TranslationsDef } from "./config.ts";
4
+ import type { QueryHandlerDefinition, StreamHandlerDefinition, WriteHandlerDefinition } from "./define-handler.ts";
5
+ import type { RegisterEntityCrudOptions } from "./entity-handlers.ts";
6
+ import type { EntityTableMeta } from "./entity-table-meta-types.ts";
7
+ import type { KumikoEventTypeMap } from "./event-type-map.ts";
8
+ import type { ExtensionOptionsArgs, ExtensionOptionsFor } from "./extension-options-map.ts";
9
+ import type { BootCheckFn, MetricOptions, SecretKeyHandle, SecretNamespaceHandle, SecretNamespaceOptions, SecretOptions, StoreTableOptions, UiHints } from "./feature.ts";
10
+ import type { EntityDefinition } from "./fields.ts";
11
+ import type { AccessRule, AuthClaimsFn, ClaimKeyHandle, ClaimKeyType, DeclarativeEventMigration, EntityRef, EscapeHatchDeclaration, EventDef, EventPiiStance, EventUpcastFn, HandlerRef, NameOrRef, QualifiedEventName, RateLimitDeclaration, StreamHandlerFn } from "./handlers.ts";
12
+ import type { HookPhase, PostDeleteHookFn, PostQueryHookFn, PostSaveHookFn, PreDeleteHookFn, PreSaveHookFn, SearchPayloadContributorFn, ValidationHookFn } from "./hooks.ts";
13
+ import type { HttpRouteDefinition } from "./http-route.ts";
14
+ import type { ContentCollectionDefinition, NavDefinition } from "./nav.ts";
15
+ import type { EntityProjectionExtension, MultiStreamProjectionDefinition, ProjectionDefinition } from "./projection.ts";
16
+ import type { RelationDefinition } from "./relations.ts";
17
+ import type { ScreenDefinition } from "./screen.ts";
18
+ import type { TreeActionDef, TreeActionsHandle } from "./tree-node.ts";
19
+ import type { WebSocketRouteDefinition } from "./websocket-route.ts";
20
+ import type { WorkspaceDefinition } from "./workspace.ts";
21
+ type RefOrRefs = NameOrRef | readonly NameOrRef[];
22
+ type HookTarget = RefOrRefs | {
23
+ readonly allOf: NameOrRef;
24
+ };
25
+ export type EntityObjectDefinition = {
26
+ readonly name: string;
27
+ readonly backingTable?: unknown;
28
+ } & EntityDefinition;
29
+ type HookEscapeHatch = {
30
+ readonly escapeHatch?: EscapeHatchDeclaration;
31
+ };
32
+ type HookPhaseOption = {
33
+ readonly phase?: HookPhase;
34
+ };
35
+ export type HookDefinition = {
36
+ readonly type: "validation";
37
+ readonly target: RefOrRefs;
38
+ readonly handler: ValidationHookFn;
39
+ readonly escapeHatch?: never;
40
+ readonly phase?: never;
41
+ } | ({
42
+ readonly type: "preSave";
43
+ readonly target: RefOrRefs;
44
+ readonly handler: PreSaveHookFn;
45
+ readonly phase?: never;
46
+ } & HookEscapeHatch) | ({
47
+ readonly type: "postSave";
48
+ readonly target: HookTarget;
49
+ readonly handler: PostSaveHookFn;
50
+ } & HookEscapeHatch & HookPhaseOption) | ({
51
+ readonly type: "preDelete";
52
+ readonly target: HookTarget;
53
+ readonly handler: PreDeleteHookFn;
54
+ readonly phase?: never;
55
+ } & HookEscapeHatch) | ({
56
+ readonly type: "postDelete";
57
+ readonly target: HookTarget;
58
+ readonly handler: PostDeleteHookFn;
59
+ } & HookEscapeHatch & HookPhaseOption) | ({
60
+ readonly type: "postQuery";
61
+ readonly target: HookTarget;
62
+ readonly handler: PostQueryHookFn;
63
+ readonly phase?: never;
64
+ } & HookEscapeHatch);
65
+ /**
66
+ * `r.requires({ features })` declares the features this one depends on; the
67
+ * `.projection` extension declares read-side projection tables that this
68
+ * feature's pipeline steps are allowed to write via
69
+ * `r.step.unsafeProjectionUpsert` (hard-required for any unsafeProjection-*
70
+ * step usage, see Q10).
71
+ *
72
+ * @example r.requires({ features: ["auth", "tenant"] });
73
+ */
74
+ export type RequiresApi = ((options: {
75
+ readonly features: readonly string[];
76
+ }) => void) & {
77
+ readonly projection: (tableName: string) => void;
78
+ readonly step: (stepKind: string) => void;
79
+ };
80
+ /**
81
+ * `TFeature` is the literal feature-name from `defineFeature("foo", ...)` —
82
+ * default-`string` keeps every existing usage zero-config. Strict-typed
83
+ * features (apps that opt into the literal-name flavour) get propagated
84
+ * through to `defineEvent` so the returned `EventDef.name` is a literal
85
+ * `${CamelToKebab<TFeature>}:event:${CamelToKebab<TInner>}`. That literal
86
+ * threads through `ctx.events.append({ type: eventDef.name, ... })`,
87
+ * keeping strict-mode alive even when handlers route via `eventDef.name`
88
+ * instead of hand-typed string literals.
89
+ */
90
+ export type FeatureRegistrar<TFeature extends string = string> = {
91
+ systemScope(): void;
92
+ describe(text: string): void;
93
+ requires: RequiresApi;
94
+ optionalRequires(...featureNames: string[]): void;
95
+ optionalRequires(options: {
96
+ readonly features: readonly string[];
97
+ }): void;
98
+ toggleable(options: {
99
+ default: boolean;
100
+ }): void;
101
+ adoptsBundledLists(): void;
102
+ uiHints(hints: UiHints): void;
103
+ /**
104
+ * @example r.entity({ name: "note", fields: { title: createTextField({ required: true }) } });
105
+ */
106
+ entity(definition: EntityObjectDefinition): EntityRef;
107
+ crud(entityName: string, entity: EntityDefinition, options?: RegisterEntityCrudOptions): void;
108
+ /**
109
+ * @example r.writeHandler({ name: "note:archive", schema, handler, access: { roles: ["User"] } });
110
+ */
111
+ writeHandler<TName extends string, TSchema extends ZodType, TPrepared = undefined>(def: WriteHandlerDefinition<TName, TSchema, unknown, KumikoEventTypeMap, TPrepared>): HandlerRef;
112
+ /**
113
+ * @example r.queryHandler({ name: "note:list", schema, handler, access: { roles: ["User"] } });
114
+ */
115
+ queryHandler<TName extends string, TSchema extends ZodType>(def: QueryHandlerDefinition<TName, TSchema>): HandlerRef;
116
+ streamHandler<TName extends string, TSchema extends ZodType>(def: StreamHandlerDefinition<TName, TSchema>): HandlerRef;
117
+ streamHandler<TSchema extends ZodType>(name: string, schema: TSchema, handler: StreamHandlerFn<z.infer<TSchema>>, options: {
118
+ access: AccessRule;
119
+ rateLimit?: RateLimitDeclaration;
120
+ escapeHatch?: EscapeHatchDeclaration;
121
+ }): HandlerRef;
122
+ relation(entity: NameOrRef, relationName: string, definition: RelationDefinition): void;
123
+ relation<TDef extends RelationDefinition>(definition: {
124
+ readonly entity: NameOrRef;
125
+ readonly name: string;
126
+ } & TDef): void;
127
+ /**
128
+ * @example r.hook({ type: "postSave", target: { allOf: noteRef }, handler: notifyOwner });
129
+ */
130
+ hook(definition: HookDefinition): void;
131
+ searchPayloadExtension(entity: NameOrRef, fn: SearchPayloadContributorFn): void;
132
+ config<T extends ConfigKeyType>(keyName: string, def: ConfigKeyDefinition<T>): ConfigKeyHandle<T>;
133
+ config<TKeys extends Readonly<Record<string, ConfigKeyDefinition<ConfigKeyType>>>>(definition: {
134
+ readonly keys: TKeys;
135
+ readonly seeds?: Readonly<Record<string, ConfigSeedDef>>;
136
+ }): {
137
+ readonly [K in keyof TKeys]: ConfigKeyHandle<TKeys[K]["type"]>;
138
+ };
139
+ job(name: string, options: Omit<JobDefinition, "name" | "handler">, handler: JobHandlerFn): void;
140
+ job(definition: JobDefinition): void;
141
+ notification(name: string, definition: {
142
+ readonly trigger?: {
143
+ readonly on: NameOrRef;
144
+ };
145
+ readonly recipient?: NotificationRecipientFn;
146
+ readonly data?: NotificationDataFn;
147
+ readonly templates?: Readonly<Record<string, NotificationTemplateFn>>;
148
+ readonly variables?: TemplateVariables;
149
+ readonly record?: NotificationRecordMapping;
150
+ readonly template?: Readonly<Record<string, NotificationTemplateBody>>;
151
+ readonly meta?: Readonly<Record<string, NotificationTemplateMeta>>;
152
+ readonly localizedTemplate?: () => NotificationLocalizedTemplate;
153
+ readonly renamedFrom?: readonly string[];
154
+ readonly systemOnly?: boolean;
155
+ readonly unsubscribe?: boolean;
156
+ readonly tenantTransport?: boolean;
157
+ }): void;
158
+ notification(definition: {
159
+ readonly name: string;
160
+ readonly trigger?: {
161
+ readonly on: NameOrRef;
162
+ };
163
+ readonly recipient?: NotificationRecipientFn;
164
+ readonly data?: NotificationDataFn;
165
+ readonly templates?: Readonly<Record<string, NotificationTemplateFn>>;
166
+ readonly variables?: TemplateVariables;
167
+ readonly record?: NotificationRecordMapping;
168
+ readonly template?: Readonly<Record<string, NotificationTemplateBody>>;
169
+ readonly meta?: Readonly<Record<string, NotificationTemplateMeta>>;
170
+ readonly localizedTemplate?: () => NotificationLocalizedTemplate;
171
+ readonly renamedFrom?: readonly string[];
172
+ readonly systemOnly?: boolean;
173
+ readonly unsubscribe?: boolean;
174
+ readonly tenantTransport?: boolean;
175
+ }): void;
176
+ translations(def: TranslationsDef): void;
177
+ defineEvent<const TInner extends string, TPayload>(name: TInner, schema: ZodType<TPayload>, options: {
178
+ readonly version?: number;
179
+ readonly piiFields: EventPiiStance;
180
+ readonly migrations?: readonly {
181
+ readonly fromVersion: number;
182
+ readonly toVersion: number;
183
+ readonly transform: EventUpcastFn | DeclarativeEventMigration;
184
+ }[];
185
+ }): EventDef<TPayload, QualifiedEventName<TFeature, TInner>>;
186
+ readsConfig(...qualifiedKeys: string[]): void;
187
+ readsConfig(options: {
188
+ readonly keys: readonly string[];
189
+ }): void;
190
+ referenceData(entity: NameOrRef, data: readonly Record<string, unknown>[], options?: {
191
+ upsertKey?: string;
192
+ }): void;
193
+ referenceData(definition: {
194
+ readonly entity: NameOrRef;
195
+ readonly data: readonly Record<string, unknown>[];
196
+ readonly upsertKey?: string;
197
+ }): void;
198
+ extendsRegistrar(name: string, def: RegistrarExtensionDef): void;
199
+ useExtension<N extends string>(extensionName: N, entity: NameOrRef, ...options: ExtensionOptionsArgs<N>): void;
200
+ useExtension<N extends string>(definition: {
201
+ readonly name: N;
202
+ readonly entity: NameOrRef;
203
+ } & ExtensionOptionsFor<N>): void;
204
+ /**
205
+ * Declares which config key selects the active provider under an
206
+ * extension point — called by the point-owning foundation (e.g.
207
+ * `r.extensionSelector("mailTransport", configKeys.provider)`).
208
+ * Readiness gating counts a provider-feature's `required` keys and
209
+ * secrets only while that provider is the selected one. Registry-build
210
+ * fails on duplicate declarations per extension and on selector keys
211
+ * that no mounted feature declares. A masked tenant selector key makes the
212
+ * Settings-Hub render one tenant dashboard for the owner: the selector as a
213
+ * select of the mounted plugin ids, the selected plugin's config and secrets
214
+ * as panels, and plugin features lose their own tenant nav. `config:write:set`
215
+ * rejects unknown plugin ids (`""` clears). `options.panels` adds the owner's own
216
+ * `custom`/`screen` panels to that dashboard, after the selection and before the
217
+ * plugin panels (a short `screen` id means a screen of the declaring feature).
218
+ */
219
+ extensionSelector(extensionName: string, key: {
220
+ readonly name: string;
221
+ } | string, options?: {
222
+ readonly panels?: readonly ExtensionSelectorPanel[];
223
+ }): void;
224
+ /**
225
+ * Marker declaration: this feature provides a cross-feature API under the
226
+ * given name. The implementation is registered separately as a query or
227
+ * write handler under the QN pattern; `r.exposesApi` is only a boot-check
228
+ * surface.
229
+ *
230
+ * The boot validator checks that every `r.usesApi(name)` finds a matching
231
+ * exposer, that the exposer feature is listed in requires/optionalRequires,
232
+ * and that no API name is exposed more than once.
233
+ *
234
+ * ```ts
235
+ * defineFeature("compliance-profiles", (r) => {
236
+ * r.exposesApi("compliance.forTenant");
237
+ * r.queryHandler({
238
+ * name: "compliance:query:for-tenant",
239
+ * // ... actual implementation
240
+ * });
241
+ * });
242
+ * ```
243
+ */
244
+ exposesApi(apiName: string): void;
245
+ /**
246
+ * Declares that this feature calls a cross-feature API. The boot validator
247
+ * checks that another feature provides `r.exposesApi(apiName)` and that
248
+ * this feature lists it in `r.requires/optionalRequires`.
249
+ *
250
+ * ```ts
251
+ * defineFeature("user-data-rights", (r) => {
252
+ * r.requires({ features: ["compliance-profiles"] });
253
+ * r.usesApi("compliance.forTenant");
254
+ * });
255
+ * ```
256
+ */
257
+ usesApi(apiName: string): void;
258
+ /**
259
+ * Declares a boot-time mount-invariant for this feature. `fn` runs once
260
+ * at boot with a ctx exposing every mounted feature — throw to fail the
261
+ * boot with a clear message. Multiple calls per feature are allowed.
262
+ *
263
+ * Use for cross-feature invariants that `r.requires` can't express
264
+ * because they're conditional (only fail when the feature has a
265
+ * specific shape), e.g. "if this feature has PII entities, some
266
+ * user-data-hook feature must be mounted" (the prompt-store trap —
267
+ * a UserData feature was written but never mounted, and nothing caught
268
+ * it at boot):
269
+ *
270
+ * ```ts
271
+ * defineFeature("prompt-store", (r) => {
272
+ * const promptFields = { text: createTextField({ personal: "self", find: "none" }) };
273
+ * r.entity({ name: "prompt", fields: promptFields });
274
+ * r.bootCheck(({ features }) => {
275
+ * // Conditional on this feature's own shape (has a pii field) —
276
+ * // r.requires({ features: ["user-data-hook"] }) can't express that, it would fail
277
+ * // even for a prompt-store variant with no PII fields at all.
278
+ * const hasPiiField = Object.values(promptFields).some((f) => f.pii);
279
+ * const hasUserDataHook = features.some((f) => f.name === "user-data-hook");
280
+ * if (hasPiiField && !hasUserDataHook) {
281
+ * throw new Error("prompt-store has PII fields but no user-data-hook feature is mounted");
282
+ * }
283
+ * });
284
+ * });
285
+ * ```
286
+ */
287
+ bootCheck(fn: BootCheckFn): void;
288
+ metric(shortName: string, options: MetricOptions): void;
289
+ metric(definition: {
290
+ readonly name: string;
291
+ } & MetricOptions): void;
292
+ secret(shortName: string, options: SecretOptions): SecretKeyHandle;
293
+ secret(definition: {
294
+ readonly name: string;
295
+ } & SecretOptions): SecretKeyHandle;
296
+ secretNamespace(shortName: string, options: SecretNamespaceOptions): SecretNamespaceHandle;
297
+ secretNamespace(definition: {
298
+ readonly name: string;
299
+ } & SecretNamespaceOptions): SecretNamespaceHandle;
300
+ projection(definition: ProjectionDefinition): void;
301
+ multiStreamProjection(definition: MultiStreamProjectionDefinition): void;
302
+ extendEntityProjection(entityName: string, extension: EntityProjectionExtension): void;
303
+ authClaims(fn: AuthClaimsFn): void;
304
+ claimKey<T extends ClaimKeyType>(shortName: string, options: {
305
+ readonly type: T;
306
+ }): ClaimKeyHandle<T>;
307
+ claimKey<T extends ClaimKeyType>(definition: {
308
+ readonly name: string;
309
+ readonly type: T;
310
+ }): ClaimKeyHandle<T>;
311
+ screen(definition: ScreenDefinition): void;
312
+ nav(definition: NavDefinition): void;
313
+ contentCollection(definition: ContentCollectionDefinition): string;
314
+ workspace(definition: WorkspaceDefinition): void;
315
+ httpRoute(definition: HttpRouteDefinition): void;
316
+ webSocketRoute(definition: WebSocketRouteDefinition): void;
317
+ storeTable(meta: EntityTableMeta, options: StoreTableOptions): void;
318
+ treeActions<const TActions extends Record<string, TreeActionDef>>(actions: TActions): TreeActionsHandle<TFeature, TActions>;
319
+ envSchema(schema: z.ZodObject<z.ZodRawShape>): void;
320
+ };
321
+ export {};
@@ -0,0 +1 @@
1
+ export {};