@nxtedition/lib 21.6.3 → 21.8.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/app.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import * as inspector from 'node:inspector'
2
2
  import os from 'node:os'
3
+ import http from 'node:http'
3
4
  import net from 'node:net'
4
5
  import assert from 'node:assert'
6
+ import cluster from 'node:cluster'
5
7
  import stream from 'node:stream'
6
8
  import { Buffer } from 'node:buffer'
7
9
  import { getDockerSecretsSync } from './docker-secrets.js'
@@ -802,46 +804,138 @@ export function makeApp(appConfig, onTerminate) {
802
804
  }
803
805
  }
804
806
 
805
- const inspectBC = new BroadcastChannel('nxt:inspect')
806
- const inspectSet = new Set()
807
+ if (appConfig.inspect !== false && !cluster.isWorker) {
808
+ // TODO (fix): What about cluster?
807
809
 
808
- if (serviceWorkerId) {
809
- inspectBC.onmessage = ({ data }) => {
810
- const { type, id } = data
810
+ const inspectBC = new BroadcastChannel('nxt:inspect')
811
+ let inspectOpen = false
811
812
 
812
- if (id !== serviceWorkerId) {
813
- return
814
- }
813
+ if (!isMainThread) {
814
+ inspectBC.onmessage = ({ data }) => {
815
+ const { type, id } = data
816
+
817
+ if (id !== threadId) {
818
+ return
819
+ }
815
820
 
816
- if (type === 'inspect:open') {
817
- // TODO (fix): What happens if you call inspect:open multiple times?
818
- inspector.open(data.port, data.hostname)
819
- } else if (type === 'inspect:close') {
820
- inspector.close()
821
+ if (type === 'inspect:open') {
822
+ // TODO (fix): What happens if you call inspect:open multiple times?
823
+ if (!inspectOpen) {
824
+ inspector.open(data.port, data.hostname)
825
+ inspectOpen = true
826
+ }
827
+ } else if (type === 'inspect:close') {
828
+ if (inspectOpen) {
829
+ inspector.close()
830
+ inspectOpen = false
831
+ }
832
+ }
821
833
  }
822
- }
823
834
 
824
- inspectBC.postMessage({ type: 'inspect:register', id: serviceWorkerId })
825
- destroyers.unshift(() => {
826
- inspector.close()
827
- inspectBC.postMessage({ type: 'inspect:unregister', id: serviceWorkerId })
828
- setTimeout(() => {
829
- inspectBC.close()
830
- }, 100)
831
- })
832
- } else {
833
- inspectBC.onmessage = ({ data }) => {
834
- const { type } = data
835
+ inspectBC.postMessage({
836
+ type: 'inspect:register',
837
+ id: threadId,
838
+ worker: {
839
+ id: threadId,
840
+ name: serviceName,
841
+ module: serviceModule,
842
+ version: serviceVersion,
843
+ workerId: serviceWorkerId,
844
+ instanceId: serviceInstanceId,
845
+ threadId,
846
+ processId: process.pid,
847
+ },
848
+ })
849
+ destroyers.unshift(() => {
850
+ if (inspectOpen) {
851
+ inspector.close()
852
+ inspectOpen = false
853
+ }
854
+ inspectBC.postMessage({ type: 'inspect:unregister', id: threadId })
855
+ setTimeout(() => {
856
+ inspectBC.close()
857
+ }, 100)
858
+ })
859
+ } else {
860
+ const inspectMap = new Set()
861
+
862
+ inspectBC.onmessage = ({ data }) => {
863
+ const { type, worker } = data
835
864
 
836
- if (type === 'inspect:register') {
837
- inspectSet.add(data.id)
838
- } else if (type === 'inspect:unregister') {
839
- inspectSet.delete(data.id)
865
+ if (type === 'inspect:register') {
866
+ inspectMap.set(data.id, worker)
867
+ } else if (type === 'inspect:unregister') {
868
+ inspectMap.delete(data.id)
869
+ }
840
870
  }
871
+ destroyers.unshift(() => inspectBC.close())
872
+
873
+ // TODO (fix): Determinisitc port also in dev mode... hash of something?
874
+ const port =
875
+ appConfig.inspect?.port ?? (isProduction ? 38603 : Math.floor(38000 + Math.random() * 1000))
876
+ const server = http
877
+ .createServer(async (req, res) => {
878
+ try {
879
+ // TOOD (fix); Better matching...
880
+ if (!req.url.startsWith('/inspect')) {
881
+ res.statusCode = 404
882
+ res.end()
883
+ return
884
+ }
885
+
886
+ if (req.method === 'GET') {
887
+ res.end(
888
+ JSON.stringify({
889
+ workers: Array.from(inspectMap.values()),
890
+ }),
891
+ )
892
+ return
893
+ }
894
+
895
+ if (req.method === 'POST') {
896
+ const m = req.url.match(/^\/inspect\/([^/]+)/)
897
+ if (!m) {
898
+ res.statusCode = 404
899
+ res.end()
900
+ return
901
+ } else {
902
+ const { port, hostname } = await json(req)
903
+
904
+ // TODO (fix): What about return value & errors?
905
+ inspectBC.postMessage({
906
+ type: port || hostname ? 'inspect:open' : 'inspect:close',
907
+ id: parseInt(m[1]),
908
+ port,
909
+ hostname,
910
+ })
911
+
912
+ res.end()
913
+ return
914
+ }
915
+ }
916
+
917
+ res.statusCode = 405
918
+ res.end()
919
+ } catch (err) {
920
+ logger?.error({ err }, 'inspect http error')
921
+
922
+ if (res.headersSent) {
923
+ res.destroy()
924
+ } else {
925
+ res.statusCode = 500
926
+ res.end()
927
+ }
928
+ }
929
+ })
930
+ .listen(port)
931
+
932
+ logger.debug({ port }, 'inspect listening')
933
+
934
+ destroyers.unshift(() => new Promise((resolve) => server.close(resolve)))
841
935
  }
842
- destroyers.unshift(() => inspectBC.close())
843
936
  }
844
937
 
938
+ // TODO (fix): Deprecate
845
939
  if (appConfig.http) {
846
940
  const httpConfig = { ...appConfig.http, ...config.http }
847
941
 
@@ -864,36 +958,6 @@ export function makeApp(appConfig, onTerminate) {
864
958
  return next()
865
959
  }
866
960
  },
867
- async ({ req, res }, next) => {
868
- if (req.url.startsWith('/inspect')) {
869
- if (req.method === 'GET') {
870
- res.end(JSON.stringify({ workers: Array.from(inspectSet).map((id) => ({ id })) }))
871
- } else if (req.method === 'POST') {
872
- const m = req.url.match(/^\/inspect\/([^/]+)/)
873
- if (!m) {
874
- res.statusCode = 404
875
- res.end()
876
- } else {
877
- const { port, hostname } = await json(req)
878
-
879
- // TODO (fix): What about return value & errors?
880
- inspectBC.postMessage({
881
- type: port || hostname ? 'inspect:open' : 'inspect:close',
882
- id: m[1],
883
- port,
884
- hostname,
885
- })
886
-
887
- res.end()
888
- }
889
- } else {
890
- res.statusCode = 405
891
- res.end()
892
- }
893
- } else {
894
- return next()
895
- }
896
- },
897
961
  appConfig.http.request
898
962
  ? appConfig.http.request
899
963
  : typeof appConfig.http === 'function'
package/couch.js CHANGED
@@ -119,7 +119,7 @@ export function makeCouch(opts) {
119
119
  * @param {string} [options.since=null] - The sequence number to start from.
120
120
  * @param {number} [options.highWaterMark=128 * 1024] - Buffering.
121
121
  * @param {object} [options.selector=null] - The selector to filter changes.
122
- * @yields {Array<{ id: string, seq?: string, doc?: Object, deleted?: boolean, changes: Array<{ rev: string }> }>}
122
+ * @return {AsyncGenerator<Array<{ id: string, seq?: string, doc?: Object, deleted?: boolean, changes: Array<{ rev: string }>}> & { lastSeq: string | null | 0 }}
123
123
  */
124
124
  async function* changes({ client = defaultClient, signal = null, logger, ...options } = {}) {
125
125
  const params = {}
package/merge-ranges.js CHANGED
@@ -29,7 +29,13 @@ export default function mergeRanges(ranges) {
29
29
  const range = ranges[n]
30
30
  const top = stack[stack.length - 1]
31
31
 
32
- if (range.length !== 2 || !Number.isFinite(range[0]) || !Number.isFinite(range[1])) {
32
+ if (
33
+ !Array.isArray(range) ||
34
+ range.length !== 2 ||
35
+ !Number.isFinite(range[0]) ||
36
+ !Number.isFinite(range[1]) ||
37
+ range[0] > range[1]
38
+ ) {
33
39
  continue
34
40
  }
35
41
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/lib",
3
- "version": "21.6.3",
3
+ "version": "21.8.0",
4
4
  "license": "MIT",
5
5
  "author": "Robert Nagy <robert.nagy@boffins.se>",
6
6
  "type": "module",
package/s3.js CHANGED
@@ -10,13 +10,13 @@ const QUEUE = new PQueue({ concurrency: 8 })
10
10
  const MD5_HEX_EXPR = /^[A-F0-9]{32}$/i
11
11
 
12
12
  /**
13
- * @typedef {import('undici').Dispatcher} Dispatcher
14
- * @typedef {import('@aws-sdk/client-s3').S3ClientConfig & {dispatcher?: Dispatcher}} S3ClientConfig
13
+ * @import { Dispatcher } from 'undici'
14
+ * @import { S3ClientConfig, CreateMultipartUploadRequest } from '@aws-sdk/client-s3'
15
15
  */
16
16
 
17
17
  export class S3Client extends AWS.S3Client {
18
18
  /**
19
- * @param {S3ClientConfig} config
19
+ * @param {S3ClientConfig & { dispatcher?: Dispatcher }} config
20
20
  */
21
21
  constructor(config) {
22
22
  const { dispatcher, ...options } = config
@@ -79,12 +79,11 @@ class UndiciRequestHandler extends NodeHttpHandler {
79
79
  * @param {Object} options.logger - The logger to use.
80
80
  * @param {number} [options.partSize=16e6] - The size of each part in the multipart upload.
81
81
  * @param {PQueue} [options.queue] - The queue to use for part uploads.
82
- * @param {Object} options.params - The parameters for the upload.
83
- * @param {Buffer|NodeJS.ReadStream} options.params.Body - The data to upload.
84
- * @param {string} options.params.Key - The key of the object.
85
- * @param {string} options.params.Bucket - The name of the bucket.
86
- * @param {string} [options.params.ContentMD5] - The MD5 hash of the object as base64 string.
87
- * @param {number} [options.params.ContentLength] - The length of the object.
82
+ * @param {CreateMultipartUploadRequest & {
83
+ * Body: Buffer | NodeJS.ReadStream,
84
+ * ContentMD5?: string,
85
+ * ContentLength?: number,
86
+ * }} options.params - The parameters for the upload.
88
87
  * @returns {Promise<Object>} The result of the upload.
89
88
  */
90
89
  export async function upload({
@@ -107,7 +106,7 @@ export async function upload({
107
106
  throw new Error('Invalid params')
108
107
  }
109
108
 
110
- const { Body, Key, Bucket, ContentMD5, ContentLength } = params
109
+ const { Body, Key, Bucket, ContentMD5, ContentLength, ...createMultipartParams } = params
111
110
 
112
111
  const size = ContentLength != null ? Number(ContentLength) : null
113
112
 
@@ -144,7 +143,7 @@ export async function upload({
144
143
  }
145
144
 
146
145
  const multipartUploadOutput = await s3.send(
147
- new AWS.CreateMultipartUploadCommand({ Bucket, Key }),
146
+ new AWS.CreateMultipartUploadCommand({ ...createMultipartParams, Bucket, Key }),
148
147
  { abortSignal: uploader.signal },
149
148
  )
150
149
  uploader.signal.throwIfAborted()