@lepsto/app-dev 0.2.0 → 0.3.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @lepsto/app-dev
2
2
 
3
- Vite plugin for local development of Lepsto Apps. It proxies `/lessly-api/*` to the
3
+ Vite plugin for local development of Lepsto Apps. It proxies `/lepsto-api/*` (and, for apps scaffolded before the rename, `/lessly-api/*`) to the
4
4
  Lepsto REST API with an automatically-injected bearer token, exposes the dev
5
5
  `import.meta.env` values your App needs, and serves the Module-Federation manifest
6
6
  at the origin root.
@@ -10,10 +10,10 @@ at the origin root.
10
10
  ```ts
11
11
  // vite.config.ts
12
12
  import { defineConfig } from 'vite';
13
- import { lesslyAppDev } from '@lepsto/app-dev';
13
+ import { lepstoAppDev } from '@lepsto/app-dev';
14
14
 
15
15
  export default defineConfig({
16
- plugins: [lesslyAppDev()],
16
+ plugins: [lepstoAppDev()],
17
17
  });
18
18
  ```
19
19
 
@@ -41,7 +41,7 @@ localStorage.setItem('lessly:dev-remote', '<app-slug>=http://localhost:5174')
41
41
  The contract has two halves that are easy to confuse:
42
42
 
43
43
  - **key** — the App's Module Federation remote name, i.e. `id` in
44
- `lessly.app.yaml` (= `APP_NAME` in `config/app-name.mjs`). Not the product
44
+ `lepsto.app.yaml` / `lessly.app.yaml` (= `APP_NAME` in `config/app-name.mjs`). Not the product
45
45
  slug.
46
46
  - **value** — the App's **own** dev origin, a bare origin with no base path. The
47
47
  shell fetches `<origin>/mf-manifest.json` at the origin root; the plugin's
@@ -56,14 +56,14 @@ shell from loading an `http://localhost` remote.
56
56
 
57
57
  ## Authentication
58
58
 
59
- The plugin injects `Authorization: Bearer <token>` on every `/lessly-api/*` request
59
+ The plugin injects `Authorization: Bearer <token>` on every `/lepsto-api/*` (or legacy `/lessly-api/*`) request
60
60
  using, in order: a valid cached token → a stored token in `~/.lessly/credentials.json`
61
61
  → a refreshed token → `DEV_API_KEY` exchange → interactive device login. Token
62
62
  refresh is coordinated across processes with a file lock beside the credentials file.
63
63
 
64
64
  ## Scope: REST-only (v1)
65
65
 
66
- **This plugin proxies HTTP REST calls only. WebSocket upgrades on `/lessly-api/*`
66
+ **This plugin proxies HTTP REST calls only. WebSocket upgrades on `/lepsto-api/*`
67
67
  are intentionally not proxied in v1** — Lepsto Apps use the REST API for local
68
68
  development, and adding WS proxying (and its auth/reconnect semantics) is deferred
69
69
  until a concrete need exists. If you require realtime transport locally, connect to
package/dist/index.d.ts CHANGED
@@ -1,24 +1,30 @@
1
1
  import { Plugin } from 'vite';
2
2
 
3
- type LesslyEnvName = 'staging' | 'prod';
4
- interface LesslyAppDevOptions {
3
+ type LepstoEnvName = 'staging' | 'prod';
4
+ /** @deprecated Use {@link LepstoEnvName}. */
5
+ type LesslyEnvName = LepstoEnvName;
6
+ interface LepstoAppDevOptions {
5
7
  /** Override env; defaults to LEPSTO_ENV (deprecated fallback: LESSLY_ENV) or 'staging'. */
6
- env?: LesslyEnvName;
8
+ env?: LepstoEnvName;
7
9
  /** Override REST API base; defaults to VITE_API_URL or the env default. */
8
10
  apiUrl?: string;
9
11
  /** Vite dev-server proxy path. Fixed contract C1. */
10
- proxyPath?: '/lessly-api';
12
+ proxyPath?: '/lepsto-api' | '/lessly-api';
11
13
  }
14
+ /** @deprecated Use {@link LepstoAppDevOptions}. */
15
+ type LesslyAppDevOptions = LepstoAppDevOptions;
12
16
  interface ResolvedEnv {
13
- name: LesslyEnvName;
17
+ name: LepstoEnvName;
14
18
  apiUrl: string;
15
19
  authServer: string;
16
- profile: LesslyEnvName;
20
+ /** Deployed Workspace shell for this environment — where a developer loads their App. */
21
+ shellOrigin: string;
22
+ profile: LepstoEnvName;
17
23
  }
18
24
 
19
25
  declare function resolveShellDevOrigin(processEnv?: Record<string, string | undefined>): string;
20
26
 
21
- interface LesslyAppDevDeps {
27
+ interface LepstoAppDevDeps {
22
28
  /** Override the home dir used to locate ~/.lessly (tests). */
23
29
  homedir?: () => string;
24
30
  /** Override token-provider construction (tests / advanced embedding). */
@@ -29,6 +35,10 @@ interface LesslyAppDevDeps {
29
35
  get(): Promise<string>;
30
36
  };
31
37
  }
32
- declare function lesslyAppDev(options?: LesslyAppDevOptions, deps?: LesslyAppDevDeps): Plugin;
38
+ declare function lepstoAppDev(options?: LepstoAppDevOptions, deps?: LepstoAppDevDeps): Plugin;
39
+ /** @deprecated Use {@link lepstoAppDev}. Kept as an alias for existing configs. */
40
+ declare const lesslyAppDev: typeof lepstoAppDev;
41
+ /** @deprecated Use {@link LepstoAppDevDeps}. */
42
+ type LesslyAppDevDeps = LepstoAppDevDeps;
33
43
 
34
- export { type LesslyAppDevDeps, type LesslyAppDevOptions, type LesslyEnvName, type ResolvedEnv, lesslyAppDev, resolveShellDevOrigin };
44
+ export { type LepstoAppDevDeps, type LepstoAppDevOptions, type LepstoEnvName, type LesslyAppDevDeps, type LesslyAppDevOptions, type LesslyEnvName, type ResolvedEnv, lepstoAppDev, lesslyAppDev, resolveShellDevOrigin };
package/dist/index.js CHANGED
@@ -1,11 +1,32 @@
1
1
  // src/index.ts
2
2
  import { loadEnv } from "vite";
3
3
 
4
- // src/env.ts
5
- var HOSTS = {
6
- staging: { apiUrl: "https://api.lessly.dev", authServer: "https://auth.lessly.dev" },
7
- prod: { apiUrl: "https://api.lessly.com", authServer: "https://auth.lessly.com" }
4
+ // src/platform-hosts.ts
5
+ var PLATFORM_BASE_DOMAIN = {
6
+ staging: "lepsto.dev",
7
+ prod: "lessly.com"
8
+ };
9
+ var SUBDOMAIN = {
10
+ staging: { api: "api", auth: "auth", shell: "console" },
11
+ prod: { api: "api", auth: "auth", shell: "app" }
12
+ };
13
+ function platformOrigin(subdomain, env) {
14
+ return `https://${subdomain}.${PLATFORM_BASE_DOMAIN[env]}`;
15
+ }
16
+ var PLATFORM_HOSTS = {
17
+ staging: {
18
+ apiUrl: platformOrigin(SUBDOMAIN.staging.api, "staging"),
19
+ authServer: platformOrigin(SUBDOMAIN.staging.auth, "staging"),
20
+ shellOrigin: platformOrigin(SUBDOMAIN.staging.shell, "staging")
21
+ },
22
+ prod: {
23
+ apiUrl: platformOrigin(SUBDOMAIN.prod.api, "prod"),
24
+ authServer: platformOrigin(SUBDOMAIN.prod.auth, "prod"),
25
+ shellOrigin: platformOrigin(SUBDOMAIN.prod.shell, "prod")
26
+ }
8
27
  };
28
+
29
+ // src/env.ts
9
30
  function resolveEnv(input = {}) {
10
31
  const pe = input.processEnv ?? process.env;
11
32
  let envVar = pe.LEPSTO_ENV;
@@ -16,38 +37,42 @@ function resolveEnv(input = {}) {
16
37
  }
17
38
  }
18
39
  const name = input.env ?? (envVar === "prod" ? "prod" : "staging");
19
- const hosts = HOSTS[name];
40
+ const hosts = PLATFORM_HOSTS[name];
20
41
  return {
21
42
  name,
22
43
  profile: name,
23
44
  apiUrl: input.apiUrl ?? pe.VITE_API_URL ?? hosts.apiUrl,
24
- authServer: hosts.authServer
45
+ authServer: hosts.authServer,
46
+ shellOrigin: hosts.shellOrigin
25
47
  };
26
48
  }
27
49
 
28
50
  // src/proxy.ts
29
51
  function buildProxyConfig(args) {
30
- const prefix = args.proxyPath;
31
- const key = `^${prefix}(?:[/?]|$)`;
52
+ const out = {};
53
+ for (const prefix of [args.proxyPath, ...args.legacyPaths ?? []]) {
54
+ out[`^${prefix}(?:[/?]|$)`] = proxyEntry(args.apiUrl, prefix);
55
+ }
56
+ return out;
57
+ }
58
+ function proxyEntry(apiUrl, prefix) {
32
59
  return {
33
- [key]: {
34
- target: args.apiUrl,
35
- changeOrigin: true,
36
- rewrite: (path) => path.replace(new RegExp(`^${prefix}`), ""),
37
- // Bearer injection happens in the awaited auth middleware (see auth-middleware.ts);
38
- // Vite forwards incoming request headers to the upstream. Here we only surface a
39
- // one-time hint when the upstream rejects our token.
40
- configure: (proxy) => {
41
- let warned = false;
42
- proxy.on("proxyRes", (proxyRes) => {
43
- if (proxyRes.statusCode === 401 && !warned) {
44
- warned = true;
45
- console.warn(
46
- " [lessly-app-dev] upstream returned 401 Unauthorized. Your session may be revoked server-side. Delete ~/.lessly/credentials.json and re-run dev to re-login."
47
- );
48
- }
49
- });
50
- }
60
+ target: apiUrl,
61
+ changeOrigin: true,
62
+ rewrite: (path) => path.replace(new RegExp(`^${prefix}`), ""),
63
+ // Bearer injection happens in the awaited auth middleware (see auth-middleware.ts);
64
+ // Vite forwards incoming request headers to the upstream. Here we only surface a
65
+ // one-time hint when the upstream rejects our token.
66
+ configure: (proxy) => {
67
+ let warned = false;
68
+ proxy.on("proxyRes", (proxyRes) => {
69
+ if (proxyRes.statusCode === 401 && !warned) {
70
+ warned = true;
71
+ console.warn(
72
+ " [lessly-app-dev] upstream returned 401 Unauthorized. Your session may be revoked server-side. Delete ~/.lessly/credentials.json and re-run dev to re-login."
73
+ );
74
+ }
75
+ });
51
76
  }
52
77
  };
53
78
  }
@@ -64,7 +89,12 @@ function productIdMissingWarning() {
64
89
  }
65
90
  function buildDefine(args) {
66
91
  const define = {
67
- "import.meta.env.VITE_LESSLY_BASE_URL": JSON.stringify(args.proxyPath)
92
+ "import.meta.env.VITE_LEPSTO_BASE_URL": JSON.stringify(args.proxyPath),
93
+ // Unchanged for apps scaffolded before the rename: the legacy name still
94
+ // resolves to the legacy prefix, which stays proxied and authenticated.
95
+ "import.meta.env.VITE_LESSLY_BASE_URL": JSON.stringify(
96
+ args.legacyProxyPath ?? args.proxyPath
97
+ )
68
98
  };
69
99
  if (args.productId) define["import.meta.env.VITE_PRODUCT_ID"] = JSON.stringify(args.productId);
70
100
  return define;
@@ -373,7 +403,7 @@ async function resolveApiKeyToken(args) {
373
403
  function resolveShellDevOrigin(processEnv = process.env) {
374
404
  return processEnv.SHELL_DEV_ORIGIN ?? "http://localhost:5173";
375
405
  }
376
- var DEFAULT_SHELL_ORIGIN = "https://app.lessly.dev";
406
+ var DEFAULT_SHELL_ORIGIN = PLATFORM_HOSTS.staging.shellOrigin;
377
407
  function shellOverrideMessage(input) {
378
408
  const { slug, appOrigin, base = "/", productSlug } = input;
379
409
  const shellOrigin = input.shellOrigin ?? DEFAULT_SHELL_ORIGIN;
@@ -401,13 +431,17 @@ function shellOverrideMessage(input) {
401
431
  // src/app-slug.ts
402
432
  import { readFileSync as readFileSync3 } from "fs";
403
433
  import { join as join3 } from "path";
434
+ var MANIFEST_FILES = ["lepsto.app.yaml", "lessly.app.yaml"];
404
435
  function resolveAppSlug(root, readFile = (p) => readFileSync3(p, "utf8")) {
405
436
  let raw;
406
- try {
407
- raw = readFile(join3(root, "lessly.app.yaml"));
408
- } catch {
409
- return void 0;
437
+ for (const file of MANIFEST_FILES) {
438
+ try {
439
+ raw = readFile(join3(root, file));
440
+ break;
441
+ } catch {
442
+ }
410
443
  }
444
+ if (raw === void 0) return void 0;
411
445
  for (const line of raw.split("\n")) {
412
446
  const match = /^id:\s*(.*)$/.exec(line);
413
447
  if (!match) continue;
@@ -436,10 +470,12 @@ function mfManifestMiddleware(getBase) {
436
470
 
437
471
  // src/auth-middleware.ts
438
472
  function createAuthMiddleware(args) {
439
- const boundary = new RegExp(`^${args.proxyPath}(?:[/?]|$)`);
473
+ const boundaries = [args.proxyPath, ...args.legacyPaths ?? []].map(
474
+ (prefix) => new RegExp(`^${prefix}(?:[/?]|$)`)
475
+ );
440
476
  return (req, res, next) => {
441
477
  const url = req.url ?? "";
442
- if (!boundary.test(url)) return next();
478
+ if (!boundaries.some((b) => b.test(url))) return next();
443
479
  if (req.headers["sec-fetch-site"] === "cross-site") {
444
480
  res.statusCode = 403;
445
481
  res.end("cross-site requests to the Lessly dev proxy are not allowed");
@@ -457,8 +493,9 @@ function createAuthMiddleware(args) {
457
493
 
458
494
  // src/index.ts
459
495
  import { homedir as homedir2 } from "os";
460
- var PROXY_PATH = "/lessly-api";
461
- function lesslyAppDev(options = {}, deps = {}) {
496
+ var PROXY_PATH = "/lepsto-api";
497
+ var LEGACY_PROXY_PATHS = ["/lessly-api"];
498
+ function lepstoAppDev(options = {}, deps = {}) {
462
499
  const homedirFn = deps.homedir ?? homedir2;
463
500
  const devDeps = defaultDeviceDeps();
464
501
  const post = devDeps.post;
@@ -491,12 +528,26 @@ function lesslyAppDev(options = {}, deps = {}) {
491
528
  productSlug = fileEnv.VITE_PRODUCT_SLUG;
492
529
  token = buildToken(resolvedEnv, fileEnv.DEV_API_KEY);
493
530
  return {
494
- server: { proxy: buildProxyConfig({ apiUrl: resolvedEnv.apiUrl, proxyPath: PROXY_PATH }) },
495
- define: buildDefine({ productId, proxyPath: PROXY_PATH })
531
+ server: {
532
+ proxy: buildProxyConfig({
533
+ apiUrl: resolvedEnv.apiUrl,
534
+ proxyPath: PROXY_PATH,
535
+ legacyPaths: LEGACY_PROXY_PATHS
536
+ })
537
+ },
538
+ define: buildDefine({
539
+ productId,
540
+ proxyPath: PROXY_PATH,
541
+ legacyProxyPath: LEGACY_PROXY_PATHS[0]
542
+ })
496
543
  };
497
544
  },
498
545
  configureServer(server) {
499
- if (token) server.middlewares.use(createAuthMiddleware({ proxyPath: PROXY_PATH, token }));
546
+ if (token) {
547
+ server.middlewares.use(
548
+ createAuthMiddleware({ proxyPath: PROXY_PATH, legacyPaths: LEGACY_PROXY_PATHS, token })
549
+ );
550
+ }
500
551
  server.middlewares.use(mfManifestMiddleware(() => server.config.base));
501
552
  server.httpServer?.once("listening", () => {
502
553
  const env = resolvedEnv;
@@ -513,6 +564,7 @@ function lesslyAppDev(options = {}, deps = {}) {
513
564
  slug: appSlug,
514
565
  appOrigin,
515
566
  base: server.config.base ?? "/",
567
+ shellOrigin: env.shellOrigin,
516
568
  productSlug
517
569
  })
518
570
  );
@@ -520,7 +572,9 @@ function lesslyAppDev(options = {}, deps = {}) {
520
572
  }
521
573
  };
522
574
  }
575
+ var lesslyAppDev = lepstoAppDev;
523
576
  export {
577
+ lepstoAppDev,
524
578
  lesslyAppDev,
525
579
  resolveShellDevOrigin
526
580
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lepsto/app-dev",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Vite plugin for local development of Lepsto Apps",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",