@shipeasy/devtools-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +40 -0
- package/README.md +19 -0
- package/dist/api.d.mts +143 -0
- package/dist/api.d.ts +143 -0
- package/dist/api.js +10 -0
- package/dist/api.mjs +10 -0
- package/dist/auth.d.mts +57 -0
- package/dist/auth.d.ts +57 -0
- package/dist/auth.js +12 -0
- package/dist/auth.mjs +12 -0
- package/dist/chunk-2B75JLTX.js +25 -0
- package/dist/chunk-5VQLXGNR.mjs +101 -0
- package/dist/chunk-6SU5REDK.mjs +2848 -0
- package/dist/chunk-7NO55TFC.mjs +25 -0
- package/dist/chunk-GUTBZI2O.js +2848 -0
- package/dist/chunk-I2KLQ2HA.js +22 -0
- package/dist/chunk-KPO3R7ND.js +28 -0
- package/dist/chunk-LCRTLHGL.js +101 -0
- package/dist/chunk-LZDNBMH6.mjs +1394 -0
- package/dist/chunk-MK56AKZR.js +178 -0
- package/dist/chunk-RANNOC72.js +72 -0
- package/dist/chunk-RH6X24HG.mjs +28 -0
- package/dist/chunk-U5DDQYSF.mjs +22 -0
- package/dist/chunk-VHYOR3XA.mjs +178 -0
- package/dist/chunk-WP2TWHG3.mjs +72 -0
- package/dist/chunk-YQHCUZGP.js +1394 -0
- package/dist/feedback-state.d.mts +29 -0
- package/dist/feedback-state.d.ts +29 -0
- package/dist/feedback-state.js +9 -0
- package/dist/feedback-state.mjs +9 -0
- package/dist/forms.d.mts +95 -0
- package/dist/forms.d.ts +95 -0
- package/dist/forms.js +21 -0
- package/dist/forms.mjs +21 -0
- package/dist/gate-flow.d.mts +41 -0
- package/dist/gate-flow.d.ts +41 -0
- package/dist/gate-flow.js +17 -0
- package/dist/gate-flow.mjs +17 -0
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +86 -0
- package/dist/index.mjs +86 -0
- package/dist/public-report.d.mts +56 -0
- package/dist/public-report.d.ts +56 -0
- package/dist/public-report.js +12 -0
- package/dist/public-report.mjs +12 -0
- package/dist/self-report.d.mts +25 -0
- package/dist/self-report.d.ts +25 -0
- package/dist/self-report.js +73 -0
- package/dist/self-report.mjs +73 -0
- package/dist/types-D0sroUDD.d.mts +278 -0
- package/dist/types-D0sroUDD.d.ts +278 -0
- package/dist/zod.gen-DTKpgw-T.d.mts +64 -0
- package/dist/zod.gen-DTKpgw-T.d.ts +64 -0
- package/package.json +58 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/gate-flow.ts
|
|
2
|
+
var AUTO_ATTRS = /* @__PURE__ */ new Set([
|
|
3
|
+
"country",
|
|
4
|
+
"continent",
|
|
5
|
+
"region",
|
|
6
|
+
"is_eu",
|
|
7
|
+
"is_bot",
|
|
8
|
+
"browser",
|
|
9
|
+
"os",
|
|
10
|
+
"device",
|
|
11
|
+
"is_mobile",
|
|
12
|
+
"ip",
|
|
13
|
+
"referrer",
|
|
14
|
+
"request_url",
|
|
15
|
+
"locale",
|
|
16
|
+
"timezone",
|
|
17
|
+
"city"
|
|
18
|
+
]);
|
|
19
|
+
var OP_LABELS = {
|
|
20
|
+
eq: "is",
|
|
21
|
+
neq: "is not",
|
|
22
|
+
in: "is one of",
|
|
23
|
+
not_in: "is not one of",
|
|
24
|
+
gt: ">",
|
|
25
|
+
gte: "\u2265",
|
|
26
|
+
lt: "<",
|
|
27
|
+
lte: "\u2264",
|
|
28
|
+
contains: "contains",
|
|
29
|
+
regex: "matches",
|
|
30
|
+
semver_gt: "version >",
|
|
31
|
+
semver_gte: "version \u2265",
|
|
32
|
+
semver_lt: "version <",
|
|
33
|
+
semver_lte: "version \u2264"
|
|
34
|
+
};
|
|
35
|
+
function fmtValue(value) {
|
|
36
|
+
if (Array.isArray(value)) return value.map((v) => String(v)).join(", ");
|
|
37
|
+
if (typeof value === "string") return value;
|
|
38
|
+
if (value == null) return "";
|
|
39
|
+
return JSON.stringify(value);
|
|
40
|
+
}
|
|
41
|
+
function sourceFor(step) {
|
|
42
|
+
if (step.whitelist) return "whitelist";
|
|
43
|
+
if (step.isPublicFloor || step.rules.length === 0) return "public";
|
|
44
|
+
if (step.rules.some((r) => AUTO_ATTRS.has(r.attr))) return "auto";
|
|
45
|
+
return "user";
|
|
46
|
+
}
|
|
47
|
+
function stepTitle(e) {
|
|
48
|
+
if (e.locked) return "Public";
|
|
49
|
+
if (e.whitelist) return _optionalChain([e, 'access', _ => _.name, 'optionalAccess', _2 => _2.trim, 'call', _3 => _3()]) || "Whitelist";
|
|
50
|
+
if (_optionalChain([e, 'access', _4 => _4.name, 'optionalAccess', _5 => _5.trim, 'call', _6 => _6()])) return e.name.trim();
|
|
51
|
+
if (e.type === "rollout") return "Percentage rollout";
|
|
52
|
+
return "Condition";
|
|
53
|
+
}
|
|
54
|
+
function buildGateFlow(gate) {
|
|
55
|
+
const stack = Array.isArray(gate.stack) ? gate.stack : [];
|
|
56
|
+
if (stack.length > 0) {
|
|
57
|
+
return stack.map((e, i) => {
|
|
58
|
+
const type = e.type === "rollout" ? "rollout" : "condition";
|
|
59
|
+
const rules = Array.isArray(e.rules) ? e.rules : [];
|
|
60
|
+
const whitelist = e.whitelist ? Array.isArray(_optionalChain([rules, 'access', _7 => _7[0], 'optionalAccess', _8 => _8.value])) ? rules[0].value.map(String) : [] : null;
|
|
61
|
+
const isPublicFloor = Boolean(e.locked) && type === "rollout";
|
|
62
|
+
const base = {
|
|
63
|
+
key: _nullishCoalesce(e.id, () => ( String(i))),
|
|
64
|
+
type,
|
|
65
|
+
title: stepTitle(e),
|
|
66
|
+
rules,
|
|
67
|
+
pass: e.pass === "any" ? "any" : "all",
|
|
68
|
+
rolloutPct: _nullishCoalesce(e.rolloutPct, () => ( (type === "condition" ? 1e4 : 0))),
|
|
69
|
+
bucketBy: _optionalChain([e, 'access', _9 => _9.bucketBy, 'optionalAccess', _10 => _10.trim, 'call', _11 => _11()]) || "user_id",
|
|
70
|
+
isPublicFloor,
|
|
71
|
+
whitelist
|
|
72
|
+
};
|
|
73
|
+
return { ...base, source: sourceFor(base) };
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
const flatRules = Array.isArray(gate.rules) ? gate.rules : [];
|
|
77
|
+
const steps = [];
|
|
78
|
+
if (flatRules.length) {
|
|
79
|
+
const cond = {
|
|
80
|
+
key: "flat-cond",
|
|
81
|
+
type: "condition",
|
|
82
|
+
title: "Targeting rules",
|
|
83
|
+
rules: flatRules,
|
|
84
|
+
pass: "all",
|
|
85
|
+
rolloutPct: 1e4,
|
|
86
|
+
bucketBy: "user_id",
|
|
87
|
+
isPublicFloor: false,
|
|
88
|
+
whitelist: null
|
|
89
|
+
};
|
|
90
|
+
steps.push({ ...cond, source: sourceFor(cond) });
|
|
91
|
+
}
|
|
92
|
+
const floor = {
|
|
93
|
+
key: "flat-rollout",
|
|
94
|
+
type: "rollout",
|
|
95
|
+
title: "Public",
|
|
96
|
+
rules: [],
|
|
97
|
+
pass: "all",
|
|
98
|
+
rolloutPct: _nullishCoalesce(gate.rolloutPct, () => ( 0)),
|
|
99
|
+
bucketBy: "user_id",
|
|
100
|
+
isPublicFloor: true,
|
|
101
|
+
whitelist: null
|
|
102
|
+
};
|
|
103
|
+
steps.push({ ...floor, source: sourceFor(floor) });
|
|
104
|
+
return steps;
|
|
105
|
+
}
|
|
106
|
+
function ruleSummary(step) {
|
|
107
|
+
if (step.whitelist) return step.whitelist.length ? step.whitelist.join(", ") : "No entries yet";
|
|
108
|
+
if (step.rules.length === 0) return "everyone who reaches here";
|
|
109
|
+
return step.rules.map((r) => `${r.attr} ${_nullishCoalesce(OP_LABELS[r.op], () => ( r.op))} ${fmtValue(r.value) || "\u2014"}`).join(" \xB7 ");
|
|
110
|
+
}
|
|
111
|
+
function cmpSemver(a, b) {
|
|
112
|
+
const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
|
|
113
|
+
const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
|
|
114
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
115
|
+
const d = (_nullishCoalesce(pa[i], () => ( 0))) - (_nullishCoalesce(pb[i], () => ( 0)));
|
|
116
|
+
if (d !== 0) return d < 0 ? -1 : 1;
|
|
117
|
+
}
|
|
118
|
+
return 0;
|
|
119
|
+
}
|
|
120
|
+
function evalRule(rule, user) {
|
|
121
|
+
const has = Object.prototype.hasOwnProperty.call(user, rule.attr);
|
|
122
|
+
const uv = user[rule.attr];
|
|
123
|
+
const v = rule.value;
|
|
124
|
+
const s = (x) => x == null ? "" : String(x);
|
|
125
|
+
switch (rule.op) {
|
|
126
|
+
case "eq":
|
|
127
|
+
return has && s(uv) === s(v);
|
|
128
|
+
case "neq":
|
|
129
|
+
return !has || s(uv) !== s(v);
|
|
130
|
+
case "in":
|
|
131
|
+
return has && Array.isArray(v) && v.some((x) => s(x) === s(uv));
|
|
132
|
+
case "not_in":
|
|
133
|
+
return !has || !Array.isArray(v) || !v.some((x) => s(x) === s(uv));
|
|
134
|
+
case "gt":
|
|
135
|
+
return has && Number(uv) > Number(v);
|
|
136
|
+
case "gte":
|
|
137
|
+
return has && Number(uv) >= Number(v);
|
|
138
|
+
case "lt":
|
|
139
|
+
return has && Number(uv) < Number(v);
|
|
140
|
+
case "lte":
|
|
141
|
+
return has && Number(uv) <= Number(v);
|
|
142
|
+
case "contains":
|
|
143
|
+
return has && s(uv).includes(s(v));
|
|
144
|
+
case "regex":
|
|
145
|
+
try {
|
|
146
|
+
return has && new RegExp(s(v)).test(s(uv));
|
|
147
|
+
} catch (e2) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
case "semver_gt":
|
|
151
|
+
return has && cmpSemver(s(uv), s(v)) > 0;
|
|
152
|
+
case "semver_gte":
|
|
153
|
+
return has && cmpSemver(s(uv), s(v)) >= 0;
|
|
154
|
+
case "semver_lt":
|
|
155
|
+
return has && cmpSemver(s(uv), s(v)) < 0;
|
|
156
|
+
case "semver_lte":
|
|
157
|
+
return has && cmpSemver(s(uv), s(v)) <= 0;
|
|
158
|
+
default:
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function evalStepMatch(step, user, passOverride) {
|
|
163
|
+
if (step.type !== "condition" || step.rules.length === 0 || step.whitelist) return "n/a";
|
|
164
|
+
if (!user) return "n/a";
|
|
165
|
+
const mode = _nullishCoalesce(_nullishCoalesce(passOverride, () => ( step.pass)), () => ( "all"));
|
|
166
|
+
const results = step.rules.map((r) => evalRule(r, user));
|
|
167
|
+
const ok = mode === "any" ? results.some(Boolean) : results.every(Boolean);
|
|
168
|
+
return ok ? "pass" : "fail";
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
exports.OP_LABELS = OP_LABELS; exports.fmtValue = fmtValue; exports.buildGateFlow = buildGateFlow; exports.ruleSummary = ruleSummary; exports.evalRule = evalRule; exports.evalStepMatch = evalStepMatch;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } var _class;
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
var _chunkKPO3R7NDjs = require('./chunk-KPO3R7ND.js');
|
|
5
|
+
|
|
6
|
+
// src/public-report.ts
|
|
7
|
+
var PublicTicketsDisabled = (_class = class extends Error {
|
|
8
|
+
__init() {this.status = 403}
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);_class.prototype.__init.call(this);;
|
|
11
|
+
this.name = "PublicTicketsDisabled";
|
|
12
|
+
}
|
|
13
|
+
}, _class);
|
|
14
|
+
async function postPublicReport(body, opts, noun) {
|
|
15
|
+
const edge = (_nullishCoalesce(opts.edgeBaseUrl, () => ( _chunkKPO3R7NDjs.DEFAULT_EDGE_BASE_URL))).replace(/\/$/, "");
|
|
16
|
+
const fetchImpl = _nullishCoalesce(opts.fetch, () => ( ((...args) => fetch(...args))));
|
|
17
|
+
const res = await fetchImpl(`${edge}/cli/report`, {
|
|
18
|
+
method: "POST",
|
|
19
|
+
headers: {
|
|
20
|
+
"Content-Type": "application/json",
|
|
21
|
+
"X-SDK-Key": opts.clientKey
|
|
22
|
+
},
|
|
23
|
+
body: JSON.stringify(body)
|
|
24
|
+
});
|
|
25
|
+
if (res.status === 403) {
|
|
26
|
+
const resBody = await res.json().catch(() => ({}));
|
|
27
|
+
throw new PublicTicketsDisabled(
|
|
28
|
+
_nullishCoalesce(resBody.error, () => ( "Public reporting is not enabled for this project."))
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
if (!res.ok) {
|
|
32
|
+
const resBody = await res.json().catch(() => ({}));
|
|
33
|
+
throw new Error(_nullishCoalesce(resBody.error, () => ( `${noun} failed (HTTP ${res.status}). Please try again.`)));
|
|
34
|
+
}
|
|
35
|
+
const data = await res.json();
|
|
36
|
+
return { number: data.number, deduped: data.deduped === true };
|
|
37
|
+
}
|
|
38
|
+
async function submitPublicBug(input, opts) {
|
|
39
|
+
return postPublicReport(
|
|
40
|
+
{
|
|
41
|
+
title: input.title,
|
|
42
|
+
...input.error ? { error: input.error } : {},
|
|
43
|
+
...input.description ? { description: input.description } : {},
|
|
44
|
+
...input.reporterEmail ? { reporter_email: input.reporterEmail } : {},
|
|
45
|
+
...input.step ? { step: input.step } : {},
|
|
46
|
+
...input.context ? { context: input.context } : {}
|
|
47
|
+
},
|
|
48
|
+
opts,
|
|
49
|
+
"Bug report"
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
async function submitPublicFeature(input, opts) {
|
|
53
|
+
return postPublicReport(
|
|
54
|
+
{
|
|
55
|
+
type: "feature",
|
|
56
|
+
title: input.title,
|
|
57
|
+
...input.description ? { description: input.description } : {},
|
|
58
|
+
...input.useCase ? { use_case: input.useCase } : {},
|
|
59
|
+
...input.reporterEmail ? { reporter_email: input.reporterEmail } : {},
|
|
60
|
+
...input.step ? { step: input.step } : {},
|
|
61
|
+
...input.context ? { context: input.context } : {}
|
|
62
|
+
},
|
|
63
|
+
opts,
|
|
64
|
+
"Feature request"
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
exports.PublicTicketsDisabled = PublicTicketsDisabled; exports.submitPublicBug = submitPublicBug; exports.submitPublicFeature = submitPublicFeature;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__export,
|
|
3
|
+
__reExport
|
|
4
|
+
} from "./chunk-U5DDQYSF.mjs";
|
|
5
|
+
|
|
6
|
+
// src/types.ts
|
|
7
|
+
var types_exports = {};
|
|
8
|
+
__export(types_exports, {
|
|
9
|
+
DEFAULT_ADMIN_BASE_URL: () => DEFAULT_ADMIN_BASE_URL,
|
|
10
|
+
DEFAULT_EDGE_BASE_URL: () => DEFAULT_EDGE_BASE_URL,
|
|
11
|
+
PERMISSIVE_CONFIG_SCHEMA: () => PERMISSIVE_CONFIG_SCHEMA
|
|
12
|
+
});
|
|
13
|
+
__reExport(types_exports, devtools_contract_star);
|
|
14
|
+
import * as devtools_contract_star from "@shipeasy/sdk/devtools-contract";
|
|
15
|
+
var DEFAULT_ADMIN_BASE_URL = "https://shipeasy.ai";
|
|
16
|
+
var DEFAULT_EDGE_BASE_URL = "https://api.shipeasy.ai";
|
|
17
|
+
var PERMISSIVE_CONFIG_SCHEMA = {
|
|
18
|
+
type: "object",
|
|
19
|
+
properties: {},
|
|
20
|
+
additionalProperties: true
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export {
|
|
24
|
+
DEFAULT_ADMIN_BASE_URL,
|
|
25
|
+
DEFAULT_EDGE_BASE_URL,
|
|
26
|
+
PERMISSIVE_CONFIG_SCHEMA,
|
|
27
|
+
types_exports
|
|
28
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
__export,
|
|
21
|
+
__reExport
|
|
22
|
+
};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// src/gate-flow.ts
|
|
2
|
+
var AUTO_ATTRS = /* @__PURE__ */ new Set([
|
|
3
|
+
"country",
|
|
4
|
+
"continent",
|
|
5
|
+
"region",
|
|
6
|
+
"is_eu",
|
|
7
|
+
"is_bot",
|
|
8
|
+
"browser",
|
|
9
|
+
"os",
|
|
10
|
+
"device",
|
|
11
|
+
"is_mobile",
|
|
12
|
+
"ip",
|
|
13
|
+
"referrer",
|
|
14
|
+
"request_url",
|
|
15
|
+
"locale",
|
|
16
|
+
"timezone",
|
|
17
|
+
"city"
|
|
18
|
+
]);
|
|
19
|
+
var OP_LABELS = {
|
|
20
|
+
eq: "is",
|
|
21
|
+
neq: "is not",
|
|
22
|
+
in: "is one of",
|
|
23
|
+
not_in: "is not one of",
|
|
24
|
+
gt: ">",
|
|
25
|
+
gte: "\u2265",
|
|
26
|
+
lt: "<",
|
|
27
|
+
lte: "\u2264",
|
|
28
|
+
contains: "contains",
|
|
29
|
+
regex: "matches",
|
|
30
|
+
semver_gt: "version >",
|
|
31
|
+
semver_gte: "version \u2265",
|
|
32
|
+
semver_lt: "version <",
|
|
33
|
+
semver_lte: "version \u2264"
|
|
34
|
+
};
|
|
35
|
+
function fmtValue(value) {
|
|
36
|
+
if (Array.isArray(value)) return value.map((v) => String(v)).join(", ");
|
|
37
|
+
if (typeof value === "string") return value;
|
|
38
|
+
if (value == null) return "";
|
|
39
|
+
return JSON.stringify(value);
|
|
40
|
+
}
|
|
41
|
+
function sourceFor(step) {
|
|
42
|
+
if (step.whitelist) return "whitelist";
|
|
43
|
+
if (step.isPublicFloor || step.rules.length === 0) return "public";
|
|
44
|
+
if (step.rules.some((r) => AUTO_ATTRS.has(r.attr))) return "auto";
|
|
45
|
+
return "user";
|
|
46
|
+
}
|
|
47
|
+
function stepTitle(e) {
|
|
48
|
+
if (e.locked) return "Public";
|
|
49
|
+
if (e.whitelist) return e.name?.trim() || "Whitelist";
|
|
50
|
+
if (e.name?.trim()) return e.name.trim();
|
|
51
|
+
if (e.type === "rollout") return "Percentage rollout";
|
|
52
|
+
return "Condition";
|
|
53
|
+
}
|
|
54
|
+
function buildGateFlow(gate) {
|
|
55
|
+
const stack = Array.isArray(gate.stack) ? gate.stack : [];
|
|
56
|
+
if (stack.length > 0) {
|
|
57
|
+
return stack.map((e, i) => {
|
|
58
|
+
const type = e.type === "rollout" ? "rollout" : "condition";
|
|
59
|
+
const rules = Array.isArray(e.rules) ? e.rules : [];
|
|
60
|
+
const whitelist = e.whitelist ? Array.isArray(rules[0]?.value) ? rules[0].value.map(String) : [] : null;
|
|
61
|
+
const isPublicFloor = Boolean(e.locked) && type === "rollout";
|
|
62
|
+
const base = {
|
|
63
|
+
key: e.id ?? String(i),
|
|
64
|
+
type,
|
|
65
|
+
title: stepTitle(e),
|
|
66
|
+
rules,
|
|
67
|
+
pass: e.pass === "any" ? "any" : "all",
|
|
68
|
+
rolloutPct: e.rolloutPct ?? (type === "condition" ? 1e4 : 0),
|
|
69
|
+
bucketBy: e.bucketBy?.trim() || "user_id",
|
|
70
|
+
isPublicFloor,
|
|
71
|
+
whitelist
|
|
72
|
+
};
|
|
73
|
+
return { ...base, source: sourceFor(base) };
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
const flatRules = Array.isArray(gate.rules) ? gate.rules : [];
|
|
77
|
+
const steps = [];
|
|
78
|
+
if (flatRules.length) {
|
|
79
|
+
const cond = {
|
|
80
|
+
key: "flat-cond",
|
|
81
|
+
type: "condition",
|
|
82
|
+
title: "Targeting rules",
|
|
83
|
+
rules: flatRules,
|
|
84
|
+
pass: "all",
|
|
85
|
+
rolloutPct: 1e4,
|
|
86
|
+
bucketBy: "user_id",
|
|
87
|
+
isPublicFloor: false,
|
|
88
|
+
whitelist: null
|
|
89
|
+
};
|
|
90
|
+
steps.push({ ...cond, source: sourceFor(cond) });
|
|
91
|
+
}
|
|
92
|
+
const floor = {
|
|
93
|
+
key: "flat-rollout",
|
|
94
|
+
type: "rollout",
|
|
95
|
+
title: "Public",
|
|
96
|
+
rules: [],
|
|
97
|
+
pass: "all",
|
|
98
|
+
rolloutPct: gate.rolloutPct ?? 0,
|
|
99
|
+
bucketBy: "user_id",
|
|
100
|
+
isPublicFloor: true,
|
|
101
|
+
whitelist: null
|
|
102
|
+
};
|
|
103
|
+
steps.push({ ...floor, source: sourceFor(floor) });
|
|
104
|
+
return steps;
|
|
105
|
+
}
|
|
106
|
+
function ruleSummary(step) {
|
|
107
|
+
if (step.whitelist) return step.whitelist.length ? step.whitelist.join(", ") : "No entries yet";
|
|
108
|
+
if (step.rules.length === 0) return "everyone who reaches here";
|
|
109
|
+
return step.rules.map((r) => `${r.attr} ${OP_LABELS[r.op] ?? r.op} ${fmtValue(r.value) || "\u2014"}`).join(" \xB7 ");
|
|
110
|
+
}
|
|
111
|
+
function cmpSemver(a, b) {
|
|
112
|
+
const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
|
|
113
|
+
const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
|
|
114
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
115
|
+
const d = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
116
|
+
if (d !== 0) return d < 0 ? -1 : 1;
|
|
117
|
+
}
|
|
118
|
+
return 0;
|
|
119
|
+
}
|
|
120
|
+
function evalRule(rule, user) {
|
|
121
|
+
const has = Object.prototype.hasOwnProperty.call(user, rule.attr);
|
|
122
|
+
const uv = user[rule.attr];
|
|
123
|
+
const v = rule.value;
|
|
124
|
+
const s = (x) => x == null ? "" : String(x);
|
|
125
|
+
switch (rule.op) {
|
|
126
|
+
case "eq":
|
|
127
|
+
return has && s(uv) === s(v);
|
|
128
|
+
case "neq":
|
|
129
|
+
return !has || s(uv) !== s(v);
|
|
130
|
+
case "in":
|
|
131
|
+
return has && Array.isArray(v) && v.some((x) => s(x) === s(uv));
|
|
132
|
+
case "not_in":
|
|
133
|
+
return !has || !Array.isArray(v) || !v.some((x) => s(x) === s(uv));
|
|
134
|
+
case "gt":
|
|
135
|
+
return has && Number(uv) > Number(v);
|
|
136
|
+
case "gte":
|
|
137
|
+
return has && Number(uv) >= Number(v);
|
|
138
|
+
case "lt":
|
|
139
|
+
return has && Number(uv) < Number(v);
|
|
140
|
+
case "lte":
|
|
141
|
+
return has && Number(uv) <= Number(v);
|
|
142
|
+
case "contains":
|
|
143
|
+
return has && s(uv).includes(s(v));
|
|
144
|
+
case "regex":
|
|
145
|
+
try {
|
|
146
|
+
return has && new RegExp(s(v)).test(s(uv));
|
|
147
|
+
} catch {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
case "semver_gt":
|
|
151
|
+
return has && cmpSemver(s(uv), s(v)) > 0;
|
|
152
|
+
case "semver_gte":
|
|
153
|
+
return has && cmpSemver(s(uv), s(v)) >= 0;
|
|
154
|
+
case "semver_lt":
|
|
155
|
+
return has && cmpSemver(s(uv), s(v)) < 0;
|
|
156
|
+
case "semver_lte":
|
|
157
|
+
return has && cmpSemver(s(uv), s(v)) <= 0;
|
|
158
|
+
default:
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function evalStepMatch(step, user, passOverride) {
|
|
163
|
+
if (step.type !== "condition" || step.rules.length === 0 || step.whitelist) return "n/a";
|
|
164
|
+
if (!user) return "n/a";
|
|
165
|
+
const mode = passOverride ?? step.pass ?? "all";
|
|
166
|
+
const results = step.rules.map((r) => evalRule(r, user));
|
|
167
|
+
const ok = mode === "any" ? results.some(Boolean) : results.every(Boolean);
|
|
168
|
+
return ok ? "pass" : "fail";
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export {
|
|
172
|
+
OP_LABELS,
|
|
173
|
+
fmtValue,
|
|
174
|
+
buildGateFlow,
|
|
175
|
+
ruleSummary,
|
|
176
|
+
evalRule,
|
|
177
|
+
evalStepMatch
|
|
178
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_EDGE_BASE_URL,
|
|
3
|
+
types_exports
|
|
4
|
+
} from "./chunk-RH6X24HG.mjs";
|
|
5
|
+
|
|
6
|
+
// src/public-report.ts
|
|
7
|
+
var PublicTicketsDisabled = class extends Error {
|
|
8
|
+
status = 403;
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "PublicTicketsDisabled";
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
async function postPublicReport(body, opts, noun) {
|
|
15
|
+
const edge = (opts.edgeBaseUrl ?? DEFAULT_EDGE_BASE_URL).replace(/\/$/, "");
|
|
16
|
+
const fetchImpl = opts.fetch ?? ((...args) => fetch(...args));
|
|
17
|
+
const res = await fetchImpl(`${edge}/cli/report`, {
|
|
18
|
+
method: "POST",
|
|
19
|
+
headers: {
|
|
20
|
+
"Content-Type": "application/json",
|
|
21
|
+
"X-SDK-Key": opts.clientKey
|
|
22
|
+
},
|
|
23
|
+
body: JSON.stringify(body)
|
|
24
|
+
});
|
|
25
|
+
if (res.status === 403) {
|
|
26
|
+
const resBody = await res.json().catch(() => ({}));
|
|
27
|
+
throw new PublicTicketsDisabled(
|
|
28
|
+
resBody.error ?? "Public reporting is not enabled for this project."
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
if (!res.ok) {
|
|
32
|
+
const resBody = await res.json().catch(() => ({}));
|
|
33
|
+
throw new Error(resBody.error ?? `${noun} failed (HTTP ${res.status}). Please try again.`);
|
|
34
|
+
}
|
|
35
|
+
const data = await res.json();
|
|
36
|
+
return { number: data.number, deduped: data.deduped === true };
|
|
37
|
+
}
|
|
38
|
+
async function submitPublicBug(input, opts) {
|
|
39
|
+
return postPublicReport(
|
|
40
|
+
{
|
|
41
|
+
title: input.title,
|
|
42
|
+
...input.error ? { error: input.error } : {},
|
|
43
|
+
...input.description ? { description: input.description } : {},
|
|
44
|
+
...input.reporterEmail ? { reporter_email: input.reporterEmail } : {},
|
|
45
|
+
...input.step ? { step: input.step } : {},
|
|
46
|
+
...input.context ? { context: input.context } : {}
|
|
47
|
+
},
|
|
48
|
+
opts,
|
|
49
|
+
"Bug report"
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
async function submitPublicFeature(input, opts) {
|
|
53
|
+
return postPublicReport(
|
|
54
|
+
{
|
|
55
|
+
type: "feature",
|
|
56
|
+
title: input.title,
|
|
57
|
+
...input.description ? { description: input.description } : {},
|
|
58
|
+
...input.useCase ? { use_case: input.useCase } : {},
|
|
59
|
+
...input.reporterEmail ? { reporter_email: input.reporterEmail } : {},
|
|
60
|
+
...input.step ? { step: input.step } : {},
|
|
61
|
+
...input.context ? { context: input.context } : {}
|
|
62
|
+
},
|
|
63
|
+
opts,
|
|
64
|
+
"Feature request"
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export {
|
|
69
|
+
PublicTicketsDisabled,
|
|
70
|
+
submitPublicBug,
|
|
71
|
+
submitPublicFeature
|
|
72
|
+
};
|