Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- `strait test`: one pass/fail list that says you're set up right: the engine answers, the secret key is valid (which workspace, test or live), and the workspace's link domain serves apple-app-site-association and assetlinks.json. Exit 1 on any failure, `--json` for CI.
- `strait events add <name>`: prints ready-to-paste event tracking code for React Native, Flutter, iOS, Android or the web (detected from the project files, or `--platform`), using each SDK's real `trackEvent` call.
- `strait events add --platform web` prints `@straitlink/web`'s new `trackEvent` instead of a hand-written `POST /v1/event` (needs the web SDK release that adds it).

## 0.2.2 (4 Nov 2026)

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ npx @straitlink/cli events add sign_up --platform ios # react-native | flutter
npx @straitlink/cli events add purchase ./mobile # [dir]: the app folder, default the current one
```

Prints ready-to-paste code with each SDK's own call: `trackEvent` in React Native (and Expo), Flutter, iOS and Android, using the client `strait init` wrote; on the web, `reportWebOpen` plus the `POST /v1/event` the app SDKs send (`@straitlink/web` has no event helper yet). The platform is detected from `package.json`, `pubspec.yaml`, Gradle files, an `.xcodeproj` or `Package.swift`. Nothing is written. Event names are 1-64 letters, numbers, spaces and `_ . : -`.
Prints ready-to-paste code with each SDK's own call: `trackEvent` in React Native (and Expo), Flutter, iOS and Android, using the client `strait init` wrote; on the web, `reportWebOpen` on page load plus `trackEvent` from `@straitlink/web`. The platform is detected from `package.json`, `pubspec.yaml`, Gradle files, an `.xcodeproj` or `Package.swift`. Nothing is written. Event names are 1-64 letters, numbers, spaces and `_ . : -`.

## Install lines: one constant

Expand Down
1 change: 1 addition & 0 deletions docs/adr/0001-strait-test-and-events-add.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ A developer with a secret key and an app, in a terminal or a CI job.
the existing `runDoctor` on the workspace's link domain. `strait events add` prints the one call each
SDK already has (`trackEvent` in React Native, Flutter, iOS, Android; the web SDK has no event helper,
so it prints the same `POST /v1/event` its own example uses). No engine, SDK or database change.
*Update 5 Nov 2026:* the web SDK gained `trackEvent` (strait-sdk-web ADR 0002); the web snippet now prints it.
- **New engine endpoint for a combined check.** Rejected: everything needed is already served.
- **Write the event code into the user's files.** Rejected: we can't know where their purchase happens;
printing is safe and reviewable.
Expand Down
24 changes: 7 additions & 17 deletions src/events.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// Flutter 0.9 links.trackEvent(name, value:, currency:, linkId:, clickId:) lib/src/client.dart (no eventId)
// iOS 0.10 strait.trackEvent(_:value:currency:linkId:clickId:eventId:completion:) StraitLinks.swift
// Android 0.8 client.trackEvent(name, value, currency, linkId, clickId, eventId) StraitClient.kt (blocks)
// Web 0.7 no event helper: the same POST /v1/event as sdk-web/examples/web/src/main.ts
// Web (Unreleased) trackEvent(config, name, { value, currency, eventId }) src/index.ts (sdk-web ADR 0002)
// `links` / `StraitLinks.client` are the names `strait init` writes (templates.mjs).
import { existsSync, readdirSync } from 'node:fs';
import { join } from 'node:path';
Expand Down Expand Up @@ -86,25 +86,15 @@ strait.trackEvent("${n}") { ok in
// For revenue add value = 49.99, currency = "USD", eventId = order.id (a retry is counted once).
val ok = StraitLinks.client.trackEvent("${n}")
`,
web: (n, { key, host }) => `import { reportWebOpen } from '@straitlink/web';
web: (n, { key, host }) => `import { reportWebOpen, trackEvent } from '@straitlink/web';

const strait = { publishableKey: '${key}', endpoint: 'https://${host}' };

// On page load: remember the tap this visit came from.
const open = await reportWebOpen(strait);
if (open.clickId) localStorage.setItem('strait_click', open.clickId);
// On page load: counts the open and remembers the tap this visit came from.
await reportWebOpen(strait);

// Where "${n}" happens. @straitlink/web has no event helper yet, so this is the same
// POST /v1/event the app SDKs send. For revenue add value: 49.99, currency: 'USD', eventId: order.id
await fetch(\`\${strait.endpoint}/v1/event\`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
publishableKey: strait.publishableKey,
event: '${n}',
platform: 'web',
clickId: localStorage.getItem('strait_click') ?? undefined,
}),
});
// Where "${n}" happens. Resolves true when Strait took it; never throws. For revenue add
// { value: 49.99, currency: 'USD', eventId: order.id } (eventId: a repeat is counted once).
await trackEvent(strait, '${n}');
`,
};
9 changes: 7 additions & 2 deletions test/events.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ test('each platform prints its SDK\'s real event call with the name', async () =
flutter: [/import 'links\.dart';/, /await links\.trackEvent\('purchase'\);/, /value: 49\.99, currency: 'USD'/],
ios: [/strait\.trackEvent\("purchase"\) \{ ok in/, /eventId: order\.id/],
android: [/StraitLinks\.client\.trackEvent\("purchase"\)/, /off the main thread/],
web: [/import \{ reportWebOpen \} from '@straitlink\/web'/, /\/v1\/event/, /event: 'purchase'/, /platform: 'web'/, /clickId/],
web: [/import \{ reportWebOpen, trackEvent \} from '@straitlink\/web'/, /await reportWebOpen\(strait\);/, /await trackEvent\(strait, 'purchase'\);/, /value: 49\.99, currency: 'USD', eventId: order\.id/],
};
for (const [platform, patterns] of Object.entries(want)) {
const r = await run(['add', 'purchase', '--platform', platform, FIXTURES]);
Expand Down Expand Up @@ -59,7 +59,12 @@ test('detects the platform from the project files', async () => {
const spm = await run(['add', 'signup', dirWith({ 'Package.swift': '// swift-tools-version:5.9' })]);
assert.match(spm.out, /strait\.trackEvent\("signup"\)/);
const web = await run(['add', 'signup', dirWith({ 'package.json': JSON.stringify({ dependencies: { vite: '5' } }) })]);
assert.match(web.out, /reportWebOpen/);
assert.match(web.out, /trackEvent\(strait, 'signup'\)/);
});

test('web uses the SDK call, not a hand-written request', async () => {
const r = await run(['add', 'purchase', '--platform', 'web']);
assert.doesNotMatch(r.out, /fetch\(|\/v1\/event|localStorage/);
});

test('web fills in the publishable key and link host from the environment when set', async () => {
Expand Down
Loading