@reopt-ai/data-contract 0.6.3 → 0.7.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 (103) hide show
  1. package/dist/client.cjs +228 -321
  2. package/dist/client.cjs.map +1 -1
  3. package/dist/client.d.cts +57 -71
  4. package/dist/client.d.cts.map +1 -0
  5. package/dist/client.d.ts +57 -71
  6. package/dist/client.d.ts.map +1 -0
  7. package/dist/client.js +228 -322
  8. package/dist/client.js.map +1 -1
  9. package/dist/control.cjs +158 -54
  10. package/dist/control.cjs.map +1 -1
  11. package/dist/control.d.cts +121 -137
  12. package/dist/control.d.cts.map +1 -0
  13. package/dist/control.d.ts +121 -137
  14. package/dist/control.d.ts.map +1 -0
  15. package/dist/control.js +134 -54
  16. package/dist/control.js.map +1 -1
  17. package/dist/events-BUIeU60t.d.cts +142 -0
  18. package/dist/events-BUIeU60t.d.cts.map +1 -0
  19. package/dist/events-BUIeU60t.d.ts +142 -0
  20. package/dist/events-BUIeU60t.d.ts.map +1 -0
  21. package/dist/events.cjs +110 -15
  22. package/dist/events.cjs.map +1 -1
  23. package/dist/events.d.cts +2 -59
  24. package/dist/events.d.ts +2 -59
  25. package/dist/events.js +101 -15
  26. package/dist/events.js.map +1 -1
  27. package/dist/identity.cjs +150 -55
  28. package/dist/identity.cjs.map +1 -1
  29. package/dist/identity.d.cts +21 -20
  30. package/dist/identity.d.cts.map +1 -0
  31. package/dist/identity.d.ts +21 -20
  32. package/dist/identity.d.ts.map +1 -0
  33. package/dist/identity.js +126 -56
  34. package/dist/identity.js.map +1 -1
  35. package/dist/index.cjs +46 -25
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +37 -54
  38. package/dist/index.d.cts.map +1 -0
  39. package/dist/index.d.ts +37 -54
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +36 -25
  42. package/dist/index.js.map +1 -1
  43. package/dist/ingest.cjs +152 -57
  44. package/dist/ingest.cjs.map +1 -1
  45. package/dist/ingest.d.cts +182 -193
  46. package/dist/ingest.d.cts.map +1 -0
  47. package/dist/ingest.d.ts +182 -193
  48. package/dist/ingest.d.ts.map +1 -0
  49. package/dist/ingest.js +126 -57
  50. package/dist/ingest.js.map +1 -1
  51. package/dist/query.cjs +284 -84
  52. package/dist/query.cjs.map +1 -1
  53. package/dist/query.d.cts +456 -469
  54. package/dist/query.d.cts.map +1 -0
  55. package/dist/query.d.ts +456 -469
  56. package/dist/query.d.ts.map +1 -0
  57. package/dist/query.js +245 -84
  58. package/dist/query.js.map +1 -1
  59. package/dist/report.cjs +104 -74
  60. package/dist/report.cjs.map +1 -1
  61. package/dist/report.d.cts +182 -136
  62. package/dist/report.d.cts.map +1 -0
  63. package/dist/report.d.ts +182 -136
  64. package/dist/report.d.ts.map +1 -0
  65. package/dist/report.js +93 -74
  66. package/dist/report.js.map +1 -1
  67. package/dist/version-B_0qb1_K.cjs +53 -0
  68. package/dist/version-B_0qb1_K.cjs.map +1 -0
  69. package/dist/version-C09Cyf4R.js +12 -0
  70. package/dist/version-C09Cyf4R.js.map +1 -0
  71. package/dist/{version-DJ2Grb_N.d.ts → version-CGujUGWd.d.cts} +5 -3
  72. package/dist/version-CGujUGWd.d.cts.map +1 -0
  73. package/dist/{version-DJ2Grb_N.d.cts → version-CGujUGWd.d.ts} +5 -3
  74. package/dist/version-CGujUGWd.d.ts.map +1 -0
  75. package/package.json +69 -28
  76. package/dist/chunk-3EEANNYN.cjs +0 -62
  77. package/dist/chunk-3EEANNYN.cjs.map +0 -1
  78. package/dist/chunk-7W33YGRO.js +0 -160
  79. package/dist/chunk-7W33YGRO.js.map +0 -1
  80. package/dist/chunk-F2RNXJGO.cjs +0 -19
  81. package/dist/chunk-F2RNXJGO.cjs.map +0 -1
  82. package/dist/chunk-FP2X3PRK.js +0 -62
  83. package/dist/chunk-FP2X3PRK.js.map +0 -1
  84. package/dist/chunk-JCEWUSUM.js +0 -53
  85. package/dist/chunk-JCEWUSUM.js.map +0 -1
  86. package/dist/chunk-L3JPDRVG.cjs +0 -160
  87. package/dist/chunk-L3JPDRVG.cjs.map +0 -1
  88. package/dist/chunk-L4KBQAOO.js +0 -204
  89. package/dist/chunk-L4KBQAOO.js.map +0 -1
  90. package/dist/chunk-NJ4JKNBX.js +0 -361
  91. package/dist/chunk-NJ4JKNBX.js.map +0 -1
  92. package/dist/chunk-ORNTN6AK.cjs +0 -53
  93. package/dist/chunk-ORNTN6AK.cjs.map +0 -1
  94. package/dist/chunk-PSUINLW7.js +0 -19
  95. package/dist/chunk-PSUINLW7.js.map +0 -1
  96. package/dist/chunk-RU26QFM2.js +0 -179
  97. package/dist/chunk-RU26QFM2.js.map +0 -1
  98. package/dist/chunk-XDH5QQYF.cjs +0 -361
  99. package/dist/chunk-XDH5QQYF.cjs.map +0 -1
  100. package/dist/chunk-YCTGHWTP.cjs +0 -204
  101. package/dist/chunk-YCTGHWTP.cjs.map +0 -1
  102. package/dist/chunk-ZI6A3WQP.cjs +0 -179
  103. package/dist/chunk-ZI6A3WQP.cjs.map +0 -1
package/dist/ingest.cjs CHANGED
@@ -1,59 +1,154 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_identity = require("./identity.cjs");
3
+ const require_events = require("./events.cjs");
4
+ let zod = require("zod");
5
+ //#region src/ingest.ts
6
+ const MAX_TRACK_PAYLOAD_BYTES = 512e3;
7
+ const zClientEventMetadata = {
8
+ eventId: zod.z.uuid(),
9
+ timestamp: zod.z.number().int().nonnegative(),
10
+ deviceId: zod.z.string().regex(require_identity.IDENTITY_ID_PATTERN, "expected an identity id").optional()
11
+ };
12
+ const zTrackPayload = zod.z.object({
13
+ name: zod.z.string().min(1).max(200),
14
+ properties: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
15
+ profileId: zod.z.string().max(500).or(zod.z.number()).optional()
16
+ }).refine((data) => !require_events.RESERVED_EVENT_NAMES.includes(data.name), {
17
+ message: `Event name cannot be one of the reserved names: ${require_events.RESERVED_EVENT_NAMES.join(", ")}`,
18
+ path: ["name"]
19
+ });
20
+ const zIdentifyPayload = zod.z.object({
21
+ profileId: zod.z.string().min(1).max(500).or(zod.z.number()),
22
+ firstName: zod.z.string().max(200).optional(),
23
+ lastName: zod.z.string().max(200).optional(),
24
+ email: zod.z.email().max(320).optional(),
25
+ avatar: zod.z.url().max(2e3).optional(),
26
+ properties: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
27
+ });
28
+ const zIncrementPayload = zod.z.object({
29
+ profileId: zod.z.string().min(1).max(500).or(zod.z.number()),
30
+ property: zod.z.string().min(1).max(200),
31
+ value: zod.z.number().positive().optional()
32
+ });
33
+ const zDecrementPayload = zod.z.object({
34
+ profileId: zod.z.string().min(1).max(500).or(zod.z.number()),
35
+ property: zod.z.string().min(1).max(200),
36
+ value: zod.z.number().positive().optional()
37
+ });
38
+ const zTrackHandlerPayload = zod.z.discriminatedUnion("type", [
39
+ zod.z.object({
40
+ type: zod.z.literal("track"),
41
+ payload: zTrackPayload,
42
+ ...zClientEventMetadata
43
+ }),
44
+ zod.z.object({
45
+ type: zod.z.literal("identify"),
46
+ payload: zIdentifyPayload,
47
+ ...zClientEventMetadata
48
+ }),
49
+ zod.z.object({
50
+ type: zod.z.literal("increment"),
51
+ payload: zIncrementPayload,
52
+ ...zClientEventMetadata
53
+ }),
54
+ zod.z.object({
55
+ type: zod.z.literal("decrement"),
56
+ payload: zDecrementPayload,
57
+ ...zClientEventMetadata
58
+ })
59
+ ]);
60
+ const INGEST_MODES = ["browser", "server"];
61
+ const zIngestMode = zod.z.enum(INGEST_MODES);
62
+ const INGEST_REJECTION_REASONS = [
63
+ "validation_failed",
64
+ "reserved_name",
65
+ "duplicate_in_batch"
66
+ ];
67
+ const zIngestRejectionReason = zod.z.enum(INGEST_REJECTION_REASONS);
68
+ const zIngestRejection = zod.z.object({
69
+ eventId: zod.z.string(),
70
+ index: zod.z.number().int().nonnegative(),
71
+ reason: zIngestRejectionReason,
72
+ message: zod.z.string().optional()
73
+ });
74
+ const ingestCounts = {
75
+ accepted: zod.z.number().int().nonnegative(),
76
+ duplicates: zod.z.number().int().nonnegative(),
77
+ rejected: zod.z.array(zIngestRejection)
78
+ };
79
+ const zSessionCredential = zod.z.object({
80
+ id: zod.z.string().regex(require_identity.IDENTITY_ID_PATTERN),
81
+ token: zod.z.string().regex(/^[A-Za-z0-9_-]{43}$/)
82
+ });
83
+ const zIngestOkResponse = zod.z.object({
84
+ status: zod.z.literal("ok"),
85
+ requestId: zod.z.string(),
86
+ mode: zIngestMode,
87
+ ...ingestCounts,
88
+ queued: zod.z.boolean(),
89
+ session: zSessionCredential.optional()
90
+ });
91
+ const zIngestAcceptedResponse = zod.z.object({
92
+ status: zod.z.literal("accepted"),
93
+ requestId: zod.z.string(),
94
+ mode: zIngestMode,
95
+ ...ingestCounts,
96
+ backgroundQueued: zod.z.literal(false),
97
+ replay: zod.z.literal("unmaterialized-raw-events"),
98
+ session: zSessionCredential.optional()
99
+ });
100
+ const zIngestResponse = zod.z.discriminatedUnion("status", [zIngestOkResponse, zIngestAcceptedResponse]);
101
+ function reconcileIngestResponse(response, sentCount) {
102
+ return response.accepted + response.duplicates + response.rejected.length === sentCount;
103
+ }
104
+ const INGEST_ERROR_CODES = [
105
+ "unauthorized",
106
+ "invalid_json",
107
+ "payload_too_large",
108
+ "empty_batch",
109
+ "alias_unsupported",
110
+ "project_without_organization",
111
+ "validation_failed",
112
+ "rate_limited",
113
+ "quota_exceeded",
114
+ "project_purging",
115
+ "internal_error"
116
+ ];
117
+ const zIngestErrorCode = zod.z.enum(INGEST_ERROR_CODES);
118
+ const zIngestError = zod.z.object({
119
+ status: zod.z.number().int(),
120
+ code: zIngestErrorCode,
121
+ error: zod.z.string(),
122
+ message: zod.z.string().optional(),
123
+ errors: zod.z.unknown().optional(),
124
+ requestId: zod.z.string().optional()
125
+ });
126
+ //#endregion
127
+ exports.AUTO_EVENT_NAMES = require_events.AUTO_EVENT_NAMES;
128
+ exports.AUTO_EVENT_PROPERTIES = require_events.AUTO_EVENT_PROPERTIES;
129
+ exports.AUTO_EVENT_ROLLUP_KEYS = require_events.AUTO_EVENT_ROLLUP_KEYS;
130
+ exports.INGEST_ERROR_CODES = INGEST_ERROR_CODES;
131
+ exports.INGEST_MODES = INGEST_MODES;
132
+ exports.INGEST_REJECTION_REASONS = INGEST_REJECTION_REASONS;
133
+ exports.MAX_TRACK_PAYLOAD_BYTES = MAX_TRACK_PAYLOAD_BYTES;
134
+ exports.RELEASE_PROPERTY = require_events.RELEASE_PROPERTY;
135
+ exports.RESERVED_EVENT_NAMES = require_events.RESERVED_EVENT_NAMES;
136
+ exports.SCROLL_DEPTH_BUCKETS = require_events.SCROLL_DEPTH_BUCKETS;
137
+ exports.reconcileIngestResponse = reconcileIngestResponse;
138
+ exports.scrollDepthBucket = require_events.scrollDepthBucket;
139
+ exports.zDecrementPayload = zDecrementPayload;
140
+ exports.zIdentifyPayload = zIdentifyPayload;
141
+ exports.zIncrementPayload = zIncrementPayload;
142
+ exports.zIngestAcceptedResponse = zIngestAcceptedResponse;
143
+ exports.zIngestError = zIngestError;
144
+ exports.zIngestErrorCode = zIngestErrorCode;
145
+ exports.zIngestMode = zIngestMode;
146
+ exports.zIngestOkResponse = zIngestOkResponse;
147
+ exports.zIngestRejection = zIngestRejection;
148
+ exports.zIngestRejectionReason = zIngestRejectionReason;
149
+ exports.zIngestResponse = zIngestResponse;
150
+ exports.zSessionCredential = zSessionCredential;
151
+ exports.zTrackHandlerPayload = zTrackHandlerPayload;
152
+ exports.zTrackPayload = zTrackPayload;
2
153
 
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
- var _chunkZI6A3WQPcjs = require('./chunk-ZI6A3WQP.cjs');
22
- require('./chunk-L3JPDRVG.cjs');
23
- require('./chunk-F2RNXJGO.cjs');
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
- var _chunk3EEANNYNcjs = require('./chunk-3EEANNYN.cjs');
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
- exports.AUTO_EVENT_NAMES = _chunk3EEANNYNcjs.AUTO_EVENT_NAMES; exports.AUTO_EVENT_PROPERTIES = _chunk3EEANNYNcjs.AUTO_EVENT_PROPERTIES; exports.AUTO_EVENT_ROLLUP_KEYS = _chunk3EEANNYNcjs.AUTO_EVENT_ROLLUP_KEYS; exports.INGEST_ERROR_CODES = _chunkZI6A3WQPcjs.INGEST_ERROR_CODES; exports.INGEST_MODES = _chunkZI6A3WQPcjs.INGEST_MODES; exports.INGEST_REJECTION_REASONS = _chunkZI6A3WQPcjs.INGEST_REJECTION_REASONS; exports.MAX_TRACK_PAYLOAD_BYTES = _chunkZI6A3WQPcjs.MAX_TRACK_PAYLOAD_BYTES; exports.RESERVED_EVENT_NAMES = _chunk3EEANNYNcjs.RESERVED_EVENT_NAMES; exports.SCROLL_DEPTH_BUCKETS = _chunk3EEANNYNcjs.SCROLL_DEPTH_BUCKETS; exports.reconcileIngestResponse = _chunkZI6A3WQPcjs.reconcileIngestResponse; exports.scrollDepthBucket = _chunk3EEANNYNcjs.scrollDepthBucket; exports.zDecrementPayload = _chunkZI6A3WQPcjs.zDecrementPayload; exports.zIdentifyPayload = _chunkZI6A3WQPcjs.zIdentifyPayload; exports.zIncrementPayload = _chunkZI6A3WQPcjs.zIncrementPayload; exports.zIngestAcceptedResponse = _chunkZI6A3WQPcjs.zIngestAcceptedResponse; exports.zIngestError = _chunkZI6A3WQPcjs.zIngestError; exports.zIngestErrorCode = _chunkZI6A3WQPcjs.zIngestErrorCode; exports.zIngestMode = _chunkZI6A3WQPcjs.zIngestMode; exports.zIngestOkResponse = _chunkZI6A3WQPcjs.zIngestOkResponse; exports.zIngestRejection = _chunkZI6A3WQPcjs.zIngestRejection; exports.zIngestRejectionReason = _chunkZI6A3WQPcjs.zIngestRejectionReason; exports.zIngestResponse = _chunkZI6A3WQPcjs.zIngestResponse; exports.zSessionCredential = _chunkZI6A3WQPcjs.zSessionCredential; exports.zTrackHandlerPayload = _chunkZI6A3WQPcjs.zTrackHandlerPayload; exports.zTrackPayload = _chunkZI6A3WQPcjs.zTrackPayload;
59
154
  //# sourceMappingURL=ingest.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/eric/reopt-ai/reopt-data/packages/data-contract/dist/ingest.cjs"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B,gCAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,woDAAC","file":"/Users/eric/reopt-ai/reopt-data/packages/data-contract/dist/ingest.cjs"}
1
+ {"version":3,"file":"ingest.cjs","names":["z","IDENTITY_ID_PATTERN","RESERVED_EVENT_NAMES"],"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`; all consumers\n * now import this module directly and the transitional package was removed.\n */\nimport { z } from \"zod\";\nimport { IDENTITY_ID_PATTERN } from \"./identity.js\";\n\nexport {\n AUTO_EVENT_NAMES,\n AUTO_EVENT_PROPERTIES,\n AUTO_EVENT_ROLLUP_KEYS,\n RELEASE_PROPERTY,\n RESERVED_EVENT_NAMES,\n SCROLL_DEPTH_BUCKETS,\n scrollDepthBucket,\n type AutoEventName,\n type ScrollDepthBucket,\n} from \"./events.js\";\nimport { RESERVED_EVENT_NAMES } from \"./events.js\";\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 * The browser device this event belongs to, for events a server records\n * on a visitor's behalf (a form submission, an order). Lets one server\n * batch carry events from many visitors — a forwarder cannot split its\n * batches per device without breaking its failure model and its\n * `accepted + duplicates + rejected === sent` invariant.\n *\n * Honoured in `server` mode only, where it takes precedence over the\n * `reopt-device-id` request header. Ignored in `browser` mode: there the\n * header is authoritative and a body field would be trivially forged.\n * Same character set as every identity id.\n */\n deviceId: z.string().regex(IDENTITY_ID_PATTERN, \"expected an identity id\").optional(),\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/**\n * The session ingest filed this batch under, with the token that lets the\n * browser (or a server acting for it) join the same session later with\n * authority — see `signSessionToken` in `./identity`. Browser mode only;\n * absent when the batch carried no sessionable event.\n */\nexport const zSessionCredential = z.object({\n id: z.string().regex(IDENTITY_ID_PATTERN),\n token: z.string().regex(/^[A-Za-z0-9_-]{43}$/),\n});\nexport type SessionCredential = z.infer<typeof zSessionCredential>;\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 session: zSessionCredential.optional(),\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 session: zSessionCredential.optional(),\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":";;;;;AA4BA,MAAa,0BAA0B;AAEvC,MAAM,uBAAuB;CAO3B,SAASA,IAAAA,EAAE,KAAK;CAEhB,WAAWA,IAAAA,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY;CAaxC,UAAUA,IAAAA,EAAE,OAAO,CAAC,CAAC,MAAMC,iBAAAA,qBAAqB,yBAAyB,CAAC,CAAC,SAAS;AACtF;AAEA,MAAa,gBAAgBD,IAAAA,EAC1B,OAAO;CACN,MAAMA,IAAAA,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG;CAC/B,YAAYA,IAAAA,EAAE,OAAOA,IAAAA,EAAE,OAAO,GAAGA,IAAAA,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS;CACvD,WAAWA,IAAAA,EAAE,OAAO,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAGA,IAAAA,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS;AACzD,CAAC,CAAC,CACD,QAAQ,SAAS,CAACE,eAAAA,qBAAqB,SAAS,KAAK,IAAa,GAAG;CACpE,SAAS,mDAAmDA,eAAAA,qBAAqB,KAAK,IAAI;CAC1F,MAAM,CAAC,MAAM;AACf,CAAC;AAEH,MAAa,mBAAmBF,IAAAA,EAAE,OAAO;CACvC,WAAWA,IAAAA,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAGA,IAAAA,EAAE,OAAO,CAAC;CACnD,WAAWA,IAAAA,EAAE,OAAO,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,SAAS;CACxC,UAAUA,IAAAA,EAAE,OAAO,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,SAAS;CACvC,OAAOA,IAAAA,EAAE,MAAM,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,SAAS;CACnC,QAAQA,IAAAA,EAAE,IAAI,CAAC,CAAC,IAAI,GAAI,CAAC,CAAC,SAAS;CACnC,YAAYA,IAAAA,EAAE,OAAOA,IAAAA,EAAE,OAAO,GAAGA,IAAAA,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS;AACzD,CAAC;AAED,MAAa,oBAAoBA,IAAAA,EAAE,OAAO;CACxC,WAAWA,IAAAA,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAGA,IAAAA,EAAE,OAAO,CAAC;CACnD,UAAUA,IAAAA,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG;CACnC,OAAOA,IAAAA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;AACxC,CAAC;AAED,MAAa,oBAAoBA,IAAAA,EAAE,OAAO;CACxC,WAAWA,IAAAA,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAGA,IAAAA,EAAE,OAAO,CAAC;CACnD,UAAUA,IAAAA,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG;CACnC,OAAOA,IAAAA,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;AACxC,CAAC;AAED,MAAa,uBAAuBA,IAAAA,EAAE,mBAAmB,QAAQ;CAC/DA,IAAAA,EAAE,OAAO;EAAE,MAAMA,IAAAA,EAAE,QAAQ,OAAO;EAAG,SAAS;EAAe,GAAG;CAAqB,CAAC;CACtFA,IAAAA,EAAE,OAAO;EAAE,MAAMA,IAAAA,EAAE,QAAQ,UAAU;EAAG,SAAS;EAAkB,GAAG;CAAqB,CAAC;CAC5FA,IAAAA,EAAE,OAAO;EAAE,MAAMA,IAAAA,EAAE,QAAQ,WAAW;EAAG,SAAS;EAAmB,GAAG;CAAqB,CAAC;CAC9FA,IAAAA,EAAE,OAAO;EAAE,MAAMA,IAAAA,EAAE,QAAQ,WAAW;EAAG,SAAS;EAAmB,GAAG;CAAqB,CAAC;AAChG,CAAC;AAqBD,MAAa,eAAe,CAAC,WAAW,QAAQ;AAChD,MAAa,cAAcA,IAAAA,EAAE,KAAK,YAAY;AAU9C,MAAa,2BAA2B;CAEtC;CAEA;CAEA;AACF;AACA,MAAa,yBAAyBA,IAAAA,EAAE,KAAK,wBAAwB;AAGrE,MAAa,mBAAmBA,IAAAA,EAAE,OAAO;CAEvC,SAASA,IAAAA,EAAE,OAAO;CAElB,OAAOA,IAAAA,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY;CACpC,QAAQ;CACR,SAASA,IAAAA,EAAE,OAAO,CAAC,CAAC,SAAS;AAC/B,CAAC;AAGD,MAAM,eAAe;CAEnB,UAAUA,IAAAA,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY;CAMvC,YAAYA,IAAAA,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY;CAEzC,UAAUA,IAAAA,EAAE,MAAM,gBAAgB;AACpC;AAQA,MAAa,qBAAqBA,IAAAA,EAAE,OAAO;CACzC,IAAIA,IAAAA,EAAE,OAAO,CAAC,CAAC,MAAMC,iBAAAA,mBAAmB;CACxC,OAAOD,IAAAA,EAAE,OAAO,CAAC,CAAC,MAAM,qBAAqB;AAC/C,CAAC;AAID,MAAa,oBAAoBA,IAAAA,EAAE,OAAO;CACxC,QAAQA,IAAAA,EAAE,QAAQ,IAAI;CACtB,WAAWA,IAAAA,EAAE,OAAO;CACpB,MAAM;CACN,GAAG;CACH,QAAQA,IAAAA,EAAE,QAAQ;CAClB,SAAS,mBAAmB,SAAS;AACvC,CAAC;AAQD,MAAa,0BAA0BA,IAAAA,EAAE,OAAO;CAC9C,QAAQA,IAAAA,EAAE,QAAQ,UAAU;CAC5B,WAAWA,IAAAA,EAAE,OAAO;CACpB,MAAM;CACN,GAAG;CACH,kBAAkBA,IAAAA,EAAE,QAAQ,KAAK;CACjC,QAAQA,IAAAA,EAAE,QAAQ,2BAA2B;CAC7C,SAAS,mBAAmB,SAAS;AACvC,CAAC;AAED,MAAa,kBAAkBA,IAAAA,EAAE,mBAAmB,UAAU,CAAC,mBAAmB,uBAAuB,CAAC;AAc1G,SAAgB,wBAAwB,UAA0B,WAA4B;CAC5F,OAAO,SAAS,WAAW,SAAS,aAAa,SAAS,SAAS,WAAW;AAChF;AAQA,MAAa,qBAAqB;CAEhC;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;AACF;AACA,MAAa,mBAAmBA,IAAAA,EAAE,KAAK,kBAAkB;AAGzD,MAAa,eAAeA,IAAAA,EAAE,OAAO;CACnC,QAAQA,IAAAA,EAAE,OAAO,CAAC,CAAC,IAAI;CACvB,MAAM;CACN,OAAOA,IAAAA,EAAE,OAAO;CAChB,SAASA,IAAAA,EAAE,OAAO,CAAC,CAAC,SAAS;CAE7B,QAAQA,IAAAA,EAAE,QAAQ,CAAC,CAAC,SAAS;CAC7B,WAAWA,IAAAA,EAAE,OAAO,CAAC,CAAC,SAAS;AACjC,CAAC"}