{"version":3,"file":"define-B6_ij6t2.mjs","names":["isChannelExportMeta"],"sources":["../src/channels/define.ts"],"sourcesContent":["import { Type, type Static, type TSchema } from \"@sinclair/typebox\";\nimport { Channel } from \"../channels\";\nimport {\n  CHANNEL_SYMBOL,\n  isChannelDefinition,\n  isChannelExport as isChannelExportMeta,\n  type ChannelAuthConfig,\n  type ChannelAuthResult,\n  type ChannelAuthScheme,\n  type ChannelDefinition,\n  type ChannelHandlerContext,\n  type ChannelLifecycleConfig,\n  type MessageHandler,\n  type VerifyInput,\n} from \"./meta\";\nimport type {\n  ChannelConnectOptions,\n  ChannelLiveTransport,\n  ChannelMessage,\n  ChannelPublishOptions,\n  ChannelSocket,\n  ChannelSubscribeOptions,\n  ChannelSubscription,\n} from \"../types\";\n\nexport interface ChannelConfig<\n  ParamsSchema extends TSchema | undefined,\n  Params,\n  Messages,\n> extends ChannelLifecycleConfig {\n  /**\n   * Wire name for the channel. Use a stable kebab-case name, usually matching\n   * the filename in `<app_root>/channels/<name>.ts`.\n   */\n  name: string;\n  /**\n   * TypeBox schema for query params required to bind this channel.\n   *\n   * Omit for an unparameterized channel.\n   */\n  paramsSchema?: (t: typeof Type) => ParamsSchema extends TSchema ? ParamsSchema : TSchema;\n  /**\n   * Authorization policy for subscribe, publish, and connect operations.\n   *\n   * Omit or set to `false` for a public channel.\n   */\n  auth?: false | ChannelAuthConfig<Params>;\n  /**\n   * Optional WebSocket message handlers. Presence of a handler map makes the\n   * channel connectable over WebSocket; otherwise browser subscribers use SSE.\n   */\n  handler?: ChannelHandlerMap<Params, Messages> | undefined;\n}\n\ntype ChannelHandlerMap<Params, Messages> = {\n  [T in keyof Messages]?: MessageHandler<Messages[T], Params>;\n};\n\ntype ConfigMessageHandler<Data, Params> = {\n  handle(data: Data, ctx: ChannelHandlerContext<Params>): Data | void | Promise<Data | void>;\n}[\"handle\"];\n\ntype ConfigChannelHandlerMap<Params> = Record<string, ConfigMessageHandler<unknown, Params>>;\n\ntype ChannelConfigWithParams<ParamsSchema extends TSchema> = Omit<\n  ChannelConfig<ParamsSchema, Static<ParamsSchema>, Record<string, unknown>>,\n  \"handler\" | \"paramsSchema\"\n> & {\n  paramsSchema: (t: typeof Type) => ParamsSchema;\n  handler?: ConfigChannelHandlerMap<Static<ParamsSchema>> | undefined;\n};\n\ntype ChannelConfigWithoutParams = Omit<\n  ChannelConfig<undefined, Record<string, never>, Record<string, unknown>>,\n  \"handler\" | \"paramsSchema\"\n> & {\n  paramsSchema?: undefined;\n  handler?: ConfigChannelHandlerMap<Record<string, never>> | undefined;\n};\n\ntype AnyChannelConfig = ChannelLifecycleConfig & {\n  name: string;\n  paramsSchema?: ((t: typeof Type) => TSchema) | undefined;\n  auth?:\n    | false\n    | {\n        headerName?: string | false;\n        cookieName?: string;\n        verify: unknown;\n      };\n  handler?: Record<string, unknown> | undefined;\n};\n\n/**\n * Bound channel handle returned by {@link defineChannel}.\n *\n * Parameterized channels return this after calling the exported channel with\n * params. Unparameterized channels export the handle directly.\n */\nexport type ChannelHandle<\n  Params,\n  Messages,\n  Transport extends ChannelLiveTransport = ChannelLiveTransport,\n> = {\n  readonly __params?: Params;\n  readonly __messages?: Messages;\n  /** Channel name plus encoded params, useful for logging and diagnostics. */\n  readonly name: string;\n  /**\n   * Publish a typed message to current channel subscribers.\n   */\n  publish<T extends keyof Messages & string>(\n    message: { type: T; data: Messages[T] },\n    options?: ChannelPublishOptions,\n  ): Promise<ChannelMessage<Messages[T]>>;\n  /**\n   * Subscribe to messages with the default browser transport.\n   */\n  subscribe(options?: ChannelSubscribeOptions): ChannelSubscription;\n  /**\n   * Open a WebSocket connection. Present only when the channel has handlers.\n   */\n} & (Transport extends \"ws\"\n  ? { connect(options?: ChannelConnectOptions): ChannelSocket }\n  : { readonly connect?: never });\n\n/**\n * Metadata attached to every channel export for Tako's discovery pass.\n */\nexport interface ChannelExportMeta<\n  Params,\n  Messages,\n  Transport extends ChannelLiveTransport = ChannelLiveTransport,\n> {\n  readonly definition: ChannelDefinition<Params, Messages>;\n  /**\n   * Narrow the message map for this channel without changing runtime behavior.\n   */\n  $messageTypes<NewMessages>(): ChannelExport<Params, NewMessages, Transport>;\n}\n\n/**\n * Public shape exported from a `<app_root>/channels/<name>.ts` file.\n *\n * Channels with params are callable and return a {@link ChannelHandle}; channels\n * without params are already bound handles.\n */\nexport type ChannelExport<\n  Params,\n  Messages,\n  Transport extends ChannelLiveTransport = ChannelLiveTransport,\n> = (Record<string, never> extends Params\n  ? ChannelHandle<Params, Messages, Transport>\n  : (params: Params) => ChannelHandle<Params, Messages, Transport>) &\n  ChannelExportMeta<Params, Messages, Transport>;\n\nfunction lifecycle(config: ChannelLifecycleConfig): ChannelLifecycleConfig {\n  return {\n    ...(config.replayWindowMs !== undefined && { replayWindowMs: config.replayWindowMs }),\n    ...(config.inactivityTtlMs !== undefined && { inactivityTtlMs: config.inactivityTtlMs }),\n    ...(config.keepaliveIntervalMs !== undefined && {\n      keepaliveIntervalMs: config.keepaliveIntervalMs,\n    }),\n    ...(config.maxConnectionLifetimeMs !== undefined && {\n      maxConnectionLifetimeMs: config.maxConnectionLifetimeMs,\n    }),\n  };\n}\n\nfunction encodeParams(params: Record<string, unknown>): string {\n  const search = new URLSearchParams();\n  for (const [key, value] of Object.entries(params)) {\n    if (value === undefined || value === null) continue;\n    search.set(key, encodeQueryValue(value));\n  }\n  const query = search.toString();\n  return query ? `?${query}` : \"\";\n}\n\nfunction encodeQueryValue(value: unknown): string {\n  if (typeof value === \"string\") return value;\n  if (typeof value === \"number\" || typeof value === \"boolean\" || typeof value === \"bigint\") {\n    return value.toString();\n  }\n  return JSON.stringify(value);\n}\n\nfunction makeHandle<P, M, Transport extends ChannelLiveTransport>(\n  definition: ChannelDefinition<P, M>,\n  params: P,\n): ChannelHandle<P, M, Transport> {\n  const query = encodeParams(params as Record<string, unknown>);\n  const makeChannel = () =>\n    new Channel(definition.channel, definition.transport, params as Record<string, unknown>);\n  const handle = {\n    get name() {\n      return `${definition.channel}${query}`;\n    },\n    publish<T extends keyof M & string>(\n      message: { type: T; data: M[T] },\n      options?: ChannelPublishOptions,\n    ) {\n      return makeChannel().publish(message, options);\n    },\n    subscribe(options?: ChannelSubscribeOptions) {\n      return makeChannel().subscribe(options);\n    },\n  };\n  if (definition.transport === \"ws\") {\n    Object.defineProperty(handle, \"connect\", {\n      value(options?: ChannelConnectOptions) {\n        return makeChannel().connect(options);\n      },\n      enumerable: true,\n      configurable: true,\n    });\n  }\n  return handle as ChannelHandle<P, M, Transport>;\n}\n\nfunction attachMeta<P, M, Transport extends ChannelLiveTransport, T extends object>(\n  target: T,\n  definition: ChannelDefinition<P, M>,\n): T & ChannelExportMeta<P, M, Transport> {\n  Object.defineProperty(target, \"definition\", {\n    value: definition,\n    writable: false,\n    enumerable: false,\n    configurable: false,\n  });\n  Object.defineProperty(target, \"$messageTypes\", {\n    value: function messageTypesNarrow<NewM>() {\n      return this as unknown as ChannelExport<P, NewM, Transport>;\n    },\n    writable: false,\n    enumerable: false,\n    configurable: false,\n  });\n  return target as T & ChannelExportMeta<P, M, Transport>;\n}\n\n/**\n * Define a typed realtime channel.\n *\n * Put one default export in each `<app_root>/channels/*.ts` file. The optional\n * `paramsSchema` controls the typed params needed to bind the channel, `auth`\n * controls subscribe/publish/connect authorization, and `handler` enables\n * WebSocket messages.\n *\n * @example\n * ```ts\n * import { defineChannel } from \"tako.sh\";\n *\n * type Messages = { msg: { text: string } };\n *\n * export default defineChannel({\n *   name: \"chat\",\n *   paramsSchema: (t) => t.Object({ roomId: t.String() }),\n * }).$messageTypes<Messages>();\n * ```\n */\nexport function defineChannel<ParamsSchema extends TSchema>(\n  config: ChannelConfigWithParams<ParamsSchema> & {\n    handler: ConfigChannelHandlerMap<Static<ParamsSchema>>;\n  },\n): ChannelExport<Static<ParamsSchema>, Record<string, unknown>, \"ws\">;\nexport function defineChannel<ParamsSchema extends TSchema>(\n  config: ChannelConfigWithParams<ParamsSchema> & { handler?: undefined },\n): ChannelExport<Static<ParamsSchema>, Record<string, unknown>, \"sse\">;\nexport function defineChannel(\n  config: ChannelConfigWithoutParams & {\n    handler: ConfigChannelHandlerMap<Record<string, never>>;\n  },\n): ChannelExport<Record<string, never>, Record<string, unknown>, \"ws\">;\nexport function defineChannel(\n  config: ChannelConfigWithoutParams & { handler?: undefined },\n): ChannelExport<Record<string, never>, Record<string, unknown>, \"sse\">;\nexport function defineChannel(input: unknown): unknown {\n  const config = input as AnyChannelConfig;\n  const schema = config.paramsSchema?.(Type) ?? Type.Object({});\n  const auth: ChannelDefinition<unknown, Record<string, unknown>>[\"auth\"] =\n    config.auth === undefined || config.auth === false\n      ? false\n      : {\n          headerName:\n            config.auth.headerName === undefined ? \"authorization\" : config.auth.headerName,\n          ...(config.auth.cookieName !== undefined && { cookieName: config.auth.cookieName }),\n          verify: config.auth.verify as ChannelAuthConfig<unknown>[\"verify\"],\n        };\n  const definition: ChannelDefinition<unknown, Record<string, unknown>> = {\n    type: CHANNEL_SYMBOL,\n    channel: config.name,\n    paramsSchema: schema,\n    auth,\n    hasParams: config.paramsSchema !== undefined,\n    ...(config.handler !== undefined\n      ? {\n          handler: config.handler as NonNullable<\n            ChannelDefinition<unknown, Record<string, unknown>>[\"handler\"]\n          >,\n          transport: \"ws\" as const,\n        }\n      : {}),\n    ...lifecycle(config),\n  };\n\n  if (definition.hasParams) {\n    const callable = (params: unknown) => makeHandle(definition, params);\n    return attachMeta(callable, definition);\n  }\n\n  const handle = makeHandle(definition, {});\n  return attachMeta(handle, definition);\n}\n\n/** Narrow `value` to a `ChannelExport` produced by `defineChannel`. */\nexport function isChannelExport(\n  value: unknown,\n): value is ChannelExport<unknown, unknown, ChannelLiveTransport> {\n  return isChannelExportMeta(value);\n}\n\nexport {\n  CHANNEL_SYMBOL,\n  isChannelDefinition,\n  type ChannelAuthConfig,\n  type ChannelAuthResult,\n  type ChannelAuthScheme,\n  type ChannelDefinition,\n  type ChannelHandlerContext,\n  type ChannelLifecycleConfig,\n  type MessageHandler,\n  type VerifyInput,\n};\n"],"mappings":";;;;;AA4JA,SAAS,UAAU,QAAwD;CACzE,OAAO;EACL,GAAI,OAAO,mBAAmB,UAAa,EAAE,gBAAgB,OAAO,gBAAgB;EACpF,GAAI,OAAO,oBAAoB,UAAa,EAAE,iBAAiB,OAAO,iBAAiB;EACvF,GAAI,OAAO,wBAAwB,UAAa,EAC9C,qBAAqB,OAAO,qBAC7B;EACD,GAAI,OAAO,4BAA4B,UAAa,EAClD,yBAAyB,OAAO,yBACjC;EACF;;AAGH,SAAS,aAAa,QAAyC;CAC7D,MAAM,SAAS,IAAI,iBAAiB;CACpC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,EAAE;EACjD,IAAI,UAAU,UAAa,UAAU,MAAM;EAC3C,OAAO,IAAI,KAAK,iBAAiB,MAAM,CAAC;;CAE1C,MAAM,QAAQ,OAAO,UAAU;CAC/B,OAAO,QAAQ,IAAI,UAAU;;AAG/B,SAAS,iBAAiB,OAAwB;CAChD,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,aAAa,OAAO,UAAU,UAC9E,OAAO,MAAM,UAAU;CAEzB,OAAO,KAAK,UAAU,MAAM;;AAG9B,SAAS,WACP,YACA,QACgC;CAChC,MAAM,QAAQ,aAAa,OAAkC;CAC7D,MAAM,oBACJ,IAAI,QAAQ,WAAW,SAAS,WAAW,WAAW,OAAkC;CAC1F,MAAM,SAAS;EACb,IAAI,OAAO;GACT,OAAO,GAAG,WAAW,UAAU;;EAEjC,QACE,SACA,SACA;GACA,OAAO,aAAa,CAAC,QAAQ,SAAS,QAAQ;;EAEhD,UAAU,SAAmC;GAC3C,OAAO,aAAa,CAAC,UAAU,QAAQ;;EAE1C;CACD,IAAI,WAAW,cAAc,MAC3B,OAAO,eAAe,QAAQ,WAAW;EACvC,MAAM,SAAiC;GACrC,OAAO,aAAa,CAAC,QAAQ,QAAQ;;EAEvC,YAAY;EACZ,cAAc;EACf,CAAC;CAEJ,OAAO;;AAGT,SAAS,WACP,QACA,YACwC;CACxC,OAAO,eAAe,QAAQ,cAAc;EAC1C,OAAO;EACP,UAAU;EACV,YAAY;EACZ,cAAc;EACf,CAAC;CACF,OAAO,eAAe,QAAQ,iBAAiB;EAC7C,OAAO,SAAS,qBAA2B;GACzC,OAAO;;EAET,UAAU;EACV,YAAY;EACZ,cAAc;EACf,CAAC;CACF,OAAO;;AAuCT,SAAgB,cAAc,OAAyB;CACrD,MAAM,SAAS;CACf,MAAM,SAAS,OAAO,eAAe,KAAK,IAAI,KAAK,OAAO,EAAE,CAAC;CAC7D,MAAM,OACJ,OAAO,SAAS,UAAa,OAAO,SAAS,QACzC,QACA;EACE,YACE,OAAO,KAAK,eAAe,SAAY,kBAAkB,OAAO,KAAK;EACvE,GAAI,OAAO,KAAK,eAAe,UAAa,EAAE,YAAY,OAAO,KAAK,YAAY;EAClF,QAAQ,OAAO,KAAK;EACrB;CACP,MAAM,aAAkE;EACtE,MAAM;EACN,SAAS,OAAO;EAChB,cAAc;EACd;EACA,WAAW,OAAO,iBAAiB;EACnC,GAAI,OAAO,YAAY,SACnB;GACE,SAAS,OAAO;GAGhB,WAAW;GACZ,GACD,EAAE;EACN,GAAG,UAAU,OAAO;EACrB;CAED,IAAI,WAAW,WAAW;EACxB,MAAM,YAAY,WAAoB,WAAW,YAAY,OAAO;EACpE,OAAO,WAAW,UAAU,WAAW;;CAIzC,OAAO,WADQ,WAAW,YAAY,EAAE,CAChB,EAAE,WAAW;;;AAIvC,SAAgB,gBACd,OACgE;CAChE,OAAOA,kBAAoB,MAAM"}