@nextera.one/axis-server-sdk 1.1.0 → 1.2.1
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/core/index.d.mts +2 -96
- package/dist/core/index.d.ts +2 -96
- package/dist/core/index.js +94 -294
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +74 -205
- package/dist/core/index.mjs.map +1 -1
- package/dist/index.d.mts +133 -3
- package/dist/index.d.ts +133 -3
- package/dist/index.js +662 -426
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +616 -321
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -1
package/dist/index.mjs
CHANGED
|
@@ -18,6 +18,7 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
18
18
|
if (kind && result) __defProp(target, key, result);
|
|
19
19
|
return result;
|
|
20
20
|
};
|
|
21
|
+
var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
21
22
|
|
|
22
23
|
// src/decorators/handler.decorator.ts
|
|
23
24
|
import { Injectable, SetMetadata } from "@nestjs/common";
|
|
@@ -56,6 +57,24 @@ function Intent(action, options) {
|
|
|
56
57
|
};
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
// src/decorators/intent-body.decorator.ts
|
|
61
|
+
import "reflect-metadata";
|
|
62
|
+
var INTENT_BODY_KEY = "axis:intent:body";
|
|
63
|
+
function IntentBody(decoder) {
|
|
64
|
+
return (target, propertyKey) => {
|
|
65
|
+
Reflect.defineMetadata(INTENT_BODY_KEY, decoder, target, propertyKey);
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/decorators/intent-sensors.decorator.ts
|
|
70
|
+
import "reflect-metadata";
|
|
71
|
+
var INTENT_SENSORS_KEY = "axis:intent:sensors";
|
|
72
|
+
function IntentSensors(sensors) {
|
|
73
|
+
return (target, propertyKey) => {
|
|
74
|
+
Reflect.defineMetadata(INTENT_SENSORS_KEY, sensors, target, propertyKey);
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
59
78
|
// src/decorators/tlv-field.decorator.ts
|
|
60
79
|
import "reflect-metadata";
|
|
61
80
|
var TLV_FIELDS_KEY = "axis:tlv:fields";
|
|
@@ -117,142 +136,19 @@ function TlvRange(min, max, message) {
|
|
|
117
136
|
// src/decorators/dto-schema.util.ts
|
|
118
137
|
import "reflect-metadata";
|
|
119
138
|
|
|
120
|
-
// src/core/varint.ts
|
|
121
|
-
function encodeVarint(value) {
|
|
122
|
-
if (value < 0) throw new Error("Varint must be unsigned");
|
|
123
|
-
const bytes2 = [];
|
|
124
|
-
while (true) {
|
|
125
|
-
const byte = value & 127;
|
|
126
|
-
value >>>= 7;
|
|
127
|
-
if (value === 0) {
|
|
128
|
-
bytes2.push(byte);
|
|
129
|
-
break;
|
|
130
|
-
}
|
|
131
|
-
bytes2.push(byte | 128);
|
|
132
|
-
}
|
|
133
|
-
return new Uint8Array(bytes2);
|
|
134
|
-
}
|
|
135
|
-
function decodeVarint(buf, offset = 0) {
|
|
136
|
-
let value = 0;
|
|
137
|
-
let shift = 0;
|
|
138
|
-
let length = 0;
|
|
139
|
-
while (true) {
|
|
140
|
-
if (offset + length >= buf.length) {
|
|
141
|
-
throw new Error("Varint decode out of bounds");
|
|
142
|
-
}
|
|
143
|
-
const byte = buf[offset + length];
|
|
144
|
-
value += (byte & 127) * Math.pow(2, shift);
|
|
145
|
-
length++;
|
|
146
|
-
shift += 7;
|
|
147
|
-
if ((byte & 128) === 0) {
|
|
148
|
-
break;
|
|
149
|
-
}
|
|
150
|
-
if (length > 8) throw new Error("Varint too large");
|
|
151
|
-
}
|
|
152
|
-
return { value, length };
|
|
153
|
-
}
|
|
154
|
-
function varintLength(value) {
|
|
155
|
-
if (value < 0) throw new Error("Varint must be unsigned");
|
|
156
|
-
let len = 0;
|
|
157
|
-
do {
|
|
158
|
-
value >>>= 7;
|
|
159
|
-
len++;
|
|
160
|
-
} while (value !== 0);
|
|
161
|
-
return len;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
139
|
// src/core/tlv.ts
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
for (const t of sorted) {
|
|
174
|
-
totalSize += varintLength(t.type);
|
|
175
|
-
totalSize += varintLength(t.value.length);
|
|
176
|
-
totalSize += t.value.length;
|
|
177
|
-
}
|
|
178
|
-
const buf = new Uint8Array(totalSize);
|
|
179
|
-
let offset = 0;
|
|
180
|
-
for (const t of sorted) {
|
|
181
|
-
const typeBytes = encodeVarint(t.type);
|
|
182
|
-
buf.set(typeBytes, offset);
|
|
183
|
-
offset += typeBytes.length;
|
|
184
|
-
const lenBytes = encodeVarint(t.value.length);
|
|
185
|
-
buf.set(lenBytes, offset);
|
|
186
|
-
offset += lenBytes.length;
|
|
187
|
-
buf.set(t.value, offset);
|
|
188
|
-
offset += t.value.length;
|
|
189
|
-
}
|
|
190
|
-
return buf;
|
|
191
|
-
}
|
|
192
|
-
function decodeTLVsList(buf, maxItems = 1024) {
|
|
193
|
-
const list = [];
|
|
194
|
-
let offset = 0;
|
|
195
|
-
while (offset < buf.length) {
|
|
196
|
-
if (list.length >= maxItems) throw new Error("TLV_LIMIT");
|
|
197
|
-
const { value: type, length: typeLen } = decodeVarint(buf, offset);
|
|
198
|
-
offset += typeLen;
|
|
199
|
-
const { value: len, length: lenLen } = decodeVarint(buf, offset);
|
|
200
|
-
offset += lenLen;
|
|
201
|
-
if (offset + len > buf.length) {
|
|
202
|
-
throw new Error(`TLV violation: Length ${len} exceeds buffer`);
|
|
203
|
-
}
|
|
204
|
-
const value = buf.slice(offset, offset + len);
|
|
205
|
-
list.push({ type, value });
|
|
206
|
-
offset += len;
|
|
207
|
-
}
|
|
208
|
-
return list;
|
|
209
|
-
}
|
|
210
|
-
function decodeTLVs(buf) {
|
|
211
|
-
const map2 = /* @__PURE__ */ new Map();
|
|
212
|
-
let offset = 0;
|
|
213
|
-
let lastType = -1;
|
|
214
|
-
while (offset < buf.length) {
|
|
215
|
-
const { value: type, length: typeLen } = decodeVarint(buf, offset);
|
|
216
|
-
offset += typeLen;
|
|
217
|
-
if (type <= lastType) {
|
|
218
|
-
throw new Error(
|
|
219
|
-
`TLV violation: Unsorted or duplicate type ${type} after ${lastType}`
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
lastType = type;
|
|
223
|
-
const { value: len, length: lenLen } = decodeVarint(buf, offset);
|
|
224
|
-
offset += lenLen;
|
|
225
|
-
if (offset + len > buf.length) {
|
|
226
|
-
throw new Error(`TLV violation: Length ${len} exceeds buffer`);
|
|
227
|
-
}
|
|
228
|
-
const value = buf.slice(offset, offset + len);
|
|
229
|
-
map2.set(type, value);
|
|
230
|
-
offset += len;
|
|
231
|
-
}
|
|
232
|
-
return map2;
|
|
233
|
-
}
|
|
234
|
-
function decodeObject(bytes2, depth = 0, limits = { maxDepth: 8, maxItems: 128 }) {
|
|
235
|
-
if (depth > limits.maxDepth) {
|
|
236
|
-
throw new Error("OBJECT_DEPTH_EXCEEDED");
|
|
237
|
-
}
|
|
238
|
-
const map2 = decodeTLVs(bytes2);
|
|
239
|
-
return map2;
|
|
240
|
-
}
|
|
241
|
-
function decodeArray(bytes2, itemType, maxItems = 256) {
|
|
242
|
-
const list = decodeTLVsList(bytes2, maxItems);
|
|
243
|
-
const items = [];
|
|
244
|
-
for (const tlv2 of list) {
|
|
245
|
-
if (tlv2.type !== itemType) {
|
|
246
|
-
throw new Error(`INVALID_ARRAY_ITEM:${tlv2.type}`);
|
|
247
|
-
}
|
|
248
|
-
items.push(tlv2.value);
|
|
249
|
-
}
|
|
250
|
-
return items;
|
|
251
|
-
}
|
|
140
|
+
import {
|
|
141
|
+
TLV,
|
|
142
|
+
encodeTLVs,
|
|
143
|
+
decodeTLVs,
|
|
144
|
+
decodeTLVsList,
|
|
145
|
+
decodeObject,
|
|
146
|
+
decodeArray
|
|
147
|
+
} from "@nextera.one/axis-protocol";
|
|
252
148
|
|
|
253
149
|
// src/decorators/dto-schema.util.ts
|
|
254
150
|
function extractDtoSchema(dto) {
|
|
255
|
-
const fieldMetas = Reflect.
|
|
151
|
+
const fieldMetas = Reflect.getMetadata(TLV_FIELDS_KEY, dto) || [];
|
|
256
152
|
if (fieldMetas.length === 0) {
|
|
257
153
|
throw new Error(
|
|
258
154
|
`DTO class ${dto.name} has no @TlvField decorators \u2014 nothing to validate`
|
|
@@ -271,7 +167,7 @@ function extractDtoSchema(dto) {
|
|
|
271
167
|
scope: m.options.scope
|
|
272
168
|
};
|
|
273
169
|
});
|
|
274
|
-
const validatorMetas = Reflect.
|
|
170
|
+
const validatorMetas = Reflect.getMetadata(TLV_VALIDATORS_KEY, dto) || [];
|
|
275
171
|
const validators = /* @__PURE__ */ new Map();
|
|
276
172
|
for (const vm of validatorMetas) {
|
|
277
173
|
const tag = tagByProp.get(vm.property);
|
|
@@ -286,7 +182,7 @@ function extractDtoSchema(dto) {
|
|
|
286
182
|
return { fields, validators };
|
|
287
183
|
}
|
|
288
184
|
function buildDtoDecoder(dto) {
|
|
289
|
-
const fieldMetas = Reflect.
|
|
185
|
+
const fieldMetas = Reflect.getMetadata(TLV_FIELDS_KEY, dto) || [];
|
|
290
186
|
if (fieldMetas.length === 0) {
|
|
291
187
|
throw new Error(
|
|
292
188
|
`DTO class ${dto.name} has no @TlvField decorators \u2014 cannot build decoder`
|
|
@@ -392,11 +288,146 @@ __decorateClass([
|
|
|
392
288
|
], AxisResponseDto.prototype, "updated_by", 2);
|
|
393
289
|
|
|
394
290
|
// src/engine/intent.router.ts
|
|
395
|
-
import { Injectable as Injectable2 } from "@nestjs/common";
|
|
291
|
+
import { Injectable as Injectable2, Logger, Optional } from "@nestjs/common";
|
|
292
|
+
|
|
293
|
+
// src/sensor/axis-sensor.ts
|
|
294
|
+
var Decision = /* @__PURE__ */ ((Decision2) => {
|
|
295
|
+
Decision2["ALLOW"] = "ALLOW";
|
|
296
|
+
Decision2["DENY"] = "DENY";
|
|
297
|
+
Decision2["THROTTLE"] = "THROTTLE";
|
|
298
|
+
Decision2["FLAG"] = "FLAG";
|
|
299
|
+
return Decision2;
|
|
300
|
+
})(Decision || {});
|
|
301
|
+
function normalizeSensorDecision(sensorDecision) {
|
|
302
|
+
if ("action" in sensorDecision) {
|
|
303
|
+
switch (sensorDecision.action) {
|
|
304
|
+
case "ALLOW":
|
|
305
|
+
return {
|
|
306
|
+
allow: true,
|
|
307
|
+
riskScore: 0,
|
|
308
|
+
reasons: [],
|
|
309
|
+
meta: sensorDecision.meta
|
|
310
|
+
};
|
|
311
|
+
case "DENY":
|
|
312
|
+
return {
|
|
313
|
+
allow: false,
|
|
314
|
+
riskScore: 100,
|
|
315
|
+
reasons: [sensorDecision.code, sensorDecision.reason].filter(
|
|
316
|
+
Boolean
|
|
317
|
+
),
|
|
318
|
+
meta: sensorDecision.meta,
|
|
319
|
+
retryAfterMs: sensorDecision.retryAfterMs
|
|
320
|
+
};
|
|
321
|
+
case "THROTTLE":
|
|
322
|
+
return {
|
|
323
|
+
allow: false,
|
|
324
|
+
riskScore: 50,
|
|
325
|
+
reasons: ["RATE_LIMIT"],
|
|
326
|
+
retryAfterMs: sensorDecision.retryAfterMs,
|
|
327
|
+
meta: sensorDecision.meta
|
|
328
|
+
};
|
|
329
|
+
case "FLAG":
|
|
330
|
+
return {
|
|
331
|
+
allow: true,
|
|
332
|
+
riskScore: sensorDecision.scoreDelta,
|
|
333
|
+
reasons: sensorDecision.reasons,
|
|
334
|
+
meta: sensorDecision.meta
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return {
|
|
339
|
+
allow: sensorDecision.allow,
|
|
340
|
+
riskScore: sensorDecision.riskScore,
|
|
341
|
+
reasons: sensorDecision.reasons,
|
|
342
|
+
tags: sensorDecision.tags,
|
|
343
|
+
meta: sensorDecision.meta,
|
|
344
|
+
tighten: sensorDecision.tighten,
|
|
345
|
+
retryAfterMs: sensorDecision.retryAfterMs
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
var SensorDecisions = {
|
|
349
|
+
allow(meta, tags) {
|
|
350
|
+
return {
|
|
351
|
+
decision: "ALLOW" /* ALLOW */,
|
|
352
|
+
allow: true,
|
|
353
|
+
riskScore: 0,
|
|
354
|
+
reasons: [],
|
|
355
|
+
tags,
|
|
356
|
+
meta
|
|
357
|
+
};
|
|
358
|
+
},
|
|
359
|
+
deny(code, reason, meta) {
|
|
360
|
+
return {
|
|
361
|
+
decision: "DENY" /* DENY */,
|
|
362
|
+
allow: false,
|
|
363
|
+
riskScore: 100,
|
|
364
|
+
code,
|
|
365
|
+
reasons: [code, reason].filter(Boolean),
|
|
366
|
+
meta
|
|
367
|
+
};
|
|
368
|
+
},
|
|
369
|
+
throttle(retryAfterMs, meta) {
|
|
370
|
+
return {
|
|
371
|
+
decision: "THROTTLE" /* THROTTLE */,
|
|
372
|
+
allow: false,
|
|
373
|
+
riskScore: 50,
|
|
374
|
+
retryAfterMs,
|
|
375
|
+
code: "RATE_LIMIT",
|
|
376
|
+
reasons: ["RATE_LIMIT"],
|
|
377
|
+
meta
|
|
378
|
+
};
|
|
379
|
+
},
|
|
380
|
+
flag(scoreDelta, reasons, meta) {
|
|
381
|
+
return {
|
|
382
|
+
decision: "FLAG" /* FLAG */,
|
|
383
|
+
allow: true,
|
|
384
|
+
riskScore: scoreDelta,
|
|
385
|
+
scoreDelta,
|
|
386
|
+
reasons,
|
|
387
|
+
meta
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
// src/engine/intent.router.ts
|
|
396
393
|
var IntentRouter = class {
|
|
397
|
-
constructor() {
|
|
394
|
+
constructor(moduleRef) {
|
|
395
|
+
this.moduleRef = moduleRef;
|
|
396
|
+
this.logger = new Logger(IntentRouter.name);
|
|
398
397
|
/** Internal registry of dynamic intent handlers */
|
|
399
398
|
this.handlers = /* @__PURE__ */ new Map();
|
|
399
|
+
/** Per-intent sensor classes (resolved at call time) */
|
|
400
|
+
this.intentSensors = /* @__PURE__ */ new Map();
|
|
401
|
+
/** Per-intent body decoders */
|
|
402
|
+
this.intentDecoders = /* @__PURE__ */ new Map();
|
|
403
|
+
/** Per-intent TLV schemas */
|
|
404
|
+
this.intentSchemas = /* @__PURE__ */ new Map();
|
|
405
|
+
/** Per-intent custom validators */
|
|
406
|
+
this.intentValidators = /* @__PURE__ */ new Map();
|
|
407
|
+
/** Per-intent operation kind */
|
|
408
|
+
this.intentKinds = /* @__PURE__ */ new Map();
|
|
409
|
+
}
|
|
410
|
+
getSchema(intent) {
|
|
411
|
+
return this.intentSchemas.get(intent);
|
|
412
|
+
}
|
|
413
|
+
getValidators(intent) {
|
|
414
|
+
return this.intentValidators.get(intent);
|
|
415
|
+
}
|
|
416
|
+
has(intent) {
|
|
417
|
+
return this.handlers.has(intent) || IntentRouter.BUILTIN_INTENTS.has(intent);
|
|
418
|
+
}
|
|
419
|
+
getRegisteredIntents() {
|
|
420
|
+
return [...IntentRouter.BUILTIN_INTENTS, ...this.handlers.keys()];
|
|
421
|
+
}
|
|
422
|
+
getIntentEntry(intent) {
|
|
423
|
+
if (!this.has(intent)) return null;
|
|
424
|
+
return {
|
|
425
|
+
schema: this.intentSchemas.get(intent),
|
|
426
|
+
validators: this.intentValidators.get(intent),
|
|
427
|
+
hasSensors: this.intentSensors.has(intent),
|
|
428
|
+
builtin: IntentRouter.BUILTIN_INTENTS.has(intent),
|
|
429
|
+
kind: this.intentKinds.get(intent)
|
|
430
|
+
};
|
|
400
431
|
}
|
|
401
432
|
/**
|
|
402
433
|
* Registers a handler for a specific intent.
|
|
@@ -432,6 +463,16 @@ var IntentRouter = class {
|
|
|
432
463
|
} else {
|
|
433
464
|
this.register(intentName, fn);
|
|
434
465
|
}
|
|
466
|
+
this.registerIntentMeta(intentName, Object.getPrototypeOf(instance), String(route.methodName));
|
|
467
|
+
}
|
|
468
|
+
const proto = Object.getPrototypeOf(instance);
|
|
469
|
+
for (const key of Object.getOwnPropertyNames(proto)) {
|
|
470
|
+
const meta = Reflect.getMetadata(INTENT_METADATA_KEY, proto, key);
|
|
471
|
+
if (!meta?.intent) continue;
|
|
472
|
+
if (!this.handlers.has(meta.intent)) {
|
|
473
|
+
this.register(meta.intent, instance[key].bind(instance));
|
|
474
|
+
}
|
|
475
|
+
this.registerIntentMeta(meta.intent, proto, key);
|
|
435
476
|
}
|
|
436
477
|
}
|
|
437
478
|
/**
|
|
@@ -455,6 +496,7 @@ var IntentRouter = class {
|
|
|
455
496
|
intent = new TextDecoder().decode(intentBytes);
|
|
456
497
|
let effect;
|
|
457
498
|
if (intent === "system.ping" || intent === "public.ping") {
|
|
499
|
+
this.logger.debug("PING received");
|
|
458
500
|
effect = {
|
|
459
501
|
ok: true,
|
|
460
502
|
effect: "pong",
|
|
@@ -495,6 +537,7 @@ var IntentRouter = class {
|
|
|
495
537
|
if (!innerIntent) {
|
|
496
538
|
throw new Error("INTENT.EXEC missing inner intent");
|
|
497
539
|
}
|
|
540
|
+
this.logger.debug(`EXEC: routing to inner intent '${innerIntent}'`);
|
|
498
541
|
const innerFrame = {
|
|
499
542
|
...frame,
|
|
500
543
|
headers: new Map(frame.headers),
|
|
@@ -510,8 +553,23 @@ var IntentRouter = class {
|
|
|
510
553
|
if (!handler) {
|
|
511
554
|
throw new Error(`Intent not found: ${intent}`);
|
|
512
555
|
}
|
|
556
|
+
const sensorClasses = this.intentSensors.get(intent);
|
|
557
|
+
if (sensorClasses && sensorClasses.length > 0) {
|
|
558
|
+
await this.runIntentSensors(sensorClasses, intent, frame);
|
|
559
|
+
}
|
|
560
|
+
const decoder = this.intentDecoders.get(intent);
|
|
561
|
+
let decodedBody = frame.body;
|
|
562
|
+
if (decoder) {
|
|
563
|
+
try {
|
|
564
|
+
decodedBody = decoder(Buffer.from(frame.body));
|
|
565
|
+
} catch (decodeErr) {
|
|
566
|
+
throw new Error(
|
|
567
|
+
`IntentBody decode failed for ${intent}: ${decodeErr.message}`
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
513
571
|
if (typeof handler === "function") {
|
|
514
|
-
const resultBody = await handler(frame.body, frame.headers);
|
|
572
|
+
const resultBody = decoder ? await handler(decodedBody, frame.headers) : await handler(frame.body, frame.headers);
|
|
515
573
|
effect = {
|
|
516
574
|
ok: true,
|
|
517
575
|
effect: "complete",
|
|
@@ -521,10 +579,7 @@ var IntentRouter = class {
|
|
|
521
579
|
if (typeof handler.handle === "function") {
|
|
522
580
|
effect = await handler.handle(frame);
|
|
523
581
|
} else if (typeof handler.execute === "function") {
|
|
524
|
-
const bodyRes = await handler.execute(
|
|
525
|
-
frame.body,
|
|
526
|
-
frame.headers
|
|
527
|
-
);
|
|
582
|
+
const bodyRes = decoder ? await handler.execute(decodedBody, frame.headers) : await handler.execute(frame.body, frame.headers);
|
|
528
583
|
effect = {
|
|
529
584
|
ok: true,
|
|
530
585
|
effect: "complete",
|
|
@@ -537,99 +592,200 @@ var IntentRouter = class {
|
|
|
537
592
|
}
|
|
538
593
|
}
|
|
539
594
|
}
|
|
540
|
-
this.
|
|
595
|
+
this.logIntent(intent, start, true);
|
|
541
596
|
return effect;
|
|
542
597
|
} catch (e) {
|
|
543
|
-
|
|
598
|
+
this.logIntent(intent, start, false, e.message);
|
|
544
599
|
throw e;
|
|
545
600
|
}
|
|
546
601
|
}
|
|
547
|
-
|
|
602
|
+
logIntent(intent, start, ok, error) {
|
|
548
603
|
const diff = process.hrtime(start);
|
|
549
|
-
|
|
604
|
+
const ms = (diff[0] * 1e3 + diff[1] / 1e6).toFixed(2);
|
|
605
|
+
if (ok) {
|
|
606
|
+
this.logger.debug(`${intent} completed in ${ms}ms`);
|
|
607
|
+
} else {
|
|
608
|
+
this.logger.warn(`${intent} failed in ${ms}ms - ${error}`);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
registerIntentMeta(intent, proto, methodName) {
|
|
612
|
+
const decoder = Reflect.getMetadata(INTENT_BODY_KEY, proto, methodName);
|
|
613
|
+
if (decoder) {
|
|
614
|
+
this.intentDecoders.set(intent, decoder);
|
|
615
|
+
}
|
|
616
|
+
const sensors = Reflect.getMetadata(INTENT_SENSORS_KEY, proto, methodName);
|
|
617
|
+
if (sensors && Array.isArray(sensors) && sensors.length > 0) {
|
|
618
|
+
this.intentSensors.set(intent, sensors);
|
|
619
|
+
}
|
|
620
|
+
const meta = Reflect.getMetadata(INTENT_METADATA_KEY, proto, methodName);
|
|
621
|
+
if (meta) {
|
|
622
|
+
this.storeSchema(meta);
|
|
623
|
+
if (meta.kind) {
|
|
624
|
+
this.intentKinds.set(intent, meta.kind);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
async runIntentSensors(sensorClasses, intent, frame) {
|
|
629
|
+
if (!this.moduleRef) return;
|
|
630
|
+
for (const SensorClass of sensorClasses) {
|
|
631
|
+
let sensor;
|
|
632
|
+
try {
|
|
633
|
+
sensor = this.moduleRef.get(SensorClass, { strict: false });
|
|
634
|
+
} catch {
|
|
635
|
+
this.logger.warn(
|
|
636
|
+
`@IntentSensors: could not resolve ${SensorClass.name} for ${intent}`
|
|
637
|
+
);
|
|
638
|
+
continue;
|
|
639
|
+
}
|
|
640
|
+
const sensorInput = {
|
|
641
|
+
rawBytes: frame.body,
|
|
642
|
+
intent,
|
|
643
|
+
body: frame.body,
|
|
644
|
+
headerTLVs: frame.headers,
|
|
645
|
+
metadata: { phase: "intent", intent }
|
|
646
|
+
};
|
|
647
|
+
if (sensor.supports && !sensor.supports(sensorInput)) continue;
|
|
648
|
+
const decision = normalizeSensorDecision(await sensor.run(sensorInput));
|
|
649
|
+
if (!decision.allow) {
|
|
650
|
+
const reason = decision.reasons[0] || `${sensor.name}:DENIED`;
|
|
651
|
+
this.logger.warn(
|
|
652
|
+
`Intent sensor ${sensor.name} denied ${intent}: ${reason}`
|
|
653
|
+
);
|
|
654
|
+
throw new Error(`SENSOR_DENY:${reason}`);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
storeSchema(meta) {
|
|
659
|
+
if (meta.dto) {
|
|
660
|
+
if (meta.tlv && meta.tlv.length > 0) {
|
|
661
|
+
this.logger.warn(
|
|
662
|
+
`${meta.intent}: both 'dto' and 'tlv' specified - using dto, ignoring tlv`
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
const extracted = extractDtoSchema(meta.dto);
|
|
666
|
+
const schema2 = {
|
|
667
|
+
intent: meta.intent,
|
|
668
|
+
version: 1,
|
|
669
|
+
bodyProfile: meta.bodyProfile || "TLV_MAP",
|
|
670
|
+
fields: extracted.fields.map((f) => ({
|
|
671
|
+
name: f.name,
|
|
672
|
+
tlv: f.tag,
|
|
673
|
+
kind: f.kind,
|
|
674
|
+
required: f.required,
|
|
675
|
+
maxLen: f.maxLen,
|
|
676
|
+
max: f.max,
|
|
677
|
+
scope: f.scope
|
|
678
|
+
}))
|
|
679
|
+
};
|
|
680
|
+
this.intentSchemas.set(meta.intent, schema2);
|
|
681
|
+
if (extracted.validators.size > 0) {
|
|
682
|
+
this.intentValidators.set(meta.intent, extracted.validators);
|
|
683
|
+
}
|
|
684
|
+
if (!this.intentDecoders.has(meta.intent)) {
|
|
685
|
+
this.intentDecoders.set(meta.intent, buildDtoDecoder(meta.dto));
|
|
686
|
+
}
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
if (!meta.tlv || meta.tlv.length === 0) return;
|
|
690
|
+
const schema = {
|
|
691
|
+
intent: meta.intent,
|
|
692
|
+
version: 1,
|
|
693
|
+
bodyProfile: meta.bodyProfile || "TLV_MAP",
|
|
694
|
+
fields: meta.tlv.map((f) => ({
|
|
695
|
+
name: f.name,
|
|
696
|
+
tlv: f.tag,
|
|
697
|
+
kind: f.kind,
|
|
698
|
+
required: f.required,
|
|
699
|
+
maxLen: f.maxLen,
|
|
700
|
+
max: f.max,
|
|
701
|
+
scope: f.scope
|
|
702
|
+
}))
|
|
703
|
+
};
|
|
704
|
+
this.intentSchemas.set(meta.intent, schema);
|
|
550
705
|
}
|
|
551
706
|
};
|
|
707
|
+
/** Intents handled inline in route() — not in `handlers` map */
|
|
708
|
+
IntentRouter.BUILTIN_INTENTS = /* @__PURE__ */ new Set([
|
|
709
|
+
"system.ping",
|
|
710
|
+
"public.ping",
|
|
711
|
+
"system.time",
|
|
712
|
+
"system.echo",
|
|
713
|
+
"INTENT.EXEC",
|
|
714
|
+
"axis.intent.exec"
|
|
715
|
+
]);
|
|
552
716
|
IntentRouter = __decorateClass([
|
|
553
|
-
Injectable2()
|
|
717
|
+
Injectable2(),
|
|
718
|
+
__decorateParam(0, Optional())
|
|
554
719
|
], IntentRouter);
|
|
555
720
|
|
|
556
721
|
// src/core/constants.ts
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
BodyProfile2[BodyProfile2["TLV_MAP"] = 1] = "TLV_MAP";
|
|
625
|
-
BodyProfile2[BodyProfile2["OBJ"] = 2] = "OBJ";
|
|
626
|
-
BodyProfile2[BodyProfile2["ARR"] = 3] = "ARR";
|
|
627
|
-
return BodyProfile2;
|
|
628
|
-
})(BodyProfile || {});
|
|
629
|
-
var ERR_INVALID_PACKET = "INVALID_PACKET";
|
|
630
|
-
var ERR_BAD_SIGNATURE = "BAD_SIGNATURE";
|
|
631
|
-
var ERR_REPLAY_DETECTED = "REPLAY_DETECTED";
|
|
632
|
-
var ERR_CONTRACT_VIOLATION = "CONTRACT_VIOLATION";
|
|
722
|
+
import {
|
|
723
|
+
AXIS_MAGIC,
|
|
724
|
+
AXIS_VERSION,
|
|
725
|
+
MAX_HDR_LEN,
|
|
726
|
+
MAX_BODY_LEN,
|
|
727
|
+
MAX_SIG_LEN,
|
|
728
|
+
MAX_FRAME_LEN,
|
|
729
|
+
FLAG_BODY_TLV,
|
|
730
|
+
FLAG_CHAIN_REQ,
|
|
731
|
+
FLAG_HAS_WITNESS,
|
|
732
|
+
TLV_PID,
|
|
733
|
+
TLV_TS,
|
|
734
|
+
TLV_INTENT,
|
|
735
|
+
TLV_ACTOR_ID,
|
|
736
|
+
TLV_PROOF_TYPE,
|
|
737
|
+
TLV_PROOF_REF,
|
|
738
|
+
TLV_NONCE,
|
|
739
|
+
TLV_AUD,
|
|
740
|
+
TLV_REALM,
|
|
741
|
+
TLV_NODE,
|
|
742
|
+
TLV_TRACE_ID,
|
|
743
|
+
TLV_KID,
|
|
744
|
+
TLV_RID,
|
|
745
|
+
TLV_OK,
|
|
746
|
+
TLV_EFFECT,
|
|
747
|
+
TLV_ERROR_CODE,
|
|
748
|
+
TLV_ERROR_MSG,
|
|
749
|
+
TLV_PREV_HASH,
|
|
750
|
+
TLV_RECEIPT_HASH,
|
|
751
|
+
TLV_NODE_KID,
|
|
752
|
+
TLV_NODE_CERT_HASH,
|
|
753
|
+
TLV_LOOM_PRESENCE_ID,
|
|
754
|
+
TLV_LOOM_WRIT,
|
|
755
|
+
TLV_LOOM_THREAD_HASH,
|
|
756
|
+
TLV_UPLOAD_ID,
|
|
757
|
+
TLV_INDEX,
|
|
758
|
+
TLV_OFFSET,
|
|
759
|
+
TLV_SHA256_CHUNK,
|
|
760
|
+
TLV_CAPSULE,
|
|
761
|
+
TLV_BODY_OBJ,
|
|
762
|
+
TLV_BODY_ARR,
|
|
763
|
+
NCERT_NODE_ID,
|
|
764
|
+
NCERT_KID,
|
|
765
|
+
NCERT_ALG,
|
|
766
|
+
NCERT_PUB,
|
|
767
|
+
NCERT_NBF,
|
|
768
|
+
NCERT_EXP,
|
|
769
|
+
NCERT_SCOPE,
|
|
770
|
+
NCERT_ISSUER_KID,
|
|
771
|
+
NCERT_PAYLOAD,
|
|
772
|
+
NCERT_SIG,
|
|
773
|
+
PROOF_NONE,
|
|
774
|
+
PROOF_CAPSULE,
|
|
775
|
+
PROOF_JWT,
|
|
776
|
+
PROOF_MTLS,
|
|
777
|
+
PROOF_LOOM,
|
|
778
|
+
PROOF_WITNESS,
|
|
779
|
+
ProofType,
|
|
780
|
+
BodyProfile,
|
|
781
|
+
ERR_INVALID_PACKET,
|
|
782
|
+
ERR_BAD_SIGNATURE,
|
|
783
|
+
ERR_REPLAY_DETECTED,
|
|
784
|
+
ERR_CONTRACT_VIOLATION
|
|
785
|
+
} from "@nextera.one/axis-protocol";
|
|
786
|
+
|
|
787
|
+
// src/core/varint.ts
|
|
788
|
+
import { encodeVarint, decodeVarint, varintLength } from "@nextera.one/axis-protocol";
|
|
633
789
|
|
|
634
790
|
// src/core/signature.ts
|
|
635
791
|
import * as crypto from "crypto";
|
|
@@ -1998,105 +2154,6 @@ function buildPacket(hdr, body, sig, flags = 0) {
|
|
|
1998
2154
|
};
|
|
1999
2155
|
}
|
|
2000
2156
|
|
|
2001
|
-
// src/sensor/axis-sensor.ts
|
|
2002
|
-
var Decision = /* @__PURE__ */ ((Decision2) => {
|
|
2003
|
-
Decision2["ALLOW"] = "ALLOW";
|
|
2004
|
-
Decision2["DENY"] = "DENY";
|
|
2005
|
-
Decision2["THROTTLE"] = "THROTTLE";
|
|
2006
|
-
Decision2["FLAG"] = "FLAG";
|
|
2007
|
-
return Decision2;
|
|
2008
|
-
})(Decision || {});
|
|
2009
|
-
function normalizeSensorDecision(sensorDecision) {
|
|
2010
|
-
if ("action" in sensorDecision) {
|
|
2011
|
-
switch (sensorDecision.action) {
|
|
2012
|
-
case "ALLOW":
|
|
2013
|
-
return {
|
|
2014
|
-
allow: true,
|
|
2015
|
-
riskScore: 0,
|
|
2016
|
-
reasons: [],
|
|
2017
|
-
meta: sensorDecision.meta
|
|
2018
|
-
};
|
|
2019
|
-
case "DENY":
|
|
2020
|
-
return {
|
|
2021
|
-
allow: false,
|
|
2022
|
-
riskScore: 100,
|
|
2023
|
-
reasons: [sensorDecision.code, sensorDecision.reason].filter(
|
|
2024
|
-
Boolean
|
|
2025
|
-
),
|
|
2026
|
-
meta: sensorDecision.meta,
|
|
2027
|
-
retryAfterMs: sensorDecision.retryAfterMs
|
|
2028
|
-
};
|
|
2029
|
-
case "THROTTLE":
|
|
2030
|
-
return {
|
|
2031
|
-
allow: false,
|
|
2032
|
-
riskScore: 50,
|
|
2033
|
-
reasons: ["RATE_LIMIT"],
|
|
2034
|
-
retryAfterMs: sensorDecision.retryAfterMs,
|
|
2035
|
-
meta: sensorDecision.meta
|
|
2036
|
-
};
|
|
2037
|
-
case "FLAG":
|
|
2038
|
-
return {
|
|
2039
|
-
allow: true,
|
|
2040
|
-
riskScore: sensorDecision.scoreDelta,
|
|
2041
|
-
reasons: sensorDecision.reasons,
|
|
2042
|
-
meta: sensorDecision.meta
|
|
2043
|
-
};
|
|
2044
|
-
}
|
|
2045
|
-
}
|
|
2046
|
-
return {
|
|
2047
|
-
allow: sensorDecision.allow,
|
|
2048
|
-
riskScore: sensorDecision.riskScore,
|
|
2049
|
-
reasons: sensorDecision.reasons,
|
|
2050
|
-
tags: sensorDecision.tags,
|
|
2051
|
-
meta: sensorDecision.meta,
|
|
2052
|
-
tighten: sensorDecision.tighten,
|
|
2053
|
-
retryAfterMs: sensorDecision.retryAfterMs
|
|
2054
|
-
};
|
|
2055
|
-
}
|
|
2056
|
-
var SensorDecisions = {
|
|
2057
|
-
allow(meta, tags) {
|
|
2058
|
-
return {
|
|
2059
|
-
decision: "ALLOW" /* ALLOW */,
|
|
2060
|
-
allow: true,
|
|
2061
|
-
riskScore: 0,
|
|
2062
|
-
reasons: [],
|
|
2063
|
-
tags,
|
|
2064
|
-
meta
|
|
2065
|
-
};
|
|
2066
|
-
},
|
|
2067
|
-
deny(code, reason, meta) {
|
|
2068
|
-
return {
|
|
2069
|
-
decision: "DENY" /* DENY */,
|
|
2070
|
-
allow: false,
|
|
2071
|
-
riskScore: 100,
|
|
2072
|
-
code,
|
|
2073
|
-
reasons: [code, reason].filter(Boolean),
|
|
2074
|
-
meta
|
|
2075
|
-
};
|
|
2076
|
-
},
|
|
2077
|
-
throttle(retryAfterMs, meta) {
|
|
2078
|
-
return {
|
|
2079
|
-
decision: "THROTTLE" /* THROTTLE */,
|
|
2080
|
-
allow: false,
|
|
2081
|
-
riskScore: 50,
|
|
2082
|
-
retryAfterMs,
|
|
2083
|
-
code: "RATE_LIMIT",
|
|
2084
|
-
reasons: ["RATE_LIMIT"],
|
|
2085
|
-
meta
|
|
2086
|
-
};
|
|
2087
|
-
},
|
|
2088
|
-
flag(scoreDelta, reasons, meta) {
|
|
2089
|
-
return {
|
|
2090
|
-
decision: "FLAG" /* FLAG */,
|
|
2091
|
-
allow: true,
|
|
2092
|
-
riskScore: scoreDelta,
|
|
2093
|
-
scoreDelta,
|
|
2094
|
-
reasons,
|
|
2095
|
-
meta
|
|
2096
|
-
};
|
|
2097
|
-
}
|
|
2098
|
-
};
|
|
2099
|
-
|
|
2100
2157
|
// src/security/scopes.ts
|
|
2101
2158
|
function hasScope(scopes, required) {
|
|
2102
2159
|
if (!Array.isArray(scopes) || scopes.length === 0) {
|
|
@@ -2394,13 +2451,245 @@ function isTimestampValid(ts, skewSeconds = 120) {
|
|
|
2394
2451
|
const diff = Math.abs(now - ts);
|
|
2395
2452
|
return diff <= skewSeconds;
|
|
2396
2453
|
}
|
|
2454
|
+
|
|
2455
|
+
// src/upload/axis-files.handlers.ts
|
|
2456
|
+
import { Inject, Injectable as Injectable3, Logger as Logger2, Optional as Optional2 } from "@nestjs/common";
|
|
2457
|
+
import * as crypto2 from "crypto";
|
|
2458
|
+
|
|
2459
|
+
// src/upload/upload.tokens.ts
|
|
2460
|
+
var AXIS_UPLOAD_SESSION_STORE = "AXIS_UPLOAD_SESSION_STORE";
|
|
2461
|
+
var AXIS_UPLOAD_FILE_STORE = "AXIS_UPLOAD_FILE_STORE";
|
|
2462
|
+
var AXIS_UPLOAD_RECEIPT_SIGNER = "AXIS_UPLOAD_RECEIPT_SIGNER";
|
|
2463
|
+
|
|
2464
|
+
// src/upload/axis-files.handlers.ts
|
|
2465
|
+
var AxisFilesDownloadHandler = class {
|
|
2466
|
+
constructor(sessions, files) {
|
|
2467
|
+
this.sessions = sessions;
|
|
2468
|
+
this.files = files;
|
|
2469
|
+
this.logger = new Logger2(AxisFilesDownloadHandler.name);
|
|
2470
|
+
this.name = "axis.files.download";
|
|
2471
|
+
this.open = true;
|
|
2472
|
+
this.description = "File download handler";
|
|
2473
|
+
}
|
|
2474
|
+
async execute(body, headers) {
|
|
2475
|
+
const h = headers;
|
|
2476
|
+
if (!h) throw new Error("MISSING_HEADERS");
|
|
2477
|
+
const uploadIdBytes = h.get(20);
|
|
2478
|
+
if (!uploadIdBytes) throw new Error("MISSING_UPLOAD_ID");
|
|
2479
|
+
const uploadId = new TextDecoder().decode(uploadIdBytes);
|
|
2480
|
+
let rangeStart = 0;
|
|
2481
|
+
let rangeLen = -1;
|
|
2482
|
+
const startBytes = h.get(21);
|
|
2483
|
+
if (startBytes) {
|
|
2484
|
+
const { value } = decodeVarint(startBytes);
|
|
2485
|
+
rangeStart = value;
|
|
2486
|
+
}
|
|
2487
|
+
const lenBytes = h.get(22);
|
|
2488
|
+
if (lenBytes) {
|
|
2489
|
+
const { value } = decodeVarint(lenBytes);
|
|
2490
|
+
rangeLen = value;
|
|
2491
|
+
}
|
|
2492
|
+
const session = await this.sessions.findByFileId(uploadId);
|
|
2493
|
+
if (!session) {
|
|
2494
|
+
throw new Error(`SESSION_NOT_FOUND: ${uploadId}`);
|
|
2495
|
+
}
|
|
2496
|
+
if (session.status !== "COMPLETE") {
|
|
2497
|
+
throw new Error(`FILE_NOT_READY: Status is ${session.status}`);
|
|
2498
|
+
}
|
|
2499
|
+
const stat = await this.files.statFinal(
|
|
2500
|
+
uploadId,
|
|
2501
|
+
session.filename
|
|
2502
|
+
);
|
|
2503
|
+
const fileSize = stat.size;
|
|
2504
|
+
if (rangeStart < 0) rangeStart = 0;
|
|
2505
|
+
if (rangeStart >= fileSize) throw new Error("RANGE_OUT_OF_BOUNDS");
|
|
2506
|
+
let end = fileSize;
|
|
2507
|
+
if (rangeLen >= 0) {
|
|
2508
|
+
end = Math.min(rangeStart + rangeLen, fileSize);
|
|
2509
|
+
}
|
|
2510
|
+
const actualLen = end - rangeStart;
|
|
2511
|
+
const buffer = await this.files.readFinalRange(
|
|
2512
|
+
uploadId,
|
|
2513
|
+
session.filename,
|
|
2514
|
+
rangeStart,
|
|
2515
|
+
actualLen
|
|
2516
|
+
);
|
|
2517
|
+
const responseHeaders = /* @__PURE__ */ new Map();
|
|
2518
|
+
responseHeaders.set(30, encodeVarint(fileSize));
|
|
2519
|
+
responseHeaders.set(31, encodeVarint(rangeStart));
|
|
2520
|
+
responseHeaders.set(32, encodeVarint(actualLen));
|
|
2521
|
+
return {
|
|
2522
|
+
ok: true,
|
|
2523
|
+
effect: "FILE_PART",
|
|
2524
|
+
body: buffer,
|
|
2525
|
+
headers: responseHeaders
|
|
2526
|
+
};
|
|
2527
|
+
}
|
|
2528
|
+
};
|
|
2529
|
+
__decorateClass([
|
|
2530
|
+
Intent("file.download", { absolute: true, kind: "read" })
|
|
2531
|
+
], AxisFilesDownloadHandler.prototype, "execute", 1);
|
|
2532
|
+
AxisFilesDownloadHandler = __decorateClass([
|
|
2533
|
+
Handler("axis.files.download"),
|
|
2534
|
+
Injectable3(),
|
|
2535
|
+
__decorateParam(0, Inject(AXIS_UPLOAD_SESSION_STORE)),
|
|
2536
|
+
__decorateParam(1, Inject(AXIS_UPLOAD_FILE_STORE))
|
|
2537
|
+
], AxisFilesDownloadHandler);
|
|
2538
|
+
var AxisFilesFinalizeHandler = class {
|
|
2539
|
+
constructor(sessions, files, keyring) {
|
|
2540
|
+
this.sessions = sessions;
|
|
2541
|
+
this.files = files;
|
|
2542
|
+
this.keyring = keyring;
|
|
2543
|
+
this.logger = new Logger2(AxisFilesFinalizeHandler.name);
|
|
2544
|
+
this.name = "axis.files.finalize";
|
|
2545
|
+
this.open = false;
|
|
2546
|
+
this.description = "File upload finalization handler";
|
|
2547
|
+
}
|
|
2548
|
+
async execute(body, headers) {
|
|
2549
|
+
const bodyStr = new TextDecoder().decode(body);
|
|
2550
|
+
const req = JSON.parse(bodyStr);
|
|
2551
|
+
const { fileId, expectedHash } = req;
|
|
2552
|
+
if (!fileId) throw new Error("MISSING_FILE_ID");
|
|
2553
|
+
const session = await this.sessions.findByFileId(fileId);
|
|
2554
|
+
if (!session) throw new Error("SESSION_NOT_FOUND");
|
|
2555
|
+
if (!await this.files.hasTemp(fileId)) {
|
|
2556
|
+
throw new Error("CHUNKS_NOT_FOUND");
|
|
2557
|
+
}
|
|
2558
|
+
const hash = crypto2.createHash("sha256");
|
|
2559
|
+
const rs = this.files.createTempReadStream(fileId);
|
|
2560
|
+
for await (const chunk of rs) {
|
|
2561
|
+
hash.update(chunk);
|
|
2562
|
+
}
|
|
2563
|
+
const finalHash = hash.digest("hex");
|
|
2564
|
+
if (expectedHash && finalHash !== expectedHash) {
|
|
2565
|
+
throw new Error("HASH_MISMATCH");
|
|
2566
|
+
}
|
|
2567
|
+
const finalPath = await this.files.moveTempToFinal(
|
|
2568
|
+
fileId,
|
|
2569
|
+
session.filename
|
|
2570
|
+
);
|
|
2571
|
+
await this.sessions.updateStatus(fileId, "COMPLETE", null);
|
|
2572
|
+
if (!this.keyring) {
|
|
2573
|
+
this.logger.warn("Receipt signer not configured; returning unsigned receipt");
|
|
2574
|
+
return {
|
|
2575
|
+
ok: true,
|
|
2576
|
+
effect: "FILE_FINALIZED",
|
|
2577
|
+
body: new TextEncoder().encode(
|
|
2578
|
+
JSON.stringify({
|
|
2579
|
+
uploadId: fileId,
|
|
2580
|
+
sha256_final: finalHash,
|
|
2581
|
+
totalSize: session.totalSize,
|
|
2582
|
+
tsMs: Date.now(),
|
|
2583
|
+
path: finalPath
|
|
2584
|
+
})
|
|
2585
|
+
)
|
|
2586
|
+
};
|
|
2587
|
+
}
|
|
2588
|
+
const receiptData = {
|
|
2589
|
+
uploadId: fileId,
|
|
2590
|
+
sha256_final: finalHash,
|
|
2591
|
+
totalSize: session.totalSize,
|
|
2592
|
+
tsMs: Date.now()
|
|
2593
|
+
};
|
|
2594
|
+
const receiptJson = JSON.stringify(receiptData);
|
|
2595
|
+
const receiptBody = new TextEncoder().encode(receiptJson);
|
|
2596
|
+
const SIG_PRESENT = 1;
|
|
2597
|
+
const responseFrame = {
|
|
2598
|
+
flags: SIG_PRESENT,
|
|
2599
|
+
headers: /* @__PURE__ */ new Map(),
|
|
2600
|
+
body: receiptBody,
|
|
2601
|
+
sig: new Uint8Array(0)
|
|
2602
|
+
};
|
|
2603
|
+
const signTarget = getSignTarget(responseFrame);
|
|
2604
|
+
const { sig, kid } = this.keyring.signActive(signTarget);
|
|
2605
|
+
responseFrame.sig = sig;
|
|
2606
|
+
return {
|
|
2607
|
+
ok: true,
|
|
2608
|
+
effect: "FILE_FINALIZED",
|
|
2609
|
+
data: encodeFrame(responseFrame),
|
|
2610
|
+
headers: /* @__PURE__ */ new Map([[1, new TextEncoder().encode(kid)]])
|
|
2611
|
+
};
|
|
2612
|
+
}
|
|
2613
|
+
};
|
|
2614
|
+
__decorateClass([
|
|
2615
|
+
Intent("file.finalize", { absolute: true, kind: "action" })
|
|
2616
|
+
], AxisFilesFinalizeHandler.prototype, "execute", 1);
|
|
2617
|
+
AxisFilesFinalizeHandler = __decorateClass([
|
|
2618
|
+
Handler("axis.files.finalize"),
|
|
2619
|
+
Injectable3(),
|
|
2620
|
+
__decorateParam(0, Inject(AXIS_UPLOAD_SESSION_STORE)),
|
|
2621
|
+
__decorateParam(1, Inject(AXIS_UPLOAD_FILE_STORE)),
|
|
2622
|
+
__decorateParam(2, Optional2()),
|
|
2623
|
+
__decorateParam(2, Inject(AXIS_UPLOAD_RECEIPT_SIGNER))
|
|
2624
|
+
], AxisFilesFinalizeHandler);
|
|
2625
|
+
|
|
2626
|
+
// src/upload/disk-upload-file.store.ts
|
|
2627
|
+
import * as fs from "fs";
|
|
2628
|
+
import * as path from "path";
|
|
2629
|
+
var DiskUploadFileStore = class {
|
|
2630
|
+
constructor(options) {
|
|
2631
|
+
this.uploadDir = options.uploadDir;
|
|
2632
|
+
this.chunkDir = options.chunkDir;
|
|
2633
|
+
}
|
|
2634
|
+
getFinalPath(fileId, filename) {
|
|
2635
|
+
const safeFilename = filename ? path.basename(filename) : fileId;
|
|
2636
|
+
return path.join(this.uploadDir, safeFilename);
|
|
2637
|
+
}
|
|
2638
|
+
getTempPath(fileId) {
|
|
2639
|
+
const safeId = path.basename(fileId);
|
|
2640
|
+
return path.join(this.chunkDir, safeId);
|
|
2641
|
+
}
|
|
2642
|
+
async statFinal(fileId, filename) {
|
|
2643
|
+
const finalPath = this.getFinalPath(fileId, filename);
|
|
2644
|
+
if (!fs.existsSync(finalPath)) {
|
|
2645
|
+
throw new Error("FILE_MISSING_ON_DISK");
|
|
2646
|
+
}
|
|
2647
|
+
const stat = fs.statSync(finalPath);
|
|
2648
|
+
return { path: finalPath, size: stat.size };
|
|
2649
|
+
}
|
|
2650
|
+
async readFinalRange(fileId, filename, start, length) {
|
|
2651
|
+
const finalPath = this.getFinalPath(fileId, filename);
|
|
2652
|
+
const buffer = Buffer.alloc(length);
|
|
2653
|
+
const fd = fs.openSync(finalPath, "r");
|
|
2654
|
+
try {
|
|
2655
|
+
fs.readSync(fd, buffer, 0, length, start);
|
|
2656
|
+
} finally {
|
|
2657
|
+
fs.closeSync(fd);
|
|
2658
|
+
}
|
|
2659
|
+
return buffer;
|
|
2660
|
+
}
|
|
2661
|
+
async hasTemp(fileId) {
|
|
2662
|
+
const tempPath = this.getTempPath(fileId);
|
|
2663
|
+
return fs.existsSync(tempPath);
|
|
2664
|
+
}
|
|
2665
|
+
async moveTempToFinal(fileId, filename) {
|
|
2666
|
+
const tempPath = this.getTempPath(fileId);
|
|
2667
|
+
const finalPath = this.getFinalPath(fileId, filename);
|
|
2668
|
+
try {
|
|
2669
|
+
await fs.promises.rename(tempPath, finalPath);
|
|
2670
|
+
} catch {
|
|
2671
|
+
await fs.promises.copyFile(tempPath, finalPath);
|
|
2672
|
+
await fs.promises.unlink(tempPath);
|
|
2673
|
+
}
|
|
2674
|
+
return finalPath;
|
|
2675
|
+
}
|
|
2676
|
+
createTempReadStream(fileId) {
|
|
2677
|
+
const tempPath = this.getTempPath(fileId);
|
|
2678
|
+
return fs.createReadStream(tempPath);
|
|
2679
|
+
}
|
|
2680
|
+
};
|
|
2397
2681
|
export {
|
|
2398
2682
|
ATS1_HDR,
|
|
2399
2683
|
ATS1_SCHEMA,
|
|
2400
2684
|
AXIS_MAGIC,
|
|
2401
2685
|
AXIS_OPCODES,
|
|
2686
|
+
AXIS_UPLOAD_FILE_STORE,
|
|
2687
|
+
AXIS_UPLOAD_RECEIPT_SIGNER,
|
|
2688
|
+
AXIS_UPLOAD_SESSION_STORE,
|
|
2402
2689
|
AXIS_VERSION,
|
|
2403
2690
|
ats1_exports as Ats1Codec,
|
|
2691
|
+
AxisFilesDownloadHandler,
|
|
2692
|
+
AxisFilesFinalizeHandler,
|
|
2404
2693
|
AxisFrameZ,
|
|
2405
2694
|
AxisIdDto,
|
|
2406
2695
|
T as AxisPacketTags,
|
|
@@ -2413,6 +2702,7 @@ export {
|
|
|
2413
2702
|
DEFAULT_CONTRACTS,
|
|
2414
2703
|
DEFAULT_TIMEOUT,
|
|
2415
2704
|
Decision,
|
|
2705
|
+
DiskUploadFileStore,
|
|
2416
2706
|
ERR_BAD_SIGNATURE,
|
|
2417
2707
|
ERR_CONTRACT_VIOLATION,
|
|
2418
2708
|
ERR_INVALID_PACKET,
|
|
@@ -2424,14 +2714,18 @@ export {
|
|
|
2424
2714
|
FLAG_HAS_WITNESS,
|
|
2425
2715
|
HANDLER_METADATA_KEY,
|
|
2426
2716
|
Handler,
|
|
2717
|
+
INTENT_BODY_KEY,
|
|
2427
2718
|
INTENT_METADATA_KEY,
|
|
2428
2719
|
INTENT_REQUIREMENTS,
|
|
2429
2720
|
INTENT_ROUTES_KEY,
|
|
2430
2721
|
INTENT_SENSITIVITY_MAP,
|
|
2722
|
+
INTENT_SENSORS_KEY,
|
|
2431
2723
|
INTENT_TIMEOUTS,
|
|
2432
2724
|
Intent,
|
|
2725
|
+
IntentBody,
|
|
2433
2726
|
IntentRouter,
|
|
2434
2727
|
IntentSensitivity,
|
|
2728
|
+
IntentSensors,
|
|
2435
2729
|
MAX_BODY_LEN,
|
|
2436
2730
|
MAX_FRAME_LEN,
|
|
2437
2731
|
MAX_HDR_LEN,
|
|
@@ -2465,6 +2759,7 @@ export {
|
|
|
2465
2759
|
Schema2012_PasskeyLoginVerifyRes,
|
|
2466
2760
|
Schema2021_PasskeyRegisterOptionsReq,
|
|
2467
2761
|
SensorDecisions,
|
|
2762
|
+
TLV,
|
|
2468
2763
|
TLV_ACTOR_ID,
|
|
2469
2764
|
TLV_AUD,
|
|
2470
2765
|
TLV_BODY_ARR,
|