@api.global/typedsocket 6.0.0 → 6.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.clienttagreconciler.d.ts +51 -0
- package/dist_ts/classes.clienttagreconciler.js +290 -0
- package/dist_ts/classes.nativebyteerror.js +26 -0
- package/dist_ts/{nativebytes.manager.d.ts → classes.nativebytemanager.d.ts} +8 -3
- package/dist_ts/classes.nativebytemanager.js +2343 -0
- package/dist_ts/{typedsocket.classes.typedsocket.d.ts → classes.typedsocket.d.ts} +21 -21
- package/dist_ts/{typedsocket.classes.typedsocket.js → classes.typedsocket.js} +123 -260
- package/dist_ts/{typedsocket.tags.d.ts → classes.typedsockettagpolicymanager.d.ts} +9 -2
- package/dist_ts/classes.typedsockettagpolicymanager.js +549 -0
- package/dist_ts/{nativebytes.constants.js → constants.nativebytes.js} +1 -1
- package/dist_ts/{nativebytes.codec.d.ts → helpers.nativebytecodec.d.ts} +1 -1
- package/dist_ts/helpers.nativebytecodec.js +328 -0
- package/dist_ts/helpers.websocket.d.ts +8 -0
- package/dist_ts/helpers.websocket.js +13 -0
- package/dist_ts/index.d.ts +9 -8
- package/dist_ts/index.js +6 -6
- package/dist_ts/interfaces.diagnostics.d.ts +73 -0
- package/dist_ts/interfaces.diagnostics.js +2 -0
- package/dist_ts/{nativebytes.types.d.ts → interfaces.nativebytes.d.ts} +2 -2
- package/dist_ts/interfaces.nativebytes.js +2 -0
- package/dist_ts/plugins.js +14 -0
- package/package.json +1 -1
- package/readme.hints.md +26 -1
- package/readme.md +120 -55
- package/dist_ts/nativebytes.codec.js +0 -328
- package/dist_ts/nativebytes.errors.js +0 -26
- package/dist_ts/nativebytes.manager.js +0 -2308
- package/dist_ts/nativebytes.types.js +0 -2
- package/dist_ts/typedsocket.plugins.js +0 -14
- package/dist_ts/typedsocket.tags.js +0 -521
- /package/dist_ts/{nativebytes.errors.d.ts → classes.nativebyteerror.d.ts} +0 -0
- /package/dist_ts/{nativebytes.constants.d.ts → constants.nativebytes.d.ts} +0 -0
- /package/dist_ts/{typedsocket.plugins.d.ts → plugins.d.ts} +0 -0
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
// @apiglobal scope
|
|
2
|
-
import * as typedrequest from '@api.global/typedrequest';
|
|
3
|
-
import * as typedrequestInterfaces from '@api.global/typedrequest-interfaces';
|
|
4
|
-
export { typedrequest, typedrequestInterfaces };
|
|
5
|
-
// @pushrocks scope
|
|
6
|
-
import * as smartdelay from '@push.rocks/smartdelay';
|
|
7
|
-
import * as smarthash from '@push.rocks/smarthash/web';
|
|
8
|
-
import * as smartjson from '@push.rocks/smartjson';
|
|
9
|
-
import * as smartpromise from '@push.rocks/smartpromise';
|
|
10
|
-
import * as smartrx from '@push.rocks/smartrx';
|
|
11
|
-
import * as smartstring from '@push.rocks/smartstring';
|
|
12
|
-
import * as smarturl from '@push.rocks/smarturl';
|
|
13
|
-
export { smartdelay, smarthash, smartjson, smartpromise, smartrx, smartstring, smarturl };
|
|
14
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZWRzb2NrZXQucGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3R5cGVkc29ja2V0LnBsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsbUJBQW1CO0FBQ25CLE9BQU8sS0FBSyxZQUFZLE1BQU0sMEJBQTBCLENBQUM7QUFDekQsT0FBTyxLQUFLLHNCQUFzQixNQUFNLHFDQUFxQyxDQUFDO0FBRTlFLE9BQU8sRUFBRSxZQUFZLEVBQUUsc0JBQXNCLEVBQUUsQ0FBQztBQUVoRCxtQkFBbUI7QUFDbkIsT0FBTyxLQUFLLFVBQVUsTUFBTSx3QkFBd0IsQ0FBQztBQUNyRCxPQUFPLEtBQUssU0FBUyxNQUFNLDJCQUEyQixDQUFDO0FBQ3ZELE9BQU8sS0FBSyxTQUFTLE1BQU0sdUJBQXVCLENBQUM7QUFDbkQsT0FBTyxLQUFLLFlBQVksTUFBTSwwQkFBMEIsQ0FBQztBQUN6RCxPQUFPLEtBQUssT0FBTyxNQUFNLHFCQUFxQixDQUFDO0FBQy9DLE9BQU8sS0FBSyxXQUFXLE1BQU0seUJBQXlCLENBQUM7QUFDdkQsT0FBTyxLQUFLLFFBQVEsTUFBTSxzQkFBc0IsQ0FBQztBQUVqRCxPQUFPLEVBQUUsVUFBVSxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsWUFBWSxFQUFFLE9BQU8sRUFBRSxXQUFXLEVBQUUsUUFBUSxFQUFFLENBQUMifQ==
|
|
@@ -1,521 +0,0 @@
|
|
|
1
|
-
import * as plugins from './typedsocket.plugins.js';
|
|
2
|
-
export const TYPEDSOCKET_MAX_TAG_NAME_BYTES = 128;
|
|
3
|
-
export const TYPEDSOCKET_MAX_TAG_PAYLOAD_BYTES = 4 * 1024;
|
|
4
|
-
export const TYPEDSOCKET_MAX_CLIENT_TAGS_PER_PEER = 16;
|
|
5
|
-
export const TYPEDSOCKET_MAX_CLIENT_TAG_RETAINED_BYTES_PER_PEER = 16 * 1024;
|
|
6
|
-
export const TYPEDSOCKET_MAX_RETAINED_TAG_MUTATIONS_PER_PEER = 8;
|
|
7
|
-
export const TYPEDSOCKET_MAX_RETAINED_TAG_MUTATIONS_PER_RULE = 32;
|
|
8
|
-
export const TYPEDSOCKET_MAX_RETAINED_TAG_MUTATIONS_PER_SERVER = 256;
|
|
9
|
-
export const TYPEDSOCKET_MAX_TAG_AUTHORIZATION_TIMEOUT_MS = 5_000;
|
|
10
|
-
export const TYPEDSOCKET_MAX_SERVER_TAG_NAMES_PER_PEER = 64;
|
|
11
|
-
export const TYPEDSOCKET_MAX_SERVER_TAG_RETAINED_BYTES_PER_PEER = 64 * 1024;
|
|
12
|
-
const TAG_PREFIX = '__typedsocket_tag__';
|
|
13
|
-
const textEncoder = new TextEncoder();
|
|
14
|
-
export class TypedSocketClientTagDeniedError extends Error {
|
|
15
|
-
constructor(codeArg, messageArg) {
|
|
16
|
-
super(messageArg);
|
|
17
|
-
this.name = 'TypedSocketClientTagDeniedError';
|
|
18
|
-
this.code = codeArg;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
function deny(codeArg, messageArg) {
|
|
22
|
-
return {
|
|
23
|
-
success: false,
|
|
24
|
-
code: codeArg,
|
|
25
|
-
message: messageArg,
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
function validateTagName(nameArg) {
|
|
29
|
-
if (typeof nameArg !== 'string' || nameArg.length === 0) {
|
|
30
|
-
return deny('INVALID_TAG_NAME', 'TypedSocket tag names must be non-empty strings.');
|
|
31
|
-
}
|
|
32
|
-
if (textEncoder.encode(nameArg).byteLength > TYPEDSOCKET_MAX_TAG_NAME_BYTES) {
|
|
33
|
-
return deny('TAG_NAME_TOO_LARGE', `TypedSocket tag names must not exceed ${TYPEDSOCKET_MAX_TAG_NAME_BYTES} UTF-8 bytes.`);
|
|
34
|
-
}
|
|
35
|
-
return undefined;
|
|
36
|
-
}
|
|
37
|
-
export function createTypedSocketTagPayloadSnapshot(payloadArg) {
|
|
38
|
-
let encoded;
|
|
39
|
-
let payload;
|
|
40
|
-
try {
|
|
41
|
-
encoded = plugins.smartjson.stringify({ value: payloadArg });
|
|
42
|
-
const parsed = plugins.smartjson.parse(encoded);
|
|
43
|
-
payload = typeof parsed === 'object' && parsed !== null
|
|
44
|
-
? Reflect.get(parsed, 'value')
|
|
45
|
-
: undefined;
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
throw new TypedSocketClientTagDeniedError('INVALID_TAG_PAYLOAD', 'TypedSocket tag payloads must be SmartJSON-serializable.');
|
|
49
|
-
}
|
|
50
|
-
const byteLength = textEncoder.encode(encoded).byteLength;
|
|
51
|
-
if (byteLength > TYPEDSOCKET_MAX_TAG_PAYLOAD_BYTES) {
|
|
52
|
-
throw new TypedSocketClientTagDeniedError('TAG_PAYLOAD_TOO_LARGE', `TypedSocket tag payloads must not exceed ${TYPEDSOCKET_MAX_TAG_PAYLOAD_BYTES} serialized bytes.`);
|
|
53
|
-
}
|
|
54
|
-
return { payload, encoded, byteLength };
|
|
55
|
-
}
|
|
56
|
-
function cloneTagPayload(payloadArg) {
|
|
57
|
-
return createTypedSocketTagPayloadSnapshot(payloadArg).payload;
|
|
58
|
-
}
|
|
59
|
-
export class TypedSocketTagPolicyManager {
|
|
60
|
-
constructor(policyArg) {
|
|
61
|
-
this.rules = new Map();
|
|
62
|
-
this.peerStates = new WeakMap();
|
|
63
|
-
this.ruleStates = new Map();
|
|
64
|
-
this.connectionAuthorityIds = new WeakMap();
|
|
65
|
-
this.routingSurfaceAuthorityIds = new WeakMap();
|
|
66
|
-
this.closedPeers = new WeakSet();
|
|
67
|
-
this.retainedMutationWork = 0;
|
|
68
|
-
this.stopping = false;
|
|
69
|
-
const timeoutMs = policyArg?.authorizationTimeoutMs
|
|
70
|
-
?? TYPEDSOCKET_MAX_TAG_AUTHORIZATION_TIMEOUT_MS;
|
|
71
|
-
if (!Number.isSafeInteger(timeoutMs)
|
|
72
|
-
|| timeoutMs < 1
|
|
73
|
-
|| timeoutMs > TYPEDSOCKET_MAX_TAG_AUTHORIZATION_TIMEOUT_MS) {
|
|
74
|
-
throw new Error(`clientTagPolicy.authorizationTimeoutMs must be an integer from 1 to ${TYPEDSOCKET_MAX_TAG_AUTHORIZATION_TIMEOUT_MS}.`);
|
|
75
|
-
}
|
|
76
|
-
this.authorizationTimeoutMs = timeoutMs;
|
|
77
|
-
for (const rule of policyArg?.rules ?? []) {
|
|
78
|
-
const nameDenial = validateTagName(rule?.name);
|
|
79
|
-
if (nameDenial)
|
|
80
|
-
throw new Error(nameDenial.message);
|
|
81
|
-
if (rule.owner !== 'client' && rule.owner !== 'server') {
|
|
82
|
-
throw new Error(`TypedSocket client tag rule "${rule.name}" has an invalid owner.`);
|
|
83
|
-
}
|
|
84
|
-
if (typeof rule.validateAndAuthorize !== 'function') {
|
|
85
|
-
throw new Error(`TypedSocket client tag rule "${rule.name}" requires validateAndAuthorize().`);
|
|
86
|
-
}
|
|
87
|
-
if (this.rules.has(rule.name)) {
|
|
88
|
-
throw new Error(`TypedSocket client tag rule "${rule.name}" is duplicated.`);
|
|
89
|
-
}
|
|
90
|
-
this.rules.set(rule.name, Object.freeze({ ...rule }));
|
|
91
|
-
this.ruleStates.set(rule.name, { retainedWork: 0, orphanedAuthorizations: 0 });
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
async setClientTag(peerArg, nameArg, payloadArg) {
|
|
95
|
-
const common = this.validateClientMutation(peerArg, nameArg);
|
|
96
|
-
if ('success' in common)
|
|
97
|
-
return common;
|
|
98
|
-
let payloadSnapshot;
|
|
99
|
-
try {
|
|
100
|
-
payloadSnapshot = createTypedSocketTagPayloadSnapshot(payloadArg);
|
|
101
|
-
}
|
|
102
|
-
catch (errorArg) {
|
|
103
|
-
return errorArg instanceof TypedSocketClientTagDeniedError
|
|
104
|
-
? deny(errorArg.code, errorArg.message)
|
|
105
|
-
: deny('INVALID_TAG_PAYLOAD', 'TypedSocket tag payload validation failed.');
|
|
106
|
-
}
|
|
107
|
-
return this.enqueueMutation(common.peer, common.rule, common.name, async (stateArg, executionArg) => {
|
|
108
|
-
return this.executeSet(common.peer, stateArg, common.rule, common.name, payloadSnapshot, executionArg);
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
async removeClientTag(peerArg, nameArg) {
|
|
112
|
-
const common = this.validateClientMutation(peerArg, nameArg);
|
|
113
|
-
if ('success' in common)
|
|
114
|
-
return common;
|
|
115
|
-
return this.enqueueMutation(common.peer, common.rule, common.name, async (stateArg, executionArg) => {
|
|
116
|
-
return this.executeRemove(common.peer, stateArg, common.rule, common.name, executionArg);
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
setServerTag(peerArg, nameArg, payloadArg) {
|
|
120
|
-
this.assertServerMutationName(nameArg);
|
|
121
|
-
if (!this.isPeerLive(peerArg)) {
|
|
122
|
-
throw new Error('Cannot set a TypedSocket server tag on a detached or closed connection.');
|
|
123
|
-
}
|
|
124
|
-
const payloadSnapshot = createTypedSocketTagPayloadSnapshot(payloadArg);
|
|
125
|
-
const state = this.getPeerState(peerArg);
|
|
126
|
-
this.retainServerTag(state, nameArg, payloadSnapshot.byteLength);
|
|
127
|
-
this.releaseAdmission(state, nameArg);
|
|
128
|
-
state.protectedServerNames.add(nameArg);
|
|
129
|
-
peerArg.tags.add(nameArg);
|
|
130
|
-
peerArg.data.set(`${TAG_PREFIX}${nameArg}`, payloadSnapshot.payload);
|
|
131
|
-
}
|
|
132
|
-
removeServerTag(peerArg, nameArg) {
|
|
133
|
-
this.assertServerMutationName(nameArg);
|
|
134
|
-
if (!this.isPeerLive(peerArg)) {
|
|
135
|
-
throw new Error('Cannot remove a TypedSocket server tag from a detached or closed connection.');
|
|
136
|
-
}
|
|
137
|
-
const state = this.getPeerState(peerArg);
|
|
138
|
-
this.retainServerTag(state, nameArg, 0);
|
|
139
|
-
this.releaseAdmission(state, nameArg);
|
|
140
|
-
state.protectedServerNames.add(nameArg);
|
|
141
|
-
peerArg.tags.delete(nameArg);
|
|
142
|
-
peerArg.data.delete(`${TAG_PREFIX}${nameArg}`);
|
|
143
|
-
}
|
|
144
|
-
handlePeerClose(peerArg) {
|
|
145
|
-
this.closedPeers.add(peerArg);
|
|
146
|
-
const state = this.peerStates.get(peerArg);
|
|
147
|
-
if (!state)
|
|
148
|
-
return;
|
|
149
|
-
for (const controller of state.authorizationControllers) {
|
|
150
|
-
controller.abort(new Error('TypedSocket connection closed.'));
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
setAttachedPeersProvider(providerArg) {
|
|
154
|
-
this.attachedPeersProvider = providerArg;
|
|
155
|
-
}
|
|
156
|
-
stop(peersArg) {
|
|
157
|
-
this.stopping = true;
|
|
158
|
-
for (const peer of peersArg)
|
|
159
|
-
this.handlePeerClose(peer);
|
|
160
|
-
this.attachedPeersProvider = undefined;
|
|
161
|
-
}
|
|
162
|
-
getRetainedMutationWork() {
|
|
163
|
-
return this.retainedMutationWork;
|
|
164
|
-
}
|
|
165
|
-
getRetainedMutationWorkForRule(nameArg) {
|
|
166
|
-
return this.ruleStates.get(nameArg)?.retainedWork ?? 0;
|
|
167
|
-
}
|
|
168
|
-
validateClientMutation(peerArg, nameArg) {
|
|
169
|
-
const nameDenial = validateTagName(nameArg);
|
|
170
|
-
if (nameDenial)
|
|
171
|
-
return nameDenial;
|
|
172
|
-
if (!peerArg) {
|
|
173
|
-
return deny('TAG_CONNECTION_CLOSED', 'TypedSocket tag mutation has no live connection.');
|
|
174
|
-
}
|
|
175
|
-
if (!this.isPeerLive(peerArg)) {
|
|
176
|
-
return deny('TAG_CONNECTION_CLOSED', 'TypedSocket tag mutation connection is not attached and live.');
|
|
177
|
-
}
|
|
178
|
-
if (this.rules.size === 0) {
|
|
179
|
-
return deny('CLIENT_TAGS_DISABLED', 'TypedSocket client tag mutation is disabled.');
|
|
180
|
-
}
|
|
181
|
-
if (typeof nameArg !== 'string') {
|
|
182
|
-
return deny('INVALID_TAG_NAME', 'TypedSocket tag names must be non-empty strings.');
|
|
183
|
-
}
|
|
184
|
-
const rule = this.rules.get(nameArg);
|
|
185
|
-
if (!rule) {
|
|
186
|
-
return deny('TAG_NOT_ALLOWED', 'TypedSocket client tag name is not allowed.');
|
|
187
|
-
}
|
|
188
|
-
return { peer: peerArg, name: nameArg, rule };
|
|
189
|
-
}
|
|
190
|
-
async enqueueMutation(peerArg, ruleArg, nameArg, operationArg) {
|
|
191
|
-
const state = this.getPeerState(peerArg);
|
|
192
|
-
const ruleState = this.ruleStates.get(ruleArg.name);
|
|
193
|
-
if (!ruleState) {
|
|
194
|
-
return deny('TAG_NOT_ALLOWED', 'TypedSocket client tag rule state is unavailable.');
|
|
195
|
-
}
|
|
196
|
-
if (state.activeMutationOperations + state.orphanedAuthorizations
|
|
197
|
-
>= TYPEDSOCKET_MAX_RETAINED_TAG_MUTATIONS_PER_PEER
|
|
198
|
-
|| ruleState.retainedWork >= TYPEDSOCKET_MAX_RETAINED_TAG_MUTATIONS_PER_RULE
|
|
199
|
-
|| this.retainedMutationWork >= TYPEDSOCKET_MAX_RETAINED_TAG_MUTATIONS_PER_SERVER) {
|
|
200
|
-
return deny('TAG_MUTATION_CAPACITY_EXCEEDED', 'TypedSocket client tag mutation capacity is exhausted.');
|
|
201
|
-
}
|
|
202
|
-
state.activeMutationOperations++;
|
|
203
|
-
ruleState.retainedWork++;
|
|
204
|
-
this.retainedMutationWork++;
|
|
205
|
-
const previousTail = state.mutationTails.get(nameArg) ?? Promise.resolve();
|
|
206
|
-
const execution = {};
|
|
207
|
-
const operation = previousTail.then(() => operationArg(state, execution));
|
|
208
|
-
const tail = operation.then(() => undefined, () => undefined);
|
|
209
|
-
state.mutationTails.set(nameArg, tail);
|
|
210
|
-
try {
|
|
211
|
-
return await operation;
|
|
212
|
-
}
|
|
213
|
-
finally {
|
|
214
|
-
state.activeMutationOperations--;
|
|
215
|
-
if (execution.orphanOutcome) {
|
|
216
|
-
state.orphanedAuthorizations++;
|
|
217
|
-
ruleState.orphanedAuthorizations++;
|
|
218
|
-
void execution.orphanOutcome.then(() => {
|
|
219
|
-
state.orphanedAuthorizations--;
|
|
220
|
-
ruleState.orphanedAuthorizations--;
|
|
221
|
-
ruleState.retainedWork--;
|
|
222
|
-
this.retainedMutationWork--;
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
else {
|
|
226
|
-
ruleState.retainedWork--;
|
|
227
|
-
this.retainedMutationWork--;
|
|
228
|
-
}
|
|
229
|
-
if (state.mutationTails.get(nameArg) === tail) {
|
|
230
|
-
state.mutationTails.delete(nameArg);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
async executeSet(peerArg, stateArg, ruleArg, nameArg, payloadSnapshotArg, executionArg) {
|
|
235
|
-
if (!this.isPeerLive(peerArg)) {
|
|
236
|
-
return deny('TAG_CONNECTION_CLOSED', 'TypedSocket tag connection is closed.');
|
|
237
|
-
}
|
|
238
|
-
const existing = this.reconcileName(peerArg, stateArg, nameArg);
|
|
239
|
-
if (stateArg.protectedServerNames.has(nameArg) || existing?.owner === 'server') {
|
|
240
|
-
return deny('TAG_SERVER_OWNED', 'TypedSocket server-owned tags cannot be overwritten.');
|
|
241
|
-
}
|
|
242
|
-
if (existing && ruleArg.owner !== 'client') {
|
|
243
|
-
return deny('TAG_SERVER_OWNED', 'TypedSocket server-owned tag proposals are one-time.');
|
|
244
|
-
}
|
|
245
|
-
const fingerprint = this.createFingerprint(existing);
|
|
246
|
-
const routingSurface = peerArg.routingSurface;
|
|
247
|
-
const authorization = await this.authorize(peerArg, stateArg, ruleArg, {
|
|
248
|
-
operation: 'set',
|
|
249
|
-
name: nameArg,
|
|
250
|
-
payload: payloadSnapshotArg.payload,
|
|
251
|
-
existing,
|
|
252
|
-
}, executionArg);
|
|
253
|
-
if (!authorization.allowed)
|
|
254
|
-
return authorization.denial;
|
|
255
|
-
if (!this.isPeerLive(peerArg)) {
|
|
256
|
-
return deny('TAG_CONNECTION_CLOSED', 'TypedSocket tag connection closed during authorization.');
|
|
257
|
-
}
|
|
258
|
-
const current = this.reconcileName(peerArg, stateArg, nameArg);
|
|
259
|
-
if (!routingSurface
|
|
260
|
-
|| peerArg.routingSurface !== routingSurface
|
|
261
|
-
|| stateArg.protectedServerNames.has(nameArg)
|
|
262
|
-
|| this.createFingerprint(current) !== fingerprint) {
|
|
263
|
-
return deny('TAG_SERVER_OWNED', 'TypedSocket tag ownership changed during authorization.');
|
|
264
|
-
}
|
|
265
|
-
const retainedBytes = textEncoder.encode(nameArg).byteLength + payloadSnapshotArg.byteLength;
|
|
266
|
-
const nextCount = stateArg.admissions.size + (current ? 0 : 1);
|
|
267
|
-
const nextBytes = stateArg.admittedBytes - (current?.retainedBytes ?? 0) + retainedBytes;
|
|
268
|
-
if (nextCount > TYPEDSOCKET_MAX_CLIENT_TAGS_PER_PEER) {
|
|
269
|
-
return deny('TAG_COUNT_LIMIT_EXCEEDED', `TypedSocket clients may retain at most ${TYPEDSOCKET_MAX_CLIENT_TAGS_PER_PEER} tags per connection.`);
|
|
270
|
-
}
|
|
271
|
-
if (nextBytes > TYPEDSOCKET_MAX_CLIENT_TAG_RETAINED_BYTES_PER_PEER) {
|
|
272
|
-
return deny('TAG_RETAINED_BYTES_EXCEEDED', `TypedSocket client tags may retain at most ${TYPEDSOCKET_MAX_CLIENT_TAG_RETAINED_BYTES_PER_PEER} bytes per connection.`);
|
|
273
|
-
}
|
|
274
|
-
if (current)
|
|
275
|
-
this.releaseAdmission(stateArg, nameArg);
|
|
276
|
-
const admission = {
|
|
277
|
-
...payloadSnapshotArg,
|
|
278
|
-
owner: ruleArg.owner,
|
|
279
|
-
retainedBytes,
|
|
280
|
-
};
|
|
281
|
-
stateArg.admissions.set(nameArg, admission);
|
|
282
|
-
stateArg.admittedBytes += retainedBytes;
|
|
283
|
-
if (ruleArg.owner === 'server')
|
|
284
|
-
stateArg.protectedServerNames.add(nameArg);
|
|
285
|
-
peerArg.tags.add(nameArg);
|
|
286
|
-
peerArg.data.set(`${TAG_PREFIX}${nameArg}`, payloadSnapshotArg.payload);
|
|
287
|
-
return this.createSuccess(peerArg, stateArg, nameArg);
|
|
288
|
-
}
|
|
289
|
-
async executeRemove(peerArg, stateArg, ruleArg, nameArg, executionArg) {
|
|
290
|
-
if (!this.isPeerLive(peerArg)) {
|
|
291
|
-
return deny('TAG_CONNECTION_CLOSED', 'TypedSocket tag connection is closed.');
|
|
292
|
-
}
|
|
293
|
-
const existing = this.reconcileName(peerArg, stateArg, nameArg);
|
|
294
|
-
if (stateArg.protectedServerNames.has(nameArg) || existing?.owner === 'server') {
|
|
295
|
-
return deny('TAG_SERVER_OWNED', 'TypedSocket server-owned tags cannot be removed.');
|
|
296
|
-
}
|
|
297
|
-
if (!existing)
|
|
298
|
-
return this.createSuccess(peerArg, stateArg, nameArg, false);
|
|
299
|
-
if (ruleArg.owner !== 'client') {
|
|
300
|
-
return deny('TAG_SERVER_OWNED', 'TypedSocket server-owned tags cannot be removed.');
|
|
301
|
-
}
|
|
302
|
-
const fingerprint = this.createFingerprint(existing);
|
|
303
|
-
const routingSurface = peerArg.routingSurface;
|
|
304
|
-
const authorization = await this.authorize(peerArg, stateArg, ruleArg, {
|
|
305
|
-
operation: 'remove',
|
|
306
|
-
name: nameArg,
|
|
307
|
-
payload: existing.payload,
|
|
308
|
-
existing,
|
|
309
|
-
}, executionArg);
|
|
310
|
-
if (!authorization.allowed)
|
|
311
|
-
return authorization.denial;
|
|
312
|
-
if (!this.isPeerLive(peerArg)) {
|
|
313
|
-
return deny('TAG_CONNECTION_CLOSED', 'TypedSocket tag connection closed during authorization.');
|
|
314
|
-
}
|
|
315
|
-
const current = this.reconcileName(peerArg, stateArg, nameArg);
|
|
316
|
-
if (!routingSurface
|
|
317
|
-
|| peerArg.routingSurface !== routingSurface
|
|
318
|
-
|| stateArg.protectedServerNames.has(nameArg)
|
|
319
|
-
|| this.createFingerprint(current) !== fingerprint) {
|
|
320
|
-
return deny('TAG_SERVER_OWNED', 'TypedSocket tag ownership changed during authorization.');
|
|
321
|
-
}
|
|
322
|
-
this.releaseAdmission(stateArg, nameArg);
|
|
323
|
-
peerArg.tags.delete(nameArg);
|
|
324
|
-
peerArg.data.delete(`${TAG_PREFIX}${nameArg}`);
|
|
325
|
-
return this.createSuccess(peerArg, stateArg, nameArg, false);
|
|
326
|
-
}
|
|
327
|
-
async authorize(peerArg, stateArg, ruleArg, contextArg, executionArg) {
|
|
328
|
-
const routingSurface = peerArg.routingSurface;
|
|
329
|
-
if (!routingSurface) {
|
|
330
|
-
return {
|
|
331
|
-
allowed: false,
|
|
332
|
-
denial: deny('TAG_CONNECTION_CLOSED', 'TypedSocket tag authorization requires an exact routing surface.'),
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
const controller = new AbortController();
|
|
336
|
-
stateArg.authorizationControllers.add(controller);
|
|
337
|
-
const authority = Object.freeze({
|
|
338
|
-
connectionId: this.getConnectionAuthorityId(peerArg),
|
|
339
|
-
routingSurfaceId: this.getRoutingSurfaceAuthorityId(routingSurface),
|
|
340
|
-
});
|
|
341
|
-
const existing = contextArg.existing
|
|
342
|
-
? Object.freeze({
|
|
343
|
-
owner: 'client',
|
|
344
|
-
payload: cloneTagPayload(contextArg.existing.payload),
|
|
345
|
-
})
|
|
346
|
-
: undefined;
|
|
347
|
-
const validationContext = Object.freeze({
|
|
348
|
-
operation: contextArg.operation,
|
|
349
|
-
name: contextArg.name,
|
|
350
|
-
payload: cloneTagPayload(contextArg.payload),
|
|
351
|
-
existing,
|
|
352
|
-
authority,
|
|
353
|
-
abortSignal: controller.signal,
|
|
354
|
-
});
|
|
355
|
-
let callbackSettled = false;
|
|
356
|
-
const outcome = Promise.resolve()
|
|
357
|
-
.then(() => ruleArg.validateAndAuthorize(validationContext))
|
|
358
|
-
.then((allowedArg) => {
|
|
359
|
-
callbackSettled = true;
|
|
360
|
-
return allowedArg === true;
|
|
361
|
-
}, () => {
|
|
362
|
-
callbackSettled = true;
|
|
363
|
-
return false;
|
|
364
|
-
});
|
|
365
|
-
let timeout;
|
|
366
|
-
let abortListener;
|
|
367
|
-
const timeoutResult = new Promise((resolveArg) => {
|
|
368
|
-
timeout = setTimeout(() => {
|
|
369
|
-
resolveArg('timeout');
|
|
370
|
-
controller.abort(new Error('TypedSocket tag authorization timed out.'));
|
|
371
|
-
}, this.authorizationTimeoutMs);
|
|
372
|
-
});
|
|
373
|
-
const abortResult = new Promise((resolveArg) => {
|
|
374
|
-
abortListener = () => resolveArg('aborted');
|
|
375
|
-
controller.signal.addEventListener('abort', abortListener, { once: true });
|
|
376
|
-
});
|
|
377
|
-
try {
|
|
378
|
-
const result = await Promise.race([
|
|
379
|
-
outcome.then((allowedArg) => allowedArg ? 'allowed' : 'denied'),
|
|
380
|
-
timeoutResult,
|
|
381
|
-
abortResult,
|
|
382
|
-
]);
|
|
383
|
-
if (result === 'allowed')
|
|
384
|
-
return { allowed: true };
|
|
385
|
-
if (!callbackSettled)
|
|
386
|
-
executionArg.orphanOutcome = outcome;
|
|
387
|
-
if (result === 'timeout') {
|
|
388
|
-
return {
|
|
389
|
-
allowed: false,
|
|
390
|
-
denial: deny('TAG_AUTHORIZATION_TIMEOUT', 'TypedSocket tag authorization timed out.'),
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
if (result === 'aborted') {
|
|
394
|
-
return {
|
|
395
|
-
allowed: false,
|
|
396
|
-
denial: deny('TAG_CONNECTION_CLOSED', 'TypedSocket tag authorization was cancelled.'),
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
return {
|
|
400
|
-
allowed: false,
|
|
401
|
-
denial: deny('TAG_AUTHORIZATION_DENIED', 'TypedSocket tag authorization was denied.'),
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
finally {
|
|
405
|
-
if (timeout)
|
|
406
|
-
clearTimeout(timeout);
|
|
407
|
-
if (abortListener)
|
|
408
|
-
controller.signal.removeEventListener('abort', abortListener);
|
|
409
|
-
stateArg.authorizationControllers.delete(controller);
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
reconcileName(peerArg, stateArg, nameArg) {
|
|
413
|
-
const tracked = stateArg.admissions.get(nameArg);
|
|
414
|
-
const dataKey = `${TAG_PREFIX}${nameArg}`;
|
|
415
|
-
const hasTag = peerArg.tags.has(nameArg);
|
|
416
|
-
const hasPayload = peerArg.data.has(dataKey);
|
|
417
|
-
if (tracked) {
|
|
418
|
-
let matches = hasTag && hasPayload;
|
|
419
|
-
if (matches) {
|
|
420
|
-
try {
|
|
421
|
-
matches = createTypedSocketTagPayloadSnapshot(peerArg.data.get(dataKey)).encoded
|
|
422
|
-
=== tracked.encoded;
|
|
423
|
-
}
|
|
424
|
-
catch {
|
|
425
|
-
matches = false;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
if (matches)
|
|
429
|
-
return tracked;
|
|
430
|
-
this.releaseAdmission(stateArg, nameArg);
|
|
431
|
-
stateArg.protectedServerNames.add(nameArg);
|
|
432
|
-
return undefined;
|
|
433
|
-
}
|
|
434
|
-
if (hasTag || hasPayload)
|
|
435
|
-
stateArg.protectedServerNames.add(nameArg);
|
|
436
|
-
return undefined;
|
|
437
|
-
}
|
|
438
|
-
releaseAdmission(stateArg, nameArg) {
|
|
439
|
-
const existing = stateArg.admissions.get(nameArg);
|
|
440
|
-
if (!existing)
|
|
441
|
-
return;
|
|
442
|
-
stateArg.admissions.delete(nameArg);
|
|
443
|
-
stateArg.admittedBytes -= existing.retainedBytes;
|
|
444
|
-
}
|
|
445
|
-
createFingerprint(admissionArg) {
|
|
446
|
-
return admissionArg ? `${admissionArg.owner}:${admissionArg.encoded}` : 'absent';
|
|
447
|
-
}
|
|
448
|
-
createSuccess(peerArg, stateArg, nameArg, retainRevisionArg = true) {
|
|
449
|
-
const tagRevision = (stateArg.successfulRevisionByName.get(nameArg) ?? 0) + 1;
|
|
450
|
-
if (retainRevisionArg)
|
|
451
|
-
stateArg.successfulRevisionByName.set(nameArg, tagRevision);
|
|
452
|
-
else
|
|
453
|
-
stateArg.successfulRevisionByName.delete(nameArg);
|
|
454
|
-
return {
|
|
455
|
-
success: true,
|
|
456
|
-
connectionId: this.getConnectionAuthorityId(peerArg),
|
|
457
|
-
tagRevision,
|
|
458
|
-
};
|
|
459
|
-
}
|
|
460
|
-
getPeerState(peerArg) {
|
|
461
|
-
let state = this.peerStates.get(peerArg);
|
|
462
|
-
if (state)
|
|
463
|
-
return state;
|
|
464
|
-
state = {
|
|
465
|
-
admissions: new Map(),
|
|
466
|
-
protectedServerNames: new Set(),
|
|
467
|
-
successfulRevisionByName: new Map(),
|
|
468
|
-
mutationTails: new Map(),
|
|
469
|
-
authorizationControllers: new Set(),
|
|
470
|
-
serverRetainedBytesByName: new Map(),
|
|
471
|
-
activeMutationOperations: 0,
|
|
472
|
-
orphanedAuthorizations: 0,
|
|
473
|
-
admittedBytes: 0,
|
|
474
|
-
serverRetainedBytes: 0,
|
|
475
|
-
};
|
|
476
|
-
this.peerStates.set(peerArg, state);
|
|
477
|
-
return state;
|
|
478
|
-
}
|
|
479
|
-
isPeerLive(peerArg) {
|
|
480
|
-
return !this.stopping
|
|
481
|
-
&& !this.closedPeers.has(peerArg)
|
|
482
|
-
&& peerArg.readyState === 1
|
|
483
|
-
&& (!this.attachedPeersProvider || this.attachedPeersProvider().includes(peerArg));
|
|
484
|
-
}
|
|
485
|
-
getConnectionAuthorityId(peerArg) {
|
|
486
|
-
let id = this.connectionAuthorityIds.get(peerArg);
|
|
487
|
-
if (!id) {
|
|
488
|
-
id = plugins.smartstring.create.createCryptoRandomString();
|
|
489
|
-
this.connectionAuthorityIds.set(peerArg, id);
|
|
490
|
-
}
|
|
491
|
-
return id;
|
|
492
|
-
}
|
|
493
|
-
getRoutingSurfaceAuthorityId(routingSurfaceArg) {
|
|
494
|
-
let id = this.routingSurfaceAuthorityIds.get(routingSurfaceArg);
|
|
495
|
-
if (!id) {
|
|
496
|
-
id = plugins.smartstring.create.createCryptoRandomString();
|
|
497
|
-
this.routingSurfaceAuthorityIds.set(routingSurfaceArg, id);
|
|
498
|
-
}
|
|
499
|
-
return id;
|
|
500
|
-
}
|
|
501
|
-
assertServerMutationName(nameArg) {
|
|
502
|
-
const denial = validateTagName(nameArg);
|
|
503
|
-
if (denial)
|
|
504
|
-
throw new Error(denial.message);
|
|
505
|
-
}
|
|
506
|
-
retainServerTag(stateArg, nameArg, payloadBytesArg) {
|
|
507
|
-
const previousBytes = stateArg.serverRetainedBytesByName.get(nameArg) ?? 0;
|
|
508
|
-
if (previousBytes === 0
|
|
509
|
-
&& stateArg.serverRetainedBytesByName.size >= TYPEDSOCKET_MAX_SERVER_TAG_NAMES_PER_PEER) {
|
|
510
|
-
throw new Error(`TypedSocket servers may protect at most ${TYPEDSOCKET_MAX_SERVER_TAG_NAMES_PER_PEER} tag names per connection.`);
|
|
511
|
-
}
|
|
512
|
-
const retainedBytes = textEncoder.encode(nameArg).byteLength + payloadBytesArg;
|
|
513
|
-
const nextTotal = stateArg.serverRetainedBytes - previousBytes + retainedBytes;
|
|
514
|
-
if (nextTotal > TYPEDSOCKET_MAX_SERVER_TAG_RETAINED_BYTES_PER_PEER) {
|
|
515
|
-
throw new Error(`TypedSocket server tags may retain at most ${TYPEDSOCKET_MAX_SERVER_TAG_RETAINED_BYTES_PER_PEER} bytes per connection.`);
|
|
516
|
-
}
|
|
517
|
-
stateArg.serverRetainedBytesByName.set(nameArg, retainedBytes);
|
|
518
|
-
stateArg.serverRetainedBytes = nextTotal;
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZWRzb2NrZXQudGFncy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3R5cGVkc29ja2V0LnRhZ3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSwwQkFBMEIsQ0FBQztBQUVwRCxNQUFNLENBQUMsTUFBTSw4QkFBOEIsR0FBRyxHQUFHLENBQUM7QUFDbEQsTUFBTSxDQUFDLE1BQU0saUNBQWlDLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQztBQUMxRCxNQUFNLENBQUMsTUFBTSxvQ0FBb0MsR0FBRyxFQUFFLENBQUM7QUFDdkQsTUFBTSxDQUFDLE1BQU0sa0RBQWtELEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQztBQUM1RSxNQUFNLENBQUMsTUFBTSwrQ0FBK0MsR0FBRyxDQUFDLENBQUM7QUFDakUsTUFBTSxDQUFDLE1BQU0sK0NBQStDLEdBQUcsRUFBRSxDQUFDO0FBQ2xFLE1BQU0sQ0FBQyxNQUFNLGlEQUFpRCxHQUFHLEdBQUcsQ0FBQztBQUNyRSxNQUFNLENBQUMsTUFBTSw0Q0FBNEMsR0FBRyxLQUFLLENBQUM7QUFDbEUsTUFBTSxDQUFDLE1BQU0seUNBQXlDLEdBQUcsRUFBRSxDQUFDO0FBQzVELE1BQU0sQ0FBQyxNQUFNLGtEQUFrRCxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUM7QUFFNUUsTUFBTSxVQUFVLEdBQUcscUJBQXFCLENBQUM7QUFDekMsTUFBTSxXQUFXLEdBQUcsSUFBSSxXQUFXLEVBQUUsQ0FBQztBQTRFdEMsTUFBTSxPQUFPLCtCQUFnQyxTQUFRLEtBQUs7SUFHeEQsWUFBWSxPQUFrQyxFQUFFLFVBQWtCO1FBQ2hFLEtBQUssQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUNsQixJQUFJLENBQUMsSUFBSSxHQUFHLGlDQUFpQyxDQUFDO1FBQzlDLElBQUksQ0FBQyxJQUFJLEdBQUcsT0FBTyxDQUFDO0lBQ3RCLENBQUM7Q0FDRjtBQXVDRCxTQUFTLElBQUksQ0FDWCxPQUFr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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|