@learnpack/learnpack 2.1.50 → 2.1.52

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,199 +1,199 @@
1
- import logger from "../utils/console"
2
- import * as fs from "fs"
3
- // import em from "events"
4
- import * as XXH from "xxhashjs"
5
-
6
- // possible events to dispatch
7
- const events = {
8
- START_EXERCISE: "start_exercise",
9
- INIT: "initializing",
10
- RUNNING: "configuration_loaded",
11
- END: "connection_ended",
12
- RESET_EXERCISE: "reset_exercise",
13
- OPEN_FILES: "open_files",
14
- OPEN_WINDOW: "open_window",
15
- INSTRUCTIONS_CLOSED: "instructions_closed",
16
- OPEN_TERMINAL: "open_terminal",
17
- }
18
-
19
- let options = {
20
- path: null,
21
- create: false,
22
- }
23
- let lastHash: any = null
24
- let watcher: any = null // subscribe to file and listen to changes
25
- let actions: any = null // action queue
26
-
27
- const loadDispatcher = (opts: any) => {
28
- actions = [{ name: "initializing", time: now() }]
29
- logger.debug(`Loading from ${opts.path}`)
30
-
31
- let exists = fs.existsSync(opts.path)
32
- if (opts.create) {
33
- if (exists)
34
- actions.push({ name: "reset", time: now() })
35
- fs.writeFileSync(opts.path, JSON.stringify(actions), { flag: "w" })
36
- exists = true
37
- }
38
-
39
- if (!exists)
40
- throw new Error(`Invalid queue path, missing file at: ${opts.path}`)
41
-
42
- let incomingActions = []
43
- try {
44
- const content = fs.readFileSync(opts.path, "utf-8")
45
- incomingActions = JSON.parse(content)
46
- if (!Array.isArray(incomingActions))
47
- incomingActions = []
48
- } catch {
49
- incomingActions = []
50
- logger.debug("Error loading VSCode Actions file")
51
- }
52
-
53
- logger.debug("Actions load ", incomingActions)
54
- return incomingActions
55
- }
56
-
57
- // eslint-disable-next-line
58
- const enqueue = (name: string, data: any | undefined = undefined) => {
59
- if (!Object.values(events).includes(name)) {
60
- logger.debug(`Invalid event ${name}`)
61
- throw new Error(`Invalid action ${name}`)
62
- }
63
-
64
- if (!actions)
65
- actions = []
66
-
67
- actions.push({ name, time: now(), data: data })
68
- logger.debug(`EMIT -> ${name}:Exporting changes to ${options.path}`)
69
-
70
- return fs.writeFileSync(options.path || "", JSON.stringify(actions))
71
- }
72
-
73
- const now = () => {
74
- const hrTime = process.hrtime()
75
- // eslint-disable-next-line
76
- const htTime0 = hrTime[0] * 1000000;
77
- return (htTime0 + hrTime[1]) / 1000
78
- }
79
-
80
- const loadFile = (filePath: string) => {
81
- if (!fs.existsSync(filePath))
82
- throw new Error(`No queue.json file to load on ${filePath}`)
83
-
84
- const content = fs.readFileSync(filePath, "utf8")
85
- const newHash = XXH.h32(content, 0xAB_CD).toString(16)
86
- const isUpdated = lastHash !== newHash
87
- lastHash = newHash
88
- const incomingActions = JSON.parse(content)
89
- return { isUpdated, incomingActions }
90
- }
91
-
92
- const dequeue = () => {
93
- // first time dequeue loads
94
- if (!actions)
95
- actions = []
96
-
97
- const { isUpdated, incomingActions } = loadFile(options.path || "")
98
-
99
- if (!isUpdated) {
100
- /**
101
- * make sure no tasks are executed from the queue by matching both
102
- * queues (the incoming with current one)
103
- */
104
- actions = incomingActions
105
- logger.debug(
106
- `No new actions to process: ${actions.length}/${incomingActions.length}`
107
- )
108
- return null
109
- }
110
-
111
- // do i need to reset actions to zero?
112
- if (actions.length > 0 && actions[0].time !== incomingActions[0].time) {
113
- actions = []
114
- }
115
-
116
- const action = incomingActions[incomingActions.length - 1]
117
- logger.debug("Dequeing action ", action)
118
- actions.push(action)
119
- return action
120
- }
121
-
122
- const pull = (callback: (T: any) => any) => {
123
- logger.debug("Pulling actions")
124
- let incoming = dequeue()
125
- while (incoming) {
126
- callback(incoming)
127
- incoming = dequeue()
128
- }
129
- }
130
-
131
- const reset = (callback: (T?: any) => any) => {
132
- logger.debug("Queue reseted")
133
- actions = []
134
- if (fs.existsSync(options.path || "")) {
135
- fs.writeFileSync(options.path || "", "[]")
136
- callback()
137
- }
138
- }
139
-
140
- const onPull = (callback: (T?: any) => any) => {
141
- // eslint-disable-next-line
142
- const chokidar = require("chokidar");
143
-
144
- logger.debug("Starting to listen...")
145
- try {
146
- loadFile(options.path || "")
147
- } catch {
148
- logger.debug("No previoues queue file, waiting for it to be created...")
149
- }
150
-
151
- if (!watcher) {
152
- logger.debug(`Watching ${options.path}`)
153
- watcher = chokidar.watch(`${options.path}`, {
154
- persistent: true,
155
- })
156
- } else
157
- logger.debug("Already watching queue path")
158
-
159
- watcher.on("add", () => pull(callback)).on("change", () => pull(callback))
160
-
161
- return true
162
- }
163
-
164
- const onReset = (callback: (T?: any) => any) => {
165
- // eslint-disable-next-line
166
- const chokidar = require("chokidar");
167
-
168
- if (!watcher) {
169
- logger.debug(`Watching ${options.path}`)
170
- watcher = chokidar.watch(`${options.path}`, {
171
- persistent: true,
172
- })
173
- }
174
-
175
- watcher.on("unlink", () => reset(callback))
176
-
177
- return true
178
- }
179
-
180
- export default {
181
- events,
182
- dispatcher: (opts: any = {}) => {
183
- if (!actions) {
184
- options = { ...options, ...opts }
185
- logger.debug("Initializing queue dispatcher", options)
186
- actions = loadDispatcher(options)
187
- }
188
-
189
- return { enqueue, events }
190
- },
191
- listener: (opts: any = {}) => {
192
- if (!actions) {
193
- options = { ...options, ...opts }
194
- logger.debug("Initializing queue listener", options)
195
- }
196
-
197
- return { onPull, onReset, events }
198
- },
199
- }
1
+ import logger from "../utils/console"
2
+ import * as fs from "fs"
3
+ // import em from "events"
4
+ import * as XXH from "xxhashjs"
5
+
6
+ // possible events to dispatch
7
+ const events = {
8
+ START_EXERCISE: "start_exercise",
9
+ INIT: "initializing",
10
+ RUNNING: "configuration_loaded",
11
+ END: "connection_ended",
12
+ RESET_EXERCISE: "reset_exercise",
13
+ OPEN_FILES: "open_files",
14
+ OPEN_WINDOW: "open_window",
15
+ INSTRUCTIONS_CLOSED: "instructions_closed",
16
+ OPEN_TERMINAL: "open_terminal",
17
+ }
18
+
19
+ let options = {
20
+ path: null,
21
+ create: false,
22
+ }
23
+ let lastHash: any = null
24
+ let watcher: any = null // subscribe to file and listen to changes
25
+ let actions: any = null // action queue
26
+
27
+ const loadDispatcher = (opts: any) => {
28
+ actions = [{ name: "initializing", time: now() }]
29
+ logger.debug(`Loading from ${opts.path}`)
30
+
31
+ let exists = fs.existsSync(opts.path)
32
+ if (opts.create) {
33
+ if (exists)
34
+ actions.push({ name: "reset", time: now() })
35
+ fs.writeFileSync(opts.path, JSON.stringify(actions), { flag: "w" })
36
+ exists = true
37
+ }
38
+
39
+ if (!exists)
40
+ throw new Error(`Invalid queue path, missing file at: ${opts.path}`)
41
+
42
+ let incomingActions = []
43
+ try {
44
+ const content = fs.readFileSync(opts.path, "utf-8")
45
+ incomingActions = JSON.parse(content)
46
+ if (!Array.isArray(incomingActions))
47
+ incomingActions = []
48
+ } catch {
49
+ incomingActions = []
50
+ logger.debug("Error loading VSCode Actions file")
51
+ }
52
+
53
+ logger.debug("Actions load ", incomingActions)
54
+ return incomingActions
55
+ }
56
+
57
+ // eslint-disable-next-line
58
+ const enqueue = (name: string, data: any | undefined = undefined) => {
59
+ if (!Object.values(events).includes(name)) {
60
+ logger.debug(`Invalid event ${name}`)
61
+ throw new Error(`Invalid action ${name}`)
62
+ }
63
+
64
+ if (!actions)
65
+ actions = []
66
+
67
+ actions.push({ name, time: now(), data: data })
68
+ logger.debug(`EMIT -> ${name}:Exporting changes to ${options.path}`)
69
+
70
+ return fs.writeFileSync(options.path || "", JSON.stringify(actions))
71
+ }
72
+
73
+ const now = () => {
74
+ const hrTime = process.hrtime()
75
+ // eslint-disable-next-line
76
+ const htTime0 = hrTime[0] * 1000000;
77
+ return (htTime0 + hrTime[1]) / 1000
78
+ }
79
+
80
+ const loadFile = (filePath: string) => {
81
+ if (!fs.existsSync(filePath))
82
+ throw new Error(`No queue.json file to load on ${filePath}`)
83
+
84
+ const content = fs.readFileSync(filePath, "utf8")
85
+ const newHash = XXH.h32(content, 0xAB_CD).toString(16)
86
+ const isUpdated = lastHash !== newHash
87
+ lastHash = newHash
88
+ const incomingActions = JSON.parse(content)
89
+ return { isUpdated, incomingActions }
90
+ }
91
+
92
+ const dequeue = () => {
93
+ // first time dequeue loads
94
+ if (!actions)
95
+ actions = []
96
+
97
+ const { isUpdated, incomingActions } = loadFile(options.path || "")
98
+
99
+ if (!isUpdated) {
100
+ /**
101
+ * make sure no tasks are executed from the queue by matching both
102
+ * queues (the incoming with current one)
103
+ */
104
+ actions = incomingActions
105
+ logger.debug(
106
+ `No new actions to process: ${actions.length}/${incomingActions.length}`
107
+ )
108
+ return null
109
+ }
110
+
111
+ // do i need to reset actions to zero?
112
+ if (actions.length > 0 && actions[0].time !== incomingActions[0].time) {
113
+ actions = []
114
+ }
115
+
116
+ const action = incomingActions[incomingActions.length - 1]
117
+ logger.debug("Dequeing action ", action)
118
+ actions.push(action)
119
+ return action
120
+ }
121
+
122
+ const pull = (callback: (T: any) => any) => {
123
+ logger.debug("Pulling actions")
124
+ let incoming = dequeue()
125
+ while (incoming) {
126
+ callback(incoming)
127
+ incoming = dequeue()
128
+ }
129
+ }
130
+
131
+ const reset = (callback: (T?: any) => any) => {
132
+ logger.debug("Queue reseted")
133
+ actions = []
134
+ if (fs.existsSync(options.path || "")) {
135
+ fs.writeFileSync(options.path || "", "[]")
136
+ callback()
137
+ }
138
+ }
139
+
140
+ const onPull = (callback: (T?: any) => any) => {
141
+ // eslint-disable-next-line
142
+ const chokidar = require("chokidar");
143
+
144
+ logger.debug("Starting to listen...")
145
+ try {
146
+ loadFile(options.path || "")
147
+ } catch {
148
+ logger.debug("No previoues queue file, waiting for it to be created...")
149
+ }
150
+
151
+ if (!watcher) {
152
+ logger.debug(`Watching ${options.path}`)
153
+ watcher = chokidar.watch(`${options.path}`, {
154
+ persistent: true,
155
+ })
156
+ } else
157
+ logger.debug("Already watching queue path")
158
+
159
+ watcher.on("add", () => pull(callback)).on("change", () => pull(callback))
160
+
161
+ return true
162
+ }
163
+
164
+ const onReset = (callback: (T?: any) => any) => {
165
+ // eslint-disable-next-line
166
+ const chokidar = require("chokidar");
167
+
168
+ if (!watcher) {
169
+ logger.debug(`Watching ${options.path}`)
170
+ watcher = chokidar.watch(`${options.path}`, {
171
+ persistent: true,
172
+ })
173
+ }
174
+
175
+ watcher.on("unlink", () => reset(callback))
176
+
177
+ return true
178
+ }
179
+
180
+ export default {
181
+ events,
182
+ dispatcher: (opts: any = {}) => {
183
+ if (!actions) {
184
+ options = { ...options, ...opts }
185
+ logger.debug("Initializing queue dispatcher", options)
186
+ actions = loadDispatcher(options)
187
+ }
188
+
189
+ return { enqueue, events }
190
+ },
191
+ listener: (opts: any = {}) => {
192
+ if (!actions) {
193
+ options = { ...options, ...opts }
194
+ logger.debug("Initializing queue listener", options)
195
+ }
196
+
197
+ return { onPull, onReset, events }
198
+ },
199
+ }
@@ -0,0 +1,121 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "language": {
6
+ "type": "string",
7
+ "enum": ["react", "auto", "html", "python", "node", "dom"]
8
+ },
9
+ "slug": {
10
+ "type": "string"
11
+ },
12
+ "title": {
13
+ "type": "object",
14
+ "properties": {
15
+ "us": {
16
+ "type": "string"
17
+ },
18
+ "es": {
19
+ "type": "string"
20
+ }
21
+ },
22
+ "required": ["us"]
23
+ },
24
+ "repository": {
25
+ "type": "string",
26
+ "format": "uri"
27
+ },
28
+ "preview": {
29
+ "type": "string",
30
+ "format": "uri"
31
+ },
32
+ "description": {
33
+ "type": "object",
34
+ "properties": {
35
+ "us": {
36
+ "type": "string"
37
+ },
38
+ "es": {
39
+ "type": "string"
40
+ }
41
+ },
42
+ "required": ["us"]
43
+ },
44
+ "duration": {
45
+ "type": "integer"
46
+ },
47
+ "projectType": {
48
+ "type": "string",
49
+ "enum": ["tutorial", "project"]
50
+ },
51
+ "difficulty": {
52
+ "type": "string",
53
+ "enum": ["easy","beginner", "medium", "hard"]
54
+ },
55
+ "videoSolutions": {
56
+ "type": "boolean"
57
+ },
58
+ "bugsLink": {
59
+ "type": "string",
60
+ "format": "uri"
61
+ },
62
+ "grading": {
63
+ "type": ["string", "null"],
64
+ "enum": ["isolated", "incremental", null]
65
+ },
66
+ "editor": {
67
+ "type": "object",
68
+ "properties": {
69
+ "version": {
70
+ "type": "string"
71
+ },
72
+ "mode": {
73
+ "type": "string",
74
+ "enum": ["extension", "preview"]
75
+ },
76
+ "agent": {
77
+ "type": "string",
78
+ "enum": ["vscode", "os"]
79
+ }
80
+ },
81
+ "required": ["version"]
82
+ },
83
+ "telemetry": {
84
+ "type": "object",
85
+ "properties": {
86
+ "batch": {
87
+ "type": "string",
88
+ "format": "uri"
89
+ }
90
+ },
91
+ "required": ["batch"]
92
+ },
93
+ "video": {
94
+ "type": "object",
95
+ "properties": {
96
+ "intro": {
97
+ "type": "object",
98
+ "properties": {
99
+ "us": {
100
+ "type": "string",
101
+ "format": "uri"
102
+ },
103
+ "es": {
104
+ "type": "string",
105
+ "format": "uri"
106
+ }
107
+ }
108
+ }
109
+ },
110
+ "required": ["intro"]
111
+ }
112
+ },
113
+ "required": [
114
+ "slug",
115
+ "title",
116
+ "description",
117
+ "duration",
118
+ "difficulty",
119
+ "grading"
120
+ ]
121
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "files.autoSave": "afterDelay",
3
+ "files.autoSaveDelay": 700,
4
+ "editor.minimap.enabled": false,
5
+ "workbench.editorAssociations": {
6
+ "*.md": "vscode.markdown.preview.editor"
7
+ },
8
+ "json.schemas": [
9
+ {
10
+ "fileMatch": ["learn.json"],
11
+ "url": "./.vscode/schema.json"
12
+ }
13
+ ]
14
+ }
@@ -0,0 +1,122 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "language": {
6
+ "type": "string",
7
+ "enum": ["react", "auto", "html", "python", "node", "dom"]
8
+ },
9
+ "slug": {
10
+ "type": "string"
11
+ },
12
+ "title": {
13
+ "type": "object",
14
+ "properties": {
15
+ "us": {
16
+ "type": "string"
17
+ },
18
+ "es": {
19
+ "type": "string"
20
+ }
21
+ },
22
+ "required": ["us"]
23
+ },
24
+ "repository": {
25
+ "type": "string",
26
+ "format": "uri"
27
+ },
28
+ "preview": {
29
+ "type": "string",
30
+ "format": "uri"
31
+ },
32
+ "description": {
33
+ "type": "object",
34
+ "properties": {
35
+ "us": {
36
+ "type": "string"
37
+ },
38
+ "es": {
39
+ "type": "string"
40
+ }
41
+ },
42
+ "required": ["us"]
43
+ },
44
+ "duration": {
45
+ "type": "integer"
46
+ },
47
+ "projectType": {
48
+ "type": "string",
49
+ "enum": ["tutorial", "project"]
50
+ },
51
+ "difficulty": {
52
+ "type": "string",
53
+ "enum": ["easy","beginner", "medium", "hard"]
54
+ },
55
+ "videoSolutions": {
56
+ "type": "boolean"
57
+ },
58
+ "bugsLink": {
59
+ "type": "string",
60
+ "format": "uri"
61
+ },
62
+ "grading": {
63
+ "type": ["string", "null"],
64
+ "enum": ["isolated", "incremental", null]
65
+ },
66
+ "editor": {
67
+ "type": "object",
68
+ "properties": {
69
+ "version": {
70
+ "type": "string"
71
+ },
72
+ "mode": {
73
+ "type": "string",
74
+ "enum": ["extension", "preview"]
75
+ },
76
+ "agent": {
77
+ "type": "string",
78
+ "enum": ["vscode", "os"]
79
+ }
80
+ },
81
+ "required": ["version"]
82
+ },
83
+ "telemetry": {
84
+ "type": "object",
85
+ "properties": {
86
+ "batch": {
87
+ "type": "string",
88
+ "format": "uri"
89
+ }
90
+ },
91
+ "required": ["batch"]
92
+ },
93
+ "video": {
94
+ "type": "object",
95
+ "properties": {
96
+ "intro": {
97
+ "type": "object",
98
+ "properties": {
99
+ "us": {
100
+ "type": "string",
101
+ "format": "uri"
102
+ },
103
+ "es": {
104
+ "type": "string",
105
+ "format": "uri"
106
+ }
107
+ }
108
+ }
109
+ },
110
+ "required": ["intro"]
111
+ }
112
+ },
113
+ "required": [
114
+ "slug",
115
+ "title",
116
+ "description",
117
+ "duration",
118
+ "difficulty",
119
+ "grading"
120
+ ]
121
+ }
122
+
@@ -0,0 +1,14 @@
1
+ {
2
+ "files.autoSave": "afterDelay",
3
+ "files.autoSaveDelay": 700,
4
+ "editor.minimap.enabled": false,
5
+ "workbench.editorAssociations": {
6
+ "*.md": "vscode.markdown.preview.editor"
7
+ },
8
+ "json.schemas": [
9
+ {
10
+ "fileMatch": ["learn.json"],
11
+ "url": "./.vscode/schema.json"
12
+ }
13
+ ]
14
+ }