@cosmicdrift/kumiko-types 0.387.0 → 0.388.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
@@ -231,6 +231,8 @@ export type NotificationTemplateMeta = {
231
231
  readonly layoutSlug?: string;
232
232
  readonly category?: "transactional" | "marketing";
233
233
  readonly sender?: string;
234
+ readonly senderName?: string;
235
+ readonly replyTo?: string;
234
236
  };
235
237
  export type NotificationTemplateBody = {
236
238
  readonly body: string;
@@ -14,6 +14,9 @@ export type WriteHandlerDefinition<TName extends string = string, TSchema extend
14
14
  readonly unsafeSkipTransitionGuard?: boolean;
15
15
  readonly rateLimit?: RateLimitDeclaration;
16
16
  readonly additionalRateLimits?: readonly PayloadRateLimitOption[];
17
+ /** Tokens each request books on every declared rateLimit bucket, computed from the validated
18
+ * payload (e.g. `(p) => p.files.length`). Replaces the options' `cost`; results below 1 count as 1. */
19
+ readonly rateLimitCost?: (payload: z.infer<TSchema>) => number;
17
20
  readonly escapeHatch?: EscapeHatchDeclaration;
18
21
  readonly reserveBeforeTransaction?: PreTransactionReservation;
19
22
  readonly beforeTransaction?: BeforeTransactionPhase<z.infer<TSchema>, TPrepared>;
@@ -31,6 +34,9 @@ export type WriteHandlerInput<TName extends string = string, TSchema extends Zod
31
34
  readonly unsafeSkipTransitionGuard?: boolean;
32
35
  readonly rateLimit?: RateLimitDeclaration;
33
36
  readonly additionalRateLimits?: readonly PayloadRateLimitOption[];
37
+ /** Tokens each request books on every declared rateLimit bucket, computed from the validated
38
+ * payload (e.g. `(p) => p.files.length`). Replaces the options' `cost`; results below 1 count as 1. */
39
+ readonly rateLimitCost?: (payload: z.infer<TSchema>) => number;
34
40
  readonly escapeHatch?: EscapeHatchDeclaration;
35
41
  readonly reserveBeforeTransaction?: PreTransactionReservation;
36
42
  readonly beforeTransaction?: BeforeTransactionPhase<z.infer<TSchema>, TPrepared>;
@@ -3,6 +3,7 @@ import type { EntityDefinition } from "./fields.ts";
3
3
  import type { SessionUser, WriteResult } from "./handlers.ts";
4
4
  import type { DeleteContext, SaveContext } from "./hooks.ts";
5
5
  import type { EntityId, TenantId } from "./identifiers.ts";
6
+ import type { ListAggregatePayload, ListAggregateResult } from "./list-aggregate-types.ts";
6
7
  import type { SearchAdapter } from "./search-adapter.ts";
7
8
  import type { TenantDb } from "./tenant-db-types.ts";
8
9
  export type ParentVisibilityOption = {
@@ -21,6 +22,38 @@ export type LockGuardOption = {
21
22
  readonly unsafeSkipLockGuard?: boolean;
22
23
  readonly writePurpose?: WritePurpose;
23
24
  };
25
+ export type ListRuntimeOptions = {
26
+ readonly searchAdapter?: SearchAdapter;
27
+ readonly includeDeleted?: boolean;
28
+ readonly referenceSearch?: {
29
+ readonly fields: ReadonlyArray<{
30
+ readonly fieldName: string;
31
+ readonly targetEntityName: string;
32
+ readonly labelField: string;
33
+ }>;
34
+ readonly resolveEntity: (entityName: string) => EntityDefinition | undefined;
35
+ };
36
+ readonly referenceSort?: {
37
+ readonly fields: ReadonlyArray<{
38
+ readonly fieldName: string;
39
+ readonly targetEntityName: string;
40
+ readonly labelField: string;
41
+ }>;
42
+ readonly resolveEntity: (entityName: string) => EntityDefinition | undefined;
43
+ };
44
+ readonly parentVisibility?: ParentVisibilityOption;
45
+ };
46
+ type ListFilterOption = {
47
+ readonly field: string;
48
+ readonly op: "eq" | "ne" | "lt" | "gt" | "lte" | "gte" | "in";
49
+ readonly value: unknown;
50
+ };
51
+ export type ListAggregateRequest = {
52
+ readonly search?: string | undefined;
53
+ readonly filter?: ListFilterOption | undefined;
54
+ readonly filters?: readonly ListFilterOption[] | undefined;
55
+ readonly aggregate: ListAggregatePayload;
56
+ };
24
57
  export type EventStoreExecutor<Row extends Record<string, unknown> = Record<string, unknown>> = {
25
58
  create: (payload: Record<string, unknown>, user: SessionUser, db: TenantDb, options?: StreamTenantOption & {
26
59
  preSave?: PreSaveRunner;
@@ -70,30 +103,13 @@ export type EventStoreExecutor<Row extends Record<string, unknown> = Record<stri
70
103
  * one from ctx.searchAdapter here at runtime.
71
104
  * options.searchAdapter (build-time) wins — the runtime override
72
105
  * is the fallback for the default wrapper. */
73
- runtimeOptions?: {
74
- readonly searchAdapter?: SearchAdapter;
75
- readonly includeDeleted?: boolean;
76
- readonly referenceSearch?: {
77
- readonly fields: ReadonlyArray<{
78
- readonly fieldName: string;
79
- readonly targetEntityName: string;
80
- readonly labelField: string;
81
- }>;
82
- readonly resolveEntity: (entityName: string) => EntityDefinition | undefined;
83
- };
84
- readonly referenceSort?: {
85
- readonly fields: ReadonlyArray<{
86
- readonly fieldName: string;
87
- readonly targetEntityName: string;
88
- readonly labelField: string;
89
- }>;
90
- readonly resolveEntity: (entityName: string) => EntityDefinition | undefined;
91
- };
92
- readonly parentVisibility?: ParentVisibilityOption;
93
- }) => Promise<CursorResult<Row>>;
106
+ runtimeOptions?: ListRuntimeOptions) => Promise<CursorResult<Row>>;
107
+ /** Groups and measures over the same WHERE as `list` (filters, search, ownership, field-read gates); paging and sort are ignored. */
108
+ aggregate: (payload: ListAggregateRequest, user: SessionUser, db: TenantDb, runtimeOptions?: ListRuntimeOptions) => Promise<ListAggregateResult>;
94
109
  detail: (payload: {
95
110
  id: EntityId;
96
111
  }, user: SessionUser, db: TenantDb, options?: {
97
112
  readonly parentVisibility?: ParentVisibilityOption;
98
113
  }) => Promise<Row | null>;
99
114
  };
115
+ export {};
package/dist/fields.js CHANGED
@@ -47,7 +47,7 @@ function isFieldTypeName(name) {
47
47
  export const FIELD_TYPE_NAMES = Object.keys(FIELD_TYPE_NAME_FLAGS).filter(isFieldTypeName);
48
48
  // Field types with no bound, editable widget on the auto-wired entityEdit
49
49
  // path (render-field.tsx renders a read-only banner instead) — shared by
50
- // the boot validator (packages/framework/src/engine/boot-validator/screens.ts)
50
+ // the boot validator (packages/framework/src/engine/boot-validator/screens-forms.ts)
51
51
  // and the client-side required-field check (packages/renderer/src/app/form-schema.ts).
52
52
  // Embedded LIST fields (`multiple: true`) are the one exception — they get
53
53
  // their own EmbeddedListField grid widget — callers must check that
@@ -632,6 +632,9 @@ export type WriteHandlerDef = {
632
632
  /** Per-payload-field limits, e.g. per recipient address. Complements
633
633
  * `rateLimit`; anonymous handlers still need an ip-keyed `rateLimit`. */
634
634
  readonly additionalRateLimits?: readonly PayloadRateLimitOption[];
635
+ /** Tokens each request books on every declared rateLimit bucket, computed from the validated
636
+ * payload (e.g. `(p) => p.files.length`). Replaces the options' `cost`; results below 1 count as 1. */
637
+ readonly rateLimitCost?: (payload: unknown) => number;
635
638
  readonly escapeHatch?: EscapeHatchDeclaration;
636
639
  /** Reserves before the handler transaction opens. Reached through a nested ctx.write the
637
640
  * reservation cannot run, so the dispatcher rejects it unless the top-level batch reserved it. */
@@ -0,0 +1,38 @@
1
+ import * as z from "zod";
2
+ import type { AggregateKey } from "./aggregate-types.ts";
3
+ export declare const MAX_LIST_AGGREGATE_MEASURES = 20;
4
+ export declare const LIST_AGGREGATE_MEASURE_KINDS: readonly ["sum", "avg", "count", "min", "max"];
5
+ export type ListAggregateMeasureKind = (typeof LIST_AGGREGATE_MEASURE_KINDS)[number];
6
+ export declare const listAggregatePayloadSchema: z.ZodObject<{
7
+ groupBy: z.ZodOptional<z.ZodString>;
8
+ measures: z.ZodDefault<z.ZodArray<z.ZodObject<{
9
+ field: z.ZodString;
10
+ kind: z.ZodEnum<{
11
+ avg: "avg";
12
+ count: "count";
13
+ max: "max";
14
+ min: "min";
15
+ sum: "sum";
16
+ }>;
17
+ }, z.core.$strip>>>;
18
+ }, z.core.$strip>;
19
+ export type ListAggregatePayload = {
20
+ readonly groupBy?: string | undefined;
21
+ readonly measures: readonly {
22
+ readonly field: string;
23
+ readonly kind: ListAggregateMeasureKind;
24
+ }[];
25
+ };
26
+ export type ListAggregateValue = number | string | null;
27
+ export type ListAggregateGroup = {
28
+ readonly key: AggregateKey;
29
+ readonly label?: string | null;
30
+ readonly count: number;
31
+ readonly measures: readonly ListAggregateValue[];
32
+ };
33
+ export type ListAggregateResult = {
34
+ readonly groups: readonly ListAggregateGroup[];
35
+ readonly truncated?: true;
36
+ readonly searchTruncated?: true;
37
+ };
38
+ export declare function isListAggregateResult(value: unknown): value is ListAggregateResult;
@@ -0,0 +1,19 @@
1
+ import * as z from "zod";
2
+ import { isPlainObject } from "./type-guards.js";
3
+ export const MAX_LIST_AGGREGATE_MEASURES = 20;
4
+ export const LIST_AGGREGATE_MEASURE_KINDS = ["sum", "avg", "count", "min", "max"];
5
+ export const listAggregatePayloadSchema = z.object({
6
+ groupBy: z.string().optional(),
7
+ measures: z
8
+ .array(z.object({ field: z.string(), kind: z.enum(LIST_AGGREGATE_MEASURE_KINDS) }))
9
+ .max(MAX_LIST_AGGREGATE_MEASURES)
10
+ .default([]),
11
+ });
12
+ const LIST_AGGREGATE_RESULT_KEYS = ["groups", "truncated", "searchTruncated"];
13
+ // Closed key set: a row that merely has a `groups` field (jsonb column) must not
14
+ // be mistaken for an aggregate and skip the row-level read filter.
15
+ export function isListAggregateResult(value) {
16
+ return (isPlainObject(value) &&
17
+ Array.isArray(value["groups"]) &&
18
+ Object.keys(value).every((key) => LIST_AGGREGATE_RESULT_KEYS.includes(key)));
19
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-types",
3
- "version": "0.387.0",
3
+ "version": "0.388.0",
4
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": "MIT",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -154,6 +154,10 @@
154
154
  "types": "./dist/aggregate-types.d.ts",
155
155
  "default": "./dist/aggregate-types.js"
156
156
  },
157
+ "./list-aggregate-types": {
158
+ "types": "./dist/list-aggregate-types.d.ts",
159
+ "default": "./dist/list-aggregate-types.js"
160
+ },
157
161
  "./tenant-db-types": {
158
162
  "types": "./dist/tenant-db-types.d.ts",
159
163
  "default": "./dist/tenant-db-types.js"
@@ -281,7 +285,7 @@
281
285
  },
282
286
  "devDependencies": {
283
287
  "hono": "^4.13.1",
284
- "postgres": "npm:@bender0oo0/postgres@3.4.9-kumiko.1",
288
+ "postgres": "npm:@bender0oo0/postgres@3.4.9-kumiko.2",
285
289
  "temporal-polyfill": "^0.3.2",
286
290
  "zod": "^4.4.3",
287
291
  "ioredis": "^5.10.1"
@@ -433,6 +437,10 @@
433
437
  "types": "./dist/aggregate-types.d.ts",
434
438
  "default": "./dist/aggregate-types.js"
435
439
  },
440
+ "./list-aggregate-types": {
441
+ "types": "./dist/list-aggregate-types.d.ts",
442
+ "default": "./dist/list-aggregate-types.js"
443
+ },
436
444
  "./tenant-db-types": {
437
445
  "types": "./dist/tenant-db-types.d.ts",
438
446
  "default": "./dist/tenant-db-types.js"