@animastor/orchestration 0.1.1
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/LICENSE +21 -0
- package/README.md +75 -0
- package/package.json +51 -0
- package/src/host/host-bindings.js +138 -0
- package/src/index.js +99 -0
- package/src/orchestration/index.js +40 -0
- package/src/orchestration/orchestrator.js +630 -0
- package/src/orchestration/runtime-result-consumer.js +54 -0
- package/src/orchestration/scene-callbacks.js +542 -0
- package/src/orchestration/scene-orchestrator.js +601 -0
- package/src/orchestration/scene-restoration.js +129 -0
- package/src/orchestration/scene-utils.js +36 -0
- package/src/runtime/active-scenes-index.js +164 -0
- package/src/runtime/audio-fsm-port.js +187 -0
- package/src/runtime/circuit-breaker.js +588 -0
- package/src/runtime/counter-reconciliation.js +308 -0
- package/src/runtime/dispatch-engine.js +1768 -0
- package/src/runtime/failure-taxonomy.js +125 -0
- package/src/runtime/hub-cancel-port.js +122 -0
- package/src/runtime/layer-config-port.js +136 -0
- package/src/runtime/lease-manager.js +607 -0
- package/src/runtime/persistence-port.js +162 -0
- package/src/runtime/placeholder-audio-port.js +117 -0
- package/src/runtime/progress-events-port.js +121 -0
- package/src/runtime/reconciliation-engine.js +2405 -0
- package/src/runtime/retry-budget-manager.js +229 -0
- package/src/runtime/runtime-metrics.js +427 -0
- package/src/runtime/runtime-result-emitter.js +123 -0
- package/src/runtime/runtime-scheduler.js +629 -0
- package/src/runtime/scene-data-port.js +114 -0
- package/src/runtime/scene-window.js +827 -0
- package/src/runtime/video-fsm-port.js +189 -0
- package/src/runtime/worker-health.js +270 -0
- package/test/counter-reconciliation.test.js +164 -0
- package/test/mocks/redis-mock.js +221 -0
- package/test/test-bindings.js +60 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// ======================================================
|
|
2
|
+
// FAILURE TAXONOMY - MINIMAL CLASSIFIER
|
|
3
|
+
// ======================================================
|
|
4
|
+
// S1 (2026-07-19): сокращён с 424 строк до ~80.
|
|
5
|
+
// Избыточные pattern-matching, severity levels, retry policies,
|
|
6
|
+
// retry metadata, error wrappers и metrics counters удалены —
|
|
7
|
+
// они не использовались в production-пути.
|
|
8
|
+
//
|
|
9
|
+
// Использование:
|
|
10
|
+
// - orchestrator.failStage(): classifyFailure() → failureType для journal
|
|
11
|
+
// - dispatch-engine.finalizeDispatch('failure'): failureType для retry budget
|
|
12
|
+
//
|
|
13
|
+
// Контракт:
|
|
14
|
+
// classifyFailure(err) → { type: 'transient'|'permanent'|'infrastructure'|'orchestration',
|
|
15
|
+
// retryable: boolean }
|
|
16
|
+
//
|
|
17
|
+
// Классификация: trans|orchestration → transient (retry), permanent/infrastructure → нет.
|
|
18
|
+
// Отброшенные патче HTTP-коды и stack-trace парсинг мёртвого retry-manager не нужны.
|
|
19
|
+
|
|
20
|
+
const logPrefix = '[FAILURE]';
|
|
21
|
+
function log(msg) { console.log(`${logPrefix} ${msg}`); }
|
|
22
|
+
|
|
23
|
+
const FailureType = {
|
|
24
|
+
TRANSIENT: 'transient',
|
|
25
|
+
PERMANENT: 'permanent',
|
|
26
|
+
INFRASTRUCTURE: 'infrastructure',
|
|
27
|
+
ORCHESTRATION: 'orchestration'
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const FailureSeverity = {
|
|
31
|
+
LOW: 'low',
|
|
32
|
+
MEDIUM: 'medium',
|
|
33
|
+
HIGH: 'high',
|
|
34
|
+
CRITICAL: 'critical'
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Минимальный набор паттернов для отличия permanent (не retry) от остального.
|
|
38
|
+
const PERMANENT_PATTERNS = [
|
|
39
|
+
/invalid.*workflow/i,
|
|
40
|
+
/invalid.*asset/i,
|
|
41
|
+
/corrupted/i,
|
|
42
|
+
/invalid.*scene.*data/i,
|
|
43
|
+
/missing.*required.*field/i,
|
|
44
|
+
/unsupported.*format/i,
|
|
45
|
+
/(.*)not.*found$/i
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
const INFRASTRUCTURE_PATTERNS = [
|
|
49
|
+
/redis.*unavailable/i,
|
|
50
|
+
/redis.*connection/i,
|
|
51
|
+
/filesystem.*error/i,
|
|
52
|
+
/ffmpeg.*missing/i,
|
|
53
|
+
/ffmpeg.*not.*found/i,
|
|
54
|
+
/disk.*full/i,
|
|
55
|
+
/out.*of.*memory/i
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
function matchPattern(message, patterns) {
|
|
59
|
+
if (!message) return false;
|
|
60
|
+
for (const p of patterns) {
|
|
61
|
+
if (p.test(message)) return true;
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Classify an error into a failure category.
|
|
68
|
+
* Returns: { type, retryable, severity, message, code }
|
|
69
|
+
*
|
|
70
|
+
* Priority: infrastructure → permanent → orchestration → transient (default).
|
|
71
|
+
* For 1-5 concurrent users этого достаточно — нет нужды в HTTP-кодах и severity logic.
|
|
72
|
+
*/
|
|
73
|
+
function classifyFailure(err) {
|
|
74
|
+
if (!err) {
|
|
75
|
+
return {
|
|
76
|
+
type: FailureType.TRANSIENT,
|
|
77
|
+
retryable: true,
|
|
78
|
+
severity: FailureSeverity.LOW,
|
|
79
|
+
message: 'Unknown error'
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const message = (err.message || err.toString() || '');
|
|
84
|
+
const code = err.code || null;
|
|
85
|
+
const lower = message.toLowerCase();
|
|
86
|
+
|
|
87
|
+
let type = FailureType.TRANSIENT;
|
|
88
|
+
let retryable = true;
|
|
89
|
+
let severity = FailureSeverity.MEDIUM;
|
|
90
|
+
|
|
91
|
+
if (matchPattern(lower, INFRASTRUCTURE_PATTERNS)) {
|
|
92
|
+
type = FailureType.INFRASTRUCTURE;
|
|
93
|
+
retryable = true;
|
|
94
|
+
severity = FailureSeverity.HIGH;
|
|
95
|
+
} else if (matchPattern(lower, PERMANENT_PATTERNS)) {
|
|
96
|
+
type = FailureType.PERMANENT;
|
|
97
|
+
retryable = false;
|
|
98
|
+
severity = FailureSeverity.HIGH;
|
|
99
|
+
} else if (/invalid.*transition|duplicate.*dispatch|stale.*lease|counter.*drift|token.*mismatch/i.test(lower)) {
|
|
100
|
+
type = FailureType.ORCHESTRATION;
|
|
101
|
+
retryable = false;
|
|
102
|
+
severity = FailureSeverity.MEDIUM;
|
|
103
|
+
} else if (code === 'EPERM' || code === 'EACCES' || code === 'ENOENT') {
|
|
104
|
+
type = FailureType.PERMANENT;
|
|
105
|
+
retryable = false;
|
|
106
|
+
severity = FailureSeverity.HIGH;
|
|
107
|
+
} else {
|
|
108
|
+
type = FailureType.TRANSIENT;
|
|
109
|
+
retryable = true;
|
|
110
|
+
severity = FailureSeverity.LOW;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return { type, retryable, severity, message, code, classifiedAt: Date.now() };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function getFailureTypeKeys() {
|
|
117
|
+
return ['transient', 'permanent', 'infrastructure', 'orchestration'];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module.exports = {
|
|
121
|
+
FailureType,
|
|
122
|
+
FailureSeverity,
|
|
123
|
+
classifyFailure,
|
|
124
|
+
getFailureTypeKeys
|
|
125
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// ======================================================
|
|
2
|
+
// O-9 PORT: HubCancelPort — runtime/orchestration
|
|
3
|
+
// hub-queue cleanup contract
|
|
4
|
+
// ======================================================
|
|
5
|
+
// The runtime and orchestration tiers must not own GPU Hub HTTP cleanup.
|
|
6
|
+
// The hub URL/API-key resolution, the DELETE /queue/clear endpoint shape,
|
|
7
|
+
// per-dispatch retry/warn behavior and the { requested, cleared, failed }
|
|
8
|
+
// result accounting stay HOST-side (backend/src/runtime/dispatch-engine.js
|
|
9
|
+
// clearHubDispatches — the same auth and error-handling contract every
|
|
10
|
+
// caller uses); the tiers purge cancelled dispatches from the hub queue
|
|
11
|
+
// ONLY through this port, wired by the composition root (backend.cjs) at
|
|
12
|
+
// startup.
|
|
13
|
+
//
|
|
14
|
+
// runtime/** + orchestration/** (tier consumers)
|
|
15
|
+
// ↓ consume hub-cancel ops through
|
|
16
|
+
// runtime/hub-cancel-port.js ← THIS PORT (tier-owned contract)
|
|
17
|
+
// ↑ wired by the host
|
|
18
|
+
// storage/hub-cancel-adapter (host adapter → dispatch-engine)
|
|
19
|
+
//
|
|
20
|
+
// This is the O-2/O-3/O-4/O-5/O-7/O-8 port convention (runtime/persistence-
|
|
21
|
+
// port.js, runtime/scene-data-port.js, runtime/placeholder-audio-port.js,
|
|
22
|
+
// runtime/progress-events-port.js, runtime/audio-fsm-port.js,
|
|
23
|
+
// runtime/video-fsm-port.js): a zero-require contract module, a fail-fast
|
|
24
|
+
// call-time resolver, one set/wired/reset surface. No hub URL, no API key,
|
|
25
|
+
// no fetch, no HTTP method/endpoint knowledge and no "future ops" cross
|
|
26
|
+
// the boundary — the contract below is EXACTLY the set of operations the
|
|
27
|
+
// two tiers use today (measured at O-9 reconnaissance, §32.26):
|
|
28
|
+
//
|
|
29
|
+
// clearHubDispatches — best-effort removal of the queue/running copies
|
|
30
|
+
// a cancelled dispatch still owns on the GPU Hub
|
|
31
|
+
// (orchestrator resetScenes cancel leg,
|
|
32
|
+
// reconciliation RELEASE_STALE_LEASE recovery leg).
|
|
33
|
+
// Returns { requested, cleared, failed }; hub
|
|
34
|
+
// unavailability is non-fatal per-id (warn + count),
|
|
35
|
+
// never throws for a single failed id.
|
|
36
|
+
//
|
|
37
|
+
// Optional-load/error semantics are the CALLER's concern, preserved
|
|
38
|
+
// exactly through the port: the reconciliation RELEASE_STALE_LEASE leg
|
|
39
|
+
// keeps its pre-O-9 `try { … } catch (hubErr) { warn }` best-effort
|
|
40
|
+
// wrapper around the port call (hub cleanup failure must not block state
|
|
41
|
+
// recovery); the orchestrator resetScenes leg keeps its plain await.
|
|
42
|
+
//
|
|
43
|
+
// Deliberately NOT in the port: lease cancellation itself (cancelActive-
|
|
44
|
+
// Dispatch / clearLeasesForScenes / clearLeasesForBookByStage / clearAll-
|
|
45
|
+
// LeasesForBook stay direct dispatch-engine calls — they are Redis-key
|
|
46
|
+
// domain operations, not hub HTTP), job dispatch (the S-3/S-6 Dispatch-
|
|
47
|
+
// Transport seam owns it), any combined "cancel everything" surface and
|
|
48
|
+
// any CRUD "for later".
|
|
49
|
+
//
|
|
50
|
+
// Docs: docs/architecture/generation-module-extraction-reconnaissance.md §32.26 (O-9)
|
|
51
|
+
// ======================================================
|
|
52
|
+
|
|
53
|
+
const OPS = [
|
|
54
|
+
'clearHubDispatches',
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
let impl = null;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Wire the host hub-cancel adapter. Called once by the composition root
|
|
61
|
+
* (backend.cjs) BEFORE any runtime/orchestration hub-cancel consumer runs.
|
|
62
|
+
* The adapter must expose every op in OPS as a function; lazy resolution
|
|
63
|
+
* of the host implementation happens at CALL time (the O-2..O-8 adapter
|
|
64
|
+
* discipline), so wiring never forces a dispatch-engine load.
|
|
65
|
+
* @param {object} adapter — map with every op in OPS as a function
|
|
66
|
+
*/
|
|
67
|
+
function setHubCancelPort(adapter) {
|
|
68
|
+
if (!adapter) {
|
|
69
|
+
throw new Error('hub-cancel-port: adapter is required');
|
|
70
|
+
}
|
|
71
|
+
for (const op of OPS) {
|
|
72
|
+
if (typeof adapter[op] !== 'function') {
|
|
73
|
+
throw new Error(`hub-cancel-port: adapter must implement ${op}()`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
impl = adapter;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Resolve the port at CALL time — fail-fast when unwired. */
|
|
80
|
+
function hubCancel() {
|
|
81
|
+
if (!impl) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
'hub-cancel-port: not wired — the composition root must call ' +
|
|
84
|
+
'setHubCancelPort(adapter) (storage/hub-cancel-adapter) ' +
|
|
85
|
+
'before runtime/orchestration hub-cancel consumers run (O-9)'
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
return impl;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Shorthand op resolver: `hubCancelOp('clearHubDispatches')(...)`.
|
|
93
|
+
* Fails fast when the port or the single op is missing — a missing hub
|
|
94
|
+
* cleanup silently degrading into a no-op would accumulate duplicate job
|
|
95
|
+
* copies a worker would later drain (audit c8b79f6).
|
|
96
|
+
* @param {string} op — op name from OPS
|
|
97
|
+
*/
|
|
98
|
+
function hubCancelOp(op) {
|
|
99
|
+
const fn = impl?.[op];
|
|
100
|
+
if (typeof fn !== 'function') {
|
|
101
|
+
throw new Error(`hub-cancel-port: op '${op}' is not wired (O-9)`);
|
|
102
|
+
}
|
|
103
|
+
return fn;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function isHubCancelPortWired() {
|
|
107
|
+
return impl !== null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Test hygiene: drop the wiring (mocha hooks). */
|
|
111
|
+
function _resetHubCancelPort() {
|
|
112
|
+
impl = null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = {
|
|
116
|
+
OPS,
|
|
117
|
+
setHubCancelPort,
|
|
118
|
+
hubCancel,
|
|
119
|
+
hubCancelOp,
|
|
120
|
+
isHubCancelPortWired,
|
|
121
|
+
_resetHubCancelPort,
|
|
122
|
+
};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// ======================================================
|
|
2
|
+
// O-10 PORT: LayerConfigPort — runtime/orchestration
|
|
3
|
+
// per-book layer-config contract
|
|
4
|
+
// ======================================================
|
|
5
|
+
// The runtime and orchestration tiers must not know the layer-config host
|
|
6
|
+
// service. The Redis key grammar (`animastor:layer-config:*`), the
|
|
7
|
+
// normalize/clamp pipeline, the durable book.json copy (Кирпич №2) and
|
|
8
|
+
// the fs scanning the service performs stay HOST-side
|
|
9
|
+
// (backend/src/services/layer-config.js); the tiers consume the
|
|
10
|
+
// per-book layer-config operations they actually use ONLY through this
|
|
11
|
+
// port, wired by the composition root (backend.cjs) at startup.
|
|
12
|
+
//
|
|
13
|
+
// runtime/** + orchestration/** (tier consumers)
|
|
14
|
+
// ↓ consume layer-config ops through
|
|
15
|
+
// runtime/layer-config-port.js ← THIS PORT (tier-owned contract)
|
|
16
|
+
// ↑ wired by the host
|
|
17
|
+
// storage/layer-config-adapter (host service adapter)
|
|
18
|
+
//
|
|
19
|
+
// This is the O-2..O-9 port convention (runtime/persistence-port.js,
|
|
20
|
+
// runtime/scene-data-port.js, runtime/placeholder-audio-port.js,
|
|
21
|
+
// runtime/progress-events-port.js, runtime/audio-fsm-port.js,
|
|
22
|
+
// runtime/video-fsm-port.js, runtime/hub-cancel-port.js): a zero-require
|
|
23
|
+
// contract module, a fail-fast call-time resolver, one set/wired/reset
|
|
24
|
+
// surface. No Redis key, no fs path, no BOOKS_DIR, no normalize/clamp
|
|
25
|
+
// rules and no "future ops" cross the boundary — the contract below is
|
|
26
|
+
// EXACTLY the set of operations the two tiers use today (measured at
|
|
27
|
+
// O-10 reconnaissance, §32.27):
|
|
28
|
+
//
|
|
29
|
+
// get — read the normalized per-book layer config
|
|
30
|
+
// (scene-orchestrator executeVideoDispatch
|
|
31
|
+
// per-type timeout leg; reconciliation-engine
|
|
32
|
+
// rebuildWorkList enabled-layers read)
|
|
33
|
+
// restoreFromBooks — heal missing Redis keys from the durable book.json
|
|
34
|
+
// copy (reconciliation-engine PHASE C6)
|
|
35
|
+
//
|
|
36
|
+
// The tier-local RAW Redis reads of `animastor:layer-config:*`
|
|
37
|
+
// (reconciliation-engine video-stall threshold, scene-window
|
|
38
|
+
// isWindowComplete/isSceneWindowDone, runtime-scheduler getLayerConfig)
|
|
39
|
+
// are NOT part of this port: they read the key in place with their own
|
|
40
|
+
// bespoke fallback semantics (partial-parse, per-field defaults) that a
|
|
41
|
+
// normalize() call would change — moving them is its own measured
|
|
42
|
+
// decision, not a rider on this seam. The raw Redis channel itself is
|
|
43
|
+
// not a host-service edge (redis is an injected parameter).
|
|
44
|
+
//
|
|
45
|
+
// Deliberately NOT in the port: set/persistToBook (routes/import
|
|
46
|
+
// workflow), getChunkSize (routes + services/agent consumers),
|
|
47
|
+
// SCOPES/ANALYSIS_MODES/DEFAULTS vocabulary (routes/services constants),
|
|
48
|
+
// key/KEY_PREFIX grammar, normalize (host-internal), and any CRUD "for
|
|
49
|
+
// later". The host adapter owns every Redis read, the durable book.json
|
|
50
|
+
// scan and every clamp rule.
|
|
51
|
+
//
|
|
52
|
+
// Optional-load/error semantics are the CALLER's concern, preserved
|
|
53
|
+
// exactly through the port: the reconciliation PHASE C6 leg keeps its
|
|
54
|
+
// pre-O-10 `try { … } catch (err) { warn; summary.errors }` wrapper with
|
|
55
|
+
// the missing-module degrade (a require failure of the host service
|
|
56
|
+
// previously left `layerConfig = null` and skipped the phase silently —
|
|
57
|
+
// the adapter's lazy call-time resolver throws at the same point the
|
|
58
|
+
// pre-O-10 require did, and the caller's catch handles it identically);
|
|
59
|
+
// the scene-orchestrator timeout leg keeps its `try { … } catch (_) {}`
|
|
60
|
+
// tolerance; the rebuildWorkList read stays a plain mandatory await.
|
|
61
|
+
//
|
|
62
|
+
// Docs: docs/architecture/generation-module-extraction-reconnaissance.md §32.27 (O-10)
|
|
63
|
+
// ======================================================
|
|
64
|
+
|
|
65
|
+
const OPS = [
|
|
66
|
+
'get',
|
|
67
|
+
'restoreFromBooks',
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
let impl = null;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Wire the host layer-config adapter. Called once by the composition root
|
|
74
|
+
* (backend.cjs) BEFORE any runtime/orchestration layer-config consumer
|
|
75
|
+
* runs. The adapter must expose every op in OPS as a function; lazy
|
|
76
|
+
* resolution of the host implementation happens at CALL time (the
|
|
77
|
+
* O-2..O-9 adapter discipline), so wiring never forces a
|
|
78
|
+
* services/layer-config load.
|
|
79
|
+
* @param {object} adapter — flat map with every op in OPS as a function.
|
|
80
|
+
*/
|
|
81
|
+
function setLayerConfigPort(adapter) {
|
|
82
|
+
if (!adapter) {
|
|
83
|
+
throw new Error('layer-config-port: adapter is required');
|
|
84
|
+
}
|
|
85
|
+
for (const op of OPS) {
|
|
86
|
+
if (typeof adapter[op] !== 'function') {
|
|
87
|
+
throw new Error(`layer-config-port: adapter must implement ${op}()`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
impl = adapter;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Resolve the port at CALL time — fail-fast when unwired. */
|
|
94
|
+
function layerConfig() {
|
|
95
|
+
if (!impl) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
'layer-config-port: not wired — the composition root must call ' +
|
|
98
|
+
'setLayerConfigPort(adapter) (storage/layer-config-adapter) ' +
|
|
99
|
+
'before runtime/orchestration layer-config consumers run (O-10)'
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
return impl;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Shorthand op resolver: `layerConfigOp('get')(...)`.
|
|
107
|
+
* Fails fast when the port or the single op is missing — callers that
|
|
108
|
+
* tolerate failure keep their own try/catch around the call (the
|
|
109
|
+
* caller-owned optional-load discipline of O-7/O-9).
|
|
110
|
+
* @param {string} op — op name from OPS
|
|
111
|
+
*/
|
|
112
|
+
function layerConfigOp(op) {
|
|
113
|
+
const fn = impl?.[op];
|
|
114
|
+
if (typeof fn !== 'function') {
|
|
115
|
+
throw new Error(`layer-config-port: op '${op}' is not wired (O-10)`);
|
|
116
|
+
}
|
|
117
|
+
return fn;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function isLayerConfigPortWired() {
|
|
121
|
+
return impl !== null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Test hygiene: drop the wiring (mocha hooks). */
|
|
125
|
+
function _resetLayerConfigPort() {
|
|
126
|
+
impl = null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
module.exports = {
|
|
130
|
+
OPS,
|
|
131
|
+
setLayerConfigPort,
|
|
132
|
+
layerConfig,
|
|
133
|
+
layerConfigOp,
|
|
134
|
+
isLayerConfigPortWired,
|
|
135
|
+
_resetLayerConfigPort,
|
|
136
|
+
};
|