pinokiod 7.3.14 → 7.4.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.
Files changed (51) hide show
  1. package/kernel/api/index.js +111 -15
  2. package/kernel/api/script/index.js +10 -0
  3. package/kernel/autolaunch.js +111 -0
  4. package/kernel/environment.js +89 -1
  5. package/kernel/git.js +9 -19
  6. package/kernel/index.js +142 -43
  7. package/kernel/launch_requirements.js +1115 -0
  8. package/kernel/ready.js +231 -0
  9. package/kernel/script.js +16 -0
  10. package/kernel/shells.js +9 -1
  11. package/kernel/workspace_status.js +111 -45
  12. package/package.json +1 -1
  13. package/server/autolaunch.js +725 -0
  14. package/server/index.js +244 -411
  15. package/server/public/logs.js +15 -1
  16. package/server/public/style.css +99 -31
  17. package/server/views/app.ejs +263 -159
  18. package/server/views/autolaunch.ejs +363 -75
  19. package/server/views/index.ejs +550 -26
  20. package/server/views/logs.ejs +14 -12
  21. package/server/views/partials/app_autolaunch_dependency_events.ejs +99 -0
  22. package/server/views/partials/app_autolaunch_dependency_save.ejs +10 -0
  23. package/server/views/partials/app_autolaunch_dependency_styles.ejs +357 -0
  24. package/server/views/partials/app_autolaunch_modal_helpers.ejs +347 -0
  25. package/server/views/partials/autolaunch_dependency_helpers.ejs +204 -0
  26. package/server/views/partials/autolaunch_dependency_save.ejs +13 -0
  27. package/server/views/partials/autolaunch_dependency_styles.ejs +347 -0
  28. package/server/views/partials/home_action_modal.ejs +4 -1
  29. package/server/views/partials/launch_requirements_status_client.ejs +271 -0
  30. package/server/views/partials/launch_requirements_status_styles.ejs +171 -0
  31. package/server/views/partials/launch_settings_dependency_save_factory.ejs +45 -0
  32. package/server/views/partials/launch_settings_dependency_script_loader_factory.ejs +25 -0
  33. package/server/views/terminal.ejs +196 -2
  34. package/test/home-autolaunch-live-ui.test.js +455 -0
  35. package/test/launch-requirements-browser.test.js +579 -0
  36. package/test/launch-requirements-contract-coverage.test.js +627 -0
  37. package/test/launch-requirements-real-browser.js +625 -0
  38. package/test/launch-requirements-status-client.test.js +132 -0
  39. package/test/launch-requirements.test.js +1806 -0
  40. package/test/launch-settings-ui.test.js +370 -0
  41. package/test/ready-state.test.js +49 -0
  42. package/test/server-autolaunch.test.js +1052 -0
  43. package/test/startup-git-index-benchmark.js +409 -0
  44. package/test/startup-git-index-browser.js +320 -0
  45. package/test/startup-git-index-performance.test.js +380 -0
  46. package/test/startup-git-index-refactor.test.js +450 -0
  47. package/test/startup-git-index-route.test.js +588 -0
  48. package/test/universal-launcher.smoke.spec.js +10 -9
  49. package/test/workspace-gitignore-benchmark.js +815 -0
  50. package/test/workspace-gitignore-path-scoped.test.js +256 -0
  51. package/spec/INSTRUCTION_SYNC.md +0 -432
@@ -0,0 +1,132 @@
1
+ const assert = require("node:assert/strict")
2
+ const fs = require("node:fs/promises")
3
+ const path = require("node:path")
4
+ const test = require("node:test")
5
+ const ejs = require("ejs")
6
+ const { JSDOM } = require("jsdom")
7
+
8
+ const root = path.resolve(__dirname, "..")
9
+ const statusClientPath = path.resolve(root, "server/views/partials/launch_requirements_status_client.ejs")
10
+
11
+ async function renderStatusClient() {
12
+ const template = await fs.readFile(statusClientPath, "utf8")
13
+ return ejs.render(template, {
14
+ name: "target",
15
+ config: { title: "Target" },
16
+ launch_requirements_status_enabled: true
17
+ }, {
18
+ filename: statusClientPath
19
+ })
20
+ }
21
+
22
+ function jsonResponse(payload, status = 200) {
23
+ return {
24
+ ok: status >= 200 && status < 300,
25
+ json: async () => payload
26
+ }
27
+ }
28
+
29
+ async function waitFor(predicate, message = "condition") {
30
+ const deadline = Date.now() + 2000
31
+ while (Date.now() < deadline) {
32
+ if (predicate()) return
33
+ await new Promise((resolve) => setTimeout(resolve, 20))
34
+ }
35
+ assert.fail(`Timed out waiting for ${message}`)
36
+ }
37
+
38
+ test("launch requirements stop clears local status instead of rendering a stopped acknowledgement", async () => {
39
+ const script = await renderStatusClient()
40
+ const status = {
41
+ state: "waiting",
42
+ requirements: [
43
+ {
44
+ id: "helper",
45
+ title: "Helper",
46
+ state: "starting",
47
+ script: "start.js",
48
+ icon: "/pinokio-black.png"
49
+ },
50
+ {
51
+ id: "middle",
52
+ title: "Middle",
53
+ state: "waiting",
54
+ waiting_for: ["helper"],
55
+ icon: "/pinokio-black.png"
56
+ }
57
+ ]
58
+ }
59
+
60
+ let socketCallback = null
61
+ let cancelPosts = 0
62
+ let refreshCalls = 0
63
+ const dom = new JSDOM(`<!doctype html>
64
+ <main>
65
+ <div data-launch-requirements-status hidden></div>
66
+ </main>
67
+ <script>${script}</script>
68
+ `, {
69
+ url: "http://127.0.0.1/app",
70
+ runScripts: "dangerously",
71
+ pretendToBeVisual: true,
72
+ beforeParse(window) {
73
+ window.Socket = function Socket() {
74
+ this.run = function run(_options, callback) {
75
+ socketCallback = callback
76
+ return Promise.resolve()
77
+ }
78
+ this.close = function close() {}
79
+ }
80
+ window.refresh = function refresh() {
81
+ refreshCalls += 1
82
+ }
83
+ window.fetch = async function fetch(url, options = {}) {
84
+ if (String(url).includes("/cancel") && options.method === "POST") {
85
+ cancelPosts += 1
86
+ return jsonResponse({ ok: true, cancelled: true })
87
+ }
88
+ return jsonResponse({ ok: true, status })
89
+ }
90
+ }
91
+ })
92
+
93
+ const { document } = dom.window
94
+ const statusRoot = document.querySelector("[data-launch-requirements-status]")
95
+ await waitFor(() => statusRoot.textContent.includes("Preparing required apps"), "initial preparation render")
96
+
97
+ document.querySelector("[data-launch-requirements-stop]").click()
98
+ await waitFor(() => statusRoot.hidden === true, "status card to clear")
99
+
100
+ assert.equal(cancelPosts, 1)
101
+ assert.equal(refreshCalls, 1)
102
+ assert.equal(statusRoot.hidden, true)
103
+ assert.doesNotMatch(statusRoot.textContent, /Launch stopped/)
104
+ assert.doesNotMatch(statusRoot.textContent, /Stopped/)
105
+
106
+ socketCallback({
107
+ type: "launch.requirements",
108
+ data: { status: null }
109
+ })
110
+ await new Promise((resolve) => setTimeout(resolve, 20))
111
+ assert.equal(statusRoot.hidden, true)
112
+ assert.doesNotMatch(statusRoot.textContent, /Launch stopped/)
113
+
114
+ socketCallback({
115
+ type: "launch.requirements",
116
+ data: {
117
+ status: {
118
+ state: "waiting",
119
+ requirements: [{
120
+ id: "other",
121
+ title: "Other",
122
+ state: "starting",
123
+ script: "other.js"
124
+ }]
125
+ }
126
+ }
127
+ })
128
+ await waitFor(() => statusRoot.textContent.includes("Preparing required apps"), "new status replaces acknowledgement")
129
+
130
+ assert.doesNotMatch(statusRoot.textContent, /Launch stopped/)
131
+ assert.match(statusRoot.textContent, /Other/)
132
+ })