@crossdelta/cloudevents 0.6.3 → 0.6.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/bin/cli.js +1226 -0
- package/dist/index.cjs +1507 -333
- package/dist/{index.d.mts → index.d.cts} +705 -52
- package/dist/index.d.ts +705 -52
- package/dist/index.js +1484 -326
- package/package.json +24 -10
package/dist/index.cjs
CHANGED
|
@@ -1,14 +1,40 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var cloudevents = require('cloudevents');
|
|
4
|
+
var pluralizeLib = require('pluralize');
|
|
4
5
|
var fs = require('fs');
|
|
5
6
|
var path = require('path');
|
|
7
|
+
var flowcore = require('@crossdelta/flowcore');
|
|
6
8
|
var url = require('url');
|
|
7
9
|
var glob = require('glob');
|
|
8
10
|
var zod = require('zod');
|
|
9
11
|
var nats = require('nats');
|
|
10
12
|
|
|
11
13
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
14
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
|
+
|
|
16
|
+
function _interopNamespace(e) {
|
|
17
|
+
if (e && e.__esModule) return e;
|
|
18
|
+
var n = Object.create(null);
|
|
19
|
+
if (e) {
|
|
20
|
+
Object.keys(e).forEach(function (k) {
|
|
21
|
+
if (k !== 'default') {
|
|
22
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
23
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
get: function () { return e[k]; }
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
n.default = e;
|
|
31
|
+
return Object.freeze(n);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
var pluralizeLib__default = /*#__PURE__*/_interopDefault(pluralizeLib);
|
|
35
|
+
var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
|
|
36
|
+
var path__namespace = /*#__PURE__*/_interopNamespace(path);
|
|
37
|
+
|
|
12
38
|
var __defProp = Object.defineProperty;
|
|
13
39
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
14
40
|
var __esm = (fn, res) => function __init() {
|
|
@@ -20,9 +46,26 @@ var __export = (target, all) => {
|
|
|
20
46
|
};
|
|
21
47
|
|
|
22
48
|
// src/infrastructure/errors.ts
|
|
23
|
-
var handleErrorWithContext, createValidationError, createCloudEventParseError;
|
|
49
|
+
var ValidationError, CloudEventParseError, handleErrorWithContext, createValidationError, createCloudEventParseError;
|
|
24
50
|
var init_errors = __esm({
|
|
25
51
|
"src/infrastructure/errors.ts"() {
|
|
52
|
+
ValidationError = class extends Error {
|
|
53
|
+
constructor(eventType, details) {
|
|
54
|
+
super(`Validation failed for event type: ${eventType}`);
|
|
55
|
+
this.eventType = eventType;
|
|
56
|
+
this.details = details;
|
|
57
|
+
this.name = "ValidationError";
|
|
58
|
+
}
|
|
59
|
+
type = "ValidationError";
|
|
60
|
+
};
|
|
61
|
+
CloudEventParseError = class extends Error {
|
|
62
|
+
constructor(message, cause) {
|
|
63
|
+
super(message);
|
|
64
|
+
this.cause = cause;
|
|
65
|
+
this.name = "CloudEventParseError";
|
|
66
|
+
}
|
|
67
|
+
type = "CloudEventParseError";
|
|
68
|
+
};
|
|
26
69
|
handleErrorWithContext = (error, context, additionalInfo) => {
|
|
27
70
|
const errorInfo = {
|
|
28
71
|
message: error.message,
|
|
@@ -35,17 +78,8 @@ var init_errors = __esm({
|
|
|
35
78
|
console.error("[Error Handler]", JSON.stringify(errorInfo, null, 2));
|
|
36
79
|
return errorInfo;
|
|
37
80
|
};
|
|
38
|
-
createValidationError = (eventType, details) => (
|
|
39
|
-
|
|
40
|
-
eventType,
|
|
41
|
-
message: `Validation failed for event type: ${eventType}`,
|
|
42
|
-
details
|
|
43
|
-
});
|
|
44
|
-
createCloudEventParseError = (message, cause) => ({
|
|
45
|
-
type: "CloudEventParseError",
|
|
46
|
-
message,
|
|
47
|
-
cause
|
|
48
|
-
});
|
|
81
|
+
createValidationError = (eventType, details) => new ValidationError(eventType, details);
|
|
82
|
+
createCloudEventParseError = (message, cause) => new CloudEventParseError(message, cause);
|
|
49
83
|
}
|
|
50
84
|
});
|
|
51
85
|
|
|
@@ -219,46 +253,842 @@ var init_raw_event = __esm({
|
|
|
219
253
|
};
|
|
220
254
|
}
|
|
221
255
|
});
|
|
256
|
+
exports.pluralize = void 0; exports.singularize = void 0;
|
|
257
|
+
var init_pluralize = __esm({
|
|
258
|
+
"src/utils/pluralize.ts"() {
|
|
259
|
+
exports.pluralize = (word) => pluralizeLib__default.default.plural(word);
|
|
260
|
+
exports.singularize = (word) => pluralizeLib__default.default.singular(word);
|
|
261
|
+
}
|
|
262
|
+
});
|
|
222
263
|
|
|
223
|
-
// src/
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
264
|
+
// src/utils/index.ts
|
|
265
|
+
var init_utils = __esm({
|
|
266
|
+
"src/utils/index.ts"() {
|
|
267
|
+
init_pluralize();
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// src/domain/naming.ts
|
|
272
|
+
exports.toKebabCase = void 0; exports.toPascalCase = void 0; exports.validateEventType = void 0; exports.isValidEventType = void 0; exports.deriveEventNames = void 0; exports.getContractPaths = void 0; exports.getHandlerPath = void 0; exports.getStreamName = void 0; exports.parseEventTypeFromHandler = void 0; exports.parseEventTypeFromContract = void 0; exports.normalizeSubject = void 0;
|
|
273
|
+
var init_naming = __esm({
|
|
274
|
+
"src/domain/naming.ts"() {
|
|
275
|
+
init_utils();
|
|
276
|
+
exports.toKebabCase = (str) => str.replace(/\./g, "-").toLowerCase();
|
|
277
|
+
exports.toPascalCase = (str) => str.split(/[-.]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join("");
|
|
278
|
+
exports.validateEventType = (eventType) => {
|
|
279
|
+
const errors = [];
|
|
280
|
+
if (!eventType) {
|
|
281
|
+
errors.push("Event type is required");
|
|
282
|
+
return { valid: false, errors };
|
|
283
|
+
}
|
|
284
|
+
if (!eventType.includes(".")) {
|
|
285
|
+
errors.push("Event type must include a namespace separator (e.g., order.created)");
|
|
286
|
+
}
|
|
287
|
+
if (eventType !== eventType.toLowerCase()) {
|
|
288
|
+
errors.push("Event type must be lowercase");
|
|
289
|
+
}
|
|
290
|
+
if (eventType.startsWith(".") || eventType.endsWith(".")) {
|
|
291
|
+
errors.push("Event type must not start or end with a dot");
|
|
292
|
+
}
|
|
293
|
+
if (eventType.includes("..")) {
|
|
294
|
+
errors.push("Event type must not contain consecutive dots");
|
|
295
|
+
}
|
|
296
|
+
const validChars = /^[a-z0-9.-]+$/;
|
|
297
|
+
if (!validChars.test(eventType)) {
|
|
298
|
+
errors.push("Event type must only contain lowercase letters, numbers, dots, and dashes");
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
valid: errors.length === 0,
|
|
302
|
+
errors
|
|
303
|
+
};
|
|
304
|
+
};
|
|
305
|
+
exports.isValidEventType = (eventType) => exports.validateEventType(eventType).valid;
|
|
306
|
+
exports.deriveEventNames = (eventType) => {
|
|
307
|
+
const parts = eventType.split(".");
|
|
308
|
+
const namespace = parts[0];
|
|
309
|
+
const action = parts.slice(1).join("-");
|
|
310
|
+
const kebab = exports.toKebabCase(eventType);
|
|
311
|
+
const pascal = exports.toPascalCase(eventType);
|
|
312
|
+
const pluralNamespace = exports.pluralize(namespace);
|
|
313
|
+
const pluralPascal = exports.toPascalCase(`${pluralNamespace}.${action}`);
|
|
314
|
+
return {
|
|
315
|
+
eventType,
|
|
316
|
+
kebab,
|
|
317
|
+
pascal,
|
|
318
|
+
schemaName: `${pascal}Schema`,
|
|
319
|
+
typeName: `${pascal}Data`,
|
|
320
|
+
contractName: `${pluralPascal}Contract`,
|
|
321
|
+
handlerFile: `${kebab}.handler.ts`,
|
|
322
|
+
streamName: pluralNamespace.toUpperCase(),
|
|
323
|
+
domain: pluralNamespace.toLowerCase(),
|
|
324
|
+
action,
|
|
325
|
+
namespace
|
|
326
|
+
};
|
|
327
|
+
};
|
|
328
|
+
exports.getContractPaths = (eventType) => {
|
|
329
|
+
const { domain, action } = exports.deriveEventNames(eventType);
|
|
330
|
+
return {
|
|
331
|
+
relativePath: `events/${domain}/${action}.ts`,
|
|
332
|
+
folder: domain,
|
|
333
|
+
filename: action
|
|
334
|
+
};
|
|
335
|
+
};
|
|
336
|
+
exports.getHandlerPath = (eventType) => {
|
|
337
|
+
const { handlerFile } = exports.deriveEventNames(eventType);
|
|
338
|
+
return `events/${handlerFile}`;
|
|
339
|
+
};
|
|
340
|
+
exports.getStreamName = (eventType) => exports.deriveEventNames(eventType).streamName;
|
|
341
|
+
exports.parseEventTypeFromHandler = (filename) => {
|
|
342
|
+
const match = filename.match(/^(.+)\.handler\.ts$/);
|
|
343
|
+
if (!match) return null;
|
|
344
|
+
const kebab = match[1];
|
|
345
|
+
const parts = kebab.split("-");
|
|
346
|
+
if (parts.length < 2) return null;
|
|
347
|
+
const action = parts.at(-1);
|
|
348
|
+
if (!action) return null;
|
|
349
|
+
const namespace = parts.slice(0, -1).join("-");
|
|
350
|
+
return `${namespace}.${action}`;
|
|
351
|
+
};
|
|
352
|
+
exports.parseEventTypeFromContract = (path2) => {
|
|
353
|
+
const cleanPath = path2.replace(/^events\//, "");
|
|
354
|
+
const match = cleanPath.match(/^([^/]+)\/(.+)\.ts$/);
|
|
355
|
+
if (!match) return null;
|
|
356
|
+
const [, folder, filename] = match;
|
|
357
|
+
const namespace = exports.singularize(folder);
|
|
358
|
+
return `${namespace}.${filename}`;
|
|
359
|
+
};
|
|
360
|
+
exports.normalizeSubject = (eventType) => {
|
|
361
|
+
const { domain, action } = exports.deriveEventNames(eventType);
|
|
362
|
+
return `${domain}.${action}`;
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
// src/effects/types.ts
|
|
368
|
+
exports.streamWired = void 0; exports.contractCreated = void 0; exports.handlerCreated = void 0;
|
|
369
|
+
var init_types = __esm({
|
|
370
|
+
"src/effects/types.ts"() {
|
|
371
|
+
exports.streamWired = (stream, servicePath) => ({
|
|
372
|
+
kind: "stream.wired",
|
|
373
|
+
stream,
|
|
374
|
+
servicePath
|
|
375
|
+
});
|
|
376
|
+
exports.contractCreated = (path2, eventType) => ({
|
|
377
|
+
kind: "contract.created",
|
|
378
|
+
path: path2,
|
|
379
|
+
eventType
|
|
380
|
+
});
|
|
381
|
+
exports.handlerCreated = (path2, eventType, servicePath) => ({
|
|
382
|
+
kind: "handler.created",
|
|
383
|
+
path: path2,
|
|
384
|
+
eventType,
|
|
385
|
+
servicePath
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
// src/effects/index.ts
|
|
391
|
+
var init_effects = __esm({
|
|
392
|
+
"src/effects/index.ts"() {
|
|
393
|
+
init_types();
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
var createNodeFileSystem; exports.createMemoryFileSystem = void 0;
|
|
397
|
+
var init_types2 = __esm({
|
|
398
|
+
"src/generators/types.ts"() {
|
|
399
|
+
createNodeFileSystem = () => {
|
|
400
|
+
return {
|
|
401
|
+
readFile: (filePath) => {
|
|
402
|
+
try {
|
|
403
|
+
return fs__namespace.readFileSync(filePath, "utf-8");
|
|
404
|
+
} catch {
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
writeFile: (filePath, content) => {
|
|
409
|
+
const dir = path__namespace.dirname(filePath);
|
|
410
|
+
if (!fs__namespace.existsSync(dir)) {
|
|
411
|
+
fs__namespace.mkdirSync(dir, { recursive: true });
|
|
412
|
+
}
|
|
413
|
+
fs__namespace.writeFileSync(filePath, content, "utf-8");
|
|
414
|
+
},
|
|
415
|
+
exists: (filePath) => fs__namespace.existsSync(filePath),
|
|
416
|
+
mkdir: (dirPath) => fs__namespace.mkdirSync(dirPath, { recursive: true })
|
|
417
|
+
};
|
|
418
|
+
};
|
|
419
|
+
exports.createMemoryFileSystem = (initialFiles = {}) => {
|
|
420
|
+
const files = { ...initialFiles };
|
|
421
|
+
return {
|
|
422
|
+
files,
|
|
423
|
+
readFile: (path2) => files[path2] ?? null,
|
|
424
|
+
writeFile: (path2, content) => {
|
|
425
|
+
files[path2] = content;
|
|
426
|
+
},
|
|
427
|
+
exists: (path2) => path2 in files,
|
|
428
|
+
mkdir: () => {
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
var fieldTypeToZod, generateSchemaFields; exports.generateContractContent = void 0; exports.getContractFilePath = void 0; exports.generateContract = void 0;
|
|
435
|
+
var init_contract = __esm({
|
|
436
|
+
"src/generators/contract.ts"() {
|
|
437
|
+
init_naming();
|
|
438
|
+
init_types2();
|
|
439
|
+
fieldTypeToZod = (type, optional) => {
|
|
440
|
+
const baseTypes = {
|
|
441
|
+
string: "z.string()",
|
|
442
|
+
number: "z.number()",
|
|
443
|
+
boolean: "z.boolean()",
|
|
444
|
+
date: "z.string().datetime()",
|
|
445
|
+
datetime: "z.string().datetime()",
|
|
446
|
+
array: "z.array(z.unknown())",
|
|
447
|
+
object: "z.record(z.unknown())"
|
|
448
|
+
};
|
|
449
|
+
const base = baseTypes[type];
|
|
450
|
+
return optional ? `${base}.optional()` : base;
|
|
451
|
+
};
|
|
452
|
+
generateSchemaFields = (fields) => {
|
|
453
|
+
if (!fields || fields.length === 0) {
|
|
454
|
+
return ` id: z.string(),
|
|
455
|
+
createdAt: z.string(),`;
|
|
456
|
+
}
|
|
457
|
+
return fields.map((field) => ` ${field.name}: ${fieldTypeToZod(field.type, field.optional)},`).join("\n");
|
|
458
|
+
};
|
|
459
|
+
exports.generateContractContent = (options) => {
|
|
460
|
+
const { eventType, fields } = options;
|
|
461
|
+
const names = exports.deriveEventNames(eventType);
|
|
462
|
+
const schemaFields = generateSchemaFields(fields);
|
|
463
|
+
const schemaBody = schemaFields ? `{
|
|
464
|
+
${schemaFields}
|
|
465
|
+
}` : "{}";
|
|
466
|
+
return `import { createContract } from '@crossdelta/cloudevents'
|
|
467
|
+
import { z } from 'zod'
|
|
468
|
+
|
|
469
|
+
export const ${names.schemaName} = z.object(${schemaBody})
|
|
470
|
+
|
|
471
|
+
export const ${names.contractName} = createContract({
|
|
472
|
+
type: '${eventType}',
|
|
473
|
+
channel: { stream: '${names.streamName}' },
|
|
474
|
+
schema: ${names.schemaName},
|
|
475
|
+
})
|
|
476
|
+
|
|
477
|
+
export type ${names.typeName} = z.infer<typeof ${names.contractName}.schema>
|
|
478
|
+
`;
|
|
479
|
+
};
|
|
480
|
+
exports.getContractFilePath = (eventType, basePath) => {
|
|
481
|
+
const paths = exports.getContractPaths(eventType);
|
|
482
|
+
return `${basePath}/${paths.relativePath}`;
|
|
483
|
+
};
|
|
484
|
+
exports.generateContract = (options, fs2 = createNodeFileSystem()) => {
|
|
485
|
+
const filePath = exports.getContractFilePath(options.eventType, options.basePath);
|
|
486
|
+
const content = exports.generateContractContent(options);
|
|
487
|
+
const exists = fs2.exists(filePath);
|
|
488
|
+
if (!exists) {
|
|
489
|
+
fs2.writeFile(filePath, content);
|
|
490
|
+
return flowcore.change.created(filePath);
|
|
491
|
+
}
|
|
492
|
+
if (options.force) {
|
|
493
|
+
fs2.writeFile(filePath, content);
|
|
494
|
+
return flowcore.change.updated(filePath);
|
|
495
|
+
}
|
|
496
|
+
return flowcore.change.skipped("File already exists");
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
// src/generators/exports.ts
|
|
502
|
+
var hasExport, addExportToIndex, ensureDomainExport;
|
|
503
|
+
var init_exports = __esm({
|
|
504
|
+
"src/generators/exports.ts"() {
|
|
505
|
+
init_types2();
|
|
506
|
+
hasExport = (content, exportPath) => {
|
|
507
|
+
const patterns = [
|
|
508
|
+
new RegExp(`^\\s*export \\* from ['"]${exportPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}['"]`, "m"),
|
|
509
|
+
new RegExp(`^\\s*export \\* from ['"]${exportPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\.ts['"]`, "m")
|
|
510
|
+
];
|
|
511
|
+
return patterns.some((p) => p.test(content));
|
|
512
|
+
};
|
|
513
|
+
addExportToIndex = (options, fs2 = createNodeFileSystem()) => {
|
|
514
|
+
const { indexPath, exportPath, force } = options;
|
|
515
|
+
if (!fs2.exists(indexPath)) {
|
|
516
|
+
fs2.writeFile(indexPath, `export * from '${exportPath}'
|
|
517
|
+
`);
|
|
518
|
+
return { added: true, path: indexPath };
|
|
519
|
+
}
|
|
520
|
+
const content = fs2.readFile(indexPath);
|
|
521
|
+
if (!content) {
|
|
522
|
+
fs2.writeFile(indexPath, `export * from '${exportPath}'
|
|
523
|
+
`);
|
|
524
|
+
return { added: true, path: indexPath };
|
|
525
|
+
}
|
|
526
|
+
if (!force && hasExport(content, exportPath)) {
|
|
527
|
+
return { added: false, path: indexPath, alreadyExists: true };
|
|
528
|
+
}
|
|
529
|
+
const commentedExport = new RegExp(`//\\s*export \\* from ['"]${exportPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}['"]`);
|
|
530
|
+
let newContent;
|
|
531
|
+
if (commentedExport.test(content)) {
|
|
532
|
+
newContent = content.replace(commentedExport, `export * from '${exportPath}'`);
|
|
533
|
+
} else {
|
|
534
|
+
const trimmed = content.trimEnd();
|
|
535
|
+
newContent = trimmed.length > 0 ? `${trimmed}
|
|
536
|
+
export * from '${exportPath}'
|
|
537
|
+
` : `export * from '${exportPath}'
|
|
538
|
+
`;
|
|
539
|
+
}
|
|
540
|
+
fs2.writeFile(indexPath, newContent);
|
|
541
|
+
return { added: true, path: indexPath };
|
|
542
|
+
};
|
|
543
|
+
ensureDomainExport = (options, fs2 = createNodeFileSystem()) => {
|
|
544
|
+
const results = [];
|
|
545
|
+
const { contractsBasePath, domain, eventFile } = options;
|
|
546
|
+
const eventsIndexPath = `${contractsBasePath}/events/index.ts`;
|
|
547
|
+
const domainExportResult = addExportToIndex(
|
|
548
|
+
{
|
|
549
|
+
indexPath: eventsIndexPath,
|
|
550
|
+
exportPath: `./${domain}`
|
|
551
|
+
},
|
|
552
|
+
fs2
|
|
553
|
+
);
|
|
554
|
+
results.push(domainExportResult);
|
|
555
|
+
const domainIndexPath = `${contractsBasePath}/events/${domain}/index.ts`;
|
|
556
|
+
const eventExportResult = addExportToIndex(
|
|
557
|
+
{
|
|
558
|
+
indexPath: domainIndexPath,
|
|
559
|
+
exportPath: `./${eventFile}`
|
|
560
|
+
},
|
|
561
|
+
fs2
|
|
562
|
+
);
|
|
563
|
+
results.push(eventExportResult);
|
|
564
|
+
const mainIndexPath = `${contractsBasePath}/index.ts`;
|
|
565
|
+
const mainExportResult = addExportToIndex(
|
|
566
|
+
{
|
|
567
|
+
indexPath: mainIndexPath,
|
|
568
|
+
exportPath: "./events"
|
|
569
|
+
},
|
|
570
|
+
fs2
|
|
571
|
+
);
|
|
572
|
+
results.push(mainExportResult);
|
|
573
|
+
return results;
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
exports.generateEventHandlerContent = void 0; exports.getHandlerFilePath = void 0; exports.generateEventHandler = void 0;
|
|
578
|
+
var init_handler = __esm({
|
|
579
|
+
"src/generators/handler.ts"() {
|
|
580
|
+
init_naming();
|
|
581
|
+
init_types2();
|
|
582
|
+
exports.generateEventHandlerContent = (options) => {
|
|
583
|
+
const { eventType, contractsPackage = "@crossdelta/contracts" } = options;
|
|
584
|
+
const names = exports.deriveEventNames(eventType);
|
|
585
|
+
return `import { handleEvent } from '@crossdelta/cloudevents'
|
|
586
|
+
import { ${names.contractName}, type ${names.typeName} } from '${contractsPackage}'
|
|
587
|
+
|
|
588
|
+
export default handleEvent(${names.contractName}, async (data: ${names.typeName}) => {
|
|
589
|
+
// TODO: Implement event handling logic
|
|
590
|
+
console.log('Received ${eventType}:', data)
|
|
591
|
+
})
|
|
592
|
+
`;
|
|
593
|
+
};
|
|
594
|
+
exports.getHandlerFilePath = (eventType, basePath) => {
|
|
595
|
+
const handlerPath = exports.getHandlerPath(eventType);
|
|
596
|
+
return `${basePath}/${handlerPath}`;
|
|
597
|
+
};
|
|
598
|
+
exports.generateEventHandler = (options, fs2 = createNodeFileSystem()) => {
|
|
599
|
+
const filePath = exports.getHandlerFilePath(options.eventType, options.basePath);
|
|
600
|
+
const content = exports.generateEventHandlerContent(options);
|
|
601
|
+
const exists = fs2.exists(filePath);
|
|
602
|
+
if (!exists) {
|
|
603
|
+
fs2.writeFile(filePath, content);
|
|
604
|
+
return flowcore.change.created(filePath);
|
|
605
|
+
}
|
|
606
|
+
if (options.force) {
|
|
607
|
+
fs2.writeFile(filePath, content);
|
|
608
|
+
return flowcore.change.updated(filePath);
|
|
609
|
+
}
|
|
610
|
+
return flowcore.change.skipped("File already exists");
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
var fakerInstance, fakerLoadAttempted, tryLoadFaker, getFaker; exports.initFaker = void 0; var createFieldGenerators, generateByFieldName, generateByFormat, generateByZodType, generateMockValue, generateMockFields; exports.generateMockContent = void 0; exports.getMockFilePath = void 0; exports.generateMock = void 0; var generateDefaultMockFields, processMockField, generateJsonMockData, parseSchemaFromSource; exports.getJsonMockPath = void 0; exports.jsonMockExists = void 0; exports.loadJsonMock = void 0; exports.generateJsonMock = void 0; exports.generateJsonMockFromContract = void 0;
|
|
615
|
+
var init_mock = __esm({
|
|
616
|
+
"src/generators/mock.ts"() {
|
|
617
|
+
init_naming();
|
|
618
|
+
init_types2();
|
|
619
|
+
fakerInstance = null;
|
|
620
|
+
fakerLoadAttempted = false;
|
|
621
|
+
tryLoadFaker = async () => {
|
|
622
|
+
if (fakerLoadAttempted) return fakerInstance;
|
|
623
|
+
fakerLoadAttempted = true;
|
|
233
624
|
try {
|
|
234
|
-
|
|
625
|
+
const { faker } = await import('@faker-js/faker');
|
|
626
|
+
fakerInstance = faker;
|
|
627
|
+
return faker;
|
|
235
628
|
} catch {
|
|
236
|
-
|
|
629
|
+
return null;
|
|
237
630
|
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const
|
|
242
|
-
return
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
631
|
+
};
|
|
632
|
+
getFaker = () => fakerInstance;
|
|
633
|
+
exports.initFaker = async () => {
|
|
634
|
+
const faker = await tryLoadFaker();
|
|
635
|
+
return faker !== null;
|
|
636
|
+
};
|
|
637
|
+
createFieldGenerators = () => [
|
|
638
|
+
{ pattern: "email", generate: () => "user@example.com", fakerGenerate: () => getFaker()?.internet.email(), hint: "email" },
|
|
639
|
+
{ pattern: "id", generate: () => crypto.randomUUID(), fakerGenerate: () => getFaker()?.string.uuid(), hint: "uuid" },
|
|
640
|
+
{ pattern: "firstname", generate: () => "John", fakerGenerate: () => getFaker()?.person.firstName(), hint: "firstName" },
|
|
641
|
+
{ pattern: "lastname", generate: () => "Doe", fakerGenerate: () => getFaker()?.person.lastName(), hint: "lastName" },
|
|
642
|
+
{ pattern: "name", generate: () => "John Doe", fakerGenerate: () => getFaker()?.person.fullName(), hint: "fullName" },
|
|
643
|
+
{ pattern: "phone", generate: () => "+1-555-123-4567", fakerGenerate: () => getFaker()?.phone.number(), hint: "phoneNumber" },
|
|
644
|
+
{ pattern: "address", generate: () => "123 Main St", fakerGenerate: () => getFaker()?.location.streetAddress(), hint: "streetAddress" },
|
|
645
|
+
{ pattern: "street", generate: () => "123 Main St", fakerGenerate: () => getFaker()?.location.streetAddress(), hint: "streetAddress" },
|
|
646
|
+
{ pattern: "city", generate: () => "New York", fakerGenerate: () => getFaker()?.location.city(), hint: "city" },
|
|
647
|
+
{ pattern: "country", generate: () => "USA", fakerGenerate: () => getFaker()?.location.country(), hint: "country" },
|
|
648
|
+
{ pattern: "price", generate: () => 99.99, fakerGenerate: () => getFaker()?.number.float({ min: 10, max: 1e3, fractionDigits: 2 }), hint: "price" },
|
|
649
|
+
{ pattern: "amount", generate: () => 99.99, fakerGenerate: () => getFaker()?.number.float({ min: 10, max: 1e3, fractionDigits: 2 }), hint: "price" },
|
|
650
|
+
{ pattern: "total", generate: () => 99.99, fakerGenerate: () => getFaker()?.number.float({ min: 10, max: 1e3, fractionDigits: 2 }), hint: "price" },
|
|
651
|
+
{ pattern: "quantity", generate: () => 1, fakerGenerate: () => getFaker()?.number.int({ min: 1, max: 10 }), hint: "quantity" },
|
|
652
|
+
{ pattern: "date", generate: () => (/* @__PURE__ */ new Date()).toISOString(), fakerGenerate: () => getFaker()?.date.recent().toISOString(), hint: "date" },
|
|
653
|
+
{ pattern: "createdat", generate: () => (/* @__PURE__ */ new Date()).toISOString(), fakerGenerate: () => getFaker()?.date.recent().toISOString(), hint: "date" },
|
|
654
|
+
{ pattern: "updatedat", generate: () => (/* @__PURE__ */ new Date()).toISOString(), fakerGenerate: () => getFaker()?.date.recent().toISOString(), hint: "date" }
|
|
655
|
+
];
|
|
656
|
+
generateByFieldName = (fieldName, useFaker = false) => {
|
|
657
|
+
const generators = createFieldGenerators();
|
|
658
|
+
const lowerField = fieldName.toLowerCase();
|
|
659
|
+
const matched = generators.find((g) => lowerField.includes(g.pattern));
|
|
660
|
+
if (!matched) return null;
|
|
661
|
+
const value = (useFaker ? matched.fakerGenerate?.() : void 0) ?? matched.generate();
|
|
662
|
+
return { value, hint: matched.hint };
|
|
663
|
+
};
|
|
664
|
+
generateByFormat = (format, useFaker = false) => {
|
|
665
|
+
const faker = getFaker();
|
|
666
|
+
const formatGenerators = {
|
|
667
|
+
email: {
|
|
668
|
+
static: () => "user@example.com",
|
|
669
|
+
faker: faker ? () => faker.internet.email() : void 0
|
|
670
|
+
},
|
|
671
|
+
datetime: {
|
|
672
|
+
static: () => (/* @__PURE__ */ new Date()).toISOString(),
|
|
673
|
+
faker: faker ? () => faker.date.recent().toISOString() : void 0
|
|
674
|
+
},
|
|
675
|
+
url: {
|
|
676
|
+
static: () => "https://example.com",
|
|
677
|
+
faker: faker ? () => faker.internet.url() : void 0
|
|
678
|
+
},
|
|
679
|
+
uuid: {
|
|
680
|
+
static: () => crypto.randomUUID(),
|
|
681
|
+
faker: faker ? () => faker.string.uuid() : void 0
|
|
682
|
+
},
|
|
683
|
+
date: {
|
|
684
|
+
static: () => (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
685
|
+
faker: faker ? () => faker.date.recent().toISOString().split("T")[0] : void 0
|
|
686
|
+
},
|
|
687
|
+
time: {
|
|
688
|
+
static: () => (/* @__PURE__ */ new Date()).toISOString().split("T")[1]?.split(".")[0],
|
|
689
|
+
faker: faker ? () => faker.date.recent().toISOString().split("T")[1]?.split(".")[0] : void 0
|
|
690
|
+
}
|
|
691
|
+
};
|
|
692
|
+
const gen = formatGenerators[format];
|
|
693
|
+
if (!gen) return "example";
|
|
694
|
+
return useFaker && gen.faker ? gen.faker() : gen.static();
|
|
695
|
+
};
|
|
696
|
+
generateByZodType = (zodType, useFaker = false) => {
|
|
697
|
+
const faker = getFaker();
|
|
698
|
+
const typeGenerators = {
|
|
699
|
+
string: {
|
|
700
|
+
static: () => "example",
|
|
701
|
+
faker: faker ? () => faker.lorem.word() : void 0
|
|
702
|
+
},
|
|
703
|
+
number: {
|
|
704
|
+
static: () => 42,
|
|
705
|
+
faker: faker ? () => faker.number.int({ min: 1, max: 100 }) : void 0
|
|
706
|
+
},
|
|
707
|
+
boolean: {
|
|
708
|
+
static: () => true,
|
|
709
|
+
faker: faker ? () => faker.datatype.boolean() : void 0
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
const gen = typeGenerators[zodType];
|
|
713
|
+
if (!gen) return void 0;
|
|
714
|
+
return useFaker && gen.faker ? gen.faker() : gen.static();
|
|
715
|
+
};
|
|
716
|
+
generateMockValue = (type) => {
|
|
717
|
+
const mockValues = {
|
|
718
|
+
string: "'mock-string'",
|
|
719
|
+
number: "42",
|
|
720
|
+
boolean: "true",
|
|
721
|
+
date: "new Date().toISOString()",
|
|
722
|
+
datetime: "new Date().toISOString()",
|
|
723
|
+
array: "[]",
|
|
724
|
+
object: "{}"
|
|
725
|
+
};
|
|
726
|
+
return mockValues[type];
|
|
727
|
+
};
|
|
728
|
+
generateMockFields = (fields) => {
|
|
729
|
+
if (!fields || fields.length === 0) {
|
|
730
|
+
return ` id: 'mock-id',
|
|
731
|
+
createdAt: new Date().toISOString(),`;
|
|
732
|
+
}
|
|
733
|
+
return fields.filter((field) => !field.optional).map((field) => ` ${field.name}: ${generateMockValue(field.type)},`).join("\n");
|
|
734
|
+
};
|
|
735
|
+
exports.generateMockContent = (options) => {
|
|
736
|
+
const { eventType, fields } = options;
|
|
737
|
+
const names = exports.deriveEventNames(eventType);
|
|
738
|
+
return `import type { ${names.typeName} } from '../${names.domain}/${names.action}'
|
|
739
|
+
|
|
740
|
+
export const mock${names.pascal}: ${names.typeName} = {
|
|
741
|
+
${generateMockFields(fields)}
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
export const create${names.pascal}Mock = (overrides: Partial<${names.typeName}> = {}): ${names.typeName} => ({
|
|
745
|
+
...mock${names.pascal},
|
|
746
|
+
...overrides,
|
|
747
|
+
})
|
|
748
|
+
`;
|
|
749
|
+
};
|
|
750
|
+
exports.getMockFilePath = (eventType, basePath) => {
|
|
751
|
+
const names = exports.deriveEventNames(eventType);
|
|
752
|
+
return `${basePath}/${names.kebab}.mock.ts`;
|
|
753
|
+
};
|
|
754
|
+
exports.generateMock = (options, fs2 = createNodeFileSystem()) => {
|
|
755
|
+
const filePath = exports.getMockFilePath(options.eventType, options.basePath);
|
|
756
|
+
const content = exports.generateMockContent(options);
|
|
757
|
+
const exists = fs2.exists(filePath);
|
|
758
|
+
if (!exists) {
|
|
759
|
+
fs2.writeFile(filePath, content);
|
|
760
|
+
return flowcore.change.created(filePath);
|
|
761
|
+
}
|
|
762
|
+
if (options.force) {
|
|
763
|
+
fs2.writeFile(filePath, content);
|
|
764
|
+
return flowcore.change.updated(filePath);
|
|
765
|
+
}
|
|
766
|
+
return flowcore.change.skipped("File already exists");
|
|
767
|
+
};
|
|
768
|
+
generateDefaultMockFields = (useFaker) => ({
|
|
769
|
+
data: {
|
|
770
|
+
id: (useFaker ? getFaker()?.string.uuid() : void 0) ?? crypto.randomUUID(),
|
|
771
|
+
createdAt: (useFaker ? getFaker()?.date.recent().toISOString() : void 0) ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
772
|
+
},
|
|
773
|
+
faker: { id: "uuid", createdAt: "date" }
|
|
774
|
+
});
|
|
775
|
+
processMockField = (field, useFaker, data, fakerHints) => {
|
|
776
|
+
const byName = generateByFieldName(field.name, useFaker);
|
|
777
|
+
if (byName) {
|
|
778
|
+
data[field.name] = byName.value;
|
|
779
|
+
if (byName.hint) fakerHints[field.name] = byName.hint;
|
|
780
|
+
} else {
|
|
781
|
+
data[field.name] = generateByZodType(field.type, useFaker) ?? "example";
|
|
782
|
+
}
|
|
783
|
+
};
|
|
784
|
+
generateJsonMockData = (options = {}) => {
|
|
785
|
+
const { fields, useFaker = false } = options;
|
|
786
|
+
if (!fields || fields.length === 0) {
|
|
787
|
+
return generateDefaultMockFields(useFaker);
|
|
788
|
+
}
|
|
789
|
+
const data = {};
|
|
790
|
+
const fakerHints = {};
|
|
791
|
+
for (const field of fields) {
|
|
792
|
+
if (!field.optional) processMockField(field, useFaker, data, fakerHints);
|
|
793
|
+
}
|
|
794
|
+
return { data, faker: fakerHints };
|
|
795
|
+
};
|
|
796
|
+
parseSchemaFromSource = (schemaBody, useFaker = false) => {
|
|
797
|
+
const data = {};
|
|
798
|
+
const fakerHints = {};
|
|
799
|
+
const fieldRegex = /(\w+):\s*z\.(\w+)\(/g;
|
|
800
|
+
const matches = Array.from(schemaBody.matchAll(fieldRegex));
|
|
801
|
+
for (const [, fieldName, zodType] of matches) {
|
|
802
|
+
if (zodType === "array" || zodType === "object") continue;
|
|
803
|
+
const byName = generateByFieldName(fieldName, useFaker);
|
|
804
|
+
if (byName) {
|
|
805
|
+
data[fieldName] = byName.value;
|
|
806
|
+
if (byName.hint) fakerHints[fieldName] = byName.hint;
|
|
807
|
+
} else {
|
|
808
|
+
data[fieldName] = generateByZodType(zodType, useFaker) ?? "example";
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
const chainedRegex = /(\w+):\s*z\.string\(\)\.(datetime|email|url|uuid)\(\)/g;
|
|
812
|
+
const chainedMatches = Array.from(schemaBody.matchAll(chainedRegex));
|
|
813
|
+
for (const [, fieldName, method] of chainedMatches) {
|
|
814
|
+
data[fieldName] = generateByFormat(method, useFaker);
|
|
815
|
+
fakerHints[fieldName] = method === "datetime" ? "date" : method;
|
|
816
|
+
}
|
|
817
|
+
return { data, faker: fakerHints };
|
|
818
|
+
};
|
|
819
|
+
exports.getJsonMockPath = (eventType, contractsPath) => {
|
|
820
|
+
const names = exports.deriveEventNames(eventType);
|
|
821
|
+
return `${contractsPath}/events/${names.domain}/${names.action}.mock.json`;
|
|
822
|
+
};
|
|
823
|
+
exports.jsonMockExists = (eventType, contractsPath, fs2 = createNodeFileSystem()) => fs2.exists(exports.getJsonMockPath(eventType, contractsPath));
|
|
824
|
+
exports.loadJsonMock = (eventType, contractsPath, fs2 = createNodeFileSystem()) => {
|
|
825
|
+
const mockPath = exports.getJsonMockPath(eventType, contractsPath);
|
|
826
|
+
const content = fs2.readFile(mockPath);
|
|
827
|
+
if (!content) return null;
|
|
828
|
+
try {
|
|
829
|
+
return JSON.parse(content);
|
|
830
|
+
} catch {
|
|
831
|
+
return null;
|
|
832
|
+
}
|
|
833
|
+
};
|
|
834
|
+
exports.generateJsonMock = (options, fs2 = createNodeFileSystem()) => {
|
|
835
|
+
const { eventType, contractsPath, fields, force, useFaker = false } = options;
|
|
836
|
+
const mockPath = exports.getJsonMockPath(eventType, contractsPath);
|
|
837
|
+
const exists = fs2.exists(mockPath);
|
|
838
|
+
const { data, faker: fakerHints } = generateJsonMockData({ fields, useFaker });
|
|
839
|
+
const mockData = {
|
|
840
|
+
eventName: eventType,
|
|
841
|
+
description: `Mock data for ${eventType} event`,
|
|
842
|
+
data
|
|
843
|
+
};
|
|
844
|
+
if (Object.keys(fakerHints).length > 0) {
|
|
845
|
+
mockData.faker = fakerHints;
|
|
846
|
+
}
|
|
847
|
+
const content = JSON.stringify(mockData, null, 2);
|
|
848
|
+
if (!exists) {
|
|
849
|
+
fs2.writeFile(mockPath, content);
|
|
850
|
+
return flowcore.change.created(mockPath);
|
|
851
|
+
}
|
|
852
|
+
if (force) {
|
|
853
|
+
fs2.writeFile(mockPath, content);
|
|
854
|
+
return flowcore.change.updated(mockPath);
|
|
855
|
+
}
|
|
856
|
+
return flowcore.change.skipped("File already exists");
|
|
857
|
+
};
|
|
858
|
+
exports.generateJsonMockFromContract = (eventType, contractsPath, options = {}, fs2 = createNodeFileSystem()) => {
|
|
859
|
+
const { useFaker = false } = options;
|
|
860
|
+
const names = exports.deriveEventNames(eventType);
|
|
861
|
+
const contractPath = `${contractsPath}/events/${names.domain}/${names.action}.ts`;
|
|
862
|
+
const contractContent = fs2.readFile(contractPath);
|
|
863
|
+
if (!contractContent) return null;
|
|
864
|
+
const schemaMatch = contractContent.match(/export const \w+Schema\s*=\s*z\.object\(\s*\{([\s\S]*?)\}\s*\)/m);
|
|
865
|
+
if (!schemaMatch) return null;
|
|
866
|
+
const { data, faker: fakerHints } = parseSchemaFromSource(schemaMatch[1], useFaker);
|
|
867
|
+
const mockPath = exports.getJsonMockPath(eventType, contractsPath);
|
|
868
|
+
const mockData = {
|
|
869
|
+
eventName: eventType,
|
|
870
|
+
description: `Mock data for ${eventType} event`,
|
|
871
|
+
data
|
|
872
|
+
};
|
|
873
|
+
if (Object.keys(fakerHints).length > 0) {
|
|
874
|
+
mockData.faker = fakerHints;
|
|
875
|
+
}
|
|
876
|
+
const content = JSON.stringify(mockData, null, 2);
|
|
877
|
+
fs2.writeFile(mockPath, content);
|
|
878
|
+
return flowcore.change.created(mockPath);
|
|
879
|
+
};
|
|
260
880
|
}
|
|
261
|
-
};
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
// src/generators/index.ts
|
|
884
|
+
var generators_exports = {};
|
|
885
|
+
__export(generators_exports, {
|
|
886
|
+
addExportToIndex: () => addExportToIndex,
|
|
887
|
+
createMemoryFileSystem: () => exports.createMemoryFileSystem,
|
|
888
|
+
createNodeFileSystem: () => createNodeFileSystem,
|
|
889
|
+
ensureDomainExport: () => ensureDomainExport,
|
|
890
|
+
generateContract: () => exports.generateContract,
|
|
891
|
+
generateContractContent: () => exports.generateContractContent,
|
|
892
|
+
generateEventHandler: () => exports.generateEventHandler,
|
|
893
|
+
generateEventHandlerContent: () => exports.generateEventHandlerContent,
|
|
894
|
+
generateJsonMock: () => exports.generateJsonMock,
|
|
895
|
+
generateJsonMockFromContract: () => exports.generateJsonMockFromContract,
|
|
896
|
+
generateMock: () => exports.generateMock,
|
|
897
|
+
generateMockContent: () => exports.generateMockContent,
|
|
898
|
+
getContractFilePath: () => exports.getContractFilePath,
|
|
899
|
+
getHandlerFilePath: () => exports.getHandlerFilePath,
|
|
900
|
+
getJsonMockPath: () => exports.getJsonMockPath,
|
|
901
|
+
getMockFilePath: () => exports.getMockFilePath,
|
|
902
|
+
initFaker: () => exports.initFaker,
|
|
903
|
+
jsonMockExists: () => exports.jsonMockExists,
|
|
904
|
+
loadJsonMock: () => exports.loadJsonMock
|
|
905
|
+
});
|
|
906
|
+
var init_generators = __esm({
|
|
907
|
+
"src/generators/index.ts"() {
|
|
908
|
+
init_contract();
|
|
909
|
+
init_exports();
|
|
910
|
+
init_handler();
|
|
911
|
+
init_mock();
|
|
912
|
+
init_types2();
|
|
913
|
+
}
|
|
914
|
+
});
|
|
915
|
+
|
|
916
|
+
// src/flows/create-event.flow.ts
|
|
917
|
+
var create_event_flow_exports = {};
|
|
918
|
+
__export(create_event_flow_exports, {
|
|
919
|
+
createEventFlowSteps: () => exports.createEventFlowSteps,
|
|
920
|
+
parseFieldsInput: () => exports.parseFieldsInput
|
|
921
|
+
});
|
|
922
|
+
var parseField; exports.parseFieldsInput = void 0; var generateContractTask, addExportsTask, generateMockTask, generateHandlerTask, flow, isInvalidEventType, getEventTypeErrorMessage, ensureInfraStep, ensureGenerationStep, eventTypeStep, validateEventTypeStep, schemaFieldsStep, serviceSelectionStep, generateFilesStep, printSummary, printSummaryStep; exports.createEventFlowSteps = void 0;
|
|
923
|
+
var init_create_event_flow = __esm({
|
|
924
|
+
"src/flows/create-event.flow.ts"() {
|
|
925
|
+
init_naming();
|
|
926
|
+
init_effects();
|
|
927
|
+
init_generators();
|
|
928
|
+
parseField = (field) => {
|
|
929
|
+
const [nameWithOptional, type = "string"] = field.split(":").map((s) => s.trim());
|
|
930
|
+
const optional = nameWithOptional.endsWith("?");
|
|
931
|
+
const name = optional ? nameWithOptional.slice(0, -1) : nameWithOptional;
|
|
932
|
+
return { name, type, optional };
|
|
933
|
+
};
|
|
934
|
+
exports.parseFieldsInput = (input2) => input2.trim() === "" ? [] : input2.split(",").map((f) => f.trim()).filter(Boolean).map(parseField);
|
|
935
|
+
generateContractTask = (ctx) => {
|
|
936
|
+
const generation = flowcore.initGenerationContext(ctx);
|
|
937
|
+
const result = exports.generateContract(
|
|
938
|
+
{
|
|
939
|
+
eventType: ctx.eventType,
|
|
940
|
+
basePath: ctx.config.contractsPath,
|
|
941
|
+
fields: ctx.fields ?? [],
|
|
942
|
+
force: ctx.force ?? false
|
|
943
|
+
},
|
|
944
|
+
ctx._fs
|
|
945
|
+
);
|
|
946
|
+
flowcore.trackChange(result).record(generation.artifacts, "Contract").onCreated((path2) => {
|
|
947
|
+
generation.addEffect(exports.contractCreated(path2, ctx.eventType));
|
|
948
|
+
});
|
|
949
|
+
};
|
|
950
|
+
addExportsTask = (ctx) => {
|
|
951
|
+
const names = exports.deriveEventNames(ctx.eventType);
|
|
952
|
+
ensureDomainExport(
|
|
953
|
+
{
|
|
954
|
+
contractsBasePath: ctx.config.contractsPath,
|
|
955
|
+
domain: names.domain,
|
|
956
|
+
eventFile: names.action
|
|
957
|
+
},
|
|
958
|
+
ctx._fs
|
|
959
|
+
);
|
|
960
|
+
};
|
|
961
|
+
generateMockTask = (ctx) => {
|
|
962
|
+
const generation = flowcore.initGenerationContext(ctx);
|
|
963
|
+
const result = exports.generateJsonMock(
|
|
964
|
+
{
|
|
965
|
+
eventType: ctx.eventType,
|
|
966
|
+
contractsPath: ctx.config.contractsPath,
|
|
967
|
+
fields: ctx.fields ?? [],
|
|
968
|
+
force: ctx.force ?? false
|
|
969
|
+
},
|
|
970
|
+
ctx._fs
|
|
971
|
+
);
|
|
972
|
+
flowcore.trackChange(result).record(generation.artifacts, "Mock");
|
|
973
|
+
};
|
|
974
|
+
generateHandlerTask = (ctx) => {
|
|
975
|
+
if (!ctx.servicePath) return;
|
|
976
|
+
const generation = flowcore.initGenerationContext(ctx);
|
|
977
|
+
const names = exports.deriveEventNames(ctx.eventType);
|
|
978
|
+
const handlerBasePath = ctx.servicePath.endsWith("/src") ? ctx.servicePath : `${ctx.servicePath}/src`;
|
|
979
|
+
const result = exports.generateEventHandler(
|
|
980
|
+
{
|
|
981
|
+
eventType: ctx.eventType,
|
|
982
|
+
basePath: handlerBasePath,
|
|
983
|
+
contractsPackage: ctx.config.contractsPackage ?? "",
|
|
984
|
+
force: ctx.force ?? false
|
|
985
|
+
},
|
|
986
|
+
ctx._fs
|
|
987
|
+
);
|
|
988
|
+
flowcore.trackChange(result).record(generation.artifacts, "Handler").onCreated((path2) => {
|
|
989
|
+
if (ctx.servicePath) {
|
|
990
|
+
generation.addEffect(exports.handlerCreated(path2, ctx.eventType, ctx.servicePath));
|
|
991
|
+
generation.addEffect(exports.streamWired(names.streamName, ctx.servicePath));
|
|
992
|
+
}
|
|
993
|
+
});
|
|
994
|
+
};
|
|
995
|
+
flow = flowcore.createFlow();
|
|
996
|
+
isInvalidEventType = (ctx) => !exports.isValidEventType(ctx.eventType);
|
|
997
|
+
getEventTypeErrorMessage = (ctx) => `Invalid event type: ${exports.validateEventType(ctx.eventType).errors.join(", ")}`;
|
|
998
|
+
ensureInfraStep = flow.ensure("_fs", (ctx) => ctx.config.fs ?? createNodeFileSystem()).build();
|
|
999
|
+
ensureGenerationStep = flow.ensure("_generation", () => flowcore.createGenerationResult()).build();
|
|
1000
|
+
eventTypeStep = flow.input("eventType").prompt({
|
|
1001
|
+
title: "Event Type",
|
|
1002
|
+
message: "Enter event type (e.g., order.created):"
|
|
1003
|
+
}).validateBy(exports.validateEventType).skipIfSet().build();
|
|
1004
|
+
validateEventTypeStep = flow.abortIf(isInvalidEventType, getEventTypeErrorMessage).build();
|
|
1005
|
+
schemaFieldsStep = flowcore.input("fieldsInput").prompt({
|
|
1006
|
+
title: "Schema Fields",
|
|
1007
|
+
message: "Schema fields (e.g., orderId:string,total:number) or leave empty:"
|
|
1008
|
+
}).mapTo("fields", exports.parseFieldsInput).skipWhen((ctx) => ctx.fields !== void 0).build();
|
|
1009
|
+
serviceSelectionStep = flow.select("servicePath").title("Service Selection").prompt("Select service to create handler in:").choicesFrom((ctx) => (ctx.availableServices ?? []).map((s) => ({ name: s, value: s }))).optional("Skip handler creation").skipIfSet().build();
|
|
1010
|
+
generateFilesStep = flow.task("Generate files").steps([generateContractTask, addExportsTask, generateMockTask, generateHandlerTask]).produces("fields", "_generation").build();
|
|
1011
|
+
printSummary = (ctx) => {
|
|
1012
|
+
flowcore.printGenerationSummary(flowcore.initGenerationContext(ctx));
|
|
1013
|
+
};
|
|
1014
|
+
printSummaryStep = flow.task("Print summary").steps([printSummary]).build();
|
|
1015
|
+
exports.createEventFlowSteps = [
|
|
1016
|
+
ensureInfraStep,
|
|
1017
|
+
ensureGenerationStep,
|
|
1018
|
+
eventTypeStep,
|
|
1019
|
+
validateEventTypeStep,
|
|
1020
|
+
schemaFieldsStep,
|
|
1021
|
+
serviceSelectionStep,
|
|
1022
|
+
generateFilesStep,
|
|
1023
|
+
printSummaryStep
|
|
1024
|
+
];
|
|
1025
|
+
}
|
|
1026
|
+
});
|
|
1027
|
+
|
|
1028
|
+
// src/flows/list-events.flow.ts
|
|
1029
|
+
var list_events_flow_exports = {};
|
|
1030
|
+
__export(list_events_flow_exports, {
|
|
1031
|
+
discoverEventTypes: () => exports.discoverEventTypes,
|
|
1032
|
+
listEventsFlowSteps: () => exports.listEventsFlowSteps
|
|
1033
|
+
});
|
|
1034
|
+
var isContractFile, makeRelativePath, safeReadDir, safeStat, processEntry, findContractFiles, parseEventType, matchesPattern; exports.discoverEventTypes = void 0; var flow2; exports.listEventsFlowSteps = void 0;
|
|
1035
|
+
var init_list_events_flow = __esm({
|
|
1036
|
+
"src/flows/list-events.flow.ts"() {
|
|
1037
|
+
init_naming();
|
|
1038
|
+
isContractFile = (filename) => filename.endsWith(".ts") && !filename.endsWith(".test.ts") && filename !== "index.ts";
|
|
1039
|
+
makeRelativePath = (contractsPath, fullPath) => fullPath.replace(`${contractsPath}/`, "").replace(/\\/g, "/");
|
|
1040
|
+
safeReadDir = (dir) => {
|
|
1041
|
+
try {
|
|
1042
|
+
return fs.readdirSync(dir);
|
|
1043
|
+
} catch {
|
|
1044
|
+
return [];
|
|
1045
|
+
}
|
|
1046
|
+
};
|
|
1047
|
+
safeStat = (path2) => {
|
|
1048
|
+
try {
|
|
1049
|
+
const stat = fs.statSync(path2);
|
|
1050
|
+
return { isDirectory: stat.isDirectory() };
|
|
1051
|
+
} catch {
|
|
1052
|
+
return null;
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1055
|
+
processEntry = (dir, entry, contractsPath) => {
|
|
1056
|
+
const fullPath = path.join(dir, entry);
|
|
1057
|
+
const stat = safeStat(fullPath);
|
|
1058
|
+
if (!stat) return [];
|
|
1059
|
+
if (stat.isDirectory) {
|
|
1060
|
+
return findContractFiles(fullPath, contractsPath);
|
|
1061
|
+
}
|
|
1062
|
+
if (isContractFile(entry)) {
|
|
1063
|
+
return [makeRelativePath(contractsPath, fullPath)];
|
|
1064
|
+
}
|
|
1065
|
+
return [];
|
|
1066
|
+
};
|
|
1067
|
+
findContractFiles = (dir, contractsPath) => safeReadDir(dir).flatMap((entry) => processEntry(dir, entry, contractsPath));
|
|
1068
|
+
parseEventType = (filePath) => exports.parseEventTypeFromContract(filePath);
|
|
1069
|
+
matchesPattern = (pattern) => (eventType) => !pattern || pattern.trim() === "" || eventType.includes(pattern);
|
|
1070
|
+
exports.discoverEventTypes = (contractsPath, pattern) => {
|
|
1071
|
+
const eventsDir = path.join(contractsPath, "events");
|
|
1072
|
+
if (!fs.existsSync(eventsDir)) {
|
|
1073
|
+
return [];
|
|
1074
|
+
}
|
|
1075
|
+
return findContractFiles(eventsDir, contractsPath).map(parseEventType).filter((eventType) => eventType !== null).filter(matchesPattern(pattern)).sort();
|
|
1076
|
+
};
|
|
1077
|
+
flow2 = flowcore.createFlow();
|
|
1078
|
+
exports.listEventsFlowSteps = [
|
|
1079
|
+
flow2.input("pattern").prompt({
|
|
1080
|
+
title: "Filter Pattern",
|
|
1081
|
+
message: "Filter by pattern (leave empty for all):"
|
|
1082
|
+
}).skipWhen((ctx) => ctx.pattern != null).build(),
|
|
1083
|
+
flow2.task("Discover events").steps([
|
|
1084
|
+
(ctx) => {
|
|
1085
|
+
const filterPattern = ctx.pattern && ctx.pattern.trim() !== "" ? ctx.pattern : void 0;
|
|
1086
|
+
ctx.eventTypes = exports.discoverEventTypes(ctx.config.contractsPath, filterPattern);
|
|
1087
|
+
}
|
|
1088
|
+
]).produces("eventTypes").build()
|
|
1089
|
+
];
|
|
1090
|
+
}
|
|
1091
|
+
});
|
|
262
1092
|
|
|
263
1093
|
// src/domain/contract-helper.ts
|
|
264
1094
|
function createContract(options) {
|
|
@@ -272,162 +1102,173 @@ function createContract(options) {
|
|
|
272
1102
|
channel: resolvedChannel
|
|
273
1103
|
};
|
|
274
1104
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
1105
|
+
var init_contract_helper = __esm({
|
|
1106
|
+
"src/domain/contract-helper.ts"() {
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
278
1109
|
|
|
279
1110
|
// src/domain/validation.ts
|
|
280
|
-
var hasShape = (schema) => "shape" in schema && schema.shape !== null && schema.shape !== void 0 && typeof schema.shape === "object";
|
|
281
|
-
var hasTypeField = (shape) => "type" in shape && shape.type !== null && shape.type !== void 0 && typeof shape.type === "object" && "value" in shape.type;
|
|
282
|
-
var extractTypeValue = (typeField) => typeof typeField.value === "string" ? typeField.value : void 0;
|
|
283
|
-
var safeExtractType = (schema) => {
|
|
284
|
-
if (!hasShape(schema)) return void 0;
|
|
285
|
-
if (!hasTypeField(schema.shape)) return void 0;
|
|
286
|
-
return extractTypeValue(schema.shape.type);
|
|
287
|
-
};
|
|
288
1111
|
function isValidHandler(value) {
|
|
289
1112
|
if (typeof value !== "function") return false;
|
|
290
1113
|
const handler = value;
|
|
291
1114
|
return !!(handler.__eventarcMetadata || handler.prototype?.handle);
|
|
292
1115
|
}
|
|
293
|
-
var extractTypeFromSchema =
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
try {
|
|
306
|
-
const currentDir = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
|
|
307
|
-
directories.add(currentDir);
|
|
308
|
-
if (currentDir.includes("/dist/")) {
|
|
309
|
-
const pkgRoot = currentDir.split("/dist/")[0];
|
|
310
|
-
if (pkgRoot) directories.add(pkgRoot);
|
|
311
|
-
}
|
|
312
|
-
} catch {
|
|
313
|
-
}
|
|
314
|
-
return [...directories].filter((dir) => fs.existsSync(dir));
|
|
315
|
-
};
|
|
316
|
-
var loadHandlers = async (filePath, filter) => {
|
|
317
|
-
try {
|
|
318
|
-
if (!fs.existsSync(filePath)) {
|
|
319
|
-
return [];
|
|
320
|
-
}
|
|
321
|
-
const module = await import(filePath);
|
|
322
|
-
return Object.entries(module).filter(([name, handler]) => isValidHandler(handler) && (!filter || filter(name, handler))).map(([name, handler]) => {
|
|
323
|
-
const HandlerClass = handler;
|
|
324
|
-
const hasNoMeaningfulName = !HandlerClass.name || HandlerClass.name === "" || HandlerClass.name === "HandlerClass";
|
|
325
|
-
const isNamedExport = name !== "default";
|
|
326
|
-
if (hasNoMeaningfulName && isNamedExport) {
|
|
327
|
-
return Object.defineProperty(HandlerClass, "name", { value: name, configurable: true });
|
|
328
|
-
}
|
|
329
|
-
return HandlerClass;
|
|
330
|
-
});
|
|
331
|
-
} catch (error) {
|
|
332
|
-
logger?.warn(`Failed to load ${filePath}`);
|
|
333
|
-
logger?.warn(`Error type: ${typeof error}`);
|
|
334
|
-
logger?.warn(`Error constructor: ${error?.constructor?.name}`);
|
|
335
|
-
if (error instanceof Error) {
|
|
336
|
-
logger?.warn(`Error message: ${error.message}`);
|
|
337
|
-
if (error.stack) {
|
|
338
|
-
logger?.warn(`Stack trace:
|
|
339
|
-
${error.stack}`);
|
|
340
|
-
}
|
|
341
|
-
} else {
|
|
342
|
-
logger?.warn(`Error value: ${String(error)}`);
|
|
1116
|
+
var hasShape, hasTypeField, extractTypeValue, safeExtractType; exports.extractTypeFromSchema = void 0;
|
|
1117
|
+
var init_validation = __esm({
|
|
1118
|
+
"src/domain/validation.ts"() {
|
|
1119
|
+
hasShape = (schema) => "shape" in schema && schema.shape !== null && schema.shape !== void 0 && typeof schema.shape === "object";
|
|
1120
|
+
hasTypeField = (shape) => "type" in shape && shape.type !== null && shape.type !== void 0 && typeof shape.type === "object" && "value" in shape.type;
|
|
1121
|
+
extractTypeValue = (typeField) => typeof typeField.value === "string" ? typeField.value : void 0;
|
|
1122
|
+
safeExtractType = (schema) => {
|
|
1123
|
+
if (!hasShape(schema)) return void 0;
|
|
1124
|
+
if (!hasTypeField(schema.shape)) return void 0;
|
|
1125
|
+
return extractTypeValue(schema.shape.type);
|
|
1126
|
+
};
|
|
1127
|
+
exports.extractTypeFromSchema = (schema) => {
|
|
343
1128
|
try {
|
|
344
|
-
|
|
1129
|
+
return safeExtractType(schema);
|
|
345
1130
|
} catch {
|
|
346
|
-
|
|
1131
|
+
return void 0;
|
|
347
1132
|
}
|
|
348
|
-
}
|
|
349
|
-
return [];
|
|
350
|
-
}
|
|
351
|
-
};
|
|
352
|
-
var deduplicateHandlers = (handlers) => handlers.reduce((acc, handler) => {
|
|
353
|
-
const existing = acc.find((h) => h.name === handler.name);
|
|
354
|
-
if (!existing) {
|
|
355
|
-
acc.push(handler);
|
|
356
|
-
}
|
|
357
|
-
return acc;
|
|
358
|
-
}, []);
|
|
359
|
-
var EXTENSION_FALLBACKS = ["js", "mjs", "cjs"];
|
|
360
|
-
var expandPatternVariants = (pattern, preferCompiled) => {
|
|
361
|
-
if (!/\.tsx?\b/.test(pattern)) {
|
|
362
|
-
return [pattern];
|
|
363
|
-
}
|
|
364
|
-
const basePattern = pattern;
|
|
365
|
-
const compiledVariants = EXTENSION_FALLBACKS.map((ext) => basePattern.replace(/\.tsx?\b/g, `.${ext}`));
|
|
366
|
-
const ordered = preferCompiled ? [...compiledVariants, basePattern] : [basePattern, ...compiledVariants];
|
|
367
|
-
return [...new Set(ordered)];
|
|
368
|
-
};
|
|
369
|
-
var shouldScanDirectory = (basePath, variant) => {
|
|
370
|
-
if (!fs.existsSync(basePath)) {
|
|
371
|
-
return false;
|
|
372
|
-
}
|
|
373
|
-
const dirPrefix = variant.match(/^([^*{[]+)\//)?.[1];
|
|
374
|
-
if (dirPrefix) {
|
|
375
|
-
const fullDirPath = path.join(basePath, dirPrefix);
|
|
376
|
-
return fs.existsSync(fullDirPath);
|
|
1133
|
+
};
|
|
377
1134
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
var
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
for (const variant of variants) {
|
|
1135
|
+
});
|
|
1136
|
+
var getSearchDirectories, loadHandlers, deduplicateHandlers, EXTENSION_FALLBACKS, expandPatternVariants, shouldScanDirectory, discoverFiles, discoverHandlers;
|
|
1137
|
+
var init_discovery = __esm({
|
|
1138
|
+
"src/domain/discovery.ts"() {
|
|
1139
|
+
init_infrastructure();
|
|
1140
|
+
init_validation();
|
|
1141
|
+
getSearchDirectories = () => {
|
|
1142
|
+
const directories = /* @__PURE__ */ new Set();
|
|
1143
|
+
directories.add(process.cwd());
|
|
388
1144
|
try {
|
|
389
|
-
|
|
390
|
-
|
|
1145
|
+
const currentDir = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
|
|
1146
|
+
directories.add(currentDir);
|
|
1147
|
+
if (currentDir.includes("/dist/")) {
|
|
1148
|
+
const pkgRoot = currentDir.split("/dist/")[0];
|
|
1149
|
+
if (pkgRoot) directories.add(pkgRoot);
|
|
391
1150
|
}
|
|
392
|
-
const files = await glob.glob(variant, {
|
|
393
|
-
cwd: basePath,
|
|
394
|
-
absolute: true,
|
|
395
|
-
nodir: true,
|
|
396
|
-
windowsPathsNoEscape: true
|
|
397
|
-
});
|
|
398
|
-
const existingFiles = files.filter((f) => fs.existsSync(f));
|
|
399
|
-
allFiles.push(...existingFiles);
|
|
400
1151
|
} catch {
|
|
401
1152
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
1153
|
+
return [...directories].filter((dir) => fs.existsSync(dir));
|
|
1154
|
+
};
|
|
1155
|
+
loadHandlers = async (filePath, filter) => {
|
|
1156
|
+
try {
|
|
1157
|
+
if (!fs.existsSync(filePath)) {
|
|
1158
|
+
return [];
|
|
1159
|
+
}
|
|
1160
|
+
const module = await import(filePath);
|
|
1161
|
+
return Object.entries(module).filter(([name, handler]) => isValidHandler(handler) && (!filter || filter(name, handler))).map(([name, handler]) => {
|
|
1162
|
+
const HandlerClass = handler;
|
|
1163
|
+
const hasNoMeaningfulName = !HandlerClass.name || HandlerClass.name === "" || HandlerClass.name === "HandlerClass";
|
|
1164
|
+
const isNamedExport = name !== "default";
|
|
1165
|
+
if (hasNoMeaningfulName && isNamedExport) {
|
|
1166
|
+
return Object.defineProperty(HandlerClass, "name", { value: name, configurable: true });
|
|
1167
|
+
}
|
|
1168
|
+
return HandlerClass;
|
|
1169
|
+
});
|
|
1170
|
+
} catch (error) {
|
|
1171
|
+
logger?.warn(`Failed to load ${filePath}`);
|
|
1172
|
+
logger?.warn(`Error type: ${typeof error}`);
|
|
1173
|
+
logger?.warn(`Error constructor: ${error?.constructor?.name}`);
|
|
1174
|
+
if (error instanceof Error) {
|
|
1175
|
+
logger?.warn(`Error message: ${error.message}`);
|
|
1176
|
+
if (error.stack) {
|
|
1177
|
+
logger?.warn(`Stack trace:
|
|
1178
|
+
${error.stack}`);
|
|
1179
|
+
}
|
|
1180
|
+
} else {
|
|
1181
|
+
logger?.warn(`Error value: ${String(error)}`);
|
|
1182
|
+
try {
|
|
1183
|
+
logger?.warn(`Error JSON: ${JSON.stringify(error, null, 2)}`);
|
|
1184
|
+
} catch {
|
|
1185
|
+
logger?.warn("(Error is not JSON-serializable)");
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
return [];
|
|
416
1189
|
}
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
1190
|
+
};
|
|
1191
|
+
deduplicateHandlers = (handlers) => handlers.reduce((acc, handler) => {
|
|
1192
|
+
const existing = acc.find((h) => h.name === handler.name);
|
|
1193
|
+
if (!existing) {
|
|
1194
|
+
acc.push(handler);
|
|
1195
|
+
}
|
|
1196
|
+
return acc;
|
|
1197
|
+
}, []);
|
|
1198
|
+
EXTENSION_FALLBACKS = ["js", "mjs", "cjs"];
|
|
1199
|
+
expandPatternVariants = (pattern, preferCompiled) => {
|
|
1200
|
+
if (!/\.tsx?\b/.test(pattern)) {
|
|
1201
|
+
return [pattern];
|
|
1202
|
+
}
|
|
1203
|
+
const basePattern = pattern;
|
|
1204
|
+
const compiledVariants = EXTENSION_FALLBACKS.map((ext) => basePattern.replace(/\.tsx?\b/g, `.${ext}`));
|
|
1205
|
+
const ordered = preferCompiled ? [...compiledVariants, basePattern] : [basePattern, ...compiledVariants];
|
|
1206
|
+
return [...new Set(ordered)];
|
|
1207
|
+
};
|
|
1208
|
+
shouldScanDirectory = (basePath, variant) => {
|
|
1209
|
+
if (!fs.existsSync(basePath)) {
|
|
1210
|
+
return false;
|
|
1211
|
+
}
|
|
1212
|
+
const dirPrefix = variant.match(/^([^*{[]+)\//)?.[1];
|
|
1213
|
+
if (dirPrefix) {
|
|
1214
|
+
const fullDirPath = path.join(basePath, dirPrefix);
|
|
1215
|
+
return fs.existsSync(fullDirPath);
|
|
1216
|
+
}
|
|
1217
|
+
return true;
|
|
1218
|
+
};
|
|
1219
|
+
discoverFiles = async (pattern, basePath, preferCompiled) => {
|
|
1220
|
+
const isTestPattern = pattern.startsWith("test/");
|
|
1221
|
+
const prefixedPattern = isTestPattern ? pattern : `{src,dist,build,lib,out}/${pattern}`;
|
|
1222
|
+
const patterns = preferCompiled ? [prefixedPattern, pattern] : [pattern, prefixedPattern];
|
|
1223
|
+
const allFiles = [];
|
|
1224
|
+
for (const globPattern of patterns) {
|
|
1225
|
+
const variants = expandPatternVariants(globPattern, preferCompiled);
|
|
1226
|
+
for (const variant of variants) {
|
|
1227
|
+
try {
|
|
1228
|
+
if (!shouldScanDirectory(basePath, variant)) {
|
|
1229
|
+
continue;
|
|
1230
|
+
}
|
|
1231
|
+
const files = await glob.glob(variant, {
|
|
1232
|
+
cwd: basePath,
|
|
1233
|
+
absolute: true,
|
|
1234
|
+
nodir: true,
|
|
1235
|
+
windowsPathsNoEscape: true
|
|
1236
|
+
});
|
|
1237
|
+
const existingFiles = files.filter((f) => fs.existsSync(f));
|
|
1238
|
+
allFiles.push(...existingFiles);
|
|
1239
|
+
} catch {
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
return [...new Set(allFiles)];
|
|
1244
|
+
};
|
|
1245
|
+
discoverHandlers = async (pattern, options = {}) => {
|
|
1246
|
+
const { filter } = options;
|
|
1247
|
+
const searchDirectories = getSearchDirectories();
|
|
1248
|
+
const preferCompiled = searchDirectories.some((dir) => dir.includes("/dist/") || dir.includes("\\dist\\"));
|
|
1249
|
+
try {
|
|
1250
|
+
const uniqueFiles = /* @__PURE__ */ new Set();
|
|
1251
|
+
for (const basePath of searchDirectories) {
|
|
1252
|
+
const discoveredFiles = await discoverFiles(pattern, basePath, preferCompiled);
|
|
1253
|
+
for (const file of discoveredFiles) {
|
|
1254
|
+
uniqueFiles.add(file);
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
if (uniqueFiles.size === 0) {
|
|
1258
|
+
logger.warn(`No files found matching pattern: ${pattern}`);
|
|
1259
|
+
return [];
|
|
1260
|
+
}
|
|
1261
|
+
const handlers = await Promise.all([...uniqueFiles].map((file) => loadHandlers(file, filter)));
|
|
1262
|
+
const flatHandlers = handlers.flat();
|
|
1263
|
+
const uniqueHandlers = deduplicateHandlers(flatHandlers);
|
|
1264
|
+
return uniqueHandlers;
|
|
1265
|
+
} catch (error) {
|
|
1266
|
+
logger.error("Discovery failed:", error);
|
|
1267
|
+
return [];
|
|
1268
|
+
}
|
|
1269
|
+
};
|
|
429
1270
|
}
|
|
430
|
-
};
|
|
1271
|
+
});
|
|
431
1272
|
function extractFromValueGetter(typeField) {
|
|
432
1273
|
if ("value" in typeField && typeof typeField.value === "string") {
|
|
433
1274
|
return typeField.value;
|
|
@@ -521,160 +1362,353 @@ function handleEvent(schemaOrOptions, handler, eventType) {
|
|
|
521
1362
|
function eventSchema(schema) {
|
|
522
1363
|
return zod.z.object(schema);
|
|
523
1364
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
init_infrastructure();
|
|
527
|
-
|
|
528
|
-
// src/processing/dlq-safe.ts
|
|
529
|
-
init_infrastructure();
|
|
530
|
-
init_infrastructure();
|
|
531
|
-
|
|
532
|
-
// src/utils/pluralize.ts
|
|
533
|
-
var pluralize = (word) => {
|
|
534
|
-
if (word.endsWith("y") && !["a", "e", "i", "o", "u"].includes(word[word.length - 2])) {
|
|
535
|
-
return `${word.slice(0, -1)}ies`;
|
|
536
|
-
}
|
|
537
|
-
if (word.endsWith("s") || word.endsWith("sh") || word.endsWith("ch") || word.endsWith("x")) {
|
|
538
|
-
return `${word}es`;
|
|
1365
|
+
var init_handler_factory = __esm({
|
|
1366
|
+
"src/domain/handler-factory.ts"() {
|
|
539
1367
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
return word.slice(0, -1);
|
|
1368
|
+
});
|
|
1369
|
+
|
|
1370
|
+
// src/domain/index.ts
|
|
1371
|
+
var init_domain = __esm({
|
|
1372
|
+
"src/domain/index.ts"() {
|
|
1373
|
+
init_contract_helper();
|
|
1374
|
+
init_discovery();
|
|
1375
|
+
init_handler_factory();
|
|
1376
|
+
init_naming();
|
|
1377
|
+
init_validation();
|
|
551
1378
|
}
|
|
552
|
-
|
|
553
|
-
};
|
|
1379
|
+
});
|
|
554
1380
|
|
|
555
1381
|
// src/publishing/nats.publisher.ts
|
|
556
|
-
var
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
};
|
|
566
|
-
var getNatsConnection =
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
1382
|
+
var nats_publisher_exports = {};
|
|
1383
|
+
__export(nats_publisher_exports, {
|
|
1384
|
+
__resetNatsPublisher: () => exports.__resetNatsPublisher,
|
|
1385
|
+
closeConnection: () => exports.closeConnection,
|
|
1386
|
+
deriveStreamFromType: () => exports.deriveStreamFromType,
|
|
1387
|
+
deriveSubjectFromType: () => exports.deriveSubjectFromType,
|
|
1388
|
+
publish: () => exports.publish,
|
|
1389
|
+
publishNatsEvent: () => exports.publishNatsEvent,
|
|
1390
|
+
publishNatsRawEvent: () => exports.publishNatsRawEvent
|
|
1391
|
+
});
|
|
1392
|
+
var sc, natsConnectionPromise, deriveSubjectFromEventType, getNatsConnection; exports.closeConnection = void 0; exports.__resetNatsPublisher = void 0; exports.deriveSubjectFromType = void 0; exports.deriveStreamFromType = void 0; exports.publishNatsRawEvent = void 0; exports.publishNatsEvent = void 0; exports.publish = void 0;
|
|
1393
|
+
var init_nats_publisher = __esm({
|
|
1394
|
+
"src/publishing/nats.publisher.ts"() {
|
|
1395
|
+
init_domain();
|
|
1396
|
+
init_infrastructure();
|
|
1397
|
+
init_utils();
|
|
1398
|
+
sc = nats.StringCodec();
|
|
1399
|
+
natsConnectionPromise = null;
|
|
1400
|
+
deriveSubjectFromEventType = (eventType) => {
|
|
1401
|
+
const parts = eventType.split(".");
|
|
1402
|
+
if (parts.length < 2) return eventType;
|
|
1403
|
+
const domain = parts[0];
|
|
1404
|
+
const action = parts.slice(1).join(".");
|
|
1405
|
+
const pluralDomain = exports.pluralize(domain);
|
|
1406
|
+
return `${pluralDomain}.${action}`;
|
|
1407
|
+
};
|
|
1408
|
+
getNatsConnection = async (servers) => {
|
|
1409
|
+
if (!natsConnectionPromise) {
|
|
1410
|
+
const url = servers ?? process.env.NATS_URL ?? "nats://localhost:4222";
|
|
1411
|
+
natsConnectionPromise = nats.connect({ servers: url }).then((connection) => {
|
|
1412
|
+
logger.debug(`[NATS] connected to ${url}`);
|
|
1413
|
+
return connection;
|
|
1414
|
+
}).catch((error) => {
|
|
1415
|
+
logger.error("[NATS] connection error", error);
|
|
1416
|
+
natsConnectionPromise = null;
|
|
1417
|
+
throw error;
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1420
|
+
return natsConnectionPromise;
|
|
1421
|
+
};
|
|
1422
|
+
exports.closeConnection = async () => {
|
|
1423
|
+
if (natsConnectionPromise) {
|
|
1424
|
+
try {
|
|
1425
|
+
const nc = await natsConnectionPromise;
|
|
1426
|
+
if (nc && typeof nc.drain === "function") {
|
|
1427
|
+
await nc.drain();
|
|
1428
|
+
logger.debug("[NATS] connection closed");
|
|
1429
|
+
}
|
|
1430
|
+
} catch {
|
|
1431
|
+
} finally {
|
|
1432
|
+
natsConnectionPromise = null;
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
};
|
|
1436
|
+
exports.__resetNatsPublisher = async () => {
|
|
1437
|
+
await exports.closeConnection();
|
|
1438
|
+
};
|
|
1439
|
+
exports.deriveSubjectFromType = (eventType, config) => {
|
|
1440
|
+
if (!config?.typeToSubjectMap) {
|
|
1441
|
+
return config?.defaultSubjectPrefix ? `${config.defaultSubjectPrefix}.${eventType}` : eventType;
|
|
1442
|
+
}
|
|
1443
|
+
const sortedPrefixes = Object.keys(config.typeToSubjectMap).sort((a, b) => b.length - a.length);
|
|
1444
|
+
for (const prefix of sortedPrefixes) {
|
|
1445
|
+
if (eventType.startsWith(prefix)) {
|
|
1446
|
+
const suffix = eventType.slice(prefix.length);
|
|
1447
|
+
const mappedPrefix = config.typeToSubjectMap[prefix];
|
|
1448
|
+
const cleanSuffix = suffix.startsWith(".") ? suffix.slice(1) : suffix;
|
|
1449
|
+
return cleanSuffix ? `${mappedPrefix}.${cleanSuffix}` : mappedPrefix;
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
return config.defaultSubjectPrefix ? `${config.defaultSubjectPrefix}.${eventType}` : eventType;
|
|
1453
|
+
};
|
|
1454
|
+
exports.deriveStreamFromType = (eventType, config) => {
|
|
1455
|
+
if (!config?.typeToStreamMap) return void 0;
|
|
1456
|
+
const sortedPrefixes = Object.keys(config.typeToStreamMap).sort((a, b) => b.length - a.length);
|
|
1457
|
+
for (const prefix of sortedPrefixes) {
|
|
1458
|
+
if (eventType.startsWith(prefix)) {
|
|
1459
|
+
return config.typeToStreamMap[prefix];
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
return void 0;
|
|
1463
|
+
};
|
|
1464
|
+
exports.publishNatsRawEvent = async (subjectName, eventType, eventData, options) => {
|
|
1465
|
+
const cloudEvent = {
|
|
1466
|
+
specversion: "1.0",
|
|
1467
|
+
type: eventType,
|
|
1468
|
+
source: options?.source || "hono-service",
|
|
1469
|
+
id: crypto.randomUUID(),
|
|
1470
|
+
time: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1471
|
+
datacontenttype: "application/json",
|
|
1472
|
+
data: eventData,
|
|
1473
|
+
...options?.subject && { subject: options.subject },
|
|
1474
|
+
...options?.tenantId && { tenantid: options.tenantId }
|
|
1475
|
+
};
|
|
1476
|
+
const data = JSON.stringify(cloudEvent);
|
|
1477
|
+
const nc = await getNatsConnection(options?.servers);
|
|
1478
|
+
nc.publish(subjectName, sc.encode(data));
|
|
1479
|
+
logger.debug(`Published CloudEvent ${eventType} to NATS subject ${subjectName} (id=${cloudEvent.id})`);
|
|
1480
|
+
if (options?.closeAfterPublish) {
|
|
1481
|
+
await exports.closeConnection();
|
|
1482
|
+
}
|
|
1483
|
+
return cloudEvent.id;
|
|
1484
|
+
};
|
|
1485
|
+
exports.publishNatsEvent = async (subjectName, schema, eventData, options) => {
|
|
1486
|
+
const eventType = exports.extractTypeFromSchema(schema);
|
|
1487
|
+
if (!eventType) {
|
|
1488
|
+
throw new Error("Could not extract event type from schema. Make sure your schema has proper metadata.");
|
|
1489
|
+
}
|
|
1490
|
+
const validationResult = schema.safeParse(eventData);
|
|
1491
|
+
if (!validationResult.success) {
|
|
1492
|
+
const validationDetails = validationResult.error.issues.map((issue) => ({
|
|
1493
|
+
code: issue.code,
|
|
1494
|
+
message: issue.message,
|
|
1495
|
+
path: issue.path.filter((p) => typeof p !== "symbol"),
|
|
1496
|
+
expected: "expected" in issue ? String(issue.expected) : void 0,
|
|
1497
|
+
received: "received" in issue ? String(issue.received) : void 0
|
|
1498
|
+
}));
|
|
1499
|
+
const handlerValidationError = {
|
|
1500
|
+
handlerName: `NatsPublisher:${eventType}`,
|
|
1501
|
+
validationErrors: validationDetails
|
|
1502
|
+
};
|
|
1503
|
+
throw createValidationError(eventType, [handlerValidationError]);
|
|
1504
|
+
}
|
|
1505
|
+
return exports.publishNatsRawEvent(subjectName, eventType, validationResult.data, options);
|
|
1506
|
+
};
|
|
1507
|
+
exports.publish = async (eventTypeOrContract, eventData, options) => {
|
|
1508
|
+
const eventType = typeof eventTypeOrContract === "string" ? eventTypeOrContract : eventTypeOrContract.type;
|
|
1509
|
+
const natsSubject = typeof eventTypeOrContract === "string" ? deriveSubjectFromEventType(eventTypeOrContract) : eventTypeOrContract.channel?.subject ?? eventTypeOrContract.type;
|
|
1510
|
+
return exports.publishNatsRawEvent(natsSubject, eventType, eventData, options);
|
|
1511
|
+
};
|
|
577
1512
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
1513
|
+
});
|
|
1514
|
+
|
|
1515
|
+
// src/flows/publish-event.flow.ts
|
|
1516
|
+
var publish_event_flow_exports = {};
|
|
1517
|
+
__export(publish_event_flow_exports, {
|
|
1518
|
+
parseDataInput: () => exports.parseDataInput,
|
|
1519
|
+
publishEventFlowSteps: () => exports.publishEventFlowSteps
|
|
1520
|
+
});
|
|
1521
|
+
exports.parseDataInput = void 0; var loadDataFromInput, publishToNats, hasExplicitData, isInvalidEventType2, getEventTypeErrorMessage2, flow3; exports.publishEventFlowSteps = void 0;
|
|
1522
|
+
var init_publish_event_flow = __esm({
|
|
1523
|
+
"src/flows/publish-event.flow.ts"() {
|
|
1524
|
+
init_naming();
|
|
1525
|
+
init_generators();
|
|
1526
|
+
exports.parseDataInput = (input2) => {
|
|
1527
|
+
if (!input2.trim()) return {};
|
|
1528
|
+
try {
|
|
1529
|
+
return JSON.parse(input2);
|
|
1530
|
+
} catch {
|
|
1531
|
+
return null;
|
|
1532
|
+
}
|
|
1533
|
+
};
|
|
1534
|
+
loadDataFromInput = async (ctx) => {
|
|
1535
|
+
if (ctx.dataInput !== void 0 && ctx.dataInput.trim() !== "") {
|
|
1536
|
+
const parsed = exports.parseDataInput(ctx.dataInput);
|
|
1537
|
+
if (parsed === null) {
|
|
1538
|
+
throw new Error("Invalid JSON data");
|
|
1539
|
+
}
|
|
1540
|
+
ctx.data = parsed;
|
|
1541
|
+
return;
|
|
1542
|
+
}
|
|
1543
|
+
const { loadJsonMock: loadJsonMock2 } = await Promise.resolve().then(() => (init_generators(), generators_exports));
|
|
1544
|
+
const mock = loadJsonMock2(ctx.eventType, ctx.config.contractsPath);
|
|
1545
|
+
if (mock?.data) {
|
|
1546
|
+
ctx.data = mock.data;
|
|
1547
|
+
ctx.usedMock = true;
|
|
1548
|
+
return;
|
|
1549
|
+
}
|
|
1550
|
+
ctx.data = {};
|
|
1551
|
+
};
|
|
1552
|
+
publishToNats = async (ctx) => {
|
|
1553
|
+
const { publish: publish2 } = await Promise.resolve().then(() => (init_nats_publisher(), nats_publisher_exports));
|
|
1554
|
+
await publish2(ctx.eventType, ctx.data ?? {}, {
|
|
1555
|
+
source: "pf-cli",
|
|
1556
|
+
closeAfterPublish: true
|
|
1557
|
+
});
|
|
1558
|
+
ctx.published = true;
|
|
1559
|
+
const mockHint = ctx.usedMock ? " (using mock data)" : "";
|
|
1560
|
+
console.log(`\u2705 Published ${ctx.eventType}${mockHint}`);
|
|
1561
|
+
};
|
|
1562
|
+
hasExplicitData = (ctx) => ctx.data !== void 0 || exports.jsonMockExists(ctx.eventType, ctx.config.contractsPath);
|
|
1563
|
+
isInvalidEventType2 = (ctx) => !exports.isValidEventType(ctx.eventType);
|
|
1564
|
+
getEventTypeErrorMessage2 = (ctx) => `Invalid event type: ${exports.validateEventType(ctx.eventType).errors.join(", ")}`;
|
|
1565
|
+
flow3 = flowcore.createFlow();
|
|
1566
|
+
exports.publishEventFlowSteps = [
|
|
1567
|
+
flow3.input("eventType").prompt({
|
|
1568
|
+
title: "Event Type",
|
|
1569
|
+
message: "Event type to publish (e.g., order.created):"
|
|
1570
|
+
}).validateBy(exports.validateEventType).skipIfSet().build(),
|
|
1571
|
+
flow3.abortIf(isInvalidEventType2, getEventTypeErrorMessage2).build(),
|
|
1572
|
+
flow3.input("dataInput").prompt({
|
|
1573
|
+
title: "Event Data",
|
|
1574
|
+
message: "Event data as JSON (or leave empty for mock data):"
|
|
1575
|
+
}).skipWhen(hasExplicitData).build(),
|
|
1576
|
+
flow3.task("Publish event").steps([
|
|
1577
|
+
loadDataFromInput,
|
|
1578
|
+
publishToNats
|
|
1579
|
+
]).produces("data", "published", "usedMock").build()
|
|
1580
|
+
];
|
|
592
1581
|
}
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
1582
|
+
});
|
|
1583
|
+
|
|
1584
|
+
// src/adapters/cloudevents/cloudevents.ts
|
|
1585
|
+
init_infrastructure();
|
|
1586
|
+
var hasCloudEventHeaders = (headers) => Object.keys(headers).some((key) => key.toLowerCase().startsWith("ce-"));
|
|
1587
|
+
var isRecord = (value) => typeof value === "object" && value !== null;
|
|
1588
|
+
var parseEventFromContext = async (context) => {
|
|
1589
|
+
try {
|
|
1590
|
+
const headers = context.req.header();
|
|
1591
|
+
const rawBody = await context.req.text();
|
|
1592
|
+
let parsedBody;
|
|
1593
|
+
if (rawBody?.length) {
|
|
1594
|
+
try {
|
|
1595
|
+
parsedBody = JSON.parse(rawBody);
|
|
1596
|
+
} catch {
|
|
1597
|
+
parsedBody = void 0;
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
const bodyObject = isRecord(parsedBody) ? parsedBody : void 0;
|
|
1601
|
+
if (bodyObject && "specversion" in bodyObject) {
|
|
1602
|
+
const { parseStructuredMode: parseStructuredMode2 } = await Promise.resolve().then(() => (init_structured_mode(), structured_mode_exports));
|
|
1603
|
+
return parseStructuredMode2(bodyObject);
|
|
1604
|
+
}
|
|
1605
|
+
if (bodyObject && "message" in bodyObject) {
|
|
1606
|
+
const { parsePubSubMessage: parsePubSubMessage2 } = await Promise.resolve().then(() => (init_pubsub(), pubsub_exports));
|
|
1607
|
+
return await parsePubSubMessage2(bodyObject, headers);
|
|
601
1608
|
}
|
|
1609
|
+
if (hasCloudEventHeaders(headers)) {
|
|
1610
|
+
const { parseBinaryMode: parseBinaryMode2 } = await Promise.resolve().then(() => (init_binary_mode(), binary_mode_exports));
|
|
1611
|
+
return await parseBinaryMode2(headers, rawBody);
|
|
1612
|
+
}
|
|
1613
|
+
const { parseRawEvent: parseRawEvent2 } = await Promise.resolve().then(() => (init_raw_event(), raw_event_exports));
|
|
1614
|
+
if (bodyObject) {
|
|
1615
|
+
return parseRawEvent2(bodyObject);
|
|
1616
|
+
}
|
|
1617
|
+
return parseRawEvent2({ raw: rawBody });
|
|
1618
|
+
} catch (error) {
|
|
1619
|
+
logger.error("Failed to parse event:", error);
|
|
1620
|
+
throw new Error(`Failed to parse CloudEvent: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
602
1621
|
}
|
|
603
|
-
return void 0;
|
|
604
1622
|
};
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
1623
|
+
|
|
1624
|
+
// src/flows/index.ts
|
|
1625
|
+
init_create_event_flow();
|
|
1626
|
+
init_list_events_flow();
|
|
1627
|
+
init_publish_event_flow();
|
|
1628
|
+
|
|
1629
|
+
// src/api.ts
|
|
1630
|
+
var createEvent = async (eventType, options = {}) => {
|
|
1631
|
+
const { fields, service, contractsPath, mocksPath, force } = options;
|
|
1632
|
+
const result = await flowcore.runFlow(exports.createEventFlowSteps, {
|
|
1633
|
+
initialContext: {
|
|
1634
|
+
eventType,
|
|
1635
|
+
fields: typeof fields === "string" ? exports.parseFieldsInput(fields) : fields,
|
|
1636
|
+
servicePath: service,
|
|
1637
|
+
availableServices: [],
|
|
1638
|
+
// No services in simple API
|
|
1639
|
+
force,
|
|
1640
|
+
config: {
|
|
1641
|
+
contractsPath: contractsPath ?? "packages/contracts/src",
|
|
1642
|
+
mocksPath
|
|
1643
|
+
}
|
|
1644
|
+
},
|
|
1645
|
+
inputs: {},
|
|
1646
|
+
// Non-interactive mode
|
|
1647
|
+
exitOnError: true
|
|
1648
|
+
});
|
|
1649
|
+
if (!result.success) {
|
|
1650
|
+
throw new Error(result.abortReason ?? "Failed to create event");
|
|
1651
|
+
}
|
|
1652
|
+
return {
|
|
1653
|
+
created: result.context._generation?.created ?? [],
|
|
1654
|
+
skipped: result.context._generation?.skipped ?? [],
|
|
1655
|
+
effects: result.context._generation?.effects ?? []
|
|
616
1656
|
};
|
|
617
|
-
const data = JSON.stringify(cloudEvent);
|
|
618
|
-
const nc = await getNatsConnection(options?.servers);
|
|
619
|
-
nc.publish(subjectName, sc.encode(data));
|
|
620
|
-
logger.debug(`Published CloudEvent ${eventType} to NATS subject ${subjectName} (id=${cloudEvent.id})`);
|
|
621
|
-
return cloudEvent.id;
|
|
622
1657
|
};
|
|
623
|
-
var
|
|
624
|
-
const
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
}));
|
|
637
|
-
const handlerValidationError = {
|
|
638
|
-
handlerName: `NatsPublisher:${eventType}`,
|
|
639
|
-
validationErrors: validationDetails
|
|
640
|
-
};
|
|
641
|
-
throw createValidationError(eventType, [handlerValidationError]);
|
|
1658
|
+
var listEvents = async (options = {}) => {
|
|
1659
|
+
const { pattern, contractsPath } = options;
|
|
1660
|
+
const result = await flowcore.runFlow(exports.listEventsFlowSteps, {
|
|
1661
|
+
initialContext: {
|
|
1662
|
+
pattern: pattern ?? "",
|
|
1663
|
+
config: {
|
|
1664
|
+
contractsPath: contractsPath ?? "packages/contracts/src"
|
|
1665
|
+
}
|
|
1666
|
+
},
|
|
1667
|
+
exitOnError: true
|
|
1668
|
+
});
|
|
1669
|
+
if (!result.success) {
|
|
1670
|
+
throw new Error(result.abortReason ?? "Failed to list events");
|
|
642
1671
|
}
|
|
643
|
-
return
|
|
644
|
-
};
|
|
645
|
-
var publish = async (eventTypeOrContract, eventData, options) => {
|
|
646
|
-
const eventType = typeof eventTypeOrContract === "string" ? eventTypeOrContract : eventTypeOrContract.type;
|
|
647
|
-
const natsSubject = typeof eventTypeOrContract === "string" ? deriveSubjectFromEventType(eventTypeOrContract) : eventTypeOrContract.channel?.subject ?? eventTypeOrContract.type;
|
|
648
|
-
return publishNatsRawEvent(natsSubject, eventType, eventData, options);
|
|
1672
|
+
return result.context.eventTypes ?? [];
|
|
649
1673
|
};
|
|
650
|
-
var
|
|
651
|
-
|
|
1674
|
+
var publishEvent = async (eventType, data, options = {}) => {
|
|
1675
|
+
const { natsUrl, contractsPath } = options;
|
|
1676
|
+
const result = await flowcore.runFlow(exports.publishEventFlowSteps, {
|
|
1677
|
+
initialContext: {
|
|
1678
|
+
eventType,
|
|
1679
|
+
data,
|
|
1680
|
+
config: {
|
|
1681
|
+
contractsPath: contractsPath ?? "packages/contracts/src",
|
|
1682
|
+
natsUrl: natsUrl ?? "nats://localhost:4222"
|
|
1683
|
+
},
|
|
1684
|
+
published: false
|
|
1685
|
+
},
|
|
1686
|
+
exitOnError: true
|
|
1687
|
+
});
|
|
1688
|
+
if (!result.success) {
|
|
1689
|
+
throw new Error(result.abortReason ?? "Failed to publish event");
|
|
1690
|
+
}
|
|
1691
|
+
return { success: true };
|
|
652
1692
|
};
|
|
653
1693
|
|
|
1694
|
+
// src/index.ts
|
|
1695
|
+
init_domain();
|
|
1696
|
+
init_naming();
|
|
1697
|
+
init_effects();
|
|
1698
|
+
init_generators();
|
|
1699
|
+
|
|
1700
|
+
// src/middlewares/cloudevents-middleware.ts
|
|
1701
|
+
init_infrastructure();
|
|
1702
|
+
|
|
1703
|
+
// src/processing/dlq-safe.ts
|
|
1704
|
+
init_infrastructure();
|
|
1705
|
+
|
|
1706
|
+
// src/publishing/index.ts
|
|
1707
|
+
init_nats_publisher();
|
|
1708
|
+
|
|
654
1709
|
// src/publishing/pubsub.publisher.ts
|
|
1710
|
+
init_domain();
|
|
655
1711
|
init_infrastructure();
|
|
656
|
-
async function publishEvent(topicName, schema, eventData, options) {
|
|
657
|
-
const eventType = extractTypeFromSchema(schema);
|
|
658
|
-
if (!eventType) {
|
|
659
|
-
throw new Error("Could not extract event type from schema. Make sure your schema has proper metadata.");
|
|
660
|
-
}
|
|
661
|
-
const validationResult = schema.safeParse(eventData);
|
|
662
|
-
if (!validationResult.success) {
|
|
663
|
-
const validationDetails = validationResult.error.issues.map((issue) => ({
|
|
664
|
-
code: issue.code,
|
|
665
|
-
message: issue.message,
|
|
666
|
-
path: issue.path.filter((p) => typeof p !== "symbol"),
|
|
667
|
-
expected: "expected" in issue ? String(issue.expected) : void 0,
|
|
668
|
-
received: "received" in issue ? String(issue.received) : void 0
|
|
669
|
-
}));
|
|
670
|
-
const handlerValidationError = {
|
|
671
|
-
handlerName: `Publisher:${eventType}`,
|
|
672
|
-
validationErrors: validationDetails
|
|
673
|
-
};
|
|
674
|
-
throw createValidationError(eventType, [handlerValidationError]);
|
|
675
|
-
}
|
|
676
|
-
return publishRawEvent(topicName, eventType, validationResult.data, options);
|
|
677
|
-
}
|
|
678
1712
|
async function publishRawEvent(topicName, eventType, eventData, options) {
|
|
679
1713
|
const { PubSub } = await import('@google-cloud/pubsub');
|
|
680
1714
|
const pubsub = new PubSub({
|
|
@@ -793,6 +1827,7 @@ var createProcessingContext = (eventType, eventData, context, originalCloudEvent
|
|
|
793
1827
|
});
|
|
794
1828
|
|
|
795
1829
|
// src/processing/handler-cache.ts
|
|
1830
|
+
init_domain();
|
|
796
1831
|
init_logging();
|
|
797
1832
|
var createEmptyCache = () => /* @__PURE__ */ new Map();
|
|
798
1833
|
var createEmptyMessageIds = () => /* @__PURE__ */ new Set();
|
|
@@ -814,7 +1849,7 @@ var processHandler = (HandlerClass) => {
|
|
|
814
1849
|
const metadata = HandlerClass.__eventarcMetadata;
|
|
815
1850
|
if (!metadata) return null;
|
|
816
1851
|
const instance = new HandlerClass();
|
|
817
|
-
const eventType = metadata.declaredType || extractTypeFromSchema(metadata.schema);
|
|
1852
|
+
const eventType = metadata.declaredType || exports.extractTypeFromSchema(metadata.schema);
|
|
818
1853
|
if (!eventType) return null;
|
|
819
1854
|
return {
|
|
820
1855
|
type: eventType,
|
|
@@ -1116,6 +2151,142 @@ function cloudEvents(options = {}) {
|
|
|
1116
2151
|
};
|
|
1117
2152
|
}
|
|
1118
2153
|
|
|
2154
|
+
// package.json
|
|
2155
|
+
var package_default = {
|
|
2156
|
+
name: "@crossdelta/cloudevents",
|
|
2157
|
+
version: "0.6.4",
|
|
2158
|
+
description: "CloudEvents toolkit for TypeScript - Zod validation, handler discovery, NATS JetStream"};
|
|
2159
|
+
|
|
2160
|
+
// src/plugin.ts
|
|
2161
|
+
var createAddCommand = (config) => ({
|
|
2162
|
+
name: "add",
|
|
2163
|
+
description: "Add a new event contract, handler, and mock",
|
|
2164
|
+
args: [{ name: "eventType", description: "Event type (e.g., order.created)", required: false }],
|
|
2165
|
+
options: [
|
|
2166
|
+
{ flags: "-s, --service <path>", description: "Service path for handler" },
|
|
2167
|
+
{ flags: "-f, --fields <fields>", description: "Schema fields (e.g., orderId:string,total:number)" },
|
|
2168
|
+
{ flags: "--force", description: "Overwrite existing files", default: false }
|
|
2169
|
+
],
|
|
2170
|
+
run: async (args, opts) => {
|
|
2171
|
+
const { runFlow: runFlow2 } = await import('@crossdelta/flowcore');
|
|
2172
|
+
const { createEventFlowSteps: createEventFlowSteps2, parseFieldsInput: parseFieldsInput2 } = await Promise.resolve().then(() => (init_create_event_flow(), create_event_flow_exports));
|
|
2173
|
+
const result = await runFlow2(createEventFlowSteps2, {
|
|
2174
|
+
initialContext: {
|
|
2175
|
+
eventType: args.eventType,
|
|
2176
|
+
fields: opts.fields ? parseFieldsInput2(opts.fields) : void 0,
|
|
2177
|
+
servicePath: opts.service,
|
|
2178
|
+
availableServices: config.availableServices,
|
|
2179
|
+
force: opts.force,
|
|
2180
|
+
config: {
|
|
2181
|
+
contractsPath: config.contractsPath,
|
|
2182
|
+
contractsPackage: config.contractsPackage,
|
|
2183
|
+
mocksPath: config.mocksPath
|
|
2184
|
+
}
|
|
2185
|
+
},
|
|
2186
|
+
exitOnError: true
|
|
2187
|
+
});
|
|
2188
|
+
if (!result.success) {
|
|
2189
|
+
throw new Error(result.abortReason ?? "Unknown error");
|
|
2190
|
+
}
|
|
2191
|
+
return { effects: result.context._generation?.effects ?? [] };
|
|
2192
|
+
}
|
|
2193
|
+
});
|
|
2194
|
+
var createListCommand = (config) => ({
|
|
2195
|
+
name: "list",
|
|
2196
|
+
description: "List event types in the workspace",
|
|
2197
|
+
args: [],
|
|
2198
|
+
options: [{ flags: "-p, --pattern <pattern>", description: "Filter by pattern" }],
|
|
2199
|
+
run: async (_args, opts) => {
|
|
2200
|
+
const { runFlow: runFlow2 } = await import('@crossdelta/flowcore');
|
|
2201
|
+
const { listEventsFlowSteps: listEventsFlowSteps2 } = await Promise.resolve().then(() => (init_list_events_flow(), list_events_flow_exports));
|
|
2202
|
+
const pattern = opts.pattern !== void 0 ? opts.pattern : "";
|
|
2203
|
+
const result = await runFlow2(listEventsFlowSteps2, {
|
|
2204
|
+
initialContext: {
|
|
2205
|
+
pattern,
|
|
2206
|
+
config: { contractsPath: config.contractsPath }
|
|
2207
|
+
},
|
|
2208
|
+
exitOnError: true
|
|
2209
|
+
});
|
|
2210
|
+
if (!result.success) {
|
|
2211
|
+
throw new Error(result.abortReason ?? "Unknown error");
|
|
2212
|
+
}
|
|
2213
|
+
const eventTypes = result.context.eventTypes ?? [];
|
|
2214
|
+
const output = eventTypes.length > 0 ? `
|
|
2215
|
+
Event types:
|
|
2216
|
+
${eventTypes.map((e) => ` \u2022 ${e}`).join("\n")}` : "\n\u2139\uFE0F No event types found";
|
|
2217
|
+
return { effects: [], output };
|
|
2218
|
+
}
|
|
2219
|
+
});
|
|
2220
|
+
var createPublishCommand = (config) => ({
|
|
2221
|
+
name: "publish",
|
|
2222
|
+
description: "Publish an event to NATS",
|
|
2223
|
+
args: [{ name: "eventType", description: "Event type to publish", required: true }],
|
|
2224
|
+
options: [{ flags: "-d, --data <json>", description: "Event data as JSON" }],
|
|
2225
|
+
run: async (args, opts) => {
|
|
2226
|
+
const { runFlow: runFlow2 } = await import('@crossdelta/flowcore');
|
|
2227
|
+
const { publishEventFlowSteps: publishEventFlowSteps2, parseDataInput: parseDataInput2 } = await Promise.resolve().then(() => (init_publish_event_flow(), publish_event_flow_exports));
|
|
2228
|
+
const data = opts.data ? parseDataInput2(opts.data) ?? void 0 : void 0;
|
|
2229
|
+
const result = await runFlow2(publishEventFlowSteps2, {
|
|
2230
|
+
initialContext: {
|
|
2231
|
+
eventType: args.eventType,
|
|
2232
|
+
data,
|
|
2233
|
+
config: { contractsPath: config.contractsPath, natsUrl: config.natsUrl },
|
|
2234
|
+
published: false
|
|
2235
|
+
},
|
|
2236
|
+
exitOnError: true
|
|
2237
|
+
});
|
|
2238
|
+
if (!result.success) {
|
|
2239
|
+
throw new Error(result.abortReason ?? "Unknown error");
|
|
2240
|
+
}
|
|
2241
|
+
return { effects: [] };
|
|
2242
|
+
}
|
|
2243
|
+
});
|
|
2244
|
+
var createFlows = () => [
|
|
2245
|
+
{
|
|
2246
|
+
name: "create-event",
|
|
2247
|
+
description: "Interactive flow for creating event artifacts",
|
|
2248
|
+
getSteps: async () => {
|
|
2249
|
+
const { createEventFlowSteps: createEventFlowSteps2 } = await Promise.resolve().then(() => (init_create_event_flow(), create_event_flow_exports));
|
|
2250
|
+
return createEventFlowSteps2;
|
|
2251
|
+
}
|
|
2252
|
+
},
|
|
2253
|
+
{
|
|
2254
|
+
name: "list-events",
|
|
2255
|
+
description: "Interactive flow for listing events",
|
|
2256
|
+
getSteps: async () => {
|
|
2257
|
+
const { listEventsFlowSteps: listEventsFlowSteps2 } = await Promise.resolve().then(() => (init_list_events_flow(), list_events_flow_exports));
|
|
2258
|
+
return listEventsFlowSteps2;
|
|
2259
|
+
}
|
|
2260
|
+
},
|
|
2261
|
+
{
|
|
2262
|
+
name: "publish-event",
|
|
2263
|
+
description: "Interactive flow for publishing events",
|
|
2264
|
+
getSteps: async () => {
|
|
2265
|
+
const { publishEventFlowSteps: publishEventFlowSteps2 } = await Promise.resolve().then(() => (init_publish_event_flow(), publish_event_flow_exports));
|
|
2266
|
+
return publishEventFlowSteps2;
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
];
|
|
2270
|
+
var createPfPlugin = (options = {}) => {
|
|
2271
|
+
const config = {
|
|
2272
|
+
contractsPath: options.contractsPath ?? "packages/contracts/src",
|
|
2273
|
+
contractsPackage: options.contractsPackage,
|
|
2274
|
+
mocksPath: options.mocksPath,
|
|
2275
|
+
availableServices: options.availableServices ?? [],
|
|
2276
|
+
natsUrl: options.natsUrl ?? "nats://localhost:4222"
|
|
2277
|
+
};
|
|
2278
|
+
return {
|
|
2279
|
+
name: package_default.name.replace("@crossdelta/", ""),
|
|
2280
|
+
version: package_default.version,
|
|
2281
|
+
description: package_default.description,
|
|
2282
|
+
commands: [createAddCommand(config), createListCommand(config), createPublishCommand(config)],
|
|
2283
|
+
flows: createFlows(),
|
|
2284
|
+
setup: (context) => {
|
|
2285
|
+
context.logger.debug("cloudevents plugin loaded");
|
|
2286
|
+
}
|
|
2287
|
+
};
|
|
2288
|
+
};
|
|
2289
|
+
|
|
1119
2290
|
// src/transports/nats/base-message-processor.ts
|
|
1120
2291
|
function extractTenantId(ce) {
|
|
1121
2292
|
const extensions = ce;
|
|
@@ -1213,6 +2384,9 @@ function createBaseMessageProcessor(deps) {
|
|
|
1213
2384
|
handleUnhandledError
|
|
1214
2385
|
};
|
|
1215
2386
|
}
|
|
2387
|
+
|
|
2388
|
+
// src/transports/nats/jetstream-consumer.ts
|
|
2389
|
+
init_domain();
|
|
1216
2390
|
init_logging();
|
|
1217
2391
|
|
|
1218
2392
|
// src/transports/nats/jetstream-message-processor.ts
|
|
@@ -1494,6 +2668,9 @@ async function consumeJetStreamStreams(options) {
|
|
|
1494
2668
|
}
|
|
1495
2669
|
var ensureJetStreams = ensureJetStreamStreams;
|
|
1496
2670
|
var consumeJetStreams = consumeJetStreamStreams;
|
|
2671
|
+
|
|
2672
|
+
// src/transports/nats/nats-consumer.ts
|
|
2673
|
+
init_domain();
|
|
1497
2674
|
init_logging();
|
|
1498
2675
|
|
|
1499
2676
|
// src/transports/nats/nats-message-processor.ts
|
|
@@ -1586,7 +2763,9 @@ async function consumeNatsEvents(options) {
|
|
|
1586
2763
|
return sub;
|
|
1587
2764
|
}
|
|
1588
2765
|
|
|
1589
|
-
|
|
2766
|
+
// src/index.ts
|
|
2767
|
+
init_utils();
|
|
2768
|
+
|
|
1590
2769
|
exports.checkAndMarkProcessed = checkAndMarkProcessed;
|
|
1591
2770
|
exports.clearHandlerCache = clearHandlerCache;
|
|
1592
2771
|
exports.cloudEvents = cloudEvents;
|
|
@@ -1595,22 +2774,17 @@ exports.consumeJetStreamStreams = consumeJetStreamStreams;
|
|
|
1595
2774
|
exports.consumeJetStreams = consumeJetStreams;
|
|
1596
2775
|
exports.consumeNatsEvents = consumeNatsEvents;
|
|
1597
2776
|
exports.createContract = createContract;
|
|
2777
|
+
exports.createEvent = createEvent;
|
|
1598
2778
|
exports.createInMemoryIdempotencyStore = createInMemoryIdempotencyStore;
|
|
1599
|
-
exports.
|
|
1600
|
-
exports.deriveSubjectFromType = deriveSubjectFromType;
|
|
2779
|
+
exports.createPfPlugin = createPfPlugin;
|
|
1601
2780
|
exports.ensureJetStreamStream = ensureJetStreamStream;
|
|
1602
2781
|
exports.ensureJetStreamStreams = ensureJetStreamStreams;
|
|
1603
2782
|
exports.ensureJetStreams = ensureJetStreams;
|
|
1604
2783
|
exports.eventSchema = eventSchema;
|
|
1605
|
-
exports.extractTypeFromSchema = extractTypeFromSchema;
|
|
1606
2784
|
exports.getDefaultIdempotencyStore = getDefaultIdempotencyStore;
|
|
1607
2785
|
exports.handleEvent = handleEvent;
|
|
2786
|
+
exports.listEvents = listEvents;
|
|
1608
2787
|
exports.parseEventFromContext = parseEventFromContext;
|
|
1609
|
-
exports.pluralize = pluralize;
|
|
1610
|
-
exports.publish = publish;
|
|
1611
2788
|
exports.publishEvent = publishEvent;
|
|
1612
|
-
exports.publishNatsEvent = publishNatsEvent;
|
|
1613
|
-
exports.publishNatsRawEvent = publishNatsRawEvent;
|
|
1614
2789
|
exports.publishRawEvent = publishRawEvent;
|
|
1615
2790
|
exports.resetDefaultIdempotencyStore = resetDefaultIdempotencyStore;
|
|
1616
|
-
exports.singularize = singularize;
|