diff --git a/.changeset/locals-key-dual-package-fix.md b/.changeset/locals-key-dual-package-fix.md new file mode 100644 index 00000000000..38d42e19dfb --- /dev/null +++ b/.changeset/locals-key-dual-package-fix.md @@ -0,0 +1,5 @@ +--- +"@trigger.dev/core": patch +--- + +Fix `LocalsKey` type incompatibility across dual-package builds. The phantom value-type brand no longer uses a module-level `unique symbol`, so a single TypeScript compilation that resolves the type from both the ESM and CJS outputs (which can happen under certain pnpm hoisting layouts) no longer sees two structurally-incompatible variants of the same type. diff --git a/packages/core/src/v3/locals/manager.ts b/packages/core/src/v3/locals/manager.ts index 6984d9f4146..3cd0f80d84a 100644 --- a/packages/core/src/v3/locals/manager.ts +++ b/packages/core/src/v3/locals/manager.ts @@ -5,7 +5,7 @@ export class NoopLocalsManager implements LocalsManager { return { __type: Symbol(), id, - } as unknown as LocalsKey; + }; } getLocal(key: LocalsKey): T | undefined { @@ -23,7 +23,7 @@ export class StandardLocalsManager implements LocalsManager { return { __type: key, id, - } as unknown as LocalsKey; + }; } getLocal(key: LocalsKey): T | undefined { diff --git a/packages/core/src/v3/locals/types.ts b/packages/core/src/v3/locals/types.ts index aab683df091..84abc4c70f5 100644 --- a/packages/core/src/v3/locals/types.ts +++ b/packages/core/src/v3/locals/types.ts @@ -1,10 +1,22 @@ -declare const __local: unique symbol; -type BrandLocal = { [__local]: T }; - -// Create a type-safe store for your locals -export type LocalsKey = BrandLocal & { +/** + * A type-safe key for `locals`. Carries the value type `T` as a phantom + * marker on the optional `__valueType` field so two keys with different + * value types are distinguishable at the type level. + * + * The phantom field is intentionally not anchored to a `unique symbol`: + * dual-package builds (`tshy`) emit separate `.d.ts` files for ESM and + * CJS outputs, and each `unique symbol` declaration in a `.d.ts` is its + * own nominal type. If a single compilation ever resolves `LocalsKey` + * from both the ESM and CJS paths — which happens under certain pnpm + * hoisting layouts — `unique symbol` brands produce structurally + * incompatible variants of the same type. A plain string brand avoids + * the hazard. + */ +export type LocalsKey = { readonly id: string; - readonly __type: unique symbol; + readonly __type: symbol; + /** Phantom carrier for the value type — never read at runtime. */ + readonly __valueType?: T; }; export interface LocalsManager {