joist-core 2.3.0-next.59 → 2.3.0-next.60
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/build/reactiveHints.cjs
CHANGED
|
@@ -110,7 +110,7 @@ function reverseSubHint(rootType, entityType, hint, meta, fields, readOnlyFields
|
|
|
110
110
|
maybeRecursive.push({
|
|
111
111
|
kind: isReadOnly ? "read-only" : "update",
|
|
112
112
|
entity: entityType,
|
|
113
|
-
fields:
|
|
113
|
+
fields: recursiveMembershipFields(meta, m2oFieldName),
|
|
114
114
|
path: [otherFieldName]
|
|
115
115
|
});
|
|
116
116
|
return reverseReactiveHint(rootType, entityType, subHint, void 0, false).map(appendPath(otherFieldName));
|
|
@@ -121,7 +121,7 @@ function reverseSubHint(rootType, entityType, hint, meta, fields, readOnlyFields
|
|
|
121
121
|
maybeRecursive.push({
|
|
122
122
|
kind: isReadOnly ? "read-only" : "update",
|
|
123
123
|
entity: entityType,
|
|
124
|
-
fields:
|
|
124
|
+
fields: recursiveMembershipFields(meta, m2oFieldName),
|
|
125
125
|
path: [otherFieldName]
|
|
126
126
|
});
|
|
127
127
|
return reverseReactiveHint(rootType, entityType, subHint, void 0, false).map(appendPath(otherFieldName));
|
|
@@ -131,7 +131,7 @@ function reverseSubHint(rootType, entityType, hint, meta, fields, readOnlyFields
|
|
|
131
131
|
maybeRecursive.push({
|
|
132
132
|
kind: isReadOnly ? "read-only" : "update",
|
|
133
133
|
entity: entityType,
|
|
134
|
-
fields:
|
|
134
|
+
fields: recursiveMembershipFields(meta, m2mFieldName),
|
|
135
135
|
path: [otherFieldName]
|
|
136
136
|
});
|
|
137
137
|
return reverseReactiveHint(rootType, entityType, subHint, void 0, false).map(appendPath(otherFieldName));
|
|
@@ -193,7 +193,7 @@ async function followReverseHint(reactionName, entities, reverseHint) {
|
|
|
193
193
|
if (!relation) continue;
|
|
194
194
|
const currentValuePromise = maybeApplyTypeFilter(relation.load(), viaType);
|
|
195
195
|
promises.push(currentValuePromise);
|
|
196
|
-
const fieldKind = require_EntityMetadata.getMetadata(c).
|
|
196
|
+
const fieldKind = require_EntityMetadata.getMetadata(c).allFields[fieldName]?.kind;
|
|
197
197
|
const isReference = fieldKind === "m2o" || fieldKind === "poly";
|
|
198
198
|
const isManyToMany = fieldKind === "m2m";
|
|
199
199
|
if (isReference) for (const value of require_BaseEntity.getInstanceData(c).getReferenceHistory(fieldName)) promises.push(maybeApplyTypeFilter(loadReferenceHistoryValue(c, value), viaType));
|
|
@@ -311,6 +311,11 @@ function appendPath(reverseField) {
|
|
|
311
311
|
return rt;
|
|
312
312
|
};
|
|
313
313
|
}
|
|
314
|
+
/** Recursive membership changes on soft deletion or resurrection, even when the underlying relation is unchanged. */
|
|
315
|
+
function recursiveMembershipFields(meta, fieldName) {
|
|
316
|
+
const deletedAt = require_EntityMetadata.getBaseMeta(meta).timestampFields?.deletedAt;
|
|
317
|
+
return deletedAt ? [fieldName, deletedAt] : [fieldName];
|
|
318
|
+
}
|
|
314
319
|
function isAllReadOnly(hint) {
|
|
315
320
|
for (const [key, subHint] of Object.entries(require_normalizeHints.normalizeHint(hint))) if (!((key.endsWith("_ro") || key.endsWith(":ro")) && isAllReadOnly(subHint))) return false;
|
|
316
321
|
return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactiveHints.cjs","names":["getMetadata","normalizeHint","suffixRe","getProperties","PropertyImpl","ReactiveGetterImpl","RecursiveParentsCollectionImpl","RecursiveChildrenCollectionImpl","RecursiveM2mCollectionImpl","getInstanceData","getEmInternalApi","flatAndUnique","isEntity","isId","getBaseAndSelfMetas"],"sources":["../src/reactiveHints.ts"],"sourcesContent":["import { getInstanceData } from \"./BaseEntity.ts\";\nimport { type Changes } from \"./changes.ts\";\nimport { type Entity, isEntity } from \"./Entity.ts\";\nimport { type MaybeAbstractEntityConstructor, getEmInternalApi, isId } from \"./EntityManager.ts\";\nimport {\n type EntityMetadata,\n type ManyToManyField,\n type ManyToOneField,\n type OneToManyField,\n type OneToOneField,\n type PolymorphicFieldComponent,\n getBaseAndSelfMetas,\n getMetadata,\n} from \"./EntityMetadata.ts\";\nimport { getProperties } from \"./getProperties.ts\";\nimport { type LoadHint, type Loadable, type Loaded } from \"./loadHints.ts\";\nimport { type NormalizeHint, type SuffixSeperator, normalizeHint, suffixRe } from \"./normalizeHints.ts\";\nimport {\n type Collection,\n type EnumCollection,\n type LoadedCollection,\n type LoadedEnumCollection,\n type LoadedProperty,\n type LoadedReadOnlyCollection,\n type LoadedReference,\n type ManyToManyCollection,\n type OneToOneReference,\n type PolymorphicReference,\n type Property,\n PropertyImpl,\n type ReactiveGetter,\n type ReadOnlyCollection,\n type Reference,\n} from \"./relations/index.ts\";\nimport { type LoadedOneToOneReference } from \"./relations/OneToOneReference.ts\";\nimport { ReactiveGetterImpl } from \"./relations/ReactiveGetter.ts\";\nimport {\n RecursiveChildrenCollectionImpl,\n RecursiveM2mCollectionImpl,\n RecursiveParentsCollectionImpl,\n} from \"./relations/RecursiveCollection.ts\";\nimport { type FieldsOf, type RelationsOf } from \"./typeMap.ts\";\nimport { fail, flatAndUnique, mergeNormalizedHints } from \"./utils.ts\";\n\n/** The keys in `T` that rules & hooks can react to. */\nexport type Reactable<T extends Entity> =\n // This will be primitives + enums + m2os\n FieldsOf<T> &\n // We include `Loadable` so that we include hasReactiveProperties,\n // which are reversable but won't be in any of our codegen types.\n Loadable<T> &\n Gettable<T> &\n SuffixedFieldsOf<T> &\n SuffixedLoadable<T> &\n SuffixedGettable<T>;\n\n/** Finds `hasReactiveGetters` which are not \"loadable\" b/c they're always loaded. */\nexport type Gettable<T extends Entity> = {\n -readonly [K in keyof T as GettableValue<T[K]> extends never ? never : K]: GettableValue<T[K]>;\n};\n\ntype GettableValue<V> = V extends ReactiveGetter<any, infer P> ? P : never;\n\n/** The fields of `T` suffixed with `:ro` or `_ro`. */\ntype SuffixedFieldsOf<T extends Entity> = {\n [K in keyof FieldsOf<T> & string as `${K}${SuffixSeperator}ro`]: FieldsOf<T>[K];\n};\n\ntype SuffixedLoadable<T extends Entity> = {\n [K in keyof Loadable<T> & string as `${K}${SuffixSeperator}ro`]: Loadable<T>[K];\n};\n\ntype SuffixedGettable<T extends Entity> = {\n [K in keyof Gettable<T> & string as `${K}${SuffixSeperator}ro`]: Gettable<T>[K];\n};\n\n/**\n * A reactive hint of a single key, multiple keys, or nested keys and sub-hints.\n *\n * Reactive hints are different from load hints in that reactive hints include specific\n * fields to react against, while load hints contain only relations, collections, and async\n * properties to preload.\n */\nexport type ReactiveHint<T extends Entity> =\n | (keyof Reactable<T> & string)\n | ReadonlyArray<keyof Reactable<T> & string>\n | NestedReactiveHint<T>;\n\nexport type NestedReactiveHint<T extends Entity> = {\n [K in keyof Reactable<T>]?: Reactable<T>[K] extends infer U extends Entity ? ReactiveHint<U> : {};\n};\n\n/** Given an entity `T` that is being reacted with hint `H`, mark only the `H` attributes visible & populated. */\nexport type Reacted<T extends Entity, H> = Entity & {\n [K in keyof NormalizeHint<H> & keyof T]: T[K] extends OneToOneReference<any, infer U>\n ? LoadedOneToOneReference<T, Entity & Reacted<U, NormalizeHint<H>[K]>>\n : // Add an explicit check for PolymorphicReference because it can add `PolymorphicReference &` which gives access to `idIfSet`\n // ...although maybe LoadedReference should just have `idIfSet` in it as well? LoadedOneToOneReference does...\n T[K] extends PolymorphicReference<any, infer U, infer N>\n ? PolymorphicReference<T, U, N> & LoadedReference<T, Entity & Reacted<U, NormalizeHint<H>[K]>, N>\n : T[K] extends Reference<any, infer U, infer N>\n ? LoadedReference<T, Entity & Reacted<U, NormalizeHint<H>[K]>, N>\n : T[K] extends EnumCollection<any, infer E>\n ? LoadedEnumCollection<T, E>\n : T[K] extends Collection<any, infer U>\n ? LoadedCollection<T, Entity & Reacted<U, NormalizeHint<H>[K]>>\n : T[K] extends ReadOnlyCollection<any, infer U>\n ? LoadedReadOnlyCollection<T, Entity & Reacted<U, NormalizeHint<H>[K]>>\n : T[K] extends Property<any, infer V>\n ? LoadedProperty<any, V>\n : T[K];\n} & {\n /**\n * Gives reactive rules & fields a way to get the full entity if they really need it.\n *\n * This should be used carefully b/c it will side-step the reactive system, and instead\n * reactive rules & fields should explicitly declare access to the fields they need, which\n * will then allow accessing the field via the `Reacted<Author, ...>` type.\n */\n fullNonReactiveAccess: Loaded<T, H>;\n /** Allow detecting if a reactive change is due to nuances like `hasUpdated` or `hasChanged`. */\n changes: Changes<T, keyof (FieldsOf<T> & RelationsOf<T>), keyof NormalizeHint<H>>;\n} & MaybeTransientFields<T>;\n\n/** Allow returning reacted entities from `hasReactiveReference`. */\nexport type MaybeReactedEntity<V> = V extends Entity ? { fullNonReactiveAccess: V } : V;\n\n/**\n * Allow returning reacted entities from `hasReactiveProperty`.\n *\n * The `MaybeReactedEntity` is used by `hasReactiveReference` which already has `undefined | never`\n * broken out as a separate generic; `hasReactiveProperty` does not, and so this type conditionally\n * pulls `undefined` out first, and then defers to `MaybeReactedEntity`.\n */\nexport type MaybeReactedPropertyEntity<V> = V extends (infer V1 extends Entity) | undefined\n ? MaybeReactedEntity<V1> | undefined\n : MaybeReactedEntity<V>;\n\n/**\n * A reactive hint that only allows fields immediately on the entity, i.e. no nested hints.\n *\n * This is used by `ReactiveGetter`s to guarantee that their lambda functions will\n * always be `.get`-able and hence not need loading.\n */\nexport type ShallowReactiveHint<T extends Entity> =\n | (keyof FieldsOf<T> & string)\n | ReadonlyArray<keyof FieldsOf<T> & string>;\n\n/** If the domain model has transient fields, allow reactive behavior to see it, i.e. don't run validation rules for special operations. */\nexport type MaybeTransientFields<T> = \"transientFields\" extends keyof T\n ? { transientFields: T[\"transientFields\"] }\n : {};\n\n/**\n * Takes a reactive hint and returns the `ReactiveTarget`s that, should they change, need to\n * walk back to this watching reactable.\n *\n * @param rootType The original type that contained the reactive rule/field, used for helpful error messages\n * @param entityType The current entity of the step we're walking\n * @param hint The current hint we're walking\n * @param reactForOtherSide the name of our FK column, if the previous collection needs to react to our FK changing\n * @param isFirst\n */\nexport function reverseReactiveHint<T extends Entity>(\n rootType: MaybeAbstractEntityConstructor<T>,\n entityType: MaybeAbstractEntityConstructor<T>,\n hint: ReactiveHint<T>,\n reactForOtherSide?: string,\n reactForOtherSideIsReadOnly?: \"field-read-only\" | \"hint-read-only\" | false,\n isFirst: boolean = true,\n): ReactiveTarget[] {\n const meta = getMetadata(entityType);\n // This is the list of fields for this `entityType` that we will react to their changing values\n const fields: string[] = [];\n const readOnlyFields: string[] = [];\n const maybeRecursive: ReactiveTarget[] = [];\n\n // If the hint before us was a collection, i.e. { books: [\"title\"] }, besides just reacting\n // to our `title` changing, `reactForOtherSide` tells us to react to our `author` field as well.\n if (reactForOtherSide) {\n const _fields = reactForOtherSideIsReadOnly ? readOnlyFields : fields;\n _fields.push(reactForOtherSide);\n if (meta.timestampFields?.deletedAt) {\n _fields.push(meta.timestampFields.deletedAt);\n }\n }\n\n // Look through the hint for our own fields, i.e. `[\"firstName\", \"lastName]`, and nested hints like `{ books: \"title\" }`.\n const subHintTargets: ReactiveTarget[] = Object.entries(normalizeHint(hint)).flatMap(([keyMaybeSuffix, subHint]) => {\n return reverseSubHint(\n rootType,\n entityType,\n hint,\n meta,\n // Pass fields & readOnlyFields, so `reverseSubHint` can append any of our primitive fields it finds\n fields,\n readOnlyFields,\n maybeRecursive,\n keyMaybeSuffix, // maybeSuffix = might end with `_ro` like `name_ro`\n subHint,\n );\n });\n\n const myTargets: ReactiveTarget[] = [];\n // If any of our primitives (or m2o fields) change, establish a reactive path from \"here\" (entityType)\n // that is initially empty (path: []) but will have paths layered on by upstream callers\n if (fields.length > 0 || isFirst || reactForOtherSideIsReadOnly === \"field-read-only\") {\n myTargets.push({ kind: \"update\", entity: entityType, fields, path: [] });\n }\n // Normally read-only fields don't trigger true async reactivity, but IsLoadedCache\n // needs them for same-EM cache invalidation.\n if (readOnlyFields.length > 0) {\n myTargets.push({ kind: \"read-only\", entity: entityType, fields: readOnlyFields, path: [] });\n }\n\n return [...myTargets, ...maybeRecursive, ...subHintTargets];\n}\n\nfunction reverseSubHint(\n rootType: MaybeAbstractEntityConstructor<any>,\n entityType: MaybeAbstractEntityConstructor<any>,\n hint: any,\n meta: EntityMetadata,\n fields: string[],\n readOnlyFields: string[],\n maybeRecursive: ReactiveTarget[],\n keyMaybeSuffix: string,\n subHint: any,\n): ReactiveTarget[] {\n const key = keyMaybeSuffix.replace(suffixRe, \"\");\n const field = meta.allFields[key];\n const isReadOnly = !!keyMaybeSuffix.match(suffixRe) || (field && field.immutable);\n const _fields = isReadOnly ? readOnlyFields : fields;\n if (field) {\n switch (field.kind) {\n case \"m2o\": {\n // If this is a ReactiveReference, maybe we do something different?\n _fields.push(field.fieldName);\n const otherMeta = field.otherMetadata();\n // If this is a ReactiveReference, it won't have an \"other\" side, which is fine if:\n // - we're only reacting to the RR value itself (and have no sub-hints), or\n // - all of our sub-hints are read-only\n const otherField = otherMeta.allFields[field.otherFieldName];\n if (!otherField && !isAllReadOnly(subHint)) {\n throw new Error(\n `Invalid hint in ${rootType.name}.ts, we don't currently support reacting through ReactiveReferences, ${JSON.stringify(hint)}`,\n );\n }\n return reverseReactiveHint(rootType, otherMeta.cstr, subHint, undefined, false, false).map(\n appendPath(maybeAddTypeFilterSuffix(meta, field)),\n );\n }\n case \"poly\": {\n _fields.push(field.fieldName);\n // A poly is basically multiple m2os glued together, so copy/paste the `case m2o` code\n // above but do it for each component FK, and glue them together.\n return field.components.flatMap((comp) => {\n return reverseReactiveHint(rootType, comp.otherMetadata().cstr, subHint, undefined, false, false).map(\n appendPath(maybeAddTypeFilterSuffix(meta, comp)),\n );\n });\n }\n case \"m2m\": {\n // While o2m and o2o can watch for just FK changes by passing `reactForOtherSide` (the FK lives in the other\n // table), for m2m reactivity we push the collection name into the reactive hint, because it's effectively\n // \"the other/reverse side\", and JoinRows will trigger `queueDownstreamReactables` explicitly, instead of\n // `setField` which handles regular/non-m2m mutations.\n _fields.push(field.fieldName);\n const otherFieldName = maybeAddTypeFilterSuffix(meta, field);\n return reverseReactiveHint(\n rootType,\n field.otherMetadata().cstr,\n subHint,\n // For m2m, we can always pass undefined here, as otherwise having the opposite m2m collection\n // recalc all of its children will cause over-reactivity\n undefined,\n false,\n false,\n ).map(appendPath(otherFieldName));\n }\n case \"o2m\":\n case \"o2o\": {\n const isOtherReadOnly = field.otherMetadata().allFields[field.otherFieldName].immutable;\n const otherFieldName = maybeAddTypeFilterSuffix(meta, field);\n // This is not a field, but we want our reverse side to be reactive, so pass reactForOtherSide\n return reverseReactiveHint(\n rootType,\n field.otherMetadata().cstr,\n subHint,\n // IsLoadedCache invalidation always needs to know the otherField name, so we must\n // end up with either a kind=update or kind=read-only target with `otherFieldName` in it.\n field.otherFieldName,\n // We can have three outcomes:\n // - kind=update fields=[otherFieldName] (normal behavior)\n // - kind=update fields=[] + kind=read-only fields=[otherFieldName] (isOtherReadOnly)\n // - kind=read-only fields=[otherFieldName] (hint isReadOnly)\n isReadOnly ? \"hint-read-only\" : isOtherReadOnly ? \"field-read-only\" : false,\n false,\n ).map(appendPath(otherFieldName));\n }\n case \"primaryKey\":\n case \"primitive\":\n case \"enum\":\n _fields.push(key);\n return [];\n case \"m2mEnum\":\n // An enum m2m is a leaf of enum codes (no \"other side\" entity to recurse into); like a m2m,\n // `JoinRows` calls `queueDownstreamReactables` explicitly when its membership changes.\n _fields.push(key);\n return [];\n default:\n throw new Error(`Invalid hint in ${rootType.name}.ts hint ${JSON.stringify(hint)}`);\n }\n } else {\n // We only need to look for ReactiveProperties here, because ReactiveFields & ReactiveReferences\n // have underlying primitive fields that, when/if they change, will be handled in the ^ code.\n //\n // I.e. we specifically don't need to handle RFs & RRs ^, because the EntityManager.flush loop will\n // notice their primitive values changing, and kicking off any downstream reactive fields as necessary.\n const p = getProperties(meta)[key];\n if (p instanceof PropertyImpl) {\n // If the field is marked as readonly (i.e. using the `_ro` suffix), then we can assume that applies to its\n // entire hint as well and can simply omit it. This also allows us to use non-reactive async props as long as\n // they are readonly.\n if (isReadOnly) return [];\n if (!p.reactiveHint) {\n throw new Error(\n `Property ${key} cannot be used in reactive hints in ${rootType.name}.ts hint ${JSON.stringify(\n hint,\n )}, please use hasReactiveProperty instead`,\n );\n }\n return reverseReactiveHint(rootType, meta.cstr, p.reactiveHint, undefined, false, false);\n } else if (p instanceof ReactiveGetterImpl) {\n // If the field is marked as readonly, then we can assume that applies to its entire hint as well and can\n // simply omit it.\n if (isReadOnly) return [];\n return reverseReactiveHint(rootType, meta.cstr, p.reactiveHint, undefined, false, false);\n } else if (p instanceof RecursiveParentsCollectionImpl) {\n // I.e. if this is `Author.mentorsRecursive`:\n // - fieldName=mentorsRecursive (being watched by the caller)\n // - otherFieldName=menteesRecursive (our reversal for walking down)\n // - m2oFieldName = mentor\n //\n // Note that \"reversing parents\" is generally easier, b/c when the tree changes, any given node can\n // still \"walk down\" to find its children w/the \"current value\" childrenRecursive; and any just-now\n // orphaned parents will themselves trigger their own childrenRecursive changes (i.e. we do not need\n // any \"old reference value\" / \"new reference value\" handling).\n //\n // I.e. given a tree of `A is mentor of B is mentor of C`, when B changes mentors, and we need to\n // \"walk to all affected/children mentees, so they can observe their new mentorsRecursive\", merely\n // walking down from (b itself & it's mentees) will find all affected mentees.\n const { otherFieldName, m2oFieldName } = p;\n // When a mentor.firstName/etc field changes, walk to its transitive children (through otherFieldName)\n // via the `return reverseReactiveHint` line below.\n //\n // But if the `author.mentor` field itself changes, we ourselves are also \"a new mentee\" of the mentor, so\n // do `fields.push(m2oFieldName)` to notify our direct reactable (a new mentee), and then our transitive\n // children/mentees. This avoids going \"up to the mentor, and walking down all of his mentees\", which would go\n // through our siblings, which would not have changed/need reactivity.\n _fields.push(m2oFieldName);\n maybeRecursive.push({\n kind: isReadOnly ? \"read-only\" : \"update\",\n entity: entityType,\n fields: [m2oFieldName],\n path: [otherFieldName],\n });\n // Any reactables who were watching `parentsRecursive`, we'll back down to by going through `childrenRecursive`\n return reverseReactiveHint(rootType, entityType, subHint, undefined, false).map(appendPath(otherFieldName));\n } else if (p instanceof RecursiveChildrenCollectionImpl) {\n const { otherFieldName, o2mFieldName } = p;\n // I.e. this is `Author.menteesRecursive`\n // - fieldName=menteesRecursive` (being watched by the caller)\n // - otherFieldName=mentorsRecursive` (our reversal for walking up)\n // - The o2m's otherFieldName gives us the FK field (e.g., \"mentor\") on the child entity.\n //\n // Note that \"reversing children\" is trickier than reversing parents, b/c when the tree changes,\n // we need to consider both \"old reference value\" and \"new reference value\" scenarios.\n const o2mField = meta.allFields[o2mFieldName] as OneToManyField | OneToOneField;\n const m2oFieldName = o2mField.otherFieldName;\n _fields.push(m2oFieldName);\n maybeRecursive.push({\n kind: isReadOnly ? \"read-only\" : \"update\",\n entity: entityType,\n fields: [m2oFieldName], // i.e. parent\n path: [otherFieldName], // i.e. parentsRecursive to tell them \"observe your new childrenRecursive\"\n });\n // Any reactables who were watching `childrenRecursive`, we'll up to by going through `parentsRecursive`\n return reverseReactiveHint(rootType, entityType, subHint, undefined, false).map(appendPath(otherFieldName));\n } else if (p instanceof RecursiveM2mCollectionImpl) {\n // I.e. if this is `User.parentsRecursive`:\n // - fieldName=parentsRecursive (being watched by the caller)\n // - otherFieldName=childrenRecursive (our reversal for walking down)\n // - m2mFieldName=parents (the underlying m2m collection)\n //\n // For m2m recursive, the \"change signal\" is when join rows are added/removed, so we push\n // the m2m collection name into fields (like regular m2m handling). To find affected entities\n // that are watching `parentsRecursive`, we walk through `childrenRecursive` (the reverse direction).\n const { otherFieldName, m2mFieldName } = p;\n // When a join row changes (e.g. parents collection is mutated), notify both this entity\n // and its transitive children/parents via otherFieldName\n _fields.push(m2mFieldName);\n maybeRecursive.push({\n kind: isReadOnly ? \"read-only\" : \"update\",\n entity: entityType,\n fields: [m2mFieldName],\n path: [otherFieldName],\n });\n // Any reactables who were watching this recursive collection, we'll reach via the other direction\n return reverseReactiveHint(rootType, entityType, subHint, undefined, false).map(appendPath(otherFieldName));\n } else {\n throw new Error(`Invalid hint in ${rootType.name}.ts ${JSON.stringify(hint)}`);\n }\n }\n}\n\nfunction maybeAddTypeFilterSuffix(\n meta: EntityMetadata,\n field: ManyToOneField | OneToManyField | ManyToManyField | OneToOneField | PolymorphicFieldComponent,\n): string {\n const otherField = field.otherMetadata().allFields[field.otherFieldName];\n if (!otherField) {\n // This is usually an error, unless if its ReactiveReference which we don't create o2m-s for\n const isReactiveReference = \"kind\" in field && field.kind === \"m2o\" && field.derived === \"async\";\n if (!isReactiveReference) fail(`No field ${field.otherMetadata().type}.${field.otherFieldName}`);\n return field.otherFieldName;\n }\n // If we're Foo, and the other field (which we'll traverse back from at runtime) is actually\n // a poly FK pointing back to multiple `owner=Foo | Bar | Zaz`, add a suffix of `@Foo` so\n // that any runtime traversal that hit `owner` and see a `Bar | Zaz` will stop.\n const nextFieldIsPoly = otherField.kind === \"poly\";\n // If we're a SubType, and the other field points to our base type, assume there might be\n // SubType-specific fields in the path so far, so tell the other side to only walk back through\n // us if its value is actually a SubType.\n const nextFieldPointsToBaseType =\n !!meta.baseType && otherField.kind !== \"poly\" && (otherField as any).otherMetadata().cstr !== meta.cstr;\n return nextFieldIsPoly || nextFieldPointsToBaseType ? `${field.otherFieldName}@${meta.type}` : field.otherFieldName;\n}\n\n/**\n * Walks `reverseHint` for every entity in `entities`.\n *\n * I.e. given `[book1, book2]` and `[\"author\", 'publisher\"]`, will return all the books' authors' publishers.\n *\n * Note that for references (and only references), we walk both through \"the current value\"\n * and prior values remembered in `referenceHistory`. This is fundamentally because references are the true \"owner\"\n * of the relation, while collections are derived.\n *\n * For example, with reversals walking through collections:\n *\n * - Given a hint `book: { author: firstName }`\n * - Which reverses to `author[firstName] -> books`\n * - When we walk through `a1.books`, we use only the current value, which might have only `[b2]` in it.\n * - We don't need to worry about \"the old a1.books\" value, which might have previously had `[b1]` in it,\n * because the `b1.author` changing authors (from `a1` to `a2`) will trigger its own reactivity.\n *\n * Contrast with reversals walking through references:\n *\n * - Given a hint `author: { books: \"title\" }`\n * - Which reverses to `book[title] -> author`\n * - When we traverse through `b1.author`, we use both the current value and remembered values, so that both\n * \"our prior authors\" and \"our new author\" see their latest `author.books` collection values.\n *\n * Delete cleanup runs before reverse hints are walked, so the current relation value may already be unset by the\n * time we get here. For persisted entities, `cleanupOnEntityDeleted` clears the owning FK via `setField`, which\n * remembers the prior FK in `referenceHistory`; this lets us still find the old owner. For collection reversals\n * this is enough because collections are derived from the owning FK: removing `b1` from `a1.books` is represented\n * by `b1.author` changing, not by an independent \"old books\" snapshot.\n *\n * References can also have transient values during a unit of work, i.e. a ReactiveField might observe `b1.author=a2`\n * before the final flush changes it to `a3`. `setField` remembers both original and transient reference values\n * separately from `originalData`, and we walk them in addition to the current value.\n */\nexport async function followReverseHint(\n reactionName: string,\n entities: Entity[],\n reverseHint: string[],\n): Promise<Entity[]> {\n // Start at the current (unique) entities\n let current = new Set(entities);\n const paths = [...reverseHint];\n // And \"walk backwards\" through the reverse hint\n while (paths.length) {\n const path = paths.shift()!;\n const [fieldName, viaType] = path.split(\"@\");\n const promises = new Array(current.size);\n // The path might touch either a reference or a collection\n for (const c of current as Set<any>) {\n const relation = c[fieldName];\n // If `!relation`, I'd really like to `fail`, but atm we're knowingly creating invalid reversals for when\n // a ReactiveField depends on a ReactiveReference; b/c RRs lack an \"other side\", we cannot walk them in\n // reverse, and so for awhile we \"supported\" the RF -> RR pattern by relying on a totally-read-only reactive\n // hint -> dropped during reversal.\n //\n // However, now IsLoadedCache means that even these \"totally read only\" RFs need to be reactive at least\n // during their initial EM creation, to avoid stale values being observed during first-flush mutations,\n // and so we're now stuck with these \"invalid RR reversals\" until we can figure out a better way to handle them.\n // (Now that IsLoadedCache doesn't technically walk paths, it just invalidates given entity+field pairs, so\n // it is not bothered by an invalid/missing RR reversal.)\n if (!relation) continue;\n // fail(`Attempting to react for ${reactionName} for ${c.toString()}, but there is no \"reverse walkable\" field ${c.constructor.name}.${fieldName}`,);\n const currentValuePromise = maybeApplyTypeFilter(relation.load(), viaType);\n // Always wait for the relation itself\n promises.push(currentValuePromise);\n // If we're going from Book.author back to Author to re-validate the Author.books collection,\n // see if Book.author has changed, so we can re-validate both the old author's books and the\n // new author's books.\n const fieldKind = getMetadata(c).fields[fieldName]?.kind;\n const isReference = fieldKind === \"m2o\" || fieldKind === \"poly\";\n const isManyToMany = fieldKind === \"m2m\";\n // See jsdoc comment about why this is only necessary for references...\n if (isReference) {\n for (const value of getInstanceData(c).getReferenceHistory(fieldName)) {\n promises.push(maybeApplyTypeFilter(loadReferenceHistoryValue(c, value), viaType));\n }\n }\n if (isManyToMany) {\n const m2m = c[fieldName] as ManyToManyCollection<any, any>;\n const joinRows = getEmInternalApi(m2m.entity.em).joinRows(m2m);\n // Return a tuple of [currentRows, removedRows]\n promises.push(joinRows.removedFor(m2m, c));\n }\n // Walk up the old parents\n if (relation instanceof RecursiveParentsCollectionImpl) {\n const { m2oFieldName } = relation;\n for (const value of getInstanceData(c).getReferenceHistory(m2oFieldName)) {\n promises.push(\n // First load the old parent itself\n loadReferenceHistoryValue(c, value).then((oldParent) => {\n if (!oldParent) return undefined;\n // And then resolve its fieldName=parentsRecursive\n const oldParentRecursiveParents = (oldParent as any)[fieldName];\n return oldParentRecursiveParents.load().then((parents: any) => {\n return [oldParent, ...parents];\n });\n }),\n );\n }\n }\n }\n const nextLevel = await Promise.all(promises);\n // Make a new set so that we dedupe as we go\n current = flatAndUnique(nextLevel);\n }\n return [...current];\n}\n\n/** Converts a normalized reactive `hint` into a load hint. */\nexport function convertToLoadHint<T extends Entity>(\n meta: EntityMetadata,\n hint: ReactiveHint<T>,\n allowCustomKeys = false,\n): LoadHint<T> {\n const loadHint = {};\n // Process the hints individually instead of just calling Object.fromEntries so that\n // we can handle inlined reactive hints that overlap.\n for (const [keyMaybeSuffix, subHint] of Object.entries(normalizeHint(hint))) {\n let key = keyMaybeSuffix.replace(suffixRe, \"\");\n let field = meta.allFields[key];\n\n // If `allowCustomKeys` is enabled, we're probably doing `toJSON`,\n // so look for `companyId` & `companyIds` to turn into `company`\n if (!field && allowCustomKeys) {\n const realField = Object.values(meta.allFields).find((f) => f.fieldIdName === key);\n if (realField) {\n field = realField;\n key = realField.fieldName;\n }\n }\n\n if (field) {\n switch (field.kind) {\n case \"m2m\":\n case \"m2o\":\n case \"o2m\":\n case \"o2o\":\n mergeNormalizedHints(loadHint, { [key]: convertToLoadHint(field.otherMetadata(), subHint, allowCustomKeys) });\n break;\n case \"poly\":\n for (const comp of field.components) {\n // Write `{ parent: { child@Type: hint } }` into the load hint\n mergeNormalizedHints(loadHint, {\n [key]: Object.fromEntries(\n Object.entries(convertToLoadHint(comp.otherMetadata(), subHint, allowCustomKeys)).map(\n // Map the subHint keys to add in `@Type`\n ([subKey, subHint]) => [`${subKey}@${comp.otherMetadata().type}`, subHint],\n ),\n ),\n });\n }\n break;\n case \"primitive\":\n case \"enum\":\n if (field.derived === \"async\") {\n mergeNormalizedHints(loadHint, { [key]: {} });\n }\n continue;\n case \"m2mEnum\":\n // The enum codes are a leaf, but the collection itself must be loaded for the reaction to read it.\n mergeNormalizedHints(loadHint, { [key]: {} });\n break;\n case \"primaryKey\":\n continue;\n default:\n throw new Error(`Invalid reactive hint ${meta.tableName} ${JSON.stringify(hint)}`);\n }\n } else {\n const p = getProperties(meta)[key];\n if (p instanceof RecursiveParentsCollectionImpl) {\n mergeNormalizedHints(loadHint, { [p.fieldName]: convertToLoadHint(meta, subHint, allowCustomKeys) });\n } else if (p instanceof RecursiveChildrenCollectionImpl) {\n mergeNormalizedHints(loadHint, { [p.fieldName]: convertToLoadHint(meta, subHint, allowCustomKeys) });\n } else if (p instanceof RecursiveM2mCollectionImpl) {\n mergeNormalizedHints(loadHint, { [p.fieldName]: convertToLoadHint(meta, subHint, allowCustomKeys) });\n } else if (p && p.reactiveHint) {\n mergeNormalizedHints(loadHint, convertToLoadHint(meta, p.reactiveHint, allowCustomKeys));\n } else if (!allowCustomKeys) {\n fail(`Invalid reactive hint on ${meta.tableName} ${JSON.stringify(hint)}`);\n }\n }\n }\n return loadHint as any;\n}\n\n/** An entity that, when `fields` change, should trigger the reactive rule/field pointed to by `path`. */\nexport interface ReactiveTarget {\n kind: \"read-only\" | \"update\";\n /** The entity that contains a field our reactive rule/field accesses. */\n entity: MaybeAbstractEntityConstructor<any>;\n /** The field(s) that our reactive rule/field accesses, plus any implicit fields like FKs. */\n fields: string[];\n /** The path from this `entity` back to the source reactive rule/field. */\n path: string[];\n}\n\nfunction maybeApplyTypeFilter(loadPromise: Promise<Entity | Entity[] | undefined>, viaType: string | undefined) {\n if (viaType) {\n return loadPromise.then((loaded) => {\n if (Array.isArray(loaded)) {\n return loaded.filter((e) => isTypeOrSubType(e, viaType));\n } else if (loaded && isTypeOrSubType(loaded, viaType)) {\n return loaded;\n } else {\n return undefined;\n }\n });\n }\n return loadPromise;\n}\n\n/** Loads a remembered reference value so reverse-hint walking can revisit it. */\nfunction loadReferenceHistoryValue(entity: Entity, value: any): Promise<Entity | undefined> {\n if (isEntity(value)) return Promise.resolve(value);\n if (isId(value)) return entity.em.load(value) as Promise<Entity>;\n return Promise.resolve(undefined);\n}\n\n/** Returns true if `entity` is exactly `typeName` or a subtype of `typeName`.\n *\n * Used to honor poly/type filters like `@Publisher` during reverse-hint walks:\n * a `SmallPublisher` entity should pass an `@Publisher` filter because it is\n * a Publisher via inheritance.\n */\nexport function isTypeOrSubType(entity: Entity, typeName: string): boolean {\n const meta = getMetadata(entity);\n return meta.type === typeName || meta.baseTypes.some((b) => b.type === typeName);\n}\n\nexport function isPolyHint(key: string): boolean {\n return key.includes(\"@\");\n}\n\nexport function splitPolyHintToKeyAndType(key: string): [string, string] {\n const [k, type] = key.split(\"@\");\n return [k, type];\n}\n\n/** If this hint is a poly like `author@Book`, return the type, otherwise return undefined. */\nexport function getRelationFromMaybePolyKey(entity: Entity, key: string): any {\n if (isPolyHint(key)) {\n const [realKey, typeName] = splitPolyHintToKeyAndType(key);\n // Even though `entity[realKey]` might exist, if it's from a different poly type, we don't want it\n const isApplicable = getBaseAndSelfMetas(getMetadata(entity)).some((meta) => meta.type === typeName);\n if (!isApplicable) return undefined;\n return (entity as any)[realKey];\n } else {\n return (entity as any)[key];\n }\n}\n\n/**\n * For an `author -> books -> bookReviews` hint, appends `BookReview.book`, `Book.author`,\n * i.e. the \"other side\" relation, to the reversed path on the way \"out\" from a mutated BookReview\n * to the target `Author` RF/RR/rule.\n */\nfunction appendPath(reverseField: string): (rt: ReactiveTarget) => ReactiveTarget {\n return (rt) => {\n // We can mutate this array b/c it's only owned by a single/continually-updated `ReactiveTarget`\n rt.path = [...rt.path, reverseField];\n return rt;\n };\n}\n\nfunction isAllReadOnly(hint: any): boolean {\n for (const [key, subHint] of Object.entries(normalizeHint(hint))) {\n const okay = (key.endsWith(\"_ro\") || key.endsWith(\":ro\")) && isAllReadOnly(subHint);\n if (!okay) return false;\n }\n return true;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"reactiveHints.cjs","names":["getMetadata","normalizeHint","suffixRe","getProperties","PropertyImpl","ReactiveGetterImpl","RecursiveParentsCollectionImpl","RecursiveChildrenCollectionImpl","RecursiveM2mCollectionImpl","getInstanceData","getEmInternalApi","flatAndUnique","isEntity","isId","getBaseAndSelfMetas","getBaseMeta"],"sources":["../src/reactiveHints.ts"],"sourcesContent":["import { getInstanceData } from \"./BaseEntity.ts\";\nimport { type Changes } from \"./changes.ts\";\nimport { type Entity, isEntity } from \"./Entity.ts\";\nimport { type MaybeAbstractEntityConstructor, getEmInternalApi, isId } from \"./EntityManager.ts\";\nimport {\n type EntityMetadata,\n type ManyToManyField,\n type ManyToOneField,\n type OneToManyField,\n type OneToOneField,\n type PolymorphicFieldComponent,\n getBaseAndSelfMetas,\n getBaseMeta,\n getMetadata,\n} from \"./EntityMetadata.ts\";\nimport { getProperties } from \"./getProperties.ts\";\nimport { type LoadHint, type Loadable, type Loaded } from \"./loadHints.ts\";\nimport { type NormalizeHint, type SuffixSeperator, normalizeHint, suffixRe } from \"./normalizeHints.ts\";\nimport {\n type Collection,\n type EnumCollection,\n type LoadedCollection,\n type LoadedEnumCollection,\n type LoadedProperty,\n type LoadedReadOnlyCollection,\n type LoadedReference,\n type ManyToManyCollection,\n type OneToOneReference,\n type PolymorphicReference,\n type Property,\n PropertyImpl,\n type ReactiveGetter,\n type ReadOnlyCollection,\n type Reference,\n} from \"./relations/index.ts\";\nimport { type LoadedOneToOneReference } from \"./relations/OneToOneReference.ts\";\nimport { ReactiveGetterImpl } from \"./relations/ReactiveGetter.ts\";\nimport {\n RecursiveChildrenCollectionImpl,\n RecursiveM2mCollectionImpl,\n RecursiveParentsCollectionImpl,\n} from \"./relations/RecursiveCollection.ts\";\nimport { type FieldsOf, type RelationsOf } from \"./typeMap.ts\";\nimport { fail, flatAndUnique, mergeNormalizedHints } from \"./utils.ts\";\n\n/** The keys in `T` that rules & hooks can react to. */\nexport type Reactable<T extends Entity> =\n // This will be primitives + enums + m2os\n FieldsOf<T> &\n // We include `Loadable` so that we include hasReactiveProperties,\n // which are reversable but won't be in any of our codegen types.\n Loadable<T> &\n Gettable<T> &\n SuffixedFieldsOf<T> &\n SuffixedLoadable<T> &\n SuffixedGettable<T>;\n\n/** Finds `hasReactiveGetters` which are not \"loadable\" b/c they're always loaded. */\nexport type Gettable<T extends Entity> = {\n -readonly [K in keyof T as GettableValue<T[K]> extends never ? never : K]: GettableValue<T[K]>;\n};\n\ntype GettableValue<V> = V extends ReactiveGetter<any, infer P> ? P : never;\n\n/** The fields of `T` suffixed with `:ro` or `_ro`. */\ntype SuffixedFieldsOf<T extends Entity> = {\n [K in keyof FieldsOf<T> & string as `${K}${SuffixSeperator}ro`]: FieldsOf<T>[K];\n};\n\ntype SuffixedLoadable<T extends Entity> = {\n [K in keyof Loadable<T> & string as `${K}${SuffixSeperator}ro`]: Loadable<T>[K];\n};\n\ntype SuffixedGettable<T extends Entity> = {\n [K in keyof Gettable<T> & string as `${K}${SuffixSeperator}ro`]: Gettable<T>[K];\n};\n\n/**\n * A reactive hint of a single key, multiple keys, or nested keys and sub-hints.\n *\n * Reactive hints are different from load hints in that reactive hints include specific\n * fields to react against, while load hints contain only relations, collections, and async\n * properties to preload.\n */\nexport type ReactiveHint<T extends Entity> =\n | (keyof Reactable<T> & string)\n | ReadonlyArray<keyof Reactable<T> & string>\n | NestedReactiveHint<T>;\n\nexport type NestedReactiveHint<T extends Entity> = {\n [K in keyof Reactable<T>]?: Reactable<T>[K] extends infer U extends Entity ? ReactiveHint<U> : {};\n};\n\n/** Given an entity `T` that is being reacted with hint `H`, mark only the `H` attributes visible & populated. */\nexport type Reacted<T extends Entity, H> = Entity & {\n [K in keyof NormalizeHint<H> & keyof T]: T[K] extends OneToOneReference<any, infer U>\n ? LoadedOneToOneReference<T, Entity & Reacted<U, NormalizeHint<H>[K]>>\n : // Add an explicit check for PolymorphicReference because it can add `PolymorphicReference &` which gives access to `idIfSet`\n // ...although maybe LoadedReference should just have `idIfSet` in it as well? LoadedOneToOneReference does...\n T[K] extends PolymorphicReference<any, infer U, infer N>\n ? PolymorphicReference<T, U, N> & LoadedReference<T, Entity & Reacted<U, NormalizeHint<H>[K]>, N>\n : T[K] extends Reference<any, infer U, infer N>\n ? LoadedReference<T, Entity & Reacted<U, NormalizeHint<H>[K]>, N>\n : T[K] extends EnumCollection<any, infer E>\n ? LoadedEnumCollection<T, E>\n : T[K] extends Collection<any, infer U>\n ? LoadedCollection<T, Entity & Reacted<U, NormalizeHint<H>[K]>>\n : T[K] extends ReadOnlyCollection<any, infer U>\n ? LoadedReadOnlyCollection<T, Entity & Reacted<U, NormalizeHint<H>[K]>>\n : T[K] extends Property<any, infer V>\n ? LoadedProperty<any, V>\n : T[K];\n} & {\n /**\n * Gives reactive rules & fields a way to get the full entity if they really need it.\n *\n * This should be used carefully b/c it will side-step the reactive system, and instead\n * reactive rules & fields should explicitly declare access to the fields they need, which\n * will then allow accessing the field via the `Reacted<Author, ...>` type.\n */\n fullNonReactiveAccess: Loaded<T, H>;\n /** Allow detecting if a reactive change is due to nuances like `hasUpdated` or `hasChanged`. */\n changes: Changes<T, keyof (FieldsOf<T> & RelationsOf<T>), keyof NormalizeHint<H>>;\n} & MaybeTransientFields<T>;\n\n/** Allow returning reacted entities from `hasReactiveReference`. */\nexport type MaybeReactedEntity<V> = V extends Entity ? { fullNonReactiveAccess: V } : V;\n\n/**\n * Allow returning reacted entities from `hasReactiveProperty`.\n *\n * The `MaybeReactedEntity` is used by `hasReactiveReference` which already has `undefined | never`\n * broken out as a separate generic; `hasReactiveProperty` does not, and so this type conditionally\n * pulls `undefined` out first, and then defers to `MaybeReactedEntity`.\n */\nexport type MaybeReactedPropertyEntity<V> = V extends (infer V1 extends Entity) | undefined\n ? MaybeReactedEntity<V1> | undefined\n : MaybeReactedEntity<V>;\n\n/**\n * A reactive hint that only allows fields immediately on the entity, i.e. no nested hints.\n *\n * This is used by `ReactiveGetter`s to guarantee that their lambda functions will\n * always be `.get`-able and hence not need loading.\n */\nexport type ShallowReactiveHint<T extends Entity> =\n | (keyof FieldsOf<T> & string)\n | ReadonlyArray<keyof FieldsOf<T> & string>;\n\n/** If the domain model has transient fields, allow reactive behavior to see it, i.e. don't run validation rules for special operations. */\nexport type MaybeTransientFields<T> = \"transientFields\" extends keyof T\n ? { transientFields: T[\"transientFields\"] }\n : {};\n\n/**\n * Takes a reactive hint and returns the `ReactiveTarget`s that, should they change, need to\n * walk back to this watching reactable.\n *\n * @param rootType The original type that contained the reactive rule/field, used for helpful error messages\n * @param entityType The current entity of the step we're walking\n * @param hint The current hint we're walking\n * @param reactForOtherSide the name of our FK column, if the previous collection needs to react to our FK changing\n * @param isFirst\n */\nexport function reverseReactiveHint<T extends Entity>(\n rootType: MaybeAbstractEntityConstructor<T>,\n entityType: MaybeAbstractEntityConstructor<T>,\n hint: ReactiveHint<T>,\n reactForOtherSide?: string,\n reactForOtherSideIsReadOnly?: \"field-read-only\" | \"hint-read-only\" | false,\n isFirst: boolean = true,\n): ReactiveTarget[] {\n const meta = getMetadata(entityType);\n // This is the list of fields for this `entityType` that we will react to their changing values\n const fields: string[] = [];\n const readOnlyFields: string[] = [];\n const maybeRecursive: ReactiveTarget[] = [];\n\n // If the hint before us was a collection, i.e. { books: [\"title\"] }, besides just reacting\n // to our `title` changing, `reactForOtherSide` tells us to react to our `author` field as well.\n if (reactForOtherSide) {\n const _fields = reactForOtherSideIsReadOnly ? readOnlyFields : fields;\n _fields.push(reactForOtherSide);\n if (meta.timestampFields?.deletedAt) {\n _fields.push(meta.timestampFields.deletedAt);\n }\n }\n\n // Look through the hint for our own fields, i.e. `[\"firstName\", \"lastName]`, and nested hints like `{ books: \"title\" }`.\n const subHintTargets: ReactiveTarget[] = Object.entries(normalizeHint(hint)).flatMap(([keyMaybeSuffix, subHint]) => {\n return reverseSubHint(\n rootType,\n entityType,\n hint,\n meta,\n // Pass fields & readOnlyFields, so `reverseSubHint` can append any of our primitive fields it finds\n fields,\n readOnlyFields,\n maybeRecursive,\n keyMaybeSuffix, // maybeSuffix = might end with `_ro` like `name_ro`\n subHint,\n );\n });\n\n const myTargets: ReactiveTarget[] = [];\n // If any of our primitives (or m2o fields) change, establish a reactive path from \"here\" (entityType)\n // that is initially empty (path: []) but will have paths layered on by upstream callers\n if (fields.length > 0 || isFirst || reactForOtherSideIsReadOnly === \"field-read-only\") {\n myTargets.push({ kind: \"update\", entity: entityType, fields, path: [] });\n }\n // Normally read-only fields don't trigger true async reactivity, but IsLoadedCache\n // needs them for same-EM cache invalidation.\n if (readOnlyFields.length > 0) {\n myTargets.push({ kind: \"read-only\", entity: entityType, fields: readOnlyFields, path: [] });\n }\n\n return [...myTargets, ...maybeRecursive, ...subHintTargets];\n}\n\nfunction reverseSubHint(\n rootType: MaybeAbstractEntityConstructor<any>,\n entityType: MaybeAbstractEntityConstructor<any>,\n hint: any,\n meta: EntityMetadata,\n fields: string[],\n readOnlyFields: string[],\n maybeRecursive: ReactiveTarget[],\n keyMaybeSuffix: string,\n subHint: any,\n): ReactiveTarget[] {\n const key = keyMaybeSuffix.replace(suffixRe, \"\");\n const field = meta.allFields[key];\n const isReadOnly = !!keyMaybeSuffix.match(suffixRe) || (field && field.immutable);\n const _fields = isReadOnly ? readOnlyFields : fields;\n if (field) {\n switch (field.kind) {\n case \"m2o\": {\n // If this is a ReactiveReference, maybe we do something different?\n _fields.push(field.fieldName);\n const otherMeta = field.otherMetadata();\n // If this is a ReactiveReference, it won't have an \"other\" side, which is fine if:\n // - we're only reacting to the RR value itself (and have no sub-hints), or\n // - all of our sub-hints are read-only\n const otherField = otherMeta.allFields[field.otherFieldName];\n if (!otherField && !isAllReadOnly(subHint)) {\n throw new Error(\n `Invalid hint in ${rootType.name}.ts, we don't currently support reacting through ReactiveReferences, ${JSON.stringify(hint)}`,\n );\n }\n return reverseReactiveHint(rootType, otherMeta.cstr, subHint, undefined, false, false).map(\n appendPath(maybeAddTypeFilterSuffix(meta, field)),\n );\n }\n case \"poly\": {\n _fields.push(field.fieldName);\n // A poly is basically multiple m2os glued together, so copy/paste the `case m2o` code\n // above but do it for each component FK, and glue them together.\n return field.components.flatMap((comp) => {\n return reverseReactiveHint(rootType, comp.otherMetadata().cstr, subHint, undefined, false, false).map(\n appendPath(maybeAddTypeFilterSuffix(meta, comp)),\n );\n });\n }\n case \"m2m\": {\n // While o2m and o2o can watch for just FK changes by passing `reactForOtherSide` (the FK lives in the other\n // table), for m2m reactivity we push the collection name into the reactive hint, because it's effectively\n // \"the other/reverse side\", and JoinRows will trigger `queueDownstreamReactables` explicitly, instead of\n // `setField` which handles regular/non-m2m mutations.\n _fields.push(field.fieldName);\n const otherFieldName = maybeAddTypeFilterSuffix(meta, field);\n return reverseReactiveHint(\n rootType,\n field.otherMetadata().cstr,\n subHint,\n // For m2m, we can always pass undefined here, as otherwise having the opposite m2m collection\n // recalc all of its children will cause over-reactivity\n undefined,\n false,\n false,\n ).map(appendPath(otherFieldName));\n }\n case \"o2m\":\n case \"o2o\": {\n const isOtherReadOnly = field.otherMetadata().allFields[field.otherFieldName].immutable;\n const otherFieldName = maybeAddTypeFilterSuffix(meta, field);\n // This is not a field, but we want our reverse side to be reactive, so pass reactForOtherSide\n return reverseReactiveHint(\n rootType,\n field.otherMetadata().cstr,\n subHint,\n // IsLoadedCache invalidation always needs to know the otherField name, so we must\n // end up with either a kind=update or kind=read-only target with `otherFieldName` in it.\n field.otherFieldName,\n // We can have three outcomes:\n // - kind=update fields=[otherFieldName] (normal behavior)\n // - kind=update fields=[] + kind=read-only fields=[otherFieldName] (isOtherReadOnly)\n // - kind=read-only fields=[otherFieldName] (hint isReadOnly)\n isReadOnly ? \"hint-read-only\" : isOtherReadOnly ? \"field-read-only\" : false,\n false,\n ).map(appendPath(otherFieldName));\n }\n case \"primaryKey\":\n case \"primitive\":\n case \"enum\":\n _fields.push(key);\n return [];\n case \"m2mEnum\":\n // An enum m2m is a leaf of enum codes (no \"other side\" entity to recurse into); like a m2m,\n // `JoinRows` calls `queueDownstreamReactables` explicitly when its membership changes.\n _fields.push(key);\n return [];\n default:\n throw new Error(`Invalid hint in ${rootType.name}.ts hint ${JSON.stringify(hint)}`);\n }\n } else {\n // We only need to look for ReactiveProperties here, because ReactiveFields & ReactiveReferences\n // have underlying primitive fields that, when/if they change, will be handled in the ^ code.\n //\n // I.e. we specifically don't need to handle RFs & RRs ^, because the EntityManager.flush loop will\n // notice their primitive values changing, and kicking off any downstream reactive fields as necessary.\n const p = getProperties(meta)[key];\n if (p instanceof PropertyImpl) {\n // If the field is marked as readonly (i.e. using the `_ro` suffix), then we can assume that applies to its\n // entire hint as well and can simply omit it. This also allows us to use non-reactive async props as long as\n // they are readonly.\n if (isReadOnly) return [];\n if (!p.reactiveHint) {\n throw new Error(\n `Property ${key} cannot be used in reactive hints in ${rootType.name}.ts hint ${JSON.stringify(\n hint,\n )}, please use hasReactiveProperty instead`,\n );\n }\n return reverseReactiveHint(rootType, meta.cstr, p.reactiveHint, undefined, false, false);\n } else if (p instanceof ReactiveGetterImpl) {\n // If the field is marked as readonly, then we can assume that applies to its entire hint as well and can\n // simply omit it.\n if (isReadOnly) return [];\n return reverseReactiveHint(rootType, meta.cstr, p.reactiveHint, undefined, false, false);\n } else if (p instanceof RecursiveParentsCollectionImpl) {\n // I.e. if this is `Author.mentorsRecursive`:\n // - fieldName=mentorsRecursive (being watched by the caller)\n // - otherFieldName=menteesRecursive (our reversal for walking down)\n // - m2oFieldName = mentor\n //\n // Note that \"reversing parents\" is generally easier, b/c when the tree changes, any given node can\n // still \"walk down\" to find its children w/the \"current value\" childrenRecursive; and any just-now\n // orphaned parents will themselves trigger their own childrenRecursive changes (i.e. we do not need\n // any \"old reference value\" / \"new reference value\" handling).\n //\n // I.e. given a tree of `A is mentor of B is mentor of C`, when B changes mentors, and we need to\n // \"walk to all affected/children mentees, so they can observe their new mentorsRecursive\", merely\n // walking down from (b itself & it's mentees) will find all affected mentees.\n const { otherFieldName, m2oFieldName } = p;\n // When a mentor.firstName/etc field changes, walk to its transitive children (through otherFieldName)\n // via the `return reverseReactiveHint` line below.\n //\n // But if the `author.mentor` field itself changes, we ourselves are also \"a new mentee\" of the mentor, so\n // do `fields.push(m2oFieldName)` to notify our direct reactable (a new mentee), and then our transitive\n // children/mentees. This avoids going \"up to the mentor, and walking down all of his mentees\", which would go\n // through our siblings, which would not have changed/need reactivity.\n _fields.push(m2oFieldName);\n maybeRecursive.push({\n kind: isReadOnly ? \"read-only\" : \"update\",\n entity: entityType,\n fields: recursiveMembershipFields(meta, m2oFieldName),\n path: [otherFieldName],\n });\n // Any reactables who were watching `parentsRecursive`, we'll back down to by going through `childrenRecursive`\n return reverseReactiveHint(rootType, entityType, subHint, undefined, false).map(appendPath(otherFieldName));\n } else if (p instanceof RecursiveChildrenCollectionImpl) {\n const { otherFieldName, o2mFieldName } = p;\n // I.e. this is `Author.menteesRecursive`\n // - fieldName=menteesRecursive` (being watched by the caller)\n // - otherFieldName=mentorsRecursive` (our reversal for walking up)\n // - The o2m's otherFieldName gives us the FK field (e.g., \"mentor\") on the child entity.\n //\n // Note that \"reversing children\" is trickier than reversing parents, b/c when the tree changes,\n // we need to consider both \"old reference value\" and \"new reference value\" scenarios.\n const o2mField = meta.allFields[o2mFieldName] as OneToManyField | OneToOneField;\n const m2oFieldName = o2mField.otherFieldName;\n _fields.push(m2oFieldName);\n maybeRecursive.push({\n kind: isReadOnly ? \"read-only\" : \"update\",\n entity: entityType,\n fields: recursiveMembershipFields(meta, m2oFieldName), // i.e. parent\n path: [otherFieldName], // i.e. parentsRecursive to tell them \"observe your new childrenRecursive\"\n });\n // Any reactables who were watching `childrenRecursive`, we'll up to by going through `parentsRecursive`\n return reverseReactiveHint(rootType, entityType, subHint, undefined, false).map(appendPath(otherFieldName));\n } else if (p instanceof RecursiveM2mCollectionImpl) {\n // I.e. if this is `User.parentsRecursive`:\n // - fieldName=parentsRecursive (being watched by the caller)\n // - otherFieldName=childrenRecursive (our reversal for walking down)\n // - m2mFieldName=parents (the underlying m2m collection)\n //\n // For m2m recursive, the \"change signal\" is when join rows are added/removed, so we push\n // the m2m collection name into fields (like regular m2m handling). To find affected entities\n // that are watching `parentsRecursive`, we walk through `childrenRecursive` (the reverse direction).\n const { otherFieldName, m2mFieldName } = p;\n // When a join row changes (e.g. parents collection is mutated), notify both this entity\n // and its transitive children/parents via otherFieldName\n _fields.push(m2mFieldName);\n maybeRecursive.push({\n kind: isReadOnly ? \"read-only\" : \"update\",\n entity: entityType,\n fields: recursiveMembershipFields(meta, m2mFieldName),\n path: [otherFieldName],\n });\n // Any reactables who were watching this recursive collection, we'll reach via the other direction\n return reverseReactiveHint(rootType, entityType, subHint, undefined, false).map(appendPath(otherFieldName));\n } else {\n throw new Error(`Invalid hint in ${rootType.name}.ts ${JSON.stringify(hint)}`);\n }\n }\n}\n\nfunction maybeAddTypeFilterSuffix(\n meta: EntityMetadata,\n field: ManyToOneField | OneToManyField | ManyToManyField | OneToOneField | PolymorphicFieldComponent,\n): string {\n const otherField = field.otherMetadata().allFields[field.otherFieldName];\n if (!otherField) {\n // This is usually an error, unless if its ReactiveReference which we don't create o2m-s for\n const isReactiveReference = \"kind\" in field && field.kind === \"m2o\" && field.derived === \"async\";\n if (!isReactiveReference) fail(`No field ${field.otherMetadata().type}.${field.otherFieldName}`);\n return field.otherFieldName;\n }\n // If we're Foo, and the other field (which we'll traverse back from at runtime) is actually\n // a poly FK pointing back to multiple `owner=Foo | Bar | Zaz`, add a suffix of `@Foo` so\n // that any runtime traversal that hit `owner` and see a `Bar | Zaz` will stop.\n const nextFieldIsPoly = otherField.kind === \"poly\";\n // If we're a SubType, and the other field points to our base type, assume there might be\n // SubType-specific fields in the path so far, so tell the other side to only walk back through\n // us if its value is actually a SubType.\n const nextFieldPointsToBaseType =\n !!meta.baseType && otherField.kind !== \"poly\" && (otherField as any).otherMetadata().cstr !== meta.cstr;\n return nextFieldIsPoly || nextFieldPointsToBaseType ? `${field.otherFieldName}@${meta.type}` : field.otherFieldName;\n}\n\n/**\n * Walks `reverseHint` for every entity in `entities`.\n *\n * I.e. given `[book1, book2]` and `[\"author\", 'publisher\"]`, will return all the books' authors' publishers.\n *\n * Note that for references (and only references), we walk both through \"the current value\"\n * and prior values remembered in `referenceHistory`. This is fundamentally because references are the true \"owner\"\n * of the relation, while collections are derived.\n *\n * For example, with reversals walking through collections:\n *\n * - Given a hint `book: { author: firstName }`\n * - Which reverses to `author[firstName] -> books`\n * - When we walk through `a1.books`, we use only the current value, which might have only `[b2]` in it.\n * - We don't need to worry about \"the old a1.books\" value, which might have previously had `[b1]` in it,\n * because the `b1.author` changing authors (from `a1` to `a2`) will trigger its own reactivity.\n *\n * Contrast with reversals walking through references:\n *\n * - Given a hint `author: { books: \"title\" }`\n * - Which reverses to `book[title] -> author`\n * - When we traverse through `b1.author`, we use both the current value and remembered values, so that both\n * \"our prior authors\" and \"our new author\" see their latest `author.books` collection values.\n *\n * Delete cleanup runs before reverse hints are walked, so the current relation value may already be unset by the\n * time we get here. For persisted entities, `cleanupOnEntityDeleted` clears the owning FK via `setField`, which\n * remembers the prior FK in `referenceHistory`; this lets us still find the old owner. For collection reversals\n * this is enough because collections are derived from the owning FK: removing `b1` from `a1.books` is represented\n * by `b1.author` changing, not by an independent \"old books\" snapshot.\n *\n * References can also have transient values during a unit of work, i.e. a ReactiveField might observe `b1.author=a2`\n * before the final flush changes it to `a3`. `setField` remembers both original and transient reference values\n * separately from `originalData`, and we walk them in addition to the current value.\n */\nexport async function followReverseHint(\n reactionName: string,\n entities: Entity[],\n reverseHint: string[],\n): Promise<Entity[]> {\n // Start at the current (unique) entities\n let current = new Set(entities);\n const paths = [...reverseHint];\n // And \"walk backwards\" through the reverse hint\n while (paths.length) {\n const path = paths.shift()!;\n const [fieldName, viaType] = path.split(\"@\");\n const promises = new Array(current.size);\n // The path might touch either a reference or a collection\n for (const c of current as Set<any>) {\n const relation = c[fieldName];\n // If `!relation`, I'd really like to `fail`, but atm we're knowingly creating invalid reversals for when\n // a ReactiveField depends on a ReactiveReference; b/c RRs lack an \"other side\", we cannot walk them in\n // reverse, and so for awhile we \"supported\" the RF -> RR pattern by relying on a totally-read-only reactive\n // hint -> dropped during reversal.\n //\n // However, now IsLoadedCache means that even these \"totally read only\" RFs need to be reactive at least\n // during their initial EM creation, to avoid stale values being observed during first-flush mutations,\n // and so we're now stuck with these \"invalid RR reversals\" until we can figure out a better way to handle them.\n // (Now that IsLoadedCache doesn't technically walk paths, it just invalidates given entity+field pairs, so\n // it is not bothered by an invalid/missing RR reversal.)\n if (!relation) continue;\n // fail(`Attempting to react for ${reactionName} for ${c.toString()}, but there is no \"reverse walkable\" field ${c.constructor.name}.${fieldName}`,);\n const currentValuePromise = maybeApplyTypeFilter(relation.load(), viaType);\n // Always wait for the relation itself\n promises.push(currentValuePromise);\n // If we're going from Book.author back to Author to re-validate the Author.books collection,\n // see if Book.author has changed, so we can re-validate both the old author's books and the\n // new author's books.\n const fieldKind = getMetadata(c).allFields[fieldName]?.kind;\n const isReference = fieldKind === \"m2o\" || fieldKind === \"poly\";\n const isManyToMany = fieldKind === \"m2m\";\n // See jsdoc comment about why this is only necessary for references...\n if (isReference) {\n for (const value of getInstanceData(c).getReferenceHistory(fieldName)) {\n promises.push(maybeApplyTypeFilter(loadReferenceHistoryValue(c, value), viaType));\n }\n }\n if (isManyToMany) {\n const m2m = c[fieldName] as ManyToManyCollection<any, any>;\n const joinRows = getEmInternalApi(m2m.entity.em).joinRows(m2m);\n // Return a tuple of [currentRows, removedRows]\n promises.push(joinRows.removedFor(m2m, c));\n }\n // Walk up the old parents\n if (relation instanceof RecursiveParentsCollectionImpl) {\n const { m2oFieldName } = relation;\n for (const value of getInstanceData(c).getReferenceHistory(m2oFieldName)) {\n promises.push(\n // First load the old parent itself\n loadReferenceHistoryValue(c, value).then((oldParent) => {\n if (!oldParent) return undefined;\n // And then resolve its fieldName=parentsRecursive\n const oldParentRecursiveParents = (oldParent as any)[fieldName];\n return oldParentRecursiveParents.load().then((parents: any) => {\n return [oldParent, ...parents];\n });\n }),\n );\n }\n }\n }\n const nextLevel = await Promise.all(promises);\n // Make a new set so that we dedupe as we go\n current = flatAndUnique(nextLevel);\n }\n return [...current];\n}\n\n/** Converts a normalized reactive `hint` into a load hint. */\nexport function convertToLoadHint<T extends Entity>(\n meta: EntityMetadata,\n hint: ReactiveHint<T>,\n allowCustomKeys = false,\n): LoadHint<T> {\n const loadHint = {};\n // Process the hints individually instead of just calling Object.fromEntries so that\n // we can handle inlined reactive hints that overlap.\n for (const [keyMaybeSuffix, subHint] of Object.entries(normalizeHint(hint))) {\n let key = keyMaybeSuffix.replace(suffixRe, \"\");\n let field = meta.allFields[key];\n\n // If `allowCustomKeys` is enabled, we're probably doing `toJSON`,\n // so look for `companyId` & `companyIds` to turn into `company`\n if (!field && allowCustomKeys) {\n const realField = Object.values(meta.allFields).find((f) => f.fieldIdName === key);\n if (realField) {\n field = realField;\n key = realField.fieldName;\n }\n }\n\n if (field) {\n switch (field.kind) {\n case \"m2m\":\n case \"m2o\":\n case \"o2m\":\n case \"o2o\":\n mergeNormalizedHints(loadHint, { [key]: convertToLoadHint(field.otherMetadata(), subHint, allowCustomKeys) });\n break;\n case \"poly\":\n for (const comp of field.components) {\n // Write `{ parent: { child@Type: hint } }` into the load hint\n mergeNormalizedHints(loadHint, {\n [key]: Object.fromEntries(\n Object.entries(convertToLoadHint(comp.otherMetadata(), subHint, allowCustomKeys)).map(\n // Map the subHint keys to add in `@Type`\n ([subKey, subHint]) => [`${subKey}@${comp.otherMetadata().type}`, subHint],\n ),\n ),\n });\n }\n break;\n case \"primitive\":\n case \"enum\":\n if (field.derived === \"async\") {\n mergeNormalizedHints(loadHint, { [key]: {} });\n }\n continue;\n case \"m2mEnum\":\n // The enum codes are a leaf, but the collection itself must be loaded for the reaction to read it.\n mergeNormalizedHints(loadHint, { [key]: {} });\n break;\n case \"primaryKey\":\n continue;\n default:\n throw new Error(`Invalid reactive hint ${meta.tableName} ${JSON.stringify(hint)}`);\n }\n } else {\n const p = getProperties(meta)[key];\n if (p instanceof RecursiveParentsCollectionImpl) {\n mergeNormalizedHints(loadHint, { [p.fieldName]: convertToLoadHint(meta, subHint, allowCustomKeys) });\n } else if (p instanceof RecursiveChildrenCollectionImpl) {\n mergeNormalizedHints(loadHint, { [p.fieldName]: convertToLoadHint(meta, subHint, allowCustomKeys) });\n } else if (p instanceof RecursiveM2mCollectionImpl) {\n mergeNormalizedHints(loadHint, { [p.fieldName]: convertToLoadHint(meta, subHint, allowCustomKeys) });\n } else if (p && p.reactiveHint) {\n mergeNormalizedHints(loadHint, convertToLoadHint(meta, p.reactiveHint, allowCustomKeys));\n } else if (!allowCustomKeys) {\n fail(`Invalid reactive hint on ${meta.tableName} ${JSON.stringify(hint)}`);\n }\n }\n }\n return loadHint as any;\n}\n\n/** An entity that, when `fields` change, should trigger the reactive rule/field pointed to by `path`. */\nexport interface ReactiveTarget {\n kind: \"read-only\" | \"update\";\n /** The entity that contains a field our reactive rule/field accesses. */\n entity: MaybeAbstractEntityConstructor<any>;\n /** The field(s) that our reactive rule/field accesses, plus any implicit fields like FKs. */\n fields: string[];\n /** The path from this `entity` back to the source reactive rule/field. */\n path: string[];\n}\n\nfunction maybeApplyTypeFilter(loadPromise: Promise<Entity | Entity[] | undefined>, viaType: string | undefined) {\n if (viaType) {\n return loadPromise.then((loaded) => {\n if (Array.isArray(loaded)) {\n return loaded.filter((e) => isTypeOrSubType(e, viaType));\n } else if (loaded && isTypeOrSubType(loaded, viaType)) {\n return loaded;\n } else {\n return undefined;\n }\n });\n }\n return loadPromise;\n}\n\n/** Loads a remembered reference value so reverse-hint walking can revisit it. */\nfunction loadReferenceHistoryValue(entity: Entity, value: any): Promise<Entity | undefined> {\n if (isEntity(value)) return Promise.resolve(value);\n if (isId(value)) return entity.em.load(value) as Promise<Entity>;\n return Promise.resolve(undefined);\n}\n\n/** Returns true if `entity` is exactly `typeName` or a subtype of `typeName`.\n *\n * Used to honor poly/type filters like `@Publisher` during reverse-hint walks:\n * a `SmallPublisher` entity should pass an `@Publisher` filter because it is\n * a Publisher via inheritance.\n */\nexport function isTypeOrSubType(entity: Entity, typeName: string): boolean {\n const meta = getMetadata(entity);\n return meta.type === typeName || meta.baseTypes.some((b) => b.type === typeName);\n}\n\nexport function isPolyHint(key: string): boolean {\n return key.includes(\"@\");\n}\n\nexport function splitPolyHintToKeyAndType(key: string): [string, string] {\n const [k, type] = key.split(\"@\");\n return [k, type];\n}\n\n/** If this hint is a poly like `author@Book`, return the type, otherwise return undefined. */\nexport function getRelationFromMaybePolyKey(entity: Entity, key: string): any {\n if (isPolyHint(key)) {\n const [realKey, typeName] = splitPolyHintToKeyAndType(key);\n // Even though `entity[realKey]` might exist, if it's from a different poly type, we don't want it\n const isApplicable = getBaseAndSelfMetas(getMetadata(entity)).some((meta) => meta.type === typeName);\n if (!isApplicable) return undefined;\n return (entity as any)[realKey];\n } else {\n return (entity as any)[key];\n }\n}\n\n/**\n * For an `author -> books -> bookReviews` hint, appends `BookReview.book`, `Book.author`,\n * i.e. the \"other side\" relation, to the reversed path on the way \"out\" from a mutated BookReview\n * to the target `Author` RF/RR/rule.\n */\nfunction appendPath(reverseField: string): (rt: ReactiveTarget) => ReactiveTarget {\n return (rt) => {\n // We can mutate this array b/c it's only owned by a single/continually-updated `ReactiveTarget`\n rt.path = [...rt.path, reverseField];\n return rt;\n };\n}\n\n/** Recursive membership changes on soft deletion or resurrection, even when the underlying relation is unchanged. */\nfunction recursiveMembershipFields(meta: EntityMetadata, fieldName: string): string[] {\n const deletedAt = getBaseMeta(meta).timestampFields?.deletedAt;\n return deletedAt ? [fieldName, deletedAt] : [fieldName];\n}\n\nfunction isAllReadOnly(hint: any): boolean {\n for (const [key, subHint] of Object.entries(normalizeHint(hint))) {\n const okay = (key.endsWith(\"_ro\") || key.endsWith(\":ro\")) && isAllReadOnly(subHint);\n if (!okay) return false;\n }\n return true;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactiveHints.d.cts","names":[],"sources":["../src/reactiveHints.ts"],"mappings":";;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"reactiveHints.d.cts","names":[],"sources":["../src/reactiveHints.ts"],"mappings":";;;;;;;;;;;;;;;;;;KA8CY,UAAU,UAAU,UAE9B,SAAS,KAGP,SAAS,KACT,SAAS,KACT,iBAAiB,KACjB,iBAAiB,KACjB,iBAAiB;;KAGT,SAAS,UAAU,uBAClB,WAAW,KAAK,cAAc,EAAE,4BAA4B,IAAI,cAAc,EAAE;KAGxF,cAAc,KAAK,UAAU,0BAA0B,KAAK;;KAG5D,iBAAiB,UAAU,aAC7B,WAAW,SAAS,kBAAkB,IAAI,sBAAsB,SAAS,GAAG;KAG1E,iBAAiB,UAAU,aAC7B,WAAW,SAAS,kBAAkB,IAAI,sBAAsB,SAAS,GAAG;KAG1E,iBAAiB,UAAU,aAC7B,WAAW,SAAS,kBAAkB,IAAI,sBAAsB,SAAS,GAAG;;;;;;;;KAUnE,aAAa,UAAU,iBACxB,UAAU,eACjB,oBAAoB,UAAU,eAC9B,mBAAmB;KAEX,mBAAmB,UAAU,aACtC,WAAW,UAAU,MAAM,UAAU,GAAG,kBAAiB,UAAU,UAAS,aAAa;;KAIhF,QAAQ,UAAU,QAAQ,KAAK,YACxC,WAAW,cAAc,WAAW,IAAI,EAAE,WAAW,6BAA6B,KAC/E,wBAAwB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,OAGhE,EAAE,WAAW,gCAAgC,SAAS,KACpD,qBAAqB,GAAG,GAAG,KAAK,gBAAgB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,KAAK,KAC7F,EAAE,WAAW,qBAAqB,SAAS,KACzC,gBAAgB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,KAAK,KAC7D,EAAE,WAAW,0BAA0B,KACrC,qBAAqB,GAAG,KACxB,EAAE,WAAW,sBAAsB,KACjC,iBAAiB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,OACzD,EAAE,WAAW,8BAA8B,KACzC,yBAAyB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,OACjE,EAAE,WAAW,oBAAoB,KAC/B,oBAAoB,KACpB,EAAE;;;;;;;;EASlB,uBAAuB,OAAO,GAAG;;EAEjC,SAAS,QAAQ,UAAU,SAAS,KAAK,YAAY,WAAW,cAAc;IAC5E,qBAAqB;;KAGb,mBAAmB,KAAK,UAAU;EAAW,uBAAuB;IAAM;;;;;;;;KAS1E,2BAA2B,KAAK,iBAAiB,WAAW,sBACpE,mBAAmB,kBACnB,mBAAmB;;;;;;;KAQX,oBAAoB,UAAU,iBAC/B,SAAS,eAChB,oBAAoB,SAAS;;KAGrB,qBAAqB,qCAAqC;EAChE,iBAAiB;;;;;;;;;;;;iBAaP,oBAAoB,UAAU,QAC5C,UAAU,+BAA+B,IACzC,YAAY,+BAA+B,IAC3C,MAAM,aAAa,IACnB,4BACA,4EACA,oBACC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+SmB,kBACpB,sBACA,UAAU,UACV,wBACC,QAAQ;;iBAuEK,kBAAkB,UAAU,QAC1C,MAAM,gBACN,MAAM,aAAa,IACnB,4BACC,SAAS;;UAyEK;EACf;;EAEA,QAAQ;;EAER;;EAEA;;;;;;;;iBA+Bc,gBAAgB,QAAQ,QAAQ;iBAKhC,WAAW;iBAIX,0BAA0B;;iBAM1B,4BAA4B,QAAQ,QAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactiveHints.d.mts","names":[],"sources":["../src/reactiveHints.ts"],"mappings":";;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"reactiveHints.d.mts","names":[],"sources":["../src/reactiveHints.ts"],"mappings":";;;;;;;;;;;;;;;;;;KA8CY,UAAU,UAAU,UAE9B,SAAS,KAGP,SAAS,KACT,SAAS,KACT,iBAAiB,KACjB,iBAAiB,KACjB,iBAAiB;;KAGT,SAAS,UAAU,uBAClB,WAAW,KAAK,cAAc,EAAE,4BAA4B,IAAI,cAAc,EAAE;KAGxF,cAAc,KAAK,UAAU,0BAA0B,KAAK;;KAG5D,iBAAiB,UAAU,aAC7B,WAAW,SAAS,kBAAkB,IAAI,sBAAsB,SAAS,GAAG;KAG1E,iBAAiB,UAAU,aAC7B,WAAW,SAAS,kBAAkB,IAAI,sBAAsB,SAAS,GAAG;KAG1E,iBAAiB,UAAU,aAC7B,WAAW,SAAS,kBAAkB,IAAI,sBAAsB,SAAS,GAAG;;;;;;;;KAUnE,aAAa,UAAU,iBACxB,UAAU,eACjB,oBAAoB,UAAU,eAC9B,mBAAmB;KAEX,mBAAmB,UAAU,aACtC,WAAW,UAAU,MAAM,UAAU,GAAG,kBAAiB,UAAU,UAAS,aAAa;;KAIhF,QAAQ,UAAU,QAAQ,KAAK,YACxC,WAAW,cAAc,WAAW,IAAI,EAAE,WAAW,6BAA6B,KAC/E,wBAAwB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,OAGhE,EAAE,WAAW,gCAAgC,SAAS,KACpD,qBAAqB,GAAG,GAAG,KAAK,gBAAgB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,KAAK,KAC7F,EAAE,WAAW,qBAAqB,SAAS,KACzC,gBAAgB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,KAAK,KAC7D,EAAE,WAAW,0BAA0B,KACrC,qBAAqB,GAAG,KACxB,EAAE,WAAW,sBAAsB,KACjC,iBAAiB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,OACzD,EAAE,WAAW,8BAA8B,KACzC,yBAAyB,GAAG,SAAS,QAAQ,GAAG,cAAc,GAAG,OACjE,EAAE,WAAW,oBAAoB,KAC/B,oBAAoB,KACpB,EAAE;;;;;;;;EASlB,uBAAuB,OAAO,GAAG;;EAEjC,SAAS,QAAQ,UAAU,SAAS,KAAK,YAAY,WAAW,cAAc;IAC5E,qBAAqB;;KAGb,mBAAmB,KAAK,UAAU;EAAW,uBAAuB;IAAM;;;;;;;;KAS1E,2BAA2B,KAAK,iBAAiB,WAAW,sBACpE,mBAAmB,kBACnB,mBAAmB;;;;;;;KAQX,oBAAoB,UAAU,iBAC/B,SAAS,eAChB,oBAAoB,SAAS;;KAGrB,qBAAqB,qCAAqC;EAChE,iBAAiB;;;;;;;;;;;;iBAaP,oBAAoB,UAAU,QAC5C,UAAU,+BAA+B,IACzC,YAAY,+BAA+B,IAC3C,MAAM,aAAa,IACnB,4BACA,4EACA,oBACC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+SmB,kBACpB,sBACA,UAAU,UACV,wBACC,QAAQ;;iBAuEK,kBAAkB,UAAU,QAC1C,MAAM,gBACN,MAAM,aAAa,IACnB,4BACC,SAAS;;UAyEK;EACf;;EAEA,QAAQ;;EAER;;EAEA;;;;;;;;iBA+Bc,gBAAgB,QAAQ,QAAQ;iBAKhC,WAAW;iBAIX,0BAA0B;;iBAM1B,4BAA4B,QAAQ,QAAQ"}
|
package/build/reactiveHints.js
CHANGED
|
@@ -5,7 +5,7 @@ import "./relations/OneToOneReference.js";
|
|
|
5
5
|
import { ReactiveGetterImpl } from "./relations/ReactiveGetter.js";
|
|
6
6
|
import { RecursiveChildrenCollectionImpl, RecursiveM2mCollectionImpl, RecursiveParentsCollectionImpl } from "./relations/RecursiveCollection.js";
|
|
7
7
|
import "./relations/index.js";
|
|
8
|
-
import { getBaseAndSelfMetas, getMetadata } from "./EntityMetadata.js";
|
|
8
|
+
import { getBaseAndSelfMetas, getBaseMeta, getMetadata } from "./EntityMetadata.js";
|
|
9
9
|
import "./changes.js";
|
|
10
10
|
import { normalizeHint, suffixRe } from "./normalizeHints.js";
|
|
11
11
|
import "./loadHints.js";
|
|
@@ -109,7 +109,7 @@ function reverseSubHint(rootType, entityType, hint, meta, fields, readOnlyFields
|
|
|
109
109
|
maybeRecursive.push({
|
|
110
110
|
kind: isReadOnly ? "read-only" : "update",
|
|
111
111
|
entity: entityType,
|
|
112
|
-
fields:
|
|
112
|
+
fields: recursiveMembershipFields(meta, m2oFieldName),
|
|
113
113
|
path: [otherFieldName]
|
|
114
114
|
});
|
|
115
115
|
return reverseReactiveHint(rootType, entityType, subHint, void 0, false).map(appendPath(otherFieldName));
|
|
@@ -120,7 +120,7 @@ function reverseSubHint(rootType, entityType, hint, meta, fields, readOnlyFields
|
|
|
120
120
|
maybeRecursive.push({
|
|
121
121
|
kind: isReadOnly ? "read-only" : "update",
|
|
122
122
|
entity: entityType,
|
|
123
|
-
fields:
|
|
123
|
+
fields: recursiveMembershipFields(meta, m2oFieldName),
|
|
124
124
|
path: [otherFieldName]
|
|
125
125
|
});
|
|
126
126
|
return reverseReactiveHint(rootType, entityType, subHint, void 0, false).map(appendPath(otherFieldName));
|
|
@@ -130,7 +130,7 @@ function reverseSubHint(rootType, entityType, hint, meta, fields, readOnlyFields
|
|
|
130
130
|
maybeRecursive.push({
|
|
131
131
|
kind: isReadOnly ? "read-only" : "update",
|
|
132
132
|
entity: entityType,
|
|
133
|
-
fields:
|
|
133
|
+
fields: recursiveMembershipFields(meta, m2mFieldName),
|
|
134
134
|
path: [otherFieldName]
|
|
135
135
|
});
|
|
136
136
|
return reverseReactiveHint(rootType, entityType, subHint, void 0, false).map(appendPath(otherFieldName));
|
|
@@ -192,7 +192,7 @@ async function followReverseHint(reactionName, entities, reverseHint) {
|
|
|
192
192
|
if (!relation) continue;
|
|
193
193
|
const currentValuePromise = maybeApplyTypeFilter(relation.load(), viaType);
|
|
194
194
|
promises.push(currentValuePromise);
|
|
195
|
-
const fieldKind = getMetadata(c).
|
|
195
|
+
const fieldKind = getMetadata(c).allFields[fieldName]?.kind;
|
|
196
196
|
const isReference = fieldKind === "m2o" || fieldKind === "poly";
|
|
197
197
|
const isManyToMany = fieldKind === "m2m";
|
|
198
198
|
if (isReference) for (const value of getInstanceData(c).getReferenceHistory(fieldName)) promises.push(maybeApplyTypeFilter(loadReferenceHistoryValue(c, value), viaType));
|
|
@@ -310,6 +310,11 @@ function appendPath(reverseField) {
|
|
|
310
310
|
return rt;
|
|
311
311
|
};
|
|
312
312
|
}
|
|
313
|
+
/** Recursive membership changes on soft deletion or resurrection, even when the underlying relation is unchanged. */
|
|
314
|
+
function recursiveMembershipFields(meta, fieldName) {
|
|
315
|
+
const deletedAt = getBaseMeta(meta).timestampFields?.deletedAt;
|
|
316
|
+
return deletedAt ? [fieldName, deletedAt] : [fieldName];
|
|
317
|
+
}
|
|
313
318
|
function isAllReadOnly(hint) {
|
|
314
319
|
for (const [key, subHint] of Object.entries(normalizeHint(hint))) if (!((key.endsWith("_ro") || key.endsWith(":ro")) && isAllReadOnly(subHint))) return false;
|
|
315
320
|
return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactiveHints.js","names":[],"sources":["../src/reactiveHints.ts"],"sourcesContent":["import { getInstanceData } from \"./BaseEntity.ts\";\nimport { type Changes } from \"./changes.ts\";\nimport { type Entity, isEntity } from \"./Entity.ts\";\nimport { type MaybeAbstractEntityConstructor, getEmInternalApi, isId } from \"./EntityManager.ts\";\nimport {\n type EntityMetadata,\n type ManyToManyField,\n type ManyToOneField,\n type OneToManyField,\n type OneToOneField,\n type PolymorphicFieldComponent,\n getBaseAndSelfMetas,\n getMetadata,\n} from \"./EntityMetadata.ts\";\nimport { getProperties } from \"./getProperties.ts\";\nimport { type LoadHint, type Loadable, type Loaded } from \"./loadHints.ts\";\nimport { type NormalizeHint, type SuffixSeperator, normalizeHint, suffixRe } from \"./normalizeHints.ts\";\nimport {\n type Collection,\n type EnumCollection,\n type LoadedCollection,\n type LoadedEnumCollection,\n type LoadedProperty,\n type LoadedReadOnlyCollection,\n type LoadedReference,\n type ManyToManyCollection,\n type OneToOneReference,\n type PolymorphicReference,\n type Property,\n PropertyImpl,\n type ReactiveGetter,\n type ReadOnlyCollection,\n type Reference,\n} from \"./relations/index.ts\";\nimport { type LoadedOneToOneReference } from \"./relations/OneToOneReference.ts\";\nimport { ReactiveGetterImpl } from \"./relations/ReactiveGetter.ts\";\nimport {\n RecursiveChildrenCollectionImpl,\n RecursiveM2mCollectionImpl,\n RecursiveParentsCollectionImpl,\n} from \"./relations/RecursiveCollection.ts\";\nimport { type FieldsOf, type RelationsOf } from \"./typeMap.ts\";\nimport { fail, flatAndUnique, mergeNormalizedHints } from \"./utils.ts\";\n\n/** The keys in `T` that rules & hooks can react to. */\nexport type Reactable<T extends Entity> =\n // This will be primitives + enums + m2os\n FieldsOf<T> &\n // We include `Loadable` so that we include hasReactiveProperties,\n // which are reversable but won't be in any of our codegen types.\n Loadable<T> &\n Gettable<T> &\n SuffixedFieldsOf<T> &\n SuffixedLoadable<T> &\n SuffixedGettable<T>;\n\n/** Finds `hasReactiveGetters` which are not \"loadable\" b/c they're always loaded. */\nexport type Gettable<T extends Entity> = {\n -readonly [K in keyof T as GettableValue<T[K]> extends never ? never : K]: GettableValue<T[K]>;\n};\n\ntype GettableValue<V> = V extends ReactiveGetter<any, infer P> ? P : never;\n\n/** The fields of `T` suffixed with `:ro` or `_ro`. */\ntype SuffixedFieldsOf<T extends Entity> = {\n [K in keyof FieldsOf<T> & string as `${K}${SuffixSeperator}ro`]: FieldsOf<T>[K];\n};\n\ntype SuffixedLoadable<T extends Entity> = {\n [K in keyof Loadable<T> & string as `${K}${SuffixSeperator}ro`]: Loadable<T>[K];\n};\n\ntype SuffixedGettable<T extends Entity> = {\n [K in keyof Gettable<T> & string as `${K}${SuffixSeperator}ro`]: Gettable<T>[K];\n};\n\n/**\n * A reactive hint of a single key, multiple keys, or nested keys and sub-hints.\n *\n * Reactive hints are different from load hints in that reactive hints include specific\n * fields to react against, while load hints contain only relations, collections, and async\n * properties to preload.\n */\nexport type ReactiveHint<T extends Entity> =\n | (keyof Reactable<T> & string)\n | ReadonlyArray<keyof Reactable<T> & string>\n | NestedReactiveHint<T>;\n\nexport type NestedReactiveHint<T extends Entity> = {\n [K in keyof Reactable<T>]?: Reactable<T>[K] extends infer U extends Entity ? ReactiveHint<U> : {};\n};\n\n/** Given an entity `T` that is being reacted with hint `H`, mark only the `H` attributes visible & populated. */\nexport type Reacted<T extends Entity, H> = Entity & {\n [K in keyof NormalizeHint<H> & keyof T]: T[K] extends OneToOneReference<any, infer U>\n ? LoadedOneToOneReference<T, Entity & Reacted<U, NormalizeHint<H>[K]>>\n : // Add an explicit check for PolymorphicReference because it can add `PolymorphicReference &` which gives access to `idIfSet`\n // ...although maybe LoadedReference should just have `idIfSet` in it as well? LoadedOneToOneReference does...\n T[K] extends PolymorphicReference<any, infer U, infer N>\n ? PolymorphicReference<T, U, N> & LoadedReference<T, Entity & Reacted<U, NormalizeHint<H>[K]>, N>\n : T[K] extends Reference<any, infer U, infer N>\n ? LoadedReference<T, Entity & Reacted<U, NormalizeHint<H>[K]>, N>\n : T[K] extends EnumCollection<any, infer E>\n ? LoadedEnumCollection<T, E>\n : T[K] extends Collection<any, infer U>\n ? LoadedCollection<T, Entity & Reacted<U, NormalizeHint<H>[K]>>\n : T[K] extends ReadOnlyCollection<any, infer U>\n ? LoadedReadOnlyCollection<T, Entity & Reacted<U, NormalizeHint<H>[K]>>\n : T[K] extends Property<any, infer V>\n ? LoadedProperty<any, V>\n : T[K];\n} & {\n /**\n * Gives reactive rules & fields a way to get the full entity if they really need it.\n *\n * This should be used carefully b/c it will side-step the reactive system, and instead\n * reactive rules & fields should explicitly declare access to the fields they need, which\n * will then allow accessing the field via the `Reacted<Author, ...>` type.\n */\n fullNonReactiveAccess: Loaded<T, H>;\n /** Allow detecting if a reactive change is due to nuances like `hasUpdated` or `hasChanged`. */\n changes: Changes<T, keyof (FieldsOf<T> & RelationsOf<T>), keyof NormalizeHint<H>>;\n} & MaybeTransientFields<T>;\n\n/** Allow returning reacted entities from `hasReactiveReference`. */\nexport type MaybeReactedEntity<V> = V extends Entity ? { fullNonReactiveAccess: V } : V;\n\n/**\n * Allow returning reacted entities from `hasReactiveProperty`.\n *\n * The `MaybeReactedEntity` is used by `hasReactiveReference` which already has `undefined | never`\n * broken out as a separate generic; `hasReactiveProperty` does not, and so this type conditionally\n * pulls `undefined` out first, and then defers to `MaybeReactedEntity`.\n */\nexport type MaybeReactedPropertyEntity<V> = V extends (infer V1 extends Entity) | undefined\n ? MaybeReactedEntity<V1> | undefined\n : MaybeReactedEntity<V>;\n\n/**\n * A reactive hint that only allows fields immediately on the entity, i.e. no nested hints.\n *\n * This is used by `ReactiveGetter`s to guarantee that their lambda functions will\n * always be `.get`-able and hence not need loading.\n */\nexport type ShallowReactiveHint<T extends Entity> =\n | (keyof FieldsOf<T> & string)\n | ReadonlyArray<keyof FieldsOf<T> & string>;\n\n/** If the domain model has transient fields, allow reactive behavior to see it, i.e. don't run validation rules for special operations. */\nexport type MaybeTransientFields<T> = \"transientFields\" extends keyof T\n ? { transientFields: T[\"transientFields\"] }\n : {};\n\n/**\n * Takes a reactive hint and returns the `ReactiveTarget`s that, should they change, need to\n * walk back to this watching reactable.\n *\n * @param rootType The original type that contained the reactive rule/field, used for helpful error messages\n * @param entityType The current entity of the step we're walking\n * @param hint The current hint we're walking\n * @param reactForOtherSide the name of our FK column, if the previous collection needs to react to our FK changing\n * @param isFirst\n */\nexport function reverseReactiveHint<T extends Entity>(\n rootType: MaybeAbstractEntityConstructor<T>,\n entityType: MaybeAbstractEntityConstructor<T>,\n hint: ReactiveHint<T>,\n reactForOtherSide?: string,\n reactForOtherSideIsReadOnly?: \"field-read-only\" | \"hint-read-only\" | false,\n isFirst: boolean = true,\n): ReactiveTarget[] {\n const meta = getMetadata(entityType);\n // This is the list of fields for this `entityType` that we will react to their changing values\n const fields: string[] = [];\n const readOnlyFields: string[] = [];\n const maybeRecursive: ReactiveTarget[] = [];\n\n // If the hint before us was a collection, i.e. { books: [\"title\"] }, besides just reacting\n // to our `title` changing, `reactForOtherSide` tells us to react to our `author` field as well.\n if (reactForOtherSide) {\n const _fields = reactForOtherSideIsReadOnly ? readOnlyFields : fields;\n _fields.push(reactForOtherSide);\n if (meta.timestampFields?.deletedAt) {\n _fields.push(meta.timestampFields.deletedAt);\n }\n }\n\n // Look through the hint for our own fields, i.e. `[\"firstName\", \"lastName]`, and nested hints like `{ books: \"title\" }`.\n const subHintTargets: ReactiveTarget[] = Object.entries(normalizeHint(hint)).flatMap(([keyMaybeSuffix, subHint]) => {\n return reverseSubHint(\n rootType,\n entityType,\n hint,\n meta,\n // Pass fields & readOnlyFields, so `reverseSubHint` can append any of our primitive fields it finds\n fields,\n readOnlyFields,\n maybeRecursive,\n keyMaybeSuffix, // maybeSuffix = might end with `_ro` like `name_ro`\n subHint,\n );\n });\n\n const myTargets: ReactiveTarget[] = [];\n // If any of our primitives (or m2o fields) change, establish a reactive path from \"here\" (entityType)\n // that is initially empty (path: []) but will have paths layered on by upstream callers\n if (fields.length > 0 || isFirst || reactForOtherSideIsReadOnly === \"field-read-only\") {\n myTargets.push({ kind: \"update\", entity: entityType, fields, path: [] });\n }\n // Normally read-only fields don't trigger true async reactivity, but IsLoadedCache\n // needs them for same-EM cache invalidation.\n if (readOnlyFields.length > 0) {\n myTargets.push({ kind: \"read-only\", entity: entityType, fields: readOnlyFields, path: [] });\n }\n\n return [...myTargets, ...maybeRecursive, ...subHintTargets];\n}\n\nfunction reverseSubHint(\n rootType: MaybeAbstractEntityConstructor<any>,\n entityType: MaybeAbstractEntityConstructor<any>,\n hint: any,\n meta: EntityMetadata,\n fields: string[],\n readOnlyFields: string[],\n maybeRecursive: ReactiveTarget[],\n keyMaybeSuffix: string,\n subHint: any,\n): ReactiveTarget[] {\n const key = keyMaybeSuffix.replace(suffixRe, \"\");\n const field = meta.allFields[key];\n const isReadOnly = !!keyMaybeSuffix.match(suffixRe) || (field && field.immutable);\n const _fields = isReadOnly ? readOnlyFields : fields;\n if (field) {\n switch (field.kind) {\n case \"m2o\": {\n // If this is a ReactiveReference, maybe we do something different?\n _fields.push(field.fieldName);\n const otherMeta = field.otherMetadata();\n // If this is a ReactiveReference, it won't have an \"other\" side, which is fine if:\n // - we're only reacting to the RR value itself (and have no sub-hints), or\n // - all of our sub-hints are read-only\n const otherField = otherMeta.allFields[field.otherFieldName];\n if (!otherField && !isAllReadOnly(subHint)) {\n throw new Error(\n `Invalid hint in ${rootType.name}.ts, we don't currently support reacting through ReactiveReferences, ${JSON.stringify(hint)}`,\n );\n }\n return reverseReactiveHint(rootType, otherMeta.cstr, subHint, undefined, false, false).map(\n appendPath(maybeAddTypeFilterSuffix(meta, field)),\n );\n }\n case \"poly\": {\n _fields.push(field.fieldName);\n // A poly is basically multiple m2os glued together, so copy/paste the `case m2o` code\n // above but do it for each component FK, and glue them together.\n return field.components.flatMap((comp) => {\n return reverseReactiveHint(rootType, comp.otherMetadata().cstr, subHint, undefined, false, false).map(\n appendPath(maybeAddTypeFilterSuffix(meta, comp)),\n );\n });\n }\n case \"m2m\": {\n // While o2m and o2o can watch for just FK changes by passing `reactForOtherSide` (the FK lives in the other\n // table), for m2m reactivity we push the collection name into the reactive hint, because it's effectively\n // \"the other/reverse side\", and JoinRows will trigger `queueDownstreamReactables` explicitly, instead of\n // `setField` which handles regular/non-m2m mutations.\n _fields.push(field.fieldName);\n const otherFieldName = maybeAddTypeFilterSuffix(meta, field);\n return reverseReactiveHint(\n rootType,\n field.otherMetadata().cstr,\n subHint,\n // For m2m, we can always pass undefined here, as otherwise having the opposite m2m collection\n // recalc all of its children will cause over-reactivity\n undefined,\n false,\n false,\n ).map(appendPath(otherFieldName));\n }\n case \"o2m\":\n case \"o2o\": {\n const isOtherReadOnly = field.otherMetadata().allFields[field.otherFieldName].immutable;\n const otherFieldName = maybeAddTypeFilterSuffix(meta, field);\n // This is not a field, but we want our reverse side to be reactive, so pass reactForOtherSide\n return reverseReactiveHint(\n rootType,\n field.otherMetadata().cstr,\n subHint,\n // IsLoadedCache invalidation always needs to know the otherField name, so we must\n // end up with either a kind=update or kind=read-only target with `otherFieldName` in it.\n field.otherFieldName,\n // We can have three outcomes:\n // - kind=update fields=[otherFieldName] (normal behavior)\n // - kind=update fields=[] + kind=read-only fields=[otherFieldName] (isOtherReadOnly)\n // - kind=read-only fields=[otherFieldName] (hint isReadOnly)\n isReadOnly ? \"hint-read-only\" : isOtherReadOnly ? \"field-read-only\" : false,\n false,\n ).map(appendPath(otherFieldName));\n }\n case \"primaryKey\":\n case \"primitive\":\n case \"enum\":\n _fields.push(key);\n return [];\n case \"m2mEnum\":\n // An enum m2m is a leaf of enum codes (no \"other side\" entity to recurse into); like a m2m,\n // `JoinRows` calls `queueDownstreamReactables` explicitly when its membership changes.\n _fields.push(key);\n return [];\n default:\n throw new Error(`Invalid hint in ${rootType.name}.ts hint ${JSON.stringify(hint)}`);\n }\n } else {\n // We only need to look for ReactiveProperties here, because ReactiveFields & ReactiveReferences\n // have underlying primitive fields that, when/if they change, will be handled in the ^ code.\n //\n // I.e. we specifically don't need to handle RFs & RRs ^, because the EntityManager.flush loop will\n // notice their primitive values changing, and kicking off any downstream reactive fields as necessary.\n const p = getProperties(meta)[key];\n if (p instanceof PropertyImpl) {\n // If the field is marked as readonly (i.e. using the `_ro` suffix), then we can assume that applies to its\n // entire hint as well and can simply omit it. This also allows us to use non-reactive async props as long as\n // they are readonly.\n if (isReadOnly) return [];\n if (!p.reactiveHint) {\n throw new Error(\n `Property ${key} cannot be used in reactive hints in ${rootType.name}.ts hint ${JSON.stringify(\n hint,\n )}, please use hasReactiveProperty instead`,\n );\n }\n return reverseReactiveHint(rootType, meta.cstr, p.reactiveHint, undefined, false, false);\n } else if (p instanceof ReactiveGetterImpl) {\n // If the field is marked as readonly, then we can assume that applies to its entire hint as well and can\n // simply omit it.\n if (isReadOnly) return [];\n return reverseReactiveHint(rootType, meta.cstr, p.reactiveHint, undefined, false, false);\n } else if (p instanceof RecursiveParentsCollectionImpl) {\n // I.e. if this is `Author.mentorsRecursive`:\n // - fieldName=mentorsRecursive (being watched by the caller)\n // - otherFieldName=menteesRecursive (our reversal for walking down)\n // - m2oFieldName = mentor\n //\n // Note that \"reversing parents\" is generally easier, b/c when the tree changes, any given node can\n // still \"walk down\" to find its children w/the \"current value\" childrenRecursive; and any just-now\n // orphaned parents will themselves trigger their own childrenRecursive changes (i.e. we do not need\n // any \"old reference value\" / \"new reference value\" handling).\n //\n // I.e. given a tree of `A is mentor of B is mentor of C`, when B changes mentors, and we need to\n // \"walk to all affected/children mentees, so they can observe their new mentorsRecursive\", merely\n // walking down from (b itself & it's mentees) will find all affected mentees.\n const { otherFieldName, m2oFieldName } = p;\n // When a mentor.firstName/etc field changes, walk to its transitive children (through otherFieldName)\n // via the `return reverseReactiveHint` line below.\n //\n // But if the `author.mentor` field itself changes, we ourselves are also \"a new mentee\" of the mentor, so\n // do `fields.push(m2oFieldName)` to notify our direct reactable (a new mentee), and then our transitive\n // children/mentees. This avoids going \"up to the mentor, and walking down all of his mentees\", which would go\n // through our siblings, which would not have changed/need reactivity.\n _fields.push(m2oFieldName);\n maybeRecursive.push({\n kind: isReadOnly ? \"read-only\" : \"update\",\n entity: entityType,\n fields: [m2oFieldName],\n path: [otherFieldName],\n });\n // Any reactables who were watching `parentsRecursive`, we'll back down to by going through `childrenRecursive`\n return reverseReactiveHint(rootType, entityType, subHint, undefined, false).map(appendPath(otherFieldName));\n } else if (p instanceof RecursiveChildrenCollectionImpl) {\n const { otherFieldName, o2mFieldName } = p;\n // I.e. this is `Author.menteesRecursive`\n // - fieldName=menteesRecursive` (being watched by the caller)\n // - otherFieldName=mentorsRecursive` (our reversal for walking up)\n // - The o2m's otherFieldName gives us the FK field (e.g., \"mentor\") on the child entity.\n //\n // Note that \"reversing children\" is trickier than reversing parents, b/c when the tree changes,\n // we need to consider both \"old reference value\" and \"new reference value\" scenarios.\n const o2mField = meta.allFields[o2mFieldName] as OneToManyField | OneToOneField;\n const m2oFieldName = o2mField.otherFieldName;\n _fields.push(m2oFieldName);\n maybeRecursive.push({\n kind: isReadOnly ? \"read-only\" : \"update\",\n entity: entityType,\n fields: [m2oFieldName], // i.e. parent\n path: [otherFieldName], // i.e. parentsRecursive to tell them \"observe your new childrenRecursive\"\n });\n // Any reactables who were watching `childrenRecursive`, we'll up to by going through `parentsRecursive`\n return reverseReactiveHint(rootType, entityType, subHint, undefined, false).map(appendPath(otherFieldName));\n } else if (p instanceof RecursiveM2mCollectionImpl) {\n // I.e. if this is `User.parentsRecursive`:\n // - fieldName=parentsRecursive (being watched by the caller)\n // - otherFieldName=childrenRecursive (our reversal for walking down)\n // - m2mFieldName=parents (the underlying m2m collection)\n //\n // For m2m recursive, the \"change signal\" is when join rows are added/removed, so we push\n // the m2m collection name into fields (like regular m2m handling). To find affected entities\n // that are watching `parentsRecursive`, we walk through `childrenRecursive` (the reverse direction).\n const { otherFieldName, m2mFieldName } = p;\n // When a join row changes (e.g. parents collection is mutated), notify both this entity\n // and its transitive children/parents via otherFieldName\n _fields.push(m2mFieldName);\n maybeRecursive.push({\n kind: isReadOnly ? \"read-only\" : \"update\",\n entity: entityType,\n fields: [m2mFieldName],\n path: [otherFieldName],\n });\n // Any reactables who were watching this recursive collection, we'll reach via the other direction\n return reverseReactiveHint(rootType, entityType, subHint, undefined, false).map(appendPath(otherFieldName));\n } else {\n throw new Error(`Invalid hint in ${rootType.name}.ts ${JSON.stringify(hint)}`);\n }\n }\n}\n\nfunction maybeAddTypeFilterSuffix(\n meta: EntityMetadata,\n field: ManyToOneField | OneToManyField | ManyToManyField | OneToOneField | PolymorphicFieldComponent,\n): string {\n const otherField = field.otherMetadata().allFields[field.otherFieldName];\n if (!otherField) {\n // This is usually an error, unless if its ReactiveReference which we don't create o2m-s for\n const isReactiveReference = \"kind\" in field && field.kind === \"m2o\" && field.derived === \"async\";\n if (!isReactiveReference) fail(`No field ${field.otherMetadata().type}.${field.otherFieldName}`);\n return field.otherFieldName;\n }\n // If we're Foo, and the other field (which we'll traverse back from at runtime) is actually\n // a poly FK pointing back to multiple `owner=Foo | Bar | Zaz`, add a suffix of `@Foo` so\n // that any runtime traversal that hit `owner` and see a `Bar | Zaz` will stop.\n const nextFieldIsPoly = otherField.kind === \"poly\";\n // If we're a SubType, and the other field points to our base type, assume there might be\n // SubType-specific fields in the path so far, so tell the other side to only walk back through\n // us if its value is actually a SubType.\n const nextFieldPointsToBaseType =\n !!meta.baseType && otherField.kind !== \"poly\" && (otherField as any).otherMetadata().cstr !== meta.cstr;\n return nextFieldIsPoly || nextFieldPointsToBaseType ? `${field.otherFieldName}@${meta.type}` : field.otherFieldName;\n}\n\n/**\n * Walks `reverseHint` for every entity in `entities`.\n *\n * I.e. given `[book1, book2]` and `[\"author\", 'publisher\"]`, will return all the books' authors' publishers.\n *\n * Note that for references (and only references), we walk both through \"the current value\"\n * and prior values remembered in `referenceHistory`. This is fundamentally because references are the true \"owner\"\n * of the relation, while collections are derived.\n *\n * For example, with reversals walking through collections:\n *\n * - Given a hint `book: { author: firstName }`\n * - Which reverses to `author[firstName] -> books`\n * - When we walk through `a1.books`, we use only the current value, which might have only `[b2]` in it.\n * - We don't need to worry about \"the old a1.books\" value, which might have previously had `[b1]` in it,\n * because the `b1.author` changing authors (from `a1` to `a2`) will trigger its own reactivity.\n *\n * Contrast with reversals walking through references:\n *\n * - Given a hint `author: { books: \"title\" }`\n * - Which reverses to `book[title] -> author`\n * - When we traverse through `b1.author`, we use both the current value and remembered values, so that both\n * \"our prior authors\" and \"our new author\" see their latest `author.books` collection values.\n *\n * Delete cleanup runs before reverse hints are walked, so the current relation value may already be unset by the\n * time we get here. For persisted entities, `cleanupOnEntityDeleted` clears the owning FK via `setField`, which\n * remembers the prior FK in `referenceHistory`; this lets us still find the old owner. For collection reversals\n * this is enough because collections are derived from the owning FK: removing `b1` from `a1.books` is represented\n * by `b1.author` changing, not by an independent \"old books\" snapshot.\n *\n * References can also have transient values during a unit of work, i.e. a ReactiveField might observe `b1.author=a2`\n * before the final flush changes it to `a3`. `setField` remembers both original and transient reference values\n * separately from `originalData`, and we walk them in addition to the current value.\n */\nexport async function followReverseHint(\n reactionName: string,\n entities: Entity[],\n reverseHint: string[],\n): Promise<Entity[]> {\n // Start at the current (unique) entities\n let current = new Set(entities);\n const paths = [...reverseHint];\n // And \"walk backwards\" through the reverse hint\n while (paths.length) {\n const path = paths.shift()!;\n const [fieldName, viaType] = path.split(\"@\");\n const promises = new Array(current.size);\n // The path might touch either a reference or a collection\n for (const c of current as Set<any>) {\n const relation = c[fieldName];\n // If `!relation`, I'd really like to `fail`, but atm we're knowingly creating invalid reversals for when\n // a ReactiveField depends on a ReactiveReference; b/c RRs lack an \"other side\", we cannot walk them in\n // reverse, and so for awhile we \"supported\" the RF -> RR pattern by relying on a totally-read-only reactive\n // hint -> dropped during reversal.\n //\n // However, now IsLoadedCache means that even these \"totally read only\" RFs need to be reactive at least\n // during their initial EM creation, to avoid stale values being observed during first-flush mutations,\n // and so we're now stuck with these \"invalid RR reversals\" until we can figure out a better way to handle them.\n // (Now that IsLoadedCache doesn't technically walk paths, it just invalidates given entity+field pairs, so\n // it is not bothered by an invalid/missing RR reversal.)\n if (!relation) continue;\n // fail(`Attempting to react for ${reactionName} for ${c.toString()}, but there is no \"reverse walkable\" field ${c.constructor.name}.${fieldName}`,);\n const currentValuePromise = maybeApplyTypeFilter(relation.load(), viaType);\n // Always wait for the relation itself\n promises.push(currentValuePromise);\n // If we're going from Book.author back to Author to re-validate the Author.books collection,\n // see if Book.author has changed, so we can re-validate both the old author's books and the\n // new author's books.\n const fieldKind = getMetadata(c).fields[fieldName]?.kind;\n const isReference = fieldKind === \"m2o\" || fieldKind === \"poly\";\n const isManyToMany = fieldKind === \"m2m\";\n // See jsdoc comment about why this is only necessary for references...\n if (isReference) {\n for (const value of getInstanceData(c).getReferenceHistory(fieldName)) {\n promises.push(maybeApplyTypeFilter(loadReferenceHistoryValue(c, value), viaType));\n }\n }\n if (isManyToMany) {\n const m2m = c[fieldName] as ManyToManyCollection<any, any>;\n const joinRows = getEmInternalApi(m2m.entity.em).joinRows(m2m);\n // Return a tuple of [currentRows, removedRows]\n promises.push(joinRows.removedFor(m2m, c));\n }\n // Walk up the old parents\n if (relation instanceof RecursiveParentsCollectionImpl) {\n const { m2oFieldName } = relation;\n for (const value of getInstanceData(c).getReferenceHistory(m2oFieldName)) {\n promises.push(\n // First load the old parent itself\n loadReferenceHistoryValue(c, value).then((oldParent) => {\n if (!oldParent) return undefined;\n // And then resolve its fieldName=parentsRecursive\n const oldParentRecursiveParents = (oldParent as any)[fieldName];\n return oldParentRecursiveParents.load().then((parents: any) => {\n return [oldParent, ...parents];\n });\n }),\n );\n }\n }\n }\n const nextLevel = await Promise.all(promises);\n // Make a new set so that we dedupe as we go\n current = flatAndUnique(nextLevel);\n }\n return [...current];\n}\n\n/** Converts a normalized reactive `hint` into a load hint. */\nexport function convertToLoadHint<T extends Entity>(\n meta: EntityMetadata,\n hint: ReactiveHint<T>,\n allowCustomKeys = false,\n): LoadHint<T> {\n const loadHint = {};\n // Process the hints individually instead of just calling Object.fromEntries so that\n // we can handle inlined reactive hints that overlap.\n for (const [keyMaybeSuffix, subHint] of Object.entries(normalizeHint(hint))) {\n let key = keyMaybeSuffix.replace(suffixRe, \"\");\n let field = meta.allFields[key];\n\n // If `allowCustomKeys` is enabled, we're probably doing `toJSON`,\n // so look for `companyId` & `companyIds` to turn into `company`\n if (!field && allowCustomKeys) {\n const realField = Object.values(meta.allFields).find((f) => f.fieldIdName === key);\n if (realField) {\n field = realField;\n key = realField.fieldName;\n }\n }\n\n if (field) {\n switch (field.kind) {\n case \"m2m\":\n case \"m2o\":\n case \"o2m\":\n case \"o2o\":\n mergeNormalizedHints(loadHint, { [key]: convertToLoadHint(field.otherMetadata(), subHint, allowCustomKeys) });\n break;\n case \"poly\":\n for (const comp of field.components) {\n // Write `{ parent: { child@Type: hint } }` into the load hint\n mergeNormalizedHints(loadHint, {\n [key]: Object.fromEntries(\n Object.entries(convertToLoadHint(comp.otherMetadata(), subHint, allowCustomKeys)).map(\n // Map the subHint keys to add in `@Type`\n ([subKey, subHint]) => [`${subKey}@${comp.otherMetadata().type}`, subHint],\n ),\n ),\n });\n }\n break;\n case \"primitive\":\n case \"enum\":\n if (field.derived === \"async\") {\n mergeNormalizedHints(loadHint, { [key]: {} });\n }\n continue;\n case \"m2mEnum\":\n // The enum codes are a leaf, but the collection itself must be loaded for the reaction to read it.\n mergeNormalizedHints(loadHint, { [key]: {} });\n break;\n case \"primaryKey\":\n continue;\n default:\n throw new Error(`Invalid reactive hint ${meta.tableName} ${JSON.stringify(hint)}`);\n }\n } else {\n const p = getProperties(meta)[key];\n if (p instanceof RecursiveParentsCollectionImpl) {\n mergeNormalizedHints(loadHint, { [p.fieldName]: convertToLoadHint(meta, subHint, allowCustomKeys) });\n } else if (p instanceof RecursiveChildrenCollectionImpl) {\n mergeNormalizedHints(loadHint, { [p.fieldName]: convertToLoadHint(meta, subHint, allowCustomKeys) });\n } else if (p instanceof RecursiveM2mCollectionImpl) {\n mergeNormalizedHints(loadHint, { [p.fieldName]: convertToLoadHint(meta, subHint, allowCustomKeys) });\n } else if (p && p.reactiveHint) {\n mergeNormalizedHints(loadHint, convertToLoadHint(meta, p.reactiveHint, allowCustomKeys));\n } else if (!allowCustomKeys) {\n fail(`Invalid reactive hint on ${meta.tableName} ${JSON.stringify(hint)}`);\n }\n }\n }\n return loadHint as any;\n}\n\n/** An entity that, when `fields` change, should trigger the reactive rule/field pointed to by `path`. */\nexport interface ReactiveTarget {\n kind: \"read-only\" | \"update\";\n /** The entity that contains a field our reactive rule/field accesses. */\n entity: MaybeAbstractEntityConstructor<any>;\n /** The field(s) that our reactive rule/field accesses, plus any implicit fields like FKs. */\n fields: string[];\n /** The path from this `entity` back to the source reactive rule/field. */\n path: string[];\n}\n\nfunction maybeApplyTypeFilter(loadPromise: Promise<Entity | Entity[] | undefined>, viaType: string | undefined) {\n if (viaType) {\n return loadPromise.then((loaded) => {\n if (Array.isArray(loaded)) {\n return loaded.filter((e) => isTypeOrSubType(e, viaType));\n } else if (loaded && isTypeOrSubType(loaded, viaType)) {\n return loaded;\n } else {\n return undefined;\n }\n });\n }\n return loadPromise;\n}\n\n/** Loads a remembered reference value so reverse-hint walking can revisit it. */\nfunction loadReferenceHistoryValue(entity: Entity, value: any): Promise<Entity | undefined> {\n if (isEntity(value)) return Promise.resolve(value);\n if (isId(value)) return entity.em.load(value) as Promise<Entity>;\n return Promise.resolve(undefined);\n}\n\n/** Returns true if `entity` is exactly `typeName` or a subtype of `typeName`.\n *\n * Used to honor poly/type filters like `@Publisher` during reverse-hint walks:\n * a `SmallPublisher` entity should pass an `@Publisher` filter because it is\n * a Publisher via inheritance.\n */\nexport function isTypeOrSubType(entity: Entity, typeName: string): boolean {\n const meta = getMetadata(entity);\n return meta.type === typeName || meta.baseTypes.some((b) => b.type === typeName);\n}\n\nexport function isPolyHint(key: string): boolean {\n return key.includes(\"@\");\n}\n\nexport function splitPolyHintToKeyAndType(key: string): [string, string] {\n const [k, type] = key.split(\"@\");\n return [k, type];\n}\n\n/** If this hint is a poly like `author@Book`, return the type, otherwise return undefined. */\nexport function getRelationFromMaybePolyKey(entity: Entity, key: string): any {\n if (isPolyHint(key)) {\n const [realKey, typeName] = splitPolyHintToKeyAndType(key);\n // Even though `entity[realKey]` might exist, if it's from a different poly type, we don't want it\n const isApplicable = getBaseAndSelfMetas(getMetadata(entity)).some((meta) => meta.type === typeName);\n if (!isApplicable) return undefined;\n return (entity as any)[realKey];\n } else {\n return (entity as any)[key];\n }\n}\n\n/**\n * For an `author -> books -> bookReviews` hint, appends `BookReview.book`, `Book.author`,\n * i.e. the \"other side\" relation, to the reversed path on the way \"out\" from a mutated BookReview\n * to the target `Author` RF/RR/rule.\n */\nfunction appendPath(reverseField: string): (rt: ReactiveTarget) => ReactiveTarget {\n return (rt) => {\n // We can mutate this array b/c it's only owned by a single/continually-updated `ReactiveTarget`\n rt.path = [...rt.path, reverseField];\n return rt;\n };\n}\n\nfunction isAllReadOnly(hint: any): boolean {\n for (const [key, subHint] of Object.entries(normalizeHint(hint))) {\n const okay = (key.endsWith(\"_ro\") || key.endsWith(\":ro\")) && isAllReadOnly(subHint);\n if (!okay) return false;\n }\n return true;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"reactiveHints.js","names":[],"sources":["../src/reactiveHints.ts"],"sourcesContent":["import { getInstanceData } from \"./BaseEntity.ts\";\nimport { type Changes } from \"./changes.ts\";\nimport { type Entity, isEntity } from \"./Entity.ts\";\nimport { type MaybeAbstractEntityConstructor, getEmInternalApi, isId } from \"./EntityManager.ts\";\nimport {\n type EntityMetadata,\n type ManyToManyField,\n type ManyToOneField,\n type OneToManyField,\n type OneToOneField,\n type PolymorphicFieldComponent,\n getBaseAndSelfMetas,\n getBaseMeta,\n getMetadata,\n} from \"./EntityMetadata.ts\";\nimport { getProperties } from \"./getProperties.ts\";\nimport { type LoadHint, type Loadable, type Loaded } from \"./loadHints.ts\";\nimport { type NormalizeHint, type SuffixSeperator, normalizeHint, suffixRe } from \"./normalizeHints.ts\";\nimport {\n type Collection,\n type EnumCollection,\n type LoadedCollection,\n type LoadedEnumCollection,\n type LoadedProperty,\n type LoadedReadOnlyCollection,\n type LoadedReference,\n type ManyToManyCollection,\n type OneToOneReference,\n type PolymorphicReference,\n type Property,\n PropertyImpl,\n type ReactiveGetter,\n type ReadOnlyCollection,\n type Reference,\n} from \"./relations/index.ts\";\nimport { type LoadedOneToOneReference } from \"./relations/OneToOneReference.ts\";\nimport { ReactiveGetterImpl } from \"./relations/ReactiveGetter.ts\";\nimport {\n RecursiveChildrenCollectionImpl,\n RecursiveM2mCollectionImpl,\n RecursiveParentsCollectionImpl,\n} from \"./relations/RecursiveCollection.ts\";\nimport { type FieldsOf, type RelationsOf } from \"./typeMap.ts\";\nimport { fail, flatAndUnique, mergeNormalizedHints } from \"./utils.ts\";\n\n/** The keys in `T` that rules & hooks can react to. */\nexport type Reactable<T extends Entity> =\n // This will be primitives + enums + m2os\n FieldsOf<T> &\n // We include `Loadable` so that we include hasReactiveProperties,\n // which are reversable but won't be in any of our codegen types.\n Loadable<T> &\n Gettable<T> &\n SuffixedFieldsOf<T> &\n SuffixedLoadable<T> &\n SuffixedGettable<T>;\n\n/** Finds `hasReactiveGetters` which are not \"loadable\" b/c they're always loaded. */\nexport type Gettable<T extends Entity> = {\n -readonly [K in keyof T as GettableValue<T[K]> extends never ? never : K]: GettableValue<T[K]>;\n};\n\ntype GettableValue<V> = V extends ReactiveGetter<any, infer P> ? P : never;\n\n/** The fields of `T` suffixed with `:ro` or `_ro`. */\ntype SuffixedFieldsOf<T extends Entity> = {\n [K in keyof FieldsOf<T> & string as `${K}${SuffixSeperator}ro`]: FieldsOf<T>[K];\n};\n\ntype SuffixedLoadable<T extends Entity> = {\n [K in keyof Loadable<T> & string as `${K}${SuffixSeperator}ro`]: Loadable<T>[K];\n};\n\ntype SuffixedGettable<T extends Entity> = {\n [K in keyof Gettable<T> & string as `${K}${SuffixSeperator}ro`]: Gettable<T>[K];\n};\n\n/**\n * A reactive hint of a single key, multiple keys, or nested keys and sub-hints.\n *\n * Reactive hints are different from load hints in that reactive hints include specific\n * fields to react against, while load hints contain only relations, collections, and async\n * properties to preload.\n */\nexport type ReactiveHint<T extends Entity> =\n | (keyof Reactable<T> & string)\n | ReadonlyArray<keyof Reactable<T> & string>\n | NestedReactiveHint<T>;\n\nexport type NestedReactiveHint<T extends Entity> = {\n [K in keyof Reactable<T>]?: Reactable<T>[K] extends infer U extends Entity ? ReactiveHint<U> : {};\n};\n\n/** Given an entity `T` that is being reacted with hint `H`, mark only the `H` attributes visible & populated. */\nexport type Reacted<T extends Entity, H> = Entity & {\n [K in keyof NormalizeHint<H> & keyof T]: T[K] extends OneToOneReference<any, infer U>\n ? LoadedOneToOneReference<T, Entity & Reacted<U, NormalizeHint<H>[K]>>\n : // Add an explicit check for PolymorphicReference because it can add `PolymorphicReference &` which gives access to `idIfSet`\n // ...although maybe LoadedReference should just have `idIfSet` in it as well? LoadedOneToOneReference does...\n T[K] extends PolymorphicReference<any, infer U, infer N>\n ? PolymorphicReference<T, U, N> & LoadedReference<T, Entity & Reacted<U, NormalizeHint<H>[K]>, N>\n : T[K] extends Reference<any, infer U, infer N>\n ? LoadedReference<T, Entity & Reacted<U, NormalizeHint<H>[K]>, N>\n : T[K] extends EnumCollection<any, infer E>\n ? LoadedEnumCollection<T, E>\n : T[K] extends Collection<any, infer U>\n ? LoadedCollection<T, Entity & Reacted<U, NormalizeHint<H>[K]>>\n : T[K] extends ReadOnlyCollection<any, infer U>\n ? LoadedReadOnlyCollection<T, Entity & Reacted<U, NormalizeHint<H>[K]>>\n : T[K] extends Property<any, infer V>\n ? LoadedProperty<any, V>\n : T[K];\n} & {\n /**\n * Gives reactive rules & fields a way to get the full entity if they really need it.\n *\n * This should be used carefully b/c it will side-step the reactive system, and instead\n * reactive rules & fields should explicitly declare access to the fields they need, which\n * will then allow accessing the field via the `Reacted<Author, ...>` type.\n */\n fullNonReactiveAccess: Loaded<T, H>;\n /** Allow detecting if a reactive change is due to nuances like `hasUpdated` or `hasChanged`. */\n changes: Changes<T, keyof (FieldsOf<T> & RelationsOf<T>), keyof NormalizeHint<H>>;\n} & MaybeTransientFields<T>;\n\n/** Allow returning reacted entities from `hasReactiveReference`. */\nexport type MaybeReactedEntity<V> = V extends Entity ? { fullNonReactiveAccess: V } : V;\n\n/**\n * Allow returning reacted entities from `hasReactiveProperty`.\n *\n * The `MaybeReactedEntity` is used by `hasReactiveReference` which already has `undefined | never`\n * broken out as a separate generic; `hasReactiveProperty` does not, and so this type conditionally\n * pulls `undefined` out first, and then defers to `MaybeReactedEntity`.\n */\nexport type MaybeReactedPropertyEntity<V> = V extends (infer V1 extends Entity) | undefined\n ? MaybeReactedEntity<V1> | undefined\n : MaybeReactedEntity<V>;\n\n/**\n * A reactive hint that only allows fields immediately on the entity, i.e. no nested hints.\n *\n * This is used by `ReactiveGetter`s to guarantee that their lambda functions will\n * always be `.get`-able and hence not need loading.\n */\nexport type ShallowReactiveHint<T extends Entity> =\n | (keyof FieldsOf<T> & string)\n | ReadonlyArray<keyof FieldsOf<T> & string>;\n\n/** If the domain model has transient fields, allow reactive behavior to see it, i.e. don't run validation rules for special operations. */\nexport type MaybeTransientFields<T> = \"transientFields\" extends keyof T\n ? { transientFields: T[\"transientFields\"] }\n : {};\n\n/**\n * Takes a reactive hint and returns the `ReactiveTarget`s that, should they change, need to\n * walk back to this watching reactable.\n *\n * @param rootType The original type that contained the reactive rule/field, used for helpful error messages\n * @param entityType The current entity of the step we're walking\n * @param hint The current hint we're walking\n * @param reactForOtherSide the name of our FK column, if the previous collection needs to react to our FK changing\n * @param isFirst\n */\nexport function reverseReactiveHint<T extends Entity>(\n rootType: MaybeAbstractEntityConstructor<T>,\n entityType: MaybeAbstractEntityConstructor<T>,\n hint: ReactiveHint<T>,\n reactForOtherSide?: string,\n reactForOtherSideIsReadOnly?: \"field-read-only\" | \"hint-read-only\" | false,\n isFirst: boolean = true,\n): ReactiveTarget[] {\n const meta = getMetadata(entityType);\n // This is the list of fields for this `entityType` that we will react to their changing values\n const fields: string[] = [];\n const readOnlyFields: string[] = [];\n const maybeRecursive: ReactiveTarget[] = [];\n\n // If the hint before us was a collection, i.e. { books: [\"title\"] }, besides just reacting\n // to our `title` changing, `reactForOtherSide` tells us to react to our `author` field as well.\n if (reactForOtherSide) {\n const _fields = reactForOtherSideIsReadOnly ? readOnlyFields : fields;\n _fields.push(reactForOtherSide);\n if (meta.timestampFields?.deletedAt) {\n _fields.push(meta.timestampFields.deletedAt);\n }\n }\n\n // Look through the hint for our own fields, i.e. `[\"firstName\", \"lastName]`, and nested hints like `{ books: \"title\" }`.\n const subHintTargets: ReactiveTarget[] = Object.entries(normalizeHint(hint)).flatMap(([keyMaybeSuffix, subHint]) => {\n return reverseSubHint(\n rootType,\n entityType,\n hint,\n meta,\n // Pass fields & readOnlyFields, so `reverseSubHint` can append any of our primitive fields it finds\n fields,\n readOnlyFields,\n maybeRecursive,\n keyMaybeSuffix, // maybeSuffix = might end with `_ro` like `name_ro`\n subHint,\n );\n });\n\n const myTargets: ReactiveTarget[] = [];\n // If any of our primitives (or m2o fields) change, establish a reactive path from \"here\" (entityType)\n // that is initially empty (path: []) but will have paths layered on by upstream callers\n if (fields.length > 0 || isFirst || reactForOtherSideIsReadOnly === \"field-read-only\") {\n myTargets.push({ kind: \"update\", entity: entityType, fields, path: [] });\n }\n // Normally read-only fields don't trigger true async reactivity, but IsLoadedCache\n // needs them for same-EM cache invalidation.\n if (readOnlyFields.length > 0) {\n myTargets.push({ kind: \"read-only\", entity: entityType, fields: readOnlyFields, path: [] });\n }\n\n return [...myTargets, ...maybeRecursive, ...subHintTargets];\n}\n\nfunction reverseSubHint(\n rootType: MaybeAbstractEntityConstructor<any>,\n entityType: MaybeAbstractEntityConstructor<any>,\n hint: any,\n meta: EntityMetadata,\n fields: string[],\n readOnlyFields: string[],\n maybeRecursive: ReactiveTarget[],\n keyMaybeSuffix: string,\n subHint: any,\n): ReactiveTarget[] {\n const key = keyMaybeSuffix.replace(suffixRe, \"\");\n const field = meta.allFields[key];\n const isReadOnly = !!keyMaybeSuffix.match(suffixRe) || (field && field.immutable);\n const _fields = isReadOnly ? readOnlyFields : fields;\n if (field) {\n switch (field.kind) {\n case \"m2o\": {\n // If this is a ReactiveReference, maybe we do something different?\n _fields.push(field.fieldName);\n const otherMeta = field.otherMetadata();\n // If this is a ReactiveReference, it won't have an \"other\" side, which is fine if:\n // - we're only reacting to the RR value itself (and have no sub-hints), or\n // - all of our sub-hints are read-only\n const otherField = otherMeta.allFields[field.otherFieldName];\n if (!otherField && !isAllReadOnly(subHint)) {\n throw new Error(\n `Invalid hint in ${rootType.name}.ts, we don't currently support reacting through ReactiveReferences, ${JSON.stringify(hint)}`,\n );\n }\n return reverseReactiveHint(rootType, otherMeta.cstr, subHint, undefined, false, false).map(\n appendPath(maybeAddTypeFilterSuffix(meta, field)),\n );\n }\n case \"poly\": {\n _fields.push(field.fieldName);\n // A poly is basically multiple m2os glued together, so copy/paste the `case m2o` code\n // above but do it for each component FK, and glue them together.\n return field.components.flatMap((comp) => {\n return reverseReactiveHint(rootType, comp.otherMetadata().cstr, subHint, undefined, false, false).map(\n appendPath(maybeAddTypeFilterSuffix(meta, comp)),\n );\n });\n }\n case \"m2m\": {\n // While o2m and o2o can watch for just FK changes by passing `reactForOtherSide` (the FK lives in the other\n // table), for m2m reactivity we push the collection name into the reactive hint, because it's effectively\n // \"the other/reverse side\", and JoinRows will trigger `queueDownstreamReactables` explicitly, instead of\n // `setField` which handles regular/non-m2m mutations.\n _fields.push(field.fieldName);\n const otherFieldName = maybeAddTypeFilterSuffix(meta, field);\n return reverseReactiveHint(\n rootType,\n field.otherMetadata().cstr,\n subHint,\n // For m2m, we can always pass undefined here, as otherwise having the opposite m2m collection\n // recalc all of its children will cause over-reactivity\n undefined,\n false,\n false,\n ).map(appendPath(otherFieldName));\n }\n case \"o2m\":\n case \"o2o\": {\n const isOtherReadOnly = field.otherMetadata().allFields[field.otherFieldName].immutable;\n const otherFieldName = maybeAddTypeFilterSuffix(meta, field);\n // This is not a field, but we want our reverse side to be reactive, so pass reactForOtherSide\n return reverseReactiveHint(\n rootType,\n field.otherMetadata().cstr,\n subHint,\n // IsLoadedCache invalidation always needs to know the otherField name, so we must\n // end up with either a kind=update or kind=read-only target with `otherFieldName` in it.\n field.otherFieldName,\n // We can have three outcomes:\n // - kind=update fields=[otherFieldName] (normal behavior)\n // - kind=update fields=[] + kind=read-only fields=[otherFieldName] (isOtherReadOnly)\n // - kind=read-only fields=[otherFieldName] (hint isReadOnly)\n isReadOnly ? \"hint-read-only\" : isOtherReadOnly ? \"field-read-only\" : false,\n false,\n ).map(appendPath(otherFieldName));\n }\n case \"primaryKey\":\n case \"primitive\":\n case \"enum\":\n _fields.push(key);\n return [];\n case \"m2mEnum\":\n // An enum m2m is a leaf of enum codes (no \"other side\" entity to recurse into); like a m2m,\n // `JoinRows` calls `queueDownstreamReactables` explicitly when its membership changes.\n _fields.push(key);\n return [];\n default:\n throw new Error(`Invalid hint in ${rootType.name}.ts hint ${JSON.stringify(hint)}`);\n }\n } else {\n // We only need to look for ReactiveProperties here, because ReactiveFields & ReactiveReferences\n // have underlying primitive fields that, when/if they change, will be handled in the ^ code.\n //\n // I.e. we specifically don't need to handle RFs & RRs ^, because the EntityManager.flush loop will\n // notice their primitive values changing, and kicking off any downstream reactive fields as necessary.\n const p = getProperties(meta)[key];\n if (p instanceof PropertyImpl) {\n // If the field is marked as readonly (i.e. using the `_ro` suffix), then we can assume that applies to its\n // entire hint as well and can simply omit it. This also allows us to use non-reactive async props as long as\n // they are readonly.\n if (isReadOnly) return [];\n if (!p.reactiveHint) {\n throw new Error(\n `Property ${key} cannot be used in reactive hints in ${rootType.name}.ts hint ${JSON.stringify(\n hint,\n )}, please use hasReactiveProperty instead`,\n );\n }\n return reverseReactiveHint(rootType, meta.cstr, p.reactiveHint, undefined, false, false);\n } else if (p instanceof ReactiveGetterImpl) {\n // If the field is marked as readonly, then we can assume that applies to its entire hint as well and can\n // simply omit it.\n if (isReadOnly) return [];\n return reverseReactiveHint(rootType, meta.cstr, p.reactiveHint, undefined, false, false);\n } else if (p instanceof RecursiveParentsCollectionImpl) {\n // I.e. if this is `Author.mentorsRecursive`:\n // - fieldName=mentorsRecursive (being watched by the caller)\n // - otherFieldName=menteesRecursive (our reversal for walking down)\n // - m2oFieldName = mentor\n //\n // Note that \"reversing parents\" is generally easier, b/c when the tree changes, any given node can\n // still \"walk down\" to find its children w/the \"current value\" childrenRecursive; and any just-now\n // orphaned parents will themselves trigger their own childrenRecursive changes (i.e. we do not need\n // any \"old reference value\" / \"new reference value\" handling).\n //\n // I.e. given a tree of `A is mentor of B is mentor of C`, when B changes mentors, and we need to\n // \"walk to all affected/children mentees, so they can observe their new mentorsRecursive\", merely\n // walking down from (b itself & it's mentees) will find all affected mentees.\n const { otherFieldName, m2oFieldName } = p;\n // When a mentor.firstName/etc field changes, walk to its transitive children (through otherFieldName)\n // via the `return reverseReactiveHint` line below.\n //\n // But if the `author.mentor` field itself changes, we ourselves are also \"a new mentee\" of the mentor, so\n // do `fields.push(m2oFieldName)` to notify our direct reactable (a new mentee), and then our transitive\n // children/mentees. This avoids going \"up to the mentor, and walking down all of his mentees\", which would go\n // through our siblings, which would not have changed/need reactivity.\n _fields.push(m2oFieldName);\n maybeRecursive.push({\n kind: isReadOnly ? \"read-only\" : \"update\",\n entity: entityType,\n fields: recursiveMembershipFields(meta, m2oFieldName),\n path: [otherFieldName],\n });\n // Any reactables who were watching `parentsRecursive`, we'll back down to by going through `childrenRecursive`\n return reverseReactiveHint(rootType, entityType, subHint, undefined, false).map(appendPath(otherFieldName));\n } else if (p instanceof RecursiveChildrenCollectionImpl) {\n const { otherFieldName, o2mFieldName } = p;\n // I.e. this is `Author.menteesRecursive`\n // - fieldName=menteesRecursive` (being watched by the caller)\n // - otherFieldName=mentorsRecursive` (our reversal for walking up)\n // - The o2m's otherFieldName gives us the FK field (e.g., \"mentor\") on the child entity.\n //\n // Note that \"reversing children\" is trickier than reversing parents, b/c when the tree changes,\n // we need to consider both \"old reference value\" and \"new reference value\" scenarios.\n const o2mField = meta.allFields[o2mFieldName] as OneToManyField | OneToOneField;\n const m2oFieldName = o2mField.otherFieldName;\n _fields.push(m2oFieldName);\n maybeRecursive.push({\n kind: isReadOnly ? \"read-only\" : \"update\",\n entity: entityType,\n fields: recursiveMembershipFields(meta, m2oFieldName), // i.e. parent\n path: [otherFieldName], // i.e. parentsRecursive to tell them \"observe your new childrenRecursive\"\n });\n // Any reactables who were watching `childrenRecursive`, we'll up to by going through `parentsRecursive`\n return reverseReactiveHint(rootType, entityType, subHint, undefined, false).map(appendPath(otherFieldName));\n } else if (p instanceof RecursiveM2mCollectionImpl) {\n // I.e. if this is `User.parentsRecursive`:\n // - fieldName=parentsRecursive (being watched by the caller)\n // - otherFieldName=childrenRecursive (our reversal for walking down)\n // - m2mFieldName=parents (the underlying m2m collection)\n //\n // For m2m recursive, the \"change signal\" is when join rows are added/removed, so we push\n // the m2m collection name into fields (like regular m2m handling). To find affected entities\n // that are watching `parentsRecursive`, we walk through `childrenRecursive` (the reverse direction).\n const { otherFieldName, m2mFieldName } = p;\n // When a join row changes (e.g. parents collection is mutated), notify both this entity\n // and its transitive children/parents via otherFieldName\n _fields.push(m2mFieldName);\n maybeRecursive.push({\n kind: isReadOnly ? \"read-only\" : \"update\",\n entity: entityType,\n fields: recursiveMembershipFields(meta, m2mFieldName),\n path: [otherFieldName],\n });\n // Any reactables who were watching this recursive collection, we'll reach via the other direction\n return reverseReactiveHint(rootType, entityType, subHint, undefined, false).map(appendPath(otherFieldName));\n } else {\n throw new Error(`Invalid hint in ${rootType.name}.ts ${JSON.stringify(hint)}`);\n }\n }\n}\n\nfunction maybeAddTypeFilterSuffix(\n meta: EntityMetadata,\n field: ManyToOneField | OneToManyField | ManyToManyField | OneToOneField | PolymorphicFieldComponent,\n): string {\n const otherField = field.otherMetadata().allFields[field.otherFieldName];\n if (!otherField) {\n // This is usually an error, unless if its ReactiveReference which we don't create o2m-s for\n const isReactiveReference = \"kind\" in field && field.kind === \"m2o\" && field.derived === \"async\";\n if (!isReactiveReference) fail(`No field ${field.otherMetadata().type}.${field.otherFieldName}`);\n return field.otherFieldName;\n }\n // If we're Foo, and the other field (which we'll traverse back from at runtime) is actually\n // a poly FK pointing back to multiple `owner=Foo | Bar | Zaz`, add a suffix of `@Foo` so\n // that any runtime traversal that hit `owner` and see a `Bar | Zaz` will stop.\n const nextFieldIsPoly = otherField.kind === \"poly\";\n // If we're a SubType, and the other field points to our base type, assume there might be\n // SubType-specific fields in the path so far, so tell the other side to only walk back through\n // us if its value is actually a SubType.\n const nextFieldPointsToBaseType =\n !!meta.baseType && otherField.kind !== \"poly\" && (otherField as any).otherMetadata().cstr !== meta.cstr;\n return nextFieldIsPoly || nextFieldPointsToBaseType ? `${field.otherFieldName}@${meta.type}` : field.otherFieldName;\n}\n\n/**\n * Walks `reverseHint` for every entity in `entities`.\n *\n * I.e. given `[book1, book2]` and `[\"author\", 'publisher\"]`, will return all the books' authors' publishers.\n *\n * Note that for references (and only references), we walk both through \"the current value\"\n * and prior values remembered in `referenceHistory`. This is fundamentally because references are the true \"owner\"\n * of the relation, while collections are derived.\n *\n * For example, with reversals walking through collections:\n *\n * - Given a hint `book: { author: firstName }`\n * - Which reverses to `author[firstName] -> books`\n * - When we walk through `a1.books`, we use only the current value, which might have only `[b2]` in it.\n * - We don't need to worry about \"the old a1.books\" value, which might have previously had `[b1]` in it,\n * because the `b1.author` changing authors (from `a1` to `a2`) will trigger its own reactivity.\n *\n * Contrast with reversals walking through references:\n *\n * - Given a hint `author: { books: \"title\" }`\n * - Which reverses to `book[title] -> author`\n * - When we traverse through `b1.author`, we use both the current value and remembered values, so that both\n * \"our prior authors\" and \"our new author\" see their latest `author.books` collection values.\n *\n * Delete cleanup runs before reverse hints are walked, so the current relation value may already be unset by the\n * time we get here. For persisted entities, `cleanupOnEntityDeleted` clears the owning FK via `setField`, which\n * remembers the prior FK in `referenceHistory`; this lets us still find the old owner. For collection reversals\n * this is enough because collections are derived from the owning FK: removing `b1` from `a1.books` is represented\n * by `b1.author` changing, not by an independent \"old books\" snapshot.\n *\n * References can also have transient values during a unit of work, i.e. a ReactiveField might observe `b1.author=a2`\n * before the final flush changes it to `a3`. `setField` remembers both original and transient reference values\n * separately from `originalData`, and we walk them in addition to the current value.\n */\nexport async function followReverseHint(\n reactionName: string,\n entities: Entity[],\n reverseHint: string[],\n): Promise<Entity[]> {\n // Start at the current (unique) entities\n let current = new Set(entities);\n const paths = [...reverseHint];\n // And \"walk backwards\" through the reverse hint\n while (paths.length) {\n const path = paths.shift()!;\n const [fieldName, viaType] = path.split(\"@\");\n const promises = new Array(current.size);\n // The path might touch either a reference or a collection\n for (const c of current as Set<any>) {\n const relation = c[fieldName];\n // If `!relation`, I'd really like to `fail`, but atm we're knowingly creating invalid reversals for when\n // a ReactiveField depends on a ReactiveReference; b/c RRs lack an \"other side\", we cannot walk them in\n // reverse, and so for awhile we \"supported\" the RF -> RR pattern by relying on a totally-read-only reactive\n // hint -> dropped during reversal.\n //\n // However, now IsLoadedCache means that even these \"totally read only\" RFs need to be reactive at least\n // during their initial EM creation, to avoid stale values being observed during first-flush mutations,\n // and so we're now stuck with these \"invalid RR reversals\" until we can figure out a better way to handle them.\n // (Now that IsLoadedCache doesn't technically walk paths, it just invalidates given entity+field pairs, so\n // it is not bothered by an invalid/missing RR reversal.)\n if (!relation) continue;\n // fail(`Attempting to react for ${reactionName} for ${c.toString()}, but there is no \"reverse walkable\" field ${c.constructor.name}.${fieldName}`,);\n const currentValuePromise = maybeApplyTypeFilter(relation.load(), viaType);\n // Always wait for the relation itself\n promises.push(currentValuePromise);\n // If we're going from Book.author back to Author to re-validate the Author.books collection,\n // see if Book.author has changed, so we can re-validate both the old author's books and the\n // new author's books.\n const fieldKind = getMetadata(c).allFields[fieldName]?.kind;\n const isReference = fieldKind === \"m2o\" || fieldKind === \"poly\";\n const isManyToMany = fieldKind === \"m2m\";\n // See jsdoc comment about why this is only necessary for references...\n if (isReference) {\n for (const value of getInstanceData(c).getReferenceHistory(fieldName)) {\n promises.push(maybeApplyTypeFilter(loadReferenceHistoryValue(c, value), viaType));\n }\n }\n if (isManyToMany) {\n const m2m = c[fieldName] as ManyToManyCollection<any, any>;\n const joinRows = getEmInternalApi(m2m.entity.em).joinRows(m2m);\n // Return a tuple of [currentRows, removedRows]\n promises.push(joinRows.removedFor(m2m, c));\n }\n // Walk up the old parents\n if (relation instanceof RecursiveParentsCollectionImpl) {\n const { m2oFieldName } = relation;\n for (const value of getInstanceData(c).getReferenceHistory(m2oFieldName)) {\n promises.push(\n // First load the old parent itself\n loadReferenceHistoryValue(c, value).then((oldParent) => {\n if (!oldParent) return undefined;\n // And then resolve its fieldName=parentsRecursive\n const oldParentRecursiveParents = (oldParent as any)[fieldName];\n return oldParentRecursiveParents.load().then((parents: any) => {\n return [oldParent, ...parents];\n });\n }),\n );\n }\n }\n }\n const nextLevel = await Promise.all(promises);\n // Make a new set so that we dedupe as we go\n current = flatAndUnique(nextLevel);\n }\n return [...current];\n}\n\n/** Converts a normalized reactive `hint` into a load hint. */\nexport function convertToLoadHint<T extends Entity>(\n meta: EntityMetadata,\n hint: ReactiveHint<T>,\n allowCustomKeys = false,\n): LoadHint<T> {\n const loadHint = {};\n // Process the hints individually instead of just calling Object.fromEntries so that\n // we can handle inlined reactive hints that overlap.\n for (const [keyMaybeSuffix, subHint] of Object.entries(normalizeHint(hint))) {\n let key = keyMaybeSuffix.replace(suffixRe, \"\");\n let field = meta.allFields[key];\n\n // If `allowCustomKeys` is enabled, we're probably doing `toJSON`,\n // so look for `companyId` & `companyIds` to turn into `company`\n if (!field && allowCustomKeys) {\n const realField = Object.values(meta.allFields).find((f) => f.fieldIdName === key);\n if (realField) {\n field = realField;\n key = realField.fieldName;\n }\n }\n\n if (field) {\n switch (field.kind) {\n case \"m2m\":\n case \"m2o\":\n case \"o2m\":\n case \"o2o\":\n mergeNormalizedHints(loadHint, { [key]: convertToLoadHint(field.otherMetadata(), subHint, allowCustomKeys) });\n break;\n case \"poly\":\n for (const comp of field.components) {\n // Write `{ parent: { child@Type: hint } }` into the load hint\n mergeNormalizedHints(loadHint, {\n [key]: Object.fromEntries(\n Object.entries(convertToLoadHint(comp.otherMetadata(), subHint, allowCustomKeys)).map(\n // Map the subHint keys to add in `@Type`\n ([subKey, subHint]) => [`${subKey}@${comp.otherMetadata().type}`, subHint],\n ),\n ),\n });\n }\n break;\n case \"primitive\":\n case \"enum\":\n if (field.derived === \"async\") {\n mergeNormalizedHints(loadHint, { [key]: {} });\n }\n continue;\n case \"m2mEnum\":\n // The enum codes are a leaf, but the collection itself must be loaded for the reaction to read it.\n mergeNormalizedHints(loadHint, { [key]: {} });\n break;\n case \"primaryKey\":\n continue;\n default:\n throw new Error(`Invalid reactive hint ${meta.tableName} ${JSON.stringify(hint)}`);\n }\n } else {\n const p = getProperties(meta)[key];\n if (p instanceof RecursiveParentsCollectionImpl) {\n mergeNormalizedHints(loadHint, { [p.fieldName]: convertToLoadHint(meta, subHint, allowCustomKeys) });\n } else if (p instanceof RecursiveChildrenCollectionImpl) {\n mergeNormalizedHints(loadHint, { [p.fieldName]: convertToLoadHint(meta, subHint, allowCustomKeys) });\n } else if (p instanceof RecursiveM2mCollectionImpl) {\n mergeNormalizedHints(loadHint, { [p.fieldName]: convertToLoadHint(meta, subHint, allowCustomKeys) });\n } else if (p && p.reactiveHint) {\n mergeNormalizedHints(loadHint, convertToLoadHint(meta, p.reactiveHint, allowCustomKeys));\n } else if (!allowCustomKeys) {\n fail(`Invalid reactive hint on ${meta.tableName} ${JSON.stringify(hint)}`);\n }\n }\n }\n return loadHint as any;\n}\n\n/** An entity that, when `fields` change, should trigger the reactive rule/field pointed to by `path`. */\nexport interface ReactiveTarget {\n kind: \"read-only\" | \"update\";\n /** The entity that contains a field our reactive rule/field accesses. */\n entity: MaybeAbstractEntityConstructor<any>;\n /** The field(s) that our reactive rule/field accesses, plus any implicit fields like FKs. */\n fields: string[];\n /** The path from this `entity` back to the source reactive rule/field. */\n path: string[];\n}\n\nfunction maybeApplyTypeFilter(loadPromise: Promise<Entity | Entity[] | undefined>, viaType: string | undefined) {\n if (viaType) {\n return loadPromise.then((loaded) => {\n if (Array.isArray(loaded)) {\n return loaded.filter((e) => isTypeOrSubType(e, viaType));\n } else if (loaded && isTypeOrSubType(loaded, viaType)) {\n return loaded;\n } else {\n return undefined;\n }\n });\n }\n return loadPromise;\n}\n\n/** Loads a remembered reference value so reverse-hint walking can revisit it. */\nfunction loadReferenceHistoryValue(entity: Entity, value: any): Promise<Entity | undefined> {\n if (isEntity(value)) return Promise.resolve(value);\n if (isId(value)) return entity.em.load(value) as Promise<Entity>;\n return Promise.resolve(undefined);\n}\n\n/** Returns true if `entity` is exactly `typeName` or a subtype of `typeName`.\n *\n * Used to honor poly/type filters like `@Publisher` during reverse-hint walks:\n * a `SmallPublisher` entity should pass an `@Publisher` filter because it is\n * a Publisher via inheritance.\n */\nexport function isTypeOrSubType(entity: Entity, typeName: string): boolean {\n const meta = getMetadata(entity);\n return meta.type === typeName || meta.baseTypes.some((b) => b.type === typeName);\n}\n\nexport function isPolyHint(key: string): boolean {\n return key.includes(\"@\");\n}\n\nexport function splitPolyHintToKeyAndType(key: string): [string, string] {\n const [k, type] = key.split(\"@\");\n return [k, type];\n}\n\n/** If this hint is a poly like `author@Book`, return the type, otherwise return undefined. */\nexport function getRelationFromMaybePolyKey(entity: Entity, key: string): any {\n if (isPolyHint(key)) {\n const [realKey, typeName] = splitPolyHintToKeyAndType(key);\n // Even though `entity[realKey]` might exist, if it's from a different poly type, we don't want it\n const isApplicable = getBaseAndSelfMetas(getMetadata(entity)).some((meta) => meta.type === typeName);\n if (!isApplicable) return undefined;\n return (entity as any)[realKey];\n } else {\n return (entity as any)[key];\n }\n}\n\n/**\n * For an `author -> books -> bookReviews` hint, appends `BookReview.book`, `Book.author`,\n * i.e. the \"other side\" relation, to the reversed path on the way \"out\" from a mutated BookReview\n * to the target `Author` RF/RR/rule.\n */\nfunction appendPath(reverseField: string): (rt: ReactiveTarget) => ReactiveTarget {\n return (rt) => {\n // We can mutate this array b/c it's only owned by a single/continually-updated `ReactiveTarget`\n rt.path = [...rt.path, reverseField];\n return rt;\n };\n}\n\n/** Recursive membership changes on soft deletion or resurrection, even when the underlying relation is unchanged. */\nfunction recursiveMembershipFields(meta: EntityMetadata, fieldName: string): string[] {\n const deletedAt = getBaseMeta(meta).timestampFields?.deletedAt;\n return deletedAt ? [fieldName, deletedAt] : [fieldName];\n}\n\nfunction isAllReadOnly(hint: any): boolean {\n for (const [key, subHint] of Object.entries(normalizeHint(hint))) {\n const okay = (key.endsWith(\"_ro\") || key.endsWith(\":ro\")) && isAllReadOnly(subHint);\n if (!okay) return false;\n }\n return true;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "joist-core",
|
|
3
|
-
"version": "2.3.0-next.
|
|
3
|
+
"version": "2.3.0-next.60",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"build"
|
|
43
43
|
],
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"joist-utils": "2.3.0-next.
|
|
45
|
+
"joist-utils": "2.3.0-next.60"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"ansis": "^4.3.1",
|