@nebulr-group/bridge-auth-core 0.1.2 → 0.4.0-beta.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/billing/bridge-subscription.d.ts +71 -0
- package/dist/billing/bridge-subscription.d.ts.map +1 -0
- package/dist/billing/bridge-subscription.js +209 -0
- package/dist/billing/bridge-subscription.js.map +1 -0
- package/dist/billing/entitlements-store.d.ts +61 -0
- package/dist/billing/entitlements-store.d.ts.map +1 -0
- package/dist/billing/entitlements-store.js +127 -0
- package/dist/billing/entitlements-store.js.map +1 -0
- package/dist/billing/fetch-billing-state.d.ts +9 -0
- package/dist/billing/fetch-billing-state.d.ts.map +1 -0
- package/dist/billing/fetch-billing-state.js +22 -0
- package/dist/billing/fetch-billing-state.js.map +1 -0
- package/dist/billing/lock-signal.d.ts +6 -0
- package/dist/billing/lock-signal.d.ts.map +1 -0
- package/dist/billing/lock-signal.js +8 -0
- package/dist/billing/lock-signal.js.map +1 -0
- package/dist/billing/quota-store.d.ts +86 -0
- package/dist/billing/quota-store.d.ts.map +1 -0
- package/dist/billing/quota-store.js +180 -0
- package/dist/billing/quota-store.js.map +1 -0
- package/dist/billing/types.d.ts +64 -0
- package/dist/billing/types.d.ts.map +1 -0
- package/dist/billing/types.js +49 -0
- package/dist/billing/types.js.map +1 -0
- package/dist/billing/use-bridge.d.ts +131 -0
- package/dist/billing/use-bridge.d.ts.map +1 -0
- package/dist/billing/use-bridge.js +181 -0
- package/dist/billing/use-bridge.js.map +1 -0
- package/dist/bridge-auth.d.ts +36 -1
- package/dist/bridge-auth.d.ts.map +1 -1
- package/dist/bridge-auth.js +57 -3
- package/dist/bridge-auth.js.map +1 -1
- package/dist/direct-auth.d.ts +1 -0
- package/dist/direct-auth.d.ts.map +1 -1
- package/dist/direct-auth.js +7 -0
- package/dist/direct-auth.js.map +1 -1
- package/dist/errors.d.ts +11 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +14 -0
- package/dist/errors.js.map +1 -1
- package/dist/flags/attribute-providers.d.ts +229 -0
- package/dist/flags/attribute-providers.d.ts.map +1 -0
- package/dist/flags/attribute-providers.js +375 -0
- package/dist/flags/attribute-providers.js.map +1 -0
- package/dist/flags/dev-attribute-provider.d.ts +67 -0
- package/dist/flags/dev-attribute-provider.d.ts.map +1 -0
- package/dist/flags/dev-attribute-provider.js +200 -0
- package/dist/flags/dev-attribute-provider.js.map +1 -0
- package/dist/flags/evaluator.d.ts +58 -0
- package/dist/flags/evaluator.d.ts.map +1 -0
- package/dist/flags/evaluator.js +160 -0
- package/dist/flags/evaluator.js.map +1 -0
- package/dist/flags/flag.d.ts +199 -0
- package/dist/flags/flag.d.ts.map +1 -0
- package/dist/flags/flag.js +337 -0
- package/dist/flags/flag.js.map +1 -0
- package/dist/flags/identity.d.ts +77 -0
- package/dist/flags/identity.d.ts.map +1 -0
- package/dist/flags/identity.js +134 -0
- package/dist/flags/identity.js.map +1 -0
- package/dist/flags/index.d.ts +20 -0
- package/dist/flags/index.d.ts.map +1 -0
- package/dist/flags/index.js +21 -0
- package/dist/flags/index.js.map +1 -0
- package/dist/flags/operators.d.ts +63 -0
- package/dist/flags/operators.d.ts.map +1 -0
- package/dist/flags/operators.js +299 -0
- package/dist/flags/operators.js.map +1 -0
- package/dist/flags/propagation.d.ts +16 -0
- package/dist/flags/propagation.d.ts.map +1 -0
- package/dist/flags/propagation.js +107 -0
- package/dist/flags/propagation.js.map +1 -0
- package/dist/flags/realtime.d.ts +328 -0
- package/dist/flags/realtime.d.ts.map +1 -0
- package/dist/flags/realtime.js +504 -0
- package/dist/flags/realtime.js.map +1 -0
- package/dist/flags/runtime-mode.d.ts +32 -0
- package/dist/flags/runtime-mode.d.ts.map +1 -0
- package/dist/flags/runtime-mode.js +84 -0
- package/dist/flags/runtime-mode.js.map +1 -0
- package/dist/flags/telemetry.d.ts +79 -0
- package/dist/flags/telemetry.d.ts.map +1 -0
- package/dist/flags/telemetry.js +286 -0
- package/dist/flags/telemetry.js.map +1 -0
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +11 -1
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +14 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -1
- package/dist/index.js.map +1 -1
- package/dist/management-types.d.ts +38 -0
- package/dist/management-types.d.ts.map +1 -1
- package/dist/route-guard.d.ts.map +1 -1
- package/dist/route-guard.js +15 -5
- package/dist/route-guard.js.map +1 -1
- package/dist/token-manager.d.ts +3 -1
- package/dist/token-manager.d.ts.map +1 -1
- package/dist/token-manager.js +10 -6
- package/dist/token-manager.js.map +1 -1
- package/dist/types.d.ts +10 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/usage/index.d.ts +3 -0
- package/dist/usage/index.d.ts.map +1 -0
- package/dist/usage/index.js +4 -0
- package/dist/usage/index.js.map +1 -0
- package/dist/usage/storage/durable-storage.d.ts +47 -0
- package/dist/usage/storage/durable-storage.d.ts.map +1 -0
- package/dist/usage/storage/durable-storage.js +27 -0
- package/dist/usage/storage/durable-storage.js.map +1 -0
- package/dist/usage/storage/index.d.ts +7 -0
- package/dist/usage/storage/index.d.ts.map +1 -0
- package/dist/usage/storage/index.js +28 -0
- package/dist/usage/storage/index.js.map +1 -0
- package/dist/usage/storage/indexeddb-storage.d.ts +18 -0
- package/dist/usage/storage/indexeddb-storage.d.ts.map +1 -0
- package/dist/usage/storage/indexeddb-storage.js +159 -0
- package/dist/usage/storage/indexeddb-storage.js.map +1 -0
- package/dist/usage/storage/node-fs-storage.d.ts +29 -0
- package/dist/usage/storage/node-fs-storage.d.ts.map +1 -0
- package/dist/usage/storage/node-fs-storage.js +180 -0
- package/dist/usage/storage/node-fs-storage.js.map +1 -0
- package/dist/usage/storage/noop-storage.d.ts +17 -0
- package/dist/usage/storage/noop-storage.d.ts.map +1 -0
- package/dist/usage/storage/noop-storage.js +67 -0
- package/dist/usage/storage/noop-storage.js.map +1 -0
- package/dist/usage/usage-reporter.d.ts +64 -0
- package/dist/usage/usage-reporter.d.ts.map +1 -0
- package/dist/usage/usage-reporter.js +234 -0
- package/dist/usage/usage-reporter.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export declare const OPERATORS: readonly ["eq", "neq", "contains", "not_contains", "in", "not_in", "gt", "lt", "between", "regex", "exists", "not_exists"];
|
|
2
|
+
export type Operator = (typeof OPERATORS)[number];
|
|
3
|
+
export declare const OPERATOR_VERSION: 1;
|
|
4
|
+
export type AttributeType = 'string' | 'number' | 'boolean' | 'date';
|
|
5
|
+
export declare function isOperator(value: unknown): value is Operator;
|
|
6
|
+
export declare function isOperatorValidForType(op: Operator, type: AttributeType): boolean;
|
|
7
|
+
export declare function validOperatorsForType(type: AttributeType): ReadonlyArray<Operator>;
|
|
8
|
+
export declare const CONDITIONS_PER_RULE_MAX = 50;
|
|
9
|
+
export type ConditionValue = string | number | boolean | null;
|
|
10
|
+
export interface Condition {
|
|
11
|
+
attribute: string;
|
|
12
|
+
operator: Operator;
|
|
13
|
+
values: ReadonlyArray<ConditionValue>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Evaluate a single condition against an attribute value.
|
|
17
|
+
*
|
|
18
|
+
* @param condition The condition (attribute, operator, values) from the rule.
|
|
19
|
+
* @param attributeValue The value of the named attribute in the eval context.
|
|
20
|
+
* May be undefined / null if the SDK didn't supply it.
|
|
21
|
+
* @returns true if the condition matches; false otherwise.
|
|
22
|
+
*
|
|
23
|
+
* Operator semantics:
|
|
24
|
+
* eq attributeValue === values[0]
|
|
25
|
+
* neq attributeValue !== values[0]
|
|
26
|
+
* contains String(attributeValue).includes(String(values[0]))
|
|
27
|
+
* not_contains ! contains
|
|
28
|
+
* in values.includes(attributeValue)
|
|
29
|
+
* not_in ! in
|
|
30
|
+
* gt Number(attributeValue) > Number(values[0])
|
|
31
|
+
* lt Number(attributeValue) < Number(values[0])
|
|
32
|
+
* between Number(values[0]) <= Number(attributeValue) <= Number(values[1])
|
|
33
|
+
* (values[0] and values[1] are auto-swapped if out of order)
|
|
34
|
+
* regex new RegExp(String(values[0])).test(String(attributeValue))
|
|
35
|
+
* exists attributeValue !== undefined && attributeValue !== null
|
|
36
|
+
* not_exists ! exists
|
|
37
|
+
*
|
|
38
|
+
* Fail-safe rule: any malformed input (wrong number of values, non-numeric
|
|
39
|
+
* value for a numeric operator, invalid regex, oversized input) returns false
|
|
40
|
+
* rather than throwing. The server must still validate at save time; this
|
|
41
|
+
* function is intentionally lenient at runtime so a bad rule never crashes
|
|
42
|
+
* the consuming app.
|
|
43
|
+
*/
|
|
44
|
+
export declare function evaluateCondition(condition: Condition, attributeValue: unknown): boolean;
|
|
45
|
+
export interface ValidationError {
|
|
46
|
+
/** Index of the condition within its containing branch (or -1 if global). */
|
|
47
|
+
conditionIndex: number;
|
|
48
|
+
attribute: string;
|
|
49
|
+
operator: Operator;
|
|
50
|
+
message: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Validate a list of conditions against a known attribute type map at save time.
|
|
54
|
+
*
|
|
55
|
+
* @param conditions The conditions to validate (e.g. a branch's `conditions`).
|
|
56
|
+
* @param attributeTypes Map of attribute key → known type. If an attribute is
|
|
57
|
+
* not in the map its type is treated as unknown and
|
|
58
|
+
* validation only checks operator membership in the
|
|
59
|
+
* locked set (not type compatibility).
|
|
60
|
+
* @returns list of validation errors; empty list means valid.
|
|
61
|
+
*/
|
|
62
|
+
export declare function validateConditions(conditions: ReadonlyArray<Condition>, attributeTypes?: Readonly<Record<string, AttributeType>>): ValidationError[];
|
|
63
|
+
//# sourceMappingURL=operators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operators.d.ts","sourceRoot":"","sources":["../../src/flags/operators.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,SAAS,4HAaZ,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAElD,eAAO,MAAM,gBAAgB,EAAG,CAAU,CAAC;AAE3C,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;AAarE,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAE5D;AAED,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAEjF;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,aAAa,GAAG,aAAa,CAAC,QAAQ,CAAC,CAElF;AAqBD,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAI1C,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AAE9D,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;CACvC;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,SAAS,EAAE,cAAc,EAAE,OAAO,GAAG,OAAO,CA4FxF;AAID,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,aAAa,CAAC,SAAS,CAAC,EACpC,cAAc,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAM,GAC3D,eAAe,EAAE,CAwGnB"}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
// The locked operator set for Bridge Feature Flags v1.
|
|
2
|
+
//
|
|
3
|
+
// This module is the SINGLE SOURCE OF TRUTH for both:
|
|
4
|
+
// - bridge-api server-side rule evaluation
|
|
5
|
+
// - bridge-* SDK client-side rule evaluation (via auth-core)
|
|
6
|
+
//
|
|
7
|
+
// Adding or removing an operator is a breaking change of the v1 contract.
|
|
8
|
+
// The `OPERATOR_VERSION` constant is forward-compat space — a stored rule
|
|
9
|
+
// carries the operator-set version it was authored against, so future
|
|
10
|
+
// expansions of the closed set can be migrated cleanly.
|
|
11
|
+
export const OPERATORS = [
|
|
12
|
+
'eq',
|
|
13
|
+
'neq',
|
|
14
|
+
'contains',
|
|
15
|
+
'not_contains',
|
|
16
|
+
'in',
|
|
17
|
+
'not_in',
|
|
18
|
+
'gt',
|
|
19
|
+
'lt',
|
|
20
|
+
'between',
|
|
21
|
+
'regex',
|
|
22
|
+
'exists',
|
|
23
|
+
'not_exists',
|
|
24
|
+
];
|
|
25
|
+
export const OPERATOR_VERSION = 1;
|
|
26
|
+
// Which operators are valid for which attribute type. Used by:
|
|
27
|
+
// - Server-side save-time validation (reject mixed-type rules)
|
|
28
|
+
// - Admin UI operator picker (filter dropdown by attribute type)
|
|
29
|
+
// - SDK eval (defensive — should already be validated server-side)
|
|
30
|
+
const VALID_OPERATORS_BY_TYPE = {
|
|
31
|
+
string: ['eq', 'neq', 'contains', 'not_contains', 'in', 'not_in', 'regex', 'exists', 'not_exists'],
|
|
32
|
+
number: ['eq', 'neq', 'gt', 'lt', 'between', 'in', 'not_in', 'exists', 'not_exists'],
|
|
33
|
+
boolean: ['eq', 'neq', 'exists', 'not_exists'],
|
|
34
|
+
date: ['eq', 'neq', 'gt', 'lt', 'between', 'exists', 'not_exists'],
|
|
35
|
+
};
|
|
36
|
+
export function isOperator(value) {
|
|
37
|
+
return typeof value === 'string' && OPERATORS.includes(value);
|
|
38
|
+
}
|
|
39
|
+
export function isOperatorValidForType(op, type) {
|
|
40
|
+
return VALID_OPERATORS_BY_TYPE[type]?.includes(op) ?? false;
|
|
41
|
+
}
|
|
42
|
+
export function validOperatorsForType(type) {
|
|
43
|
+
return VALID_OPERATORS_BY_TYPE[type] ?? [];
|
|
44
|
+
}
|
|
45
|
+
// ── Safety limits ───────────────────────────────────────────────────────────
|
|
46
|
+
//
|
|
47
|
+
// Bounds on inputs that protect against denial-of-service or pathological
|
|
48
|
+
// rules. These are deliberately generous — they exist to draw a line, not to
|
|
49
|
+
// constrain reasonable use. Anything beyond these limits is treated as a
|
|
50
|
+
// non-match rather than evaluated (fail-safe).
|
|
51
|
+
// Cap regex input length to mitigate ReDoS on malicious patterns. Inputs
|
|
52
|
+
// longer than this are treated as non-matching rather than evaluated.
|
|
53
|
+
// Note: a real sandboxed regex engine (e.g. re2-wasm) is a future hardening
|
|
54
|
+
// step. v1 caps input + uses try/catch around the standard engine.
|
|
55
|
+
const REGEX_INPUT_MAX = 10_000;
|
|
56
|
+
// Hard cap on the number of items in `in` / `not_in` value arrays.
|
|
57
|
+
const IN_LIST_MAX = 1_000;
|
|
58
|
+
// Hard cap on the number of conditions allowed in a single rule (across
|
|
59
|
+
// all branches combined). Enforced at save time by the server, exposed here
|
|
60
|
+
// so the UI can surface the limit consistently.
|
|
61
|
+
export const CONDITIONS_PER_RULE_MAX = 50;
|
|
62
|
+
// ── Evaluation ──────────────────────────────────────────────────────────────
|
|
63
|
+
/**
|
|
64
|
+
* Evaluate a single condition against an attribute value.
|
|
65
|
+
*
|
|
66
|
+
* @param condition The condition (attribute, operator, values) from the rule.
|
|
67
|
+
* @param attributeValue The value of the named attribute in the eval context.
|
|
68
|
+
* May be undefined / null if the SDK didn't supply it.
|
|
69
|
+
* @returns true if the condition matches; false otherwise.
|
|
70
|
+
*
|
|
71
|
+
* Operator semantics:
|
|
72
|
+
* eq attributeValue === values[0]
|
|
73
|
+
* neq attributeValue !== values[0]
|
|
74
|
+
* contains String(attributeValue).includes(String(values[0]))
|
|
75
|
+
* not_contains ! contains
|
|
76
|
+
* in values.includes(attributeValue)
|
|
77
|
+
* not_in ! in
|
|
78
|
+
* gt Number(attributeValue) > Number(values[0])
|
|
79
|
+
* lt Number(attributeValue) < Number(values[0])
|
|
80
|
+
* between Number(values[0]) <= Number(attributeValue) <= Number(values[1])
|
|
81
|
+
* (values[0] and values[1] are auto-swapped if out of order)
|
|
82
|
+
* regex new RegExp(String(values[0])).test(String(attributeValue))
|
|
83
|
+
* exists attributeValue !== undefined && attributeValue !== null
|
|
84
|
+
* not_exists ! exists
|
|
85
|
+
*
|
|
86
|
+
* Fail-safe rule: any malformed input (wrong number of values, non-numeric
|
|
87
|
+
* value for a numeric operator, invalid regex, oversized input) returns false
|
|
88
|
+
* rather than throwing. The server must still validate at save time; this
|
|
89
|
+
* function is intentionally lenient at runtime so a bad rule never crashes
|
|
90
|
+
* the consuming app.
|
|
91
|
+
*/
|
|
92
|
+
export function evaluateCondition(condition, attributeValue) {
|
|
93
|
+
const { operator, values } = condition;
|
|
94
|
+
// exists / not_exists handle missing values specially — short-circuit first
|
|
95
|
+
if (operator === 'exists') {
|
|
96
|
+
return attributeValue !== undefined && attributeValue !== null;
|
|
97
|
+
}
|
|
98
|
+
if (operator === 'not_exists') {
|
|
99
|
+
return attributeValue === undefined || attributeValue === null;
|
|
100
|
+
}
|
|
101
|
+
// For every other operator: a missing attribute is always a non-match
|
|
102
|
+
if (attributeValue === undefined || attributeValue === null) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
switch (operator) {
|
|
106
|
+
case 'eq':
|
|
107
|
+
return values.length > 0 && values[0] === attributeValue;
|
|
108
|
+
case 'neq':
|
|
109
|
+
return values.length > 0 && values[0] !== attributeValue;
|
|
110
|
+
case 'contains': {
|
|
111
|
+
if (values.length === 0)
|
|
112
|
+
return false;
|
|
113
|
+
return String(attributeValue).includes(String(values[0]));
|
|
114
|
+
}
|
|
115
|
+
case 'not_contains': {
|
|
116
|
+
if (values.length === 0)
|
|
117
|
+
return false;
|
|
118
|
+
return !String(attributeValue).includes(String(values[0]));
|
|
119
|
+
}
|
|
120
|
+
case 'in': {
|
|
121
|
+
if (values.length === 0 || values.length > IN_LIST_MAX)
|
|
122
|
+
return false;
|
|
123
|
+
return values.includes(attributeValue);
|
|
124
|
+
}
|
|
125
|
+
case 'not_in': {
|
|
126
|
+
// An empty `not_in` list is vacuously true (no values to exclude).
|
|
127
|
+
if (values.length > IN_LIST_MAX)
|
|
128
|
+
return false;
|
|
129
|
+
return !values.includes(attributeValue);
|
|
130
|
+
}
|
|
131
|
+
case 'gt': {
|
|
132
|
+
if (values.length === 0)
|
|
133
|
+
return false;
|
|
134
|
+
const n = Number(attributeValue);
|
|
135
|
+
const t = Number(values[0]);
|
|
136
|
+
if (Number.isNaN(n) || Number.isNaN(t))
|
|
137
|
+
return false;
|
|
138
|
+
return n > t;
|
|
139
|
+
}
|
|
140
|
+
case 'lt': {
|
|
141
|
+
if (values.length === 0)
|
|
142
|
+
return false;
|
|
143
|
+
const n = Number(attributeValue);
|
|
144
|
+
const t = Number(values[0]);
|
|
145
|
+
if (Number.isNaN(n) || Number.isNaN(t))
|
|
146
|
+
return false;
|
|
147
|
+
return n < t;
|
|
148
|
+
}
|
|
149
|
+
case 'between': {
|
|
150
|
+
if (values.length < 2)
|
|
151
|
+
return false;
|
|
152
|
+
const n = Number(attributeValue);
|
|
153
|
+
const a = Number(values[0]);
|
|
154
|
+
const b = Number(values[1]);
|
|
155
|
+
if (Number.isNaN(n) || Number.isNaN(a) || Number.isNaN(b))
|
|
156
|
+
return false;
|
|
157
|
+
const [lo, hi] = a <= b ? [a, b] : [b, a];
|
|
158
|
+
return n >= lo && n <= hi;
|
|
159
|
+
}
|
|
160
|
+
case 'regex': {
|
|
161
|
+
if (values.length === 0)
|
|
162
|
+
return false;
|
|
163
|
+
const input = String(attributeValue);
|
|
164
|
+
if (input.length > REGEX_INPUT_MAX)
|
|
165
|
+
return false;
|
|
166
|
+
try {
|
|
167
|
+
const re = new RegExp(String(values[0]));
|
|
168
|
+
return re.test(input);
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
// Invalid regex pattern — fail-safe: treat as non-match.
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
default: {
|
|
176
|
+
// Exhaustiveness check — `operator` is typed as Operator, so this
|
|
177
|
+
// branch should be unreachable. If it's hit, the rule was authored
|
|
178
|
+
// against a future operator set and we should fail-safe.
|
|
179
|
+
const _exhaustive = operator;
|
|
180
|
+
void _exhaustive;
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Validate a list of conditions against a known attribute type map at save time.
|
|
187
|
+
*
|
|
188
|
+
* @param conditions The conditions to validate (e.g. a branch's `conditions`).
|
|
189
|
+
* @param attributeTypes Map of attribute key → known type. If an attribute is
|
|
190
|
+
* not in the map its type is treated as unknown and
|
|
191
|
+
* validation only checks operator membership in the
|
|
192
|
+
* locked set (not type compatibility).
|
|
193
|
+
* @returns list of validation errors; empty list means valid.
|
|
194
|
+
*/
|
|
195
|
+
export function validateConditions(conditions, attributeTypes = {}) {
|
|
196
|
+
const errors = [];
|
|
197
|
+
conditions.forEach((c, i) => {
|
|
198
|
+
if (!isOperator(c.operator)) {
|
|
199
|
+
errors.push({
|
|
200
|
+
conditionIndex: i,
|
|
201
|
+
attribute: c.attribute,
|
|
202
|
+
operator: c.operator,
|
|
203
|
+
message: `Unknown operator "${c.operator}". Valid operators: ${OPERATORS.join(', ')}.`,
|
|
204
|
+
});
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
// Per-operator structural validation
|
|
208
|
+
switch (c.operator) {
|
|
209
|
+
case 'between':
|
|
210
|
+
if (c.values.length !== 2) {
|
|
211
|
+
errors.push({
|
|
212
|
+
conditionIndex: i,
|
|
213
|
+
attribute: c.attribute,
|
|
214
|
+
operator: c.operator,
|
|
215
|
+
message: `Operator "between" requires exactly 2 values, got ${c.values.length}.`,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
break;
|
|
219
|
+
case 'in':
|
|
220
|
+
case 'not_in':
|
|
221
|
+
if (c.values.length === 0) {
|
|
222
|
+
errors.push({
|
|
223
|
+
conditionIndex: i,
|
|
224
|
+
attribute: c.attribute,
|
|
225
|
+
operator: c.operator,
|
|
226
|
+
message: `Operator "${c.operator}" requires at least 1 value.`,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
else if (c.values.length > IN_LIST_MAX) {
|
|
230
|
+
errors.push({
|
|
231
|
+
conditionIndex: i,
|
|
232
|
+
attribute: c.attribute,
|
|
233
|
+
operator: c.operator,
|
|
234
|
+
message: `Operator "${c.operator}" supports at most ${IN_LIST_MAX} values, got ${c.values.length}.`,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
break;
|
|
238
|
+
case 'exists':
|
|
239
|
+
case 'not_exists':
|
|
240
|
+
if (c.values.length !== 0) {
|
|
241
|
+
errors.push({
|
|
242
|
+
conditionIndex: i,
|
|
243
|
+
attribute: c.attribute,
|
|
244
|
+
operator: c.operator,
|
|
245
|
+
message: `Operator "${c.operator}" takes no values, got ${c.values.length}.`,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
break;
|
|
249
|
+
case 'regex':
|
|
250
|
+
if (c.values.length !== 1) {
|
|
251
|
+
errors.push({
|
|
252
|
+
conditionIndex: i,
|
|
253
|
+
attribute: c.attribute,
|
|
254
|
+
operator: c.operator,
|
|
255
|
+
message: `Operator "regex" requires exactly 1 value, got ${c.values.length}.`,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
try {
|
|
260
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
261
|
+
const _ = new RegExp(String(c.values[0]));
|
|
262
|
+
}
|
|
263
|
+
catch (e) {
|
|
264
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
265
|
+
errors.push({
|
|
266
|
+
conditionIndex: i,
|
|
267
|
+
attribute: c.attribute,
|
|
268
|
+
operator: c.operator,
|
|
269
|
+
message: `Invalid regex pattern: ${msg}`,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
break;
|
|
274
|
+
default:
|
|
275
|
+
// Most binary operators expect exactly 1 value
|
|
276
|
+
if (c.values.length !== 1) {
|
|
277
|
+
errors.push({
|
|
278
|
+
conditionIndex: i,
|
|
279
|
+
attribute: c.attribute,
|
|
280
|
+
operator: c.operator,
|
|
281
|
+
message: `Operator "${c.operator}" requires exactly 1 value, got ${c.values.length}.`,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
// Type-vs-operator compatibility (only when the attribute's type is known)
|
|
287
|
+
const type = attributeTypes[c.attribute];
|
|
288
|
+
if (type && !isOperatorValidForType(c.operator, type)) {
|
|
289
|
+
errors.push({
|
|
290
|
+
conditionIndex: i,
|
|
291
|
+
attribute: c.attribute,
|
|
292
|
+
operator: c.operator,
|
|
293
|
+
message: `Operator "${c.operator}" cannot be used with attribute "${c.attribute}" of type ${type}. Valid operators for ${type}: ${validOperatorsForType(type).join(', ')}.`,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
return errors;
|
|
298
|
+
}
|
|
299
|
+
//# sourceMappingURL=operators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operators.js","sourceRoot":"","sources":["../../src/flags/operators.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { EvalContext } from './evaluator.js';
|
|
2
|
+
/** Canonical HTTP header carrying the serialized eval context. */
|
|
3
|
+
export declare const BRIDGE_CONTEXT_HEADER: "x-bridge-context";
|
|
4
|
+
/** Serialize an eval context into a base64url-encoded JSON string. */
|
|
5
|
+
export declare function serializeContext(ctx: EvalContext): string;
|
|
6
|
+
/**
|
|
7
|
+
* Parse the header value back into an EvalContext. Returns `undefined` if the
|
|
8
|
+
* value is missing, malformed, or carries an unknown wire version. Callers
|
|
9
|
+
* should treat undefined as "no context shipped" — never throw on the request
|
|
10
|
+
* path.
|
|
11
|
+
*/
|
|
12
|
+
export declare function deserializeContext(headerValue: string | undefined | null): EvalContext | undefined;
|
|
13
|
+
export declare function serverInstanceId(): string;
|
|
14
|
+
/** Test-only — reset the cached server-instance id. */
|
|
15
|
+
export declare function __resetServerInstanceId(): void;
|
|
16
|
+
//# sourceMappingURL=propagation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"propagation.d.ts","sourceRoot":"","sources":["../../src/flags/propagation.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAOlD,kEAAkE;AAClE,eAAO,MAAM,qBAAqB,EAAG,kBAA2B,CAAC;AAWjE,sEAAsE;AACtE,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CAMzD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,WAAW,GAAG,SAAS,CAoBlG;AAcD,wBAAgB,gBAAgB,IAAI,MAAM,CAKzC;AAED,uDAAuD;AACvD,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Frontend → backend context propagation (TBP-171).
|
|
2
|
+
//
|
|
3
|
+
// The frontend SDK serializes the eval context into a header on outgoing
|
|
4
|
+
// HTTP requests; the backend SDK / NestJS guard / Express middleware reads
|
|
5
|
+
// the same header and rehydrates the context for backend evals. Same flag,
|
|
6
|
+
// same answer, on both ends of the wire.
|
|
7
|
+
//
|
|
8
|
+
// Header name + format are part of the SDK ↔ backend contract — don't
|
|
9
|
+
// change without a coordinated rollout.
|
|
10
|
+
import { generateAnonymousId } from './identity.js';
|
|
11
|
+
/** Canonical HTTP header carrying the serialized eval context. */
|
|
12
|
+
export const BRIDGE_CONTEXT_HEADER = 'x-bridge-context';
|
|
13
|
+
/** Wire format version — bump when the JSON shape changes incompatibly. */
|
|
14
|
+
const WIRE_VERSION = 1;
|
|
15
|
+
/** Serialize an eval context into a base64url-encoded JSON string. */
|
|
16
|
+
export function serializeContext(ctx) {
|
|
17
|
+
const wire = { v: WIRE_VERSION };
|
|
18
|
+
if (ctx.identity)
|
|
19
|
+
wire.i = ctx.identity;
|
|
20
|
+
// Omit empty attributes object — keep the wire small.
|
|
21
|
+
if (ctx.attributes && Object.keys(ctx.attributes).length > 0)
|
|
22
|
+
wire.a = ctx.attributes;
|
|
23
|
+
return base64UrlEncode(JSON.stringify(wire));
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Parse the header value back into an EvalContext. Returns `undefined` if the
|
|
27
|
+
* value is missing, malformed, or carries an unknown wire version. Callers
|
|
28
|
+
* should treat undefined as "no context shipped" — never throw on the request
|
|
29
|
+
* path.
|
|
30
|
+
*/
|
|
31
|
+
export function deserializeContext(headerValue) {
|
|
32
|
+
if (typeof headerValue !== 'string' || headerValue.length === 0)
|
|
33
|
+
return undefined;
|
|
34
|
+
let json;
|
|
35
|
+
try {
|
|
36
|
+
json = base64UrlDecode(headerValue);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
let parsed;
|
|
42
|
+
try {
|
|
43
|
+
parsed = JSON.parse(json);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
if (!parsed || typeof parsed !== 'object')
|
|
49
|
+
return undefined;
|
|
50
|
+
if (parsed.v !== WIRE_VERSION)
|
|
51
|
+
return undefined;
|
|
52
|
+
return {
|
|
53
|
+
identity: typeof parsed.i === 'string' ? parsed.i : undefined,
|
|
54
|
+
attributes: parsed.a && typeof parsed.a === 'object' ? { ...parsed.a } : {},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Generate a stable per-process server-instance ID for backend SDKs that
|
|
59
|
+
* want to evaluate system-level flags (TBP-172). The first call mints a UUID
|
|
60
|
+
* v4; subsequent calls return the same value for the lifetime of the process.
|
|
61
|
+
*
|
|
62
|
+
* The ID is INTENTIONALLY ephemeral across restarts — system flags should
|
|
63
|
+
* target either a small set of stable identifiers (region, version, role) or
|
|
64
|
+
* a hash of those, not the auto-generated process ID. The process ID is
|
|
65
|
+
* primarily for telemetry / debugging where "which server replied to me"
|
|
66
|
+
* matters.
|
|
67
|
+
*/
|
|
68
|
+
let _serverInstanceId;
|
|
69
|
+
export function serverInstanceId() {
|
|
70
|
+
if (!_serverInstanceId) {
|
|
71
|
+
_serverInstanceId = `srv_${generateAnonymousId().replace(/^anon_/, '')}`;
|
|
72
|
+
}
|
|
73
|
+
return _serverInstanceId;
|
|
74
|
+
}
|
|
75
|
+
/** Test-only — reset the cached server-instance id. */
|
|
76
|
+
export function __resetServerInstanceId() {
|
|
77
|
+
_serverInstanceId = undefined;
|
|
78
|
+
}
|
|
79
|
+
// ── base64url helpers (no dep on Buffer; works in browser + node) ───────────
|
|
80
|
+
function base64UrlEncode(input) {
|
|
81
|
+
// Use globalThis.btoa when available (browsers, modern Node), fall back to Buffer.
|
|
82
|
+
const g = globalThis;
|
|
83
|
+
let b64;
|
|
84
|
+
if (typeof g.btoa === 'function') {
|
|
85
|
+
// btoa handles binary strings — convert UTF-8 to binary string first
|
|
86
|
+
b64 = g.btoa(unescape(encodeURIComponent(input)));
|
|
87
|
+
}
|
|
88
|
+
else if (typeof Buffer !== 'undefined') {
|
|
89
|
+
b64 = Buffer.from(input, 'utf-8').toString('base64');
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
throw new Error('No base64 encoder available');
|
|
93
|
+
}
|
|
94
|
+
return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
95
|
+
}
|
|
96
|
+
function base64UrlDecode(input) {
|
|
97
|
+
const padded = input.replace(/-/g, '+').replace(/_/g, '/') + '='.repeat((4 - (input.length % 4)) % 4);
|
|
98
|
+
const g = globalThis;
|
|
99
|
+
if (typeof g.atob === 'function') {
|
|
100
|
+
return decodeURIComponent(escape(g.atob(padded)));
|
|
101
|
+
}
|
|
102
|
+
if (typeof Buffer !== 'undefined') {
|
|
103
|
+
return Buffer.from(padded, 'base64').toString('utf-8');
|
|
104
|
+
}
|
|
105
|
+
throw new Error('No base64 decoder available');
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=propagation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"propagation.js","sourceRoot":"","sources":["../../src/flags/propagation.ts"],"names":[],"mappings":"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"}
|