nuxt-files-sdk 0.0.5 → 0.1.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/LICENSE +1 -1
- package/README.md +35 -9
- package/dist/config/load.js +67 -0
- package/dist/config/load.js.map +1 -0
- package/dist/config/merge.d.ts +5 -0
- package/dist/config/merge.js +22 -0
- package/dist/config/merge.js.map +1 -0
- package/dist/config/prune.js +55 -0
- package/dist/config/prune.js.map +1 -0
- package/dist/config.d.ts +101 -103
- package/dist/config.js +2 -2
- package/dist/config.js.map +1 -1
- package/dist/devtools/auth.js +49 -0
- package/dist/devtools/auth.js.map +1 -0
- package/dist/devtools/client/app.js +6 -4
- package/dist/devtools/client/browser-agent-rpc-BXhoSh1z-DRoP5jHQ.js +1 -0
- package/dist/devtools/client/index.html +4 -1
- package/dist/devtools/devframe.js +7 -1
- package/dist/devtools/devframe.js.map +1 -1
- package/dist/devtools/diagnostics.d.ts +1 -1
- package/dist/devtools/diagnostics.js +1 -1
- package/dist/devtools/diagnostics.js.map +1 -1
- package/dist/devtools/files-read.d.ts +1 -1
- package/dist/devtools/files-write.d.ts +1 -1
- package/dist/devtools/files.js +7 -4
- package/dist/devtools/files.js.map +1 -1
- package/dist/devtools/index.js +24 -6
- package/dist/devtools/index.js.map +1 -1
- package/dist/devtools/nuxt-v3-handler.js +1 -1
- package/dist/devtools/nuxt-v3-handler.js.map +1 -1
- package/dist/devtools/nuxt-v3.js +1 -1
- package/dist/devtools/nuxt-v3.js.map +1 -1
- package/dist/devtools/nuxt-v4.js.map +1 -1
- package/dist/devtools/snapshot.d.ts +8 -2
- package/dist/devtools/snapshot.js +13 -3
- package/dist/devtools/snapshot.js.map +1 -1
- package/dist/devtools/token.d.ts +14 -0
- package/dist/devtools/token.js +17 -0
- package/dist/devtools/token.js.map +1 -0
- package/dist/integration/nitro.d.ts +6 -0
- package/dist/integration/nitro.js +160 -63
- package/dist/integration/nitro.js.map +1 -1
- package/dist/module.d.ts +1 -4
- package/dist/module.js +31 -20
- package/dist/module.js.map +1 -1
- package/dist/nitro.d.ts +2 -4
- package/dist/nitro.js +2 -4
- package/dist/nitro.js.map +1 -1
- package/dist/package.js +1 -1
- package/dist/runtime/internal.d.ts +11 -0
- package/dist/runtime/internal.js +30 -0
- package/dist/runtime/internal.js.map +1 -0
- package/dist/runtime/normalize.js +29 -0
- package/dist/runtime/normalize.js.map +1 -0
- package/dist/runtime/provider-types.d.ts +53 -0
- package/dist/runtime/registry.d.ts +55 -8
- package/dist/runtime/registry.js +125 -89
- package/dist/runtime/registry.js.map +1 -1
- package/dist/runtime.d.ts +8 -9
- package/dist/runtime.js +8 -25
- package/dist/runtime.js.map +1 -1
- package/package.json +25 -14
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,20 +1,46 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img alt="nuxt-files-sdk" src="https://shieldcn.dev/header/transparent.svg?title=Nuxt+Files+SDK&subtitle=Files+SDK+integration+for+Nuxt+with+DevTools.&logo=nuxt&mode=dark&font=geist&border=false" />
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://npmx.dev/package/nuxt-files-sdk"><img alt="badge" src="https://shieldcn.dev/npm/nuxt-files-sdk.svg?size=xs&font=geist&split=true" /></a>
|
|
7
|
+
<a href="https://github.com/liria24/nuxt-files-sdk"><img alt="license" src="https://shieldcn.dev/github/liria24/nuxt-files-sdk/license.svg?size=xs&font=geist&split=true" /></a>
|
|
8
|
+
<a href="https://github.com/liria24/nuxt-files-sdk/actions"><img alt="CI" src="https://shieldcn.dev/github/liria24/nuxt-files-sdk/ci.svg?size=xs&font=geist&split=true" /></a>
|
|
9
|
+
<a href="https://github.com/liria24/nuxt-files-sdk/commits"><img alt="last commit" src="https://shieldcn.dev/github/liria24/nuxt-files-sdk/last-commit.svg?size=xs&font=geist&split=true" /></a>
|
|
10
|
+
</p>
|
|
4
11
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
12
|
+
<p align="center">
|
|
13
|
+
Unofficial <a href="https://files-sdk.dev">Files SDK</a> integration for Nuxt and Nitro.
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
<p align="center">
|
|
17
|
+
Docs: <a href="https://nuxt-files-sdk.liria.me">https://nuxt-files-sdk.liria.me</a>
|
|
18
|
+
</p>
|
|
8
19
|
|
|
9
20
|
```bash
|
|
10
|
-
|
|
21
|
+
npm i nuxt-files-sdk
|
|
11
22
|
```
|
|
12
23
|
|
|
13
24
|
```ts
|
|
14
25
|
// nuxt.config.ts
|
|
15
|
-
export default defineNuxtConfig({
|
|
26
|
+
export default defineNuxtConfig({
|
|
27
|
+
modules: ['nuxt-files-sdk'],
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
// files.config.ts
|
|
31
|
+
export default defineFilesConfig({
|
|
32
|
+
storage: {
|
|
33
|
+
adapter: 'fs',
|
|
34
|
+
config: { root: '.data/files-devtools' },
|
|
35
|
+
},
|
|
36
|
+
$test: { storage: { adapter: 'memory' } },
|
|
37
|
+
})
|
|
16
38
|
```
|
|
17
39
|
|
|
18
|
-
|
|
40
|
+
<p align="center">
|
|
41
|
+
<a href="https://github.com/liria24/nuxt-files-sdk/graphs/contributors"><img alt="contributors" src="https://shieldcn.dev/contributors/liria24/nuxt-files-sdk.svg?title=false&mode=dark&font=geist" /></a>
|
|
42
|
+
</p>
|
|
19
43
|
|
|
20
|
-
|
|
44
|
+
<p align="center">
|
|
45
|
+
<img alt="npm downloads chart" src="https://shieldcn.dev/chart/npm/nuxt-files-sdk.svg?font=geist&logo=false&height=300" />
|
|
46
|
+
</p>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { normalizeFilesConfig } from "../runtime/normalize.js";
|
|
2
|
+
import { mergeFilesConfig } from "./merge.js";
|
|
3
|
+
import { basename, dirname } from "node:path";
|
|
4
|
+
import { readFile } from "node:fs/promises";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { loadConfig } from "c12";
|
|
7
|
+
import { createJiti } from "jiti";
|
|
8
|
+
//#region src/config/load.ts
|
|
9
|
+
const isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
10
|
+
const isResolvedFilesConfig = (value) => {
|
|
11
|
+
normalizeFilesConfig(value);
|
|
12
|
+
return true;
|
|
13
|
+
};
|
|
14
|
+
const hasEnvironmentStorage = (config) => [
|
|
15
|
+
config.$development,
|
|
16
|
+
config.$production,
|
|
17
|
+
config.$test,
|
|
18
|
+
config.$prerender,
|
|
19
|
+
...isObject(config.$env) ? Object.values(config.$env) : []
|
|
20
|
+
].some((branch) => isObject(branch) && branch.storage !== void 0);
|
|
21
|
+
/** c12 selects the first environment; a later prerender override is applied with the same merger. */
|
|
22
|
+
const loadFilesConfig = async ({ configPath, environments, alias, injectImports }) => {
|
|
23
|
+
const jiti = createJiti(import.meta.url, {
|
|
24
|
+
alias: {
|
|
25
|
+
...alias,
|
|
26
|
+
"nuxt-files-sdk/config": fileURLToPath(new URL("../config.js", import.meta.url))
|
|
27
|
+
},
|
|
28
|
+
interopDefault: true,
|
|
29
|
+
moduleCache: false
|
|
30
|
+
});
|
|
31
|
+
const importer = Object.assign(jiti, { import: async (id) => {
|
|
32
|
+
const source = await readFile(id, "utf8");
|
|
33
|
+
const code = injectImports ? (await injectImports(source, id)).code : source;
|
|
34
|
+
const loaded = await jiti.evalModule(code, {
|
|
35
|
+
filename: id,
|
|
36
|
+
async: true
|
|
37
|
+
});
|
|
38
|
+
return loaded && typeof loaded === "object" && "default" in loaded ? loaded.default : loaded;
|
|
39
|
+
} });
|
|
40
|
+
const { config } = await loadConfig({
|
|
41
|
+
cwd: dirname(configPath),
|
|
42
|
+
configFile: basename(configPath),
|
|
43
|
+
configFileRequired: true,
|
|
44
|
+
rcFile: false,
|
|
45
|
+
extend: false,
|
|
46
|
+
envName: environments[0] ?? false,
|
|
47
|
+
jiti: importer,
|
|
48
|
+
merger: mergeFilesConfig
|
|
49
|
+
});
|
|
50
|
+
let resolved = config;
|
|
51
|
+
for (const environment of environments.slice(1)) {
|
|
52
|
+
const direct = resolved[`$${environment}`];
|
|
53
|
+
const named = isObject(resolved.$env) ? resolved.$env[environment] : void 0;
|
|
54
|
+
resolved = mergeFilesConfig({
|
|
55
|
+
...isObject(direct) ? direct : {},
|
|
56
|
+
...isObject(named) ? named : {}
|
|
57
|
+
}, resolved);
|
|
58
|
+
}
|
|
59
|
+
const result = Object.fromEntries(Object.entries(resolved).filter(([key]) => !key.startsWith("$")));
|
|
60
|
+
if (isObject(result) && result.storage === void 0 && hasEnvironmentStorage(resolved)) return void 0;
|
|
61
|
+
if (!isResolvedFilesConfig(result)) throw new Error("[nuxt-files-sdk:invalid-config] Invalid resolved configuration.");
|
|
62
|
+
return result;
|
|
63
|
+
};
|
|
64
|
+
//#endregion
|
|
65
|
+
export { loadFilesConfig };
|
|
66
|
+
|
|
67
|
+
//# sourceMappingURL=load.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load.js","names":[],"sources":["../../src/config/load.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises'\nimport { basename, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nimport { loadConfig } from 'c12'\nimport { createJiti } from 'jiti'\n\nimport type { FilesConfig } from '../config'\nimport { normalizeFilesConfig } from '../runtime/normalize'\nimport { mergeFilesConfig } from './merge'\n\nexport interface FilesConfigLoaderOptions {\n configPath: string\n environments: readonly string[]\n alias?: Record<string, string> | undefined\n injectImports?: ((code: string, id?: string) => Promise<{ code: string }>) | undefined\n}\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n value !== null && typeof value === 'object' && !Array.isArray(value)\nconst isResolvedFilesConfig = (value: unknown): value is FilesConfig => {\n normalizeFilesConfig(value)\n return true\n}\n\nconst hasEnvironmentStorage = (config: Record<string, unknown>): boolean =>\n [\n config.$development,\n config.$production,\n config.$test,\n config.$prerender,\n ...(isObject(config.$env) ? Object.values(config.$env) : []),\n ].some((branch) => isObject(branch) && branch.storage !== undefined)\n\n/** c12 selects the first environment; a later prerender override is applied with the same merger. */\nexport const loadFilesConfig = async ({\n configPath,\n environments,\n alias,\n injectImports,\n}: FilesConfigLoaderOptions): Promise<FilesConfig | undefined> => {\n const jiti = createJiti(import.meta.url, {\n alias: {\n ...alias,\n 'nuxt-files-sdk/config': fileURLToPath(new URL('../config.js', import.meta.url)),\n },\n interopDefault: true,\n moduleCache: false,\n })\n const importer = Object.assign(jiti, {\n import: async (id: string) => {\n const source = await readFile(id, 'utf8')\n const code = injectImports ? (await injectImports(source, id)).code : source\n const loaded: unknown = await jiti.evalModule(code, { filename: id, async: true })\n return loaded && typeof loaded === 'object' && 'default' in loaded ? loaded.default : loaded\n },\n })\n const { config } = await loadConfig<Record<string, unknown>>({\n cwd: dirname(configPath),\n configFile: basename(configPath),\n configFileRequired: true,\n rcFile: false,\n extend: false,\n envName: environments[0] ?? false,\n jiti: importer,\n merger: mergeFilesConfig,\n })\n let resolved: Record<string, unknown> = config\n for (const environment of environments.slice(1)) {\n const direct = resolved[`$${environment}`]\n const named = isObject(resolved.$env) ? resolved.$env[environment] : undefined\n resolved = mergeFilesConfig(\n { ...(isObject(direct) ? direct : {}), ...(isObject(named) ? named : {}) },\n resolved,\n )\n }\n const result: unknown = Object.fromEntries(Object.entries(resolved).filter(([key]) => !key.startsWith('$')))\n if (isObject(result) && result.storage === undefined && hasEnvironmentStorage(resolved)) return undefined\n if (!isResolvedFilesConfig(result))\n throw new Error('[nuxt-files-sdk:invalid-config] Invalid resolved configuration.')\n return result\n}\n"],"mappings":";;;;;;;;AAkBA,MAAM,YAAY,UACd,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AACvE,MAAM,yBAAyB,UAAyC;CACpE,qBAAqB,KAAK;CAC1B,OAAO;AACX;AAEA,MAAM,yBAAyB,WAC3B;CACI,OAAO;CACP,OAAO;CACP,OAAO;CACP,OAAO;CACP,GAAI,SAAS,OAAO,IAAI,IAAI,OAAO,OAAO,OAAO,IAAI,IAAI,CAAC;AAC9D,CAAC,CAAC,MAAM,WAAW,SAAS,MAAM,KAAK,OAAO,YAAY,KAAA,CAAS;;AAGvE,MAAa,kBAAkB,OAAO,EAClC,YACA,cACA,OACA,oBAC8D;CAC9D,MAAM,OAAO,WAAW,YAAY,KAAK;EACrC,OAAO;GACH,GAAG;GACH,yBAAyB,cAAc,IAAI,IAAI,gBAAgB,YAAY,GAAG,CAAC;EACnF;EACA,gBAAgB;EAChB,aAAa;CACjB,CAAC;CACD,MAAM,WAAW,OAAO,OAAO,MAAM,EACjC,QAAQ,OAAO,OAAe;EAC1B,MAAM,SAAS,MAAM,SAAS,IAAI,MAAM;EACxC,MAAM,OAAO,iBAAiB,MAAM,cAAc,QAAQ,EAAE,EAAA,CAAG,OAAO;EACtE,MAAM,SAAkB,MAAM,KAAK,WAAW,MAAM;GAAE,UAAU;GAAI,OAAO;EAAK,CAAC;EACjF,OAAO,UAAU,OAAO,WAAW,YAAY,aAAa,SAAS,OAAO,UAAU;CAC1F,EACJ,CAAC;CACD,MAAM,EAAE,WAAW,MAAM,WAAoC;EACzD,KAAK,QAAQ,UAAU;EACvB,YAAY,SAAS,UAAU;EAC/B,oBAAoB;EACpB,QAAQ;EACR,QAAQ;EACR,SAAS,aAAa,MAAM;EAC5B,MAAM;EACN,QAAQ;CACZ,CAAC;CACD,IAAI,WAAoC;CACxC,KAAK,MAAM,eAAe,aAAa,MAAM,CAAC,GAAG;EAC7C,MAAM,SAAS,SAAS,IAAI;EAC5B,MAAM,QAAQ,SAAS,SAAS,IAAI,IAAI,SAAS,KAAK,eAAe,KAAA;EACrE,WAAW,iBACP;GAAE,GAAI,SAAS,MAAM,IAAI,SAAS,CAAC;GAAI,GAAI,SAAS,KAAK,IAAI,QAAQ,CAAC;EAAG,GACzE,QACJ;CACJ;CACA,MAAM,SAAkB,OAAO,YAAY,OAAO,QAAQ,QAAQ,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,WAAW,GAAG,CAAC,CAAC;CAC3G,IAAI,SAAS,MAAM,KAAK,OAAO,YAAY,KAAA,KAAa,sBAAsB,QAAQ,GAAG,OAAO,KAAA;CAChG,IAAI,CAAC,sBAAsB,MAAM,GAC7B,MAAM,IAAI,MAAM,iEAAiE;CACrF,OAAO;AACX"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
//#region src/config/merge.d.ts
|
|
2
|
+
/** c12 merger with Files provider boundaries and replacement arrays. Sources are highest priority first. */
|
|
3
|
+
export declare const mergeFilesConfig: (...sources: Array<Record<string, unknown> | null | undefined>) => Record<string, unknown>;
|
|
4
|
+
//#endregion
|
|
5
|
+
//# sourceMappingURL=merge.d.ts.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/config/merge.ts
|
|
2
|
+
const isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3
|
+
const merge = (override, base, path) => {
|
|
4
|
+
if (override === void 0) return base;
|
|
5
|
+
if (!isObject(override) || !isObject(base)) return override;
|
|
6
|
+
const providerChanged = path[0] === "storage" && path.length <= 2 && "adapter" in override && override.adapter !== base.adapter;
|
|
7
|
+
const result = { ...base };
|
|
8
|
+
if (providerChanged) delete result.config;
|
|
9
|
+
for (const [key, value] of Object.entries(override)) result[key] = merge(value, result[key], [...path, key]);
|
|
10
|
+
return result;
|
|
11
|
+
};
|
|
12
|
+
/** c12 merger with Files provider boundaries and replacement arrays. Sources are highest priority first. */
|
|
13
|
+
const mergeFilesConfig = (...sources) => {
|
|
14
|
+
return sources.reduceRight((base, source) => {
|
|
15
|
+
const value = merge(source, base, []);
|
|
16
|
+
return isObject(value) ? value : base;
|
|
17
|
+
}, {});
|
|
18
|
+
};
|
|
19
|
+
//#endregion
|
|
20
|
+
export { mergeFilesConfig };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.js","names":[],"sources":["../../src/config/merge.ts"],"sourcesContent":["const isObject = (value: unknown): value is Record<string, unknown> =>\n value !== null && typeof value === 'object' && !Array.isArray(value)\n\nconst merge = (override: unknown, base: unknown, path: string[]): unknown => {\n if (override === undefined) return base\n if (!isObject(override) || !isObject(base)) return override\n const providerChanged =\n path[0] === 'storage' && path.length <= 2 && 'adapter' in override && override.adapter !== base.adapter\n const result: Record<string, unknown> = { ...base }\n if (providerChanged) delete result.config\n for (const [key, value] of Object.entries(override)) {\n result[key] = merge(value, result[key], [...path, key])\n }\n return result\n}\n\n/** c12 merger with Files provider boundaries and replacement arrays. Sources are highest priority first. */\nexport const mergeFilesConfig = (\n ...sources: Array<Record<string, unknown> | null | undefined>\n): Record<string, unknown> => {\n return sources.reduceRight<Record<string, unknown>>((base, source) => {\n const value = merge(source, base, [])\n return isObject(value) ? value : base\n }, {})\n}\n"],"mappings":";AAAA,MAAM,YAAY,UACd,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAEvE,MAAM,SAAS,UAAmB,MAAe,SAA4B;CACzE,IAAI,aAAa,KAAA,GAAW,OAAO;CACnC,IAAI,CAAC,SAAS,QAAQ,KAAK,CAAC,SAAS,IAAI,GAAG,OAAO;CACnD,MAAM,kBACF,KAAK,OAAO,aAAa,KAAK,UAAU,KAAK,aAAa,YAAY,SAAS,YAAY,KAAK;CACpG,MAAM,SAAkC,EAAE,GAAG,KAAK;CAClD,IAAI,iBAAiB,OAAO,OAAO;CACnC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,GAC9C,OAAO,OAAO,MAAM,OAAO,OAAO,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC;CAE1D,OAAO;AACX;;AAGA,MAAa,oBACT,GAAG,YACuB;CAC1B,OAAO,QAAQ,aAAsC,MAAM,WAAW;EAClE,MAAM,QAAQ,MAAM,QAAQ,MAAM,CAAC,CAAC;EACpC,OAAO,SAAS,KAAK,IAAI,QAAQ;CACrC,GAAG,CAAC,CAAC;AACT"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { dirname, resolve } from "node:path";
|
|
2
|
+
import { parse } from "@babel/parser";
|
|
3
|
+
//#region src/config/prune.ts
|
|
4
|
+
const unwrap = (node) => node.type === "TSAsExpression" || node.type === "TSSatisfiesExpression" || node.type === "ParenthesizedExpression" ? unwrap(node.expression) : node;
|
|
5
|
+
const keyOf = (node) => node.key?.type === "Identifier" ? node.key.name : typeof node.key?.value === "string" ? node.key.value : void 0;
|
|
6
|
+
/** Remove inactive environment literals before bundling the user config. */
|
|
7
|
+
const pruneFilesConfigSource = (source, configPath, environments) => {
|
|
8
|
+
const body = parse(source, {
|
|
9
|
+
sourceType: "module",
|
|
10
|
+
plugins: ["typescript"]
|
|
11
|
+
}).program.body;
|
|
12
|
+
const exported = body.find((node) => node.type === "ExportDefaultDeclaration")?.declaration;
|
|
13
|
+
if (!exported) throw new Error("[nuxt-files-sdk:invalid-config] files.config.ts needs a default export.");
|
|
14
|
+
let root = unwrap(exported);
|
|
15
|
+
if (root.type === "CallExpression") root = unwrap(root.arguments?.[0] ?? root);
|
|
16
|
+
if (root.type === "Identifier") {
|
|
17
|
+
const declaration = body.flatMap((node) => node.declarations ?? []).find((node) => node.id?.name === root.name);
|
|
18
|
+
if (declaration?.init) root = unwrap(declaration.init);
|
|
19
|
+
}
|
|
20
|
+
if (root.type !== "ObjectExpression") throw new Error("[nuxt-files-sdk:invalid-config] The default Files config must be a static object literal.");
|
|
21
|
+
const edits = [];
|
|
22
|
+
const active = new Set(environments);
|
|
23
|
+
for (const property of root.properties ?? []) {
|
|
24
|
+
const key = keyOf(property);
|
|
25
|
+
if (!key?.startsWith("$")) continue;
|
|
26
|
+
if (key === "$env" && property.value) {
|
|
27
|
+
const env = unwrap(property.value);
|
|
28
|
+
if (env.type !== "ObjectExpression") throw new Error("[nuxt-files-sdk:invalid-config] $env must be an object.");
|
|
29
|
+
for (const entry of env.properties ?? []) if (!active.has(keyOf(entry) ?? "")) edits.push({
|
|
30
|
+
start: entry.start,
|
|
31
|
+
end: entry.end,
|
|
32
|
+
text: "...{}"
|
|
33
|
+
});
|
|
34
|
+
} else if (!active.has(key.slice(1))) edits.push({
|
|
35
|
+
start: property.start,
|
|
36
|
+
end: property.end,
|
|
37
|
+
text: "...{}"
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
for (const node of body) {
|
|
41
|
+
if (!node.source || typeof node.source.value !== "string" || !node.source.value.startsWith(".")) continue;
|
|
42
|
+
edits.push({
|
|
43
|
+
start: node.source.start,
|
|
44
|
+
end: node.source.end,
|
|
45
|
+
text: JSON.stringify(resolve(dirname(configPath), node.source.value).replaceAll("\\", "/"))
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
let result = source;
|
|
49
|
+
for (const edit of edits.toSorted((left, right) => right.start - left.start)) result = result.slice(0, edit.start) + edit.text + result.slice(edit.end);
|
|
50
|
+
return result;
|
|
51
|
+
};
|
|
52
|
+
//#endregion
|
|
53
|
+
export { pruneFilesConfigSource };
|
|
54
|
+
|
|
55
|
+
//# sourceMappingURL=prune.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prune.js","names":[],"sources":["../../src/config/prune.ts"],"sourcesContent":["import { dirname, resolve } from 'node:path'\n\nimport { parse } from '@babel/parser'\n\ninterface SyntaxNode {\n type: string\n start: number\n end: number\n name?: string\n value?: unknown\n key?: SyntaxNode\n source?: SyntaxNode\n declaration?: SyntaxNode\n declarations?: SyntaxNode[]\n id?: SyntaxNode\n init?: SyntaxNode\n expression?: SyntaxNode\n callee?: SyntaxNode\n arguments?: SyntaxNode[]\n properties?: SyntaxNode[]\n body?: SyntaxNode[]\n}\n\nconst unwrap = (node: SyntaxNode): SyntaxNode =>\n node.type === 'TSAsExpression' || node.type === 'TSSatisfiesExpression' || node.type === 'ParenthesizedExpression'\n ? unwrap(node.expression!)\n : node\nconst keyOf = (node: SyntaxNode): string | undefined =>\n node.key?.type === 'Identifier' ? node.key.name : typeof node.key?.value === 'string' ? node.key.value : undefined\n\n/** Remove inactive environment literals before bundling the user config. */\nexport const pruneFilesConfigSource = (source: string, configPath: string, environments: readonly string[]): string => {\n // Babel's node union is wider than the fields inspected here.\n // oxlint-disable typescript/no-unsafe-type-assertion\n const body = (\n parse(source, { sourceType: 'module', plugins: ['typescript'] }) as unknown as { program: SyntaxNode }\n ).program.body!\n // oxlint-enable typescript/no-unsafe-type-assertion\n const exported = body.find((node) => node.type === 'ExportDefaultDeclaration')?.declaration\n if (!exported) throw new Error('[nuxt-files-sdk:invalid-config] files.config.ts needs a default export.')\n let root = unwrap(exported)\n if (root.type === 'CallExpression') root = unwrap(root.arguments?.[0] ?? root)\n if (root.type === 'Identifier') {\n const declaration = body.flatMap((node) => node.declarations ?? []).find((node) => node.id?.name === root.name)\n if (declaration?.init) root = unwrap(declaration.init)\n }\n if (root.type !== 'ObjectExpression') {\n throw new Error('[nuxt-files-sdk:invalid-config] The default Files config must be a static object literal.')\n }\n const edits: Array<{ start: number; end: number; text: string }> = []\n const active = new Set(environments)\n for (const property of root.properties ?? []) {\n const key = keyOf(property)\n if (!key?.startsWith('$')) continue\n if (key === '$env' && property.value) {\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion\n const env = unwrap(property.value as SyntaxNode)\n if (env.type !== 'ObjectExpression')\n throw new Error('[nuxt-files-sdk:invalid-config] $env must be an object.')\n for (const entry of env.properties ?? []) {\n if (!active.has(keyOf(entry) ?? '')) edits.push({ start: entry.start, end: entry.end, text: '...{}' })\n }\n } else if (!active.has(key.slice(1))) {\n edits.push({ start: property.start, end: property.end, text: '...{}' })\n }\n }\n // The selected module lives under the build directory; preserve relative import resolution.\n for (const node of body) {\n if (!node.source || typeof node.source.value !== 'string' || !node.source.value.startsWith('.')) continue\n edits.push({\n start: node.source.start,\n end: node.source.end,\n text: JSON.stringify(resolve(dirname(configPath), node.source.value).replaceAll('\\\\', '/')),\n })\n }\n let result = source\n for (const edit of edits.toSorted((left, right) => right.start - left.start)) {\n result = result.slice(0, edit.start) + edit.text + result.slice(edit.end)\n }\n return result\n}\n"],"mappings":";;;AAuBA,MAAM,UAAU,SACZ,KAAK,SAAS,oBAAoB,KAAK,SAAS,2BAA2B,KAAK,SAAS,4BACnF,OAAO,KAAK,UAAW,IACvB;AACV,MAAM,SAAS,SACX,KAAK,KAAK,SAAS,eAAe,KAAK,IAAI,OAAO,OAAO,KAAK,KAAK,UAAU,WAAW,KAAK,IAAI,QAAQ,KAAA;;AAG7G,MAAa,0BAA0B,QAAgB,YAAoB,iBAA4C;CAGnH,MAAM,OACF,MAAM,QAAQ;EAAE,YAAY;EAAU,SAAS,CAAC,YAAY;CAAE,CAAC,CAAC,CAClE,QAAQ;CAEV,MAAM,WAAW,KAAK,MAAM,SAAS,KAAK,SAAS,0BAA0B,CAAC,EAAE;CAChF,IAAI,CAAC,UAAU,MAAM,IAAI,MAAM,yEAAyE;CACxG,IAAI,OAAO,OAAO,QAAQ;CAC1B,IAAI,KAAK,SAAS,kBAAkB,OAAO,OAAO,KAAK,YAAY,MAAM,IAAI;CAC7E,IAAI,KAAK,SAAS,cAAc;EAC5B,MAAM,cAAc,KAAK,SAAS,SAAS,KAAK,gBAAgB,CAAC,CAAC,CAAC,CAAC,MAAM,SAAS,KAAK,IAAI,SAAS,KAAK,IAAI;EAC9G,IAAI,aAAa,MAAM,OAAO,OAAO,YAAY,IAAI;CACzD;CACA,IAAI,KAAK,SAAS,oBACd,MAAM,IAAI,MAAM,2FAA2F;CAE/G,MAAM,QAA6D,CAAC;CACpE,MAAM,SAAS,IAAI,IAAI,YAAY;CACnC,KAAK,MAAM,YAAY,KAAK,cAAc,CAAC,GAAG;EAC1C,MAAM,MAAM,MAAM,QAAQ;EAC1B,IAAI,CAAC,KAAK,WAAW,GAAG,GAAG;EAC3B,IAAI,QAAQ,UAAU,SAAS,OAAO;GAElC,MAAM,MAAM,OAAO,SAAS,KAAmB;GAC/C,IAAI,IAAI,SAAS,oBACb,MAAM,IAAI,MAAM,yDAAyD;GAC7E,KAAK,MAAM,SAAS,IAAI,cAAc,CAAC,GACnC,IAAI,CAAC,OAAO,IAAI,MAAM,KAAK,KAAK,EAAE,GAAG,MAAM,KAAK;IAAE,OAAO,MAAM;IAAO,KAAK,MAAM;IAAK,MAAM;GAAQ,CAAC;EAE7G,OAAO,IAAI,CAAC,OAAO,IAAI,IAAI,MAAM,CAAC,CAAC,GAC/B,MAAM,KAAK;GAAE,OAAO,SAAS;GAAO,KAAK,SAAS;GAAK,MAAM;EAAQ,CAAC;CAE9E;CAEA,KAAK,MAAM,QAAQ,MAAM;EACrB,IAAI,CAAC,KAAK,UAAU,OAAO,KAAK,OAAO,UAAU,YAAY,CAAC,KAAK,OAAO,MAAM,WAAW,GAAG,GAAG;EACjG,MAAM,KAAK;GACP,OAAO,KAAK,OAAO;GACnB,KAAK,KAAK,OAAO;GACjB,MAAM,KAAK,UAAU,QAAQ,QAAQ,UAAU,GAAG,KAAK,OAAO,KAAK,CAAC,CAAC,WAAW,MAAM,GAAG,CAAC;EAC9F,CAAC;CACL;CACA,IAAI,SAAS;CACb,KAAK,MAAM,QAAQ,MAAM,UAAU,MAAM,UAAU,MAAM,QAAQ,KAAK,KAAK,GACvE,SAAS,OAAO,MAAM,GAAG,KAAK,KAAK,IAAI,KAAK,OAAO,OAAO,MAAM,KAAK,GAAG;CAE5E,OAAO;AACX"}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,115 +1,113 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ProviderFactories } from "./runtime/provider-types.js";
|
|
2
|
+
import { Adapter, FilesHooks, FilesOptions, FilesPlugin, ProviderSlug } from "files-sdk";
|
|
3
|
+
import { AuthorizeContext, AuthorizeResult, CreateFilesRouterOptions } from "files-sdk/api";
|
|
4
|
+
import { H3Event } from "h3";
|
|
2
5
|
//#region src/config.d.ts
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
'digitalocean-spaces': typeof import('files-sdk/digitalocean-spaces').digitaloceanSpaces;
|
|
16
|
-
dropbox: typeof import('files-sdk/dropbox').dropbox;
|
|
17
|
-
exoscale: typeof import('files-sdk/exoscale').exoscale;
|
|
18
|
-
filebase: typeof import('files-sdk/filebase').filebase;
|
|
19
|
-
'firebase-storage': typeof import('files-sdk/firebase-storage').firebaseStorage;
|
|
20
|
-
fs: typeof import('files-sdk/fs').fs;
|
|
21
|
-
ftp: typeof import('files-sdk/ftp').ftp;
|
|
22
|
-
gcs: typeof import('files-sdk/gcs').gcs;
|
|
23
|
-
'google-drive': typeof import('files-sdk/google-drive').googleDrive;
|
|
24
|
-
hetzner: typeof import('files-sdk/hetzner').hetzner;
|
|
25
|
-
'ibm-cos': typeof import('files-sdk/ibm-cos').ibmCos;
|
|
26
|
-
'idrive-e2': typeof import('files-sdk/idrive-e2').idriveE2;
|
|
27
|
-
memory: typeof import('files-sdk/memory').memory;
|
|
28
|
-
minio: typeof import('files-sdk/minio').minio;
|
|
29
|
-
neon: typeof import('files-sdk/neon').neon;
|
|
30
|
-
'netlify-blobs': typeof import('files-sdk/netlify-blobs').netlifyBlobs;
|
|
31
|
-
onedrive: typeof import('files-sdk/onedrive').onedrive;
|
|
32
|
-
'oracle-cloud': typeof import('files-sdk/oracle-cloud').oracleCloud;
|
|
33
|
-
ovhcloud: typeof import('files-sdk/ovhcloud').ovhcloud;
|
|
34
|
-
pocketbase: typeof import('files-sdk/pocketbase').pocketbase;
|
|
35
|
-
r2: typeof import('files-sdk/r2').r2;
|
|
36
|
-
s3: typeof import('files-sdk/s3').s3;
|
|
37
|
-
's3-fetch': typeof import('files-sdk/s3-fetch').s3Fetch;
|
|
38
|
-
scaleway: typeof import('files-sdk/scaleway').scaleway;
|
|
39
|
-
sftp: typeof import('files-sdk/sftp').sftp;
|
|
40
|
-
sharepoint: typeof import('files-sdk/sharepoint').sharepoint;
|
|
41
|
-
storj: typeof import('files-sdk/storj').storj;
|
|
42
|
-
supabase: typeof import('files-sdk/supabase').supabase;
|
|
43
|
-
tencent: typeof import('files-sdk/tencent').tencent;
|
|
44
|
-
tigris: typeof import('files-sdk/tigris').tigris;
|
|
45
|
-
uploadthing: typeof import('files-sdk/uploadthing').uploadthing;
|
|
46
|
-
'vercel-blob': typeof import('files-sdk/vercel-blob').vercelBlob;
|
|
47
|
-
vultr: typeof import('files-sdk/vultr').vultr;
|
|
48
|
-
wasabi: typeof import('files-sdk/wasabi').wasabi;
|
|
49
|
-
webdav: typeof import('files-sdk/webdav').webdav;
|
|
50
|
-
yandex: typeof import('files-sdk/yandex').yandex;
|
|
51
|
-
}
|
|
52
|
-
type ProviderOptions<Provider extends ProviderSlug> = Parameters<ProviderFactories[Provider]>[0];
|
|
53
|
-
type ConfigValue<Provider extends ProviderSlug> = Exclude<ProviderOptions<Provider>, undefined>;
|
|
54
|
-
type ConfigInput<Provider extends ProviderSlug> = ConfigValue<Provider> | (() => ConfigValue<Provider>);
|
|
55
|
-
type ProviderConfig<Provider extends ProviderSlug> = {
|
|
56
|
-
/** Native provider factory options, or a synchronous runtime resolver for them. */
|
|
57
|
-
config?: ConfigInput<Provider>;
|
|
58
|
-
} & (undefined extends ProviderOptions<Provider> ? unknown : {
|
|
59
|
-
config: ConfigInput<Provider>;
|
|
6
|
+
export type AdapterFactory = (...args: never[]) => Adapter;
|
|
7
|
+
type StorageAdapter = ProviderSlug | AdapterFactory;
|
|
8
|
+
export type FilesPluginContext = {
|
|
9
|
+
storage: (name: string) => Adapter;
|
|
10
|
+
};
|
|
11
|
+
type FactoryFor<A extends StorageAdapter> = A extends ProviderSlug ? ProviderFactories[A] : A;
|
|
12
|
+
type AdapterOptions<A extends StorageAdapter> = Parameters<FactoryFor<A>> extends [] ? undefined : [Parameters<FactoryFor<A>>[0]] extends [never] ? unknown : Parameters<FactoryFor<A>>[0];
|
|
13
|
+
type AdapterConfig<A extends StorageAdapter> = {
|
|
14
|
+
/** Native adapter factory options, or a synchronous runtime resolver for them. */
|
|
15
|
+
config?: Exclude<AdapterOptions<A>, undefined> | (() => Exclude<AdapterOptions<A>, undefined>);
|
|
16
|
+
} & (undefined extends AdapterOptions<A> ? unknown : {
|
|
17
|
+
config: Exclude<AdapterOptions<A>, undefined> | (() => Exclude<AdapterOptions<A>, undefined>);
|
|
60
18
|
});
|
|
19
|
+
type CommonFilesOptions<A extends StorageAdapter> = Omit<FilesOptions<ReturnType<FactoryFor<A>>>, 'adapter' | 'hooks' | 'plugins'>;
|
|
61
20
|
/** Configuration for one Files SDK storage. */
|
|
62
|
-
export type StorageConfig<Provider extends
|
|
63
|
-
/** Files SDK provider slug
|
|
21
|
+
export type StorageConfig<Provider extends StorageAdapter = StorageAdapter, Plugins extends readonly FilesPlugin[] = readonly FilesPlugin[]> = Provider extends StorageAdapter ? {
|
|
22
|
+
/** Files SDK provider slug or a compatible adapter factory. */
|
|
64
23
|
adapter: Provider;
|
|
65
|
-
|
|
66
|
-
plugins?: Plugins;
|
|
67
|
-
/** Native Files SDK lifecycle hooks. */
|
|
24
|
+
plugins?: Plugins | ((context: FilesPluginContext) => Plugins);
|
|
68
25
|
hooks?: FilesHooks;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export type
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
26
|
+
} & CommonFilesOptions<Provider> & AdapterConfig<Provider> : never;
|
|
27
|
+
export type FilesRoute = Omit<CreateFilesRouterOptions, 'files' | 'authorize'> & {
|
|
28
|
+
path: string;
|
|
29
|
+
storage?: string;
|
|
30
|
+
authorize?: (context: AuthorizeContext & {
|
|
31
|
+
event: H3Event;
|
|
32
|
+
}) => AuthorizeResult | Promise<AuthorizeResult>;
|
|
33
|
+
};
|
|
34
|
+
export type StorageSet = StorageConfig | Record<string, StorageConfig>;
|
|
35
|
+
/** One environment's Files options, before or after resolution. */
|
|
36
|
+
export interface FilesEnvironmentConfig {
|
|
37
|
+
storage?: StorageSet;
|
|
38
|
+
routes?: readonly FilesRoute[];
|
|
39
|
+
}
|
|
40
|
+
/** User-authored configuration. Environment blocks can override every Files option. */
|
|
41
|
+
export interface FilesConfigInput extends FilesEnvironmentConfig {
|
|
42
|
+
/** Overrides applied by the development server. */
|
|
43
|
+
$development?: FilesEnvironmentConfig;
|
|
44
|
+
$production?: FilesEnvironmentConfig;
|
|
45
|
+
$test?: FilesEnvironmentConfig;
|
|
46
|
+
$prerender?: FilesEnvironmentConfig;
|
|
47
|
+
$env?: Record<string, FilesEnvironmentConfig>;
|
|
86
48
|
}
|
|
87
|
-
/**
|
|
88
|
-
export interface
|
|
49
|
+
/** Environment-resolved runtime configuration. */
|
|
50
|
+
export interface FilesConfig extends FilesEnvironmentConfig {
|
|
51
|
+
storage: StorageSet;
|
|
52
|
+
}
|
|
53
|
+
export interface SingleFilesConfig extends FilesConfig {
|
|
89
54
|
/** Files SDK storage configuration. */
|
|
90
|
-
storage:
|
|
91
|
-
/** Development-only provider settings keyed by storage name. */
|
|
92
|
-
devStorage?: Partial<{ [Name in keyof Storages]: DevStorageConfig; }>;
|
|
55
|
+
storage: StorageConfig;
|
|
93
56
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
storage?: never;
|
|
97
|
-
devStorage: Storage;
|
|
57
|
+
export interface NamedFilesConfig extends FilesConfig {
|
|
58
|
+
storage: Record<string, StorageConfig>;
|
|
98
59
|
}
|
|
99
|
-
|
|
100
|
-
|
|
60
|
+
type EntryCheck<T> = T extends {
|
|
61
|
+
adapter: infer A extends StorageAdapter;
|
|
62
|
+
} ? T extends StorageConfig<A> ? Record<Exclude<keyof T, keyof StorageConfig<A>>, never> : StorageConfig<A> : never;
|
|
63
|
+
type StorageCheck<T> = T extends {
|
|
64
|
+
adapter: unknown;
|
|
65
|
+
} ? EntryCheck<T> : T extends Record<string, unknown> ? { [K in keyof T]: EntryCheck<T[K]>; } : never;
|
|
66
|
+
type StorageOf<T> = T extends {
|
|
67
|
+
storage: infer S;
|
|
68
|
+
} ? S : never;
|
|
69
|
+
type StorageBranches<T> = StorageOf<T> | StorageOf<T extends {
|
|
70
|
+
$development: infer E;
|
|
71
|
+
} ? E : never> | StorageOf<T extends {
|
|
72
|
+
$production: infer E;
|
|
73
|
+
} ? E : never> | StorageOf<T extends {
|
|
74
|
+
$test: infer E;
|
|
75
|
+
} ? E : never> | StorageOf<T extends {
|
|
76
|
+
$prerender: infer E;
|
|
77
|
+
} ? E : never> | StorageOf<T extends {
|
|
78
|
+
$env: infer E;
|
|
79
|
+
} ? E[keyof E] : never>;
|
|
80
|
+
type StorageNames<T> = StorageBranches<T> extends (infer S) ? S extends {
|
|
81
|
+
adapter: unknown;
|
|
82
|
+
} ? never : S extends object ? Extract<keyof S, string> : never : never;
|
|
83
|
+
type RouteCheck<T> = FilesRoute & ([StorageNames<T>] extends [never] ? {
|
|
101
84
|
storage?: never;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
85
|
+
} : {
|
|
86
|
+
storage: StorageNames<T>;
|
|
87
|
+
});
|
|
88
|
+
type EnvironmentCheck<T, Root> = T extends object ? Record<Exclude<keyof T, 'storage' | 'routes'>, never> & (T extends {
|
|
89
|
+
storage: infer S;
|
|
90
|
+
} ? {
|
|
91
|
+
storage: S & StorageCheck<S>;
|
|
92
|
+
} : unknown) & (T extends {
|
|
93
|
+
routes: unknown;
|
|
94
|
+
} ? {
|
|
95
|
+
routes: readonly RouteCheck<Root>[];
|
|
96
|
+
} : unknown) : never;
|
|
97
|
+
type InputCheck<T> = Record<Exclude<keyof T, 'storage' | 'routes' | '$development' | '$production' | '$test' | '$prerender' | '$env'>, never> & (T extends {
|
|
98
|
+
storage: infer S;
|
|
99
|
+
} ? {
|
|
100
|
+
storage: S & StorageCheck<S>;
|
|
101
|
+
} : unknown) & (T extends {
|
|
102
|
+
routes: unknown;
|
|
103
|
+
} ? {
|
|
104
|
+
routes: readonly RouteCheck<T>[];
|
|
105
|
+
} : unknown) & { [K in Extract<keyof T, '$development' | '$production' | '$test' | '$prerender'>]: T[K] & EnvironmentCheck<T[K], T>; } & (T extends {
|
|
106
|
+
$env: infer E extends Record<string, unknown>;
|
|
107
|
+
} ? {
|
|
108
|
+
$env: { [K in keyof E]: E[K] & EnvironmentCheck<E[K], T>; };
|
|
109
|
+
} : unknown);
|
|
110
|
+
/** Preserve storage names, adapters, and plugin literals without resolving an environment. */
|
|
111
|
+
export declare function defineFilesConfig<const C extends object>(config: C & FilesConfigInput & InputCheck<NoInfer<C>>): C;
|
|
114
112
|
//#endregion
|
|
115
113
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
//#region src/config.ts
|
|
2
|
+
/** Preserve storage names, adapters, and plugin literals without resolving an environment. */
|
|
2
3
|
function defineFilesConfig(config) {
|
|
3
|
-
|
|
4
|
-
return { storage: config.storage };
|
|
4
|
+
return config;
|
|
5
5
|
}
|
|
6
6
|
//#endregion
|
|
7
7
|
export { defineFilesConfig };
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["import type { FilesHooks, FilesPlugin, ProviderSlug } from 'files-sdk'\
|
|
1
|
+
{"version":3,"file":"config.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["import type { Adapter, FilesHooks, FilesOptions, FilesPlugin, ProviderSlug } from 'files-sdk'\nimport type { AuthorizeContext, AuthorizeResult, CreateFilesRouterOptions } from 'files-sdk/api'\nimport type { H3Event } from 'h3'\n\nimport type { ProviderFactories } from './runtime/provider-types'\n\nexport type AdapterFactory = (...args: never[]) => Adapter\ntype StorageAdapter = ProviderSlug | AdapterFactory\nexport type FilesPluginContext = { storage: (name: string) => Adapter }\ntype FactoryFor<A extends StorageAdapter> = A extends ProviderSlug ? ProviderFactories[A] : A\ntype AdapterOptions<A extends StorageAdapter> =\n Parameters<FactoryFor<A>> extends []\n ? undefined\n : [Parameters<FactoryFor<A>>[0]] extends [never]\n ? unknown\n : Parameters<FactoryFor<A>>[0]\ntype AdapterConfig<A extends StorageAdapter> = {\n /** Native adapter factory options, or a synchronous runtime resolver for them. */\n config?: Exclude<AdapterOptions<A>, undefined> | (() => Exclude<AdapterOptions<A>, undefined>)\n} & (undefined extends AdapterOptions<A>\n ? unknown\n : { config: Exclude<AdapterOptions<A>, undefined> | (() => Exclude<AdapterOptions<A>, undefined>) })\ntype CommonFilesOptions<A extends StorageAdapter> = Omit<\n FilesOptions<ReturnType<FactoryFor<A>>>,\n 'adapter' | 'hooks' | 'plugins'\n>\n\n/** Configuration for one Files SDK storage. */\nexport type StorageConfig<\n Provider extends StorageAdapter = StorageAdapter,\n Plugins extends readonly FilesPlugin[] = readonly FilesPlugin[],\n> = Provider extends StorageAdapter\n ? {\n /** Files SDK provider slug or a compatible adapter factory. */\n adapter: Provider\n plugins?: Plugins | ((context: FilesPluginContext) => Plugins)\n hooks?: FilesHooks\n } & CommonFilesOptions<Provider> &\n AdapterConfig<Provider>\n : never\n\nexport type FilesRoute = Omit<CreateFilesRouterOptions, 'files' | 'authorize'> & {\n path: string\n storage?: string\n authorize?: (context: AuthorizeContext & { event: H3Event }) => AuthorizeResult | Promise<AuthorizeResult>\n}\n\nexport type StorageSet = StorageConfig | Record<string, StorageConfig>\n/** One environment's Files options, before or after resolution. */\nexport interface FilesEnvironmentConfig {\n storage?: StorageSet\n routes?: readonly FilesRoute[]\n}\n/** User-authored configuration. Environment blocks can override every Files option. */\nexport interface FilesConfigInput extends FilesEnvironmentConfig {\n /** Overrides applied by the development server. */\n $development?: FilesEnvironmentConfig\n $production?: FilesEnvironmentConfig\n $test?: FilesEnvironmentConfig\n $prerender?: FilesEnvironmentConfig\n $env?: Record<string, FilesEnvironmentConfig>\n}\n/** Environment-resolved runtime configuration. */\nexport interface FilesConfig extends FilesEnvironmentConfig {\n storage: StorageSet\n}\nexport interface SingleFilesConfig extends FilesConfig {\n /** Files SDK storage configuration. */\n storage: StorageConfig\n}\nexport interface NamedFilesConfig extends FilesConfig {\n storage: Record<string, StorageConfig>\n}\n\ntype EntryCheck<T> = T extends { adapter: infer A extends StorageAdapter }\n ? T extends StorageConfig<A>\n ? Record<Exclude<keyof T, keyof StorageConfig<A>>, never>\n : StorageConfig<A>\n : never\ntype StorageCheck<T> = T extends { adapter: unknown }\n ? EntryCheck<T>\n : T extends Record<string, unknown>\n ? { [K in keyof T]: EntryCheck<T[K]> }\n : never\ntype StorageOf<T> = T extends { storage: infer S } ? S : never\ntype StorageBranches<T> =\n | StorageOf<T>\n | StorageOf<T extends { $development: infer E } ? E : never>\n | StorageOf<T extends { $production: infer E } ? E : never>\n | StorageOf<T extends { $test: infer E } ? E : never>\n | StorageOf<T extends { $prerender: infer E } ? E : never>\n | StorageOf<T extends { $env: infer E } ? E[keyof E] : never>\ntype StorageNames<T> =\n StorageBranches<T> extends infer S\n ? S extends { adapter: unknown }\n ? never\n : S extends object\n ? Extract<keyof S, string>\n : never\n : never\ntype RouteCheck<T> = FilesRoute &\n ([StorageNames<T>] extends [never] ? { storage?: never } : { storage: StorageNames<T> })\ntype EnvironmentCheck<T, Root> = T extends object\n ? Record<Exclude<keyof T, 'storage' | 'routes'>, never> &\n (T extends { storage: infer S } ? { storage: S & StorageCheck<S> } : unknown) &\n (T extends { routes: unknown } ? { routes: readonly RouteCheck<Root>[] } : unknown)\n : never\ntype InputCheck<T> = Record<\n Exclude<keyof T, 'storage' | 'routes' | '$development' | '$production' | '$test' | '$prerender' | '$env'>,\n never\n> &\n (T extends { storage: infer S } ? { storage: S & StorageCheck<S> } : unknown) &\n (T extends { routes: unknown } ? { routes: readonly RouteCheck<T>[] } : unknown) & {\n [K in Extract<keyof T, '$development' | '$production' | '$test' | '$prerender'>]: T[K] &\n EnvironmentCheck<T[K], T>\n } & (T extends { $env: infer E extends Record<string, unknown> }\n ? { $env: { [K in keyof E]: E[K] & EnvironmentCheck<E[K], T> } }\n : unknown)\n\n/** Preserve storage names, adapters, and plugin literals without resolving an environment. */\nexport function defineFilesConfig<const C extends object>(config: C & FilesConfigInput & InputCheck<NoInfer<C>>): C {\n return config\n}\n"],"mappings":";;AAwHA,SAAgB,kBAA0C,QAA0D;CAChH,OAAO;AACX"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
//#region src/devtools/auth.ts
|
|
2
|
+
const FILES_DEVTOOLS_TOKEN_TTL = 6e4;
|
|
3
|
+
const encoder = new TextEncoder();
|
|
4
|
+
const base64Url = (bytes) => btoa(String.fromCharCode(...bytes)).replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
|
|
5
|
+
const decodeBase64Url = (value) => {
|
|
6
|
+
const base64 = value.replaceAll("-", "+").replaceAll("_", "/");
|
|
7
|
+
const decoded = atob(`${base64}${"=".repeat((4 - base64.length % 4) % 4)}`);
|
|
8
|
+
const bytes = new Uint8Array(new ArrayBuffer(decoded.length));
|
|
9
|
+
for (let index = 0; index < decoded.length; index += 1) bytes[index] = decoded.charCodeAt(index);
|
|
10
|
+
return bytes.buffer;
|
|
11
|
+
};
|
|
12
|
+
const key = (secret) => crypto.subtle.importKey("raw", encoder.encode(secret), {
|
|
13
|
+
name: "HMAC",
|
|
14
|
+
hash: "SHA-256"
|
|
15
|
+
}, false, ["sign", "verify"]);
|
|
16
|
+
const createFilesDevtoolsToken = async (secret, now = Date.now()) => {
|
|
17
|
+
const expiresAt = now + FILES_DEVTOOLS_TOKEN_TTL;
|
|
18
|
+
const payload = String(expiresAt);
|
|
19
|
+
const signature = await crypto.subtle.sign("HMAC", await key(secret), encoder.encode(payload));
|
|
20
|
+
return {
|
|
21
|
+
token: `${payload}.${base64Url(new Uint8Array(signature))}`,
|
|
22
|
+
expiresAt
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
const verifyFilesDevtoolsToken = async (token, secret, now = Date.now()) => {
|
|
26
|
+
const separator = token.indexOf(".");
|
|
27
|
+
if (separator < 1) return false;
|
|
28
|
+
const payload = token.slice(0, separator);
|
|
29
|
+
const expiresAt = Number(payload);
|
|
30
|
+
if (!Number.isSafeInteger(expiresAt) || expiresAt <= now || expiresAt > now + 6e4) return false;
|
|
31
|
+
try {
|
|
32
|
+
return await crypto.subtle.verify("HMAC", await key(secret), decodeBase64Url(token.slice(separator + 1)), encoder.encode(payload));
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const authorizationHeader = (headers) => {
|
|
38
|
+
if (headers instanceof Headers) return headers.get("authorization") ?? void 0;
|
|
39
|
+
const value = headers.get?.("authorization") ?? headers.authorization;
|
|
40
|
+
return Array.isArray(value) ? value[0] : value;
|
|
41
|
+
};
|
|
42
|
+
const authorizeFilesDevtoolsRequest = async (request, secret) => {
|
|
43
|
+
const authorization = authorizationHeader(request.headers);
|
|
44
|
+
return authorization?.startsWith("Bearer ") ? verifyFilesDevtoolsToken(authorization.slice(7), secret) : false;
|
|
45
|
+
};
|
|
46
|
+
//#endregion
|
|
47
|
+
export { FILES_DEVTOOLS_TOKEN_TTL, authorizeFilesDevtoolsRequest, createFilesDevtoolsToken, verifyFilesDevtoolsToken };
|
|
48
|
+
|
|
49
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","names":[],"sources":["../../src/devtools/auth.ts"],"sourcesContent":["export interface FilesDevtoolsToken {\n token: string\n expiresAt: number\n}\n\nexport const FILES_DEVTOOLS_TOKEN_TTL = 60_000\n\nconst encoder = new TextEncoder()\nconst base64Url = (bytes: Uint8Array): string =>\n btoa(String.fromCharCode(...bytes))\n .replaceAll('+', '-')\n .replaceAll('/', '_')\n .replaceAll('=', '')\nconst decodeBase64Url = (value: string): ArrayBuffer => {\n const base64 = value.replaceAll('-', '+').replaceAll('_', '/')\n const decoded = atob(`${base64}${'='.repeat((4 - (base64.length % 4)) % 4)}`)\n const bytes = new Uint8Array(new ArrayBuffer(decoded.length))\n for (let index = 0; index < decoded.length; index += 1) bytes[index] = decoded.charCodeAt(index)\n return bytes.buffer\n}\nconst key = (secret: string): Promise<CryptoKey> =>\n crypto.subtle.importKey('raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign', 'verify'])\n\nexport const createFilesDevtoolsToken = async (secret: string, now = Date.now()): Promise<FilesDevtoolsToken> => {\n const expiresAt = now + FILES_DEVTOOLS_TOKEN_TTL\n const payload = String(expiresAt)\n const signature = await crypto.subtle.sign('HMAC', await key(secret), encoder.encode(payload))\n return { token: `${payload}.${base64Url(new Uint8Array(signature))}`, expiresAt }\n}\n\nexport const verifyFilesDevtoolsToken = async (token: string, secret: string, now = Date.now()): Promise<boolean> => {\n const separator = token.indexOf('.')\n if (separator < 1) return false\n const payload = token.slice(0, separator)\n const expiresAt = Number(payload)\n if (!Number.isSafeInteger(expiresAt) || expiresAt <= now || expiresAt > now + FILES_DEVTOOLS_TOKEN_TTL) return false\n try {\n return await crypto.subtle.verify(\n 'HMAC',\n await key(secret),\n decodeBase64Url(token.slice(separator + 1)),\n encoder.encode(payload),\n )\n } catch {\n return false\n }\n}\n\ntype RequestHeaders =\n | Headers\n | {\n authorization?: string | string[] | undefined\n get?: ((name: string) => string | null | undefined) | undefined\n }\n\nconst authorizationHeader = (headers: RequestHeaders): string | undefined => {\n if (headers instanceof Headers) return headers.get('authorization') ?? undefined\n const value = headers.get?.('authorization') ?? headers.authorization\n return Array.isArray(value) ? value[0] : value\n}\n\nexport const authorizeFilesDevtoolsRequest = async (\n request: { headers: RequestHeaders },\n secret: string,\n): Promise<boolean> => {\n const authorization = authorizationHeader(request.headers)\n return authorization?.startsWith('Bearer ')\n ? verifyFilesDevtoolsToken(authorization.slice('Bearer '.length), secret)\n : false\n}\n"],"mappings":";AAKA,MAAa,2BAA2B;AAExC,MAAM,UAAU,IAAI,YAAY;AAChC,MAAM,aAAa,UACf,KAAK,OAAO,aAAa,GAAG,KAAK,CAAC,CAAC,CAC9B,WAAW,KAAK,GAAG,CAAC,CACpB,WAAW,KAAK,GAAG,CAAC,CACpB,WAAW,KAAK,EAAE;AAC3B,MAAM,mBAAmB,UAA+B;CACpD,MAAM,SAAS,MAAM,WAAW,KAAK,GAAG,CAAC,CAAC,WAAW,KAAK,GAAG;CAC7D,MAAM,UAAU,KAAK,GAAG,SAAS,IAAI,QAAQ,IAAK,OAAO,SAAS,KAAM,CAAC,GAAG;CAC5E,MAAM,QAAQ,IAAI,WAAW,IAAI,YAAY,QAAQ,MAAM,CAAC;CAC5D,KAAK,IAAI,QAAQ,GAAG,QAAQ,QAAQ,QAAQ,SAAS,GAAG,MAAM,SAAS,QAAQ,WAAW,KAAK;CAC/F,OAAO,MAAM;AACjB;AACA,MAAM,OAAO,WACT,OAAO,OAAO,UAAU,OAAO,QAAQ,OAAO,MAAM,GAAG;CAAE,MAAM;CAAQ,MAAM;AAAU,GAAG,OAAO,CAAC,QAAQ,QAAQ,CAAC;AAEvH,MAAa,2BAA2B,OAAO,QAAgB,MAAM,KAAK,IAAI,MAAmC;CAC7G,MAAM,YAAY,MAAM;CACxB,MAAM,UAAU,OAAO,SAAS;CAChC,MAAM,YAAY,MAAM,OAAO,OAAO,KAAK,QAAQ,MAAM,IAAI,MAAM,GAAG,QAAQ,OAAO,OAAO,CAAC;CAC7F,OAAO;EAAE,OAAO,GAAG,QAAQ,GAAG,UAAU,IAAI,WAAW,SAAS,CAAC;EAAK;CAAU;AACpF;AAEA,MAAa,2BAA2B,OAAO,OAAe,QAAgB,MAAM,KAAK,IAAI,MAAwB;CACjH,MAAM,YAAY,MAAM,QAAQ,GAAG;CACnC,IAAI,YAAY,GAAG,OAAO;CAC1B,MAAM,UAAU,MAAM,MAAM,GAAG,SAAS;CACxC,MAAM,YAAY,OAAO,OAAO;CAChC,IAAI,CAAC,OAAO,cAAc,SAAS,KAAK,aAAa,OAAO,YAAY,MAAA,KAAgC,OAAO;CAC/G,IAAI;EACA,OAAO,MAAM,OAAO,OAAO,OACvB,QACA,MAAM,IAAI,MAAM,GAChB,gBAAgB,MAAM,MAAM,YAAY,CAAC,CAAC,GAC1C,QAAQ,OAAO,OAAO,CAC1B;CACJ,QAAQ;EACJ,OAAO;CACX;AACJ;AASA,MAAM,uBAAuB,YAAgD;CACzE,IAAI,mBAAmB,SAAS,OAAO,QAAQ,IAAI,eAAe,KAAK,KAAA;CACvE,MAAM,QAAQ,QAAQ,MAAM,eAAe,KAAK,QAAQ;CACxD,OAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,KAAK;AAC7C;AAEA,MAAa,gCAAgC,OACzC,SACA,WACmB;CACnB,MAAM,gBAAgB,oBAAoB,QAAQ,OAAO;CACzD,OAAO,eAAe,WAAW,SAAS,IACpC,yBAAyB,cAAc,MAAM,CAAgB,GAAG,MAAM,IACtE;AACV"}
|