joist-core 2.3.0-next.99 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/build/EntityManager.cjs +1 -1
  2. package/build/EntityManager.cjs.map +1 -1
  3. package/build/EntityManager.d.cts +9 -8
  4. package/build/EntityManager.d.cts.map +1 -1
  5. package/build/EntityManager.d.mts +9 -8
  6. package/build/EntityManager.d.mts.map +1 -1
  7. package/build/EntityManager.js +1 -1
  8. package/build/EntityManager.js.map +1 -1
  9. package/build/dataloaders/findCountDataLoader.cjs +2 -0
  10. package/build/dataloaders/findCountDataLoader.cjs.map +1 -1
  11. package/build/dataloaders/findCountDataLoader.d.cts.map +1 -1
  12. package/build/dataloaders/findCountDataLoader.d.mts.map +1 -1
  13. package/build/dataloaders/findCountDataLoader.js +2 -0
  14. package/build/dataloaders/findCountDataLoader.js.map +1 -1
  15. package/build/dataloaders/findDataLoader.cjs +1 -0
  16. package/build/dataloaders/findDataLoader.cjs.map +1 -1
  17. package/build/dataloaders/findDataLoader.d.cts.map +1 -1
  18. package/build/dataloaders/findDataLoader.d.mts.map +1 -1
  19. package/build/dataloaders/findDataLoader.js +2 -1
  20. package/build/dataloaders/findDataLoader.js.map +1 -1
  21. package/build/dataloaders/findIdsDataLoader.cjs +2 -0
  22. package/build/dataloaders/findIdsDataLoader.cjs.map +1 -1
  23. package/build/dataloaders/findIdsDataLoader.d.cts.map +1 -1
  24. package/build/dataloaders/findIdsDataLoader.d.mts.map +1 -1
  25. package/build/dataloaders/findIdsDataLoader.js +2 -0
  26. package/build/dataloaders/findIdsDataLoader.js.map +1 -1
  27. package/build/dataloaders/findPaginatedDataLoader.cjs +2 -0
  28. package/build/dataloaders/findPaginatedDataLoader.cjs.map +1 -1
  29. package/build/dataloaders/findPaginatedDataLoader.d.cts.map +1 -1
  30. package/build/dataloaders/findPaginatedDataLoader.d.mts.map +1 -1
  31. package/build/dataloaders/findPaginatedDataLoader.js +2 -0
  32. package/build/dataloaders/findPaginatedDataLoader.js.map +1 -1
  33. package/build/index.cjs +6 -3
  34. package/build/index.cjs.map +1 -1
  35. package/build/index.d.cts +6 -4
  36. package/build/index.d.cts.map +1 -1
  37. package/build/index.d.mts +6 -4
  38. package/build/index.d.mts.map +1 -1
  39. package/build/index.js +6 -5
  40. package/build/index.js.map +1 -1
  41. package/build/keys.cjs +6 -1
  42. package/build/keys.cjs.map +1 -1
  43. package/build/keys.d.cts +3 -1
  44. package/build/keys.d.cts.map +1 -1
  45. package/build/keys.d.mts +3 -1
  46. package/build/keys.d.mts.map +1 -1
  47. package/build/keys.js +6 -2
  48. package/build/keys.js.map +1 -1
  49. package/build/queries/find/QueryParser.cjs +1 -2
  50. package/build/queries/find/QueryParser.cjs.map +1 -1
  51. package/build/queries/find/QueryParser.d.cts.map +1 -1
  52. package/build/queries/find/QueryParser.d.mts.map +1 -1
  53. package/build/queries/find/QueryParser.js +2 -3
  54. package/build/queries/find/QueryParser.js.map +1 -1
  55. package/build/queries/find/QueryVisitor.cjs +17 -0
  56. package/build/queries/find/QueryVisitor.cjs.map +1 -1
  57. package/build/queries/find/QueryVisitor.d.cts +3 -1
  58. package/build/queries/find/QueryVisitor.d.cts.map +1 -1
  59. package/build/queries/find/QueryVisitor.d.mts +3 -1
  60. package/build/queries/find/QueryVisitor.d.mts.map +1 -1
  61. package/build/queries/find/QueryVisitor.js +17 -1
  62. package/build/queries/find/QueryVisitor.js.map +1 -1
  63. package/build/queries/sql/Expr.cjs +18 -1
  64. package/build/queries/sql/Expr.cjs.map +1 -1
  65. package/build/queries/sql/Expr.d.cts +13 -3
  66. package/build/queries/sql/Expr.d.cts.map +1 -1
  67. package/build/queries/sql/Expr.d.mts +13 -3
  68. package/build/queries/sql/Expr.d.mts.map +1 -1
  69. package/build/queries/sql/Expr.js +18 -2
  70. package/build/queries/sql/Expr.js.map +1 -1
  71. package/build/queries/sql/JoinTree.cjs +72 -0
  72. package/build/queries/sql/JoinTree.cjs.map +1 -0
  73. package/build/queries/sql/JoinTree.d.cts +107 -0
  74. package/build/queries/sql/JoinTree.d.cts.map +1 -0
  75. package/build/queries/sql/JoinTree.d.mts +107 -0
  76. package/build/queries/sql/JoinTree.d.mts.map +1 -0
  77. package/build/queries/sql/JoinTree.js +71 -0
  78. package/build/queries/sql/JoinTree.js.map +1 -0
  79. package/build/queries/sql/Tables.cjs +103 -6
  80. package/build/queries/sql/Tables.cjs.map +1 -1
  81. package/build/queries/sql/Tables.d.cts +72 -13
  82. package/build/queries/sql/Tables.d.cts.map +1 -1
  83. package/build/queries/sql/Tables.d.mts +72 -13
  84. package/build/queries/sql/Tables.d.mts.map +1 -1
  85. package/build/queries/sql/Tables.js +101 -10
  86. package/build/queries/sql/Tables.js.map +1 -1
  87. package/build/queries/sql/custom.cjs +52 -0
  88. package/build/queries/sql/custom.cjs.map +1 -0
  89. package/build/queries/sql/custom.d.cts +85 -0
  90. package/build/queries/sql/custom.d.cts.map +1 -0
  91. package/build/queries/sql/custom.d.mts +85 -0
  92. package/build/queries/sql/custom.d.mts.map +1 -0
  93. package/build/queries/sql/custom.js +49 -0
  94. package/build/queries/sql/custom.js.map +1 -0
  95. package/build/queries/sql/execute.cjs +68 -28
  96. package/build/queries/sql/execute.cjs.map +1 -1
  97. package/build/queries/sql/execute.d.cts +110 -11
  98. package/build/queries/sql/execute.d.cts.map +1 -1
  99. package/build/queries/sql/execute.d.mts +110 -11
  100. package/build/queries/sql/execute.d.mts.map +1 -1
  101. package/build/queries/sql/execute.js +69 -29
  102. package/build/queries/sql/execute.js.map +1 -1
  103. package/build/queries/sql/expressions/types.d.cts +5 -5
  104. package/build/queries/sql/expressions/types.d.cts.map +1 -1
  105. package/build/queries/sql/expressions/types.d.mts +5 -5
  106. package/build/queries/sql/expressions/types.d.mts.map +1 -1
  107. package/build/queries/sql/keywords.cjs +1 -1
  108. package/build/queries/sql/keywords.cjs.map +1 -1
  109. package/build/queries/sql/keywords.d.cts +1 -1
  110. package/build/queries/sql/keywords.d.mts +1 -1
  111. package/build/queries/sql/keywords.js +1 -1
  112. package/build/queries/sql/keywords.js.map +1 -1
  113. package/build/queries/sql/query.cjs +100 -38
  114. package/build/queries/sql/query.cjs.map +1 -1
  115. package/build/queries/sql/query.d.cts +96 -46
  116. package/build/queries/sql/query.d.cts.map +1 -1
  117. package/build/queries/sql/query.d.mts +96 -46
  118. package/build/queries/sql/query.d.mts.map +1 -1
  119. package/build/queries/sql/query.js +102 -40
  120. package/build/queries/sql/query.js.map +1 -1
  121. package/build/queries/valueFilters.cjs +28 -36
  122. package/build/queries/valueFilters.cjs.map +1 -1
  123. package/build/queries/valueFilters.d.cts +2 -4
  124. package/build/queries/valueFilters.d.cts.map +1 -1
  125. package/build/queries/valueFilters.d.mts +2 -4
  126. package/build/queries/valueFilters.d.mts.map +1 -1
  127. package/build/queries/valueFilters.js +29 -36
  128. package/build/queries/valueFilters.js.map +1 -1
  129. package/build/serde/serde.cjs.map +1 -1
  130. package/build/serde/serde.js.map +1 -1
  131. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { getInstanceData } from \"src/BaseEntity.ts\";\nimport { getDefaultDependencies } from \"src/defaults.ts\";\nimport { type Entity } from \"src/Entity.ts\";\nimport { type EntityConstructor, type MaybeAbstractEntityConstructor } from \"src/EntityManager.ts\";\nimport { type EntityMetadata, getBaseMeta, getMetadata } from \"src/EntityMetadata.ts\";\nimport { getField, setField } from \"src/fields.ts\";\nimport { getProperties } from \"src/getProperties.ts\";\nimport type { New } from \"src/loading/loadHints.ts\";\nimport { isAllSqlPaths } from \"src/loading/loadLens.ts\";\nimport { FactoryInitialValue } from \"src/newTestInstance.ts\";\nimport { partitionHint } from \"src/preloading/partitionHint.ts\";\nimport { buildWhereClause } from \"src/queries/renderConditions.ts\";\nimport { AbstractRelationImpl } from \"src/relations/AbstractRelationImpl.ts\";\nimport { AsyncReactiveFieldImpl } from \"src/relations/AsyncReactiveField.ts\";\nimport {\n isAsyncProperty,\n isAsyncReactiveField,\n isLazyField,\n isProperty,\n isReactiveField,\n isReactiveGetter,\n} from \"src/relations/index.ts\";\nimport { ReactiveFieldImpl } from \"src/relations/ReactiveField.ts\";\nimport { type OptsOf } from \"src/typeMap.ts\";\nimport { fail } from \"src/utils.ts\";\n\nexport const testing = { isAllSqlPaths, getDefaultDependencies, partitionHint };\nexport const internals = { buildWhereClause };\nexport { newPgConnectionConfig } from \"joist-utils\";\nexport { AliasAssigner } from \"src/queries/sql/AliasAssigner.ts\";\nexport {\n type AndCondition,\n type ConditionGroup,\n type DomainPredicate,\n type OrCondition,\n type PredicateBrand,\n type SqlCondition,\n type SqlPredicate,\n} from \"src/queries/conditions.ts\";\n// Domain aliases belong to em.find; physical table expressions belong to em.query/em.execute.\nexport {\n alias,\n aliases,\n getAliasMetadata,\n getAliasMgmt,\n getMaybeCtiAlias,\n isAlias,\n newAliasProxy,\n type Alias,\n type AliasBrand,\n type AliasMgmt,\n type AliasColumn,\n type EntityAlias,\n type PolyAlias,\n type PrimitiveAlias,\n} from \"src/queries/find/Aliases.ts\";\nexport {\n table,\n tables,\n tableMgmt,\n getTableMetadata,\n getTableMgmt,\n isTable,\n newTableProxy,\n type Table,\n type TableFilter,\n type TableBrand,\n type TableFor,\n type TableMgmt,\n type ReferenceJoin,\n type PrimitiveColumn,\n type EntityColumn,\n type ReferenceColumn,\n type CollectionJoin,\n type PolyReference,\n} from \"src/queries/sql/Tables.ts\";\nexport { BaseEntity, getInstanceData } from \"src/BaseEntity.ts\";\nexport { ConditionBuilder } from \"src/queries/ConditionBuilder.ts\";\nexport { type Entity, type IdType, isEntity } from \"src/Entity.ts\";\nexport type * from \"src/EntityFields.ts\";\nexport * from \"src/queries/find/EntityFilter.ts\";\nexport * from \"src/queries/find/EntityGraphQLFilter.ts\";\nexport * from \"src/EntityManager.ts\";\nexport * from \"src/EntityMetadata.ts\";\nexport type {\n DeleteStatement,\n ExecuteResult,\n InsertStatement,\n InsertValues,\n MutationReturning,\n MutationStatement,\n UpdateStatement,\n UpdateValues,\n} from \"src/queries/sql/execute.ts\";\nexport type { EnumMetadata } from \"src/EnumMetadata.ts\";\n// `em.query`'s expression surface. Only the user-facing types are re-exported: the runtime half\n// (BaseExpr, asNode, deferredCondition, the FnExpr/TemplateExpr node classes) stays internal to\n// joist-core, so `toSql`/`decode`/`encode` never show up as something a user could call.\nexport {\n type ArrayAggOptions,\n type Expr,\n type ExprBrand,\n exprBrand,\n type ExprLike,\n type InnerJoin,\n type LeftJoin,\n} from \"src/queries/sql/Expr.ts\";\nexport {\n expr,\n type CaseElse,\n type CaseWhen,\n type ExprFromInput,\n type ExprInput,\n} from \"src/queries/sql/expressions/expression.ts\";\nexport { skipCondition } from \"src/queries/skipCondition.ts\";\nexport type { EntityOrId, HintNode } from \"src/loading/HintTree.ts\";\nexport { InstanceData } from \"src/InstanceData.ts\";\nexport { type JoinColumnValue, type JoinRow, JoinRowOperation, type ManyToManyLike } from \"src/flush/JoinRows.ts\";\nexport type * from \"src/flush/PendingChanges.ts\";\nexport { Plugin } from \"src/PluginManager.ts\";\nexport * from \"src/queries/find/QueryParser.ts\";\nexport * from \"src/queries/find/buildFindQuery.ts\";\nexport * from \"src/queries/renderConditions.ts\";\nexport type {\n ColumnCondition,\n ExistsCondition,\n ParsedExpressionCondition,\n ParsedExpressionFilter,\n ParsedValueFilter,\n RawCondition,\n} from \"src/queries/parsedConditions.ts\";\nexport {\n type ParsedEntityFilter,\n makeLike,\n mapToDb,\n parseEntityFilter,\n parseValueFilter,\n} from \"src/queries/valueFilters.ts\";\nexport {\n filterSoftDeletes,\n lazyExcludedSelects,\n maybeAddNotSoftDeleted,\n stiSubtypeFilter,\n} from \"src/queries/entityQueryUtils.ts\";\nexport * from \"src/queries/find/QueryParser.collectionJoins.ts\";\nexport { visitConditions } from \"src/queries/find/QueryVisitor.ts\";\nexport * from \"src/RowData.ts\";\nexport { type JoinRowTodo, Todo } from \"src/flush/Todo.ts\";\nexport * from \"src/changes.ts\";\nexport { ConfigApi, type EntityHook, resetBootFlag } from \"src/config.ts\";\nexport {\n configureMetadata,\n getConstructorFromTaggedId,\n getMetadataForTable,\n getMetadataForType,\n maybeGetConstructorFromReference,\n} from \"src/configure.ts\";\nexport { driverApi } from \"src/drivers/driverApi.ts\";\nexport * from \"src/drivers/index.ts\";\nexport { getField, isChangeableField, isFieldSet, setField } from \"src/fields.ts\";\nexport * from \"src/getProperties.ts\";\nexport * from \"src/json.ts\";\nexport * from \"src/keys.ts\";\nexport { kq, kqDot, kqStar } from \"src/queries/sql/keywords.ts\";\nexport {\n assertLoaded,\n type DeepNew,\n ensureLoaded,\n isLoaded,\n isNew,\n type Loadable,\n type Loaded,\n type LoadHint,\n type MarkLoaded,\n maybePopulateThen,\n type NestedLoadHint,\n type New,\n type RelationsIn,\n unsafeLoaded,\n} from \"src/loading/loadHints.ts\";\nexport * from \"src/loading/loadLens.ts\";\nexport { setFactoryWriter } from \"src/logging/FactoryLogger.ts\";\nexport * from \"src/logging/FieldLogger.ts\";\nexport { ReactionLogger, setReactionLogging } from \"src/logging/ReactionLogger.ts\";\nexport { lazyField } from \"src/newEntity.ts\";\nexport {\n defaultValue,\n factories,\n type FactoryEntityOpt,\n type FactoryOpts,\n getTestIndex,\n isFactoryCreation,\n maybeBranchValue,\n maybeNew,\n maybeNewPoly,\n newTestInstance,\n noValue,\n setFactoryLogging,\n testIndex,\n} from \"src/newTestInstance.ts\";\nexport { deepNormalizeHint, normalizeHint } from \"src/normalizeHints.ts\";\nexport { ImmutableEntitiesPlugin } from \"src/plugins/ImmutableEntitiesPlugin.ts\";\nexport type { JoinResult, PreloadHydrator, PreloadPlugin } from \"src/plugins/PreloadPlugin.ts\";\nexport { JsonAggregatePreloader } from \"src/preloading/JsonAggregatePreloader.ts\";\n// `em.query`'s query surface; the parse pipeline (SubqueryHandle, parseUserQuery, Plan) stays internal\nexport {\n type CheckScope,\n type Clauses,\n type EntityQuery,\n type ExpressionOrderBy,\n type ExistsQuery,\n entityQueryBrand,\n type MaybeNull,\n type NameOf,\n type NotWidened,\n type OrderByDirection,\n type OrderByKeys,\n type Query,\n type QueryArg,\n type QueryCondition,\n type QueryJoin,\n type QueryJoins,\n type QueryRow,\n type QuerySelect,\n type QuerySource,\n type QueryValue,\n recursiveQuery,\n type RecursiveOptions,\n type ScalarQuery,\n type SetQuery,\n query,\n queryMaybe,\n sql,\n type Subquery,\n type SubqueryBrand,\n subqueryBrand,\n type WithInput,\n type WithSource,\n} from \"src/queries/sql/query.ts\";\nexport {\n convertToLoadHint,\n isTypeOrSubType,\n type Reactable,\n type Reacted,\n type ReactiveHint,\n type ReactiveTarget,\n reverseReactiveHint,\n} from \"src/reactivity/reactiveHints.ts\";\nexport * from \"src/relations/index.ts\";\nexport {\n cannotBeChanged,\n cannotBeUpdated,\n type GenericError,\n maxValueRule,\n minValueRule,\n mustBeSubType,\n newRequiredLazyFieldRule,\n newRequiredRule,\n rangeValueRule,\n ValidationCode,\n type ValidationError,\n ValidationErrors,\n type ValidationRule,\n type ValidationRuleInternal,\n type ValidationRuleResult,\n} from \"src/rules.ts\";\nexport { getRuntimeConfig, setRuntimeConfig, type RuntimeConfig } from \"src/runtimeConfig.ts\";\nexport { nowUTC } from \"src/nowUTC.ts\";\nexport * from \"src/serde/serde.ts\";\nexport * from \"src/serde/columns.ts\";\nexport * from \"src/serde/fieldSerde.ts\";\nexport * from \"src/queries/find/scopes.ts\";\nexport { maybeRequireTemporal, requireTemporal, Temporal } from \"src/serde/temporal.ts\";\nexport * from \"src/serde/temporalMappers.ts\";\nexport { isInTrustedContext, runInTrustedContext } from \"src/trusted.ts\";\nexport type * from \"src/typeMap.ts\";\nexport { buildUnnestCte, ensureRectangularArraySizes } from \"src/queries/unnest.ts\";\nexport { type DeepPartialOrNull, updatePartial, upsert } from \"src/upsert.ts\";\nexport {\n abbreviation,\n asNew,\n assertNever,\n cleanSql,\n cleanStringValue,\n fail,\n failIfAnyRejected,\n indexBy,\n partition,\n zeroTo,\n} from \"src/utils.ts\";\nexport { ensureWithLoaded, StubbedRelation, type WithLoaded, withLoaded } from \"src/loading/withLoaded.ts\";\n\n// https://spin.atomicobject.com/2018/01/15/typescript-flexible-nominal-typing/\ninterface Flavoring<FlavorT> {\n _type?: FlavorT;\n}\n\nexport type Flavor<T, FlavorT> = T & Flavoring<FlavorT>;\n\n/**\n * Sets each value in `values` on the current entity.\n *\n * The default behavior is that passing a value as either `null` or `undefined` will set\n * the field as `undefined`, i.e. automatic `null` to `undefined` conversion.\n *\n * However, if you pass `ignoreUndefined: true`, then any opt that is `undefined` will be treated\n * as \"do not set\", and `null` will still mean \"set to `undefined`\". This is useful for implementing\n * APIs were an input of `undefined` means \"do not set / noop\" and `null` means \"unset\".\n *\n * Note that constructors _always_ call this method, but if the call is coming from `em.hydrate`, we\n * use `values` being a primary key to short-circuit and let hydration callers assign the values\n * returned by the serde `fromRow` methods.\n */\nexport function setOpts<T extends Entity>(\n entity: T,\n values: Partial<OptsOf<T>> | undefined,\n opts?: { partial?: boolean; calledFromConstructor?: boolean },\n): void {\n const { calledFromConstructor = false, partial } = opts || {};\n // If `values` is undefined, we're being called by `createPartial` that will do its\n // own opt handling, but we still want the sync defaults applied after this opts handling.\n if (values !== undefined) {\n const meta = getMetadata(entity);\n for (const [key, _value] of Object.entries(values as {})) {\n setOpt(meta, entity, key, _value, partial, calledFromConstructor);\n }\n }\n}\n\n/**\n * Applies some standard behavior & protections to `entity[key] = value`. I.e.\n *\n * - We don't set over AsyncProperties/relations/etc., and instead call current.set(value)\n * - We catch missing/invalid field names\n * - We handle FactoryInitialValues\n */\nexport function setOpt<T extends Entity>(\n meta: EntityMetadata<T>,\n entity: T,\n key: string,\n _value: any,\n partial = false,\n calledFromConstructor = false,\n): void {\n const field = meta.allFields[key];\n if (!field) {\n // Allow setting non-field properties like fullName setters\n const prop = getProperties(meta)[key];\n if (!prop) {\n throw new Error(`Unknown field ${key}`);\n }\n }\n\n // If partial is set, we treat undefined as a noop\n if (partial && _value === undefined) return;\n // Ignore the STI discriminator, em.register will set this accordingly\n if (meta.inheritanceType === \"sti\" && getBaseMeta(meta).stiDiscriminatorField === key) return;\n\n // We let optional opts fields be `| null` for convenience, and convert to undefined.\n const value = _value === null ? undefined : _value;\n\n // Use `getField` to side-step `id` blowing up on new entities that are setting an\n // explicit id; otherwise use `entity[key]` to get back the relation.\n const current = key === \"id\" ? getField(entity, key) : (entity as any)[key];\n\n if (current instanceof AbstractRelationImpl) {\n if (calledFromConstructor) {\n current.setFromOpts(value);\n } else {\n current.set(value);\n }\n } else if (isLazyField(current)) {\n current.set(value);\n } else if (isProperty(current) || isAsyncProperty(current) || isReactiveGetter(current)) {\n throw new Error(`Invalid argument, cannot set over ${key} ${current.constructor.name}`);\n } else if (isReactiveField(current) || isAsyncReactiveField(current)) {\n if (value instanceof FactoryInitialValue) {\n if (current instanceof ReactiveFieldImpl) {\n current.setFactoryValue(value.value);\n } else if (current instanceof AsyncReactiveFieldImpl) {\n current.setFactoryValue(value.value);\n } else {\n throw new Error(`Unhandled case ${current.constructor.name}`);\n }\n } else {\n throw new Error(`Invalid argument, cannot set over ${key} ${current.constructor.name}`);\n }\n } else {\n // If setting an explicit id, go through setField, otherwise use\n // `entity[key]` to set the value directly to that we go through setters.\n if (key === \"id\" && entity.isNewEntity) {\n setField(entity, key, value);\n } else {\n (entity as any)[key] = value;\n }\n }\n}\n\nexport function ensureNotDeleted(entity: Entity, ignore?: \"pending\"): void {\n if (entity.isDeletedEntity && (ignore === undefined || getInstanceData(entity).isDeletedAndFlushed)) {\n fail(`${entity} is marked as deleted`);\n }\n}\n\n/** Adds `null` to every key in `T` to accept partial-update-style input. */\nexport type PartialOrNull<T> = {\n [P in keyof T]?: T[P] | null;\n};\n\nexport function getRequiredKeys<T extends Entity>(entity: T): string[];\nexport function getRequiredKeys<T extends Entity>(type: EntityConstructor<T>): string[];\nexport function getRequiredKeys<T extends Entity>(entityOrType: T | EntityConstructor<T>): string[] {\n return Object.values(getMetadata(entityOrType as any).fields)\n .filter((f) => f.required)\n .map((f) => f.fieldName);\n}\n\nexport function getRelations(entity: Entity): AbstractRelationImpl<any, any>[] {\n return Object.entries(getProperties(getMetadata(entity)))\n .filter(([, v]) => v instanceof AbstractRelationImpl)\n .map(([name]) => (entity as any)[name]);\n}\n\nexport function getRelationEntries(entity: Entity): [string, AbstractRelationImpl<any, any>][] {\n return Object.entries(getProperties(getMetadata(entity)))\n .filter(([, v]) => v instanceof AbstractRelationImpl)\n .map(([name]) => [name, (entity as any)[name]]);\n}\n\n/** Casts a \"maybe abstract\" cstr to a concrete cstr when the calling code knows it's safe. */\nexport function asConcreteCstr<T extends Entity>(cstr: MaybeAbstractEntityConstructor<T>): EntityConstructor<T> {\n return cstr as any;\n}\n\n/**\n * Thrown when `.id` is accessed on an entity that does not have an id yet.\n *\n * For Postgres, entities are actually allowed to have ids pre-INSERT, if you call\n * `em.assignNewIds()`. Other databases typically require INSERTs to trigger the auto\n * id assignment.\n */\nexport class NoIdError extends Error {}\n\n/** Throws a `NoIdError` for `entity`, i.e. because `id` was called before being saved. */\nexport function failNoIdYet(entity: string): never {\n throw new NoIdError(`${entity} has no id yet`);\n}\n\n/**\n * Add a static function since getters can't have type guards.\n *\n * See https://github.com/microsoft/TypeScript/issues/43368\n */\nexport function isNewEntity<T extends Entity>(entity: T): entity is New<T> {\n return entity.isNewEntity;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { getInstanceData } from \"src/BaseEntity.ts\";\nimport { getDefaultDependencies } from \"src/defaults.ts\";\nimport { type Entity } from \"src/Entity.ts\";\nimport { type EntityConstructor, type MaybeAbstractEntityConstructor } from \"src/EntityManager.ts\";\nimport { type EntityMetadata, getBaseMeta, getMetadata } from \"src/EntityMetadata.ts\";\nimport { getField, setField } from \"src/fields.ts\";\nimport { getProperties } from \"src/getProperties.ts\";\nimport type { New } from \"src/loading/loadHints.ts\";\nimport { isAllSqlPaths } from \"src/loading/loadLens.ts\";\nimport { FactoryInitialValue } from \"src/newTestInstance.ts\";\nimport { partitionHint } from \"src/preloading/partitionHint.ts\";\nimport { buildWhereClause } from \"src/queries/renderConditions.ts\";\nimport { AbstractRelationImpl } from \"src/relations/AbstractRelationImpl.ts\";\nimport { AsyncReactiveFieldImpl } from \"src/relations/AsyncReactiveField.ts\";\nimport {\n isAsyncProperty,\n isAsyncReactiveField,\n isLazyField,\n isProperty,\n isReactiveField,\n isReactiveGetter,\n} from \"src/relations/index.ts\";\nimport { ReactiveFieldImpl } from \"src/relations/ReactiveField.ts\";\nimport { type OptsOf } from \"src/typeMap.ts\";\nimport { fail } from \"src/utils.ts\";\n\nexport const testing = { isAllSqlPaths, getDefaultDependencies, partitionHint };\nexport const internals = { buildWhereClause };\nexport { newPgConnectionConfig } from \"joist-utils\";\nexport { AliasAssigner } from \"src/queries/sql/AliasAssigner.ts\";\nexport type { JoinTree } from \"src/queries/sql/JoinTree.ts\";\nexport {\n type AndCondition,\n type ConditionGroup,\n type DomainPredicate,\n type OrCondition,\n type PredicateBrand,\n type SqlCondition,\n type SqlPredicate,\n} from \"src/queries/conditions.ts\";\n// Domain aliases belong to em.find; physical table expressions belong to em.query/em.execute.\nexport {\n alias,\n aliases,\n getAliasMetadata,\n getAliasMgmt,\n getMaybeCtiAlias,\n isAlias,\n newAliasProxy,\n type Alias,\n type AliasBrand,\n type AliasMgmt,\n type AliasColumn,\n type EntityAlias,\n type PolyAlias,\n type PrimitiveAlias,\n} from \"src/queries/find/Aliases.ts\";\nexport {\n table,\n tables,\n tableMgmt,\n getTableMetadata,\n getTableMgmt,\n isTable,\n newTableProxy,\n type Table,\n type TableFilter,\n type TableBrand,\n type TableSourceBrand,\n type TableFor,\n type TableMgmt,\n type TableSourceMgmt,\n type ReferenceJoin,\n type PrimitiveColumn,\n type EntityColumn,\n type ReferenceColumn,\n type CollectionJoin,\n type PolyReference,\n} from \"src/queries/sql/Tables.ts\";\nexport {\n declareTable,\n type CustomColumnConfig,\n type CustomColumnInput,\n type CustomColumnInputs,\n type CustomColumnType,\n type CustomTable,\n type CustomTableDefinition,\n type CustomTableFor,\n} from \"src/queries/sql/custom.ts\";\nexport { BaseEntity, getInstanceData } from \"src/BaseEntity.ts\";\nexport { ConditionBuilder } from \"src/queries/ConditionBuilder.ts\";\nexport { type Entity, type IdType, isEntity } from \"src/Entity.ts\";\nexport type * from \"src/EntityFields.ts\";\nexport * from \"src/queries/find/EntityFilter.ts\";\nexport * from \"src/queries/find/EntityGraphQLFilter.ts\";\nexport * from \"src/EntityManager.ts\";\nexport * from \"src/EntityMetadata.ts\";\nexport type {\n DeleteStatement,\n ExecuteResult,\n InsertStatement,\n InsertValues,\n MutationReturning,\n MutationStatement,\n UpdateStatement,\n UpdateValues,\n} from \"src/queries/sql/execute.ts\";\nexport type { EnumMetadata } from \"src/EnumMetadata.ts\";\n// `em.query`'s expression surface. Only the user-facing types are re-exported: the runtime half\n// (BaseExpr, asNode, deferredCondition, the FnExpr/TemplateExpr node classes) stays internal to\n// joist-core, so `toSql`/`decode`/`encode` never show up as something a user could call.\nexport {\n type ArrayAggOptions,\n type Expr,\n type ExprBrand,\n exprBrand,\n type ExprLike,\n type InnerJoin,\n type LeftJoin,\n} from \"src/queries/sql/Expr.ts\";\nexport {\n expr,\n type CaseElse,\n type CaseWhen,\n type ExprFromInput,\n type ExprInput,\n} from \"src/queries/sql/expressions/expression.ts\";\nexport { skipCondition } from \"src/queries/skipCondition.ts\";\nexport type { EntityOrId, HintNode } from \"src/loading/HintTree.ts\";\nexport { InstanceData } from \"src/InstanceData.ts\";\nexport { type JoinColumnValue, type JoinRow, JoinRowOperation, type ManyToManyLike } from \"src/flush/JoinRows.ts\";\nexport type * from \"src/flush/PendingChanges.ts\";\nexport { Plugin } from \"src/PluginManager.ts\";\nexport * from \"src/queries/find/QueryParser.ts\";\nexport * from \"src/queries/find/buildFindQuery.ts\";\nexport * from \"src/queries/renderConditions.ts\";\nexport type {\n ColumnCondition,\n ExistsCondition,\n ParsedExpressionCondition,\n ParsedExpressionFilter,\n ParsedValueFilter,\n RawCondition,\n} from \"src/queries/parsedConditions.ts\";\nexport {\n type ParsedEntityFilter,\n makeLike,\n mapToDb,\n parseEntityFilter,\n parseValueFilter,\n} from \"src/queries/valueFilters.ts\";\nexport {\n filterSoftDeletes,\n lazyExcludedSelects,\n maybeAddNotSoftDeleted,\n stiSubtypeFilter,\n} from \"src/queries/entityQueryUtils.ts\";\nexport * from \"src/queries/find/QueryParser.collectionJoins.ts\";\nexport { visitConditions } from \"src/queries/find/QueryVisitor.ts\";\nexport * from \"src/RowData.ts\";\nexport { type JoinRowTodo, Todo } from \"src/flush/Todo.ts\";\nexport * from \"src/changes.ts\";\nexport { ConfigApi, type EntityHook, resetBootFlag } from \"src/config.ts\";\nexport {\n configureMetadata,\n getConstructorFromTaggedId,\n getMetadataForTable,\n getMetadataForType,\n maybeGetConstructorFromReference,\n} from \"src/configure.ts\";\nexport { driverApi } from \"src/drivers/driverApi.ts\";\nexport * from \"src/drivers/index.ts\";\nexport { getField, isChangeableField, isFieldSet, setField } from \"src/fields.ts\";\nexport * from \"src/getProperties.ts\";\nexport * from \"src/json.ts\";\nexport * from \"src/keys.ts\";\nexport { kq, kqDot, kqStar } from \"src/queries/sql/keywords.ts\";\nexport {\n assertLoaded,\n type DeepNew,\n ensureLoaded,\n isLoaded,\n isNew,\n type Loadable,\n type Loaded,\n type LoadHint,\n type MarkLoaded,\n maybePopulateThen,\n type NestedLoadHint,\n type New,\n type RelationsIn,\n unsafeLoaded,\n} from \"src/loading/loadHints.ts\";\nexport * from \"src/loading/loadLens.ts\";\nexport { setFactoryWriter } from \"src/logging/FactoryLogger.ts\";\nexport * from \"src/logging/FieldLogger.ts\";\nexport { ReactionLogger, setReactionLogging } from \"src/logging/ReactionLogger.ts\";\nexport { lazyField } from \"src/newEntity.ts\";\nexport {\n defaultValue,\n factories,\n type FactoryEntityOpt,\n type FactoryOpts,\n getTestIndex,\n isFactoryCreation,\n maybeBranchValue,\n maybeNew,\n maybeNewPoly,\n newTestInstance,\n noValue,\n setFactoryLogging,\n testIndex,\n} from \"src/newTestInstance.ts\";\nexport { deepNormalizeHint, normalizeHint } from \"src/normalizeHints.ts\";\nexport { ImmutableEntitiesPlugin } from \"src/plugins/ImmutableEntitiesPlugin.ts\";\nexport type { JoinResult, PreloadHydrator, PreloadPlugin } from \"src/plugins/PreloadPlugin.ts\";\nexport { JsonAggregatePreloader } from \"src/preloading/JsonAggregatePreloader.ts\";\n// `em.query`'s query surface; the parse pipeline (SubqueryHandle, parseUserQuery, Plan) stays internal\nexport {\n type CheckScope,\n type Clauses,\n type EntityQuery,\n type ExpressionOrderBy,\n type ExistsQuery,\n entityQueryBrand,\n type MaybeNull,\n type NameOf,\n type NotWidened,\n type OrderByDirection,\n type OrderByKeys,\n type Query,\n type QueryArg,\n type QueryCondition,\n type QueryJoin,\n type QueryJoinInput,\n type QueryJoinList,\n type QueryRow,\n type QuerySelect,\n type QuerySource,\n type QueryValue,\n recursiveQuery,\n type RecursiveOptions,\n type ResolvedJoins,\n type ScalarQuery,\n type SetQuery,\n query,\n queryMaybe,\n sql,\n type Subquery,\n type SubqueryBrand,\n subqueryBrand,\n type WithInput,\n type WithSource,\n} from \"src/queries/sql/query.ts\";\nexport {\n convertToLoadHint,\n isTypeOrSubType,\n type Reactable,\n type Reacted,\n type ReactiveHint,\n type ReactiveTarget,\n reverseReactiveHint,\n} from \"src/reactivity/reactiveHints.ts\";\nexport * from \"src/relations/index.ts\";\nexport {\n cannotBeChanged,\n cannotBeUpdated,\n type GenericError,\n maxValueRule,\n minValueRule,\n mustBeSubType,\n newRequiredLazyFieldRule,\n newRequiredRule,\n rangeValueRule,\n ValidationCode,\n type ValidationError,\n ValidationErrors,\n type ValidationRule,\n type ValidationRuleInternal,\n type ValidationRuleResult,\n} from \"src/rules.ts\";\nexport { getRuntimeConfig, setRuntimeConfig, type RuntimeConfig } from \"src/runtimeConfig.ts\";\nexport { nowUTC } from \"src/nowUTC.ts\";\nexport * from \"src/serde/serde.ts\";\nexport * from \"src/serde/columns.ts\";\nexport * from \"src/serde/fieldSerde.ts\";\nexport * from \"src/queries/find/scopes.ts\";\nexport { maybeRequireTemporal, requireTemporal, Temporal } from \"src/serde/temporal.ts\";\nexport * from \"src/serde/temporalMappers.ts\";\nexport { isInTrustedContext, runInTrustedContext } from \"src/trusted.ts\";\nexport type * from \"src/typeMap.ts\";\nexport { buildUnnestCte, ensureRectangularArraySizes } from \"src/queries/unnest.ts\";\nexport { type DeepPartialOrNull, updatePartial, upsert } from \"src/upsert.ts\";\nexport {\n abbreviation,\n asNew,\n assertNever,\n cleanSql,\n cleanStringValue,\n fail,\n failIfAnyRejected,\n indexBy,\n partition,\n zeroTo,\n} from \"src/utils.ts\";\nexport { ensureWithLoaded, StubbedRelation, type WithLoaded, withLoaded } from \"src/loading/withLoaded.ts\";\n\n// https://spin.atomicobject.com/2018/01/15/typescript-flexible-nominal-typing/\ninterface Flavoring<FlavorT> {\n _type?: FlavorT;\n}\n\nexport type Flavor<T, FlavorT> = T & Flavoring<FlavorT>;\n\n/**\n * Sets each value in `values` on the current entity.\n *\n * The default behavior is that passing a value as either `null` or `undefined` will set\n * the field as `undefined`, i.e. automatic `null` to `undefined` conversion.\n *\n * However, if you pass `ignoreUndefined: true`, then any opt that is `undefined` will be treated\n * as \"do not set\", and `null` will still mean \"set to `undefined`\". This is useful for implementing\n * APIs were an input of `undefined` means \"do not set / noop\" and `null` means \"unset\".\n *\n * Note that constructors _always_ call this method, but if the call is coming from `em.hydrate`, we\n * use `values` being a primary key to short-circuit and let hydration callers assign the values\n * returned by the serde `fromRow` methods.\n */\nexport function setOpts<T extends Entity>(\n entity: T,\n values: Partial<OptsOf<T>> | undefined,\n opts?: { partial?: boolean; calledFromConstructor?: boolean },\n): void {\n const { calledFromConstructor = false, partial } = opts || {};\n // If `values` is undefined, we're being called by `createPartial` that will do its\n // own opt handling, but we still want the sync defaults applied after this opts handling.\n if (values !== undefined) {\n const meta = getMetadata(entity);\n for (const [key, _value] of Object.entries(values as {})) {\n setOpt(meta, entity, key, _value, partial, calledFromConstructor);\n }\n }\n}\n\n/**\n * Applies some standard behavior & protections to `entity[key] = value`. I.e.\n *\n * - We don't set over AsyncProperties/relations/etc., and instead call current.set(value)\n * - We catch missing/invalid field names\n * - We handle FactoryInitialValues\n */\nexport function setOpt<T extends Entity>(\n meta: EntityMetadata<T>,\n entity: T,\n key: string,\n _value: any,\n partial = false,\n calledFromConstructor = false,\n): void {\n const field = meta.allFields[key];\n if (!field) {\n // Allow setting non-field properties like fullName setters\n const prop = getProperties(meta)[key];\n if (!prop) {\n throw new Error(`Unknown field ${key}`);\n }\n }\n\n // If partial is set, we treat undefined as a noop\n if (partial && _value === undefined) return;\n // Ignore the STI discriminator, em.register will set this accordingly\n if (meta.inheritanceType === \"sti\" && getBaseMeta(meta).stiDiscriminatorField === key) return;\n\n // We let optional opts fields be `| null` for convenience, and convert to undefined.\n const value = _value === null ? undefined : _value;\n\n // Use `getField` to side-step `id` blowing up on new entities that are setting an\n // explicit id; otherwise use `entity[key]` to get back the relation.\n const current = key === \"id\" ? getField(entity, key) : (entity as any)[key];\n\n if (current instanceof AbstractRelationImpl) {\n if (calledFromConstructor) {\n current.setFromOpts(value);\n } else {\n current.set(value);\n }\n } else if (isLazyField(current)) {\n current.set(value);\n } else if (isProperty(current) || isAsyncProperty(current) || isReactiveGetter(current)) {\n throw new Error(`Invalid argument, cannot set over ${key} ${current.constructor.name}`);\n } else if (isReactiveField(current) || isAsyncReactiveField(current)) {\n if (value instanceof FactoryInitialValue) {\n if (current instanceof ReactiveFieldImpl) {\n current.setFactoryValue(value.value);\n } else if (current instanceof AsyncReactiveFieldImpl) {\n current.setFactoryValue(value.value);\n } else {\n throw new Error(`Unhandled case ${current.constructor.name}`);\n }\n } else {\n throw new Error(`Invalid argument, cannot set over ${key} ${current.constructor.name}`);\n }\n } else {\n // If setting an explicit id, go through setField, otherwise use\n // `entity[key]` to set the value directly to that we go through setters.\n if (key === \"id\" && entity.isNewEntity) {\n setField(entity, key, value);\n } else {\n (entity as any)[key] = value;\n }\n }\n}\n\nexport function ensureNotDeleted(entity: Entity, ignore?: \"pending\"): void {\n if (entity.isDeletedEntity && (ignore === undefined || getInstanceData(entity).isDeletedAndFlushed)) {\n fail(`${entity} is marked as deleted`);\n }\n}\n\n/** Adds `null` to every key in `T` to accept partial-update-style input. */\nexport type PartialOrNull<T> = {\n [P in keyof T]?: T[P] | null;\n};\n\nexport function getRequiredKeys<T extends Entity>(entity: T): string[];\nexport function getRequiredKeys<T extends Entity>(type: EntityConstructor<T>): string[];\nexport function getRequiredKeys<T extends Entity>(entityOrType: T | EntityConstructor<T>): string[] {\n return Object.values(getMetadata(entityOrType as any).fields)\n .filter((f) => f.required)\n .map((f) => f.fieldName);\n}\n\nexport function getRelations(entity: Entity): AbstractRelationImpl<any, any>[] {\n return Object.entries(getProperties(getMetadata(entity)))\n .filter(([, v]) => v instanceof AbstractRelationImpl)\n .map(([name]) => (entity as any)[name]);\n}\n\nexport function getRelationEntries(entity: Entity): [string, AbstractRelationImpl<any, any>][] {\n return Object.entries(getProperties(getMetadata(entity)))\n .filter(([, v]) => v instanceof AbstractRelationImpl)\n .map(([name]) => [name, (entity as any)[name]]);\n}\n\n/** Casts a \"maybe abstract\" cstr to a concrete cstr when the calling code knows it's safe. */\nexport function asConcreteCstr<T extends Entity>(cstr: MaybeAbstractEntityConstructor<T>): EntityConstructor<T> {\n return cstr as any;\n}\n\n/**\n * Thrown when `.id` is accessed on an entity that does not have an id yet.\n *\n * For Postgres, entities are actually allowed to have ids pre-INSERT, if you call\n * `em.assignNewIds()`. Other databases typically require INSERTs to trigger the auto\n * id assignment.\n */\nexport class NoIdError extends Error {}\n\n/** Throws a `NoIdError` for `entity`, i.e. because `id` was called before being saved. */\nexport function failNoIdYet(entity: string): never {\n throw new NoIdError(`${entity} has no id yet`);\n}\n\n/**\n * Add a static function since getters can't have type guards.\n *\n * See https://github.com/microsoft/TypeScript/issues/43368\n */\nexport function isNewEntity<T extends Entity>(entity: T): entity is New<T> {\n return entity.isNewEntity;\n}\n"],"mappings":"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"}
package/build/keys.cjs CHANGED
@@ -47,7 +47,11 @@ function maybeNumberUnlessUuid(meta, key) {
47
47
  /** Converts `dbValue` (big int, int, uuid) to a tagged string, unless its undefined. */
48
48
  function keyToTaggedId(meta, dbValue) {
49
49
  if (dbValue === void 0 || dbValue === null) return void 0;
50
- return `${meta.tagName}${tagDelimiter ?? ""}${dbValue}`;
50
+ return `${taggedIdPrefix(meta)}${dbValue}`;
51
+ }
52
+ /** Returns the entity tag followed by the configured ID delimiter. */
53
+ function taggedIdPrefix(meta) {
54
+ return `${meta.tagName}${tagDelimiter ?? ""}`;
51
55
  }
52
56
  /** Fails if any keys are untagged; internal batch-loader keys must always be tagged. */
53
57
  function assertIdsAreTagged(keys) {
@@ -169,6 +173,7 @@ exports.setTaggedIdDelimiter = setTaggedIdDelimiter;
169
173
  exports.tagFromId = tagFromId;
170
174
  exports.tagId = tagId;
171
175
  exports.tagIds = tagIds;
176
+ exports.taggedIdPrefix = taggedIdPrefix;
172
177
  exports.toIdOf = toIdOf;
173
178
  exports.toTaggedId = toTaggedId;
174
179
  exports.unsafeDeTagIds = unsafeDeTagIds;
@@ -1 +1 @@
1
- {"version":3,"file":"keys.cjs","names":["assertNever","isEntity","BaseEntity","getMetadata","fail"],"sources":["../src/keys.ts"],"sourcesContent":["import { BaseEntity } from \"src/BaseEntity.ts\";\nimport { type Entity, type IdType, isEntity } from \"src/Entity.ts\";\nimport { type EntityConstructor, type IdOf, type TaggedId } from \"src/EntityManager.ts\";\nimport { type EntityMetadata, getMetadata } from \"src/EntityMetadata.ts\";\nimport { type Reference } from \"src/relations/index.ts\";\nimport { assertNever, fail } from \"src/utils.ts\";\n\nlet tagDelimiter: string | undefined = \":\";\n\n/** Sets the process-wide tagged-id format during metadata boot. */\nexport function setTaggedIdDelimiter(delimiter: string | undefined): void {\n tagDelimiter = delimiter;\n}\n\n// I'm not entirely sure this is still necessary, but use a small subset of EntityMetadata so\n// that this file doesn't have to import the type and potentially create import cycles.\ntype HasTagName = {\n tagName: string;\n /** The database column type, i.e. used to do `::type` casts in Postgres. */\n idDbType: \"bigint\" | \"int\" | \"uuid\" | \"text\";\n};\n\n/** Converts our internal/always-tagged `id` to the domain entity's `id` type (could be a number or uuid). */\nexport function toIdOf<T extends Entity>(meta: EntityMetadata<T>, id: TaggedId | undefined): IdOf<T> | undefined {\n if (!id) return undefined;\n switch (meta.idType) {\n case \"number\":\n return Number(deTagId(meta, id)) as IdOf<T>;\n case \"tagged-string\":\n return id as IdOf<T>;\n case \"untagged-string\":\n return deTagId(meta, id) as IdOf<T>;\n default:\n return assertNever(meta.idType);\n }\n}\n\n// Before a referred-to object is saved, we keep its instance in our data\n// map, and then assume it will be persisted before we're asked to persist\nexport function maybeResolveReferenceToId(\n value: Entity | Reference<any, any, any> | string | undefined,\n): TaggedId | undefined {\n return typeof value === \"string\" ? value : value?.idTaggedMaybe;\n}\n\n/** Converts `value` to a number, i.e. for string ids, unless its undefined. */\nexport function keyToNumber(meta: HasTagName, value: string | number): number;\nexport function keyToNumber(meta: HasTagName, value: string | number | undefined): number | undefined;\nexport function keyToNumber(meta: HasTagName, value: any): number | undefined {\n if (value === undefined || value === null) {\n return undefined;\n } else if (typeof value === \"number\") {\n return value;\n } else if (typeof value === \"string\") {\n if (tagDelimiter === undefined) {\n const id = value.startsWith(meta.tagName) ? value.slice(meta.tagName.length) : value;\n return maybeNumberUnlessUuid(meta, id);\n }\n const delimiterIndex = value.indexOf(tagDelimiter);\n if (delimiterIndex === -1) {\n return maybeNumberUnlessUuid(meta, value);\n }\n const tag = value.slice(0, delimiterIndex);\n if (tag !== meta.tagName) {\n throw new Error(`Invalid tagged id, expected tag ${meta.tagName}, got ${value}`);\n }\n return maybeNumberUnlessUuid(meta, value.slice(delimiterIndex + tagDelimiter.length));\n } else {\n throw new Error(`Invalid key ${value}`);\n }\n}\n\n// UUIDs, text, and bigints are strings at the database boundary, so lie to the type system and pretend they're numbers.\nfunction maybeNumberUnlessUuid(meta: HasTagName, key: string): number {\n switch (meta.idDbType) {\n case \"bigint\":\n case \"uuid\":\n case \"text\":\n return key as any;\n case \"int\":\n return Number(key);\n default:\n assertNever(meta.idDbType);\n }\n}\n\n/** Converts `dbValue` (big int, int, uuid) to a tagged string, unless its undefined. */\nexport function keyToTaggedId(meta: HasTagName, dbValue: string | number): TaggedId | undefined {\n if (dbValue === undefined || dbValue === null) return undefined;\n return `${meta.tagName}${tagDelimiter ?? \"\"}${dbValue}`;\n}\n\n/** Fails if any keys are untagged; internal batch-loader keys must always be tagged. */\nexport function assertIdsAreTagged(keys: readonly string[]): void {\n for (const key of keys) {\n const isTagged = tagDelimiter === undefined ? validSlugId.test(key) : key.indexOf(tagDelimiter) !== -1;\n if (!isTagged) throw new Error(`Key ${key} is missing a tag`);\n }\n}\n\nconst validSlugId = /^([a-z]+)(\\d+)$/i;\nconst uuidIshId = /[0-9a-z-]+/i;\n\n/** Returns whether `id` is tagged and a probably-correct value. */\nexport function isTaggedId(id: string | number): boolean;\n/** Returns whether `id` is tagged and the tag matches `meta`'s tag. */\nexport function isTaggedId(meta: EntityMetadata, id: string): boolean;\nexport function isTaggedId(metaOrId: string | number | HasTagName, id?: string): boolean {\n if (typeof metaOrId === \"number\") {\n // number overload\n return false;\n } else if (typeof metaOrId === \"string\") {\n // string overload\n return tagDelimiter === undefined ? validSlugId.test(metaOrId) : hasTagDelimiter(metaOrId);\n } else {\n // meta + string overload\n return isTaggedIdForMeta(metaOrId, id!);\n }\n}\n\nexport function assertIdIsTagged(id: string): void {\n if (!isTaggedId(id)) {\n throw new Error(`Key is not tagged ${id}`);\n }\n}\n\n/** We accept an entity in case this is m2o storing a not-yet-saved entity. */\nexport function toTaggedId(meta: HasTagName, id: IdType): TaggedId;\nexport function toTaggedId(meta: HasTagName, id: IdType | undefined): TaggedId | undefined;\nexport function toTaggedId(meta: HasTagName, id: IdType | undefined): TaggedId | undefined {\n if (typeof id === \"number\") {\n return `${meta.tagName}${tagDelimiter ?? \"\"}${id}`;\n } else if (typeof id === \"string\") {\n // This seems odd, but is covered by a unit test, so I guess we need it?\n if (id === \"\") return undefined;\n if (tagDelimiter === undefined) {\n const tag = validSlugId.exec(id)?.[1];\n if (tag && tag !== meta.tagName) {\n throw new Error(`Invalid tagged id, expected tag ${meta.tagName}, got ${id}`);\n }\n return tag ? id : `${meta.tagName}${id}`;\n } else {\n const i = id.indexOf(tagDelimiter);\n if (i === -1) return `${meta.tagName}${tagDelimiter}${id}`;\n const tag = id.slice(0, i);\n if (tag !== meta.tagName) {\n throw new Error(`Invalid tagged id, expected tag ${meta.tagName}, got ${id}`);\n }\n }\n }\n return id;\n}\n\n/** Similar to `toTaggedId`, but we accept an entity for handling relations to not-yet-saved entities. */\nexport function ensureTagged<T extends Entity>(\n meta: HasTagName,\n value: T | string | number | undefined,\n): T | TaggedId | undefined {\n return isEntity(value) ? value : toTaggedId(meta, value);\n}\n\n/** Tags a potentially untagged id, while our API inputs still accept either tagged or untagged ids. */\nexport function tagId(meta: HasTagName, id: string | number): string;\nexport function tagId(cstr: EntityConstructor<any>, id: string | number): string;\nexport function tagId(meta: HasTagName, id: string | number | null | undefined): string | undefined;\nexport function tagId(cstr: EntityConstructor<any>, id: string | number | null | undefined): string | undefined;\nexport function tagId(\n metaOrCstr: HasTagName | EntityConstructor<any>,\n id: string | number | null | undefined,\n): string | undefined {\n const meta = metadata(metaOrCstr);\n const tag = meta.tagName;\n if (typeof id === \"number\") {\n return `${tag}${tagDelimiter ?? \"\"}${id}`;\n }\n if (id === null || id === undefined) {\n return undefined;\n }\n if (tagDelimiter === undefined) {\n const existingTag = validSlugId.exec(id)?.[1];\n if (existingTag && existingTag !== tag) {\n throw new Error(`Invalid tagged id, expected tag ${tag}, got ${id}`);\n }\n return existingTag ? id : `${tag}${id}`;\n }\n // Avoid using includes/split, for a faster indexOf + length + startsWith\n const delimiterIndex = id.indexOf(tagDelimiter);\n if (delimiterIndex !== -1) {\n if (delimiterIndex !== tag.length || !id.startsWith(tag)) {\n throw new Error(`Invalid tagged id, expected tag ${tag}, got ${id}`);\n }\n return id;\n }\n return `${tag}${tagDelimiter}${id}`;\n}\n\n/** Adds the tag prefixes. */\nexport function tagIds(meta: HasTagName, keys: readonly (string | number)[]): readonly string[];\nexport function tagIds(cstr: EntityConstructor<any>, keys: readonly (string | number)[]): readonly string[];\nexport function tagIds(\n metaOrCstr: HasTagName | EntityConstructor<any>,\n keys: readonly (string | number)[],\n): readonly string[] {\n return keys.map((k) => tagId(metaOrCstr as any, k));\n}\n\nexport function deTagId(meta: HasTagName, id: string | number): string;\nexport function deTagId(meta: HasTagName, id: string | number | undefined): string | undefined;\nexport function deTagId(entity: Entity): string;\nexport function deTagId(entityOrMeta: Entity | HasTagName, id?: string | number): string | undefined {\n const meta = entityOrMeta instanceof BaseEntity ? getMetadata(entityOrMeta) : (entityOrMeta as HasTagName);\n id = id ?? (entityOrMeta as Entity).id;\n return id === undefined ? undefined : keyToNumber(meta, id).toString();\n}\n\n/** Removes the tag prefixes so we can use the keys for SQL operations. */\nexport function deTagIds(meta: HasTagName, keys: readonly string[]): readonly string[] {\n const deTagged = Array(keys.length);\n for (let i = 0; i < keys.length; i++) deTagged[i] = deTagId(meta, keys[i]);\n return deTagged;\n}\n\n/** Removes the tag prefixes so we can use the keys for SQL operations. */\nexport function unsafeDeTagIds(keys: readonly string[]): readonly string[] {\n const deTagged = Array(keys.length);\n for (let i = 0; i < keys.length; i++) {\n if (tagDelimiter === undefined) {\n deTagged[i] = validSlugId.exec(keys[i])?.[2];\n } else {\n const delimiterIndex = keys[i].indexOf(tagDelimiter);\n deTagged[i] = delimiterIndex === -1 ? undefined : keys[i].slice(delimiterIndex + tagDelimiter.length);\n }\n }\n return deTagged;\n}\n\n/** Given a tagged id, returns its tag. */\nexport function tagFromId(id: string): string {\n if (tagDelimiter === undefined) {\n return validSlugId.exec(id)?.[1] ?? fail(`Unknown tagged id format: \"${id}\"`);\n }\n const delimiterIndex = id.indexOf(tagDelimiter);\n return delimiterIndex > 0 && delimiterIndex + tagDelimiter.length < id.length\n ? id.slice(0, delimiterIndex)\n : fail(`Unknown tagged id format: \"${id}\"`);\n}\n\n/** Returns the metadata needed to format an id. */\nfunction metadata(metaOrCstr: HasTagName | EntityConstructor<any>): HasTagName {\n return typeof metaOrCstr === \"function\" ? getMetadata(metaOrCstr) : metaOrCstr;\n}\n\n/** Returns whether `id` has the tag and database id shape required by `meta`. */\nfunction isTaggedIdForMeta(meta: HasTagName, id: string): boolean {\n if (tagDelimiter === undefined) {\n return validSlugId.exec(id)?.[1] === meta.tagName;\n }\n const delimiterIndex = id.indexOf(tagDelimiter);\n if (delimiterIndex === -1) return false;\n const tag = id.slice(0, delimiterIndex);\n const untaggedId = id.slice(delimiterIndex + tagDelimiter.length);\n if (meta.tagName !== tag) return false;\n switch (meta.idDbType) {\n case \"int\":\n case \"bigint\":\n return !Number.isNaN(Number(untaggedId));\n case \"uuid\":\n return uuidIshId.test(untaggedId);\n case \"text\":\n return true;\n default:\n return assertNever(meta.idDbType);\n }\n}\n\n/** Returns whether `id` has a non-empty tag and value separated by the configured delimiter. */\nfunction hasTagDelimiter(id: string): boolean {\n if (tagDelimiter === undefined) return false;\n const delimiterIndex = id.indexOf(tagDelimiter);\n return delimiterIndex > 0 && delimiterIndex + tagDelimiter.length < id.length;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"keys.cjs","names":["assertNever","isEntity","BaseEntity","getMetadata","fail"],"sources":["../src/keys.ts"],"sourcesContent":["import { BaseEntity } from \"src/BaseEntity.ts\";\nimport { type Entity, type IdType, isEntity } from \"src/Entity.ts\";\nimport { type EntityConstructor, type IdOf, type TaggedId } from \"src/EntityManager.ts\";\nimport { type EntityMetadata, getMetadata } from \"src/EntityMetadata.ts\";\nimport { type Reference } from \"src/relations/index.ts\";\nimport { assertNever, fail } from \"src/utils.ts\";\n\nlet tagDelimiter: string | undefined = \":\";\n\n/** Sets the process-wide tagged-id format during metadata boot. */\nexport function setTaggedIdDelimiter(delimiter: string | undefined): void {\n tagDelimiter = delimiter;\n}\n\n// I'm not entirely sure this is still necessary, but use a small subset of EntityMetadata so\n// that this file doesn't have to import the type and potentially create import cycles.\ntype HasTagName = {\n tagName: string;\n /** The database column type, i.e. used to do `::type` casts in Postgres. */\n idDbType: \"bigint\" | \"int\" | \"uuid\" | \"text\";\n};\n\n/** Converts our internal/always-tagged `id` to the domain entity's `id` type (could be a number or uuid). */\nexport function toIdOf<T extends Entity>(meta: EntityMetadata<T>, id: TaggedId | undefined): IdOf<T> | undefined {\n if (!id) return undefined;\n switch (meta.idType) {\n case \"number\":\n return Number(deTagId(meta, id)) as IdOf<T>;\n case \"tagged-string\":\n return id as IdOf<T>;\n case \"untagged-string\":\n return deTagId(meta, id) as IdOf<T>;\n default:\n return assertNever(meta.idType);\n }\n}\n\n// Before a referred-to object is saved, we keep its instance in our data\n// map, and then assume it will be persisted before we're asked to persist\nexport function maybeResolveReferenceToId(\n value: Entity | Reference<any, any, any> | string | undefined,\n): TaggedId | undefined {\n return typeof value === \"string\" ? value : value?.idTaggedMaybe;\n}\n\n/** Converts `value` to a number, i.e. for string ids, unless its undefined. */\nexport function keyToNumber(meta: HasTagName, value: string | number): number;\nexport function keyToNumber(meta: HasTagName, value: string | number | undefined): number | undefined;\nexport function keyToNumber(meta: HasTagName, value: any): number | undefined {\n if (value === undefined || value === null) {\n return undefined;\n } else if (typeof value === \"number\") {\n return value;\n } else if (typeof value === \"string\") {\n if (tagDelimiter === undefined) {\n const id = value.startsWith(meta.tagName) ? value.slice(meta.tagName.length) : value;\n return maybeNumberUnlessUuid(meta, id);\n }\n const delimiterIndex = value.indexOf(tagDelimiter);\n if (delimiterIndex === -1) {\n return maybeNumberUnlessUuid(meta, value);\n }\n const tag = value.slice(0, delimiterIndex);\n if (tag !== meta.tagName) {\n throw new Error(`Invalid tagged id, expected tag ${meta.tagName}, got ${value}`);\n }\n return maybeNumberUnlessUuid(meta, value.slice(delimiterIndex + tagDelimiter.length));\n } else {\n throw new Error(`Invalid key ${value}`);\n }\n}\n\n// UUIDs, text, and bigints are strings at the database boundary, so lie to the type system and pretend they're numbers.\nfunction maybeNumberUnlessUuid(meta: HasTagName, key: string): number {\n switch (meta.idDbType) {\n case \"bigint\":\n case \"uuid\":\n case \"text\":\n return key as any;\n case \"int\":\n return Number(key);\n default:\n assertNever(meta.idDbType);\n }\n}\n\n/** Converts `dbValue` (big int, int, uuid) to a tagged string, unless its undefined. */\nexport function keyToTaggedId(meta: HasTagName, dbValue: string | number): TaggedId | undefined {\n if (dbValue === undefined || dbValue === null) return undefined;\n return `${taggedIdPrefix(meta)}${dbValue}`;\n}\n\n/** Returns the entity tag followed by the configured ID delimiter. */\nexport function taggedIdPrefix(meta: Pick<HasTagName, \"tagName\">): string {\n return `${meta.tagName}${tagDelimiter ?? \"\"}`;\n}\n\n/** Fails if any keys are untagged; internal batch-loader keys must always be tagged. */\nexport function assertIdsAreTagged(keys: readonly string[]): void {\n for (const key of keys) {\n const isTagged = tagDelimiter === undefined ? validSlugId.test(key) : key.indexOf(tagDelimiter) !== -1;\n if (!isTagged) throw new Error(`Key ${key} is missing a tag`);\n }\n}\n\nconst validSlugId = /^([a-z]+)(\\d+)$/i;\nconst uuidIshId = /[0-9a-z-]+/i;\n\n/** Returns whether `id` is tagged and a probably-correct value. */\nexport function isTaggedId(id: string | number): boolean;\n/** Returns whether `id` is tagged and the tag matches `meta`'s tag. */\nexport function isTaggedId(meta: EntityMetadata, id: string): boolean;\nexport function isTaggedId(metaOrId: string | number | HasTagName, id?: string): boolean {\n if (typeof metaOrId === \"number\") {\n // number overload\n return false;\n } else if (typeof metaOrId === \"string\") {\n // string overload\n return tagDelimiter === undefined ? validSlugId.test(metaOrId) : hasTagDelimiter(metaOrId);\n } else {\n // meta + string overload\n return isTaggedIdForMeta(metaOrId, id!);\n }\n}\n\nexport function assertIdIsTagged(id: string): void {\n if (!isTaggedId(id)) {\n throw new Error(`Key is not tagged ${id}`);\n }\n}\n\n/** We accept an entity in case this is m2o storing a not-yet-saved entity. */\nexport function toTaggedId(meta: HasTagName, id: IdType): TaggedId;\nexport function toTaggedId(meta: HasTagName, id: IdType | undefined): TaggedId | undefined;\nexport function toTaggedId(meta: HasTagName, id: IdType | undefined): TaggedId | undefined {\n if (typeof id === \"number\") {\n return `${meta.tagName}${tagDelimiter ?? \"\"}${id}`;\n } else if (typeof id === \"string\") {\n // This seems odd, but is covered by a unit test, so I guess we need it?\n if (id === \"\") return undefined;\n if (tagDelimiter === undefined) {\n const tag = validSlugId.exec(id)?.[1];\n if (tag && tag !== meta.tagName) {\n throw new Error(`Invalid tagged id, expected tag ${meta.tagName}, got ${id}`);\n }\n return tag ? id : `${meta.tagName}${id}`;\n } else {\n const i = id.indexOf(tagDelimiter);\n if (i === -1) return `${meta.tagName}${tagDelimiter}${id}`;\n const tag = id.slice(0, i);\n if (tag !== meta.tagName) {\n throw new Error(`Invalid tagged id, expected tag ${meta.tagName}, got ${id}`);\n }\n }\n }\n return id;\n}\n\n/** Similar to `toTaggedId`, but we accept an entity for handling relations to not-yet-saved entities. */\nexport function ensureTagged<T extends Entity>(\n meta: HasTagName,\n value: T | string | number | undefined,\n): T | TaggedId | undefined {\n return isEntity(value) ? value : toTaggedId(meta, value);\n}\n\n/** Tags a potentially untagged id, while our API inputs still accept either tagged or untagged ids. */\nexport function tagId(meta: HasTagName, id: string | number): string;\nexport function tagId(cstr: EntityConstructor<any>, id: string | number): string;\nexport function tagId(meta: HasTagName, id: string | number | null | undefined): string | undefined;\nexport function tagId(cstr: EntityConstructor<any>, id: string | number | null | undefined): string | undefined;\nexport function tagId(\n metaOrCstr: HasTagName | EntityConstructor<any>,\n id: string | number | null | undefined,\n): string | undefined {\n const meta = metadata(metaOrCstr);\n const tag = meta.tagName;\n if (typeof id === \"number\") {\n return `${tag}${tagDelimiter ?? \"\"}${id}`;\n }\n if (id === null || id === undefined) {\n return undefined;\n }\n if (tagDelimiter === undefined) {\n const existingTag = validSlugId.exec(id)?.[1];\n if (existingTag && existingTag !== tag) {\n throw new Error(`Invalid tagged id, expected tag ${tag}, got ${id}`);\n }\n return existingTag ? id : `${tag}${id}`;\n }\n // Avoid using includes/split, for a faster indexOf + length + startsWith\n const delimiterIndex = id.indexOf(tagDelimiter);\n if (delimiterIndex !== -1) {\n if (delimiterIndex !== tag.length || !id.startsWith(tag)) {\n throw new Error(`Invalid tagged id, expected tag ${tag}, got ${id}`);\n }\n return id;\n }\n return `${tag}${tagDelimiter}${id}`;\n}\n\n/** Adds the tag prefixes. */\nexport function tagIds(meta: HasTagName, keys: readonly (string | number)[]): readonly string[];\nexport function tagIds(cstr: EntityConstructor<any>, keys: readonly (string | number)[]): readonly string[];\nexport function tagIds(\n metaOrCstr: HasTagName | EntityConstructor<any>,\n keys: readonly (string | number)[],\n): readonly string[] {\n return keys.map((k) => tagId(metaOrCstr as any, k));\n}\n\nexport function deTagId(meta: HasTagName, id: string | number): string;\nexport function deTagId(meta: HasTagName, id: string | number | undefined): string | undefined;\nexport function deTagId(entity: Entity): string;\nexport function deTagId(entityOrMeta: Entity | HasTagName, id?: string | number): string | undefined {\n const meta = entityOrMeta instanceof BaseEntity ? getMetadata(entityOrMeta) : (entityOrMeta as HasTagName);\n id = id ?? (entityOrMeta as Entity).id;\n return id === undefined ? undefined : keyToNumber(meta, id).toString();\n}\n\n/** Removes the tag prefixes so we can use the keys for SQL operations. */\nexport function deTagIds(meta: HasTagName, keys: readonly string[]): readonly string[] {\n const deTagged = Array(keys.length);\n for (let i = 0; i < keys.length; i++) deTagged[i] = deTagId(meta, keys[i]);\n return deTagged;\n}\n\n/** Removes the tag prefixes so we can use the keys for SQL operations. */\nexport function unsafeDeTagIds(keys: readonly string[]): readonly string[] {\n const deTagged = Array(keys.length);\n for (let i = 0; i < keys.length; i++) {\n if (tagDelimiter === undefined) {\n deTagged[i] = validSlugId.exec(keys[i])?.[2];\n } else {\n const delimiterIndex = keys[i].indexOf(tagDelimiter);\n deTagged[i] = delimiterIndex === -1 ? undefined : keys[i].slice(delimiterIndex + tagDelimiter.length);\n }\n }\n return deTagged;\n}\n\n/** Given a tagged id, returns its tag. */\nexport function tagFromId(id: string): string {\n if (tagDelimiter === undefined) {\n return validSlugId.exec(id)?.[1] ?? fail(`Unknown tagged id format: \"${id}\"`);\n }\n const delimiterIndex = id.indexOf(tagDelimiter);\n return delimiterIndex > 0 && delimiterIndex + tagDelimiter.length < id.length\n ? id.slice(0, delimiterIndex)\n : fail(`Unknown tagged id format: \"${id}\"`);\n}\n\n/** Returns the metadata needed to format an id. */\nfunction metadata(metaOrCstr: HasTagName | EntityConstructor<any>): HasTagName {\n return typeof metaOrCstr === \"function\" ? getMetadata(metaOrCstr) : metaOrCstr;\n}\n\n/** Returns whether `id` has the tag and database id shape required by `meta`. */\nfunction isTaggedIdForMeta(meta: HasTagName, id: string): boolean {\n if (tagDelimiter === undefined) {\n return validSlugId.exec(id)?.[1] === meta.tagName;\n }\n const delimiterIndex = id.indexOf(tagDelimiter);\n if (delimiterIndex === -1) return false;\n const tag = id.slice(0, delimiterIndex);\n const untaggedId = id.slice(delimiterIndex + tagDelimiter.length);\n if (meta.tagName !== tag) return false;\n switch (meta.idDbType) {\n case \"int\":\n case \"bigint\":\n return !Number.isNaN(Number(untaggedId));\n case \"uuid\":\n return uuidIshId.test(untaggedId);\n case \"text\":\n return true;\n default:\n return assertNever(meta.idDbType);\n }\n}\n\n/** Returns whether `id` has a non-empty tag and value separated by the configured delimiter. */\nfunction hasTagDelimiter(id: string): boolean {\n if (tagDelimiter === undefined) return false;\n const delimiterIndex = id.indexOf(tagDelimiter);\n return delimiterIndex > 0 && delimiterIndex + tagDelimiter.length < id.length;\n}\n"],"mappings":"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"}
package/build/keys.d.cts CHANGED
@@ -19,6 +19,8 @@ declare function keyToNumber(meta: HasTagName, value: string | number): number;
19
19
  declare function keyToNumber(meta: HasTagName, value: string | number | undefined): number | undefined;
20
20
  /** Converts `dbValue` (big int, int, uuid) to a tagged string, unless its undefined. */
21
21
  declare function keyToTaggedId(meta: HasTagName, dbValue: string | number): TaggedId | undefined;
22
+ /** Returns the entity tag followed by the configured ID delimiter. */
23
+ declare function taggedIdPrefix(meta: Pick<HasTagName, "tagName">): string;
22
24
  /** Fails if any keys are untagged; internal batch-loader keys must always be tagged. */
23
25
  declare function assertIdsAreTagged(keys: readonly string[]): void;
24
26
  /** Returns whether `id` is tagged and a probably-correct value. */
@@ -49,5 +51,5 @@ declare function unsafeDeTagIds(keys: readonly string[]): readonly string[];
49
51
  /** Given a tagged id, returns its tag. */
50
52
  declare function tagFromId(id: string): string;
51
53
  //#endregion
52
- export { assertIdIsTagged, assertIdsAreTagged, deTagId, deTagIds, ensureTagged, isTaggedId, keyToNumber, keyToTaggedId, maybeResolveReferenceToId, setTaggedIdDelimiter, tagFromId, tagId, tagIds, toIdOf, toTaggedId, unsafeDeTagIds };
54
+ export { assertIdIsTagged, assertIdsAreTagged, deTagId, deTagIds, ensureTagged, isTaggedId, keyToNumber, keyToTaggedId, maybeResolveReferenceToId, setTaggedIdDelimiter, tagFromId, tagId, tagIds, taggedIdPrefix, toIdOf, toTaggedId, unsafeDeTagIds };
53
55
  //# sourceMappingURL=keys.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"keys.d.cts","names":[],"sources":["../src/keys.ts"],"mappings":";;;;;;;iBAUgB,qBAAqB;KAMhC;EACH;;EAEA;;;iBAIc,OAAO,UAAU,QAAQ,MAAM,eAAe,IAAI,IAAI,uBAAuB,KAAK;iBAgBlF,0BACd,OAAO,SAAS,gDACf;;iBAKa,YAAY,MAAM,YAAY;iBAC9B,YAAY,MAAM,YAAY;;iBAwC9B,cAAc,MAAM,YAAY,2BAA2B;;iBAM3D,mBAAmB;;iBAWnB,WAAW;;iBAEX,WAAW,MAAM,gBAAgB;iBAcjC,iBAAiB;;iBAOjB,WAAW,MAAM,YAAY,IAAI,SAAS;iBAC1C,WAAW,MAAM,YAAY,IAAI,qBAAqB;;iBA0BtD,aAAa,UAAU,QACrC,MAAM,YACN,OAAO,kCACN,IAAI;;iBAKS,MAAM,MAAM,YAAY;iBACxB,MAAM,MAAM,wBAAwB;iBACpC,MAAM,MAAM,YAAY;iBACxB,MAAM,MAAM,wBAAwB;;iBAgCpC,OAAO,MAAM,YAAY;iBACzB,OAAO,MAAM,wBAAwB;iBAQrC,QAAQ,MAAM,YAAY;iBAC1B,QAAQ,MAAM,YAAY;iBAC1B,QAAQ,QAAQ;;iBAQhB,SAAS,MAAM,YAAY;;iBAO3B,eAAe;;iBAcf,UAAU"}
1
+ {"version":3,"file":"keys.d.cts","names":[],"sources":["../src/keys.ts"],"mappings":";;;;;;;iBAUgB,qBAAqB;KAMhC;EACH;;EAEA;;;iBAIc,OAAO,UAAU,QAAQ,MAAM,eAAe,IAAI,IAAI,uBAAuB,KAAK;iBAgBlF,0BACd,OAAO,SAAS,gDACf;;iBAKa,YAAY,MAAM,YAAY;iBAC9B,YAAY,MAAM,YAAY;;iBAwC9B,cAAc,MAAM,YAAY,2BAA2B;;iBAM3D,eAAe,MAAM,KAAK;;iBAK1B,mBAAmB;;iBAWnB,WAAW;;iBAEX,WAAW,MAAM,gBAAgB;iBAcjC,iBAAiB;;iBAOjB,WAAW,MAAM,YAAY,IAAI,SAAS;iBAC1C,WAAW,MAAM,YAAY,IAAI,qBAAqB;;iBA0BtD,aAAa,UAAU,QACrC,MAAM,YACN,OAAO,kCACN,IAAI;;iBAKS,MAAM,MAAM,YAAY;iBACxB,MAAM,MAAM,wBAAwB;iBACpC,MAAM,MAAM,YAAY;iBACxB,MAAM,MAAM,wBAAwB;;iBAgCpC,OAAO,MAAM,YAAY;iBACzB,OAAO,MAAM,wBAAwB;iBAQrC,QAAQ,MAAM,YAAY;iBAC1B,QAAQ,MAAM,YAAY;iBAC1B,QAAQ,QAAQ;;iBAQhB,SAAS,MAAM,YAAY;;iBAO3B,eAAe;;iBAcf,UAAU"}
package/build/keys.d.mts CHANGED
@@ -19,6 +19,8 @@ declare function keyToNumber(meta: HasTagName, value: string | number): number;
19
19
  declare function keyToNumber(meta: HasTagName, value: string | number | undefined): number | undefined;
20
20
  /** Converts `dbValue` (big int, int, uuid) to a tagged string, unless its undefined. */
21
21
  declare function keyToTaggedId(meta: HasTagName, dbValue: string | number): TaggedId | undefined;
22
+ /** Returns the entity tag followed by the configured ID delimiter. */
23
+ declare function taggedIdPrefix(meta: Pick<HasTagName, "tagName">): string;
22
24
  /** Fails if any keys are untagged; internal batch-loader keys must always be tagged. */
23
25
  declare function assertIdsAreTagged(keys: readonly string[]): void;
24
26
  /** Returns whether `id` is tagged and a probably-correct value. */
@@ -49,5 +51,5 @@ declare function unsafeDeTagIds(keys: readonly string[]): readonly string[];
49
51
  /** Given a tagged id, returns its tag. */
50
52
  declare function tagFromId(id: string): string;
51
53
  //#endregion
52
- export { assertIdIsTagged, assertIdsAreTagged, deTagId, deTagIds, ensureTagged, isTaggedId, keyToNumber, keyToTaggedId, maybeResolveReferenceToId, setTaggedIdDelimiter, tagFromId, tagId, tagIds, toIdOf, toTaggedId, unsafeDeTagIds };
54
+ export { assertIdIsTagged, assertIdsAreTagged, deTagId, deTagIds, ensureTagged, isTaggedId, keyToNumber, keyToTaggedId, maybeResolveReferenceToId, setTaggedIdDelimiter, tagFromId, tagId, tagIds, taggedIdPrefix, toIdOf, toTaggedId, unsafeDeTagIds };
53
55
  //# sourceMappingURL=keys.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"keys.d.mts","names":[],"sources":["../src/keys.ts"],"mappings":";;;;;;;iBAUgB,qBAAqB;KAMhC;EACH;;EAEA;;;iBAIc,OAAO,UAAU,QAAQ,MAAM,eAAe,IAAI,IAAI,uBAAuB,KAAK;iBAgBlF,0BACd,OAAO,SAAS,gDACf;;iBAKa,YAAY,MAAM,YAAY;iBAC9B,YAAY,MAAM,YAAY;;iBAwC9B,cAAc,MAAM,YAAY,2BAA2B;;iBAM3D,mBAAmB;;iBAWnB,WAAW;;iBAEX,WAAW,MAAM,gBAAgB;iBAcjC,iBAAiB;;iBAOjB,WAAW,MAAM,YAAY,IAAI,SAAS;iBAC1C,WAAW,MAAM,YAAY,IAAI,qBAAqB;;iBA0BtD,aAAa,UAAU,QACrC,MAAM,YACN,OAAO,kCACN,IAAI;;iBAKS,MAAM,MAAM,YAAY;iBACxB,MAAM,MAAM,wBAAwB;iBACpC,MAAM,MAAM,YAAY;iBACxB,MAAM,MAAM,wBAAwB;;iBAgCpC,OAAO,MAAM,YAAY;iBACzB,OAAO,MAAM,wBAAwB;iBAQrC,QAAQ,MAAM,YAAY;iBAC1B,QAAQ,MAAM,YAAY;iBAC1B,QAAQ,QAAQ;;iBAQhB,SAAS,MAAM,YAAY;;iBAO3B,eAAe;;iBAcf,UAAU"}
1
+ {"version":3,"file":"keys.d.mts","names":[],"sources":["../src/keys.ts"],"mappings":";;;;;;;iBAUgB,qBAAqB;KAMhC;EACH;;EAEA;;;iBAIc,OAAO,UAAU,QAAQ,MAAM,eAAe,IAAI,IAAI,uBAAuB,KAAK;iBAgBlF,0BACd,OAAO,SAAS,gDACf;;iBAKa,YAAY,MAAM,YAAY;iBAC9B,YAAY,MAAM,YAAY;;iBAwC9B,cAAc,MAAM,YAAY,2BAA2B;;iBAM3D,eAAe,MAAM,KAAK;;iBAK1B,mBAAmB;;iBAWnB,WAAW;;iBAEX,WAAW,MAAM,gBAAgB;iBAcjC,iBAAiB;;iBAOjB,WAAW,MAAM,YAAY,IAAI,SAAS;iBAC1C,WAAW,MAAM,YAAY,IAAI,qBAAqB;;iBA0BtD,aAAa,UAAU,QACrC,MAAM,YACN,OAAO,kCACN,IAAI;;iBAKS,MAAM,MAAM,YAAY;iBACxB,MAAM,MAAM,wBAAwB;iBACpC,MAAM,MAAM,YAAY;iBACxB,MAAM,MAAM,wBAAwB;;iBAgCpC,OAAO,MAAM,YAAY;iBACzB,OAAO,MAAM,wBAAwB;iBAQrC,QAAQ,MAAM,YAAY;iBAC1B,QAAQ,MAAM,YAAY;iBAC1B,QAAQ,QAAQ;;iBAQhB,SAAS,MAAM,YAAY;;iBAO3B,eAAe;;iBAcf,UAAU"}
package/build/keys.js CHANGED
@@ -46,7 +46,11 @@ function maybeNumberUnlessUuid(meta, key) {
46
46
  /** Converts `dbValue` (big int, int, uuid) to a tagged string, unless its undefined. */
47
47
  function keyToTaggedId(meta, dbValue) {
48
48
  if (dbValue === void 0 || dbValue === null) return void 0;
49
- return `${meta.tagName}${tagDelimiter ?? ""}${dbValue}`;
49
+ return `${taggedIdPrefix(meta)}${dbValue}`;
50
+ }
51
+ /** Returns the entity tag followed by the configured ID delimiter. */
52
+ function taggedIdPrefix(meta) {
53
+ return `${meta.tagName}${tagDelimiter ?? ""}`;
50
54
  }
51
55
  /** Fails if any keys are untagged; internal batch-loader keys must always be tagged. */
52
56
  function assertIdsAreTagged(keys) {
@@ -155,6 +159,6 @@ function hasTagDelimiter(id) {
155
159
  return delimiterIndex > 0 && delimiterIndex + tagDelimiter.length < id.length;
156
160
  }
157
161
  //#endregion
158
- export { assertIdIsTagged, assertIdsAreTagged, deTagId, deTagIds, ensureTagged, isTaggedId, keyToNumber, keyToTaggedId, maybeResolveReferenceToId, setTaggedIdDelimiter, tagFromId, tagId, tagIds, toIdOf, toTaggedId, unsafeDeTagIds };
162
+ export { assertIdIsTagged, assertIdsAreTagged, deTagId, deTagIds, ensureTagged, isTaggedId, keyToNumber, keyToTaggedId, maybeResolveReferenceToId, setTaggedIdDelimiter, tagFromId, tagId, tagIds, taggedIdPrefix, toIdOf, toTaggedId, unsafeDeTagIds };
159
163
 
160
164
  //# sourceMappingURL=keys.js.map
package/build/keys.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"keys.js","names":[],"sources":["../src/keys.ts"],"sourcesContent":["import { BaseEntity } from \"src/BaseEntity.ts\";\nimport { type Entity, type IdType, isEntity } from \"src/Entity.ts\";\nimport { type EntityConstructor, type IdOf, type TaggedId } from \"src/EntityManager.ts\";\nimport { type EntityMetadata, getMetadata } from \"src/EntityMetadata.ts\";\nimport { type Reference } from \"src/relations/index.ts\";\nimport { assertNever, fail } from \"src/utils.ts\";\n\nlet tagDelimiter: string | undefined = \":\";\n\n/** Sets the process-wide tagged-id format during metadata boot. */\nexport function setTaggedIdDelimiter(delimiter: string | undefined): void {\n tagDelimiter = delimiter;\n}\n\n// I'm not entirely sure this is still necessary, but use a small subset of EntityMetadata so\n// that this file doesn't have to import the type and potentially create import cycles.\ntype HasTagName = {\n tagName: string;\n /** The database column type, i.e. used to do `::type` casts in Postgres. */\n idDbType: \"bigint\" | \"int\" | \"uuid\" | \"text\";\n};\n\n/** Converts our internal/always-tagged `id` to the domain entity's `id` type (could be a number or uuid). */\nexport function toIdOf<T extends Entity>(meta: EntityMetadata<T>, id: TaggedId | undefined): IdOf<T> | undefined {\n if (!id) return undefined;\n switch (meta.idType) {\n case \"number\":\n return Number(deTagId(meta, id)) as IdOf<T>;\n case \"tagged-string\":\n return id as IdOf<T>;\n case \"untagged-string\":\n return deTagId(meta, id) as IdOf<T>;\n default:\n return assertNever(meta.idType);\n }\n}\n\n// Before a referred-to object is saved, we keep its instance in our data\n// map, and then assume it will be persisted before we're asked to persist\nexport function maybeResolveReferenceToId(\n value: Entity | Reference<any, any, any> | string | undefined,\n): TaggedId | undefined {\n return typeof value === \"string\" ? value : value?.idTaggedMaybe;\n}\n\n/** Converts `value` to a number, i.e. for string ids, unless its undefined. */\nexport function keyToNumber(meta: HasTagName, value: string | number): number;\nexport function keyToNumber(meta: HasTagName, value: string | number | undefined): number | undefined;\nexport function keyToNumber(meta: HasTagName, value: any): number | undefined {\n if (value === undefined || value === null) {\n return undefined;\n } else if (typeof value === \"number\") {\n return value;\n } else if (typeof value === \"string\") {\n if (tagDelimiter === undefined) {\n const id = value.startsWith(meta.tagName) ? value.slice(meta.tagName.length) : value;\n return maybeNumberUnlessUuid(meta, id);\n }\n const delimiterIndex = value.indexOf(tagDelimiter);\n if (delimiterIndex === -1) {\n return maybeNumberUnlessUuid(meta, value);\n }\n const tag = value.slice(0, delimiterIndex);\n if (tag !== meta.tagName) {\n throw new Error(`Invalid tagged id, expected tag ${meta.tagName}, got ${value}`);\n }\n return maybeNumberUnlessUuid(meta, value.slice(delimiterIndex + tagDelimiter.length));\n } else {\n throw new Error(`Invalid key ${value}`);\n }\n}\n\n// UUIDs, text, and bigints are strings at the database boundary, so lie to the type system and pretend they're numbers.\nfunction maybeNumberUnlessUuid(meta: HasTagName, key: string): number {\n switch (meta.idDbType) {\n case \"bigint\":\n case \"uuid\":\n case \"text\":\n return key as any;\n case \"int\":\n return Number(key);\n default:\n assertNever(meta.idDbType);\n }\n}\n\n/** Converts `dbValue` (big int, int, uuid) to a tagged string, unless its undefined. */\nexport function keyToTaggedId(meta: HasTagName, dbValue: string | number): TaggedId | undefined {\n if (dbValue === undefined || dbValue === null) return undefined;\n return `${meta.tagName}${tagDelimiter ?? \"\"}${dbValue}`;\n}\n\n/** Fails if any keys are untagged; internal batch-loader keys must always be tagged. */\nexport function assertIdsAreTagged(keys: readonly string[]): void {\n for (const key of keys) {\n const isTagged = tagDelimiter === undefined ? validSlugId.test(key) : key.indexOf(tagDelimiter) !== -1;\n if (!isTagged) throw new Error(`Key ${key} is missing a tag`);\n }\n}\n\nconst validSlugId = /^([a-z]+)(\\d+)$/i;\nconst uuidIshId = /[0-9a-z-]+/i;\n\n/** Returns whether `id` is tagged and a probably-correct value. */\nexport function isTaggedId(id: string | number): boolean;\n/** Returns whether `id` is tagged and the tag matches `meta`'s tag. */\nexport function isTaggedId(meta: EntityMetadata, id: string): boolean;\nexport function isTaggedId(metaOrId: string | number | HasTagName, id?: string): boolean {\n if (typeof metaOrId === \"number\") {\n // number overload\n return false;\n } else if (typeof metaOrId === \"string\") {\n // string overload\n return tagDelimiter === undefined ? validSlugId.test(metaOrId) : hasTagDelimiter(metaOrId);\n } else {\n // meta + string overload\n return isTaggedIdForMeta(metaOrId, id!);\n }\n}\n\nexport function assertIdIsTagged(id: string): void {\n if (!isTaggedId(id)) {\n throw new Error(`Key is not tagged ${id}`);\n }\n}\n\n/** We accept an entity in case this is m2o storing a not-yet-saved entity. */\nexport function toTaggedId(meta: HasTagName, id: IdType): TaggedId;\nexport function toTaggedId(meta: HasTagName, id: IdType | undefined): TaggedId | undefined;\nexport function toTaggedId(meta: HasTagName, id: IdType | undefined): TaggedId | undefined {\n if (typeof id === \"number\") {\n return `${meta.tagName}${tagDelimiter ?? \"\"}${id}`;\n } else if (typeof id === \"string\") {\n // This seems odd, but is covered by a unit test, so I guess we need it?\n if (id === \"\") return undefined;\n if (tagDelimiter === undefined) {\n const tag = validSlugId.exec(id)?.[1];\n if (tag && tag !== meta.tagName) {\n throw new Error(`Invalid tagged id, expected tag ${meta.tagName}, got ${id}`);\n }\n return tag ? id : `${meta.tagName}${id}`;\n } else {\n const i = id.indexOf(tagDelimiter);\n if (i === -1) return `${meta.tagName}${tagDelimiter}${id}`;\n const tag = id.slice(0, i);\n if (tag !== meta.tagName) {\n throw new Error(`Invalid tagged id, expected tag ${meta.tagName}, got ${id}`);\n }\n }\n }\n return id;\n}\n\n/** Similar to `toTaggedId`, but we accept an entity for handling relations to not-yet-saved entities. */\nexport function ensureTagged<T extends Entity>(\n meta: HasTagName,\n value: T | string | number | undefined,\n): T | TaggedId | undefined {\n return isEntity(value) ? value : toTaggedId(meta, value);\n}\n\n/** Tags a potentially untagged id, while our API inputs still accept either tagged or untagged ids. */\nexport function tagId(meta: HasTagName, id: string | number): string;\nexport function tagId(cstr: EntityConstructor<any>, id: string | number): string;\nexport function tagId(meta: HasTagName, id: string | number | null | undefined): string | undefined;\nexport function tagId(cstr: EntityConstructor<any>, id: string | number | null | undefined): string | undefined;\nexport function tagId(\n metaOrCstr: HasTagName | EntityConstructor<any>,\n id: string | number | null | undefined,\n): string | undefined {\n const meta = metadata(metaOrCstr);\n const tag = meta.tagName;\n if (typeof id === \"number\") {\n return `${tag}${tagDelimiter ?? \"\"}${id}`;\n }\n if (id === null || id === undefined) {\n return undefined;\n }\n if (tagDelimiter === undefined) {\n const existingTag = validSlugId.exec(id)?.[1];\n if (existingTag && existingTag !== tag) {\n throw new Error(`Invalid tagged id, expected tag ${tag}, got ${id}`);\n }\n return existingTag ? id : `${tag}${id}`;\n }\n // Avoid using includes/split, for a faster indexOf + length + startsWith\n const delimiterIndex = id.indexOf(tagDelimiter);\n if (delimiterIndex !== -1) {\n if (delimiterIndex !== tag.length || !id.startsWith(tag)) {\n throw new Error(`Invalid tagged id, expected tag ${tag}, got ${id}`);\n }\n return id;\n }\n return `${tag}${tagDelimiter}${id}`;\n}\n\n/** Adds the tag prefixes. */\nexport function tagIds(meta: HasTagName, keys: readonly (string | number)[]): readonly string[];\nexport function tagIds(cstr: EntityConstructor<any>, keys: readonly (string | number)[]): readonly string[];\nexport function tagIds(\n metaOrCstr: HasTagName | EntityConstructor<any>,\n keys: readonly (string | number)[],\n): readonly string[] {\n return keys.map((k) => tagId(metaOrCstr as any, k));\n}\n\nexport function deTagId(meta: HasTagName, id: string | number): string;\nexport function deTagId(meta: HasTagName, id: string | number | undefined): string | undefined;\nexport function deTagId(entity: Entity): string;\nexport function deTagId(entityOrMeta: Entity | HasTagName, id?: string | number): string | undefined {\n const meta = entityOrMeta instanceof BaseEntity ? getMetadata(entityOrMeta) : (entityOrMeta as HasTagName);\n id = id ?? (entityOrMeta as Entity).id;\n return id === undefined ? undefined : keyToNumber(meta, id).toString();\n}\n\n/** Removes the tag prefixes so we can use the keys for SQL operations. */\nexport function deTagIds(meta: HasTagName, keys: readonly string[]): readonly string[] {\n const deTagged = Array(keys.length);\n for (let i = 0; i < keys.length; i++) deTagged[i] = deTagId(meta, keys[i]);\n return deTagged;\n}\n\n/** Removes the tag prefixes so we can use the keys for SQL operations. */\nexport function unsafeDeTagIds(keys: readonly string[]): readonly string[] {\n const deTagged = Array(keys.length);\n for (let i = 0; i < keys.length; i++) {\n if (tagDelimiter === undefined) {\n deTagged[i] = validSlugId.exec(keys[i])?.[2];\n } else {\n const delimiterIndex = keys[i].indexOf(tagDelimiter);\n deTagged[i] = delimiterIndex === -1 ? undefined : keys[i].slice(delimiterIndex + tagDelimiter.length);\n }\n }\n return deTagged;\n}\n\n/** Given a tagged id, returns its tag. */\nexport function tagFromId(id: string): string {\n if (tagDelimiter === undefined) {\n return validSlugId.exec(id)?.[1] ?? fail(`Unknown tagged id format: \"${id}\"`);\n }\n const delimiterIndex = id.indexOf(tagDelimiter);\n return delimiterIndex > 0 && delimiterIndex + tagDelimiter.length < id.length\n ? id.slice(0, delimiterIndex)\n : fail(`Unknown tagged id format: \"${id}\"`);\n}\n\n/** Returns the metadata needed to format an id. */\nfunction metadata(metaOrCstr: HasTagName | EntityConstructor<any>): HasTagName {\n return typeof metaOrCstr === \"function\" ? getMetadata(metaOrCstr) : metaOrCstr;\n}\n\n/** Returns whether `id` has the tag and database id shape required by `meta`. */\nfunction isTaggedIdForMeta(meta: HasTagName, id: string): boolean {\n if (tagDelimiter === undefined) {\n return validSlugId.exec(id)?.[1] === meta.tagName;\n }\n const delimiterIndex = id.indexOf(tagDelimiter);\n if (delimiterIndex === -1) return false;\n const tag = id.slice(0, delimiterIndex);\n const untaggedId = id.slice(delimiterIndex + tagDelimiter.length);\n if (meta.tagName !== tag) return false;\n switch (meta.idDbType) {\n case \"int\":\n case \"bigint\":\n return !Number.isNaN(Number(untaggedId));\n case \"uuid\":\n return uuidIshId.test(untaggedId);\n case \"text\":\n return true;\n default:\n return assertNever(meta.idDbType);\n }\n}\n\n/** Returns whether `id` has a non-empty tag and value separated by the configured delimiter. */\nfunction hasTagDelimiter(id: string): boolean {\n if (tagDelimiter === undefined) return false;\n const delimiterIndex = id.indexOf(tagDelimiter);\n return delimiterIndex > 0 && delimiterIndex + tagDelimiter.length < id.length;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"keys.js","names":[],"sources":["../src/keys.ts"],"sourcesContent":["import { BaseEntity } from \"src/BaseEntity.ts\";\nimport { type Entity, type IdType, isEntity } from \"src/Entity.ts\";\nimport { type EntityConstructor, type IdOf, type TaggedId } from \"src/EntityManager.ts\";\nimport { type EntityMetadata, getMetadata } from \"src/EntityMetadata.ts\";\nimport { type Reference } from \"src/relations/index.ts\";\nimport { assertNever, fail } from \"src/utils.ts\";\n\nlet tagDelimiter: string | undefined = \":\";\n\n/** Sets the process-wide tagged-id format during metadata boot. */\nexport function setTaggedIdDelimiter(delimiter: string | undefined): void {\n tagDelimiter = delimiter;\n}\n\n// I'm not entirely sure this is still necessary, but use a small subset of EntityMetadata so\n// that this file doesn't have to import the type and potentially create import cycles.\ntype HasTagName = {\n tagName: string;\n /** The database column type, i.e. used to do `::type` casts in Postgres. */\n idDbType: \"bigint\" | \"int\" | \"uuid\" | \"text\";\n};\n\n/** Converts our internal/always-tagged `id` to the domain entity's `id` type (could be a number or uuid). */\nexport function toIdOf<T extends Entity>(meta: EntityMetadata<T>, id: TaggedId | undefined): IdOf<T> | undefined {\n if (!id) return undefined;\n switch (meta.idType) {\n case \"number\":\n return Number(deTagId(meta, id)) as IdOf<T>;\n case \"tagged-string\":\n return id as IdOf<T>;\n case \"untagged-string\":\n return deTagId(meta, id) as IdOf<T>;\n default:\n return assertNever(meta.idType);\n }\n}\n\n// Before a referred-to object is saved, we keep its instance in our data\n// map, and then assume it will be persisted before we're asked to persist\nexport function maybeResolveReferenceToId(\n value: Entity | Reference<any, any, any> | string | undefined,\n): TaggedId | undefined {\n return typeof value === \"string\" ? value : value?.idTaggedMaybe;\n}\n\n/** Converts `value` to a number, i.e. for string ids, unless its undefined. */\nexport function keyToNumber(meta: HasTagName, value: string | number): number;\nexport function keyToNumber(meta: HasTagName, value: string | number | undefined): number | undefined;\nexport function keyToNumber(meta: HasTagName, value: any): number | undefined {\n if (value === undefined || value === null) {\n return undefined;\n } else if (typeof value === \"number\") {\n return value;\n } else if (typeof value === \"string\") {\n if (tagDelimiter === undefined) {\n const id = value.startsWith(meta.tagName) ? value.slice(meta.tagName.length) : value;\n return maybeNumberUnlessUuid(meta, id);\n }\n const delimiterIndex = value.indexOf(tagDelimiter);\n if (delimiterIndex === -1) {\n return maybeNumberUnlessUuid(meta, value);\n }\n const tag = value.slice(0, delimiterIndex);\n if (tag !== meta.tagName) {\n throw new Error(`Invalid tagged id, expected tag ${meta.tagName}, got ${value}`);\n }\n return maybeNumberUnlessUuid(meta, value.slice(delimiterIndex + tagDelimiter.length));\n } else {\n throw new Error(`Invalid key ${value}`);\n }\n}\n\n// UUIDs, text, and bigints are strings at the database boundary, so lie to the type system and pretend they're numbers.\nfunction maybeNumberUnlessUuid(meta: HasTagName, key: string): number {\n switch (meta.idDbType) {\n case \"bigint\":\n case \"uuid\":\n case \"text\":\n return key as any;\n case \"int\":\n return Number(key);\n default:\n assertNever(meta.idDbType);\n }\n}\n\n/** Converts `dbValue` (big int, int, uuid) to a tagged string, unless its undefined. */\nexport function keyToTaggedId(meta: HasTagName, dbValue: string | number): TaggedId | undefined {\n if (dbValue === undefined || dbValue === null) return undefined;\n return `${taggedIdPrefix(meta)}${dbValue}`;\n}\n\n/** Returns the entity tag followed by the configured ID delimiter. */\nexport function taggedIdPrefix(meta: Pick<HasTagName, \"tagName\">): string {\n return `${meta.tagName}${tagDelimiter ?? \"\"}`;\n}\n\n/** Fails if any keys are untagged; internal batch-loader keys must always be tagged. */\nexport function assertIdsAreTagged(keys: readonly string[]): void {\n for (const key of keys) {\n const isTagged = tagDelimiter === undefined ? validSlugId.test(key) : key.indexOf(tagDelimiter) !== -1;\n if (!isTagged) throw new Error(`Key ${key} is missing a tag`);\n }\n}\n\nconst validSlugId = /^([a-z]+)(\\d+)$/i;\nconst uuidIshId = /[0-9a-z-]+/i;\n\n/** Returns whether `id` is tagged and a probably-correct value. */\nexport function isTaggedId(id: string | number): boolean;\n/** Returns whether `id` is tagged and the tag matches `meta`'s tag. */\nexport function isTaggedId(meta: EntityMetadata, id: string): boolean;\nexport function isTaggedId(metaOrId: string | number | HasTagName, id?: string): boolean {\n if (typeof metaOrId === \"number\") {\n // number overload\n return false;\n } else if (typeof metaOrId === \"string\") {\n // string overload\n return tagDelimiter === undefined ? validSlugId.test(metaOrId) : hasTagDelimiter(metaOrId);\n } else {\n // meta + string overload\n return isTaggedIdForMeta(metaOrId, id!);\n }\n}\n\nexport function assertIdIsTagged(id: string): void {\n if (!isTaggedId(id)) {\n throw new Error(`Key is not tagged ${id}`);\n }\n}\n\n/** We accept an entity in case this is m2o storing a not-yet-saved entity. */\nexport function toTaggedId(meta: HasTagName, id: IdType): TaggedId;\nexport function toTaggedId(meta: HasTagName, id: IdType | undefined): TaggedId | undefined;\nexport function toTaggedId(meta: HasTagName, id: IdType | undefined): TaggedId | undefined {\n if (typeof id === \"number\") {\n return `${meta.tagName}${tagDelimiter ?? \"\"}${id}`;\n } else if (typeof id === \"string\") {\n // This seems odd, but is covered by a unit test, so I guess we need it?\n if (id === \"\") return undefined;\n if (tagDelimiter === undefined) {\n const tag = validSlugId.exec(id)?.[1];\n if (tag && tag !== meta.tagName) {\n throw new Error(`Invalid tagged id, expected tag ${meta.tagName}, got ${id}`);\n }\n return tag ? id : `${meta.tagName}${id}`;\n } else {\n const i = id.indexOf(tagDelimiter);\n if (i === -1) return `${meta.tagName}${tagDelimiter}${id}`;\n const tag = id.slice(0, i);\n if (tag !== meta.tagName) {\n throw new Error(`Invalid tagged id, expected tag ${meta.tagName}, got ${id}`);\n }\n }\n }\n return id;\n}\n\n/** Similar to `toTaggedId`, but we accept an entity for handling relations to not-yet-saved entities. */\nexport function ensureTagged<T extends Entity>(\n meta: HasTagName,\n value: T | string | number | undefined,\n): T | TaggedId | undefined {\n return isEntity(value) ? value : toTaggedId(meta, value);\n}\n\n/** Tags a potentially untagged id, while our API inputs still accept either tagged or untagged ids. */\nexport function tagId(meta: HasTagName, id: string | number): string;\nexport function tagId(cstr: EntityConstructor<any>, id: string | number): string;\nexport function tagId(meta: HasTagName, id: string | number | null | undefined): string | undefined;\nexport function tagId(cstr: EntityConstructor<any>, id: string | number | null | undefined): string | undefined;\nexport function tagId(\n metaOrCstr: HasTagName | EntityConstructor<any>,\n id: string | number | null | undefined,\n): string | undefined {\n const meta = metadata(metaOrCstr);\n const tag = meta.tagName;\n if (typeof id === \"number\") {\n return `${tag}${tagDelimiter ?? \"\"}${id}`;\n }\n if (id === null || id === undefined) {\n return undefined;\n }\n if (tagDelimiter === undefined) {\n const existingTag = validSlugId.exec(id)?.[1];\n if (existingTag && existingTag !== tag) {\n throw new Error(`Invalid tagged id, expected tag ${tag}, got ${id}`);\n }\n return existingTag ? id : `${tag}${id}`;\n }\n // Avoid using includes/split, for a faster indexOf + length + startsWith\n const delimiterIndex = id.indexOf(tagDelimiter);\n if (delimiterIndex !== -1) {\n if (delimiterIndex !== tag.length || !id.startsWith(tag)) {\n throw new Error(`Invalid tagged id, expected tag ${tag}, got ${id}`);\n }\n return id;\n }\n return `${tag}${tagDelimiter}${id}`;\n}\n\n/** Adds the tag prefixes. */\nexport function tagIds(meta: HasTagName, keys: readonly (string | number)[]): readonly string[];\nexport function tagIds(cstr: EntityConstructor<any>, keys: readonly (string | number)[]): readonly string[];\nexport function tagIds(\n metaOrCstr: HasTagName | EntityConstructor<any>,\n keys: readonly (string | number)[],\n): readonly string[] {\n return keys.map((k) => tagId(metaOrCstr as any, k));\n}\n\nexport function deTagId(meta: HasTagName, id: string | number): string;\nexport function deTagId(meta: HasTagName, id: string | number | undefined): string | undefined;\nexport function deTagId(entity: Entity): string;\nexport function deTagId(entityOrMeta: Entity | HasTagName, id?: string | number): string | undefined {\n const meta = entityOrMeta instanceof BaseEntity ? getMetadata(entityOrMeta) : (entityOrMeta as HasTagName);\n id = id ?? (entityOrMeta as Entity).id;\n return id === undefined ? undefined : keyToNumber(meta, id).toString();\n}\n\n/** Removes the tag prefixes so we can use the keys for SQL operations. */\nexport function deTagIds(meta: HasTagName, keys: readonly string[]): readonly string[] {\n const deTagged = Array(keys.length);\n for (let i = 0; i < keys.length; i++) deTagged[i] = deTagId(meta, keys[i]);\n return deTagged;\n}\n\n/** Removes the tag prefixes so we can use the keys for SQL operations. */\nexport function unsafeDeTagIds(keys: readonly string[]): readonly string[] {\n const deTagged = Array(keys.length);\n for (let i = 0; i < keys.length; i++) {\n if (tagDelimiter === undefined) {\n deTagged[i] = validSlugId.exec(keys[i])?.[2];\n } else {\n const delimiterIndex = keys[i].indexOf(tagDelimiter);\n deTagged[i] = delimiterIndex === -1 ? undefined : keys[i].slice(delimiterIndex + tagDelimiter.length);\n }\n }\n return deTagged;\n}\n\n/** Given a tagged id, returns its tag. */\nexport function tagFromId(id: string): string {\n if (tagDelimiter === undefined) {\n return validSlugId.exec(id)?.[1] ?? fail(`Unknown tagged id format: \"${id}\"`);\n }\n const delimiterIndex = id.indexOf(tagDelimiter);\n return delimiterIndex > 0 && delimiterIndex + tagDelimiter.length < id.length\n ? id.slice(0, delimiterIndex)\n : fail(`Unknown tagged id format: \"${id}\"`);\n}\n\n/** Returns the metadata needed to format an id. */\nfunction metadata(metaOrCstr: HasTagName | EntityConstructor<any>): HasTagName {\n return typeof metaOrCstr === \"function\" ? getMetadata(metaOrCstr) : metaOrCstr;\n}\n\n/** Returns whether `id` has the tag and database id shape required by `meta`. */\nfunction isTaggedIdForMeta(meta: HasTagName, id: string): boolean {\n if (tagDelimiter === undefined) {\n return validSlugId.exec(id)?.[1] === meta.tagName;\n }\n const delimiterIndex = id.indexOf(tagDelimiter);\n if (delimiterIndex === -1) return false;\n const tag = id.slice(0, delimiterIndex);\n const untaggedId = id.slice(delimiterIndex + tagDelimiter.length);\n if (meta.tagName !== tag) return false;\n switch (meta.idDbType) {\n case \"int\":\n case \"bigint\":\n return !Number.isNaN(Number(untaggedId));\n case \"uuid\":\n return uuidIshId.test(untaggedId);\n case \"text\":\n return true;\n default:\n return assertNever(meta.idDbType);\n }\n}\n\n/** Returns whether `id` has a non-empty tag and value separated by the configured delimiter. */\nfunction hasTagDelimiter(id: string): boolean {\n if (tagDelimiter === undefined) return false;\n const delimiterIndex = id.indexOf(tagDelimiter);\n return delimiterIndex > 0 && delimiterIndex + tagDelimiter.length < id.length;\n}\n"],"mappings":"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"}
@@ -174,7 +174,6 @@ function parseFindQuery(meta, filter, opts = {}) {
174
174
  const f = require_queries_valueFilters.parseEntityFilter(meta, subFilter[key]);
175
175
  if (!f) {} else if (f.kind === "join") throw new Error("Joins through polys are not supported");
176
176
  else if (f.kind === "eq" || f.kind === "ne") {
177
- if (require_queries_valueFilters.isNilIdValue(f.value)) return;
178
177
  const comp = field.components.find((p) => {
179
178
  const otherMeta = p.otherMetadata();
180
179
  const cstr = require_configure.getConstructorFromTaggedId(f.value);
@@ -291,7 +290,7 @@ function parseFindQuery(meta, filter, opts = {}) {
291
290
  columnName: field.columnNames[1],
292
291
  dbType: otherMeta.idDbType,
293
292
  mapToDb(value) {
294
- return value === null || require_queries_valueFilters.isNilIdValue(value) ? value : require_keys.keyToNumber(otherMeta, require_keys.maybeResolveReferenceToId(value));
293
+ return value === null ? value : require_keys.keyToNumber(otherMeta, require_keys.maybeResolveReferenceToId(value));
295
294
  }
296
295
  };
297
296
  targetCb.addSimpleCondition({