@uns-kit/core 3.0.6 → 3.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATIONS.md +57 -3
- package/dist/config/project.config.extension.d.ts +1 -1
- package/dist/config/project.config.extension.d.ts.map +1 -1
- package/dist/examples/datahub-client.js +6 -7
- package/dist/examples/datahub-client.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +29 -30
- package/dist/logger.js.map +1 -1
- package/dist/tools/auth/auth-client.d.ts +2 -0
- package/dist/tools/auth/auth-client.d.ts.map +1 -1
- package/dist/tools/auth/auth-client.js +9 -7
- package/dist/tools/auth/auth-client.js.map +1 -1
- package/dist/tools/auth/index.d.ts +2 -0
- package/dist/tools/auth/index.d.ts.map +1 -1
- package/dist/tools/auth/index.js +1 -0
- package/dist/tools/auth/index.js.map +1 -1
- package/dist/tools/auth/service-token-provider.d.ts +44 -0
- package/dist/tools/auth/service-token-provider.d.ts.map +1 -0
- package/dist/tools/auth/service-token-provider.js +54 -0
- package/dist/tools/auth/service-token-provider.js.map +1 -0
- package/dist/tools/datahub/datahub-client.d.ts +4 -0
- package/dist/tools/datahub/datahub-client.d.ts.map +1 -1
- package/dist/tools/datahub/datahub-client.js +5 -0
- package/dist/tools/datahub/datahub-client.js.map +1 -1
- package/dist/tools/generate-config-schema.js +67 -11
- package/dist/tools/generate-config-schema.js.map +1 -1
- package/dist/uns/uns-attributes.d.ts +2 -2
- package/dist/uns/uns-interfaces.d.ts +5 -1
- package/dist/uns/uns-interfaces.d.ts.map +1 -1
- package/dist/uns/uns-interfaces.js.map +1 -1
- package/dist/uns-config/config-schema.d.ts +405 -5
- package/dist/uns-config/config-schema.d.ts.map +1 -1
- package/dist/uns-config/host-placeholders.d.ts +14 -98
- package/dist/uns-config/host-placeholders.d.ts.map +1 -1
- package/dist/uns-config/mqtt-channel.d.ts +18 -0
- package/dist/uns-config/mqtt-channel.d.ts.map +1 -0
- package/dist/uns-config/mqtt-channel.js +75 -0
- package/dist/uns-config/mqtt-channel.js.map +1 -0
- package/dist/uns-config/schema-tolls.d.ts +1 -2
- package/dist/uns-config/schema-tolls.d.ts.map +1 -1
- package/dist/uns-config/schema-tolls.js +2 -4
- package/dist/uns-config/schema-tolls.js.map +1 -1
- package/dist/uns-config/schema-tools.d.ts +6 -1
- package/dist/uns-config/schema-tools.d.ts.map +1 -1
- package/dist/uns-config/schema-tools.js +7 -8
- package/dist/uns-config/schema-tools.js.map +1 -1
- package/dist/uns-config/secret-placeholders.d.ts +8 -86
- package/dist/uns-config/secret-placeholders.d.ts.map +1 -1
- package/dist/uns-config/uns-core-schema.d.ts +191 -3106
- package/dist/uns-config/uns-core-schema.d.ts.map +1 -1
- package/dist/uns-config/uns-core-schema.js +61 -68
- package/dist/uns-config/uns-core-schema.js.map +1 -1
- package/package.json +3 -4
package/MIGRATIONS.md
CHANGED
|
@@ -9,6 +9,62 @@ Agents must inspect the application's existing ownership and shutdown flow
|
|
|
9
9
|
before editing it. The examples below describe the intended behavior, not a
|
|
10
10
|
mechanical search-and-replace operation.
|
|
11
11
|
|
|
12
|
+
## 3.0.9 - MQTT channel inheritance
|
|
13
|
+
|
|
14
|
+
Apply this migration when upgrading from `@uns-kit/core` `<3.0.9` to
|
|
15
|
+
`>=3.0.9` if an application supports optional `input` or `output` broker
|
|
16
|
+
overrides.
|
|
17
|
+
|
|
18
|
+
Resolve channels with `resolveMqttChannel(config.infra, config.input)` or
|
|
19
|
+
`resolveMqttChannel(config.infra, config.output)`. Pass its `host` to the
|
|
20
|
+
proxy constructor and `mqttChannelParameters(channel)` to the connection
|
|
21
|
+
options. A partial channel override now inherits the complete `infra` broker
|
|
22
|
+
configuration, including credentials, TLS material, reconnect settings,
|
|
23
|
+
servers/hosts, and MQTT v5 properties. An explicit channel field still wins.
|
|
24
|
+
|
|
25
|
+
## 3.0.8 - Controller-managed service tokens
|
|
26
|
+
|
|
27
|
+
Apply this migration when upgrading from `@uns-kit/core` `<3.0.8` to
|
|
28
|
+
`>=3.0.8` for RTT services that call a DataHub controller or a controller-
|
|
29
|
+
proxied API.
|
|
30
|
+
|
|
31
|
+
Use `ServiceTokenProvider` as the `UnsClient` `tokenProvider`. It reads the
|
|
32
|
+
controller-mounted `UNS_SERVICE_TOKEN_FILE` first, then `UNS_SERVICE_TOKEN`,
|
|
33
|
+
then the resolved `uns.token` configuration value. Keep `AuthClient` only as a
|
|
34
|
+
legacy local-development fallback for services that still have
|
|
35
|
+
`uns.email`/`uns.password`.
|
|
36
|
+
|
|
37
|
+
When `UNS_SERVICE_TOKEN_FILE` is set, it is authoritative. A missing or empty
|
|
38
|
+
file is an error; do not fall back to a user credential or copy the token into
|
|
39
|
+
the service configuration. The provider re-reads the mounted file for each
|
|
40
|
+
request, so the controller can rotate credentials atomically without restarting
|
|
41
|
+
the RTT process.
|
|
42
|
+
|
|
43
|
+
## 3.0.7 - Zod 4 configuration contracts
|
|
44
|
+
|
|
45
|
+
Apply this migration when upgrading from `@uns-kit/core` `<3.0.7` to
|
|
46
|
+
`>=3.0.7`.
|
|
47
|
+
|
|
48
|
+
`@uns-kit/core` and `@uns-kit/database` now use Zod 4. Applications that own a
|
|
49
|
+
`src/config/project.config.extension.ts` file must use the same Zod major at the
|
|
50
|
+
schema-composition boundary. Update the application's direct `zod` dependency
|
|
51
|
+
to `^4.4.3` before regenerating configuration artifacts.
|
|
52
|
+
|
|
53
|
+
The UNS core and database schemas preserve their previous accepted inputs and
|
|
54
|
+
defaults. The migration is nevertheless source-sensitive for applications that
|
|
55
|
+
inspect Zod internals or use Zod-3-only type aliases. Replace those seams with
|
|
56
|
+
the public Zod 4 API:
|
|
57
|
+
|
|
58
|
+
- replace `z.AnyZodObject` with `z.ZodObject<z.ZodRawShape>`;
|
|
59
|
+
- replace `z.ZodTypeAny` with `z.ZodType` where a broad schema type is needed;
|
|
60
|
+
- read object keys through `.shape`, not `_def.shape()`;
|
|
61
|
+
- pass both key and value schemas to `z.record(keySchema, valueSchema)`;
|
|
62
|
+
- prefer object `.extend()` or shape spreading over deprecated `.merge()`.
|
|
63
|
+
|
|
64
|
+
After updating the extension schema, run `pnpm run generate-config-schema` and
|
|
65
|
+
check both `config.schema.json` and `src/config/app-config.ts`. Then run the
|
|
66
|
+
application's typecheck and its existing configuration compatibility tests.
|
|
67
|
+
|
|
12
68
|
## 3.0.0 - MQTT table columns use named objects
|
|
13
69
|
|
|
14
70
|
Apply this migration when upgrading from `@uns-kit/core` `<3.0.0` to
|
|
@@ -22,9 +78,7 @@ column directly addressable as, for example,
|
|
|
22
78
|
Before:
|
|
23
79
|
|
|
24
80
|
```ts
|
|
25
|
-
columns: [
|
|
26
|
-
{ name: "power", type: "double", value: 42.1, uom: "kW" },
|
|
27
|
-
]
|
|
81
|
+
columns: [{ name: "power", type: "double", value: 42.1, uom: "kW" }];
|
|
28
82
|
```
|
|
29
83
|
|
|
30
84
|
After:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
export declare const projectExtrasSchema: z.ZodObject<{},
|
|
2
|
+
export declare const projectExtrasSchema: z.ZodObject<{}, z.core.$strip>;
|
|
3
3
|
export type ProjectExtras = z.infer<typeof projectExtrasSchema>;
|
|
4
4
|
//# sourceMappingURL=project.config.extension.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.config.extension.d.ts","sourceRoot":"","sources":["../../src/config/project.config.extension.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,mBAAmB,
|
|
1
|
+
{"version":3,"file":"project.config.extension.d.ts","sourceRoot":"","sources":["../../src/config/project.config.extension.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,mBAAmB,gCAAe,CAAC;AAEhD,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { ConfigFile, UnsClient } from "../index.js";
|
|
2
|
-
import { AuthClient } from "../tools/auth/auth-client.js";
|
|
1
|
+
import { AuthClient, ConfigFile, ServiceTokenProvider, UnsClient } from "../index.js";
|
|
3
2
|
async function main() {
|
|
4
3
|
const config = await ConfigFile.loadConfig();
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
const tokenProvider = new ServiceTokenProvider({
|
|
5
|
+
configToken: typeof config.uns.token === "string" ? config.uns.token : undefined,
|
|
6
|
+
fallback: await AuthClient.create(),
|
|
7
|
+
});
|
|
8
8
|
const client = new UnsClient(config.uns.rest, {
|
|
9
|
-
|
|
10
|
-
authClient,
|
|
9
|
+
tokenProvider,
|
|
11
10
|
});
|
|
12
11
|
const valueTopic = "sij/acroni/vv/hrm-furnace/equipment/pusher/output-quantity";
|
|
13
12
|
const tableTopic = "sij/acroni/vv/hrm-furnace/process-segment/slab-001/trend-data";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"datahub-client.js","sourceRoot":"","sources":["../../src/examples/datahub-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"datahub-client.js","sourceRoot":"","sources":["../../src/examples/datahub-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { AuthClient, ConfigFile, ServiceTokenProvider, UnsClient } from \"../index.js\";\n\nasync function main(): Promise<void> {\n const config = await ConfigFile.loadConfig();\n const tokenProvider = new ServiceTokenProvider({\n configToken: typeof config.uns.token === \"string\" ? config.uns.token : undefined,\n fallback: await AuthClient.create(),\n });\n const client = new UnsClient(config.uns.rest, {\n tokenProvider,\n });\n\n const valueTopic = \"sij/acroni/vv/hrm-furnace/equipment/pusher/output-quantity\";\n const tableTopic = \"sij/acroni/vv/hrm-furnace/process-segment/slab-001/trend-data\";\n\n const to = new Date();\n const from = new Date(to.getTime() - 7 * 24 * 60 * 60 * 1000);\n\n const lastValue = await client.lastValue(valueTopic);\n console.log(\"lastValue:\");\n console.dir(lastValue, { depth: null });\n console.log();\n\n const data = await client.getAttributeData(tableTopic, {\n from: from.toISOString(),\n to: to.toISOString(),\n table: \"uns_hrm_furnace_trend_data_table\",\n dedupe: false,\n summaryOnly: false,\n });\n console.log(\"getData:\");\n if (data === null) {\n console.log(null);\n } else {\n console.dir(data.toRecords().slice(0, 3), { depth: null });\n }\n console.log();\n\n const customData = await client.getData(\"/projects/project-name/path-to-data/data\", {\n fromDate: \"20260325\",\n });\n console.log(\"getData:\");\n console.dir(await customData.json(), { depth: null });\n console.log();\n\n const history = await client.history([valueTopic, tableTopic], {\n from: from.toISOString(),\n to: to.toISOString(),\n limit: 500,\n dedupe: false,\n summaryOnly: false,\n });\n console.log(\"history:\");\n if (history === null) {\n console.log(null);\n } else {\n console.dir(history.byTopic, { depth: null });\n }\n}\n\nvoid main().catch((error) => {\n console.error(error);\n process.exitCode = 1;\n});\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,10 @@ export { ConfigFile } from "./config-file.js";
|
|
|
6
6
|
export { default as logger } from "./logger.js";
|
|
7
7
|
export { getLogger } from "./logger.js";
|
|
8
8
|
export { resolveInfisicalConfig } from "./uns-config/secret-resolver.js";
|
|
9
|
+
export { mqttChannelParameters, resolveMqttChannel } from "./uns-config/mqtt-channel.js";
|
|
10
|
+
export type { MqttChannelConfig, ResolvedMqttChannel } from "./uns-config/mqtt-channel.js";
|
|
11
|
+
export { AuthClient, ServiceTokenProvider } from "./tools/auth/index.js";
|
|
12
|
+
export type { AccessTokenProvider, ServiceTokenProviderOptions } from "./tools/auth/index.js";
|
|
9
13
|
export { UnsClient, LastValueResult, ClientError, RangeResult, BatchRangeTopicResult, BatchRangeResponse, } from "./tools/datahub/datahub-client.js";
|
|
10
14
|
export type { LastValuePayload, CatchAllTimeField, CatchAllAggregate, RangeQueryOptions, RangeColumn, RangeStats, RangePayload, BatchRangeTopicPayload, BatchRangeResponsePayload, } from "./tools/datahub/datahub-client.js";
|
|
11
15
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,4BAA4B,CAAC;AACxE,YAAY,EACV,qBAAqB,EACrB,wBAAwB,EACxB,2BAA2B,EAC3B,4BAA4B,GAC7B,MAAM,4BAA4B,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,2BAA2B,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EACL,SAAS,EACT,eAAe,EACf,WAAW,EACX,WAAW,EACX,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,mCAAmC,CAAC;AAC3C,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EACX,UAAU,EACV,YAAY,EACZ,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,mCAAmC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,4BAA4B,CAAC;AACxE,YAAY,EACV,qBAAqB,EACrB,wBAAwB,EACxB,2BAA2B,EAC3B,4BAA4B,GAC7B,MAAM,4BAA4B,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,2BAA2B,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AACzF,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAC3F,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AACzE,YAAY,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AAC9F,OAAO,EACL,SAAS,EACT,eAAe,EACf,WAAW,EACX,WAAW,EACX,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,mCAAmC,CAAC;AAC3C,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EACX,UAAU,EACV,YAAY,EACZ,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,mCAAmC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -5,5 +5,7 @@ export { ConfigFile } from "./config-file.js";
|
|
|
5
5
|
export { default as logger } from "./logger.js";
|
|
6
6
|
export { getLogger } from "./logger.js";
|
|
7
7
|
export { resolveInfisicalConfig } from "./uns-config/secret-resolver.js";
|
|
8
|
+
export { mqttChannelParameters, resolveMqttChannel } from "./uns-config/mqtt-channel.js";
|
|
9
|
+
export { AuthClient, ServiceTokenProvider } from "./tools/auth/index.js";
|
|
8
10
|
export { UnsClient, LastValueResult, ClientError, RangeResult, BatchRangeTopicResult, BatchRangeResponse, } from "./tools/datahub/datahub-client.js";
|
|
9
11
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAOxE,cAAc,yBAAyB,CAAC;AACxC,cAAc,2BAA2B,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EACL,SAAS,EACT,eAAe,EACf,WAAW,EACX,WAAW,EACX,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,mCAAmC,CAAC","sourcesContent":["export { default as UnsProxyProcess } from \"./uns/uns-proxy-process.js\";\nexport type {\n UnsProxyProcessPlugin,\n UnsProxyProcessPluginApi,\n UnsProxyProcessPluginMethod,\n UnsProxyProcessPluginMethods,\n} from \"./uns/uns-proxy-process.js\";\nexport * from \"./uns/uns-interfaces.js\";\nexport * from \"./uns/service-metadata.js\";\nexport { ConfigFile } from \"./config-file.js\";\nexport { default as logger } from \"./logger.js\";\nexport { getLogger } from \"./logger.js\";\nexport { resolveInfisicalConfig } from \"./uns-config/secret-resolver.js\";\nexport {\n UnsClient,\n LastValueResult,\n ClientError,\n RangeResult,\n BatchRangeTopicResult,\n BatchRangeResponse,\n} from \"./tools/datahub/datahub-client.js\";\nexport type {\n LastValuePayload,\n CatchAllTimeField,\n CatchAllAggregate,\n RangeQueryOptions,\n RangeColumn,\n RangeStats,\n RangePayload,\n BatchRangeTopicPayload,\n BatchRangeResponsePayload,\n} from \"./tools/datahub/datahub-client.js\";\n
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAOxE,cAAc,yBAAyB,CAAC;AACxC,cAAc,2BAA2B,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAEzF,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAEzE,OAAO,EACL,SAAS,EACT,eAAe,EACf,WAAW,EACX,WAAW,EACX,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,mCAAmC,CAAC","sourcesContent":["export { default as UnsProxyProcess } from \"./uns/uns-proxy-process.js\";\nexport type {\n UnsProxyProcessPlugin,\n UnsProxyProcessPluginApi,\n UnsProxyProcessPluginMethod,\n UnsProxyProcessPluginMethods,\n} from \"./uns/uns-proxy-process.js\";\nexport * from \"./uns/uns-interfaces.js\";\nexport * from \"./uns/service-metadata.js\";\nexport { ConfigFile } from \"./config-file.js\";\nexport { default as logger } from \"./logger.js\";\nexport { getLogger } from \"./logger.js\";\nexport { resolveInfisicalConfig } from \"./uns-config/secret-resolver.js\";\nexport { mqttChannelParameters, resolveMqttChannel } from \"./uns-config/mqtt-channel.js\";\nexport type { MqttChannelConfig, ResolvedMqttChannel } from \"./uns-config/mqtt-channel.js\";\nexport { AuthClient, ServiceTokenProvider } from \"./tools/auth/index.js\";\nexport type { AccessTokenProvider, ServiceTokenProviderOptions } from \"./tools/auth/index.js\";\nexport {\n UnsClient,\n LastValueResult,\n ClientError,\n RangeResult,\n BatchRangeTopicResult,\n BatchRangeResponse,\n} from \"./tools/datahub/datahub-client.js\";\nexport type {\n LastValuePayload,\n CatchAllTimeField,\n CatchAllAggregate,\n RangeQueryOptions,\n RangeColumn,\n RangeStats,\n RangePayload,\n BatchRangeTopicPayload,\n BatchRangeResponsePayload,\n} from \"./tools/datahub/datahub-client.js\";\n"]}
|
package/dist/logger.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAyEA,QAAA,MAAM,MAAM,0BAiBV,CAAC;AAEH,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,4BAU3C;AAED,eAAe,MAAM,CAAC"}
|
package/dist/logger.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import os from
|
|
4
|
-
import path from
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
1
|
+
/// <reference path="./types/winston-gelf.d.ts" />
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import os from "os";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
import { createLogger, format, transports } from "winston";
|
|
7
|
+
import GelfTransport from "winston-gelf";
|
|
8
|
+
import { ConfigFile } from "./config-file.js";
|
|
8
9
|
function loadPackageMeta() {
|
|
9
|
-
const packageJsonPath = path.join(process.cwd(),
|
|
10
|
+
const packageJsonPath = path.join(process.cwd(), "package.json");
|
|
10
11
|
try {
|
|
11
|
-
const raw = fs.readFileSync(packageJsonPath,
|
|
12
|
+
const raw = fs.readFileSync(packageJsonPath, "utf8");
|
|
12
13
|
const packageMeta = JSON.parse(raw);
|
|
13
14
|
if (packageMeta.name || packageMeta.version) {
|
|
14
15
|
return packageMeta;
|
|
@@ -30,54 +31,52 @@ function loadLoggingConfig() {
|
|
|
30
31
|
const packageMeta = loadPackageMeta();
|
|
31
32
|
const loggingConfig = loadLoggingConfig();
|
|
32
33
|
const gelfMeta = {
|
|
33
|
-
service: packageMeta.name ??
|
|
34
|
-
version: packageMeta.version ??
|
|
35
|
-
environment: process.env.NODE_ENV ||
|
|
36
|
-
hostname: os.hostname()
|
|
34
|
+
service: packageMeta.name ?? "unknown-service",
|
|
35
|
+
version: packageMeta.version ?? "unknown",
|
|
36
|
+
environment: process.env.NODE_ENV || "unknown",
|
|
37
|
+
hostname: os.hostname(),
|
|
37
38
|
};
|
|
38
|
-
const consoleFormat = format.combine(format.timestamp({ format:
|
|
39
|
+
const consoleFormat = format.combine(format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), format.errors({ stack: true }), format.splat(), format.json());
|
|
39
40
|
const loggerTransports = [
|
|
40
41
|
new transports.Console({
|
|
41
|
-
format: consoleFormat
|
|
42
|
-
})
|
|
42
|
+
format: consoleFormat,
|
|
43
|
+
}),
|
|
43
44
|
];
|
|
44
45
|
if (loggingConfig) {
|
|
45
46
|
loggerTransports.push(new GelfTransport({
|
|
46
47
|
format: format((info) => ({
|
|
47
48
|
...info,
|
|
48
|
-
...gelfMeta
|
|
49
|
+
...gelfMeta,
|
|
49
50
|
}))(),
|
|
50
51
|
gelfPro: {
|
|
51
52
|
adapterName: loggingConfig.adapter,
|
|
52
53
|
adapterOptions: {
|
|
53
54
|
host: loggingConfig.host,
|
|
54
|
-
port: loggingConfig.port
|
|
55
|
-
}
|
|
56
|
-
}
|
|
55
|
+
port: loggingConfig.port,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
57
58
|
}));
|
|
58
59
|
}
|
|
59
60
|
const logger = createLogger({
|
|
60
|
-
level: loggingConfig?.level ??
|
|
61
|
-
format: format.combine(format.timestamp({ format:
|
|
61
|
+
level: loggingConfig?.level ?? "info",
|
|
62
|
+
format: format.combine(format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), format.errors({ stack: true }), // Include stack trace
|
|
62
63
|
format.splat(), format.json()),
|
|
63
64
|
transports: loggerTransports,
|
|
64
65
|
exceptionHandlers: [
|
|
65
66
|
new transports.Console({
|
|
66
|
-
format: consoleFormat
|
|
67
|
+
format: consoleFormat,
|
|
67
68
|
}), // Log exceptions to console
|
|
68
69
|
// Remove exception logging to file
|
|
69
70
|
// new transports.File({ filename: 'logs/exceptions.log' })
|
|
70
|
-
]
|
|
71
|
+
],
|
|
71
72
|
});
|
|
72
73
|
export function getLogger(modulePath) {
|
|
73
|
-
const normalizedModulePath = modulePath.startsWith(
|
|
74
|
-
? fileURLToPath(modulePath)
|
|
75
|
-
: modulePath;
|
|
74
|
+
const normalizedModulePath = modulePath.startsWith("file:") ? fileURLToPath(modulePath) : modulePath;
|
|
76
75
|
const relativePath = path.relative(process.cwd(), normalizedModulePath);
|
|
77
|
-
const withoutExt = relativePath.replace(/\.[^/.]+$/,
|
|
78
|
-
const dotted = withoutExt.split(path.sep).join(
|
|
76
|
+
const withoutExt = relativePath.replace(/\.[^/.]+$/, "");
|
|
77
|
+
const dotted = withoutExt.split(path.sep).join(".");
|
|
79
78
|
return logger.child({
|
|
80
|
-
facility: dotted
|
|
79
|
+
facility: dotted,
|
|
81
80
|
});
|
|
82
81
|
}
|
|
83
82
|
export default logger;
|
package/dist/logger.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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","sourcesContent":["/// <reference path=\"./types/winston-gelf.d.ts\" />\nimport fs from \"fs\";\nimport os from \"os\";\nimport path from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { createLogger, format, transports } from \"winston\";\nimport GelfTransport from \"winston-gelf\";\n\nimport { ConfigFile } from \"./config-file.js\";\n\nfunction loadPackageMeta(): { name?: string; version?: string } {\n const packageJsonPath = path.join(process.cwd(), \"package.json\");\n\n try {\n const raw = fs.readFileSync(packageJsonPath, \"utf8\");\n const packageMeta = JSON.parse(raw) as { name?: string; version?: string };\n if (packageMeta.name || packageMeta.version) {\n return packageMeta;\n }\n } catch {\n // Use unknown values when the application package metadata is unavailable.\n }\n\n return {};\n}\n\nfunction loadLoggingConfig() {\n try {\n return ConfigFile.loadRawConfig().logging;\n } catch {\n return undefined;\n }\n}\n\nconst packageMeta = loadPackageMeta();\nconst loggingConfig = loadLoggingConfig();\nconst gelfMeta = {\n service: packageMeta.name ?? \"unknown-service\",\n version: packageMeta.version ?? \"unknown\",\n environment: process.env.NODE_ENV || \"unknown\",\n hostname: os.hostname(),\n};\nconst consoleFormat = format.combine(\n format.timestamp({ format: \"YYYY-MM-DD HH:mm:ss\" }),\n format.errors({ stack: true }),\n format.splat(),\n format.json(),\n);\n\nconst loggerTransports = [\n new transports.Console({\n format: consoleFormat,\n }),\n];\n\nif (loggingConfig) {\n loggerTransports.push(\n new GelfTransport({\n format: format((info) => ({\n ...info,\n ...gelfMeta,\n }))(),\n gelfPro: {\n adapterName: loggingConfig.adapter,\n adapterOptions: {\n host: loggingConfig.host,\n port: loggingConfig.port,\n },\n },\n }),\n );\n}\n\nconst logger = createLogger({\n level: loggingConfig?.level ?? \"info\",\n\n format: format.combine(\n format.timestamp({ format: \"YYYY-MM-DD HH:mm:ss\" }),\n format.errors({ stack: true }), // Include stack trace\n format.splat(),\n format.json(),\n ),\n transports: loggerTransports,\n exceptionHandlers: [\n new transports.Console({\n format: consoleFormat,\n }), // Log exceptions to console\n // Remove exception logging to file\n // new transports.File({ filename: 'logs/exceptions.log' })\n ],\n});\n\nexport function getLogger(modulePath: string) {\n const normalizedModulePath = modulePath.startsWith(\"file:\") ? fileURLToPath(modulePath) : modulePath;\n\n const relativePath = path.relative(process.cwd(), normalizedModulePath);\n const withoutExt = relativePath.replace(/\\.[^/.]+$/, \"\");\n const dotted = withoutExt.split(path.sep).join(\".\");\n\n return logger.child({\n facility: dotted,\n });\n}\n\nexport default logger;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-client.d.ts","sourceRoot":"","sources":["../../../src/tools/auth/auth-client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth-client.d.ts","sourceRoot":"","sources":["../../../src/tools/auth/auth-client.ts"],"names":[],"mappings":"AAQA;;;GAGG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAS;IACzC,OAAO,CAAC,KAAK,CAAgB;IAE7B,OAAO;WAQM,MAAM,IAAI,OAAO,CAAC,UAAU,CAAC;IAapC,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAuCvC,OAAO,CAAC,MAAM,CAAC,SAAS;IAWxB,OAAO,CAAC,MAAM,CAAC,QAAQ;mBAOF,gBAAgB;IAWrC,OAAO,CAAC,MAAM,CAAC,oBAAoB;YAWrB,KAAK;YAmBL,OAAO;YAwBP,aAAa;CAI5B"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ConfigFile } from "../../config-file.js";
|
|
2
2
|
import { SecureStoreFactory } from "./secure-store.js";
|
|
3
3
|
import jwt from "jsonwebtoken";
|
|
4
|
-
const cfg = await ConfigFile.loadConfig();
|
|
5
4
|
/**
|
|
6
5
|
* AuthClient handles acquiring and refreshing JWT access tokens
|
|
7
6
|
* using the configured REST base URL.
|
|
@@ -9,17 +8,22 @@ const cfg = await ConfigFile.loadConfig();
|
|
|
9
8
|
export class AuthClient {
|
|
10
9
|
restBase;
|
|
11
10
|
namespace;
|
|
11
|
+
configEmail;
|
|
12
|
+
configPassword;
|
|
12
13
|
store;
|
|
13
|
-
constructor(restBase) {
|
|
14
|
+
constructor(restBase, configEmail, configPassword) {
|
|
14
15
|
this.restBase = restBase.replace(/\/$/, "");
|
|
15
16
|
// namespace store by rest base to allow multiple environments
|
|
16
17
|
this.namespace = `uns-auth:${this.restBase}`;
|
|
18
|
+
this.configEmail = configEmail;
|
|
19
|
+
this.configPassword = configPassword;
|
|
17
20
|
}
|
|
18
21
|
static async create() {
|
|
22
|
+
const cfg = await ConfigFile.loadConfig();
|
|
19
23
|
const restBase = cfg?.uns?.rest;
|
|
20
24
|
if (!restBase)
|
|
21
25
|
throw new Error("config.uns.rest is not set");
|
|
22
|
-
const client = new AuthClient(restBase);
|
|
26
|
+
const client = new AuthClient(restBase, typeof cfg?.uns?.email === "string" ? cfg.uns.email : undefined, typeof cfg?.uns?.password === "string" ? cfg.uns.password : undefined);
|
|
23
27
|
client.store = await SecureStoreFactory.create(client.namespace);
|
|
24
28
|
return client;
|
|
25
29
|
}
|
|
@@ -41,11 +45,9 @@ export class AuthClient {
|
|
|
41
45
|
}
|
|
42
46
|
}
|
|
43
47
|
// First try to get email and password from config
|
|
44
|
-
|
|
45
|
-
const configPassword = cfg?.uns?.password;
|
|
46
|
-
if (typeof configEmail === "string" && typeof configPassword === "string") {
|
|
48
|
+
if (this.configEmail && this.configPassword) {
|
|
47
49
|
try {
|
|
48
|
-
const loggedIn = await this.login(configEmail, configPassword);
|
|
50
|
+
const loggedIn = await this.login(this.configEmail, this.configPassword);
|
|
49
51
|
await this.persistTokens(loggedIn.accessToken, loggedIn.refreshToken);
|
|
50
52
|
return loggedIn.accessToken;
|
|
51
53
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-client.js","sourceRoot":"","sources":["../../../src/tools/auth/auth-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { ConfigFile } from \"../../config-file.js\";\nimport { SecureStoreFactory, ISecureStore } from \"./secure-store.js\";\nimport jwt from \"jsonwebtoken\";\n\nconst cfg = await ConfigFile.loadConfig();\n\ntype LoginResponse = {\n accessToken: string;\n};\n\n/**\n * AuthClient handles acquiring and refreshing JWT access tokens\n * using the configured REST base URL.\n */\nexport class AuthClient {\n private readonly restBase: string;\n private readonly namespace: string;\n private store!: ISecureStore;\n\n private constructor(restBase: string) {\n this.restBase = restBase.replace(/\\/$/, \"\");\n // namespace store by rest base to allow multiple environments\n this.namespace = `uns-auth:${this.restBase}`;\n }\n\n static async create(): Promise<AuthClient> {\n const restBase: string = cfg?.uns?.rest;\n if (!restBase) throw new Error(\"config.uns.rest is not set\");\n const client = new AuthClient(restBase);\n client.store = await SecureStoreFactory.create(client.namespace);\n return client;\n }\n\n async getAccessToken(): Promise<string> {\n const accessToken = await this.store.get(\"accessToken\");\n const refreshToken = await this.store.get(\"refreshToken\");\n\n if (accessToken && !AuthClient.isExpired(accessToken)) {\n return accessToken;\n }\n\n // Try refresh if we have refresh token\n if (refreshToken) {\n try {\n const refreshed = await this.refresh(refreshToken);\n await this.persistTokens(refreshed.accessToken, refreshed.refreshToken);\n return refreshed.accessToken;\n } catch {\n // ignore, fallback to login\n }\n }\n\n // First try to get email and password from config\n const configEmail = cfg?.uns?.email;\n const configPassword = cfg?.uns?.password;\n if (typeof configEmail === \"string\" && typeof configPassword === \"string\") {\n try {\n const loggedIn = await this.login(configEmail, configPassword);\n await this.persistTokens(loggedIn.accessToken, loggedIn.refreshToken);\n return loggedIn.accessToken;\n } catch (error) {\n throw new Error(\n `Authentication failed using config.json credentials from uns.email/uns.password: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n }\n\n throw new Error(\n \"No access token available. AuthClient reads config.json and expected valid uns.email and uns.password.\",\n );\n }\n\n private static isExpired(token: string, skewSeconds = 30): boolean {\n try {\n const decoded: any = jwt.decode(token);\n if (!decoded || typeof decoded !== \"object\" || !decoded.exp) return true;\n const now = Math.floor(Date.now() / 1000);\n return decoded.exp <= now + skewSeconds;\n } catch {\n return true;\n }\n }\n\n private static endpoint(base: string, tail: string): string {\n // base ends without trailing slash; tail must not start with slash\n const b = base.replace(/\\/$/, \"\");\n const t = tail.replace(/^\\//, \"\");\n return `${b}/${t}`;\n }\n\n private static async fetchWithTimeout(url: string, init: RequestInit = {}, timeoutMs = 10_000): Promise<Response> {\n const controller = new AbortController();\n const id = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const resp = await fetch(url, { ...init, signal: controller.signal });\n return resp;\n } finally {\n clearTimeout(id);\n }\n }\n\n private static extractRefreshCookie(resp: Response): string | null {\n const anyHeaders: any = resp.headers as any;\n const getSetCookie = typeof anyHeaders.getSetCookie === \"function\" ? anyHeaders.getSetCookie.bind(anyHeaders) : null;\n const candidates: string[] = getSetCookie ? getSetCookie() : (resp.headers.get(\"set-cookie\") ? [resp.headers.get(\"set-cookie\") as string] : []);\n for (const header of candidates) {\n const match = header.match(/(?:^|;\\s*)(?:RefreshToken|rt)=([^;]+)/i);\n if (match) return match[1];\n }\n return null;\n }\n\n private async login(email: string, password: string): Promise<{ accessToken: string; refreshToken: string; }> {\n const url = AuthClient.endpoint(this.restBase, \"auth/login\");\n const resp = await AuthClient.fetchWithTimeout(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email, password }),\n }, 12_000);\n\n if (!resp.ok) {\n throw new Error(`Login failed: ${resp.status} ${resp.statusText}`);\n }\n const data = (await resp.json()) as LoginResponse;\n const refreshToken = AuthClient.extractRefreshCookie(resp);\n if (!data?.accessToken || !refreshToken) {\n throw new Error(\"Login response missing tokens\");\n }\n return { accessToken: data.accessToken, refreshToken };\n }\n\n private async refresh(refreshToken: string): Promise<{ accessToken: string; refreshToken: string; }> {\n const url = AuthClient.endpoint(this.restBase, \"auth/refresh\");\n let lastError: Error | undefined;\n for (const cookieName of [\"RefreshToken\", \"rt\"]) {\n const resp = await AuthClient.fetchWithTimeout(url, {\n method: \"POST\",\n headers: {\n \"Cookie\": `${cookieName}=${refreshToken}`,\n },\n }, 8_000);\n\n if (!resp.ok) {\n lastError = new Error(`Refresh failed: ${resp.status} ${resp.statusText}`);\n continue;\n }\n const data = (await resp.json()) as LoginResponse;\n const newRefreshToken = AuthClient.extractRefreshCookie(resp) || refreshToken;\n if (!data?.accessToken) throw new Error(\"Refresh response missing accessToken\");\n return { accessToken: data.accessToken, refreshToken: newRefreshToken };\n }\n\n throw lastError ?? new Error(\"Refresh failed.\");\n }\n\n private async persistTokens(accessToken: string, refreshToken: string): Promise<void> {\n await this.store.set(\"accessToken\", accessToken);\n await this.store.set(\"refreshToken\", refreshToken);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"auth-client.js","sourceRoot":"","sources":["../../../src/tools/auth/auth-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { ConfigFile } from \"../../config-file.js\";\nimport { SecureStoreFactory, ISecureStore } from \"./secure-store.js\";\nimport jwt from \"jsonwebtoken\";\n\ntype LoginResponse = {\n accessToken: string;\n};\n\n/**\n * AuthClient handles acquiring and refreshing JWT access tokens\n * using the configured REST base URL.\n */\nexport class AuthClient {\n private readonly restBase: string;\n private readonly namespace: string;\n private readonly configEmail?: string;\n private readonly configPassword?: string;\n private store!: ISecureStore;\n\n private constructor(restBase: string, configEmail?: string, configPassword?: string) {\n this.restBase = restBase.replace(/\\/$/, \"\");\n // namespace store by rest base to allow multiple environments\n this.namespace = `uns-auth:${this.restBase}`;\n this.configEmail = configEmail;\n this.configPassword = configPassword;\n }\n\n static async create(): Promise<AuthClient> {\n const cfg = await ConfigFile.loadConfig();\n const restBase: string = cfg?.uns?.rest;\n if (!restBase) throw new Error(\"config.uns.rest is not set\");\n const client = new AuthClient(\n restBase,\n typeof cfg?.uns?.email === \"string\" ? cfg.uns.email : undefined,\n typeof cfg?.uns?.password === \"string\" ? cfg.uns.password : undefined,\n );\n client.store = await SecureStoreFactory.create(client.namespace);\n return client;\n }\n\n async getAccessToken(): Promise<string> {\n const accessToken = await this.store.get(\"accessToken\");\n const refreshToken = await this.store.get(\"refreshToken\");\n\n if (accessToken && !AuthClient.isExpired(accessToken)) {\n return accessToken;\n }\n\n // Try refresh if we have refresh token\n if (refreshToken) {\n try {\n const refreshed = await this.refresh(refreshToken);\n await this.persistTokens(refreshed.accessToken, refreshed.refreshToken);\n return refreshed.accessToken;\n } catch {\n // ignore, fallback to login\n }\n }\n\n // First try to get email and password from config\n if (this.configEmail && this.configPassword) {\n try {\n const loggedIn = await this.login(this.configEmail, this.configPassword);\n await this.persistTokens(loggedIn.accessToken, loggedIn.refreshToken);\n return loggedIn.accessToken;\n } catch (error) {\n throw new Error(\n `Authentication failed using config.json credentials from uns.email/uns.password: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n }\n\n throw new Error(\n \"No access token available. AuthClient reads config.json and expected valid uns.email and uns.password.\",\n );\n }\n\n private static isExpired(token: string, skewSeconds = 30): boolean {\n try {\n const decoded: any = jwt.decode(token);\n if (!decoded || typeof decoded !== \"object\" || !decoded.exp) return true;\n const now = Math.floor(Date.now() / 1000);\n return decoded.exp <= now + skewSeconds;\n } catch {\n return true;\n }\n }\n\n private static endpoint(base: string, tail: string): string {\n // base ends without trailing slash; tail must not start with slash\n const b = base.replace(/\\/$/, \"\");\n const t = tail.replace(/^\\//, \"\");\n return `${b}/${t}`;\n }\n\n private static async fetchWithTimeout(url: string, init: RequestInit = {}, timeoutMs = 10_000): Promise<Response> {\n const controller = new AbortController();\n const id = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const resp = await fetch(url, { ...init, signal: controller.signal });\n return resp;\n } finally {\n clearTimeout(id);\n }\n }\n\n private static extractRefreshCookie(resp: Response): string | null {\n const anyHeaders: any = resp.headers as any;\n const getSetCookie = typeof anyHeaders.getSetCookie === \"function\" ? anyHeaders.getSetCookie.bind(anyHeaders) : null;\n const candidates: string[] = getSetCookie ? getSetCookie() : (resp.headers.get(\"set-cookie\") ? [resp.headers.get(\"set-cookie\") as string] : []);\n for (const header of candidates) {\n const match = header.match(/(?:^|;\\s*)(?:RefreshToken|rt)=([^;]+)/i);\n if (match) return match[1];\n }\n return null;\n }\n\n private async login(email: string, password: string): Promise<{ accessToken: string; refreshToken: string; }> {\n const url = AuthClient.endpoint(this.restBase, \"auth/login\");\n const resp = await AuthClient.fetchWithTimeout(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email, password }),\n }, 12_000);\n\n if (!resp.ok) {\n throw new Error(`Login failed: ${resp.status} ${resp.statusText}`);\n }\n const data = (await resp.json()) as LoginResponse;\n const refreshToken = AuthClient.extractRefreshCookie(resp);\n if (!data?.accessToken || !refreshToken) {\n throw new Error(\"Login response missing tokens\");\n }\n return { accessToken: data.accessToken, refreshToken };\n }\n\n private async refresh(refreshToken: string): Promise<{ accessToken: string; refreshToken: string; }> {\n const url = AuthClient.endpoint(this.restBase, \"auth/refresh\");\n let lastError: Error | undefined;\n for (const cookieName of [\"RefreshToken\", \"rt\"]) {\n const resp = await AuthClient.fetchWithTimeout(url, {\n method: \"POST\",\n headers: {\n \"Cookie\": `${cookieName}=${refreshToken}`,\n },\n }, 8_000);\n\n if (!resp.ok) {\n lastError = new Error(`Refresh failed: ${resp.status} ${resp.statusText}`);\n continue;\n }\n const data = (await resp.json()) as LoginResponse;\n const newRefreshToken = AuthClient.extractRefreshCookie(resp) || refreshToken;\n if (!data?.accessToken) throw new Error(\"Refresh response missing accessToken\");\n return { accessToken: data.accessToken, refreshToken: newRefreshToken };\n }\n\n throw lastError ?? new Error(\"Refresh failed.\");\n }\n\n private async persistTokens(accessToken: string, refreshToken: string): Promise<void> {\n await this.store.set(\"accessToken\", accessToken);\n await this.store.set(\"refreshToken\", refreshToken);\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,YAAY,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAC"}
|
package/dist/tools/auth/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC","sourcesContent":["export { AuthClient } from \"./auth-client.js\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC","sourcesContent":["export { AuthClient } from \"./auth-client.js\";\nexport { ServiceTokenProvider } from \"./service-token-provider.js\";\nexport type { AccessTokenProvider, ServiceTokenProviderOptions } from \"./service-token-provider.js\";\n"]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A source of bearer tokens for controller and DataHub requests.
|
|
3
|
+
*
|
|
4
|
+
* The interface is intentionally small so services can use a controller-issued
|
|
5
|
+
* machine token without depending on the legacy user-login client.
|
|
6
|
+
*/
|
|
7
|
+
export interface AccessTokenProvider {
|
|
8
|
+
getAccessToken(): Promise<string | undefined>;
|
|
9
|
+
}
|
|
10
|
+
type ServiceTokenEnvironment = Partial<Pick<NodeJS.ProcessEnv, "UNS_SERVICE_TOKEN_FILE" | "UNS_SERVICE_TOKEN">>;
|
|
11
|
+
export type ServiceTokenProviderOptions = {
|
|
12
|
+
/**
|
|
13
|
+
* Path to a controller-managed token file. When set, the file is
|
|
14
|
+
* authoritative and failures are reported instead of falling back to another
|
|
15
|
+
* credential source.
|
|
16
|
+
*/
|
|
17
|
+
tokenFile?: string;
|
|
18
|
+
/** Environment used to resolve UNS_SERVICE_TOKEN_FILE and UNS_SERVICE_TOKEN. */
|
|
19
|
+
environment?: ServiceTokenEnvironment;
|
|
20
|
+
/** Resolved config.uns.token value. */
|
|
21
|
+
configToken?: string;
|
|
22
|
+
/** Legacy development fallback, normally an AuthClient using uns.email/password. */
|
|
23
|
+
fallback?: AccessTokenProvider;
|
|
24
|
+
/** Test seam; production uses node:fs/promises.readFile. */
|
|
25
|
+
readFile?: (path: string, encoding: BufferEncoding) => Promise<string>;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Resolves service credentials in the order used by controller-managed RTT
|
|
29
|
+
* services: mounted token file, environment, configuration, then the legacy
|
|
30
|
+
* login provider. The mounted file is deliberately read for each request so
|
|
31
|
+
* atomically-rotated controller credentials are picked up without restart.
|
|
32
|
+
*/
|
|
33
|
+
export declare class ServiceTokenProvider implements AccessTokenProvider {
|
|
34
|
+
private readonly tokenFile?;
|
|
35
|
+
private readonly environment;
|
|
36
|
+
private readonly configToken?;
|
|
37
|
+
private readonly fallback?;
|
|
38
|
+
private readonly readFile;
|
|
39
|
+
constructor(options?: ServiceTokenProviderOptions);
|
|
40
|
+
getAccessToken(): Promise<string | undefined>;
|
|
41
|
+
private readControllerManagedToken;
|
|
42
|
+
}
|
|
43
|
+
export {};
|
|
44
|
+
//# sourceMappingURL=service-token-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-token-provider.d.ts","sourceRoot":"","sources":["../../../src/tools/auth/service-token-provider.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAC/C;AAED,KAAK,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,wBAAwB,GAAG,mBAAmB,CAAC,CAAC,CAAC;AAEhH,MAAM,MAAM,2BAA2B,GAAG;IACxC;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,WAAW,CAAC,EAAE,uBAAuB,CAAC;IACtC,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IAC/B,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACxE,CAAC;AAEF;;;;;GAKG;AACH,qBAAa,oBAAqB,YAAW,mBAAmB;IAC9D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA0B;IACtD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAsB;IAChD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA8D;gBAE3E,OAAO,GAAE,2BAAgC;IAQ/C,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;YAkBrC,0BAA0B;CAazC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { readFile as readFileFromFs } from "node:fs/promises";
|
|
2
|
+
/**
|
|
3
|
+
* Resolves service credentials in the order used by controller-managed RTT
|
|
4
|
+
* services: mounted token file, environment, configuration, then the legacy
|
|
5
|
+
* login provider. The mounted file is deliberately read for each request so
|
|
6
|
+
* atomically-rotated controller credentials are picked up without restart.
|
|
7
|
+
*/
|
|
8
|
+
export class ServiceTokenProvider {
|
|
9
|
+
tokenFile;
|
|
10
|
+
environment;
|
|
11
|
+
configToken;
|
|
12
|
+
fallback;
|
|
13
|
+
readFile;
|
|
14
|
+
constructor(options = {}) {
|
|
15
|
+
this.tokenFile = normalizeToken(options.tokenFile);
|
|
16
|
+
this.environment = options.environment ?? process.env;
|
|
17
|
+
this.configToken = normalizeToken(options.configToken);
|
|
18
|
+
this.fallback = options.fallback;
|
|
19
|
+
this.readFile = options.readFile ?? readFileFromFs;
|
|
20
|
+
}
|
|
21
|
+
async getAccessToken() {
|
|
22
|
+
const tokenFile = this.tokenFile ?? normalizeToken(this.environment.UNS_SERVICE_TOKEN_FILE);
|
|
23
|
+
if (tokenFile) {
|
|
24
|
+
return this.readControllerManagedToken(tokenFile);
|
|
25
|
+
}
|
|
26
|
+
const environmentToken = normalizeToken(this.environment.UNS_SERVICE_TOKEN);
|
|
27
|
+
if (environmentToken) {
|
|
28
|
+
return environmentToken;
|
|
29
|
+
}
|
|
30
|
+
if (this.configToken) {
|
|
31
|
+
return this.configToken;
|
|
32
|
+
}
|
|
33
|
+
return this.fallback?.getAccessToken();
|
|
34
|
+
}
|
|
35
|
+
async readControllerManagedToken(path) {
|
|
36
|
+
try {
|
|
37
|
+
const token = normalizeToken(await this.readFile(path, "utf8"));
|
|
38
|
+
if (!token) {
|
|
39
|
+
throw new Error("empty token file");
|
|
40
|
+
}
|
|
41
|
+
return token;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
throw new Error("Controller-managed service token file is unavailable.", {
|
|
45
|
+
cause: error instanceof Error ? error : undefined,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function normalizeToken(value) {
|
|
51
|
+
const token = value?.trim();
|
|
52
|
+
return token ? token : undefined;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=service-token-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-token-provider.js","sourceRoot":"","sources":["../../../src/tools/auth/service-token-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,cAAc,EAAE,MAAM,kBAAkB,CAAC;AA+B9D;;;;;GAKG;AACH,MAAM,OAAO,oBAAoB;IACd,SAAS,CAAU;IACnB,WAAW,CAA0B;IACrC,WAAW,CAAU;IACrB,QAAQ,CAAuB;IAC/B,QAAQ,CAA8D;IAEvF,YAAY,UAAuC,EAAE;QACnD,IAAI,CAAC,SAAS,GAAG,cAAc,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACnD,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC;QACtD,IAAI,CAAC,WAAW,GAAG,cAAc,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACvD,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,cAAc,CAAC;IACrD,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,sBAAsB,CAAC,CAAC;QAC5F,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,IAAI,CAAC,0BAA0B,CAAC,SAAS,CAAC,CAAC;QACpD,CAAC;QAED,MAAM,gBAAgB,GAAG,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,iBAAiB,CAAC,CAAC;QAC5E,IAAI,gBAAgB,EAAE,CAAC;YACrB,OAAO,gBAAgB,CAAC;QAC1B,CAAC;QAED,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,OAAO,IAAI,CAAC,WAAW,CAAC;QAC1B,CAAC;QAED,OAAO,IAAI,CAAC,QAAQ,EAAE,cAAc,EAAE,CAAC;IACzC,CAAC;IAEO,KAAK,CAAC,0BAA0B,CAAC,IAAY;QACnD,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,cAAc,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;YAChE,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;YACtC,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,uDAAuD,EAAE;gBACvE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;aAClD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;CACF;AAED,SAAS,cAAc,CAAC,KAAyB;IAC/C,MAAM,KAAK,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC;IAC5B,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACnC,CAAC","sourcesContent":["import { readFile as readFileFromFs } from \"node:fs/promises\";\n\n/**\n * A source of bearer tokens for controller and DataHub requests.\n *\n * The interface is intentionally small so services can use a controller-issued\n * machine token without depending on the legacy user-login client.\n */\nexport interface AccessTokenProvider {\n getAccessToken(): Promise<string | undefined>;\n}\n\ntype ServiceTokenEnvironment = Partial<Pick<NodeJS.ProcessEnv, \"UNS_SERVICE_TOKEN_FILE\" | \"UNS_SERVICE_TOKEN\">>;\n\nexport type ServiceTokenProviderOptions = {\n /**\n * Path to a controller-managed token file. When set, the file is\n * authoritative and failures are reported instead of falling back to another\n * credential source.\n */\n tokenFile?: string;\n /** Environment used to resolve UNS_SERVICE_TOKEN_FILE and UNS_SERVICE_TOKEN. */\n environment?: ServiceTokenEnvironment;\n /** Resolved config.uns.token value. */\n configToken?: string;\n /** Legacy development fallback, normally an AuthClient using uns.email/password. */\n fallback?: AccessTokenProvider;\n /** Test seam; production uses node:fs/promises.readFile. */\n readFile?: (path: string, encoding: BufferEncoding) => Promise<string>;\n};\n\n/**\n * Resolves service credentials in the order used by controller-managed RTT\n * services: mounted token file, environment, configuration, then the legacy\n * login provider. The mounted file is deliberately read for each request so\n * atomically-rotated controller credentials are picked up without restart.\n */\nexport class ServiceTokenProvider implements AccessTokenProvider {\n private readonly tokenFile?: string;\n private readonly environment: ServiceTokenEnvironment;\n private readonly configToken?: string;\n private readonly fallback?: AccessTokenProvider;\n private readonly readFile: (path: string, encoding: BufferEncoding) => Promise<string>;\n\n constructor(options: ServiceTokenProviderOptions = {}) {\n this.tokenFile = normalizeToken(options.tokenFile);\n this.environment = options.environment ?? process.env;\n this.configToken = normalizeToken(options.configToken);\n this.fallback = options.fallback;\n this.readFile = options.readFile ?? readFileFromFs;\n }\n\n async getAccessToken(): Promise<string | undefined> {\n const tokenFile = this.tokenFile ?? normalizeToken(this.environment.UNS_SERVICE_TOKEN_FILE);\n if (tokenFile) {\n return this.readControllerManagedToken(tokenFile);\n }\n\n const environmentToken = normalizeToken(this.environment.UNS_SERVICE_TOKEN);\n if (environmentToken) {\n return environmentToken;\n }\n\n if (this.configToken) {\n return this.configToken;\n }\n\n return this.fallback?.getAccessToken();\n }\n\n private async readControllerManagedToken(path: string): Promise<string> {\n try {\n const token = normalizeToken(await this.readFile(path, \"utf8\"));\n if (!token) {\n throw new Error(\"empty token file\");\n }\n return token;\n } catch (error) {\n throw new Error(\"Controller-managed service token file is unavailable.\", {\n cause: error instanceof Error ? error : undefined,\n });\n }\n }\n}\n\nfunction normalizeToken(value: string | undefined): string | undefined {\n const token = value?.trim();\n return token ? token : undefined;\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AuthClient } from "../auth/auth-client.js";
|
|
2
|
+
import type { AccessTokenProvider } from "../auth/service-token-provider.js";
|
|
2
3
|
export type LastValuePayload = {
|
|
3
4
|
topic: string;
|
|
4
5
|
value: unknown;
|
|
@@ -93,6 +94,8 @@ export type UnsClientOptions = {
|
|
|
93
94
|
apiBasePath?: string;
|
|
94
95
|
token?: string;
|
|
95
96
|
timeoutMs?: number;
|
|
97
|
+
/** Preferred non-interactive service credential source. */
|
|
98
|
+
tokenProvider?: AccessTokenProvider;
|
|
96
99
|
authClient?: AuthClient;
|
|
97
100
|
};
|
|
98
101
|
export type RequestOptions = {
|
|
@@ -104,6 +107,7 @@ export declare class UnsClient {
|
|
|
104
107
|
private readonly baseUrl;
|
|
105
108
|
private readonly apiUrl;
|
|
106
109
|
private readonly timeoutMs;
|
|
110
|
+
private readonly tokenProvider?;
|
|
107
111
|
private readonly authClient?;
|
|
108
112
|
private manualAccessToken?;
|
|
109
113
|
constructor(baseUrl: string, options?: UnsClientOptions);
|