@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
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
// `UnprocessableOpts.details` was tightened (fw#2460) to `& { readonly reason?:
|
|
3
|
-
// never }` — `reason` is owned exclusively by the ctor's first positional
|
|
4
|
-
// argument, which already folds it into `details` internally
|
|
5
|
-
// (`{ ...opts?.details, reason }`). Any consumer that ALSO puts `reason`
|
|
6
|
-
// inside its `details` object literal now fails with TS2322. This codemod
|
|
7
|
-
// removes that redundant `reason` property; the value is never lost, it was
|
|
8
|
-
// already duplicated from the positional argument.
|
|
9
|
-
//
|
|
10
|
-
// Usage: bun scripts/codemod/unprocessable-error-details-reason.ts <targetDir> [--dry-run]
|
|
11
|
-
import { readdirSync } from "node:fs";
|
|
12
|
-
import { join, relative, resolve } from "node:path";
|
|
13
|
-
import { Node, Project, SyntaxKind, } from "ts-morph";
|
|
14
|
-
const EXCLUDE_DIRS = new Set(["node_modules", "dist", "build"]);
|
|
15
|
-
const KUMIKO_MODULE_RE = /^@cosmicdrift\/kumiko-/;
|
|
16
|
-
function findTargetFiles(rootDir) {
|
|
17
|
-
// Walk the tree and skip excluded dirs while descending — filtering the
|
|
18
|
-
// absolute path after a full scan silently no-ops when the repo root
|
|
19
|
-
// itself contains "/build/" or "/node_modules/" (fw#2289).
|
|
20
|
-
const files = [];
|
|
21
|
-
const walk = (dir) => {
|
|
22
|
-
let entries;
|
|
23
|
-
try {
|
|
24
|
-
entries = readdirSync(dir, { withFileTypes: true });
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
// skip: unreadable directory during walk — treat as empty.
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
for (const ent of entries) {
|
|
31
|
-
if (ent.isDirectory()) {
|
|
32
|
-
if (EXCLUDE_DIRS.has(ent.name) || ent.name.startsWith("."))
|
|
33
|
-
continue;
|
|
34
|
-
walk(join(dir, ent.name));
|
|
35
|
-
continue;
|
|
36
|
-
}
|
|
37
|
-
if (ent.isFile() && /\.(ts|tsx)$/.test(ent.name)) {
|
|
38
|
-
files.push(join(dir, ent.name));
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
walk(rootDir);
|
|
43
|
-
return files.sort();
|
|
44
|
-
}
|
|
45
|
-
/** Local-name -> where it's imported from, for every named value import in the file. */
|
|
46
|
-
function collectImportedNames(sourceFile) {
|
|
47
|
-
const bindings = new Map();
|
|
48
|
-
for (const imp of sourceFile.getImportDeclarations()) {
|
|
49
|
-
if (imp.isTypeOnly())
|
|
50
|
-
continue;
|
|
51
|
-
const moduleSpecifier = imp.getModuleSpecifierValue();
|
|
52
|
-
for (const spec of imp.getNamedImports()) {
|
|
53
|
-
if (spec.isTypeOnly())
|
|
54
|
-
continue;
|
|
55
|
-
const importedName = spec.getName();
|
|
56
|
-
const localName = spec.getAliasNode()?.getText() ?? importedName;
|
|
57
|
-
bindings.set(localName, { importedName, moduleSpecifier });
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return bindings;
|
|
61
|
-
}
|
|
62
|
-
/** Resolves `opts.details` to a statically-inspectable object literal, or explains why it can't be. */
|
|
63
|
-
function resolveDetailsProperty(optsLiteral) {
|
|
64
|
-
const prop = optsLiteral.getProperty("details");
|
|
65
|
-
if (!prop)
|
|
66
|
-
return { kind: "absent" };
|
|
67
|
-
if (Node.isShorthandPropertyAssignment(prop)) {
|
|
68
|
-
return { kind: "unresolvable", note: "details is a shorthand property (a variable)" };
|
|
69
|
-
}
|
|
70
|
-
if (!Node.isPropertyAssignment(prop)) {
|
|
71
|
-
return { kind: "unresolvable", note: "details is not a plain property assignment" };
|
|
72
|
-
}
|
|
73
|
-
if (Node.isComputedPropertyName(prop.getNameNode())) {
|
|
74
|
-
return { kind: "unresolvable", note: "details key is computed" };
|
|
75
|
-
}
|
|
76
|
-
const init = prop.getInitializer();
|
|
77
|
-
if (!init)
|
|
78
|
-
return { kind: "unresolvable", note: "details has no initializer" };
|
|
79
|
-
if (Node.isObjectLiteralExpression(init))
|
|
80
|
-
return { kind: "object-literal", literal: init };
|
|
81
|
-
const note = Node.isIdentifier(init)
|
|
82
|
-
? `details is a variable ("${init.getText()}")`
|
|
83
|
-
: Node.isCallExpression(init)
|
|
84
|
-
? "details is a call-expression result"
|
|
85
|
-
: Node.isConditionalExpression(init)
|
|
86
|
-
? "details is a conditional expression"
|
|
87
|
-
: `details is not a statically-analyzable object literal (${init.getKindName()})`;
|
|
88
|
-
return { kind: "unresolvable", note };
|
|
89
|
-
}
|
|
90
|
-
/** True if removing this shorthand-bound identifier would leave its declaration with no other use. */
|
|
91
|
-
function wouldLeaveUnusedVariable(nameNode) {
|
|
92
|
-
try {
|
|
93
|
-
const refs = nameNode.findReferencesAsNodes();
|
|
94
|
-
const others = refs.filter((r) => r !== nameNode);
|
|
95
|
-
return others.length === 0;
|
|
96
|
-
}
|
|
97
|
-
catch {
|
|
98
|
-
// Can't verify — treat as unsafe rather than guess.
|
|
99
|
-
return true;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
/** Removes the `reason` property from a `details` object literal, in place. */
|
|
103
|
-
function removeReasonProperty(detailsLiteral) {
|
|
104
|
-
const properties = detailsLiteral.getProperties();
|
|
105
|
-
if (properties.some((p) => Node.isSpreadAssignment(p))) {
|
|
106
|
-
return {
|
|
107
|
-
kind: "skip",
|
|
108
|
-
note: "details object literal contains a spread — not statically decidable",
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
for (const prop of properties) {
|
|
112
|
-
if (Node.isPropertyAssignment(prop)) {
|
|
113
|
-
const nameNode = prop.getNameNode();
|
|
114
|
-
if (Node.isComputedPropertyName(nameNode)) {
|
|
115
|
-
const expr = nameNode.getExpression();
|
|
116
|
-
if (Node.isStringLiteral(expr) && expr.getLiteralText() === "reason") {
|
|
117
|
-
return { kind: "skip", note: "reason key is computed — not safe to remove mechanically" };
|
|
118
|
-
}
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
const propName = Node.isIdentifier(nameNode)
|
|
122
|
-
? nameNode.getText()
|
|
123
|
-
: Node.isStringLiteral(nameNode)
|
|
124
|
-
? nameNode.getLiteralText()
|
|
125
|
-
: undefined;
|
|
126
|
-
if (propName === "reason") {
|
|
127
|
-
prop.remove();
|
|
128
|
-
return { kind: "removed" };
|
|
129
|
-
}
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
if (Node.isShorthandPropertyAssignment(prop) && prop.getName() === "reason") {
|
|
133
|
-
if (wouldLeaveUnusedVariable(prop.getNameNode())) {
|
|
134
|
-
return {
|
|
135
|
-
kind: "skip",
|
|
136
|
-
note: "reason is a shorthand property; removing it would leave the bound variable unused",
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
prop.remove();
|
|
140
|
-
return { kind: "removed" };
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
return { kind: "absent" };
|
|
144
|
-
}
|
|
145
|
-
function processNewExpression(args, report) {
|
|
146
|
-
const optsArg = args[1];
|
|
147
|
-
if (!optsArg)
|
|
148
|
-
return "not-a-match";
|
|
149
|
-
if (!Node.isObjectLiteralExpression(optsArg)) {
|
|
150
|
-
report("second argument (opts) is not a statically-analyzable object literal — cannot verify details.reason");
|
|
151
|
-
return "skipped";
|
|
152
|
-
}
|
|
153
|
-
const detailsRes = resolveDetailsProperty(optsArg);
|
|
154
|
-
if (detailsRes.kind === "absent")
|
|
155
|
-
return "not-a-match";
|
|
156
|
-
if (detailsRes.kind === "unresolvable") {
|
|
157
|
-
report(detailsRes.note);
|
|
158
|
-
return "skipped";
|
|
159
|
-
}
|
|
160
|
-
const removal = removeReasonProperty(detailsRes.literal);
|
|
161
|
-
if (removal.kind === "absent")
|
|
162
|
-
return "not-a-match";
|
|
163
|
-
if (removal.kind === "skip") {
|
|
164
|
-
report(removal.note);
|
|
165
|
-
return "skipped";
|
|
166
|
-
}
|
|
167
|
-
return "changed";
|
|
168
|
-
}
|
|
169
|
-
/** Migrates one file in place. Returns how many `reason` properties were removed. */
|
|
170
|
-
function migrateFile(sourceFile, skips) {
|
|
171
|
-
const importedNames = collectImportedNames(sourceFile);
|
|
172
|
-
let removedCount = 0;
|
|
173
|
-
for (const newExpr of sourceFile.getDescendantsOfKind(SyntaxKind.NewExpression)) {
|
|
174
|
-
const callee = newExpr.getExpression();
|
|
175
|
-
if (!Node.isIdentifier(callee))
|
|
176
|
-
continue;
|
|
177
|
-
const localName = callee.getText();
|
|
178
|
-
const binding = importedNames.get(localName);
|
|
179
|
-
const isFrameworkUnprocessableError = binding?.importedName === "UnprocessableError";
|
|
180
|
-
if (!isFrameworkUnprocessableError) {
|
|
181
|
-
if (localName !== "UnprocessableError")
|
|
182
|
-
continue;
|
|
183
|
-
const note = binding
|
|
184
|
-
? `UnprocessableError is imported from "${binding.moduleSpecifier}", not a @cosmicdrift/kumiko-* package`
|
|
185
|
-
: "UnprocessableError has no @cosmicdrift/kumiko-* import in this file (likely a locally defined class)";
|
|
186
|
-
skips.push({ file: sourceFile.getFilePath(), line: newExpr.getStartLineNumber(), note });
|
|
187
|
-
continue;
|
|
188
|
-
}
|
|
189
|
-
if (!KUMIKO_MODULE_RE.test(binding.moduleSpecifier)) {
|
|
190
|
-
skips.push({
|
|
191
|
-
file: sourceFile.getFilePath(),
|
|
192
|
-
line: newExpr.getStartLineNumber(),
|
|
193
|
-
note: `UnprocessableError is imported from "${binding.moduleSpecifier}", not a @cosmicdrift/kumiko-* package`,
|
|
194
|
-
});
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
const outcome = processNewExpression(newExpr.getArguments(), (note) => {
|
|
198
|
-
skips.push({ file: sourceFile.getFilePath(), line: newExpr.getStartLineNumber(), note });
|
|
199
|
-
});
|
|
200
|
-
if (outcome === "changed")
|
|
201
|
-
removedCount++;
|
|
202
|
-
}
|
|
203
|
-
return removedCount;
|
|
204
|
-
}
|
|
205
|
-
async function main() {
|
|
206
|
-
const positional = process.argv.slice(2).filter((a) => !a.startsWith("--"));
|
|
207
|
-
const dryRun = process.argv.includes("--dry-run");
|
|
208
|
-
const rootDir = resolve(positional[0] ?? process.cwd());
|
|
209
|
-
const files = findTargetFiles(rootDir);
|
|
210
|
-
const project = new Project({
|
|
211
|
-
skipAddingFilesFromTsConfig: true,
|
|
212
|
-
skipFileDependencyResolution: true,
|
|
213
|
-
});
|
|
214
|
-
let touchedFiles = 0;
|
|
215
|
-
let removedTotal = 0;
|
|
216
|
-
const skips = [];
|
|
217
|
-
for (const file of files) {
|
|
218
|
-
const sourceFile = project.addSourceFileAtPath(file);
|
|
219
|
-
const removed = migrateFile(sourceFile, skips);
|
|
220
|
-
if (removed === 0)
|
|
221
|
-
continue;
|
|
222
|
-
touchedFiles++;
|
|
223
|
-
removedTotal += removed;
|
|
224
|
-
if (!dryRun)
|
|
225
|
-
sourceFile.saveSync();
|
|
226
|
-
}
|
|
227
|
-
console.log(`\nScanned ${files.length} files under ${rootDir}${dryRun ? " (dry-run)" : ""}.`);
|
|
228
|
-
console.log(`Touched ${touchedFiles} files, removed ${removedTotal} redundant "reason" propert(y/ies).`);
|
|
229
|
-
console.log(`Skipped ${skips.length} site(s):`);
|
|
230
|
-
for (const s of skips) {
|
|
231
|
-
console.log(` ${relative(rootDir, s.file)}:${s.line} — ${s.note}`);
|
|
232
|
-
}
|
|
233
|
-
console.log();
|
|
234
|
-
}
|
|
235
|
-
await main();
|