Skip to content

v2 refactor: @devrev/airsync-sdk (rename, state/adapter split, emit-from-return)#208

Draft
radovanjorgic wants to merge 22 commits into
mainfrom
v2
Draft

v2 refactor: @devrev/airsync-sdk (rename, state/adapter split, emit-from-return)#208
radovanjorgic wants to merge 22 commits into
mainfrom
v2

Conversation

@radovanjorgic

Copy link
Copy Markdown
Collaborator

v2 refactor of @devrev/airsync-sdk (formerly @devrev/ts-adaas)

Clean, commit-by-commit rebuild of the SDK for v2. Breaking changes are intended. Each commit is single-purpose and keeps npm run build green; the test suite and api-extractor report are intentionally left for the closing steps.

Draft / WIP. Phase 1 (structural) + post-review cleanup are done. Phase 2 (api report, exposure audit, tests, migration skill, final JSDoc) is pending — see checklist.

Rename & branding

  • Package @devrev/ts-adaas@devrev/airsync-sdk (2.0.0-beta.0)
  • AirdropEvent/AirdropMessageAirSyncEvent/AirSyncMessage (hard rename, no alias); prose Airdrop/ADaaSAirSync
  • Protected (NOT renamed): /internal/airdrop.* API routes, AIRDROP_* mapping enum, the 'ADaaS' literal

Deletions / cleanup

  • Removed src/deprecated/** and its barrel exports
  • Removed the old event-type compatibility layer (deprecated EXTRACTION_* enum members + event-type-translation.ts); backend now sends/accepts only the new event types
  • Removed dead pre-1.15.2 migrateProcessedAttachments shim (v2 assumes on-disk state ≥ v1.15.2)
  • Removed dead createAdapterState dispatcher (obviated by the C6 entry-point split)

State redesign

  • Split State into BaseState + ExtractionState + LoadingState
  • On-disk state is now an envelope { connectorState, sdkState }, with a v1-flat → v2-envelope migration shim on read
  • adapter.state now returns connector state only; SDK fields move to adapter.sdkState

Adapter redesign

  • Split WorkerAdapter into BaseAdapter + ExtractionAdapter + LoadingAdapter
  • emit() is now a template method (beforeEmit / buildEmitPayload / afterEmit) and SDK-internal

Execution model (emit-from-return)

  • Worker task/onTimeout now return a TaskResult (success / progress / delay / error); the SDK maps status → phase event and emits exactly once
  • processTask split into processExtractionTask + processLoadingTask
  • Loader/streaming methods return TaskResult instead of emitting mid-flight

Misc

  • Moved the emit primitive common/control-protocol.tsmultithreading/emit.ts (co-located with its only consumers)

Remaining before ready-for-review

  • C8 — regenerate api-extractor report (airsync-sdk.api.md)
  • C9 — public-surface exposure audit
  • C10 — fix tests + decide bw-compat baseline
  • C11 — migrate-v2 skill (v1→v2 catalog)
  • C7 — JSDoc pass (redone lean, as the final step)

🤖 Generated with Claude Code

radovanjorgic and others added 22 commits June 9, 2026 08:01
Self-contained plan for the v2 rebuild: branch strategy, 12-commit
sequence, hard rules, and baked-in reference data (enum old->new tables,
deprecated-file list, connector import surface).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Package identity change for the v2 AirSync rebrand. Scoped to package
identity only:
- package.json name, version (2.0.0-beta.0), description
- package-lock.json (regenerated)
- README install command
- release workflow references

Deferred to later commits: README Airdrop->AirSync prose (C2), the
api-extractor report filename + config under backwards-compatibility (C8,
test/report territory).

Ref: V2_PROGRESS.md C0

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove the entire src/deprecated/ tree (Adapter, DemoExtractor, HTTPClient,
deprecated Uploader, demo metadata) and its four barrel re-exports from
src/index.ts. These were unused by production code and any of the inspectable
connectors (zero live references).

Add tsconfig.build.json that extends tsconfig.json and excludes test files
(**/*.test.ts, src/tests). Point the 'build' script at it so 'npm run build'
compiles only shippable source. This keeps the build green across subsequent
v2 commits even while the test suite (run separately via ts-jest) lags behind
the rename/contract changes until it is fixed in Phase 2.

BREAKING CHANGE: removed deprecated exports Adapter, DemoExtractor, HTTPClient,
and the legacy Uploader from the public API.

Ref: V2_PROGRESS.md C1

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d prose

Rename the public types AirdropEvent -> AirSyncEvent and AirdropMessage ->
AirSyncMessage across all production code (no back-compat alias), and update
stale 'ADaaS'/'Airdrop' branding in comments and JSDoc to 'AirSync'.

Left untouched (platform contracts): the /internal/airdrop.* API route strings,
the AIRDROP_* mapping enum members and their 'airdrop_*' values, and the
external_system_type: 'ADaaS' string literal.

BREAKING CHANGE: AirdropEvent and AirdropMessage are renamed to AirSyncEvent
and AirSyncMessage. Connectors must update their imports and type references.

Ref: V2_PROGRESS.md C2

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The backend now sends and accepts only the new event-type values, so the
old->new translation indirection and the deprecated enum members are no
longer needed.

- Delete deprecated members from EventType and ExtractorEventType (the
  EXTRACTION_* values) and the typo/plural dupes from LoaderEventType
  (DataLoadingDelay, AttachmentsLoading*), keeping only the new members.
- Delete src/common/event-type-translation.ts and its test.
- Rewire the four callers to use event types directly with no translation:
  process-task.ts, spawn.ts (incoming), control-protocol.ts emit() and
  worker-adapter.ts emit() (outgoing).
- Drop the now-dead old-member case arms in spawn.helpers.ts (return values
  unchanged).

Behavior is unchanged: translation was identity for new values, which are
now the only values on the wire.

BREAKING CHANGE: removed deprecated enum members. Connectors must use the new
event-type members (e.g. EventType.StartExtractingMetadata instead of
EventType.ExtractionMetadataStart; LoaderEventType.DataLoadingDelayed instead
of DataLoadingDelay).

Ref: V2_PROGRESS.md C3

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tate

Structural split of the monolithic State class into a shared abstract
BaseState plus mode-specific ExtractionState and LoadingState subclasses.
The on-disk state shape is unchanged: still the flat
AdapterState = ConnectorState & SdkState (the connector/SDK envelope split
comes in a later commit).

- base-state.ts: abstract BaseState owns the shared lifecycle (init, fetch,
  postState) and installInitialDomainMappingIfNeeded, extracted from the old
  createAdapterState factory.
- extraction-state.ts: ExtractionState seeds extractionSdkState and adds
  resolveExtractionWindow (time-value resolution, pending-boundary reuse,
  lastSyncStarted, window validation); createExtractionState factory.
- loading-state.ts: LoadingState seeds loadingSdkState; createLoadingState
  factory.
- state.ts: thin module re-exporting the classes/factories; createAdapterState
  is now a dispatcher selecting the mode-specific state by event_context.mode.
- Consumers (types/workers.ts, worker-adapter.ts) reference BaseState.

Behavior is preserved. Loading mode previously ran extraction-window code
that was inert for loading events (no matching event types, no pending
boundaries in loadingSdkState); routing it to LoadingState only drops those
inert log lines.

Ref: V2_PROGRESS.md C4a

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…gin/v2)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Separate connector-owned state from SDK bookkeeping on disk. State is now
persisted as a v2 envelope { connectorState, sdkState } instead of a single
flat blob that merged both, so SDK internals stay encapsulated and can never
collide with connector keys.

- state.interfaces.ts: add AdapterStateEnvelope and V1_SDK_STATE_KEYS (the
  union of the SDK-owned initial-state keys); mark the flat AdapterState
  deprecated. SdkState stays a single combined interface (narrowing into
  per-mode variants is deferred to the adapter split).
- base-state.ts: hold _connectorState and _sdkState separately; the state
  getter/setter now exposes connector state only; add an sdkState getter/setter
  for SDK internals; init() routes fetched state through a normalizeFetchedState()
  migration shim; postState() persists the envelope.
- Migration shim: a v2 envelope is used as-is; a legacy flat v1 blob is split
  by V1_SDK_STATE_KEYS (SDK keys to sdkState, the rest to connectorState);
  a half-envelope or non-object fails loud. Existing syncs migrate on first read.
- extraction-state.ts: resolve the extraction window against sdkState.
- worker-adapter.ts: adapter.state now returns ConnectorState; add an
  adapter.sdkState getter; all internal SDK-field access goes through sdkState.
- attachments-streaming-pool.ts: read toDevRev via adapter.sdkState.

BREAKING CHANGE: adapter.state no longer exposes SDK bookkeeping fields
(lastSyncStarted, workersOldest, toDevRev, fromDevRev, snapInVersionId, ...).
Connector state is now disjoint from SDK state. Persisted v1 state is migrated
automatically on read.

Ref: V2_PROGRESS.md C4b

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the monolithic WorkerAdapter with a BaseAdapter holding the shared
state access and the emit() control-protocol flow as a template method, plus
ExtractionAdapter and LoadingAdapter subclasses that own their mode-specific
surface.

- base-adapter.ts: abstract BaseAdapter owns event/options/isTimeout/
  hasWorkerEmitted, adapterState, uploader, state/sdkState/extractionScope
  accessors, postState(), and emit(). emit() is a template method with three
  hooks: beforeEmit (pre-emit work), buildEmitPayload (mode payload extras),
  afterEmit (post-emit cleanup).
- extraction-adapter.ts: ExtractionAdapter owns repos, artifacts, attachment
  streaming/processing. beforeEmit uploads repos and updates extraction
  boundaries (incl. lastSuccessfulSyncStarted promotion); buildEmitPayload adds
  artifacts; afterEmit clears them. An override emit() handles the inline
  external-sync-unit upload before delegating to BaseAdapter.emit().
- loading-adapter.ts: LoadingAdapter owns mappers, loader reports, item and
  attachment loading. buildEmitPayload adds reports + processed_files.
- worker-adapter.helpers.ts moved to adapters/loading-adapter.helpers.ts
  (loader-only). Old worker-adapter.ts removed.
- WorkerAdapter is now a type alias ExtractionAdapter | LoadingAdapter.
  processTask builds the concrete adapter from the event's sync mode and passes
  it to task/onTimeout. Attachment processor types and the streaming pool are
  typed to ExtractionAdapter.

Behavior is preserved: emit() runs the same steps in the same order, the
mode-specific payloads are unchanged, and the extraction-boundary bookkeeping
is identical. The emit(eventType, data) signature is kept; the emit-from-return
contract change is a separate commit.

BREAKING CHANGE: WorkerAdapter is no longer a constructable class. Extraction
tasks receive an ExtractionAdapter and loading tasks a LoadingAdapter; the
methods available are scoped to the worker's mode.

Ref: V2_PROGRESS.md C5

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Connectors now signal how a phase ended by RETURNING a TaskResult from their
task/onTimeout callbacks; the SDK maps that status to the phase-appropriate
platform event and emits it exactly once. Connectors never call emit directly.

- types/workers.ts: add TaskResult ({status: 'success'|'progress'|'delay'
  (+delaySeconds)|'error' (+error)}) and TaskStatus. ProcessTaskInterface.task
  now returns Promise<TaskResult>; onTimeout is optional and also returns
  Promise<TaskResult>. TaskAdapterInterface is generic over the adapter. The
  WorkerAdapter union alias is removed.
- spawn.helpers.ts: add getEventTypeForResult + EVENT_PHASE_MAP, mapping each
  incoming EventType + status to the outgoing event. Resumable phases (data/
  attachment extraction, data/attachment loading) honour every status; non-
  resumable phases (ESU, metadata, state deletions) only have done/error, so
  a progress/delay there is illegal and emits an error with a descriptive msg.
- base-adapter.ts: emit() is now protected (SDK-internal). New public
  emitFromResult(result) — the driver-invoked bridge that picks the event from
  the mapping and emits with the right payload. State is still saved before
  emit for non-stateless events.
- extraction-adapter.ts: streamAttachments returns a TaskResult (timeout ->
  progress, pool error/delay -> error/delay, otherwise success) instead of
  emitting and exiting. The inline external-sync-unit emit override is removed
  (ESUs flow through the repo; connectors push then return success).
- loading-adapter.ts: loadItemTypes and loadAttachments return a TaskResult;
  their former mid-flight emits (rate-limit -> delay, timeout -> progress,
  error -> error) become returns. They no longer call process.exit.
- process-task.ts: processTask is split into processExtractionTask and
  processLoadingTask over a shared runWorkerTask driver that runs task (then
  onTimeout, or a default progress result, on timeout) and emits once from the
  returned TaskResult.
- index.ts: export processExtractionTask + processLoadingTask; remove
  processTask. emit is no longer part of the public surface.

BREAKING CHANGE: connectors must return a TaskResult from task/onTimeout
instead of calling adapter.emit(...), and import processExtractionTask /
processLoadingTask instead of processTask. adapter.emit is no longer public;
the loader and attachment-streaming methods return a TaskResult to be returned
from the task. onTimeout is now optional (defaults to a progress handoff).

Ref: V2_PROGRESS.md C6

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Documentation-only pass (C7). Brings the v2-new structural code and the
under-documented older modules up to the `src/mappers/mappers.ts` style bar
(one-line what + "Used to/for ..." usage line + typed @param/@returns).

Covered:
- adapters: BaseAdapter template-method emit + emitFromResult, ExtractionAdapter
  (repos/artifacts/attachment streaming), LoadingAdapter (mappers/reports/loading).
- state: BaseState lifecycle, the v1->v2 normalizeFetchedState migration shim,
  ExtractionState window resolution, the mode-based createAdapterState dispatcher,
  and state.interfaces (SdkState, envelope, V1_SDK_STATE_KEYS).
- multithreading: processExtractionTask/processLoadingTask + runWorkerTask driver,
  spawn/Spawn supervision, and the getEventTypeForResult / EVENT_PHASE_MAP
  status->event mapping.
- repo, uploader (+helpers/interfaces), attachments-streaming-pool.
- common: control-protocol emit, install-initial-domain-mapping, errors;
  types/loading (previously zero JSDoc) and the types barrel.

No executable code, type signatures, imports, or string literals changed
(verified: every changed line is a comment). Build green, lint clean.
Three independent cleanups from the v2 self-review:

1. Drop the pre-1.15.2 processed-attachments migration. `migrateProcessedAttachments`
   only upgraded the legacy `string[]` form of `lastProcessedAttachmentsIdsList` to
   the structured `ProcessedAttachment[]` form. That `string[]` format predates
   v1.15.2 (commit c3aa151, Feb 2026); from v1.15.2 on, state is already written as
   `ProcessedAttachment[]`, and the list is per-cycle scratch state cleared at the end
   of each completed attachment phase. v2 assumes on-disk state written by >= v1.15.2
   (connectors are on >= 1.16, typically 1.19, before moving to v2), so the shim is
   dead weight. The call site now just initializes the list to [] when absent.

2. Move the `emit` primitive `common/control-protocol.ts` -> `multithreading/emit.ts`.
   The file's sole export is `emit`, used only by the multithreading layer
   (base-adapter, spawn) — co-locating it with its consumers and naming it after its
   content. Import paths updated.

3. Remove the dead `createAdapterState` dispatcher from `state.ts`. After C6 split the
   single entry point into processExtractionTask/processLoadingTask, process-task.ts
   calls createExtractionState/createLoadingState directly; the mode dispatcher had
   zero callers. `state.ts` is now a thin re-export barrel.

Build green, lint clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant