// runtime/subsystems/integration/incremental-read.ts
async function mapConcurrent(ids, fn, limit) {
  const out = /* @__PURE__ */ new Map();
  if (ids.length === 0) return out;
  const width = Math.max(1, Math.min(limit, ids.length));
  let next = 0;
  const worker = async () => {
    while (next < ids.length) {
      const idx = next++;
      const id = ids[idx];
      out.set(id, await fn(id));
    }
  };
  await Promise.all(Array.from({ length: width }, worker));
  return out;
}
var IncrementalReadBase = class {
  /**
   * Whether the vendor takes the request predicate server-side. Declared, not
   * enforced here — surfaced into the emission manifest (R3) so the falsifier
   * suite (R4) can record which adapters filter post-hydrate. `false` is the
   * honest floor (e.g. Gmail without `q=`), handled via `matchesRecord`.
   */
  filterPushdown = false;
  /** Max concurrent in-flight calls for a `mapConcurrent`-built `hydrate`. */
  hydrateConcurrency = 10;
  /** `Change<T>.source` provenance stamped by `listChanges`. */
  changeSource = "poll";
  /**
   * Whether this source's cursor strategy is divisible (RFC-0003 §3). When
   * `true` (default — sortable watermarks like `systemModstamp`/`timestamp`/
   * `replayId`), `listChanges` emits each record's per-ref cursor, so the
   * orchestrator may checkpoint mid-walk and a crash resumes from the last
   * delivered ref.
   *
   * When `false` (atomic opaque tokens — Gmail `historyId`, Calendar
   * `syncToken`), `listChanges` WITHHOLDS per-ref cursors and emits the
   * end-of-walk token only on the final record, so the orchestrator's
   * persist-last-yielded lifecycle can never persist an unresumable mid-walk
   * token. The cost is blast-radius: an interrupted atomic run resumes
   * all-or-nothing from the prior persisted token. For atomic *backfills* that
   * radius is the whole enumerate walk — bound it with `ReadRequest.pageSize`
   * (smaller pages ⇒ shorter walks per run). Per-page atomic checkpointing is a
   * future refinement; R2 gates at end-of-walk.
   *
   * Codegen (R3) sets this from the strategy kind via `isDivisibleCursor`.
   */
  cursorDivisible = true;
  // ---- Optional filter hooks — exactly one is live per `filterPushdown` ----
  /** Pre-hydrate predicate over the cheap ref (preferred — avoids hydration). */
  matchesRef(_ref, _filter) {
    return true;
  }
  /** Post-hydrate predicate over the canonical record (the no-pushdown floor). */
  matchesRecord(_record, _filter) {
    return true;
  }
  /**
   * Resolve the filter for a subscription when adapting to `listChanges`
   * (which has no filter argument). Defaults to none; codegen wiring (R3)
   * overrides this to thread `DetectionConfig.filters`.
   */
  filterFor(_subscription) {
    return void 0;
  }
  // ---- PROVIDED by the base ----
  /**
   * Stream canonical records for a request. Filter is applied BEFORE hydrate
   * (structural: a kept ref is hydrated, a rejected one never is), so an
   * adapter cannot hydrate-then-discard. A hydrate miss (deleted mid-run) is
   * skipped, never fabricated.
   */
  async *read(req, ctx) {
    for await (const refPage of this.enumerate(req.mode, req.filter, req.pageSize, ctx)) {
      const kept = refPage.filter((ref) => this.matchesRef(ref, req.filter));
      if (kept.length === 0) continue;
      const raws = await this.hydrate(
        kept.map((ref) => ref.externalId),
        ctx
      );
      for (const ref of kept) {
        const raw = raws.get(ref.externalId);
        if (raw === void 0 || raw === null) continue;
        const record = this.toCanonical(raw);
        if (record !== null && this.matchesRecord(record, req.filter)) {
          yield { externalId: ref.externalId, record, raw, cursor: ref.cursor };
        }
      }
    }
  }
  /**
   * `RandomRead<T>` — single-record read, provided for free as
   * `toCanonical ∘ hydrate([id])`. Reuses the adapter's batched fetch + miss
   * tolerance; returns `null` for a missing or undecodable record.
   */
  async get(id, ctx) {
    const raws = await this.hydrate([id], ctx);
    const raw = raws.get(id);
    if (raw === void 0 || raw === null) return null;
    return this.toCanonical(raw);
  }
  /**
   * `IChangeSource<T>` adaptation. Maps the orchestrator's by-value cursor to a
   * `ReadMode` (`null` → `full` backfill, else `delta`), streams `read()`, and
   * stamps each `SourcedRecord` into a `Change<T>`. All records surface as
   * `'updated'`; the orchestrator's diff stage classifies create-vs-update and
   * deletes arrive as tombstone refs (`toCanonical` may flag them).
   *
   * Cursor emission honors `cursorDivisible` (RFC-0003 §3). Divisible: each
   * record carries its own per-ref cursor. Atomic: per-ref cursors are withheld
   * (`undefined`, which the orchestrator skips persisting) and the end-of-walk
   * token rides only on the final record — so a mid-walk crash never persists
   * an unresumable token. If an atomic run yields no surviving records, no
   * cursor is persisted and the next run re-reads the same (empty) delta — a
   * bounded inefficiency, never data loss.
   */
  async *listChanges(subscription, cursor) {
    const mode = cursor === null || cursor === void 0 ? { kind: "full" } : { kind: "delta", cursor };
    const filter = this.filterFor(subscription);
    const stream = this.read({ mode, filter }, { subscription });
    if (this.cursorDivisible) {
      for await (const sourced of stream) {
        yield this.toChange(sourced, sourced.cursor);
      }
      return;
    }
    let prev = null;
    for await (const sourced of stream) {
      if (prev !== null) yield this.toChange(prev, void 0);
      prev = sourced;
    }
    if (prev !== null) yield this.toChange(prev, prev.cursor);
  }
  /** Stamp a `SourcedRecord` into a `Change<T>` with an explicit emitted cursor. */
  toChange(sourced, cursor) {
    return {
      externalId: sourced.externalId,
      operation: "updated",
      record: sourced.record,
      cursor,
      source: this.changeSource
    };
  }
};

export {
  mapConcurrent,
  IncrementalReadBase
};
//# sourceMappingURL=chunk-LG57S2SC.js.map