@classytic/arc 2.7.3 → 2.8.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.
Files changed (114) hide show
  1. package/README.md +2 -2
  2. package/dist/{HookSystem-D7lfx--K.mjs → HookSystem-BjFu7zf1.mjs} +3 -2
  3. package/dist/adapters/index.d.mts +2 -2
  4. package/dist/audit/index.d.mts +1 -1
  5. package/dist/audit/index.mjs +1 -1
  6. package/dist/audit/mongodb.d.mts +1 -1
  7. package/dist/audit/mongodb.mjs +1 -1
  8. package/dist/auth/index.d.mts +4 -4
  9. package/dist/auth/index.mjs +2 -2
  10. package/dist/auth/redis-session.d.mts +1 -1
  11. package/dist/{betterAuthOpenApi-CCw3YX0g.mjs → betterAuthOpenApi-CHCIuA-p.mjs} +1 -1
  12. package/dist/cache/index.d.mts +2 -2
  13. package/dist/cache/index.mjs +1 -1
  14. package/dist/cli/commands/docs.mjs +2 -2
  15. package/dist/cli/commands/generate.mjs +1 -1
  16. package/dist/cli/commands/introspect.mjs +1 -1
  17. package/dist/core/index.d.mts +2 -2
  18. package/dist/core/index.mjs +3 -2
  19. package/dist/core-BfrfxNqO.mjs +34 -0
  20. package/dist/{core-BWekSEju.mjs → createActionRouter-CbkIAaGh.mjs} +6 -36
  21. package/dist/{createApp-D7e77m8C.mjs → createApp-Cy8eUNKQ.mjs} +10 -10
  22. package/dist/{defineResource-DZzyl4a4.mjs → defineResource-CovBXvTB.mjs} +75 -9
  23. package/dist/docs/index.d.mts +2 -2
  24. package/dist/docs/index.mjs +1 -1
  25. package/dist/dynamic/index.d.mts +2 -2
  26. package/dist/dynamic/index.mjs +1 -1
  27. package/dist/{elevation-By_p2lnn.mjs → elevation-BBGFjzIP.mjs} +1 -1
  28. package/dist/{errorHandler-pCpEtNd7.d.mts → errorHandler-BeN-ERN7.d.mts} +1 -1
  29. package/dist/{eventPlugin-CdvUoUna.d.mts → eventPlugin-CAOWMQS8.d.mts} +1 -1
  30. package/dist/events/index.d.mts +3 -3
  31. package/dist/events/index.mjs +1 -1
  32. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  33. package/dist/events/transports/redis.d.mts +1 -1
  34. package/dist/factory/index.d.mts +1 -1
  35. package/dist/factory/index.mjs +7 -6
  36. package/dist/hooks/index.d.mts +1 -1
  37. package/dist/hooks/index.mjs +1 -1
  38. package/dist/idempotency/index.d.mts +3 -3
  39. package/dist/idempotency/mongodb.d.mts +1 -1
  40. package/dist/idempotency/mongodb.mjs +1 -3
  41. package/dist/idempotency/redis.d.mts +1 -1
  42. package/dist/{index-C9eYNjGR.d.mts → index-BpMhrFgn.d.mts} +1 -1
  43. package/dist/{index-B0extFr4.d.mts → index-CBru2y5Y.d.mts} +3 -3
  44. package/dist/{index-BjShrzoj.d.mts → index-qct60lnl.d.mts} +14 -14
  45. package/dist/index.d.mts +7 -7
  46. package/dist/index.mjs +4 -4
  47. package/dist/integrations/event-gateway.d.mts +1 -1
  48. package/dist/integrations/event-gateway.mjs +1 -1
  49. package/dist/integrations/index.d.mts +1 -1
  50. package/dist/integrations/mcp/index.d.mts +2 -2
  51. package/dist/integrations/mcp/index.mjs +1 -1
  52. package/dist/integrations/mcp/testing.d.mts +1 -1
  53. package/dist/integrations/mcp/testing.mjs +1 -1
  54. package/dist/integrations/streamline.d.mts +39 -7
  55. package/dist/integrations/streamline.mjs +106 -4
  56. package/dist/{interface-B91alUzq.d.mts → interface-IJqN3pXK.d.mts} +145 -4
  57. package/dist/{mongodb-Cgu9F1Nd.d.mts → mongodb-B1eVtFhw.d.mts} +1 -1
  58. package/dist/{mongodb-B7zupyck.d.mts → mongodb-NShVZDMr.d.mts} +1 -1
  59. package/dist/{openapi-BBSTVcMm.mjs → openapi-AYLVjqVe.mjs} +1 -1
  60. package/dist/org/index.d.mts +2 -2
  61. package/dist/permissions/index.d.mts +3 -3
  62. package/dist/plugins/index.d.mts +4 -4
  63. package/dist/plugins/index.mjs +8 -8
  64. package/dist/plugins/tracing-entry.d.mts +1 -1
  65. package/dist/plugins/tracing-entry.mjs +1 -1
  66. package/dist/policies/index.d.mts +1 -1
  67. package/dist/presets/index.d.mts +1 -1
  68. package/dist/presets/multiTenant.d.mts +1 -1
  69. package/dist/{queryCachePlugin-Ckl71mkc.d.mts → queryCachePlugin-BCFVXnxK.d.mts} +1 -1
  70. package/dist/{redis-3TQxm2VZ.d.mts → redis-Bunu3qWg.d.mts} +1 -1
  71. package/dist/{redis-stream-Dag5LFa9.d.mts → redis-stream-CF1lrKVk.d.mts} +1 -1
  72. package/dist/registry/index.d.mts +1 -1
  73. package/dist/registry/index.mjs +2 -2
  74. package/dist/{resourceToTools-BJkoQoUP.mjs → resourceToTools-C_1SMiCz.mjs} +1 -1
  75. package/dist/rpc/index.d.mts +1 -1
  76. package/dist/{schemaConverter-0TyONAwM.mjs → schemaConverter-Y5EejTnJ.mjs} +1 -4
  77. package/dist/scope/index.d.mts +2 -2
  78. package/dist/scope/index.mjs +1 -1
  79. package/dist/{sse-6W0hjVS_.mjs → sse-CD5Hghpu.mjs} +1 -1
  80. package/dist/testing/index.d.mts +2 -2
  81. package/dist/testing/index.mjs +1 -1
  82. package/dist/types/index.d.mts +5 -5
  83. package/dist/{types-B4BNthET.d.mts → types-BoaZHr-2.d.mts} +1 -1
  84. package/dist/{types-C5g2oRC7.d.mts → types-Ct0PUUSp.d.mts} +1 -1
  85. package/dist/{types-2FlNl0mL.d.mts → types-gUxAIZHp.d.mts} +13 -9
  86. package/dist/utils/index.d.mts +3 -3
  87. package/dist/utils/index.mjs +1 -1
  88. package/package.json +8 -7
  89. package/skills/arc/SKILL.md +23 -4
  90. package/skills/arc/references/integrations.md +1 -1
  91. package/skills/arc/references/mcp.md +2 -0
  92. /package/dist/{EventTransport-C4VheKeC.d.mts → EventTransport-n1KBxC_N.d.mts} +0 -0
  93. /package/dist/{ResourceRegistry-DsHiG9cL.mjs → ResourceRegistry-BOtJuRCs.mjs} +0 -0
  94. /package/dist/{caching-5DtLwIqb.mjs → caching-CHH-iHs3.mjs} +0 -0
  95. /package/dist/{circuitBreaker-BBPDt-J_.d.mts → circuitBreaker-BGVoB1hD.d.mts} +0 -0
  96. /package/dist/{elevation-D7WK0RXq.d.mts → elevation-UJO3-NvX.d.mts} +0 -0
  97. /package/dist/{errorHandler-CH8wk1eD.mjs → errorHandler-BW08lEiy.mjs} +0 -0
  98. /package/dist/{errors-BS6lZvWy.d.mts → errors-BI8kEKsO.d.mts} +0 -0
  99. /package/dist/{eventPlugin-B6U_nCFU.mjs → eventPlugin-x4jo3sG0.mjs} +0 -0
  100. /package/dist/{externalPaths-iba7jD3d.d.mts → externalPaths-BQ8QijNH.d.mts} +0 -0
  101. /package/dist/{fields-D4nMDqnK.d.mts → fields-DoeDgh2b.d.mts} +0 -0
  102. /package/dist/{interface-CSbZdv_3.d.mts → interface-CkkWm5uR.d.mts} +0 -0
  103. /package/dist/{interface-CG7oRZjX.d.mts → interface-bpoLKKqx.d.mts} +0 -0
  104. /package/dist/{logger-DLg8-Ueg.mjs → logger-CDjpjySd.mjs} +0 -0
  105. /package/dist/{metrics-Qnvwc-LQ.mjs → metrics-DuhiSEZI.mjs} +0 -0
  106. /package/dist/{mongodb-B7X7P1P8.mjs → mongodb-5Ff3w8jy.mjs} +0 -0
  107. /package/dist/{pluralize-Dckfq6US.mjs → pluralize-BneOJkpi.mjs} +0 -0
  108. /package/dist/{queryCachePlugin-CwTpR04-.mjs → queryCachePlugin-D0iIVhW_.mjs} +0 -0
  109. /package/dist/{registry-B3lRFBWo.mjs → registry-B0Wl7uVV.mjs} +0 -0
  110. /package/dist/{replyHelpers-uDUIYh7u.mjs → replyHelpers-CXtJDAZ0.mjs} +0 -0
  111. /package/dist/{sessionManager-CEo9jwPI.d.mts → sessionManager-BkzVU8h2.d.mts} +0 -0
  112. /package/dist/{tracing-DEqdGkr-.d.mts → tracing-xqXzWeaf.d.mts} +0 -0
  113. /package/dist/{types--D3vvfdt.d.mts → types-CN6JvmYz.d.mts} +0 -0
  114. /package/dist/{versioning-CdBbFefk.mjs → versioning-CPU_5Xfs.mjs} +0 -0
@@ -1,4 +1,4 @@
1
- import { a as edgePreset, c as testingPreset, i as developmentPreset, n as createApp, o as getPreset, s as productionPreset, t as ArcFactory } from "../createApp-D7e77m8C.mjs";
1
+ import { a as edgePreset, c as testingPreset, i as developmentPreset, n as createApp, o as getPreset, s as productionPreset, t as ArcFactory } from "../createApp-Cy8eUNKQ.mjs";
2
2
  import { readdir } from "node:fs/promises";
3
3
  import { dirname, join, resolve } from "node:path";
4
4
  import { fileURLToPath, pathToFileURL } from "node:url";
@@ -163,14 +163,15 @@ async function loadResources(dir, options = {}) {
163
163
  }
164
164
  resources.push(resource);
165
165
  }
166
- if (!silent) {
166
+ const log = silent ? void 0 : options?.logger;
167
+ if (log) {
167
168
  if (failed.length) {
168
- console.warn(`[arc] loadResources: ${failed.length} file(s) failed to import:`);
169
- for (const f of failed) console.warn(` - ${f}`);
169
+ log.warn(`[arc] loadResources: ${failed.length} file(s) failed to import:`);
170
+ for (const f of failed) log.warn(` - ${f}`);
170
171
  }
171
172
  if (skipped.length) {
172
- console.warn(`[arc] loadResources: ${skipped.length} file(s) skipped (no default export with toPlugin):`);
173
- for (const f of skipped) console.warn(` - ${f}`);
173
+ log.warn(`[arc] loadResources: ${skipped.length} file(s) skipped (no default export with toPlugin):`);
174
+ for (const f of skipped) log.warn(` - ${f}`);
174
175
  }
175
176
  }
176
177
  return resources;
@@ -1,2 +1,2 @@
1
- import { _n as defineHook, an as HookOperation, cn as HookSystem, dn as afterDelete, fn as afterUpdate, gn as createHookSystem, hn as beforeUpdate, in as HookHandler, ln as HookSystemOptions, mn as beforeDelete, nn as DefineHookOptions, on as HookPhase, pn as beforeCreate, rn as HookContext, sn as HookRegistration, un as afterCreate } from "../interface-B91alUzq.mjs";
1
+ import { Cn as defineHook, Sn as createHookSystem, _n as afterDelete, bn as beforeDelete, cn as DefineHookOptions, dn as HookOperation, fn as HookPhase, gn as afterCreate, hn as HookSystemOptions, ln as HookContext, mn as HookSystem, pn as HookRegistration, un as HookHandler, vn as afterUpdate, xn as beforeUpdate, yn as beforeCreate } from "../interface-IJqN3pXK.mjs";
2
2
  export { type DefineHookOptions, type HookContext, type HookHandler, type HookOperation, type HookPhase, type HookRegistration, HookSystem, type HookSystemOptions, afterCreate, afterDelete, afterUpdate, beforeCreate, beforeDelete, beforeUpdate, createHookSystem, defineHook };
@@ -1,2 +1,2 @@
1
- import { a as beforeCreate, c as createHookSystem, i as afterUpdate, l as defineHook, n as afterCreate, o as beforeDelete, r as afterDelete, s as beforeUpdate, t as HookSystem } from "../HookSystem-D7lfx--K.mjs";
1
+ import { a as beforeCreate, c as createHookSystem, i as afterUpdate, l as defineHook, n as afterCreate, o as beforeDelete, r as afterDelete, s as beforeUpdate, t as HookSystem } from "../HookSystem-BjFu7zf1.mjs";
2
2
  export { HookSystem, afterCreate, afterDelete, afterUpdate, beforeCreate, beforeDelete, beforeUpdate, createHookSystem, defineHook };
@@ -1,6 +1,6 @@
1
- import { i as createIdempotencyResult, n as IdempotencyResult, r as IdempotencyStore, t as IdempotencyLock } from "../interface-CSbZdv_3.mjs";
2
- import { n as MongoIdempotencyStoreOptions } from "../mongodb-B7zupyck.mjs";
3
- import { r as RedisIdempotencyStoreOptions, t as RedisClient } from "../redis-3TQxm2VZ.mjs";
1
+ import { i as createIdempotencyResult, n as IdempotencyResult, r as IdempotencyStore, t as IdempotencyLock } from "../interface-CkkWm5uR.mjs";
2
+ import { n as MongoIdempotencyStoreOptions } from "../mongodb-NShVZDMr.mjs";
3
+ import { r as RedisIdempotencyStoreOptions, t as RedisClient } from "../redis-Bunu3qWg.mjs";
4
4
  import { FastifyPluginAsync } from "fastify";
5
5
 
6
6
  //#region src/idempotency/idempotencyPlugin.d.ts
@@ -1,2 +1,2 @@
1
- import { n as MongoIdempotencyStoreOptions, t as MongoIdempotencyStore } from "../mongodb-B7zupyck.mjs";
1
+ import { n as MongoIdempotencyStoreOptions, t as MongoIdempotencyStore } from "../mongodb-NShVZDMr.mjs";
2
2
  export { MongoIdempotencyStore, type MongoIdempotencyStoreOptions };
@@ -9,9 +9,7 @@ var MongoIdempotencyStore = class {
9
9
  this.connection = options.connection;
10
10
  this.collectionName = options.collection ?? "arc_idempotency";
11
11
  this.ttlMs = options.ttlMs ?? 864e5;
12
- if (options.createIndex !== false) this.ensureIndex().catch((err) => {
13
- console.warn("[MongoIdempotencyStore] Failed to create index:", err);
14
- });
12
+ if (options.createIndex !== false) this.ensureIndex().catch(() => {});
15
13
  }
16
14
  get collection() {
17
15
  return this.connection.db.collection(this.collectionName);
@@ -1,2 +1,2 @@
1
- import { n as RedisIdempotencyStore, r as RedisIdempotencyStoreOptions, t as RedisClient } from "../redis-3TQxm2VZ.mjs";
1
+ import { n as RedisIdempotencyStore, r as RedisIdempotencyStoreOptions, t as RedisClient } from "../redis-Bunu3qWg.mjs";
2
2
  export { type RedisClient, RedisIdempotencyStore, type RedisIdempotencyStoreOptions };
@@ -1,4 +1,4 @@
1
- import { G as OpenApiSchemas, Q as QueryParserInterface, Ut as CrudRepository, c as ValidationResult, ft as RouteSchemaOptions, n as AdapterSchemaContext, o as RepositoryLike, q as ParsedQuery, r as DataAdapter, s as SchemaMetadata } from "./interface-B91alUzq.mjs";
1
+ import { Y as OpenApiSchemas, Yt as CrudRepository, Z as ParsedQuery, c as ValidationResult, n as AdapterSchemaContext, nt as QueryParserInterface, o as RepositoryLike, r as DataAdapter, s as SchemaMetadata, vt as RouteSchemaOptions } from "./interface-IJqN3pXK.mjs";
2
2
  import { Model } from "mongoose";
3
3
 
4
4
  //#region src/adapters/mongoose.d.ts
@@ -1,6 +1,6 @@
1
- import { r as RequestScope } from "./types--D3vvfdt.mjs";
2
- import { n as PermissionContext, r as PermissionResult, t as PermissionCheck } from "./types-B4BNthET.mjs";
3
- import { i as CacheStore, t as CacheLogger } from "./interface-CG7oRZjX.mjs";
1
+ import { r as RequestScope } from "./types-CN6JvmYz.mjs";
2
+ import { n as PermissionContext, r as PermissionResult, t as PermissionCheck } from "./types-BoaZHr-2.mjs";
3
+ import { i as CacheStore, t as CacheLogger } from "./interface-bpoLKKqx.mjs";
4
4
  import { FastifyRequest } from "fastify";
5
5
 
6
6
  //#region src/permissions/applyPermissionResult.d.ts
@@ -1,6 +1,6 @@
1
- import { r as RequestScope } from "./types--D3vvfdt.mjs";
2
- import { C as CrudRouterOptions, Ft as IController, It as IControllerResponse, Lt as IRequestContext, Vt as ResourceDefinition, it as RequestWithExtras, k as FastifyWithDecorators, nt as RequestContext, ot as ResourceConfig, u as AnyRecord, x as CrudController } from "./interface-B91alUzq.mjs";
3
- import { t as PermissionCheck } from "./types-B4BNthET.mjs";
1
+ import { r as RequestScope } from "./types-CN6JvmYz.mjs";
2
+ import { D as CrudRouterOptions, Ht as IControllerResponse, N as FastifyWithDecorators, T as CrudController, Ut as IRequestContext, Vt as IController, ct as RequestWithExtras, m as AnyRecord, ot as RequestContext, qt as ResourceDefinition, ut as ResourceConfig } from "./interface-IJqN3pXK.mjs";
3
+ import { t as PermissionCheck } from "./types-BoaZHr-2.mjs";
4
4
  import { FastifyInstance, FastifyReply, FastifyRequest, RouteHandlerMethod } from "fastify";
5
5
 
6
6
  //#region src/constants.d.ts
@@ -60,7 +60,7 @@ declare const RESERVED_QUERY_PARAMS: Readonly<Set<string>>;
60
60
  * @param req - Full Fastify request object
61
61
  * @returns Action result (will be wrapped in success response)
62
62
  */
63
- type ActionHandler<TData = any, TResult = any> = (id: string, data: TData, req: RequestWithExtras) => Promise<TResult>;
63
+ type ActionHandler<TData = Record<string, unknown>, TResult = unknown> = (id: string, data: TData, req: RequestWithExtras) => Promise<TResult>;
64
64
  /**
65
65
  * Action router configuration
66
66
  */
@@ -68,31 +68,31 @@ interface ActionRouterConfig {
68
68
  /**
69
69
  * OpenAPI tag for grouping routes
70
70
  */
71
- tag?: string;
71
+ readonly tag?: string;
72
72
  /**
73
73
  * Action handlers map
74
74
  * @example { approve: (id, data, req) => service.approve(id), ... }
75
75
  */
76
- actions: Record<string, ActionHandler>;
76
+ readonly actions: Record<string, ActionHandler>;
77
77
  /**
78
78
  * Per-action permission checks (PermissionCheck functions)
79
79
  * @example { approve: requireRoles(['admin', 'manager']), cancel: requireRoles(['admin']) }
80
80
  */
81
- actionPermissions?: Record<string, PermissionCheck>;
81
+ readonly actionPermissions?: Record<string, PermissionCheck>;
82
82
  /**
83
83
  * Per-action JSON schema for body validation
84
84
  * @example { dispatch: { transport: { type: 'object' } } }
85
85
  */
86
- actionSchemas?: Record<string, Record<string, any>>;
86
+ readonly actionSchemas?: Record<string, Record<string, unknown>>;
87
87
  /**
88
88
  * Global permission check applied to all actions (if action-specific not defined)
89
89
  */
90
- globalAuth?: PermissionCheck;
90
+ readonly globalAuth?: PermissionCheck;
91
91
  /**
92
92
  * Optional idempotency service
93
93
  * If provided, will handle idempotency-key header
94
94
  */
95
- idempotencyService?: IdempotencyService;
95
+ readonly idempotencyService?: IdempotencyService;
96
96
  /**
97
97
  * Custom error handler for action execution failures
98
98
  * @param error - The error thrown by action handler
@@ -100,7 +100,7 @@ interface ActionRouterConfig {
100
100
  * @param id - The resource ID
101
101
  * @returns Status code and error response
102
102
  */
103
- onError?: (error: Error, action: string, id: string) => {
103
+ readonly onError?: (error: Error, action: string, id: string) => {
104
104
  statusCode: number;
105
105
  error: string;
106
106
  code?: string;
@@ -111,11 +111,11 @@ interface ActionRouterConfig {
111
111
  * Apps can provide their own implementation
112
112
  */
113
113
  interface IdempotencyService {
114
- check(key: string, payload: any): Promise<{
114
+ check(key: string, payload: unknown): Promise<{
115
115
  isNew: boolean;
116
- existingResult?: any;
116
+ existingResult?: unknown;
117
117
  }>;
118
- complete(key: string | undefined, result: any): Promise<void>;
118
+ complete(key: string | undefined, result: unknown): Promise<void>;
119
119
  fail(key: string | undefined, error: Error): Promise<void>;
120
120
  }
121
121
  /**
package/dist/index.d.mts CHANGED
@@ -1,10 +1,10 @@
1
- import { $ as RateLimitConfig, $t as PipelineContext, A as FieldRule, C as CrudRouterOptions, D as FastifyRequestExtras, F as InferDocType, Ft as IController, Gt as PaginatedResult, H as MiddlewareConfig, Ht as defineResource, I as InferResourceDoc, It as IControllerResponse, Jt as Guard, K as OwnershipCheck, L as IntrospectionData, Lt as IRequestContext, M as HealthCheck, N as HealthOptions, Nt as ControllerLike, O as FastifyWithAuth, P as InferAdapterDoc, Qt as PipelineConfig, R as IntrospectionPluginOptions, Rt as RouteHandler, S as CrudRouteKey, St as envelope, T as EventDefinition, Tt as BaseControllerOptions, U as MiddlewareHandler, Ut as CrudRepository, Vt as ResourceDefinition, Xt as NextFunction, Y as PresetFunction, Yt as Interceptor, Z as PresetResult, Zt as OperationFilter, _t as TypedResourceConfig, a as RelationMetadata, bt as ValidateOptions, c as ValidationResult, d as ApiResponse, dt as RouteHandlerMethod, en as PipelineStep, et as RegistryEntry, ft as RouteSchemaOptions, g as AuthPluginOptions, gt as TypedRepository, ht as TypedController, i as FieldMetadata, it as RequestWithExtras, j as GracefulShutdownOptions, k as FastifyWithDecorators, l as AdditionalRoute, lt as ResourceMetadata, m as ArcRequest, nt as RequestContext, o as RepositoryLike, ot as ResourceConfig, p as ArcInternalMetadata, pt as ServiceContext, qt as QueryOptions, r as DataAdapter, rt as RequestIdOptions, s as SchemaMetadata, tn as Transform, tt as RegistryStats, u as AnyRecord, w as CrudSchemas, wt as BaseController, x as CrudController, xt as ValidationResult$1, y as ConfigError, yt as UserOrganization, z as JWTPayload } from "./interface-B91alUzq.mjs";
2
- import { a as applyFieldWritePermissions, i as applyFieldReadPermissions, n as FieldPermissionMap, o as fields, t as FieldPermission } from "./fields-D4nMDqnK.mjs";
3
- import { i as UserBase, n as PermissionContext, r as PermissionResult, t as PermissionCheck } from "./types-B4BNthET.mjs";
4
- import { l as createMongooseAdapter, o as createPrismaAdapter, s as MongooseAdapter, t as PrismaAdapter } from "./index-C9eYNjGR.mjs";
5
- import { A as MutationOperation, C as HOOK_PHASES, D as MAX_REGEX_LENGTH, E as MAX_FILTER_DEPTH, M as SYSTEM_FIELDS, O as MAX_SEARCH_LENGTH, S as HOOK_OPERATIONS, T as HookPhase, _ as DEFAULT_LIMIT, a as getControllerScope, b as DEFAULT_TENANT_FIELD, g as DEFAULT_ID_FIELD, h as CrudOperation, j as RESERVED_QUERY_PARAMS, k as MUTATION_OPERATIONS, m as CRUD_OPERATIONS, s as defineResourceVariants, v as DEFAULT_MAX_LIMIT, w as HookOperation, x as DEFAULT_UPDATE_METHOD, y as DEFAULT_SORT } from "./index-BjShrzoj.mjs";
6
- import { C as authenticated, D as publicRead, E as presets_d_exports, O as publicReadAdminWrite, S as adminOnly, T as ownerWithAdminBypass, _ as requireScopeContext, a as allOf, c as createDynamicPermissionMatrix, d as requireAuth, f as requireOrgInScope, g as requireRoles, h as requireOwnership, k as readOnly, l as createOrgPermissions, m as requireOrgRole, n as DynamicPermissionMatrix, o as allowPublic, p as requireOrgMembership, r as DynamicPermissionMatrixConfig, s as anyOf, u as denyAll, v as requireServiceScope, w as fullPublic, x as when, y as requireTeamMembership } from "./index-B0extFr4.mjs";
7
- import { a as NotFoundError, d as ValidationError, f as createDomainError, i as ForbiddenError, t as ArcError, u as UnauthorizedError } from "./errors-BS6lZvWy.mjs";
1
+ import { $ as PresetFunction, $t as QueryOptions, At as BaseController, B as InferResourceDoc, C as ConfigError, Ct as TypedResourceConfig, D as CrudRouterOptions, Dt as ValidationResult$1, E as CrudRouteKey, Et as ValidateOptions, F as GracefulShutdownOptions, H as IntrospectionPluginOptions, Ht as IControllerResponse, I as HealthCheck, Jt as defineResource, K as MiddlewareConfig, L as HealthOptions, M as FastifyWithAuth, N as FastifyWithDecorators, O as CrudSchemas, Ot as envelope, P as FieldRule, R as InferAdapterDoc, St as TypedRepository, T as CrudController, Tt as UserOrganization, U as JWTPayload, Ut as IRequestContext, V as IntrospectionData, Vt as IController, Wt as RouteHandler, X as OwnershipCheck, Yt as CrudRepository, Zt as PaginatedResult, _ as ArcInternalMetadata, a as RelationMetadata, an as PipelineContext, at as RegistryStats, b as AuthPluginOptions, c as ValidationResult, ct as RequestWithExtras, en as Guard, gt as RouteHandlerMethod, h as ApiResponse, i as FieldMetadata, in as PipelineConfig, it as RegistryEntry, j as FastifyRequestExtras, jt as BaseControllerOptions, k as EventDefinition, m as AnyRecord, nn as NextFunction, o as RepositoryLike, on as PipelineStep, ot as RequestContext, p as AdditionalRoute, pt as ResourceMetadata, q as MiddlewareHandler, qt as ResourceDefinition, r as DataAdapter, rn as OperationFilter, rt as RateLimitConfig, s as SchemaMetadata, sn as Transform, st as RequestIdOptions, tn as Interceptor, tt as PresetResult, ut as ResourceConfig, v as ArcRequest, vt as RouteSchemaOptions, xt as TypedController, yt as ServiceContext, z as InferDocType, zt as ControllerLike } from "./interface-IJqN3pXK.mjs";
2
+ import { a as applyFieldWritePermissions, i as applyFieldReadPermissions, n as FieldPermissionMap, o as fields, t as FieldPermission } from "./fields-DoeDgh2b.mjs";
3
+ import { i as UserBase, n as PermissionContext, r as PermissionResult, t as PermissionCheck } from "./types-BoaZHr-2.mjs";
4
+ import { l as createMongooseAdapter, o as createPrismaAdapter, s as MongooseAdapter, t as PrismaAdapter } from "./index-BpMhrFgn.mjs";
5
+ import { A as MutationOperation, C as HOOK_PHASES, D as MAX_REGEX_LENGTH, E as MAX_FILTER_DEPTH, M as SYSTEM_FIELDS, O as MAX_SEARCH_LENGTH, S as HOOK_OPERATIONS, T as HookPhase, _ as DEFAULT_LIMIT, a as getControllerScope, b as DEFAULT_TENANT_FIELD, g as DEFAULT_ID_FIELD, h as CrudOperation, j as RESERVED_QUERY_PARAMS, k as MUTATION_OPERATIONS, m as CRUD_OPERATIONS, s as defineResourceVariants, v as DEFAULT_MAX_LIMIT, w as HookOperation, x as DEFAULT_UPDATE_METHOD, y as DEFAULT_SORT } from "./index-qct60lnl.mjs";
6
+ import { C as authenticated, D as publicRead, E as presets_d_exports, O as publicReadAdminWrite, S as adminOnly, T as ownerWithAdminBypass, _ as requireScopeContext, a as allOf, c as createDynamicPermissionMatrix, d as requireAuth, f as requireOrgInScope, g as requireRoles, h as requireOwnership, k as readOnly, l as createOrgPermissions, m as requireOrgRole, n as DynamicPermissionMatrix, o as allowPublic, p as requireOrgMembership, r as DynamicPermissionMatrixConfig, s as anyOf, u as denyAll, v as requireServiceScope, w as fullPublic, x as when, y as requireTeamMembership } from "./index-CBru2y5Y.mjs";
7
+ import { a as NotFoundError, d as ValidationError, f as createDomainError, i as ForbiddenError, t as ArcError, u as UnauthorizedError } from "./errors-BI8kEKsO.mjs";
8
8
  import { AsyncLocalStorage } from "node:async_hooks";
9
9
  import { RouteHandlerMethod as RouteHandlerMethod$1 } from "fastify";
10
10
 
package/dist/index.mjs CHANGED
@@ -3,12 +3,12 @@ import { a as createMongooseAdapter, i as MongooseAdapter, r as createPrismaAdap
3
3
  import { t as BaseController } from "./BaseController-CpMfCXdn.mjs";
4
4
  import { envelope } from "./types/index.mjs";
5
5
  import { n as applyFieldWritePermissions, r as fields, t as applyFieldReadPermissions } from "./fields-ipsbIRPK.mjs";
6
- import { t as defineResourceVariants } from "./core-BWekSEju.mjs";
7
6
  import { t as requestContext } from "./requestContext-xHIKedG6.mjs";
8
7
  import { d as createDomainError, i as NotFoundError, l as UnauthorizedError, r as ForbiddenError, t as ArcError, u as ValidationError } from "./errors-Cg58SLNi.mjs";
9
- import { a as validateResourceConfig, f as getControllerScope, i as formatValidationErrors, m as pipe, n as defineResource, r as assertValidConfig, t as ResourceDefinition } from "./defineResource-DZzyl4a4.mjs";
8
+ import { a as validateResourceConfig, f as getControllerScope, i as formatValidationErrors, m as pipe, n as defineResource, r as assertValidConfig, t as ResourceDefinition } from "./defineResource-CovBXvTB.mjs";
10
9
  import { C as publicRead, S as presets_exports, T as readOnly, _ as when, a as createOrgPermissions, b as fullPublic, c as requireOrgInScope, d as requireOwnership, f as requireRoles, h as requireTeamMembership, i as createDynamicPermissionMatrix, l as requireOrgMembership, m as requireServiceScope, n as allowPublic, o as denyAll, p as requireScopeContext, r as anyOf, s as requireAuth, t as allOf, u as requireOrgRole, v as adminOnly, w as publicReadAdminWrite, x as ownerWithAdminBypass, y as authenticated } from "./permissions-CH4cNwJi.mjs";
11
- import { n as configureArcLogger, t as arcLog } from "./logger-DLg8-Ueg.mjs";
10
+ import { t as defineResourceVariants } from "./core-BfrfxNqO.mjs";
11
+ import { n as configureArcLogger, t as arcLog } from "./logger-CDjpjySd.mjs";
12
12
  //#region src/middleware/middleware.ts
13
13
  /**
14
14
  * Named Middleware — Priority-based, conditional middleware execution.
@@ -128,6 +128,6 @@ function transform(name, handlerOrOptions) {
128
128
  }
129
129
  //#endregion
130
130
  //#region src/index.ts
131
- const version = "2.7.3";
131
+ const version = "2.8.0";
132
132
  //#endregion
133
133
  export { ArcError, BaseController, CRUD_OPERATIONS, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, ForbiddenError, HOOK_OPERATIONS, HOOK_PHASES, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, MongooseAdapter, NotFoundError, PrismaAdapter, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, UnauthorizedError, ValidationError, adminOnly, allOf, allowPublic, anyOf, applyFieldReadPermissions, applyFieldWritePermissions, arcLog, assertValidConfig, authenticated, configureArcLogger, createDomainError, createDynamicPermissionMatrix, createMongooseAdapter, createOrgPermissions, createPrismaAdapter, defineResource, defineResourceVariants, denyAll, envelope, fields, formatValidationErrors, fullPublic, getControllerScope, guard, intercept, middleware, ownerWithAdminBypass, presets_exports as permissions, pipe, publicRead, publicReadAdminWrite, readOnly, requestContext, requireAuth, requireOrgInScope, requireOrgMembership, requireOrgRole, requireOwnership, requireRoles, requireScopeContext, requireServiceScope, requireTeamMembership, sortMiddlewares, transform, validateResourceConfig, version, when };
@@ -1,4 +1,4 @@
1
- import { t as DomainEvent } from "../EventTransport-C4VheKeC.mjs";
1
+ import { t as DomainEvent } from "../EventTransport-n1KBxC_N.mjs";
2
2
  import { WebSocketClient, WebSocketMessage } from "./websocket.mjs";
3
3
  import { FastifyPluginAsync, FastifyRequest } from "fastify";
4
4
 
@@ -4,7 +4,7 @@ const eventGatewayPluginImpl = async (fastify, opts = {}) => {
4
4
  const { auth = true, orgScoped = false, roomPolicy, maxMessageBytes, maxSubscriptionsPerClient, authenticate } = opts;
5
5
  if (auth && !authenticate && !fastify.hasDecorator("authenticate")) throw new Error("[arc-event-gateway] auth is true but fastify.authenticate is not registered. Register an auth plugin first, provide a custom authenticate function, or set auth: false.");
6
6
  if (opts.sse !== false) {
7
- const { default: ssePlugin } = await import("../sse-6W0hjVS_.mjs").then((n) => n.r);
7
+ const { default: ssePlugin } = await import("../sse-CD5Hghpu.mjs").then((n) => n.r);
8
8
  await fastify.register(ssePlugin, {
9
9
  path: opts.sse?.path ?? "/events/stream",
10
10
  requireAuth: auth,
@@ -1,7 +1,7 @@
1
1
  import { WebSocketClient, WebSocketMessage, WebSocketPluginOptions } from "./websocket.mjs";
2
2
  import { EventGatewayOptions } from "./event-gateway.mjs";
3
3
  import { JobDefinition, JobDispatchOptions, JobDispatcher, JobMeta, JobsPluginOptions, QueueStats } from "./jobs.mjs";
4
- import { c as McpResourceConfig, f as ToolAnnotations, i as CrudOperation, l as PromptDefinition, m as ToolDefinition, n as CallToolResult, o as McpAuthResult, p as ToolContext, r as CreateMcpServerConfig, s as McpPluginOptions, t as BetterAuthHandler } from "../types-C5g2oRC7.mjs";
4
+ import { c as McpResourceConfig, f as ToolAnnotations, i as CrudOperation, l as PromptDefinition, m as ToolDefinition, n as CallToolResult, o as McpAuthResult, p as ToolContext, r as CreateMcpServerConfig, s as McpPluginOptions, t as BetterAuthHandler } from "../types-Ct0PUUSp.mjs";
5
5
  import { StreamlinePluginOptions, WorkflowLike, WorkflowRunLike } from "./streamline.mjs";
6
6
  import { WebhookDeliveryRecord, WebhookManager, WebhookPluginOptions, WebhookStore, WebhookSubscription } from "./webhooks.mjs";
7
7
  export { type BetterAuthHandler, type CallToolResult, type CreateMcpServerConfig, type CrudOperation, type EventGatewayOptions, type JobDefinition, type JobDispatchOptions, type JobDispatcher, type JobMeta, type JobsPluginOptions, type McpAuthResult, type McpPluginOptions, type McpResourceConfig, type PromptDefinition, type QueueStats, type StreamlinePluginOptions, type ToolAnnotations, type ToolContext, type ToolDefinition, type WebSocketClient, type WebSocketMessage, type WebSocketPluginOptions, type WebhookDeliveryRecord, type WebhookManager, type WebhookPluginOptions, type WebhookStore, type WebhookSubscription, type WorkflowLike, type WorkflowRunLike };
@@ -1,5 +1,5 @@
1
- import { Vt as ResourceDefinition } from "../../interface-B91alUzq.mjs";
2
- import { a as McpAuthResolver, c as McpResourceConfig, d as SessionEntry, f as ToolAnnotations, i as CrudOperation, l as PromptDefinition, m as ToolDefinition, n as CallToolResult, o as McpAuthResult, p as ToolContext, r as CreateMcpServerConfig, s as McpPluginOptions, t as BetterAuthHandler, u as PromptResult } from "../../types-C5g2oRC7.mjs";
1
+ import { qt as ResourceDefinition } from "../../interface-IJqN3pXK.mjs";
2
+ import { a as McpAuthResolver, c as McpResourceConfig, d as SessionEntry, f as ToolAnnotations, i as CrudOperation, l as PromptDefinition, m as ToolDefinition, n as CallToolResult, o as McpAuthResult, p as ToolContext, r as CreateMcpServerConfig, s as McpPluginOptions, t as BetterAuthHandler, u as PromptResult } from "../../types-Ct0PUUSp.mjs";
3
3
  import { FastifyPluginAsync } from "fastify";
4
4
  import { z } from "zod";
5
5
 
@@ -1,4 +1,4 @@
1
- import { n as fieldRulesToZod, r as createMcpServer, t as resourceToTools } from "../../resourceToTools-BJkoQoUP.mjs";
1
+ import { n as fieldRulesToZod, r as createMcpServer, t as resourceToTools } from "../../resourceToTools-C_1SMiCz.mjs";
2
2
  import { createHash } from "node:crypto";
3
3
  import fp from "fastify-plugin";
4
4
  //#region src/integrations/mcp/definePrompt.ts
@@ -1,4 +1,4 @@
1
- import { o as McpAuthResult, s as McpPluginOptions } from "../../types-C5g2oRC7.mjs";
1
+ import { o as McpAuthResult, s as McpPluginOptions } from "../../types-Ct0PUUSp.mjs";
2
2
 
3
3
  //#region src/integrations/mcp/testing.d.ts
4
4
  interface TestMcpClientOptions {
@@ -1,4 +1,4 @@
1
- import { r as createMcpServer, t as resourceToTools } from "../../resourceToTools-BJkoQoUP.mjs";
1
+ import { r as createMcpServer, t as resourceToTools } from "../../resourceToTools-C_1SMiCz.mjs";
2
2
  //#region src/integrations/mcp/testing.ts
3
3
  /**
4
4
  * @classytic/arc/mcp/testing — MCP Test Utilities
@@ -1,28 +1,44 @@
1
1
  import { FastifyPluginAsync } from "fastify";
2
2
 
3
3
  //#region src/integrations/streamline.d.ts
4
+ /** Start options — matches @classytic/streamline v2.1 StartOptions */
5
+ interface WorkflowStartOptions {
6
+ meta?: Record<string, unknown>;
7
+ idempotencyKey?: string;
8
+ priority?: number;
9
+ }
4
10
  /** Minimal workflow interface — matches @classytic/streamline's createWorkflow() return */
5
11
  interface WorkflowLike {
6
12
  definition: {
7
13
  id: string;
8
14
  name?: string;
9
- steps: Record<string, unknown>;
15
+ steps: Record<string, unknown> | unknown[];
10
16
  };
11
17
  engine: {
12
- start(input: unknown, meta?: unknown): Promise<WorkflowRunLike>;
18
+ start(input: unknown, options?: WorkflowStartOptions): Promise<WorkflowRunLike>;
13
19
  execute(runId: string): Promise<WorkflowRunLike>;
14
20
  resume(runId: string, payload?: unknown): Promise<WorkflowRunLike>;
15
21
  cancel(runId: string): Promise<WorkflowRunLike>;
16
22
  pause?(runId: string): Promise<WorkflowRunLike>;
17
23
  rewindTo?(runId: string, stepId: string): Promise<WorkflowRunLike>;
18
24
  get(runId: string): Promise<WorkflowRunLike | null>;
25
+ waitFor?(runId: string, options?: {
26
+ timeout?: number;
27
+ }): Promise<WorkflowRunLike>;
19
28
  shutdown?(): void;
20
29
  };
21
- start(input: unknown, meta?: unknown): Promise<WorkflowRunLike>;
30
+ start(input: unknown, options?: WorkflowStartOptions): Promise<WorkflowRunLike>;
22
31
  resume(runId: string, payload?: unknown): Promise<WorkflowRunLike>;
23
32
  cancel(runId: string): Promise<WorkflowRunLike>;
24
33
  get(runId: string): Promise<WorkflowRunLike | null>;
25
34
  shutdown?(): void;
35
+ /** Streamline container for event bridging (streamline >=2.1) */
36
+ container?: {
37
+ eventBus: {
38
+ on(event: string, listener: (...args: unknown[]) => void): void;
39
+ off(event: string, listener: (...args: unknown[]) => void): void;
40
+ };
41
+ };
26
42
  }
27
43
  interface WorkflowRunLike {
28
44
  _id: string;
@@ -30,11 +46,14 @@ interface WorkflowRunLike {
30
46
  status: string;
31
47
  context?: unknown;
32
48
  input?: unknown;
33
- steps?: Record<string, unknown>;
49
+ steps?: unknown[];
34
50
  error?: unknown;
51
+ idempotencyKey?: string;
52
+ priority?: number;
53
+ concurrencyKey?: string;
54
+ stepLogs?: unknown[];
35
55
  createdAt?: Date;
36
56
  updatedAt?: Date;
37
- [key: string]: unknown;
38
57
  }
39
58
  interface StreamlinePluginOptions {
40
59
  /** Array of workflows created with createWorkflow() */
@@ -43,8 +62,21 @@ interface StreamlinePluginOptions {
43
62
  prefix?: string;
44
63
  /** Require authentication for all workflow endpoints (default: true) */
45
64
  auth?: boolean;
46
- /** Connect workflow events to Arc's event bus (default: true) */
65
+ /** Connect workflow lifecycle events to Arc's event bus (default: true) */
47
66
  bridgeEvents?: boolean;
67
+ /**
68
+ * Bridge step-level events (step:started, step:completed, step:failed) to Arc's event bus.
69
+ * Disabled by default — enable for dashboards or monitoring.
70
+ * Requires the workflow to expose `container.eventBus` (streamline >=2.1).
71
+ * @default false
72
+ */
73
+ bridgeStepEvents?: boolean;
74
+ /**
75
+ * Enable SSE streaming endpoint: GET /:workflowId/runs/:runId/stream
76
+ * Streams step-level events as Server-Sent Events for live UI updates.
77
+ * @default false
78
+ */
79
+ enableStreaming?: boolean;
48
80
  /** Custom permission check for workflow operations */
49
81
  permissions?: {
50
82
  start?: (request: unknown) => boolean | Promise<boolean>;
@@ -57,4 +89,4 @@ interface StreamlinePluginOptions {
57
89
  /** Pluggable streamline integration for Arc */
58
90
  declare const streamlinePlugin: FastifyPluginAsync<StreamlinePluginOptions>;
59
91
  //#endregion
60
- export { StreamlinePluginOptions, WorkflowLike, WorkflowRunLike, streamlinePlugin };
92
+ export { StreamlinePluginOptions, WorkflowLike, WorkflowRunLike, WorkflowStartOptions, streamlinePlugin };
@@ -1,6 +1,6 @@
1
1
  //#region src/integrations/streamline.ts
2
2
  const streamlinePluginImpl = async (fastify, options) => {
3
- const { workflows, prefix = "/workflows", auth = true, bridgeEvents = true, permissions: perms } = options;
3
+ const { workflows, prefix = "/workflows", auth = true, bridgeEvents = true, bridgeStepEvents = false, enableStreaming = false, permissions: perms } = options;
4
4
  const registry = /* @__PURE__ */ new Map();
5
5
  for (const wf of workflows) {
6
6
  const id = wf.definition.id;
@@ -22,8 +22,12 @@ const streamlinePluginImpl = async (fastify, options) => {
22
22
  success: false,
23
23
  error: "Forbidden"
24
24
  });
25
- const { input, meta } = request.body ?? {};
26
- const run = await wf.start(input, meta);
25
+ const { input, meta, idempotencyKey, priority } = request.body ?? {};
26
+ const run = await wf.start(input, {
27
+ meta,
28
+ idempotencyKey,
29
+ priority
30
+ });
27
31
  if (bridgeEvents && fastify.events?.publish) try {
28
32
  await fastify.events.publish(`workflow.${id}.started`, {
29
33
  runId: run._id,
@@ -105,6 +109,26 @@ const streamlinePluginImpl = async (fastify, options) => {
105
109
  data: run
106
110
  };
107
111
  });
112
+ fastify.post(`${routePrefix}/runs/:runId/execute`, { preHandler: authPreHandler }, async (request, _reply) => {
113
+ const { runId } = request.params;
114
+ return {
115
+ success: true,
116
+ data: await wf.engine.execute(runId)
117
+ };
118
+ });
119
+ if (wf.engine.waitFor) fastify.get(`${routePrefix}/runs/:runId/wait`, { preHandler: authPreHandler }, async (request, reply) => {
120
+ if (!await checkPerm("get", request)) return reply.status(403).send({
121
+ success: false,
122
+ error: "Forbidden"
123
+ });
124
+ const { runId } = request.params;
125
+ const { timeout } = request.query ?? {};
126
+ const timeoutMs = timeout ? Number.parseInt(timeout, 10) : 3e4;
127
+ return {
128
+ success: true,
129
+ data: await wf.engine.waitFor(runId, { timeout: Math.min(timeoutMs, 12e4) })
130
+ };
131
+ });
108
132
  if (wf.engine.pause) fastify.post(`${routePrefix}/runs/:runId/pause`, { preHandler: authPreHandler }, async (request, _reply) => {
109
133
  const { runId } = request.params;
110
134
  return {
@@ -124,6 +148,84 @@ const streamlinePluginImpl = async (fastify, options) => {
124
148
  data: await wf.engine.rewindTo?.(runId, stepId)
125
149
  };
126
150
  });
151
+ if (bridgeStepEvents && wf.container?.eventBus && fastify.events?.publish) for (const eventName of [
152
+ "step:started",
153
+ "step:completed",
154
+ "step:failed",
155
+ "step:skipped",
156
+ "step:retry-scheduled"
157
+ ]) wf.container.eventBus.on(eventName, (payload) => {
158
+ const p = payload;
159
+ fastify.events.publish(`workflow.${id}.${eventName}`, {
160
+ runId: p?.runId,
161
+ stepId: p?.stepId,
162
+ workflowId: id,
163
+ ...p
164
+ }).catch((err) => {
165
+ fastify.log.warn({
166
+ err,
167
+ workflowId: id
168
+ }, `Failed to bridge ${eventName}`);
169
+ });
170
+ });
171
+ if (enableStreaming && wf.container?.eventBus) fastify.get(`${routePrefix}/runs/:runId/stream`, { preHandler: authPreHandler }, async (request, reply) => {
172
+ if (!await checkPerm("get", request)) return reply.status(403).send({
173
+ success: false,
174
+ error: "Forbidden"
175
+ });
176
+ const { runId } = request.params;
177
+ if (!await wf.get(runId)) return reply.status(404).send({
178
+ success: false,
179
+ error: "Workflow run not found"
180
+ });
181
+ reply.raw.writeHead(200, {
182
+ "Content-Type": "text/event-stream",
183
+ "Cache-Control": "no-cache",
184
+ Connection: "keep-alive"
185
+ });
186
+ const events = [
187
+ "step:started",
188
+ "step:completed",
189
+ "step:failed",
190
+ "step:skipped",
191
+ "workflow:completed",
192
+ "workflow:failed",
193
+ "workflow:cancelled"
194
+ ];
195
+ const listeners = [];
196
+ let closed = false;
197
+ const send = (event, data) => {
198
+ if (closed) return;
199
+ try {
200
+ reply.raw.write(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`);
201
+ } catch {
202
+ cleanup();
203
+ }
204
+ };
205
+ const cleanup = () => {
206
+ if (closed) return;
207
+ closed = true;
208
+ for (const { event, fn } of listeners) wf.container?.eventBus.off(event, fn);
209
+ listeners.length = 0;
210
+ try {
211
+ reply.raw.end();
212
+ } catch {}
213
+ };
214
+ for (const eventName of events) {
215
+ const fn = (payload) => {
216
+ const p = payload;
217
+ if (p?.runId !== runId) return;
218
+ send(eventName, p);
219
+ if (eventName === "workflow:completed" || eventName === "workflow:failed" || eventName === "workflow:cancelled") cleanup();
220
+ };
221
+ wf.container.eventBus.on(eventName, fn);
222
+ listeners.push({
223
+ event: eventName,
224
+ fn
225
+ });
226
+ }
227
+ request.raw.on("close", cleanup);
228
+ });
127
229
  }
128
230
  fastify.get(prefix, { preHandler: authPreHandler }, async () => {
129
231
  return {
@@ -131,7 +233,7 @@ const streamlinePluginImpl = async (fastify, options) => {
131
233
  data: Array.from(registry.entries()).map(([id, wf]) => ({
132
234
  id,
133
235
  name: wf.definition.name ?? id,
134
- steps: Object.keys(wf.definition.steps)
236
+ steps: Array.isArray(wf.definition.steps) ? wf.definition.steps.map((s) => s.id ?? String(s)) : Object.keys(wf.definition.steps)
135
237
  }))
136
238
  };
137
239
  });