@cosmicdrift/kumiko-types 0.198.0 → 0.199.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-types",
3
- "version": "0.198.0",
3
+ "version": "0.199.1",
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": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
package/src/feature.ts CHANGED
@@ -932,6 +932,10 @@ export type Registry = {
932
932
  ): readonly SearchPayloadContributorFn[];
933
933
  getHandlerEntity(qualifiedHandler: string): string | undefined;
934
934
  isHandlerSystemScoped(qualifiedHandler: string): boolean;
935
+ // Job counterpart to isHandlerSystemScoped — a job's owning feature is
936
+ // tracked separately (jobFeatureMap) since jobs aren't write/query/stream
937
+ // handlers and don't populate handlerFeatureMap.
938
+ isJobSystemScoped(qualifiedJobName: string): boolean;
935
939
  getHandlerFeature(qualifiedHandler: string): string | undefined;
936
940
  // True iff at least one registered handler declares a `rateLimit`
937
941
  // option. Pre-computed at registry-build so the boot path can skip
package/src/handlers.ts CHANGED
@@ -177,6 +177,7 @@ export type WriteResult<TData = unknown> =
177
177
  // Forward import: Registry is in feature.ts (circular type import — fine in TS)
178
178
  import type { Registry } from "./feature";
179
179
  import type { TenantId } from "./identifiers";
180
+ import type { UncheckedSystemDb } from "./tenant-db-types";
180
181
 
181
182
  // Minimal interface for job event triggers (framework-owned, concrete type in jobs/)
182
183
  export type JobRunnerRef = {
@@ -358,6 +359,9 @@ export type HandlerContext<TMap extends object = KumikoEventTypeMap> = SharedCon
358
359
  // outside the DB. `undefined` when the pipeline has no outside-tx source
359
360
  // for this dispatch (see dispatch-shared.ts) — callers must check before use.
360
361
  readonly dbOutsideTransaction: TenantDb | undefined;
362
+ // Only present for r.systemScope() handlers, bound to the same `db` as above.
363
+ // Non-system handlers never receive this — reach for `db` instead.
364
+ readonly systemDb?: UncheckedSystemDb;
361
365
  readonly registry: Registry;
362
366
  /** Aktiver SessionUser des Handler-Aufrufs — Convenience-Alias zu
363
367
  * `event.user`. Existiert weil Handler intuitiv `ctx.user.tenantId`
@@ -573,6 +577,15 @@ export type JobContext = SharedContextFields & {
573
577
  readonly systemUser: SessionUser;
574
578
  readonly log: Logger;
575
579
  readonly triggeredBy: { readonly id: string; readonly tenantId: TenantId } | null;
580
+ // Only present for jobs whose owning feature declares r.systemScope(),
581
+ // mirroring HandlerContext.systemDb (dispatch-shared.ts buildHandlerContext).
582
+ // assertTenantMatch()/acknowledgeCrossTenant() return a TenantDb, not the
583
+ // raw DbConnection above — job code that needs a DbRunner for a helper
584
+ // like reindexEntity() reaches through `.raw` on that TenantDb. `.raw`
585
+ // bypasses tenant filtering entirely, so it's only safe to hand to a
586
+ // helper that filters by tenantId itself (as reindexEntity does) — never
587
+ // pass it to code that trusts the connection to already be scoped.
588
+ readonly systemDb?: UncheckedSystemDb;
576
589
  readonly write: (qn: string, payload: unknown) => Promise<WriteResult>;
577
590
  readonly queryAs: (user: SessionUser, qn: string, payload: unknown) => Promise<unknown>;
578
591
  // Multi-trigger jobs (`on: [...]`) use this to tell which trigger fired —
@@ -56,3 +56,16 @@ export type TenantDb = {
56
56
  ): Promise<readonly T[]>;
57
57
  deleteMany(table: WritableTable, where: WhereObject): Promise<void>;
58
58
  };
59
+
60
+ // Symbol.for (global registry) so the brand identity matches even if kumiko-types
61
+ // resolves to two independent copies (workspace symlink vs. published npm) — a plain
62
+ // `Symbol()` per copy would make each resolution's `UncheckedSystemDb` structurally
63
+ // incompatible with the other, per the kumiko.secret precedent in secrets-types.ts.
64
+ export const SYSTEM_SCOPE_CHECK_BRAND: unique symbol = Symbol.for("kumiko.system-scope-check");
65
+
66
+ export type UncheckedSystemDb = {
67
+ readonly [SYSTEM_SCOPE_CHECK_BRAND]: true;
68
+ assertTenantMatch(tenantId: TenantId): TenantDb;
69
+ assertRowsTenant<T>(rows: readonly T[], tenantField: keyof T): readonly T[];
70
+ acknowledgeCrossTenant(reason: string): TenantDb;
71
+ };