@cosmicdrift/kumiko-types 0.326.1 → 0.328.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/action-icon.d.ts +3 -0
- package/dist/action-icon.js +51 -0
- package/{src/agent-exposure.ts → dist/agent-exposure.d.ts} +8 -15
- package/dist/agent-exposure.js +16 -0
- package/dist/concurrency-mode.d.ts +1 -0
- package/dist/config-scope.d.ts +1 -0
- package/dist/config-scope.js +1 -0
- package/dist/config.d.ts +234 -0
- package/dist/config.js +1 -0
- package/dist/cursor-types.d.ts +16 -0
- package/dist/cursor-types.js +1 -0
- package/dist/db-connection.d.ts +14 -0
- package/dist/db-connection.js +1 -0
- package/dist/define-handler.d.ts +58 -0
- package/dist/define-handler.js +1 -0
- package/dist/derivatives-types.d.ts +53 -0
- package/dist/derivatives-types.js +3 -0
- package/dist/entity-cache.d.ts +16 -0
- package/dist/entity-cache.js +1 -0
- package/dist/entity-handlers.d.ts +54 -0
- package/dist/entity-handlers.js +1 -0
- package/dist/entity-table-meta-types.d.ts +44 -0
- package/dist/entity-table-meta-types.js +6 -0
- package/dist/envelope-cipher-types.d.ts +5 -0
- package/dist/envelope-cipher-types.js +1 -0
- package/dist/event-store-executor-types.d.ts +86 -0
- package/dist/event-store-executor-types.js +1 -0
- package/dist/event-store-types.d.ts +37 -0
- package/dist/event-store-types.js +7 -0
- package/dist/event-type-map.d.ts +8 -0
- package/{src/event-type-map.ts → dist/event-type-map.js} +1 -16
- package/dist/executor-brand.d.ts +8 -0
- package/dist/executor-brand.js +1 -0
- package/dist/extension-options-map.d.ts +9 -0
- package/dist/extension-options-map.js +4 -0
- package/dist/feature.d.ts +518 -0
- package/dist/feature.js +1 -0
- package/dist/field-icon.d.ts +1 -0
- package/dist/field-icon.js +1 -0
- package/dist/fields.d.ts +729 -0
- package/dist/fields.js +50 -0
- package/dist/file-handle-types.d.ts +12 -0
- package/dist/file-handle-types.js +1 -0
- package/dist/file-provider-resolver-types.d.ts +3 -0
- package/dist/file-provider-resolver-types.js +1 -0
- package/dist/file-storage-provider-types.d.ts +17 -0
- package/dist/file-storage-provider-types.js +1 -0
- package/dist/geo-tz.d.ts +17 -0
- package/{src/geo-tz.ts → dist/geo-tz.js} +1 -20
- package/dist/handlers.d.ts +493 -0
- package/dist/handlers.js +49 -0
- package/dist/hook-phase.d.ts +1 -0
- package/dist/hook-phase.js +1 -0
- package/dist/hooks.d.ts +75 -0
- package/dist/hooks.js +1 -0
- package/dist/http-route.d.ts +52 -0
- package/dist/http-route.js +13 -0
- package/dist/identifiers.d.ts +8 -0
- package/{src/identifiers.ts → dist/identifiers.js} +7 -24
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/kms-adapter-types.d.ts +68 -0
- package/dist/kms-adapter-types.js +49 -0
- package/dist/logger.d.ts +7 -0
- package/dist/logger.js +1 -0
- package/dist/multi-stream-apply-context-types.d.ts +18 -0
- package/dist/multi-stream-apply-context-types.js +1 -0
- package/dist/nav-icon.d.ts +3 -0
- package/dist/nav-icon.js +100 -0
- package/dist/nav.d.ts +39 -0
- package/dist/nav.js +1 -0
- package/dist/observability/index.d.ts +3 -0
- package/dist/observability/index.js +2 -0
- package/dist/observability/metric.d.ts +34 -0
- package/dist/observability/metric.js +3 -0
- package/dist/observability/provider.d.ts +24 -0
- package/dist/observability/provider.js +4 -0
- package/dist/observability/span.d.ts +32 -0
- package/dist/observability/span.js +4 -0
- package/dist/ownership.d.ts +33 -0
- package/dist/ownership.js +5 -0
- package/dist/projection.d.ts +38 -0
- package/dist/projection.js +1 -0
- package/dist/rate-limit-types.d.ts +19 -0
- package/dist/rate-limit-types.js +1 -0
- package/dist/relations.d.ts +25 -0
- package/dist/relations.js +1 -0
- package/dist/schema-table-types.d.ts +15 -0
- package/{src/schema-table-types.ts → dist/schema-table-types.js} +0 -20
- package/dist/screen.d.ts +1265 -0
- package/dist/screen.js +3 -0
- package/dist/search-adapter.d.ts +32 -0
- package/dist/search-adapter.js +1 -0
- package/dist/secrets-types.d.ts +52 -0
- package/dist/secrets-types.js +30 -0
- package/dist/snapshot-types.d.ts +13 -0
- package/dist/snapshot-types.js +1 -0
- package/dist/step.d.ts +304 -0
- package/dist/step.js +11 -0
- package/dist/target-ref.d.ts +5 -0
- package/{src/target-ref.ts → dist/target-ref.js} +1 -6
- package/dist/tenancy-brand.d.ts +6 -0
- package/dist/tenancy-brand.js +1 -0
- package/dist/tenant-db-types.d.ts +67 -0
- package/dist/tenant-db-types.js +5 -0
- package/dist/tree-node.d.ts +26 -0
- package/dist/tree-node.js +22 -0
- package/dist/tz-context.d.ts +48 -0
- package/dist/tz-context.js +6 -0
- package/dist/websocket-route.d.ts +46 -0
- package/dist/websocket-route.js +10 -0
- package/dist/where-clause-types.d.ts +20 -0
- package/dist/where-clause-types.js +1 -0
- package/dist/workspace.d.ts +10 -0
- package/dist/workspace.js +1 -0
- package/dist/write-error-info-types.d.ts +12 -0
- package/dist/write-error-info-types.js +1 -0
- package/package.json +362 -119
- package/src/changes.json +12 -0
- package/src/__tests__/handlers.test.ts +0 -40
- package/src/__tests__/plural-forms.test.ts +0 -18
- package/src/action-icon.ts +0 -57
- package/src/concurrency-mode.ts +0 -5
- package/src/config-scope.ts +0 -5
- package/src/config.ts +0 -563
- package/src/cursor-types.ts +0 -18
- package/src/db-connection.ts +0 -25
- package/src/define-handler.ts +0 -138
- package/src/derivatives-types.ts +0 -106
- package/src/entity-cache.ts +0 -35
- package/src/entity-handlers.ts +0 -68
- package/src/entity-table-meta-types.ts +0 -101
- package/src/envelope-cipher-types.ts +0 -6
- package/src/event-store-executor-types.ts +0 -163
- package/src/event-store-types.ts +0 -85
- package/src/executor-brand.ts +0 -19
- package/src/extension-options-map.ts +0 -21
- package/src/feature.ts +0 -1132
- package/src/field-icon.ts +0 -23
- package/src/fields.ts +0 -1114
- package/src/file-handle-types.ts +0 -20
- package/src/file-provider-resolver-types.ts +0 -7
- package/src/file-storage-provider-types.ts +0 -54
- package/src/handlers.ts +0 -1246
- package/src/hook-phase.ts +0 -2
- package/src/hooks.ts +0 -170
- package/src/http-route.ts +0 -78
- package/src/kms-adapter-types.ts +0 -128
- package/src/logger.ts +0 -7
- package/src/multi-stream-apply-context-types.ts +0 -52
- package/src/nav-icon.ts +0 -105
- package/src/nav.ts +0 -139
- package/src/observability/index.ts +0 -28
- package/src/observability/metric.ts +0 -56
- package/src/observability/provider.ts +0 -32
- package/src/observability/span.ts +0 -58
- package/src/ownership.ts +0 -83
- package/src/projection.ts +0 -170
- package/src/rate-limit-types.ts +0 -32
- package/src/relations.ts +0 -52
- package/src/screen.ts +0 -1593
- package/src/search-adapter.ts +0 -45
- package/src/secrets-types.ts +0 -182
- package/src/snapshot-types.ts +0 -36
- package/src/step.ts +0 -390
- package/src/tenancy-brand.ts +0 -9
- package/src/tenant-db-types.ts +0 -116
- package/src/tree-node.ts +0 -134
- package/src/tz-context.ts +0 -63
- package/src/where-clause-types.ts +0 -31
- package/src/workspace.ts +0 -42
- package/src/write-error-info-types.ts +0 -20
- /package/{src/index.ts → dist/concurrency-mode.js} +0 -0
package/src/config.ts
DELETED
|
@@ -1,563 +0,0 @@
|
|
|
1
|
-
import type { ZodType } from "zod";
|
|
2
|
-
import type { ConcurrencyMode } from "./concurrency-mode";
|
|
3
|
-
import type { ConfigScope } from "./config-scope";
|
|
4
|
-
import type { DbConnection } from "./db-connection";
|
|
5
|
-
import type { FieldDefinition } from "./fields";
|
|
6
|
-
import type { EscapeHatchDeclaration, JobContext } from "./handlers";
|
|
7
|
-
import type {
|
|
8
|
-
PostDeleteHookFn,
|
|
9
|
-
PostSaveHookFn,
|
|
10
|
-
PreDeleteHookFn,
|
|
11
|
-
PreQueryHookFn,
|
|
12
|
-
PreSaveHookFn,
|
|
13
|
-
} from "./hooks";
|
|
14
|
-
import type { TenantId } from "./identifiers";
|
|
15
|
-
import type { NavIconKey } from "./nav-icon";
|
|
16
|
-
import type { TenantDb } from "./tenant-db-types";
|
|
17
|
-
|
|
18
|
-
// --- Config ---
|
|
19
|
-
|
|
20
|
-
export type ConfigKeyAccess = {
|
|
21
|
-
readonly read: readonly string[];
|
|
22
|
-
readonly write: readonly string[];
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export type ConfigKeyType = "text" | "number" | "boolean" | "select";
|
|
26
|
-
|
|
27
|
-
export type ConfigValue<T extends ConfigKeyType> = T extends "number"
|
|
28
|
-
? number
|
|
29
|
-
: T extends "boolean"
|
|
30
|
-
? boolean
|
|
31
|
-
: T extends "text" | "select"
|
|
32
|
-
? string
|
|
33
|
-
: never;
|
|
34
|
-
|
|
35
|
-
// Bounds for numeric config keys. Enforced as hard-reject (not silent-clamp)
|
|
36
|
-
// in set.write.ts: a tenant-admin setting a value outside [min, max] gets a
|
|
37
|
-
// 400 "out_of_bounds" — silent clamping would be a UX trap ("I entered 9999,
|
|
38
|
-
// it saved as 1000, why?"). Per-Request helpers MAY clamp — that's a
|
|
39
|
-
// different call site where the caller often can't control the exact value.
|
|
40
|
-
// Only meaningful for type="number"; boot-validator rejects on other types.
|
|
41
|
-
export type ConfigBounds = {
|
|
42
|
-
readonly min?: number;
|
|
43
|
-
readonly max?: number;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
// Ctx a `computed` key-resolver gets. Mirrors what the resolver itself has:
|
|
47
|
-
// tenantId + userId for scope-aware lookups, db for ad-hoc queries (e.g.
|
|
48
|
-
// "read the current subscription plan for this tenant"). Intentionally
|
|
49
|
-
// narrow — giving it the full AppContext would leak deps like `redis`
|
|
50
|
-
// into declaration-level code that shouldn't need them.
|
|
51
|
-
export type ConfigComputedContext = {
|
|
52
|
-
readonly tenantId: TenantId;
|
|
53
|
-
readonly userId: string;
|
|
54
|
-
readonly db: DbConnection | TenantDb;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
// Computed-value resolver. Called when no scope-row AND no app-boot-override
|
|
58
|
-
// exist for this key — sits one step above keyDef.default.
|
|
59
|
-
//
|
|
60
|
-
// Use-case: plan-based limits ("Pro tenants get maxUploadSizeMB=100"). The
|
|
61
|
-
// feature declares *how* to compute the value, the handler stays neutral:
|
|
62
|
-
// const max = await ctx.config(handle); // resolver calls computed
|
|
63
|
-
//
|
|
64
|
-
// Row wins over computed: a tenant-admin that sets a specific value
|
|
65
|
-
// overrides the plan-default. If you want "plan is a hard policy", reject
|
|
66
|
-
// set on the handler-side — don't try to invert the cascade.
|
|
67
|
-
export type ConfigComputedFn<T extends ConfigKeyType = ConfigKeyType> = (
|
|
68
|
-
ctx: ConfigComputedContext,
|
|
69
|
-
) => Promise<ConfigValue<T>>;
|
|
70
|
-
|
|
71
|
-
// Storage-Backing eines provisionierten Config-Keys. "config" (Default) =
|
|
72
|
-
// config_values-Projektion mit voller Cascade (user→tenant→system→app→default).
|
|
73
|
-
// "secrets" = read_tenant_secrets (flach pro (tenant,key), AES-GCM-Envelope mit
|
|
74
|
-
// Rotation/Audit, KEINE Cascade) — nur sinnvoll für scope:system ohne
|
|
75
|
-
// Tenant-Override. Die backing×scope-Matrix erzwingt der boot-validator.
|
|
76
|
-
export type ConfigBacking = "config" | "secrets";
|
|
77
|
-
|
|
78
|
-
// Minimal read surface the config resolver needs to dispatch a
|
|
79
|
-
// backing="secrets" key to the secrets store, without coupling the engine
|
|
80
|
-
// types to the full SecretsContext. The app's `ctx.secrets` (a SecretsContext)
|
|
81
|
-
// is structurally assignable. Threaded per-call (not at resolver construction)
|
|
82
|
-
// because the resolver is framework-auto-created while `ctx.secrets` is
|
|
83
|
-
// app-provided — only the request context sees both.
|
|
84
|
-
export type ConfigSecretsReader = {
|
|
85
|
-
get(tenantId: TenantId, key: string): Promise<{ readonly reveal: () => string } | undefined>;
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
export type ConfigKeyDefinition<T extends ConfigKeyType = ConfigKeyType> = {
|
|
89
|
-
readonly type: T;
|
|
90
|
-
readonly default?: ConfigValue<T>;
|
|
91
|
-
readonly scope: ConfigScope;
|
|
92
|
-
readonly access: ConfigKeyAccess;
|
|
93
|
-
readonly encrypted?: boolean;
|
|
94
|
-
/** User/admin may legitimately see the value — unlike `encrypted`
|
|
95
|
-
* (shared master-key cipher), this is the subject-KMS: the value is
|
|
96
|
-
* encrypted under the DEK of the scope actually written to (tenant-row
|
|
97
|
-
* → tenant subject, user-row → user subject). Only on `type: "text"`,
|
|
98
|
-
* `scope !== "system"` (no subject there), and mutually exclusive with
|
|
99
|
-
* `encrypted` (kumiko-platform#231/#459). */
|
|
100
|
-
readonly piiEncrypted?: boolean;
|
|
101
|
-
readonly options?: readonly string[];
|
|
102
|
-
readonly bounds?: ConfigBounds;
|
|
103
|
-
// Per-key string-pattern validation for type="text". The value must match
|
|
104
|
-
// the regex at write time — set.write hard-rejects a mismatch with
|
|
105
|
-
// ValidationError("invalid_format"), same posture as bounds. Stored as a
|
|
106
|
-
// serializable {regex, flags} pair (not a RegExp/predicate) so it survives
|
|
107
|
-
// JSON like `bounds`/`options` (feature-manifest, docgen) and compiles per
|
|
108
|
-
// write via new RegExp. Keep patterns anchored + length-bounded: the value
|
|
109
|
-
// is tenant-supplied (untrusted), an unbounded catastrophic-backtracking
|
|
110
|
-
// regex applied to it would be a ReDoS vector.
|
|
111
|
-
readonly pattern?: { readonly regex: string; readonly flags?: string };
|
|
112
|
-
readonly computed?: ConfigComputedFn<T>;
|
|
113
|
-
// Per-Request opt-in. Default false — resolveConfigOrParam wirft für
|
|
114
|
-
// Keys ohne diese Marke, auch wenn der Caller paramValue übergibt. Das
|
|
115
|
-
// zwingt Feature-Devs zur expliziten Entscheidung "dieser Key darf pro
|
|
116
|
-
// Request überschrieben werden" — statt versehentlich zu erlauben dass
|
|
117
|
-
// ein Query-Param jede beliebige Tenant-Config umgeht.
|
|
118
|
-
//
|
|
119
|
-
// Nicht kombinierbar mit type="text" (Boot-Reject) — Text-Werte sind
|
|
120
|
-
// immer gesperrt wegen XSS/SQL/Shell-Risiko, selbst mit Opt-in.
|
|
121
|
-
// Nicht kombinierbar mit encrypted (Boot-Reject) — encrypted Keys
|
|
122
|
-
// werden nicht transient aus Query-Strings heraus gelesen.
|
|
123
|
-
readonly allowPerRequest?: boolean;
|
|
124
|
-
// Tenant must supply a real value before the owning feature works — for
|
|
125
|
-
// text keys an empty/whitespace value counts as unset. Surfaced by
|
|
126
|
-
// config:query:readiness; keep in sync with the feature's requireNonEmpty
|
|
127
|
-
// calls in its build-fn.
|
|
128
|
-
readonly required?: boolean;
|
|
129
|
-
|
|
130
|
-
// --- Provisioning-Metadata (optional auf createTenant/System/UserConfig) ---
|
|
131
|
-
// ENV-Var-Name, dessen Wert beim Boot als app-override-Default dieses Keys
|
|
132
|
-
// gebrückt wird. Reiner Fallback — überschreibt keinen gesetzten Row.
|
|
133
|
-
readonly env?: string;
|
|
134
|
-
// false → der geerbte system-row-Wert wird für Tenant-Admins redigiert
|
|
135
|
-
// (cascade.query + values.query): der Tenant sieht weder den Wert noch dass
|
|
136
|
-
// er gesetzt ist, nur den eigenen Override. Greift quell-basiert auf jeden
|
|
137
|
-
// Wert aus der system-row — nicht scope-gebunden; typischer Fall ist ein
|
|
138
|
-
// scope:tenant Key, dessen Plattform-Default in der system-row liegt (SMTP-
|
|
139
|
-
// Creds). Default true = transparente Cascade.
|
|
140
|
-
readonly inheritedToTenant?: boolean;
|
|
141
|
-
// "config" (Default, volle Cascade) oder "secrets" (flach pro (tenant,key)).
|
|
142
|
-
readonly backing?: ConfigBacking;
|
|
143
|
-
// Markiert den Key als user-facing Einstellung: der Self-Populating
|
|
144
|
-
// Settings-Hub leitet daraus automatisch Screen+Nav-Eintrag ab (kein
|
|
145
|
-
// manuelles r.screen/r.nav). Fehlt `mask`, gilt der Key als internes
|
|
146
|
-
// Plumbing (ENV-provisioniert/computed) und erscheint NICHT im Hub.
|
|
147
|
-
readonly mask?: ConfigMask;
|
|
148
|
-
// Überschreibt, unter welchem Settings-Hub-Namespace/Screen dieser Key
|
|
149
|
-
// gruppiert wird (Default: das deklarierende Feature). Erlaubt einem
|
|
150
|
-
// Feature, seine Keys unter einem fremden oder geteilten Namespace zu
|
|
151
|
-
// bündeln (z.B. viele flache Migrations-Flags unter "tenant-settings"),
|
|
152
|
-
// ohne dass das Ziel-Feature sie kennt. Rührt NICHT an qualifiziertem
|
|
153
|
-
// Namen, Storage, Seeds oder App-Overrides — nur reine UI-Gruppierung.
|
|
154
|
-
// Muss kebab-case sein (Boot-Validierung).
|
|
155
|
-
readonly group?: string;
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
// Label carrier for the settings hub. `title` is an i18n key (no literal —
|
|
159
|
-
// guarded), `icon` a NavIconKey for the nav entry, `order` the sort weight
|
|
160
|
-
// within its audience group.
|
|
161
|
-
export type ConfigMask = {
|
|
162
|
-
readonly title: string;
|
|
163
|
-
readonly icon?: NavIconKey;
|
|
164
|
-
readonly order?: number;
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
export type ConfigDefinition = {
|
|
168
|
-
readonly keys: Readonly<Record<string, ConfigKeyDefinition>>;
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
export type ConfigKeyHandle<T extends ConfigKeyType = ConfigKeyType> = {
|
|
172
|
-
readonly name: string;
|
|
173
|
-
readonly type: T;
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
export type ConfigAccessor = {
|
|
177
|
-
(qualifiedKey: string): Promise<string | number | boolean | undefined>;
|
|
178
|
-
<T extends ConfigKeyType>(handle: ConfigKeyHandle<T>): Promise<ConfigValue<T> | undefined>;
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
export type ConfigAccessorFactory = (deps: {
|
|
182
|
-
readonly user: { readonly id: string; readonly tenantId: TenantId };
|
|
183
|
-
readonly db: DbConnection | TenantDb;
|
|
184
|
-
// Present when the app wired `extraContext.secrets`. Lets the internal
|
|
185
|
-
// `ctx.config.get` read a backing="secrets" key transparently from the
|
|
186
|
-
// secrets store; absent → a backing="secrets" read throws loud.
|
|
187
|
-
readonly secrets?: ConfigSecretsReader;
|
|
188
|
-
}) => ConfigAccessor;
|
|
189
|
-
|
|
190
|
-
// Row shape returned by ConfigResolver.getAll — just enough for the
|
|
191
|
-
// values.query handler to project. Stored as `unknown` value because the
|
|
192
|
-
// resolver hands raw JSON strings; deserialization is the resolver's job.
|
|
193
|
-
//
|
|
194
|
-
// Post-ES the config_values projection PK is a UUID (event-store aggregate
|
|
195
|
-
// id) and tenantId is non-null (system-scope rows carry SYSTEM_TENANT_ID).
|
|
196
|
-
// The shape stays backward-compatible for read callers: they only touch
|
|
197
|
-
// `value` and `key`.
|
|
198
|
-
export type ConfigStoredRow = {
|
|
199
|
-
readonly id: string;
|
|
200
|
-
readonly key: string;
|
|
201
|
-
readonly value: string | null;
|
|
202
|
-
readonly tenantId: string;
|
|
203
|
-
readonly userId: string | null;
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
// Extended row with resolution source so the UI can display where each
|
|
207
|
-
// value came from.
|
|
208
|
-
export type ConfigStoredRowWithSource = ConfigStoredRow & {
|
|
209
|
-
readonly source: ConfigValueSource;
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
// Which layer of the cascade actually produced a value. Emitted only by
|
|
213
|
-
// `getWithSource` — regular `get` hides this to keep the hot-path simple.
|
|
214
|
-
// Use-case: Ops-debugging ("warum ist mein Wert 50 und nicht 100?") without
|
|
215
|
-
// poking through six scope-row-lookups by hand.
|
|
216
|
-
export type ConfigValueSource =
|
|
217
|
-
| "user-row" // user-scoped row (only for scope:user keys)
|
|
218
|
-
| "tenant-row" // tenant-scoped row
|
|
219
|
-
| "system-row" // system-scoped row (tenantId = SYSTEM_TENANT_ID, userId = null)
|
|
220
|
-
| "app-override" // from createConfigResolver({ appOverrides })
|
|
221
|
-
| "computed" // computed resolver in the key declaration
|
|
222
|
-
| "default" // keyDef.default
|
|
223
|
-
| "missing"; // no row, no override, no computed, no default
|
|
224
|
-
|
|
225
|
-
export type ConfigValueWithSource = {
|
|
226
|
-
readonly value: string | number | boolean | undefined;
|
|
227
|
-
readonly source: ConfigValueSource;
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
/// Full cascade for a single config key — every level the resolver
|
|
231
|
-
/// walks through, with the winning level marked.
|
|
232
|
-
export type ConfigCascadeLevel = {
|
|
233
|
-
readonly label: string;
|
|
234
|
-
readonly value: string | number | boolean | undefined;
|
|
235
|
-
readonly source: ConfigValueSource;
|
|
236
|
-
readonly isActive: boolean;
|
|
237
|
-
readonly hasValue: boolean;
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
export type ConfigCascade = {
|
|
241
|
-
readonly value: string | number | boolean | undefined;
|
|
242
|
-
readonly source: ConfigValueSource;
|
|
243
|
-
readonly levels: readonly ConfigCascadeLevel[];
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
// Minimal contract handlers (set/reset/values.query) call against the
|
|
247
|
-
// resolver. Lives in the framework so SharedContextFields.configResolver
|
|
248
|
-
// can drop the `unknown` cast — the concrete implementation in
|
|
249
|
-
// bundled-features/config/resolver.ts implements this shape.
|
|
250
|
-
// Read-only contract: writes flow through the config feature's
|
|
251
|
-
// write-handlers (set / reset), which append events + let the event-store-
|
|
252
|
-
// executor materialise the projection. The resolver is purely a read
|
|
253
|
-
// cascade (user → tenant → system → app-override → computed → default).
|
|
254
|
-
export type ConfigResolver = {
|
|
255
|
-
get(
|
|
256
|
-
qualifiedKey: string,
|
|
257
|
-
keyDef: ConfigKeyDefinition,
|
|
258
|
-
tenantId: TenantId,
|
|
259
|
-
userId: string,
|
|
260
|
-
db: DbConnection | TenantDb,
|
|
261
|
-
secretsReader?: ConfigSecretsReader,
|
|
262
|
-
): Promise<string | number | boolean | undefined>;
|
|
263
|
-
|
|
264
|
-
// Same cascade as get() but also reports which layer produced the value.
|
|
265
|
-
// Intended for Ops/Support tooling — never call this from hot-path
|
|
266
|
-
// handlers (it builds the source tag even when the caller doesn't look
|
|
267
|
-
// at it). Row-lookup count is identical to get(); the extra work is a
|
|
268
|
-
// small branch tag.
|
|
269
|
-
getWithSource(
|
|
270
|
-
qualifiedKey: string,
|
|
271
|
-
keyDef: ConfigKeyDefinition,
|
|
272
|
-
tenantId: TenantId,
|
|
273
|
-
userId: string,
|
|
274
|
-
db: DbConnection | TenantDb,
|
|
275
|
-
secretsReader?: ConfigSecretsReader,
|
|
276
|
-
): Promise<ConfigValueWithSource>;
|
|
277
|
-
|
|
278
|
-
getAll(
|
|
279
|
-
tenantId: TenantId,
|
|
280
|
-
userId: string,
|
|
281
|
-
db: DbConnection | TenantDb,
|
|
282
|
-
): Promise<ReadonlyMap<string, ConfigStoredRow>>;
|
|
283
|
-
|
|
284
|
-
// Returns ALL cascade levels for a single key — not just the winner.
|
|
285
|
-
// Each level shows its value (or undefined if not set) and whether it
|
|
286
|
-
// is the active/winning level. Levels are ordered by specificity
|
|
287
|
-
// descending (most specific first).
|
|
288
|
-
getCascade(
|
|
289
|
-
qualifiedKey: string,
|
|
290
|
-
keyDef: ConfigKeyDefinition,
|
|
291
|
-
tenantId: TenantId,
|
|
292
|
-
userId: string,
|
|
293
|
-
db: DbConnection | TenantDb,
|
|
294
|
-
secretsReader?: ConfigSecretsReader,
|
|
295
|
-
): Promise<ConfigCascade>;
|
|
296
|
-
|
|
297
|
-
// Batch variant: resolves cascades for N keys in one DB round-trip.
|
|
298
|
-
// keyDefs must contain definitions for every key in the keys array.
|
|
299
|
-
// Returns a map of qualifiedKey → ConfigCascade. backing="secrets" keys
|
|
300
|
-
// resolve their system rung from the secrets store via secretsReader
|
|
301
|
-
// (one read each — they are system-only and rare).
|
|
302
|
-
getCascadeBatch(
|
|
303
|
-
keys: readonly string[],
|
|
304
|
-
keyDefs: ReadonlyMap<string, ConfigKeyDefinition>,
|
|
305
|
-
tenantId: TenantId,
|
|
306
|
-
userId: string,
|
|
307
|
-
db: DbConnection | TenantDb,
|
|
308
|
-
secretsReader?: ConfigSecretsReader,
|
|
309
|
-
): Promise<ReadonlyMap<string, ConfigCascade>>;
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
// --- Process-Placement (runIn) ---
|
|
313
|
-
|
|
314
|
-
// Which deploy-shape a consumer / job is allowed to run in. Filtered at
|
|
315
|
-
// entrypoint boot: createApiEntrypoint picks up "api"|"both", createWorker
|
|
316
|
-
// Entrypoint picks up "worker"|"both", createAllInOneEntrypoint takes
|
|
317
|
-
// everything. Default is "worker" for every async consumer/job — that's the
|
|
318
|
-
// sensible prod default (API instances stay request-focused, heavy async
|
|
319
|
-
// work lives on the worker fleet). Opt into "api" only for latency-
|
|
320
|
-
// sensitive or in-memory-stateful consumers (e.g. later: SSE per-instance
|
|
321
|
-
// push in Welle 2.7).
|
|
322
|
-
//
|
|
323
|
-
// Feature-hooks (r.hook preSave/postSave/…) intentionally have no runIn —
|
|
324
|
-
// they run in-TX in whatever process handles the command. Splitting them
|
|
325
|
-
// would break atomicity. If you want async work, use r.job or
|
|
326
|
-
// r.multiStreamProjection.
|
|
327
|
-
export type RunIn = "api" | "worker" | "both";
|
|
328
|
-
|
|
329
|
-
// Jobs are queue-delivered via BullMQ with one dedicated queue per lane
|
|
330
|
-
// ("kumiko-jobs-api" vs "kumiko-jobs-worker") and one dedicated event-
|
|
331
|
-
// enqueuer consumer per lane. "both" would mean "dispatch to both queues",
|
|
332
|
-
// which over-delivers the job; the Marten-style cursor/queue fan-out is not
|
|
333
|
-
// free. Restrict at the type level so the boot-validator never has to
|
|
334
|
-
// report it.
|
|
335
|
-
export type JobRunIn = Exclude<RunIn, "both">;
|
|
336
|
-
|
|
337
|
-
// --- Jobs ---
|
|
338
|
-
|
|
339
|
-
export type JobHandlerFn = (payload: Record<string, unknown>, context: JobContext) => Promise<void>;
|
|
340
|
-
|
|
341
|
-
export type JobTrigger =
|
|
342
|
-
// `on` akzeptiert ein einzelnes Handler-Ref ODER eine Liste. Multi-
|
|
343
|
-
// Trigger-Form ist DRY für Fanout-Patterns: ein Job-Body, mehrere
|
|
344
|
-
// Trigger (z.B. webhook-fanout: incident.open / incident.update /
|
|
345
|
-
// maintenance.start) statt N r.job-Calls mit demselben Handler-Body.
|
|
346
|
-
// Im Handler-payload landet `_triggerName: string` damit der Code
|
|
347
|
-
// weiß, welcher Trigger gefeuert hat.
|
|
348
|
-
//
|
|
349
|
-
// Two delivery semantics depending on what `on` resolves to (kumiko-
|
|
350
|
-
// framework#1505): a write/query-handler QN dispatches synchronously
|
|
351
|
-
// from dispatch-write.ts's afterCommitHooks — effectively once. An
|
|
352
|
-
// r.defineEvent QN (e.g. one an r.multiStreamProjection appends via
|
|
353
|
-
// ctx.unsafeAppendEvent) dispatches async via the job-trigger
|
|
354
|
-
// event-consumer (createJobTriggerEventConsumer, pipeline/system-
|
|
355
|
-
// hooks.ts) — cursor delivery, at-least-once. Handlers triggered on an
|
|
356
|
-
// r.defineEvent QN must be idempotent.
|
|
357
|
-
| {
|
|
358
|
-
readonly on: import("./handlers").NameOrRef | readonly import("./handlers").NameOrRef[];
|
|
359
|
-
// Equality filter on top-level payload fields, checked before enqueue —
|
|
360
|
-
// lets N jobs share one broad event QN, partitioned by a discriminant.
|
|
361
|
-
readonly where?: Readonly<Record<string, string | number | boolean>>;
|
|
362
|
-
}
|
|
363
|
-
| { readonly cron: string }
|
|
364
|
-
| { readonly manual: true };
|
|
365
|
-
|
|
366
|
-
export type JobBackoffStrategy = "fixed" | "exponential";
|
|
367
|
-
// String form uses the framework's default base delay; the object form's
|
|
368
|
-
// `delayMs` is the base BullMQ multiplies: "fixed" keeps it constant,
|
|
369
|
-
// "exponential" scales it delayMs * 2^(attemptsMade-1) per retry.
|
|
370
|
-
export type JobBackoff =
|
|
371
|
-
| JobBackoffStrategy
|
|
372
|
-
| { readonly type: JobBackoffStrategy; readonly delayMs?: number | undefined };
|
|
373
|
-
|
|
374
|
-
export type JobDefinition = {
|
|
375
|
-
readonly name: string;
|
|
376
|
-
readonly handler: JobHandlerFn;
|
|
377
|
-
readonly trigger: JobTrigger;
|
|
378
|
-
// "sequential" is mutual exclusion, not a FIFO queue: same-name dispatches
|
|
379
|
-
// never run concurrently, but a loser of the per-name lock is re-enqueued
|
|
380
|
-
// to the back of its queue, so a job dispatched later can still complete
|
|
381
|
-
// before one dispatched earlier (fw#3265). Don't rely on dispatch order.
|
|
382
|
-
readonly concurrency?: ConcurrencyMode | undefined;
|
|
383
|
-
readonly maxPerTenant?: number | undefined;
|
|
384
|
-
readonly debounceMs?: number | undefined;
|
|
385
|
-
readonly retries?: number | undefined;
|
|
386
|
-
readonly backoff?: JobBackoff | undefined;
|
|
387
|
-
readonly timeout?: number | undefined;
|
|
388
|
-
readonly schema?: ZodType | undefined;
|
|
389
|
-
// Enqueue this job once when a runner for its lane starts. Fire-and-forget:
|
|
390
|
-
// the enqueue is awaited, the run is not, so a throwing handler only fails
|
|
391
|
-
// the queue job — the process keeps booting and reports ready.
|
|
392
|
-
// Deduped via a persistent per-queue marker set at enqueue time (not the
|
|
393
|
-
// fixed job id alone, which BullMQ's bounded retention can evict), so it
|
|
394
|
-
// runs at most once per Redis dataset: a boot job that already ran (or
|
|
395
|
-
// failed) is not retried on a later deploy.
|
|
396
|
-
// Never a deploy gate — use `bootGate` for that.
|
|
397
|
-
readonly runOnBoot?: boolean | undefined;
|
|
398
|
-
// Run this job inline while the runner for its lane starts, before cron
|
|
399
|
-
// schedules and `runOnBoot` enqueues. The handler is awaited and a throw
|
|
400
|
-
// rejects the runner's start() — which rejects runProdApp's boot, so the
|
|
401
|
-
// process exits before it ever reports ready. That makes it a real deploy
|
|
402
|
-
// gate, unlike `runOnBoot`. Runs on every start, never deduped.
|
|
403
|
-
// `retries`/`timeout`/`backoff` are BullMQ options and do not apply on the
|
|
404
|
-
// inline path. Incompatible with `perTenant` and `concurrency: "sequential"`.
|
|
405
|
-
// The gate runs wherever a job runner for its lane starts — a setup that
|
|
406
|
-
// starts no runner for that lane runs no gate.
|
|
407
|
-
readonly bootGate?: boolean | undefined;
|
|
408
|
-
readonly perTenant?: boolean | undefined;
|
|
409
|
-
// Which deploy-lane runs this job. Default "worker". Set "api" only for
|
|
410
|
-
// short CPU-light handlers (token cleanup, in-process cache warmup) that
|
|
411
|
-
// don't justify a separate worker container — long/CPU-heavy jobs on the
|
|
412
|
-
// API lane will starve request handlers.
|
|
413
|
-
readonly runIn?: JobRunIn | undefined;
|
|
414
|
-
// Grants `ctx.db.unsafeRaw(reason)` for this job, audited as `unsafe-raw`.
|
|
415
|
-
readonly escapeHatch?: EscapeHatchDeclaration | undefined;
|
|
416
|
-
// Opt in to a tenant-visible failure record (`jobs:query:failures`). Only a
|
|
417
|
-
// translation key ever reaches the tenant: a thrown KumikoError's `i18nKey`
|
|
418
|
-
// when it carries one, otherwise `messageKey`. The provider's own message
|
|
419
|
-
// stays in the run log. Only the last attempt records, and a later
|
|
420
|
-
// successful run of the same job and subject clears the record.
|
|
421
|
-
// `subjectFields` names payload fields that scope the record (e.g.
|
|
422
|
-
// `["campaignId", "language"]`); each must hold a primitive or the run
|
|
423
|
-
// throws. Omit it for one record per job and tenant. Their values are
|
|
424
|
-
// stored in clear, unlike the encrypted run payload — never name a PII
|
|
425
|
-
// field here.
|
|
426
|
-
readonly tenantVisibleFailure?:
|
|
427
|
-
| {
|
|
428
|
-
readonly messageKey: string;
|
|
429
|
-
readonly subjectFields?: readonly string[] | undefined;
|
|
430
|
-
}
|
|
431
|
-
| undefined;
|
|
432
|
-
};
|
|
433
|
-
|
|
434
|
-
// --- Notifications ---
|
|
435
|
-
|
|
436
|
-
export type NotificationRecipientFn = (
|
|
437
|
-
result: import("./hooks").SaveContext,
|
|
438
|
-
) => string | readonly string[] | { readonly tenant: string } | null;
|
|
439
|
-
|
|
440
|
-
export type NotificationDataFn = (result: import("./hooks").SaveContext) => Record<string, unknown>;
|
|
441
|
-
|
|
442
|
-
// Per-channel template function: transforms raw notification data into channel-specific format.
|
|
443
|
-
// Example: inApp gets { title, body }, email gets { subject, sections }.
|
|
444
|
-
export type NotificationTemplateFn = (data: Record<string, unknown>) => Record<string, unknown>;
|
|
445
|
-
|
|
446
|
-
export type NotificationDefinition = {
|
|
447
|
-
readonly name: string;
|
|
448
|
-
readonly trigger: { readonly on: string };
|
|
449
|
-
readonly recipient: NotificationRecipientFn;
|
|
450
|
-
readonly data: NotificationDataFn;
|
|
451
|
-
readonly templates: Readonly<Record<string, NotificationTemplateFn>> | undefined;
|
|
452
|
-
};
|
|
453
|
-
|
|
454
|
-
// --- Translations ---
|
|
455
|
-
|
|
456
|
-
// CLDR plural categories for one locale's variants of a translation string.
|
|
457
|
-
// `other` is the CLDR-mandated fallback and therefore required; the rest
|
|
458
|
-
// only exist in some locales (e.g. "few"/"many" for Slavic plurals).
|
|
459
|
-
export type PluralForms = {
|
|
460
|
-
readonly zero?: string;
|
|
461
|
-
readonly one?: string;
|
|
462
|
-
readonly two?: string;
|
|
463
|
-
readonly few?: string;
|
|
464
|
-
readonly many?: string;
|
|
465
|
-
readonly other: string;
|
|
466
|
-
};
|
|
467
|
-
export type TranslationValue = string | PluralForms;
|
|
468
|
-
export type TranslationEntry = Readonly<Record<string, TranslationValue>>;
|
|
469
|
-
export type TranslationKeys = Readonly<Record<string, TranslationEntry>>;
|
|
470
|
-
|
|
471
|
-
export type TranslationsDef = {
|
|
472
|
-
readonly keys: TranslationKeys;
|
|
473
|
-
};
|
|
474
|
-
|
|
475
|
-
// --- Registrar Extensions ---
|
|
476
|
-
|
|
477
|
-
export type RegistrarExtensionHooks = {
|
|
478
|
-
readonly preSave?: PreSaveHookFn;
|
|
479
|
-
readonly postSave?: PostSaveHookFn;
|
|
480
|
-
readonly preDelete?: PreDeleteHookFn;
|
|
481
|
-
readonly postDelete?: PostDeleteHookFn;
|
|
482
|
-
readonly preQuery?: PreQueryHookFn;
|
|
483
|
-
};
|
|
484
|
-
|
|
485
|
-
export type UiExtensionDef = {
|
|
486
|
-
readonly editSection?: string;
|
|
487
|
-
readonly listColumns?: string;
|
|
488
|
-
readonly filters?: string;
|
|
489
|
-
};
|
|
490
|
-
|
|
491
|
-
export type RegistrarExtensionDef = {
|
|
492
|
-
readonly onRegister?: (entityName: string, options?: Record<string, unknown>) => void;
|
|
493
|
-
readonly extendSchema?: (entityName: string) => Record<string, FieldDefinition>;
|
|
494
|
-
readonly hooks?: RegistrarExtensionHooks;
|
|
495
|
-
readonly extendSearch?: (entityName: string) => Record<string, unknown>;
|
|
496
|
-
readonly uiExtension?: UiExtensionDef;
|
|
497
|
-
};
|
|
498
|
-
|
|
499
|
-
export type RegistrarExtensionRegistration = {
|
|
500
|
-
readonly extensionName: string;
|
|
501
|
-
readonly entityName: string;
|
|
502
|
-
readonly options?: Record<string, unknown> | undefined;
|
|
503
|
-
// Owning feature — annotated by the registry at merge time so consumers
|
|
504
|
-
// (readiness gating) can map a registration back to the feature's keys.
|
|
505
|
-
readonly featureName?: string;
|
|
506
|
-
};
|
|
507
|
-
|
|
508
|
-
// Declared by the extension-point-owning foundation via r.extensionSelector:
|
|
509
|
-
// "which provider under <extensionName> is active is chosen by <qualifiedKey>".
|
|
510
|
-
// Readiness counts a provider-feature's required keys only when selected.
|
|
511
|
-
export type ExtensionSelectorDef = {
|
|
512
|
-
readonly extensionName: string;
|
|
513
|
-
readonly qualifiedKey: string;
|
|
514
|
-
};
|
|
515
|
-
|
|
516
|
-
// --- Reference Data ---
|
|
517
|
-
|
|
518
|
-
export type ReferenceDataDef = {
|
|
519
|
-
readonly entityName: string;
|
|
520
|
-
readonly data: readonly Record<string, unknown>[];
|
|
521
|
-
readonly upsertKey?: string | undefined;
|
|
522
|
-
};
|
|
523
|
-
|
|
524
|
-
// --- Config Seeding ---
|
|
525
|
-
|
|
526
|
-
// A deploy-time default for a config key, written via the event-store
|
|
527
|
-
// executor at boot. Idempotent — if the stream already exists the executor
|
|
528
|
-
// returns version_conflict and seedConfigValues counts it as skipped.
|
|
529
|
-
// See config-seeding.md.
|
|
530
|
-
//
|
|
531
|
-
// `scope` is optional on the factory-output: createSeed leaves it unset
|
|
532
|
-
// (define-feature derives it from keyDef.scope). createSystemSeed /
|
|
533
|
-
// createTenantSeed / createUserSeed always set it explicitly.
|
|
534
|
-
//
|
|
535
|
-
// `tenantId` / `userId` semantics:
|
|
536
|
-
// - system scope: both stay undefined (row stored under SYSTEM_TENANT_ID).
|
|
537
|
-
// - tenant scope: tenantId optional (undefined → fallback row under
|
|
538
|
-
// SYSTEM_TENANT_ID, visible to all tenants via resolver cascade).
|
|
539
|
-
// - user scope: BOTH tenantId AND userId required, otherwise the resolver
|
|
540
|
-
// can never match the row (user-scope cascade looks up the user's actual
|
|
541
|
-
// tenantId, not SYSTEM_TENANT_ID).
|
|
542
|
-
export type ConfigSeedDef = {
|
|
543
|
-
readonly key: string; // fully-qualified config key name (set by define-feature)
|
|
544
|
-
readonly value: string | number | boolean;
|
|
545
|
-
readonly scope?: ConfigScope;
|
|
546
|
-
readonly tenantId?: string;
|
|
547
|
-
readonly userId?: string;
|
|
548
|
-
};
|
|
549
|
-
|
|
550
|
-
// Factory types for ergonomic seed creation in r.config({ seeds }).
|
|
551
|
-
|
|
552
|
-
export type CreateSeedOptions = {
|
|
553
|
-
readonly value: string | number | boolean;
|
|
554
|
-
};
|
|
555
|
-
|
|
556
|
-
export type CreateTenantSeedOptions = {
|
|
557
|
-
readonly tenantId?: string;
|
|
558
|
-
};
|
|
559
|
-
|
|
560
|
-
export type CreateUserSeedOptions = {
|
|
561
|
-
readonly tenantId: string;
|
|
562
|
-
readonly userId: string;
|
|
563
|
-
};
|
package/src/cursor-types.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { EntityId, TenantId } from "./identifiers";
|
|
2
|
-
|
|
3
|
-
export type CursorQueryOptions = {
|
|
4
|
-
tenantId: TenantId;
|
|
5
|
-
cursor?: string;
|
|
6
|
-
limit?: number;
|
|
7
|
-
filterIds?: readonly EntityId[];
|
|
8
|
-
sort?: string;
|
|
9
|
-
sortDirection?: "asc" | "desc";
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export type CursorResult<T> = {
|
|
13
|
-
rows: T[];
|
|
14
|
-
nextCursor: string | null;
|
|
15
|
-
/** Optional total row count — only present when the caller sets
|
|
16
|
-
* `totalCount: true` on the query. */
|
|
17
|
-
total?: number;
|
|
18
|
-
};
|
package/src/db-connection.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type postgres from "postgres";
|
|
2
|
-
|
|
3
|
-
// Minimal structural surface both postgres-js and Bun.SQL satisfy for
|
|
4
|
-
// asRawClient() / query helpers. Avoid `| any` — TS unions with `any`
|
|
5
|
-
// collapse to `any` and erase the postgres side entirely.
|
|
6
|
-
export type RawDbClient = {
|
|
7
|
-
// Signatures are intentionally loose: postgres-js uses (string, values?),
|
|
8
|
-
// Bun.SQL uses tagged templates / unsafe(string). Call sites go through
|
|
9
|
-
// asRawClient() which normalizes at runtime.
|
|
10
|
-
// biome-ignore lint/suspicious/noExplicitAny: cross-provider unsafe arity
|
|
11
|
-
unsafe: (...args: any[]) => any;
|
|
12
|
-
// biome-ignore lint/suspicious/noExplicitAny: cross-provider begin arity
|
|
13
|
-
begin: (...args: any[]) => any;
|
|
14
|
-
end?: (options?: { timeout?: number }) => Promise<void>;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
// Raw SQL client (postgres-js instance or Bun.SQL). Distinct from the
|
|
18
|
-
// framework's structural pool handle (`DbPoolHandle` in db/api.ts).
|
|
19
|
-
export type DbConnection = ReturnType<typeof postgres> | RawDbClient;
|
|
20
|
-
export type DbTx = postgres.TransactionSql<Record<string, unknown>> | RawDbClient;
|
|
21
|
-
export type DbRunner = DbConnection | DbTx;
|
|
22
|
-
export type DbRow = Record<string, unknown>;
|
|
23
|
-
export type PgClient = ReturnType<typeof postgres>;
|
|
24
|
-
|
|
25
|
-
export type PgListenClient = ReturnType<typeof postgres>;
|