@uns-kit/core 3.0.28 → 3.0.30

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 (103) hide show
  1. package/MIGRATIONS.md +100 -0
  2. package/README.md +10 -0
  3. package/dist/config/app-config.d.ts +14 -0
  4. package/dist/config/app-config.d.ts.map +1 -1
  5. package/dist/config/app-config.js.map +1 -1
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/runtime/configuration-group-authorization.d.ts +50 -0
  11. package/dist/runtime/configuration-group-authorization.d.ts.map +1 -0
  12. package/dist/runtime/configuration-group-authorization.js +113 -0
  13. package/dist/runtime/configuration-group-authorization.js.map +1 -0
  14. package/dist/runtime/configuration-group-contract.d.ts +75 -0
  15. package/dist/runtime/configuration-group-contract.d.ts.map +1 -0
  16. package/dist/runtime/configuration-group-contract.js +233 -0
  17. package/dist/runtime/configuration-group-contract.js.map +1 -0
  18. package/dist/runtime/process-group-authorization.d.ts +2 -0
  19. package/dist/runtime/process-group-authorization.d.ts.map +1 -1
  20. package/dist/runtime/process-group-authorization.js +34 -12
  21. package/dist/runtime/process-group-authorization.js.map +1 -1
  22. package/dist/runtime/process-group-backup-contract.d.ts +22 -0
  23. package/dist/runtime/process-group-backup-contract.d.ts.map +1 -0
  24. package/dist/runtime/process-group-backup-contract.js +20 -0
  25. package/dist/runtime/process-group-backup-contract.js.map +1 -0
  26. package/dist/runtime/process-group-backup-contract.json +7 -0
  27. package/dist/runtime/process-group-configured-upgrade.d.ts +4 -0
  28. package/dist/runtime/process-group-configured-upgrade.d.ts.map +1 -0
  29. package/dist/runtime/process-group-configured-upgrade.js +107 -0
  30. package/dist/runtime/process-group-configured-upgrade.js.map +1 -0
  31. package/dist/runtime/process-group-generation.d.ts +166 -0
  32. package/dist/runtime/process-group-generation.d.ts.map +1 -0
  33. package/dist/runtime/process-group-generation.js +278 -0
  34. package/dist/runtime/process-group-generation.js.map +1 -0
  35. package/dist/runtime/process-group-inspection.d.ts +2 -0
  36. package/dist/runtime/process-group-inspection.d.ts.map +1 -1
  37. package/dist/runtime/process-group-inspection.js +120 -40
  38. package/dist/runtime/process-group-inspection.js.map +1 -1
  39. package/dist/runtime/process-group-resources.d.ts +1 -0
  40. package/dist/runtime/process-group-resources.d.ts.map +1 -1
  41. package/dist/runtime/process-group-resources.js +8 -0
  42. package/dist/runtime/process-group-resources.js.map +1 -1
  43. package/dist/runtime/process-group-runtime-selection.d.ts +14 -0
  44. package/dist/runtime/process-group-runtime-selection.d.ts.map +1 -0
  45. package/dist/runtime/process-group-runtime-selection.js +29 -0
  46. package/dist/runtime/process-group-runtime-selection.js.map +1 -0
  47. package/dist/runtime/process-group-snapshot.mjs +60 -8
  48. package/dist/runtime/process-group-staging.d.ts +2 -2
  49. package/dist/runtime/process-group-staging.d.ts.map +1 -1
  50. package/dist/runtime/process-group-staging.js +6 -4
  51. package/dist/runtime/process-group-staging.js.map +1 -1
  52. package/dist/runtime/process-group-supervision.d.ts +2 -2
  53. package/dist/runtime/process-group-supervision.d.ts.map +1 -1
  54. package/dist/runtime/process-group-supervision.js +3 -2
  55. package/dist/runtime/process-group-supervision.js.map +1 -1
  56. package/dist/runtime/process-group-upgrade.d.ts +76 -6
  57. package/dist/runtime/process-group-upgrade.d.ts.map +1 -1
  58. package/dist/runtime/process-group-upgrade.js +7 -1
  59. package/dist/runtime/process-group-upgrade.js.map +1 -1
  60. package/dist/uns/uns-interfaces.d.ts +3 -0
  61. package/dist/uns/uns-interfaces.d.ts.map +1 -1
  62. package/dist/uns/uns-interfaces.js.map +1 -1
  63. package/dist/uns/uns-proxy-process.d.ts.map +1 -1
  64. package/dist/uns/uns-proxy-process.js +3 -0
  65. package/dist/uns/uns-proxy-process.js.map +1 -1
  66. package/dist/uns-config/config-schema.d.ts +16 -0
  67. package/dist/uns-config/config-schema.d.ts.map +1 -1
  68. package/dist/uns-config/mqtt-channel.d.ts +1 -1
  69. package/dist/uns-config/mqtt-channel.d.ts.map +1 -1
  70. package/dist/uns-config/mqtt-channel.js +3 -0
  71. package/dist/uns-config/mqtt-channel.js.map +1 -1
  72. package/dist/uns-config/uns-core-schema.d.ts +137 -0
  73. package/dist/uns-config/uns-core-schema.d.ts.map +1 -1
  74. package/dist/uns-config/uns-core-schema.js +6 -1
  75. package/dist/uns-config/uns-core-schema.js.map +1 -1
  76. package/dist/uns-mqtt/mqtt-interfaces.d.ts +5 -0
  77. package/dist/uns-mqtt/mqtt-interfaces.d.ts.map +1 -1
  78. package/dist/uns-mqtt/mqtt-interfaces.js.map +1 -1
  79. package/dist/uns-mqtt/mqtt-proxy.d.ts +3 -0
  80. package/dist/uns-mqtt/mqtt-proxy.d.ts.map +1 -1
  81. package/dist/uns-mqtt/mqtt-proxy.js +26 -1
  82. package/dist/uns-mqtt/mqtt-proxy.js.map +1 -1
  83. package/dist/uns-mqtt/mqtt-worker.d.ts +3 -1
  84. package/dist/uns-mqtt/mqtt-worker.d.ts.map +1 -1
  85. package/dist/uns-mqtt/mqtt-worker.js +45 -8
  86. package/dist/uns-mqtt/mqtt-worker.js.map +1 -1
  87. package/dist/uns-mqtt/publish-queue-contract.d.ts +33 -0
  88. package/dist/uns-mqtt/publish-queue-contract.d.ts.map +1 -0
  89. package/dist/uns-mqtt/publish-queue-contract.js +18 -0
  90. package/dist/uns-mqtt/publish-queue-contract.js.map +1 -0
  91. package/dist/uns-mqtt/publish-queue-store.d.ts +33 -0
  92. package/dist/uns-mqtt/publish-queue-store.d.ts.map +1 -0
  93. package/dist/uns-mqtt/publish-queue-store.js +177 -0
  94. package/dist/uns-mqtt/publish-queue-store.js.map +1 -0
  95. package/dist/uns-mqtt/publish-queue.d.ts +43 -0
  96. package/dist/uns-mqtt/publish-queue.d.ts.map +1 -0
  97. package/dist/uns-mqtt/publish-queue.js +235 -0
  98. package/dist/uns-mqtt/publish-queue.js.map +1 -0
  99. package/dist/uns-mqtt/uns-mqtt-proxy.d.ts +7 -0
  100. package/dist/uns-mqtt/uns-mqtt-proxy.d.ts.map +1 -1
  101. package/dist/uns-mqtt/uns-mqtt-proxy.js +98 -5
  102. package/dist/uns-mqtt/uns-mqtt-proxy.js.map +1 -1
  103. package/package.json +1 -1
@@ -0,0 +1,20 @@
1
+ import { z } from "zod";
2
+ import contract from "./process-group-backup-contract.json" with { type: "json" };
3
+ /** SDK-owned fixed resource budget; hosts never accept it from an operation request. */
4
+ export const processGroupBackupLimits = Object.freeze(z.strictObject({
5
+ schemaVersion: z.literal(1),
6
+ maximumJournals: z.number().int().min(1).max(128),
7
+ maximumManifestBytes: z.literal(262144),
8
+ maximumSnapshotBytes: z.literal(536870912),
9
+ maximumCapsuleBytes: z.literal(1073741824),
10
+ }).parse(contract));
11
+ /** Public coverage only; identities, checkpoints and payloads remain encrypted. */
12
+ export const processGroupStateBackupSchema = z.strictObject({
13
+ schemaVersion: z.literal(1),
14
+ state: z.enum(["included", "empty"]),
15
+ rootSha256: z.string().regex(/^[a-f0-9]{64}$/),
16
+ journals: z.number().int().min(0).max(processGroupBackupLimits.maximumJournals),
17
+ bytes: z.number().int().positive().max(processGroupBackupLimits.maximumCapsuleBytes),
18
+ sha256: z.string().regex(/^[a-f0-9]{64}$/),
19
+ }).refine(v => v.state === "included" ? v.journals > 0 : v.journals === 0);
20
+ //# sourceMappingURL=process-group-backup-contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-group-backup-contract.js","sourceRoot":"","sources":["../../src/runtime/process-group-backup-contract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,QAAQ,MAAM,sCAAsC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAElF,wFAAwF;AACxF,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC;IACnE,aAAa,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3B,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;IACjD,oBAAoB,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;IACvC,oBAAoB,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;IAC1C,mBAAmB,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC;CAC3C,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;AAEpB,mFAAmF;AACnF,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAC,YAAY,CAAC;IAC1D,aAAa,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3B,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACpC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,gBAAgB,CAAC;IAC9C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,wBAAwB,CAAC,eAAe,CAAC;IAC/E,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,wBAAwB,CAAC,mBAAmB,CAAC;IACpF,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,gBAAgB,CAAC;CAC3C,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC","sourcesContent":["import { z } from \"zod\";\nimport contract from \"./process-group-backup-contract.json\" with { type: \"json\" };\n\n/** SDK-owned fixed resource budget; hosts never accept it from an operation request. */\nexport const processGroupBackupLimits = Object.freeze(z.strictObject({\n schemaVersion: z.literal(1),\n maximumJournals: z.number().int().min(1).max(128),\n maximumManifestBytes: z.literal(262144),\n maximumSnapshotBytes: z.literal(536870912),\n maximumCapsuleBytes: z.literal(1073741824),\n}).parse(contract));\n\n/** Public coverage only; identities, checkpoints and payloads remain encrypted. */\nexport const processGroupStateBackupSchema = z.strictObject({\n schemaVersion: z.literal(1),\n state: z.enum([\"included\", \"empty\"]),\n rootSha256: z.string().regex(/^[a-f0-9]{64}$/),\n journals: z.number().int().min(0).max(processGroupBackupLimits.maximumJournals),\n bytes: z.number().int().positive().max(processGroupBackupLimits.maximumCapsuleBytes),\n sha256: z.string().regex(/^[a-f0-9]{64}$/),\n}).refine(v => v.state === \"included\" ? v.journals > 0 : v.journals === 0);\n"]}
@@ -0,0 +1,7 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "maximumJournals": 32,
4
+ "maximumManifestBytes": 262144,
5
+ "maximumSnapshotBytes": 536870912,
6
+ "maximumCapsuleBytes": 1073741824
7
+ }
@@ -0,0 +1,4 @@
1
+ import type { PreparedGroupUpgrade, UpgradeConfigurations } from "./process-group-upgrade.js";
2
+ /** Same generation, different paired artifacts. Never initializes or rewrites the journal. */
3
+ export declare function reviewInstalledConfigurationGroupUpgrade(configs: UpgradeConfigurations, controllerName: string, stateRoot: string, excludedUnsFields?: readonly string[]): PreparedGroupUpgrade;
4
+ //# sourceMappingURL=process-group-configured-upgrade.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-group-configured-upgrade.d.ts","sourceRoot":"","sources":["../../src/runtime/process-group-configured-upgrade.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAA6B,oBAAoB,EAAwB,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAE/I,8FAA8F;AAC9F,wBAAgB,wCAAwC,CACtD,OAAO,EAAE,qBAAqB,EAC9B,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,MAAM,EACjB,iBAAiB,GAAE,SAAS,MAAM,EAAO,GACxC,oBAAoB,CAyGtB"}
@@ -0,0 +1,107 @@
1
+ import path from "node:path";
2
+ import { configurationGroupAuthorizationSchema } from "./configuration-group-authorization.js";
3
+ import { configurationResourceHash } from "./configuration-group-contract.js";
4
+ import { authorizeProcessGroupRuntime, processGroupAuthorizationFile, validateProcessGroupPolicyCandidate } from "./process-group-authorization.js";
5
+ import { resolvedProcessGroupChannels } from "./process-group-contract.js";
6
+ import { describeInstalledProcessGroupGeneration, inspectGenerationJournal } from "./process-group-generation.js";
7
+ import { groupArtifactHash, groupChannelsHash, configurationAuthorizationHash as hash, readProcessGroupResource as read, sha256, } from "./process-group-resources.js";
8
+ /** Same generation, different paired artifacts. Never initializes or rewrites the journal. */
9
+ export function reviewInstalledConfigurationGroupUpgrade(configs, controllerName, stateRoot, excludedUnsFields = []) {
10
+ const context = (c) => ({
11
+ packageRoot: c.packageRoot,
12
+ instanceDirectory: c.instanceDirectory,
13
+ rawConfiguration: c.raw,
14
+ channels: resolvedProcessGroupChannels(c.resolved),
15
+ controllerName,
16
+ stateRoot,
17
+ });
18
+ const source = configs.source.publisher, target = configs.target.publisher, a = describeInstalledProcessGroupGeneration(source), b = describeInstalledProcessGroupGeneration(target);
19
+ if (a.d.protocolVersion !== 2 || b.d.protocolVersion !== 2 || a.d.namespace !== b.d.namespace || hash(a.reference) !== hash(b.reference))
20
+ throw Error("Unchanged configuration generation required; review resource changes as a separate generation");
21
+ const pkg = (c) => JSON.parse(read(path.join(c.packageRoot, "package.json")).toString()), sourcePackage = pkg(source), targetPackage = pkg(target);
22
+ if (!sourcePackage.version || !targetPackage.version || sourcePackage.version === targetPackage.version)
23
+ throw Error("Two different installed paired versions required");
24
+ const old = authorizeProcessGroupRuntime(context(source));
25
+ const sourcePolicy = read(old.file, true), previous = configurationGroupAuthorizationSchema.parse(JSON.parse(sourcePolicy.toString()));
26
+ const portable = (raw) => {
27
+ const copy = structuredClone(raw), uns = { ...copy["uns"] };
28
+ for (const key of excludedUnsFields)
29
+ delete uns[key];
30
+ copy["uns"] = uns;
31
+ return hash(copy);
32
+ };
33
+ const members = [];
34
+ const directories = new Set();
35
+ for (const side of ["source", "target"])
36
+ for (const role of ["publisher", "receiver"]) {
37
+ const c = configs[side][role], g = describeInstalledProcessGroupGeneration(c), installed = pkg(c);
38
+ if (g.d.protocolVersion !== 2 ||
39
+ g.d.namespace !== a.d.namespace ||
40
+ installed.name !== sourcePackage.name ||
41
+ installed.version !== (side === "source" ? sourcePackage.version : targetPackage.version) ||
42
+ c.packageRoot !== configs[side].publisher.packageRoot ||
43
+ directories.has(c.instanceDirectory) ||
44
+ hash(g.reference) !== hash(a.reference) ||
45
+ g.normalized.uns["processName"] !== (role === "publisher" ? a.processName : a.receiverProcessName) ||
46
+ (role === "receiver"
47
+ ? g.normalized.runtimeRole !== "admission"
48
+ : g.normalized.runtimeRole !== undefined && g.normalized.runtimeRole !== "publisher") ||
49
+ portable(c.raw) !== portable(configs.source[role].raw) ||
50
+ groupChannelsHash(g.channels) !== groupChannelsHash(a.channels) ||
51
+ processGroupAuthorizationFile(context(c)) !== old.file)
52
+ throw Error("Exact paired configuration, resources, broker identity and retained journal required");
53
+ directories.add(c.instanceDirectory);
54
+ if (side === "source") {
55
+ const proof = authorizeProcessGroupRuntime(context(c));
56
+ if (proof.role !== (role === "publisher" ? "target" : "receiver") || proof.sha256 !== sha256(sourcePolicy))
57
+ throw Error("Exact authorized source pair required");
58
+ }
59
+ members.push({
60
+ side,
61
+ role,
62
+ processName: String(g.normalized.uns["processName"]),
63
+ configurationSha256: hash(c.raw),
64
+ artifactSha256: groupArtifactHash(c.packageRoot),
65
+ });
66
+ }
67
+ const policy = configurationGroupAuthorizationSchema.parse({
68
+ ...previous,
69
+ packageRoot: target.packageRoot,
70
+ descriptorSha256: configurationResourceHash(targetPackage.unsDatahub.processGroupConfigurationRuntime),
71
+ artifactSha256: groupArtifactHash(target.packageRoot),
72
+ placements: [
73
+ ...previous.placements.filter((p) => p.role === "source"),
74
+ ...["publisher", "receiver"].map((role) => ({
75
+ instanceId: path.basename(configs.target[role].instanceDirectory),
76
+ processName: members.find((m) => m.side === "target" && m.role === role).processName,
77
+ role: role === "publisher" ? "target" : "receiver",
78
+ configurationSha256: configurationResourceHash(configs.target[role].raw),
79
+ })),
80
+ ],
81
+ });
82
+ const targetPolicy = Buffer.from(JSON.stringify(policy));
83
+ for (const role of ["publisher", "receiver"])
84
+ validateProcessGroupPolicyCandidate(context(configs.target[role]), targetPolicy);
85
+ inspectGenerationJournal(a.reference); // Pending, previously completed work is valid and must remain bound.
86
+ if (sha256(read(old.file, true)) !== sha256(sourcePolicy))
87
+ throw Error("Source policy changed during review");
88
+ return {
89
+ review: {
90
+ protocolVersion: 1,
91
+ namespace: a.d.namespace,
92
+ groupId: a.id,
93
+ policyFile: old.file,
94
+ sourcePolicySha256: sha256(sourcePolicy),
95
+ targetPolicySha256: sha256(targetPolicy),
96
+ journalId: a.reference.journalId,
97
+ directory: a.reference.directory,
98
+ journalContractSha256: a.reference.journalContractSha256,
99
+ profiles: a.reference.profiles,
100
+ retainedJournal: a.reference,
101
+ members,
102
+ },
103
+ sourcePolicy,
104
+ targetPolicy,
105
+ };
106
+ }
107
+ //# sourceMappingURL=process-group-configured-upgrade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-group-configured-upgrade.js","sourceRoot":"","sources":["../../src/runtime/process-group-configured-upgrade.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,qCAAqC,EAAE,MAAM,wCAAwC,CAAC;AAC/F,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,4BAA4B,EAAE,6BAA6B,EAAE,mCAAmC,EAAE,MAAM,kCAAkC,CAAC;AACpJ,OAAO,EAAE,4BAA4B,EAAE,MAAM,6BAA6B,CAAC;AAC3E,OAAO,EAAE,uCAAuC,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAClH,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,8BAA8B,IAAI,IAAI,EACtC,wBAAwB,IAAI,IAAI,EAChC,MAAM,GACP,MAAM,8BAA8B,CAAC;AAGtC,8FAA8F;AAC9F,MAAM,UAAU,wCAAwC,CACtD,OAA8B,EAC9B,cAAsB,EACtB,SAAiB,EACjB,oBAAuC,EAAE;IAEzC,MAAM,OAAO,GAAG,CAAC,CAAuB,EAAE,EAAE,CAAC,CAAC;QAC5C,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,iBAAiB,EAAE,CAAC,CAAC,iBAAiB;QACtC,gBAAgB,EAAE,CAAC,CAAC,GAAG;QACvB,QAAQ,EAAE,4BAA4B,CAAC,CAAC,CAAC,QAAQ,CAAC;QAClD,cAAc;QACd,SAAS;KACV,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,EACrC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,EACjC,CAAC,GAAG,uCAAuC,CAAC,MAAM,CAAC,EACnD,CAAC,GAAG,uCAAuC,CAAC,MAAM,CAAC,CAAC;IACtD,IAAI,CAAC,CAAC,CAAC,CAAC,eAAe,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,eAAe,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QACtI,MAAM,KAAK,CAAC,+FAA+F,CAAC,CAAC;IAC/G,MAAM,GAAG,GAAG,CAAC,CAAuB,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,EAC5G,aAAa,GAAG,GAAG,CAAC,MAAM,CAAC,EAC3B,aAAa,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,IAAI,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,aAAa,CAAC,OAAO,IAAI,aAAa,CAAC,OAAO,KAAK,aAAa,CAAC,OAAO;QACrG,MAAM,KAAK,CAAC,kDAAkD,CAAC,CAAC;IAClE,MAAM,GAAG,GAAG,4BAA4B,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1D,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,EACvC,QAAQ,GAAG,qCAAqC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAC9F,MAAM,QAAQ,GAAG,CAAC,GAA4B,EAAE,EAAE;QAChD,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,EAC/B,GAAG,GAAG,EAAE,GAAI,IAAI,CAAC,KAAK,CAA6B,EAAE,CAAC;QACxD,KAAK,MAAM,GAAG,IAAI,iBAAiB;YAAE,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;QACrD,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC;QAClB,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC,CAAC;IACF,MAAM,OAAO,GAAyC,EAAE,CAAC;IACzD,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;IACtC,KAAK,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAU;QAC9C,KAAK,MAAM,IAAI,IAAI,CAAC,WAAW,EAAE,UAAU,CAAU,EAAE,CAAC;YACtD,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAC3B,CAAC,GAAG,uCAAuC,CAAC,CAAC,CAAC,EAC9C,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;YACrB,IACE,CAAC,CAAC,CAAC,CAAC,eAAe,KAAK,CAAC;gBACzB,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;gBAC/B,SAAS,CAAC,IAAI,KAAK,aAAa,CAAC,IAAI;gBACrC,SAAS,CAAC,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC;gBACzF,CAAC,CAAC,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,WAAW;gBACrD,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,iBAAiB,CAAC;gBACpC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;gBACvC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC;gBAClG,CAAC,IAAI,KAAK,UAAU;oBAClB,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW,KAAK,WAAW;oBAC1C,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW,KAAK,SAAS,IAAI,CAAC,CAAC,UAAU,CAAC,WAAW,KAAK,WAAW,CAAC;gBACvF,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC;gBACtD,iBAAiB,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,iBAAiB,CAAC,CAAC,CAAC,QAAQ,CAAC;gBAC/D,6BAA6B,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,IAAI;gBAEtD,MAAM,KAAK,CAAC,sFAAsF,CAAC,CAAC;YACtG,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC;YACrC,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACtB,MAAM,KAAK,GAAG,4BAA4B,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;gBACvD,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,YAAY,CAAC;oBACxG,MAAM,KAAK,CAAC,uCAAuC,CAAC,CAAC;YACzD,CAAC;YACD,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI;gBACJ,IAAI;gBACJ,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;gBACpD,mBAAmB,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;gBAChC,cAAc,EAAE,iBAAiB,CAAC,CAAC,CAAC,WAAW,CAAC;aACjD,CAAC,CAAC;QACL,CAAC;IACH,MAAM,MAAM,GAAG,qCAAqC,CAAC,KAAK,CAAC;QACzD,GAAG,QAAQ;QACX,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,gBAAgB,EAAE,yBAAyB,CAAC,aAAa,CAAC,UAAU,CAAC,gCAAgC,CAAC;QACtG,cAAc,EAAE,iBAAiB,CAAC,MAAM,CAAC,WAAW,CAAC;QACrD,UAAU,EAAE;YACV,GAAG,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;YACzD,GAAI,CAAC,WAAW,EAAE,UAAU,CAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBACrD,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,iBAAiB,CAAC;gBACjE,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAE,CAAC,WAAW;gBACrF,IAAI,EAAE,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU;gBAClD,mBAAmB,EAAE,yBAAyB,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC;aACzE,CAAC,CAAC;SACJ;KACF,CAAC,CAAC;IACH,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IACzD,KAAK,MAAM,IAAI,IAAI,CAAC,WAAW,EAAE,UAAU,CAAU;QAAE,mCAAmC,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;IACxI,wBAAwB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,qEAAqE;IAC5G,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,KAAK,MAAM,CAAC,YAAY,CAAC;QAAE,MAAM,KAAK,CAAC,qCAAqC,CAAC,CAAC;IAC9G,OAAO;QACL,MAAM,EAAE;YACN,eAAe,EAAE,CAAC;YAClB,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS;YACxB,OAAO,EAAE,CAAC,CAAC,EAAE;YACb,UAAU,EAAE,GAAG,CAAC,IAAI;YACpB,kBAAkB,EAAE,MAAM,CAAC,YAAY,CAAC;YACxC,kBAAkB,EAAE,MAAM,CAAC,YAAY,CAAC;YACxC,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,SAAS;YAChC,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,SAAS;YAChC,qBAAqB,EAAE,CAAC,CAAC,SAAS,CAAC,qBAAqB;YACxD,QAAQ,EAAE,CAAC,CAAC,SAAS,CAAC,QAAQ;YAC9B,eAAe,EAAE,CAAC,CAAC,SAAS;YAC5B,OAAO;SACR;QACD,YAAY;QACZ,YAAY;KACb,CAAC;AACJ,CAAC","sourcesContent":["import path from \"node:path\";\n\nimport { configurationGroupAuthorizationSchema } from \"./configuration-group-authorization.js\";\nimport { configurationResourceHash } from \"./configuration-group-contract.js\";\nimport { authorizeProcessGroupRuntime, processGroupAuthorizationFile, validateProcessGroupPolicyCandidate } from \"./process-group-authorization.js\";\nimport { resolvedProcessGroupChannels } from \"./process-group-contract.js\";\nimport { describeInstalledProcessGroupGeneration, inspectGenerationJournal } from \"./process-group-generation.js\";\nimport {\n groupArtifactHash,\n groupChannelsHash,\n configurationAuthorizationHash as hash,\n readProcessGroupResource as read,\n sha256,\n} from \"./process-group-resources.js\";\nimport type { GroupVersionUpgradeReview, PreparedGroupUpgrade, UpgradeConfiguration, UpgradeConfigurations } from \"./process-group-upgrade.js\";\n\n/** Same generation, different paired artifacts. Never initializes or rewrites the journal. */\nexport function reviewInstalledConfigurationGroupUpgrade(\n configs: UpgradeConfigurations,\n controllerName: string,\n stateRoot: string,\n excludedUnsFields: readonly string[] = [],\n): PreparedGroupUpgrade {\n const context = (c: UpgradeConfiguration) => ({\n packageRoot: c.packageRoot,\n instanceDirectory: c.instanceDirectory,\n rawConfiguration: c.raw,\n channels: resolvedProcessGroupChannels(c.resolved),\n controllerName,\n stateRoot,\n });\n const source = configs.source.publisher,\n target = configs.target.publisher,\n a = describeInstalledProcessGroupGeneration(source),\n b = describeInstalledProcessGroupGeneration(target);\n if (a.d.protocolVersion !== 2 || b.d.protocolVersion !== 2 || a.d.namespace !== b.d.namespace || hash(a.reference) !== hash(b.reference))\n throw Error(\"Unchanged configuration generation required; review resource changes as a separate generation\");\n const pkg = (c: UpgradeConfiguration) => JSON.parse(read(path.join(c.packageRoot, \"package.json\")).toString()),\n sourcePackage = pkg(source),\n targetPackage = pkg(target);\n if (!sourcePackage.version || !targetPackage.version || sourcePackage.version === targetPackage.version)\n throw Error(\"Two different installed paired versions required\");\n const old = authorizeProcessGroupRuntime(context(source));\n const sourcePolicy = read(old.file, true),\n previous = configurationGroupAuthorizationSchema.parse(JSON.parse(sourcePolicy.toString()));\n const portable = (raw: Record<string, unknown>) => {\n const copy = structuredClone(raw),\n uns = { ...(copy[\"uns\"] as Record<string, unknown>) };\n for (const key of excludedUnsFields) delete uns[key];\n copy[\"uns\"] = uns;\n return hash(copy);\n };\n const members: GroupVersionUpgradeReview[\"members\"] = [];\n const directories = new Set<string>();\n for (const side of [\"source\", \"target\"] as const)\n for (const role of [\"publisher\", \"receiver\"] as const) {\n const c = configs[side][role],\n g = describeInstalledProcessGroupGeneration(c),\n installed = pkg(c);\n if (\n g.d.protocolVersion !== 2 ||\n g.d.namespace !== a.d.namespace ||\n installed.name !== sourcePackage.name ||\n installed.version !== (side === \"source\" ? sourcePackage.version : targetPackage.version) ||\n c.packageRoot !== configs[side].publisher.packageRoot ||\n directories.has(c.instanceDirectory) ||\n hash(g.reference) !== hash(a.reference) ||\n g.normalized.uns[\"processName\"] !== (role === \"publisher\" ? a.processName : a.receiverProcessName) ||\n (role === \"receiver\"\n ? g.normalized.runtimeRole !== \"admission\"\n : g.normalized.runtimeRole !== undefined && g.normalized.runtimeRole !== \"publisher\") ||\n portable(c.raw) !== portable(configs.source[role].raw) ||\n groupChannelsHash(g.channels) !== groupChannelsHash(a.channels) ||\n processGroupAuthorizationFile(context(c)) !== old.file\n )\n throw Error(\"Exact paired configuration, resources, broker identity and retained journal required\");\n directories.add(c.instanceDirectory);\n if (side === \"source\") {\n const proof = authorizeProcessGroupRuntime(context(c));\n if (proof.role !== (role === \"publisher\" ? \"target\" : \"receiver\") || proof.sha256 !== sha256(sourcePolicy))\n throw Error(\"Exact authorized source pair required\");\n }\n members.push({\n side,\n role,\n processName: String(g.normalized.uns[\"processName\"]),\n configurationSha256: hash(c.raw),\n artifactSha256: groupArtifactHash(c.packageRoot),\n });\n }\n const policy = configurationGroupAuthorizationSchema.parse({\n ...previous,\n packageRoot: target.packageRoot,\n descriptorSha256: configurationResourceHash(targetPackage.unsDatahub.processGroupConfigurationRuntime),\n artifactSha256: groupArtifactHash(target.packageRoot),\n placements: [\n ...previous.placements.filter((p) => p.role === \"source\"),\n ...([\"publisher\", \"receiver\"] as const).map((role) => ({\n instanceId: path.basename(configs.target[role].instanceDirectory),\n processName: members.find((m) => m.side === \"target\" && m.role === role)!.processName,\n role: role === \"publisher\" ? \"target\" : \"receiver\",\n configurationSha256: configurationResourceHash(configs.target[role].raw),\n })),\n ],\n });\n const targetPolicy = Buffer.from(JSON.stringify(policy));\n for (const role of [\"publisher\", \"receiver\"] as const) validateProcessGroupPolicyCandidate(context(configs.target[role]), targetPolicy);\n inspectGenerationJournal(a.reference); // Pending, previously completed work is valid and must remain bound.\n if (sha256(read(old.file, true)) !== sha256(sourcePolicy)) throw Error(\"Source policy changed during review\");\n return {\n review: {\n protocolVersion: 1,\n namespace: a.d.namespace,\n groupId: a.id,\n policyFile: old.file,\n sourcePolicySha256: sha256(sourcePolicy),\n targetPolicySha256: sha256(targetPolicy),\n journalId: a.reference.journalId,\n directory: a.reference.directory,\n journalContractSha256: a.reference.journalContractSha256,\n profiles: a.reference.profiles,\n retainedJournal: a.reference,\n members,\n },\n sourcePolicy,\n targetPolicy,\n };\n}\n"]}
@@ -0,0 +1,166 @@
1
+ import { z } from "zod";
2
+ import type { GroupVersionUpgradeReview, PreparedGroupUpgrade, UpgradeConfiguration, UpgradeConfigurations } from "./process-group-upgrade.js";
3
+ export declare const processGroupGenerationInputSchema: z.ZodObject<{
4
+ protocolVersion: z.ZodLiteral<2>;
5
+ source: z.ZodObject<{
6
+ publisher: z.ZodObject<{
7
+ controller: z.ZodString;
8
+ rttNode: z.ZodString;
9
+ version: z.ZodString;
10
+ instanceId: z.ZodString;
11
+ }, z.core.$strict>;
12
+ receiver: z.ZodObject<{
13
+ controller: z.ZodString;
14
+ rttNode: z.ZodString;
15
+ version: z.ZodString;
16
+ instanceId: z.ZodString;
17
+ }, z.core.$strict>;
18
+ }, z.core.$strict>;
19
+ target: z.ZodObject<{
20
+ publisher: z.ZodObject<{
21
+ controller: z.ZodString;
22
+ rttNode: z.ZodString;
23
+ version: z.ZodString;
24
+ instanceId: z.ZodString;
25
+ }, z.core.$strict>;
26
+ receiver: z.ZodObject<{
27
+ controller: z.ZodString;
28
+ rttNode: z.ZodString;
29
+ version: z.ZodString;
30
+ instanceId: z.ZodString;
31
+ }, z.core.$strict>;
32
+ }, z.core.$strict>;
33
+ acknowledgeInputPause: z.ZodLiteral<true>;
34
+ acknowledgeResourceReview: z.ZodLiteral<true>;
35
+ acknowledgePossibleReplay: z.ZodLiteral<true>;
36
+ }, z.core.$strict>;
37
+ export type ProcessGroupGenerationInput = z.infer<typeof processGroupGenerationInputSchema>;
38
+ export type GenerationJournalReference = {
39
+ directory: string;
40
+ journalId: string;
41
+ journalContractSha256: string;
42
+ generationId: string;
43
+ groupSha256?: string;
44
+ contractVersion: 1 | 2;
45
+ maxRecords: number;
46
+ maxBytes: number;
47
+ minFreeBytes: number;
48
+ profiles: Array<{
49
+ processName: string;
50
+ mappingSha256: string;
51
+ dataGroup: string;
52
+ }>;
53
+ };
54
+ export type GroupGenerationReview = Omit<GroupVersionUpgradeReview, "protocolVersion"> & {
55
+ protocolVersion: 2;
56
+ sourcePolicyFile: string;
57
+ sourceJournal: GenerationJournalReference;
58
+ targetJournal: GenerationJournalReference;
59
+ delivery: {
60
+ inputPausePossible: true;
61
+ retainedBrokerSessionMayReplay: true;
62
+ exactlyOnceOutput: false;
63
+ };
64
+ resourceChanges: Array<{
65
+ processName: string;
66
+ beforeSha256: string;
67
+ afterSha256: string;
68
+ dataGroup: string;
69
+ }>;
70
+ };
71
+ /** Bounded read-only storage evidence; no initializer, rewriting or replay. */
72
+ export declare function inspectGenerationJournal(reference: GenerationJournalReference): {
73
+ records: number;
74
+ bytes: number;
75
+ admitted: number;
76
+ completed: number;
77
+ lastSequence: number;
78
+ };
79
+ /** Recheck immediately before the first launch; initialization is never silently reused. */
80
+ export declare function inspectPristineGenerationJournal(reference: GenerationJournalReference): {
81
+ records: number;
82
+ bytes: number;
83
+ admitted: number;
84
+ completed: number;
85
+ lastSequence: number;
86
+ };
87
+ /** Data-only normalized revision and immutable journal contract shared by reviewed transitions. */
88
+ export declare function describeInstalledProcessGroupGeneration(c: UpgradeConfiguration): {
89
+ d: {
90
+ protocolVersion: 2;
91
+ namespace: string;
92
+ publisherPath: string[];
93
+ groupPath: string[];
94
+ memberPath: string[];
95
+ journalPath: string[];
96
+ runtimeRolePath: string[];
97
+ receiverRole: string;
98
+ match?: {
99
+ path: string[];
100
+ value: string;
101
+ };
102
+ };
103
+ normalized: {
104
+ matches: boolean;
105
+ uns: Record<string, unknown>;
106
+ publisher: Record<string, unknown>;
107
+ journal: unknown;
108
+ runtimeRole: unknown;
109
+ configurationSnapshot: import("./configuration-group-contract.js").ConfigurationGroupSnapshot;
110
+ };
111
+ channels: import("../index.js").ResolvedMqttChannel[];
112
+ id: string;
113
+ processName: string;
114
+ receiverProcessName: string;
115
+ inputTopic: string;
116
+ reference: {
117
+ directory: string;
118
+ journalId: string;
119
+ generationId: string;
120
+ groupSha256: string;
121
+ contractVersion: 2;
122
+ journalContractSha256: string;
123
+ maxRecords: number;
124
+ maxBytes: number;
125
+ minFreeBytes: number;
126
+ profiles: {
127
+ processName: string;
128
+ mappingSha256: string;
129
+ dataGroup: string;
130
+ }[];
131
+ };
132
+ } | {
133
+ d: import("./process-group-contract.js").ProcessGroupRuntimeDescriptor;
134
+ normalized: {
135
+ matches: boolean;
136
+ uns: Record<string, unknown>;
137
+ publisher: Record<string, unknown>;
138
+ journal: unknown;
139
+ runtimeRole: unknown;
140
+ configurationSnapshot: import("./configuration-group-contract.js").ConfigurationGroupSnapshot;
141
+ };
142
+ channels: import("../index.js").ResolvedMqttChannel[];
143
+ id: string;
144
+ processName: string;
145
+ receiverProcessName: string;
146
+ inputTopic: string;
147
+ reference: {
148
+ directory: string;
149
+ journalId: string;
150
+ generationId: string;
151
+ contractVersion: 1;
152
+ journalContractSha256: string;
153
+ maxRecords: number;
154
+ maxBytes: number;
155
+ minFreeBytes: number;
156
+ profiles: {
157
+ processName: string;
158
+ mappingSha256: string;
159
+ dataGroup: string;
160
+ }[];
161
+ groupSha256?: undefined;
162
+ };
163
+ };
164
+ /** Operator reviews opaque before/after digests; business equivalence is application-owned. */
165
+ export declare function reviewInstalledProcessGroupGeneration(configs: UpgradeConfigurations, controllerName: string, stateRoot: string, excludedUnsFields?: readonly string[]): PreparedGroupUpgrade;
166
+ //# sourceMappingURL=process-group-generation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-group-generation.d.ts","sourceRoot":"","sources":["../../src/runtime/process-group-generation.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAoBxB,OAAO,KAAK,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAG/I,eAAO,MAAM,iCAAiC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAkB1C,CAAC;AACL,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAC5F,MAAM,MAAM,0BAA0B,GAAG;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,CAAC,GAAG,CAAC,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,KAAK,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpF,CAAC;AACF,MAAM,MAAM,qBAAqB,GAAG,IAAI,CAAC,yBAAyB,EAAE,iBAAiB,CAAC,GAAG;IACvF,eAAe,EAAE,CAAC,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,0BAA0B,CAAC;IAC1C,aAAa,EAAE,0BAA0B,CAAC;IAC1C,QAAQ,EAAE;QAAE,kBAAkB,EAAE,IAAI,CAAC;QAAC,8BAA8B,EAAE,IAAI,CAAC;QAAC,iBAAiB,EAAE,KAAK,CAAA;KAAE,CAAC;IACvG,eAAe,EAAE,KAAK,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/G,CAAC;AAEF,+EAA+E;AAC/E,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,0BAA0B;;;;;;EAyE7E;AAED,4FAA4F;AAC5F,wBAAgB,gCAAgC,CAAC,SAAS,EAAE,0BAA0B;;;;;;EAKrF;AAED,mGAAmG;AACnG,wBAAgB,uCAAuC,CAAC,CAAC,EAAE,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8D9E;AAED,+FAA+F;AAC/F,wBAAgB,qCAAqC,CACnD,OAAO,EAAE,qBAAqB,EAC9B,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,MAAM,EACjB,iBAAiB,GAAE,SAAS,MAAM,EAAO,GACxC,oBAAoB,CAyHtB"}
@@ -0,0 +1,278 @@
1
+ import fs from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import path from "node:path";
4
+ import { z } from "zod";
5
+ import { configurationGroupJournalContract, configurationGroupJournalSchema } from "./configuration-group-contract.js";
6
+ import { authorizeProcessGroupRuntime } from "./process-group-authorization.js";
7
+ import { processGroupManifestSchema } from "./process-group-authorization.js";
8
+ import { processGroupJournalContract, processGroupJournalSchema, processGroupSourceIdentity, resolvedProcessGroupChannels, } from "./process-group-contract.js";
9
+ import { groupPlacementSchema, placementKey } from "./process-group-control-contract.js";
10
+ import { groupArtifactHash, groupChannelsHash, configurationAuthorizationHash as hash, readProcessGroupResource as read, sha256, } from "./process-group-resources.js";
11
+ import { normalizeRuntimeProcessGroup, selectedProcessGroupDescriptor } from "./process-group-runtime-selection.js";
12
+ const pair = z.strictObject({ publisher: groupPlacementSchema, receiver: groupPlacementSchema });
13
+ export const processGroupGenerationInputSchema = z
14
+ .strictObject({
15
+ protocolVersion: z.literal(2),
16
+ source: pair,
17
+ target: pair,
18
+ acknowledgeInputPause: z.literal(true),
19
+ acknowledgeResourceReview: z.literal(true),
20
+ acknowledgePossibleReplay: z.literal(true),
21
+ })
22
+ .superRefine((v, c) => {
23
+ const all = [v.source.publisher, v.source.receiver, v.target.publisher, v.target.receiver];
24
+ if (new Set(all.map(placementKey)).size !== 4 ||
25
+ all.some((p) => p.controller !== all[0].controller || p.rttNode !== all[0].rttNode) ||
26
+ v.source.publisher.version !== v.source.receiver.version ||
27
+ v.target.publisher.version !== v.target.receiver.version)
28
+ c.addIssue({ code: "custom", message: "Four distinct paired placements on one package/controller required" });
29
+ });
30
+ /** Bounded read-only storage evidence; no initializer, rewriting or replay. */
31
+ export function inspectGenerationJournal(reference) {
32
+ const file = path.join(reference.directory, "admission.sqlite"), stat = fs.lstatSync(file);
33
+ if (fs.realpathSync(file) !== file ||
34
+ !stat.isFile() ||
35
+ stat.nlink !== 1 ||
36
+ stat.uid !== process.getuid?.() ||
37
+ (stat.mode & 0o077) !== 0 ||
38
+ stat.size > 512 * 1024 * 1024)
39
+ throw Error("Private retained generation journal required");
40
+ const { DatabaseSync } = createRequire(import.meta.url)("node:sqlite");
41
+ const db = new DatabaseSync(file, { readOnly: true });
42
+ try {
43
+ db.exec("PRAGMA query_only=ON; PRAGMA busy_timeout=1000; BEGIN");
44
+ const meta = Object.fromEntries(db
45
+ .prepare("SELECT key,value FROM metadata WHERE key IN ('id','contract')")
46
+ .all()
47
+ .map((r) => [r["key"], r["value"]]));
48
+ if (meta["id"] !== reference.journalId ||
49
+ typeof meta["contract"] !== "string" ||
50
+ meta["contract"].length > 1048576 ||
51
+ sha256(meta["contract"]) !== reference.journalContractSha256)
52
+ throw Error("Reviewed journal generation changed");
53
+ const rows = db.prepare("SELECT name,contract,completed,last_seq FROM profiles ORDER BY name").all();
54
+ if (rows.length !== reference.profiles.length)
55
+ throw Error("Journal profile set differs");
56
+ for (const profile of reference.profiles) {
57
+ const row = rows.find((r) => r["name"] === profile.processName), binding = row ? JSON.parse(String(row["contract"])) : null;
58
+ if (!binding ||
59
+ binding.processName !== profile.processName ||
60
+ binding.dataGroup !== profile.dataGroup ||
61
+ binding[reference.contractVersion === 1 ? "mappingSha256" : "resourceSha256"] !== profile.mappingSha256)
62
+ throw Error("Journal resource binding differs");
63
+ }
64
+ const usage = db.prepare("SELECT records,bytes,admitted FROM usage WHERE id=1").get();
65
+ const actual = db.prepare("SELECT count(*) records,coalesce(sum(bytes),0) bytes FROM tasks").get();
66
+ const numbers = [usage?.["records"], usage?.["bytes"], usage?.["admitted"], ...rows.flatMap((r) => [r["completed"], r["last_seq"]])];
67
+ const completed = rows.reduce((n, r) => n + Number(r["completed"]), 0);
68
+ if (!usage ||
69
+ !actual ||
70
+ numbers.some((n) => typeof n !== "number" || !Number.isSafeInteger(n) || n < 0) ||
71
+ usage["records"] !== actual["records"] ||
72
+ usage["bytes"] !== actual["bytes"] ||
73
+ !Number.isSafeInteger(completed) ||
74
+ Number(usage["admitted"]) !== completed + Number(usage["records"]) ||
75
+ rows.some((r) => (r["completed"] === 0) !== (r["last_seq"] === 0)) ||
76
+ Number(usage["records"]) > reference.maxRecords ||
77
+ Number(usage["bytes"]) > reference.maxBytes)
78
+ throw Error("Journal usage is unconfirmed");
79
+ return {
80
+ records: Number(usage["records"]),
81
+ bytes: Number(usage["bytes"]),
82
+ admitted: Number(usage["admitted"]),
83
+ completed,
84
+ lastSequence: rows.reduce((n, r) => Math.max(n, Number(r["last_seq"])), 0),
85
+ };
86
+ }
87
+ finally {
88
+ try {
89
+ db.exec("ROLLBACK");
90
+ }
91
+ finally {
92
+ db.close();
93
+ }
94
+ }
95
+ }
96
+ /** Recheck immediately before the first launch; initialization is never silently reused. */
97
+ export function inspectPristineGenerationJournal(reference) {
98
+ const status = inspectGenerationJournal(reference);
99
+ if (status.records || status.bytes || status.admitted || status.completed || status.lastSequence)
100
+ throw Error("Target generation is not pristine; inspect the existing initialization outcome");
101
+ return status;
102
+ }
103
+ /** Data-only normalized revision and immutable journal contract shared by reviewed transitions. */
104
+ export function describeInstalledProcessGroupGeneration(c) {
105
+ const d = selectedProcessGroupDescriptor(c.packageRoot, c.raw);
106
+ if (!d)
107
+ throw Error("Declared process group required");
108
+ const normalized = normalizeRuntimeProcessGroup(c.raw, d);
109
+ if (!normalized.matches)
110
+ throw Error("Runtime selector differs");
111
+ const channels = resolvedProcessGroupChannels(c.resolved), sourceIdentity = processGroupSourceIdentity(channels[1]);
112
+ if (d.protocolVersion === 2) {
113
+ const snapshot = normalized.configurationSnapshot;
114
+ const journal = configurationGroupJournalSchema.parse(normalized.journal);
115
+ const contract = configurationGroupJournalContract(snapshot, sourceIdentity, journal, snapshot.profiles.map(({ processName, resourceSha256, dataGroup }) => ({ processName, resourceSha256, dataGroup })));
116
+ return {
117
+ d,
118
+ normalized,
119
+ channels,
120
+ id: snapshot.group.id,
121
+ processName: snapshot.group.processName,
122
+ receiverProcessName: snapshot.group.receiverProcessName,
123
+ inputTopic: snapshot.group.inputTopic,
124
+ reference: {
125
+ directory: journal.directory,
126
+ journalId: journal.journalId,
127
+ generationId: journal.generationId,
128
+ groupSha256: snapshot.sha256,
129
+ contractVersion: 2,
130
+ journalContractSha256: sha256(contract),
131
+ maxRecords: journal.maxRecords,
132
+ maxBytes: journal.maxBytes,
133
+ minFreeBytes: journal.minFreeBytes,
134
+ profiles: snapshot.profiles.map(({ processName, resourceSha256, dataGroup }) => ({ processName, mappingSha256: resourceSha256, dataGroup })),
135
+ },
136
+ };
137
+ }
138
+ const journal = processGroupJournalSchema.parse(normalized.journal);
139
+ if (!d.manifests.some((m) => m.file === journal.groupManifest))
140
+ throw Error("Registered file group required");
141
+ const group = processGroupManifestSchema.parse(JSON.parse(read(path.resolve(c.packageRoot, journal.groupManifest)).toString()));
142
+ const contract = processGroupJournalContract(sourceIdentity, journal, group.profiles);
143
+ return {
144
+ d,
145
+ normalized,
146
+ channels,
147
+ id: group.id,
148
+ processName: group.processName,
149
+ receiverProcessName: group.processName + "-admission",
150
+ inputTopic: group.inputTopic,
151
+ reference: {
152
+ directory: journal.directory,
153
+ journalId: journal.journalId,
154
+ generationId: group.id,
155
+ contractVersion: 1,
156
+ journalContractSha256: sha256(contract),
157
+ maxRecords: journal.maxRecords,
158
+ maxBytes: journal.maxBytes,
159
+ minFreeBytes: journal.minFreeBytes,
160
+ profiles: group.profiles.map(({ processName, mappingSha256, dataGroup }) => ({ processName, mappingSha256, dataGroup })),
161
+ },
162
+ };
163
+ }
164
+ /** Operator reviews opaque before/after digests; business equivalence is application-owned. */
165
+ export function reviewInstalledProcessGroupGeneration(configs, controllerName, stateRoot, excludedUnsFields = []) {
166
+ const context = (c) => ({
167
+ packageRoot: c.packageRoot,
168
+ instanceDirectory: c.instanceDirectory,
169
+ rawConfiguration: c.raw,
170
+ channels: resolvedProcessGroupChannels(c.resolved),
171
+ controllerName,
172
+ stateRoot,
173
+ });
174
+ const a = describeInstalledProcessGroupGeneration(configs.source.publisher), b = describeInstalledProcessGroupGeneration(configs.target.publisher);
175
+ if (b.d.protocolVersion !== 2 ||
176
+ a.d.namespace !== b.d.namespace ||
177
+ a.id !== b.id ||
178
+ a.processName !== b.processName ||
179
+ a.receiverProcessName !== b.receiverProcessName ||
180
+ a.inputTopic !== b.inputTopic ||
181
+ a.reference.directory === b.reference.directory ||
182
+ !a.reference.journalId ||
183
+ !b.reference.journalId ||
184
+ a.reference.journalId === b.reference.journalId ||
185
+ a.reference.maxRecords !== b.reference.maxRecords ||
186
+ a.reference.maxBytes !== b.reference.maxBytes ||
187
+ a.reference.minFreeBytes !== b.reference.minFreeBytes)
188
+ throw Error("Distinct bounded generations with the same process/input identity required");
189
+ const policyFiles = {}, policies = {};
190
+ const members = [];
191
+ const portable = (raw, d) => {
192
+ const copy = structuredClone(raw), uns = { ...copy["uns"] };
193
+ for (const key of excludedUnsFields)
194
+ delete uns[key];
195
+ copy["uns"] = uns;
196
+ for (const keys of [d.publisherPath, d.journalPath, d.runtimeRolePath]) {
197
+ let node = copy;
198
+ let present = true;
199
+ for (const key of keys.slice(0, -1)) {
200
+ const child = node[key];
201
+ if (child === undefined) {
202
+ present = false;
203
+ break;
204
+ }
205
+ if (!child || typeof child !== "object" || Array.isArray(child))
206
+ throw Error("Invalid resource selector parent");
207
+ node = child;
208
+ }
209
+ if (present)
210
+ delete node[keys.at(-1)];
211
+ }
212
+ return hash(copy);
213
+ };
214
+ for (const side of ["source", "target"])
215
+ for (const role of ["publisher", "receiver"]) {
216
+ const c = configs[side][role], g = describeInstalledProcessGroupGeneration(c), expected = side === "source" ? a : b, proof = authorizeProcessGroupRuntime(context(c));
217
+ if (proof.role !== (role === "publisher" ? "target" : "receiver") ||
218
+ proof.directory !== expected.reference.directory ||
219
+ proof.journalId !== expected.reference.journalId ||
220
+ hash(g.reference) !== hash(expected.reference) ||
221
+ g.normalized.uns["processName"] !== (role === "publisher" ? g.processName : g.receiverProcessName) ||
222
+ (role === "receiver"
223
+ ? g.normalized.runtimeRole !== "admission"
224
+ : g.normalized.runtimeRole !== undefined && g.normalized.runtimeRole !== "publisher"))
225
+ throw Error("Exact reviewed generation pair required");
226
+ const pkg = JSON.parse(read(path.join(c.packageRoot, "package.json")).toString());
227
+ if (pkg.name !== JSON.parse(read(path.join(configs.source.publisher.packageRoot, "package.json")).toString()).name ||
228
+ portable(c.raw, g.d) !== portable(configs.source[role].raw, a.d) ||
229
+ groupChannelsHash(g.channels) !== groupChannelsHash(a.channels))
230
+ throw Error("Configuration or broker identity changed outside the reviewed resources");
231
+ if (policyFiles[side] && policyFiles[side] !== proof.file)
232
+ throw Error("Pair policies differ");
233
+ policyFiles[side] = proof.file;
234
+ policies[side] = read(proof.file, true);
235
+ members.push({
236
+ side,
237
+ role,
238
+ processName: String(g.normalized.uns["processName"]),
239
+ configurationSha256: hash(c.raw),
240
+ artifactSha256: groupArtifactHash(c.packageRoot),
241
+ });
242
+ }
243
+ if (policyFiles.source === policyFiles.target)
244
+ throw Error("Each generation requires its own retained policy");
245
+ const profiles = a.reference.profiles;
246
+ if (profiles.length !== b.reference.profiles.length ||
247
+ profiles.some((p) => !b.reference.profiles.some((q) => q.processName === p.processName && q.dataGroup === p.dataGroup)))
248
+ throw Error("Logical profile identities or data groups changed");
249
+ inspectGenerationJournal(a.reference);
250
+ inspectPristineGenerationJournal(b.reference);
251
+ if (sha256(read(policyFiles.source, true)) !== sha256(policies.source) || sha256(read(policyFiles.target, true)) !== sha256(policies.target))
252
+ throw Error("Reviewed policies changed");
253
+ const review = {
254
+ protocolVersion: 2,
255
+ namespace: b.d.namespace,
256
+ groupId: b.id,
257
+ policyFile: policyFiles.target,
258
+ sourcePolicyFile: policyFiles.source,
259
+ sourcePolicySha256: sha256(policies.source),
260
+ targetPolicySha256: sha256(policies.target),
261
+ journalId: b.reference.journalId,
262
+ directory: b.reference.directory,
263
+ journalContractSha256: b.reference.journalContractSha256,
264
+ profiles: b.reference.profiles,
265
+ members,
266
+ sourceJournal: a.reference,
267
+ targetJournal: b.reference,
268
+ delivery: { inputPausePossible: true, retainedBrokerSessionMayReplay: true, exactlyOnceOutput: false },
269
+ resourceChanges: profiles.map((p) => ({
270
+ processName: p.processName,
271
+ beforeSha256: p.mappingSha256,
272
+ afterSha256: b.reference.profiles.find((q) => q.processName === p.processName).mappingSha256,
273
+ dataGroup: p.dataGroup,
274
+ })),
275
+ };
276
+ return { review, sourcePolicy: policies.source, targetPolicy: policies.target };
277
+ }
278
+ //# sourceMappingURL=process-group-generation.js.map