@clockworkdog/cogs-client 2.11.2 → 3.0.0-alpha.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/dist/browser/index.mjs +3575 -1033
- package/dist/browser/index.umd.js +22 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/types/CogsClientMessage.d.ts +19 -1
- package/dist/types/MediaSchema.d.ts +199 -0
- package/dist/types/MediaSchema.js +153 -0
- package/package.json +8 -5
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export type TemporalProperties = z.infer<typeof TemporalProperties>;
|
|
3
|
+
declare const TemporalProperties: z.ZodObject<{
|
|
4
|
+
t: z.ZodNumber;
|
|
5
|
+
rate: z.ZodNumber;
|
|
6
|
+
}, z.core.$strip>;
|
|
7
|
+
export type VisualProperties = z.infer<typeof VisualProperties>;
|
|
8
|
+
declare const VisualProperties: z.ZodObject<{
|
|
9
|
+
opacity: z.ZodNumber;
|
|
10
|
+
}, z.core.$strip>;
|
|
11
|
+
export type AudialProperties = z.infer<typeof AudialProperties>;
|
|
12
|
+
declare const AudialProperties: z.ZodObject<{
|
|
13
|
+
volume: z.ZodNumber;
|
|
14
|
+
}, z.core.$strip>;
|
|
15
|
+
export type ImageMetadata = z.infer<typeof ImageMetadata>;
|
|
16
|
+
declare const ImageMetadata: z.ZodObject<{
|
|
17
|
+
type: z.ZodLiteral<"image">;
|
|
18
|
+
file: z.ZodString;
|
|
19
|
+
fit: z.ZodUnion<readonly [z.ZodLiteral<"contain">, z.ZodLiteral<"cover">, z.ZodLiteral<"none">]>;
|
|
20
|
+
}, z.core.$strip>;
|
|
21
|
+
export type AudioMetadata = z.infer<typeof AudioMetadata>;
|
|
22
|
+
declare const AudioMetadata: z.ZodObject<{
|
|
23
|
+
type: z.ZodLiteral<"audio">;
|
|
24
|
+
file: z.ZodString;
|
|
25
|
+
audioOutput: z.ZodString;
|
|
26
|
+
}, z.core.$strip>;
|
|
27
|
+
export type VideoMetadata = z.infer<typeof VideoMetadata>;
|
|
28
|
+
declare const VideoMetadata: z.ZodObject<{
|
|
29
|
+
type: z.ZodLiteral<"video">;
|
|
30
|
+
file: z.ZodString;
|
|
31
|
+
audioOutput: z.ZodString;
|
|
32
|
+
fit: z.ZodUnion<readonly [z.ZodLiteral<"contain">, z.ZodLiteral<"cover">, z.ZodLiteral<"none">]>;
|
|
33
|
+
}, z.core.$strip>;
|
|
34
|
+
export type NullKeyframe = z.infer<typeof NullKeyframe>;
|
|
35
|
+
declare const NullKeyframe: z.ZodTuple<[z.ZodNumber, z.ZodNull], null>;
|
|
36
|
+
/**
|
|
37
|
+
* Keyframes are indexed by a timestamp given in ms
|
|
38
|
+
*/
|
|
39
|
+
export type InitialImageKeyframe = z.infer<typeof InitialImageKeyframe>;
|
|
40
|
+
declare const InitialImageKeyframe: z.ZodTuple<[z.ZodNumber, z.ZodObject<{
|
|
41
|
+
set: z.ZodOptional<z.ZodObject<{
|
|
42
|
+
opacity: z.ZodOptional<z.ZodNumber>;
|
|
43
|
+
}, z.core.$strip>>;
|
|
44
|
+
}, z.core.$strip>], null>;
|
|
45
|
+
/**
|
|
46
|
+
* Keyframes are indexed by a timestamp given in ms
|
|
47
|
+
*/
|
|
48
|
+
export type ImageKeyframe = z.infer<typeof ImageKeyframe>;
|
|
49
|
+
declare const ImageKeyframe: z.ZodTuple<[z.ZodNumber, z.ZodObject<{
|
|
50
|
+
set: z.ZodOptional<z.ZodObject<{
|
|
51
|
+
opacity: z.ZodOptional<z.ZodNumber>;
|
|
52
|
+
}, z.core.$strip>>;
|
|
53
|
+
lerp: z.ZodOptional<z.ZodObject<{
|
|
54
|
+
opacity: z.ZodOptional<z.ZodNumber>;
|
|
55
|
+
}, z.core.$strip>>;
|
|
56
|
+
}, z.core.$strip>], null>;
|
|
57
|
+
/**
|
|
58
|
+
* Keyframes are indexed by a timestamp given in ms
|
|
59
|
+
*/
|
|
60
|
+
export type InitialAudioKeyframe = z.infer<typeof InitialAudioKeyframe>;
|
|
61
|
+
declare const InitialAudioKeyframe: z.ZodTuple<[z.ZodNumber, z.ZodObject<{
|
|
62
|
+
set: z.ZodOptional<z.ZodObject<{
|
|
63
|
+
volume: z.ZodOptional<z.ZodNumber>;
|
|
64
|
+
t: z.ZodOptional<z.ZodNumber>;
|
|
65
|
+
rate: z.ZodOptional<z.ZodNumber>;
|
|
66
|
+
}, z.core.$strip>>;
|
|
67
|
+
}, z.core.$strip>], null>;
|
|
68
|
+
/**
|
|
69
|
+
* Keyframes are indexed by a timestamp given in ms
|
|
70
|
+
*/
|
|
71
|
+
export type AudioKeyframe = z.infer<typeof AudioKeyframe>;
|
|
72
|
+
declare const AudioKeyframe: z.ZodTuple<[z.ZodNumber, z.ZodObject<{
|
|
73
|
+
set: z.ZodOptional<z.ZodObject<{
|
|
74
|
+
volume: z.ZodOptional<z.ZodNumber>;
|
|
75
|
+
t: z.ZodOptional<z.ZodNumber>;
|
|
76
|
+
rate: z.ZodOptional<z.ZodNumber>;
|
|
77
|
+
}, z.core.$strip>>;
|
|
78
|
+
lerp: z.ZodOptional<z.ZodObject<{
|
|
79
|
+
volume: z.ZodOptional<z.ZodNumber>;
|
|
80
|
+
}, z.core.$strip>>;
|
|
81
|
+
}, z.core.$strip>], null>;
|
|
82
|
+
/**
|
|
83
|
+
* Keyframes are indexed by a timestamp given in ms
|
|
84
|
+
*/
|
|
85
|
+
export type InitialVideoKeyframe = z.infer<typeof InitialVideoKeyframe>;
|
|
86
|
+
declare const InitialVideoKeyframe: z.ZodTuple<[z.ZodNumber, z.ZodObject<{
|
|
87
|
+
set: z.ZodOptional<z.ZodObject<{
|
|
88
|
+
opacity: z.ZodOptional<z.ZodNumber>;
|
|
89
|
+
volume: z.ZodOptional<z.ZodNumber>;
|
|
90
|
+
t: z.ZodOptional<z.ZodNumber>;
|
|
91
|
+
rate: z.ZodOptional<z.ZodNumber>;
|
|
92
|
+
}, z.core.$strip>>;
|
|
93
|
+
}, z.core.$strip>], null>;
|
|
94
|
+
/**
|
|
95
|
+
* Keyframes are indexed by a timestamp given in ms
|
|
96
|
+
*/
|
|
97
|
+
export type VideoKeyframe = z.infer<typeof VideoKeyframe>;
|
|
98
|
+
declare const VideoKeyframe: z.ZodTuple<[z.ZodNumber, z.ZodObject<{
|
|
99
|
+
set: z.ZodOptional<z.ZodObject<{
|
|
100
|
+
opacity: z.ZodOptional<z.ZodNumber>;
|
|
101
|
+
volume: z.ZodOptional<z.ZodNumber>;
|
|
102
|
+
t: z.ZodOptional<z.ZodNumber>;
|
|
103
|
+
rate: z.ZodOptional<z.ZodNumber>;
|
|
104
|
+
}, z.core.$strip>>;
|
|
105
|
+
lerp: z.ZodOptional<z.ZodObject<{
|
|
106
|
+
opacity: z.ZodOptional<z.ZodNumber>;
|
|
107
|
+
volume: z.ZodOptional<z.ZodNumber>;
|
|
108
|
+
}, z.core.$strip>>;
|
|
109
|
+
}, z.core.$strip>], null>;
|
|
110
|
+
export declare const MediaSurfaceStateSchema: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
|
|
111
|
+
keyframes: z.ZodTuple<[z.ZodTuple<[z.ZodNumber, z.ZodObject<{
|
|
112
|
+
set: z.ZodOptional<z.ZodObject<{
|
|
113
|
+
opacity: z.ZodOptional<z.ZodNumber>;
|
|
114
|
+
}, z.core.$strip>>;
|
|
115
|
+
lerp: z.ZodOptional<z.ZodObject<{
|
|
116
|
+
opacity: z.ZodOptional<z.ZodNumber>;
|
|
117
|
+
}, z.core.$strip>>;
|
|
118
|
+
}, z.core.$strip>], null>], z.ZodUnion<readonly [z.ZodTuple<[z.ZodNumber, z.ZodObject<{
|
|
119
|
+
set: z.ZodOptional<z.ZodObject<{
|
|
120
|
+
opacity: z.ZodOptional<z.ZodNumber>;
|
|
121
|
+
}, z.core.$strip>>;
|
|
122
|
+
}, z.core.$strip>], null>, z.ZodTuple<[z.ZodNumber, z.ZodNull], null>]>>;
|
|
123
|
+
type: z.ZodLiteral<"image">;
|
|
124
|
+
file: z.ZodString;
|
|
125
|
+
fit: z.ZodUnion<readonly [z.ZodLiteral<"contain">, z.ZodLiteral<"cover">, z.ZodLiteral<"none">]>;
|
|
126
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
127
|
+
keyframes: z.ZodTuple<[z.ZodTuple<[z.ZodNumber, z.ZodObject<{
|
|
128
|
+
set: z.ZodOptional<z.ZodObject<{
|
|
129
|
+
volume: z.ZodOptional<z.ZodNumber>;
|
|
130
|
+
t: z.ZodOptional<z.ZodNumber>;
|
|
131
|
+
rate: z.ZodOptional<z.ZodNumber>;
|
|
132
|
+
}, z.core.$strip>>;
|
|
133
|
+
lerp: z.ZodOptional<z.ZodObject<{
|
|
134
|
+
volume: z.ZodOptional<z.ZodNumber>;
|
|
135
|
+
}, z.core.$strip>>;
|
|
136
|
+
}, z.core.$strip>], null>], z.ZodUnion<readonly [z.ZodTuple<[z.ZodNumber, z.ZodObject<{
|
|
137
|
+
set: z.ZodOptional<z.ZodObject<{
|
|
138
|
+
volume: z.ZodOptional<z.ZodNumber>;
|
|
139
|
+
t: z.ZodOptional<z.ZodNumber>;
|
|
140
|
+
rate: z.ZodOptional<z.ZodNumber>;
|
|
141
|
+
}, z.core.$strip>>;
|
|
142
|
+
}, z.core.$strip>], null>, z.ZodTuple<[z.ZodNumber, z.ZodNull], null>]>>;
|
|
143
|
+
type: z.ZodLiteral<"audio">;
|
|
144
|
+
file: z.ZodString;
|
|
145
|
+
audioOutput: z.ZodString;
|
|
146
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
147
|
+
keyframes: z.ZodTuple<[z.ZodTuple<[z.ZodNumber, z.ZodObject<{
|
|
148
|
+
set: z.ZodOptional<z.ZodObject<{
|
|
149
|
+
opacity: z.ZodOptional<z.ZodNumber>;
|
|
150
|
+
volume: z.ZodOptional<z.ZodNumber>;
|
|
151
|
+
t: z.ZodOptional<z.ZodNumber>;
|
|
152
|
+
rate: z.ZodOptional<z.ZodNumber>;
|
|
153
|
+
}, z.core.$strip>>;
|
|
154
|
+
lerp: z.ZodOptional<z.ZodObject<{
|
|
155
|
+
opacity: z.ZodOptional<z.ZodNumber>;
|
|
156
|
+
volume: z.ZodOptional<z.ZodNumber>;
|
|
157
|
+
}, z.core.$strip>>;
|
|
158
|
+
}, z.core.$strip>], null>], z.ZodUnion<readonly [z.ZodTuple<[z.ZodNumber, z.ZodObject<{
|
|
159
|
+
set: z.ZodOptional<z.ZodObject<{
|
|
160
|
+
opacity: z.ZodOptional<z.ZodNumber>;
|
|
161
|
+
volume: z.ZodOptional<z.ZodNumber>;
|
|
162
|
+
t: z.ZodOptional<z.ZodNumber>;
|
|
163
|
+
rate: z.ZodOptional<z.ZodNumber>;
|
|
164
|
+
}, z.core.$strip>>;
|
|
165
|
+
}, z.core.$strip>], null>, z.ZodTuple<[z.ZodNumber, z.ZodNull], null>]>>;
|
|
166
|
+
type: z.ZodLiteral<"video">;
|
|
167
|
+
file: z.ZodString;
|
|
168
|
+
audioOutput: z.ZodString;
|
|
169
|
+
fit: z.ZodUnion<readonly [z.ZodLiteral<"contain">, z.ZodLiteral<"cover">, z.ZodLiteral<"none">]>;
|
|
170
|
+
}, z.core.$strip>]>>;
|
|
171
|
+
export type ImageOptions = VisualProperties;
|
|
172
|
+
export type AudioOptions = TemporalProperties & AudialProperties;
|
|
173
|
+
export type VideoOptions = TemporalProperties & VisualProperties & AudialProperties;
|
|
174
|
+
export type ImageState = {
|
|
175
|
+
type: 'image';
|
|
176
|
+
file: string;
|
|
177
|
+
fit: 'cover' | 'contain' | 'none';
|
|
178
|
+
keyframes: [InitialImageKeyframe, ...Array<ImageKeyframe | NullKeyframe>];
|
|
179
|
+
};
|
|
180
|
+
export type AudioState = {
|
|
181
|
+
type: 'audio';
|
|
182
|
+
file: string;
|
|
183
|
+
audioOutput: string;
|
|
184
|
+
keyframes: [InitialAudioKeyframe, ...Array<AudioKeyframe | NullKeyframe>];
|
|
185
|
+
};
|
|
186
|
+
export type VideoState = {
|
|
187
|
+
type: 'video';
|
|
188
|
+
file: string;
|
|
189
|
+
fit: 'cover' | 'contain' | 'none';
|
|
190
|
+
audioOutput: string;
|
|
191
|
+
keyframes: [InitialVideoKeyframe, ...Array<VideoKeyframe | NullKeyframe>];
|
|
192
|
+
};
|
|
193
|
+
export type MediaClipState = ImageState | AudioState | VideoState;
|
|
194
|
+
export type MediaSurfaceState = Record<string, MediaClipState>;
|
|
195
|
+
export type UnionsEqual<A, B> = Exclude<A, B> extends never ? (Exclude<B, A> extends never ? true : false) : false;
|
|
196
|
+
export declare const defaultImageOptions: ImageOptions;
|
|
197
|
+
export declare const defaultAudioOptions: AudioOptions;
|
|
198
|
+
export declare const defaultVideoOptions: VideoOptions;
|
|
199
|
+
export {};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const TemporalProperties = z.object({
|
|
3
|
+
t: z.number().gte(0),
|
|
4
|
+
rate: z.number().gte(0),
|
|
5
|
+
});
|
|
6
|
+
const VisualProperties = z.object({
|
|
7
|
+
opacity: z.number().gte(0).lte(1),
|
|
8
|
+
});
|
|
9
|
+
const AudialProperties = z.object({
|
|
10
|
+
volume: z.number().gte(0).lte(1),
|
|
11
|
+
});
|
|
12
|
+
const ImageMetadata = z.object({
|
|
13
|
+
type: z.literal('image'),
|
|
14
|
+
file: z.string(),
|
|
15
|
+
fit: z.union([z.literal('contain'), z.literal('cover'), z.literal('none')]),
|
|
16
|
+
});
|
|
17
|
+
const AudioMetadata = z.object({
|
|
18
|
+
type: z.literal('audio'),
|
|
19
|
+
file: z.string(),
|
|
20
|
+
audioOutput: z.string(),
|
|
21
|
+
});
|
|
22
|
+
const VideoMetadata = z.object({
|
|
23
|
+
type: z.literal('video'),
|
|
24
|
+
file: z.string(),
|
|
25
|
+
audioOutput: z.string(),
|
|
26
|
+
fit: z.union([z.literal('contain'), z.literal('cover'), z.literal('none')]),
|
|
27
|
+
});
|
|
28
|
+
const NullKeyframe = z.tuple([z.number(), z.null()]);
|
|
29
|
+
const InitialImageKeyframe = z.tuple([
|
|
30
|
+
z.number(),
|
|
31
|
+
z
|
|
32
|
+
.object({
|
|
33
|
+
set: z
|
|
34
|
+
.object({
|
|
35
|
+
...VisualProperties.shape,
|
|
36
|
+
})
|
|
37
|
+
.partial(),
|
|
38
|
+
})
|
|
39
|
+
.partial(),
|
|
40
|
+
]);
|
|
41
|
+
const ImageKeyframe = z.tuple([
|
|
42
|
+
z.number(),
|
|
43
|
+
z
|
|
44
|
+
.object({
|
|
45
|
+
set: z
|
|
46
|
+
.object({
|
|
47
|
+
...VisualProperties.shape,
|
|
48
|
+
})
|
|
49
|
+
.partial(),
|
|
50
|
+
lerp: z
|
|
51
|
+
.object({
|
|
52
|
+
...VisualProperties.shape,
|
|
53
|
+
})
|
|
54
|
+
.partial(),
|
|
55
|
+
})
|
|
56
|
+
.partial(),
|
|
57
|
+
]);
|
|
58
|
+
const InitialAudioKeyframe = z.tuple([
|
|
59
|
+
z.number(),
|
|
60
|
+
z
|
|
61
|
+
.object({
|
|
62
|
+
set: z
|
|
63
|
+
.object({
|
|
64
|
+
...TemporalProperties.shape,
|
|
65
|
+
...AudialProperties.shape,
|
|
66
|
+
})
|
|
67
|
+
.partial(),
|
|
68
|
+
})
|
|
69
|
+
.partial(),
|
|
70
|
+
]);
|
|
71
|
+
const AudioKeyframe = z.tuple([
|
|
72
|
+
z.number(),
|
|
73
|
+
z
|
|
74
|
+
.object({
|
|
75
|
+
set: z
|
|
76
|
+
.object({
|
|
77
|
+
...TemporalProperties.shape,
|
|
78
|
+
...AudialProperties.shape,
|
|
79
|
+
})
|
|
80
|
+
.partial(),
|
|
81
|
+
lerp: z
|
|
82
|
+
.object({
|
|
83
|
+
...AudialProperties.shape,
|
|
84
|
+
})
|
|
85
|
+
.partial(),
|
|
86
|
+
})
|
|
87
|
+
.partial(),
|
|
88
|
+
]);
|
|
89
|
+
const InitialVideoKeyframe = z.tuple([
|
|
90
|
+
z.number(),
|
|
91
|
+
z
|
|
92
|
+
.object({
|
|
93
|
+
set: z
|
|
94
|
+
.object({
|
|
95
|
+
...TemporalProperties.shape,
|
|
96
|
+
...AudialProperties.shape,
|
|
97
|
+
...VisualProperties.shape,
|
|
98
|
+
})
|
|
99
|
+
.partial(),
|
|
100
|
+
})
|
|
101
|
+
.partial(),
|
|
102
|
+
]);
|
|
103
|
+
const VideoKeyframe = z.tuple([
|
|
104
|
+
z.number(),
|
|
105
|
+
z
|
|
106
|
+
.object({
|
|
107
|
+
set: z
|
|
108
|
+
.object({
|
|
109
|
+
...TemporalProperties.shape,
|
|
110
|
+
...AudialProperties.shape,
|
|
111
|
+
...VisualProperties.shape,
|
|
112
|
+
})
|
|
113
|
+
.partial(),
|
|
114
|
+
lerp: z
|
|
115
|
+
.object({
|
|
116
|
+
...AudialProperties.shape,
|
|
117
|
+
...VisualProperties.shape,
|
|
118
|
+
})
|
|
119
|
+
.partial(),
|
|
120
|
+
})
|
|
121
|
+
.partial(),
|
|
122
|
+
]);
|
|
123
|
+
const ImageClip = z.object({
|
|
124
|
+
...ImageMetadata.shape,
|
|
125
|
+
keyframes: z.tuple([ImageKeyframe], z.union([InitialImageKeyframe, NullKeyframe])),
|
|
126
|
+
});
|
|
127
|
+
const AudioClip = z.object({
|
|
128
|
+
...AudioMetadata.shape,
|
|
129
|
+
keyframes: z.tuple([AudioKeyframe], z.union([InitialAudioKeyframe, NullKeyframe])),
|
|
130
|
+
});
|
|
131
|
+
const VideoClip = z.object({
|
|
132
|
+
...VideoMetadata.shape,
|
|
133
|
+
keyframes: z.tuple([VideoKeyframe], z.union([InitialVideoKeyframe, NullKeyframe])),
|
|
134
|
+
});
|
|
135
|
+
export const MediaSurfaceStateSchema = z.record(z.string(), z.union([ImageClip, AudioClip, VideoClip]));
|
|
136
|
+
true;
|
|
137
|
+
true;
|
|
138
|
+
true;
|
|
139
|
+
true;
|
|
140
|
+
export const defaultImageOptions = {
|
|
141
|
+
opacity: 1,
|
|
142
|
+
};
|
|
143
|
+
export const defaultAudioOptions = {
|
|
144
|
+
t: 0,
|
|
145
|
+
rate: 1,
|
|
146
|
+
volume: 1,
|
|
147
|
+
};
|
|
148
|
+
export const defaultVideoOptions = {
|
|
149
|
+
t: 0,
|
|
150
|
+
rate: 1,
|
|
151
|
+
volume: 1,
|
|
152
|
+
opacity: 1,
|
|
153
|
+
};
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Connect to COGS to build a custom Media Master",
|
|
4
4
|
"author": "Clockwork Dog <info@clockwork.dog>",
|
|
5
5
|
"homepage": "https://github.com/clockwork-dog/cogs-sdk/tree/main/packages/javascript",
|
|
6
|
-
"version": "
|
|
6
|
+
"version": "3.0.0-alpha.0",
|
|
7
7
|
"keywords": [],
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"repository": {
|
|
@@ -30,12 +30,14 @@
|
|
|
30
30
|
"build:browser": "vite build",
|
|
31
31
|
"watch-build": "tsc -w",
|
|
32
32
|
"build-docs": "typedoc --out ../../docs/javascript --name @clockworkdog/cogs-client src/index.ts",
|
|
33
|
-
"release": "yarn npm publish --access public"
|
|
33
|
+
"release": "yarn npm publish --access public",
|
|
34
|
+
"prerelease": "yarn npm publish --access public --tag=next"
|
|
34
35
|
},
|
|
35
36
|
"dependencies": {
|
|
36
|
-
"@clockworkdog/timesync": "^
|
|
37
|
+
"@clockworkdog/timesync": "^3.0.0-alpha.0",
|
|
37
38
|
"howler": "clockwork-dog/howler.js#fix-looping-clips",
|
|
38
|
-
"reconnecting-websocket": "^4.4.0"
|
|
39
|
+
"reconnecting-websocket": "^4.4.0",
|
|
40
|
+
"zod": "^4.1.13"
|
|
39
41
|
},
|
|
40
42
|
"devDependencies": {
|
|
41
43
|
"@eslint/js": "^9.17.0",
|
|
@@ -52,5 +54,6 @@
|
|
|
52
54
|
"typescript-eslint": "^8.18.1",
|
|
53
55
|
"vite": "^7.1.12",
|
|
54
56
|
"vitest": "^4.0.6"
|
|
55
|
-
}
|
|
57
|
+
},
|
|
58
|
+
"stableVersion": "0.0.0"
|
|
56
59
|
}
|