alpic 0.0.0-dev.ge4d40f3 → 0.0.0-dev.ge4e4833

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 (151) hide show
  1. package/README.md +4 -0
  2. package/bin/dev.js +0 -0
  3. package/dist/__tests__/audit.e2e.test.js +1 -1
  4. package/dist/__tests__/audit.e2e.test.js.map +1 -1
  5. package/dist/__tests__/environment-delete.e2e.test.d.ts +1 -0
  6. package/dist/__tests__/environment-delete.e2e.test.js +95 -0
  7. package/dist/__tests__/environment-delete.e2e.test.js.map +1 -0
  8. package/dist/__tests__/insights-category.e2e.test.d.ts +1 -0
  9. package/dist/__tests__/insights-category.e2e.test.js +247 -0
  10. package/dist/__tests__/insights-category.e2e.test.js.map +1 -0
  11. package/dist/__tests__/insights.e2e.test.d.ts +1 -0
  12. package/dist/__tests__/insights.e2e.test.js +193 -0
  13. package/dist/__tests__/insights.e2e.test.js.map +1 -0
  14. package/dist/__tests__/mock-api.d.ts +21 -0
  15. package/dist/__tests__/mock-api.js +375 -32
  16. package/dist/__tests__/mock-api.js.map +1 -1
  17. package/dist/__tests__/submission.e2e.test.d.ts +1 -0
  18. package/dist/__tests__/submission.e2e.test.js +173 -0
  19. package/dist/__tests__/submission.e2e.test.js.map +1 -0
  20. package/dist/__tests__/team-inspect.e2e.test.js +26 -1
  21. package/dist/__tests__/team-inspect.e2e.test.js.map +1 -1
  22. package/dist/__tests__/telemetry-destinations.e2e.test.d.ts +1 -0
  23. package/dist/__tests__/telemetry-destinations.e2e.test.js +105 -0
  24. package/dist/__tests__/telemetry-destinations.e2e.test.js.map +1 -0
  25. package/dist/__tests__/tunnel.e2e.test.js +67 -14
  26. package/dist/__tests__/tunnel.e2e.test.js.map +1 -1
  27. package/dist/__tests__/utils.d.ts +3 -0
  28. package/dist/__tests__/utils.js +13 -0
  29. package/dist/__tests__/utils.js.map +1 -1
  30. package/dist/commands/audit.js +3 -1
  31. package/dist/commands/audit.js.map +1 -1
  32. package/dist/commands/deploy.js +11 -11
  33. package/dist/commands/deploy.js.map +1 -1
  34. package/dist/commands/deployment/list.js +3 -3
  35. package/dist/commands/deployment/list.js.map +1 -1
  36. package/dist/commands/environment/delete.d.ts +13 -0
  37. package/dist/commands/environment/delete.js +45 -0
  38. package/dist/commands/environment/delete.js.map +1 -0
  39. package/dist/commands/environment/inspect.js +3 -3
  40. package/dist/commands/environment/inspect.js.map +1 -1
  41. package/dist/commands/environment/list.js +3 -3
  42. package/dist/commands/environment/list.js.map +1 -1
  43. package/dist/commands/insights/category/create.d.ts +17 -0
  44. package/dist/commands/insights/category/create.js +63 -0
  45. package/dist/commands/insights/category/create.js.map +1 -0
  46. package/dist/commands/insights/category/delete.d.ts +13 -0
  47. package/dist/commands/insights/category/delete.js +40 -0
  48. package/dist/commands/insights/category/delete.js.map +1 -0
  49. package/dist/commands/insights/category/link.d.ts +14 -0
  50. package/dist/commands/insights/category/link.js +36 -0
  51. package/dist/commands/insights/category/link.js.map +1 -0
  52. package/dist/commands/insights/category/list.d.ts +13 -0
  53. package/dist/commands/insights/category/list.js +46 -0
  54. package/dist/commands/insights/category/list.js.map +1 -0
  55. package/dist/commands/insights/category/unlink.d.ts +14 -0
  56. package/dist/commands/insights/category/unlink.js +36 -0
  57. package/dist/commands/insights/category/unlink.js.map +1 -0
  58. package/dist/commands/insights/category/update.d.ts +16 -0
  59. package/dist/commands/insights/category/update.js +45 -0
  60. package/dist/commands/insights/category/update.js.map +1 -0
  61. package/dist/commands/insights/feedback/list.d.ts +19 -0
  62. package/dist/commands/insights/feedback/list.js +73 -0
  63. package/dist/commands/insights/feedback/list.js.map +1 -0
  64. package/dist/commands/insights/intent/list.d.ts +22 -0
  65. package/dist/commands/insights/intent/list.js +89 -0
  66. package/dist/commands/insights/intent/list.js.map +1 -0
  67. package/dist/commands/link.js +3 -3
  68. package/dist/commands/link.js.map +1 -1
  69. package/dist/commands/logs.js +1 -9
  70. package/dist/commands/logs.js.map +1 -1
  71. package/dist/commands/project/inspect.js +3 -3
  72. package/dist/commands/project/inspect.js.map +1 -1
  73. package/dist/commands/project/list.js +3 -3
  74. package/dist/commands/project/list.js.map +1 -1
  75. package/dist/commands/submission/create.d.ts +15 -0
  76. package/dist/commands/submission/create.js +59 -0
  77. package/dist/commands/submission/create.js.map +1 -0
  78. package/dist/commands/submission/inspect.d.ts +10 -0
  79. package/dist/commands/submission/inspect.js +56 -0
  80. package/dist/commands/submission/inspect.js.map +1 -0
  81. package/dist/commands/submission/list.d.ts +13 -0
  82. package/dist/commands/submission/list.js +63 -0
  83. package/dist/commands/submission/list.js.map +1 -0
  84. package/dist/commands/submission/update.d.ts +12 -0
  85. package/dist/commands/submission/update.js +37 -0
  86. package/dist/commands/submission/update.js.map +1 -0
  87. package/dist/commands/team/inspect.js +25 -10
  88. package/dist/commands/team/inspect.js.map +1 -1
  89. package/dist/commands/team/list.js +3 -3
  90. package/dist/commands/team/list.js.map +1 -1
  91. package/dist/commands/telemetry/destinations/create.d.ts +19 -0
  92. package/dist/commands/telemetry/destinations/create.js +57 -0
  93. package/dist/commands/telemetry/destinations/create.js.map +1 -0
  94. package/dist/commands/telemetry/destinations/delete.d.ts +13 -0
  95. package/dist/commands/telemetry/destinations/delete.js +27 -0
  96. package/dist/commands/telemetry/destinations/delete.js.map +1 -0
  97. package/dist/commands/telemetry/destinations/list.d.ts +13 -0
  98. package/dist/commands/telemetry/destinations/list.js +48 -0
  99. package/dist/commands/telemetry/destinations/list.js.map +1 -0
  100. package/dist/commands/telemetry/destinations/update.d.ts +20 -0
  101. package/dist/commands/telemetry/destinations/update.js +62 -0
  102. package/dist/commands/telemetry/destinations/update.js.map +1 -0
  103. package/dist/commands/tunnel.js +5 -5
  104. package/dist/commands/tunnel.js.map +1 -1
  105. package/dist/lib/agent-mode.d.ts +8 -1
  106. package/dist/lib/agent-mode.js +25 -26
  107. package/dist/lib/agent-mode.js.map +1 -1
  108. package/dist/lib/alpic-command.d.ts +2 -1
  109. package/dist/lib/alpic-command.js +3 -3
  110. package/dist/lib/alpic-command.js.map +1 -1
  111. package/dist/lib/audit.d.ts +1 -0
  112. package/dist/lib/audit.js +20 -13
  113. package/dist/lib/audit.js.map +1 -1
  114. package/dist/lib/base-workflow.d.ts +2 -1
  115. package/dist/lib/base-workflow.js +3 -3
  116. package/dist/lib/base-workflow.js.map +1 -1
  117. package/dist/lib/deployment.js +12 -12
  118. package/dist/lib/deployment.js.map +1 -1
  119. package/dist/lib/environment-variable.js +13 -13
  120. package/dist/lib/environment-variable.js.map +1 -1
  121. package/dist/lib/insights.d.ts +19 -0
  122. package/dist/lib/insights.js +54 -0
  123. package/dist/lib/insights.js.map +1 -0
  124. package/dist/lib/link.js +4 -4
  125. package/dist/lib/link.js.map +1 -1
  126. package/dist/lib/playground.js +25 -25
  127. package/dist/lib/playground.js.map +1 -1
  128. package/dist/lib/publish.js +3 -3
  129. package/dist/lib/publish.js.map +1 -1
  130. package/dist/lib/resolve.d.ts +7 -6
  131. package/dist/lib/resolve.js +23 -18
  132. package/dist/lib/resolve.js.map +1 -1
  133. package/dist/lib/submission.d.ts +13 -0
  134. package/dist/lib/submission.js +52 -0
  135. package/dist/lib/submission.js.map +1 -0
  136. package/dist/lib/telemetry-destinations.d.ts +9 -0
  137. package/dist/lib/telemetry-destinations.js +21 -0
  138. package/dist/lib/telemetry-destinations.js.map +1 -0
  139. package/dist/lib/telemetry-destinations.test.d.ts +1 -0
  140. package/dist/lib/telemetry-destinations.test.js +14 -0
  141. package/dist/lib/telemetry-destinations.test.js.map +1 -0
  142. package/dist/lib/telemetry.js +11 -5
  143. package/dist/lib/telemetry.js.map +1 -1
  144. package/dist/lib/utils.d.ts +8 -1
  145. package/dist/lib/utils.js +14 -1
  146. package/dist/lib/utils.js.map +1 -1
  147. package/dist/lib/utils.test.js +28 -5
  148. package/dist/lib/utils.test.js.map +1 -1
  149. package/dist/sentry.js +1 -0
  150. package/dist/sentry.js.map +1 -1
  151. package/package.json +22 -21
@@ -8,6 +8,32 @@ import { OpenAPIHandler } from "@orpc/openapi/node";
8
8
  import { implement, ORPCError } from "@orpc/server";
9
9
  import express, {} from "express";
10
10
  import { inc as semverInc } from "semver";
11
+ function createEmptyMockData() {
12
+ return {
13
+ tunnelTicket: null,
14
+ teams: null,
15
+ projects: new Map(),
16
+ environments: new Map(),
17
+ deployments: new Map(),
18
+ environmentVariables: new Map(),
19
+ runtimeLogs: new Map(),
20
+ runtimeLogPages: new Map(),
21
+ deploymentLogPages: new Map(),
22
+ deploymentLogCallCounts: new Map(),
23
+ publishedServers: new Map(),
24
+ beaconGetResponses: [],
25
+ beaconGetCallCount: 0,
26
+ playgrounds: new Map(),
27
+ teamUsages: new Map(),
28
+ intents: new Map(),
29
+ feedbacks: new Map(),
30
+ intentCategories: new Map(),
31
+ intentCategoryCounter: 0,
32
+ submissions: new Map(),
33
+ submissionPrefills: new Map(),
34
+ telemetryDestinations: new Map(),
35
+ };
36
+ }
11
37
  const __testDirname = dirname(fileURLToPath(import.meta.url));
12
38
  const FIXTURE_CERT_PATH = join(__testDirname, "fixtures", "certs", "localhost-cert.pem");
13
39
  const FIXTURE_KEY_PATH = join(__testDirname, "fixtures", "certs", "localhost-key.pem");
@@ -26,22 +52,7 @@ export class MockApiServer {
26
52
  callHistory = [];
27
53
  constructor() {
28
54
  this.app = express();
29
- this.mockData = {
30
- tunnelTicket: null,
31
- teams: null,
32
- projects: new Map(),
33
- environments: new Map(),
34
- deployments: new Map(),
35
- environmentVariables: new Map(),
36
- runtimeLogs: new Map(),
37
- runtimeLogPages: new Map(),
38
- deploymentLogPages: new Map(),
39
- deploymentLogCallCounts: new Map(),
40
- publishedServers: new Map(),
41
- beaconGetResponses: [],
42
- beaconGetCallCount: 0,
43
- playgrounds: new Map(),
44
- };
55
+ this.mockData = createEmptyMockData();
45
56
  }
46
57
  async start() {
47
58
  if (this.server) {
@@ -140,6 +151,15 @@ export class MockApiServer {
140
151
  ];
141
152
  }),
142
153
  },
154
+ getUsage: {
155
+ v1: implement(contract.teams.getUsage.v1).handler(async ({ input }) => {
156
+ const usage = this.mockData.teamUsages.get(input.teamId);
157
+ if (!usage) {
158
+ throw new ORPCError("NOT_FOUND", { message: "Team usage not found" });
159
+ }
160
+ return usage;
161
+ }),
162
+ },
143
163
  },
144
164
  analytics: {
145
165
  get: {
@@ -172,6 +192,129 @@ export class MockApiServer {
172
192
  }),
173
193
  },
174
194
  },
195
+ intents: {
196
+ list: {
197
+ v1: implement(contract.intents.list.v1).handler(async ({ input }) => {
198
+ if (!this.mockData.environments.has(input.environmentId)) {
199
+ throw new ORPCError("NOT_FOUND", { message: "Environment not found" });
200
+ }
201
+ let intents = this.mockData.intents.get(input.environmentId) ?? [];
202
+ if (input.search) {
203
+ const search = input.search.toLowerCase();
204
+ intents = intents.filter((intent) => intent.message.toLowerCase().includes(search) ||
205
+ intent.toolName.toLowerCase().includes(search) ||
206
+ intent.categories.some(({ name }) => name.toLowerCase().includes(search)));
207
+ }
208
+ if (input.tools) {
209
+ intents = intents.filter(({ toolName }) => input.tools?.includes(toolName));
210
+ }
211
+ if (input.clientIds) {
212
+ intents = intents.filter(({ clientId }) => clientId !== null && input.clientIds?.includes(clientId));
213
+ }
214
+ if (input.categoryIds) {
215
+ intents = intents.filter(({ categories }) => categories.some(({ id }) => input.categoryIds?.includes(id)));
216
+ }
217
+ intents = [...intents].sort((left, right) => input.sortDir === "asc"
218
+ ? left.createdAt.getTime() - right.createdAt.getTime()
219
+ : right.createdAt.getTime() - left.createdAt.getTime());
220
+ return {
221
+ intents: intents.slice(input.offset, input.offset + input.limit),
222
+ totalCount: intents.length,
223
+ };
224
+ }),
225
+ },
226
+ },
227
+ intentCategories: {
228
+ list: {
229
+ v1: implement(contract.intentCategories.list.v1).handler(async ({ input }) => {
230
+ const categories = this.requireMockEnvironmentCategories(input.environmentId);
231
+ return categories.map(({ linkedIntentIds, ...category }) => ({
232
+ ...category,
233
+ intentCount: linkedIntentIds.size,
234
+ }));
235
+ }),
236
+ },
237
+ create: {
238
+ v1: implement(contract.intentCategories.create.v1).handler(async ({ input }) => {
239
+ const categories = this.requireMockEnvironmentCategories(input.environmentId);
240
+ this.requireMockIntents(input.environmentId, input.intentIds ?? [], "BAD_REQUEST");
241
+ const { linkedIntentIds, ...category } = {
242
+ id: `cat-mock-${++this.mockData.intentCategoryCounter}`,
243
+ createdAt: new Date("2026-08-13T00:00:00.000Z"),
244
+ environmentId: input.environmentId,
245
+ name: input.name,
246
+ color: input.color ?? "gray",
247
+ linkedIntentIds: new Set(input.intentIds ?? []),
248
+ };
249
+ categories.push({ ...category, linkedIntentIds });
250
+ return category;
251
+ }),
252
+ },
253
+ update: {
254
+ v1: implement(contract.intentCategories.update.v1).handler(async ({ input }) => {
255
+ const category = this.requireMockIntentCategory(input.environmentId, input.categoryId);
256
+ category.name = input.name ?? category.name;
257
+ category.color = input.color ?? category.color;
258
+ const { linkedIntentIds, ...updated } = category;
259
+ return updated;
260
+ }),
261
+ },
262
+ delete: {
263
+ v1: implement(contract.intentCategories.delete.v1).handler(async ({ input }) => {
264
+ const category = this.requireMockIntentCategory(input.environmentId, input.categoryId);
265
+ this.mockData.intentCategories.set(input.environmentId, (this.mockData.intentCategories.get(input.environmentId) ?? []).filter(({ id }) => id !== category.id));
266
+ this.removeCategoryFromIntents(input.environmentId, category.id, null);
267
+ return { success: true };
268
+ }),
269
+ },
270
+ link: {
271
+ v1: implement(contract.intentCategories.link.v1).handler(async ({ input }) => {
272
+ const category = this.requireMockIntentCategory(input.environmentId, input.categoryId);
273
+ this.requireMockIntents(input.environmentId, input.intentIds, "NOT_FOUND");
274
+ for (const intentId of input.intentIds) {
275
+ category.linkedIntentIds.add(intentId);
276
+ }
277
+ for (const intent of this.mockData.intents.get(input.environmentId) ?? []) {
278
+ const isLinked = intent.intentIds.some((intentId) => category.linkedIntentIds.has(intentId));
279
+ if (isLinked && !intent.categories.some(({ id }) => id === category.id)) {
280
+ intent.categories.push({ id: category.id, name: category.name, color: category.color });
281
+ }
282
+ }
283
+ return { success: true };
284
+ }),
285
+ },
286
+ unlink: {
287
+ v1: implement(contract.intentCategories.unlink.v1).handler(async ({ input }) => {
288
+ const category = this.requireMockIntentCategory(input.environmentId, input.categoryId);
289
+ for (const intentId of input.intentIds) {
290
+ category.linkedIntentIds.delete(intentId);
291
+ }
292
+ this.removeCategoryFromIntents(input.environmentId, category.id, category.linkedIntentIds);
293
+ return { success: true };
294
+ }),
295
+ },
296
+ },
297
+ feedbacks: {
298
+ list: {
299
+ v1: implement(contract.feedbacks.list.v1).handler(async ({ input }) => {
300
+ if (!this.mockData.environments.has(input.environmentId)) {
301
+ throw new ORPCError("NOT_FOUND", { message: "Environment not found" });
302
+ }
303
+ let feedbacks = this.mockData.feedbacks.get(input.environmentId) ?? [];
304
+ if (input.search) {
305
+ const search = input.search.toLowerCase();
306
+ feedbacks = feedbacks.filter((feedback) => feedback.content.toLowerCase().includes(search) || feedback.source.toLowerCase().includes(search));
307
+ }
308
+ if (input.clientIds) {
309
+ feedbacks = feedbacks.filter(({ clientId }) => clientId !== null && input.clientIds?.includes(clientId));
310
+ }
311
+ return {
312
+ feedbacks: feedbacks.slice(input.offset, input.offset + input.limit),
313
+ totalCount: feedbacks.length,
314
+ };
315
+ }),
316
+ },
317
+ },
175
318
  projects: {
176
319
  list: {
177
320
  v1: implement(contract.projects.list.v1).handler(async ({ input }) => {
@@ -355,6 +498,30 @@ export class MockApiServer {
355
498
  return environment;
356
499
  }),
357
500
  },
501
+ delete: {
502
+ v1: implement(contract.environments.delete.v1).handler(async ({ input }) => {
503
+ const environment = this.mockData.environments.get(input.environmentId);
504
+ if (!environment) {
505
+ throw new ORPCError("NOT_FOUND", {
506
+ message: "Environment not found",
507
+ });
508
+ }
509
+ const project = this.mockData.projects.get(environment.projectId);
510
+ if (project?.productionEnvironment?.id === input.environmentId) {
511
+ throw new ORPCError("BAD_REQUEST", {
512
+ message: "Cannot delete the production environment of the project",
513
+ });
514
+ }
515
+ this.mockData.environments.delete(input.environmentId);
516
+ if (project) {
517
+ this.mockData.projects.set(environment.projectId, {
518
+ ...project,
519
+ environments: project.environments.filter((env) => env.id !== input.environmentId),
520
+ });
521
+ }
522
+ return { success: true };
523
+ }),
524
+ },
358
525
  getLogs: {
359
526
  v1: implement(contract.environments.getLogs.v1).handler(async ({ input }) => {
360
527
  const environment = this.mockData.environments.get(input.environmentId);
@@ -422,6 +589,80 @@ export class MockApiServer {
422
589
  }),
423
590
  },
424
591
  },
592
+ telemetryDestinations: {
593
+ list: {
594
+ v1: implement(contract.telemetryDestinations.list.v1).handler(async ({ input }) => {
595
+ if (!this.mockData.environments.has(input.environmentId)) {
596
+ throw new ORPCError("NOT_FOUND", { message: "Environment not found" });
597
+ }
598
+ return this.mockData.telemetryDestinations.get(input.environmentId) ?? [];
599
+ }),
600
+ },
601
+ create: {
602
+ v1: implement(contract.telemetryDestinations.create.v1).handler(async ({ input }) => {
603
+ if (!this.mockData.environments.has(input.environmentId)) {
604
+ throw new ORPCError("NOT_FOUND", { message: "Environment not found" });
605
+ }
606
+ const destination = {
607
+ id: randomUUID(),
608
+ signal: input.signal,
609
+ endpoint: input.endpoint,
610
+ protocol: input.protocol,
611
+ status: "active",
612
+ lastFailureReason: null,
613
+ isScopedToIntentionalRequestsOnly: input.signal === "traces" && input.isScopedToIntentionalRequestsOnly,
614
+ headers: input.headers.map((header) => header.isSecret
615
+ ? { id: randomUUID(), name: header.name, value: null, isSecret: true }
616
+ : { id: randomUUID(), name: header.name, value: header.value, isSecret: false }),
617
+ };
618
+ this.mockData.telemetryDestinations.set(input.environmentId, [
619
+ ...(this.mockData.telemetryDestinations.get(input.environmentId) ?? []),
620
+ destination,
621
+ ]);
622
+ return destination;
623
+ }),
624
+ },
625
+ update: {
626
+ v1: implement(contract.telemetryDestinations.update.v1).handler(async ({ input }) => {
627
+ const destinations = this.mockData.telemetryDestinations.get(input.environmentId) ?? [];
628
+ const current = destinations.find(({ id }) => id === input.destinationId);
629
+ if (current === undefined) {
630
+ throw new ORPCError("NOT_FOUND", { message: "Telemetry destination not found" });
631
+ }
632
+ const updated = {
633
+ ...current,
634
+ endpoint: input.endpoint ?? current.endpoint,
635
+ protocol: input.protocol ?? current.protocol,
636
+ isScopedToIntentionalRequestsOnly: current.signal === "traces"
637
+ ? (input.isScopedToIntentionalRequestsOnly ?? current.isScopedToIntentionalRequestsOnly)
638
+ : false,
639
+ headers: input.headers?.map((header) => {
640
+ const previous = header.id === undefined ? undefined : current.headers.find(({ id }) => id === header.id);
641
+ const id = header.id ?? randomUUID();
642
+ if (header.isSecret) {
643
+ return { id, name: header.name, value: null, isSecret: true };
644
+ }
645
+ const value = header.value ?? previous?.value;
646
+ if (value === undefined || value === null) {
647
+ throw new ORPCError("BAD_REQUEST", { message: `Enter a value for the ${header.name} header` });
648
+ }
649
+ return { id, name: header.name, value, isSecret: false };
650
+ }) ?? current.headers,
651
+ };
652
+ this.mockData.telemetryDestinations.set(input.environmentId, destinations.map((destination) => (destination.id === updated.id ? updated : destination)));
653
+ return updated;
654
+ }),
655
+ },
656
+ delete: {
657
+ v1: implement(contract.telemetryDestinations.delete.v1).handler(async ({ input }) => {
658
+ if (!this.mockData.environments.has(input.environmentId)) {
659
+ throw new ORPCError("NOT_FOUND", { message: "Environment not found" });
660
+ }
661
+ this.mockData.telemetryDestinations.set(input.environmentId, (this.mockData.telemetryDestinations.get(input.environmentId) ?? []).filter(({ id }) => id !== input.destinationId));
662
+ return { success: true };
663
+ }),
664
+ },
665
+ },
425
666
  environmentVariables: {
426
667
  list: {
427
668
  v1: implement(contract.environmentVariables.list.v1).handler(async ({ input }) => {
@@ -640,6 +881,73 @@ export class MockApiServer {
640
881
  }),
641
882
  },
642
883
  },
884
+ submissions: {
885
+ create: {
886
+ v1: implement(contract.submissions.create.v1).handler(async ({ input }) => {
887
+ const environment = this.mockData.environments.get(input.environmentId);
888
+ if (!environment) {
889
+ throw new ORPCError("NOT_FOUND", { message: "Environment not found" });
890
+ }
891
+ const formData = this.mockData.submissionPrefills.get(input.platform) ?? {};
892
+ const now = new Date();
893
+ const submission = {
894
+ id: `sub-${randomUUID()}`,
895
+ environmentId: input.environmentId,
896
+ platform: input.platform,
897
+ formData,
898
+ prefillSnapshot: formData,
899
+ auditId: "audit-1",
900
+ urlVerification: null,
901
+ createdAt: now,
902
+ updatedAt: now,
903
+ };
904
+ this.mockData.submissions.set(submission.id, submission);
905
+ return submission;
906
+ }),
907
+ },
908
+ list: {
909
+ v1: implement(contract.submissions.list.v1).handler(async ({ input }) => {
910
+ const project = this.mockData.projects.get(input.projectId);
911
+ if (!project) {
912
+ throw new ORPCError("NOT_FOUND", { message: "Project not found" });
913
+ }
914
+ const environmentIds = new Set(project.environments.map((environment) => environment.id));
915
+ let submissions = [...this.mockData.submissions.values()].filter((submission) => environmentIds.has(submission.environmentId));
916
+ if (input.environmentId) {
917
+ submissions = submissions.filter((submission) => submission.environmentId === input.environmentId);
918
+ }
919
+ if (input.platform) {
920
+ submissions = submissions.filter((submission) => submission.platform === input.platform);
921
+ }
922
+ return submissions;
923
+ }),
924
+ },
925
+ get: {
926
+ v1: implement(contract.submissions.get.v1).handler(async ({ input }) => {
927
+ const submission = this.mockData.submissions.get(input.submissionId);
928
+ if (!submission) {
929
+ throw new ORPCError("NOT_FOUND", { message: "Submission not found" });
930
+ }
931
+ return submission;
932
+ }),
933
+ },
934
+ update: {
935
+ v1: implement(contract.submissions.update.v1).handler(async ({ input }) => {
936
+ const submission = this.mockData.submissions.get(input.submissionId);
937
+ if (!submission) {
938
+ throw new ORPCError("NOT_FOUND", { message: "Submission not found" });
939
+ }
940
+ if (submission.platform !== input.platform) {
941
+ throw new ORPCError("BAD_REQUEST", {
942
+ message: `Platform mismatch: submission ${submission.id} is ${submission.platform}, payload claims ${input.platform}`,
943
+ });
944
+ }
945
+ const updated = { ...submission, formData: input.formData, updatedAt: new Date() };
946
+ this.mockData.submissions.set(submission.id, updated);
947
+ return updated;
948
+ }),
949
+ },
950
+ },
643
951
  deployments: {
644
952
  list: {
645
953
  v1: implement(contract.deployments.list.v1).handler(async ({ input }) => {
@@ -748,22 +1056,7 @@ export class MockApiServer {
748
1056
  }
749
1057
  reset() {
750
1058
  this.callHistory = [];
751
- this.mockData = {
752
- tunnelTicket: null,
753
- teams: null,
754
- projects: new Map(),
755
- environments: new Map(),
756
- deployments: new Map(),
757
- environmentVariables: new Map(),
758
- runtimeLogs: new Map(),
759
- runtimeLogPages: new Map(),
760
- deploymentLogPages: new Map(),
761
- deploymentLogCallCounts: new Map(),
762
- publishedServers: new Map(),
763
- beaconGetResponses: [],
764
- beaconGetCallCount: 0,
765
- playgrounds: new Map(),
766
- };
1059
+ this.mockData = createEmptyMockData();
767
1060
  }
768
1061
  async stop() {
769
1062
  return new Promise((resolve, reject) => {
@@ -834,6 +1127,9 @@ export class MockApiServer {
834
1127
  hasStripeAccount: false,
835
1128
  }));
836
1129
  }
1130
+ setTeamUsage(usage) {
1131
+ this.mockData.teamUsages.set(usage.teamId, usage);
1132
+ }
837
1133
  addEnvironmentVariables(environmentId, variables) {
838
1134
  this.mockData.environmentVariables.set(environmentId, variables);
839
1135
  }
@@ -867,6 +1163,53 @@ export class MockApiServer {
867
1163
  setPlayground(environmentId, playground) {
868
1164
  this.mockData.playgrounds.set(environmentId, playground);
869
1165
  }
1166
+ setIntents(environmentId, intents) {
1167
+ this.mockData.intents.set(environmentId, intents);
1168
+ }
1169
+ setIntentCategories(environmentId, categories) {
1170
+ this.mockData.intentCategories.set(environmentId, categories.map((category) => ({ ...category, linkedIntentIds: new Set(category.linkedIntentIds ?? []) })));
1171
+ }
1172
+ setTelemetryDestinations(environmentId, destinations) {
1173
+ this.mockData.telemetryDestinations.set(environmentId, destinations);
1174
+ }
1175
+ requireMockEnvironmentCategories(environmentId) {
1176
+ if (!this.mockData.environments.has(environmentId)) {
1177
+ throw new ORPCError("NOT_FOUND", { message: "Environment not found" });
1178
+ }
1179
+ const categories = this.mockData.intentCategories.get(environmentId) ?? [];
1180
+ this.mockData.intentCategories.set(environmentId, categories);
1181
+ return categories;
1182
+ }
1183
+ requireMockIntentCategory(environmentId, categoryId) {
1184
+ const category = this.requireMockEnvironmentCategories(environmentId).find(({ id }) => id === categoryId);
1185
+ if (!category) {
1186
+ throw new ORPCError("NOT_FOUND", { message: "Intent category not found" });
1187
+ }
1188
+ return category;
1189
+ }
1190
+ requireMockIntents(environmentId, intentIds, errorCode) {
1191
+ const knownIntentIds = new Set((this.mockData.intents.get(environmentId) ?? []).flatMap((intent) => intent.intentIds));
1192
+ if (intentIds.some((intentId) => !knownIntentIds.has(intentId))) {
1193
+ throw new ORPCError(errorCode, { message: "One or more intents not found" });
1194
+ }
1195
+ }
1196
+ removeCategoryFromIntents(environmentId, categoryId, remainingIntentIds) {
1197
+ for (const intent of this.mockData.intents.get(environmentId) ?? []) {
1198
+ const stillLinked = remainingIntentIds !== null && intent.intentIds.some((intentId) => remainingIntentIds.has(intentId));
1199
+ if (!stillLinked) {
1200
+ intent.categories = intent.categories.filter(({ id }) => id !== categoryId);
1201
+ }
1202
+ }
1203
+ }
1204
+ setFeedbacks(environmentId, feedbacks) {
1205
+ this.mockData.feedbacks.set(environmentId, feedbacks);
1206
+ }
1207
+ addSubmission(submission) {
1208
+ this.mockData.submissions.set(submission.id, submission);
1209
+ }
1210
+ setSubmissionPrefill(platform, formData) {
1211
+ this.mockData.submissionPrefills.set(platform, formData);
1212
+ }
870
1213
  addProject(project) {
871
1214
  this.mockData.projects.set(project.id, project);
872
1215
  if (project.productionEnvironment) {