@pikku/cli 0.9.2 → 0.9.4
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 +24 -0
- package/bin/pikku-all.ts +10 -3
- package/bin/pikku-rpc.ts +28 -0
- package/bin/pikku.ts +4 -0
- package/cli.schema.json +4 -0
- package/dist/bin/pikku-all.js +7 -3
- package/dist/bin/pikku-rpc.d.ts +4 -0
- package/dist/bin/pikku-rpc.js +19 -0
- package/dist/bin/pikku.js +4 -0
- package/dist/src/pikku-cli-config.d.ts +3 -0
- package/dist/src/pikku-cli-config.js +15 -4
- package/dist/src/serialize-import-map.js +62 -1
- package/dist/src/serialize-pikku-types.js +77 -5
- package/dist/src/utils.js +24 -2
- package/dist/src/wirings/functions/pikku-command-function-types.js +2 -2
- package/dist/src/wirings/functions/pikku-command-functions.d.ts +0 -4
- package/dist/src/wirings/functions/pikku-command-functions.js +10 -31
- package/dist/src/wirings/functions/pikku-command-services.js +1 -1
- package/dist/src/wirings/functions/pikku-function-types.js +2 -2
- package/dist/src/wirings/functions/serialize-function-imports.d.ts +6 -0
- package/dist/src/wirings/functions/serialize-function-imports.js +59 -0
- package/dist/src/wirings/rpc/pikku-command-rpc-client.js +2 -2
- package/dist/src/wirings/rpc/pikku-command-rpc-map.d.ts +2 -1
- package/dist/src/wirings/rpc/pikku-command-rpc-map.js +9 -3
- package/dist/src/wirings/rpc/pikku-command-rpc.js +7 -2
- package/dist/src/wirings/rpc/serialize-typed-rpc-map.d.ts +1 -2
- package/dist/src/wirings/rpc/serialize-typed-rpc-map.js +2 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/pikku-cli-config.ts +35 -5
- package/src/serialize-import-map.ts +67 -2
- package/src/serialize-pikku-types.ts +77 -5
- package/src/utils.ts +31 -2
- package/src/wirings/functions/pikku-command-function-types.ts +6 -2
- package/src/wirings/functions/pikku-command-functions.ts +28 -64
- package/src/wirings/functions/pikku-command-services.ts +1 -1
- package/src/wirings/functions/pikku-function-types.ts +6 -2
- package/src/wirings/functions/serialize-function-imports.ts +97 -0
- package/src/wirings/rpc/pikku-command-rpc-client.ts +8 -4
- package/src/wirings/rpc/pikku-command-rpc-map.ts +27 -4
- package/src/wirings/rpc/pikku-command-rpc.ts +16 -6
- package/src/wirings/rpc/serialize-typed-rpc-map.ts +4 -5
- package/dist/src/wirings/functions/pikku-functions.d.ts +0 -6
- package/dist/src/wirings/functions/pikku-functions.js +0 -35
- package/dist/src/wirings/rpc/pikku-rpc.d.ts +0 -2
- package/dist/src/wirings/rpc/pikku-rpc.js +0 -6
- package/src/wirings/functions/pikku-functions.ts +0 -84
- package/src/wirings/rpc/pikku-rpc.ts +0 -22
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { getFileImportRelativePath } from '../../utils.js';
|
|
2
|
+
export const serializeFunctionImports = (outputPath, functionsMap, functionsMeta, packageMappings = {}) => {
|
|
3
|
+
const serializedImports = [
|
|
4
|
+
`/* Import and register functions used by RPCs */`,
|
|
5
|
+
`import { addFunction } from '@pikku/core'`,
|
|
6
|
+
];
|
|
7
|
+
const serializedRegistrations = [];
|
|
8
|
+
// Sort by function name for consistent output
|
|
9
|
+
const sortedEntries = Array.from(functionsMap.entries()).sort((a, b) => a[0].localeCompare(b[0]));
|
|
10
|
+
for (const [name, { path, exportedName }] of sortedEntries) {
|
|
11
|
+
const filePath = getFileImportRelativePath(outputPath, path, packageMappings);
|
|
12
|
+
// Find the function metadata to check if it's a direct function
|
|
13
|
+
// The functionsMeta is keyed by the function name (exported name)
|
|
14
|
+
const funcMeta = functionsMeta[name];
|
|
15
|
+
const isDirectFunction = funcMeta?.isDirectFunction ?? false;
|
|
16
|
+
// For directly exported functions, we can just import and register them
|
|
17
|
+
if (name === exportedName) {
|
|
18
|
+
serializedImports.push(`import { ${exportedName} } from '${filePath}'`);
|
|
19
|
+
if (isDirectFunction) {
|
|
20
|
+
// Direct function: pikkuFunc(fn) - needs to be wrapped
|
|
21
|
+
serializedRegistrations.push(`addFunction('${name}', { func: ${exportedName} })`);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
// Object format: pikkuFunc({ func: fn }) - can be used directly
|
|
25
|
+
serializedRegistrations.push(`addFunction('${name}', ${exportedName} as any) // TODO`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// For renamed functions, we need to import and alias them
|
|
29
|
+
else {
|
|
30
|
+
serializedImports.push(`import { ${exportedName} as ${name} } from '${filePath}'`);
|
|
31
|
+
if (isDirectFunction) {
|
|
32
|
+
// Direct function: pikkuFunc(fn) - needs to be wrapped
|
|
33
|
+
serializedRegistrations.push(`addFunction('${name}', { func: ${name} })`);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
// Object format: pikkuFunc({ func: fn }) - can be used directly
|
|
37
|
+
serializedRegistrations.push(`addFunction('${name}', ${name} as any) // TODO`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Add a blank line between imports and registrations
|
|
42
|
+
if (serializedImports.length > 0 && serializedRegistrations.length > 0) {
|
|
43
|
+
serializedImports.push('');
|
|
44
|
+
}
|
|
45
|
+
// Combine the imports and registrations
|
|
46
|
+
return [...serializedImports, ...serializedRegistrations].join('\n');
|
|
47
|
+
};
|
|
48
|
+
export const generateRuntimeMeta = (functions) => {
|
|
49
|
+
const runtimeMeta = {};
|
|
50
|
+
for (const [key, { pikkuFuncName, inputSchemaName, outputSchemaName, expose },] of Object.entries(functions)) {
|
|
51
|
+
runtimeMeta[key] = {
|
|
52
|
+
pikkuFuncName,
|
|
53
|
+
inputSchemaName,
|
|
54
|
+
outputSchemaName,
|
|
55
|
+
expose,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return runtimeMeta;
|
|
59
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { serializeRPCWrapper } from './serialize-rpc-wrapper.js';
|
|
2
2
|
import { getFileImportRelativePath, logCommandInfoAndTime, writeFileInDir, } from '../../utils.js';
|
|
3
|
-
export const pikkuRPCClient = async (logger, { rpcWiringsFile, rpcMapDeclarationFile, packageMappings }) => {
|
|
4
|
-
return await logCommandInfoAndTime(logger, 'Generating RPC
|
|
3
|
+
export const pikkuRPCClient = async (logger, { rpcWiringsFile, rpcMapDeclarationFile, rpcInternalMapDeclarationFile, packageMappings, }) => {
|
|
4
|
+
return await logCommandInfoAndTime(logger, 'Generating RPC wrappers', 'Generated RPC wrappers', [
|
|
5
5
|
rpcWiringsFile === undefined || rpcWiringsFile === null,
|
|
6
6
|
"rpcWiringsFile isn't set in the pikku config",
|
|
7
7
|
], async () => {
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { logCommandInfoAndTime, writeFileInDir } from '../../utils.js';
|
|
2
2
|
import { serializeTypedRPCMap } from './serialize-typed-rpc-map.js';
|
|
3
|
-
export const
|
|
4
|
-
return await logCommandInfoAndTime(logger, 'Creating RPC map', 'Created RPC map', [false], async () => {
|
|
5
|
-
const content = serializeTypedRPCMap(
|
|
3
|
+
export const pikkuRPCInternalMap = async (logger, { rpcInternalMapDeclarationFile, packageMappings }, { functions, rpc }) => {
|
|
4
|
+
return await logCommandInfoAndTime(logger, 'Creating RPC internal map', 'Created RPC internal map', [false], async () => {
|
|
5
|
+
const content = serializeTypedRPCMap(rpcInternalMapDeclarationFile, packageMappings, functions.typesMap, functions.meta, rpc.internalMeta);
|
|
6
|
+
await writeFileInDir(logger, rpcInternalMapDeclarationFile, content);
|
|
7
|
+
});
|
|
8
|
+
};
|
|
9
|
+
export const pikkuRPCExposedMap = async (logger, { rpcMapDeclarationFile, packageMappings }, { functions, rpc }) => {
|
|
10
|
+
return await logCommandInfoAndTime(logger, 'Creating RPC external map', 'Created RPC external map', [false], async () => {
|
|
11
|
+
const content = serializeTypedRPCMap(rpcMapDeclarationFile, packageMappings, functions.typesMap, functions.meta, rpc.exposedMeta);
|
|
6
12
|
await writeFileInDir(logger, rpcMapDeclarationFile, content);
|
|
7
13
|
});
|
|
8
14
|
};
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { logCommandInfoAndTime, writeFileInDir } from '../../utils.js';
|
|
2
|
-
export const pikkuRPC = async (logger, { rpcWiringMetaFile }, { rpc }) => {
|
|
2
|
+
export const pikkuRPC = async (logger, { rpcInternalWiringMetaFile, rpcWiringMetaFile }, { rpc }) => {
|
|
3
3
|
return await logCommandInfoAndTime(logger, 'Finding RPCs tasks', 'Found RPCs', [false], async () => {
|
|
4
|
-
|
|
4
|
+
if (rpc.internalFiles.size > 0) {
|
|
5
|
+
await writeFileInDir(logger, rpcInternalWiringMetaFile, `import { pikkuState } from '@pikku/core'\npikkuState('rpc', 'meta', ${JSON.stringify(rpc.internalMeta, null, 2)})`);
|
|
6
|
+
}
|
|
7
|
+
if (rpc.exposedFiles.size > 0) {
|
|
8
|
+
await writeFileInDir(logger, rpcWiringMetaFile, `import { pikkuState } from '@pikku/core'\npikkuState('rpc', 'meta', ${JSON.stringify(rpc.exposedFiles, null, 2)})`);
|
|
9
|
+
}
|
|
5
10
|
});
|
|
6
11
|
};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { RPCMeta } from '@pikku/core/rpc';
|
|
2
1
|
import { TypesMap } from '@pikku/inspector';
|
|
3
2
|
import { FunctionsMeta } from '@pikku/core';
|
|
4
|
-
export declare const serializeTypedRPCMap: (relativeToPath: string, packageMappings: Record<string, string>, typesMap: TypesMap, functionsMeta: FunctionsMeta, rpcMeta: Record<string,
|
|
3
|
+
export declare const serializeTypedRPCMap: (relativeToPath: string, packageMappings: Record<string, string>, typesMap: TypesMap, functionsMeta: FunctionsMeta, rpcMeta: Record<string, string>) => string;
|
|
@@ -31,6 +31,7 @@ export type TypedPikkuRPC = {
|
|
|
31
31
|
depth: number;
|
|
32
32
|
global: boolean;
|
|
33
33
|
invoke: RPCInvoke;
|
|
34
|
+
invokeExposed: (name: string, data: any) => Promise<any>
|
|
34
35
|
}
|
|
35
36
|
`;
|
|
36
37
|
};
|
|
@@ -38,7 +39,7 @@ function generateRPCs(rpcMeta, functionsMeta, typesMap, requiredTypes) {
|
|
|
38
39
|
// Initialize an object to collect RPCs
|
|
39
40
|
const rpcsObj = {};
|
|
40
41
|
// Iterate through RPC metadata
|
|
41
|
-
for (const [funcName,
|
|
42
|
+
for (const [funcName, pikkuFuncName] of Object.entries(rpcMeta)) {
|
|
42
43
|
const functionMeta = functionsMeta[pikkuFuncName];
|
|
43
44
|
if (!functionMeta) {
|
|
44
45
|
throw new Error(`Function ${funcName} not found in functionsMeta. Please check your configuration.`);
|
|
@@ -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/runtimes/nextjs/pikku-command-nextjs.ts","../src/runtimes/nextjs/serialize-nextjs-backend-wrapper.ts","../src/runtimes/nextjs/serialize-nextjs-http-wrapper.ts","../src/wirings/channels/pikku-channels.ts","../src/wirings/channels/pikku-command-channels-map.ts","../src/wirings/channels/pikku-command-channels.ts","../src/wirings/channels/pikku-command-websocket-typed.ts","../src/wirings/channels/serialize-typed-channel-map.ts","../src/wirings/channels/serialize-websocket-wrapper.ts","../src/wirings/fetch/index.ts","../src/wirings/functions/pikku-command-function-types.ts","../src/wirings/functions/pikku-command-functions.ts","../src/wirings/functions/pikku-command-services.ts","../src/wirings/functions/pikku-function-types.ts","../src/wirings/functions/
|
|
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-rpc.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/runtimes/nextjs/pikku-command-nextjs.ts","../src/runtimes/nextjs/serialize-nextjs-backend-wrapper.ts","../src/runtimes/nextjs/serialize-nextjs-http-wrapper.ts","../src/wirings/channels/pikku-channels.ts","../src/wirings/channels/pikku-command-channels-map.ts","../src/wirings/channels/pikku-command-channels.ts","../src/wirings/channels/pikku-command-websocket-typed.ts","../src/wirings/channels/serialize-typed-channel-map.ts","../src/wirings/channels/serialize-websocket-wrapper.ts","../src/wirings/fetch/index.ts","../src/wirings/functions/pikku-command-function-types.ts","../src/wirings/functions/pikku-command-functions.ts","../src/wirings/functions/pikku-command-services.ts","../src/wirings/functions/pikku-function-types.ts","../src/wirings/functions/serialize-function-imports.ts","../src/wirings/http/openapi-spec-generator.ts","../src/wirings/http/pikku-command-http-map.ts","../src/wirings/http/pikku-command-http-routes.ts","../src/wirings/http/pikku-command-openapi.ts","../src/wirings/http/pikku-http-routes.ts","../src/wirings/http/serialize-fetch-wrapper.ts","../src/wirings/http/serialize-typed-http-map.ts","../src/wirings/mcp/pikku-command-mcp-json.ts","../src/wirings/mcp/pikku-command-mcp.ts","../src/wirings/mcp/serialize-mcp-json.ts","../src/wirings/queue/pikku-command-queue-map.ts","../src/wirings/queue/pikku-command-queue-service.ts","../src/wirings/queue/pikku-command-queue.ts","../src/wirings/queue/pikku-queue-map.ts","../src/wirings/queue/pikku-queue.ts","../src/wirings/queue/serialize-queue-map.ts","../src/wirings/queue/serialize-queue-meta.ts","../src/wirings/queue/serialize-queue-wrapper.ts","../src/wirings/rpc/pikku-command-rpc-client.ts","../src/wirings/rpc/pikku-command-rpc-map.ts","../src/wirings/rpc/pikku-command-rpc.ts","../src/wirings/rpc/serialize-rpc-wrapper.ts","../src/wirings/rpc/serialize-typed-rpc-map.ts","../src/wirings/scheduler/pikku-command-scheduler.ts","../src/wirings/scheduler/serialize-scheduler-meta.ts"],"version":"5.9.2"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/cli",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
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": "^4.0.2",
|
|
25
|
-
"@pikku/core": "^0.9.
|
|
26
|
-
"@pikku/inspector": "^0.9.
|
|
25
|
+
"@pikku/core": "^0.9.4",
|
|
26
|
+
"@pikku/inspector": "^0.9.3",
|
|
27
27
|
"@types/cookie": "^1.0.0",
|
|
28
28
|
"@types/uuid": "^10.0.0",
|
|
29
29
|
"chalk": "^5.5.0",
|
package/src/pikku-cli-config.ts
CHANGED
|
@@ -15,6 +15,7 @@ export interface PikkuCLICoreOutputFiles {
|
|
|
15
15
|
// Function definitions
|
|
16
16
|
functionsFile: string
|
|
17
17
|
functionsMetaFile: string
|
|
18
|
+
functionsMetaMinFile: string
|
|
18
19
|
|
|
19
20
|
// HTTP
|
|
20
21
|
httpWiringsFile: string
|
|
@@ -26,7 +27,11 @@ export interface PikkuCLICoreOutputFiles {
|
|
|
26
27
|
channelsWiringMetaFile: string
|
|
27
28
|
channelsMapDeclarationFile: string
|
|
28
29
|
|
|
29
|
-
// RPC
|
|
30
|
+
// RPC Internal
|
|
31
|
+
rpcInternalWiringMetaFile: string
|
|
32
|
+
rpcInternalMapDeclarationFile: string
|
|
33
|
+
|
|
34
|
+
// RPC Exposed
|
|
30
35
|
rpcWiringMetaFile: string
|
|
31
36
|
rpcMapDeclarationFile: string
|
|
32
37
|
|
|
@@ -166,7 +171,8 @@ const _getPikkuCLIConfig = async (
|
|
|
166
171
|
const functionDir = join(result.outDir, 'function')
|
|
167
172
|
const httpDir = join(result.outDir, 'http')
|
|
168
173
|
const channelDir = join(result.outDir, 'channel')
|
|
169
|
-
const
|
|
174
|
+
const internalRPCDirectory = join(result.outDir, 'rpc-internal')
|
|
175
|
+
const externalRPCDirectory = join(result.outDir, 'rpc')
|
|
170
176
|
const schedulerDir = join(result.outDir, 'scheduler')
|
|
171
177
|
const queueDir = join(result.outDir, 'queue')
|
|
172
178
|
const mcpDir = join(result.outDir, 'mcp')
|
|
@@ -187,6 +193,12 @@ const _getPikkuCLIConfig = async (
|
|
|
187
193
|
'pikku-functions-meta.gen.ts'
|
|
188
194
|
)
|
|
189
195
|
}
|
|
196
|
+
if (!result.functionsMetaMinFile) {
|
|
197
|
+
result.functionsMetaMinFile = join(
|
|
198
|
+
functionDir,
|
|
199
|
+
'pikku-functions-meta.min.gen.ts'
|
|
200
|
+
)
|
|
201
|
+
}
|
|
190
202
|
if (!result.typesDeclarationFile) {
|
|
191
203
|
result.typesDeclarationFile = join(result.outDir, 'pikku-types.gen.ts')
|
|
192
204
|
}
|
|
@@ -225,13 +237,31 @@ const _getPikkuCLIConfig = async (
|
|
|
225
237
|
)
|
|
226
238
|
}
|
|
227
239
|
|
|
228
|
-
//
|
|
240
|
+
// Internal
|
|
241
|
+
if (!result.rpcInternalWiringMetaFile) {
|
|
242
|
+
result.rpcInternalWiringMetaFile = join(
|
|
243
|
+
internalRPCDirectory,
|
|
244
|
+
'pikku-rpc-wirings-meta.internal.gen.ts'
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (!result.rpcInternalMapDeclarationFile) {
|
|
249
|
+
result.rpcInternalMapDeclarationFile = join(
|
|
250
|
+
internalRPCDirectory,
|
|
251
|
+
'pikku-rpc-wirings-map.internal.gen.d.ts'
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// External
|
|
229
256
|
if (!result.rpcWiringMetaFile) {
|
|
230
|
-
result.rpcWiringMetaFile = join(
|
|
257
|
+
result.rpcWiringMetaFile = join(
|
|
258
|
+
externalRPCDirectory,
|
|
259
|
+
'pikku-rpc-wirings-meta.gen.ts'
|
|
260
|
+
)
|
|
231
261
|
}
|
|
232
262
|
if (!result.rpcMapDeclarationFile) {
|
|
233
263
|
result.rpcMapDeclarationFile = join(
|
|
234
|
-
|
|
264
|
+
externalRPCDirectory,
|
|
235
265
|
'pikku-rpc-wirings-map.gen.d.ts'
|
|
236
266
|
)
|
|
237
267
|
}
|
|
@@ -9,8 +9,73 @@ export const serializeImportMap = (
|
|
|
9
9
|
) => {
|
|
10
10
|
const paths = new Map<string, string[]>()
|
|
11
11
|
Array.from(requiredTypes).forEach((requiredType) => {
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
let originalName, uniqueName, path
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const typeMeta = typesMap.getTypeMeta(requiredType)
|
|
16
|
+
originalName = typeMeta.originalName
|
|
17
|
+
uniqueName = typeMeta.uniqueName
|
|
18
|
+
path = typeMeta.path
|
|
19
|
+
} catch (e) {
|
|
20
|
+
// Handle missing types by trying to find a suitable import path
|
|
21
|
+
// Look through all existing types in the map to find a path that might contain this type
|
|
22
|
+
let foundPath: string | null = null
|
|
23
|
+
|
|
24
|
+
// Get all unique paths from the typesMap
|
|
25
|
+
const allPaths = new Set<string>()
|
|
26
|
+
typesMap.customTypes.forEach(({ type, references }) => {
|
|
27
|
+
references.forEach((ref) => {
|
|
28
|
+
try {
|
|
29
|
+
const refMeta = typesMap.getTypeMeta(ref)
|
|
30
|
+
if (refMeta.path) {
|
|
31
|
+
allPaths.add(refMeta.path)
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
// Continue
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// Also check direct types in the map
|
|
40
|
+
try {
|
|
41
|
+
const mapEntries = (typesMap as any).map?.entries?.() || []
|
|
42
|
+
for (const [_, typeMeta] of mapEntries) {
|
|
43
|
+
if (typeMeta.path) {
|
|
44
|
+
allPaths.add(typeMeta.path)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
} catch {
|
|
48
|
+
// Continue
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// For PascalCase types, prefer paths that look like type definition files
|
|
52
|
+
if (/^[A-Z]/.test(requiredType)) {
|
|
53
|
+
for (const candidatePath of allPaths) {
|
|
54
|
+
if (
|
|
55
|
+
candidatePath.includes('types') ||
|
|
56
|
+
candidatePath.includes('.d.')
|
|
57
|
+
) {
|
|
58
|
+
foundPath = candidatePath
|
|
59
|
+
break
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// If no types file found, use the first available path
|
|
64
|
+
if (!foundPath && allPaths.size > 0) {
|
|
65
|
+
foundPath = Array.from(allPaths)[0] || null
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (foundPath) {
|
|
70
|
+
originalName = requiredType
|
|
71
|
+
uniqueName = requiredType
|
|
72
|
+
path = foundPath
|
|
73
|
+
} else {
|
|
74
|
+
// No suitable path found, skip
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
14
79
|
if (!path) {
|
|
15
80
|
// This is a custom type that exists in file, so we don't need to import it
|
|
16
81
|
return
|
|
@@ -13,7 +13,7 @@ export const serializePikkuTypes = (
|
|
|
13
13
|
* This is used to provide the application types in the typescript project
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import { CorePikkuFunctionConfig, CorePikkuPermission, CorePikkuMiddleware, addHTTPMiddleware } from '@pikku/core'
|
|
16
|
+
import { CorePikkuFunctionConfig, CorePikkuPermission, CorePikkuMiddleware, addHTTPMiddleware, addMiddleware, addMiddlewareForTags, addPermission } from '@pikku/core'
|
|
17
17
|
import { CorePikkuFunction, CorePikkuFunctionSessionless } from '@pikku/core/function'
|
|
18
18
|
import { CoreHTTPFunctionWiring, AssertHTTPWiringParams, wireHTTP as wireHTTPCore } from '@pikku/core/http'
|
|
19
19
|
import { CoreScheduledTask, wireScheduler as wireSchedulerCore } from '@pikku/core/scheduler'
|
|
@@ -58,7 +58,7 @@ type PikkuFunctionSessionless<
|
|
|
58
58
|
Out = never,
|
|
59
59
|
ChannelData = null, // null means optional channel
|
|
60
60
|
MCPData = null, // null means optional MCP
|
|
61
|
-
RequiredServices extends Services = Services &
|
|
61
|
+
RequiredServices extends Services = Omit<Services, 'rpc'> &
|
|
62
62
|
{ rpc: TypedPikkuRPC } & (
|
|
63
63
|
[ChannelData] extends [null]
|
|
64
64
|
? { channel?: PikkuChannel<unknown, Out> } // Optional channel
|
|
@@ -84,7 +84,7 @@ type PikkuFunction<
|
|
|
84
84
|
Out = never,
|
|
85
85
|
ChannelData = null, // null means optional channel
|
|
86
86
|
MCPData = null, // null means optional MCP
|
|
87
|
-
RequiredServices extends Services = Services &
|
|
87
|
+
RequiredServices extends Services = Omit<Services, 'rpc'> &
|
|
88
88
|
{ rpc: TypedPikkuRPC } & (
|
|
89
89
|
[ChannelData] extends [null]
|
|
90
90
|
? { channel?: PikkuChannel<unknown, Out> } // Optional channel
|
|
@@ -118,7 +118,7 @@ type ChannelWiring<ChannelData, Channel extends string> = CoreChannel<ChannelDat
|
|
|
118
118
|
* Type definition for scheduled tasks that run at specified intervals.
|
|
119
119
|
* These are sessionless functions that execute based on cron expressions.
|
|
120
120
|
*/
|
|
121
|
-
type SchedulerWiring = CoreScheduledTask<PikkuFunctionSessionless<void, void
|
|
121
|
+
type SchedulerWiring = CoreScheduledTask<PikkuFunctionSessionless<void, void>, PikkuMiddleware>
|
|
122
122
|
|
|
123
123
|
/**
|
|
124
124
|
* Type definition for queue workers that process background jobs.
|
|
@@ -201,7 +201,7 @@ export const pikkuFunc = <In, Out = unknown>(
|
|
|
201
201
|
export const pikkuSessionlessFunc = <In, Out = unknown>(
|
|
202
202
|
func:
|
|
203
203
|
| PikkuFunctionSessionless<In, Out>
|
|
204
|
-
| CorePikkuFunctionConfig<PikkuFunctionSessionless<In, Out>, PikkuPermission<In
|
|
204
|
+
| CorePikkuFunctionConfig<PikkuFunctionSessionless<In, Out>, PikkuPermission<In>, PikkuMiddleware>
|
|
205
205
|
) => {
|
|
206
206
|
return typeof func === 'function' ? func : func.func
|
|
207
207
|
}
|
|
@@ -351,6 +351,78 @@ export const wireChannel = <ChannelData, Channel extends string>(
|
|
|
351
351
|
*/
|
|
352
352
|
export { addHTTPMiddleware }
|
|
353
353
|
|
|
354
|
+
/**
|
|
355
|
+
* Adds global middleware for a specific tag.
|
|
356
|
+
*
|
|
357
|
+
* This function allows you to register middleware that will be applied to
|
|
358
|
+
* any wiring (HTTP, Channel, Queue, Scheduler, MCP) that includes the matching tag.
|
|
359
|
+
*
|
|
360
|
+
* @param tag - The tag that the middleware should apply to.
|
|
361
|
+
* @param middleware - The middleware array to apply for the specified tag.
|
|
362
|
+
*
|
|
363
|
+
* @throws Error if middleware for the tag already exists.
|
|
364
|
+
*
|
|
365
|
+
* @example
|
|
366
|
+
* \`\`\`typescript
|
|
367
|
+
* // Add admin middleware for admin endpoints
|
|
368
|
+
* addMiddleware('admin', [adminMiddleware])
|
|
369
|
+
*
|
|
370
|
+
* // Add authentication middleware for auth endpoints
|
|
371
|
+
* addMiddleware('auth', [authMiddleware])
|
|
372
|
+
*
|
|
373
|
+
* // Add logging middleware for all API endpoints
|
|
374
|
+
* addMiddleware('api', [loggingMiddleware])
|
|
375
|
+
* \`\`\`
|
|
376
|
+
*/
|
|
377
|
+
export { addMiddleware }
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Combines tag-based middleware with wiring-specific middleware.
|
|
381
|
+
*
|
|
382
|
+
* This helper function gets middleware for tags and combines it with any
|
|
383
|
+
* wiring-specific middleware, avoiding the need for manual spreading.
|
|
384
|
+
*
|
|
385
|
+
* @param wiringMiddleware - Wiring-specific middleware.
|
|
386
|
+
* @param tags - Array of tags to look up middleware for.
|
|
387
|
+
* @returns Combined array of tag-based and wiring-specific middleware.
|
|
388
|
+
*
|
|
389
|
+
* @example
|
|
390
|
+
* \`\`\`typescript
|
|
391
|
+
* // Instead of:
|
|
392
|
+
* const taggedMiddleware = getMiddlewareForTags(tags)
|
|
393
|
+
* const combined = [...taggedMiddleware, ...(middleware || [])]
|
|
394
|
+
*
|
|
395
|
+
* // Use:
|
|
396
|
+
* const combined = addMiddlewareForTags(middleware, tags)
|
|
397
|
+
* \`\`\`
|
|
398
|
+
*/
|
|
399
|
+
export { addMiddlewareForTags }
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Adds global permissions for a specific tag.
|
|
403
|
+
*
|
|
404
|
+
* This function allows you to register permissions that will be applied to
|
|
405
|
+
* any wiring (HTTP, Channel, Queue, Scheduler, MCP) that includes the matching tag.
|
|
406
|
+
*
|
|
407
|
+
* @param tag - The tag that the permissions should apply to.
|
|
408
|
+
* @param permissions - The permissions array to apply for the specified tag.
|
|
409
|
+
*
|
|
410
|
+
* @throws Error if permissions for the tag already exist.
|
|
411
|
+
*
|
|
412
|
+
* @example
|
|
413
|
+
* \`\`\`typescript
|
|
414
|
+
* // Add admin permissions for admin endpoints
|
|
415
|
+
* addPermission('admin', [adminPermission])
|
|
416
|
+
*
|
|
417
|
+
* // Add authentication permissions for auth endpoints
|
|
418
|
+
* addPermission('auth', [authPermission])
|
|
419
|
+
*
|
|
420
|
+
* // Add read permissions for all API endpoints
|
|
421
|
+
* addPermission('api', [readPermission])
|
|
422
|
+
* \`\`\`
|
|
423
|
+
*/
|
|
424
|
+
export { addPermission }
|
|
425
|
+
|
|
354
426
|
/**
|
|
355
427
|
* Registers an HTTP wiring with the Pikku framework.
|
|
356
428
|
*
|
package/src/utils.ts
CHANGED
|
@@ -371,9 +371,38 @@ export function generateCustomTypes(
|
|
|
371
371
|
${Array.from(typesMap.customTypes.entries())
|
|
372
372
|
.map(([name, { type, references }]) => {
|
|
373
373
|
references.forEach((name) => {
|
|
374
|
-
|
|
375
|
-
requiredTypes.add(originalName)
|
|
374
|
+
requiredTypes.add(name)
|
|
376
375
|
})
|
|
376
|
+
|
|
377
|
+
// Extract type names from the type string that might not be in references
|
|
378
|
+
const typeString = type
|
|
379
|
+
// Use regex to extract potential type names (PascalCase identifiers)
|
|
380
|
+
const typeNameRegex = /\b[A-Z][a-zA-Z0-9]*\b/g
|
|
381
|
+
const potentialTypes = typeString.match(typeNameRegex) || []
|
|
382
|
+
|
|
383
|
+
potentialTypes.forEach((typeName) => {
|
|
384
|
+
// Skip string literals and common keywords
|
|
385
|
+
if (
|
|
386
|
+
typeString.includes(`"${typeName}"`) ||
|
|
387
|
+
['Pick', 'Omit', 'Partial', 'Required', 'Record', 'Readonly'].includes(
|
|
388
|
+
typeName
|
|
389
|
+
)
|
|
390
|
+
) {
|
|
391
|
+
return
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Try to find this type in the typesMap and add it if found
|
|
395
|
+
try {
|
|
396
|
+
const typeMeta = typesMap.getTypeMeta(typeName)
|
|
397
|
+
if (typeMeta.path) {
|
|
398
|
+
requiredTypes.add(typeName)
|
|
399
|
+
}
|
|
400
|
+
} catch (e) {
|
|
401
|
+
// Type not found in map, but add it anyway for fallback resolution
|
|
402
|
+
requiredTypes.add(typeName)
|
|
403
|
+
}
|
|
404
|
+
})
|
|
405
|
+
|
|
377
406
|
return `export type ${name} = ${type}`
|
|
378
407
|
})
|
|
379
408
|
.join('\n')}`
|
|
@@ -9,7 +9,11 @@ import { PikkuCommand } from '../../types.js'
|
|
|
9
9
|
|
|
10
10
|
export const pikkuFunctionTypes: PikkuCommand = async (
|
|
11
11
|
logger,
|
|
12
|
-
{
|
|
12
|
+
{
|
|
13
|
+
typesDeclarationFile: typesFile,
|
|
14
|
+
packageMappings,
|
|
15
|
+
rpcInternalMapDeclarationFile,
|
|
16
|
+
},
|
|
13
17
|
visitState,
|
|
14
18
|
options = {}
|
|
15
19
|
) => {
|
|
@@ -39,7 +43,7 @@ export const pikkuFunctionTypes: PikkuCommand = async (
|
|
|
39
43
|
`import type { ${singletonServicesType.type} } from '${getFileImportRelativePath(typesFile, singletonServicesType.typePath, packageMappings)}'`,
|
|
40
44
|
singletonServicesType.type,
|
|
41
45
|
`import type { ${sessionServicesType.type} } from '${getFileImportRelativePath(typesFile, sessionServicesType.typePath, packageMappings)}'`,
|
|
42
|
-
`import type { TypedPikkuRPC } from '${getFileImportRelativePath(typesFile,
|
|
46
|
+
`import type { TypedPikkuRPC } from '${getFileImportRelativePath(typesFile, rpcInternalMapDeclarationFile, packageMappings)}'`
|
|
43
47
|
)
|
|
44
48
|
|
|
45
49
|
await writeFileInDir(logger, typesFile, content)
|
|
@@ -1,63 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getFileImportRelativePath,
|
|
3
|
-
logCommandInfoAndTime,
|
|
4
|
-
writeFileInDir,
|
|
5
|
-
} from '../../utils.js'
|
|
1
|
+
import { logCommandInfoAndTime, writeFileInDir } from '../../utils.js'
|
|
6
2
|
import { PikkuCommand } from '../../types.js'
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
packageMappings: Record<string, string> = {}
|
|
12
|
-
) => {
|
|
13
|
-
const serializedImports: string[] = [
|
|
14
|
-
`/* Import and register RPCs */`,
|
|
15
|
-
`import { addFunction } from '@pikku/core'`,
|
|
16
|
-
]
|
|
17
|
-
|
|
18
|
-
const serializedRegistrations: string[] = []
|
|
19
|
-
|
|
20
|
-
// Sort by function name for consistent output
|
|
21
|
-
const sortedEntries = Array.from(functionsMap.entries()).sort((a, b) =>
|
|
22
|
-
a[0].localeCompare(b[0])
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
for (const [name, { path, exportedName }] of sortedEntries) {
|
|
26
|
-
const filePath = getFileImportRelativePath(
|
|
27
|
-
outputPath,
|
|
28
|
-
path,
|
|
29
|
-
packageMappings
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
// For directly exported functions, we can just import and register them
|
|
33
|
-
if (name === exportedName) {
|
|
34
|
-
serializedImports.push(`import { ${exportedName} } from '${filePath}'`)
|
|
35
|
-
serializedRegistrations.push(
|
|
36
|
-
`addFunction('${name}', { func: ${exportedName} })`
|
|
37
|
-
)
|
|
38
|
-
}
|
|
39
|
-
// For renamed functions, we need to import and alias them
|
|
40
|
-
else {
|
|
41
|
-
serializedImports.push(
|
|
42
|
-
`import { ${exportedName} as ${name} } from '${filePath}'`
|
|
43
|
-
)
|
|
44
|
-
serializedRegistrations.push(`addFunction('${name}', ${name})`)
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Add a blank line between imports and registrations
|
|
49
|
-
if (serializedImports.length > 0 && serializedRegistrations.length > 0) {
|
|
50
|
-
serializedImports.push('')
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Combine the imports and registrations
|
|
54
|
-
return [...serializedImports, ...serializedRegistrations].join('\n')
|
|
55
|
-
}
|
|
3
|
+
import {
|
|
4
|
+
generateRuntimeMeta,
|
|
5
|
+
serializeFunctionImports,
|
|
6
|
+
} from './serialize-function-imports.js'
|
|
56
7
|
|
|
57
8
|
export const pikkuFunctions: PikkuCommand = async (
|
|
58
9
|
logger,
|
|
59
|
-
{ functionsMetaFile, functionsFile, packageMappings },
|
|
60
|
-
{ functions }
|
|
10
|
+
{ functionsMetaFile, functionsMetaMinFile, functionsFile, packageMappings },
|
|
11
|
+
{ functions, rpc }
|
|
61
12
|
) => {
|
|
62
13
|
return await logCommandInfoAndTime(
|
|
63
14
|
logger,
|
|
@@ -65,20 +16,33 @@ export const pikkuFunctions: PikkuCommand = async (
|
|
|
65
16
|
'Serialized Pikku functions',
|
|
66
17
|
[false],
|
|
67
18
|
async () => {
|
|
19
|
+
// Generate full metadata
|
|
68
20
|
await writeFileInDir(
|
|
69
21
|
logger,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
functionsFile,
|
|
73
|
-
functions.files,
|
|
74
|
-
packageMappings
|
|
75
|
-
)
|
|
22
|
+
functionsMetaFile,
|
|
23
|
+
`import { pikkuState } from '@pikku/core'\npikkuState('function', 'meta', ${JSON.stringify(functions.meta, null, 2)})`
|
|
76
24
|
)
|
|
25
|
+
|
|
26
|
+
// Generate minimal metadata (runtime)
|
|
27
|
+
const runtimeMeta = generateRuntimeMeta(functions.meta)
|
|
77
28
|
await writeFileInDir(
|
|
78
29
|
logger,
|
|
79
|
-
|
|
80
|
-
`import { pikkuState } from '@pikku/core'\npikkuState('function', 'meta', ${JSON.stringify(
|
|
30
|
+
functionsMetaMinFile,
|
|
31
|
+
`import { pikkuState } from '@pikku/core'\npikkuState('function', 'meta', ${JSON.stringify(runtimeMeta, null, 2)})`
|
|
81
32
|
)
|
|
33
|
+
|
|
34
|
+
if (rpc.exposedFiles.size > 0 || rpc.internalFiles.size > 0) {
|
|
35
|
+
await writeFileInDir(
|
|
36
|
+
logger,
|
|
37
|
+
functionsFile,
|
|
38
|
+
serializeFunctionImports(
|
|
39
|
+
functionsFile,
|
|
40
|
+
rpc.internalFiles,
|
|
41
|
+
functions.meta,
|
|
42
|
+
packageMappings
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
}
|
|
82
46
|
}
|
|
83
47
|
)
|
|
84
48
|
}
|
|
@@ -95,7 +95,7 @@ export const pikkuServices: PikkuCommand = async (
|
|
|
95
95
|
logger,
|
|
96
96
|
'Generating Pikku services map',
|
|
97
97
|
'Generated Pikku services map',
|
|
98
|
-
[
|
|
98
|
+
[false],
|
|
99
99
|
async () => {
|
|
100
100
|
const { sessionServicesType, singletonServicesType } =
|
|
101
101
|
await getPikkuFilesAndMethods(
|
|
@@ -9,7 +9,11 @@ import { PikkuCommand } from '../../types.js'
|
|
|
9
9
|
|
|
10
10
|
export const pikkuFunctionTypes: PikkuCommand = async (
|
|
11
11
|
logger,
|
|
12
|
-
{
|
|
12
|
+
{
|
|
13
|
+
typesDeclarationFile: typesFile,
|
|
14
|
+
packageMappings,
|
|
15
|
+
rpcInternalMapDeclarationFile,
|
|
16
|
+
},
|
|
13
17
|
visitState,
|
|
14
18
|
options = {}
|
|
15
19
|
) => {
|
|
@@ -39,7 +43,7 @@ export const pikkuFunctionTypes: PikkuCommand = async (
|
|
|
39
43
|
`import type { ${singletonServicesType.type} } from '${getFileImportRelativePath(typesFile, singletonServicesType.typePath, packageMappings)}'`,
|
|
40
44
|
singletonServicesType.type,
|
|
41
45
|
`import type { ${sessionServicesType.type} } from '${getFileImportRelativePath(typesFile, sessionServicesType.typePath, packageMappings)}'`,
|
|
42
|
-
`import type { TypedPikkuRPC } from '${getFileImportRelativePath(typesFile,
|
|
46
|
+
`import type { TypedPikkuRPC } from '${getFileImportRelativePath(typesFile, rpcInternalMapDeclarationFile, packageMappings)}'`
|
|
43
47
|
)
|
|
44
48
|
await writeFileInDir(logger, typesFile, content)
|
|
45
49
|
}
|