@percepta/create 3.1.3 → 3.1.5

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 (39) hide show
  1. package/README.md +8 -8
  2. package/dist/git-ops-C2CIjuce.js +51 -0
  3. package/dist/git-ops-C2CIjuce.js.map +1 -0
  4. package/dist/index.js +1073 -1067
  5. package/dist/index.js.map +1 -0
  6. package/dist/init-OeK4Yk6_.js +52 -0
  7. package/dist/init-OeK4Yk6_.js.map +1 -0
  8. package/dist/status-DC8mvHZj.js +48 -0
  9. package/dist/status-DC8mvHZj.js.map +1 -0
  10. package/dist/sync-C5Pd32VM.js +101 -0
  11. package/dist/sync-C5Pd32VM.js.map +1 -0
  12. package/dist/upstream-F6m8zRBQ.js +85 -0
  13. package/dist/upstream-F6m8zRBQ.js.map +1 -0
  14. package/package.json +23 -24
  15. package/templates/webapp/AGENTS.md +1 -1
  16. package/templates/webapp/README.md +1 -1
  17. package/templates/webapp/agent-skills/database.md +5 -1
  18. package/templates/webapp/agent-skills/deploy.md +5 -3
  19. package/templates/webapp/agent-skills/inngest.md +13 -8
  20. package/templates/webapp/agent-skills/oneshot.md +1 -1
  21. package/templates/webapp/deploy/README.md +2 -2
  22. package/templates/webapp/deploy/ryvn/environments/percepta-test/installations/__APP_NAME__.env.percepta-test.serviceinstallation.yaml +3 -3
  23. package/templates/webapp/package.json.template +2 -2
  24. package/templates/webapp/scripts/deploy-percepta-test.ts +311 -36
  25. package/templates/webapp/scripts/generate-migrations.ts +28 -0
  26. package/templates/webapp/src/drizzle/__tests__/migrationSql.test.ts +24 -0
  27. package/templates/webapp/src/drizzle/migrationSql.ts +8 -0
  28. package/templates/webapp/src/services/inngest/AppWorkflowService.ts +19 -0
  29. package/templates/webapp/src/services/inngest/__tests__/AppWorkflowService.test.ts +19 -0
  30. package/templates/webapp/src/services/inngest/events/AppEvents.ts +7 -13
  31. package/templates/webapp/src/services/inngest/events/payloads/ExampleEventPayload.ts +1 -3
  32. package/dist/chunk-CO3YWUD6.js +0 -139
  33. package/dist/chunk-DCM7JOSC.js +0 -49
  34. package/dist/chunk-V5EJIUBJ.js +0 -60
  35. package/dist/index.d.ts +0 -1
  36. package/dist/init-EQZ2TCSJ.js +0 -96
  37. package/dist/status-QW5TQDYY.js +0 -76
  38. package/dist/sync-RLBZDOFB.js +0 -136
  39. package/dist/upstream-TQFVPMEG.js +0 -144
@@ -81,6 +81,11 @@ interface Installation {
81
81
  name?: string;
82
82
  status?: string;
83
83
  health?: string;
84
+ release?: {
85
+ commitSha?: string;
86
+ currentVersion?: string;
87
+ targetVersion?: string;
88
+ };
84
89
  }
85
90
 
86
91
  interface VariableGroup {
@@ -91,6 +96,17 @@ interface VariableGroup {
91
96
  }>;
92
97
  }
93
98
 
99
+ interface Release {
100
+ version?: string;
101
+ commit?: {
102
+ sha?: string;
103
+ };
104
+ }
105
+
106
+ interface ReleaseList {
107
+ releases?: Release[];
108
+ }
109
+
94
110
  function parseArgs(argv: string[]): Options {
95
111
  const options: Options = {
96
112
  yes: false,
@@ -151,6 +167,22 @@ function run(
151
167
  return typeof result === "string" ? result.trim() : "";
152
168
  }
153
169
 
170
+ function runWithInput(
171
+ command: string,
172
+ args: string[],
173
+ cwd: string,
174
+ inputText: string,
175
+ ): string {
176
+ const result = execFileSync(command, args, {
177
+ cwd,
178
+ encoding: "utf8",
179
+ input: inputText,
180
+ stdio: ["pipe", "inherit", "inherit"],
181
+ });
182
+
183
+ return typeof result === "string" ? result.trim() : "";
184
+ }
185
+
154
186
  function runJson<T>(command: string, args: string[], cwd: string): T {
155
187
  const outputText = run(command, args, cwd, "pipe");
156
188
  return JSON.parse(outputText) as T;
@@ -248,7 +280,7 @@ async function ensureGitHubRepo(
248
280
  "repo",
249
281
  "create",
250
282
  repoSlug,
251
- "--private",
283
+ "--internal",
252
284
  "--source",
253
285
  monorepoRoot,
254
286
  "--remote",
@@ -301,6 +333,10 @@ function upsertService(manifestPath: string, monorepoRoot: string): void {
301
333
  run("ryvn", ["replace", "-f", manifestPath], monorepoRoot);
302
334
  }
303
335
 
336
+ function getCurrentCommitSha(monorepoRoot: string): string {
337
+ return run("git", ["rev-parse", "HEAD"], monorepoRoot, "pipe");
338
+ }
339
+
304
340
  function getInstallation(
305
341
  name: string,
306
342
  monorepoRoot: string,
@@ -335,6 +371,39 @@ function installationExists(name: string, monorepoRoot: string): boolean {
335
371
  return getInstallation(name, monorepoRoot) !== null;
336
372
  }
337
373
 
374
+ function isHealthyAtCommit(
375
+ installation: Installation | null,
376
+ commitSha: string,
377
+ ): boolean {
378
+ return (
379
+ installation?.status === "UP_TO_DATE" &&
380
+ installation.health === "HEALTHY" &&
381
+ installation.release?.commitSha === commitSha
382
+ );
383
+ }
384
+
385
+ function getReleaseForCommit(
386
+ serviceName: string,
387
+ commitSha: string,
388
+ monorepoRoot: string,
389
+ ): Release | null {
390
+ try {
391
+ const releaseList = runJson<ReleaseList>(
392
+ "ryvn",
393
+ ["get", "release", "--service", serviceName, "-o", "json"],
394
+ monorepoRoot,
395
+ );
396
+
397
+ return (
398
+ releaseList.releases?.find(
399
+ (release) => release.commit?.sha === commitSha,
400
+ ) ?? null
401
+ );
402
+ } catch {
403
+ return null;
404
+ }
405
+ }
406
+
338
407
  function assertHealthyPlatformInstallation(
339
408
  installation: Installation | null,
340
409
  name: string,
@@ -429,19 +498,170 @@ function upsertInstallation(
429
498
  name: string,
430
499
  manifestPath: string,
431
500
  monorepoRoot: string,
501
+ expectedCommitSha: string,
502
+ manifestContents?: string,
503
+ ): void {
504
+ const existingInstallation = getInstallation(name, monorepoRoot);
505
+ if (isHealthyAtCommit(existingInstallation, expectedCommitSha)) {
506
+ console.log(
507
+ `${name} is already healthy at ${expectedCommitSha.slice(0, 7)}; skipping installation replace.`,
508
+ );
509
+ return;
510
+ }
511
+
512
+ const action = existingInstallation == null ? "create" : "replace";
513
+ try {
514
+ if (existingInstallation == null) {
515
+ if (manifestContents == null) {
516
+ run("ryvn", ["create", "-f", manifestPath], monorepoRoot);
517
+ } else {
518
+ runWithInput(
519
+ "ryvn",
520
+ ["create", "-f", "-"],
521
+ monorepoRoot,
522
+ manifestContents,
523
+ );
524
+ }
525
+ } else if (manifestContents == null) {
526
+ run("ryvn", ["replace", "-f", manifestPath], monorepoRoot);
527
+ } else {
528
+ runWithInput(
529
+ "ryvn",
530
+ ["replace", "-f", "-"],
531
+ monorepoRoot,
532
+ manifestContents,
533
+ );
534
+ }
535
+ } catch (error) {
536
+ const currentInstallation = getInstallation(name, monorepoRoot);
537
+ if (isHealthyAtCommit(currentInstallation, expectedCommitSha)) {
538
+ console.log(
539
+ `${name} ${action} returned an error, but Ryvn now reports it healthy at ${expectedCommitSha.slice(0, 7)}; continuing.`,
540
+ );
541
+ return;
542
+ }
543
+
544
+ throw error;
545
+ }
546
+ }
547
+
548
+ function printFailedWorkflowLog(
549
+ repoSlug: string,
550
+ runId: number,
551
+ monorepoRoot: string,
432
552
  ): void {
433
- if (installationExists(name, monorepoRoot)) {
434
- run("ryvn", ["replace", "-f", manifestPath], monorepoRoot);
435
- } else {
436
- run("ryvn", ["create", "-f", manifestPath], monorepoRoot);
553
+ try {
554
+ console.log("");
555
+ console.log(`Failed workflow log for ${runId}:`);
556
+ run(
557
+ "gh",
558
+ ["run", "view", String(runId), "--repo", repoSlug, "--log-failed"],
559
+ monorepoRoot,
560
+ );
561
+ } catch {
562
+ console.log(`Could not fetch failed workflow logs for ${runId}.`);
563
+ }
564
+ }
565
+
566
+ async function ensureReleaseForCurrentCommit(
567
+ serviceName: string,
568
+ repoSlug: string,
569
+ workflowFile: string,
570
+ monorepoRoot: string,
571
+ options: Options,
572
+ commitSha: string,
573
+ ): Promise<Release> {
574
+ const existingRelease = getReleaseForCommit(
575
+ serviceName,
576
+ commitSha,
577
+ monorepoRoot,
578
+ );
579
+ if (existingRelease != null) {
580
+ console.log(
581
+ `${serviceName} release ${existingRelease.version ?? "unknown"} already exists for ${commitSha.slice(0, 7)}.`,
582
+ );
583
+ return existingRelease;
584
+ }
585
+
586
+ await triggerWorkflow(
587
+ serviceName,
588
+ repoSlug,
589
+ workflowFile,
590
+ monorepoRoot,
591
+ options,
592
+ commitSha,
593
+ );
594
+
595
+ const release = getReleaseForCommit(serviceName, commitSha, monorepoRoot);
596
+ if (release == null) {
597
+ throw new Error(
598
+ `No Ryvn release for ${serviceName} at ${commitSha} after ${workflowFile}.`,
599
+ );
600
+ }
601
+
602
+ return release;
603
+ }
604
+
605
+ function assertReleaseExistsForSkippedWorkflow(
606
+ serviceName: string,
607
+ commitSha: string,
608
+ monorepoRoot: string,
609
+ ): Release {
610
+ const release = getReleaseForCommit(serviceName, commitSha, monorepoRoot);
611
+ if (release == null) {
612
+ throw new Error(
613
+ `--skip-workflows was set, but no ${serviceName} Ryvn release exists for commit ${commitSha}. Rerun without --skip-workflows or confirm the release was created.`,
614
+ );
615
+ }
616
+
617
+ return release;
618
+ }
619
+
620
+ function logReleaseSummary(serviceName: string, release: Release): void {
621
+ console.log(
622
+ `${serviceName} release ready: ${release.version ?? "unknown version"}.`,
623
+ );
624
+ }
625
+
626
+ function requireReleaseForCurrentCommit(
627
+ serviceName: string,
628
+ repoSlug: string,
629
+ workflowFile: string,
630
+ monorepoRoot: string,
631
+ options: Options,
632
+ commitSha: string,
633
+ ): Promise<Release> {
634
+ if (options.skipWorkflows) {
635
+ return Promise.resolve(
636
+ assertReleaseExistsForSkippedWorkflow(
637
+ serviceName,
638
+ commitSha,
639
+ monorepoRoot,
640
+ ),
641
+ );
437
642
  }
643
+
644
+ return ensureReleaseForCurrentCommit(
645
+ serviceName,
646
+ repoSlug,
647
+ workflowFile,
648
+ monorepoRoot,
649
+ options,
650
+ commitSha,
651
+ );
652
+ }
653
+
654
+ function isRetryableReleaseFailure(error: unknown): boolean {
655
+ return error instanceof Error;
438
656
  }
439
657
 
440
658
  async function triggerWorkflow(
659
+ serviceName: string,
441
660
  repoSlug: string,
442
661
  workflowFile: string,
443
662
  monorepoRoot: string,
444
663
  options: Options,
664
+ commitSha: string,
445
665
  ): Promise<void> {
446
666
  if (options.skipWorkflows) return;
447
667
 
@@ -501,18 +721,32 @@ async function triggerWorkflow(
501
721
  }
502
722
 
503
723
  console.log(`Watching ${runInfo.url}`);
504
- run(
505
- "gh",
506
- [
507
- "run",
508
- "watch",
509
- String(runInfo.databaseId),
510
- "--repo",
511
- repoSlug,
512
- "--exit-status",
513
- ],
514
- monorepoRoot,
515
- );
724
+ try {
725
+ run(
726
+ "gh",
727
+ [
728
+ "run",
729
+ "watch",
730
+ String(runInfo.databaseId),
731
+ "--repo",
732
+ repoSlug,
733
+ "--exit-status",
734
+ ],
735
+ monorepoRoot,
736
+ );
737
+ } catch (error) {
738
+ printFailedWorkflowLog(repoSlug, runInfo.databaseId, monorepoRoot);
739
+
740
+ const release = getReleaseForCommit(serviceName, commitSha, monorepoRoot);
741
+ if (release != null && isRetryableReleaseFailure(error)) {
742
+ console.log(
743
+ `${workflowFile} reported a failure, but ${serviceName} release ${release.version ?? "unknown"} exists for ${commitSha.slice(0, 7)}; continuing.`,
744
+ );
745
+ return;
746
+ }
747
+
748
+ throw error;
749
+ }
516
750
  }
517
751
 
518
752
  function latestTask(tasks: InstallationTask[]): InstallationTask | null {
@@ -619,12 +853,11 @@ function parseEnvFile(
619
853
  );
620
854
  }
621
855
 
622
- async function patchInstallationSecrets(
856
+ async function readInstallationSecrets(
623
857
  packageDir: string,
624
- monorepoRoot: string,
625
858
  options: Options,
626
- ): Promise<void> {
627
- if (options.skipSecrets) return;
859
+ ): Promise<Array<{ name: string; value: string }>> {
860
+ if (options.skipSecrets) return [];
628
861
 
629
862
  const secretsPath = path.join(
630
863
  packageDir,
@@ -636,20 +869,42 @@ async function patchInstallationSecrets(
636
869
  console.log(
637
870
  `No secrets file found at ${secretsPath}; skipping secret patch.`,
638
871
  );
639
- return;
872
+ return [];
873
+ }
874
+
875
+ return parseEnvFile(await readFile(secretsPath, "utf8"));
876
+ }
877
+
878
+ function appendInstallationSecrets(
879
+ manifestContents: string,
880
+ secrets: Array<{ name: string; value: string }>,
881
+ ): string {
882
+ if (secrets.length === 0) return manifestContents;
883
+
884
+ const marker = " variableGroups:\n";
885
+ if (!manifestContents.includes(marker)) {
886
+ throw new Error("Installation manifest is missing variableGroups marker.");
640
887
  }
641
888
 
642
- const secrets = parseEnvFile(await readFile(secretsPath, "utf8"));
889
+ const secretYaml = [
890
+ " secrets:",
891
+ ...secrets.flatMap(({ name, value }) => [
892
+ ` - name: ${name}`,
893
+ ` value: ${JSON.stringify(value)}`,
894
+ ]),
895
+ "",
896
+ ].join("\n");
897
+
898
+ return manifestContents.replace(marker, `${secretYaml}${marker}`);
899
+ }
900
+
901
+ function patchExistingInstallationSecrets(
902
+ secrets: Array<{ name: string; value: string }>,
903
+ monorepoRoot: string,
904
+ ): void {
643
905
  if (secrets.length === 0) return;
644
906
 
645
- const secretEnv = secrets.map(({ name, value }) => ({
646
- key: name,
647
- value,
648
- isSecret: true,
649
- }));
650
- // Strategic merge preserves existing non-secret env entries while upserting
651
- // these generated secrets by key.
652
- const patch = JSON.stringify({ spec: { env: secretEnv, secrets } });
907
+ const patch = JSON.stringify({ spec: { secrets } });
653
908
  const result = spawnSync(
654
909
  "ryvn",
655
910
  [
@@ -672,7 +927,7 @@ async function patchInstallationSecrets(
672
927
  );
673
928
 
674
929
  if (result.status !== 0) {
675
- throw new Error(`Failed to patch secrets for ${APP_NAME}.`);
930
+ throw new Error(`Failed to patch generated app secrets for ${APP_NAME}.`);
676
931
  }
677
932
  }
678
933
 
@@ -776,6 +1031,7 @@ async function main(): Promise<void> {
776
1031
 
777
1032
  const repoSlug = await ensureGitHubRepo(monorepoRoot, options);
778
1033
  ensureCleanAndPushed(monorepoRoot, options);
1034
+ const commitSha = getCurrentCommitSha(monorepoRoot);
779
1035
 
780
1036
  const ryvnDir = path.join(packageDir, "deploy", "ryvn");
781
1037
  const serviceManifest = path.join(ryvnDir, `${APP_NAME}.service.yaml`);
@@ -802,27 +1058,46 @@ async function main(): Promise<void> {
802
1058
  upsertService(terraformServiceManifest, monorepoRoot);
803
1059
  upsertService(serviceManifest, monorepoRoot);
804
1060
 
805
- await triggerWorkflow(
1061
+ const terraformRelease = await requireReleaseForCurrentCommit(
1062
+ TERRAFORM_SERVICE_NAME,
806
1063
  repoSlug,
807
1064
  `${TERRAFORM_SERVICE_NAME}-ryvn-release.yaml`,
808
1065
  monorepoRoot,
809
1066
  options,
1067
+ commitSha,
810
1068
  );
1069
+ logReleaseSummary(TERRAFORM_SERVICE_NAME, terraformRelease);
811
1070
  upsertInstallation(
812
1071
  TERRAFORM_SERVICE_NAME,
813
1072
  terraformInstallationManifest,
814
1073
  monorepoRoot,
1074
+ commitSha,
815
1075
  );
816
1076
  await waitForTerraformApply(monorepoRoot, options);
817
1077
 
818
- await triggerWorkflow(
1078
+ const appRelease = await requireReleaseForCurrentCommit(
1079
+ APP_NAME,
819
1080
  repoSlug,
820
1081
  `${APP_NAME}-ryvn-release.yaml`,
821
1082
  monorepoRoot,
822
1083
  options,
1084
+ commitSha,
1085
+ );
1086
+ logReleaseSummary(APP_NAME, appRelease);
1087
+ const secrets = await readInstallationSecrets(packageDir, options);
1088
+ if (installationExists(APP_NAME, monorepoRoot)) {
1089
+ patchExistingInstallationSecrets(secrets, monorepoRoot);
1090
+ }
1091
+ upsertInstallation(
1092
+ APP_NAME,
1093
+ installationManifest,
1094
+ monorepoRoot,
1095
+ commitSha,
1096
+ appendInstallationSecrets(
1097
+ await readFile(installationManifest, "utf8"),
1098
+ secrets,
1099
+ ),
823
1100
  );
824
- upsertInstallation(APP_NAME, installationManifest, monorepoRoot);
825
- await patchInstallationSecrets(packageDir, monorepoRoot, options);
826
1101
  await waitForHealthy(monorepoRoot, options);
827
1102
 
828
1103
  await verifyDeployment();
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env tsx
2
+
3
+ import { execFileSync } from "node:child_process";
4
+ import { readFileSync, readdirSync, writeFileSync } from "node:fs";
5
+ import path from "node:path";
6
+ import { normalizeSchemaRelativeReferences } from "../src/drizzle/migrationSql";
7
+
8
+ const migrationsDir = path.resolve("src", "drizzle", "migrations");
9
+
10
+ function main(): void {
11
+ execFileSync("drizzle-kit", ["generate"], {
12
+ cwd: process.cwd(),
13
+ stdio: "inherit",
14
+ });
15
+
16
+ for (const fileName of readdirSync(migrationsDir)) {
17
+ if (!fileName.endsWith(".sql")) continue;
18
+
19
+ const filePath = path.join(migrationsDir, fileName);
20
+ const original = readFileSync(filePath, "utf8");
21
+ const normalized = normalizeSchemaRelativeReferences(original);
22
+ if (normalized !== original) {
23
+ writeFileSync(filePath, normalized);
24
+ }
25
+ }
26
+ }
27
+
28
+ main();
@@ -0,0 +1,24 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { normalizeSchemaRelativeReferences } from "../migrationSql";
3
+
4
+ describe("normalizeSchemaRelativeReferences", () => {
5
+ it("keeps generated foreign keys schema-relative for DATABASE_SCHEMA deploys", () => {
6
+ expect(
7
+ normalizeSchemaRelativeReferences(
8
+ 'ALTER TABLE "child" ADD CONSTRAINT "child_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."parent"("id") ON DELETE cascade;',
9
+ ),
10
+ ).toBe(
11
+ 'ALTER TABLE "child" ADD CONSTRAINT "child_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "parent"("id") ON DELETE cascade;',
12
+ );
13
+ });
14
+
15
+ it("leaves already schema-relative references unchanged", () => {
16
+ expect(
17
+ normalizeSchemaRelativeReferences(
18
+ 'ALTER TABLE "child" ADD CONSTRAINT "child_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "parent"("id");',
19
+ ),
20
+ ).toBe(
21
+ 'ALTER TABLE "child" ADD CONSTRAINT "child_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "parent"("id");',
22
+ );
23
+ });
24
+ });
@@ -0,0 +1,8 @@
1
+ const PUBLIC_SCHEMA_REFERENCE_PATTERN = /REFERENCES\s+"public"\."([^"]+)"/g;
2
+
3
+ export function normalizeSchemaRelativeReferences(sql: string): string {
4
+ return sql.replace(
5
+ PUBLIC_SCHEMA_REFERENCE_PATTERN,
6
+ (_match, tableName: string) => `REFERENCES "${tableName}"`,
7
+ );
8
+ }
@@ -0,0 +1,19 @@
1
+ import { type AppInngest, InngestService } from "./InngestService";
2
+ import { type ExampleEventPayload } from "./events/payloads/ExampleEventPayload";
3
+
4
+ type AppWorkflowClient = Pick<AppInngest, "send">;
5
+
6
+ export class AppWorkflowService {
7
+ public static create(): AppWorkflowService {
8
+ return new AppWorkflowService(InngestService.create().client);
9
+ }
10
+
11
+ public constructor(private inngestClient: AppWorkflowClient) {}
12
+
13
+ public async sendExampleEvent(payload: ExampleEventPayload): Promise<void> {
14
+ await this.inngestClient.send({
15
+ name: "app/example.event",
16
+ data: payload,
17
+ });
18
+ }
19
+ }
@@ -0,0 +1,19 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { AppWorkflowService } from "../AppWorkflowService";
3
+ import { AppEvents } from "../events/AppEvents";
4
+
5
+ describe("AppWorkflowService", () => {
6
+ it("sends event data in the shape validated by AppEvents", async () => {
7
+ const payload = { exampleId: "example-1" };
8
+ const send = vi.fn().mockResolvedValue(undefined);
9
+ const service = new AppWorkflowService({ send } as never);
10
+
11
+ await service.sendExampleEvent(payload);
12
+
13
+ expect(AppEvents["app/example.event"].parse(payload)).toEqual(payload);
14
+ expect(send).toHaveBeenCalledWith({
15
+ name: "app/example.event",
16
+ data: payload,
17
+ });
18
+ });
19
+ });
@@ -1,4 +1,3 @@
1
- import z from "zod";
2
1
  import { ExampleEventPayload } from "./payloads/ExampleEventPayload";
3
2
 
4
3
  /**
@@ -6,29 +5,24 @@ import { ExampleEventPayload } from "./payloads/ExampleEventPayload";
6
5
  *
7
6
  * Each event should have:
8
7
  * - A unique name (conventionally "app/event.name")
9
- * - A Zod schema for the event data
8
+ * - A Zod schema for event.data. Do not wrap the payload in another
9
+ * `{ data: ... }` object here.
10
10
  *
11
11
  * @example
12
12
  * ```ts
13
13
  * export const AppEvents = {
14
14
  * "app/user.created": z.object({
15
- * data: z.object({
16
- * userId: z.string(),
17
- * email: z.string(),
18
- * }),
15
+ * userId: z.string(),
16
+ * email: z.string(),
19
17
  * }),
20
18
  * "app/order.completed": z.object({
21
- * data: z.object({
22
- * orderId: z.string(),
23
- * total: z.number(),
24
- * }),
19
+ * orderId: z.string(),
20
+ * total: z.number(),
25
21
  * }),
26
22
  * };
27
23
  * ```
28
24
  */
29
25
  export const AppEvents = {
30
26
  // Example event - replace with your actual events
31
- "app/example.event": z.object({
32
- data: ExampleEventPayload.SCHEMA,
33
- }),
27
+ "app/example.event": ExampleEventPayload.SCHEMA,
34
28
  };
@@ -7,8 +7,6 @@ import z from "zod";
7
7
  export type ExampleEventPayload = z.infer<typeof ExampleEventPayload.SCHEMA>;
8
8
  export namespace ExampleEventPayload {
9
9
  export const SCHEMA = z.object({
10
- // Add your event properties here
11
- // exampleId: z.string(),
12
- // data: z.record(z.unknown()),
10
+ exampleId: z.string(),
13
11
  });
14
12
  }