ThemeParks.wiki Parks API - v2.0.0
    Preparing search index...

    Class DestinationAbstract

    Index
    allowEmptyEntityList: boolean = false

    Opt out of the collapsed-entity-list guard in getEntities.

    Only for a destination that legitimately publishes no attractions, restaurants or shows — a listing that exists to carry opening hours, say. A destination that normally has content and simply lost it upstream must NOT set this: the whole point of the guard is that losing everything looks identical to having nothing.

    false
    
    cacheKeyPrefix?: string

    Optional cache key prefix for all cached methods. When set, this prefix is prepended to all cache keys, preventing cache collisions when multiple instances of the same base class exist (e.g., multiple parks using the same framework).

    Can be set directly as a string property, or implement getCacheKeyPrefix() method for dynamic prefixes.

    class MyPark extends Destination {
    constructor(options) {
    super(options);
    this.cacheKeyPrefix = `mypark:${this.parkId}`;
    }
    }
    config: { [key: string]: string | string[] } = {}
    hasLiveStream: boolean = false

    Whether this destination supports a real-time live data stream. Parks with WebSocket or database sync feeds set this to true. The collector checks this to decide whether to run a stream loop.

    false
    
    httpQueue: HttpQueue = ...
    language:
        | "en"
        | "en-gb"
        | "en-us"
        | "de"
        | "fr"
        | "es"
        | "it"
        | "nl"
        | "ja"
        | "ko"
        | "zh" = 'en'

    Default language for localized strings Can be overridden by subclasses or via {PREFIX}_LANGUAGE env var with

    decorator

    'en'
    
    liveEntityRetirementMaxFraction: number = 0.5

    Fraction of tracked entities that may retire in one snapshot before the gate assumes the feed is degraded rather than the entities retired.

    A source can return a well-formed but gutted payload — an empty array, a CDN stub, a partial regeneration — which parses cleanly and so never throws. Retiring on that publishes a confident CLOSED for a park that is open. Mass disappearance is far more often a broken feed than a mass retirement, so past this share the gate declines to act and warns.

    Measured against raw absence rather than against the entities that are eligible to close, because eligibility needs the age window to elapse and a guard that waited for it could not see a collapse until the collapse had already outlived it.

    Known limit: this catches simultaneous collapses, not staggered ones. A source shedding rows in waves, with a retirement window between them, keeps every wave under the threshold and is genuinely indistinguishable from progressive retirement without venue-level grouping. Every simultaneity-based guard shares this, the collector's own bulk-close guard included. Grouping the fraction per parkId is the refinement if it ever bites in practice.

    0.5
    
    liveEntityRetirementMinBulk: number = 5

    Floor below which liveEntityRetirementMaxFraction does not apply. A destination tracking a handful of entities can legitimately retire most of them at once; the proportional guard is aimed at collapses measured in dozens.

    5
    
    liveEntityRetirementMinMisses: number = 3

    How many consecutive successful snapshots an entity must be absent from before it can retire, on top of liveEntityRetirementMs.

    Age alone is wall-clock, and nothing advances it while the source is down: a proxy block, a container stop or a deploy gap longer than the window all leave every timestamp stale, so the first poll to succeed afterwards would retire everything missing from that one sample on the strength of a single observation. Requiring the absence to repeat means a close always rests on several builds.

    This also sets how long a collapse must persist before the degraded-feed guard arms, which couples it to the collector's polling interval across repo boundaries: liveEntityRetirementMinMisses × pollInterval has to land comfortably inside liveEntityRetirementMs, or the guard cannot arm before the age window opens and a mistimed recovery is closed silently again. Universal is the tightest case at 3 × 45 minutes against a four hour window, a 1.78x margin; the boundary sits at a 80 minute poll. Raising the collector's closed-park interval past that, or dropping a destination's window below 135 minutes, reopens the hole.

    3
    
    liveEntityRetirementMs: number = ...

    How long an entity may be missing from a full buildLiveData() snapshot before retireMissingLiveEntities presumes it retired and force-closes it. Conservative default: the confirmed real-world cases were stale 17 and 50+ days, so a week already improves on both by an order of magnitude while tolerating a normal multi-day show hiatus. Override per-destination if evidence supports a tighter or looser window.

    7 days
    
    liveEntityRetirementRepeatMs: number = ...

    How long a retired entity keeps having its CLOSED row re-appended before the gate forgets it entirely.

    The repeat exists so a close that was built but never delivered is not lost for good. It cannot run forever, though: a permanently dead id would otherwise count as fresh evidence of a collapse on every future build, and enough of them accumulated across seasons would trip liveEntityRetirementMaxFraction for good and silently disable the gate. A few days covers any rejected batch or dead process by orders of magnitude, and bounds the pile.

    3 days
    
    proxyConfig: ProxyConfig | null = null
    retireMissingLiveEntities: boolean = false

    Opt-in: force-close a previously-live entity once it has been absent from a full buildLiveData() snapshot for longer than liveEntityRetirementMs.

    The collector is upsert-only with no delete path, so simply omitting a retired entity from buildLiveData() output achieves nothing — the last value sits there indefinitely (see nigloland.ts RIDE_RETIREMENT_MS for the same trap on a wait-time signal). Confirmed independently on two park modules: a seasonal show run ends, the entity leaves the upstream feed entirely, and its live row freezes mid-run — 50+ days OPERATING on one, 17+ days on another (parksapi #74, #83).

    Off by default. A destination whose buildLiveData() legitimately omits entities for unrelated reasons (a genuinely partial feed, a bug) would have them wrongly force-closed, so this only applies where the pattern has actually been confirmed. Only ever applied to a full snapshot (scope undefined) — a partial/streaming build's absentees carry no meaning and are never gated.

    false
    
    timezone: string = 'UTC'

    Timezone for this destination. Subclasses should override this with the park's local timezone. Used by virtual queue helpers to format dates in the park's timezone.

    'UTC'
    
    • Protected

      Internal initialization hook for subclasses

      Override this method to provide custom initialization logic. Called once per instance by init().

      Returns Promise<void>

    • Inject proxy settings into HTTP requests. Runs last (priority 999) so all auth/header injectors fire before the URL is rewritten to point at the proxy service.

      Parameters

      Returns Promise<void>

    • Unwrap proxy responses (e.g., Scrapfly wraps responses in JSON). Runs first (priority -999) so downstream response handlers see the unwrapped response, not the proxy envelope.

      Parameters

      Returns Promise<void>

    • Add a prefix to use when looking up config values from environment variables or config object. This allows multiple destinations to co-exist in the same environment without clashing on config keys.

      Parameters

      • prefix: string

        Prefix to add to config lookups (e.g. 'UNIVERSAL' to check UNIVERSAL_ env vars)

      Returns void

    • Helper to build boarding group queue data

      Constructs a BOARDING_GROUP queue object with allocation information. Automatically formats dates in the destination's timezone.

      Parameters

      • status: "AVAILABLE" | "PAUSED" | "CLOSED"

        Boarding group status (AVAILABLE, PAUSED, or CLOSED)

      • Optionaloptions: {
            currentGroupEnd?: number | null;
            currentGroupStart?: number | null;
            estimatedWait?: number | null;
            nextAllocationTime?: string | Date | null;
        }

        Additional boarding group information

      Returns {
          allocationStatus: "AVAILABLE" | "PAUSED" | "CLOSED" | null;
          currentGroupEnd: number | null;
          currentGroupStart: number | null;
          estimatedWait: number | null;
          nextAllocationTime: string | null;
      }

      Boarding group queue object

      • allocationStatus: "AVAILABLE" | "PAUSED" | "CLOSED" | null

        State of boarding group availability

      • currentGroupEnd: number | null

        Current boarding group end number

      • currentGroupStart: number | null

        Current boarding group start number

      • estimatedWait: number | null

        Estimated wait time in minutes

      • nextAllocationTime: string | null

        Next boarding group allocation time

      // In buildLiveData() for Rise of the Resistance
      liveData.queue!.BOARDING_GROUP = this.buildBoardingGroupQueue('AVAILABLE', {
      currentGroupStart: 45,
      currentGroupEnd: 60,
      estimatedWait: 30
      });
    • Build the list of entities for this destination

      Subclasses should override this method to return their entities. The returned entities will automatically have their parkId and destinationId resolved based on the parent hierarchy.

      Returns Promise<Entity[]>

      List of entities (hierarchy will be resolved automatically)

    • Build live data for all entities in this destination

      Subclasses should override this method to return live data (wait times, operating status, showtimes, etc.) for their entities.

      Parameters

      • Optionalscope: ReadonlySet<string>

        Optional set of published entity ids to limit the build to (see getLiveData). Streaming destinations may honour it to build only the changed entities; an override that ignores it returns the full snapshot.

      Returns Promise<LiveData[]>

      List of live data for entities

    • Build a real-time stream of live data updates

      Override this method in parks that have a WebSocket or database sync feed. The default implementation is an empty generator (returns immediately, never yields).

      The connection to the live data source should be opened in _init(), not here. This method subscribes to the already-open connection and yields updates as they arrive.

      Returns AsyncGenerator<LiveData[]>

      Async generator yielding LiveData[] per update

    • Helper to build paid return time queue data

      Constructs a PAID_RETURN_TIME queue object with pricing information. Automatically formats dates in the destination's timezone.

      Parameters

      • state: "AVAILABLE" | "TEMP_FULL" | "FINISHED"

        Queue state (AVAILABLE, TEMP_FULL, or FINISHED)

      • returnStart: string | Date | null

        Start time of return window (Date or ISO string)

      • returnEnd: string | Date | null

        End time of return window (Date or ISO string)

      • currency: string

        Currency code (e.g., 'USD', 'EUR')

      • amountCents: number | null

        Price in cents (e.g., 1500 for $15.00)

      Returns {
          price: PriceData | null;
          returnEnd: string | null;
          returnStart: string | null;
          state: "AVAILABLE" | "TEMP_FULL" | "FINISHED" | null;
      }

      Paid return time queue object

      • price: PriceData | null

        Price information for paid return time

      • returnEnd: string | null

        End time of return window

      • returnStart: string | null

        Start time of return window

      • state: "AVAILABLE" | "TEMP_FULL" | "FINISHED" | null

        State of return time availability

      // In buildLiveData() for Lightning Lane/Express Pass
      liveData.queue!.PAID_RETURN_TIME = this.buildPaidReturnTimeQueue(
      'AVAILABLE',
      new Date('2024-10-15T14:30:00'),
      null,
      'USD',
      1500
      );
    • Helper to build return time queue data

      Constructs a RETURN_TIME queue object with proper formatting. Automatically formats dates in the destination's timezone.

      Parameters

      • state: "AVAILABLE" | "TEMP_FULL" | "FINISHED"

        Queue state (AVAILABLE, TEMP_FULL, or FINISHED)

      • returnStart: string | Date | null

        Start time of return window (Date or ISO string)

      • returnEnd: string | Date | null

        End time of return window (Date or ISO string)

      Returns {
          returnEnd: string | null;
          returnStart: string | null;
          state: "AVAILABLE" | "TEMP_FULL" | "FINISHED" | null;
      }

      Return time queue object

      • returnEnd: string | null

        End time of return window

      • returnStart: string | null

        Start time of return window

      • state: "AVAILABLE" | "TEMP_FULL" | "FINISHED" | null

        State of return time availability

      // In buildLiveData()
      liveData.queue!.RETURN_TIME = this.buildReturnTimeQueue(
      'AVAILABLE',
      new Date('2024-10-15T14:30:00'),
      new Date('2024-10-15T14:45:00')
      );
    • Build schedules for all entities in this destination

      Subclasses should override this method to return operating hours, show times, and other schedule information for their entities.

      Returns Promise<EntitySchedule[]>

      List of schedules for entities

    • Calculate return window based on current wait time

      Common pattern for parks like Efteling where virtual queue window is calculated as: now + waitTime to now + waitTime + windowDuration

      Parameters

      • waitMinutes: number

        Wait time in minutes to add to base time

      • Optionaloptions: { baseTime?: Date; windowMinutes?: number }

        Optional configuration

      Returns { end: string; start: string }

      Object with formatted start and end times

      // In buildLiveData() for calculated return windows
      const window = this.calculateReturnWindow(45, { windowMinutes: 15 });
      liveData.queue!.RETURN_TIME = this.buildReturnTimeQueue(
      'AVAILABLE',
      window.start,
      window.end
      );
    • Fetch the latest version of an app from the themeparks.wiki appwatch mirror (Play Store metadata). Used by getAppwatchVersion(); subclasses shouldn't call this directly.

      Parameters

      • packageId: string

      Returns Promise<HTTPObj>

    • Get the latest published version string of a mobile app, via appwatch. Returns fallback when appwatch is unreachable or doesn't have the version field. Cached 12h per (subclass, packageId) — invalidate the cache entry from a response-error handler if the upstream API ever starts gating on version.

      Parameters

      • packageId: string
      • fallback: string = ''

      Returns Promise<string>

      const v = await this.getAppwatchVersion('com.example.app', this.appVersion);
      headers['App-Version'] = v;
    • Optional method to dynamically generate a cache key prefix. If implemented, this takes precedence over the cacheKeyPrefix property. Can return a string or Promise.

      Returns string | Promise<string>

      class MyPark extends Destination {
      getCacheKeyPrefix() {
      return `mypark:${this.parkId}`;
      }
      }
    • Get all entities (parks, attractions, dining, shows, hotels) for this destination

      ⚠️ DO NOT OVERRIDE THIS METHOD ⚠️

      This method automatically calls init() before fetching entities, and calls resolveEntityHierarchy() on the returned entities to set parkId and destinationId based on parent relationships.

      To provide entities, implement buildEntityList() instead.

      Returns Promise<Entity[]>

      List of entities with resolved hierarchy

      This method is final and should not be overridden.

    • Get live data for all entities in this destination

      ⚠️ DO NOT OVERRIDE THIS METHOD ⚠️

      This method automatically calls init() before fetching live data. If you need to provide post-processing or validation of live data, consider using the transform pattern in buildLiveData() instead.

      To provide live data, implement buildLiveData() instead.

      Parameters

      • Optionalscope: ReadonlySet<string>

        Optional set of published entity ids to limit the build to. Streaming (push) destinations pass the ids whose source docs changed this fire so the build is emit-per-changed-entity rather than a full snapshot. Undefined (the default, and all poll/REST destinations) builds everything. Passed through to buildLiveData(); subclasses that ignore it stay full-snapshot.

      Returns Promise<LiveData[]>

      List of live data for entities

      This method is final and should not be overridden.

    • Get localized string value with fallback logic

      Handles both simple strings and multi-language objects. For multi-language objects, uses intelligent fallback: tries exact match, then base language (en-gb -> en), then fallback language, then first available.

      Parameters

      • value: LocalisedString

        LocalisedString (string or multi-language object)

      • Optionallanguage: "en" | "en-gb" | "en-us" | "de" | "fr" | "es" | "it" | "nl" | "ja" | "ko" | "zh"

        Preferred language code (defaults to instance language config)

      • fallbackLanguage: "en" | "en-gb" | "en-us" | "de" | "fr" | "es" | "it" | "nl" | "ja" | "ko" | "zh" = 'en'

        Fallback language if preferred unavailable (defaults to 'en')

      Returns string

      Localized string value

      // Simple string - returns as-is
      this.getLocalizedString("Space Mountain") // => "Space Mountain"

      // Multi-language object with exact match
      this.getLocalizedString({ en: "Space Mountain", fr: "Space Mountain" }, "fr")
      // => "Space Mountain"

      // Multi-language with base language fallback
      this.getLocalizedString({ en: "Space Mountain" }, "en-gb")
      // => "Space Mountain" (falls back to 'en')
    • Get schedules for all entities in this destination

      ⚠️ DO NOT OVERRIDE THIS METHOD ⚠️

      This method automatically calls init() before fetching schedules. If you need to provide post-processing or validation of schedules, consider using the transform pattern in buildSchedules() instead.

      To provide schedules, implement buildSchedules() instead.

      Returns Promise<EntitySchedule[]>

      List of schedules for entities

      This method is final and should not be overridden.

    • The latest upstream snapshot time recorded for each park, one entry per park. Empty for a destination whose source carries no stamp.

      A park the latest build did not read keeps its previous entry, with its previous readAt, so a caller should judge recency from readAt rather than assume every entry was read by the latest build.

      Returns SourceObservation[]

    • Initialize the destination

      This method is called automatically before any data retrieval methods (getEntities, getLiveData, getSchedules). It runs only once per instance, even if called multiple times, thanks to @reusable({forever: true}).

      Subclasses should override _init() instead of this method.

      Returns Promise<void>

      class MyPark extends Destination {
      protected async _init() {
      await this.connectToDatabase();
      await this.loadConfig();
      }
      }
    • Map array of source items to Entity objects

      Helper method to reduce boilerplate when converting API responses to Entity objects. Provides declarative mapping configuration instead of manual object construction.

      Note: This method does NOT set parkId automatically. Use resolveEntityHierarchy() after mapping all entities to correctly populate parkId and destinationId based on the parent chain.

      Type Parameters

      • T

      Parameters

      Returns Entity[]

      const entities = this.mapEntities(apiRides, {
      idField: 'Id',
      nameField: 'MblDisplayName',
      entityType: 'ATTRACTION',
      parentIdField: 'VenueId',
      locationFields: { lat: 'Latitude', lng: 'Longitude' },
      destinationId: 'universalorlando',
      timezone: 'America/New_York',
      filter: (ride) => ride.IsActive === true,
      });
    • Record a park's upstream snapshot time, for sources that publish one. Call from buildLiveData() whenever the source's stamp was read, including when the snapshot is then withheld: that is when the age matters most.

      A non-finite time or an empty park id is ignored, so a parse failure can never read as a timestamp.

      Parameters

      • parkId: string

        The park's entity id.

      • observedAt: number | Date

        When the source says its snapshot was generated.

      • readAt: number | Date = ...

        When the snapshot was read. Defaults to now.

      Returns void

    • Resolve entity hierarchy relationships (parkId and destinationId)

      Walks the parent chain for each entity to correctly set parkId and destinationId based on ancestor types. This handles edge cases like:

      • Attractions at destinations (no park) vs attractions at parks
      • Hotels inside parks vs hotels at destinations
      • Transport between parks vs transport within parks

      Rules:

      • DESTINATION entities: no parent, no parkId, destinationId = self
      • PARK entities: parent should be DESTINATION, no parkId
      • All other entities: parkId = first PARK ancestor (if any), destinationId = first DESTINATION ancestor

      Validation:

      • Throws error if circular parent references detected
      • Throws error if any entity has no DESTINATION in parent chain
      • Throws error if any PARK has no DESTINATION parent

      Parameters

      • entities: Entity[]

        Array of entities to resolve

      Returns Entity[]

      Same array with parkId and destinationId correctly set

      If circular references or missing destination in hierarchy

      async getEntities(): Promise<Entity[]> {
      const entities = [
      ...this.mapEntities(parks, ...),
      ...this.mapEntities(attractions, ...),
      ];
      return this.resolveEntityHierarchy(entities);
      }
    • Stream live data updates in real time

      Returns an async generator that yields LiveData[] arrays as updates arrive from the upstream feed. Each yield contains only the item(s) that changed — a single-element array for per-document sources (e.g. Couchbase Lite), or a full snapshot for sources that send all data at once (e.g. WebSocket).

      For parks without a live feed (hasLiveStream = false), the generator returns immediately without yielding.

      The generator ends when the upstream connection closes. The caller is responsible for reconnecting by calling streamLiveData() again in a loop.

      Returns AsyncGenerator<LiveData[]>