mqtt-plus 1.4.13 → 1.4.15

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 (92) hide show
  1. package/AGENTS.md +0 -1
  2. package/CHANGELOG.md +18 -0
  3. package/dst-stage1/mqtt-plus-api.js +1 -0
  4. package/dst-stage1/mqtt-plus-api.js.map +1 -0
  5. package/dst-stage1/mqtt-plus-auth.js +1 -0
  6. package/dst-stage1/mqtt-plus-auth.js.map +1 -0
  7. package/dst-stage1/mqtt-plus-base.js +1 -0
  8. package/dst-stage1/mqtt-plus-base.js.map +1 -0
  9. package/dst-stage1/mqtt-plus-codec.js +1 -0
  10. package/dst-stage1/mqtt-plus-codec.js.map +1 -0
  11. package/dst-stage1/mqtt-plus-encode.js +1 -0
  12. package/dst-stage1/mqtt-plus-encode.js.map +1 -0
  13. package/dst-stage1/mqtt-plus-error.js +1 -0
  14. package/dst-stage1/mqtt-plus-error.js.map +1 -0
  15. package/dst-stage1/mqtt-plus-event.js +1 -0
  16. package/dst-stage1/mqtt-plus-event.js.map +1 -0
  17. package/dst-stage1/mqtt-plus-info.js +1 -0
  18. package/dst-stage1/mqtt-plus-info.js.map +1 -0
  19. package/dst-stage1/mqtt-plus-meta.js +1 -0
  20. package/dst-stage1/mqtt-plus-meta.js.map +1 -0
  21. package/dst-stage1/mqtt-plus-msg.js +1 -0
  22. package/dst-stage1/mqtt-plus-msg.js.map +1 -0
  23. package/dst-stage1/mqtt-plus-options.js +1 -0
  24. package/dst-stage1/mqtt-plus-options.js.map +1 -0
  25. package/dst-stage1/mqtt-plus-service.js +1 -0
  26. package/dst-stage1/mqtt-plus-service.js.map +1 -0
  27. package/dst-stage1/mqtt-plus-sink.js +14 -2
  28. package/dst-stage1/mqtt-plus-sink.js.map +1 -0
  29. package/dst-stage1/mqtt-plus-source.js +27 -16
  30. package/dst-stage1/mqtt-plus-source.js.map +1 -0
  31. package/dst-stage1/mqtt-plus-subscription.js +1 -0
  32. package/dst-stage1/mqtt-plus-subscription.js.map +1 -0
  33. package/dst-stage1/mqtt-plus-timer.js +1 -0
  34. package/dst-stage1/mqtt-plus-timer.js.map +1 -0
  35. package/dst-stage1/mqtt-plus-trace.js +1 -0
  36. package/dst-stage1/mqtt-plus-trace.js.map +1 -0
  37. package/dst-stage1/mqtt-plus-util.js +1 -0
  38. package/dst-stage1/mqtt-plus-util.js.map +1 -0
  39. package/dst-stage1/mqtt-plus-version.d.ts +1 -1
  40. package/dst-stage1/mqtt-plus-version.js +3 -1
  41. package/dst-stage1/mqtt-plus-version.js.map +1 -0
  42. package/dst-stage1/mqtt-plus.js +1 -0
  43. package/dst-stage1/mqtt-plus.js.map +1 -0
  44. package/dst-stage2/mqtt-plus.cjs.js +1972 -2161
  45. package/dst-stage2/mqtt-plus.esm.js +1934 -2131
  46. package/dst-stage2/mqtt-plus.umd.js +13 -14
  47. package/etc/c8.json +16 -0
  48. package/etc/knip.jsonc +7 -1
  49. package/etc/stx.conf +36 -4
  50. package/etc/tsc.json +1 -1
  51. package/etc/vite.mts +11 -5
  52. package/package.d/vite+8.0.0.patch +12 -0
  53. package/package.json +12 -3
  54. package/src/mqtt-plus-sink.ts +14 -2
  55. package/src/mqtt-plus-source.ts +27 -16
  56. package/src/mqtt-plus-version.ts +2 -3
  57. package/tst/.c8/base.css +224 -0
  58. package/tst/.c8/block-navigation.js +87 -0
  59. package/tst/.c8/favicon.png +0 -0
  60. package/tst/.c8/index.html +371 -0
  61. package/tst/.c8/mqtt-plus-auth.ts.html +538 -0
  62. package/tst/.c8/mqtt-plus-base.ts.html +826 -0
  63. package/tst/.c8/mqtt-plus-codec.ts.html +457 -0
  64. package/tst/.c8/mqtt-plus-encode.ts.html +310 -0
  65. package/tst/.c8/mqtt-plus-error.ts.html +1186 -0
  66. package/tst/.c8/mqtt-plus-event.ts.html +733 -0
  67. package/tst/.c8/mqtt-plus-meta.ts.html +271 -0
  68. package/tst/.c8/mqtt-plus-msg.ts.html +1693 -0
  69. package/tst/.c8/mqtt-plus-options.ts.html +319 -0
  70. package/tst/.c8/mqtt-plus-service.ts.html +865 -0
  71. package/tst/.c8/mqtt-plus-sink.ts.html +1645 -0
  72. package/tst/.c8/mqtt-plus-source.ts.html +1585 -0
  73. package/tst/.c8/mqtt-plus-subscription.ts.html +706 -0
  74. package/tst/.c8/mqtt-plus-timer.ts.html +286 -0
  75. package/tst/.c8/mqtt-plus-trace.ts.html +463 -0
  76. package/tst/.c8/mqtt-plus-util.ts.html +823 -0
  77. package/tst/.c8/mqtt-plus-version.ts.html +205 -0
  78. package/tst/.c8/mqtt-plus.ts.html +193 -0
  79. package/tst/.c8/prettify.css +1 -0
  80. package/tst/.c8/prettify.js +2 -0
  81. package/tst/.c8/sort-arrow-sprite.png +0 -0
  82. package/tst/.c8/sorter.js +210 -0
  83. package/tst/.c8/tmp/coverage-6577-1773528098323-2.json +1 -0
  84. package/tst/.c8/tmp/coverage-6577-1773528098331-1.json +1 -0
  85. package/tst/.c8/tmp/coverage-6577-1773528098353-0.json +1 -0
  86. package/tst/.c8/tmp/coverage-6578-1773528089194-0.json +1 -0
  87. package/tst/mqtt-plus-2-event.spec.ts +29 -0
  88. package/tst/mqtt-plus-6-misc.spec.ts +79 -2
  89. package/tst/mqtt-plus-7-spool.spec.ts +101 -0
  90. package/tst/mqtt-plus-8-run.spec.ts +115 -0
  91. package/tst/{tsc.json → tsc.cov.json} +4 -3
  92. package/tst/tsc.std.json +31 -0
package/etc/c8.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "all": false,
3
+ "reports-dir": "tst/.c8",
4
+ "extension": [ ".ts", ".js" ],
5
+ "src": [ "." ],
6
+ "include": [ "src/**/*.ts" ],
7
+ "exclude": [ "node_modules/**/*" ],
8
+ "exclude-after-remap": true,
9
+ "check-coverage": true,
10
+ "statements": 95,
11
+ "branches": 75,
12
+ "functions": 95,
13
+ "lines": 95,
14
+ "reporter": [ "text", "html" ],
15
+ "clean": true
16
+ }
package/etc/knip.jsonc CHANGED
@@ -37,6 +37,8 @@
37
37
  "tst/mqtt-plus-4-sink.spec.ts",
38
38
  "tst/mqtt-plus-5-source.spec.ts",
39
39
  "tst/mqtt-plus-6-misc.spec.ts",
40
+ "tst/mqtt-plus-7-spool.spec.ts",
41
+ "tst/mqtt-plus-8-run.spec.ts",
40
42
  "etc/vite.mts",
41
43
  "etc/d2.mts"
42
44
  ],
@@ -46,7 +48,11 @@
46
48
  ],
47
49
  "ignoreDependencies": [
48
50
  "shx",
49
- "chokidar-cli"
51
+ "chokidar-cli",
52
+ "patch-package",
53
+ "c8",
54
+ "source-map-support",
55
+ "open-cli"
50
56
  ]
51
57
  }
52
58
 
package/etc/stx.conf CHANGED
@@ -22,6 +22,16 @@
22
22
  ## SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
  ##
24
24
 
25
+ # make patches
26
+ patch-make
27
+ npm shrinkwrap && \
28
+ patch-package --patch-dir package.d && \
29
+ shx rm -f npm-shrinkwrap.json
30
+
31
+ # (internal) apply patches
32
+ patch-apply
33
+ patch-package --patch-dir package.d
34
+
25
35
  # static code analysis (linting)
26
36
  lint
27
37
  tsc --project etc/tsc.json --noEmit && \
@@ -33,6 +43,11 @@ build : lint
33
43
  VITE_BUILD_FORMATS=es,cjs vite --config etc/vite.mts build --mode production
34
44
  VITE_BUILD_FORMATS=umd vite --config etc/vite.mts build --mode production
35
45
 
46
+ # code compilation/transpiling (building, development)
47
+ build-dev : lint
48
+ VITE_BUILD_FORMATS=es,cjs vite --config etc/vite.mts build --mode development
49
+ VITE_BUILD_FORMATS=umd vite --config etc/vite.mts build --mode development
50
+
36
51
  # documentation compilation
37
52
  build-doc
38
53
  node etc/d2.mts etc/d2.theme.d2 doc/mqtt-plus-comm-event-emission.d2 doc/mqtt-plus-comm-event-emission.svg
@@ -44,19 +59,36 @@ build-doc
44
59
  # development loop
45
60
  dev
46
61
  chokidar \
47
- "etc/*" "src/*" "tst/*" \
48
- -c "npm start build" \
62
+ "etc/*" "src/*.ts" "tst/*.ts" \
63
+ -c "npm start build-dev" \
49
64
  --debounce 1000 --throttle 1000 \
50
65
  --initial --verbose
51
66
 
52
67
  # run test suite
53
68
  test
54
- tsc --project tst/tsc.json --noEmit && \
69
+ tsc --project tst/tsc.std.json --noEmit && \
70
+ eslint --config etc/eslint.mts tst/mqtt-plus-0-*.ts tst/*.spec.ts && \
71
+ if [ ".$MQTT_BROKER" = . ]; then if [ ".`which docker 2>&1`" != . ]; then MQTT_BROKER=mosquitto; fi; else MQTT_BROKER=aedes; fi && \
72
+ MQTT_BROKER="$MQTT_BROKER" \
73
+ TSX_TSCONFIG_PATH="tst/tsc.std.json" \
74
+ NODE_OPTIONS="--import=tsx --trace-warnings -r source-map-support/register" \
75
+ mocha --require tst/mqtt-plus-0-fixture.ts tst/*.spec.ts
76
+
77
+ # run test suite (with code coverage)
78
+ test-cov
79
+ tsc --project tst/tsc.cov.json --noEmit && \
55
80
  eslint --config etc/eslint.mts tst/mqtt-plus-0-*.ts tst/*.spec.ts && \
56
81
  if [ ".$MQTT_BROKER" = . ]; then if [ ".`which docker 2>&1`" != . ]; then MQTT_BROKER=mosquitto; fi; else MQTT_BROKER=aedes; fi && \
57
- MQTT_BROKER="$MQTT_BROKER" NODE_OPTIONS="--import=tsx --trace-warnings" \
82
+ MQTT_BROKER="$MQTT_BROKER" \
83
+ TSX_TSCONFIG_PATH="tst/tsc.cov.json" \
84
+ NODE_OPTIONS="--import=tsx --trace-warnings -r source-map-support/register" \
85
+ c8 --config etc/c8.json \
58
86
  mocha --require tst/mqtt-plus-0-fixture.ts tst/*.spec.ts
59
87
 
88
+ # open code coverage report
89
+ open-cov
90
+ open-cli tst/.c8/index.html
91
+
60
92
  # execute simple sample for testing
61
93
  sample
62
94
  npx tsx sample/sample.ts
package/etc/tsc.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "resolveJsonModule": false,
13
13
  "isolatedModules": false,
14
14
  "esModuleInterop": false,
15
- "lib": [ "es2022", "dom" ],
15
+ "lib": [ "es2022" ],
16
16
  "skipLibCheck": true,
17
17
  "declaration": true,
18
18
  "noEmit": false,
package/etc/vite.mts CHANGED
@@ -25,6 +25,7 @@
25
25
  import * as Vite from "vite"
26
26
  import { tscPlugin } from "@wroud/vite-plugin-tsc"
27
27
  import { nodePolyfills } from "vite-plugin-node-polyfills"
28
+ import replace from "@rollup/plugin-replace"
28
29
  import pkg from "../package.json"
29
30
 
30
31
  const formats = process.env.VITE_BUILD_FORMATS ?? "es"
@@ -34,12 +35,12 @@ export default Vite.defineConfig(({ command, mode }) => ({
34
35
  appType: "custom",
35
36
  base: "",
36
37
  root: "",
37
- define: {
38
- "__VERSION__": `"${pkg.version.replace(/\.\d+$/, "")}"`
39
- },
40
38
  plugins: [
41
39
  tscPlugin({
42
- tscArgs: [ "--build", "etc/tsc.json" ],
40
+ tscArgs: [
41
+ "--build", "etc/tsc.json",
42
+ ...(mode === "development" ? [ "--sourceMap" ] : [])
43
+ ],
43
44
  packageManager: "npx",
44
45
  prebuild: true
45
46
  }),
@@ -47,7 +48,12 @@ export default Vite.defineConfig(({ command, mode }) => ({
47
48
  include: [ "events", "stream", "buffer" ],
48
49
  globals: {},
49
50
  protocolImports: true
50
- }) ] : [])
51
+ }) ] : []),
52
+ replace({
53
+ "\"0.0\"": `"${pkg.version.replace(/\.\d+$/, "")}"`,
54
+ delimiters: [ "", "" ],
55
+ preventAssignment: true
56
+ })
51
57
  ],
52
58
  build: {
53
59
  rollupOptions: {
@@ -0,0 +1,12 @@
1
+ diff --git a/node_modules/vite/dist/node/chunks/node.js b/node_modules/vite/dist/node/chunks/node.js
2
+ index 8901df3..1f0f344 100644
3
+ --- a/node_modules/vite/dist/node/chunks/node.js
4
+ +++ b/node_modules/vite/dist/node/chunks/node.js
5
+ @@ -34548,7 +34548,6 @@ async function runConfigHook(config, plugins, configEnv) {
6
+ const res = await getHookHandler(hook).call(context, conf, configEnv);
7
+ if (res && res !== conf) {
8
+ if (hasBothRollupOptionsAndRolldownOptions(res)) context.warn(`Both \`rollupOptions\` and \`rolldownOptions\` were specified by ${JSON.stringify(p.name)} plugin. \`rollupOptions\` specified by that plugin will be ignored.`);
9
+ - if (res.esbuild) context.warn(`\`esbuild\` option was specified by ${JSON.stringify(p.name)} plugin. This option is deprecated, please use \`oxc\` instead.`);
10
+ if (res.optimizeDeps?.esbuildOptions) context.warn(`\`optimizeDeps.esbuildOptions\` option was specified by ${JSON.stringify(p.name)} plugin. This option is deprecated, please use \`optimizeDeps.rolldownOptions\` instead.`);
11
+ conf = mergeConfig(conf, res);
12
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mqtt-plus",
3
- "version": "1.4.13",
3
+ "version": "1.4.15",
4
4
  "description": "MQTT Communication Patterns",
5
5
  "keywords": [ "mqtt",
6
6
  "event", "emit",
@@ -30,6 +30,7 @@
30
30
  "./umd": { "types": "./dst-stage1/mqtt-plus.d.ts", "default": "./dst-stage2/mqtt-plus.umd.js" }
31
31
  },
32
32
  "devDependencies": {
33
+ "patch-package": "8.0.1",
33
34
  "@rse/stx": "1.1.4",
34
35
  "shx": "0.4.0",
35
36
  "eslint": "10.0.3",
@@ -43,6 +44,8 @@
43
44
  "chai": "6.2.2",
44
45
  "sinon-chai": "4.0.1",
45
46
  "sinon": "21.0.2",
47
+ "c8": "11.0.0",
48
+ "source-map-support": "0.5.21",
46
49
  "typescript": "5.9.3",
47
50
  "typescript-eslint": "8.57.0",
48
51
  "@typescript-eslint/parser": "8.57.0",
@@ -50,14 +53,16 @@
50
53
  "aedes": "1.0.1",
51
54
  "mosquitto": "1.0.2",
52
55
  "textframe": "1.2.1",
53
- "vite": "7.3.1",
56
+ "vite": "8.0.0",
54
57
  "vite-plugin-node-polyfills": "0.25.0",
58
+ "@rollup/plugin-replace": "6.0.3",
55
59
  "@wroud/vite-plugin-tsc": "0.12.2",
56
60
  "@terrastruct/d2": "0.1.33",
57
61
  "chokidar-cli": "3.0.0",
58
62
  "chalk": "5.6.2",
63
+ "open-cli": "8.0.0",
59
64
 
60
- "@types/node": "25.4.0",
65
+ "@types/node": "25.5.0",
61
66
  "@types/mocha": "10.0.10",
62
67
  "@types/chai": "5.2.3",
63
68
  "@types/sinon-chai": "4.0.0",
@@ -66,6 +71,9 @@
66
71
  "peerDependencies": {
67
72
  "mqtt": ">=4.0.0"
68
73
  },
74
+ "overrides": {
75
+ "vite-plugin-node-polyfills": { "vite": ">=7.0.0" }
76
+ },
69
77
  "dependencies": {
70
78
  "nanoid": "5.1.6",
71
79
  "cbor2": "2.3.0",
@@ -79,6 +87,7 @@
79
87
  "node": ">=16.0.0"
80
88
  },
81
89
  "scripts": {
90
+ "postinstall": "npm start patch-apply",
82
91
  "prepublishOnly": "npm start build",
83
92
  "start": "stx -v4 -c etc/stx.conf",
84
93
  "test": "npm start test"
@@ -162,6 +162,8 @@ export class SinkTrait<T extends APISchema = APISchema> extends SourceTrait<T> {
162
162
  /* utility functions for timeout management */
163
163
  const pushTimerId = `sink-push-recv:${requestId}`
164
164
  const refreshPushTimeout = () => this.timerRefresh(pushTimerId, () => {
165
+ if (streamEnded)
166
+ return
165
167
  const stream = this.pushStreams.get(requestId)
166
168
  if (stream !== undefined)
167
169
  stream.destroy(new Error("push stream timeout"))
@@ -201,6 +203,7 @@ export class SinkTrait<T extends APISchema = APISchema> extends SourceTrait<T> {
201
203
  return
202
204
  if (chunkParsed.error !== undefined) {
203
205
  streamEnded = true
206
+ clearPushTimeout()
204
207
  readable.destroy(new Error(chunkParsed.error))
205
208
  }
206
209
  else {
@@ -212,6 +215,7 @@ export class SinkTrait<T extends APISchema = APISchema> extends SourceTrait<T> {
212
215
  }
213
216
  if (chunkParsed.final) {
214
217
  streamEnded = true
218
+ clearPushTimeout()
215
219
  readable.push(null)
216
220
  }
217
221
  }
@@ -248,6 +252,10 @@ export class SinkTrait<T extends APISchema = APISchema> extends SourceTrait<T> {
248
252
  /* call handler */
249
253
  await callback(...params, info)
250
254
 
255
+ /* ensure stream is consumed or destroyed to prevent hang */
256
+ if (readable.readableFlowing !== true && !readable.destroyed)
257
+ readable.resume()
258
+
251
259
  /* await full stream consumption before confirming success */
252
260
  await streamDone
253
261
 
@@ -382,6 +390,7 @@ export class SinkTrait<T extends APISchema = APISchema> extends SourceTrait<T> {
382
390
  let remoteError = false
383
391
  let pushAcked = false
384
392
  let pushFinalized = false
393
+ let pushDataFinalSent = false
385
394
  let pushFinalizeResolve!: () => void
386
395
  let pushFinalizeReject!: (reason?: any) => void
387
396
  const pushFinalize = new Promise<void>((resolve, reject) => {
@@ -465,6 +474,8 @@ export class SinkTrait<T extends APISchema = APISchema> extends SourceTrait<T> {
465
474
  name, chunk, error, final, this.options.id, receiver)
466
475
  const message = this.codec.encode(chunkMsg)
467
476
  await this.publishToTopic(chunkTopic, message, { qos: 2, ...options })
477
+ if (error === undefined && final)
478
+ pushDataFinalSent = true
468
479
  }
469
480
 
470
481
  /* iterate over all chunks of the buffer */
@@ -491,8 +502,9 @@ export class SinkTrait<T extends APISchema = APISchema> extends SourceTrait<T> {
491
502
  abortController.abort(error)
492
503
 
493
504
  /* send error chunk only if push was acked and error did not originate from receiver
494
- (before ack, the sink has no chunk handler yet and will time out on its own) */
495
- if (pushAcked && !remoteError) {
505
+ (before ack, the sink has no chunk handler yet and will time out on its own;
506
+ after final data chunk, no additional terminal chunk should be sent) */
507
+ if (pushAcked && !remoteError && !pushDataFinalSent) {
496
508
  const chunkTopic = this.options.topicMake(name, "sink-push-request", receiver)
497
509
  const chunkMsg = this.msg.makeSinkPushChunk(requestId,
498
510
  name, undefined, error.message, true, this.options.id, receiver)
@@ -142,6 +142,13 @@ export class SourceTrait<T extends APISchema = APISchema> extends ServiceTrait<T
142
142
  await this.publishToTopic(responseTopic, message, { qos: options.qos ?? 2 })
143
143
  }
144
144
 
145
+ /* create a resource spool for request cleanup */
146
+ const reqSpool = new Spool()
147
+ reqSpool.roll(() => {
148
+ this.onResponse.delete(`source-fetch-credit:${requestId}`)
149
+ this.sourceControllers.delete(requestId)
150
+ })
151
+
145
152
  /* define abort controller and signal */
146
153
  const abortController = new AbortController()
147
154
  this.sourceControllers.set(requestId, abortController)
@@ -163,11 +170,11 @@ export class SourceTrait<T extends APISchema = APISchema> extends ServiceTrait<T
163
170
  gate.abort()
164
171
  this.sourceCreditGates.delete(requestId)
165
172
  }
166
- this.sourceControllers.delete(requestId)
167
- this.onResponse.delete(`source-fetch-credit:${requestId}`)
173
+ reqSpool.unroll()
168
174
  })
169
175
  const clearSourceTimeout = () => this.timerClear(sourceTimerId)
170
176
  refreshSourceTimeout()
177
+ reqSpool.roll(() => { clearSourceTimeout() })
171
178
 
172
179
  /* callback for creating and sending a chunk message */
173
180
  const sendChunk = async (
@@ -185,6 +192,7 @@ export class SourceTrait<T extends APISchema = APISchema> extends ServiceTrait<T
185
192
  /* call the handler callback */
186
193
  let ackSent = false
187
194
  let creditGate: CreditGate | undefined
195
+ let cancelledByFetcher = false
188
196
  try {
189
197
  if (topicName !== request.name)
190
198
  throw new Error(`source name mismatch (topic: "${topicName}", payload: "${request.name}")`)
@@ -197,13 +205,19 @@ export class SourceTrait<T extends APISchema = APISchema> extends ServiceTrait<T
197
205
  const initialCredit = request.credit
198
206
  creditGate = (initialCredit !== undefined && initialCredit > 0)
199
207
  ? new CreditGate(initialCredit) : undefined
200
- if (creditGate)
208
+ if (creditGate) {
201
209
  this.sourceCreditGates.set(requestId, creditGate)
210
+ reqSpool.roll(() => {
211
+ creditGate!.abort()
212
+ this.sourceCreditGates.delete(requestId)
213
+ })
214
+ }
202
215
 
203
216
  /* register credit/cancel handler (unconditional for cancel support) */
204
217
  this.onResponse.set(`source-fetch-credit:${requestId}`, (creditParsed: SourceFetchCredit) => {
205
218
  if (creditParsed.credit === 0) {
206
219
  /* cancel signal from fetcher */
220
+ cancelledByFetcher = true
207
221
  abortController.abort(new Error(`source fetch "${name}" cancelled by fetcher`))
208
222
  return
209
223
  }
@@ -238,22 +252,19 @@ export class SourceTrait<T extends APISchema = APISchema> extends ServiceTrait<T
238
252
  const error = ensureError(err, `handler for source "${name}" failed`)
239
253
  abortController.abort(error)
240
254
 
241
- /* send error as nak response or as error chunk */
242
- this.error(error)
243
- if (ackSent)
244
- await sendChunk(undefined, error.message, true).catch(() => {})
245
- else
246
- await sendResponse(error.message).catch(() => {})
255
+ /* on explicit fetcher cancellation, abort silently without emitting error responses */
256
+ if (!cancelledByFetcher) {
257
+ /* send error as nak response or as error chunk */
258
+ this.error(error)
259
+ if (ackSent)
260
+ await sendChunk(undefined, error.message, true).catch(() => {})
261
+ else
262
+ await sendResponse(error.message).catch(() => {})
263
+ }
247
264
  }
248
265
  finally {
249
266
  /* cleanup resources */
250
- clearSourceTimeout()
251
- if (creditGate) {
252
- creditGate.abort()
253
- this.sourceCreditGates.delete(requestId)
254
- }
255
- this.sourceControllers.delete(requestId)
256
- this.onResponse.delete(`source-fetch-credit:${requestId}`)
267
+ await reqSpool.unroll()
257
268
  }
258
269
  })
259
270
  spool.roll(() => { this.onRequest.delete(`source-fetch-request:${name}`) })
@@ -33,9 +33,8 @@ export const versionToNum = (str: string) => {
33
33
  return parseInt(m[1], 10) * 100 + minor
34
34
  }
35
35
 
36
- /* package version (string format, injected) */
37
- declare const __VERSION__: string
38
- export const VERSION = __VERSION__
36
+ /* package version (string format, overridden) */
37
+ export const VERSION = "0.0"
39
38
 
40
39
  /* package version (numeric format) */
41
40
  export const version = versionToNum(VERSION)
@@ -0,0 +1,224 @@
1
+ body, html {
2
+ margin:0; padding: 0;
3
+ height: 100%;
4
+ }
5
+ body {
6
+ font-family: Helvetica Neue, Helvetica, Arial;
7
+ font-size: 14px;
8
+ color:#333;
9
+ }
10
+ .small { font-size: 12px; }
11
+ *, *:after, *:before {
12
+ -webkit-box-sizing:border-box;
13
+ -moz-box-sizing:border-box;
14
+ box-sizing:border-box;
15
+ }
16
+ h1 { font-size: 20px; margin: 0;}
17
+ h2 { font-size: 14px; }
18
+ pre {
19
+ font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
20
+ margin: 0;
21
+ padding: 0;
22
+ -moz-tab-size: 2;
23
+ -o-tab-size: 2;
24
+ tab-size: 2;
25
+ }
26
+ a { color:#0074D9; text-decoration:none; }
27
+ a:hover { text-decoration:underline; }
28
+ .strong { font-weight: bold; }
29
+ .space-top1 { padding: 10px 0 0 0; }
30
+ .pad2y { padding: 20px 0; }
31
+ .pad1y { padding: 10px 0; }
32
+ .pad2x { padding: 0 20px; }
33
+ .pad2 { padding: 20px; }
34
+ .pad1 { padding: 10px; }
35
+ .space-left2 { padding-left:55px; }
36
+ .space-right2 { padding-right:20px; }
37
+ .center { text-align:center; }
38
+ .clearfix { display:block; }
39
+ .clearfix:after {
40
+ content:'';
41
+ display:block;
42
+ height:0;
43
+ clear:both;
44
+ visibility:hidden;
45
+ }
46
+ .fl { float: left; }
47
+ @media only screen and (max-width:640px) {
48
+ .col3 { width:100%; max-width:100%; }
49
+ .hide-mobile { display:none!important; }
50
+ }
51
+
52
+ .quiet {
53
+ color: #7f7f7f;
54
+ color: rgba(0,0,0,0.5);
55
+ }
56
+ .quiet a { opacity: 0.7; }
57
+
58
+ .fraction {
59
+ font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
60
+ font-size: 10px;
61
+ color: #555;
62
+ background: #E8E8E8;
63
+ padding: 4px 5px;
64
+ border-radius: 3px;
65
+ vertical-align: middle;
66
+ }
67
+
68
+ div.path a:link, div.path a:visited { color: #333; }
69
+ table.coverage {
70
+ border-collapse: collapse;
71
+ margin: 10px 0 0 0;
72
+ padding: 0;
73
+ }
74
+
75
+ table.coverage td {
76
+ margin: 0;
77
+ padding: 0;
78
+ vertical-align: top;
79
+ }
80
+ table.coverage td.line-count {
81
+ text-align: right;
82
+ padding: 0 5px 0 20px;
83
+ }
84
+ table.coverage td.line-coverage {
85
+ text-align: right;
86
+ padding-right: 10px;
87
+ min-width:20px;
88
+ }
89
+
90
+ table.coverage td span.cline-any {
91
+ display: inline-block;
92
+ padding: 0 5px;
93
+ width: 100%;
94
+ }
95
+ .missing-if-branch {
96
+ display: inline-block;
97
+ margin-right: 5px;
98
+ border-radius: 3px;
99
+ position: relative;
100
+ padding: 0 4px;
101
+ background: #333;
102
+ color: yellow;
103
+ }
104
+
105
+ .skip-if-branch {
106
+ display: none;
107
+ margin-right: 10px;
108
+ position: relative;
109
+ padding: 0 4px;
110
+ background: #ccc;
111
+ color: white;
112
+ }
113
+ .missing-if-branch .typ, .skip-if-branch .typ {
114
+ color: inherit !important;
115
+ }
116
+ .coverage-summary {
117
+ border-collapse: collapse;
118
+ width: 100%;
119
+ }
120
+ .coverage-summary tr { border-bottom: 1px solid #bbb; }
121
+ .keyline-all { border: 1px solid #ddd; }
122
+ .coverage-summary td, .coverage-summary th { padding: 10px; }
123
+ .coverage-summary tbody { border: 1px solid #bbb; }
124
+ .coverage-summary td { border-right: 1px solid #bbb; }
125
+ .coverage-summary td:last-child { border-right: none; }
126
+ .coverage-summary th {
127
+ text-align: left;
128
+ font-weight: normal;
129
+ white-space: nowrap;
130
+ }
131
+ .coverage-summary th.file { border-right: none !important; }
132
+ .coverage-summary th.pct { }
133
+ .coverage-summary th.pic,
134
+ .coverage-summary th.abs,
135
+ .coverage-summary td.pct,
136
+ .coverage-summary td.abs { text-align: right; }
137
+ .coverage-summary td.file { white-space: nowrap; }
138
+ .coverage-summary td.pic { min-width: 120px !important; }
139
+ .coverage-summary tfoot td { }
140
+
141
+ .coverage-summary .sorter {
142
+ height: 10px;
143
+ width: 7px;
144
+ display: inline-block;
145
+ margin-left: 0.5em;
146
+ background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
147
+ }
148
+ .coverage-summary .sorted .sorter {
149
+ background-position: 0 -20px;
150
+ }
151
+ .coverage-summary .sorted-desc .sorter {
152
+ background-position: 0 -10px;
153
+ }
154
+ .status-line { height: 10px; }
155
+ /* yellow */
156
+ .cbranch-no { background: yellow !important; color: #111; }
157
+ /* dark red */
158
+ .red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
159
+ .low .chart { border:1px solid #C21F39 }
160
+ .highlighted,
161
+ .highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
162
+ background: #C21F39 !important;
163
+ }
164
+ /* medium red */
165
+ .cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
166
+ /* light red */
167
+ .low, .cline-no { background:#FCE1E5 }
168
+ /* light green */
169
+ .high, .cline-yes { background:rgb(230,245,208) }
170
+ /* medium green */
171
+ .cstat-yes { background:rgb(161,215,106) }
172
+ /* dark green */
173
+ .status-line.high, .high .cover-fill { background:rgb(77,146,33) }
174
+ .high .chart { border:1px solid rgb(77,146,33) }
175
+ /* dark yellow (gold) */
176
+ .status-line.medium, .medium .cover-fill { background: #f9cd0b; }
177
+ .medium .chart { border:1px solid #f9cd0b; }
178
+ /* light yellow */
179
+ .medium { background: #fff4c2; }
180
+
181
+ .cstat-skip { background: #ddd; color: #111; }
182
+ .fstat-skip { background: #ddd; color: #111 !important; }
183
+ .cbranch-skip { background: #ddd !important; color: #111; }
184
+
185
+ span.cline-neutral { background: #eaeaea; }
186
+
187
+ .coverage-summary td.empty {
188
+ opacity: .5;
189
+ padding-top: 4px;
190
+ padding-bottom: 4px;
191
+ line-height: 1;
192
+ color: #888;
193
+ }
194
+
195
+ .cover-fill, .cover-empty {
196
+ display:inline-block;
197
+ height: 12px;
198
+ }
199
+ .chart {
200
+ line-height: 0;
201
+ }
202
+ .cover-empty {
203
+ background: white;
204
+ }
205
+ .cover-full {
206
+ border-right: none !important;
207
+ }
208
+ pre.prettyprint {
209
+ border: none !important;
210
+ padding: 0 !important;
211
+ margin: 0 !important;
212
+ }
213
+ .com { color: #999 !important; }
214
+ .ignore-none { color: #999; font-weight: normal; }
215
+
216
+ .wrapper {
217
+ min-height: 100%;
218
+ height: auto !important;
219
+ height: 100%;
220
+ margin: 0 auto -48px;
221
+ }
222
+ .footer, .push {
223
+ height: 48px;
224
+ }