@cosmicdrift/kumiko-types 0.327.0 → 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.
Files changed (174) hide show
  1. package/dist/action-icon.d.ts +3 -0
  2. package/dist/action-icon.js +51 -0
  3. package/{src/agent-exposure.ts → dist/agent-exposure.d.ts} +8 -15
  4. package/dist/agent-exposure.js +16 -0
  5. package/dist/concurrency-mode.d.ts +1 -0
  6. package/dist/config-scope.d.ts +1 -0
  7. package/dist/config-scope.js +1 -0
  8. package/dist/config.d.ts +234 -0
  9. package/dist/config.js +1 -0
  10. package/dist/cursor-types.d.ts +16 -0
  11. package/dist/cursor-types.js +1 -0
  12. package/dist/db-connection.d.ts +14 -0
  13. package/dist/db-connection.js +1 -0
  14. package/dist/define-handler.d.ts +58 -0
  15. package/dist/define-handler.js +1 -0
  16. package/dist/derivatives-types.d.ts +53 -0
  17. package/dist/derivatives-types.js +3 -0
  18. package/dist/entity-cache.d.ts +16 -0
  19. package/dist/entity-cache.js +1 -0
  20. package/dist/entity-handlers.d.ts +54 -0
  21. package/dist/entity-handlers.js +1 -0
  22. package/dist/entity-table-meta-types.d.ts +44 -0
  23. package/dist/entity-table-meta-types.js +6 -0
  24. package/dist/envelope-cipher-types.d.ts +5 -0
  25. package/dist/envelope-cipher-types.js +1 -0
  26. package/dist/event-store-executor-types.d.ts +86 -0
  27. package/dist/event-store-executor-types.js +1 -0
  28. package/dist/event-store-types.d.ts +37 -0
  29. package/dist/event-store-types.js +7 -0
  30. package/dist/event-type-map.d.ts +8 -0
  31. package/{src/event-type-map.ts → dist/event-type-map.js} +1 -16
  32. package/dist/executor-brand.d.ts +8 -0
  33. package/dist/executor-brand.js +1 -0
  34. package/dist/extension-options-map.d.ts +9 -0
  35. package/dist/extension-options-map.js +4 -0
  36. package/dist/feature.d.ts +518 -0
  37. package/dist/feature.js +1 -0
  38. package/dist/field-icon.d.ts +1 -0
  39. package/dist/field-icon.js +1 -0
  40. package/dist/fields.d.ts +729 -0
  41. package/dist/fields.js +50 -0
  42. package/dist/file-handle-types.d.ts +12 -0
  43. package/dist/file-handle-types.js +1 -0
  44. package/dist/file-provider-resolver-types.d.ts +3 -0
  45. package/dist/file-provider-resolver-types.js +1 -0
  46. package/dist/file-storage-provider-types.d.ts +17 -0
  47. package/dist/file-storage-provider-types.js +1 -0
  48. package/dist/geo-tz.d.ts +17 -0
  49. package/{src/geo-tz.ts → dist/geo-tz.js} +1 -20
  50. package/dist/handlers.d.ts +493 -0
  51. package/dist/handlers.js +49 -0
  52. package/dist/hook-phase.d.ts +1 -0
  53. package/dist/hook-phase.js +1 -0
  54. package/dist/hooks.d.ts +75 -0
  55. package/dist/hooks.js +1 -0
  56. package/dist/http-route.d.ts +52 -0
  57. package/dist/http-route.js +13 -0
  58. package/dist/identifiers.d.ts +8 -0
  59. package/{src/identifiers.ts → dist/identifiers.js} +7 -24
  60. package/dist/index.d.ts +1 -0
  61. package/dist/index.js +1 -0
  62. package/dist/kms-adapter-types.d.ts +68 -0
  63. package/dist/kms-adapter-types.js +49 -0
  64. package/dist/logger.d.ts +7 -0
  65. package/dist/logger.js +1 -0
  66. package/dist/multi-stream-apply-context-types.d.ts +18 -0
  67. package/dist/multi-stream-apply-context-types.js +1 -0
  68. package/dist/nav-icon.d.ts +3 -0
  69. package/dist/nav-icon.js +100 -0
  70. package/dist/nav.d.ts +39 -0
  71. package/dist/nav.js +1 -0
  72. package/dist/observability/index.d.ts +3 -0
  73. package/dist/observability/index.js +2 -0
  74. package/dist/observability/metric.d.ts +34 -0
  75. package/dist/observability/metric.js +3 -0
  76. package/dist/observability/provider.d.ts +24 -0
  77. package/dist/observability/provider.js +4 -0
  78. package/dist/observability/span.d.ts +32 -0
  79. package/dist/observability/span.js +4 -0
  80. package/dist/ownership.d.ts +33 -0
  81. package/dist/ownership.js +5 -0
  82. package/dist/projection.d.ts +38 -0
  83. package/dist/projection.js +1 -0
  84. package/dist/rate-limit-types.d.ts +19 -0
  85. package/dist/rate-limit-types.js +1 -0
  86. package/dist/relations.d.ts +25 -0
  87. package/dist/relations.js +1 -0
  88. package/dist/schema-table-types.d.ts +15 -0
  89. package/{src/schema-table-types.ts → dist/schema-table-types.js} +0 -20
  90. package/dist/screen.d.ts +1265 -0
  91. package/dist/screen.js +3 -0
  92. package/dist/search-adapter.d.ts +32 -0
  93. package/dist/search-adapter.js +1 -0
  94. package/dist/secrets-types.d.ts +52 -0
  95. package/dist/secrets-types.js +30 -0
  96. package/dist/snapshot-types.d.ts +13 -0
  97. package/dist/snapshot-types.js +1 -0
  98. package/dist/step.d.ts +304 -0
  99. package/dist/step.js +11 -0
  100. package/dist/target-ref.d.ts +5 -0
  101. package/{src/target-ref.ts → dist/target-ref.js} +1 -6
  102. package/dist/tenancy-brand.d.ts +6 -0
  103. package/dist/tenancy-brand.js +1 -0
  104. package/dist/tenant-db-types.d.ts +67 -0
  105. package/dist/tenant-db-types.js +5 -0
  106. package/dist/tree-node.d.ts +26 -0
  107. package/dist/tree-node.js +22 -0
  108. package/dist/tz-context.d.ts +48 -0
  109. package/dist/tz-context.js +6 -0
  110. package/dist/websocket-route.d.ts +46 -0
  111. package/dist/websocket-route.js +10 -0
  112. package/dist/where-clause-types.d.ts +20 -0
  113. package/dist/where-clause-types.js +1 -0
  114. package/dist/workspace.d.ts +10 -0
  115. package/dist/workspace.js +1 -0
  116. package/dist/write-error-info-types.d.ts +12 -0
  117. package/dist/write-error-info-types.js +1 -0
  118. package/package.json +360 -121
  119. package/src/changes.json +12 -0
  120. package/src/__tests__/handlers.test.ts +0 -40
  121. package/src/__tests__/plural-forms.test.ts +0 -18
  122. package/src/action-icon.ts +0 -57
  123. package/src/concurrency-mode.ts +0 -5
  124. package/src/config-scope.ts +0 -5
  125. package/src/config.ts +0 -563
  126. package/src/cursor-types.ts +0 -18
  127. package/src/db-connection.ts +0 -25
  128. package/src/define-handler.ts +0 -138
  129. package/src/derivatives-types.ts +0 -106
  130. package/src/entity-cache.ts +0 -35
  131. package/src/entity-handlers.ts +0 -68
  132. package/src/entity-table-meta-types.ts +0 -101
  133. package/src/envelope-cipher-types.ts +0 -6
  134. package/src/event-store-executor-types.ts +0 -163
  135. package/src/event-store-types.ts +0 -85
  136. package/src/executor-brand.ts +0 -19
  137. package/src/extension-options-map.ts +0 -21
  138. package/src/feature.ts +0 -1141
  139. package/src/field-icon.ts +0 -23
  140. package/src/fields.ts +0 -1114
  141. package/src/file-handle-types.ts +0 -20
  142. package/src/file-provider-resolver-types.ts +0 -7
  143. package/src/file-storage-provider-types.ts +0 -54
  144. package/src/handlers.ts +0 -1246
  145. package/src/hook-phase.ts +0 -2
  146. package/src/hooks.ts +0 -170
  147. package/src/http-route.ts +0 -78
  148. package/src/kms-adapter-types.ts +0 -128
  149. package/src/logger.ts +0 -7
  150. package/src/multi-stream-apply-context-types.ts +0 -52
  151. package/src/nav-icon.ts +0 -105
  152. package/src/nav.ts +0 -139
  153. package/src/observability/index.ts +0 -28
  154. package/src/observability/metric.ts +0 -56
  155. package/src/observability/provider.ts +0 -32
  156. package/src/observability/span.ts +0 -58
  157. package/src/ownership.ts +0 -83
  158. package/src/projection.ts +0 -170
  159. package/src/rate-limit-types.ts +0 -32
  160. package/src/relations.ts +0 -52
  161. package/src/screen.ts +0 -1593
  162. package/src/search-adapter.ts +0 -45
  163. package/src/secrets-types.ts +0 -182
  164. package/src/snapshot-types.ts +0 -36
  165. package/src/step.ts +0 -390
  166. package/src/tenancy-brand.ts +0 -9
  167. package/src/tenant-db-types.ts +0 -116
  168. package/src/tree-node.ts +0 -134
  169. package/src/tz-context.ts +0 -63
  170. package/src/websocket-route.ts +0 -68
  171. package/src/where-clause-types.ts +0 -31
  172. package/src/workspace.ts +0 -42
  173. package/src/write-error-info-types.ts +0 -20
  174. /package/{src/index.ts → dist/concurrency-mode.js} +0 -0
@@ -0,0 +1,49 @@
1
+ // AccessRule can arrive from untyped sources (pattern-library JSON, Designer)
2
+ // where `openToAll` doesn't actually match the declared union — narrow via
3
+ // `unknown` instead of trusting the static type, deny on anything malformed.
4
+ export function isOpenToAllGranted(rule) {
5
+ if (!("openToAll" in rule))
6
+ return false;
7
+ const openToAll = rule.openToAll;
8
+ if (typeof openToAll !== "object" || openToAll === null)
9
+ return false;
10
+ if (!("reason" in openToAll) || typeof openToAll.reason !== "string")
11
+ return false;
12
+ return openToAll.reason.trim().length > 0;
13
+ }
14
+ // UI-visibility predicate — DEFAULT-VISIBLE, the opposite stance from
15
+ // `hasAccess`/AccessRule's own default-deny doc above: an unset `access`
16
+ // means every signed-in user may see the screen/nav/workspace, not nobody.
17
+ // The engine's dispatcher access check stays default-deny; this only
18
+ // decides what a client renders/lists, never what a handler accepts.
19
+ // Same `unknown`-narrowing caution as isOpenToAllGranted — `access` can
20
+ // arrive from untyped sources.
21
+ export function isUiAccessGranted(access, userRoles) {
22
+ if (access === undefined)
23
+ return true;
24
+ if ("openToAll" in access)
25
+ return isOpenToAllGranted(access);
26
+ if (userRoles === undefined)
27
+ return false;
28
+ if (!("roles" in access))
29
+ return false;
30
+ return access.roles.some((role) => userRoles.includes(role));
31
+ }
32
+ export function normalizeEventPiiSubject(spec) {
33
+ if ("subjectField" in spec) {
34
+ return { kind: "user", ownerField: spec.subjectField, whenAbsent: undefined };
35
+ }
36
+ if (spec.personal === "tenant")
37
+ return { kind: "tenant" };
38
+ if (spec.personal === "self")
39
+ return { kind: "self" };
40
+ return { kind: "user", ownerField: spec.personal.of, whenAbsent: spec.personal.whenAbsent };
41
+ }
42
+ export function isRateLimitDisabled(v) {
43
+ return v !== undefined && "disabled" in v && v.disabled === true;
44
+ }
45
+ // Marker set by defineEntityQueryHandler on both of its verbs. A string, not
46
+ // a Symbol(), for the same reason as PAGED_QUERY_HANDLER_BRAND: a symlinked
47
+ // workspace can evaluate a module twice, and two Symbol() calls would not
48
+ // compare equal across those instances.
49
+ export const ENTITY_CONVENTION_QUERY_BRAND = "__kumikoEntityConventionQuery";
@@ -0,0 +1 @@
1
+ export type HookPhase = "inTransaction" | "afterCommit";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,75 @@
1
+ import type { StoredEvent } from "./event-store-types.js";
2
+ import type { AppContext } from "./handlers.js";
3
+ import type { HookPhase } from "./hook-phase.js";
4
+ import type { EntityId } from "./identifiers.js";
5
+ export type ValidationError = {
6
+ readonly field: string;
7
+ readonly error: string;
8
+ };
9
+ export type ValidationHookFn = (data: Readonly<Record<string, unknown>>) => readonly ValidationError[] | null;
10
+ export type SaveContext = {
11
+ readonly kind: "save";
12
+ readonly id: EntityId;
13
+ readonly data: Readonly<Record<string, unknown>>;
14
+ readonly changes: Readonly<Record<string, unknown>>;
15
+ readonly previous: Readonly<Record<string, unknown>>;
16
+ readonly isNew: boolean;
17
+ readonly entityName?: string | undefined;
18
+ readonly event?: StoredEvent | undefined;
19
+ };
20
+ export type DeleteContext = {
21
+ readonly kind: "delete";
22
+ readonly id: EntityId;
23
+ readonly data: Readonly<Record<string, unknown>>;
24
+ readonly entityName?: string | undefined;
25
+ readonly event?: StoredEvent | undefined;
26
+ };
27
+ export type LifecycleResult = SaveContext | DeleteContext;
28
+ export type PreSaveHookFn = (changes: Record<string, unknown>, context: AppContext & {
29
+ readonly previous: Readonly<Record<string, unknown>>;
30
+ readonly isNew: boolean;
31
+ }) => Promise<Record<string, unknown>>;
32
+ export type PostSaveHookFn = (result: SaveContext, context: AppContext) => Promise<void>;
33
+ export type PostSaveBatchHookFn = (results: readonly SaveContext[], context: AppContext) => Promise<void>;
34
+ export type PreDeleteHookFn = (payload: DeleteContext, context: AppContext) => Promise<void>;
35
+ export type PostDeleteHookFn = (payload: DeleteContext, context: AppContext) => Promise<void>;
36
+ export type PostDeleteBatchHookFn = (payloads: readonly DeleteContext[], context: AppContext) => Promise<void>;
37
+ export type PreQueryHookFn = (payload: Record<string, unknown>, context: AppContext) => Promise<Record<string, unknown>>;
38
+ export type PostQueryHookFn = (result: {
39
+ readonly entityName: string | undefined;
40
+ readonly rows: ReadonlyArray<Record<string, unknown>>;
41
+ }, context: AppContext) => Promise<{
42
+ rows: ReadonlyArray<Record<string, unknown>>;
43
+ }>;
44
+ export type LifecycleHookFn = PreSaveHookFn | PostSaveHookFn | PreDeleteHookFn | PostDeleteHookFn | PreQueryHookFn | PostQueryHookFn;
45
+ export type { HookPhase } from "./hook-phase.js";
46
+ export type HookOwner = {
47
+ readonly featureName?: string;
48
+ };
49
+ export type PhasedHook<TFn> = {
50
+ readonly fn: TFn;
51
+ readonly phase: HookPhase;
52
+ } & HookOwner;
53
+ export type OwnedFn<TFn> = {
54
+ readonly fn: TFn;
55
+ } & HookOwner;
56
+ export type HookMap = {
57
+ readonly validation?: Readonly<Record<string, ValidationHookFn>>;
58
+ readonly preSave?: Readonly<Record<string, readonly OwnedFn<PreSaveHookFn>[]>>;
59
+ readonly postSave?: Readonly<Record<string, readonly PhasedHook<PostSaveHookFn>[]>>;
60
+ readonly preDelete?: Readonly<Record<string, readonly PhasedHook<PreDeleteHookFn>[]>>;
61
+ readonly postDelete?: Readonly<Record<string, readonly PhasedHook<PostDeleteHookFn>[]>>;
62
+ readonly preQuery?: Readonly<Record<string, readonly OwnedFn<PreQueryHookFn>[]>>;
63
+ readonly postQuery?: Readonly<Record<string, readonly OwnedFn<PostQueryHookFn>[]>>;
64
+ };
65
+ export type EntityHookMap = {
66
+ readonly postSave?: Readonly<Record<string, readonly PhasedHook<PostSaveHookFn>[]>>;
67
+ readonly preDelete?: Readonly<Record<string, readonly PhasedHook<PreDeleteHookFn>[]>>;
68
+ readonly postDelete?: Readonly<Record<string, readonly PhasedHook<PostDeleteHookFn>[]>>;
69
+ readonly postQuery?: Readonly<Record<string, readonly OwnedFn<PostQueryHookFn>[]>>;
70
+ };
71
+ export type SearchPayloadContributorFn = (args: {
72
+ readonly entityName: string;
73
+ readonly entityId: EntityId;
74
+ readonly state: Record<string, unknown>;
75
+ }) => Record<string, unknown> | Promise<Record<string, unknown>>;
package/dist/hooks.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,52 @@
1
+ import type { Context } from "hono";
2
+ /** Subset von HTTP-Methoden den wir aktiv unterstützen. Hono spricht
3
+ * alle, aber das hier sind die einzigen die ein Feature-Author
4
+ * realistisch deklariert. */
5
+ export type HttpRouteMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
6
+ /** Dependencies die der Handler vom Framework bekommt. App-Author kann
7
+ * die App selbst aufrufen (`deps.app.fetch(...)` für intern-call) oder
8
+ * direkt per dispatcher Daten ziehen. Db/Redis sind die rohen Connections
9
+ * — wer Tenant-Scope braucht muss durch dispatcher.query gehen.
10
+ *
11
+ * Hono-typing: `Context<any, any>` weil das Hono-Type-Param-Setup nur
12
+ * intern relevant ist. Concrete Hono-app wird im Boot-Path zugewiesen. */
13
+ export type HttpRouteHandlerDeps = {
14
+ /** Die Hono-app — Handler kann via app.fetch(...) interne Routes
15
+ * ansprechen (z.B. /api/query mit der vollen Auth-/Anonymous-Chain). */
16
+ readonly app: import("hono").Hono<any, any>;
17
+ /** Run a query handler in-process, forcing a SPECIFIC tenant — WITHOUT
18
+ * going through the public /api/query HTTP layer (no header parsing, no
19
+ * anonymousAccess tenant resolution). The synthesized caller carries
20
+ * anonymous-level access ONLY (same role a real anonymous request would
21
+ * have, no more) — the primitive forces the tenant, not the privilege
22
+ * level, so it stays safe to call from any `anonymous: true` route
23
+ * without risking a field-level disclosure a real anonymous caller
24
+ * couldn't already get. Use this whenever the route needs a tenant
25
+ * other than the one the request resolves to (e.g. always
26
+ * SYSTEM_TENANT_ID regardless of the visited host) — spoofing that via
27
+ * an internal X-Tenant header on `app.fetch(...)` is indistinguishable
28
+ * from an external client and gets rejected by resolverTrust:
29
+ * "authoritative" anonymousAccess configs (see auth-middleware.ts). */
30
+ readonly systemQuery: (type: string, payload: unknown, tenantId: import("./identifiers.js").TenantId) => Promise<unknown>;
31
+ /** Caller IP resolved via the server's configured, trustedProxyHops-aware
32
+ * resolver (same one requestIdMiddleware uses for `/api/*`) — a route
33
+ * handler needing the caller's IP (rate limiting, audit) should read
34
+ * this instead of parsing X-Forwarded-For itself, which trusts
35
+ * whichever hop count the deployment actually has in front of it. */
36
+ readonly clientIp: string;
37
+ };
38
+ export type HttpRouteHandler = (c: Context<any, any>, deps: HttpRouteHandlerDeps) => Response | Promise<Response>;
39
+ export type HttpRouteDefinition = {
40
+ /** HTTP-Methode — bei Hono-Mount via app.{get,post,...}(path). */
41
+ readonly method: HttpRouteMethod;
42
+ /** URL-Pfad (Hono-Pattern, z.B. "/feed.xml" oder "/og/:tenantId.png"). */
43
+ readonly path: string;
44
+ /** true = public, no session required. false = mounted behind the
45
+ * session auth chain (no anonymous fallthrough, PAT rate limit,
46
+ * origin + CSRF guards) — a request without a session gets 401. The
47
+ * handler reads the caller via getUser(c). */
48
+ readonly anonymous: boolean;
49
+ /** Hono-Handler. Bekommt Hono-Context + Framework-Deps; returnt
50
+ * Response (sync oder async). */
51
+ readonly handler: HttpRouteHandler;
52
+ };
@@ -0,0 +1,13 @@
1
+ // HTTP route definition — feature-declared HTTP endpoints outside
2
+ // the /api/write|query|batch pipeline. Use case: RSS/Atom feeds, OpenAPI
3
+ // specs, OG image generators, webhook receivers — anything where the
4
+ // feature author controls the wire format themselves.
5
+ //
6
+ // Pattern symmetric to r.queryHandler / r.writeHandler: definition as
7
+ // part of the feature (not the app bootstrapping). Phase-3 multi-tenant
8
+ // becomes trivial because tenant context is picked up via host resolution.
9
+ //
10
+ // Escape-hatch stays: runProdApp.extraRoutes (declarative list with
11
+ // `entry` tier) for hand-rolled routes that have nothing to do with a
12
+ // feature (e.g. platform-specific static-serving logic).
13
+ export {};
@@ -0,0 +1,8 @@
1
+ export type TenantId = string;
2
+ export declare const UUID_SHAPE_PATTERN = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$";
3
+ export declare function isUuid(value: unknown): value is string;
4
+ export declare function parseTenantId(value: unknown): TenantId | null;
5
+ export declare const SYSTEM_TENANT_ID: TenantId;
6
+ export declare function isSystemTenant(tenantId: TenantId | null | undefined): boolean;
7
+ export declare const SYSTEM_USER_ID = "00000000-0000-0000-0000-000000000000";
8
+ export type EntityId = number | string;
@@ -2,11 +2,6 @@
2
2
  // travels through the framework. One declaration per concept so future
3
3
  // representation changes (branded types, UUID validation, opaque wrappers)
4
4
  // land in a single place.
5
-
6
- // Tenant identifier — UUID string today. May become branded/opaque later
7
- // without touching call sites.
8
- export type TenantId = string;
9
-
10
5
  // Lowercase UUID (any RFC-4122 variant). Strict enough to keep client-
11
6
  // supplied junk (e.g. SQL fragments, path-traversal probes) out of the
12
7
  // pipeline; loose enough that v4 / v7 / nil all match. Any caller that
@@ -18,46 +13,34 @@ export type TenantId = string;
18
13
  // exact same shape instead of growing a second copy that drifts.
19
14
  export const UUID_SHAPE_PATTERN = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$";
20
15
  const TENANT_ID_REGEX = new RegExp(UUID_SHAPE_PATTERN);
21
-
22
16
  // Shared shape check for any UUID-formatted identifier crossing an
23
17
  // untrusted boundary (fileRefId, entity ids, ...) — same loose,
24
18
  // version-agnostic regex as TENANT_ID_REGEX (v4/v7/nil all match), kept as
25
19
  // one predicate so call sites don't grow their own copy of the shape.
26
- export function isUuid(value: unknown): value is string {
27
- return typeof value === "string" && TENANT_ID_REGEX.test(value);
20
+ export function isUuid(value) {
21
+ return typeof value === "string" && TENANT_ID_REGEX.test(value);
28
22
  }
29
-
30
23
  // Validates a candidate string against the tenantId format and returns it
31
24
  // as a TenantId, or `null` when it doesn't match. Use at every system
32
25
  // boundary that admits untrusted input (HTTP headers, cookies, query
33
26
  // params). Returning null instead of throwing keeps the caller in charge
34
27
  // of the rejection shape — middleware returns 400, batch jobs may filter
35
28
  // + log, and unit tests don't need a try/catch.
36
- export function parseTenantId(value: unknown): TenantId | null {
37
- return isUuid(value) ? value : null;
29
+ export function parseTenantId(value) {
30
+ return isUuid(value) ? value : null;
38
31
  }
39
-
40
32
  // "System-scope" tenant marker: handlers carry this tenantId when the event
41
33
  // doesn't belong to any particular tenant (reference data, cross-tenant
42
34
  // jobs, global config). The concrete UUID is a valid v4 (not all-zeroes —
43
35
  // Postgres' UUID type rejects invalid variants), chosen to be easy to
44
36
  // eyeball in logs. Central constant so call sites don't re-type the string
45
37
  // and the isSystemTenant() check stays in sync.
46
- export const SYSTEM_TENANT_ID: TenantId = "00000000-0000-4000-8000-000000000000";
47
-
48
- export function isSystemTenant(tenantId: TenantId | null | undefined): boolean {
49
- return !tenantId || tenantId === SYSTEM_TENANT_ID;
38
+ export const SYSTEM_TENANT_ID = "00000000-0000-4000-8000-000000000000";
39
+ export function isSystemTenant(tenantId) {
40
+ return !tenantId || tenantId === SYSTEM_TENANT_ID;
50
41
  }
51
-
52
42
  // "No human caller" marker on event-store `createdBy`. Stringified so it
53
43
  // round-trips through SessionUser.id; not a real UUID, and createdBy is text.
54
44
  // Lives here rather than next to createSystemUser() so client-side code (the
55
45
  // reference-label map) can read it without importing a runtime module.
56
46
  export const SYSTEM_USER_ID = "00000000-0000-0000-0000-000000000000";
57
-
58
- // Primary-key identifier for any entity row. Two shapes coexist because of
59
- // the entity-def `idType` switch: classic CRUD entities keep `serial` (number),
60
- // while tenant + ES aggregates run on `uuid` (string). Call sites that pass
61
- // the id through to the DB layer stay agnostic; only code that formats ids
62
- // for URLs, logs, or cache keys needs `String(id)` — JS coerces both safely.
63
- export type EntityId = number | string;
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,68 @@
1
+ import type { TenantId } from "./identifiers.js";
2
+ export type SubjectId = {
3
+ readonly kind: "user";
4
+ readonly userId: string;
5
+ } | {
6
+ readonly kind: "tenant";
7
+ readonly tenantId: TenantId;
8
+ } | {
9
+ readonly kind: "record";
10
+ readonly entity: string;
11
+ readonly id: string;
12
+ };
13
+ export type SubjectKey = string;
14
+ export declare function subjectKeyForUser(userId: string): SubjectKey;
15
+ export declare function subjectKeyForTenant(tenantId: TenantId): SubjectKey;
16
+ export declare const RECORD_ENTITY_PATTERN: RegExp;
17
+ export declare function subjectKeyForRecord(entity: string, id: string): SubjectKey;
18
+ export declare function subjectIdToKey(subject: SubjectId): SubjectKey;
19
+ export declare function subjectIdFromKey(key: SubjectKey): SubjectId;
20
+ export interface KmsContext {
21
+ readonly tenantId?: TenantId;
22
+ readonly requestId: string;
23
+ readonly userId?: string;
24
+ readonly eraseReason?: string;
25
+ }
26
+ export interface KmsHealth {
27
+ readonly ok: boolean;
28
+ readonly latencyMs: number;
29
+ readonly details?: Record<string, unknown>;
30
+ }
31
+ export type SubjectDek = Buffer;
32
+ interface KmsAdapterBase {
33
+ /**
34
+ * Creates a fresh subject key. Throws KeyAlreadyExistsError when the
35
+ * subject already has one — including an erased tombstone: a shredded
36
+ * subject must never get a new key, or forget could be undone by
37
+ * re-encrypting under it.
38
+ */
39
+ createKey(subject: SubjectId, ctx: KmsContext): Promise<void>;
40
+ /**
41
+ * Erases the key material immediately; the tombstone row stays for the
42
+ * audit trail. Idempotent — repeat calls and unknown subjects are no-ops.
43
+ */
44
+ eraseKey(subject: SubjectId, ctx: KmsContext): Promise<void>;
45
+ /** Probe for boot + readiness. Throws when the backend is unreachable. */
46
+ health(): Promise<KmsHealth>;
47
+ }
48
+ export interface LocalKeyKmsAdapter extends KmsAdapterBase {
49
+ readonly capabilities: {
50
+ readonly mode: "local-key";
51
+ };
52
+ /**
53
+ * Throws KeyErasedError after eraseKey (callers render "[[erased]]"),
54
+ * KeyNotFoundError when the subject never had a key (typically a bug).
55
+ */
56
+ getKey(subject: SubjectId, ctx: KmsContext): Promise<SubjectDek>;
57
+ }
58
+ export interface RemoteCryptoKmsAdapter extends KmsAdapterBase {
59
+ readonly capabilities: {
60
+ readonly mode: "remote-crypto";
61
+ };
62
+ encrypt(subject: SubjectId, plaintext: Uint8Array, ctx: KmsContext): Promise<Uint8Array>;
63
+ /** Same error contract as LocalKeyKmsAdapter.getKey. */
64
+ decrypt(subject: SubjectId, ciphertext: Uint8Array, ctx: KmsContext): Promise<Uint8Array>;
65
+ }
66
+ export type KmsAdapter = LocalKeyKmsAdapter | RemoteCryptoKmsAdapter;
67
+ export declare function isLocalKeyKmsAdapter(adapter: KmsAdapter): adapter is LocalKeyKmsAdapter;
68
+ export {};
@@ -0,0 +1,49 @@
1
+ export function subjectKeyForUser(userId) {
2
+ return `user:${userId}`;
3
+ }
4
+ export function subjectKeyForTenant(tenantId) {
5
+ return `tenant:${tenantId}`;
6
+ }
7
+ // Registry entity names are identifier-shaped; a free-form aggregate_type
8
+ // (events-schema.ts is a plain text column) could otherwise mint a record
9
+ // key that subjectIdSchema (forget-subject.write.ts) later refuses to
10
+ // shred — enforced once here so minting and shredding can never disagree.
11
+ export const RECORD_ENTITY_PATTERN = /^[A-Za-z][A-Za-z0-9_-]*$/;
12
+ export function subjectKeyForRecord(entity, id) {
13
+ if (!RECORD_ENTITY_PATTERN.test(entity)) {
14
+ throw new Error(`Invalid record entity for subject key: "${entity}" — must match ${RECORD_ENTITY_PATTERN}`);
15
+ }
16
+ if (id === "")
17
+ throw new Error("Invalid record id for subject key: empty");
18
+ return `record:${entity}:${id}`;
19
+ }
20
+ export function subjectIdToKey(subject) {
21
+ switch (subject.kind) {
22
+ case "user":
23
+ return subjectKeyForUser(subject.userId);
24
+ case "tenant":
25
+ return subjectKeyForTenant(subject.tenantId);
26
+ case "record":
27
+ return subjectKeyForRecord(subject.entity, subject.id);
28
+ }
29
+ }
30
+ export function subjectIdFromKey(key) {
31
+ if (key.startsWith("user:"))
32
+ return { kind: "user", userId: key.slice("user:".length) };
33
+ if (key.startsWith("tenant:")) {
34
+ return { kind: "tenant", tenantId: key.slice("tenant:".length) }; // @cast-boundary parse of a key this module minted
35
+ }
36
+ if (key.startsWith("record:")) {
37
+ const rest = key.slice("record:".length);
38
+ const separatorIndex = rest.indexOf(":");
39
+ const entity = separatorIndex === -1 ? "" : rest.slice(0, separatorIndex);
40
+ const id = separatorIndex === -1 ? "" : rest.slice(separatorIndex + 1);
41
+ if (entity === "" || id === "")
42
+ throw new Error(`Invalid subject key: ${key}`);
43
+ return { kind: "record", entity, id };
44
+ }
45
+ throw new Error(`Invalid subject key: ${key}`);
46
+ }
47
+ export function isLocalKeyKmsAdapter(adapter) {
48
+ return adapter.capabilities.mode === "local-key";
49
+ }
@@ -0,0 +1,7 @@
1
+ export type Logger = {
2
+ info(msg: string, data?: Record<string, unknown>): void;
3
+ warn(msg: string, data?: Record<string, unknown>): void;
4
+ error(msg: string, data?: Record<string, unknown>): void;
5
+ debug(msg: string, data?: Record<string, unknown>): void;
6
+ child(context: Record<string, unknown>): Logger;
7
+ };
package/dist/logger.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ import type { DerivativesContext } from "./derivatives-types.js";
2
+ import type { StoredEvent } from "./event-store-types.js";
3
+ import type { KumikoEventTypeMap } from "./event-type-map.js";
4
+ import type { Registry } from "./feature.js";
5
+ import type { FileContext } from "./file-handle-types.js";
6
+ import type { AppendEventFn, UnsafeAppendEventFn } from "./handlers.js";
7
+ import type { SecretsContext } from "./secrets-types.js";
8
+ export type MultiStreamApplyContext<TMap extends object = KumikoEventTypeMap> = {
9
+ readonly appendEvent: AppendEventFn<TMap>;
10
+ readonly unsafeAppendEvent: UnsafeAppendEventFn;
11
+ readonly loadAggregate: (aggregateId: string, options?: {
12
+ readonly asOf?: Temporal.Instant;
13
+ }) => Promise<readonly StoredEvent[]>;
14
+ readonly registry: Registry;
15
+ readonly files?: FileContext;
16
+ readonly derivatives?: DerivativesContext;
17
+ readonly secrets?: SecretsContext;
18
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ export declare const NAV_ICON_KEYS: readonly ["dashboard", "layout-grid", "book-open", "clipboard-list", "package", "gauge", "list", "table", "layers", "building", "calculator", "wallet", "coins", "credit-card", "piggy-bank", "receipt", "chart", "bar-chart", "trending", "sparkles", "wand", "calendar", "file", "folder", "folder-open", "home", "bell", "shield", "shield-check", "send", "settings", "users", "user", "search", "tag", "key", "link", "palette", "share", "server", "mail", "lock", "hash", "download", "upload", "rocket", "plus", "languages", "trash", "x", "check", "arrow-left", "arrow-right", "copy", "pencil", "eye", "eye-off", "filter", "refresh", "more-horizontal", "more-vertical", "external-link", "chevron-down", "chevron-left", "chevron-right", "save", "undo", "archive", "star", "flag", "clock", "map-pin", "phone", "printer", "alert-triangle", "info", "check-circle", "x-circle", "loader", "mic", "circle-stop", "image"];
2
+ export type NavIconKey = (typeof NAV_ICON_KEYS)[number];
3
+ export type IconKey = NavIconKey;
@@ -0,0 +1,100 @@
1
+ // Closed vocabulary of nav and button icon keys. NavDefinition.icon,
2
+ // ScreenNavSugar.icon, ConfigMask.icon and ButtonProps.icon/iconEnd are all
3
+ // typed against this union, so an unregistered key is a compile error at the
4
+ // r.nav()/r.screen()/config-mask/button call site instead of a silent
5
+ // missing-icon at runtime.
6
+ //
7
+ // The renderer-web NAV_ICONS map (packages/renderer-web/src/icons.tsx) is
8
+ // checked against this same union via `satisfies`, so the two can't drift —
9
+ // add a key here only together with its lucide-react entry there, and vice
10
+ // versa.
11
+ //
12
+ // Declared as a runtime tuple (not just a type) so callers that sit outside
13
+ // this repo's `tsc --build` project graph (e.g. samples, external consumers
14
+ // resolving from a stale published type) still get a runtime-checkable
15
+ // vocabulary — the boot-validator's validateActionHasIcon checks a declared
16
+ // icon against NAV_ICON_KEYS instead of trusting "not undefined".
17
+ export const NAV_ICON_KEYS = [
18
+ "dashboard",
19
+ "layout-grid",
20
+ "book-open",
21
+ "clipboard-list",
22
+ "package",
23
+ "gauge",
24
+ "list",
25
+ "table",
26
+ "layers",
27
+ "building",
28
+ "calculator",
29
+ "wallet",
30
+ "coins",
31
+ "credit-card",
32
+ "piggy-bank",
33
+ "receipt",
34
+ "chart",
35
+ "bar-chart",
36
+ "trending",
37
+ "sparkles",
38
+ "wand",
39
+ "calendar",
40
+ "file",
41
+ "folder",
42
+ "folder-open",
43
+ "home",
44
+ "bell",
45
+ "shield",
46
+ "shield-check",
47
+ "send",
48
+ "settings",
49
+ "users",
50
+ "user",
51
+ "search",
52
+ "tag",
53
+ "key",
54
+ "link",
55
+ "palette",
56
+ "share",
57
+ "server",
58
+ "mail",
59
+ "lock",
60
+ "hash",
61
+ "download",
62
+ "upload",
63
+ "rocket",
64
+ "plus",
65
+ "languages",
66
+ "trash",
67
+ "x",
68
+ "check",
69
+ "arrow-left",
70
+ "arrow-right",
71
+ "copy",
72
+ "pencil",
73
+ "eye",
74
+ "eye-off",
75
+ "filter",
76
+ "refresh",
77
+ "more-horizontal",
78
+ "more-vertical",
79
+ "external-link",
80
+ "chevron-down",
81
+ "chevron-left",
82
+ "chevron-right",
83
+ "save",
84
+ "undo",
85
+ "archive",
86
+ "star",
87
+ "flag",
88
+ "clock",
89
+ "map-pin",
90
+ "phone",
91
+ "printer",
92
+ "alert-triangle",
93
+ "info",
94
+ "check-circle",
95
+ "x-circle",
96
+ "loader",
97
+ "mic",
98
+ "circle-stop",
99
+ "image",
100
+ ];
package/dist/nav.d.ts ADDED
@@ -0,0 +1,39 @@
1
+ import type { AccessRule } from "./handlers.js";
2
+ import type { NavIconKey } from "./nav-icon.js";
3
+ import type { TargetRef } from "./target-ref.js";
4
+ import type { TreeAction } from "./tree-node.js";
5
+ export type { IconKey, NavIconKey } from "./nav-icon.js";
6
+ export type NavDefinition = {
7
+ readonly id: string;
8
+ readonly label: string;
9
+ readonly icon?: NavIconKey;
10
+ readonly parent?: string;
11
+ readonly order?: number;
12
+ readonly screen?: string;
13
+ readonly target?: TargetRef;
14
+ readonly actions?: readonly TreeAction[];
15
+ readonly createAction?: TreeAction;
16
+ readonly provider?: boolean;
17
+ readonly access?: AccessRule;
18
+ readonly workspaces?: readonly string[];
19
+ };
20
+ /** Client editor format for r.contentCollection() — not template body storage formats. */
21
+ export type ContentEditorFormat = "plain" | "rich" | "markdown";
22
+ export type ContentCollectionDefinition = {
23
+ readonly id: string;
24
+ readonly kind: string;
25
+ readonly access?: AccessRule;
26
+ readonly ownership?: "tenant" | "user";
27
+ readonly contentFormat?: ContentEditorFormat;
28
+ readonly variableSchema?: Readonly<Record<string, string>>;
29
+ readonly nav: {
30
+ readonly label: string;
31
+ readonly icon?: NavIconKey;
32
+ readonly parent?: string;
33
+ readonly order?: number;
34
+ readonly access?: AccessRule;
35
+ readonly workspaces?: readonly string[];
36
+ readonly createAction?: TreeAction;
37
+ readonly actions?: readonly TreeAction[];
38
+ };
39
+ };
package/dist/nav.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ export type { Counter, Gauge, Histogram, Meter, MetricDefinition, MetricLabels, MetricsHandle, MetricType, } from "./metric.js";
2
+ export type { ObservabilityOptions, ObservabilityProvider, SamplingConfig, SensitiveFilterConfig, } from "./provider.js";
3
+ export type { SerializedTraceContext, Span, SpanAttributes, SpanAttributeValue, SpanKind, SpanStatus, StartSpanOptions, Tracer, } from "./span.js";
@@ -0,0 +1,2 @@
1
+ // Barrel for observability types. Split into span/metric/provider so each module stays focused.
2
+ export {};
@@ -0,0 +1,34 @@
1
+ export type MetricLabels = Record<string, string | number | boolean>;
2
+ export type MetricType = "counter" | "histogram" | "gauge";
3
+ export type MetricDefinition = {
4
+ readonly name: string;
5
+ readonly type: MetricType;
6
+ readonly description?: string;
7
+ readonly labels?: readonly string[];
8
+ readonly buckets?: readonly number[];
9
+ readonly unit?: string;
10
+ readonly tenantLabel?: boolean;
11
+ };
12
+ export interface Counter {
13
+ inc(value?: number, labels?: MetricLabels): void;
14
+ }
15
+ export interface Histogram {
16
+ observe(value: number, labels?: MetricLabels): void;
17
+ }
18
+ export interface Gauge {
19
+ set(value: number, labels?: MetricLabels): void;
20
+ inc(value?: number, labels?: MetricLabels): void;
21
+ dec(value?: number, labels?: MetricLabels): void;
22
+ }
23
+ export interface Meter {
24
+ registerMetric(def: MetricDefinition): void;
25
+ counter(name: string): Counter;
26
+ histogram(name: string): Histogram;
27
+ gauge(name: string): Gauge;
28
+ definitions(): ReadonlyMap<string, MetricDefinition>;
29
+ }
30
+ export interface MetricsHandle {
31
+ inc(name: string, labels?: MetricLabels, value?: number): void;
32
+ observe(name: string, value: number, labels?: MetricLabels): void;
33
+ set(name: string, value: number, labels?: MetricLabels): void;
34
+ }
@@ -0,0 +1,3 @@
1
+ // Metric + Meter contract. Feature code interacts through ctx.metrics
2
+ // (MetricsHandle); the framework wires Counter/Histogram/Gauge behind that.
3
+ export {};