@pome-sh/checks 0.1.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/CHANGELOG.md +17 -0
- package/README.md +88 -0
- package/dist/_types/sdk/check-discrimination.d.ts +7 -0
- package/dist/_types/sdk/check-state-path.d.ts +71 -0
- package/dist/_types/sdk/checks.d.ts +93 -0
- package/dist/_types/sdk/db.d.ts +44 -0
- package/dist/_types/sdk/failure-injection-rules.d.ts +32 -0
- package/dist/_types/twin-github/src/check-kind.d.ts +3 -0
- package/dist/_types/twin-github/src/check-state.d.ts +77 -0
- package/dist/_types/twin-github/src/checks.d.ts +50 -0
- package/dist/_types/twin-github/src/seed.d.ts +92 -0
- package/dist/_types/twin-github/src/types.d.ts +244 -0
- package/dist/_types/twin-gmail/src/check-kind.d.ts +3 -0
- package/dist/_types/twin-gmail/src/check-state.d.ts +138 -0
- package/dist/_types/twin-gmail/src/checks.d.ts +19 -0
- package/dist/_types/twin-gmail/src/faults.d.ts +21 -0
- package/dist/_types/twin-gmail/src/seed.d.ts +247 -0
- package/dist/_types/twin-gmail/src/types.d.ts +150 -0
- package/dist/_types/twin-linear/src/check-kind.d.ts +3 -0
- package/dist/_types/twin-linear/src/check-state.d.ts +123 -0
- package/dist/_types/twin-linear/src/checks.d.ts +29 -0
- package/dist/_types/twin-linear/src/seed.d.ts +166 -0
- package/dist/_types/twin-linear/src/types.d.ts +366 -0
- package/dist/_types/twin-slack/src/check-kind.d.ts +3 -0
- package/dist/_types/twin-slack/src/check-state.d.ts +107 -0
- package/dist/_types/twin-slack/src/checks.d.ts +14 -0
- package/dist/_types/twin-slack/src/seed.d.ts +62 -0
- package/dist/_types/twin-slack/src/types.d.ts +182 -0
- package/dist/_types/twin-stripe/src/check-kind.d.ts +3 -0
- package/dist/_types/twin-stripe/src/check-state.d.ts +107 -0
- package/dist/_types/twin-stripe/src/checks.d.ts +22 -0
- package/dist/_types/twin-stripe/src/seed.d.ts +213 -0
- package/dist/_types/twin-stripe/src/types.d.ts +252 -0
- package/dist/_types/wire/index.d.ts +23 -0
- package/dist/_types/wire/otel/event-schema.d.ts +424 -0
- package/dist/_types/wire/otel/index.d.ts +25 -0
- package/dist/_types/wire/otel/legacy-shim.d.ts +105 -0
- package/dist/_types/wire/otel/map-span.d.ts +64 -0
- package/dist/_types/wire/otel/nano.d.ts +26 -0
- package/dist/_types/wire/otel/project.d.ts +42 -0
- package/dist/_types/wire/otel/semconv.d.ts +57 -0
- package/dist/_types/wire/otel/span-event.d.ts +198 -0
- package/dist/_types/wire/recorder-events.d.ts +526 -0
- package/dist/_types/wire/redaction.d.ts +2 -0
- package/dist/chunk-4WXX5VPA.js +238 -0
- package/dist/chunk-5SJ4PVO5.js +981 -0
- package/dist/chunk-GYFGMULG.js +1138 -0
- package/dist/chunk-IVENH4KX.js +814 -0
- package/dist/chunk-NORTPYDQ.js +1015 -0
- package/dist/chunk-SJ6SVRAA.js +432 -0
- package/dist/chunk-ZXE6LAM3.js +1 -0
- package/dist/dsl.d.ts +11 -0
- package/dist/dsl.js +2 -0
- package/dist/github.d.ts +2 -0
- package/dist/github.js +2 -0
- package/dist/gmail.d.ts +2 -0
- package/dist/gmail.js +2 -0
- package/dist/index.d.ts +163 -0
- package/dist/index.js +24 -0
- package/dist/linear.d.ts +2 -0
- package/dist/linear.js +2 -0
- package/dist/slack.d.ts +2 -0
- package/dist/slack.js +2 -0
- package/dist/stripe.d.ts +2 -0
- package/dist/stripe.js +2 -0
- package/package.json +93 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
// ../sdk/dist/check-discrimination.js
|
|
4
|
+
function isRealPass(outcome) {
|
|
5
|
+
return outcome.passed === true && (outcome.status === void 0 || outcome.status === "passed");
|
|
6
|
+
}
|
|
7
|
+
function isRealFail(outcome) {
|
|
8
|
+
return outcome.passed === false && (outcome.status === void 0 || outcome.status === "failed");
|
|
9
|
+
}
|
|
10
|
+
var describeOutcome = (outcome) => `passed=${outcome.passed} status=${outcome.status ?? "-"} reason=${JSON.stringify(outcome.reason)}`;
|
|
11
|
+
function reasonOnEmptyWorld(def, args) {
|
|
12
|
+
try {
|
|
13
|
+
return def.evaluate(args, { seed: {}, final: {}, tape: [] }).reason;
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function probeDiscrimination(def, args) {
|
|
19
|
+
const worlds = def.discriminatingWorlds(args);
|
|
20
|
+
if (worlds === null)
|
|
21
|
+
return { kind: "declined" };
|
|
22
|
+
const passing = def.evaluate(args, worlds.passing);
|
|
23
|
+
if (!isRealPass(passing)) {
|
|
24
|
+
return {
|
|
25
|
+
kind: "broken",
|
|
26
|
+
arm: "passing",
|
|
27
|
+
detail: `passing world did not pass \u2014 ${describeOutcome(passing)}`
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const failing = def.evaluate(args, worlds.failing);
|
|
31
|
+
if (!isRealFail(failing)) {
|
|
32
|
+
return {
|
|
33
|
+
kind: "broken",
|
|
34
|
+
arm: "failing",
|
|
35
|
+
detail: `failing world did not fail \u2014 ${describeOutcome(failing)}`
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const degenerate = reasonOnEmptyWorld(def, args);
|
|
39
|
+
if (degenerate !== null && failing.reason === degenerate) {
|
|
40
|
+
return {
|
|
41
|
+
kind: "broken",
|
|
42
|
+
arm: "degenerate",
|
|
43
|
+
detail: `the failing world's reason is the one an EMPTY world produces (${JSON.stringify(failing.reason)}), so it fails through its selector rather than its assertion`
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return { kind: "discriminates" };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ../sdk/dist/check-state-path.js
|
|
50
|
+
function escapeSegment(segment) {
|
|
51
|
+
return segment.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
52
|
+
}
|
|
53
|
+
function unescapeSegment(segment) {
|
|
54
|
+
return segment.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
55
|
+
}
|
|
56
|
+
function statePath(...segments) {
|
|
57
|
+
return segments.map((segment) => `/${escapeSegment(String(segment))}`).join("");
|
|
58
|
+
}
|
|
59
|
+
function childStatePath(base, ...segments) {
|
|
60
|
+
return `${base}${statePath(...segments)}`;
|
|
61
|
+
}
|
|
62
|
+
function resolveStatePath(tree, pointer) {
|
|
63
|
+
if (pointer === "")
|
|
64
|
+
return { value: tree };
|
|
65
|
+
if (!pointer.startsWith("/"))
|
|
66
|
+
return null;
|
|
67
|
+
let cursor = tree;
|
|
68
|
+
for (const raw of pointer.slice(1).split("/")) {
|
|
69
|
+
const segment = unescapeSegment(raw);
|
|
70
|
+
if (Array.isArray(cursor)) {
|
|
71
|
+
if (!/^(?:0|[1-9][0-9]*)$/.test(segment))
|
|
72
|
+
return null;
|
|
73
|
+
const index = Number(segment);
|
|
74
|
+
if (index >= cursor.length)
|
|
75
|
+
return null;
|
|
76
|
+
cursor = cursor[index];
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (cursor === null || typeof cursor !== "object")
|
|
80
|
+
return null;
|
|
81
|
+
const record = cursor;
|
|
82
|
+
if (!Object.prototype.hasOwnProperty.call(record, segment))
|
|
83
|
+
return null;
|
|
84
|
+
cursor = record[segment];
|
|
85
|
+
}
|
|
86
|
+
return { value: cursor };
|
|
87
|
+
}
|
|
88
|
+
function probeArm(def, args, arm, world) {
|
|
89
|
+
const outcome = def.evaluate(args, world);
|
|
90
|
+
const paths = outcome.evidenceStatePaths;
|
|
91
|
+
if (paths === void 0)
|
|
92
|
+
return { kind: "uncited", arm };
|
|
93
|
+
if (!Array.isArray(paths) || paths.length === 0) {
|
|
94
|
+
return {
|
|
95
|
+
kind: "malformed",
|
|
96
|
+
arm,
|
|
97
|
+
detail: `evidenceStatePaths must be omitted when empty, not sent as ${JSON.stringify(paths)}`
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
for (const pointer of paths) {
|
|
101
|
+
if (typeof pointer !== "string" || pointer === "") {
|
|
102
|
+
return {
|
|
103
|
+
kind: "malformed",
|
|
104
|
+
arm,
|
|
105
|
+
detail: `${JSON.stringify(pointer)} is not a pointer \u2014 the whole-document pointer "" is not a citation`
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
if (resolveStatePath(world.final, pointer) === null) {
|
|
109
|
+
return { kind: "unresolvable", arm, pointer };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return { kind: "cites" };
|
|
113
|
+
}
|
|
114
|
+
function probeStateCitation(def, args) {
|
|
115
|
+
const worlds = def.discriminatingWorlds(args);
|
|
116
|
+
if (worlds === null)
|
|
117
|
+
return { kind: "declined" };
|
|
118
|
+
const passing = probeArm(def, args, "passing", worlds.passing);
|
|
119
|
+
if (passing.kind !== "cites")
|
|
120
|
+
return passing;
|
|
121
|
+
return probeArm(def, args, "failing", worlds.failing);
|
|
122
|
+
}
|
|
123
|
+
var VACUITY_SENTINEL = "pome-vacuity-never";
|
|
124
|
+
var VACUITY_SENTINEL_SNAKE = "pome_vacuity_never";
|
|
125
|
+
var VACUITY_SENTINEL_NUMBER = 987654321;
|
|
126
|
+
var repoRef = {
|
|
127
|
+
name: "repo",
|
|
128
|
+
pattern: "[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+",
|
|
129
|
+
example: "acme/api",
|
|
130
|
+
render: (value) => value,
|
|
131
|
+
parse: (raw) => raw
|
|
132
|
+
};
|
|
133
|
+
function oneOf(name, values, example) {
|
|
134
|
+
if (values.length === 0)
|
|
135
|
+
throw new Error(`param type ${name}: oneOf needs at least one value`);
|
|
136
|
+
return {
|
|
137
|
+
name,
|
|
138
|
+
pattern: `(?:${values.map(escapeLiteral).join("|")})`,
|
|
139
|
+
example: example ?? values[0],
|
|
140
|
+
render: (value) => value,
|
|
141
|
+
parse: (raw) => raw
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function captureGroupCount(source) {
|
|
145
|
+
return new RegExp(`${source}|`).exec("").length - 1;
|
|
146
|
+
}
|
|
147
|
+
var SLOT_RE = /\{([a-z][a-z0-9_]*)\}/g;
|
|
148
|
+
function templateSlots(template) {
|
|
149
|
+
const literals = [];
|
|
150
|
+
const params = [];
|
|
151
|
+
let cursor = 0;
|
|
152
|
+
for (const match of template.matchAll(SLOT_RE)) {
|
|
153
|
+
literals.push(template.slice(cursor, match.index));
|
|
154
|
+
params.push(match[1]);
|
|
155
|
+
cursor = match.index + match[0].length;
|
|
156
|
+
}
|
|
157
|
+
literals.push(template.slice(cursor));
|
|
158
|
+
return { literals, params };
|
|
159
|
+
}
|
|
160
|
+
function escapeLiteral(literal) {
|
|
161
|
+
return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
162
|
+
}
|
|
163
|
+
function defineCheck(def) {
|
|
164
|
+
const { params } = templateSlots(def.template);
|
|
165
|
+
const seen = /* @__PURE__ */ new Set();
|
|
166
|
+
for (const param of params) {
|
|
167
|
+
if (seen.has(param)) {
|
|
168
|
+
throw new Error(`check ${def.id}: duplicate template slot {${param}}`);
|
|
169
|
+
}
|
|
170
|
+
seen.add(param);
|
|
171
|
+
if (!def.params[param]) {
|
|
172
|
+
throw new Error(`check ${def.id}: template slot {${param}} has no declared param type`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
for (const declared of Object.keys(def.params)) {
|
|
176
|
+
if (!seen.has(declared)) {
|
|
177
|
+
throw new Error(`check ${def.id}: declared param \`${declared}\` is not used by the template`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
for (const [name, type] of Object.entries(def.params)) {
|
|
181
|
+
const paramType = type;
|
|
182
|
+
if (!new RegExp(`^${paramType.pattern}$`).test(paramType.example)) {
|
|
183
|
+
throw new Error(`check ${def.id}: param \`${name}\` example ${JSON.stringify(paramType.example)} does not match its own pattern /${paramType.pattern}/`);
|
|
184
|
+
}
|
|
185
|
+
const groups = captureGroupCount(paramType.pattern);
|
|
186
|
+
if (groups > 0) {
|
|
187
|
+
throw new Error(`check ${def.id}: param \`${name}\` pattern /${paramType.pattern}/ opens ${groups} capture group(s). Slot patterns must be group-free \u2014 use (?:\u2026) \u2014 or every slot after this one binds the wrong value.`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return def;
|
|
191
|
+
}
|
|
192
|
+
function renderCheck(def, args) {
|
|
193
|
+
const { literals, params } = templateSlots(def.template);
|
|
194
|
+
let rendered = literals[0];
|
|
195
|
+
params.forEach((param, index) => {
|
|
196
|
+
const type = def.params[param];
|
|
197
|
+
rendered += type.render(args[param]) + literals[index + 1];
|
|
198
|
+
});
|
|
199
|
+
return rendered;
|
|
200
|
+
}
|
|
201
|
+
function buildPattern(template, slotSource) {
|
|
202
|
+
const { literals, params } = templateSlots(template);
|
|
203
|
+
let source = `^${escapeLiteral(literals[0])}`;
|
|
204
|
+
params.forEach((_param, index) => {
|
|
205
|
+
source += `(${slotSource(index)})${escapeLiteral(literals[index + 1])}`;
|
|
206
|
+
});
|
|
207
|
+
return new RegExp(`${source}$`);
|
|
208
|
+
}
|
|
209
|
+
function checkPattern(def) {
|
|
210
|
+
const { params } = templateSlots(def.template);
|
|
211
|
+
return buildPattern(def.template, (index) => def.params[params[index]].pattern);
|
|
212
|
+
}
|
|
213
|
+
function checksDigest(defs) {
|
|
214
|
+
const rows = defs.map((def) => ({
|
|
215
|
+
id: def.id,
|
|
216
|
+
substrate: def.substrate,
|
|
217
|
+
// The compiled source, not the template: this also catches a change in
|
|
218
|
+
// `buildPattern` itself, which comparing templates would miss.
|
|
219
|
+
pattern: checkPattern(def).source
|
|
220
|
+
})).sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
221
|
+
return `sha256:${createHash("sha256").update(JSON.stringify(rows), "utf8").digest("hex")}`;
|
|
222
|
+
}
|
|
223
|
+
function checkNearMissPattern(def) {
|
|
224
|
+
return buildPattern(def.template, () => ".+?");
|
|
225
|
+
}
|
|
226
|
+
function parseCheck(def, text) {
|
|
227
|
+
const matched = text.trim().match(checkPattern(def));
|
|
228
|
+
if (!matched)
|
|
229
|
+
return null;
|
|
230
|
+
const { params } = templateSlots(def.template);
|
|
231
|
+
const args = {};
|
|
232
|
+
params.forEach((param, index) => {
|
|
233
|
+
args[param] = def.params[param].parse(matched[index + 1]);
|
|
234
|
+
});
|
|
235
|
+
return args;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export { VACUITY_SENTINEL, VACUITY_SENTINEL_NUMBER, VACUITY_SENTINEL_SNAKE, checkNearMissPattern, checkPattern, checksDigest, childStatePath, defineCheck, oneOf, parseCheck, probeDiscrimination, probeStateCitation, renderCheck, repoRef, resolveStatePath, statePath, templateSlots };
|