@pikku/cli 0.10.0 → 0.10.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.
- package/.pikku/channel/pikku-channel-types.gen.ts +4 -3
- package/.pikku/channel/pikku-channels-map.gen.d.ts +2 -2
- package/.pikku/channel/pikku-channels-meta.gen.ts +1 -1
- package/.pikku/channel/pikku-channels.gen.ts +1 -1
- package/.pikku/cli/pikku-cli-types.gen.ts +23 -1
- package/.pikku/cli/pikku-cli-wirings-meta.gen.ts +4 -115
- package/.pikku/cli/pikku-cli-wirings.gen.ts +2 -3
- package/.pikku/function/pikku-function-types.gen.ts +1 -1
- package/.pikku/function/pikku-functions-meta.gen.ts +156 -138
- package/.pikku/function/pikku-functions-meta.min.gen.ts +37 -32
- package/.pikku/function/pikku-functions.gen.ts +1 -1
- package/.pikku/http/pikku-http-types.gen.ts +1 -1
- package/.pikku/http/pikku-http-wirings-map.gen.d.ts +2 -2
- package/.pikku/http/pikku-http-wirings-meta.gen.ts +1 -1
- package/.pikku/http/pikku-http-wirings.gen.ts +1 -1
- package/.pikku/mcp/pikku-mcp-types.gen.ts +1 -1
- package/.pikku/mcp/pikku-mcp-wirings-meta.gen.ts +1 -1
- package/.pikku/mcp/pikku-mcp-wirings.gen.ts +1 -1
- package/.pikku/pikku-bootstrap.gen.ts +1 -1
- package/.pikku/pikku-services.gen.ts +16 -12
- package/.pikku/pikku-types.gen.ts +1 -1
- package/.pikku/pikku-websocket.gen.ts +15 -1
- package/.pikku/queue/pikku-queue-types.gen.ts +1 -1
- package/.pikku/queue/pikku-queue-workers-wirings-map.gen.d.ts +2 -2
- package/.pikku/queue/pikku-queue-workers-wirings-meta.gen.ts +1 -1
- package/.pikku/queue/pikku-queue-workers-wirings.gen.ts +1 -1
- package/.pikku/rpc/pikku-rpc-wirings-map.gen.d.ts +2 -2
- package/.pikku/rpc/pikku-rpc-wirings-map.internal.gen.d.ts +10 -9
- package/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.ts +9 -8
- package/.pikku/scheduler/pikku-scheduler-types.gen.ts +1 -1
- package/.pikku/scheduler/pikku-schedulers-wirings-meta.gen.ts +1 -1
- package/.pikku/scheduler/pikku-schedulers-wirings.gen.ts +1 -1
- package/.pikku/schemas/register.gen.ts +5 -5
- package/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
- package/.pikku/schemas/schemas/PikkuChannelsOutput.schema.json +1 -1
- package/.pikku/schemas/schemas/PikkuSchemasOutput.schema.json +1 -1
- package/CHANGELOG.md +58 -0
- package/bin/pikku.ts +30 -21
- package/cli.schema.json +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +4 -3
- package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
- package/dist/.pikku/channel/pikku-channels-meta.gen.js +1 -1
- package/dist/.pikku/channel/pikku-channels.gen.d.ts +1 -1
- package/dist/.pikku/channel/pikku-channels.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +18 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.js +20 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +4 -115
- package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -2
- package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -2
- package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.js +156 -138
- package/dist/.pikku/function/pikku-functions-meta.min.gen.js +37 -32
- package/dist/.pikku/function/pikku-functions.gen.js +1 -1
- package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
- package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
- package/dist/.pikku/http/pikku-http-wirings-meta.gen.js +1 -1
- package/dist/.pikku/http/pikku-http-wirings.gen.d.ts +1 -1
- package/dist/.pikku/http/pikku-http-wirings.gen.js +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
- package/dist/.pikku/mcp/pikku-mcp-wirings-meta.gen.js +1 -1
- package/dist/.pikku/mcp/pikku-mcp-wirings.gen.d.ts +1 -1
- package/dist/.pikku/mcp/pikku-mcp-wirings.gen.js +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
- package/dist/.pikku/pikku-services.gen.d.ts +9 -6
- package/dist/.pikku/pikku-services.gen.js +8 -2
- package/dist/.pikku/pikku-types.gen.d.ts +1 -1
- package/dist/.pikku/pikku-types.gen.js +1 -1
- package/dist/.pikku/pikku-websocket.gen.d.ts +15 -1
- package/dist/.pikku/pikku-websocket.gen.js +15 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +9 -8
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
- package/dist/.pikku/scheduler/pikku-schedulers-wirings-meta.gen.js +1 -1
- package/dist/.pikku/scheduler/pikku-schedulers-wirings.gen.d.ts +1 -1
- package/dist/.pikku/scheduler/pikku-schedulers-wirings.gen.js +1 -1
- package/dist/.pikku/schemas/register.gen.js +3 -3
- package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/PikkuChannelsOutput.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/PikkuSchemasOutput.schema.json +1 -1
- package/dist/bin/pikku.js +24 -19
- package/dist/src/cli.wiring.js +107 -99
- package/dist/src/functions/commands/all.js +31 -2
- package/dist/src/functions/commands/bootstrap.d.ts +1 -0
- package/dist/src/functions/commands/bootstrap.js +23 -0
- package/dist/src/functions/runtimes/nextjs/serialize-nextjs-backend-wrapper.js +46 -2
- package/dist/src/functions/wirings/channels/serialize-channel-types.js +3 -2
- package/dist/src/functions/wirings/channels/serialize-websocket-wrapper.js +14 -0
- package/dist/src/functions/wirings/cli/pikku-command-cli-entry.js +4 -4
- package/dist/src/functions/wirings/cli/serialize-channel-cli-client.js +24 -4
- package/dist/src/functions/wirings/cli/serialize-channel-cli.js +32 -7
- package/dist/src/functions/wirings/cli/serialize-cli-types.js +22 -0
- package/dist/src/functions/wirings/functions/pikku-command-services.d.ts +1 -1
- package/dist/src/functions/wirings/functions/pikku-command-services.js +54 -26
- package/dist/src/functions/wirings/functions/schemas.js +2 -2
- package/dist/src/functions/wirings/http/pikku-command-openapi.js +1 -1
- package/dist/src/functions/wirings/middleware/pikku-command-middleware.js +3 -10
- package/dist/src/middleware/log-command-info-and-time.d.ts +1 -1
- package/dist/src/middleware/log-command-info-and-time.js +8 -5
- package/dist/src/services/cli-logger.service.d.ts +7 -2
- package/dist/src/services/cli-logger.service.js +16 -4
- package/dist/src/services.js +77 -12
- package/dist/src/utils/check-required-types.js +11 -1
- package/dist/src/utils/command-summary.d.ts +43 -0
- package/dist/src/utils/command-summary.js +73 -0
- package/dist/src/utils/file-writer.js +2 -2
- package/dist/src/utils/pikku-cli-config.js +28 -0
- package/dist/src/utils/schema-generator.d.ts +2 -2
- package/dist/src/utils/schema-generator.js +3 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -4
- package/pikku.config.json +5 -2
- package/src/cli.wiring.ts +106 -101
- package/src/functions/commands/all.ts +38 -2
- package/src/functions/commands/bootstrap.ts +27 -0
- package/src/functions/runtimes/nextjs/serialize-nextjs-backend-wrapper.ts +46 -2
- package/src/functions/wirings/channels/serialize-channel-types.ts +3 -2
- package/src/functions/wirings/channels/serialize-websocket-wrapper.ts +14 -0
- package/src/functions/wirings/cli/pikku-command-cli-entry.ts +4 -4
- package/src/functions/wirings/cli/serialize-channel-cli-client.ts +24 -4
- package/src/functions/wirings/cli/serialize-channel-cli.ts +40 -8
- package/src/functions/wirings/cli/serialize-cli-types.ts +22 -0
- package/src/functions/wirings/functions/pikku-command-services.ts +57 -28
- package/src/functions/wirings/functions/schemas.ts +4 -3
- package/src/functions/wirings/http/pikku-command-openapi.ts +2 -1
- package/src/functions/wirings/middleware/pikku-command-middleware.ts +11 -22
- package/src/middleware/log-command-info-and-time.ts +8 -5
- package/src/services/cli-logger.service.ts +20 -5
- package/src/services.ts +86 -11
- package/src/utils/check-required-types.ts +16 -1
- package/src/utils/command-summary.ts +101 -0
- package/src/utils/file-writer.ts +2 -2
- package/src/utils/pikku-cli-config.ts +28 -0
- package/src/utils/schema-generator.ts +5 -4
- package/types/application-types.d.ts +5 -1
- package/types/config.d.ts +16 -6
- package/.pikku/cli/pikku-cli-channel.gen.ts +0 -34
- package/.pikku/cli/pikku-cli-client.gen.ts +0 -43
- package/.pikku/cli/pikku-cli.gen.ts +0 -41
- package/dist/.pikku/cli/pikku-cli-channel.gen.d.ts +0 -1
- package/dist/.pikku/cli/pikku-cli-channel.gen.js +0 -33
- package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +0 -10
- package/dist/.pikku/cli/pikku-cli-client.gen.js +0 -34
- package/dist/.pikku/cli/pikku-cli.gen.d.ts +0 -10
- package/dist/.pikku/cli/pikku-cli.gen.js +0 -38
|
@@ -5,6 +5,8 @@ import { writeFileInDir } from '../../../utils/file-writer.js'
|
|
|
5
5
|
import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-time.js'
|
|
6
6
|
|
|
7
7
|
export const serializeServicesMap = (
|
|
8
|
+
allSingletonServices: string[],
|
|
9
|
+
allSessionServices: string[],
|
|
8
10
|
requiredServices: Set<string>,
|
|
9
11
|
forceRequiredServices: string[] = [],
|
|
10
12
|
servicesImport: string,
|
|
@@ -18,8 +20,18 @@ export const serializeServicesMap = (
|
|
|
18
20
|
// - Session factories
|
|
19
21
|
const usedServices = new Set(requiredServices)
|
|
20
22
|
|
|
21
|
-
// Internal services that are created internally
|
|
22
|
-
|
|
23
|
+
// Internal services that are created internally by the framework (PikkuInteraction)
|
|
24
|
+
// These should not appear in the services maps
|
|
25
|
+
const internalServices = new Set([
|
|
26
|
+
'rpc',
|
|
27
|
+
'mcp',
|
|
28
|
+
'channel',
|
|
29
|
+
'userSession',
|
|
30
|
+
'cli',
|
|
31
|
+
'http',
|
|
32
|
+
'queue',
|
|
33
|
+
'scheduledTask',
|
|
34
|
+
])
|
|
23
35
|
|
|
24
36
|
// Add force-required services that might not be detected from function inspection
|
|
25
37
|
forceRequiredServices.forEach((service) => {
|
|
@@ -28,44 +40,59 @@ export const serializeServicesMap = (
|
|
|
28
40
|
}
|
|
29
41
|
})
|
|
30
42
|
|
|
31
|
-
// Create a map of services with true for all needed services
|
|
32
|
-
const servicesMap = Object.fromEntries(
|
|
33
|
-
Array.from(usedServices)
|
|
34
|
-
.sort()
|
|
35
|
-
.map((service) => [service, true])
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
// Generate the TypeScript code
|
|
39
|
-
const serviceKeys = Object.keys(servicesMap).sort()
|
|
40
|
-
|
|
41
43
|
// Services that are always required internally by the framework
|
|
42
44
|
const defaultServices = ['config', 'logger', 'variables', 'schema']
|
|
45
|
+
defaultServices.forEach((service) => usedServices.add(service))
|
|
43
46
|
|
|
44
|
-
//
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
// Create singleton services map: all singleton services with true/false based on usage
|
|
48
|
+
const singletonServicesMap: Record<string, boolean> = {}
|
|
49
|
+
allSingletonServices.forEach((service) => {
|
|
50
|
+
singletonServicesMap[service] = usedServices.has(service)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// Create session services map: all session services with true/false based on usage
|
|
54
|
+
// Exclude internal framework services (PikkuInteraction)
|
|
55
|
+
const sessionServicesMap: Record<string, boolean> = {}
|
|
56
|
+
allSessionServices.forEach((service) => {
|
|
57
|
+
if (!internalServices.has(service)) {
|
|
58
|
+
sessionServicesMap[service] = usedServices.has(service)
|
|
59
|
+
}
|
|
60
|
+
})
|
|
48
61
|
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
62
|
+
// Get all required service names (those marked as true)
|
|
63
|
+
const requiredSingletonServiceNames = Object.keys(singletonServicesMap)
|
|
64
|
+
.filter((key) => singletonServicesMap[key])
|
|
65
|
+
.sort()
|
|
66
|
+
const requiredSessionServiceNames = Object.keys(sessionServicesMap)
|
|
67
|
+
.filter((key) => sessionServicesMap[key])
|
|
68
|
+
.sort()
|
|
52
69
|
|
|
53
70
|
const code = [
|
|
54
71
|
servicesImport,
|
|
55
72
|
sessionServicesImport,
|
|
56
|
-
"import type { PikkuInteraction } from '@pikku/core'",
|
|
57
73
|
'',
|
|
58
|
-
'
|
|
59
|
-
|
|
74
|
+
'// Singleton services map: true if required, false if available but unused',
|
|
75
|
+
'export const requiredSingletonServices = {',
|
|
76
|
+
...Object.keys(singletonServicesMap)
|
|
77
|
+
.sort()
|
|
78
|
+
.map((service) => ` '${service}': ${singletonServicesMap[service]},`),
|
|
79
|
+
'} as const',
|
|
80
|
+
'',
|
|
81
|
+
'// Session services map: true if required, false if available but unused',
|
|
82
|
+
'export const requiredSessionServices = {',
|
|
83
|
+
...Object.keys(sessionServicesMap)
|
|
84
|
+
.sort()
|
|
85
|
+
.map((service) => ` '${service}': ${sessionServicesMap[service]},`),
|
|
60
86
|
'} as const',
|
|
61
87
|
'',
|
|
62
|
-
'//
|
|
63
|
-
|
|
64
|
-
|
|
88
|
+
'// Type exports',
|
|
89
|
+
requiredSingletonServiceNames.length > 0
|
|
90
|
+
? `export type RequiredSingletonServices = Pick<SingletonServices, ${requiredSingletonServiceNames.map((key) => `'${key}'`).join(' | ')}> & Partial<Omit<SingletonServices, ${requiredSingletonServiceNames.map((key) => `'${key}'`).join(' | ')}>>`
|
|
91
|
+
: 'export type RequiredSingletonServices = Partial<SingletonServices>',
|
|
65
92
|
'',
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
93
|
+
requiredSessionServiceNames.length > 0
|
|
94
|
+
? `export type RequiredSessionServices = Pick<Services, ${requiredSessionServiceNames.map((key) => `'${key}'`).join(' | ')}> & Partial<Omit<Services, ${requiredSessionServiceNames.map((key) => `'${key}'`).join(' | ')}>>`
|
|
95
|
+
: 'export type RequiredSessionServices = Partial<Services>',
|
|
69
96
|
'',
|
|
70
97
|
].join('\n')
|
|
71
98
|
|
|
@@ -95,6 +122,8 @@ export const pikkuServices: any = pikkuSessionlessFunc<void, void>({
|
|
|
95
122
|
const sessionServicesImport = `import type { ${sessionServicesType.type} } from '${getFileImportRelativePath(config.typesDeclarationFile, sessionServicesType.typePath, config.packageMappings)}'`
|
|
96
123
|
|
|
97
124
|
const servicesCode = serializeServicesMap(
|
|
125
|
+
visitState.serviceAggregation.allSingletonServices,
|
|
126
|
+
visitState.serviceAggregation.allSessionServices,
|
|
98
127
|
visitState.serviceAggregation.requiredServices,
|
|
99
128
|
config.forceRequiredServices,
|
|
100
129
|
servicesImport,
|
|
@@ -21,7 +21,8 @@ export const pikkuSchemas: any = pikkuSessionlessFunc<
|
|
|
21
21
|
visitState.functions.typesMap,
|
|
22
22
|
visitState.functions.meta,
|
|
23
23
|
visitState.http.meta,
|
|
24
|
-
config.schemasFromTypes
|
|
24
|
+
config.schemasFromTypes,
|
|
25
|
+
config.schema?.additionalProperties
|
|
25
26
|
)
|
|
26
27
|
|
|
27
28
|
await saveSchemas(
|
|
@@ -30,8 +31,8 @@ export const pikkuSchemas: any = pikkuSessionlessFunc<
|
|
|
30
31
|
schemas,
|
|
31
32
|
visitState.functions.typesMap,
|
|
32
33
|
visitState.functions.meta,
|
|
33
|
-
config.
|
|
34
|
-
config.
|
|
34
|
+
config.schemasFromTypes,
|
|
35
|
+
config.schema?.supportsImportAttributes
|
|
35
36
|
)
|
|
36
37
|
|
|
37
38
|
return true
|
|
@@ -27,7 +27,8 @@ export const pikkuOpenAPI: any = pikkuSessionlessFunc<void, void>({
|
|
|
27
27
|
functions.typesMap,
|
|
28
28
|
functions.meta,
|
|
29
29
|
http.meta,
|
|
30
|
-
schemasFromTypes
|
|
30
|
+
schemasFromTypes,
|
|
31
|
+
config.schema?.additionalProperties
|
|
31
32
|
)
|
|
32
33
|
const openAPISpec = await generateOpenAPISpec(
|
|
33
34
|
logger,
|
|
@@ -15,17 +15,18 @@ export const pikkuMiddleware: any = pikkuSessionlessFunc<
|
|
|
15
15
|
|
|
16
16
|
let filesGenerated = false
|
|
17
17
|
|
|
18
|
-
// Check if there are any middleware
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
// Check if there are any middleware groups
|
|
19
|
+
const hasHTTPGroups = state.http.routeMiddleware.size > 0
|
|
20
|
+
const hasTagGroups = state.middleware.tagMiddleware.size > 0
|
|
21
|
+
|
|
22
|
+
if (hasHTTPGroups || hasTagGroups) {
|
|
23
|
+
await writeFileInDir(
|
|
24
|
+
logger,
|
|
25
|
+
config.middlewareGroupsMetaFile,
|
|
26
|
+
serializeMiddlewareGroupsMeta(state)
|
|
27
|
+
)
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
if (hasFactories) {
|
|
29
|
+
// Always generate middleware imports file when groups exist (even if empty)
|
|
29
30
|
await writeFileInDir(
|
|
30
31
|
logger,
|
|
31
32
|
middlewareFile,
|
|
@@ -36,19 +37,7 @@ export const pikkuMiddleware: any = pikkuSessionlessFunc<
|
|
|
36
37
|
packageMappings
|
|
37
38
|
)
|
|
38
39
|
)
|
|
39
|
-
filesGenerated = true
|
|
40
|
-
}
|
|
41
40
|
|
|
42
|
-
// Generate middleware groups metadata file
|
|
43
|
-
const hasHTTPGroups = state.http.routeMiddleware.size > 0
|
|
44
|
-
const hasTagGroups = state.middleware.tagMiddleware.size > 0
|
|
45
|
-
|
|
46
|
-
if (hasHTTPGroups || hasTagGroups) {
|
|
47
|
-
await writeFileInDir(
|
|
48
|
-
logger,
|
|
49
|
-
config.middlewareGroupsMetaFile,
|
|
50
|
-
serializeMiddlewareGroupsMeta(state)
|
|
51
|
-
)
|
|
52
41
|
filesGenerated = true
|
|
53
42
|
}
|
|
54
43
|
|
|
@@ -15,21 +15,24 @@ export interface LogCommandInfoOptions {
|
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Middleware to log command execution timing and status
|
|
18
|
-
*
|
|
18
|
+
* Uses debug level so it only shows with --verbose flag
|
|
19
19
|
*/
|
|
20
20
|
export const logCommandInfoAndTime = ({
|
|
21
21
|
commandStart,
|
|
22
22
|
commandEnd,
|
|
23
23
|
}: LogCommandInfoOptions): PikkuMiddleware => {
|
|
24
24
|
return async ({ logger }, _interaction, next) => {
|
|
25
|
-
// Log start
|
|
25
|
+
// Log start (debug level - only shows with --verbose)
|
|
26
26
|
const start = Date.now()
|
|
27
|
-
logger.
|
|
27
|
+
logger.debug(`• ${commandStart}...`)
|
|
28
28
|
|
|
29
29
|
// Execute the function
|
|
30
30
|
await next()
|
|
31
31
|
|
|
32
|
-
// Log completion
|
|
33
|
-
logger.
|
|
32
|
+
// Log completion (debug level - only shows with --verbose)
|
|
33
|
+
logger.debug({
|
|
34
|
+
type: 'success',
|
|
35
|
+
message: `✓ ${commandEnd} in ${Date.now() - start}ms.`,
|
|
36
|
+
})
|
|
34
37
|
}
|
|
35
38
|
}
|
|
@@ -15,7 +15,7 @@ const BASE_ERROR_URL = 'https://pikku.dev/errors'
|
|
|
15
15
|
|
|
16
16
|
export class CLILogger implements Logger {
|
|
17
17
|
private silent: boolean
|
|
18
|
-
private level: LogLevel = LogLevel.
|
|
18
|
+
private level: LogLevel = LogLevel.warn // default to warn level
|
|
19
19
|
private criticalErrors: string[] = []
|
|
20
20
|
|
|
21
21
|
constructor({
|
|
@@ -27,7 +27,7 @@ export class CLILogger implements Logger {
|
|
|
27
27
|
}) {
|
|
28
28
|
this.silent = silent
|
|
29
29
|
if (logLogo && !silent) {
|
|
30
|
-
this.
|
|
30
|
+
this.logLogo()
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -35,6 +35,14 @@ export class CLILogger implements Logger {
|
|
|
35
35
|
this.level = level
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
setSilent(silent: boolean): void {
|
|
39
|
+
this.silent = silent
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
isSilent(): boolean {
|
|
43
|
+
return this.silent
|
|
44
|
+
}
|
|
45
|
+
|
|
38
46
|
info(message: string | { message: string; type?: string }) {
|
|
39
47
|
if (this.level > LogLevel.info || this.silent) return
|
|
40
48
|
|
|
@@ -59,9 +67,16 @@ export class CLILogger implements Logger {
|
|
|
59
67
|
console.error(chalk.yellow(message))
|
|
60
68
|
}
|
|
61
69
|
|
|
62
|
-
debug(message: string) {
|
|
70
|
+
debug(message: string | { message: string; type?: string }) {
|
|
63
71
|
if (this.level > LogLevel.debug || this.silent) return
|
|
64
|
-
|
|
72
|
+
|
|
73
|
+
let c = chalk.gray
|
|
74
|
+
if (typeof message === 'object') {
|
|
75
|
+
if (message.type === 'success') {
|
|
76
|
+
c = chalk.green
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
console.log(c(typeof message === 'string' ? message : message.message))
|
|
65
80
|
}
|
|
66
81
|
|
|
67
82
|
critical(code: ErrorCode, message: string) {
|
|
@@ -75,7 +90,7 @@ export class CLILogger implements Logger {
|
|
|
75
90
|
return this.criticalErrors.length > 0
|
|
76
91
|
}
|
|
77
92
|
|
|
78
|
-
|
|
93
|
+
logLogo() {
|
|
79
94
|
this.primary(logo)
|
|
80
95
|
// // When running from dist/, __filename is dist/src/services/cli-logger.service.js
|
|
81
96
|
// // So we need to go up 3 levels: dist/src/services -> dist/src -> dist -> package.json
|
package/src/services.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
CreateSingletonServices,
|
|
11
11
|
} from '@pikku/core'
|
|
12
12
|
import { pikkuCLIRender } from '@pikku/core/cli'
|
|
13
|
-
import { LocalVariablesService } from '@pikku/core/services'
|
|
13
|
+
import { LocalVariablesService, LogLevel } from '@pikku/core/services'
|
|
14
14
|
import { CLILogger } from './services/cli-logger.service.js'
|
|
15
15
|
import { getPikkuCLIConfig } from './utils/pikku-cli-config.js'
|
|
16
16
|
import {
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
serializeInspectorState,
|
|
21
21
|
deserializeInspectorState,
|
|
22
22
|
filterInspectorState,
|
|
23
|
+
getInitialInspectorState,
|
|
23
24
|
} from '@pikku/inspector'
|
|
24
25
|
import { glob } from 'tinyglobby'
|
|
25
26
|
import path from 'path'
|
|
@@ -30,7 +31,9 @@ import {
|
|
|
30
31
|
} from './services/cli-logger-forwarder.service.js'
|
|
31
32
|
import { readFile, writeFile } from 'fs/promises'
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
// Logger instance will be configured with log level from CLI flags in createConfig
|
|
35
|
+
// Logo will be displayed conditionally in createConfig based on --silent flag
|
|
36
|
+
const logger = new CLILogger({ logLogo: false, silent: false })
|
|
34
37
|
|
|
35
38
|
/**
|
|
36
39
|
* Parse a comma-separated string or array into an array of trimmed, non-empty strings
|
|
@@ -94,7 +97,7 @@ function parseCLIFilters(data: any): InspectorFilters {
|
|
|
94
97
|
export const defaultCLIRenderer = pikkuCLIRender<
|
|
95
98
|
ForwardedLogMessage,
|
|
96
99
|
SingletonServices
|
|
97
|
-
>((
|
|
100
|
+
>(({ logger }, data) => {
|
|
98
101
|
if (data) {
|
|
99
102
|
logger[data.level]({ message: data.message, type: data.type })
|
|
100
103
|
}
|
|
@@ -105,11 +108,9 @@ export const defaultCLIRenderer = pikkuCLIRender<
|
|
|
105
108
|
* This renderer can be used in CLI-over-channel clients
|
|
106
109
|
*/
|
|
107
110
|
export const clientCLIRenderer = pikkuCLIRender<ForwardedLogMessage>(
|
|
108
|
-
(
|
|
111
|
+
({ logger }, data) => {
|
|
109
112
|
if (data) {
|
|
110
|
-
|
|
111
|
-
const prefix = data.type ? `[${data.type}] ` : ''
|
|
112
|
-
console.log(`${prefix}${data.message}`)
|
|
113
|
+
logger[data.level]?.({ message: data.message, type: data.type } as any)
|
|
113
114
|
}
|
|
114
115
|
}
|
|
115
116
|
)
|
|
@@ -118,6 +119,37 @@ export const createConfig: CreateConfig<Config, [PikkuCLIConfig]> = async (
|
|
|
118
119
|
_variablesService,
|
|
119
120
|
data
|
|
120
121
|
) => {
|
|
122
|
+
// Determine log level based on CLI flags with precedence:
|
|
123
|
+
// --silent > --loglevel > --verbose > --info > default (warn)
|
|
124
|
+
let logLevel: LogLevel = LogLevel.warn // default
|
|
125
|
+
let isSilent = false
|
|
126
|
+
|
|
127
|
+
if ((data as any).silent) {
|
|
128
|
+
logLevel = LogLevel.critical
|
|
129
|
+
isSilent = true
|
|
130
|
+
} else if ((data as any).loglevel) {
|
|
131
|
+
const levelStr = (data as any).loglevel
|
|
132
|
+
if (LogLevel[levelStr] !== undefined) {
|
|
133
|
+
logLevel = LogLevel[levelStr as keyof typeof LogLevel]
|
|
134
|
+
} else {
|
|
135
|
+
logger.warn(
|
|
136
|
+
`Invalid log level "${levelStr}". Valid levels: trace, debug, info, warn, error, critical. Using default (warn).`
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
} else if ((data as any).verbose) {
|
|
140
|
+
logLevel = LogLevel.debug
|
|
141
|
+
} else if ((data as any).info) {
|
|
142
|
+
logLevel = LogLevel.info
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
logger.setLevel(logLevel)
|
|
146
|
+
logger.setSilent(isSilent)
|
|
147
|
+
|
|
148
|
+
// Display logo unless in silent mode
|
|
149
|
+
if (!isSilent) {
|
|
150
|
+
logger.logLogo()
|
|
151
|
+
}
|
|
152
|
+
|
|
121
153
|
const cliConfig = await getPikkuCLIConfig(logger, data.configFile, [], true)
|
|
122
154
|
|
|
123
155
|
// Load inspector state from file if stateInput is provided
|
|
@@ -170,19 +202,62 @@ export const createSingletonServices: CreateSingletonServices<
|
|
|
170
202
|
| Omit<InspectorState, 'typesLookup'>
|
|
171
203
|
| undefined = preloadedInspectorState
|
|
172
204
|
|
|
173
|
-
const getInspectorState = async (
|
|
205
|
+
const getInspectorState = async (
|
|
206
|
+
refresh: boolean = false,
|
|
207
|
+
setupOnly: boolean = false,
|
|
208
|
+
bootstrapMode: boolean = false
|
|
209
|
+
) => {
|
|
210
|
+
// In bootstrap mode, return a minimal "zero state" with core types
|
|
211
|
+
// This allows bootstrap to run immediately without inspecting the codebase
|
|
212
|
+
if (bootstrapMode) {
|
|
213
|
+
const corePackagePath = '@pikku/core'
|
|
214
|
+
const initialState = getInitialInspectorState(rootDir)
|
|
215
|
+
|
|
216
|
+
// Populate filesAndMethods with core types from @pikku/core
|
|
217
|
+
initialState.filesAndMethods = {
|
|
218
|
+
userSessionType: {
|
|
219
|
+
file: corePackagePath,
|
|
220
|
+
variable: 'CoreUserSession',
|
|
221
|
+
type: 'CoreUserSession',
|
|
222
|
+
typePath: corePackagePath,
|
|
223
|
+
},
|
|
224
|
+
sessionServicesType: {
|
|
225
|
+
file: corePackagePath,
|
|
226
|
+
variable: 'CoreServices',
|
|
227
|
+
type: 'CoreServices',
|
|
228
|
+
typePath: corePackagePath,
|
|
229
|
+
},
|
|
230
|
+
singletonServicesType: {
|
|
231
|
+
file: corePackagePath,
|
|
232
|
+
variable: 'CoreSingletonServices',
|
|
233
|
+
type: 'CoreSingletonServices',
|
|
234
|
+
typePath: corePackagePath,
|
|
235
|
+
},
|
|
236
|
+
pikkuConfigType: {
|
|
237
|
+
file: corePackagePath,
|
|
238
|
+
variable: 'CoreConfig',
|
|
239
|
+
type: 'CoreConfig',
|
|
240
|
+
typePath: corePackagePath,
|
|
241
|
+
},
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return initialState
|
|
245
|
+
}
|
|
246
|
+
|
|
174
247
|
// Get or refresh the unfiltered state
|
|
175
248
|
if (!unfilteredState || refresh) {
|
|
176
249
|
// Run inspector WITHOUT filters to get full state
|
|
177
250
|
const wiringFiles = (
|
|
178
251
|
await Promise.all(
|
|
179
252
|
srcDirectories.map((dir) =>
|
|
180
|
-
glob(`${path.join(rootDir, dir)}/**/*.ts
|
|
253
|
+
glob(`${path.join(rootDir, dir)}/**/*.ts`, {
|
|
254
|
+
ignore: config.ignoreFiles || [],
|
|
255
|
+
})
|
|
181
256
|
)
|
|
182
257
|
)
|
|
183
258
|
).flat()
|
|
184
|
-
unfilteredState =
|
|
185
|
-
|
|
259
|
+
unfilteredState = inspect(logger, wiringFiles, {
|
|
260
|
+
setupOnly,
|
|
186
261
|
types: {
|
|
187
262
|
configFileType: config.configFile,
|
|
188
263
|
userSessionType: config.userSessionType,
|
|
@@ -13,8 +13,23 @@ export const checkRequiredTypes = (
|
|
|
13
13
|
errors: FilesAndMethodsErrors,
|
|
14
14
|
requires: RequiredTypes = {}
|
|
15
15
|
): void => {
|
|
16
|
+
// Filter out errors that are about missing Types when we have the corresponding Factory
|
|
17
|
+
// e.g., if we have a CreateConfig factory, we don't need a CoreConfig type
|
|
18
|
+
const errorMessages = Array.from(errors.keys())
|
|
19
|
+
const hasCreateConfigFactory = errorMessages.every(
|
|
20
|
+
(msg) => !msg.includes('No CreateConfig found')
|
|
21
|
+
)
|
|
22
|
+
|
|
16
23
|
// Only throw if there are errors AND we require those types
|
|
17
|
-
const hasRequiredErrors =
|
|
24
|
+
const hasRequiredErrors = errorMessages.some((message) => {
|
|
25
|
+
// Skip CoreConfig type errors if we have a CreateConfig factory
|
|
26
|
+
if (
|
|
27
|
+
requires.config &&
|
|
28
|
+
message.includes('No CoreConfig found') &&
|
|
29
|
+
hasCreateConfigFactory
|
|
30
|
+
) {
|
|
31
|
+
return false
|
|
32
|
+
}
|
|
18
33
|
if (requires.config && message.includes('CoreConfig')) return true
|
|
19
34
|
if (requires.sessionServiceType && message.includes('CoreServices'))
|
|
20
35
|
return true
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import chalk from 'chalk'
|
|
2
|
+
|
|
3
|
+
export interface CommandSummaryStats {
|
|
4
|
+
httpRoutes?: number
|
|
5
|
+
channels?: number
|
|
6
|
+
functions?: number
|
|
7
|
+
scheduledTasks?: number
|
|
8
|
+
queueWorkers?: number
|
|
9
|
+
mcpEndpoints?: number
|
|
10
|
+
cliCommands?: number
|
|
11
|
+
[key: string]: number | undefined
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Utility class to collect and display command execution summaries
|
|
16
|
+
*/
|
|
17
|
+
export class CommandSummary {
|
|
18
|
+
private stats: CommandSummaryStats = {}
|
|
19
|
+
private startTime: number
|
|
20
|
+
private commandName: string
|
|
21
|
+
|
|
22
|
+
constructor(commandName: string) {
|
|
23
|
+
this.commandName = commandName
|
|
24
|
+
this.startTime = Date.now()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Increment a stat counter
|
|
29
|
+
*/
|
|
30
|
+
increment(key: keyof CommandSummaryStats, count: number = 1): void {
|
|
31
|
+
this.stats[key] = (this.stats[key] || 0) + count
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Set a stat value directly
|
|
36
|
+
*/
|
|
37
|
+
set(key: keyof CommandSummaryStats, value: number): void {
|
|
38
|
+
this.stats[key] = value
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get current stats
|
|
43
|
+
*/
|
|
44
|
+
getStats(): CommandSummaryStats {
|
|
45
|
+
return { ...this.stats }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get elapsed time in milliseconds
|
|
50
|
+
*/
|
|
51
|
+
getElapsedTime(): number {
|
|
52
|
+
return Date.now() - this.startTime
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Format the summary for display
|
|
57
|
+
*/
|
|
58
|
+
format(): string {
|
|
59
|
+
const elapsed = this.getElapsedTime()
|
|
60
|
+
const lines: string[] = []
|
|
61
|
+
|
|
62
|
+
// Header with timing
|
|
63
|
+
lines.push(
|
|
64
|
+
chalk.green(`\npikku ${this.commandName} (completed in ${elapsed}ms)`)
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
// Stats
|
|
68
|
+
const statLabels: Record<keyof CommandSummaryStats, string> = {
|
|
69
|
+
httpRoutes: 'HTTP routes',
|
|
70
|
+
channels: 'WebSocket channels',
|
|
71
|
+
functions: 'functions',
|
|
72
|
+
scheduledTasks: 'scheduled tasks',
|
|
73
|
+
queueWorkers: 'queue workers',
|
|
74
|
+
mcpEndpoints: 'MCP endpoints',
|
|
75
|
+
cliCommands: 'CLI commands',
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
for (const [key, label] of Object.entries(statLabels)) {
|
|
79
|
+
const value = this.stats[key]
|
|
80
|
+
if (value !== undefined && value > 0) {
|
|
81
|
+
lines.push(chalk.gray(` • ${value} ${label}`))
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// If no stats, show a simple completion message
|
|
86
|
+
if (Object.values(this.stats).every((v) => !v || v === 0)) {
|
|
87
|
+
return chalk.green(
|
|
88
|
+
`\npikku ${this.commandName} (completed in ${elapsed}ms)\n`
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return lines.join('\n') + '\n'
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Check if there are any stats to display
|
|
97
|
+
*/
|
|
98
|
+
hasStats(): boolean {
|
|
99
|
+
return Object.values(this.stats).some((v) => v && v > 0)
|
|
100
|
+
}
|
|
101
|
+
}
|
package/src/utils/file-writer.ts
CHANGED
|
@@ -51,7 +51,7 @@ export const writeFileInDir = async (
|
|
|
51
51
|
await writeFile(path, content, 'utf-8')
|
|
52
52
|
|
|
53
53
|
if (logWrite) {
|
|
54
|
-
logger.
|
|
54
|
+
logger.debug({ message: `✓ File written to ${path}`, type: 'success' })
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
}
|
|
@@ -66,7 +66,7 @@ export const removeFileInDir = async (
|
|
|
66
66
|
await rm(path, { force: true })
|
|
67
67
|
|
|
68
68
|
if (logRemove) {
|
|
69
|
-
logger.
|
|
69
|
+
logger.debug({ message: `✓ File removed at ${path}`, type: 'success' })
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
}
|
|
@@ -64,6 +64,21 @@ const _getPikkuCLIConfig = async (
|
|
|
64
64
|
...extendedConfig.packageMappings,
|
|
65
65
|
...config.packageMappings,
|
|
66
66
|
},
|
|
67
|
+
ignoreFiles: config.ignoreFiles ??
|
|
68
|
+
extendedConfig.ignoreFiles ?? [
|
|
69
|
+
'**/*.gen.ts',
|
|
70
|
+
'**/*.test.ts',
|
|
71
|
+
'**/*.spec.ts',
|
|
72
|
+
'**/node_modules/**',
|
|
73
|
+
'**/.pikku/**',
|
|
74
|
+
'**/dist/**',
|
|
75
|
+
],
|
|
76
|
+
schema: {
|
|
77
|
+
additionalProperties: false,
|
|
78
|
+
supportsImportAttributes: false,
|
|
79
|
+
...extendedConfig.schema,
|
|
80
|
+
...config.schema,
|
|
81
|
+
},
|
|
67
82
|
}
|
|
68
83
|
} else {
|
|
69
84
|
result = {
|
|
@@ -73,6 +88,19 @@ const _getPikkuCLIConfig = async (
|
|
|
73
88
|
rootDir: config.rootDir
|
|
74
89
|
? resolve(configDir, config.rootDir)
|
|
75
90
|
: configDir,
|
|
91
|
+
ignoreFiles: config.ignoreFiles ?? [
|
|
92
|
+
'**/*.gen.ts',
|
|
93
|
+
'**/*.test.ts',
|
|
94
|
+
'**/*.spec.ts',
|
|
95
|
+
'**/node_modules/**',
|
|
96
|
+
'**/.pikku/**',
|
|
97
|
+
'**/dist/**',
|
|
98
|
+
],
|
|
99
|
+
schema: {
|
|
100
|
+
additionalProperties: false,
|
|
101
|
+
supportsImportAttributes: false,
|
|
102
|
+
...config.schema,
|
|
103
|
+
},
|
|
76
104
|
}
|
|
77
105
|
}
|
|
78
106
|
|
|
@@ -12,7 +12,8 @@ export async function generateSchemas(
|
|
|
12
12
|
typesMap: TypesMap,
|
|
13
13
|
functionMeta: FunctionsMeta,
|
|
14
14
|
httpWiringsMeta: HTTPWiringsMeta,
|
|
15
|
-
additionalTypes?: string[]
|
|
15
|
+
additionalTypes?: string[],
|
|
16
|
+
additionalProperties: boolean = false
|
|
16
17
|
): Promise<Record<string, JSONValue>> {
|
|
17
18
|
const schemasSet = new Set(typesMap.customTypes.keys())
|
|
18
19
|
for (const { inputs, outputs } of Object.values(functionMeta)) {
|
|
@@ -56,7 +57,7 @@ export async function generateSchemas(
|
|
|
56
57
|
sortProps: true,
|
|
57
58
|
strictTuples: false,
|
|
58
59
|
encodeRefs: false,
|
|
59
|
-
additionalProperties
|
|
60
|
+
additionalProperties,
|
|
60
61
|
})
|
|
61
62
|
const schemas: Record<string, JSONValue> = {}
|
|
62
63
|
|
|
@@ -86,8 +87,8 @@ export async function saveSchemas(
|
|
|
86
87
|
schemas: Record<string, JSONValue>,
|
|
87
88
|
typesMap: TypesMap,
|
|
88
89
|
functionsMeta: FunctionsMeta,
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
additionalTypes?: string[],
|
|
91
|
+
supportsImportAttributes: boolean = false
|
|
91
92
|
) {
|
|
92
93
|
await writeFileInDir(
|
|
93
94
|
logger,
|