@evinvest/sites-contract 0.1.4 → 0.1.5
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 +4 -3
- package/dist/experiments.js +3 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/message-ref.d.ts +7 -0
- package/dist/message-ref.js +22 -0
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
The Service-Arb sites ↔ panel contract, in TS: what a landing reads from the panel and sends
|
|
4
4
|
to it. Its source is the panel — `crates/panel_core` and `contracts/proto/sa/v1` in
|
|
5
5
|
[EV-Service-Arb/sites_panel](https://github.com/EV-Service-Arb/sites_panel) — and this is its
|
|
6
|
-
only TS form.
|
|
6
|
+
only TS form. Its one dependency is `@evinvest/experiments` (zero-dep), no Node imports: it is bundled into landing pages and
|
|
7
7
|
the panel's front end.
|
|
8
8
|
|
|
9
9
|
```text
|
|
@@ -15,9 +15,10 @@ src/*.ts the one TS implementation of what both sides run, hel
|
|
|
15
15
|
price priceOf: what a page runs pricing checkPricingModel
|
|
16
16
|
booking bookingRequested, isLeadRef booking-config checkBookingConfig, checkBookingUrl
|
|
17
17
|
place checkPlaceLive: a place's live settings, field by field
|
|
18
|
-
phone normalizePhone, phoneProblem
|
|
18
|
+
phone normalizePhone, phoneProblem message-ref messageRefOf: a ref as typed
|
|
19
19
|
ingest siteEvent, ingestBody, uuidV7, isOpaqueId: the only way a site builds an event
|
|
20
|
-
experiments parseLiveExperiments, declarationProblem
|
|
20
|
+
experiments parseLiveExperiments, declarationProblem; validWeights, validHoldout are
|
|
21
|
+
@evinvest/experiments' (what applyOverrides takes), re-exported
|
|
21
22
|
```
|
|
22
23
|
|
|
23
24
|
A page bundles what it runs and not the panel's checks: they sit in modules of their own.
|
package/dist/experiments.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { validHoldout, validWeights } from "@evinvest/experiments";
|
|
1
2
|
import { EXPERIMENT_LIMITS } from "./generated/experiment-limits.js";
|
|
2
3
|
import { charCount, isObject, trim } from "./text.js";
|
|
3
4
|
/**
|
|
@@ -45,10 +46,9 @@ export function declarationProblem(d) {
|
|
|
45
46
|
return "variant";
|
|
46
47
|
if (new Set(d.variants).size !== d.variants.length)
|
|
47
48
|
return "duplicate_variant";
|
|
48
|
-
|
|
49
|
-
if (!weightsOk)
|
|
49
|
+
if (validWeights(d.weights, d.variants.length) === undefined)
|
|
50
50
|
return "weights";
|
|
51
|
-
if (d.holdout !== undefined &&
|
|
51
|
+
if (d.holdout !== undefined && validHoldout(d.holdout) === undefined)
|
|
52
52
|
return "holdout";
|
|
53
53
|
if (d.summary !== undefined && charCount(trim(d.summary)) > EXPERIMENT_LIMITS.maxSummary)
|
|
54
54
|
return "summary";
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,9 @@ export * from "./generated/index.js";
|
|
|
2
2
|
export { bookingRequested, isBookingProvider, isLeadRef, isPageProvider, preferredDateInWindow, type BookingRequested } from "./booking.js";
|
|
3
3
|
export { checkBookingConfig, checkBookingUrl, type BookingProblem } from "./booking-config.js";
|
|
4
4
|
export { declarationProblem, parseLiveExperiments, type DeclarationProblem } from "./experiments.js";
|
|
5
|
+
export { validHoldout, validWeights } from "@evinvest/experiments";
|
|
5
6
|
export { ingestBody, isOpaqueId, siteEvent, uuidV7, type LeadCreated, type SiteEvent, type SiteEventHead, type SiteProperties } from "./ingest.js";
|
|
7
|
+
export { messageRefOf } from "./message-ref.js";
|
|
6
8
|
export { normalizePhone, phoneProblem } from "./phone.js";
|
|
7
9
|
export { checkPlaceLive, isInstant, type PlaceProblem } from "./place.js";
|
|
8
10
|
export { mulBp, priceOf, roundTo, type Price, type PriceLine, type PricingInputs, type PricingLabels } from "./price.js";
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,9 @@ export * from "./generated/index.js";
|
|
|
2
2
|
export { bookingRequested, isBookingProvider, isLeadRef, isPageProvider, preferredDateInWindow } from "./booking.js";
|
|
3
3
|
export { checkBookingConfig, checkBookingUrl } from "./booking-config.js";
|
|
4
4
|
export { declarationProblem, parseLiveExperiments } from "./experiments.js";
|
|
5
|
+
export { validHoldout, validWeights } from "@evinvest/experiments";
|
|
5
6
|
export { ingestBody, isOpaqueId, siteEvent, uuidV7 } from "./ingest.js";
|
|
7
|
+
export { messageRefOf } from "./message-ref.js";
|
|
6
8
|
export { normalizePhone, phoneProblem } from "./phone.js";
|
|
7
9
|
export { checkPlaceLive, isInstant } from "./place.js";
|
|
8
10
|
export { mulBp, priceOf, roundTo } from "./price.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A ref as an operator types or pastes it ("aq-7k3f ", "Réf. AQ-7K3F", "ref aq 7k3f", "AQ 7K3F")
|
|
3
|
+
* in the form the landing made it, `AQ-7K3F`, or `null` when it cannot be one
|
|
4
|
+
* (`contracts/vectors/message-ref.json`). In the code Crockford's look-alikes are read as it
|
|
5
|
+
* reads them: O is 0, I and L are 1.
|
|
6
|
+
*/
|
|
7
|
+
export declare function messageRefOf(raw: string | null): string | null;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { LEAD_LIMITS } from "./generated/lead-limits.js";
|
|
2
|
+
const MESSAGE_REF = /* @__PURE__ */ new RegExp(LEAD_LIMITS.messageRef);
|
|
3
|
+
/** A label pasted with the ref ("Réf. ", "ref ", "Ref: "): the prefilled WhatsApp line ends "Réf. AQ-7K3F". */
|
|
4
|
+
const REF_LABEL = /^R[EÉ]F(?:[EÉ]RENCE)?(?:[.:#]\s*|\s+)/;
|
|
5
|
+
/** The prefix and the code, apart: a dash, spaces, or both between them. */
|
|
6
|
+
const REF_PARTS = /^([A-Z]{2,4})[\s-]*([0-9A-Z]{4,8})$/;
|
|
7
|
+
/**
|
|
8
|
+
* A ref as an operator types or pastes it ("aq-7k3f ", "Réf. AQ-7K3F", "ref aq 7k3f", "AQ 7K3F")
|
|
9
|
+
* in the form the landing made it, `AQ-7K3F`, or `null` when it cannot be one
|
|
10
|
+
* (`contracts/vectors/message-ref.json`). In the code Crockford's look-alikes are read as it
|
|
11
|
+
* reads them: O is 0, I and L are 1.
|
|
12
|
+
*/
|
|
13
|
+
export function messageRefOf(raw) {
|
|
14
|
+
const text = (raw ?? "").trim().toUpperCase().replace(REF_LABEL, "").trim();
|
|
15
|
+
const parts = REF_PARTS.exec(text);
|
|
16
|
+
const prefix = parts?.[1];
|
|
17
|
+
const code = parts?.[2];
|
|
18
|
+
if (prefix === undefined || code === undefined)
|
|
19
|
+
return null;
|
|
20
|
+
const ref = `${prefix}-${code.replace(/O/g, "0").replace(/[IL]/g, "1")}`;
|
|
21
|
+
return MESSAGE_REF.test(ref) ? ref : null;
|
|
22
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evinvest/sites-contract",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "The Service-Arb sites ↔ panel contract in TS: the shapes and vocabularies generated from the panel's Rust and protos, and the one TS implementation of what both sides run — pricing, booking and place checks, phone numbers, the ingest events a site sends.",
|
|
5
5
|
"license": "BlueOak-1.0.0",
|
|
6
6
|
"publishConfig": {
|
|
@@ -35,6 +35,9 @@
|
|
|
35
35
|
"typecheck": "tsc --noEmit",
|
|
36
36
|
"prepare": "npm run build"
|
|
37
37
|
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@evinvest/experiments": "^0.6.1"
|
|
40
|
+
},
|
|
38
41
|
"devDependencies": {
|
|
39
42
|
"@types/node": "^22.10.0",
|
|
40
43
|
"typescript": "^5.9.3",
|