@apso/cli 0.16.1 → 0.18.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/dist/commands/generate.js +10 -2
- package/dist/commands/mcp/serve.js +14 -0
- package/dist/lib/apsorc-parser.d.ts +8 -0
- package/dist/lib/apsorc-parser.js +4 -0
- package/dist/lib/events.d.ts +32 -0
- package/dist/lib/events.js +44 -0
- package/dist/lib/generators/base.d.ts +12 -1
- package/dist/lib/generators/base.js +9 -0
- package/dist/lib/generators/typescript.d.ts +23 -1
- package/dist/lib/generators/typescript.js +36 -1
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.js +5 -1
- package/dist/lib/templates/events/event-emitting.entities.eta +23 -0
- package/dist/lib/types/entity.d.ts +10 -0
- package/dist/lib/types/generator.d.ts +15 -1
- package/npm-shrinkwrap.json +2 -2
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
|
@@ -13,7 +13,7 @@ class Generate extends base_command_1.default {
|
|
|
13
13
|
const { flags } = await this.parse(Generate);
|
|
14
14
|
const skipFormat = flags["skip-format"];
|
|
15
15
|
const totalBuildStart = perf_hooks_1.performance.now();
|
|
16
|
-
const { rootFolder, entities, relationshipMap, apiType, auth, language: configLanguage } = (0, lib_1.parseApsorc)();
|
|
16
|
+
const { rootFolder, entities, relationshipMap, apiType, auth, emitEvents, language: configLanguage } = (0, lib_1.parseApsorc)();
|
|
17
17
|
// Resolve language: flag > .apsorc > prompt
|
|
18
18
|
let language;
|
|
19
19
|
if (flags.language) {
|
|
@@ -63,6 +63,7 @@ class Generate extends base_command_1.default {
|
|
|
63
63
|
entities,
|
|
64
64
|
relationshipMap,
|
|
65
65
|
auth,
|
|
66
|
+
emitEvents,
|
|
66
67
|
};
|
|
67
68
|
const generator = (0, lib_1.createGenerator)(generatorConfig);
|
|
68
69
|
const validationResult = generator.validateConfig(generatorConfig);
|
|
@@ -148,8 +149,15 @@ class Generate extends base_command_1.default {
|
|
|
148
149
|
await (0, file_system_1.createFile)(fullPath, file.content);
|
|
149
150
|
}
|
|
150
151
|
generatedFileCount += guardFiles.length;
|
|
152
|
+
// Generate domain-event spine (only when at least one entity opts in)
|
|
153
|
+
const domainEventFiles = await generator.generateDomainEvents(entities, lowerCaseApiType, { emitEvents });
|
|
154
|
+
for (const file of domainEventFiles) {
|
|
155
|
+
const fullPath = path.join(autogenPath, file.path);
|
|
156
|
+
// eslint-disable-next-line no-await-in-loop
|
|
157
|
+
await (0, file_system_1.createFile)(fullPath, file.content);
|
|
158
|
+
}
|
|
151
159
|
// Generate index module
|
|
152
|
-
const indexFiles = await generator.generateIndexModule(entities, lowerCaseApiType);
|
|
160
|
+
const indexFiles = await generator.generateIndexModule(entities, lowerCaseApiType, { emitEvents });
|
|
153
161
|
for (const file of indexFiles) {
|
|
154
162
|
const fullPath = path.join(autogenPath, file.path);
|
|
155
163
|
// eslint-disable-next-line no-await-in-loop
|
|
@@ -923,6 +923,7 @@ const INLINE_SCHEMA_REFERENCE = `# Schema Quick Reference
|
|
|
923
923
|
"updated_at": true,
|
|
924
924
|
"primaryKeyType": "serial",
|
|
925
925
|
"scopeBy": "organizationId",
|
|
926
|
+
"emitEvents": true,
|
|
926
927
|
"fields": [
|
|
927
928
|
{ "name": "fieldName", "type": "text", "length": 100 }
|
|
928
929
|
],
|
|
@@ -932,6 +933,19 @@ const INLINE_SCHEMA_REFERENCE = `# Schema Quick Reference
|
|
|
932
933
|
}
|
|
933
934
|
\`\`\`
|
|
934
935
|
|
|
936
|
+
## Domain Events (emitEvents)
|
|
937
|
+
Set \`emitEvents: true\` (per-entity or as a top-level default) to durably log
|
|
938
|
+
state changes. A top-level \`emitEvents: true\` enables it for every entity; an
|
|
939
|
+
individual entity can opt out with \`emitEvents: false\` (effective value is
|
|
940
|
+
\`entity.emitEvents ?? <top-level emitEvents> ?? false\`).
|
|
941
|
+
|
|
942
|
+
When at least one entity opts in, the generator emits a single schema-derived
|
|
943
|
+
manifest at \`autogen/events/event-emitting.entities.ts\` exporting
|
|
944
|
+
\`EVENT_EMITTING_ENTITIES\` (the opted-in entity classes) and
|
|
945
|
+
\`EVENT_EMITTING_ENTITY_NAMES\`. The domain-event engine itself ships in the
|
|
946
|
+
\`@apso/domain-events\` library and is wired by the \`domain-events\` skill; the
|
|
947
|
+
CLI no longer generates the engine code.
|
|
948
|
+
|
|
935
949
|
## Relationship Types
|
|
936
950
|
- OneToMany: parent has many children
|
|
937
951
|
- ManyToOne: child belongs to parent
|
|
@@ -14,6 +14,12 @@ export type ApsorcType = {
|
|
|
14
14
|
relationships: ApsorcRelationship[];
|
|
15
15
|
auth?: AuthConfig;
|
|
16
16
|
language?: TargetLanguage;
|
|
17
|
+
/**
|
|
18
|
+
* Top-level default for the opt-in DomainEvent ("emitEvents") feature.
|
|
19
|
+
* When true, every entity emits domain events unless it opts out with
|
|
20
|
+
* its own `emitEvents: false`.
|
|
21
|
+
*/
|
|
22
|
+
emitEvents?: boolean;
|
|
17
23
|
};
|
|
18
24
|
type ParsedApsorcData = {
|
|
19
25
|
entities: Entity[];
|
|
@@ -26,6 +32,8 @@ type ParsedApsorc = {
|
|
|
26
32
|
relationshipMap: RelationshipMap;
|
|
27
33
|
auth?: AuthConfig;
|
|
28
34
|
language?: TargetLanguage;
|
|
35
|
+
/** Top-level default for the DomainEvent ("emitEvents") feature. */
|
|
36
|
+
emitEvents?: boolean;
|
|
29
37
|
};
|
|
30
38
|
export declare const parseApsorcV1: (apsorc: ApsorcType) => ParsedApsorcData;
|
|
31
39
|
export declare const parseApsorcV2: (apsorc: ApsorcType) => ParsedApsorcData;
|
|
@@ -36,6 +36,7 @@ const parseRc = () => {
|
|
|
36
36
|
const relationships = apsoConfig.relationships || [];
|
|
37
37
|
const auth = apsoConfig.auth;
|
|
38
38
|
const language = apsoConfig.language;
|
|
39
|
+
const emitEvents = apsoConfig.emitEvents;
|
|
39
40
|
return {
|
|
40
41
|
rootFolder,
|
|
41
42
|
apiType,
|
|
@@ -44,6 +45,7 @@ const parseRc = () => {
|
|
|
44
45
|
relationships,
|
|
45
46
|
auth,
|
|
46
47
|
language,
|
|
48
|
+
emitEvents,
|
|
47
49
|
};
|
|
48
50
|
};
|
|
49
51
|
const parseApsorc = () => {
|
|
@@ -63,6 +65,7 @@ const parseApsorc = () => {
|
|
|
63
65
|
apiType: apsoConfig.apiType,
|
|
64
66
|
auth: apsoConfig.auth,
|
|
65
67
|
language: apsoConfig.language,
|
|
68
|
+
emitEvents: apsoConfig.emitEvents,
|
|
66
69
|
...(0, exports.parseApsorcV1)(apsoConfig),
|
|
67
70
|
};
|
|
68
71
|
if (debug) {
|
|
@@ -79,6 +82,7 @@ const parseApsorc = () => {
|
|
|
79
82
|
apiType: apsoConfig.apiType,
|
|
80
83
|
auth: apsoConfig.auth,
|
|
81
84
|
language: apsoConfig.language,
|
|
85
|
+
emitEvents: apsoConfig.emitEvents,
|
|
82
86
|
...(() => {
|
|
83
87
|
const relStart = perf_hooks_1.performance.now();
|
|
84
88
|
const parsed = (0, exports.parseApsorcV2)(apsoConfig);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Entity } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Helpers for the opt-in DomainEvent ("emitEvents") feature.
|
|
4
|
+
*
|
|
5
|
+
* Under the Apso Distribution Model the domain-event engine ships in the
|
|
6
|
+
* `@apso/domain-events` library (wired by the `domain-events` skill). The CLI
|
|
7
|
+
* only uses these helpers to compute which entities opted in, and emits a
|
|
8
|
+
* schema-derived manifest (`events/event-emitting.entities.ts`).
|
|
9
|
+
*
|
|
10
|
+
* Flag resolution: the effective per-entity value is
|
|
11
|
+
* `entity.emitEvents ?? globalEmitEvents ?? false`
|
|
12
|
+
* so a top-level `emitEvents: true` enables it for every entity, while an
|
|
13
|
+
* individual entity can opt out with `emitEvents: false` (global default WITH
|
|
14
|
+
* per-entity opt-out).
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Computes whether a single entity has domain events enabled, given the
|
|
18
|
+
* top-level (global) default.
|
|
19
|
+
*/
|
|
20
|
+
export declare function isEmitEventsEnabled(entity: Entity, globalEmitEvents?: boolean): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Computes the set of entities that have opted in to domain-event emission.
|
|
23
|
+
* This is the single source of truth used everywhere (generator, wiring,
|
|
24
|
+
* subscriber scoping).
|
|
25
|
+
*/
|
|
26
|
+
export declare function getEventEmittingEntities(entities: Entity[], globalEmitEvents?: boolean): Entity[];
|
|
27
|
+
/**
|
|
28
|
+
* Returns true when at least one entity has domain events enabled, meaning the
|
|
29
|
+
* event-emitting manifest (`events/event-emitting.entities.ts`) should be
|
|
30
|
+
* generated.
|
|
31
|
+
*/
|
|
32
|
+
export declare function hasEventEmittingEntities(entities: Entity[], globalEmitEvents?: boolean): boolean;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hasEventEmittingEntities = exports.getEventEmittingEntities = exports.isEmitEventsEnabled = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Helpers for the opt-in DomainEvent ("emitEvents") feature.
|
|
6
|
+
*
|
|
7
|
+
* Under the Apso Distribution Model the domain-event engine ships in the
|
|
8
|
+
* `@apso/domain-events` library (wired by the `domain-events` skill). The CLI
|
|
9
|
+
* only uses these helpers to compute which entities opted in, and emits a
|
|
10
|
+
* schema-derived manifest (`events/event-emitting.entities.ts`).
|
|
11
|
+
*
|
|
12
|
+
* Flag resolution: the effective per-entity value is
|
|
13
|
+
* `entity.emitEvents ?? globalEmitEvents ?? false`
|
|
14
|
+
* so a top-level `emitEvents: true` enables it for every entity, while an
|
|
15
|
+
* individual entity can opt out with `emitEvents: false` (global default WITH
|
|
16
|
+
* per-entity opt-out).
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Computes whether a single entity has domain events enabled, given the
|
|
20
|
+
* top-level (global) default.
|
|
21
|
+
*/
|
|
22
|
+
function isEmitEventsEnabled(entity, globalEmitEvents) {
|
|
23
|
+
var _a, _b;
|
|
24
|
+
return (_b = (_a = entity.emitEvents) !== null && _a !== void 0 ? _a : globalEmitEvents) !== null && _b !== void 0 ? _b : false;
|
|
25
|
+
}
|
|
26
|
+
exports.isEmitEventsEnabled = isEmitEventsEnabled;
|
|
27
|
+
/**
|
|
28
|
+
* Computes the set of entities that have opted in to domain-event emission.
|
|
29
|
+
* This is the single source of truth used everywhere (generator, wiring,
|
|
30
|
+
* subscriber scoping).
|
|
31
|
+
*/
|
|
32
|
+
function getEventEmittingEntities(entities, globalEmitEvents) {
|
|
33
|
+
return entities.filter((entity) => isEmitEventsEnabled(entity, globalEmitEvents));
|
|
34
|
+
}
|
|
35
|
+
exports.getEventEmittingEntities = getEventEmittingEntities;
|
|
36
|
+
/**
|
|
37
|
+
* Returns true when at least one entity has domain events enabled, meaning the
|
|
38
|
+
* event-emitting manifest (`events/event-emitting.entities.ts`) should be
|
|
39
|
+
* generated.
|
|
40
|
+
*/
|
|
41
|
+
function hasEventEmittingEntities(entities, globalEmitEvents) {
|
|
42
|
+
return entities.some((entity) => isEmitEventsEnabled(entity, globalEmitEvents));
|
|
43
|
+
}
|
|
44
|
+
exports.hasEventEmittingEntities = hasEventEmittingEntities;
|
|
@@ -52,7 +52,9 @@ export declare abstract class BaseGenerator implements LanguageGenerator {
|
|
|
52
52
|
/**
|
|
53
53
|
* Generate the index/main module file
|
|
54
54
|
*/
|
|
55
|
-
abstract generateIndexModule(entities: Entity[], apiType: string
|
|
55
|
+
abstract generateIndexModule(entities: Entity[], apiType: string, opts?: {
|
|
56
|
+
emitEvents?: boolean;
|
|
57
|
+
}): Promise<GeneratedFile[]>;
|
|
56
58
|
/**
|
|
57
59
|
* Generate guard files (auth, scope, etc.)
|
|
58
60
|
*/
|
|
@@ -62,6 +64,15 @@ export declare abstract class BaseGenerator implements LanguageGenerator {
|
|
|
62
64
|
* Default no-op. Override in language generators that need query utils.
|
|
63
65
|
*/
|
|
64
66
|
generateQueryUtils(_entities: Entity[], _apiType: string): Promise<GeneratedFile[]>;
|
|
67
|
+
/**
|
|
68
|
+
* Emit a schema-derived manifest of entities that opt in to `emitEvents`.
|
|
69
|
+
* The domain-event engine ships in the `@apso/domain-events` library (wired
|
|
70
|
+
* by the `domain-events` skill); the generator only declares participants.
|
|
71
|
+
* Default no-op. Override in language generators that support it.
|
|
72
|
+
*/
|
|
73
|
+
generateDomainEvents(_entities: Entity[], _apiType?: string, _opts?: {
|
|
74
|
+
emitEvents?: boolean;
|
|
75
|
+
}): Promise<GeneratedFile[]>;
|
|
65
76
|
/**
|
|
66
77
|
* Render a template file with the given data
|
|
67
78
|
*/
|
|
@@ -27,6 +27,15 @@ class BaseGenerator {
|
|
|
27
27
|
async generateQueryUtils(_entities, _apiType) {
|
|
28
28
|
return [];
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Emit a schema-derived manifest of entities that opt in to `emitEvents`.
|
|
32
|
+
* The domain-event engine ships in the `@apso/domain-events` library (wired
|
|
33
|
+
* by the `domain-events` skill); the generator only declares participants.
|
|
34
|
+
* Default no-op. Override in language generators that support it.
|
|
35
|
+
*/
|
|
36
|
+
async generateDomainEvents(_entities, _apiType, _opts) {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
30
39
|
/**
|
|
31
40
|
* Render a template file with the given data
|
|
32
41
|
*/
|
|
@@ -16,8 +16,30 @@ export declare class TypeScriptGenerator extends BaseGenerator {
|
|
|
16
16
|
generateModule(options: EntityGenerationOptions): Promise<GeneratedFile[]>;
|
|
17
17
|
generateMigration(_options: MigrationGenerationOptions): Promise<GeneratedFile[]>;
|
|
18
18
|
generateEnums(entities: Entity[], apiType: string): Promise<GeneratedFile[]>;
|
|
19
|
-
generateIndexModule(entities: Entity[], apiType: string
|
|
19
|
+
generateIndexModule(entities: Entity[], apiType: string, _opts?: {
|
|
20
|
+
emitEvents?: boolean;
|
|
21
|
+
}): Promise<GeneratedFile[]>;
|
|
20
22
|
generateGuards(entities: Entity[], auth?: AuthConfig): Promise<GeneratedFile[]>;
|
|
23
|
+
/**
|
|
24
|
+
* Emits a schema-derived manifest of the entities that have opted in to
|
|
25
|
+
* domain-event emission (`.apsorc` `emitEvents`).
|
|
26
|
+
*
|
|
27
|
+
* Under the Apso Distribution Model the domain-event engine no longer ships
|
|
28
|
+
* as generated code; it lives in the `@apso/domain-events` library and is
|
|
29
|
+
* wired by the `domain-events` skill. The CLI's only responsibility is to
|
|
30
|
+
* declare WHICH entities participate, via a single generated file:
|
|
31
|
+
* `events/event-emitting.entities.ts`.
|
|
32
|
+
*
|
|
33
|
+
* Returns an empty array when no entity is opted in, so callers can skip
|
|
34
|
+
* wiring entirely.
|
|
35
|
+
*
|
|
36
|
+
* @param entities All entities from the configuration
|
|
37
|
+
* @param _apiType API type (unused; events are REST/Graphql agnostic)
|
|
38
|
+
* @param opts.emitEvents Top-level (global) default for emitEvents
|
|
39
|
+
*/
|
|
40
|
+
generateDomainEvents(entities: Entity[], _apiType?: string, opts?: {
|
|
41
|
+
emitEvents?: boolean;
|
|
42
|
+
}): Promise<GeneratedFile[]>;
|
|
21
43
|
/**
|
|
22
44
|
* Normalizes auth config by applying defaults for DB session providers
|
|
23
45
|
*/
|
|
@@ -15,6 +15,7 @@ const fs = tslib_1.__importStar(require("fs"));
|
|
|
15
15
|
const os = tslib_1.__importStar(require("os"));
|
|
16
16
|
const util_1 = require("util");
|
|
17
17
|
const guards_1 = require("../guards");
|
|
18
|
+
const events_1 = require("../events");
|
|
18
19
|
const auth_1 = require("../types/auth");
|
|
19
20
|
const unlinkAsync = (0, util_1.promisify)(fs.unlink);
|
|
20
21
|
/**
|
|
@@ -302,7 +303,7 @@ class TypeScriptGenerator extends base_1.BaseGenerator {
|
|
|
302
303
|
},
|
|
303
304
|
];
|
|
304
305
|
}
|
|
305
|
-
async generateIndexModule(entities, apiType) {
|
|
306
|
+
async generateIndexModule(entities, apiType, _opts) {
|
|
306
307
|
const content = await this.renderTemplate(`./${apiType}/index-module-${apiType}`, { entities });
|
|
307
308
|
return [
|
|
308
309
|
{
|
|
@@ -355,6 +356,40 @@ class TypeScriptGenerator extends base_1.BaseGenerator {
|
|
|
355
356
|
});
|
|
356
357
|
return files;
|
|
357
358
|
}
|
|
359
|
+
/**
|
|
360
|
+
* Emits a schema-derived manifest of the entities that have opted in to
|
|
361
|
+
* domain-event emission (`.apsorc` `emitEvents`).
|
|
362
|
+
*
|
|
363
|
+
* Under the Apso Distribution Model the domain-event engine no longer ships
|
|
364
|
+
* as generated code; it lives in the `@apso/domain-events` library and is
|
|
365
|
+
* wired by the `domain-events` skill. The CLI's only responsibility is to
|
|
366
|
+
* declare WHICH entities participate, via a single generated file:
|
|
367
|
+
* `events/event-emitting.entities.ts`.
|
|
368
|
+
*
|
|
369
|
+
* Returns an empty array when no entity is opted in, so callers can skip
|
|
370
|
+
* wiring entirely.
|
|
371
|
+
*
|
|
372
|
+
* @param entities All entities from the configuration
|
|
373
|
+
* @param _apiType API type (unused; events are REST/Graphql agnostic)
|
|
374
|
+
* @param opts.emitEvents Top-level (global) default for emitEvents
|
|
375
|
+
*/
|
|
376
|
+
async generateDomainEvents(entities, _apiType, opts) {
|
|
377
|
+
const emittingEntities = (0, events_1.getEventEmittingEntities)(entities, opts === null || opts === void 0 ? void 0 : opts.emitEvents);
|
|
378
|
+
if (emittingEntities.length === 0) {
|
|
379
|
+
return [];
|
|
380
|
+
}
|
|
381
|
+
const content = await this.renderTemplate("./events/event-emitting.entities.eta", {
|
|
382
|
+
emittingEntities: emittingEntities.map((entity) => ({
|
|
383
|
+
name: entity.name,
|
|
384
|
+
})),
|
|
385
|
+
});
|
|
386
|
+
return [
|
|
387
|
+
{
|
|
388
|
+
path: "events/event-emitting.entities.ts",
|
|
389
|
+
content,
|
|
390
|
+
},
|
|
391
|
+
];
|
|
392
|
+
}
|
|
358
393
|
/**
|
|
359
394
|
* Normalizes auth config by applying defaults for DB session providers
|
|
360
395
|
*/
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { Entity, RelationshipMap } from "./types";
|
|
2
2
|
export { parseApsorc } from "./apsorc-parser";
|
|
3
3
|
export { hasScopedEntities, getScopedEntities } from "./guards";
|
|
4
|
+
export { isEmitEventsEnabled, getEventEmittingEntities, hasEventEmittingEntities, } from "./events";
|
|
4
5
|
export { BaseGenerator, TypeScriptGenerator, createGenerator, isLanguageSupported, getSupportedLanguages, getImplementedLanguages, } from "./generators";
|
|
5
6
|
export type { TargetLanguage, GeneratedFile, ValidationResult, GeneratorConfig, LanguageGenerator, } from "./types";
|
package/dist/lib/index.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getImplementedLanguages = exports.getSupportedLanguages = exports.isLanguageSupported = exports.createGenerator = exports.TypeScriptGenerator = exports.BaseGenerator = exports.getScopedEntities = exports.hasScopedEntities = exports.parseApsorc = void 0;
|
|
3
|
+
exports.getImplementedLanguages = exports.getSupportedLanguages = exports.isLanguageSupported = exports.createGenerator = exports.TypeScriptGenerator = exports.BaseGenerator = exports.hasEventEmittingEntities = exports.getEventEmittingEntities = exports.isEmitEventsEnabled = exports.getScopedEntities = exports.hasScopedEntities = exports.parseApsorc = void 0;
|
|
4
4
|
var apsorc_parser_1 = require("./apsorc-parser");
|
|
5
5
|
Object.defineProperty(exports, "parseApsorc", { enumerable: true, get: function () { return apsorc_parser_1.parseApsorc; } });
|
|
6
6
|
var guards_1 = require("./guards");
|
|
7
7
|
Object.defineProperty(exports, "hasScopedEntities", { enumerable: true, get: function () { return guards_1.hasScopedEntities; } });
|
|
8
8
|
Object.defineProperty(exports, "getScopedEntities", { enumerable: true, get: function () { return guards_1.getScopedEntities; } });
|
|
9
|
+
var events_1 = require("./events");
|
|
10
|
+
Object.defineProperty(exports, "isEmitEventsEnabled", { enumerable: true, get: function () { return events_1.isEmitEventsEnabled; } });
|
|
11
|
+
Object.defineProperty(exports, "getEventEmittingEntities", { enumerable: true, get: function () { return events_1.getEventEmittingEntities; } });
|
|
12
|
+
Object.defineProperty(exports, "hasEventEmittingEntities", { enumerable: true, get: function () { return events_1.hasEventEmittingEntities; } });
|
|
9
13
|
// Generator exports
|
|
10
14
|
var generators_1 = require("./generators");
|
|
11
15
|
Object.defineProperty(exports, "BaseGenerator", { enumerable: true, get: function () { return generators_1.BaseGenerator; } });
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<%~ includeFile('../header.eta') %>
|
|
2
|
+
|
|
3
|
+
<% it.emittingEntities.forEach((entity) => { %>
|
|
4
|
+
import { <%= entity.name %> } from '../<%= entity.name %>/<%= entity.name %>.entity';
|
|
5
|
+
<%}) %>
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Schema-derived manifest of entities that opted in to domain-event emission
|
|
9
|
+
* (`.apsorc` `emitEvents`). The domain-event engine itself lives in the
|
|
10
|
+
* `@apso/domain-events` library and is wired by the `domain-events` skill;
|
|
11
|
+
* this file only declares WHICH entities participate.
|
|
12
|
+
*/
|
|
13
|
+
export const EVENT_EMITTING_ENTITIES = [
|
|
14
|
+
<% it.emittingEntities.forEach((entity) => { %>
|
|
15
|
+
<%= entity.name %>,
|
|
16
|
+
<%}) %>
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
export const EVENT_EMITTING_ENTITY_NAMES = [
|
|
20
|
+
<% it.emittingEntities.forEach((entity) => { %>
|
|
21
|
+
'<%= entity.name %>',
|
|
22
|
+
<%}) %>
|
|
23
|
+
];
|
|
@@ -45,5 +45,15 @@ export interface Entity {
|
|
|
45
45
|
* Optional configuration for how scope enforcement should behave.
|
|
46
46
|
*/
|
|
47
47
|
scopeOptions?: ScopeOptions;
|
|
48
|
+
/**
|
|
49
|
+
* When true, state changes to this entity (insert/update/remove) emit a
|
|
50
|
+
* durable DomainEvent row written in the SAME database transaction as the
|
|
51
|
+
* change (transactional-outbox pattern, surfaced as generic "domain events").
|
|
52
|
+
*
|
|
53
|
+
* Resolution: the effective value is `entity.emitEvents ?? <global emitEvents> ?? false`.
|
|
54
|
+
* This means a top-level `emitEvents: true` enables it for every entity, while
|
|
55
|
+
* an individual entity can opt out with `emitEvents: false`.
|
|
56
|
+
*/
|
|
57
|
+
emitEvents?: boolean;
|
|
48
58
|
associations?: Association[];
|
|
49
59
|
}
|
|
@@ -104,6 +104,11 @@ export interface GeneratorConfig {
|
|
|
104
104
|
* Authentication configuration (optional)
|
|
105
105
|
*/
|
|
106
106
|
auth?: AuthConfig;
|
|
107
|
+
/**
|
|
108
|
+
* Top-level default for the opt-in DomainEvent ("emitEvents") feature.
|
|
109
|
+
* Effective per-entity value is `entity.emitEvents ?? emitEvents ?? false`.
|
|
110
|
+
*/
|
|
111
|
+
emitEvents?: boolean;
|
|
107
112
|
/**
|
|
108
113
|
* Language-specific configuration options
|
|
109
114
|
*/
|
|
@@ -229,7 +234,9 @@ export interface LanguageGenerator {
|
|
|
229
234
|
/**
|
|
230
235
|
* Generate the index/main module file that ties everything together
|
|
231
236
|
*/
|
|
232
|
-
generateIndexModule(entities: Entity[], apiType: string
|
|
237
|
+
generateIndexModule(entities: Entity[], apiType: string, opts?: {
|
|
238
|
+
emitEvents?: boolean;
|
|
239
|
+
}): Promise<GeneratedFile[]>;
|
|
233
240
|
/**
|
|
234
241
|
* Generate guard files (auth, scope, etc.)
|
|
235
242
|
*/
|
|
@@ -238,6 +245,13 @@ export interface LanguageGenerator {
|
|
|
238
245
|
* Generate shared query utility files (filter, sort, pagination)
|
|
239
246
|
*/
|
|
240
247
|
generateQueryUtils(entities: Entity[], apiType: string): Promise<GeneratedFile[]>;
|
|
248
|
+
/**
|
|
249
|
+
* Emit a schema-derived manifest of entities that opt in to `emitEvents`
|
|
250
|
+
* (engine lives in `@apso/domain-events`). Returns [] when none are opted in.
|
|
251
|
+
*/
|
|
252
|
+
generateDomainEvents(entities: Entity[], apiType?: string, opts?: {
|
|
253
|
+
emitEvents?: boolean;
|
|
254
|
+
}): Promise<GeneratedFile[]>;
|
|
241
255
|
}
|
|
242
256
|
/**
|
|
243
257
|
* Factory function type for creating language generators
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apso/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@apso/cli",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.18.0",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@electric-sql/pglite": "^0.2.17",
|
package/oclif.manifest.json
CHANGED