@uns-kit/core 3.0.46 → 3.0.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/MIGRATIONS.md +29 -0
  2. package/README.md +24 -22
  3. package/dist/runtime/catalog-publication-client.d.ts +21 -0
  4. package/dist/runtime/catalog-publication-client.d.ts.map +1 -0
  5. package/dist/runtime/catalog-publication-client.js +92 -0
  6. package/dist/runtime/catalog-publication-client.js.map +1 -0
  7. package/dist/runtime/catalog-publication.d.ts +97 -0
  8. package/dist/runtime/catalog-publication.d.ts.map +1 -0
  9. package/dist/runtime/catalog-publication.js +185 -0
  10. package/dist/runtime/catalog-publication.js.map +1 -0
  11. package/dist/runtime/service-access-examples/README.md +20 -0
  12. package/dist/runtime/service-access-examples/background-reader/README.md +25 -0
  13. package/dist/runtime/service-access-examples/background-reader/build.mjs +7 -0
  14. package/dist/runtime/service-access-examples/background-reader/config-example.json +22 -0
  15. package/dist/runtime/service-access-examples/background-reader/package.json +28 -0
  16. package/dist/runtime/service-access-examples/background-reader/service-access.json +11 -0
  17. package/dist/runtime/service-access-examples/background-reader/src/index.mjs +55 -0
  18. package/dist/runtime/service-access-examples/background-reader/src/reader.mjs +21 -0
  19. package/dist/runtime/service-access-examples/catalog-summary/README.md +26 -0
  20. package/dist/runtime/service-access-examples/catalog-summary/build.mjs +7 -0
  21. package/dist/runtime/service-access-examples/catalog-summary/config-example.json +23 -0
  22. package/dist/runtime/service-access-examples/catalog-summary/package.json +29 -0
  23. package/dist/runtime/service-access-examples/catalog-summary/service-access.json +65 -0
  24. package/dist/runtime/service-access-examples/catalog-summary/src/index.mjs +50 -0
  25. package/dist/runtime/service-access-examples/catalog-summary/src/offer.mjs +35 -0
  26. package/dist/runtime/service-access-guide.md +246 -0
  27. package/dist/runtime/service-access.d.ts +23 -0
  28. package/dist/runtime/service-access.d.ts.map +1 -0
  29. package/dist/runtime/service-access.js +62 -0
  30. package/dist/runtime/service-access.js.map +1 -0
  31. package/dist/runtime/service-access.test.d.ts +2 -0
  32. package/dist/runtime/service-access.test.d.ts.map +1 -0
  33. package/dist/runtime/service-access.test.js +57 -0
  34. package/dist/runtime/service-access.test.js.map +1 -0
  35. package/package.json +14 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-access.d.ts","sourceRoot":"","sources":["../../src/runtime/service-access.ts"],"names":[],"mappings":"AAEA,OAAO,EAAwB,KAAK,0BAA0B,EAAmC,MAAM,0BAA0B,CAAC;AAGlI,eAAO,MAAM,uBAAuB,IAAI,CAAC;AACzC,eAAO,MAAM,6BAA6B,kBAAkB,CAAC;AAI7D,eAAO,MAAM,uBAAuB,6HAM1B,CAAC;AAaX,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,EAAE,CAAC,CAAC;IACZ,WAAW,EAAE;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,WAAW,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACzD,YAAY,CAAC,EAAE,0BAA0B,CAAC;CAC3C,CAAC;AACF,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,wBAAwB,CAQtF;AACD,eAAO,MAAM,mBAAmB,4BAAiB,CAAC;AAClD;qFACqF;AACrF,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,wBAAwB;;;EAKzE;AACD,oFAAoF;AACpF,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAU7E"}
@@ -0,0 +1,62 @@
1
+ import { z } from "zod";
2
+ import { CATALOG_SOURCE_SCOPE, parseCatalogPublicationManifest } from "./catalog-publication.js";
3
+ import { operatorDigest, operatorJson } from "./operator-api.js";
4
+ export const SERVICE_ACCESS_PROTOCOL = 1;
5
+ export const SERVICE_ACCESS_MANIFEST_FIELD = "serviceAccess";
6
+ // Only existing platform data/runtime capabilities. Administrative authority is
7
+ // never a package-requestable scope. UNS rules bound direct UNS reads; other
8
+ // runtime capabilities retain their existing platform authorization semantics.
9
+ export const SERVICE_RESOURCE_SCOPES = [
10
+ "read:uns",
11
+ "identity:asset:publish",
12
+ "runtime:captures:read",
13
+ "runtime:captures:write",
14
+ "runtime:triggers:read",
15
+ ];
16
+ const rule = z
17
+ .string()
18
+ .min(1)
19
+ .max(512)
20
+ .refine((s) => s.split("/").every((p, i, a) => p === "+" || (p === "#" && i === a.length - 1) || /^[A-Za-z0-9-]+$/.test(p)));
21
+ const envelope = z
22
+ .object({
23
+ protocol: z.literal(1),
24
+ permissions: z.object({ scopes: z.array(z.enum(SERVICE_RESOURCE_SCOPES)).max(5), accessRules: z.array(rule).max(64) }).strict(),
25
+ publications: z.unknown().optional(),
26
+ })
27
+ .strict();
28
+ export function parseServiceAccessDeclaration(value) {
29
+ operatorJson(value);
30
+ const parsed = envelope.parse(value);
31
+ const permissions = { scopes: [...new Set(parsed.permissions.scopes)].sort(), accessRules: [...new Set(parsed.permissions.accessRules)].sort() };
32
+ if (!!permissions.scopes.length !== !!permissions.accessRules.length)
33
+ throw Error("service_access_resources_required");
34
+ const publications = parsed.publications === undefined ? undefined : parseCatalogPublicationManifest(parsed.publications);
35
+ if (!permissions.scopes.length && !publications)
36
+ throw Error("service_access_capability_required");
37
+ return { protocol: 1, permissions, ...(publications ? { publications } : {}) };
38
+ }
39
+ export const serviceAccessDigest = operatorDigest;
40
+ /** Publication source rules are deliberately not raw UNS permissions. A service
41
+ * combining a background reader with a catalog must not gain their cross-product. */
42
+ export function serviceAccessCeiling(declaration) {
43
+ return {
44
+ scopes: [...declaration.permissions.scopes, ...(declaration.publications ? [CATALOG_SOURCE_SCOPE] : [])].sort(),
45
+ accessRules: [...declaration.permissions.accessRules],
46
+ };
47
+ }
48
+ /** Conservative MQTT filter inclusion, not matching a filter as if it were data. */
49
+ export function serviceRuleWithin(requested, allowed) {
50
+ const a = allowed.split("/"), r = requested.split("/");
51
+ let i = 0;
52
+ for (; i < a.length; i++) {
53
+ if (a[i] === "#")
54
+ return true;
55
+ if (r[i] === undefined || r[i] === "#")
56
+ return false;
57
+ if (a[i] !== r[i] && a[i] !== "+")
58
+ return false;
59
+ }
60
+ return i === r.length;
61
+ }
62
+ //# sourceMappingURL=service-access.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-access.js","sourceRoot":"","sources":["../../src/runtime/service-access.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\n\nimport { CATALOG_SOURCE_SCOPE, type CatalogPublicationManifest, parseCatalogPublicationManifest } from \"./catalog-publication.js\";\nimport { operatorDigest, operatorJson } from \"./operator-api.js\";\n\nexport const SERVICE_ACCESS_PROTOCOL = 1;\nexport const SERVICE_ACCESS_MANIFEST_FIELD = \"serviceAccess\";\n// Only existing platform data/runtime capabilities. Administrative authority is\n// never a package-requestable scope. UNS rules bound direct UNS reads; other\n// runtime capabilities retain their existing platform authorization semantics.\nexport const SERVICE_RESOURCE_SCOPES = [\n \"read:uns\",\n \"identity:asset:publish\",\n \"runtime:captures:read\",\n \"runtime:captures:write\",\n \"runtime:triggers:read\",\n] as const;\nconst rule = z\n .string()\n .min(1)\n .max(512)\n .refine((s) => s.split(\"/\").every((p, i, a) => p === \"+\" || (p === \"#\" && i === a.length - 1) || /^[A-Za-z0-9-]+$/.test(p)));\nconst envelope = z\n .object({\n protocol: z.literal(1),\n permissions: z.object({ scopes: z.array(z.enum(SERVICE_RESOURCE_SCOPES)).max(5), accessRules: z.array(rule).max(64) }).strict(),\n publications: z.unknown().optional(),\n })\n .strict();\nexport type ServiceAccessDeclaration = {\n protocol: 1;\n permissions: { scopes: string[]; accessRules: string[] };\n publications?: CatalogPublicationManifest;\n};\nexport function parseServiceAccessDeclaration(value: unknown): ServiceAccessDeclaration {\n operatorJson(value);\n const parsed = envelope.parse(value);\n const permissions = { scopes: [...new Set(parsed.permissions.scopes)].sort(), accessRules: [...new Set(parsed.permissions.accessRules)].sort() };\n if (!!permissions.scopes.length !== !!permissions.accessRules.length) throw Error(\"service_access_resources_required\");\n const publications = parsed.publications === undefined ? undefined : parseCatalogPublicationManifest(parsed.publications);\n if (!permissions.scopes.length && !publications) throw Error(\"service_access_capability_required\");\n return { protocol: 1, permissions, ...(publications ? { publications } : {}) };\n}\nexport const serviceAccessDigest = operatorDigest;\n/** Publication source rules are deliberately not raw UNS permissions. A service\n * combining a background reader with a catalog must not gain their cross-product. */\nexport function serviceAccessCeiling(declaration: ServiceAccessDeclaration) {\n return {\n scopes: [...declaration.permissions.scopes, ...(declaration.publications ? [CATALOG_SOURCE_SCOPE] : [])].sort(),\n accessRules: [...declaration.permissions.accessRules],\n };\n}\n/** Conservative MQTT filter inclusion, not matching a filter as if it were data. */\nexport function serviceRuleWithin(requested: string, allowed: string): boolean {\n const a = allowed.split(\"/\"),\n r = requested.split(\"/\");\n let i = 0;\n for (; i < a.length; i++) {\n if (a[i] === \"#\") return true;\n if (r[i] === undefined || r[i] === \"#\") return false;\n if (a[i] !== r[i] && a[i] !== \"+\") return false;\n }\n return i === r.length;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=service-access.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-access.test.d.ts","sourceRoot":"","sources":["../../src/runtime/service-access.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,57 @@
1
+ import { expect, it } from "vitest";
2
+ import { parseServiceAccessDeclaration, serviceAccessCeiling, serviceAccessDigest } from "./service-access.js";
3
+ import { serviceRuleWithin } from "./service-access.js";
4
+ const background = { protocol: 1, permissions: { scopes: ["read:uns"], accessRules: ["factory/sensors/#"] } };
5
+ it("supports an unrelated background service without catalog or HTTP", () => {
6
+ const d = parseServiceAccessDeclaration(background);
7
+ expect(d.publications).toBeUndefined();
8
+ expect(serviceAccessCeiling(d)).toEqual(background.permissions);
9
+ });
10
+ it.each(["admin:all", "catalog:sources:read", "write:anything"])("rejects package-requested privileged or unknown scope %s", (scope) => {
11
+ expect(() => parseServiceAccessDeclaration({ ...background, permissions: { ...background.permissions, scopes: [scope] } })).toThrow();
12
+ });
13
+ it.each([
14
+ { protocol: 2 },
15
+ { unknown: true },
16
+ { permissions: { scopes: ["read:uns"], accessRules: [] } },
17
+ { permissions: { scopes: [], accessRules: [] } },
18
+ { permissions: { scopes: ["read:uns"], accessRules: ["factory/#/escape"] } },
19
+ ])("fails closed on malformed declaration %j", (patch) => expect(() => parseServiceAccessDeclaration({ ...background, ...patch })).toThrow());
20
+ it("normalizes duplicate requirements for stable review digests", () => expect(serviceAccessDigest(parseServiceAccessDeclaration({
21
+ ...background,
22
+ permissions: { scopes: ["read:uns", "read:uns"], accessRules: ["factory/sensors/#", "factory/sensors/#"] },
23
+ }))).toBe(serviceAccessDigest(parseServiceAccessDeclaration(background))));
24
+ it.each([
25
+ ["plant/a", "plant/#", true],
26
+ ["plant/+", "plant/#", true],
27
+ ["plant/#", "plant/+", false],
28
+ ["#", "plant/#", false],
29
+ ["other/a", "plant/#", false],
30
+ ])("validates resource narrowing %s within %s", (r, a, expected) => expect(serviceRuleWithin(r, a)).toBe(expected));
31
+ it("does not combine publication sources with raw background rules", () => {
32
+ const d = parseServiceAccessDeclaration({
33
+ ...background,
34
+ publications: {
35
+ protocol: 1,
36
+ operations: [
37
+ {
38
+ id: "summary",
39
+ title: "Summary",
40
+ method: "GET",
41
+ path: "/api/derived/summary",
42
+ mode: "approved-service",
43
+ requestSchema: {
44
+ type: "object",
45
+ additionalProperties: false,
46
+ required: ["from", "to"],
47
+ properties: { from: { type: "string", format: "date-time" }, to: { type: "string", format: "date-time" } },
48
+ },
49
+ responseSchema: { type: "integer" },
50
+ sources: [{ id: "history", kind: "history", topic: "private/published" }],
51
+ },
52
+ ],
53
+ },
54
+ });
55
+ expect(serviceAccessCeiling(d)).toEqual({ scopes: ["catalog:sources:read", "read:uns"], accessRules: ["factory/sensors/#"] });
56
+ });
57
+ //# sourceMappingURL=service-access.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-access.test.js","sourceRoot":"","sources":["../../src/runtime/service-access.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, it } from \"vitest\";\n\nimport { parseServiceAccessDeclaration, serviceAccessCeiling, serviceAccessDigest } from \"./service-access.js\";\nimport { serviceRuleWithin } from \"./service-access.js\";\n\nconst background = { protocol: 1, permissions: { scopes: [\"read:uns\"], accessRules: [\"factory/sensors/#\"] } };\nit(\"supports an unrelated background service without catalog or HTTP\", () => {\n const d = parseServiceAccessDeclaration(background);\n expect(d.publications).toBeUndefined();\n expect(serviceAccessCeiling(d)).toEqual(background.permissions);\n});\nit.each([\"admin:all\", \"catalog:sources:read\", \"write:anything\"])(\"rejects package-requested privileged or unknown scope %s\", (scope) => {\n expect(() => parseServiceAccessDeclaration({ ...background, permissions: { ...background.permissions, scopes: [scope] } })).toThrow();\n});\nit.each([\n { protocol: 2 },\n { unknown: true },\n { permissions: { scopes: [\"read:uns\"], accessRules: [] } },\n { permissions: { scopes: [], accessRules: [] } },\n { permissions: { scopes: [\"read:uns\"], accessRules: [\"factory/#/escape\"] } },\n])(\"fails closed on malformed declaration %j\", (patch) => expect(() => parseServiceAccessDeclaration({ ...background, ...patch })).toThrow());\nit(\"normalizes duplicate requirements for stable review digests\", () =>\n expect(\n serviceAccessDigest(\n parseServiceAccessDeclaration({\n ...background,\n permissions: { scopes: [\"read:uns\", \"read:uns\"], accessRules: [\"factory/sensors/#\", \"factory/sensors/#\"] },\n }),\n ),\n ).toBe(serviceAccessDigest(parseServiceAccessDeclaration(background))));\n\nit.each([\n [\"plant/a\", \"plant/#\", true],\n [\"plant/+\", \"plant/#\", true],\n [\"plant/#\", \"plant/+\", false],\n [\"#\", \"plant/#\", false],\n [\"other/a\", \"plant/#\", false],\n])(\"validates resource narrowing %s within %s\", (r, a, expected) => expect(serviceRuleWithin(r as string, a as string)).toBe(expected));\nit(\"does not combine publication sources with raw background rules\", () => {\n const d = parseServiceAccessDeclaration({\n ...background,\n publications: {\n protocol: 1,\n operations: [\n {\n id: \"summary\",\n title: \"Summary\",\n method: \"GET\",\n path: \"/api/derived/summary\",\n mode: \"approved-service\",\n requestSchema: {\n type: \"object\",\n additionalProperties: false,\n required: [\"from\", \"to\"],\n properties: { from: { type: \"string\", format: \"date-time\" }, to: { type: \"string\", format: \"date-time\" } },\n },\n responseSchema: { type: \"integer\" },\n sources: [{ id: \"history\", kind: \"history\", topic: \"private/published\" }],\n },\n ],\n },\n });\n expect(serviceAccessCeiling(d)).toEqual({ scopes: [\"catalog:sources:read\", \"read:uns\"], accessRules: [\"factory/sensors/#\"] });\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uns-kit/core",
3
- "version": "3.0.46",
3
+ "version": "3.0.48",
4
4
  "description": "Core utilities and runtime building blocks for UNS-based realtime transformers.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -23,6 +23,19 @@
23
23
  "MIGRATIONS.md"
24
24
  ],
25
25
  "exports": {
26
+ "./runtime/service-access.js": {
27
+ "types": "./dist/runtime/service-access.d.ts",
28
+ "import": "./dist/runtime/service-access.js"
29
+ },
30
+ "./runtime/catalog-publication.js": {
31
+ "types": "./dist/runtime/catalog-publication.d.ts",
32
+ "import": "./dist/runtime/catalog-publication.js"
33
+ },
34
+ "./runtime/catalog-publication-client.js": {
35
+ "types": "./dist/runtime/catalog-publication-client.d.ts",
36
+ "import": "./dist/runtime/catalog-publication-client.js"
37
+ },
38
+ "./runtime/service-access-guide.md": "./dist/runtime/service-access-guide.md",
26
39
  "./runtime/retained-store.js": {
27
40
  "types": "./dist/runtime/retained-store.d.ts",
28
41
  "import": "./dist/runtime/retained-store.js"