@pikku/cli 0.6.6 → 0.6.9

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/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @pikku/cli
2
2
 
3
+ ## 0.6.9
4
+
5
+ ### Patch Changes
6
+
7
+ - 7e7ec0c: chore: show packageVersion in cli header
8
+
9
+ ## 0.6.8
10
+
11
+ ### Patch Changes
12
+
13
+ - bdcc89a: feat: adding intro logo to cli based commands
14
+
15
+ ## 0.6.7
16
+
17
+ ### Patch Changes
18
+
19
+ - 7859b28: breaking: changing overrides for addRoute to wrap instead due to random conflict override errors
20
+ - 269a532: fix: fixing some typing issues
21
+ - Updated dependencies [7859b28]
22
+ - Updated dependencies [269a532]
23
+ - @pikku/core@0.6.11
24
+
3
25
  ## 0.6.6
4
26
 
5
27
  ### Patch Changes
package/bin/pikku-all.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Command } from 'commander'
2
2
  import {
3
3
  getFileImportRelativePath,
4
+ logInfo,
4
5
  logPikkuLogo,
5
6
  PikkuCLIOptions,
6
7
  writeFileInDir,
@@ -45,7 +46,7 @@ export const action = async (options: PikkuCLIOptions): Promise<void> => {
45
46
 
46
47
  // This is needed since the addRoutes function will add the routes to the visitState
47
48
  if (!typesDeclarationFileExists) {
48
- console.log(`\x1b[34m• Type file first created, inspecting again...\x1b[0m`)
49
+ logInfo(`• Type file first created, inspecting again...\x1b[0m`)
49
50
  visitState = await inspectorGlob(
50
51
  cliConfig.rootDir,
51
52
  cliConfig.routeDirectories
@@ -79,9 +80,7 @@ export const action = async (options: PikkuCLIOptions): Promise<void> => {
79
80
  await pikkuNext(cliConfig, visitState, options)
80
81
 
81
82
  if (cliConfig.openAPI) {
82
- console.log(
83
- `\x1b[34m• OpenAPI requires a reinspection to pickup new generated types..\x1b[0m`
84
- )
83
+ logInfo(`• OpenAPI requires a reinspection to pickup new generated types..`)
85
84
  visitState = await inspectorGlob(
86
85
  cliConfig.rootDir,
87
86
  cliConfig.routeDirectories
@@ -17,10 +17,10 @@ export const pikkuFetch = async ({
17
17
  await logCommandInfoAndTime(
18
18
  'Generating fetch wrapper',
19
19
  'Generated fetch wrapper',
20
- [fetchFile === undefined, 'fetchFile is required in pikku config'],
20
+ [fetchFile === undefined, "fetchFile isn't set in the pikku config"],
21
21
  async () => {
22
22
  if (!fetchFile) {
23
- throw new Error('fetchFile is required in pikku config')
23
+ throw new Error("fetchFile is isn't set in the pikku config")
24
24
  }
25
25
 
26
26
  const routesMapDeclarationPath = getFileImportRelativePath(
@@ -17,10 +17,13 @@ export const pikkuWebSocket = async ({
17
17
  await logCommandInfoAndTime(
18
18
  'Generating websocket wrapper',
19
19
  'Generated websocket wrapper',
20
- [websocketFile === undefined, 'websocketFile is required in pikku config'],
20
+ [
21
+ websocketFile === undefined,
22
+ "websocketFile isn't set in the pikku config",
23
+ ],
21
24
  async () => {
22
25
  if (!websocketFile) {
23
- throw new Error('fetchFile is required in pikku config')
26
+ throw new Error("fetchFile is isn't set in the pikku config")
24
27
  }
25
28
 
26
29
  const channelsMapDeclarationPath = getFileImportRelativePath(
@@ -1,4 +1,4 @@
1
- import { getFileImportRelativePath, logPikkuLogo, writeFileInDir, } from '../src/utils.js';
1
+ import { getFileImportRelativePath, logInfo, logPikkuLogo, writeFileInDir, } from '../src/utils.js';
2
2
  import { getPikkuCLIConfig } from '../src/pikku-cli-config.js';
3
3
  import { pikkuRoutes } from './pikku-routes.js';
4
4
  import { pikkuFunctionTypes } from './pikku-function-types.js';
@@ -28,7 +28,7 @@ export const action = async (options) => {
28
28
  await pikkuFunctionTypes(cliConfig, options, visitState);
29
29
  // This is needed since the addRoutes function will add the routes to the visitState
30
30
  if (!typesDeclarationFileExists) {
31
- console.log(`\x1b[34m• Type file first created, inspecting again...\x1b[0m`);
31
+ logInfo(`• Type file first created, inspecting again...\x1b[0m`);
32
32
  visitState = await inspectorGlob(cliConfig.rootDir, cliConfig.routeDirectories);
33
33
  }
34
34
  const routes = await pikkuRoutes(cliConfig, visitState);
@@ -53,7 +53,7 @@ export const action = async (options) => {
53
53
  }
54
54
  await pikkuNext(cliConfig, visitState, options);
55
55
  if (cliConfig.openAPI) {
56
- console.log(`\x1b[34m• OpenAPI requires a reinspection to pickup new generated types..\x1b[0m`);
56
+ logInfo(`• OpenAPI requires a reinspection to pickup new generated types..`);
57
57
  visitState = await inspectorGlob(cliConfig.rootDir, cliConfig.routeDirectories);
58
58
  await pikkuOpenAPI(cliConfig, visitState);
59
59
  }
@@ -2,9 +2,9 @@ import { getFileImportRelativePath, logCommandInfoAndTime, logPikkuLogo, writeFi
2
2
  import { getPikkuCLIConfig } from '../src/pikku-cli-config.js';
3
3
  import { serializeFetchWrapper } from '../src/http/serialize-fetch-wrapper.js';
4
4
  export const pikkuFetch = async ({ fetchFile, routesMapDeclarationFile, packageMappings, }) => {
5
- await logCommandInfoAndTime('Generating fetch wrapper', 'Generated fetch wrapper', [fetchFile === undefined, 'fetchFile is required in pikku config'], async () => {
5
+ await logCommandInfoAndTime('Generating fetch wrapper', 'Generated fetch wrapper', [fetchFile === undefined, "fetchFile isn't set in the pikku config"], async () => {
6
6
  if (!fetchFile) {
7
- throw new Error('fetchFile is required in pikku config');
7
+ throw new Error("fetchFile is isn't set in the pikku config");
8
8
  }
9
9
  const routesMapDeclarationPath = getFileImportRelativePath(fetchFile, routesMapDeclarationFile, packageMappings);
10
10
  const content = [serializeFetchWrapper(routesMapDeclarationPath)];
@@ -2,9 +2,12 @@ import { getFileImportRelativePath, logCommandInfoAndTime, logPikkuLogo, writeFi
2
2
  import { getPikkuCLIConfig } from '../src/pikku-cli-config.js';
3
3
  import { serializeWebsocketWrapper } from '../src/channels/serialize-websocket-wrapper.js';
4
4
  export const pikkuWebSocket = async ({ websocketFile, channelsMapDeclarationFile, packageMappings, }) => {
5
- await logCommandInfoAndTime('Generating websocket wrapper', 'Generated websocket wrapper', [websocketFile === undefined, 'websocketFile is required in pikku config'], async () => {
5
+ await logCommandInfoAndTime('Generating websocket wrapper', 'Generated websocket wrapper', [
6
+ websocketFile === undefined,
7
+ "websocketFile isn't set in the pikku config",
8
+ ], async () => {
6
9
  if (!websocketFile) {
7
- throw new Error('fetchFile is required in pikku config');
10
+ throw new Error("fetchFile is isn't set in the pikku config");
8
11
  }
9
12
  const channelsMapDeclarationPath = getFileImportRelativePath(websocketFile, channelsMapDeclarationFile, packageMappings);
10
13
  const content = [serializeWebsocketWrapper(channelsMapDeclarationPath)];
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@pikku/cli",
3
+ "version": "0.6.9",
4
+ "author": "yasser.fadl@gmail.com",
5
+ "license": "MIT",
6
+ "bin": {
7
+ "pikku": "dist/bin/pikku.js"
8
+ },
9
+ "type": "module",
10
+ "main": "dist/bin/pikku.js",
11
+ "module": "dist/bin/pikku.js",
12
+ "scripts": {
13
+ "tsc": "tsc",
14
+ "build:esm": "tsc -b",
15
+ "schema": "ts-json-schema-generator -o cli.schema.json --path 'src/pikku-cli-config.ts' --type 'PikkuCLIConfig'",
16
+ "build": "yarn build:esm && yarn schema",
17
+ "ncu": "npx npm-check-updates -x '/.*glob.*/'",
18
+ "release": "yarn build && npm test",
19
+ "test": "bash run-tests.sh",
20
+ "test:watch": "bash run-tests.sh --watch",
21
+ "test:coverage": "bash run-tests.sh --coverage"
22
+ },
23
+ "dependencies": {
24
+ "@openapi-contrib/json-schema-to-openapi-schema": "^3.0.2",
25
+ "@pikku/core": "^0.6.11",
26
+ "@pikku/inspector": "^0.6.2",
27
+ "@types/cookie": "^0.6.0",
28
+ "@types/uuid": "^10.0.0",
29
+ "chalk": "^5.4.1",
30
+ "commander": "^12",
31
+ "glob": "^10",
32
+ "path-to-regexp": "^8.2.0",
33
+ "ts-json-schema-generator": "^2.3.0",
34
+ "typescript": "^5.6",
35
+ "yaml": "^2.6.0"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^22.7.8"
39
+ },
40
+ "engines": {
41
+ "node": ">=18"
42
+ }
43
+ }
@@ -1,4 +1,5 @@
1
1
  import { serializeImportMap } from '../core/serialize-import-map.js';
2
+ import { generateCustomTypes } from '../http/serialize-typed-route-map.js';
2
3
  export const serializeTypedChannelsMap = (relativeToPath, packageMappings, typesMap, channelsMeta) => {
3
4
  const { channels, requiredTypes } = generateChannels(channelsMeta);
4
5
  typesMap.customTypes.forEach(({ references }) => {
@@ -7,11 +8,13 @@ export const serializeTypedChannelsMap = (relativeToPath, packageMappings, types
7
8
  }
8
9
  });
9
10
  const imports = serializeImportMap(relativeToPath, packageMappings, typesMap, requiredTypes);
11
+ const serializedCustomTypes = generateCustomTypes(typesMap, requiredTypes);
10
12
  return `/**
11
13
  * This provides the structure needed for TypeScript to be aware of channels
12
14
  */
13
15
 
14
16
  ${imports}
17
+ ${serializedCustomTypes}
15
18
 
16
19
  interface ChannelHandler<I, O> {
17
20
  input: I;
@@ -67,7 +70,7 @@ function generateChannels(channelsMeta) {
67
70
  for (const [key, methods] of Object.entries(routes)) {
68
71
  routesStr += ` readonly ${key}: {\n`;
69
72
  for (const [method, handler] of Object.entries(methods)) {
70
- routesStr += ` readonly ${method}: ChannelHandler<${formatTypeArray(handler.inputTypes)}, ${formatTypeArray(handler.outputTypes)}>,\n`;
73
+ routesStr += ` readonly ${method}: ChannelHandler<${formatTypeArray(handler.inputTypes)}, ${formatTypeArray(handler.outputTypes) || 'never'}>,\n`;
71
74
  }
72
75
  routesStr += ' },\n';
73
76
  }
@@ -6,10 +6,10 @@ export const serializePikkuTypes = (userSessionTypeImport, userSessionTypeName,
6
6
  * This is used to provide the application types in the typescript project
7
7
  */
8
8
 
9
- import { CoreAPIFunction, CoreAPIFunctionSessionless, CoreAPIPermission, MakeRequired } from '@pikku/core'
10
- import { CoreHTTPFunctionRoute, AssertRouteParams } from '@pikku/core/http'
11
- import { CoreScheduledTask } from '@pikku/core/scheduler'
12
- import { CoreAPIChannel, CoreChannelConnection, CoreChannelDisconnection, CoreChannelMessage, PikkuChannel } from '@pikku/core/channel'
9
+ import { CoreAPIFunction, CoreAPIFunctionSessionless, CoreAPIPermission } from '@pikku/core'
10
+ import { CoreHTTPFunctionRoute, AssertRouteParams, addRoute as addCoreHTTP } from '@pikku/core/http'
11
+ import { CoreScheduledTask, addScheduledTask as addCoreScheduledTask } from '@pikku/core/scheduler'
12
+ import { CoreAPIChannel, PikkuChannel, addChannel as addCoreChannel } from '@pikku/core/channel'
13
13
 
14
14
  ${userSessionTypeImport}
15
15
  ${sessionServicesTypeImport}
@@ -20,29 +20,27 @@ export type APIFunctionSessionless<In = unknown, Out = never, RequiredServices =
20
20
  export type APIFunction<In = unknown, Out = never, RequiredServices = ${servicesTypeName}> = CoreAPIFunction<In, Out, RequiredServices, ${userSessionTypeName}>
21
21
  type APIRoute<In, Out, Route extends string> = CoreHTTPFunctionRoute<In, Out, Route, APIFunction<In, Out>, APIFunctionSessionless<In, Out>, APIPermission<In>>
22
22
 
23
- export type ChannelConnection<Out = never, ChannelData = unknown, RequiredServices extends ${servicesTypeName} = ${servicesTypeName}> = (services: MakeRequired<Services, 'eventHub'>, channel: PikkuChannel<${userSessionTypeName}, ChannelData, Out>) => Promise<void>
24
- export type ChannelDisconnection<ChannelData = unknown, RequiredServices extends ${servicesTypeName} = ${servicesTypeName}> = (services: MakeRequired<Services, 'eventHub'>, channel: PikkuChannel<${userSessionTypeName}, ChannelData, never>) => Promise<void>
25
- export type ChannelMessage<In, Out = never, ChannelData = unknown, RequiredServices extends ${servicesTypeName} = ${servicesTypeName}> = (services: MakeRequired<Services, 'eventHub'>, channel: PikkuChannel<${userSessionTypeName}, ChannelData, Out>, data: In) => Promise<Out | void>
26
- type APIChannel<ChannelData, Channel extends string, In extends unknown, Out extends unknown> = CoreAPIChannel<ChannelData, Channel, ChannelConnection, ChannelDisconnection, ChannelMessage<In, Out, ChannelData>>
23
+ export type ChannelConnection<Out = unknown, ChannelData = unknown, RequiredServices extends ${servicesTypeName} = ${servicesTypeName}> = (services: RequiredServices, channel: PikkuChannel<${userSessionTypeName}, ChannelData, Out>) => Promise<void>
24
+ export type ChannelDisconnection<ChannelData = unknown, RequiredServices extends ${servicesTypeName} = ${servicesTypeName}> = (services: RequiredServices, channel: PikkuChannel<${userSessionTypeName}, ChannelData, never>) => Promise<void>
25
+ export type ChannelMessage<In, Out = unknown, ChannelData = unknown, RequiredServices extends ${servicesTypeName} = ${servicesTypeName}> = (services: RequiredServices, channel: PikkuChannel<${userSessionTypeName}, ChannelData, Out>, data: In) => Promise<Out | void>
26
+ type APIChannel<ChannelData, Channel extends string> = CoreAPIChannel<ChannelData, Channel, ChannelConnection, ChannelDisconnection, ChannelMessage<any, any, ChannelData>, ChannelMessage<any, any, ChannelData>, APIPermission>
27
27
 
28
28
  type ScheduledTask = CoreScheduledTask<APIFunctionSessionless<void, void>, ${userSessionTypeName}>
29
29
 
30
- declare module "@pikku/core" {
31
- // type APIPermission<In = unknown, RequiredServices = ${servicesTypeName}> = CoreAPIPermission<In, RequiredServices, ${userSessionTypeName}>
32
- // type APIFunction = <In = unknown, Out = never, RequiredServices = ${servicesTypeName}> = CoreAPIFunction<In, Out, RequiredServices, ${userSessionTypeName}>
33
- // type APIFunctionSessionless = <In = unknown, Out = never, RequiredServices = ${servicesTypeName}> = CoreAPIFunctionSessionless<In, Out, RequiredServices, ${userSessionTypeName}>
34
-
35
- function addChannel<ChannelData, Channel extends string>(
36
- channel: APIChannel<ChannelData, Channel> & AssertRouteParams<ChannelData, Channel>
37
- ): void;
30
+ export const addChannel = <ChannelData, Channel extends string>(
31
+ channel: APIChannel<ChannelData, Channel> & AssertRouteParams<ChannelData, Channel>
32
+ ) => {
33
+ addCoreChannel(channel as any) // TODO
34
+ }
38
35
 
39
- function addRoute<In, Out, Route extends string>(
40
- route: APIRoute<In, Out, Route> & AssertRouteParams<In, Route>
41
- ): void;
36
+ export const addRoute = <In, Out, Route extends string>(
37
+ route: APIRoute<In, Out, Route> & AssertRouteParams<In, Route>
38
+ ) => {
39
+ addCoreHTTP(route)
40
+ }
42
41
 
43
- function addScheduledTask(
44
- task: ScheduledTask
45
- ): void;
42
+ export const addScheduledTask = (task: ScheduledTask) => {
43
+ addCoreScheduledTask(task as any) // TODO
46
44
  }
47
45
  `;
48
46
  };
@@ -1,3 +1,4 @@
1
1
  import { HTTPRoutesMeta } from '@pikku/core/http';
2
2
  import { MetaInputTypes, TypesMap } from '@pikku/inspector';
3
3
  export declare const serializeTypedRoutesMap: (relativeToPath: string, packageMappings: Record<string, string>, typesMap: TypesMap, routesMeta: HTTPRoutesMeta, metaTypes: MetaInputTypes) => string;
4
+ export declare function generateCustomTypes(typesMap: TypesMap, requiredTypes: Set<string>): string;
@@ -30,7 +30,7 @@ export type RoutesWithMethod<Method extends string> = {
30
30
  }[keyof RoutesMap];
31
31
  `;
32
32
  };
33
- function generateCustomTypes(typesMap, requiredTypes) {
33
+ export function generateCustomTypes(typesMap, requiredTypes) {
34
34
  return `
35
35
  // Custom types are those that are defined directly within generics
36
36
  // or are broken into simpler types
@@ -61,7 +61,7 @@ const _getPikkuCLIConfig = async (configFile = undefined, requiredFields, exitPr
61
61
  result.channelsFile = join(result.outDir, 'pikku-channels.gen.ts');
62
62
  }
63
63
  if (!result.typesDeclarationFile) {
64
- result.typesDeclarationFile = join(result.outDir, 'pikku-types.gen.d.ts');
64
+ result.typesDeclarationFile = join(result.outDir, 'pikku-types.gen.ts');
65
65
  }
66
66
  if (!result.routesMapDeclarationFile) {
67
67
  result.routesMapDeclarationFile = join(result.outDir, 'pikku-routes-map.gen.d.ts');
@@ -1,5 +1,5 @@
1
1
  import { createGenerator, RootlessError } from 'ts-json-schema-generator';
2
- import { writeFileInDir } from '../utils.js';
2
+ import { logInfo, writeFileInDir } from '../utils.js';
3
3
  import { mkdir, writeFile } from 'fs/promises';
4
4
  export async function generateSchemas(tsconfig, typesMap, routesMeta) {
5
5
  const schemasSet = new Set(typesMap.customTypes.keys());
@@ -34,7 +34,7 @@ export async function generateSchemas(tsconfig, typesMap, routesMeta) {
34
34
  catch (e) {
35
35
  // Ignore rootless errors
36
36
  if (e instanceof RootlessError) {
37
- console.log('Error generating schema since it has no root:', schema);
37
+ console.error('Error generating schema since it has no root:', schema);
38
38
  return;
39
39
  }
40
40
  throw e;
@@ -56,7 +56,7 @@ export async function saveSchemas(schemaParentDir, schemas, typesMap, routesMeta
56
56
  ...typesMap.customTypes.keys(),
57
57
  ]);
58
58
  if (desiredSchemas.size === 0) {
59
- console.log(`\x1b[34m• Skipping schemas since none found.\x1b[0m`);
59
+ logInfo(`• Skipping schemas since none found.\x1b[0m`);
60
60
  return;
61
61
  }
62
62
  await mkdir(`${schemaParentDir}/schemas`, { recursive: true });
@@ -1,4 +1,7 @@
1
1
  import { InspectorState } from '@pikku/inspector';
2
+ export declare const logPrimary: (message: string) => void;
3
+ export declare const logSuccess: (message: string) => void;
4
+ export declare const logInfo: (message: string) => void;
2
5
  export declare const getFileImportRelativePath: (from: string, to: string, packageMappings: Record<string, string>) => string;
3
6
  interface Meta {
4
7
  file: string;
package/dist/src/utils.js CHANGED
@@ -1,6 +1,17 @@
1
1
  // import packageInfo from '../package.json'
2
2
  import { relative, dirname } from 'path';
3
3
  import { mkdir, writeFile } from 'fs/promises';
4
+ import chalk from 'chalk';
5
+ import packageJson from '../package.json';
6
+ export const logPrimary = (message) => {
7
+ console.log(chalk.green(message));
8
+ };
9
+ export const logSuccess = (message) => {
10
+ console.log(chalk.green(message));
11
+ };
12
+ export const logInfo = (message) => {
13
+ console.log(chalk.blue(message));
14
+ };
4
15
  export const getFileImportRelativePath = (from, to, packageMappings) => {
5
16
  let filePath = relative(dirname(from), to);
6
17
  if (!/^\.+\//.test(filePath)) {
@@ -86,21 +97,30 @@ export const writeFileInDir = async (path, content, ignoreModifyComment = false)
86
97
  }
87
98
  await mkdir(dirname(path), { recursive: true });
88
99
  await writeFile(path, content, 'utf-8');
89
- console.log(`\x1b[32m✓ File written to ${path}\x1b[0m`);
100
+ logSuccess(`✓ File written to ${path}`);
90
101
  };
91
102
  export const logCommandInfoAndTime = async (commandStart, commandEnd, [skipCondition, skipMessage = 'none found'], callback) => {
92
103
  if (skipCondition === true) {
93
- console.log(`\x1b[34m• Skipping ${commandStart} since ${skipMessage}.\x1b[0m`);
104
+ logInfo(`• Skipping ${commandStart} since ${skipMessage}.`);
94
105
  return false;
95
106
  }
96
107
  const start = Date.now();
97
- console.log(`\x1b[34m• ${commandStart}...\x1b[0m`);
108
+ chalk.blue(`• ${commandStart}...`);
98
109
  await callback();
99
- console.log(`\x1b[32m✓ ${commandEnd} in ${Date.now() - start}ms.\x1b[0m`);
110
+ logSuccess(`✓ ${commandEnd} in ${Date.now() - start}ms.`);
100
111
  return true;
101
112
  };
113
+ const logo = `
114
+ ______ _ _ _
115
+ (_____ (_) | | |
116
+ _____) )| | _| | _ _ _
117
+ | ____/ | |_/ ) |_/ ) | | |
118
+ | | | | _ (| _ (| |_| |
119
+ |_| |_|_| \_)_| \_)____/
120
+ `;
102
121
  export const logPikkuLogo = () => {
103
- console.log(`\x1b[33m⚙️ PIKKU CLI ⚙️\n-------------------\x1b[0m`);
122
+ logPrimary(logo);
123
+ logPrimary(`⚙️ Welcome to the Pikku CLI (v${packageJson})\n`);
104
124
  };
105
125
  // TODO: add version back in once the ESM dust settles
106
126
  export const DO_NOT_MODIFY_COMMENT = `/**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/cli",
3
- "version": "0.6.6",
3
+ "version": "0.6.9",
4
4
  "author": "yasser.fadl@gmail.com",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -14,7 +14,7 @@
14
14
  "build:esm": "tsc -b",
15
15
  "schema": "ts-json-schema-generator -o cli.schema.json --path 'src/pikku-cli-config.ts' --type 'PikkuCLIConfig'",
16
16
  "build": "yarn build:esm && yarn schema",
17
- "ncu": "ncu -x '/.*glob.*/'",
17
+ "ncu": "npx npm-check-updates -x '/.*glob.*/'",
18
18
  "release": "yarn build && npm test",
19
19
  "test": "bash run-tests.sh",
20
20
  "test:watch": "bash run-tests.sh --watch",
@@ -22,10 +22,11 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@openapi-contrib/json-schema-to-openapi-schema": "^3.0.2",
25
- "@pikku/core": "^0.6.7",
25
+ "@pikku/core": "^0.6.11",
26
26
  "@pikku/inspector": "^0.6.2",
27
27
  "@types/cookie": "^0.6.0",
28
28
  "@types/uuid": "^10.0.0",
29
+ "chalk": "^5.4.1",
29
30
  "commander": "^12",
30
31
  "glob": "^10",
31
32
  "path-to-regexp": "^8.2.0",
@@ -1,6 +1,7 @@
1
1
  import { ChannelsMeta } from '@pikku/core/channel'
2
2
  import { serializeImportMap } from '../core/serialize-import-map.js'
3
3
  import { TypesMap } from '@pikku/inspector'
4
+ import { generateCustomTypes } from '../http/serialize-typed-route-map.js'
4
5
 
5
6
  export const serializeTypedChannelsMap = (
6
7
  relativeToPath: string,
@@ -20,11 +21,13 @@ export const serializeTypedChannelsMap = (
20
21
  typesMap,
21
22
  requiredTypes
22
23
  )
24
+ const serializedCustomTypes = generateCustomTypes(typesMap, requiredTypes)
23
25
  return `/**
24
26
  * This provides the structure needed for TypeScript to be aware of channels
25
27
  */
26
28
 
27
29
  ${imports}
30
+ ${serializedCustomTypes}
28
31
 
29
32
  interface ChannelHandler<I, O> {
30
33
  input: I;
@@ -106,7 +109,7 @@ function generateChannels(channelsMeta: ChannelsMeta) {
106
109
  for (const [method, handler] of Object.entries(methods)) {
107
110
  routesStr += ` readonly ${method}: ChannelHandler<${formatTypeArray(
108
111
  handler.inputTypes
109
- )}, ${formatTypeArray(handler.outputTypes)}>,\n`
112
+ )}, ${formatTypeArray(handler.outputTypes) || 'never'}>,\n`
110
113
  }
111
114
  routesStr += ' },\n'
112
115
  }
@@ -11,10 +11,10 @@ export const serializePikkuTypes = (
11
11
  * This is used to provide the application types in the typescript project
12
12
  */
13
13
 
14
- import { CoreAPIFunction, CoreAPIFunctionSessionless, CoreAPIPermission, MakeRequired } from '@pikku/core'
15
- import { CoreHTTPFunctionRoute, AssertRouteParams } from '@pikku/core/http'
16
- import { CoreScheduledTask } from '@pikku/core/scheduler'
17
- import { CoreAPIChannel, CoreChannelConnection, CoreChannelDisconnection, CoreChannelMessage, PikkuChannel } from '@pikku/core/channel'
14
+ import { CoreAPIFunction, CoreAPIFunctionSessionless, CoreAPIPermission } from '@pikku/core'
15
+ import { CoreHTTPFunctionRoute, AssertRouteParams, addRoute as addCoreHTTP } from '@pikku/core/http'
16
+ import { CoreScheduledTask, addScheduledTask as addCoreScheduledTask } from '@pikku/core/scheduler'
17
+ import { CoreAPIChannel, PikkuChannel, addChannel as addCoreChannel } from '@pikku/core/channel'
18
18
 
19
19
  ${userSessionTypeImport}
20
20
  ${sessionServicesTypeImport}
@@ -25,29 +25,27 @@ export type APIFunctionSessionless<In = unknown, Out = never, RequiredServices =
25
25
  export type APIFunction<In = unknown, Out = never, RequiredServices = ${servicesTypeName}> = CoreAPIFunction<In, Out, RequiredServices, ${userSessionTypeName}>
26
26
  type APIRoute<In, Out, Route extends string> = CoreHTTPFunctionRoute<In, Out, Route, APIFunction<In, Out>, APIFunctionSessionless<In, Out>, APIPermission<In>>
27
27
 
28
- export type ChannelConnection<Out = never, ChannelData = unknown, RequiredServices extends ${servicesTypeName} = ${servicesTypeName}> = (services: MakeRequired<Services, 'eventHub'>, channel: PikkuChannel<${userSessionTypeName}, ChannelData, Out>) => Promise<void>
29
- export type ChannelDisconnection<ChannelData = unknown, RequiredServices extends ${servicesTypeName} = ${servicesTypeName}> = (services: MakeRequired<Services, 'eventHub'>, channel: PikkuChannel<${userSessionTypeName}, ChannelData, never>) => Promise<void>
30
- export type ChannelMessage<In, Out = never, ChannelData = unknown, RequiredServices extends ${servicesTypeName} = ${servicesTypeName}> = (services: MakeRequired<Services, 'eventHub'>, channel: PikkuChannel<${userSessionTypeName}, ChannelData, Out>, data: In) => Promise<Out | void>
31
- type APIChannel<ChannelData, Channel extends string, In extends unknown, Out extends unknown> = CoreAPIChannel<ChannelData, Channel, ChannelConnection, ChannelDisconnection, ChannelMessage<In, Out, ChannelData>>
28
+ export type ChannelConnection<Out = unknown, ChannelData = unknown, RequiredServices extends ${servicesTypeName} = ${servicesTypeName}> = (services: RequiredServices, channel: PikkuChannel<${userSessionTypeName}, ChannelData, Out>) => Promise<void>
29
+ export type ChannelDisconnection<ChannelData = unknown, RequiredServices extends ${servicesTypeName} = ${servicesTypeName}> = (services: RequiredServices, channel: PikkuChannel<${userSessionTypeName}, ChannelData, never>) => Promise<void>
30
+ export type ChannelMessage<In, Out = unknown, ChannelData = unknown, RequiredServices extends ${servicesTypeName} = ${servicesTypeName}> = (services: RequiredServices, channel: PikkuChannel<${userSessionTypeName}, ChannelData, Out>, data: In) => Promise<Out | void>
31
+ type APIChannel<ChannelData, Channel extends string> = CoreAPIChannel<ChannelData, Channel, ChannelConnection, ChannelDisconnection, ChannelMessage<any, any, ChannelData>, ChannelMessage<any, any, ChannelData>, APIPermission>
32
32
 
33
33
  type ScheduledTask = CoreScheduledTask<APIFunctionSessionless<void, void>, ${userSessionTypeName}>
34
34
 
35
- declare module "@pikku/core" {
36
- // type APIPermission<In = unknown, RequiredServices = ${servicesTypeName}> = CoreAPIPermission<In, RequiredServices, ${userSessionTypeName}>
37
- // type APIFunction = <In = unknown, Out = never, RequiredServices = ${servicesTypeName}> = CoreAPIFunction<In, Out, RequiredServices, ${userSessionTypeName}>
38
- // type APIFunctionSessionless = <In = unknown, Out = never, RequiredServices = ${servicesTypeName}> = CoreAPIFunctionSessionless<In, Out, RequiredServices, ${userSessionTypeName}>
39
-
40
- function addChannel<ChannelData, Channel extends string>(
41
- channel: APIChannel<ChannelData, Channel> & AssertRouteParams<ChannelData, Channel>
42
- ): void;
35
+ export const addChannel = <ChannelData, Channel extends string>(
36
+ channel: APIChannel<ChannelData, Channel> & AssertRouteParams<ChannelData, Channel>
37
+ ) => {
38
+ addCoreChannel(channel as any) // TODO
39
+ }
43
40
 
44
- function addRoute<In, Out, Route extends string>(
45
- route: APIRoute<In, Out, Route> & AssertRouteParams<In, Route>
46
- ): void;
41
+ export const addRoute = <In, Out, Route extends string>(
42
+ route: APIRoute<In, Out, Route> & AssertRouteParams<In, Route>
43
+ ) => {
44
+ addCoreHTTP(route)
45
+ }
47
46
 
48
- function addScheduledTask(
49
- task: ScheduledTask
50
- ): void;
47
+ export const addScheduledTask = (task: ScheduledTask) => {
48
+ addCoreScheduledTask(task as any) // TODO
51
49
  }
52
50
  `
53
51
  }
@@ -46,7 +46,10 @@ export type RoutesWithMethod<Method extends string> = {
46
46
  `
47
47
  }
48
48
 
49
- function generateCustomTypes(typesMap: TypesMap, requiredTypes: Set<string>) {
49
+ export function generateCustomTypes(
50
+ typesMap: TypesMap,
51
+ requiredTypes: Set<string>
52
+ ) {
50
53
  return `
51
54
  // Custom types are those that are defined directly within generics
52
55
  // or are broken into simpler types
@@ -118,10 +118,7 @@ const _getPikkuCLIConfig = async (
118
118
  result.channelsFile = join(result.outDir, 'pikku-channels.gen.ts')
119
119
  }
120
120
  if (!result.typesDeclarationFile) {
121
- result.typesDeclarationFile = join(
122
- result.outDir,
123
- 'pikku-types.gen.d.ts'
124
- )
121
+ result.typesDeclarationFile = join(result.outDir, 'pikku-types.gen.ts')
125
122
  }
126
123
  if (!result.routesMapDeclarationFile) {
127
124
  result.routesMapDeclarationFile = join(
@@ -1,5 +1,5 @@
1
1
  import { createGenerator, RootlessError } from 'ts-json-schema-generator'
2
- import { writeFileInDir } from '../utils.js'
2
+ import { logInfo, writeFileInDir } from '../utils.js'
3
3
  import { mkdir, writeFile } from 'fs/promises'
4
4
  import { JSONValue } from '@pikku/core'
5
5
  import { HTTPRoutesMeta } from '@pikku/core/http'
@@ -42,7 +42,7 @@ export async function generateSchemas(
42
42
  } catch (e) {
43
43
  // Ignore rootless errors
44
44
  if (e instanceof RootlessError) {
45
- console.log('Error generating schema since it has no root:', schema)
45
+ console.error('Error generating schema since it has no root:', schema)
46
46
  return
47
47
  }
48
48
  throw e
@@ -80,7 +80,7 @@ export async function saveSchemas(
80
80
  ])
81
81
 
82
82
  if (desiredSchemas.size === 0) {
83
- console.log(`\x1b[34m• Skipping schemas since none found.\x1b[0m`)
83
+ logInfo(`• Skipping schemas since none found.\x1b[0m`)
84
84
  return
85
85
  }
86
86
 
package/src/utils.ts CHANGED
@@ -2,6 +2,20 @@
2
2
  import { relative, dirname } from 'path'
3
3
  import { PathToNameAndType, InspectorState } from '@pikku/inspector'
4
4
  import { mkdir, writeFile } from 'fs/promises'
5
+ import chalk from 'chalk'
6
+ import packageJson from '../package.json'
7
+
8
+ export const logPrimary = (message: string) => {
9
+ console.log(chalk.green(message))
10
+ }
11
+
12
+ export const logSuccess = (message: string) => {
13
+ console.log(chalk.green(message))
14
+ }
15
+
16
+ export const logInfo = (message: string) => {
17
+ console.log(chalk.blue(message))
18
+ }
5
19
 
6
20
  export const getFileImportRelativePath = (
7
21
  from: string,
@@ -184,7 +198,7 @@ export const writeFileInDir = async (
184
198
 
185
199
  await mkdir(dirname(path), { recursive: true })
186
200
  await writeFile(path, content, 'utf-8')
187
- console.log(`\x1b[32m✓ File written to ${path}\x1b[0m`)
201
+ logSuccess(`✓ File written to ${path}`)
188
202
  }
189
203
 
190
204
  export const logCommandInfoAndTime = async (
@@ -194,22 +208,30 @@ export const logCommandInfoAndTime = async (
194
208
  callback: (...args: any[]) => Promise<unknown>
195
209
  ): Promise<boolean> => {
196
210
  if (skipCondition === true) {
197
- console.log(
198
- `\x1b[34m• Skipping ${commandStart} since ${skipMessage}.\x1b[0m`
199
- )
211
+ logInfo(`• Skipping ${commandStart} since ${skipMessage}.`)
200
212
  return false
201
213
  }
202
214
 
203
215
  const start = Date.now()
204
- console.log(`\x1b[34m• ${commandStart}...\x1b[0m`)
216
+ chalk.blue(`• ${commandStart}...`)
205
217
  await callback()
206
218
 
207
- console.log(`\x1b[32m✓ ${commandEnd} in ${Date.now() - start}ms.\x1b[0m`)
219
+ logSuccess(`✓ ${commandEnd} in ${Date.now() - start}ms.`)
208
220
  return true
209
221
  }
210
222
 
223
+ const logo = `
224
+ ______ _ _ _
225
+ (_____ (_) | | |
226
+ _____) )| | _| | _ _ _
227
+ | ____/ | |_/ ) |_/ ) | | |
228
+ | | | | _ (| _ (| |_| |
229
+ |_| |_|_| \_)_| \_)____/
230
+ `
231
+
211
232
  export const logPikkuLogo = () => {
212
- console.log(`\x1b[33m⚙️ PIKKU CLI ⚙️\n-------------------\x1b[0m`)
233
+ logPrimary(logo)
234
+ logPrimary(`⚙️ Welcome to the Pikku CLI (v${packageJson})\n`)
213
235
  }
214
236
 
215
237
  // TODO: add version back in once the ESM dust settles