@adhdev/mesh-shared 1.0.60-rc.1 → 1.0.60-rc.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +15 -0
- package/dist/index.mjs.map +1 -1
- package/dist/ws-protocol.d.ts +47 -0
- package/package.json +1 -1
- package/src/ws-protocol.ts +63 -0
package/dist/ws-protocol.d.ts
CHANGED
|
@@ -48,3 +48,50 @@ export declare const DAEMON_TO_SERVER_WS_MSGS: readonly DaemonToServerWsMsg[];
|
|
|
48
48
|
export declare const SERVER_TO_DAEMON_WS_MSGS: readonly ServerToDaemonWsMsg[];
|
|
49
49
|
export declare function isDaemonToServerWsMsg(value: unknown): value is DaemonToServerWsMsg;
|
|
50
50
|
export declare function isServerToDaemonWsMsg(value: unknown): value is ServerToDaemonWsMsg;
|
|
51
|
+
/**
|
|
52
|
+
* `auth_ok.payload.limits` — the plan-limit contract the server sends DOWN to
|
|
53
|
+
* the daemon for client-side enforcement.
|
|
54
|
+
*
|
|
55
|
+
* WHY THIS EXISTS: the wire spelling is `maxP2Pconnections` (lowercase `c`),
|
|
56
|
+
* inherited from `PlanLimits` in the proprietary server's plan-limits.ts. The
|
|
57
|
+
* daemon-cloud consumer independently declared the same field as
|
|
58
|
+
* `maxP2PConnections` (capital `C`) and assigned it from a `payload as any`
|
|
59
|
+
* cast — so the two spellings NEVER met under a type, TypeScript had nothing
|
|
60
|
+
* to compare, and the limit arrived as `undefined` at every comparison site.
|
|
61
|
+
* `undefined !== -1` passes the "is it unlimited?" guard, then every
|
|
62
|
+
* `count >= undefined` is false: enforcement silently degraded to unlimited on
|
|
63
|
+
* all plans, in production, for months.
|
|
64
|
+
*
|
|
65
|
+
* The lesson is NOT "pick a spelling" — it is that a cross-package wire
|
|
66
|
+
* contract asserted by hand on both ends with an `any` in between cannot fail
|
|
67
|
+
* loudly. Both producer (server) and consumer (daemon-cloud) must now import
|
|
68
|
+
* THIS symbol, so a rename breaks the build on both sides instead of silently
|
|
69
|
+
* disabling a paid-plan limit. Use `normalizeAuthOkLimits()` at the parse
|
|
70
|
+
* boundary; never re-spell these fields locally.
|
|
71
|
+
*/
|
|
72
|
+
export interface AuthOkPlanLimits {
|
|
73
|
+
/** P2P concurrent connection count (-1 = unlimited). WIRE SPELLING — lowercase `c`. */
|
|
74
|
+
maxP2Pconnections: number;
|
|
75
|
+
/** Screenshot send interval floor (seconds). 0 = real-time. */
|
|
76
|
+
screenshotIntervalSeconds: number;
|
|
77
|
+
/** Daily screenshot usage budget (minutes). -1 = unlimited. */
|
|
78
|
+
dailyScreenshotMinutes: number;
|
|
79
|
+
/** Max connectable machines/daemons (-1 = unlimited). */
|
|
80
|
+
maxMachines: number;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Parse-boundary normalizer for `auth_ok.payload.limits`.
|
|
84
|
+
*
|
|
85
|
+
* Returns `null` when the payload carries no usable limits object, so callers
|
|
86
|
+
* keep their existing "no limits known yet" branch. Individual non-numeric or
|
|
87
|
+
* missing fields fall back to the UNLIMITED sentinel (-1) for count-style
|
|
88
|
+
* limits and 0 for the interval floor, matching how each consumer already
|
|
89
|
+
* treats "no constraint" — a malformed field must never be coerced to a
|
|
90
|
+
* MORE restrictive value than the server intended, or a server-side schema
|
|
91
|
+
* change would start throttling paying users.
|
|
92
|
+
*
|
|
93
|
+
* Accepts the capital-`C` misspelling as an input alias ONLY so a daemon
|
|
94
|
+
* talking to a mixed-version fleet cannot regress; output is always the
|
|
95
|
+
* canonical wire spelling.
|
|
96
|
+
*/
|
|
97
|
+
export declare function normalizeAuthOkLimits(raw: unknown): AuthOkPlanLimits | null;
|
package/package.json
CHANGED
package/src/ws-protocol.ts
CHANGED
|
@@ -105,3 +105,66 @@ export function isDaemonToServerWsMsg(value: unknown): value is DaemonToServerWs
|
|
|
105
105
|
export function isServerToDaemonWsMsg(value: unknown): value is ServerToDaemonWsMsg {
|
|
106
106
|
return typeof value === 'string' && (SERVER_TO_DAEMON_WS_MSGS as readonly string[]).includes(value);
|
|
107
107
|
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* `auth_ok.payload.limits` — the plan-limit contract the server sends DOWN to
|
|
111
|
+
* the daemon for client-side enforcement.
|
|
112
|
+
*
|
|
113
|
+
* WHY THIS EXISTS: the wire spelling is `maxP2Pconnections` (lowercase `c`),
|
|
114
|
+
* inherited from `PlanLimits` in the proprietary server's plan-limits.ts. The
|
|
115
|
+
* daemon-cloud consumer independently declared the same field as
|
|
116
|
+
* `maxP2PConnections` (capital `C`) and assigned it from a `payload as any`
|
|
117
|
+
* cast — so the two spellings NEVER met under a type, TypeScript had nothing
|
|
118
|
+
* to compare, and the limit arrived as `undefined` at every comparison site.
|
|
119
|
+
* `undefined !== -1` passes the "is it unlimited?" guard, then every
|
|
120
|
+
* `count >= undefined` is false: enforcement silently degraded to unlimited on
|
|
121
|
+
* all plans, in production, for months.
|
|
122
|
+
*
|
|
123
|
+
* The lesson is NOT "pick a spelling" — it is that a cross-package wire
|
|
124
|
+
* contract asserted by hand on both ends with an `any` in between cannot fail
|
|
125
|
+
* loudly. Both producer (server) and consumer (daemon-cloud) must now import
|
|
126
|
+
* THIS symbol, so a rename breaks the build on both sides instead of silently
|
|
127
|
+
* disabling a paid-plan limit. Use `normalizeAuthOkLimits()` at the parse
|
|
128
|
+
* boundary; never re-spell these fields locally.
|
|
129
|
+
*/
|
|
130
|
+
export interface AuthOkPlanLimits {
|
|
131
|
+
/** P2P concurrent connection count (-1 = unlimited). WIRE SPELLING — lowercase `c`. */
|
|
132
|
+
maxP2Pconnections: number;
|
|
133
|
+
/** Screenshot send interval floor (seconds). 0 = real-time. */
|
|
134
|
+
screenshotIntervalSeconds: number;
|
|
135
|
+
/** Daily screenshot usage budget (minutes). -1 = unlimited. */
|
|
136
|
+
dailyScreenshotMinutes: number;
|
|
137
|
+
/** Max connectable machines/daemons (-1 = unlimited). */
|
|
138
|
+
maxMachines: number;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Parse-boundary normalizer for `auth_ok.payload.limits`.
|
|
143
|
+
*
|
|
144
|
+
* Returns `null` when the payload carries no usable limits object, so callers
|
|
145
|
+
* keep their existing "no limits known yet" branch. Individual non-numeric or
|
|
146
|
+
* missing fields fall back to the UNLIMITED sentinel (-1) for count-style
|
|
147
|
+
* limits and 0 for the interval floor, matching how each consumer already
|
|
148
|
+
* treats "no constraint" — a malformed field must never be coerced to a
|
|
149
|
+
* MORE restrictive value than the server intended, or a server-side schema
|
|
150
|
+
* change would start throttling paying users.
|
|
151
|
+
*
|
|
152
|
+
* Accepts the capital-`C` misspelling as an input alias ONLY so a daemon
|
|
153
|
+
* talking to a mixed-version fleet cannot regress; output is always the
|
|
154
|
+
* canonical wire spelling.
|
|
155
|
+
*/
|
|
156
|
+
export function normalizeAuthOkLimits(raw: unknown): AuthOkPlanLimits | null {
|
|
157
|
+
if (!raw || typeof raw !== 'object') return null;
|
|
158
|
+
const src = raw as Record<string, unknown>;
|
|
159
|
+
const num = (value: unknown, fallback: number): number =>
|
|
160
|
+
typeof value === 'number' && Number.isFinite(value) ? value : fallback;
|
|
161
|
+
return {
|
|
162
|
+
maxP2Pconnections: num(
|
|
163
|
+
src.maxP2Pconnections !== undefined ? src.maxP2Pconnections : src.maxP2PConnections,
|
|
164
|
+
-1,
|
|
165
|
+
),
|
|
166
|
+
screenshotIntervalSeconds: num(src.screenshotIntervalSeconds, 0),
|
|
167
|
+
dailyScreenshotMinutes: num(src.dailyScreenshotMinutes, -1),
|
|
168
|
+
maxMachines: num(src.maxMachines, -1),
|
|
169
|
+
};
|
|
170
|
+
}
|