@scenar/cli 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/example-bundle/assets/index-BU7trbqC.js +147 -0
- package/example-bundle/assets/style-COSwl6WP.css +1 -0
- package/example-bundle/index.html +6 -2
- package/example-bundle/pack-manifest.json +8 -8
- package/package.json +4 -9
- package/src/__tests__/embed-snippet.test.ts +1 -1
- package/src/__tests__/install-command.test.ts +97 -0
- package/src/__tests__/local-url.test.ts +17 -0
- package/src/__tests__/pack-generate-embed-entry.test.ts +22 -0
- package/src/__tests__/run-install.test.ts +222 -0
- package/src/__tests__/scenario-validator.test.ts +0 -1
- package/src/api.d.ts +3 -1
- package/src/api.d.ts.map +1 -1
- package/src/api.js +3 -1
- package/src/api.js.map +1 -1
- package/src/api.ts +10 -1
- package/src/commands/install.d.ts +3 -0
- package/src/commands/install.d.ts.map +1 -0
- package/src/commands/install.js +67 -0
- package/src/commands/install.js.map +1 -0
- package/src/commands/install.ts +89 -0
- package/src/commands/pack.js +2 -2
- package/src/commands/pack.js.map +1 -1
- package/src/commands/pack.ts +2 -2
- package/src/commands/serve.d.ts.map +1 -1
- package/src/commands/serve.js +1 -2
- package/src/commands/serve.js.map +1 -1
- package/src/commands/serve.ts +1 -2
- package/src/commands/try.js +1 -1
- package/src/commands/try.ts +1 -1
- package/src/embed/embed-snippet.d.ts.map +1 -0
- package/src/embed/embed-snippet.js.map +1 -0
- package/src/index.d.ts.map +1 -1
- package/src/index.js +3 -5
- package/src/index.js.map +1 -1
- package/src/index.ts +3 -5
- package/src/install/run-install.d.ts +72 -0
- package/src/install/run-install.d.ts.map +1 -0
- package/src/install/run-install.js +378 -0
- package/src/install/run-install.js.map +1 -0
- package/src/install/run-install.ts +461 -0
- package/src/install/scaffold.d.ts +29 -0
- package/src/install/scaffold.d.ts.map +1 -0
- package/src/install/scaffold.js +231 -0
- package/src/install/scaffold.js.map +1 -0
- package/src/install/scaffold.ts +253 -0
- package/src/pack/generate-embed-entry.d.ts +11 -0
- package/src/pack/generate-embed-entry.d.ts.map +1 -1
- package/src/pack/generate-embed-entry.js +34 -0
- package/src/pack/generate-embed-entry.js.map +1 -1
- package/src/pack/generate-embed-entry.ts +35 -0
- package/src/pack/pack-manifest.d.ts +1 -1
- package/src/pack/pack-manifest.d.ts.map +1 -1
- package/src/pack/pack-manifest.ts +1 -1
- package/src/publish/publish-flow.d.ts +3 -3
- package/src/publish/publish-flow.js +3 -3
- package/src/publish/publish-flow.ts +3 -3
- package/src/publish/run-publish.js +1 -1
- package/src/publish/run-publish.js.map +1 -1
- package/src/publish/run-publish.ts +1 -1
- package/src/render/detect-render-export.d.ts +2 -2
- package/src/render/detect-render-export.js +2 -2
- package/src/render/detect-render-export.ts +2 -2
- package/src/serve/local-url.d.ts +8 -0
- package/src/serve/local-url.d.ts.map +1 -0
- package/src/serve/local-url.js +21 -0
- package/src/serve/local-url.js.map +1 -0
- package/src/serve/local-url.ts +20 -0
- package/src/serve/run-serve.js +2 -2
- package/src/serve/run-serve.js.map +1 -1
- package/src/serve/run-serve.ts +2 -2
- package/tsconfig.tsbuildinfo +1 -1
- package/example-bundle/assets/index-BLQ14SEo.js +0 -147
- package/example-bundle/assets/style-Dnzx_jUO.css +0 -1
- package/src/__tests__/deploy-command.test.ts +0 -45
- package/src/__tests__/deploy-flow.test.ts +0 -125
- package/src/__tests__/deploy-upload.test.ts +0 -39
- package/src/commands/deploy.d.ts +0 -3
- package/src/commands/deploy.d.ts.map +0 -1
- package/src/commands/deploy.js +0 -150
- package/src/commands/deploy.js.map +0 -1
- package/src/commands/deploy.ts +0 -186
- package/src/commands/preview.d.ts +0 -3
- package/src/commands/preview.d.ts.map +0 -1
- package/src/commands/preview.js +0 -93
- package/src/commands/preview.js.map +0 -1
- package/src/commands/preview.ts +0 -138
- package/src/deploy/client.d.ts +0 -20
- package/src/deploy/client.d.ts.map +0 -1
- package/src/deploy/client.js +0 -22
- package/src/deploy/client.js.map +0 -1
- package/src/deploy/client.ts +0 -28
- package/src/deploy/deploy-flow.d.ts +0 -78
- package/src/deploy/deploy-flow.d.ts.map +0 -1
- package/src/deploy/deploy-flow.js +0 -53
- package/src/deploy/deploy-flow.js.map +0 -1
- package/src/deploy/deploy-flow.ts +0 -115
- package/src/deploy/embed-snippet.d.ts.map +0 -1
- package/src/deploy/embed-snippet.js.map +0 -1
- package/src/deploy/upload.d.ts +0 -10
- package/src/deploy/upload.d.ts.map +0 -1
- package/src/deploy/upload.js +0 -23
- package/src/deploy/upload.js.map +0 -1
- package/src/deploy/upload.ts +0 -29
- /package/src/{deploy → embed}/embed-snippet.d.ts +0 -0
- /package/src/{deploy → embed}/embed-snippet.js +0 -0
- /package/src/{deploy → embed}/embed-snippet.ts +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
@source "./**/*.js";@layer scenar{.scenar{--scenar-surface: #ffffff;--scenar-border: #e5e7eb;--scenar-foreground: #0f172a;--scenar-muted-foreground: #64748b;--scenar-card: #ffffff;--scenar-accent: #f1f5f9;--scenar-primary: #0f172a;--scenar-ring: #0f172a}.scenar.dark{--scenar-surface: #0f172a;--scenar-border: #1e293b;--scenar-foreground: #f8fafc;--scenar-muted-foreground: #94a3b8;--scenar-card: #1e293b;--scenar-accent: #1e293b;--scenar-primary: #f8fafc;--scenar-ring: #f8fafc}}/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-neutral-900:oklch(20.5% 0 0);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-4xl:56rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wider:.05em;--leading-relaxed:1.625;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--drop-shadow-sm:0 1px 2px #00000026;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-foreground:var(--scenar-foreground);--color-border:var(--scenar-border)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.visible{visibility:visible}.absolute{position:absolute}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing) * 0)}.inset-x-0{inset-inline:calc(var(--spacing) * 0)}.inset-y-0{inset-block:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.top-0{top:calc(var(--spacing) * 0)}.top-1{top:calc(var(--spacing) * 1)}.top-1\/2{top:50%}.top-3{top:calc(var(--spacing) * 3)}.-right-px{right:-1px}.right-0{right:calc(var(--spacing) * 0)}.-bottom-px{bottom:-1px}.bottom-0{bottom:calc(var(--spacing) * 0)}.bottom-1{bottom:calc(var(--spacing) * 1)}.bottom-2{bottom:calc(var(--spacing) * 2)}.bottom-full{bottom:100%}.left-0{left:calc(var(--spacing) * 0)}.left-1\/2{left:50%}.left-4{left:calc(var(--spacing) * 4)}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mr-0\.5{margin-right:calc(var(--spacing) * .5)}.mr-1{margin-right:calc(var(--spacing) * 1)}.mr-1\.5{margin-right:calc(var(--spacing) * 1.5)}.mr-3{margin-right:calc(var(--spacing) * 3)}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.ml-0\.5{margin-left:calc(var(--spacing) * .5)}.ml-1{margin-left:calc(var(--spacing) * 1)}.ml-4{margin-left:calc(var(--spacing) * 4)}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-1{height:calc(var(--spacing) * 1)}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2\.5{height:calc(var(--spacing) * 2.5)}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-10{height:calc(var(--spacing) * 10)}.h-14{height:calc(var(--spacing) * 14)}.h-16{height:calc(var(--spacing) * 16)}.h-\[7px\]{height:7px}.h-\[14px\]{height:14px}.h-\[15px\]{height:15px}.h-full{height:100%}.min-h-0{min-height:calc(var(--spacing) * 0)}.w-0\.5{width:calc(var(--spacing) * .5)}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-1\/3{width:33.3333%}.w-2\.5{width:calc(var(--spacing) * 2.5)}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-6{width:calc(var(--spacing) * 6)}.w-7{width:calc(var(--spacing) * 7)}.w-8{width:calc(var(--spacing) * 8)}.w-9{width:calc(var(--spacing) * 9)}.w-14{width:calc(var(--spacing) * 14)}.w-16{width:calc(var(--spacing) * 16)}.w-\[3px\]{width:3px}.w-\[7px\]{width:7px}.w-\[14px\]{width:14px}.w-\[15px\]{width:15px}.w-\[42px\]{width:42px}.w-\[160px\]{width:160px}.w-full{width:100%}.w-px{width:1px}.max-w-4xl{max-width:var(--container-4xl)}.max-w-\[80\%\]{max-width:80%}.max-w-\[180px\]{max-width:180px}.max-w-\[200px\]{max-width:200px}.min-w-0{min-width:calc(var(--spacing) * 0)}.min-w-\[2rem\]{min-width:2rem}.flex-1{flex:1}.shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-x-2{--tw-translate-x:calc(var(--spacing) * -2);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-x-px{--tw-translate-x:-1px;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-2{--tw-translate-y:calc(var(--spacing) * -2);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-px{--tw-translate-y:-1px;translate:var(--tw-translate-x) var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.cursor-pointer{cursor:pointer}.resize{resize:both}.flex-col{flex-direction:column}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-\[5px\]{gap:5px}:where(.space-y-2\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-sm{border-radius:var(--radius-sm)}.rounded-t-lg{border-top-left-radius:var(--radius-lg);border-top-right-radius:var(--radius-lg)}.rounded-r{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-\[\#1a1a1a\]{border-color:#1a1a1a}.border-\[\#1e1e1e\]{border-color:#1e1e1e}.border-\[\#2a2a2e\]{border-color:#2a2a2e}.border-\[\#2d2d2d\]{border-color:#2d2d2d}.border-\[\#3a3a3a\]{border-color:#3a3a3a}.border-\[\#3a3f47\]{border-color:#3a3f47}.border-\[\#3c4043\]{border-color:#3c4043}.border-\[\#007acc\]{border-color:#007acc}.border-\[\#ff6c37\]{border-color:#ff6c37}.border-\[var\(--scenar-border\)\],.border-border{border-color:var(--scenar-border)}.border-primary\/50{border-color:var(--scenar-primary)}@supports (color:color-mix(in lab,red,red)){.border-primary\/50{border-color:color-mix(in oklab,var(--scenar-primary) 50%,transparent)}}.border-transparent{border-color:#0000}.bg-\[\#0b93f6\]{background-color:#0b93f6}.bg-\[\#1a1a1a\]{background-color:#1a1a1a}.bg-\[\#1a73e8\]{background-color:#1a73e8}.bg-\[\#1c1c1c\]{background-color:#1c1c1c}.bg-\[\#1e1e1e\]{background-color:#1e1e1e}.bg-\[\#2a2a2e\]{background-color:#2a2a2e}.bg-\[\#2c2c2c\]{background-color:#2c2c2c}.bg-\[\#2d2d2d\]{background-color:#2d2d2d}.bg-\[\#3b82f6\]{background-color:#3b82f6}.bg-\[\#3b82f6\]\/10{background-color:#3b82f61a}.bg-\[\#3b82f6\]\/15{background-color:#3b82f626}.bg-\[\#5f6368\]{background-color:#5f6368}.bg-\[\#007acc\]{background-color:#007acc}.bg-\[\#007acc\]\/10{background-color:#007acc1a}.bg-\[\#22c55e\]{background-color:#22c55e}.bg-\[\#22c55e\]\/15{background-color:#22c55e26}.bg-\[\#28c840\]{background-color:#28c840}.bg-\[\#94a3b8\]\/15{background-color:#94a3b826}.bg-\[\#181b1f\]{background-color:#181b1f}.bg-\[\#999\]{background-color:#999}.bg-\[\#3274d9\]{background-color:#3274d9}.bg-\[\#04395e\]{background-color:#04395e}.bg-\[\#6366f1\]{background-color:#6366f1}.bg-\[\#35363a\]{background-color:#35363a}.bg-\[\#111217\]{background-color:#111217}.bg-\[\#202124\]{background-color:#202124}.bg-\[\#252526\]{background-color:#252526}.bg-\[\#323232\]{background-color:#323232}.bg-\[\#323233\]{background-color:#323233}.bg-\[\#333333\]{background-color:#333}.bg-\[\#404040\]{background-color:#404040}.bg-\[\#eab308\]\/15{background-color:#eab30826}.bg-\[\#ef4444\]\/15{background-color:#ef444426}.bg-\[\#febc2e\]{background-color:#febc2e}.bg-\[\#ff5f57\]{background-color:#ff5f57}.bg-\[var\(--scenar-accent\)\]{background-color:var(--scenar-accent)}.bg-\[var\(--scenar-card\)\]{background-color:var(--scenar-card)}.bg-\[var\(--scenar-surface\)\],.bg-background{background-color:var(--scenar-surface)}.bg-black{background-color:var(--color-black)}.bg-black\/60{background-color:#0009}@supports (color:color-mix(in lab,red,red)){.bg-black\/60{background-color:color-mix(in oklab,var(--color-black) 60%,transparent)}}.bg-black\/70{background-color:#000000b3}@supports (color:color-mix(in lab,red,red)){.bg-black\/70{background-color:color-mix(in oklab,var(--color-black) 70%,transparent)}}.bg-card{background-color:var(--scenar-card)}.bg-foreground\/25{background-color:var(--scenar-foreground)}@supports (color:color-mix(in lab,red,red)){.bg-foreground\/25{background-color:color-mix(in oklab,var(--scenar-foreground) 25%,transparent)}}.bg-foreground\/60{background-color:var(--scenar-foreground)}@supports (color:color-mix(in lab,red,red)){.bg-foreground\/60{background-color:color-mix(in oklab,var(--scenar-foreground) 60%,transparent)}}.bg-foreground\/80{background-color:var(--scenar-foreground)}@supports (color:color-mix(in lab,red,red)){.bg-foreground\/80{background-color:color-mix(in oklab,var(--scenar-foreground) 80%,transparent)}}.bg-white{background-color:var(--color-white)}.bg-white\/80{background-color:#fffc}@supports (color:color-mix(in lab,red,red)){.bg-white\/80{background-color:color-mix(in oklab,var(--color-white) 80%,transparent)}}.bg-white\/90{background-color:#ffffffe6}@supports (color:color-mix(in lab,red,red)){.bg-white\/90{background-color:color-mix(in oklab,var(--color-white) 90%,transparent)}}.bg-gradient-to-br{--tw-gradient-position:to bottom right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.from-\[\#6366f1\]{--tw-gradient-from:#6366f1;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-\[\#ff6600\]{--tw-gradient-from:#f60;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-\[\#8b5cf6\]{--tw-gradient-to:#8b5cf6;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-\[\#f5a623\]{--tw-gradient-to:#f5a623;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-6{padding-block:calc(var(--spacing) * 6)}.py-\[2px\]{padding-block:2px}.pt-1{padding-top:calc(var(--spacing) * 1)}.pt-1\.5{padding-top:calc(var(--spacing) * 1.5)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-\[10px\]{padding-top:10px}.pt-\[14px\]{padding-top:14px}.pr-1{padding-right:calc(var(--spacing) * 1)}.pr-2{padding-right:calc(var(--spacing) * 2)}.pr-3{padding-right:calc(var(--spacing) * 3)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pb-1{padding-bottom:calc(var(--spacing) * 1)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pl-1{padding-left:calc(var(--spacing) * 1)}.pl-1\.5{padding-left:calc(var(--spacing) * 1.5)}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[8px\]{font-size:8px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[14px\]{font-size:14px}.text-\[16px\]{font-size:16px}.leading-\[1\.6\]{--tw-leading:1.6;line-height:1.6}.leading-\[18px\]{--tw-leading:18px;line-height:18px}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.whitespace-pre{white-space:pre}.whitespace-pre-wrap{white-space:pre-wrap}.text-\[\#3b82f6\]{color:#3b82f6}.text-\[\#5f6368\]{color:#5f6368}.text-\[\#6e7681\]{color:#6e7681}.text-\[\#9aa0a6\]{color:#9aa0a6}.text-\[\#9d9d9d\]{color:#9d9d9d}.text-\[\#22c55e\]{color:#22c55e}.text-\[\#50fa7b\]{color:#50fa7b}.text-\[\#94a3b8\]{color:#94a3b8}.text-\[\#519aba\]{color:#519aba}.text-\[\#666\]{color:#666}.text-\[\#888\]{color:#888}.text-\[\#64748b\]{color:#64748b}.text-\[\#666666\]{color:#666}.text-\[\#858585\]{color:#858585}.text-\[\#a0a0a0\]{color:#a0a0a0}.text-\[\#bd93f9\]{color:#bd93f9}.text-\[\#bdc1c6\]{color:#bdc1c6}.text-\[\#c6c6c6\]{color:#c6c6c6}.text-\[\#cccccc\]{color:#ccc}.text-\[\#d4d4d4\]{color:#d4d4d4}.text-\[\#dcb67a\]{color:#dcb67a}.text-\[\#e8e8e8\]{color:#e8e8e8}.text-\[\#e8eaed\]{color:#e8eaed}.text-\[\#e6995b\]{color:#e6995b}.text-\[\#eab308\]{color:#eab308}.text-\[\#ef4444\]{color:#ef4444}.text-\[\#f8f8f2\]{color:#f8f8f2}.text-\[\#f8f8f2\]\/70{color:#f8f8f2b3}.text-\[\#ff5555\]{color:#f55}.text-\[\#ffffff\]{color:#fff}.text-\[var\(--scenar-foreground\)\]{color:var(--scenar-foreground)}.text-\[var\(--scenar-muted-foreground\)\]{color:var(--scenar-muted-foreground)}.text-foreground,.text-foreground\/70{color:var(--scenar-foreground)}@supports (color:color-mix(in lab,red,red)){.text-foreground\/70{color:color-mix(in oklab,var(--scenar-foreground) 70%,transparent)}}.text-muted-foreground{color:var(--scenar-muted-foreground)}.text-neutral-900{color:var(--color-neutral-900)}.text-primary{color:var(--scenar-primary)}.text-white{color:var(--color-white)}.text-white\/70{color:#ffffffb3}@supports (color:color-mix(in lab,red,red)){.text-white\/70{color:color-mix(in oklab,var(--color-white) 70%,transparent)}}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.opacity-0{opacity:0}.shadow-\[0_0_30px_rgba\(255\,255\,255\,0\.3\)\]{--tw-shadow:0 0 30px var(--tw-shadow-color,#ffffff4d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring,.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-white\/30{--tw-ring-color:#ffffff4d}@supports (color:color-mix(in lab,red,red)){.ring-white\/30{--tw-ring-color:color-mix(in oklab, var(--color-white) 30%, transparent)}}.drop-shadow-sm{--tw-drop-shadow-size:drop-shadow(0 1px 2px var(--tw-drop-shadow-color,#00000026));--tw-drop-shadow:drop-shadow(var(--drop-shadow-sm));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[height\]{transition-property:height;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.select-none{-webkit-user-select:none;user-select:none}@media (hover:hover){.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}@media (hover:hover){.hover\:h-1\.5:hover{height:calc(var(--spacing) * 1.5)}.hover\:scale-105:hover{--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.hover\:scale-110:hover{--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x) var(--tw-scale-y)}.hover\:bg-accent:hover{background-color:var(--scenar-accent)}.hover\:text-foreground:hover{color:var(--scenar-foreground)}}}@layer scenar{.scenar{--scenar-surface:#fff;--scenar-border:#e5e7eb;--scenar-foreground:#0f172a;--scenar-muted-foreground:#64748b;--scenar-card:#fff;--scenar-accent:#f1f5f9;--scenar-primary:#0f172a;--scenar-ring:#0f172a}.scenar.dark{--scenar-surface:#0f172a;--scenar-border:#1e293b;--scenar-foreground:#f8fafc;--scenar-muted-foreground:#94a3b8;--scenar-card:#1e293b;--scenar-accent:#1e293b;--scenar-primary:#f8fafc;--scenar-ring:#f8fafc}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"<length-percentage>";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"<length-percentage>";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"<length-percentage>";inherits:false;initial-value:100%}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
-
import { createProgram } from "../index.js";
|
|
3
|
-
|
|
4
|
-
describe("scenar deploy", () => {
|
|
5
|
-
let stderrOutput: string;
|
|
6
|
-
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
stderrOutput = "";
|
|
9
|
-
vi.spyOn(process.stderr, "write").mockImplementation((chunk) => {
|
|
10
|
-
stderrOutput += String(chunk);
|
|
11
|
-
return true;
|
|
12
|
-
});
|
|
13
|
-
process.exitCode = undefined;
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it("registers the deploy command", () => {
|
|
17
|
-
const program = createProgram();
|
|
18
|
-
const cmd = program.commands.find((c) => c.name() === "deploy");
|
|
19
|
-
expect(cmd).toBeDefined();
|
|
20
|
-
expect(cmd!.description()).toContain("embed URL");
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("accepts a bundle directory argument and exposes its options", () => {
|
|
24
|
-
const program = createProgram();
|
|
25
|
-
const cmd = program.commands.find((c) => c.name() === "deploy");
|
|
26
|
-
expect(cmd!.registeredArguments[0]!.name()).toBe("bundleDir");
|
|
27
|
-
const opts = cmd!.options.map((o) => o.long);
|
|
28
|
-
expect(opts).toContain("--backend");
|
|
29
|
-
expect(opts).toContain("--org");
|
|
30
|
-
expect(opts).toContain("--slug");
|
|
31
|
-
expect(opts).toContain("--name");
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it("fails clearly when the bundle directory does not exist", async () => {
|
|
35
|
-
const program = createProgram();
|
|
36
|
-
program.exitOverride();
|
|
37
|
-
try {
|
|
38
|
-
await program.parseAsync(["node", "scenar", "deploy", "/tmp/__no_bundle__", "--org", "acme"]);
|
|
39
|
-
} catch {
|
|
40
|
-
// exitOverride
|
|
41
|
-
}
|
|
42
|
-
expect(process.exitCode).toBe(1);
|
|
43
|
-
expect(stderrOutput).toContain("Error");
|
|
44
|
-
});
|
|
45
|
-
});
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
runDeployFlow,
|
|
4
|
-
toDeclaredFiles,
|
|
5
|
-
localViewUrl,
|
|
6
|
-
type DeployFlowDeps,
|
|
7
|
-
type UploadTarget,
|
|
8
|
-
} from "../deploy/deploy-flow.js";
|
|
9
|
-
import type { PackManifest } from "../pack/pack-manifest.js";
|
|
10
|
-
|
|
11
|
-
const MANIFEST: PackManifest = {
|
|
12
|
-
schemaVersion: 1,
|
|
13
|
-
scenarioId: "welcome-tour",
|
|
14
|
-
files: [
|
|
15
|
-
{ path: "index.html", sha256: "a".repeat(64), sizeBytes: 397, contentType: "text/html" },
|
|
16
|
-
{ path: "assets/app.js", sha256: "b".repeat(64), sizeBytes: 356965, contentType: "text/javascript" },
|
|
17
|
-
{ path: "scenario.json", sha256: "c".repeat(64), sizeBytes: 91, contentType: "application/json" },
|
|
18
|
-
],
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
describe("toDeclaredFiles", () => {
|
|
22
|
-
it("maps manifest files to declared files with bigint sizes and lowercase-hex sha256", () => {
|
|
23
|
-
const declared = toDeclaredFiles(MANIFEST);
|
|
24
|
-
expect(declared).toHaveLength(3);
|
|
25
|
-
expect(declared[0]).toEqual({
|
|
26
|
-
relativePath: "index.html",
|
|
27
|
-
sha256: "a".repeat(64),
|
|
28
|
-
sizeBytes: 397n,
|
|
29
|
-
contentType: "text/html",
|
|
30
|
-
});
|
|
31
|
-
expect(typeof declared[1]!.sizeBytes).toBe("bigint");
|
|
32
|
-
expect(declared[1]!.sizeBytes).toBe(356965n);
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
describe("runDeployFlow", () => {
|
|
37
|
-
it("ensures the scenario before creating the session, then uploads then completes", async () => {
|
|
38
|
-
const calls: string[] = [];
|
|
39
|
-
const targets: UploadTarget[] = [
|
|
40
|
-
{
|
|
41
|
-
relativePath: "index.html",
|
|
42
|
-
presignedPutUrl: "https://store/idx",
|
|
43
|
-
requiredHeaders: { "Content-Type": "text/html", "x-amz-checksum-sha256": "AAAA" },
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
relativePath: "assets/app.js",
|
|
47
|
-
presignedPutUrl: "https://store/js",
|
|
48
|
-
requiredHeaders: { "Content-Type": "text/javascript", "x-amz-checksum-sha256": "BBBB" },
|
|
49
|
-
},
|
|
50
|
-
];
|
|
51
|
-
|
|
52
|
-
const uploaded: Array<{ target: UploadTarget; bytes: number }> = [];
|
|
53
|
-
|
|
54
|
-
const deps: DeployFlowDeps = {
|
|
55
|
-
applyScenario: vi.fn(async (input) => {
|
|
56
|
-
calls.push("apply");
|
|
57
|
-
expect(input).toEqual({ org: "acme", slug: "welcome-tour", name: "Welcome" });
|
|
58
|
-
return "scn_123";
|
|
59
|
-
}),
|
|
60
|
-
createSession: vi.fn(async (scenarioId, files) => {
|
|
61
|
-
calls.push("create");
|
|
62
|
-
expect(scenarioId).toBe("scn_123");
|
|
63
|
-
expect(files).toHaveLength(3);
|
|
64
|
-
return { deployId: "dep_456", uploadTargets: targets };
|
|
65
|
-
}),
|
|
66
|
-
readBundleFile: vi.fn(async (rel) => new Uint8Array(rel.length)),
|
|
67
|
-
uploadFile: vi.fn(async (target, bytes) => {
|
|
68
|
-
calls.push(`upload:${target.relativePath}`);
|
|
69
|
-
uploaded.push({ target, bytes: bytes.byteLength });
|
|
70
|
-
}),
|
|
71
|
-
completeSession: vi.fn(async (deployId) => {
|
|
72
|
-
calls.push("complete");
|
|
73
|
-
expect(deployId).toBe("dep_456");
|
|
74
|
-
return "https://d-dep_456.localhost:8787/";
|
|
75
|
-
}),
|
|
76
|
-
log: () => {},
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
const result = await runDeployFlow(deps, {
|
|
80
|
-
manifest: MANIFEST,
|
|
81
|
-
org: "acme",
|
|
82
|
-
slug: "welcome-tour",
|
|
83
|
-
name: "Welcome",
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
expect(result).toEqual({ deployId: "dep_456", embedUrl: "https://d-dep_456.localhost:8787/" });
|
|
87
|
-
// Ordering: ensure -> create -> upload each (in target order) -> complete.
|
|
88
|
-
expect(calls).toEqual(["apply", "create", "upload:index.html", "upload:assets/app.js", "complete"]);
|
|
89
|
-
// Required headers are passed through verbatim to each upload.
|
|
90
|
-
expect(uploaded[0]!.target.requiredHeaders).toEqual(targets[0]!.requiredHeaders);
|
|
91
|
-
expect(uploaded[1]!.target.requiredHeaders).toEqual(targets[1]!.requiredHeaders);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it("propagates a missing-embed-url failure from completion", async () => {
|
|
95
|
-
const deps: DeployFlowDeps = {
|
|
96
|
-
applyScenario: async () => "scn_1",
|
|
97
|
-
createSession: async () => ({ deployId: "dep_1", uploadTargets: [] }),
|
|
98
|
-
readBundleFile: async () => new Uint8Array(),
|
|
99
|
-
uploadFile: async () => {},
|
|
100
|
-
completeSession: async () => {
|
|
101
|
-
throw new Error("deploy completed but returned no embed_url");
|
|
102
|
-
},
|
|
103
|
-
log: () => {},
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
await expect(
|
|
107
|
-
runDeployFlow(deps, { manifest: MANIFEST, org: "acme", slug: "welcome-tour", name: "x" }),
|
|
108
|
-
).rejects.toThrow(/embed_url/);
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
describe("localViewUrl", () => {
|
|
113
|
-
it("downgrades https to http for *.localhost hosts (wrangler dev serves http)", () => {
|
|
114
|
-
expect(localViewUrl("https://d-abc.localhost:8787/")).toBe("http://d-abc.localhost:8787/");
|
|
115
|
-
expect(localViewUrl("https://localhost:8787/")).toBe("http://localhost:8787/");
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it("leaves production https URLs untouched", () => {
|
|
119
|
-
expect(localViewUrl("https://d-abc.scenar.ai/")).toBe("https://d-abc.scenar.ai/");
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it("returns non-URL input unchanged", () => {
|
|
123
|
-
expect(localViewUrl("not a url")).toBe("not a url");
|
|
124
|
-
});
|
|
125
|
-
});
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
2
|
-
import { putFile } from "../deploy/upload.js";
|
|
3
|
-
|
|
4
|
-
const realFetch = globalThis.fetch;
|
|
5
|
-
|
|
6
|
-
afterEach(() => {
|
|
7
|
-
globalThis.fetch = realFetch;
|
|
8
|
-
vi.restoreAllMocks();
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
describe("putFile", () => {
|
|
12
|
-
it("PUTs the raw bytes and replays required headers verbatim", async () => {
|
|
13
|
-
const seen: { url?: string; init?: RequestInit } = {};
|
|
14
|
-
globalThis.fetch = vi.fn(async (url: any, init: any) => {
|
|
15
|
-
seen.url = String(url);
|
|
16
|
-
seen.init = init;
|
|
17
|
-
return new Response(null, { status: 200 });
|
|
18
|
-
}) as unknown as typeof fetch;
|
|
19
|
-
|
|
20
|
-
const bytes = new Uint8Array([1, 2, 3]);
|
|
21
|
-
const headers = { "Content-Type": "text/html", "x-amz-checksum-sha256": "AAAA" };
|
|
22
|
-
await putFile("https://store/key", headers, bytes);
|
|
23
|
-
|
|
24
|
-
expect(seen.url).toBe("https://store/key");
|
|
25
|
-
expect(seen.init!.method).toBe("PUT");
|
|
26
|
-
expect(seen.init!.headers).toEqual(headers);
|
|
27
|
-
expect(seen.init!.body).toBe(bytes);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it("throws with the status and body detail on a non-2xx response", async () => {
|
|
31
|
-
globalThis.fetch = vi.fn(async () =>
|
|
32
|
-
new Response("SignatureDoesNotMatch", { status: 403, statusText: "Forbidden" }),
|
|
33
|
-
) as unknown as typeof fetch;
|
|
34
|
-
|
|
35
|
-
await expect(putFile("https://store/key", {}, new Uint8Array())).rejects.toThrow(
|
|
36
|
-
/upload failed \(403 Forbidden\): SignatureDoesNotMatch/,
|
|
37
|
-
);
|
|
38
|
-
});
|
|
39
|
-
});
|
package/src/commands/deploy.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../../src/commands/deploy.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsBpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAqF5D"}
|
package/src/commands/deploy.js
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import { resolve, join } from "node:path";
|
|
2
|
-
import { readFile, stat } from "node:fs/promises";
|
|
3
|
-
import { PACK_MANIFEST_FILE } from "../pack/pack-manifest.js";
|
|
4
|
-
import { readBundleViewport } from "../bundle/read-viewport.js";
|
|
5
|
-
import { createBackendClients } from "../deploy/client.js";
|
|
6
|
-
import { putFile } from "../deploy/upload.js";
|
|
7
|
-
import { buildEmbedSnippet } from "../deploy/embed-snippet.js";
|
|
8
|
-
import { runDeployFlow, localViewUrl } from "../deploy/deploy-flow.js";
|
|
9
|
-
/** Default local backend (gRPC). Local dev shifts off 8080 to avoid colliding with stigmer-service. */
|
|
10
|
-
const DEFAULT_BACKEND = "http://localhost:8090";
|
|
11
|
-
/** Slug rule from ApiResourceMetadata: lowercase, hyphens, start letter, end alphanumeric. */
|
|
12
|
-
const SLUG_PATTERN = /^[a-z][a-z0-9-]*[a-z0-9]$/;
|
|
13
|
-
export function registerDeployCommand(program) {
|
|
14
|
-
program
|
|
15
|
-
.command("deploy")
|
|
16
|
-
.description("Deploy a packed bundle to Scenar Cloud and print its embed URL.\n\n" +
|
|
17
|
-
"Takes a bundle directory produced by `scenar pack` (containing\n" +
|
|
18
|
-
"pack-manifest.json). It ensures the parent scenario exists, opens a\n" +
|
|
19
|
-
"two-phase upload session, uploads every file directly to object\n" +
|
|
20
|
-
"storage via presigned URLs, completes the session, and prints the\n" +
|
|
21
|
-
"deploy's embed URL.\n\n" +
|
|
22
|
-
"Backend defaults to http://localhost:8090 (override with --backend or\n" +
|
|
23
|
-
"SCENAR_BACKEND). The owning org is required (--org or SCENAR_ORG).")
|
|
24
|
-
.argument("<bundleDir>", "path to a packed bundle directory (from scenar pack)")
|
|
25
|
-
.option("--backend <url>", "backend gRPC endpoint (default: $SCENAR_BACKEND or http://localhost:8090)")
|
|
26
|
-
.option("--org <org>", "owning organization (default: $SCENAR_ORG)")
|
|
27
|
-
.option("--slug <slug>", "scenario slug (default: the packed scenario id)")
|
|
28
|
-
.option("--name <name>", "scenario display name (default: the slug)")
|
|
29
|
-
.action(async (bundleDir, options) => {
|
|
30
|
-
const resolvedDir = resolve(bundleDir);
|
|
31
|
-
try {
|
|
32
|
-
const info = await stat(resolvedDir).catch(() => null);
|
|
33
|
-
if (!info || !info.isDirectory()) {
|
|
34
|
-
throw new Error(`${bundleDir} is not a directory. Pass a bundle produced by \`scenar pack\`.`);
|
|
35
|
-
}
|
|
36
|
-
const manifest = await readPackManifest(resolvedDir);
|
|
37
|
-
const backend = options.backend ?? process.env.SCENAR_BACKEND ?? DEFAULT_BACKEND;
|
|
38
|
-
const org = options.org ?? process.env.SCENAR_ORG;
|
|
39
|
-
if (!org) {
|
|
40
|
-
throw new Error("an owning org is required: pass --org <org> or set SCENAR_ORG.");
|
|
41
|
-
}
|
|
42
|
-
const slug = options.slug ?? manifest.scenarioId;
|
|
43
|
-
if (!SLUG_PATTERN.test(slug)) {
|
|
44
|
-
throw new Error(`invalid scenario slug "${slug}". Slugs are lowercase letters, digits,\n` +
|
|
45
|
-
"and hyphens, starting with a letter and ending alphanumeric. Pass --slug to override.");
|
|
46
|
-
}
|
|
47
|
-
const name = options.name ?? slug;
|
|
48
|
-
process.stderr.write(`Bundle: ${resolvedDir}\n`);
|
|
49
|
-
process.stderr.write(`Backend: ${backend}\n`);
|
|
50
|
-
process.stderr.write(`Org: ${org}\n`);
|
|
51
|
-
process.stderr.write(`Scenario: ${slug}\n\n`);
|
|
52
|
-
const clients = createBackendClients(backend);
|
|
53
|
-
const deps = makeDeps(clients, resolvedDir);
|
|
54
|
-
const { deployId, embedUrl } = await runDeployFlow(deps, { manifest, org, slug, name });
|
|
55
|
-
process.stderr.write(`\n\x1b[32m✓\x1b[0m Deployed ${deployId}\n`);
|
|
56
|
-
process.stdout.write(`${embedUrl}\n`);
|
|
57
|
-
const localUrl = localViewUrl(embedUrl);
|
|
58
|
-
if (localUrl !== embedUrl) {
|
|
59
|
-
process.stderr.write(` Local view: ${localUrl}\n`);
|
|
60
|
-
}
|
|
61
|
-
// Embed snippet (guidance → stderr; stdout stays the bare URL for
|
|
62
|
-
// piping). Responsive iframe at the bundle's canonical aspect ratio
|
|
63
|
-
// (DD-002/DD-004); falls back to the default viewport for bundles packed
|
|
64
|
-
// before the viewport was recorded.
|
|
65
|
-
const { viewport, recorded } = await readBundleViewport(resolvedDir);
|
|
66
|
-
if (!recorded) {
|
|
67
|
-
process.stderr.write(` Note: no recorded viewport for "${name}"; ` +
|
|
68
|
-
`snippet uses the default ${viewport.width}x${viewport.height}. ` +
|
|
69
|
-
"Re-pack with the current CLI to embed at the exact aspect ratio.\n");
|
|
70
|
-
}
|
|
71
|
-
process.stderr.write("\n Embed snippet (paste into any page):\n\n");
|
|
72
|
-
process.stderr.write(`${indent(buildEmbedSnippet({ embedUrl, viewport, title: name }))}\n`);
|
|
73
|
-
}
|
|
74
|
-
catch (error) {
|
|
75
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
76
|
-
process.stderr.write(`\x1b[31mError:\x1b[0m ${msg}\n`);
|
|
77
|
-
process.exitCode = 1;
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
/** Build the flow dependencies from real Connect clients + the bundle on disk. */
|
|
82
|
-
function makeDeps(clients, bundleDir) {
|
|
83
|
-
return {
|
|
84
|
-
async applyScenario({ org, slug, name }) {
|
|
85
|
-
// Idempotent upsert by (org, slug). The spec is a minimal valid placeholder
|
|
86
|
-
// (one step) — the embed plays from the packed JS, so the Scenario resource
|
|
87
|
-
// serves as the authorization parent and publication anchor, not the
|
|
88
|
-
// serving source. A richer spec projection is a deliberate follow-up.
|
|
89
|
-
const scenario = await clients.scenario.apply({
|
|
90
|
-
apiVersion: "scenario.scenar.ai/v1",
|
|
91
|
-
kind: "Scenario",
|
|
92
|
-
metadata: { org, slug, name },
|
|
93
|
-
spec: { steps: [{ view: "scenario", delayMs: 0 }] },
|
|
94
|
-
});
|
|
95
|
-
const id = scenario.metadata?.id;
|
|
96
|
-
if (!id) {
|
|
97
|
-
throw new Error("scenario apply returned no metadata.id");
|
|
98
|
-
}
|
|
99
|
-
return id;
|
|
100
|
-
},
|
|
101
|
-
async createSession(scenarioId, files) {
|
|
102
|
-
const response = await clients.deploy.createDeployUploadSession({ scenarioId, files });
|
|
103
|
-
return {
|
|
104
|
-
deployId: response.deployId,
|
|
105
|
-
uploadTargets: response.uploadTargets.map((target) => ({
|
|
106
|
-
relativePath: target.relativePath,
|
|
107
|
-
presignedPutUrl: target.presignedPutUrl,
|
|
108
|
-
requiredHeaders: target.requiredHeaders,
|
|
109
|
-
})),
|
|
110
|
-
};
|
|
111
|
-
},
|
|
112
|
-
readBundleFile(relativePath) {
|
|
113
|
-
return readFile(join(bundleDir, ...relativePath.split("/")));
|
|
114
|
-
},
|
|
115
|
-
uploadFile(target, bytes) {
|
|
116
|
-
return putFile(target.presignedPutUrl, target.requiredHeaders, bytes);
|
|
117
|
-
},
|
|
118
|
-
async completeSession(deployId) {
|
|
119
|
-
const deploy = await clients.deploy.completeDeployUploadSession({ deployId });
|
|
120
|
-
const embedUrl = deploy.status?.embedUrl;
|
|
121
|
-
if (!embedUrl) {
|
|
122
|
-
throw new Error("deploy completed but returned no embed_url");
|
|
123
|
-
}
|
|
124
|
-
return embedUrl;
|
|
125
|
-
},
|
|
126
|
-
log(message) {
|
|
127
|
-
process.stderr.write(`${message}\n`);
|
|
128
|
-
},
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
/** Indent every line by two spaces (for nesting a block under a heading). */
|
|
132
|
-
function indent(block) {
|
|
133
|
-
return block.replace(/^/gm, " ");
|
|
134
|
-
}
|
|
135
|
-
async function readPackManifest(bundleDir) {
|
|
136
|
-
const manifestPath = join(bundleDir, PACK_MANIFEST_FILE);
|
|
137
|
-
let raw;
|
|
138
|
-
try {
|
|
139
|
-
raw = await readFile(manifestPath, "utf-8");
|
|
140
|
-
}
|
|
141
|
-
catch {
|
|
142
|
-
throw new Error(`no ${PACK_MANIFEST_FILE} in ${bundleDir}. Run \`scenar pack\` to produce a bundle first.`);
|
|
143
|
-
}
|
|
144
|
-
const parsed = JSON.parse(raw);
|
|
145
|
-
if (!parsed.scenarioId || !Array.isArray(parsed.files) || parsed.files.length === 0) {
|
|
146
|
-
throw new Error(`${PACK_MANIFEST_FILE} is malformed or lists no files.`);
|
|
147
|
-
}
|
|
148
|
-
return parsed;
|
|
149
|
-
}
|
|
150
|
-
//# sourceMappingURL=deploy.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../../src/commands/deploy.ts"],"names":[],"mappings":"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"}
|
package/src/commands/deploy.ts
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import { resolve, join } from "node:path";
|
|
2
|
-
import { readFile, stat } from "node:fs/promises";
|
|
3
|
-
import { Command } from "commander";
|
|
4
|
-
import type { PackManifest } from "../pack/pack-manifest.js";
|
|
5
|
-
import { PACK_MANIFEST_FILE } from "../pack/pack-manifest.js";
|
|
6
|
-
import { readBundleViewport } from "../bundle/read-viewport.js";
|
|
7
|
-
import { createBackendClients } from "../deploy/client.js";
|
|
8
|
-
import { putFile } from "../deploy/upload.js";
|
|
9
|
-
import { buildEmbedSnippet } from "../deploy/embed-snippet.js";
|
|
10
|
-
import { runDeployFlow, localViewUrl, type DeployFlowDeps } from "../deploy/deploy-flow.js";
|
|
11
|
-
|
|
12
|
-
/** Default local backend (gRPC). Local dev shifts off 8080 to avoid colliding with stigmer-service. */
|
|
13
|
-
const DEFAULT_BACKEND = "http://localhost:8090";
|
|
14
|
-
|
|
15
|
-
/** Slug rule from ApiResourceMetadata: lowercase, hyphens, start letter, end alphanumeric. */
|
|
16
|
-
const SLUG_PATTERN = /^[a-z][a-z0-9-]*[a-z0-9]$/;
|
|
17
|
-
|
|
18
|
-
interface DeployOptions {
|
|
19
|
-
backend?: string;
|
|
20
|
-
org?: string;
|
|
21
|
-
slug?: string;
|
|
22
|
-
name?: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function registerDeployCommand(program: Command): void {
|
|
26
|
-
program
|
|
27
|
-
.command("deploy")
|
|
28
|
-
.description(
|
|
29
|
-
"Deploy a packed bundle to Scenar Cloud and print its embed URL.\n\n" +
|
|
30
|
-
"Takes a bundle directory produced by `scenar pack` (containing\n" +
|
|
31
|
-
"pack-manifest.json). It ensures the parent scenario exists, opens a\n" +
|
|
32
|
-
"two-phase upload session, uploads every file directly to object\n" +
|
|
33
|
-
"storage via presigned URLs, completes the session, and prints the\n" +
|
|
34
|
-
"deploy's embed URL.\n\n" +
|
|
35
|
-
"Backend defaults to http://localhost:8090 (override with --backend or\n" +
|
|
36
|
-
"SCENAR_BACKEND). The owning org is required (--org or SCENAR_ORG).",
|
|
37
|
-
)
|
|
38
|
-
.argument("<bundleDir>", "path to a packed bundle directory (from scenar pack)")
|
|
39
|
-
.option("--backend <url>", "backend gRPC endpoint (default: $SCENAR_BACKEND or http://localhost:8090)")
|
|
40
|
-
.option("--org <org>", "owning organization (default: $SCENAR_ORG)")
|
|
41
|
-
.option("--slug <slug>", "scenario slug (default: the packed scenario id)")
|
|
42
|
-
.option("--name <name>", "scenario display name (default: the slug)")
|
|
43
|
-
.action(async (bundleDir: string, options: DeployOptions) => {
|
|
44
|
-
const resolvedDir = resolve(bundleDir);
|
|
45
|
-
|
|
46
|
-
try {
|
|
47
|
-
const info = await stat(resolvedDir).catch(() => null);
|
|
48
|
-
if (!info || !info.isDirectory()) {
|
|
49
|
-
throw new Error(
|
|
50
|
-
`${bundleDir} is not a directory. Pass a bundle produced by \`scenar pack\`.`,
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const manifest = await readPackManifest(resolvedDir);
|
|
55
|
-
|
|
56
|
-
const backend = options.backend ?? process.env.SCENAR_BACKEND ?? DEFAULT_BACKEND;
|
|
57
|
-
const org = options.org ?? process.env.SCENAR_ORG;
|
|
58
|
-
if (!org) {
|
|
59
|
-
throw new Error(
|
|
60
|
-
"an owning org is required: pass --org <org> or set SCENAR_ORG.",
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
const slug = options.slug ?? manifest.scenarioId;
|
|
64
|
-
if (!SLUG_PATTERN.test(slug)) {
|
|
65
|
-
throw new Error(
|
|
66
|
-
`invalid scenario slug "${slug}". Slugs are lowercase letters, digits,\n` +
|
|
67
|
-
"and hyphens, starting with a letter and ending alphanumeric. Pass --slug to override.",
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
const name = options.name ?? slug;
|
|
71
|
-
|
|
72
|
-
process.stderr.write(`Bundle: ${resolvedDir}\n`);
|
|
73
|
-
process.stderr.write(`Backend: ${backend}\n`);
|
|
74
|
-
process.stderr.write(`Org: ${org}\n`);
|
|
75
|
-
process.stderr.write(`Scenario: ${slug}\n\n`);
|
|
76
|
-
|
|
77
|
-
const clients = createBackendClients(backend);
|
|
78
|
-
const deps = makeDeps(clients, resolvedDir);
|
|
79
|
-
|
|
80
|
-
const { deployId, embedUrl } = await runDeployFlow(deps, { manifest, org, slug, name });
|
|
81
|
-
|
|
82
|
-
process.stderr.write(`\n\x1b[32m✓\x1b[0m Deployed ${deployId}\n`);
|
|
83
|
-
process.stdout.write(`${embedUrl}\n`);
|
|
84
|
-
|
|
85
|
-
const localUrl = localViewUrl(embedUrl);
|
|
86
|
-
if (localUrl !== embedUrl) {
|
|
87
|
-
process.stderr.write(` Local view: ${localUrl}\n`);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Embed snippet (guidance → stderr; stdout stays the bare URL for
|
|
91
|
-
// piping). Responsive iframe at the bundle's canonical aspect ratio
|
|
92
|
-
// (DD-002/DD-004); falls back to the default viewport for bundles packed
|
|
93
|
-
// before the viewport was recorded.
|
|
94
|
-
const { viewport, recorded } = await readBundleViewport(resolvedDir);
|
|
95
|
-
if (!recorded) {
|
|
96
|
-
process.stderr.write(
|
|
97
|
-
` Note: no recorded viewport for "${name}"; ` +
|
|
98
|
-
`snippet uses the default ${viewport.width}x${viewport.height}. ` +
|
|
99
|
-
"Re-pack with the current CLI to embed at the exact aspect ratio.\n",
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
process.stderr.write("\n Embed snippet (paste into any page):\n\n");
|
|
103
|
-
process.stderr.write(`${indent(buildEmbedSnippet({ embedUrl, viewport, title: name }))}\n`);
|
|
104
|
-
} catch (error) {
|
|
105
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
106
|
-
process.stderr.write(`\x1b[31mError:\x1b[0m ${msg}\n`);
|
|
107
|
-
process.exitCode = 1;
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/** Build the flow dependencies from real Connect clients + the bundle on disk. */
|
|
113
|
-
function makeDeps(
|
|
114
|
-
clients: ReturnType<typeof createBackendClients>,
|
|
115
|
-
bundleDir: string,
|
|
116
|
-
): DeployFlowDeps {
|
|
117
|
-
return {
|
|
118
|
-
async applyScenario({ org, slug, name }) {
|
|
119
|
-
// Idempotent upsert by (org, slug). The spec is a minimal valid placeholder
|
|
120
|
-
// (one step) — the embed plays from the packed JS, so the Scenario resource
|
|
121
|
-
// serves as the authorization parent and publication anchor, not the
|
|
122
|
-
// serving source. A richer spec projection is a deliberate follow-up.
|
|
123
|
-
const scenario = await clients.scenario.apply({
|
|
124
|
-
apiVersion: "scenario.scenar.ai/v1",
|
|
125
|
-
kind: "Scenario",
|
|
126
|
-
metadata: { org, slug, name },
|
|
127
|
-
spec: { steps: [{ view: "scenario", delayMs: 0 }] },
|
|
128
|
-
});
|
|
129
|
-
const id = scenario.metadata?.id;
|
|
130
|
-
if (!id) {
|
|
131
|
-
throw new Error("scenario apply returned no metadata.id");
|
|
132
|
-
}
|
|
133
|
-
return id;
|
|
134
|
-
},
|
|
135
|
-
async createSession(scenarioId, files) {
|
|
136
|
-
const response = await clients.deploy.createDeployUploadSession({ scenarioId, files });
|
|
137
|
-
return {
|
|
138
|
-
deployId: response.deployId,
|
|
139
|
-
uploadTargets: response.uploadTargets.map((target) => ({
|
|
140
|
-
relativePath: target.relativePath,
|
|
141
|
-
presignedPutUrl: target.presignedPutUrl,
|
|
142
|
-
requiredHeaders: target.requiredHeaders,
|
|
143
|
-
})),
|
|
144
|
-
};
|
|
145
|
-
},
|
|
146
|
-
readBundleFile(relativePath) {
|
|
147
|
-
return readFile(join(bundleDir, ...relativePath.split("/")));
|
|
148
|
-
},
|
|
149
|
-
uploadFile(target, bytes) {
|
|
150
|
-
return putFile(target.presignedPutUrl, target.requiredHeaders, bytes);
|
|
151
|
-
},
|
|
152
|
-
async completeSession(deployId) {
|
|
153
|
-
const deploy = await clients.deploy.completeDeployUploadSession({ deployId });
|
|
154
|
-
const embedUrl = deploy.status?.embedUrl;
|
|
155
|
-
if (!embedUrl) {
|
|
156
|
-
throw new Error("deploy completed but returned no embed_url");
|
|
157
|
-
}
|
|
158
|
-
return embedUrl;
|
|
159
|
-
},
|
|
160
|
-
log(message) {
|
|
161
|
-
process.stderr.write(`${message}\n`);
|
|
162
|
-
},
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/** Indent every line by two spaces (for nesting a block under a heading). */
|
|
167
|
-
function indent(block: string): string {
|
|
168
|
-
return block.replace(/^/gm, " ");
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
async function readPackManifest(bundleDir: string): Promise<PackManifest> {
|
|
172
|
-
const manifestPath = join(bundleDir, PACK_MANIFEST_FILE);
|
|
173
|
-
let raw: string;
|
|
174
|
-
try {
|
|
175
|
-
raw = await readFile(manifestPath, "utf-8");
|
|
176
|
-
} catch {
|
|
177
|
-
throw new Error(
|
|
178
|
-
`no ${PACK_MANIFEST_FILE} in ${bundleDir}. Run \`scenar pack\` to produce a bundle first.`,
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
const parsed = JSON.parse(raw) as PackManifest;
|
|
182
|
-
if (!parsed.scenarioId || !Array.isArray(parsed.files) || parsed.files.length === 0) {
|
|
183
|
-
throw new Error(`${PACK_MANIFEST_FILE} is malformed or lists no files.`);
|
|
184
|
-
}
|
|
185
|
-
return parsed;
|
|
186
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../../src/commands/preview.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAcpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAuB7D"}
|