From f0e656b33bcaf4ad29a1a8ae43a90303b13bf950 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Fri, 20 Mar 2026 15:40:21 +0000 Subject: [PATCH 01/96] data access skeleton --- acs-data-access/.gitignore | 2 + acs-data-access/bin/api.js | 52 +++++++++ acs-data-access/lib/api-v1.js | 192 +++++++++++++++++++++++++++++++ acs-data-access/lib/constants.js | 50 ++++++++ acs-data-access/lib/dataflow.js | 114 ++++++++++++++++++ acs-data-access/lib/notify.js | 79 +++++++++++++ acs-data-access/lib/routes.js | 15 +++ acs-data-access/lib/validate.js | 36 ++++++ acs-data-access/package.json | 25 ++++ lib/js-rx-client/lib/auth.js | 29 +++++ 10 files changed, 594 insertions(+) create mode 100644 acs-data-access/.gitignore create mode 100644 acs-data-access/bin/api.js create mode 100644 acs-data-access/lib/api-v1.js create mode 100644 acs-data-access/lib/constants.js create mode 100644 acs-data-access/lib/dataflow.js create mode 100644 acs-data-access/lib/notify.js create mode 100644 acs-data-access/lib/routes.js create mode 100644 acs-data-access/lib/validate.js create mode 100644 acs-data-access/package.json diff --git a/acs-data-access/.gitignore b/acs-data-access/.gitignore new file mode 100644 index 000000000..13dfa3637 --- /dev/null +++ b/acs-data-access/.gitignore @@ -0,0 +1,2 @@ +.env +node_modules/ \ No newline at end of file diff --git a/acs-data-access/bin/api.js b/acs-data-access/bin/api.js new file mode 100644 index 000000000..0099bcc25 --- /dev/null +++ b/acs-data-access/bin/api.js @@ -0,0 +1,52 @@ +#!/usr/bin/env node + +/* +* ACS Data Access Service +* Entry point: api.js +*/ + + +import { RxClient, UUIDs } from '@amrc-factoryplus/rx-client'; +import { WebAPI } from '@amrc-factoryplus/service-api'; +import { routes } from '../lib/routes.js'; +import { Version, Service } from '../lib/constants.js'; +import {DataFlow} from '../lib/dataflow.js'; + +const { env } = process; + +const fplus = await new RxClient({ + env, +}).init(); + + +const debug = fplus.debug; +debug.log("app", "Starting acs-data-service revision %s", GIT_VERSION); + +// the dataflow object is full of sequences that does most of the work of this service. Sequence means an RX observable. +const data = new DataFlow({ + fplus +}); + +// const api = await new WebAPI({ +// ping: { +// version: Version, +// service: UUIDs.Service.DataAccess, +// software: { +// vendor: 'AMRC', +// application: 'acs-data-access', +// }, +// }, +// debug: fplus.debug, +// realm: env.REALM, +// hostname: env.HOSTNAME, +// keytab: env.SERVER_KEYTAB, +// http_port: env.PORT, +// max_age: env.CACHE_MAX_AGE, +// routes: routes({ +// fplus, +// }), +// }).init(); + +// api.run(); + +data.run(); diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js new file mode 100644 index 000000000..333b40a67 --- /dev/null +++ b/acs-data-access/lib/api-v1.js @@ -0,0 +1,192 @@ +/* +* ACS Data Access Service +* APIV1 +*/ + + +import { APIError } from "@amrc-factoryplus/service-api"; + +function fail(status, message) { + throw new APIError(status); +} + +export class APIv1 { + constructor(opts) { + this.data = opts.data; + this.fplus = opts.fplus; + this.auth = this.fplus.Auth; + this.log = this.fplus.debug.bound("api-v1"); + this.routes = this.setup_routes(); + } + + setup_routes() { + let api = express.Router(); + + api.route("/metadata") + .get(this.metadata_get_list.bind(this)); + + api.route("/metadata/:uuid") + .get(this.metadata_get.bind(this)); + + + api.route("/data/:uuid") + .post(this.data_get.bind(this)); + + + return api; + } + + /** GET. Returns a list of Dataset UUIDs that the client has READ access to. + * The dataset can be optionally restricted by from and to dates. + * Dates must be in the format ISO date-time string in UTC 2025-11-13T09:33:18.000Z + * @param from {date} (optional, inclusive) query param + * @param to {date} (optional, inclusive) query param + */ + async metadata_get_list(req, res){ + const principal = req.auth; + const {from, to} = req.query; + + // 1. If from and/or to provided - check their format + // 2. Check the client is authenticated + // 3. Retrieve the list of metadata UUIDs from ConfigDB and return + + const response = await this.auth.fetch_auth_acl(principal_type, principal); + + + } + + + + + + + /** GET. Accepts Dataset UUID and returns metadata about a Published dataset. + * @param uuid {request param} + * @param + * @returns metadata - JSON object with properties: + * uuid {UUID} - Dataset UUID + * name {string} - Dataset name from General Information + * from {date} - Dataset starting bound - derived from bounds of dataset Sessions (if any) + * to {date} - Dataset finishing bound - derived from bounds of dataset Sessions (if any) + * function {array} - Array of Functional classes - UUIDs of classes dataset belongs to (only members of Functional dataset type) + * metadata {object} - Map of metadata configs keyed by Application UUID - contains all config entries for dataset which use applications in the Dataset metadata class + * parts {array} - Subset datasets - contains UUIDs of all subclasses of dataset the client has READ access to. + */ + async metadata_get(req, res){ + const {uuid} = req.params + + + // + } + + + /** POST. Queries all possible Influx suffixes, combines the measuremenets and returns actual data from a dataset. + * Empty POST body requests all dataset measurements. + * @param {*} req + * @param {*} res + * @returns CSV with columns: + * device - Device this data point comes from + * metric - metric name (don't include Influx :x suffix) + * timestamp - ISO string + * value - actual data value + * unit - Engineering unit (if available) + */ + async data_get(req, res){ + + } + + /** GET. + * + * @param {*} req + * @param {*} res + * @returns list of dataset UUIDs the client has permission to EDIT + */ + async structure_get_list(req, res){ + + } + + /** GET. Fetches structural definition of dataset + * Requires EDIT permission on the dataset + * READONLY clients can't see this structure + * If dataset is structurally INVALID -> Special UUID (structurally invalid dataset) and absent config + * @param {*} req + * @param {*} res + * @returns object: + * uuid {UUID} - Dataset UUID + * class {UUID} - Structural class: Sparkplug device, Union Dataset, Session + * config {any} - Structural definition: "not visible", Union components, Session limits + */ + async structure_get(req, res){ + + } + + /** POST. Creates a new dataset. + * + * @param {*} req.body must be object (class, config) without uuid. + * @param {*} res + * @returns new dataset's UUID - JSON string + */ + async structure_create(req, res){ + + } + + /** PUT. Updates dataset definition. + * + * @param {*} req.body must be object (class, config) and UUID (optional) + * @param {*} res + */ + async structure_update(req, res){ + + } + + + async name_get(req, res) { + const { uuid } = req.params; + + // This URL could not possibly exist (different from "could exist in the future") + if (!valid_uuid(uuid)) fail(410); + + const names = await rx.firstValueFrom(this.data.names); + const name = names.get(uuid); + if (!name) fail(404); + + const ok = await this.auth.check_acl( + // Principal + req.auth, + // Your own service permission + Perm.MyGetNamePermission, + // Target UUID + uuid, + // Do we accept wildcards? + true, + ); + + if (!ok) { + fail(403); + } + + return res.status(200).json(name); + } + + async name_put(req, res) { + const { uuid } = req.params; + if (!valid_uuid(uuid)) fail(410); + + // May need this in your own service. We don't in this case. This is checking validity of what you give the service. + // if (grant && !valid_grant(grant)) fail(422); + + const ok = await this.auth.check_acl( + req.auth, + Perm.MyPutNamePermission, + uuid, + true, + ); + + if (!ok) { + fail(403); + } + + const retval = await this.data.request({ type: "name", object: uuid, name: req.body }); + return res.status(retval.status).end(); + } +} diff --git a/acs-data-access/lib/constants.js b/acs-data-access/lib/constants.js new file mode 100644 index 000000000..0e312af50 --- /dev/null +++ b/acs-data-access/lib/constants.js @@ -0,0 +1,50 @@ + +export const DataAccess = { + App: { + DatasetDefinition: "eae2d4ae-164d-4dc6-b646-7e0320057bd9", + DatasetMetadata: "e3b9fd2c-9de1-470b-9675-739e2a55b77f", + UnionComponents: "1c4ca454-de38-44d9-92fb-aa5218bfa257", + SessionLimits: "8754c000-3778-4ae6-b2b8-bbcd959bb775", + MESIdentifiers: "af178f0c-3b1e-44f2-9724-5cf06e8fd056", + }, + + Class: { + Measurement: "cce0ac4e-b5ba-4837-b45d-c74df55aa2d7", + Dataset: "c31d3cbd-01cd-4833-8014-c4512aef1e5c", + SparkplugDevice: "18773d6d-a70d-443a-b29a-3f1583195290", + Session: "2f555be8-e7fc-4fb0-b62f-809cefd8a141", + Union: "c57a42a9-348c-4972-a05b-9082c20f2afc", + Published: "414d2d10-6be8-4c27-8e9f-c716ef5432b9", + Partial: "6c583d11-9a88-4bc1-b77c-81b01e9c9827", + MESDataset: "586205bf-81c6-4091-9d2c-f3c0465ebdc4", + Equipment: "4c93ddc1-e610-4efe-91e3-a355f9ba1a09", + WorkOrder: "b416e44c-c57e-4486-9431-64c425f1b2c6", + Product: "4a089748-b26b-4f12-8f1a-164bfba97809", + Operation: "bd0354eb-b8f7-4bd9-8407-0588e545603c", + MES: "2c691583-89fe-4421-bf2c-64e34e663711" + }, + + Group: { + DatasetGroup: "17e37253-8626-4031-b217-28c6a03e91c1", + DatasetRoleGroup: "56c52f70-0649-4962-8526-9ec9d1c85ca4", + StructuralDatasetType: "70ff7bea-bb2d-48c2-88fd-4f7a79b1aa3c", + FunctionalDatasetGroup: "86e5b048-e956-4820-939e-3abf3eda4e03", + }, + + Service: { + DataAccess: "06cee697-29d3-4972-9479-bc392e24946e" + }, + + Requirement: { + ServiceRole: "dd18050c-a9ab-4287-8af2-e983f20e89c8" + }, + + Perm: { + All: "1e3cb5aa-a3f7-4bb6-9ef1-c3bc08427f60", + CreateDataset: "2d666b41-7a0d-4845-ad59-3113f25b469a", + IncludeInUnion: "94d51085-af83-4796-8059-fcd578e3f572", + ReadDataset: "ec48462e-37eb-4f56-8efa-83d813e85559", + UseForSession: "c089b9a9-06cd-4211-94fc-9ad52a759987", + EditDataset: "af06b9e5-456a-43e4-b636-5b17de28fc7f", + } +} diff --git a/acs-data-access/lib/dataflow.js b/acs-data-access/lib/dataflow.js new file mode 100644 index 000000000..be0adf788 --- /dev/null +++ b/acs-data-access/lib/dataflow.js @@ -0,0 +1,114 @@ +/* +* ACS Data Access Service +* Data-Flow / sequence management +*/ + +import { RxClient, UUIDs } from '@amrc-factoryplus/rx-client'; +import { DataAccess } from './constants.js'; + +export class DataFlow { + constructor(opts) { + const { fplus } = opts; + + // this.root = opts.root_principal; // names of the kerberos principal that overrides all ACL checks. comes from the environment. Always set to the admin principal name. Override principal. + + this.log = fplus.debug.bound("data"); + this.cdb = fplus.ConfigDB; + this.auth = fplus.Auth; + + /* Requests to update the database */ + /* it's a subject which means we can push values down the sequence. That is driven from requests to the api to make changes. + All the rest are driven from external sources of data (db or ConfigDB). + */ + /** + * for the DataFlow service we'd want to think about what sources of + * information we need to track from the configDB and we start building sequences that track that information and transform it into forms that are useful to us. + */ + this.requests = new rx.Subject(); + /* Responses to these requests */ + this.responses = this._build_responses(); + this.metadata = this._build_metadata(); + + this.sessions = this.cdb.search_app(DataAccess.App.SessionLimits); + + this.names = this._build_names(); + } + + + _build_metadata(){ + + } + + update_name(r) { + // Something like this but returning an HTTP-like response. (put_config returns an exception that needs catching) + this.cdb.put_config(UUIDs.App.Info, r.object, { name: r.name }); + }; + + + _build_names() { + return rxx.rx( + cdb.search_app(UUIDs.App.Info), + // rx.map acts on the sequence of responses, entries.map acts on the entries maps + rx.map(entries => entries.map(entry => entry.name)), + // Cache the map (1 refers to 1 previous value) + rx.shareReplay(1)); + } + + + + _build_responses() { + // this obj is a map from request type to a method that handles this request. + const updaters = { + // This tells you what to do with a "name" type object + // UUIDs.App.Info == "General Information" application + name: this.update_name, + }; + + /** + * the processing loop that actually does the work which takes the sequence of requests that are coming in from the method down below where it pushes to the subject. And then each time we get a new request it looks up how we perform this request, it actually performs it with updater. + * If we get a request that we don't have an updater for -> just return 500. If exists -> we call the updater. + */ + return rxx.rx(this.requests, + rx.mergeMap(r => { + const updater = updaters[r.type] + ?? (async () => ({ status: 500 })); + + return updater.call(this, r) + .then(rv => ({ ...rv, type: r.type, request: r.request })); + }), + rx.share()); + } + + /** + * This function tells the data flow to start subscribing to things and making things happen. + */ + run() { + this.sessions.subscribe(ss => this.log("SESSION UPDATE %o", ss.toJS())); + } + + + + + + /** Request a change to the database. + * + * This submits a change request to the database and returns a + * Promise containing an HTTP status code for the outcome. If a + * change is made then the change will also be published to + * `updates`. + * + * @param r A request object. Must contain a `kind` field. + * @returns A response object containing a `status` field. + */ + // The actual processing of the request happens in the _build_responses method that builds the sequence. + request(r) { + const request = uuidv4(); + /* Construct the Promise to the response before we send the + * request. This avoids a race condition. */ + const response = rx.firstValueFrom(rxx.rx( + this.responses, + rx.filter(r => r.request == request))); + this.requests.next({ ...r, request }); + return response; + } +} diff --git a/acs-data-access/lib/notify.js b/acs-data-access/lib/notify.js new file mode 100644 index 000000000..392940f84 --- /dev/null +++ b/acs-data-access/lib/notify.js @@ -0,0 +1,79 @@ +/* +* ACS Data Access Service +* DataAccessNotify +*/ + + +import * as rx from "rxjs"; + +import * as rxx from "@amrc-factoryplus/rx-util"; +import { Notify } from "@amrc-factoryplus/service-api"; + +export class DataAccessNotify { + constructor(opts) { + this.data = opts.data; + this.log = opts.debug.bound("notify"); + + this.notify = this.build_notify(opts.api); + } + + run() { + this.log("Running Data Access Notify Server"); + this.notify.run(); + } + + /** + * constructs a Notify object + * acts as an express router + * routes notify requests to methods you give it. + */ + build_notify(api) { + const notify = new Notify({ + api, + log: this.log, + }); + + // This is basically Express. This is more strict in that directories must end with a "/". It won't redirect. + + notify.watch("v1/metadata", this.metadata_list.bind(this)); + notify.watch("v1/metadata/:uuid", ) + notify.search("v1/metadata/", ) + + notify.watch("v1/structure/", ) + notify.watch("v1/structure/:uuid", ) + notify.search("v1/structure/", ) + + return notify; + } + + /** + * Returns a list of Dataset UUIDs the client has access to + */ + metadata_list(sess){ + + } + + /** + * Get names for all objects the principal can see. + */ + name_list(sess) { + // We should combine this with the session permissions to see the objects + return rxx.rx( + this.data.names, + rx.map(entries => entries.keySeq().toArray()), + rx.mergeMap( + // You need to filter out what they can't see here using `sess` (not in a .filter :)) + ), + rx.map( + // You need to build your response objects here + )); + } + + + /** + * Get changes to the name of a particular object `obj`. + */ + object_name(sess, obj) { + // Do similar to `name_list` here. + } +} diff --git a/acs-data-access/lib/routes.js b/acs-data-access/lib/routes.js new file mode 100644 index 000000000..ddf5364fb --- /dev/null +++ b/acs-data-access/lib/routes.js @@ -0,0 +1,15 @@ +/* +* ACS Data Access Service +* Routes +*/ + + +import { APIv1 } from './api-v1.js'; + +export function routes(opts) { + const api_v1 = new APIv1(opts); + + return (app) => { + app.use('/v1', api_v1.routes); + }; +} diff --git a/acs-data-access/lib/validate.js b/acs-data-access/lib/validate.js new file mode 100644 index 000000000..45ffe5bd7 --- /dev/null +++ b/acs-data-access/lib/validate.js @@ -0,0 +1,36 @@ +/* + * ACS Data Access service + * Data validation routines + */ + +export const UUID_rx = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/; +export const KRB_rx = /^[a-zA-Z0-9_./-]+@[A-Za-z0-9-.]+$/; + +export const booleans = { + undefined: false, + "true": true, "false": false, + "1": true, "0": false, + on: true, off: false, + yes: true, no: false, +}; + +export function valid_uuid(uuid) { + if (UUID_rx.test(uuid)) + return true; + //debug.log("debug", `Ignoring invalid UUID [${uuid}]`); + return false; +} + +export function valid_krb(krb) { + if (KRB_rx.test(krb)) + return true; + //debug.log("debug", `Ignoring invalid principal [${krb}]`); + return false; +} + +export function valid_grant (grant) { + if (!valid_uuid(grant.principal)) return false; + if (!valid_uuid(grant.permission)) return false; + if (!valid_uuid(grant.target)) return false; + return (grant.plural === true || grant.plural === false); +} diff --git a/acs-data-access/package.json b/acs-data-access/package.json new file mode 100644 index 000000000..156bda9ec --- /dev/null +++ b/acs-data-access/package.json @@ -0,0 +1,25 @@ +{ + "name": "acs-data-access", + "version": "1.0.0", + "description": "Data Access Service supports creating, finding and downloading datasets via a WebAPI.", + "main": "server.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@amrc-factoryplus/rx-client": "file:../lib/js-rx-client", + "@amrc-factoryplus/rx-util": "file:../lib/js-rx-util", + "@amrc-factoryplus/service-api": "file:../lib/js-service-api", + "@amrc-factoryplus/service-client": "file:../lib/js-service-client", + "@dotenvx/dotenvx": "^1.39.0", + "express": "^4.21.2", + "stream-size": "^0.0.6", + "uuid": "^11.0.5" + }, + "devDependencies": { + "eslint": "^9.23.0" + } +} diff --git a/lib/js-rx-client/lib/auth.js b/lib/js-rx-client/lib/auth.js index d74b8a5a6..93a4cb5ff 100644 --- a/lib/js-rx-client/lib/auth.js +++ b/lib/js-rx-client/lib/auth.js @@ -35,4 +35,33 @@ export class Auth extends Interfaces.Auth { const res = await rx.firstValueFrom(this._watch_acl(path)); return [res.status, res.body]; } + + /** + * We have not handled bootstrap acls + * @param {*} principal + * @returns + */ + watch_acl (principal) { + const url = urljoin("v2", "acl", "kerberos", principal); + return rxx.rx( + this._watch_acl(url), + + rx.filter(r => r.status == 200), + rx.map(r => r.body) + ); + } + + + watch_acl_with_perm (principal, permission) { + return rxx.rx( + this.watch_acl(principal), + rx.map(arr => + arr.filter(x => x.permission == permission) + .map(x => x.target) + ), + rx.map(arr => new Set(arr)) + ); + } + + // watch_acl_with_perm(prin) } From a2299573bb0f7cd61831da5c2e6f50bccfa1f8e6 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 23 Mar 2026 09:53:02 +0000 Subject: [PATCH 02/96] data-access service deployment files --- .github/workflows/publish.yml | 1 + Makefile | 2 ++ acs-data-access/Dockerfile | 32 ++++++++++++++++++++++++++++++++ acs-data-access/Makefile | 7 +++++++ 4 files changed, 42 insertions(+) create mode 100644 acs-data-access/Dockerfile create mode 100644 acs-data-access/Makefile diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 46b708483..0f3043871 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -224,6 +224,7 @@ jobs: - acs-admin - acs-cluster-manager - acs-cmdesc + - acs-data-access - acs-edge-sync - acs-files - acs-git diff --git a/Makefile b/Makefile index d7ce30691..671ff34c0 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,7 @@ subdirs+= acs-auth subdirs+= acs-cluster-manager subdirs+= acs-cmdesc subdirs+= acs-configdb +subdirs+= acs-data-access subdirs+= acs-directory subdirs+= acs-edge subdirs+= acs-edge-sync @@ -67,4 +68,5 @@ subdirs+= historian-sparkplug subdirs+= historian-uns subdirs+= uns-ingester-sparkplug + include ${mk}/acs.subdirs.mk diff --git a/acs-data-access/Dockerfile b/acs-data-access/Dockerfile new file mode 100644 index 000000000..940f69306 --- /dev/null +++ b/acs-data-access/Dockerfile @@ -0,0 +1,32 @@ +ARG base_version +ARG base_prefix=ghcr.io/amrc-factoryplus/acs-base + +FROM ${base_prefix}-js-build:${base_version} AS build +ARG acs_npm=NO + +# Install the node application on the build container where we can +# compile the native modules. +RUN install -d -o node -g node /home/node/app \ + && mkdir /home/node/lib +COPY --from=lib . /home/node/lib/ + +WORKDIR /home/node/app +USER node +COPY package*.json ./ +RUN <<'SHELL' + touch /home/node/.npmrc + npm install --save=false --install-links +SHELL +COPY --chown=node . . +RUN <<'SHELL' + echo "export const GIT_VERSION=\"$revision\";" > ./lib/git-version.js +SHELL + +FROM ${base_prefix}-js-run:${base_version} + +# Copy across from the build container. +WORKDIR /home/node/app +COPY --from=build --chown=root:root /home/node/app ./ + +USER node +CMD node bin/api.js diff --git a/acs-data-access/Makefile b/acs-data-access/Makefile new file mode 100644 index 000000000..2cab50f13 --- /dev/null +++ b/acs-data-access/Makefile @@ -0,0 +1,7 @@ +top=.. +include ${top}/mk/acs.init.mk + +repo?=acs-data-access +k8s.deployment?=data-access + +include ${mk}/acs.js.mk From 673965934f076a10f457b3bb21e8d8b238a7b268 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 23 Mar 2026 09:55:13 +0000 Subject: [PATCH 03/96] js-rx-client - new method to watch acl with permission --- lib/js-rx-client/lib/auth.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/js-rx-client/lib/auth.js b/lib/js-rx-client/lib/auth.js index 93a4cb5ff..843b69270 100644 --- a/lib/js-rx-client/lib/auth.js +++ b/lib/js-rx-client/lib/auth.js @@ -51,7 +51,6 @@ export class Auth extends Interfaces.Auth { ); } - watch_acl_with_perm (principal, permission) { return rxx.rx( this.watch_acl(principal), @@ -62,6 +61,4 @@ export class Auth extends Interfaces.Auth { rx.map(arr => new Set(arr)) ); } - - // watch_acl_with_perm(prin) } From 02ac346a1720da7e78046929bffb4466fe4fd178 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 23 Mar 2026 09:57:28 +0000 Subject: [PATCH 04/96] dataflow start --- acs-data-access/bin/api.js | 4 +- acs-data-access/lib/dataflow.js | 19 +- acs-data-access/package-lock.json | 2353 +++++++++++++++++++++++++++++ acs-data-access/package.json | 1 + 4 files changed, 2370 insertions(+), 7 deletions(-) create mode 100644 acs-data-access/package-lock.json diff --git a/acs-data-access/bin/api.js b/acs-data-access/bin/api.js index 0099bcc25..1944971f0 100644 --- a/acs-data-access/bin/api.js +++ b/acs-data-access/bin/api.js @@ -9,7 +9,7 @@ import { RxClient, UUIDs } from '@amrc-factoryplus/rx-client'; import { WebAPI } from '@amrc-factoryplus/service-api'; import { routes } from '../lib/routes.js'; -import { Version, Service } from '../lib/constants.js'; +// import { Version, Service } from '../lib/constants.js'; import {DataFlow} from '../lib/dataflow.js'; const { env } = process; @@ -20,7 +20,7 @@ const fplus = await new RxClient({ const debug = fplus.debug; -debug.log("app", "Starting acs-data-service revision %s", GIT_VERSION); +debug.log("app", "Starting acs-data-service revision %s"); // the dataflow object is full of sequences that does most of the work of this service. Sequence means an RX observable. const data = new DataFlow({ diff --git a/acs-data-access/lib/dataflow.js b/acs-data-access/lib/dataflow.js index be0adf788..3859e788c 100644 --- a/acs-data-access/lib/dataflow.js +++ b/acs-data-access/lib/dataflow.js @@ -2,16 +2,20 @@ * ACS Data Access Service * Data-Flow / sequence management */ +import rx from 'rxjs'; +import * as rxx from "@amrc-factoryplus/rx-util"; import { RxClient, UUIDs } from '@amrc-factoryplus/rx-client'; + import { DataAccess } from './constants.js'; + + + export class DataFlow { constructor(opts) { const { fplus } = opts; - // this.root = opts.root_principal; // names of the kerberos principal that overrides all ACL checks. comes from the environment. Always set to the admin principal name. Override principal. - this.log = fplus.debug.bound("data"); this.cdb = fplus.ConfigDB; this.auth = fplus.Auth; @@ -29,14 +33,17 @@ export class DataFlow { this.responses = this._build_responses(); this.metadata = this._build_metadata(); + // combine these into map ??? with key = structural dataset class UUID this.sessions = this.cdb.search_app(DataAccess.App.SessionLimits); + this.unions = this.cdb.search_app(DataAccess.App.UnionComponents); + - this.names = this._build_names(); + // this.names = this._build_names(); } _build_metadata(){ - + } update_name(r) { @@ -47,7 +54,7 @@ export class DataFlow { _build_names() { return rxx.rx( - cdb.search_app(UUIDs.App.Info), + this.cdb.search_app(UUIDs.App.Info), // rx.map acts on the sequence of responses, entries.map acts on the entries maps rx.map(entries => entries.map(entry => entry.name)), // Cache the map (1 refers to 1 previous value) @@ -84,6 +91,8 @@ export class DataFlow { */ run() { this.sessions.subscribe(ss => this.log("SESSION UPDATE %o", ss.toJS())); + this.unions.subscribe(ss => this.log("UNION UPDATE %o", ss.toJS())); + } diff --git a/acs-data-access/package-lock.json b/acs-data-access/package-lock.json new file mode 100644 index 000000000..14e066fbc --- /dev/null +++ b/acs-data-access/package-lock.json @@ -0,0 +1,2353 @@ +{ + "name": "acs-data-access", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "acs-data-access", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@amrc-factoryplus/rx-client": "file:../lib/js-rx-client", + "@amrc-factoryplus/rx-util": "file:../lib/js-rx-util", + "@amrc-factoryplus/service-api": "file:../lib/js-service-api", + "@amrc-factoryplus/service-client": "file:../lib/js-service-client", + "@dotenvx/dotenvx": "^1.39.0", + "express": "^4.21.2", + "rxjs": "^7.8.2", + "stream-size": "^0.0.6", + "uuid": "^11.0.5" + }, + "devDependencies": { + "eslint": "^9.23.0" + } + }, + "../lib/js-rx-client": { + "name": "@amrc-factoryplus/rx-client", + "version": "v0.0.1-bmz.1", + "license": "ISC", + "dependencies": { + "@amrc-factoryplus/rx-util": "file:../js-rx-util", + "@amrc-factoryplus/service-client": "file:../js-service-client", + "immutable": "^5.0.0-rc.2", + "rxjs": "^7.8.1", + "uuid": "^11.0.2" + }, + "devDependencies": { + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "^9.14.0", + "eslint": "^9.14.0", + "globals": "^15.12.0", + "jsdoc": "^4.0.4" + } + }, + "../lib/js-rx-util": { + "name": "@amrc-factoryplus/rx-util", + "version": "0.0.3", + "license": "ISC", + "dependencies": { + "immutable": "^5.0.0-beta.5", + "rxjs": "^7.8.1" + }, + "devDependencies": { + "@eslint/js": "^9.18.0", + "eslint": "^9.18.0", + "globals": "^15.14.0" + } + }, + "../lib/js-service-api": { + "name": "@amrc-factoryplus/service-api", + "version": "0.1.0", + "license": "ISC", + "os": [ + "!win32" + ], + "dependencies": { + "@amrc-factoryplus/rx-util": "file:../js-rx-util", + "@amrc-factoryplus/service-client": "file:../js-service-client", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "deep-equal": "^2.2.3", + "express": "^5.0.1", + "gssapi.js": "^2.0.1", + "immutable": "^5.1.3", + "optional-js": "^2.3.0", + "path-to-regexp": "^6.2.1", + "rxjs": "^7.8.1", + "uuid": "^11.0.2", + "ws": "^8.18.0" + }, + "devDependencies": { + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "^9.13.0", + "eslint": "^9.13.0", + "globals": "^15.11.0" + } + }, + "../lib/js-service-client": { + "name": "@amrc-factoryplus/service-client", + "version": "1.6.0", + "license": "ISC", + "dependencies": { + "content-type": "^1.0.5", + "isomorphic-ws": "^5.0.0", + "mqtt": "^5.3.6", + "optional-js": "^2.3.0", + "semver": "^7.6.0", + "sparkplug-payload": "^1.0.3" + }, + "devDependencies": { + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "^9.13.0", + "eslint": "^9.13.0", + "globals": "^15.11.0" + }, + "optionalDependencies": { + "got-fetch": "^5.1.8", + "gssapi.js": "^2.0.1" + } + }, + "node_modules/@amrc-factoryplus/rx-client": { + "resolved": "../lib/js-rx-client", + "link": true + }, + "node_modules/@amrc-factoryplus/rx-util": { + "resolved": "../lib/js-rx-util", + "link": true + }, + "node_modules/@amrc-factoryplus/service-api": { + "resolved": "../lib/js-service-api", + "link": true + }, + "node_modules/@amrc-factoryplus/service-client": { + "resolved": "../lib/js-service-client", + "link": true + }, + "node_modules/@dotenvx/dotenvx": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.57.0.tgz", + "integrity": "sha512-WsTEcqfHzKmLFZh3jLGd7o4iCkrIupp+qFH2FJUJtQXUh2GcOnLXD00DcrhlO4H8QSmaKnW9lugOEbrdpu25kA==", + "license": "BSD-3-Clause", + "dependencies": { + "commander": "^11.1.0", + "dotenv": "^17.2.1", + "eciesjs": "^0.4.10", + "execa": "^5.1.1", + "fdir": "^6.2.0", + "ignore": "^5.3.0", + "object-treeify": "1.1.33", + "picomatch": "^4.0.2", + "which": "^4.0.0" + }, + "bin": { + "dotenvx": "src/cli/dotenvx.js" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/@ecies/ciphers": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@ecies/ciphers/-/ciphers-0.2.5.tgz", + "integrity": "sha512-GalEZH4JgOMHYYcYmVqnFirFsjZHeoGMDt9IxEnM9F7GRUUyUksJ7Ou53L83WHJq3RWKD3AcBpo0iQh0oMpf8A==", + "license": "MIT", + "engines": { + "bun": ">=1", + "deno": ">=2", + "node": ">=16" + }, + "peerDependencies": { + "@noble/ciphers": "^1.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dotenv": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eciesjs": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.18.tgz", + "integrity": "sha512-wG99Zcfcys9fZux7Cft8BAX/YrOJLJSZ3jyYPfhZHqN2E+Ffx+QXBDsv3gubEgPtV6dTzJMSQUwk1H98/t/0wQ==", + "license": "MIT", + "dependencies": { + "@ecies/ciphers": "^0.2.5", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "^1.9.7", + "@noble/hashes": "^1.8.0" + }, + "engines": { + "bun": ">=1", + "deno": ">=2", + "node": ">=16" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-treeify": { + "version": "1.1.33", + "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-1.1.33.tgz", + "integrity": "sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stream-size": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/stream-size/-/stream-size-0.0.6.tgz", + "integrity": "sha512-TZsxxZzKPkiD7fxcKx0Ze9s0+WnGGbX1yWZNoXXmN6YjyEu/62pYkLKo7P627N27BT9kI93ZfZAx38brr/hq+Q==", + "license": "MIT" + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/acs-data-access/package.json b/acs-data-access/package.json index 156bda9ec..f3a98dc04 100644 --- a/acs-data-access/package.json +++ b/acs-data-access/package.json @@ -16,6 +16,7 @@ "@amrc-factoryplus/service-client": "file:../lib/js-service-client", "@dotenvx/dotenvx": "^1.39.0", "express": "^4.21.2", + "rxjs": "^7.8.2", "stream-size": "^0.0.6", "uuid": "^11.0.5" }, From b530f683534268b61d5e4b2f5594387f80a8a5ff Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 23 Mar 2026 09:58:11 +0000 Subject: [PATCH 05/96] gitignore --- acs-data-access/.gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/acs-data-access/.gitignore b/acs-data-access/.gitignore index 13dfa3637..c7b916f01 100644 --- a/acs-data-access/.gitignore +++ b/acs-data-access/.gitignore @@ -1,2 +1,3 @@ .env -node_modules/ \ No newline at end of file +node_modules/ +tmp/ \ No newline at end of file From c904d496464afb67ae0ee8714ccd5e45b46ee4e6 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 23 Mar 2026 10:59:27 +0000 Subject: [PATCH 06/96] acs-config-db package.lock update --- acs-configdb/package-lock.json | 5025 ++++++++++++++++++++++++++++++++ 1 file changed, 5025 insertions(+) create mode 100644 acs-configdb/package-lock.json diff --git a/acs-configdb/package-lock.json b/acs-configdb/package-lock.json new file mode 100644 index 000000000..ebff38034 --- /dev/null +++ b/acs-configdb/package-lock.json @@ -0,0 +1,5025 @@ +{ + "name": "acs-configdb", + "version": "1.8.2", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "acs-configdb", + "version": "1.8.2", + "license": "MIT", + "dependencies": { + "@amrc-factoryplus/pg-client": "file:../lib/js-pg-client", + "@amrc-factoryplus/rx-client": "file:../lib/js-rx-client", + "@amrc-factoryplus/rx-util": "file:../lib/js-rx-util", + "@amrc-factoryplus/service-api": "file:../lib/js-service-api", + "@amrc-factoryplus/service-client": "file:../lib/js-service-client", + "ajv": "^8.10.0", + "ajv-formats": "^2.1.1", + "deep-equal": "^2.2.3", + "express-openapi-validator": "^4.13.2", + "json-merge-patch": "^1.0.2", + "rxjs": "^7.8.1" + }, + "devDependencies": { + "@eslint/js": "^9.13.0", + "eslint": "^9.13.0", + "globals": "^15.11.0", + "yaml": "^2.7.0" + } + }, + "node_modules/@amrc-factoryplus/pg-client": { + "version": "0.0.1", + "resolved": "file:../lib/js-pg-client", + "license": "ISC", + "dependencies": { + "pg": "^8.13.1", + "pg-native": "^3.2.0" + } + }, + "node_modules/@amrc-factoryplus/rx-client": { + "version": "v0.0.1-bmz.1", + "resolved": "file:../lib/js-rx-client", + "license": "ISC", + "dependencies": { + "@amrc-factoryplus/rx-util": "file:../js-rx-util", + "@amrc-factoryplus/service-client": "file:../js-service-client", + "immutable": "^5.0.0-rc.2", + "rxjs": "^7.8.1", + "uuid": "^11.0.2" + } + }, + "node_modules/@amrc-factoryplus/rx-util": { + "version": "0.0.3", + "resolved": "file:../lib/js-rx-util", + "license": "ISC", + "dependencies": { + "immutable": "^5.0.0-beta.5", + "rxjs": "^7.8.1" + } + }, + "node_modules/@amrc-factoryplus/service-api": { + "version": "0.1.0", + "resolved": "file:../lib/js-service-api", + "license": "ISC", + "os": [ + "!win32" + ], + "dependencies": { + "@amrc-factoryplus/rx-util": "file:../js-rx-util", + "@amrc-factoryplus/service-client": "file:../js-service-client", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "deep-equal": "^2.2.3", + "express": "^5.0.1", + "gssapi.js": "^2.0.1", + "immutable": "^5.1.3", + "optional-js": "^2.3.0", + "path-to-regexp": "^6.2.1", + "rxjs": "^7.8.1", + "uuid": "^11.0.2", + "ws": "^8.18.0" + } + }, + "node_modules/@amrc-factoryplus/service-client": { + "version": "1.6.0", + "resolved": "file:../lib/js-service-client", + "license": "ISC", + "dependencies": { + "content-type": "^1.0.5", + "isomorphic-ws": "^5.0.0", + "mqtt": "^5.3.6", + "optional-js": "^2.3.0", + "semver": "^7.6.0", + "sparkplug-payload": "^1.0.3" + }, + "optionalDependencies": { + "got-fetch": "^5.1.8", + "gssapi.js": "^2.0.1" + } + }, + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz", + "integrity": "sha512-GBD2Le9w2+lVFoc4vswGI/TjkNIZSVp7+9xPf+X3uidBfWnAeUWmquteSyt0+VCrhNMWj/FTABISQrD3Z/YA+w==", + "license": "MIT", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.6", + "call-me-maybe": "^1.0.1", + "js-yaml": "^4.1.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", + "license": "MIT" + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, + "node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "license": "MIT" + }, + "node_modules/@types/multer": { + "version": "1.4.13", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.13.tgz", + "integrity": "sha512-bhhdtPw7JqCiEfC9Jimx5LqX9BDIPJEh2q/fQ4bqbBPtyEZYr3cvF22NwG0DmPZNYA0CAf2CnqDB4KIGGpJcaw==", + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/node": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", + "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "license": "MIT" + }, + "node_modules/@types/readable-stream": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.23.tgz", + "integrity": "sha512-wwXrtQvbMHxCbBgjHaMGEmImFTQxxpfMOR/ZoQnXxB1woqkUbdLGFDgauo00Py9IudiaqSeiBiulSV9i6XIPig==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz", + "integrity": "sha512-iFY7JCgHbepc0b82yLaw4IMortylNb6wG4kL+4R0C3iv6i+RHGHux/yUX5BTiRvSX/shMnngjR1YyNMnXEFh5A==", + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/are-we-there-yet": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.0.6.tgz", + "integrity": "sha512-Zfw6bteqM9gQXZ1BIWOgM8xEwMrUGoyL8nW13+O+OOgNX3YhuDN1GDgg1NzdTlmm3j+9sHy7uBZ12r+z9lXnZQ==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.0 || ^1.1.13" + } + }, + "node_modules/are-we-there-yet/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/are-we-there-yet/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/are-we-there-yet/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "license": "MIT", + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.1.6.tgz", + "integrity": "sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg==", + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^4.2.0" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/bl/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/broker-factory": { + "version": "3.1.14", + "resolved": "https://registry.npmjs.org/broker-factory/-/broker-factory-3.1.14.tgz", + "integrity": "sha512-L45k5HMbPIrMid0nTOZ/UPXG/c0aRuQKVrSDFIb1zOkvfiyHgYmIjc3cSiN1KwQIvRDOtKE0tfb3I9EZ3CmpQQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "fast-unique-numbers": "^9.0.27", + "tslib": "^2.8.1", + "worker-factory": "^7.0.49" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffer-shims": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha512-Zy8ZXMyxIT6RMTeY7OP/bDndfj6bwCan7SS98CEndS6deHwWPpseeHlwarNcBim+etXnF9HBc1non5JgDaJU1g==", + "license": "MIT" + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "engines": { + "node": ">=0.2.0" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", + "license": "MIT" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "license": "MIT/X11", + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==", + "license": "ISC", + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/cmake-js": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-6.3.2.tgz", + "integrity": "sha512-7MfiQ/ijzeE2kO+WFB9bv4QP5Dn2yVaAP2acFJr4NIFy2hT4w6O4EpOTLNcohR5IPX7M4wNf/5taIqMj7UA9ug==", + "license": "MIT", + "dependencies": { + "axios": "^0.21.1", + "bluebird": "^3", + "debug": "^4", + "fs-extra": "^5.0.0", + "is-iojs": "^1.0.1", + "lodash": "^4", + "memory-stream": "0", + "npmlog": "^1.2.0", + "rc": "^1.2.7", + "semver": "^5.0.3", + "splitargs": "0", + "tar": "^4", + "unzipper": "^0.8.13", + "url-join": "0", + "which": "^1.0.9", + "yargs": "^3.6.0" + }, + "bin": { + "cmake-js": "bin/cmake-js" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/cmake-js/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/cmake-js/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commist": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", + "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==", + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-equal": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/duplexer2/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexer2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-openapi-validator": { + "version": "4.13.9", + "resolved": "https://registry.npmjs.org/express-openapi-validator/-/express-openapi-validator-4.13.9.tgz", + "integrity": "sha512-8wByqPqy/J2f1Fj6UcRhVCxoofPPqmJi45zQ81tGecxsIFt/a9RN35/vD1zOlRxGH90P8ijOIzGcOjC7QzY/3g==", + "license": "MIT", + "dependencies": { + "@types/multer": "^1.4.7", + "ajv": "^6.12.6", + "content-type": "^1.0.4", + "json-schema-ref-parser": "^9.0.9", + "lodash.clonedeep": "^4.5.0", + "lodash.get": "^4.4.2", + "lodash.uniq": "^4.5.0", + "lodash.zipobject": "^4.1.3", + "media-typer": "^1.1.0", + "multer": "^2.0.1", + "ono": "^7.1.3", + "path-to-regexp": "^6.2.0" + } + }, + "node_modules/express-openapi-validator/node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/express-openapi-validator/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-unique-numbers": { + "version": "9.0.27", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-9.0.27.tgz", + "integrity": "sha512-nDA9ADeINN8SA2u2wCtU+siWFTTDqQR37XvgPIDDmboWQeExz7X0mImxuaN+kJddliIqy2FpVRmnvRZ+j8i1/A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18.2.0" + } + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 14.17" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-extra": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", + "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "node_modules/fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "license": "ISC", + "dependencies": { + "minipass": "^2.6.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz", + "integrity": "sha512-fVbU2wRE91yDvKUnrIaQlHKAWKY5e08PmztCrwuH5YVQ+Z/p3d0ny2T48o6uvAAXHIUnfaQdHkmxYbQft1eHVA==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "ansi": "^0.3.0", + "has-unicode": "^2.0.0", + "lodash.pad": "^4.1.0", + "lodash.padend": "^4.1.0", + "lodash.padstart": "^4.1.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/got-fetch": { + "version": "5.1.10", + "resolved": "https://registry.npmjs.org/got-fetch/-/got-fetch-5.1.10.tgz", + "integrity": "sha512-Gwj/A2htjvLEcY07PKDItv0WCPEs3dV2vWeZ+9TVBSKSTuWEZ4oXaMD0ZAOsajwx2orahQWN4HI0MfRyWSZsbg==", + "deprecated": "please use built-in fetch in nodejs", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "got": "^12.0.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/gssapi.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/gssapi.js/-/gssapi.js-2.0.1.tgz", + "integrity": "sha512-5D+qylV8uIKMaxTTKhpzvfGRz5+okr/rpwC6YlaCoDVBpvkuNgL8pM7+r7aTzZ2oT5dA4w2WoZTV82opGj+RZQ==", + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "bindings": "^1.5.0", + "cmake-js": "^6.1.0", + "node-addon-api": "^1.7.2" + } + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC" + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", + "license": "MIT" + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause", + "optional": true, + "peer": true + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", + "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "license": "MIT", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-iojs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-iojs/-/is-iojs-1.1.0.tgz", + "integrity": "sha512-tLn1j3wYSL6DkvEI+V/j0pKohpa5jk+ER74v6S4SgCXnjS0WA+DoZbwZBrrhgwksMvtuwndyGeG5F8YMsoBzSA==", + "license": "MIT" + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/json-merge-patch": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-merge-patch/-/json-merge-patch-1.0.2.tgz", + "integrity": "sha512-M6Vp2GN9L7cfuMXiWOmHj9bEFbeC250iVtcKQbqVgEsDVYnIsrNsbU+h/Y/PkbBQCtEa4Bez+Ebv0zfbC8ObLg==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + } + }, + "node_modules/json-schema-ref-parser": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz", + "integrity": "sha512-qcP2lmGy+JUoQJ4DOQeLaZDqH9qSkeGCK3suKWxJXS82dg728Mn3j97azDMaOUmJAN4uCq91LdPx4K7E8F1a7Q==", + "deprecated": "Please switch to @apidevtools/json-schema-ref-parser", + "license": "MIT", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "9.0.9" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", + "license": "MIT", + "dependencies": { + "invert-kv": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/libpq": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/libpq/-/libpq-1.10.0.tgz", + "integrity": "sha512-PHY+JGD3+9X5b2emXLh+WJEnz1jhczO1xs25ZH0xbMWvQi+Hd9X/mTZOrGA99Rcw/DvNjsBRlegroqigpNfaJA==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "1.5.0", + "nan": "~2.23.1" + } + }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "license": "ISC" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "license": "MIT" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.pad": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz", + "integrity": "sha512-mvUHifnLqM+03YNzeTBS1/Gr6JRFjd3rRx88FHWUvamVaT9k2O/kXha3yBSOwB9/DTQrSTLJNHvLBBt2FdX7Mg==", + "license": "MIT" + }, + "node_modules/lodash.padend": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", + "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==", + "license": "MIT" + }, + "node_modules/lodash.padstart": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", + "integrity": "sha512-sW73O6S8+Tg66eY56DBk85aQzzUJDtpoXFBgELMd5P/SotAguo+1kYO6RuYgXxA4HJH3LFTFPASX6ET6bjfriw==", + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "license": "MIT" + }, + "node_modules/lodash.zipobject": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/lodash.zipobject/-/lodash.zipobject-4.1.3.tgz", + "integrity": "sha512-A9SzX4hMKWS25MyalwcOnNoplyHbkNVsjidhTp8ru0Sj23wY9GWBKS8gAIGDSAqeWjIjvE4KBEl24XXAs+v4wQ==", + "license": "MIT" + }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "license": "Apache-2.0" + }, + "node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/memory-stream": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/memory-stream/-/memory-stream-0.0.3.tgz", + "integrity": "sha512-q0D3m846qY6ZkIt+19ZemU5vH56lpOZZwoJc3AICARKh/menBuayQUjAGPrqtHQQMUYERSdOrej92J9kz7LgYA==", + "license": "MMIT", + "dependencies": { + "readable-stream": "~1.0.26-2" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "license": "ISC", + "dependencies": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "node_modules/minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "license": "MIT", + "dependencies": { + "minipass": "^2.9.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mqtt": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.15.0.tgz", + "integrity": "sha512-KC+wAssYk83Qu5bT8YDzDYgUJxPhbLeVsDvpY2QvL28PnXYJzC2WkKruyMUgBAZaQ7h9lo9k2g4neRNUUxzgMw==", + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.21", + "@types/ws": "^8.18.1", + "commist": "^3.2.0", + "concat-stream": "^2.0.0", + "debug": "^4.4.1", + "help-me": "^5.0.0", + "lru-cache": "^10.4.3", + "minimist": "^1.2.8", + "mqtt-packet": "^9.0.2", + "number-allocator": "^1.0.14", + "readable-stream": "^4.7.0", + "rfdc": "^1.4.1", + "socks": "^2.8.6", + "split2": "^4.2.0", + "worker-timers": "^8.0.23", + "ws": "^8.18.3" + }, + "bin": { + "mqtt": "build/bin/mqtt.js", + "mqtt_pub": "build/bin/pub.js", + "mqtt_sub": "build/bin/sub.js" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/mqtt-packet": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.2.tgz", + "integrity": "sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA==", + "license": "MIT", + "dependencies": { + "bl": "^6.0.8", + "debug": "^4.3.4", + "process-nextick-args": "^2.0.1" + } + }, + "node_modules/mqtt/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/mqtt/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/multer": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.1.1.tgz", + "integrity": "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "type-is": "^1.6.18" + }, + "engines": { + "node": ">= 10.16.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/multer/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nan": { + "version": "2.23.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.1.tgz", + "integrity": "sha512-r7bBUGKzlqk8oPBDYxt6Z0aEdF1G1rwlMcLk8LCOMbOzf0mG+JUfUzG4fIMWwHWP0iyaLWEQZJmtB7nOHEm/qw==", + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "license": "MIT" + }, + "node_modules/normalize-url": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.1.1.tgz", + "integrity": "sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npmlog": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-1.2.1.tgz", + "integrity": "sha512-1J5KqSRvESP6XbjPaXt2H6qDzgizLTM7x0y1cXIjP2PpvdCqyNC7TO3cPRKsuYlElbi/DwkzRRdG2zpmE0IktQ==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "ansi": "~0.3.0", + "are-we-there-yet": "~1.0.0", + "gauge": "~1.2.0" + } + }, + "node_modules/number-allocator": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", + "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.1", + "js-sdsl": "4.3.0" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-9jnfVriq7uJM4o5ganUY54ntUm+5EK21EGaQ5NWnkWg3zz5ywbbonlBguRcnmF1/HDiIe3zxNxXcO1YPBmPcQQ==", + "license": "MIT", + "dependencies": { + "@jsdevtools/ono": "7.1.3" + } + }, + "node_modules/optional-js": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/optional-js/-/optional-js-2.3.0.tgz", + "integrity": "sha512-B0LLi+Vg+eko++0z/b8zIv57kp7HKEzaPJo7LowJXMUKYdf+3XJGu/cw03h/JhIOsLnP+cG5QnTHAuicjA5fMw==", + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==", + "license": "MIT", + "dependencies": { + "lcid": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=12.20" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "license": "MIT" + }, + "node_modules/pg": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz", + "integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.12.0", + "pg-pool": "^3.13.0", + "pg-protocol": "^1.13.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.3.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz", + "integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.12.0.tgz", + "integrity": "sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-native": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/pg-native/-/pg-native-3.7.0.tgz", + "integrity": "sha512-q2V5DynvPt4PD75q1DqZOUrieEgE4bf/flEeLCzzs8axgn8x2mRCUhd1DP0cqMz8FEdpVEDb0/zKblQWeijbGg==", + "license": "MIT", + "dependencies": { + "libpq": "^1.8.15", + "pg-types": "2.2.0" + } + }, + "node_modules/pg-pool": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.13.0.tgz", + "integrity": "sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.13.0.tgz", + "integrity": "sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/protobufjs": { + "version": "6.11.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz", + "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": ">=13.7.0", + "long": "^4.0.0" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "license": "MIT" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/router/node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/sparkplug-payload": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sparkplug-payload/-/sparkplug-payload-1.0.3.tgz", + "integrity": "sha512-JAQSyuHVQQe/LzIlJdcIaD1F1c+rbXoolII3H1whQkhuZ96+G53RoPWqP9zCPZYFjvfSMHnQNIedGIZw/zjHJw==", + "license": "EPL-2.0", + "dependencies": { + "@types/long": "^4.0.0", + "long": "^4.0.0", + "protobufjs": "^6.11.3" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/splitargs": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/splitargs/-/splitargs-0.0.7.tgz", + "integrity": "sha512-UUFYD2oWbNwULH6WoVtLUOw8ch586B+HUqcsAjjjeoBQAM1bD4wZRXu01koaxyd8UeYpybWqW4h+lO1Okv40Tg==", + "license": "ISC" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "license": "MIT" + }, + "node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "license": "MIT", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "4.4.19", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", + "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", + "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "chownr": "^1.1.4", + "fs-minipass": "^1.2.7", + "minipass": "^2.9.0", + "minizlib": "^1.3.3", + "mkdirp": "^0.5.5", + "safe-buffer": "^5.2.1", + "yallist": "^3.1.1" + }, + "engines": { + "node": ">=4.5" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "license": "MIT/X11", + "engines": { + "node": "*" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "license": "MIT" + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unzipper": { + "version": "0.8.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.8.14.tgz", + "integrity": "sha512-8rFtE7EP5ssOwGpN2dt1Q4njl0N1hUXJ7sSPz0leU2hRdq6+pra57z4YPBlVqm40vcgv6ooKZEAx48fMTv9x4w==", + "license": "MIT", + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "~1.0.10", + "listenercount": "~1.0.1", + "readable-stream": "~2.1.5", + "setimmediate": "~1.0.4" + } + }, + "node_modules/unzipper/node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "license": "MIT" + }, + "node_modules/unzipper/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/unzipper/node_modules/process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha512-yN0WQmuCX63LP/TMvAg31nvT6m4vDqJEiiv2CAZqWOGNWutc9DfDk1NPYYmKUFmaVM2UwDowH4u5AHWYP/jxKw==", + "license": "MIT" + }, + "node_modules/unzipper/node_modules/readable-stream": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz", + "integrity": "sha512-NkXT2AER7VKXeXtJNSaWLpWIhmtSE3K2PguaLEeWr4JILghcIKqoLt1A3wHrnpDC5+ekf8gfk1GKWkFXe4odMw==", + "license": "MIT", + "dependencies": { + "buffer-shims": "^1.0.0", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-join": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-0.0.1.tgz", + "integrity": "sha512-H6dnQ/yPAAVzMQRvEvyz01hhfQL5qRWSEt7BX8t9DqnPw9BjMb64fjIRq76Uvf1hkHp+mTZvEVJ5guXOT0Xqaw==", + "license": "MIT" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/window-size": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", + "integrity": "sha512-2thx4pB0cV3h+Bw7QmMXcEbdmOzv9t0HFplJH/Lz6yu60hXYy5RT8rUu+wlIreVxWsGN20mo+MHeCSfUpQBwPw==", + "license": "MIT", + "bin": { + "window-size": "cli.js" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/worker-factory": { + "version": "7.0.49", + "resolved": "https://registry.npmjs.org/worker-factory/-/worker-factory-7.0.49.tgz", + "integrity": "sha512-lW7tpgy6aUv2dFsQhv1yv+XFzdkCf/leoKRTGMPVK5/die6RrUjqgJHJf556qO+ZfytNG6wPXc17E8zzsOLUDw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "fast-unique-numbers": "^9.0.27", + "tslib": "^2.8.1" + } + }, + "node_modules/worker-timers": { + "version": "8.0.31", + "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-8.0.31.tgz", + "integrity": "sha512-ngkq5S6JuZyztom8tDgBzorLo9byhBMko/sXfgiUD945AuzKGg1GCgDMCC3NaYkicLpGKXutONM36wEX8UbBCA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "tslib": "^2.8.1", + "worker-timers-broker": "^8.0.16", + "worker-timers-worker": "^9.0.14" + } + }, + "node_modules/worker-timers-broker": { + "version": "8.0.16", + "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-8.0.16.tgz", + "integrity": "sha512-JyP3AvUGyPGbBGW7XiUewm2+0pN/aYo1QpVf5kdXAfkDZcN3p7NbWrG6XnyDEpDIvfHk/+LCnOW/NsuiU9riYA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "broker-factory": "^3.1.14", + "fast-unique-numbers": "^9.0.27", + "tslib": "^2.8.1", + "worker-timers-worker": "^9.0.14" + } + }, + "node_modules/worker-timers-worker": { + "version": "9.0.14", + "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-9.0.14.tgz", + "integrity": "sha512-/qF06C60sXmSLfUl7WglvrDIbspmPOM8UrG63Dnn4bi2x4/DfqHS/+dxF5B+MdHnYO5tVuZYLHdAodrKdabTIg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "tslib": "^2.8.1", + "worker-factory": "^7.0.49" + } + }, + "node_modules/wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", + "license": "MIT", + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "license": "ISC" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", + "integrity": "sha512-ONJZiimStfZzhKamYvR/xvmgW3uEkAUFSP91y2caTEPhzF6uP2JfPiVZcq66b/YR0C3uitxSV7+T1x8p5bkmMg==", + "license": "MIT", + "dependencies": { + "camelcase": "^2.0.1", + "cliui": "^3.0.3", + "decamelize": "^1.1.1", + "os-locale": "^1.4.0", + "string-width": "^1.0.1", + "window-size": "^0.1.4", + "y18n": "^3.2.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} From 248967f829cb6874081a31686910ef0b3b829e04 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 23 Mar 2026 11:31:20 +0000 Subject: [PATCH 07/96] data access helm --- deploy/templates/data-access/data-access.yaml | 75 +++++++++++++++++++ deploy/templates/data-access/ingress.yaml | 23 ++++++ 2 files changed, 98 insertions(+) create mode 100644 deploy/templates/data-access/data-access.yaml create mode 100644 deploy/templates/data-access/ingress.yaml diff --git a/deploy/templates/data-access/data-access.yaml b/deploy/templates/data-access/data-access.yaml new file mode 100644 index 000000000..2115b0880 --- /dev/null +++ b/deploy/templates/data-access/data-access.yaml @@ -0,0 +1,75 @@ +{{- if .Values.data-access.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: data-access + namespace: {{ .Release.Namespace }} + labels: + component: data-access +spec: + replicas: 1 + selector: + matchLabels: + component: data-access + template: + metadata: + labels: + component: data-access + factory-plus.service: data-access + spec: + {{- with .Values.acs.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: krb5-conf + configMap: + name: krb5-conf + - name: krb5-keytabs + secret: + secretName: data-access-keytabs + + containers: + - name: data-access +{{ include "amrc-connectivity-stack.image" (list . .Values.data-access) | indent 10 }} + command: [ "/usr/bin/k5start", "-Uf", "/keytabs/client" ] + args: [ "node", "bin/api.js" ] + env: + - name: KRB5_CONFIG + value: /config/krb5-conf/krb5.conf + - name: CLIENT_KEYTAB + value: /keytabs/client + - name: SERVER_KEYTAB + value: /keytabs/server + - name: HOSTNAME + value: data-access.{{ .Release.Namespace }}.svc.cluster.local + - name: REALM + value: {{ .Values.identity.realm | required "values.identity.realm is required!" }} + - name: ROOT_PRINCIPAL + value: admin@{{ .Values.identity.realm | required "values.identity.realm is required!" }} + - name: VERBOSE + value: {{.Values.data-access.verbosity | quote | required "values.data-access.verbosity is required!"}} + - name: PORT + value: "8080" +{{ include "amrc-connectivity-stack.cache-max-age" (list . "data-access") | indent 12 }} + - name: DIRECTORY_URL + value: http://directory.{{ .Release.Namespace }}.svc.cluster.local + volumeMounts: + - mountPath: /config/krb5-conf + name: krb5-conf + - mountPath: /keytabs + name: krb5-keytabs +--- +apiVersion: v1 +kind: Service +metadata: + name: data-access + namespace: {{ .Release.Namespace }} +spec: + ports: + - name: "web" + port: 80 + targetPort: 8080 + selector: + factory-plus.service: data-access +{{- end -}} diff --git a/deploy/templates/data-access/ingress.yaml b/deploy/templates/data-access/ingress.yaml new file mode 100644 index 000000000..3ee71d1fa --- /dev/null +++ b/deploy/templates/data-access/ingress.yaml @@ -0,0 +1,23 @@ +{{ if .Values.data-access.enabled }} +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: data-access-ingressroute + namespace: {{ .Release.Namespace }} +spec: + entryPoints: + - {{ .Values.acs.secure | ternary "websecure" "web" }} + routes: + - match: Host(`data-access.{{.Values.acs.baseUrl | required "values.acs.baseUrl is required"}}`) + kind: Rule + services: + - name: data-access + port: 80 + namespace: {{ .Release.Namespace }} + {{- if .Values.acs.secure }} + tls: + secretName: {{ coalesce .Values.data-access.tlsSecretName .Values.acs.tlsSecretName }} + domains: + - main: data-access.{{.Values.acs.baseUrl | required "values.acs.baseUrl is required"}} + {{- end -}} +{{- end -}} From 440488500a29f7885f5273399a616272ca7d49c8 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 23 Mar 2026 12:04:03 +0000 Subject: [PATCH 08/96] node modules dockerignored --- acs-data-access/.dockerignore | 1 + acs-opcua-server-edge/.dockerignore | 1 + 2 files changed, 2 insertions(+) create mode 100644 acs-data-access/.dockerignore create mode 100644 acs-opcua-server-edge/.dockerignore diff --git a/acs-data-access/.dockerignore b/acs-data-access/.dockerignore new file mode 100644 index 000000000..40b878db5 --- /dev/null +++ b/acs-data-access/.dockerignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/acs-opcua-server-edge/.dockerignore b/acs-opcua-server-edge/.dockerignore new file mode 100644 index 000000000..40b878db5 --- /dev/null +++ b/acs-opcua-server-edge/.dockerignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file From 48dc1f1fa0084d6d35d65d7ecd3fae8063af1b6d Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 23 Mar 2026 12:42:50 +0000 Subject: [PATCH 09/96] deploy data access --- deploy/templates/data-access/data-access.yaml | 8 ++++---- deploy/templates/data-access/ingress.yaml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/deploy/templates/data-access/data-access.yaml b/deploy/templates/data-access/data-access.yaml index 2115b0880..aad11efd8 100644 --- a/deploy/templates/data-access/data-access.yaml +++ b/deploy/templates/data-access/data-access.yaml @@ -1,4 +1,4 @@ -{{- if .Values.data-access.enabled }} +{{- if .Values.dataAccess.enabled }} apiVersion: apps/v1 kind: Deployment metadata: @@ -31,7 +31,7 @@ spec: containers: - name: data-access -{{ include "amrc-connectivity-stack.image" (list . .Values.data-access) | indent 10 }} +{{ include "amrc-connectivity-stack.image" (list . .Values.dataAccess) | indent 10 }} command: [ "/usr/bin/k5start", "-Uf", "/keytabs/client" ] args: [ "node", "bin/api.js" ] env: @@ -48,10 +48,10 @@ spec: - name: ROOT_PRINCIPAL value: admin@{{ .Values.identity.realm | required "values.identity.realm is required!" }} - name: VERBOSE - value: {{.Values.data-access.verbosity | quote | required "values.data-access.verbosity is required!"}} + value: {{.Values.dataAccess.verbosity | quote | required "values.dataAccess.verbosity is required!"}} - name: PORT value: "8080" -{{ include "amrc-connectivity-stack.cache-max-age" (list . "data-access") | indent 12 }} +{{ include "amrc-connectivity-stack.cache-max-age" (list . "dataAccess") | indent 12 }} - name: DIRECTORY_URL value: http://directory.{{ .Release.Namespace }}.svc.cluster.local volumeMounts: diff --git a/deploy/templates/data-access/ingress.yaml b/deploy/templates/data-access/ingress.yaml index 3ee71d1fa..f381287cb 100644 --- a/deploy/templates/data-access/ingress.yaml +++ b/deploy/templates/data-access/ingress.yaml @@ -1,4 +1,4 @@ -{{ if .Values.data-access.enabled }} +{{ if .Values.dataAccess.enabled }} apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: @@ -16,7 +16,7 @@ spec: namespace: {{ .Release.Namespace }} {{- if .Values.acs.secure }} tls: - secretName: {{ coalesce .Values.data-access.tlsSecretName .Values.acs.tlsSecretName }} + secretName: {{ coalesce .Values.dataAccess.tlsSecretName .Values.acs.tlsSecretName }} domains: - main: data-access.{{.Values.acs.baseUrl | required "values.acs.baseUrl is required"}} {{- end -}} From ea1c9dd902ee9031db535caa694502af6d2849ce Mon Sep 17 00:00:00 2001 From: amrc-za Date: Tue, 24 Mar 2026 08:38:40 +0000 Subject: [PATCH 10/96] data access fixed keytabs/server not found --- deploy/templates/auth/principals/services.yaml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/deploy/templates/auth/principals/services.yaml b/deploy/templates/auth/principals/services.yaml index d26914260..c6089606c 100644 --- a/deploy/templates/auth/principals/services.yaml +++ b/deploy/templates/auth/principals/services.yaml @@ -113,18 +113,20 @@ spec: - HTTP/git.{{.Values.acs.baseUrl | required "values.acs.baseUrl is required"}}@{{ .Values.identity.realm | required "values.identity.realm is required!" }} secret: git-keytabs/server {{- end }} ---- -{{- if .Values.openid.enabled }} + + +{{- if .Values.dataAccess.enabled }} apiVersion: factoryplus.app.amrc.co.uk/v1 kind: KerberosKey metadata: - name: http.openid + name: http.data-access namespace: {{ .Release.Namespace }} spec: type: Random - principal: HTTP/openid.{{ .Release.Namespace }}.svc.cluster.local@{{ .Values.identity.realm | required "values.identity.realm is required!" }} + principal: HTTP/data-access.{{ .Release.Namespace }}.svc.cluster.local@{{ .Values.identity.realm | required "values.identity.realm is required!" }} + secret: data-access-keytabs/server additionalPrincipals: - - HTTP/openid.{{.Values.acs.baseUrl | required "values.acs.baseUrl is required"}}@{{ .Values.identity.realm | required "values.identity.realm is required!" }} + - HTTP/data-access.{{.Values.acs.baseUrl | required "values.acs.baseUrl is required"}}@{{ .Values.identity.realm | required "values.identity.realm is required!" }} {{- end }} --- {{- if .Values.dataAccess.enabled }} From c25f2b34218099d5215f76b1ece049462ac91c89 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 25 Mar 2026 12:15:30 +0000 Subject: [PATCH 11/96] data access - dataflow build metadata and code clean up --- acs-data-access/bin/api.js | 63 ++++++++++------ acs-data-access/lib/api-v1.js | 53 ++++++++------ acs-data-access/lib/dataflow.js | 126 +++++++++----------------------- acs-data-access/lib/notify.js | 54 +++++++------- acs-data-access/lib/routes.js | 15 ---- 5 files changed, 134 insertions(+), 177 deletions(-) delete mode 100644 acs-data-access/lib/routes.js diff --git a/acs-data-access/bin/api.js b/acs-data-access/bin/api.js index 1944971f0..867b7d49a 100644 --- a/acs-data-access/bin/api.js +++ b/acs-data-access/bin/api.js @@ -8,9 +8,9 @@ import { RxClient, UUIDs } from '@amrc-factoryplus/rx-client'; import { WebAPI } from '@amrc-factoryplus/service-api'; -import { routes } from '../lib/routes.js'; -// import { Version, Service } from '../lib/constants.js'; import {DataFlow} from '../lib/dataflow.js'; +import { APIv1 } from '../lib/api-v1.js'; +import {DataAccessNotify} from '../lib/notify.js'; const { env } = process; @@ -18,35 +18,52 @@ const fplus = await new RxClient({ env, }).init(); +const Version = "2.0.0"; const debug = fplus.debug; debug.log("app", "Starting acs-data-service revision %s"); + // the dataflow object is full of sequences that does most of the work of this service. Sequence means an RX observable. const data = new DataFlow({ fplus }); -// const api = await new WebAPI({ -// ping: { -// version: Version, -// service: UUIDs.Service.DataAccess, -// software: { -// vendor: 'AMRC', -// application: 'acs-data-access', -// }, -// }, -// debug: fplus.debug, -// realm: env.REALM, -// hostname: env.HOSTNAME, -// keytab: env.SERVER_KEYTAB, -// http_port: env.PORT, -// max_age: env.CACHE_MAX_AGE, -// routes: routes({ -// fplus, -// }), -// }).init(); - -// api.run(); +const apiv1 = new APIv1({ data, debug }); + +const api = await new WebAPI({ + debug: fplus.debug, + ping: { + version: Version, + service: UUIDs.Service.DataAccess, + software: { + vendor: 'AMRC', + application: 'acs-data-access', + }, + }, + realm: env.REALM, + hostname: env.HOSTNAME, + keytab: env.SERVER_KEYTAB, + http_port: env.PORT, + max_age: env.CACHE_MAX_AGE, + routes: app => { + app.use("/v1", apiv1.routes); + } +}).init(); + + +const notify = new DataAccessNotify({ + api, data, debug +}); + +debug.log("app", "Running Data Access DataFlow"); data.run(); + +// debug.log("app", "Running Data Access Notify"); +// notify.run(); + +debug.log("app", "Running Data Access WebAPI") +api.run(); + + diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 333b40a67..68ef25740 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -3,8 +3,12 @@ * APIV1 */ +import express from "express"; +import * as rx from "rxjs"; import { APIError } from "@amrc-factoryplus/service-api"; +import { DataAccess } from "./constants.js"; +import { valid_grant, valid_krb, valid_uuid } from "./validate.js"; function fail(status, message) { throw new APIError(status); @@ -13,9 +17,9 @@ function fail(status, message) { export class APIv1 { constructor(opts) { this.data = opts.data; - this.fplus = opts.fplus; - this.auth = this.fplus.Auth; - this.log = this.fplus.debug.bound("api-v1"); + + this.log = opts.debug.bound("apiv1"); + this.routes = this.setup_routes(); } @@ -23,15 +27,25 @@ export class APIv1 { let api = express.Router(); api.route("/metadata") - .get(this.metadata_get_list.bind(this)); + .get(this.metadata_list.bind(this)); api.route("/metadata/:uuid") - .get(this.metadata_get.bind(this)); - + .get(this.metadata_uuid.bind(this)); + api.route("/data/:uuid") - .post(this.data_get.bind(this)); + .post(this.dataset_data.bind(this)); + + api.route("/structure") + .get(this.structure_list.bind(this)) + .post(this.structure_create.bind(this)); + + api.route("/structure/:uuid") + .get(this.structure_uuid.bind(this)) + .put(this.structure_update.bind(this)) + + return api; } @@ -42,24 +56,16 @@ export class APIv1 { * @param from {date} (optional, inclusive) query param * @param to {date} (optional, inclusive) query param */ - async metadata_get_list(req, res){ + async metadata_list(req, res){ const principal = req.auth; - const {from, to} = req.query; - - // 1. If from and/or to provided - check their format - // 2. Check the client is authenticated - // 3. Retrieve the list of metadata UUIDs from ConfigDB and return - - const response = await this.auth.fetch_auth_acl(principal_type, principal); + // const response = await this.data.find_metadata(principal); + return res.status(200).json(principal); } - - - /** GET. Accepts Dataset UUID and returns metadata about a Published dataset. * @param uuid {request param} * @param @@ -72,7 +78,7 @@ export class APIv1 { * metadata {object} - Map of metadata configs keyed by Application UUID - contains all config entries for dataset which use applications in the Dataset metadata class * parts {array} - Subset datasets - contains UUIDs of all subclasses of dataset the client has READ access to. */ - async metadata_get(req, res){ + async metadata_uuid(req, res){ const {uuid} = req.params @@ -91,7 +97,7 @@ export class APIv1 { * value - actual data value * unit - Engineering unit (if available) */ - async data_get(req, res){ + async dataset_data(req, res){ } @@ -101,7 +107,7 @@ export class APIv1 { * @param {*} res * @returns list of dataset UUIDs the client has permission to EDIT */ - async structure_get_list(req, res){ + async structure_list(req, res){ } @@ -116,7 +122,7 @@ export class APIv1 { * class {UUID} - Structural class: Sparkplug device, Union Dataset, Session * config {any} - Structural definition: "not visible", Union components, Session limits */ - async structure_get(req, res){ + async structure_uuid(req, res){ } @@ -140,6 +146,9 @@ export class APIv1 { } + + + async name_get(req, res) { const { uuid } = req.params; diff --git a/acs-data-access/lib/dataflow.js b/acs-data-access/lib/dataflow.js index 3859e788c..66a7e2709 100644 --- a/acs-data-access/lib/dataflow.js +++ b/acs-data-access/lib/dataflow.js @@ -2,14 +2,16 @@ * ACS Data Access Service * Data-Flow / sequence management */ -import rx from 'rxjs'; -import * as rxx from "@amrc-factoryplus/rx-util"; -import { RxClient, UUIDs } from '@amrc-factoryplus/rx-client'; - -import { DataAccess } from './constants.js'; +import imm from "immutable"; +import rx from "rxjs"; +// import { UUIDs } from "@amrc-factoryplus/service-client"; +import * as rxx from "@amrc-factoryplus/rx-util"; +import { Optional, Response } from "@amrc-factoryplus/rx-util"; +import { DataAccess as Constants } from './constants.js'; +import { valid_uuid, valid_krb } from "./validate.js"; export class DataFlow { @@ -20,104 +22,48 @@ export class DataFlow { this.cdb = fplus.ConfigDB; this.auth = fplus.Auth; - /* Requests to update the database */ - /* it's a subject which means we can push values down the sequence. That is driven from requests to the api to make changes. - All the rest are driven from external sources of data (db or ConfigDB). - */ - /** - * for the DataFlow service we'd want to think about what sources of - * information we need to track from the configDB and we start building sequences that track that information and transform it into forms that are useful to us. - */ - this.requests = new rx.Subject(); - /* Responses to these requests */ - this.responses = this._build_responses(); this.metadata = this._build_metadata(); - - // combine these into map ??? with key = structural dataset class UUID - this.sessions = this.cdb.search_app(DataAccess.App.SessionLimits); - this.unions = this.cdb.search_app(DataAccess.App.UnionComponents); - - - // this.names = this._build_names(); - } - - - _build_metadata(){ - } - update_name(r) { - // Something like this but returning an HTTP-like response. (put_config returns an exception that needs catching) - this.cdb.put_config(UUIDs.App.Info, r.object, { name: r.name }); - }; - - - _build_names() { + _build_metadata() { return rxx.rx( - this.cdb.search_app(UUIDs.App.Info), - // rx.map acts on the sequence of responses, entries.map acts on the entries maps - rx.map(entries => entries.map(entry => entry.name)), - // Cache the map (1 refers to 1 previous value) - rx.shareReplay(1)); - } - - + rx.combineLatest([ + this.cdb.search_app(Constants.App.SessionLimits), + this.cdb.search_app(Constants.App.UnionComponents) + ]), + rx.map(([sessions, unions]) => { + const sessionDatasets = sessions.keySeq().toList(); + + const unionDatasets = unions + .valueSeq() // all arrays + .flatten() // flatten arrays + .toSet() // dedupe + .toList(); + + return imm.Map({ + [Constants.Class.Session]: sessionDatasets, + [Constants.Class.Union]: unionDatasets, + }); + }), - _build_responses() { - // this obj is a map from request type to a method that handles this request. - const updaters = { - // This tells you what to do with a "name" type object - // UUIDs.App.Info == "General Information" application - name: this.update_name, - }; + rxx.shareLatest() + ); + } - /** - * the processing loop that actually does the work which takes the sequence of requests that are coming in from the method down below where it pushes to the subject. And then each time we get a new request it looks up how we perform this request, it actually performs it with updater. - * If we get a request that we don't have an updater for -> just return 500. If exists -> we call the updater. - */ - return rxx.rx(this.requests, - rx.mergeMap(r => { - const updater = updaters[r.type] - ?? (async () => ({ status: 500 })); - return updater.call(this, r) - .then(rv => ({ ...rv, type: r.type, request: r.request })); - }), - rx.share()); - } - - /** - * This function tells the data flow to start subscribing to things and making things happen. - */ run() { - this.sessions.subscribe(ss => this.log("SESSION UPDATE %o", ss.toJS())); - this.unions.subscribe(ss => this.log("UNION UPDATE %o", ss.toJS())); - + this.metadata.subscribe(ss => this.log("METADATA UPDATE %o", ss.toJS())); + } + // Track list of datasets for principal with permission + track_metadata(principal, permission){ + + } + find_metadata(principal){ - /** Request a change to the database. - * - * This submits a change request to the database and returns a - * Promise containing an HTTP status code for the outcome. If a - * change is made then the change will also be published to - * `updates`. - * - * @param r A request object. Must contain a `kind` field. - * @returns A response object containing a `status` field. - */ - // The actual processing of the request happens in the _build_responses method that builds the sequence. - request(r) { - const request = uuidv4(); - /* Construct the Promise to the response before we send the - * request. This avoids a race condition. */ - const response = rx.firstValueFrom(rxx.rx( - this.responses, - rx.filter(r => r.request == request))); - this.requests.next({ ...r, request }); - return response; } } diff --git a/acs-data-access/lib/notify.js b/acs-data-access/lib/notify.js index 392940f84..36923f1ee 100644 --- a/acs-data-access/lib/notify.js +++ b/acs-data-access/lib/notify.js @@ -3,7 +3,6 @@ * DataAccessNotify */ - import * as rx from "rxjs"; import * as rxx from "@amrc-factoryplus/rx-util"; @@ -13,7 +12,6 @@ export class DataAccessNotify { constructor(opts) { this.data = opts.data; this.log = opts.debug.bound("notify"); - this.notify = this.build_notify(opts.api); } @@ -22,37 +20,47 @@ export class DataAccessNotify { this.notify.run(); } - /** - * constructs a Notify object - * acts as an express router - * routes notify requests to methods you give it. - */ build_notify(api) { const notify = new Notify({ api, log: this.log, }); - // This is basically Express. This is more strict in that directories must end with a "/". It won't redirect. - - notify.watch("v1/metadata", this.metadata_list.bind(this)); - notify.watch("v1/metadata/:uuid", ) - notify.search("v1/metadata/", ) - - notify.watch("v1/structure/", ) - notify.watch("v1/structure/:uuid", ) - notify.search("v1/structure/", ) + notify.watch("v1/metadata/", this.metadata_list.bind(this)); + notify.watch("v1/metadata/:uuid", this.metadata_uuid.bind(this)); + notify.search("v1/metadata/", this.metadata_search.bind(this)); + notify.watch("v1/structure/", this.structure_list.bind(this)); + notify.watch("v1/structure/:uuid", this.structure_uuid.bind(this)); + notify.search("v1/structure/", this.structure_search.bind(this)); + return notify; } - /** - * Returns a list of Dataset UUIDs the client has access to - */ metadata_list(sess){ } + metadata_uuid(sess){ + + } + + metadata_search(sess){ + + } + + structure_list(sess){ + + } + + structure_uuid(sess){ + + } + + structure_search(sess){ + + } + /** * Get names for all objects the principal can see. */ @@ -68,12 +76,4 @@ export class DataAccessNotify { // You need to build your response objects here )); } - - - /** - * Get changes to the name of a particular object `obj`. - */ - object_name(sess, obj) { - // Do similar to `name_list` here. - } } diff --git a/acs-data-access/lib/routes.js b/acs-data-access/lib/routes.js deleted file mode 100644 index ddf5364fb..000000000 --- a/acs-data-access/lib/routes.js +++ /dev/null @@ -1,15 +0,0 @@ -/* -* ACS Data Access Service -* Routes -*/ - - -import { APIv1 } from './api-v1.js'; - -export function routes(opts) { - const api_v1 = new APIv1(opts); - - return (app) => { - app.use('/v1', api_v1.routes); - }; -} From 9155114446b5d3cb871fabe4886a9f2e3e102652 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 25 Mar 2026 15:27:25 +0000 Subject: [PATCH 12/96] deploy - fixed git krbs keytabs not being created --- deploy/templates/auth/principals/services.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/templates/auth/principals/services.yaml b/deploy/templates/auth/principals/services.yaml index c6089606c..626366f57 100644 --- a/deploy/templates/auth/principals/services.yaml +++ b/deploy/templates/auth/principals/services.yaml @@ -114,7 +114,7 @@ spec: secret: git-keytabs/server {{- end }} - +--- {{- if .Values.dataAccess.enabled }} apiVersion: factoryplus.app.amrc.co.uk/v1 kind: KerberosKey From 4ad614a706148a8c745b18d269b97d2ddc846ac9 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 25 Mar 2026 15:28:29 +0000 Subject: [PATCH 13/96] data access dataflow - keeping track of datasets with contents not just list of uuids --- acs-data-access/lib/dataflow.js | 42 +++++++++++---------------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/acs-data-access/lib/dataflow.js b/acs-data-access/lib/dataflow.js index 66a7e2709..7ab5fae97 100644 --- a/acs-data-access/lib/dataflow.js +++ b/acs-data-access/lib/dataflow.js @@ -3,7 +3,7 @@ * Data-Flow / sequence management */ -import imm from "immutable"; +import { Map as IMap } from "immutable"; import rx from "rxjs"; // import { UUIDs } from "@amrc-factoryplus/service-client"; @@ -22,27 +22,19 @@ export class DataFlow { this.cdb = fplus.ConfigDB; this.auth = fplus.Auth; - this.metadata = this._build_metadata(); + this.dataset_definitions = this._build_dataset_definitions(); } - _build_metadata() { + _build_dataset_definitions() { return rxx.rx( rx.combineLatest([ - this.cdb.search_app(Constants.App.SessionLimits), - this.cdb.search_app(Constants.App.UnionComponents) + this.cdb.search_app(Constants.App.SessionLimits), + this.cdb.search_app(Constants.App.UnionComponents) ]), rx.map(([sessions, unions]) => { - const sessionDatasets = sessions.keySeq().toList(); - - const unionDatasets = unions - .valueSeq() // all arrays - .flatten() // flatten arrays - .toSet() // dedupe - .toList(); - - return imm.Map({ - [Constants.Class.Session]: sessionDatasets, - [Constants.Class.Union]: unionDatasets, + return IMap({ + [Constants.Class.Session]: sessions, + [Constants.Class.Union]: unions, }); }), @@ -50,20 +42,14 @@ export class DataFlow { ); } - run() { - this.metadata.subscribe(ss => this.log("METADATA UPDATE %o", ss.toJS())); - + this.dataset_definitions.subscribe(ss => + this.log("Dataset Definitions UPDATE %o", ss.toJS()) + ); } - - // Track list of datasets for principal with permission - track_metadata(principal, permission){ - + get_dataset_definitions(){ + this.dataset_definitions; } - - find_metadata(principal){ - - } -} +} \ No newline at end of file From 08a7eaaaf67584c08892577653ecf17acaa5c2b7 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 26 Mar 2026 10:26:52 +0000 Subject: [PATCH 14/96] data access get /metadata api ready --- acs-data-access/bin/api.js | 12 ++++++++---- acs-data-access/lib/api-v1.js | 33 +++++++++++++++++++++++++++++---- acs-data-access/lib/dataflow.js | 8 +++++--- 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/acs-data-access/bin/api.js b/acs-data-access/bin/api.js index 867b7d49a..0c8cd70d7 100644 --- a/acs-data-access/bin/api.js +++ b/acs-data-access/bin/api.js @@ -30,7 +30,7 @@ const data = new DataFlow({ }); -const apiv1 = new APIv1({ data, debug }); +const apiv1 = new APIv1({ data, debug, auth: fplus.Auth }); const api = await new WebAPI({ debug: fplus.debug, @@ -54,16 +54,20 @@ const api = await new WebAPI({ const notify = new DataAccessNotify({ - api, data, debug + api, data, debug, + auth: fplus.Auth }); debug.log("app", "Running Data Access DataFlow"); data.run(); -// debug.log("app", "Running Data Access Notify"); -// notify.run(); +debug.log("app", "Running Data Access Notify"); +notify.run(); debug.log("app", "Running Data Access WebAPI") api.run(); + + + diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 68ef25740..83f105060 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -7,9 +7,10 @@ import express from "express"; import * as rx from "rxjs"; import { APIError } from "@amrc-factoryplus/service-api"; -import { DataAccess } from "./constants.js"; +import { DataAccess as Constants } from "./constants.js"; import { valid_grant, valid_krb, valid_uuid } from "./validate.js"; + function fail(status, message) { throw new APIError(status); } @@ -17,6 +18,7 @@ function fail(status, message) { export class APIv1 { constructor(opts) { this.data = opts.data; + this.auth = opts.auth; this.log = opts.debug.bound("apiv1"); @@ -50,18 +52,41 @@ export class APIv1 { return api; } + /** GET. Returns a list of Dataset UUIDs that the client has READ access to. * The dataset can be optionally restricted by from and to dates. * Dates must be in the format ISO date-time string in UTC 2025-11-13T09:33:18.000Z * @param from {date} (optional, inclusive) query param * @param to {date} (optional, inclusive) query param */ - async metadata_list(req, res){ + async metadata_list(req, res) { const principal = req.auth; - // const response = await this.data.find_metadata(principal); - return res.status(200).json(principal); + const result = await rx.firstValueFrom( + rx.combineLatest([ + this.data.get_dataset_definitions(), + this.auth.watch_acl_with_perm(principal, Constants.Perm.ReadDataset) + ]).pipe( + rx.map(([datasets, targets]) => { + const output = []; + + datasets.forEach((value) => { + const obj = value.toJS ? value.toJS() : value; + + Object.entries(obj).forEach(([key, val]) => { + // key is the dataset UUID + if (targets.has(key)) { + output.push(key); + } + }); + }); + + return output; + }) + ) + ); + return res.status(200).json(result); } diff --git a/acs-data-access/lib/dataflow.js b/acs-data-access/lib/dataflow.js index 7ab5fae97..cf96e610a 100644 --- a/acs-data-access/lib/dataflow.js +++ b/acs-data-access/lib/dataflow.js @@ -42,14 +42,16 @@ export class DataFlow { ); } + get_dataset_definitions(){ + return this.dataset_definitions; + } + run() { this.dataset_definitions.subscribe(ss => this.log("Dataset Definitions UPDATE %o", ss.toJS()) ); } - get_dataset_definitions(){ - this.dataset_definitions; - } + } \ No newline at end of file From c7d7e4dcd6295f649bbcf3967ac5975782f5fe9e Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 30 Mar 2026 15:40:26 +0100 Subject: [PATCH 15/96] data access api created helper method to get list dataset uuids --- acs-data-access/lib/api-v1.js | 61 ++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 83f105060..18aca8aa4 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -53,6 +53,33 @@ export class APIv1 { } + async _get_dataset_uuid_list(principal, permission){ + const result = await rx.firstValueFrom( + rx.combineLatest([ + this.data.get_dataset_definitions(), + this.auth.watch_acl_with_perm(principal, permission) + ]).pipe( + rx.map(([datasets, targets]) => { + const output = []; + + datasets.forEach((value) => { + const obj = value.toJS ? value.toJS() : value; + + Object.entries(obj).forEach(([key, val]) => { + // key is the dataset UUID + if (targets.has(key)) { + output.push(key); + } + }); + }); + + return output; + }) + ) + ); + return result; + } + /** GET. Returns a list of Dataset UUIDs that the client has READ access to. * The dataset can be optionally restricted by from and to dates. * Dates must be in the format ISO date-time string in UTC 2025-11-13T09:33:18.000Z @@ -60,31 +87,7 @@ export class APIv1 { * @param to {date} (optional, inclusive) query param */ async metadata_list(req, res) { - const principal = req.auth; - - const result = await rx.firstValueFrom( - rx.combineLatest([ - this.data.get_dataset_definitions(), - this.auth.watch_acl_with_perm(principal, Constants.Perm.ReadDataset) - ]).pipe( - rx.map(([datasets, targets]) => { - const output = []; - - datasets.forEach((value) => { - const obj = value.toJS ? value.toJS() : value; - - Object.entries(obj).forEach(([key, val]) => { - // key is the dataset UUID - if (targets.has(key)) { - output.push(key); - } - }); - }); - - return output; - }) - ) - ); + const result = await this._get_dataset_uuid_list(req.auth, Constants.Perm.ReadDataset); return res.status(200).json(result); } @@ -105,9 +108,7 @@ export class APIv1 { */ async metadata_uuid(req, res){ const {uuid} = req.params - - - // + if (!valid_uuid(uuid)) fail(410); } @@ -123,7 +124,7 @@ export class APIv1 { * unit - Engineering unit (if available) */ async dataset_data(req, res){ - + // } /** GET. @@ -133,7 +134,9 @@ export class APIv1 { * @returns list of dataset UUIDs the client has permission to EDIT */ async structure_list(req, res){ + const result = await this._get_dataset_uuid_list(req.auth, Constants.Perm.EditDataset); + return res.status(200).json(result); } /** GET. Fetches structural definition of dataset From 112ebdd0ac2e68f078872553c12200d5a95fbaee Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 30 Mar 2026 16:26:22 +0100 Subject: [PATCH 16/96] data access dataflow added method to get dataset content by uuid --- acs-data-access/lib/dataflow.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/acs-data-access/lib/dataflow.js b/acs-data-access/lib/dataflow.js index cf96e610a..97c6b6623 100644 --- a/acs-data-access/lib/dataflow.js +++ b/acs-data-access/lib/dataflow.js @@ -46,6 +46,22 @@ export class DataFlow { return this.dataset_definitions; } + get_dataset_by_uuid(uuid) { + return this.dataset_definitions.pipe( + rx.map(defs => { + const js = defs.toJS(); // convert Imm.Map to JS obj + + for (const group of Object.values(js)) { + if (group[uuid]) { + return group[uuid]; + } + } + + return null; // not found + }) + ); + } + run() { this.dataset_definitions.subscribe(ss => this.log("Dataset Definitions UPDATE %o", ss.toJS()) From f38d59183a4da9c5b465c7f68e550f74e6e5bd9f Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 30 Mar 2026 16:26:47 +0100 Subject: [PATCH 17/96] data access api structure_uuid --- acs-data-access/lib/api-v1.js | 36 +++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 18aca8aa4..7ded2b702 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -10,7 +10,6 @@ import { APIError } from "@amrc-factoryplus/service-api"; import { DataAccess as Constants } from "./constants.js"; import { valid_grant, valid_krb, valid_uuid } from "./validate.js"; - function fail(status, message) { throw new APIError(status); } @@ -19,9 +18,7 @@ export class APIv1 { constructor(opts) { this.data = opts.data; this.auth = opts.auth; - this.log = opts.debug.bound("apiv1"); - this.routes = this.setup_routes(); } @@ -33,12 +30,10 @@ export class APIv1 { api.route("/metadata/:uuid") .get(this.metadata_uuid.bind(this)); - api.route("/data/:uuid") .post(this.dataset_data.bind(this)); - api.route("/structure") .get(this.structure_list.bind(this)) .post(this.structure_create.bind(this)); @@ -47,13 +42,10 @@ export class APIv1 { .get(this.structure_uuid.bind(this)) .put(this.structure_update.bind(this)) - - return api; } - - async _get_dataset_uuid_list(principal, permission){ + async _get_dataset_uuids(principal, permission){ const result = await rx.firstValueFrom( rx.combineLatest([ this.data.get_dataset_definitions(), @@ -150,9 +142,29 @@ export class APIv1 { * class {UUID} - Structural class: Sparkplug device, Union Dataset, Session * config {any} - Structural definition: "not visible", Union components, Session limits */ - async structure_uuid(req, res){ - - } + async structure_uuid(req, res) { + const { uuid } = req.params; + + if (!valid_uuid(uuid)) return fail(410); + + const ok = await this.auth.check_acl( + req.auth, + Constants.Perm.EditDataset, + uuid, + true, + ); + + if (!ok) return fail(403); + + const result = await rx.firstValueFrom( + this.data.get_dataset_by_uuid(uuid) + ); + + if (!result) return fail(404); + + return res.status(200).json(result); + } + /** POST. Creates a new dataset. * From efe3463f9ffc09c12f7e894eb11df632cb33d8ec Mon Sep 17 00:00:00 2001 From: amrc-za Date: Tue, 31 Mar 2026 08:07:35 +0100 Subject: [PATCH 18/96] data-access space format and comments --- acs-data-access/lib/api-v1.js | 61 +++++++++++++++++------------------ 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 7ded2b702..1964afc84 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -46,30 +46,30 @@ export class APIv1 { } async _get_dataset_uuids(principal, permission){ - const result = await rx.firstValueFrom( - rx.combineLatest([ - this.data.get_dataset_definitions(), - this.auth.watch_acl_with_perm(principal, permission) - ]).pipe( - rx.map(([datasets, targets]) => { - const output = []; - - datasets.forEach((value) => { - const obj = value.toJS ? value.toJS() : value; - - Object.entries(obj).forEach(([key, val]) => { - // key is the dataset UUID - if (targets.has(key)) { - output.push(key); - } - }); - }); - - return output; - }) - ) - ); - return result; + const result = await rx.firstValueFrom( + rx.combineLatest([ + this.data.get_dataset_definitions(), + this.auth.watch_acl_with_perm(principal, permission) + ]).pipe( + rx.map(([datasets, targets]) => { + const output = []; + + datasets.forEach((value) => { + const obj = value.toJS ? value.toJS() : value; + + Object.entries(obj).forEach(([key, val]) => { + // key is the dataset UUID + if (targets.has(key)) { + output.push(key); + } + }); + }); + + return output; + }) + ) + ); + return result; } /** GET. Returns a list of Dataset UUIDs that the client has READ access to. @@ -80,12 +80,9 @@ export class APIv1 { */ async metadata_list(req, res) { const result = await this._get_dataset_uuid_list(req.auth, Constants.Perm.ReadDataset); - return res.status(200).json(result); } - - /** GET. Accepts Dataset UUID and returns metadata about a Published dataset. * @param uuid {request param} * @param @@ -99,6 +96,7 @@ export class APIv1 { * parts {array} - Subset datasets - contains UUIDs of all subclasses of dataset the client has READ access to. */ async metadata_uuid(req, res){ + // To be implemented const {uuid} = req.params if (!valid_uuid(uuid)) fail(410); } @@ -116,7 +114,7 @@ export class APIv1 { * unit - Engineering unit (if available) */ async dataset_data(req, res){ - // + // To be implemented using influxdb-client-js } /** GET. @@ -127,7 +125,6 @@ export class APIv1 { */ async structure_list(req, res){ const result = await this._get_dataset_uuid_list(req.auth, Constants.Perm.EditDataset); - return res.status(200).json(result); } @@ -144,7 +141,7 @@ export class APIv1 { */ async structure_uuid(req, res) { const { uuid } = req.params; - + if (!valid_uuid(uuid)) return fail(410); const ok = await this.auth.check_acl( @@ -173,7 +170,7 @@ export class APIv1 { * @returns new dataset's UUID - JSON string */ async structure_create(req, res){ - + // To be implemented } /** PUT. Updates dataset definition. @@ -182,7 +179,7 @@ export class APIv1 { * @param {*} res */ async structure_update(req, res){ - + // To be implemented } From ce599bbf6250f227f1d02a04c76394993cf4da0f Mon Sep 17 00:00:00 2001 From: amrc-za Date: Tue, 31 Mar 2026 09:10:25 +0100 Subject: [PATCH 19/96] data access api method name change --- acs-data-access/lib/api-v1.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 1964afc84..da7f233eb 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -79,7 +79,7 @@ export class APIv1 { * @param to {date} (optional, inclusive) query param */ async metadata_list(req, res) { - const result = await this._get_dataset_uuid_list(req.auth, Constants.Perm.ReadDataset); + const result = await this._get_dataset_uuids(req.auth, Constants.Perm.ReadDataset); return res.status(200).json(result); } @@ -124,7 +124,7 @@ export class APIv1 { * @returns list of dataset UUIDs the client has permission to EDIT */ async structure_list(req, res){ - const result = await this._get_dataset_uuid_list(req.auth, Constants.Perm.EditDataset); + const result = await this._get_dataset_uuids(req.auth, Constants.Perm.EditDataset); return res.status(200).json(result); } @@ -170,7 +170,14 @@ export class APIv1 { * @returns new dataset's UUID - JSON string */ async structure_create(req, res){ - // To be implemented + const ok = await this.auth.check_acl( + req.auth, + Constants.Perm.CreateDataset, + uuid, + true, + ); + + if (!ok) return fail(403); } /** PUT. Updates dataset definition. From 474afdc8cc5ea76d2a7d2bbf83770157cafd7b91 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 2 Apr 2026 13:40:01 +0100 Subject: [PATCH 20/96] data access api.js - passing configDB instance to apiV1 --- acs-data-access/bin/api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acs-data-access/bin/api.js b/acs-data-access/bin/api.js index 0c8cd70d7..b02345d67 100644 --- a/acs-data-access/bin/api.js +++ b/acs-data-access/bin/api.js @@ -30,7 +30,7 @@ const data = new DataFlow({ }); -const apiv1 = new APIv1({ data, debug, auth: fplus.Auth }); +const apiv1 = new APIv1({ data, debug, auth: fplus.Auth, cdb: fplus.ConfigDB }); const api = await new WebAPI({ debug: fplus.debug, From 8f2d8788d7f0d7c1d80f22fca2c92175cd00c924 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 2 Apr 2026 13:41:37 +0100 Subject: [PATCH 21/96] data access - constants - removed structure dataset type uuids --- acs-data-access/lib/constants.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/acs-data-access/lib/constants.js b/acs-data-access/lib/constants.js index 0e312af50..6fbb957cb 100644 --- a/acs-data-access/lib/constants.js +++ b/acs-data-access/lib/constants.js @@ -1,8 +1,8 @@ - export const DataAccess = { App: { DatasetDefinition: "eae2d4ae-164d-4dc6-b646-7e0320057bd9", DatasetMetadata: "e3b9fd2c-9de1-470b-9675-739e2a55b77f", + SparkplugSrc: "f5d550c4-2831-11f1-b0b0-83fda3035799", UnionComponents: "1c4ca454-de38-44d9-92fb-aa5218bfa257", SessionLimits: "8754c000-3778-4ae6-b2b8-bbcd959bb775", MESIdentifiers: "af178f0c-3b1e-44f2-9724-5cf06e8fd056", @@ -11,9 +11,6 @@ export const DataAccess = { Class: { Measurement: "cce0ac4e-b5ba-4837-b45d-c74df55aa2d7", Dataset: "c31d3cbd-01cd-4833-8014-c4512aef1e5c", - SparkplugDevice: "18773d6d-a70d-443a-b29a-3f1583195290", - Session: "2f555be8-e7fc-4fb0-b62f-809cefd8a141", - Union: "c57a42a9-348c-4972-a05b-9082c20f2afc", Published: "414d2d10-6be8-4c27-8e9f-c716ef5432b9", Partial: "6c583d11-9a88-4bc1-b77c-81b01e9c9827", MESDataset: "586205bf-81c6-4091-9d2c-f3c0465ebdc4", @@ -33,7 +30,11 @@ export const DataAccess = { Service: { DataAccess: "06cee697-29d3-4972-9479-bc392e24946e" - }, + }, + + Special: { + InvalidDataset: "696396a0-2831-11f1-9b12-33d63b8c5115", + }, Requirement: { ServiceRole: "dd18050c-a9ab-4287-8af2-e983f20e89c8" @@ -46,5 +47,6 @@ export const DataAccess = { ReadDataset: "ec48462e-37eb-4f56-8efa-83d813e85559", UseForSession: "c089b9a9-06cd-4211-94fc-9ad52a759987", EditDataset: "af06b9e5-456a-43e4-b636-5b17de28fc7f", + UseSparkplug: "788b049c-2831-11f1-99fd-2b0bf86d6f77", } -} +} \ No newline at end of file From 4c7af11b7bbed1306ccc131f4b7a9e4ceb34473a Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 2 Apr 2026 13:48:40 +0100 Subject: [PATCH 22/96] data access - flattened dataset definitions map to keys to be dataset uuids --- acs-data-access/lib/api-v1.js | 134 ++++++++++++++++++++++++++------ acs-data-access/lib/dataflow.js | 45 +++++------ 2 files changed, 132 insertions(+), 47 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index da7f233eb..f8ccae128 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -4,6 +4,7 @@ */ import express from "express"; +import { Map as IMap } from "immutable"; import * as rx from "rxjs"; import { APIError } from "@amrc-factoryplus/service-api"; @@ -14,10 +15,16 @@ function fail(status, message) { throw new APIError(status); } +const mapStructureToPermission = IMap() + .set(Constants.App.UnionComponents, Constants.Perm.IncludeInUnion) + .set(Constants.App.SessionLimits, Constants.Perm.UseForSession) + .set(Constants.App.SparkplugSrc, Constants.Perm.UseSparkplug); + export class APIv1 { constructor(opts) { this.data = opts.data; this.auth = opts.auth; + this.cdb = opts.cdb; this.log = opts.debug.bound("apiv1"); this.routes = this.setup_routes(); } @@ -45,33 +52,29 @@ export class APIv1 { return api; } - async _get_dataset_uuids(principal, permission){ + async _get_dataset_uuids(principal, permission) { const result = await rx.firstValueFrom( rx.combineLatest([ this.data.get_dataset_definitions(), this.auth.watch_acl_with_perm(principal, permission) ]).pipe( rx.map(([datasets, targets]) => { - const output = []; - - datasets.forEach((value) => { - const obj = value.toJS ? value.toJS() : value; + // datasets: IMap + // targets: Set or ISet of allowed datasetIds - Object.entries(obj).forEach(([key, val]) => { - // key is the dataset UUID - if (targets.has(key)) { - output.push(key); - } - }); - }); - - return output; + return datasets + .keySeq() // get all dataset IDs + .filter(id => targets.has(id)) + .toArray(); // convert to plain array }) ) ); + return result; } + + /** GET. Returns a list of Dataset UUIDs that the client has READ access to. * The dataset can be optionally restricted by from and to dates. * Dates must be in the format ISO date-time string in UTC 2025-11-13T09:33:18.000Z @@ -79,8 +82,8 @@ export class APIv1 { * @param to {date} (optional, inclusive) query param */ async metadata_list(req, res) { - const result = await this._get_dataset_uuids(req.auth, Constants.Perm.ReadDataset); - return res.status(200).json(result); + const uuids = await this._get_dataset_uuids(req.auth, Constants.Perm.ReadDataset); + return res.status(200).json(uuids); } /** GET. Accepts Dataset UUID and returns metadata about a Published dataset. @@ -124,8 +127,8 @@ export class APIv1 { * @returns list of dataset UUIDs the client has permission to EDIT */ async structure_list(req, res){ - const result = await this._get_dataset_uuids(req.auth, Constants.Perm.EditDataset); - return res.status(200).json(result); + const uuids = await this._get_dataset_uuids(req.auth, Constants.Perm.EditDataset); + return res.status(200).json(uuids); } /** GET. Fetches structural definition of dataset @@ -154,7 +157,9 @@ export class APIv1 { if (!ok) return fail(403); const result = await rx.firstValueFrom( - this.data.get_dataset_by_uuid(uuid) + this.data.get_dataset_definitions().pipe( + rx.map(datasets => datasets.get(uuid)) + ) ); if (!result) return fail(404); @@ -163,23 +168,106 @@ export class APIv1 { } + async _check_second_level_permission(principal, structure, config){ + if(structure == Constants.App.SessionLimits){ + + const target = config.src; + if(!target) return fail(422); + + const ok = await this.auth.check_acl( + principal, + mapStructureToPermission.get(structure), + target, + true + ); + + return ok; + + }else if(structure == Constants.App.UnionComponents){ + // Todo: check list length? + if(config.length == 0) return true; + + for(let target of config){ + const ok = await this.auth.check_acl( + principal, + mapStructureToPermission.get(structure), + target, + true + ); + if(!ok) return false; + } + return true; + + }else if( structure == Constants.App.SparkplugSrc){ + + const target = config.src; + if(!target) return fail(422); + + const ok = await this.auth.check_acl( + principal, + mapStructureToPermission.get(structure), + target, + true + ); + + return ok; + }else{ + // Unhandled Structure type + return fail(500); + } + } + /** POST. Creates a new dataset. * - * @param {*} req.body must be object (class, config) without uuid. + * @param {*} req.body must be object (structure, config) without uuid. * @param {*} res * @returns new dataset's UUID - JSON string */ async structure_create(req, res){ + const structure = req.body.structure; + if(!structure) return fail(422); + + if (!valid_uuid(structure)) return fail(410); + const ok = await this.auth.check_acl( req.auth, Constants.Perm.CreateDataset, - uuid, - true, + structure, + true ); if (!ok) return fail(403); + + // 2nd persmission check depends on structure + // if session -> session permission, UseForSession, target = src + // if union -> IncludeInUnion, target = all datasets in the list + // if sprk -> + + + const config = req.body.config; + if(!config) return fail(422); + + const ok2 = await this._check_second_level_permission(req.auth, structure, config); + if(!ok2) return fail(403); + + + // Create new Dataset Object + const newDatasetUuid = await this.cdb.create_object(Constants.Class.Dataset); + this.log("Created new dataset object in ConfigDB", newDatasetUuid); + if(!newDatasetUuid) return fail(400); + + // Create config entry for the dataset object + try{ + await this.cdb.put_config(structure, newDatasetUuid, config); + }catch(err){ + return fail(422); + } + + return res.status(200).json(newDatasetUuid); } + + /** PUT. Updates dataset definition. * * @param {*} req.body must be object (class, config) and UUID (optional) @@ -187,6 +275,8 @@ export class APIv1 { */ async structure_update(req, res){ // To be implemented + + // 2 level permission check same as in POST } diff --git a/acs-data-access/lib/dataflow.js b/acs-data-access/lib/dataflow.js index 97c6b6623..caa2b5796 100644 --- a/acs-data-access/lib/dataflow.js +++ b/acs-data-access/lib/dataflow.js @@ -6,9 +6,7 @@ import { Map as IMap } from "immutable"; import rx from "rxjs"; -// import { UUIDs } from "@amrc-factoryplus/service-client"; import * as rxx from "@amrc-factoryplus/rx-util"; -import { Optional, Response } from "@amrc-factoryplus/rx-util"; import { DataAccess as Constants } from './constants.js'; import { valid_uuid, valid_krb } from "./validate.js"; @@ -25,17 +23,33 @@ export class DataFlow { this.dataset_definitions = this._build_dataset_definitions(); } + // Todo: check if dataset is duplicated (in all unions, session, sparkplug) -> set as invalid dataset _build_dataset_definitions() { return rxx.rx( rx.combineLatest([ + this.cdb.search_app(Constants.App.SparkplugSrc), this.cdb.search_app(Constants.App.SessionLimits), this.cdb.search_app(Constants.App.UnionComponents) ]), - rx.map(([sessions, unions]) => { - return IMap({ - [Constants.Class.Session]: sessions, - [Constants.Class.Union]: unions, + + rx.switchMap(([sprkDevices, sessions, unions]) => { + const grouped = IMap({ + [Constants.App.SessionLimits]: sessions, + [Constants.App.UnionComponents]: unions, + [Constants.App.SparkplugSrc]: sprkDevices }); + + return rx.from(grouped.entrySeq()).pipe( + rx.mergeMap(([structure, datasets]) => + rx.from((datasets || IMap()).entrySeq()).pipe( + rx.map(([datasetId, config]) => ({ + datasetId, + value: { structure, config } + })) + ) + ), + rx.reduce((acc, { datasetId, value }) => acc.set(datasetId, value), IMap()) + ); }), rxx.shareLatest() @@ -46,28 +60,9 @@ export class DataFlow { return this.dataset_definitions; } - get_dataset_by_uuid(uuid) { - return this.dataset_definitions.pipe( - rx.map(defs => { - const js = defs.toJS(); // convert Imm.Map to JS obj - - for (const group of Object.values(js)) { - if (group[uuid]) { - return group[uuid]; - } - } - - return null; // not found - }) - ); - } - run() { this.dataset_definitions.subscribe(ss => this.log("Dataset Definitions UPDATE %o", ss.toJS()) ); } - - - } \ No newline at end of file From fe7b367c83d5d8f7c3d5d8e923328f12db1a1b55 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 2 Apr 2026 15:27:09 +0100 Subject: [PATCH 23/96] data access api-v1 implemented put structure --- acs-data-access/lib/api-v1.js | 78 +++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 31 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index f8ccae128..71b8cc234 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -171,7 +171,7 @@ export class APIv1 { async _check_second_level_permission(principal, structure, config){ if(structure == Constants.App.SessionLimits){ - const target = config.src; + const target = config.source; if(!target) return fail(422); const ok = await this.auth.check_acl( @@ -180,7 +180,7 @@ export class APIv1 { target, true ); - + return ok; }else if(structure == Constants.App.UnionComponents){ @@ -200,7 +200,7 @@ export class APIv1 { }else if( structure == Constants.App.SparkplugSrc){ - const target = config.src; + const target = config.source; if(!target) return fail(422); const ok = await this.auth.check_acl( @@ -217,20 +217,14 @@ export class APIv1 { } } - /** POST. Creates a new dataset. - * - * @param {*} req.body must be object (structure, config) without uuid. - * @param {*} res - * @returns new dataset's UUID - JSON string - */ - async structure_create(req, res){ - const structure = req.body.structure; + + async _update_dataset_config(principal, structure, config, objectUuid){ if(!structure) return fail(422); if (!valid_uuid(structure)) return fail(410); const ok = await this.auth.check_acl( - req.auth, + principal, Constants.Perm.CreateDataset, structure, true @@ -238,49 +232,71 @@ export class APIv1 { if (!ok) return fail(403); - // 2nd persmission check depends on structure - // if session -> session permission, UseForSession, target = src - // if union -> IncludeInUnion, target = all datasets in the list - // if sprk -> - - - const config = req.body.config; if(!config) return fail(422); - const ok2 = await this._check_second_level_permission(req.auth, structure, config); + // Check the principal has appropriate permission for all dataset sources in config. + const ok2 = await this._check_second_level_permission(principal, structure, config); + if(!ok2) return fail(403); // Create new Dataset Object - const newDatasetUuid = await this.cdb.create_object(Constants.Class.Dataset); - this.log("Created new dataset object in ConfigDB", newDatasetUuid); - if(!newDatasetUuid) return fail(400); + if(!objectUuid){ + objectUuid = await this.cdb.create_object(Constants.Class.Dataset); + + this.log("Created new dataset object in ConfigDB", objectUuid); + + if(!objectUuid) return fail(400); + } // Create config entry for the dataset object try{ - await this.cdb.put_config(structure, newDatasetUuid, config); + await this.cdb.put_config(structure, objectUuid, config); }catch(err){ return fail(422); } - return res.status(200).json(newDatasetUuid); + return objectUuid; } - - /** PUT. Updates dataset definition. + /** POST. Creates a new dataset. * - * @param {*} req.body must be object (class, config) and UUID (optional) + * @param {*} req.body must be object (structure, config) without uuid. * @param {*} res + * @returns new dataset's UUID - JSON string */ - async structure_update(req, res){ - // To be implemented + async structure_create(req, res){ + const objectUuid = await this._update_dataset_config( + req.auth, + req.body.structure, + req.body.config, + null + ); - // 2 level permission check same as in POST + if(!objectUuid) return fail(500); + return res.status(200).json(objectUuid); } + + /** PUT. Updates dataset definition. Principal should have CreateDataset permission + * + * @param {*} req.body must be object (structure, config) and UUID (optional) + * @param {*} res + */ + async structure_update(req, res){ + const objectUuid = await this._update_dataset_config( + req.auth, + req.body.structure, + req.body.config, + req.params.uuid + ); + if(!objectUuid) return fail(500); + + return res.status(200).json(objectUuid); + } async name_get(req, res) { From bf8d5fa5f6252d4243125e7a664d5f21ff0c848a Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 8 Apr 2026 14:41:20 +0100 Subject: [PATCH 24/96] data access - package json added influx client lib --- acs-data-access/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/acs-data-access/package.json b/acs-data-access/package.json index f3a98dc04..50a09340c 100644 --- a/acs-data-access/package.json +++ b/acs-data-access/package.json @@ -15,6 +15,7 @@ "@amrc-factoryplus/service-api": "file:../lib/js-service-api", "@amrc-factoryplus/service-client": "file:../lib/js-service-client", "@dotenvx/dotenvx": "^1.39.0", + "@influxdata/influxdb-client": "^1.35.0", "express": "^4.21.2", "rxjs": "^7.8.2", "stream-size": "^0.0.6", From 348732316ef83e7666804f9e51ff79a943fa79c8 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 8 Apr 2026 14:41:48 +0100 Subject: [PATCH 25/96] data access - added dataset validity enum --- acs-data-access/lib/validate.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/acs-data-access/lib/validate.js b/acs-data-access/lib/validate.js index 45ffe5bd7..b3e3279e9 100644 --- a/acs-data-access/lib/validate.js +++ b/acs-data-access/lib/validate.js @@ -34,3 +34,10 @@ export function valid_grant (grant) { if (!valid_uuid(grant.target)) return false; return (grant.plural === true || grant.plural === false); } + + +export const DatasetValidity = Object.freeze({ + VALID: 'valid', + INVALID: 'invalid', + ALL: 'all' +}); \ No newline at end of file From 7df70592a0f2f9ba6e4fa769d471a2af249fd784 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 8 Apr 2026 14:48:13 +0100 Subject: [PATCH 26/96] data access api and dataflow update to keep track of invalid datasets --- acs-data-access/lib/api-v1.js | 98 +++++++++++++++++++++++++++++---- acs-data-access/lib/dataflow.js | 53 +++++++++++++++--- 2 files changed, 130 insertions(+), 21 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 71b8cc234..7c6d73dc6 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -6,10 +6,13 @@ import express from "express"; import { Map as IMap } from "immutable"; import * as rx from "rxjs"; +import {InfluxDB, flux} from '@influxdata/influxdb-client'; import { APIError } from "@amrc-factoryplus/service-api"; import { DataAccess as Constants } from "./constants.js"; -import { valid_grant, valid_krb, valid_uuid } from "./validate.js"; +import { valid_grant, valid_krb, valid_uuid, DatasetValidity } from "./validate.js"; + + function fail(status, message) { throw new APIError(status); @@ -26,6 +29,9 @@ export class APIv1 { this.auth = opts.auth; this.cdb = opts.cdb; this.log = opts.debug.bound("apiv1"); + this.influx_bucket = opts.influx_bucket; + this.influx_org = opts.influx_org; + this.influx_query_api = opts.influx_client.getQueryApi(opts.influx_org); this.routes = this.setup_routes(); } @@ -52,10 +58,12 @@ export class APIv1 { return api; } - async _get_dataset_uuids(principal, permission) { + async _get_allowed_dataset_uuids(principal, permission, dataset_validity) { + const dataset_def_obs = this.data.get_dataset_definitions(dataset_validity); + const result = await rx.firstValueFrom( rx.combineLatest([ - this.data.get_dataset_definitions(), + dataset_def_obs, this.auth.watch_acl_with_perm(principal, permission) ]).pipe( rx.map(([datasets, targets]) => { @@ -80,9 +88,10 @@ export class APIv1 { * Dates must be in the format ISO date-time string in UTC 2025-11-13T09:33:18.000Z * @param from {date} (optional, inclusive) query param * @param to {date} (optional, inclusive) query param + * Don't return invalid datasets */ async metadata_list(req, res) { - const uuids = await this._get_dataset_uuids(req.auth, Constants.Perm.ReadDataset); + const uuids = await this._get_allowed_dataset_uuids(req.auth, Constants.Perm.ReadDataset, DatasetValidity.VALID); return res.status(200).json(uuids); } @@ -97,9 +106,10 @@ export class APIv1 { * function {array} - Array of Functional classes - UUIDs of classes dataset belongs to (only members of Functional dataset type) * metadata {object} - Map of metadata configs keyed by Application UUID - contains all config entries for dataset which use applications in the Dataset metadata class * parts {array} - Subset datasets - contains UUIDs of all subclasses of dataset the client has READ access to. + * Don't return valid response if dataset is invalid */ async metadata_uuid(req, res){ - // To be implemented + // to be implemented const {uuid} = req.params if (!valid_uuid(uuid)) fail(410); } @@ -115,9 +125,50 @@ export class APIv1 { * timestamp - ISO string * value - actual data value * unit - Engineering unit (if available) + * Don't return valid response if dataset is invalid */ async dataset_data(req, res){ - // To be implemented using influxdb-client-js + // to be implemented + const dataset_uuid = req.params.uuid; + // if (!valid_uuid(dataset_uuid)) return fail(410); + + // const dataset_def = await rx.firstValueFrom( + // this.data.get_dataset_definitions().pipe( + // rx.map(datasets => datasets.get(dataset_uuid)) + // ) + // ); + + // // Recursion until the structure is Device -> get data out of influx + + // const source_uuid = dataset_def.config.source; + + // // get sprk uuid for that device + // // const instance_uuid = + + // if (!source_uuid) return fail(404); + + // const query = flux`from(bucket: "${this.influx_bucket}") + // |> range(start: -1d) + // |> filter(fn: (r) => r.topLevelInstance == "${source_uuid}")`; + + // const rows = []; + + // this.influx_query_api.queryRows(query, { + // next: (row, tableMeta) => { + // const o = tableMeta.toObject(row); + // rows.push({ + // _time: o._time, + // _measurement: o._measurement, + // _value: o._value + // }); + // }, + // error: (err) => { + // return res.status(500).json(err); + // }, + // complete: () => { + // return res.status(200).json(rows); + // } + // }); } /** GET. @@ -125,9 +176,10 @@ export class APIv1 { * @param {*} req * @param {*} res * @returns list of dataset UUIDs the client has permission to EDIT + * the response should include invalid datasets */ async structure_list(req, res){ - const uuids = await this._get_dataset_uuids(req.auth, Constants.Perm.EditDataset); + const uuids = await this._get_allowed_dataset_uuids(req.auth, Constants.Perm.EditDataset, DatasetValidity.ALL); return res.status(200).json(uuids); } @@ -141,6 +193,7 @@ export class APIv1 { * uuid {UUID} - Dataset UUID * class {UUID} - Structural class: Sparkplug device, Union Dataset, Session * config {any} - Structural definition: "not visible", Union components, Session limits + * the response should specify structure field as invalid uuid in definition if the dataset is invalid */ async structure_uuid(req, res) { const { uuid } = req.params; @@ -156,15 +209,21 @@ export class APIv1 { if (!ok) return fail(403); - const result = await rx.firstValueFrom( - this.data.get_dataset_definitions().pipe( + const definition = await rx.firstValueFrom( + this.data.get_dataset_definitions(DatasetValidity.ALL).pipe( rx.map(datasets => datasets.get(uuid)) ) ); - if (!result) return fail(404); + if (!definition) return fail(404); + + if (definition.length > 1){ + for(let def of definition){ + def.structure = Constants.Special.InvalidDataset; + } + } - return res.status(200).json(result); + return res.status(200).json(definition); } @@ -184,7 +243,6 @@ export class APIv1 { return ok; }else if(structure == Constants.App.UnionComponents){ - // Todo: check list length? if(config.length == 0) return true; for(let target of config){ @@ -285,6 +343,7 @@ export class APIv1 { * @param {*} req.body must be object (structure, config) and UUID (optional) * @param {*} res */ + async structure_update(req, res){ const objectUuid = await this._update_dataset_config( req.auth, @@ -295,6 +354,21 @@ export class APIv1 { if(!objectUuid) return fail(500); + // remove definitions from other structure apps + const invalid_defs = await rx.firstValueFrom( + this.data.get_dataset_definitions(DatasetValidity.INVALID).pipe( + rx.map(datasets => datasets.get(req.params.uuid)), // extract defs + rx.filter(defs => defs && defs.length), // ensure exists + rx.map(defs => defs.filter(d => d.structure !== req.body.structure)) // filter array + ) + ); + + if(invalid_defs){ + for (let i_def of invalid_defs){ + this.cdb.delete_config(i_def.structure, objectUuid); + } + } + return res.status(200).json(objectUuid); } diff --git a/acs-data-access/lib/dataflow.js b/acs-data-access/lib/dataflow.js index caa2b5796..be974ea8a 100644 --- a/acs-data-access/lib/dataflow.js +++ b/acs-data-access/lib/dataflow.js @@ -9,8 +9,8 @@ import rx from "rxjs"; import * as rxx from "@amrc-factoryplus/rx-util"; import { DataAccess as Constants } from './constants.js'; -import { valid_uuid, valid_krb } from "./validate.js"; - +import { valid_uuid, valid_krb, DatasetValidity } from "./validate.js"; +import { RxClient, UUIDs } from '@amrc-factoryplus/rx-client'; export class DataFlow { constructor(opts) { @@ -23,7 +23,14 @@ export class DataFlow { this.dataset_definitions = this._build_dataset_definitions(); } - // Todo: check if dataset is duplicated (in all unions, session, sparkplug) -> set as invalid dataset + + run() { + // this.dataset_definitions.subscribe(ss => + // this.log("Dataset Definitions UPDATE %o", ss.toJS()) + // ); + + } + _build_dataset_definitions() { return rxx.rx( rx.combineLatest([ @@ -40,15 +47,21 @@ export class DataFlow { }); return rx.from(grouped.entrySeq()).pipe( + // Flatten all datasets across all apps rx.mergeMap(([structure, datasets]) => rx.from((datasets || IMap()).entrySeq()).pipe( rx.map(([datasetId, config]) => ({ datasetId, - value: { structure, config } + definition: { structure, config } })) ) ), - rx.reduce((acc, { datasetId, value }) => acc.set(datasetId, value), IMap()) + + // Group into: { datasetId: [definitions] } + rx.reduce((acc, { datasetId, definition }) => { + const existing = acc.get(datasetId, []); + return acc.set(datasetId, [...existing, definition]); + }, IMap()) ); }), @@ -56,13 +69,35 @@ export class DataFlow { ); } - get_dataset_definitions(){ + get_dataset_definitions(validity){ + if(validity == DatasetValidity.VALID){ + return this._get_valid_dataset_definitions(); + }else if (validity == DatasetValidity.INVALID){ + return this._get_invalid_dataset_definitions(); + }else if(validity == DatasetValidity.ALL){ + return this._get_all_dataset_definitions(); + }else{ + throw Error("Unhandled dataset validity type."); + } + } + + _get_all_dataset_definitions(){ return this.dataset_definitions; } - run() { - this.dataset_definitions.subscribe(ss => - this.log("Dataset Definitions UPDATE %o", ss.toJS()) + _get_valid_dataset_definitions() { + return this.dataset_definitions.pipe( + rx.map((map) => + map.filter((definitions) => definitions.length === 1) + ) + ); + } + + _get_invalid_dataset_definitions() { + return this.dataset_definitions.pipe( + rx.map((map) => + map.filter((definitions) => definitions.length > 1) + ) ); } } \ No newline at end of file From 7cc88b87e614831ed358a091d659bbb7fca0fa6f Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 9 Apr 2026 15:32:41 +0100 Subject: [PATCH 27/96] data access - get data from influx partial implementation --- acs-data-access/bin/api.js | 16 +- acs-data-access/lib/api-v1.js | 307 ++++++++++++++++++++++++---------- 2 files changed, 230 insertions(+), 93 deletions(-) diff --git a/acs-data-access/bin/api.js b/acs-data-access/bin/api.js index b02345d67..20dd617d7 100644 --- a/acs-data-access/bin/api.js +++ b/acs-data-access/bin/api.js @@ -5,6 +5,7 @@ * Entry point: api.js */ +import {InfluxDB, flux} from '@influxdata/influxdb-client'; import { RxClient, UUIDs } from '@amrc-factoryplus/rx-client'; import { WebAPI } from '@amrc-factoryplus/service-api'; @@ -23,6 +24,11 @@ const Version = "2.0.0"; const debug = fplus.debug; debug.log("app", "Starting acs-data-service revision %s"); +const influxClient = new InfluxDB({ + url: env.INFLUXDB_URL, + token: env.INFLUXDB_TOKEN +}); + // the dataflow object is full of sequences that does most of the work of this service. Sequence means an RX observable. const data = new DataFlow({ @@ -30,7 +36,14 @@ const data = new DataFlow({ }); -const apiv1 = new APIv1({ data, debug, auth: fplus.Auth, cdb: fplus.ConfigDB }); +const apiv1 = new APIv1({ + data, debug, + auth: fplus.Auth, + cdb: fplus.ConfigDB, + influx_client: influxClient, + influx_org: env.INFLUXDB_ORG, + influx_bucket: env.INFLUXDB_BUCKET, +}); const api = await new WebAPI({ debug: fplus.debug, @@ -50,6 +63,7 @@ const api = await new WebAPI({ routes: app => { app.use("/v1", apiv1.routes); } + }).init(); diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 7c6d73dc6..9a9a4acf1 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -12,8 +12,6 @@ import { APIError } from "@amrc-factoryplus/service-api"; import { DataAccess as Constants } from "./constants.js"; import { valid_grant, valid_krb, valid_uuid, DatasetValidity } from "./validate.js"; - - function fail(status, message) { throw new APIError(status); } @@ -126,49 +124,222 @@ export class APIv1 { * value - actual data value * unit - Engineering unit (if available) * Don't return valid response if dataset is invalid + + Influx tags: + [ + "_start", + "_stop", + "_field", + "_measurement", + "bottomLevelInstance", + "bottomLevelSchema", + "device", + "group", + "node", + "path", + "topLevelInstance", + "topLevelSchema", + "usesInstances", + "usesSchemas" + ] */ - async dataset_data(req, res){ - // to be implemented + + async _check_second_level_permission(principal, structure, config, permission){ + if(structure == Constants.App.SessionLimits){ + + const target = config.source; + if(!target) return fail(422); + + const ok = await this.auth.check_acl( + principal, + permission, + target, + true + ); + + return ok; + + }else if(structure == Constants.App.UnionComponents){ + if(config.length == 0) return true; + + for(let target of config){ + const ok = await this.auth.check_acl( + principal, + permission, + target, + true + ); + if(!ok) return false; + } + return true; + + }else if( structure == Constants.App.SparkplugSrc){ + + const target = config.source; + if(!target) return fail(422); + + const ok = await this.auth.check_acl( + principal, + permission, + target, + true + ); + + return ok; + }else{ + // Unhandled Structure type + return fail(500); + } + } + + async _run_influx_query(topLevelInstance) { + const query = flux`from(bucket: "${this.influx_bucket}") + |> range(start: -1d) + |> filter(fn: (r) => r.topLevelInstance == "${topLevelInstance}")`; + + return new Promise((resolve, reject) => { + const rows = []; + + this.influx_query_api.queryRows(query, { + next: (row, tableMeta) => { + const o = tableMeta.toObject(row); + rows.push({ + device: o.device, + metric: o._measurement, + timestamp: o._time, + value: o._value, + unit: o.unit, + path: o.path + }); + }, + error: (err) => reject(err), + complete: () => resolve(rows) + }); + }); + } + + + + async _get_dataset_data(dataset_uuid, visited = new Set()) { + if (visited.has(dataset_uuid)) { + throw new Error(`Circular dataset reference detected at ${dataset_uuid}`); + } + + visited.add(dataset_uuid); + + const dataset_def = await this._get_dataset_def_by_uuid(dataset_uuid); + if (!dataset_def) return []; + + const { structure, config } = dataset_def; + + // ------------------------- + // 1. SparkplugSrc (BASE CASE) + // ------------------------- + if (structure === Constants.App.SparkplugSrc) { + const source_uuid = config?.source; + if (!source_uuid) return []; + + // NOTE: here source_uuid is the topLevelInstance for influx + const data = await this._run_influx_query(source_uuid); + return data; + } + + // ------------------------- + // 2. SessionLimits (SINGLE REFERENCE) + // ------------------------- + else if (structure === Constants.App.SessionLimits) { + const source_uuid = config?.source; + if (!source_uuid) return []; + + return this._get_dataset_data(source_uuid, visited); + } + + // ------------------------- + // 3. UnionComponents (MULTIPLE REFERENCES) + // ------------------------- + else if (structure === Constants.App.UnionComponents) { + const dataset_uuids = config; + + if (!Array.isArray(dataset_uuids)) return []; + + const results = await Promise.all( + dataset_uuids.map(uuid => + this._get_dataset_data(uuid, new Set(visited)) // isolate branch + ) + ); + + return results.flat(); + } + + // ------------------------- + // 4. Unknown structure + // ------------------------- + else { + throw new Error(`Unhandled structure type: ${structure}`); + } + } + + + + + async _get_dataset_def_by_uuid(uuid){ + const def = await rx.firstValueFrom( + this.data.get_dataset_definitions(DatasetValidity.VALID).pipe( + rx.map(datasets => datasets.get(uuid)?.[0]) + ) + ); + return def; + } + + _convert_to_csv(rows) { + if (!rows.length) return ""; + + const headers = Object.keys(rows[0]); + const lines = rows.map(row => + headers.map(h => JSON.stringify(row[h] ?? "")).join(",") + ); + + return [headers.join(","), ...lines].join("\n"); + } + + async dataset_data(req, res) { const dataset_uuid = req.params.uuid; - // if (!valid_uuid(dataset_uuid)) return fail(410); - - // const dataset_def = await rx.firstValueFrom( - // this.data.get_dataset_definitions().pipe( - // rx.map(datasets => datasets.get(dataset_uuid)) - // ) - // ); - - // // Recursion until the structure is Device -> get data out of influx - - // const source_uuid = dataset_def.config.source; - - // // get sprk uuid for that device - // // const instance_uuid = - - // if (!source_uuid) return fail(404); - - // const query = flux`from(bucket: "${this.influx_bucket}") - // |> range(start: -1d) - // |> filter(fn: (r) => r.topLevelInstance == "${source_uuid}")`; - - // const rows = []; - - // this.influx_query_api.queryRows(query, { - // next: (row, tableMeta) => { - // const o = tableMeta.toObject(row); - // rows.push({ - // _time: o._time, - // _measurement: o._measurement, - // _value: o._value - // }); - // }, - // error: (err) => { - // return res.status(500).json(err); - // }, - // complete: () => { - // return res.status(200).json(rows); - // } - // }); + if (!valid_uuid(dataset_uuid)) return fail(410); + + const dataset_def = await this._get_dataset_def_by_uuid(dataset_uuid); + if (!dataset_def) return fail(404); + + const ok = await this.auth.check_acl( + req.auth, + Constants.Perm.ReadDataset, + dataset_uuid, + true, + ); + if (!ok) return fail(403); + + const structure = dataset_def.structure; + const config = dataset_def.config; + + const ok2 = this._check_second_level_permission( + req.auth, + structure, + config, + Constants.Perm.ReadDataset + ); + if (!ok2) return fail(403); + + try { + const rows = await this._get_dataset_data(dataset_uuid); + + const csv = this._convert_to_csv(rows); + + res.setHeader("Content-Type", "text/csv"); + return res.status(200).send(csv); + + } catch (err) { + console.error(err); + return fail(500); + } } /** GET. @@ -227,54 +398,6 @@ export class APIv1 { } - async _check_second_level_permission(principal, structure, config){ - if(structure == Constants.App.SessionLimits){ - - const target = config.source; - if(!target) return fail(422); - - const ok = await this.auth.check_acl( - principal, - mapStructureToPermission.get(structure), - target, - true - ); - - return ok; - - }else if(structure == Constants.App.UnionComponents){ - if(config.length == 0) return true; - - for(let target of config){ - const ok = await this.auth.check_acl( - principal, - mapStructureToPermission.get(structure), - target, - true - ); - if(!ok) return false; - } - return true; - - }else if( structure == Constants.App.SparkplugSrc){ - - const target = config.source; - if(!target) return fail(422); - - const ok = await this.auth.check_acl( - principal, - mapStructureToPermission.get(structure), - target, - true - ); - - return ok; - }else{ - // Unhandled Structure type - return fail(500); - } - } - async _update_dataset_config(principal, structure, config, objectUuid){ if(!structure) return fail(422); @@ -293,7 +416,7 @@ export class APIv1 { if(!config) return fail(422); // Check the principal has appropriate permission for all dataset sources in config. - const ok2 = await this._check_second_level_permission(principal, structure, config); + const ok2 = await this._check_second_level_permission(principal, structure, config, mapStructureToPermission[structure]); if(!ok2) return fail(403); From d2e63502160801c3bb2462a26fbe81240661b4d6 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 13 Apr 2026 10:58:33 +0100 Subject: [PATCH 28/96] deploy data access - added influx vars and secret --- deploy/templates/data-access/data-access.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/deploy/templates/data-access/data-access.yaml b/deploy/templates/data-access/data-access.yaml index aad11efd8..d33ddfba3 100644 --- a/deploy/templates/data-access/data-access.yaml +++ b/deploy/templates/data-access/data-access.yaml @@ -54,6 +54,17 @@ spec: {{ include "amrc-connectivity-stack.cache-max-age" (list . "dataAccess") | indent 12 }} - name: DIRECTORY_URL value: http://directory.{{ .Release.Namespace }}.svc.cluster.local + - name: INFLUXDB_URL + value: http://acs-influxdb2.{{ .Release.Namespace }}.svc.cluster.local + - name: INFLUXDB_ORG + value: default + - name: INFLUXDB_BUCKET + value: default + - name: INFLUXDB_TOKEN + valueFrom: + secretKeyRef: + name: influxdb-auth + key: admin-token volumeMounts: - mountPath: /config/krb5-conf name: krb5-conf From 40c89d9c5c032bf5157e1e3b36ba51c730e805e8 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 13 Apr 2026 13:10:26 +0100 Subject: [PATCH 29/96] data access updated flux query --- acs-data-access/lib/api-v1.js | 50 ++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 9a9a4acf1..2bf6dc1b2 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -192,10 +192,41 @@ export class APIv1 { } } - async _run_influx_query(topLevelInstance) { - const query = flux`from(bucket: "${this.influx_bucket}") - |> range(start: -1d) - |> filter(fn: (r) => r.topLevelInstance == "${topLevelInstance}")`; + async _run_influx_query(topLevelInstance, meta = {}) { + const { from, to, measurement } = meta; + + // Build range + const rangeClause = (from && to) + ? `|> range(start: ${from}, stop: ${to})` + : `|> range(start: -5m)`; + + // Optional measurement filter + const measurementFilter = measurement + ? `|> filter(fn: (r) => r._measurement == "${measurement}")` + : ``; + + const query = ` + import "strings" + + from(bucket: "${this.influx_bucket}") + ${rangeClause} + |> filter(fn: (r) => r.topLevelInstance == "${topLevelInstance}") + ${measurementFilter} + |> drop(columns: [ + "_start","_stop","_field","table", + "bottomLevelInstance","bottomLevelSchema", + "group","node","topLevelSchema", + "usesInstances","usesSchemas" + ]) + |> map(fn: (r) => ({ r with path: if exists r.path then r.path else "" })) + |> pivot( + rowKey: ["_time"], + columnKey: ["_measurement"], + valueColumn: "_value" + ) + |> group() + |> sort(columns: ["_time"], desc: false) + `; return new Promise((resolve, reject) => { const rows = []; @@ -203,16 +234,9 @@ export class APIv1 { this.influx_query_api.queryRows(query, { next: (row, tableMeta) => { const o = tableMeta.toObject(row); - rows.push({ - device: o.device, - metric: o._measurement, - timestamp: o._time, - value: o._value, - unit: o.unit, - path: o.path - }); + rows.push(o); }, - error: (err) => reject(err), + error: reject, complete: () => resolve(rows) }); }); From 43b8ae6223796db75760987a3e182f68e6a41933 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 15 Apr 2026 17:57:35 +0100 Subject: [PATCH 30/96] data access dataflow watching general info app --- acs-data-access/lib/dataflow.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/acs-data-access/lib/dataflow.js b/acs-data-access/lib/dataflow.js index be974ea8a..bbeb043f2 100644 --- a/acs-data-access/lib/dataflow.js +++ b/acs-data-access/lib/dataflow.js @@ -21,6 +21,7 @@ export class DataFlow { this.auth = fplus.Auth; this.dataset_definitions = this._build_dataset_definitions(); + this.general_infos = this._build_general_info(); } @@ -28,9 +29,23 @@ export class DataFlow { // this.dataset_definitions.subscribe(ss => // this.log("Dataset Definitions UPDATE %o", ss.toJS()) // ); + + // this.log("GENERAL INFO UUID: ", UUIDs.App.Info); + // this.general_infos.subscribe(x => this.log("General infos UPDATE %o", x.toJS())); } + // watch members of Dataset Metadata app Constants.App.DatasetMetadata and then watch members of those members + + + + _build_general_info(){ + return rxx.rx( + this.cdb.search_app(UUIDs.App.Info), + rxx.shareLatest() + ); + } + _build_dataset_definitions() { return rxx.rx( rx.combineLatest([ @@ -69,6 +84,11 @@ export class DataFlow { ); } + get_general_infos(){ + return this.general_infos; + } + + get_dataset_definitions(validity){ if(validity == DatasetValidity.VALID){ return this._get_valid_dataset_definitions(); From 6dac21b459afbee617a5daec31b502f5bef38503 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 15 Apr 2026 17:58:24 +0100 Subject: [PATCH 31/96] data access meta uuid endpoint partially implemented --- acs-data-access/lib/api-v1.js | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 2bf6dc1b2..8d5356a9d 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -10,7 +10,7 @@ import {InfluxDB, flux} from '@influxdata/influxdb-client'; import { APIError } from "@amrc-factoryplus/service-api"; import { DataAccess as Constants } from "./constants.js"; -import { valid_grant, valid_krb, valid_uuid, DatasetValidity } from "./validate.js"; +import { valid_uuid, DatasetValidity } from "./validate.js"; function fail(status, message) { throw new APIError(status); @@ -105,11 +105,38 @@ export class APIv1 { * metadata {object} - Map of metadata configs keyed by Application UUID - contains all config entries for dataset which use applications in the Dataset metadata class * parts {array} - Subset datasets - contains UUIDs of all subclasses of dataset the client has READ access to. * Don't return valid response if dataset is invalid + * + */ async metadata_uuid(req, res){ - // to be implemented - const {uuid} = req.params - if (!valid_uuid(uuid)) fail(410); + const dataset_uuid = req.params.uuid; + if (!valid_uuid(dataset_uuid)) fail(410); + + const ok = await this.auth.check_acl( + req.auth, + Constants.Perm.ReadDataset, + dataset_uuid, + true, + ); + + if (!ok) return fail(403); + + const datasets = await this._get_allowed_dataset_uuids(req.auth, Constants.Perm.ReadDataset, DatasetValidity.VALID); + const is_valid = await datasets.includes(dataset_uuid) + if(!is_valid) return fail(404); + + const infos = await rx.firstValueFrom(this.data.get_general_infos()); + const info = infos.get(dataset_uuid); + if(!info) return fail(404); + + + + const meta = { + uuid: dataset_uuid, + name: info.name, + } + return res.status(200).json(meta); + } From 688c17dfd74a9cf4428e0e50bfc8db9df274a68f Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 16 Apr 2026 15:13:04 +0100 Subject: [PATCH 32/96] data access dataflow watching dataset metadata members and their configs --- acs-data-access/lib/dataflow.js | 42 +++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/acs-data-access/lib/dataflow.js b/acs-data-access/lib/dataflow.js index bbeb043f2..6756ad289 100644 --- a/acs-data-access/lib/dataflow.js +++ b/acs-data-access/lib/dataflow.js @@ -22,6 +22,7 @@ export class DataFlow { this.dataset_definitions = this._build_dataset_definitions(); this.general_infos = this._build_general_info(); + this.metadata = this._build_metadata(); } @@ -35,8 +36,41 @@ export class DataFlow { } - // watch members of Dataset Metadata app Constants.App.DatasetMetadata and then watch members of those members - + // 1. Watch direct members of Constants.App.DatasetMetadata + // 2. For each member -> Search App + _build_metadata() { + return rxx.rx( + this.cdb.watch_members(Constants.App.DatasetMetadata), + + rx.switchMap((uuidsSet) => { + const uuids = uuidsSet.toArray(); + + if (uuids.length === 0) { + return rx.of(IMap()); + } + + const streams = uuids.map(uuid => + this.cdb.search_app(uuid).pipe( + rx.map(result => [uuid, result]) + ) + ); + + return rx.combineLatest(streams).pipe( + rx.map(entries => { + let map = IMap(); + + for (const [uuid, result] of entries) { + map = map.set(uuid, result); + } + + return map; + }) + ); + }), + + rxx.shareLatest() + ); + } _build_general_info(){ @@ -84,6 +118,10 @@ export class DataFlow { ); } + get_metadata(){ + return this.metadata; + } + get_general_infos(){ return this.general_infos; } From 4f616a5e53e818ba4411abcfd7c32a7a8ddbfa51 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 20 Apr 2026 13:22:35 +0100 Subject: [PATCH 33/96] lib js-service-client - added DataAccess interface --- lib/js-service-client/lib/interfaces.js | 1 + lib/js-service-client/lib/service-client.js | 1 + .../lib/service/data-access.js | 30 +++++++++++++++++++ .../lib/service/discovery.js | 1 + 4 files changed, 33 insertions(+) create mode 100644 lib/js-service-client/lib/service/data-access.js diff --git a/lib/js-service-client/lib/interfaces.js b/lib/js-service-client/lib/interfaces.js index 0b32ae233..c5f13d3d9 100644 --- a/lib/js-service-client/lib/interfaces.js +++ b/lib/js-service-client/lib/interfaces.js @@ -13,3 +13,4 @@ export { Fetch } from "./service/fetch.js"; export { Git } from "./service/git.js"; export { MQTTInterface } from "./service/mqtt.js"; export { Files } from "./service/files.js"; +export {DataAccess} from "./service/data-access.js"; diff --git a/lib/js-service-client/lib/service-client.js b/lib/js-service-client/lib/service-client.js index e0efacadc..3dcdf8f0b 100644 --- a/lib/js-service-client/lib/service-client.js +++ b/lib/js-service-client/lib/service-client.js @@ -142,4 +142,5 @@ ServiceClient.define_interfaces( ["Git", SI.Git, ``], ["MQTT", SI.MQTTInterface, `mqtt_client`], ["Files", SI.Files, ``], + ["DataAccess", SI.DataAccess, ``] ); diff --git a/lib/js-service-client/lib/service/data-access.js b/lib/js-service-client/lib/service/data-access.js new file mode 100644 index 000000000..4d9eb1425 --- /dev/null +++ b/lib/js-service-client/lib/service/data-access.js @@ -0,0 +1,30 @@ +/* + * Factory+ NodeJS Utilities + * Data Access service interface. + */ +import { Service } from "../uuids.js"; +import { ServiceInterface } from "./service-interface.js"; + +/** Interface to the Data Access service + * + * + */ + +export class DataAccess extends ServiceInterface{ + constructor(fplus){ + super(fplus); + this.service = Service.DataAccess; + this.log = fplus.debug.bound("data-access"); + } + + /** Returns list of dataset uuids with read dataset permission. + * + */ + async get_dataset_uuids(){ + const [st, json,] = await this.fetch('v1/metadata'); + if(st === 404) return []; + if(st !== 200) + this.throw(`Can't get dataset uuids ${st} ${json}`); + return [json]; + } +} \ No newline at end of file diff --git a/lib/js-service-client/lib/service/discovery.js b/lib/js-service-client/lib/service/discovery.js index 2179f9dd0..b9fc24f06 100644 --- a/lib/js-service-client/lib/service/discovery.js +++ b/lib/js-service-client/lib/service/discovery.js @@ -35,6 +35,7 @@ export class Discovery extends ServiceInterface { [opts.directory_url, Service.Directory], [opts.mqtt_url, Service.MQTT], [opts.files_url, Service.Files], + [opts.dataAccess_url, Service.DataAccess] ]; for (const [pr, srv] of presets) { if (pr == null) continue; From 7f6ee6f037f4f27b03f6827b7d93eed8df03aa51 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 20 Apr 2026 13:26:30 +0100 Subject: [PATCH 34/96] data access dataflow - changed metadata structure and watching functional group --- acs-data-access/lib/dataflow.js | 41 +++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/acs-data-access/lib/dataflow.js b/acs-data-access/lib/dataflow.js index 6756ad289..d0fab9eb5 100644 --- a/acs-data-access/lib/dataflow.js +++ b/acs-data-access/lib/dataflow.js @@ -3,7 +3,7 @@ * Data-Flow / sequence management */ -import { Map as IMap } from "immutable"; +import { List as IList, Map as IMap } from "immutable"; import rx from "rxjs"; import * as rxx from "@amrc-factoryplus/rx-util"; @@ -23,9 +23,9 @@ export class DataFlow { this.dataset_definitions = this._build_dataset_definitions(); this.general_infos = this._build_general_info(); this.metadata = this._build_metadata(); + this.functional_types = this._build_functional_types(); } - run() { // this.dataset_definitions.subscribe(ss => // this.log("Dataset Definitions UPDATE %o", ss.toJS()) @@ -36,8 +36,28 @@ export class DataFlow { } - // 1. Watch direct members of Constants.App.DatasetMetadata - // 2. For each member -> Search App + // Watch member members of functional group to see what functional type datasets have + _build_functional_types() { + return rxx.rx( + this.cdb.watch_member_members(Constants.Group.FunctionalDatasetGroup), + rx.map(data => { + let result = IMap(); + + data.forEach((memberIds, groupId) => { + memberIds.forEach(memberId => { + result = result.update( + memberId, + list => (list ? list.push(groupId) : IList([groupId])) + ); + }); + }); + + return result; + }), + rxx.shareLatest() + ); + } + _build_metadata() { return rxx.rx( this.cdb.watch_members(Constants.App.DatasetMetadata), @@ -59,15 +79,18 @@ export class DataFlow { rx.map(entries => { let map = IMap(); - for (const [uuid, result] of entries) { - map = map.set(uuid, result); + for (const [outerUuid, result] of entries) { + for (const [innerUuid, payload] of result) { + const existing = map.get(innerUuid, IMap()); + + map = map.set(innerUuid, existing.set(outerUuid, payload)); + } } return map; }) ); }), - rxx.shareLatest() ); } @@ -118,6 +141,10 @@ export class DataFlow { ); } + get_functional_types(){ + return this.functional_types; + } + get_metadata(){ return this.metadata; } From 59756e9695a92e306e5fa6ab96783701961f54d9 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 20 Apr 2026 15:14:07 +0100 Subject: [PATCH 35/96] data access dataset metadata endpoint returning function --- acs-data-access/lib/api-v1.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 8d5356a9d..9d8f84063 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -79,8 +79,6 @@ export class APIv1 { return result; } - - /** GET. Returns a list of Dataset UUIDs that the client has READ access to. * The dataset can be optionally restricted by from and to dates. * Dates must be in the format ISO date-time string in UTC 2025-11-13T09:33:18.000Z @@ -105,8 +103,6 @@ export class APIv1 { * metadata {object} - Map of metadata configs keyed by Application UUID - contains all config entries for dataset which use applications in the Dataset metadata class * parts {array} - Subset datasets - contains UUIDs of all subclasses of dataset the client has READ access to. * Don't return valid response if dataset is invalid - * - */ async metadata_uuid(req, res){ const dataset_uuid = req.params.uuid; @@ -129,14 +125,15 @@ export class APIv1 { const info = infos.get(dataset_uuid); if(!info) return fail(404); - - + const f_types = await rx.firstValueFrom(this.data.get_functional_types()); + const f_type = f_types.get(dataset_uuid); + const meta = { uuid: dataset_uuid, name: info.name, + function: f_type } return res.status(200).json(meta); - } @@ -331,7 +328,6 @@ export class APIv1 { } - async _get_dataset_def_by_uuid(uuid){ const def = await rx.firstValueFrom( From dbe01f270d639ce670beb6750a4c139a14ba10d7 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 20 Apr 2026 15:58:57 +0100 Subject: [PATCH 36/96] data access data endpoint added header with filename --- acs-data-access/lib/api-v1.js | 1 + 1 file changed, 1 insertion(+) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 9d8f84063..9394918c6 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -381,6 +381,7 @@ export class APIv1 { const csv = this._convert_to_csv(rows); res.setHeader("Content-Type", "text/csv"); + res.setHeader("Content-Disposition", `attachment; filename=${dataset_uuid}.csv`); return res.status(200).send(csv); } catch (err) { From acd2a0f311c2d2fb2649b232af29ca8a5d476810 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 22 Apr 2026 16:01:40 +0100 Subject: [PATCH 37/96] data access service-client interface metadata and download methods --- .../lib/service/data-access.js | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/lib/js-service-client/lib/service/data-access.js b/lib/js-service-client/lib/service/data-access.js index 4d9eb1425..75ea7f0d3 100644 --- a/lib/js-service-client/lib/service/data-access.js +++ b/lib/js-service-client/lib/service/data-access.js @@ -17,14 +17,48 @@ export class DataAccess extends ServiceInterface{ this.log = fplus.debug.bound("data-access"); } - /** Returns list of dataset uuids with read dataset permission. - * + /** + * Returns list of dataset uuids with READ_DATASET permission. */ async get_dataset_uuids(){ - const [st, json,] = await this.fetch('v1/metadata'); + const [st, json] = await this.fetch('v1/metadata'); if(st === 404) return []; + if(st == 403) + this.throw(`Unauthorised to access dataset ${uuid}`); if(st !== 200) this.throw(`Can't get dataset uuids ${st} ${json}`); - return [json]; + return json; + } + + + /** + * Returns json object with dataset metadata + */ + async get_dataset_metadata(uuid){ + const [st, json] = await this.fetch(`v1/metadata/${uuid}`); + if(st == 404) return {}; + if(st == 403) + this.throw(`Unauthorised to access dataset ${uuid}`); + if(st != 200) + this.throw(`Can't get metadata for dataset ${uuid}\n ${st} ${json}`); + return json; + } + + /** + * Download data in csv format + */ + async download_data(uuid){ + const [st, json] = await this.fetch({ + url: `data/${uuid}`, + method: 'POST', + }); + + if(st == 404) return "Not Found"; + if(st == 403) + this.throw(`Unauthorised to access dataset ${uuid}`); + if(st != 200) + this.throw(`Can't download data for dataset ${uuid}\n ${st} ${json}`); + + return json; } } \ No newline at end of file From 4b879ca4e0735b1ebb0852d7aa00aa5280c4f639 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 22 Apr 2026 16:02:22 +0100 Subject: [PATCH 38/96] data access api minor fixes --- acs-data-access/lib/api-v1.js | 66 +++++++++++++++++------------------ 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 9394918c6..4795e91ca 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -104,7 +104,7 @@ export class APIv1 { * parts {array} - Subset datasets - contains UUIDs of all subclasses of dataset the client has READ access to. * Don't return valid response if dataset is invalid */ - async metadata_uuid(req, res){ + async metadata_uuid(req, res){ const dataset_uuid = req.params.uuid; if (!valid_uuid(dataset_uuid)) fail(410); @@ -125,13 +125,26 @@ export class APIv1 { const info = infos.get(dataset_uuid); if(!info) return fail(404); - const f_types = await rx.firstValueFrom(this.data.get_functional_types()); - const f_type = f_types.get(dataset_uuid); + const f_types = await rx.firstValueFrom(this.data.get_functional_types()); + const f_type = f_types.get(dataset_uuid); + + const all_metadata = await rx.firstValueFrom(this.data.get_metadata()); + const metadata = all_metadata.get(dataset_uuid); + + /** + * If queried dataset is session -> only use its from and to? + */ + const from = null; + const to = null; const meta = { uuid: dataset_uuid, name: info.name, - function: f_type + from: from ? from : undefined, + to: to ? to : undefined, + function: f_type, + metadata: metadata, + parts: [] } return res.status(200).json(meta); } @@ -169,17 +182,17 @@ export class APIv1 { */ async _check_second_level_permission(principal, structure, config, permission){ - if(structure == Constants.App.SessionLimits){ - + if(structure == Constants.App.SessionLimits || structure == Constants.App.SparkplugSrc){ + this.log(`second level ACL check for structure ${structure} and config ${config}`); const target = config.source; if(!target) return fail(422); const ok = await this.auth.check_acl( - principal, - permission, - target, - true - ); + principal, + permission, + target, + true + ); return ok; @@ -197,22 +210,10 @@ export class APIv1 { } return true; - }else if( structure == Constants.App.SparkplugSrc){ - - const target = config.source; - if(!target) return fail(422); - - const ok = await this.auth.check_acl( - principal, - permission, - target, - true - ); - - return ok; }else{ // Unhandled Structure type - return fail(500); + this.log("Unhandled Structure type") + return false; } } @@ -353,9 +354,6 @@ export class APIv1 { const dataset_uuid = req.params.uuid; if (!valid_uuid(dataset_uuid)) return fail(410); - const dataset_def = await this._get_dataset_def_by_uuid(dataset_uuid); - if (!dataset_def) return fail(404); - const ok = await this.auth.check_acl( req.auth, Constants.Perm.ReadDataset, @@ -364,13 +362,13 @@ export class APIv1 { ); if (!ok) return fail(403); - const structure = dataset_def.structure; - const config = dataset_def.config; + const dataset_def = await this._get_dataset_def_by_uuid(dataset_uuid); + if (!dataset_def) return fail(404); - const ok2 = this._check_second_level_permission( + const ok2 = await this._check_second_level_permission( req.auth, - structure, - config, + dataset_def?.structure, + dataset_def?.config, Constants.Perm.ReadDataset ); if (!ok2) return fail(403); @@ -385,7 +383,7 @@ export class APIv1 { return res.status(200).send(csv); } catch (err) { - console.error(err); + this.log(err); return fail(500); } } From fc3d61d8ce0ffb8238e137a048e9cd2765d3e135 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 23 Apr 2026 16:52:18 +0100 Subject: [PATCH 39/96] data access fixed bug with accessing imap items --- acs-data-access/lib/api-v1.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 4795e91ca..2704b279c 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -459,10 +459,13 @@ export class APIv1 { if (!ok) return fail(403); + this.log(`Passed ACL Check to CreateDataset for structure ${structure}`); + if(!config) return fail(422); // Check the principal has appropriate permission for all dataset sources in config. - const ok2 = await this._check_second_level_permission(principal, structure, config, mapStructureToPermission[structure]); + this.log(`Now will try ACL check ${mapStructureToPermission.get(structure)} for ${config.source} `); + const ok2 = await this._check_second_level_permission(principal, structure, config, mapStructureToPermission.get(structure)); if(!ok2) return fail(403); From 284efbaf6348e1d53829305c81873584adf3f4b6 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 23 Apr 2026 16:53:06 +0100 Subject: [PATCH 40/96] data access interface for js-service-client implemented --- .../lib/service/data-access.js | 94 +++++++++++++++++-- 1 file changed, 88 insertions(+), 6 deletions(-) diff --git a/lib/js-service-client/lib/service/data-access.js b/lib/js-service-client/lib/service/data-access.js index 75ea7f0d3..0f4c42782 100644 --- a/lib/js-service-client/lib/service/data-access.js +++ b/lib/js-service-client/lib/service/data-access.js @@ -2,6 +2,8 @@ * Factory+ NodeJS Utilities * Data Access service interface. */ +import { writeFile } from 'fs/promises'; + import { Service } from "../uuids.js"; import { ServiceInterface } from "./service-interface.js"; @@ -20,7 +22,7 @@ export class DataAccess extends ServiceInterface{ /** * Returns list of dataset uuids with READ_DATASET permission. */ - async get_dataset_uuids(){ + async get_readable_dataset_uuids(){ const [st, json] = await this.fetch('v1/metadata'); if(st === 404) return []; if(st == 403) @@ -47,18 +49,98 @@ export class DataAccess extends ServiceInterface{ /** * Download data in csv format */ - async download_data(uuid){ + async download_data(uuid) { + const [st, csv] = await this.fetch({ + url: `v1/data/${uuid}`, + method: 'POST', + }); + + if (st === 404) return "Not Found"; + if (st === 403) + throw new Error(`Unauthorised to access dataset ${uuid}`); + if (st != 200) + throw new Error(`Can't download data for dataset ${uuid} ${res.status}`); + + + // const buffer = Buffer.from(await res.arrayBuffer()); + + // // filename (optional) + // const disposition = res.headers.get('content-disposition'); + // let filename = `${uuid}.csv`; + // if (disposition?.includes('filename=')) { + // filename = disposition.split('filename=')[1]; + // } + + // await writeFile(filename, buffer); + + // return filename; + } + + + async get_editable_dataset_uuids(){ + const [st, json] = await this.fetch('v1/structure'); + if(st == 404) return []; + if(st == 403) + this.throw(`Unauthorised to access dataset ${uuid}`); + if(st != 200) + this.throw(`Can't get dataset uuids ${st} ${json}`); + return json; + } + + /** + * + * @param {*} structure is one of the Dataset Definition app uuids src, session or union. + * @param {*} config is the config entry content of dataset + */ + async create_dataset(structure, config){ const [st, json] = await this.fetch({ - url: `data/${uuid}`, - method: 'POST', + url:'v1/structure', + method: 'POST', + body: { + structure, + config + } }); - if(st == 404) return "Not Found"; + if(st == 404) return ""; + if(st == 403) + this.throw(`Unauthorised to create dataset`); + if(st != 200) + this.throw(`Can't create dataset ${st} ${json}`); + return json; + } + + + + /** + * Returns dataset definition (content of the config entry) + */ + + async get_dataset_definition(uuid){ + const [st, json] = await this.fetch(`v1/structure/${uuid}`); + if(st == 404) return []; if(st == 403) this.throw(`Unauthorised to access dataset ${uuid}`); if(st != 200) - this.throw(`Can't download data for dataset ${uuid}\n ${st} ${json}`); + this.throw(`Can't get dataset definition ${st} ${json}`); + return json; + } + + async update_dataset_definition(uuid, structure, config){ + const [st, json] = await this.fetch({ + url: `v1/structure/${uuid}`, + method: 'PUT', + body: { + structure, + config + } + }); + if(st == 404) return []; + if(st == 403) + this.throw(`Unauthorised to update dataset ${uuid}`); + if(st != 200) + this.throw(`Can't update dataset definition ${st} ${json}`); return json; } } \ No newline at end of file From dd00298bb995956823ef8da31e5e40efe5840352 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Fri, 24 Apr 2026 12:43:20 +0100 Subject: [PATCH 41/96] data access implemented download endpoint and fixed minor bugs --- acs-data-access/lib/api-v1.js | 325 +++++++++++++++++--------------- acs-data-access/lib/dataflow.js | 34 ++++ acs-data-access/lib/utils.js | 11 ++ 3 files changed, 215 insertions(+), 155 deletions(-) create mode 100644 acs-data-access/lib/utils.js diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 2704b279c..20090ac77 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -1,6 +1,6 @@ /* * ACS Data Access Service -* APIV1 +* APIv1 */ import express from "express"; @@ -11,6 +11,7 @@ import {InfluxDB, flux} from '@influxdata/influxdb-client'; import { APIError } from "@amrc-factoryplus/service-api"; import { DataAccess as Constants } from "./constants.js"; import { valid_uuid, DatasetValidity } from "./validate.js"; +import {convert_to_csv} from './utils.js'; function fail(status, message) { throw new APIError(status); @@ -56,38 +57,17 @@ export class APIv1 { return api; } - async _get_allowed_dataset_uuids(principal, permission, dataset_validity) { - const dataset_def_obs = this.data.get_dataset_definitions(dataset_validity); - - const result = await rx.firstValueFrom( - rx.combineLatest([ - dataset_def_obs, - this.auth.watch_acl_with_perm(principal, permission) - ]).pipe( - rx.map(([datasets, targets]) => { - // datasets: IMap - // targets: Set or ISet of allowed datasetIds - - return datasets - .keySeq() // get all dataset IDs - .filter(id => targets.has(id)) - .toArray(); // convert to plain array - }) - ) - ); - return result; - } - /** GET. Returns a list of Dataset UUIDs that the client has READ access to. - * The dataset can be optionally restricted by from and to dates. + /** GET. Returns a list of Dataset UUIDs that the client has READ_DATASET access to. + * The dataset can be optionally restricted by from and to dates (this is not implemented yet). * Dates must be in the format ISO date-time string in UTC 2025-11-13T09:33:18.000Z * @param from {date} (optional, inclusive) query param * @param to {date} (optional, inclusive) query param * Don't return invalid datasets */ async metadata_list(req, res) { - const uuids = await this._get_allowed_dataset_uuids(req.auth, Constants.Perm.ReadDataset, DatasetValidity.VALID); + const uuids = await this.data.get_allowed_dataset_uuids(req.auth, Constants.Perm.ReadDataset, DatasetValidity.VALID); return res.status(200).json(uuids); } @@ -106,7 +86,7 @@ export class APIv1 { */ async metadata_uuid(req, res){ const dataset_uuid = req.params.uuid; - if (!valid_uuid(dataset_uuid)) fail(410); + if (!valid_uuid(dataset_uuid)) fail(410, `${dataset_uuid} is invalid uuid.`); const ok = await this.auth.check_acl( req.auth, @@ -115,15 +95,15 @@ export class APIv1 { true, ); - if (!ok) return fail(403); + if (!ok) return fail(403, `You don't have Read permission for ${dataset_uuid}`); - const datasets = await this._get_allowed_dataset_uuids(req.auth, Constants.Perm.ReadDataset, DatasetValidity.VALID); + const datasets = await this.data.get_allowed_dataset_uuids(req.auth, Constants.Perm.ReadDataset, DatasetValidity.VALID); const is_valid = await datasets.includes(dataset_uuid) - if(!is_valid) return fail(404); + if(!is_valid) return fail(404, `Dataset ${dataset_uuid} is not found: it's either invalid or does not exist.`); const infos = await rx.firstValueFrom(this.data.get_general_infos()); const info = infos.get(dataset_uuid); - if(!info) return fail(404); + if(!info) return fail(404, `General info does not exist for dataset ${dataset_uuid}`); const f_types = await rx.firstValueFrom(this.data.get_functional_types()); const f_type = f_types.get(dataset_uuid); @@ -132,8 +112,8 @@ export class APIv1 { const metadata = all_metadata.get(dataset_uuid); /** - * If queried dataset is session -> only use its from and to? - */ + * if the queried dataset does not have from to time period -> recursively find the earliest and latest timestamps from source datasets. + */ const from = null; const to = null; @@ -185,7 +165,8 @@ export class APIv1 { if(structure == Constants.App.SessionLimits || structure == Constants.App.SparkplugSrc){ this.log(`second level ACL check for structure ${structure} and config ${config}`); const target = config.source; - if(!target) return fail(422); + if(!target) return fail(422, `Dataset definition does not contain source.`); + if(!valid_uuid(target)) return fail(422, `Source uuid ${target} is invalid.`); const ok = await this.auth.check_acl( principal, @@ -198,6 +179,7 @@ export class APIv1 { }else if(structure == Constants.App.UnionComponents){ if(config.length == 0) return true; + if(!Array.isArray(config)) return fail(422, `Dataset def of structure ${structure} must be Array.`); for(let target of config){ const ok = await this.auth.check_acl( @@ -212,8 +194,7 @@ export class APIv1 { }else{ // Unhandled Structure type - this.log("Unhandled Structure type") - return false; + return fail(422, `Structure ${structure} is unknown`); } } @@ -268,91 +249,143 @@ export class APIv1 { } + _merge_intervals(queries){ + const grouped = {}; + + for (const q of queries){ + if(!grouped[q.source]) grouped[q.source] = []; + grouped[q.source].push(q); + } + + const result = []; + + for(const source in grouped){ + const intervals = grouped[source] + .map(q => ({ + from: q.from ? new Date(q.from) : new Date(0), + to: q.to ? new Date(q.to) : new Date() + })) + .sort((a, b) => a.from - b.from); + + + let merged = []; + + for(const interval of intervals){ + if(!merged.length){ + merged.push(interval); + continue; + } + const last = merged[merged.length - 1]; + + if(interval.from <= last.to){ + last.to = new Date(Math.max(last.to, interval.to)); + }else{ + merged.push(interval); + } + } + + for (const m of merged){ + result.push({ + source, + from: m.from, + to: m.to + }); + } + } + return result; + } + - async _get_dataset_data(dataset_uuid, visited = new Set()) { + async _resolve_dataset(dataset_uuid, from=null, to=null, visited = new Set()) { if (visited.has(dataset_uuid)) { - throw new Error(`Circular dataset reference detected at ${dataset_uuid}`); + return fail(404, `Circular dataset reference detected at ${dataset_uuid}`); } visited.add(dataset_uuid); - const dataset_def = await this._get_dataset_def_by_uuid(dataset_uuid); - if (!dataset_def) return []; + const dataset_def = await this.data.get_dataset_def_by_uuid(dataset_uuid); + if (!dataset_def) return fail(404, `Dataset definition not found for ${dataset_uuid}`); const { structure, config } = dataset_def; + if(!structure) return fail(422, `Structure is not found in dataset definition ${dataset_uuid}`); + if(!valid_uuid(structure)) return fail(422, `Structure uuid is invalid for dataset ${dataset_uuid}`); // ------------------------- // 1. SparkplugSrc (BASE CASE) // ------------------------- if (structure === Constants.App.SparkplugSrc) { const source_uuid = config?.source; - if (!source_uuid) return []; - - // NOTE: here source_uuid is the topLevelInstance for influx - const data = await this._run_influx_query(source_uuid); - return data; + + if (!source_uuid) return fail(422, `Dataset def ${dataset_uuid} does not contain source uuid.`); + + if(!valid_uuid(source_uuid)) return fail(422, `Dataset ${dataset_uuid}'s source uuid is invalid.`); + + return [{ + source: source_uuid, + from, + to + }]; } + + // ------------------------- // 2. SessionLimits (SINGLE REFERENCE) // ------------------------- else if (structure === Constants.App.SessionLimits) { const source_uuid = config?.source; - if (!source_uuid) return []; + if (!source_uuid) return fail(422, `Dataset def ${dataset_uuid} does not contain source uuid`); - return this._get_dataset_data(source_uuid, visited); + if(!valid_uuid(source_uuid)) return fail(422, `Dataset ${dataset_uuid}'s source is invalid uuid`); + + const session_from = config?.from ? new Date(config.from) : null; + const session_to = config?.to ? new Date(config.to) : null; + + const new_from = from && session_from + ? new Date(Math.max(from, session_from)) + : (from || session_from); + + const new_to = to && session_to + ? new Date(Math.min(to, session_to)) + : (to || session_to); + + if(new_from && new_to && new_from >= new_to){ + return []; // no overlap + } + + return this._resolve_dataset(source_uuid, new_from, new_to, visited); } + + // ------------------------- // 3. UnionComponents (MULTIPLE REFERENCES) // ------------------------- else if (structure === Constants.App.UnionComponents) { - const dataset_uuids = config; - - if (!Array.isArray(dataset_uuids)) return []; - + if(!Array.isArray(config)) return fail(422, `Union structure type dataset def must be Array ${dataset_uuid}`); const results = await Promise.all( - dataset_uuids.map(uuid => - this._get_dataset_data(uuid, new Set(visited)) // isolate branch - ) + config.map(uuid => + this._resolve_dataset(uuid, from, to, new Set(visited)) + ) ); return results.flat(); } + + // ------------------------- // 4. Unknown structure // ------------------------- else { - throw new Error(`Unhandled structure type: ${structure}`); + return fail(422, `Dataset ${dataset_uuid} has unknown structure type ${structure}`); } } - - async _get_dataset_def_by_uuid(uuid){ - const def = await rx.firstValueFrom( - this.data.get_dataset_definitions(DatasetValidity.VALID).pipe( - rx.map(datasets => datasets.get(uuid)?.[0]) - ) - ); - return def; - } - - _convert_to_csv(rows) { - if (!rows.length) return ""; - - const headers = Object.keys(rows[0]); - const lines = rows.map(row => - headers.map(h => JSON.stringify(row[h] ?? "")).join(",") - ); - - return [headers.join(","), ...lines].join("\n"); - } - async dataset_data(req, res) { const dataset_uuid = req.params.uuid; - if (!valid_uuid(dataset_uuid)) return fail(410); + if (!valid_uuid(dataset_uuid)) return fail(422, `Dataset uuid ${dataset_uuid} is invalid.`); const ok = await this.auth.check_acl( req.auth, @@ -360,10 +393,10 @@ export class APIv1 { dataset_uuid, true, ); - if (!ok) return fail(403); + if (!ok) return fail(403, `You don't have Read permission for dataset ${dataset_uuid}`); - const dataset_def = await this._get_dataset_def_by_uuid(dataset_uuid); - if (!dataset_def) return fail(404); + const dataset_def = await this.data.get_dataset_def_by_uuid(dataset_uuid); + if (!dataset_def) return fail(404, `Dataset def ${dataset_uuid} not found or invalid.`); const ok2 = await this._check_second_level_permission( req.auth, @@ -371,12 +404,25 @@ export class APIv1 { dataset_def?.config, Constants.Perm.ReadDataset ); - if (!ok2) return fail(403); + if (!ok2) return fail(403, `You don't have permissions to Read the contents of dataset ${dataset_uuid}.`); try { - const rows = await this._get_dataset_data(dataset_uuid); + const resolved = await this._resolve_dataset(dataset_uuid); + + const merged = this._merge_intervals(resolved); + + const results = await Promise.all( + merged.map(q => + this._run_influx_query(q.source, { + from: q.from?.toISOString(), + to: q.to?.toISOString() + }) + ) + ); + + const rows = results.flat(); - const csv = this._convert_to_csv(rows); + const csv = convert_to_csv(rows); res.setHeader("Content-Type", "text/csv"); res.setHeader("Content-Disposition", `attachment; filename=${dataset_uuid}.csv`); @@ -388,6 +434,22 @@ export class APIv1 { } } + + + + + + + + + + + + + + + + /** GET. * * @param {*} req @@ -396,7 +458,7 @@ export class APIv1 { * the response should include invalid datasets */ async structure_list(req, res){ - const uuids = await this._get_allowed_dataset_uuids(req.auth, Constants.Perm.EditDataset, DatasetValidity.ALL); + const uuids = await this.data.get_allowed_dataset_uuids(req.auth, Constants.Perm.EditDataset, DatasetValidity.ALL); return res.status(200).json(uuids); } @@ -415,7 +477,7 @@ export class APIv1 { async structure_uuid(req, res) { const { uuid } = req.params; - if (!valid_uuid(uuid)) return fail(410); + if (!valid_uuid(uuid)) return fail(422, `Dataset uuid ${uuid} is invalid.`); const ok = await this.auth.check_acl( req.auth, @@ -424,7 +486,7 @@ export class APIv1 { true, ); - if (!ok) return fail(403); + if (!ok) return fail(403, `You don't have permission to Edit dataset ${uuid}.`); const definition = await rx.firstValueFrom( this.data.get_dataset_definitions(DatasetValidity.ALL).pipe( @@ -432,7 +494,7 @@ export class APIv1 { ) ); - if (!definition) return fail(404); + if (!definition) return fail(404, `Dataset def with uuid ${uuid} not found.`); if (definition.length > 1){ for(let def of definition){ @@ -444,12 +506,10 @@ export class APIv1 { } - async _update_dataset_config(principal, structure, config, objectUuid){ - if(!structure) return fail(422); + if(!structure) return fail(422, `Structure not provided.`); + if(!valid_uuid(structure)) return fail(422, `Structure uuid ${structure} is invalid.`) - if (!valid_uuid(structure)) return fail(410); - const ok = await this.auth.check_acl( principal, Constants.Perm.CreateDataset, @@ -457,33 +517,34 @@ export class APIv1 { true ); - if (!ok) return fail(403); + if (!ok) return fail(403, `You don't have Create permission for structure ${structure}`); this.log(`Passed ACL Check to CreateDataset for structure ${structure}`); - if(!config) return fail(422); + if(!config) return fail(422, `Config not provided.`); // Check the principal has appropriate permission for all dataset sources in config. this.log(`Now will try ACL check ${mapStructureToPermission.get(structure)} for ${config.source} `); const ok2 = await this._check_second_level_permission(principal, structure, config, mapStructureToPermission.get(structure)); - if(!ok2) return fail(403); + if(!ok2) return fail(403, `You don't have ${mapStructureToPermission.get(structure)} permission for dataset source(s).`); // Create new Dataset Object if(!objectUuid){ objectUuid = await this.cdb.create_object(Constants.Class.Dataset); + if(!objectUuid) return fail(400, `New Dataset object couldn't be created in ConfigDB.`); + this.log("Created new dataset object in ConfigDB", objectUuid); - - if(!objectUuid) return fail(400); } // Create config entry for the dataset object try{ await this.cdb.put_config(structure, objectUuid, config); + this.log("Updated dataset def in ConfigDB", objectUuid); }catch(err){ - return fail(422); + return fail(422, `Config entry for ${objectUuid} couldn't be updated.`); } return objectUuid; @@ -504,7 +565,7 @@ export class APIv1 { null ); - if(!objectUuid) return fail(500); + if(!objectUuid) return fail(422, `Failed to create new Dataset.`); return res.status(200).json(objectUuid); } @@ -517,81 +578,35 @@ export class APIv1 { */ async structure_update(req, res){ + const dataset_uuid = req.params.uuid; + if(!valid_uuid(dataset_uuid)) return fail(422, `Dataset uuid is invalid.`); + const objectUuid = await this._update_dataset_config( req.auth, req.body.structure, req.body.config, - req.params.uuid + dataset_uuid ); - if(!objectUuid) return fail(500); + if(!objectUuid) return fail(422, `Failed to update dataset ${dataset_uuid}`); // remove definitions from other structure apps const invalid_defs = await rx.firstValueFrom( this.data.get_dataset_definitions(DatasetValidity.INVALID).pipe( - rx.map(datasets => datasets.get(req.params.uuid)), // extract defs - rx.filter(defs => defs && defs.length), // ensure exists - rx.map(defs => defs.filter(d => d.structure !== req.body.structure)) // filter array + rx.map(datasets => datasets.get(dataset_uuid) ?? []), + rx.map(defs => defs.filter(d => d.structure !== req.body.structure)), + rx.map(filtered => filtered.length ? filtered : undefined) ) ); - if(invalid_defs){ - for (let i_def of invalid_defs){ + if (invalid_defs?.length) { + this.log(`Deleting invalid defs from ConfigDB`); + + for (const i_def of invalid_defs) { this.cdb.delete_config(i_def.structure, objectUuid); } } return res.status(200).json(objectUuid); } - - - async name_get(req, res) { - const { uuid } = req.params; - - // This URL could not possibly exist (different from "could exist in the future") - if (!valid_uuid(uuid)) fail(410); - - const names = await rx.firstValueFrom(this.data.names); - const name = names.get(uuid); - if (!name) fail(404); - - const ok = await this.auth.check_acl( - // Principal - req.auth, - // Your own service permission - Perm.MyGetNamePermission, - // Target UUID - uuid, - // Do we accept wildcards? - true, - ); - - if (!ok) { - fail(403); - } - - return res.status(200).json(name); - } - - async name_put(req, res) { - const { uuid } = req.params; - if (!valid_uuid(uuid)) fail(410); - - // May need this in your own service. We don't in this case. This is checking validity of what you give the service. - // if (grant && !valid_grant(grant)) fail(422); - - const ok = await this.auth.check_acl( - req.auth, - Perm.MyPutNamePermission, - uuid, - true, - ); - - if (!ok) { - fail(403); - } - - const retval = await this.data.request({ type: "name", object: uuid, name: req.body }); - return res.status(retval.status).end(); - } } diff --git a/acs-data-access/lib/dataflow.js b/acs-data-access/lib/dataflow.js index d0fab9eb5..c4c2a13c9 100644 --- a/acs-data-access/lib/dataflow.js +++ b/acs-data-access/lib/dataflow.js @@ -154,6 +154,40 @@ export class DataFlow { } + async get_allowed_dataset_uuids(principal, permission, dataset_validity) { + const dataset_def_obs = this.get_dataset_definitions(dataset_validity); + + const result = await rx.firstValueFrom( + rx.combineLatest([ + dataset_def_obs, + this.auth.watch_acl_with_perm(principal, permission) + ]).pipe( + rx.map(([datasets, targets]) => { + // datasets: IMap + // targets: Set or ISet of allowed datasetIds + + return datasets + .keySeq() // get all dataset IDs + .filter(id => targets.has(id)) + .toArray(); // convert to plain array + }) + ) + ); + + return result; + } + + + async get_dataset_def_by_uuid(uuid){ + const def = await rx.firstValueFrom( + this.get_dataset_definitions(DatasetValidity.VALID).pipe( + rx.map(datasets => datasets.get(uuid)?.[0]) + ) + ); + return def; + } + + get_dataset_definitions(validity){ if(validity == DatasetValidity.VALID){ return this._get_valid_dataset_definitions(); diff --git a/acs-data-access/lib/utils.js b/acs-data-access/lib/utils.js new file mode 100644 index 000000000..abad300fc --- /dev/null +++ b/acs-data-access/lib/utils.js @@ -0,0 +1,11 @@ + +export const convert_to_csv = (rows) => { + if (!rows.length) return ""; + + const headers = Object.keys(rows[0]); + const lines = rows.map(row => + headers.map(h => JSON.stringify(row[h] ?? "")).join(",") + ); + + return [headers.join(","), ...lines].join("\n"); +} \ No newline at end of file From 03320c83d99b9e06551cc9d3a31ed0ec57be8544 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Fri, 24 Apr 2026 14:45:51 +0100 Subject: [PATCH 42/96] data access put influx logic to separate script --- acs-data-access/bin/api.js | 14 +- acs-data-access/lib/api-v1.js | 203 +++++++++++++-------------- acs-data-access/lib/influx-reader.js | 87 ++++++++++++ acs-data-access/lib/utils.js | 15 +- 4 files changed, 208 insertions(+), 111 deletions(-) create mode 100644 acs-data-access/lib/influx-reader.js diff --git a/acs-data-access/bin/api.js b/acs-data-access/bin/api.js index 20dd617d7..c03f5ecae 100644 --- a/acs-data-access/bin/api.js +++ b/acs-data-access/bin/api.js @@ -12,6 +12,7 @@ import { WebAPI } from '@amrc-factoryplus/service-api'; import {DataFlow} from '../lib/dataflow.js'; import { APIv1 } from '../lib/api-v1.js'; import {DataAccessNotify} from '../lib/notify.js'; +import { InfluxReader } from '../lib/influx-reader.js'; const { env } = process; @@ -35,14 +36,19 @@ const data = new DataFlow({ fplus }); +const influxReader = new InfluxReader({ + debug, + influx_client: influxClient, + influx_org: env.INFLUXDB_ORG, + influx_bucket: env.INFLUXDB_BUCKET, +}) const apiv1 = new APIv1({ - data, debug, + data, + debug, auth: fplus.Auth, cdb: fplus.ConfigDB, - influx_client: influxClient, - influx_org: env.INFLUXDB_ORG, - influx_bucket: env.INFLUXDB_BUCKET, + influxReader }); const api = await new WebAPI({ diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 20090ac77..b941b38f6 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -6,12 +6,11 @@ import express from "express"; import { Map as IMap } from "immutable"; import * as rx from "rxjs"; -import {InfluxDB, flux} from '@influxdata/influxdb-client'; import { APIError } from "@amrc-factoryplus/service-api"; import { DataAccess as Constants } from "./constants.js"; import { valid_uuid, DatasetValidity } from "./validate.js"; -import {convert_to_csv} from './utils.js'; +import {convert_to_csv, minDate, maxDate} from './utils.js'; function fail(status, message) { throw new APIError(status); @@ -28,9 +27,7 @@ export class APIv1 { this.auth = opts.auth; this.cdb = opts.cdb; this.log = opts.debug.bound("apiv1"); - this.influx_bucket = opts.influx_bucket; - this.influx_org = opts.influx_org; - this.influx_query_api = opts.influx_client.getQueryApi(opts.influx_org); + this.influxReader = opts.influxReader; this.routes = this.setup_routes(); } @@ -71,6 +68,66 @@ export class APIv1 { return res.status(200).json(uuids); } + + async _get_dataset_time_bounds(dataset_uuid, visited=new Set()){ + if(visited.has(dataset_uuid)) return fail(422, `Cycle detected at dataset ${dataset_uuid}`); + + visited.add(dataset_uuid); + + const def = await this.data.get_dataset_def_by_uuid(dataset_uuid); + if(!def) return fail(404, `Dataset def for ${dataset_uuid} not found`); + + const {structure, config} = def; + + // ------------------------ + // 1. SESSION + // ------------------------ + if(structure === Constants.App.SessionLimits){ + const child = await this._get_dataset_time_bounds(config.source, visited); + + const from = config.from ? new Date(config.from) : null; + const to = config.to ? new Date(config.to) : null; + + return { + from: maxDate(from, child.from), + to: minDate(to, child.to) + }; + } + + + // ------------------------ + // 2. SPARKPLUG SRC + // ------------------------ + if(structure === Constants.App.SparkplugSrc){ + const {from, to} = await this._get_influx_bounds(config.source); + + return {from, to} + } + + + // ------------------------ + // 3. UNION + // ------------------------ + if(structure === Constants.App.UnionComponents){ + const results = await Promise.all( + config.map(source_uuid => + this._get_dataset_time_bounds(source_uuid, new Set(visited)) + ) + ); + + const valid = results.filter(r => r.from && r.to); + + if(!valid.length) return {from: null, to: null}; + + return { + from: new Date(Math.min(...valid.map(v => v.from))), + to: new Date(Math.max(...valid.map(v => v.to))) + }; + } + + return {from: null, to: null}; + } + /** GET. Accepts Dataset UUID and returns metadata about a Published dataset. * @param uuid {request param} * @param @@ -114,14 +171,13 @@ export class APIv1 { /** * if the queried dataset does not have from to time period -> recursively find the earliest and latest timestamps from source datasets. */ - const from = null; - const to = null; + const {from, to} = await this._get_dataset_time_bounds(dataset_uuid); const meta = { uuid: dataset_uuid, name: info.name, - from: from ? from : undefined, - to: to ? to : undefined, + from: from, + to: to, function: f_type, metadata: metadata, parts: [] @@ -130,37 +186,6 @@ export class APIv1 { } - /** POST. Queries all possible Influx suffixes, combines the measuremenets and returns actual data from a dataset. - * Empty POST body requests all dataset measurements. - * @param {*} req - * @param {*} res - * @returns CSV with columns: - * device - Device this data point comes from - * metric - metric name (don't include Influx :x suffix) - * timestamp - ISO string - * value - actual data value - * unit - Engineering unit (if available) - * Don't return valid response if dataset is invalid - - Influx tags: - [ - "_start", - "_stop", - "_field", - "_measurement", - "bottomLevelInstance", - "bottomLevelSchema", - "device", - "group", - "node", - "path", - "topLevelInstance", - "topLevelSchema", - "usesInstances", - "usesSchemas" - ] - */ - async _check_second_level_permission(principal, structure, config, permission){ if(structure == Constants.App.SessionLimits || structure == Constants.App.SparkplugSrc){ this.log(`second level ACL check for structure ${structure} and config ${config}`); @@ -198,55 +223,7 @@ export class APIv1 { } } - async _run_influx_query(topLevelInstance, meta = {}) { - const { from, to, measurement } = meta; - - // Build range - const rangeClause = (from && to) - ? `|> range(start: ${from}, stop: ${to})` - : `|> range(start: -5m)`; - - // Optional measurement filter - const measurementFilter = measurement - ? `|> filter(fn: (r) => r._measurement == "${measurement}")` - : ``; - - const query = ` - import "strings" - - from(bucket: "${this.influx_bucket}") - ${rangeClause} - |> filter(fn: (r) => r.topLevelInstance == "${topLevelInstance}") - ${measurementFilter} - |> drop(columns: [ - "_start","_stop","_field","table", - "bottomLevelInstance","bottomLevelSchema", - "group","node","topLevelSchema", - "usesInstances","usesSchemas" - ]) - |> map(fn: (r) => ({ r with path: if exists r.path then r.path else "" })) - |> pivot( - rowKey: ["_time"], - columnKey: ["_measurement"], - valueColumn: "_value" - ) - |> group() - |> sort(columns: ["_time"], desc: false) - `; - - return new Promise((resolve, reject) => { - const rows = []; - - this.influx_query_api.queryRows(query, { - next: (row, tableMeta) => { - const o = tableMeta.toObject(row); - rows.push(o); - }, - error: reject, - complete: () => resolve(rows) - }); - }); - } + _merge_intervals(queries){ @@ -382,7 +359,36 @@ export class APIv1 { } } - + /** POST. Queries all possible Influx suffixes, combines the measuremenets and returns actual data from a dataset. + * Empty POST body requests all dataset measurements. + * @param {*} req + * @param {*} res + * @returns CSV with columns: + * device - Device this data point comes from + * metric - metric name (don't include Influx :x suffix) + * timestamp - ISO string + * value - actual data value + * unit - Engineering unit (if available) + * Don't return valid response if dataset is invalid + + Influx tags: + [ + "_start", + "_stop", + "_field", + "_measurement", + "bottomLevelInstance", + "bottomLevelSchema", + "device", + "group", + "node", + "path", + "topLevelInstance", + "topLevelSchema", + "usesInstances", + "usesSchemas" + ] + */ async dataset_data(req, res) { const dataset_uuid = req.params.uuid; if (!valid_uuid(dataset_uuid)) return fail(422, `Dataset uuid ${dataset_uuid} is invalid.`); @@ -413,7 +419,7 @@ export class APIv1 { const results = await Promise.all( merged.map(q => - this._run_influx_query(q.source, { + this.influxReader.get_dataset_data(q.source, { from: q.from?.toISOString(), to: q.to?.toISOString() }) @@ -435,21 +441,6 @@ export class APIv1 { } - - - - - - - - - - - - - - - /** GET. * * @param {*} req diff --git a/acs-data-access/lib/influx-reader.js b/acs-data-access/lib/influx-reader.js new file mode 100644 index 000000000..3d2e8ad9c --- /dev/null +++ b/acs-data-access/lib/influx-reader.js @@ -0,0 +1,87 @@ + +import {InfluxDB, flux} from '@influxdata/influxdb-client'; + +export class InfluxReader{ + constructor(opts){ + this.log = opts.debug.bound("influxReader"); + this.influx_bucket = opts.influx_bucket; + this.influx_org = opts.influx_org; + this.influx_query_api = opts.influx_client.getQueryApi(opts.influx_org); + } + + async get_dataset_data(topLevelInstance, meta = {}) { + const { from, to, measurement } = meta; + + // Build range + const rangeClause = (from && to) + ? `|> range(start: ${from}, stop: ${to})` + : `|> range(start: -5m)`; + + // Optional measurement filter + const measurementFilter = measurement + ? `|> filter(fn: (r) => r._measurement == "${measurement}")` + : ``; + + const query = ` + import "strings" + + from(bucket: "${this.influx_bucket}") + ${rangeClause} + |> filter(fn: (r) => r.topLevelInstance == "${topLevelInstance}") + ${measurementFilter} + |> drop(columns: [ + "_start","_stop","_field","table", + "bottomLevelInstance","bottomLevelSchema", + "group","node","topLevelSchema", + "usesInstances","usesSchemas" + ]) + |> map(fn: (r) => ({ r with path: if exists r.path then r.path else "" })) + |> pivot( + rowKey: ["_time"], + columnKey: ["_measurement"], + valueColumn: "_value" + ) + |> group() + |> sort(columns: ["_time"], desc: false) + `; + const res = await this._run_influx(query); + return res; + } + + async _run_min_max_time_agg(source_uuid, fn) { + const query = ` + from(bucket: "${this.influx_bucket}") + |> range(start: 0) + |> filter(fn: (r) => r.topLevelInstance == "${source_uuid}") + |> keep(columns: ["_time"]) + |> group() + |> ${fn}(column: "_time") + `; + + const rows = await this.run_flux(query); + return rows?.[0]?._value ?? null; + } + + async get_influx_time_bounds(topLevelInstance) { + const [from, to] = await Promise.all([ + this._run_min_max_time_agg(topLevelInstance, "min"), + this._run_min_max_time_agg(topLevelInstance, "max"), + ]); + + return { from, to }; + } + + async _run_influx(query) { + return new Promise((resolve, reject) => { + const rows = []; + + this.influx_query_api.queryRows(query, { + next: (row, tableMeta) => { + rows.push(tableMeta.toObject(row)); + }, + error: reject, + complete: () => resolve(rows) + }); + }); + } +} \ No newline at end of file diff --git a/acs-data-access/lib/utils.js b/acs-data-access/lib/utils.js index abad300fc..d35f6c7e2 100644 --- a/acs-data-access/lib/utils.js +++ b/acs-data-access/lib/utils.js @@ -1,5 +1,5 @@ -export const convert_to_csv = (rows) => { +export function convert_to_csv (rows){ if (!rows.length) return ""; const headers = Object.keys(rows[0]); @@ -8,4 +8,17 @@ export const convert_to_csv = (rows) => { ); return [headers.join(","), ...lines].join("\n"); +} + + +export function minDate(a, b) { + if (!a) return b; + if (!b) return a; + return a < b ? a : b; +} + +export function maxDate(a, b) { + if (!a) return b; + if (!b) return a; + return a > b ? a : b; } \ No newline at end of file From a9fb232626f210a39f227f87be9dc1d7e22b22e0 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Fri, 24 Apr 2026 14:49:42 +0100 Subject: [PATCH 43/96] data access influx reader fixed typo --- acs-data-access/lib/influx-reader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acs-data-access/lib/influx-reader.js b/acs-data-access/lib/influx-reader.js index 3d2e8ad9c..319be51f3 100644 --- a/acs-data-access/lib/influx-reader.js +++ b/acs-data-access/lib/influx-reader.js @@ -58,7 +58,7 @@ export class InfluxReader{ |> ${fn}(column: "_time") `; - const rows = await this.run_flux(query); + const rows = await this._run_influx(query); return rows?.[0]?._value ?? null; } From 2b3e233bd2f319f10596eaccd49a5e70e173d64e Mon Sep 17 00:00:00 2001 From: amrc-za Date: Fri, 24 Apr 2026 14:50:36 +0100 Subject: [PATCH 44/96] data access api fixed bug --- acs-data-access/lib/api-v1.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index b941b38f6..26d75ad57 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -99,7 +99,7 @@ export class APIv1 { // 2. SPARKPLUG SRC // ------------------------ if(structure === Constants.App.SparkplugSrc){ - const {from, to} = await this._get_influx_bounds(config.source); + const {from, to} = await this.influxReader.get_influx_time_bounds(config.source); return {from, to} } From 5d31bb27e73fd3447c14814ef5455ea1f1ec7d18 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 27 Apr 2026 11:26:47 +0100 Subject: [PATCH 45/96] data-access api adding subclass relationship on create/update --- acs-data-access/lib/api-v1.js | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 26d75ad57..4ca2622c9 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -173,14 +173,16 @@ export class APIv1 { */ const {from, to} = await this._get_dataset_time_bounds(dataset_uuid); + const parts = null; + const meta = { uuid: dataset_uuid, name: info.name, - from: from, - to: to, + from, + to, function: f_type, - metadata: metadata, - parts: [] + metadata, + parts } return res.status(200).json(meta); } @@ -531,16 +533,25 @@ export class APIv1 { } // Create config entry for the dataset object - try{ - await this.cdb.put_config(structure, objectUuid, config); - this.log("Updated dataset def in ConfigDB", objectUuid); - }catch(err){ - return fail(422, `Config entry for ${objectUuid} couldn't be updated.`); - } + await this.cdb.put_config(structure, objectUuid, config); + + await this._create_subclass_relationship(structure, objectUuid, config); return objectUuid; } + async _create_subclass_relationship(structure, dataset_uuid, config){ + if(structure === Constants.App.SessionLimits){ + const source = config.source; + await this.cdb.class_add_subclass(source, dataset_uuid); + } + else if(structure === Constants.App.UnionComponents){ + for (let source in config){ + await this.cdb.class_add_subclass(dataset_uuid, source); + } + } + } + /** POST. Creates a new dataset. * From 3e2b5755d69c681a1eea3199288bc41f601c88c7 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 27 Apr 2026 13:26:28 +0100 Subject: [PATCH 46/96] data access dataflow keeping track of dataset members subclasses as parts --- acs-data-access/lib/dataflow.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/acs-data-access/lib/dataflow.js b/acs-data-access/lib/dataflow.js index c4c2a13c9..edf3f8ed8 100644 --- a/acs-data-access/lib/dataflow.js +++ b/acs-data-access/lib/dataflow.js @@ -24,6 +24,7 @@ export class DataFlow { this.general_infos = this._build_general_info(); this.metadata = this._build_metadata(); this.functional_types = this._build_functional_types(); + this.parts = this._build_parts(); } run() { @@ -33,9 +34,20 @@ export class DataFlow { // this.log("GENERAL INFO UUID: ", UUIDs.App.Info); // this.general_infos.subscribe(x => this.log("General infos UPDATE %o", x.toJS())); - } + // watch Dataset's members' subclasses + _build_parts(){ + return rxx.rx( + this.cdb.watch_members_direct_subclasses(Constants.Class.Dataset), + rx.map(map => map.map(set => + set.filter(x => this.cdb.class_has_member(Constants.Class.Dataset, x)) + )), + rxx.shareLatest() + ) + } + + // Watch member members of functional group to see what functional type datasets have _build_functional_types() { return rxx.rx( @@ -141,6 +153,10 @@ export class DataFlow { ); } + get_parts(){ + return this.parts; + } + get_functional_types(){ return this.functional_types; } @@ -154,6 +170,7 @@ export class DataFlow { } + async get_allowed_dataset_uuids(principal, permission, dataset_validity) { const dataset_def_obs = this.get_dataset_definitions(dataset_validity); From 9cc7aa23e68f7de362f6b53681a640d9758800cd Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 27 Apr 2026 13:26:54 +0100 Subject: [PATCH 47/96] data access api returning parts as part of metadata --- acs-data-access/lib/api-v1.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 4ca2622c9..328a67c88 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -173,7 +173,8 @@ export class APIv1 { */ const {from, to} = await this._get_dataset_time_bounds(dataset_uuid); - const parts = null; + const all_parts = await rx.firstValueFrom(this.data.get_parts()); + const parts = all_parts.get(dataset_uuid); const meta = { uuid: dataset_uuid, From 1a3f137284e3148a907abefbfa90e8668f497fdb Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 27 Apr 2026 13:38:04 +0100 Subject: [PATCH 48/96] data access api checking for read perm for parts metadata --- acs-data-access/lib/api-v1.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 328a67c88..c1c71eb47 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -175,6 +175,16 @@ export class APIv1 { const all_parts = await rx.firstValueFrom(this.data.get_parts()); const parts = all_parts.get(dataset_uuid); + + const allowed_parts = []; + + for (let part of parts){ + const ok = await this.auth.check_acl(req.auth, Constants.Perm.ReadDataset, part, true); + if(ok){ + allowed_parts.push(part); + } + } + const meta = { uuid: dataset_uuid, @@ -183,7 +193,7 @@ export class APIv1 { to, function: f_type, metadata, - parts + parts: allowed_parts } return res.status(200).json(meta); } From 60f7d620a3f4592d90b58affe1e35053ce92086e Mon Sep 17 00:00:00 2001 From: amrc-za Date: Tue, 28 Apr 2026 11:11:50 +0100 Subject: [PATCH 49/96] data access api parts minor changes --- acs-data-access/lib/api-v1.js | 19 ++++++++--------- acs-data-access/lib/dataflow.js | 36 ++++++++++++++++----------------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index c1c71eb47..538547254 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -87,7 +87,7 @@ export class APIv1 { const from = config.from ? new Date(config.from) : null; const to = config.to ? new Date(config.to) : null; - + this.log("Session influx time bounds", maxDate(from, child.from), minDate(to, child.to)); return { from: maxDate(from, child.from), to: minDate(to, child.to) @@ -98,9 +98,9 @@ export class APIv1 { // ------------------------ // 2. SPARKPLUG SRC // ------------------------ - if(structure === Constants.App.SparkplugSrc){ + else if(structure === Constants.App.SparkplugSrc){ const {from, to} = await this.influxReader.get_influx_time_bounds(config.source); - + this.log("Sprk SRC influx time bounds", from, to); return {from, to} } @@ -108,7 +108,7 @@ export class APIv1 { // ------------------------ // 3. UNION // ------------------------ - if(structure === Constants.App.UnionComponents){ + else if(structure === Constants.App.UnionComponents){ const results = await Promise.all( config.map(source_uuid => this._get_dataset_time_bounds(source_uuid, new Set(visited)) @@ -119,6 +119,8 @@ export class APIv1 { if(!valid.length) return {from: null, to: null}; + this.log("Union influx time bounds: "); + return { from: new Date(Math.min(...valid.map(v => v.from))), to: new Date(Math.max(...valid.map(v => v.to))) @@ -128,6 +130,7 @@ export class APIv1 { return {from: null, to: null}; } + /** GET. Accepts Dataset UUID and returns metadata about a Published dataset. * @param uuid {request param} * @param @@ -168,9 +171,7 @@ export class APIv1 { const all_metadata = await rx.firstValueFrom(this.data.get_metadata()); const metadata = all_metadata.get(dataset_uuid); - /** - * if the queried dataset does not have from to time period -> recursively find the earliest and latest timestamps from source datasets. - */ + const {from, to} = await this._get_dataset_time_bounds(dataset_uuid); const all_parts = await rx.firstValueFrom(this.data.get_parts()); @@ -189,8 +190,8 @@ export class APIv1 { const meta = { uuid: dataset_uuid, name: info.name, - from, - to, + from: from ? from : undefined, + to: to ? to : undefined, function: f_type, metadata, parts: allowed_parts diff --git a/acs-data-access/lib/dataflow.js b/acs-data-access/lib/dataflow.js index edf3f8ed8..6489fa4b5 100644 --- a/acs-data-access/lib/dataflow.js +++ b/acs-data-access/lib/dataflow.js @@ -3,10 +3,10 @@ * Data-Flow / sequence management */ -import { List as IList, Map as IMap } from "immutable"; +import { List as IList, Map as IMap, Set as ISet } from "immutable"; import rx from "rxjs"; -import * as rxx from "@amrc-factoryplus/rx-util"; +import * as rxu from "@amrc-factoryplus/rx-util"; import { DataAccess as Constants } from './constants.js'; import { valid_uuid, valid_krb, DatasetValidity } from "./validate.js"; @@ -34,23 +34,23 @@ export class DataFlow { // this.log("GENERAL INFO UUID: ", UUIDs.App.Info); // this.general_infos.subscribe(x => this.log("General infos UPDATE %o", x.toJS())); + this.parts.subscribe(x => this.log("Parts UPDATE %o", x.toJS())); } // watch Dataset's members' subclasses - _build_parts(){ - return rxx.rx( - this.cdb.watch_members_direct_subclasses(Constants.Class.Dataset), - rx.map(map => map.map(set => - set.filter(x => this.cdb.class_has_member(Constants.Class.Dataset, x)) + _build_parts() { + return rxu.rx( + this.cdb.watch_members_subclasses(Constants.Class.Dataset), + rx.map(map => map.map(subclass_uuids => + subclass_uuids.filter(x => this.cdb.class_has_member(Constants.Class.Dataset, x)) )), - rxx.shareLatest() - ) + rxu.shareLatest() + ); } - // Watch member members of functional group to see what functional type datasets have _build_functional_types() { - return rxx.rx( + return rxu.rx( this.cdb.watch_member_members(Constants.Group.FunctionalDatasetGroup), rx.map(data => { let result = IMap(); @@ -66,12 +66,12 @@ export class DataFlow { return result; }), - rxx.shareLatest() + rxu.shareLatest() ); } _build_metadata() { - return rxx.rx( + return rxu.rx( this.cdb.watch_members(Constants.App.DatasetMetadata), rx.switchMap((uuidsSet) => { @@ -103,20 +103,20 @@ export class DataFlow { }) ); }), - rxx.shareLatest() + rxu.shareLatest() ); } _build_general_info(){ - return rxx.rx( + return rxu.rx( this.cdb.search_app(UUIDs.App.Info), - rxx.shareLatest() + rxu.shareLatest() ); } _build_dataset_definitions() { - return rxx.rx( + return rxu.rx( rx.combineLatest([ this.cdb.search_app(Constants.App.SparkplugSrc), this.cdb.search_app(Constants.App.SessionLimits), @@ -149,7 +149,7 @@ export class DataFlow { ); }), - rxx.shareLatest() + rxu.shareLatest() ); } From 0e91010a7933d90a0e57039080e4d2dbfd221978 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Tue, 28 Apr 2026 12:58:40 +0100 Subject: [PATCH 50/96] data access metadata endpoint fixed dates --- acs-data-access/lib/api-v1.js | 28 ++++++++++++++++++++++------ acs-data-access/lib/influx-reader.js | 5 +++-- acs-data-access/lib/utils.js | 10 ++++++---- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 538547254..9a7cf0320 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -70,6 +70,7 @@ export class APIv1 { async _get_dataset_time_bounds(dataset_uuid, visited=new Set()){ + this.log(`Running get dataset time bounds for ${dataset_uuid}`); if(visited.has(dataset_uuid)) return fail(422, `Cycle detected at dataset ${dataset_uuid}`); visited.add(dataset_uuid); @@ -98,7 +99,7 @@ export class APIv1 { // ------------------------ // 2. SPARKPLUG SRC // ------------------------ - else if(structure === Constants.App.SparkplugSrc){ + if(structure === Constants.App.SparkplugSrc){ const {from, to} = await this.influxReader.get_influx_time_bounds(config.source); this.log("Sprk SRC influx time bounds", from, to); return {from, to} @@ -108,7 +109,9 @@ export class APIv1 { // ------------------------ // 3. UNION // ------------------------ - else if(structure === Constants.App.UnionComponents){ + if(structure === Constants.App.UnionComponents){ + this.log("Union influx time bounds: "); + const results = await Promise.all( config.map(source_uuid => this._get_dataset_time_bounds(source_uuid, new Set(visited)) @@ -116,14 +119,28 @@ export class APIv1 { ); const valid = results.filter(r => r.from && r.to); + this.log(`After getting time bounds for UNION`); + this.log(valid); if(!valid.length) return {from: null, to: null}; - this.log("Union influx time bounds: "); + + const from_times = valid + .map(v => new Date(v.from).getTime()) + .filter(t => !isNaN(t)); + + + const to_times = valid + .map(v => new Date(v.to).getTime()) + .filter(t => !isNaN(t)); + + if (!from_times.length || !to_times.length) { + return { from: null, to: null }; + } return { - from: new Date(Math.min(...valid.map(v => v.from))), - to: new Date(Math.max(...valid.map(v => v.to))) + from: new Date(Math.min(...from_times)), + to: new Date(Math.max(...to_times)) }; } @@ -171,7 +188,6 @@ export class APIv1 { const all_metadata = await rx.firstValueFrom(this.data.get_metadata()); const metadata = all_metadata.get(dataset_uuid); - const {from, to} = await this._get_dataset_time_bounds(dataset_uuid); const all_parts = await rx.firstValueFrom(this.data.get_parts()); diff --git a/acs-data-access/lib/influx-reader.js b/acs-data-access/lib/influx-reader.js index 319be51f3..3a17a23a9 100644 --- a/acs-data-access/lib/influx-reader.js +++ b/acs-data-access/lib/influx-reader.js @@ -59,7 +59,8 @@ export class InfluxReader{ `; const rows = await this._run_influx(query); - return rows?.[0]?._value ?? null; + + return rows?.[0]?._time ?? null; } async get_influx_time_bounds(topLevelInstance) { @@ -67,7 +68,7 @@ export class InfluxReader{ this._run_min_max_time_agg(topLevelInstance, "min"), this._run_min_max_time_agg(topLevelInstance, "max"), ]); - + this.log(`Get influx time bounds is run: from=${from} to=${to}`) return { from, to }; } diff --git a/acs-data-access/lib/utils.js b/acs-data-access/lib/utils.js index d35f6c7e2..dc739691d 100644 --- a/acs-data-access/lib/utils.js +++ b/acs-data-access/lib/utils.js @@ -11,14 +11,16 @@ export function convert_to_csv (rows){ } -export function minDate(a, b) { +const toTime = d => d ? new Date(d).getTime() : null; + +export function maxDate(a, b) { if (!a) return b; if (!b) return a; - return a < b ? a : b; + return toTime(a) > toTime(b) ? a : b; } -export function maxDate(a, b) { +export function minDate(a, b) { if (!a) return b; if (!b) return a; - return a > b ? a : b; + return toTime(a) < toTime(b) ? a : b; } \ No newline at end of file From 56185f25a765400aefaad74dbee855c58d0d3046 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Tue, 28 Apr 2026 15:36:32 +0100 Subject: [PATCH 51/96] data access dataflow passing fplus props separately --- acs-data-access/bin/api.js | 4 +++- acs-data-access/lib/dataflow.js | 10 ++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/acs-data-access/bin/api.js b/acs-data-access/bin/api.js index c03f5ecae..c2cb8befc 100644 --- a/acs-data-access/bin/api.js +++ b/acs-data-access/bin/api.js @@ -33,7 +33,9 @@ const influxClient = new InfluxDB({ // the dataflow object is full of sequences that does most of the work of this service. Sequence means an RX observable. const data = new DataFlow({ - fplus + debug, + cdb: fplus.ConfigDB, + auth: fplus.Auth, }); const influxReader = new InfluxReader({ diff --git a/acs-data-access/lib/dataflow.js b/acs-data-access/lib/dataflow.js index 6489fa4b5..680840793 100644 --- a/acs-data-access/lib/dataflow.js +++ b/acs-data-access/lib/dataflow.js @@ -14,11 +14,9 @@ import { RxClient, UUIDs } from '@amrc-factoryplus/rx-client'; export class DataFlow { constructor(opts) { - const { fplus } = opts; - - this.log = fplus.debug.bound("data"); - this.cdb = fplus.ConfigDB; - this.auth = fplus.Auth; + this.log = opts.debug.bound("data"); + this.cdb = opts.cdb; + this.auth = opts.auth; this.dataset_definitions = this._build_dataset_definitions(); this.general_infos = this._build_general_info(); @@ -34,7 +32,7 @@ export class DataFlow { // this.log("GENERAL INFO UUID: ", UUIDs.App.Info); // this.general_infos.subscribe(x => this.log("General infos UPDATE %o", x.toJS())); - this.parts.subscribe(x => this.log("Parts UPDATE %o", x.toJS())); + // this.parts.subscribe(x => this.log("Parts UPDATE %o", x.toJS())); } // watch Dataset's members' subclasses From 169ba0d7e03921fa4f12d16ba2bb3de67e7725f1 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 29 Apr 2026 15:54:31 +0100 Subject: [PATCH 52/96] data access api fixed subclass linking logic --- acs-data-access/lib/api-v1.js | 85 ++++++++++++++++++++++++++++------- 1 file changed, 68 insertions(+), 17 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 9a7cf0320..a94646d07 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -8,6 +8,7 @@ import { Map as IMap } from "immutable"; import * as rx from "rxjs"; import { APIError } from "@amrc-factoryplus/service-api"; +import { retryBackoff } from "@amrc-factoryplus/rx-util"; import { DataAccess as Constants } from "./constants.js"; import { valid_uuid, DatasetValidity } from "./validate.js"; import {convert_to_csv, minDate, maxDate} from './utils.js'; @@ -49,13 +50,11 @@ export class APIv1 { api.route("/structure/:uuid") .get(this.structure_uuid.bind(this)) - .put(this.structure_update.bind(this)) + .put(this.structure_update.bind(this)); return api; } - - /** GET. Returns a list of Dataset UUIDs that the client has READ_DATASET access to. * The dataset can be optionally restricted by from and to dates (this is not implemented yet). * Dates must be in the format ISO date-time string in UTC 2025-11-13T09:33:18.000Z @@ -159,11 +158,11 @@ export class APIv1 { * function {array} - Array of Functional classes - UUIDs of classes dataset belongs to (only members of Functional dataset type) * metadata {object} - Map of metadata configs keyed by Application UUID - contains all config entries for dataset which use applications in the Dataset metadata class * parts {array} - Subset datasets - contains UUIDs of all subclasses of dataset the client has READ access to. - * Don't return valid response if dataset is invalid + * Returns 404 if dataset is invalid. */ async metadata_uuid(req, res){ const dataset_uuid = req.params.uuid; - if (!valid_uuid(dataset_uuid)) fail(410, `${dataset_uuid} is invalid uuid.`); + if (!valid_uuid(dataset_uuid)) fail(422, `${dataset_uuid} is invalid uuid.`); const ok = await this.auth.check_acl( req.auth, @@ -210,7 +209,8 @@ export class APIv1 { to: to ? to : undefined, function: f_type, metadata, - parts: allowed_parts + parts: parts, + allowed_parts: allowed_parts } return res.status(200).json(meta); } @@ -555,27 +555,36 @@ export class APIv1 { if(!objectUuid){ objectUuid = await this.cdb.create_object(Constants.Class.Dataset); - if(!objectUuid) return fail(400, `New Dataset object couldn't be created in ConfigDB.`); + if(!objectUuid) return fail(500, `New Dataset object couldn't be created in ConfigDB.`); this.log("Created new dataset object in ConfigDB", objectUuid); } // Create config entry for the dataset object await this.cdb.put_config(structure, objectUuid, config); + this.log(`Added config for ${objectUuid}`); - await this._create_subclass_relationship(structure, objectUuid, config); + this._create_subclass_relationship(structure, objectUuid, config); + this.log(`Created subclass relationship`); return objectUuid; } - async _create_subclass_relationship(structure, dataset_uuid, config){ - if(structure === Constants.App.SessionLimits){ - const source = config.source; - await this.cdb.class_add_subclass(source, dataset_uuid); + async _create_subclass_relationship(structure, dataset_uuid, config) { + if (structure === Constants.App.SessionLimits) { + await rx.lastValueFrom( + rx.defer(() => + this.cdb.class_add_subclass(config.source, dataset_uuid) + ).pipe(retryBackoff(500, e => this.log(e))) + ); } - else if(structure === Constants.App.UnionComponents){ - for (let source in config){ - await this.cdb.class_add_subclass(dataset_uuid, source); + else if (structure === Constants.App.UnionComponents) { + for (let source of config) { + await rx.lastValueFrom( + rx.defer(() => + this.cdb.class_add_subclass(dataset_uuid, source) + ).pipe(retryBackoff(500, e => this.log(e))) + ); } } } @@ -600,6 +609,31 @@ export class APIv1 { } + async _unlink_subclasses(structure, dataset_uuid, curr_config, new_config){ + + if(structure === Constants.App.SessionLimits){ + if(curr_config.source != new_config.source){ + // remove the dataset from subclasses of its source dataset + await this.cdb.class_remove_subclass(curr_config.source, dataset_uuid); + this.log(`Removed ${dataset_uuid} from ${curr_config.source} subclasses.`) + } + }else if(structure === Constants.App.UnionComponents){ + if(curr_config.length > 0){ + // if new_config does not include sources from curr_config -> remove subclass rel. + const sources_to_remove = curr_config.filter(x => !new_config.includes(x)); + + for (const source_uuid of sources_to_remove){ + await this.cdb.class_remove_subclass(dataset_uuid, source_uuid); + this.log(`Removed ${dataset_uuid} from ${source_uuid} subclasses.`); + } + } + + }else if (structure === Constants.App.SparkplugSrc){ + return; + }else{ + return fail(422, `Unknown structure type ${structure}`); + } + } /** PUT. Updates dataset definition. Principal should have CreateDataset permission * @@ -611,10 +645,27 @@ export class APIv1 { const dataset_uuid = req.params.uuid; if(!valid_uuid(dataset_uuid)) return fail(422, `Dataset uuid is invalid.`); + const structure = req.body.structure; + const new_config = req.body.config; + + // get current config + const all_defs = await rx.firstValueFrom(this.data.get_dataset_definitions(DatasetValidity.ALL)); + const curr_defs = all_defs.get(dataset_uuid); + + const curr_config_for_structure = curr_defs.find(c => c.structure === structure)?.config; + + if(!curr_config_for_structure){ + this.log(`Current config for structure does not exist.`); + } + // update subclass links if config def for this structure already exists and being updated. + if(curr_config_for_structure){ + await this._unlink_subclasses(structure, dataset_uuid,curr_config_for_structure, new_config); + } + const objectUuid = await this._update_dataset_config( req.auth, - req.body.structure, - req.body.config, + structure, + new_config, dataset_uuid ); From 349e285abec2ef5148e8f08beb41865bdafca235 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 29 Apr 2026 16:09:31 +0100 Subject: [PATCH 53/96] data access api metadata removed fail if general info not found --- acs-data-access/lib/api-v1.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index a94646d07..4116c361a 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -179,8 +179,7 @@ export class APIv1 { const infos = await rx.firstValueFrom(this.data.get_general_infos()); const info = infos.get(dataset_uuid); - if(!info) return fail(404, `General info does not exist for dataset ${dataset_uuid}`); - + const f_types = await rx.firstValueFrom(this.data.get_functional_types()); const f_type = f_types.get(dataset_uuid); @@ -204,13 +203,12 @@ export class APIv1 { const meta = { uuid: dataset_uuid, - name: info.name, + name: info ? info.name : "UNKNOWN", from: from ? from : undefined, to: to ? to : undefined, function: f_type, metadata, - parts: parts, - allowed_parts: allowed_parts + parts: allowed_parts } return res.status(200).json(meta); } From 8b79de4b37b15286e0a6f128c60b040612d8250d Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 30 Apr 2026 08:57:37 +0100 Subject: [PATCH 54/96] data access removed debugging logs --- acs-data-access/lib/api-v1.js | 96 +++++++++++++++------------- acs-data-access/lib/influx-reader.js | 2 +- 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 4116c361a..45b5007cf 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -69,7 +69,7 @@ export class APIv1 { async _get_dataset_time_bounds(dataset_uuid, visited=new Set()){ - this.log(`Running get dataset time bounds for ${dataset_uuid}`); + if(visited.has(dataset_uuid)) return fail(422, `Cycle detected at dataset ${dataset_uuid}`); visited.add(dataset_uuid); @@ -87,7 +87,7 @@ export class APIv1 { const from = config.from ? new Date(config.from) : null; const to = config.to ? new Date(config.to) : null; - this.log("Session influx time bounds", maxDate(from, child.from), minDate(to, child.to)); + return { from: maxDate(from, child.from), to: minDate(to, child.to) @@ -100,7 +100,7 @@ export class APIv1 { // ------------------------ if(structure === Constants.App.SparkplugSrc){ const {from, to} = await this.influxReader.get_influx_time_bounds(config.source); - this.log("Sprk SRC influx time bounds", from, to); + return {from, to} } @@ -109,7 +109,6 @@ export class APIv1 { // 3. UNION // ------------------------ if(structure === Constants.App.UnionComponents){ - this.log("Union influx time bounds: "); const results = await Promise.all( config.map(source_uuid => @@ -118,17 +117,12 @@ export class APIv1 { ); const valid = results.filter(r => r.from && r.to); - this.log(`After getting time bounds for UNION`); - this.log(valid); - if(!valid.length) return {from: null, to: null}; - const from_times = valid .map(v => new Date(v.from).getTime()) .filter(t => !isNaN(t)); - const to_times = valid .map(v => new Date(v.to).getTime()) .filter(t => !isNaN(t)); @@ -174,8 +168,8 @@ export class APIv1 { if (!ok) return fail(403, `You don't have Read permission for ${dataset_uuid}`); const datasets = await this.data.get_allowed_dataset_uuids(req.auth, Constants.Perm.ReadDataset, DatasetValidity.VALID); - const is_valid = await datasets.includes(dataset_uuid) - if(!is_valid) return fail(404, `Dataset ${dataset_uuid} is not found: it's either invalid or does not exist.`); + const is_valid_exist = await datasets.includes(dataset_uuid) + if(!is_valid_exist) return fail(404, `Dataset ${dataset_uuid} is not found or invalid.`); const infos = await rx.firstValueFrom(this.data.get_general_infos()); const info = infos.get(dataset_uuid); @@ -214,11 +208,14 @@ export class APIv1 { } + // Check if principal has necessary permission to referenced source async _check_second_level_permission(principal, structure, config, permission){ + if(structure == Constants.App.SessionLimits || structure == Constants.App.SparkplugSrc){ - this.log(`second level ACL check for structure ${structure} and config ${config}`); + const target = config.source; if(!target) return fail(422, `Dataset definition does not contain source.`); + if(!valid_uuid(target)) return fail(422, `Source uuid ${target} is invalid.`); const ok = await this.auth.check_acl( @@ -232,17 +229,21 @@ export class APIv1 { }else if(structure == Constants.App.UnionComponents){ if(config.length == 0) return true; - if(!Array.isArray(config)) return fail(422, `Dataset def of structure ${structure} must be Array.`); + + if(!Array.isArray(config)) return fail(422, `Dataset def of structure ${structure} must be an Array.`); for(let target of config){ + const ok = await this.auth.check_acl( principal, permission, target, true ); + if(!ok) return false; } + return true; }else{ @@ -252,8 +253,6 @@ export class APIv1 { } - - _merge_intervals(queries){ const grouped = {}; @@ -301,6 +300,7 @@ export class APIv1 { } + // Resolve nested datasets async _resolve_dataset(dataset_uuid, from=null, to=null, visited = new Set()) { if (visited.has(dataset_uuid)) { return fail(404, `Circular dataset reference detected at ${dataset_uuid}`); @@ -312,7 +312,9 @@ export class APIv1 { if (!dataset_def) return fail(404, `Dataset definition not found for ${dataset_uuid}`); const { structure, config } = dataset_def; + if(!structure) return fail(422, `Structure is not found in dataset definition ${dataset_uuid}`); + if(!valid_uuid(structure)) return fail(422, `Structure uuid is invalid for dataset ${dataset_uuid}`); // ------------------------- @@ -339,6 +341,7 @@ export class APIv1 { // ------------------------- else if (structure === Constants.App.SessionLimits) { const source_uuid = config?.source; + if (!source_uuid) return fail(422, `Dataset def ${dataset_uuid} does not contain source uuid`); if(!valid_uuid(source_uuid)) return fail(422, `Dataset ${dataset_uuid}'s source is invalid uuid`); @@ -368,6 +371,7 @@ export class APIv1 { // ------------------------- else if (structure === Constants.App.UnionComponents) { if(!Array.isArray(config)) return fail(422, `Union structure type dataset def must be Array ${dataset_uuid}`); + const results = await Promise.all( config.map(uuid => this._resolve_dataset(uuid, from, to, new Set(visited)) @@ -474,7 +478,7 @@ export class APIv1 { * @param {*} req * @param {*} res * @returns list of dataset UUIDs the client has permission to EDIT - * the response should include invalid datasets + * the response includes invalid datasets */ async structure_list(req, res){ const uuids = await this.data.get_allowed_dataset_uuids(req.auth, Constants.Perm.EditDataset, DatasetValidity.ALL); @@ -491,7 +495,7 @@ export class APIv1 { * uuid {UUID} - Dataset UUID * class {UUID} - Structural class: Sparkplug device, Union Dataset, Session * config {any} - Structural definition: "not visible", Union components, Session limits - * the response should specify structure field as invalid uuid in definition if the dataset is invalid + * the response includes invalid datasets but their structure field references Invalid Dataset uuid */ async structure_uuid(req, res) { const { uuid } = req.params; @@ -527,6 +531,7 @@ export class APIv1 { async _update_dataset_config(principal, structure, config, objectUuid){ if(!structure) return fail(422, `Structure not provided.`); + if(!valid_uuid(structure)) return fail(422, `Structure uuid ${structure} is invalid.`) const ok = await this.auth.check_acl( @@ -538,17 +543,13 @@ export class APIv1 { if (!ok) return fail(403, `You don't have Create permission for structure ${structure}`); - this.log(`Passed ACL Check to CreateDataset for structure ${structure}`); - if(!config) return fail(422, `Config not provided.`); // Check the principal has appropriate permission for all dataset sources in config. - this.log(`Now will try ACL check ${mapStructureToPermission.get(structure)} for ${config.source} `); const ok2 = await this._check_second_level_permission(principal, structure, config, mapStructureToPermission.get(structure)); if(!ok2) return fail(403, `You don't have ${mapStructureToPermission.get(structure)} permission for dataset source(s).`); - // Create new Dataset Object if(!objectUuid){ objectUuid = await this.cdb.create_object(Constants.Class.Dataset); @@ -564,19 +565,22 @@ export class APIv1 { this._create_subclass_relationship(structure, objectUuid, config); - this.log(`Created subclass relationship`); + this.log(`Created subclass relationship for ${objectUuid}`); return objectUuid; } async _create_subclass_relationship(structure, dataset_uuid, config) { if (structure === Constants.App.SessionLimits) { + await rx.lastValueFrom( rx.defer(() => this.cdb.class_add_subclass(config.source, dataset_uuid) ).pipe(retryBackoff(500, e => this.log(e))) ); + } else if (structure === Constants.App.UnionComponents) { + for (let source of config) { await rx.lastValueFrom( rx.defer(() => @@ -584,29 +588,10 @@ export class APIv1 { ).pipe(retryBackoff(500, e => this.log(e))) ); } - } - } - - /** POST. Creates a new dataset. - * - * @param {*} req.body must be object (structure, config) without uuid. - * @param {*} res - * @returns new dataset's UUID - JSON string - */ - async structure_create(req, res){ - const objectUuid = await this._update_dataset_config( - req.auth, - req.body.structure, - req.body.config, - null - ); - - if(!objectUuid) return fail(422, `Failed to create new Dataset.`); - return res.status(200).json(objectUuid); + } } - async _unlink_subclasses(structure, dataset_uuid, curr_config, new_config){ if(structure === Constants.App.SessionLimits){ @@ -632,7 +617,27 @@ export class APIv1 { return fail(422, `Unknown structure type ${structure}`); } } + + /** POST. Creates a new dataset. + * + * @param {*} req.body must be object (structure, config) without uuid. + * @param {*} res + * @returns new dataset's UUID - JSON string + */ + async structure_create(req, res){ + const objectUuid = await this._update_dataset_config( + req.auth, + req.body.structure, + req.body.config, + null + ); + + if(!objectUuid) return fail(422, `Failed to create new Dataset.`); + return res.status(200).json(objectUuid); + } + + /** PUT. Updates dataset definition. Principal should have CreateDataset permission * * @param {*} req.body must be object (structure, config) and UUID (optional) @@ -641,9 +646,11 @@ export class APIv1 { async structure_update(req, res){ const dataset_uuid = req.params.uuid; + if(!valid_uuid(dataset_uuid)) return fail(422, `Dataset uuid is invalid.`); const structure = req.body.structure; + const new_config = req.body.config; // get current config @@ -655,6 +662,7 @@ export class APIv1 { if(!curr_config_for_structure){ this.log(`Current config for structure does not exist.`); } + // update subclass links if config def for this structure already exists and being updated. if(curr_config_for_structure){ await this._unlink_subclasses(structure, dataset_uuid,curr_config_for_structure, new_config); @@ -669,13 +677,15 @@ export class APIv1 { if(!objectUuid) return fail(422, `Failed to update dataset ${dataset_uuid}`); - // remove definitions from other structure apps + // remove definitions from other structure apps if the dataset was invalid const invalid_defs = await rx.firstValueFrom( + this.data.get_dataset_definitions(DatasetValidity.INVALID).pipe( rx.map(datasets => datasets.get(dataset_uuid) ?? []), rx.map(defs => defs.filter(d => d.structure !== req.body.structure)), rx.map(filtered => filtered.length ? filtered : undefined) ) + ); if (invalid_defs?.length) { diff --git a/acs-data-access/lib/influx-reader.js b/acs-data-access/lib/influx-reader.js index 3a17a23a9..03ad3ddee 100644 --- a/acs-data-access/lib/influx-reader.js +++ b/acs-data-access/lib/influx-reader.js @@ -68,7 +68,7 @@ export class InfluxReader{ this._run_min_max_time_agg(topLevelInstance, "min"), this._run_min_max_time_agg(topLevelInstance, "max"), ]); - this.log(`Get influx time bounds is run: from=${from} to=${to}`) + return { from, to }; } From dd4dc07718912e73a76d5fbc5605f4bf6eadf883 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 6 May 2026 13:46:58 +0100 Subject: [PATCH 55/96] data access convert to csv fixed char escaping --- acs-data-access/lib/utils.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/acs-data-access/lib/utils.js b/acs-data-access/lib/utils.js index dc739691d..ea285014f 100644 --- a/acs-data-access/lib/utils.js +++ b/acs-data-access/lib/utils.js @@ -1,10 +1,23 @@ +function escapeCsv(value) { + if (value == null) return ""; -export function convert_to_csv (rows){ + const str = String(value); + + // If it contains special chars, wrap in quotes + if (/[",\n\r]/.test(str)) { + return `"${str.replace(/"/g, '""')}"`; + } + + return str; +} + + +export function convertToCsv (rows){ if (!rows.length) return ""; const headers = Object.keys(rows[0]); const lines = rows.map(row => - headers.map(h => JSON.stringify(row[h] ?? "")).join(",") + headers.map(h => escapeCsv(row[h])).join(",") ); return [headers.join(","), ...lines].join("\n"); From 4680eaafb467dc05d6598218097be70b1f616bc1 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 6 May 2026 13:47:31 +0100 Subject: [PATCH 56/96] data access influx reader fixed range bug --- acs-data-access/lib/influx-reader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acs-data-access/lib/influx-reader.js b/acs-data-access/lib/influx-reader.js index 03ad3ddee..a7d0fa35e 100644 --- a/acs-data-access/lib/influx-reader.js +++ b/acs-data-access/lib/influx-reader.js @@ -15,7 +15,7 @@ export class InfluxReader{ // Build range const rangeClause = (from && to) ? `|> range(start: ${from}, stop: ${to})` - : `|> range(start: -5m)`; + : `|> range(start: 0)`; // Optional measurement filter const measurementFilter = measurement From f09ba8563394ef5099ef7032175b531a572834c2 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 6 May 2026 13:48:41 +0100 Subject: [PATCH 57/96] data access api - moved invalid dataset logic to dataflow --- acs-data-access/lib/api-v1.js | 380 ++++++++++++-------------------- acs-data-access/lib/dataflow.js | 302 +++++++++++++++++++------ 2 files changed, 371 insertions(+), 311 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 45b5007cf..b49a4f4a8 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -4,23 +4,20 @@ */ import express from "express"; -import { Map as IMap } from "immutable"; +import { Map as IMap, Seq as ISeq } from "immutable"; import * as rx from "rxjs"; import { APIError } from "@amrc-factoryplus/service-api"; import { retryBackoff } from "@amrc-factoryplus/rx-util"; import { DataAccess as Constants } from "./constants.js"; import { valid_uuid, DatasetValidity } from "./validate.js"; -import {convert_to_csv, minDate, maxDate} from './utils.js'; +import { convertToCsv } from './utils.js'; -function fail(status, message) { - throw new APIError(status); -} -const mapStructureToPermission = IMap() - .set(Constants.App.UnionComponents, Constants.Perm.IncludeInUnion) - .set(Constants.App.SessionLimits, Constants.Perm.UseForSession) - .set(Constants.App.SparkplugSrc, Constants.Perm.UseSparkplug); +function fail(log, status, message) { + log(message); + throw new APIError(status); + } export class APIv1 { constructor(opts) { @@ -67,80 +64,6 @@ export class APIv1 { return res.status(200).json(uuids); } - - async _get_dataset_time_bounds(dataset_uuid, visited=new Set()){ - - if(visited.has(dataset_uuid)) return fail(422, `Cycle detected at dataset ${dataset_uuid}`); - - visited.add(dataset_uuid); - - const def = await this.data.get_dataset_def_by_uuid(dataset_uuid); - if(!def) return fail(404, `Dataset def for ${dataset_uuid} not found`); - - const {structure, config} = def; - - // ------------------------ - // 1. SESSION - // ------------------------ - if(structure === Constants.App.SessionLimits){ - const child = await this._get_dataset_time_bounds(config.source, visited); - - const from = config.from ? new Date(config.from) : null; - const to = config.to ? new Date(config.to) : null; - - return { - from: maxDate(from, child.from), - to: minDate(to, child.to) - }; - } - - - // ------------------------ - // 2. SPARKPLUG SRC - // ------------------------ - if(structure === Constants.App.SparkplugSrc){ - const {from, to} = await this.influxReader.get_influx_time_bounds(config.source); - - return {from, to} - } - - - // ------------------------ - // 3. UNION - // ------------------------ - if(structure === Constants.App.UnionComponents){ - - const results = await Promise.all( - config.map(source_uuid => - this._get_dataset_time_bounds(source_uuid, new Set(visited)) - ) - ); - - const valid = results.filter(r => r.from && r.to); - if(!valid.length) return {from: null, to: null}; - - const from_times = valid - .map(v => new Date(v.from).getTime()) - .filter(t => !isNaN(t)); - - const to_times = valid - .map(v => new Date(v.to).getTime()) - .filter(t => !isNaN(t)); - - if (!from_times.length || !to_times.length) { - return { from: null, to: null }; - } - - return { - from: new Date(Math.min(...from_times)), - to: new Date(Math.max(...to_times)) - }; - } - - return {from: null, to: null}; - } - - /** GET. Accepts Dataset UUID and returns metadata about a Published dataset. * @param uuid {request param} * @param @@ -156,7 +79,7 @@ export class APIv1 { */ async metadata_uuid(req, res){ const dataset_uuid = req.params.uuid; - if (!valid_uuid(dataset_uuid)) fail(422, `${dataset_uuid} is invalid uuid.`); + if (!valid_uuid(dataset_uuid)) fail(this.log, 422, `${dataset_uuid} is invalid uuid.`); const ok = await this.auth.check_acl( req.auth, @@ -165,62 +88,69 @@ export class APIv1 { true, ); - if (!ok) return fail(403, `You don't have Read permission for ${dataset_uuid}`); + if (!ok) return fail(this.log, 403, `You don't have READ permissions for ${dataset_uuid}`); - const datasets = await this.data.get_allowed_dataset_uuids(req.auth, Constants.Perm.ReadDataset, DatasetValidity.VALID); - const is_valid_exist = await datasets.includes(dataset_uuid) - if(!is_valid_exist) return fail(404, `Dataset ${dataset_uuid} is not found or invalid.`); + const datasets = await rx.firstValueFrom(this.data.get_dataset_definitions(DatasetValidity.VALID)); + const dataset = await datasets.get(dataset_uuid); + if(!dataset) return fail(this.log, 404, `Dataset ${dataset_uuid} is not found or invalid.`); + + const from = dataset.config?.from; + const to = dataset.config?.to; - const infos = await rx.firstValueFrom(this.data.get_general_infos()); + const infos = await rx.firstValueFrom(this.data.general_infos); const info = infos.get(dataset_uuid); - const f_types = await rx.firstValueFrom(this.data.get_functional_types()); - const f_type = f_types.get(dataset_uuid); + const all_f_types = await rx.firstValueFrom(this.data.functional_types); + const f_types = all_f_types.get(dataset_uuid); - const all_metadata = await rx.firstValueFrom(this.data.get_metadata()); + const all_metadata = await rx.firstValueFrom(this.data.metadata); const metadata = all_metadata.get(dataset_uuid); - const {from, to} = await this._get_dataset_time_bounds(dataset_uuid); - - const all_parts = await rx.firstValueFrom(this.data.get_parts()); - const parts = all_parts.get(dataset_uuid); - - const allowed_parts = []; - - for (let part of parts){ - const ok = await this.auth.check_acl(req.auth, Constants.Perm.ReadDataset, part, true); - if(ok){ - allowed_parts.push(part); - } - } - + const parts = await this.data.get_dataset_allowed_parts(dataset_uuid, req.auth, Constants.Perm.ReadDataset); const meta = { uuid: dataset_uuid, name: info ? info.name : "UNKNOWN", from: from ? from : undefined, to: to ? to : undefined, - function: f_type, + function: f_types, metadata, - parts: allowed_parts + parts: parts } return res.status(200).json(meta); } // Check if principal has necessary permission to referenced source - async _check_second_level_permission(principal, structure, config, permission){ + async _check_second_level_permission(principal, structure, config){ - if(structure == Constants.App.SessionLimits || structure == Constants.App.SparkplugSrc){ + if( structure == Constants.App.SparkplugSrc){ const target = config.source; - if(!target) return fail(422, `Dataset definition does not contain source.`); + if(!target) return fail(this.log, 422, `Dataset definition does not contain source.`); - if(!valid_uuid(target)) return fail(422, `Source uuid ${target} is invalid.`); + if(!valid_uuid(target)) return fail(this.log, 422, `Source uuid ${target} is invalid.`); const ok = await this.auth.check_acl( principal, - permission, + Constants.Perm.UseSparkplug, + target, + true + ); + + return ok; + } + + if(structure == Constants.App.SessionLimits){ + + const target = config.source; + if(!target) return fail(this.log, 422, `Dataset definition does not contain source.`); + + if(!valid_uuid(target)) return fail(this.log, 422, `Source uuid ${target} is invalid.`); + + const ok = await this.auth.check_acl( + principal, + Constants.Perm.UseForSession, target, true ); @@ -230,13 +160,13 @@ export class APIv1 { }else if(structure == Constants.App.UnionComponents){ if(config.length == 0) return true; - if(!Array.isArray(config)) return fail(422, `Dataset def of structure ${structure} must be an Array.`); + if(!Array.isArray(config)) return fail(this.log, 422, `Dataset def of structure ${structure} must be an Array.`); for(let target of config){ const ok = await this.auth.check_acl( principal, - permission, + Constants.Perm.IncludeInUnion, target, true ); @@ -248,74 +178,67 @@ export class APIv1 { }else{ // Unhandled Structure type - return fail(422, `Structure ${structure} is unknown`); + return fail(this.log, 422, `Structure ${structure} is unknown`); } } - - _merge_intervals(queries){ - const grouped = {}; - - for (const q of queries){ - if(!grouped[q.source]) grouped[q.source] = []; - grouped[q.source].push(q); - } - - const result = []; - - for(const source in grouped){ - const intervals = grouped[source] - .map(q => ({ - from: q.from ? new Date(q.from) : new Date(0), - to: q.to ? new Date(q.to) : new Date() - })) - .sort((a, b) => a.from - b.from); - - - let merged = []; - - for(const interval of intervals){ - if(!merged.length){ - merged.push(interval); - continue; - } - const last = merged[merged.length - 1]; - - if(interval.from <= last.to){ - last.to = new Date(Math.max(last.to, interval.to)); - }else{ - merged.push(interval); - } + _merge_intervals(queries) { + return ISeq(queries) + .groupBy(q => q.source) + .flatMap((group, source) => { + const intervals = group + .map(q => ({ + from: q.from ? new Date(q.from) : new Date(0), + to: q.to ? new Date(q.to) : new Date() + })) + .sortBy(i => i.from) + .toArray(); + + const merged = []; + + for (const interval of intervals) { + if (!merged.length) { + merged.push(interval); + continue; + } + + const last = merged[merged.length - 1]; + + if (interval.from <= last.to) { + last.to = new Date(Math.max(last.to, interval.to)); + } else { + merged.push(interval); + } } - for (const m of merged){ - result.push({ - source, - from: m.from, - to: m.to - }); - } - } - return result; + return merged.map(m => ({ + source, + from: m.from, + to: m.to + })); + }) + .toArray(); } // Resolve nested datasets async _resolve_dataset(dataset_uuid, from=null, to=null, visited = new Set()) { - if (visited.has(dataset_uuid)) { - return fail(404, `Circular dataset reference detected at ${dataset_uuid}`); - } + const datasets = await rx.firstValueFrom(this.data.datasets); + const dataset = await datasets.get(dataset_uuid); - visited.add(dataset_uuid); + if (!dataset) return fail(this.log, 404, `Dataset not found for ${dataset_uuid}`); - const dataset_def = await this.data.get_dataset_def_by_uuid(dataset_uuid); - if (!dataset_def) return fail(404, `Dataset definition not found for ${dataset_uuid}`); + if(dataset.validity === DatasetValidity.INVALID) return fail(this.log, 404, `Invalid dataset ${dataset_uuid}`); + + if(visited.has(dataset_uuid)) return fail(this.log, 404, `Circular dataset reference detected at ${dataset_uuid}`); + + visited.add(dataset_uuid); - const { structure, config } = dataset_def; + const { structure, config } = dataset; - if(!structure) return fail(422, `Structure is not found in dataset definition ${dataset_uuid}`); + if(!structure) return fail(this.log, 404, `Structure not found for ${dataset_uuid}`); - if(!valid_uuid(structure)) return fail(422, `Structure uuid is invalid for dataset ${dataset_uuid}`); + if(!valid_uuid(structure)) return fail(this.log, 404, `Invalid structure uuid for ${dataset_uuid}`); // ------------------------- // 1. SparkplugSrc (BASE CASE) @@ -323,9 +246,9 @@ export class APIv1 { if (structure === Constants.App.SparkplugSrc) { const source_uuid = config?.source; - if (!source_uuid) return fail(422, `Dataset def ${dataset_uuid} does not contain source uuid.`); + if (!source_uuid) return fail(this.log, 404, `Dataset def ${dataset_uuid} does not contain source uuid.`); - if(!valid_uuid(source_uuid)) return fail(422, `Dataset ${dataset_uuid}'s source uuid is invalid.`); + if(!valid_uuid(source_uuid)) return fail(this.log, 404, `Dataset ${dataset_uuid}'s source uuid is invalid.`); return [{ source: source_uuid, @@ -342,9 +265,9 @@ export class APIv1 { else if (structure === Constants.App.SessionLimits) { const source_uuid = config?.source; - if (!source_uuid) return fail(422, `Dataset def ${dataset_uuid} does not contain source uuid`); + if (!source_uuid) return fail(this.log, 404, `Dataset def ${dataset_uuid} does not contain source uuid`); - if(!valid_uuid(source_uuid)) return fail(422, `Dataset ${dataset_uuid}'s source is invalid uuid`); + if(!valid_uuid(source_uuid)) return fail(this.log, 404, `Dataset ${dataset_uuid}'s source is invalid uuid`); const session_from = config?.from ? new Date(config.from) : null; const session_to = config?.to ? new Date(config.to) : null; @@ -370,7 +293,7 @@ export class APIv1 { // 3. UnionComponents (MULTIPLE REFERENCES) // ------------------------- else if (structure === Constants.App.UnionComponents) { - if(!Array.isArray(config)) return fail(422, `Union structure type dataset def must be Array ${dataset_uuid}`); + if(!Array.isArray(config)) return fail(this.log, 404, `Union structure type dataset def must be Array ${dataset_uuid}`); const results = await Promise.all( config.map(uuid => @@ -387,7 +310,7 @@ export class APIv1 { // 4. Unknown structure // ------------------------- else { - return fail(422, `Dataset ${dataset_uuid} has unknown structure type ${structure}`); + return fail(this.log, 404, `Dataset ${dataset_uuid} has unknown structure type ${structure}`); } } @@ -423,7 +346,7 @@ export class APIv1 { */ async dataset_data(req, res) { const dataset_uuid = req.params.uuid; - if (!valid_uuid(dataset_uuid)) return fail(422, `Dataset uuid ${dataset_uuid} is invalid.`); + if (!valid_uuid(dataset_uuid)) return fail(this.log, 422, `Dataset uuid ${dataset_uuid} is invalid.`); const ok = await this.auth.check_acl( req.auth, @@ -431,22 +354,10 @@ export class APIv1 { dataset_uuid, true, ); - if (!ok) return fail(403, `You don't have Read permission for dataset ${dataset_uuid}`); - - const dataset_def = await this.data.get_dataset_def_by_uuid(dataset_uuid); - if (!dataset_def) return fail(404, `Dataset def ${dataset_uuid} not found or invalid.`); - - const ok2 = await this._check_second_level_permission( - req.auth, - dataset_def?.structure, - dataset_def?.config, - Constants.Perm.ReadDataset - ); - if (!ok2) return fail(403, `You don't have permissions to Read the contents of dataset ${dataset_uuid}.`); + if (!ok) return fail(this.log, 403, `You don't have Read permission for dataset ${dataset_uuid}`); try { const resolved = await this._resolve_dataset(dataset_uuid); - const merged = this._merge_intervals(resolved); const results = await Promise.all( @@ -457,10 +368,10 @@ export class APIv1 { }) ) ); - const rows = results.flat(); + this.log(`# influx rows = ${rows.length}`); - const csv = convert_to_csv(rows); + const csv = convertToCsv(rows); res.setHeader("Content-Type", "text/csv"); res.setHeader("Content-Disposition", `attachment; filename=${dataset_uuid}.csv`); @@ -468,7 +379,7 @@ export class APIv1 { } catch (err) { this.log(err); - return fail(500); + return fail(this.log, 500); } } @@ -498,41 +409,33 @@ export class APIv1 { * the response includes invalid datasets but their structure field references Invalid Dataset uuid */ async structure_uuid(req, res) { - const { uuid } = req.params; - - if (!valid_uuid(uuid)) return fail(422, `Dataset uuid ${uuid} is invalid.`); + const dataset_uuid = req.params.uuid; + if (!valid_uuid(dataset_uuid)) return fail(this.log, 422, `Dataset uuid ${dataset_uuid} is invalid.`); const ok = await this.auth.check_acl( req.auth, Constants.Perm.EditDataset, - uuid, + dataset_uuid, true, ); - if (!ok) return fail(403, `You don't have permission to Edit dataset ${uuid}.`); + if (!ok) return fail(this.log, 403, `You don't have permission to Edit dataset ${uuid}.`); - const definition = await rx.firstValueFrom( + const dataset = await rx.firstValueFrom( this.data.get_dataset_definitions(DatasetValidity.ALL).pipe( - rx.map(datasets => datasets.get(uuid)) + rx.map(datasets => datasets.get(dataset_uuid)) ) ); - if (!definition) return fail(404, `Dataset def with uuid ${uuid} not found.`); - - if (definition.length > 1){ - for(let def of definition){ - def.structure = Constants.Special.InvalidDataset; - } - } - - return res.status(200).json(definition); + if (!dataset) return fail(this.log, 404, `Dataset not found for ${dataset_uuid}.`); + return res.status(200).json(dataset); } async _update_dataset_config(principal, structure, config, objectUuid){ - if(!structure) return fail(422, `Structure not provided.`); - - if(!valid_uuid(structure)) return fail(422, `Structure uuid ${structure} is invalid.`) + if(!structure) return fail(this.log, 422, `Structure not provided.`); + if(!valid_uuid(structure)) return fail(this.log, 422, `Structure uuid ${structure} is invalid.`); + if(!config) return fail(this.log, 422, `Config not provided.`); const ok = await this.auth.check_acl( principal, @@ -541,21 +444,16 @@ export class APIv1 { true ); - if (!ok) return fail(403, `You don't have Create permission for structure ${structure}`); + if (!ok) return fail(this.log, 403, `You don't have Create permission for structure ${structure}`); - if(!config) return fail(422, `Config not provided.`); - // Check the principal has appropriate permission for all dataset sources in config. - const ok2 = await this._check_second_level_permission(principal, structure, config, mapStructureToPermission.get(structure)); + const ok2 = await this._check_second_level_permission(principal, structure, config); - if(!ok2) return fail(403, `You don't have ${mapStructureToPermission.get(structure)} permission for dataset source(s).`); + if(!ok2) return fail(this.log, 403, `You don't have permission for dataset source(s) ${dataset_uuid}`); // Create new Dataset Object if(!objectUuid){ objectUuid = await this.cdb.create_object(Constants.Class.Dataset); - - if(!objectUuid) return fail(500, `New Dataset object couldn't be created in ConfigDB.`); - this.log("Created new dataset object in ConfigDB", objectUuid); } @@ -571,27 +469,23 @@ export class APIv1 { async _create_subclass_relationship(structure, dataset_uuid, config) { if (structure === Constants.App.SessionLimits) { - - await rx.lastValueFrom( - rx.defer(() => - this.cdb.class_add_subclass(config.source, dataset_uuid) - ).pipe(retryBackoff(500, e => this.log(e))) - ); - + await this._add_cdb_subclass(config?.source, dataset_uuid); } else if (structure === Constants.App.UnionComponents) { - for (let source of config) { - await rx.lastValueFrom( - rx.defer(() => - this.cdb.class_add_subclass(dataset_uuid, source) - ).pipe(retryBackoff(500, e => this.log(e))) - ); + this._add_cdb_subclass(dataset_uuid, source); } - } } + async _add_cdb_subclass(klass, obj){ + await rx.lastValueFrom( + rx.defer(() => + this.cdb.class_add_subclass(klass, obj) + ).pipe(retryBackoff(500, e => this.log(e))) + ); + } + async _unlink_subclasses(structure, dataset_uuid, curr_config, new_config){ if(structure === Constants.App.SessionLimits){ @@ -614,7 +508,7 @@ export class APIv1 { }else if (structure === Constants.App.SparkplugSrc){ return; }else{ - return fail(422, `Unknown structure type ${structure}`); + return fail(this.log, 422, `Unknown structure type ${structure}`); } } @@ -633,7 +527,6 @@ export class APIv1 { null ); - if(!objectUuid) return fail(422, `Failed to create new Dataset.`); return res.status(200).json(objectUuid); } @@ -644,20 +537,29 @@ export class APIv1 { * @param {*} res */ + /* + If dataset valid: + - + */ async structure_update(req, res){ const dataset_uuid = req.params.uuid; - if(!valid_uuid(dataset_uuid)) return fail(422, `Dataset uuid is invalid.`); + if(!valid_uuid(dataset_uuid)) return fail(this.log, 422, `Invalid uuid ${dataset_uuid}`); const structure = req.body.structure; + if(!structure) return fail(this.log, 422, `Structure not provided`); const new_config = req.body.config; + if(!new_config) return fail(this.log, 422, `Config not provided`); // get current config - const all_defs = await rx.firstValueFrom(this.data.get_dataset_definitions(DatasetValidity.ALL)); - const curr_defs = all_defs.get(dataset_uuid); + const datasets = await rx.firstValueFrom(this.data.get_dataset_definitions(DatasetValidity.ALL)); + const dataset = datasets.get(dataset_uuid); + + + // const { config } = dataset; + // if(!config) return fail(this.log, 404, `Config not found for ${dataset_uuid}`); - const curr_config_for_structure = curr_defs.find(c => c.structure === structure)?.config; if(!curr_config_for_structure){ this.log(`Current config for structure does not exist.`); @@ -675,7 +577,7 @@ export class APIv1 { dataset_uuid ); - if(!objectUuid) return fail(422, `Failed to update dataset ${dataset_uuid}`); + if(!objectUuid) return fail(this.log, 422, `Failed to update dataset ${dataset_uuid}`); // remove definitions from other structure apps if the dataset was invalid const invalid_defs = await rx.firstValueFrom( diff --git a/acs-data-access/lib/dataflow.js b/acs-data-access/lib/dataflow.js index 680840793..c2963cce9 100644 --- a/acs-data-access/lib/dataflow.js +++ b/acs-data-access/lib/dataflow.js @@ -11,6 +11,7 @@ import * as rxu from "@amrc-factoryplus/rx-util"; import { DataAccess as Constants } from './constants.js'; import { valid_uuid, valid_krb, DatasetValidity } from "./validate.js"; import { RxClient, UUIDs } from '@amrc-factoryplus/rx-client'; +import { minDate, maxDate } from './utils.js'; export class DataFlow { constructor(opts) { @@ -18,7 +19,7 @@ export class DataFlow { this.cdb = opts.cdb; this.auth = opts.auth; - this.dataset_definitions = this._build_dataset_definitions(); + this.datasets = this._build_datasets(); this.general_infos = this._build_general_info(); this.metadata = this._build_metadata(); this.functional_types = this._build_functional_types(); @@ -38,9 +39,12 @@ export class DataFlow { // watch Dataset's members' subclasses _build_parts() { return rxu.rx( - this.cdb.watch_members_subclasses(Constants.Class.Dataset), - rx.map(map => map.map(subclass_uuids => - subclass_uuids.filter(x => this.cdb.class_has_member(Constants.Class.Dataset, x)) + rx.combineLatest([ + this.cdb.watch_members_subclasses(Constants.Class.Dataset), + this.cdb.watch_members(Constants.Class.Dataset) + ]), + rx.map(([subclassesMap, datasetMembers]) => subclassesMap.map( + subclass_uuids => subclass_uuids.filter(x => datasetMembers.has(x)) )), rxu.shareLatest() ); @@ -72,32 +76,33 @@ export class DataFlow { return rxu.rx( this.cdb.watch_members(Constants.App.DatasetMetadata), - rx.switchMap((uuidsSet) => { - const uuids = uuidsSet.toArray(); + rx.switchMap((metaAppMembersSet) => { + const metaAppUuids = metaAppMembersSet.toArray(); - if (uuids.length === 0) { + if (metaAppUuids.length === 0) { return rx.of(IMap()); } - const streams = uuids.map(uuid => - this.cdb.search_app(uuid).pipe( - rx.map(result => [uuid, result]) + const metadataStreams = metaAppUuids.map(appId => + this.cdb.search_app(appId).pipe( + rxu.shareLatest(), + rx.map(metadataByDataset => [appId, metadataByDataset]) ) ); - return rx.combineLatest(streams).pipe( - rx.map(entries => { - let map = IMap(); + return rx.combineLatest(metadataStreams).pipe( + rx.map(appEntries => { + let result = IMap(); - for (const [outerUuid, result] of entries) { - for (const [innerUuid, payload] of result) { - const existing = map.get(innerUuid, IMap()); + for (const [appId, metadataByDataset] of appEntries) { + for (const [datasetId, payload] of metadataByDataset) { + const existing = result.get(datasetId, IMap()); - map = map.set(innerUuid, existing.set(outerUuid, payload)); + result = result.set(datasetId, existing.set(appId, payload)); } } - return map; + return result; }) ); }), @@ -113,7 +118,14 @@ export class DataFlow { ); } - _build_dataset_definitions() { + + /* + 1. search structure apps for all dataset configs + 2. invalid if exists in more than one structural app + 3. invalid if contains self-referencing source or invalid source + 4. if invalid throw away the definition and create a new one with the same dataset_uuid but structure = Constants.Special.InvalidDataset + */ + _build_datasets() { return rxu.rx( rx.combineLatest([ this.cdb.search_app(Constants.App.SparkplugSrc), @@ -121,60 +133,212 @@ export class DataFlow { this.cdb.search_app(Constants.App.UnionComponents) ]), - rx.switchMap(([sprkDevices, sessions, unions]) => { + rx.map(([sprkDevices, sessions, unions]) => { const grouped = IMap({ [Constants.App.SessionLimits]: sessions, [Constants.App.UnionComponents]: unions, [Constants.App.SparkplugSrc]: sprkDevices }); - return rx.from(grouped.entrySeq()).pipe( - // Flatten all datasets across all apps - rx.mergeMap(([structure, datasets]) => - rx.from((datasets || IMap()).entrySeq()).pipe( - rx.map(([datasetId, config]) => ({ - datasetId, - definition: { structure, config } - })) - ) - ), - - // Group into: { datasetId: [definitions] } - rx.reduce((acc, { datasetId, definition }) => { - const existing = acc.get(datasetId, []); - return acc.set(datasetId, [...existing, definition]); - }, IMap()) - ); + // ---------------------------- + // 1. Flatten + group by datasetId + // ---------------------------- + let map = grouped.entrySeq().reduce((acc, [structure, datasets]) => { + return (datasets || IMap()).entrySeq().reduce((innerAcc, [datasetId, config]) => { + const existing = innerAcc.get(datasetId, []); + return innerAcc.set(datasetId, [ + ...existing, + { structure, config } + ]); + }, acc); + }, IMap()); + + // ---------------------------- + // 2. Normalise duplicates + // ---------------------------- + map = map.map((definitions) => { + if (definitions.length === 1) { + return definitions[0]; + } + + return { + structure: Constants.Special.InvalidDataset, + config: null + }; + }); + + // ---------------------------- + // 3. Recursive resolution (cached) + // ---------------------------- + const cache = new Map(); + + const resolve = (datasetId, visited = new Set()) => { + if (cache.has(datasetId)) return cache.get(datasetId); + + if (visited.has(datasetId)) { + const result = { + validity: DatasetValidity.INVALID, + from: null, + to: null, + error: 'Cycle detected' + }; + cache.set(datasetId, result); + return result; + } + + visited.add(datasetId); + + const def = map.get(datasetId); + + if (!def || def.structure === Constants.Special.InvalidDataset) { + const result = { + validity: DatasetValidity.INVALID, + from: null, + to: null + }; + cache.set(datasetId, result); + return result; + } + + const { structure, config } = def; + + // ------------------------ + // SESSION + // ------------------------ + if (structure === Constants.App.SessionLimits) { + const child = resolve(config.source, new Set(visited)); + + if (child.validity === DatasetValidity.INVALID) { + cache.set(datasetId, child); + return child; + } + + const from = config.from ? new Date(config.from) : null; + const to = config.to ? new Date(config.to) : null; + + const result = { + validity: DatasetValidity.VALID, + from: maxDate(from, child.from), + to: minDate(to, child.to) + }; + + cache.set(datasetId, result); + return result; + } + + // ------------------------ + // SPARKPLUG + // ------------------------ + if (structure === Constants.App.SparkplugSrc) { + const result = { + validity: DatasetValidity.VALID, + from: null, + to: null + }; + + cache.set(datasetId, result); + return result; + } + + // ------------------------ + // UNION + // ------------------------ + if (structure === Constants.App.UnionComponents) { + const children = config.map(id => + resolve(id, new Set(visited)) + ); + + const validChildren = children.filter( + c => c.validity === DatasetValidity.VALID + ); + + if (validChildren.length !== children.length) { + const result = { + validity: DatasetValidity.INVALID, + from: null, + to: null + }; + cache.set(datasetId, result); + return result; + } + + const fromTimes = validChildren + .map(v => v.from?.getTime()) + .filter(t => !isNaN(t)); + + const toTimes = validChildren + .map(v => v.to?.getTime()) + .filter(t => !isNaN(t)); + + const result = { + validity: DatasetValidity.VALID, + from: fromTimes.length + ? new Date(Math.min(...fromTimes)) + : null, + to: toTimes.length + ? new Date(Math.max(...toTimes)) + : null + }; + + cache.set(datasetId, result); + return result; + } + + // ------------------------ + // Fallback + // ------------------------ + const result = { + validity: DatasetValidity.INVALID, + from: null, + to: null + }; + + cache.set(datasetId, result); + return result; + }; + + // ---------------------------- + // 4. Resolve all datasets + // ---------------------------- + return map.map((def, datasetId) => { + const resolved = resolve(datasetId); + + return { + ...def, + ...resolved + }; + }); }), rxu.shareLatest() ); } - get_parts(){ - return this.parts; - } + async get_dataset_allowed_parts(dataset_uuid, principal, permission){ + const result = await rx.firstValueFrom( + rx.combineLatest([ + this.parts, + this.auth.watch_acl_with_perm(principal, permission) + ]).pipe( + rx.map(([partsMap, allowedParts]) => { + const parts = partsMap.get(dataset_uuid); - get_functional_types(){ - return this.functional_types; - } + if(!parts) return []; - get_metadata(){ - return this.metadata; - } + return parts.filter(partId => allowedParts.has(partId)); + }) + ) + ); - get_general_infos(){ - return this.general_infos; + return result; } - - async get_allowed_dataset_uuids(principal, permission, dataset_validity) { - const dataset_def_obs = this.get_dataset_definitions(dataset_validity); + const datasets = this.get_dataset_definitions(dataset_validity); - const result = await rx.firstValueFrom( + const allowed_datasets = await rx.firstValueFrom( rx.combineLatest([ - dataset_def_obs, + datasets, this.auth.watch_acl_with_perm(principal, permission) ]).pipe( rx.map(([datasets, targets]) => { @@ -183,26 +347,14 @@ export class DataFlow { return datasets .keySeq() // get all dataset IDs - .filter(id => targets.has(id)) - .toArray(); // convert to plain array + .filter(id => targets.has(id)); }) ) ); - return result; + return allowed_datasets; } - - async get_dataset_def_by_uuid(uuid){ - const def = await rx.firstValueFrom( - this.get_dataset_definitions(DatasetValidity.VALID).pipe( - rx.map(datasets => datasets.get(uuid)?.[0]) - ) - ); - return def; - } - - get_dataset_definitions(validity){ if(validity == DatasetValidity.VALID){ return this._get_valid_dataset_definitions(); @@ -216,21 +368,27 @@ export class DataFlow { } _get_all_dataset_definitions(){ - return this.dataset_definitions; + return this.datasets; } _get_valid_dataset_definitions() { - return this.dataset_definitions.pipe( + return this.datasets.pipe( rx.map((map) => - map.filter((definitions) => definitions.length === 1) + map.filter( + (definition) => + definition.structure !== Constants.Special.InvalidDataset + ) ) ); } _get_invalid_dataset_definitions() { - return this.dataset_definitions.pipe( + return this.datasets.pipe( rx.map((map) => - map.filter((definitions) => definitions.length > 1) + map.filter( + (definition) => + definition.structure === Constants.Special.InvalidDataset + ) ) ); } From 99596462fedfd0b6e56f11a2515942e23469a91f Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 7 May 2026 10:49:52 +0100 Subject: [PATCH 58/96] data access api structure create and update logic changed --- acs-data-access/lib/api-v1.js | 154 ++++++++++++++++++---------------- 1 file changed, 81 insertions(+), 73 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index b49a4f4a8..148725be4 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -432,24 +432,10 @@ export class APIv1 { } - async _update_dataset_config(principal, structure, config, objectUuid){ - if(!structure) return fail(this.log, 422, `Structure not provided.`); - if(!valid_uuid(structure)) return fail(this.log, 422, `Structure uuid ${structure} is invalid.`); - if(!config) return fail(this.log, 422, `Config not provided.`); - - const ok = await this.auth.check_acl( - principal, - Constants.Perm.CreateDataset, - structure, - true - ); - - if (!ok) return fail(this.log, 403, `You don't have Create permission for structure ${structure}`); - - // Check the principal has appropriate permission for all dataset sources in config. + async _update_dataset_config(principal, structure, config, objectUuid){ const ok2 = await this._check_second_level_permission(principal, structure, config); - if(!ok2) return fail(this.log, 403, `You don't have permission for dataset source(s) ${dataset_uuid}`); + if(!ok2) return fail(this.log, 403, `You don't have permission for source(s) in config.`); // Create new Dataset Object if(!objectUuid){ @@ -473,7 +459,7 @@ export class APIv1 { } else if (structure === Constants.App.UnionComponents) { for (let source of config) { - this._add_cdb_subclass(dataset_uuid, source); + await this._add_cdb_subclass(dataset_uuid, source); } } } @@ -486,29 +472,22 @@ export class APIv1 { ); } - async _unlink_subclasses(structure, dataset_uuid, curr_config, new_config){ - - if(structure === Constants.App.SessionLimits){ - if(curr_config.source != new_config.source){ + async _unlink_subclasses(dataset_uuid, structure, config){ + if (structure === Constants.App.SparkplugSrc) return; + else if(structure === Constants.App.SessionLimits){ // remove the dataset from subclasses of its source dataset - await this.cdb.class_remove_subclass(curr_config.source, dataset_uuid); - this.log(`Removed ${dataset_uuid} from ${curr_config.source} subclasses.`) - } + await this.cdb.class_remove_subclass(config.source, dataset_uuid); + this.log(`Removed ${dataset_uuid} from ${config.source} subclasses.`) + }else if(structure === Constants.App.UnionComponents){ - if(curr_config.length > 0){ - // if new_config does not include sources from curr_config -> remove subclass rel. - const sources_to_remove = curr_config.filter(x => !new_config.includes(x)); + if(config.length <= 0) return; - for (const source_uuid of sources_to_remove){ - await this.cdb.class_remove_subclass(dataset_uuid, source_uuid); - this.log(`Removed ${dataset_uuid} from ${source_uuid} subclasses.`); - } + for (const source_uuid of config){ + await this.cdb.class_remove_subclass(dataset_uuid, source_uuid); + this.log(`Removed ${dataset_uuid} from ${source_uuid} subclasses.`); } - - }else if (structure === Constants.App.SparkplugSrc){ - return; }else{ - return fail(this.log, 422, `Unknown structure type ${structure}`); + return fail(this.log, 404, `Unknown structure type ${structure}`); } } @@ -520,10 +499,26 @@ export class APIv1 { * @returns new dataset's UUID - JSON string */ async structure_create(req, res){ + const {structure, config} = req.body; + + if(!structure) return fail(this.log, 422, `Structure not provided in request body.`); + if(!valid_uuid(structure)) return fail(this.log, 422, `Structure uuid ${structure} is invalid.`); + + if(!config) return fail(this.log, 422, `Config not provided in request body.`) + + const ok = await this.auth.check_acl( + req.auth, + Constants.Perm.CreateDataset, + structure, + true + ); + + if (!ok) return fail(this.log, 403, `You don't have Create permission for structure ${structure}`); + const objectUuid = await this._update_dataset_config( req.auth, - req.body.structure, - req.body.config, + structure, + config, null ); @@ -536,14 +531,20 @@ export class APIv1 { * @param {*} req.body must be object (structure, config) and UUID (optional) * @param {*} res */ - /* - If dataset valid: - - + For VALID dataset: + Don't let changing the structure type only config. So, if request.body.structure != dataset.structure then return 409. + Remove all subclass relationships between dataset and its current config sources + Add subclass relationships between datasets and its new config sources + + For INVALID dataset: + Remove config entries for this dataset from all structure apps and ignore 404 + Don't remove any existing subclass relationships -> Completely ignore existing subclass relationships for Invalid ones. + Add subclass relationship between dataset and its new config source */ + async structure_update(req, res){ const dataset_uuid = req.params.uuid; - if(!valid_uuid(dataset_uuid)) return fail(this.log, 422, `Invalid uuid ${dataset_uuid}`); const structure = req.body.structure; @@ -552,52 +553,59 @@ export class APIv1 { const new_config = req.body.config; if(!new_config) return fail(this.log, 422, `Config not provided`); - // get current config + const ok = await this.auth.check_acl( + req.auth, + Constants.Perm.EditDataset, + dataset_uuid, + true + ); + + if (!ok) return fail(this.log, 403, `You don't have Edit permission for dataset ${dataset_uuid}`); + const datasets = await rx.firstValueFrom(this.data.get_dataset_definitions(DatasetValidity.ALL)); const dataset = datasets.get(dataset_uuid); + if(!dataset) return fail(this.log, 404, `Dataset ${dataset_uuid} not found.`); + const current_structure = dataset.structure; + const current_config = dataset.config; + const is_valid = (dataset.validity == DatasetValidity.VALID); - // const { config } = dataset; - // if(!config) return fail(this.log, 404, `Config not found for ${dataset_uuid}`); - - - if(!curr_config_for_structure){ - this.log(`Current config for structure does not exist.`); - } + // for VALID dataset + if(is_valid){ + if(current_structure != structure) return fail(this.log, 409, `Changing structure type is not allowed (current: ${current_structure}, new: ${structure})`); - // update subclass links if config def for this structure already exists and being updated. - if(curr_config_for_structure){ - await this._unlink_subclasses(structure, dataset_uuid,curr_config_for_structure, new_config); + // remove all subclass relationships with current config sources + await this._unlink_subclasses( + dataset_uuid, + current_structure, + current_config + ); } + // For INVALID dataset + else{ + // delete configs for all other structures + const all_structure_apps = Object.values(Constants.App); + + for(const s of all_structure_apps){ + try{ + await this.cdb.delete_config(s, dataset_uuid); + }catch(e){ + this.log(e); + } + } + } const objectUuid = await this._update_dataset_config( req.auth, structure, new_config, - dataset_uuid - ); - - if(!objectUuid) return fail(this.log, 422, `Failed to update dataset ${dataset_uuid}`); - - // remove definitions from other structure apps if the dataset was invalid - const invalid_defs = await rx.firstValueFrom( - - this.data.get_dataset_definitions(DatasetValidity.INVALID).pipe( - rx.map(datasets => datasets.get(dataset_uuid) ?? []), - rx.map(defs => defs.filter(d => d.structure !== req.body.structure)), - rx.map(filtered => filtered.length ? filtered : undefined) - ) - + dataset_uuid, ); - if (invalid_defs?.length) { - this.log(`Deleting invalid defs from ConfigDB`); - - for (const i_def of invalid_defs) { - this.cdb.delete_config(i_def.structure, objectUuid); - } + if(!objectUuid){ + return fail(this.log, 500, `Failed to update dataset ${dataset_uuid}`); } - + return res.status(200).json(objectUuid); } } From f787886d8ec9ae903a682ea02c355c7bbc870188 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 7 May 2026 11:14:20 +0100 Subject: [PATCH 59/96] data access api PUT structure added ServiceError check --- acs-data-access/lib/api-v1.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 148725be4..288419011 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -8,6 +8,7 @@ import { Map as IMap, Seq as ISeq } from "immutable"; import * as rx from "rxjs"; import { APIError } from "@amrc-factoryplus/service-api"; +import { ServiceError } from "@amrc-factoryplus/service-client"; import { retryBackoff } from "@amrc-factoryplus/rx-util"; import { DataAccess as Constants } from "./constants.js"; import { valid_uuid, DatasetValidity } from "./validate.js"; @@ -590,7 +591,7 @@ export class APIv1 { try{ await this.cdb.delete_config(s, dataset_uuid); }catch(e){ - this.log(e); + ServiceError.check(404); } } } From 3328b8fbd5a458b88c103d0d2d6b50f788a0711d Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 7 May 2026 11:33:27 +0100 Subject: [PATCH 60/96] data service service client - added status to error throw --- .../lib/service/data-access.js | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/js-service-client/lib/service/data-access.js b/lib/js-service-client/lib/service/data-access.js index 0f4c42782..2b630e206 100644 --- a/lib/js-service-client/lib/service/data-access.js +++ b/lib/js-service-client/lib/service/data-access.js @@ -24,11 +24,11 @@ export class DataAccess extends ServiceInterface{ */ async get_readable_dataset_uuids(){ const [st, json] = await this.fetch('v1/metadata'); - if(st === 404) return []; + if(st == 404) return []; if(st == 403) - this.throw(`Unauthorised to access dataset ${uuid}`); - if(st !== 200) - this.throw(`Can't get dataset uuids ${st} ${json}`); + this.throw(`Unauthorised get dataset uuids`, st); + if(st != 200) + this.throw(`Can't get dataset uuids`, st); return json; } @@ -40,9 +40,9 @@ export class DataAccess extends ServiceInterface{ const [st, json] = await this.fetch(`v1/metadata/${uuid}`); if(st == 404) return {}; if(st == 403) - this.throw(`Unauthorised to access dataset ${uuid}`); + this.throw(`Unauthorised to access dataset ${uuid}`, st); if(st != 200) - this.throw(`Can't get metadata for dataset ${uuid}\n ${st} ${json}`); + this.throw(`Can't get metadata for dataset ${uuid}`, st); return json; } @@ -55,11 +55,11 @@ export class DataAccess extends ServiceInterface{ method: 'POST', }); - if (st === 404) return "Not Found"; - if (st === 403) - throw new Error(`Unauthorised to access dataset ${uuid}`); + if (st == 404) return "Not Found"; + if (st == 403) + this.throw(`Unauthorised to access dataset ${uuid}`, st); if (st != 200) - throw new Error(`Can't download data for dataset ${uuid} ${res.status}`); + this.throw(`Can't download data for dataset ${uuid}`, st); // const buffer = Buffer.from(await res.arrayBuffer()); @@ -81,9 +81,9 @@ export class DataAccess extends ServiceInterface{ const [st, json] = await this.fetch('v1/structure'); if(st == 404) return []; if(st == 403) - this.throw(`Unauthorised to access dataset ${uuid}`); + this.throw(`Unauthorised to get dataset uuids`, st); if(st != 200) - this.throw(`Can't get dataset uuids ${st} ${json}`); + this.throw(`Can't get dataset uuids`, st); return json; } @@ -104,9 +104,9 @@ export class DataAccess extends ServiceInterface{ if(st == 404) return ""; if(st == 403) - this.throw(`Unauthorised to create dataset`); + this.throw(`Unauthorised to create a dataset`, st); if(st != 200) - this.throw(`Can't create dataset ${st} ${json}`); + this.throw(`Can't create a dataset`, st); return json; } @@ -120,9 +120,9 @@ export class DataAccess extends ServiceInterface{ const [st, json] = await this.fetch(`v1/structure/${uuid}`); if(st == 404) return []; if(st == 403) - this.throw(`Unauthorised to access dataset ${uuid}`); + this.throw(`Unauthorised to access dataset ${uuid}`, st); if(st != 200) - this.throw(`Can't get dataset definition ${st} ${json}`); + this.throw(`Can't get dataset definition ${uuid}`, st); return json; } @@ -138,9 +138,9 @@ export class DataAccess extends ServiceInterface{ }); if(st == 404) return []; if(st == 403) - this.throw(`Unauthorised to update dataset ${uuid}`); + this.throw(`Unauthorised to update dataset ${uuid}`, st); if(st != 200) - this.throw(`Can't update dataset definition ${st} ${json}`); + this.throw(`Can't update dataset definition ${uuid}`, st); return json; } } \ No newline at end of file From 2085638113816cefc694d2480cce55213c974d01 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 7 May 2026 12:00:53 +0100 Subject: [PATCH 61/96] data access api removed code duplication for creating subclasses --- acs-data-access/lib/api-v1.js | 37 +++++++---------------------------- 1 file changed, 7 insertions(+), 30 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 288419011..8fcecbbf2 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -326,24 +326,6 @@ export class APIv1 { * value - actual data value * unit - Engineering unit (if available) * Don't return valid response if dataset is invalid - - Influx tags: - [ - "_start", - "_stop", - "_field", - "_measurement", - "bottomLevelInstance", - "bottomLevelSchema", - "device", - "group", - "node", - "path", - "topLevelInstance", - "topLevelSchema", - "usesInstances", - "usesSchemas" - ] */ async dataset_data(req, res) { const dataset_uuid = req.params.uuid; @@ -455,22 +437,17 @@ export class APIv1 { } async _create_subclass_relationship(structure, dataset_uuid, config) { - if (structure === Constants.App.SessionLimits) { - await this._add_cdb_subclass(config?.source, dataset_uuid); - } - else if (structure === Constants.App.UnionComponents) { - for (let source of config) { - await this._add_cdb_subclass(dataset_uuid, source); - } - } - } - - async _add_cdb_subclass(klass, obj){ + const subclasses = + structure == Constants.App.SessionLimits ? [[config.source, dataset_uuid]] + : structure == Constants.App.UnionComponents ? config.map(source => [dataset_uuid, source]) + : []; + for (const subclass of subclasses) { await rx.lastValueFrom( rx.defer(() => - this.cdb.class_add_subclass(klass, obj) + this.cdb.class_add_subclass(...subclass) ).pipe(retryBackoff(500, e => this.log(e))) ); + } } async _unlink_subclasses(dataset_uuid, structure, config){ From aae7bcdb304bd4d45a4a1f2f023c3b3745615f9f Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 7 May 2026 13:40:21 +0100 Subject: [PATCH 62/96] data access dataflow refactored build metadata and datasets --- acs-data-access/lib/dataflow.js | 162 ++++++++++++++++++-------------- 1 file changed, 91 insertions(+), 71 deletions(-) diff --git a/acs-data-access/lib/dataflow.js b/acs-data-access/lib/dataflow.js index c2963cce9..e291f31db 100644 --- a/acs-data-access/lib/dataflow.js +++ b/acs-data-access/lib/dataflow.js @@ -72,42 +72,46 @@ export class DataFlow { ); } - _build_metadata() { + _build_apps_configs(app_klass){ return rxu.rx( - this.cdb.watch_members(Constants.App.DatasetMetadata), + this.cdb.watch_members(app_klass), - rx.switchMap((metaAppMembersSet) => { - const metaAppUuids = metaAppMembersSet.toArray(); + rx.switchMap((membersSet) => { + const appUuids = membersSet.toArray(); - if (metaAppUuids.length === 0) { - return rx.of(IMap()); - } + if (appUuids.length === 0) { + return rx.of(IMap()); + } - const metadataStreams = metaAppUuids.map(appId => - this.cdb.search_app(appId).pipe( - rxu.shareLatest(), - rx.map(metadataByDataset => [appId, metadataByDataset]) - ) - ); + const dataStreams = appUuids.map(appId => + this.cdb.search_app(appId).pipe( + rxu.shareLatest(), + rx.map(config => [appId, config]) + ) + ); - return rx.combineLatest(metadataStreams).pipe( - rx.map(appEntries => { - let result = IMap(); + return rx.combineLatest(dataStreams).pipe( + rx.map(appEntries => { + let result = IMap(); - for (const [appId, metadataByDataset] of appEntries) { - for (const [datasetId, payload] of metadataByDataset) { - const existing = result.get(datasetId, IMap()); + for (const [appId, config] of appEntries) { + for (const [datasetId, payload] of config) { + const existing = result.get(datasetId, IMap()); - result = result.set(datasetId, existing.set(appId, payload)); - } - } + result = result.set(datasetId, existing.set(appId, payload)); + } + } - return result; - }) + return result; + }) + ); + }), + rxu.shareLatest() ); - }), - rxu.shareLatest() - ); + } + + _build_metadata() { + return this._build_apps_configs(Constants.App.DatasetMetadata); } @@ -119,6 +123,7 @@ export class DataFlow { } + /* 1. search structure apps for all dataset configs 2. invalid if exists in more than one structural app @@ -127,61 +132,66 @@ export class DataFlow { */ _build_datasets() { return rxu.rx( - rx.combineLatest([ - this.cdb.search_app(Constants.App.SparkplugSrc), - this.cdb.search_app(Constants.App.SessionLimits), - this.cdb.search_app(Constants.App.UnionComponents) - ]), + this._build_apps_configs(Constants.App.DatasetDefinition), - rx.map(([sprkDevices, sessions, unions]) => { - const grouped = IMap({ - [Constants.App.SessionLimits]: sessions, - [Constants.App.UnionComponents]: unions, - [Constants.App.SparkplugSrc]: sprkDevices - }); + rx.map((groupedConfigs) => { // ---------------------------- - // 1. Flatten + group by datasetId + // 1. Normalise duplicates // ---------------------------- - let map = grouped.entrySeq().reduce((acc, [structure, datasets]) => { - return (datasets || IMap()).entrySeq().reduce((innerAcc, [datasetId, config]) => { - const existing = innerAcc.get(datasetId, []); - return innerAcc.set(datasetId, [ - ...existing, - { structure, config } - ]); - }, acc); - }, IMap()); - - // ---------------------------- - // 2. Normalise duplicates - // ---------------------------- - map = map.map((definitions) => { - if (definitions.length === 1) { - return definitions[0]; + // + // Convert: + // + // datasetId => IMap({ + // appId => payload + // }) + // + // into: + // + // datasetId => { + // structure, + // config + // } + // + // or INVALID if multiple definitions exist + // + + let map = groupedConfigs.map((definitions) => { + const entries = definitions.entrySeq().toArray(); + + if (entries.length !== 1) { + return { + structure: Constants.Special.InvalidDataset, + config: null + }; } + const [structure, config] = entries[0]; + return { - structure: Constants.Special.InvalidDataset, - config: null + structure, + config }; }); // ---------------------------- - // 3. Recursive resolution (cached) + // 2. Recursive resolution (cached) // ---------------------------- + const cache = new Map(); const resolve = (datasetId, visited = new Set()) => { - if (cache.has(datasetId)) return cache.get(datasetId); + if (cache.has(datasetId)) + return cache.get(datasetId); if (visited.has(datasetId)) { const result = { validity: DatasetValidity.INVALID, from: null, to: null, - error: 'Cycle detected' + error: "Cycle detected" }; + cache.set(datasetId, result); return result; } @@ -190,12 +200,15 @@ export class DataFlow { const def = map.get(datasetId); - if (!def || def.structure === Constants.Special.InvalidDataset) { + if (!def || + def.structure === Constants.Special.InvalidDataset) { + const result = { validity: DatasetValidity.INVALID, from: null, to: null }; + cache.set(datasetId, result); return result; } @@ -205,6 +218,7 @@ export class DataFlow { // ------------------------ // SESSION // ------------------------ + if (structure === Constants.App.SessionLimits) { const child = resolve(config.source, new Set(visited)); @@ -213,8 +227,13 @@ export class DataFlow { return child; } - const from = config.from ? new Date(config.from) : null; - const to = config.to ? new Date(config.to) : null; + const from = config.from + ? new Date(config.from) + : null; + + const to = config.to + ? new Date(config.to) + : null; const result = { validity: DatasetValidity.VALID, @@ -229,6 +248,7 @@ export class DataFlow { // ------------------------ // SPARKPLUG // ------------------------ + if (structure === Constants.App.SparkplugSrc) { const result = { validity: DatasetValidity.VALID, @@ -243,6 +263,7 @@ export class DataFlow { // ------------------------ // UNION // ------------------------ + if (structure === Constants.App.UnionComponents) { const children = config.map(id => resolve(id, new Set(visited)) @@ -258,6 +279,7 @@ export class DataFlow { from: null, to: null }; + cache.set(datasetId, result); return result; } @@ -287,6 +309,7 @@ export class DataFlow { // ------------------------ // Fallback // ------------------------ + const result = { validity: DatasetValidity.INVALID, from: null, @@ -298,16 +321,13 @@ export class DataFlow { }; // ---------------------------- - // 4. Resolve all datasets + // 3. Resolve all datasets // ---------------------------- - return map.map((def, datasetId) => { - const resolved = resolve(datasetId); - return { - ...def, - ...resolved - }; - }); + return map.map((def, datasetId) => ({ + ...def, + ...resolve(datasetId) + })); }), rxu.shareLatest() From 761a121a61ab7cfdf2b74524c0d01d472978e451 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 11 May 2026 16:14:44 +0100 Subject: [PATCH 63/96] data access - streaming data download --- acs-data-access/lib/api-v1.js | 246 ++++++++++++--------------- acs-data-access/lib/influx-reader.js | 155 +++++++++++------ acs-data-access/lib/utils.js | 41 +++-- 3 files changed, 226 insertions(+), 216 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 8fcecbbf2..38dd1fb49 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -4,7 +4,7 @@ */ import express from "express"; -import { Map as IMap, Seq as ISeq } from "immutable"; +import { Map as IMap, Seq as ISeq, merge } from "immutable"; import * as rx from "rxjs"; import { APIError } from "@amrc-factoryplus/service-api"; @@ -12,7 +12,7 @@ import { ServiceError } from "@amrc-factoryplus/service-client"; import { retryBackoff } from "@amrc-factoryplus/rx-util"; import { DataAccess as Constants } from "./constants.js"; import { valid_uuid, DatasetValidity } from "./validate.js"; -import { convertToCsv } from './utils.js'; +import { csv_escape, maxDate, minDate } from './utils.js'; function fail(log, status, message) { @@ -183,138 +183,99 @@ export class APIv1 { } } - _merge_intervals(queries) { - return ISeq(queries) - .groupBy(q => q.source) - .flatMap((group, source) => { - const intervals = group - .map(q => ({ - from: q.from ? new Date(q.from) : new Date(0), - to: q.to ? new Date(q.to) : new Date() - })) - .sortBy(i => i.from) - .toArray(); - - const merged = []; - - for (const interval of intervals) { - if (!merged.length) { - merged.push(interval); - continue; - } - - const last = merged[merged.length - 1]; - - if (interval.from <= last.to) { - last.to = new Date(Math.max(last.to, interval.to)); - } else { - merged.push(interval); - } - } - - return merged.map(m => ({ - source, - from: m.from, - to: m.to - })); - }) - .toArray(); - } + async _resolve_dataset( + dataset_uuid, + inherited_range = {from: null, to: null}, + visited = new Set(), + ){ + // prevent circular references + if(visited.has(dataset_uuid)) return fail(this.log, 404, `Circular dataset reference detected ${dataset_uuid}`); - // Resolve nested datasets - async _resolve_dataset(dataset_uuid, from=null, to=null, visited = new Set()) { - const datasets = await rx.firstValueFrom(this.data.datasets); - const dataset = await datasets.get(dataset_uuid); + visited.add(dataset_uuid); - if (!dataset) return fail(this.log, 404, `Dataset not found for ${dataset_uuid}`); - - if(dataset.validity === DatasetValidity.INVALID) return fail(this.log, 404, `Invalid dataset ${dataset_uuid}`); - - if(visited.has(dataset_uuid)) return fail(this.log, 404, `Circular dataset reference detected at ${dataset_uuid}`); + const datasets = await rx.firstValueFrom(this.data.datasets); + const dataset = await datasets.get(dataset_uuid); - visited.add(dataset_uuid); + if(!dataset) return fail(this.log, 404, `Dataset not found ${dataset_uuid}`); - const { structure, config } = dataset; + if(dataset.validity !== DatasetValidity.VALID) return fail(this.log, 404, `Invalid dataset ${dataset_uuid}`); + + const {structure, config} = dataset; + if(!structure) return fail(this.log, 404, `Structure not found for dataset ${dataset_uuid}`); + if(!config) return fail(this.log, 404, `Config not found for ${dataset_uuid}`); + + // ====================================================== + // SparkplugSRC + // ====================================================== + + if(structure == Constants.App.SparkplugSrc){ + return [ + { + dataset_uuid, + device_uuid: config.source, + from: inherited_range.from, + to: inherited_range.to, + }, + ]; + } - if(!structure) return fail(this.log, 404, `Structure not found for ${dataset_uuid}`); + // ====================================================== + // SessionLimits + // ====================================================== - if(!valid_uuid(structure)) return fail(this.log, 404, `Invalid structure uuid for ${dataset_uuid}`); - - // ------------------------- - // 1. SparkplugSrc (BASE CASE) - // ------------------------- - if (structure === Constants.App.SparkplugSrc) { - const source_uuid = config?.source; - - if (!source_uuid) return fail(this.log, 404, `Dataset def ${dataset_uuid} does not contain source uuid.`); - - if(!valid_uuid(source_uuid)) return fail(this.log, 404, `Dataset ${dataset_uuid}'s source uuid is invalid.`); - - return [{ - source: source_uuid, - from, - to - }]; - } - - - - // ------------------------- - // 2. SessionLimits (SINGLE REFERENCE) - // ------------------------- - else if (structure === Constants.App.SessionLimits) { - const source_uuid = config?.source; - - if (!source_uuid) return fail(this.log, 404, `Dataset def ${dataset_uuid} does not contain source uuid`); - - if(!valid_uuid(source_uuid)) return fail(this.log, 404, `Dataset ${dataset_uuid}'s source is invalid uuid`); - - const session_from = config?.from ? new Date(config.from) : null; - const session_to = config?.to ? new Date(config.to) : null; - - const new_from = from && session_from - ? new Date(Math.max(from, session_from)) - : (from || session_from); - - const new_to = to && session_to - ? new Date(Math.min(to, session_to)) - : (to || session_to); - - if(new_from && new_to && new_from >= new_to){ - return []; // no overlap - } + if(structure == Constants.App.SessionLimits){ + const session_range = { + from: config.from ?? null, + to: config.to ?? null, + }; + + const merged_range = this._intersectRanges( + inherited_range, + session_range, + ); - return this._resolve_dataset(source_uuid, new_from, new_to, visited); - } + return this._resolve_dataset( + config.source, + merged_range, + visited, + ); + } + // ====================================================== + // UnionComponents + // ====================================================== + if(structure == Constants.App.UnionComponents){ + if(!Array.isArray(config)) return fail(this.log, 404, `Union dataset config must be an array.`); + const results = []; - // ------------------------- - // 3. UnionComponents (MULTIPLE REFERENCES) - // ------------------------- - else if (structure === Constants.App.UnionComponents) { - if(!Array.isArray(config)) return fail(this.log, 404, `Union structure type dataset def must be Array ${dataset_uuid}`); - - const results = await Promise.all( - config.map(uuid => - this._resolve_dataset(uuid, from, to, new Set(visited)) - ) + for (const src_ds_uuid of config){ + const src_results = await this._resolve_dataset( + src_ds_uuid, + inherited_range, + new Set(visited), ); - - return results.flat(); + results.push(...src_results); } + return results; + } + return fail(this.log, 404, `Unknown dataset structure ${structure}`); + } + _intersectRanges(parent, child){ + const from = maxDate(parent.from, child.from); + const to = minDate(parent.to, child.to); - // ------------------------- - // 4. Unknown structure - // ------------------------- - else { - return fail(this.log, 404, `Dataset ${dataset_uuid} has unknown structure type ${structure}`); + if(from && to && new Date(from) > new Date(to)){ + return fail(this.log, 404, `Invalid intersected range. From: ${from} > to: ${to}`); } + + return {from, to}; } + /** POST. Queries all possible Influx suffixes, combines the measuremenets and returns actual data from a dataset. * Empty POST body requests all dataset measurements. * @param {*} req @@ -328,41 +289,44 @@ export class APIv1 { * Don't return valid response if dataset is invalid */ async dataset_data(req, res) { + const dataset_uuid = req.params.uuid; - if (!valid_uuid(dataset_uuid)) return fail(this.log, 422, `Dataset uuid ${dataset_uuid} is invalid.`); + + if (!valid_uuid(dataset_uuid)) { + return fail(this.log, 422, `Invalid dataset uuid`); + } const ok = await this.auth.check_acl( - req.auth, - Constants.Perm.ReadDataset, - dataset_uuid, - true, + req.auth, + Constants.Perm.ReadDataset, + dataset_uuid, + true, ); - if (!ok) return fail(this.log, 403, `You don't have Read permission for dataset ${dataset_uuid}`); + + if (!ok) { + return fail(this.log, 403); + } try { - const resolved = await this._resolve_dataset(dataset_uuid); - const merged = this._merge_intervals(resolved); - - const results = await Promise.all( - merged.map(q => - this.influxReader.get_dataset_data(q.source, { - from: q.from?.toISOString(), - to: q.to?.toISOString() - }) - ) - ); - const rows = results.flat(); - this.log(`# influx rows = ${rows.length}`); + // Resolve dataset tree + const resolved = await this._resolve_dataset(dataset_uuid); - const csv = convertToCsv(rows); + res.setHeader("Content-Type", "text/csv"); + res.setHeader( + "Content-Disposition", + `attachment; filename="${dataset_uuid}.csv"` + ); - res.setHeader("Content-Type", "text/csv"); - res.setHeader("Content-Disposition", `attachment; filename=${dataset_uuid}.csv`); - return res.status(200).send(csv); + await this.influxReader.stream_dataset_data( + resolved, + res, + {}, + ); - } catch (err) { - this.log(err); - return fail(this.log, 500); + res.end(); + }catch (err) { + this.log(err); + return fail(this.log, 500); } } diff --git a/acs-data-access/lib/influx-reader.js b/acs-data-access/lib/influx-reader.js index a7d0fa35e..02ffb71be 100644 --- a/acs-data-access/lib/influx-reader.js +++ b/acs-data-access/lib/influx-reader.js @@ -1,5 +1,6 @@ import {InfluxDB, flux} from '@influxdata/influxdb-client'; +import {csv_escape} from './utils.js'; export class InfluxReader{ constructor(opts){ @@ -9,79 +10,125 @@ export class InfluxReader{ this.influx_query_api = opts.influx_client.getQueryApi(opts.influx_org); } - async get_dataset_data(topLevelInstance, meta = {}) { - const { from, to, measurement } = meta; + async stream_dataset_data(device_sources, writable, meta = {}) { - // Build range - const rangeClause = (from && to) - ? `|> range(start: ${from}, stop: ${to})` - : `|> range(start: 0)`; + const measurementFilter = meta.measurement + ? `|> filter(fn: (r) => r._measurement == "${meta.measurement}")` + : ``; - // Optional measurement filter - const measurementFilter = measurement - ? `|> filter(fn: (r) => r._measurement == "${measurement}")` - : ``; + // ===================================================== + // Device + per-device session filtering + // ===================================================== - const query = ` - import "strings" + const deviceConditions = device_sources.map(src => { + + const timeParts = []; + + if (src.from) { + timeParts.push(`r._time >= time(v: "${src.from}")`); + } + + if (src.to) { + timeParts.push(`r._time <= time(v: "${src.to}")`); + } + + const timeExpr = timeParts.length + ? ` and ${timeParts.join(" and ")}` + : ""; + + return `(r.topLevelInstance == "${src.device_uuid}"${timeExpr})`; + + }).join(" or "); + + // ===================================================== + // Global bounds + // ===================================================== + + const froms = device_sources + .map(s => s.from) + .filter(Boolean) + .sort(); + const tos = device_sources + .map(s => s.to) + .filter(Boolean) + .sort(); + + const globalFrom = froms.length + ? froms[0] + : "0"; + + const globalTo = tos.length + ? tos[tos.length - 1] + : null; + + const rangeClause = globalTo + ? `|> range(start: time(v: "${globalFrom}"), stop: time(v: "${globalTo}"))` + : `|> range(start: ${globalFrom})`; + + // ===================================================== + // QUERY + // ===================================================== + + const query = ` from(bucket: "${this.influx_bucket}") + ${rangeClause} - |> filter(fn: (r) => r.topLevelInstance == "${topLevelInstance}") + + |> filter(fn: (r) => ${deviceConditions}) + ${measurementFilter} - |> drop(columns: [ - "_start","_stop","_field","table", - "bottomLevelInstance","bottomLevelSchema", - "group","node","topLevelSchema", - "usesInstances","usesSchemas" - ]) - |> map(fn: (r) => ({ r with path: if exists r.path then r.path else "" })) + + |> map(fn: (r) => ({ + r with + column_name: + if exists r.path and r.path != "" + then r.device + ":" + r.path + ":" + r._measurement + else r.device + ":" + r._measurement + })) + + // IMPORTANT: + // remove grouping before pivot + |> group() + |> pivot( - rowKey: ["_time"], - columnKey: ["_measurement"], - valueColumn: "_value" + rowKey: ["_time", "unit"], + columnKey: ["column_name"], + valueColumn: "_value" ) - |> group() + |> sort(columns: ["_time"], desc: false) `; - const res = await this._run_influx(query); - return res; + + return this._stream_influx_csv(query, writable); } - async _run_min_max_time_agg(source_uuid, fn) { - const query = ` - from(bucket: "${this.influx_bucket}") - |> range(start: 0) - |> filter(fn: (r) => r.topLevelInstance == "${source_uuid}") - |> keep(columns: ["_time"]) - |> group() - |> ${fn}(column: "_time") - `; + async _stream_influx_csv(query, writable) { + return new Promise((resolve, reject) => { + let headersWritten = false; - const rows = await this._run_influx(query); + this.influx_query_api.queryRows(query, { + next: (row, tableMeta) => { + const obj = tableMeta.toObject(row); - return rows?.[0]?._time ?? null; - } + // Write headers once + if (!headersWritten) { + writable.write( + Object.keys(obj).join(",") + "\n" + ); + headersWritten = true; + } - async get_influx_time_bounds(topLevelInstance) { - const [from, to] = await Promise.all([ - this._run_min_max_time_agg(topLevelInstance, "min"), - this._run_min_max_time_agg(topLevelInstance, "max"), - ]); + const csvRow = Object.values(obj) + .map(v => csv_escape(v)) + .join(","); - return { from, to }; - } + writable.write(csvRow + "\n"); + }, - async _run_influx(query) { - return new Promise((resolve, reject) => { - const rows = []; + error: reject, - this.influx_query_api.queryRows(query, { - next: (row, tableMeta) => { - rows.push(tableMeta.toObject(row)); - }, - error: reject, - complete: () => resolve(rows) + complete: resolve, }); }); } diff --git a/acs-data-access/lib/utils.js b/acs-data-access/lib/utils.js index ea285014f..ac6430d17 100644 --- a/acs-data-access/lib/utils.js +++ b/acs-data-access/lib/utils.js @@ -1,10 +1,13 @@ -function escapeCsv(value) { +export function csv_escape(value) { if (value == null) return ""; const str = String(value); - // If it contains special chars, wrap in quotes - if (/[",\n\r]/.test(str)) { + if ( + str.includes(",") || + str.includes('"') || + str.includes("\n") + ) { return `"${str.replace(/"/g, '""')}"`; } @@ -12,28 +15,24 @@ function escapeCsv(value) { } -export function convertToCsv (rows){ - if (!rows.length) return ""; - - const headers = Object.keys(rows[0]); - const lines = rows.map(row => - headers.map(h => escapeCsv(row[h])).join(",") - ); - - return [headers.join(","), ...lines].join("\n"); -} +// const toTime = d => d ? new Date(d).getTime() : null; +export function maxDate(a, b) { -const toTime = d => d ? new Date(d).getTime() : null; + if (!a) return b + if (!b) return a -export function maxDate(a, b) { - if (!a) return b; - if (!b) return a; - return toTime(a) > toTime(b) ? a : b; + return new Date(a) > new Date(b) + ? a + : b } export function minDate(a, b) { - if (!a) return b; - if (!b) return a; - return toTime(a) < toTime(b) ? a : b; + + if (!a) return b + if (!b) return a + + return new Date(a) < new Date(b) + ? a + : b } \ No newline at end of file From 18e713a12a7206f9d5c100d662def1a3bc3dd518 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 13 May 2026 11:27:20 +0100 Subject: [PATCH 64/96] data access - refactored removed datasetvalidity check --- acs-data-access/lib/api-v1.js | 66 +-- acs-data-access/lib/dataflow.js | 814 +++++++++++++++++++++----------- acs-data-access/lib/validate.js | 9 +- 3 files changed, 587 insertions(+), 302 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 38dd1fb49..5bad6953a 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -11,7 +11,7 @@ import { APIError } from "@amrc-factoryplus/service-api"; import { ServiceError } from "@amrc-factoryplus/service-client"; import { retryBackoff } from "@amrc-factoryplus/rx-util"; import { DataAccess as Constants } from "./constants.js"; -import { valid_uuid, DatasetValidity } from "./validate.js"; +import { valid_uuid } from "./validate.js"; import { csv_escape, maxDate, minDate } from './utils.js'; @@ -61,7 +61,7 @@ export class APIv1 { * Don't return invalid datasets */ async metadata_list(req, res) { - const uuids = await this.data.get_allowed_dataset_uuids(req.auth, Constants.Perm.ReadDataset, DatasetValidity.VALID); + const uuids = await rx.firstValueFrom(this.data.allowed_valid_dataset_uuids(req.auth, Constants.Perm.ReadDataset)); return res.status(200).json(uuids); } @@ -91,32 +91,36 @@ export class APIv1 { if (!ok) return fail(this.log, 403, `You don't have READ permissions for ${dataset_uuid}`); - const datasets = await rx.firstValueFrom(this.data.get_dataset_definitions(DatasetValidity.VALID)); - const dataset = await datasets.get(dataset_uuid); - if(!dataset) return fail(this.log, 404, `Dataset ${dataset_uuid} is not found or invalid.`); - - const from = dataset.config?.from; - const to = dataset.config?.to; - - const infos = await rx.firstValueFrom(this.data.general_infos); - const info = infos.get(dataset_uuid); - - const all_f_types = await rx.firstValueFrom(this.data.functional_types); - const f_types = all_f_types.get(dataset_uuid); - const all_metadata = await rx.firstValueFrom(this.data.metadata); - const metadata = all_metadata.get(dataset_uuid); + const [ + datasets, + infos, + all_f_types, + all_metadata, + parts + ] = await Promise.all([ + rx.firstValueFrom(this.data.allowed_valid_datasets(req.auth, Constants.Perm.ReadDataset)), + rx.firstValueFrom(this.data.general_infos), + rx.firstValueFrom(this.data.functional_types), + rx.firstValueFrom(this.data.metadata), + rx.firstValueFrom( + this.data.allowed_dataset_parts(dataset_uuid, req.auth, Constants.Perm.ReadDataset) + ) + ]); - const parts = await this.data.get_dataset_allowed_parts(dataset_uuid, req.auth, Constants.Perm.ReadDataset); + const dataset = datasets.get(dataset_uuid); + if(!dataset) return fail(this.log, 404, `Dataset ${dataset_uuid} is not found or invalid.`); + + const info = infos.get(dataset_uuid); const meta = { uuid: dataset_uuid, - name: info ? info.name : "UNKNOWN", - from: from ? from : undefined, - to: to ? to : undefined, - function: f_types, - metadata, - parts: parts + name: info.name ? info.name : "UNKNOWN", + from: dataset.from ? dataset.from : undefined, + to: dataset.to ? dataset.to : undefined, + function: all_f_types.get(dataset_uuid), + metadata: all_metadata.get(dataset_uuid), + parts } return res.status(200).json(meta); } @@ -195,15 +199,17 @@ export class APIv1 { visited.add(dataset_uuid); const datasets = await rx.firstValueFrom(this.data.datasets); - const dataset = await datasets.get(dataset_uuid); + const dataset = datasets.get(dataset_uuid); if(!dataset) return fail(this.log, 404, `Dataset not found ${dataset_uuid}`); - if(dataset.validity !== DatasetValidity.VALID) return fail(this.log, 404, `Invalid dataset ${dataset_uuid}`); - const {structure, config} = dataset; if(!structure) return fail(this.log, 404, `Structure not found for dataset ${dataset_uuid}`); + + if(structure === Constants.Special.InvalidDataset) return fail(this.log, 404, `Invalid dataset ${dataset_uuid}`); + if(!config) return fail(this.log, 404, `Config not found for ${dataset_uuid}`); + // ====================================================== // SparkplugSRC @@ -339,7 +345,7 @@ export class APIv1 { * the response includes invalid datasets */ async structure_list(req, res){ - const uuids = await this.data.get_allowed_dataset_uuids(req.auth, Constants.Perm.EditDataset, DatasetValidity.ALL); + const uuids = await rx.firstValueFrom(this.data.allowed_all_dataset_uuids(req.auth, Constants.Perm.EditDataset)); return res.status(200).json(uuids); } @@ -369,7 +375,7 @@ export class APIv1 { if (!ok) return fail(this.log, 403, `You don't have permission to Edit dataset ${uuid}.`); const dataset = await rx.firstValueFrom( - this.data.get_dataset_definitions(DatasetValidity.ALL).pipe( + this.data.allowed_all_datasets(req.auth, Constants.Perm.EditDataset).pipe( rx.map(datasets => datasets.get(dataset_uuid)) ) ); @@ -504,13 +510,13 @@ export class APIv1 { if (!ok) return fail(this.log, 403, `You don't have Edit permission for dataset ${dataset_uuid}`); - const datasets = await rx.firstValueFrom(this.data.get_dataset_definitions(DatasetValidity.ALL)); + const datasets = await rx.firstValueFrom(this.data.allowed_all_datasets(req.auth, Constants.Perm.EditDataset)); const dataset = datasets.get(dataset_uuid); if(!dataset) return fail(this.log, 404, `Dataset ${dataset_uuid} not found.`); const current_structure = dataset.structure; const current_config = dataset.config; - const is_valid = (dataset.validity == DatasetValidity.VALID); + const is_valid = current_structure !== Constants.Special.InvalidDataset; // for VALID dataset if(is_valid){ diff --git a/acs-data-access/lib/dataflow.js b/acs-data-access/lib/dataflow.js index e291f31db..29c037713 100644 --- a/acs-data-access/lib/dataflow.js +++ b/acs-data-access/lib/dataflow.js @@ -3,14 +3,13 @@ * Data-Flow / sequence management */ -import { List as IList, Map as IMap, Set as ISet } from "immutable"; +import { List as IList, Map as IMap } from "immutable"; import rx from "rxjs"; -import * as rxu from "@amrc-factoryplus/rx-util"; +import * as rxu from "@amrc-factoryplus/rx-util"; -import { DataAccess as Constants } from './constants.js'; -import { valid_uuid, valid_krb, DatasetValidity } from "./validate.js"; -import { RxClient, UUIDs } from '@amrc-factoryplus/rx-client'; +import { DataAccess as Constants } from './constants.js'; +import { UUIDs } from '@amrc-factoryplus/rx-client'; import { minDate, maxDate } from './utils.js'; export class DataFlow { @@ -20,6 +19,11 @@ export class DataFlow { this.auth = opts.auth; this.datasets = this._build_datasets(); + + this.valid_datasets = this._build_valid_datasets(); + + this.invalid_datasets = this._build_invalid_datasets(); + this.general_infos = this._build_general_info(); this.metadata = this._build_metadata(); this.functional_types = this._build_functional_types(); @@ -27,33 +31,184 @@ export class DataFlow { } run() { - // this.dataset_definitions.subscribe(ss => + // this.datasets.subscribe(ss => // this.log("Dataset Definitions UPDATE %o", ss.toJS()) // ); + } + + /* + * --------------------------------------------------- + * Rx Operators + * --------------------------------------------------- + */ + + filter_allowed_datasets(principal, permission) { + const acls = this.auth.watch_acl_with_perm( + principal, + permission + ); + + return source => source.pipe( + rx.combineLatestWith(acls), + + rx.map(([datasets, targets]) => + datasets.filter( + (_, datasetId) => targets.has(datasetId) + ) + ) + ); + } + + select_dataset_uuids() { + return source => source.pipe( + rx.map(datasets => datasets.keySeq()) + ); + } + + /* + * ----------------------------------------------- + * Public composed streams + * --------------------------------------------------- + */ + + allowed_valid_datasets(principal, permission) { + return this.valid_datasets.pipe( + this.filter_allowed_datasets( + principal, + permission + ) + ); + } + + allowed_valid_dataset_uuids(principal, permission) { + return this.valid_datasets.pipe( + this.filter_allowed_datasets( + principal, + permission + ), + + this.select_dataset_uuids() + ); + } + + allowed_invalid_datasets(principal, permission) { + return this.invalid_datasets.pipe( + this.filter_allowed_datasets( + principal, + permission + ) + ); + } + + allowed_invalid_dataset_uuids(principal, permission) { + return this.invalid_datasets.pipe( + this.filter_allowed_datasets( + principal, + permission + ), + + this.select_dataset_uuids() + ); + } + + allowed_all_datasets(principal, permission) { + return this.datasets.pipe( + this.filter_allowed_datasets( + principal, + permission + ) + ); + } + + allowed_all_dataset_uuids(principal, permission) { + return this.datasets.pipe( + this.filter_allowed_datasets( + principal, + permission + ), + + this.select_dataset_uuids() + ); + } + + /* + * ------------------------------------------------ + * Parts + * ------------------------------------------------ + */ + + allowed_dataset_parts( + dataset_uuid, + principal, + permission + ) { + return rx.combineLatest([ + this.parts, + this.auth.watch_acl_with_perm( + principal, + permission + ) + ]).pipe( + rx.map(([partsMap, allowedParts]) => { + const parts = + partsMap.get(dataset_uuid); + + if (!parts) + return []; - // this.log("GENERAL INFO UUID: ", UUIDs.App.Info); - // this.general_infos.subscribe(x => this.log("General infos UPDATE %o", x.toJS())); - // this.parts.subscribe(x => this.log("Parts UPDATE %o", x.toJS())); + return parts.filter( + partId => + allowedParts.has(partId) + ); + }), + + rxu.shareLatest() + ); } - // watch Dataset's members' subclasses + /* + * -------------------------------------------------- + * Watch Dataset's members' subclasses + * ---------------------------------------------------- + */ + _build_parts() { return rxu.rx( rx.combineLatest([ - this.cdb.watch_members_subclasses(Constants.Class.Dataset), - this.cdb.watch_members(Constants.Class.Dataset) + this.cdb.watch_members_subclasses( + Constants.Class.Dataset + ), + + this.cdb.watch_members( + Constants.Class.Dataset + ) ]), - rx.map(([subclassesMap, datasetMembers]) => subclassesMap.map( - subclass_uuids => subclass_uuids.filter(x => datasetMembers.has(x)) - )), + + rx.map(([subclassesMap, datasetMembers]) => + subclassesMap.map( + subclass_uuids => + subclass_uuids.filter( + x => datasetMembers.has(x) + ) + ) + ), + rxu.shareLatest() ); } - // Watch member members of functional group to see what functional type datasets have + /* + * ---------------------------------------------------- + * Functional types + * ---------------------------------------------------- + */ + _build_functional_types() { return rxu.rx( - this.cdb.watch_member_members(Constants.Group.FunctionalDatasetGroup), + this.cdb.watch_member_members( + Constants.Group.FunctionalDatasetGroup + ), + rx.map(data => { let result = IMap(); @@ -61,355 +216,486 @@ export class DataFlow { memberIds.forEach(memberId => { result = result.update( memberId, - list => (list ? list.push(groupId) : IList([groupId])) + list => + list + ? list.push(groupId) + : IList([groupId]) ); }); }); return result; }), + rxu.shareLatest() ); } - _build_apps_configs(app_klass){ - return rxu.rx( - this.cdb.watch_members(app_klass), - - rx.switchMap((membersSet) => { - const appUuids = membersSet.toArray(); + /* + * ----------------------------------------------------- + * Generic app config builder + * ---------------------------------------------------- + */ - if (appUuids.length === 0) { - return rx.of(IMap()); + _build_apps_configs(app_klass) { + return rxu.rx( + this.cdb.watch_members(app_klass), + + rx.switchMap((membersSet) => { + const appUuids = + membersSet.toArray(); + + if (appUuids.length === 0) { + return rx.of(IMap()); + } + + const dataStreams = + appUuids.map(appId => + this.cdb.search_app(appId).pipe( + rxu.shareLatest(), + + rx.map(config => [ + appId, + config + ]) + ) + ); + + return rx.combineLatest( + dataStreams + ).pipe( + rx.map(appEntries => { + let result = IMap(); + + for (const [appId, config] + of appEntries) { + + for (const [datasetId, payload] + of config) { + + const existing = + result.get( + datasetId, + IMap() + ); + + result = result.set( + datasetId, + existing.set( + appId, + payload + ) + ); + } } - const dataStreams = appUuids.map(appId => - this.cdb.search_app(appId).pipe( - rxu.shareLatest(), - rx.map(config => [appId, config]) - ) - ); - - return rx.combineLatest(dataStreams).pipe( - rx.map(appEntries => { - let result = IMap(); - - for (const [appId, config] of appEntries) { - for (const [datasetId, payload] of config) { - const existing = result.get(datasetId, IMap()); - - result = result.set(datasetId, existing.set(appId, payload)); - } - } - - return result; - }) - ); - }), - rxu.shareLatest() + return result; + }) ); + }), + + rxu.shareLatest() + ); } + /* + * ------------------------------------------------- + * Metadata + * --------------------------------------------------- + */ + _build_metadata() { - return this._build_apps_configs(Constants.App.DatasetMetadata); + return this._build_apps_configs( + Constants.App.DatasetMetadata + ); } - - - _build_general_info(){ + + /* + * -------------------------------------------------- + * General info + * ------------------------------------------------ + */ + + _build_general_info() { return rxu.rx( - this.cdb.search_app(UUIDs.App.Info), + this.cdb.search_app( + UUIDs.App.Info + ), + rxu.shareLatest() ); } - - /* - 1. search structure apps for all dataset configs - 2. invalid if exists in more than one structural app - 3. invalid if contains self-referencing source or invalid source - 4. if invalid throw away the definition and create a new one with the same dataset_uuid but structure = Constants.Special.InvalidDataset - */ + * ------------------------------------------------- + * Dataset builder + * ------------------------------------------------- + */ + _build_datasets() { return rxu.rx( - this._build_apps_configs(Constants.App.DatasetDefinition), + this._build_apps_configs( + Constants.App.DatasetDefinition + ), rx.map((groupedConfigs) => { - // ---------------------------- - // 1. Normalise duplicates - // ---------------------------- - // - // Convert: - // - // datasetId => IMap({ - // appId => payload - // }) - // - // into: - // - // datasetId => { - // structure, - // config - // } - // - // or INVALID if multiple definitions exist - // - - let map = groupedConfigs.map((definitions) => { - const entries = definitions.entrySeq().toArray(); - - if (entries.length !== 1) { + /* + * ------------------------------------------------------------ + * 1. Normalise duplicate definitions + * ------------------------------------------------------------ + * + * Convert: + * + * datasetId => IMap({ + * appId => payload + * }) + * + * into: + * + * datasetId => { + * structure, + * config + * } + * + * Invalid datasets are represented directly by: + * + * structure === Constants.Special.InvalidDataset + */ + + let map = groupedConfigs.map( + (definitions) => { + + const entries = + definitions + .entrySeq() + .toArray(); + + /* + * Multiple structural definitions + * => invalid dataset + */ + + if (entries.length !== 1) { + return { + structure: + Constants.Special.InvalidDataset, + + config: null, + from: null, + to: null + }; + } + + const [structure, config] = + entries[0]; + return { - structure: Constants.Special.InvalidDataset, - config: null + structure, + config }; } + ); - const [structure, config] = entries[0]; + /* + * ------------------------------------------------------------ + * 2. Recursive resolution + * ------------------------------------------------------------ + * + * resolve(datasetId) + * + * Returns: + * + * { from, to } + * + * or: + * + * null + * + * where null means invalid. + */ - return { - structure, - config - }; - }); + const cache = new Map(); - // ---------------------------- - // 2. Recursive resolution (cached) - // ---------------------------- + const resolve = + (datasetId, visited = new Set()) => { - const cache = new Map(); + /* + * Cached result + */ - const resolve = (datasetId, visited = new Set()) => { if (cache.has(datasetId)) return cache.get(datasetId); - if (visited.has(datasetId)) { - const result = { - validity: DatasetValidity.INVALID, - from: null, - to: null, - error: "Cycle detected" - }; + /* + * Cycle detection + */ - cache.set(datasetId, result); - return result; + if (visited.has(datasetId)) { + cache.set(datasetId, null); + return null; } visited.add(datasetId); - const def = map.get(datasetId); + const def = + map.get(datasetId); - if (!def || - def.structure === Constants.Special.InvalidDataset) { + /* + * Missing dataset + */ - const result = { - validity: DatasetValidity.INVALID, - from: null, - to: null - }; - - cache.set(datasetId, result); - return result; + if (!def) { + cache.set(datasetId, null); + return null; } - const { structure, config } = def; - - // ------------------------ - // SESSION - // ------------------------ + /* + * Already invalid + */ - if (structure === Constants.App.SessionLimits) { - const child = resolve(config.source, new Set(visited)); + if ( + def.structure === + Constants.Special.InvalidDataset + ) { + cache.set(datasetId, null); + return null; + } - if (child.validity === DatasetValidity.INVALID) { - cache.set(datasetId, child); - return child; + const { + structure, + config + } = def; + + /* + * -------------------------------------------------- + * SESSION LIMITS + * -------------------------------------------------- + */ + + if ( + structure === + Constants.App.SessionLimits + ) { + + const child = + resolve( + config.source, + new Set(visited) + ); + + /* + * Invalid child + */ + + if (!child) { + cache.set(datasetId, null); + return null; } - const from = config.from - ? new Date(config.from) - : null; + const from = + config.from + ? new Date(config.from) + : null; - const to = config.to - ? new Date(config.to) - : null; + const to = + config.to + ? new Date(config.to) + : null; const result = { - validity: DatasetValidity.VALID, - from: maxDate(from, child.from), - to: minDate(to, child.to) + from: + maxDate( + from, + child.from + ), + + to: + minDate( + to, + child.to + ) }; cache.set(datasetId, result); + return result; } - // ------------------------ - // SPARKPLUG - // ------------------------ + /* + * -------------------------------------------------- + * SPARKPLUG SOURCE + * -------------------------------------------------- + */ + + if ( + structure === + Constants.App.SparkplugSrc + ) { - if (structure === Constants.App.SparkplugSrc) { const result = { - validity: DatasetValidity.VALID, from: null, to: null }; cache.set(datasetId, result); + return result; } - // ------------------------ - // UNION - // ------------------------ - - if (structure === Constants.App.UnionComponents) { - const children = config.map(id => - resolve(id, new Set(visited)) - ); - - const validChildren = children.filter( - c => c.validity === DatasetValidity.VALID - ); - - if (validChildren.length !== children.length) { - const result = { - validity: DatasetValidity.INVALID, - from: null, - to: null - }; - - cache.set(datasetId, result); - return result; + /* + * -------------------------------------------------- + * UNION COMPONENTS + * -------------------------------------------------- + */ + + if ( + structure === + Constants.App.UnionComponents + ) { + + const children = + config.map(id => + resolve( + id, + new Set(visited) + ) + ); + + /* + * Any invalid child + */ + + if (children.some(c => !c)) { + cache.set(datasetId, null); + return null; } - const fromTimes = validChildren - .map(v => v.from?.getTime()) - .filter(t => !isNaN(t)); - - const toTimes = validChildren - .map(v => v.to?.getTime()) - .filter(t => !isNaN(t)); + const fromTimes = + children + .map(v => + v.from?.getTime() + ) + .filter(t => + !isNaN(t) + ); + + const toTimes = + children + .map(v => + v.to?.getTime() + ) + .filter(t => + !isNaN(t) + ); const result = { - validity: DatasetValidity.VALID, - from: fromTimes.length - ? new Date(Math.min(...fromTimes)) - : null, - to: toTimes.length - ? new Date(Math.max(...toTimes)) - : null + from: + fromTimes.length + ? new Date( + Math.min( + ...fromTimes + ) + ) + : null, + + to: + toTimes.length + ? new Date( + Math.max( + ...toTimes + ) + ) + : null }; cache.set(datasetId, result); + return result; } - // ------------------------ - // Fallback - // ------------------------ + /* + * -------------------------------------------------- + * Unknown structure + * -------------------------------------------------- + */ - const result = { - validity: DatasetValidity.INVALID, - from: null, - to: null - }; + cache.set(datasetId, null); - cache.set(datasetId, result); - return result; + return null; }; - // ---------------------------- - // 3. Resolve all datasets - // ---------------------------- + /* + * ------------------------------------------------------------ + * 3. Resolve all datasets + * ------------------------------------------------------------ + */ - return map.map((def, datasetId) => ({ - ...def, - ...resolve(datasetId) - })); - }), + return map.map( + (def, datasetId) => { - rxu.shareLatest() - ); - } + /* + * Already invalid due to duplicate defs + */ - async get_dataset_allowed_parts(dataset_uuid, principal, permission){ - const result = await rx.firstValueFrom( - rx.combineLatest([ - this.parts, - this.auth.watch_acl_with_perm(principal, permission) - ]).pipe( - rx.map(([partsMap, allowedParts]) => { - const parts = partsMap.get(dataset_uuid); + if ( + def.structure === + Constants.Special.InvalidDataset + ) { + return def; + } - if(!parts) return []; + const resolved = + resolve(datasetId); - return parts.filter(partId => allowedParts.has(partId)); - }) - ) - ); + /* + * Invalid dependency graph + */ - return result; - } + if (!resolved) { + return { + structure: + Constants.Special.InvalidDataset, - async get_allowed_dataset_uuids(principal, permission, dataset_validity) { - const datasets = this.get_dataset_definitions(dataset_validity); - - const allowed_datasets = await rx.firstValueFrom( - rx.combineLatest([ - datasets, - this.auth.watch_acl_with_perm(principal, permission) - ]).pipe( - rx.map(([datasets, targets]) => { - // datasets: IMap - // targets: Set or ISet of allowed datasetIds - - return datasets - .keySeq() // get all dataset IDs - .filter(id => targets.has(id)); - }) - ) - ); + config: null, + from: null, + to: null + }; + } - return allowed_datasets; - } + /* + * Valid dataset + */ - get_dataset_definitions(validity){ - if(validity == DatasetValidity.VALID){ - return this._get_valid_dataset_definitions(); - }else if (validity == DatasetValidity.INVALID){ - return this._get_invalid_dataset_definitions(); - }else if(validity == DatasetValidity.ALL){ - return this._get_all_dataset_definitions(); - }else{ - throw Error("Unhandled dataset validity type."); - } - } + return { + ...def, + ...resolved + }; + } + ); + }), - _get_all_dataset_definitions(){ - return this.datasets; + rxu.shareLatest() + ); } - _get_valid_dataset_definitions() { + _build_valid_datasets(){ return this.datasets.pipe( - rx.map((map) => - map.filter( - (definition) => - definition.structure !== Constants.Special.InvalidDataset - ) - ) + rx.map(map => map.filter( + def => def.structure !== Constants.Special.InvalidDataset + )), + rxu.shareLatest() ); } - _get_invalid_dataset_definitions() { + _build_invalid_datasets(){ return this.datasets.pipe( - rx.map((map) => - map.filter( - (definition) => - definition.structure === Constants.Special.InvalidDataset - ) - ) + rx.map(map => map.filter( + def => def.structure === Constants.Special.InvalidDataset + )), + rxu.shareLatest() ); } -} \ No newline at end of file +} + + diff --git a/acs-data-access/lib/validate.js b/acs-data-access/lib/validate.js index b3e3279e9..6e8b44194 100644 --- a/acs-data-access/lib/validate.js +++ b/acs-data-access/lib/validate.js @@ -33,11 +33,4 @@ export function valid_grant (grant) { if (!valid_uuid(grant.permission)) return false; if (!valid_uuid(grant.target)) return false; return (grant.plural === true || grant.plural === false); -} - - -export const DatasetValidity = Object.freeze({ - VALID: 'valid', - INVALID: 'invalid', - ALL: 'all' -}); \ No newline at end of file +} \ No newline at end of file From 9fa76f643768b37164ac675231f3580c37841c31 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Fri, 1 May 2026 13:30:56 +0100 Subject: [PATCH 65/96] rx-client lib added data-access interface - partially implemented --- lib/js-rx-client/lib/data-access.js | 36 +++++++++++++++++++++++++++++ lib/js-rx-client/lib/interfaces.js | 2 +- lib/js-rx-client/lib/rx-client.js | 1 + 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 lib/js-rx-client/lib/data-access.js diff --git a/lib/js-rx-client/lib/data-access.js b/lib/js-rx-client/lib/data-access.js new file mode 100644 index 000000000..652ccbf63 --- /dev/null +++ b/lib/js-rx-client/lib/data-access.js @@ -0,0 +1,36 @@ +/* + * Factory+ Rx interface + * Data Access notify + * Copyright 2026 University of Sheffield + */ + +import * as imm from "immutable"; +import * as rx from "rxjs"; + +import { Interfaces, urljoin } from "@amrc-factoryplus/service-client"; +import * as rxx from "@amrc-factoryplus/rx-util"; + +import { NotifyV2 } from "./notify-v2.js"; + +/** Extended DataAccess interface class. + * This supports all the methods from the base DataAccess service + * interface as well as methods to access the notify API. + */ +export class DataAccess extends Interfaces.DataAccess { + constructor(fplus){ + super(fplus); + + /** A NotifyV2 object for the DataAccess service. + * @type NotifyV2 */ + this.notify = new NotifyV2(this); + } + + /** + * Watch Dataset uuids with READ_DATASET permission + * @returns list of uuids + */ + watch_metadata_uuids(){ + return this.notify.watch(`v1/metadata/`); + } + +} \ No newline at end of file diff --git a/lib/js-rx-client/lib/interfaces.js b/lib/js-rx-client/lib/interfaces.js index 002856c00..c38c4dc80 100644 --- a/lib/js-rx-client/lib/interfaces.js +++ b/lib/js-rx-client/lib/interfaces.js @@ -9,4 +9,4 @@ export { Auth } from "./auth.js"; export { ConfigDB } from "./configdb.js"; - +export { DataAccess } from './data-access.js'; diff --git a/lib/js-rx-client/lib/rx-client.js b/lib/js-rx-client/lib/rx-client.js index 2282cf16d..34a189648 100644 --- a/lib/js-rx-client/lib/rx-client.js +++ b/lib/js-rx-client/lib/rx-client.js @@ -20,6 +20,7 @@ export class RxClient extends ServiceClient { RxClient.define_interfaces( ["ConfigDB", SI.ConfigDB, ``], ["Auth", SI.Auth, ``], + ["DataAccess", SI.DataAccess, ``], ); /** From 9618ae455de518e110d2448e57d860407eb99f68 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 13 May 2026 11:49:37 +0100 Subject: [PATCH 66/96] data access metadata uuid api fixed bug with info --- acs-data-access/lib/api-v1.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 5bad6953a..23affd831 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -115,7 +115,7 @@ export class APIv1 { const meta = { uuid: dataset_uuid, - name: info.name ? info.name : "UNKNOWN", + name: info?.name ? info.name : "UNKNOWN", from: dataset.from ? dataset.from : undefined, to: dataset.to ? dataset.to : undefined, function: all_f_types.get(dataset_uuid), From 7171dcd9c15662f4533a7e149f84d081254fb367 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Fri, 15 May 2026 09:15:29 +0100 Subject: [PATCH 67/96] data access notify service added implementations --- acs-data-access/lib/notify.js | 270 ++++++++++++++++++++++++++++++---- 1 file changed, 238 insertions(+), 32 deletions(-) diff --git a/acs-data-access/lib/notify.js b/acs-data-access/lib/notify.js index 36923f1ee..2ee747ccc 100644 --- a/acs-data-access/lib/notify.js +++ b/acs-data-access/lib/notify.js @@ -4,14 +4,21 @@ */ import * as rx from "rxjs"; +import { Map as IMap } from "immutable"; -import * as rxx from "@amrc-factoryplus/rx-util"; +import * as rxu from "@amrc-factoryplus/rx-util"; import { Notify } from "@amrc-factoryplus/service-api"; +import { DataAccess as Constants } from "./constants.js"; +import { valid_uuid } from "./validate.js"; +import { response } from "express"; + export class DataAccessNotify { constructor(opts) { this.data = opts.data; + this.auth = opts.auth; this.log = opts.debug.bound("notify"); + this.notify = this.build_notify(opts.api); } @@ -26,54 +33,253 @@ export class DataAccessNotify { log: this.log, }); - notify.watch("v1/metadata/", this.metadata_list.bind(this)); - notify.watch("v1/metadata/:uuid", this.metadata_uuid.bind(this)); - notify.search("v1/metadata/", this.metadata_search.bind(this)); - - notify.watch("v1/structure/", this.structure_list.bind(this)); - notify.watch("v1/structure/:uuid", this.structure_uuid.bind(this)); - notify.search("v1/structure/", this.structure_search.bind(this)); + notify.watch( + "v2/metadata/", + this.metadata_list.bind(this) + ); + + notify.watch( + "v2/metadata/:uuid", + this.metadata_uuid.bind(this) + ); + + notify.search( + "v2/metadata/", + this.metadata_search.bind(this) + ); + + notify.watch( + "v2/structure/", + this.structure_list.bind(this) + ); + + notify.watch( + "v2/structure/:uuid", + this.structure_uuid.bind(this) + ); + + notify.search( + "v2/structure/", + this.structure_search.bind(this) + ); return notify; } - metadata_list(sess){ - + /* + * ========================================================= + * METADATA + * ========================================================= + */ + + metadata_list(sess) { + this.log(`Notify metadata list is hit: ${sess.principal}`); + return this.data.allowed_valid_dataset_uuids( + sess.principal, + Constants.Perm.ReadDataset + ).pipe( + rx.map(data => ({ + status: 200, + response: {body: data} + })) + ); } - metadata_uuid(sess){ + metadata_uuid(sess, uuid) { + if (!valid_uuid(uuid)) return; - } + return rx.combineLatest([ + this.data.allowed_valid_datasets( + sess.principal, + Constants.Perm.ReadDataset + ), - metadata_search(sess){ + this.data.general_infos, - } + this.data.functional_types, - structure_list(sess){ + this.data.metadata, - } + this.data.allowed_dataset_parts( + uuid, + sess.principal, + Constants.Perm.ReadDataset + ), + ]).pipe( + rx.map(([ + datasets, + infos, + all_f_types, + all_metadata, + parts, + ]) => { + + const dataset = + datasets.get(uuid); + + /* + * Dataset missing or invalid + */ + + if (!dataset) + return null; + + const info = + infos.get(uuid); + + return { + uuid: uuid, + + name: + info?.name ?? + "UNKNOWN", - structure_uuid(sess){ + from: + dataset.from ?? + undefined, + + to: + dataset.to ?? + undefined, + + function: + all_f_types.get(uuid), + + metadata: + all_metadata.get(uuid), + + parts, + }; + }), + + rx.distinctUntilChanged( + (a, b) => + JSON.stringify(a) === + JSON.stringify(b) + ), + rxu.shareLatest(), + rx.map(data => ({ + status: 200, + response: {body: data} + })) + ); } - - structure_search(sess){ + /* + * Searchable metadata map + */ + + metadata_search(sess, filter) { + return rx.combineLatest([ + this.data.allowed_valid_datasets( + sess.principal, + Constants.Perm.ReadDataset + ), + + this.data.general_infos, + ]).pipe( + rx.map(([datasets, infos]) => { + + let result = IMap(); + + datasets.forEach( + (_, dataset_uuid) => { + + const info = + infos.get(dataset_uuid); + + result = result.set( + dataset_uuid, + { + uuid: dataset_uuid, + name: + info?.name ?? + "UNKNOWN", + } + ); + } + ); + + return result; + }), + + rxu.shareLatest() + ); } - /** - * Get names for all objects the principal can see. + /* + * ========================================================= + * STRUCTURE + * ========================================================= */ - name_list(sess) { - // We should combine this with the session permissions to see the objects - return rxx.rx( - this.data.names, - rx.map(entries => entries.keySeq().toArray()), - rx.mergeMap( - // You need to filter out what they can't see here using `sess` (not in a .filter :)) + + structure_list(sess) { + return this.data.allowed_all_dataset_uuids( + sess.principal, + Constants.Perm.EditDataset + ).pipe( + rx.map(data => ({ + status: 200, + response: {body: data} + })) + ); + } + + structure_uuid(sess, uuid) { + if (!valid_uuid(uuid)) return; + + return this.data.allowed_all_datasets( + sess.principal, + Constants.Perm.EditDataset + ).pipe( + rx.map(datasets => + datasets.get(uuid) + ), + + rx.distinctUntilChanged( + (a, b) => + JSON.stringify(a) === + JSON.stringify(b) ), - rx.map( - // You need to build your response objects here - )); + + rxu.shareLatest(), + + rx.map(data => ({ + status: 200, + response: {body: data} + })) + + ); + } + + structure_search(sess) { + return this.data.allowed_all_datasets( + sess.principal, + Constants.Perm.EditDataset + ).pipe( + rx.map(datasets => { + + let result = IMap(); + + datasets.forEach( + (dataset, dataset_uuid) => { + + result = result.set( + dataset_uuid, + { + uuid: dataset_uuid, + structure: + dataset.structure, + } + ); + } + ); + + return result; + }), + + rxu.shareLatest() + ); } -} +} \ No newline at end of file From 63a8af374dc6633d54ce534c2d4db066a3a32c87 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Fri, 15 May 2026 09:16:13 +0100 Subject: [PATCH 68/96] rx-client interface for data access added all methods --- lib/js-rx-client/lib/data-access.js | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/lib/js-rx-client/lib/data-access.js b/lib/js-rx-client/lib/data-access.js index 652ccbf63..fcadd573e 100644 --- a/lib/js-rx-client/lib/data-access.js +++ b/lib/js-rx-client/lib/data-access.js @@ -27,10 +27,29 @@ export class DataAccess extends Interfaces.DataAccess { /** * Watch Dataset uuids with READ_DATASET permission - * @returns list of uuids + * @returns */ - watch_metadata_uuids(){ - return this.notify.watch(`v1/metadata/`); + watch_metadata_list(){ + return this.notify.watch(`v2/metadata/`); } + watch_single_metadata(uuid){ + return this.notify.watch(`v2/metadata/${uuid}`); + } + + search_metadata(filter){ + return this.notify.search(`v2/metadata/`, filter); + } + + watch_structure_list(){ + return this.notify.watch('v2/structure/'); + } + + watch_single_structure(uuid){ + return this.notify.watch(`v2/structure/${uuid}`); + } + + search_structure(filter){ + return this.notify.search('v2/structure/', filter); + } } \ No newline at end of file From d14bc96522ca03292146f0916e4837a70cd6bbbb Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 20 May 2026 09:59:40 +0100 Subject: [PATCH 69/96] data access installed deep-equal --- acs-data-access/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/acs-data-access/package.json b/acs-data-access/package.json index 50a09340c..6924d3b60 100644 --- a/acs-data-access/package.json +++ b/acs-data-access/package.json @@ -16,6 +16,7 @@ "@amrc-factoryplus/service-client": "file:../lib/js-service-client", "@dotenvx/dotenvx": "^1.39.0", "@influxdata/influxdb-client": "^1.35.0", + "deep-equal": "^2.2.3", "express": "^4.21.2", "rxjs": "^7.8.2", "stream-size": "^0.0.6", From d21230ccaafd368a5bff63aaa6cf559483499734 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 20 May 2026 10:00:18 +0100 Subject: [PATCH 70/96] data access - fixed notify metadata SEARCH --- acs-data-access/lib/notify.js | 305 +++++++++++++++++++++++----------- 1 file changed, 212 insertions(+), 93 deletions(-) diff --git a/acs-data-access/lib/notify.js b/acs-data-access/lib/notify.js index 2ee747ccc..add08540f 100644 --- a/acs-data-access/lib/notify.js +++ b/acs-data-access/lib/notify.js @@ -3,6 +3,7 @@ * DataAccessNotify */ +import deep_equal from "deep-equal"; import * as rx from "rxjs"; import { Map as IMap } from "immutable"; @@ -66,102 +67,103 @@ export class DataAccessNotify { return notify; } + /* * ========================================================= - * METADATA + * METADATA * ========================================================= */ - + // WATCH metadata_list(sess) { - this.log(`Notify metadata list is hit: ${sess.principal}`); return this.data.allowed_valid_dataset_uuids( sess.principal, Constants.Perm.ReadDataset ).pipe( rx.map(data => ({ status: 200, - response: {body: data} + response: { + body: data + } })) ); } - metadata_uuid(sess, uuid) { + _metadata_resource_state(sess, uuid) { if (!valid_uuid(uuid)) return; - return rx.combineLatest([ + const datasets$ = this.data.allowed_valid_datasets( sess.principal, Constants.Perm.ReadDataset - ), - - this.data.general_infos, - - this.data.functional_types, - - this.data.metadata, - + ).pipe( + rx.startWith(IMap()) + ); + + const infos$ = + this.data.general_infos.pipe( + rx.startWith(IMap()) + ); + + const ftypes$ = + this.data.functional_types.pipe( + rx.startWith(IMap()) + ); + + const metadata$ = + this.data.metadata.pipe( + rx.startWith(IMap()) + ); + + const parts$ = this.data.allowed_dataset_parts( uuid, sess.principal, Constants.Perm.ReadDataset - ), + ).pipe( + rx.startWith(IMap()) + ); + + return rx.combineLatest([ + datasets$, + infos$, + ftypes$, + metadata$, + parts$, ]).pipe( - rx.map(([ - datasets, - infos, - all_f_types, - all_metadata, - parts, - ]) => { - const dataset = - datasets.get(uuid); + rx.map(([datasets, infos, ftypes, metadata, parts]) => { - /* - * Dataset missing or invalid - */ + const dataset = datasets.get(uuid); - if (!dataset) - return null; + if (!dataset) return; - const info = - infos.get(uuid); + const info = infos.get(uuid); return { - uuid: uuid, - - name: - info?.name ?? - "UNKNOWN", - - from: - dataset.from ?? - undefined, - - to: - dataset.to ?? - undefined, - - function: - all_f_types.get(uuid), - - metadata: - all_metadata.get(uuid), - - parts, - }; + uuid, + name: info?.name ?? "UNKNOWN", + from: dataset.from ?? undefined, + to: dataset.to ?? undefined, + function: ftypes?.get(uuid), + metadata: metadata?.get(uuid), + parts: parts ?? undefined, + } }), - rx.distinctUntilChanged( - (a, b) => - JSON.stringify(a) === - JSON.stringify(b) - ), + rx.distinctUntilChanged(deep_equal), + rxu.shareLatest() + ); + } - rxu.shareLatest(), + // WATCH + metadata_uuid(sess, uuid) { + return this._metadata_resource_state(sess, uuid) + .pipe( rx.map(data => ({ - status: 200, - response: {body: data} + status: 200, + response: { + body: data + } })) ); } @@ -170,43 +172,160 @@ export class DataAccessNotify { * Searchable metadata map */ - metadata_search(sess, filter) { - return rx.combineLatest([ - this.data.allowed_valid_datasets( - sess.principal, - Constants.Perm.ReadDataset - ), - this.data.general_infos, - ]).pipe( - rx.map(([datasets, infos]) => { - let result = IMap(); + _build_search_source(uuids) { + const shared = uuids.pipe( + rxu.shareLatest() + ); + + return { + full: async () => { + const state = await rx.firstValueFrom( + shared.pipe( + rx.take(1) + ) + ); + + const children = Object.fromEntries( + state.entrySeq().map(([uuid, response]) => [ + uuid, + { + status: response.status, + body: response.body, + ...(response.headers + ? { headers: response.headers } + : {}), + } + ]) + ); + + return { + children, + response: { status: 204 }, + }; + }, + + updates: shared.pipe( + rx.startWith(IMap()), + rx.pairwise(), + + rx.mergeMap(([prev, curr]) => { + const updates = []; + + /** + * Created / Updated + */ + curr.forEach((response, key) => { + const old = prev.get(key); + + const plain = { + status: response.status, + body: response.body, + ...(response.headers + ? { headers: response.headers } + : {}), + }; + + /** + * Newly visible + */ + if (!old) { + updates.push({ + status: 200, + child: key, + response: { + ...plain, + status: plain.status === 200 + ? 201 + : plain.status, + }, + }); + + return; + } + + /** + * Changed + */ + if (!deep_equal(old, response)) { + updates.push({ + status: 200, + child: key, + response: plain, + }); + } + }); + + /** + * Removed + */ + prev.forEach((_, key) => { + if (!curr.has(key)) { + updates.push({ + status: 200, + child: key, + response: { + status: 404, + }, + }); + } + }); + + return rx.from(updates); + }) + ), + + /** + * Must be an operator function. + */ + acl: rx.pipe(), + }; + } + + metadata_search(sess) { + const dataset_uuids = this.data + .allowed_valid_dataset_uuids( + sess.principal, + Constants.Perm.ReadDataset + ) + .pipe( + rx.switchMap(uuids => { + const list = uuids.toArray?.() ?? []; + + if (list.length === 0) + return rx.of(IMap()); + + return rx.combineLatest( + list.map(uuid => + this._metadata_resource_state(sess, uuid).pipe( + rx.filter(x => x && typeof x === "object"), + + /** + * Convert to plain serialisable object. + */ + rx.map(res => [uuid, { + status: 200, + body: res + }]) + ) + ) + ).pipe( + rx.map(entries => IMap(entries)) + ); + }), + + rxu.shareLatest() + ); + + return this._build_search_source(dataset_uuids); + } + - datasets.forEach( - (_, dataset_uuid) => { - const info = - infos.get(dataset_uuid); - result = result.set( - dataset_uuid, - { - uuid: dataset_uuid, - name: - info?.name ?? - "UNKNOWN", - } - ); - } - ); - return result; - }), - rxu.shareLatest() - ); - } /* * ========================================================= @@ -221,7 +340,7 @@ export class DataAccessNotify { ).pipe( rx.map(data => ({ status: 200, - response: {body: data} + body: data })) ); } @@ -247,7 +366,7 @@ export class DataAccessNotify { rx.map(data => ({ status: 200, - response: {body: data} + body: data })) ); From 817c74cc06fdea34c1cb61e5c63dbef579ee34cb Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 20 May 2026 11:08:49 +0100 Subject: [PATCH 71/96] data access structure uuid - removed from and to fields from returning obj --- acs-data-access/lib/api-v1.js | 3 ++- acs-data-access/lib/notify.js | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 23affd831..74e5d8af7 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -381,7 +381,8 @@ export class APIv1 { ); if (!dataset) return fail(this.log, 404, `Dataset not found for ${dataset_uuid}.`); - return res.status(200).json(dataset); + const {from, to, ...def} = dataset; + return res.status(200).json(def); } diff --git a/acs-data-access/lib/notify.js b/acs-data-access/lib/notify.js index add08540f..e2a7b0a3a 100644 --- a/acs-data-access/lib/notify.js +++ b/acs-data-access/lib/notify.js @@ -340,7 +340,9 @@ export class DataAccessNotify { ).pipe( rx.map(data => ({ status: 200, - body: data + response: { + body: data + } })) ); } @@ -356,19 +358,17 @@ export class DataAccessNotify { datasets.get(uuid) ), - rx.distinctUntilChanged( - (a, b) => - JSON.stringify(a) === - JSON.stringify(b) - ), + rx.distinctUntilChanged(deep_equal), rxu.shareLatest(), - rx.map(data => ({ - status: 200, - body: data - })) - + rx.map(dataset_def => { + const {from, to, ...body} = dataset_def; + return { + status: 200, + response: { body } + } + }), ); } From c76c6ae8860955bdcda2ccb0dac42c1be4878372 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 20 May 2026 11:09:17 +0100 Subject: [PATCH 72/96] data access package-lock --- acs-data-access/package-lock.json | 3022 ++++++++++++++++++++++++++++- 1 file changed, 2915 insertions(+), 107 deletions(-) diff --git a/acs-data-access/package-lock.json b/acs-data-access/package-lock.json index 14e066fbc..0cd572feb 100644 --- a/acs-data-access/package-lock.json +++ b/acs-data-access/package-lock.json @@ -14,6 +14,8 @@ "@amrc-factoryplus/service-api": "file:../lib/js-service-api", "@amrc-factoryplus/service-client": "file:../lib/js-service-client", "@dotenvx/dotenvx": "^1.39.0", + "@influxdata/influxdb-client": "^1.35.0", + "deep-equal": "^2.2.3", "express": "^4.21.2", "rxjs": "^7.8.2", "stream-size": "^0.0.6", @@ -23,9 +25,48 @@ "eslint": "^9.23.0" } }, - "../lib/js-rx-client": { - "name": "@amrc-factoryplus/rx-client", + "../lib/js-rx-util": { + "name": "@amrc-factoryplus/rx-util", + "version": "0.0.3", + "extraneous": true, + "license": "ISC", + "dependencies": { + "immutable": "^5.0.0-beta.5", + "rxjs": "^7.8.1" + }, + "devDependencies": { + "@eslint/js": "^9.18.0", + "eslint": "^9.18.0", + "globals": "^15.14.0" + } + }, + "../lib/js-service-client": { + "name": "@amrc-factoryplus/service-client", + "version": "1.6.0", + "extraneous": true, + "license": "ISC", + "dependencies": { + "content-type": "^1.0.5", + "isomorphic-ws": "^5.0.0", + "mqtt": "^5.3.6", + "optional-js": "^2.3.0", + "semver": "^7.6.0", + "sparkplug-payload": "^1.0.3" + }, + "devDependencies": { + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "^9.13.0", + "eslint": "^9.13.0", + "globals": "^15.11.0" + }, + "optionalDependencies": { + "got-fetch": "^5.1.8", + "gssapi.js": "^2.0.1" + } + }, + "node_modules/@amrc-factoryplus/rx-client": { "version": "v0.0.1-bmz.1", + "resolved": "file:../lib/js-rx-client", "license": "ISC", "dependencies": { "@amrc-factoryplus/rx-util": "file:../js-rx-util", @@ -33,32 +74,20 @@ "immutable": "^5.0.0-rc.2", "rxjs": "^7.8.1", "uuid": "^11.0.2" - }, - "devDependencies": { - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "^9.14.0", - "eslint": "^9.14.0", - "globals": "^15.12.0", - "jsdoc": "^4.0.4" } }, - "../lib/js-rx-util": { - "name": "@amrc-factoryplus/rx-util", + "node_modules/@amrc-factoryplus/rx-util": { "version": "0.0.3", + "resolved": "file:../lib/js-rx-util", "license": "ISC", "dependencies": { "immutable": "^5.0.0-beta.5", "rxjs": "^7.8.1" - }, - "devDependencies": { - "@eslint/js": "^9.18.0", - "eslint": "^9.18.0", - "globals": "^15.14.0" } }, - "../lib/js-service-api": { - "name": "@amrc-factoryplus/service-api", + "node_modules/@amrc-factoryplus/service-api": { "version": "0.1.0", + "resolved": "file:../lib/js-service-api", "license": "ISC", "os": [ "!win32" @@ -72,22 +101,317 @@ "express": "^5.0.1", "gssapi.js": "^2.0.1", "immutable": "^5.1.3", + "jose": "^5.9.6", "optional-js": "^2.3.0", "path-to-regexp": "^6.2.1", "rxjs": "^7.8.1", "uuid": "^11.0.2", "ws": "^8.18.0" + } + }, + "node_modules/@amrc-factoryplus/service-api/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" }, - "devDependencies": { - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "^9.13.0", - "eslint": "^9.13.0", - "globals": "^15.11.0" + "engines": { + "node": ">= 0.6" } }, - "../lib/js-service-client": { - "name": "@amrc-factoryplus/service-client", + "node_modules/@amrc-factoryplus/service-api/node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@amrc-factoryplus/service-api/node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@amrc-factoryplus/service-api/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/@amrc-factoryplus/service-api/node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@amrc-factoryplus/service-api/node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@amrc-factoryplus/service-api/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@amrc-factoryplus/service-api/node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@amrc-factoryplus/service-api/node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@amrc-factoryplus/service-api/node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@amrc-factoryplus/service-api/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@amrc-factoryplus/service-api/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@amrc-factoryplus/service-api/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@amrc-factoryplus/service-api/node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "license": "MIT" + }, + "node_modules/@amrc-factoryplus/service-api/node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/@amrc-factoryplus/service-api/node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@amrc-factoryplus/service-api/node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@amrc-factoryplus/service-api/node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "license": "MIT", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@amrc-factoryplus/service-api/node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@amrc-factoryplus/service-client": { "version": "1.6.0", + "resolved": "file:../lib/js-service-client", "license": "ISC", "dependencies": { "content-type": "^1.0.5", @@ -97,32 +421,19 @@ "semver": "^7.6.0", "sparkplug-payload": "^1.0.3" }, - "devDependencies": { - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "^9.13.0", - "eslint": "^9.13.0", - "globals": "^15.11.0" - }, "optionalDependencies": { "got-fetch": "^5.1.8", "gssapi.js": "^2.0.1" } }, - "node_modules/@amrc-factoryplus/rx-client": { - "resolved": "../lib/js-rx-client", - "link": true - }, - "node_modules/@amrc-factoryplus/rx-util": { - "resolved": "../lib/js-rx-util", - "link": true - }, - "node_modules/@amrc-factoryplus/service-api": { - "resolved": "../lib/js-service-api", - "link": true - }, - "node_modules/@amrc-factoryplus/service-client": { - "resolved": "../lib/js-service-client", - "link": true + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } }, "node_modules/@dotenvx/dotenvx": { "version": "1.57.0", @@ -357,6 +668,12 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@influxdata/influxdb-client": { + "version": "1.35.0", + "resolved": "https://registry.npmjs.org/@influxdata/influxdb-client/-/influxdb-client-1.35.0.tgz", + "integrity": "sha512-woWMi8PDpPQpvTsRaUw4Ig+nOGS/CWwAwS66Fa1Vr/EkW+NEwxI8YfPBsdBMn33jK2Y86/qMiiuX/ROHIkJLTw==", + "license": "MIT" + }, "node_modules/@noble/ciphers": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", @@ -396,22 +713,166 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" }, - "node_modules/accepts": { - "version": "1.3.8", + "node_modules/@protobufjs/codegen": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.2.tgz", + "integrity": "sha512-pa0vFRuws4wkvaXKK1uXZMAwAX4/t8ANaJo45iw/oQHNQ9q5xUzwgFmVJGXiga2BeN+zpX7Vf9vmsiIa2J+MUw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", + "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", + "license": "BSD-3-Clause" + }, + "node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", + "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "node_modules/@types/readable-stream": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.23.tgz", + "integrity": "sha512-wwXrtQvbMHxCbBgjHaMGEmImFTQxxpfMOR/ZoQnXxB1woqkUbdLGFDgauo00Py9IudiaqSeiBiulSV9i6XIPig==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "license": "MIT", @@ -463,6 +924,21 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz", + "integrity": "sha512-iFY7JCgHbepc0b82yLaw4IMortylNb6wG4kL+4R0C3iv6i+RHGHux/yUX5BTiRvSX/shMnngjR1YyNMnXEFh5A==", + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -479,6 +955,53 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/are-we-there-yet": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.0.6.tgz", + "integrity": "sha512-Zfw6bteqM9gQXZ1BIWOgM8xEwMrUGoyL8nW13+O+OOgNX3YhuDN1GDgg1NzdTlmm3j+9sHy7uBZ12r+z9lXnZQ==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.0 || ^1.1.13" + } + }, + "node_modules/are-we-there-yet/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/are-we-there-yet/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/are-we-there-yet/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -486,17 +1009,150 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "license": "MIT", + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.1.6.tgz", + "integrity": "sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg==", + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^4.2.0" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/bl/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "license": "MIT" }, "node_modules/body-parser": { @@ -542,13 +1198,77 @@ "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, + "node_modules/broker-factory": { + "version": "3.1.14", + "resolved": "https://registry.npmjs.org/broker-factory/-/broker-factory-3.1.14.tgz", + "integrity": "sha512-L45k5HMbPIrMid0nTOZ/UPXG/c0aRuQKVrSDFIb1zOkvfiyHgYmIjc3cSiN1KwQIvRDOtKE0tfb3I9EZ3CmpQQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "fast-unique-numbers": "^9.0.27", + "tslib": "^2.8.1", + "worker-factory": "^7.0.49" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffer-shims": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha512-Zy8ZXMyxIT6RMTeY7OP/bDndfj6bwCan7SS98CEndS6deHwWPpseeHlwarNcBim+etXnF9HBc1non5JgDaJU1g==", + "license": "MIT" + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "engines": { + "node": ">=0.2.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -558,6 +1278,55 @@ "node": ">= 0.8" } }, + "node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -597,6 +1366,27 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "license": "MIT/X11", + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -614,14 +1404,97 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==", + "license": "ISC", "dependencies": { - "color-name": "~1.1.4" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/cmake-js": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-6.3.2.tgz", + "integrity": "sha512-7MfiQ/ijzeE2kO+WFB9bv4QP5Dn2yVaAP2acFJr4NIFy2hT4w6O4EpOTLNcohR5IPX7M4wNf/5taIqMj7UA9ug==", + "license": "MIT", + "dependencies": { + "axios": "^0.21.1", + "bluebird": "^3", + "debug": "^4", + "fs-extra": "^5.0.0", + "is-iojs": "^1.0.1", + "lodash": "^4", + "memory-stream": "0", + "npmlog": "^1.2.0", + "rc": "^1.2.7", + "semver": "^5.0.3", + "splitargs": "0", + "tar": "^4", + "unzipper": "^0.8.13", + "url-join": "0", + "which": "^1.0.9", + "yargs": "^3.6.0" + }, + "bin": { + "cmake-js": "bin/cmake-js" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/cmake-js/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/cmake-js/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/cmake-js/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" }, "engines": { "node": ">=7.0.0" @@ -643,13 +1516,56 @@ "node": ">=16" } }, + "node_modules/commist": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", + "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==", + "license": "MIT" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, "license": "MIT" }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -686,6 +1602,29 @@ "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", "license": "MIT" }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -725,7 +1664,6 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -739,6 +1677,87 @@ } } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-equal": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -746,6 +1765,57 @@ "dev": true, "license": "MIT" }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT" + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -791,6 +1861,51 @@ "node": ">= 0.4" } }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/duplexer2/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexer2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/eciesjs": { "version": "0.4.18", "resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.18.tgz", @@ -841,6 +1956,26 @@ "node": ">= 0.4" } }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -1035,6 +2170,24 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -1140,6 +2293,19 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-unique-numbers": { + "version": "9.0.27", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-9.0.27.tgz", + "integrity": "sha512-nDA9ADeINN8SA2u2wCtU+siWFTTDqQR37XvgPIDDmboWQeExz7X0mImxuaN+kJddliIqy2FpVRmnvRZ+j8i1/A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18.2.0" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -1170,6 +2336,12 @@ "node": ">=16.0.0" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, "node_modules/finalhandler": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", @@ -1241,6 +2413,52 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 14.17" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -1259,6 +2477,48 @@ "node": ">= 0.6" } }, + "node_modules/fs-extra": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", + "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "node_modules/fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "license": "ISC", + "dependencies": { + "minipass": "^2.6.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -1268,6 +2528,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz", + "integrity": "sha512-fVbU2wRE91yDvKUnrIaQlHKAWKY5e08PmztCrwuH5YVQ+Z/p3d0ny2T48o6uvAAXHIUnfaQdHkmxYbQft1eHVA==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "ansi": "^0.3.0", + "has-unicode": "^2.0.0", + "lodash.pad": "^4.1.0", + "lodash.padend": "^4.1.0", + "lodash.padstart": "^4.1.0" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -1317,6 +2600,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -1355,6 +2659,77 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/got": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/got-fetch": { + "version": "5.1.10", + "resolved": "https://registry.npmjs.org/got-fetch/-/got-fetch-5.1.10.tgz", + "integrity": "sha512-Gwj/A2htjvLEcY07PKDItv0WCPEs3dV2vWeZ+9TVBSKSTuWEZ4oXaMD0ZAOsajwx2orahQWN4HI0MfRyWSZsbg==", + "deprecated": "please use built-in fetch in nodejs", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "got": "^12.0.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/gssapi.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/gssapi.js/-/gssapi.js-2.0.1.tgz", + "integrity": "sha512-5D+qylV8uIKMaxTTKhpzvfGRz5+okr/rpwC6YlaCoDVBpvkuNgL8pM7+r7aTzZ2oT5dA4w2WoZTV82opGj+RZQ==", + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "bindings": "^1.5.0", + "cmake-js": "^6.1.0", + "node-addon-api": "^1.7.2" + } + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1365,6 +2740,18 @@ "node": ">=8" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -1377,6 +2764,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC" + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -1389,6 +2797,20 @@ "node": ">= 0.4" } }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", + "license": "MIT" + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause", + "optional": true, + "peer": true + }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -1409,6 +2831,21 @@ "url": "https://opencollective.com/express" } }, + "node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -1430,6 +2867,26 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -1439,6 +2896,12 @@ "node": ">= 4" } }, + "node_modules/immutable": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", + "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -1466,12 +2929,61 @@ "node": ">=0.8.19" } }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ip-address": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -1481,6 +2993,98 @@ "node": ">= 0.10" } }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1491,6 +3095,18 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "license": "MIT", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1504,6 +3120,91 @@ "node": ">=0.10.0" } }, + "node_modules/is-iojs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-iojs/-/is-iojs-1.1.0.tgz", + "integrity": "sha512-tLn1j3wYSL6DkvEI+V/j0pKohpa5jk+ER74v6S4SgCXnjS0WA+DoZbwZBrrhgwksMvtuwndyGeG5F8YMsoBzSA==", + "license": "MIT" + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -1516,6 +3217,73 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, "node_modules/isexe": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", @@ -1525,6 +3293,34 @@ "node": ">=18" } }, + "node_modules/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/jose": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.10.0.tgz", + "integrity": "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, "node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", @@ -1542,7 +3338,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/json-schema-traverse": { @@ -1559,16 +3355,37 @@ "dev": true, "license": "MIT" }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } }, + "node_modules/lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", + "license": "MIT", + "dependencies": { + "invert-kv": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -1583,6 +3400,12 @@ "node": ">= 0.8.0" } }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "license": "ISC" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -1593,18 +3416,68 @@ "p-locate": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.pad": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz", + "integrity": "sha512-mvUHifnLqM+03YNzeTBS1/Gr6JRFjd3rRx88FHWUvamVaT9k2O/kXha3yBSOwB9/DTQrSTLJNHvLBBt2FdX7Mg==", + "license": "MIT" + }, + "node_modules/lodash.padend": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", + "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==", + "license": "MIT" + }, + "node_modules/lodash.padstart": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", + "integrity": "sha512-sW73O6S8+Tg66eY56DBk85aQzzUJDtpoXFBgELMd5P/SotAguo+1kYO6RuYgXxA4HJH3LFTFPASX6ET6bjfriw==", + "license": "MIT" + }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "license": "Apache-2.0" + }, + "node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" }, "node_modules/math-intrinsics": { "version": "1.1.0", @@ -1624,6 +3497,15 @@ "node": ">= 0.6" } }, + "node_modules/memory-stream": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/memory-stream/-/memory-stream-0.0.3.tgz", + "integrity": "sha512-q0D3m846qY6ZkIt+19ZemU5vH56lpOZZwoJc3AICARKh/menBuayQUjAGPrqtHQQMUYERSdOrej92J9kz7LgYA==", + "license": "MMIT", + "dependencies": { + "readable-stream": "~1.0.26-2" + } + }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -1690,11 +3572,24 @@ "node": ">=6" } }, + "node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -1703,6 +3598,114 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "license": "ISC", + "dependencies": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "node_modules/minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "license": "MIT", + "dependencies": { + "minipass": "^2.9.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mqtt": { + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.15.1.tgz", + "integrity": "sha512-V1WnkGuJh3ec9QXzy5Iylw8OOBK+Xu1WhxcQ9mMpLThG+/JZIMV1PgLNRgIiqXhZnvnVLsuyxHl5A/3bHHbcAA==", + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.21", + "@types/ws": "^8.18.1", + "commist": "^3.2.0", + "concat-stream": "^2.0.0", + "debug": "^4.4.1", + "help-me": "^5.0.0", + "lru-cache": "^10.4.3", + "minimist": "^1.2.8", + "mqtt-packet": "^9.0.2", + "number-allocator": "^1.0.14", + "readable-stream": "^4.7.0", + "rfdc": "^1.4.1", + "socks": "^2.8.6", + "split2": "^4.2.0", + "worker-timers": "^8.0.23", + "ws": "^8.18.3" + }, + "bin": { + "mqtt": "build/bin/mqtt.js", + "mqtt_pub": "build/bin/pub.js", + "mqtt_sub": "build/bin/sub.js" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/mqtt-packet": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.2.tgz", + "integrity": "sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA==", + "license": "MIT", + "dependencies": { + "bl": "^6.0.8", + "debug": "^4.3.4", + "process-nextick-args": "^2.0.1" + } + }, + "node_modules/mqtt/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/mqtt/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -1725,6 +3728,26 @@ "node": ">= 0.6" } }, + "node_modules/node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "license": "MIT" + }, + "node_modules/normalize-url": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.1.1.tgz", + "integrity": "sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -1737,6 +3760,46 @@ "node": ">=8" } }, + "node_modules/npmlog": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-1.2.1.tgz", + "integrity": "sha512-1J5KqSRvESP6XbjPaXt2H6qDzgizLTM7x0y1cXIjP2PpvdCqyNC7TO3cPRKsuYlElbi/DwkzRRdG2zpmE0IktQ==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "ansi": "~0.3.0", + "are-we-there-yet": "~1.0.0", + "gauge": "~1.2.0" + } + }, + "node_modules/number-allocator": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", + "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.1", + "js-sdsl": "4.3.0" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -1749,6 +3812,31 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/object-treeify": { "version": "1.1.33", "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-1.1.33.tgz", @@ -1758,6 +3846,26 @@ "node": ">= 10" } }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -1770,6 +3878,15 @@ "node": ">= 0.8" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", @@ -1785,6 +3902,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/optional-js": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/optional-js/-/optional-js-2.3.0.tgz", + "integrity": "sha512-B0LLi+Vg+eko++0z/b8zIv57kp7HKEzaPJo7LowJXMUKYdf+3XJGu/cw03h/JhIOsLnP+cG5QnTHAuicjA5fMw==", + "license": "MIT" + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -1803,6 +3926,29 @@ "node": ">= 0.8.0" } }, + "node_modules/os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==", + "license": "MIT", + "dependencies": { + "lcid": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=12.20" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -1867,6 +4013,15 @@ "node": ">=8" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -1894,6 +4049,15 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -1904,6 +4068,47 @@ "node": ">= 0.8.0" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/protobufjs": { + "version": "6.11.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.6.tgz", + "integrity": "sha512-k8BHqgPBOtrlougZZqF2uUk5Z7bN8f0wj+3e8M3hvtSv0NBAz4VBy5f6R5Nxq/l+i7mRFTgNZb2trxqTpHNY/A==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": ">=13.7.0", + "long": "^4.0.0" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -1933,47 +4138,193 @@ "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.1.0" + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "license": "MIT" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "lowercase-keys": "^3.0.0" }, "engines": { - "node": ">=0.6" + "node": ">=14.16" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" } }, - "node_modules/raw-body": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "license": "MIT", "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "unpipe": "~1.0.0" + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 18" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, + "node_modules/router/node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", "license": "MIT", - "engines": { - "node": ">=4" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/rxjs": { @@ -2005,12 +4356,41 @@ ], "license": "MIT" }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/send": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", @@ -2065,6 +4445,44 @@ "node": ">= 0.8.0" } }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -2170,6 +4588,56 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz", + "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.1.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/sparkplug-payload": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sparkplug-payload/-/sparkplug-payload-1.0.3.tgz", + "integrity": "sha512-JAQSyuHVQQe/LzIlJdcIaD1F1c+rbXoolII3H1whQkhuZ96+G53RoPWqP9zCPZYFjvfSMHnQNIedGIZw/zjHJw==", + "license": "EPL-2.0", + "dependencies": { + "@types/long": "^4.0.0", + "long": "^4.0.0", + "protobufjs": "^6.11.3" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/splitargs": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/splitargs/-/splitargs-0.0.7.tgz", + "integrity": "sha512-UUFYD2oWbNwULH6WoVtLUOw8ch586B+HUqcsAjjjeoBQAM1bD4wZRXu01koaxyd8UeYpybWqW4h+lO1Okv40Tg==", + "license": "ISC" + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -2179,12 +4647,57 @@ "node": ">= 0.8" } }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/stream-size": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/stream-size/-/stream-size-0.0.6.tgz", "integrity": "sha512-TZsxxZzKPkiD7fxcKx0Ze9s0+WnGGbX1yWZNoXXmN6YjyEu/62pYkLKo7P627N27BT9kI93ZfZAx38brr/hq+Q==", "license": "MIT" }, + "node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "license": "MIT" + }, + "node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "license": "MIT", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -2220,6 +4733,25 @@ "node": ">=8" } }, + "node_modules/tar": { + "version": "4.4.19", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", + "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", + "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "chownr": "^1.1.4", + "fs-minipass": "^1.2.7", + "minipass": "^2.9.0", + "minizlib": "^1.3.3", + "mkdirp": "^0.5.5", + "safe-buffer": "^5.2.1", + "yallist": "^3.1.1" + }, + "engines": { + "node": ">=4.5" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -2229,6 +4761,15 @@ "node": ">=0.6" } }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "license": "MIT/X11", + "engines": { + "node": "*" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -2261,6 +4802,27 @@ "node": ">= 0.6" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "license": "MIT" + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -2270,6 +4832,56 @@ "node": ">= 0.8" } }, + "node_modules/unzipper": { + "version": "0.8.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.8.14.tgz", + "integrity": "sha512-8rFtE7EP5ssOwGpN2dt1Q4njl0N1hUXJ7sSPz0leU2hRdq6+pra57z4YPBlVqm40vcgv6ooKZEAx48fMTv9x4w==", + "license": "MIT", + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "~1.0.10", + "listenercount": "~1.0.1", + "readable-stream": "~2.1.5", + "setimmediate": "~1.0.4" + } + }, + "node_modules/unzipper/node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "license": "MIT" + }, + "node_modules/unzipper/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/unzipper/node_modules/process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha512-yN0WQmuCX63LP/TMvAg31nvT6m4vDqJEiiv2CAZqWOGNWutc9DfDk1NPYYmKUFmaVM2UwDowH4u5AHWYP/jxKw==", + "license": "MIT" + }, + "node_modules/unzipper/node_modules/readable-stream": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz", + "integrity": "sha512-NkXT2AER7VKXeXtJNSaWLpWIhmtSE3K2PguaLEeWr4JILghcIKqoLt1A3wHrnpDC5+ekf8gfk1GKWkFXe4odMw==", + "license": "MIT", + "dependencies": { + "buffer-shims": "^1.0.0", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -2280,6 +4892,18 @@ "punycode": "^2.1.0" } }, + "node_modules/url-join": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-0.0.1.tgz", + "integrity": "sha512-H6dnQ/yPAAVzMQRvEvyz01hhfQL5qRWSEt7BX8t9DqnPw9BjMb64fjIRq76Uvf1hkHp+mTZvEVJ5guXOT0Xqaw==", + "license": "MIT" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -2326,6 +4950,76 @@ "node": "^16.13.0 || >=18.0.0" } }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/window-size": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", + "integrity": "sha512-2thx4pB0cV3h+Bw7QmMXcEbdmOzv9t0HFplJH/Lz6yu60hXYy5RT8rUu+wlIreVxWsGN20mo+MHeCSfUpQBwPw==", + "license": "MIT", + "bin": { + "window-size": "cli.js" + }, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -2336,6 +5030,120 @@ "node": ">=0.10.0" } }, + "node_modules/worker-factory": { + "version": "7.0.49", + "resolved": "https://registry.npmjs.org/worker-factory/-/worker-factory-7.0.49.tgz", + "integrity": "sha512-lW7tpgy6aUv2dFsQhv1yv+XFzdkCf/leoKRTGMPVK5/die6RrUjqgJHJf556qO+ZfytNG6wPXc17E8zzsOLUDw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "fast-unique-numbers": "^9.0.27", + "tslib": "^2.8.1" + } + }, + "node_modules/worker-timers": { + "version": "8.0.31", + "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-8.0.31.tgz", + "integrity": "sha512-ngkq5S6JuZyztom8tDgBzorLo9byhBMko/sXfgiUD945AuzKGg1GCgDMCC3NaYkicLpGKXutONM36wEX8UbBCA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "tslib": "^2.8.1", + "worker-timers-broker": "^8.0.16", + "worker-timers-worker": "^9.0.14" + } + }, + "node_modules/worker-timers-broker": { + "version": "8.0.16", + "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-8.0.16.tgz", + "integrity": "sha512-JyP3AvUGyPGbBGW7XiUewm2+0pN/aYo1QpVf5kdXAfkDZcN3p7NbWrG6XnyDEpDIvfHk/+LCnOW/NsuiU9riYA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "broker-factory": "^3.1.14", + "fast-unique-numbers": "^9.0.27", + "tslib": "^2.8.1", + "worker-timers-worker": "^9.0.14" + } + }, + "node_modules/worker-timers-worker": { + "version": "9.0.14", + "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-9.0.14.tgz", + "integrity": "sha512-/qF06C60sXmSLfUl7WglvrDIbspmPOM8UrG63Dnn4bi2x4/DfqHS/+dxF5B+MdHnYO5tVuZYLHdAodrKdabTIg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "tslib": "^2.8.1", + "worker-factory": "^7.0.49" + } + }, + "node_modules/wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", + "license": "MIT", + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz", + "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "license": "ISC" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + }, + "node_modules/yargs": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", + "integrity": "sha512-ONJZiimStfZzhKamYvR/xvmgW3uEkAUFSP91y2caTEPhzF6uP2JfPiVZcq66b/YR0C3uitxSV7+T1x8p5bkmMg==", + "license": "MIT", + "dependencies": { + "camelcase": "^2.0.1", + "cliui": "^3.0.3", + "decamelize": "^1.1.1", + "os-locale": "^1.4.0", + "string-width": "^1.0.1", + "window-size": "^0.1.4", + "y18n": "^3.2.0" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", From e6017ba5e435a4a092d1cae80fc7059eea4e544a Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 20 May 2026 12:01:17 +0100 Subject: [PATCH 73/96] data-access notify structure SEARCH done --- acs-data-access/lib/notify.js | 99 ++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 41 deletions(-) diff --git a/acs-data-access/lib/notify.js b/acs-data-access/lib/notify.js index e2a7b0a3a..adab0090c 100644 --- a/acs-data-access/lib/notify.js +++ b/acs-data-access/lib/notify.js @@ -347,58 +347,75 @@ export class DataAccessNotify { ); } - structure_uuid(sess, uuid) { - if (!valid_uuid(uuid)) return; - return this.data.allowed_all_datasets( - sess.principal, - Constants.Perm.EditDataset - ).pipe( - rx.map(datasets => - datasets.get(uuid) - ), + _structure_resource_state(sess, uuid) { + if (!valid_uuid(uuid)) return; - rx.distinctUntilChanged(deep_equal), + return this.data.allowed_valid_datasets( + sess.principal, + Constants.Perm.EditDataset + ).pipe( + rx.map(datasets => datasets.get(uuid)), - rxu.shareLatest(), + rx.filter(Boolean), - rx.map(dataset_def => { - const {from, to, ...body} = dataset_def; - return { - status: 200, - response: { body } - } - }), + rx.map(dataset_def => { + const { from, to, ...body } = dataset_def; + return body; + }), + + rx.distinctUntilChanged(deep_equal), + + rxu.shareLatest(), + ); + } + + structure_uuid(sess, uuid) { + return this._structure_resource_state(sess, uuid) + .pipe( + rx.map(body => ({ + status: 200, + response: {body} + })), ); } structure_search(sess) { - return this.data.allowed_all_datasets( - sess.principal, - Constants.Perm.EditDataset - ).pipe( - rx.map(datasets => { - - let result = IMap(); + const dataset_uuids = this.data + .allowed_all_dataset_uuids( + sess.principal, + Constants.Perm.EditDataset + ) + .pipe( + rx.switchMap(uuids => { + const list = uuids.toArray?.() ?? []; - datasets.forEach( - (dataset, dataset_uuid) => { + if (list.length === 0) + return rx.of(IMap()); - result = result.set( - dataset_uuid, - { - uuid: dataset_uuid, - structure: - dataset.structure, - } - ); - } - ); + return rx.combineLatest( + list.map(uuid => + this._structure_resource_state(sess, uuid).pipe( + rx.map(body => [ + uuid, + { + status: 200, + body, + } + ]) + ) + ) + ).pipe( + rx.map(entries => IMap(entries)) + ); + }), - return result; - }), + rxu.shareLatest() + ); - rxu.shareLatest() - ); + return this._build_search_source(dataset_uuids); } + + + } \ No newline at end of file From a46e60fc6edfc0bd5ee6d870c8ce49ab28b0188b Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 20 May 2026 13:30:48 +0100 Subject: [PATCH 74/96] data access added test script for rx client --- acs-data-access/tests/test-notify-api.js | 71 ++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 acs-data-access/tests/test-notify-api.js diff --git a/acs-data-access/tests/test-notify-api.js b/acs-data-access/tests/test-notify-api.js new file mode 100644 index 000000000..6387909a4 --- /dev/null +++ b/acs-data-access/tests/test-notify-api.js @@ -0,0 +1,71 @@ + + +import {RxClient} from "@amrc-factoryplus/rx-client"; +import * as rxu from "@amrc-factoryplus/rx-util"; +import * as rx from "rxjs"; +import readline from "node:readline"; +import process from "node:process"; + + +/** + * This is a test script that uses rx-client DataAccess client interface + * to subscribe to DataAccess Notify API. + */ +async function main(){ + const fplus = new RxClient({ + directory_url: process.env.DIRECTORY_URL, + username: process.env.HUMAN_USERNAME, + password: process.env.HUMAN_PASSWORD, + verbose: 'ALL' + }); + + const DATASET_UUID = ""; + + const subscription = fplus.DataAccess + // .watch_metadata_list() + // .watch_single_metadata(DATASET_UUID) + // .search_metadata() + // .watch_structure_list() + // .watch_single_structure(DATASET_UUID) + .search_structure() + .subscribe({ + next: value => { + console.log("TEST: UPDATE from DataAccess"); + console.log(value?.toJS()); + console.log(""); + }, + + error: err => { + console.error("ERROR"); + console.error(err); + }, + + complete: () => { + console.log("COMPLETE"); + }, + }); + + // ---------------------------------------- + // Keep process alive + quit on q + // ---------------------------------------- + readline.emitKeypressEvents(process.stdin); + + if(process.stdin.isTTY) + process.stdin.setRawMode(true); + + console.log("Press q to quit"); + + process.stdin.on("keypress", (_, key) => { + if(key.name === "q"){ + console.log("\nQuitting"); + subscription.unsubscribe(); + process.exit(0); + } + }); +} + + +main().catch(err => { + console.error(err); + process.exit(1); +}) \ No newline at end of file From 94b058cfea5ebc51643298c4bcfb72fa50ce88fb Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 21 May 2026 11:03:37 +0100 Subject: [PATCH 75/96] data-access download data http checking if write is complete --- acs-data-access/lib/api-v1.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 74e5d8af7..a03ce803e 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -328,8 +328,9 @@ export class APIv1 { res, {}, ); - - res.end(); + if (!res.writableEnded) { + res.end(); + } }catch (err) { this.log(err); return fail(this.log, 500); From 66ea3e6f796f0ba6b00876c8f4282a5501ba1e83 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 21 May 2026 11:04:32 +0100 Subject: [PATCH 76/96] data access added simple test http endpoints via service client --- acs-data-access/tests/test-http-api.js | 34 ++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 acs-data-access/tests/test-http-api.js diff --git a/acs-data-access/tests/test-http-api.js b/acs-data-access/tests/test-http-api.js new file mode 100644 index 000000000..afff2ac3e --- /dev/null +++ b/acs-data-access/tests/test-http-api.js @@ -0,0 +1,34 @@ + + +import {ServiceClient} from "@amrc-factoryplus/service-client"; +import * as rx from "rxjs"; +import readline from "node:readline"; +import process from "node:process"; + + +/** + * This is a test script that uses rx-client DataAccess client interface + * to subscribe to DataAccess Notify API. + */ +async function main(){ + const fplus = new ServiceClient({ + directory_url: process.env.DIRECTORY_URL, + username: process.env.HUMAN_USERNAME, + password: process.env.HUMAN_PASSWORD, + verbose: 'ALL' + }); + + const DATASET_UUID = ""; + + const res = await fplus.DataAccess + // .get_metadata_list(); + // .get_single_metadata(DATASET_UUID) + .download_data(DATASET_UUID) + + console.log("Test Data Access HTTP"); + console.log(res); + console.log(""); +} + + +main() \ No newline at end of file From ef79e5e1d33dfebcbff7c840de264eb858147477 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 21 May 2026 11:05:20 +0100 Subject: [PATCH 77/96] js-service-client updated data access interface --- .../lib/service/data-access.js | 82 +++++++++++++------ 1 file changed, 57 insertions(+), 25 deletions(-) diff --git a/lib/js-service-client/lib/service/data-access.js b/lib/js-service-client/lib/service/data-access.js index 2b630e206..c26944b6d 100644 --- a/lib/js-service-client/lib/service/data-access.js +++ b/lib/js-service-client/lib/service/data-access.js @@ -2,10 +2,14 @@ * Factory+ NodeJS Utilities * Data Access service interface. */ -import { writeFile } from 'fs/promises'; + +import fs from "node:fs"; +import { pipeline } from "node:stream/promises"; +import { Readable } from "node:stream"; import { Service } from "../uuids.js"; import { ServiceInterface } from "./service-interface.js"; +import path from "node:path"; /** Interface to the Data Access service * @@ -22,7 +26,7 @@ export class DataAccess extends ServiceInterface{ /** * Returns list of dataset uuids with READ_DATASET permission. */ - async get_readable_dataset_uuids(){ + async get_metadata_list(){ const [st, json] = await this.fetch('v1/metadata'); if(st == 404) return []; if(st == 403) @@ -36,7 +40,7 @@ export class DataAccess extends ServiceInterface{ /** * Returns json object with dataset metadata */ - async get_dataset_metadata(uuid){ + async get_single_metadata(uuid){ const [st, json] = await this.fetch(`v1/metadata/${uuid}`); if(st == 404) return {}; if(st == 403) @@ -47,37 +51,65 @@ export class DataAccess extends ServiceInterface{ } /** - * Download data in csv format + * Download dataset CSV stream + * Returns a readable stream */ - async download_data(uuid) { - const [st, csv] = await this.fetch({ - url: `v1/data/${uuid}`, +/** + * Returns a stream of dataset CSV data. + */ + async download_data(uuid, output_dir=".") { + const [st, stream, _, headers] = await this.fetch({ + url: `v1/data/${uuid}`, method: 'POST', + accept: "text/csv", + response_type: "stream", }); - if (st == 404) return "Not Found"; - if (st == 403) - this.throw(`Unauthorised to access dataset ${uuid}`, st); - if (st != 200) - this.throw(`Can't download data for dataset ${uuid}`, st); + if (st == 404) { + return; + } + + if (st == 403) { + this.throw( + `Unauthorised to access dataset ${uuid}`, + st, + ); + } + + if (st != 200) { + this.throw( + `Can't download data for dataset ${uuid}`, + st, + ); + } - - // const buffer = Buffer.from(await res.arrayBuffer()); + const disposition = headers.get("Content-Disposition") ?? ""; - // // filename (optional) - // const disposition = res.headers.get('content-disposition'); - // let filename = `${uuid}.csv`; - // if (disposition?.includes('filename=')) { - // filename = disposition.split('filename=')[1]; - // } + const filename = + /filename="([^"]+)"/i.exec(disposition)?.[1] + ?? `${uuid}.csv`; - // await writeFile(filename, buffer); + /** + * Basic filename sanitization + */ + const safeFilename = filename.replace(/[\/\\]/g, "_"); - // return filename; + + const filepath = path.join(output_dir, safeFilename); + + /** + * fetch() returns a WHATWG ReadableStream + * Convert to Node stream for pipeline() + */ + + await pipeline(stream, fs.createWriteStream(filepath)); + return filepath; } - async get_editable_dataset_uuids(){ + + + async get_structure_list(){ const [st, json] = await this.fetch('v1/structure'); if(st == 404) return []; if(st == 403) @@ -116,7 +148,7 @@ export class DataAccess extends ServiceInterface{ * Returns dataset definition (content of the config entry) */ - async get_dataset_definition(uuid){ + async get_single_structure(uuid){ const [st, json] = await this.fetch(`v1/structure/${uuid}`); if(st == 404) return []; if(st == 403) @@ -127,7 +159,7 @@ export class DataAccess extends ServiceInterface{ } - async update_dataset_definition(uuid, structure, config){ + async update_dataset(uuid, structure, config){ const [st, json] = await this.fetch({ url: `v1/structure/${uuid}`, method: 'PUT', From b375767439083bc6f1ca16ab707aa6ebf7a02fd7 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 21 May 2026 11:05:38 +0100 Subject: [PATCH 78/96] data access package lock --- acs-data-access/package-lock.json | 2307 +---------------------------- 1 file changed, 73 insertions(+), 2234 deletions(-) diff --git a/acs-data-access/package-lock.json b/acs-data-access/package-lock.json index 0cd572feb..49fc6ab02 100644 --- a/acs-data-access/package-lock.json +++ b/acs-data-access/package-lock.json @@ -25,48 +25,9 @@ "eslint": "^9.23.0" } }, - "../lib/js-rx-util": { - "name": "@amrc-factoryplus/rx-util", - "version": "0.0.3", - "extraneous": true, - "license": "ISC", - "dependencies": { - "immutable": "^5.0.0-beta.5", - "rxjs": "^7.8.1" - }, - "devDependencies": { - "@eslint/js": "^9.18.0", - "eslint": "^9.18.0", - "globals": "^15.14.0" - } - }, - "../lib/js-service-client": { - "name": "@amrc-factoryplus/service-client", - "version": "1.6.0", - "extraneous": true, - "license": "ISC", - "dependencies": { - "content-type": "^1.0.5", - "isomorphic-ws": "^5.0.0", - "mqtt": "^5.3.6", - "optional-js": "^2.3.0", - "semver": "^7.6.0", - "sparkplug-payload": "^1.0.3" - }, - "devDependencies": { - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "^9.13.0", - "eslint": "^9.13.0", - "globals": "^15.11.0" - }, - "optionalDependencies": { - "got-fetch": "^5.1.8", - "gssapi.js": "^2.0.1" - } - }, - "node_modules/@amrc-factoryplus/rx-client": { + "../lib/js-rx-client": { + "name": "@amrc-factoryplus/rx-client", "version": "v0.0.1-bmz.1", - "resolved": "file:../lib/js-rx-client", "license": "ISC", "dependencies": { "@amrc-factoryplus/rx-util": "file:../js-rx-util", @@ -74,20 +35,32 @@ "immutable": "^5.0.0-rc.2", "rxjs": "^7.8.1", "uuid": "^11.0.2" + }, + "devDependencies": { + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "^9.14.0", + "eslint": "^9.14.0", + "globals": "^15.12.0", + "jsdoc": "^4.0.4" } }, - "node_modules/@amrc-factoryplus/rx-util": { + "../lib/js-rx-util": { + "name": "@amrc-factoryplus/rx-util", "version": "0.0.3", - "resolved": "file:../lib/js-rx-util", "license": "ISC", "dependencies": { "immutable": "^5.0.0-beta.5", "rxjs": "^7.8.1" + }, + "devDependencies": { + "@eslint/js": "^9.18.0", + "eslint": "^9.18.0", + "globals": "^15.14.0" } }, - "node_modules/@amrc-factoryplus/service-api": { + "../lib/js-service-api": { + "name": "@amrc-factoryplus/service-api", "version": "0.1.0", - "resolved": "file:../lib/js-service-api", "license": "ISC", "os": [ "!win32" @@ -107,311 +80,17 @@ "rxjs": "^7.8.1", "uuid": "^11.0.2", "ws": "^8.18.0" - } - }, - "node_modules/@amrc-factoryplus/service-api/node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "license": "MIT", - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@amrc-factoryplus/service-api/node_modules/body-parser": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", - "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", - "license": "MIT", - "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.3", - "http-errors": "^2.0.0", - "iconv-lite": "^0.7.0", - "on-finished": "^2.4.1", - "qs": "^6.14.1", - "raw-body": "^3.0.1", - "type-is": "^2.0.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/@amrc-factoryplus/service-api/node_modules/content-disposition": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", - "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/@amrc-factoryplus/service-api/node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, - "node_modules/@amrc-factoryplus/service-api/node_modules/express": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", - "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", - "license": "MIT", - "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.1", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "depd": "^2.0.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/@amrc-factoryplus/service-api/node_modules/finalhandler": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", - "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/@amrc-factoryplus/service-api/node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@amrc-factoryplus/service-api/node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/@amrc-factoryplus/service-api/node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@amrc-factoryplus/service-api/node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@amrc-factoryplus/service-api/node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@amrc-factoryplus/service-api/node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/@amrc-factoryplus/service-api/node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@amrc-factoryplus/service-api/node_modules/path-to-regexp": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", - "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", - "license": "MIT" - }, - "node_modules/@amrc-factoryplus/service-api/node_modules/raw-body": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", - "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.7.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/@amrc-factoryplus/service-api/node_modules/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", - "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.3", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.1", - "mime-types": "^3.0.2", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/@amrc-factoryplus/service-api/node_modules/serve-static": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", - "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" - }, - "engines": { - "node": ">= 18" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/@amrc-factoryplus/service-api/node_modules/type-is": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", - "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", - "license": "MIT", - "dependencies": { - "content-type": "^2.0.0", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/@amrc-factoryplus/service-api/node_modules/type-is/node_modules/content-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "devDependencies": { + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "^9.13.0", + "eslint": "^9.13.0", + "globals": "^15.11.0" } }, - "node_modules/@amrc-factoryplus/service-client": { + "../lib/js-service-client": { + "name": "@amrc-factoryplus/service-client", "version": "1.6.0", - "resolved": "file:../lib/js-service-client", "license": "ISC", "dependencies": { "content-type": "^1.0.5", @@ -421,19 +100,32 @@ "semver": "^7.6.0", "sparkplug-payload": "^1.0.3" }, + "devDependencies": { + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "^9.13.0", + "eslint": "^9.13.0", + "globals": "^15.11.0" + }, "optionalDependencies": { "got-fetch": "^5.1.8", "gssapi.js": "^2.0.1" } }, - "node_modules/@babel/runtime": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } + "node_modules/@amrc-factoryplus/rx-client": { + "resolved": "../lib/js-rx-client", + "link": true + }, + "node_modules/@amrc-factoryplus/rx-util": { + "resolved": "../lib/js-rx-util", + "link": true + }, + "node_modules/@amrc-factoryplus/service-api": { + "resolved": "../lib/js-service-api", + "link": true + }, + "node_modules/@amrc-factoryplus/service-client": { + "resolved": "../lib/js-service-client", + "link": true }, "node_modules/@dotenvx/dotenvx": { "version": "1.57.0", @@ -713,97 +405,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", - "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", - "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.2.tgz", - "integrity": "sha512-pa0vFRuws4wkvaXKK1uXZMAwAX4/t8ANaJo45iw/oQHNQ9q5xUzwgFmVJGXiga2BeN+zpX7Vf9vmsiIa2J+MUw==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", - "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", - "license": "BSD-3-Clause" - }, - "node_modules/@sindresorhus/is": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", - "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@szmarczak/http-timer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", - "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "defer-to-connect": "^2.0.1" - }, - "engines": { - "node": ">=14.16" - } - }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -811,14 +412,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -826,51 +419,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "25.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", - "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", - "license": "MIT", - "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" - } - }, - "node_modules/@types/readable-stream": { - "version": "4.0.23", - "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.23.tgz", - "integrity": "sha512-wwXrtQvbMHxCbBgjHaMGEmImFTQxxpfMOR/ZoQnXxB1woqkUbdLGFDgauo00Py9IudiaqSeiBiulSV9i6XIPig==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -924,21 +472,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz", - "integrity": "sha512-iFY7JCgHbepc0b82yLaw4IMortylNb6wG4kL+4R0C3iv6i+RHGHux/yUX5BTiRvSX/shMnngjR1YyNMnXEFh5A==", - "license": "MIT" - }, - "node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -955,53 +488,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/are-we-there-yet": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.0.6.tgz", - "integrity": "sha512-Zfw6bteqM9gQXZ1BIWOgM8xEwMrUGoyL8nW13+O+OOgNX3YhuDN1GDgg1NzdTlmm3j+9sHy7uBZ12r+z9lXnZQ==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.0 || ^1.1.13" - } - }, - "node_modules/are-we-there-yet/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/are-we-there-yet/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/are-we-there-yet/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/are-we-there-yet/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1046,113 +532,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.14.0" - } - }, "node_modules/balanced-match": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/big-integer": { - "version": "1.6.52", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", - "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", - "license": "Unlicense", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/binary": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", - "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", - "license": "MIT", - "dependencies": { - "buffers": "~0.1.1", - "chainsaw": "~0.1.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "license": "MIT", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bl": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/bl/-/bl-6.1.6.tgz", - "integrity": "sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg==", - "license": "MIT", - "dependencies": { - "@types/readable-stream": "^4.0.0", - "buffer": "^6.0.3", - "inherits": "^2.0.4", - "readable-stream": "^4.2.0" - } - }, - "node_modules/bl/node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/bl/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, "license": "MIT" }, "node_modules/body-parser": { @@ -1198,77 +582,13 @@ "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "node_modules/broker-factory": { - "version": "3.1.14", - "resolved": "https://registry.npmjs.org/broker-factory/-/broker-factory-3.1.14.tgz", - "integrity": "sha512-L45k5HMbPIrMid0nTOZ/UPXG/c0aRuQKVrSDFIb1zOkvfiyHgYmIjc3cSiN1KwQIvRDOtKE0tfb3I9EZ3CmpQQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.29.2", - "fast-unique-numbers": "^9.0.27", - "tslib": "^2.8.1", - "worker-factory": "^7.0.49" - } - }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" - }, - "node_modules/buffer-indexof-polyfill": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", - "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/buffer-shims": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", - "integrity": "sha512-Zy8ZXMyxIT6RMTeY7OP/bDndfj6bwCan7SS98CEndS6deHwWPpseeHlwarNcBim+etXnF9HBc1non5JgDaJU1g==", - "license": "MIT" - }, - "node_modules/buffers": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", - "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", - "engines": { - "node": ">=0.2.0" - } - }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1278,37 +598,6 @@ "node": ">= 0.8" } }, - "node_modules/cacheable-lookup": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", - "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/cacheable-request": { - "version": "10.2.14", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", - "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@types/http-cache-semantics": "^4.0.2", - "get-stream": "^6.0.1", - "http-cache-semantics": "^4.1.1", - "keyv": "^4.5.3", - "mimic-response": "^4.0.0", - "normalize-url": "^8.0.0", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - } - }, "node_modules/call-bind": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", @@ -1366,27 +655,6 @@ "node": ">=6" } }, - "node_modules/camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/chainsaw": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", - "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", - "license": "MIT/X11", - "dependencies": { - "traverse": ">=0.3.0 <0.4" - }, - "engines": { - "node": "*" - } - }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1404,89 +672,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "license": "ISC" - }, - "node_modules/cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==", - "license": "ISC", - "dependencies": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - } - }, - "node_modules/cmake-js": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-6.3.2.tgz", - "integrity": "sha512-7MfiQ/ijzeE2kO+WFB9bv4QP5Dn2yVaAP2acFJr4NIFy2hT4w6O4EpOTLNcohR5IPX7M4wNf/5taIqMj7UA9ug==", - "license": "MIT", - "dependencies": { - "axios": "^0.21.1", - "bluebird": "^3", - "debug": "^4", - "fs-extra": "^5.0.0", - "is-iojs": "^1.0.1", - "lodash": "^4", - "memory-stream": "0", - "npmlog": "^1.2.0", - "rc": "^1.2.7", - "semver": "^5.0.3", - "splitargs": "0", - "tar": "^4", - "unzipper": "^0.8.13", - "url-join": "0", - "which": "^1.0.9", - "yargs": "^3.6.0" - }, - "bin": { - "cmake-js": "bin/cmake-js" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/cmake-js/node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/cmake-js/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/cmake-js/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1516,56 +701,13 @@ "node": ">=16" } }, - "node_modules/commist": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", - "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==", - "license": "MIT" - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, "license": "MIT" }, - "node_modules/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "engines": [ - "node >= 6.0" - ], - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/concat-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/concat-stream/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -1602,29 +744,6 @@ "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", "license": "MIT" }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, - "node_modules/cors": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", - "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1664,6 +783,7 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -1677,46 +797,6 @@ } } }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/deep-equal": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", @@ -1749,15 +829,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -1765,17 +836,6 @@ "dev": true, "license": "MIT" }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=10" - } - }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -1810,12 +870,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "license": "MIT" - }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -1861,51 +915,6 @@ "node": ">= 0.4" } }, - "node_modules/duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", - "license": "BSD-3-Clause", - "dependencies": { - "readable-stream": "^2.0.2" - } - }, - "node_modules/duplexer2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/duplexer2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/duplexer2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/duplexer2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/eciesjs": { "version": "0.4.18", "resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.18.tgz", @@ -2170,24 +1179,6 @@ "node": ">= 0.6" } }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -2293,19 +1284,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-unique-numbers": { - "version": "9.0.27", - "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-9.0.27.tgz", - "integrity": "sha512-nDA9ADeINN8SA2u2wCtU+siWFTTDqQR37XvgPIDDmboWQeExz7X0mImxuaN+kJddliIqy2FpVRmnvRZ+j8i1/A==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.29.2", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=18.2.0" - } - }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -2336,12 +1314,6 @@ "node": ">=16.0.0" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "license": "MIT" - }, "node_modules/finalhandler": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", @@ -2413,26 +1385,6 @@ "dev": true, "license": "ISC" }, - "node_modules/follow-redirects": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", - "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -2448,17 +1400,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/form-data-encoder": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", - "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">= 14.17" - } - }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -2467,56 +1408,14 @@ "engines": { "node": ">= 0.6" } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-extra": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", - "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "node_modules/fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "license": "ISC", - "dependencies": { - "minipass": "^2.6.0" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "license": "ISC" - }, - "node_modules/fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "dependencies": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - }, + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", "engines": { - "node": ">=0.6" + "node": ">= 0.6" } }, "node_modules/function-bind": { @@ -2537,20 +1436,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gauge": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz", - "integrity": "sha512-fVbU2wRE91yDvKUnrIaQlHKAWKY5e08PmztCrwuH5YVQ+Z/p3d0ny2T48o6uvAAXHIUnfaQdHkmxYbQft1eHVA==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "dependencies": { - "ansi": "^0.3.0", - "has-unicode": "^2.0.0", - "lodash.pad": "^4.1.0", - "lodash.padend": "^4.1.0", - "lodash.padstart": "^4.1.0" - } - }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -2600,27 +1485,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2659,65 +1523,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/got": { - "version": "12.6.1", - "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", - "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@sindresorhus/is": "^5.2.0", - "@szmarczak/http-timer": "^5.0.1", - "cacheable-lookup": "^7.0.0", - "cacheable-request": "^10.2.8", - "decompress-response": "^6.0.0", - "form-data-encoder": "^2.1.2", - "get-stream": "^6.0.1", - "http2-wrapper": "^2.1.10", - "lowercase-keys": "^3.0.0", - "p-cancelable": "^3.0.0", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/got-fetch": { - "version": "5.1.10", - "resolved": "https://registry.npmjs.org/got-fetch/-/got-fetch-5.1.10.tgz", - "integrity": "sha512-Gwj/A2htjvLEcY07PKDItv0WCPEs3dV2vWeZ+9TVBSKSTuWEZ4oXaMD0ZAOsajwx2orahQWN4HI0MfRyWSZsbg==", - "deprecated": "please use built-in fetch in nodejs", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "got": "^12.0.0" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/gssapi.js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/gssapi.js/-/gssapi.js-2.0.1.tgz", - "integrity": "sha512-5D+qylV8uIKMaxTTKhpzvfGRz5+okr/rpwC6YlaCoDVBpvkuNgL8pM7+r7aTzZ2oT5dA4w2WoZTV82opGj+RZQ==", - "hasInstallScript": true, - "license": "ISC", - "dependencies": { - "bindings": "^1.5.0", - "cmake-js": "^6.1.0", - "node-addon-api": "^1.7.2" - } - }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -2779,12 +1584,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "license": "ISC" - }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -2797,20 +1596,6 @@ "node": ">= 0.4" } }, - "node_modules/help-me": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", - "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", - "license": "MIT" - }, - "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "license": "BSD-2-Clause", - "optional": true, - "peer": true - }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -2831,21 +1616,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/http2-wrapper": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", - "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.2.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -2867,26 +1637,6 @@ "node": ">=0.10.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -2896,12 +1646,6 @@ "node": ">= 4" } }, - "node_modules/immutable": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", - "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", - "license": "MIT" - }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -2929,29 +1673,12 @@ "node": ">=0.8.19" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -2966,24 +1693,6 @@ "node": ">= 0.4" } }, - "node_modules/invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ip-address": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", - "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -3095,18 +1804,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", - "license": "MIT", - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -3120,12 +1817,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-iojs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-iojs/-/is-iojs-1.1.0.tgz", - "integrity": "sha512-tLn1j3wYSL6DkvEI+V/j0pKohpa5jk+ER74v6S4SgCXnjS0WA+DoZbwZBrrhgwksMvtuwndyGeG5F8YMsoBzSA==", - "license": "MIT" - }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -3154,12 +1845,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT" - }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -3293,34 +1978,6 @@ "node": ">=18" } }, - "node_modules/isomorphic-ws": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", - "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", - "license": "MIT", - "peerDependencies": { - "ws": "*" - } - }, - "node_modules/jose": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/jose/-/jose-5.10.0.tgz", - "integrity": "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, "node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", @@ -3338,7 +1995,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/json-schema-traverse": { @@ -3355,37 +2012,16 @@ "dev": true, "license": "MIT" }, - "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "license": "MIT", - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } }, - "node_modules/lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", - "license": "MIT", - "dependencies": { - "invert-kv": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -3400,12 +2036,6 @@ "node": ">= 0.8.0" } }, - "node_modules/listenercount": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", - "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", - "license": "ISC" - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3422,12 +2052,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -3435,50 +2059,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.pad": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz", - "integrity": "sha512-mvUHifnLqM+03YNzeTBS1/Gr6JRFjd3rRx88FHWUvamVaT9k2O/kXha3yBSOwB9/DTQrSTLJNHvLBBt2FdX7Mg==", - "license": "MIT" - }, - "node_modules/lodash.padend": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", - "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==", - "license": "MIT" - }, - "node_modules/lodash.padstart": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", - "integrity": "sha512-sW73O6S8+Tg66eY56DBk85aQzzUJDtpoXFBgELMd5P/SotAguo+1kYO6RuYgXxA4HJH3LFTFPASX6ET6bjfriw==", - "license": "MIT" - }, - "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", - "license": "Apache-2.0" - }, - "node_modules/lowercase-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", - "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -3497,15 +2077,6 @@ "node": ">= 0.6" } }, - "node_modules/memory-stream": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/memory-stream/-/memory-stream-0.0.3.tgz", - "integrity": "sha512-q0D3m846qY6ZkIt+19ZemU5vH56lpOZZwoJc3AICARKh/menBuayQUjAGPrqtHQQMUYERSdOrej92J9kz7LgYA==", - "license": "MMIT", - "dependencies": { - "readable-stream": "~1.0.26-2" - } - }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -3572,24 +2143,11 @@ "node": ">=6" } }, - "node_modules/mimic-response": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", - "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -3598,114 +2156,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "license": "ISC", - "dependencies": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "node_modules/minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "license": "MIT", - "dependencies": { - "minipass": "^2.9.0" - } - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mqtt": { - "version": "5.15.1", - "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.15.1.tgz", - "integrity": "sha512-V1WnkGuJh3ec9QXzy5Iylw8OOBK+Xu1WhxcQ9mMpLThG+/JZIMV1PgLNRgIiqXhZnvnVLsuyxHl5A/3bHHbcAA==", - "license": "MIT", - "dependencies": { - "@types/readable-stream": "^4.0.21", - "@types/ws": "^8.18.1", - "commist": "^3.2.0", - "concat-stream": "^2.0.0", - "debug": "^4.4.1", - "help-me": "^5.0.0", - "lru-cache": "^10.4.3", - "minimist": "^1.2.8", - "mqtt-packet": "^9.0.2", - "number-allocator": "^1.0.14", - "readable-stream": "^4.7.0", - "rfdc": "^1.4.1", - "socks": "^2.8.6", - "split2": "^4.2.0", - "worker-timers": "^8.0.23", - "ws": "^8.18.3" - }, - "bin": { - "mqtt": "build/bin/mqtt.js", - "mqtt_pub": "build/bin/pub.js", - "mqtt_sub": "build/bin/sub.js" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/mqtt-packet": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.2.tgz", - "integrity": "sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA==", - "license": "MIT", - "dependencies": { - "bl": "^6.0.8", - "debug": "^4.3.4", - "process-nextick-args": "^2.0.1" - } - }, - "node_modules/mqtt/node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/mqtt/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -3721,83 +2171,23 @@ }, "node_modules/negotiator": { "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-addon-api": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", - "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", - "license": "MIT" - }, - "node_modules/normalize-url": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.1.1.tgz", - "integrity": "sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npmlog": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-1.2.1.tgz", - "integrity": "sha512-1J5KqSRvESP6XbjPaXt2H6qDzgizLTM7x0y1cXIjP2PpvdCqyNC7TO3cPRKsuYlElbi/DwkzRRdG2zpmE0IktQ==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "dependencies": { - "ansi": "~0.3.0", - "are-we-there-yet": "~1.0.0", - "gauge": "~1.2.0" - } - }, - "node_modules/number-allocator": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", - "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.1", - "js-sdsl": "4.3.0" - } - }, - "node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/object-inspect": { @@ -3878,15 +2268,6 @@ "node": ">= 0.8" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", @@ -3902,12 +2283,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/optional-js": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/optional-js/-/optional-js-2.3.0.tgz", - "integrity": "sha512-B0LLi+Vg+eko++0z/b8zIv57kp7HKEzaPJo7LowJXMUKYdf+3XJGu/cw03h/JhIOsLnP+cG5QnTHAuicjA5fMw==", - "license": "MIT" - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -3926,29 +2301,6 @@ "node": ">= 0.8.0" } }, - "node_modules/os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==", - "license": "MIT", - "dependencies": { - "lcid": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-cancelable": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", - "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=12.20" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -4013,15 +2365,6 @@ "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -4068,47 +2411,6 @@ "node": ">= 0.8.0" } }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, - "node_modules/protobufjs": { - "version": "6.11.6", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.6.tgz", - "integrity": "sha512-k8BHqgPBOtrlougZZqF2uUk5Z7bN8f0wj+3e8M3hvtSv0NBAz4VBy5f6R5Nxq/l+i7mRFTgNZb2trxqTpHNY/A==", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", - "@types/node": ">=13.7.0", - "long": "^4.0.0" - }, - "bin": { - "pbjs": "bin/pbjs", - "pbts": "bin/pbts" - } - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -4147,20 +2449,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -4185,48 +2473,6 @@ "node": ">= 0.8" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/readable-stream/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "license": "MIT" - }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", @@ -4247,14 +2493,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -4265,68 +2503,6 @@ "node": ">=4" } }, - "node_modules/responselike": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", - "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "lowercase-keys": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "license": "MIT" - }, - "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/router": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/router/node_modules/path-to-regexp": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", - "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", @@ -4379,18 +2555,6 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, - "node_modules/semver": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/send": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", @@ -4477,12 +2641,6 @@ "node": ">= 0.4" } }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "license": "MIT" - }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -4588,56 +2746,6 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz", - "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", - "license": "MIT", - "dependencies": { - "ip-address": "^10.1.1", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/sparkplug-payload": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sparkplug-payload/-/sparkplug-payload-1.0.3.tgz", - "integrity": "sha512-JAQSyuHVQQe/LzIlJdcIaD1F1c+rbXoolII3H1whQkhuZ96+G53RoPWqP9zCPZYFjvfSMHnQNIedGIZw/zjHJw==", - "license": "EPL-2.0", - "dependencies": { - "@types/long": "^4.0.0", - "long": "^4.0.0", - "protobufjs": "^6.11.3" - } - }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "license": "ISC", - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/splitargs": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/splitargs/-/splitargs-0.0.7.tgz", - "integrity": "sha512-UUFYD2oWbNwULH6WoVtLUOw8ch586B+HUqcsAjjjeoBQAM1bD4wZRXu01koaxyd8UeYpybWqW4h+lO1Okv40Tg==", - "license": "ISC" - }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -4666,38 +2774,6 @@ "integrity": "sha512-TZsxxZzKPkiD7fxcKx0Ze9s0+WnGGbX1yWZNoXXmN6YjyEu/62pYkLKo7P627N27BT9kI93ZfZAx38brr/hq+Q==", "license": "MIT" }, - "node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", - "license": "MIT" - }, - "node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", - "license": "MIT", - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -4733,25 +2809,6 @@ "node": ">=8" } }, - "node_modules/tar": { - "version": "4.4.19", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", - "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", - "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", - "dependencies": { - "chownr": "^1.1.4", - "fs-minipass": "^1.2.7", - "minipass": "^2.9.0", - "minizlib": "^1.3.3", - "mkdirp": "^0.5.5", - "safe-buffer": "^5.2.1", - "yallist": "^3.1.1" - }, - "engines": { - "node": ">=4.5" - } - }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -4761,15 +2818,6 @@ "node": ">=0.6" } }, - "node_modules/traverse": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", - "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", - "license": "MIT/X11", - "engines": { - "node": "*" - } - }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -4802,27 +2850,6 @@ "node": ">= 0.6" } }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "license": "MIT" - }, - "node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", - "license": "MIT" - }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -4832,56 +2859,6 @@ "node": ">= 0.8" } }, - "node_modules/unzipper": { - "version": "0.8.14", - "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.8.14.tgz", - "integrity": "sha512-8rFtE7EP5ssOwGpN2dt1Q4njl0N1hUXJ7sSPz0leU2hRdq6+pra57z4YPBlVqm40vcgv6ooKZEAx48fMTv9x4w==", - "license": "MIT", - "dependencies": { - "big-integer": "^1.6.17", - "binary": "~0.3.0", - "bluebird": "~3.4.1", - "buffer-indexof-polyfill": "~1.0.0", - "duplexer2": "~0.1.4", - "fstream": "~1.0.10", - "listenercount": "~1.0.1", - "readable-stream": "~2.1.5", - "setimmediate": "~1.0.4" - } - }, - "node_modules/unzipper/node_modules/bluebird": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", - "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", - "license": "MIT" - }, - "node_modules/unzipper/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/unzipper/node_modules/process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha512-yN0WQmuCX63LP/TMvAg31nvT6m4vDqJEiiv2CAZqWOGNWutc9DfDk1NPYYmKUFmaVM2UwDowH4u5AHWYP/jxKw==", - "license": "MIT" - }, - "node_modules/unzipper/node_modules/readable-stream": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz", - "integrity": "sha512-NkXT2AER7VKXeXtJNSaWLpWIhmtSE3K2PguaLEeWr4JILghcIKqoLt1A3wHrnpDC5+ekf8gfk1GKWkFXe4odMw==", - "license": "MIT", - "dependencies": { - "buffer-shims": "^1.0.0", - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" - } - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -4892,18 +2869,6 @@ "punycode": "^2.1.0" } }, - "node_modules/url-join": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-0.0.1.tgz", - "integrity": "sha512-H6dnQ/yPAAVzMQRvEvyz01hhfQL5qRWSEt7BX8t9DqnPw9BjMb64fjIRq76Uvf1hkHp+mTZvEVJ5guXOT0Xqaw==", - "license": "MIT" - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -5008,18 +2973,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/window-size": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", - "integrity": "sha512-2thx4pB0cV3h+Bw7QmMXcEbdmOzv9t0HFplJH/Lz6yu60hXYy5RT8rUu+wlIreVxWsGN20mo+MHeCSfUpQBwPw==", - "license": "MIT", - "bin": { - "window-size": "cli.js" - }, - "engines": { - "node": ">= 0.10.0" - } - }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -5030,120 +2983,6 @@ "node": ">=0.10.0" } }, - "node_modules/worker-factory": { - "version": "7.0.49", - "resolved": "https://registry.npmjs.org/worker-factory/-/worker-factory-7.0.49.tgz", - "integrity": "sha512-lW7tpgy6aUv2dFsQhv1yv+XFzdkCf/leoKRTGMPVK5/die6RrUjqgJHJf556qO+ZfytNG6wPXc17E8zzsOLUDw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.29.2", - "fast-unique-numbers": "^9.0.27", - "tslib": "^2.8.1" - } - }, - "node_modules/worker-timers": { - "version": "8.0.31", - "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-8.0.31.tgz", - "integrity": "sha512-ngkq5S6JuZyztom8tDgBzorLo9byhBMko/sXfgiUD945AuzKGg1GCgDMCC3NaYkicLpGKXutONM36wEX8UbBCA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.29.2", - "tslib": "^2.8.1", - "worker-timers-broker": "^8.0.16", - "worker-timers-worker": "^9.0.14" - } - }, - "node_modules/worker-timers-broker": { - "version": "8.0.16", - "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-8.0.16.tgz", - "integrity": "sha512-JyP3AvUGyPGbBGW7XiUewm2+0pN/aYo1QpVf5kdXAfkDZcN3p7NbWrG6XnyDEpDIvfHk/+LCnOW/NsuiU9riYA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.29.2", - "broker-factory": "^3.1.14", - "fast-unique-numbers": "^9.0.27", - "tslib": "^2.8.1", - "worker-timers-worker": "^9.0.14" - } - }, - "node_modules/worker-timers-worker": { - "version": "9.0.14", - "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-9.0.14.tgz", - "integrity": "sha512-/qF06C60sXmSLfUl7WglvrDIbspmPOM8UrG63Dnn4bi2x4/DfqHS/+dxF5B+MdHnYO5tVuZYLHdAodrKdabTIg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.29.2", - "tslib": "^2.8.1", - "worker-factory": "^7.0.49" - } - }, - "node_modules/wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", - "license": "MIT", - "dependencies": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/ws": { - "version": "8.20.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz", - "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/y18n": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", - "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", - "license": "ISC" - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "license": "ISC" - }, - "node_modules/yargs": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", - "integrity": "sha512-ONJZiimStfZzhKamYvR/xvmgW3uEkAUFSP91y2caTEPhzF6uP2JfPiVZcq66b/YR0C3uitxSV7+T1x8p5bkmMg==", - "license": "MIT", - "dependencies": { - "camelcase": "^2.0.1", - "cliui": "^3.0.3", - "decamelize": "^1.1.1", - "os-locale": "^1.4.0", - "string-width": "^1.0.1", - "window-size": "^0.1.4", - "y18n": "^3.2.0" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", From dddb38dbb6386bd49949641bb4305aaa92fe04f1 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 1 Jun 2026 16:39:23 +0100 Subject: [PATCH 79/96] data access download http endpoint returns zip with separate csv files per device --- acs-data-access/lib/api-v1.js | 23 +- acs-data-access/lib/influx-reader.js | 249 ++++++----- acs-data-access/package-lock.json | 602 ++++++++++++++++++++++++++- acs-data-access/package.json | 3 + 4 files changed, 747 insertions(+), 130 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index a03ce803e..4a46b9080 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -315,7 +315,7 @@ export class APIv1 { try { // Resolve dataset tree - const resolved = await this._resolve_dataset(dataset_uuid); + const resolved_sources = await this._resolve_dataset(dataset_uuid); res.setHeader("Content-Type", "text/csv"); res.setHeader( @@ -323,14 +323,21 @@ export class APIv1 { `attachment; filename="${dataset_uuid}.csv"` ); - await this.influxReader.stream_dataset_data( - resolved, - res, - {}, + const zipStream = this.influxReader.exportDevices(resolved_sources); + + + res.setHeader( + "Content-Type", + "application/zip" ); - if (!res.writableEnded) { - res.end(); - } + + res.setHeader( + "Content-Disposition", + `attachment; filename=${dataset_uuid}.zip` + ); + + zipStream.pipe(res); + }catch (err) { this.log(err); return fail(this.log, 500); diff --git a/acs-data-access/lib/influx-reader.js b/acs-data-access/lib/influx-reader.js index 02ffb71be..6cbdb6d86 100644 --- a/acs-data-access/lib/influx-reader.js +++ b/acs-data-access/lib/influx-reader.js @@ -1,135 +1,162 @@ +import { ZipArchive } from "archiver"; +import { PassThrough } from "stream"; +import { once } from "events"; +import pLimit from "p-limit"; -import {InfluxDB, flux} from '@influxdata/influxdb-client'; -import {csv_escape} from './utils.js'; - -export class InfluxReader{ - constructor(opts){ +export class InfluxReader { + constructor(opts) { this.log = opts.debug.bound("influxReader"); + this.influx_bucket = opts.influx_bucket; this.influx_org = opts.influx_org; - this.influx_query_api = opts.influx_client.getQueryApi(opts.influx_org); - } - - async stream_dataset_data(device_sources, writable, meta = {}) { - - const measurementFilter = meta.measurement - ? `|> filter(fn: (r) => r._measurement == "${meta.measurement}")` - : ``; - - // ===================================================== - // Device + per-device session filtering - // ===================================================== - - const deviceConditions = device_sources.map(src => { - - const timeParts = []; - - if (src.from) { - timeParts.push(`r._time >= time(v: "${src.from}")`); - } - if (src.to) { - timeParts.push(`r._time <= time(v: "${src.to}")`); - } + this.influx_query_api = + opts.influx_client.getQueryApi( + this.influx_org + ); - const timeExpr = timeParts.length - ? ` and ${timeParts.join(" and ")}` - : ""; - - return `(r.topLevelInstance == "${src.device_uuid}"${timeExpr})`; + this.limit = pLimit(4); + } - }).join(" or "); + exportDevices(deviceSources, meta = {}) { + const archive = new ZipArchive({ + zlib: { + level: 0, + }, + }); - // ===================================================== - // Global bounds - // ===================================================== + archive.on("warning", err => { + this.log("archive warning", err); + }); - const froms = device_sources - .map(s => s.from) - .filter(Boolean) - .sort(); + archive.on("error", err => { + this.log("archive error", err); + archive.destroy(err); + }); - const tos = device_sources - .map(s => s.to) - .filter(Boolean) - .sort(); + this.#appendDevices( + archive, + deviceSources, + meta + ).catch(err => { + archive.destroy(err); + }); - const globalFrom = froms.length - ? froms[0] - : "0"; + return archive; + } - const globalTo = tos.length - ? tos[tos.length - 1] - : null; + async #appendDevices(archive, deviceSources, meta) { + for (const source of deviceSources) { + await this.limit(() => + new Promise((resolve, reject) => { + const csvStream = new PassThrough({ + highWaterMark: 1024 * 1024, + }); + + archive.append(csvStream, { + name: `${source.device_uuid}.csv`, + }); + + this.#streamDevice(source, csvStream, meta) + .then(resolve) + .catch(reject); + }) + ); + } + + await new Promise((resolve, reject) => { + archive.once("error", reject); + archive.once("close", resolve); + archive.finalize(); + }); + } - const rangeClause = globalTo - ? `|> range(start: time(v: "${globalFrom}"), stop: time(v: "${globalTo}"))` - : `|> range(start: ${globalFrom})`; + async #streamDevice( + source, + writable, + meta + ) { + const query = + this.#buildFluxQuery( + source, + meta + ); + + this.log( + "streaming", + source.device_uuid + ); + + const response = + this.influx_query_api.response(query); + + try { + for await ( + const chunk of response.iterateLines() + ) { + if ( + !writable.write( + chunk + "\n" + ) + ) { + await once( + writable, + "drain" + ); + } + } - // ===================================================== - // QUERY - // ===================================================== + writable.end(); + + await once( + writable, + "finish" + ); + } + catch (err) { + writable.destroy(err); + throw err; + } + } - const query = ` - from(bucket: "${this.influx_bucket}") + #buildFluxQuery( + source, + meta = {} + ) { + const start = + source.from ?? + "1970-01-01T00:00:00Z"; + + const stop = + source.to ?? + "2100-01-01T00:00:00Z"; + + const measurementFilter = + meta.measurement + ? ` + |> filter( + fn: (r) => + r._measurement == + "${meta.measurement}" + ) + ` + : ""; - ${rangeClause} + return ` + from(bucket: "${this.influx_bucket}") - |> filter(fn: (r) => ${deviceConditions}) + |> range( + start: time(v: "${start}"), + stop: time(v: "${stop}") + ) ${measurementFilter} - |> map(fn: (r) => ({ - r with - column_name: - if exists r.path and r.path != "" - then r.device + ":" + r.path + ":" + r._measurement - else r.device + ":" + r._measurement - })) - - // IMPORTANT: - // remove grouping before pivot - |> group() - - |> pivot( - rowKey: ["_time", "unit"], - columnKey: ["column_name"], - valueColumn: "_value" + |> filter( + fn: (r) => + r.topLevelInstance == + "${source.device_uuid}" ) - - |> sort(columns: ["_time"], desc: false) `; - - return this._stream_influx_csv(query, writable); - } - - async _stream_influx_csv(query, writable) { - return new Promise((resolve, reject) => { - let headersWritten = false; - - this.influx_query_api.queryRows(query, { - next: (row, tableMeta) => { - const obj = tableMeta.toObject(row); - - // Write headers once - if (!headersWritten) { - writable.write( - Object.keys(obj).join(",") + "\n" - ); - headersWritten = true; - } - - const csvRow = Object.values(obj) - .map(v => csv_escape(v)) - .join(","); - - writable.write(csvRow + "\n"); - }, - - error: reject, - - complete: resolve, - }); - }); } } \ No newline at end of file diff --git a/acs-data-access/package-lock.json b/acs-data-access/package-lock.json index 49fc6ab02..8a969a437 100644 --- a/acs-data-access/package-lock.json +++ b/acs-data-access/package-lock.json @@ -15,9 +15,12 @@ "@amrc-factoryplus/service-client": "file:../lib/js-service-client", "@dotenvx/dotenvx": "^1.39.0", "@influxdata/influxdb-client": "^1.35.0", + "archiver": "^8.0.0", "deep-equal": "^2.2.3", "express": "^4.21.2", + "p-limit": "^7.3.0", "rxjs": "^7.8.2", + "stream": "^0.0.3", "stream-size": "^0.0.6", "uuid": "^11.0.5" }, @@ -419,6 +422,18 @@ "dev": true, "license": "MIT" }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -488,6 +503,38 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/archiver": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-8.0.0.tgz", + "integrity": "sha512-fV1orZfsnPn9BaSByR/qE67rJCLJEy2Ox5bq7nJh+jquWaNh6Sfec75kJ2T6PtdGUbPQlrVoSVCEOa5SdiTQ1g==", + "license": "MIT", + "dependencies": { + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "is-stream": "^4.0.0", + "lazystream": "^1.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^3.0.0", + "tar-stream": "^3.0.0", + "zip-stream": "^7.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/archiver/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -517,6 +564,12 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -532,6 +585,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/b4a": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", + "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -539,6 +606,117 @@ "dev": true, "license": "MIT" }, + "node_modules/bare-events": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.3.tgz", + "integrity": "sha512-HdUm8EMQBLaJvGUdidNNbqpA1kYkwNcb+MYxkxCLAPJGQzlv9J0C24h8V65Z4c5GLd/JEALDvpFCQgpLJqc0zw==", + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/bare-fs": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.1.tgz", + "integrity": "sha512-WDRsyVN52eAx/lBamKD6uyw8H4228h/x0sGGGegOamM2cd7Pag88GfMQalobXI+HaEUxpCkbKQUDOQqt9wawRw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-os": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.9.1.tgz", + "integrity": "sha512-6M5XjcnsygQNPMCMPXSK379xrJFiZ/AEMNBmFEmQW8d/789VQATvriyi5r0HYTL9TkQ26rn3kgdTG3aisbrXkQ==", + "license": "Apache-2.0", + "engines": { + "bare": ">=1.14.0" + } + }, + "node_modules/bare-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", + "license": "Apache-2.0", + "dependencies": { + "bare-os": "^3.0.1" + } + }, + "node_modules/bare-stream": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.1.tgz", + "integrity": "sha512-Vp0cnjYyrEC4whYTymQ+YZi6pBpfiICZO3cfRG8sy67ZNWe951urv1x4eW1BKNngw3U+3fPYb5JQvHbCtxH7Ow==", + "license": "Apache-2.0", + "dependencies": { + "streamx": "^2.25.0", + "teex": "^1.0.1" + }, + "peerDependencies": { + "bare-abort-controller": "*", + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + }, + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.3.tgz", + "integrity": "sha512-Kccpc7ACfXaxfeInfqKcZtW4pT5YBn1mesc4sCsun6sRwtbJ4h+sNOaksUpYEJUKfN65YWC6Bw2OJEFiKxq8nQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/body-parser": { "version": "1.20.4", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", @@ -589,6 +767,39 @@ "concat-map": "0.0.1" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -701,6 +912,46 @@ "node": ">=16" } }, + "node_modules/component-emitter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-2.0.0.tgz", + "integrity": "sha512-4m5s3Me2xxlVKG9PkZpQqHQR7bgpnN7joDMJ4yvVkVXngjoITG76IaZmzmywSeRTeTpc6N6r3H3+KyUurV8OYw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/compress-commons": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-7.0.1.tgz", + "integrity": "sha512-g0S8KAD8qf4+V//pr3BfB1aBnARLXNz2Gx+jmHU0LEriUuoQUOPOulVquHKTJ8+EAIIO7fhseNDr9wK5Q9FKBQ==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^7.0.1", + "is-stream": "^4.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/compress-commons/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -744,6 +995,37 @@ "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", "license": "MIT" }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-7.0.1.tgz", + "integrity": "sha512-IBWsY8xznyQrcHn8h4bC8/4ErNke5elzgG8GcqF4RFPw6aHkWWRc7Tgw6upjaTX/CT/yQgqYENkxYsTYN+hW2g==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1179,6 +1461,33 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -1270,6 +1579,12 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "license": "MIT" + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1637,6 +1952,26 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -2022,6 +2357,54 @@ "json-buffer": "3.0.1" } }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -2178,6 +2561,15 @@ "node": ">= 0.6" } }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -2302,16 +2694,15 @@ } }, "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.0.tgz", + "integrity": "sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw==", "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "yocto-queue": "^1.2.1" }, "engines": { - "node": ">=10" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2333,6 +2724,35 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -2411,6 +2831,21 @@ "node": ">= 0.8.0" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -2473,6 +2908,73 @@ "node": ">= 0.8" } }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readdir-glob": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-3.0.0.tgz", + "integrity": "sha512-AhNB2KgKeVJr16nK9LLZbJNWnYoT23ZrumNKFDebHBdkC8KHSqWo871JAUhoWC/RtjEVdqNMFpM6qrwRbaUqpw==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^10.2.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/yqnn" + } + }, + "node_modules/readdir-glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", @@ -2768,12 +3270,41 @@ "node": ">= 0.4" } }, + "node_modules/stream": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/stream/-/stream-0.0.3.tgz", + "integrity": "sha512-aMsbn7VKrl4A2T7QAQQbzgN7NVc70vgF5INQrBXqn4dCXN1zy3L9HGgLO5s7PExmdrzTJ8uR/27aviW8or8/+A==", + "license": "MIT", + "dependencies": { + "component-emitter": "^2.0.0" + } + }, "node_modules/stream-size": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/stream-size/-/stream-size-0.0.6.tgz", "integrity": "sha512-TZsxxZzKPkiD7fxcKx0Ze9s0+WnGGbX1yWZNoXXmN6YjyEu/62pYkLKo7P627N27BT9kI93ZfZAx38brr/hq+Q==", "license": "MIT" }, + "node_modules/streamx": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.26.0.tgz", + "integrity": "sha512-VvNG1K72Po/xwJzxZFnZ++Tbrv4lwSptsbkFuzXCJAYZvCK5nnxsvXU6ajqkv7chyiI1Y0YXq2Jh8Iy8Y7NF/A==", + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -2809,6 +3340,36 @@ "node": ">=8" } }, + "node_modules/tar-stream": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.0.tgz", + "integrity": "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" + } + }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -2869,6 +3430,12 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -2984,17 +3551,30 @@ } }, "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12.20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zip-stream": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-7.0.5.tgz", + "integrity": "sha512-dSvYKdvLsAHCDqPOhIwk/q5CvuWtTB3Dgpoe0uVEFjTzIOAmsQpprX25InCvrvJsirEbu1OHyy67n/kAj1Sw/w==", + "license": "MIT", + "dependencies": { + "compress-commons": "^7.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">=18" + } } } } diff --git a/acs-data-access/package.json b/acs-data-access/package.json index 6924d3b60..d0544a84e 100644 --- a/acs-data-access/package.json +++ b/acs-data-access/package.json @@ -16,9 +16,12 @@ "@amrc-factoryplus/service-client": "file:../lib/js-service-client", "@dotenvx/dotenvx": "^1.39.0", "@influxdata/influxdb-client": "^1.35.0", + "archiver": "^8.0.0", "deep-equal": "^2.2.3", "express": "^4.21.2", + "p-limit": "^7.3.0", "rxjs": "^7.8.2", + "stream": "^0.0.3", "stream-size": "^0.0.6", "uuid": "^11.0.5" }, From e644060c629794f1b6ddedead6703b8e3ba9dd4a Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 1 Jun 2026 16:40:18 +0100 Subject: [PATCH 80/96] data access js service client download_data handles zip --- .../lib/service/data-access.js | 51 ++++++++++--------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/lib/js-service-client/lib/service/data-access.js b/lib/js-service-client/lib/service/data-access.js index c26944b6d..37b9b54f4 100644 --- a/lib/js-service-client/lib/service/data-access.js +++ b/lib/js-service-client/lib/service/data-access.js @@ -57,58 +57,61 @@ export class DataAccess extends ServiceInterface{ /** * Returns a stream of dataset CSV data. */ - async download_data(uuid, output_dir=".") { + async download_data(uuid, output_dir = ".") { const [st, stream, _, headers] = await this.fetch({ url: `v1/data/${uuid}`, - method: 'POST', - accept: "text/csv", + method: "POST", + accept: "application/zip", response_type: "stream", }); - if (st == 404) { - return; - } + if (st === 404) return; - if (st == 403) { + if (st === 403) { this.throw( `Unauthorised to access dataset ${uuid}`, - st, + st ); } - if (st != 200) { + if (st !== 200) { this.throw( `Can't download data for dataset ${uuid}`, - st, + st ); } - const disposition = headers.get("Content-Disposition") ?? ""; + const disposition = + headers.get("Content-Disposition") ?? ""; const filename = - /filename="([^"]+)"/i.exec(disposition)?.[1] - ?? `${uuid}.csv`; + /filename\*?=(?:UTF-8'')?("?)([^";]+)\1/i.exec(disposition)?.[2] + ?? `${uuid}.zip`; - /** - * Basic filename sanitization - */ const safeFilename = filename.replace(/[\/\\]/g, "_"); + fs.mkdirSync(output_dir, { recursive: true }); const filepath = path.join(output_dir, safeFilename); - /** - * fetch() returns a WHATWG ReadableStream - * Convert to Node stream for pipeline() - */ - - await pipeline(stream, fs.createWriteStream(filepath)); - return filepath; - } + const nodeStream = + stream instanceof Readable + ? stream + : Readable.fromWeb(stream); + nodeStream.on("error", err => { + this.throw(`Download stream error: ${err.message}`); + }); + await pipeline( + nodeStream, + fs.createWriteStream(filepath) + ); + return filepath; + } + async get_structure_list(){ const [st, json] = await this.fetch('v1/structure'); if(st == 404) return []; From 969e0cdc3887983ed5ecf4c9a65b390ed972960f Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 1 Jun 2026 16:57:14 +0100 Subject: [PATCH 81/96] data access indlux query removed unnecessary cols --- acs-data-access/lib/influx-reader.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/acs-data-access/lib/influx-reader.js b/acs-data-access/lib/influx-reader.js index 6cbdb6d86..4877d9b0f 100644 --- a/acs-data-access/lib/influx-reader.js +++ b/acs-data-access/lib/influx-reader.js @@ -119,6 +119,11 @@ export class InfluxReader { } } + +// ,result,table,_start,_stop,_time,_value,_field,_measurement,device,group,node,topLevelInstance,topLevelSchema,unit,usesInstances,usesSchemas + +// _time, _value, _measurement, device, unit + #buildFluxQuery( source, meta = {} @@ -157,6 +162,14 @@ export class InfluxReader { r.topLevelInstance == "${source.device_uuid}" ) + + |> keep(columns: [ + "_time", + "_value", + "_measurement", + "device", + "unit" + ]) `; } } \ No newline at end of file From 51fa96593e028455ff9c4080c75f5e925d0445de Mon Sep 17 00:00:00 2001 From: amrc-za Date: Tue, 2 Jun 2026 11:57:49 +0100 Subject: [PATCH 82/96] data access removed unnecessary comment --- acs-data-access/lib/influx-reader.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/acs-data-access/lib/influx-reader.js b/acs-data-access/lib/influx-reader.js index 4877d9b0f..66000c5a1 100644 --- a/acs-data-access/lib/influx-reader.js +++ b/acs-data-access/lib/influx-reader.js @@ -120,10 +120,6 @@ export class InfluxReader { } -// ,result,table,_start,_stop,_time,_value,_field,_measurement,device,group,node,topLevelInstance,topLevelSchema,unit,usesInstances,usesSchemas - -// _time, _value, _measurement, device, unit - #buildFluxQuery( source, meta = {} From b41d59cc4ae77dfd6127c438af490539e45afc0b Mon Sep 17 00:00:00 2001 From: amrc-za Date: Tue, 2 Jun 2026 15:18:52 +0100 Subject: [PATCH 83/96] data access GET /metadata vitest --- acs-data-access/package-lock.json | 1281 ++++++++++++++++- acs-data-access/package.json | 5 +- .../tests/http/metadata_list.test.js | 300 ++++ 3 files changed, 1559 insertions(+), 27 deletions(-) create mode 100644 acs-data-access/tests/http/metadata_list.test.js diff --git a/acs-data-access/package-lock.json b/acs-data-access/package-lock.json index 8a969a437..6f68e3411 100644 --- a/acs-data-access/package-lock.json +++ b/acs-data-access/package-lock.json @@ -25,7 +25,8 @@ "uuid": "^11.0.5" }, "devDependencies": { - "eslint": "^9.23.0" + "eslint": "^9.23.0", + "vitest": "^4.1.8" } }, "../lib/js-rx-client": { @@ -167,6 +168,40 @@ "@noble/ciphers": "^1.0.0" } }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", @@ -369,6 +404,32 @@ "integrity": "sha512-woWMi8PDpPQpvTsRaUw4Ig+nOGS/CWwAwS66Fa1Vr/EkW+NEwxI8YfPBsdBMn33jK2Y86/qMiiuX/ROHIkJLTw==", "license": "MIT" }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, "node_modules/@noble/ciphers": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", @@ -408,6 +469,334 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@oxc-project/types": { + "version": "0.133.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", + "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", + "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", + "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", + "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", + "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", + "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", + "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", + "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", + "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", + "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", + "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", + "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", + "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", + "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", + "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", + "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -422,6 +811,119 @@ "dev": true, "license": "MIT" }, + "node_modules/@vitest/expect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.8.tgz", + "integrity": "sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.8", + "@vitest/utils": "4.1.8", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.8.tgz", + "integrity": "sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.8", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.8.tgz", + "integrity": "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.8.tgz", + "integrity": "sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.8", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.8.tgz", + "integrity": "sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.8", + "@vitest/utils": "4.1.8", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.8.tgz", + "integrity": "sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.8.tgz", + "integrity": "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.8", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -564,6 +1066,16 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -866,6 +1378,16 @@ "node": ">=6" } }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -980,6 +1502,13 @@ "node": ">= 0.6" } }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/cookie": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", @@ -1171,6 +1700,16 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/dotenv": { "version": "17.3.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", @@ -1267,6 +1806,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -1442,6 +1988,16 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -1511,6 +2067,16 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/express": { "version": "4.22.1", "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", @@ -1733,6 +2299,21 @@ "node": ">= 0.6" } }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -2390,33 +2971,306 @@ "util-deprecate": "~1.0.1" } }, - "node_modules/lazystream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/lazystream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.8.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/locate-path": { @@ -2442,6 +3296,16 @@ "dev": true, "license": "MIT" }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -2545,6 +3409,25 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -2648,6 +3531,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -2800,10 +3694,24 @@ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", "engines": { "node": ">=12" @@ -2821,6 +3729,35 @@ "node": ">= 0.4" } }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -3005,6 +3942,40 @@ "node": ">=4" } }, + "node_modules/rolldown": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", + "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.133.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.3", + "@rolldown/binding-darwin-arm64": "1.0.3", + "@rolldown/binding-darwin-x64": "1.0.3", + "@rolldown/binding-freebsd-x64": "1.0.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", + "@rolldown/binding-linux-arm64-gnu": "1.0.3", + "@rolldown/binding-linux-arm64-musl": "1.0.3", + "@rolldown/binding-linux-ppc64-gnu": "1.0.3", + "@rolldown/binding-linux-s390x-gnu": "1.0.3", + "@rolldown/binding-linux-x64-gnu": "1.0.3", + "@rolldown/binding-linux-x64-musl": "1.0.3", + "@rolldown/binding-openharmony-arm64": "1.0.3", + "@rolldown/binding-wasm32-wasi": "1.0.3", + "@rolldown/binding-win32-arm64-msvc": "1.0.3", + "@rolldown/binding-win32-x64-msvc": "1.0.3" + } + }, "node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", @@ -3242,12 +4213,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -3257,6 +4252,13 @@ "node": ">= 0.8" } }, + "node_modules/std-env": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -3370,6 +4372,50 @@ "b4a": "^1.6.4" } }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -3467,6 +4513,174 @@ "node": ">= 0.8" } }, + "node_modules/vite": { + "version": "8.0.16", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", + "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.15", + "rolldown": "1.0.3", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.18", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.8.tgz", + "integrity": "sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.8", + "@vitest/mocker": "4.1.8", + "@vitest/pretty-format": "4.1.8", + "@vitest/runner": "4.1.8", + "@vitest/snapshot": "4.1.8", + "@vitest/spy": "4.1.8", + "@vitest/utils": "4.1.8", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.8", + "@vitest/browser-preview": "4.1.8", + "@vitest/browser-webdriverio": "4.1.8", + "@vitest/coverage-istanbul": "4.1.8", + "@vitest/coverage-v8": "4.1.8", + "@vitest/ui": "4.1.8", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, "node_modules/which": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", @@ -3540,6 +4754,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/acs-data-access/package.json b/acs-data-access/package.json index d0544a84e..c3b25c1bf 100644 --- a/acs-data-access/package.json +++ b/acs-data-access/package.json @@ -5,7 +5,7 @@ "main": "server.js", "type": "module", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "vitest run" }, "author": "", "license": "ISC", @@ -26,6 +26,7 @@ "uuid": "^11.0.5" }, "devDependencies": { - "eslint": "^9.23.0" + "eslint": "^9.23.0", + "vitest": "^4.1.8" } } diff --git a/acs-data-access/tests/http/metadata_list.test.js b/acs-data-access/tests/http/metadata_list.test.js new file mode 100644 index 000000000..b1ec52412 --- /dev/null +++ b/acs-data-access/tests/http/metadata_list.test.js @@ -0,0 +1,300 @@ +import {ServiceClient} from "@amrc-factoryplus/service-client"; +import process from "node:process"; +import {beforeAll, describe, expect, test} from 'vitest'; +import { DataAccess as Constants } from "../../lib/constants.js"; +import {Test_Uuids} from '../temp_uuids.js'; + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +describe('GET /metadata', () => { + let test_fplus; + let admin_fplus; + let TEST_PRINCIPAL; + + beforeAll(async () => { + TEST_PRINCIPAL = "bbbca528-3099-47c9-ab25-e95e1ea06e93"; + + admin_fplus = new ServiceClient({ + directory_url: process.env.DIRECTORY_URL, + username: process.env.ADMIN_USERNAME, + password: process.env.ADMIN_PASSWORD, + verbose: 'ALL' + }); + + + test_fplus = new ServiceClient({ + directory_url: process.env.DIRECTORY_URL, + username: process.env.TEST_HUMAN_USERNAME, + password: process.env.TEST_HUMAN_PASSWORD, + verbose: 'ALL' + }); + + }); + + + + async function addGrant(principal, permission, target, plural){ + const grant_uuid = await admin_fplus.Auth.add_grant({ + principal, + permission, + target, + plural + }); + + return grant_uuid; + } + + async function deleteGrant(grant_uuid){ + await admin_fplus.Auth.delete_grant(grant_uuid) + } + + async function makeInvalid(dataset_uuid, app){ + + } + + async function makeValid(dataset_uuid, app){ + + } + + test('Datasets exist but user has no permissions', async () => { + const res = await fplus.DataAccess.get_metadata_list(); + expect(res).toEqual([]); + }); + + test('User has unrelated permission to one dataset', async () => { + const dataset_uuid = Test_Uuids.Src_Datasets.TestDeviceDataset; + + // Grant unrelated EditDataset permission to the user + const grant_uuid = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.EditDataset, + dataset_uuid, + false + ); + + await sleep(1000); + + const res = await test_fplus.DataAccess.get_metadata_list(); + expect(res).toEqual([]); + + await deleteGrant(grant_uuid); + await sleep(1000); + }); + + test('User has correct Read permission to one dataset', async () => { + const dataset_uuid = Test_Uuids.Src_Datasets.TestDeviceDataset; + + // Grant correct permission + const grant_uuid = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.ReadDataset, + dataset_uuid, + false + ); + await sleep(1000); + + const res = await test_fplus.DataAccess.get_metadata_list(); + expect(res).toContain(dataset_uuid); + + await deleteGrant(grant_uuid); + + await sleep(100); + }); + + test('User has correct permission to multiple datasets', async () => { + const datasets = [ + Test_Uuids.Src_Datasets.TestDeviceDataset, + Test_Uuids.Session_Datasets.TestSessionAllDataset + ]; + + // Add read grants + let grants = []; + for (let dataset_uuid of datasets){ + const grant_uuid = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.ReadDataset, + dataset_uuid, + false + ) + grants.push(grant_uuid); + } + + await sleep(1000); + + const res = await test_fplus.DataAccess.get_metadata_list(); + expect(res).toHaveLength(datasets.length); + expect(res).toEqual(expect.arrayContaining(datasets)); + + // Revoke grants + for(let grant of grants){ + await deleteGrant(grant); + } + + await sleep(100); + }); + + test('Multiple datasets, some readable', async () => { + const datasets = { + Readable: Test_Uuids.Src_Datasets.TestDeviceDataset, + Unreadable: Test_Uuids.Session_Datasets.TestSessionAllDataset + }; + + // ======== add grants ====== + let grants = []; + const read_grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.ReadDataset, + datasets.Readable, + false + ); + grants.push(read_grant); + + const edit_grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.EditDataset, + datasets.Unreadable, + false + ); + grants.push(edit_grant); + + await sleep(1000); + + // ========================= + + + const res = await test_fplus.DataAccess.get_metadata_list(); + expect(res).toHaveLength(1); + expect(res).toContain(datasets.Readable); + + + // Revoke grants + for(let grant of grants){ + await deleteGrant(grant); + } + + await sleep(100); + }); + + test('Multiple (simple SRC) datasets with Read permissions, invalid ones must be excluded', async () => { + const datasets = [ + Test_Uuids.Src_Datasets.TestDeviceDataset, + Test_Uuids.Src_Datasets.Node2_TestDoubleDeviceDataset, + Test_Uuids.Src_Datasets.Node2_TestFloutDeviceDataset + ]; + + + // ----- add grants --- + const grants = []; + for (let uuid of datasets){ + const read_grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.ReadDataset, + uuid, + false + ); + grants.push(read_grant); + } + + await sleep(1000); + + // --------------------- + + + const invalid_dataset = datasets[0]; + + // make dataset invalid + await admin_fplus.ConfigDB.put_config( + Constants.App.UnionComponents, + invalid_dataset, + [] + ); + await sleep(1000); + + // ========================= + + const res = await test_fplus.DataAccess.get_metadata_list(); + + expect(res).toHaveLength(2); + expect(res).not.toContain(invalid_dataset); + + await sleep(1000); + + // ==== remove invalid config ====== + await admin_fplus.ConfigDB.delete_config( + Constants.App.SparkplugSrc, + invalid_dataset + ); + + await sleep(500); + + // ==== Revoke grants ==== + for (let uuid of grants){ + await deleteGrant(uuid); + } + }, 30000); + + + test.only('Invalid Union dataset composed of only Src datasets', async () => { + const invalid_union_dataset = Test_Uuids.Union_Datasets.TestDoublesUnionDataset; + const child_datasets = [ + Test_Uuids.Src_Datasets.TestDeviceDataset, + Test_Uuids.Src_Datasets.TestDoubleDeviceDataset + ]; + + const datasets = [ + invalid_union_dataset, + ...child_datasets + ]; + + // ---- add READ grants --- + const grants = []; + for (let uuid of datasets){ + const read_grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.ReadDataset, + uuid, + false + ); + grants.push(read_grant); + } + + await sleep(1000); + + + // ---- Make Union dataset invalid + await admin_fplus.ConfigDB.put_config( + Constants.App.SparkplugSrc, + invalid_union_dataset, + { + "source": Test_Uuids.Devices.Node2_TestDoubleDevice + } + ); + + await sleep(500); + + + const res = await test_fplus.DataAccess.get_metadata_list(); + console.log(res); + + + expect(res).toHaveLength(child_datasets.length); + expect(res).not.toContain(invalid_union_dataset); + + + // ---- Make Union dataset valid again + await admin_fplus.ConfigDB.delete_config( + Constants.App.SparkplugSrc, + invalid_union_dataset + ); + + // ==== Revoke grants ==== + for (let uuid of grants){ + await deleteGrant(uuid); + } + + }, 30000); +}); + + From a70528117fee1ca17f4d596a87bb855ceb652c79 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 3 Jun 2026 09:39:25 +0100 Subject: [PATCH 84/96] data access http GET /metadata tests - fixed fplus client cache bug --- .../tests/http/metadata_list.test.js | 59 ++++++++++--------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/acs-data-access/tests/http/metadata_list.test.js b/acs-data-access/tests/http/metadata_list.test.js index b1ec52412..9184b5daf 100644 --- a/acs-data-access/tests/http/metadata_list.test.js +++ b/acs-data-access/tests/http/metadata_list.test.js @@ -1,6 +1,6 @@ import {ServiceClient} from "@amrc-factoryplus/service-client"; import process from "node:process"; -import {beforeAll, describe, expect, test} from 'vitest'; +import {afterAll, beforeAll, beforeEach, describe, expect, test} from 'vitest'; import { DataAccess as Constants } from "../../lib/constants.js"; import {Test_Uuids} from '../temp_uuids.js'; @@ -11,29 +11,40 @@ function sleep(ms) { describe('GET /metadata', () => { let test_fplus; let admin_fplus; - let TEST_PRINCIPAL; + const TEST_PRINCIPAL=process.env.TEST_HUMAN_UUID; - beforeAll(async () => { - TEST_PRINCIPAL = "bbbca528-3099-47c9-ab25-e95e1ea06e93"; + let ALL_GRANTS = []; + beforeAll(async () => { admin_fplus = new ServiceClient({ directory_url: process.env.DIRECTORY_URL, username: process.env.ADMIN_USERNAME, password: process.env.ADMIN_PASSWORD, verbose: 'ALL' }); + }); - + + beforeEach(async () => { test_fplus = new ServiceClient({ directory_url: process.env.DIRECTORY_URL, username: process.env.TEST_HUMAN_USERNAME, password: process.env.TEST_HUMAN_PASSWORD, verbose: 'ALL' }); - + await sleep(1000); // }); + // Clean up grants if not removed in case of failure. + afterAll(async () => { + if(ALL_GRANTS.length > 0){ + for (let grant of ALL_GRANTS){ + await deleteGrant(grant); + } + } + }); + async function addGrant(principal, permission, target, plural){ const grant_uuid = await admin_fplus.Auth.add_grant({ @@ -42,24 +53,25 @@ describe('GET /metadata', () => { target, plural }); - + ALL_GRANTS.push(grant_uuid); return grant_uuid; } async function deleteGrant(grant_uuid){ await admin_fplus.Auth.delete_grant(grant_uuid) + ALL_GRANTS = ALL_GRANTS.filter(uuid => uuid !== grant_uuid); } - async function makeInvalid(dataset_uuid, app){ - + async function addConfig(app, obj, config){ + await admin_fplus.ConfigDB.put_config( app, obj, config ); } - async function makeValid(dataset_uuid, app){ - + async function removeConfig(app, obj){ + await admin_fplus.ConfigDB.delete_config( app, obj ); } test('Datasets exist but user has no permissions', async () => { - const res = await fplus.DataAccess.get_metadata_list(); + const res = await test_fplus.DataAccess.get_metadata_list(); expect(res).toEqual([]); }); @@ -73,14 +85,14 @@ describe('GET /metadata', () => { dataset_uuid, false ); - await sleep(1000); + const res = await test_fplus.DataAccess.get_metadata_list(); expect(res).toEqual([]); await deleteGrant(grant_uuid); - await sleep(1000); + }); test('User has correct Read permission to one dataset', async () => { @@ -100,7 +112,6 @@ describe('GET /metadata', () => { await deleteGrant(grant_uuid); - await sleep(100); }); test('User has correct permission to multiple datasets', async () => { @@ -131,8 +142,6 @@ describe('GET /metadata', () => { for(let grant of grants){ await deleteGrant(grant); } - - await sleep(100); }); test('Multiple datasets, some readable', async () => { @@ -173,8 +182,6 @@ describe('GET /metadata', () => { for(let grant of grants){ await deleteGrant(grant); } - - await sleep(100); }); test('Multiple (simple SRC) datasets with Read permissions, invalid ones must be excluded', async () => { @@ -205,7 +212,7 @@ describe('GET /metadata', () => { const invalid_dataset = datasets[0]; // make dataset invalid - await admin_fplus.ConfigDB.put_config( + await addConfig( Constants.App.UnionComponents, invalid_dataset, [] @@ -222,11 +229,7 @@ describe('GET /metadata', () => { await sleep(1000); // ==== remove invalid config ====== - await admin_fplus.ConfigDB.delete_config( - Constants.App.SparkplugSrc, - invalid_dataset - ); - + await removeConfig(Constants.App.SparkplugSrc, invalid_dataset); await sleep(500); // ==== Revoke grants ==== @@ -236,7 +239,7 @@ describe('GET /metadata', () => { }, 30000); - test.only('Invalid Union dataset composed of only Src datasets', async () => { + test('Invalid Union dataset composed of only Src datasets', async () => { const invalid_union_dataset = Test_Uuids.Union_Datasets.TestDoublesUnionDataset; const child_datasets = [ Test_Uuids.Src_Datasets.TestDeviceDataset, @@ -264,7 +267,7 @@ describe('GET /metadata', () => { // ---- Make Union dataset invalid - await admin_fplus.ConfigDB.put_config( + await addConfig( Constants.App.SparkplugSrc, invalid_union_dataset, { @@ -284,7 +287,7 @@ describe('GET /metadata', () => { // ---- Make Union dataset valid again - await admin_fplus.ConfigDB.delete_config( + await removeConfig( Constants.App.SparkplugSrc, invalid_union_dataset ); From 259fcfdad69dc9801eed7ed2d155fc4900ae68e6 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 3 Jun 2026 10:41:45 +0100 Subject: [PATCH 85/96] data access tests GET /metadata added parent child invalid tests --- .../tests/http/metadata_list.test.js | 269 ++++++++++++++++-- 1 file changed, 250 insertions(+), 19 deletions(-) diff --git a/acs-data-access/tests/http/metadata_list.test.js b/acs-data-access/tests/http/metadata_list.test.js index 9184b5daf..49f224c0d 100644 --- a/acs-data-access/tests/http/metadata_list.test.js +++ b/acs-data-access/tests/http/metadata_list.test.js @@ -89,10 +89,11 @@ describe('GET /metadata', () => { const res = await test_fplus.DataAccess.get_metadata_list(); - expect(res).toEqual([]); - await deleteGrant(grant_uuid); + await deleteGrant(grant_uuid); + + expect(res).toEqual([]); }); test('User has correct Read permission to one dataset', async () => { @@ -108,10 +109,11 @@ describe('GET /metadata', () => { await sleep(1000); const res = await test_fplus.DataAccess.get_metadata_list(); - expect(res).toContain(dataset_uuid); await deleteGrant(grant_uuid); + expect(res).toContain(dataset_uuid); + }); test('User has correct permission to multiple datasets', async () => { @@ -135,13 +137,13 @@ describe('GET /metadata', () => { await sleep(1000); const res = await test_fplus.DataAccess.get_metadata_list(); - expect(res).toHaveLength(datasets.length); - expect(res).toEqual(expect.arrayContaining(datasets)); // Revoke grants for(let grant of grants){ await deleteGrant(grant); } + expect(res).toHaveLength(datasets.length); + expect(res).toEqual(expect.arrayContaining(datasets)); }); test('Multiple datasets, some readable', async () => { @@ -174,14 +176,13 @@ describe('GET /metadata', () => { const res = await test_fplus.DataAccess.get_metadata_list(); - expect(res).toHaveLength(1); - expect(res).toContain(datasets.Readable); - // Revoke grants for(let grant of grants){ await deleteGrant(grant); } + expect(res).toHaveLength(1); + expect(res).toContain(datasets.Readable); }); test('Multiple (simple SRC) datasets with Read permissions, invalid ones must be excluded', async () => { @@ -223,9 +224,6 @@ describe('GET /metadata', () => { const res = await test_fplus.DataAccess.get_metadata_list(); - expect(res).toHaveLength(2); - expect(res).not.toContain(invalid_dataset); - await sleep(1000); // ==== remove invalid config ====== @@ -236,18 +234,89 @@ describe('GET /metadata', () => { for (let uuid of grants){ await deleteGrant(uuid); } + expect(res).toHaveLength(2); + expect(res).not.toContain(invalid_dataset); }, 30000); - - test('Invalid Union dataset composed of only Src datasets', async () => { - const invalid_union_dataset = Test_Uuids.Union_Datasets.TestDoublesUnionDataset; + /** + * Invalid union dataset is not returned. + * But the child datasets should still be returned as long as they are valid on their own. + */ + test('Invalid (Parent) Union dataset composed of only Src datasets', async () => { + const invalid_parent_dataset = Test_Uuids.Union_Datasets.TestDoublesUnionDataset; const child_datasets = [ Test_Uuids.Src_Datasets.TestDeviceDataset, Test_Uuids.Src_Datasets.TestDoubleDeviceDataset ]; const datasets = [ - invalid_union_dataset, + invalid_parent_dataset, + ...child_datasets + ]; + + // ---- add READ grants --- + const grants = []; + for (let uuid of datasets){ + const read_grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.ReadDataset, + uuid, + false + ); + grants.push(read_grant); + } + + await sleep(1000); + + + // ---- Make Union dataset invalid + await addConfig( + Constants.App.SparkplugSrc, + invalid_parent_dataset, + { + "source": Test_Uuids.Devices.Node2_TestDoubleDevice + } + ); + + await sleep(500); + + + const res = await test_fplus.DataAccess.get_metadata_list(); + + // ---- Make Union dataset valid again + await removeConfig( + Constants.App.SparkplugSrc, + invalid_parent_dataset + ); + + // ==== Revoke grants ==== + for (let uuid of grants){ + await deleteGrant(uuid); + } + expect(res).toHaveLength(child_datasets.length); + expect(res).not.toContain(invalid_parent_dataset); + + }, 30000); + + /** + * Union dataset composed of: + * > SessionNode2DoubleDataset: + * > Node2_TestDoubleDeviceDataset - source dataset + * > TestDoublesUnionDataset: + * > TestDeviceDataset - source dataset + * > TestDoubleDeviceDataset - source dataset + */ + + test('Invalid (Parent) Union dataset composed of Session and Union', async () => { + + const invalid_parent_dataset = Test_Uuids.Union_Datasets.TestNestedUnionDataset; + const child_datasets = [ + Test_Uuids.Session_Datasets.SessionNode2DoubleDataset, + Test_Uuids.Union_Datasets.TestDoublesUnionDataset + ]; + + const datasets = [ + invalid_parent_dataset, ...child_datasets ]; @@ -269,7 +338,7 @@ describe('GET /metadata', () => { // ---- Make Union dataset invalid await addConfig( Constants.App.SparkplugSrc, - invalid_union_dataset, + invalid_parent_dataset, { "source": Test_Uuids.Devices.Node2_TestDoubleDevice } @@ -279,17 +348,177 @@ describe('GET /metadata', () => { const res = await test_fplus.DataAccess.get_metadata_list(); - console.log(res); + // ---- Make Union dataset valid again + await removeConfig( + Constants.App.SparkplugSrc, + invalid_parent_dataset + ); + + // ==== Revoke grants ==== + for (let uuid of grants){ + await deleteGrant(uuid); + } + expect(res).toHaveLength(child_datasets.length); + expect(res).not.toContain(invalid_parent_dataset); + }, 30000); + + + test('Invalid (Parent) Session dataset composed of Union', async () => { + const invalid_parent_dataset = Test_Uuids.Session_Datasets.TestSessionAllDataset; + const child_datasets = [Test_Uuids.Union_Datasets.TestUnionAllDataset]; + + const datasets = [ + invalid_parent_dataset, + ...child_datasets + ]; + + // ---- add READ grants --- + const grants = []; + for (let uuid of datasets){ + const read_grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.ReadDataset, + uuid, + false + ); + grants.push(read_grant); + } + + await sleep(1000); + + + // ---- Make Union dataset invalid + await addConfig( + Constants.App.SparkplugSrc, + invalid_parent_dataset, + { + "source": Test_Uuids.Devices.Node2_TestDoubleDevice + } + ); + + await sleep(500); + + const res = await test_fplus.DataAccess.get_metadata_list(); + // ---- Make Union dataset valid again + await removeConfig( + Constants.App.SparkplugSrc, + invalid_parent_dataset + ); + + // ==== Revoke grants ==== + for (let uuid of grants){ + await deleteGrant(uuid); + } + expect(res).toHaveLength(child_datasets.length); - expect(res).not.toContain(invalid_union_dataset); + expect(res).not.toContain(invalid_parent_dataset); + + }, 30000); + + test('Invalid Child (SRC) makes Parent (Union) invalid too', async () => { + const invalid_child_dataset = Test_Uuids.Src_Datasets.TestDeviceDataset; + const parent_dataset = Test_Uuids.Union_Datasets.TestUnionAllDataset; + + const datasets = [ + invalid_child_dataset, + parent_dataset + ]; + + // ---- add READ grants --- + const grants = []; + for (let uuid of datasets){ + const read_grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.ReadDataset, + uuid, + false + ); + grants.push(read_grant); + } + + await sleep(1000); + + + // ---- Make Union dataset invalid + await addConfig( + Constants.App.SessionLimits, + invalid_child_dataset, + { + "source": "invalid", + "from": "invalid", + "to": "invalid" + } + ); + + await sleep(500); + + + const res = await test_fplus.DataAccess.get_metadata_list(); + // ---- Make Union dataset valid again await removeConfig( + Constants.App.SessionLimits, + invalid_child_dataset + ); + + // ==== Revoke grants ==== + for (let uuid of grants){ + await deleteGrant(uuid); + } + + expect(res).toHaveLength(0); + + }, 30000); + + + + + test.only('Invalid Child (Union) makes Parent (Session) invalid too', async () => { + const invalid_child_dataset = Test_Uuids.Union_Datasets.TestUnionAllDataset; + const parent_dataset = Test_Uuids.Session_Datasets.TestSessionAllDataset; + + const datasets = [ + invalid_child_dataset, + parent_dataset + ]; + + // ---- add READ grants --- + const grants = []; + for (let uuid of datasets){ + const read_grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.ReadDataset, + uuid, + false + ); + grants.push(read_grant); + } + + await sleep(1000); + + + // ---- Make Union dataset invalid + await addConfig( Constants.App.SparkplugSrc, - invalid_union_dataset + invalid_child_dataset, + { + "source": "invalid" + } + ); + + await sleep(500); + + + const res = await test_fplus.DataAccess.get_metadata_list(); + + // ---- Make Union dataset valid again + await removeConfig( + Constants.App.SparkplugSrc, + invalid_child_dataset ); // ==== Revoke grants ==== @@ -297,6 +526,8 @@ describe('GET /metadata', () => { await deleteGrant(uuid); } + expect(res).toHaveLength(0); + }, 30000); }); From ed05520909d0e6d87c3857e076df4ac629855dc1 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 3 Jun 2026 10:51:56 +0100 Subject: [PATCH 86/96] data access tests GET /metadata added invalid session child test --- .../tests/http/metadata_list.test.js | 71 +++++++++++++++++-- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/acs-data-access/tests/http/metadata_list.test.js b/acs-data-access/tests/http/metadata_list.test.js index 49f224c0d..8d7e29de6 100644 --- a/acs-data-access/tests/http/metadata_list.test.js +++ b/acs-data-access/tests/http/metadata_list.test.js @@ -240,9 +240,9 @@ describe('GET /metadata', () => { /** * Invalid union dataset is not returned. - * But the child datasets should still be returned as long as they are valid on their own. + * But the child datasets should still be returned as long as they are valid on their own and have ReadDataset permission. */ - test('Invalid (Parent) Union dataset composed of only Src datasets', async () => { + test('Invalid Parent (Union) composed of only SRC does not invalidate children', async () => { const invalid_parent_dataset = Test_Uuids.Union_Datasets.TestDoublesUnionDataset; const child_datasets = [ Test_Uuids.Src_Datasets.TestDeviceDataset, @@ -307,7 +307,7 @@ describe('GET /metadata', () => { * > TestDoubleDeviceDataset - source dataset */ - test('Invalid (Parent) Union dataset composed of Session and Union', async () => { + test('Invalid Parent (Union) composed of Session and Union does NOT invalidate children', async () => { const invalid_parent_dataset = Test_Uuids.Union_Datasets.TestNestedUnionDataset; const child_datasets = [ @@ -364,7 +364,7 @@ describe('GET /metadata', () => { }, 30000); - test('Invalid (Parent) Session dataset composed of Union', async () => { + test('Invalid Parent (Session) dataset composed of (Union) child does NOT invalidate children', async () => { const invalid_parent_dataset = Test_Uuids.Session_Datasets.TestSessionAllDataset; const child_datasets = [Test_Uuids.Union_Datasets.TestUnionAllDataset]; @@ -419,7 +419,7 @@ describe('GET /metadata', () => { - test('Invalid Child (SRC) makes Parent (Union) invalid too', async () => { + test('Invalid Child (SRC) invalidates Parent (Union)', async () => { const invalid_child_dataset = Test_Uuids.Src_Datasets.TestDeviceDataset; const parent_dataset = Test_Uuids.Union_Datasets.TestUnionAllDataset; @@ -477,7 +477,7 @@ describe('GET /metadata', () => { - test.only('Invalid Child (Union) makes Parent (Session) invalid too', async () => { + test('Invalid Child (Union) invalidates Parent (Session)', async () => { const invalid_child_dataset = Test_Uuids.Union_Datasets.TestUnionAllDataset; const parent_dataset = Test_Uuids.Session_Datasets.TestSessionAllDataset; @@ -529,6 +529,65 @@ describe('GET /metadata', () => { expect(res).toHaveLength(0); }, 30000); + + + + test.only('Invalid Child (Session) invalidates Parent (Union)', async () => { + const invalid_child_dataset = Test_Uuids.Session_Datasets.SessionNode2DoubleDataset; + const valid_child_dataset = Test_Uuids.Union_Datasets.TestDoublesUnionDataset; + const parent_dataset = Test_Uuids.Union_Datasets.TestNestedUnionDataset; + + const datasets = [ + invalid_child_dataset, + parent_dataset, + valid_child_dataset + ]; + + // ---- add READ grants --- + const grants = []; + for (let uuid of datasets){ + const read_grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.ReadDataset, + uuid, + false + ); + grants.push(read_grant); + } + + await sleep(1000); + + + // ---- Make Union dataset invalid + await addConfig( + Constants.App.SparkplugSrc, + invalid_child_dataset, + { + "source": "invalid" + } + ); + + await sleep(500); + + + const res = await test_fplus.DataAccess.get_metadata_list(); + console.log(res); + // ---- Make Union dataset valid again + await removeConfig( + Constants.App.SparkplugSrc, + invalid_child_dataset + ); + + // ==== Revoke grants ==== + for (let uuid of grants){ + await deleteGrant(uuid); + } + + expect(res).not.toContain(parent_dataset); + expect(res).not.toContain(invalid_child_dataset); + expect(res).toContain(valid_child_dataset); + + }, 30000); }); From 990aeebf7387c0620f14e676f5f23e2d6f1b0479 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 3 Jun 2026 11:18:13 +0100 Subject: [PATCH 87/96] data access tests GET /metadata removed cache --- .../tests/http/metadata_list.test.js | 91 +++++++++++-------- 1 file changed, 51 insertions(+), 40 deletions(-) diff --git a/acs-data-access/tests/http/metadata_list.test.js b/acs-data-access/tests/http/metadata_list.test.js index 8d7e29de6..08f9b00a8 100644 --- a/acs-data-access/tests/http/metadata_list.test.js +++ b/acs-data-access/tests/http/metadata_list.test.js @@ -9,23 +9,21 @@ function sleep(ms) { } describe('GET /metadata', () => { - let test_fplus; - let admin_fplus; const TEST_PRINCIPAL=process.env.TEST_HUMAN_UUID; + let test_fplus; + let admin_fplus; let ALL_GRANTS = []; - beforeAll(async () => { + + beforeEach(async () => { admin_fplus = new ServiceClient({ directory_url: process.env.DIRECTORY_URL, username: process.env.ADMIN_USERNAME, password: process.env.ADMIN_PASSWORD, verbose: 'ALL' }); - }); - - beforeEach(async () => { test_fplus = new ServiceClient({ directory_url: process.env.DIRECTORY_URL, username: process.env.TEST_HUMAN_USERNAME, @@ -38,15 +36,18 @@ describe('GET /metadata', () => { // Clean up grants if not removed in case of failure. afterAll(async () => { - if(ALL_GRANTS.length > 0){ - for (let grant of ALL_GRANTS){ - await deleteGrant(grant); - } + for (let grant of ALL_GRANTS){ + await deleteGrant(grant); } }); async function addGrant(principal, permission, target, plural){ + if(!principal || + !permission || + !target + ) return; + const grant_uuid = await admin_fplus.Auth.add_grant({ principal, permission, @@ -58,15 +59,24 @@ describe('GET /metadata', () => { } async function deleteGrant(grant_uuid){ + if(!grant_uuid) return; + await admin_fplus.Auth.delete_grant(grant_uuid) ALL_GRANTS = ALL_GRANTS.filter(uuid => uuid !== grant_uuid); } async function addConfig(app, obj, config){ + if(!app || + !obj || + !config + ) return; + await admin_fplus.ConfigDB.put_config( app, obj, config ); } async function removeConfig(app, obj){ + if(!app || !obj) return; + await admin_fplus.ConfigDB.delete_config( app, obj ); } @@ -90,7 +100,6 @@ describe('GET /metadata', () => { const res = await test_fplus.DataAccess.get_metadata_list(); - await deleteGrant(grant_uuid); expect(res).toEqual([]); @@ -210,11 +219,13 @@ describe('GET /metadata', () => { // --------------------- - const invalid_dataset = datasets[0]; + const invalid_dataset = Test_Uuids.Src_Datasets.TestDeviceDataset; + const invalid_structure = Constants.App.UnionComponents; + // make dataset invalid await addConfig( - Constants.App.UnionComponents, + invalid_structure, invalid_dataset, [] ); @@ -227,7 +238,7 @@ describe('GET /metadata', () => { await sleep(1000); // ==== remove invalid config ====== - await removeConfig(Constants.App.SparkplugSrc, invalid_dataset); + await removeConfig(invalid_structure, invalid_dataset); await sleep(500); // ==== Revoke grants ==== @@ -238,12 +249,11 @@ describe('GET /metadata', () => { expect(res).not.toContain(invalid_dataset); }, 30000); - /** - * Invalid union dataset is not returned. - * But the child datasets should still be returned as long as they are valid on their own and have ReadDataset permission. - */ + test('Invalid Parent (Union) composed of only SRC does not invalidate children', async () => { const invalid_parent_dataset = Test_Uuids.Union_Datasets.TestDoublesUnionDataset; + const invalid_structure = Constants.App.SparkplugSrc; + const child_datasets = [ Test_Uuids.Src_Datasets.TestDeviceDataset, Test_Uuids.Src_Datasets.TestDoubleDeviceDataset @@ -271,7 +281,7 @@ describe('GET /metadata', () => { // ---- Make Union dataset invalid await addConfig( - Constants.App.SparkplugSrc, + invalid_structure, invalid_parent_dataset, { "source": Test_Uuids.Devices.Node2_TestDoubleDevice @@ -285,7 +295,7 @@ describe('GET /metadata', () => { // ---- Make Union dataset valid again await removeConfig( - Constants.App.SparkplugSrc, + invalid_structure, invalid_parent_dataset ); @@ -298,18 +308,12 @@ describe('GET /metadata', () => { }, 30000); - /** - * Union dataset composed of: - * > SessionNode2DoubleDataset: - * > Node2_TestDoubleDeviceDataset - source dataset - * > TestDoublesUnionDataset: - * > TestDeviceDataset - source dataset - * > TestDoubleDeviceDataset - source dataset - */ test('Invalid Parent (Union) composed of Session and Union does NOT invalidate children', async () => { const invalid_parent_dataset = Test_Uuids.Union_Datasets.TestNestedUnionDataset; + const invalid_structure = Constants.App.SparkplugSrc; + const child_datasets = [ Test_Uuids.Session_Datasets.SessionNode2DoubleDataset, Test_Uuids.Union_Datasets.TestDoublesUnionDataset @@ -337,7 +341,7 @@ describe('GET /metadata', () => { // ---- Make Union dataset invalid await addConfig( - Constants.App.SparkplugSrc, + invalid_structure, invalid_parent_dataset, { "source": Test_Uuids.Devices.Node2_TestDoubleDevice @@ -351,7 +355,7 @@ describe('GET /metadata', () => { // ---- Make Union dataset valid again await removeConfig( - Constants.App.SparkplugSrc, + invalid_structure, invalid_parent_dataset ); @@ -366,6 +370,8 @@ describe('GET /metadata', () => { test('Invalid Parent (Session) dataset composed of (Union) child does NOT invalidate children', async () => { const invalid_parent_dataset = Test_Uuids.Session_Datasets.TestSessionAllDataset; + const invalid_structure = Constants.App.SparkplugSrc; + const child_datasets = [Test_Uuids.Union_Datasets.TestUnionAllDataset]; const datasets = [ @@ -390,7 +396,7 @@ describe('GET /metadata', () => { // ---- Make Union dataset invalid await addConfig( - Constants.App.SparkplugSrc, + invalid_structure, invalid_parent_dataset, { "source": Test_Uuids.Devices.Node2_TestDoubleDevice @@ -403,7 +409,7 @@ describe('GET /metadata', () => { // ---- Make Union dataset valid again await removeConfig( - Constants.App.SparkplugSrc, + invalid_structure, invalid_parent_dataset ); @@ -421,6 +427,8 @@ describe('GET /metadata', () => { test('Invalid Child (SRC) invalidates Parent (Union)', async () => { const invalid_child_dataset = Test_Uuids.Src_Datasets.TestDeviceDataset; + const invalid_structure = Constants.App.SessionLimits; + const parent_dataset = Test_Uuids.Union_Datasets.TestUnionAllDataset; const datasets = [ @@ -445,7 +453,7 @@ describe('GET /metadata', () => { // ---- Make Union dataset invalid await addConfig( - Constants.App.SessionLimits, + invalid_structure, invalid_child_dataset, { "source": "invalid", @@ -461,7 +469,7 @@ describe('GET /metadata', () => { // ---- Make Union dataset valid again await removeConfig( - Constants.App.SessionLimits, + invalid_structure, invalid_child_dataset ); @@ -479,6 +487,7 @@ describe('GET /metadata', () => { test('Invalid Child (Union) invalidates Parent (Session)', async () => { const invalid_child_dataset = Test_Uuids.Union_Datasets.TestUnionAllDataset; + const invalid_structure = Constants.App.SparkplugSrc; const parent_dataset = Test_Uuids.Session_Datasets.TestSessionAllDataset; const datasets = [ @@ -503,7 +512,7 @@ describe('GET /metadata', () => { // ---- Make Union dataset invalid await addConfig( - Constants.App.SparkplugSrc, + invalid_structure, invalid_child_dataset, { "source": "invalid" @@ -517,7 +526,7 @@ describe('GET /metadata', () => { // ---- Make Union dataset valid again await removeConfig( - Constants.App.SparkplugSrc, + invalid_structure, invalid_child_dataset ); @@ -532,8 +541,10 @@ describe('GET /metadata', () => { - test.only('Invalid Child (Session) invalidates Parent (Union)', async () => { + test('Invalid Child (Session) invalidates Parent (Union)', async () => { const invalid_child_dataset = Test_Uuids.Session_Datasets.SessionNode2DoubleDataset; + const invalid_structure = Constants.App.SparkplugSrc; + const valid_child_dataset = Test_Uuids.Union_Datasets.TestDoublesUnionDataset; const parent_dataset = Test_Uuids.Union_Datasets.TestNestedUnionDataset; @@ -560,7 +571,7 @@ describe('GET /metadata', () => { // ---- Make Union dataset invalid await addConfig( - Constants.App.SparkplugSrc, + invalid_structure, invalid_child_dataset, { "source": "invalid" @@ -571,10 +582,10 @@ describe('GET /metadata', () => { const res = await test_fplus.DataAccess.get_metadata_list(); - console.log(res); + // ---- Make Union dataset valid again await removeConfig( - Constants.App.SparkplugSrc, + invalid_structure, invalid_child_dataset ); From a37525567ed15d7770ea532496b044fdc4f64329 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 3 Jun 2026 11:57:49 +0100 Subject: [PATCH 88/96] data access tests GET /structure --- .../tests/http/metadata_list.test.js | 10 +- .../tests/http/structure_list.test.js | 598 ++++++++++++++++++ 2 files changed, 603 insertions(+), 5 deletions(-) create mode 100644 acs-data-access/tests/http/structure_list.test.js diff --git a/acs-data-access/tests/http/metadata_list.test.js b/acs-data-access/tests/http/metadata_list.test.js index 08f9b00a8..df158bce4 100644 --- a/acs-data-access/tests/http/metadata_list.test.js +++ b/acs-data-access/tests/http/metadata_list.test.js @@ -30,7 +30,7 @@ describe('GET /metadata', () => { password: process.env.TEST_HUMAN_PASSWORD, verbose: 'ALL' }); - await sleep(1000); // + await sleep(500); }); @@ -85,7 +85,7 @@ describe('GET /metadata', () => { expect(res).toEqual([]); }); - test('User has unrelated permission to one dataset', async () => { + test('User has irrelevant permission to one dataset', async () => { const dataset_uuid = Test_Uuids.Src_Datasets.TestDeviceDataset; // Grant unrelated EditDataset permission to the user @@ -105,7 +105,7 @@ describe('GET /metadata', () => { expect(res).toEqual([]); }); - test('User has correct Read permission to one dataset', async () => { + test('User has relevant permission to one dataset', async () => { const dataset_uuid = Test_Uuids.Src_Datasets.TestDeviceDataset; // Grant correct permission @@ -125,7 +125,7 @@ describe('GET /metadata', () => { }); - test('User has correct permission to multiple datasets', async () => { + test('User has relevant permission to multiple datasets', async () => { const datasets = [ Test_Uuids.Src_Datasets.TestDeviceDataset, Test_Uuids.Session_Datasets.TestSessionAllDataset @@ -155,7 +155,7 @@ describe('GET /metadata', () => { expect(res).toEqual(expect.arrayContaining(datasets)); }); - test('Multiple datasets, some readable', async () => { + test('Multiple datasets, some have relevant permission', async () => { const datasets = { Readable: Test_Uuids.Src_Datasets.TestDeviceDataset, Unreadable: Test_Uuids.Session_Datasets.TestSessionAllDataset diff --git a/acs-data-access/tests/http/structure_list.test.js b/acs-data-access/tests/http/structure_list.test.js new file mode 100644 index 000000000..9a21009b5 --- /dev/null +++ b/acs-data-access/tests/http/structure_list.test.js @@ -0,0 +1,598 @@ +import {ServiceClient} from "@amrc-factoryplus/service-client"; +import process from "node:process"; +import {afterAll, beforeAll, beforeEach, describe, expect, test} from 'vitest'; +import { DataAccess as Constants } from "../../lib/constants.js"; +import {Test_Uuids} from '../temp_uuids.js'; + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +describe('GET /structure', () => { + const TEST_PRINCIPAL=process.env.TEST_HUMAN_UUID; + + let test_fplus; + let admin_fplus; + let ALL_GRANTS = []; + + beforeEach(async () => { + admin_fplus = new ServiceClient({ + directory_url: process.env.DIRECTORY_URL, + username: process.env.ADMIN_USERNAME, + password: process.env.ADMIN_PASSWORD, + verbose: 'ALL' + }); + + test_fplus = new ServiceClient({ + directory_url: process.env.DIRECTORY_URL, + username: process.env.TEST_HUMAN_USERNAME, + password: process.env.TEST_HUMAN_PASSWORD, + verbose: 'ALL' + }); + await sleep(500); + }); + + + // Clean up grants if not removed in case of failure. + afterAll(async () => { + for (let grant of ALL_GRANTS){ + await deleteGrant(grant); + } + }); + + + async function addGrant(principal, permission, target, plural){ + if(!principal || + !permission || + !target + ) return; + + const grant_uuid = await admin_fplus.Auth.add_grant({ + principal, + permission, + target, + plural + }); + ALL_GRANTS.push(grant_uuid); + return grant_uuid; + } + + async function deleteGrant(grant_uuid){ + if(!grant_uuid) return; + + await admin_fplus.Auth.delete_grant(grant_uuid) + ALL_GRANTS = ALL_GRANTS.filter(uuid => uuid !== grant_uuid); + } + + async function addConfig(app, obj, config){ + if(!app || + !obj || + !config + ) return; + + await admin_fplus.ConfigDB.put_config( app, obj, config ); + } + + async function removeConfig(app, obj){ + if(!app || !obj) return; + + await admin_fplus.ConfigDB.delete_config( app, obj ); + } + + test('Datasets exist but user has no permissions', async () => { + const res = await test_fplus.DataAccess.get_structure_list(); + expect(res).toEqual([]); + }); + + test('User has unrelated permission to one dataset', async () => { + const dataset_uuid = Test_Uuids.Src_Datasets.TestDeviceDataset; + + // Grant unrelated permission to the user + const grant_uuid = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.ReadDataset, + dataset_uuid, + false + ); + await sleep(1000); + + const res = await test_fplus.DataAccess.get_structure_list(); + + await deleteGrant(grant_uuid); + + expect(res).toEqual([]); + }); + + test('User has correct permission to one dataset', async () => { + const dataset_uuid = Test_Uuids.Src_Datasets.TestDeviceDataset; + + // Grant correct permission + const grant_uuid = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.EditDataset, + dataset_uuid, + false + ); + await sleep(1000); + + const res = await test_fplus.DataAccess.get_structure_list(); + + await deleteGrant(grant_uuid); + + expect(res).toContain(dataset_uuid); + + }); + + test('User has correct permission to multiple datasets', async () => { + const datasets = [ + Test_Uuids.Src_Datasets.TestDeviceDataset, + Test_Uuids.Session_Datasets.TestSessionAllDataset, + ]; + + + // Add grants + let grants = []; + + for (let dataset_uuid of datasets){ + const grant_uuid = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.EditDataset, + dataset_uuid, + false + ) + grants.push(grant_uuid); + } + + await sleep(1000); + + const res = await test_fplus.DataAccess.get_structure_list(); + + // Revoke grants + for(let grant of grants){ + await deleteGrant(grant); + } + expect(res).toHaveLength(datasets.length); + expect(res).toEqual(expect.arrayContaining(datasets)); + }); + + test('Multiple datasets, some have relevant permission', async () => { + const datasets = { + Relevant: Test_Uuids.Src_Datasets.TestDeviceDataset, + Irrelevant: Test_Uuids.Session_Datasets.TestSessionAllDataset + }; + + // ======== add grants ====== + let grants = []; + const relevant_grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.EditDataset, + datasets.Relevant, + false + ); + grants.push(relevant_grant); + + const irrelevant_grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.ReadDataset, + datasets.Irrelevant, + false + ); + grants.push(irrelevant_grant); + + await sleep(1000); + + // ========================= + + + const res = await test_fplus.DataAccess.get_structure_list(); + + // Revoke grants + for(let grant of grants){ + await deleteGrant(grant); + } + expect(res).toHaveLength(1); + expect(res).toContain(datasets.Relevant); + expect(res).not.toContain(datasets.Irrelevant); + }); + + + test('Multiple (simple SRC) datasets with relevant permissions, invalid ones must be invcluded', async () => { + const datasets = [ + Test_Uuids.Src_Datasets.TestDeviceDataset, + Test_Uuids.Src_Datasets.Node2_TestDoubleDeviceDataset, + Test_Uuids.Src_Datasets.Node2_TestFloutDeviceDataset + ]; + + + // ----- add grants --- + const grants = []; + for (let uuid of datasets){ + const relevant_grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.EditDataset, + uuid, + false + ); + grants.push(relevant_grant); + } + + await sleep(1000); + + // --------------------- + + + const invalid_dataset = Test_Uuids.Src_Datasets.TestDeviceDataset; + const invalid_structure = Constants.App.UnionComponents; + + + // make dataset invalid + await addConfig( + invalid_structure, + invalid_dataset, + [] + ); + await sleep(1000); + + // ========================= + + const res = await test_fplus.DataAccess.get_structure_list(); + + await sleep(1000); + + // ==== remove invalid config ====== + await removeConfig(invalid_structure, invalid_dataset); + await sleep(500); + + // ==== Revoke grants ==== + for (let uuid of grants){ + await deleteGrant(uuid); + } + expect(res).toHaveLength(datasets.length); + expect(res).toEqual(expect.arrayContaining(datasets)); + }, 30000); + + + test('Invalid Parent (Union) composed of only SRC - all included', async () => { + const invalid_parent_dataset = Test_Uuids.Union_Datasets.TestDoublesUnionDataset; + const invalid_structure = Constants.App.SparkplugSrc; + + const child_datasets = [ + Test_Uuids.Src_Datasets.TestDeviceDataset, + Test_Uuids.Src_Datasets.TestDoubleDeviceDataset + ]; + + const datasets = [ + invalid_parent_dataset, + ...child_datasets + ]; + + // ---- add grants --- + const grants = []; + for (let uuid of datasets){ + const relevant_grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.EditDataset, + uuid, + false + ); + grants.push(relevant_grant); + } + + await sleep(1000); + + + // ---- Make Union dataset invalid + await addConfig( + invalid_structure, + invalid_parent_dataset, + { + "source": Test_Uuids.Devices.Node2_TestDoubleDevice + } + ); + + await sleep(500); + + + const res = await test_fplus.DataAccess.get_structure_list(); + + // ---- Make Union dataset valid again + await removeConfig( + invalid_structure, + invalid_parent_dataset + ); + + // ==== Revoke grants ==== + for (let uuid of grants){ + await deleteGrant(uuid); + } + expect(res).toHaveLength(datasets.length); + expect(res).toEqual(expect.arrayContaining(datasets)); + + }, 30000); + + test('Invalid Parent (Union) composed of Session and Union - all included', async () => { + + const invalid_parent_dataset = Test_Uuids.Union_Datasets.TestNestedUnionDataset; + const invalid_structure = Constants.App.SparkplugSrc; + + const child_datasets = [ + Test_Uuids.Session_Datasets.SessionNode2DoubleDataset, + Test_Uuids.Union_Datasets.TestDoublesUnionDataset + ]; + + const datasets = [ + invalid_parent_dataset, + ...child_datasets + ]; + + // ---- add grants --- + const grants = []; + for (let uuid of datasets){ + const relevant_grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.EditDataset, + uuid, + false + ); + grants.push(relevant_grant); + } + + await sleep(1000); + + + // ---- Make Union dataset invalid + await addConfig( + invalid_structure, + invalid_parent_dataset, + { + "source": Test_Uuids.Devices.Node2_TestDoubleDevice + } + ); + + await sleep(500); + + + const res = await test_fplus.DataAccess.get_structure_list(); + + // ---- Make Union dataset valid again + await removeConfig( + invalid_structure, + invalid_parent_dataset + ); + + // ==== Revoke grants ==== + for (let uuid of grants){ + await deleteGrant(uuid); + } + expect(res).toHaveLength(datasets.length); + expect(res).toEqual(expect.arrayContaining(datasets)); + }, 30000); + + + test('Invalid Parent (Session) dataset composed of (Union) - all included', async () => { + const invalid_parent_dataset = Test_Uuids.Session_Datasets.TestSessionAllDataset; + const invalid_structure = Constants.App.SparkplugSrc; + + const child_datasets = [Test_Uuids.Union_Datasets.TestUnionAllDataset]; + + const datasets = [ + invalid_parent_dataset, + ...child_datasets + ]; + + // ---- add grants --- + const grants = []; + for (let uuid of datasets){ + const relevant_grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.EditDataset, + uuid, + false + ); + grants.push(relevant_grant); + } + + await sleep(1000); + + + // ---- Make Union dataset invalid + await addConfig( + invalid_structure, + invalid_parent_dataset, + { + "source": Test_Uuids.Devices.Node2_TestDoubleDevice + } + ); + + await sleep(500); + + const res = await test_fplus.DataAccess.get_structure_list(); + + // ---- Make Union dataset valid again + await removeConfig( + invalid_structure, + invalid_parent_dataset + ); + + // ==== Revoke grants ==== + for (let uuid of grants){ + await deleteGrant(uuid); + } + + expect(res).toHaveLength(datasets.length); + expect(res).toEqual(expect.arrayContaining(datasets)); + + }, 30000); + + test('Invalid Child (SRC) invalidates Parent (Union) - all included', async () => { + const invalid_child_dataset = Test_Uuids.Src_Datasets.TestDeviceDataset; + const invalid_structure = Constants.App.SessionLimits; + + const parent_dataset = Test_Uuids.Union_Datasets.TestUnionAllDataset; + + const datasets = [ + invalid_child_dataset, + parent_dataset + ]; + + // ---- add grants --- + const grants = []; + for (let uuid of datasets){ + const relevant_grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.EditDataset, + uuid, + false + ); + grants.push(relevant_grant); + } + + await sleep(1000); + + + // ---- Make Union dataset invalid + await addConfig( + invalid_structure, + invalid_child_dataset, + { + "source": "invalid", + "from": "invalid", + "to": "invalid" + } + ); + + await sleep(500); + + + const res = await test_fplus.DataAccess.get_structure_list(); + + // ---- Make Union dataset valid again + await removeConfig( + invalid_structure, + invalid_child_dataset + ); + + // ==== Revoke grants ==== + for (let uuid of grants){ + await deleteGrant(uuid); + } + + expect(res).toHaveLength(datasets.length); + expect(res).toEqual(expect.arrayContaining(datasets)); + + }, 30000); + + test('Invalid Child (Union) invalidates Parent (Session) - all included', async () => { + const invalid_child_dataset = Test_Uuids.Union_Datasets.TestUnionAllDataset; + const invalid_structure = Constants.App.SparkplugSrc; + const parent_dataset = Test_Uuids.Session_Datasets.TestSessionAllDataset; + + const datasets = [ + invalid_child_dataset, + parent_dataset + ]; + + // ---- add grants --- + const grants = []; + for (let uuid of datasets){ + const relevant_grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.EditDataset, + uuid, + false + ); + grants.push(relevant_grant); + } + + await sleep(1000); + + + // ---- Make Union dataset invalid + await addConfig( + invalid_structure, + invalid_child_dataset, + { + "source": "invalid" + } + ); + + await sleep(500); + + + const res = await test_fplus.DataAccess.get_structure_list(); + + // ---- Make Union dataset valid again + await removeConfig( + invalid_structure, + invalid_child_dataset + ); + + // ==== Revoke grants ==== + for (let uuid of grants){ + await deleteGrant(uuid); + } + + expect(res).toHaveLength(datasets.length); + expect(res).toEqual(expect.arrayContaining(datasets)); + + }, 30000); + + + test('Invalid Child (Session) invalidates Parent (Union)', async () => { + const invalid_child_dataset = Test_Uuids.Session_Datasets.SessionNode2DoubleDataset; + const invalid_structure = Constants.App.SparkplugSrc; + + const valid_child_dataset = Test_Uuids.Union_Datasets.TestDoublesUnionDataset; + const parent_dataset = Test_Uuids.Union_Datasets.TestNestedUnionDataset; + + const datasets = [ + invalid_child_dataset, + parent_dataset, + valid_child_dataset + ]; + + // ---- add grants --- + const grants = []; + for (let uuid of datasets){ + const relevant_grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.EditDataset, + uuid, + false + ); + grants.push(relevant_grant); + } + + await sleep(1000); + + + // ---- Make Union dataset invalid + await addConfig( + invalid_structure, + invalid_child_dataset, + { + "source": "invalid" + } + ); + + await sleep(500); + + + const res = await test_fplus.DataAccess.get_structure_list(); + + // ---- Make Union dataset valid again + await removeConfig( + invalid_structure, + invalid_child_dataset + ); + + // ==== Revoke grants ==== + for (let uuid of grants){ + await deleteGrant(uuid); + } + + expect(res).toHaveLength(datasets.length); + expect(res).toEqual(expect.arrayContaining(datasets)); + + }, 30000); +}); \ No newline at end of file From ae4965d5106278457fde60ab8654500d8994469c Mon Sep 17 00:00:00 2001 From: amrc-za Date: Wed, 3 Jun 2026 14:28:00 +0100 Subject: [PATCH 89/96] data access webapi added config validation --- acs-data-access/lib/api-v1.js | 45 +++++++++++++++++++++++++++++++-- acs-data-access/lib/validate.js | 14 ++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 4a46b9080..0b8ddb5ba 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -11,7 +11,7 @@ import { APIError } from "@amrc-factoryplus/service-api"; import { ServiceError } from "@amrc-factoryplus/service-client"; import { retryBackoff } from "@amrc-factoryplus/rx-util"; import { DataAccess as Constants } from "./constants.js"; -import { valid_uuid } from "./validate.js"; +import { valid_uuid, valid_datetime } from "./validate.js"; import { csv_escape, maxDate, minDate } from './utils.js'; @@ -455,13 +455,54 @@ export class APIv1 { * @param {*} res * @returns new dataset's UUID - JSON string */ + + _validate_config(config, structure){ + if(structure === Constants.App.SparkplugSrc){ + const source = config.source; + if(!source) + return fail(this.log, 422, `req.body.config for SparkplugSrc structure type ${structure} must contain "source" field.`); + + if(!valid_uuid(source)) + return fail(this.log, 422, `req.body.config.source uuid is invalid.`); + + } + else if(structure === Constants.App.SessionLimits){ + const {to, from, source} = config; + + if(!to || !from || !source) + return fail(this.log, 422, `req.body.config for SessionLimits type must contain "source", "to" and "from" fields.`); + + if(!valid_uuid(source)) + return fail(this.log, 422, `req.body.config.source uuid is invalid.`); + + if(!valid_datetime(to)) + return fail(this.log, 422, `req.body.config.to datetime format is invalid.`); + + if(!valid_datetime(from)) + return fail(this.log, 422, `req.body.config.from datetime format is invalid.`); + } + else if(structure === Constants.App.UnionComponents){ + if(!Array.isArray(config)) + return fail(this.log, 422, `req.body.config must be array []`); + + for(let src of config){ + if (!valid_uuid(src)) + return fail(this.log, 422. `${src} is invalid UUID`); + } + } + else{ + return fail(this.log, 404, `Unknown structure type ${structure}`); + } + } + async structure_create(req, res){ const {structure, config} = req.body; if(!structure) return fail(this.log, 422, `Structure not provided in request body.`); if(!valid_uuid(structure)) return fail(this.log, 422, `Structure uuid ${structure} is invalid.`); - if(!config) return fail(this.log, 422, `Config not provided in request body.`) + if(!config) return fail(this.log, 422, `Config not provided in request body.`); + this._validate_config(config, structure); const ok = await this.auth.check_acl( req.auth, diff --git a/acs-data-access/lib/validate.js b/acs-data-access/lib/validate.js index 6e8b44194..e2e74470b 100644 --- a/acs-data-access/lib/validate.js +++ b/acs-data-access/lib/validate.js @@ -33,4 +33,18 @@ export function valid_grant (grant) { if (!valid_uuid(grant.permission)) return false; if (!valid_uuid(grant.target)) return false; return (grant.plural === true || grant.plural === false); +} + + +export function valid_datetime(datetime_str){ + // 1. Check the exact string format using RegEx + const regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/; + if (!regex.test(datetime_str)) return false; + + // 2. Check if the calendar date and time are physically valid + const timestamp = Date.parse(datetime_str); + if (isNaN(timestamp)) return false; + + // 3. Ensure it matches the original string (prevents date rolling like Feb 30 -> Mar 2) + return new Date(timestamp).toISOString() === datetime_str; } \ No newline at end of file From 4eb6a7199cef837148949a669195e33ec9741b53 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 4 Jun 2026 09:24:45 +0100 Subject: [PATCH 90/96] data-access tests structure creaate --- .../tests/http/structure_create.test.js | 228 ++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 acs-data-access/tests/http/structure_create.test.js diff --git a/acs-data-access/tests/http/structure_create.test.js b/acs-data-access/tests/http/structure_create.test.js new file mode 100644 index 000000000..92c899ec5 --- /dev/null +++ b/acs-data-access/tests/http/structure_create.test.js @@ -0,0 +1,228 @@ +import {ServiceClient} from "@amrc-factoryplus/service-client"; +import process from "node:process"; +import {afterAll, beforeAll, beforeEach, describe, expect, test} from 'vitest'; +import { DataAccess as Constants } from "../../lib/constants.js"; +import * as Temp_Data from '../temp_data.js'; + + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +describe('POST /structure', () => { + const TEST_PRINCIPAL=process.env.TEST_HUMAN_UUID; + + let test_fplus; + let admin_fplus; + let ALL_GRANTS = []; + + const ALL_INVALID_BODY_CASES = [ + ...Temp_Data.Invalid_Body_Structure, + ...Temp_Data.Invalid_Body_Config.SRC, + ...Temp_Data.Invalid_Body_Config.Session, + ...Temp_Data.Invalid_Body_Config.Union, + ]; + + const ALL_VALID_BODY_CASES = [ + Temp_Data.Valid_Body.SRC, + Temp_Data.Valid_Body.Session, + Temp_Data.Valid_Body.Union + ]; + + beforeEach(async () => { + admin_fplus = new ServiceClient({ + directory_url: process.env.DIRECTORY_URL, + username: process.env.ADMIN_USERNAME, + password: process.env.ADMIN_PASSWORD, + verbose: 'ALL' + }); + + test_fplus = new ServiceClient({ + directory_url: process.env.DIRECTORY_URL, + username: process.env.TEST_HUMAN_USERNAME, + password: process.env.TEST_HUMAN_PASSWORD, + verbose: 'ALL' + }); + await sleep(1000); + }); + + + // Clean up grants if not removed in case of failure. + afterAll(async () => { + for (let grant of ALL_GRANTS){ + await deleteGrant(grant); + } + }); + + + async function addGrant(principal, permission, target, plural){ + if(!principal || + !permission || + !target + ) return; + + const grant_uuid = await admin_fplus.Auth.add_grant({ + principal, + permission, + target, + plural + }); + ALL_GRANTS.push(grant_uuid); + return grant_uuid; + } + + async function deleteGrant(grant_uuid){ + if(!grant_uuid) return; + + await admin_fplus.Auth.delete_grant(grant_uuid) + ALL_GRANTS = ALL_GRANTS.filter(uuid => uuid !== grant_uuid); + } + + async function deleteConfigDBObj(obj_uuid){ + if(!obj_uuid) return; + + await admin_fplus.ConfigDB.delete_object(obj_uuid); + } + + + + test('User has no CreateDataset permission', async () => { + try { + await test_fplus.DataAccess.create_dataset( + Temp_Data.Valid_Body.SRC.structure, + Temp_Data.Valid_Body.SRC.config + ); + + expect.fail('Expected create_dataset to throw'); + } + catch (err) { + expect(err.status).not.toBe(200); + } + }); + + test('User has CreateDataset permission for irrelevant structure', async () => { + const irrelevant_structure = Constants.App.SessionLimits; + + const irrelevant_grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.CreateDataset, + irrelevant_structure, + false + ); + await sleep(500); + + try { + await test_fplus.DataAccess.create_dataset( + Temp_Data.Valid_Body.Session.structure, + Temp_Data.Valid_Body.Session.config + ); + + expect.fail('Expected create_dataset to throw'); + } + catch (err) { + expect(err.status).not.toBe(200); + } + + await deleteGrant(irrelevant_grant); + }); + + + + test.each(ALL_INVALID_BODY_CASES)('Invalid body cases - case %#', async (testCase) => { + try { + + await test_fplus.DataAccess.create_dataset(testCase.structure, testCase.config); + expect.fail('Expected create_dataset to throw'); + + } catch (err) { + + expect(err.status).not.toBe(200); + } + }); + + test.each(ALL_VALID_BODY_CASES)('Missing 2nd level permission cases - case %#', async (testCase) => { + try{ + // Add 1st level grant + + const grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.CreateDataset, + testCase.structure, + false + ); + + await test_fplus.DataAccess.create_dataset(testCase.structure, testCase.config); + + + expect.fail('Expected create_dataset to throw'); + + // Revoke 1st level grant + await deleteGrant(grant); + } catch(err){ + console.log(err); + expect(err.status).not.toBe(200); + } + }); + + + test.only.each(ALL_VALID_BODY_CASES)('Create - case %#', async (testCase) => { + + let grants = []; + + // add 1st level grant + const grant_uuid = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.CreateDataset, + test.structure, + false + ); + grants.push(grant_uuid); + + await sleep(500); + + + if(testCase.structure === Constants.App.UnionComponents){ + for(let src of testCase.config){ + const g = await addGrant( + TEST_PRINCIPAL, + testCase.secondLevelPerm, + src, + false + ); + grants.push(g); + await sleep(1000); + } + }else{ + const g = await addGrant( + TEST_PRINCIPAL, + testCase.secondLevelPerm, + testCase.config.source, + false + ); + grants.push(g); + } + + + await sleep(1000); + + const obj_uuid = await test_fplus.DataAccess.create_dataset( + testCase.structure, + testCase.config + ); + + console.log("HERE IS THE NEW OBJECT CREATED: ", obj_uuid); + + for(let grant of grants){ + await deleteGrant(grant); + await sleep(1000); + } + + await deleteConfigDBObj(obj_uuid); + await sleep(1000); + + expect(obj_uuid).not.toBe(undefined); + }, 30000); + + + +}); \ No newline at end of file From 8c5033c37cf646f59a137e25c11fde6bb0361fad Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 4 Jun 2026 09:26:07 +0100 Subject: [PATCH 91/96] data access tests temp data --- .../tests/http/metadata_list.test.js | 2 +- .../tests/http/structure_list.test.js | 2 +- acs-data-access/tests/temp_data.js | 199 ++++++++++++++++++ 3 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 acs-data-access/tests/temp_data.js diff --git a/acs-data-access/tests/http/metadata_list.test.js b/acs-data-access/tests/http/metadata_list.test.js index df158bce4..bba4318b2 100644 --- a/acs-data-access/tests/http/metadata_list.test.js +++ b/acs-data-access/tests/http/metadata_list.test.js @@ -2,7 +2,7 @@ import {ServiceClient} from "@amrc-factoryplus/service-client"; import process from "node:process"; import {afterAll, beforeAll, beforeEach, describe, expect, test} from 'vitest'; import { DataAccess as Constants } from "../../lib/constants.js"; -import {Test_Uuids} from '../temp_uuids.js'; +import {Test_Uuids} from '../temp_data.js'; function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); diff --git a/acs-data-access/tests/http/structure_list.test.js b/acs-data-access/tests/http/structure_list.test.js index 9a21009b5..ac57017d1 100644 --- a/acs-data-access/tests/http/structure_list.test.js +++ b/acs-data-access/tests/http/structure_list.test.js @@ -2,7 +2,7 @@ import {ServiceClient} from "@amrc-factoryplus/service-client"; import process from "node:process"; import {afterAll, beforeAll, beforeEach, describe, expect, test} from 'vitest'; import { DataAccess as Constants } from "../../lib/constants.js"; -import {Test_Uuids} from '../temp_uuids.js'; +import {Test_Uuids} from '../temp_data.js'; function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); diff --git a/acs-data-access/tests/temp_data.js b/acs-data-access/tests/temp_data.js new file mode 100644 index 000000000..96ce6aabb --- /dev/null +++ b/acs-data-access/tests/temp_data.js @@ -0,0 +1,199 @@ +import { config } from "rxjs"; +import { DataAccess as Constants } from "../lib/constants.js"; + +export const Test_Uuids = { + Devices: { + TestDevice: "5880686f-13f9-4089-b236-825d002bc911", + TestDoubleDevice: "1298e74a-37d3-4717-aba1-4c1e67b953c1", + TestFloatDevice: "34b6910c-85ec-445b-9071-0813891e6ff7", + + Node2_TestDoubleDevice: "d60d00bf-eae7-4f88-9ad2-cfc4c638ac7b", + Node2_TestFloutDevice: "961cb1c0-3d00-44e8-90bc-3fdf917746fc" + }, + + Src_Datasets: { + Node2_TestDoubleDeviceDataset: "720ecd5a-c5d2-49d5-bf5a-8ca01dfdb7df", + Node2_TestFloutDeviceDataset: "9975fb51-ed1c-4bc9-a845-331c801f140f", + TestDeviceDataset: "492194aa-4ced-44d1-96c1-4e09e4d52f45", + TestDoubleDeviceDataset: "3af79d1f-bdc2-41a8-bf2b-bd16af8a92a2", + TestFloatDeviceDataset: "3dc230e9-ab36-4729-8d93-6d5c421f7d7f", + }, + + Union_Datasets: { + TestDoublesUnionDataset: "f0f1da5d-7a9d-431a-afd1-eec467ed21cb", + TestNestedUnionDataset: "21e0dfa8-f044-4e87-8606-f528686205d8", + TestUnionAllDataset: "e2a4c530-dc0f-417d-b00b-329b0e90e033", + }, + + Session_Datasets: { + SessionNode2DoubleDataset: "811da559-727b-4808-8d99-c31f90520966", + TestSessionAllDataset: "74cac8a1-88a6-4b08-b297-a3c05a388ec5", + } +} + +export const Valid_Body = { + SRC: { + structure: Constants.App.SparkplugSrc, + config: { + source: Test_Uuids.Devices.Node2_TestDoubleDevice + }, + secondLevelPerm: Constants.Perm.UseSparkplug + }, + + Session: { + structure: Constants.App.SessionLimits, + config: { + source: Test_Uuids.Src_Datasets.Node2_TestDoubleDeviceDataset, + from: "2026-06-01T08:52:00.000Z", + to: "2026-06-01T11:55:00.000Z", + }, + secondLevelPerm: Constants.Perm.UseForSession + }, + + Union: { + structure: Constants.App.UnionComponents, + config: [ + Test_Uuids.Session_Datasets.SessionNode2DoubleDataset, + Test_Uuids.Union_Datasets.TestDoublesUnionDataset + ], + secondLevelPerm: Constants.Perm.IncludeInUnion + } +} + + +export const Invalid_Body_Structure = [ + // Invalid_No_Structure + { + config: { + source: Test_Uuids.Src_Datasets.Node2_TestDoubleDeviceDataset + } + }, + + // Invalid_Bad_Structure_Uuid + { + structure: 'xxx', + config: { + source: Test_Uuids.Src_Datasets.Node2_TestDoubleDeviceDataset + } + }, + + // Invalid_Unhandled_Structure_Type + { + structure: '74cac8a1-88a6-4b08-b297-a3c05a388ec5', + config: { + source: Test_Uuids.Src_Datasets.Node2_TestDoubleDeviceDataset + } + } +] + +export const Invalid_Body_Config = { + SRC:[ + // no config + { + structure: Constants.App.SparkplugSrc + }, + + // Invalid_No_Source + { + structure: Constants.App.SparkplugSrc, + config: {} + }, + + // Invalid_Bad_Source_Uuid for SRC + { + structure: Constants.App.SparkplugSrc, + config: { + source: 'xxx' + } + }, + + // Invalid_No_Config for SRC + { + structure: Constants.App.SparkplugSrc, + undefined + }, + + // Invalid_Config_Format for SRC + { + structure: Constants.App.SparkplugSrc, + config: [] + }, + + // Invalid_Bad_Child_Uuid for SRC + { + structure: Constants.App.SparkplugSrc, + config: { + source: "xxx" + } + } + ], + Session:[ + // Invalid_No_Source: + { + structure: Constants.App.SessionLimits, + config: { + from: "2026-06-01T08:52:00.000Z", + to: "2026-06-01T11:55:00.000Z", + } + }, + + // Invalid_No_From: + { + structure: Constants.App.SessionLimits, + config: { + source: Test_Uuids.Src_Datasets.Node2_TestDoubleDeviceDataset, + to: "2026-06-01T11:55:00.000Z", + } + }, + + // Invalid_No_To: + { + structure: Constants.App.SessionLimits, + config: { + source: Test_Uuids.Src_Datasets.Node2_TestDoubleDeviceDataset, + from: "2026-06-01T08:52:00.000Z", + } + }, + + // Invalid config format + { + structure: Constants.App.SessionLimits, + config: [] + }, + + // Empty config + { + structure: Constants.App.SessionLimits, + config: {} + }, + + // No config + { + structure: Constants.App.SessionLimits + }, + ], + Union: [ + // no config + { + structure: Constants.App.UnionComponents, + }, + + // config wrong format + { + structure: Constants.App.UnionComponents, + config: {} + }, + + // invalid source uuid + { + structure: Constants.App.UnionComponents, + config: [ + Test_Uuids.Session_Datasets.SessionNode2DoubleDataset, + "xxx", + Test_Uuids.Session_Datasets.TestSessionAllDataset, + ] + }, + ], +} + + From 98a6f6408413c58c50de34f8bea8e84c71315cc7 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 4 Jun 2026 12:53:28 +0100 Subject: [PATCH 92/96] data access added delete endpoint --- acs-data-access/lib/api-v1.js | 40 +++++++++++++++++++ acs-data-access/lib/constants.js | 1 + .../lib/service/data-access.js | 14 +++++-- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/acs-data-access/lib/api-v1.js b/acs-data-access/lib/api-v1.js index 0b8ddb5ba..f0ca15c13 100644 --- a/acs-data-access/lib/api-v1.js +++ b/acs-data-access/lib/api-v1.js @@ -50,9 +50,47 @@ export class APIv1 { .get(this.structure_uuid.bind(this)) .put(this.structure_update.bind(this)); + + api.route("/delete/:uuid") + .get(this.delete_dataset.bind(this)); + return api; } + async delete_dataset(req, res){ + const dataset_uuid = req.params.uuid; + if(!dataset_uuid) return this.fail(this.log, 422, `No req.params.uuid`); + if(!valid_uuid(dataset_uuid)) return this.fail(this.log, 422, `Invalid uuid ${dataset_uuid}`); + + const ok = await this.auth.check_acl( + req.auth, + Constants.Perm.DeleteDataset, + dataset_uuid, + true, + ); + + if (!ok) return fail(this.log, 403, `You don't have DELETE permissions for ${dataset_uuid}`); + + + this.log(`Data Access /delete/${dataset_uuid} is called`); + + // remove all subclass relationships + const subclasses = await this.cdb.class_subclasses(dataset_uuid); + for(let s of subclasses){ + await this.cdb.class_remove_subclass(dataset_uuid, s); + } + + // delete the dataset object + try{ + await this.cdb.delete_object(dataset_uuid); + }catch(err){ + this.log("CANT DELETE CONFIGDB OBJECT"); + this.log(err); + } + + return res.status(200).json(dataset_uuid); + } + /** GET. Returns a list of Dataset UUIDs that the client has READ_DATASET access to. * The dataset can be optionally restricted by from and to dates (this is not implemented yet). * Dates must be in the format ISO date-time string in UTC 2025-11-13T09:33:18.000Z @@ -547,9 +585,11 @@ export class APIv1 { const structure = req.body.structure; if(!structure) return fail(this.log, 422, `Structure not provided`); + if(!valid_uuid(structure)) return fail(this.log, 422, `Structure uuid is invalid`); const new_config = req.body.config; if(!new_config) return fail(this.log, 422, `Config not provided`); + this._validate_config(new_config, structure); const ok = await this.auth.check_acl( req.auth, diff --git a/acs-data-access/lib/constants.js b/acs-data-access/lib/constants.js index 6fbb957cb..7172c5147 100644 --- a/acs-data-access/lib/constants.js +++ b/acs-data-access/lib/constants.js @@ -47,6 +47,7 @@ export const DataAccess = { ReadDataset: "ec48462e-37eb-4f56-8efa-83d813e85559", UseForSession: "c089b9a9-06cd-4211-94fc-9ad52a759987", EditDataset: "af06b9e5-456a-43e4-b636-5b17de28fc7f", + DeleteDataset: "6f301df8-0ad1-496f-8391-8de92c43ad8e", UseSparkplug: "788b049c-2831-11f1-99fd-2b0bf86d6f77", } } \ No newline at end of file diff --git a/lib/js-service-client/lib/service/data-access.js b/lib/js-service-client/lib/service/data-access.js index 37b9b54f4..9aedac0c9 100644 --- a/lib/js-service-client/lib/service/data-access.js +++ b/lib/js-service-client/lib/service/data-access.js @@ -23,6 +23,14 @@ export class DataAccess extends ServiceInterface{ this.log = fplus.debug.bound("data-access"); } + async delete_dataset(uuid){ + const [st, json] = await this.fetch(`v1/delete/${uuid}`); + if(st != 200) + this.throw (`Can't delete dataset ${uuid}`, st, json); + + return json; + } + /** * Returns list of dataset uuids with READ_DATASET permission. */ @@ -54,9 +62,9 @@ export class DataAccess extends ServiceInterface{ * Download dataset CSV stream * Returns a readable stream */ -/** - * Returns a stream of dataset CSV data. - */ + /** + * Returns a stream of dataset CSV data. + */ async download_data(uuid, output_dir = ".") { const [st, stream, _, headers] = await this.fetch({ url: `v1/data/${uuid}`, From a90ee084bbfc680cf586a9f9d59d5eac7fe99442 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Thu, 4 Jun 2026 12:55:28 +0100 Subject: [PATCH 93/96] data access test create added subclass relationship tests --- .../tests/http/structure_create.test.js | 122 ++++++++++++++++-- 1 file changed, 109 insertions(+), 13 deletions(-) diff --git a/acs-data-access/tests/http/structure_create.test.js b/acs-data-access/tests/http/structure_create.test.js index 92c899ec5..bf2d4c396 100644 --- a/acs-data-access/tests/http/structure_create.test.js +++ b/acs-data-access/tests/http/structure_create.test.js @@ -78,10 +78,10 @@ describe('POST /structure', () => { ALL_GRANTS = ALL_GRANTS.filter(uuid => uuid !== grant_uuid); } - async function deleteConfigDBObj(obj_uuid){ - if(!obj_uuid) return; + async function deleteDataset(uuid){ + if(!uuid) return; - await admin_fplus.ConfigDB.delete_object(obj_uuid); + await admin_fplus.DataAccess.delete_dataset(uuid); } @@ -165,7 +165,7 @@ describe('POST /structure', () => { }); - test.only.each(ALL_VALID_BODY_CASES)('Create - case %#', async (testCase) => { + test.each(ALL_VALID_BODY_CASES)('Create - case %#', async (testCase) => { let grants = []; @@ -173,7 +173,7 @@ describe('POST /structure', () => { const grant_uuid = await addGrant( TEST_PRINCIPAL, Constants.Perm.CreateDataset, - test.structure, + testCase.structure, false ); grants.push(grant_uuid); @@ -200,29 +200,125 @@ describe('POST /structure', () => { false ); grants.push(g); + await sleep(1000); } - - await sleep(1000); - - const obj_uuid = await test_fplus.DataAccess.create_dataset( + const dataset_uuid = await test_fplus.DataAccess.create_dataset( testCase.structure, testCase.config ); - console.log("HERE IS THE NEW OBJECT CREATED: ", obj_uuid); - + console.log("HERE IS THE NEW OBJECT CREATED: ", dataset_uuid); + for(let grant of grants){ await deleteGrant(grant); await sleep(1000); } - await deleteConfigDBObj(obj_uuid); + // await deleteConfigDBObj(obj_uuid); + await deleteDataset(dataset_uuid); await sleep(1000); - expect(obj_uuid).not.toBe(undefined); + expect(dataset_uuid).not.toBe(undefined); }, 30000); + // /* =============================================== */ + // /* -------- Test subclass relationships ---------- */ + // /* =============================================== */ + test('Session dataset becomes subclass of its source dataset', async () => { + const testCase = Temp_Data.Valid_Body.Session; + // 1. grants: CreateDataset and IncludeInSession for src + let grants = []; + const create_grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.CreateDataset, + testCase.structure, + false + ); + grants.push(create_grant); + + await sleep(1000); + + const scnd_lvl_grant = await addGrant( + TEST_PRINCIPAL, + testCase.secondLevelPerm, + testCase.config.source, + false + ); + + grants.push(scnd_lvl_grant); + await sleep(3000); + + // 2. dataset_uuid = create session dataset + const dataset_uuid = await test_fplus.DataAccess.create_dataset(testCase.structure, testCase.config); + + // 3. query the source's subclasses + const subclasses = await admin_fplus.ConfigDB.class_subclasses(testCase.config.source); + + // 4. revoke grants + for(let grant of grants){ + await deleteGrant(grant); + await sleep(1000); + } + + // 5. Delete dataset + await deleteDataset(dataset_uuid); + + + // 6. assert source's subclasses include new dataset_uuid + expect(subclasses).toContainEqual(dataset_uuid); + }, 30000); + + test('Union dataset becomes superclass of its sources ', async () => { + const testCase = Temp_Data.Valid_Body.Union; + // 1. grants: CreateDataset and UseInUnion for src + let grants = []; + + const create_grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.CreateDataset, + testCase.structure, + false + ); + grants.push(create_grant); + + await sleep(500); + for (const s of testCase.config){ + const g = await addGrant( + TEST_PRINCIPAL, + testCase.secondLevelPerm, + s, + false + ); + grants.push(g); + await sleep(100); + } + + + // 2. dataset_uuid = create dataset + const dataset_uuid = await test_fplus.DataAccess.create_dataset(testCase.structure, testCase.config); + + await sleep(1000); + + // 3. query the dataset's subclasses + const subclasses = await admin_fplus.ConfigDB.class_subclasses(dataset_uuid); + + await sleep(500); + + + // 4. revoke grants + for(let grant of grants){ + await deleteGrant(grant); + await sleep(1000); + } + + // 5. delete dataset + await deleteDataset(dataset_uuid); + + // 6. assert new dataset's subclasses include all the config sources + expect(subclasses).toEqual(expect.arrayContaining(testCase.config)); + + }, 30000); }); \ No newline at end of file From dbc238fc2460256836cd3a9bbc929a1148eb2843 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 8 Jun 2026 09:50:11 +0100 Subject: [PATCH 94/96] data access tests update --- acs-data-access/package.json | 2 +- .../tests/http/metadata_list.test.js | 2 +- .../tests/http/metadata_single.test.js | 143 ++++++++++++++++++ .../tests/http/structure_list.test.js | 2 +- 4 files changed, 146 insertions(+), 3 deletions(-) create mode 100644 acs-data-access/tests/http/metadata_single.test.js diff --git a/acs-data-access/package.json b/acs-data-access/package.json index c3b25c1bf..44fdba8cc 100644 --- a/acs-data-access/package.json +++ b/acs-data-access/package.json @@ -5,7 +5,7 @@ "main": "server.js", "type": "module", "scripts": { - "test": "vitest run" + "test": "vitest run ./tests/current" }, "author": "", "license": "ISC", diff --git a/acs-data-access/tests/http/metadata_list.test.js b/acs-data-access/tests/http/metadata_list.test.js index bba4318b2..5e1336bcf 100644 --- a/acs-data-access/tests/http/metadata_list.test.js +++ b/acs-data-access/tests/http/metadata_list.test.js @@ -2,7 +2,7 @@ import {ServiceClient} from "@amrc-factoryplus/service-client"; import process from "node:process"; import {afterAll, beforeAll, beforeEach, describe, expect, test} from 'vitest'; import { DataAccess as Constants } from "../../lib/constants.js"; -import {Test_Uuids} from '../temp_data.js'; +import * as Temp_Data from '../temp_data.js'; function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); diff --git a/acs-data-access/tests/http/metadata_single.test.js b/acs-data-access/tests/http/metadata_single.test.js new file mode 100644 index 000000000..a746b641f --- /dev/null +++ b/acs-data-access/tests/http/metadata_single.test.js @@ -0,0 +1,143 @@ +import {ServiceClient} from "@amrc-factoryplus/service-client"; +import process from "node:process"; +import {afterAll, beforeAll, beforeEach, describe, expect, test} from 'vitest'; +import { DataAccess as Constants } from "../../lib/constants.js"; +import * as Temp_Data from '../temp_data.js'; + + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +describe('GET /structure/:uuid', () => { + const TEST_PRINCIPAL=process.env.TEST_HUMAN_UUID; + + let test_fplus; + let admin_fplus; + let ALL_GRANTS = []; + + beforeEach(async () => { + admin_fplus = new ServiceClient({ + directory_url: process.env.DIRECTORY_URL, + username: process.env.ADMIN_USERNAME, + password: process.env.ADMIN_PASSWORD, + verbose: 'ALL' + }); + + test_fplus = new ServiceClient({ + directory_url: process.env.DIRECTORY_URL, + username: process.env.TEST_HUMAN_USERNAME, + password: process.env.TEST_HUMAN_PASSWORD, + verbose: 'ALL' + }); + await sleep(1000); + }); + + + // Clean up grants if not removed in case of failure. + afterAll(async () => { + for (let grant of ALL_GRANTS){ + await deleteGrant(grant); + } + }); + + + async function addGrant(principal, permission, target, plural){ + if(!principal || + !permission || + !target + ) return; + + const grant_uuid = await admin_fplus.Auth.add_grant({ + principal, + permission, + target, + plural + }); + ALL_GRANTS.push(grant_uuid); + return grant_uuid; + } + + async function deleteGrant(grant_uuid){ + if(!grant_uuid) return; + + await admin_fplus.Auth.delete_grant(grant_uuid) + ALL_GRANTS = ALL_GRANTS.filter(uuid => uuid !== grant_uuid); + } + + async function deleteDataset(uuid){ + if(!uuid) return; + + await admin_fplus.DataAccess.delete_dataset(uuid); + } + + test('Dataset exist but no permission', async () => { + const testCase = Temp_Data.Test_Uuids.Src_Datasets.TestDeviceDataset; + + try { + const res = await test_fplus.DataAccess.get_single_metadata(testCase); + expect.fail('Expected to throw'); + } + catch (err) { + expect(err.status).not.toBe(200); + } + }); + + test('User has irrelevant permission to the dataset', async () => { + const testCase = Temp_Data.Test_Uuids.Src_Datasets.TestDeviceDataset; + + const grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.EditDataset, + testCase, + false + ); + + try { + const res = await test_fplus.DataAccess.get_single_metadata(testCase); + expect.fail('Expected to throw'); + } + catch (err) { + expect(err.status).not.toBe(200); + } + finally{ + await deleteGrant(grant); + + } + }); + + test('Invalid uuid', async () => { + const testCase = 'xxx'; + + try { + const res = await test_fplus.DataAccess.get_single_metadata(testCase); + expect.fail('Expected to throw'); + } + catch (err) { + expect(err.status).not.toBe(200); + } + }); + + test('Invalid dataset should not be returned', async () => { + + }); + + test.only('Session format', async () => { + const testCase = Temp_Data.Test_Uuids.Session_Datasets.SessionNode2DoubleDataset; + + const grant = await addGrant( + TEST_PRINCIPAL, + Constants.Perm.ReadDataset, + testCase, + false + ); + + const res = await test_fplus.DataAccess.get_single_metadata(testCase); + + console.log(res); + + await deleteGrant(grant); + + expect(res).toB(0); + }); +}); \ No newline at end of file diff --git a/acs-data-access/tests/http/structure_list.test.js b/acs-data-access/tests/http/structure_list.test.js index ac57017d1..9678d909c 100644 --- a/acs-data-access/tests/http/structure_list.test.js +++ b/acs-data-access/tests/http/structure_list.test.js @@ -2,7 +2,7 @@ import {ServiceClient} from "@amrc-factoryplus/service-client"; import process from "node:process"; import {afterAll, beforeAll, beforeEach, describe, expect, test} from 'vitest'; import { DataAccess as Constants } from "../../lib/constants.js"; -import {Test_Uuids} from '../temp_data.js'; +import * as Temp_Data from '../temp_data.js'; function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); From e2e10f2362733ea4d2522e3adc3f52b598a0b564 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 8 Jun 2026 10:07:47 +0100 Subject: [PATCH 95/96] data access test scripts --- .../{test-http-api.js => tst-http-api.js} | 26 ++- acs-data-access/tests/tst-influx-directly.js | 212 ++++++++++++++++++ .../{test-notify-api.js => tst-notify-api.js} | 0 3 files changed, 228 insertions(+), 10 deletions(-) rename acs-data-access/tests/{test-http-api.js => tst-http-api.js} (55%) create mode 100644 acs-data-access/tests/tst-influx-directly.js rename acs-data-access/tests/{test-notify-api.js => tst-notify-api.js} (100%) diff --git a/acs-data-access/tests/test-http-api.js b/acs-data-access/tests/tst-http-api.js similarity index 55% rename from acs-data-access/tests/test-http-api.js rename to acs-data-access/tests/tst-http-api.js index afff2ac3e..893236ced 100644 --- a/acs-data-access/tests/test-http-api.js +++ b/acs-data-access/tests/tst-http-api.js @@ -1,8 +1,6 @@ import {ServiceClient} from "@amrc-factoryplus/service-client"; -import * as rx from "rxjs"; -import readline from "node:readline"; import process from "node:process"; @@ -10,20 +8,28 @@ import process from "node:process"; * This is a test script that uses rx-client DataAccess client interface * to subscribe to DataAccess Notify API. */ + + + // .get_metadata_list(); + // .get_single_metadata(DATASET_UUID) async function main(){ const fplus = new ServiceClient({ directory_url: process.env.DIRECTORY_URL, - username: process.env.HUMAN_USERNAME, - password: process.env.HUMAN_PASSWORD, + username: process.env.TEST_HUMAN_USERNAME, + password: process.env.TEST_HUMAN_PASSWORD, verbose: 'ALL' }); - const DATASET_UUID = ""; - const res = await fplus.DataAccess - // .get_metadata_list(); - // .get_single_metadata(DATASET_UUID) - .download_data(DATASET_UUID) + const uuid = "a7594958-4b03-45e4-8ac0-4af8d1e77e3f"; + const res = await fplus.DataAccess.delete_dataset(uuid); + + + + + // const res = await fplus.DataAccess.download_data(DATASET_UUID) + + // const res = await fplus.DataAccess.get_metadata_list(); console.log("Test Data Access HTTP"); console.log(res); @@ -31,4 +37,4 @@ async function main(){ } -main() \ No newline at end of file +main().catch(console.error); \ No newline at end of file diff --git a/acs-data-access/tests/tst-influx-directly.js b/acs-data-access/tests/tst-influx-directly.js new file mode 100644 index 000000000..c8320a86f --- /dev/null +++ b/acs-data-access/tests/tst-influx-directly.js @@ -0,0 +1,212 @@ +import {InfluxDB, flux} from '@influxdata/influxdb-client'; +import { count } from 'rxjs'; + +const url = process.env.INFLUXDB_URL; +const token = process.env.INFLUXDB_TOKEN; +const org = process.env.INFLUXDB_ORG; +const bucket = process.env.INFLUXDB_BUCKET; + +const influxDB = new InfluxDB({ url, token }); +const queryApi = influxDB.getQueryApi(org); + +/** + * Get all data for a specific topLevelInstance tag. + */ +async function getAllDataForTag(topLevelInstance) { + const flux = ` + from(bucket: "${bucket}") + |> range(start: 0) + |> filter(fn: (r) => r["topLevelInstance"] == "${topLevelInstance}") + `; + + const rows = []; + + return new Promise((resolve, reject) => { + queryApi.queryRows(flux, { + next(row, tableMeta) { + rows.push(tableMeta.toObject(row)); + }, + error(error) { + reject(error); + }, + complete() { + resolve(rows); + } + }); + }); +} + +/** + * Get first and last timestamps for a specific topLevelInstance tag. + */ +async function getTimestampRange(topLevelInstance) { + const firstQuery = ` + from(bucket: "${bucket}") + |> range(start: 0) + |> filter(fn: (r) => r["topLevelInstance"] == "${topLevelInstance}") + |> sort(columns: ["_time"]) + |> limit(n: 1) + `; + + const lastQuery = ` + from(bucket: "${bucket}") + |> range(start: 0) + |> filter(fn: (r) => r["topLevelInstance"] == "${topLevelInstance}") + |> sort(columns: ["_time"], desc: true) + |> limit(n: 1) + `; + + async function runSingleRowQuery(flux) { + return new Promise((resolve, reject) => { + let result = null; + + queryApi.queryRows(flux, { + next(row, tableMeta) { + result = tableMeta.toObject(row); + }, + error(error) { + reject(error); + }, + complete() { + resolve(result); + } + }); + }); + } + + const [first, last] = await Promise.all([ + runSingleRowQuery(firstQuery), + runSingleRowQuery(lastQuery) + ]); + + return { + firstTimestamp: first?._time || null, + lastTimestamp: last?._time || null + }; +} + + +async function countRowsForTag(topLevelInstance) { + const flux = ` + from(bucket: "${bucket}") + |> range(start: 0) + |> filter(fn: (r) => r["topLevelInstance"] == "${topLevelInstance}") + |> group() + |> count(column: "_value") + |> sum(column: "_value") + `; + + return new Promise((resolve, reject) => { + let total = 0; + + queryApi.queryRows(flux, { + next(row, tableMeta) { + const obj = tableMeta.toObject(row); + total = obj._value || 0; + }, + error: reject, + complete: () => resolve(total), + }); + }); +} + + +async function getAllTagsAndFieldsForTag(topLevelInstance) { + const flux = ` + from(bucket: "${bucket}") + |> range(start: 0) + |> filter(fn: (r) => r["topLevelInstance"] == "${topLevelInstance}") + |> limit(n: 1000) + `; + + return new Promise((resolve, reject) => { + const tags = new Set(); + const fields = new Set(); + + queryApi.queryRows(flux, { + next(row, tableMeta) { + const obj = tableMeta.toObject(row); + + // Fields + if (obj._field) { + fields.add(obj._field); + } + + // Tags = all string keys except reserved Influx columns + for (const key of Object.keys(obj)) { + if ( + !["_time", "_value", "_field", "_measurement"].includes(key) + ) { + tags.add(key); + } + } + }, + error(err) { + reject(err); + }, + complete() { + resolve({ + tags: [...tags], + fields: [...fields], + }); + }, + }); + }); +} + +/** + * Example usage + */ + +const SESSION_DATASETS = { + "TestSessionAllDataset": "74cac8a1-88a6-4b08-b297-a3c05a388ec5" +} + +const UNION_DATASETS = { + "TestUnionAllDataset": "e2a4c530-dc0f-417d-b00b-329b0e90e033" +} + +const SRC_DATASETS = { + "TestDeviceDataset": "492194aa-4ced-44d1-96c1-4e09e4d52f45", + "TestDoubleDeviceDataset": "3af79d1f-bdc2-41a8-bf2b-bd16af8a92a2", + "TestFloatDeviceDataset": "3dc230e9-ab36-4729-8d93-6d5c421f7d7f", + + "Node2_TestDoubleDeviceDataset": "720ecd5a-c5d2-49d5-bf5a-8ca01dfdb7df", + + "Node2_TestFloutDeviceDataset": "9975fb51-ed1c-4bc9-a845-331c801f140f", +} + +// NODE 1 +const DEVICES_MAP = { + "TestDevice": "5880686f-13f9-4089-b236-825d002bc911", + "TestDoubleDevice": "1298e74a-37d3-4717-aba1-4c1e67b953c1", + "TestFloatDevice": "34b6910c-85ec-445b-9071-0813891e6ff7", + + "Node2_TestDoubleDevice": "d60d00bf-eae7-4f88-9ad2-cfc4c638ac7b", + "Node2_TestFloutDevice": "961cb1c0-3d00-44e8-90bc-3fdf917746fc" +} + +async function main() { + // device uuid + const topLevelInstance = DEVICES_MAP.TestDoubleDevice; + + console.log(`Fetching data for ${topLevelInstance}...`); + + // const data = await getAllDataForTag(topLevelInstance); + + // console.log(`Found ${data.length} rows`); + + // Show first few rows + // console.dir(data.slice(0, 5), { depth: null }); + + const rows_count = await countRowsForTag(topLevelInstance); + console.log("Rows count:", rows_count); + + const time_range = await getTimestampRange(topLevelInstance); + console.log('Timestamp range:', time_range); + + const fields = await getAllTagsAndFieldsForTag(topLevelInstance); + console.log("Fields:", fields); +} + +main().catch(console.error); \ No newline at end of file diff --git a/acs-data-access/tests/test-notify-api.js b/acs-data-access/tests/tst-notify-api.js similarity index 100% rename from acs-data-access/tests/test-notify-api.js rename to acs-data-access/tests/tst-notify-api.js From fe59ac7d56c2e9068004d305112aad36dc45a728 Mon Sep 17 00:00:00 2001 From: amrc-za Date: Mon, 8 Jun 2026 10:41:31 +0100 Subject: [PATCH 96/96] data access client lib updated empty return formats --- lib/js-service-client/lib/service/data-access.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/js-service-client/lib/service/data-access.js b/lib/js-service-client/lib/service/data-access.js index 9aedac0c9..96e446f04 100644 --- a/lib/js-service-client/lib/service/data-access.js +++ b/lib/js-service-client/lib/service/data-access.js @@ -25,6 +25,7 @@ export class DataAccess extends ServiceInterface{ async delete_dataset(uuid){ const [st, json] = await this.fetch(`v1/delete/${uuid}`); + if(st == 404) return ""; if(st != 200) this.throw (`Can't delete dataset ${uuid}`, st, json); @@ -161,7 +162,7 @@ export class DataAccess extends ServiceInterface{ async get_single_structure(uuid){ const [st, json] = await this.fetch(`v1/structure/${uuid}`); - if(st == 404) return []; + if(st == 404) return {}; if(st == 403) this.throw(`Unauthorised to access dataset ${uuid}`, st); if(st != 200) @@ -179,7 +180,7 @@ export class DataAccess extends ServiceInterface{ config } }); - if(st == 404) return []; + if(st == 404) return ""; if(st == 403) this.throw(`Unauthorised to update dataset ${uuid}`, st); if(st != 200)