@dreamboard-games/cli 0.1.30-alpha.4 → 0.1.30-alpha.40

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 (156) hide show
  1. package/README.md +32 -113
  2. package/dist/agent-verifier/agent-workspace-verifier.mjs +2084 -57
  3. package/dist/agent-verifier/agent-workspace-verifier.mjs.map +1 -1
  4. package/dist/agent-verifier/{chunk-XQXDOBYB.mjs → chunk-4I2WWAPK.mjs} +27 -10
  5. package/dist/agent-verifier/chunk-4I2WWAPK.mjs.map +1 -0
  6. package/dist/agent-verifier/{chunk-C3VW3DTA.mjs → chunk-BWBN2TDJ.mjs} +535 -633
  7. package/dist/agent-verifier/chunk-BWBN2TDJ.mjs.map +1 -0
  8. package/dist/agent-verifier/{chunk-TAEQKBJB.mjs → chunk-GWRZRWCF.mjs} +1 -1
  9. package/dist/agent-verifier/chunk-GWRZRWCF.mjs.map +1 -0
  10. package/dist/agent-verifier/chunk-HUBV22JQ.mjs +89 -0
  11. package/dist/agent-verifier/chunk-HUBV22JQ.mjs.map +1 -0
  12. package/dist/agent-verifier/{chunk-MW2QIWWA.mjs → chunk-KAA3B4DI.mjs} +215 -223
  13. package/dist/agent-verifier/chunk-KAA3B4DI.mjs.map +1 -0
  14. package/dist/agent-verifier/{chunk-27EEIZCI.mjs → chunk-KDAQ4CZY.mjs} +34 -27
  15. package/dist/agent-verifier/chunk-KDAQ4CZY.mjs.map +1 -0
  16. package/dist/agent-verifier/{chunk-IAYRNVUC.mjs → chunk-LMW66VBH.mjs} +2 -13
  17. package/dist/agent-verifier/{chunk-IAYRNVUC.mjs.map → chunk-LMW66VBH.mjs.map} +1 -1
  18. package/dist/agent-verifier/{chunk-776W3UGV.mjs → chunk-LROY5SN2.mjs} +7 -45
  19. package/dist/agent-verifier/chunk-LROY5SN2.mjs.map +1 -0
  20. package/dist/agent-verifier/{chunk-H76MT5UR.mjs → chunk-M7UVBANQ.mjs} +2 -1
  21. package/dist/agent-verifier/chunk-M7UVBANQ.mjs.map +1 -0
  22. package/dist/agent-verifier/{chunk-5NYBTZB4.mjs → chunk-MIRGCMUC.mjs} +112 -26
  23. package/dist/agent-verifier/chunk-MIRGCMUC.mjs.map +1 -0
  24. package/dist/agent-verifier/{chunk-NAK77WXW.mjs → chunk-MYMVXTZT.mjs} +4 -5
  25. package/dist/agent-verifier/chunk-MYMVXTZT.mjs.map +1 -0
  26. package/dist/agent-verifier/chunk-OJFZVGEL.mjs +492 -0
  27. package/dist/agent-verifier/chunk-OJFZVGEL.mjs.map +1 -0
  28. package/dist/agent-verifier/{chunk-XKCJBIRY.mjs → chunk-QD4SQNUP.mjs} +2 -2
  29. package/dist/agent-verifier/{chunk-QBAF7EYR.mjs → chunk-TTB7AIHZ.mjs} +4 -4
  30. package/dist/agent-verifier/{chunk-QBAF7EYR.mjs.map → chunk-TTB7AIHZ.mjs.map} +1 -1
  31. package/dist/agent-verifier/{chunk-F2DIOJJZ.mjs → chunk-XCQQIPCO.mjs} +5 -46
  32. package/dist/agent-verifier/chunk-XCQQIPCO.mjs.map +1 -0
  33. package/dist/agent-verifier/{global-config-NYCSCAUI.mjs → global-config-2NUESNEQ.mjs} +5 -5
  34. package/dist/agent-verifier/{keychain-backend-A3MRWLPF.mjs → keychain-backend-FF4I6ODB.mjs} +11 -6
  35. package/dist/agent-verifier/keychain-backend-FF4I6ODB.mjs.map +1 -0
  36. package/dist/agent-verifier/{local-files-QVJ2H3MH.mjs → local-files-OF4QFISU.mjs} +8 -8
  37. package/dist/agent-verifier/{chunk-UIOLGH4A.mjs → local-typecheck-DHVLM37Z.mjs} +4 -4
  38. package/dist/agent-verifier/local-typecheck-DHVLM37Z.mjs.map +1 -0
  39. package/dist/agent-verifier/{materialize-workspace-OZKOQCSQ.mjs → materialize-workspace-JBDL6LF4.mjs} +22 -22
  40. package/dist/agent-verifier/materialize-workspace-JBDL6LF4.mjs.map +1 -0
  41. package/dist/agent-verifier/{chunk-Z6OZWUIZ.mjs → reducer-bundle-preflight-GLUJKTWU.mjs} +75 -24
  42. package/dist/agent-verifier/reducer-bundle-preflight-GLUJKTWU.mjs.map +1 -0
  43. package/dist/agent-verifier/{chunk-YDIOW2BO.mjs → reducer-contract-preflight-WVQQPW5F.mjs} +7 -6
  44. package/dist/agent-verifier/reducer-contract-preflight-WVQQPW5F.mjs.map +1 -0
  45. package/dist/agent-verifier/{chunk-ON62IGWK.mjs → reducer-native-test-harness-XQUPIT5D.mjs} +480 -703
  46. package/dist/agent-verifier/reducer-native-test-harness-XQUPIT5D.mjs.map +1 -0
  47. package/dist/agent-verifier/static-scaffold-U5DXE23S.mjs +24 -0
  48. package/dist/agent-verifier/{workspace-codegen-WPZHMATU.mjs → workspace-codegen-SPPVHURX.mjs} +3 -3
  49. package/dist/agent-verifier/{workspace-dependencies-B6A2ZX55.mjs → workspace-dependencies-5HEEKZFP.mjs} +5 -3
  50. package/dist/authoring-compatibility-internal.js +12 -0
  51. package/dist/chunk-5IYJOVUA.js +3902 -0
  52. package/dist/chunk-5IYJOVUA.js.map +1 -0
  53. package/dist/chunk-6NYVJYN4.js +313 -0
  54. package/dist/chunk-6NYVJYN4.js.map +1 -0
  55. package/dist/chunk-EQNBQVIW.js +204 -0
  56. package/dist/chunk-EQNBQVIW.js.map +1 -0
  57. package/dist/{chunk-M4SCKH5M.js → chunk-USZAPMQ4.js} +2488 -4993
  58. package/dist/chunk-USZAPMQ4.js.map +1 -0
  59. package/dist/{global-config-YBFEGJQG.js → global-config-RBMW7IVA.js} +3 -2
  60. package/dist/index.js +3099 -6188
  61. package/dist/index.js.map +1 -1
  62. package/dist/internal.js +35 -9
  63. package/dist/internal.js.map +1 -1
  64. package/dist/{keychain-backend-JHTXAKWC.js → keychain-backend-FSNTNTZE.js} +11 -6
  65. package/dist/keychain-backend-FSNTNTZE.js.map +1 -0
  66. package/package.json +9 -19
  67. package/release/authoring-release-set.json +38 -0
  68. package/skills/dreamboard/SKILL.md +32 -30
  69. package/skills/dreamboard/references/building-your-first-game.md +16 -16
  70. package/skills/dreamboard/references/cli.md +54 -54
  71. package/skills/dreamboard/references/manifest-authoring.md +11 -3
  72. package/skills/dreamboard/references/quickstart.md +19 -16
  73. package/skills/dreamboard/references/testing.md +6 -13
  74. package/dist/agent-verifier/chunk-27EEIZCI.mjs.map +0 -1
  75. package/dist/agent-verifier/chunk-5NYBTZB4.mjs.map +0 -1
  76. package/dist/agent-verifier/chunk-776W3UGV.mjs.map +0 -1
  77. package/dist/agent-verifier/chunk-C3VW3DTA.mjs.map +0 -1
  78. package/dist/agent-verifier/chunk-F2DIOJJZ.mjs.map +0 -1
  79. package/dist/agent-verifier/chunk-G42BGGG2.mjs +0 -70
  80. package/dist/agent-verifier/chunk-G42BGGG2.mjs.map +0 -1
  81. package/dist/agent-verifier/chunk-H76MT5UR.mjs.map +0 -1
  82. package/dist/agent-verifier/chunk-IDVQXGAO.mjs +0 -222
  83. package/dist/agent-verifier/chunk-IDVQXGAO.mjs.map +0 -1
  84. package/dist/agent-verifier/chunk-JO5AMVZU.mjs +0 -1744
  85. package/dist/agent-verifier/chunk-JO5AMVZU.mjs.map +0 -1
  86. package/dist/agent-verifier/chunk-KDBSVLCF.mjs +0 -624
  87. package/dist/agent-verifier/chunk-KDBSVLCF.mjs.map +0 -1
  88. package/dist/agent-verifier/chunk-MW2QIWWA.mjs.map +0 -1
  89. package/dist/agent-verifier/chunk-NAK77WXW.mjs.map +0 -1
  90. package/dist/agent-verifier/chunk-ON62IGWK.mjs.map +0 -1
  91. package/dist/agent-verifier/chunk-QZH6IEZS.mjs +0 -39
  92. package/dist/agent-verifier/chunk-QZH6IEZS.mjs.map +0 -1
  93. package/dist/agent-verifier/chunk-TAEQKBJB.mjs.map +0 -1
  94. package/dist/agent-verifier/chunk-UIOLGH4A.mjs.map +0 -1
  95. package/dist/agent-verifier/chunk-XQXDOBYB.mjs.map +0 -1
  96. package/dist/agent-verifier/chunk-YDIOW2BO.mjs.map +0 -1
  97. package/dist/agent-verifier/chunk-Z6OZWUIZ.mjs.map +0 -1
  98. package/dist/agent-verifier/compile-576O7TYP.mjs +0 -312
  99. package/dist/agent-verifier/compile-576O7TYP.mjs.map +0 -1
  100. package/dist/agent-verifier/keychain-backend-A3MRWLPF.mjs.map +0 -1
  101. package/dist/agent-verifier/local-typecheck-2JWG5IGL.mjs +0 -10
  102. package/dist/agent-verifier/materialize-workspace-OZKOQCSQ.mjs.map +0 -1
  103. package/dist/agent-verifier/reducer-bundle-preflight-7NYZF5ZT.mjs +0 -20
  104. package/dist/agent-verifier/reducer-contract-preflight-COD2CO22.mjs +0 -11
  105. package/dist/agent-verifier/reducer-native-test-harness-QC7HZUK4.mjs +0 -50
  106. package/dist/agent-verifier/static-scaffold-JBUE3ROP.mjs +0 -27
  107. package/dist/agent-verifier/sync-C6S3OGCD.mjs +0 -588
  108. package/dist/agent-verifier/sync-C6S3OGCD.mjs.map +0 -1
  109. package/dist/agent-verifier/test-Y5UGQV7J.mjs +0 -353
  110. package/dist/agent-verifier/test-Y5UGQV7J.mjs.map +0 -1
  111. package/dist/agent-verifier/workspace-codegen-WPZHMATU.mjs.map +0 -1
  112. package/dist/agent-verifier/workspace-dependencies-B6A2ZX55.mjs.map +0 -1
  113. package/dist/chunk-3NRROR4P.js +0 -432
  114. package/dist/chunk-3NRROR4P.js.map +0 -1
  115. package/dist/chunk-M4SCKH5M.js.map +0 -1
  116. package/dist/dev-host/components/drawer.tsx +0 -132
  117. package/dist/dev-host/components/input.tsx +0 -21
  118. package/dist/dev-host/dev-api-proxy-plugin.ts +0 -328
  119. package/dist/dev-host/dev-author-dom-warnings.ts +0 -100
  120. package/dist/dev-host/dev-diagnostics.ts +0 -62
  121. package/dist/dev-host/dev-fallback-stylesheet.ts +0 -53
  122. package/dist/dev-host/dev-hmr-guard-plugin.ts +0 -47
  123. package/dist/dev-host/dev-host-controller.ts +0 -674
  124. package/dist/dev-host/dev-host-player-query.ts +0 -17
  125. package/dist/dev-host/dev-host-session-transport.ts +0 -52
  126. package/dist/dev-host/dev-host-storage.ts +0 -56
  127. package/dist/dev-host/dev-log-relay-plugin.ts +0 -510
  128. package/dist/dev-host/dev-runtime-config.ts +0 -14
  129. package/dist/dev-host/dev-runtime-platform.ts +0 -335
  130. package/dist/dev-host/dev-virtual-modules-plugin.ts +0 -64
  131. package/dist/dev-host/host-main.css +0 -224
  132. package/dist/dev-host/host-main.tsx +0 -948
  133. package/dist/dev-host/index.html +0 -56
  134. package/dist/dev-host/lib/utils.ts +0 -6
  135. package/dist/dev-host/plugin-main.ts +0 -61
  136. package/dist/dev-host/plugin.html +0 -24
  137. package/dist/dev-host/shared-styles.css +0 -144
  138. package/dist/dev-host/start-dev-server.ts +0 -140
  139. package/dist/dev-host/virtual-modules.d.ts +0 -27
  140. package/dist/global-config-YBFEGJQG.js.map +0 -1
  141. package/dist/keychain-backend-JHTXAKWC.js.map +0 -1
  142. package/skills/dreamboard/scripts/events-extract.mjs +0 -218
  143. /package/dist/agent-verifier/{chunk-XKCJBIRY.mjs.map → chunk-QD4SQNUP.mjs.map} +0 -0
  144. /package/dist/agent-verifier/{global-config-NYCSCAUI.mjs.map → global-config-2NUESNEQ.mjs.map} +0 -0
  145. /package/dist/agent-verifier/{local-files-QVJ2H3MH.mjs.map → local-files-OF4QFISU.mjs.map} +0 -0
  146. /package/dist/agent-verifier/{local-typecheck-2JWG5IGL.mjs.map → static-scaffold-U5DXE23S.mjs.map} +0 -0
  147. /package/dist/agent-verifier/{reducer-bundle-preflight-7NYZF5ZT.mjs.map → workspace-codegen-SPPVHURX.mjs.map} +0 -0
  148. /package/dist/agent-verifier/{reducer-contract-preflight-COD2CO22.mjs.map → workspace-dependencies-5HEEKZFP.mjs.map} +0 -0
  149. /package/dist/{agent-verifier/reducer-native-test-harness-QC7HZUK4.mjs.map → authoring-compatibility-internal.js.map} +0 -0
  150. /package/dist/{agent-verifier/static-scaffold-JBUE3ROP.mjs.map → global-config-RBMW7IVA.js.map} +0 -0
  151. /package/{dist/scaffold → scaffold}/assets/static/app/tsconfig.framework.json +0 -0
  152. /package/{dist/scaffold → scaffold}/assets/static/app/tsconfig.json +0 -0
  153. /package/{dist/scaffold → scaffold}/assets/static/ui/index.tsx +0 -0
  154. /package/{dist/scaffold → scaffold}/assets/static/ui/style.css +0 -0
  155. /package/{dist/scaffold → scaffold}/assets/static/ui/tsconfig.framework.json +0 -0
  156. /package/{dist/scaffold → scaffold}/assets/static/ui/tsconfig.json +0 -0
@@ -1,132 +0,0 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
- import { Drawer as DrawerPrimitive } from "vaul";
5
-
6
- import { cn } from "../lib/utils.js";
7
-
8
- function Drawer({
9
- ...props
10
- }: React.ComponentProps<typeof DrawerPrimitive.Root>) {
11
- return <DrawerPrimitive.Root data-slot="drawer" {...props} />;
12
- }
13
-
14
- function DrawerTrigger({
15
- ...props
16
- }: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
17
- return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />;
18
- }
19
-
20
- function DrawerPortal({
21
- ...props
22
- }: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
23
- return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />;
24
- }
25
-
26
- function DrawerClose({
27
- ...props
28
- }: React.ComponentProps<typeof DrawerPrimitive.Close>) {
29
- return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />;
30
- }
31
-
32
- function DrawerOverlay({
33
- className,
34
- ...props
35
- }: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {
36
- return (
37
- <DrawerPrimitive.Overlay
38
- data-slot="drawer-overlay"
39
- className={cn(
40
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
41
- className,
42
- )}
43
- {...props}
44
- />
45
- );
46
- }
47
-
48
- function DrawerContent({
49
- className,
50
- children,
51
- ...props
52
- }: React.ComponentProps<typeof DrawerPrimitive.Content>) {
53
- return (
54
- <DrawerPortal data-slot="drawer-portal">
55
- <DrawerOverlay />
56
- <DrawerPrimitive.Content
57
- data-slot="drawer-content"
58
- className={cn(
59
- "group/drawer-content bg-background fixed z-50 flex h-auto flex-col",
60
- "data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b",
61
- "data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg data-[vaul-drawer-direction=bottom]:border-t",
62
- "data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm",
63
- "data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm",
64
- className,
65
- )}
66
- {...props}
67
- >
68
- <div className="bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" />
69
- {children}
70
- </DrawerPrimitive.Content>
71
- </DrawerPortal>
72
- );
73
- }
74
-
75
- function DrawerHeader({ className, ...props }: React.ComponentProps<"div">) {
76
- return (
77
- <div
78
- data-slot="drawer-header"
79
- className={cn("flex flex-col gap-1.5 p-4", className)}
80
- {...props}
81
- />
82
- );
83
- }
84
-
85
- function DrawerFooter({ className, ...props }: React.ComponentProps<"div">) {
86
- return (
87
- <div
88
- data-slot="drawer-footer"
89
- className={cn("mt-auto flex flex-col gap-2 p-4", className)}
90
- {...props}
91
- />
92
- );
93
- }
94
-
95
- function DrawerTitle({
96
- className,
97
- ...props
98
- }: React.ComponentProps<typeof DrawerPrimitive.Title>) {
99
- return (
100
- <DrawerPrimitive.Title
101
- data-slot="drawer-title"
102
- className={cn("text-foreground font-semibold", className)}
103
- {...props}
104
- />
105
- );
106
- }
107
-
108
- function DrawerDescription({
109
- className,
110
- ...props
111
- }: React.ComponentProps<typeof DrawerPrimitive.Description>) {
112
- return (
113
- <DrawerPrimitive.Description
114
- data-slot="drawer-description"
115
- className={cn("text-muted-foreground text-sm", className)}
116
- {...props}
117
- />
118
- );
119
- }
120
-
121
- export {
122
- Drawer,
123
- DrawerPortal,
124
- DrawerOverlay,
125
- DrawerTrigger,
126
- DrawerClose,
127
- DrawerContent,
128
- DrawerHeader,
129
- DrawerFooter,
130
- DrawerTitle,
131
- DrawerDescription,
132
- };
@@ -1,21 +0,0 @@
1
- import * as React from "react";
2
-
3
- import { cn } from "../lib/utils.js";
4
-
5
- function Input({ className, type, ...props }: React.ComponentProps<"input">) {
6
- return (
7
- <input
8
- type={type}
9
- data-slot="input"
10
- className={cn(
11
- "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-white h-12 w-full min-w-0 border-2 border-border bg-white px-4 py-2 text-base font-sans outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-lg wobbly-border-md",
12
- "focus-visible:border-ring focus-visible:ring-ring/20 focus-visible:ring-[4px]",
13
- "aria-invalid:ring-destructive/20 aria-invalid:border-destructive",
14
- className,
15
- )}
16
- {...props}
17
- />
18
- );
19
- }
20
-
21
- export { Input };
@@ -1,328 +0,0 @@
1
- /**
2
- * Reverse-proxy plugin for the `dreamboard dev` Vite server.
3
- *
4
- * Every `/api/*` request the browser makes is intercepted here, run
5
- * through the Clerk OAuth refresh flow when needed, then forwarded to the configured upstream
6
- * backend with an `Authorization: Bearer <fresh-token>` header injected
7
- * on the wire. The access and refresh tokens never reach the browser.
8
- *
9
- * Failure contract:
10
- * - Permanent refresh failure (stored refresh token invalid) responds
11
- * with `401 { error: "session_invalid", message }` so the browser can
12
- * show a "Run dreamboard login" overlay instead of surfacing a
13
- * confusing upstream 401.
14
- * - Transient refresh failure (network blip) falls back to the snapshot
15
- * access token in `ResolvedConfig` when we still have one - this lets
16
- * a short outage degrade to "existing token" behavior instead of
17
- * blocking the iframe outright.
18
- * - Upstream connection failure responds with `502 { error:
19
- * "upstream_unavailable", message }`.
20
- *
21
- * The proxy is deliberately unaware of the session file / persist
22
- * endpoint / log relay; those live in `dev-log-relay-plugin.ts`.
23
- */
24
-
25
- import http, { type IncomingMessage, type ServerResponse } from "node:http";
26
- import https from "node:https";
27
- import { EventEmitter } from "node:events";
28
- import consola from "consola";
29
- import type { Plugin } from "vite";
30
- import {
31
- getAuthTokenExpiry,
32
- refreshResolvedAuthSession,
33
- } from "../config/resolve.js";
34
- import { resolveLocalHarnessAccessToken } from "../config/local-harness-auth.js";
35
- import type { ResolvedConfig } from "../types.js";
36
-
37
- export type ResolvedBearerOk = {
38
- readonly kind: "ok";
39
- readonly token: string | null;
40
- };
41
- export type ResolvedBearerPermanentFailure = {
42
- readonly kind: "permanent_invalid";
43
- readonly message: string;
44
- };
45
- export type ResolvedBearer = ResolvedBearerOk | ResolvedBearerPermanentFailure;
46
-
47
- export interface DevApiProxyPluginDeps {
48
- /**
49
- * Hook point for tests: resolve the bearer token (or a permanent
50
- * failure) synchronously once per request.
51
- */
52
- resolveBearer?: (config: ResolvedConfig) => Promise<ResolvedBearer>;
53
- /**
54
- * Hook point for tests: construct the underlying proxy. Allows
55
- * injecting an in-memory proxy that talks to a fake upstream.
56
- */
57
- createProxy?: (target: string) => DevApiProxy;
58
- }
59
-
60
- type DevApiProxy = {
61
- on(event: "error", listener: ProxyErrorListener): DevApiProxy;
62
- web(
63
- req: IncomingMessage,
64
- res: ServerResponse,
65
- options?: Record<string, never>,
66
- callback?: (err?: Error) => void,
67
- ): void;
68
- close(): void;
69
- };
70
-
71
- type ProxyErrorListener = (
72
- err: Error,
73
- req: IncomingMessage,
74
- res: ServerResponse,
75
- ) => void;
76
-
77
- export function createDevApiProxyPlugin(options: {
78
- config: ResolvedConfig;
79
- deps?: DevApiProxyPluginDeps;
80
- }): Plugin {
81
- const { config, deps } = options;
82
- const target = config.apiBaseUrl;
83
-
84
- return {
85
- name: "dreamboard-dev-api-proxy",
86
- configureServer(server) {
87
- const createProxy =
88
- deps?.createProxy ??
89
- ((proxyTarget: string) => createStreamingProxy(proxyTarget));
90
-
91
- const proxy = createProxy(target);
92
-
93
- proxy.on("error", (err, _req, res) => {
94
- consola.debug(`[dev-proxy] upstream error: ${formatUnknown(err)}`);
95
- if (res instanceof Object && "writeHead" in res && !res.headersSent) {
96
- respondUpstreamUnavailable(res as ServerResponse, err);
97
- } else if (res && "destroy" in res) {
98
- (res as { destroy: () => void }).destroy();
99
- }
100
- });
101
-
102
- const resolveBearer = deps?.resolveBearer ?? resolveDevBearer;
103
-
104
- // NOTE: we intentionally do NOT mount this middleware on `/api`.
105
- // Connect-style `middlewares.use(path, handler)` strips the mount
106
- // prefix from `req.url` before invoking the handler, which would
107
- // cause the proxy to forward `/sessions/.../status` instead of
108
- // `/api/sessions/.../status` and the backend would respond 404.
109
- // Filtering inside the handler keeps the full path intact.
110
- server.middlewares.use((req, res, next) => {
111
- if (!req.url || !isApiRequest(req.url)) {
112
- next();
113
- return;
114
- }
115
- void handleApiRequest({ req, res, config, proxy, resolveBearer });
116
- });
117
-
118
- server.httpServer?.once("close", () => {
119
- proxy.close();
120
- });
121
- },
122
- };
123
- }
124
-
125
- async function handleApiRequest(options: {
126
- req: IncomingMessage;
127
- res: ServerResponse;
128
- config: ResolvedConfig;
129
- proxy: DevApiProxy;
130
- resolveBearer: (config: ResolvedConfig) => Promise<ResolvedBearer>;
131
- }): Promise<void> {
132
- const { req, res, config, proxy, resolveBearer } = options;
133
- try {
134
- const bearer = await resolveBearer(config);
135
- if (bearer.kind === "permanent_invalid") {
136
- respondSessionInvalid(res, bearer.message);
137
- return;
138
- }
139
-
140
- if (bearer.token) {
141
- req.headers.authorization = `Bearer ${bearer.token}`;
142
- } else {
143
- delete req.headers.authorization;
144
- }
145
-
146
- proxy.web(req, res, {}, (err) => {
147
- if (!err) return;
148
- consola.debug(`[dev-proxy] forward error: ${formatUnknown(err)}`);
149
- if (!res.headersSent) {
150
- respondUpstreamUnavailable(res, err);
151
- }
152
- });
153
- } catch (err) {
154
- consola.debug(`[dev-proxy] pre-forward error: ${formatUnknown(err)}`);
155
- respondRefreshFailed(res, err);
156
- }
157
- }
158
-
159
- function createStreamingProxy(target: string): DevApiProxy {
160
- const targetUrl = new URL(target);
161
- const events = new EventEmitter();
162
- const activeRequests = new Set<http.ClientRequest>();
163
-
164
- const proxy: DevApiProxy = {
165
- on(event, listener) {
166
- events.on(event, listener);
167
- return proxy;
168
- },
169
- web(req, res, _options, callback) {
170
- const upstreamUrl = new URL(req.url ?? "/", targetUrl);
171
- const headers = createForwardHeaders(req, targetUrl);
172
-
173
- const client = targetUrl.protocol === "https:" ? https : http;
174
- const upstreamReq = client.request(
175
- upstreamUrl,
176
- {
177
- method: req.method,
178
- headers,
179
- },
180
- (upstreamRes) => {
181
- res.writeHead(
182
- upstreamRes.statusCode ?? 502,
183
- upstreamRes.statusMessage,
184
- upstreamRes.headers,
185
- );
186
- upstreamRes.pipe(res);
187
- },
188
- );
189
-
190
- activeRequests.add(upstreamReq);
191
-
192
- const handleError = (err: Error) => {
193
- activeRequests.delete(upstreamReq);
194
- callback?.(err);
195
- events.emit("error", err, req, res);
196
- };
197
-
198
- upstreamReq.on("error", handleError);
199
- upstreamReq.on("close", () => activeRequests.delete(upstreamReq));
200
- req.on("aborted", () => upstreamReq.destroy());
201
- req.pipe(upstreamReq);
202
- },
203
- close() {
204
- for (const request of activeRequests) {
205
- request.destroy();
206
- }
207
- activeRequests.clear();
208
- events.removeAllListeners();
209
- },
210
- };
211
-
212
- return proxy;
213
- }
214
-
215
- function createForwardHeaders(
216
- req: IncomingMessage,
217
- targetUrl: URL,
218
- ): http.OutgoingHttpHeaders {
219
- const headers: http.OutgoingHttpHeaders = { ...req.headers };
220
-
221
- // Browser requests are same-origin with the dev host. Once the CLI proxies
222
- // them to the backend, they are server-to-server requests; forwarding the
223
- // browser Origin from a Cloudflare/LAN host makes backend CORS reject valid
224
- // dev traffic.
225
- delete headers.origin;
226
- delete headers["access-control-request-headers"];
227
- delete headers["access-control-request-method"];
228
-
229
- headers.host = targetUrl.host;
230
- headers["x-forwarded-host"] = req.headers.host;
231
- headers["x-forwarded-proto"] = targetUrl.protocol.replace(":", "");
232
-
233
- return headers;
234
- }
235
-
236
- export async function resolveDevBearer(
237
- config: ResolvedConfig,
238
- ): Promise<ResolvedBearer> {
239
- const localHarnessToken = resolveLocalHarnessAccessToken(config);
240
- if (localHarnessToken) {
241
- return { kind: "ok", token: localHarnessToken };
242
- }
243
-
244
- // Env/flag-provided tokens are not owned by `CredentialStore` and must
245
- // not be rotated. Forward them as-is.
246
- if (!usesStoredSession(config)) {
247
- return { kind: "ok", token: config.authToken ?? null };
248
- }
249
-
250
- const expiry = getAuthTokenExpiry(config.authToken);
251
- const isExpired = expiry !== null && expiry.getTime() <= Date.now();
252
- if (!isExpired) {
253
- return { kind: "ok", token: config.authToken ?? null };
254
- }
255
-
256
- if (!config.refreshToken) {
257
- return {
258
- kind: "permanent_invalid",
259
- message:
260
- "Stored Dreamboard session is expired or invalid. Run `dreamboard login` to authenticate again.",
261
- };
262
- }
263
-
264
- const refreshed = await refreshResolvedAuthSession(config);
265
- return {
266
- kind: "ok",
267
- token: refreshed?.accessToken ?? config.authToken ?? null,
268
- };
269
- }
270
-
271
- function usesStoredSession(config: ResolvedConfig): boolean {
272
- return (
273
- config.authTokenSource === "global" &&
274
- config.refreshTokenSource === "global"
275
- );
276
- }
277
-
278
- function respondSessionInvalid(res: ServerResponse, message: string): void {
279
- if (res.headersSent) return;
280
- res.statusCode = 401;
281
- res.setHeader("content-type", "application/json");
282
- res.end(
283
- JSON.stringify({
284
- error: "session_invalid",
285
- message,
286
- }),
287
- );
288
- }
289
-
290
- function respondUpstreamUnavailable(res: ServerResponse, error: unknown): void {
291
- if (res.headersSent) return;
292
- res.statusCode = 502;
293
- res.setHeader("content-type", "application/json");
294
- res.end(
295
- JSON.stringify({
296
- error: "upstream_unavailable",
297
- message: formatUnknown(error),
298
- }),
299
- );
300
- }
301
-
302
- function respondRefreshFailed(res: ServerResponse, error: unknown): void {
303
- if (res.headersSent) return;
304
- res.statusCode = 502;
305
- res.setHeader("content-type", "application/json");
306
- res.end(
307
- JSON.stringify({
308
- error: "refresh_failed",
309
- message: formatUnknown(error),
310
- }),
311
- );
312
- }
313
-
314
- function isApiRequest(url: string): boolean {
315
- return url === "/api" || url.startsWith("/api/") || url.startsWith("/api?");
316
- }
317
-
318
- function formatUnknown(value: unknown): string {
319
- if (value instanceof Error) {
320
- return value.message || value.name || "Unknown error";
321
- }
322
- if (typeof value === "string") return value;
323
- try {
324
- return JSON.stringify(value);
325
- } catch {
326
- return String(value);
327
- }
328
- }
@@ -1,100 +0,0 @@
1
- type ZoneItemElement = Pick<Element, "getAttribute" | "querySelector">;
2
-
3
- type ZoneItemRoot = Pick<ParentNode, "querySelectorAll">;
4
-
5
- export interface PlayableZoneItemWarning {
6
- key: string;
7
- message: string;
8
- }
9
-
10
- const PLAYABLE_ZONE_ITEM_SELECTOR =
11
- '[data-dreamboard-zone-item][data-playable="true"]';
12
- const INTERACTION_CARD_INPUT_SELECTOR =
13
- "[data-dreamboard-interaction-card-input]";
14
-
15
- export function collectPlayableZoneItemWarnings(
16
- root: ZoneItemRoot,
17
- ): PlayableZoneItemWarning[] {
18
- const warnings: PlayableZoneItemWarning[] = [];
19
- for (const item of root.querySelectorAll(PLAYABLE_ZONE_ITEM_SELECTOR)) {
20
- const element = item as ZoneItemElement;
21
- if (element.querySelector(INTERACTION_CARD_INPUT_SELECTOR)) {
22
- continue;
23
- }
24
-
25
- const zone = element.getAttribute("data-zone") ?? "unknown";
26
- const cardId = element.getAttribute("data-card-id") ?? "unknown";
27
- const cardType = element.getAttribute("data-card-type") ?? null;
28
- const key = `${zone}:${cardId}`;
29
- const cardLabel =
30
- cardType && cardType !== cardId
31
- ? `'${cardId}' (${cardType})`
32
- : `'${cardId}'`;
33
- warnings.push({
34
- key,
35
- message: [
36
- `[dreamboard] Playable card ${cardLabel} in zone '${zone}' rendered without an interaction card input.`,
37
- "This usually means the UI rendered a raw Card/custom tile instead of <handSurface.Card>.",
38
- "Render the surface card consistently and let Dreamboard disable unavailable interactions.",
39
- ].join(" "),
40
- });
41
- }
42
- return warnings;
43
- }
44
-
45
- export function installPlayableZoneItemWarnings(
46
- options: {
47
- root?: Document;
48
- warn?: (...args: unknown[]) => void;
49
- MutationObserverImpl?: typeof MutationObserver;
50
- schedule?: (callback: () => void) => void;
51
- } = {},
52
- ): () => void {
53
- const root = options.root ?? document;
54
- const warn = options.warn ?? console.warn.bind(console);
55
- const MutationObserverImpl =
56
- options.MutationObserverImpl ?? window.MutationObserver;
57
- const schedule =
58
- options.schedule ??
59
- ((callback) => {
60
- window.requestAnimationFrame(callback);
61
- });
62
- const emitted = new Set<string>();
63
- let scheduled = false;
64
-
65
- const scan = () => {
66
- scheduled = false;
67
- for (const warning of collectPlayableZoneItemWarnings(root)) {
68
- if (emitted.has(warning.key)) {
69
- continue;
70
- }
71
- emitted.add(warning.key);
72
- warn(warning.message);
73
- }
74
- };
75
-
76
- const requestScan = () => {
77
- if (scheduled) {
78
- return;
79
- }
80
- scheduled = true;
81
- schedule(scan);
82
- };
83
-
84
- const observer = new MutationObserverImpl(requestScan);
85
- observer.observe(root.documentElement, {
86
- childList: true,
87
- subtree: true,
88
- attributes: true,
89
- attributeFilter: [
90
- "data-dreamboard-zone-item",
91
- "data-playable",
92
- "data-dreamboard-interaction-card-input",
93
- ],
94
- });
95
- requestScan();
96
-
97
- return () => {
98
- observer.disconnect();
99
- };
100
- }
@@ -1,62 +0,0 @@
1
- import type { LoggerLike } from "@dreamboard-games/ui-host-runtime/runtime";
2
-
3
- export type DevLogEnvelope = {
4
- source: "host" | "plugin" | "sse";
5
- level: "log" | "warn" | "error" | "info";
6
- message: string;
7
- };
8
-
9
- export type DevDiagnosticsLevel = "errors" | "verbose";
10
-
11
- export function resolveDevDiagnosticsLevel(
12
- debug: boolean,
13
- ): DevDiagnosticsLevel {
14
- return debug ? "verbose" : "errors";
15
- }
16
-
17
- export function createDevDiagnosticsLogger(
18
- level: DevDiagnosticsLevel,
19
- ): LoggerLike {
20
- if (level === "verbose") {
21
- return {
22
- log: (...args) => console.log(...args),
23
- warn: (...args) => console.warn(...args),
24
- error: (...args) => console.error(...args),
25
- };
26
- }
27
-
28
- return {
29
- log: () => {},
30
- warn: () => {},
31
- error: (...args) => console.error(...args),
32
- };
33
- }
34
-
35
- export function shouldRelayDevLog(
36
- diagnosticsLevel: DevDiagnosticsLevel,
37
- payload: DevLogEnvelope,
38
- ): boolean {
39
- return (
40
- diagnosticsLevel === "verbose" ||
41
- payload.level === "warn" ||
42
- payload.level === "error"
43
- );
44
- }
45
-
46
- export function formatConsoleArgs(args: unknown[]): string {
47
- return args.map((value) => stringifyForRelay(value)).join(" ");
48
- }
49
-
50
- export function stringifyForRelay(value: unknown): string {
51
- if (typeof value === "string") {
52
- return value;
53
- }
54
- if (value instanceof Error) {
55
- return value.stack ?? value.message;
56
- }
57
- try {
58
- return JSON.stringify(value);
59
- } catch {
60
- return String(value);
61
- }
62
- }
@@ -1,53 +0,0 @@
1
- import { createHash, randomUUID } from "node:crypto";
2
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
3
- import path from "node:path";
4
- import { normalizePath } from "vite";
5
-
6
- export function prepareFallbackStylesheet(options: {
7
- projectRoot: string;
8
- repoRoot: string;
9
- }): string | null {
10
- const projectStylePath = path.resolve(options.projectRoot, "ui/style.css");
11
- if (existsSync(projectStylePath)) {
12
- return null;
13
- }
14
-
15
- const generatedDir = path.resolve(options.repoRoot, ".dreamboard-dev");
16
- mkdirSync(generatedDir, { recursive: true });
17
-
18
- const fingerprint = createHash("sha256")
19
- .update(options.projectRoot, "utf8")
20
- .digest("hex")
21
- .slice(0, 8);
22
- const instanceId = randomUUID().replaceAll("-", "").slice(0, 8);
23
- const generatedPath = path.join(
24
- generatedDir,
25
- `plugin-styles-${fingerprint}-${instanceId}.css`,
26
- );
27
- const sharedStylesPath = normalizePath(
28
- path.resolve(
29
- options.repoRoot,
30
- "node_modules/@dreamboard-games/sdk/dist/ui/plugin-styles.css",
31
- ),
32
- );
33
- const uiSourcePath = normalizePath(
34
- path.resolve(options.projectRoot, "ui/**/*.{ts,tsx}"),
35
- );
36
- const sharedSourcePath = normalizePath(
37
- path.resolve(options.projectRoot, "shared/**/*.{ts,tsx}"),
38
- );
39
-
40
- writeFileSync(
41
- generatedPath,
42
- [
43
- `@import "/@fs/${sharedStylesPath}";`,
44
- "",
45
- `@source "${uiSourcePath}";`,
46
- `@source "${sharedSourcePath}";`,
47
- "",
48
- ].join("\n"),
49
- "utf8",
50
- );
51
-
52
- return generatedPath;
53
- }