@companion-surface/base 1.0.0 → 1.1.2
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/CHANGELOG.md +26 -0
- package/dist/__tests__/manifest.spec.d.ts +2 -0
- package/dist/__tests__/manifest.spec.d.ts.map +1 -0
- package/dist/__tests__/manifest.spec.js +95 -0
- package/dist/__tests__/manifest.spec.js.map +1 -0
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +9 -6
- package/dist/manifest.js.map +1 -1
- package/dist/surface-api/detection.d.ts +8 -3
- package/dist/surface-api/detection.d.ts.map +1 -1
- package/dist/surface-api/plugin.d.ts +26 -2
- package/dist/surface-api/plugin.d.ts.map +1 -1
- package/dist/surface-api/remote.d.ts +3 -3
- package/dist/surface-api/remote.d.ts.map +1 -1
- package/dist/util.d.ts +0 -21
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +0 -66
- package/dist/util.js.map +1 -1
- package/package.json +3 -3
- package/dist/__tests__/util.test.d.ts +0 -2
- package/dist/__tests__/util.test.d.ts.map +0 -1
- package/dist/__tests__/util.test.js +0 -213
- package/dist/__tests__/util.test.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.1.2](https://github.com/bitfocus/companion-surface-api/compare/companion-surface-base-v1.1.1...companion-surface-base-v1.1.2) (2026-03-18)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* force version bump ([fd38376](https://github.com/bitfocus/companion-surface-api/commit/fd383769d733bb403afd16161dc7fc8cfbca95da))
|
|
9
|
+
|
|
10
|
+
## [1.1.1](https://github.com/bitfocus/companion-surface-api/compare/companion-surface-base-v1.1.0...companion-surface-base-v1.1.1) (2026-03-18)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* validateManifest order of operations ([bf0e587](https://github.com/bitfocus/companion-surface-api/commit/bf0e587b160b02fb47a142375953e416df079ca8))
|
|
16
|
+
|
|
17
|
+
## [1.1.0](https://github.com/bitfocus/companion-surface-api/compare/companion-surface-base-v1.0.0...companion-surface-base-v1.1.0) (2026-01-05)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
* refine surfaceId generation ([#33](https://github.com/bitfocus/companion-surface-api/issues/33)) ([8f23f82](https://github.com/bitfocus/companion-surface-api/commit/8f23f8265fad79f31b243a2d67412b21935eadc2))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Bug Fixes
|
|
26
|
+
|
|
27
|
+
* remove unused StableDeviceIdGenerator ([b237c01](https://github.com/bitfocus/companion-surface-api/commit/b237c01c2ff702c709c674444adce1a3fd8df271))
|
|
28
|
+
|
|
3
29
|
## [1.0.0](https://github.com/bitfocus/companion-surface-api/compare/companion-surface-base-v0.7.0...companion-surface-base-v1.0.0) (2025-12-15)
|
|
4
30
|
|
|
5
31
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.spec.d.ts","sourceRoot":"","sources":["../../src/__tests__/manifest.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { validateSurfaceManifest } from '../manifest.js';
|
|
3
|
+
function validManifest(overrides = {}) {
|
|
4
|
+
return {
|
|
5
|
+
type: 'surface',
|
|
6
|
+
id: 'companion-surface-test',
|
|
7
|
+
name: 'Test Surface',
|
|
8
|
+
shortname: 'test',
|
|
9
|
+
description: 'A test surface module',
|
|
10
|
+
version: '1.0.0',
|
|
11
|
+
license: 'MIT',
|
|
12
|
+
repository: 'https://github.com/example/companion-surface-test',
|
|
13
|
+
bugs: 'https://github.com/example/companion-surface-test/issues',
|
|
14
|
+
maintainers: [{ name: 'Test Author' }],
|
|
15
|
+
runtime: {
|
|
16
|
+
type: 'node22',
|
|
17
|
+
apiVersion: '1.0.0',
|
|
18
|
+
entrypoint: 'index.js',
|
|
19
|
+
},
|
|
20
|
+
products: ['Test Product'],
|
|
21
|
+
keywords: ['test'],
|
|
22
|
+
usbIds: [],
|
|
23
|
+
...overrides,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
describe('validateSurfaceManifest', () => {
|
|
27
|
+
describe('basic type validation', () => {
|
|
28
|
+
it('throws when passed null', () => {
|
|
29
|
+
expect(() => validateSurfaceManifest(null, true)).toThrow('Manifest is not an object');
|
|
30
|
+
});
|
|
31
|
+
it('throws when passed undefined', () => {
|
|
32
|
+
expect(() => validateSurfaceManifest(undefined, true)).toThrow('Manifest is not an object');
|
|
33
|
+
});
|
|
34
|
+
it('throws when passed a string', () => {
|
|
35
|
+
expect(() => validateSurfaceManifest('hello', true)).toThrow('Manifest is not an object');
|
|
36
|
+
});
|
|
37
|
+
it('throws when passed a number', () => {
|
|
38
|
+
expect(() => validateSurfaceManifest(42, true)).toThrow('Manifest is not an object');
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe('schema validation', () => {
|
|
42
|
+
it('accepts a valid manifest', () => {
|
|
43
|
+
expect(() => validateSurfaceManifest(validManifest(), true)).not.toThrow();
|
|
44
|
+
});
|
|
45
|
+
it('throws when type is not surface', () => {
|
|
46
|
+
expect(() => validateSurfaceManifest(validManifest({ type: 'module' }), true)).toThrow("Manifest 'type' must be 'surface'");
|
|
47
|
+
});
|
|
48
|
+
it('throws when id is missing', () => {
|
|
49
|
+
const m = validManifest();
|
|
50
|
+
delete m.id;
|
|
51
|
+
expect(() => validateSurfaceManifest(m, true)).toThrow('Manifest validation failed');
|
|
52
|
+
});
|
|
53
|
+
it('throws when name is missing', () => {
|
|
54
|
+
const m = validManifest();
|
|
55
|
+
delete m.name;
|
|
56
|
+
expect(() => validateSurfaceManifest(m, true)).toThrow('Manifest validation failed');
|
|
57
|
+
});
|
|
58
|
+
it('throws when products is empty', () => {
|
|
59
|
+
expect(() => validateSurfaceManifest(validManifest({ products: [] }), true)).toThrow('Manifest validation failed');
|
|
60
|
+
});
|
|
61
|
+
it('throws when runtime.type is wrong', () => {
|
|
62
|
+
expect(() => validateSurfaceManifest(validManifest({ runtime: { ...validManifest().runtime, type: 'node99' } }), true)).toThrow('Manifest validation failed');
|
|
63
|
+
});
|
|
64
|
+
it('throws when runtime.apiVersion is missing', () => {
|
|
65
|
+
const m = validManifest();
|
|
66
|
+
delete m.runtime.apiVersion;
|
|
67
|
+
expect(() => validateSurfaceManifest(m, true)).toThrow('Manifest validation failed');
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe('template placeholder checks (looseChecks=false)', () => {
|
|
71
|
+
it('throws when id contains the template name', () => {
|
|
72
|
+
expect(() => validateSurfaceManifest(validManifest({ id: 'companion-surface-your-module-name' }), false)).toThrow(`Manifest incorrectly references template module 'your-module-name'`);
|
|
73
|
+
});
|
|
74
|
+
it('throws when shortname contains the template shortname', () => {
|
|
75
|
+
expect(() => validateSurfaceManifest(validManifest({ shortname: 'module-shortname' }), false)).toThrow(`Manifest incorrectly references template module 'module-shortname'`);
|
|
76
|
+
});
|
|
77
|
+
it('throws when description contains the template description', () => {
|
|
78
|
+
const desc = 'A short one line description of your module';
|
|
79
|
+
expect(() => validateSurfaceManifest(validManifest({ description: desc }), false)).toThrow(`Manifest incorrectly references template module '${desc}'`);
|
|
80
|
+
});
|
|
81
|
+
it('throws when a maintainer name contains the template name', () => {
|
|
82
|
+
expect(() => validateSurfaceManifest(validManifest({ maintainers: [{ name: 'Your name' }] }), false)).toThrow(`Manifest incorrectly references template module 'Your name'`);
|
|
83
|
+
});
|
|
84
|
+
it('throws when a maintainer email contains the template email', () => {
|
|
85
|
+
expect(() => validateSurfaceManifest(validManifest({ maintainers: [{ name: 'Test Author', email: 'Your email' }] }), false)).toThrow(`Manifest incorrectly references template module 'Your email'`);
|
|
86
|
+
});
|
|
87
|
+
it('throws when products contains the template product', () => {
|
|
88
|
+
expect(() => validateSurfaceManifest(validManifest({ products: ['Your product'] }), false)).toThrow(`Manifest incorrectly references template module 'Your product'`);
|
|
89
|
+
});
|
|
90
|
+
it('does not throw for template placeholders when looseChecks=true', () => {
|
|
91
|
+
expect(() => validateSurfaceManifest(validManifest({ products: ['Your product'] }), true)).not.toThrow();
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
//# sourceMappingURL=manifest.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.spec.js","sourceRoot":"","sources":["../../src/__tests__/manifest.spec.ts"],"names":[],"mappings":"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"}
|
package/dist/manifest.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AAKvE,mBAAmB,4BAA4B,CAAA;AAE/C,yDAAyD;AACzD,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,qBAAqB,EAAE,WAAW,EAAE,OAAO,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AAKvE,mBAAmB,4BAA4B,CAAA;AAE/C,yDAAyD;AACzD,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,qBAAqB,EAAE,WAAW,EAAE,OAAO,GAAG,IAAI,CAqCnG"}
|
package/dist/manifest.js
CHANGED
|
@@ -3,8 +3,17 @@
|
|
|
3
3
|
import validateSurfaceManifestSchema from '../generated/validate_manifest.js';
|
|
4
4
|
/** Validate that a manifest looks correctly populated */
|
|
5
5
|
export function validateSurfaceManifest(manifest, looseChecks) {
|
|
6
|
+
if (!manifest || typeof manifest !== 'object') {
|
|
7
|
+
throw new Error(`Manifest is not an object`);
|
|
8
|
+
}
|
|
6
9
|
if (manifest.type !== 'surface')
|
|
7
10
|
throw new Error(`Manifest 'type' must be 'surface'`);
|
|
11
|
+
if (!validateSurfaceManifestSchema(manifest)) {
|
|
12
|
+
const errors = validateSurfaceManifestSchema.errors;
|
|
13
|
+
if (!errors)
|
|
14
|
+
throw new Error(`Manifest failed validation with unknown reason`);
|
|
15
|
+
throw new Error(`Manifest validation failed: ${JSON.stringify(errors)}`);
|
|
16
|
+
}
|
|
8
17
|
if (!looseChecks) {
|
|
9
18
|
const manifestStr = JSON.stringify(manifest);
|
|
10
19
|
if (manifestStr.includes('your-module-name'))
|
|
@@ -22,11 +31,5 @@ export function validateSurfaceManifest(manifest, looseChecks) {
|
|
|
22
31
|
if (manifestStr.includes('Your product'))
|
|
23
32
|
throw new Error(`Manifest incorrectly references template module 'Your product'`);
|
|
24
33
|
}
|
|
25
|
-
if (!validateSurfaceManifestSchema(manifest)) {
|
|
26
|
-
const errors = validateSurfaceManifestSchema.errors;
|
|
27
|
-
if (!errors)
|
|
28
|
-
throw new Error(`Manifest failed validation with unknown reason`);
|
|
29
|
-
throw new Error(`Manifest validation failed: ${JSON.stringify(errors)}`);
|
|
30
|
-
}
|
|
31
34
|
}
|
|
32
35
|
//# sourceMappingURL=manifest.js.map
|
package/dist/manifest.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AACA,8BAA8B;AAC9B,+CAA+C;AAC/C,OAAO,6BAA6B,MAAM,mCAAmC,CAAA;AAI7E,yDAAyD;AACzD,MAAM,UAAU,uBAAuB,CAAC,QAA+B,EAAE,WAAoB;IAC5F,IAAI,QAAQ,CAAC,IAAI,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAA;IAErF,IAAI,CAAC,WAAW,EAAE,CAAC;QAClB,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;QAC5C,IAAI,WAAW,CAAC,QAAQ,CAAC,kBAAkB,CAAC;YAC3C,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAA;QAEtF,IAAI,WAAW,CAAC,QAAQ,CAAC,kBAAkB,CAAC;YAC3C,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAA;QAEtF,IAAI,WAAW,CAAC,QAAQ,CAAC,6CAA6C,CAAC;YACtE,MAAM,IAAI,KAAK,CAAC,+FAA+F,CAAC,CAAA;QAEjH,IAAI,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAA;QAE/E,IAAI,WAAW,CAAC,QAAQ,CAAC,YAAY,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAA;QAEhF,IAAI,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC;YACvC,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAA;QAElF,IAAI,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC;YACvC,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAA;IACnF,CAAC;
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AACA,8BAA8B;AAC9B,+CAA+C;AAC/C,OAAO,6BAA6B,MAAM,mCAAmC,CAAA;AAI7E,yDAAyD;AACzD,MAAM,UAAU,uBAAuB,CAAC,QAA+B,EAAE,WAAoB;IAC5F,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAA;IAC7C,CAAC;IAED,IAAI,QAAQ,CAAC,IAAI,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAA;IAErF,IAAI,CAAC,6BAA6B,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9C,MAAM,MAAM,GAAG,6BAA6B,CAAC,MAAM,CAAA;QACnD,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAA;QAE9E,MAAM,IAAI,KAAK,CAAC,+BAA+B,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IACzE,CAAC;IAED,IAAI,CAAC,WAAW,EAAE,CAAC;QAClB,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;QAC5C,IAAI,WAAW,CAAC,QAAQ,CAAC,kBAAkB,CAAC;YAC3C,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAA;QAEtF,IAAI,WAAW,CAAC,QAAQ,CAAC,kBAAkB,CAAC;YAC3C,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAA;QAEtF,IAAI,WAAW,CAAC,QAAQ,CAAC,6CAA6C,CAAC;YACtE,MAAM,IAAI,KAAK,CAAC,+FAA+F,CAAC,CAAA;QAEjH,IAAI,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAA;QAE/E,IAAI,WAAW,CAAC,QAAQ,CAAC,YAAY,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAA;QAEhF,IAAI,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC;YACvC,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAA;QAElF,IAAI,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC;YACvC,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAA;IACnF,CAAC;AACF,CAAC"}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import type EventEmitter from 'node:events';
|
|
2
|
-
import type {
|
|
2
|
+
import type { DetectionSurfaceInfo } from './plugin.js';
|
|
3
3
|
export interface SurfacePluginDetectionEvents<TInfo> {
|
|
4
4
|
/** Emitted when surfaces are detected */
|
|
5
|
-
surfacesAdded: [surfaceInfos:
|
|
5
|
+
surfacesAdded: [surfaceInfos: DetectionSurfaceInfo<TInfo>[]];
|
|
6
|
+
/**
|
|
7
|
+
* Emitted when discovered surfaces are lost
|
|
8
|
+
* Note: This is important to call, to allow the id to be reused by a newly detected surface later on
|
|
9
|
+
*/
|
|
10
|
+
surfacesRemoved: [deviceHandles: string[]];
|
|
6
11
|
}
|
|
7
12
|
/**
|
|
8
13
|
* For some plugins which only support using a builtin detection mechanism, this can be used to provide the detection info
|
|
@@ -18,6 +23,6 @@ export interface SurfacePluginDetection<TInfo> extends EventEmitter<SurfacePlugi
|
|
|
18
23
|
* You can use this to cleanup any resources/handles for this surface, as it will not be used further
|
|
19
24
|
* @param surfaceInfo The info about the surface which was rejected
|
|
20
25
|
*/
|
|
21
|
-
rejectSurface(surfaceInfo:
|
|
26
|
+
rejectSurface(surfaceInfo: DetectionSurfaceInfo<TInfo>): void;
|
|
22
27
|
}
|
|
23
28
|
//# sourceMappingURL=detection.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detection.d.ts","sourceRoot":"","sources":["../../src/surface-api/detection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,aAAa,CAAA;AAC3C,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"detection.d.ts","sourceRoot":"","sources":["../../src/surface-api/detection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,aAAa,CAAA;AAC3C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAEvD,MAAM,WAAW,4BAA4B,CAAC,KAAK;IAClD,yCAAyC;IACzC,aAAa,EAAE,CAAC,YAAY,EAAE,oBAAoB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAC5D;;;OAGG;IACH,eAAe,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,CAAC,CAAA;CAC1C;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB,CAAC,KAAK,CAAE,SAAQ,YAAY,CAAC,4BAA4B,CAAC,KAAK,CAAC,CAAC;IACvG;;;OAGG;IACH,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAE5B;;;;OAIG;IACH,aAAa,CAAC,WAAW,EAAE,oBAAoB,CAAC,KAAK,CAAC,GAAG,IAAI,CAAA;CAC7D"}
|
|
@@ -44,7 +44,7 @@ export interface SurfacePlugin<TInfo> {
|
|
|
44
44
|
* Perform a scan for devices, but not open them
|
|
45
45
|
* Note: This should only be used if the plugin uses a protocol where we don't have other handling for
|
|
46
46
|
*/
|
|
47
|
-
scanForSurfaces?: () => Promise<
|
|
47
|
+
scanForSurfaces?: () => Promise<DetectionSurfaceInfo<TInfo>[]>;
|
|
48
48
|
/**
|
|
49
49
|
* Open a discovered/known surface
|
|
50
50
|
* This can be called for multiple surfaces in parallel
|
|
@@ -60,9 +60,16 @@ export interface SurfacePlugin<TInfo> {
|
|
|
60
60
|
*/
|
|
61
61
|
export interface DiscoveredSurfaceInfo<TInfo> {
|
|
62
62
|
/**
|
|
63
|
-
*
|
|
63
|
+
* Desired id of the surface. Typically a serialnumber.
|
|
64
|
+
* It may not be opened with this id, as collisions may be resolved by the host
|
|
65
|
+
* This does not have to be unique, if collisions are found it will be given a suffix to make it unique
|
|
64
66
|
*/
|
|
65
67
|
surfaceId: string;
|
|
68
|
+
/**
|
|
69
|
+
* Set this to true if the surface id is known to not be unique and should always be given a suffix
|
|
70
|
+
* Otherwise, a suffix will only be added if a collision is detected
|
|
71
|
+
*/
|
|
72
|
+
surfaceIdIsNotUnique?: boolean;
|
|
66
73
|
/**
|
|
67
74
|
* Human friendly description of the surface. Typically a model name
|
|
68
75
|
*/
|
|
@@ -72,4 +79,21 @@ export interface DiscoveredSurfaceInfo<TInfo> {
|
|
|
72
79
|
*/
|
|
73
80
|
pluginInfo: TInfo;
|
|
74
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Information about a surface discovered via a detection/remote mechanism.
|
|
84
|
+
* Extends {@link DiscoveredSurfaceInfo} with a stable tracking handle (`deviceHandle`)
|
|
85
|
+
* that can be used to re-identify the same physical device between scans.
|
|
86
|
+
*/
|
|
87
|
+
export interface DetectionSurfaceInfo<TInfo> extends DiscoveredSurfaceInfo<TInfo> {
|
|
88
|
+
/**
|
|
89
|
+
* A stable unique identifier for the device.
|
|
90
|
+
* This is used to identify the same physical device between scans and operations, until it is disconnected. It is not shown to the user
|
|
91
|
+
*
|
|
92
|
+
* This is the same logical concept that other interfaces may refer to as `devicePath`,
|
|
93
|
+
* but is named `deviceHandle` here because, depending on the transport or platform,
|
|
94
|
+
* it may not literally be a filesystem path. For USB devices it will typically be the
|
|
95
|
+
* device path, while for other transports it can be another stable identifier.
|
|
96
|
+
*/
|
|
97
|
+
deviceHandle: string;
|
|
98
|
+
}
|
|
75
99
|
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/surface-api/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAClD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAEjD;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,KAAK;IACnC;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,sBAAsB,CAAC,KAAK,CAAC,CAAA;IAElD;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,mBAAmB,CAAC,KAAK,CAAC,CAAA;IAE5C;;;;OAIG;IACH,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAErB;;;;OAIG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAExB;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,qBAAqB,CAAC,KAAK,CAAC,GAAG,IAAI,CAAA;IAEnF;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/surface-api/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAClD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAEjD;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,KAAK;IACnC;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,sBAAsB,CAAC,KAAK,CAAC,CAAA;IAElD;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,mBAAmB,CAAC,KAAK,CAAC,CAAA;IAE5C;;;;OAIG;IACH,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAErB;;;;OAIG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAExB;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,qBAAqB,CAAC,KAAK,CAAC,GAAG,IAAI,CAAA;IAEnF;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC,oBAAoB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAE9D;;;;;;;OAOG;IACH,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAA;CAC1G;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB,CAAC,KAAK;IAC3C;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAE9B;;OAEG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB;;OAEG;IACH,UAAU,EAAE,KAAK,CAAA;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB,CAAC,KAAK,CAAE,SAAQ,qBAAqB,CAAC,KAAK,CAAC;IAChF;;;;;;;;OAQG;IACH,YAAY,EAAE,MAAM,CAAA;CACpB"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type EventEmitter from 'node:events';
|
|
2
2
|
import type { OptionsObject, SomeCompanionInputField } from './input.js';
|
|
3
3
|
import type { RemoteSurfaceConnectionInfo } from './types.js';
|
|
4
|
-
import type {
|
|
4
|
+
import type { DetectionSurfaceInfo } from './plugin.js';
|
|
5
5
|
export interface SurfacePluginRemoteEvents<TInfo> {
|
|
6
|
-
surfacesConnected: [surfaceInfos:
|
|
6
|
+
surfacesConnected: [surfaceInfos: DetectionSurfaceInfo<TInfo>[]];
|
|
7
7
|
/**
|
|
8
8
|
* Fired when surface discovery detects new surfaces
|
|
9
9
|
* This is used as suggestions in the ui of remote surfaces the user can setup
|
|
@@ -49,7 +49,7 @@ export interface SurfacePluginRemote<TInfo> extends EventEmitter<SurfacePluginRe
|
|
|
49
49
|
* You can use this to cleanup any resources/handles for this surface, as it will not be used further
|
|
50
50
|
* @param surfaceInfo The info about the surface which was rejected
|
|
51
51
|
*/
|
|
52
|
-
rejectSurface(surfaceInfo:
|
|
52
|
+
rejectSurface(surfaceInfo: DetectionSurfaceInfo<TInfo>): void;
|
|
53
53
|
}
|
|
54
54
|
export interface DiscoveredRemoteSurfaceInfo {
|
|
55
55
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../../src/surface-api/remote.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,aAAa,CAAA;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AACxE,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAA;AAC7D,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../../src/surface-api/remote.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,aAAa,CAAA;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AACxE,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAA;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAEvD,MAAM,WAAW,yBAAyB,CAAC,KAAK;IAC/C,iBAAiB,EAAE,CAAC,YAAY,EAAE,oBAAoB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAGhE;;;OAGG;IACH,gBAAgB,EAAE,CAAC,eAAe,EAAE,2BAA2B,EAAE,CAAC,CAAA;IAClE;;;OAGG;IACH,oBAAoB,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,CAAC,CAAA;CAC/C;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB,CAAC,KAAK,CAAE,SAAQ,YAAY,CAAC,yBAAyB,CAAC,KAAK,CAAC,CAAC;IACjG;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,uBAAuB,EAAE,CAAA;IAEhD;;;;;;;OAOG;IACH,QAAQ,CAAC,4BAA4B,EAAE,MAAM,GAAG,IAAI,CAAA;IAEpD;;;OAGG;IACH,gBAAgB,CAAC,eAAe,EAAE,2BAA2B,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAE/E;;;OAGG;IACH,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAEvD;;;;OAIG;IACH,aAAa,CAAC,WAAW,EAAE,oBAAoB,CAAC,KAAK,CAAC,GAAG,IAAI,CAAA;CAC7D;AAED,MAAM,WAAW,2BAA2B;IAC3C;;OAEG;IACH,EAAE,EAAE,MAAM,CAAA;IACV;;OAEG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAA;IAKnB;;;OAGG;IACH,MAAM,EAAE,aAAa,CAAA;CACrB"}
|
package/dist/util.d.ts
CHANGED
|
@@ -5,25 +5,4 @@ export interface RgbColor {
|
|
|
5
5
|
b: number;
|
|
6
6
|
}
|
|
7
7
|
export declare function parseColor(color: string | undefined): RgbColor;
|
|
8
|
-
/**
|
|
9
|
-
* Generator for stable fake serial numbers for usb devices without hardware serials.
|
|
10
|
-
*
|
|
11
|
-
* Generates deterministic serials by hashing just the uniqueness key and an index.
|
|
12
|
-
*/
|
|
13
|
-
export declare class StableDeviceIdGenerator {
|
|
14
|
-
#private;
|
|
15
|
-
/**
|
|
16
|
-
* Generate a stable serial number for a device without a hardware serial.
|
|
17
|
-
*
|
|
18
|
-
* @param uniquenessKey - Identifier for the device type (e.g., "vendorId:productId")
|
|
19
|
-
* @param path - Device path (e.g., "/dev/hidraw0")
|
|
20
|
-
* @returns A stable fake serial number
|
|
21
|
-
*/
|
|
22
|
-
generateId(uniquenessKey: string, devicePath: string): string;
|
|
23
|
-
/**
|
|
24
|
-
* Prune cache entries for devices that were not seen during the current scan.
|
|
25
|
-
* This should be called once after processing all devices in a scan.
|
|
26
|
-
*/
|
|
27
|
-
pruneNotSeen(): void;
|
|
28
|
-
}
|
|
29
8
|
//# sourceMappingURL=util.d.ts.map
|
package/dist/util.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,EAAE,EAAE,KAAK,GAAG,IAAI,CAE3C;AAED,MAAM,WAAW,QAAQ;IACxB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACT;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,CAM9D"}
|
package/dist/util.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
1
|
export function assertNever(_v) {
|
|
3
2
|
// Nothing to do
|
|
4
3
|
}
|
|
@@ -8,69 +7,4 @@ export function parseColor(color) {
|
|
|
8
7
|
const b = color ? parseInt(color.substr(5, 2), 16) : 0;
|
|
9
8
|
return { r, g, b };
|
|
10
9
|
}
|
|
11
|
-
/**
|
|
12
|
-
* Generator for stable fake serial numbers for usb devices without hardware serials.
|
|
13
|
-
*
|
|
14
|
-
* Generates deterministic serials by hashing just the uniqueness key and an index.
|
|
15
|
-
*/
|
|
16
|
-
export class StableDeviceIdGenerator {
|
|
17
|
-
/**
|
|
18
|
-
* Cache of device info to fake serial
|
|
19
|
-
* Map: `${uniquenessKey}||${devicePath}` -> string
|
|
20
|
-
*/
|
|
21
|
-
#previousForDevicePath = new Map();
|
|
22
|
-
/**
|
|
23
|
-
* Track which device paths were seen during the current scan
|
|
24
|
-
* Set of keys: `${uniquenessKey}||${devicePath}`
|
|
25
|
-
*/
|
|
26
|
-
#seenThisScan = new Set();
|
|
27
|
-
/**
|
|
28
|
-
* Track which serialnumbers were returned during this scan
|
|
29
|
-
*/
|
|
30
|
-
#returnedThisScan = new Set();
|
|
31
|
-
/**
|
|
32
|
-
* Generate a stable serial number for a device without a hardware serial.
|
|
33
|
-
*
|
|
34
|
-
* @param uniquenessKey - Identifier for the device type (e.g., "vendorId:productId")
|
|
35
|
-
* @param path - Device path (e.g., "/dev/hidraw0")
|
|
36
|
-
* @returns A stable fake serial number
|
|
37
|
-
*/
|
|
38
|
-
generateId(uniquenessKey, devicePath) {
|
|
39
|
-
const pathCacheKey = `${uniquenessKey}||${devicePath}`;
|
|
40
|
-
// Mark as seen during this scan
|
|
41
|
-
this.#seenThisScan.add(pathCacheKey);
|
|
42
|
-
// If there is something cached against the devicePath, use that
|
|
43
|
-
const cachedSerial = this.#previousForDevicePath.get(pathCacheKey);
|
|
44
|
-
if (cachedSerial)
|
|
45
|
-
return cachedSerial;
|
|
46
|
-
// Loop until we find a non-colliding ID
|
|
47
|
-
for (let i = 0;; i++) {
|
|
48
|
-
const fakeSerial = createHash('sha1').update(`${uniquenessKey}||${i}`).digest('hex');
|
|
49
|
-
if (!this.#returnedThisScan.has(fakeSerial)) {
|
|
50
|
-
this.#returnedThisScan.add(fakeSerial);
|
|
51
|
-
this.#previousForDevicePath.set(pathCacheKey, fakeSerial);
|
|
52
|
-
return fakeSerial;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Prune cache entries for devices that were not seen during the current scan.
|
|
58
|
-
* This should be called once after processing all devices in a scan.
|
|
59
|
-
*/
|
|
60
|
-
pruneNotSeen() {
|
|
61
|
-
// Remove path cache entries that weren't seen
|
|
62
|
-
for (const [key] of this.#previousForDevicePath.entries()) {
|
|
63
|
-
if (!this.#seenThisScan.has(key)) {
|
|
64
|
-
this.#previousForDevicePath.delete(key);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
// Clear the seen set for the next scan
|
|
68
|
-
this.#seenThisScan.clear();
|
|
69
|
-
this.#returnedThisScan.clear();
|
|
70
|
-
// Prepopulate the returned set with existing serials to avoid collisions
|
|
71
|
-
for (const serial of this.#previousForDevicePath.values()) {
|
|
72
|
-
this.#returnedThisScan.add(serial);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
10
|
//# sourceMappingURL=util.js.map
|
package/dist/util.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,WAAW,CAAC,EAAS;IACpC,gBAAgB;AACjB,CAAC;AAQD,MAAM,UAAU,UAAU,CAAC,KAAyB;IACnD,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACtD,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACtD,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAEtD,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAA;AACnB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@companion-surface/base",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "https://github.com/bitfocus/companion-surface-api",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"tslib": "^2.8.1"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"ajv": "^8.
|
|
36
|
-
"typedoc": "^0.28.
|
|
35
|
+
"ajv": "^8.18.0",
|
|
36
|
+
"typedoc": "^0.28.17"
|
|
37
37
|
}
|
|
38
38
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"util.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/util.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'vitest';
|
|
2
|
-
import { StableDeviceIdGenerator } from '../util.js';
|
|
3
|
-
describe('StableDeviceIdGenerator', () => {
|
|
4
|
-
test('generates different IDs for different uniqueness keys', () => {
|
|
5
|
-
const generator = new StableDeviceIdGenerator();
|
|
6
|
-
const id1 = generator.generateId('1234:5678', '/dev/hidraw0');
|
|
7
|
-
const id2 = generator.generateId('1234:9999', '/dev/hidraw1');
|
|
8
|
-
expect(id1).not.toBe(id2);
|
|
9
|
-
expect(id1).toHaveLength(40); // SHA1 hex digest length
|
|
10
|
-
expect(id2).toHaveLength(40);
|
|
11
|
-
});
|
|
12
|
-
test('generates different IDs for same uniqueness key with different paths', () => {
|
|
13
|
-
const generator = new StableDeviceIdGenerator();
|
|
14
|
-
const id1 = generator.generateId('1234:5678', '/dev/hidraw0');
|
|
15
|
-
const id2 = generator.generateId('1234:5678', '/dev/hidraw1');
|
|
16
|
-
expect(id1).not.toBe(id2);
|
|
17
|
-
});
|
|
18
|
-
test('returns same ID for same device path on repeated calls', () => {
|
|
19
|
-
const generator = new StableDeviceIdGenerator();
|
|
20
|
-
const id1 = generator.generateId('1234:5678', '/dev/hidraw0');
|
|
21
|
-
const id2 = generator.generateId('1234:5678', '/dev/hidraw0');
|
|
22
|
-
expect(id1).toBe(id2);
|
|
23
|
-
});
|
|
24
|
-
test('generates stable IDs with counter increment', () => {
|
|
25
|
-
const generator = new StableDeviceIdGenerator();
|
|
26
|
-
const id1 = generator.generateId('1234:5678', '/dev/hidraw0');
|
|
27
|
-
const id2 = generator.generateId('1234:5678', '/dev/hidraw1');
|
|
28
|
-
const id3 = generator.generateId('1234:5678', '/dev/hidraw2');
|
|
29
|
-
// All should be unique
|
|
30
|
-
expect(new Set([id1, id2, id3]).size).toBe(3);
|
|
31
|
-
});
|
|
32
|
-
test('separate instances have independent state', () => {
|
|
33
|
-
const generator1 = new StableDeviceIdGenerator();
|
|
34
|
-
const generator2 = new StableDeviceIdGenerator();
|
|
35
|
-
const id1 = generator1.generateId('1234:5678', '/dev/hidraw0');
|
|
36
|
-
const id2 = generator2.generateId('1234:5678', '/dev/hidraw1');
|
|
37
|
-
// Same inputs in different instances should produce same ID
|
|
38
|
-
expect(id1).toBe(id2);
|
|
39
|
-
});
|
|
40
|
-
test('handles multiple devices with same uniqueness key in batch', () => {
|
|
41
|
-
const generator = new StableDeviceIdGenerator();
|
|
42
|
-
// Simulate 3 identical devices (same vendor/product) with different paths
|
|
43
|
-
const ids = [
|
|
44
|
-
generator.generateId('1234:5678', '/dev/hidraw0'),
|
|
45
|
-
generator.generateId('1234:5678', '/dev/hidraw1'),
|
|
46
|
-
generator.generateId('1234:5678', '/dev/hidraw2'),
|
|
47
|
-
];
|
|
48
|
-
// All should be unique
|
|
49
|
-
expect(new Set(ids).size).toBe(3);
|
|
50
|
-
// Requesting same path again should return cached ID
|
|
51
|
-
expect(generator.generateId('1234:5678', '/dev/hidraw1')).toBe(ids[1]);
|
|
52
|
-
});
|
|
53
|
-
test('generates valid hex SHA1 hashes', () => {
|
|
54
|
-
const generator = new StableDeviceIdGenerator();
|
|
55
|
-
const id = generator.generateId('1234:5678', '/dev/hidraw0');
|
|
56
|
-
// Should be 40 character hex string
|
|
57
|
-
expect(id).toMatch(/^[0-9a-f]{40}$/);
|
|
58
|
-
});
|
|
59
|
-
test('deduplicates multiple endpoints of same device', () => {
|
|
60
|
-
const generator = new StableDeviceIdGenerator();
|
|
61
|
-
// Simulate same device with multiple HID endpoints (same path)
|
|
62
|
-
const id1 = generator.generateId('1234:5678', '/dev/hidraw0');
|
|
63
|
-
const id2 = generator.generateId('1234:5678', '/dev/hidraw0');
|
|
64
|
-
const id3 = generator.generateId('1234:5678', '/dev/hidraw0');
|
|
65
|
-
expect(id1).toBe(id2);
|
|
66
|
-
expect(id2).toBe(id3);
|
|
67
|
-
});
|
|
68
|
-
test('handles empty uniqueness key', () => {
|
|
69
|
-
const generator = new StableDeviceIdGenerator();
|
|
70
|
-
const id1 = generator.generateId('', '/dev/hidraw0');
|
|
71
|
-
const id2 = generator.generateId('', '/dev/hidraw1');
|
|
72
|
-
expect(id1).not.toBe(id2);
|
|
73
|
-
expect(id1).toHaveLength(40);
|
|
74
|
-
});
|
|
75
|
-
test('real-world scenario: 2 identical Stream Decks', () => {
|
|
76
|
-
const generator = new StableDeviceIdGenerator();
|
|
77
|
-
// Same vendor:product, different USB paths
|
|
78
|
-
const deck1 = generator.generateId('4057:96', '/dev/hidraw0');
|
|
79
|
-
const deck2 = generator.generateId('4057:96', '/dev/hidraw1');
|
|
80
|
-
expect(deck1).not.toBe(deck2);
|
|
81
|
-
expect(deck1).toHaveLength(40);
|
|
82
|
-
expect(deck2).toHaveLength(40);
|
|
83
|
-
// Re-enumerating same devices should return same IDs
|
|
84
|
-
expect(generator.generateId('4057:96', '/dev/hidraw0')).toBe(deck1);
|
|
85
|
-
expect(generator.generateId('4057:96', '/dev/hidraw1')).toBe(deck2);
|
|
86
|
-
});
|
|
87
|
-
test('pruneNotSeen removes devices not seen in current scan', () => {
|
|
88
|
-
const generator = new StableDeviceIdGenerator();
|
|
89
|
-
// First scan: 3 devices
|
|
90
|
-
const id1 = generator.generateId('1234:5678', '/dev/hidraw0');
|
|
91
|
-
const id2 = generator.generateId('1234:5678', '/dev/hidraw1');
|
|
92
|
-
const id3 = generator.generateId('1234:5678', '/dev/hidraw2');
|
|
93
|
-
generator.pruneNotSeen();
|
|
94
|
-
// Second scan: only 2 devices (hidraw1 disconnected)
|
|
95
|
-
const id1Again = generator.generateId('1234:5678', '/dev/hidraw0');
|
|
96
|
-
const id3Again = generator.generateId('1234:5678', '/dev/hidraw2');
|
|
97
|
-
// IDs should be preserved for devices still present
|
|
98
|
-
expect(id1Again).toBe(id1);
|
|
99
|
-
expect(id3Again).toBe(id3);
|
|
100
|
-
generator.pruneNotSeen();
|
|
101
|
-
// Third scan: hidraw1 reconnects, should get a NEW ID (not cached)
|
|
102
|
-
const id1Third = generator.generateId('1234:5678', '/dev/hidraw0');
|
|
103
|
-
const id2Reconnect = generator.generateId('1234:5678', '/dev/hidraw1');
|
|
104
|
-
const id3Third = generator.generateId('1234:5678', '/dev/hidraw2');
|
|
105
|
-
expect(id1Third).toBe(id1);
|
|
106
|
-
expect(id3Third).toBe(id3);
|
|
107
|
-
// id2Reconnect can reuse the same hash since it was pruned and counter is deterministic
|
|
108
|
-
// It gets index 1 again which produces the same hash as before
|
|
109
|
-
expect(id2Reconnect).toBe(id2);
|
|
110
|
-
});
|
|
111
|
-
test('pruneNotSeen clears scan tracking sets', () => {
|
|
112
|
-
const generator = new StableDeviceIdGenerator();
|
|
113
|
-
// First scan
|
|
114
|
-
const id1 = generator.generateId('1234:5678', '/dev/hidraw0');
|
|
115
|
-
const id2 = generator.generateId('1234:5678', '/dev/hidraw1');
|
|
116
|
-
generator.pruneNotSeen();
|
|
117
|
-
// Second scan - should be able to reuse index 0, 1 etc
|
|
118
|
-
const id3 = generator.generateId('5678:1234', '/dev/hidraw3');
|
|
119
|
-
const id4 = generator.generateId('5678:1234', '/dev/hidraw4');
|
|
120
|
-
// All should be unique
|
|
121
|
-
expect(new Set([id1, id2, id3, id4]).size).toBe(4);
|
|
122
|
-
});
|
|
123
|
-
test('state preserved across multiple scan cycles', () => {
|
|
124
|
-
const generator = new StableDeviceIdGenerator();
|
|
125
|
-
// Scan 1: 3 devices
|
|
126
|
-
const scan1_dev0 = generator.generateId('1234:5678', '/dev/hidraw0');
|
|
127
|
-
const scan1_dev1 = generator.generateId('1234:5678', '/dev/hidraw1');
|
|
128
|
-
const scan1_dev2 = generator.generateId('1234:5678', '/dev/hidraw2');
|
|
129
|
-
generator.pruneNotSeen();
|
|
130
|
-
// Scan 2: same 3 devices
|
|
131
|
-
const scan2_dev0 = generator.generateId('1234:5678', '/dev/hidraw0');
|
|
132
|
-
const scan2_dev1 = generator.generateId('1234:5678', '/dev/hidraw1');
|
|
133
|
-
const scan2_dev2 = generator.generateId('1234:5678', '/dev/hidraw2');
|
|
134
|
-
generator.pruneNotSeen();
|
|
135
|
-
// Scan 3: same 3 devices
|
|
136
|
-
const scan3_dev0 = generator.generateId('1234:5678', '/dev/hidraw0');
|
|
137
|
-
const scan3_dev1 = generator.generateId('1234:5678', '/dev/hidraw1');
|
|
138
|
-
const scan3_dev2 = generator.generateId('1234:5678', '/dev/hidraw2');
|
|
139
|
-
generator.pruneNotSeen();
|
|
140
|
-
// All scans should return identical IDs for same paths
|
|
141
|
-
expect(scan2_dev0).toBe(scan1_dev0);
|
|
142
|
-
expect(scan2_dev1).toBe(scan1_dev1);
|
|
143
|
-
expect(scan2_dev2).toBe(scan1_dev2);
|
|
144
|
-
expect(scan3_dev0).toBe(scan1_dev0);
|
|
145
|
-
expect(scan3_dev1).toBe(scan1_dev1);
|
|
146
|
-
expect(scan3_dev2).toBe(scan1_dev2);
|
|
147
|
-
});
|
|
148
|
-
test('device reconnection at different path reuses counter', () => {
|
|
149
|
-
const generator = new StableDeviceIdGenerator();
|
|
150
|
-
// Scan 1: device at hidraw0
|
|
151
|
-
const id1 = generator.generateId('1234:5678', '/dev/hidraw0');
|
|
152
|
-
generator.pruneNotSeen();
|
|
153
|
-
// Scan 2: device disconnected (not seen)
|
|
154
|
-
generator.pruneNotSeen();
|
|
155
|
-
// Scan 3: same device type reconnects at different path
|
|
156
|
-
const id2 = generator.generateId('1234:5678', '/dev/hidraw5');
|
|
157
|
-
generator.pruneNotSeen();
|
|
158
|
-
// Gets same ID because counter resets and it's index 0 again for this uniqueness key
|
|
159
|
-
expect(id2).toBe(id1);
|
|
160
|
-
// But now if we add another device of same type, it gets different ID
|
|
161
|
-
const id3 = generator.generateId('1234:5678', '/dev/hidraw6');
|
|
162
|
-
expect(id3).not.toBe(id2);
|
|
163
|
-
});
|
|
164
|
-
test('handles partial device changes between scans', () => {
|
|
165
|
-
const generator = new StableDeviceIdGenerator();
|
|
166
|
-
// Scan 1: 4 devices
|
|
167
|
-
const dev0_scan1 = generator.generateId('1234:5678', '/dev/hidraw0');
|
|
168
|
-
const _dev1_scan1 = generator.generateId('1234:5678', '/dev/hidraw1');
|
|
169
|
-
const _dev2_scan1 = generator.generateId('1234:5678', '/dev/hidraw2');
|
|
170
|
-
const dev3_scan1 = generator.generateId('1234:5678', '/dev/hidraw3');
|
|
171
|
-
generator.pruneNotSeen();
|
|
172
|
-
// Scan 2: hidraw1 and hidraw2 removed, hidraw4 added
|
|
173
|
-
const dev0_scan2 = generator.generateId('1234:5678', '/dev/hidraw0');
|
|
174
|
-
const dev3_scan2 = generator.generateId('1234:5678', '/dev/hidraw3');
|
|
175
|
-
const dev4_scan2 = generator.generateId('1234:5678', '/dev/hidraw4');
|
|
176
|
-
generator.pruneNotSeen();
|
|
177
|
-
// Persistent devices keep their IDs
|
|
178
|
-
expect(dev0_scan2).toBe(dev0_scan1);
|
|
179
|
-
expect(dev3_scan2).toBe(dev3_scan1);
|
|
180
|
-
// New device gets next available counter index (reuses counter after reset)
|
|
181
|
-
// After reset, counter 0 goes to dev0, counter 1 goes to dev3, counter 2 goes to dev4
|
|
182
|
-
expect(dev4_scan2).not.toBe(dev0_scan1);
|
|
183
|
-
expect(dev4_scan2).not.toBe(dev3_scan1);
|
|
184
|
-
// All current IDs are unique
|
|
185
|
-
expect(new Set([dev0_scan2, dev3_scan2, dev4_scan2]).size).toBe(3);
|
|
186
|
-
});
|
|
187
|
-
test('multiple device types tracked independently', () => {
|
|
188
|
-
const generator = new StableDeviceIdGenerator();
|
|
189
|
-
// Scan 1: different device types
|
|
190
|
-
const typeA_dev0 = generator.generateId('1111:2222', '/dev/hidraw0');
|
|
191
|
-
const typeB_dev1 = generator.generateId('3333:4444', '/dev/hidraw1');
|
|
192
|
-
const _typeA_dev2 = generator.generateId('1111:2222', '/dev/hidraw2');
|
|
193
|
-
generator.pruneNotSeen();
|
|
194
|
-
// Scan 2: one device of each type removed
|
|
195
|
-
const typeA_dev0_s2 = generator.generateId('1111:2222', '/dev/hidraw0');
|
|
196
|
-
const typeB_dev1_s2 = generator.generateId('3333:4444', '/dev/hidraw1');
|
|
197
|
-
generator.pruneNotSeen();
|
|
198
|
-
// IDs should be preserved
|
|
199
|
-
expect(typeA_dev0_s2).toBe(typeA_dev0);
|
|
200
|
-
expect(typeB_dev1_s2).toBe(typeB_dev1);
|
|
201
|
-
// Scan 3: add another typeA device at new path
|
|
202
|
-
const typeA_dev0_s3 = generator.generateId('1111:2222', '/dev/hidraw0');
|
|
203
|
-
const typeB_dev1_s3 = generator.generateId('3333:4444', '/dev/hidraw1');
|
|
204
|
-
const typeA_dev3_s3 = generator.generateId('1111:2222', '/dev/hidraw3');
|
|
205
|
-
expect(typeA_dev0_s3).toBe(typeA_dev0);
|
|
206
|
-
expect(typeB_dev1_s3).toBe(typeB_dev1);
|
|
207
|
-
// New path for typeA gets next counter index (different from dev0)
|
|
208
|
-
expect(typeA_dev3_s3).not.toBe(typeA_dev0);
|
|
209
|
-
// All current devices have unique IDs
|
|
210
|
-
expect(new Set([typeA_dev0_s3, typeB_dev1_s3, typeA_dev3_s3]).size).toBe(3);
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
//# sourceMappingURL=util.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"util.test.js","sourceRoot":"","sources":["../../src/__tests__/util.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AAEpD,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACxC,IAAI,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAClE,MAAM,SAAS,GAAG,IAAI,uBAAuB,EAAE,CAAA;QAE/C,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAC7D,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAE7D,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACzB,MAAM,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAA,CAAC,yBAAyB;QACtD,MAAM,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAA;IAC7B,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,sEAAsE,EAAE,GAAG,EAAE;QACjF,MAAM,SAAS,GAAG,IAAI,uBAAuB,EAAE,CAAA;QAE/C,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAC7D,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAE7D,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC1B,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,wDAAwD,EAAE,GAAG,EAAE;QACnE,MAAM,SAAS,GAAG,IAAI,uBAAuB,EAAE,CAAA;QAE/C,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAC7D,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAE7D,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACtB,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACxD,MAAM,SAAS,GAAG,IAAI,uBAAuB,EAAE,CAAA;QAE/C,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAC7D,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAC7D,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAE7D,uBAAuB;QACvB,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAC9C,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACtD,MAAM,UAAU,GAAG,IAAI,uBAAuB,EAAE,CAAA;QAChD,MAAM,UAAU,GAAG,IAAI,uBAAuB,EAAE,CAAA;QAEhD,MAAM,GAAG,GAAG,UAAU,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAC9D,MAAM,GAAG,GAAG,UAAU,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAE9D,4DAA4D;QAC5D,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACtB,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACvE,MAAM,SAAS,GAAG,IAAI,uBAAuB,EAAE,CAAA;QAE/C,0EAA0E;QAC1E,MAAM,GAAG,GAAG;YACX,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC;YACjD,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC;YACjD,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC;SACjD,CAAA;QAED,uBAAuB;QACvB,MAAM,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAEjC,qDAAqD;QACrD,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;IACvE,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,iCAAiC,EAAE,GAAG,EAAE;QAC5C,MAAM,SAAS,GAAG,IAAI,uBAAuB,EAAE,CAAA;QAE/C,MAAM,EAAE,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAE5D,oCAAoC;QACpC,MAAM,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAA;IACrC,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,gDAAgD,EAAE,GAAG,EAAE;QAC3D,MAAM,SAAS,GAAG,IAAI,uBAAuB,EAAE,CAAA;QAE/C,+DAA+D;QAC/D,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAC7D,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAC7D,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAE7D,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACrB,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACtB,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACzC,MAAM,SAAS,GAAG,IAAI,uBAAuB,EAAE,CAAA;QAE/C,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,EAAE,EAAE,cAAc,CAAC,CAAA;QACpD,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,EAAE,EAAE,cAAc,CAAC,CAAA;QAEpD,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACzB,MAAM,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAA;IAC7B,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,+CAA+C,EAAE,GAAG,EAAE;QAC1D,MAAM,SAAS,GAAG,IAAI,uBAAuB,EAAE,CAAA;QAE/C,2CAA2C;QAC3C,MAAM,KAAK,GAAG,SAAS,CAAC,UAAU,CAAC,SAAS,EAAE,cAAc,CAAC,CAAA;QAC7D,MAAM,KAAK,GAAG,SAAS,CAAC,UAAU,CAAC,SAAS,EAAE,cAAc,CAAC,CAAA;QAE7D,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAC7B,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAA;QAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAA;QAE9B,qDAAqD;QACrD,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACnE,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACpE,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAClE,MAAM,SAAS,GAAG,IAAI,uBAAuB,EAAE,CAAA;QAE/C,wBAAwB;QACxB,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAC7D,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAC7D,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAE7D,SAAS,CAAC,YAAY,EAAE,CAAA;QAExB,qDAAqD;QACrD,MAAM,QAAQ,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAClE,MAAM,QAAQ,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAElE,oDAAoD;QACpD,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAC1B,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAE1B,SAAS,CAAC,YAAY,EAAE,CAAA;QAExB,mEAAmE;QACnE,MAAM,QAAQ,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAClE,MAAM,YAAY,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACtE,MAAM,QAAQ,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAElE,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAC1B,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAC1B,wFAAwF;QACxF,+DAA+D;QAC/D,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC/B,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,wCAAwC,EAAE,GAAG,EAAE;QACnD,MAAM,SAAS,GAAG,IAAI,uBAAuB,EAAE,CAAA;QAE/C,aAAa;QACb,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAC7D,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAE7D,SAAS,CAAC,YAAY,EAAE,CAAA;QAExB,uDAAuD;QACvD,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAC7D,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAE7D,uBAAuB;QACvB,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACnD,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACxD,MAAM,SAAS,GAAG,IAAI,uBAAuB,EAAE,CAAA;QAE/C,oBAAoB;QACpB,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACpE,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACpE,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACpE,SAAS,CAAC,YAAY,EAAE,CAAA;QAExB,yBAAyB;QACzB,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACpE,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACpE,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACpE,SAAS,CAAC,YAAY,EAAE,CAAA;QAExB,yBAAyB;QACzB,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACpE,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACpE,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACpE,SAAS,CAAC,YAAY,EAAE,CAAA;QAExB,uDAAuD;QACvD,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QACnC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QACnC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QACnC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QACnC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QACnC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IACpC,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,sDAAsD,EAAE,GAAG,EAAE;QACjE,MAAM,SAAS,GAAG,IAAI,uBAAuB,EAAE,CAAA;QAE/C,4BAA4B;QAC5B,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAC7D,SAAS,CAAC,YAAY,EAAE,CAAA;QAExB,yCAAyC;QACzC,SAAS,CAAC,YAAY,EAAE,CAAA;QAExB,wDAAwD;QACxD,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAC7D,SAAS,CAAC,YAAY,EAAE,CAAA;QAExB,qFAAqF;QACrF,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAErB,sEAAsE;QACtE,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAC7D,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC1B,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACzD,MAAM,SAAS,GAAG,IAAI,uBAAuB,EAAE,CAAA;QAE/C,oBAAoB;QACpB,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACpE,MAAM,WAAW,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACrE,MAAM,WAAW,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACrE,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACpE,SAAS,CAAC,YAAY,EAAE,CAAA;QAExB,qDAAqD;QACrD,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACpE,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACpE,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACpE,SAAS,CAAC,YAAY,EAAE,CAAA;QAExB,oCAAoC;QACpC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QACnC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QAEnC,4EAA4E;QAC5E,sFAAsF;QACtF,MAAM,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QACvC,MAAM,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QAEvC,6BAA6B;QAC7B,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACnE,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACxD,MAAM,SAAS,GAAG,IAAI,uBAAuB,EAAE,CAAA;QAE/C,iCAAiC;QACjC,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACpE,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACpE,MAAM,WAAW,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACrE,SAAS,CAAC,YAAY,EAAE,CAAA;QAExB,0CAA0C;QAC1C,MAAM,aAAa,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACvE,MAAM,aAAa,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACvE,SAAS,CAAC,YAAY,EAAE,CAAA;QAExB,0BAA0B;QAC1B,MAAM,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QACtC,MAAM,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QAEtC,+CAA+C;QAC/C,MAAM,aAAa,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACvE,MAAM,aAAa,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QACvE,MAAM,aAAa,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAEvE,MAAM,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QACtC,MAAM,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QACtC,mEAAmE;QACnE,MAAM,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QAC1C,sCAAsC;QACtC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,aAAa,EAAE,aAAa,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAC5E,CAAC,CAAC,CAAA;AACH,CAAC,CAAC,CAAA"}
|