@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.
- package/README.md +32 -113
- package/dist/agent-verifier/agent-workspace-verifier.mjs +2084 -57
- package/dist/agent-verifier/agent-workspace-verifier.mjs.map +1 -1
- package/dist/agent-verifier/{chunk-XQXDOBYB.mjs → chunk-4I2WWAPK.mjs} +27 -10
- package/dist/agent-verifier/chunk-4I2WWAPK.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-C3VW3DTA.mjs → chunk-BWBN2TDJ.mjs} +535 -633
- package/dist/agent-verifier/chunk-BWBN2TDJ.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-TAEQKBJB.mjs → chunk-GWRZRWCF.mjs} +1 -1
- package/dist/agent-verifier/chunk-GWRZRWCF.mjs.map +1 -0
- package/dist/agent-verifier/chunk-HUBV22JQ.mjs +89 -0
- package/dist/agent-verifier/chunk-HUBV22JQ.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-MW2QIWWA.mjs → chunk-KAA3B4DI.mjs} +215 -223
- package/dist/agent-verifier/chunk-KAA3B4DI.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-27EEIZCI.mjs → chunk-KDAQ4CZY.mjs} +34 -27
- package/dist/agent-verifier/chunk-KDAQ4CZY.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-IAYRNVUC.mjs → chunk-LMW66VBH.mjs} +2 -13
- package/dist/agent-verifier/{chunk-IAYRNVUC.mjs.map → chunk-LMW66VBH.mjs.map} +1 -1
- package/dist/agent-verifier/{chunk-776W3UGV.mjs → chunk-LROY5SN2.mjs} +7 -45
- package/dist/agent-verifier/chunk-LROY5SN2.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-H76MT5UR.mjs → chunk-M7UVBANQ.mjs} +2 -1
- package/dist/agent-verifier/chunk-M7UVBANQ.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-5NYBTZB4.mjs → chunk-MIRGCMUC.mjs} +112 -26
- package/dist/agent-verifier/chunk-MIRGCMUC.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-NAK77WXW.mjs → chunk-MYMVXTZT.mjs} +4 -5
- package/dist/agent-verifier/chunk-MYMVXTZT.mjs.map +1 -0
- package/dist/agent-verifier/chunk-OJFZVGEL.mjs +492 -0
- package/dist/agent-verifier/chunk-OJFZVGEL.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-XKCJBIRY.mjs → chunk-QD4SQNUP.mjs} +2 -2
- package/dist/agent-verifier/{chunk-QBAF7EYR.mjs → chunk-TTB7AIHZ.mjs} +4 -4
- package/dist/agent-verifier/{chunk-QBAF7EYR.mjs.map → chunk-TTB7AIHZ.mjs.map} +1 -1
- package/dist/agent-verifier/{chunk-F2DIOJJZ.mjs → chunk-XCQQIPCO.mjs} +5 -46
- package/dist/agent-verifier/chunk-XCQQIPCO.mjs.map +1 -0
- package/dist/agent-verifier/{global-config-NYCSCAUI.mjs → global-config-2NUESNEQ.mjs} +5 -5
- package/dist/agent-verifier/{keychain-backend-A3MRWLPF.mjs → keychain-backend-FF4I6ODB.mjs} +11 -6
- package/dist/agent-verifier/keychain-backend-FF4I6ODB.mjs.map +1 -0
- package/dist/agent-verifier/{local-files-QVJ2H3MH.mjs → local-files-OF4QFISU.mjs} +8 -8
- package/dist/agent-verifier/{chunk-UIOLGH4A.mjs → local-typecheck-DHVLM37Z.mjs} +4 -4
- package/dist/agent-verifier/local-typecheck-DHVLM37Z.mjs.map +1 -0
- package/dist/agent-verifier/{materialize-workspace-OZKOQCSQ.mjs → materialize-workspace-JBDL6LF4.mjs} +22 -22
- package/dist/agent-verifier/materialize-workspace-JBDL6LF4.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-Z6OZWUIZ.mjs → reducer-bundle-preflight-GLUJKTWU.mjs} +75 -24
- package/dist/agent-verifier/reducer-bundle-preflight-GLUJKTWU.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-YDIOW2BO.mjs → reducer-contract-preflight-WVQQPW5F.mjs} +7 -6
- package/dist/agent-verifier/reducer-contract-preflight-WVQQPW5F.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-ON62IGWK.mjs → reducer-native-test-harness-XQUPIT5D.mjs} +480 -703
- package/dist/agent-verifier/reducer-native-test-harness-XQUPIT5D.mjs.map +1 -0
- package/dist/agent-verifier/static-scaffold-U5DXE23S.mjs +24 -0
- package/dist/agent-verifier/{workspace-codegen-WPZHMATU.mjs → workspace-codegen-SPPVHURX.mjs} +3 -3
- package/dist/agent-verifier/{workspace-dependencies-B6A2ZX55.mjs → workspace-dependencies-5HEEKZFP.mjs} +5 -3
- package/dist/authoring-compatibility-internal.js +12 -0
- package/dist/chunk-5IYJOVUA.js +3902 -0
- package/dist/chunk-5IYJOVUA.js.map +1 -0
- package/dist/chunk-6NYVJYN4.js +313 -0
- package/dist/chunk-6NYVJYN4.js.map +1 -0
- package/dist/chunk-EQNBQVIW.js +204 -0
- package/dist/chunk-EQNBQVIW.js.map +1 -0
- package/dist/{chunk-M4SCKH5M.js → chunk-USZAPMQ4.js} +2488 -4993
- package/dist/chunk-USZAPMQ4.js.map +1 -0
- package/dist/{global-config-YBFEGJQG.js → global-config-RBMW7IVA.js} +3 -2
- package/dist/index.js +3099 -6188
- package/dist/index.js.map +1 -1
- package/dist/internal.js +35 -9
- package/dist/internal.js.map +1 -1
- package/dist/{keychain-backend-JHTXAKWC.js → keychain-backend-FSNTNTZE.js} +11 -6
- package/dist/keychain-backend-FSNTNTZE.js.map +1 -0
- package/package.json +9 -19
- package/release/authoring-release-set.json +38 -0
- package/skills/dreamboard/SKILL.md +32 -30
- package/skills/dreamboard/references/building-your-first-game.md +16 -16
- package/skills/dreamboard/references/cli.md +54 -54
- package/skills/dreamboard/references/manifest-authoring.md +11 -3
- package/skills/dreamboard/references/quickstart.md +19 -16
- package/skills/dreamboard/references/testing.md +6 -13
- package/dist/agent-verifier/chunk-27EEIZCI.mjs.map +0 -1
- package/dist/agent-verifier/chunk-5NYBTZB4.mjs.map +0 -1
- package/dist/agent-verifier/chunk-776W3UGV.mjs.map +0 -1
- package/dist/agent-verifier/chunk-C3VW3DTA.mjs.map +0 -1
- package/dist/agent-verifier/chunk-F2DIOJJZ.mjs.map +0 -1
- package/dist/agent-verifier/chunk-G42BGGG2.mjs +0 -70
- package/dist/agent-verifier/chunk-G42BGGG2.mjs.map +0 -1
- package/dist/agent-verifier/chunk-H76MT5UR.mjs.map +0 -1
- package/dist/agent-verifier/chunk-IDVQXGAO.mjs +0 -222
- package/dist/agent-verifier/chunk-IDVQXGAO.mjs.map +0 -1
- package/dist/agent-verifier/chunk-JO5AMVZU.mjs +0 -1744
- package/dist/agent-verifier/chunk-JO5AMVZU.mjs.map +0 -1
- package/dist/agent-verifier/chunk-KDBSVLCF.mjs +0 -624
- package/dist/agent-verifier/chunk-KDBSVLCF.mjs.map +0 -1
- package/dist/agent-verifier/chunk-MW2QIWWA.mjs.map +0 -1
- package/dist/agent-verifier/chunk-NAK77WXW.mjs.map +0 -1
- package/dist/agent-verifier/chunk-ON62IGWK.mjs.map +0 -1
- package/dist/agent-verifier/chunk-QZH6IEZS.mjs +0 -39
- package/dist/agent-verifier/chunk-QZH6IEZS.mjs.map +0 -1
- package/dist/agent-verifier/chunk-TAEQKBJB.mjs.map +0 -1
- package/dist/agent-verifier/chunk-UIOLGH4A.mjs.map +0 -1
- package/dist/agent-verifier/chunk-XQXDOBYB.mjs.map +0 -1
- package/dist/agent-verifier/chunk-YDIOW2BO.mjs.map +0 -1
- package/dist/agent-verifier/chunk-Z6OZWUIZ.mjs.map +0 -1
- package/dist/agent-verifier/compile-576O7TYP.mjs +0 -312
- package/dist/agent-verifier/compile-576O7TYP.mjs.map +0 -1
- package/dist/agent-verifier/keychain-backend-A3MRWLPF.mjs.map +0 -1
- package/dist/agent-verifier/local-typecheck-2JWG5IGL.mjs +0 -10
- package/dist/agent-verifier/materialize-workspace-OZKOQCSQ.mjs.map +0 -1
- package/dist/agent-verifier/reducer-bundle-preflight-7NYZF5ZT.mjs +0 -20
- package/dist/agent-verifier/reducer-contract-preflight-COD2CO22.mjs +0 -11
- package/dist/agent-verifier/reducer-native-test-harness-QC7HZUK4.mjs +0 -50
- package/dist/agent-verifier/static-scaffold-JBUE3ROP.mjs +0 -27
- package/dist/agent-verifier/sync-C6S3OGCD.mjs +0 -588
- package/dist/agent-verifier/sync-C6S3OGCD.mjs.map +0 -1
- package/dist/agent-verifier/test-Y5UGQV7J.mjs +0 -353
- package/dist/agent-verifier/test-Y5UGQV7J.mjs.map +0 -1
- package/dist/agent-verifier/workspace-codegen-WPZHMATU.mjs.map +0 -1
- package/dist/agent-verifier/workspace-dependencies-B6A2ZX55.mjs.map +0 -1
- package/dist/chunk-3NRROR4P.js +0 -432
- package/dist/chunk-3NRROR4P.js.map +0 -1
- package/dist/chunk-M4SCKH5M.js.map +0 -1
- package/dist/dev-host/components/drawer.tsx +0 -132
- package/dist/dev-host/components/input.tsx +0 -21
- package/dist/dev-host/dev-api-proxy-plugin.ts +0 -328
- package/dist/dev-host/dev-author-dom-warnings.ts +0 -100
- package/dist/dev-host/dev-diagnostics.ts +0 -62
- package/dist/dev-host/dev-fallback-stylesheet.ts +0 -53
- package/dist/dev-host/dev-hmr-guard-plugin.ts +0 -47
- package/dist/dev-host/dev-host-controller.ts +0 -674
- package/dist/dev-host/dev-host-player-query.ts +0 -17
- package/dist/dev-host/dev-host-session-transport.ts +0 -52
- package/dist/dev-host/dev-host-storage.ts +0 -56
- package/dist/dev-host/dev-log-relay-plugin.ts +0 -510
- package/dist/dev-host/dev-runtime-config.ts +0 -14
- package/dist/dev-host/dev-runtime-platform.ts +0 -335
- package/dist/dev-host/dev-virtual-modules-plugin.ts +0 -64
- package/dist/dev-host/host-main.css +0 -224
- package/dist/dev-host/host-main.tsx +0 -948
- package/dist/dev-host/index.html +0 -56
- package/dist/dev-host/lib/utils.ts +0 -6
- package/dist/dev-host/plugin-main.ts +0 -61
- package/dist/dev-host/plugin.html +0 -24
- package/dist/dev-host/shared-styles.css +0 -144
- package/dist/dev-host/start-dev-server.ts +0 -140
- package/dist/dev-host/virtual-modules.d.ts +0 -27
- package/dist/global-config-YBFEGJQG.js.map +0 -1
- package/dist/keychain-backend-JHTXAKWC.js.map +0 -1
- package/skills/dreamboard/scripts/events-extract.mjs +0 -218
- /package/dist/agent-verifier/{chunk-XKCJBIRY.mjs.map → chunk-QD4SQNUP.mjs.map} +0 -0
- /package/dist/agent-verifier/{global-config-NYCSCAUI.mjs.map → global-config-2NUESNEQ.mjs.map} +0 -0
- /package/dist/agent-verifier/{local-files-QVJ2H3MH.mjs.map → local-files-OF4QFISU.mjs.map} +0 -0
- /package/dist/agent-verifier/{local-typecheck-2JWG5IGL.mjs.map → static-scaffold-U5DXE23S.mjs.map} +0 -0
- /package/dist/agent-verifier/{reducer-bundle-preflight-7NYZF5ZT.mjs.map → workspace-codegen-SPPVHURX.mjs.map} +0 -0
- /package/dist/agent-verifier/{reducer-contract-preflight-COD2CO22.mjs.map → workspace-dependencies-5HEEKZFP.mjs.map} +0 -0
- /package/dist/{agent-verifier/reducer-native-test-harness-QC7HZUK4.mjs.map → authoring-compatibility-internal.js.map} +0 -0
- /package/dist/{agent-verifier/static-scaffold-JBUE3ROP.mjs.map → global-config-RBMW7IVA.js.map} +0 -0
- /package/{dist/scaffold → scaffold}/assets/static/app/tsconfig.framework.json +0 -0
- /package/{dist/scaffold → scaffold}/assets/static/app/tsconfig.json +0 -0
- /package/{dist/scaffold → scaffold}/assets/static/ui/index.tsx +0 -0
- /package/{dist/scaffold → scaffold}/assets/static/ui/style.css +0 -0
- /package/{dist/scaffold → scaffold}/assets/static/ui/tsconfig.framework.json +0 -0
- /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
|
-
}
|