{"version":3,"file":"index.mjs","names":[],"sources":["../../src/core/metadata.ts"],"sourcesContent":["// oxlint-disable typescript/no-explicit-any\nimport { unsafeCoerce } from \"@prodkit/shared/runtime\";\nimport type { Op } from \"../index.js\";\n\ndeclare const EMPTY_META: unique symbol;\ndeclare const BLOCKING: unique symbol;\n\n/**\n * Metadata merge algebra for composed operations.\n *\n * Operations carry extension metadata on `M`. When they compose (`flatMap`, combinators,\n * yielded custom instructions), {@link MergeMeta} accumulates requirements from both sides.\n *\n * {@link EmptyMeta} is the identity element: merging with empty metadata leaves the other\n * operand unchanged.\n *\n * Per-key merge outcomes:\n * - Keys present on only one side are kept as-is.\n * - Plain values at the same key union (requirements accumulate).\n * - When either side at a key is {@link Blocking}, the merged value is `Blocking` with\n *   payloads unioned. `Blocking` takes precedence over plain values at that key.\n *\n * {@link MergeMetaObjects} merges two object shapes key-by-key. {@link MergeUnionMeta} applies\n * the same rules when a generator yields multiple custom instructions. {@link CollectBlockingPayload}\n * extracts `Blocking` payload types during union merges so blocking requirements stay branded.\n */\ntype MergeBlockingValue<VA, VB> =\n  VA extends Blocking<infer TA>\n    ? VB extends Blocking<infer TB>\n      ? Blocking<TA | TB>\n      : Blocking<TA>\n    : VB extends Blocking<infer TB>\n      ? Blocking<TB>\n      : VA | VB;\n\ntype MergeMetaValue<A, B, K extends PropertyKey> = K extends keyof StripEmpty<A> &\n  keyof StripEmpty<B>\n  ? MergeBlockingValue<StripEmpty<A>[K], StripEmpty<B>[K]>\n  : K extends keyof StripEmpty<A>\n    ? StripEmpty<A>[K]\n    : K extends keyof StripEmpty<B>\n      ? StripEmpty<B>[K]\n      : never;\n\ntype MergeMetaObjects<A, B> = NormalizeMeta<{\n  [K in keyof StripEmpty<A> | keyof StripEmpty<B>]: MergeMetaValue<A, B, K>;\n}>;\n\ntype UnionMetaValueAt<U, K extends PropertyKey> = U extends Record<K, infer V> ? V : never;\n\ntype CollectBlockingPayload<U, K extends PropertyKey> =\n  U extends Record<K, Blocking<infer R>> ? R : never;\n\ntype MergeUnionMetaValue<U, K extends PropertyKey> = [CollectBlockingPayload<U, K>] extends [never]\n  ? UnionMetaValueAt<U, K>\n  : Blocking<CollectBlockingPayload<U, K>>;\n\nexport type MergeUnionMeta<U> = NormalizeMeta<\n  [U] extends [never]\n    ? EmptyMeta\n    : {\n        [K in AllMetaKeys<U>]: MergeUnionMetaValue<U, K>;\n      }\n>;\n\n/** Merges metadata accumulated across two composed operations. See merge algebra above. */\nexport type MergeMeta<A, B> =\n  IsAny<A> extends true\n    ? any\n    : IsAny<B> extends true\n      ? any\n      : [A] extends [EmptyMeta]\n        ? NormalizeMeta<B>\n        : [B] extends [EmptyMeta]\n          ? NormalizeMeta<A>\n          : MergeMetaObjects<A, MergeMetaRight<B>>;\n\nexport type SetBlockingMeta<M, K extends PropertyKey, T> = NormalizeMeta<\n  Simplify<StripEmpty<M> & { [P in K]: Blocking<T> }>\n>;\n\n/** An operation that is not ready for top-level `.run()`. */\nexport type BlockingOp<T, E, A, M = EmptyMeta, K extends PropertyKey = string, P = true> = Op<\n  T,\n  E,\n  A,\n  SetBlockingMeta<M, K, P>\n>;\n\n/**\n * Marks an operation as needing extension-specific preconditions before\n * top-level `.run()` by placing `Blocking<P>` on a metadata key.\n */\nexport function withBlocking<T, E, A, M, const K extends PropertyKey, P = true>(\n  op: Op<T, E, A, M>,\n  _key: K,\n): BlockingOp<T, E, A, M, K, P> {\n  // SAFETY: withBlocking only changes metadata phantom types; the runtime callable is the same op instance.\n  return unsafeCoerce(op);\n}\n\n/**\n * Runnable gating from metadata.\n *\n * Top-level {@link BaseOp.run} is available only when every metadata key is satisfied.\n * {@link HasBlocking} is true when any key still carries {@link Blocking} with a non-empty\n * payload. {@link IsRunnable} is false in that case, so `.run()` is not on the operation type.\n *\n * Extension packages block `.run()` by attaching `Blocking` to metadata keys (or via\n * `withBlocking(...)`). Callers satisfy those requirements through extension-specific runners\n * first; clearing or replacing blocking metadata is what makes `.run()` type-check again.\n */\nexport type IsRunnable<M> =\n  IsAny<M> extends true ? true : [HasBlocking<M>] extends [true] ? false : true;\n\n/** True when metadata still carries an unsatisfied {@link Blocking} requirement on any key. */\ntype HasBlocking<M> = keyof StripEmpty<M> extends never\n  ? false\n  : {\n        [K in keyof StripEmpty<M>]: StripEmpty<M>[K] extends Blocking<infer R>\n          ? [R] extends [never]\n            ? false\n            : true\n          : false;\n      }[keyof StripEmpty<M>] extends true\n    ? true\n    : false;\n\n/**\n * Empty metadata; the merge identity element.\n *\n * Operations with no extension requirements use `EmptyMeta`. Merging with `EmptyMeta` leaves\n * the other operand unchanged in both directions.\n */\nexport type EmptyMeta = {\n  readonly [EMPTY_META]: true;\n};\n\n/**\n * Branded metadata value that blocks top-level `.run()` until its payload is satisfied.\n *\n * During metadata merge, `Blocking` at a key takes precedence over plain values and unions\n * payloads with other `Blocking` values at the same key.\n */\nexport type Blocking<T> = { readonly [BLOCKING]: T };\n\ntype IsAny<T> = 0 extends 1 & T ? true : false;\nexport type NormalizeMeta<M> = [M] extends [never]\n  ? EmptyMeta\n  : M extends EmptyMeta\n    ? EmptyMeta\n    : M extends object\n      ? keyof M extends never\n        ? EmptyMeta\n        : Simplify<M>\n      : M;\n\nexport type StripEmpty<M> = [M] extends [never] ? {} : M extends EmptyMeta ? {} : M;\nexport type Simplify<T> = T extends object ? { [K in keyof T]: T[K] } : T;\ntype WithoutEmptyMeta<M> = M extends EmptyMeta ? never : M;\ntype MergeMetaRight<B> = [WithoutEmptyMeta<B>] extends [never] ? EmptyMeta : WithoutEmptyMeta<B>;\ntype AllMetaKeys<U> = U extends unknown ? keyof U : never;\n"],"mappings":";;;;;;AA6FA,SAAgB,aACd,IACA,MAC8B;CAE9B,OAAO,aAAa,EAAE;AACxB"}