@cosmicdrift/kumiko-framework 0.334.0 → 0.336.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/anonymous-live-entities.d.ts +2 -0
- package/dist/api/anonymous-live-entities.js +17 -0
- package/dist/api/auth-routes.js +19 -4
- package/dist/api/client-ip.js +3 -6
- package/dist/api/extra-route.js +3 -2
- package/dist/api/index.d.ts +2 -1
- package/dist/api/index.js +1 -0
- package/dist/api/pii-leak-guard.js +7 -4
- package/dist/api/redis-sse-broker.d.ts +2 -0
- package/dist/api/redis-sse-broker.js +6 -7
- package/dist/api/routes.js +16 -17
- package/dist/api/server.js +20 -10
- package/dist/api/sse-broker.d.ts +3 -0
- package/dist/api/sse-broker.js +14 -0
- package/dist/api/sse-route.d.ts +5 -2
- package/dist/api/sse-route.js +71 -11
- package/dist/bun-db/connection.js +0 -2
- package/dist/bun-db/index.d.ts +1 -1
- package/dist/bun-db/index.js +1 -1
- package/dist/bun-db/query.d.ts +1 -0
- package/dist/bun-db/query.js +15 -4
- package/dist/crypto/kek-source.js +40 -36
- package/dist/crypto/kms-adapter.js +2 -2
- package/dist/db/apply-entity-event.js +8 -2
- package/dist/db/blind-index-cleanup.js +32 -11
- package/dist/db/entity-table-meta.js +5 -3
- package/dist/db/event-store-executor-context.js +13 -19
- package/dist/db/event-store-executor-read.js +45 -38
- package/dist/db/event-store-executor-write.js +15 -17
- package/dist/db/migrate-generator.js +27 -28
- package/dist/db/pg-error.js +9 -5
- package/dist/db/queries/backfill-pii.js +15 -5
- package/dist/db/queries/shadow-swap.js +5 -4
- package/dist/db/table-builder.d.ts +1 -0
- package/dist/db/table-builder.js +7 -15
- package/dist/db/tenant-db.js +18 -2
- package/dist/derivatives/variant-key.js +3 -1
- package/dist/engine/boot-validator/access-declarations.js +12 -3
- package/dist/engine/boot-validator/entity-handler.js +13 -1
- package/dist/engine/boot-validator/entity-list-screens.d.ts +1 -0
- package/dist/engine/boot-validator/entity-list-screens.js +2 -2
- package/dist/engine/boot-validator/index.js +6 -6
- package/dist/engine/boot-validator/nav.d.ts +2 -1
- package/dist/engine/boot-validator/nav.js +10 -20
- package/dist/engine/boot-validator/ownership.d.ts +1 -1
- package/dist/engine/boot-validator/ownership.js +2 -1
- package/dist/engine/boot-validator/pii-retention.d.ts +2 -0
- package/dist/engine/boot-validator/pii-retention.js +5 -2
- package/dist/engine/boot-validator/projection-list-screens.js +8 -0
- package/dist/engine/boot-validator/query-output-columns.js +53 -5
- package/dist/engine/boot-validator/screens.d.ts +2 -2
- package/dist/engine/boot-validator/screens.js +125 -46
- package/dist/engine/boot-validator/transfer-graph.js +9 -6
- package/dist/engine/boot-validator/url-prefill-fields.js +8 -6
- package/dist/engine/boot-validator/zod-shape.js +17 -4
- package/dist/engine/build-app-schema.js +65 -20
- package/dist/engine/build-config-feature-schema.js +8 -1
- package/dist/engine/changeset-changes.js +24 -10
- package/dist/engine/create-app.js +10 -1
- package/dist/engine/extension-names.d.ts +4 -0
- package/dist/engine/extension-names.js +4 -0
- package/dist/engine/feature-ast/extractors/events.js +11 -8
- package/dist/engine/feature-ast/extractors/handlers.js +39 -16
- package/dist/engine/feature-ast/extractors/resolve-import.d.ts +7 -2
- package/dist/engine/feature-ast/extractors/resolve-import.js +42 -15
- package/dist/engine/feature-ast/parse.d.ts +1 -0
- package/dist/engine/feature-ast/parse.js +18 -3
- package/dist/engine/feature-ast/patch.js +80 -61
- package/dist/engine/feature-ast/patcher.d.ts +1 -0
- package/dist/engine/feature-ast/patcher.js +2 -1
- package/dist/engine/feature-ast/render.js +1 -1
- package/dist/engine/feature-entity-handlers.d.ts +1 -0
- package/dist/engine/feature-entity-handlers.js +2 -0
- package/dist/engine/index.d.ts +4 -3
- package/dist/engine/index.js +3 -2
- package/dist/engine/ownership.d.ts +1 -0
- package/dist/engine/ownership.js +15 -2
- package/dist/engine/pattern-library/mixed-schemas.js +15 -29
- package/dist/engine/pattern-library/shared-fields.d.ts +2 -0
- package/dist/engine/pattern-library/shared-fields.js +17 -0
- package/dist/engine/registry-facade.js +3 -0
- package/dist/engine/registry-validate.d.ts +1 -0
- package/dist/engine/registry-validate.js +14 -0
- package/dist/engine/registry.js +2 -1
- package/dist/engine/role-assignment.d.ts +7 -1
- package/dist/engine/role-assignment.js +27 -7
- package/dist/engine/schema-builder.d.ts +1 -0
- package/dist/engine/schema-builder.js +9 -2
- package/dist/engine/screen-helpers.js +15 -4
- package/dist/engine/steps/_event-match.js +34 -7
- package/dist/engine/steps/_read-source.js +8 -0
- package/dist/engine/system-user.d.ts +1 -0
- package/dist/engine/system-user.js +3 -0
- package/dist/engine/where-rule-lint.js +22 -4
- package/dist/env/dry-run.js +10 -3
- package/dist/env/index.js +21 -1
- package/dist/errors/reasons.d.ts +0 -1
- package/dist/errors/reasons.js +2 -5
- package/dist/event-store/event-store.js +10 -2
- package/dist/event-store/provenance-append.js +4 -1
- package/dist/files/file-routes.d.ts +0 -1
- package/dist/files/file-routes.js +22 -7
- package/dist/files/local-provider.js +13 -10
- package/dist/files/provider-resolver.js +20 -1
- package/dist/files/types.js +16 -1
- package/dist/i18n/request-locale.d.ts +3 -2
- package/dist/i18n/request-locale.js +16 -6
- package/dist/i18n/required-surface-keys.js +23 -35
- package/dist/i18n/settings-hub-keys.js +3 -0
- package/dist/jobs/index.d.ts +2 -2
- package/dist/jobs/index.js +1 -1
- package/dist/jobs/job-runner.d.ts +2 -0
- package/dist/jobs/job-runner.js +172 -59
- package/dist/migrations/index.d.ts +1 -1
- package/dist/migrations/index.js +1 -1
- package/dist/migrations/pending-rebuilds.d.ts +4 -0
- package/dist/migrations/pending-rebuilds.js +23 -1
- package/dist/observability/escape-hatch-report.d.ts +1 -0
- package/dist/observability/escape-hatch-report.js +3 -0
- package/dist/observability/index.d.ts +2 -1
- package/dist/observability/index.js +2 -1
- package/dist/observability/standard-metrics.d.ts +4 -0
- package/dist/observability/standard-metrics.js +16 -0
- package/dist/pipeline/dispatch-query.d.ts +1 -1
- package/dist/pipeline/dispatch-query.js +2 -2
- package/dist/pipeline/dispatch-shared.d.ts +2 -3
- package/dist/pipeline/dispatch-shared.js +14 -9
- package/dist/pipeline/dispatch-stream.js +2 -5
- package/dist/pipeline/dispatch-write.js +22 -5
- package/dist/pipeline/dispatcher.js +1 -2
- package/dist/pipeline/event-dispatcher-admin.js +12 -2
- package/dist/pipeline/event-dispatcher-delivery.js +5 -0
- package/dist/pipeline/event-dispatcher.d.ts +1 -0
- package/dist/pipeline/event-dispatcher.js +30 -12
- package/dist/pipeline/index.d.ts +1 -1
- package/dist/pipeline/index.js +1 -1
- package/dist/pipeline/member-read-only-transaction.js +1 -14
- package/dist/pipeline/msp-rebuild.d.ts +1 -0
- package/dist/pipeline/msp-rebuild.js +1 -0
- package/dist/pipeline/system-hooks.d.ts +1 -1
- package/dist/pipeline/system-hooks.js +19 -20
- package/dist/pipeline/tenant-timezone-cache.d.ts +2 -0
- package/dist/pipeline/tenant-timezone-cache.js +20 -6
- package/dist/pipeline/write-origin.js +13 -6
- package/dist/redis/pubsub-signal.d.ts +2 -0
- package/dist/redis/pubsub-signal.js +2 -2
- package/dist/schema-cli.js +4 -14
- package/dist/search/meilisearch-adapter.js +17 -6
- package/dist/search/purge-subject.js +8 -3
- package/dist/stack/drain-event-consumers.js +3 -0
- package/dist/stack/drain-jobs.d.ts +3 -1
- package/dist/stack/drain-jobs.js +55 -13
- package/dist/stack/table-helpers.js +10 -8
- package/dist/testing/closed-connection-error.js +2 -3
- package/dist/testing/wait-for.js +1 -0
- package/dist/ui-types/index.d.ts +1 -1
- package/dist/ui-types/index.js +1 -1
- package/dist/upgrade-cli.d.ts +1 -0
- package/dist/upgrade-cli.js +36 -15
- package/package.json +4 -4
- package/src/changes.json +264 -0
- package/src/scripts/codemod/migrate-cross-tenant.ts +14 -2
- package/src/scripts/codemod/migrate-db-raw.ts +16 -8
- package/src/scripts/codemod/migrate-open-to-all.ts +7 -1
- package/src/scripts/codemod/pii-personal-migration.ts +111 -44
- package/src/scripts/codemod/unprocessable-error-details-reason.ts +136 -42
- package/dist/scripts/codemod/crypto-shredding-testing-move.d.ts +0 -2
- package/dist/scripts/codemod/crypto-shredding-testing-move.js +0 -109
- package/dist/scripts/codemod/migrate-cross-tenant.d.ts +0 -19
- package/dist/scripts/codemod/migrate-cross-tenant.js +0 -266
- package/dist/scripts/codemod/migrate-db-raw.d.ts +0 -23
- package/dist/scripts/codemod/migrate-db-raw.js +0 -345
- package/dist/scripts/codemod/migrate-open-to-all.d.ts +0 -22
- package/dist/scripts/codemod/migrate-open-to-all.js +0 -194
- package/dist/scripts/codemod/pii-personal-migration.d.ts +0 -11
- package/dist/scripts/codemod/pii-personal-migration.js +0 -615
- package/dist/scripts/codemod/unprocessable-error-details-reason.d.ts +0 -2
- package/dist/scripts/codemod/unprocessable-error-details-reason.js +0 -235
|
@@ -105,9 +105,25 @@ function resolveDetailsProperty(optsLiteral: ObjectLiteralExpression): DetailsRe
|
|
|
105
105
|
/** True if removing this shorthand-bound identifier would leave its declaration with no other use. */
|
|
106
106
|
function wouldLeaveUnusedVariable(nameNode: Identifier): boolean {
|
|
107
107
|
try {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
// The shorthand name resolves to the object-literal property symbol too, and
|
|
109
|
+
// a declaration counts as a reference, so look up the variable's own uses.
|
|
110
|
+
const shorthand = nameNode.getParent();
|
|
111
|
+
const variableSymbol = Node.isShorthandPropertyAssignment(shorthand)
|
|
112
|
+
? shorthand.getValueSymbol()
|
|
113
|
+
: undefined;
|
|
114
|
+
const declarations = variableSymbol?.getDeclarations() ?? [];
|
|
115
|
+
const declarationNames = declarations.flatMap((declaration) =>
|
|
116
|
+
Node.isVariableDeclaration(declaration) || Node.isParameterDeclaration(declaration)
|
|
117
|
+
? [declaration.getNameNode()]
|
|
118
|
+
: [],
|
|
119
|
+
);
|
|
120
|
+
// No resolvable declaration: can't verify, treat as unsafe.
|
|
121
|
+
if (declarationNames.length === 0) return true;
|
|
122
|
+
return declarationNames
|
|
123
|
+
.flatMap((declarationName) =>
|
|
124
|
+
Node.isIdentifier(declarationName) ? declarationName.findReferencesAsNodes() : [],
|
|
125
|
+
)
|
|
126
|
+
.every((usage) => usage === nameNode);
|
|
111
127
|
} catch {
|
|
112
128
|
// Can't verify — treat as unsafe rather than guess.
|
|
113
129
|
return true;
|
|
@@ -122,12 +138,11 @@ type RemovalResult =
|
|
|
122
138
|
/** Removes the `reason` property from a `details` object literal, in place. */
|
|
123
139
|
function removeReasonProperty(detailsLiteral: ObjectLiteralExpression): RemovalResult {
|
|
124
140
|
const properties = detailsLiteral.getProperties();
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
141
|
+
const hasSpread = properties.some((p) => Node.isSpreadAssignment(p));
|
|
142
|
+
const spreadSkip: RemovalResult = {
|
|
143
|
+
kind: "skip",
|
|
144
|
+
note: "details object literal has a spread next to an explicit reason — not statically decidable",
|
|
145
|
+
};
|
|
131
146
|
|
|
132
147
|
for (const prop of properties) {
|
|
133
148
|
if (Node.isPropertyAssignment(prop)) {
|
|
@@ -145,12 +160,14 @@ function removeReasonProperty(detailsLiteral: ObjectLiteralExpression): RemovalR
|
|
|
145
160
|
? nameNode.getLiteralText()
|
|
146
161
|
: undefined;
|
|
147
162
|
if (propName === "reason") {
|
|
163
|
+
if (hasSpread) return spreadSkip;
|
|
148
164
|
prop.remove();
|
|
149
165
|
return { kind: "removed" };
|
|
150
166
|
}
|
|
151
167
|
continue;
|
|
152
168
|
}
|
|
153
169
|
if (Node.isShorthandPropertyAssignment(prop) && prop.getName() === "reason") {
|
|
170
|
+
if (hasSpread) return spreadSkip;
|
|
154
171
|
if (wouldLeaveUnusedVariable(prop.getNameNode())) {
|
|
155
172
|
return {
|
|
156
173
|
kind: "skip",
|
|
@@ -166,13 +183,20 @@ function removeReasonProperty(detailsLiteral: ObjectLiteralExpression): RemovalR
|
|
|
166
183
|
|
|
167
184
|
type CallOutcome = "changed" | "skipped" | "not-a-match";
|
|
168
185
|
|
|
169
|
-
|
|
186
|
+
// "manual": a concrete `reason` the codemod found but cannot remove safely.
|
|
187
|
+
// "unverified": opts/details not statically analyzable — may well be fine, so
|
|
188
|
+
// it is listed apart to keep the actionable list short.
|
|
189
|
+
type SkipKind = "manual" | "unverified";
|
|
190
|
+
type ReportSkip = (note: string, kind: SkipKind) => void;
|
|
191
|
+
|
|
192
|
+
function processNewExpression(args: readonly Node[], report: ReportSkip): CallOutcome {
|
|
170
193
|
const optsArg = args[1];
|
|
171
194
|
if (!optsArg) return "not-a-match";
|
|
172
195
|
|
|
173
196
|
if (!Node.isObjectLiteralExpression(optsArg)) {
|
|
174
197
|
report(
|
|
175
198
|
"second argument (opts) is not a statically-analyzable object literal — cannot verify details.reason",
|
|
199
|
+
"unverified",
|
|
176
200
|
);
|
|
177
201
|
return "skipped";
|
|
178
202
|
}
|
|
@@ -180,20 +204,103 @@ function processNewExpression(args: readonly Node[], report: (note: string) => v
|
|
|
180
204
|
const detailsRes = resolveDetailsProperty(optsArg);
|
|
181
205
|
if (detailsRes.kind === "absent") return "not-a-match";
|
|
182
206
|
if (detailsRes.kind === "unresolvable") {
|
|
183
|
-
report(detailsRes.note);
|
|
207
|
+
report(detailsRes.note, "unverified");
|
|
184
208
|
return "skipped";
|
|
185
209
|
}
|
|
186
210
|
|
|
187
211
|
const removal = removeReasonProperty(detailsRes.literal);
|
|
188
212
|
if (removal.kind === "absent") return "not-a-match";
|
|
189
213
|
if (removal.kind === "skip") {
|
|
190
|
-
report(removal.note);
|
|
214
|
+
report(removal.note, "manual");
|
|
215
|
+
return "skipped";
|
|
216
|
+
}
|
|
217
|
+
return "changed";
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// `failUnprocessable(reason, details)` takes the details literal directly as
|
|
221
|
+
// its second argument (no `{ details }` wrapper like the ctor's opts).
|
|
222
|
+
function processFailUnprocessableCall(args: readonly Node[], report: ReportSkip): CallOutcome {
|
|
223
|
+
const detailsArg = args[1];
|
|
224
|
+
if (!detailsArg) return "not-a-match";
|
|
225
|
+
|
|
226
|
+
if (!Node.isObjectLiteralExpression(detailsArg)) {
|
|
227
|
+
report(
|
|
228
|
+
"second argument (details) is not a statically-analyzable object literal — cannot verify details.reason",
|
|
229
|
+
"unverified",
|
|
230
|
+
);
|
|
231
|
+
return "skipped";
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const removal = removeReasonProperty(detailsArg);
|
|
235
|
+
if (removal.kind === "absent") return "not-a-match";
|
|
236
|
+
if (removal.kind === "skip") {
|
|
237
|
+
report(removal.note, "manual");
|
|
191
238
|
return "skipped";
|
|
192
239
|
}
|
|
193
240
|
return "changed";
|
|
194
241
|
}
|
|
195
242
|
|
|
196
|
-
type SkipEntry = {
|
|
243
|
+
type SkipEntry = {
|
|
244
|
+
readonly file: string;
|
|
245
|
+
readonly line: number;
|
|
246
|
+
readonly note: string;
|
|
247
|
+
readonly kind: SkipKind;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
type MigratableCall = {
|
|
251
|
+
readonly exportName: "UnprocessableError" | "failUnprocessable";
|
|
252
|
+
readonly localKind: "class" | "function";
|
|
253
|
+
readonly process: (args: readonly Node[], report: ReportSkip) => CallOutcome;
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const UNPROCESSABLE_ERROR_CTOR: MigratableCall = {
|
|
257
|
+
exportName: "UnprocessableError",
|
|
258
|
+
localKind: "class",
|
|
259
|
+
process: processNewExpression,
|
|
260
|
+
};
|
|
261
|
+
const FAIL_UNPROCESSABLE_CALL: MigratableCall = {
|
|
262
|
+
exportName: "failUnprocessable",
|
|
263
|
+
localKind: "function",
|
|
264
|
+
process: processFailUnprocessableCall,
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
function migrateCallSite(
|
|
268
|
+
sourceFile: SourceFile,
|
|
269
|
+
importedNames: ReadonlyMap<string, ImportedBinding>,
|
|
270
|
+
site: Node & { getExpression(): Node; getArguments(): Node[] },
|
|
271
|
+
target: MigratableCall,
|
|
272
|
+
skips: SkipEntry[],
|
|
273
|
+
): boolean {
|
|
274
|
+
const callee = site.getExpression();
|
|
275
|
+
if (!Node.isIdentifier(callee)) return false;
|
|
276
|
+
const localName = callee.getText();
|
|
277
|
+
const line = site.getStartLineNumber();
|
|
278
|
+
const file = sourceFile.getFilePath();
|
|
279
|
+
|
|
280
|
+
const binding = importedNames.get(localName);
|
|
281
|
+
if (binding?.importedName !== target.exportName) {
|
|
282
|
+
if (localName !== target.exportName) return false;
|
|
283
|
+
const note = binding
|
|
284
|
+
? `${target.exportName} is imported from "${binding.moduleSpecifier}", not a @cosmicdrift/kumiko-* package`
|
|
285
|
+
: `${target.exportName} has no @cosmicdrift/kumiko-* import in this file (likely a locally defined ${target.localKind})`;
|
|
286
|
+
skips.push({ file, line, note, kind: "manual" });
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
if (!KUMIKO_MODULE_RE.test(binding.moduleSpecifier)) {
|
|
290
|
+
skips.push({
|
|
291
|
+
file,
|
|
292
|
+
line,
|
|
293
|
+
note: `${target.exportName} is imported from "${binding.moduleSpecifier}", not a @cosmicdrift/kumiko-* package`,
|
|
294
|
+
kind: "manual",
|
|
295
|
+
});
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const outcome = target.process(site.getArguments(), (note, kind) =>
|
|
300
|
+
skips.push({ file, line, note, kind }),
|
|
301
|
+
);
|
|
302
|
+
return outcome === "changed";
|
|
303
|
+
}
|
|
197
304
|
|
|
198
305
|
/** Migrates one file in place. Returns how many `reason` properties were removed. */
|
|
199
306
|
function migrateFile(sourceFile: SourceFile, skips: SkipEntry[]): number {
|
|
@@ -201,34 +308,14 @@ function migrateFile(sourceFile: SourceFile, skips: SkipEntry[]): number {
|
|
|
201
308
|
let removedCount = 0;
|
|
202
309
|
|
|
203
310
|
for (const newExpr of sourceFile.getDescendantsOfKind(SyntaxKind.NewExpression)) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const localName = callee.getText();
|
|
207
|
-
|
|
208
|
-
const binding = importedNames.get(localName);
|
|
209
|
-
const isFrameworkUnprocessableError = binding?.importedName === "UnprocessableError";
|
|
210
|
-
|
|
211
|
-
if (!isFrameworkUnprocessableError) {
|
|
212
|
-
if (localName !== "UnprocessableError") continue;
|
|
213
|
-
const note = binding
|
|
214
|
-
? `UnprocessableError is imported from "${binding.moduleSpecifier}", not a @cosmicdrift/kumiko-* package`
|
|
215
|
-
: "UnprocessableError has no @cosmicdrift/kumiko-* import in this file (likely a locally defined class)";
|
|
216
|
-
skips.push({ file: sourceFile.getFilePath(), line: newExpr.getStartLineNumber(), note });
|
|
217
|
-
continue;
|
|
311
|
+
if (migrateCallSite(sourceFile, importedNames, newExpr, UNPROCESSABLE_ERROR_CTOR, skips)) {
|
|
312
|
+
removedCount++;
|
|
218
313
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
note: `UnprocessableError is imported from "${binding.moduleSpecifier}", not a @cosmicdrift/kumiko-* package`,
|
|
224
|
-
});
|
|
225
|
-
continue;
|
|
314
|
+
}
|
|
315
|
+
for (const call of sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
316
|
+
if (migrateCallSite(sourceFile, importedNames, call, FAIL_UNPROCESSABLE_CALL, skips)) {
|
|
317
|
+
removedCount++;
|
|
226
318
|
}
|
|
227
|
-
|
|
228
|
-
const outcome = processNewExpression(newExpr.getArguments(), (note) => {
|
|
229
|
-
skips.push({ file: sourceFile.getFilePath(), line: newExpr.getStartLineNumber(), note });
|
|
230
|
-
});
|
|
231
|
-
if (outcome === "changed") removedCount++;
|
|
232
319
|
}
|
|
233
320
|
|
|
234
321
|
return removedCount;
|
|
@@ -263,9 +350,16 @@ async function main(): Promise<void> {
|
|
|
263
350
|
console.log(
|
|
264
351
|
`Touched ${touchedFiles} files, removed ${removedTotal} redundant "reason" propert(y/ies).`,
|
|
265
352
|
);
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
console.log(
|
|
353
|
+
for (const kind of ["manual", "unverified"] as const) {
|
|
354
|
+
const ofKind = skips.filter((s) => s.kind === kind);
|
|
355
|
+
console.log(
|
|
356
|
+
kind === "manual"
|
|
357
|
+
? `Skipped ${ofKind.length} site(s) needing manual work:`
|
|
358
|
+
: `Unverified ${ofKind.length} site(s) (opts/details not statically analyzable, may be fine):`,
|
|
359
|
+
);
|
|
360
|
+
for (const s of ofKind) {
|
|
361
|
+
console.log(` ${relative(rootDir, s.file)}:${s.line} — ${s.note}`);
|
|
362
|
+
}
|
|
269
363
|
}
|
|
270
364
|
console.log();
|
|
271
365
|
}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
// Rewrites `import { resetPiiSubjectKmsForTests, resetBlindIndexKeyForTests }
|
|
3
|
-
// from "@cosmicdrift/kumiko-framework/crypto"` to import both names from
|
|
4
|
-
// "@cosmicdrift/kumiko-framework/testing" instead (fw#1631). Only the
|
|
5
|
-
// package barrel specifier is rewritten — relative deep-imports of the
|
|
6
|
-
// defining module are left untouched, per the changes.json migration note.
|
|
7
|
-
// Idempotent: an already-migrated file has nothing left to move.
|
|
8
|
-
//
|
|
9
|
-
// Usage: bun scripts/codemod/crypto-shredding-testing-move.ts <targetDir> [--dry-run]
|
|
10
|
-
import { readdirSync } from "node:fs";
|
|
11
|
-
import { join, resolve } from "node:path";
|
|
12
|
-
import { Project } from "ts-morph";
|
|
13
|
-
const OLD_SPECIFIER = "@cosmicdrift/kumiko-framework/crypto";
|
|
14
|
-
const NEW_SPECIFIER = "@cosmicdrift/kumiko-framework/testing";
|
|
15
|
-
const MOVED_NAMES = new Set(["resetPiiSubjectKmsForTests", "resetBlindIndexKeyForTests"]);
|
|
16
|
-
const EXCLUDE_DIRS = new Set(["node_modules", "dist", "build"]);
|
|
17
|
-
function findTargetFiles(rootDir) {
|
|
18
|
-
// Walk the tree and skip excluded dirs while descending — filtering the
|
|
19
|
-
// absolute path after a full Glob scan silently no-ops when the repo root
|
|
20
|
-
// itself contains "/build/" or "/node_modules/" (fw#2289).
|
|
21
|
-
const files = [];
|
|
22
|
-
const walk = (dir) => {
|
|
23
|
-
let entries;
|
|
24
|
-
try {
|
|
25
|
-
entries = readdirSync(dir, { withFileTypes: true });
|
|
26
|
-
}
|
|
27
|
-
catch {
|
|
28
|
-
// skip: unreadable directory during walk — treat as empty.
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
for (const ent of entries) {
|
|
32
|
-
if (ent.isDirectory()) {
|
|
33
|
-
if (EXCLUDE_DIRS.has(ent.name) || ent.name.startsWith("."))
|
|
34
|
-
continue;
|
|
35
|
-
walk(join(dir, ent.name));
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
if (ent.isFile() && /\.(ts|tsx)$/.test(ent.name)) {
|
|
39
|
-
files.push(join(dir, ent.name));
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
walk(rootDir);
|
|
44
|
-
return files.sort();
|
|
45
|
-
}
|
|
46
|
-
/** Move matching value imports from crypto → testing. Returns count of names moved. */
|
|
47
|
-
function migrateFile(sourceFile) {
|
|
48
|
-
// Value imports only — a pre-existing `import type { … } from testing`
|
|
49
|
-
// must not receive runtime helpers (they would be erased).
|
|
50
|
-
const oldImports = sourceFile
|
|
51
|
-
.getImportDeclarations()
|
|
52
|
-
.filter((d) => d.getModuleSpecifierValue() === OLD_SPECIFIER && !d.isTypeOnly());
|
|
53
|
-
if (oldImports.length === 0)
|
|
54
|
-
return 0;
|
|
55
|
-
let fileMoved = 0;
|
|
56
|
-
for (const oldImport of oldImports) {
|
|
57
|
-
const movedHere = oldImport.getNamedImports().filter((spec) => MOVED_NAMES.has(spec.getName()));
|
|
58
|
-
if (movedHere.length === 0)
|
|
59
|
-
continue;
|
|
60
|
-
const names = movedHere.map((spec) => spec.getName());
|
|
61
|
-
const existingNewImport = sourceFile
|
|
62
|
-
.getImportDeclarations()
|
|
63
|
-
.find((d) => d.getModuleSpecifierValue() === NEW_SPECIFIER && !d.isTypeOnly());
|
|
64
|
-
if (existingNewImport) {
|
|
65
|
-
const already = new Set(existingNewImport.getNamedImports().map((s) => s.getName()));
|
|
66
|
-
for (const name of names) {
|
|
67
|
-
if (!already.has(name))
|
|
68
|
-
existingNewImport.addNamedImport(name);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
sourceFile.addImportDeclaration({ moduleSpecifier: NEW_SPECIFIER, namedImports: names });
|
|
73
|
-
}
|
|
74
|
-
for (const spec of movedHere)
|
|
75
|
-
spec.remove();
|
|
76
|
-
const remaining = oldImport.getNamedImports().length > 0 ||
|
|
77
|
-
!!oldImport.getDefaultImport() ||
|
|
78
|
-
!!oldImport.getNamespaceImport();
|
|
79
|
-
if (!remaining)
|
|
80
|
-
oldImport.remove();
|
|
81
|
-
fileMoved += names.length;
|
|
82
|
-
}
|
|
83
|
-
return fileMoved;
|
|
84
|
-
}
|
|
85
|
-
async function main() {
|
|
86
|
-
const positional = process.argv.slice(2).filter((a) => !a.startsWith("--"));
|
|
87
|
-
const dryRun = process.argv.includes("--dry-run");
|
|
88
|
-
const rootDir = resolve(positional[0] ?? process.cwd());
|
|
89
|
-
const files = findTargetFiles(rootDir);
|
|
90
|
-
const project = new Project({
|
|
91
|
-
skipAddingFilesFromTsConfig: true,
|
|
92
|
-
skipFileDependencyResolution: true,
|
|
93
|
-
});
|
|
94
|
-
let touchedFiles = 0;
|
|
95
|
-
let movedNames = 0;
|
|
96
|
-
for (const file of files) {
|
|
97
|
-
const sourceFile = project.addSourceFileAtPath(file);
|
|
98
|
-
const fileMoved = migrateFile(sourceFile);
|
|
99
|
-
if (fileMoved === 0)
|
|
100
|
-
continue;
|
|
101
|
-
touchedFiles++;
|
|
102
|
-
movedNames += fileMoved;
|
|
103
|
-
if (!dryRun)
|
|
104
|
-
sourceFile.saveSync();
|
|
105
|
-
}
|
|
106
|
-
console.log(`\nScanned ${files.length} files under ${rootDir}${dryRun ? " (dry-run)" : ""}.`);
|
|
107
|
-
console.log(`Touched ${touchedFiles} files, moved ${movedNames} import(s) from "${OLD_SPECIFIER}" to "${NEW_SPECIFIER}".\n`);
|
|
108
|
-
}
|
|
109
|
-
await main();
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
export interface Rewrite {
|
|
3
|
-
readonly file: string;
|
|
4
|
-
readonly line: number;
|
|
5
|
-
readonly before: string;
|
|
6
|
-
readonly after: string;
|
|
7
|
-
}
|
|
8
|
-
export interface ManualSite {
|
|
9
|
-
readonly file: string;
|
|
10
|
-
readonly line: number;
|
|
11
|
-
readonly why: string;
|
|
12
|
-
readonly handlerName?: string;
|
|
13
|
-
}
|
|
14
|
-
export interface MigrateCrossTenantResult {
|
|
15
|
-
readonly output: string;
|
|
16
|
-
readonly rewrites: Rewrite[];
|
|
17
|
-
readonly manual: ManualSite[];
|
|
18
|
-
}
|
|
19
|
-
export declare function migrateCrossTenantSource(source: string, fileName: string): MigrateCrossTenantResult;
|
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
// migrate-cross-tenant — rewrites `crossTenant: true` on entity-convention
|
|
3
|
-
// handlers to `escapeHatch: { reason }` where the handler name and verb can
|
|
4
|
-
// be derived from the call, and reports every other site for review (fw#2915).
|
|
5
|
-
//
|
|
6
|
-
// Usage:
|
|
7
|
-
// bun scripts/codemod/migrate-cross-tenant.ts [--dry-run] <path...>
|
|
8
|
-
import { readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
9
|
-
import { join, resolve } from "node:path";
|
|
10
|
-
import { Node, Project, SyntaxKind, } from "ts-morph";
|
|
11
|
-
const VERB_IN_NAME_CALLEES = {
|
|
12
|
-
defineEntityCreateHandler: "create",
|
|
13
|
-
defineEntityUpdateHandler: "update",
|
|
14
|
-
defineEntityDeleteHandler: "delete",
|
|
15
|
-
defineEntityRestoreHandler: "restore",
|
|
16
|
-
defineEntityListHandler: "list",
|
|
17
|
-
defineEntityDetailHandler: "detail",
|
|
18
|
-
};
|
|
19
|
-
const VERB_IN_ARG_CALLEES = new Set(["defineEntityWriteHandler", "defineEntityQueryHandler"]);
|
|
20
|
-
const READ_VERBS = new Set(["list", "detail"]);
|
|
21
|
-
function isCrossTenantAssignment(node) {
|
|
22
|
-
return Node.isPropertyAssignment(node) && node.getName() === "crossTenant";
|
|
23
|
-
}
|
|
24
|
-
function getCalleeName(call) {
|
|
25
|
-
const expr = call.getExpression();
|
|
26
|
-
if (Node.isIdentifier(expr))
|
|
27
|
-
return expr.getText();
|
|
28
|
-
if (Node.isPropertyAccessExpression(expr))
|
|
29
|
-
return expr.getName();
|
|
30
|
-
return undefined;
|
|
31
|
-
}
|
|
32
|
-
function hasEscapeHatchProperty(obj) {
|
|
33
|
-
return obj.getProperty("escapeHatch") !== undefined;
|
|
34
|
-
}
|
|
35
|
-
function deriveAutoRewriteTarget(objectLiteral) {
|
|
36
|
-
const call = objectLiteral.getParentIfKind(SyntaxKind.CallExpression);
|
|
37
|
-
if (!call)
|
|
38
|
-
return undefined;
|
|
39
|
-
if (!call.getArguments().includes(objectLiteral))
|
|
40
|
-
return undefined;
|
|
41
|
-
const calleeName = getCalleeName(call);
|
|
42
|
-
if (!calleeName)
|
|
43
|
-
return undefined;
|
|
44
|
-
const firstArg = call.getArguments()[0];
|
|
45
|
-
if (!firstArg || !Node.isStringLiteral(firstArg))
|
|
46
|
-
return undefined;
|
|
47
|
-
const firstArgValue = firstArg.getLiteralValue();
|
|
48
|
-
const verbFromName = VERB_IN_NAME_CALLEES[calleeName];
|
|
49
|
-
if (verbFromName) {
|
|
50
|
-
return {
|
|
51
|
-
handlerName: `${firstArgValue}:${verbFromName}`,
|
|
52
|
-
entityName: firstArgValue,
|
|
53
|
-
verb: verbFromName,
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
if (VERB_IN_ARG_CALLEES.has(calleeName)) {
|
|
57
|
-
const sep = firstArgValue.indexOf(":");
|
|
58
|
-
if (sep === -1)
|
|
59
|
-
return undefined;
|
|
60
|
-
const entityName = firstArgValue.slice(0, sep);
|
|
61
|
-
const verb = firstArgValue.slice(sep + 1);
|
|
62
|
-
if (!entityName || !verb)
|
|
63
|
-
return undefined;
|
|
64
|
-
return { handlerName: firstArgValue, entityName, verb };
|
|
65
|
-
}
|
|
66
|
-
return undefined;
|
|
67
|
-
}
|
|
68
|
-
function deriveEntityCrudContext(objectLiteral) {
|
|
69
|
-
const verbGroupAssignment = objectLiteral.getParent();
|
|
70
|
-
if (!Node.isPropertyAssignment(verbGroupAssignment))
|
|
71
|
-
return undefined;
|
|
72
|
-
const verbGroup = verbGroupAssignment.getName();
|
|
73
|
-
if (verbGroup !== "write" && verbGroup !== "read")
|
|
74
|
-
return undefined;
|
|
75
|
-
const optionsObject = verbGroupAssignment.getParentIfKind(SyntaxKind.ObjectLiteralExpression);
|
|
76
|
-
const call = optionsObject?.getParentIfKind(SyntaxKind.CallExpression);
|
|
77
|
-
if (!call || !optionsObject || !call.getArguments().includes(optionsObject))
|
|
78
|
-
return undefined;
|
|
79
|
-
const calleeName = getCalleeName(call);
|
|
80
|
-
if (calleeName !== "registerEntityCrud" && calleeName !== "crud")
|
|
81
|
-
return undefined;
|
|
82
|
-
const args = call.getArguments();
|
|
83
|
-
const entityArg = calleeName === "crud" ? args[0] : args[1];
|
|
84
|
-
const entityName = entityArg && Node.isStringLiteral(entityArg) ? entityArg.getLiteralValue() : undefined;
|
|
85
|
-
return `under ${calleeName}(${entityName ? `"${entityName}", ` : ""}...) → ${verbGroup}: multiple verbs share this crossTenant — set escapeHatch per verb manually, one reason each`;
|
|
86
|
-
}
|
|
87
|
-
function classifyManualReason(objectLiteral) {
|
|
88
|
-
const entityCrudWhy = deriveEntityCrudContext(objectLiteral);
|
|
89
|
-
if (entityCrudWhy)
|
|
90
|
-
return entityCrudWhy;
|
|
91
|
-
const call = objectLiteral.getParentIfKind(SyntaxKind.CallExpression);
|
|
92
|
-
if (!call?.getArguments().includes(objectLiteral)) {
|
|
93
|
-
return "crossTenant is not set directly in a handler call (e.g. a spread/shared object) — give each consuming handler its own escapeHatch reason";
|
|
94
|
-
}
|
|
95
|
-
const calleeName = getCalleeName(call);
|
|
96
|
-
if (!calleeName || (!VERB_IN_NAME_CALLEES[calleeName] && !VERB_IN_ARG_CALLEES.has(calleeName))) {
|
|
97
|
-
return `call \`${calleeName ?? "<unknown>"}\` is not a recognized entity-convention handler factory — add escapeHatch manually`;
|
|
98
|
-
}
|
|
99
|
-
return "handler name/verb could not be derived from the call's first argument — add escapeHatch manually";
|
|
100
|
-
}
|
|
101
|
-
function deriveBestEffortHandlerName(assignment) {
|
|
102
|
-
const call = assignment.getFirstAncestorByKind(SyntaxKind.CallExpression);
|
|
103
|
-
const firstArg = call?.getArguments()[0];
|
|
104
|
-
return firstArg && Node.isStringLiteral(firstArg) ? firstArg.getLiteralValue() : undefined;
|
|
105
|
-
}
|
|
106
|
-
function buildEscapeHatchReason(target) {
|
|
107
|
-
const verbKind = READ_VERBS.has(target.verb) ? "reads" : "writes";
|
|
108
|
-
return `${target.handlerName} ${verbKind} ${target.entityName} rows across every tenant (migrated from crossTenant: true; state the operator use case here)`;
|
|
109
|
-
}
|
|
110
|
-
export function migrateCrossTenantSource(source, fileName) {
|
|
111
|
-
const project = new Project({ useInMemoryFileSystem: true, skipFileDependencyResolution: true });
|
|
112
|
-
const sourceFile = project.createSourceFile(fileName, source);
|
|
113
|
-
const targets = sourceFile
|
|
114
|
-
.getDescendantsOfKind(SyntaxKind.PropertyAssignment)
|
|
115
|
-
.filter(isCrossTenantAssignment);
|
|
116
|
-
const rewrites = [];
|
|
117
|
-
const manual = [];
|
|
118
|
-
for (const assignment of targets) {
|
|
119
|
-
if (assignment.wasForgotten())
|
|
120
|
-
continue;
|
|
121
|
-
const line = assignment.getStartLineNumber();
|
|
122
|
-
const init = assignment.getInitializer();
|
|
123
|
-
if (init?.getKind() === SyntaxKind.FalseKeyword)
|
|
124
|
-
continue;
|
|
125
|
-
if (init?.getKind() !== SyntaxKind.TrueKeyword) {
|
|
126
|
-
manual.push({
|
|
127
|
-
file: fileName,
|
|
128
|
-
line,
|
|
129
|
-
why: `crossTenant is not a literal boolean (\`${init?.getText() ?? "<missing>"}\`) — replace manually with escapeHatch: { reason }`,
|
|
130
|
-
...(deriveBestEffortHandlerName(assignment) !== undefined && {
|
|
131
|
-
handlerName: deriveBestEffortHandlerName(assignment),
|
|
132
|
-
}),
|
|
133
|
-
});
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
const objectLiteral = assignment.getParentIfKind(SyntaxKind.ObjectLiteralExpression);
|
|
137
|
-
if (!objectLiteral)
|
|
138
|
-
continue;
|
|
139
|
-
if (hasEscapeHatchProperty(objectLiteral)) {
|
|
140
|
-
manual.push({
|
|
141
|
-
file: fileName,
|
|
142
|
-
line,
|
|
143
|
-
why: "this object already declares escapeHatch — merge the two manually and remove crossTenant",
|
|
144
|
-
...(deriveBestEffortHandlerName(assignment) !== undefined && {
|
|
145
|
-
handlerName: deriveBestEffortHandlerName(assignment),
|
|
146
|
-
}),
|
|
147
|
-
});
|
|
148
|
-
continue;
|
|
149
|
-
}
|
|
150
|
-
const target = deriveAutoRewriteTarget(objectLiteral);
|
|
151
|
-
if (!target) {
|
|
152
|
-
manual.push({
|
|
153
|
-
file: fileName,
|
|
154
|
-
line,
|
|
155
|
-
why: classifyManualReason(objectLiteral),
|
|
156
|
-
...(deriveBestEffortHandlerName(assignment) !== undefined && {
|
|
157
|
-
handlerName: deriveBestEffortHandlerName(assignment),
|
|
158
|
-
}),
|
|
159
|
-
});
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
const before = assignment.getText();
|
|
163
|
-
const after = `escapeHatch: { reason: ${JSON.stringify(buildEscapeHatchReason(target))} }`;
|
|
164
|
-
assignment.replaceWithText(after);
|
|
165
|
-
rewrites.push({ file: fileName, line, before, after });
|
|
166
|
-
}
|
|
167
|
-
return { output: sourceFile.getFullText(), rewrites, manual };
|
|
168
|
-
}
|
|
169
|
-
class CliError extends Error {
|
|
170
|
-
}
|
|
171
|
-
function parseArgs(argv) {
|
|
172
|
-
let dryRun = false;
|
|
173
|
-
const paths = [];
|
|
174
|
-
for (const arg of argv) {
|
|
175
|
-
if (arg === "--dry-run") {
|
|
176
|
-
dryRun = true;
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
if (arg.startsWith("--"))
|
|
180
|
-
throw new CliError(`unknown flag: ${arg}`);
|
|
181
|
-
paths.push(arg);
|
|
182
|
-
}
|
|
183
|
-
if (paths.length === 0)
|
|
184
|
-
throw new CliError("at least one path is required");
|
|
185
|
-
return { dryRun, paths };
|
|
186
|
-
}
|
|
187
|
-
function isMigratableFile(name) {
|
|
188
|
-
return /\.(ts|tsx)$/.test(name);
|
|
189
|
-
}
|
|
190
|
-
function walkDir(dir, out) {
|
|
191
|
-
for (const name of readdirSync(dir)) {
|
|
192
|
-
if (name === "node_modules")
|
|
193
|
-
continue;
|
|
194
|
-
const full = join(dir, name);
|
|
195
|
-
const stat = statSync(full);
|
|
196
|
-
if (stat.isDirectory()) {
|
|
197
|
-
walkDir(full, out);
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
if (isMigratableFile(name))
|
|
201
|
-
out.push(full);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
function collectFiles(paths) {
|
|
205
|
-
const files = [];
|
|
206
|
-
for (const p of paths) {
|
|
207
|
-
const abs = resolve(p);
|
|
208
|
-
const stat = statSync(abs);
|
|
209
|
-
if (stat.isDirectory())
|
|
210
|
-
walkDir(abs, files);
|
|
211
|
-
else if (isMigratableFile(abs))
|
|
212
|
-
files.push(abs);
|
|
213
|
-
}
|
|
214
|
-
return files;
|
|
215
|
-
}
|
|
216
|
-
async function main() {
|
|
217
|
-
let options;
|
|
218
|
-
try {
|
|
219
|
-
options = parseArgs(process.argv.slice(2));
|
|
220
|
-
}
|
|
221
|
-
catch (err) {
|
|
222
|
-
if (err instanceof CliError) {
|
|
223
|
-
console.error(`[migrate-cross-tenant] ${err.message}`);
|
|
224
|
-
process.exit(2);
|
|
225
|
-
}
|
|
226
|
-
throw err;
|
|
227
|
-
}
|
|
228
|
-
const files = collectFiles(options.paths);
|
|
229
|
-
const allManual = [];
|
|
230
|
-
let filesChanged = 0;
|
|
231
|
-
let totalRewrites = 0;
|
|
232
|
-
for (const file of files) {
|
|
233
|
-
const before = readFileSync(file, "utf8");
|
|
234
|
-
if (!before.includes("crossTenant"))
|
|
235
|
-
continue;
|
|
236
|
-
const result = migrateCrossTenantSource(before, file);
|
|
237
|
-
allManual.push(...result.manual);
|
|
238
|
-
if (result.rewrites.length === 0)
|
|
239
|
-
continue;
|
|
240
|
-
filesChanged += 1;
|
|
241
|
-
totalRewrites += result.rewrites.length;
|
|
242
|
-
if (options.dryRun) {
|
|
243
|
-
console.log(`\n=== ${file} (${result.rewrites.length} rewrite(s)) ===`);
|
|
244
|
-
for (const r of result.rewrites) {
|
|
245
|
-
console.log(`${file}:${r.line}`);
|
|
246
|
-
console.log(`- ${r.before}`);
|
|
247
|
-
console.log(`+ ${r.after}`);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
else {
|
|
251
|
-
writeFileSync(file, result.output);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
console.log(`\n[migrate-cross-tenant] ${options.dryRun ? "would change" : "changed"} ${filesChanged} file(s); ` +
|
|
255
|
-
`rewrites: ${totalRewrites}`);
|
|
256
|
-
if (allManual.length > 0) {
|
|
257
|
-
console.log(`\n[migrate-cross-tenant] manual review needed (${allManual.length}):`);
|
|
258
|
-
for (const m of allManual) {
|
|
259
|
-
console.log(`${m.file}:${m.line} ${m.handlerName ?? "<unknown>"} ${m.why}`);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
process.exit(0);
|
|
263
|
-
}
|
|
264
|
-
if (import.meta.main) {
|
|
265
|
-
await main();
|
|
266
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
export type RewriteRule = "global-table" | "own-tenant";
|
|
3
|
-
export interface Rewrite {
|
|
4
|
-
readonly file: string;
|
|
5
|
-
readonly line: number;
|
|
6
|
-
readonly rule: RewriteRule;
|
|
7
|
-
readonly before: string;
|
|
8
|
-
readonly after: string;
|
|
9
|
-
}
|
|
10
|
-
export interface ManualSite {
|
|
11
|
-
readonly file: string;
|
|
12
|
-
readonly line: number;
|
|
13
|
-
readonly enclosing: string;
|
|
14
|
-
readonly text: string;
|
|
15
|
-
}
|
|
16
|
-
export interface MigrateDbRawResult {
|
|
17
|
-
readonly output: string;
|
|
18
|
-
readonly rewrites: Rewrite[];
|
|
19
|
-
readonly manual: ManualSite[];
|
|
20
|
-
}
|
|
21
|
-
export declare function migrateDbRawSource(source: string, fileName: string, opts: {
|
|
22
|
-
globalTables: ReadonlySet<string>;
|
|
23
|
-
}): MigrateDbRawResult;
|