@anycon/protocol 0.1.1 → 0.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/src/crypto.ts CHANGED
@@ -124,6 +124,15 @@ export interface EnrollCrypto {
124
124
  /** `loadCrypto()` 的结果:配对 / 重连 / 传输 + 账号内接入。 */
125
125
  export type FullCrypto = Crypto & EnrollCrypto
126
126
 
127
+ /** dev server 预览隧道(ADR-39)用到的部分。只有预览页的 service worker 用;`loadCrypto()` 的结果同时满足它。 */
128
+ export interface PreviewCrypto {
129
+ /**
130
+ * 预览隧道握手 `Noise_NKpsk0` 的 initiator:没有静态密钥,钉住 daemon 静态公钥 `daemonPublic`(32 字节)。
131
+ * `psk` 是预览票据密钥(32 字节),`prologue` 用 `previewPrologue(label, ticketId)` 算。
132
+ */
133
+ previewInitiator(daemonPublic: Uint8Array, psk: Uint8Array, prologue: Uint8Array): Handshake
134
+ }
135
+
127
136
  /** wasm 产物不存在或加载失败。 */
128
137
  export class CryptoUnavailableError extends Error {
129
138
  override readonly name = 'CryptoUnavailableError'
@@ -171,6 +180,7 @@ export interface WasmModule {
171
180
  WasmHandshake: {
172
181
  pairing(role: Role, local: RawKeypair, psk: Uint8Array): RawHandshake
173
182
  reconnect(role: Role, local: RawKeypair, remotePublic?: Uint8Array): RawHandshake
183
+ previewInitiator(daemonPublic: Uint8Array, psk: Uint8Array, prologue: Uint8Array): RawHandshake
174
184
  }
175
185
  derivePairSecrets(code: string): RawPairSecrets
176
186
  derivePsk(code: string): Uint8Array
@@ -262,7 +272,7 @@ function wrapCpace(raw: RawCpace): Cpace {
262
272
  }
263
273
  }
264
274
 
265
- function wrapModule(mod: WasmModule): FullCrypto {
275
+ function wrapModule(mod: WasmModule): FullCrypto & PreviewCrypto {
266
276
  // 传给 wasm 的 Keypair 每次临时重建。握手对象建好后释放它:wasm 若按值消费了它,free 是空操作。
267
277
  const withKeypair = (kp: Keypair, build: (raw: RawKeypair) => RawHandshake): Handshake => {
268
278
  const raw = mod.WasmKeypair.fromSecret(kp.secret)
@@ -283,6 +293,8 @@ function wrapModule(mod: WasmModule): FullCrypto {
283
293
  withKeypair(local, (raw) => mod.WasmHandshake.pairing(role, raw, psk)),
284
294
  reconnect: (role, local, remotePublic) =>
285
295
  withKeypair(local, (raw) => mod.WasmHandshake.reconnect(role, raw, remotePublic)),
296
+ previewInitiator: (daemonPublic, psk, prologue) =>
297
+ wrapHandshake(mod.WasmHandshake.previewInitiator(daemonPublic, psk, prologue)),
286
298
  derivePairSecrets: (code) => {
287
299
  const raw = mod.derivePairSecrets(code)
288
300
  try {
@@ -346,7 +358,7 @@ async function nodeWasmBytes(): Promise<Uint8Array> {
346
358
  }
347
359
  }
348
360
 
349
- async function initialize(options: LoadCryptoOptions): Promise<FullCrypto> {
361
+ async function initialize(options: LoadCryptoOptions): Promise<FullCrypto & PreviewCrypto> {
350
362
  const mod = await (options.module ?? importWasmModule())
351
363
  const input: unknown = options.wasm ?? (isNode() ? await nodeWasmBytes() : undefined)
352
364
  // wasm-bindgen ≥ 0.2.93 的对象形参;不给 input 时浏览器走胶水 js 里的 `new URL(…, import.meta.url)`。
@@ -354,13 +366,13 @@ async function initialize(options: LoadCryptoOptions): Promise<FullCrypto> {
354
366
  return wrapModule(mod)
355
367
  }
356
368
 
357
- let cached: Promise<FullCrypto> | undefined
369
+ let cached: Promise<FullCrypto & PreviewCrypto> | undefined
358
370
 
359
371
  /**
360
372
  * 加载并初始化 wasm。进程内只初始化一次(第二次调用返回同一个 Promise;失败不缓存,可重试)。
361
373
  * wasm 产物不存在时抛 `CryptoUnavailableError`,消息里写明要跑 `just gen-wasm`。
362
374
  */
363
- export function loadCrypto(options: LoadCryptoOptions = {}): Promise<FullCrypto> {
375
+ export function loadCrypto(options: LoadCryptoOptions = {}): Promise<FullCrypto & PreviewCrypto> {
364
376
  if (!cached) {
365
377
  cached = initialize(options).catch((err: unknown) => {
366
378
  cached = undefined
package/src/envelope.ts CHANGED
@@ -99,7 +99,7 @@ type ControlType = Control['type']
99
99
 
100
100
  /** 每个变体的字段声明顺序(不含 `type`),对应 control.rs。 */
101
101
  const CONTROL_FIELD_ORDER: Readonly<Record<ControlType, readonly string[]>> = {
102
- auth: ['device_token', 'protocol_version', 'client'],
102
+ auth: ['device_token', 'protocol_version', 'client', 'preview_token'],
103
103
  auth_ok: ['device_id', 'server_time_ms', 'peers_online', 'quota'],
104
104
  auth_fail: ['reason'],
105
105
  pair_begin: ['match_id'],
@@ -117,12 +117,13 @@ const CONTROL_FIELD_ORDER: Readonly<Record<ControlType, readonly string[]>> = {
117
117
  revoke: ['device'],
118
118
  peer_online: ['device'],
119
119
  peer_offline: ['device'],
120
+ preview_peer: ['peer', 'label'],
120
121
  undeliverable: ['to', 'seq'],
121
122
  queue_overflow: ['to', 'dropped_seqs'],
122
123
  queue_drop: ['seq'],
123
124
  backpressure: ['to', 'seq'],
124
125
  dropped: ['seq', 'reason'],
125
- rate_limited: ['retry_after_ms'],
126
+ rate_limited: ['retry_after_ms', 'to'],
126
127
  quota_update: ['quota'],
127
128
  rotate_token: [],
128
129
  token_rotated: ['device_token'],
@@ -130,10 +131,11 @@ const CONTROL_FIELD_ORDER: Readonly<Record<ControlType, readonly string[]>> = {
130
131
 
131
132
  /** Rust 侧 `#[serde(skip_serializing_if = "Option::is_none")]` 的字段:None 时整键省略,而不是编码为 nil。 */
132
133
  const CONTROL_SKIP_NONE: Readonly<Record<string, ReadonlySet<string>>> = {
133
- auth: new Set(['device_token']),
134
+ auth: new Set(['device_token', 'preview_token']),
134
135
  auth_ok: new Set(['quota']),
135
136
  pair_done: new Set(['device_id', 'device_token']),
136
137
  enroll_failed: new Set(['enroll_id']),
138
+ rate_limited: new Set(['to']),
137
139
  }
138
140
 
139
141
  /** 全部控制消息的 `type`,对应 control.rs 的 `Control` 变体(snake_case)。 */