@arthurzakharov/convert-service 1.0.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.
- package/README.md +428 -0
- package/dist/app.d.ts +3 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +37 -0
- package/dist/app.js.map +1 -0
- package/dist/client.d.ts +60 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +207 -0
- package/dist/client.js.map +1 -0
- package/dist/contracts.d.ts +205 -0
- package/dist/contracts.d.ts.map +1 -0
- package/dist/contracts.js +3 -0
- package/dist/contracts.js.map +1 -0
- package/dist/convert/client.d.ts +7 -0
- package/dist/convert/client.d.ts.map +1 -0
- package/dist/convert/client.js +61 -0
- package/dist/convert/client.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/routes/bucket.d.ts +3 -0
- package/dist/routes/bucket.d.ts.map +1 -0
- package/dist/routes/bucket.js +52 -0
- package/dist/routes/bucket.js.map +1 -0
- package/dist/routes/convert-route-utils.d.ts +20 -0
- package/dist/routes/convert-route-utils.d.ts.map +1 -0
- package/dist/routes/convert-route-utils.js +123 -0
- package/dist/routes/convert-route-utils.js.map +1 -0
- package/dist/routes/experiences.d.ts +3 -0
- package/dist/routes/experiences.d.ts.map +1 -0
- package/dist/routes/experiences.js +120 -0
- package/dist/routes/experiences.js.map +1 -0
- package/dist/routes/features.d.ts +3 -0
- package/dist/routes/features.d.ts.map +1 -0
- package/dist/routes/features.js +126 -0
- package/dist/routes/features.js.map +1 -0
- package/dist/routes/health.d.ts +3 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +16 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/track.d.ts +3 -0
- package/dist/routes/track.d.ts.map +1 -0
- package/dist/routes/track.js +37 -0
- package/dist/routes/track.js.map +1 -0
- package/dist/utils/logger.d.ts +11 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +136 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/segments.d.ts +25 -0
- package/dist/utils/segments.d.ts.map +1 -0
- package/dist/utils/segments.js +103 -0
- package/dist/utils/segments.js.map +1 -0
- package/package.json +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
# convert-service
|
|
2
|
+
|
|
3
|
+
Server-side proxy for the [Convert.com](https://www.convert.com/) FullStack SDK. Exposes a small HTTP API so frontend apps can bucket visitors, read FullStack configuration, run feature flags, and track conversions without embedding SDK keys in the browser.
|
|
4
|
+
|
|
5
|
+
The bucketing endpoints follow Convert's documented FullStack flow for [experiences and variations](https://docs.developers.convert.com/docs/experiences-and-variations): create a visitor context, evaluate targeting/location rules, then return the selected variation or feature state.
|
|
6
|
+
|
|
7
|
+
## Endpoints
|
|
8
|
+
|
|
9
|
+
| Method | Path | Description |
|
|
10
|
+
|--------|------|-------------|
|
|
11
|
+
| `GET` | `/health` | Returns service version and commit hash |
|
|
12
|
+
| `POST` | `/bucket` | Run all active experiences for a visitor |
|
|
13
|
+
| `GET` | `/experiences` | List configured experiences for a project |
|
|
14
|
+
| `GET` | `/experiences/:experienceKey` | Read one experience by key |
|
|
15
|
+
| `GET` | `/experiences/by-id/:experienceId` | Read one experience by ID |
|
|
16
|
+
| `GET` | `/experiences/:experienceKey/variations/:variationKey` | Read one variation by experience/variation key |
|
|
17
|
+
| `GET` | `/experiences/by-id/:experienceId/variations/:variationId` | Read one variation by experience/variation ID |
|
|
18
|
+
| `POST` | `/experiences/run` | Run one experience by key for a visitor |
|
|
19
|
+
| `POST` | `/experiences/run-by-id` | Run one experience by ID for a visitor |
|
|
20
|
+
| `GET` | `/features` | List configured FullStack features for a project |
|
|
21
|
+
| `GET` | `/features/:featureKey` | Read one feature by key |
|
|
22
|
+
| `GET` | `/features/by-id/:featureId` | Read one feature by ID |
|
|
23
|
+
| `POST` | `/features/run-all` | Run all feature flags for a visitor |
|
|
24
|
+
| `POST` | `/features/run` | Run one feature flag by key for a visitor |
|
|
25
|
+
| `POST` | `/features/run-by-id` | Run one feature flag by ID for a visitor |
|
|
26
|
+
| `POST` | `/track` | Track a conversion goal |
|
|
27
|
+
|
|
28
|
+
### `GET /health`
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"status": "ok",
|
|
33
|
+
"version": "1.0.0",
|
|
34
|
+
"commit": "a3f9c12",
|
|
35
|
+
"timestamp": "2026-06-11T17:00:00.000Z"
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### `POST /bucket`
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
// Request
|
|
43
|
+
{
|
|
44
|
+
"projectKey": "passexperten",
|
|
45
|
+
"visitorId": "user-unique-id",
|
|
46
|
+
"visitorProperties": { "country": "DE" }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Response
|
|
50
|
+
{
|
|
51
|
+
"visitorId": "user-unique-id",
|
|
52
|
+
"variations": [
|
|
53
|
+
{
|
|
54
|
+
"experienceKey": "exp-key",
|
|
55
|
+
"experienceName": "My Experiment",
|
|
56
|
+
"variationKey": "variation-1",
|
|
57
|
+
"variationName": "Variation 1",
|
|
58
|
+
"changes": []
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Shared bucketing fields
|
|
65
|
+
|
|
66
|
+
`POST /bucket`, `/experiences/run`, `/experiences/run-by-id`, `/features/run-all`, `/features/run`, and `/features/run-by-id` accept these Convert bucketing fields:
|
|
67
|
+
|
|
68
|
+
| Field | Required | Description |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| `projectKey` | Yes | Supported project key, for example `passexperten` |
|
|
71
|
+
| `visitorId` | Yes | Stable visitor identifier |
|
|
72
|
+
| `visitorProperties` | No | Audience/segment targeting properties |
|
|
73
|
+
| `locationProperties` | No | Location rule matching properties |
|
|
74
|
+
| `pageUrl` | No | Page URL used while deriving default segments |
|
|
75
|
+
| `campaign` | No | Campaign value used while deriving default segments |
|
|
76
|
+
| `updateVisitorProperties` | No | Whether to update in-memory visitor properties |
|
|
77
|
+
| `forceVariationId` | No | Force a specific variation ID when running experiences |
|
|
78
|
+
| `enableTracking` | No | Whether Convert should track the bucketing event immediately |
|
|
79
|
+
| `environment` | No | Override Convert environment for this decision |
|
|
80
|
+
| `typeCasting` | No | Feature variable type casting flag |
|
|
81
|
+
| `experienceKeys` | No | Limit feature evaluation to specific experience keys |
|
|
82
|
+
|
|
83
|
+
### `GET /experiences`
|
|
84
|
+
|
|
85
|
+
```http
|
|
86
|
+
GET /experiences?projectKey=passexperten
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"experiences": [
|
|
92
|
+
{
|
|
93
|
+
"id": "100001",
|
|
94
|
+
"key": "headline-test",
|
|
95
|
+
"name": "Headline Test",
|
|
96
|
+
"status": "active",
|
|
97
|
+
"variations": []
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### `GET /experiences/:experienceKey`
|
|
104
|
+
|
|
105
|
+
```http
|
|
106
|
+
GET /experiences/headline-test?projectKey=passexperten
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"experience": {
|
|
112
|
+
"id": "100001",
|
|
113
|
+
"key": "headline-test",
|
|
114
|
+
"name": "Headline Test",
|
|
115
|
+
"variations": []
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The ID-based equivalent is:
|
|
121
|
+
|
|
122
|
+
```http
|
|
123
|
+
GET /experiences/by-id/100001?projectKey=passexperten
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### `GET /experiences/:experienceKey/variations/:variationKey`
|
|
127
|
+
|
|
128
|
+
```http
|
|
129
|
+
GET /experiences/headline-test/variations/variation-a?projectKey=passexperten
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"variation": {
|
|
135
|
+
"id": "200001",
|
|
136
|
+
"key": "variation-a",
|
|
137
|
+
"name": "Variation A",
|
|
138
|
+
"changes": []
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The ID-based equivalent is:
|
|
144
|
+
|
|
145
|
+
```http
|
|
146
|
+
GET /experiences/by-id/100001/variations/200001?projectKey=passexperten
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### `POST /experiences/run`
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
// Request
|
|
153
|
+
{
|
|
154
|
+
"projectKey": "passexperten",
|
|
155
|
+
"visitorId": "user-unique-id",
|
|
156
|
+
"experienceKey": "headline-test",
|
|
157
|
+
"visitorProperties": { "country": "DE" },
|
|
158
|
+
"locationProperties": { "url": "https://www.passexperten.de/" }
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Response
|
|
162
|
+
{
|
|
163
|
+
"visitorId": "user-unique-id",
|
|
164
|
+
"variation": {
|
|
165
|
+
"id": "200001",
|
|
166
|
+
"experienceId": "100001",
|
|
167
|
+
"experienceKey": "headline-test",
|
|
168
|
+
"experienceName": "Headline Test",
|
|
169
|
+
"variationKey": "variation-a",
|
|
170
|
+
"variationName": "Variation A",
|
|
171
|
+
"status": "running",
|
|
172
|
+
"bucketingAllocation": 5000,
|
|
173
|
+
"changes": []
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
The ID-based request uses `experienceId`:
|
|
179
|
+
|
|
180
|
+
```json
|
|
181
|
+
{
|
|
182
|
+
"projectKey": "passexperten",
|
|
183
|
+
"visitorId": "user-unique-id",
|
|
184
|
+
"experienceId": "100001"
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### `GET /features`
|
|
189
|
+
|
|
190
|
+
```http
|
|
191
|
+
GET /features?projectKey=passexperten
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"features": [
|
|
197
|
+
{
|
|
198
|
+
"id": "300001",
|
|
199
|
+
"key": "checkout-flow",
|
|
200
|
+
"name": "Checkout Flow",
|
|
201
|
+
"variables": [
|
|
202
|
+
{ "key": "enabled", "type": "boolean" }
|
|
203
|
+
]
|
|
204
|
+
}
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### `POST /features/run-all`
|
|
210
|
+
|
|
211
|
+
```json
|
|
212
|
+
// Request
|
|
213
|
+
{
|
|
214
|
+
"projectKey": "passexperten",
|
|
215
|
+
"visitorId": "user-unique-id",
|
|
216
|
+
"visitorProperties": { "country": "DE" }
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Response
|
|
220
|
+
{
|
|
221
|
+
"visitorId": "user-unique-id",
|
|
222
|
+
"features": [
|
|
223
|
+
{
|
|
224
|
+
"experienceId": "100001",
|
|
225
|
+
"experienceKey": "headline-test",
|
|
226
|
+
"experienceName": "Headline Test",
|
|
227
|
+
"id": "300001",
|
|
228
|
+
"key": "checkout-flow",
|
|
229
|
+
"name": "Checkout Flow",
|
|
230
|
+
"status": "enabled",
|
|
231
|
+
"variables": { "enabled": true }
|
|
232
|
+
}
|
|
233
|
+
]
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### `POST /features/run`
|
|
238
|
+
|
|
239
|
+
```json
|
|
240
|
+
// Request
|
|
241
|
+
{
|
|
242
|
+
"projectKey": "passexperten",
|
|
243
|
+
"visitorId": "user-unique-id",
|
|
244
|
+
"featureKey": "checkout-flow",
|
|
245
|
+
"experienceKeys": ["headline-test"]
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Response
|
|
249
|
+
{
|
|
250
|
+
"visitorId": "user-unique-id",
|
|
251
|
+
"feature": {
|
|
252
|
+
"experienceId": "100001",
|
|
253
|
+
"experienceKey": "headline-test",
|
|
254
|
+
"id": "300001",
|
|
255
|
+
"key": "checkout-flow",
|
|
256
|
+
"status": "enabled",
|
|
257
|
+
"variables": { "enabled": true }
|
|
258
|
+
},
|
|
259
|
+
"features": [
|
|
260
|
+
{
|
|
261
|
+
"experienceId": "100001",
|
|
262
|
+
"experienceKey": "headline-test",
|
|
263
|
+
"id": "300001",
|
|
264
|
+
"key": "checkout-flow",
|
|
265
|
+
"status": "enabled",
|
|
266
|
+
"variables": { "enabled": true }
|
|
267
|
+
}
|
|
268
|
+
]
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
The ID-based request uses `featureId`:
|
|
273
|
+
|
|
274
|
+
```json
|
|
275
|
+
{
|
|
276
|
+
"projectKey": "passexperten",
|
|
277
|
+
"visitorId": "user-unique-id",
|
|
278
|
+
"featureId": "300001"
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### `POST /track`
|
|
283
|
+
|
|
284
|
+
```json
|
|
285
|
+
// Request
|
|
286
|
+
{
|
|
287
|
+
"projectKey": "passexperten",
|
|
288
|
+
"visitorId": "user-unique-id",
|
|
289
|
+
"goalKey": "purchase",
|
|
290
|
+
"attributes": {
|
|
291
|
+
"conversionData": [
|
|
292
|
+
{ "key": "amount", "value": 49.99 }
|
|
293
|
+
]
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Response
|
|
298
|
+
{ "success": true }
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## Supported projects
|
|
302
|
+
|
|
303
|
+
| `projectKey` | Site |
|
|
304
|
+
|---|---|
|
|
305
|
+
| `passexperten` | passexperten.de |
|
|
306
|
+
| `bussgeldcheck` | bussgeldcheck.de |
|
|
307
|
+
|
|
308
|
+
## Local development
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
cp .env.example .env
|
|
312
|
+
# Fill in real SDK keys from Convert dashboard → Project Settings → SDK Keys
|
|
313
|
+
|
|
314
|
+
npm install
|
|
315
|
+
npm run dev # ts-node-dev with hot reload on port 3100
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## Frontend npm helper
|
|
319
|
+
|
|
320
|
+
This package also exposes a browser-safe helper for frontend applications. Import it from the `client` subpath so the frontend bundle does not import the Express service entrypoint:
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
import { createConvertServiceClient } from 'convert-service/client';
|
|
324
|
+
|
|
325
|
+
const convert = createConvertServiceClient({
|
|
326
|
+
baseUrl: 'https://convert-service.example.com',
|
|
327
|
+
projectKey: 'passexperten',
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
const { variation } = await convert.runExperience({
|
|
331
|
+
experienceKey: 'headline-test',
|
|
332
|
+
visitorProperties: { country: 'DE' },
|
|
333
|
+
locationProperties: { url: window.location.href },
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
if (variation?.variationKey === 'variation-a') {
|
|
337
|
+
// render variation-specific frontend behavior
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
The helper manages a stable visitor ID in a first-party cookie named `convert_visitor_id` by default. You can override or seed it when needed:
|
|
342
|
+
|
|
343
|
+
```ts
|
|
344
|
+
const convert = createConvertServiceClient({
|
|
345
|
+
baseUrl: 'https://convert-service.example.com',
|
|
346
|
+
projectKey: 'bussgeldcheck',
|
|
347
|
+
visitorCookieName: 'bc_convert_vid',
|
|
348
|
+
visitorCookieMaxAgeDays: 180,
|
|
349
|
+
defaultVisitorProperties: { app: 'bussgeldcheck-web' },
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
convert.setVisitorId(currentUser.id);
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Available helper methods:
|
|
356
|
+
|
|
357
|
+
| Method | Service endpoint |
|
|
358
|
+
|---|---|
|
|
359
|
+
| `health()` | `GET /health` |
|
|
360
|
+
| `bucket()` | `POST /bucket` |
|
|
361
|
+
| `listExperiences()` | `GET /experiences` |
|
|
362
|
+
| `getExperienceByKey()` | `GET /experiences/:experienceKey` |
|
|
363
|
+
| `getExperienceById()` | `GET /experiences/by-id/:experienceId` |
|
|
364
|
+
| `getVariationByKey()` | `GET /experiences/:experienceKey/variations/:variationKey` |
|
|
365
|
+
| `getVariationById()` | `GET /experiences/by-id/:experienceId/variations/:variationId` |
|
|
366
|
+
| `runExperience()` | `POST /experiences/run` |
|
|
367
|
+
| `runExperienceById()` | `POST /experiences/run-by-id` |
|
|
368
|
+
| `listFeatures()` | `GET /features` |
|
|
369
|
+
| `getFeatureByKey()` | `GET /features/:featureKey` |
|
|
370
|
+
| `getFeatureById()` | `GET /features/by-id/:featureId` |
|
|
371
|
+
| `runFeatures()` | `POST /features/run-all` |
|
|
372
|
+
| `runFeature()` | `POST /features/run` |
|
|
373
|
+
| `runFeatureById()` | `POST /features/run-by-id` |
|
|
374
|
+
| `trackConversion()` | `POST /track` |
|
|
375
|
+
|
|
376
|
+
Endpoint contracts are exported from `convert-service/contracts`:
|
|
377
|
+
|
|
378
|
+
```ts
|
|
379
|
+
import type {
|
|
380
|
+
ConvertApiEndpoints,
|
|
381
|
+
EndpointRequest,
|
|
382
|
+
EndpointResponse,
|
|
383
|
+
RunFeatureResponse,
|
|
384
|
+
} from 'convert-service/contracts';
|
|
385
|
+
|
|
386
|
+
type RunFeatureRequest = EndpointRequest<'POST /features/run'>;
|
|
387
|
+
type BucketResponse = EndpointResponse<'POST /bucket'>;
|
|
388
|
+
type AllEndpoints = keyof ConvertApiEndpoints;
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
Use these types in frontend wrappers, tests, or mocks when you need exact request/response compatibility with this service.
|
|
392
|
+
|
|
393
|
+
## Environment variables
|
|
394
|
+
|
|
395
|
+
| Variable | Required | Description |
|
|
396
|
+
|---|---|---|
|
|
397
|
+
| `CONVERT_SDK_KEY_PASSEXPERTEN` | Yes | SDK key for passexperten project |
|
|
398
|
+
| `CONVERT_SDK_KEY_BUSSGELDCHECK` | Yes | SDK key for bussgeldcheck project |
|
|
399
|
+
| `CONVERT_ENVIRONMENT` | Yes | `staging` or `live` |
|
|
400
|
+
| `CONVERT_DATA_REFRESH_INTERVAL` | No | Config refresh interval in ms (default: `300000`) |
|
|
401
|
+
| `CORS_ORIGINS` | No | Comma-separated allowed origins (default: `*`) |
|
|
402
|
+
| `PORT` | No | Port to listen on (default: `3100`) |
|
|
403
|
+
|
|
404
|
+
## Docker
|
|
405
|
+
|
|
406
|
+
```bash
|
|
407
|
+
docker build -t convert-service .
|
|
408
|
+
docker run -p 3100:3100 --env-file .env convert-service
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
## Deployment
|
|
412
|
+
|
|
413
|
+
The service deploys to [Railway](https://railway.app) automatically via GitHub Actions.
|
|
414
|
+
|
|
415
|
+
**Flow:**
|
|
416
|
+
- Push to any branch / open a PR → CI runs (typecheck + build)
|
|
417
|
+
- Merge to `main` → auto-deploys to Railway
|
|
418
|
+
|
|
419
|
+
**One-time setup:**
|
|
420
|
+
1. Create a Railway project and link it to this repo
|
|
421
|
+
2. Set all env variables in the Railway dashboard
|
|
422
|
+
3. Add `RAILWAY_TOKEN` to GitHub repo → Settings → Secrets and variables → Actions
|
|
423
|
+
|
|
424
|
+
## Tech stack
|
|
425
|
+
|
|
426
|
+
- Node 20 / TypeScript
|
|
427
|
+
- Express
|
|
428
|
+
- [@convertcom/js-sdk](https://www.npmjs.com/package/@convertcom/js-sdk)
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAQA,QAAA,MAAM,GAAG,6CAAY,CAAC;AA+BtB,eAAe,GAAG,CAAC"}
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const express_1 = __importDefault(require("express"));
|
|
7
|
+
const cors_1 = __importDefault(require("cors"));
|
|
8
|
+
const bucket_1 = __importDefault(require("./routes/bucket"));
|
|
9
|
+
const experiences_1 = __importDefault(require("./routes/experiences"));
|
|
10
|
+
const features_1 = __importDefault(require("./routes/features"));
|
|
11
|
+
const track_1 = __importDefault(require("./routes/track"));
|
|
12
|
+
const health_1 = __importDefault(require("./routes/health"));
|
|
13
|
+
const app = (0, express_1.default)();
|
|
14
|
+
// ── CORS ──────────────────────────────────────────────────────────────────────
|
|
15
|
+
const allowedOrigins = (process.env.CORS_ORIGINS ?? '')
|
|
16
|
+
.split(',')
|
|
17
|
+
.map((o) => o.trim())
|
|
18
|
+
.filter(Boolean);
|
|
19
|
+
app.use((0, cors_1.default)({
|
|
20
|
+
origin: allowedOrigins.length > 0 ? allowedOrigins : '*',
|
|
21
|
+
methods: ['GET', 'POST', 'OPTIONS'],
|
|
22
|
+
allowedHeaders: ['Content-Type', 'Authorization'],
|
|
23
|
+
}));
|
|
24
|
+
// ── Body parsing ──────────────────────────────────────────────────────────────
|
|
25
|
+
app.use(express_1.default.json());
|
|
26
|
+
// ── Routes ────────────────────────────────────────────────────────────────────
|
|
27
|
+
app.use('/health', health_1.default);
|
|
28
|
+
app.use('/bucket', bucket_1.default);
|
|
29
|
+
app.use('/experiences', experiences_1.default);
|
|
30
|
+
app.use('/features', features_1.default);
|
|
31
|
+
app.use('/track', track_1.default);
|
|
32
|
+
// ── 404 ───────────────────────────────────────────────────────────────────────
|
|
33
|
+
app.use((_req, res) => {
|
|
34
|
+
res.status(404).json({ error: 'Not found' });
|
|
35
|
+
});
|
|
36
|
+
exports.default = app;
|
|
37
|
+
//# sourceMappingURL=app.js.map
|
package/dist/app.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":";;;;;AAAA,sDAA8B;AAC9B,gDAAwB;AACxB,4DAA0C;AAC1C,sEAAoD;AACpD,gEAA8C;AAC9C,0DAAwC;AACxC,4DAA0C;AAE1C,MAAM,GAAG,GAAG,IAAA,iBAAO,GAAE,CAAC;AAEtB,iFAAiF;AACjF,MAAM,cAAc,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC;KACpD,KAAK,CAAC,GAAG,CAAC;KACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;KACpB,MAAM,CAAC,OAAO,CAAC,CAAC;AAEnB,GAAG,CAAC,GAAG,CACL,IAAA,cAAI,EAAC;IACH,MAAM,EAAE,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG;IACxD,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC;IACnC,cAAc,EAAE,CAAC,cAAc,EAAE,eAAe,CAAC;CAClD,CAAC,CACH,CAAC;AAEF,iFAAiF;AACjF,GAAG,CAAC,GAAG,CAAC,iBAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AAExB,iFAAiF;AACjF,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,gBAAY,CAAC,CAAC;AACjC,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,gBAAY,CAAC,CAAC;AACjC,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,qBAAiB,CAAC,CAAC;AAC3C,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,kBAAc,CAAC,CAAC;AACrC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,eAAW,CAAC,CAAC;AAE/B,iFAAiF;AACjF,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IACpB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;AAC/C,CAAC,CAAC,CAAC;AAEH,kBAAe,GAAG,CAAC"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { BucketRequest, BucketResponse, ConvertApiEndpoints, EndpointKey, EndpointRequest, EndpointResponse, GetExperienceByIdRequest, GetExperienceByKeyRequest, GetFeatureByIdRequest, GetFeatureByKeyRequest, GetVariationByIdRequest, GetVariationByKeyRequest, ListExperiencesRequest, ListFeaturesRequest, ProjectKey, RunExperienceByIdRequest, RunExperienceRequest, RunExperienceResponse, RunFeatureByIdRequest, RunFeatureRequest, RunFeatureResponse, RunFeaturesRequest, RunFeaturesResponse, TrackConversionRequest, TrackConversionResponse } from './contracts';
|
|
2
|
+
type FetchLike = typeof fetch;
|
|
3
|
+
type HeadersInitLike = Record<string, string>;
|
|
4
|
+
type RequestCredentialsLike = 'include' | 'omit' | 'same-origin';
|
|
5
|
+
type RequestWithoutDefaults<T> = Omit<T, 'projectKey' | 'visitorId'> & Partial<Pick<T, Extract<keyof T, 'projectKey' | 'visitorId'>>>;
|
|
6
|
+
export interface ConvertServiceClientOptions {
|
|
7
|
+
baseUrl: string;
|
|
8
|
+
projectKey: ProjectKey;
|
|
9
|
+
visitorId?: string;
|
|
10
|
+
visitorCookieName?: string;
|
|
11
|
+
visitorCookieMaxAgeDays?: number;
|
|
12
|
+
fetcher?: FetchLike;
|
|
13
|
+
credentials?: RequestCredentialsLike;
|
|
14
|
+
defaultVisitorProperties?: Record<string, unknown>;
|
|
15
|
+
defaultLocationProperties?: Record<string, unknown>;
|
|
16
|
+
defaultHeaders?: HeadersInitLike;
|
|
17
|
+
}
|
|
18
|
+
export declare class ConvertServiceError extends Error {
|
|
19
|
+
status: number;
|
|
20
|
+
payload: unknown;
|
|
21
|
+
constructor(message: string, status: number, payload: unknown);
|
|
22
|
+
}
|
|
23
|
+
export declare class ConvertServiceClient {
|
|
24
|
+
private readonly baseUrl;
|
|
25
|
+
private readonly projectKey;
|
|
26
|
+
private readonly visitorCookieName;
|
|
27
|
+
private readonly visitorCookieMaxAgeDays;
|
|
28
|
+
private readonly fetcher;
|
|
29
|
+
private readonly credentials;
|
|
30
|
+
private readonly defaultVisitorProperties?;
|
|
31
|
+
private readonly defaultLocationProperties?;
|
|
32
|
+
private readonly defaultHeaders?;
|
|
33
|
+
private visitorId?;
|
|
34
|
+
constructor(options: ConvertServiceClientOptions);
|
|
35
|
+
getVisitorId(): string;
|
|
36
|
+
setVisitorId(visitorId: string): void;
|
|
37
|
+
request<T extends EndpointKey>(endpoint: T, request: EndpointRequest<T>): Promise<EndpointResponse<T>>;
|
|
38
|
+
health(): Promise<ConvertApiEndpoints['GET /health']['response']>;
|
|
39
|
+
bucket(request?: RequestWithoutDefaults<BucketRequest>): Promise<BucketResponse>;
|
|
40
|
+
listExperiences(request?: Partial<ListExperiencesRequest>): Promise<ConvertApiEndpoints['GET /experiences']['response']>;
|
|
41
|
+
getExperienceByKey(request: RequestWithoutDefaults<GetExperienceByKeyRequest>): Promise<ConvertApiEndpoints['GET /experiences/:experienceKey']['response']>;
|
|
42
|
+
getExperienceById(request: RequestWithoutDefaults<GetExperienceByIdRequest>): Promise<ConvertApiEndpoints['GET /experiences/by-id/:experienceId']['response']>;
|
|
43
|
+
getVariationByKey(request: RequestWithoutDefaults<GetVariationByKeyRequest>): Promise<ConvertApiEndpoints['GET /experiences/:experienceKey/variations/:variationKey']['response']>;
|
|
44
|
+
getVariationById(request: RequestWithoutDefaults<GetVariationByIdRequest>): Promise<ConvertApiEndpoints['GET /experiences/by-id/:experienceId/variations/:variationId']['response']>;
|
|
45
|
+
runExperience(request: RequestWithoutDefaults<RunExperienceRequest>): Promise<RunExperienceResponse>;
|
|
46
|
+
runExperienceById(request: RequestWithoutDefaults<RunExperienceByIdRequest>): Promise<RunExperienceResponse>;
|
|
47
|
+
listFeatures(request?: Partial<ListFeaturesRequest>): Promise<ConvertApiEndpoints['GET /features']['response']>;
|
|
48
|
+
getFeatureByKey(request: RequestWithoutDefaults<GetFeatureByKeyRequest>): Promise<ConvertApiEndpoints['GET /features/:featureKey']['response']>;
|
|
49
|
+
getFeatureById(request: RequestWithoutDefaults<GetFeatureByIdRequest>): Promise<ConvertApiEndpoints['GET /features/by-id/:featureId']['response']>;
|
|
50
|
+
runFeatures(request?: RequestWithoutDefaults<RunFeaturesRequest>): Promise<RunFeaturesResponse>;
|
|
51
|
+
runFeature(request: RequestWithoutDefaults<RunFeatureRequest>): Promise<RunFeatureResponse>;
|
|
52
|
+
runFeatureById(request: RequestWithoutDefaults<RunFeatureByIdRequest>): Promise<RunFeatureResponse>;
|
|
53
|
+
trackConversion(request: RequestWithoutDefaults<TrackConversionRequest>): Promise<TrackConversionResponse>;
|
|
54
|
+
private withProject;
|
|
55
|
+
private withVisitorDefaults;
|
|
56
|
+
private withBucketingDefaults;
|
|
57
|
+
}
|
|
58
|
+
export declare function createConvertServiceClient(options: ConvertServiceClientOptions): ConvertServiceClient;
|
|
59
|
+
export {};
|
|
60
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|