@volter/twin-segment 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +154 -0
  2. package/client/segment-mirror.css +45 -0
  3. package/client/segment-mirror.tsx +154 -0
  4. package/dist/client/segment-mirror.bundle.js +342 -0
  5. package/dist/client/segment-mirror.css +45 -0
  6. package/dist/client/segment-mirror.d.ts +34 -0
  7. package/dist/client/segment-mirror.js +80 -0
  8. package/dist/client/segment-mirror.tsx +154 -0
  9. package/dist/src/cli.d.ts +2 -0
  10. package/dist/src/cli.js +34 -0
  11. package/dist/src/index.d.ts +8 -0
  12. package/dist/src/index.js +53 -0
  13. package/dist/src/segment-budget.d.ts +41 -0
  14. package/dist/src/segment-budget.js +112 -0
  15. package/dist/src/segment-capabilities.d.ts +12 -0
  16. package/dist/src/segment-capabilities.gen.d.ts +3 -0
  17. package/dist/src/segment-capabilities.gen.js +22 -0
  18. package/dist/src/segment-capabilities.js +907 -0
  19. package/dist/src/segment-conformance.d.ts +8 -0
  20. package/dist/src/segment-conformance.js +106 -0
  21. package/dist/src/segment-connector.d.ts +76 -0
  22. package/dist/src/segment-connector.js +226 -0
  23. package/dist/src/segment-mirror-ui.d.ts +42 -0
  24. package/dist/src/segment-mirror-ui.js +143 -0
  25. package/dist/src/segment-server.d.ts +25 -0
  26. package/dist/src/segment-server.js +90 -0
  27. package/dist/src/segment-surface.gen.d.ts +48 -0
  28. package/dist/src/segment-surface.gen.js +267 -0
  29. package/dist/src/segment-twin.d.ts +98 -0
  30. package/dist/src/segment-twin.js +543 -0
  31. package/package.json +59 -0
  32. package/src/cli.ts +30 -0
  33. package/src/index.ts +87 -0
  34. package/src/segment-budget.ts +138 -0
  35. package/src/segment-capabilities.gen.ts +25 -0
  36. package/src/segment-capabilities.ts +988 -0
  37. package/src/segment-conformance.ts +123 -0
  38. package/src/segment-connector.ts +233 -0
  39. package/src/segment-journey.uitest.ts +116 -0
  40. package/src/segment-mirror-ui.ts +157 -0
  41. package/src/segment-server.ts +95 -0
  42. package/src/segment-surface.gen.ts +277 -0
  43. package/src/segment-twin.ts +664 -0
@@ -0,0 +1,95 @@
1
+ import { serveHttp } from '@volter/world-core';
2
+ import { dropped, events, groups, handleSegmentTwinRequest, identities } from './segment-twin.ts';
3
+ import { statefulTwinManifest, worldNow } from '@volter/world-core';
4
+
5
+ /** Options every Segment-twin HTTP surface needs, independent of who owns the socket. */
6
+ export interface SegmentTwinFetchOptions {
7
+ root?: string;
8
+ }
9
+
10
+ /** The keyless discovery door's body: a frozen VALUE, built once at module load from
11
+ * constants only. Nothing here reads the clock or state, so every serve of GET /twin is
12
+ * byte-identical (serve-path determinism, R9). */
13
+ const BASE_MANIFEST = statefulTwinManifest({
14
+ vendor: 'segment',
15
+ twinOf: 'the Segment HTTP Tracking API',
16
+ stores: 'tracked events, identify calls, group calls and refused (dropped) messages',
17
+ identity: 'Authenticate as Segment does — the write key as HTTP Basic username, an OAuth bearer, or `writeKey` in the body; the twin accepts any non-sentinel credential.',
18
+ });
19
+ /** THE STORE DOOR's projections (R5c). The Segment HTTP Tracking API is WRITE-ONLY — every route
20
+ * is an ingest and none of them reads a message back — so these four named projections are the
21
+ * only way to observe what was accepted, and `dropped` is the only way to see what was
22
+ * accepted-and-refused (Segment answers 200 and silently drops; this twin records the reason).
23
+ * They are also what makes this pack's determinism checkable at the resource level rather than
24
+ * only at the manifest door. */
25
+ const STORES: Record<string, (root?: string) => unknown> = { events, identities, groups, dropped };
26
+ const STORE_NAMES = Object.keys(STORES).sort();
27
+ /** The manifest EDUCATES: it names the store door the way the kernel adapter does. */
28
+ const MANIFEST = {
29
+ ...BASE_MANIFEST,
30
+ stores: STORE_NAMES,
31
+ doors: { ...(BASE_MANIFEST.doors as Record<string, unknown>), store: 'GET /twin/store/<name>' },
32
+ };
33
+
34
+ /** The twin's HTTP face. Two things it must get right, both invisible to an in-process verify:
35
+ * 1. THE AUTHORIZATION HEADER IS PART OF THE PAYLOAD on this vendor. Two of Segment's three
36
+ * documented auth schemes live in that header (Basic with the write key as the username;
37
+ * OAuth Bearer), so a fetch that dropped it would make an analytics-node v1 client — which
38
+ * sends `auth: { username: writeKey }` and NO writeKey in the body — arrive anonymous.
39
+ * 2. An empty-body status must serve a genuinely EMPTY body: `Response.json(null)` puts the
40
+ * four bytes "null" on the wire (the A3-caught serialization class in
41
+ * var/line/mixpanel/REVIEW.md).
42
+ *
43
+ * FETCH-FIRST (runtime contract R12b): this is the pack's whole HTTP surface as a plain fetch,
44
+ * and `createSegmentTwinServer` is one line of `Bun.serve` around it. It is a CUSTOM fetch, not
45
+ * the kernel adapter (`createTwinFetchFromHandler`): the authorization threading (1) and the
46
+ * handler-throw guard (below) are this pack's own. The keyless `GET /twin` manifest door the
47
+ * adapter would have provided is mounted here by hand, answering the same
48
+ * `statefulTwinManifest` shape — discovery is a door every twin owes (serve-path determinism
49
+ * R9 replays it), and `/twin` shadows no Segment route: the Tracking API lives under `/v1/*`. */
50
+ export function createSegmentTwinFetch(options: SegmentTwinFetchOptions = {}): (request: Request) => Promise<Response> {
51
+ return async function segmentTwinFetch(request: Request): Promise<Response> {
52
+ try {
53
+ const url = new URL(request.url);
54
+ const cleanPath = url.pathname.replace(/\/+$/, '') || '/';
55
+ // The keyless discovery door, before any vendor dispatch: a pure function of the
56
+ // manifest VALUE — no clock, no state — so replaying it is byte-identical (R9).
57
+ if (request.method === 'GET' && cleanPath === '/twin') {
58
+ return Response.json(MANIFEST);
59
+ }
60
+ // THE STORE DOOR (R5c) — see STORES above. Read-only, deterministic, served by the same
61
+ // fetch the vendor paths come out of; `/twin/*` shadows no Segment route (`/v1/*`).
62
+ if (request.method === 'GET' && cleanPath.startsWith('/twin/store/')) {
63
+ const name = cleanPath.slice('/twin/store/'.length);
64
+ const store = STORES[name];
65
+ if (!store) return Response.json({ error: 'unknown store', store: name, stores: STORE_NAMES }, { status: 404 });
66
+ return Response.json(store(options.root));
67
+ }
68
+ const res = await handleSegmentTwinRequest({
69
+ method: request.method,
70
+ path: url.pathname + url.search,
71
+ body: request.method === 'GET' || request.method === 'HEAD' ? undefined : await request.text(),
72
+ contentType: request.headers.get('content-type') ?? undefined,
73
+ authorization: request.headers.get('authorization') ?? undefined,
74
+ // The WORLD instant (R9): `receivedAt` and every minted messageId come from the world
75
+ // clock, never wall time.
76
+ occurredAt: worldNow(),
77
+ root: options.root,
78
+ });
79
+ if (res.body === null || res.body === undefined) return new Response(null, { status: res.status, headers: res.headers });
80
+ return Response.json(res.body, { status: res.status, headers: res.headers });
81
+ } catch (err) {
82
+ // Never let a handler throw escape the fetch callback: under `bun test` that is reported as
83
+ // "unhandled between tests" and can stop later test registration, hiding the real defect
84
+ // (ADDING_A_TWIN §8). Answer the vendor's own error SHAPE.
85
+ return Response.json({ code: 'twin_internal_error', message: String(err) }, { status: 500 });
86
+ }
87
+ };
88
+ }
89
+
90
+ export async function createSegmentTwinServer(options: { port?: number; root?: string } = {}) {
91
+ return await serveHttp({
92
+ port: options.port ?? 0,
93
+ fetch: createSegmentTwinFetch(options),
94
+ });
95
+ }
@@ -0,0 +1,277 @@
1
+ // GENERATED by scripts/sdk-compile.ts from var/line/segment/SURFACE.json — the vendor
2
+ // publishes no machine-readable spec; every op below carries the evidence it was ratified on.
3
+ // Data only; regenerate, never hand-edit.
4
+
5
+ export type TwinOp = {
6
+ id: string; area: string; method: string; path: string; summary: string;
7
+ pathParams: string[]; requiredQuery: string[]; requiredBody: string[];
8
+ successStatus: number; responseSkeleton: unknown; specFile: string; evidence: string; tier: string;
9
+ };
10
+
11
+ export const OPS: TwinOp[] = [
12
+ {
13
+ "id": "alias",
14
+ "area": "alias",
15
+ "method": "POST",
16
+ "path": "/v1/alias",
17
+ "summary": "associate one identity with another: a `previousId` (userId or anonymousId) is aliased onto a new `userId`",
18
+ "pathParams": [],
19
+ "requiredQuery": [],
20
+ "requiredBody": [],
21
+ "successStatus": 200,
22
+ "responseSkeleton": null,
23
+ "specFile": "SURFACE.json (sdk/docs-ratified, no vendor spec exists)",
24
+ "evidence": "DOCS ONLY, NOT VERIFIED OFFLINE. github.com/segmentio/segment-docs src/connections/sources/catalog/libraries/server/http-api/index.md '## Alias' declares `POST https://api.segment.io/v1/alias` with a worked body {previousId, userId, timestamp, writeKey} and a field table naming previousId. Absent from @segment/analytics-node@2's wire surface (batched as type:'alias'); the SDK's AliasParams (src/app/types/params.ts) requires both userId and previousId.",
25
+ "tier": "common"
26
+ },
27
+ {
28
+ "id": "batch",
29
+ "area": "batch",
30
+ "method": "POST",
31
+ "path": "/v1/batch",
32
+ "summary": "the SERVER-side batched ingestion envelope every official server SDK sends: {batch:[<message>...], writeKey, sentAt}, each message discriminated by its own `type`",
33
+ "pathParams": [],
34
+ "requiredQuery": [],
35
+ "requiredBody": [],
36
+ "successStatus": 200,
37
+ "responseSkeleton": null,
38
+ "specFile": "SURFACE.json (sdk/docs-ratified, no vendor spec exists)",
39
+ "evidence": "SDK WIRE LITERAL. @segment/analytics-node@2.3.0 src/plugins/segmentio/publisher.ts:73-76 builds the one and only URL this SDK ever posts to: tryCreateFormattedUrl(host ?? 'https://api.segment.io', path ?? '/v1/batch'); the body at :243-247 is JSON.stringify({batch: events, writeKey: this._writeKey, sentAt: new Date()}). It is the SOLE real path literal in var/line/segment/dossier/wire.json pathLiterals (the other, '/bar', is a JSDoc example). Corroborated in a second first-party SDK (analytics-python segment/analytics/request.py: url = remove_trailing_slash(host or 'https://api.segment.io') + '/v1/batch') and in the vendor's own reference, github.com/segmentio/segment-docs src/connections/sources/catalog/libraries/server/http-api/index.md '## Batch'.",
40
+ "tier": "core"
41
+ },
42
+ {
43
+ "id": "browser_track",
44
+ "area": "track",
45
+ "method": "POST",
46
+ "path": "/v1/t",
47
+ "summary": "analytics.js's short-form tracking route on the api.segment.io/v1 base — the path a browser proxy must forward",
48
+ "pathParams": [],
49
+ "requiredQuery": [],
50
+ "requiredBody": [],
51
+ "successStatus": 200,
52
+ "responseSkeleton": null,
53
+ "specFile": "SURFACE.json (sdk/docs-ratified, no vendor spec exists)",
54
+ "evidence": "DOCS ONLY, NOT VERIFIED OFFLINE. github.com/segmentio/segment-docs src/connections/sources/catalog/libraries/website/javascript/custom-proxy.md:123-125 documents the Analytics.js `integrations['Segment.io'].apiHost` proxy as: '// POST https://MY-CUSTOM-API-PROXY.com/v1/t --> proxies to // https://api.segment.io/v1/t'. Only the `/v1/t` short form appears on a first-party page; the sibling short routes analytics.js may use for the other call types are NOT documented anywhere in segment-docs and are deliberately not invented here. No literal in @segment/analytics-node@2 (a server SDK; this is the browser route).",
55
+ "tier": "niche"
56
+ },
57
+ {
58
+ "id": "group",
59
+ "area": "group",
60
+ "method": "POST",
61
+ "path": "/v1/group",
62
+ "summary": "associate an identified user with a group (company/account/team) and record group traits",
63
+ "pathParams": [],
64
+ "requiredQuery": [],
65
+ "requiredBody": [],
66
+ "successStatus": 200,
67
+ "responseSkeleton": null,
68
+ "specFile": "SURFACE.json (sdk/docs-ratified, no vendor spec exists)",
69
+ "evidence": "DOCS ONLY, NOT VERIFIED OFFLINE. github.com/segmentio/segment-docs src/connections/sources/catalog/libraries/server/http-api/index.md '## Group' declares `POST https://api.segment.io/v1/group` with a worked body {userId, groupId, traits, timestamp, writeKey} and a field table naming groupId and group traits. Absent from @segment/analytics-node@2's wire surface (batched as type:'group').",
70
+ "tier": "core"
71
+ },
72
+ {
73
+ "id": "identify",
74
+ "area": "identify",
75
+ "method": "POST",
76
+ "path": "/v1/identify",
77
+ "summary": "tie a user to their actions and record `traits` about them",
78
+ "pathParams": [],
79
+ "requiredQuery": [],
80
+ "requiredBody": [],
81
+ "successStatus": 200,
82
+ "responseSkeleton": null,
83
+ "specFile": "SURFACE.json (sdk/docs-ratified, no vendor spec exists)",
84
+ "evidence": "DOCS ONLY, NOT VERIFIED OFFLINE. github.com/segmentio/segment-docs src/connections/sources/catalog/libraries/server/http-api/index.md '## Identify' declares `POST https://api.segment.io/v1/identify` with a worked body {userId, traits, context, timestamp, writeKey} and a field table (anonymousId, context, integrations, timestamp, identify traits, userId); the '## Selecting Destinations' section posts an integrations-bearing identify to the same URL. Absent from @segment/analytics-node@2's wire surface (that SDK sends type:'identify' messages inside /v1/batch).",
85
+ "tier": "core"
86
+ },
87
+ {
88
+ "id": "mobile_batch",
89
+ "area": "batch",
90
+ "method": "POST",
91
+ "path": "/v1/b",
92
+ "summary": "the MOBILE batch endpoint — a distinct route from /v1/batch, which the vendor reserves for server-side sending",
93
+ "pathParams": [],
94
+ "requiredQuery": [],
95
+ "requiredBody": [],
96
+ "successStatus": 200,
97
+ "responseSkeleton": null,
98
+ "specFile": "SURFACE.json (sdk/docs-ratified, no vendor spec exists)",
99
+ "evidence": "DOCS ONLY, NOT VERIFIED OFFLINE. github.com/segmentio/segment-docs src/connections/sources/catalog/libraries/mobile/apple/index.md:163 and src/connections/sources/catalog/libraries/mobile/react-native/index.md:781: 'you must forward the batched events to https://api.segment.io/v1/b. The https://api.segment.io/v1/batch endpoint is reserved for events arriving from server-side sending, and proxying to that endpoint for your mobile events may result in unexpected behavior.' Also react-native/index.md:91, the `proxy` option: 'a batch url to post to instead of https://api.segment.io/v1/b'. No literal in @segment/analytics-node@2 — this is the Swift/Kotlin/React-Native route, and no mobile SDK is pinned in this dossier.",
100
+ "tier": "common"
101
+ },
102
+ {
103
+ "id": "oauth_token",
104
+ "area": "oauth",
105
+ "method": "POST",
106
+ "path": "/token",
107
+ "summary": "OAuth 2.0 client-credentials token exchange on the regional authorization server: a signed RS256 JWT client assertion is traded for a short-lived access token",
108
+ "pathParams": [],
109
+ "requiredQuery": [],
110
+ "requiredBody": [],
111
+ "successStatus": 200,
112
+ "responseSkeleton": null,
113
+ "specFile": "SURFACE.json (sdk/docs-ratified, no vendor spec exists)",
114
+ "evidence": "SDK WIRE LITERAL (interpolated, which is why wire.json's plain-string grep missed it). @segment/analytics-node@2.3.0 src/lib/token-manager.ts requestAccessToken(): `const accessTokenEndpoint = ${this.authServer}/token`, POSTed as application/x-www-form-urlencoded with grant_type=client_credentials & client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer & client_assertion=<signed JWT> & scope; authServer defaults to 'https://oauth2.segment.io' (same file's constructor) and the EU value 'https://oauth2.eu1.segmentapis.com' is documented on src/lib/types.ts OAuthSettings.authServer. Corroborated by github.com/segmentio/segment-docs src/connections/oauth.md '## Obtain the access token', which names both regional authorization servers and the same four form parameters.",
115
+ "tier": "niche"
116
+ },
117
+ {
118
+ "id": "page",
119
+ "area": "page",
120
+ "method": "POST",
121
+ "path": "/v1/page",
122
+ "summary": "record a web page view, with optional category, name and properties",
123
+ "pathParams": [],
124
+ "requiredQuery": [],
125
+ "requiredBody": [],
126
+ "successStatus": 200,
127
+ "responseSkeleton": null,
128
+ "specFile": "SURFACE.json (sdk/docs-ratified, no vendor spec exists)",
129
+ "evidence": "DOCS ONLY, NOT VERIFIED OFFLINE. github.com/segmentio/segment-docs src/connections/sources/catalog/libraries/server/http-api/index.md '## Page' declares `POST https://api.segment.io/v1/page` with a worked body {userId, name, timestamp, writeKey} and a field table (anonymousId, context, integrations, page name, page properties, timestamp, userId). Absent from @segment/analytics-node@2's wire surface (batched as type:'page').",
130
+ "tier": "core"
131
+ },
132
+ {
133
+ "id": "pixel_alias",
134
+ "area": "pixel",
135
+ "method": "GET",
136
+ "path": "/v1/pixel/alias",
137
+ "summary": "Tracking Pixel API: an alias call carried in the query string, answering 200 with a 1x1 transparent GIF",
138
+ "pathParams": [],
139
+ "requiredQuery": [],
140
+ "requiredBody": [],
141
+ "successStatus": 200,
142
+ "responseSkeleton": null,
143
+ "specFile": "SURFACE.json (sdk/docs-ratified, no vendor spec exists)",
144
+ "evidence": "DOCS ONLY, NOT VERIFIED OFFLINE. github.com/segmentio/segment-docs src/connections/sources/catalog/libraries/server/pixel-tracking-api/index.md '#### Pixel Routes' enumerates exactly six routes as a literal block — /v1/pixel/identify, /v1/pixel/group, /v1/pixel/alias, /v1/pixel/page, /v1/pixel/screen, /v1/pixel/track — under the signature `https://api.segment.io/v1/pixel/<METHOD ENDPOINT>?data=<base64-ENCODED-JSON>`, with 'Each endpoint *always* responds with a 200 <empty-gif>, even if an error occurs'. Not in any SDK.",
145
+ "tier": "niche"
146
+ },
147
+ {
148
+ "id": "pixel_group",
149
+ "area": "pixel",
150
+ "method": "GET",
151
+ "path": "/v1/pixel/group",
152
+ "summary": "Tracking Pixel API: a group call carried in the query string, answering 200 with a 1x1 transparent GIF",
153
+ "pathParams": [],
154
+ "requiredQuery": [],
155
+ "requiredBody": [],
156
+ "successStatus": 200,
157
+ "responseSkeleton": null,
158
+ "specFile": "SURFACE.json (sdk/docs-ratified, no vendor spec exists)",
159
+ "evidence": "DOCS ONLY, NOT VERIFIED OFFLINE. github.com/segmentio/segment-docs src/connections/sources/catalog/libraries/server/pixel-tracking-api/index.md '#### Pixel Routes' enumerates exactly six routes as a literal block — /v1/pixel/identify, /v1/pixel/group, /v1/pixel/alias, /v1/pixel/page, /v1/pixel/screen, /v1/pixel/track — under the signature `https://api.segment.io/v1/pixel/<METHOD ENDPOINT>?data=<base64-ENCODED-JSON>`, with 'Each endpoint *always* responds with a 200 <empty-gif>, even if an error occurs'. Not in any SDK.",
160
+ "tier": "niche"
161
+ },
162
+ {
163
+ "id": "pixel_identify",
164
+ "area": "pixel",
165
+ "method": "GET",
166
+ "path": "/v1/pixel/identify",
167
+ "summary": "Tracking Pixel API: an identify call carried in the query string, answering 200 with a 1x1 transparent GIF",
168
+ "pathParams": [],
169
+ "requiredQuery": [],
170
+ "requiredBody": [],
171
+ "successStatus": 200,
172
+ "responseSkeleton": null,
173
+ "specFile": "SURFACE.json (sdk/docs-ratified, no vendor spec exists)",
174
+ "evidence": "DOCS ONLY, NOT VERIFIED OFFLINE. github.com/segmentio/segment-docs src/connections/sources/catalog/libraries/server/pixel-tracking-api/index.md '#### Pixel Routes' enumerates exactly six routes as a literal block — /v1/pixel/identify, /v1/pixel/group, /v1/pixel/alias, /v1/pixel/page, /v1/pixel/screen, /v1/pixel/track — under the signature `https://api.segment.io/v1/pixel/<METHOD ENDPOINT>?data=<base64-ENCODED-JSON>`, with 'Each endpoint *always* responds with a 200 <empty-gif>, even if an error occurs'. Not in any SDK.",
175
+ "tier": "niche"
176
+ },
177
+ {
178
+ "id": "pixel_page",
179
+ "area": "pixel",
180
+ "method": "GET",
181
+ "path": "/v1/pixel/page",
182
+ "summary": "Tracking Pixel API: a page call carried in the query string, answering 200 with a 1x1 transparent GIF",
183
+ "pathParams": [],
184
+ "requiredQuery": [],
185
+ "requiredBody": [],
186
+ "successStatus": 200,
187
+ "responseSkeleton": null,
188
+ "specFile": "SURFACE.json (sdk/docs-ratified, no vendor spec exists)",
189
+ "evidence": "DOCS ONLY, NOT VERIFIED OFFLINE. github.com/segmentio/segment-docs src/connections/sources/catalog/libraries/server/pixel-tracking-api/index.md '#### Pixel Routes' enumerates exactly six routes as a literal block — /v1/pixel/identify, /v1/pixel/group, /v1/pixel/alias, /v1/pixel/page, /v1/pixel/screen, /v1/pixel/track — under the signature `https://api.segment.io/v1/pixel/<METHOD ENDPOINT>?data=<base64-ENCODED-JSON>`, with 'Each endpoint *always* responds with a 200 <empty-gif>, even if an error occurs'. Not in any SDK.",
190
+ "tier": "niche"
191
+ },
192
+ {
193
+ "id": "pixel_screen",
194
+ "area": "pixel",
195
+ "method": "GET",
196
+ "path": "/v1/pixel/screen",
197
+ "summary": "Tracking Pixel API: a screen call carried in the query string, answering 200 with a 1x1 transparent GIF",
198
+ "pathParams": [],
199
+ "requiredQuery": [],
200
+ "requiredBody": [],
201
+ "successStatus": 200,
202
+ "responseSkeleton": null,
203
+ "specFile": "SURFACE.json (sdk/docs-ratified, no vendor spec exists)",
204
+ "evidence": "DOCS ONLY, NOT VERIFIED OFFLINE. github.com/segmentio/segment-docs src/connections/sources/catalog/libraries/server/pixel-tracking-api/index.md '#### Pixel Routes' enumerates exactly six routes as a literal block — /v1/pixel/identify, /v1/pixel/group, /v1/pixel/alias, /v1/pixel/page, /v1/pixel/screen, /v1/pixel/track — under the signature `https://api.segment.io/v1/pixel/<METHOD ENDPOINT>?data=<base64-ENCODED-JSON>`, with 'Each endpoint *always* responds with a 200 <empty-gif>, even if an error occurs'. Not in any SDK.",
205
+ "tier": "niche"
206
+ },
207
+ {
208
+ "id": "pixel_track",
209
+ "area": "pixel",
210
+ "method": "GET",
211
+ "path": "/v1/pixel/track",
212
+ "summary": "Tracking Pixel API: a track call carried in the query string (base64 `?data=` or plain params), answering 200 with a 1x1 transparent GIF",
213
+ "pathParams": [],
214
+ "requiredQuery": [],
215
+ "requiredBody": [],
216
+ "successStatus": 200,
217
+ "responseSkeleton": null,
218
+ "specFile": "SURFACE.json (sdk/docs-ratified, no vendor spec exists)",
219
+ "evidence": "DOCS ONLY, NOT VERIFIED OFFLINE. github.com/segmentio/segment-docs src/connections/sources/catalog/libraries/server/pixel-tracking-api/index.md gives the signature `https://api.segment.io/v1/pixel/<METHOD ENDPOINT>?data=<base64-ENCODED-JSON>`, lists /v1/pixel/track among its six '#### Pixel Routes', states 'Each endpoint *always* responds with a 200 <empty-gif>, even if an error occurs', and shows both the base64 img-tag form (:67) and the unencoded query-param form (:61). Corroborated at src/connections/sources/custom-domain.md:79 and src/guides/how-to-guides/cross-channel-tracking.md:77. Not in any SDK: this route exists for email/ad contexts where JavaScript and POST are impossible.",
220
+ "tier": "niche"
221
+ },
222
+ {
223
+ "id": "screen",
224
+ "area": "screen",
225
+ "method": "POST",
226
+ "path": "/v1/screen",
227
+ "summary": "record a mobile app screen view, with optional category, name and properties",
228
+ "pathParams": [],
229
+ "requiredQuery": [],
230
+ "requiredBody": [],
231
+ "successStatus": 200,
232
+ "responseSkeleton": null,
233
+ "specFile": "SURFACE.json (sdk/docs-ratified, no vendor spec exists)",
234
+ "evidence": "DOCS ONLY, NOT VERIFIED OFFLINE. github.com/segmentio/segment-docs src/connections/sources/catalog/libraries/server/http-api/index.md '## Screen' declares `POST https://api.segment.io/v1/screen` with a worked body {userId, name, timestamp, writeKey}. @segment/analytics-node@2 exposes a .screen() method (src/app/analytics-node.ts) and its SegmentEventType union includes 'screen' (src/app/types/segment-event.ts), but the message still travels inside /v1/batch — no per-type path literal ships.",
235
+ "tier": "common"
236
+ },
237
+ {
238
+ "id": "track",
239
+ "area": "track",
240
+ "method": "POST",
241
+ "path": "/v1/track",
242
+ "summary": "record an action a user performed: required `event` name plus optional `properties`",
243
+ "pathParams": [],
244
+ "requiredQuery": [],
245
+ "requiredBody": [],
246
+ "successStatus": 200,
247
+ "responseSkeleton": null,
248
+ "specFile": "SURFACE.json (sdk/docs-ratified, no vendor spec exists)",
249
+ "evidence": "DOCS ONLY, NOT VERIFIED OFFLINE. github.com/segmentio/segment-docs src/connections/sources/catalog/libraries/server/http-api/index.md '## Track' declares `POST https://api.segment.io/v1/track` with a worked JSON body {userId, event, properties, context, timestamp, writeKey}; the same page's '### writeKey authentication' and '#### OAuth' curl examples both post to that exact URL. NO path literal for it exists in @segment/analytics-node@2 — that SDK batches every call type onto /v1/batch — so this entry is grounded in the vendor's reference page, not in shipped SDK bytes.",
250
+ "tier": "core"
251
+ }
252
+ ];
253
+
254
+ export type TwinRequest = { method: string; path: string; body?: string };
255
+ export type TwinResponse = { status: number; body: unknown; headers?: Record<string, string> };
256
+ export type OpHandler = (ctx: { op: TwinOp; params: Record<string, string>; query: URLSearchParams; body: unknown; root?: string }) => TwinResponse | Promise<TwinResponse>;
257
+
258
+ const compiled = OPS.map((op) => ({ op, re: new RegExp('^' + op.path.replace(/[.*+?^$()|[\]\\]/g, '\\$&').replace(/\{([^}]+)\}/g, '(?<$1>[^/]+)') + '$') }));
259
+
260
+ /** Match one request against the ratified surface. PURE LOOKUP — dispatch belongs to the pack. */
261
+ export function matchOp(req: TwinRequest): { op: TwinOp; params: Record<string, string>; query: URLSearchParams } | undefined {
262
+ const url = new URL(req.path, 'http://twin.local');
263
+ const match = compiled.find((c) => c.op.method === req.method.toUpperCase() && c.re.test(url.pathname));
264
+ if (!match) return undefined;
265
+ return { op: match.op, params: { ...(match.re.exec(url.pathname)?.groups ?? {}) }, query: url.searchParams };
266
+ }
267
+
268
+ /** First required query/body field missing, or undefined (empty for sdk-ratified surfaces —
269
+ * validation truth lives in the pack's semantics, grounded per op). */
270
+ export function missingRequired(op: TwinOp, query: URLSearchParams, body: unknown): string | undefined {
271
+ for (const q of op.requiredQuery) if (!query.has(q)) return q;
272
+ for (const f of op.requiredBody) if (!body || typeof body !== 'object' || (body as Record<string, unknown>)[f] === undefined) return f;
273
+ return undefined;
274
+ }
275
+
276
+ /** SCAFFOLD default error body — the A2 station replaces this with the vendor's REAL envelope. */
277
+ export const errorBody = (message: string, field?: string) => ({ success: false, message, ...(field ? { field } : {}) });