guardrail-ship 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.
Files changed (119) hide show
  1. package/dist/index.d.ts +7 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +7 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/mock-implementation.d.ts +1 -0
  6. package/dist/mock-implementation.d.ts.map +1 -0
  7. package/dist/mock-implementation.js +2 -0
  8. package/dist/mock-implementation.js.map +1 -0
  9. package/dist/mockproof/__tests__/import-graph-scanner.test.d.ts +5 -0
  10. package/dist/mockproof/__tests__/import-graph-scanner.test.d.ts.map +1 -0
  11. package/dist/mockproof/__tests__/import-graph-scanner.test.js +92 -0
  12. package/dist/mockproof/__tests__/import-graph-scanner.test.js.map +1 -0
  13. package/dist/mockproof/import-graph-scanner.d.ts +93 -0
  14. package/dist/mockproof/import-graph-scanner.d.ts.map +1 -0
  15. package/dist/mockproof/import-graph-scanner.js +411 -0
  16. package/dist/mockproof/import-graph-scanner.js.map +1 -0
  17. package/dist/mockproof/index.d.ts +10 -0
  18. package/dist/mockproof/index.d.ts.map +1 -0
  19. package/dist/mockproof/index.js +10 -0
  20. package/dist/mockproof/index.js.map +1 -0
  21. package/dist/reality-mode/auth-enforcer.d.ts +13 -0
  22. package/dist/reality-mode/auth-enforcer.d.ts.map +1 -0
  23. package/dist/reality-mode/auth-enforcer.js +90 -0
  24. package/dist/reality-mode/auth-enforcer.js.map +1 -0
  25. package/dist/reality-mode/explorer/critical-flows.d.ts +71 -0
  26. package/dist/reality-mode/explorer/critical-flows.d.ts.map +1 -0
  27. package/dist/reality-mode/explorer/critical-flows.js +463 -0
  28. package/dist/reality-mode/explorer/critical-flows.js.map +1 -0
  29. package/dist/reality-mode/explorer/flow-parser.d.ts +52 -0
  30. package/dist/reality-mode/explorer/flow-parser.d.ts.map +1 -0
  31. package/dist/reality-mode/explorer/flow-parser.js +250 -0
  32. package/dist/reality-mode/explorer/flow-parser.js.map +1 -0
  33. package/dist/reality-mode/explorer/index.d.ts +11 -0
  34. package/dist/reality-mode/explorer/index.d.ts.map +1 -0
  35. package/dist/reality-mode/explorer/index.js +11 -0
  36. package/dist/reality-mode/explorer/index.js.map +1 -0
  37. package/dist/reality-mode/explorer/runtime-explorer.d.ts +35 -0
  38. package/dist/reality-mode/explorer/runtime-explorer.d.ts.map +1 -0
  39. package/dist/reality-mode/explorer/runtime-explorer.js +688 -0
  40. package/dist/reality-mode/explorer/runtime-explorer.js.map +1 -0
  41. package/dist/reality-mode/explorer/surface-discovery.d.ts +60 -0
  42. package/dist/reality-mode/explorer/surface-discovery.d.ts.map +1 -0
  43. package/dist/reality-mode/explorer/surface-discovery.js +357 -0
  44. package/dist/reality-mode/explorer/surface-discovery.js.map +1 -0
  45. package/dist/reality-mode/explorer/types.d.ts +275 -0
  46. package/dist/reality-mode/explorer/types.d.ts.map +1 -0
  47. package/dist/reality-mode/explorer/types.js +8 -0
  48. package/dist/reality-mode/explorer/types.js.map +1 -0
  49. package/dist/reality-mode/fake-success-detector.d.ts +10 -0
  50. package/dist/reality-mode/fake-success-detector.d.ts.map +1 -0
  51. package/dist/reality-mode/fake-success-detector.js +76 -0
  52. package/dist/reality-mode/fake-success-detector.js.map +1 -0
  53. package/dist/reality-mode/index.d.ts +14 -0
  54. package/dist/reality-mode/index.d.ts.map +1 -0
  55. package/dist/reality-mode/index.js +14 -0
  56. package/dist/reality-mode/index.js.map +1 -0
  57. package/dist/reality-mode/reality-scanner.d.ts +48 -0
  58. package/dist/reality-mode/reality-scanner.d.ts.map +1 -0
  59. package/dist/reality-mode/reality-scanner.js +516 -0
  60. package/dist/reality-mode/reality-scanner.js.map +1 -0
  61. package/dist/reality-mode/report-generator.d.ts +11 -0
  62. package/dist/reality-mode/report-generator.d.ts.map +1 -0
  63. package/dist/reality-mode/report-generator.js +233 -0
  64. package/dist/reality-mode/report-generator.js.map +1 -0
  65. package/dist/reality-mode/traffic-classifier.d.ts +14 -0
  66. package/dist/reality-mode/traffic-classifier.d.ts.map +1 -0
  67. package/dist/reality-mode/traffic-classifier.js +131 -0
  68. package/dist/reality-mode/traffic-classifier.js.map +1 -0
  69. package/dist/reality-mode/types.d.ts +90 -0
  70. package/dist/reality-mode/types.d.ts.map +1 -0
  71. package/dist/reality-mode/types.js +2 -0
  72. package/dist/reality-mode/types.js.map +1 -0
  73. package/dist/ship-badge/__tests__/ship-badge-generator.test.d.ts +5 -0
  74. package/dist/ship-badge/__tests__/ship-badge-generator.test.d.ts.map +1 -0
  75. package/dist/ship-badge/__tests__/ship-badge-generator.test.js +146 -0
  76. package/dist/ship-badge/__tests__/ship-badge-generator.test.js.map +1 -0
  77. package/dist/ship-badge/index.d.ts +9 -0
  78. package/dist/ship-badge/index.d.ts.map +1 -0
  79. package/dist/ship-badge/index.js +9 -0
  80. package/dist/ship-badge/index.js.map +1 -0
  81. package/dist/ship-badge/ship-badge-generator.d.ts +136 -0
  82. package/dist/ship-badge/ship-badge-generator.d.ts.map +1 -0
  83. package/dist/ship-badge/ship-badge-generator.js +681 -0
  84. package/dist/ship-badge/ship-badge-generator.js.map +1 -0
  85. package/package.json +20 -0
  86. package/src/index.ts +7 -0
  87. package/src/mock-implementation.ts +0 -0
  88. package/src/mockproof/__tests__/import-graph-scanner.test.ts +115 -0
  89. package/src/mockproof/import-graph-scanner.d.ts +93 -0
  90. package/src/mockproof/import-graph-scanner.d.ts.map +1 -0
  91. package/src/mockproof/import-graph-scanner.js +482 -0
  92. package/src/mockproof/import-graph-scanner.ts +540 -0
  93. package/src/mockproof/index.ts +18 -0
  94. package/src/reality-mode/auth-enforcer.ts +97 -0
  95. package/src/reality-mode/explorer/critical-flows.ts +504 -0
  96. package/src/reality-mode/explorer/flow-parser.ts +293 -0
  97. package/src/reality-mode/explorer/index.ts +22 -0
  98. package/src/reality-mode/explorer/runtime-explorer.ts +715 -0
  99. package/src/reality-mode/explorer/surface-discovery.ts +498 -0
  100. package/src/reality-mode/explorer/templates/example-flows/auth-flow.yaml +41 -0
  101. package/src/reality-mode/explorer/templates/example-flows/checkout-flow.yaml +66 -0
  102. package/src/reality-mode/explorer/templates/example-flows/contact-form.yaml +43 -0
  103. package/src/reality-mode/explorer/templates/github-action.yml +132 -0
  104. package/src/reality-mode/explorer/types.ts +356 -0
  105. package/src/reality-mode/fake-success-detector.ts +89 -0
  106. package/src/reality-mode/index.ts +19 -0
  107. package/src/reality-mode/reality-scanner.d.ts +123 -0
  108. package/src/reality-mode/reality-scanner.d.ts.map +1 -0
  109. package/src/reality-mode/reality-scanner.js +526 -0
  110. package/src/reality-mode/reality-scanner.ts +576 -0
  111. package/src/reality-mode/report-generator.ts +253 -0
  112. package/src/reality-mode/traffic-classifier.ts +169 -0
  113. package/src/reality-mode/types.ts +95 -0
  114. package/src/ship-badge/__tests__/ship-badge-generator.test.ts +162 -0
  115. package/src/ship-badge/index.ts +16 -0
  116. package/src/ship-badge/ship-badge-generator.d.ts +136 -0
  117. package/src/ship-badge/ship-badge-generator.d.ts.map +1 -0
  118. package/src/ship-badge/ship-badge-generator.js +779 -0
  119. package/src/ship-badge/ship-badge-generator.ts +873 -0
@@ -0,0 +1,293 @@
1
+ /**
2
+ * Flow Pack YAML Parser
3
+ *
4
+ * Parses custom flow definitions from YAML files so users can
5
+ * define their own critical paths to test.
6
+ *
7
+ * Example YAML:
8
+ * ```yaml
9
+ * id: custom-checkout
10
+ * name: Custom Checkout Flow
11
+ * description: Tests our specific checkout process
12
+ * steps:
13
+ * - action: navigate
14
+ * target: /products
15
+ * - action: click
16
+ * target: button:has-text("Add to Cart")
17
+ * - action: navigate
18
+ * target: /checkout
19
+ * - action: fill
20
+ * target: "#email"
21
+ * value: "{{email}}"
22
+ * assertions:
23
+ * - type: url-contains
24
+ * value: /confirmation
25
+ * critical: true
26
+ * ```
27
+ */
28
+
29
+ import type { CriticalFlow, FlowStep, FlowAssertion } from "./types";
30
+
31
+ interface RawFlowYAML {
32
+ id: string;
33
+ name: string;
34
+ description?: string;
35
+ required?: boolean;
36
+ steps: Array<{
37
+ action: string;
38
+ target?: string;
39
+ value?: string;
40
+ timeout?: number;
41
+ }>;
42
+ assertions?: Array<{
43
+ type: string;
44
+ value: string;
45
+ critical?: boolean;
46
+ }>;
47
+ }
48
+
49
+ /**
50
+ * Parse a YAML flow definition into a CriticalFlow object
51
+ */
52
+ export function parseFlowYAML(yaml: string): CriticalFlow {
53
+ // Simple YAML parser for flow definitions
54
+ // In production, would use a proper YAML library
55
+ const lines = yaml.split("\n");
56
+ const flow: Partial<RawFlowYAML> = {
57
+ steps: [],
58
+ assertions: [],
59
+ };
60
+
61
+ let currentSection: "root" | "steps" | "assertions" = "root";
62
+ let currentItem: Record<string, any> | null = null;
63
+
64
+ for (const line of lines) {
65
+ const trimmed = line.trim();
66
+ if (!trimmed || trimmed.startsWith("#")) continue;
67
+
68
+ // Detect section changes
69
+ if (trimmed === "steps:") {
70
+ currentSection = "steps";
71
+ continue;
72
+ }
73
+ if (trimmed === "assertions:") {
74
+ currentSection = "assertions";
75
+ continue;
76
+ }
77
+
78
+ // Handle list items
79
+ if (trimmed.startsWith("- ")) {
80
+ if (currentItem && currentSection === "steps") {
81
+ flow.steps!.push(currentItem as any);
82
+ } else if (currentItem && currentSection === "assertions") {
83
+ flow.assertions!.push(currentItem as any);
84
+ }
85
+
86
+ currentItem = {};
87
+ const content = trimmed.slice(2);
88
+ if (content.includes(":")) {
89
+ const [keyPart, ...valueParts] = content.split(":");
90
+ const key = keyPart?.trim();
91
+ const value = valueParts
92
+ .join(":")
93
+ .trim()
94
+ .replace(/^["']|["']$/g, "");
95
+ if (key) currentItem[key] = parseValue(value);
96
+ }
97
+ continue;
98
+ }
99
+
100
+ // Handle properties
101
+ if (trimmed.includes(":")) {
102
+ const [keyPart, ...valueParts] = trimmed.split(":");
103
+ const key = keyPart?.trim();
104
+ const value = valueParts
105
+ .join(":")
106
+ .trim()
107
+ .replace(/^["']|["']$/g, "");
108
+
109
+ if (key && currentSection === "root") {
110
+ (flow as any)[key] = parseValue(value);
111
+ } else if (key && currentItem) {
112
+ currentItem[key] = parseValue(value);
113
+ }
114
+ }
115
+ }
116
+
117
+ // Don't forget the last item
118
+ if (currentItem) {
119
+ if (currentSection === "steps") {
120
+ flow.steps!.push(currentItem as any);
121
+ } else if (currentSection === "assertions") {
122
+ flow.assertions!.push(currentItem as any);
123
+ }
124
+ }
125
+
126
+ return convertToFlow(flow as RawFlowYAML);
127
+ }
128
+
129
+ /**
130
+ * Parse a value, handling booleans and numbers
131
+ */
132
+ function parseValue(value: string): string | number | boolean {
133
+ if (value === "true") return true;
134
+ if (value === "false") return false;
135
+ if (/^\d+$/.test(value)) return parseInt(value, 10);
136
+ return value;
137
+ }
138
+
139
+ /**
140
+ * Convert raw YAML to CriticalFlow
141
+ */
142
+ function convertToFlow(raw: RawFlowYAML): CriticalFlow {
143
+ return {
144
+ id: raw.id || "custom-flow",
145
+ name: raw.name || "Custom Flow",
146
+ description: raw.description || "",
147
+ required: raw.required ?? false,
148
+ steps: raw.steps.map(
149
+ (s): FlowStep => ({
150
+ action: s.action as FlowStep["action"],
151
+ target: s.target,
152
+ value: s.value,
153
+ timeout: s.timeout,
154
+ }),
155
+ ),
156
+ assertions: (raw.assertions || []).map(
157
+ (a): FlowAssertion => ({
158
+ type: a.type as FlowAssertion["type"],
159
+ value: a.value,
160
+ critical: a.critical ?? true,
161
+ }),
162
+ ),
163
+ };
164
+ }
165
+
166
+ /**
167
+ * Load flow from file path
168
+ */
169
+ export async function loadFlowFromFile(
170
+ filePath: string,
171
+ ): Promise<CriticalFlow> {
172
+ const fs = await import("fs").then((m) => m.promises);
173
+ const content = await fs.readFile(filePath, "utf-8");
174
+ return parseFlowYAML(content);
175
+ }
176
+
177
+ /**
178
+ * Load all flows from a directory
179
+ */
180
+ export async function loadFlowsFromDirectory(
181
+ dirPath: string,
182
+ ): Promise<CriticalFlow[]> {
183
+ const fs = await import("fs").then((m) => m.promises);
184
+ const path = await import("path");
185
+
186
+ const flows: CriticalFlow[] = [];
187
+
188
+ try {
189
+ const files = await fs.readdir(dirPath);
190
+
191
+ for (const file of files) {
192
+ if (file.endsWith(".yaml") || file.endsWith(".yml")) {
193
+ const filePath = path.join(dirPath, file);
194
+ const flow = await loadFlowFromFile(filePath);
195
+ flows.push(flow);
196
+ }
197
+ }
198
+ } catch (error) {
199
+ // Directory doesn't exist or can't be read
200
+ }
201
+
202
+ return flows;
203
+ }
204
+
205
+ /**
206
+ * Validate a flow definition
207
+ */
208
+ export function validateFlow(flow: CriticalFlow): {
209
+ valid: boolean;
210
+ errors: string[];
211
+ } {
212
+ const errors: string[] = [];
213
+
214
+ if (!flow.id) errors.push("Flow must have an id");
215
+ if (!flow.name) errors.push("Flow must have a name");
216
+ if (!flow.steps || flow.steps.length === 0)
217
+ errors.push("Flow must have at least one step");
218
+
219
+ const validActions = ["navigate", "click", "fill", "wait", "assert"];
220
+ for (const step of flow.steps || []) {
221
+ if (!validActions.includes(step.action)) {
222
+ errors.push(`Invalid action: ${step.action}`);
223
+ }
224
+ if (step.action !== "wait" && !step.target) {
225
+ errors.push(`Step "${step.action}" requires a target`);
226
+ }
227
+ if (step.action === "fill" && !step.value) {
228
+ errors.push("Fill action requires a value");
229
+ }
230
+ }
231
+
232
+ const validAssertionTypes = [
233
+ "url-contains",
234
+ "element-visible",
235
+ "element-hidden",
236
+ "cookie-exists",
237
+ "localstorage-has",
238
+ "network-success",
239
+ "no-errors",
240
+ ];
241
+ for (const assertion of flow.assertions || []) {
242
+ if (!validAssertionTypes.includes(assertion.type)) {
243
+ errors.push(`Invalid assertion type: ${assertion.type}`);
244
+ }
245
+ }
246
+
247
+ return { valid: errors.length === 0, errors };
248
+ }
249
+
250
+ /**
251
+ * Generate example flow YAML
252
+ */
253
+ export function generateExampleFlowYAML(): string {
254
+ return `# Example Critical Flow Definition
255
+ # Save this as .guardrail/flows/my-flow.yaml
256
+
257
+ id: my-custom-flow
258
+ name: My Custom Flow
259
+ description: Tests my specific user journey
260
+ required: false
261
+
262
+ steps:
263
+ - action: navigate
264
+ target: /my-page
265
+
266
+ - action: fill
267
+ target: input[name="email"]
268
+ value: "{{email}}"
269
+
270
+ - action: fill
271
+ target: input[name="password"]
272
+ value: "{{password}}"
273
+
274
+ - action: click
275
+ target: button[type="submit"]
276
+
277
+ - action: wait
278
+ timeout: 3000
279
+
280
+ assertions:
281
+ - type: url-contains
282
+ value: /success
283
+ critical: true
284
+
285
+ - type: element-visible
286
+ value: .welcome-message
287
+ critical: false
288
+
289
+ - type: no-errors
290
+ value: ""
291
+ critical: true
292
+ `;
293
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Reality Explorer - Main Export
3
+ *
4
+ * The "legit" Reality Mode that actually tests everything in your app.
5
+ */
6
+
7
+ export * from "./types";
8
+ export { SurfaceDiscovery } from "./surface-discovery";
9
+ export { RuntimeExplorer, createDefaultConfig } from "./runtime-explorer";
10
+ export {
11
+ FLOW_PACKS,
12
+ getAllFlows,
13
+ getFlowPack,
14
+ generateFlowTest,
15
+ } from "./critical-flows";
16
+ export {
17
+ parseFlowYAML,
18
+ loadFlowFromFile,
19
+ loadFlowsFromDirectory,
20
+ validateFlow,
21
+ generateExampleFlowYAML,
22
+ } from "./flow-parser";