@a-company/paradigm 1.5.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 (114) hide show
  1. package/README.md +142 -0
  2. package/dist/accept-orchestration-CWZNCGZX.js +188 -0
  3. package/dist/agents-suggest-35LIQKDH.js +83 -0
  4. package/dist/aggregate-W7Q6VIM2.js +88 -0
  5. package/dist/auto-IU7VN55K.js +470 -0
  6. package/dist/beacon-B47XSTL7.js +251 -0
  7. package/dist/chunk-2M6OSOIG.js +1302 -0
  8. package/dist/chunk-4NCFWYGG.js +110 -0
  9. package/dist/chunk-5C4SGQKH.js +705 -0
  10. package/dist/chunk-5GOA7WYD.js +1095 -0
  11. package/dist/chunk-5JGJACDU.js +37 -0
  12. package/dist/chunk-6QC3YGB6.js +114 -0
  13. package/dist/chunk-753RICFF.js +325 -0
  14. package/dist/chunk-AD2LSCHB.js +1595 -0
  15. package/dist/chunk-CHSHON3O.js +669 -0
  16. package/dist/chunk-ELLR7WP6.js +3175 -0
  17. package/dist/chunk-ILOWBJRC.js +12 -0
  18. package/dist/chunk-IRKUEJVW.js +405 -0
  19. package/dist/chunk-MC7XC7XQ.js +533 -0
  20. package/dist/chunk-MO4EEYFW.js +38 -0
  21. package/dist/chunk-MQWH7PFI.js +13366 -0
  22. package/dist/chunk-N6PJAPDE.js +364 -0
  23. package/dist/chunk-PBHIFAL4.js +259 -0
  24. package/dist/chunk-PMXRGPRQ.js +305 -0
  25. package/dist/chunk-PW2EXJQT.js +689 -0
  26. package/dist/chunk-TAP5N3HH.js +245 -0
  27. package/dist/chunk-THFVK5AE.js +148 -0
  28. package/dist/chunk-UM54F7G5.js +1533 -0
  29. package/dist/chunk-UUZ2DMG5.js +185 -0
  30. package/dist/chunk-WS5KM7OL.js +780 -0
  31. package/dist/chunk-YDNKXH4Z.js +2316 -0
  32. package/dist/chunk-YO6DVTL7.js +99 -0
  33. package/dist/claude-SUYNN72C.js +362 -0
  34. package/dist/claude-cli-OF43XAO3.js +276 -0
  35. package/dist/claude-code-PW6SKD2M.js +126 -0
  36. package/dist/claude-code-teams-JLZ5IXB6.js +199 -0
  37. package/dist/constellation-K3CIQCHI.js +225 -0
  38. package/dist/cost-AEK6R7HK.js +174 -0
  39. package/dist/cost-KYXIQ62X.js +93 -0
  40. package/dist/cursor-cli-IHJMPRCW.js +269 -0
  41. package/dist/cursorrules-KI5QWHIX.js +84 -0
  42. package/dist/diff-AJJ5H6HV.js +125 -0
  43. package/dist/dist-7MPIRMTZ-IOQOREMZ.js +10866 -0
  44. package/dist/dist-NHJQVVUW.js +68 -0
  45. package/dist/dist-ZEMSQV74.js +20 -0
  46. package/dist/doctor-6Y6L6HEB.js +11 -0
  47. package/dist/echo-VYZW3OTT.js +248 -0
  48. package/dist/export-R4FJ5NOH.js +38 -0
  49. package/dist/history-EVO3L6SC.js +277 -0
  50. package/dist/hooks-MBWE4ILT.js +12 -0
  51. package/dist/index.d.ts +2 -0
  52. package/dist/index.js +568 -0
  53. package/dist/lint-HXKTWRNO.js +316 -0
  54. package/dist/manual-Y3QOXWYA.js +204 -0
  55. package/dist/mcp.js +14745 -0
  56. package/dist/orchestrate-4ZH5GUQH.js +323 -0
  57. package/dist/probe-OYCP4JYG.js +151 -0
  58. package/dist/promote-Z52ZJTJU.js +181 -0
  59. package/dist/providers-4PGPZEWP.js +104 -0
  60. package/dist/remember-6VZ74B7E.js +77 -0
  61. package/dist/ripple-SBQOSTZD.js +215 -0
  62. package/dist/sentinel-LCFD56OJ.js +43 -0
  63. package/dist/server-F5ITNK6T.js +9846 -0
  64. package/dist/server-T6WIFYRQ.js +16076 -0
  65. package/dist/setup-DF4F3ICN.js +25 -0
  66. package/dist/setup-JHBPZAG7.js +296 -0
  67. package/dist/shift-HKIAP4ZN.js +226 -0
  68. package/dist/snapshot-GTVPRYZG.js +62 -0
  69. package/dist/spawn-BJRQA2NR.js +196 -0
  70. package/dist/summary-H6J6N6PJ.js +140 -0
  71. package/dist/switch-6EANJ7O6.js +232 -0
  72. package/dist/sync-BEOCW7TZ.js +11 -0
  73. package/dist/team-NWP2KJAB.js +32 -0
  74. package/dist/test-MA5TWJQV.js +934 -0
  75. package/dist/thread-JCJVRUQR.js +258 -0
  76. package/dist/triage-ETVXXFMV.js +1880 -0
  77. package/dist/tutorial-L5Q3ZDHK.js +666 -0
  78. package/dist/university-R2WDQLSI.js +40 -0
  79. package/dist/upgrade-5B3YGGC6.js +550 -0
  80. package/dist/validate-F3YHBCRZ.js +39 -0
  81. package/dist/validate-QEEY6KFS.js +64 -0
  82. package/dist/watch-4LT4O6K7.js +123 -0
  83. package/dist/watch-6IIWPWDN.js +111 -0
  84. package/dist/wisdom-LRM4FFCH.js +319 -0
  85. package/package.json +68 -0
  86. package/templates/paradigm/config.yaml +175 -0
  87. package/templates/paradigm/docs/commands.md +727 -0
  88. package/templates/paradigm/docs/decisions/000-template.md +47 -0
  89. package/templates/paradigm/docs/decisions/README.md +26 -0
  90. package/templates/paradigm/docs/error-patterns.md +215 -0
  91. package/templates/paradigm/docs/patterns.md +358 -0
  92. package/templates/paradigm/docs/queries.md +200 -0
  93. package/templates/paradigm/docs/troubleshooting.md +477 -0
  94. package/templates/paradigm/echoes.yaml +25 -0
  95. package/templates/paradigm/prompts/add-feature.md +152 -0
  96. package/templates/paradigm/prompts/add-gate.md +117 -0
  97. package/templates/paradigm/prompts/debug-auth.md +174 -0
  98. package/templates/paradigm/prompts/implement-ftux.md +722 -0
  99. package/templates/paradigm/prompts/implement-sandbox.md +651 -0
  100. package/templates/paradigm/prompts/read-docs.md +84 -0
  101. package/templates/paradigm/prompts/refactor.md +106 -0
  102. package/templates/paradigm/prompts/run-e2e-tests.md +340 -0
  103. package/templates/paradigm/prompts/trace-flow.md +202 -0
  104. package/templates/paradigm/prompts/validate-portals.md +279 -0
  105. package/templates/paradigm/specs/context-tracking.md +200 -0
  106. package/templates/paradigm/specs/context.md +461 -0
  107. package/templates/paradigm/specs/disciplines.md +413 -0
  108. package/templates/paradigm/specs/history.md +339 -0
  109. package/templates/paradigm/specs/logger.md +303 -0
  110. package/templates/paradigm/specs/navigator.md +236 -0
  111. package/templates/paradigm/specs/purpose.md +265 -0
  112. package/templates/paradigm/specs/scan.md +177 -0
  113. package/templates/paradigm/specs/symbols.md +451 -0
  114. package/templates/paradigm/specs/wisdom.md +294 -0
@@ -0,0 +1,934 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ parseGateConfig
4
+ } from "./chunk-IRKUEJVW.js";
5
+ import "./chunk-MO4EEYFW.js";
6
+
7
+ // src/commands/portal/test.ts
8
+ import * as path3 from "path";
9
+ import * as fs3 from "fs";
10
+ import chalk from "chalk";
11
+
12
+ // ../portal/sdk/dist/chunk-BRTWJEZU.js
13
+ var globalGateClient = null;
14
+ function setGateClient(client) {
15
+ globalGateClient = client;
16
+ }
17
+ function getGateClient() {
18
+ return globalGateClient;
19
+ }
20
+
21
+ // ../portal/manager/dist/index.js
22
+ import * as fs from "fs";
23
+ import * as path from "path";
24
+ import * as fs2 from "fs";
25
+ import * as path2 from "path";
26
+ async function validateGateway(gateId, testCases, client) {
27
+ const gateClient = client || getGateClient();
28
+ if (!gateClient) {
29
+ return {
30
+ passed: false,
31
+ results: [],
32
+ errors: ["No gate client configured"]
33
+ };
34
+ }
35
+ const results = [];
36
+ const errors = [];
37
+ for (const testCase of testCases) {
38
+ try {
39
+ const result = await gateClient.check(gateId, testCase.entity);
40
+ const passed = result.passed === testCase.expected;
41
+ if (testCase.expectedPrizes && result.passed) {
42
+ const triggeredPrizeIds = result.triggeredPrizes.map((p) => p.id);
43
+ const missingPrizes = testCase.expectedPrizes.filter(
44
+ (id) => !triggeredPrizeIds.includes(id)
45
+ );
46
+ if (missingPrizes.length > 0) {
47
+ errors.push(
48
+ `Test "${testCase.name}": Expected prizes not triggered: ${missingPrizes.join(", ")}`
49
+ );
50
+ }
51
+ }
52
+ results.push({
53
+ testCase,
54
+ result,
55
+ passed
56
+ });
57
+ if (!passed) {
58
+ errors.push(
59
+ `Test "${testCase.name}": Expected ${testCase.expected ? "pass" : "fail"}, got ${result.passed ? "pass" : "fail"}`
60
+ );
61
+ }
62
+ } catch (error) {
63
+ errors.push(`Test "${testCase.name}": ${error.message}`);
64
+ results.push({
65
+ testCase,
66
+ result: {
67
+ gate: { id: gateId, locks: [], prizes: [] },
68
+ passed: false,
69
+ lockResults: [],
70
+ triggeredPrizes: [],
71
+ timestamp: Date.now()
72
+ },
73
+ passed: false
74
+ });
75
+ }
76
+ }
77
+ return {
78
+ passed: errors.length === 0,
79
+ results,
80
+ errors
81
+ };
82
+ }
83
+ async function generateTests(gateConfigPath, options = {}) {
84
+ const {
85
+ outputDir = "tests/gates",
86
+ framework = "jest",
87
+ includeFlows = true
88
+ } = options;
89
+ const gateConfig = await parseGateConfig(gateConfigPath);
90
+ const generatedFiles = [];
91
+ const absOutputDir = path.resolve(outputDir);
92
+ if (!fs.existsSync(absOutputDir)) {
93
+ fs.mkdirSync(absOutputDir, { recursive: true });
94
+ }
95
+ for (const gate of gateConfig.gates) {
96
+ const testFile = generateGateTest(gate, framework);
97
+ const fileName = `${gate.id.replace(/[^a-z0-9]/gi, "-")}.test.ts`;
98
+ const filePath = path.join(absOutputDir, fileName);
99
+ fs.writeFileSync(filePath, testFile, "utf8");
100
+ generatedFiles.push(filePath);
101
+ }
102
+ if (includeFlows && gateConfig.flows.length > 0) {
103
+ const flowsDir = path.join(absOutputDir, "..", "flows");
104
+ if (!fs.existsSync(flowsDir)) {
105
+ fs.mkdirSync(flowsDir, { recursive: true });
106
+ }
107
+ for (const flow of gateConfig.flows) {
108
+ const testFile = generateFlowTest(flow, gateConfig.gates, framework);
109
+ const fileName = `${flow.id.replace(/[^a-z0-9]/gi, "-")}.test.ts`;
110
+ const filePath = path.join(flowsDir, fileName);
111
+ fs.writeFileSync(filePath, testFile, "utf8");
112
+ generatedFiles.push(filePath);
113
+ }
114
+ }
115
+ return generatedFiles;
116
+ }
117
+ function generateGateTest(gate, framework) {
118
+ const testFn = framework === "mocha" ? "it" : framework === "vitest" ? "it" : "test";
119
+ const gateId = gate.id;
120
+ const gateName = gate.description || gateId;
121
+ const lockTests = gate.locks.map((lock) => {
122
+ const lockTests2 = lock.keys.map((key, idx) => {
123
+ return ` ${testFn}('should check key: ${key.description || `Key ${idx + 1}`}', async () => {
124
+ const entity = {
125
+ // TODO: Add entity properties needed for this key
126
+ // Key expression: ${key.expression}
127
+ };
128
+
129
+ const result = await checkGateway('${gateId}', entity);
130
+ // TODO: Assert expected result
131
+ expect(result.passed).toBeDefined();
132
+ });`;
133
+ });
134
+ return ` describe('Lock: ${lock.description || lock.id}', () => {
135
+ ${lockTests2.join("\n\n")}
136
+ });`;
137
+ });
138
+ return `/**
139
+ * Generated test file for gate: ${gateName}
140
+ * Gate ID: ${gateId}
141
+ *
142
+ * TODO: Fill in entity mock data and expected results
143
+ */
144
+
145
+ import { checkGateway, validateGateway } from '@a-company/portal-manager';
146
+ import { createGate, setGateClient } from '@a-company/portal-sdk';
147
+ import { parseGateConfig } from '@a-company/portal-core';
148
+
149
+ ${framework === "vitest" ? "import { describe, it, expect, beforeAll } from 'vitest';" : framework === "mocha" ? "import { describe, it } from 'mocha';" : "import { describe, test as it, expect, beforeAll } from '@jest/globals';"}
150
+
151
+ describe('Gate: ${gateName}', () => {
152
+ beforeAll(async () => {
153
+ // Load gate configuration
154
+ const config = await parseGateConfig('./portal.yaml');
155
+ const client = createGate(config);
156
+ setGateClient(client);
157
+ });
158
+
159
+ ${lockTests.join("\n\n")}
160
+
161
+ ${testFn}('should trigger prizes when gate passes', async () => {
162
+ const entity = {
163
+ // TODO: Add entity that passes all locks
164
+ };
165
+
166
+ const result = await checkGateway('${gateId}', entity);
167
+
168
+ if (result.passed) {
169
+ expect(result.triggeredPrizes.length).toBeGreaterThan(0);
170
+ // TODO: Assert specific prizes
171
+ ${gate.prizes.map((p) => `// expect(result.triggeredPrizes).toContainEqual(expect.objectContaining({ id: '${p.id}' }));`).join("\n ")}
172
+ }
173
+ });
174
+ });
175
+ `;
176
+ }
177
+ function generateFlowTest(flow, _gates, framework) {
178
+ const testFn = framework === "mocha" ? "it" : framework === "vitest" ? "it" : "test";
179
+ const flowName = flow.description || flow.id;
180
+ const gateTests = flow.gates.map((gateId, idx) => {
181
+ return ` ${testFn}('should pass gate ${idx + 1}: ${gateId}', async () => {
182
+ const entity = {
183
+ // TODO: Add entity properties
184
+ };
185
+
186
+ const result = await checkGateway('${gateId}', entity);
187
+ expect(result.passed).toBe(true);
188
+ });`;
189
+ });
190
+ return `/**
191
+ * Generated test file for flow: ${flowName}
192
+ * Flow ID: ${flow.id}
193
+ *
194
+ * TODO: Fill in entity mock data for each gate in the flow
195
+ */
196
+
197
+ import { checkGateway } from '@a-company/portal-manager';
198
+ import { createGate, setGateClient } from '@a-company/portal-sdk';
199
+ import { parseGateConfig } from '@a-company/portal-core';
200
+
201
+ ${framework === "vitest" ? "import { describe, it, expect, beforeAll } from 'vitest';" : framework === "mocha" ? "import { describe, it } from 'mocha';" : "import { describe, test as it, expect, beforeAll } from '@jest/globals';"}
202
+
203
+ describe('Flow: ${flowName}', () => {
204
+ beforeAll(async () => {
205
+ const config = await parseGateConfig('./portal.yaml');
206
+ const client = createGate(config);
207
+ setGateClient(client);
208
+ });
209
+
210
+ ${testFn}('should complete entire flow', async () => {
211
+ const entity = {
212
+ // TODO: Add entity that passes all gates in flow
213
+ };
214
+
215
+ ${gateTests.join("\n\n")}
216
+ });
217
+ });
218
+ `;
219
+ }
220
+ async function scanComponents(options) {
221
+ const { rootDir, patterns = ["**/*.tsx", "**/*.jsx"] } = options;
222
+ const components = [];
223
+ const scannedFiles = /* @__PURE__ */ new Set();
224
+ function scanDirectory(dir) {
225
+ try {
226
+ const entries = fs2.readdirSync(dir, { withFileTypes: true });
227
+ for (const entry of entries) {
228
+ const fullPath = path2.join(dir, entry.name);
229
+ if (entry.name === "node_modules" || entry.name === ".git" || entry.name === "dist" || entry.name === "build") {
230
+ continue;
231
+ }
232
+ if (entry.isDirectory()) {
233
+ scanDirectory(fullPath);
234
+ } else if (entry.isFile()) {
235
+ const matchesPattern = patterns.some((pattern) => {
236
+ if (pattern.includes("**")) {
237
+ const ext = pattern.split(".").pop();
238
+ return fullPath.endsWith(`.${ext}`);
239
+ }
240
+ return fullPath.endsWith(pattern);
241
+ });
242
+ if (matchesPattern && !scannedFiles.has(fullPath)) {
243
+ scannedFiles.add(fullPath);
244
+ const info = analyzeComponent(fullPath);
245
+ if (info) {
246
+ components.push(info);
247
+ }
248
+ }
249
+ }
250
+ }
251
+ } catch (error) {
252
+ }
253
+ }
254
+ scanDirectory(rootDir);
255
+ return components;
256
+ }
257
+ function analyzeComponent(filePath) {
258
+ try {
259
+ const content = fs2.readFileSync(filePath, "utf8");
260
+ const componentName = path2.basename(filePath, path2.extname(filePath));
261
+ const gatePatterns = [
262
+ /@Gate\(['"]([^'"]+)['"]\)/g,
263
+ /@Gateway\(['"]([^'"]+)['"]\)/g,
264
+ /GateGuard\(['"]([^'"]+)['"]\)/g,
265
+ /checkGateway\(['"]([^'"]+)['"]/g,
266
+ /gate:\s*['"]([^'"]+)['"]/g
267
+ ];
268
+ const requiredGates = /* @__PURE__ */ new Set();
269
+ const missingChecks = [];
270
+ for (const pattern of gatePatterns) {
271
+ let match;
272
+ while ((match = pattern.exec(content)) !== null) {
273
+ const gateId = match[1];
274
+ if (gateId && gateId.startsWith("^")) {
275
+ requiredGates.add(gateId);
276
+ }
277
+ }
278
+ }
279
+ const hasGateCheck = content.includes("checkGateway") || content.includes("GateGuard") || content.includes("Gateway") || content.includes("useGate");
280
+ if (requiredGates.size > 0 && !hasGateCheck) {
281
+ missingChecks.push(...Array.from(requiredGates));
282
+ }
283
+ return {
284
+ filePath,
285
+ componentName,
286
+ requiredGates: Array.from(requiredGates),
287
+ missingChecks
288
+ };
289
+ } catch (error) {
290
+ return null;
291
+ }
292
+ }
293
+ function generateComponentReport(components) {
294
+ const lines = [];
295
+ lines.push("# Component Access Report\n");
296
+ lines.push(`Generated: ${(/* @__PURE__ */ new Date()).toISOString()}
297
+ `);
298
+ lines.push(`Total Components: ${components.length}
299
+ `);
300
+ const withMissingChecks = components.filter((c) => c.missingChecks.length > 0);
301
+ if (withMissingChecks.length > 0) {
302
+ lines.push(`
303
+ ## Components Missing Gate Checks (${withMissingChecks.length})
304
+ `);
305
+ for (const component of withMissingChecks) {
306
+ lines.push(`### ${component.componentName}`);
307
+ lines.push(`File: ${component.filePath}`);
308
+ lines.push(`Missing checks for gates: ${component.missingChecks.join(", ")}
309
+ `);
310
+ }
311
+ } else {
312
+ lines.push("\n\u2705 All components have proper gate checks!\n");
313
+ }
314
+ return lines.join("\n");
315
+ }
316
+
317
+ // ../portal/sdk/dist/index.js
318
+ function tokenize(expr) {
319
+ const tokens = [];
320
+ let i = 0;
321
+ while (i < expr.length) {
322
+ const char = expr[i];
323
+ if (/\s/.test(char)) {
324
+ i++;
325
+ continue;
326
+ }
327
+ if (char === '"' || char === "'") {
328
+ const quote = char;
329
+ let str = "";
330
+ i++;
331
+ while (i < expr.length && expr[i] !== quote) {
332
+ if (expr[i] === "\\" && i + 1 < expr.length) {
333
+ i++;
334
+ str += expr[i];
335
+ } else {
336
+ str += expr[i];
337
+ }
338
+ i++;
339
+ }
340
+ i++;
341
+ tokens.push({ type: "STRING", value: str });
342
+ continue;
343
+ }
344
+ if (/\d/.test(char)) {
345
+ let num = "";
346
+ while (i < expr.length && /[\d.]/.test(expr[i])) {
347
+ num += expr[i];
348
+ i++;
349
+ }
350
+ tokens.push({ type: "NUMBER", value: num });
351
+ continue;
352
+ }
353
+ if (/[a-zA-Z_]/.test(char)) {
354
+ let ident = "";
355
+ while (i < expr.length && /[a-zA-Z0-9_]/.test(expr[i])) {
356
+ ident += expr[i];
357
+ i++;
358
+ }
359
+ if (ident === "true" || ident === "false") {
360
+ tokens.push({ type: "BOOLEAN", value: ident });
361
+ } else if (ident === "null" || ident === "undefined") {
362
+ tokens.push({ type: "NULL", value: ident });
363
+ } else if (["and", "or", "not", "includes", "in"].includes(ident)) {
364
+ tokens.push({ type: "OPERATOR", value: ident });
365
+ } else {
366
+ tokens.push({ type: "IDENTIFIER", value: ident });
367
+ }
368
+ continue;
369
+ }
370
+ if (expr.slice(i, i + 3) === "===" || expr.slice(i, i + 3) === "!==") {
371
+ tokens.push({ type: "OPERATOR", value: expr.slice(i, i + 3) });
372
+ i += 3;
373
+ continue;
374
+ }
375
+ if (expr.slice(i, i + 2) === "==" || expr.slice(i, i + 2) === "!=" || expr.slice(i, i + 2) === "<=" || expr.slice(i, i + 2) === ">=" || expr.slice(i, i + 2) === "&&" || expr.slice(i, i + 2) === "||") {
376
+ tokens.push({ type: "OPERATOR", value: expr.slice(i, i + 2) });
377
+ i += 2;
378
+ continue;
379
+ }
380
+ if ("<>=!".includes(char)) {
381
+ tokens.push({ type: "OPERATOR", value: char });
382
+ i++;
383
+ continue;
384
+ }
385
+ if (char === "(") {
386
+ tokens.push({ type: "LPAREN", value: char });
387
+ i++;
388
+ continue;
389
+ }
390
+ if (char === ")") {
391
+ tokens.push({ type: "RPAREN", value: char });
392
+ i++;
393
+ continue;
394
+ }
395
+ if (char === ".") {
396
+ tokens.push({ type: "DOT", value: char });
397
+ i++;
398
+ continue;
399
+ }
400
+ if (char === ",") {
401
+ tokens.push({ type: "COMMA", value: char });
402
+ i++;
403
+ continue;
404
+ }
405
+ i++;
406
+ }
407
+ tokens.push({ type: "EOF", value: "" });
408
+ return tokens;
409
+ }
410
+ var ExpressionEvaluator = class {
411
+ tokens;
412
+ pos;
413
+ context;
414
+ constructor(tokens, context) {
415
+ this.tokens = tokens;
416
+ this.pos = 0;
417
+ this.context = context;
418
+ }
419
+ current() {
420
+ return this.tokens[this.pos] || { type: "EOF", value: "" };
421
+ }
422
+ advance() {
423
+ const token = this.current();
424
+ this.pos++;
425
+ return token;
426
+ }
427
+ evaluate() {
428
+ return this.parseOr();
429
+ }
430
+ parseOr() {
431
+ let left = this.parseAnd();
432
+ while (this.current().value === "||" || this.current().value === "or") {
433
+ this.advance();
434
+ const right = this.parseAnd();
435
+ left = Boolean(left) || Boolean(right);
436
+ }
437
+ return left;
438
+ }
439
+ parseAnd() {
440
+ let left = this.parseNot();
441
+ while (this.current().value === "&&" || this.current().value === "and") {
442
+ this.advance();
443
+ const right = this.parseNot();
444
+ left = Boolean(left) && Boolean(right);
445
+ }
446
+ return left;
447
+ }
448
+ parseNot() {
449
+ if (this.current().value === "!" || this.current().value === "not") {
450
+ this.advance();
451
+ return !Boolean(this.parseNot());
452
+ }
453
+ return this.parseComparison();
454
+ }
455
+ parseComparison() {
456
+ let left = this.parsePrimary();
457
+ const op = this.current();
458
+ if (op.type === "OPERATOR") {
459
+ switch (op.value) {
460
+ case "==":
461
+ case "===":
462
+ this.advance();
463
+ return left === this.parsePrimary();
464
+ case "!=":
465
+ case "!==":
466
+ this.advance();
467
+ return left !== this.parsePrimary();
468
+ case "<":
469
+ this.advance();
470
+ return left < this.parsePrimary();
471
+ case ">":
472
+ this.advance();
473
+ return left > this.parsePrimary();
474
+ case "<=":
475
+ this.advance();
476
+ return left <= this.parsePrimary();
477
+ case ">=":
478
+ this.advance();
479
+ return left >= this.parsePrimary();
480
+ case "includes":
481
+ this.advance();
482
+ const includesValue = this.parsePrimary();
483
+ if (Array.isArray(left)) {
484
+ return left.includes(includesValue);
485
+ }
486
+ if (typeof left === "string") {
487
+ return left.includes(String(includesValue));
488
+ }
489
+ return false;
490
+ case "in":
491
+ this.advance();
492
+ const inArray = this.parsePrimary();
493
+ if (Array.isArray(inArray)) {
494
+ return inArray.includes(left);
495
+ }
496
+ return false;
497
+ }
498
+ }
499
+ return left;
500
+ }
501
+ parsePrimary() {
502
+ const token = this.current();
503
+ if (token.type === "LPAREN") {
504
+ this.advance();
505
+ const value = this.parseOr();
506
+ if (this.current().type === "RPAREN") {
507
+ this.advance();
508
+ }
509
+ return value;
510
+ }
511
+ if (token.type === "STRING") {
512
+ this.advance();
513
+ return token.value;
514
+ }
515
+ if (token.type === "NUMBER") {
516
+ this.advance();
517
+ return parseFloat(token.value);
518
+ }
519
+ if (token.type === "BOOLEAN") {
520
+ this.advance();
521
+ return token.value === "true";
522
+ }
523
+ if (token.type === "NULL") {
524
+ this.advance();
525
+ return null;
526
+ }
527
+ if (token.type === "IDENTIFIER") {
528
+ return this.parseIdentifier();
529
+ }
530
+ return null;
531
+ }
532
+ parseIdentifier() {
533
+ let value = this.context;
534
+ while (this.current().type === "IDENTIFIER" || this.current().type === "DOT") {
535
+ if (this.current().type === "DOT") {
536
+ this.advance();
537
+ continue;
538
+ }
539
+ const key = this.advance().value;
540
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
541
+ value = value[key];
542
+ } else {
543
+ value = void 0;
544
+ }
545
+ if (this.current().type === "LPAREN") {
546
+ this.advance();
547
+ const args = [];
548
+ while (this.current().type !== "RPAREN" && this.current().type !== "EOF") {
549
+ args.push(this.parseOr());
550
+ if (this.current().type === "COMMA") {
551
+ this.advance();
552
+ }
553
+ }
554
+ if (this.current().type === "RPAREN") {
555
+ this.advance();
556
+ }
557
+ return this.callMethod(key, value, args);
558
+ }
559
+ if (this.current().type !== "DOT") {
560
+ break;
561
+ }
562
+ }
563
+ return value;
564
+ }
565
+ callMethod(method, target, args) {
566
+ switch (method) {
567
+ case "includes":
568
+ if (Array.isArray(target)) {
569
+ return target.includes(args[0]);
570
+ }
571
+ if (typeof target === "string") {
572
+ return target.includes(String(args[0]));
573
+ }
574
+ return false;
575
+ case "startsWith":
576
+ if (typeof target === "string") {
577
+ return target.startsWith(String(args[0]));
578
+ }
579
+ return false;
580
+ case "endsWith":
581
+ if (typeof target === "string") {
582
+ return target.endsWith(String(args[0]));
583
+ }
584
+ return false;
585
+ case "length":
586
+ if (Array.isArray(target) || typeof target === "string") {
587
+ return target.length;
588
+ }
589
+ return 0;
590
+ default:
591
+ return null;
592
+ }
593
+ }
594
+ };
595
+ function evaluateExpression(expression, context) {
596
+ try {
597
+ const tokens = tokenize(expression);
598
+ const evaluator = new ExpressionEvaluator(tokens, context);
599
+ const result = evaluator.evaluate();
600
+ return {
601
+ passed: Boolean(result)
602
+ };
603
+ } catch (error) {
604
+ return {
605
+ passed: false,
606
+ error: error.message
607
+ };
608
+ }
609
+ }
610
+ var GateClient = class {
611
+ config;
612
+ prizeHandlers = /* @__PURE__ */ new Map();
613
+ firedPrizes = [];
614
+ ws = null;
615
+ options;
616
+ entityIdResolver;
617
+ constructor(config, options = {}) {
618
+ this.config = config;
619
+ this.options = options;
620
+ this.entityIdResolver = options.entityIdResolver || this.defaultEntityIdResolver;
621
+ if (options.devMode) {
622
+ this.connectWatcher();
623
+ }
624
+ }
625
+ /**
626
+ * Default entity ID resolver - looks for id, _id, userId, entityId
627
+ */
628
+ defaultEntityIdResolver(entity) {
629
+ return entity.id || entity._id || entity.userId || entity.entityId || "anonymous";
630
+ }
631
+ /**
632
+ * Connect to the Gate watcher server
633
+ */
634
+ connectWatcher() {
635
+ const url = this.options.watcherUrl || `ws://localhost:${this.config.settings.dev.watcherPort}`;
636
+ try {
637
+ if (typeof WebSocket !== "undefined") {
638
+ this.ws = new WebSocket(url);
639
+ this.ws.onopen = () => {
640
+ console.log("[Gate SDK] Connected to watcher");
641
+ };
642
+ this.ws.onerror = () => {
643
+ console.warn("[Gate SDK] Failed to connect to watcher");
644
+ };
645
+ this.ws.onclose = () => {
646
+ setTimeout(() => this.connectWatcher(), 5e3);
647
+ };
648
+ }
649
+ } catch {
650
+ console.warn("[Gate SDK] WebSocket not available for watcher connection");
651
+ }
652
+ }
653
+ /**
654
+ * Send event to watcher
655
+ */
656
+ sendWatcherEvent(event) {
657
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
658
+ this.ws.send(JSON.stringify(event));
659
+ }
660
+ }
661
+ /**
662
+ * Register a handler for a prize
663
+ */
664
+ onPrize(prizeId, handler) {
665
+ if (!this.prizeHandlers.has(prizeId)) {
666
+ this.prizeHandlers.set(prizeId, []);
667
+ }
668
+ this.prizeHandlers.get(prizeId).push(handler);
669
+ return () => {
670
+ const handlers = this.prizeHandlers.get(prizeId);
671
+ if (handlers) {
672
+ const index = handlers.indexOf(handler);
673
+ if (index >= 0) {
674
+ handlers.splice(index, 1);
675
+ }
676
+ }
677
+ };
678
+ }
679
+ /**
680
+ * Check if an entity can pass through a gate
681
+ */
682
+ async check(gateId, entity) {
683
+ const gate = this.config.gates.find((g) => g.id === gateId);
684
+ if (!gate) {
685
+ throw new Error(`Gate not found: ${gateId}`);
686
+ }
687
+ const entityId = this.entityIdResolver(entity);
688
+ const timestamp = Date.now();
689
+ this.sendWatcherEvent({
690
+ type: "gate:check",
691
+ timestamp,
692
+ entityId,
693
+ data: { gate, entitySnapshot: entity }
694
+ });
695
+ const lockResults = [];
696
+ let allLocksPassed = true;
697
+ for (const lock of gate.locks) {
698
+ const keyResults = [];
699
+ let lockPassed;
700
+ if (lock.mode === "any") {
701
+ lockPassed = false;
702
+ for (const key of lock.keys) {
703
+ const { passed, error } = evaluateExpression(key.expression, entity);
704
+ keyResults.push({ key, passed, error });
705
+ if (passed) {
706
+ lockPassed = true;
707
+ }
708
+ }
709
+ } else {
710
+ lockPassed = true;
711
+ for (const key of lock.keys) {
712
+ const { passed, error } = evaluateExpression(key.expression, entity);
713
+ keyResults.push({ key, passed, error });
714
+ if (!passed) {
715
+ lockPassed = false;
716
+ }
717
+ }
718
+ }
719
+ lockResults.push({
720
+ lock,
721
+ passed: lockPassed,
722
+ keyResults
723
+ });
724
+ if (!lockPassed) {
725
+ allLocksPassed = false;
726
+ }
727
+ }
728
+ const triggeredPrizes = [];
729
+ if (allLocksPassed) {
730
+ for (const prize of gate.prizes) {
731
+ if (prize.oneTime) {
732
+ const alreadyFired = this.firedPrizes.some(
733
+ (fp) => fp.entityId === entityId && fp.prizeId === prize.id
734
+ );
735
+ if (alreadyFired) {
736
+ continue;
737
+ }
738
+ }
739
+ triggeredPrizes.push(prize);
740
+ if (prize.oneTime) {
741
+ this.firedPrizes.push({
742
+ entityId,
743
+ prizeId: prize.id,
744
+ timestamp
745
+ });
746
+ }
747
+ const handlers = this.prizeHandlers.get(prize.id) || [];
748
+ for (const handler of handlers) {
749
+ try {
750
+ await handler(entity, {
751
+ gate,
752
+ prize,
753
+ timestamp
754
+ });
755
+ } catch (error) {
756
+ console.error(`[Gate SDK] Prize handler error for ${prize.id}:`, error);
757
+ }
758
+ }
759
+ this.sendWatcherEvent({
760
+ type: "prize:fire",
761
+ timestamp: Date.now(),
762
+ entityId,
763
+ data: { prizeId: prize.id, metadata: prize.metadata }
764
+ });
765
+ }
766
+ }
767
+ const result = {
768
+ gate,
769
+ passed: allLocksPassed,
770
+ lockResults,
771
+ triggeredPrizes,
772
+ timestamp,
773
+ entitySnapshot: entity
774
+ };
775
+ this.sendWatcherEvent({
776
+ type: allLocksPassed ? "gate:pass" : "gate:fail",
777
+ timestamp: Date.now(),
778
+ entityId,
779
+ data: result
780
+ });
781
+ return result;
782
+ }
783
+ /**
784
+ * Get a gate by ID
785
+ */
786
+ getGate(gateId) {
787
+ return this.config.gates.find((g) => g.id === gateId);
788
+ }
789
+ /**
790
+ * Get all gates
791
+ */
792
+ getGates() {
793
+ return this.config.gates;
794
+ }
795
+ /**
796
+ * Get all flows
797
+ */
798
+ getFlows() {
799
+ return this.config.flows;
800
+ }
801
+ /**
802
+ * Reset fired prizes for an entity (useful for testing)
803
+ */
804
+ resetPrizes(entityId) {
805
+ if (entityId) {
806
+ this.firedPrizes = this.firedPrizes.filter((fp) => fp.entityId !== entityId);
807
+ } else {
808
+ this.firedPrizes = [];
809
+ }
810
+ }
811
+ /**
812
+ * Disconnect from watcher
813
+ */
814
+ disconnect() {
815
+ if (this.ws) {
816
+ this.ws.close();
817
+ this.ws = null;
818
+ }
819
+ }
820
+ };
821
+ async function createGate(configPath, options = {}) {
822
+ const config = await parseGateConfig(configPath);
823
+ const devMode = options.devMode ?? process.env.NODE_ENV !== "production";
824
+ return new GateClient(config, {
825
+ ...options,
826
+ devMode
827
+ });
828
+ }
829
+ var isProduction = typeof process !== "undefined" ? process.env.NODE_ENV === "production" : typeof import.meta !== "undefined" && import.meta.env?.PROD;
830
+ var enableValidation = typeof process !== "undefined" ? process.env.PORTAL_VALIDATION === "true" : typeof import.meta !== "undefined" && import.meta.env?.VITE_ENABLE_PORTAL_VALIDATION === "true";
831
+ var isTestMode = typeof process !== "undefined" ? process.env.PORTAL_TEST_MODE === "true" : typeof import.meta !== "undefined" && import.meta.env?.VITE_TEST_MODE === "true";
832
+
833
+ // src/commands/portal/test.ts
834
+ async function gateTestCommand(targetPath, options) {
835
+ const rootDir = targetPath ? path3.resolve(targetPath) : process.cwd();
836
+ const gateConfigPath = path3.join(rootDir, "portal.yaml");
837
+ if (!fs3.existsSync(gateConfigPath)) {
838
+ console.error(chalk.red(`\u274C portal.yaml not found at ${gateConfigPath}`));
839
+ console.error(chalk.gray(" Run `paradigm init` or create portal.yaml manually"));
840
+ process.exit(1);
841
+ }
842
+ if (options.generate) {
843
+ console.log(chalk.blue("\n\u{1F527} Generating test files...\n"));
844
+ try {
845
+ const generatedFiles = await generateTests(gateConfigPath, {
846
+ outputDir: options.output || "tests/gates",
847
+ framework: options.framework || "jest",
848
+ includeFlows: true
849
+ });
850
+ console.log(chalk.green(`\u2705 Generated ${generatedFiles.length} test files:`));
851
+ for (const file of generatedFiles) {
852
+ console.log(chalk.gray(` ${path3.relative(rootDir, file)}`));
853
+ }
854
+ console.log("");
855
+ } catch (error) {
856
+ console.error(chalk.red(`\u274C Error generating tests: ${error.message}`));
857
+ process.exit(1);
858
+ }
859
+ return;
860
+ }
861
+ if (options.component) {
862
+ console.log(chalk.blue("\n\u{1F50D} Scanning components for gate checks...\n"));
863
+ try {
864
+ const components = await scanComponents({
865
+ rootDir,
866
+ gateConfigPath
867
+ });
868
+ const report = generateComponentReport(components);
869
+ console.log(report);
870
+ const withMissing = components.filter((c) => c.missingChecks.length > 0);
871
+ if (withMissing.length > 0) {
872
+ process.exit(1);
873
+ }
874
+ } catch (error) {
875
+ console.error(chalk.red(`\u274C Error scanning components: ${error.message}`));
876
+ process.exit(1);
877
+ }
878
+ return;
879
+ }
880
+ if (options.gate) {
881
+ console.log(chalk.blue(`
882
+ \u{1F9EA} Testing gate: ${options.gate}
883
+ `));
884
+ try {
885
+ const config = await parseGateConfig(gateConfigPath);
886
+ const client = await createGate(gateConfigPath);
887
+ setGateClient(client);
888
+ const gate = config.gates.find((g) => g.id === options.gate);
889
+ if (!gate) {
890
+ console.error(chalk.red(`\u274C Gate "${options.gate}" not found`));
891
+ process.exit(1);
892
+ }
893
+ const testCases = [
894
+ {
895
+ name: "Empty entity (should fail)",
896
+ entity: {},
897
+ expected: false
898
+ },
899
+ {
900
+ name: "Entity with required properties",
901
+ entity: {
902
+ // TODO: Add properties based on gate locks
903
+ user: { id: "test-user" }
904
+ },
905
+ expected: true
906
+ }
907
+ ];
908
+ const result = await validateGateway(options.gate, testCases, client);
909
+ if (result.passed) {
910
+ console.log(chalk.green("\u2705 All tests passed\n"));
911
+ } else {
912
+ console.log(chalk.red("\u274C Some tests failed\n"));
913
+ for (const error of result.errors) {
914
+ console.log(chalk.red(` ${error}`));
915
+ }
916
+ console.log("");
917
+ process.exit(1);
918
+ }
919
+ } catch (error) {
920
+ console.error(chalk.red(`\u274C Error testing gate: ${error.message}`));
921
+ process.exit(1);
922
+ }
923
+ return;
924
+ }
925
+ console.log(chalk.blue("\n\u{1F9EA} Portal Testing\n"));
926
+ console.log("Usage:");
927
+ console.log(" paradigm portal test --generate Generate test files");
928
+ console.log(" paradigm portal test --portal ^auth-required Test specific portal");
929
+ console.log(" paradigm portal test --component Validate component access");
930
+ console.log("");
931
+ }
932
+ export {
933
+ gateTestCommand
934
+ };