@lyku/lockstep-core 0.2.1 → 1.3.1
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/LLMs.md +17 -13
- package/README.md +121 -126
- package/diagrams/generate.d.ts +10 -0
- package/diagrams/generate.d.ts.map +1 -0
- package/diagrams/generate.js +149 -0
- package/diagrams/generate.js.map +1 -0
- package/package.json +1 -1
package/LLMs.md
CHANGED
|
@@ -5,19 +5,23 @@ This document is a complete mechanical reference for the `@lyku/lockstep-core` l
|
|
|
5
5
|
## Pipeline overview
|
|
6
6
|
|
|
7
7
|
```
|
|
8
|
-
|
|
9
|
-
↓ generateJsonModels
|
|
10
|
-
|
|
11
|
-
↓ generateDbTypes
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
↓ generateApiTypes
|
|
16
|
-
|
|
17
|
-
↓ generateHandles
|
|
18
|
-
|
|
19
|
-
↓
|
|
20
|
-
|
|
8
|
+
Table schemas (PostgresRecordModel)
|
|
9
|
+
↓ generateJsonModels (this package)
|
|
10
|
+
JSON schemas + TypeScript types
|
|
11
|
+
↓ generateDbTypes (this package)
|
|
12
|
+
Kysely Database interface
|
|
13
|
+
|
|
14
|
+
API definitions (TsonHandlerModel)
|
|
15
|
+
↓ generateApiTypes (this package)
|
|
16
|
+
Request/Response TypeScript types
|
|
17
|
+
↓ generateHandles (@lyku/lockstep-handles-ts)
|
|
18
|
+
Handler factories with validators
|
|
19
|
+
↓ generateTsClient (@lyku/lockstep-client-ts)
|
|
20
|
+
Type-safe HTTP client
|
|
21
|
+
|
|
22
|
+
Table schemas
|
|
23
|
+
↓ detectDrift / generateMigration (@lyku/lockstep-pg)
|
|
24
|
+
SQL migrations
|
|
21
25
|
```
|
|
22
26
|
|
|
23
27
|
---
|
package/README.md
CHANGED
|
@@ -1,23 +1,76 @@
|
|
|
1
1
|
# @lyku/lockstep-core
|
|
2
2
|
|
|
3
|
-
Schema-driven code generation for TypeScript. Define your database tables and API endpoints once, generate everything else: TypeScript types, Kysely types, runtime validators,
|
|
3
|
+
Schema-driven code generation for TypeScript. Define your database tables and API endpoints once, generate everything else: TypeScript types, Kysely types, runtime validators, and more.
|
|
4
|
+
|
|
5
|
+
## The lockstep ecosystem
|
|
6
|
+
|
|
7
|
+
<!-- Generated from libs/lockstep-core/diagrams/pipeline.mmd — do not edit manually -->
|
|
8
|
+
|
|
9
|
+
```mermaid
|
|
10
|
+
graph TD
|
|
11
|
+
input-tables["Table Schemas<br/><small>PostgresRecordModel</small>"]
|
|
12
|
+
input-api["API Definitions<br/><small>TsonHandlerModel</small>"]
|
|
13
|
+
|
|
14
|
+
core-jsonmodels["generateJsonModels"]
|
|
15
|
+
core-dbtypes["generateDbTypes"]
|
|
16
|
+
core-apitypes["generateApiTypes"]
|
|
17
|
+
core-validators["buildValidator"]
|
|
18
|
+
|
|
19
|
+
handles-gen["generateHandles"]
|
|
20
|
+
|
|
21
|
+
client-gen["generateTsClient"]
|
|
22
|
+
|
|
23
|
+
pg-drift["detectDrift"]
|
|
24
|
+
pg-migrate["generateMigration"]
|
|
25
|
+
|
|
26
|
+
output-jsonschemas["JSON Schemas +<br/>TypeScript Types"]
|
|
27
|
+
output-kysely["Kysely Database<br/>Interface"]
|
|
28
|
+
output-reqres["Request / Response<br/>TypeScript Types"]
|
|
29
|
+
output-handlers["Handler Factories<br/>+ Validators"]
|
|
30
|
+
output-client["Type-safe<br/>HTTP Client"]
|
|
31
|
+
output-sql["SQL Migrations"]
|
|
32
|
+
|
|
33
|
+
input-tables --> core-jsonmodels
|
|
34
|
+
core-jsonmodels --> output-jsonschemas
|
|
35
|
+
output-jsonschemas --> core-dbtypes
|
|
36
|
+
core-dbtypes --> output-kysely
|
|
37
|
+
|
|
38
|
+
input-api --> core-apitypes
|
|
39
|
+
core-apitypes --> output-reqres
|
|
40
|
+
input-api --> core-validators
|
|
41
|
+
|
|
42
|
+
output-reqres --> handles-gen
|
|
43
|
+
input-api --> handles-gen
|
|
44
|
+
core-validators -.-> handles-gen
|
|
45
|
+
handles-gen --> output-handlers
|
|
46
|
+
|
|
47
|
+
output-reqres --> client-gen
|
|
48
|
+
input-api --> client-gen
|
|
49
|
+
client-gen --> output-client
|
|
50
|
+
|
|
51
|
+
input-tables --> pg-drift
|
|
52
|
+
pg-drift --> pg-migrate
|
|
53
|
+
pg-migrate --> output-sql
|
|
54
|
+
|
|
55
|
+
classDef highlight fill:#4f46e5,stroke:#3730a3,color:#fff,stroke-width:2px
|
|
56
|
+
classDef dimmed fill:#f1f5f9,stroke:#cbd5e1,color:#94a3b8,stroke-width:1px
|
|
57
|
+
classDef inputNode fill:#fef3c7,stroke:#f59e0b,color:#92400e,stroke-width:1px
|
|
58
|
+
classDef outputNode fill:#d1fae5,stroke:#10b981,color:#065f46,stroke-width:1px
|
|
59
|
+
classDef ownedOutput fill:#a5b4fc,stroke:#4f46e5,color:#1e1b4b,stroke-width:2px
|
|
60
|
+
classDef ownedInput fill:#fde68a,stroke:#4f46e5,color:#1e1b4b,stroke-width:2px
|
|
61
|
+
class core-jsonmodels,core-dbtypes,core-apitypes,core-validators highlight
|
|
62
|
+
class handles-gen,client-gen,pg-drift,pg-migrate dimmed
|
|
63
|
+
class output-handlers,output-client,output-sql outputNode
|
|
64
|
+
class input-tables,input-api ownedInput
|
|
65
|
+
class output-jsonschemas,output-kysely,output-reqres ownedOutput
|
|
66
|
+
```
|
|
4
67
|
|
|
5
|
-
|
|
68
|
+
This package (`lockstep-core`) provides the schema type system, the first three generators, runtime validators, and BON serialization. Handler and client generation live in separate packages.
|
|
6
69
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
↓ generateDbTypes
|
|
12
|
-
db-types (Kysely Database interface)
|
|
13
|
-
|
|
14
|
-
mapi-models (API endpoint contracts)
|
|
15
|
-
↓ generateApiTypes
|
|
16
|
-
mapi-types (Request/Response TypeScript types)
|
|
17
|
-
↓ generateHandles
|
|
18
|
-
handles (Handler factories + validators)
|
|
19
|
-
↓ generateClient
|
|
20
|
-
monolith-ts-api (Type-safe HTTP + WebSocket client)
|
|
70
|
+
## Installation
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
npm install @lyku/lockstep-core
|
|
21
74
|
```
|
|
22
75
|
|
|
23
76
|
## Defining schemas
|
|
@@ -25,17 +78,17 @@ monolith-ts-api (Type-safe HTTP + WebSocket client)
|
|
|
25
78
|
### Database tables
|
|
26
79
|
|
|
27
80
|
```typescript
|
|
28
|
-
// libs/pg-models/src/friendship.ts
|
|
29
81
|
import type { PostgresRecordModel } from '@lyku/lockstep-core';
|
|
30
82
|
|
|
31
|
-
export const
|
|
83
|
+
export const users = {
|
|
32
84
|
properties: {
|
|
33
85
|
id: { type: 'bigserial' },
|
|
86
|
+
username: { type: 'varchar', maxLength: 32 },
|
|
34
87
|
created: { type: 'timestamptz', default: { sql: 'CURRENT_TIMESTAMP' } },
|
|
35
|
-
|
|
36
|
-
|
|
88
|
+
email: { type: 'text' },
|
|
89
|
+
banned: { type: 'boolean' },
|
|
37
90
|
},
|
|
38
|
-
required: ['
|
|
91
|
+
required: ['id', 'username', 'created', 'email'],
|
|
39
92
|
} as const satisfies PostgresRecordModel;
|
|
40
93
|
```
|
|
41
94
|
|
|
@@ -44,24 +97,20 @@ Supported column types: `bigint`, `bigserial`, `serial`, `integer`, `smallint`,
|
|
|
44
97
|
### API endpoints
|
|
45
98
|
|
|
46
99
|
```typescript
|
|
47
|
-
// libs/mapi-models/src/createFriendRequest.ts
|
|
48
100
|
import type { TsonHandlerModel } from '@lyku/lockstep-core';
|
|
49
101
|
|
|
50
|
-
export const
|
|
102
|
+
export const getUser = {
|
|
51
103
|
request: { type: 'bigint' },
|
|
52
104
|
response: {
|
|
53
105
|
type: 'object',
|
|
54
|
-
properties: {
|
|
55
|
-
|
|
106
|
+
properties: {
|
|
107
|
+
id: { type: 'bigint' },
|
|
108
|
+
username: { type: 'string' },
|
|
109
|
+
},
|
|
110
|
+
required: ['id', 'username'],
|
|
56
111
|
},
|
|
57
112
|
authenticated: true,
|
|
58
|
-
throws: [400, 401, 404
|
|
59
|
-
|
|
60
|
-
title: 'Create Friend Request',
|
|
61
|
-
description: 'Sends a friend request to another user.',
|
|
62
|
-
category: 'Friendship',
|
|
63
|
-
tags: ['social', 'friends'],
|
|
64
|
-
since: '1.0.0',
|
|
113
|
+
throws: [400, 401, 404],
|
|
65
114
|
} as const satisfies TsonHandlerModel;
|
|
66
115
|
```
|
|
67
116
|
|
|
@@ -91,14 +140,14 @@ The type system used by API models:
|
|
|
91
140
|
|
|
92
141
|
String formats: `email`, `uuid`, `uri`, `ipv4`, `ipv6`, `hostname`, `date-time`.
|
|
93
142
|
|
|
94
|
-
##
|
|
143
|
+
## Generators
|
|
95
144
|
|
|
96
|
-
###
|
|
145
|
+
### `generateJsonModels`
|
|
97
146
|
|
|
98
147
|
Converts PostgreSQL column schemas to JSON schemas and TypeScript types.
|
|
99
148
|
|
|
100
149
|
```bash
|
|
101
|
-
|
|
150
|
+
lockstep generate json-models --models ./my-tables --output ./generated/json-models
|
|
102
151
|
```
|
|
103
152
|
|
|
104
153
|
Produces two type variants per table:
|
|
@@ -106,12 +155,12 @@ Produces two type variants per table:
|
|
|
106
155
|
- Full type (all fields, optionals included)
|
|
107
156
|
- Insertable type (only required fields, auto-generated columns excluded)
|
|
108
157
|
|
|
109
|
-
###
|
|
158
|
+
### `generateDbTypes`
|
|
110
159
|
|
|
111
160
|
Generates a Kysely `Database` interface from PostgreSQL table definitions.
|
|
112
161
|
|
|
113
162
|
```bash
|
|
114
|
-
|
|
163
|
+
lockstep generate db-types --models ./my-tables --output ./generated/db-types --file kysely.d.ts
|
|
115
164
|
```
|
|
116
165
|
|
|
117
166
|
Output:
|
|
@@ -129,88 +178,29 @@ export interface Database {
|
|
|
129
178
|
}
|
|
130
179
|
```
|
|
131
180
|
|
|
132
|
-
###
|
|
181
|
+
### `generateApiTypes`
|
|
133
182
|
|
|
134
183
|
Generates TypeScript request/response types from API models.
|
|
135
184
|
|
|
136
185
|
```bash
|
|
137
|
-
|
|
186
|
+
lockstep generate api-types --models ./my-endpoints --output ./generated/api-types
|
|
138
187
|
```
|
|
139
188
|
|
|
140
189
|
Output:
|
|
141
190
|
|
|
142
191
|
```typescript
|
|
143
|
-
export type
|
|
144
|
-
export type
|
|
192
|
+
export type GetUserRequest = bigint;
|
|
193
|
+
export type GetUserResponse = { id: bigint; username: string };
|
|
145
194
|
|
|
146
195
|
export type ApiTypes = {
|
|
147
|
-
|
|
148
|
-
request:
|
|
149
|
-
response:
|
|
196
|
+
getUser: {
|
|
197
|
+
request: GetUserRequest;
|
|
198
|
+
response: GetUserResponse;
|
|
150
199
|
};
|
|
151
200
|
// ...
|
|
152
201
|
};
|
|
153
202
|
```
|
|
154
203
|
|
|
155
|
-
### 4. `generateHandles`
|
|
156
|
-
|
|
157
|
-
Generates handler factory functions with built-in request validation.
|
|
158
|
-
|
|
159
|
-
```bash
|
|
160
|
-
from-schema generate handles \
|
|
161
|
-
--models ./mapi-models --output ./handles \
|
|
162
|
-
--models-pkg @myapp/mapi-models \
|
|
163
|
-
--types-pkg @myapp/mapi-types
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
Each handler factory wraps your implementation with type safety:
|
|
167
|
-
|
|
168
|
-
```typescript
|
|
169
|
-
// Generated
|
|
170
|
-
export declare const handleCreateFriendRequest: (handler: (request: CreateFriendRequestRequest, context: SecureHttpContext<typeof createFriendRequest>) => CreateFriendRequestResponse | Promise<CreateFriendRequestResponse>) => {
|
|
171
|
-
readonly execute: typeof handler;
|
|
172
|
-
readonly validator: { validate; validateOrThrow; isValid };
|
|
173
|
-
readonly model: typeof createFriendRequest;
|
|
174
|
-
};
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
Usage in route handlers:
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
import { handleCreateFriendRequest } from '@lyku/handles';
|
|
181
|
-
|
|
182
|
-
export const createFriendRequest = handleCreateFriendRequest(async (targetUserId, { requester }) => {
|
|
183
|
-
// fully typed: targetUserId is bigint, requester is bigint
|
|
184
|
-
const bond = await createBond(requester, targetUserId);
|
|
185
|
-
return { id: bond.id };
|
|
186
|
-
});
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
### 5. `generateClient`
|
|
190
|
-
|
|
191
|
-
Generates a type-safe API client using MessagePack serialization.
|
|
192
|
-
|
|
193
|
-
```bash
|
|
194
|
-
from-schema generate client \
|
|
195
|
-
--models ./mapi-models --output ./client \
|
|
196
|
-
--types-pkg @myapp/mapi-types
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
The generated client supports both HTTP and WebSocket endpoints:
|
|
200
|
-
|
|
201
|
-
```typescript
|
|
202
|
-
import { createClient } from '@myapp/client';
|
|
203
|
-
|
|
204
|
-
const api = createClient({ baseUrl: 'https://api.example.com' });
|
|
205
|
-
|
|
206
|
-
// HTTP — returns Promise<Response>
|
|
207
|
-
const result = await api.createFriendRequest(456n);
|
|
208
|
-
|
|
209
|
-
// WebSocket — returns StreamSocket
|
|
210
|
-
const socket = api.subscribeToPosts({ groupId: 1n });
|
|
211
|
-
socket.listen((post) => console.log(post));
|
|
212
|
-
```
|
|
213
|
-
|
|
214
204
|
## Handler contexts
|
|
215
205
|
|
|
216
206
|
Handlers receive a typed context object based on auth requirements:
|
|
@@ -244,19 +234,16 @@ Context fields:
|
|
|
244
234
|
|
|
245
235
|
## Runtime validation
|
|
246
236
|
|
|
247
|
-
|
|
237
|
+
Build validators from any schema:
|
|
248
238
|
|
|
249
239
|
```typescript
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
// Collect all validation errors
|
|
253
|
-
const errors = handle.validator.validate(input);
|
|
240
|
+
import { buildValidator } from '@lyku/lockstep-core';
|
|
254
241
|
|
|
255
|
-
|
|
256
|
-
handle.validator.validateOrThrow(input);
|
|
242
|
+
const validator = buildValidator('input', { type: 'string', minLength: 1 });
|
|
257
243
|
|
|
258
|
-
//
|
|
259
|
-
|
|
244
|
+
validator.validate(input); // returns string[] of errors
|
|
245
|
+
validator.validateOrThrow(input); // throws on invalid
|
|
246
|
+
validator.isValid(input); // returns true or first error string
|
|
260
247
|
```
|
|
261
248
|
|
|
262
249
|
Validators check types, required fields, string lengths/patterns/formats, numeric ranges, enum membership, and array constraints.
|
|
@@ -283,29 +270,37 @@ API models support rich documentation metadata:
|
|
|
283
270
|
|
|
284
271
|
```typescript
|
|
285
272
|
{
|
|
286
|
-
title: '
|
|
287
|
-
description: '
|
|
288
|
-
category: '
|
|
289
|
-
tags: ['
|
|
273
|
+
title: 'Get User',
|
|
274
|
+
description: 'Fetch a user by ID.',
|
|
275
|
+
category: 'Users',
|
|
276
|
+
tags: ['users'],
|
|
290
277
|
since: '1.0.0',
|
|
291
|
-
examples: [{ title: 'Basic', request: 456n, response: { id:
|
|
292
|
-
notes: ['
|
|
293
|
-
errors: [{ code:
|
|
294
|
-
relatedEndpoints: ['
|
|
295
|
-
rateLimit: { requests:
|
|
296
|
-
deprecated: { since: '2.0.0', useInstead: '
|
|
297
|
-
requiredPermissions: ['
|
|
278
|
+
examples: [{ title: 'Basic', request: 456n, response: { id: 456n, username: 'alice' } }],
|
|
279
|
+
notes: ['Returns 404 if user does not exist'],
|
|
280
|
+
errors: [{ code: 404, title: 'Not Found', description: '...' }],
|
|
281
|
+
relatedEndpoints: ['updateUser', 'deleteUser'],
|
|
282
|
+
rateLimit: { requests: 100, period: '1 minute', scope: 'user' },
|
|
283
|
+
deprecated: { since: '2.0.0', useInstead: 'getUserV2', reason: '...' },
|
|
284
|
+
requiredPermissions: ['users:read'],
|
|
298
285
|
}
|
|
299
286
|
```
|
|
300
287
|
|
|
301
288
|
## Exports
|
|
302
289
|
|
|
303
290
|
```
|
|
304
|
-
@lyku/lockstep-core
|
|
305
|
-
@lyku/lockstep-core/contexts
|
|
306
|
-
@lyku/lockstep-core/generators # Code generators (json-models, db-types, api-types
|
|
291
|
+
@lyku/lockstep-core # Schema types, BON, type converters, validators
|
|
292
|
+
@lyku/lockstep-core/contexts # Handler context types
|
|
293
|
+
@lyku/lockstep-core/generators # Code generators (json-models, db-types, api-types)
|
|
307
294
|
```
|
|
308
295
|
|
|
296
|
+
## Related packages
|
|
297
|
+
|
|
298
|
+
| Package | Purpose |
|
|
299
|
+
| --------------------------- | ------------------------------------------------ |
|
|
300
|
+
| `@lyku/lockstep-handles-ts` | Generate typed handler factories from API models |
|
|
301
|
+
| `@lyku/lockstep-client-ts` | Generate typed HTTP clients from API models |
|
|
302
|
+
| `@lyku/lockstep-pg` | PostgreSQL drift detection and migrations |
|
|
303
|
+
|
|
309
304
|
## License
|
|
310
305
|
|
|
311
306
|
GPL-3.0
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates per-package pipeline diagrams from the master pipeline.mmd.
|
|
3
|
+
*
|
|
4
|
+
* Each variant highlights the nodes belonging to that package and dims everything else.
|
|
5
|
+
* Output is a mermaid code block ready to paste into a README.
|
|
6
|
+
*
|
|
7
|
+
* Usage: bun libs/lockstep-core/diagrams/generate.ts
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=generate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../../libs/lockstep-core/diagrams/generate.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/* eslint-disable security/detect-non-literal-fs-filename, security/detect-object-injection */
|
|
2
|
+
/**
|
|
3
|
+
* Generates per-package pipeline diagrams from the master pipeline.mmd.
|
|
4
|
+
*
|
|
5
|
+
* Each variant highlights the nodes belonging to that package and dims everything else.
|
|
6
|
+
* Output is a mermaid code block ready to paste into a README.
|
|
7
|
+
*
|
|
8
|
+
* Usage: bun libs/lockstep-core/diagrams/generate.ts
|
|
9
|
+
*/
|
|
10
|
+
import { readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
11
|
+
import { join, dirname } from 'path';
|
|
12
|
+
const DIAGRAMS_DIR = dirname(new URL(import.meta.url).pathname);
|
|
13
|
+
const master = readFileSync(join(DIAGRAMS_DIR, 'pipeline.mmd'), 'utf-8');
|
|
14
|
+
// Strip comment lines from master
|
|
15
|
+
const masterLines = master
|
|
16
|
+
.split('\n')
|
|
17
|
+
.filter((line) => !line.trim().startsWith('%%'));
|
|
18
|
+
const packages = {
|
|
19
|
+
'lockstep-core': {
|
|
20
|
+
prefix: 'core-',
|
|
21
|
+
title: '@lyku/lockstep-core',
|
|
22
|
+
},
|
|
23
|
+
'lockstep-handles-ts': {
|
|
24
|
+
prefix: 'handles-',
|
|
25
|
+
title: '@lyku/lockstep-handles-ts',
|
|
26
|
+
},
|
|
27
|
+
'lockstep-client-ts': {
|
|
28
|
+
prefix: 'client-',
|
|
29
|
+
title: '@lyku/lockstep-client-ts',
|
|
30
|
+
},
|
|
31
|
+
'lockstep-pg': {
|
|
32
|
+
prefix: 'pg-',
|
|
33
|
+
title: '@lyku/lockstep-pg',
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
// Find all node IDs in the diagram
|
|
37
|
+
const nodeIdRegex = /^\s{4}(\S+?)[[("]/m;
|
|
38
|
+
const allNodeIds = [];
|
|
39
|
+
for (const line of masterLines) {
|
|
40
|
+
const match = line.match(nodeIdRegex);
|
|
41
|
+
if (match) {
|
|
42
|
+
allNodeIds.push(match[1]);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function generateVariant(pkgKey) {
|
|
46
|
+
const pkg = packages[pkgKey];
|
|
47
|
+
const highlighted = allNodeIds.filter((id) => id.startsWith(pkg.prefix));
|
|
48
|
+
const dimmed = allNodeIds.filter((id) => !id.startsWith(pkg.prefix) && !id.startsWith('input-') && !id.startsWith('output-'));
|
|
49
|
+
const inputs = allNodeIds.filter((id) => id.startsWith('input-'));
|
|
50
|
+
const outputs = allNodeIds.filter((id) => id.startsWith('output-'));
|
|
51
|
+
// Find which outputs are directly produced by this package's nodes
|
|
52
|
+
// by scanning edges: highlighted-node --> output-node
|
|
53
|
+
const ownedOutputs = new Set();
|
|
54
|
+
for (const line of masterLines) {
|
|
55
|
+
const edgeMatch = line.match(/^\s{4}(\S+)\s+--.*?-->\s+(\S+)/);
|
|
56
|
+
const simpleEdge = line.match(/^\s{4}(\S+)\s+-->\s+(\S+)/);
|
|
57
|
+
const dottedEdge = line.match(/^\s{4}(\S+)\s+-\.\s*->\s+(\S+)/);
|
|
58
|
+
const dottedEdge2 = line.match(/^\s{4}(\S+)\s+-\.+->\s+(\S+)/);
|
|
59
|
+
const edge = edgeMatch || simpleEdge || dottedEdge || dottedEdge2;
|
|
60
|
+
if (edge) {
|
|
61
|
+
const [, from, to] = edge;
|
|
62
|
+
if (from.startsWith(pkg.prefix) && to.startsWith('output-')) {
|
|
63
|
+
ownedOutputs.add(to);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Find which inputs feed directly into this package's nodes
|
|
68
|
+
const ownedInputs = new Set();
|
|
69
|
+
for (const line of masterLines) {
|
|
70
|
+
const edgeMatch = line.match(/^\s{4}(\S+)\s+--.*?-->\s+(\S+)/);
|
|
71
|
+
const simpleEdge = line.match(/^\s{4}(\S+)\s+-->\s+(\S+)/);
|
|
72
|
+
const dottedEdge = line.match(/^\s{4}(\S+)\s+-\.\s*->\s+(\S+)/);
|
|
73
|
+
const dottedEdge2 = line.match(/^\s{4}(\S+)\s+-\.+->\s+(\S+)/);
|
|
74
|
+
const edge = edgeMatch || simpleEdge || dottedEdge || dottedEdge2;
|
|
75
|
+
if (edge) {
|
|
76
|
+
const [, from, to] = edge;
|
|
77
|
+
if (from.startsWith('input-') && to.startsWith(pkg.prefix)) {
|
|
78
|
+
ownedInputs.add(from);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const classDefs = [
|
|
83
|
+
'classDef highlight fill:#4f46e5,stroke:#3730a3,color:#fff,stroke-width:2px',
|
|
84
|
+
'classDef dimmed fill:#f1f5f9,stroke:#cbd5e1,color:#94a3b8,stroke-width:1px',
|
|
85
|
+
'classDef inputNode fill:#fef3c7,stroke:#f59e0b,color:#92400e,stroke-width:1px',
|
|
86
|
+
'classDef outputNode fill:#d1fae5,stroke:#10b981,color:#065f46,stroke-width:1px',
|
|
87
|
+
'classDef ownedOutput fill:#a5b4fc,stroke:#4f46e5,color:#1e1b4b,stroke-width:2px',
|
|
88
|
+
'classDef ownedInput fill:#fde68a,stroke:#4f46e5,color:#1e1b4b,stroke-width:2px',
|
|
89
|
+
];
|
|
90
|
+
const classAssignments = [];
|
|
91
|
+
if (highlighted.length > 0) {
|
|
92
|
+
classAssignments.push(`class ${highlighted.join(',')} highlight`);
|
|
93
|
+
}
|
|
94
|
+
if (dimmed.length > 0) {
|
|
95
|
+
classAssignments.push(`class ${dimmed.join(',')} dimmed`);
|
|
96
|
+
}
|
|
97
|
+
// Assign input/output classes, with owned variants taking precedence
|
|
98
|
+
const regularInputs = inputs.filter((id) => !ownedInputs.has(id));
|
|
99
|
+
const regularOutputs = outputs.filter((id) => !ownedOutputs.has(id));
|
|
100
|
+
if (regularInputs.length > 0) {
|
|
101
|
+
classAssignments.push(`class ${regularInputs.join(',')} inputNode`);
|
|
102
|
+
}
|
|
103
|
+
if (regularOutputs.length > 0) {
|
|
104
|
+
classAssignments.push(`class ${regularOutputs.join(',')} outputNode`);
|
|
105
|
+
}
|
|
106
|
+
if (ownedInputs.size > 0) {
|
|
107
|
+
classAssignments.push(`class ${[...ownedInputs].join(',')} ownedInput`);
|
|
108
|
+
}
|
|
109
|
+
if (ownedOutputs.size > 0) {
|
|
110
|
+
classAssignments.push(`class ${[...ownedOutputs].join(',')} ownedOutput`);
|
|
111
|
+
}
|
|
112
|
+
const lines = [
|
|
113
|
+
...masterLines,
|
|
114
|
+
'',
|
|
115
|
+
...classDefs,
|
|
116
|
+
...classAssignments,
|
|
117
|
+
];
|
|
118
|
+
return lines.join('\n').trim();
|
|
119
|
+
}
|
|
120
|
+
// Generate all variants
|
|
121
|
+
mkdirSync(join(DIAGRAMS_DIR, 'generated'), { recursive: true });
|
|
122
|
+
for (const [pkgKey, _pkg] of Object.entries(packages)) {
|
|
123
|
+
const variant = generateVariant(pkgKey);
|
|
124
|
+
const outPath = join(DIAGRAMS_DIR, 'generated', `${pkgKey}.mmd`);
|
|
125
|
+
writeFileSync(outPath, variant + '\n');
|
|
126
|
+
console.log(`Generated ${outPath}`);
|
|
127
|
+
}
|
|
128
|
+
// Also generate a "full" variant with no highlighting (all nodes normal)
|
|
129
|
+
const fullVariant = [
|
|
130
|
+
...masterLines,
|
|
131
|
+
'',
|
|
132
|
+
`classDef inputNode fill:#fef3c7,stroke:#f59e0b,color:#92400e,stroke-width:1px`,
|
|
133
|
+
`classDef outputNode fill:#d1fae5,stroke:#10b981,color:#065f46,stroke-width:1px`,
|
|
134
|
+
`classDef coreNode fill:#dbeafe,stroke:#3b82f6,color:#1e3a5f,stroke-width:1px`,
|
|
135
|
+
`classDef handlesNode fill:#e0e7ff,stroke:#6366f1,color:#312e81,stroke-width:1px`,
|
|
136
|
+
`classDef clientNode fill:#fce7f3,stroke:#ec4899,color:#831843,stroke-width:1px`,
|
|
137
|
+
`classDef pgNode fill:#fef9c3,stroke:#eab308,color:#713f12,stroke-width:1px`,
|
|
138
|
+
`class ${allNodeIds.filter((id) => id.startsWith('input-')).join(',')} inputNode`,
|
|
139
|
+
`class ${allNodeIds.filter((id) => id.startsWith('output-')).join(',')} outputNode`,
|
|
140
|
+
`class ${allNodeIds.filter((id) => id.startsWith('core-')).join(',')} coreNode`,
|
|
141
|
+
`class ${allNodeIds.filter((id) => id.startsWith('handles-')).join(',')} handlesNode`,
|
|
142
|
+
`class ${allNodeIds.filter((id) => id.startsWith('client-')).join(',')} clientNode`,
|
|
143
|
+
`class ${allNodeIds.filter((id) => id.startsWith('pg-')).join(',')} pgNode`,
|
|
144
|
+
]
|
|
145
|
+
.join('\n')
|
|
146
|
+
.trim();
|
|
147
|
+
writeFileSync(join(DIAGRAMS_DIR, 'generated', 'full.mmd'), fullVariant + '\n');
|
|
148
|
+
console.log(`Generated ${join(DIAGRAMS_DIR, 'generated', 'full.mmd')}`);
|
|
149
|
+
//# sourceMappingURL=generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../../../../libs/lockstep-core/diagrams/generate.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED