@kinotic-ai/core 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -68,6 +68,7 @@ __export(exports_src, {
68
68
  Version: () => Version,
69
69
  TextEventFactory: () => TextEventFactory,
70
70
  Sort: () => Sort,
71
+ SessionKeepAliveMode: () => SessionKeepAliveMode,
71
72
  ServiceRegistry: () => ServiceRegistry,
72
73
  ServerInfo: () => ServerInfo,
73
74
  Scope: () => Scope,
@@ -79,7 +80,6 @@ __export(exports_src, {
79
80
  OffsetPageable: () => OffsetPageable,
80
81
  NullHandling: () => NullHandling,
81
82
  KinoticSingleton: () => KinoticSingleton,
82
- KinoticProjectConfig: () => KinoticProjectConfig,
83
83
  KinoticError: () => KinoticError,
84
84
  Kinotic: () => Kinotic,
85
85
  JsonEventFactory: () => JsonEventFactory,
@@ -96,7 +96,6 @@ __export(exports_src, {
96
96
  Context: () => Context,
97
97
  ConnectionInfo: () => ConnectionInfo,
98
98
  ConnectedInfo: () => ConnectedInfo,
99
- ConnectHeaders: () => ConnectHeaders,
100
99
  CONTEXT_METADATA_KEY: () => CONTEXT_METADATA_KEY,
101
100
  AuthorizationError: () => AuthorizationError,
102
101
  AuthenticationError: () => AuthenticationError,
@@ -105,19 +104,22 @@ __export(exports_src, {
105
104
  module.exports = __toCommonJS(exports_src);
106
105
 
107
106
  // packages/core/src/api/ConnectionInfo.ts
108
- class ConnectHeaders {
109
- }
110
-
111
107
  class ServerInfo {
112
108
  host;
113
109
  port;
114
110
  useSSL;
115
111
  }
112
+ var SessionKeepAliveMode;
113
+ ((SessionKeepAliveMode2) => {
114
+ SessionKeepAliveMode2["NONE"] = "NONE";
115
+ SessionKeepAliveMode2["ACTIVITY"] = "ACTIVITY";
116
+ SessionKeepAliveMode2["CONNECTION"] = "CONNECTION";
117
+ })(SessionKeepAliveMode ||= {});
116
118
 
117
119
  class ConnectionInfo extends ServerInfo {
118
- connectHeaders;
120
+ webSocketFactory;
119
121
  maxConnectionAttempts;
120
- disableStickySession;
122
+ sessionKeepAlive = "ACTIVITY" /* ACTIVITY */;
121
123
  }
122
124
  // packages/core/src/api/errors/KinoticError.ts
123
125
  class KinoticError extends Error {
@@ -133,10 +135,8 @@ var EventConstants;
133
135
  EventConstants2["CONTENT_TYPE_HEADER"] = "content-type";
134
136
  EventConstants2["CONTENT_LENGTH_HEADER"] = "content-length";
135
137
  EventConstants2["REPLY_TO_HEADER"] = "reply-to";
136
- EventConstants2["REPLY_TO_ID_HEADER"] = "reply-to-id";
137
- EventConstants2["SESSION_HEADER"] = "session";
138
138
  EventConstants2["CONNECTED_INFO_HEADER"] = "connected-info";
139
- EventConstants2["DISABLE_STICKY_SESSION_HEADER"] = "disable-sticky-session";
139
+ EventConstants2["SESSION_KEEP_ALIVE_HEADER"] = "session-keep-alive";
140
140
  EventConstants2["CORRELATION_ID_HEADER"] = "__correlation-id";
141
141
  EventConstants2["ERROR_HEADER"] = "error";
142
142
  EventConstants2["COMPLETE_HEADER"] = "complete";
@@ -149,6 +149,8 @@ var EventConstants;
149
149
  EventConstants2["SERVICE_DESTINATION_SCHEME"] = "srv";
150
150
  EventConstants2["STREAM_DESTINATION_PREFIX"] = "stream://";
151
151
  EventConstants2["STREAM_DESTINATION_SCHEME"] = "stream";
152
+ EventConstants2["REPLY_DESTINATION_PREFIX"] = "reply://";
153
+ EventConstants2["REPLY_DESTINATION_SCHEME"] = "reply";
152
154
  EventConstants2["CONTENT_JSON"] = "application/json";
153
155
  EventConstants2["CONTENT_TEXT"] = "text/plain";
154
156
  EventConstants2["TRACEPARENT_HEADER"] = "traceparent";
@@ -158,8 +160,8 @@ var EventConstants;
158
160
  // packages/core/src/internal/api/event/StompConnectionManager.ts
159
161
  var import_rx_stomp = require("@stomp/rx-stomp");
160
162
  var import_stompjs = require("@stomp/stompjs");
161
- var import_uuid = require("uuid");
162
163
  var import_debug = __toESM(require("debug"));
164
+ var import_uuid = require("uuid");
163
165
 
164
166
  class StompConnectionManager {
165
167
  lastWebsocketError = null;
@@ -172,9 +174,10 @@ class StompConnectionManager {
172
174
  initialConnectionSuccessful = false;
173
175
  debugLogger = import_debug.default("kinoitc:stomp");
174
176
  uuidv4 = import_uuid.v4();
175
- replyToId = import_uuid.v4();
176
- _replyToCri = "srv://" /* SERVICE_DESTINATION_PREFIX */ + this.replyToId + ":" + this.uuidv4 + "@kinoitc.js.EventBus/replyHandler";
177
+ _replyToCri = null;
178
+ serverHeadersSubscription = null;
177
179
  deactivationHandler = null;
180
+ replyToCriChangedHandler = null;
178
181
  get active() {
179
182
  return !!this.rxStomp;
180
183
  }
@@ -202,31 +205,21 @@ class StompConnectionManager {
202
205
  this.initialConnectionSuccessful = false;
203
206
  this.lastWebsocketError = null;
204
207
  this.maxConnectionAttemptsReached = false;
208
+ this._replyToCri = null;
209
+ this.serverHeadersSubscription?.unsubscribe();
210
+ this.serverHeadersSubscription = null;
205
211
  const url = "ws" + (connectionInfo.useSSL ? "s" : "") + "://" + connectionInfo.host + (connectionInfo.port ? ":" + connectionInfo.port : "") + "/v1";
206
212
  this.rxStomp = new import_rx_stomp.RxStomp;
207
- let connectHeadersInternal = typeof connectionInfo.connectHeaders !== "function" && connectionInfo.connectHeaders != null ? connectionInfo.connectHeaders : {};
208
213
  const stompConfig = {
209
214
  brokerURL: url,
210
- connectHeaders: connectHeadersInternal,
215
+ connectHeaders: {
216
+ ["session-keep-alive" /* SESSION_KEEP_ALIVE_HEADER */]: connectionInfo.sessionKeepAlive
217
+ },
211
218
  heartbeatIncoming: 120000,
212
219
  heartbeatOutgoing: 30000,
213
220
  reconnectDelay: this.INITIAL_RECONNECT_DELAY,
221
+ webSocketFactory: connectionInfo.webSocketFactory,
214
222
  beforeConnect: async () => {
215
- if (typeof connectionInfo.connectHeaders === "function") {
216
- const headers = await connectionInfo.connectHeaders();
217
- for (const key in headers) {
218
- connectHeadersInternal[key] = headers[key];
219
- }
220
- }
221
- if (connectionInfo.disableStickySession) {
222
- connectHeadersInternal["disable-sticky-session" /* DISABLE_STICKY_SESSION_HEADER */] = "true";
223
- }
224
- if (connectHeadersInternal["reply-to-id" /* REPLY_TO_ID_HEADER */]) {
225
- this.replyToId = connectHeadersInternal["reply-to-id" /* REPLY_TO_ID_HEADER */];
226
- this._replyToCri = "srv://" /* SERVICE_DESTINATION_PREFIX */ + this.replyToId + ":" + this.uuidv4 + "@kinoitc.js.EventBus/replyHandler";
227
- } else {
228
- connectHeadersInternal["reply-to-id" /* REPLY_TO_ID_HEADER */] = this.replyToId;
229
- }
230
223
  if (connectionInfo?.maxConnectionAttempts) {
231
224
  this.connectionAttempts++;
232
225
  if (this.connectionAttempts > connectionInfo.maxConnectionAttempts) {
@@ -268,36 +261,29 @@ class StompConnectionManager {
268
261
  this.rxStomp = null;
269
262
  reject(message);
270
263
  });
271
- const serverHeadersSubscription = this.rxStomp.serverHeaders$.subscribe((value) => {
272
- let connectedInfoJson = value["connected-info" /* CONNECTED_INFO_HEADER */];
273
- if (connectedInfoJson != null) {
274
- const connectedInfo = JSON.parse(connectedInfoJson);
275
- if (!connectionInfo.disableStickySession) {
276
- serverHeadersSubscription.unsubscribe();
277
- if (connectedInfo.sessionId != null && connectedInfo.replyToId != null) {
278
- if (connectionInfo.connectHeaders != null) {
279
- for (let key in connectHeadersInternal) {
280
- delete connectHeadersInternal[key];
281
- }
282
- }
283
- connectHeadersInternal["session" /* SESSION_HEADER */] = connectedInfo.sessionId;
284
- resolve(connectedInfo);
285
- } else {
286
- reject("Server did not return proper data for successful login");
287
- }
288
- } else if (typeof connectionInfo.connectHeaders === "function") {
289
- for (let key in connectHeadersInternal) {
290
- delete connectHeadersInternal[key];
291
- }
292
- if (!this.initialConnectionSuccessful) {
293
- resolve(connectedInfo);
294
- }
295
- } else if (typeof connectionInfo.connectHeaders === "object") {
296
- serverHeadersSubscription.unsubscribe();
297
- resolve(connectedInfo);
264
+ this.serverHeadersSubscription = this.rxStomp.serverHeaders$.subscribe((value) => {
265
+ const connectedInfoJson = value["connected-info" /* CONNECTED_INFO_HEADER */];
266
+ const firstConnect = this._replyToCri == null;
267
+ if (connectedInfoJson == null) {
268
+ if (firstConnect) {
269
+ reject("Server did not return proper data for successful login");
298
270
  }
299
- } else {
300
- reject("Server did not return proper data for successful login");
271
+ return;
272
+ }
273
+ const connectedInfo = JSON.parse(connectedInfoJson);
274
+ if (connectedInfo.replyToId == null) {
275
+ if (firstConnect) {
276
+ reject("Server did not return a replyToId for successful login");
277
+ }
278
+ return;
279
+ }
280
+ const newReplyToCri = "reply://" /* REPLY_DESTINATION_PREFIX */ + connectedInfo.replyToId + ":" + this.uuidv4 + "@kinoitc.js.EventBus/replyHandler";
281
+ if (firstConnect) {
282
+ this._replyToCri = newReplyToCri;
283
+ resolve(connectedInfo);
284
+ } else if (this._replyToCri !== newReplyToCri) {
285
+ this._replyToCri = newReplyToCri;
286
+ this.replyToCriChangedHandler?.(newReplyToCri);
301
287
  }
302
288
  });
303
289
  this.rxStomp.activate();
@@ -306,6 +292,8 @@ class StompConnectionManager {
306
292
  async deactivate(force) {
307
293
  if (this.rxStomp) {
308
294
  await this.rxStomp.deactivate({ force });
295
+ this.serverHeadersSubscription?.unsubscribe();
296
+ this.serverHeadersSubscription = null;
309
297
  if (this.deactivationHandler) {
310
298
  this.deactivationHandler();
311
299
  }
@@ -386,6 +374,10 @@ class EventBus {
386
374
  this.stompConnectionManager.deactivationHandler = () => {
387
375
  this.cleanup();
388
376
  };
377
+ this.stompConnectionManager.replyToCriChangedHandler = (replyToCri) => {
378
+ this.replyToCri = replyToCri;
379
+ this.resetRequestReplies("Reply destination changed");
380
+ };
389
381
  }
390
382
  isConnectionActive() {
391
383
  return this.stompConnectionManager.active;
@@ -453,7 +445,7 @@ class EventBus {
453
445
  })).subscribe({
454
446
  next(value) {
455
447
  if (value.hasHeader("control" /* CONTROL_HEADER */)) {
456
- if (value.headers.get("control" /* CONTROL_HEADER */) === "complete") {
448
+ if (value.headers.get("control" /* CONTROL_HEADER */) === "complete" /* CONTROL_VALUE_COMPLETE */) {
457
449
  serverSignaledCompletion = true;
458
450
  subscriber.complete();
459
451
  } else {
@@ -497,8 +489,16 @@ class EventBus {
497
489
  return this._observe(cri);
498
490
  }
499
491
  cleanup() {
492
+ this.resetRequestReplies("Connection disconnected");
493
+ if (this.errorSubjectSubscription) {
494
+ this.errorSubjectSubscription.unsubscribe();
495
+ this.errorSubjectSubscription = null;
496
+ }
497
+ this.serverInfo = null;
498
+ }
499
+ resetRequestReplies(reason) {
500
500
  if (this.requestRepliesSubject != null) {
501
- this.requestRepliesSubject.error(new Error("Connection disconnected"));
501
+ this.requestRepliesSubject.error(new Error(reason));
502
502
  if (this.requestRepliesSubscription != null) {
503
503
  this.requestRepliesSubscription.unsubscribe();
504
504
  this.requestRepliesSubscription = null;
@@ -506,11 +506,6 @@ class EventBus {
506
506
  this.requestRepliesSubject = null;
507
507
  this.requestRepliesObservable = null;
508
508
  }
509
- if (this.errorSubjectSubscription) {
510
- this.errorSubjectSubscription.unsubscribe();
511
- this.errorSubjectSubscription = null;
512
- }
513
- this.serverInfo = null;
514
509
  }
515
510
  createSendUnavailableError() {
516
511
  let ret = "You must call connect on the event bus before sending any request";
@@ -994,7 +989,7 @@ var import_operators2 = require("rxjs/operators");
994
989
  // packages/core/package.json
995
990
  var package_default = {
996
991
  name: "@kinotic-ai/core",
997
- version: "1.2.1",
992
+ version: "1.3.0",
998
993
  type: "module",
999
994
  files: [
1000
995
  "dist"
@@ -1022,44 +1017,44 @@ var package_default = {
1022
1017
  },
1023
1018
  scripts: {
1024
1019
  "type-check": "tsc --noEmit",
1025
- test: "vitest run",
1020
+ test: "vitest run --mode development",
1026
1021
  coverage: "vitest run --coverage",
1027
1022
  "ui-test": "vitest --ui --coverage.enabled=true --mode development"
1028
1023
  },
1029
1024
  devDependencies: {
1030
- "@opentelemetry/exporter-trace-otlp-grpc": "^0.212.0",
1031
- "@opentelemetry/resources": "^2.5.1",
1032
- "@opentelemetry/sdk-metrics": "^2.5.1",
1033
- "@opentelemetry/sdk-node": "^0.212.0",
1034
- "@opentelemetry/sdk-trace-node": "^2.5.1",
1035
- "@types/node": "^25.3.2",
1025
+ "@opentelemetry/exporter-trace-otlp-grpc": "^0.214.0",
1026
+ "@opentelemetry/resources": "^2.6.1",
1027
+ "@opentelemetry/sdk-metrics": "^2.6.1",
1028
+ "@opentelemetry/sdk-node": "^0.214.0",
1029
+ "@opentelemetry/sdk-trace-node": "^2.6.1",
1030
+ "@types/node": "^25.6.0",
1036
1031
  "@types/uuid": "^11.0.0",
1037
1032
  "@types/ws": "^8.18.1",
1038
- "@vitest/coverage-v8": "^4.0.18",
1039
- "@vitest/runner": "^4.0.18",
1040
- "@vitest/ui": "^4.0.18",
1041
- allure: "^3.3.1",
1042
- "allure-vitest": "^3.5.0",
1043
- dotenv: "^17.3.1",
1044
- "properties-file": "^3.6.4",
1045
- testcontainers: "^11.12.0",
1033
+ "@vitest/coverage-v8": "^4.1.4",
1034
+ "@vitest/runner": "^4.1.4",
1035
+ "@vitest/ui": "^4.1.4",
1036
+ allure: "^3.4.1",
1037
+ "allure-vitest": "^3.7.1",
1038
+ dotenv: "^17.4.1",
1039
+ "properties-file": "^4.0.0",
1040
+ testcontainers: "^11.14.0",
1046
1041
  tslib: "^2.8.1",
1047
- typescript: "^5.9.3",
1048
- vitest: "^4.0.18",
1049
- ws: "^8.19.0"
1042
+ typescript: "^6.0.2",
1043
+ vitest: "^4.1.4",
1044
+ ws: "^8.20.0"
1050
1045
  },
1051
1046
  dependencies: {
1052
- "@opentelemetry/api": "^1.9.0",
1047
+ "@opentelemetry/api": "^1.9.1",
1053
1048
  "@opentelemetry/semantic-conventions": "^1.40.0",
1054
1049
  "@stomp/rx-stomp": "^2.3.0",
1055
1050
  "@stomp/stompjs": "^7.3.0",
1056
- "@types/debug": "^4.1.12",
1051
+ "@types/debug": "^4.1.13",
1057
1052
  debug: "^4.4.3",
1058
1053
  "p-tap": "^4.0.0",
1059
1054
  "reflect-metadata": "^0.2.2",
1060
1055
  rxjs: "^7.8.2",
1061
1056
  "typescript-optional": "3.0.0-alpha.3",
1062
- uuid: "^11.0.0"
1057
+ uuid: "^13.0.0"
1063
1058
  },
1064
1059
  peerDependenciesMeta: {
1065
1060
  typescript: {
@@ -1243,16 +1238,6 @@ class KinoticSingleton {
1243
1238
  }
1244
1239
  }
1245
1240
  var Kinotic = new KinoticSingleton;
1246
- // packages/core/src/api/KinoticProjectConfig.ts
1247
- class KinoticProjectConfig {
1248
- name;
1249
- description;
1250
- application;
1251
- entitiesPaths;
1252
- generatedPath;
1253
- fileExtensionForImports = ".js";
1254
- validate;
1255
- }
1256
1241
  // packages/core/src/api/crud/AbstractIterablePage.ts
1257
1242
  class AbstractIterablePage {
1258
1243
  pageable;
@@ -1354,6 +1339,9 @@ class CrudServiceProxy {
1354
1339
  create(entity) {
1355
1340
  return this.serviceProxy.invoke("create", [entity]);
1356
1341
  }
1342
+ createSync(entity) {
1343
+ return this.serviceProxy.invoke("createSync", [entity]);
1344
+ }
1357
1345
  deleteById(id) {
1358
1346
  return this.serviceProxy.invoke("deleteById", [id]);
1359
1347
  }
@@ -1495,7 +1483,6 @@ class AuthorizationError extends KinoticError {
1495
1483
  }
1496
1484
  // packages/core/src/api/security/ConnectedInfo.ts
1497
1485
  class ConnectedInfo {
1498
- sessionId;
1499
1486
  replyToId;
1500
1487
  participant;
1501
1488
  }
@@ -1503,11 +1490,15 @@ class ConnectedInfo {
1503
1490
  class Participant {
1504
1491
  id;
1505
1492
  tenantId;
1493
+ authScopeType;
1494
+ authScopeId;
1506
1495
  metadata;
1507
1496
  roles;
1508
- constructor(id, tenantId, metadata, roles) {
1497
+ constructor(id, tenantId, authScopeType, authScopeId, metadata, roles) {
1509
1498
  this.id = id;
1510
1499
  this.tenantId = tenantId;
1500
+ this.authScopeType = authScopeType;
1501
+ this.authScopeId = authScopeId;
1511
1502
  this.metadata = metadata || new Map;
1512
1503
  this.roles = roles || [];
1513
1504
  }
package/dist/index.d.cts CHANGED
@@ -1,26 +1,53 @@
1
1
  /**
2
- * ConnectHeaders to use during connection to the kinoitc server
3
- * These headers will be sent as part of the STOMP CONNECT frame
4
- * This is typically used for authentication information, but any data can be sent
2
+ * Structural shape of a WebSocket used by the underlying STOMP client.
3
+ * Copied from the WebSocket interface to avoid pulling in the DOM typelib,
4
+ * so this type stays usable in Node environments where `lib: dom` is not set.
5
5
  */
6
- declare class ConnectHeaders {
7
- [key: string]: string;
6
+ interface IWebSocket {
7
+ url: string;
8
+ binaryType?: string;
9
+ readyState: number;
10
+ onopen: ((ev?: any) => any) | undefined | null;
11
+ onclose: ((ev?: any) => any) | undefined | null;
12
+ onerror: ((ev: any) => any) | undefined | null;
13
+ onmessage: ((ev: any) => any) | undefined | null;
14
+ close(code?: number, reason?: string): void;
15
+ send(data: string | ArrayBuffer): void;
8
16
  }
17
+ /**
18
+ * Factory invoked on every (re)connect to produce the WebSocket the STOMP
19
+ * client will use. Supply this in Node when you need to set headers on the
20
+ * upgrade request (for example, an Authorization header). Browser callers
21
+ * normally leave this unset and rely on the session cookie established by a
22
+ * prior REST login.
23
+ */
24
+ type WebSocketFactory = () => IWebSocket;
9
25
  declare class ServerInfo {
10
26
  host: string;
11
27
  port?: number | null;
12
28
  useSSL?: boolean | null;
13
29
  }
30
+ declare enum SessionKeepAliveMode {
31
+ NONE = "NONE",
32
+ ACTIVITY = "ACTIVITY",
33
+ CONNECTION = "CONNECTION"
34
+ }
14
35
  /**
15
- * ConnectionInfo provides the information needed to connect to the kinoitc server
36
+ * ConnectionInfo provides the information needed to connect to the kinoitc server.
37
+ *
38
+ * Authentication is performed during the WebSocket upgrade (handshake), not in
39
+ * the STOMP CONNECT frame. In the browser, log in via the REST endpoints first
40
+ * and the established session cookie will be used. In Node, supply a
41
+ * {@link WebSocketFactory} that attaches the required upgrade headers.
16
42
  */
17
43
  declare class ConnectionInfo extends ServerInfo {
18
44
  /**
19
- * The headers to send during the connection to the kinoitc server.
20
- * If a function is provided, it will be called to get the headers each time the connection is established.
21
- * This is useful for providing dynamic headers, such as a JWT token that expires.
45
+ * Optional factory used to create the underlying WebSocket. Use this in
46
+ * Node to attach custom headers (such as Authorization) to the upgrade
47
+ * request. If omitted, a default WebSocket is created and authentication
48
+ * is expected to come from the session cookie.
22
49
  */
23
- connectHeaders?: ConnectHeaders | (() => Promise<ConnectHeaders>);
50
+ webSocketFactory?: WebSocketFactory;
24
51
  /**
25
52
  * The maximum number of connection attempts to make during the {@link IEventBus} initial connection request.
26
53
  * If the limit is reached the {@link IEventBus} will return an error to the caller of {@link IEventBus#connect}
@@ -28,10 +55,11 @@ declare class ConnectionInfo extends ServerInfo {
28
55
  */
29
56
  maxConnectionAttempts?: number | null;
30
57
  /**
31
- * If true, the session will not be kept alive after the connection is established and then disrupted.
32
- * If false, the session will be kept alive after the connection is established and then disrupted, for a period of time.
58
+ * Controls whether session expiration is extended by gateway activity or by an active websocket connection.
59
+ * Defaults to {@link SessionKeepAliveMode.ACTIVITY}.
60
+ * Use {@link SessionKeepAliveMode.NONE} to remove the session when the websocket connection closes.
33
61
  */
34
- disableStickySession?: boolean | null;
62
+ sessionKeepAlive: SessionKeepAliveMode;
35
63
  }
36
64
  import { Optional } from "typescript-optional";
37
65
  import { Observable } from "rxjs";
@@ -59,6 +87,19 @@ interface IParticipant extends Identifiable<string> {
59
87
  */
60
88
  tenantId?: string | null;
61
89
  /**
90
+ * The scope layer this participant authenticated against.
91
+ * Well-known values are "SYSTEM", "ORGANIZATION", and "APPLICATION",
92
+ * but custom values are allowed for extensibility.
93
+ */
94
+ authScopeType?: string | null;
95
+ /**
96
+ * The identifier of the specific scope this participant belongs to.
97
+ * For example, "kinotic" for system scope, an organization ID, or an application ID.
98
+ * Together with {@link authScopeType}, uniquely identifies which user pool
99
+ * this participant was authenticated from.
100
+ */
101
+ authScopeId?: string | null;
102
+ /**
62
103
  * Metadata is a map of key value pairs that can be used to store additional information about a participant
63
104
  *
64
105
  * @return a map of key value pairs
@@ -77,15 +118,16 @@ interface IParticipant extends Identifiable<string> {
77
118
  declare class Participant implements IParticipant {
78
119
  id: string;
79
120
  tenantId?: string | null;
121
+ authScopeType?: string | null;
122
+ authScopeId?: string | null;
80
123
  metadata: Map<string, string>;
81
124
  roles: string[];
82
- constructor(id: string, tenantId?: string, metadata?: Map<string, string>, roles?: string[]);
125
+ constructor(id: string, tenantId?: string, authScopeType?: string, authScopeId?: string, metadata?: Map<string, string>, roles?: string[]);
83
126
  }
84
127
  /**
85
128
  * Contains information about the connection that was established
86
129
  */
87
130
  declare class ConnectedInfo {
88
- sessionId: string;
89
131
  replyToId: string;
90
132
  participant: Participant;
91
133
  }
@@ -238,22 +280,13 @@ declare enum EventConstants {
238
280
  CONTENT_LENGTH_HEADER = "content-length",
239
281
  REPLY_TO_HEADER = "reply-to",
240
282
  /**
241
- * This is the replyToId that will be supplied by the client, which will be used when sending replies to the client.
242
- */
243
- REPLY_TO_ID_HEADER = "reply-to-id",
244
- /**
245
- * Header provided by the sever on connection to represent the user's session id
246
- */
247
- SESSION_HEADER = "session",
248
- /**
249
283
  * Header provided by the server on connection to provide the {@link ConnectionInfo} as a JSON string
250
284
  */
251
285
  CONNECTED_INFO_HEADER = "connected-info",
252
286
  /**
253
- * Header provided by the client on connection request to represent that the server
254
- * should not keep the session alive after any network disconnection.
287
+ * Header provided by the client on connection request to choose how the session is kept alive.
255
288
  */
256
- DISABLE_STICKY_SESSION_HEADER = "disable-sticky-session",
289
+ SESSION_KEEP_ALIVE_HEADER = "session-keep-alive",
257
290
  /**
258
291
  * Correlates a response with a given request
259
292
  * Headers that start with __ will always be persisted between messages
@@ -282,6 +315,8 @@ declare enum EventConstants {
282
315
  SERVICE_DESTINATION_SCHEME = "srv",
283
316
  STREAM_DESTINATION_PREFIX = "stream://",
284
317
  STREAM_DESTINATION_SCHEME = "stream",
318
+ REPLY_DESTINATION_PREFIX = "reply://",
319
+ REPLY_DESTINATION_SCHEME = "reply",
285
320
  CONTENT_JSON = "application/json",
286
321
  CONTENT_TEXT = "text/plain",
287
322
  /**
@@ -641,66 +676,6 @@ declare function Version(version: string): (target: Function) => void;
641
676
  declare function Context(): (target: any, propertyKey: string, parameterIndex: number) => void;
642
677
  declare function Publish(namespace: string, name?: string): (target: Function) => any;
643
678
  /**
644
- * Configuration for a single entities path and its corresponding repository output.
645
- */
646
- type EntitiesPathConfig = {
647
- /**
648
- * The path to search for classes decorated with @Entity.
649
- */
650
- path: string;
651
- /**
652
- * The path where generated Repository classes will be placed.
653
- */
654
- repositoryPath: string;
655
- /**
656
- * If true, the subfolder structure under the entities path will be mirrored under the repository path.
657
- * For example, if entitiesPath is "src/model" and contains "payments/Payment.ts",
658
- * the generated repository will be placed in "repositoryPath/payments/".
659
- * If false, all generated repositories are placed directly in the repositoryPath.
660
- * Defaults to true.
661
- */
662
- mirrorFolderStructure?: boolean;
663
- };
664
- /**
665
- * The project configuration for a Kinotic project.
666
- */
667
- declare class KinoticProjectConfig {
668
- /**
669
- * The name of the project or undefined if a project name is used.
670
- * i.e. if the project is typescript the package.json name will be used.
671
- */
672
- name?: string;
673
- /**
674
- * The description of the project.
675
- */
676
- description?: string;
677
- /**
678
- * The Kinotic Application that this project belongs to.
679
- */
680
- application: string;
681
- /**
682
- * The paths to search for classes decorated with @Entity that Kinotic will be created for.
683
- * Each entry can be a string (simple path) or an {@link EntitiesPathConfig} object for full control
684
- * over where repository classes are generated.
685
- *
686
- * When a plain string is provided, the {@link generatedPath} will be used as the repository output path.
687
- */
688
- entitiesPaths: (string | EntitiesPathConfig)[];
689
- /**
690
- * The default path to where generated files will be placed when entitiesPaths contains plain strings.
691
- * Ignored for entitiesPaths entries that use {@link EntitiesPathConfig}.
692
- */
693
- generatedPath?: string;
694
- /**
695
- * The file extension to use for imports in generated files.
696
- */
697
- fileExtensionForImports: string;
698
- /**
699
- * If true the generated Repository classes will validate all data before sending to the server.
700
- */
701
- validate?: boolean;
702
- }
703
- /**
704
679
  * A page is a sublist of a list of objects.
705
680
  * @author Navid Mitchell
706
681
  */
@@ -949,6 +924,15 @@ interface ICrudServiceProxy<T extends Identifiable<string>> extends IEditableDat
949
924
  */
950
925
  create(entity: T): Promise<T>;
951
926
  /**
927
+ * Creates a new entity if one does not already exist for the given id, and waits for the
928
+ * change to be visible in search results before returning.
929
+ * Use this when you need read-your-write consistency immediately after creation.
930
+ *
931
+ * @param entity to create if one does not already exist
932
+ * @return a {@link Promise} containing the new entity after it is searchable, or an error if an exception occurred
933
+ */
934
+ createSync(entity: T): Promise<T>;
935
+ /**
952
936
  * Saves a given entity. Use the returned instance for further operations as the save operation might have changed the
953
937
  * entity instance completely.
954
938
  *
@@ -1017,6 +1001,7 @@ declare class CrudServiceProxy<T extends Identifiable<string>> implements ICrudS
1017
1001
  constructor(serviceProxy: IServiceProxy);
1018
1002
  count(): Promise<number>;
1019
1003
  create(entity: T): Promise<T>;
1004
+ createSync(entity: T): Promise<T>;
1020
1005
  deleteById(id: string): Promise<void>;
1021
1006
  findAll(pageable: Pageable): Promise<IterablePage<T>>;
1022
1007
  findAllSinglePage(pageable: Pageable): Promise<Page<T>>;
@@ -1129,6 +1114,12 @@ declare class EventBus implements IEventBus {
1129
1114
  observe(cri: string): Observable3<IEvent>;
1130
1115
  private cleanup;
1131
1116
  /**
1117
+ * Tears down the shared request-replies stream so the next request rebuilds it against the
1118
+ * current {@link replyToCri}. Any in-flight requests are failed with the given reason since
1119
+ * their replies can no longer be delivered.
1120
+ */
1121
+ private resetRequestReplies;
1122
+ /**
1132
1123
  * Creates the proper error to return if this.stompConnectionManager?.rxStomp is not available on a send request
1133
1124
  */
1134
1125
  private createSendUnavailableError;
@@ -1152,4 +1143,4 @@ declare class ParticipantConstants {
1152
1143
  static readonly PARTICIPANT_TYPE_NODE: string;
1153
1144
  static readonly CLI_PARTICIPANT_ID: string;
1154
1145
  }
1155
- export { createCRI, Version, TextEventFactory, Sort, ServiceRegistry, ServiceContext, ServerInfo, Scope, Publish, ParticipantConstants, Participant, Pageable, Page, Order, OffsetPageable, NullHandling, KinoticSingleton, KinoticProjectConfig, KinoticPlugin, KinoticError, Kinotic, JsonEventFactory, IterablePage, Identifiable, IServiceRegistry, IServiceProxy, IParticipant, IKinotic, IEventFactory, IEventBus, IEvent, IEditableDataSource, IDataSource, ICrudServiceProxyFactory, ICrudServiceProxy, FunctionalIterablePage, EventConstants, EventBus, Event, EntitiesPathConfig, Direction, DefaultCRI, DataSourceUtils, CursorPageable, CrudServiceProxyFactory, CrudServiceProxy, ContextInterceptor, Context, ConnectionInfo, ConnectedInfo, ConnectHeaders, CRI, CONTEXT_METADATA_KEY, AuthorizationError, AuthenticationError, AbstractIterablePage };
1146
+ export { createCRI, WebSocketFactory, Version, TextEventFactory, Sort, SessionKeepAliveMode, ServiceRegistry, ServiceContext, ServerInfo, Scope, Publish, ParticipantConstants, Participant, Pageable, Page, Order, OffsetPageable, NullHandling, KinoticSingleton, KinoticPlugin, KinoticError, Kinotic, JsonEventFactory, IterablePage, Identifiable, IWebSocket, IServiceRegistry, IServiceProxy, IParticipant, IKinotic, IEventFactory, IEventBus, IEvent, IEditableDataSource, IDataSource, ICrudServiceProxyFactory, ICrudServiceProxy, FunctionalIterablePage, EventConstants, EventBus, Event, Direction, DefaultCRI, DataSourceUtils, CursorPageable, CrudServiceProxyFactory, CrudServiceProxy, ContextInterceptor, Context, ConnectionInfo, ConnectedInfo, CRI, CONTEXT_METADATA_KEY, AuthorizationError, AuthenticationError, AbstractIterablePage };
package/dist/index.d.ts CHANGED
@@ -1,26 +1,53 @@
1
1
  /**
2
- * ConnectHeaders to use during connection to the kinoitc server
3
- * These headers will be sent as part of the STOMP CONNECT frame
4
- * This is typically used for authentication information, but any data can be sent
2
+ * Structural shape of a WebSocket used by the underlying STOMP client.
3
+ * Copied from the WebSocket interface to avoid pulling in the DOM typelib,
4
+ * so this type stays usable in Node environments where `lib: dom` is not set.
5
5
  */
6
- declare class ConnectHeaders {
7
- [key: string]: string;
6
+ interface IWebSocket {
7
+ url: string;
8
+ binaryType?: string;
9
+ readyState: number;
10
+ onopen: ((ev?: any) => any) | undefined | null;
11
+ onclose: ((ev?: any) => any) | undefined | null;
12
+ onerror: ((ev: any) => any) | undefined | null;
13
+ onmessage: ((ev: any) => any) | undefined | null;
14
+ close(code?: number, reason?: string): void;
15
+ send(data: string | ArrayBuffer): void;
8
16
  }
17
+ /**
18
+ * Factory invoked on every (re)connect to produce the WebSocket the STOMP
19
+ * client will use. Supply this in Node when you need to set headers on the
20
+ * upgrade request (for example, an Authorization header). Browser callers
21
+ * normally leave this unset and rely on the session cookie established by a
22
+ * prior REST login.
23
+ */
24
+ type WebSocketFactory = () => IWebSocket;
9
25
  declare class ServerInfo {
10
26
  host: string;
11
27
  port?: number | null;
12
28
  useSSL?: boolean | null;
13
29
  }
30
+ declare enum SessionKeepAliveMode {
31
+ NONE = "NONE",
32
+ ACTIVITY = "ACTIVITY",
33
+ CONNECTION = "CONNECTION"
34
+ }
14
35
  /**
15
- * ConnectionInfo provides the information needed to connect to the kinoitc server
36
+ * ConnectionInfo provides the information needed to connect to the kinoitc server.
37
+ *
38
+ * Authentication is performed during the WebSocket upgrade (handshake), not in
39
+ * the STOMP CONNECT frame. In the browser, log in via the REST endpoints first
40
+ * and the established session cookie will be used. In Node, supply a
41
+ * {@link WebSocketFactory} that attaches the required upgrade headers.
16
42
  */
17
43
  declare class ConnectionInfo extends ServerInfo {
18
44
  /**
19
- * The headers to send during the connection to the kinoitc server.
20
- * If a function is provided, it will be called to get the headers each time the connection is established.
21
- * This is useful for providing dynamic headers, such as a JWT token that expires.
45
+ * Optional factory used to create the underlying WebSocket. Use this in
46
+ * Node to attach custom headers (such as Authorization) to the upgrade
47
+ * request. If omitted, a default WebSocket is created and authentication
48
+ * is expected to come from the session cookie.
22
49
  */
23
- connectHeaders?: ConnectHeaders | (() => Promise<ConnectHeaders>);
50
+ webSocketFactory?: WebSocketFactory;
24
51
  /**
25
52
  * The maximum number of connection attempts to make during the {@link IEventBus} initial connection request.
26
53
  * If the limit is reached the {@link IEventBus} will return an error to the caller of {@link IEventBus#connect}
@@ -28,10 +55,11 @@ declare class ConnectionInfo extends ServerInfo {
28
55
  */
29
56
  maxConnectionAttempts?: number | null;
30
57
  /**
31
- * If true, the session will not be kept alive after the connection is established and then disrupted.
32
- * If false, the session will be kept alive after the connection is established and then disrupted, for a period of time.
58
+ * Controls whether session expiration is extended by gateway activity or by an active websocket connection.
59
+ * Defaults to {@link SessionKeepAliveMode.ACTIVITY}.
60
+ * Use {@link SessionKeepAliveMode.NONE} to remove the session when the websocket connection closes.
33
61
  */
34
- disableStickySession?: boolean | null;
62
+ sessionKeepAlive: SessionKeepAliveMode;
35
63
  }
36
64
  import { Optional } from "typescript-optional";
37
65
  import { Observable } from "rxjs";
@@ -59,6 +87,19 @@ interface IParticipant extends Identifiable<string> {
59
87
  */
60
88
  tenantId?: string | null;
61
89
  /**
90
+ * The scope layer this participant authenticated against.
91
+ * Well-known values are "SYSTEM", "ORGANIZATION", and "APPLICATION",
92
+ * but custom values are allowed for extensibility.
93
+ */
94
+ authScopeType?: string | null;
95
+ /**
96
+ * The identifier of the specific scope this participant belongs to.
97
+ * For example, "kinotic" for system scope, an organization ID, or an application ID.
98
+ * Together with {@link authScopeType}, uniquely identifies which user pool
99
+ * this participant was authenticated from.
100
+ */
101
+ authScopeId?: string | null;
102
+ /**
62
103
  * Metadata is a map of key value pairs that can be used to store additional information about a participant
63
104
  *
64
105
  * @return a map of key value pairs
@@ -77,15 +118,16 @@ interface IParticipant extends Identifiable<string> {
77
118
  declare class Participant implements IParticipant {
78
119
  id: string;
79
120
  tenantId?: string | null;
121
+ authScopeType?: string | null;
122
+ authScopeId?: string | null;
80
123
  metadata: Map<string, string>;
81
124
  roles: string[];
82
- constructor(id: string, tenantId?: string, metadata?: Map<string, string>, roles?: string[]);
125
+ constructor(id: string, tenantId?: string, authScopeType?: string, authScopeId?: string, metadata?: Map<string, string>, roles?: string[]);
83
126
  }
84
127
  /**
85
128
  * Contains information about the connection that was established
86
129
  */
87
130
  declare class ConnectedInfo {
88
- sessionId: string;
89
131
  replyToId: string;
90
132
  participant: Participant;
91
133
  }
@@ -238,22 +280,13 @@ declare enum EventConstants {
238
280
  CONTENT_LENGTH_HEADER = "content-length",
239
281
  REPLY_TO_HEADER = "reply-to",
240
282
  /**
241
- * This is the replyToId that will be supplied by the client, which will be used when sending replies to the client.
242
- */
243
- REPLY_TO_ID_HEADER = "reply-to-id",
244
- /**
245
- * Header provided by the sever on connection to represent the user's session id
246
- */
247
- SESSION_HEADER = "session",
248
- /**
249
283
  * Header provided by the server on connection to provide the {@link ConnectionInfo} as a JSON string
250
284
  */
251
285
  CONNECTED_INFO_HEADER = "connected-info",
252
286
  /**
253
- * Header provided by the client on connection request to represent that the server
254
- * should not keep the session alive after any network disconnection.
287
+ * Header provided by the client on connection request to choose how the session is kept alive.
255
288
  */
256
- DISABLE_STICKY_SESSION_HEADER = "disable-sticky-session",
289
+ SESSION_KEEP_ALIVE_HEADER = "session-keep-alive",
257
290
  /**
258
291
  * Correlates a response with a given request
259
292
  * Headers that start with __ will always be persisted between messages
@@ -282,6 +315,8 @@ declare enum EventConstants {
282
315
  SERVICE_DESTINATION_SCHEME = "srv",
283
316
  STREAM_DESTINATION_PREFIX = "stream://",
284
317
  STREAM_DESTINATION_SCHEME = "stream",
318
+ REPLY_DESTINATION_PREFIX = "reply://",
319
+ REPLY_DESTINATION_SCHEME = "reply",
285
320
  CONTENT_JSON = "application/json",
286
321
  CONTENT_TEXT = "text/plain",
287
322
  /**
@@ -641,66 +676,6 @@ declare function Version(version: string): (target: Function) => void;
641
676
  declare function Context(): (target: any, propertyKey: string, parameterIndex: number) => void;
642
677
  declare function Publish(namespace: string, name?: string): (target: Function) => any;
643
678
  /**
644
- * Configuration for a single entities path and its corresponding repository output.
645
- */
646
- type EntitiesPathConfig = {
647
- /**
648
- * The path to search for classes decorated with @Entity.
649
- */
650
- path: string;
651
- /**
652
- * The path where generated Repository classes will be placed.
653
- */
654
- repositoryPath: string;
655
- /**
656
- * If true, the subfolder structure under the entities path will be mirrored under the repository path.
657
- * For example, if entitiesPath is "src/model" and contains "payments/Payment.ts",
658
- * the generated repository will be placed in "repositoryPath/payments/".
659
- * If false, all generated repositories are placed directly in the repositoryPath.
660
- * Defaults to true.
661
- */
662
- mirrorFolderStructure?: boolean;
663
- };
664
- /**
665
- * The project configuration for a Kinotic project.
666
- */
667
- declare class KinoticProjectConfig {
668
- /**
669
- * The name of the project or undefined if a project name is used.
670
- * i.e. if the project is typescript the package.json name will be used.
671
- */
672
- name?: string;
673
- /**
674
- * The description of the project.
675
- */
676
- description?: string;
677
- /**
678
- * The Kinotic Application that this project belongs to.
679
- */
680
- application: string;
681
- /**
682
- * The paths to search for classes decorated with @Entity that Kinotic will be created for.
683
- * Each entry can be a string (simple path) or an {@link EntitiesPathConfig} object for full control
684
- * over where repository classes are generated.
685
- *
686
- * When a plain string is provided, the {@link generatedPath} will be used as the repository output path.
687
- */
688
- entitiesPaths: (string | EntitiesPathConfig)[];
689
- /**
690
- * The default path to where generated files will be placed when entitiesPaths contains plain strings.
691
- * Ignored for entitiesPaths entries that use {@link EntitiesPathConfig}.
692
- */
693
- generatedPath?: string;
694
- /**
695
- * The file extension to use for imports in generated files.
696
- */
697
- fileExtensionForImports: string;
698
- /**
699
- * If true the generated Repository classes will validate all data before sending to the server.
700
- */
701
- validate?: boolean;
702
- }
703
- /**
704
679
  * A page is a sublist of a list of objects.
705
680
  * @author Navid Mitchell
706
681
  */
@@ -949,6 +924,15 @@ interface ICrudServiceProxy<T extends Identifiable<string>> extends IEditableDat
949
924
  */
950
925
  create(entity: T): Promise<T>;
951
926
  /**
927
+ * Creates a new entity if one does not already exist for the given id, and waits for the
928
+ * change to be visible in search results before returning.
929
+ * Use this when you need read-your-write consistency immediately after creation.
930
+ *
931
+ * @param entity to create if one does not already exist
932
+ * @return a {@link Promise} containing the new entity after it is searchable, or an error if an exception occurred
933
+ */
934
+ createSync(entity: T): Promise<T>;
935
+ /**
952
936
  * Saves a given entity. Use the returned instance for further operations as the save operation might have changed the
953
937
  * entity instance completely.
954
938
  *
@@ -1017,6 +1001,7 @@ declare class CrudServiceProxy<T extends Identifiable<string>> implements ICrudS
1017
1001
  constructor(serviceProxy: IServiceProxy);
1018
1002
  count(): Promise<number>;
1019
1003
  create(entity: T): Promise<T>;
1004
+ createSync(entity: T): Promise<T>;
1020
1005
  deleteById(id: string): Promise<void>;
1021
1006
  findAll(pageable: Pageable): Promise<IterablePage<T>>;
1022
1007
  findAllSinglePage(pageable: Pageable): Promise<Page<T>>;
@@ -1129,6 +1114,12 @@ declare class EventBus implements IEventBus {
1129
1114
  observe(cri: string): Observable3<IEvent>;
1130
1115
  private cleanup;
1131
1116
  /**
1117
+ * Tears down the shared request-replies stream so the next request rebuilds it against the
1118
+ * current {@link replyToCri}. Any in-flight requests are failed with the given reason since
1119
+ * their replies can no longer be delivered.
1120
+ */
1121
+ private resetRequestReplies;
1122
+ /**
1132
1123
  * Creates the proper error to return if this.stompConnectionManager?.rxStomp is not available on a send request
1133
1124
  */
1134
1125
  private createSendUnavailableError;
@@ -1152,4 +1143,4 @@ declare class ParticipantConstants {
1152
1143
  static readonly PARTICIPANT_TYPE_NODE: string;
1153
1144
  static readonly CLI_PARTICIPANT_ID: string;
1154
1145
  }
1155
- export { createCRI, Version, TextEventFactory, Sort, ServiceRegistry, ServiceContext, ServerInfo, Scope, Publish, ParticipantConstants, Participant, Pageable, Page, Order, OffsetPageable, NullHandling, KinoticSingleton, KinoticProjectConfig, KinoticPlugin, KinoticError, Kinotic, JsonEventFactory, IterablePage, Identifiable, IServiceRegistry, IServiceProxy, IParticipant, IKinotic, IEventFactory, IEventBus, IEvent, IEditableDataSource, IDataSource, ICrudServiceProxyFactory, ICrudServiceProxy, FunctionalIterablePage, EventConstants, EventBus, Event, EntitiesPathConfig, Direction, DefaultCRI, DataSourceUtils, CursorPageable, CrudServiceProxyFactory, CrudServiceProxy, ContextInterceptor, Context, ConnectionInfo, ConnectedInfo, ConnectHeaders, CRI, CONTEXT_METADATA_KEY, AuthorizationError, AuthenticationError, AbstractIterablePage };
1146
+ export { createCRI, WebSocketFactory, Version, TextEventFactory, Sort, SessionKeepAliveMode, ServiceRegistry, ServiceContext, ServerInfo, Scope, Publish, ParticipantConstants, Participant, Pageable, Page, Order, OffsetPageable, NullHandling, KinoticSingleton, KinoticPlugin, KinoticError, Kinotic, JsonEventFactory, IterablePage, Identifiable, IWebSocket, IServiceRegistry, IServiceProxy, IParticipant, IKinotic, IEventFactory, IEventBus, IEvent, IEditableDataSource, IDataSource, ICrudServiceProxyFactory, ICrudServiceProxy, FunctionalIterablePage, EventConstants, EventBus, Event, Direction, DefaultCRI, DataSourceUtils, CursorPageable, CrudServiceProxyFactory, CrudServiceProxy, ContextInterceptor, Context, ConnectionInfo, ConnectedInfo, CRI, CONTEXT_METADATA_KEY, AuthorizationError, AuthenticationError, AbstractIterablePage };
package/dist/index.js CHANGED
@@ -2,19 +2,22 @@ import { createRequire } from "node:module";
2
2
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
3
 
4
4
  // packages/core/src/api/ConnectionInfo.ts
5
- class ConnectHeaders {
6
- }
7
-
8
5
  class ServerInfo {
9
6
  host;
10
7
  port;
11
8
  useSSL;
12
9
  }
10
+ var SessionKeepAliveMode;
11
+ ((SessionKeepAliveMode2) => {
12
+ SessionKeepAliveMode2["NONE"] = "NONE";
13
+ SessionKeepAliveMode2["ACTIVITY"] = "ACTIVITY";
14
+ SessionKeepAliveMode2["CONNECTION"] = "CONNECTION";
15
+ })(SessionKeepAliveMode ||= {});
13
16
 
14
17
  class ConnectionInfo extends ServerInfo {
15
- connectHeaders;
18
+ webSocketFactory;
16
19
  maxConnectionAttempts;
17
- disableStickySession;
20
+ sessionKeepAlive = "ACTIVITY" /* ACTIVITY */;
18
21
  }
19
22
  // packages/core/src/api/errors/KinoticError.ts
20
23
  class KinoticError extends Error {
@@ -30,10 +33,8 @@ var EventConstants;
30
33
  EventConstants2["CONTENT_TYPE_HEADER"] = "content-type";
31
34
  EventConstants2["CONTENT_LENGTH_HEADER"] = "content-length";
32
35
  EventConstants2["REPLY_TO_HEADER"] = "reply-to";
33
- EventConstants2["REPLY_TO_ID_HEADER"] = "reply-to-id";
34
- EventConstants2["SESSION_HEADER"] = "session";
35
36
  EventConstants2["CONNECTED_INFO_HEADER"] = "connected-info";
36
- EventConstants2["DISABLE_STICKY_SESSION_HEADER"] = "disable-sticky-session";
37
+ EventConstants2["SESSION_KEEP_ALIVE_HEADER"] = "session-keep-alive";
37
38
  EventConstants2["CORRELATION_ID_HEADER"] = "__correlation-id";
38
39
  EventConstants2["ERROR_HEADER"] = "error";
39
40
  EventConstants2["COMPLETE_HEADER"] = "complete";
@@ -46,6 +47,8 @@ var EventConstants;
46
47
  EventConstants2["SERVICE_DESTINATION_SCHEME"] = "srv";
47
48
  EventConstants2["STREAM_DESTINATION_PREFIX"] = "stream://";
48
49
  EventConstants2["STREAM_DESTINATION_SCHEME"] = "stream";
50
+ EventConstants2["REPLY_DESTINATION_PREFIX"] = "reply://";
51
+ EventConstants2["REPLY_DESTINATION_SCHEME"] = "reply";
49
52
  EventConstants2["CONTENT_JSON"] = "application/json";
50
53
  EventConstants2["CONTENT_TEXT"] = "text/plain";
51
54
  EventConstants2["TRACEPARENT_HEADER"] = "traceparent";
@@ -55,8 +58,8 @@ var EventConstants;
55
58
  // packages/core/src/internal/api/event/StompConnectionManager.ts
56
59
  import { RxStomp } from "@stomp/rx-stomp";
57
60
  import { ReconnectionTimeMode } from "@stomp/stompjs";
58
- import { v4 as uuidv4 } from "uuid";
59
61
  import debug from "debug";
62
+ import { v4 as uuidv4 } from "uuid";
60
63
 
61
64
  class StompConnectionManager {
62
65
  lastWebsocketError = null;
@@ -69,9 +72,10 @@ class StompConnectionManager {
69
72
  initialConnectionSuccessful = false;
70
73
  debugLogger = debug("kinoitc:stomp");
71
74
  uuidv4 = uuidv4();
72
- replyToId = uuidv4();
73
- _replyToCri = "srv://" /* SERVICE_DESTINATION_PREFIX */ + this.replyToId + ":" + this.uuidv4 + "@kinoitc.js.EventBus/replyHandler";
75
+ _replyToCri = null;
76
+ serverHeadersSubscription = null;
74
77
  deactivationHandler = null;
78
+ replyToCriChangedHandler = null;
75
79
  get active() {
76
80
  return !!this.rxStomp;
77
81
  }
@@ -99,31 +103,21 @@ class StompConnectionManager {
99
103
  this.initialConnectionSuccessful = false;
100
104
  this.lastWebsocketError = null;
101
105
  this.maxConnectionAttemptsReached = false;
106
+ this._replyToCri = null;
107
+ this.serverHeadersSubscription?.unsubscribe();
108
+ this.serverHeadersSubscription = null;
102
109
  const url = "ws" + (connectionInfo.useSSL ? "s" : "") + "://" + connectionInfo.host + (connectionInfo.port ? ":" + connectionInfo.port : "") + "/v1";
103
110
  this.rxStomp = new RxStomp;
104
- let connectHeadersInternal = typeof connectionInfo.connectHeaders !== "function" && connectionInfo.connectHeaders != null ? connectionInfo.connectHeaders : {};
105
111
  const stompConfig = {
106
112
  brokerURL: url,
107
- connectHeaders: connectHeadersInternal,
113
+ connectHeaders: {
114
+ ["session-keep-alive" /* SESSION_KEEP_ALIVE_HEADER */]: connectionInfo.sessionKeepAlive
115
+ },
108
116
  heartbeatIncoming: 120000,
109
117
  heartbeatOutgoing: 30000,
110
118
  reconnectDelay: this.INITIAL_RECONNECT_DELAY,
119
+ webSocketFactory: connectionInfo.webSocketFactory,
111
120
  beforeConnect: async () => {
112
- if (typeof connectionInfo.connectHeaders === "function") {
113
- const headers = await connectionInfo.connectHeaders();
114
- for (const key in headers) {
115
- connectHeadersInternal[key] = headers[key];
116
- }
117
- }
118
- if (connectionInfo.disableStickySession) {
119
- connectHeadersInternal["disable-sticky-session" /* DISABLE_STICKY_SESSION_HEADER */] = "true";
120
- }
121
- if (connectHeadersInternal["reply-to-id" /* REPLY_TO_ID_HEADER */]) {
122
- this.replyToId = connectHeadersInternal["reply-to-id" /* REPLY_TO_ID_HEADER */];
123
- this._replyToCri = "srv://" /* SERVICE_DESTINATION_PREFIX */ + this.replyToId + ":" + this.uuidv4 + "@kinoitc.js.EventBus/replyHandler";
124
- } else {
125
- connectHeadersInternal["reply-to-id" /* REPLY_TO_ID_HEADER */] = this.replyToId;
126
- }
127
121
  if (connectionInfo?.maxConnectionAttempts) {
128
122
  this.connectionAttempts++;
129
123
  if (this.connectionAttempts > connectionInfo.maxConnectionAttempts) {
@@ -165,36 +159,29 @@ class StompConnectionManager {
165
159
  this.rxStomp = null;
166
160
  reject(message);
167
161
  });
168
- const serverHeadersSubscription = this.rxStomp.serverHeaders$.subscribe((value) => {
169
- let connectedInfoJson = value["connected-info" /* CONNECTED_INFO_HEADER */];
170
- if (connectedInfoJson != null) {
171
- const connectedInfo = JSON.parse(connectedInfoJson);
172
- if (!connectionInfo.disableStickySession) {
173
- serverHeadersSubscription.unsubscribe();
174
- if (connectedInfo.sessionId != null && connectedInfo.replyToId != null) {
175
- if (connectionInfo.connectHeaders != null) {
176
- for (let key in connectHeadersInternal) {
177
- delete connectHeadersInternal[key];
178
- }
179
- }
180
- connectHeadersInternal["session" /* SESSION_HEADER */] = connectedInfo.sessionId;
181
- resolve(connectedInfo);
182
- } else {
183
- reject("Server did not return proper data for successful login");
184
- }
185
- } else if (typeof connectionInfo.connectHeaders === "function") {
186
- for (let key in connectHeadersInternal) {
187
- delete connectHeadersInternal[key];
188
- }
189
- if (!this.initialConnectionSuccessful) {
190
- resolve(connectedInfo);
191
- }
192
- } else if (typeof connectionInfo.connectHeaders === "object") {
193
- serverHeadersSubscription.unsubscribe();
194
- resolve(connectedInfo);
162
+ this.serverHeadersSubscription = this.rxStomp.serverHeaders$.subscribe((value) => {
163
+ const connectedInfoJson = value["connected-info" /* CONNECTED_INFO_HEADER */];
164
+ const firstConnect = this._replyToCri == null;
165
+ if (connectedInfoJson == null) {
166
+ if (firstConnect) {
167
+ reject("Server did not return proper data for successful login");
195
168
  }
196
- } else {
197
- reject("Server did not return proper data for successful login");
169
+ return;
170
+ }
171
+ const connectedInfo = JSON.parse(connectedInfoJson);
172
+ if (connectedInfo.replyToId == null) {
173
+ if (firstConnect) {
174
+ reject("Server did not return a replyToId for successful login");
175
+ }
176
+ return;
177
+ }
178
+ const newReplyToCri = "reply://" /* REPLY_DESTINATION_PREFIX */ + connectedInfo.replyToId + ":" + this.uuidv4 + "@kinoitc.js.EventBus/replyHandler";
179
+ if (firstConnect) {
180
+ this._replyToCri = newReplyToCri;
181
+ resolve(connectedInfo);
182
+ } else if (this._replyToCri !== newReplyToCri) {
183
+ this._replyToCri = newReplyToCri;
184
+ this.replyToCriChangedHandler?.(newReplyToCri);
198
185
  }
199
186
  });
200
187
  this.rxStomp.activate();
@@ -203,6 +190,8 @@ class StompConnectionManager {
203
190
  async deactivate(force) {
204
191
  if (this.rxStomp) {
205
192
  await this.rxStomp.deactivate({ force });
193
+ this.serverHeadersSubscription?.unsubscribe();
194
+ this.serverHeadersSubscription = null;
206
195
  if (this.deactivationHandler) {
207
196
  this.deactivationHandler();
208
197
  }
@@ -283,6 +272,10 @@ class EventBus {
283
272
  this.stompConnectionManager.deactivationHandler = () => {
284
273
  this.cleanup();
285
274
  };
275
+ this.stompConnectionManager.replyToCriChangedHandler = (replyToCri) => {
276
+ this.replyToCri = replyToCri;
277
+ this.resetRequestReplies("Reply destination changed");
278
+ };
286
279
  }
287
280
  isConnectionActive() {
288
281
  return this.stompConnectionManager.active;
@@ -350,7 +343,7 @@ class EventBus {
350
343
  })).subscribe({
351
344
  next(value) {
352
345
  if (value.hasHeader("control" /* CONTROL_HEADER */)) {
353
- if (value.headers.get("control" /* CONTROL_HEADER */) === "complete") {
346
+ if (value.headers.get("control" /* CONTROL_HEADER */) === "complete" /* CONTROL_VALUE_COMPLETE */) {
354
347
  serverSignaledCompletion = true;
355
348
  subscriber.complete();
356
349
  } else {
@@ -394,8 +387,16 @@ class EventBus {
394
387
  return this._observe(cri);
395
388
  }
396
389
  cleanup() {
390
+ this.resetRequestReplies("Connection disconnected");
391
+ if (this.errorSubjectSubscription) {
392
+ this.errorSubjectSubscription.unsubscribe();
393
+ this.errorSubjectSubscription = null;
394
+ }
395
+ this.serverInfo = null;
396
+ }
397
+ resetRequestReplies(reason) {
397
398
  if (this.requestRepliesSubject != null) {
398
- this.requestRepliesSubject.error(new Error("Connection disconnected"));
399
+ this.requestRepliesSubject.error(new Error(reason));
399
400
  if (this.requestRepliesSubscription != null) {
400
401
  this.requestRepliesSubscription.unsubscribe();
401
402
  this.requestRepliesSubscription = null;
@@ -403,11 +404,6 @@ class EventBus {
403
404
  this.requestRepliesSubject = null;
404
405
  this.requestRepliesObservable = null;
405
406
  }
406
- if (this.errorSubjectSubscription) {
407
- this.errorSubjectSubscription.unsubscribe();
408
- this.errorSubjectSubscription = null;
409
- }
410
- this.serverInfo = null;
411
407
  }
412
408
  createSendUnavailableError() {
413
409
  let ret = "You must call connect on the event bus before sending any request";
@@ -894,7 +890,7 @@ import { first, map as map2 } from "rxjs/operators";
894
890
  // packages/core/package.json
895
891
  var package_default = {
896
892
  name: "@kinotic-ai/core",
897
- version: "1.2.1",
893
+ version: "1.3.0",
898
894
  type: "module",
899
895
  files: [
900
896
  "dist"
@@ -922,44 +918,44 @@ var package_default = {
922
918
  },
923
919
  scripts: {
924
920
  "type-check": "tsc --noEmit",
925
- test: "vitest run",
921
+ test: "vitest run --mode development",
926
922
  coverage: "vitest run --coverage",
927
923
  "ui-test": "vitest --ui --coverage.enabled=true --mode development"
928
924
  },
929
925
  devDependencies: {
930
- "@opentelemetry/exporter-trace-otlp-grpc": "^0.212.0",
931
- "@opentelemetry/resources": "^2.5.1",
932
- "@opentelemetry/sdk-metrics": "^2.5.1",
933
- "@opentelemetry/sdk-node": "^0.212.0",
934
- "@opentelemetry/sdk-trace-node": "^2.5.1",
935
- "@types/node": "^25.3.2",
926
+ "@opentelemetry/exporter-trace-otlp-grpc": "^0.214.0",
927
+ "@opentelemetry/resources": "^2.6.1",
928
+ "@opentelemetry/sdk-metrics": "^2.6.1",
929
+ "@opentelemetry/sdk-node": "^0.214.0",
930
+ "@opentelemetry/sdk-trace-node": "^2.6.1",
931
+ "@types/node": "^25.6.0",
936
932
  "@types/uuid": "^11.0.0",
937
933
  "@types/ws": "^8.18.1",
938
- "@vitest/coverage-v8": "^4.0.18",
939
- "@vitest/runner": "^4.0.18",
940
- "@vitest/ui": "^4.0.18",
941
- allure: "^3.3.1",
942
- "allure-vitest": "^3.5.0",
943
- dotenv: "^17.3.1",
944
- "properties-file": "^3.6.4",
945
- testcontainers: "^11.12.0",
934
+ "@vitest/coverage-v8": "^4.1.4",
935
+ "@vitest/runner": "^4.1.4",
936
+ "@vitest/ui": "^4.1.4",
937
+ allure: "^3.4.1",
938
+ "allure-vitest": "^3.7.1",
939
+ dotenv: "^17.4.1",
940
+ "properties-file": "^4.0.0",
941
+ testcontainers: "^11.14.0",
946
942
  tslib: "^2.8.1",
947
- typescript: "^5.9.3",
948
- vitest: "^4.0.18",
949
- ws: "^8.19.0"
943
+ typescript: "^6.0.2",
944
+ vitest: "^4.1.4",
945
+ ws: "^8.20.0"
950
946
  },
951
947
  dependencies: {
952
- "@opentelemetry/api": "^1.9.0",
948
+ "@opentelemetry/api": "^1.9.1",
953
949
  "@opentelemetry/semantic-conventions": "^1.40.0",
954
950
  "@stomp/rx-stomp": "^2.3.0",
955
951
  "@stomp/stompjs": "^7.3.0",
956
- "@types/debug": "^4.1.12",
952
+ "@types/debug": "^4.1.13",
957
953
  debug: "^4.4.3",
958
954
  "p-tap": "^4.0.0",
959
955
  "reflect-metadata": "^0.2.2",
960
956
  rxjs: "^7.8.2",
961
957
  "typescript-optional": "3.0.0-alpha.3",
962
- uuid: "^11.0.0"
958
+ uuid: "^13.0.0"
963
959
  },
964
960
  peerDependenciesMeta: {
965
961
  typescript: {
@@ -1143,16 +1139,6 @@ class KinoticSingleton {
1143
1139
  }
1144
1140
  }
1145
1141
  var Kinotic = new KinoticSingleton;
1146
- // packages/core/src/api/KinoticProjectConfig.ts
1147
- class KinoticProjectConfig {
1148
- name;
1149
- description;
1150
- application;
1151
- entitiesPaths;
1152
- generatedPath;
1153
- fileExtensionForImports = ".js";
1154
- validate;
1155
- }
1156
1142
  // packages/core/src/api/crud/AbstractIterablePage.ts
1157
1143
  class AbstractIterablePage {
1158
1144
  pageable;
@@ -1254,6 +1240,9 @@ class CrudServiceProxy {
1254
1240
  create(entity) {
1255
1241
  return this.serviceProxy.invoke("create", [entity]);
1256
1242
  }
1243
+ createSync(entity) {
1244
+ return this.serviceProxy.invoke("createSync", [entity]);
1245
+ }
1257
1246
  deleteById(id) {
1258
1247
  return this.serviceProxy.invoke("deleteById", [id]);
1259
1248
  }
@@ -1395,7 +1384,6 @@ class AuthorizationError extends KinoticError {
1395
1384
  }
1396
1385
  // packages/core/src/api/security/ConnectedInfo.ts
1397
1386
  class ConnectedInfo {
1398
- sessionId;
1399
1387
  replyToId;
1400
1388
  participant;
1401
1389
  }
@@ -1403,11 +1391,15 @@ class ConnectedInfo {
1403
1391
  class Participant {
1404
1392
  id;
1405
1393
  tenantId;
1394
+ authScopeType;
1395
+ authScopeId;
1406
1396
  metadata;
1407
1397
  roles;
1408
- constructor(id, tenantId, metadata, roles) {
1398
+ constructor(id, tenantId, authScopeType, authScopeId, metadata, roles) {
1409
1399
  this.id = id;
1410
1400
  this.tenantId = tenantId;
1401
+ this.authScopeType = authScopeType;
1402
+ this.authScopeId = authScopeId;
1411
1403
  this.metadata = metadata || new Map;
1412
1404
  this.roles = roles || [];
1413
1405
  }
@@ -1426,6 +1418,7 @@ export {
1426
1418
  Version,
1427
1419
  TextEventFactory,
1428
1420
  Sort,
1421
+ SessionKeepAliveMode,
1429
1422
  ServiceRegistry,
1430
1423
  ServerInfo,
1431
1424
  Scope,
@@ -1437,7 +1430,6 @@ export {
1437
1430
  OffsetPageable,
1438
1431
  NullHandling,
1439
1432
  KinoticSingleton,
1440
- KinoticProjectConfig,
1441
1433
  KinoticError,
1442
1434
  Kinotic,
1443
1435
  JsonEventFactory,
@@ -1454,7 +1446,6 @@ export {
1454
1446
  Context,
1455
1447
  ConnectionInfo,
1456
1448
  ConnectedInfo,
1457
- ConnectHeaders,
1458
1449
  CONTEXT_METADATA_KEY,
1459
1450
  AuthorizationError,
1460
1451
  AuthenticationError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kinotic-ai/core",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"
@@ -28,44 +28,44 @@
28
28
  },
29
29
  "scripts": {
30
30
  "type-check": "tsc --noEmit",
31
- "test": "vitest run",
31
+ "test": "vitest run --mode development",
32
32
  "coverage": "vitest run --coverage",
33
33
  "ui-test": "vitest --ui --coverage.enabled=true --mode development"
34
34
  },
35
35
  "devDependencies": {
36
- "@opentelemetry/exporter-trace-otlp-grpc": "^0.212.0",
37
- "@opentelemetry/resources": "^2.5.1",
38
- "@opentelemetry/sdk-metrics": "^2.5.1",
39
- "@opentelemetry/sdk-node": "^0.212.0",
40
- "@opentelemetry/sdk-trace-node": "^2.5.1",
41
- "@types/node": "^25.3.2",
36
+ "@opentelemetry/exporter-trace-otlp-grpc": "^0.214.0",
37
+ "@opentelemetry/resources": "^2.6.1",
38
+ "@opentelemetry/sdk-metrics": "^2.6.1",
39
+ "@opentelemetry/sdk-node": "^0.214.0",
40
+ "@opentelemetry/sdk-trace-node": "^2.6.1",
41
+ "@types/node": "^25.6.0",
42
42
  "@types/uuid": "^11.0.0",
43
43
  "@types/ws": "^8.18.1",
44
- "@vitest/coverage-v8": "^4.0.18",
45
- "@vitest/runner": "^4.0.18",
46
- "@vitest/ui": "^4.0.18",
47
- "allure": "^3.3.1",
48
- "allure-vitest": "^3.5.0",
49
- "dotenv": "^17.3.1",
50
- "properties-file": "^3.6.4",
51
- "testcontainers": "^11.12.0",
44
+ "@vitest/coverage-v8": "^4.1.4",
45
+ "@vitest/runner": "^4.1.4",
46
+ "@vitest/ui": "^4.1.4",
47
+ "allure": "^3.4.1",
48
+ "allure-vitest": "^3.7.1",
49
+ "dotenv": "^17.4.1",
50
+ "properties-file": "^4.0.0",
51
+ "testcontainers": "^11.14.0",
52
52
  "tslib": "^2.8.1",
53
- "typescript": "^5.9.3",
54
- "vitest": "^4.0.18",
55
- "ws": "^8.19.0"
53
+ "typescript": "^6.0.2",
54
+ "vitest": "^4.1.4",
55
+ "ws": "^8.20.0"
56
56
  },
57
57
  "dependencies": {
58
- "@opentelemetry/api": "^1.9.0",
58
+ "@opentelemetry/api": "^1.9.1",
59
59
  "@opentelemetry/semantic-conventions": "^1.40.0",
60
60
  "@stomp/rx-stomp": "^2.3.0",
61
61
  "@stomp/stompjs": "^7.3.0",
62
- "@types/debug": "^4.1.12",
62
+ "@types/debug": "^4.1.13",
63
63
  "debug": "^4.4.3",
64
64
  "p-tap": "^4.0.0",
65
65
  "reflect-metadata": "^0.2.2",
66
66
  "rxjs": "^7.8.2",
67
67
  "typescript-optional": "3.0.0-alpha.3",
68
- "uuid": "^11.0.0"
68
+ "uuid": "^13.0.0"
69
69
  },
70
70
  "peerDependenciesMeta": {
71
71
  "typescript": {