Skip to content

Adding a Platform

This guide describes how to add a new gig platform to Comma. Platforms are data-only: one definition file plus registry wiring. The shift form, terminology merge, and analytics gates read from PlatformRegistry—they should not need new if (platformId === '…') branches for a normal addition.

For broader registry philosophy, see Registry Architecture in this folder.


  1. Copy src/registry/platforms/_TEMPLATE.platform.js to src/registry/platforms/{id}.platform.js (use a lowercase slug for id, e.g. glovo).
  2. Fill required fields and optional sections (specificSchema, alertChecks, etc.).
  3. Add or reuse a logo SVG in src/registry/platforms/_logos.js.
  4. Register the module in src/registry/platforms/index.js: import + entry in the PLATFORMS array (keep other last).
  5. If the platform should appear for a province-first market (e.g. Ontario), add its id to that province’s availablePlatforms (e.g. src/registry/provinces/CA/ON.province.js).
  6. Run node build.js --prod and fix any validation issues reported at startup (assertPlatformRegistryValid in main.js).

End users never upload or paste SVG in Settings → Platforms. Activation only picks from the catalog rows already in IndexedDB; Other stays name + color only. Every bundled platform must ship a non-empty logo string in code (see Logos); the app shows that SVG in the header switcher (tabs), onboarding platform grid, and renderPlatformBadge—there is no per-user logo override in the database.


Path: src/registry/platforms/{id}.platform.js
Export: export default { … } (one catalog object per file).

These are enforced by validatePlatformDefinition in src/registry/platforms/index.js (including a non-empty logo string):

FieldNotes
idUnique string slug, lowercase (e.g. skip). Used everywhere: Dexie platforms.id, shift platformId, and other stable string keys.
nameHuman-readable label (e.g. SkipTheDishes).
colorCSS hex for badges, charts, tabs (e.g. #ED5A1F).
terminologyObject with at least driver and delivery non-empty strings. Optional: bonus, surge (used in copy and live labels).
logoRequired non-empty inline SVG string (typically imported from _logos.js). Used in the UI switcher and badges; not editable in Settings.
relevantFieldsArray of string keys for metrics/UI hints; may be [].
helpUrlSupport URL string; use '' if none.
FieldNotes
payoutWeekday06 (Sunday–Saturday) or omit; used where payout hints exist.
analyticsModulesFeature flags analytics may consult via platformAnalyticsEnabled (see below). Prefer listing every key for clarity, matching existing platforms.
specificSchemaPer-platform extra shift fields rendered automatically on the shift form for this platformId. Not used on id === 'other' by validation rules for schema rows—keep other minimal.
alertChecksOptional rules for notification-style checks (see schema in src/registry/types.js PlatformAlertCheckDef).

The canonical TypeScript-style shape is documented as PlatformCatalogEntry in src/registry/types.js.


specificSchema (platform-only shift fields)

Section titled “specificSchema (platform-only shift fields)”

Each entry drives an extra control on the advanced shift form:

specificSchema: [
{ key: 'creditsPromos', kind: 'number', min: 0 },
{ key: 'cityScore', kind: 'number', min: 0, max: 100 },
{ key: 'notes', kind: 'string' },
{ key: 'tags', kind: 'stringArray' },
{ key: 'meta', kind: 'object' },
],

Supported kind values: 'number' | 'string' | 'object' | 'stringArray'.

Optional: min, max, labelKey (i18n key under shifts.ps.* or your own t() key).

Values are stored with the shift under customFields (merged with legacy platformSpecific during save/migration). Do not invent parallel storage keys unless you extend the pipeline.


Boolean map used by src/modules/analytics/analytics.js via platformAnalyticsEnabled(platformId, module) from src/registry/platforms/terminology.js.

Keys used today (mirror a real platform file, e.g. skip.platform.js):

  • bonusTracking
  • surgeAnalysis
  • blockEarnings
  • batchTracking
  • orderTypeTracking
  • questTracking
  • promotionsTracking

Set a flag to true only if analytics logic for that module is meaningful for the new platform.


  1. Add something like export const SVG_XX = \<svg …>`; to [_logos.js`](../src/registry/platforms/_logos.js), or reuse an existing export.
  2. In {id}.platform.js: import { SVG_XX } from './_logos.js'; then logo: SVG_XX.

Keep SVG compact; it is bundled as a string.


Edit src/registry/platforms/index.js:

import myplatform from './myplatform.platform.js';
const PLATFORMS = [
doordash,
ubereats,
// …
myplatform,
other, // must remain last: fallback catalog entry
];

PlatformRegistry.getById(unknown) resolves unknown ids to other, so other stays the safe tail.


Ontario (and other provinces) expose availablePlatforms: an array of platform id strings. If your platform operates in that market, append its id there (e.g. CA/ON.province.js availablePlatforms).

If you skip this step, the catalog still loads, but onboarding and province-driven UX may not surface the platform where you expect.

For country-level defaults when no province row exists (e.g. many US states), add defaultAvailablePlatforms on the country definition; resolution order is in Market Resolution.


  • Onboarding setup JSON uses exportKind: 'comma_setup', a numeric version, and arrays of platform ids in the same lowercase form as the registry. countryId and provinceId (or tax region code) travel with the export for portable imports.
  • Treat id as immutable once users have data; renaming requires a Dexie migration mapping old → new ids.
  • Full rules: Market Resolution (exports and portability).

Base terminology comes from the platform definition. Active rows in IndexedDB platforms can carry a per-user terminology object; at runtime those merge in syncPlatformTerminologyFromRows (see terminology.js). You normally do not change code for that—only the default def in {id}.platform.js.


  • Startup: assertPlatformRegistryValid() walks every definition; invalid specificSchema / alertChecks throws during boot.
  • Build: node build.js --prod must succeed.
  • Manual: Enable the platform in Settings, open Add shift, pick the platform, and confirm specificSchema fields render and save; reload and edit to confirm round-trip.

ExampleUse when
other.platform.jsFallback: no specificSchema.
skip.platform.jsSmall schema + promotionsTracking.
doordash.platform.jsSchema + alertChecks.

AreaFile
Templatesrc/registry/platforms/_TEMPLATE.platform.js
Registry + validationsrc/registry/platforms/index.js
Logo stringssrc/registry/platforms/_logos.js
Typedefssrc/registry/types.js (PlatformCatalogEntry)
Terminology mergesrc/registry/platforms/terminology.js
Shift form extrassrc/modules/shifts/shift-form.js (reads specificSchema)
Ontario market listsrc/registry/provinces/CA/ON.province.js

If you add a new analytics module name, you must implement the corresponding checks in analytics (or leave the flag false until you do).


  • Adding a Province — province registry, availablePlatforms, and expense categories.
  • Adding a Country — country registry and tax profile before wiring provinces.