@norskvideo/norsk-auto-manager 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +344 -0
- package/lib/src/automanager.d.ts +1070 -0
- package/lib/src/automanager.js +2746 -0
- package/lib/src/clock.d.ts +14 -0
- package/lib/src/clock.js +21 -0
- package/lib/src/conversions.d.ts +59 -0
- package/lib/src/conversions.js +416 -0
- package/lib/src/index.d.ts +9 -0
- package/lib/src/index.js +37 -0
- package/lib/src/inventory.d.ts +34 -0
- package/lib/src/inventory.js +64 -0
- package/lib/src/knownCapabilities.d.ts +18 -0
- package/lib/src/knownCapabilities.js +22 -0
- package/lib/src/placement.d.ts +268 -0
- package/lib/src/placement.js +468 -0
- package/lib/src/settingsValidation.d.ts +34 -0
- package/lib/src/settingsValidation.js +232 -0
- package/lib/src/shared/utils.d.ts +17 -0
- package/lib/src/shared/utils.js +220 -0
- package/lib/src/types.d.ts +295 -0
- package/lib/src/types.js +20 -0
- package/lib/src/validation.d.ts +68 -0
- package/lib/src/validation.js +198 -0
- package/package.json +66 -0
- package/src/automanager.ts +3570 -0
- package/src/clock.ts +29 -0
- package/src/conversions.ts +458 -0
- package/src/index.ts +27 -0
- package/src/inventory.ts +64 -0
- package/src/knownCapabilities.ts +23 -0
- package/src/placement.ts +804 -0
- package/src/settingsValidation.ts +386 -0
- package/src/types.ts +346 -0
- package/src/validation.ts +297 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Phase D1 — runtime validation of AutoSettings. Hand-rolled in the
|
|
3
|
+
// same shape as `validateBundle` (validation.ts) rather than dragging
|
|
4
|
+
// in zod for a small surface. Returns a list of structured errors —
|
|
5
|
+
// empty means valid.
|
|
6
|
+
//
|
|
7
|
+
// `AutoManager.run` invokes this at entry and throws
|
|
8
|
+
// `AutoSettingsValidationError` if anything fails. Catches typos and
|
|
9
|
+
// shape mistakes at boot rather than mid-flight.
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.AutoSettingsValidationError = void 0;
|
|
12
|
+
exports.validateAutoSettings = validateAutoSettings;
|
|
13
|
+
/**
|
|
14
|
+
* Validate an `AutoSettings` value at boot. Returns all errors found
|
|
15
|
+
* (does not short-circuit). Callbacks are not validated — TypeScript
|
|
16
|
+
* structural typing already covers them as well as a runtime check
|
|
17
|
+
* usefully can. The point of this validator is to catch
|
|
18
|
+
* configuration mistakes operators make (typos in pool names, bad
|
|
19
|
+
* enum values, negative timeouts), not to second-guess the type
|
|
20
|
+
* system.
|
|
21
|
+
*
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
function validateAutoSettings(settings) {
|
|
25
|
+
const errors = [];
|
|
26
|
+
const ctx = new Ctx(errors);
|
|
27
|
+
ctx.checkPositive(settings.pendingWindow, "pendingWindow");
|
|
28
|
+
ctx.checkPositive(settings.removeStoppedNodesAfter, "removeStoppedNodesAfter");
|
|
29
|
+
// Flat optional tunables.
|
|
30
|
+
ctx.checkOptionalPositive(settings.rejectionBackoffMs, "rejectionBackoffMs");
|
|
31
|
+
ctx.checkOptionalPositive(settings.rejectionEscalationCount, "rejectionEscalationCount");
|
|
32
|
+
ctx.checkOptionalPositive(settings.failureBackoffMs, "failureBackoffMs");
|
|
33
|
+
ctx.checkOptionalPositive(settings.maxRestartsPerHour, "maxRestartsPerHour");
|
|
34
|
+
ctx.checkOptionalPositive(settings.jobStartupGraceMs, "jobStartupGraceMs");
|
|
35
|
+
ctx.checkOptionalPositive(settings.hotSpareReconcileIntervalMs, "hotSpareReconcileIntervalMs");
|
|
36
|
+
// Placement pools.
|
|
37
|
+
const knownPoolNames = new Set();
|
|
38
|
+
if (!Array.isArray(settings.placementPools)) {
|
|
39
|
+
ctx.add("invalidType", "placementPools", "must be an array");
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
settings.placementPools.forEach((pool, i) => {
|
|
43
|
+
validatePool(pool, `placementPools[${i}]`, knownPoolNames, ctx);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
// Typed configs.
|
|
47
|
+
if (settings.bands !== undefined) {
|
|
48
|
+
validateBandConfig(settings.bands, "bands", ctx);
|
|
49
|
+
}
|
|
50
|
+
if (settings.placement !== undefined) {
|
|
51
|
+
validatePlacementConfig(settings.placement, "placement", ctx);
|
|
52
|
+
}
|
|
53
|
+
if (settings.hotSpares !== undefined) {
|
|
54
|
+
if (!Array.isArray(settings.hotSpares)) {
|
|
55
|
+
ctx.add("invalidType", "hotSpares", "must be an array");
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
settings.hotSpares.forEach((entry, i) => {
|
|
59
|
+
validateHotSpare(entry, `hotSpares[${i}]`, knownPoolNames, settings.placementPools ?? [], ctx);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return errors;
|
|
64
|
+
}
|
|
65
|
+
function validatePool(pool, path, knownPoolNames, ctx) {
|
|
66
|
+
if (typeof pool.name !== "string" || pool.name.length === 0) {
|
|
67
|
+
ctx.add("invalidType", `${path}.name`, "must be a non-empty string");
|
|
68
|
+
}
|
|
69
|
+
else if (knownPoolNames.has(pool.name)) {
|
|
70
|
+
ctx.add("duplicatePool", `${path}.name`, `duplicate pool name "${pool.name}"`);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
knownPoolNames.add(pool.name);
|
|
74
|
+
}
|
|
75
|
+
if (!Array.isArray(pool.tiers) || pool.tiers.length === 0) {
|
|
76
|
+
ctx.add("incompatibleConfig", `${path}.tiers`, `pool needs at least one tier`);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const tierNames = new Set();
|
|
80
|
+
let fixedTierCount = 0;
|
|
81
|
+
pool.tiers.forEach((tier, i) => {
|
|
82
|
+
const tp = `${path}.tiers[${i}]`;
|
|
83
|
+
if (typeof tier.name !== "string" || tier.name.length === 0) {
|
|
84
|
+
ctx.add("invalidType", `${tp}.name`, "must be a non-empty string");
|
|
85
|
+
}
|
|
86
|
+
else if (tierNames.has(tier.name)) {
|
|
87
|
+
ctx.add("duplicatePool", `${tp}.name`, `duplicate tier name "${tier.name}"`);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
tierNames.add(tier.name);
|
|
91
|
+
}
|
|
92
|
+
if (tier.scaleOut === "fixed")
|
|
93
|
+
fixedTierCount++;
|
|
94
|
+
validateTier(tier, tp, ctx);
|
|
95
|
+
});
|
|
96
|
+
// nodeMatchesTier maps an untagged (pre-registered cluster) node to the
|
|
97
|
+
// pool's fixed tier, so that mapping must be unambiguous.
|
|
98
|
+
if (fixedTierCount > 1) {
|
|
99
|
+
ctx.add("incompatibleConfig", `${path}.tiers`, `pool may have at most one "fixed" tier (found ${fixedTierCount})`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function validateTier(tier, path, ctx) {
|
|
103
|
+
if (!["aws", "oci", "local-servers"].includes(tier.kind)) {
|
|
104
|
+
ctx.add("invalidEnumValue", `${path}.kind`, `must be one of "aws" | "oci" | "local-servers"`);
|
|
105
|
+
}
|
|
106
|
+
if (!["binpack", "spread"].includes(tier.packingStrategy)) {
|
|
107
|
+
ctx.add("invalidEnumValue", `${path}.packingStrategy`, `must be "binpack" or "spread"`);
|
|
108
|
+
}
|
|
109
|
+
if (!["elastic", "fixed"].includes(tier.scaleOut)) {
|
|
110
|
+
ctx.add("invalidEnumValue", `${path}.scaleOut`, `must be "elastic" or "fixed"`);
|
|
111
|
+
}
|
|
112
|
+
if (tier.kind === "local-servers" && tier.scaleOut === "elastic") {
|
|
113
|
+
ctx.add("incompatibleConfig", `${path}`, `cluster tiers cannot be "elastic" (no auto-scale)`);
|
|
114
|
+
}
|
|
115
|
+
if (!Array.isArray(tier.candidateInstanceTypes)) {
|
|
116
|
+
ctx.add("invalidType", `${path}.candidateInstanceTypes`, "must be an array");
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
if (tier.scaleOut === "elastic" && tier.candidateInstanceTypes.length === 0) {
|
|
120
|
+
ctx.add("incompatibleConfig", `${path}.candidateInstanceTypes`, `elastic tier needs at least one candidate instance type`);
|
|
121
|
+
}
|
|
122
|
+
tier.candidateInstanceTypes.forEach((c, i) => {
|
|
123
|
+
const p = `${path}.candidateInstanceTypes[${i}]`;
|
|
124
|
+
if (typeof c.instanceType !== "string" || c.instanceType.length === 0) {
|
|
125
|
+
ctx.add("invalidType", `${p}.instanceType`, "must be non-empty string");
|
|
126
|
+
}
|
|
127
|
+
if (typeof c.totalCapacity !== "number" || c.totalCapacity <= 0) {
|
|
128
|
+
ctx.add("outOfRange", `${p}.totalCapacity`, "must be > 0");
|
|
129
|
+
}
|
|
130
|
+
if (typeof c.totalCores !== "number" ||
|
|
131
|
+
c.totalCores <= 0 ||
|
|
132
|
+
!Number.isInteger(c.totalCores)) {
|
|
133
|
+
ctx.add("outOfRange", `${p}.totalCores`, "must be a positive integer");
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function validateBandConfig(bands, path, ctx) {
|
|
139
|
+
if (bands.gold) {
|
|
140
|
+
if (bands.gold.onFailure !== "restart") {
|
|
141
|
+
ctx.add("invalidEnumValue", `${path}.gold.onFailure`, `must be "restart"`);
|
|
142
|
+
}
|
|
143
|
+
if (!["preferHotSpare", "any"].includes(bands.gold.restartPlacement)) {
|
|
144
|
+
ctx.add("invalidEnumValue", `${path}.gold.restartPlacement`, `must be "preferHotSpare" or "any"`);
|
|
145
|
+
}
|
|
146
|
+
ctx.checkOptionalPositive(bands.gold.maxRestartsPerHour, `${path}.gold.maxRestartsPerHour`);
|
|
147
|
+
}
|
|
148
|
+
if (bands.silver) {
|
|
149
|
+
if (bands.silver.onFailure !== "restart") {
|
|
150
|
+
ctx.add("invalidEnumValue", `${path}.silver.onFailure`, `must be "restart"`);
|
|
151
|
+
}
|
|
152
|
+
if (bands.silver.restartPlacement !== "any") {
|
|
153
|
+
ctx.add("invalidEnumValue", `${path}.silver.restartPlacement`, `must be "any"`);
|
|
154
|
+
}
|
|
155
|
+
ctx.checkOptionalPositive(bands.silver.maxRestartsPerHour, `${path}.silver.maxRestartsPerHour`);
|
|
156
|
+
}
|
|
157
|
+
if (bands.bronze) {
|
|
158
|
+
if (bands.bronze.onFailure !== "drop") {
|
|
159
|
+
ctx.add("invalidEnumValue", `${path}.bronze.onFailure`, `must be "drop"`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function validatePlacementConfig(cfg, path, ctx) {
|
|
164
|
+
ctx.checkOptionalPositive(cfg.rejectionBackoffMs, `${path}.rejectionBackoffMs`);
|
|
165
|
+
ctx.checkOptionalPositive(cfg.rejectionEscalationCount, `${path}.rejectionEscalationCount`);
|
|
166
|
+
ctx.checkOptionalPositive(cfg.failureBackoffMs, `${path}.failureBackoffMs`);
|
|
167
|
+
ctx.checkOptionalPositive(cfg.jobStartupGraceMs, `${path}.jobStartupGraceMs`);
|
|
168
|
+
}
|
|
169
|
+
function validateHotSpare(cfg, path, knownPoolNames, placementPools, ctx) {
|
|
170
|
+
if (typeof cfg.pool !== "string" || cfg.pool.length === 0) {
|
|
171
|
+
ctx.add("invalidType", `${path}.pool`, "must be a non-empty string");
|
|
172
|
+
}
|
|
173
|
+
else if (!knownPoolNames.has(cfg.pool)) {
|
|
174
|
+
ctx.add("unknownPool", `${path}.pool`, `pool "${cfg.pool}" is not declared in placementPools`);
|
|
175
|
+
}
|
|
176
|
+
if (typeof cfg.targetCount !== "number" ||
|
|
177
|
+
cfg.targetCount < 0 ||
|
|
178
|
+
!Number.isInteger(cfg.targetCount)) {
|
|
179
|
+
ctx.add("outOfRange", `${path}.targetCount`, `must be a non-negative integer (got ${cfg.targetCount})`);
|
|
180
|
+
}
|
|
181
|
+
if (cfg.forBand !== undefined && !["gold", "silver", "bronze"].includes(cfg.forBand)) {
|
|
182
|
+
ctx.add("invalidEnumValue", `${path}.forBand`, `must be "gold" | "silver" | "bronze"`);
|
|
183
|
+
}
|
|
184
|
+
if (cfg.spec === undefined || typeof cfg.spec !== "object") {
|
|
185
|
+
ctx.add("missingRequired", `${path}.spec`, "spec is required");
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
// A pool with an elastic tier needs an instance type to provision against.
|
|
189
|
+
const pool = placementPools.find((p) => p.name === cfg.pool);
|
|
190
|
+
if (pool &&
|
|
191
|
+
pool.tiers.some((t) => t.scaleOut === "elastic") &&
|
|
192
|
+
!cfg.spec.instanceType) {
|
|
193
|
+
ctx.add("missingRequired", `${path}.spec.instanceType`, `elastic pool spares require spec.instanceType`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
class Ctx {
|
|
197
|
+
constructor(errors) {
|
|
198
|
+
this.errors = errors;
|
|
199
|
+
}
|
|
200
|
+
add(code, path, message) {
|
|
201
|
+
this.errors.push({ code, path, message });
|
|
202
|
+
}
|
|
203
|
+
checkPositive(value, path) {
|
|
204
|
+
if (typeof value !== "number" || value <= 0) {
|
|
205
|
+
this.add("outOfRange", path, `must be a positive number (got ${value})`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
checkOptionalPositive(value, path) {
|
|
209
|
+
if (value === undefined)
|
|
210
|
+
return;
|
|
211
|
+
if (typeof value !== "number" || value <= 0) {
|
|
212
|
+
this.add("outOfRange", path, `must be a positive number when set (got ${value})`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Thrown by `AutoManager.run` when `validateAutoSettings` finds
|
|
218
|
+
* configuration errors. The `.errors` property carries the structured
|
|
219
|
+
* details; `.message` contains a human-readable summary.
|
|
220
|
+
*
|
|
221
|
+
* @public
|
|
222
|
+
*/
|
|
223
|
+
class AutoSettingsValidationError extends Error {
|
|
224
|
+
constructor(errors) {
|
|
225
|
+
super(`AutoSettings validation failed (${errors.length} error${errors.length === 1 ? "" : "s"}):\n ` +
|
|
226
|
+
errors.map((e) => `${e.path}: ${e.message}`).join("\n "));
|
|
227
|
+
this.errors = errors;
|
|
228
|
+
this.name = "AutoSettingsValidationError";
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
exports.AutoSettingsValidationError = AutoSettingsValidationError;
|
|
232
|
+
//# sourceMappingURL=settingsValidation.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** @public */
|
|
2
|
+
export declare function norskHost(): string;
|
|
3
|
+
/** @public */
|
|
4
|
+
export declare function norskPort(): string;
|
|
5
|
+
/** @public */
|
|
6
|
+
export declare function publicUrlPrefix(): string;
|
|
7
|
+
/** @public */
|
|
8
|
+
export declare function debugUrlPrefix(): string;
|
|
9
|
+
/** @public */
|
|
10
|
+
export declare function clientHostExternal(): string;
|
|
11
|
+
/** @public */
|
|
12
|
+
export declare function clientPortExternal(): string;
|
|
13
|
+
/** @public */
|
|
14
|
+
export declare function clientHostInternal(): string;
|
|
15
|
+
/** @public */
|
|
16
|
+
export declare function clientPortInternal(): string;
|
|
17
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.unwrapEither = exports.isRight = exports.isLeft = exports.makeRight = exports.makeLeft = void 0;
|
|
37
|
+
exports.provideFull = provideFull;
|
|
38
|
+
exports.exhaustiveCheck = exhaustiveCheck;
|
|
39
|
+
exports.mkCase = mkCase;
|
|
40
|
+
exports.mkMessageCase = mkMessageCase;
|
|
41
|
+
exports.mapOptional = mapOptional;
|
|
42
|
+
exports.debuglog = debuglog;
|
|
43
|
+
exports.errorlog = errorlog;
|
|
44
|
+
exports.invokeOnCreate = invokeOnCreate;
|
|
45
|
+
exports.mandatory = mandatory;
|
|
46
|
+
exports.norskHost = norskHost;
|
|
47
|
+
exports.norskPort = norskPort;
|
|
48
|
+
exports.publicUrlPrefix = publicUrlPrefix;
|
|
49
|
+
exports.debugUrlPrefix = debugUrlPrefix;
|
|
50
|
+
exports.clientHostExternal = clientHostExternal;
|
|
51
|
+
exports.clientPortExternal = clientPortExternal;
|
|
52
|
+
exports.clientHostInternal = clientHostInternal;
|
|
53
|
+
exports.clientPortInternal = clientPortInternal;
|
|
54
|
+
const util = __importStar(require("util"));
|
|
55
|
+
const protobuf_1 = require("@bufbuild/protobuf");
|
|
56
|
+
/**
|
|
57
|
+
* @internal
|
|
58
|
+
*
|
|
59
|
+
* Ask TypeScript to tell us when we've left the fields out of a message.
|
|
60
|
+
*
|
|
61
|
+
* Usage:
|
|
62
|
+
* provideFull(MyProtobufMessageSchema, { myField })
|
|
63
|
+
* instead of
|
|
64
|
+
* create(MyProtobufMessageSchema, { myField })
|
|
65
|
+
*/
|
|
66
|
+
function provideFull(schema, v) {
|
|
67
|
+
// This no longer does it's job I'm just trying to compile as a first step
|
|
68
|
+
return (0, protobuf_1.create)(schema, v);
|
|
69
|
+
}
|
|
70
|
+
/** @internal
|
|
71
|
+
*
|
|
72
|
+
* unreachable
|
|
73
|
+
*/
|
|
74
|
+
function exhaustiveCheck(a) {
|
|
75
|
+
throw new Error(`Unhandled case: ${a}`);
|
|
76
|
+
}
|
|
77
|
+
/** @internal */
|
|
78
|
+
function mkCase(obj) {
|
|
79
|
+
const keys = Object.keys(obj);
|
|
80
|
+
if (keys.length !== 1)
|
|
81
|
+
throw new Error("Bad case");
|
|
82
|
+
return { case: keys[0], value: obj[keys[0]] };
|
|
83
|
+
}
|
|
84
|
+
/** @internal */
|
|
85
|
+
function mkMessageCase(obj) {
|
|
86
|
+
return { message: mkCase(obj) };
|
|
87
|
+
}
|
|
88
|
+
/** @internal */
|
|
89
|
+
function mapOptional(fn, value) {
|
|
90
|
+
return value !== undefined ? fn(value) : undefined;
|
|
91
|
+
}
|
|
92
|
+
/** @internal */
|
|
93
|
+
function debuglog(msg, ...param) {
|
|
94
|
+
util.debuglog("norsk")(util.formatWithOptions({ maxArrayLength: null, depth: null, colors: true }, "[" + new Date().toISOString() + "] " + msg, ...param));
|
|
95
|
+
}
|
|
96
|
+
/** @internal */
|
|
97
|
+
function errorlog(msg, ...param) {
|
|
98
|
+
util.debuglog("norsk")(util.formatWithOptions({ maxArrayLength: null, depth: null, colors: true }, "[" + new Date().toISOString() + "] " + msg, ...param));
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* @internal
|
|
102
|
+
* Invoke a node's `onCreate` callback, warning loudly if it returned a Promise.
|
|
103
|
+
*
|
|
104
|
+
* `onCreate` MUST be synchronous (its declared type is `(node) => void`). Frames
|
|
105
|
+
* flow as soon as it returns, so an `async` onCreate doesn't block them — it
|
|
106
|
+
* suspends at its first `await` and the SDK proceeds, meaning any subscriptions
|
|
107
|
+
* set up after that `await` race the node's first frames. This catches the
|
|
108
|
+
* `async`-onCreate footgun at runtime for both TypeScript and plain-JS clients
|
|
109
|
+
* (no lint or type-checking required), at the cost of one `typeof` per node.
|
|
110
|
+
*/
|
|
111
|
+
function invokeOnCreate(onCreate, node) {
|
|
112
|
+
if (!onCreate)
|
|
113
|
+
return;
|
|
114
|
+
const result = onCreate(node);
|
|
115
|
+
if (result != null && typeof result.then === "function") {
|
|
116
|
+
// console.warn (not the NODE_DEBUG-gated errorlog) so this is always visible —
|
|
117
|
+
// the whole point is to surface the footgun without any opt-in.
|
|
118
|
+
console.warn("[norsk-sdk] onCreate for node '%s' returned a Promise — onCreate must be SYNCHRONOUS (was it declared `async`?). " +
|
|
119
|
+
"Frames flow as soon as onCreate returns, so an async onCreate does NOT block them, and any subscriptions " +
|
|
120
|
+
"set up after an `await` will race the node's first frames. Do subscription setup synchronously in onCreate.", node?.id ?? "<unknown>");
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/** @internal */
|
|
124
|
+
function mandatory(value, name) {
|
|
125
|
+
if (value !== undefined) {
|
|
126
|
+
return value;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
if (name === undefined) {
|
|
130
|
+
throw new Error();
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
throw new Error(`Mandatory variable '${name}' is undefined`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/** @internal */
|
|
138
|
+
const makeLeft = (value) => ({ left: value });
|
|
139
|
+
exports.makeLeft = makeLeft;
|
|
140
|
+
/** @internal */
|
|
141
|
+
const makeRight = (value) => ({ right: value });
|
|
142
|
+
exports.makeRight = makeRight;
|
|
143
|
+
/** @internal */
|
|
144
|
+
const isLeft = (e) => {
|
|
145
|
+
return e.left !== undefined;
|
|
146
|
+
};
|
|
147
|
+
exports.isLeft = isLeft;
|
|
148
|
+
/** @internal */
|
|
149
|
+
const isRight = (e) => {
|
|
150
|
+
return e.right !== undefined;
|
|
151
|
+
};
|
|
152
|
+
exports.isRight = isRight;
|
|
153
|
+
/** @internal */
|
|
154
|
+
const unwrapEither = ({ left, right, }) => {
|
|
155
|
+
if (right !== undefined && left !== undefined) {
|
|
156
|
+
throw new Error(`Received both left and right values at runtime when opening an Either\nLeft: ${JSON.stringify(left)}\nRight: ${JSON.stringify(right)}`);
|
|
157
|
+
/*
|
|
158
|
+
We're throwing in this function because this can only occur at runtime if something
|
|
159
|
+
happens that the TypeScript compiler couldn't anticipate. That means the application
|
|
160
|
+
is in an unexpected state and we should terminate immediately.
|
|
161
|
+
*/
|
|
162
|
+
}
|
|
163
|
+
if (left !== undefined) {
|
|
164
|
+
return left; // Typescript is getting confused and returning this type as `T | undefined` unless we add the type assertion
|
|
165
|
+
}
|
|
166
|
+
if (right !== undefined) {
|
|
167
|
+
return right;
|
|
168
|
+
}
|
|
169
|
+
throw new Error(`Received no left or right values at runtime when opening Either`);
|
|
170
|
+
};
|
|
171
|
+
exports.unwrapEither = unwrapEither;
|
|
172
|
+
/** @public */
|
|
173
|
+
function norskHost() {
|
|
174
|
+
if (process.env.NORSK_HOST)
|
|
175
|
+
return process.env.NORSK_HOST;
|
|
176
|
+
return "127.0.0.1";
|
|
177
|
+
}
|
|
178
|
+
/** @public */
|
|
179
|
+
function norskPort() {
|
|
180
|
+
if (process.env.NORSK_PORT)
|
|
181
|
+
return process.env.NORSK_PORT;
|
|
182
|
+
return "6790";
|
|
183
|
+
}
|
|
184
|
+
/** @public */
|
|
185
|
+
function publicUrlPrefix() {
|
|
186
|
+
if (process.env.PUBLIC_URL_PREFIX)
|
|
187
|
+
return process.env.PUBLIC_URL_PREFIX;
|
|
188
|
+
return "http://127.0.0.1:8080";
|
|
189
|
+
}
|
|
190
|
+
/** @public */
|
|
191
|
+
function debugUrlPrefix() {
|
|
192
|
+
if (process.env.DEBUG_URL_PREFIX)
|
|
193
|
+
return process.env.DEBUG_URL_PREFIX;
|
|
194
|
+
return "http://" + norskHost() + ":6791";
|
|
195
|
+
}
|
|
196
|
+
/** @public */
|
|
197
|
+
function clientHostExternal() {
|
|
198
|
+
if (process.env.CLIENT_HOST_EXTERNAL)
|
|
199
|
+
return process.env.CLIENT_HOST_EXTERNAL;
|
|
200
|
+
return "127.0.0.1";
|
|
201
|
+
}
|
|
202
|
+
/** @public */
|
|
203
|
+
function clientPortExternal() {
|
|
204
|
+
if (process.env.CLIENT_PORT_EXTERNAL)
|
|
205
|
+
return process.env.CLIENT_PORT_EXTERNAL;
|
|
206
|
+
return "3000";
|
|
207
|
+
}
|
|
208
|
+
/** @public */
|
|
209
|
+
function clientHostInternal() {
|
|
210
|
+
if (process.env.CLIENT_HOST_INTERNAL)
|
|
211
|
+
return process.env.CLIENT_HOST_INTERNAL;
|
|
212
|
+
return "127.0.0.1";
|
|
213
|
+
}
|
|
214
|
+
/** @public */
|
|
215
|
+
function clientPortInternal() {
|
|
216
|
+
if (process.env.CLIENT_PORT_INTERNAL)
|
|
217
|
+
return process.env.CLIENT_PORT_INTERNAL;
|
|
218
|
+
return "3000";
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=utils.js.map
|