@auto-engineer/narrative 0.11.13 → 0.11.14
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +11 -0
- package/dist/src/data-narrative-builders.d.ts +7 -5
- package/dist/src/data-narrative-builders.d.ts.map +1 -1
- package/dist/src/data-narrative-builders.js +19 -1
- package/dist/src/data-narrative-builders.js.map +1 -1
- package/dist/src/getNarratives.specs.js +227 -0
- package/dist/src/getNarratives.specs.js.map +1 -1
- package/dist/src/model-to-narrative.specs.js +385 -1
- package/dist/src/model-to-narrative.specs.js.map +1 -1
- package/dist/src/schema.d.ts +398 -199
- package/dist/src/schema.d.ts.map +1 -1
- package/dist/src/schema.js +8 -1
- package/dist/src/schema.js.map +1 -1
- package/dist/src/transformers/model-to-narrative/generators/flow.d.ts.map +1 -1
- package/dist/src/transformers/model-to-narrative/generators/flow.js +49 -12
- package/dist/src/transformers/model-to-narrative/generators/flow.js.map +1 -1
- package/dist/src/types.d.ts +6 -8
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/src/data-narrative-builders.ts +41 -9
- package/src/getNarratives.specs.ts +260 -0
- package/src/model-to-narrative.specs.ts +397 -1
- package/src/schema.ts +12 -1
- package/src/transformers/model-to-narrative/generators/flow.ts +85 -26
- package/src/types.ts +7 -9
|
@@ -501,7 +501,9 @@ narrative('Test Flow with IDs', 'FLOW-123', () => {
|
|
|
501
501
|
should('allow filtering');
|
|
502
502
|
});
|
|
503
503
|
})
|
|
504
|
-
.server(() => {
|
|
504
|
+
.server(() => {
|
|
505
|
+
specs('Product data specs', () => {});
|
|
506
|
+
});
|
|
505
507
|
});
|
|
506
508
|
`);
|
|
507
509
|
});
|
|
@@ -2031,4 +2033,398 @@ narrative('Todo List Summary', 'TODO-001', () => {
|
|
|
2031
2033
|
expect(code).not.toContain('.when({})');
|
|
2032
2034
|
expect(code).not.toContain('.when<');
|
|
2033
2035
|
});
|
|
2036
|
+
|
|
2037
|
+
describe('projection DSL generation', () => {
|
|
2038
|
+
it('should generate fromSingletonProjection for singleton projections', async () => {
|
|
2039
|
+
const modelWithSingletonProjection: Model = {
|
|
2040
|
+
variant: 'specs',
|
|
2041
|
+
narratives: [
|
|
2042
|
+
{
|
|
2043
|
+
name: 'Todo Summary Flow',
|
|
2044
|
+
id: 'TODO-SUMMARY',
|
|
2045
|
+
slices: [
|
|
2046
|
+
{
|
|
2047
|
+
name: 'views todo summary',
|
|
2048
|
+
id: 'SUMMARY-SLICE',
|
|
2049
|
+
type: 'query',
|
|
2050
|
+
client: {
|
|
2051
|
+
description: 'Summary client',
|
|
2052
|
+
},
|
|
2053
|
+
server: {
|
|
2054
|
+
description: 'Summary server',
|
|
2055
|
+
data: [
|
|
2056
|
+
{
|
|
2057
|
+
target: {
|
|
2058
|
+
type: 'State',
|
|
2059
|
+
name: 'TodoListSummary',
|
|
2060
|
+
},
|
|
2061
|
+
origin: {
|
|
2062
|
+
type: 'projection',
|
|
2063
|
+
name: 'TodoSummary',
|
|
2064
|
+
singleton: true,
|
|
2065
|
+
},
|
|
2066
|
+
},
|
|
2067
|
+
],
|
|
2068
|
+
specs: {
|
|
2069
|
+
name: 'Summary Rules',
|
|
2070
|
+
rules: [],
|
|
2071
|
+
},
|
|
2072
|
+
},
|
|
2073
|
+
},
|
|
2074
|
+
],
|
|
2075
|
+
},
|
|
2076
|
+
],
|
|
2077
|
+
messages: [
|
|
2078
|
+
{
|
|
2079
|
+
type: 'state',
|
|
2080
|
+
name: 'TodoListSummary',
|
|
2081
|
+
fields: [
|
|
2082
|
+
{ name: 'summaryId', type: 'string', required: true },
|
|
2083
|
+
{ name: 'totalTodos', type: 'number', required: true },
|
|
2084
|
+
],
|
|
2085
|
+
metadata: { version: 1 },
|
|
2086
|
+
},
|
|
2087
|
+
],
|
|
2088
|
+
integrations: [],
|
|
2089
|
+
};
|
|
2090
|
+
|
|
2091
|
+
const code = await modelToNarrative(modelWithSingletonProjection);
|
|
2092
|
+
|
|
2093
|
+
expect(code).toEqual(`import { data, narrative, query, source, specs } from '@auto-engineer/narrative';
|
|
2094
|
+
import type { State } from '@auto-engineer/narrative';
|
|
2095
|
+
type TodoListSummary = State<
|
|
2096
|
+
'TodoListSummary',
|
|
2097
|
+
{
|
|
2098
|
+
summaryId: string;
|
|
2099
|
+
totalTodos: number;
|
|
2100
|
+
}
|
|
2101
|
+
>;
|
|
2102
|
+
narrative('Todo Summary Flow', 'TODO-SUMMARY', () => {
|
|
2103
|
+
query('views todo summary', 'SUMMARY-SLICE').server(() => {
|
|
2104
|
+
data([source().state('TodoListSummary').fromSingletonProjection('TodoSummary')]);
|
|
2105
|
+
specs('Summary Rules', () => {});
|
|
2106
|
+
});
|
|
2107
|
+
});
|
|
2108
|
+
`);
|
|
2109
|
+
});
|
|
2110
|
+
|
|
2111
|
+
it('should generate fromProjection with single idField for regular projections', async () => {
|
|
2112
|
+
const modelWithRegularProjection: Model = {
|
|
2113
|
+
variant: 'specs',
|
|
2114
|
+
narratives: [
|
|
2115
|
+
{
|
|
2116
|
+
name: 'Todo Flow',
|
|
2117
|
+
id: 'TODO-FLOW',
|
|
2118
|
+
slices: [
|
|
2119
|
+
{
|
|
2120
|
+
name: 'views todo',
|
|
2121
|
+
id: 'TODO-SLICE',
|
|
2122
|
+
type: 'query',
|
|
2123
|
+
client: {
|
|
2124
|
+
description: 'Todo client',
|
|
2125
|
+
},
|
|
2126
|
+
server: {
|
|
2127
|
+
description: 'Todo server',
|
|
2128
|
+
data: [
|
|
2129
|
+
{
|
|
2130
|
+
target: {
|
|
2131
|
+
type: 'State',
|
|
2132
|
+
name: 'TodoState',
|
|
2133
|
+
},
|
|
2134
|
+
origin: {
|
|
2135
|
+
type: 'projection',
|
|
2136
|
+
name: 'Todos',
|
|
2137
|
+
idField: 'todoId',
|
|
2138
|
+
},
|
|
2139
|
+
},
|
|
2140
|
+
],
|
|
2141
|
+
specs: {
|
|
2142
|
+
name: 'Todo Rules',
|
|
2143
|
+
rules: [],
|
|
2144
|
+
},
|
|
2145
|
+
},
|
|
2146
|
+
},
|
|
2147
|
+
],
|
|
2148
|
+
},
|
|
2149
|
+
],
|
|
2150
|
+
messages: [
|
|
2151
|
+
{
|
|
2152
|
+
type: 'state',
|
|
2153
|
+
name: 'TodoState',
|
|
2154
|
+
fields: [
|
|
2155
|
+
{ name: 'todoId', type: 'string', required: true },
|
|
2156
|
+
{ name: 'description', type: 'string', required: true },
|
|
2157
|
+
],
|
|
2158
|
+
metadata: { version: 1 },
|
|
2159
|
+
},
|
|
2160
|
+
],
|
|
2161
|
+
integrations: [],
|
|
2162
|
+
};
|
|
2163
|
+
|
|
2164
|
+
const code = await modelToNarrative(modelWithRegularProjection);
|
|
2165
|
+
|
|
2166
|
+
expect(code).toEqual(`import { data, narrative, query, source, specs } from '@auto-engineer/narrative';
|
|
2167
|
+
import type { State } from '@auto-engineer/narrative';
|
|
2168
|
+
type TodoState = State<
|
|
2169
|
+
'TodoState',
|
|
2170
|
+
{
|
|
2171
|
+
todoId: string;
|
|
2172
|
+
description: string;
|
|
2173
|
+
}
|
|
2174
|
+
>;
|
|
2175
|
+
narrative('Todo Flow', 'TODO-FLOW', () => {
|
|
2176
|
+
query('views todo', 'TODO-SLICE').server(() => {
|
|
2177
|
+
data([source().state('TodoState').fromProjection('Todos', 'todoId')]);
|
|
2178
|
+
specs('Todo Rules', () => {});
|
|
2179
|
+
});
|
|
2180
|
+
});
|
|
2181
|
+
`);
|
|
2182
|
+
});
|
|
2183
|
+
|
|
2184
|
+
it('should generate fromCompositeProjection with array idField for composite key projections', async () => {
|
|
2185
|
+
const modelWithCompositeProjection: Model = {
|
|
2186
|
+
variant: 'specs',
|
|
2187
|
+
narratives: [
|
|
2188
|
+
{
|
|
2189
|
+
name: 'User Project Flow',
|
|
2190
|
+
id: 'USER-PROJECT-FLOW',
|
|
2191
|
+
slices: [
|
|
2192
|
+
{
|
|
2193
|
+
name: 'views user project',
|
|
2194
|
+
id: 'USER-PROJECT-SLICE',
|
|
2195
|
+
type: 'query',
|
|
2196
|
+
client: {
|
|
2197
|
+
description: 'User project client',
|
|
2198
|
+
},
|
|
2199
|
+
server: {
|
|
2200
|
+
description: 'User project server',
|
|
2201
|
+
data: [
|
|
2202
|
+
{
|
|
2203
|
+
target: {
|
|
2204
|
+
type: 'State',
|
|
2205
|
+
name: 'UserProjectState',
|
|
2206
|
+
},
|
|
2207
|
+
origin: {
|
|
2208
|
+
type: 'projection',
|
|
2209
|
+
name: 'UserProjects',
|
|
2210
|
+
idField: ['userId', 'projectId'],
|
|
2211
|
+
},
|
|
2212
|
+
},
|
|
2213
|
+
],
|
|
2214
|
+
specs: {
|
|
2215
|
+
name: 'User Project Rules',
|
|
2216
|
+
rules: [],
|
|
2217
|
+
},
|
|
2218
|
+
},
|
|
2219
|
+
},
|
|
2220
|
+
],
|
|
2221
|
+
},
|
|
2222
|
+
],
|
|
2223
|
+
messages: [
|
|
2224
|
+
{
|
|
2225
|
+
type: 'state',
|
|
2226
|
+
name: 'UserProjectState',
|
|
2227
|
+
fields: [
|
|
2228
|
+
{ name: 'userId', type: 'string', required: true },
|
|
2229
|
+
{ name: 'projectId', type: 'string', required: true },
|
|
2230
|
+
{ name: 'role', type: 'string', required: true },
|
|
2231
|
+
],
|
|
2232
|
+
metadata: { version: 1 },
|
|
2233
|
+
},
|
|
2234
|
+
],
|
|
2235
|
+
integrations: [],
|
|
2236
|
+
};
|
|
2237
|
+
|
|
2238
|
+
const code = await modelToNarrative(modelWithCompositeProjection);
|
|
2239
|
+
|
|
2240
|
+
expect(code).toEqual(`import { data, narrative, query, source, specs } from '@auto-engineer/narrative';
|
|
2241
|
+
import type { State } from '@auto-engineer/narrative';
|
|
2242
|
+
type UserProjectState = State<
|
|
2243
|
+
'UserProjectState',
|
|
2244
|
+
{
|
|
2245
|
+
userId: string;
|
|
2246
|
+
projectId: string;
|
|
2247
|
+
role: string;
|
|
2248
|
+
}
|
|
2249
|
+
>;
|
|
2250
|
+
narrative('User Project Flow', 'USER-PROJECT-FLOW', () => {
|
|
2251
|
+
query('views user project', 'USER-PROJECT-SLICE').server(() => {
|
|
2252
|
+
data([source().state('UserProjectState').fromCompositeProjection('UserProjects', ['userId', 'projectId'])]);
|
|
2253
|
+
specs('User Project Rules', () => {});
|
|
2254
|
+
});
|
|
2255
|
+
});
|
|
2256
|
+
`);
|
|
2257
|
+
});
|
|
2258
|
+
|
|
2259
|
+
it('should generate all three projection types in a single narrative', async () => {
|
|
2260
|
+
const modelWithAllProjectionTypes: Model = {
|
|
2261
|
+
variant: 'specs',
|
|
2262
|
+
narratives: [
|
|
2263
|
+
{
|
|
2264
|
+
name: 'All Projection Types',
|
|
2265
|
+
id: 'ALL-PROJ',
|
|
2266
|
+
slices: [
|
|
2267
|
+
{
|
|
2268
|
+
name: 'views summary',
|
|
2269
|
+
id: 'SUMMARY-SLICE',
|
|
2270
|
+
type: 'query',
|
|
2271
|
+
client: {
|
|
2272
|
+
description: 'Summary client',
|
|
2273
|
+
},
|
|
2274
|
+
server: {
|
|
2275
|
+
description: 'Summary server',
|
|
2276
|
+
data: [
|
|
2277
|
+
{
|
|
2278
|
+
target: {
|
|
2279
|
+
type: 'State',
|
|
2280
|
+
name: 'TodoListSummary',
|
|
2281
|
+
},
|
|
2282
|
+
origin: {
|
|
2283
|
+
type: 'projection',
|
|
2284
|
+
name: 'TodoSummary',
|
|
2285
|
+
singleton: true,
|
|
2286
|
+
},
|
|
2287
|
+
},
|
|
2288
|
+
],
|
|
2289
|
+
specs: {
|
|
2290
|
+
name: 'Summary Rules',
|
|
2291
|
+
rules: [],
|
|
2292
|
+
},
|
|
2293
|
+
},
|
|
2294
|
+
},
|
|
2295
|
+
{
|
|
2296
|
+
name: 'views todo',
|
|
2297
|
+
id: 'TODO-SLICE',
|
|
2298
|
+
type: 'query',
|
|
2299
|
+
client: {
|
|
2300
|
+
description: 'Todo client',
|
|
2301
|
+
},
|
|
2302
|
+
server: {
|
|
2303
|
+
description: 'Todo server',
|
|
2304
|
+
data: [
|
|
2305
|
+
{
|
|
2306
|
+
target: {
|
|
2307
|
+
type: 'State',
|
|
2308
|
+
name: 'TodoState',
|
|
2309
|
+
},
|
|
2310
|
+
origin: {
|
|
2311
|
+
type: 'projection',
|
|
2312
|
+
name: 'Todos',
|
|
2313
|
+
idField: 'todoId',
|
|
2314
|
+
},
|
|
2315
|
+
},
|
|
2316
|
+
],
|
|
2317
|
+
specs: {
|
|
2318
|
+
name: 'Todo Rules',
|
|
2319
|
+
rules: [],
|
|
2320
|
+
},
|
|
2321
|
+
},
|
|
2322
|
+
},
|
|
2323
|
+
{
|
|
2324
|
+
name: 'views user project todos',
|
|
2325
|
+
id: 'USER-PROJECT-SLICE',
|
|
2326
|
+
type: 'query',
|
|
2327
|
+
client: {
|
|
2328
|
+
description: 'User project client',
|
|
2329
|
+
},
|
|
2330
|
+
server: {
|
|
2331
|
+
description: 'User project server',
|
|
2332
|
+
data: [
|
|
2333
|
+
{
|
|
2334
|
+
target: {
|
|
2335
|
+
type: 'State',
|
|
2336
|
+
name: 'UserProjectTodos',
|
|
2337
|
+
},
|
|
2338
|
+
origin: {
|
|
2339
|
+
type: 'projection',
|
|
2340
|
+
name: 'UserProjectTodos',
|
|
2341
|
+
idField: ['userId', 'projectId'],
|
|
2342
|
+
},
|
|
2343
|
+
},
|
|
2344
|
+
],
|
|
2345
|
+
specs: {
|
|
2346
|
+
name: 'User Project Rules',
|
|
2347
|
+
rules: [],
|
|
2348
|
+
},
|
|
2349
|
+
},
|
|
2350
|
+
},
|
|
2351
|
+
],
|
|
2352
|
+
},
|
|
2353
|
+
],
|
|
2354
|
+
messages: [
|
|
2355
|
+
{
|
|
2356
|
+
type: 'state',
|
|
2357
|
+
name: 'TodoListSummary',
|
|
2358
|
+
fields: [
|
|
2359
|
+
{ name: 'summaryId', type: 'string', required: true },
|
|
2360
|
+
{ name: 'totalTodos', type: 'number', required: true },
|
|
2361
|
+
],
|
|
2362
|
+
metadata: { version: 1 },
|
|
2363
|
+
},
|
|
2364
|
+
{
|
|
2365
|
+
type: 'state',
|
|
2366
|
+
name: 'TodoState',
|
|
2367
|
+
fields: [
|
|
2368
|
+
{ name: 'todoId', type: 'string', required: true },
|
|
2369
|
+
{ name: 'description', type: 'string', required: true },
|
|
2370
|
+
],
|
|
2371
|
+
metadata: { version: 1 },
|
|
2372
|
+
},
|
|
2373
|
+
{
|
|
2374
|
+
type: 'state',
|
|
2375
|
+
name: 'UserProjectTodos',
|
|
2376
|
+
fields: [
|
|
2377
|
+
{ name: 'userId', type: 'string', required: true },
|
|
2378
|
+
{ name: 'projectId', type: 'string', required: true },
|
|
2379
|
+
{ name: 'todos', type: 'Array<string>', required: true },
|
|
2380
|
+
],
|
|
2381
|
+
metadata: { version: 1 },
|
|
2382
|
+
},
|
|
2383
|
+
],
|
|
2384
|
+
integrations: [],
|
|
2385
|
+
};
|
|
2386
|
+
|
|
2387
|
+
const code = await modelToNarrative(modelWithAllProjectionTypes);
|
|
2388
|
+
|
|
2389
|
+
expect(code).toEqual(`import { data, narrative, query, source, specs } from '@auto-engineer/narrative';
|
|
2390
|
+
import type { State } from '@auto-engineer/narrative';
|
|
2391
|
+
type TodoListSummary = State<
|
|
2392
|
+
'TodoListSummary',
|
|
2393
|
+
{
|
|
2394
|
+
summaryId: string;
|
|
2395
|
+
totalTodos: number;
|
|
2396
|
+
}
|
|
2397
|
+
>;
|
|
2398
|
+
type TodoState = State<
|
|
2399
|
+
'TodoState',
|
|
2400
|
+
{
|
|
2401
|
+
todoId: string;
|
|
2402
|
+
description: string;
|
|
2403
|
+
}
|
|
2404
|
+
>;
|
|
2405
|
+
type UserProjectTodos = State<
|
|
2406
|
+
'UserProjectTodos',
|
|
2407
|
+
{
|
|
2408
|
+
userId: string;
|
|
2409
|
+
projectId: string;
|
|
2410
|
+
todos: string[];
|
|
2411
|
+
}
|
|
2412
|
+
>;
|
|
2413
|
+
narrative('All Projection Types', 'ALL-PROJ', () => {
|
|
2414
|
+
query('views summary', 'SUMMARY-SLICE').server(() => {
|
|
2415
|
+
data([source().state('TodoListSummary').fromSingletonProjection('TodoSummary')]);
|
|
2416
|
+
specs('Summary Rules', () => {});
|
|
2417
|
+
});
|
|
2418
|
+
query('views todo', 'TODO-SLICE').server(() => {
|
|
2419
|
+
data([source().state('TodoState').fromProjection('Todos', 'todoId')]);
|
|
2420
|
+
specs('Todo Rules', () => {});
|
|
2421
|
+
});
|
|
2422
|
+
query('views user project todos', 'USER-PROJECT-SLICE').server(() => {
|
|
2423
|
+
data([source().state('UserProjectTodos').fromCompositeProjection('UserProjectTodos', ['userId', 'projectId'])]);
|
|
2424
|
+
specs('User Project Rules', () => {});
|
|
2425
|
+
});
|
|
2426
|
+
});
|
|
2427
|
+
`);
|
|
2428
|
+
});
|
|
2429
|
+
});
|
|
2034
2430
|
});
|
package/src/schema.ts
CHANGED
|
@@ -49,7 +49,18 @@ export const OriginSchema = z
|
|
|
49
49
|
z.object({
|
|
50
50
|
type: z.literal('projection'),
|
|
51
51
|
name: z.string(),
|
|
52
|
-
idField: z
|
|
52
|
+
idField: z
|
|
53
|
+
.union([z.string(), z.array(z.string())])
|
|
54
|
+
.optional()
|
|
55
|
+
.describe(
|
|
56
|
+
'Field(s) from event used as the projection unique identifier. Can be single field or array for composite keys. Omit for singleton projections.',
|
|
57
|
+
),
|
|
58
|
+
singleton: z
|
|
59
|
+
.boolean()
|
|
60
|
+
.optional()
|
|
61
|
+
.describe(
|
|
62
|
+
'True if this is a singleton projection that aggregates data from multiple entities into one document',
|
|
63
|
+
),
|
|
53
64
|
}),
|
|
54
65
|
z.object({
|
|
55
66
|
type: z.literal('readModel'),
|
|
@@ -110,6 +110,42 @@ function addDestinationToChain(f: tsNS.NodeFactory, chain: tsNS.Expression, dest
|
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
function buildProjectionCall(
|
|
114
|
+
f: tsNS.NodeFactory,
|
|
115
|
+
baseCall: tsNS.Expression,
|
|
116
|
+
origin: { type: 'projection'; name: string; idField?: string | string[]; singleton?: boolean },
|
|
117
|
+
): tsNS.Expression {
|
|
118
|
+
if (origin.singleton === true) {
|
|
119
|
+
return f.createCallExpression(
|
|
120
|
+
f.createPropertyAccessExpression(baseCall, f.createIdentifier('fromSingletonProjection')),
|
|
121
|
+
undefined,
|
|
122
|
+
[f.createStringLiteral(origin.name)],
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
if (Array.isArray(origin.idField)) {
|
|
126
|
+
return f.createCallExpression(
|
|
127
|
+
f.createPropertyAccessExpression(baseCall, f.createIdentifier('fromCompositeProjection')),
|
|
128
|
+
undefined,
|
|
129
|
+
[
|
|
130
|
+
f.createStringLiteral(origin.name),
|
|
131
|
+
f.createArrayLiteralExpression(origin.idField.map((field) => f.createStringLiteral(field))),
|
|
132
|
+
],
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
if (typeof origin.idField === 'string' && origin.idField.length > 0) {
|
|
136
|
+
return f.createCallExpression(
|
|
137
|
+
f.createPropertyAccessExpression(baseCall, f.createIdentifier('fromProjection')),
|
|
138
|
+
undefined,
|
|
139
|
+
[f.createStringLiteral(origin.name), f.createStringLiteral(origin.idField)],
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
return f.createCallExpression(
|
|
143
|
+
f.createPropertyAccessExpression(baseCall, f.createIdentifier('fromSingletonProjection')),
|
|
144
|
+
undefined,
|
|
145
|
+
[f.createStringLiteral(origin.name)],
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
113
149
|
function buildStateCall(
|
|
114
150
|
ts: typeof import('typescript'),
|
|
115
151
|
f: tsNS.NodeFactory,
|
|
@@ -135,11 +171,7 @@ function buildStateCall(
|
|
|
135
171
|
);
|
|
136
172
|
}
|
|
137
173
|
case 'projection':
|
|
138
|
-
return f
|
|
139
|
-
f.createPropertyAccessExpression(baseStateCall, f.createIdentifier('fromProjection')),
|
|
140
|
-
undefined,
|
|
141
|
-
[f.createStringLiteral(origin.name), f.createStringLiteral(origin.idField)],
|
|
142
|
-
);
|
|
174
|
+
return buildProjectionCall(f, baseStateCall, origin);
|
|
143
175
|
case 'database': {
|
|
144
176
|
const args: tsNS.Expression[] = [f.createStringLiteral(origin.collection)];
|
|
145
177
|
if (origin.query !== null && origin.query !== undefined) {
|
|
@@ -173,10 +205,39 @@ function buildStateCall(
|
|
|
173
205
|
}
|
|
174
206
|
}
|
|
175
207
|
|
|
208
|
+
function buildProjectionArgs(
|
|
209
|
+
f: tsNS.NodeFactory,
|
|
210
|
+
origin: { name: string; idField?: string | string[]; singleton?: boolean },
|
|
211
|
+
): tsNS.Expression[] {
|
|
212
|
+
if (origin.singleton === true) {
|
|
213
|
+
return [f.createStringLiteral(origin.name)];
|
|
214
|
+
}
|
|
215
|
+
if (Array.isArray(origin.idField)) {
|
|
216
|
+
return [
|
|
217
|
+
f.createStringLiteral(origin.name),
|
|
218
|
+
f.createArrayLiteralExpression(origin.idField.map((field) => f.createStringLiteral(field))),
|
|
219
|
+
];
|
|
220
|
+
}
|
|
221
|
+
if (typeof origin.idField === 'string' && origin.idField.length > 0) {
|
|
222
|
+
return [f.createStringLiteral(origin.name), f.createStringLiteral(origin.idField)];
|
|
223
|
+
}
|
|
224
|
+
return [f.createStringLiteral(origin.name)];
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function getProjectionMethodName(origin: { idField?: string | string[]; singleton?: boolean }): string {
|
|
228
|
+
if (origin.singleton === true) {
|
|
229
|
+
return 'fromSingletonProjection';
|
|
230
|
+
}
|
|
231
|
+
if (Array.isArray(origin.idField)) {
|
|
232
|
+
return 'fromCompositeProjection';
|
|
233
|
+
}
|
|
234
|
+
return 'fromProjection';
|
|
235
|
+
}
|
|
236
|
+
|
|
176
237
|
function buildOriginArgs(ts: typeof import('typescript'), f: tsNS.NodeFactory, origin: Origin): tsNS.Expression[] {
|
|
177
238
|
switch (origin.type) {
|
|
178
239
|
case 'projection':
|
|
179
|
-
return
|
|
240
|
+
return buildProjectionArgs(f, origin);
|
|
180
241
|
case 'integration': {
|
|
181
242
|
const [sys] = origin.systems;
|
|
182
243
|
return [f.createIdentifier(sys)];
|
|
@@ -205,7 +266,7 @@ function buildOriginArgs(ts: typeof import('typescript'), f: tsNS.NodeFactory, o
|
|
|
205
266
|
function getOriginMethodName(origin: Origin): string {
|
|
206
267
|
switch (origin.type) {
|
|
207
268
|
case 'projection':
|
|
208
|
-
return
|
|
269
|
+
return getProjectionMethodName(origin);
|
|
209
270
|
case 'integration':
|
|
210
271
|
return 'fromIntegration';
|
|
211
272
|
case 'database':
|
|
@@ -454,27 +515,25 @@ function buildServerStatements(
|
|
|
454
515
|
const ruleGroups = buildRuleGroups(server.specs.rules as RuleType[]);
|
|
455
516
|
const allRuleStatements = buildConsolidatedRules(ts, f, ruleGroups, sliceType, messages);
|
|
456
517
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
);
|
|
466
|
-
|
|
467
|
-
const args: tsNS.Expression[] = [];
|
|
468
|
-
if (server.specs.name && server.specs.name.trim() !== '') {
|
|
469
|
-
args.push(f.createStringLiteral(server.specs.name));
|
|
470
|
-
}
|
|
471
|
-
args.push(arrowFunction);
|
|
518
|
+
const arrowFunction = f.createArrowFunction(
|
|
519
|
+
undefined,
|
|
520
|
+
undefined,
|
|
521
|
+
[],
|
|
522
|
+
undefined,
|
|
523
|
+
f.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
|
|
524
|
+
f.createBlock(allRuleStatements, true),
|
|
525
|
+
);
|
|
472
526
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
);
|
|
476
|
-
statements.push(specsStatement);
|
|
527
|
+
const args: tsNS.Expression[] = [];
|
|
528
|
+
if (server.specs.name && server.specs.name.trim() !== '') {
|
|
529
|
+
args.push(f.createStringLiteral(server.specs.name));
|
|
477
530
|
}
|
|
531
|
+
args.push(arrowFunction);
|
|
532
|
+
|
|
533
|
+
const specsStatement = f.createExpressionStatement(
|
|
534
|
+
f.createCallExpression(f.createIdentifier('specs'), undefined, args),
|
|
535
|
+
);
|
|
536
|
+
statements.push(specsStatement);
|
|
478
537
|
}
|
|
479
538
|
|
|
480
539
|
return statements;
|
package/src/types.ts
CHANGED
|
@@ -87,9 +87,11 @@ export const createDatabaseDestination = (collection: string): DatabaseDestinati
|
|
|
87
87
|
});
|
|
88
88
|
export const createTopicDestination = (name: string): TopicDestination => ({ type: 'topic', name });
|
|
89
89
|
|
|
90
|
-
export interface
|
|
90
|
+
export interface ProjectionOriginWithId {
|
|
91
91
|
type: 'projection';
|
|
92
92
|
name: string;
|
|
93
|
+
idField?: string | string[];
|
|
94
|
+
singleton?: boolean;
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
export interface ReadModelOrigin {
|
|
@@ -116,14 +118,8 @@ export interface IntegrationOrigin {
|
|
|
116
118
|
|
|
117
119
|
export type Origin = ProjectionOriginWithId | ReadModelOrigin | DatabaseOrigin | ApiOrigin | IntegrationOrigin;
|
|
118
120
|
|
|
119
|
-
export interface ProjectionOriginWithId {
|
|
120
|
-
type: 'projection';
|
|
121
|
-
name: string;
|
|
122
|
-
idField: string;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
121
|
// Helper functions to create origins
|
|
126
|
-
export const createProjectionOrigin = (name: string):
|
|
122
|
+
export const createProjectionOrigin = (name: string): ProjectionOriginWithId => ({ type: 'projection', name });
|
|
127
123
|
export const createReadModelOrigin = (name: string): ReadModelOrigin => ({ type: 'readModel', name });
|
|
128
124
|
export const createDatabaseOrigin = (collection: string, query?: Record<string, unknown>): DatabaseOrigin => ({
|
|
129
125
|
type: 'database',
|
|
@@ -161,7 +157,7 @@ export interface DataItem {
|
|
|
161
157
|
__type: 'sink' | 'source';
|
|
162
158
|
}
|
|
163
159
|
|
|
164
|
-
type DefaultRecord = Record<string, unknown>;
|
|
160
|
+
export type DefaultRecord = Record<string, unknown>;
|
|
165
161
|
|
|
166
162
|
export type State<
|
|
167
163
|
StateType extends string = string,
|
|
@@ -224,3 +220,5 @@ export type Event<
|
|
|
224
220
|
> & {
|
|
225
221
|
readonly kind?: 'Event';
|
|
226
222
|
};
|
|
223
|
+
|
|
224
|
+
export type ExtractStateData<T> = T extends State<string, infer Data, DefaultRecord | undefined> ? Data : never;
|