@pikku/cli 0.8.0 → 0.8.2

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.
Files changed (44) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/bin/pikku-all.ts +122 -64
  3. package/bin/pikku-fetch.ts +5 -1
  4. package/bin/pikku-nextjs.ts +6 -2
  5. package/bin/pikku-openapi.ts +3 -1
  6. package/bin/pikku-queue-service.ts +5 -1
  7. package/bin/pikku-websocket.ts +5 -1
  8. package/cli.schema.json +131 -10
  9. package/dist/bin/pikku-all.js +51 -50
  10. package/dist/bin/pikku-fetch.js +5 -1
  11. package/dist/bin/pikku-nextjs.js +6 -2
  12. package/dist/bin/pikku-openapi.js +3 -1
  13. package/dist/bin/pikku-queue-service.js +5 -1
  14. package/dist/bin/pikku-websocket.js +5 -1
  15. package/dist/src/events/functions/pikku-command-services.d.ts +3 -0
  16. package/dist/src/events/functions/pikku-command-services.js +73 -0
  17. package/dist/src/events/http/serialize-typed-http-map.d.ts +0 -1
  18. package/dist/src/events/http/serialize-typed-http-map.js +1 -14
  19. package/dist/src/events/rpc/pikku-command-rpc-client.d.ts +2 -0
  20. package/dist/src/events/rpc/pikku-command-rpc-client.js +12 -0
  21. package/dist/src/events/rpc/serialize-rpc-wrapper.d.ts +1 -0
  22. package/dist/src/events/rpc/serialize-rpc-wrapper.js +29 -0
  23. package/dist/src/events/rpc/serialize-typed-rpc-map.js +1 -1
  24. package/dist/src/inspector-glob.js +1 -1
  25. package/dist/src/pikku-cli-config.d.ts +4 -1
  26. package/dist/src/pikku-cli-config.js +26 -14
  27. package/dist/src/schema-generator.js +2 -2
  28. package/dist/src/utils.d.ts +3 -0
  29. package/dist/src/utils.js +25 -6
  30. package/dist/tsconfig.tsbuildinfo +1 -1
  31. package/package.json +3 -3
  32. package/src/events/functions/pikku-command-services.ts +125 -0
  33. package/src/events/http/serialize-typed-http-map.ts +1 -18
  34. package/src/events/rpc/pikku-command-rpc-client.ts +33 -0
  35. package/src/events/rpc/serialize-rpc-wrapper.ts +29 -0
  36. package/src/events/rpc/serialize-typed-rpc-map.ts +1 -1
  37. package/src/inspector-glob.ts +1 -1
  38. package/src/pikku-cli-config.ts +33 -17
  39. package/src/schema-generator.ts +2 -2
  40. package/src/utils.test.ts +137 -0
  41. package/src/utils.ts +31 -6
  42. package/dist/src/events/http/pikku-command-nextjs.d.ts +0 -2
  43. package/dist/src/events/http/pikku-command-nextjs.js +0 -36
  44. package/src/events/http/pikku-command-nextjs.ts +0 -111
package/dist/src/utils.js CHANGED
@@ -1,4 +1,4 @@
1
- import { relative, dirname } from 'path';
1
+ import { relative, dirname, resolve } from 'path';
2
2
  import { mkdir, writeFile } from 'fs/promises';
3
3
  import chalk from 'chalk';
4
4
  import { fileURLToPath } from 'url';
@@ -33,6 +33,11 @@ export class CLILogger {
33
33
  warn(message) {
34
34
  console.error(chalk.yellow(message));
35
35
  }
36
+ debug(message) {
37
+ if (process.env.DEBUG) {
38
+ console.log(chalk.gray(message));
39
+ }
40
+ }
36
41
  logPikkuLogo() {
37
42
  this.primary(logo);
38
43
  const packageJson = JSON.parse(readFileSync(`${dirname(__filename)}/../../package.json`, 'utf-8'));
@@ -44,14 +49,28 @@ export const getFileImportRelativePath = (from, to, packageMappings) => {
44
49
  if (!/^\.+\//.test(filePath)) {
45
50
  filePath = `./${filePath}`;
46
51
  }
47
- // let usesPackageName = false
48
- for (const [path, packageName] of Object.entries(packageMappings)) {
49
- if (filePath.includes(path)) {
50
- // usesPackageName = true
51
- filePath = filePath.replace(new RegExp(`.*${path}`), packageName);
52
+ const absolutePath = resolve(dirname(from), to);
53
+ const fromAbsolutePath = resolve(dirname(from));
54
+ // Check if both files are in the same package directory
55
+ // If so, skip packageMappings to use relative paths
56
+ let inSamePackage = false;
57
+ for (const [path] of Object.entries(packageMappings)) {
58
+ if (absolutePath.includes(path) && fromAbsolutePath.includes(path)) {
59
+ inSamePackage = true;
52
60
  break;
53
61
  }
54
62
  }
63
+ // Only apply packageMappings if files are not in the same package
64
+ if (!inSamePackage) {
65
+ // let usesPackageName = false
66
+ for (const [path, packageName] of Object.entries(packageMappings)) {
67
+ if (absolutePath.includes(path)) {
68
+ // usesPackageName = true
69
+ filePath = absolutePath.replace(new RegExp(`.*${path}`), packageName);
70
+ break;
71
+ }
72
+ }
73
+ }
55
74
  // if (usesPackageName) {
56
75
  // return filePath.replace('.ts', '')
57
76
  // }
@@ -1 +1 @@
1
- {"root":["../bin/pikku-all.ts","../bin/pikku-fetch.ts","../bin/pikku-nextjs.ts","../bin/pikku-openapi.ts","../bin/pikku-queue-service.ts","../bin/pikku-schemas.ts","../bin/pikku-websocket.ts","../bin/pikku.ts","../src/inspector-glob.ts","../src/pikku-cli-config.ts","../src/pikku-command-schemas.ts","../src/schema-generator.ts","../src/schemas.ts","../src/serialize-import-map.ts","../src/serialize-pikku-types.ts","../src/types.ts","../src/utils.ts","../src/events/channels/pikku-channels.ts","../src/events/channels/pikku-command-channels-map.ts","../src/events/channels/pikku-command-channels.ts","../src/events/channels/pikku-command-websocket-typed.ts","../src/events/channels/serialize-typed-channel-map.ts","../src/events/channels/serialize-websocket-wrapper.ts","../src/events/fetch/index.ts","../src/events/functions/pikku-command-function-types.ts","../src/events/functions/pikku-command-functions.ts","../src/events/functions/pikku-function-types.ts","../src/events/functions/pikku-functions.ts","../src/events/http/openapi-spec-generator.ts","../src/events/http/pikku-command-http-map.ts","../src/events/http/pikku-command-http-routes.ts","../src/events/http/pikku-command-nextjs.ts","../src/events/http/pikku-command-openapi.ts","../src/events/http/pikku-http-routes.ts","../src/events/http/serialize-fetch-wrapper.ts","../src/events/http/serialize-typed-http-map.ts","../src/events/mcp/pikku-command-mcp-json.ts","../src/events/mcp/pikku-command-mcp.ts","../src/events/mcp/serialize-mcp-json.ts","../src/events/queue/pikku-command-queue-map.ts","../src/events/queue/pikku-command-queue-service.ts","../src/events/queue/pikku-command-queue.ts","../src/events/queue/pikku-queue-map.ts","../src/events/queue/pikku-queue.ts","../src/events/queue/serialize-queue-map.ts","../src/events/queue/serialize-queue-meta.ts","../src/events/queue/serialize-queue-wrapper.ts","../src/events/rpc/pikku-command-rpc-map.ts","../src/events/rpc/pikku-command-rpc.ts","../src/events/rpc/pikku-rpc.ts","../src/events/rpc/serialize-typed-rpc-map.ts","../src/events/scheduler/pikku-command-scheduler.ts","../src/events/scheduler/serialize-scheduler-meta.ts","../src/runtimes/nextjs/pikku-command-nextjs.ts","../src/runtimes/nextjs/serialize-nextjs-backend-wrapper.ts","../src/runtimes/nextjs/serialize-nextjs-http-wrapper.ts"],"version":"5.8.3"}
1
+ {"root":["../bin/pikku-all.ts","../bin/pikku-fetch.ts","../bin/pikku-nextjs.ts","../bin/pikku-openapi.ts","../bin/pikku-queue-service.ts","../bin/pikku-schemas.ts","../bin/pikku-websocket.ts","../bin/pikku.ts","../src/inspector-glob.ts","../src/pikku-cli-config.ts","../src/pikku-command-schemas.ts","../src/schema-generator.ts","../src/schemas.ts","../src/serialize-import-map.ts","../src/serialize-pikku-types.ts","../src/types.ts","../src/utils.ts","../src/events/channels/pikku-channels.ts","../src/events/channels/pikku-command-channels-map.ts","../src/events/channels/pikku-command-channels.ts","../src/events/channels/pikku-command-websocket-typed.ts","../src/events/channels/serialize-typed-channel-map.ts","../src/events/channels/serialize-websocket-wrapper.ts","../src/events/fetch/index.ts","../src/events/functions/pikku-command-function-types.ts","../src/events/functions/pikku-command-functions.ts","../src/events/functions/pikku-command-services.ts","../src/events/functions/pikku-function-types.ts","../src/events/functions/pikku-functions.ts","../src/events/http/openapi-spec-generator.ts","../src/events/http/pikku-command-http-map.ts","../src/events/http/pikku-command-http-routes.ts","../src/events/http/pikku-command-openapi.ts","../src/events/http/pikku-http-routes.ts","../src/events/http/serialize-fetch-wrapper.ts","../src/events/http/serialize-typed-http-map.ts","../src/events/mcp/pikku-command-mcp-json.ts","../src/events/mcp/pikku-command-mcp.ts","../src/events/mcp/serialize-mcp-json.ts","../src/events/queue/pikku-command-queue-map.ts","../src/events/queue/pikku-command-queue-service.ts","../src/events/queue/pikku-command-queue.ts","../src/events/queue/pikku-queue-map.ts","../src/events/queue/pikku-queue.ts","../src/events/queue/serialize-queue-map.ts","../src/events/queue/serialize-queue-meta.ts","../src/events/queue/serialize-queue-wrapper.ts","../src/events/rpc/pikku-command-rpc-client.ts","../src/events/rpc/pikku-command-rpc-map.ts","../src/events/rpc/pikku-command-rpc.ts","../src/events/rpc/pikku-rpc.ts","../src/events/rpc/serialize-rpc-wrapper.ts","../src/events/rpc/serialize-typed-rpc-map.ts","../src/events/scheduler/pikku-command-scheduler.ts","../src/events/scheduler/serialize-scheduler-meta.ts","../src/runtimes/nextjs/pikku-command-nextjs.ts","../src/runtimes/nextjs/serialize-nextjs-backend-wrapper.ts","../src/runtimes/nextjs/serialize-nextjs-http-wrapper.ts"],"version":"5.8.3"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/cli",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "author": "yasser.fadl@gmail.com",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -22,8 +22,8 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@openapi-contrib/json-schema-to-openapi-schema": "^3.0.2",
25
- "@pikku/core": "^0.8.0",
26
- "@pikku/inspector": "^0.8.0",
25
+ "@pikku/core": "^0.8.2",
26
+ "@pikku/inspector": "^0.8.1",
27
27
  "@types/cookie": "^0.6.0",
28
28
  "@types/uuid": "^10.0.0",
29
29
  "chalk": "^5.4.1",
@@ -0,0 +1,125 @@
1
+ import {
2
+ getFileImportRelativePath,
3
+ getPikkuFilesAndMethods,
4
+ logCommandInfoAndTime,
5
+ writeFileInDir,
6
+ } from '../../utils.js'
7
+ import { PikkuCommand } from '../../types.js'
8
+ import { PikkuCLIConfig } from '../../pikku-cli-config.js'
9
+ import { InspectorState } from '@pikku/inspector'
10
+
11
+ export const serializeServicesMap = (
12
+ functionsMetaData: Record<string, any>,
13
+ middlewareServices: string[] = [],
14
+ servicesImport: string,
15
+ sessionServicesImport: string
16
+ ): string => {
17
+ // Extract all unique services from all functions
18
+ const usedServices = new Set<string>()
19
+
20
+ // Internal services that are created internally and not via the create service script
21
+ const internalServices = new Set(['rpc', 'mcp', 'channel', 'userSession'])
22
+
23
+ for (const funcMeta of Object.values(functionsMetaData)) {
24
+ if (funcMeta.services && Array.isArray(funcMeta.services.services)) {
25
+ funcMeta.services.services.forEach((service: string) => {
26
+ // Only include services that are not internal
27
+ if (!internalServices.has(service)) {
28
+ usedServices.add(service)
29
+ }
30
+ })
31
+ }
32
+ }
33
+
34
+ // Add middleware services that might not be detected from function inspection
35
+ middlewareServices.forEach((service) => {
36
+ if (!internalServices.has(service)) {
37
+ usedServices.add(service)
38
+ }
39
+ })
40
+
41
+ // Create a map of services with true for all needed services
42
+ const servicesMap = Object.fromEntries(
43
+ Array.from(usedServices)
44
+ .sort()
45
+ .map((service) => [service, true])
46
+ )
47
+
48
+ // Generate the TypeScript code
49
+ const serviceKeys = Object.keys(servicesMap).sort()
50
+
51
+ // Services that are always required internally by the framework
52
+ const defaultServices = ['config', 'logger', 'variables', 'schema']
53
+
54
+ // Combine default services with detected services
55
+ const allRequiredServices = [
56
+ ...new Set([...defaultServices, ...serviceKeys]),
57
+ ].sort()
58
+
59
+ // For RequiredSingletonServices, we need to pick from the actual SingletonServices interface
60
+ // This will be resolved at compile time based on what's actually in the SingletonServices interface
61
+ // We don't need to hardcode which services are singletons beyond the core framework ones
62
+
63
+ const code = [
64
+ '/**',
65
+ ' * This file was generated by the @pikku/cli',
66
+ ' */',
67
+ '',
68
+ servicesImport,
69
+ sessionServicesImport,
70
+ "import type { PikkuInteraction } from '@pikku/core'",
71
+ '',
72
+ 'export const singletonServices = {',
73
+ ...Object.keys(servicesMap).map((service) => ` '${service}': true,`),
74
+ '} as const',
75
+ '',
76
+ '// Singleton services (created once at startup)',
77
+ '// Only includes services that are both required and available in SingletonServices',
78
+ `export type RequiredSingletonServices = Pick<SingletonServices, Extract<keyof SingletonServices, ${allRequiredServices.map((key) => `'${key}'`).join(' | ')}>> & Partial<Omit<SingletonServices, ${allRequiredServices.map((key) => `'${key}'`).join(' | ')}>>`,
79
+ '',
80
+ '// Session services (created per request, can access singleton services)',
81
+ '// Omits singleton services and PikkuInteraction (mcp, rpc, http, channel)',
82
+ `export type RequiredSessionServices = Omit<Services, keyof SingletonServices | keyof PikkuInteraction>`,
83
+ '',
84
+ ].join('\n')
85
+
86
+ return code
87
+ }
88
+
89
+ export const pikkuServices: PikkuCommand = async (
90
+ logger,
91
+ cliConfig: PikkuCLIConfig,
92
+ visitState: InspectorState
93
+ ) => {
94
+ return await logCommandInfoAndTime(
95
+ logger,
96
+ 'Generating Pikku services map',
97
+ 'Generated Pikku services map',
98
+ [visitState.functions.files.size === 0],
99
+ async () => {
100
+ const { sessionServicesType, singletonServicesType } =
101
+ await getPikkuFilesAndMethods(
102
+ logger,
103
+ visitState,
104
+ cliConfig.packageMappings,
105
+ cliConfig.typesDeclarationFile,
106
+ {},
107
+ {
108
+ sessionServiceType: true,
109
+ singletonServicesType: true,
110
+ }
111
+ )
112
+
113
+ const servicesImport = `import type { ${singletonServicesType.type} } from '${getFileImportRelativePath(cliConfig.typesDeclarationFile, singletonServicesType.typePath, cliConfig.packageMappings)}'`
114
+ const sessionServicesImport = `import type { ${sessionServicesType.type} } from '${getFileImportRelativePath(cliConfig.typesDeclarationFile, sessionServicesType.typePath, cliConfig.packageMappings)}'`
115
+
116
+ const servicesCode = serializeServicesMap(
117
+ visitState.functions.meta,
118
+ cliConfig.middlewareServices,
119
+ servicesImport,
120
+ sessionServicesImport
121
+ )
122
+ await writeFileInDir(logger, cliConfig.servicesFile, servicesCode)
123
+ }
124
+ )
125
+ }
@@ -2,6 +2,7 @@ import { HTTPRoutesMeta } from '@pikku/core/http'
2
2
  import { serializeImportMap } from '../../serialize-import-map.js'
3
3
  import { MetaInputTypes, TypesMap } from '@pikku/inspector'
4
4
  import { FunctionsMeta } from '@pikku/core'
5
+ import { generateCustomTypes } from '../../utils.js'
5
6
 
6
7
  export const serializeTypedRoutesMap = (
7
8
  relativeToPath: string,
@@ -54,24 +55,6 @@ export type RoutesWithMethod<Method extends string> = {
54
55
  `
55
56
  }
56
57
 
57
- export function generateCustomTypes(
58
- typesMap: TypesMap,
59
- requiredTypes: Set<string>
60
- ) {
61
- return `
62
- // Custom types are those that are defined directly within generics
63
- // or are broken into simpler types
64
- ${Array.from(typesMap.customTypes.entries())
65
- .map(([name, { type, references }]) => {
66
- references.forEach((name) => {
67
- const originalName = typesMap.getTypeMeta(name).originalName
68
- requiredTypes.add(originalName)
69
- })
70
- return `export type ${name} = ${type}`
71
- })
72
- .join('\n')}`
73
- }
74
-
75
58
  function generateRoutes(
76
59
  routesMeta: HTTPRoutesMeta,
77
60
  functionsMeta: FunctionsMeta,
@@ -0,0 +1,33 @@
1
+ import { serializeRPCWrapper } from './serialize-rpc-wrapper.js'
2
+ import {
3
+ getFileImportRelativePath,
4
+ logCommandInfoAndTime,
5
+ writeFileInDir,
6
+ } from '../../utils.js'
7
+ import { PikkuCommandWithoutState } from '../../types.js'
8
+
9
+ export const pikkuRPCClient: PikkuCommandWithoutState = async (
10
+ logger,
11
+ { rpcFile, rpcMapDeclarationFile, packageMappings }
12
+ ) => {
13
+ return await logCommandInfoAndTime(
14
+ logger,
15
+ 'Generating RPC wrapper',
16
+ 'Generated RPC wrapper',
17
+ [rpcFile === undefined, "rpcFile isn't set in the pikku config"],
18
+ async () => {
19
+ if (!rpcFile) {
20
+ throw new Error("rpcFile isn't set in the pikku config")
21
+ }
22
+
23
+ const rpcMapDeclarationPath = getFileImportRelativePath(
24
+ rpcFile,
25
+ rpcMapDeclarationFile,
26
+ packageMappings
27
+ )
28
+
29
+ const content = [serializeRPCWrapper(rpcMapDeclarationPath)]
30
+ await writeFileInDir(logger, rpcFile, content.join('\n'))
31
+ }
32
+ )
33
+ }
@@ -0,0 +1,29 @@
1
+ export const serializeRPCWrapper = (rpcMapPath: string) => {
2
+ return `
3
+ import { PikkuFetch } from "./pikku-fetch.gen.js"
4
+ import type { RPCInvoke } from '${rpcMapPath}'
5
+
6
+ export class PikkuRPC {
7
+ pikkuFetch = new PikkuFetch()
8
+
9
+ setPikkuFetch(pikkuFetch: PikkuFetch): void {
10
+ this.pikkuFetch = pikkuFetch
11
+ }
12
+
13
+ setServerUrl(serverUrl: string): void {
14
+ this.pikkuFetch.setServerUrl(serverUrl)
15
+ }
16
+
17
+ setAuthorizationJWT(jwt: string | null): void {
18
+ this.pikkuFetch.setAuthorizationJWT(jwt)
19
+ }
20
+
21
+ // Generic RPC invoke method
22
+ invoke: RPCInvoke = async (name, data) => {
23
+ return await this.pikkuFetch.post('/rpc', { name, data })
24
+ }
25
+ }
26
+
27
+ export const pikkuRPC = new PikkuRPC();
28
+ `
29
+ }
@@ -42,7 +42,7 @@ interface RPCHandler<I, O> {
42
42
 
43
43
  ${serializedRPCs}
44
44
 
45
- type RPCInvoke = <Name extends keyof RPCMap>(
45
+ export type RPCInvoke = <Name extends keyof RPCMap>(
46
46
  name: Name,
47
47
  data: RPCMap[Name]['input'],
48
48
  options?: {
@@ -23,7 +23,7 @@ export const inspectorGlob = async (
23
23
  )
24
24
  )
25
25
  ).flat()
26
- result = await inspect(routeFiles, filters)
26
+ result = await inspect(logger, routeFiles, filters)
27
27
  }
28
28
  )
29
29
  return result!
@@ -43,6 +43,9 @@ export interface PikkuCLICoreOutputFiles {
43
43
  mcpEndpointsFile: string
44
44
  mcpEndpointsMetaFile: string
45
45
 
46
+ // Services
47
+ servicesFile: string
48
+
46
49
  // Application bootstrap
47
50
  bootstrapFile: string
48
51
  bootstrapFiles: Record<PikkuEventTypes, string>
@@ -65,6 +68,7 @@ export type PikkuCLIConfig = {
65
68
  nextHTTPFile?: string
66
69
  fetchFile?: string
67
70
  websocketFile?: string
71
+ rpcFile?: string
68
72
  queueFile?: string
69
73
  mcpJsonFile?: string
70
74
 
@@ -73,6 +77,8 @@ export type PikkuCLIConfig = {
73
77
  additionalInfo: OpenAPISpecInfo
74
78
  }
75
79
 
80
+ middlewareServices?: string[]
81
+
76
82
  filters: InspectorFilters
77
83
  } & PikkuCLICoreOutputFiles
78
84
 
@@ -81,6 +87,7 @@ const CONFIG_DIR_FILES = [
81
87
  'nextHTTPFile',
82
88
  'fetchFile',
83
89
  'websocketFile',
90
+ 'rpcFile',
84
91
  'queueFile',
85
92
  'mcpJsonFile',
86
93
  ]
@@ -88,13 +95,13 @@ const CONFIG_DIR_FILES = [
88
95
  export const getPikkuCLIConfig = async (
89
96
  configFile: string | undefined = undefined,
90
97
  requiredFields: Array<keyof PikkuCLIConfig>,
91
- tags: string[] = [],
98
+ filters: InspectorFilters = {},
92
99
  exitProcess: boolean = false
93
100
  ): Promise<PikkuCLIConfig> => {
94
101
  const config = await _getPikkuCLIConfig(
95
102
  configFile,
96
103
  requiredFields,
97
- tags,
104
+ filters,
98
105
  exitProcess
99
106
  )
100
107
  return config
@@ -103,7 +110,7 @@ export const getPikkuCLIConfig = async (
103
110
  const _getPikkuCLIConfig = async (
104
111
  configFile: string | undefined = undefined,
105
112
  requiredFields: Array<keyof PikkuCLIConfig>,
106
- tags: string[] = [],
113
+ filters: InspectorFilters = {},
107
114
  exitProcess: boolean = false
108
115
  ): Promise<PikkuCLIConfig> => {
109
116
  if (!configFile) {
@@ -131,7 +138,7 @@ const _getPikkuCLIConfig = async (
131
138
  const extendedConfig = await getPikkuCLIConfig(
132
139
  resolve(configDir, config.extends),
133
140
  [],
134
- tags,
141
+ filters,
135
142
  exitProcess
136
143
  )
137
144
  result = {
@@ -156,8 +163,9 @@ const _getPikkuCLIConfig = async (
156
163
 
157
164
  if (result.outDir) {
158
165
  // Create transport/event directories
166
+ const functionDir = join(result.outDir, 'function')
159
167
  const httpDir = join(result.outDir, 'http')
160
- const channelsDir = join(result.outDir, 'channels')
168
+ const channelDir = join(result.outDir, 'channel')
161
169
  const rpcDir = join(result.outDir, 'rpc')
162
170
  const schedulerDir = join(result.outDir, 'scheduler')
163
171
  const queueDir = join(result.outDir, 'queue')
@@ -171,11 +179,11 @@ const _getPikkuCLIConfig = async (
171
179
 
172
180
  // Functions
173
181
  if (!result.functionsFile) {
174
- result.functionsFile = join(result.outDir, 'pikku-functions.gen.ts')
182
+ result.functionsFile = join(functionDir, 'pikku-functions.gen.ts')
175
183
  }
176
184
  if (!result.functionsMetaFile) {
177
185
  result.functionsMetaFile = join(
178
- result.outDir,
186
+ functionDir,
179
187
  'pikku-functions-meta.gen.ts'
180
188
  )
181
189
  }
@@ -202,17 +210,14 @@ const _getPikkuCLIConfig = async (
202
210
 
203
211
  // Channels/WebSocket
204
212
  if (!result.channelsFile) {
205
- result.channelsFile = join(channelsDir, 'pikku-channels.gen.ts')
213
+ result.channelsFile = join(channelDir, 'pikku-channels.gen.ts')
206
214
  }
207
215
  if (!result.channelsMetaFile) {
208
- result.channelsMetaFile = join(
209
- channelsDir,
210
- 'pikku-channels-meta.gen.ts'
211
- )
216
+ result.channelsMetaFile = join(channelDir, 'pikku-channels-meta.gen.ts')
212
217
  }
213
218
  if (!result.channelsMapDeclarationFile) {
214
219
  result.channelsMapDeclarationFile = join(
215
- channelsDir,
220
+ channelDir,
216
221
  'pikku-channels-map.gen.d.ts'
217
222
  )
218
223
  }
@@ -227,12 +232,12 @@ const _getPikkuCLIConfig = async (
227
232
 
228
233
  // Scheduler
229
234
  if (!result.schedulersFile) {
230
- result.schedulersFile = join(schedulerDir, 'pikku-schedules.gen.ts')
235
+ result.schedulersFile = join(schedulerDir, 'pikku-scheduler.gen.ts')
231
236
  }
232
237
  if (!result.schedulersMetaFile) {
233
238
  result.schedulersMetaFile = join(
234
239
  schedulerDir,
235
- 'pikku-schedules-meta.gen.ts'
240
+ 'pikku-scheduler-meta.gen.ts'
236
241
  )
237
242
  }
238
243
 
@@ -253,6 +258,11 @@ const _getPikkuCLIConfig = async (
253
258
  )
254
259
  }
255
260
 
261
+ // Services
262
+ if (!result.servicesFile) {
263
+ result.servicesFile = join(result.outDir, 'pikku-services.gen.ts')
264
+ }
265
+
256
266
  // Bootstrap files
257
267
  if (!result.bootstrapFile) {
258
268
  result.bootstrapFile = join(result.outDir, 'pikku-bootstrap.gen.ts')
@@ -297,8 +307,14 @@ const _getPikkuCLIConfig = async (
297
307
  }
298
308
 
299
309
  result.filters = result.filters || {}
300
- if (tags.length > 0) {
301
- result.filters.tags = tags
310
+ if (filters.tags && filters.tags.length > 0) {
311
+ result.filters.tags = filters.tags
312
+ }
313
+ if (filters.types && filters.types.length > 0) {
314
+ result.filters.types = filters.types
315
+ }
316
+ if (filters.directories && filters.directories.length > 0) {
317
+ result.filters.directories = filters.directories
302
318
  }
303
319
 
304
320
  if (!isAbsolute(result.tsconfig)) {
@@ -53,10 +53,10 @@ export async function generateSchemas(
53
53
  } catch (e) {
54
54
  // Ignore rootless errors
55
55
  if (e instanceof RootlessError) {
56
- console.error('Error generating schema since it has no root:', schema)
56
+ logger.error(`Error generating schema since it has no root: ${schema}`)
57
57
  return
58
58
  }
59
- throw e
59
+ logger.error(`Error generating schema: ${schema}`)
60
60
  }
61
61
  })
62
62
 
@@ -0,0 +1,137 @@
1
+ import { strict as assert } from 'assert'
2
+ import { describe, test } from 'node:test'
3
+ import { getFileImportRelativePath } from './utils.js'
4
+
5
+ describe('getFileImportRelativePath', () => {
6
+ test('should return relative path for files in same directory', () => {
7
+ const from = '/project/src/file1.ts'
8
+ const to = '/project/src/file2.ts'
9
+ const packageMappings = {}
10
+
11
+ const result = getFileImportRelativePath(from, to, packageMappings)
12
+
13
+ assert.strictEqual(result, './file2.js')
14
+ })
15
+
16
+ test('should return relative path for files in different directories', () => {
17
+ const from = '/project/src/file1.ts'
18
+ const to = '/project/lib/file2.ts'
19
+ const packageMappings = {}
20
+
21
+ const result = getFileImportRelativePath(from, to, packageMappings)
22
+
23
+ assert.strictEqual(result, '../lib/file2.js')
24
+ })
25
+
26
+ test('should use package mapping when files are in different packages', () => {
27
+ const from = '/project/packages/app/src/file1.ts'
28
+ const to = '/project/packages/sdk/src/file2.ts'
29
+ const packageMappings = {
30
+ 'packages/sdk': '@myorg/sdk',
31
+ }
32
+
33
+ const result = getFileImportRelativePath(from, to, packageMappings)
34
+
35
+ assert.strictEqual(result, '@myorg/sdk/src/file2.js')
36
+ })
37
+
38
+ test('should NOT use package mapping when files are in same package', () => {
39
+ const from = '/project/packages/sdk/src/file1.ts'
40
+ const to = '/project/packages/sdk/lib/file2.ts'
41
+ const packageMappings = {
42
+ 'packages/sdk': '@myorg/sdk',
43
+ }
44
+
45
+ const result = getFileImportRelativePath(from, to, packageMappings)
46
+
47
+ assert.strictEqual(result, '../lib/file2.js')
48
+ })
49
+
50
+ test('should use package mapping when only target file is in mapped package', () => {
51
+ const from = '/project/apps/web/src/file1.ts'
52
+ const to = '/project/packages/sdk/src/file2.ts'
53
+ const packageMappings = {
54
+ 'packages/sdk': '@myorg/sdk',
55
+ }
56
+
57
+ const result = getFileImportRelativePath(from, to, packageMappings)
58
+
59
+ assert.strictEqual(result, '@myorg/sdk/src/file2.js')
60
+ })
61
+
62
+ test('should handle multiple package mappings correctly', () => {
63
+ const from = '/project/packages/app/src/file1.ts'
64
+ const to = '/project/packages/utils/src/file2.ts'
65
+ const packageMappings = {
66
+ 'packages/app': '@myorg/app',
67
+ 'packages/utils': '@myorg/utils',
68
+ }
69
+
70
+ const result = getFileImportRelativePath(from, to, packageMappings)
71
+
72
+ assert.strictEqual(result, '@myorg/utils/src/file2.js')
73
+ })
74
+
75
+ test('should preserve relative path when both files are in same package directory', () => {
76
+ const from = '/project/packages/sdk/src/components/file1.ts'
77
+ const to = '/project/packages/sdk/src/utils/file2.ts'
78
+ const packageMappings = {
79
+ 'packages/sdk': '@myorg/sdk',
80
+ }
81
+
82
+ const result = getFileImportRelativePath(from, to, packageMappings)
83
+
84
+ assert.strictEqual(result, '../utils/file2.js')
85
+ })
86
+
87
+ test('should work with nested package structures', () => {
88
+ const from = '/project/packages/app/src/file1.ts'
89
+ const to = '/project/packages/app/lib/nested/file2.ts'
90
+ const packageMappings = {
91
+ 'packages/app': '@myorg/app',
92
+ 'packages/sdk': '@myorg/sdk',
93
+ }
94
+
95
+ const result = getFileImportRelativePath(from, to, packageMappings)
96
+
97
+ assert.strictEqual(result, '../lib/nested/file2.js')
98
+ })
99
+
100
+ test('should handle complex path structures', () => {
101
+ const from =
102
+ '/Users/user/project/workspace-starter/packages/sdk/.pikku/pikku-fetch.gen.ts'
103
+ const to =
104
+ '/Users/user/project/workspace-starter/packages/functions/.pikku/http/pikku-http-routes-map.gen.d.ts'
105
+ const packageMappings = {
106
+ 'packages/sdk': '@workspace/sdk',
107
+ 'packages/functions': '@workspace/functions',
108
+ }
109
+
110
+ const result = getFileImportRelativePath(from, to, packageMappings)
111
+
112
+ assert.strictEqual(
113
+ result,
114
+ '@workspace/functions/.pikku/http/pikku-http-routes-map.gen.d.js'
115
+ )
116
+ })
117
+
118
+ test('should handle empty package mappings', () => {
119
+ const from = '/project/src/file1.ts'
120
+ const to = '/project/lib/file2.ts'
121
+ const packageMappings = {}
122
+
123
+ const result = getFileImportRelativePath(from, to, packageMappings)
124
+
125
+ assert.strictEqual(result, '../lib/file2.js')
126
+ })
127
+
128
+ test('should replace .ts extension with .js', () => {
129
+ const from = '/project/src/file1.ts'
130
+ const to = '/project/src/file2.tsx'
131
+ const packageMappings = {}
132
+
133
+ const result = getFileImportRelativePath(from, to, packageMappings)
134
+
135
+ assert.strictEqual(result, './file2.tsx'.replace('.ts', '.js'))
136
+ })
137
+ })