@cosmicdrift/kumiko-framework 0.367.0 → 0.368.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 +1 -7
- package/README.md +1 -1
- package/agents/examples/access-control.md +2 -2
- package/agents/examples/anonymous-access-multitenant.md +2 -2
- package/agents/examples/anonymous-access.md +3 -3
- package/agents/examples/apex-landing.md +1 -1
- package/agents/examples/apex-surface-auth.md +6 -7
- package/agents/examples/api-worker-split.md +7 -8
- package/agents/examples/auth-claims.md +2 -1
- package/agents/examples/auth-foundation-providers.md +3 -3
- package/agents/examples/auth-mfa.md +3 -4
- package/agents/examples/basic-entity.md +3 -4
- package/agents/examples/chat-channels.md +4 -4
- package/agents/examples/compliance-profiles-demo.md +2 -3
- package/agents/examples/cross-feature-events.md +4 -4
- package/agents/examples/crypto-shredding-hr.md +14 -9
- package/agents/examples/currencies-global.md +3 -4
- package/agents/examples/currencies-per-tenant.md +3 -4
- package/agents/examples/custom-fields-basic.md +2 -3
- package/agents/examples/custom-handlers.md +3 -5
- package/agents/examples/dashboard-stats.md +2 -3
- package/agents/examples/delivery-notifications.md +5 -6
- package/agents/examples/e2e-generator.md +1 -1
- package/agents/examples/embedded-entity-form.md +2 -3
- package/agents/examples/embedded.md +2 -3
- package/agents/examples/encrypted-tenant-config.md +5 -6
- package/agents/examples/error-contract.md +3 -4
- package/agents/examples/event-sourcing.md +19 -23
- package/agents/examples/feature-toggles.md +4 -4
- package/agents/examples/field-access.md +3 -4
- package/agents/examples/files-post-processing.md +12 -13
- package/agents/examples/golden-path.md +208 -0
- package/agents/examples/idempotency.md +2 -2
- package/agents/examples/inbound-mail-triage.md +7 -7
- package/agents/examples/lane-routing.md +3 -3
- package/agents/examples/ledger-banking.md +2 -3
- package/agents/examples/ledger-invoicing.md +2 -3
- package/agents/examples/legal-pages.md +1 -1
- package/agents/examples/lifecycle-hooks.md +3 -3
- package/agents/examples/managed-config.md +3 -4
- package/agents/examples/managed-pages.md +2 -2
- package/agents/examples/notes-history-basic.md +4 -5
- package/agents/examples/ownership.md +3 -5
- package/agents/examples/pdf-audit-export.md +2 -3
- package/agents/examples/pipeline-basics.md +2 -2
- package/agents/examples/public-share-token.md +3 -3
- package/agents/examples/rate-limiting.md +2 -1
- package/agents/examples/realtime-sse.md +2 -3
- package/agents/examples/record-detail-layout.md +1 -1
- package/agents/examples/reference-data.md +3 -3
- package/agents/examples/relations.md +2 -2
- package/agents/examples/screens-nav.md +1 -1
- package/agents/examples/search.md +2 -2
- package/agents/examples/secrets-demo.md +8 -8
- package/agents/examples/security-baseline.md +2 -2
- package/agents/examples/session-revocation.md +7 -8
- package/agents/examples/state-machine.md +181 -165
- package/agents/examples/tags-basic.md +2 -3
- package/agents/examples/tenant-isolation.md +2 -3
- package/agents/examples/tenant-settings.md +2 -2
- package/agents/examples/tier-admin.md +3 -4
- package/agents/examples/timezones.md +3 -3
- package/agents/examples/user-data-rights.md +12 -8
- package/agents/examples/user-profile.md +3 -3
- package/agents/examples/webhook-step.md +2 -3
- package/agents/examples/wizard-form.md +4 -5
- package/agents/examples/workflow-engine.md +7 -3
- package/agents/examples/writeform-section.md +1 -1
- package/agents/examples.md +1 -0
- package/dist/consumer-cli.js +2 -3
- package/dist/crypto/index.d.ts +1 -1
- package/dist/crypto/index.js +1 -1
- package/dist/crypto/owner-transition-rekey.js +2 -0
- package/dist/crypto/pg-kms-adapter.js +1 -1
- package/dist/crypto/subject-resolver.d.ts +2 -0
- package/dist/crypto/subject-resolver.js +30 -15
- package/dist/db/bun-db-deprecated.d.ts +10 -0
- package/dist/{bun-db/index.js → db/bun-db-deprecated.js} +4 -3
- package/dist/db/config-seed.js +1 -1
- package/dist/db/connection.d.ts +23 -5
- package/dist/db/connection.js +10 -5
- package/dist/db/dialect.js +1 -1
- package/dist/db/entity-field-encryption.d.ts +5 -0
- package/dist/db/entity-field-encryption.js +29 -0
- package/dist/db/entity-table-meta.js +6 -5
- package/dist/db/event-store-executor-read.js +26 -5
- package/dist/db/index.d.ts +4 -4
- package/dist/db/index.js +1 -1
- package/dist/db/migrate-runner.js +1 -1
- package/dist/db/queries/backfill-pii.js +16 -9
- package/dist/db/queries/ddl.js +1 -1
- package/dist/db/queries/event-store.d.ts +2 -0
- package/dist/db/queries/event-store.js +26 -0
- package/dist/db/queries/test-stack.js +1 -1
- package/dist/db/query.d.ts +105 -1
- package/dist/db/query.js +1450 -3
- package/dist/{stack/table-helpers.d.ts → db/table-push.d.ts} +5 -15
- package/dist/{stack/table-helpers.js → db/table-push.js} +6 -46
- package/dist/db/tenant-db.d.ts +11 -0
- package/dist/db/tenant-db.js +51 -12
- package/dist/derivatives/derivatives-context.d.ts +1 -1
- package/dist/derivatives/derivatives-context.js +1 -1
- package/dist/engine/boot-validator/gdpr-storage.js +3 -3
- package/dist/engine/boot-validator/pii-retention.js +3 -3
- package/dist/engine/boot-validator/record-owned.js +3 -3
- package/dist/engine/boot-validator/screens.js +8 -1
- package/dist/engine/bridge-stub.d.ts +4 -0
- package/dist/engine/bridge-stub.js +62 -0
- package/dist/engine/event-helpers.d.ts +2 -1
- package/dist/engine/event-helpers.js +1 -0
- package/dist/engine/feature-config-events-jobs.js +2 -2
- package/dist/engine/index.d.ts +1 -0
- package/dist/engine/index.js +1 -0
- package/dist/es-ops/operations-schema.js +1 -1
- package/dist/event-store/admin-api.js +1 -1
- package/dist/event-store/archive.js +1 -1
- package/dist/event-store/errors.d.ts +2 -1
- package/dist/event-store/errors.js +6 -2
- package/dist/event-store/event-store.js +17 -9
- package/dist/event-store/events-schema.js +1 -1
- package/dist/event-store/rebuild-dead-letter.js +3 -3
- package/dist/event-store/snapshot.js +1 -1
- package/dist/event-store/upcaster-dead-letter.js +3 -3
- package/dist/files/create-file-ref-from-bytes.js +1 -1
- package/dist/files/file-routes.js +1 -1
- package/dist/internal/stack-support.d.ts +12 -0
- package/dist/internal/stack-support.js +12 -0
- package/dist/internal/testing.d.ts +4 -0
- package/dist/internal/testing.js +5 -0
- package/dist/jobs/job-runner.js +23 -11
- package/dist/migrations/pending-rebuilds.js +1 -1
- package/dist/migrations/pii-event-backfill.js +2 -2
- package/dist/pipeline/dispatch-shared.js +43 -23
- package/dist/pipeline/dispatch-write.js +6 -2
- package/dist/pipeline/dispatcher-utils.js +6 -2
- package/dist/pipeline/event-consumer-state.js +1 -1
- package/dist/pipeline/projection-rebuild.d.ts +1 -0
- package/dist/pipeline/projection-rebuild.js +1 -1
- package/dist/pipeline/projection-state.js +1 -1
- package/dist/pipeline/system-hooks.js +1 -1
- package/dist/schema-cli.js +3 -3
- package/dist/search/purge-subject.js +20 -10
- package/dist/seeding/index.d.ts +1 -0
- package/dist/seeding/index.js +1 -0
- package/dist/stack/index.d.ts +10 -9
- package/dist/stack/index.js +11 -19
- package/dist/testing/e2e-generator.d.ts +5 -49
- package/dist/testing/e2e-generator.js +5 -382
- package/dist/testing/handler-context.d.ts +5 -4
- package/dist/testing/handler-context.js +5 -69
- package/dist/testing/index.d.ts +10 -26
- package/dist/testing/index.js +10 -34
- package/dist/testing/real-providers.d.ts +5 -3
- package/dist/testing/real-providers.js +5 -21
- package/package.json +37 -9
- package/src/changes.json +58 -0
- package/src/scripts/codemod/README.md +43 -0
- package/src/scripts/codemod/codemod-support.ts +276 -0
- package/src/scripts/codemod/db-factory.ts +810 -0
- package/src/scripts/codemod/feature-factory.ts +429 -0
- package/src/scripts/codemod/s3-imports.ts +274 -0
- package/src/scripts/codemod/tenant-db-options.ts +454 -0
- package/src/scripts/codemod/testing-move-targets.ts +161 -0
- package/src/scripts/codemod/testing-move.ts +520 -0
- package/dist/bun-db/connection.d.ts +0 -17
- package/dist/bun-db/connection.js +0 -40
- package/dist/bun-db/index.d.ts +0 -4
- package/dist/bun-db/query.d.ts +0 -106
- package/dist/bun-db/query.js +0 -1457
- package/dist/db/api.d.ts +0 -19
- package/dist/db/api.js +0 -23
- package/dist/db/bun-provider.d.ts +0 -2
- package/dist/db/bun-provider.js +0 -28
- package/dist/db/postgres-provider.d.ts +0 -2
- package/dist/db/postgres-provider.js +0 -21
- package/dist/db/query-api.d.ts +0 -2
- package/dist/db/query-api.js +0 -6
- package/dist/stack/db.d.ts +0 -22
- package/dist/stack/db.js +0 -67
- package/dist/stack/drain-event-consumers.d.ts +0 -4
- package/dist/stack/drain-event-consumers.js +0 -51
- package/dist/stack/drain-jobs.d.ts +0 -22
- package/dist/stack/drain-jobs.js +0 -141
- package/dist/stack/event-collector.d.ts +0 -9
- package/dist/stack/event-collector.js +0 -12
- package/dist/stack/push-entity-projection-tables.d.ts +0 -24
- package/dist/stack/push-entity-projection-tables.js +0 -74
- package/dist/stack/redis.d.ts +0 -17
- package/dist/stack/redis.js +0 -52
- package/dist/stack/request-helper.d.ts +0 -35
- package/dist/stack/request-helper.js +0 -144
- package/dist/stack/test-stack.d.ts +0 -192
- package/dist/stack/test-stack.js +0 -429
- package/dist/testing/access-assertions.d.ts +0 -2
- package/dist/testing/access-assertions.js +0 -13
- package/dist/testing/assertions.d.ts +0 -10
- package/dist/testing/assertions.js +0 -22
- package/dist/testing/boot-validator-fixture.d.ts +0 -3
- package/dist/testing/boot-validator-fixture.js +0 -101
- package/dist/testing/closed-connection-error.d.ts +0 -7
- package/dist/testing/closed-connection-error.js +0 -46
- package/dist/testing/db-cleanup.d.ts +0 -9
- package/dist/testing/db-cleanup.js +0 -31
- package/dist/testing/expect-error.d.ts +0 -2
- package/dist/testing/expect-error.js +0 -22
- package/dist/testing/file-provider-contract.d.ts +0 -2
- package/dist/testing/file-provider-contract.js +0 -102
- package/dist/testing/http-cookies.d.ts +0 -7
- package/dist/testing/http-cookies.js +0 -47
- package/dist/testing/late-bound.d.ts +0 -9
- package/dist/testing/late-bound.js +0 -31
- package/dist/testing/manual-clock.d.ts +0 -14
- package/dist/testing/manual-clock.js +0 -12
- package/dist/testing/multipart-helper.d.ts +0 -22
- package/dist/testing/multipart-helper.js +0 -77
- package/dist/testing/mutable-master-key-provider.d.ts +0 -7
- package/dist/testing/mutable-master-key-provider.js +0 -35
- package/dist/testing/observability-recorder.d.ts +0 -9
- package/dist/testing/observability-recorder.js +0 -29
- package/dist/testing/seed.d.ts +0 -9
- package/dist/testing/seed.js +0 -21
- package/dist/testing/shared-entities.d.ts +0 -286
- package/dist/testing/shared-entities.js +0 -56
- package/dist/testing/utils.d.ts +0 -1
- package/dist/testing/utils.js +0 -1
- package/dist/testing/wait-for.d.ts +0 -20
- package/dist/testing/wait-for.js +0 -47
- package/dist/testing/without-ambient-temporal.d.ts +0 -5
- package/dist/testing/without-ambient-temporal.js +0 -17
- package/dist/testing/zip-fixture.d.ts +0 -1
- package/dist/testing/zip-fixture.js +0 -33
- /package/dist/{stack/test-users.d.ts → seeding/seed-users.d.ts} +0 -0
- /package/dist/{stack/test-users.js → seeding/seed-users.js} +0 -0
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// testing-move — repoints imports of the former framework test subpaths to their new homes:
|
|
3
|
+
// `@cosmicdrift/kumiko-framework/{stack,testing,testing/*}` moved into `@cosmicdrift/kumiko-testing`,
|
|
4
|
+
// while `TestUsers` & co. moved to `/seeding` and `bridgeStub` to `/engine` (production code uses them).
|
|
5
|
+
//
|
|
6
|
+
// Usage:
|
|
7
|
+
// bun scripts/codemod/testing-move.ts <targetDir...> [--check | --dry-run | --write] [--no-format]
|
|
8
|
+
// Without a mode flag it writes (kumiko-upgrade --apply calls it that way). --check exits 1
|
|
9
|
+
// while any rewritable or manual site is left; --dry-run only reports.
|
|
10
|
+
|
|
11
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
12
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
13
|
+
import {
|
|
14
|
+
type ExportDeclaration,
|
|
15
|
+
type ImportDeclaration,
|
|
16
|
+
type ImportTypeNode,
|
|
17
|
+
Node,
|
|
18
|
+
Project,
|
|
19
|
+
type SourceFile,
|
|
20
|
+
SyntaxKind,
|
|
21
|
+
} from "ts-morph";
|
|
22
|
+
import { collectSourceFiles } from "./collect-source-files.js";
|
|
23
|
+
import {
|
|
24
|
+
classifyPackageSpecifier,
|
|
25
|
+
classifyRelativeSpecifier,
|
|
26
|
+
hasSplitNames,
|
|
27
|
+
type MoveTargetId,
|
|
28
|
+
mainTarget,
|
|
29
|
+
moduleSpecifierFor,
|
|
30
|
+
type OldModule,
|
|
31
|
+
targetForName,
|
|
32
|
+
} from "./testing-move-targets.js";
|
|
33
|
+
|
|
34
|
+
export interface TestingMoveSite {
|
|
35
|
+
readonly file: string;
|
|
36
|
+
readonly line: number;
|
|
37
|
+
readonly description: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface TestingMoveResult {
|
|
41
|
+
readonly output: string;
|
|
42
|
+
readonly rewrites: TestingMoveSite[];
|
|
43
|
+
readonly manual: TestingMoveSite[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface NamedElement {
|
|
47
|
+
readonly name: string;
|
|
48
|
+
readonly alias: string | undefined;
|
|
49
|
+
readonly isTypeOnly: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
type NamedDeclaration = ImportDeclaration | ExportDeclaration;
|
|
53
|
+
|
|
54
|
+
const CHEAP_PREFILTER = /\b(stack|testing)\b/;
|
|
55
|
+
const SHIM_PATH_SUFFIXES: readonly string[] = [
|
|
56
|
+
"packages/framework/src/stack/index.ts",
|
|
57
|
+
"packages/framework/src/testing/index.ts",
|
|
58
|
+
"packages/framework/src/testing/handler-context.ts",
|
|
59
|
+
"packages/framework/src/testing/real-providers.ts",
|
|
60
|
+
"packages/framework/src/testing/e2e-generator.ts",
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
function oldModuleOf(specifier: string, filePath: string): OldModule | undefined {
|
|
64
|
+
return specifier.startsWith(".")
|
|
65
|
+
? classifyRelativeSpecifier(specifier, filePath)
|
|
66
|
+
: classifyPackageSpecifier(specifier);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function groupByTarget(
|
|
70
|
+
old: OldModule,
|
|
71
|
+
elements: readonly NamedElement[],
|
|
72
|
+
): Map<MoveTargetId, NamedElement[]> {
|
|
73
|
+
const groups = new Map<MoveTargetId, NamedElement[]>();
|
|
74
|
+
for (const element of elements) {
|
|
75
|
+
const target = targetForName(old, element.name);
|
|
76
|
+
groups.set(target, [...(groups.get(target) ?? []), element]);
|
|
77
|
+
}
|
|
78
|
+
return groups;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function elementsOf(declaration: NamedDeclaration): NamedElement[] {
|
|
82
|
+
const specifiers = Node.isImportDeclaration(declaration)
|
|
83
|
+
? declaration.getNamedImports()
|
|
84
|
+
: declaration.getNamedExports();
|
|
85
|
+
return specifiers.map((specifier) => ({
|
|
86
|
+
name: specifier.getName(),
|
|
87
|
+
alias: specifier.getAliasNode()?.getText(),
|
|
88
|
+
isTypeOnly: specifier.isTypeOnly(),
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function toStructure(element: NamedElement): { name: string; alias?: string; isTypeOnly: boolean } {
|
|
93
|
+
return element.alias === undefined
|
|
94
|
+
? { name: element.name, isTypeOnly: element.isTypeOnly }
|
|
95
|
+
: { name: element.name, alias: element.alias, isTypeOnly: element.isTypeOnly };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function elementText(element: NamedElement): string {
|
|
99
|
+
const alias = element.alias === undefined ? "" : ` as ${element.alias}`;
|
|
100
|
+
return `${element.isTypeOnly ? "type " : ""}${element.name}${alias}`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function declarationText(
|
|
104
|
+
original: NamedDeclaration,
|
|
105
|
+
specifier: string,
|
|
106
|
+
elements: readonly NamedElement[],
|
|
107
|
+
): string {
|
|
108
|
+
const keyword = Node.isImportDeclaration(original) ? "import" : "export";
|
|
109
|
+
const typeOnly = original.isTypeOnly() ? " type" : "";
|
|
110
|
+
return `${keyword}${typeOnly} { ${elements.map(elementText).join(", ")} } from "${specifier}";`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function hasElement(existing: readonly NamedElement[], element: NamedElement): boolean {
|
|
114
|
+
return existing.some(
|
|
115
|
+
(candidate) => candidate.name === element.name && candidate.alias === element.alias,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function isMergeTarget(candidate: NamedDeclaration, original: NamedDeclaration): boolean {
|
|
120
|
+
if (candidate === original || candidate.isTypeOnly() !== original.isTypeOnly()) return false;
|
|
121
|
+
if (Node.isImportDeclaration(candidate)) {
|
|
122
|
+
return (
|
|
123
|
+
candidate.getNamespaceImport() === undefined && candidate.getDefaultImport() === undefined
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return !candidate.isNamespaceExport() && candidate.getNamedExports().length > 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function findMergeTarget(
|
|
130
|
+
sourceFile: SourceFile,
|
|
131
|
+
original: NamedDeclaration,
|
|
132
|
+
specifier: string,
|
|
133
|
+
): NamedDeclaration | undefined {
|
|
134
|
+
const sameKind: readonly NamedDeclaration[] = Node.isImportDeclaration(original)
|
|
135
|
+
? sourceFile.getImportDeclarations()
|
|
136
|
+
: sourceFile.getExportDeclarations();
|
|
137
|
+
return sameKind.find(
|
|
138
|
+
(candidate) =>
|
|
139
|
+
candidate.getModuleSpecifierValue() === specifier && isMergeTarget(candidate, original),
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function mergeInto(target: NamedDeclaration, elements: readonly NamedElement[]): void {
|
|
144
|
+
const present = elementsOf(target);
|
|
145
|
+
for (const element of elements) {
|
|
146
|
+
if (hasElement(present, element)) continue;
|
|
147
|
+
if (Node.isImportDeclaration(target)) target.addNamedImport(toStructure(element));
|
|
148
|
+
else target.addNamedExport(toStructure(element));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function repointDeclaration(
|
|
153
|
+
sourceFile: SourceFile,
|
|
154
|
+
declaration: NamedDeclaration,
|
|
155
|
+
old: OldModule,
|
|
156
|
+
filePath: string,
|
|
157
|
+
): MoveTargetId[] {
|
|
158
|
+
const groups = groupByTarget(old, elementsOf(declaration));
|
|
159
|
+
const replacementLines: string[] = [];
|
|
160
|
+
for (const [target, elements] of groups) {
|
|
161
|
+
const specifier = moduleSpecifierFor(target, filePath);
|
|
162
|
+
const mergeTarget = findMergeTarget(sourceFile, declaration, specifier);
|
|
163
|
+
if (mergeTarget === undefined)
|
|
164
|
+
replacementLines.push(declarationText(declaration, specifier, elements));
|
|
165
|
+
else mergeInto(mergeTarget, elements);
|
|
166
|
+
}
|
|
167
|
+
// replaceWithText keeps the blank lines around the declaration, remove() would eat them.
|
|
168
|
+
declaration.replaceWithText(replacementLines.join("\n"));
|
|
169
|
+
return [...groups.keys()];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function isWholeModuleReference(declaration: NamedDeclaration): boolean {
|
|
173
|
+
if (Node.isImportDeclaration(declaration)) {
|
|
174
|
+
return (
|
|
175
|
+
declaration.getNamespaceImport() !== undefined || declaration.getDefaultImport() !== undefined
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
return declaration.isNamespaceExport() || declaration.getNamedExports().length === 0;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function usesSplitName(
|
|
182
|
+
sourceFile: SourceFile,
|
|
183
|
+
declaration: NamedDeclaration,
|
|
184
|
+
old: OldModule,
|
|
185
|
+
): boolean {
|
|
186
|
+
const namespaceName = Node.isImportDeclaration(declaration)
|
|
187
|
+
? declaration.getNamespaceImport()?.getText()
|
|
188
|
+
: undefined;
|
|
189
|
+
if (namespaceName === undefined) return true;
|
|
190
|
+
return sourceFile
|
|
191
|
+
.getDescendantsOfKind(SyntaxKind.PropertyAccessExpression)
|
|
192
|
+
.some(
|
|
193
|
+
(access) =>
|
|
194
|
+
access.getExpression().getText() === namespaceName &&
|
|
195
|
+
targetForName(old, access.getName()) !== mainTarget(old),
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function repointWholeModule(
|
|
200
|
+
sourceFile: SourceFile,
|
|
201
|
+
declaration: NamedDeclaration,
|
|
202
|
+
old: OldModule,
|
|
203
|
+
filePath: string,
|
|
204
|
+
): MoveTargetId | undefined {
|
|
205
|
+
if (hasSplitNames(old) && usesSplitName(sourceFile, declaration, old)) return undefined;
|
|
206
|
+
const target = mainTarget(old);
|
|
207
|
+
declaration.setModuleSpecifier(moduleSpecifierFor(target, filePath));
|
|
208
|
+
return target;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function rewriteDeclarations(
|
|
212
|
+
sourceFile: SourceFile,
|
|
213
|
+
filePath: string,
|
|
214
|
+
result: { rewrites: TestingMoveSite[]; manual: TestingMoveSite[] },
|
|
215
|
+
): void {
|
|
216
|
+
const declarations: NamedDeclaration[] = [
|
|
217
|
+
...sourceFile.getImportDeclarations(),
|
|
218
|
+
...sourceFile.getExportDeclarations(),
|
|
219
|
+
];
|
|
220
|
+
// Lines are read up front: replacing an earlier declaration shifts everything below it.
|
|
221
|
+
const linesBefore = new Map(
|
|
222
|
+
declarations.map((declaration) => [declaration, declaration.getStartLineNumber()]),
|
|
223
|
+
);
|
|
224
|
+
for (const declaration of declarations) {
|
|
225
|
+
const specifier = declaration.getModuleSpecifierValue();
|
|
226
|
+
const old = specifier === undefined ? undefined : oldModuleOf(specifier, filePath);
|
|
227
|
+
if (specifier === undefined || old === undefined) continue;
|
|
228
|
+
const site = { file: filePath, line: linesBefore.get(declaration) ?? 0 };
|
|
229
|
+
if (!isWholeModuleReference(declaration)) {
|
|
230
|
+
const targets = repointDeclaration(sourceFile, declaration, old, filePath);
|
|
231
|
+
result.rewrites.push({ ...site, description: `${specifier} -> ${targets.join(", ")}` });
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
const target = repointWholeModule(sourceFile, declaration, old, filePath);
|
|
235
|
+
if (target !== undefined) {
|
|
236
|
+
result.rewrites.push({ ...site, description: `${specifier} -> ${target}` });
|
|
237
|
+
} else {
|
|
238
|
+
result.manual.push({
|
|
239
|
+
...site,
|
|
240
|
+
description: `${specifier}: namespace or default import that uses names which moved to different packages`,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function stringLiteralOf(node: Node | undefined): string | undefined {
|
|
247
|
+
return node !== undefined && Node.isStringLiteral(node) ? node.getLiteralValue() : undefined;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function importTypeSpecifier(node: ImportTypeNode): string | undefined {
|
|
251
|
+
const argument = node.getArgument();
|
|
252
|
+
return Node.isLiteralTypeNode(argument) ? stringLiteralOf(argument.getLiteral()) : undefined;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function destructuredNames(call: Node): string[] | undefined {
|
|
256
|
+
const awaited = call.getParentIfKind(SyntaxKind.AwaitExpression);
|
|
257
|
+
const declaration = (awaited ?? call).getParentIfKind(SyntaxKind.VariableDeclaration);
|
|
258
|
+
const pattern = declaration?.getNameNode();
|
|
259
|
+
if (pattern === undefined || !Node.isObjectBindingPattern(pattern)) return undefined;
|
|
260
|
+
return pattern
|
|
261
|
+
.getElements()
|
|
262
|
+
.map((element) => element.getPropertyNameNode()?.getText() ?? element.getName());
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function singleTargetFor(
|
|
266
|
+
old: OldModule,
|
|
267
|
+
names: readonly string[] | undefined,
|
|
268
|
+
): MoveTargetId | undefined {
|
|
269
|
+
if (names === undefined) return hasSplitNames(old) ? undefined : mainTarget(old);
|
|
270
|
+
const targets = new Set(names.map((name) => targetForName(old, name)));
|
|
271
|
+
return targets.size === 1 ? [...targets][0] : undefined;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function isModuleMockCall(call: Node): boolean {
|
|
275
|
+
return Node.isCallExpression(call) && call.getExpression().getText() === "mock.module";
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function isDynamicImportCall(call: Node): boolean {
|
|
279
|
+
return Node.isCallExpression(call) && call.getExpression().getKind() === SyntaxKind.ImportKeyword;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
interface SpecifierSite {
|
|
283
|
+
readonly specifier: string;
|
|
284
|
+
readonly line: number;
|
|
285
|
+
readonly names: readonly string[] | undefined;
|
|
286
|
+
readonly replace: (next: string) => void;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function importTypeSites(sourceFile: SourceFile): SpecifierSite[] {
|
|
290
|
+
const sites: SpecifierSite[] = [];
|
|
291
|
+
for (const node of sourceFile.getDescendantsOfKind(SyntaxKind.ImportType)) {
|
|
292
|
+
const specifier = importTypeSpecifier(node);
|
|
293
|
+
if (specifier === undefined) continue;
|
|
294
|
+
const qualifier = node.getQualifier()?.getText().split(".")[0];
|
|
295
|
+
sites.push({
|
|
296
|
+
specifier,
|
|
297
|
+
line: node.getStartLineNumber(),
|
|
298
|
+
names: qualifier === undefined ? undefined : [qualifier],
|
|
299
|
+
replace: (next) => node.setArgument(next),
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
return sites;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function callSites(sourceFile: SourceFile): SpecifierSite[] {
|
|
306
|
+
const sites: SpecifierSite[] = [];
|
|
307
|
+
for (const call of sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
308
|
+
if (!isDynamicImportCall(call) && !isModuleMockCall(call)) continue;
|
|
309
|
+
const argument = call.getArguments()[0];
|
|
310
|
+
const specifier = stringLiteralOf(argument);
|
|
311
|
+
if (argument === undefined || specifier === undefined) continue;
|
|
312
|
+
sites.push({
|
|
313
|
+
specifier,
|
|
314
|
+
line: call.getStartLineNumber(),
|
|
315
|
+
// mock.module replaces a whole module, so it never splits by name.
|
|
316
|
+
names: isModuleMockCall(call) ? [] : destructuredNames(call),
|
|
317
|
+
replace: (next) => argument.replaceWithText(JSON.stringify(next)),
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
return sites;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function rewriteDynamicSites(
|
|
324
|
+
sourceFile: SourceFile,
|
|
325
|
+
filePath: string,
|
|
326
|
+
result: { rewrites: TestingMoveSite[]; manual: TestingMoveSite[] },
|
|
327
|
+
): void {
|
|
328
|
+
for (const site of [...importTypeSites(sourceFile), ...callSites(sourceFile)]) {
|
|
329
|
+
const old = oldModuleOf(site.specifier, filePath);
|
|
330
|
+
if (old === undefined) continue;
|
|
331
|
+
const names = site.names?.length === 0 ? undefined : site.names;
|
|
332
|
+
const target = site.names?.length === 0 ? mainTarget(old) : singleTargetFor(old, names);
|
|
333
|
+
if (target === undefined) {
|
|
334
|
+
result.manual.push({
|
|
335
|
+
file: filePath,
|
|
336
|
+
line: site.line,
|
|
337
|
+
description: `${site.specifier}: dynamic import whose names moved to different packages`,
|
|
338
|
+
});
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
site.replace(moduleSpecifierFor(target, filePath));
|
|
342
|
+
result.rewrites.push({
|
|
343
|
+
file: filePath,
|
|
344
|
+
line: site.line,
|
|
345
|
+
description: `${site.specifier} -> ${target}`,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export function transformTestingMoveSource(
|
|
351
|
+
source: string,
|
|
352
|
+
filePath = "source.ts",
|
|
353
|
+
): TestingMoveResult {
|
|
354
|
+
const result: TestingMoveResult = { output: source, rewrites: [], manual: [] };
|
|
355
|
+
if (!CHEAP_PREFILTER.test(source)) return result;
|
|
356
|
+
const project = new Project({
|
|
357
|
+
useInMemoryFileSystem: true,
|
|
358
|
+
skipAddingFilesFromTsConfig: true,
|
|
359
|
+
skipFileDependencyResolution: true,
|
|
360
|
+
});
|
|
361
|
+
const sourceFile = project.createSourceFile(resolve(filePath), source);
|
|
362
|
+
rewriteDeclarations(sourceFile, resolve(filePath), result);
|
|
363
|
+
rewriteDynamicSites(sourceFile, resolve(filePath), result);
|
|
364
|
+
return { ...result, output: result.rewrites.length > 0 ? sourceFile.getFullText() : source };
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
type Mode = "write" | "dry-run" | "check";
|
|
368
|
+
|
|
369
|
+
interface CliOptions {
|
|
370
|
+
readonly mode: Mode;
|
|
371
|
+
readonly format: boolean;
|
|
372
|
+
readonly paths: string[];
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
class CliError extends Error {}
|
|
376
|
+
|
|
377
|
+
function parseArgs(argv: readonly string[]): CliOptions {
|
|
378
|
+
let mode: Mode | undefined;
|
|
379
|
+
let format = true;
|
|
380
|
+
const paths: string[] = [];
|
|
381
|
+
const setMode = (next: Mode): void => {
|
|
382
|
+
if (mode !== undefined && mode !== next) {
|
|
383
|
+
throw new CliError("--check, --dry-run and --write are mutually exclusive");
|
|
384
|
+
}
|
|
385
|
+
mode = next;
|
|
386
|
+
};
|
|
387
|
+
for (const arg of argv) {
|
|
388
|
+
if (arg === "--check") setMode("check");
|
|
389
|
+
else if (arg === "--dry-run") setMode("dry-run");
|
|
390
|
+
else if (arg === "--write") setMode("write");
|
|
391
|
+
else if (arg === "--no-format") format = false;
|
|
392
|
+
else if (arg.startsWith("--")) throw new CliError(`unknown flag: ${arg}`);
|
|
393
|
+
else paths.push(arg);
|
|
394
|
+
}
|
|
395
|
+
if (paths.length === 0) throw new CliError("at least one target directory is required");
|
|
396
|
+
return { mode: mode ?? "write", format, paths };
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function isMigratableFile(name: string): boolean {
|
|
400
|
+
return /\.(ts|tsx)$/.test(name) && !name.endsWith(".d.ts");
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function isSkippedPath(file: string): boolean {
|
|
404
|
+
return (
|
|
405
|
+
file.includes("/__tests__/fixtures/") ||
|
|
406
|
+
SHIM_PATH_SUFFIXES.some((suffix) => file.endsWith(suffix))
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function findBiomeBinary(startDir: string): string | undefined {
|
|
411
|
+
let dir = resolve(startDir);
|
|
412
|
+
for (;;) {
|
|
413
|
+
const candidate = join(dir, "node_modules", ".bin", "biome");
|
|
414
|
+
if (existsSync(candidate)) return candidate;
|
|
415
|
+
const parent = dirname(dir);
|
|
416
|
+
if (parent === dir) return undefined;
|
|
417
|
+
dir = parent;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const FORMAT_BATCH_SIZE = 50;
|
|
422
|
+
|
|
423
|
+
async function formatWithBiome(files: readonly string[], cwd: string): Promise<void> {
|
|
424
|
+
const biome = findBiomeBinary(cwd);
|
|
425
|
+
if (biome === undefined) {
|
|
426
|
+
console.log(
|
|
427
|
+
"[testing-move] biome not found in node_modules/.bin — format and sort imports of the changed files yourself",
|
|
428
|
+
);
|
|
429
|
+
// skip: formatting is a convenience, the rewrite itself is already on disk
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
for (let i = 0; i < files.length; i += FORMAT_BATCH_SIZE) {
|
|
433
|
+
const batch = files.slice(i, i + FORMAT_BATCH_SIZE);
|
|
434
|
+
const proc = Bun.spawn({
|
|
435
|
+
cmd: [
|
|
436
|
+
biome,
|
|
437
|
+
"check",
|
|
438
|
+
"--write",
|
|
439
|
+
"--linter-enabled=false",
|
|
440
|
+
"--no-errors-on-unmatched",
|
|
441
|
+
...batch,
|
|
442
|
+
],
|
|
443
|
+
cwd,
|
|
444
|
+
stdout: "pipe",
|
|
445
|
+
stderr: "pipe",
|
|
446
|
+
});
|
|
447
|
+
const [stderr, exitCode] = await Promise.all([new Response(proc.stderr).text(), proc.exited]);
|
|
448
|
+
if (exitCode !== 0) {
|
|
449
|
+
console.log(
|
|
450
|
+
`[testing-move] biome exited ${exitCode}; run it on the changed files yourself\n${stderr.trim()}`,
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function displayPath(file: string): string {
|
|
457
|
+
const fromCwd = relative(process.cwd(), file);
|
|
458
|
+
return fromCwd.startsWith("..") ? file : fromCwd;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
interface RunSummary {
|
|
462
|
+
readonly rewrites: TestingMoveSite[];
|
|
463
|
+
readonly manual: TestingMoveSite[];
|
|
464
|
+
readonly changedFiles: string[];
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function transformFiles(files: readonly string[], mode: Mode): RunSummary {
|
|
468
|
+
const summary: RunSummary = { rewrites: [], manual: [], changedFiles: [] };
|
|
469
|
+
for (const file of files) {
|
|
470
|
+
const before = readFileSync(file, "utf8");
|
|
471
|
+
const result = transformTestingMoveSource(before, file);
|
|
472
|
+
const shown = displayPath(file);
|
|
473
|
+
for (const site of result.rewrites)
|
|
474
|
+
console.log(`${shown}:${site.line} rewritten ${site.description}`);
|
|
475
|
+
for (const site of result.manual)
|
|
476
|
+
console.log(`${shown}:${site.line} manual ${site.description}`);
|
|
477
|
+
summary.rewrites.push(...result.rewrites);
|
|
478
|
+
summary.manual.push(...result.manual);
|
|
479
|
+
if (mode === "write" && result.output !== before) {
|
|
480
|
+
writeFileSync(file, result.output);
|
|
481
|
+
summary.changedFiles.push(file);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return summary;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function parseArgsOrExit(argv: readonly string[]): CliOptions {
|
|
488
|
+
try {
|
|
489
|
+
return parseArgs(argv);
|
|
490
|
+
} catch (err) {
|
|
491
|
+
if (err instanceof CliError) {
|
|
492
|
+
console.error(`[testing-move] ${err.message}`);
|
|
493
|
+
process.exit(2);
|
|
494
|
+
}
|
|
495
|
+
throw err;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
async function main(): Promise<void> {
|
|
500
|
+
const options = parseArgsOrExit(process.argv.slice(2));
|
|
501
|
+
const files = collectSourceFiles(options.paths, { isMigratableFile }).filter(
|
|
502
|
+
(file) => !isSkippedPath(file),
|
|
503
|
+
);
|
|
504
|
+
const { rewrites, manual, changedFiles } = transformFiles(files, options.mode);
|
|
505
|
+
|
|
506
|
+
console.log(
|
|
507
|
+
`\n[testing-move] ${options.mode === "write" ? "rewritten" : "rewritable"}: ${rewrites.length} in ${new Set(rewrites.map((site) => site.file)).size} file(s); manual: ${manual.length}`,
|
|
508
|
+
);
|
|
509
|
+
|
|
510
|
+
if (options.mode === "write" && options.format && changedFiles.length > 0) {
|
|
511
|
+
await formatWithBiome(changedFiles, resolve(options.paths[0] ?? "."));
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const hasOpenSites = rewrites.length + manual.length > 0;
|
|
515
|
+
process.exit(options.mode === "check" && hasOpenSites ? 1 : 0);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (import.meta.main) {
|
|
519
|
+
await main();
|
|
520
|
+
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/// <reference types="bun-types" preserve="true" />
|
|
2
|
-
import type { PgListenClient } from "@cosmicdrift/kumiko-types/db-connection";
|
|
3
|
-
export type { PgListenClient };
|
|
4
|
-
export type BunDbConnection = InstanceType<typeof Bun.SQL>;
|
|
5
|
-
export type BunDbTx = BunDbConnection;
|
|
6
|
-
export type BunDbRunner = BunDbConnection | BunDbTx;
|
|
7
|
-
export type BunDbConnectionOptions = {
|
|
8
|
-
readonly maxConnections?: number;
|
|
9
|
-
readonly idleTimeoutSeconds?: number;
|
|
10
|
-
readonly connectTimeoutSeconds?: number;
|
|
11
|
-
};
|
|
12
|
-
export declare function createBunDbConnection(url: string, options?: BunDbConnectionOptions): {
|
|
13
|
-
db: BunDbConnection;
|
|
14
|
-
listenClient: PgListenClient;
|
|
15
|
-
close: () => Promise<void>;
|
|
16
|
-
};
|
|
17
|
-
export declare function bunDbConnectionOptionsFromEnv(env?: Readonly<Record<string, string | undefined>>): BunDbConnectionOptions;
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/// <reference types="bun-types" preserve="true" />
|
|
2
|
-
// Bun.SQL-basierter Connection-Layer (KEIN drizzle). Production-Pfad
|
|
3
|
-
// nach drizzle-removal. Erstellt parallel zum legacy `db/connection.ts`
|
|
4
|
-
// — apps migrieren schritt-für-schritt, alte Datei weg sobald 0 Konsumenten.
|
|
5
|
-
//
|
|
6
|
-
// LISTEN for the event dispatcher uses Bun.SQL's listen() on its own
|
|
7
|
-
// dedicated connection, outside the pool's idleTimeout.
|
|
8
|
-
import { readPositiveIntEnv } from "../utils/env-parse.js";
|
|
9
|
-
export function createBunDbConnection(url, options = {}) {
|
|
10
|
-
const bunOpts = {};
|
|
11
|
-
if (options.maxConnections !== undefined)
|
|
12
|
-
bunOpts.max = options.maxConnections;
|
|
13
|
-
if (options.idleTimeoutSeconds !== undefined)
|
|
14
|
-
bunOpts.idleTimeout = options.idleTimeoutSeconds;
|
|
15
|
-
if (options.connectTimeoutSeconds !== undefined) {
|
|
16
|
-
bunOpts.connectionTimeout = options.connectTimeoutSeconds;
|
|
17
|
-
}
|
|
18
|
-
const db = new Bun.SQL(url, bunOpts);
|
|
19
|
-
return {
|
|
20
|
-
db,
|
|
21
|
-
listenClient: db,
|
|
22
|
-
close: async () => {
|
|
23
|
-
await db.end();
|
|
24
|
-
},
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
// guard:dup-ok — ENV-Reader für BunDB (gibt { db, listenClient } zurück), nicht verwandt mit redisClientOptionsFromEnv
|
|
28
|
-
export function bunDbConnectionOptionsFromEnv(env = process.env) {
|
|
29
|
-
const opts = {};
|
|
30
|
-
const max = readPositiveIntEnv(env, "DATABASE_POOL_MAX");
|
|
31
|
-
const idle = readPositiveIntEnv(env, "DATABASE_POOL_IDLE_TIMEOUT");
|
|
32
|
-
const connect = readPositiveIntEnv(env, "DATABASE_POOL_CONNECT_TIMEOUT");
|
|
33
|
-
if (max !== undefined)
|
|
34
|
-
opts.maxConnections = max;
|
|
35
|
-
if (idle !== undefined)
|
|
36
|
-
opts.idleTimeoutSeconds = idle;
|
|
37
|
-
if (connect !== undefined)
|
|
38
|
-
opts.connectTimeoutSeconds = connect;
|
|
39
|
-
return opts;
|
|
40
|
-
}
|
package/dist/bun-db/index.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export type { BunDbConnection, BunDbConnectionOptions, BunDbRunner, BunDbTx, PgListenClient, } from "./connection.js";
|
|
2
|
-
export { bunDbConnectionOptionsFromEnv, createBunDbConnection } from "./connection.js";
|
|
3
|
-
export type { InnerJoinRow, InnerJoinSpec, InsertOnConflictDoNothingOptions, JoinColumnPair, JsonTextMatch, SelectOptions, TableInfo, WhereObject, WhereOperator, WhereValue, } from "./query.js";
|
|
4
|
-
export { aggregateWhere, asEntityTableMeta, asRawClient, countWhere, type DeleteManyBatchedOptions, type DeleteManyBatchedResult, deleteMany, deleteManyBatched, extractTableInfo, fetchOne, type IncrementCounterOptions, incrementCounter, insertMany, insertOnConflictDoNothing, insertOne, instantToTimestamptzParam, isTimestamptzType, requireEntityTableMeta, runInSavepoint, runInSavepointIfSupported, runnerCanBeginTransaction, selectInnerJoin, selectMany, transaction, type UpsertOnConflictOptions, unsafeReadRetrying, updateMany, upsertByPk, upsertOnConflict, } from "./query.js";
|
package/dist/bun-db/query.d.ts
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import type { AggregateRow, AggregateSpec } from "@cosmicdrift/kumiko-types/aggregate-types";
|
|
2
|
-
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
3
|
-
import type { SelectOptions, WhereObject } from "@cosmicdrift/kumiko-types/where-clause-types";
|
|
4
|
-
import type { EntityTableMeta } from "../db/entity-table-meta.js";
|
|
5
|
-
import { type NotExecutorOnly } from "../db/table-builder.js";
|
|
6
|
-
import { asEntityTableMeta, requireEntityTableMeta } from "../db/table-introspection.js";
|
|
7
|
-
import type { DbRunner } from "@cosmicdrift/kumiko-types/db-connection";
|
|
8
|
-
import type { BunDbRunner } from "./connection.js";
|
|
9
|
-
export { asEntityTableMeta, requireEntityTableMeta };
|
|
10
|
-
export type ReservedClient = {
|
|
11
|
-
unsafe: RawClient["unsafe"];
|
|
12
|
-
release: () => unknown;
|
|
13
|
-
};
|
|
14
|
-
export type RawClient = {
|
|
15
|
-
unsafe: <TRow = unknown>(sql: string, params?: readonly unknown[]) => Promise<readonly TRow[]>;
|
|
16
|
-
begin?: <T>(fn: (tx: BunDbRunner) => Promise<T>) => Promise<T>;
|
|
17
|
-
savepoint?: <T>(fn: (sp: unknown) => Promise<T>) => Promise<T>;
|
|
18
|
-
release?: unknown;
|
|
19
|
-
reserve?: () => Promise<ReservedClient>;
|
|
20
|
-
};
|
|
21
|
-
export type RawClientWithBegin = RawClient & {
|
|
22
|
-
begin: NonNullable<RawClient["begin"]>;
|
|
23
|
-
};
|
|
24
|
-
export declare function hasBegin(client: RawClient): client is RawClientWithBegin;
|
|
25
|
-
export declare function asRawClient(db: unknown): RawClient;
|
|
26
|
-
export declare function runnerCanBeginTransaction(db: unknown): boolean;
|
|
27
|
-
export declare function runInSavepoint<T>(tx: unknown, fn: (sp: unknown) => Promise<T>): Promise<T>;
|
|
28
|
-
export declare function runInSavepointIfSupported<T>(db: unknown, fn: (sp: DbRunner) => Promise<T>): Promise<T>;
|
|
29
|
-
export type AnyDb = BunDbRunner | unknown;
|
|
30
|
-
export type { JsonTextMatch, OrderByClause, SelectOptions, WhereObject, WhereOperator, WhereValue, } from "@cosmicdrift/kumiko-types/where-clause-types";
|
|
31
|
-
type TableLike = EntityTableMeta | any;
|
|
32
|
-
type WritableTable = EntityTableMeta & NotExecutorOnly;
|
|
33
|
-
export type TableInfo = {
|
|
34
|
-
readonly name: string;
|
|
35
|
-
readonly columnOf: (field: string) => string;
|
|
36
|
-
readonly pgTypeOf: (column: string) => string | undefined;
|
|
37
|
-
readonly bigintJsModeOf: (column: string) => "number" | "bigint" | undefined;
|
|
38
|
-
readonly fieldOf: (column: string) => string;
|
|
39
|
-
readonly hasColumn: (fieldOrColumn: string) => boolean;
|
|
40
|
-
};
|
|
41
|
-
export declare function extractTableInfo(table: TableLike): TableInfo;
|
|
42
|
-
export declare function coerceRow<TOut = Record<string, unknown>>(row: Record<string, unknown>, info: TableInfo): TOut;
|
|
43
|
-
export declare function instantToTimestamptzParam(instant: Temporal.Instant): string;
|
|
44
|
-
export declare function isClosedConnectionError(err: unknown): boolean;
|
|
45
|
-
export declare function describeClosedConnectionFailure(raw: unknown, err: unknown, attempts: number): Record<string, unknown> | undefined;
|
|
46
|
-
export declare function unsafeReadRetrying<TRow>(db: AnyDb, sqlText: string, params: readonly unknown[]): Promise<readonly TRow[]>;
|
|
47
|
-
export declare function selectMany<TRow = any>(db: AnyDb, table: TableLike, where?: WhereObject, options?: SelectOptions): Promise<readonly TRow[]>;
|
|
48
|
-
export declare function fetchOne<TRow = any>(db: AnyDb, table: TableLike, where: WhereObject): Promise<TRow | undefined>;
|
|
49
|
-
export type JoinColumnPair = {
|
|
50
|
-
readonly left: string;
|
|
51
|
-
readonly right: string;
|
|
52
|
-
};
|
|
53
|
-
export type InnerJoinSpec = {
|
|
54
|
-
readonly left: TableLike;
|
|
55
|
-
readonly right: TableLike;
|
|
56
|
-
readonly on: readonly [JoinColumnPair, ...JoinColumnPair[]];
|
|
57
|
-
readonly leftWhere?: WhereObject;
|
|
58
|
-
readonly rightWhere?: WhereObject;
|
|
59
|
-
/** Fields to return per side; default: all columns of `left`, none of `right`. */
|
|
60
|
-
readonly leftFields?: readonly string[];
|
|
61
|
-
readonly rightFields?: readonly string[];
|
|
62
|
-
readonly limit?: number;
|
|
63
|
-
};
|
|
64
|
-
export type InnerJoinRow<TLeft, TRight> = {
|
|
65
|
-
readonly left: TLeft;
|
|
66
|
-
readonly right: TRight;
|
|
67
|
-
};
|
|
68
|
-
export declare function selectInnerJoin<TLeft = Record<string, unknown>, TRight = Record<string, unknown>>(db: AnyDb, spec: InnerJoinSpec): Promise<readonly InnerJoinRow<TLeft, TRight>[]>;
|
|
69
|
-
export declare function insertMany<TRow = any>(db: AnyDb, table: WritableTable, rows: ReadonlyArray<Record<string, unknown>>): Promise<readonly TRow[]>;
|
|
70
|
-
export declare function insertOne<TRow = any>(db: AnyDb, table: WritableTable, values: Record<string, unknown>): Promise<TRow | undefined>;
|
|
71
|
-
export declare function updateMany<TRow = any>(db: AnyDb, table: WritableTable, set: Record<string, unknown>, where: WhereObject): Promise<readonly TRow[]>;
|
|
72
|
-
export declare function deleteMany(db: AnyDb, table: WritableTable, where: WhereObject): Promise<void>;
|
|
73
|
-
export declare function resolveConflictColumns(table: TableLike, info: TableInfo, conflictKeys: readonly string[] | undefined): readonly string[];
|
|
74
|
-
export type UpsertOnConflictOptions = {
|
|
75
|
-
readonly conflictKeys: readonly string[];
|
|
76
|
-
/** Columns to set on conflict. Default: EXCLUDED for every inserted non-key column. */
|
|
77
|
-
readonly update?: Record<string, unknown>;
|
|
78
|
-
};
|
|
79
|
-
export type DeleteManyBatchedOptions = {
|
|
80
|
-
readonly limit: number;
|
|
81
|
-
readonly maxBatches?: number;
|
|
82
|
-
/** Row id field for the LIMIT subquery. Default `id`. */
|
|
83
|
-
readonly idColumn?: string;
|
|
84
|
-
};
|
|
85
|
-
export type DeleteManyBatchedResult = {
|
|
86
|
-
readonly deleted: number;
|
|
87
|
-
readonly batches: number;
|
|
88
|
-
};
|
|
89
|
-
export declare function countWhere(db: AnyDb, table: TableLike, where?: WhereObject): Promise<number>;
|
|
90
|
-
export declare function isTimestamptzType(pgType: string | undefined): boolean;
|
|
91
|
-
export declare function aggregateWhere(db: AnyDb, table: TableLike, spec: AggregateSpec, where?: WhereObject): Promise<readonly AggregateRow[]>;
|
|
92
|
-
export declare function upsertOnConflict<TRow = any>(db: AnyDb, table: WritableTable, values: Record<string, unknown>, options: UpsertOnConflictOptions): Promise<TRow | undefined>;
|
|
93
|
-
export declare function upsertByPk<TRow = any>(db: AnyDb, table: WritableTable, values: Record<string, unknown>, updateOnConflict?: Record<string, unknown>): Promise<TRow | undefined>;
|
|
94
|
-
export type InsertOnConflictDoNothingOptions = {
|
|
95
|
-
readonly conflictKeys?: readonly string[];
|
|
96
|
-
};
|
|
97
|
-
export declare function insertOnConflictDoNothing<TRow = any>(db: AnyDb, table: WritableTable, values: Record<string, unknown>, options?: InsertOnConflictDoNothingOptions): Promise<TRow | undefined>;
|
|
98
|
-
export type IncrementCounterOptions = {
|
|
99
|
-
readonly conflictKeys?: readonly string[];
|
|
100
|
-
/** Extra SET clauses on conflict (e.g. lastUpdatedAt: sql\`NOW()\`). */
|
|
101
|
-
readonly set?: Record<string, unknown>;
|
|
102
|
-
};
|
|
103
|
-
export declare function incrementCounter<TRow = any>(db: AnyDb, table: WritableTable, values: Record<string, unknown>, increments: Record<string, number>, options?: IncrementCounterOptions): Promise<TRow | undefined>;
|
|
104
|
-
export declare function deleteManyBatched(db: AnyDb, table: WritableTable, where: WhereObject, options: DeleteManyBatchedOptions): Promise<DeleteManyBatchedResult>;
|
|
105
|
-
export declare function transaction<T>(db: AnyDb, fn: (tx: BunDbRunner) => Promise<T>): Promise<T>;
|
|
106
|
-
export declare function runInNewTransaction<T>(db: unknown, fn: (tx: DbRunner) => Promise<T>): Promise<T>;
|