@everysk/apps-devkit 0.1.0 → 0.3.0-beta-20261016180126

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
@@ -5,12 +5,24 @@
5
5
  The build configuration every Everysk app extends: Vite, ESLint, Vitest, TypeScript and
6
6
  Prettier presets — and the toolchain itself. Declared as a `devDependency`.
7
7
 
8
- Part of [everysk/apps-sdk](https://github.com/everysk/apps-sdk), alongside
9
- [`@everysk/apps-sdk`](../apps-sdk) (the runtime) and [`@everysk/apps-cli`](../apps-cli) (the
10
- `everysk-apps` bin).
8
+ One of three: [`@everysk/apps-sdk`](https://www.npmjs.com/package/@everysk/apps-sdk) is the
9
+ runtime and [`@everysk/apps-cli`](https://www.npmjs.com/package/@everysk/apps-cli) the
10
+ `everysk-apps` bin.
11
+
12
+ ## You probably do not install this by hand
13
+
14
+ ```bash
15
+ npx @everysk/apps-cli init
16
+ ```
17
+
18
+ For a new app, that writes the config files that extend these presets, which is the part worth
19
+ getting right — copying a preset instead of extending it is what stops a fix here ever reaching
20
+ the app.
11
21
 
12
22
  ## Install
13
23
 
24
+ For adding it to an app that already exists:
25
+
14
26
  ```bash
15
27
  npm install --save-dev @everysk/apps-devkit
16
28
  ```
@@ -44,9 +56,14 @@ export default defineAppTestConfig();
44
56
  // eslint.config.js
45
57
  import { everyskConfig } from "@everysk/apps-devkit/eslint";
46
58
 
47
- export default everyskConfig();
59
+ export default everyskConfig({ tsconfigRootDir: import.meta.dirname });
48
60
  ```
49
61
 
62
+ `tsconfigRootDir` is the app's own directory, which only its config can name. In a repository of
63
+ several apps, linting them in one run (an editor does, and so does `eslint` from the root) fails
64
+ with "No tsconfigRootDir was set, and multiple candidate TSConfigRootDirs are present" without it.
65
+ `everyskConfig(extra)` still works, and so does `everyskConfig({ tsconfigRootDir, extra })`.
66
+
50
67
  ```jsonc
51
68
  // tsconfig.app.json
52
69
  {
@@ -63,7 +80,7 @@ export default everyskConfig();
63
80
 
64
81
  | Subpath | |
65
82
  |---|---|
66
- | `/vite` | `defineAppConfig`, plus the dev proxy and the two config plugins on their own |
83
+ | `/vite` | `defineAppConfig`, plus on their own: the dev proxy, the `/file/` route plugin, the token source they share, and the two config plugins |
67
84
  | `/vitest` | `defineAppTestConfig` |
68
85
  | `/eslint` | `everyskConfig` |
69
86
  | `/prettier` | the Prettier config as JSON |
@@ -76,6 +93,11 @@ but `.env` lives at the repository root), the `@` alias, React with the compiler
76
93
  the `/api` dev proxy that authenticates with OAuth2 client credentials and refreshes the token
77
94
  before it expires.
78
95
 
96
+ The dev server and `vite preview` also answer the platform's `/file/<hash>` route, so a file's
97
+ own `url` shows it there as on the platform. That route reads only the user's session, which the
98
+ dev server does not have, so the file is found through the API with the proxy's OAuth2 token,
99
+ by its `url`, and its content decoded and sent inline with its type.
100
+
79
101
  It also serves the app's `config.json` `env_vars` at `/app-config.json` in dev, read **one level
80
102
  above** the Vite root. That is why an app is `<name>/{config.json, project/}`: flatten it and the
81
103
  app boots with no configuration, silently.
@@ -1,4 +1,14 @@
1
1
  import { defineConfig } from "eslint/config";
2
+ export type EveryskConfigOptions = {
3
+ /**
4
+ * The app's directory, `import.meta.dirname` in its `eslint.config.js`. An editor lints a
5
+ * repository of several apps in one ESLint process, where typescript-eslint refuses to pick
6
+ * among their tsconfigs; only the app's own config knows which directory is its root.
7
+ */
8
+ tsconfigRootDir?: string;
9
+ /** Appended after the preset, so an app can add or override rules for its own files. */
10
+ extra?: ReturnType<typeof defineConfig>;
11
+ };
2
12
  /**
3
13
  * everyskConfig
4
14
  *
@@ -6,15 +16,14 @@ import { defineConfig } from "eslint/config";
6
16
  *
7
17
  * ```js
8
18
  * import { everyskConfig } from "@everysk/apps-devkit/eslint";
9
- * export default everyskConfig();
19
+ * export default everyskConfig({ tsconfigRootDir: import.meta.dirname });
10
20
  * ```
11
21
  *
12
22
  * It encodes the conventions that lint can actually check: no MUI barrel imports, and no
13
23
  * direct data access from pages or components. The rules that lint cannot express — which
14
24
  * subpath to import, which provider to wire — belong to the `everysk-apps` generator.
15
25
  *
16
- * @param {ReturnType<typeof defineConfig>} [extra] - Appended after the preset, so an app can
17
- * add or override rules for its own files.
26
+ * Takes the options, or the app's own blocks alone, as it did before there were options.
18
27
  */
19
- export declare const everyskConfig: (extra?: ReturnType<typeof defineConfig>) => import("eslint/config").Config[];
28
+ export declare const everyskConfig: (optionsOrExtra?: EveryskConfigOptions | ReturnType<typeof defineConfig>) => import("eslint/config").ConfigObject[];
20
29
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/eslint/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAiB,MAAM,eAAe,CAAC;AAmB5D;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,aAAa,GAAI,QAAO,UAAU,CAAC,OAAO,YAAY,CAAM,qCAgDnE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/eslint/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAiB,MAAM,eAAe,CAAC;AAmB5D,MAAM,MAAM,oBAAoB,GAAG;IAC/B;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,wFAAwF;IACxF,KAAK,CAAC,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;CAC3C,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,aAAa,GACtB,iBAAgB,oBAAoB,GAAG,UAAU,CAAC,OAAO,YAAY,CAAM,2CAuD9E,CAAC"}
@@ -24,62 +24,67 @@ const dataLayerMessage = "Data access only via entity hooks (@everysk/apps-sdk/<
24
24
  *
25
25
  * ```js
26
26
  * import { everyskConfig } from "@everysk/apps-devkit/eslint";
27
- * export default everyskConfig();
27
+ * export default everyskConfig({ tsconfigRootDir: import.meta.dirname });
28
28
  * ```
29
29
  *
30
30
  * It encodes the conventions that lint can actually check: no MUI barrel imports, and no
31
31
  * direct data access from pages or components. The rules that lint cannot express — which
32
32
  * subpath to import, which provider to wire — belong to the `everysk-apps` generator.
33
33
  *
34
- * @param {ReturnType<typeof defineConfig>} [extra] - Appended after the preset, so an app can
35
- * add or override rules for its own files.
34
+ * Takes the options, or the app's own blocks alone, as it did before there were options.
36
35
  */
37
- export const everyskConfig = (extra = []) => defineConfig([
38
- globalIgnores(["dist", "coverage"]),
39
- {
40
- files: ["**/*.{ts,tsx}"],
41
- extends: [
42
- js.configs.recommended,
43
- tseslint.configs.recommended,
44
- reactHooks.configs.flat["recommended-latest"],
45
- reactRefresh.configs.vite,
46
- ],
47
- languageOptions: {
48
- ecmaVersion: 2020,
49
- globals: globals.browser,
50
- },
51
- rules: {
52
- "@typescript-eslint/no-restricted-imports": ["error", { paths: noBarrelPaths }],
53
- },
54
- },
55
- {
56
- // A same-named rule REPLACES the base one for these files rather than merging, so
57
- // the barrel bans are repeated here.
58
- files: ["src/pages/**/*.{ts,tsx}", "src/components/**/*.{ts,tsx}"],
59
- rules: {
60
- "@typescript-eslint/no-restricted-imports": [
61
- "error",
62
- {
63
- paths: [
64
- ...noBarrelPaths,
65
- { name: "axios", message: dataLayerMessage },
66
- { name: "@/api", message: dataLayerMessage },
67
- ],
68
- patterns: [{ group: ["@/api/*"], message: dataLayerMessage }],
69
- },
36
+ export const everyskConfig = (optionsOrExtra = {}) => {
37
+ const { tsconfigRootDir, extra = [] } = Array.isArray(optionsOrExtra)
38
+ ? { extra: optionsOrExtra }
39
+ : optionsOrExtra;
40
+ return defineConfig([
41
+ globalIgnores(["dist", "coverage"]),
42
+ {
43
+ files: ["**/*.{ts,tsx}"],
44
+ extends: [
45
+ js.configs.recommended,
46
+ tseslint.configs.recommended,
47
+ reactHooks.configs.flat["recommended-latest"],
48
+ reactRefresh.configs.vite,
70
49
  ],
50
+ languageOptions: {
51
+ ecmaVersion: 2020,
52
+ globals: globals.browser,
53
+ ...(tsconfigRootDir ? { parserOptions: { tsconfigRootDir } } : {}),
54
+ },
55
+ rules: {
56
+ "@typescript-eslint/no-restricted-imports": ["error", { paths: noBarrelPaths }],
57
+ },
71
58
  },
72
- },
73
- {
74
- // Tests may reach the data layer directly, to mock an api module or build axios
75
- // stubs. Only the barrel bans remain, and the Fast Refresh rule is relaxed for
76
- // test utilities that export helpers.
77
- files: ["src/**/*.{test,spec}.{ts,tsx}", "src/test/**/*.{ts,tsx}"],
78
- rules: {
79
- "@typescript-eslint/no-restricted-imports": ["error", { paths: noBarrelPaths }],
80
- "react-refresh/only-export-components": "off",
59
+ {
60
+ // A same-named rule REPLACES the base one for these files rather than merging, so
61
+ // the barrel bans are repeated here.
62
+ files: ["src/pages/**/*.{ts,tsx}", "src/components/**/*.{ts,tsx}"],
63
+ rules: {
64
+ "@typescript-eslint/no-restricted-imports": [
65
+ "error",
66
+ {
67
+ paths: [
68
+ ...noBarrelPaths,
69
+ { name: "axios", message: dataLayerMessage },
70
+ { name: "@/api", message: dataLayerMessage },
71
+ ],
72
+ patterns: [{ group: ["@/api/*"], message: dataLayerMessage }],
73
+ },
74
+ ],
75
+ },
81
76
  },
82
- },
83
- ...extra,
84
- ]);
77
+ {
78
+ // Tests may reach the data layer directly, to mock an api module or build axios
79
+ // stubs. Only the barrel bans remain, and the Fast Refresh rule is relaxed for
80
+ // test utilities that export helpers.
81
+ files: ["src/**/*.{test,spec}.{ts,tsx}", "src/test/**/*.{ts,tsx}"],
82
+ rules: {
83
+ "@typescript-eslint/no-restricted-imports": ["error", { paths: noBarrelPaths }],
84
+ "react-refresh/only-export-components": "off",
85
+ },
86
+ },
87
+ ...extra,
88
+ ]);
89
+ };
85
90
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/eslint/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,UAAU,MAAM,2BAA2B,CAAC;AACnD,OAAO,YAAY,MAAM,6BAA6B,CAAC;AACvD,OAAO,QAAQ,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE5D,mFAAmF;AACnF,MAAM,aAAa,GAAG;IAClB;QACI,IAAI,EAAE,eAAe;QACrB,OAAO,EACH,qGAAqG;KAC5G;IACD;QACI,IAAI,EAAE,qBAAqB;QAC3B,OAAO,EAAE,gFAAgF;KAC5F;CACJ,CAAC;AAEF,8EAA8E;AAC9E,MAAM,gBAAgB,GAClB,oJAAoJ,CAAC;AAEzJ;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,QAAyC,EAAE,EAAE,EAAE,CACzE,YAAY,CAAC;IACT,aAAa,CAAC,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC;QACI,KAAK,EAAE,CAAC,eAAe,CAAC;QACxB,OAAO,EAAE;YACL,EAAE,CAAC,OAAO,CAAC,WAAW;YACtB,QAAQ,CAAC,OAAO,CAAC,WAAW;YAC5B,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,oBAAoB,CAAC;YAC7C,YAAY,CAAC,OAAO,CAAC,IAAI;SAC5B;QACD,eAAe,EAAE;YACb,WAAW,EAAE,IAAI;YACjB,OAAO,EAAE,OAAO,CAAC,OAAO;SAC3B;QACD,KAAK,EAAE;YACH,0CAA0C,EAAE,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;SAClF;KACJ;IACD;QACI,kFAAkF;QAClF,qCAAqC;QACrC,KAAK,EAAE,CAAC,yBAAyB,EAAE,8BAA8B,CAAC;QAClE,KAAK,EAAE;YACH,0CAA0C,EAAE;gBACxC,OAAO;gBACP;oBACI,KAAK,EAAE;wBACH,GAAG,aAAa;wBAChB,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE;wBAC5C,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE;qBAC/C;oBACD,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC;iBAChE;aACJ;SACJ;KACJ;IACD;QACI,gFAAgF;QAChF,+EAA+E;QAC/E,sCAAsC;QACtC,KAAK,EAAE,CAAC,+BAA+B,EAAE,wBAAwB,CAAC;QAClE,KAAK,EAAE;YACH,0CAA0C,EAAE,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;YAC/E,sCAAsC,EAAE,KAAK;SAChD;KACJ;IACD,GAAG,KAAK;CACX,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/eslint/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,UAAU,MAAM,2BAA2B,CAAC;AACnD,OAAO,YAAY,MAAM,6BAA6B,CAAC;AACvD,OAAO,QAAQ,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE5D,mFAAmF;AACnF,MAAM,aAAa,GAAG;IAClB;QACI,IAAI,EAAE,eAAe;QACrB,OAAO,EACH,qGAAqG;KAC5G;IACD;QACI,IAAI,EAAE,qBAAqB;QAC3B,OAAO,EAAE,gFAAgF;KAC5F;CACJ,CAAC;AAEF,8EAA8E;AAC9E,MAAM,gBAAgB,GAClB,oJAAoJ,CAAC;AAazJ;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CACzB,iBAAyE,EAAE,EAC7E,EAAE;IACA,MAAM,EAAE,eAAe,EAAE,KAAK,GAAG,EAAE,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC;QACjE,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE;QAC3B,CAAC,CAAC,cAAc,CAAC;IAErB,OAAO,YAAY,CAAC;QAChB,aAAa,CAAC,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QACnC;YACI,KAAK,EAAE,CAAC,eAAe,CAAC;YACxB,OAAO,EAAE;gBACL,EAAE,CAAC,OAAO,CAAC,WAAW;gBACtB,QAAQ,CAAC,OAAO,CAAC,WAAW;gBAC5B,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,oBAAoB,CAAC;gBAC7C,YAAY,CAAC,OAAO,CAAC,IAAI;aAC5B;YACD,eAAe,EAAE;gBACb,WAAW,EAAE,IAAI;gBACjB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,EAAE,eAAe,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACrE;YACD,KAAK,EAAE;gBACH,0CAA0C,EAAE,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;aAClF;SACJ;QACD;YACI,kFAAkF;YAClF,qCAAqC;YACrC,KAAK,EAAE,CAAC,yBAAyB,EAAE,8BAA8B,CAAC;YAClE,KAAK,EAAE;gBACH,0CAA0C,EAAE;oBACxC,OAAO;oBACP;wBACI,KAAK,EAAE;4BACH,GAAG,aAAa;4BAChB,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE;4BAC5C,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE;yBAC/C;wBACD,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC;qBAChE;iBACJ;aACJ;SACJ;QACD;YACI,gFAAgF;YAChF,+EAA+E;YAC/E,sCAAsC;YACtC,KAAK,EAAE,CAAC,+BAA+B,EAAE,wBAAwB,CAAC;YAClE,KAAK,EAAE;gBACH,0CAA0C,EAAE,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;gBAC/E,sCAAsC,EAAE,KAAK;aAChD;SACJ;QACD,GAAG,KAAK;KACX,CAAC,CAAC;AACP,CAAC,CAAC"}
@@ -0,0 +1,27 @@
1
+ /** The API the dev server talks to, and the token it talks to it with. */
2
+ export type ApiTokenSource = {
3
+ /** `EVERYSK_API_URL`, without a trailing slash, so a path can be appended as is. */
4
+ apiUrl: string;
5
+ /** The current access token; `null` until one arrives, and once it has expired. */
6
+ token: () => string | null;
7
+ /** Why there is no token: the last failed request, until one succeeds. */
8
+ lastError: () => Error | null;
9
+ };
10
+ /**
11
+ * createApiTokenSource
12
+ *
13
+ * An OAuth2 access token for `EVERYSK_API_URL`, from the `client_credentials` grant at
14
+ * `${EVERYSK_API_URL}/oauth2/token`, cached in memory and renewed in the background a minute
15
+ * before it expires. It is fetched at once, so a token is ready before the first request. A
16
+ * failed request is logged and tried again, waiting 5 seconds and doubling up to a minute, so a
17
+ * VPN or API that comes back later does not need a restart of the dev server; until then the
18
+ * `/api` proxy sends no token (the API answers 401) and the `/file/` route says why, from
19
+ * `lastError`. `defineAppConfig` creates one source and hands it to both, so the dev server asks
20
+ * for one token, not one per use.
21
+ *
22
+ * @returns {ApiTokenSource} The API URL, the current token, and the last failure.
23
+ * @throws {Error} If `EVERYSK_API_URL`, `EVERYSK_API_CLIENT_ID`, or `EVERYSK_API_CLIENT_SECRET`
24
+ * is missing.
25
+ */
26
+ export declare function createApiTokenSource(): ApiTokenSource;
27
+ //# sourceMappingURL=apiToken.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiToken.d.ts","sourceRoot":"","sources":["../../src/vite/apiToken.ts"],"names":[],"mappings":"AAOA,0EAA0E;AAC1E,MAAM,MAAM,cAAc,GAAG;IACzB,oFAAoF;IACpF,MAAM,EAAE,MAAM,CAAC;IACf,mFAAmF;IACnF,KAAK,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IAC3B,0EAA0E;IAC1E,SAAS,EAAE,MAAM,KAAK,GAAG,IAAI,CAAC;CACjC,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,IAAI,cAAc,CAyErD"}
@@ -0,0 +1,86 @@
1
+ /** How long before a token expires it is renewed, so a request in flight never meets an expired one. */
2
+ const REFRESH_SKEW_MS = 60_000;
3
+ /** The first wait after a failed token request; it doubles on each failure, up to the maximum. */
4
+ const FIRST_RETRY_MS = 5_000;
5
+ const MAX_RETRY_MS = 60_000;
6
+ /**
7
+ * createApiTokenSource
8
+ *
9
+ * An OAuth2 access token for `EVERYSK_API_URL`, from the `client_credentials` grant at
10
+ * `${EVERYSK_API_URL}/oauth2/token`, cached in memory and renewed in the background a minute
11
+ * before it expires. It is fetched at once, so a token is ready before the first request. A
12
+ * failed request is logged and tried again, waiting 5 seconds and doubling up to a minute, so a
13
+ * VPN or API that comes back later does not need a restart of the dev server; until then the
14
+ * `/api` proxy sends no token (the API answers 401) and the `/file/` route says why, from
15
+ * `lastError`. `defineAppConfig` creates one source and hands it to both, so the dev server asks
16
+ * for one token, not one per use.
17
+ *
18
+ * @returns {ApiTokenSource} The API URL, the current token, and the last failure.
19
+ * @throws {Error} If `EVERYSK_API_URL`, `EVERYSK_API_CLIENT_ID`, or `EVERYSK_API_CLIENT_SECRET`
20
+ * is missing.
21
+ */
22
+ export function createApiTokenSource() {
23
+ const apiUrl = process.env.EVERYSK_API_URL?.replace(/\/$/, "");
24
+ if (!apiUrl)
25
+ throw new Error("EVERYSK_API_URL must be set.");
26
+ const clientId = process.env.EVERYSK_API_CLIENT_ID;
27
+ const clientSecret = process.env.EVERYSK_API_CLIENT_SECRET;
28
+ if (!clientId || !clientSecret) {
29
+ throw new Error("EVERYSK_API_CLIENT_ID and EVERYSK_API_CLIENT_SECRET must be set (OAuth2 client credentials).");
30
+ }
31
+ const credentials = { clientId, clientSecret };
32
+ const tokenUrl = `${apiUrl}/oauth2/token`;
33
+ let accessToken = null;
34
+ let expiresAt = 0;
35
+ let lastError = null;
36
+ let retryMs = FIRST_RETRY_MS;
37
+ let timer = null;
38
+ const schedule = (delayMs) => {
39
+ if (timer)
40
+ clearTimeout(timer);
41
+ timer = setTimeout(() => void refreshToken(), delayMs);
42
+ timer.unref?.();
43
+ };
44
+ /** Asks for a token, then schedules the next request: a renewal on success, a retry on failure. */
45
+ async function requestToken() {
46
+ const res = await fetch(tokenUrl, {
47
+ method: "POST",
48
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
49
+ body: new URLSearchParams({
50
+ grant_type: "client_credentials",
51
+ client_id: credentials.clientId,
52
+ client_secret: credentials.clientSecret,
53
+ }),
54
+ });
55
+ if (!res.ok)
56
+ throw new Error(`OAuth2 token request failed: ${res.status} ${res.statusText}`);
57
+ const data = (await res.json());
58
+ if (!data.access_token)
59
+ throw new Error("OAuth2 token response had no access_token.");
60
+ const ttlMs = (data.expires_in ?? 3600) * 1000;
61
+ accessToken = data.access_token;
62
+ expiresAt = Date.now() + ttlMs;
63
+ return ttlMs;
64
+ }
65
+ async function refreshToken() {
66
+ try {
67
+ const ttlMs = await requestToken();
68
+ lastError = null;
69
+ retryMs = FIRST_RETRY_MS;
70
+ schedule(Math.max(ttlMs - REFRESH_SKEW_MS, 30_000));
71
+ }
72
+ catch (error) {
73
+ lastError = error instanceof Error ? error : new Error(String(error));
74
+ console.error(`[dev server] OAuth2 token request failed; trying again in ${retryMs / 1000}s:`, lastError);
75
+ schedule(retryMs);
76
+ retryMs = Math.min(retryMs * 2, MAX_RETRY_MS);
77
+ }
78
+ }
79
+ void refreshToken();
80
+ return {
81
+ apiUrl,
82
+ token: () => (accessToken && Date.now() < expiresAt ? accessToken : null),
83
+ lastError: () => lastError,
84
+ };
85
+ }
86
+ //# sourceMappingURL=apiToken.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiToken.js","sourceRoot":"","sources":["../../src/vite/apiToken.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,9 @@
1
1
  import { type UserConfig } from "vite";
2
+ export { createApiTokenSource, type ApiTokenSource } from "./apiToken.js";
2
3
  export { createDevServerProxy } from "./serverProxy.js";
3
4
  export { envVarsLocationPlugin } from "./plugins/envVarsLocation.js";
4
5
  export { serveDevAppConfigPlugin } from "./plugins/serveDevAppConfig.js";
6
+ export { serveDevFilesPlugin } from "./plugins/serveDevFiles.js";
5
7
  /**
6
8
  * defineAppConfig
7
9
  *
@@ -22,6 +24,8 @@ export { serveDevAppConfigPlugin } from "./plugins/serveDevAppConfig.js";
22
24
  * authenticates against `EVERYSK_API_URL` using OAuth2 client credentials.
23
25
  * - React with the compiler preset, Tailwind, and the two plugins that carry the app's
24
26
  * `config.json` `env_vars` into the page — a meta tag in build, `/app-config.json` in dev.
27
+ * - The platform's `/file/<hash>` route, on the dev server and `vite preview`, so a file's own
28
+ * `url` shows it there too. It shares the `/api` proxy's OAuth2 token.
25
29
  *
26
30
  * @param {UserConfig} [overrides] - Merged over the preset with Vite's own `mergeConfig`, so
27
31
  * arrays (plugins, aliases) are concatenated rather than replaced.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsC,KAAK,UAAU,EAAE,MAAM,MAAM,CAAC;AAU3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAEzE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,eAAe,GAAI,YAAW,UAAe,sCAyCpD,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsC,KAAK,UAAU,EAAE,MAAM,MAAM,CAAC;AAY3E,OAAO,EAAE,oBAAoB,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,eAAe,GAAI,YAAW,UAAe,sCA4CpD,CAAC"}
@@ -3,12 +3,16 @@ import { defineConfig, loadEnv, mergeConfig } from "vite";
3
3
  import react, { reactCompilerPreset } from "@vitejs/plugin-react";
4
4
  import babel from "@rolldown/plugin-babel";
5
5
  import tailwindcss from "@tailwindcss/vite";
6
+ import { createApiTokenSource } from "./apiToken.js";
6
7
  import { createDevServerProxy } from "./serverProxy.js";
7
8
  import { envVarsLocationPlugin } from "./plugins/envVarsLocation.js";
8
9
  import { serveDevAppConfigPlugin } from "./plugins/serveDevAppConfig.js";
10
+ import { serveDevFilesPlugin } from "./plugins/serveDevFiles.js";
11
+ export { createApiTokenSource } from "./apiToken.js";
9
12
  export { createDevServerProxy } from "./serverProxy.js";
10
13
  export { envVarsLocationPlugin } from "./plugins/envVarsLocation.js";
11
14
  export { serveDevAppConfigPlugin } from "./plugins/serveDevAppConfig.js";
15
+ export { serveDevFilesPlugin } from "./plugins/serveDevFiles.js";
12
16
  /**
13
17
  * defineAppConfig
14
18
  *
@@ -29,6 +33,8 @@ export { serveDevAppConfigPlugin } from "./plugins/serveDevAppConfig.js";
29
33
  * authenticates against `EVERYSK_API_URL` using OAuth2 client credentials.
30
34
  * - React with the compiler preset, Tailwind, and the two plugins that carry the app's
31
35
  * `config.json` `env_vars` into the page — a meta tag in build, `/app-config.json` in dev.
36
+ * - The platform's `/file/<hash>` route, on the dev server and `vite preview`, so a file's own
37
+ * `url` shows it there too. It shares the `/api` proxy's OAuth2 token.
32
38
  *
33
39
  * @param {UserConfig} [overrides] - Merged over the preset with Vite's own `mergeConfig`, so
34
40
  * arrays (plugins, aliases) are concatenated rather than replaced.
@@ -39,6 +45,7 @@ export const defineAppConfig = (overrides = {}) => defineConfig(({ mode, command
39
45
  const root = process.env.PROJECT_ROOT ?? process.cwd();
40
46
  const env = loadEnv(mode, root, "");
41
47
  Object.assign(process.env, env);
48
+ const tokens = command === "serve" ? createApiTokenSource() : undefined;
42
49
  const preset = {
43
50
  base: "./",
44
51
  envDir: root,
@@ -55,7 +62,7 @@ export const defineAppConfig = (overrides = {}) => defineConfig(({ mode, command
55
62
  host: true,
56
63
  port: Number(process.env.PORT) || 5173,
57
64
  strictPort: true,
58
- proxy: command === "serve" ? createDevServerProxy() : undefined,
65
+ proxy: tokens ? createDevServerProxy(tokens) : undefined,
59
66
  },
60
67
  plugins: [
61
68
  react(),
@@ -63,6 +70,7 @@ export const defineAppConfig = (overrides = {}) => defineConfig(({ mode, command
63
70
  tailwindcss(),
64
71
  envVarsLocationPlugin(),
65
72
  serveDevAppConfigPlugin(),
73
+ serveDevFilesPlugin(tokens),
66
74
  ],
67
75
  };
68
76
  return mergeConfig(preset, overrides);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE9C,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAmB,MAAM,MAAM,CAAC;AAE3E,OAAO,KAAK,EAAE,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,KAAK,MAAM,wBAAwB,CAAC;AAC3C,OAAO,WAAW,MAAM,mBAAmB,CAAC;AAE5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAEzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAEzE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,YAAwB,EAAE,EAAE,EAAE,CAC1D,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE;IAC/B,kFAAkF;IAClF,sDAAsD;IACtD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAEvD,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;IACpC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAEhC,MAAM,MAAM,GAAe;QACvB,IAAI,EAAE,IAAI;QACV,MAAM,EAAE,IAAI;QAEZ,OAAO,EAAE;YACL,KAAK,EAAE;gBACH,GAAG,EAAE,aAAa,CAAC,IAAI,GAAG,CAAC,OAAO,EAAE,UAAU,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;aACnE;SACJ;QAED,KAAK,EAAE;YACH,MAAM,EAAE,QAAQ;YAChB,WAAW,EAAE,IAAI;SACpB;QAED,MAAM,EAAE;YACJ,IAAI,EAAE,IAAI;YACV,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI;YACtC,UAAU,EAAE,IAAI;YAChB,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,oBAAoB,EAAE,CAAC,CAAC,CAAC,SAAS;SAClE;QAED,OAAO,EAAE;YACL,KAAK,EAAE;YACP,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,mBAAmB,EAAE,CAAC,EAAE,CAAC;YAC3C,WAAW,EAAE;YACb,qBAAqB,EAAE;YACvB,uBAAuB,EAAE;SAC5B;KACJ,CAAC;IAEF,OAAO,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAC1C,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE9C,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAmB,MAAM,MAAM,CAAC;AAE3E,OAAO,KAAK,EAAE,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,KAAK,MAAM,wBAAwB,CAAC;AAC3C,OAAO,WAAW,MAAM,mBAAmB,CAAC;AAE5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEjE,OAAO,EAAE,oBAAoB,EAAuB,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,YAAwB,EAAE,EAAE,EAAE,CAC1D,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE;IAC/B,kFAAkF;IAClF,sDAAsD;IACtD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAEvD,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;IACpC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAEhC,MAAM,MAAM,GAAG,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,oBAAoB,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAExE,MAAM,MAAM,GAAe;QACvB,IAAI,EAAE,IAAI;QACV,MAAM,EAAE,IAAI;QAEZ,OAAO,EAAE;YACL,KAAK,EAAE;gBACH,GAAG,EAAE,aAAa,CAAC,IAAI,GAAG,CAAC,OAAO,EAAE,UAAU,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;aACnE;SACJ;QAED,KAAK,EAAE;YACH,MAAM,EAAE,QAAQ;YAChB,WAAW,EAAE,IAAI;SACpB;QAED,MAAM,EAAE;YACJ,IAAI,EAAE,IAAI;YACV,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI;YACtC,UAAU,EAAE,IAAI;YAChB,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS;SAC3D;QAED,OAAO,EAAE;YACL,KAAK,EAAE;YACP,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,mBAAmB,EAAE,CAAC,EAAE,CAAC;YAC3C,WAAW,EAAE;YACb,qBAAqB,EAAE;YACvB,uBAAuB,EAAE;YACzB,mBAAmB,CAAC,MAAM,CAAC;SAC9B;KACJ,CAAC;IAEF,OAAO,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAC1C,CAAC,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"envVarsLocation.js","sourceRoot":"","sources":["../../../src/vite/plugins/envVarsLocation.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,qBAAqB;IACnC,OAAO;QACL,IAAI,EAAE,mBAAmB;QACzB,KAAK,EAAE,OAAO;QACd,kBAAkB;YAChB,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;QAC5E,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"envVarsLocation.js","sourceRoot":"","sources":["../../../src/vite/plugins/envVarsLocation.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,qBAAqB;IACjC,OAAO;QACH,IAAI,EAAE,mBAAmB;QACzB,KAAK,EAAE,OAAO;QACd,kBAAkB;YACd,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;QAC9E,CAAC;KACJ,CAAC;AACN,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"serveDevAppConfig.js","sourceRoot":"","sources":["../../../src/vite/plugins/serveDevAppConfig.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAIlC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,uBAAuB;IACrC,OAAO;QACL,IAAI,EAAE,sBAAsB;QAC5B,KAAK,EAAE,OAAO;QACd,eAAe,CAAC,MAAM;YACpB,sFAAsF;YACtF,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC;YAEpE,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;gBAC9C,IAAI,GAAG,CAAC,GAAG,KAAK,kBAAkB;oBAAE,OAAO,IAAI,EAAE,CAAC;gBAElD,IAAI,CAAC;oBACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;oBACvD,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,IAAI,EAAE,CAA4B,CAAC;oBAEhF,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,iCAAiC,CAAC,CAAC;oBACjE,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;oBACrB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;gBACnC,CAAC;gBAAC,MAAM,CAAC;oBACP,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;oBACrB,GAAG,CAAC,GAAG,EAAE,CAAC;gBACZ,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,0FAA0F;YAC1F,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAC/B,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE;gBACnC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,UAAU,EAAE,CAAC;oBACtC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;gBAC3C,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"serveDevAppConfig.js","sourceRoot":"","sources":["../../../src/vite/plugins/serveDevAppConfig.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAIlC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,uBAAuB;IACnC,OAAO;QACH,IAAI,EAAE,sBAAsB;QAC5B,KAAK,EAAE,OAAO;QACd,eAAe,CAAC,MAAM;YAClB,sFAAsF;YACtF,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC;YAEpE,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;gBAC5C,IAAI,GAAG,CAAC,GAAG,KAAK,kBAAkB;oBAAE,OAAO,IAAI,EAAE,CAAC;gBAElD,IAAI,CAAC;oBACD,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;oBACvD,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,IAAI,EAAE,CAA4B,CAAC;oBAEhF,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,iCAAiC,CAAC,CAAC;oBACjE,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;oBACrB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;gBACrC,CAAC;gBAAC,MAAM,CAAC;oBACL,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;oBACrB,GAAG,CAAC,GAAG,EAAE,CAAC;gBACd,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,0FAA0F;YAC1F,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAC/B,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE;gBACjC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,UAAU,EAAE,CAAC;oBACpC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;gBAC7C,CAAC;YACL,CAAC,CAAC,CAAC;QACP,CAAC;KACJ,CAAC;AACN,CAAC"}
@@ -0,0 +1,41 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import type { Plugin } from "vite";
3
+ import { type ApiTokenSource } from "../apiToken.js";
4
+ /** What `createDevFileRoute` needs: the API and its token, a `fetch` and a log to replace in tests. */
5
+ type DevFileRouteParams = ApiTokenSource & {
6
+ fetch?: typeof fetch;
7
+ log?: (message: string) => void;
8
+ };
9
+ type RouteRequest = Pick<IncomingMessage, "url" | "method">;
10
+ type RouteResponse = Pick<ServerResponse, "statusCode" | "setHeader" | "end">;
11
+ /**
12
+ * createDevFileRoute
13
+ *
14
+ * The dev server's stand-in for the platform's `/file/<hash>` route. On the platform a file's
15
+ * `url` is that route, served with the user's session and shown inline; the dev server has no
16
+ * session, so the file is found through the API with the dev proxy's OAuth2 token instead, by
17
+ * its stored `url`, and its base64 content is decoded here and sent with its own type. So an app
18
+ * shows a file through its `url` wherever it runs.
19
+ *
20
+ * Read with the OAuth2 client's permissions, not a user's, as everything through the dev proxy is.
21
+ * Every path under `/file/` is answered here, never handed on: Vite would answer it with the
22
+ * app's `index.html` and a 200. A file that cannot be served gets a status that says why, and a
23
+ * line in the terminal, since an `<img>` or `<iframe>` never shows the body. Nothing escapes the
24
+ * middleware: Connect ignores its promise, and a rejection would end the dev server.
25
+ *
26
+ * @param params - The API, its token and last failure, and a `fetch` and a log to replace in tests.
27
+ * @returns A Connect middleware that answers `/file/` and passes everything else on.
28
+ */
29
+ export declare const createDevFileRoute: ({ apiUrl, token, lastError, fetch: fetchApi, log, }: DevFileRouteParams) => (req: RouteRequest, res: RouteResponse, next: () => void) => Promise<void>;
30
+ /**
31
+ * serveDevFilesPlugin
32
+ *
33
+ * Mounts `createDevFileRoute` on the dev server and on `vite preview`, neither of which has the
34
+ * platform's `/file/` route, logging through Vite's own logger.
35
+ *
36
+ * @param {ApiTokenSource} [tokens] - The token to send; created when the server starts if
37
+ * omitted. `defineAppConfig` passes the one it shares with the `/api` proxy.
38
+ */
39
+ export declare function serveDevFilesPlugin(tokens?: ApiTokenSource): Plugin;
40
+ export {};
41
+ //# sourceMappingURL=serveDevFiles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serveDevFiles.d.ts","sourceRoot":"","sources":["../../../src/vite/plugins/serveDevFiles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAW3E,uGAAuG;AACvG,KAAK,kBAAkB,GAAG,cAAc,GAAG;IACvC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC,CAAC;AAIF,KAAK,YAAY,GAAG,IAAI,CAAC,eAAe,EAAE,KAAK,GAAG,QAAQ,CAAC,CAAC;AAC5D,KAAK,aAAa,GAAG,IAAI,CAAC,cAAc,EAAE,YAAY,GAAG,WAAW,GAAG,KAAK,CAAC,CAAC;AAgG9E;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,kBAAkB,GAC1B,qDAME,kBAAkB,MACd,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,MAAM,IAAI,kBA6C7D,CAAC;AAEN;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM,CAanE"}