@use-voltra/server 1.3.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 +30 -0
- package/build/cjs/index.js +139 -0
- package/build/cjs/index.js.map +1 -0
- package/build/cjs/package.json +3 -0
- package/build/esm/index.js +134 -0
- package/build/esm/index.js.map +1 -0
- package/build/esm/package.json +3 -0
- package/build/types/index.d.ts +78 -0
- package/build/types/index.d.ts.map +1 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
### Voltra Server Foundation
|
|
4
|
+
|
|
5
|
+
[![mit licence][license-badge]][license] [![npm downloads][npm-downloads-badge]][npm-downloads] [![PRs Welcome][prs-welcome-badge]][prs-welcome]
|
|
6
|
+
|
|
7
|
+
`@use-voltra/server` contains the shared server rendering foundation for Voltra, including request adapters and platform-agnostic widget update handlers.
|
|
8
|
+
|
|
9
|
+
> [!WARNING]
|
|
10
|
+
> This package is not intended to be installed directly in your app. Most apps should install `voltra` instead.
|
|
11
|
+
|
|
12
|
+
For installation and setup instructions, see the Voltra documentation: [use-voltra.dev](https://use-voltra.dev).
|
|
13
|
+
|
|
14
|
+
## Authors
|
|
15
|
+
|
|
16
|
+
`voltra` is an open source collaboration between [Saúl Sharma](https://github.com/saulsharma) and [Szymon Chmal](https://github.com/szymonchmal) at [Callstack][callstack-readme-with-love].
|
|
17
|
+
|
|
18
|
+
If you think it's cool, please star it 🌟. This project will always remain free to use.
|
|
19
|
+
|
|
20
|
+
[Callstack][callstack-readme-with-love] is a group of React and React Native geeks, contact us at [hello@callstack.com](mailto:hello@callstack.com) if you need any help with these or just want to say hi!
|
|
21
|
+
|
|
22
|
+
Like the project? ⚛️ [Join the Callstack team](https://callstack.com/careers/?utm_campaign=Senior_RN&utm_source=github&utm_medium=readme) who does amazing stuff for clients and drives React Native Open Source! 🔥
|
|
23
|
+
|
|
24
|
+
[callstack-readme-with-love]: https://callstack.com/?utm_source=github.com&utm_medium=referral&utm_campaign=voltra&utm_term=readme-with-love
|
|
25
|
+
[license-badge]: https://img.shields.io/npm/l/voltra?style=for-the-badge
|
|
26
|
+
[license]: https://github.com/callstackincubator/voltra/blob/main/LICENSE.txt
|
|
27
|
+
[npm-downloads-badge]: https://img.shields.io/npm/dm/voltra?style=for-the-badge
|
|
28
|
+
[npm-downloads]: https://www.npmjs.com/package/voltra
|
|
29
|
+
[prs-welcome-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=for-the-badge
|
|
30
|
+
[prs-welcome]: ./CONTRIBUTING.md
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.createWidgetUpdateHandler = createWidgetUpdateHandler;
|
|
5
|
+
exports.createWidgetUpdateNodeHandler = createWidgetUpdateNodeHandler;
|
|
6
|
+
exports.createWidgetUpdateExpressHandler = createWidgetUpdateExpressHandler;
|
|
7
|
+
function isWidgetPlatform(value) {
|
|
8
|
+
return value === 'ios' || value === 'android';
|
|
9
|
+
}
|
|
10
|
+
function isWidgetTheme(value) {
|
|
11
|
+
return value === 'light' || value === 'dark';
|
|
12
|
+
}
|
|
13
|
+
function jsonResponse(status, body) {
|
|
14
|
+
return new Response(JSON.stringify(body), {
|
|
15
|
+
status,
|
|
16
|
+
headers: {
|
|
17
|
+
'Content-Type': 'application/json',
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function normalizeHeaders(headers) {
|
|
22
|
+
const result = {};
|
|
23
|
+
headers.forEach((value, key) => {
|
|
24
|
+
result[key] = value;
|
|
25
|
+
});
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
function getNodeRequestUrl(req) {
|
|
29
|
+
const protocol = req.socket?.encrypted ? 'https' : 'http';
|
|
30
|
+
const host = req.headers.host || 'localhost';
|
|
31
|
+
return new URL(req.url || '/', `${protocol}://${host}`).toString();
|
|
32
|
+
}
|
|
33
|
+
function createFetchRequestFromNode(req) {
|
|
34
|
+
const headers = new Headers();
|
|
35
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
36
|
+
if (Array.isArray(value)) {
|
|
37
|
+
for (const item of value) {
|
|
38
|
+
headers.append(key, item);
|
|
39
|
+
}
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (typeof value === 'string') {
|
|
43
|
+
headers.set(key, value);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return new Request(getNodeRequestUrl(req), {
|
|
47
|
+
method: req.method || 'GET',
|
|
48
|
+
headers,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
async function writeFetchResponseToNode(response, res) {
|
|
52
|
+
const headers = {};
|
|
53
|
+
response.headers.forEach((value, key) => {
|
|
54
|
+
headers[key] = value;
|
|
55
|
+
});
|
|
56
|
+
res.writeHead(response.status, headers);
|
|
57
|
+
res.end(await response.text());
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Creates a Fetch API request handler for serving widget updates.
|
|
61
|
+
*/
|
|
62
|
+
function createWidgetUpdateHandler(options) {
|
|
63
|
+
const { renderIos, renderAndroid, validateToken } = options;
|
|
64
|
+
return async (request) => {
|
|
65
|
+
try {
|
|
66
|
+
const url = new URL(request.url);
|
|
67
|
+
const widgetId = url.searchParams.get('widgetId');
|
|
68
|
+
const family = url.searchParams.get('family') || undefined;
|
|
69
|
+
const platformParam = url.searchParams.get('platform');
|
|
70
|
+
if (!widgetId) {
|
|
71
|
+
return jsonResponse(400, { error: 'Missing required query parameter: widgetId' });
|
|
72
|
+
}
|
|
73
|
+
if (!isWidgetPlatform(platformParam)) {
|
|
74
|
+
return jsonResponse(400, { error: 'Missing or invalid required query parameter: platform' });
|
|
75
|
+
}
|
|
76
|
+
const platform = platformParam;
|
|
77
|
+
const themeParam = url.searchParams.get('theme');
|
|
78
|
+
const theme = isWidgetTheme(themeParam) ? themeParam : 'light';
|
|
79
|
+
const authHeader = request.headers.get('authorization');
|
|
80
|
+
const token = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : undefined;
|
|
81
|
+
if (validateToken) {
|
|
82
|
+
if (!token) {
|
|
83
|
+
return jsonResponse(401, { error: 'Authorization required' });
|
|
84
|
+
}
|
|
85
|
+
const isValid = await validateToken(token);
|
|
86
|
+
if (!isValid) {
|
|
87
|
+
return jsonResponse(401, { error: 'Invalid token' });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const renderRequest = {
|
|
91
|
+
widgetId,
|
|
92
|
+
platform,
|
|
93
|
+
theme,
|
|
94
|
+
family,
|
|
95
|
+
token,
|
|
96
|
+
headers: normalizeHeaders(request.headers),
|
|
97
|
+
};
|
|
98
|
+
const renderer = platform === 'android' ? renderAndroid : renderIos;
|
|
99
|
+
if (!renderer) {
|
|
100
|
+
const platformName = platform === 'android' ? 'Android' : 'iOS';
|
|
101
|
+
return jsonResponse(404, { error: `No ${platformName} render handler configured for widget: ${widgetId}` });
|
|
102
|
+
}
|
|
103
|
+
const jsonPayload = await renderer(renderRequest);
|
|
104
|
+
if (!jsonPayload) {
|
|
105
|
+
const errorMessage = platform === 'android' ? `No content for Android widget: ${widgetId}` : `No content for widget: ${widgetId}`;
|
|
106
|
+
return jsonResponse(404, { error: errorMessage });
|
|
107
|
+
}
|
|
108
|
+
return new Response(jsonPayload, {
|
|
109
|
+
status: 200,
|
|
110
|
+
headers: {
|
|
111
|
+
'Content-Type': 'application/json',
|
|
112
|
+
'Cache-Control': 'no-cache',
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
console.error('[Voltra] Widget update handler error:', error);
|
|
118
|
+
return jsonResponse(500, { error: 'Internal server error' });
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Creates a Node.js HTTP request handler for serving widget updates.
|
|
124
|
+
*/
|
|
125
|
+
function createWidgetUpdateNodeHandler(options) {
|
|
126
|
+
const handler = createWidgetUpdateHandler(options);
|
|
127
|
+
return async (req, res) => {
|
|
128
|
+
const request = createFetchRequestFromNode(req);
|
|
129
|
+
const response = await handler(request);
|
|
130
|
+
await writeFetchResponseToNode(response, res);
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Creates an Express-compatible request handler for serving widget updates.
|
|
135
|
+
*/
|
|
136
|
+
function createWidgetUpdateExpressHandler(options) {
|
|
137
|
+
return createWidgetUpdateNodeHandler(options);
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
function isWidgetPlatform(value) {
|
|
3
|
+
return value === 'ios' || value === 'android';
|
|
4
|
+
}
|
|
5
|
+
function isWidgetTheme(value) {
|
|
6
|
+
return value === 'light' || value === 'dark';
|
|
7
|
+
}
|
|
8
|
+
function jsonResponse(status, body) {
|
|
9
|
+
return new Response(JSON.stringify(body), {
|
|
10
|
+
status,
|
|
11
|
+
headers: {
|
|
12
|
+
'Content-Type': 'application/json',
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function normalizeHeaders(headers) {
|
|
17
|
+
const result = {};
|
|
18
|
+
headers.forEach((value, key) => {
|
|
19
|
+
result[key] = value;
|
|
20
|
+
});
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
function getNodeRequestUrl(req) {
|
|
24
|
+
const protocol = req.socket?.encrypted ? 'https' : 'http';
|
|
25
|
+
const host = req.headers.host || 'localhost';
|
|
26
|
+
return new URL(req.url || '/', `${protocol}://${host}`).toString();
|
|
27
|
+
}
|
|
28
|
+
function createFetchRequestFromNode(req) {
|
|
29
|
+
const headers = new Headers();
|
|
30
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
31
|
+
if (Array.isArray(value)) {
|
|
32
|
+
for (const item of value) {
|
|
33
|
+
headers.append(key, item);
|
|
34
|
+
}
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (typeof value === 'string') {
|
|
38
|
+
headers.set(key, value);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return new Request(getNodeRequestUrl(req), {
|
|
42
|
+
method: req.method || 'GET',
|
|
43
|
+
headers,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
async function writeFetchResponseToNode(response, res) {
|
|
47
|
+
const headers = {};
|
|
48
|
+
response.headers.forEach((value, key) => {
|
|
49
|
+
headers[key] = value;
|
|
50
|
+
});
|
|
51
|
+
res.writeHead(response.status, headers);
|
|
52
|
+
res.end(await response.text());
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Creates a Fetch API request handler for serving widget updates.
|
|
56
|
+
*/
|
|
57
|
+
export function createWidgetUpdateHandler(options) {
|
|
58
|
+
const { renderIos, renderAndroid, validateToken } = options;
|
|
59
|
+
return async (request) => {
|
|
60
|
+
try {
|
|
61
|
+
const url = new URL(request.url);
|
|
62
|
+
const widgetId = url.searchParams.get('widgetId');
|
|
63
|
+
const family = url.searchParams.get('family') || undefined;
|
|
64
|
+
const platformParam = url.searchParams.get('platform');
|
|
65
|
+
if (!widgetId) {
|
|
66
|
+
return jsonResponse(400, { error: 'Missing required query parameter: widgetId' });
|
|
67
|
+
}
|
|
68
|
+
if (!isWidgetPlatform(platformParam)) {
|
|
69
|
+
return jsonResponse(400, { error: 'Missing or invalid required query parameter: platform' });
|
|
70
|
+
}
|
|
71
|
+
const platform = platformParam;
|
|
72
|
+
const themeParam = url.searchParams.get('theme');
|
|
73
|
+
const theme = isWidgetTheme(themeParam) ? themeParam : 'light';
|
|
74
|
+
const authHeader = request.headers.get('authorization');
|
|
75
|
+
const token = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : undefined;
|
|
76
|
+
if (validateToken) {
|
|
77
|
+
if (!token) {
|
|
78
|
+
return jsonResponse(401, { error: 'Authorization required' });
|
|
79
|
+
}
|
|
80
|
+
const isValid = await validateToken(token);
|
|
81
|
+
if (!isValid) {
|
|
82
|
+
return jsonResponse(401, { error: 'Invalid token' });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const renderRequest = {
|
|
86
|
+
widgetId,
|
|
87
|
+
platform,
|
|
88
|
+
theme,
|
|
89
|
+
family,
|
|
90
|
+
token,
|
|
91
|
+
headers: normalizeHeaders(request.headers),
|
|
92
|
+
};
|
|
93
|
+
const renderer = platform === 'android' ? renderAndroid : renderIos;
|
|
94
|
+
if (!renderer) {
|
|
95
|
+
const platformName = platform === 'android' ? 'Android' : 'iOS';
|
|
96
|
+
return jsonResponse(404, { error: `No ${platformName} render handler configured for widget: ${widgetId}` });
|
|
97
|
+
}
|
|
98
|
+
const jsonPayload = await renderer(renderRequest);
|
|
99
|
+
if (!jsonPayload) {
|
|
100
|
+
const errorMessage = platform === 'android' ? `No content for Android widget: ${widgetId}` : `No content for widget: ${widgetId}`;
|
|
101
|
+
return jsonResponse(404, { error: errorMessage });
|
|
102
|
+
}
|
|
103
|
+
return new Response(jsonPayload, {
|
|
104
|
+
status: 200,
|
|
105
|
+
headers: {
|
|
106
|
+
'Content-Type': 'application/json',
|
|
107
|
+
'Cache-Control': 'no-cache',
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
console.error('[Voltra] Widget update handler error:', error);
|
|
113
|
+
return jsonResponse(500, { error: 'Internal server error' });
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Creates a Node.js HTTP request handler for serving widget updates.
|
|
119
|
+
*/
|
|
120
|
+
export function createWidgetUpdateNodeHandler(options) {
|
|
121
|
+
const handler = createWidgetUpdateHandler(options);
|
|
122
|
+
return async (req, res) => {
|
|
123
|
+
const request = createFetchRequestFromNode(req);
|
|
124
|
+
const response = await handler(request);
|
|
125
|
+
await writeFetchResponseToNode(response, res);
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Creates an Express-compatible request handler for serving widget updates.
|
|
130
|
+
*/
|
|
131
|
+
export function createWidgetUpdateExpressHandler(options) {
|
|
132
|
+
return createWidgetUpdateNodeHandler(options);
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,8BAA8B;AA4E9B,SAAS,gBAAgB,CAAC,KAAoB;IAC5C,OAAO,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,SAAS,CAAA;AAC/C,CAAC;AAED,SAAS,aAAa,CAAC,KAAoB;IACzC,OAAO,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,MAAM,CAAA;AAC9C,CAAC;AAED,SAAS,YAAY,CAAC,MAAc,EAAE,IAA4B;IAChE,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE;QACxC,MAAM;QACN,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;SACnC;KACF,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAgB;IACxC,MAAM,MAAM,GAA2B,EAAE,CAAA;IAEzC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QAC7B,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;IACrB,CAAC,CAAC,CAAA;IAEF,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,iBAAiB,CAAC,GAAoB;IAC7C,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAA;IACzD,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,WAAW,CAAA;IAC5C,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,GAAG,QAAQ,MAAM,IAAI,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAA;AACpE,CAAC;AAED,SAAS,0BAA0B,CAAC,GAAoB;IACtD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;IAE7B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QACvD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YAC3B,CAAC;YACD,SAAQ;QACV,CAAC;QAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QACzB,CAAC;IACH,CAAC;IAED,OAAO,IAAI,OAAO,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE;QACzC,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,KAAK;QAC3B,OAAO;KACR,CAAC,CAAA;AACJ,CAAC;AAED,KAAK,UAAU,wBAAwB,CAAC,QAAkB,EAAE,GAAqB;IAC/E,MAAM,OAAO,GAA2B,EAAE,CAAA;IAE1C,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACtC,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;IACtB,CAAC,CAAC,CAAA;IAEF,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACvC,GAAG,CAAC,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;AAChC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAAmC;IAC3E,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,GAAG,OAAO,CAAA;IAE3D,OAAO,KAAK,EAAE,OAAgB,EAAqB,EAAE;QACnD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YAChC,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;YACjD,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAA;YAC1D,MAAM,aAAa,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;YAEtD,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,OAAO,YAAY,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,4CAA4C,EAAE,CAAC,CAAA;YACnF,CAAC;YAED,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,EAAE,CAAC;gBACrC,OAAO,YAAY,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,uDAAuD,EAAE,CAAC,CAAA;YAC9F,CAAC;YAED,MAAM,QAAQ,GAAmB,aAAa,CAAA;YAC9C,MAAM,UAAU,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;YAChD,MAAM,KAAK,GAAgB,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAA;YAE3E,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;YACvD,MAAM,KAAK,GAAG,UAAU,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;YAEjF,IAAI,aAAa,EAAE,CAAC;gBAClB,IAAI,CAAC,KAAK,EAAE,CAAC;oBACX,OAAO,YAAY,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,wBAAwB,EAAE,CAAC,CAAA;gBAC/D,CAAC;gBAED,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,CAAA;gBAC1C,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO,YAAY,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,CAAA;gBACtD,CAAC;YACH,CAAC;YAED,MAAM,aAAa,GAAwB;gBACzC,QAAQ;gBACR,QAAQ;gBACR,KAAK;gBACL,MAAM;gBACN,KAAK;gBACL,OAAO,EAAE,gBAAgB,CAAC,OAAO,CAAC,OAAO,CAAC;aAC3C,CAAA;YAED,MAAM,QAAQ,GAAG,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAA;YACnE,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,MAAM,YAAY,GAAG,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAA;gBAC/D,OAAO,YAAY,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,YAAY,0CAA0C,QAAQ,EAAE,EAAE,CAAC,CAAA;YAC7G,CAAC;YAED,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,aAAa,CAAC,CAAA;YACjD,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,MAAM,YAAY,GAChB,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,kCAAkC,QAAQ,EAAE,CAAC,CAAC,CAAC,0BAA0B,QAAQ,EAAE,CAAA;gBAE9G,OAAO,YAAY,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAA;YACnD,CAAC;YAED,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE;gBAC/B,MAAM,EAAE,GAAG;gBACX,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,eAAe,EAAE,UAAU;iBAC5B;aACF,CAAC,CAAA;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,KAAK,CAAC,CAAA;YAC7D,OAAO,YAAY,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAA;QAC9D,CAAC;IACH,CAAC,CAAA;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,6BAA6B,CAAC,OAAmC;IAC/E,MAAM,OAAO,GAAG,yBAAyB,CAAC,OAAO,CAAC,CAAA;IAElD,OAAO,KAAK,EAAE,GAAoB,EAAE,GAAqB,EAAiB,EAAE;QAC1E,MAAM,OAAO,GAAG,0BAA0B,CAAC,GAAG,CAAC,CAAA;QAC/C,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;QACvC,MAAM,wBAAwB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAA;IAC/C,CAAC,CAAA;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gCAAgC,CAAC,OAAmC;IAClF,OAAO,6BAA6B,CAAC,OAAO,CAAC,CAAA;AAC/C,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
/**
|
|
3
|
+
* The platform that sent the widget update request.
|
|
4
|
+
* Read from the `platform` query parameter.
|
|
5
|
+
*/
|
|
6
|
+
export type WidgetPlatform = 'ios' | 'android';
|
|
7
|
+
/**
|
|
8
|
+
* The system color scheme reported by the native widget.
|
|
9
|
+
* Read from the `theme` query parameter.
|
|
10
|
+
*/
|
|
11
|
+
export type WidgetTheme = 'light' | 'dark';
|
|
12
|
+
type NodeLikeRequest = IncomingMessage & {
|
|
13
|
+
url?: string;
|
|
14
|
+
method?: string;
|
|
15
|
+
headers: Record<string, string | string[] | undefined>;
|
|
16
|
+
socket?: {
|
|
17
|
+
encrypted?: boolean;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
type NodeLikeResponse = ServerResponse;
|
|
21
|
+
export type WidgetUpdateHandler = (request: Request) => Promise<Response>;
|
|
22
|
+
export type WidgetUpdateNodeHandler = (req: NodeLikeRequest, res: NodeLikeResponse) => Promise<void>;
|
|
23
|
+
export type WidgetUpdateExpressHandler = WidgetUpdateNodeHandler;
|
|
24
|
+
export type WidgetRenderResult = Promise<string | null> | string | null;
|
|
25
|
+
export type WidgetRenderer = (request: WidgetRenderRequest) => WidgetRenderResult;
|
|
26
|
+
/**
|
|
27
|
+
* Request context provided to the widget render handler.
|
|
28
|
+
* Contains the widget ID, family, and any auth headers from the request.
|
|
29
|
+
*/
|
|
30
|
+
export interface WidgetRenderRequest {
|
|
31
|
+
/** The widget ID requesting an update */
|
|
32
|
+
widgetId: string;
|
|
33
|
+
/** The platform the request is coming from */
|
|
34
|
+
platform: WidgetPlatform;
|
|
35
|
+
/** The system color scheme (`light` or `dark`). Defaults to `light` when not provided. */
|
|
36
|
+
theme: WidgetTheme;
|
|
37
|
+
/** The widget family/size (iOS only: "systemSmall", "systemMedium", etc.) */
|
|
38
|
+
family?: string;
|
|
39
|
+
/** The authorization token from the request (if present) */
|
|
40
|
+
token?: string;
|
|
41
|
+
/** All request headers */
|
|
42
|
+
headers: Record<string, string | string[] | undefined>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Options for creating the widget update handler.
|
|
46
|
+
*/
|
|
47
|
+
export interface WidgetUpdateHandlerOptions {
|
|
48
|
+
/**
|
|
49
|
+
* Render function that returns a serialized iOS widget payload for a given request.
|
|
50
|
+
* If not provided, iOS requests will receive a 404 response.
|
|
51
|
+
*/
|
|
52
|
+
renderIos?: WidgetRenderer;
|
|
53
|
+
/**
|
|
54
|
+
* Render function that returns a serialized Android widget payload for a given request.
|
|
55
|
+
* If not provided, Android requests will receive a 404 response.
|
|
56
|
+
*/
|
|
57
|
+
renderAndroid?: WidgetRenderer;
|
|
58
|
+
/**
|
|
59
|
+
* Optional validation function for auth tokens.
|
|
60
|
+
* Return true if the token is valid, false to reject with 401.
|
|
61
|
+
* If not provided, all requests are accepted.
|
|
62
|
+
*/
|
|
63
|
+
validateToken?: (token: string) => Promise<boolean> | boolean;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Creates a Fetch API request handler for serving widget updates.
|
|
67
|
+
*/
|
|
68
|
+
export declare function createWidgetUpdateHandler(options: WidgetUpdateHandlerOptions): WidgetUpdateHandler;
|
|
69
|
+
/**
|
|
70
|
+
* Creates a Node.js HTTP request handler for serving widget updates.
|
|
71
|
+
*/
|
|
72
|
+
export declare function createWidgetUpdateNodeHandler(options: WidgetUpdateHandlerOptions): WidgetUpdateNodeHandler;
|
|
73
|
+
/**
|
|
74
|
+
* Creates an Express-compatible request handler for serving widget updates.
|
|
75
|
+
*/
|
|
76
|
+
export declare function createWidgetUpdateExpressHandler(options: WidgetUpdateHandlerOptions): WidgetUpdateExpressHandler;
|
|
77
|
+
export {};
|
|
78
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAEhE;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,SAAS,CAAA;AAE9C;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,MAAM,CAAA;AAE1C,KAAK,eAAe,GAAG,eAAe,GAAG;IACvC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;IACtD,MAAM,CAAC,EAAE;QACP,SAAS,CAAC,EAAE,OAAO,CAAA;KACpB,CAAA;CACF,CAAA;AAED,KAAK,gBAAgB,GAAG,cAAc,CAAA;AAEtC,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AACzE,MAAM,MAAM,uBAAuB,GAAG,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AACpG,MAAM,MAAM,0BAA0B,GAAG,uBAAuB,CAAA;AAChE,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,CAAA;AACvE,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,mBAAmB,KAAK,kBAAkB,CAAA;AAEjF;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAA;IAChB,8CAA8C;IAC9C,QAAQ,EAAE,cAAc,CAAA;IACxB,0FAA0F;IAC1F,KAAK,EAAE,WAAW,CAAA;IAClB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;CACvD;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,SAAS,CAAC,EAAE,cAAc,CAAA;IAE1B;;;OAGG;IACH,aAAa,CAAC,EAAE,cAAc,CAAA;IAE9B;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAA;CAC9D;AAoED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,0BAA0B,GAAG,mBAAmB,CAuElG;AAED;;GAEG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,0BAA0B,GAAG,uBAAuB,CAQ1G;AAED;;GAEG;AACH,wBAAgB,gCAAgC,CAAC,OAAO,EAAE,0BAA0B,GAAG,0BAA0B,CAEhH"}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@use-voltra/server",
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "Shared server rendering foundation for Voltra",
|
|
5
|
+
"main": "build/cjs/index.js",
|
|
6
|
+
"module": "build/esm/index.js",
|
|
7
|
+
"types": "build/types/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./build/types/index.d.ts",
|
|
11
|
+
"require": "./build/cjs/index.js",
|
|
12
|
+
"import": "./build/esm/index.js",
|
|
13
|
+
"default": "./build/esm/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./package.json": "./package.json"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"build",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "node ../../scripts/build-package.mjs packages/server",
|
|
23
|
+
"clean": "rm -rf build",
|
|
24
|
+
"lint": "oxlint src",
|
|
25
|
+
"typecheck": "tsc -p tsconfig.typecheck.json --noEmit"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"voltra",
|
|
29
|
+
"server",
|
|
30
|
+
"widget",
|
|
31
|
+
"rendering"
|
|
32
|
+
],
|
|
33
|
+
"author": "Saúl Sharma (https://x.com/saul_sharma), Szymon Chmal (https://x.com/chmalszymon)",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/callstackincubator/voltra.git",
|
|
37
|
+
"directory": "packages/server"
|
|
38
|
+
},
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/callstackincubator/voltra/issues"
|
|
41
|
+
},
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"homepage": "https://use-voltra.dev"
|
|
44
|
+
}
|