@ultrst-armada/kloks 1.4.2 → 1.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -20
- package/packages/audio-midi/dist/index.d.ts +23 -14
- package/packages/audio-midi/dist/index.js +170 -45
- package/packages/audio-midi/kloks.manifest.json +8 -8
- package/packages/audio-midi/package.json +1 -1
- package/packages/backend/dist/{WebSocketTransport-CxUzUfwl.d.ts → WebSocketTransport-CHmkJ0tj.d.ts} +109 -2
- package/packages/backend/dist/auth/index.js +6 -6
- package/packages/backend/dist/chunk-5336MTXD.js +597 -0
- package/packages/backend/dist/{chunk-T3NYNBDC.js → chunk-5E6JRFUH.js} +201 -3
- package/packages/backend/dist/{chunk-XTWHEZWS.js → chunk-7B24ZWB3.js} +16 -1
- package/packages/backend/dist/{chunk-DZ4ZGIZM.js → chunk-JXUSBKJ2.js} +91 -654
- package/packages/backend/dist/{chunk-FO6WYLW4.js → chunk-KI3LHMUN.js} +133 -46
- package/packages/backend/dist/{chunk-4ZFXEFOE.js → chunk-MPYASJ5A.js} +2 -87
- package/packages/backend/dist/{chunk-KX3OTBQ3.js → chunk-OZDYAEH4.js} +0 -114
- package/packages/backend/dist/chunk-TY32TUNI.js +376 -0
- package/packages/backend/dist/chunk-VDROUXFT.js +116 -0
- package/packages/backend/dist/chunk-ZDH4XI7W.js +140 -0
- package/packages/backend/dist/client/index.d.ts +2 -3
- package/packages/backend/dist/connectivity/index.d.ts +18 -6
- package/packages/backend/dist/connectivity/index.js +3 -7
- package/packages/backend/dist/index.d.ts +3 -6
- package/packages/backend/dist/index.js +101 -91
- package/packages/backend/dist/krnl-services.d.ts +84 -7
- package/packages/backend/dist/krnl-services.js +40 -12
- package/packages/backend/dist/services/index.d.ts +4 -6
- package/packages/backend/dist/services/index.js +2 -2
- package/packages/backend/dist/srkl/index.js +6 -4
- package/packages/backend/dist/storage/index.js +7 -7
- package/packages/backend/dist/workspace/index.js +2 -2
- package/packages/backend/kloks.manifest.json +47 -45
- package/packages/backend/package.json +3 -8
- package/packages/client/package.json +1 -1
- package/packages/contracts/dist/index.js +5 -10
- package/packages/contracts/package.json +1 -1
- package/packages/functions/dist/index.d.ts +12 -1
- package/packages/functions/dist/index.js +132 -24
- package/packages/functions/package.json +1 -1
- package/packages/orchestrations/dist/index.d.ts +33 -31
- package/packages/orchestrations/dist/index.js +81 -203
- package/packages/orchestrations/kloks.manifest.json +10 -2
- package/packages/orchestrations/package.json +2 -2
- package/packages/services/dist/index.d.ts +51 -1
- package/packages/services/dist/index.js +249 -51
- package/packages/services/kloks.manifest.json +69 -33
- package/packages/services/package.json +1 -1
- package/packages/state-graph/dist/{BaseGraph-BvkSYOTw.d.ts → BaseGraph-Bd5F1NMP.d.ts} +1 -1
- package/packages/state-graph/dist/{BaseNode-DTcd3K1B.d.ts → BaseNode-DKpGXMy8.d.ts} +6 -0
- package/packages/state-graph/dist/{CompatibilityMatrix-Dj-tCWUA.d.ts → CompatibilityMatrix-TiEJ3j3_.d.ts} +1 -1
- package/packages/state-graph/dist/domain/index.d.ts +3 -3
- package/packages/state-graph/dist/domain/index.js +0 -1
- package/packages/state-graph/dist/index.d.ts +40 -8
- package/packages/state-graph/dist/index.js +193 -44
- package/packages/state-graph/dist/rules/index.d.ts +3 -3
- package/packages/state-graph/dist/rules/index.js +0 -1
- package/packages/state-graph/dist/spatial/index.d.ts +4 -4
- package/packages/state-graph/dist/spatial/index.js +0 -1
- package/packages/state-graph/dist/state/index.js +1 -3
- package/packages/state-graph/dist/store/index.d.ts +4 -4
- package/packages/state-graph/dist/store/index.js +0 -1
- package/packages/state-graph/dist/{types-VDoc4FIc.d.ts → types-D5kQSa65.d.ts} +2 -2
- package/packages/state-graph/kloks.manifest.json +26 -10
- package/packages/state-graph/package.json +2 -8
- package/packages/backend/dist/IdempotencyManager-B5MjK871.d.ts +0 -16
- package/packages/backend/dist/chunk-25Y7AVTP.js +0 -55
- package/packages/backend/dist/chunk-3EPY34R6.js +0 -67
- package/packages/backend/dist/chunk-7KNGX522.js +0 -220
- package/packages/backend/dist/chunk-FZKBTF34.js +0 -15
- package/packages/backend/dist/chunk-QGHDJGYS.js +0 -92
- package/packages/backend/dist/chunk-UPFKBBQI.js +0 -138
- package/packages/backend/dist/chunk-VQEFULWT.js +0 -88
- package/packages/backend/dist/execution/index.d.ts +0 -124
- package/packages/backend/dist/execution/index.js +0 -20
- package/packages/backend/dist/types-BERZJ93O.d.ts +0 -110
- package/packages/state-graph/dist/chunk-3RG5ZIWI.js +0 -10
- package/packages/state-graph/dist/chunk-Q5I42UX7.js +0 -0
- package/packages/state-graph/dist/chunk-ZTHBPV24.js +0 -267
- package/packages/state-graph/dist/execution/index.d.ts +0 -152
- package/packages/state-graph/dist/execution/index.js +0 -16
- /package/packages/state-graph/dist/{chunk-SHIBCPGQ.js → chunk-EW4ACSZB.js} +0 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
// ../../node_modules/.pnpm/@ultrst-armada+krnl@1.0.3/node_modules/@ultrst-armada/krnl/packages/core/dist/index.js
|
|
2
|
+
var ServiceStateStore = class {
|
|
3
|
+
state;
|
|
4
|
+
listeners = /* @__PURE__ */ new Set();
|
|
5
|
+
constructor(initialState) {
|
|
6
|
+
this.state = { ...initialState };
|
|
7
|
+
}
|
|
8
|
+
getState() {
|
|
9
|
+
return Object.freeze({ ...this.state });
|
|
10
|
+
}
|
|
11
|
+
setState(mutation) {
|
|
12
|
+
this.state = { ...this.state, ...mutation };
|
|
13
|
+
const snapshot = this.getState();
|
|
14
|
+
for (const listener of this.listeners) {
|
|
15
|
+
listener(snapshot, mutation);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
subscribe(listener) {
|
|
19
|
+
this.listeners.add(listener);
|
|
20
|
+
return () => {
|
|
21
|
+
this.listeners.delete(listener);
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
reset(newState) {
|
|
25
|
+
this.state = { ...newState || {} };
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
var ServiceEventBus = class {
|
|
29
|
+
handlers = /* @__PURE__ */ new Map();
|
|
30
|
+
on(event, handler) {
|
|
31
|
+
if (!this.handlers.has(event)) {
|
|
32
|
+
this.handlers.set(event, /* @__PURE__ */ new Set());
|
|
33
|
+
}
|
|
34
|
+
const set = this.handlers.get(event);
|
|
35
|
+
set.add(handler);
|
|
36
|
+
return () => {
|
|
37
|
+
set.delete(handler);
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
emit(event, payload) {
|
|
41
|
+
const set = this.handlers.get(event);
|
|
42
|
+
if (!set) return;
|
|
43
|
+
for (const handler of Array.from(set)) {
|
|
44
|
+
try {
|
|
45
|
+
handler(payload);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
console.error(`[ServiceEventBus] Handler error for '${event}':`, err);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
clear() {
|
|
52
|
+
this.handlers.clear();
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var TRANSITIONS = {
|
|
56
|
+
uninitialized: ["initializing"],
|
|
57
|
+
initializing: ["ready", "uninitialized"],
|
|
58
|
+
ready: ["starting"],
|
|
59
|
+
starting: ["running", "ready"],
|
|
60
|
+
running: ["stopping"],
|
|
61
|
+
stopping: ["stopped", "running"],
|
|
62
|
+
stopped: ["starting"]
|
|
63
|
+
};
|
|
64
|
+
var LifecycleManager = class {
|
|
65
|
+
status = "uninitialized";
|
|
66
|
+
getStatus() {
|
|
67
|
+
return this.status;
|
|
68
|
+
}
|
|
69
|
+
canTransitionTo(next) {
|
|
70
|
+
return TRANSITIONS[this.status].includes(next);
|
|
71
|
+
}
|
|
72
|
+
transitionTo(next) {
|
|
73
|
+
if (!this.canTransitionTo(next)) {
|
|
74
|
+
throw new Error("[Lifecycle] Invalid transition '" + this.status + "' -> '" + next + "'.");
|
|
75
|
+
}
|
|
76
|
+
this.status = next;
|
|
77
|
+
}
|
|
78
|
+
setStatus(next) {
|
|
79
|
+
this.transitionTo(next);
|
|
80
|
+
}
|
|
81
|
+
assertRunning(serviceName) {
|
|
82
|
+
if (this.status !== "running") {
|
|
83
|
+
throw new Error(
|
|
84
|
+
"[Service:" + serviceName + "] Cannot invoke operation while status is '" + this.status + "' (expected 'running')."
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
var ServiceInstance = class {
|
|
90
|
+
constructor(definition) {
|
|
91
|
+
this.definition = definition;
|
|
92
|
+
this.id = definition.id;
|
|
93
|
+
this.name = definition.name;
|
|
94
|
+
this.version = definition.version;
|
|
95
|
+
this.lifecycle = new LifecycleManager();
|
|
96
|
+
this.eventBus = new ServiceEventBus();
|
|
97
|
+
if (definition.state) {
|
|
98
|
+
this.stateStore = new ServiceStateStore(definition.state.initial);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
definition;
|
|
102
|
+
id;
|
|
103
|
+
name;
|
|
104
|
+
version;
|
|
105
|
+
stateStore;
|
|
106
|
+
eventBus;
|
|
107
|
+
lifecycle;
|
|
108
|
+
initialized = false;
|
|
109
|
+
disposed = false;
|
|
110
|
+
getDefinition() {
|
|
111
|
+
return this.definition;
|
|
112
|
+
}
|
|
113
|
+
getStatus() {
|
|
114
|
+
return this.lifecycle.getStatus();
|
|
115
|
+
}
|
|
116
|
+
async start() {
|
|
117
|
+
if (this.disposed) throw new Error("[Service:" + this.name + "] Cannot start a disposed service.");
|
|
118
|
+
const current = this.lifecycle.getStatus();
|
|
119
|
+
if (current === "running") return;
|
|
120
|
+
if (current === "initializing" || current === "starting" || current === "stopping") {
|
|
121
|
+
throw new Error("[Service:" + this.name + "] Cannot start while status is '" + current + "'.");
|
|
122
|
+
}
|
|
123
|
+
if (!this.initialized) {
|
|
124
|
+
this.lifecycle.transitionTo("initializing");
|
|
125
|
+
try {
|
|
126
|
+
await this.definition.lifecycle?.onInit?.();
|
|
127
|
+
this.initialized = true;
|
|
128
|
+
this.lifecycle.transitionTo("ready");
|
|
129
|
+
} catch (error) {
|
|
130
|
+
this.lifecycle.transitionTo("uninitialized");
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
this.lifecycle.transitionTo("starting");
|
|
135
|
+
try {
|
|
136
|
+
await this.definition.lifecycle?.onStart?.();
|
|
137
|
+
this.lifecycle.transitionTo("running");
|
|
138
|
+
} catch (error) {
|
|
139
|
+
this.lifecycle.transitionTo("ready");
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
async stop() {
|
|
144
|
+
const current = this.lifecycle.getStatus();
|
|
145
|
+
if (current === "stopped") return;
|
|
146
|
+
if (current !== "running") {
|
|
147
|
+
throw new Error("[Service:" + this.name + "] Cannot stop while status is '" + current + "'.");
|
|
148
|
+
}
|
|
149
|
+
this.lifecycle.transitionTo("stopping");
|
|
150
|
+
try {
|
|
151
|
+
await this.definition.lifecycle?.onStop?.();
|
|
152
|
+
this.lifecycle.transitionTo("stopped");
|
|
153
|
+
} catch (error) {
|
|
154
|
+
this.lifecycle.transitionTo("running");
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async dispose() {
|
|
159
|
+
if (this.disposed) return;
|
|
160
|
+
if (this.lifecycle.getStatus() === "running") await this.stop();
|
|
161
|
+
await this.definition.lifecycle?.onDispose?.();
|
|
162
|
+
this.disposed = true;
|
|
163
|
+
}
|
|
164
|
+
async call(op, input, ctx = {}) {
|
|
165
|
+
this.lifecycle.assertRunning(this.name);
|
|
166
|
+
const operation = this.definition.operations[op];
|
|
167
|
+
if (!operation) {
|
|
168
|
+
throw new Error("[Service:" + this.name + "] Operation '" + op + "' not found.");
|
|
169
|
+
}
|
|
170
|
+
return await operation.handler(input, ctx);
|
|
171
|
+
}
|
|
172
|
+
getState() {
|
|
173
|
+
return this.stateStore?.getState() ?? Object.freeze({});
|
|
174
|
+
}
|
|
175
|
+
setState(mutation) {
|
|
176
|
+
if (!this.stateStore) {
|
|
177
|
+
this.stateStore = new ServiceStateStore({ ...mutation });
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
this.stateStore.setState(mutation);
|
|
181
|
+
}
|
|
182
|
+
on(event, handler) {
|
|
183
|
+
return this.eventBus.on(event, handler);
|
|
184
|
+
}
|
|
185
|
+
emit(event, payload) {
|
|
186
|
+
this.eventBus.emit(event, payload);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
var ServiceProxy = class {
|
|
190
|
+
constructor(definition, adapter, endpoint) {
|
|
191
|
+
this.definition = definition;
|
|
192
|
+
this.adapter = adapter;
|
|
193
|
+
this.endpoint = endpoint;
|
|
194
|
+
if (!definition) {
|
|
195
|
+
throw new Error("[ServiceProxy] Missing service definition.");
|
|
196
|
+
}
|
|
197
|
+
if (!adapter || typeof adapter.send !== "function") {
|
|
198
|
+
throw new Error("[ServiceProxy:" + (definition.name || "unknown") + "] Missing or invalid protocol adapter.");
|
|
199
|
+
}
|
|
200
|
+
if (!endpoint || typeof endpoint.address !== "string" || endpoint.address.length === 0) {
|
|
201
|
+
throw new Error("[ServiceProxy:" + (definition.name || "unknown") + "] Missing or invalid endpoint descriptor.");
|
|
202
|
+
}
|
|
203
|
+
this.id = definition.id;
|
|
204
|
+
this.name = definition.name;
|
|
205
|
+
this.version = definition.version;
|
|
206
|
+
this.stateCache = { ...definition.state?.initial ?? {} };
|
|
207
|
+
}
|
|
208
|
+
definition;
|
|
209
|
+
adapter;
|
|
210
|
+
endpoint;
|
|
211
|
+
id;
|
|
212
|
+
name;
|
|
213
|
+
version;
|
|
214
|
+
stateCache;
|
|
215
|
+
eventHandlers = /* @__PURE__ */ new Map();
|
|
216
|
+
lifecycle = new LifecycleManager();
|
|
217
|
+
disposed = false;
|
|
218
|
+
getDefinition() {
|
|
219
|
+
return this.definition;
|
|
220
|
+
}
|
|
221
|
+
getStatus() {
|
|
222
|
+
return this.lifecycle.getStatus();
|
|
223
|
+
}
|
|
224
|
+
async start() {
|
|
225
|
+
if (this.disposed) {
|
|
226
|
+
throw new Error("[ServiceProxy:" + this.name + "] Cannot start a disposed service proxy.");
|
|
227
|
+
}
|
|
228
|
+
const status = this.lifecycle.getStatus();
|
|
229
|
+
if (status === "running") return;
|
|
230
|
+
if (status === "stopping" || status === "starting" || status === "initializing") {
|
|
231
|
+
throw new Error("[ServiceProxy:" + this.name + "] Cannot start while status is '" + status + "'.");
|
|
232
|
+
}
|
|
233
|
+
if (status === "uninitialized") {
|
|
234
|
+
this.lifecycle.transitionTo("initializing");
|
|
235
|
+
this.lifecycle.transitionTo("ready");
|
|
236
|
+
}
|
|
237
|
+
this.lifecycle.transitionTo("starting");
|
|
238
|
+
this.lifecycle.transitionTo("running");
|
|
239
|
+
}
|
|
240
|
+
async stop() {
|
|
241
|
+
const status = this.lifecycle.getStatus();
|
|
242
|
+
if (status === "stopped") return;
|
|
243
|
+
if (status !== "running") {
|
|
244
|
+
throw new Error("[ServiceProxy:" + this.name + "] Cannot stop while status is '" + status + "'.");
|
|
245
|
+
}
|
|
246
|
+
this.lifecycle.transitionTo("stopping");
|
|
247
|
+
this.lifecycle.transitionTo("stopped");
|
|
248
|
+
}
|
|
249
|
+
async dispose() {
|
|
250
|
+
if (this.disposed) return;
|
|
251
|
+
if (this.lifecycle.getStatus() === "running") await this.stop();
|
|
252
|
+
this.disposed = true;
|
|
253
|
+
}
|
|
254
|
+
async call(op, input, ctx = {}) {
|
|
255
|
+
if (!this.definition.operations[op]) {
|
|
256
|
+
throw new Error("[ServiceProxy:" + this.name + "] Operation '" + op + "' not found.");
|
|
257
|
+
}
|
|
258
|
+
if (this.lifecycle.getStatus() !== "running") {
|
|
259
|
+
await this.start();
|
|
260
|
+
}
|
|
261
|
+
if (!this.adapter.send) {
|
|
262
|
+
throw new Error("[ServiceProxy:" + this.name + "] Adapter does not support send().");
|
|
263
|
+
}
|
|
264
|
+
const envelope = {
|
|
265
|
+
id: "req-" + Date.now() + "-" + Math.random().toString(36).slice(2, 7),
|
|
266
|
+
type: "request",
|
|
267
|
+
operation: op,
|
|
268
|
+
payload: input,
|
|
269
|
+
timestamp: Date.now()
|
|
270
|
+
};
|
|
271
|
+
const response = await this.adapter.send(this.endpoint, envelope);
|
|
272
|
+
if (response.error) {
|
|
273
|
+
throw new Error("[ServiceProxy:" + this.name + "] Remote error: " + response.error);
|
|
274
|
+
}
|
|
275
|
+
return response.payload;
|
|
276
|
+
}
|
|
277
|
+
getState() {
|
|
278
|
+
return Object.freeze({ ...this.stateCache });
|
|
279
|
+
}
|
|
280
|
+
setState(mutation) {
|
|
281
|
+
this.stateCache = { ...this.stateCache, ...mutation };
|
|
282
|
+
}
|
|
283
|
+
on(event, handler) {
|
|
284
|
+
if (!this.eventHandlers.has(event)) this.eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
285
|
+
const handlers = this.eventHandlers.get(event);
|
|
286
|
+
handlers.add(handler);
|
|
287
|
+
return () => handlers.delete(handler);
|
|
288
|
+
}
|
|
289
|
+
emit(event, payload) {
|
|
290
|
+
const handlers = this.eventHandlers.get(event);
|
|
291
|
+
if (!handlers) return;
|
|
292
|
+
for (const handler of Array.from(handlers)) {
|
|
293
|
+
try {
|
|
294
|
+
handler(payload);
|
|
295
|
+
} catch (error) {
|
|
296
|
+
console.error("[ServiceProxy:" + this.name + "] Event handler error for '" + event + "':", error);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
function assertRecord(value, field) {
|
|
302
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
303
|
+
throw new Error("[KrnlTS] Invalid service definition: '" + field + "' must be an object.");
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function assertNonEmptyString(value, field) {
|
|
307
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
308
|
+
throw new Error("[KrnlTS] Invalid service definition: '" + field + "' must be a non-empty string.");
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
function validateServiceDefinition(definition) {
|
|
312
|
+
if (!definition || typeof definition !== "object") {
|
|
313
|
+
throw new Error("[KrnlTS] Invalid service definition: expected an object.");
|
|
314
|
+
}
|
|
315
|
+
assertNonEmptyString(definition.id, "id");
|
|
316
|
+
assertNonEmptyString(definition.name, "name");
|
|
317
|
+
assertNonEmptyString(definition.version, "version");
|
|
318
|
+
if (!definition.operations || typeof definition.operations !== "object") {
|
|
319
|
+
throw new Error("[Service:" + definition.name + "] Invalid operations: expected an object.");
|
|
320
|
+
}
|
|
321
|
+
for (const [key, operation] of Object.entries(definition.operations)) {
|
|
322
|
+
if (!operation || typeof operation !== "object") {
|
|
323
|
+
throw new Error("[Service:" + definition.name + "] Invalid operation '" + key + "'.");
|
|
324
|
+
}
|
|
325
|
+
assertNonEmptyString(operation.id, "operations." + key + ".id");
|
|
326
|
+
assertNonEmptyString(operation.name, "operations." + key + ".name");
|
|
327
|
+
if (operation.inputSchema !== void 0) assertRecord(operation.inputSchema, "operations." + key + ".inputSchema");
|
|
328
|
+
if (operation.outputSchema !== void 0) assertRecord(operation.outputSchema, "operations." + key + ".outputSchema");
|
|
329
|
+
if (typeof operation.handler !== "function") {
|
|
330
|
+
throw new Error("[Service:" + definition.name + "] Operation '" + key + "' must define a handler.");
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
for (const [key, event] of Object.entries(definition.events ?? {})) {
|
|
334
|
+
if (!event || typeof event !== "object") {
|
|
335
|
+
throw new Error("[Service:" + definition.name + "] Invalid event '" + key + "'.");
|
|
336
|
+
}
|
|
337
|
+
assertNonEmptyString(event.name, "events." + key + ".name");
|
|
338
|
+
}
|
|
339
|
+
for (const [key, schema] of Object.entries(definition.inputs ?? {})) assertRecord(schema, "inputs." + key);
|
|
340
|
+
for (const [key, schema] of Object.entries(definition.outputs ?? {})) assertRecord(schema, "outputs." + key);
|
|
341
|
+
if (definition.state?.schema !== void 0) assertRecord(definition.state.schema, "state.schema");
|
|
342
|
+
for (const capability of definition.capabilities ?? []) {
|
|
343
|
+
assertNonEmptyString(capability.id, "capability.id");
|
|
344
|
+
assertNonEmptyString(capability.name, "capability.name");
|
|
345
|
+
if (!Array.isArray(capability.protocols)) {
|
|
346
|
+
throw new Error("[Service:" + definition.name + "] Capability '" + capability.name + "' protocols must be an array.");
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
for (const dependency of definition.dependencies ?? []) {
|
|
350
|
+
assertNonEmptyString(dependency.serviceId, "dependency.serviceId");
|
|
351
|
+
}
|
|
352
|
+
return definition;
|
|
353
|
+
}
|
|
354
|
+
function createServiceDefinition(definition) {
|
|
355
|
+
return validateServiceDefinition(definition);
|
|
356
|
+
}
|
|
357
|
+
function createService(definition) {
|
|
358
|
+
return new ServiceInstance(validateServiceDefinition(definition));
|
|
359
|
+
}
|
|
360
|
+
function createServiceProxy(definition, adapter, endpoint) {
|
|
361
|
+
validateServiceDefinition(definition);
|
|
362
|
+
if (!adapter || typeof adapter.send !== "function") {
|
|
363
|
+
throw new Error("[ServiceProxy:" + definition.name + "] Adapter must implement send().");
|
|
364
|
+
}
|
|
365
|
+
if (!endpoint || typeof endpoint.address !== "string" || endpoint.address.length === 0) {
|
|
366
|
+
throw new Error("[ServiceProxy:" + definition.name + "] Endpoint must have an address.");
|
|
367
|
+
}
|
|
368
|
+
return new ServiceProxy(definition, adapter, endpoint);
|
|
369
|
+
}
|
|
370
|
+
var asyncHooks = globalThis.process?.getBuiltinModule?.("node:async_hooks");
|
|
371
|
+
|
|
372
|
+
export {
|
|
373
|
+
createServiceDefinition,
|
|
374
|
+
createService,
|
|
375
|
+
createServiceProxy
|
|
376
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import {
|
|
2
|
+
b2ListBuckets
|
|
3
|
+
} from "./chunk-4QEMALXF.js";
|
|
4
|
+
|
|
5
|
+
// src/srkl/SrklDiscoveryService.ts
|
|
6
|
+
var cachedSrklUrl = null;
|
|
7
|
+
async function getPrimarySrklDeploymentUrl(queryDb) {
|
|
8
|
+
if (cachedSrklUrl && Date.now() < cachedSrklUrl.expiresAt) return cachedSrklUrl.url;
|
|
9
|
+
if (queryDb) {
|
|
10
|
+
try {
|
|
11
|
+
const res = await queryDb(
|
|
12
|
+
`SELECT url FROM "ultrst-srkl-deployments" WHERE is_active = true ORDER BY is_primary DESC, priority DESC, created_at ASC LIMIT 1`,
|
|
13
|
+
[]
|
|
14
|
+
);
|
|
15
|
+
if (res.rows.length > 0 && res.rows[0].url) {
|
|
16
|
+
const url = res.rows[0].url.trim().replace(/\/+$/, "");
|
|
17
|
+
cachedSrklUrl = { url, expiresAt: Date.now() + 10 * 60 * 1e3 };
|
|
18
|
+
return url;
|
|
19
|
+
}
|
|
20
|
+
} catch {
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const fallback = "https://srkl-engine.vercel.app";
|
|
24
|
+
cachedSrklUrl = { url: fallback, expiresAt: Date.now() + 10 * 60 * 1e3 };
|
|
25
|
+
return fallback;
|
|
26
|
+
}
|
|
27
|
+
async function getSrklAccounts(userId, service, queryDb) {
|
|
28
|
+
const srklUrl = await getPrimarySrklDeploymentUrl(queryDb);
|
|
29
|
+
const accounts = [];
|
|
30
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
31
|
+
if (queryDb && userId) {
|
|
32
|
+
try {
|
|
33
|
+
const uRes = await queryDb(`SELECT app_profiles FROM "ultrst-users" WHERE id = $1 LIMIT 1`, [userId]);
|
|
34
|
+
if (uRes.rows.length > 0) {
|
|
35
|
+
const p = typeof uRes.rows[0].app_profiles === "string" ? JSON.parse(uRes.rows[0].app_profiles) : uRes.rows[0].app_profiles || {};
|
|
36
|
+
const pool = [
|
|
37
|
+
...p.srkl?.storage_connections || [],
|
|
38
|
+
...p.depm?.storage_connections || [],
|
|
39
|
+
...p.qwnsr?.storage_connections || [],
|
|
40
|
+
...p.loky?.storage_connections || []
|
|
41
|
+
];
|
|
42
|
+
for (const conn of pool) {
|
|
43
|
+
if (service && !conn.provider?.toLowerCase().includes(service.toLowerCase())) continue;
|
|
44
|
+
const kId = (conn.b2KeyId || conn.keyId || conn.accessKeyId || "").trim();
|
|
45
|
+
const key = kId ? `key:${kId}` : `id:${conn.id}`;
|
|
46
|
+
if (!seenKeys.has(key)) {
|
|
47
|
+
seenKeys.add(key);
|
|
48
|
+
accounts.push({
|
|
49
|
+
id: conn.id,
|
|
50
|
+
name: conn.label || conn.id,
|
|
51
|
+
provider: conn.provider,
|
|
52
|
+
serviceId: conn.provider,
|
|
53
|
+
credentials: {
|
|
54
|
+
keyId: conn.b2KeyId || conn.keyId,
|
|
55
|
+
applicationKey: conn.b2ApplicationKey || conn.appKey,
|
|
56
|
+
bucketName: conn.b2BucketName || conn.bucketName,
|
|
57
|
+
region: conn.b2Region || conn.region,
|
|
58
|
+
endpoint: conn.b2Endpoint || conn.endpoint
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} catch {
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const res = await fetch(`${srklUrl}/api/ecosystem/services`, {
|
|
69
|
+
headers: { "x-user-id": userId, Accept: "application/json" },
|
|
70
|
+
signal: AbortSignal.timeout(5e3)
|
|
71
|
+
});
|
|
72
|
+
if (res.ok) {
|
|
73
|
+
const data = await res.json();
|
|
74
|
+
const list = Array.isArray(data.services) ? data.services : [];
|
|
75
|
+
for (const item of list) {
|
|
76
|
+
const sId = String(item.serviceId || item.id || "").toLowerCase();
|
|
77
|
+
if (service && !sId.includes(service.toLowerCase())) continue;
|
|
78
|
+
const kId = (item.credentials?.keyId || item.credentials?.b2KeyId || "").trim();
|
|
79
|
+
const key = kId ? `key:${kId}` : `id:${item.id}`;
|
|
80
|
+
if (!seenKeys.has(key)) {
|
|
81
|
+
seenKeys.add(key);
|
|
82
|
+
accounts.push(item);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
} catch {
|
|
87
|
+
}
|
|
88
|
+
return { success: true, deploymentUrl: srklUrl, count: accounts.length, accounts };
|
|
89
|
+
}
|
|
90
|
+
async function srklListBackblazeBuckets(keyId, applicationKey, _bucketId, _bucketName, userId, queryDb) {
|
|
91
|
+
let resolvedKeyId = (keyId || "").trim();
|
|
92
|
+
let resolvedAppKey = (applicationKey || "").trim();
|
|
93
|
+
if ((!resolvedKeyId || !resolvedAppKey) && userId && queryDb) {
|
|
94
|
+
const accountsRes = await getSrklAccounts(userId, "backblaze", queryDb);
|
|
95
|
+
const matched = accountsRes.accounts.find((a) => (a.credentials?.keyId || a.credentials?.b2KeyId) === resolvedKeyId) || accountsRes.accounts[0];
|
|
96
|
+
if (matched) {
|
|
97
|
+
resolvedKeyId = matched.credentials?.keyId || matched.credentials?.b2KeyId || resolvedKeyId;
|
|
98
|
+
resolvedAppKey = matched.credentials?.applicationKey || matched.credentials?.b2ApplicationKey || resolvedAppKey;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (!resolvedKeyId || !resolvedAppKey) {
|
|
102
|
+
return { success: false, buckets: [], error: "Backblaze B2 Key ID and Application Key are required" };
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
const buckets = await b2ListBuckets(resolvedKeyId, resolvedAppKey);
|
|
106
|
+
return { success: true, buckets };
|
|
107
|
+
} catch (err) {
|
|
108
|
+
return { success: false, buckets: [], error: err.message };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export {
|
|
113
|
+
getPrimarySrklDeploymentUrl,
|
|
114
|
+
getSrklAccounts,
|
|
115
|
+
srklListBackblazeBuckets
|
|
116
|
+
};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// src/auth/SessionManager.ts
|
|
2
|
+
var SessionManager = class {
|
|
3
|
+
ttl;
|
|
4
|
+
constructor(ttlMs = 864e5) {
|
|
5
|
+
this.ttl = ttlMs;
|
|
6
|
+
}
|
|
7
|
+
generateToken(userId) {
|
|
8
|
+
return `sess::${userId}::${Date.now()}::${Math.random().toString(36).substring(2, 9)}`;
|
|
9
|
+
}
|
|
10
|
+
createSession(userId, role) {
|
|
11
|
+
return {
|
|
12
|
+
token: this.generateToken(userId),
|
|
13
|
+
userId,
|
|
14
|
+
role,
|
|
15
|
+
expiresAt: Date.now() + this.ttl
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
isValid(session) {
|
|
19
|
+
return Date.now() < session.expiresAt;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// src/auth/RateLimiter.ts
|
|
24
|
+
var RateLimiter = class {
|
|
25
|
+
attempts = /* @__PURE__ */ new Map();
|
|
26
|
+
windowMs;
|
|
27
|
+
maxUser;
|
|
28
|
+
maxAdmin;
|
|
29
|
+
constructor(windowMs = 9e5, maxUser = 5, maxAdmin = 3) {
|
|
30
|
+
this.windowMs = windowMs;
|
|
31
|
+
this.maxUser = maxUser;
|
|
32
|
+
this.maxAdmin = maxAdmin;
|
|
33
|
+
}
|
|
34
|
+
check(identifier, isAdminAttempt = false) {
|
|
35
|
+
const now = Date.now();
|
|
36
|
+
let record = this.attempts.get(identifier);
|
|
37
|
+
if (!record || now - record.windowStart > this.windowMs) {
|
|
38
|
+
record = { count: 0, windowStart: now };
|
|
39
|
+
this.attempts.set(identifier, record);
|
|
40
|
+
}
|
|
41
|
+
record.count++;
|
|
42
|
+
const limit = isAdminAttempt ? this.maxAdmin : this.maxUser;
|
|
43
|
+
if (record.count > limit) {
|
|
44
|
+
throw new Error("Too many requests. Please try again later.");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
reset(identifier) {
|
|
48
|
+
this.attempts.delete(identifier);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// src/auth/UnifiedAuthCore.ts
|
|
53
|
+
import bcrypt from "bcryptjs";
|
|
54
|
+
async function loginSystemUser(email, rawPass, isAdmin, queryDb) {
|
|
55
|
+
const normEmail = (email || "").trim().toLowerCase();
|
|
56
|
+
const password = (rawPass || "").trim();
|
|
57
|
+
if (!normEmail || !password) {
|
|
58
|
+
return { success: false, status: 400, error: "Email and password are required" };
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
if (isAdmin) {
|
|
62
|
+
const credRes = await queryDb(`SELECT * FROM "ultrst-credentials" WHERE id = 'system' LIMIT 1`, []);
|
|
63
|
+
if (credRes.rows.length > 0) {
|
|
64
|
+
const cred = credRes.rows[0];
|
|
65
|
+
const matchEmail = (cred.admin_email || "").toLowerCase() === normEmail;
|
|
66
|
+
const matchPass = cred.admin_password === password || cred.admin_password && bcrypt.compareSync(password, cred.admin_password);
|
|
67
|
+
if (matchEmail && matchPass) {
|
|
68
|
+
return {
|
|
69
|
+
success: true,
|
|
70
|
+
status: 200,
|
|
71
|
+
token: `adm_${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
|
72
|
+
user: { id: "admin_sys", email: cred.admin_email, username: "System Admin", name: "System Admin", role: "admin", status: "ACTIVE" }
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const uRes = await queryDb(`SELECT * FROM "ultrst-users" WHERE LOWER(email) = $1 LIMIT 1`, [normEmail]);
|
|
78
|
+
if (uRes.rows.length === 0) {
|
|
79
|
+
return { success: false, status: 401, error: "Invalid email or password" };
|
|
80
|
+
}
|
|
81
|
+
const user = uRes.rows[0];
|
|
82
|
+
const hash = user.password_hash || "";
|
|
83
|
+
let isMatch = false;
|
|
84
|
+
if (hash.startsWith("$2a$") || hash.startsWith("$2b$")) {
|
|
85
|
+
isMatch = bcrypt.compareSync(password, hash);
|
|
86
|
+
} else {
|
|
87
|
+
isMatch = hash === password;
|
|
88
|
+
}
|
|
89
|
+
if (!isMatch) {
|
|
90
|
+
return { success: false, status: 401, error: "Invalid email or password" };
|
|
91
|
+
}
|
|
92
|
+
if (isAdmin && user.role !== "admin") {
|
|
93
|
+
return { success: false, status: 403, error: "Insufficient administrative privileges" };
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
success: true,
|
|
97
|
+
status: 200,
|
|
98
|
+
token: `usr_${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
|
99
|
+
user: {
|
|
100
|
+
id: user.id,
|
|
101
|
+
email: user.email,
|
|
102
|
+
username: user.username || user.name || user.email.split("@")[0],
|
|
103
|
+
name: user.name || user.username || "Ecosystem User",
|
|
104
|
+
role: user.role || "user",
|
|
105
|
+
status: user.status || "ACTIVE",
|
|
106
|
+
appProfiles: typeof user.app_profiles === "string" ? JSON.parse(user.app_profiles) : user.app_profiles || {}
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
} catch (err) {
|
|
110
|
+
return { success: false, status: 500, error: err.message };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async function resolveUserFromToken(tokenOrUserId, queryDb) {
|
|
114
|
+
if (!tokenOrUserId) return null;
|
|
115
|
+
let targetId = tokenOrUserId;
|
|
116
|
+
if (targetId.startsWith("Bearer ")) targetId = targetId.slice(7).trim();
|
|
117
|
+
if (targetId.startsWith("sess::")) {
|
|
118
|
+
const parts = targetId.split("::");
|
|
119
|
+
if (parts[1]) targetId = parts[1];
|
|
120
|
+
}
|
|
121
|
+
const res = await queryDb(`SELECT * FROM "ultrst-users" WHERE id = $1 OR LOWER(email) = LOWER($1) LIMIT 1`, [targetId]);
|
|
122
|
+
if (res.rows.length === 0) return null;
|
|
123
|
+
const u = res.rows[0];
|
|
124
|
+
return {
|
|
125
|
+
id: u.id,
|
|
126
|
+
email: u.email,
|
|
127
|
+
username: u.username || u.name || u.email.split("@")[0],
|
|
128
|
+
name: u.name || u.username || "Ecosystem User",
|
|
129
|
+
role: u.role || "user",
|
|
130
|
+
status: u.status || "ACTIVE",
|
|
131
|
+
appProfiles: typeof u.app_profiles === "string" ? JSON.parse(u.app_profiles) : u.app_profiles || {}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export {
|
|
136
|
+
SessionManager,
|
|
137
|
+
RateLimiter,
|
|
138
|
+
loginSystemUser,
|
|
139
|
+
resolveUserFromToken
|
|
140
|
+
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { B as BackendClient,
|
|
2
|
-
export { a as BackendClientOptions,
|
|
1
|
+
import { B as BackendClient, j as DomainProxy, d as BackendResolver, F as FrontendSession } from '../WebSocketTransport-CHmkJ0tj.js';
|
|
2
|
+
export { a as BackendClientOptions, b as BackendExecutor, e as BackendResolverOptions, C as CallbackFn, f as CallbackHandle, g as CallbackRegistry, D as Delegation, k as EventBus, l as EventHandler, m as FrontendSessionOptions, G as GapHandler, H as HTTPTransport, n as HTTPTransportOptions, J as Job, p as JobPriority, q as JobState, M as Message, s as MessageType, R as ResolutionStrategy, S as SequenceCheckResult, v as SequenceTracker, W as WebSocketTransport, w as WebSocketTransportOptions, x as createBackendClient } from '../WebSocketTransport-CHmkJ0tj.js';
|
|
3
3
|
import React from 'react';
|
|
4
|
-
export { B as BackendExecutor, C as CallbackHandle, D as Delegation, J as Job, d as JobPriority, e as JobState, M as Message, f as MessageType, R as ResolutionStrategy } from '../types-BERZJ93O.js';
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* @file useBackend.ts
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { A as AuthContext,
|
|
3
|
-
import { e as EventBus, d as CallbackRegistry, C as CallbackFn, I as ITransport } from '../WebSocketTransport-CxUzUfwl.js';
|
|
4
|
-
export { B as BackendClient, a as BackendClientOptions, b as BackendResolver, c as BackendResolverOptions, D as DomainProxy, E as ErrorHandler, f as EventHandler, F as FrontendSession, g as FrontendSessionOptions, G as GapHandler, H as HTTPTransport, h as HTTPTransportOptions, M as MessageHandler, S as SequenceCheckResult, i as SequenceTracker, W as WebSocketTransport, j as WebSocketTransportOptions, k as createBackendClient } from '../WebSocketTransport-CxUzUfwl.js';
|
|
5
|
-
export { I as IdempotencyManager } from '../IdempotencyManager-B5MjK871.js';
|
|
1
|
+
import { t as ResourceVersion, J as Job, c as BackendInstance, u as RoutingStrategy, b as BackendExecutor, k as EventBus, g as CallbackRegistry, M as Message, C as CallbackFn, f as CallbackHandle, D as Delegation, I as ITransport } from '../WebSocketTransport-CHmkJ0tj.js';
|
|
2
|
+
export { A as AuthContext, B as BackendClient, a as BackendClientOptions, d as BackendResolver, e as BackendResolverOptions, h as CapabilityContext, i as DistributedLock, j as DomainProxy, E as ErrorHandler, l as EventHandler, F as FrontendSession, m as FrontendSessionOptions, G as GapHandler, H as HTTPTransport, n as HTTPTransportOptions, o as IdempotentRequest, p as JobPriority, q as JobState, r as MessageHandler, s as MessageType, R as ResolutionStrategy, S as SequenceCheckResult, v as SequenceTracker, W as WebSocketTransport, w as WebSocketTransportOptions, x as createBackendClient } from '../WebSocketTransport-CHmkJ0tj.js';
|
|
6
3
|
|
|
7
4
|
/**
|
|
8
5
|
* @file BackendContract.ts
|
|
@@ -114,6 +111,21 @@ declare class LockManager {
|
|
|
114
111
|
clear(): void;
|
|
115
112
|
}
|
|
116
113
|
|
|
114
|
+
/**
|
|
115
|
+
* @file IdempotencyManager.ts
|
|
116
|
+
* @description In-flight mutation deduplication, concurrent attachment, and completed result caching.
|
|
117
|
+
*/
|
|
118
|
+
declare class IdempotencyManager {
|
|
119
|
+
private inFlight;
|
|
120
|
+
private completedCache;
|
|
121
|
+
private ttlMs;
|
|
122
|
+
constructor(ttlMs?: number);
|
|
123
|
+
executeIdempotent<T>(idempotencyKey: string, operation: () => Promise<T>): Promise<T>;
|
|
124
|
+
has(idempotencyKey: string): boolean;
|
|
125
|
+
prune(): void;
|
|
126
|
+
clear(): void;
|
|
127
|
+
}
|
|
128
|
+
|
|
117
129
|
/**
|
|
118
130
|
* @file JobScheduler.ts
|
|
119
131
|
* @description DAG-based job sequencing engine supporting dependency ordering, priority queues, and concurrency control.
|
|
@@ -234,4 +246,4 @@ declare class RemoteExecutor implements BackendExecutor {
|
|
|
234
246
|
getTransport(): ITransport;
|
|
235
247
|
}
|
|
236
248
|
|
|
237
|
-
export { type BackendContract, BackendExecutor, BackendInstance, BackendRegistry, type CallbackDefinition, CallbackFn, CallbackHandle, CallbackRegistry, ConcurrencyConflictError, ConcurrencyManager, Delegation, type DomainContract, EventBus, type EventDefinition, HealthMonitor, ITransport, InProcessExecutor, Job, type JobDefinition, type JobHandler, JobScheduler, LoadBalancer, LockManager, Message, type OperationDefinition, RemoteExecutor, type RemoteExecutorOptions, ResourceVersion, RoutingStrategy, createEmptyContract };
|
|
249
|
+
export { type BackendContract, BackendExecutor, BackendInstance, BackendRegistry, type CallbackDefinition, CallbackFn, CallbackHandle, CallbackRegistry, ConcurrencyConflictError, ConcurrencyManager, Delegation, type DomainContract, EventBus, type EventDefinition, HealthMonitor, ITransport, IdempotencyManager, InProcessExecutor, Job, type JobDefinition, type JobHandler, JobScheduler, LoadBalancer, LockManager, Message, type OperationDefinition, RemoteExecutor, type RemoteExecutorOptions, ResourceVersion, RoutingStrategy, createEmptyContract };
|