@cosmicdrift/kumiko-framework 0.387.0 → 0.388.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 +6 -1
- package/agents/examples/anonymous-access.md +1 -1
- package/agents/examples/apex-landing.md +204 -0
- package/agents/examples/api-worker-split.md +64 -0
- package/agents/examples/app-config.md +14 -0
- package/agents/examples/auth-mfa.md +115 -0
- package/agents/examples/env-composition.md +28 -1
- package/agents/examples/legal-pages.md +1 -1
- package/agents/examples/prod-boot-options.md +16 -0
- package/agents/examples/rate-limiting.md +48 -0
- package/agents/examples/record-detail-layout.md +692 -0
- package/agents/examples/seed-migration.md +212 -0
- package/agents/examples/wizard-form.md +941 -0
- package/agents/examples/writeform-section.md +548 -0
- package/agents/features.md +19 -2
- package/agents/widgets.md +1 -0
- package/dist/api/client-error-route.js +8 -2
- package/dist/api/index.d.ts +3 -1
- package/dist/api/index.js +3 -1
- package/dist/api/request-context.d.ts +1 -1
- package/dist/api/request-context.js +1 -1
- package/dist/api/server-boot-checks.d.ts +10 -0
- package/dist/api/server-boot-checks.js +157 -0
- package/dist/api/server-event-dispatcher.d.ts +26 -0
- package/dist/api/server-event-dispatcher.js +208 -0
- package/dist/api/server-extra-routes.d.ts +11 -0
- package/dist/api/server-extra-routes.js +203 -0
- package/dist/api/server-middleware.d.ts +20 -0
- package/dist/api/server-middleware.js +211 -0
- package/dist/api/server-routes.d.ts +19 -0
- package/dist/api/server-routes.js +293 -0
- package/dist/api/server-websocket-routes.d.ts +4 -0
- package/dist/api/server-websocket-routes.js +108 -0
- package/dist/api/server-wiring.d.ts +32 -0
- package/dist/api/server-wiring.js +236 -0
- package/dist/api/server.d.ts +10 -15
- package/dist/api/server.js +48 -1251
- package/dist/api/session-cookies.js +4 -4
- package/dist/api/sse-route.js +16 -12
- package/dist/crypto/owner-transition-rekey.d.ts +5 -1
- package/dist/crypto/owner-transition-rekey.js +1 -1
- package/dist/crypto/pii-field-encryption.d.ts +5 -1
- package/dist/crypto/pii-field-encryption.js +3 -1
- package/dist/db/aggregate-sql.d.ts +21 -0
- package/dist/db/aggregate-sql.js +155 -0
- package/dist/db/closed-connection.d.ts +9 -0
- package/dist/db/closed-connection.js +95 -0
- package/dist/db/dialect.js +1 -1
- package/dist/db/distinct-on.d.ts +1 -1
- package/dist/db/driver-row-coercion.d.ts +2 -0
- package/dist/db/driver-row-coercion.js +186 -0
- package/dist/db/driver-value-preparation.d.ts +16 -0
- package/dist/db/driver-value-preparation.js +99 -0
- package/dist/db/entity-field-encryption.d.ts +1 -0
- package/dist/db/entity-field-encryption.js +1 -1
- package/dist/db/entity-table-meta.js +103 -110
- package/dist/db/event-store-executor-context.d.ts +2 -1
- package/dist/db/event-store-executor-read-aggregate.d.ts +8 -0
- package/dist/db/event-store-executor-read-aggregate.js +112 -0
- package/dist/db/event-store-executor-read-filter.d.ts +31 -0
- package/dist/db/event-store-executor-read-filter.js +203 -0
- package/dist/db/event-store-executor-read-paging.d.ts +14 -0
- package/dist/db/event-store-executor-read-paging.js +99 -0
- package/dist/db/event-store-executor-read-rows.d.ts +6 -0
- package/dist/db/event-store-executor-read-rows.js +32 -0
- package/dist/db/event-store-executor-read-search.d.ts +28 -0
- package/dist/db/event-store-executor-read-search.js +221 -0
- package/dist/db/event-store-executor-read-sort.d.ts +41 -0
- package/dist/db/event-store-executor-read-sort.js +130 -0
- package/dist/db/event-store-executor-read-where.d.ts +24 -0
- package/dist/db/event-store-executor-read-where.js +81 -0
- package/dist/db/event-store-executor-read.d.ts +2 -9
- package/dist/db/event-store-executor-read.js +48 -701
- package/dist/db/event-store-executor-write-steps.d.ts +20 -0
- package/dist/db/event-store-executor-write-steps.js +100 -0
- package/dist/db/event-store-executor-write.js +395 -499
- package/dist/db/index.d.ts +6 -2
- package/dist/db/index.js +5 -1
- package/dist/db/list-aggregate-labels.d.ts +14 -0
- package/dist/db/list-aggregate-labels.js +26 -0
- package/dist/db/migrate-generator.js +71 -86
- package/dist/db/migrate-runner.js +4 -1
- package/dist/db/query.d.ts +2 -15
- package/dist/db/query.js +8 -761
- package/dist/db/reference-data.js +6 -1
- package/dist/db/table-builder.js +113 -166
- package/dist/db/table-info.d.ts +10 -0
- package/dist/db/table-info.js +45 -0
- package/dist/db/temporal-value-guards.d.ts +6 -0
- package/dist/db/temporal-value-guards.js +15 -0
- package/dist/db/tenant-db-runner.d.ts +1 -0
- package/dist/db/tenant-db-runner.js +3 -0
- package/dist/db/tenant-db.js +39 -9
- package/dist/db/where-clause.d.ts +6 -0
- package/dist/db/where-clause.js +236 -0
- package/dist/derivatives/delete-stored-file.js +2 -1
- package/dist/engine/boot-validator/boot-check.js +1 -1
- package/dist/engine/boot-validator/detail-screens.js +2 -2
- package/dist/engine/boot-validator/entity-handler.d.ts +1 -0
- package/dist/engine/boot-validator/entity-handler.js +17 -0
- package/dist/engine/boot-validator/form-derivations.js +35 -1
- package/dist/engine/boot-validator/i18n-keys.js +5 -3
- package/dist/engine/boot-validator/index.js +2 -0
- package/dist/engine/boot-validator/pii-retention.js +233 -155
- package/dist/engine/boot-validator/screens-actions.d.ts +1 -0
- package/dist/engine/boot-validator/screens-actions.js +8 -1
- package/dist/engine/boot-validator/screens-dashboard.js +2 -5
- package/dist/engine/boot-validator/screens-forms.js +2 -6
- package/dist/engine/boot-validator/screens-kind-config-edit.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-config-edit.js +88 -0
- package/dist/engine/boot-validator/screens-kind-entity-edit.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-entity-edit.js +55 -0
- package/dist/engine/boot-validator/screens-kind-entity-list.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-entity-list.js +228 -0
- package/dist/engine/boot-validator/screens-kind-projection-detail.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-projection-detail.js +235 -0
- package/dist/engine/boot-validator/screens-kind-projection-list.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-projection-list.js +52 -0
- package/dist/engine/boot-validator/screens-kind-secret-mint.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-secret-mint.js +28 -0
- package/dist/engine/boot-validator/screens-kind-shared.d.ts +37 -0
- package/dist/engine/boot-validator/screens-kind-shared.js +142 -0
- package/dist/engine/boot-validator/screens-lists.d.ts +2 -1
- package/dist/engine/boot-validator/screens-lists.js +12 -9
- package/dist/engine/boot-validator/screens.d.ts +0 -2
- package/dist/engine/boot-validator/screens.js +99 -860
- package/dist/engine/build-app-schema.js +56 -96
- package/dist/engine/define-feature.js +9 -2
- package/dist/engine/define-handler.js +1 -0
- package/dist/engine/entity-handlers.d.ts +13 -0
- package/dist/engine/entity-handlers.js +10 -21
- package/dist/engine/entity-list-runtime-options.d.ts +8 -0
- package/dist/engine/entity-list-runtime-options.js +18 -0
- package/dist/engine/feature-ast/extractors/handlers.js +1 -0
- package/dist/engine/feature-ast/extractors/round2.js +25 -6
- package/dist/engine/feature-ast/patch.js +4 -182
- package/dist/engine/feature-ast/pattern-change-schema.d.ts +3 -0
- package/dist/engine/feature-ast/pattern-change-schema.js +1 -0
- package/dist/engine/feature-ast/pattern-id-matchers.d.ts +7 -0
- package/dist/engine/feature-ast/pattern-id-matchers.js +210 -0
- package/dist/engine/feature-ast/patterns.d.ts +9 -0
- package/dist/engine/feature-ast/patterns.js +43 -51
- package/dist/engine/feature-ast/render.js +48 -87
- package/dist/engine/feature-entity-handlers.js +3 -0
- package/dist/engine/form-derivations.js +18 -7
- package/dist/engine/handler-field-references.js +1 -1
- package/dist/engine/project-app-schema-for-roles.d.ts +2 -0
- package/dist/engine/project-app-schema-for-roles.js +6 -4
- package/dist/engine/registry-ingest.js +1 -1
- package/dist/engine/schema-builder.js +228 -212
- package/dist/engine/screen-helpers.js +1 -1
- package/dist/engine/screen-kind.d.ts +7 -0
- package/dist/engine/screen-kind.js +1 -0
- package/dist/engine/strip-disabled-features-from-app-schema.js +51 -14
- package/dist/engine/validate-projection-allowlist.js +2 -1
- package/dist/entrypoint/index.d.ts +4 -1
- package/dist/entrypoint/index.js +7 -1
- package/dist/errors/docs-url.d.ts +1 -1
- package/dist/errors/docs-url.js +22 -2
- package/dist/es-ops/context.js +1 -1
- package/dist/event-store/admin-api.js +1 -1
- package/dist/event-store/event-store.js +10 -4
- package/dist/files/bind-pending-file-refs.js +3 -3
- package/dist/files/create-file-ref-from-bytes.js +4 -1
- package/dist/files/file-handle.js +2 -3
- package/dist/files/file-routes.js +11 -8
- package/dist/files/resolve-content-type.js +43 -0
- package/dist/files/upload-ledger.js +7 -0
- package/dist/i18n/required-surface-keys.js +182 -206
- package/dist/internal/stack-support.d.ts +2 -1
- package/dist/internal/stack-support.js +1 -1
- package/dist/jobs/job-runner-context.d.ts +32 -0
- package/dist/jobs/job-runner-context.js +305 -0
- package/dist/jobs/job-runner-execution.d.ts +7 -0
- package/dist/jobs/job-runner-execution.js +160 -0
- package/dist/jobs/job-runner-gates.d.ts +19 -0
- package/dist/jobs/job-runner-gates.js +198 -0
- package/dist/jobs/job-runner-helpers.d.ts +17 -3
- package/dist/jobs/job-runner-helpers.js +61 -2
- package/dist/jobs/job-runner-scope.d.ts +44 -0
- package/dist/jobs/job-runner-scope.js +102 -0
- package/dist/jobs/job-runner-types.d.ts +12 -0
- package/dist/jobs/job-runner.d.ts +0 -1
- package/dist/jobs/job-runner.js +47 -657
- package/dist/observability/escape-hatch-report.d.ts +3 -0
- package/dist/observability/escape-hatch-report.js +12 -1
- package/dist/pipeline/cascade-handler.js +165 -138
- package/dist/pipeline/dispatch-query.js +10 -3
- package/dist/pipeline/dispatch-shared.d.ts +13 -3
- package/dist/pipeline/dispatch-shared.js +58 -710
- package/dist/pipeline/dispatch-write.js +296 -189
- package/dist/pipeline/event-dispatcher-admin.js +2 -1
- package/dist/pipeline/event-dispatcher-delivery.js +3 -5
- package/dist/pipeline/handler-context-auth.d.ts +9 -0
- package/dist/pipeline/handler-context-auth.js +54 -0
- package/dist/pipeline/handler-context-config.d.ts +14 -0
- package/dist/pipeline/handler-context-config.js +113 -0
- package/dist/pipeline/handler-context-db.d.ts +24 -0
- package/dist/pipeline/handler-context-db.js +149 -0
- package/dist/pipeline/handler-context-dispatch.d.ts +21 -0
- package/dist/pipeline/handler-context-dispatch.js +89 -0
- package/dist/pipeline/handler-context-events.d.ts +43 -0
- package/dist/pipeline/handler-context-events.js +237 -0
- package/dist/pipeline/handler-context-notify.d.ts +12 -0
- package/dist/pipeline/handler-context-notify.js +55 -0
- package/dist/pipeline/handler-context-observability.d.ts +7 -0
- package/dist/pipeline/handler-context-observability.js +21 -0
- package/dist/pipeline/handler-context-scope.d.ts +48 -0
- package/dist/pipeline/handler-context-scope.js +88 -0
- package/dist/pipeline/notify-scope.js +12 -1
- package/dist/pipeline/pre-handler-gates.js +20 -4
- package/dist/pipeline/pre-transaction-reservations.d.ts +1 -0
- package/dist/pipeline/pre-transaction-reservations.js +9 -3
- package/dist/pipeline/projection-rebuild.js +2 -1
- package/dist/schema-cli/apply-command.d.ts +2 -0
- package/dist/schema-cli/apply-command.js +97 -0
- package/dist/schema-cli/baseline-command.d.ts +2 -0
- package/dist/schema-cli/baseline-command.js +33 -0
- package/dist/schema-cli/generate-command.d.ts +2 -0
- package/dist/schema-cli/generate-command.js +90 -0
- package/dist/schema-cli/shared.d.ts +39 -0
- package/dist/schema-cli/shared.js +95 -0
- package/dist/schema-cli/status-command.d.ts +2 -0
- package/dist/schema-cli/status-command.js +73 -0
- package/dist/schema-cli/validate-boot-input.d.ts +5 -0
- package/dist/schema-cli/validate-boot-input.js +130 -0
- package/dist/schema-cli/validate-command.d.ts +2 -0
- package/dist/schema-cli/validate-command.js +130 -0
- package/dist/schema-cli.d.ts +2 -18
- package/dist/schema-cli.js +47 -604
- package/dist/search/meilisearch-adapter.js +2 -1
- package/dist/upgrade-cli.js +149 -121
- package/package.json +4 -4
- package/src/changes.json +310 -0
- package/src/scripts/codemod/feature-layout.ts +10 -2
- package/src/scripts/codemod/object-form.ts +41 -4
- package/src/scripts/codemod/package-merge.ts +58 -8
- package/src/scripts/codemod/pii-personal-migration.ts +175 -145
- package/src/scripts/codemod/testing-injection.ts +122 -42
- package/src/scripts/codemod/testing-move.ts +29 -7
|
@@ -177,50 +177,40 @@ function reportRawSubjectLiterals(
|
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
piiEncryptedProp,
|
|
189
|
-
"piiEncrypted: true on an entity field (removed from the type system) — needs a human decision, not a mechanical mapping",
|
|
190
|
-
);
|
|
191
|
-
// skip: reported above — piiEncrypted needs a human decision, not a mechanical mapping
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
180
|
+
type SubjectProp = { readonly name: SubjectFlagName; readonly prop: PropertyAssignment };
|
|
181
|
+
type PersonalInit = { readonly personal: string; readonly reason: string | undefined };
|
|
182
|
+
type FindSupport = "text" | "longText" | "none";
|
|
183
|
+
type FindabilityProps = {
|
|
184
|
+
readonly lookupable: PropertyAssignment | undefined;
|
|
185
|
+
readonly searchable: PropertyAssignment | undefined;
|
|
186
|
+
readonly sensitive: PropertyAssignment | undefined;
|
|
187
|
+
};
|
|
194
188
|
|
|
189
|
+
function singleSubjectFlag(obj: ObjectLiteralExpression): SubjectProp | undefined {
|
|
195
190
|
const subjectProps = SUBJECT_FLAG_NAMES.map((n) => ({ name: n, prop: findProp(obj, n) })).filter(
|
|
196
191
|
(x): x is { name: SubjectFlagName; prop: PropertyAssignment } => !!x.prop,
|
|
197
192
|
);
|
|
198
|
-
const lookupableProp = findProp(obj, "lookupable");
|
|
199
|
-
const searchableProp = findProp(obj, "searchable");
|
|
200
|
-
const sensitiveProp = findProp(obj, "sensitive");
|
|
201
|
-
const anonymizeProp = obj.getProperty("anonymize");
|
|
202
|
-
|
|
203
193
|
if (subjectProps.length === 0) {
|
|
204
194
|
// `searchable`/`sensitive` alone remain valid, unrelated FieldDef
|
|
205
195
|
// properties — left untouched. `lookupable` alone can never compile
|
|
206
196
|
// (always an excess property without a `personal` to attach `find`
|
|
207
197
|
// to). `anonymize` alone has no PersonalAnnotations arm to live on.
|
|
198
|
+
const lookupableProp = findProp(obj, "lookupable");
|
|
208
199
|
if (lookupableProp) {
|
|
209
200
|
report(
|
|
210
201
|
lookupableProp,
|
|
211
202
|
"lookupable without any subject annotation — no `personal` to attach `find` to",
|
|
212
203
|
);
|
|
213
204
|
}
|
|
205
|
+
const anonymizeProp = obj.getProperty("anonymize");
|
|
214
206
|
if (anonymizeProp) {
|
|
215
207
|
report(
|
|
216
208
|
anonymizeProp,
|
|
217
209
|
"anonymize without any subject annotation — no PersonalAnnotations arm accepts anonymize alone",
|
|
218
210
|
);
|
|
219
211
|
}
|
|
220
|
-
|
|
221
|
-
return;
|
|
212
|
+
return undefined;
|
|
222
213
|
}
|
|
223
|
-
|
|
224
214
|
if (subjectProps.length > 1) {
|
|
225
215
|
const first = subjectProps[0];
|
|
226
216
|
if (first) {
|
|
@@ -229,152 +219,154 @@ function processObjectLiteral(obj: ObjectLiteralExpression, factoryName: string)
|
|
|
229
219
|
`multiple subject annotations on one field (${subjectProps.map((s) => s.name).join(", ")}) — needs a human decision on which subject is correct`,
|
|
230
220
|
);
|
|
231
221
|
}
|
|
232
|
-
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const subject = subjectProps[0];
|
|
237
|
-
if (!subject) {
|
|
238
|
-
// skip: length===0 and length>1 already returned — empty [0] is unreachable
|
|
239
|
-
return;
|
|
222
|
+
return undefined;
|
|
240
223
|
}
|
|
224
|
+
return subjectProps[0];
|
|
225
|
+
}
|
|
241
226
|
|
|
227
|
+
function findSupportOf(factoryName: string, subject: SubjectProp): FindSupport | undefined {
|
|
242
228
|
if (NO_PERSONAL_FACTORIES.has(factoryName)) {
|
|
243
229
|
report(
|
|
244
230
|
subject.prop,
|
|
245
231
|
`${factoryName} has no personal-annotation support — subject flag "${subject.name}" cannot be expressed here`,
|
|
246
232
|
);
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
233
|
+
return undefined;
|
|
234
|
+
}
|
|
235
|
+
if (TEXT_FIND_FACTORIES.has(factoryName)) return "text";
|
|
236
|
+
if (LONGTEXT_FIND_FACTORIES.has(factoryName)) return "longText";
|
|
237
|
+
if (NO_FIND_FACTORIES.has(factoryName)) return "none";
|
|
238
|
+
report(
|
|
239
|
+
subject.prop,
|
|
240
|
+
`unrecognized field factory "${factoryName}" — cannot verify its PersonalAnnotations shape`,
|
|
241
|
+
);
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function personalFromTrueFlag(
|
|
246
|
+
personal: string,
|
|
247
|
+
): (subject: SubjectProp) => PersonalInit | undefined {
|
|
248
|
+
return (subject) => {
|
|
249
|
+
if (isTrueLiteral(subject.prop.getInitializer())) return { personal, reason: undefined };
|
|
250
|
+
report(subject.prop, `${subject.name} is set to a non-\`true\` value — cannot infer intent`);
|
|
251
|
+
return undefined;
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function personalFromAllowPlaintext(subject: SubjectProp): PersonalInit | undefined {
|
|
256
|
+
const init = subject.prop.getInitializer();
|
|
257
|
+
if (!init) {
|
|
258
|
+
report(subject.prop, "allowPlaintext has no value");
|
|
259
|
+
return undefined;
|
|
260
260
|
}
|
|
261
|
+
const reason = Node.isStringLiteral(init)
|
|
262
|
+
? `"${normalizeReasonSlug(init.getLiteralText())}"`
|
|
263
|
+
: init.getText();
|
|
264
|
+
return { personal: "false", reason };
|
|
265
|
+
}
|
|
261
266
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
if (
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
if (!isTrueLiteral(subject.prop.getInitializer())) {
|
|
273
|
-
report(subject.prop, "tenantOwned is set to a non-`true` value — cannot infer intent");
|
|
274
|
-
// skip: reported above — non-`true` tenantOwned value, can't infer intent
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
personalInit = `"tenant"`;
|
|
278
|
-
} else if (subject.name === "subjectRef") {
|
|
279
|
-
if (!isTrueLiteral(subject.prop.getInitializer())) {
|
|
280
|
-
report(subject.prop, "subjectRef is set to a non-`true` value — cannot infer intent");
|
|
281
|
-
// skip: reported above — non-`true` subjectRef value, can't infer intent
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
personalInit = `"ref"`;
|
|
285
|
-
} else if (subject.name === "allowPlaintext") {
|
|
286
|
-
const init = subject.prop.getInitializer();
|
|
287
|
-
if (!init) {
|
|
288
|
-
report(subject.prop, "allowPlaintext has no value");
|
|
289
|
-
// skip: reported above — allowPlaintext has no value to migrate
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
personalInit = "false";
|
|
293
|
-
reasonInit = Node.isStringLiteral(init)
|
|
294
|
-
? `"${normalizeReasonSlug(init.getLiteralText())}"`
|
|
295
|
-
: init.getText();
|
|
296
|
-
} else {
|
|
297
|
-
const init = subject.prop.getInitializer();
|
|
298
|
-
if (!init || !Node.isObjectLiteralExpression(init)) {
|
|
299
|
-
report(subject.prop, "userOwned value is not an object literal — cannot extract ownerField");
|
|
300
|
-
// skip: reported above — userOwned value isn't an object literal, can't extract ownerField
|
|
301
|
-
return;
|
|
302
|
-
}
|
|
303
|
-
const ownerFieldProp = findProp(init, "ownerField");
|
|
304
|
-
const ownerFieldInit = ownerFieldProp?.getInitializer();
|
|
305
|
-
if (!ownerFieldInit) {
|
|
306
|
-
report(subject.prop, 'userOwned is missing an "ownerField" property');
|
|
307
|
-
// skip: reported above — userOwned is missing its ownerField property
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
310
|
-
personalInit = `{ of: ${ownerFieldInit.getText()} }`;
|
|
267
|
+
function personalFromUserOwned(subject: SubjectProp): PersonalInit | undefined {
|
|
268
|
+
const init = subject.prop.getInitializer();
|
|
269
|
+
if (!init || !Node.isObjectLiteralExpression(init)) {
|
|
270
|
+
report(subject.prop, "userOwned value is not an object literal — cannot extract ownerField");
|
|
271
|
+
return undefined;
|
|
272
|
+
}
|
|
273
|
+
const ownerFieldInit = findProp(init, "ownerField")?.getInitializer();
|
|
274
|
+
if (!ownerFieldInit) {
|
|
275
|
+
report(subject.prop, 'userOwned is missing an "ownerField" property');
|
|
276
|
+
return undefined;
|
|
311
277
|
}
|
|
278
|
+
return { personal: `{ of: ${ownerFieldInit.getText()} }`, reason: undefined };
|
|
279
|
+
}
|
|
312
280
|
|
|
313
|
-
|
|
314
|
-
|
|
281
|
+
// One row per subject flag of the mapping table in the file header; a row
|
|
282
|
+
// reports and returns undefined when the flag value cannot be mapped.
|
|
283
|
+
const PERSONAL_INIT_BY_SUBJECT: Record<
|
|
284
|
+
SubjectFlagName,
|
|
285
|
+
(subject: SubjectProp) => PersonalInit | undefined
|
|
286
|
+
> = {
|
|
287
|
+
pii: personalFromTrueFlag(`"self"`),
|
|
288
|
+
userOwned: personalFromUserOwned,
|
|
289
|
+
tenantOwned: personalFromTrueFlag(`"tenant"`),
|
|
290
|
+
subjectRef: personalFromTrueFlag(`"ref"`),
|
|
291
|
+
allowPlaintext: personalFromAllowPlaintext,
|
|
292
|
+
};
|
|
315
293
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
personal:
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
});
|
|
331
|
-
counts[subject.name === "subjectRef" ? "ref" : "personal-false"]++;
|
|
332
|
-
// skip: already transformed above — nothing left to check for this subject
|
|
294
|
+
// These PersonalAnnotations arms carry no `find` field at all.
|
|
295
|
+
function migrateFindlessSubject(
|
|
296
|
+
obj: ObjectLiteralExpression,
|
|
297
|
+
subject: SubjectProp,
|
|
298
|
+
personal: PersonalInit,
|
|
299
|
+
lookupableProp: PropertyAssignment | undefined,
|
|
300
|
+
): void {
|
|
301
|
+
const isRef = subject.name === "subjectRef";
|
|
302
|
+
if (lookupableProp) {
|
|
303
|
+
report(
|
|
304
|
+
lookupableProp,
|
|
305
|
+
`lookupable combined with personal:${isRef ? '"ref"' : "false"} — findability doesn't apply to this subject, table has no mapping`,
|
|
306
|
+
);
|
|
307
|
+
// skip: reported above — lookupable doesn't apply to this subject, no mapping to migrate
|
|
333
308
|
return;
|
|
334
309
|
}
|
|
310
|
+
applyTransform(obj, [subject.prop], {
|
|
311
|
+
personal: personal.personal,
|
|
312
|
+
reason: personal.reason,
|
|
313
|
+
find: undefined,
|
|
314
|
+
});
|
|
315
|
+
counts[isRef ? "ref" : "personal-false"]++;
|
|
316
|
+
}
|
|
335
317
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
});
|
|
351
|
-
counts["no-find"]++;
|
|
352
|
-
// skip: already transformed above — no-find factory has nothing further to check
|
|
318
|
+
function migrateNoFindField(
|
|
319
|
+
obj: ObjectLiteralExpression,
|
|
320
|
+
factoryName: string,
|
|
321
|
+
subject: SubjectProp,
|
|
322
|
+
personal: PersonalInit,
|
|
323
|
+
findability: FindabilityProps,
|
|
324
|
+
): void {
|
|
325
|
+
const stray = findability.lookupable ?? findability.searchable ?? findability.sensitive;
|
|
326
|
+
if (stray) {
|
|
327
|
+
report(
|
|
328
|
+
stray,
|
|
329
|
+
`${factoryName} has no \`find\` in its PersonalAnnotations — findability flag present on a non-text field`,
|
|
330
|
+
);
|
|
331
|
+
// skip: reported above — findability flag on a non-text field, no valid find to attach
|
|
353
332
|
return;
|
|
354
333
|
}
|
|
334
|
+
applyTransform(obj, [subject.prop], {
|
|
335
|
+
personal: personal.personal,
|
|
336
|
+
reason: undefined,
|
|
337
|
+
find: undefined,
|
|
338
|
+
});
|
|
339
|
+
counts["no-find"]++;
|
|
340
|
+
}
|
|
355
341
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
342
|
+
// lookupable + searchable and searchable alone both map to "fuzzy".
|
|
343
|
+
function findValueOf(findability: FindabilityProps): "exact" | "fuzzy" | "none" | "secret" {
|
|
344
|
+
if (findability.sensitive) return "secret";
|
|
345
|
+
if (findability.searchable) return "fuzzy";
|
|
346
|
+
if (findability.lookupable) return "exact";
|
|
347
|
+
return "none";
|
|
348
|
+
}
|
|
359
349
|
|
|
360
|
-
|
|
350
|
+
function migrateTextFindField(
|
|
351
|
+
obj: ObjectLiteralExpression,
|
|
352
|
+
subject: SubjectProp,
|
|
353
|
+
personal: PersonalInit,
|
|
354
|
+
findability: FindabilityProps,
|
|
355
|
+
support: Exclude<FindSupport, "none">,
|
|
356
|
+
): void {
|
|
357
|
+
const { lookupable, searchable, sensitive } = findability;
|
|
358
|
+
if (sensitive && (lookupable || searchable)) {
|
|
361
359
|
report(
|
|
362
|
-
|
|
360
|
+
sensitive,
|
|
363
361
|
'sensitive combined with lookupable/searchable — two find values ("secret" vs "exact"/"fuzzy"), needs a human decision',
|
|
364
362
|
);
|
|
365
363
|
// skip: reported above — sensitive plus lookupable/searchable is an ambiguous find value
|
|
366
364
|
return;
|
|
367
365
|
}
|
|
368
366
|
|
|
369
|
-
|
|
370
|
-
if (
|
|
371
|
-
|
|
372
|
-
else if (hasLookupable) find = "exact";
|
|
373
|
-
else if (hasSearchable) find = "fuzzy";
|
|
374
|
-
else find = "none";
|
|
375
|
-
|
|
376
|
-
if (isLongTextFind && (find === "exact" || find === "fuzzy")) {
|
|
377
|
-
const findSite = lookupableProp ?? searchableProp;
|
|
367
|
+
const find = findValueOf(findability);
|
|
368
|
+
if (support === "longText" && (find === "exact" || find === "fuzzy")) {
|
|
369
|
+
const findSite = lookupable ?? searchable;
|
|
378
370
|
if (findSite) {
|
|
379
371
|
report(
|
|
380
372
|
findSite,
|
|
@@ -388,7 +380,7 @@ function processObjectLiteral(obj: ObjectLiteralExpression, factoryName: string)
|
|
|
388
380
|
// searchable-alone -> "fuzzy" makes expandPersonalAnnotations add
|
|
389
381
|
// `lookupable: true`, which the field didn't carry before — that's a
|
|
390
382
|
// new `_bidx` column (DDL migration), tracked separately for the report.
|
|
391
|
-
if (
|
|
383
|
+
if (searchable && !lookupable) {
|
|
392
384
|
newLookupableSites.push({
|
|
393
385
|
file: obj.getSourceFile().getFilePath(),
|
|
394
386
|
line: subject.prop.getStartLineNumber(),
|
|
@@ -397,13 +389,51 @@ function processObjectLiteral(obj: ObjectLiteralExpression, factoryName: string)
|
|
|
397
389
|
});
|
|
398
390
|
}
|
|
399
391
|
|
|
400
|
-
const removedProps = [subject.prop,
|
|
392
|
+
const removedProps = [subject.prop, lookupable, searchable, sensitive].filter(
|
|
401
393
|
(p): p is PropertyAssignment => !!p,
|
|
402
394
|
);
|
|
403
|
-
applyTransform(obj, removedProps, { personal:
|
|
395
|
+
applyTransform(obj, removedProps, { personal: personal.personal, reason: undefined, find });
|
|
404
396
|
counts[find]++;
|
|
405
397
|
}
|
|
406
398
|
|
|
399
|
+
function processObjectLiteral(obj: ObjectLiteralExpression, factoryName: string): void {
|
|
400
|
+
// skip: field already carries `personal` — already migrated, idempotent no-op
|
|
401
|
+
if (obj.getProperty("personal")) return;
|
|
402
|
+
|
|
403
|
+
const piiEncryptedProp = obj.getProperty("piiEncrypted");
|
|
404
|
+
if (piiEncryptedProp) {
|
|
405
|
+
report(
|
|
406
|
+
piiEncryptedProp,
|
|
407
|
+
"piiEncrypted: true on an entity field (removed from the type system) — needs a human decision, not a mechanical mapping",
|
|
408
|
+
);
|
|
409
|
+
// skip: reported above — piiEncrypted needs a human decision, not a mechanical mapping
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const subject = singleSubjectFlag(obj);
|
|
414
|
+
// skip: no subject flag, or several, reported by singleSubjectFlag where needed
|
|
415
|
+
if (!subject) return;
|
|
416
|
+
const support = findSupportOf(factoryName, subject);
|
|
417
|
+
// skip: factory without a verifiable PersonalAnnotations shape, reported by findSupportOf
|
|
418
|
+
if (!support) return;
|
|
419
|
+
const personal = PERSONAL_INIT_BY_SUBJECT[subject.name](subject);
|
|
420
|
+
// skip: flag value cannot be mapped, reported by its PERSONAL_INIT_BY_SUBJECT row
|
|
421
|
+
if (!personal) return;
|
|
422
|
+
|
|
423
|
+
const findability: FindabilityProps = {
|
|
424
|
+
lookupable: findProp(obj, "lookupable"),
|
|
425
|
+
searchable: findProp(obj, "searchable"),
|
|
426
|
+
sensitive: findProp(obj, "sensitive"),
|
|
427
|
+
};
|
|
428
|
+
if (subject.name === "subjectRef" || subject.name === "allowPlaintext") {
|
|
429
|
+
migrateFindlessSubject(obj, subject, personal, findability.lookupable);
|
|
430
|
+
} else if (support === "none") {
|
|
431
|
+
migrateNoFindField(obj, factoryName, subject, personal, findability);
|
|
432
|
+
} else {
|
|
433
|
+
migrateTextFindField(obj, subject, personal, findability, support);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
407
437
|
function applyTransform(
|
|
408
438
|
obj: ObjectLiteralExpression,
|
|
409
439
|
removedProps: readonly PropertyAssignment[],
|
|
@@ -12,7 +12,14 @@
|
|
|
12
12
|
// Without a mode flag it writes (kumiko-upgrade --apply calls it that way). --check exits 1
|
|
13
13
|
// while any rewritable or manual site is left; --dry-run only reports.
|
|
14
14
|
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
type ExportDeclaration,
|
|
17
|
+
type ExportSpecifier,
|
|
18
|
+
type ImportDeclaration,
|
|
19
|
+
type ImportSpecifier,
|
|
20
|
+
Project,
|
|
21
|
+
SyntaxKind,
|
|
22
|
+
} from "ts-morph";
|
|
16
23
|
import {
|
|
17
24
|
type CodemodResult,
|
|
18
25
|
ensureNamedImport,
|
|
@@ -64,6 +71,115 @@ function reportNamespaceUses(
|
|
|
64
71
|
}
|
|
65
72
|
}
|
|
66
73
|
|
|
74
|
+
function reportStarReexport(
|
|
75
|
+
declaration: ExportDeclaration,
|
|
76
|
+
filePath: string,
|
|
77
|
+
result: SiteLists,
|
|
78
|
+
): void {
|
|
79
|
+
result.manual.push({
|
|
80
|
+
file: filePath,
|
|
81
|
+
line: declaration.getStartLineNumber(),
|
|
82
|
+
description: `export * from ${declaration.getModuleSpecifierValue()}: re-export the moved names from ${TARGET_MODULE} by hand`,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function moveNamedReexports(
|
|
87
|
+
declaration: ExportDeclaration,
|
|
88
|
+
movedExports: readonly ExportSpecifier[],
|
|
89
|
+
filePath: string,
|
|
90
|
+
result: SiteLists,
|
|
91
|
+
): void {
|
|
92
|
+
const line = declaration.getStartLineNumber();
|
|
93
|
+
const snapshots = movedExports.map((named) => ({
|
|
94
|
+
name: named.getName(),
|
|
95
|
+
alias: named.getAliasNode()?.getText(),
|
|
96
|
+
isTypeOnly: named.isTypeOnly(),
|
|
97
|
+
}));
|
|
98
|
+
for (const named of movedExports) named.remove();
|
|
99
|
+
const typeOnlyDeclaration = declaration.isTypeOnly();
|
|
100
|
+
if (declaration.getNamedExports().length === 0) declaration.remove();
|
|
101
|
+
declaration.getSourceFile().addExportDeclaration({
|
|
102
|
+
moduleSpecifier: TARGET_MODULE,
|
|
103
|
+
namedExports: snapshots,
|
|
104
|
+
isTypeOnly: typeOnlyDeclaration,
|
|
105
|
+
});
|
|
106
|
+
result.rewrites.push({
|
|
107
|
+
file: filePath,
|
|
108
|
+
line,
|
|
109
|
+
description: `export { ${snapshots.map((snapshot) => snapshot.name).join(", ")} } -> ${TARGET_MODULE}`,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function rewriteReexports(
|
|
114
|
+
declaration: ExportDeclaration,
|
|
115
|
+
moved: ReadonlySet<string>,
|
|
116
|
+
filePath: string,
|
|
117
|
+
result: SiteLists,
|
|
118
|
+
): void {
|
|
119
|
+
const namedExports = declaration.getNamedExports();
|
|
120
|
+
const movedExports = namedExports.filter((named) => moved.has(named.getName()));
|
|
121
|
+
if (namedExports.length === 0) reportStarReexport(declaration, filePath, result);
|
|
122
|
+
else if (movedExports.length > 0) moveNamedReexports(declaration, movedExports, filePath, result);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function moveNamedImports(
|
|
126
|
+
declaration: ImportDeclaration,
|
|
127
|
+
movedImports: readonly ImportSpecifier[],
|
|
128
|
+
filePath: string,
|
|
129
|
+
result: SiteLists,
|
|
130
|
+
): void {
|
|
131
|
+
const line = declaration.getStartLineNumber();
|
|
132
|
+
const typeOnlyDeclaration = declaration.isTypeOnly();
|
|
133
|
+
const sourceFile = declaration.getSourceFile();
|
|
134
|
+
const names = movedImports.map((named) => named.getName());
|
|
135
|
+
// Collected first: removing a specifier invalidates the nodes of its siblings.
|
|
136
|
+
const snapshots = movedImports.map((named) => ({
|
|
137
|
+
name: named.getName(),
|
|
138
|
+
alias: named.getAliasNode()?.getText(),
|
|
139
|
+
typeOnly: named.isTypeOnly(),
|
|
140
|
+
}));
|
|
141
|
+
for (const named of movedImports) named.remove();
|
|
142
|
+
if (
|
|
143
|
+
declaration.getNamedImports().length === 0 &&
|
|
144
|
+
declaration.getNamespaceImport() === undefined &&
|
|
145
|
+
declaration.getDefaultImport() === undefined
|
|
146
|
+
) {
|
|
147
|
+
declaration.remove();
|
|
148
|
+
}
|
|
149
|
+
for (const snapshot of snapshots) {
|
|
150
|
+
const typeOnly = typeOnlyDeclaration || snapshot.typeOnly;
|
|
151
|
+
if (snapshot.alias === undefined) {
|
|
152
|
+
ensureNamedImport(sourceFile, TARGET_MODULE, snapshot.name, typeOnly);
|
|
153
|
+
} else {
|
|
154
|
+
sourceFile.addImportDeclaration({
|
|
155
|
+
moduleSpecifier: TARGET_MODULE,
|
|
156
|
+
namedImports: [{ name: snapshot.name, alias: snapshot.alias }],
|
|
157
|
+
isTypeOnly: typeOnly,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
result.rewrites.push({
|
|
162
|
+
file: filePath,
|
|
163
|
+
line,
|
|
164
|
+
description: `${names.join(", ")} -> ${TARGET_MODULE}`,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function rewriteImportDeclaration(
|
|
169
|
+
declaration: ImportDeclaration,
|
|
170
|
+
filePath: string,
|
|
171
|
+
result: SiteLists,
|
|
172
|
+
): void {
|
|
173
|
+
const moved = movedNamesOf(declaration);
|
|
174
|
+
if (moved !== undefined) {
|
|
175
|
+
reportNamespaceUses(declaration, moved, filePath, result);
|
|
176
|
+
const movedImports = declaration
|
|
177
|
+
.getNamedImports()
|
|
178
|
+
.filter((named) => moved.has(named.getName()));
|
|
179
|
+
if (movedImports.length > 0) moveNamedImports(declaration, movedImports, filePath, result);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
67
183
|
export function transformTestingInjectionSource(
|
|
68
184
|
source: string,
|
|
69
185
|
filePath = "source.ts",
|
|
@@ -77,47 +193,11 @@ export function transformTestingInjectionSource(
|
|
|
77
193
|
});
|
|
78
194
|
const sourceFile = project.createSourceFile(filePath, source);
|
|
79
195
|
for (const declaration of sourceFile.getImportDeclarations()) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
.filter((named) => moved.has(named.getName()));
|
|
86
|
-
if (movedImports.length === 0) continue;
|
|
87
|
-
const line = declaration.getStartLineNumber();
|
|
88
|
-
const typeOnlyDeclaration = declaration.isTypeOnly();
|
|
89
|
-
const names = movedImports.map((named) => named.getName());
|
|
90
|
-
// Collected first: removing a specifier invalidates the nodes of its siblings.
|
|
91
|
-
const snapshots = movedImports.map((named) => ({
|
|
92
|
-
name: named.getName(),
|
|
93
|
-
alias: named.getAliasNode()?.getText(),
|
|
94
|
-
typeOnly: named.isTypeOnly(),
|
|
95
|
-
}));
|
|
96
|
-
for (const named of movedImports) named.remove();
|
|
97
|
-
if (
|
|
98
|
-
declaration.getNamedImports().length === 0 &&
|
|
99
|
-
declaration.getNamespaceImport() === undefined &&
|
|
100
|
-
declaration.getDefaultImport() === undefined
|
|
101
|
-
) {
|
|
102
|
-
declaration.remove();
|
|
103
|
-
}
|
|
104
|
-
for (const snapshot of snapshots) {
|
|
105
|
-
const typeOnly = typeOnlyDeclaration || snapshot.typeOnly;
|
|
106
|
-
if (snapshot.alias === undefined) {
|
|
107
|
-
ensureNamedImport(sourceFile, TARGET_MODULE, snapshot.name, typeOnly);
|
|
108
|
-
} else {
|
|
109
|
-
sourceFile.addImportDeclaration({
|
|
110
|
-
moduleSpecifier: TARGET_MODULE,
|
|
111
|
-
namedImports: [{ name: snapshot.name, alias: snapshot.alias }],
|
|
112
|
-
isTypeOnly: typeOnly,
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
result.rewrites.push({
|
|
117
|
-
file: filePath,
|
|
118
|
-
line,
|
|
119
|
-
description: `${names.join(", ")} -> ${TARGET_MODULE}`,
|
|
120
|
-
});
|
|
196
|
+
rewriteImportDeclaration(declaration, filePath, result);
|
|
197
|
+
}
|
|
198
|
+
for (const declaration of sourceFile.getExportDeclarations()) {
|
|
199
|
+
const moved = MOVED_NAMES_BY_MODULE.get(declaration.getModuleSpecifierValue() ?? "");
|
|
200
|
+
if (moved !== undefined) rewriteReexports(declaration, moved, filePath, result);
|
|
121
201
|
}
|
|
122
202
|
const output = result.rewrites.length > 0 ? sourceFile.getFullText() : source;
|
|
123
203
|
return { output, ...result };
|
|
@@ -172,7 +172,9 @@ function repointDeclaration(
|
|
|
172
172
|
function isWholeModuleReference(declaration: NamedDeclaration): boolean {
|
|
173
173
|
if (Node.isImportDeclaration(declaration)) {
|
|
174
174
|
return (
|
|
175
|
-
declaration.getNamespaceImport() !== undefined ||
|
|
175
|
+
declaration.getNamespaceImport() !== undefined ||
|
|
176
|
+
declaration.getDefaultImport() !== undefined ||
|
|
177
|
+
declaration.getNamedImports().length === 0
|
|
176
178
|
);
|
|
177
179
|
}
|
|
178
180
|
return declaration.isNamespaceExport() || declaration.getNamedExports().length === 0;
|
|
@@ -186,14 +188,34 @@ function usesSplitName(
|
|
|
186
188
|
const namespaceName = Node.isImportDeclaration(declaration)
|
|
187
189
|
? declaration.getNamespaceImport()?.getText()
|
|
188
190
|
: undefined;
|
|
191
|
+
if (
|
|
192
|
+
Node.isImportDeclaration(declaration) &&
|
|
193
|
+
declaration.getDefaultImport() === undefined &&
|
|
194
|
+
namespaceName === undefined
|
|
195
|
+
) {
|
|
196
|
+
return false; // side-effect import: nothing is used, so any package of the old module serves
|
|
197
|
+
}
|
|
189
198
|
if (namespaceName === undefined) return true;
|
|
190
199
|
return sourceFile
|
|
191
|
-
.getDescendantsOfKind(SyntaxKind.
|
|
192
|
-
.some(
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
200
|
+
.getDescendantsOfKind(SyntaxKind.Identifier)
|
|
201
|
+
.some((identifier) => namespaceUseNeedsSplit(identifier, namespaceName, old));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Conservative: any use other than `ns.name` / `ns.name` in a type position (destructuring,
|
|
205
|
+
// passing the namespace on, typeof) cannot be attributed to one target package.
|
|
206
|
+
function namespaceUseNeedsSplit(identifier: Node, namespaceName: string, old: OldModule): boolean {
|
|
207
|
+
if (identifier.getText() !== namespaceName) return false;
|
|
208
|
+
if (identifier.getFirstAncestorByKind(SyntaxKind.ImportDeclaration) !== undefined) return false;
|
|
209
|
+
const parent = identifier.getParent();
|
|
210
|
+
if (Node.isPropertyAccessExpression(parent)) {
|
|
211
|
+
if (parent.getNameNode() === identifier) return false;
|
|
212
|
+
return targetForName(old, parent.getName()) !== mainTarget(old);
|
|
213
|
+
}
|
|
214
|
+
if (Node.isQualifiedName(parent)) {
|
|
215
|
+
if (parent.getRight() === identifier) return false;
|
|
216
|
+
return targetForName(old, parent.getRight().getText()) !== mainTarget(old);
|
|
217
|
+
}
|
|
218
|
+
return true;
|
|
197
219
|
}
|
|
198
220
|
|
|
199
221
|
function repointWholeModule(
|