@reopt-ai/data-contract 0.5.0 → 0.6.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/dist/{chunk-2LZBZXBF.js → chunk-33BCA2LH.js} +1 -15
- package/dist/chunk-33BCA2LH.js.map +1 -0
- package/dist/chunk-3EEANNYN.cjs +62 -0
- package/dist/chunk-3EEANNYN.cjs.map +1 -0
- package/dist/{chunk-MFZEIH5Z.js → chunk-5BRCHDS3.js} +22 -4
- package/dist/chunk-5BRCHDS3.js.map +1 -0
- package/dist/chunk-6ZJOC4NJ.js +111 -0
- package/dist/chunk-6ZJOC4NJ.js.map +1 -0
- package/dist/chunk-FP2X3PRK.js +62 -0
- package/dist/chunk-FP2X3PRK.js.map +1 -0
- package/dist/{chunk-VQ2CD5MG.cjs → chunk-FUGY5ANJ.cjs} +13 -13
- package/dist/{chunk-VQ2CD5MG.cjs.map → chunk-FUGY5ANJ.cjs.map} +1 -1
- package/dist/chunk-IXPP5K4I.js +19 -0
- package/dist/chunk-IXPP5K4I.js.map +1 -0
- package/dist/{chunk-AHXPQQKO.js → chunk-IY4MXH35.js} +3 -3
- package/dist/chunk-JHHCT3GA.cjs +19 -0
- package/dist/chunk-JHHCT3GA.cjs.map +1 -0
- package/dist/chunk-KRJPEMFD.cjs +111 -0
- package/dist/chunk-KRJPEMFD.cjs.map +1 -0
- package/dist/{chunk-BCGR27G2.js → chunk-L3WJGIKD.js} +2 -2
- package/dist/{chunk-4B4AY44F.cjs → chunk-LA5F5KR6.cjs} +6 -6
- package/dist/{chunk-4B4AY44F.cjs.map → chunk-LA5F5KR6.cjs.map} +1 -1
- package/dist/{chunk-XI3VP5DN.cjs → chunk-OGL3P7IH.cjs} +26 -8
- package/dist/chunk-OGL3P7IH.cjs.map +1 -0
- package/dist/{chunk-OLSMH4RY.cjs → chunk-YSAEOYPD.cjs} +2 -16
- package/dist/chunk-YSAEOYPD.cjs.map +1 -0
- package/dist/client.cjs +53 -49
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +2 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.js +9 -5
- package/dist/client.js.map +1 -1
- package/dist/control.cjs +5 -4
- package/dist/control.cjs.map +1 -1
- package/dist/control.d.cts +6 -6
- package/dist/control.d.ts +6 -6
- package/dist/control.js +4 -3
- package/dist/events.cjs +17 -0
- package/dist/events.cjs.map +1 -0
- package/dist/events.d.cts +59 -0
- package/dist/events.d.ts +59 -0
- package/dist/events.js +17 -0
- package/dist/events.js.map +1 -0
- package/dist/identity.cjs +47 -0
- package/dist/identity.cjs.map +1 -0
- package/dist/identity.d.cts +90 -0
- package/dist/identity.d.ts +90 -0
- package/dist/identity.js +47 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.cjs +4 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -15
- package/dist/index.d.ts +5 -15
- package/dist/index.js +7 -5
- package/dist/ingest.cjs +16 -2
- package/dist/ingest.cjs.map +1 -1
- package/dist/ingest.d.cts +12 -9
- package/dist/ingest.d.ts +12 -9
- package/dist/ingest.js +16 -2
- package/dist/query.cjs +4 -3
- package/dist/query.cjs.map +1 -1
- package/dist/query.d.cts +9 -8
- package/dist/query.d.ts +9 -8
- package/dist/query.js +3 -2
- package/dist/version-CC4_BBvq.d.cts +26 -0
- package/dist/version-CC4_BBvq.d.ts +26 -0
- package/package.json +13 -2
- package/dist/chunk-2LZBZXBF.js.map +0 -1
- package/dist/chunk-MFZEIH5Z.js.map +0 -1
- package/dist/chunk-OLSMH4RY.cjs.map +0 -1
- package/dist/chunk-XI3VP5DN.cjs.map +0 -1
- /package/dist/{chunk-AHXPQQKO.js.map → chunk-IY4MXH35.js.map} +0 -0
- /package/dist/{chunk-BCGR27G2.js.map → chunk-L3WJGIKD.js.map} +0 -0
package/dist/query.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export { zDate, zInstant } from './index.cjs';
|
|
3
|
+
import './version-CC4_BBvq.cjs';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* `POST /api/v1/query/*` — the read contract.
|
|
@@ -526,8 +527,8 @@ declare const zTrafficOverviewResponse: z.ZodObject<{
|
|
|
526
527
|
}, z.core.$strip>;
|
|
527
528
|
type TrafficOverviewResponse = z.infer<typeof zTrafficOverviewResponse>;
|
|
528
529
|
declare const zTrafficDimension: z.ZodEnum<{
|
|
529
|
-
channel: "channel";
|
|
530
530
|
referrer: "referrer";
|
|
531
|
+
channel: "channel";
|
|
531
532
|
utm: "utm";
|
|
532
533
|
}>;
|
|
533
534
|
type TrafficDimension = z.infer<typeof zTrafficDimension>;
|
|
@@ -536,8 +537,8 @@ declare const zTrafficSourcesInput: z.ZodObject<{
|
|
|
536
537
|
startDate: z.ZodString;
|
|
537
538
|
endDate: z.ZodString;
|
|
538
539
|
dimension: z.ZodEnum<{
|
|
539
|
-
channel: "channel";
|
|
540
540
|
referrer: "referrer";
|
|
541
|
+
channel: "channel";
|
|
541
542
|
utm: "utm";
|
|
542
543
|
}>;
|
|
543
544
|
limit: z.ZodDefault<z.ZodNumber>;
|
|
@@ -561,8 +562,8 @@ declare const zTrafficSourceRow: z.ZodObject<{
|
|
|
561
562
|
type TrafficSourceRow = z.infer<typeof zTrafficSourceRow>;
|
|
562
563
|
declare const zTrafficSourcesData: z.ZodObject<{
|
|
563
564
|
dimension: z.ZodEnum<{
|
|
564
|
-
channel: "channel";
|
|
565
565
|
referrer: "referrer";
|
|
566
|
+
channel: "channel";
|
|
566
567
|
utm: "utm";
|
|
567
568
|
}>;
|
|
568
569
|
timezone: z.ZodString;
|
|
@@ -585,8 +586,8 @@ type TrafficSourcesData = z.infer<typeof zTrafficSourcesData>;
|
|
|
585
586
|
declare const zTrafficSourcesResponse: z.ZodObject<{
|
|
586
587
|
data: z.ZodObject<{
|
|
587
588
|
dimension: z.ZodEnum<{
|
|
588
|
-
channel: "channel";
|
|
589
589
|
referrer: "referrer";
|
|
590
|
+
channel: "channel";
|
|
590
591
|
utm: "utm";
|
|
591
592
|
}>;
|
|
592
593
|
timezone: z.ZodString;
|
|
@@ -628,11 +629,11 @@ declare const zTrafficSourcesResponse: z.ZodObject<{
|
|
|
628
629
|
type TrafficSourcesResponse = z.infer<typeof zTrafficSourcesResponse>;
|
|
629
630
|
declare const QUERY_ERROR_CODES: readonly ["unauthorized", "project_scope_mismatch", "query_scope_missing", "validation_failed", "unsupported_combination", "project_purging", "rate_limited", "query_timeout", "internal_error"];
|
|
630
631
|
declare const zQueryErrorCode: z.ZodEnum<{
|
|
631
|
-
unauthorized: "unauthorized";
|
|
632
632
|
validation_failed: "validation_failed";
|
|
633
|
+
unauthorized: "unauthorized";
|
|
633
634
|
rate_limited: "rate_limited";
|
|
634
|
-
internal_error: "internal_error";
|
|
635
635
|
project_purging: "project_purging";
|
|
636
|
+
internal_error: "internal_error";
|
|
636
637
|
project_scope_mismatch: "project_scope_mismatch";
|
|
637
638
|
query_scope_missing: "query_scope_missing";
|
|
638
639
|
unsupported_combination: "unsupported_combination";
|
|
@@ -642,11 +643,11 @@ type QueryErrorCode = z.infer<typeof zQueryErrorCode>;
|
|
|
642
643
|
declare const zQueryError: z.ZodObject<{
|
|
643
644
|
status: z.ZodNumber;
|
|
644
645
|
code: z.ZodEnum<{
|
|
645
|
-
unauthorized: "unauthorized";
|
|
646
646
|
validation_failed: "validation_failed";
|
|
647
|
+
unauthorized: "unauthorized";
|
|
647
648
|
rate_limited: "rate_limited";
|
|
648
|
-
internal_error: "internal_error";
|
|
649
649
|
project_purging: "project_purging";
|
|
650
|
+
internal_error: "internal_error";
|
|
650
651
|
project_scope_mismatch: "project_scope_mismatch";
|
|
651
652
|
query_scope_missing: "query_scope_missing";
|
|
652
653
|
unsupported_combination: "unsupported_combination";
|
package/dist/query.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export { zDate, zInstant } from './index.js';
|
|
3
|
+
import './version-CC4_BBvq.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* `POST /api/v1/query/*` — the read contract.
|
|
@@ -526,8 +527,8 @@ declare const zTrafficOverviewResponse: z.ZodObject<{
|
|
|
526
527
|
}, z.core.$strip>;
|
|
527
528
|
type TrafficOverviewResponse = z.infer<typeof zTrafficOverviewResponse>;
|
|
528
529
|
declare const zTrafficDimension: z.ZodEnum<{
|
|
529
|
-
channel: "channel";
|
|
530
530
|
referrer: "referrer";
|
|
531
|
+
channel: "channel";
|
|
531
532
|
utm: "utm";
|
|
532
533
|
}>;
|
|
533
534
|
type TrafficDimension = z.infer<typeof zTrafficDimension>;
|
|
@@ -536,8 +537,8 @@ declare const zTrafficSourcesInput: z.ZodObject<{
|
|
|
536
537
|
startDate: z.ZodString;
|
|
537
538
|
endDate: z.ZodString;
|
|
538
539
|
dimension: z.ZodEnum<{
|
|
539
|
-
channel: "channel";
|
|
540
540
|
referrer: "referrer";
|
|
541
|
+
channel: "channel";
|
|
541
542
|
utm: "utm";
|
|
542
543
|
}>;
|
|
543
544
|
limit: z.ZodDefault<z.ZodNumber>;
|
|
@@ -561,8 +562,8 @@ declare const zTrafficSourceRow: z.ZodObject<{
|
|
|
561
562
|
type TrafficSourceRow = z.infer<typeof zTrafficSourceRow>;
|
|
562
563
|
declare const zTrafficSourcesData: z.ZodObject<{
|
|
563
564
|
dimension: z.ZodEnum<{
|
|
564
|
-
channel: "channel";
|
|
565
565
|
referrer: "referrer";
|
|
566
|
+
channel: "channel";
|
|
566
567
|
utm: "utm";
|
|
567
568
|
}>;
|
|
568
569
|
timezone: z.ZodString;
|
|
@@ -585,8 +586,8 @@ type TrafficSourcesData = z.infer<typeof zTrafficSourcesData>;
|
|
|
585
586
|
declare const zTrafficSourcesResponse: z.ZodObject<{
|
|
586
587
|
data: z.ZodObject<{
|
|
587
588
|
dimension: z.ZodEnum<{
|
|
588
|
-
channel: "channel";
|
|
589
589
|
referrer: "referrer";
|
|
590
|
+
channel: "channel";
|
|
590
591
|
utm: "utm";
|
|
591
592
|
}>;
|
|
592
593
|
timezone: z.ZodString;
|
|
@@ -628,11 +629,11 @@ declare const zTrafficSourcesResponse: z.ZodObject<{
|
|
|
628
629
|
type TrafficSourcesResponse = z.infer<typeof zTrafficSourcesResponse>;
|
|
629
630
|
declare const QUERY_ERROR_CODES: readonly ["unauthorized", "project_scope_mismatch", "query_scope_missing", "validation_failed", "unsupported_combination", "project_purging", "rate_limited", "query_timeout", "internal_error"];
|
|
630
631
|
declare const zQueryErrorCode: z.ZodEnum<{
|
|
631
|
-
unauthorized: "unauthorized";
|
|
632
632
|
validation_failed: "validation_failed";
|
|
633
|
+
unauthorized: "unauthorized";
|
|
633
634
|
rate_limited: "rate_limited";
|
|
634
|
-
internal_error: "internal_error";
|
|
635
635
|
project_purging: "project_purging";
|
|
636
|
+
internal_error: "internal_error";
|
|
636
637
|
project_scope_mismatch: "project_scope_mismatch";
|
|
637
638
|
query_scope_missing: "query_scope_missing";
|
|
638
639
|
unsupported_combination: "unsupported_combination";
|
|
@@ -642,11 +643,11 @@ type QueryErrorCode = z.infer<typeof zQueryErrorCode>;
|
|
|
642
643
|
declare const zQueryError: z.ZodObject<{
|
|
643
644
|
status: z.ZodNumber;
|
|
644
645
|
code: z.ZodEnum<{
|
|
645
|
-
unauthorized: "unauthorized";
|
|
646
646
|
validation_failed: "validation_failed";
|
|
647
|
+
unauthorized: "unauthorized";
|
|
647
648
|
rate_limited: "rate_limited";
|
|
648
|
-
internal_error: "internal_error";
|
|
649
649
|
project_purging: "project_purging";
|
|
650
|
+
internal_error: "internal_error";
|
|
650
651
|
project_scope_mismatch: "project_scope_mismatch";
|
|
651
652
|
query_scope_missing: "query_scope_missing";
|
|
652
653
|
unsupported_combination: "unsupported_combination";
|
package/dist/query.js
CHANGED
|
@@ -36,11 +36,12 @@ import {
|
|
|
36
36
|
zTrafficSourcesInput,
|
|
37
37
|
zTrafficSourcesResponse,
|
|
38
38
|
zTrafficTotals
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-L3WJGIKD.js";
|
|
40
40
|
import {
|
|
41
41
|
zDate,
|
|
42
42
|
zInstant
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-33BCA2LH.js";
|
|
44
|
+
import "./chunk-IXPP5K4I.js";
|
|
44
45
|
export {
|
|
45
46
|
QUERY_API_PATHS,
|
|
46
47
|
QUERY_ERROR_CODES,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire version and header names — the part of the contract every party
|
|
3
|
+
* needs and none of them should pay zod for. Both `.` and `./identity`
|
|
4
|
+
* re-export these; browser bundles import them through `./identity` so the
|
|
5
|
+
* schema modules stay out of the client graph.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Contract version. Bumped on any breaking change to a schema in this package.
|
|
9
|
+
* The server echoes the version it was built against in `x-reopt-contract-version`;
|
|
10
|
+
* `createDataClient` surfaces a mismatch rather than guessing.
|
|
11
|
+
*
|
|
12
|
+
* Must equal the package version (`package-identity.test.ts` enforces it), so
|
|
13
|
+
* every publish is a wire bump: consumers that check for exact equality —
|
|
14
|
+
* every SDK — must upgrade in lockstep with the server.
|
|
15
|
+
*/
|
|
16
|
+
declare const CONTRACT_VERSION = "0.6.0";
|
|
17
|
+
/** Response header carrying the server's contract version. */
|
|
18
|
+
declare const CONTRACT_VERSION_HEADER = "x-reopt-contract-version";
|
|
19
|
+
/** Request header names shared by the ingest and query planes. */
|
|
20
|
+
declare const CLIENT_ID_HEADER = "reopt-client-id";
|
|
21
|
+
declare const CLIENT_SECRET_HEADER = "reopt-client-secret";
|
|
22
|
+
declare const WRITE_KEY_HEADER = "reopt-write-key";
|
|
23
|
+
declare const DEVICE_ID_HEADER = "reopt-device-id";
|
|
24
|
+
declare const REQUEST_ID_HEADER = "x-request-id";
|
|
25
|
+
|
|
26
|
+
export { CLIENT_ID_HEADER as C, DEVICE_ID_HEADER as D, REQUEST_ID_HEADER as R, WRITE_KEY_HEADER as W, CLIENT_SECRET_HEADER as a, CONTRACT_VERSION as b, CONTRACT_VERSION_HEADER as c };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire version and header names — the part of the contract every party
|
|
3
|
+
* needs and none of them should pay zod for. Both `.` and `./identity`
|
|
4
|
+
* re-export these; browser bundles import them through `./identity` so the
|
|
5
|
+
* schema modules stay out of the client graph.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Contract version. Bumped on any breaking change to a schema in this package.
|
|
9
|
+
* The server echoes the version it was built against in `x-reopt-contract-version`;
|
|
10
|
+
* `createDataClient` surfaces a mismatch rather than guessing.
|
|
11
|
+
*
|
|
12
|
+
* Must equal the package version (`package-identity.test.ts` enforces it), so
|
|
13
|
+
* every publish is a wire bump: consumers that check for exact equality —
|
|
14
|
+
* every SDK — must upgrade in lockstep with the server.
|
|
15
|
+
*/
|
|
16
|
+
declare const CONTRACT_VERSION = "0.6.0";
|
|
17
|
+
/** Response header carrying the server's contract version. */
|
|
18
|
+
declare const CONTRACT_VERSION_HEADER = "x-reopt-contract-version";
|
|
19
|
+
/** Request header names shared by the ingest and query planes. */
|
|
20
|
+
declare const CLIENT_ID_HEADER = "reopt-client-id";
|
|
21
|
+
declare const CLIENT_SECRET_HEADER = "reopt-client-secret";
|
|
22
|
+
declare const WRITE_KEY_HEADER = "reopt-write-key";
|
|
23
|
+
declare const DEVICE_ID_HEADER = "reopt-device-id";
|
|
24
|
+
declare const REQUEST_ID_HEADER = "x-request-id";
|
|
25
|
+
|
|
26
|
+
export { CLIENT_ID_HEADER as C, DEVICE_ID_HEADER as D, REQUEST_ID_HEADER as R, WRITE_KEY_HEADER as W, CLIENT_SECRET_HEADER as a, CONTRACT_VERSION as b, CONTRACT_VERSION_HEADER as c };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reopt-ai/data-contract",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Wire contract (zod schemas + typed client) for the reopt-data ingest and query APIs",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"directory": "packages/data-contract"
|
|
10
10
|
},
|
|
11
11
|
"type": "module",
|
|
12
|
+
"sideEffects": false,
|
|
12
13
|
"publishConfig": {
|
|
13
14
|
"access": "public",
|
|
14
15
|
"registry": "https://registry.npmjs.org",
|
|
@@ -44,6 +45,16 @@
|
|
|
44
45
|
"types": "./dist/report.d.ts",
|
|
45
46
|
"import": "./dist/report.js",
|
|
46
47
|
"require": "./dist/report.cjs"
|
|
48
|
+
},
|
|
49
|
+
"./identity": {
|
|
50
|
+
"types": "./dist/identity.d.ts",
|
|
51
|
+
"import": "./dist/identity.js",
|
|
52
|
+
"require": "./dist/identity.cjs"
|
|
53
|
+
},
|
|
54
|
+
"./events": {
|
|
55
|
+
"types": "./dist/events.d.ts",
|
|
56
|
+
"import": "./dist/events.js",
|
|
57
|
+
"require": "./dist/events.cjs"
|
|
47
58
|
}
|
|
48
59
|
},
|
|
49
60
|
"main": "./dist/index.cjs",
|
|
@@ -67,6 +78,6 @@
|
|
|
67
78
|
"check-types": "tsc --noEmit",
|
|
68
79
|
"dev": "tsup --watch",
|
|
69
80
|
"lint": "oxlint .",
|
|
70
|
-
"test": "tsx --test src/__tests__/ingest.test.ts src/__tests__/query.test.ts src/__tests__/control.test.ts src/__tests__/client.test.ts src/__tests__/report.test.ts src/__tests__/package-identity.test.ts"
|
|
81
|
+
"test": "tsx --test src/__tests__/ingest.test.ts src/__tests__/query.test.ts src/__tests__/control.test.ts src/__tests__/client.test.ts src/__tests__/report.test.ts src/__tests__/identity.test.ts src/__tests__/package-identity.test.ts"
|
|
71
82
|
}
|
|
72
83
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * `@reopt-ai/data-contract` — the wire contract between reopt-data and its\n * server-credential consumers (RFC-0023).\n *\n * Subpaths:\n * `@reopt-ai/data-contract` this module — version, shared primitives, errors\n * `@reopt-ai/data-contract/ingest` POST /api/track request + response schemas\n * `@reopt-ai/data-contract/query` POST /api/v1/query/* request + response schemas\n * `@reopt-ai/data-contract/control` provisioning request + response schemas\n * `@reopt-ai/data-contract/client` createDataClient() — typed fetch over all\n *\n * Only `/client` touches `fetch`; everything else is pure zod. The single\n * runtime dependency is zod, deliberately — consumers bundle this.\n */\nimport { z } from \"zod\";\n\n/**\n * Contract version. Bumped on any breaking change to a schema in this package.\n * The server echoes the version it was built against in `x-reopt-contract-version`;\n * `createDataClient` surfaces a mismatch rather than guessing.\n */\nexport const CONTRACT_VERSION = \"0.5.0\";\n\n/** Response header carrying the server's contract version. */\nexport const CONTRACT_VERSION_HEADER = \"x-reopt-contract-version\";\n\n/** Request header names shared by the ingest and query planes. */\nexport const CLIENT_ID_HEADER = \"reopt-client-id\";\nexport const CLIENT_SECRET_HEADER = \"reopt-client-secret\";\nexport const WRITE_KEY_HEADER = \"reopt-write-key\";\nexport const DEVICE_ID_HEADER = \"reopt-device-id\";\nexport const REQUEST_ID_HEADER = \"x-request-id\";\n\n/**\n * A real calendar date. The query's resolved IANA zone decides which instants\n * its inclusive day covers.\n */\nexport const zDate = z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"expected YYYY-MM-DD\")\n .refine(isRealCalendarDate, \"date does not exist\");\n\nfunction isRealCalendarDate(value: string): boolean {\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) return false;\n const [year, month, day] = value.split(\"-\").map(Number);\n const instant = new Date(Date.UTC(year!, month! - 1, day!));\n return instant.getUTCFullYear() === year && instant.getUTCMonth() === month! - 1 && instant.getUTCDate() === day;\n}\n\n/** ISO-8601 instant, as produced by `Date#toISOString()`. */\nexport const zInstant = z.string().min(20);\n\n/**\n * Client credentials. The same pair authenticates both planes; what a given\n * client may reach is decided server-side by `Client.scopes`\n * (`\"ingest\"` / `\"query\"` / `\"provision\"`).\n */\nexport interface ClientCredentials {\n clientId: string;\n clientSecret: string;\n}\n\n/**\n * Credentials are optional on the client itself because the provisioning\n * surface does not use them — it authenticates per call with a platform or\n * organization key. A caller that only provisions should not have to invent an\n * empty client id to satisfy a constructor.\n *\n * Reaching ingest or query without them fails with `credentials_missing`\n * rather than sending an unauthenticated request and reporting the 401 as a\n * server problem.\n */\nexport type OptionalClientCredentials = Partial<ClientCredentials>;\n\n/**\n * Every non-2xx response from either plane, plus the two failures the client\n * itself raises (`contract_mismatch`, `network_error`).\n */\nexport class DataApiError extends Error {\n /**\n * Brand for {@link DataApiError.is}. A bundler that inlines this module into\n * more than one entry point produces two distinct classes, and `instanceof`\n * silently stops matching — the brand survives that, and realm boundaries.\n */\n readonly isDataApiError = true as const;\n\n readonly status: number;\n readonly code: string;\n readonly requestId?: string;\n /** Parsed from `Retry-After`, in milliseconds. Only set on 429. */\n readonly retryAfterMs?: number;\n /** zod issues for `validation_failed`, or the raw body when unparseable. */\n readonly details?: unknown;\n\n constructor(init: {\n message: string;\n status: number;\n code: string;\n requestId?: string;\n retryAfterMs?: number;\n details?: unknown;\n }) {\n super(init.message);\n this.name = \"DataApiError\";\n this.status = init.status;\n this.code = init.code;\n this.requestId = init.requestId;\n this.retryAfterMs = init.retryAfterMs;\n this.details = init.details;\n }\n\n /** 429 and 5xx are worth retrying; 4xx are the caller's fault and are not. */\n get retryable(): boolean {\n return this.status === 429 || this.status >= 500;\n }\n\n /**\n * Prefer this over `instanceof` in a `catch`. It holds even when the class\n * has been duplicated — across CJS entry points, across bundler chunks, or\n * across realms — where `instanceof` quietly returns false.\n */\n static is(value: unknown): value is DataApiError {\n return (\n value instanceof DataApiError ||\n (typeof value === \"object\" && value !== null && (value as { isDataApiError?: unknown }).isDataApiError === true)\n );\n }\n}\n"],"mappings":";AAcA,SAAS,SAAS;AAOX,IAAM,mBAAmB;AAGzB,IAAM,0BAA0B;AAGhC,IAAM,mBAAmB;AACzB,IAAM,uBAAuB;AAC7B,IAAM,mBAAmB;AACzB,IAAM,mBAAmB;AACzB,IAAM,oBAAoB;AAM1B,IAAM,QAAQ,EAClB,OAAO,EACP,MAAM,uBAAuB,qBAAqB,EAClD,OAAO,oBAAoB,qBAAqB;AAEnD,SAAS,mBAAmB,OAAwB;AAClD,MAAI,CAAC,sBAAsB,KAAK,KAAK,EAAG,QAAO;AAC/C,QAAM,CAAC,MAAM,OAAO,GAAG,IAAI,MAAM,MAAM,GAAG,EAAE,IAAI,MAAM;AACtD,QAAM,UAAU,IAAI,KAAK,KAAK,IAAI,MAAO,QAAS,GAAG,GAAI,CAAC;AAC1D,SAAO,QAAQ,eAAe,MAAM,QAAQ,QAAQ,YAAY,MAAM,QAAS,KAAK,QAAQ,WAAW,MAAM;AAC/G;AAGO,IAAM,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE;AA4BlC,IAAM,eAAN,MAAM,sBAAqB,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM7B,iBAAiB;AAAA,EAEjB;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EAET,YAAY,MAOT;AACD,UAAM,KAAK,OAAO;AAClB,SAAK,OAAO;AACZ,SAAK,SAAS,KAAK;AACnB,SAAK,OAAO,KAAK;AACjB,SAAK,YAAY,KAAK;AACtB,SAAK,eAAe,KAAK;AACzB,SAAK,UAAU,KAAK;AAAA,EACtB;AAAA;AAAA,EAGA,IAAI,YAAqB;AACvB,WAAO,KAAK,WAAW,OAAO,KAAK,UAAU;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,GAAG,OAAuC;AAC/C,WACE,iBAAiB,iBAChB,OAAO,UAAU,YAAY,UAAU,QAAS,MAAuC,mBAAmB;AAAA,EAE/G;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ingest.ts"],"sourcesContent":["/**\n * `POST /api/track` — the ingest contract.\n *\n * The request shape is shared by both credential modes. The *response* shape\n * is where server-ingest differs: it reports per-event rejections instead of\n * failing a whole batch, because a forwarder that stalls on one malformed row\n * stops forwarding forever.\n *\n * Absorbed from the former internal `@reopt/ingest-contract`; that package is\n * now a deprecated re-export of this module.\n */\nimport { z } from \"zod\";\n\n/** Event names the pipeline reserves for its own session bookkeeping. */\nexport const RESERVED_EVENT_NAMES = [\"session_start\", \"session_end\", \"screen_view\"] as const;\n\n/** Hard cap enforced by the route before the body is even parsed. */\nexport const MAX_TRACK_PAYLOAD_BYTES = 512_000;\n\nconst zClientEventMetadata = {\n /**\n * Client-generated UUID. This is the project-scoped idempotency key stored as\n * `RawEvent.clientEventId`; the server uses a separate globally unique raw\n * event id internally. Re-sending the same eventId to the same project is\n * always safe and always reported as a duplicate.\n */\n eventId: z.uuid(),\n /** Event time, epoch milliseconds. */\n timestamp: z.number().int().nonnegative(),\n};\n\nexport const zTrackPayload = z\n .object({\n name: z.string().min(1).max(200),\n properties: z.record(z.string(), z.unknown()).optional(),\n profileId: z.string().max(500).or(z.number()).optional(),\n })\n .refine((data) => !RESERVED_EVENT_NAMES.includes(data.name as never), {\n message: `Event name cannot be one of the reserved names: ${RESERVED_EVENT_NAMES.join(\", \")}`,\n path: [\"name\"],\n });\n\nexport const zIdentifyPayload = z.object({\n profileId: z.string().min(1).max(500).or(z.number()),\n firstName: z.string().max(200).optional(),\n lastName: z.string().max(200).optional(),\n email: z.email().max(320).optional(),\n avatar: z.url().max(2000).optional(),\n properties: z.record(z.string(), z.unknown()).optional(),\n});\n\nexport const zIncrementPayload = z.object({\n profileId: z.string().min(1).max(500).or(z.number()),\n property: z.string().min(1).max(200),\n value: z.number().positive().optional(),\n});\n\nexport const zDecrementPayload = z.object({\n profileId: z.string().min(1).max(500).or(z.number()),\n property: z.string().min(1).max(200),\n value: z.number().positive().optional(),\n});\n\nexport const zTrackHandlerPayload = z.discriminatedUnion(\"type\", [\n z.object({ type: z.literal(\"track\"), payload: zTrackPayload, ...zClientEventMetadata }),\n z.object({ type: z.literal(\"identify\"), payload: zIdentifyPayload, ...zClientEventMetadata }),\n z.object({ type: z.literal(\"increment\"), payload: zIncrementPayload, ...zClientEventMetadata }),\n z.object({ type: z.literal(\"decrement\"), payload: zDecrementPayload, ...zClientEventMetadata }),\n]);\n\nexport type ITrackPayload = z.infer<typeof zTrackPayload>;\nexport type IIdentifyPayload = z.infer<typeof zIdentifyPayload>;\nexport type IIncrementPayload = z.infer<typeof zIncrementPayload>;\nexport type IDecrementPayload = z.infer<typeof zDecrementPayload>;\nexport type ITrackHandlerPayload = z.infer<typeof zTrackHandlerPayload>;\n\n// ─── Ingest mode ────────────────────────────────────────────────────────────\n\n/**\n * Decided by the credential presented, never by a header the caller sets:\n * `reopt-write-key` alone → `browser`, `reopt-client-id` + `reopt-client-secret`\n * → `server`. A separate mode header could disagree with the credential; this\n * cannot.\n *\n * `browser` keeps the 5-second click dedup and device-scoped sessions.\n * `server` drops both and relies solely on `eventId` idempotency — a server\n * batch legitimately contains the same event name many times within one second,\n * and its \"device\" is a process, not a person.\n */\nexport const INGEST_MODES = [\"browser\", \"server\"] as const;\nexport const zIngestMode = z.enum(INGEST_MODES);\nexport type IngestMode = z.infer<typeof zIngestMode>;\n\n// ─── Response ───────────────────────────────────────────────────────────────\n\n/**\n * Why a single event in a batch was dropped. These are permanent: re-sending\n * the same row produces the same rejection, so a forwarder should count it as\n * skipped and advance its cursor rather than retry.\n */\nexport const INGEST_REJECTION_REASONS = [\n /** Failed the zod schema (bad eventId, missing name, wrong types…). */\n \"validation_failed\",\n /** Used one of RESERVED_EVENT_NAMES. */\n \"reserved_name\",\n /** Another row earlier in the same batch already claimed this eventId. */\n \"duplicate_in_batch\",\n] as const;\nexport const zIngestRejectionReason = z.enum(INGEST_REJECTION_REASONS);\nexport type IngestRejectionReason = z.infer<typeof zIngestRejectionReason>;\n\nexport const zIngestRejection = z.object({\n /** May be absent or malformed on `validation_failed` — hence `string`, not `uuid`. */\n eventId: z.string(),\n /** Position in the submitted batch. The only reliable identifier when eventId itself is bad. */\n index: z.number().int().nonnegative(),\n reason: zIngestRejectionReason,\n message: z.string().optional(),\n});\nexport type IngestRejection = z.infer<typeof zIngestRejection>;\n\nconst ingestCounts = {\n /** Raw events newly persisted by this request. */\n accepted: z.number().int().nonnegative(),\n /**\n * Events the server already had. In `server` mode this is exactly the\n * eventId-idempotency count; in `browser` mode it also includes the\n * 5-second click dedup.\n */\n duplicates: z.number().int().nonnegative(),\n /** Always `[]` in `browser` mode, where a bad row fails the whole batch. */\n rejected: z.array(zIngestRejection),\n};\n\n/** 200 — accepted and handed to the materialize queue. */\nexport const zIngestOkResponse = z.object({\n status: z.literal(\"ok\"),\n requestId: z.string(),\n mode: zIngestMode,\n ...ingestCounts,\n queued: z.boolean(),\n});\n\n/**\n * 202 — raw events landed, but enqueueing the materialize task failed. No data\n * is lost: the `replay-unmaterialized` cron picks these up. A forwarder should\n * treat this as success and advance its cursor. Carries the same counts as 200\n * so the reconciliation below works here too.\n */\nexport const zIngestAcceptedResponse = z.object({\n status: z.literal(\"accepted\"),\n requestId: z.string(),\n mode: zIngestMode,\n ...ingestCounts,\n backgroundQueued: z.literal(false),\n replay: z.literal(\"unmaterialized-raw-events\"),\n});\n\nexport const zIngestResponse = z.discriminatedUnion(\"status\", [zIngestOkResponse, zIngestAcceptedResponse]);\n\nexport type IngestOkResponse = z.infer<typeof zIngestOkResponse>;\nexport type IngestAcceptedResponse = z.infer<typeof zIngestAcceptedResponse>;\nexport type IngestResponse = z.infer<typeof zIngestResponse>;\n\n/**\n * The reconciliation a forwarder runs on every 2xx:\n *\n * accepted + duplicates + rejected.length === events sent\n *\n * A mismatch means the server silently dropped something, which is exactly the\n * failure mode this contract exists to make impossible to miss.\n */\nexport function reconcileIngestResponse(response: IngestResponse, sentCount: number): boolean {\n return response.accepted + response.duplicates + response.rejected.length === sentCount;\n}\n\n// ─── Errors ─────────────────────────────────────────────────────────────────\n\n/**\n * Stable machine-readable codes. Branch on these, never on `error`/`message`,\n * which are prose and may be reworded.\n */\nexport const INGEST_ERROR_CODES = [\n /** 401 — unknown or mismatched credentials. Stop and alert; do not retry. */\n \"unauthorized\",\n /** 400 — body was not valid JSON. */\n \"invalid_json\",\n /** 400 — request exceeded MAX_TRACK_PAYLOAD_BYTES. Split the batch. */\n \"payload_too_large\",\n /** 400 — zero events submitted. */\n \"empty_batch\",\n /** 400 — batch contained a `type: \"alias\"` event, which is not supported. */\n \"alias_unsupported\",\n /** 400 — the project has no organization; it cannot be billed or quota-checked. */\n \"project_without_organization\",\n /** 400 — `browser` mode only: a row failed validation. `server` mode reports these in `rejected[]`. */\n \"validation_failed\",\n /** 429 — per-project request rate limit. `Retry-After` set. Pause this tick, keep the cursor. */\n \"rate_limited\",\n /** 429 — the organization's monthly event quota is exhausted. `Retry-After` set (capped at 1h). Alert: a human must raise the limit. */\n \"quota_exceeded\",\n /** 410 — the project is being deleted. Permanent; stop sending. */\n \"project_purging\",\n /** 500 — unexpected server failure. Back off and retry. */\n \"internal_error\",\n] as const;\nexport const zIngestErrorCode = z.enum(INGEST_ERROR_CODES);\nexport type IngestErrorCode = z.infer<typeof zIngestErrorCode>;\n\nexport const zIngestError = z.object({\n status: z.number().int(),\n code: zIngestErrorCode,\n error: z.string(),\n message: z.string().optional(),\n /** zod issues, when `code === \"validation_failed\"`. */\n errors: z.unknown().optional(),\n requestId: z.string().optional(),\n});\nexport type IngestError = z.infer<typeof zIngestError>;\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/eric/reopt-ai/reopt-data/packages/data-contract/dist/chunk-OLSMH4RY.cjs","../src/index.ts"],"names":[],"mappings":"AAAA;ACcA,0BAAkB;AAOX,IAAM,iBAAA,EAAmB,OAAA;AAGzB,IAAM,wBAAA,EAA0B,0BAAA;AAGhC,IAAM,iBAAA,EAAmB,iBAAA;AACzB,IAAM,qBAAA,EAAuB,qBAAA;AAC7B,IAAM,iBAAA,EAAmB,iBAAA;AACzB,IAAM,iBAAA,EAAmB,iBAAA;AACzB,IAAM,kBAAA,EAAoB,cAAA;AAM1B,IAAM,MAAA,EAAQ,MAAA,CAClB,MAAA,CAAO,CAAA,CACP,KAAA,CAAM,qBAAA,EAAuB,qBAAqB,CAAA,CAClD,MAAA,CAAO,kBAAA,EAAoB,qBAAqB,CAAA;AAEnD,SAAS,kBAAA,CAAmB,KAAA,EAAwB;AAClD,EAAA,GAAA,CAAI,CAAC,qBAAA,CAAsB,IAAA,CAAK,KAAK,CAAA,EAAG,OAAO,KAAA;AAC/C,EAAA,MAAM,CAAC,IAAA,EAAM,KAAA,EAAO,GAAG,EAAA,EAAI,KAAA,CAAM,KAAA,CAAM,GAAG,CAAA,CAAE,GAAA,CAAI,MAAM,CAAA;AACtD,EAAA,MAAM,QAAA,EAAU,IAAI,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,IAAA,EAAO,MAAA,EAAS,CAAA,EAAG,GAAI,CAAC,CAAA;AAC1D,EAAA,OAAO,OAAA,CAAQ,cAAA,CAAe,EAAA,IAAM,KAAA,GAAQ,OAAA,CAAQ,WAAA,CAAY,EAAA,IAAM,MAAA,EAAS,EAAA,GAAK,OAAA,CAAQ,UAAA,CAAW,EAAA,IAAM,GAAA;AAC/G;AAGO,IAAM,SAAA,EAAW,MAAA,CAAE,MAAA,CAAO,CAAA,CAAE,GAAA,CAAI,EAAE,CAAA;AA4BlC,IAAM,aAAA,YAAN,MAAM,cAAA,QAAqB,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAM7B,eAAA,EAAiB,KAAA;AAAA,EAEjB;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EAET,WAAA,CAAY,IAAA,EAOT;AACD,IAAA,KAAA,CAAM,IAAA,CAAK,OAAO,qCAAA;AAClB,IAAA,IAAA,CAAK,KAAA,EAAO,cAAA;AACZ,IAAA,IAAA,CAAK,OAAA,EAAS,IAAA,CAAK,MAAA;AACnB,IAAA,IAAA,CAAK,KAAA,EAAO,IAAA,CAAK,IAAA;AACjB,IAAA,IAAA,CAAK,UAAA,EAAY,IAAA,CAAK,SAAA;AACtB,IAAA,IAAA,CAAK,aAAA,EAAe,IAAA,CAAK,YAAA;AACzB,IAAA,IAAA,CAAK,QAAA,EAAU,IAAA,CAAK,OAAA;AAAA,EACtB;AAAA;AAAA,EAGA,IAAI,SAAA,CAAA,EAAqB;AACvB,IAAA,OAAO,IAAA,CAAK,OAAA,IAAW,IAAA,GAAO,IAAA,CAAK,OAAA,GAAU,GAAA;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,EAAA,CAAG,KAAA,EAAuC;AAC/C,IAAA,OACE,MAAA,WAAiB,cAAA,GAChB,OAAO,MAAA,IAAU,SAAA,GAAY,MAAA,IAAU,KAAA,GAAS,KAAA,CAAuC,eAAA,IAAmB,IAAA;AAAA,EAE/G;AACF,UAAA;AD1EA;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,2aAAC","file":"/Users/eric/reopt-ai/reopt-data/packages/data-contract/dist/chunk-OLSMH4RY.cjs","sourcesContent":[null,"/**\n * `@reopt-ai/data-contract` — the wire contract between reopt-data and its\n * server-credential consumers (RFC-0023).\n *\n * Subpaths:\n * `@reopt-ai/data-contract` this module — version, shared primitives, errors\n * `@reopt-ai/data-contract/ingest` POST /api/track request + response schemas\n * `@reopt-ai/data-contract/query` POST /api/v1/query/* request + response schemas\n * `@reopt-ai/data-contract/control` provisioning request + response schemas\n * `@reopt-ai/data-contract/client` createDataClient() — typed fetch over all\n *\n * Only `/client` touches `fetch`; everything else is pure zod. The single\n * runtime dependency is zod, deliberately — consumers bundle this.\n */\nimport { z } from \"zod\";\n\n/**\n * Contract version. Bumped on any breaking change to a schema in this package.\n * The server echoes the version it was built against in `x-reopt-contract-version`;\n * `createDataClient` surfaces a mismatch rather than guessing.\n */\nexport const CONTRACT_VERSION = \"0.5.0\";\n\n/** Response header carrying the server's contract version. */\nexport const CONTRACT_VERSION_HEADER = \"x-reopt-contract-version\";\n\n/** Request header names shared by the ingest and query planes. */\nexport const CLIENT_ID_HEADER = \"reopt-client-id\";\nexport const CLIENT_SECRET_HEADER = \"reopt-client-secret\";\nexport const WRITE_KEY_HEADER = \"reopt-write-key\";\nexport const DEVICE_ID_HEADER = \"reopt-device-id\";\nexport const REQUEST_ID_HEADER = \"x-request-id\";\n\n/**\n * A real calendar date. The query's resolved IANA zone decides which instants\n * its inclusive day covers.\n */\nexport const zDate = z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"expected YYYY-MM-DD\")\n .refine(isRealCalendarDate, \"date does not exist\");\n\nfunction isRealCalendarDate(value: string): boolean {\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) return false;\n const [year, month, day] = value.split(\"-\").map(Number);\n const instant = new Date(Date.UTC(year!, month! - 1, day!));\n return instant.getUTCFullYear() === year && instant.getUTCMonth() === month! - 1 && instant.getUTCDate() === day;\n}\n\n/** ISO-8601 instant, as produced by `Date#toISOString()`. */\nexport const zInstant = z.string().min(20);\n\n/**\n * Client credentials. The same pair authenticates both planes; what a given\n * client may reach is decided server-side by `Client.scopes`\n * (`\"ingest\"` / `\"query\"` / `\"provision\"`).\n */\nexport interface ClientCredentials {\n clientId: string;\n clientSecret: string;\n}\n\n/**\n * Credentials are optional on the client itself because the provisioning\n * surface does not use them — it authenticates per call with a platform or\n * organization key. A caller that only provisions should not have to invent an\n * empty client id to satisfy a constructor.\n *\n * Reaching ingest or query without them fails with `credentials_missing`\n * rather than sending an unauthenticated request and reporting the 401 as a\n * server problem.\n */\nexport type OptionalClientCredentials = Partial<ClientCredentials>;\n\n/**\n * Every non-2xx response from either plane, plus the two failures the client\n * itself raises (`contract_mismatch`, `network_error`).\n */\nexport class DataApiError extends Error {\n /**\n * Brand for {@link DataApiError.is}. A bundler that inlines this module into\n * more than one entry point produces two distinct classes, and `instanceof`\n * silently stops matching — the brand survives that, and realm boundaries.\n */\n readonly isDataApiError = true as const;\n\n readonly status: number;\n readonly code: string;\n readonly requestId?: string;\n /** Parsed from `Retry-After`, in milliseconds. Only set on 429. */\n readonly retryAfterMs?: number;\n /** zod issues for `validation_failed`, or the raw body when unparseable. */\n readonly details?: unknown;\n\n constructor(init: {\n message: string;\n status: number;\n code: string;\n requestId?: string;\n retryAfterMs?: number;\n details?: unknown;\n }) {\n super(init.message);\n this.name = \"DataApiError\";\n this.status = init.status;\n this.code = init.code;\n this.requestId = init.requestId;\n this.retryAfterMs = init.retryAfterMs;\n this.details = init.details;\n }\n\n /** 429 and 5xx are worth retrying; 4xx are the caller's fault and are not. */\n get retryable(): boolean {\n return this.status === 429 || this.status >= 500;\n }\n\n /**\n * Prefer this over `instanceof` in a `catch`. It holds even when the class\n * has been duplicated — across CJS entry points, across bundler chunks, or\n * across realms — where `instanceof` quietly returns false.\n */\n static is(value: unknown): value is DataApiError {\n return (\n value instanceof DataApiError ||\n (typeof value === \"object\" && value !== null && (value as { isDataApiError?: unknown }).isDataApiError === true)\n );\n }\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/eric/reopt-ai/reopt-data/packages/data-contract/dist/chunk-XI3VP5DN.cjs","../src/ingest.ts"],"names":[],"mappings":"AAAA;ACWA,0BAAkB;AAGX,IAAM,qBAAA,EAAuB,CAAC,eAAA,EAAiB,aAAA,EAAe,aAAa,CAAA;AAG3E,IAAM,wBAAA,EAA0B,KAAA;AAEvC,IAAM,qBAAA,EAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO3B,OAAA,EAAS,MAAA,CAAE,IAAA,CAAK,CAAA;AAAA;AAAA,EAEhB,SAAA,EAAW,MAAA,CAAE,MAAA,CAAO,CAAA,CAAE,GAAA,CAAI,CAAA,CAAE,WAAA,CAAY;AAC1C,CAAA;AAEO,IAAM,cAAA,EAAgB,MAAA,CAC1B,MAAA,CAAO;AAAA,EACN,IAAA,EAAM,MAAA,CAAE,MAAA,CAAO,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,GAAA,CAAI,GAAG,CAAA;AAAA,EAC/B,UAAA,EAAY,MAAA,CAAE,MAAA,CAAO,MAAA,CAAE,MAAA,CAAO,CAAA,EAAG,MAAA,CAAE,OAAA,CAAQ,CAAC,CAAA,CAAE,QAAA,CAAS,CAAA;AAAA,EACvD,SAAA,EAAW,MAAA,CAAE,MAAA,CAAO,CAAA,CAAE,GAAA,CAAI,GAAG,CAAA,CAAE,EAAA,CAAG,MAAA,CAAE,MAAA,CAAO,CAAC,CAAA,CAAE,QAAA,CAAS;AACzD,CAAC,CAAA,CACA,MAAA,CAAO,CAAC,IAAA,EAAA,GAAS,CAAC,oBAAA,CAAqB,QAAA,CAAS,IAAA,CAAK,IAAa,CAAA,EAAG;AAAA,EACpE,OAAA,EAAS,CAAA,gDAAA,EAAmD,oBAAA,CAAqB,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAC9E,EAAA;AACd;AAEsC;AACY,EAAA;AACX,EAAA;AACD,EAAA;AACJ,EAAA;AACA,EAAA;AACoB,EAAA;AACxD;AAEyC;AACW,EAAA;AAChB,EAAA;AACG,EAAA;AACvC;AAEyC;AACW,EAAA;AAChB,EAAA;AACG,EAAA;AACvC;AAEgE;AACuB,EAAA;AACM,EAAA;AACpB,EAAA;AACA,EAAA;AACzE;AAqB+C;AACF;AAUN;AAAA;AAEtC,EAAA;AAAA;AAEA,EAAA;AAAA;AAEA,EAAA;AACF;AACqE;AAG5B;AAAA;AAErB,EAAA;AAAA;AAEkB,EAAA;AAC5B,EAAA;AACqB,EAAA;AAC9B;AAGoB;AAAA;AAEoB,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAME,EAAA;AAAA;AAEP,EAAA;AACpC;AAG0C;AAClB,EAAA;AACF,EAAA;AACd,EAAA;AACH,EAAA;AACe,EAAA;AACnB;AAQ+C;AAClB,EAAA;AACR,EAAA;AACd,EAAA;AACH,EAAA;AAC8B,EAAA;AACY,EAAA;AAC9C;AAEiF;AAcY;AACd,EAAA;AAChF;AAQkC;AAAA;AAEhC,EAAA;AAAA;AAEA,EAAA;AAAA;AAEA,EAAA;AAAA;AAEA,EAAA;AAAA;AAEA,EAAA;AAAA;AAEA,EAAA;AAAA;AAEA,EAAA;AAAA;AAEA,EAAA;AAAA;AAEA,EAAA;AAAA;AAEA,EAAA;AAAA;AAEA,EAAA;AACF;AACyD;AAGpB;AACZ,EAAA;AACjB,EAAA;AACU,EAAA;AACa,EAAA;AAAA;AAEA,EAAA;AACE,EAAA;AAChC;ADtF6F;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/Users/eric/reopt-ai/reopt-data/packages/data-contract/dist/chunk-XI3VP5DN.cjs","sourcesContent":[null,"/**\n * `POST /api/track` — the ingest contract.\n *\n * The request shape is shared by both credential modes. The *response* shape\n * is where server-ingest differs: it reports per-event rejections instead of\n * failing a whole batch, because a forwarder that stalls on one malformed row\n * stops forwarding forever.\n *\n * Absorbed from the former internal `@reopt/ingest-contract`; that package is\n * now a deprecated re-export of this module.\n */\nimport { z } from \"zod\";\n\n/** Event names the pipeline reserves for its own session bookkeeping. */\nexport const RESERVED_EVENT_NAMES = [\"session_start\", \"session_end\", \"screen_view\"] as const;\n\n/** Hard cap enforced by the route before the body is even parsed. */\nexport const MAX_TRACK_PAYLOAD_BYTES = 512_000;\n\nconst zClientEventMetadata = {\n /**\n * Client-generated UUID. This is the project-scoped idempotency key stored as\n * `RawEvent.clientEventId`; the server uses a separate globally unique raw\n * event id internally. Re-sending the same eventId to the same project is\n * always safe and always reported as a duplicate.\n */\n eventId: z.uuid(),\n /** Event time, epoch milliseconds. */\n timestamp: z.number().int().nonnegative(),\n};\n\nexport const zTrackPayload = z\n .object({\n name: z.string().min(1).max(200),\n properties: z.record(z.string(), z.unknown()).optional(),\n profileId: z.string().max(500).or(z.number()).optional(),\n })\n .refine((data) => !RESERVED_EVENT_NAMES.includes(data.name as never), {\n message: `Event name cannot be one of the reserved names: ${RESERVED_EVENT_NAMES.join(\", \")}`,\n path: [\"name\"],\n });\n\nexport const zIdentifyPayload = z.object({\n profileId: z.string().min(1).max(500).or(z.number()),\n firstName: z.string().max(200).optional(),\n lastName: z.string().max(200).optional(),\n email: z.email().max(320).optional(),\n avatar: z.url().max(2000).optional(),\n properties: z.record(z.string(), z.unknown()).optional(),\n});\n\nexport const zIncrementPayload = z.object({\n profileId: z.string().min(1).max(500).or(z.number()),\n property: z.string().min(1).max(200),\n value: z.number().positive().optional(),\n});\n\nexport const zDecrementPayload = z.object({\n profileId: z.string().min(1).max(500).or(z.number()),\n property: z.string().min(1).max(200),\n value: z.number().positive().optional(),\n});\n\nexport const zTrackHandlerPayload = z.discriminatedUnion(\"type\", [\n z.object({ type: z.literal(\"track\"), payload: zTrackPayload, ...zClientEventMetadata }),\n z.object({ type: z.literal(\"identify\"), payload: zIdentifyPayload, ...zClientEventMetadata }),\n z.object({ type: z.literal(\"increment\"), payload: zIncrementPayload, ...zClientEventMetadata }),\n z.object({ type: z.literal(\"decrement\"), payload: zDecrementPayload, ...zClientEventMetadata }),\n]);\n\nexport type ITrackPayload = z.infer<typeof zTrackPayload>;\nexport type IIdentifyPayload = z.infer<typeof zIdentifyPayload>;\nexport type IIncrementPayload = z.infer<typeof zIncrementPayload>;\nexport type IDecrementPayload = z.infer<typeof zDecrementPayload>;\nexport type ITrackHandlerPayload = z.infer<typeof zTrackHandlerPayload>;\n\n// ─── Ingest mode ────────────────────────────────────────────────────────────\n\n/**\n * Decided by the credential presented, never by a header the caller sets:\n * `reopt-write-key` alone → `browser`, `reopt-client-id` + `reopt-client-secret`\n * → `server`. A separate mode header could disagree with the credential; this\n * cannot.\n *\n * `browser` keeps the 5-second click dedup and device-scoped sessions.\n * `server` drops both and relies solely on `eventId` idempotency — a server\n * batch legitimately contains the same event name many times within one second,\n * and its \"device\" is a process, not a person.\n */\nexport const INGEST_MODES = [\"browser\", \"server\"] as const;\nexport const zIngestMode = z.enum(INGEST_MODES);\nexport type IngestMode = z.infer<typeof zIngestMode>;\n\n// ─── Response ───────────────────────────────────────────────────────────────\n\n/**\n * Why a single event in a batch was dropped. These are permanent: re-sending\n * the same row produces the same rejection, so a forwarder should count it as\n * skipped and advance its cursor rather than retry.\n */\nexport const INGEST_REJECTION_REASONS = [\n /** Failed the zod schema (bad eventId, missing name, wrong types…). */\n \"validation_failed\",\n /** Used one of RESERVED_EVENT_NAMES. */\n \"reserved_name\",\n /** Another row earlier in the same batch already claimed this eventId. */\n \"duplicate_in_batch\",\n] as const;\nexport const zIngestRejectionReason = z.enum(INGEST_REJECTION_REASONS);\nexport type IngestRejectionReason = z.infer<typeof zIngestRejectionReason>;\n\nexport const zIngestRejection = z.object({\n /** May be absent or malformed on `validation_failed` — hence `string`, not `uuid`. */\n eventId: z.string(),\n /** Position in the submitted batch. The only reliable identifier when eventId itself is bad. */\n index: z.number().int().nonnegative(),\n reason: zIngestRejectionReason,\n message: z.string().optional(),\n});\nexport type IngestRejection = z.infer<typeof zIngestRejection>;\n\nconst ingestCounts = {\n /** Raw events newly persisted by this request. */\n accepted: z.number().int().nonnegative(),\n /**\n * Events the server already had. In `server` mode this is exactly the\n * eventId-idempotency count; in `browser` mode it also includes the\n * 5-second click dedup.\n */\n duplicates: z.number().int().nonnegative(),\n /** Always `[]` in `browser` mode, where a bad row fails the whole batch. */\n rejected: z.array(zIngestRejection),\n};\n\n/** 200 — accepted and handed to the materialize queue. */\nexport const zIngestOkResponse = z.object({\n status: z.literal(\"ok\"),\n requestId: z.string(),\n mode: zIngestMode,\n ...ingestCounts,\n queued: z.boolean(),\n});\n\n/**\n * 202 — raw events landed, but enqueueing the materialize task failed. No data\n * is lost: the `replay-unmaterialized` cron picks these up. A forwarder should\n * treat this as success and advance its cursor. Carries the same counts as 200\n * so the reconciliation below works here too.\n */\nexport const zIngestAcceptedResponse = z.object({\n status: z.literal(\"accepted\"),\n requestId: z.string(),\n mode: zIngestMode,\n ...ingestCounts,\n backgroundQueued: z.literal(false),\n replay: z.literal(\"unmaterialized-raw-events\"),\n});\n\nexport const zIngestResponse = z.discriminatedUnion(\"status\", [zIngestOkResponse, zIngestAcceptedResponse]);\n\nexport type IngestOkResponse = z.infer<typeof zIngestOkResponse>;\nexport type IngestAcceptedResponse = z.infer<typeof zIngestAcceptedResponse>;\nexport type IngestResponse = z.infer<typeof zIngestResponse>;\n\n/**\n * The reconciliation a forwarder runs on every 2xx:\n *\n * accepted + duplicates + rejected.length === events sent\n *\n * A mismatch means the server silently dropped something, which is exactly the\n * failure mode this contract exists to make impossible to miss.\n */\nexport function reconcileIngestResponse(response: IngestResponse, sentCount: number): boolean {\n return response.accepted + response.duplicates + response.rejected.length === sentCount;\n}\n\n// ─── Errors ─────────────────────────────────────────────────────────────────\n\n/**\n * Stable machine-readable codes. Branch on these, never on `error`/`message`,\n * which are prose and may be reworded.\n */\nexport const INGEST_ERROR_CODES = [\n /** 401 — unknown or mismatched credentials. Stop and alert; do not retry. */\n \"unauthorized\",\n /** 400 — body was not valid JSON. */\n \"invalid_json\",\n /** 400 — request exceeded MAX_TRACK_PAYLOAD_BYTES. Split the batch. */\n \"payload_too_large\",\n /** 400 — zero events submitted. */\n \"empty_batch\",\n /** 400 — batch contained a `type: \"alias\"` event, which is not supported. */\n \"alias_unsupported\",\n /** 400 — the project has no organization; it cannot be billed or quota-checked. */\n \"project_without_organization\",\n /** 400 — `browser` mode only: a row failed validation. `server` mode reports these in `rejected[]`. */\n \"validation_failed\",\n /** 429 — per-project request rate limit. `Retry-After` set. Pause this tick, keep the cursor. */\n \"rate_limited\",\n /** 429 — the organization's monthly event quota is exhausted. `Retry-After` set (capped at 1h). Alert: a human must raise the limit. */\n \"quota_exceeded\",\n /** 410 — the project is being deleted. Permanent; stop sending. */\n \"project_purging\",\n /** 500 — unexpected server failure. Back off and retry. */\n \"internal_error\",\n] as const;\nexport const zIngestErrorCode = z.enum(INGEST_ERROR_CODES);\nexport type IngestErrorCode = z.infer<typeof zIngestErrorCode>;\n\nexport const zIngestError = z.object({\n status: z.number().int(),\n code: zIngestErrorCode,\n error: z.string(),\n message: z.string().optional(),\n /** zod issues, when `code === \"validation_failed\"`. */\n errors: z.unknown().optional(),\n requestId: z.string().optional(),\n});\nexport type IngestError = z.infer<typeof zIngestError>;\n"]}
|
|
File without changes
|
|
File without changes
|