@labacacia/nps-sdk 1.0.0-alpha.1 → 1.0.0-alpha.3
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/CHANGELOG.cn.md +57 -0
- package/CHANGELOG.md +57 -0
- package/CONTRIBUTING.cn.md +35 -0
- package/CONTRIBUTING.md +2 -0
- package/README.cn.md +155 -0
- package/README.md +5 -3
- package/dist/core/anchor-cache.d.ts +0 -0
- package/dist/core/anchor-cache.d.ts.map +0 -0
- package/dist/core/cache.d.ts +0 -0
- package/dist/core/cache.d.ts.map +0 -0
- package/dist/core/canonical-json.d.ts +0 -0
- package/dist/core/canonical-json.d.ts.map +0 -0
- package/dist/core/codec.d.ts +0 -0
- package/dist/core/codec.d.ts.map +0 -0
- package/dist/core/codecs/index.d.ts +0 -0
- package/dist/core/codecs/index.d.ts.map +0 -0
- package/dist/core/codecs/ncp-codec.d.ts +0 -0
- package/dist/core/codecs/ncp-codec.d.ts.map +0 -0
- package/dist/core/codecs/tier1-json-codec.d.ts +0 -0
- package/dist/core/codecs/tier1-json-codec.d.ts.map +0 -0
- package/dist/core/codecs/tier2-msgpack-codec.d.ts +0 -0
- package/dist/core/codecs/tier2-msgpack-codec.d.ts.map +0 -0
- package/dist/core/crypto-provider.d.ts +0 -0
- package/dist/core/crypto-provider.d.ts.map +0 -0
- package/dist/core/exceptions.d.ts +0 -0
- package/dist/core/exceptions.d.ts.map +0 -0
- package/dist/core/frame-header.d.ts +0 -0
- package/dist/core/frame-header.d.ts.map +0 -0
- package/dist/core/frame-registry.d.ts +0 -0
- package/dist/core/frame-registry.d.ts.map +0 -0
- package/dist/core/frames.d.ts +1 -0
- package/dist/core/frames.d.ts.map +1 -1
- package/dist/core/index.cjs +90 -9
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.ts +6 -4
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +405 -9
- package/dist/core/index.js.map +1 -1
- package/dist/core/registry.d.ts +0 -0
- package/dist/core/registry.d.ts.map +0 -0
- package/dist/core/status-codes.d.ts +0 -0
- package/dist/core/status-codes.d.ts.map +0 -0
- package/dist/index.cjs +3 -1551
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -9
- package/dist/index.js.map +1 -1
- package/dist/ncp/frames/anchor-frame.d.ts +0 -0
- package/dist/ncp/frames/anchor-frame.d.ts.map +0 -0
- package/dist/ncp/frames/caps-frame.d.ts +0 -0
- package/dist/ncp/frames/caps-frame.d.ts.map +0 -0
- package/dist/ncp/frames/diff-frame.d.ts +0 -0
- package/dist/ncp/frames/diff-frame.d.ts.map +0 -0
- package/dist/ncp/frames/error-frame.d.ts +0 -0
- package/dist/ncp/frames/error-frame.d.ts.map +0 -0
- package/dist/ncp/frames/hello-frame.d.ts +0 -0
- package/dist/ncp/frames/hello-frame.d.ts.map +0 -0
- package/dist/ncp/frames/stream-frame.d.ts +0 -0
- package/dist/ncp/frames/stream-frame.d.ts.map +0 -0
- package/dist/ncp/frames.d.ts +18 -0
- package/dist/ncp/frames.d.ts.map +1 -1
- package/dist/ncp/handshake.d.ts +0 -0
- package/dist/ncp/handshake.d.ts.map +0 -0
- package/dist/ncp/index.cjs +364 -164
- package/dist/ncp/index.cjs.map +1 -1
- package/dist/ncp/index.d.ts +0 -0
- package/dist/ncp/index.d.ts.map +0 -0
- package/dist/ncp/index.js +368 -12
- package/dist/ncp/index.js.map +1 -1
- package/dist/ncp/ncp-error-codes.d.ts +0 -0
- package/dist/ncp/ncp-error-codes.d.ts.map +0 -0
- package/dist/ncp/ncp-patch-format.d.ts +0 -0
- package/dist/ncp/ncp-patch-format.d.ts.map +0 -0
- package/dist/ncp/registry.d.ts +0 -0
- package/dist/ncp/registry.d.ts.map +1 -1
- package/dist/ncp/stream-manager.d.ts +0 -0
- package/dist/ncp/stream-manager.d.ts.map +0 -0
- package/dist/ndp/frames.d.ts +0 -0
- package/dist/ndp/frames.d.ts.map +0 -0
- package/dist/ndp/index.cjs +0 -0
- package/dist/ndp/index.cjs.map +0 -0
- package/dist/ndp/index.d.ts +0 -0
- package/dist/ndp/index.d.ts.map +0 -0
- package/dist/ndp/index.js +223 -6
- package/dist/ndp/index.js.map +1 -1
- package/dist/ndp/ndp-registry.d.ts +0 -0
- package/dist/ndp/ndp-registry.d.ts.map +0 -0
- package/dist/ndp/registry.d.ts +0 -0
- package/dist/ndp/registry.d.ts.map +0 -0
- package/dist/ndp/validator.d.ts +0 -0
- package/dist/ndp/validator.d.ts.map +0 -0
- package/dist/nip/frames.d.ts +0 -0
- package/dist/nip/frames.d.ts.map +0 -0
- package/dist/nip/identity.d.ts +0 -0
- package/dist/nip/identity.d.ts.map +0 -0
- package/dist/nip/index.cjs +0 -0
- package/dist/nip/index.cjs.map +0 -0
- package/dist/nip/index.d.ts +0 -0
- package/dist/nip/index.d.ts.map +0 -0
- package/dist/nip/index.js +187 -5
- package/dist/nip/index.js.map +1 -1
- package/dist/nip/registry.d.ts +0 -0
- package/dist/nip/registry.d.ts.map +0 -0
- package/dist/nop/client.d.ts +0 -0
- package/dist/nop/client.d.ts.map +0 -0
- package/dist/nop/frames.d.ts +0 -0
- package/dist/nop/frames.d.ts.map +0 -0
- package/dist/nop/index.cjs +62 -1
- package/dist/nop/index.cjs.map +1 -1
- package/dist/nop/index.d.ts +0 -0
- package/dist/nop/index.d.ts.map +0 -0
- package/dist/nop/index.js +789 -6
- package/dist/nop/index.js.map +1 -1
- package/dist/nop/models.d.ts +0 -0
- package/dist/nop/models.d.ts.map +0 -0
- package/dist/nop/nop-types.d.ts +0 -0
- package/dist/nop/nop-types.d.ts.map +0 -0
- package/dist/nop/registry.d.ts +0 -0
- package/dist/nop/registry.d.ts.map +0 -0
- package/dist/nwp/client.d.ts +0 -0
- package/dist/nwp/client.d.ts.map +0 -0
- package/dist/nwp/frames.d.ts +0 -0
- package/dist/nwp/frames.d.ts.map +0 -0
- package/dist/nwp/index.cjs +64 -2
- package/dist/nwp/index.cjs.map +1 -1
- package/dist/nwp/index.d.ts +0 -0
- package/dist/nwp/index.d.ts.map +0 -0
- package/dist/nwp/index.js +693 -5
- package/dist/nwp/index.js.map +1 -1
- package/dist/nwp/registry.d.ts +0 -0
- package/dist/nwp/registry.d.ts.map +0 -0
- package/dist/setup.d.ts +0 -0
- package/dist/setup.d.ts.map +0 -0
- package/doc/nps-sdk.core.cn.md +321 -0
- package/doc/nps-sdk.core.md +326 -0
- package/doc/nps-sdk.ncp.cn.md +270 -0
- package/doc/nps-sdk.ncp.md +276 -0
- package/doc/nps-sdk.ndp.cn.md +267 -0
- package/doc/nps-sdk.ndp.md +273 -0
- package/doc/nps-sdk.nip.cn.md +235 -0
- package/doc/nps-sdk.nip.md +242 -0
- package/doc/nps-sdk.nop.cn.md +329 -0
- package/doc/nps-sdk.nop.md +332 -0
- package/doc/nps-sdk.nwp.cn.md +217 -0
- package/doc/nps-sdk.nwp.md +224 -0
- package/doc/overview.cn.md +149 -0
- package/doc/overview.md +153 -0
- package/package.json +21 -4
- package/src/core/frames.ts +1 -0
- package/src/core/index.ts +37 -5
- package/src/index.ts +1 -1
- package/src/ncp/frames.ts +52 -0
- package/src/ncp/registry.ts +2 -1
- package/dist/codec-CmHeovTV.d.cts +0 -120
- package/dist/codec-CmHeovTV.d.ts +0 -120
- package/dist/core/anchor-cache.js +0 -104
- package/dist/core/anchor-cache.js.map +0 -1
- package/dist/core/cache.js +0 -80
- package/dist/core/cache.js.map +0 -1
- package/dist/core/canonical-json.js +0 -44
- package/dist/core/canonical-json.js.map +0 -1
- package/dist/core/codec.js +0 -119
- package/dist/core/codec.js.map +0 -1
- package/dist/core/codecs/index.js +0 -6
- package/dist/core/codecs/index.js.map +0 -1
- package/dist/core/codecs/ncp-codec.js +0 -93
- package/dist/core/codecs/ncp-codec.js.map +0 -1
- package/dist/core/codecs/tier1-json-codec.js +0 -28
- package/dist/core/codecs/tier1-json-codec.js.map +0 -1
- package/dist/core/codecs/tier2-msgpack-codec.js +0 -26
- package/dist/core/codecs/tier2-msgpack-codec.js.map +0 -1
- package/dist/core/crypto-provider.js +0 -10
- package/dist/core/crypto-provider.js.map +0 -1
- package/dist/core/exceptions.js +0 -52
- package/dist/core/exceptions.js.map +0 -1
- package/dist/core/frame-header.js +0 -185
- package/dist/core/frame-header.js.map +0 -1
- package/dist/core/frame-registry.js +0 -63
- package/dist/core/frame-registry.js.map +0 -1
- package/dist/core/frames.js +0 -153
- package/dist/core/frames.js.map +0 -1
- package/dist/core/index.d.cts +0 -41
- package/dist/core/registry.js +0 -17
- package/dist/core/registry.js.map +0 -1
- package/dist/core/status-codes.js +0 -38
- package/dist/core/status-codes.js.map +0 -1
- package/dist/frames-B3qLdl_g.d.cts +0 -77
- package/dist/frames-Ff7-ZPUl.d.ts +0 -77
- package/dist/index.d.cts +0 -21
- package/dist/ncp/frames/anchor-frame.js +0 -54
- package/dist/ncp/frames/anchor-frame.js.map +0 -1
- package/dist/ncp/frames/caps-frame.js +0 -29
- package/dist/ncp/frames/caps-frame.js.map +0 -1
- package/dist/ncp/frames/diff-frame.js +0 -37
- package/dist/ncp/frames/diff-frame.js.map +0 -1
- package/dist/ncp/frames/error-frame.js +0 -13
- package/dist/ncp/frames/error-frame.js.map +0 -1
- package/dist/ncp/frames/hello-frame.js +0 -25
- package/dist/ncp/frames/hello-frame.js.map +0 -1
- package/dist/ncp/frames/stream-frame.js +0 -18
- package/dist/ncp/frames/stream-frame.js.map +0 -1
- package/dist/ncp/frames.js +0 -147
- package/dist/ncp/frames.js.map +0 -1
- package/dist/ncp/handshake.js +0 -80
- package/dist/ncp/handshake.js.map +0 -1
- package/dist/ncp/index.d.cts +0 -6
- package/dist/ncp/ncp-error-codes.js +0 -32
- package/dist/ncp/ncp-error-codes.js.map +0 -1
- package/dist/ncp/ncp-patch-format.js +0 -13
- package/dist/ncp/ncp-patch-format.js.map +0 -1
- package/dist/ncp/registry.js +0 -12
- package/dist/ncp/registry.js.map +0 -1
- package/dist/ncp/stream-manager.js +0 -163
- package/dist/ncp/stream-manager.js.map +0 -1
- package/dist/ndp/frames.js +0 -87
- package/dist/ndp/frames.js.map +0 -1
- package/dist/ndp/index.d.cts +0 -86
- package/dist/ndp/ndp-registry.js +0 -79
- package/dist/ndp/ndp-registry.js.map +0 -1
- package/dist/ndp/registry.js +0 -10
- package/dist/ndp/registry.js.map +0 -1
- package/dist/ndp/validator.js +0 -48
- package/dist/ndp/validator.js.map +0 -1
- package/dist/nip/frames.js +0 -81
- package/dist/nip/frames.js.map +0 -1
- package/dist/nip/identity.js +0 -94
- package/dist/nip/identity.js.map +0 -1
- package/dist/nip/index.d.cts +0 -65
- package/dist/nip/registry.js +0 -10
- package/dist/nip/registry.js.map +0 -1
- package/dist/nop/client.js +0 -90
- package/dist/nop/client.js.map +0 -1
- package/dist/nop/frames.js +0 -148
- package/dist/nop/frames.js.map +0 -1
- package/dist/nop/index.d.cts +0 -155
- package/dist/nop/models.js +0 -50
- package/dist/nop/models.js.map +0 -1
- package/dist/nop/nop-types.js +0 -44
- package/dist/nop/nop-types.js.map +0 -1
- package/dist/nop/registry.js +0 -11
- package/dist/nop/registry.js.map +0 -1
- package/dist/nwp/client.js +0 -101
- package/dist/nwp/client.js.map +0 -1
- package/dist/nwp/frames.js +0 -81
- package/dist/nwp/frames.js.map +0 -1
- package/dist/nwp/index.d.cts +0 -65
- package/dist/nwp/registry.js +0 -9
- package/dist/nwp/registry.js.map +0 -1
- package/dist/setup.js +0 -29
- package/dist/setup.js.map +0 -1
package/dist/ncp/index.cjs
CHANGED
|
@@ -1,188 +1,388 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
var
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
3
|
+
var crypto = require('crypto');
|
|
4
|
+
var canonicalizeDefault = require('canonicalize');
|
|
5
|
+
|
|
6
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
|
+
|
|
8
|
+
var canonicalizeDefault__default = /*#__PURE__*/_interopDefault(canonicalizeDefault);
|
|
9
|
+
|
|
10
|
+
// src/ncp/frames/anchor-frame.ts
|
|
11
|
+
|
|
12
|
+
// src/core/frame-header.ts
|
|
13
|
+
var NcpError = class extends Error {
|
|
14
|
+
// `code` accepts NcpErrorCode constants (preferred) as well as NPS status
|
|
15
|
+
// strings that are not NCP-prefixed (e.g. "NPS-CLIENT-CONFLICT") for cases
|
|
16
|
+
// where the spec delegates to NPS-level codes without assigning an NCP code.
|
|
17
|
+
constructor(code, message) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.code = code;
|
|
20
|
+
this.name = "NcpError";
|
|
21
|
+
}
|
|
22
|
+
code;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// src/ncp/frames/anchor-frame.ts
|
|
26
|
+
var canonicalize = canonicalizeDefault__default.default;
|
|
27
|
+
var VALID_FIELD_TYPES = [
|
|
28
|
+
"string",
|
|
29
|
+
"uint64",
|
|
30
|
+
"int64",
|
|
31
|
+
"decimal",
|
|
32
|
+
"bool",
|
|
33
|
+
"timestamp",
|
|
34
|
+
"bytes",
|
|
35
|
+
"object",
|
|
36
|
+
"array"
|
|
37
|
+
];
|
|
38
|
+
function computeAnchorId(schema) {
|
|
39
|
+
const canonical = canonicalize(schema);
|
|
40
|
+
if (!canonical) {
|
|
41
|
+
throw new NcpError("NCP-ANCHOR-SCHEMA-INVALID", "Schema cannot be canonicalized");
|
|
42
|
+
}
|
|
43
|
+
const hash = crypto.createHash("sha256").update(canonical).digest("hex");
|
|
44
|
+
return `sha256:${hash}`;
|
|
45
|
+
}
|
|
46
|
+
function validateAnchorFrame(frame) {
|
|
47
|
+
for (const field of frame.schema.fields) {
|
|
48
|
+
if (!VALID_FIELD_TYPES.includes(field.type)) {
|
|
49
|
+
throw new NcpError(
|
|
50
|
+
"NCP-ANCHOR-SCHEMA-INVALID",
|
|
51
|
+
`Unsupported field type "${field.type}" for field "${field.name}". Valid types: ${VALID_FIELD_TYPES.join(", ")}`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const expected = computeAnchorId(frame.schema);
|
|
56
|
+
if (frame.anchor_id !== expected) {
|
|
57
|
+
throw new NcpError(
|
|
58
|
+
"NCP-ANCHOR-SCHEMA-INVALID",
|
|
59
|
+
`anchor_id mismatch: expected ${expected}, got ${frame.anchor_id}`
|
|
60
|
+
);
|
|
21
61
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/ncp/frames/caps-frame.ts
|
|
65
|
+
function validateCapsFrame(frame) {
|
|
66
|
+
if (frame.count !== frame.data.length) {
|
|
67
|
+
throw new NcpError(
|
|
68
|
+
"NPS-CLIENT-BAD-FRAME",
|
|
69
|
+
`CapsFrame count mismatch: count=${frame.count}, data.length=${frame.data.length}`
|
|
28
70
|
);
|
|
29
71
|
}
|
|
72
|
+
if (frame.inline_anchor !== void 0) {
|
|
73
|
+
const computed = computeAnchorId(frame.inline_anchor.schema);
|
|
74
|
+
if (frame.inline_anchor.anchor_id !== computed) {
|
|
75
|
+
throw new NcpError(
|
|
76
|
+
"NCP-ANCHOR-SCHEMA-INVALID",
|
|
77
|
+
`inline_anchor anchor_id mismatch: expected ${computed}, got ${frame.inline_anchor.anchor_id}`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/ncp/ncp-patch-format.ts
|
|
84
|
+
var PATCH_FORMAT = {
|
|
85
|
+
JSON_PATCH: "json_patch",
|
|
86
|
+
BINARY_BITSET: "binary_bitset"
|
|
30
87
|
};
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
frameType = 2 /* DIFF */;
|
|
43
|
-
preferredTier = 1 /* MSGPACK */;
|
|
44
|
-
toDict() {
|
|
45
|
-
return {
|
|
46
|
-
anchor_ref: this.anchorRef,
|
|
47
|
-
base_seq: this.baseSeq,
|
|
48
|
-
patch: this.patch.map((p) => ({ ...p })),
|
|
49
|
-
entity_id: this.entityId ?? null
|
|
50
|
-
};
|
|
88
|
+
function isValidPatchFormat(v) {
|
|
89
|
+
return v === PATCH_FORMAT.JSON_PATCH || v === PATCH_FORMAT.BINARY_BITSET;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/ncp/frames/diff-frame.ts
|
|
93
|
+
function validateDiffSeq(frame, currentSeq) {
|
|
94
|
+
if (frame.base_seq !== currentSeq) {
|
|
95
|
+
throw new NcpError(
|
|
96
|
+
"NCP-STREAM-SEQ-GAP",
|
|
97
|
+
`DiffFrame base_seq=${frame.base_seq} does not match current seq=${currentSeq}`
|
|
98
|
+
);
|
|
51
99
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
100
|
+
}
|
|
101
|
+
function validateDiffFrame(frame, encodingTier) {
|
|
102
|
+
const fmt = frame.patch_format;
|
|
103
|
+
if (fmt !== void 0 && !isValidPatchFormat(fmt)) {
|
|
104
|
+
throw new NcpError(
|
|
105
|
+
"NCP-DIFF-FORMAT-UNSUPPORTED",
|
|
106
|
+
`Unknown patch_format "${String(fmt)}"`
|
|
58
107
|
);
|
|
59
108
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
is_last: this.isLast,
|
|
83
|
-
data: this.data,
|
|
84
|
-
anchor_ref: this.anchorRef ?? null,
|
|
85
|
-
window_size: this.windowSize ?? null
|
|
86
|
-
};
|
|
109
|
+
if (fmt === "binary_bitset" && encodingTier !== 1 /* MsgPack */) {
|
|
110
|
+
throw new NcpError(
|
|
111
|
+
"NCP-DIFF-FORMAT-UNSUPPORTED",
|
|
112
|
+
"patch_format=binary_bitset requires Tier-2 MsgPack encoding"
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/ncp/frames/error-frame.ts
|
|
118
|
+
function isErrorFrame(obj) {
|
|
119
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
120
|
+
const o = obj;
|
|
121
|
+
return o.frame === "0xFE" && typeof o.status === "string" && typeof o.error === "string";
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// src/ncp/frames/hello-frame.ts
|
|
125
|
+
function validateHelloFrame(frame) {
|
|
126
|
+
if (!frame.nps_version) {
|
|
127
|
+
throw new NcpError(
|
|
128
|
+
"NPS-CLIENT-BAD-FRAME",
|
|
129
|
+
"HelloFrame missing required field: nps_version"
|
|
130
|
+
);
|
|
87
131
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
data["is_last"],
|
|
93
|
-
data["data"],
|
|
94
|
-
data["anchor_ref"] ?? void 0,
|
|
95
|
-
data["window_size"] ?? void 0
|
|
132
|
+
if (!frame.supported_encodings || frame.supported_encodings.length === 0) {
|
|
133
|
+
throw new NcpError(
|
|
134
|
+
"NPS-CLIENT-BAD-FRAME",
|
|
135
|
+
"HelloFrame missing required field: supported_encodings (must be non-empty)"
|
|
96
136
|
);
|
|
97
137
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
this.data = data;
|
|
104
|
-
this.nextCursor = nextCursor;
|
|
105
|
-
this.tokenEst = tokenEst;
|
|
106
|
-
this.cached = cached;
|
|
107
|
-
this.tokenizerUsed = tokenizerUsed;
|
|
108
|
-
}
|
|
109
|
-
anchorRef;
|
|
110
|
-
count;
|
|
111
|
-
data;
|
|
112
|
-
nextCursor;
|
|
113
|
-
tokenEst;
|
|
114
|
-
cached;
|
|
115
|
-
tokenizerUsed;
|
|
116
|
-
frameType = 4 /* CAPS */;
|
|
117
|
-
preferredTier = 1 /* MSGPACK */;
|
|
118
|
-
toDict() {
|
|
119
|
-
return {
|
|
120
|
-
anchor_ref: this.anchorRef,
|
|
121
|
-
count: this.count,
|
|
122
|
-
data: this.data,
|
|
123
|
-
next_cursor: this.nextCursor ?? null,
|
|
124
|
-
token_est: this.tokenEst ?? null,
|
|
125
|
-
cached: this.cached ?? null,
|
|
126
|
-
tokenizer_used: this.tokenizerUsed ?? null
|
|
127
|
-
};
|
|
138
|
+
if (!frame.supported_protocols || frame.supported_protocols.length === 0) {
|
|
139
|
+
throw new NcpError(
|
|
140
|
+
"NPS-CLIENT-BAD-FRAME",
|
|
141
|
+
"HelloFrame missing required field: supported_protocols (must be non-empty)"
|
|
142
|
+
);
|
|
128
143
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/ncp/frames/stream-frame.ts
|
|
147
|
+
var UUID_V4_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
148
|
+
function validateStreamFrame(frame) {
|
|
149
|
+
if (!UUID_V4_RE.test(frame.stream_id)) {
|
|
150
|
+
throw new NcpError(
|
|
151
|
+
"NPS-CLIENT-BAD-FRAME",
|
|
152
|
+
`stream_id "${frame.stream_id}" is not a valid UUID v4`
|
|
138
153
|
);
|
|
139
154
|
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// src/ncp/ncp-error-codes.ts
|
|
158
|
+
var NCP_ERROR_CODES = {
|
|
159
|
+
// Implementation-only codes (not in spec §6 — see test_results.md spec question 2)
|
|
160
|
+
NCP_FRAME_PARSE_ERROR: "NCP-FRAME-PARSE-ERROR",
|
|
161
|
+
NCP_FRAME_INCOMPLETE: "NCP-FRAME-INCOMPLETE",
|
|
162
|
+
// Spec-defined codes
|
|
163
|
+
NCP_FRAME_UNKNOWN_TYPE: "NCP-FRAME-UNKNOWN-TYPE",
|
|
164
|
+
NCP_FRAME_PAYLOAD_TOO_LARGE: "NCP-FRAME-PAYLOAD-TOO-LARGE",
|
|
165
|
+
NCP_FRAME_FLAGS_INVALID: "NCP-FRAME-FLAGS-INVALID",
|
|
166
|
+
NCP_ANCHOR_NOT_FOUND: "NCP-ANCHOR-NOT-FOUND",
|
|
167
|
+
NCP_ANCHOR_SCHEMA_INVALID: "NCP-ANCHOR-SCHEMA-INVALID",
|
|
168
|
+
NCP_ANCHOR_ID_MISMATCH: "NCP-ANCHOR-ID-MISMATCH",
|
|
169
|
+
NCP_ANCHOR_STALE: "NCP-ANCHOR-STALE",
|
|
170
|
+
NCP_STREAM_SEQ_GAP: "NCP-STREAM-SEQ-GAP",
|
|
171
|
+
NCP_STREAM_NOT_FOUND: "NCP-STREAM-NOT-FOUND",
|
|
172
|
+
NCP_STREAM_LIMIT_EXCEEDED: "NCP-STREAM-LIMIT-EXCEEDED",
|
|
173
|
+
NCP_STREAM_WINDOW_OVERFLOW: "NCP-STREAM-WINDOW-OVERFLOW",
|
|
174
|
+
NCP_ENCODING_UNSUPPORTED: "NCP-ENCODING-UNSUPPORTED",
|
|
175
|
+
NCP_DIFF_FORMAT_UNSUPPORTED: "NCP-DIFF-FORMAT-UNSUPPORTED",
|
|
176
|
+
NCP_VERSION_INCOMPATIBLE: "NCP-VERSION-INCOMPATIBLE",
|
|
177
|
+
NCP_ENC_NOT_NEGOTIATED: "NCP-ENC-NOT-NEGOTIATED",
|
|
178
|
+
NCP_ENC_AUTH_FAILED: "NCP-ENC-AUTH-FAILED"
|
|
140
179
|
};
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
180
|
+
|
|
181
|
+
// src/ncp/handshake.ts
|
|
182
|
+
function parseVersion(v) {
|
|
183
|
+
return v.split(".").map((p) => Number.parseInt(p, 10));
|
|
184
|
+
}
|
|
185
|
+
function compareVersions(a, b) {
|
|
186
|
+
const partsA = parseVersion(a);
|
|
187
|
+
const partsB = parseVersion(b);
|
|
188
|
+
const len = Math.max(partsA.length, partsB.length);
|
|
189
|
+
for (let i = 0; i < len; i += 1) {
|
|
190
|
+
const x = partsA[i] ?? 0;
|
|
191
|
+
const y = partsB[i] ?? 0;
|
|
192
|
+
if (x !== y) return x - y;
|
|
193
|
+
}
|
|
194
|
+
return 0;
|
|
195
|
+
}
|
|
196
|
+
function negotiateVersion(client, server) {
|
|
197
|
+
const clientMin = client.min_version ?? client.nps_version;
|
|
198
|
+
const serverVersion = server.nps_version;
|
|
199
|
+
if (compareVersions(clientMin, serverVersion) > 0) {
|
|
155
200
|
return {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
details: this.details ?? null
|
|
201
|
+
session_version: serverVersion,
|
|
202
|
+
compatible: false,
|
|
203
|
+
error_code: "NCP-VERSION-INCOMPATIBLE"
|
|
160
204
|
};
|
|
161
205
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
206
|
+
const sessionVersion = compareVersions(client.nps_version, serverVersion) <= 0 ? client.nps_version : serverVersion;
|
|
207
|
+
return { session_version: sessionVersion, compatible: true };
|
|
208
|
+
}
|
|
209
|
+
function negotiateEncoding(client, server) {
|
|
210
|
+
const serverSet = new Set(server);
|
|
211
|
+
if (client.includes("msgpack") && serverSet.has("msgpack")) {
|
|
212
|
+
return { encoding: "msgpack" };
|
|
169
213
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
214
|
+
if (client.includes("json") && serverSet.has("json")) {
|
|
215
|
+
return { encoding: "json" };
|
|
216
|
+
}
|
|
217
|
+
for (const enc of client) {
|
|
218
|
+
if (serverSet.has(enc)) {
|
|
219
|
+
return { encoding: enc };
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return { encoding: null };
|
|
179
223
|
}
|
|
180
224
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
225
|
+
// src/ncp/stream-manager.ts
|
|
226
|
+
var StreamManager = class {
|
|
227
|
+
streams = /* @__PURE__ */ new Map();
|
|
228
|
+
outgoing = /* @__PURE__ */ new Map();
|
|
229
|
+
maxConcurrent;
|
|
230
|
+
constructor(options) {
|
|
231
|
+
this.maxConcurrent = options?.maxConcurrent ?? 32;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Receive a StreamFrame chunk.
|
|
235
|
+
*
|
|
236
|
+
* @returns true if stream is complete (is_last=true or error_code set).
|
|
237
|
+
* @throws {NcpError} NCP-STREAM-LIMIT-EXCEEDED if too many concurrent streams.
|
|
238
|
+
* @throws {NcpError} NCP-STREAM-NOT-FOUND if frame.seq > 0 for a stream that was never opened.
|
|
239
|
+
* @throws {NcpError} NPS-CLIENT-CONFLICT if the stream_id was already completed (stream-id reuse; see test_cases NCP-S-04).
|
|
240
|
+
* @throws {NcpError} NCP-STREAM-SEQ-GAP if sequence number is not expected.
|
|
241
|
+
*/
|
|
242
|
+
receive(frame) {
|
|
243
|
+
let stream = this.streams.get(frame.stream_id);
|
|
244
|
+
if (!stream) {
|
|
245
|
+
if (frame.seq !== 0) {
|
|
246
|
+
throw new NcpError(
|
|
247
|
+
NCP_ERROR_CODES.NCP_STREAM_NOT_FOUND,
|
|
248
|
+
`Unknown stream_id ${frame.stream_id} \u2014 first frame must have seq=0`
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
if (this.streams.size >= this.maxConcurrent) {
|
|
252
|
+
throw new NcpError(
|
|
253
|
+
NCP_ERROR_CODES.NCP_STREAM_LIMIT_EXCEEDED,
|
|
254
|
+
`Max concurrent streams (${this.maxConcurrent}) exceeded`
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
stream = {
|
|
258
|
+
streamId: frame.stream_id,
|
|
259
|
+
expectedSeq: 0,
|
|
260
|
+
chunks: [],
|
|
261
|
+
completed: false
|
|
262
|
+
};
|
|
263
|
+
this.streams.set(frame.stream_id, stream);
|
|
264
|
+
}
|
|
265
|
+
if (stream.completed) {
|
|
266
|
+
throw new NcpError(
|
|
267
|
+
"NPS-CLIENT-CONFLICT",
|
|
268
|
+
`Stream ${frame.stream_id} is already completed \u2014 cannot reuse stream_id`
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
if (frame.seq !== stream.expectedSeq) {
|
|
272
|
+
if (frame.seq < stream.expectedSeq) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
throw new NcpError(
|
|
276
|
+
NCP_ERROR_CODES.NCP_STREAM_SEQ_GAP,
|
|
277
|
+
`Expected seq ${stream.expectedSeq}, got ${frame.seq} on stream ${frame.stream_id}`
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
stream.chunks.push(frame.data);
|
|
281
|
+
stream.expectedSeq = frame.seq + 1;
|
|
282
|
+
if (frame.error_code) {
|
|
283
|
+
stream.completed = true;
|
|
284
|
+
stream.errorCode = frame.error_code;
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
if (frame.is_last) {
|
|
288
|
+
stream.completed = true;
|
|
289
|
+
return true;
|
|
290
|
+
}
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Send a StreamFrame on an outgoing stream, enforcing window-based flow control.
|
|
295
|
+
*
|
|
296
|
+
* - seq=0 with window_size initialises remainingWindow (no decrement for opening frame).
|
|
297
|
+
* - Subsequent sends decrement remainingWindow when flow control is active.
|
|
298
|
+
* - Throws NCP-STREAM-WINDOW-OVERFLOW when remainingWindow === 0.
|
|
299
|
+
*
|
|
300
|
+
* @throws {NcpError} NCP-STREAM-WINDOW-OVERFLOW if window is exhausted.
|
|
301
|
+
*/
|
|
302
|
+
send(frame) {
|
|
303
|
+
let out = this.outgoing.get(frame.stream_id);
|
|
304
|
+
if (!out) {
|
|
305
|
+
out = {
|
|
306
|
+
streamId: frame.stream_id,
|
|
307
|
+
remainingWindow: void 0,
|
|
308
|
+
paused: false
|
|
309
|
+
};
|
|
310
|
+
this.outgoing.set(frame.stream_id, out);
|
|
311
|
+
}
|
|
312
|
+
if (frame.seq === 0 && frame.window_size !== void 0) {
|
|
313
|
+
out.remainingWindow = frame.window_size;
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
if (out.remainingWindow !== void 0) {
|
|
317
|
+
if (out.remainingWindow === 0) {
|
|
318
|
+
throw new NcpError(
|
|
319
|
+
NCP_ERROR_CODES.NCP_STREAM_WINDOW_OVERFLOW,
|
|
320
|
+
`Window exhausted on stream ${frame.stream_id}`
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
out.remainingWindow -= 1;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Update the send window for a stream.
|
|
328
|
+
*
|
|
329
|
+
* Called when a reverse-direction StreamFrame arrives with data=[] and window_size set.
|
|
330
|
+
* Replaces remainingWindow with new_size. Sets paused=true when new_size === 0.
|
|
331
|
+
*/
|
|
332
|
+
updateWindow(streamId, newSize) {
|
|
333
|
+
let out = this.outgoing.get(streamId);
|
|
334
|
+
if (!out) {
|
|
335
|
+
out = {
|
|
336
|
+
streamId,
|
|
337
|
+
remainingWindow: newSize,
|
|
338
|
+
paused: newSize === 0
|
|
339
|
+
};
|
|
340
|
+
this.outgoing.set(streamId, out);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
out.remainingWindow = newSize;
|
|
344
|
+
out.paused = newSize === 0;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Returns true when the outgoing stream is paused (window=0 was received).
|
|
348
|
+
* Resumes (returns false) once a non-zero window update arrives.
|
|
349
|
+
*/
|
|
350
|
+
isPaused(streamId) {
|
|
351
|
+
return this.outgoing.get(streamId)?.paused ?? false;
|
|
352
|
+
}
|
|
353
|
+
/** Get reassembled data for a completed stream. */
|
|
354
|
+
getData(streamId) {
|
|
355
|
+
const stream = this.streams.get(streamId);
|
|
356
|
+
if (!stream || !stream.completed) return null;
|
|
357
|
+
return stream.chunks.flat();
|
|
358
|
+
}
|
|
359
|
+
/** Get error code if stream terminated with error. */
|
|
360
|
+
getError(streamId) {
|
|
361
|
+
return this.streams.get(streamId)?.errorCode;
|
|
362
|
+
}
|
|
363
|
+
/** Number of active (non-completed) streams. */
|
|
364
|
+
get activeCount() {
|
|
365
|
+
let count = 0;
|
|
366
|
+
for (const s of this.streams.values()) {
|
|
367
|
+
if (!s.completed) count++;
|
|
368
|
+
}
|
|
369
|
+
return count;
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
exports.NCP_ERROR_CODES = NCP_ERROR_CODES;
|
|
374
|
+
exports.PATCH_FORMAT = PATCH_FORMAT;
|
|
375
|
+
exports.StreamManager = StreamManager;
|
|
376
|
+
exports.computeAnchorId = computeAnchorId;
|
|
377
|
+
exports.isErrorFrame = isErrorFrame;
|
|
378
|
+
exports.isValidPatchFormat = isValidPatchFormat;
|
|
379
|
+
exports.negotiateEncoding = negotiateEncoding;
|
|
380
|
+
exports.negotiateVersion = negotiateVersion;
|
|
381
|
+
exports.validateAnchorFrame = validateAnchorFrame;
|
|
382
|
+
exports.validateCapsFrame = validateCapsFrame;
|
|
383
|
+
exports.validateDiffFrame = validateDiffFrame;
|
|
384
|
+
exports.validateDiffSeq = validateDiffSeq;
|
|
385
|
+
exports.validateHelloFrame = validateHelloFrame;
|
|
386
|
+
exports.validateStreamFrame = validateStreamFrame;
|
|
187
387
|
//# sourceMappingURL=index.cjs.map
|
|
188
388
|
//# sourceMappingURL=index.cjs.map
|