@lyku/lockstep-handles-ts 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@lyku/lockstep-handles-ts",
3
+ "version": "1.0.0",
4
+ "description": "Generate typed handler factories from TsonHandlerModel API definitions",
5
+ "type": "module",
6
+ "main": "./index.js",
7
+ "types": "./index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./index.d.ts",
11
+ "import": "./index.js",
12
+ "default": "./index.js"
13
+ }
14
+ },
15
+ "keywords": [
16
+ "lockstep",
17
+ "handler",
18
+ "codegen",
19
+ "typescript",
20
+ "api",
21
+ "type-safe"
22
+ ],
23
+ "author": "Lyku",
24
+ "license": "GPL-3.0",
25
+ "devDependencies": {
26
+ "typescript": "^5.0.0"
27
+ },
28
+ "module": "./src/index.js"
29
+ }
@@ -0,0 +1,21 @@
1
+ export interface GenerateHandlesOptions {
2
+ /** Record of endpoint name → model. Non-handler entries are automatically filtered out. */
3
+ models: Record<string, unknown>;
4
+ /** Output directory for generated files */
5
+ outputDir: string;
6
+ /** Package name for the API models (e.g. '@myapp/api-models') */
7
+ modelsPackage: string;
8
+ /** Package name for the generated API types (e.g. '@myapp/api-types') */
9
+ typesPackage: string;
10
+ /**
11
+ * Package name for handler context types.
12
+ * Default: '@lyku/lockstep-core/contexts' — uses lockstep-core's built-in context types.
13
+ * Set to your own package if you have custom context types.
14
+ */
15
+ contextPackage?: string;
16
+ /** Name of the aggregate types map exported by typesPackage (default: 'ApiTypes') */
17
+ typesName?: string;
18
+ /** Whether to clean the output directory before generating (default: true) */
19
+ clean?: boolean;
20
+ }
21
+ export declare function generateHandles(options: GenerateHandlesOptions): Promise<void>;
@@ -0,0 +1,101 @@
1
+ /* eslint-disable security/detect-non-literal-fs-filename, @typescript-eslint/no-explicit-any */
2
+ import { mkdir, writeFile, rm } from 'fs/promises';
3
+ import { join } from 'path';
4
+ import { buildValidator, stringifyBON } from '@lyku/lockstep-core';
5
+ import { formatCode, filterHandlerModels, } from '@lyku/lockstep-core/generators';
6
+ export async function generateHandles(options) {
7
+ const { outputDir, modelsPackage, typesPackage, contextPackage = '@lyku/lockstep-core/contexts', typesName = 'ApiTypes', clean = true, } = options;
8
+ const models = filterHandlerModels(options.models);
9
+ if (clean) {
10
+ await rm(outputDir, { recursive: true, force: true });
11
+ }
12
+ await mkdir(outputDir, { recursive: true });
13
+ const indexJsExports = [];
14
+ const indexDtsExports = [];
15
+ for (const [key, value] of Object.entries(models)) {
16
+ const jsLines = [];
17
+ const dtsLines = [];
18
+ const upper = key[0].toUpperCase() + key.slice(1);
19
+ const handleName = `handle${upper}`;
20
+ const request = upper + 'Request';
21
+ const isStream = 'stream' in value && value.stream;
22
+ const isAuthenticated = 'authenticated' in value && value.authenticated;
23
+ const protocol = isStream ? 'Websocket' : 'Http';
24
+ const response = protocol === 'Http'
25
+ ? 'response' in value
26
+ ? upper + 'Response'
27
+ : 'void'
28
+ : 'void';
29
+ const tweakRequest = isStream &&
30
+ typeof value.stream === 'object' &&
31
+ 'tweakRequest' in value.stream
32
+ ? upper + 'TweakRequest'
33
+ : undefined;
34
+ indexJsExports.push(`export * from './${key}.js';`);
35
+ indexDtsExports.push(`export * from './${key}';`);
36
+ // ── JS implementation ──────────────────────────────────────
37
+ jsLines.push(`import { ${key} } from '${modelsPackage}';`);
38
+ const validator = 'request' in value
39
+ ? (() => {
40
+ const v = buildValidator('request', value.request);
41
+ return `{ "validate": (request) => {const allErrors = []; ${v.validate.toString()}; return allErrors; }, "validateOrThrow": (request) => {${v.validateOrThrow.toString()}}, "isValid": (request) => {${v.isValid.toString()}; return true } }`;
42
+ })()
43
+ : '{ validate: () => [], validateOrThrow: () => {}, isValid: () => true }';
44
+ let tweakValidatorStr = '';
45
+ if (isStream &&
46
+ typeof value.stream === 'object' &&
47
+ 'tweakRequest' in value.stream) {
48
+ const tv = buildValidator('tweakRequest', value.stream.tweakRequest);
49
+ tweakValidatorStr = `tweakValidator: { "validate": (tweakRequest) => {const allErrors = []; ${tv.validate.toString()}; return allErrors; }, "validateOrThrow": (tweakRequest) => {${tv.validateOrThrow.toString()}}, "isValid": (tweakRequest) => {${tv.isValid.toString()}; return true } },`;
50
+ }
51
+ const entryPoint = isStream ? 'onOpen' : 'execute';
52
+ const handleJs = `export const ${handleName} = (handler) => ({
53
+ ${entryPoint}: handler,
54
+ validator: ${validator},
55
+ ${tweakValidatorStr}
56
+ model: ${stringifyBON(value)}
57
+ });`;
58
+ if (handleJs.includes('isObject')) {
59
+ jsLines.push(`import { isObject } from '@lyku/lockstep-core';`);
60
+ }
61
+ jsLines.push(handleJs);
62
+ // ── TypeScript declaration ──────────────────────────────────
63
+ const contextType = isStream
64
+ ? isAuthenticated
65
+ ? 'SecureSocketContext'
66
+ : 'MaybeSecureSocketContext'
67
+ : isAuthenticated
68
+ ? 'SecureHttpContext'
69
+ : 'MaybeSecureHttpContext';
70
+ const contextGeneric = isStream
71
+ ? `${contextType}<typeof ${key}, ${typesName}['${key}']>`
72
+ : `${contextType}<typeof ${key}>`;
73
+ const responseImport = response === 'void' ? '' : `, ${response}`;
74
+ const tweakImports = tweakRequest ? `, ${tweakRequest}` : '';
75
+ dtsLines.push(`import { ${key} } from '${modelsPackage}';`);
76
+ dtsLines.push(`import type { ${typesName} } from '${typesPackage}';`);
77
+ dtsLines.push(`import type { ${request}${responseImport}${tweakImports} } from '${typesPackage}';`);
78
+ dtsLines.push(`import type { ${contextType} } from '${contextPackage}';`);
79
+ const handleDts = `export declare const ${handleName}: (handler: (request: ${request}, context: ${contextGeneric}) => ${response} | Promise<${response}>) => {
80
+ readonly ${entryPoint}: (request: ${request}, context: ${contextGeneric}) => ${response} | Promise<${response}>;
81
+ readonly validator: {
82
+ validate: (request: unknown) => string[];
83
+ validateOrThrow: (request: unknown) => void;
84
+ isValid: (request: unknown) => string | true;
85
+ };
86
+ readonly model: typeof ${key};
87
+ };`;
88
+ dtsLines.push(handleDts);
89
+ // ── Write per-endpoint files ───────────────────────────────
90
+ const formattedJs = await formatCode(jsLines.join('\n'), 'babel');
91
+ await writeFile(join(outputDir, `${key}.js`), formattedJs);
92
+ const formattedDts = await formatCode(dtsLines.join('\n'), 'typescript');
93
+ await writeFile(join(outputDir, `${key}.d.ts`), formattedDts);
94
+ }
95
+ // ── Write index files ──────────────────────────────────────────
96
+ const indexJs = await formatCode(indexJsExports.join('\n'), 'babel');
97
+ await writeFile(join(outputDir, 'index.js'), indexJs);
98
+ const indexDts = await formatCode(indexDtsExports.join('\n'), 'typescript');
99
+ await writeFile(join(outputDir, 'index.d.ts'), indexDts);
100
+ }
101
+ //# sourceMappingURL=generateHandles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generateHandles.js","sourceRoot":"","sources":["../../../../libs/lockstep-handles-ts/src/generateHandles.ts"],"names":[],"mappings":"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"}
package/src/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { generateHandles } from './generateHandles';
2
+ export type { GenerateHandlesOptions } from './generateHandles';
package/src/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { generateHandles } from './generateHandles';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/lockstep-handles-ts/src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC"}