// runtime/subsystems/integration/poll-change-source.ts
var PollChangeSource = class {
  label;
  adapter;
  filters;
  externalIdSourceField;
  source;
  /**
   * When `poll.provenance === 'cdc'`, the field on the emitted record from
   * which `Change<T>.dedupKey` is read — sourced from `poll.cursor.field`
   * (Stripe-style event endpoints carry the event id on the record itself
   * and the cursor advances over the same id). `null` for default poll
   * provenance, which does NOT populate `dedupKey`.
   */
  dedupKeySourceField;
  composed;
  constructor(opts) {
    if (opts.config.mode !== "poll") {
      throw new Error(
        `PollChangeSource requires DetectionConfig.mode === 'poll'; got '${opts.config.mode}'`
      );
    }
    const config = opts.config;
    const externalIdMapping = config.mapping.find(
      (m) => m.target === "external_id"
    );
    if (!externalIdMapping) {
      throw new Error(
        "PollChangeSource: DetectionConfig.mapping must include an entry with target 'external_id' so emitted Change<T>.externalId can be populated"
      );
    }
    this.externalIdSourceField = externalIdMapping.source;
    this.adapter = opts.adapter;
    this.filters = config.filters;
    const isCdc = config.poll.provenance === "cdc";
    this.source = isCdc ? "cdc" : "poll";
    this.dedupKeySourceField = isCdc ? config.poll.cursor.field : null;
    this.label = opts.label ?? `poll-change-source:${externalIdMapping.source}`;
    const inner = (sub, cur) => this.fetch(sub, cur);
    const middlewares = opts.middlewares ?? [];
    this.composed = middlewares.reduceRight(
      (next, mw) => mw(next),
      inner
    );
  }
  listChanges(subscription, cursor) {
    return this.composed(subscription, cursor);
  }
  async *fetch(subscription, cursor) {
    const ctx = {
      subscription,
      cursor,
      filters: this.filters
    };
    for await (const { record, cursor: nextCursor } of this.adapter(ctx)) {
      const externalIdRaw = record[this.externalIdSourceField];
      if (typeof externalIdRaw !== "string" || externalIdRaw.length === 0) {
        throw new Error(
          `PollChangeSource: record missing string '${this.externalIdSourceField}' \u2014 emitted records MUST carry the canonical external id keyed by the mapping source`
        );
      }
      let dedupKey;
      if (this.dedupKeySourceField !== null) {
        const dedupRaw = record[this.dedupKeySourceField];
        if (typeof dedupRaw !== "string" || dedupRaw.length === 0) {
          throw new Error(
            `PollChangeSource: cdc-provenance record missing string '${this.dedupKeySourceField}' \u2014 when poll.provenance === 'cdc' the cursor.field must be present on each record so dedupKey can be populated`
          );
        }
        dedupKey = dedupRaw;
      }
      const change = {
        externalId: externalIdRaw,
        // Polling cannot distinguish create vs. update vs. delete on its
        // own — all yielded records are surfaced as 'updated'. The
        // orchestrator's diff stage classifies create-vs-update against
        // local state; soft-delete detection is out of scope for the
        // primitive (consumer drives via tombstone records or a separate
        // sweep — see ADR-033).
        operation: "updated",
        record,
        cursor: nextCursor,
        source: this.source,
        ...dedupKey !== void 0 ? { dedupKey } : {}
      };
      yield change;
    }
  }
};

export {
  PollChangeSource
};
//# sourceMappingURL=chunk-4MF3HKJA.js.map