@kosmojs/fetch-generator 0.0.10 → 0.0.20

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@kosmojs/fetch-generator",
4
- "version": "0.0.10",
4
+ "version": "0.0.20",
5
5
  "author": "Slee Woo",
6
6
  "license": "MIT",
7
7
  "publishConfig": {
@@ -17,10 +17,7 @@
17
17
  }
18
18
  },
19
19
  "dependencies": {
20
- "@kosmojs/devlib": "^0.0.10"
21
- },
22
- "devDependencies": {
23
- "@kosmojs/config": "^0.0.10"
20
+ "@kosmojs/dev": "^0.0.20"
24
21
  },
25
22
  "scripts": {
26
23
  "build": "esbuilder src/index.ts"
package/pkg/index.js CHANGED
@@ -1,22 +1,15 @@
1
1
  // src/factory.ts
2
- import { dirname, join } from "node:path";
3
2
  import {
4
- defaults,
5
3
  pathResolver,
6
- renderToFile
7
- } from "@kosmojs/devlib";
4
+ renderFactory,
5
+ sortRoutes
6
+ } from "@kosmojs/dev";
8
7
 
9
8
  // src/templates/fetch.hbs
10
- var fetch_default = 'import useFetch from "@kosmojs/fetch";\n\nimport { baseurl, apiurl } from "{{importPathmap.config}}";\nimport { validationSchemas } from "./schemas";\n\nimport {\n type MaybeWrapped,\n type HostOpt,\n unwrap,\n join,\n stringify,\n createHost,\n} from "{{importPathmap.fetchLib}}";\n\nexport { ValidationError } from "@kosmojs/api";\n\nexport type ParamsT = [\n {{#each route.params.schema}}\n {{#if isRest}}\n ...{{const}}: Array<string | number>\n {{else}}\n {{const}}{{#unless isRequired}}?{{/unless}}: import("./types").{{../route.params.id}}["{{name}}"],\n {{/if}}\n {{/each}}\n];\n\nexport type PayloadT = {\n {{#each route.payloadTypes}}\n {{method}}: import("./types").{{id}};\n {{/each}}\n}\n\nexport type ResponseT = {\n {{#each route.responseTypes}}\n {{method}}: import("./types").{{id}};\n {{/each}}\n}\n\nconst paramsMapper = (params: ParamsT) => {\n return {\n {{#each paramsMapper}}\n "{{name}}": params[{{idx}}],\n {{/each}}\n }\n}\n\nconst pathTokens: Array<[ path: string, param?: { isRest: boolean } ]> = [\n {{#each route.pathTokens}}\n [\n "{{path}}",{{#if param}}\n { isRest: {{#if param.isRest}}true{{else}}false{{/if}} }{{/if}}\n ],\n {{/each}}\n];\n\nconst parametrize = (params: ParamsT) => {\n const paramsClone = structuredClone(params);\n return pathTokens\n .flatMap(([ path, param ]) => {\n if (param?.isRest) {\n return paramsClone;\n }\n if (param) {\n return paramsClone.splice(0, 1);\n }\n return [path];\n })\n .join("/");\n}\n\nconst fetchApi = useFetch(join(baseurl, apiurl), { stringify });\n\n{{#each routeMethods}}\nexport const {{method}} = (\n _params{{#if ../route.optionalParams}}?{{/if}}: MaybeWrapped<ParamsT>,\n {{#if payloadType}}\n _payload{{#if payloadType.isOptional}}?{{/if}}: MaybeWrapped<\n PayloadT["{{method}}"]\n >,\n {{else}}\n _payload?: unknown,\n {{/if}}\n): Promise<\n {{#if responseType}}\n ResponseT["{{method}}"]\n {{else}}\n unknown\n {{/if}}\n> => {\n const [params, payload] = [unwrap(_params || []), unwrap(_payload || {})];\n if (validationSchemas.params) {\n validationSchemas.params.validate(paramsMapper(params as never));\n }\n if (validationSchemas.payload?.{{method}}) {\n validationSchemas.payload.{{method}}.validate(payload);\n }\n return fetchApi.{{method}}(\n parametrize(params as never),\n payload,\n )\n};\n{{/each}}\n\nexport const path = (\n params: ParamsT,\n query?: Record<string, unknown>,\n) => {\n const path = join(\n baseurl,\n {{#if route.base}}"{{route.base}}"{{else}}apiurl{{/if}},\n parametrize(params)\n );\n return query\n ? [ path, stringify(query) ].join("?")\n : path;\n}\n\nexport const href = (\n host: HostOpt,\n params: ParamsT,\n query?: Record<string, unknown>,\n) => createHost(host) + path(params, query);\n\nexport default {\n {{#each routeMethods}}\n {{method}},\n {{/each}}\n path,\n href,\n validationSchemas,\n};\n';
9
+ var fetch_default = '{{#each routes}}\nimport {{id}} from "{{ createImport "fetch" name }}";\n{{/each}}\n\nexport { ValidationError } from "@kosmojs/api/errors";\n\nexport type PayloadT = {\n{{#each routes}} "{{name}}": import("{{ createImport "fetch" name }}").PayloadT;\n{{/each}}\n}\n\nexport type ResponseT = {\n{{#each routes}} "{{name}}": import("{{ createImport "fetch" name }}").ResponseT;\n{{/each}}\n}\n\nexport default {\n{{#each routes}} "{{name}}": {{id}},\n{{#each alias }} "{{this}}": {{../id}},\n{{/each}}\n{{/each}}\n}\n';
11
10
 
12
- // src/templates/index.hbs
13
- var templates_default = '{{#each routes}}\nimport {{importName}} from "{{importPathmap.fetchApi}}";\n{{/each}}\n\nexport { ValidationError } from "@kosmojs/api";\n\nexport default {\n{{#each routes}} "{{name}}": {{importName}},\n{{#each alias }} "{{this}}": {{../importName}},\n{{/each}}\n{{/each}}\n}\n';
14
-
15
- // src/templates/lib.hbs
16
- var lib_default = 'export { stringify } from "@kosmojs/fetch";\n\nexport * from "./unwrap";\n\nexport type HostOpt =\n | string\n | { hostname: string; port?: number; secure?: boolean };\n\nexport const createHost = (host: HostOpt): string => {\n if (typeof host === "string") {\n return host;\n }\n\n if (typeof host === "object") {\n return [\n host.secure ? "https://" : "http://",\n host.hostname,\n host.port ? `:${host.port}` : "",\n ]\n .join("")\n .replace(/\\/+$/, "");\n }\n\n throw new Error(\n "Expected host to be a string or an object similar to { hostname: string; port?: number; secure?: boolean }",\n );\n};\n\nexport const join = (...args: Array<unknown>): string => {\n for (const a of args) {\n if (typeof a === "string" || typeof a === "number") {\n continue;\n }\n throw new Error(\n `The "path" argument must be of type string or number. Received type ${typeof a} (${JSON.stringify(a)})`,\n );\n }\n return args.join("/").replace(/\\/+/g, "/");\n};\n';
17
-
18
- // src/templates/types.hbs
19
- var types_default = 'export type PayloadT = {\n {{#each routes}}\n "{{name}}": import("{{importPathmap.fetchApi}}").PayloadT;\n {{/each}}\n}\n\nexport type ResponseT = {\n {{#each routes}}\n "{{name}}": import("{{importPathmap.fetchApi}}").ResponseT;\n {{/each}}\n}\n';
11
+ // src/templates/route.hbs
12
+ var route_default = 'import fetchFactory, {\n type HostOpt,\n join,\n stringify,\n createHost,\n} from "@kosmojs/fetch";\n\nimport { baseurl, apiurl } from "{{ createImport "config" }}";\nimport { validationSchemas } from "{{ createImport "libApi" route.name "schemas" }}";\n\nimport {\n type MaybeWrapped,\n unwrap,\n} from "{{ createImport "lib" "unwrap" }}";\n\nimport type {\n {{route.params.id}},\n{{#each route.payloadTypes}} {{id}},\n{{/each}}\n{{#each route.responseTypes}} {{id}},\n{{/each}}\n} from "{{ createImport "libApi" route.name "types" }}";\n\nexport { ValidationError } from "@kosmojs/api/errors";\n\nexport type ParamsT = [\n {{#each route.params.schema}}\n {{#if isRest}}\n ...{{const}}: Array<string | number>\n {{else}}\n {{const}}{{#unless isRequired}}?{{/unless}}: {{../route.params.id}}["{{name}}"],\n {{/if}}\n {{/each}}\n];\n\nexport type PayloadT = {\n {{#each route.payloadTypes}}\n {{method}}: {{id}};\n {{/each}}\n}\n\nexport type ResponseT = {\n {{#each route.responseTypes}}\n {{method}}: {{id}};\n {{/each}}\n}\n\nconst paramsMapper = (params: ParamsT) => {\n return {\n {{#each paramsMapper}}\n "{{name}}": params[{{idx}}],\n {{/each}}\n }\n}\n\nconst pathTokens: Array<[ path: string, param?: { isRest: boolean } ]> = [\n {{#each route.pathTokens}}\n [\n "{{path}}",{{#if param}}\n { isRest: {{#if param.isRest}}true{{else}}false{{/if}} }{{/if}}\n ],\n {{/each}}\n];\n\nconst parametrize = (params: ParamsT) => {\n const paramsClone = structuredClone(params);\n return pathTokens\n .flatMap(([ path, param ]) => {\n if (param?.isRest) {\n return paramsClone;\n }\n if (param) {\n return paramsClone.splice(0, 1);\n }\n return [path];\n })\n .join("/");\n}\n\nconst fetchApi = fetchFactory(join(baseurl, apiurl), { stringify });\n\n{{#each routeMethods}}\nexport const {{method}} = (\n _params{{#if ../route.optionalParams}}?{{/if}}: MaybeWrapped<ParamsT>,\n {{#if payloadType}}\n _payload{{#if payloadType.isOptional}}?{{/if}}: MaybeWrapped<\n PayloadT["{{method}}"]\n >,\n {{else}}\n _payload?: unknown,\n {{/if}}\n): Promise<\n {{#if responseType}}\n ResponseT["{{method}}"]\n {{else}}\n unknown\n {{/if}}\n> => {\n const [params, payload] = [unwrap(_params || []), unwrap(_payload || {})];\n if (validationSchemas.params) {\n validationSchemas.params.validate(paramsMapper(params as never));\n }\n if (validationSchemas.payload?.{{method}}) {\n validationSchemas.payload.{{method}}.validate(payload);\n }\n return fetchApi.{{method}}(\n parametrize(params as never),\n payload,\n )\n};\n{{/each}}\n\nexport const path = (\n params: ParamsT,\n query?: Record<string, unknown>,\n) => {\n const path = join(\n baseurl,\n {{#if route.base}}"{{route.base}}"{{else}}apiurl{{/if}},\n parametrize(params)\n );\n return query\n ? [ path, stringify(query) ].join("?")\n : path;\n}\n\nexport const href = (\n host: HostOpt,\n params: ParamsT,\n query?: Record<string, unknown>,\n) => createHost(host) + path(params, query);\n\nexport default {\n {{#each routeMethods}}\n {{method}},\n {{/each}}\n path,\n href,\n validationSchemas,\n};\n';
20
13
 
21
14
  // src/templates/unwrap.hbs
22
15
  var unwrap_default = "export type MaybeWrapped<T> = T;\nexport const unwrap = <T>(data: T) => data;\n";
@@ -27,100 +20,62 @@ var factory = async ({
27
20
  sourceFolder,
28
21
  formatters
29
22
  }) => {
30
- const { resolve } = pathResolver({ appRoot, sourceFolder });
31
- for (const [file, template] of [
32
- // These files supposed to be replaced by specialized generators,
33
- // so write them only during initialization.
34
- ["unwrap.ts", unwrap_default]
35
- ]) {
36
- await renderToFile(
37
- resolve("fetchLibDir", file),
38
- template,
39
- {},
40
- { formatters }
41
- );
42
- }
43
- const generateLibFiles = async (entries) => {
44
- for (const { kind, entry } of entries) {
45
- if (kind !== "apiRoute") {
46
- continue;
47
- }
48
- await renderToFile(
49
- resolve("apiLibDir", dirname(entry.file), "fetch.ts"),
50
- fetch_default,
51
- {
52
- route: entry,
53
- routeMethods: entry.methods.map((method) => {
54
- const payloadType = entry.payloadTypes.find(
55
- (e) => e.method === method
56
- );
57
- const responseType = entry.responseTypes.find(
58
- (e) => e.method === method
59
- );
60
- return {
61
- method,
62
- payloadType,
63
- responseType
64
- };
65
- }),
66
- paramsMapper: entry.params.schema.map(({ name }, idx) => ({
67
- name,
68
- idx
69
- })),
70
- importPathmap: {
71
- core: join(defaults.appPrefix, defaults.coreDir),
72
- config: join(sourceFolder, defaults.configDir),
73
- fetchLib: join(sourceFolder, defaults.fetchLibDir, "lib")
74
- }
75
- },
76
- { formatters }
77
- );
23
+ const { createPath, createImportHelper } = pathResolver({
24
+ appRoot,
25
+ sourceFolder
26
+ });
27
+ const { renderToFile } = renderFactory({
28
+ formatters,
29
+ helpers: {
30
+ createImport: createImportHelper
78
31
  }
79
- };
80
- const generateIndexFiles = async (entries) => {
81
- const routes = entries.flatMap(({ kind, entry }) => kind === "apiRoute" ? [entry] : []).sort((a, b) => a.name.localeCompare(b.name));
82
- for (const [file, template] of [
83
- ["index.ts", templates_default],
84
- ["lib.ts", lib_default],
85
- ["types.ts", types_default]
86
- ]) {
87
- await renderToFile(
88
- resolve("fetchLibDir", file),
89
- template,
90
- {
91
- routes: routes.map((route) => {
92
- return {
93
- ...route,
94
- importPathmap: {
95
- fetchApi: join(
96
- sourceFolder,
97
- defaults.apiLibDir,
98
- dirname(route.file),
99
- "fetch"
100
- )
101
- }
102
- };
103
- })
104
- },
105
- { formatters }
106
- );
32
+ });
33
+ await renderToFile(createPath.lib("unwrap.ts"), unwrap_default, {});
34
+ const generateLibFiles = async (entries, updatedEntries) => {
35
+ const routes = entries.flatMap(({ kind, entry }) => kind === "apiRoute" ? [entry] : []).sort(sortRoutes);
36
+ await renderToFile(`${createPath.fetch()}.ts`, fetch_default, { routes });
37
+ for (const { kind, entry } of updatedEntries) {
38
+ if (kind === "apiRoute") {
39
+ const routeMethods = entry.methods.map((method) => {
40
+ const payloadType = entry.payloadTypes.find(
41
+ (e) => e.method === method
42
+ );
43
+ const responseType = entry.responseTypes.find(
44
+ (e) => e.method === method
45
+ );
46
+ return {
47
+ method,
48
+ payloadType,
49
+ responseType
50
+ };
51
+ });
52
+ const paramsMapper = entry.params.schema.map(({ name }, idx) => ({
53
+ name,
54
+ idx
55
+ }));
56
+ await renderToFile(createPath.fetch(entry.file), route_default, {
57
+ route: entry,
58
+ routeMethods,
59
+ paramsMapper
60
+ });
61
+ }
107
62
  }
108
63
  };
109
64
  return {
110
- async watchHandler(entries, event) {
111
- if (event) {
112
- if (event.kind === "update") {
113
- await generateLibFiles(
114
- entries.filter(({ kind, entry }) => {
115
- return kind === "apiRoute" ? entry.fileFullpath === event.file || entry.referencedFiles?.includes(event.file) : false;
116
- })
117
- );
118
- }
119
- } else {
120
- await generateLibFiles(entries);
121
- }
122
- await generateIndexFiles(entries);
123
- return void 0;
65
+ async watch(entries, event) {
66
+ await generateLibFiles(
67
+ entries,
68
+ // create/overwrite lib files with proper content.
69
+ // handle 2 cases:
70
+ // - event is undefined (means initial call): process all routes
71
+ // - `update` event given: process updated route
72
+ event ? entries.filter(({ kind, entry }) => {
73
+ return event.kind === "update" ? kind === "apiRoute" ? entry.fileFullpath === event.file : false : false;
74
+ }) : entries
75
+ );
76
+ },
77
+ async build(entries) {
78
+ await generateLibFiles(entries, entries);
124
79
  }
125
80
  };
126
81
  };
@@ -129,6 +84,7 @@ var factory = async ({
129
84
  var index_default = () => {
130
85
  return {
131
86
  name: "Fetch",
87
+ kind: "fetch",
132
88
  moduleImport: import.meta.filename,
133
89
  moduleConfig: void 0,
134
90
  factory
package/pkg/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/factory.ts", "../src/templates/fetch.hbs", "../src/templates/index.hbs", "../src/templates/lib.hbs", "../src/templates/types.hbs", "../src/templates/unwrap.hbs", "../src/index.ts"],
4
- "sourcesContent": ["import { dirname, join } from \"node:path\";\n\nimport {\n defaults,\n type GeneratorFactory,\n pathResolver,\n type ResolvedEntry,\n renderToFile,\n} from \"@kosmojs/devlib\";\n\nimport fetchTpl from \"./templates/fetch.hbs\";\nimport indexTpl from \"./templates/index.hbs\";\nimport libTpl from \"./templates/lib.hbs\";\nimport typesTpl from \"./templates/types.hbs\";\nimport unwrapTpl from \"./templates/unwrap.hbs\";\n\nexport const factory: GeneratorFactory = async ({\n appRoot,\n sourceFolder,\n formatters,\n}) => {\n const { resolve } = pathResolver({ appRoot, sourceFolder });\n\n for (const [file, template] of [\n // These files supposed to be replaced by specialized generators,\n // so write them only during initialization.\n [\"unwrap.ts\", unwrapTpl],\n ]) {\n await renderToFile(\n resolve(\"fetchLibDir\", file),\n template,\n {},\n { formatters },\n );\n }\n\n const generateLibFiles = async (entries: Array<ResolvedEntry>) => {\n for (const { kind, entry } of entries) {\n if (kind !== \"apiRoute\") {\n continue;\n }\n\n await renderToFile(\n resolve(\"apiLibDir\", dirname(entry.file), \"fetch.ts\"),\n fetchTpl,\n {\n route: entry,\n routeMethods: entry.methods.map((method) => {\n const payloadType = entry.payloadTypes.find(\n (e) => e.method === method,\n );\n const responseType = entry.responseTypes.find(\n (e) => e.method === method,\n );\n return {\n method,\n payloadType,\n responseType,\n };\n }),\n paramsMapper: entry.params.schema.map(({ name }, idx) => ({\n name,\n idx,\n })),\n importPathmap: {\n core: join(defaults.appPrefix, defaults.coreDir),\n config: join(sourceFolder, defaults.configDir),\n fetchLib: join(sourceFolder, defaults.fetchLibDir, \"lib\"),\n },\n },\n { formatters },\n );\n }\n };\n\n const generateIndexFiles = async (entries: Array<ResolvedEntry>) => {\n const routes = entries\n .flatMap(({ kind, entry }) => (kind === \"apiRoute\" ? [entry] : []))\n .sort((a, b) => a.name.localeCompare(b.name));\n\n for (const [file, template] of [\n [\"index.ts\", indexTpl],\n [\"lib.ts\", libTpl],\n [\"types.ts\", typesTpl],\n ]) {\n await renderToFile(\n resolve(\"fetchLibDir\", file),\n template,\n {\n routes: routes.map((route) => {\n return {\n ...route,\n importPathmap: {\n fetchApi: join(\n sourceFolder,\n defaults.apiLibDir,\n dirname(route.file),\n \"fetch\",\n ),\n },\n };\n }),\n },\n { formatters },\n );\n }\n };\n\n return {\n async watchHandler(entries, event) {\n if (event) {\n if (event.kind === \"update\") {\n await generateLibFiles(\n entries.filter(({ kind, entry }) => {\n return kind === \"apiRoute\"\n ? entry.fileFullpath === event.file ||\n entry.referencedFiles?.includes(event.file)\n : false;\n }),\n );\n }\n } else {\n // no event means initial call\n await generateLibFiles(entries);\n }\n\n await generateIndexFiles(entries);\n\n return undefined;\n },\n };\n};\n", "import useFetch from \"@kosmojs/fetch\";\n\nimport { baseurl, apiurl } from \"{{importPathmap.config}}\";\nimport { validationSchemas } from \"./schemas\";\n\nimport {\n type MaybeWrapped,\n type HostOpt,\n unwrap,\n join,\n stringify,\n createHost,\n} from \"{{importPathmap.fetchLib}}\";\n\nexport { ValidationError } from \"@kosmojs/api\";\n\nexport type ParamsT = [\n {{#each route.params.schema}}\n {{#if isRest}}\n ...{{const}}: Array<string | number>\n {{else}}\n {{const}}{{#unless isRequired}}?{{/unless}}: import(\"./types\").{{../route.params.id}}[\"{{name}}\"],\n {{/if}}\n {{/each}}\n];\n\nexport type PayloadT = {\n {{#each route.payloadTypes}}\n {{method}}: import(\"./types\").{{id}};\n {{/each}}\n}\n\nexport type ResponseT = {\n {{#each route.responseTypes}}\n {{method}}: import(\"./types\").{{id}};\n {{/each}}\n}\n\nconst paramsMapper = (params: ParamsT) => {\n return {\n {{#each paramsMapper}}\n \"{{name}}\": params[{{idx}}],\n {{/each}}\n }\n}\n\nconst pathTokens: Array<[ path: string, param?: { isRest: boolean } ]> = [\n {{#each route.pathTokens}}\n [\n \"{{path}}\",{{#if param}}\n { isRest: {{#if param.isRest}}true{{else}}false{{/if}} }{{/if}}\n ],\n {{/each}}\n];\n\nconst parametrize = (params: ParamsT) => {\n const paramsClone = structuredClone(params);\n return pathTokens\n .flatMap(([ path, param ]) => {\n if (param?.isRest) {\n return paramsClone;\n }\n if (param) {\n return paramsClone.splice(0, 1);\n }\n return [path];\n })\n .join(\"/\");\n}\n\nconst fetchApi = useFetch(join(baseurl, apiurl), { stringify });\n\n{{#each routeMethods}}\nexport const {{method}} = (\n _params{{#if ../route.optionalParams}}?{{/if}}: MaybeWrapped<ParamsT>,\n {{#if payloadType}}\n _payload{{#if payloadType.isOptional}}?{{/if}}: MaybeWrapped<\n PayloadT[\"{{method}}\"]\n >,\n {{else}}\n _payload?: unknown,\n {{/if}}\n): Promise<\n {{#if responseType}}\n ResponseT[\"{{method}}\"]\n {{else}}\n unknown\n {{/if}}\n> => {\n const [params, payload] = [unwrap(_params || []), unwrap(_payload || {})];\n if (validationSchemas.params) {\n validationSchemas.params.validate(paramsMapper(params as never));\n }\n if (validationSchemas.payload?.{{method}}) {\n validationSchemas.payload.{{method}}.validate(payload);\n }\n return fetchApi.{{method}}(\n parametrize(params as never),\n payload,\n )\n};\n{{/each}}\n\nexport const path = (\n params: ParamsT,\n query?: Record<string, unknown>,\n) => {\n const path = join(\n baseurl,\n {{#if route.base}}\"{{route.base}}\"{{else}}apiurl{{/if}},\n parametrize(params)\n );\n return query\n ? [ path, stringify(query) ].join(\"?\")\n : path;\n}\n\nexport const href = (\n host: HostOpt,\n params: ParamsT,\n query?: Record<string, unknown>,\n) => createHost(host) + path(params, query);\n\nexport default {\n {{#each routeMethods}}\n {{method}},\n {{/each}}\n path,\n href,\n validationSchemas,\n};\n", "{{#each routes}}\nimport {{importName}} from \"{{importPathmap.fetchApi}}\";\n{{/each}}\n\nexport { ValidationError } from \"@kosmojs/api\";\n\nexport default {\n{{#each routes}} \"{{name}}\": {{importName}},\n{{#each alias }} \"{{this}}\": {{../importName}},\n{{/each}}\n{{/each}}\n}\n", "export { stringify } from \"@kosmojs/fetch\";\n\nexport * from \"./unwrap\";\n\nexport type HostOpt =\n | string\n | { hostname: string; port?: number; secure?: boolean };\n\nexport const createHost = (host: HostOpt): string => {\n if (typeof host === \"string\") {\n return host;\n }\n\n if (typeof host === \"object\") {\n return [\n host.secure ? \"https://\" : \"http://\",\n host.hostname,\n host.port ? `:${host.port}` : \"\",\n ]\n .join(\"\")\n .replace(/\\/+$/, \"\");\n }\n\n throw new Error(\n \"Expected host to be a string or an object similar to { hostname: string; port?: number; secure?: boolean }\",\n );\n};\n\nexport const join = (...args: Array<unknown>): string => {\n for (const a of args) {\n if (typeof a === \"string\" || typeof a === \"number\") {\n continue;\n }\n throw new Error(\n `The \"path\" argument must be of type string or number. Received type ${typeof a} (${JSON.stringify(a)})`,\n );\n }\n return args.join(\"/\").replace(/\\/+/g, \"/\");\n};\n", "export type PayloadT = {\n {{#each routes}}\n \"{{name}}\": import(\"{{importPathmap.fetchApi}}\").PayloadT;\n {{/each}}\n}\n\nexport type ResponseT = {\n {{#each routes}}\n \"{{name}}\": import(\"{{importPathmap.fetchApi}}\").ResponseT;\n {{/each}}\n}\n", "export type MaybeWrapped<T> = T;\nexport const unwrap = <T>(data: T) => data;\n", "import type { GeneratorConstructor } from \"@kosmojs/devlib\";\n\nimport { factory } from \"./factory\";\n\nexport default (): GeneratorConstructor => {\n return {\n name: \"Fetch\",\n moduleImport: import.meta.filename,\n moduleConfig: undefined,\n factory,\n };\n};\n"],
5
- "mappings": "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",
3
+ "sources": ["../src/factory.ts", "../src/templates/fetch.hbs", "../src/templates/route.hbs", "../src/templates/unwrap.hbs", "../src/index.ts"],
4
+ "sourcesContent": ["import {\n type GeneratorFactory,\n pathResolver,\n type ResolvedEntry,\n renderFactory,\n sortRoutes,\n} from \"@kosmojs/dev\";\n\nimport fetchTpl from \"./templates/fetch.hbs\";\nimport routeTpl from \"./templates/route.hbs\";\nimport unwrapTpl from \"./templates/unwrap.hbs\";\n\nexport const factory: GeneratorFactory = async ({\n appRoot,\n sourceFolder,\n formatters,\n}) => {\n const { createPath, createImportHelper } = pathResolver({\n appRoot,\n sourceFolder,\n });\n\n const { renderToFile } = renderFactory({\n formatters,\n helpers: {\n createImport: createImportHelper,\n },\n });\n\n // supposed to be replaced by specialized generators, write it only at initialization.\n // fetch generator always runs before other generators\n // so it is safe to re-initialize this file before specialized generators update it.\n await renderToFile(createPath.lib(\"unwrap.ts\"), unwrapTpl, {});\n\n const generateLibFiles = async (\n entries: Array<ResolvedEntry>,\n updatedEntries: Array<ResolvedEntry>,\n ) => {\n const routes = entries\n .flatMap(({ kind, entry }) => (kind === \"apiRoute\" ? [entry] : []))\n .sort(sortRoutes);\n\n await renderToFile(`${createPath.fetch()}.ts`, fetchTpl, { routes });\n\n for (const { kind, entry } of updatedEntries) {\n if (kind === \"apiRoute\") {\n const routeMethods = entry.methods.map((method) => {\n const payloadType = entry.payloadTypes.find(\n (e) => e.method === method,\n );\n const responseType = entry.responseTypes.find(\n (e) => e.method === method,\n );\n return {\n method,\n payloadType,\n responseType,\n };\n });\n\n const paramsMapper = entry.params.schema.map(({ name }, idx) => ({\n name,\n idx,\n }));\n\n await renderToFile(createPath.fetch(entry.file), routeTpl, {\n route: entry,\n routeMethods,\n paramsMapper,\n });\n }\n }\n };\n\n return {\n async watch(entries, event) {\n await generateLibFiles(\n entries,\n // create/overwrite lib files with proper content.\n // handle 2 cases:\n // - event is undefined (means initial call): process all routes\n // - `update` event given: process updated route\n event\n ? entries.filter(({ kind, entry }) => {\n return event.kind === \"update\"\n ? kind === \"apiRoute\"\n ? entry.fileFullpath === event.file\n : false\n : false;\n })\n : entries,\n );\n\n // TODO: handle `delete` event, cleanup lib files\n },\n async build(entries) {\n await generateLibFiles(entries, entries);\n },\n };\n};\n", "{{#each routes}}\nimport {{id}} from \"{{ createImport \"fetch\" name }}\";\n{{/each}}\n\nexport { ValidationError } from \"@kosmojs/api/errors\";\n\nexport type PayloadT = {\n{{#each routes}} \"{{name}}\": import(\"{{ createImport \"fetch\" name }}\").PayloadT;\n{{/each}}\n}\n\nexport type ResponseT = {\n{{#each routes}} \"{{name}}\": import(\"{{ createImport \"fetch\" name }}\").ResponseT;\n{{/each}}\n}\n\nexport default {\n{{#each routes}} \"{{name}}\": {{id}},\n{{#each alias }} \"{{this}}\": {{../id}},\n{{/each}}\n{{/each}}\n}\n", "import fetchFactory, {\n type HostOpt,\n join,\n stringify,\n createHost,\n} from \"@kosmojs/fetch\";\n\nimport { baseurl, apiurl } from \"{{ createImport \"config\" }}\";\nimport { validationSchemas } from \"{{ createImport \"libApi\" route.name \"schemas\" }}\";\n\nimport {\n type MaybeWrapped,\n unwrap,\n} from \"{{ createImport \"lib\" \"unwrap\" }}\";\n\nimport type {\n {{route.params.id}},\n{{#each route.payloadTypes}} {{id}},\n{{/each}}\n{{#each route.responseTypes}} {{id}},\n{{/each}}\n} from \"{{ createImport \"libApi\" route.name \"types\" }}\";\n\nexport { ValidationError } from \"@kosmojs/api/errors\";\n\nexport type ParamsT = [\n {{#each route.params.schema}}\n {{#if isRest}}\n ...{{const}}: Array<string | number>\n {{else}}\n {{const}}{{#unless isRequired}}?{{/unless}}: {{../route.params.id}}[\"{{name}}\"],\n {{/if}}\n {{/each}}\n];\n\nexport type PayloadT = {\n {{#each route.payloadTypes}}\n {{method}}: {{id}};\n {{/each}}\n}\n\nexport type ResponseT = {\n {{#each route.responseTypes}}\n {{method}}: {{id}};\n {{/each}}\n}\n\nconst paramsMapper = (params: ParamsT) => {\n return {\n {{#each paramsMapper}}\n \"{{name}}\": params[{{idx}}],\n {{/each}}\n }\n}\n\nconst pathTokens: Array<[ path: string, param?: { isRest: boolean } ]> = [\n {{#each route.pathTokens}}\n [\n \"{{path}}\",{{#if param}}\n { isRest: {{#if param.isRest}}true{{else}}false{{/if}} }{{/if}}\n ],\n {{/each}}\n];\n\nconst parametrize = (params: ParamsT) => {\n const paramsClone = structuredClone(params);\n return pathTokens\n .flatMap(([ path, param ]) => {\n if (param?.isRest) {\n return paramsClone;\n }\n if (param) {\n return paramsClone.splice(0, 1);\n }\n return [path];\n })\n .join(\"/\");\n}\n\nconst fetchApi = fetchFactory(join(baseurl, apiurl), { stringify });\n\n{{#each routeMethods}}\nexport const {{method}} = (\n _params{{#if ../route.optionalParams}}?{{/if}}: MaybeWrapped<ParamsT>,\n {{#if payloadType}}\n _payload{{#if payloadType.isOptional}}?{{/if}}: MaybeWrapped<\n PayloadT[\"{{method}}\"]\n >,\n {{else}}\n _payload?: unknown,\n {{/if}}\n): Promise<\n {{#if responseType}}\n ResponseT[\"{{method}}\"]\n {{else}}\n unknown\n {{/if}}\n> => {\n const [params, payload] = [unwrap(_params || []), unwrap(_payload || {})];\n if (validationSchemas.params) {\n validationSchemas.params.validate(paramsMapper(params as never));\n }\n if (validationSchemas.payload?.{{method}}) {\n validationSchemas.payload.{{method}}.validate(payload);\n }\n return fetchApi.{{method}}(\n parametrize(params as never),\n payload,\n )\n};\n{{/each}}\n\nexport const path = (\n params: ParamsT,\n query?: Record<string, unknown>,\n) => {\n const path = join(\n baseurl,\n {{#if route.base}}\"{{route.base}}\"{{else}}apiurl{{/if}},\n parametrize(params)\n );\n return query\n ? [ path, stringify(query) ].join(\"?\")\n : path;\n}\n\nexport const href = (\n host: HostOpt,\n params: ParamsT,\n query?: Record<string, unknown>,\n) => createHost(host) + path(params, query);\n\nexport default {\n {{#each routeMethods}}\n {{method}},\n {{/each}}\n path,\n href,\n validationSchemas,\n};\n", "export type MaybeWrapped<T> = T;\nexport const unwrap = <T>(data: T) => data;\n", "import type { GeneratorConstructor } from \"@kosmojs/dev\";\n\nimport { factory } from \"./factory\";\n\nexport default (): GeneratorConstructor => {\n return {\n name: \"Fetch\",\n kind: \"fetch\",\n moduleImport: import.meta.filename,\n moduleConfig: undefined,\n factory,\n };\n};\n"],
5
+ "mappings": ";AAAA;AAAA,EAEE;AAAA,EAEA;AAAA,EACA;AAAA,OACK;;;ACNP;;;ACAA;;;ACAA;;;AHYO,IAAM,UAA4B,OAAO;AAAA,EAC9C;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,QAAM,EAAE,YAAY,mBAAmB,IAAI,aAAa;AAAA,IACtD;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,EAAE,aAAa,IAAI,cAAc;AAAA,IACrC;AAAA,IACA,SAAS;AAAA,MACP,cAAc;AAAA,IAChB;AAAA,EACF,CAAC;AAKD,QAAM,aAAa,WAAW,IAAI,WAAW,GAAG,gBAAW,CAAC,CAAC;AAE7D,QAAM,mBAAmB,OACvB,SACA,mBACG;AACH,UAAM,SAAS,QACZ,QAAQ,CAAC,EAAE,MAAM,MAAM,MAAO,SAAS,aAAa,CAAC,KAAK,IAAI,CAAC,CAAE,EACjE,KAAK,UAAU;AAElB,UAAM,aAAa,GAAG,WAAW,MAAM,CAAC,OAAO,eAAU,EAAE,OAAO,CAAC;AAEnE,eAAW,EAAE,MAAM,MAAM,KAAK,gBAAgB;AAC5C,UAAI,SAAS,YAAY;AACvB,cAAM,eAAe,MAAM,QAAQ,IAAI,CAAC,WAAW;AACjD,gBAAM,cAAc,MAAM,aAAa;AAAA,YACrC,CAAC,MAAM,EAAE,WAAW;AAAA,UACtB;AACA,gBAAM,eAAe,MAAM,cAAc;AAAA,YACvC,CAAC,MAAM,EAAE,WAAW;AAAA,UACtB;AACA,iBAAO;AAAA,YACL;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF,CAAC;AAED,cAAM,eAAe,MAAM,OAAO,OAAO,IAAI,CAAC,EAAE,KAAK,GAAG,SAAS;AAAA,UAC/D;AAAA,UACA;AAAA,QACF,EAAE;AAEF,cAAM,aAAa,WAAW,MAAM,MAAM,IAAI,GAAG,eAAU;AAAA,UACzD,OAAO;AAAA,UACP;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM,MAAM,SAAS,OAAO;AAC1B,YAAM;AAAA,QACJ;AAAA;AAAA;AAAA;AAAA;AAAA,QAKA,QACI,QAAQ,OAAO,CAAC,EAAE,MAAM,MAAM,MAAM;AAClC,iBAAO,MAAM,SAAS,WAClB,SAAS,aACP,MAAM,iBAAiB,MAAM,OAC7B,QACF;AAAA,QACN,CAAC,IACD;AAAA,MACN;AAAA,IAGF;AAAA,IACA,MAAM,MAAM,SAAS;AACnB,YAAM,iBAAiB,SAAS,OAAO;AAAA,IACzC;AAAA,EACF;AACF;;;AI/FA,IAAO,gBAAQ,MAA4B;AACzC,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM;AAAA,IACN,cAAc,YAAY;AAAA,IAC1B,cAAc;AAAA,IACd;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,2 +1,2 @@
1
- import { type GeneratorFactory } from "@kosmojs/devlib";
1
+ import { type GeneratorFactory } from "@kosmojs/dev";
2
2
  export declare const factory: GeneratorFactory;
@@ -1,3 +1,3 @@
1
- import type { GeneratorConstructor } from "@kosmojs/devlib";
1
+ import type { GeneratorConstructor } from "@kosmojs/dev";
2
2
  declare const _default: () => GeneratorConstructor;
3
3
  export default _default;