rivet-design 0.28.1 → 0.28.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/cli/commands/open.js +4 -2
  2. package/dist/cli/commands/serve.js +1 -0
  3. package/dist/jest.globalSetup.d.ts +3 -0
  4. package/dist/jest.globalSetup.js +19 -0
  5. package/dist/proxy-middleware/proxy-config.d.ts +0 -1
  6. package/dist/proxy-middleware/proxy-config.js +6 -34
  7. package/dist/routes/agentVariants.js +20 -173
  8. package/dist/routes/static.js +8 -2
  9. package/dist/server.d.ts +1 -1
  10. package/dist/server.js +13 -25
  11. package/dist/services/AuthService.js +7 -61
  12. package/dist/services/templates/viteReactTs.js +1 -1
  13. package/dist/types/rivet-page-types.d.ts +48 -0
  14. package/dist/types/rivet-page-types.js +6 -0
  15. package/dist/utils/rivetPage.d.ts +8 -0
  16. package/dist/utils/rivetPage.js +24 -0
  17. package/dist/utils/rivetPage.testHelpers.d.ts +6 -0
  18. package/dist/utils/rivetPage.testHelpers.js +18 -0
  19. package/dist/utils/rivetPageDocument.d.ts +7 -0
  20. package/dist/utils/rivetPageDocument.js +41 -0
  21. package/dist/utils/scriptJson.d.ts +2 -0
  22. package/dist/utils/scriptJson.js +9 -0
  23. package/dist/utils/uiDistPath.d.ts +2 -0
  24. package/dist/utils/uiDistPath.js +22 -0
  25. package/package.json +8 -5
  26. package/src/ui/dist/assets/LibraryEntry-B8-RtQpK.js +2 -0
  27. package/src/ui/dist/assets/google-g-CpxN0HWb.png +0 -0
  28. package/src/ui/dist/assets/main-11-ME6E3.css +1 -0
  29. package/src/ui/dist/assets/main-BGCGsyhl.js +204 -0
  30. package/src/ui/dist/index.html +3 -3
  31. package/src/ui/dist/pages/empty-project.html +53 -0
  32. package/src/ui/dist/pages/rivet-page.html +53 -0
  33. package/dist/routes/emptyStatePage.d.ts +0 -2
  34. package/dist/routes/emptyStatePage.js +0 -589
  35. package/src/ui/dist/assets/LibraryEntry-DS6Ce0sW.js +0 -112
  36. package/src/ui/dist/assets/logo-animation/clasp.png +0 -0
  37. package/src/ui/dist/assets/logo-animation/eraser.png +0 -0
  38. package/src/ui/dist/assets/logo-animation/playdough.png +0 -0
  39. package/src/ui/dist/assets/logo-animation/sea/bubblephone.png +0 -0
  40. package/src/ui/dist/assets/logo-animation/sea/cart.png +0 -0
  41. package/src/ui/dist/assets/logo-animation/sea/chair.png +0 -0
  42. package/src/ui/dist/assets/logo-animation/sea/desk.png +0 -0
  43. package/src/ui/dist/assets/logo-animation/sea/mug.png +0 -0
  44. package/src/ui/dist/assets/logo-animation/sea/pitcher.png +0 -0
  45. package/src/ui/dist/assets/logo-animation/sea/pot.png +0 -0
  46. package/src/ui/dist/assets/logo-animation/sea/startac.png +0 -0
  47. package/src/ui/dist/assets/logo-animation/sea/terracotta-1.png +0 -0
  48. package/src/ui/dist/assets/logo-animation/sea/terracotta-2.png +0 -0
  49. package/src/ui/dist/assets/logo-animation/sea/vhsplant.png +0 -0
  50. package/src/ui/dist/assets/main-CQSk-6km.css +0 -1
  51. package/src/ui/dist/assets/main-DkKRuatC.js +0 -218
@@ -70,6 +70,7 @@ const SERVER_START_TIMEOUT_ENV = 'RIVET_SERVER_START_TIMEOUT_MS';
70
70
  const LOCK_WAIT_TIMEOUT_ENV = 'RIVET_LOCK_WAIT_TIMEOUT_MS';
71
71
  const OPEN_RETRY_DELAY_MS = 1_000;
72
72
  const APP_HEALTH_TIMEOUT_MS = 10_000;
73
+ const APP_HEALTH_TIMEOUT_ENV = 'RIVET_APP_HEALTH_TIMEOUT_MS';
73
74
  const RETRY_OPEN_RECOVERY = {
74
75
  isRecoverable: true,
75
76
  nextAction: { action: 'retry', delayMs: OPEN_RETRY_DELAY_MS },
@@ -247,6 +248,7 @@ const ensureProjectRuntime = async (context, io, dependencies = {}) => {
247
248
  const env = dependencies.env ?? process.env;
248
249
  const serverStartTimeoutMs = timeoutFromEnv(env, SERVER_START_TIMEOUT_ENV, DEFAULT_SERVER_START_TIMEOUT_MS);
249
250
  const lockWaitTimeoutMs = timeoutFromEnv(env, LOCK_WAIT_TIMEOUT_ENV, DEFAULT_LOCK_WAIT_TIMEOUT_MS);
251
+ const appHealthTimeoutMs = timeoutFromEnv(env, APP_HEALTH_TIMEOUT_ENV, Math.min(serverStartTimeoutMs, APP_HEALTH_TIMEOUT_MS));
250
252
  const validation = await (0, exports.validateProjectOpen)({
251
253
  context,
252
254
  io,
@@ -292,7 +294,7 @@ const ensureProjectRuntime = async (context, io, dependencies = {}) => {
292
294
  }, RETRY_OPEN_RECOVERY);
293
295
  }
294
296
  if (replacement.outcome === 'attach') {
295
- const readinessError = await verifyRuntimeReadiness(existing, Math.min(serverStartTimeoutMs, APP_HEALTH_TIMEOUT_MS));
297
+ const readinessError = await verifyRuntimeReadiness(existing, appHealthTimeoutMs);
296
298
  if (!readinessError) {
297
299
  return finishProjectOpen(await openedData(existing, true), io);
298
300
  }
@@ -346,7 +348,7 @@ const ensureProjectRuntime = async (context, io, dependencies = {}) => {
346
348
  ...(0, serverVersion_1.versionEnvelopeFields)(attached.handshake),
347
349
  });
348
350
  }
349
- const readinessError = await verifyRuntimeReadiness(attached, Math.min(serverStartTimeoutMs, APP_HEALTH_TIMEOUT_MS));
351
+ const readinessError = await verifyRuntimeReadiness(attached, appHealthTimeoutMs);
350
352
  if (readinessError)
351
353
  return readinessError;
352
354
  const data = await openedData(attached, false);
@@ -200,6 +200,7 @@ const handleServe = async (args, io) => {
200
200
  previewPort,
201
201
  agentApplyMode: 'external',
202
202
  rivetPort,
203
+ resolveProjectName: (targetPath) => library.registry.findByPath(targetPath)?.name,
203
204
  resolveLibraryOrigin: () => {
204
205
  const hub = hubDiscovery.read();
205
206
  return hub?.port && (0, pid_1.isPidAlive)(hub.pid)
@@ -0,0 +1,3 @@
1
+ declare const ensureRivetPageShells: () => void;
2
+ export default ensureRivetPageShells;
3
+ //# sourceMappingURL=jest.globalSetup.d.ts.map
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const fs_1 = __importDefault(require("fs"));
7
+ const path_1 = __importDefault(require("path"));
8
+ const rivetPage_1 = require("./utils/rivetPage");
9
+ const ensureRivetPageShells = () => {
10
+ fs_1.default.mkdirSync(rivetPage_1.RIVET_PAGE_SHELL_DIRECTORY, { recursive: true });
11
+ for (const shellFile of rivetPage_1.RIVET_PAGE_SHELL_FILES) {
12
+ const builtShell = path_1.default.join(rivetPage_1.RIVET_PAGE_SHELL_DIRECTORY, shellFile);
13
+ if (!fs_1.default.existsSync(builtShell)) {
14
+ fs_1.default.copyFileSync(path_1.default.join(__dirname, 'ui', shellFile), builtShell);
15
+ }
16
+ }
17
+ };
18
+ exports.default = ensureRivetPageShells;
19
+ //# sourceMappingURL=jest.globalSetup.js.map
@@ -14,7 +14,6 @@ export declare const resolveTargetAddressedPreview: (args: {
14
14
  resolveVariantPreviewPort?: ResolveVariantPreviewPort;
15
15
  }) => TargetAddressedPreview | null;
16
16
  export declare const markPreviewBridgeDocumentUncacheable: (proxyRes: IncomingMessage) => void;
17
- export declare const buildPreviewHoldingPage: () => string;
18
17
  export declare const createUserDevServerProxy: (userPort: number, userHost?: string, options?: {
19
18
  isPreviewBridgeEnabled?: boolean;
20
19
  expectedPreviewParentOrigin?: string;
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createUserDevServerProxy = exports.buildPreviewHoldingPage = exports.markPreviewBridgeDocumentUncacheable = exports.resolveTargetAddressedPreview = void 0;
3
+ exports.createUserDevServerProxy = exports.markPreviewBridgeDocumentUncacheable = exports.resolveTargetAddressedPreview = void 0;
4
4
  const http_proxy_middleware_1 = require("http-proxy-middleware");
5
5
  const crypto_1 = require("crypto");
6
6
  const logger_1 = require("../utils/logger");
7
+ const rivetPage_1 = require("../utils/rivetPage");
7
8
  const htmlDocument_1 = require("../utils/htmlDocument");
8
9
  const preview_connections_1 = require("./preview-connections");
9
10
  const preview_bridge_1 = require("./preview-bridge");
@@ -74,36 +75,6 @@ const markPreviewBridgeDocumentUncacheable = (proxyRes) => {
74
75
  proxyRes.headers['cache-control'] = 'no-cache, no-store';
75
76
  };
76
77
  exports.markPreviewBridgeDocumentUncacheable = markPreviewBridgeDocumentUncacheable;
77
- const buildPreviewHoldingPage = () => `<!doctype html>
78
- <html lang="en">
79
- <head>
80
- <meta charset="utf-8" />
81
- <meta name="viewport" content="width=device-width, initial-scale=1" />
82
- <title>Preview — waiting for dev server</title>
83
- <style>
84
- html, body { height: 100%; margin: 0; }
85
- body {
86
- display: flex; align-items: center; justify-content: center;
87
- background: #0f0f10; color: #a1a1aa;
88
- font: 14px/1.5 ui-sans-serif, system-ui, -apple-system, sans-serif;
89
- }
90
- .wrap { display: flex; flex-direction: column; align-items: center; gap: 14px; padding: 24px; text-align: center; }
91
- .spinner {
92
- width: 22px; height: 22px; border: 2px solid #2a2a2e; border-top-color: #a1a1aa;
93
- border-radius: 50%; animation: rivet-preview-spin 0.8s linear infinite;
94
- }
95
- @keyframes rivet-preview-spin { to { transform: rotate(360deg); } }
96
- @media (prefers-reduced-motion: reduce) { .spinner { animation: none; } }
97
- </style>
98
- </head>
99
- <body>
100
- <div class="wrap">
101
- <div class="spinner" aria-hidden="true"></div>
102
- <div>Waiting for the preview dev server…</div>
103
- </div>
104
- </body>
105
- </html>`;
106
- exports.buildPreviewHoldingPage = buildPreviewHoldingPage;
107
78
  const createUserDevServerProxy = (userPort, userHost = 'localhost', options = {}) => {
108
79
  let variantTarget = null;
109
80
  const requestTargets = new WeakMap();
@@ -311,9 +282,10 @@ const createUserDevServerProxy = (userPort, userHost = 'localhost', options = {}
311
282
  if ('writeHead' in res && !res.headersSent) {
312
283
  if (isPreviewBridgeEnabled && (0, preview_bridge_1.isHtmlDocumentRequest)(req)) {
313
284
  log.debug(`Serving preview holding page — no upstream yet (userPort=${userPort}).`);
314
- const body = (0, preview_bridge_1.injectPreviewBridge)((0, exports.buildPreviewHoldingPage)(), {
315
- expectedParentOrigin: options.expectedPreviewParentOrigin,
316
- });
285
+ const body = (0, preview_bridge_1.injectPreviewBridge)((0, rivetPage_1.renderRivetPage)({
286
+ page: { kind: 'preview-waiting' },
287
+ head: { title: 'Preview — waiting for dev server' },
288
+ }), { expectedParentOrigin: options.expectedPreviewParentOrigin });
317
289
  res.writeHead(200, {
318
290
  'Content-Type': 'text/html; charset=utf-8',
319
291
  'Cache-Control': 'no-cache, no-store',
@@ -23,6 +23,7 @@ const express_1 = require("express");
23
23
  const v3_1 = require("zod/v3");
24
24
  const api_responses_1 = require("../types/api-responses");
25
25
  const logger_1 = require("../utils/logger");
26
+ const rivetPage_1 = require("../utils/rivetPage");
26
27
  const agent_variants_1 = require("../agent-variants");
27
28
  const changeBatchClassification_1 = require("../mcp/changeBatchClassification");
28
29
  const createAgentVariantsOrchestrator_1 = require("../services/createAgentVariantsOrchestrator");
@@ -167,22 +168,6 @@ const HISTORY_STATUSES = new Set([
167
168
  ]);
168
169
  const VARIANT_SHARE_TITLE_FALLBACK = 'Prototype';
169
170
  const CANVAS_SHARE_TITLE_FALLBACK = 'Prototype canvas';
170
- const escapeHtml = (value) => value.replace(/[&<>"']/g, (char) => {
171
- switch (char) {
172
- case '&':
173
- return '&amp;';
174
- case '<':
175
- return '&lt;';
176
- case '>':
177
- return '&gt;';
178
- case '"':
179
- return '&quot;';
180
- case "'":
181
- return '&#39;';
182
- default:
183
- return char;
184
- }
185
- });
186
171
  const getVariantShareTitle = (manifest) => {
187
172
  const title = [manifest.label, manifest.brief, manifest.sessionPrompt].find((candidate) => candidate.trim().length > 0);
188
173
  return (0, PrototypeDeployService_1.normalizePrototypeShareTitle)(title) ?? VARIANT_SHARE_TITLE_FALLBACK;
@@ -190,165 +175,27 @@ const getVariantShareTitle = (manifest) => {
190
175
  const getCanvasShareTitle = (sessionPrompt) => {
191
176
  return ((0, PrototypeDeployService_1.normalizePrototypeShareTitle)(sessionPrompt) ?? CANVAS_SHARE_TITLE_FALLBACK);
192
177
  };
193
- const CANVAS_IFRAME_SANDBOX = 'allow-scripts allow-forms allow-popups';
194
178
  const RIVET_SOCIAL_IMAGE_URL = 'https://rivet.design/images/rivet-social.png';
195
179
  const RIVET_DEFAULT_SHARE_DESCRIPTION = 'Built with Rivet — the visual editor for design';
196
180
  const UNSUPPORTED_PROTOTYPE_DEPLOY_MESSAGE = "This prototype can't be deployed — only buildable app directions or static previews are supported.";
197
- const buildCanvasShareHtml = (args) => {
198
- const prompt = args.sessionPrompt.trim();
199
- const variantCards = args.variants
200
- .map((variant, index) => {
201
- const label = variant.label.trim() || `Direction ${index + 1}`;
202
- const brief = variant.brief.trim();
203
- return `<article class="variant-card">
204
- <header>
205
- <div>
206
- <p class="eyebrow">Direction ${index + 1}</p>
207
- <h2>${escapeHtml(label)}</h2>
208
- </div>
209
- <a href="${escapeHtml(variant.shareUrl)}" target="_blank" rel="noreferrer">Open</a>
210
- </header>
211
- ${brief ? `<p class="brief">${escapeHtml(brief)}</p>` : ''}
212
- <iframe title="${escapeHtml(label)}" src="${escapeHtml(variant.shareUrl)}" loading="lazy" sandbox="${CANVAS_IFRAME_SANDBOX}"></iframe>
213
- </article>`;
214
- })
215
- .join('\n');
216
- const canvasTitle = `Rivet Canvas — ${args.variants.length} directions`;
217
- const escapedTitle = escapeHtml(canvasTitle);
218
- const escapedDesc = escapeHtml(RIVET_DEFAULT_SHARE_DESCRIPTION);
219
- return `<!doctype html>
220
- <html lang="en">
221
- <head>
222
- <meta charset="utf-8">
223
- <meta name="viewport" content="width=device-width, initial-scale=1">
224
- <title>${escapedTitle}</title>
225
- <meta name="description" content="${escapedDesc}" />
226
- <meta property="og:type" content="website" />
227
- <meta property="og:title" content="${escapedTitle}" />
228
- <meta property="og:description" content="${escapedDesc}" />
229
- <meta property="og:image" content="${RIVET_SOCIAL_IMAGE_URL}" />
230
- <meta name="twitter:card" content="summary_large_image" />
231
- <meta name="twitter:title" content="${escapedTitle}" />
232
- <meta name="twitter:description" content="${escapedDesc}" />
233
- <meta name="twitter:image" content="${RIVET_SOCIAL_IMAGE_URL}" />
234
- <style>
235
- :root {
236
- color-scheme: light;
237
- --bg: #f6f4ef;
238
- --card: #ffffff;
239
- --ink: #161615;
240
- --muted: #6f6a60;
241
- --border: #ded8cc;
242
- --accent: #ff5a1f;
243
- }
244
- * { box-sizing: border-box; }
245
- body {
246
- margin: 0;
247
- background: var(--bg);
248
- color: var(--ink);
249
- font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
250
- }
251
- main {
252
- width: min(1440px, calc(100vw - 32px));
253
- margin: 0 auto;
254
- padding: 32px 0 48px;
255
- }
256
- .hero {
257
- display: flex;
258
- align-items: flex-end;
259
- justify-content: space-between;
260
- gap: 24px;
261
- margin-bottom: 24px;
262
- }
263
- h1 {
264
- margin: 0;
265
- font-size: clamp(32px, 5vw, 64px);
266
- letter-spacing: -0.06em;
267
- line-height: 0.95;
268
- }
269
- .prompt {
270
- max-width: 720px;
271
- color: var(--muted);
272
- font-size: 15px;
273
- line-height: 1.5;
274
- }
275
- .grid {
276
- display: grid;
277
- grid-template-columns: repeat(auto-fit, minmax(420px, 1fr));
278
- gap: 18px;
279
- }
280
- .variant-card {
281
- overflow: hidden;
282
- border: 1px solid var(--border);
283
- border-radius: 18px;
284
- background: var(--card);
285
- box-shadow: 0 20px 60px rgba(22, 22, 21, 0.08);
286
- }
287
- .variant-card header {
288
- display: flex;
289
- align-items: flex-start;
290
- justify-content: space-between;
291
- gap: 16px;
292
- padding: 18px 18px 0;
293
- }
294
- .eyebrow {
295
- margin: 0 0 6px;
296
- color: var(--accent);
297
- font-size: 11px;
298
- font-weight: 700;
299
- letter-spacing: 0.12em;
300
- text-transform: uppercase;
301
- }
302
- h2 {
303
- margin: 0;
304
- font-size: 20px;
305
- letter-spacing: -0.03em;
306
- }
307
- a {
308
- color: var(--ink);
309
- font-size: 13px;
310
- font-weight: 700;
311
- text-decoration: none;
312
- border-bottom: 1px solid currentColor;
313
- }
314
- .brief {
315
- margin: 10px 18px 16px;
316
- color: var(--muted);
317
- font-size: 13px;
318
- line-height: 1.45;
319
- }
320
- iframe {
321
- display: block;
322
- width: 100%;
323
- height: 720px;
324
- border: 0;
325
- border-top: 1px solid var(--border);
326
- background: white;
327
- }
328
- @media (max-width: 640px) {
329
- main { width: min(100vw - 20px, 1440px); padding-top: 20px; }
330
- .hero { display: block; }
331
- .grid { grid-template-columns: 1fr; }
332
- iframe { height: 560px; }
333
- }
334
- </style>
335
- </head>
336
- <body>
337
- <main>
338
- <section class="hero">
339
- <div>
340
- <p class="eyebrow">Rivet Canvas</p>
341
- <h1>${args.variants.length} directions</h1>
342
- </div>
343
- ${prompt ? `<p class="prompt">${escapeHtml(prompt)}</p>` : ''}
344
- </section>
345
- <section class="grid">
346
- ${variantCards}
347
- </section>
348
- </main>
349
- </body>
350
- </html>`;
351
- };
181
+ const buildCanvasSharePage = (args) => (0, rivetPage_1.renderRivetPage)({
182
+ page: {
183
+ kind: 'canvas-share',
184
+ prompt: args.sessionPrompt.trim(),
185
+ directions: args.variants.map((variant, index) => ({
186
+ label: variant.label.trim() || `Direction ${index + 1}`,
187
+ brief: variant.brief.trim(),
188
+ shareUrl: variant.shareUrl,
189
+ })),
190
+ },
191
+ head: {
192
+ title: `Rivet Canvas — ${args.variants.length} directions`,
193
+ shareCard: {
194
+ description: RIVET_DEFAULT_SHARE_DESCRIPTION,
195
+ imageUrl: RIVET_SOCIAL_IMAGE_URL,
196
+ },
197
+ },
198
+ });
352
199
  const generatedProjectWorkspaceRoot = (projectPath) => {
353
200
  const normalized = path_1.default.normalize(projectPath);
354
201
  const segments = normalized.split(path_1.default.sep);
@@ -1811,7 +1658,7 @@ const createAgentVariantsRouter = (orchestrator, bridge, telemetry, variantHisto
1811
1658
  }
1812
1659
  deployedVariants.push(deployed.variant);
1813
1660
  }
1814
- const html = buildCanvasShareHtml({
1661
+ const html = buildCanvasSharePage({
1815
1662
  sessionPrompt,
1816
1663
  variants: deployedVariants,
1817
1664
  });
@@ -42,7 +42,7 @@ const path_1 = __importDefault(require("path"));
42
42
  const fs_1 = __importDefault(require("fs"));
43
43
  const logger_1 = require("../utils/logger");
44
44
  const preview_bridge_1 = require("../proxy-middleware/preview-bridge");
45
- const emptyStatePage_1 = require("./emptyStatePage");
45
+ const rivetPage_1 = require("../utils/rivetPage");
46
46
  const log = (0, logger_1.createLogger)('StaticRoutes');
47
47
  const createStaticRoutes = (staticService, _projectPath, options = {}) => {
48
48
  const router = (0, express_1.Router)();
@@ -62,7 +62,13 @@ const createStaticRoutes = (staticService, _projectPath, options = {}) => {
62
62
  res.sendFile(entryFileName, { root: basePath });
63
63
  };
64
64
  const sendEmptyState = (res) => {
65
- res.status(200).type('html').send((0, emptyStatePage_1.renderEmptyState)());
65
+ res
66
+ .status(200)
67
+ .type('html')
68
+ .send((0, rivetPage_1.renderRivetPage)({
69
+ page: { kind: 'empty-project' },
70
+ head: { title: 'Empty project' },
71
+ }));
66
72
  };
67
73
  router.get('/static', (_req, res) => {
68
74
  const entryPath = path_1.default.join(basePath, entryFileName);
package/dist/server.d.ts CHANGED
@@ -7,7 +7,6 @@ import type { VariantRunService } from './services/VariantRunService';
7
7
  import type { VariantsRuntime } from './services/VariantsRuntime';
8
8
  export declare const INACTIVE_VARIANTS_EVENT_HEARTBEAT_MS = 30000;
9
9
  export declare const RIVET_CORS_ALLOWED_HEADERS: string[];
10
- export declare const DIST_UI_PATH: string;
11
10
  type RivetUiBootstrap = {
12
11
  agentApplyMode: 'embedded' | 'external' | null;
13
12
  queueAccessToken: string | null;
@@ -58,6 +57,7 @@ export type ServerOptions = Omit<CLIOptions, 'help' | 'version' | 'debug'> & {
58
57
  userHost?: string;
59
58
  beforeShutdown?: () => Promise<void>;
60
59
  resolveLibraryOrigin?: () => string | undefined;
60
+ resolveProjectName?: (projectPath: string) => string | undefined;
61
61
  createLibraryViewUrl?: (view: 'projects' | 'references') => Promise<string | null>;
62
62
  };
63
63
  export type ServerHandle = {
package/dist/server.js CHANGED
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.startServer = exports.runServerShutdownTasks = exports.isRivetCorsOriginAllowed = exports.isRivetUiStaticAssetPath = exports.isEmbeddedAgentUiHost = exports.resolveEmbeddedAgentUiOrigin = exports.resolveRivetListenHost = exports.sendInactiveVariantsEventStream = exports.readRivetUiIndexHtml = exports.injectRivetUiBootstrap = exports.DIST_UI_PATH = exports.RIVET_CORS_ALLOWED_HEADERS = exports.INACTIVE_VARIANTS_EVENT_HEARTBEAT_MS = void 0;
39
+ exports.startServer = exports.runServerShutdownTasks = exports.isRivetCorsOriginAllowed = exports.isRivetUiStaticAssetPath = exports.isEmbeddedAgentUiHost = exports.resolveEmbeddedAgentUiOrigin = exports.resolveRivetListenHost = exports.sendInactiveVariantsEventStream = exports.readRivetUiIndexHtml = exports.injectRivetUiBootstrap = exports.RIVET_CORS_ALLOWED_HEADERS = exports.INACTIVE_VARIANTS_EVENT_HEARTBEAT_MS = void 0;
40
40
  const editorSession_1 = require("./cli/editorSession");
41
41
  const editorAccess_1 = require("./routes/editorAccess");
42
42
  const editorAccess_2 = require("./utils/editorAccess");
@@ -77,6 +77,8 @@ const VariantHistoryService_1 = require("./services/VariantHistoryService");
77
77
  const RequestAuthContext_1 = require("./services/RequestAuthContext");
78
78
  const middleware_1 = require("./routes/library/middleware");
79
79
  const errorMessage_1 = require("./utils/errorMessage");
80
+ const scriptJson_1 = require("./utils/scriptJson");
81
+ const uiDistPath_1 = require("./utils/uiDistPath");
80
82
  const pluginContext_1 = require("./hosts/pluginContext");
81
83
  const log = (0, index_core_1.createLogger)('RivetServer');
82
84
  const QUEUE_ACCESS_TOKEN_BYTES = 32;
@@ -93,36 +95,21 @@ exports.RIVET_CORS_ALLOWED_HEADERS = [
93
95
  'x-rivet-queue-token',
94
96
  editorAccess_2.EDITOR_ACCESS_TOKEN_HEADER,
95
97
  ];
96
- const resolveUiPath = () => {
97
- const bundledPath = path_1.default.join(__dirname, 'ui/dist');
98
- const devPath = path_1.default.join(__dirname, '../src/ui/dist');
99
- if (fs_1.default.existsSync(bundledPath)) {
100
- log.debug(`Using bundled UI path: ${bundledPath}`);
101
- return bundledPath;
102
- }
103
- log.debug(`Using development UI path: ${devPath}`);
104
- return devPath;
105
- };
106
- exports.DIST_UI_PATH = resolveUiPath();
107
98
  const NO_USER_DEV_SERVER_PORT = 0;
108
99
  const setNoStoreHeaders = (res) => {
109
100
  res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
110
101
  res.setHeader('Pragma', 'no-cache');
111
102
  res.setHeader('Expires', '0');
112
103
  };
113
- const escapeScriptJson = (value) => JSON.stringify(value)
114
- .replace(/</g, '\\u003c')
115
- .replace(/\u2028/g, '\\u2028')
116
- .replace(/\u2029/g, '\\u2029');
117
104
  const injectRivetUiBootstrap = (html, bootstrap) => {
118
- const script = `<script id="${RIVET_BOOTSTRAP_SCRIPT_ID}">window.__RIVET_BOOTSTRAP__=${escapeScriptJson(bootstrap)};</script>`;
105
+ const script = `<script id="${RIVET_BOOTSTRAP_SCRIPT_ID}">window.__RIVET_BOOTSTRAP__=${(0, scriptJson_1.escapeScriptJson)(bootstrap)};</script>`;
119
106
  if (html.includes('</head>')) {
120
107
  return html.replace('</head>', `${script}</head>`);
121
108
  }
122
109
  return `${script}${html}`;
123
110
  };
124
111
  exports.injectRivetUiBootstrap = injectRivetUiBootstrap;
125
- const readRivetUiIndexHtml = () => fs_1.default.readFileSync(path_1.default.join(exports.DIST_UI_PATH, RIVET_UI_INDEX_FILENAME), 'utf8');
112
+ const readRivetUiIndexHtml = () => fs_1.default.readFileSync(path_1.default.join(uiDistPath_1.DIST_UI_PATH, RIVET_UI_INDEX_FILENAME), 'utf8');
126
113
  exports.readRivetUiIndexHtml = readRivetUiIndexHtml;
127
114
  const sendInactiveVariantsEventStream = (req, res) => {
128
115
  res.set({
@@ -366,15 +353,15 @@ const startServer = async (options) => {
366
353
  const serve = express_1.default.static(directory, uiStaticOptions);
367
354
  return (req, res, next) => isPreviewRequest(req) ? next() : serve(req, res, next);
368
355
  };
369
- app.use('/assets', serveEditorAssets(path_1.default.join(exports.DIST_UI_PATH, 'assets')));
370
- app.use('/rivet/assets', serveEditorAssets(path_1.default.join(exports.DIST_UI_PATH, 'assets')));
371
- app.use('/fonts', serveEditorAssets(path_1.default.join(exports.DIST_UI_PATH, 'fonts')));
372
- app.use('/rivet/fonts', serveEditorAssets(path_1.default.join(exports.DIST_UI_PATH, 'fonts')));
356
+ app.use('/assets', serveEditorAssets(path_1.default.join(uiDistPath_1.DIST_UI_PATH, 'assets')));
357
+ app.use('/rivet/assets', serveEditorAssets(path_1.default.join(uiDistPath_1.DIST_UI_PATH, 'assets')));
358
+ app.use('/fonts', serveEditorAssets(path_1.default.join(uiDistPath_1.DIST_UI_PATH, 'fonts')));
359
+ app.use('/rivet/fonts', serveEditorAssets(path_1.default.join(uiDistPath_1.DIST_UI_PATH, 'fonts')));
373
360
  app.get('/favicon.ico', (_req, res) => {
374
- res.sendFile('favicon.ico', { root: exports.DIST_UI_PATH });
361
+ res.sendFile('favicon.ico', { root: uiDistPath_1.DIST_UI_PATH });
375
362
  });
376
363
  app.get('/rivet/favicon.ico', (_req, res) => {
377
- res.sendFile('favicon.ico', { root: exports.DIST_UI_PATH });
364
+ res.sendFile('favicon.ico', { root: uiDistPath_1.DIST_UI_PATH });
378
365
  });
379
366
  app.get(/^\/rivet\/library(?:\/.*)?$/, (req, res) => {
380
367
  const resolvedLibraryOrigin = options.resolveLibraryOrigin?.();
@@ -391,7 +378,7 @@ const startServer = async (options) => {
391
378
  }
392
379
  sendRivetUiIndex(req, res);
393
380
  });
394
- log.info(`Serving UI assets from: ${exports.DIST_UI_PATH}`);
381
+ log.info(`Serving UI assets from: ${uiDistPath_1.DIST_UI_PATH}`);
395
382
  if (framework === 'static' && !hasUserDevServer) {
396
383
  app.get('/', (req, res, next) => {
397
384
  if (isPreviewRequest(req)) {
@@ -511,6 +498,7 @@ const startServer = async (options) => {
511
498
  ? (agentApplyMode ?? 'external')
512
499
  : null,
513
500
  projectPath: livePath,
501
+ projectName: options.resolveProjectName?.(livePath) ?? path_1.default.basename(livePath),
514
502
  projectId: options.projectId ?? null,
515
503
  libraryOrigin: options.resolveLibraryOrigin?.() ?? null,
516
504
  });
@@ -36,6 +36,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.AuthService = exports.validateAccessToken = void 0;
37
37
  const zod_1 = require("zod");
38
38
  const logger_1 = require("../utils/logger");
39
+ const rivetPage_1 = require("../utils/rivetPage");
40
+ const rivet_page_types_1 = require("../types/rivet-page-types");
39
41
  const ConfigManager_1 = require("./ConfigManager");
40
42
  const http_1 = require("http");
41
43
  const accessTokenRefresh_1 = require("./accessTokenRefresh");
@@ -46,7 +48,6 @@ const proxy_1 = require("../config/proxy");
46
48
  const log = (0, logger_1.createLogger)('AuthService');
47
49
  const LOOPBACK_HOST = '127.0.0.1';
48
50
  const LOOPBACK_PORT = 0;
49
- const CALLBACK_COMPLETE_PATH = '/complete';
50
51
  const OAUTH_CALLBACK_TIMEOUT_MS = 120_000;
51
52
  const MAX_CALLBACK_BODY_BYTES = 64 * 1024;
52
53
  const PROFILE_FETCH_TIMEOUT_MS = 10_000;
@@ -101,64 +102,6 @@ const readRequestBody = (request) => {
101
102
  request.on('error', reject);
102
103
  });
103
104
  };
104
- const getLoopbackCallbackHtml = () => {
105
- return `<!doctype html>
106
- <html>
107
- <head>
108
- <meta charset="utf-8" />
109
- <meta name="viewport" content="width=device-width, initial-scale=1" />
110
- <title>Rivet authentication</title>
111
- <style>
112
- body {
113
- margin: 0;
114
- min-height: 100vh;
115
- display: grid;
116
- place-items: center;
117
- font-family: system-ui, sans-serif;
118
- background: #0b0b0f;
119
- color: #fff;
120
- }
121
- main {
122
- max-width: 420px;
123
- padding: 32px;
124
- text-align: center;
125
- }
126
- </style>
127
- </head>
128
- <body>
129
- <main>
130
- <h1>Finishing Rivet sign in...</h1>
131
- <p>You can return to your editor after this page completes.</p>
132
- </main>
133
- <script>
134
- const params = new URLSearchParams(window.location.search);
135
- const hash = new URLSearchParams(window.location.hash.slice(1));
136
- if (window.location.hash) {
137
- window.history.replaceState(
138
- null,
139
- document.title,
140
- window.location.pathname + window.location.search
141
- );
142
- }
143
- fetch('/complete', {
144
- method: 'POST',
145
- headers: { 'Content-Type': 'application/json' },
146
- body: JSON.stringify({
147
- sessionId: params.get('session'),
148
- accessToken: hash.get('access_token'),
149
- refreshToken: hash.get('refresh_token')
150
- })
151
- }).then((response) => {
152
- document.querySelector('h1').textContent = response.ok
153
- ? 'Rivet sign in complete'
154
- : 'Rivet sign in failed';
155
- }).catch(() => {
156
- document.querySelector('h1').textContent = 'Rivet sign in failed';
157
- });
158
- </script>
159
- </body>
160
- </html>`;
161
- };
162
105
  const validateAccessToken = async (proxyUrl, accessToken) => {
163
106
  const response = await fetch(`${proxyUrl.replace(/\/$/, '')}/api/user/profile`, {
164
107
  method: 'GET',
@@ -204,11 +147,14 @@ const createLoopbackOAuthCallback = async () => {
204
147
  const server = (0, http_1.createServer)((request, response) => {
205
148
  const url = new URL(request.url ?? '/', `http://${LOOPBACK_HOST}`);
206
149
  if (request.method === 'GET') {
207
- sendTextResponse(response, 200, getLoopbackCallbackHtml());
150
+ sendTextResponse(response, 200, (0, rivetPage_1.renderRivetPage)({
151
+ page: { kind: 'sign-in-callback' },
152
+ head: { title: 'Rivet authentication' },
153
+ }));
208
154
  return;
209
155
  }
210
156
  if (request.method !== 'POST' ||
211
- url.pathname !== CALLBACK_COMPLETE_PATH) {
157
+ url.pathname !== rivet_page_types_1.SIGN_IN_CALLBACK_COMPLETE_PATH) {
212
158
  sendTextResponse(response, 404, 'Not found');
213
159
  return;
214
160
  }
@@ -200,7 +200,7 @@ textarea {
200
200
 
201
201
  .eyebrow {
202
202
  margin: 0 0 16px;
203
- color: #ff3300;
203
+ color: #e54b2c;
204
204
  font-size: 13px;
205
205
  font-weight: 700;
206
206
  letter-spacing: 0;
@@ -0,0 +1,48 @@
1
+ export declare const RIVET_PAGE_DATA_ELEMENT_ID = "rivet-page";
2
+ export declare const SIGN_IN_CALLBACK_COMPLETE_PATH = "/complete";
3
+ export type CanvasShareDirection = {
4
+ label: string;
5
+ brief: string;
6
+ shareUrl: string;
7
+ };
8
+ export type SharedShellRivetPage = {
9
+ kind: 'preview-waiting';
10
+ } | {
11
+ kind: 'sign-in-callback';
12
+ } | {
13
+ kind: 'canvas-share';
14
+ prompt: string;
15
+ directions: CanvasShareDirection[];
16
+ };
17
+ export type RivetPage = {
18
+ kind: 'empty-project';
19
+ } | SharedShellRivetPage;
20
+ export type ConnectResultMessage = {
21
+ type: 'rivet:connect-result';
22
+ provider: string;
23
+ status: 'connected' | 'error';
24
+ message?: string;
25
+ };
26
+ export type ConnectResultClosePolicy = 'always' | 'after-notifying-opener';
27
+ export type HostedRivetPage = {
28
+ kind: 'google-sign-in';
29
+ initiator: string;
30
+ providerAuthUrl: string;
31
+ } | {
32
+ kind: 'sign-in-link-expired';
33
+ } | {
34
+ kind: 'connect-result';
35
+ result: ConnectResultMessage;
36
+ notice: string;
37
+ closePolicy: ConnectResultClosePolicy;
38
+ };
39
+ export type RivetPageShareCard = {
40
+ description: string;
41
+ imageUrl: string;
42
+ };
43
+ export type RivetPageHead = {
44
+ title: string;
45
+ shareCard?: RivetPageShareCard;
46
+ robots?: 'noindex';
47
+ };
48
+ //# sourceMappingURL=rivet-page-types.d.ts.map
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SIGN_IN_CALLBACK_COMPLETE_PATH = exports.RIVET_PAGE_DATA_ELEMENT_ID = void 0;
4
+ exports.RIVET_PAGE_DATA_ELEMENT_ID = 'rivet-page';
5
+ exports.SIGN_IN_CALLBACK_COMPLETE_PATH = '/complete';
6
+ //# sourceMappingURL=rivet-page-types.js.map