@cosmicdrift/kumiko-framework 0.378.0 → 0.380.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/AGENTS.md +4 -1
- package/agents/examples/compose-stacks.md +1 -2
- package/agents/examples/currencies-global.md +2 -3
- package/agents/examples/embedded.md +23 -37
- package/agents/examples/event-sourcing.md +6 -5
- package/agents/examples/legal-pages.md +5 -5
- package/agents/examples/reference-data.md +26 -21
- package/agents/examples/relations.md +2 -1
- package/agents/features.md +4 -4
- package/dist/api/entity-read-access.d.ts +2 -0
- package/dist/api/entity-read-access.js +27 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -0
- package/dist/api/server.js +10 -3
- package/dist/api/sse-route.d.ts +5 -0
- package/dist/api/sse-route.js +59 -9
- package/dist/db/apply-entity-event.d.ts +3 -2
- package/dist/db/apply-entity-event.js +1 -1
- package/dist/db/dialect.d.ts +2 -1
- package/dist/db/dialect.js +2 -2
- package/dist/db/entity-table-from-registry.js +0 -2
- package/dist/db/entity-table-meta.d.ts +5 -3
- package/dist/db/entity-table-meta.js +6 -15
- package/dist/db/event-store-executor-write.js +21 -16
- package/dist/db/executor-managed-tables.d.ts +8 -0
- package/dist/db/executor-managed-tables.js +48 -0
- package/dist/db/index.d.ts +6 -4
- package/dist/db/index.js +4 -2
- package/dist/db/money.d.ts +1 -1
- package/dist/db/queries/backfill-pii.js +22 -12
- package/dist/db/queries/database-now.d.ts +5 -0
- package/dist/db/queries/database-now.js +12 -0
- package/dist/db/queries/event-store-orphan-rows.d.ts +8 -0
- package/dist/db/queries/event-store-orphan-rows.js +32 -0
- package/dist/db/query.d.ts +22 -11
- package/dist/db/query.js +41 -36
- package/dist/db/reference-data.d.ts +5 -4
- package/dist/db/reference-data.js +23 -21
- package/dist/db/table-builder.d.ts +2 -3
- package/dist/db/table-builder.js +2 -0
- package/dist/db/table-introspection.d.ts +2 -0
- package/dist/db/table-introspection.js +4 -0
- package/dist/db/tenant-db-runner.d.ts +2 -0
- package/dist/db/tenant-db-runner.js +7 -0
- package/dist/db/tenant-db.d.ts +9 -2
- package/dist/db/tenant-db.js +82 -25
- package/dist/engine/boot-validator/entity-handler.js +9 -4
- package/dist/engine/boot-validator/query-refs.js +8 -0
- package/dist/engine/boot-validator/screens.js +34 -9
- package/dist/engine/extensions/tenant-data.d.ts +2 -2
- package/dist/engine/feature-ui-extensions.js +4 -0
- package/dist/engine/projection-helpers.d.ts +3 -3
- package/dist/engine/registry-state.js +2 -0
- package/dist/engine/registry-validate.js +4 -1
- package/dist/engine/schema-builder.js +18 -4
- package/dist/engine/screen-helpers.d.ts +1 -0
- package/dist/engine/screen-helpers.js +3 -0
- package/dist/engine/soft-delete-cleanup.js +53 -24
- package/dist/engine/steps/unsafe-projection-delete.js +4 -2
- package/dist/entrypoint/index.d.ts +1 -3
- package/dist/errors/reasons.d.ts +1 -0
- package/dist/errors/reasons.js +4 -1
- package/dist/errors/serialize.d.ts +1 -0
- package/dist/errors/serialize.js +9 -5
- package/dist/errors/write-error-info.js +3 -2
- package/dist/errors/zod-bridge.js +7 -0
- package/dist/es-ops/operations-schema.d.ts +1 -1
- package/dist/event-store/archive.d.ts +1 -1
- package/dist/event-store/events-schema.d.ts +1 -1
- package/dist/event-store/index.d.ts +1 -0
- package/dist/event-store/index.js +1 -0
- package/dist/event-store/rebuild-dead-letter.d.ts +1 -1
- package/dist/event-store/snapshot.d.ts +1 -1
- package/dist/event-store/upcaster-dead-letter.d.ts +1 -1
- package/dist/files/action-form-upload.d.ts +16 -0
- package/dist/files/action-form-upload.js +45 -0
- package/dist/files/bind-pending-file-refs.d.ts +14 -0
- package/dist/files/bind-pending-file-refs.js +44 -4
- package/dist/files/create-file-ref.d.ts +3 -0
- package/dist/files/create-file-ref.js +41 -13
- package/dist/files/file-routes.js +29 -2
- package/dist/files/index.d.ts +2 -1
- package/dist/files/index.js +1 -0
- package/dist/files/purge-expired-file-ref-bytes.d.ts +3 -2
- package/dist/files/purge-expired-file-ref-bytes.js +11 -5
- package/dist/files/storage-tracking.d.ts +1 -1
- package/dist/files/upload-ledger.d.ts +2 -2
- package/dist/internal/testing.d.ts +7 -0
- package/dist/internal/testing.js +14 -1
- package/dist/migrations/pending-rebuilds.d.ts +1 -1
- package/dist/migrations/pii-event-backfill.d.ts +1 -1
- package/dist/migrations/pii-event-backfill.js +14 -7
- package/dist/observability/escape-hatch-report.d.ts +1 -1
- package/dist/observability/escape-hatch-report.js +14 -3
- package/dist/pipeline/cascade-handler.js +79 -10
- package/dist/pipeline/dispatch-shared.js +1 -9
- package/dist/pipeline/event-consumer-state.d.ts +1 -1
- package/dist/pipeline/msp-rebuild.js +5 -3
- package/dist/pipeline/projection-rebuild.js +4 -2
- package/dist/pipeline/projection-state.d.ts +1 -1
- package/dist/pipeline/projections-runner.js +2 -1
- package/dist/rate-limit/index.d.ts +1 -1
- package/dist/rate-limit/index.js +1 -1
- package/dist/rate-limit/middleware.d.ts +4 -0
- package/dist/rate-limit/middleware.js +5 -2
- package/dist/ui-types/index.d.ts +2 -2
- package/dist/ui-types/index.js +2 -2
- package/dist/ui-types/list-row-meta.d.ts +3 -0
- package/dist/ui-types/list-row-meta.js +12 -1
- package/kumiko-es-tables.json +6 -0
- package/package.json +4 -3
- package/src/changes.json +117 -0
- package/src/scripts/codemod/README.md +20 -0
- package/src/scripts/codemod/codemod-support.ts +1 -1
- package/src/scripts/codemod/feature-layout.ts +416 -0
- package/src/scripts/codemod/package-merge.ts +0 -11
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { executeRawQueryRead } from "./raw-sql.js";
|
|
2
|
+
// Called by createTenantDb with its inner runner; keeps raw SQL out of consumers.
|
|
3
|
+
export async function queryDatabaseNow(runner) {
|
|
4
|
+
const [row] = await executeRawQueryRead(runner, "SELECT now() AS now");
|
|
5
|
+
if (!row)
|
|
6
|
+
throw new Error("SELECT now() returned no row");
|
|
7
|
+
return row.now;
|
|
8
|
+
}
|
|
9
|
+
/** Postgres clock; inside a transaction `now()` is the transaction start, so repeated reads agree. */
|
|
10
|
+
export function readDatabaseNow(db) {
|
|
11
|
+
return db.databaseNow();
|
|
12
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { TenantId } from "../../engine/types/index.ts";
|
|
2
|
+
import { type AnyDb } from "../query.ts";
|
|
3
|
+
export type DeleteRowsWithoutReplayableStreamArgs = {
|
|
4
|
+
readonly table: unknown;
|
|
5
|
+
readonly ids: readonly string[];
|
|
6
|
+
readonly tenantId?: TenantId;
|
|
7
|
+
};
|
|
8
|
+
export declare function deleteRowsWithoutReplayableStream(db: AnyDb, args: DeleteRowsWithoutReplayableStreamArgs): Promise<readonly string[]>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { asRawClient } from "../query.js";
|
|
2
|
+
import { tableNameOf } from "../table-introspection.js";
|
|
3
|
+
import { isBoundTenantDb, tenantDbWriteRunner } from "../tenant-db-runner.js";
|
|
4
|
+
import { toPostgresArrayLiteral } from "./postgres-array-literal.js";
|
|
5
|
+
import { quoteTableIdent } from "./table-ops.js";
|
|
6
|
+
// Deletes only rows a projection rebuild would not recreate: the row's stream has no events, or is archived.
|
|
7
|
+
// One statement, so a stream cannot gain events between check and delete.
|
|
8
|
+
export async function deleteRowsWithoutReplayableStream(db, args) {
|
|
9
|
+
if (args.ids.length === 0)
|
|
10
|
+
return [];
|
|
11
|
+
const tenantBound = isBoundTenantDb(db);
|
|
12
|
+
const tenantId = tenantBound ? db.tenantId : args.tenantId;
|
|
13
|
+
const runner = tenantBound ? tenantDbWriteRunner(db, "deleteRowsWithoutReplayableStream") : db;
|
|
14
|
+
const params = [toPostgresArrayLiteral(args.ids)];
|
|
15
|
+
let tenantFilter = "";
|
|
16
|
+
if (tenantId !== undefined) {
|
|
17
|
+
params.push(tenantId);
|
|
18
|
+
tenantFilter = `AND t."tenant_id" = $2`;
|
|
19
|
+
}
|
|
20
|
+
const rows = await asRawClient(runner).unsafe(`DELETE FROM ${quoteTableIdent(tableNameOf(args.table))} t
|
|
21
|
+
WHERE t."id" = ANY($1::uuid[]) ${tenantFilter}
|
|
22
|
+
AND NOT EXISTS (
|
|
23
|
+
SELECT 1 FROM "kumiko_events" e
|
|
24
|
+
WHERE e."tenant_id" = t."tenant_id" AND e."aggregate_id" = t."id"
|
|
25
|
+
AND NOT EXISTS (
|
|
26
|
+
SELECT 1 FROM "kumiko_archived_streams" a
|
|
27
|
+
WHERE a."tenant_id" = e."tenant_id" AND a."aggregate_id" = e."aggregate_id"
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
RETURNING t."id"`, params);
|
|
31
|
+
return rows.map((row) => row.id);
|
|
32
|
+
}
|
package/dist/db/query.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { AggregateRow, AggregateSpec } from "@cosmicdrift/kumiko-types/aggregate-types";
|
|
2
2
|
import type { DbRunner, DbTx } from "@cosmicdrift/kumiko-types/db-connection";
|
|
3
|
+
import type { ExecutorManagedRunner, ExecutorManagedWriteCapability, WritableTable } from "@cosmicdrift/kumiko-types/executor-brand";
|
|
3
4
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
4
5
|
import type { SelectOptions, WhereObject } from "@cosmicdrift/kumiko-types/where-clause-types";
|
|
5
6
|
import type { EntityTableMeta } from "./entity-table-meta.ts";
|
|
6
|
-
import { type NotExecutorOnly } from "./table-builder.ts";
|
|
7
7
|
import { asEntityTableMeta, requireEntityTableMeta } from "./table-introspection.ts";
|
|
8
8
|
export { asEntityTableMeta, requireEntityTableMeta };
|
|
9
9
|
export type ReservedClient = {
|
|
@@ -23,12 +23,14 @@ export type RawClientWithBegin = RawClient & {
|
|
|
23
23
|
export declare function hasBegin(client: RawClient): client is RawClientWithBegin;
|
|
24
24
|
export declare function asRawClient(db: unknown): RawClient;
|
|
25
25
|
export declare function runnerCanBeginTransaction(db: unknown): boolean;
|
|
26
|
+
export declare function runInSavepoint<T>(tx: ExecutorManagedRunner, fn: (sp: ExecutorManagedRunner) => Promise<T>): Promise<T>;
|
|
26
27
|
export declare function runInSavepoint<T>(tx: unknown, fn: (sp: unknown) => Promise<T>): Promise<T>;
|
|
28
|
+
export declare function runInSavepointIfSupported<T>(db: ExecutorManagedRunner, fn: (sp: ExecutorManagedRunner) => Promise<T>): Promise<T>;
|
|
27
29
|
export declare function runInSavepointIfSupported<T>(db: unknown, fn: (sp: DbRunner) => Promise<T>): Promise<T>;
|
|
28
30
|
export type AnyDb = DbRunner | unknown;
|
|
31
|
+
type AnyRow = any;
|
|
29
32
|
export type { JsonTextMatch, OrderByClause, SelectOptions, WhereObject, WhereOperator, WhereValue, } from "@cosmicdrift/kumiko-types/where-clause-types";
|
|
30
33
|
type TableLike = EntityTableMeta | any;
|
|
31
|
-
type WritableTable = EntityTableMeta & NotExecutorOnly;
|
|
32
34
|
export type TableInfo = {
|
|
33
35
|
readonly name: string;
|
|
34
36
|
readonly columnOf: (field: string) => string;
|
|
@@ -43,8 +45,8 @@ export declare function instantToTimestamptzParam(instant: Temporal.Instant): st
|
|
|
43
45
|
export declare function isClosedConnectionError(err: unknown): boolean;
|
|
44
46
|
export declare function describeClosedConnectionFailure(raw: unknown, err: unknown, attempts: number): Record<string, unknown> | undefined;
|
|
45
47
|
export declare function unsafeReadRetrying<TRow>(db: AnyDb, sqlText: string, params: readonly unknown[]): Promise<readonly TRow[]>;
|
|
46
|
-
export declare function selectMany<TRow =
|
|
47
|
-
export declare function fetchOne<TRow =
|
|
48
|
+
export declare function selectMany<TRow = AnyRow>(db: AnyDb, table: TableLike, where?: WhereObject, options?: SelectOptions): Promise<readonly TRow[]>;
|
|
49
|
+
export declare function fetchOne<TRow = AnyRow>(db: AnyDb, table: TableLike, where: WhereObject): Promise<TRow | undefined>;
|
|
48
50
|
export type JoinColumnPair = {
|
|
49
51
|
readonly left: string;
|
|
50
52
|
readonly right: string;
|
|
@@ -65,10 +67,14 @@ export type InnerJoinRow<TLeft, TRight> = {
|
|
|
65
67
|
readonly right: TRight;
|
|
66
68
|
};
|
|
67
69
|
export declare function selectInnerJoin<TLeft = Record<string, unknown>, TRight = Record<string, unknown>>(db: AnyDb, spec: InnerJoinSpec): Promise<readonly InnerJoinRow<TLeft, TRight>[]>;
|
|
68
|
-
export declare function insertMany<TRow =
|
|
69
|
-
export declare function
|
|
70
|
-
export declare function
|
|
70
|
+
export declare function insertMany<TRow = AnyRow>(db: AnyDb, table: WritableTable, rows: ReadonlyArray<Record<string, unknown>>): Promise<readonly TRow[]>;
|
|
71
|
+
export declare function insertMany<TRow = AnyRow>(db: ExecutorManagedWriteCapability, table: EntityTableMeta, rows: ReadonlyArray<Record<string, unknown>>): Promise<readonly TRow[]>;
|
|
72
|
+
export declare function insertOne<TRow = AnyRow>(db: AnyDb, table: WritableTable, values: Record<string, unknown>): Promise<TRow | undefined>;
|
|
73
|
+
export declare function insertOne<TRow = AnyRow>(db: ExecutorManagedWriteCapability, table: EntityTableMeta, values: Record<string, unknown>): Promise<TRow | undefined>;
|
|
74
|
+
export declare function updateMany<TRow = AnyRow>(db: AnyDb, table: WritableTable, set: Record<string, unknown>, where: WhereObject): Promise<readonly TRow[]>;
|
|
75
|
+
export declare function updateMany<TRow = AnyRow>(db: ExecutorManagedWriteCapability, table: EntityTableMeta, set: Record<string, unknown>, where: WhereObject): Promise<readonly TRow[]>;
|
|
71
76
|
export declare function deleteMany(db: AnyDb, table: WritableTable, where: WhereObject): Promise<number>;
|
|
77
|
+
export declare function deleteMany(db: ExecutorManagedWriteCapability, table: EntityTableMeta, where: WhereObject): Promise<number>;
|
|
72
78
|
export declare function resolveConflictColumns(table: TableLike, info: TableInfo, conflictKeys: readonly string[] | undefined): readonly string[];
|
|
73
79
|
export type UpsertOnConflictOptions = {
|
|
74
80
|
readonly conflictKeys: readonly string[];
|
|
@@ -88,18 +94,23 @@ export type DeleteManyBatchedResult = {
|
|
|
88
94
|
export declare function countWhere(db: AnyDb, table: TableLike, where?: WhereObject): Promise<number>;
|
|
89
95
|
export declare function isTimestamptzType(pgType: string | undefined): boolean;
|
|
90
96
|
export declare function aggregateWhere(db: AnyDb, table: TableLike, spec: AggregateSpec, where?: WhereObject): Promise<readonly AggregateRow[]>;
|
|
91
|
-
export declare function upsertOnConflict<TRow =
|
|
92
|
-
export declare function
|
|
97
|
+
export declare function upsertOnConflict<TRow = AnyRow>(db: AnyDb, table: WritableTable, values: Record<string, unknown>, options: UpsertOnConflictOptions): Promise<TRow | undefined>;
|
|
98
|
+
export declare function upsertOnConflict<TRow = AnyRow>(db: ExecutorManagedWriteCapability, table: EntityTableMeta, values: Record<string, unknown>, options: UpsertOnConflictOptions): Promise<TRow | undefined>;
|
|
99
|
+
export declare function upsertByPk<TRow = AnyRow>(db: AnyDb, table: WritableTable, values: Record<string, unknown>, updateOnConflict?: Record<string, unknown>): Promise<TRow | undefined>;
|
|
100
|
+
export declare function upsertByPk<TRow = AnyRow>(db: ExecutorManagedWriteCapability, table: EntityTableMeta, values: Record<string, unknown>, updateOnConflict?: Record<string, unknown>): Promise<TRow | undefined>;
|
|
93
101
|
export type InsertOnConflictDoNothingOptions = {
|
|
94
102
|
readonly conflictKeys?: readonly string[];
|
|
95
103
|
};
|
|
96
|
-
export declare function insertOnConflictDoNothing<TRow =
|
|
104
|
+
export declare function insertOnConflictDoNothing<TRow = AnyRow>(db: AnyDb, table: WritableTable, values: Record<string, unknown>, options?: InsertOnConflictDoNothingOptions): Promise<TRow | undefined>;
|
|
105
|
+
export declare function insertOnConflictDoNothing<TRow = AnyRow>(db: ExecutorManagedWriteCapability, table: EntityTableMeta, values: Record<string, unknown>, options?: InsertOnConflictDoNothingOptions): Promise<TRow | undefined>;
|
|
97
106
|
export type IncrementCounterOptions = {
|
|
98
107
|
readonly conflictKeys?: readonly string[];
|
|
99
108
|
/** Extra SET clauses on conflict (e.g. lastUpdatedAt: sql\`NOW()\`). */
|
|
100
109
|
readonly set?: Record<string, unknown>;
|
|
101
110
|
};
|
|
102
|
-
export declare function incrementCounter<TRow =
|
|
111
|
+
export declare function incrementCounter<TRow = AnyRow>(db: AnyDb, table: WritableTable, values: Record<string, unknown>, increments: Record<string, number>, options?: IncrementCounterOptions): Promise<TRow | undefined>;
|
|
112
|
+
export declare function incrementCounter<TRow = AnyRow>(db: ExecutorManagedWriteCapability, table: EntityTableMeta, values: Record<string, unknown>, increments: Record<string, number>, options?: IncrementCounterOptions): Promise<TRow | undefined>;
|
|
103
113
|
export declare function deleteManyBatched(db: AnyDb, table: WritableTable, where: WhereObject, options: DeleteManyBatchedOptions): Promise<DeleteManyBatchedResult>;
|
|
114
|
+
export declare function deleteManyBatched(db: ExecutorManagedWriteCapability, table: EntityTableMeta, where: WhereObject, options: DeleteManyBatchedOptions): Promise<DeleteManyBatchedResult>;
|
|
104
115
|
export declare function transaction<T>(db: AnyDb, fn: (tx: DbTx) => Promise<T>): Promise<T>;
|
|
105
116
|
export declare function runInNewTransaction<T>(db: unknown, fn: (tx: DbRunner) => Promise<T>): Promise<T>;
|
package/dist/db/query.js
CHANGED
|
@@ -24,6 +24,7 @@ import { InternalError } from "../errors/index.js";
|
|
|
24
24
|
import { createFallbackLogger } from "../logging/index.js";
|
|
25
25
|
import { parseJsonSafe } from "../utils/safe-json.js";
|
|
26
26
|
import { SQL_EXPR_BRAND } from "./dialect.js";
|
|
27
|
+
import { executorManagedWriteDenied } from "./executor-managed-tables.js";
|
|
27
28
|
import { extractPgError } from "./pg-error.js";
|
|
28
29
|
import { toSnakeCase } from "./table-builder.js";
|
|
29
30
|
import { asEntityTableMeta, requireEntityTableMeta } from "./table-introspection.js";
|
|
@@ -77,42 +78,27 @@ export function asRawClient(db) {
|
|
|
77
78
|
export function runnerCanBeginTransaction(db) {
|
|
78
79
|
return hasBegin(asRawClient(db));
|
|
79
80
|
}
|
|
80
|
-
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// the savepoint-scoped tx; run every confined statement on THAT handle.
|
|
81
|
+
function savepointRunnerOf(sp) {
|
|
82
|
+
// @cast-boundary db-runner savepoint handle → TRunner at the single seam (boundRunner re-wraps savepoint handles with the caller's capability)
|
|
83
|
+
return sp;
|
|
84
|
+
}
|
|
85
85
|
export async function runInSavepoint(tx, fn) {
|
|
86
86
|
const raw = asRawClient(tx);
|
|
87
87
|
if (typeof raw.savepoint !== "function") {
|
|
88
88
|
throw new Error("runInSavepoint: transaction handle has no savepoint() — pass the driver tx (postgres.js TransactionSql).");
|
|
89
89
|
}
|
|
90
|
-
return raw.savepoint(fn);
|
|
90
|
+
return raw.savepoint((sp) => fn(savepointRunnerOf(sp)));
|
|
91
91
|
}
|
|
92
|
-
// Same error-confinement as runInSavepoint, but for call sites that don't
|
|
93
|
-
// know whether `db` is a bare pool connection or an active transaction
|
|
94
|
-
// (e.g. the CRUD executor, invoked both from a dispatcher tx and directly
|
|
95
|
-
// against the pool by seeds/tests). A pool connection has no savepoint() —
|
|
96
|
-
// and doesn't need one, since each statement there is its own auto-committed
|
|
97
|
-
// unit and a failed statement can't poison anything downstream.
|
|
98
|
-
//
|
|
99
|
-
// `.savepoint` being present isn't proof the transaction is still open: a
|
|
100
|
-
// committed TransactionSql-shaped db still exposes `.savepoint`, and calling
|
|
101
|
-
// it fails with PG 25P01 ("no active sql transaction") since there's no
|
|
102
|
-
// BEGIN left to nest into. That is now a programming error, not something
|
|
103
|
-
// to paper over by re-running `fn` without the savepoint's isolation.
|
|
104
92
|
export async function runInSavepointIfSupported(db, fn) {
|
|
105
93
|
const raw = asRawClient(db);
|
|
106
|
-
// @cast-boundary db-runner savepoint handle → DbRunner at the single seam
|
|
107
|
-
const asRunner = (sp) => sp;
|
|
108
94
|
if (typeof raw.savepoint !== "function")
|
|
109
|
-
return fn(
|
|
95
|
+
return fn(savepointRunnerOf(db));
|
|
110
96
|
// An error from inside fn must never be remapped, even if it carries a closed-connection code.
|
|
111
97
|
let fnStarted = false;
|
|
112
98
|
try {
|
|
113
99
|
return await raw.savepoint((sp) => {
|
|
114
100
|
fnStarted = true;
|
|
115
|
-
return fn(
|
|
101
|
+
return fn(savepointRunnerOf(sp));
|
|
116
102
|
});
|
|
117
103
|
}
|
|
118
104
|
catch (e) {
|
|
@@ -869,7 +855,6 @@ function selectColumnList(info, fields) {
|
|
|
869
855
|
})
|
|
870
856
|
.join(", ");
|
|
871
857
|
}
|
|
872
|
-
// biome-ignore lint/suspicious/noExplicitAny: opt-in default loosens row type for unannotated test fixtures
|
|
873
858
|
export async function selectMany(db, table, where, options) {
|
|
874
859
|
const scoped = tenantDbDelegate(db);
|
|
875
860
|
if (scoped) {
|
|
@@ -904,7 +889,6 @@ export async function selectMany(db, table, where, options) {
|
|
|
904
889
|
const raw = await unsafeReadRetrying(db, sqlText, values);
|
|
905
890
|
return coerceRows(raw, info);
|
|
906
891
|
}
|
|
907
|
-
// biome-ignore lint/suspicious/noExplicitAny: see selectMany default
|
|
908
892
|
export async function fetchOne(db, table, where) {
|
|
909
893
|
const rows = await selectMany(db, table, where, { limit: 1 });
|
|
910
894
|
return rows[0];
|
|
@@ -979,14 +963,13 @@ export async function selectInnerJoin(db, spec) {
|
|
|
979
963
|
};
|
|
980
964
|
});
|
|
981
965
|
}
|
|
982
|
-
// Bulk INSERT — same shape as insertOne but takes an array of rows and
|
|
983
|
-
// produces one multi-VALUES statement. Mirrors drizzle's
|
|
984
|
-
// `db.insert(t).values(rows[])`. Empty input is a no-op.
|
|
985
|
-
// biome-ignore lint/suspicious/noExplicitAny: see selectMany default
|
|
986
966
|
export async function insertMany(db, table, rows) {
|
|
987
967
|
if (rows.length === 0)
|
|
988
968
|
return [];
|
|
989
969
|
assertNotTenantScoped(db, "insertMany");
|
|
970
|
+
const managedDenied = executorManagedWriteDenied(db, table, "insertMany");
|
|
971
|
+
if (managedDenied)
|
|
972
|
+
throw managedDenied;
|
|
990
973
|
const info = extractTableInfo(table);
|
|
991
974
|
// Use the column-set from the first row; assume all rows share keys.
|
|
992
975
|
const firstRow = rows[0];
|
|
@@ -1022,12 +1005,14 @@ export async function insertMany(db, table, rows) {
|
|
|
1022
1005
|
});
|
|
1023
1006
|
return coerceRows(raw, info);
|
|
1024
1007
|
}
|
|
1025
|
-
// biome-ignore lint/suspicious/noExplicitAny: see selectMany default
|
|
1026
1008
|
export async function insertOne(db, table, values) {
|
|
1027
1009
|
const scoped = tenantDbDelegate(db);
|
|
1028
1010
|
if (scoped) {
|
|
1029
1011
|
return scoped.insertOne(table, values);
|
|
1030
1012
|
}
|
|
1013
|
+
const managedDenied = executorManagedWriteDenied(db, table, "insertOne");
|
|
1014
|
+
if (managedDenied)
|
|
1015
|
+
throw managedDenied;
|
|
1031
1016
|
const info = extractTableInfo(table);
|
|
1032
1017
|
const entries = Object.entries(values)
|
|
1033
1018
|
.filter(([k]) => info.hasColumn(k))
|
|
@@ -1058,12 +1043,14 @@ export async function insertOne(db, table, values) {
|
|
|
1058
1043
|
return undefined;
|
|
1059
1044
|
return coerceRow(first, info);
|
|
1060
1045
|
}
|
|
1061
|
-
// biome-ignore lint/suspicious/noExplicitAny: see selectMany default
|
|
1062
1046
|
export async function updateMany(db, table, set, where) {
|
|
1063
1047
|
const scoped = tenantDbDelegate(db);
|
|
1064
1048
|
if (scoped) {
|
|
1065
1049
|
return scoped.updateMany(table, set, where);
|
|
1066
1050
|
}
|
|
1051
|
+
const managedDenied = executorManagedWriteDenied(db, table, "updateMany");
|
|
1052
|
+
if (managedDenied)
|
|
1053
|
+
throw managedDenied;
|
|
1067
1054
|
const info = extractTableInfo(table);
|
|
1068
1055
|
const setEntries = Object.entries(set).map(([k, v]) => {
|
|
1069
1056
|
const col = info.columnOf(k);
|
|
@@ -1103,6 +1090,9 @@ export async function deleteMany(db, table, where) {
|
|
|
1103
1090
|
if (scoped) {
|
|
1104
1091
|
return scoped.deleteMany(table, where);
|
|
1105
1092
|
}
|
|
1093
|
+
const managedDenied = executorManagedWriteDenied(db, table, "deleteMany");
|
|
1094
|
+
if (managedDenied)
|
|
1095
|
+
throw managedDenied;
|
|
1106
1096
|
const info = extractTableInfo(table);
|
|
1107
1097
|
const w = buildWhereClause(info, where, 1);
|
|
1108
1098
|
let sqlText = `DELETE FROM ${quoteIdent(info.name)}`;
|
|
@@ -1277,9 +1267,15 @@ export async function aggregateWhere(db, table, spec, where = {}) {
|
|
|
1277
1267
|
};
|
|
1278
1268
|
});
|
|
1279
1269
|
}
|
|
1280
|
-
|
|
1281
|
-
|
|
1270
|
+
export function upsertOnConflict(db, table, values, options) {
|
|
1271
|
+
return upsertOnConflictChecked(db, table, values, options);
|
|
1272
|
+
}
|
|
1273
|
+
// upsertByPk shares this body; the checks inside gate the write, so it needs no overload pair.
|
|
1274
|
+
async function upsertOnConflictChecked(db, table, values, options) {
|
|
1282
1275
|
assertNotTenantScoped(db, "upsertOnConflict");
|
|
1276
|
+
const managedDenied = executorManagedWriteDenied(db, table, "upsertOnConflict");
|
|
1277
|
+
if (managedDenied)
|
|
1278
|
+
throw managedDenied;
|
|
1283
1279
|
const info = extractTableInfo(table);
|
|
1284
1280
|
const entries = insertEntries(info, values);
|
|
1285
1281
|
const conflictCols = resolveConflictColumns(table, info, options.conflictKeys);
|
|
@@ -1321,19 +1317,23 @@ export async function upsertOnConflict(db, table, values, options) {
|
|
|
1321
1317
|
return undefined;
|
|
1322
1318
|
return coerceRow(first, info);
|
|
1323
1319
|
}
|
|
1324
|
-
// biome-ignore lint/suspicious/noExplicitAny: see selectMany default
|
|
1325
1320
|
export async function upsertByPk(db, table, values, updateOnConflict) {
|
|
1321
|
+
const managedDenied = executorManagedWriteDenied(db, table, "upsertByPk");
|
|
1322
|
+
if (managedDenied)
|
|
1323
|
+
throw managedDenied;
|
|
1326
1324
|
const info = extractTableInfo(table);
|
|
1327
1325
|
const conflictKeys = resolveConflictColumns(table, info, undefined);
|
|
1328
1326
|
const fieldKeys = conflictKeys.map((col) => info.fieldOf(col));
|
|
1329
|
-
return
|
|
1327
|
+
return upsertOnConflictChecked(db, table, values, {
|
|
1330
1328
|
conflictKeys: fieldKeys,
|
|
1331
1329
|
...(updateOnConflict !== undefined ? { update: updateOnConflict } : {}),
|
|
1332
1330
|
});
|
|
1333
1331
|
}
|
|
1334
|
-
// biome-ignore lint/suspicious/noExplicitAny: see selectMany default
|
|
1335
1332
|
export async function insertOnConflictDoNothing(db, table, values, options) {
|
|
1336
1333
|
assertNotTenantScoped(db, "insertOnConflictDoNothing");
|
|
1334
|
+
const managedDenied = executorManagedWriteDenied(db, table, "insertOnConflictDoNothing");
|
|
1335
|
+
if (managedDenied)
|
|
1336
|
+
throw managedDenied;
|
|
1337
1337
|
const info = extractTableInfo(table);
|
|
1338
1338
|
const entries = insertEntries(info, values);
|
|
1339
1339
|
const conflictCols = resolveConflictColumns(table, info, options?.conflictKeys);
|
|
@@ -1349,9 +1349,11 @@ export async function insertOnConflictDoNothing(db, table, values, options) {
|
|
|
1349
1349
|
return undefined;
|
|
1350
1350
|
return coerceRow(first, info);
|
|
1351
1351
|
}
|
|
1352
|
-
// biome-ignore lint/suspicious/noExplicitAny: see selectMany default
|
|
1353
1352
|
export async function incrementCounter(db, table, values, increments, options = {}) {
|
|
1354
1353
|
assertNotTenantScoped(db, "incrementCounter");
|
|
1354
|
+
const managedDenied = executorManagedWriteDenied(db, table, "incrementCounter");
|
|
1355
|
+
if (managedDenied)
|
|
1356
|
+
throw managedDenied;
|
|
1355
1357
|
const info = extractTableInfo(table);
|
|
1356
1358
|
const entries = insertEntries(info, values);
|
|
1357
1359
|
const conflictCols = resolveConflictColumns(table, info, options.conflictKeys);
|
|
@@ -1402,6 +1404,9 @@ export async function incrementCounter(db, table, values, increments, options =
|
|
|
1402
1404
|
return coerceRow(first, info);
|
|
1403
1405
|
}
|
|
1404
1406
|
export async function deleteManyBatched(db, table, where, options) {
|
|
1407
|
+
const managedDenied = executorManagedWriteDenied(db, table, "deleteManyBatched");
|
|
1408
|
+
if (managedDenied)
|
|
1409
|
+
throw managedDenied;
|
|
1405
1410
|
const info = extractTableInfo(table);
|
|
1406
1411
|
const w = buildWhereClause(info, where, 1);
|
|
1407
1412
|
if (!w.sqlText) {
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
import type { Registry } from "../engine/index.ts";
|
|
1
2
|
import type { ReferenceDataDef } from "../engine/types/index.ts";
|
|
2
3
|
import type { DbConnection } from "./connection.ts";
|
|
3
|
-
import type { TableColumns } from "./dialect.ts";
|
|
4
|
-
type Table = TableColumns<any>;
|
|
5
4
|
/**
|
|
6
5
|
* Seed reference data at boot time.
|
|
7
6
|
* For each ReferenceDataDef: upsert rows (insert missing, update changed, never delete).
|
|
8
7
|
* Upsert key defaults to the first field in the data object.
|
|
8
|
+
*
|
|
9
|
+
* Rows go through the entity's event-store executor as the system user in the
|
|
10
|
+
* SYSTEM_TENANT_ID stream, so the seed is replayable on a projection rebuild.
|
|
9
11
|
*/
|
|
10
|
-
export declare function seedReferenceData(defs: readonly ReferenceDataDef[],
|
|
12
|
+
export declare function seedReferenceData(defs: readonly ReferenceDataDef[], registry: Registry, db: DbConnection): Promise<{
|
|
11
13
|
inserted: number;
|
|
12
14
|
updated: number;
|
|
13
15
|
}>;
|
|
14
|
-
export {};
|
|
@@ -1,21 +1,26 @@
|
|
|
1
|
-
import { fetchOne
|
|
1
|
+
import { fetchOne } from "../db/query.js";
|
|
2
|
+
import { createSystemUser } from "../engine/system-user.js";
|
|
2
3
|
import { SYSTEM_TENANT_ID } from "../engine/types/index.js";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
4
|
+
import { reraiseAsKumikoError } from "../errors/index.js";
|
|
5
|
+
import { entityTableFromRegistry } from "./entity-table-from-registry.js";
|
|
6
|
+
import { createEventStoreExecutor } from "./event-store-executor.js";
|
|
7
|
+
import { createTenantDb } from "./tenant-db.js";
|
|
8
8
|
/**
|
|
9
9
|
* Seed reference data at boot time.
|
|
10
10
|
* For each ReferenceDataDef: upsert rows (insert missing, update changed, never delete).
|
|
11
11
|
* Upsert key defaults to the first field in the data object.
|
|
12
|
+
*
|
|
13
|
+
* Rows go through the entity's event-store executor as the system user in the
|
|
14
|
+
* SYSTEM_TENANT_ID stream, so the seed is replayable on a projection rebuild.
|
|
12
15
|
*/
|
|
13
|
-
export async function seedReferenceData(defs,
|
|
16
|
+
export async function seedReferenceData(defs, registry, db) {
|
|
14
17
|
let inserted = 0;
|
|
15
18
|
let updated = 0;
|
|
19
|
+
const by = createSystemUser(SYSTEM_TENANT_ID);
|
|
20
|
+
const tdb = createTenantDb({ db, tenantId: SYSTEM_TENANT_ID, mode: "system" });
|
|
16
21
|
for (const def of defs) {
|
|
17
|
-
const
|
|
18
|
-
if (!
|
|
22
|
+
const entity = registry.getEntity(def.entityName);
|
|
23
|
+
if (!entity)
|
|
19
24
|
continue;
|
|
20
25
|
if (def.data.length === 0)
|
|
21
26
|
continue;
|
|
@@ -26,11 +31,13 @@ export async function seedReferenceData(defs, tables, db) {
|
|
|
26
31
|
if (!firstKey)
|
|
27
32
|
continue;
|
|
28
33
|
const upsertKey = def.upsertKey ?? firstKey;
|
|
34
|
+
const table = entityTableFromRegistry(registry, def.entityName, entity);
|
|
35
|
+
const executor = createEventStoreExecutor(table, entity, { entityName: def.entityName });
|
|
29
36
|
for (const row of def.data) {
|
|
30
37
|
const keyValue = row[upsertKey];
|
|
31
38
|
if (keyValue === undefined)
|
|
32
39
|
continue;
|
|
33
|
-
const existing =
|
|
40
|
+
const existing = await fetchOne(db, table, { [upsertKey]: keyValue });
|
|
34
41
|
if (existing) {
|
|
35
42
|
const changes = {};
|
|
36
43
|
for (const [field, value] of Object.entries(row)) {
|
|
@@ -41,21 +48,16 @@ export async function seedReferenceData(defs, tables, db) {
|
|
|
41
48
|
}
|
|
42
49
|
}
|
|
43
50
|
if (Object.keys(changes).length > 0) {
|
|
44
|
-
await
|
|
51
|
+
const result = await executor.update({ id: existing.id, version: existing.version, changes }, by, tdb);
|
|
52
|
+
if (!result.isSuccess)
|
|
53
|
+
throw reraiseAsKumikoError(result.error);
|
|
45
54
|
updated++;
|
|
46
55
|
}
|
|
47
56
|
}
|
|
48
57
|
else {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (hasColumn(table, "tenantId"))
|
|
53
|
-
values["tenantId"] = SYSTEM_TENANT_ID;
|
|
54
|
-
if (hasColumn(table, "version"))
|
|
55
|
-
values["version"] = 1;
|
|
56
|
-
if (hasColumn(table, "insertedAt"))
|
|
57
|
-
values["insertedAt"] = Temporal.Now.instant();
|
|
58
|
-
await insertOne(db, table, values);
|
|
58
|
+
const result = await executor.create({ ...row, tenantId: SYSTEM_TENANT_ID }, by, tdb);
|
|
59
|
+
if (!result.isSuccess)
|
|
60
|
+
throw reraiseAsKumikoError(result.error);
|
|
59
61
|
inserted++;
|
|
60
62
|
}
|
|
61
63
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
/// <reference types="temporal-polyfill/global" preserve="true" />
|
|
2
|
-
import type { ExecutorOnly } from "@cosmicdrift/kumiko-types/executor-brand";
|
|
3
2
|
import type { TenancyBrand } from "@cosmicdrift/kumiko-types/tenancy-brand";
|
|
4
3
|
import type { EntityDefinition, EntityRelations, FieldDefinition, FieldsMap } from "../engine/types/index.ts";
|
|
5
4
|
import type { Table } from "./event-store-executor-context.ts";
|
|
@@ -194,11 +193,11 @@ type Flatten<T> = {
|
|
|
194
193
|
/** The row `executor.list()` / `executor.detail()` return for entity `E`. A wide
|
|
195
194
|
* `EntityDefinition` (fields without literal keys) yields `Record<string, unknown>`. */
|
|
196
195
|
export type EntityRow<E extends EntityDefinition = EntityDefinition> = string extends keyof E["fields"] ? Record<string, unknown> : Flatten<RowOfColumns<BaseColumnsType<E>> & SoftDeleteRow<E> & RowForFields<E["fields"]>>;
|
|
197
|
-
export type {
|
|
196
|
+
export type { ExecutorManagedRunner, ExecutorManagedWriteCapability, Writable, WritableSchemaTable, WritableTable, } from "@cosmicdrift/kumiko-types/executor-brand";
|
|
198
197
|
export type { EntityTenancy, TenancyBrand } from "@cosmicdrift/kumiko-types/tenancy-brand";
|
|
199
198
|
type EntityTableTenancy<E extends EntityDefinition> = [NonNullable<E["tenancy"]>] extends [never] ? "tenant" : [NonNullable<E["tenancy"]>] extends ["global"] ? "global" : "tenant";
|
|
200
199
|
export type EntityTable<E extends EntityDefinition = EntityDefinition> = TableColumns<// biome-ignore lint/suspicious/noExplicitAny: drizzle's internal table-config stays generic; we layer typed columns on top via the intersection below.
|
|
201
|
-
any> & BaseColumnsType<E> & SoftDeleteColumnsType & ColumnsForEntity<E["fields"]> &
|
|
200
|
+
any> & BaseColumnsType<E> & SoftDeleteColumnsType & ColumnsForEntity<E["fields"]> & TenancyBrand<EntityTableTenancy<E>>;
|
|
202
201
|
export declare function buildBaseColumns(softDelete: boolean, idType?: "serial" | "uuid"): {
|
|
203
202
|
id: ColumnBuilder<string> | ColumnBuilder<number>;
|
|
204
203
|
tenantId: ColumnBuilder<string>;
|
package/dist/db/table-builder.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { KUMIKO_META_SYMBOL } from "@cosmicdrift/kumiko-types/schema-table-types";
|
|
2
2
|
import { toSnakeCase } from "../utils/case.js";
|
|
3
3
|
import { assertUnreachable } from "../utils/index.js";
|
|
4
|
+
import { markExecutorManagedTable } from "./executor-managed-tables.js";
|
|
4
5
|
import { columnHandleName } from "./table-introspection.js";
|
|
5
6
|
export { toSnakeCase } from "../utils/case.js";
|
|
6
7
|
import { bigint, boolean, decimalColumn, doublePrecision, index, instant, integer, isSqlExpression, jsonb, moneyAmount, table as pgTable, plainDate, serial, sql, text, uniqueIndex, uuid, } from "./dialect.js";
|
|
@@ -420,5 +421,6 @@ export function buildEntityTable(entityName, entity, options) {
|
|
|
420
421
|
return indexes;
|
|
421
422
|
});
|
|
422
423
|
stampEntityTableMeta(built, entity);
|
|
424
|
+
markExecutorManagedTable(built);
|
|
423
425
|
return built;
|
|
424
426
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { EntityTableMeta } from "@cosmicdrift/kumiko-types/entity-table-meta-types";
|
|
2
|
+
import type { Writable } from "@cosmicdrift/kumiko-types/executor-brand";
|
|
2
3
|
export declare function isEntityTableMeta(value: unknown): value is EntityTableMeta;
|
|
3
4
|
export declare function asEntityTableMeta(table: unknown): EntityTableMeta | undefined;
|
|
4
5
|
export declare function requireEntityTableMeta(table: unknown, tableName: string): EntityTableMeta;
|
|
@@ -14,3 +15,4 @@ export declare function columnHandleName(table: unknown, field: string): string
|
|
|
14
15
|
export declare function hasFieldProperty(table: unknown, field: string): boolean;
|
|
15
16
|
/** SQL column name of a JS field via the column-handle map; undefined when not mapped. */
|
|
16
17
|
export declare function columnSqlNameOf(table: unknown, field: string): string | undefined;
|
|
18
|
+
export declare function brandWritable<T extends EntityTableMeta>(table: T): T & Writable;
|
|
@@ -74,3 +74,7 @@ export function columnSqlNameOf(table, field) {
|
|
|
74
74
|
const name = handle["name"];
|
|
75
75
|
return typeof name === "string" ? name : undefined;
|
|
76
76
|
}
|
|
77
|
+
export function brandWritable(table) {
|
|
78
|
+
// @cast-boundary phantom-brand — Writable is a phantom marker with no runtime representation.
|
|
79
|
+
return table;
|
|
80
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { TenantDb } from "@cosmicdrift/kumiko-types/tenant-db-types";
|
|
2
2
|
import { InternalError } from "../errors/index.ts";
|
|
3
3
|
import type { DbRunner } from "./connection.ts";
|
|
4
|
+
export declare function isBoundTenantDb(value: unknown): value is TenantDb;
|
|
5
|
+
export declare function isDbRunner(value: unknown): value is DbRunner;
|
|
4
6
|
export declare function bindTenantDbRunner(tenantDb: TenantDb, runner: DbRunner): void;
|
|
5
7
|
export declare function tenantDbRunner(tenantDb: TenantDb): DbRunner;
|
|
6
8
|
export declare function denyTenantDbWrites(tenantDb: TenantDb, reason: string): void;
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
import { propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
1
2
|
import { InternalError } from "../errors/index.js";
|
|
2
3
|
const tenantDbRunners = new WeakMap();
|
|
4
|
+
export function isBoundTenantDb(value) {
|
|
5
|
+
return typeof value === "object" && value !== null && tenantDbRunners.has(value);
|
|
6
|
+
}
|
|
7
|
+
export function isDbRunner(value) {
|
|
8
|
+
return typeof propertyOf(value, "unsafe") === "function";
|
|
9
|
+
}
|
|
3
10
|
export function bindTenantDbRunner(tenantDb, runner) {
|
|
4
11
|
tenantDbRunners.set(tenantDb, runner);
|
|
5
12
|
}
|
package/dist/db/tenant-db.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { EntityTableMeta } from "@cosmicdrift/kumiko-types/entity-table-meta-types";
|
|
2
|
+
import type { ExecutorManagedRunner } from "@cosmicdrift/kumiko-types/executor-brand";
|
|
2
3
|
import type { EscapeHatchDeclaration, EscapeHatchReporter, EscapeHatchTarget } from "@cosmicdrift/kumiko-types/handlers";
|
|
3
4
|
import type { SchemaTable } from "@cosmicdrift/kumiko-types/schema-table-types";
|
|
4
|
-
import { type TenantDb, type TenantDbMode, type UncheckedSystemDb } from "@cosmicdrift/kumiko-types/tenant-db-types";
|
|
5
|
+
import { type ExecutorManagedTenantDb, type TenantDb, type TenantDbMode, type UncheckedSystemDb } from "@cosmicdrift/kumiko-types/tenant-db-types";
|
|
5
6
|
import { asRawClient } from "../db/query.ts";
|
|
6
7
|
import type { EntityDefinition } from "../engine/types/fields.ts";
|
|
7
8
|
import { type TenantId } from "../engine/types/identifiers.ts";
|
|
@@ -10,7 +11,9 @@ import { type Meter, type Tracer } from "../observability/index.ts";
|
|
|
10
11
|
import type { DbRunner } from "./connection.ts";
|
|
11
12
|
import { tableNameOf } from "./table-introspection.ts";
|
|
12
13
|
type Table = SchemaTable;
|
|
13
|
-
export { SYSTEM_SCOPE_CHECK_BRAND, type TenantDb, type TenantDbMode, type UncheckedSystemDb, } from "@cosmicdrift/kumiko-types/tenant-db-types";
|
|
14
|
+
export { type ExecutorManagedTenantDb, SYSTEM_SCOPE_CHECK_BRAND, type TenantDb, type TenantDbMode, type UncheckedSystemDb, } from "@cosmicdrift/kumiko-types/tenant-db-types";
|
|
15
|
+
export declare function withExecutorManagedWrites(runner: DbRunner): ExecutorManagedRunner;
|
|
16
|
+
export declare function withoutExecutorManagedWrites(runner: DbRunner): DbRunner;
|
|
14
17
|
export declare function assertPersonalDataWrite(db: TenantDb, tableName: string, keys: readonly string[], entity: EntityDefinition): void;
|
|
15
18
|
export declare function unsafeRawForDeclaredStep(holder: TenantDb | UncheckedSystemDb, reason: string): DbRunner;
|
|
16
19
|
export declare function withSystemDbUnsafeRawGrant(systemDb: UncheckedSystemDb, grant: EscapeHatchDeclaration | undefined, deniedCallerLabel: string): UncheckedSystemDb;
|
|
@@ -53,6 +56,10 @@ export type CreateTenantDbOptions = {
|
|
|
53
56
|
readonly signal?: AbortSignal;
|
|
54
57
|
readonly grants?: TenantDbGrants;
|
|
55
58
|
};
|
|
59
|
+
export declare function createTenantDb(options: CreateTenantDbOptions & {
|
|
60
|
+
readonly db: ExecutorManagedRunner;
|
|
61
|
+
}): ExecutorManagedTenantDb;
|
|
56
62
|
export declare function createTenantDb(options: CreateTenantDbOptions): TenantDb;
|
|
63
|
+
export declare function createTenantDataDestroyDb(options: Omit<CreateTenantDbOptions, "mode">): ExecutorManagedTenantDb;
|
|
57
64
|
export declare function createProjectingTenantDb(db: DbRunner, tenantId: TenantId, projectionRegistry: Registry | undefined): TenantDb;
|
|
58
65
|
export { asRawClient };
|