@trevrpc/trevrpc-js 0.2.3 → 1.1.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.
Files changed (47) hide show
  1. package/README.md +127 -46
  2. package/package.json +12 -45
  3. package/src/bytes.js +10 -0
  4. package/src/call.js +520 -0
  5. package/src/channel.js +258 -288
  6. package/src/client.js +543 -1254
  7. package/src/datagram.js +336 -0
  8. package/src/framing.js +151 -548
  9. package/src/generator.js +66 -109
  10. package/src/index.d.ts +235 -463
  11. package/src/index.js +4 -3
  12. package/src/qmux/frames.js +262 -0
  13. package/src/qmux/params.js +104 -0
  14. package/src/qmux/session.js +739 -0
  15. package/src/race.js +90 -0
  16. package/src/status.js +24 -70
  17. package/src/varint.js +73 -0
  18. package/src/wire.js +21 -89
  19. package/examples/greeter/README.md +0 -35
  20. package/examples/greeter/client.js +0 -172
  21. package/examples/greeter/greeter.node.trevrpc.d.ts +0 -53
  22. package/examples/greeter/greeter.node.trevrpc.js +0 -9
  23. package/examples/greeter/greeter.proto +0 -18
  24. package/examples/greeter/greeter.trevrpc.d.ts +0 -55
  25. package/examples/greeter/greeter.trevrpc.js +0 -103
  26. package/examples/greeter/index.html +0 -140
  27. package/examples/greeter/static-server.js +0 -168
  28. package/src/advanced.d.ts +0 -29
  29. package/src/advanced.js +0 -1
  30. package/src/browser-channel.js +0 -49
  31. package/src/connect.js +0 -6
  32. package/src/connect.node.js +0 -6
  33. package/src/index.node.d.ts +0 -10
  34. package/src/index.node.js +0 -3
  35. package/src/native-loader.js +0 -118
  36. package/src/node-advanced.d.ts +0 -27
  37. package/src/node-advanced.js +0 -1
  38. package/src/node-generated.d.ts +0 -24
  39. package/src/node-generated.js +0 -126
  40. package/src/node-index.d.ts +0 -1
  41. package/src/node-index.js +0 -7
  42. package/src/node.d.ts +0 -298
  43. package/src/node.js +0 -1456
  44. package/src/runtime.js +0 -66
  45. package/src/server-response.js +0 -49
  46. package/src/timer.js +0 -29
  47. package/src/webtransport.js +0 -639
package/src/generator.js CHANGED
@@ -89,6 +89,18 @@ message MethodDescriptorProto {
89
89
  optional string output_type = 3;
90
90
  optional bool client_streaming = 5 [default = false];
91
91
  optional bool server_streaming = 6 [default = false];
92
+ optional MethodOptions options = 4;
93
+ }
94
+
95
+ // Only the trevrpc.method extension is modelled; protobuf.js keeps every other extension out of
96
+ // the decoded message. The numbers are those of proto/trevrpc/options.proto.
97
+ message MethodOptions {
98
+ optional TrevrpcMethodOptions trevrpc_method = 58247;
99
+ }
100
+
101
+ message TrevrpcMethodOptions {
102
+ optional bool unreliable_requests = 1;
103
+ optional bool unreliable_responses = 2;
92
104
  }
93
105
 
94
106
  message FileOptions {
@@ -166,6 +178,13 @@ const FieldLabel = Object.freeze({
166
178
  Repeated: 3,
167
179
  });
168
180
 
181
+ /** Generic runtime types that generated declarations import only when they use them. */
182
+ const GeneratedTypeImports = [
183
+ "BidirectionalStreamingCall",
184
+ "ClientStreamingCall",
185
+ "ResponseStream",
186
+ ];
187
+
169
188
  const RESERVED_WORDS = new Set([
170
189
  "break",
171
190
  "case",
@@ -225,7 +244,6 @@ export function generate(request) {
225
244
  }
226
245
 
227
246
  const fileName = outputFileName(file.name, options.fileSuffix);
228
- const nodeFileName = outputFileName(file.name, `.node${options.fileSuffix}`);
229
247
  files.push({
230
248
  name: fileName,
231
249
  content: generateFile(file, rootJson, options),
@@ -234,14 +252,6 @@ export function generate(request) {
234
252
  name: declarationFileName(fileName),
235
253
  content: generateDeclarationFile(protoFiles, file, typeNames, options),
236
254
  });
237
- files.push({
238
- name: nodeFileName,
239
- content: generateNodeFile(file, fileName, options),
240
- });
241
- files.push({
242
- name: declarationFileName(nodeFileName),
243
- content: generateNodeDeclarationFile(file, fileName, typeNames, options),
244
- });
245
255
  }
246
256
 
247
257
  return { file: files };
@@ -255,8 +265,6 @@ export function parseOptions(parameter) {
255
265
  const options = {
256
266
  runtimeImport: "@trevrpc/trevrpc-js",
257
267
  runtimeTypeImport: null,
258
- nodeRuntimeImport: "@trevrpc/trevrpc-js/node/generated",
259
- nodeRuntimeTypeImport: null,
260
268
  fileSuffix: ".trevrpc.js",
261
269
  };
262
270
 
@@ -279,12 +287,6 @@ export function parseOptions(parameter) {
279
287
  case "runtime_type_import":
280
288
  options.runtimeTypeImport = value;
281
289
  break;
282
- case "node_runtime_import":
283
- options.nodeRuntimeImport = value;
284
- break;
285
- case "node_runtime_type_import":
286
- options.nodeRuntimeTypeImport = value;
287
- break;
288
290
  case "file_suffix":
289
291
  options.fileSuffix = value;
290
292
  break;
@@ -293,29 +295,15 @@ export function parseOptions(parameter) {
293
295
  }
294
296
  }
295
297
 
296
- options.runtimeTypeImport = resolveTypeImport(
297
- "runtime_import",
298
- options.runtimeImport,
299
- options.runtimeTypeImport,
300
- );
301
- options.nodeRuntimeTypeImport = resolveTypeImport(
302
- "node_runtime_import",
303
- options.nodeRuntimeImport,
304
- options.nodeRuntimeTypeImport,
305
- );
306
- return options;
307
- }
308
-
309
- function resolveTypeImport(optionName, runtimeImport, typeImport) {
310
- if (typeImport != null) {
311
- return typeImport;
312
- }
313
- if (/^[A-Za-z][A-Za-z0-9+.-]*:/u.test(runtimeImport)) {
314
- throw new Error(
315
- `${optionName} ${JSON.stringify(runtimeImport)} requires an explicit ${optionName.replace("_import", "_type_import")}`,
316
- );
298
+ if (options.runtimeTypeImport == null) {
299
+ if (/^[A-Za-z][A-Za-z0-9+.-]*:/u.test(options.runtimeImport)) {
300
+ throw new Error(
301
+ `runtime_import ${JSON.stringify(options.runtimeImport)} requires an explicit runtime_type_import`,
302
+ );
303
+ }
304
+ options.runtimeTypeImport = options.runtimeImport;
317
305
  }
318
- return runtimeImport;
306
+ return options;
319
307
  }
320
308
 
321
309
  /** Builds a protobuf.js JSON root from protobuf file descriptors. */
@@ -354,36 +342,19 @@ function generateDeclarationFile(protoFiles, file, typeNames, options) {
354
342
  .map((service) => generateServiceDeclaration(service, typeNames))
355
343
  .join("\n\n");
356
344
  const sections = [messages, services].filter(Boolean).join("\n\n");
357
-
358
- return `// Code generated by protoc-gen-trevrpc-js. DO NOT EDIT.\n\nimport type { Long, Root } from "protobufjs";\n\nimport type { BidirectionalStreamingCall, CallOptions, ClientStreamingCall, ResponseAsyncIterable, RpcServiceDescriptor, Transport, UnaryResponse } from ${JSON.stringify(
345
+ const protobufImports = sections.includes("string | Long") ? "Long, Root" : "Root";
346
+ const runtimeImports = [
347
+ "CallOptions",
348
+ "RpcServiceDescriptor",
349
+ "Transport",
350
+ ...GeneratedTypeImports.filter((name) => sections.includes(`${name}<`)),
351
+ ].sort();
352
+
353
+ return `// Code generated by protoc-gen-trevrpc-js. DO NOT EDIT.\n\nimport type { ${protobufImports} } from "protobufjs";\n\nimport type { ${runtimeImports.join(", ")} } from ${JSON.stringify(
359
354
  options.runtimeTypeImport,
360
355
  )};\n\nexport declare const root: Root;\n\n${sections}\n`;
361
356
  }
362
357
 
363
- function generateNodeFile(file, ordinaryFileName, options) {
364
- const services = (file.service ?? []).map((service) => serviceDescriptor(file, service));
365
- const serviceNames = services.map((service) => service.exportName).join(", ");
366
- const registrations = services.map(generateNodeServerRegistration).join("\n");
367
- return `// Code generated by protoc-gen-trevrpc-js. DO NOT EDIT.\n\nimport { registerTypedService } from ${JSON.stringify(
368
- options.nodeRuntimeImport,
369
- )};\nimport { ${serviceNames}, root } from ${JSON.stringify(localModuleSpecifier(ordinaryFileName))};\n\n${registrations}`;
370
- }
371
-
372
- function generateNodeDeclarationFile(file, ordinaryFileName, typeNames, options) {
373
- const services = (file.service ?? []).map((service) => serviceDescriptor(file, service));
374
- const messageNames = [...new Set(typeNames.values())].join(", ");
375
- const messageImport =
376
- messageNames === ""
377
- ? ""
378
- : `import type { ${messageNames} } from ${JSON.stringify(localModuleSpecifier(ordinaryFileName))};\n`;
379
- const declarations = services
380
- .map((service) => generateNodeServiceDeclaration(service, typeNames))
381
- .join("\n\n");
382
- return `// Code generated by protoc-gen-trevrpc-js. DO NOT EDIT.\n\nimport type { NodeServer, NodeServerCall, StreamingServerResponse, UnaryServerResponse } from ${JSON.stringify(
383
- options.nodeRuntimeTypeImport,
384
- )};\n${messageImport}\n${declarations}\n`;
385
- }
386
-
387
358
  function generateService(service) {
388
359
  const className = `${pascalIdentifier(service.name)}Client`;
389
360
  const factoryName = `create${className}`;
@@ -398,18 +369,9 @@ function generateServiceMethod(jsName, method) {
398
369
  return ` /** Calls the ${method.name} RPC. */\n ${jsName}(options = {}) {\n return this._client.${jsName}(options);\n }`;
399
370
  }
400
371
 
401
- if (method.kind === "unary") {
402
- return ` /** Calls the ${method.name} RPC. */\n ${jsName}(request, options = {}) {\n return this._client.${jsName}(request, options);\n }\n\n /** Calls the ${method.name} RPC and returns response metadata. */\n ${jsName}WithResponse(request, options = {}) {\n return this._client.${jsName}WithResponse(request, options);\n }`;
403
- }
404
-
405
372
  return ` /** Calls the ${method.name} RPC. */\n ${jsName}(request, options = {}) {\n return this._client.${jsName}(request, options);\n }`;
406
373
  }
407
374
 
408
- function generateNodeServerRegistration(service) {
409
- const registerName = `register${pascalIdentifier(service.name)}Server`;
410
- return `/** Registers typed Node handlers for the ${service.name} service. */\nexport function ${registerName}(server, handlers) {\n return registerTypedService(server, root, ${service.exportName}, handlers);\n}\n`;
411
- }
412
-
413
375
  function generateServiceDeclaration(service, typeNames) {
414
376
  const className = `${pascalIdentifier(service.name)}Client`;
415
377
  const factoryName = `create${className}`;
@@ -420,24 +382,15 @@ function generateServiceDeclaration(service, typeNames) {
420
382
  return `/** Service descriptor for the ${service.name} service. */\nexport declare const ${service.exportName}: RpcServiceDescriptor;\n\n/** Client for the ${service.name} service. */\nexport declare class ${className} {\n /** Creates a client for the ${service.name} service. */\n constructor(transport: Transport, options?: CallOptions);\n\n${methods}\n}\n\n/** Creates a client for the ${service.name} service. */\nexport declare function ${factoryName}(transport: Transport, options?: CallOptions): ${className};`;
421
383
  }
422
384
 
423
- function generateNodeServiceDeclaration(service, typeNames) {
424
- const handlerName = `${pascalIdentifier(service.name)}Handlers`;
425
- const registerName = `register${pascalIdentifier(service.name)}Server`;
426
- const handlers = Object.entries(service.methods)
427
- .map(([jsName, method]) => generateHandlerDeclaration(jsName, method, typeNames))
428
- .join("\n");
429
- return `/** Typed Node handlers for the ${service.name} service. */\nexport interface ${handlerName} {\n${handlers}\n}\n\n/** Registers typed Node handlers for the ${service.name} service. */\nexport declare function ${registerName}(server: NodeServer, handlers: ${handlerName}): NodeServer;`;
430
- }
431
-
432
385
  function generateMethodDeclaration(jsName, method, typeNames) {
433
386
  const inputType = declarationTypeReference(method.inputType, typeNames);
434
387
  const outputType = declarationTypeReference(method.outputType, typeNames);
435
388
 
436
389
  switch (method.kind) {
437
390
  case "unary":
438
- return ` /** Calls the ${method.name} RPC. */\n ${jsName}(request: ${inputType}, options?: CallOptions): Promise<${outputType}>;\n /** Calls the ${method.name} RPC and returns response metadata. */\n ${jsName}WithResponse(request: ${inputType}, options?: CallOptions): Promise<UnaryResponse<${outputType}>>;`;
391
+ return ` /** Calls the ${method.name} RPC. */\n ${jsName}(request: ${inputType}, options?: CallOptions): Promise<${outputType}>;`;
439
392
  case "serverStreaming":
440
- return ` /** Calls the ${method.name} RPC. */\n ${jsName}(request: ${inputType}, options?: CallOptions): Promise<ResponseAsyncIterable<${outputType}>>;`;
393
+ return ` /** Calls the ${method.name} RPC. */\n ${jsName}(request: ${inputType}, options?: CallOptions): Promise<ResponseStream<${outputType}>>;`;
441
394
  case "clientStreaming":
442
395
  return ` /** Calls the ${method.name} RPC. */\n ${jsName}(options?: CallOptions): Promise<ClientStreamingCall<${inputType}, ${outputType}>>;`;
443
396
  case "bidirectionalStreaming":
@@ -447,25 +400,6 @@ function generateMethodDeclaration(jsName, method, typeNames) {
447
400
  }
448
401
  }
449
402
 
450
- function generateHandlerDeclaration(jsName, method, typeNames) {
451
- const inputType = declarationTypeReference(method.inputType, typeNames);
452
- const outputType = declarationTypeReference(method.outputType, typeNames);
453
- const unaryResult = `${outputType} | UnaryServerResponse<${outputType}>`;
454
- const streamingResult = `Iterable<${outputType}> | AsyncIterable<${outputType}> | StreamingServerResponse<${outputType}>`;
455
- switch (method.kind) {
456
- case "unary":
457
- return ` ${jsName}?(request: ${inputType}, call: NodeServerCall): ${unaryResult} | Promise<${unaryResult}>;`;
458
- case "serverStreaming":
459
- return ` ${jsName}?(request: ${inputType}, call: NodeServerCall): ${streamingResult} | Promise<${streamingResult}>;`;
460
- case "clientStreaming":
461
- return ` ${jsName}?(requests: AsyncIterable<${inputType}>, call: NodeServerCall): ${unaryResult} | Promise<${unaryResult}>;`;
462
- case "bidirectionalStreaming":
463
- return ` ${jsName}?(requests: AsyncIterable<${inputType}>, call: NodeServerCall): ${streamingResult} | Promise<${streamingResult}>;`;
464
- default:
465
- throw new Error(`unsupported generated RPC kind ${JSON.stringify(method.kind)}`);
466
- }
467
- }
468
-
469
403
  function serviceDescriptor(file, service) {
470
404
  const fullName =
471
405
  file.package === "" || file.package == null ? service.name : `${file.package}.${service.name}`;
@@ -474,11 +408,13 @@ function serviceDescriptor(file, service) {
474
408
 
475
409
  for (const method of service.method ?? []) {
476
410
  const jsName = uniqueName(lowerCamelIdentifier(method.name), usedNames);
411
+ const kind = methodKind(method);
477
412
  methods[jsName] = {
478
413
  name: method.name,
479
- kind: methodKind(method),
414
+ kind,
480
415
  inputType: stripLeadingDot(method.inputType),
481
416
  outputType: stripLeadingDot(method.outputType),
417
+ ...unreliableDirections(`${fullName}.${method.name}`, kind, method.options?.trevrpcMethod),
482
418
  };
483
419
  }
484
420
 
@@ -490,6 +426,31 @@ function serviceDescriptor(file, service) {
490
426
  };
491
427
  }
492
428
 
429
+ /**
430
+ * The `(trevrpc.method)` flags of a method as descriptor fields, only for the ones that are set.
431
+ * A flag on a direction that does not stream is an error.
432
+ */
433
+ function unreliableDirections(methodName, kind, options) {
434
+ const directions = {};
435
+ if (options?.unreliableRequests) {
436
+ if (kind !== "clientStreaming" && kind !== "bidirectionalStreaming") {
437
+ throw new Error(
438
+ `${methodName}: unreliable_requests requires a client-streaming or bidirectional method`,
439
+ );
440
+ }
441
+ directions.unreliableRequests = true;
442
+ }
443
+ if (options?.unreliableResponses) {
444
+ if (kind !== "serverStreaming" && kind !== "bidirectionalStreaming") {
445
+ throw new Error(
446
+ `${methodName}: unreliable_responses requires a server-streaming or bidirectional method`,
447
+ );
448
+ }
449
+ directions.unreliableResponses = true;
450
+ }
451
+ return directions;
452
+ }
453
+
493
454
  function declarationTypeNames(files) {
494
455
  const names = new Map();
495
456
  const usedNames = new Set();
@@ -808,10 +769,6 @@ function outputFileName(inputName, suffix) {
808
769
  return `${extensionStart === -1 ? inputName : inputName.slice(0, extensionStart)}${suffix}`;
809
770
  }
810
771
 
811
- function localModuleSpecifier(fileName) {
812
- return `./${fileName.slice(fileName.lastIndexOf("/") + 1)}`;
813
- }
814
-
815
772
  function declarationFileName(outputName) {
816
773
  if (outputName.endsWith(".js")) {
817
774
  return `${outputName.slice(0, -3)}.d.ts`;