@screenly/edge-apps 1.0.0-rc.1 → 1.0.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/README.md +23 -21
- package/bin/edge-apps-scripts.js +0 -0
- package/package.json +5 -1
- package/scripts/cors-proxy-server.js +81 -33
package/README.md
CHANGED
|
@@ -6,8 +6,14 @@ A TypeScript library for interfacing with the Screenly Edge Apps API.
|
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
|
-
> [!
|
|
10
|
-
>
|
|
9
|
+
> [!NOTE]
|
|
10
|
+
> Requires Node.js >= 20.6.0.
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @screenly/edge-apps
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Or with Bun:
|
|
11
17
|
|
|
12
18
|
```bash
|
|
13
19
|
bun add @screenly/edge-apps
|
|
@@ -20,26 +26,22 @@ When developing Edge Apps locally using the library from this repository, you sh
|
|
|
20
26
|
First, in the root of this repository:
|
|
21
27
|
|
|
22
28
|
```bash
|
|
23
|
-
|
|
29
|
+
npm install
|
|
30
|
+
npm run build
|
|
31
|
+
npm link
|
|
24
32
|
```
|
|
25
33
|
|
|
26
34
|
Then in your Edge App directory:
|
|
27
35
|
|
|
28
36
|
```bash
|
|
29
37
|
cd /path/to/your-edge-app
|
|
30
|
-
|
|
38
|
+
npm link @screenly/edge-apps
|
|
31
39
|
```
|
|
32
40
|
|
|
33
41
|
To unlink the package when you're done with local development, run the following in your Edge App directory:
|
|
34
42
|
|
|
35
43
|
```bash
|
|
36
|
-
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
Then in the root of this repository:
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
bun unlink
|
|
44
|
+
npm install
|
|
43
45
|
```
|
|
44
46
|
|
|
45
47
|
## Quick Start
|
|
@@ -97,7 +99,7 @@ signalReady()
|
|
|
97
99
|
|
|
98
100
|
## Web Components
|
|
99
101
|
|
|
100
|
-
This library includes reusable web components for building consistent Edge Apps. See the [components documentation](https://github.com/Screenly/edge-apps-library/blob/
|
|
102
|
+
This library includes reusable web components for building consistent Edge Apps. See the [components documentation](https://github.com/Screenly/edge-apps-library/blob/main/docs/components.md) for usage details.
|
|
101
103
|
|
|
102
104
|
## Edge Apps Scripts CLI
|
|
103
105
|
|
|
@@ -110,13 +112,13 @@ This package provides the `edge-apps-scripts` CLI tool for running shared develo
|
|
|
110
112
|
Start the Vite development server with mock data from `screenly.yml` and `mock-data.yml`:
|
|
111
113
|
|
|
112
114
|
```bash
|
|
113
|
-
|
|
115
|
+
npm run dev
|
|
114
116
|
```
|
|
115
117
|
|
|
116
118
|
#### Building
|
|
117
119
|
|
|
118
120
|
```bash
|
|
119
|
-
|
|
121
|
+
npm run build
|
|
120
122
|
```
|
|
121
123
|
|
|
122
124
|
#### Linting
|
|
@@ -124,13 +126,13 @@ bun run build
|
|
|
124
126
|
To lint your Edge App:
|
|
125
127
|
|
|
126
128
|
```bash
|
|
127
|
-
|
|
129
|
+
npm run lint
|
|
128
130
|
```
|
|
129
131
|
|
|
130
132
|
To lint and automatically fix issues:
|
|
131
133
|
|
|
132
134
|
```bash
|
|
133
|
-
|
|
135
|
+
npm run lint -- --fix
|
|
134
136
|
```
|
|
135
137
|
|
|
136
138
|
#### Type Checking
|
|
@@ -138,7 +140,7 @@ bun run lint -- --fix
|
|
|
138
140
|
Run TypeScript type checking:
|
|
139
141
|
|
|
140
142
|
```bash
|
|
141
|
-
|
|
143
|
+
npm run type-check
|
|
142
144
|
```
|
|
143
145
|
|
|
144
146
|
### Command-Line Utilities for Edge Apps
|
|
@@ -158,7 +160,7 @@ It is recommended to add the following scripts to your Edge App's `package.json`
|
|
|
158
160
|
"build:dev": "edge-apps-scripts build:dev",
|
|
159
161
|
"lint": "edge-apps-scripts lint",
|
|
160
162
|
"type-check": "edge-apps-scripts type-check",
|
|
161
|
-
"deploy": "
|
|
163
|
+
"deploy": "npm run build && screenly edge-app deploy --path=dist/"
|
|
162
164
|
}
|
|
163
165
|
}
|
|
164
166
|
```
|
|
@@ -197,7 +199,7 @@ import type {
|
|
|
197
199
|
## Development
|
|
198
200
|
|
|
199
201
|
```bash
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
202
|
+
npm install # Install dependencies
|
|
203
|
+
npm test # Run tests
|
|
204
|
+
npm run build # Build library
|
|
203
205
|
```
|
package/bin/edge-apps-scripts.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@screenly/edge-apps",
|
|
3
|
-
"version": "1.0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "A TypeScript library for interfacing with Screenly Edge Apps API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -79,6 +79,10 @@
|
|
|
79
79
|
],
|
|
80
80
|
"author": "Screenly",
|
|
81
81
|
"license": "MIT",
|
|
82
|
+
"repository": {
|
|
83
|
+
"type": "git",
|
|
84
|
+
"url": "https://github.com/Screenly/edge-apps-library"
|
|
85
|
+
},
|
|
82
86
|
"publishConfig": {
|
|
83
87
|
"access": "public"
|
|
84
88
|
},
|
|
@@ -2,6 +2,8 @@ import http from 'http'
|
|
|
2
2
|
import https from 'https'
|
|
3
3
|
import { URL } from 'url'
|
|
4
4
|
|
|
5
|
+
const MAX_REDIRECTS = 10
|
|
6
|
+
|
|
5
7
|
function setCorsHeaders(res) {
|
|
6
8
|
res.setHeader('Access-Control-Allow-Origin', '*')
|
|
7
9
|
res.setHeader(
|
|
@@ -12,7 +14,80 @@ function setCorsHeaders(res) {
|
|
|
12
14
|
res.setHeader('Access-Control-Max-Age', '86400')
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
function
|
|
17
|
+
function handleRedirect(
|
|
18
|
+
location,
|
|
19
|
+
req,
|
|
20
|
+
res,
|
|
21
|
+
proxyRes,
|
|
22
|
+
currentUrl,
|
|
23
|
+
redirectCount,
|
|
24
|
+
) {
|
|
25
|
+
if (redirectCount >= MAX_REDIRECTS) {
|
|
26
|
+
proxyRes.resume()
|
|
27
|
+
res.writeHead(508, { 'Content-Type': 'application/json' })
|
|
28
|
+
res.end(JSON.stringify({ error: 'Too many redirects' }))
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const redirectUrl = new URL(location, currentUrl)
|
|
34
|
+
if (redirectUrl.protocol !== 'http:' && redirectUrl.protocol !== 'https:') {
|
|
35
|
+
proxyRes.resume()
|
|
36
|
+
res.writeHead(502, { 'Content-Type': 'application/json' })
|
|
37
|
+
res.end(JSON.stringify({ error: 'Invalid redirect location' }))
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
const redirectModule = redirectUrl.protocol === 'https:' ? https : http
|
|
41
|
+
proxyRes.resume()
|
|
42
|
+
makeProxyRequest(redirectModule, redirectUrl, req, res, redirectCount + 1)
|
|
43
|
+
} catch {
|
|
44
|
+
proxyRes.resume()
|
|
45
|
+
res.writeHead(502, { 'Content-Type': 'application/json' })
|
|
46
|
+
res.end(JSON.stringify({ error: 'Invalid redirect location' }))
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function handleProxyResponse(proxyRes, req, res, currentUrl, redirectCount) {
|
|
51
|
+
if (
|
|
52
|
+
proxyRes.statusCode >= 301 &&
|
|
53
|
+
proxyRes.statusCode <= 308 &&
|
|
54
|
+
proxyRes.headers.location &&
|
|
55
|
+
['GET', 'HEAD'].includes(req.method)
|
|
56
|
+
) {
|
|
57
|
+
handleRedirect(
|
|
58
|
+
proxyRes.headers.location,
|
|
59
|
+
req,
|
|
60
|
+
res,
|
|
61
|
+
proxyRes,
|
|
62
|
+
currentUrl,
|
|
63
|
+
redirectCount,
|
|
64
|
+
)
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const excludeHeaders = [
|
|
69
|
+
'connection',
|
|
70
|
+
'keep-alive',
|
|
71
|
+
'proxy-authenticate',
|
|
72
|
+
'proxy-authorization',
|
|
73
|
+
'te',
|
|
74
|
+
'trailers',
|
|
75
|
+
'transfer-encoding',
|
|
76
|
+
'upgrade',
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
Object.keys(proxyRes.headers).forEach((key) => {
|
|
80
|
+
if (!excludeHeaders.includes(key.toLowerCase())) {
|
|
81
|
+
res.setHeader(key, proxyRes.headers[key])
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
setCorsHeaders(res)
|
|
86
|
+
res.writeHead(proxyRes.statusCode)
|
|
87
|
+
proxyRes.pipe(res)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function makeProxyRequest(httpModule, parsedUrl, req, res, redirectCount = 0) {
|
|
16
91
|
const options = {
|
|
17
92
|
hostname: parsedUrl.hostname,
|
|
18
93
|
port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80),
|
|
@@ -29,38 +104,15 @@ function makeProxyRequest(httpModule, parsedUrl, req, res) {
|
|
|
29
104
|
delete options.headers['x-forwarded-proto']
|
|
30
105
|
delete options.headers['x-forwarded-host']
|
|
31
106
|
|
|
32
|
-
const proxyReq = httpModule.request(options, (proxyRes) =>
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
'keep-alive',
|
|
36
|
-
'proxy-authenticate',
|
|
37
|
-
'proxy-authorization',
|
|
38
|
-
'te',
|
|
39
|
-
'trailers',
|
|
40
|
-
'transfer-encoding',
|
|
41
|
-
'upgrade',
|
|
42
|
-
]
|
|
43
|
-
|
|
44
|
-
Object.keys(proxyRes.headers).forEach((key) => {
|
|
45
|
-
if (!excludeHeaders.includes(key.toLowerCase())) {
|
|
46
|
-
res.setHeader(key, proxyRes.headers[key])
|
|
47
|
-
}
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
res.writeHead(proxyRes.statusCode)
|
|
51
|
-
proxyRes.pipe(res)
|
|
52
|
-
})
|
|
107
|
+
const proxyReq = httpModule.request(options, (proxyRes) =>
|
|
108
|
+
handleProxyResponse(proxyRes, req, res, parsedUrl, redirectCount),
|
|
109
|
+
)
|
|
53
110
|
|
|
54
111
|
proxyReq.on('error', (error) => {
|
|
55
112
|
console.error('Proxy error:', error.message)
|
|
56
113
|
if (!res.headersSent) {
|
|
57
114
|
res.writeHead(500, { 'Content-Type': 'application/json' })
|
|
58
|
-
res.end(
|
|
59
|
-
JSON.stringify({
|
|
60
|
-
error: 'Proxy error',
|
|
61
|
-
message: error.message,
|
|
62
|
-
}),
|
|
63
|
-
)
|
|
115
|
+
res.end(JSON.stringify({ error: 'Proxy error', message: error.message }))
|
|
64
116
|
}
|
|
65
117
|
})
|
|
66
118
|
|
|
@@ -68,11 +120,7 @@ function makeProxyRequest(httpModule, parsedUrl, req, res) {
|
|
|
68
120
|
proxyReq.destroy()
|
|
69
121
|
if (!res.headersSent) {
|
|
70
122
|
res.writeHead(504, { 'Content-Type': 'application/json' })
|
|
71
|
-
res.end(
|
|
72
|
-
JSON.stringify({
|
|
73
|
-
error: 'Gateway timeout',
|
|
74
|
-
}),
|
|
75
|
-
)
|
|
123
|
+
res.end(JSON.stringify({ error: 'Gateway timeout' }))
|
|
76
124
|
}
|
|
77
125
|
})
|
|
78
126
|
|